diff --git a/jhove-apps/src/main/java/Jhove.java b/jhove-apps/src/main/java/Jhove.java index 72b289d9e..5de0a345b 100644 --- a/jhove-apps/src/main/java/Jhove.java +++ b/jhove-apps/src/main/java/Jhove.java @@ -1,295 +1,261 @@ -/********************************************************************** - * Jhove - JSTOR/Harvard Object Validation Environment - * Copyright 2004-2007 by the President and Fellows of Harvard College +/** + * ******************************************************************** Jhove - JSTOR/Harvard Object + * Validation Environment Copyright 2004-2007 by the President and Fellows of Harvard College * - * This program 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 2 of the License, or (at - * your option) any later version. - * - * This program 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. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 - * USA - **********************************************************************/ - + *

This program 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 2 + * of the License, or (at your option) any later version. + * + *

This program 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. + * + *

You should have received a copy of the GNU Lesser General Public License along with this + * program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA ******************************************************************** + */ import edu.harvard.hul.ois.jhove.App; -import edu.harvard.hul.ois.jhove.ExitCode; import edu.harvard.hul.ois.jhove.CoreMessageConstants; +import edu.harvard.hul.ois.jhove.ExitCode; import edu.harvard.hul.ois.jhove.JhoveBase; import edu.harvard.hul.ois.jhove.Module; import edu.harvard.hul.ois.jhove.OutputHandler; - import java.util.ArrayList; import java.util.List; import java.util.logging.Level; import java.util.logging.Logger; -public class Jhove -{ - /** Application name. */ - private static final String NAME = "Jhove"; - /** Logger for this class. */ - private static final Logger LOGGER = Logger.getLogger(Jhove.class.getCanonicalName()); - - private Jhove() - { - throw new AssertionError("Should never enter private constructor"); - } +public class Jhove { + /** Application name. */ + private static final String NAME = "Jhove"; + /** Logger for this class. */ + private static final Logger LOGGER = Logger.getLogger(Jhove.class.getCanonicalName()); - private static final String C_CONFIG_OPTION = "-c"; - private static final String X_CONFIG_OPTION = "-x"; - private static final String NOT_FOUND = "' not found"; - private static final String HANDLER = "Handler '"; + private Jhove() { + throw new AssertionError("Should never enter private constructor"); + } + private static final String C_CONFIG_OPTION = "-c"; + private static final String X_CONFIG_OPTION = "-x"; + private static final String NOT_FOUND = "' not found"; + private static final String HANDLER = "Handler '"; - /** - * MAIN ENTRY POINT. - */ - public static void main(String [] args) - { - // Make sure we have a satisfactory version of Java. - String version = System.getProperty("java.vm.version"); - if (version.compareTo("1.8.0") < 0) { - LOGGER.log(Level.SEVERE, CoreMessageConstants.EXC_JAVA_VER_INCMPT); - System.exit(ExitCode.INCOMPATIBLE_VM.getReturnCode()); - } + /** MAIN ENTRY POINT. */ + public static void main(String[] args) { + // Make sure we have a satisfactory version of Java. + String version = System.getProperty("java.vm.version"); + if (version.compareTo("1.8.0") < 0) { + LOGGER.log(Level.SEVERE, CoreMessageConstants.EXC_JAVA_VER_INCMPT); + System.exit(ExitCode.INCOMPATIBLE_VM.getReturnCode()); + } - try { + try { - // Initialize the application state object. - App app = App.newAppWithName(NAME); + // Initialize the application state object. + App app = App.newAppWithName(NAME); - // Retrieve configuration. - String configFile = JhoveBase.getConfigFileFromProperties(); - String saxClass = JhoveBase.getSaxClassFromProperties(); + // Retrieve configuration. + String configFile = JhoveBase.getConfigFileFromProperties(); + String saxClass = JhoveBase.getSaxClassFromProperties(); - /* Pre-parse the command line for -c and -x config options. - * With Windows, we have to deal with quote marks on our own. - * With Unix, the shell takes care of quotes for us. */ - boolean quoted = false; - for (int i = 0; i < args.length; i++) { - if (quoted) { - int len = args[i].length(); - if (args[i].charAt(len - 1) == '"') { - quoted = false; - } - } - else { - if (C_CONFIG_OPTION.equals(args[i])) { - if (i < args.length - 1) { - configFile = args[++i]; - } - } - else if (X_CONFIG_OPTION.equals(args[i])) { - if (i < args.length - 1) { - saxClass = args[++i]; - } - } - else if (args[i].charAt(0) == '"') { - quoted = true; - } - } + /* Pre-parse the command line for -c and -x config options. + * With Windows, we have to deal with quote marks on our own. + * With Unix, the shell takes care of quotes for us. */ + boolean quoted = false; + for (int i = 0; i < args.length; i++) { + if (quoted) { + int len = args[i].length(); + if (args[i].charAt(len - 1) == '"') { + quoted = false; + } + } else { + if (C_CONFIG_OPTION.equals(args[i])) { + if (i < args.length - 1) { + configFile = args[++i]; + } + } else if (X_CONFIG_OPTION.equals(args[i])) { + if (i < args.length - 1) { + saxClass = args[++i]; } + } else if (args[i].charAt(0) == '"') { + quoted = true; + } + } + } - // Initialize the JHOVE engine. - String encoding = null; - String tempDir = null; - int bufferSize = -1; - String moduleName = null; - String handlerName = null; - String aboutHandler = null; - String logLevel = null; - String outputFile = null; - boolean checksum = false; - boolean showRaw = false; - boolean signature = false; - List list = new ArrayList<>(); + // Initialize the JHOVE engine. + String encoding = null; + String tempDir = null; + int bufferSize = -1; + String moduleName = null; + String handlerName = null; + String aboutHandler = null; + String logLevel = null; + String outputFile = null; + boolean checksum = false; + boolean showRaw = false; + boolean signature = false; + List list = new ArrayList<>(); - /* - * Parse command line arguments: - * -m module Module name - * -h handler Output handler - * -e encoding Output encoding - * -H handler About handler - * -o output Output file pathname - * -t tempdir Directory for temp files - * -b bufsize Buffer size for buffered I/O - * -k Calculate checksums - * -r Display raw numeric flags - * -s Check internal signatures only - * dirFileOrUri Directories, file pathnames, or URIs - * - * The following arguments were defined in previous - * versions, but are now obsolete - * -p param OBSOLETE - * -P param OBSOLETE - */ + /* + * Parse command line arguments: + * -m module Module name + * -h handler Output handler + * -e encoding Output encoding + * -H handler About handler + * -o output Output file pathname + * -t tempdir Directory for temp files + * -b bufsize Buffer size for buffered I/O + * -k Calculate checksums + * -r Display raw numeric flags + * -s Check internal signatures only + * dirFileOrUri Directories, file pathnames, or URIs + * + * The following arguments were defined in previous + * versions, but are now obsolete + * -p param OBSOLETE + * -P param OBSOLETE + */ - quoted = false; - StringBuilder filename = null; + quoted = false; + StringBuilder filename = null; - for (int i = 0; i < args.length; i++) { - if (quoted) { - filename.append(" "); - int len = args[i].length(); - if (args[i].charAt(len - 1) == '"') { - filename.append(args[i].substring(0, len - 1)); - list.add(filename.toString()); - quoted = false; - } - else { - filename.append(args[i]); - } - } - else { - if (C_CONFIG_OPTION.equals(args[i])) { - i++; - } - else if ("-m".equals(args[i])) { - if (i < args.length - 1) { - moduleName = args[++i]; - } - } - else if ("-p".equals(args[i])) { - // Obsolete -- but eat the next arg for compatibility - if (i < args.length - 1) { - @SuppressWarnings("unused") - String moduleParam = args[++i]; - } - } - else if ("-h".equals(args[i])) { - if (i < args.length - 1) { - handlerName = args[++i]; - } - } - else if ("-P".equals(args[i])) { - // Obsolete -- but eat the next arg for compatibility - if (i < args.length - 1) { - @SuppressWarnings("unused") - String handlerParam = args[++i]; - } - } - else if ("-e".equals(args[i])) { - if (i < args.length - 1) { - encoding = args[++i]; - } - } - else if ("-H".equals(args[i])) { - if (i < args.length - 1) { - aboutHandler = args[++i]; - } - } - else if ("-l".equals(args[i])) { - if (i < args.length - 1) { - logLevel = args[++i]; - } - } - else if ("-o".equals(args[i])) { - if (i < args.length - 1) { - outputFile = args[++i]; - } - } - else if (X_CONFIG_OPTION.equals(args[i])) { - i++; - } - else if ("-t".equals(args[i])) { - if (i < args.length - 1) { - tempDir = args[++i]; - } - } - else if ("-b".equals(args[i])) { - if (i < args.length - 1) { - try { - bufferSize = Integer.parseInt(args[++i]); - } - catch (NumberFormatException nfe) { - LOGGER.log(Level.WARNING, "Invalid buffer size, using default."); - } - } - } - else if ("-k".equals(args[i])) { - checksum = true; - } - else if ("-r".equals(args[i])) { - showRaw = true; - } - else if ("-s".equals(args[i])) { - signature = true; - } - else if (args[i].charAt(0) != '-') { - if (args[i].charAt(0) == '"') { - filename = new StringBuilder(); - filename.append(args[i].substring(1)); - quoted = true; - } - else { - list.add(args[i]); - } - } - } + for (int i = 0; i < args.length; i++) { + if (quoted) { + filename.append(" "); + int len = args[i].length(); + if (args[i].charAt(len - 1) == '"') { + filename.append(args[i].substring(0, len - 1)); + list.add(filename.toString()); + quoted = false; + } else { + filename.append(args[i]); + } + } else { + if (C_CONFIG_OPTION.equals(args[i])) { + i++; + } else if ("-m".equals(args[i])) { + if (i < args.length - 1) { + moduleName = args[++i]; } - if (quoted) { - list.add(filename.toString()); + } else if ("-p".equals(args[i])) { + // Obsolete -- but eat the next arg for compatibility + if (i < args.length - 1) { + @SuppressWarnings("unused") + String moduleParam = args[++i]; } - - JhoveBase je = new JhoveBase(); - // Only set the log level if a param value was assigned - if (logLevel != null) { - je.setLogLevel(logLevel); + } else if ("-h".equals(args[i])) { + if (i < args.length - 1) { + handlerName = args[++i]; } - je.init (configFile, saxClass); - if (encoding == null) { - encoding = je.getEncoding(); + } else if ("-P".equals(args[i])) { + // Obsolete -- but eat the next arg for compatibility + if (i < args.length - 1) { + @SuppressWarnings("unused") + String handlerParam = args[++i]; } - if (tempDir == null) { - tempDir = je.getTempDirectory(); + } else if ("-e".equals(args[i])) { + if (i < args.length - 1) { + encoding = args[++i]; } - if (bufferSize < 0) { - bufferSize = je.getBufferSize(); + } else if ("-H".equals(args[i])) { + if (i < args.length - 1) { + aboutHandler = args[++i]; } - Module module = je.getModule(moduleName); - if (module == null && moduleName != null) { - LOGGER.log(Level.SEVERE, "Module '" + moduleName + NOT_FOUND); - System.exit(ExitCode.ERROR.getReturnCode()); + } else if ("-l".equals(args[i])) { + if (i < args.length - 1) { + logLevel = args[++i]; } - OutputHandler about = je.getHandler(aboutHandler); - if (about == null && aboutHandler != null) { - LOGGER.log(Level.SEVERE, HANDLER + aboutHandler + NOT_FOUND); - System.exit(ExitCode.ERROR.getReturnCode()); + } else if ("-o".equals(args[i])) { + if (i < args.length - 1) { + outputFile = args[++i]; } - OutputHandler handler = je.getHandler(handlerName); - if (handler == null && handlerName != null) { - LOGGER.log(Level.SEVERE, HANDLER + handlerName + NOT_FOUND); - System.exit(ExitCode.ERROR.getReturnCode()); + } else if (X_CONFIG_OPTION.equals(args[i])) { + i++; + } else if ("-t".equals(args[i])) { + if (i < args.length - 1) { + tempDir = args[++i]; } - String[] dirFileOrUri = null; - int len = list.size(); - if (len > 0) { - dirFileOrUri = new String[len]; - for (int i = 0; i < len; i++) { - dirFileOrUri[i] = list.get(i); - } + } else if ("-b".equals(args[i])) { + if (i < args.length - 1) { + try { + bufferSize = Integer.parseInt(args[++i]); + } catch (NumberFormatException nfe) { + LOGGER.log(Level.WARNING, "Invalid buffer size, using default."); + } } - - // Invoke the JHOVE engine. - je.setEncoding(encoding); - je.setTempDirectory(tempDir); - je.setBufferSize(bufferSize); - je.setChecksumFlag(checksum); - je.setShowRawFlag(showRaw); - je.setSignatureFlag(signature); - je.dispatch(app, module, about, handler, outputFile, dirFileOrUri); + } else if ("-k".equals(args[i])) { + checksum = true; + } else if ("-r".equals(args[i])) { + showRaw = true; + } else if ("-s".equals(args[i])) { + signature = true; + } else if (args[i].charAt(0) != '-') { + if (args[i].charAt(0) == '"') { + filename = new StringBuilder(); + filename.append(args[i].substring(1)); + quoted = true; + } else { + list.add(args[i]); + } + } } - catch (Exception e) { - LOGGER.log(Level.SEVERE, e.getMessage()); - e.printStackTrace(System.err); - System.exit(ExitCode.ERROR.getReturnCode()); + } + if (quoted) { + list.add(filename.toString()); + } + + JhoveBase je = new JhoveBase(); + // Only set the log level if a param value was assigned + if (logLevel != null) { + je.setLogLevel(logLevel); + } + je.init(configFile, saxClass); + if (encoding == null) { + encoding = je.getEncoding(); + } + if (tempDir == null) { + tempDir = je.getTempDirectory(); + } + if (bufferSize < 0) { + bufferSize = je.getBufferSize(); + } + Module module = je.getModule(moduleName); + if (module == null && moduleName != null) { + LOGGER.log(Level.SEVERE, "Module '" + moduleName + NOT_FOUND); + System.exit(ExitCode.ERROR.getReturnCode()); + } + OutputHandler about = je.getHandler(aboutHandler); + if (about == null && aboutHandler != null) { + LOGGER.log(Level.SEVERE, HANDLER + aboutHandler + NOT_FOUND); + System.exit(ExitCode.ERROR.getReturnCode()); + } + OutputHandler handler = je.getHandler(handlerName); + if (handler == null && handlerName != null) { + LOGGER.log(Level.SEVERE, HANDLER + handlerName + NOT_FOUND); + System.exit(ExitCode.ERROR.getReturnCode()); + } + String[] dirFileOrUri = null; + int len = list.size(); + if (len > 0) { + dirFileOrUri = new String[len]; + for (int i = 0; i < len; i++) { + dirFileOrUri[i] = list.get(i); } + } + + // Invoke the JHOVE engine. + je.setEncoding(encoding); + je.setTempDirectory(tempDir); + je.setBufferSize(bufferSize); + je.setChecksumFlag(checksum); + je.setShowRawFlag(showRaw); + je.setSignatureFlag(signature); + je.dispatch(app, module, about, handler, outputFile, dirFileOrUri); + } catch (Exception e) { + LOGGER.log(Level.SEVERE, e.getMessage()); + e.printStackTrace(System.err); + System.exit(ExitCode.ERROR.getReturnCode()); } + } } diff --git a/jhove-apps/src/main/java/JhoveView.java b/jhove-apps/src/main/java/JhoveView.java index 1ead3f6ef..f6ebbf129 100644 --- a/jhove-apps/src/main/java/JhoveView.java +++ b/jhove-apps/src/main/java/JhoveView.java @@ -1,139 +1,122 @@ -/********************************************************************** - * Jhove - JSTOR/Harvard Object Validation Environment - * Copyright 2004-2008 by JSTOR and the President and Fellows of Harvard College +/** + * ******************************************************************** Jhove - JSTOR/Harvard Object + * Validation Environment Copyright 2004-2008 by JSTOR and the President and Fellows of Harvard + * College * - * This program 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 2 of the License, or (at - * your option) any later version. - * - * This program 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. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 - * USA - **********************************************************************/ - + *

This program 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 2 + * of the License, or (at your option) any later version. + * + *

This program 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. + * + *

You should have received a copy of the GNU Lesser General Public License along with this + * program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA ******************************************************************** + */ import edu.harvard.hul.ois.jhove.App; -import edu.harvard.hul.ois.jhove.ExitCode; import edu.harvard.hul.ois.jhove.CoreMessageConstants; +import edu.harvard.hul.ois.jhove.ExitCode; import edu.harvard.hul.ois.jhove.JhoveBase; import edu.harvard.hul.ois.jhove.JhoveException; import edu.harvard.hul.ois.jhove.viewer.JhoveWindow; - -import javax.swing.JFrame; -import javax.swing.JOptionPane; import java.awt.Toolkit; import java.net.URL; import java.util.logging.Level; import java.util.logging.Logger; +import javax.swing.JFrame; +import javax.swing.JOptionPane; -/** - * JhoveView - JSTOR/Harvard Object Validation Environment. - */ -public class JhoveView -{ - private static final Logger LOGGER = Logger.getLogger(JhoveView.class.getCanonicalName()); - - /** Application name. */ - private static final String NAME = "JhoveView"; //$NON-NLS-1$ - /** Application icon. */ - private static final String ICON_PATH = "org/openpreservation/jhove/icon.png"; //$NON-NLS-1$ +/** JhoveView - JSTOR/Harvard Object Validation Environment. */ +public class JhoveView { + private static final Logger LOGGER = Logger.getLogger(JhoveView.class.getCanonicalName()); - /** Stub constructor. */ - private JhoveView() - { - } + /** Application name. */ + private static final String NAME = "JhoveView"; // $NON-NLS-1$ + /** Application icon. */ + private static final String ICON_PATH = "org/openpreservation/jhove/icon.png"; // $NON-NLS-1$ - /** - * Application main entry point. - * - * @param args Command-line arguments - */ - public static void main(String[] args) - { - // Make sure we have a satisfactory version of Java. - String version = System.getProperty("java.vm.version"); //$NON-NLS-1$ - if (version.compareTo("1.8.0") < 0) { //$NON-NLS-1$ - LOGGER.log(Level.SEVERE, CoreMessageConstants.EXC_JAVA_VER_INCMPT); - errorAlert(CoreMessageConstants.EXC_JAVA_VER_INCMPT); - System.exit(ExitCode.INCOMPATIBLE_VM.getReturnCode()); - } + /** Stub constructor. */ + private JhoveView() {} - // If we're running on a Macintosh, put the menubar at the top - // of the screen where it belongs. - System.setProperty("apple.laf.useScreenMenuBar", "true"); //$NON-NLS-1$ //$NON-NLS-2$ + /** + * Application main entry point. + * + * @param args Command-line arguments + */ + public static void main(String[] args) { + // Make sure we have a satisfactory version of Java. + String version = System.getProperty("java.vm.version"); // $NON-NLS-1$ + if (version.compareTo("1.8.0") < 0) { // $NON-NLS-1$ + LOGGER.log(Level.SEVERE, CoreMessageConstants.EXC_JAVA_VER_INCMPT); + errorAlert(CoreMessageConstants.EXC_JAVA_VER_INCMPT); + System.exit(ExitCode.INCOMPATIBLE_VM.getReturnCode()); + } - App app = App.newAppWithName(NAME); - try { + // If we're running on a Macintosh, put the menubar at the top + // of the screen where it belongs. + System.setProperty("apple.laf.useScreenMenuBar", "true"); // $NON-NLS-1$ //$NON-NLS-2$ - // Retrieve configuration. - String configFile = JhoveBase.getConfigFileFromProperties(); - String saxClass = JhoveBase.getSaxClassFromProperties(); + App app = App.newAppWithName(NAME); + try { - // Pre-parse the command line for -c and -x config options. - boolean quoted = false; - for (int i = 0; i < args.length; i++) { - if (quoted) { - int len = args[i].length(); - if (args[i].charAt(len - 1) == '"') { - quoted = false; - } - } - else { - if ("-c".equals(args[i])) { - if (i < args.length - 1) { - configFile = args[++i]; - } - } - else if ("-x".equals(args[i])) { - if (i < args.length - 1) { - saxClass = args[++i]; - } - } - else if (args[i].charAt(0) == '"') { - quoted = true; - } - } - } + // Retrieve configuration. + String configFile = JhoveBase.getConfigFileFromProperties(); + String saxClass = JhoveBase.getSaxClassFromProperties(); - // Initialize the JHOVE engine. - JhoveBase je = new JhoveBase(); - try { - je.init(configFile, saxClass); + // Pre-parse the command line for -c and -x config options. + boolean quoted = false; + for (int i = 0; i < args.length; i++) { + if (quoted) { + int len = args[i].length(); + if (args[i].charAt(len - 1) == '"') { + quoted = false; + } + } else { + if ("-c".equals(args[i])) { + if (i < args.length - 1) { + configFile = args[++i]; } - catch (JhoveException e) { - errorAlert(e.getMessage()); - // Keep going, so user can correct in editor + } else if ("-x".equals(args[i])) { + if (i < args.length - 1) { + saxClass = args[++i]; } + } else if (args[i].charAt(0) == '"') { + quoted = true; + } + } + } - // Create the main window to select a file. - JhoveWindow jwin = new JhoveWindow(app, je); - URL url = ClassLoader.getSystemResource(ICON_PATH); //$NON-NLS-1$ - Toolkit kit = Toolkit.getDefaultToolkit(); - jwin.setIconImage(kit.createImage(url)); - jwin.setVisible (true); + // Initialize the JHOVE engine. + JhoveBase je = new JhoveBase(); + try { + je.init(configFile, saxClass); + } catch (JhoveException e) { + errorAlert(e.getMessage()); + // Keep going, so user can correct in editor + } - } - catch (Exception e) { - e.printStackTrace(System.err); - LOGGER.log(Level.SEVERE, e.getMessage()); - System.exit(ExitCode.ERROR.getReturnCode()); - } - } + // Create the main window to select a file. + JhoveWindow jwin = new JhoveWindow(app, je); + URL url = ClassLoader.getSystemResource(ICON_PATH); // $NON-NLS-1$ + Toolkit kit = Toolkit.getDefaultToolkit(); + jwin.setIconImage(kit.createImage(url)); + jwin.setVisible(true); - /** Displays an error alert. */ - private static void errorAlert(String msg) - { - JFrame hiddenFrame = new JFrame(); - // Truncate long messages so the alert isn't wider than the screen - String message = (msg.length() > 80) ? msg.substring(0, 79) + "..." : msg; - LOGGER.log(Level.WARNING, msg); - JOptionPane.showMessageDialog(hiddenFrame, message, "Jhove Error", - JOptionPane.ERROR_MESSAGE); + } catch (Exception e) { + e.printStackTrace(System.err); + LOGGER.log(Level.SEVERE, e.getMessage()); + System.exit(ExitCode.ERROR.getReturnCode()); } + } + + /** Displays an error alert. */ + private static void errorAlert(String msg) { + JFrame hiddenFrame = new JFrame(); + // Truncate long messages so the alert isn't wider than the screen + String message = (msg.length() > 80) ? msg.substring(0, 79) + "..." : msg; + LOGGER.log(Level.WARNING, msg); + JOptionPane.showMessageDialog(hiddenFrame, message, "Jhove Error", JOptionPane.ERROR_MESSAGE); + } } diff --git a/jhove-apps/src/main/java/UserHome.java b/jhove-apps/src/main/java/UserHome.java index 406d01d53..ae1df34aa 100644 --- a/jhove-apps/src/main/java/UserHome.java +++ b/jhove-apps/src/main/java/UserHome.java @@ -1,33 +1,23 @@ -/********************************************************************** - * UserHome - JSTOR/Harvard Object Validation Environment - * Copyright 2006 by the President and Fellows of Harvard College - * - * This program 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 2 of the License, or (at - * your option) any later version. - * - * This program 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. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 - * USA - **********************************************************************/ - - - /** - * Determine the default value of the Java user.home property. + * ******************************************************************** UserHome - JSTOR/Harvard + * Object Validation Environment Copyright 2006 by the President and Fellows of Harvard College + * + *

This program 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 2 + * of the License, or (at your option) any later version. + * + *

This program 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. + * + *

You should have received a copy of the GNU Lesser General Public License along with this + * program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA ******************************************************************** */ -public class UserHome -{ - public static void main (String [] args) - { - System.out.println ("Default user.home is \"" + - System.getProperty ("user.home") + "\""); - } + +/** Determine the default value of the Java user.home property. */ +public class UserHome { + public static void main(String[] args) { + System.out.println("Default user.home is \"" + System.getProperty("user.home") + "\""); + } } diff --git a/jhove-apps/src/main/java/edu/harvard/hul/ois/jhove/ConfigWriter.java b/jhove-apps/src/main/java/edu/harvard/hul/ois/jhove/ConfigWriter.java index c936ad1c3..86a1059b4 100644 --- a/jhove-apps/src/main/java/edu/harvard/hul/ois/jhove/ConfigWriter.java +++ b/jhove-apps/src/main/java/edu/harvard/hul/ois/jhove/ConfigWriter.java @@ -1,215 +1,183 @@ -/********************************************************************** - * Jhove - JSTOR/Harvard Object Validation Environment - * Copyright 2003 by JSTOR and the President and Fellows of Harvard College +/** + * ******************************************************************** Jhove - JSTOR/Harvard Object + * Validation Environment Copyright 2003 by JSTOR and the President and Fellows of Harvard College * - **********************************************************************/ - + *

******************************************************************** + */ package edu.harvard.hul.ois.jhove; +import edu.harvard.hul.ois.jhove.viewer.ConfigWindow; import java.io.*; import java.util.*; import javax.swing.*; -import edu.harvard.hul.ois.jhove.viewer.ConfigWindow; - /** - * - * Class to write out configuration information to the configuration file. - * To minimize the chance of getting into a bad state, it writes to a - * temporary file, then replaces the old config file with that file, - * rather than directly overwriting the existing file. - * - * @author Gary McGath + * Class to write out configuration information to the configuration file. To minimize the chance of + * getting into a bad state, it writes to a temporary file, then replaces the old config file with + * that file, rather than directly overwriting the existing file. * + * @author Gary McGath */ public class ConfigWriter { - - private PrintWriter _confOut; - private File _tempFile; - private File _confFile; - ConfigWindow _parent; - - private final static String CLASS_TAG_START = " "; - private final static String CLASS_TAG_CLOSE = ""; - - /** - * Constructor. - * Creates a temporary file for writing and creates an OutputStreamWriter - * to write to it. If there is already a file located by - * file, it will not be replaced or overwritten - * until writeFile has successfully written out - * the temporary file. - * - * @param file Location of the configuration file - * - * @param parent The ConfigWindow which invoked this instance. - * May be null if invoked to write a default config file. - */ - public ConfigWriter (File file, ConfigWindow parent) throws IOException - { - _confFile = file; - _parent = parent; - // Set up a temporary file to write to. - String path = file.getParent(); - file.getParentFile().mkdirs(); // Make sure the directory exists - _tempFile = File.createTempFile ("jho", ".conf", new File (path)); - //_tempFile.createNewFile(); - FileOutputStream ostrm = new FileOutputStream (_tempFile); - OutputStreamWriter osw = new OutputStreamWriter (ostrm, "UTF-8"); - _confOut = new PrintWriter (osw); + private PrintWriter _confOut; + private File _tempFile; + private File _confFile; + ConfigWindow _parent; + + private static final String CLASS_TAG_START = " "; + private static final String CLASS_TAG_CLOSE = ""; + + /** + * Constructor. Creates a temporary file for writing and creates an OutputStreamWriter to write to + * it. If there is already a file located by file, it will not be replaced or + * overwritten until writeFile has successfully written out the temporary file. + * + * @param file Location of the configuration file + * @param parent The ConfigWindow which invoked this instance. May be null if invoked to write a + * default config file. + */ + public ConfigWriter(File file, ConfigWindow parent) throws IOException { + _confFile = file; + _parent = parent; + // Set up a temporary file to write to. + String path = file.getParent(); + file.getParentFile().mkdirs(); // Make sure the directory exists + _tempFile = File.createTempFile("jho", ".conf", new File(path)); + // _tempFile.createNewFile(); + FileOutputStream ostrm = new FileOutputStream(_tempFile); + OutputStreamWriter osw = new OutputStreamWriter(ostrm, "UTF-8"); + _confOut = new PrintWriter(osw); + } + + /** + * Writes out the content of the file to the temporary file, then deletes the existing + * configuration file (as specified by the constructor parameter) and renames the temporary file + * to the configuration file. + * + *

If the temporary file can't be written, or the configuration file can't be replaced, a + * warning dialog is put up and the configuration file remains unchanged. + */ + public void writeFile( + List modules, + List handlers, + File homeDir, + File tempDir, + String encoding, + int bufferSize) { + writeHead(); + + // Write the home and temp directories. Home must always be valid. + _confOut.println(" " + encodeContent(homeDir.getPath()) + ""); + + // Write out the encoding + if (encoding != null && encoding.length() > 0) { + _confOut.println(" " + encodeContent(encoding) + ""); } - - - /** - * Writes out the content of the file to the temporary file, - * then deletes the existing configuration file (as specified - * by the constructor parameter) and renames the temporary file - * to the configuration file. - * - * If the temporary file can't be written, or the configuration - * file can't be replaced, a warning dialog is put up and the - * configuration file remains unchanged. - */ - public void writeFile (List modules, - List handlers, - File homeDir, - File tempDir, - String encoding, - int bufferSize) - { - writeHead (); - - // Write the home and temp directories. Home must always be valid. - _confOut.println (" " + - encodeContent (homeDir.getPath ()) + - ""); - - // Write out the encoding - if (encoding != null && encoding.length() > 0) { - _confOut.println (" " + - encodeContent (encoding) + ""); - } - - if (tempDir != null) { - _confOut.println (" " + - encodeContent (tempDir.getPath ()) + - ""); - } - - // Write the buffer size if not default - if (bufferSize > 0) { - _confOut.println (" " + bufferSize + - ""); - } - - // Write out the modules - ListIterator iter = modules.listIterator (); - while (iter.hasNext ()) { - ModuleInfo minfo = iter.next (); - // The class must be non-null, but init may be null. - // If the class is empty, it's a user error (probably - // clicked "Add" and then lost track of it). Don't - // write it out. - if (!"".equals (minfo.clas)) { - _confOut.println (" "); - _confOut.println (CLASS_TAG_START + encodeContent (minfo.clas) + - CLASS_TAG_CLOSE); - if (minfo.init != null && minfo.init.length () > 0) { - _confOut.println (" " + encodeContent (minfo.init) + - ""); - } - /** tuple[2] and beyond are parameters */ - if (minfo.params != null) { - for (int i = 0; i < minfo.params.length; i++) { - _confOut.println (" " + encodeContent(minfo.params[i]) + - ""); - } - } - _confOut.println (" "); - } - } - - // Write out the handlers - ListIterator hiter = handlers.listIterator (); - while (hiter.hasNext ()) { - String handler = hiter.next ()[0]; - if (handler.length() > 0) { // Don't write out blank handler names - _confOut.println (" "); - _confOut.println (CLASS_TAG_START + encodeContent (handler) + - CLASS_TAG_CLOSE); - _confOut.println (" "); - } - } + if (tempDir != null) { + _confOut.println(" " + encodeContent(tempDir.getPath()) + ""); + } + + // Write the buffer size if not default + if (bufferSize > 0) { + _confOut.println(" " + bufferSize + ""); + } - writeTail (); - _confOut.close (); - - // Replace the old file with the new. - if (_confFile.exists () && !_confFile.delete ()) { - if (_parent != null) { - JOptionPane.showMessageDialog(_parent, - "Can't replace old config file", - "Error", - JOptionPane.ERROR_MESSAGE); - } - _tempFile.delete (); + // Write out the modules + ListIterator iter = modules.listIterator(); + while (iter.hasNext()) { + ModuleInfo minfo = iter.next(); + // The class must be non-null, but init may be null. + // If the class is empty, it's a user error (probably + // clicked "Add" and then lost track of it). Don't + // write it out. + if (!"".equals(minfo.clas)) { + _confOut.println(" "); + _confOut.println(CLASS_TAG_START + encodeContent(minfo.clas) + CLASS_TAG_CLOSE); + if (minfo.init != null && minfo.init.length() > 0) { + _confOut.println(" " + encodeContent(minfo.init) + ""); } - else { - _tempFile.renameTo (_confFile); + /** tuple[2] and beyond are parameters */ + if (minfo.params != null) { + for (int i = 0; i < minfo.params.length; i++) { + _confOut.println(" " + encodeContent(minfo.params[i]) + ""); + } } + _confOut.println(" "); + } } - - /* Write the fixed lines which begin the config file */ - private void writeHead () - { - _confOut.println(""); - _confOut.println(""); + + // Write out the handlers + ListIterator hiter = handlers.listIterator(); + while (hiter.hasNext()) { + String handler = hiter.next()[0]; + if (handler.length() > 0) { // Don't write out blank handler names + _confOut.println(" "); + _confOut.println(CLASS_TAG_START + encodeContent(handler) + CLASS_TAG_CLOSE); + _confOut.println(" "); + } } - - /* Write out the fixed end of the config file */ - private void writeTail () - { - _confOut.println(""); + + writeTail(); + _confOut.close(); + + // Replace the old file with the new. + if (_confFile.exists() && !_confFile.delete()) { + if (_parent != null) { + JOptionPane.showMessageDialog( + _parent, "Can't replace old config file", "Error", JOptionPane.ERROR_MESSAGE); + } + _tempFile.delete(); + } else { + _tempFile.renameTo(_confFile); } - - - /** - * Encodes a content String in XML-clean form, converting characters - * to entities as necessary. The null string will be - * converted to an empty string. - */ - private static String encodeContent (String content) - { - if (content == null) { - content = ""; - } - StringBuffer buffer = new StringBuffer (content); + } - int n = 0; - while ((n = buffer.indexOf ("&", n)) > -1) { - buffer.insert (n+1, "amp;"); - n +=5; - } - n = 0; - while ((n = buffer.indexOf ("<", n)) > -1) { - buffer.replace (n, n+1, "<"); - n += 4; - } - n = 0; - while ((n = buffer.indexOf (">", n)) > -1) { - buffer.replace (n, n+1, ">"); - n += 4; - } + /* Write the fixed lines which begin the config file */ + private void writeHead() { + _confOut.println(""); + _confOut.println(""); + } - return buffer.toString (); + /* Write out the fixed end of the config file */ + private void writeTail() { + _confOut.println(""); + } + + /** + * Encodes a content String in XML-clean form, converting characters to entities as necessary. The + * null string will be converted to an empty string. + */ + private static String encodeContent(String content) { + if (content == null) { + content = ""; } + StringBuffer buffer = new StringBuffer(content); + + int n = 0; + while ((n = buffer.indexOf("&", n)) > -1) { + buffer.insert(n + 1, "amp;"); + n += 5; + } + n = 0; + while ((n = buffer.indexOf("<", n)) > -1) { + buffer.replace(n, n + 1, "<"); + n += 4; + } + n = 0; + while ((n = buffer.indexOf(">", n)) > -1) { + buffer.replace(n, n + 1, ">"); + n += 4; + } + + return buffer.toString(); + } } diff --git a/jhove-apps/src/main/java/edu/harvard/hul/ois/jhove/DefaultConfigurationBuilder.java b/jhove-apps/src/main/java/edu/harvard/hul/ois/jhove/DefaultConfigurationBuilder.java index 9e29e94fe..7a4cbccef 100644 --- a/jhove-apps/src/main/java/edu/harvard/hul/ois/jhove/DefaultConfigurationBuilder.java +++ b/jhove-apps/src/main/java/edu/harvard/hul/ois/jhove/DefaultConfigurationBuilder.java @@ -3,45 +3,39 @@ import java.io.File; import java.lang.reflect.Field; -/** This class creates a default configuration if no valid configuration file - * is found. */ +/** This class creates a default configuration if no valid configuration file is found. */ public class DefaultConfigurationBuilder { - private final static String FILE_SEP = System.getProperty ("file.separator"); - private final static String HOME_DIR = System.getProperty ("user.home"); - private final static String JHOVE_DIR = HOME_DIR + FILE_SEP + "jhove"; - - private File configFile; - - - /** Constructor. A location for the file may be specified or - * left null, */ - public DefaultConfigurationBuilder (File location) { - if (location != null) { - configFile = location; - } - else { - configFile = new File (JHOVE_DIR + - FILE_SEP + "conf" + - FILE_SEP + "jhove.conf"); - } - } + private static final String FILE_SEP = System.getProperty("file.separator"); + private static final String HOME_DIR = System.getProperty("user.home"); + private static final String JHOVE_DIR = HOME_DIR + FILE_SEP + "jhove"; + + private File configFile; - public File getConfigFile () { - return configFile; + /** Constructor. A location for the file may be specified or left null, */ + public DefaultConfigurationBuilder(File location) { + if (location != null) { + configFile = location; + } else { + configFile = new File(JHOVE_DIR + FILE_SEP + "conf" + FILE_SEP + "jhove.conf"); } - - /** We can't have a static method in an Interface and override it, so we - * have to get a bit ugly to fake static inheritance. The advantage of - * this is that only the Modules with non-null default config file - * parameters have to implement the defaultConfigParams static field. */ - protected String[] getDefaultConfigParameters (Class c) { - try { - Field dcpField = c.getField("defaultConfigParams"); - return (String[]) dcpField.get(null); - } - catch (Exception e) { - return new String [] {}; - } + } + + public File getConfigFile() { + return configFile; + } + + /** + * We can't have a static method in an Interface and override it, so we have to get a bit ugly to + * fake static inheritance. The advantage of this is that only the Modules with non-null default + * config file parameters have to implement the defaultConfigParams static field. + */ + protected String[] getDefaultConfigParameters(Class c) { + try { + Field dcpField = c.getField("defaultConfigParams"); + return (String[]) dcpField.get(null); + } catch (Exception e) { + return new String[] {}; } + } } diff --git a/jhove-apps/src/main/java/edu/harvard/hul/ois/jhove/ExitCode.java b/jhove-apps/src/main/java/edu/harvard/hul/ois/jhove/ExitCode.java index 9c7160bdb..23ec9f5fe 100644 --- a/jhove-apps/src/main/java/edu/harvard/hul/ois/jhove/ExitCode.java +++ b/jhove-apps/src/main/java/edu/harvard/hul/ois/jhove/ExitCode.java @@ -1,24 +1,21 @@ package edu.harvard.hul.ois.jhove; -/** - * The set of possible exit codes returned by JHOVE applications. - */ +/** The set of possible exit codes returned by JHOVE applications. */ public enum ExitCode { - /** General error. */ - ERROR(-1), + /** General error. */ + ERROR(-1), - /** Incompatible Java VM. */ - INCOMPATIBLE_VM(-2); + /** Incompatible Java VM. */ + INCOMPATIBLE_VM(-2); + private final int returnCode; - private final int returnCode; + ExitCode(int returnCode) { + this.returnCode = returnCode; + } - ExitCode(int returnCode) { - this.returnCode = returnCode; - } - - public int getReturnCode() { - return returnCode; - } + public int getReturnCode() { + return returnCode; + } } diff --git a/jhove-apps/src/main/java/edu/harvard/hul/ois/jhove/viewer/AppInfoWindow.java b/jhove-apps/src/main/java/edu/harvard/hul/ois/jhove/viewer/AppInfoWindow.java index 918d2ac21..2a600223d 100644 --- a/jhove-apps/src/main/java/edu/harvard/hul/ois/jhove/viewer/AppInfoWindow.java +++ b/jhove-apps/src/main/java/edu/harvard/hul/ois/jhove/viewer/AppInfoWindow.java @@ -1,10 +1,14 @@ -/********************************************************************** - * JhoveView - JSTOR/Harvard Object Validation Environment - * Copyright 2003-2004 by JSTOR and the President and Fellows of Harvard College - **********************************************************************/ - +/** + * ******************************************************************** JhoveView - JSTOR/Harvard + * Object Validation Environment Copyright 2003-2004 by JSTOR and the President and Fellows of + * Harvard College ******************************************************************** + */ package edu.harvard.hul.ois.jhove.viewer; +import edu.harvard.hul.ois.jhove.App; +import edu.harvard.hul.ois.jhove.JhoveBase; +import edu.harvard.hul.ois.jhove.Module; +import edu.harvard.hul.ois.jhove.OutputHandler; import java.awt.Dimension; import java.awt.Font; import java.awt.Rectangle; @@ -14,117 +18,107 @@ import java.util.Date; import java.util.Iterator; import java.util.Map; - import javax.swing.JOptionPane; import javax.swing.JPanel; import javax.swing.JScrollPane; import javax.swing.JTextArea; -import edu.harvard.hul.ois.jhove.App; -import edu.harvard.hul.ois.jhove.JhoveBase; -import edu.harvard.hul.ois.jhove.Module; -import edu.harvard.hul.ois.jhove.OutputHandler; - -/** - * This window is for presenting information about the JHOVE application. - */ +/** This window is for presenting information about the JHOVE application. */ public class AppInfoWindow extends InfoWindow { - private JTextArea texta; + private JTextArea texta; - public AppInfoWindow(App app, JhoveBase jbase) { - super("Application Info", app, jbase); - setSaveActionListener(new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { - saveInfo(); - } - }); + public AppInfoWindow(App app, JhoveBase jbase) { + super("Application Info", app, jbase); + setSaveActionListener( + new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + saveInfo(); + } + }); - texta = new JTextArea(); - texta.setColumns(72); - JScrollPane scrollpane = new JScrollPane(texta); - texta.setFont(new Font("sansserif", Font.PLAIN, 10)); - texta.setLineWrap(true); - texta.setWrapStyleWord(true); - // Getting Swing to accept what you want for dimensions - // apparently requires setting as many dimension restrictions - // as possible, and hoping it will pay attention to some - // of them. - scrollpane.setMinimumSize(new Dimension(240, 240)); - scrollpane.setMaximumSize(new Dimension(500, 250)); - scrollpane.setPreferredSize(new Dimension(500, 250)); - getContentPane().add(scrollpane, "Center"); + texta = new JTextArea(); + texta.setColumns(72); + JScrollPane scrollpane = new JScrollPane(texta); + texta.setFont(new Font("sansserif", Font.PLAIN, 10)); + texta.setLineWrap(true); + texta.setWrapStyleWord(true); + // Getting Swing to accept what you want for dimensions + // apparently requires setting as many dimension restrictions + // as possible, and hoping it will pay attention to some + // of them. + scrollpane.setMinimumSize(new Dimension(240, 240)); + scrollpane.setMaximumSize(new Dimension(500, 250)); + scrollpane.setPreferredSize(new Dimension(500, 250)); + getContentPane().add(scrollpane, "Center"); - // Add a small panel at the bottom, since on some OS's there - // may be stuff near the bottom of a window which will conflict - // with the scroll bar. - JPanel panel = new JPanel(); - panel.setMinimumSize(new Dimension(8, 8)); - getContentPane().add(panel, "South"); + // Add a small panel at the bottom, since on some OS's there + // may be stuff near the bottom of a window which will conflict + // with the scroll bar. + JPanel panel = new JPanel(); + panel.setMinimumSize(new Dimension(8, 8)); + getContentPane().add(panel, "South"); - showApp(app, jbase); - pack(); + showApp(app, jbase); + pack(); - // Scroll to the top. - texta.setEditable(false); - texta.select(0, 0); - Rectangle r = new Rectangle(0, 0, 1, 1); - texta.scrollRectToVisible(r); - } + // Scroll to the top. + texta.setEditable(false); + texta.select(0, 0); + Rectangle r = new Rectangle(0, 0, 1, 1); + texta.scrollRectToVisible(r); + } - private void showApp(App app, JhoveBase jbase) { - String appName = app.getName(); - if (appName != null) { - texta.append("Name: " + appName + eol); - } - String rel = app.getRelease(); - if (rel != null) { - texta.append("Release: " + rel); - } - Date dt = app.getDate(); - if (dt != null) { - texta.append(" " + _dateFmt.format(dt) + eol); - } - String configFile = jbase.getConfigFile(); - if (configFile != null) { - texta.append("Configuration: " + configFile + eol); - } - String saxClass = jbase.getSaxClass(); - if (saxClass != null) { - texta.append("SAX parser: " + saxClass + eol); - } - Iterator iter = jbase.getModuleMap().keySet().iterator(); - while (iter.hasNext()) { - // Module module = jbase.getModuleMap ((String) iter.next ()); - Map moduleMap = jbase.getModuleMap(); - Module module = moduleMap.get(iter.next()); - texta.append(" Module: " + module.getName() + " " - + module.getRelease() + eol); - } - // Reporting Handlers makes no sense in the viewer app; skip - String rights = app.getRights(); - if (rights != null) { - texta.append(" Rights: " + rights + eol); - } - } + private void showApp(App app, JhoveBase jbase) { + String appName = app.getName(); + if (appName != null) { + texta.append("Name: " + appName + eol); + } + String rel = app.getRelease(); + if (rel != null) { + texta.append("Release: " + rel); + } + Date dt = app.getDate(); + if (dt != null) { + texta.append(" " + _dateFmt.format(dt) + eol); + } + String configFile = jbase.getConfigFile(); + if (configFile != null) { + texta.append("Configuration: " + configFile + eol); + } + String saxClass = jbase.getSaxClass(); + if (saxClass != null) { + texta.append("SAX parser: " + saxClass + eol); + } + Iterator iter = jbase.getModuleMap().keySet().iterator(); + while (iter.hasNext()) { + // Module module = jbase.getModuleMap ((String) iter.next ()); + Map moduleMap = jbase.getModuleMap(); + Module module = moduleMap.get(iter.next()); + texta.append(" Module: " + module.getName() + " " + module.getRelease() + eol); + } + // Reporting Handlers makes no sense in the viewer app; skip + String rights = app.getRights(); + if (rights != null) { + texta.append(" Rights: " + rights + eol); + } + } - /** - * Saves the information to a file - */ - private void saveInfo() { - PrintWriter wtr = doSaveDialog(); - if (wtr == null) { - return; - } - OutputHandler handler = selectHandler(); - try { - handler.setWriter(wtr); - handler.show(_app); - wtr.close(); - } catch (Exception e) { - JOptionPane.showMessageDialog(this, e.getMessage(), - "Error writing file", JOptionPane.ERROR_MESSAGE); - } - } + /** Saves the information to a file */ + private void saveInfo() { + PrintWriter wtr = doSaveDialog(); + if (wtr == null) { + return; + } + OutputHandler handler = selectHandler(); + try { + handler.setWriter(wtr); + handler.show(_app); + wtr.close(); + } catch (Exception e) { + JOptionPane.showMessageDialog( + this, e.getMessage(), "Error writing file", JOptionPane.ERROR_MESSAGE); + } + } } diff --git a/jhove-apps/src/main/java/edu/harvard/hul/ois/jhove/viewer/ConfigWindow.java b/jhove-apps/src/main/java/edu/harvard/hul/ois/jhove/viewer/ConfigWindow.java index c78de3b2d..deb059295 100644 --- a/jhove-apps/src/main/java/edu/harvard/hul/ois/jhove/viewer/ConfigWindow.java +++ b/jhove-apps/src/main/java/edu/harvard/hul/ois/jhove/viewer/ConfigWindow.java @@ -1,678 +1,609 @@ -/********************************************************************** - * Jhove - JSTOR/Harvard Object Validation Environment - * Copyright 2004 by JSTOR and the President and Fellows of Harvard College - **********************************************************************/ - +/** + * ******************************************************************** Jhove - JSTOR/Harvard Object + * Validation Environment Copyright 2004 by JSTOR and the President and Fellows of Harvard College + * ******************************************************************** + */ package edu.harvard.hul.ois.jhove.viewer; +import edu.harvard.hul.ois.jhove.ConfigHandler; +import edu.harvard.hul.ois.jhove.ConfigWriter; +import edu.harvard.hul.ois.jhove.ModuleInfo; +import java.awt.BorderLayout; +import java.awt.Color; import java.awt.Dimension; import java.awt.Font; import java.awt.GridLayout; -import java.awt.BorderLayout; -import java.awt.Color; import java.awt.event.*; -import java.util.*; import java.io.*; import java.text.ParseException; +import java.util.*; import javax.swing.*; import javax.swing.table.*; -//import javax.swing.border.*; -import edu.harvard.hul.ois.jhove.ConfigHandler; -import edu.harvard.hul.ois.jhove.ConfigWriter; -import edu.harvard.hul.ois.jhove.ModuleInfo; /** - * Window for high-level editing of the application's - * configuration file. - * - * @author Gary McGath + * Window for high-level editing of the application's configuration file. * + * @author Gary McGath */ - - public class ConfigWindow extends JDialog { - private final static String tempDirDefault = ""; - private final static String SANS_SERIF = "SansSerif"; - private final static String CENTER = "Center"; - private final static String CLASS = "Class"; - private final static String WEST = "West"; - private final static String EAST = "East"; - private final static String ADD = "Add"; - private final static String DELETE = "Delete"; - private final static String NORTH = "North"; - - /* The location of the config file. */ - private File _configFile; - - /* List of modules. An entry in the list is an - * array of 2 Strings, which are the fully qualified - * class and the init string respectively. */ - private List _modules; - - /* List of handlers. This is just a list of Strings - * giving the class. */ - private List _handlers; - - private int _bufferSize; - - private File _homeDir; - private File _tempDir; - private String _encoding; - - /* Display components. */ - - private Box _mainBox; - - private JTable _modTable; - private JTable _hanTable; - private AbstractTableModel _modTableModel; - private AbstractTableModel _hanTableModel; - private JLabel _homeLabel; - private JLabel _tempDirLabel; - private NumericField _bufSizeBox; - private JTextField _encodingBox; - - final static Color _tableColor = new Color (235, 230, 210); - final static Font _pathFont = new Font (SANS_SERIF, Font.PLAIN, 10); - final static Font _infoFont = new Font (SANS_SERIF, Font.PLAIN, 12); - - final static int HEIGHT = 640; - - /** - * Constructor. - * - * @param configFile The file which was opened for - * configuration information, or null - * to start with a clean slate. - * - * @param handler A ConfigHandler which has already - * processed the configuration file, - * or null if configFile is null. - */ - public ConfigWindow (JFrame parent, File configFile, ConfigHandler handler) - { - super (parent, "Jhove Configuration", true); - setDefaultCloseOperation (WindowConstants.DISPOSE_ON_CLOSE); - _configFile = configFile; - if (handler != null) { - _modules = handler.getModule (); - _handlers = handler.getHandler (); - _bufferSize = handler.getBufferSize (); - String dir = handler.getJhoveHome(); - if (dir != null) { - _homeDir = new File (dir); - } - dir = handler.getTempDir(); - if (dir != null) { - _tempDir = new File (dir); - } - _encoding = handler.getEncoding (); - } - else { - // Set up defaults - _modules = new ArrayList<> (10); - _handlers = new ArrayList<> (5); - _bufferSize = -1; - _homeDir = null; - _tempDir = null; - _encoding = null; - } - - // Set up a Box container for the window top level - _mainBox = Box.createVerticalBox (); - getContentPane().setLayout (new BorderLayout ()); - getContentPane().add (_mainBox, CENTER); - _mainBox.setBorder (BorderFactory.createLineBorder(Color.BLACK)); - - // Keep its size reasonable, taking screen size into account - java.awt.Rectangle screenRect = MainScreen.mainBounds (); - int maxHeight = screenRect.height - 200; - if (maxHeight > HEIGHT) { - maxHeight = HEIGHT; - } - _mainBox.setMaximumSize (new Dimension (500, maxHeight)); - _mainBox.setPreferredSize (new Dimension (400, maxHeight)); - - addModuleTable (); - _mainBox.add(Box.createRigidArea(new Dimension(0, 6))); - addHandlerTable (); - addHomeDir (); - addTempDir (); - addEncoding (); - addBufferSize (); - addSaveCancel (); - pack (); + private static final String tempDirDefault = ""; + private static final String SANS_SERIF = "SansSerif"; + private static final String CENTER = "Center"; + private static final String CLASS = "Class"; + private static final String WEST = "West"; + private static final String EAST = "East"; + private static final String ADD = "Add"; + private static final String DELETE = "Delete"; + private static final String NORTH = "North"; + + /* The location of the config file. */ + private File _configFile; + + /* List of modules. An entry in the list is an + * array of 2 Strings, which are the fully qualified + * class and the init string respectively. */ + private List _modules; + + /* List of handlers. This is just a list of Strings + * giving the class. */ + private List _handlers; + + private int _bufferSize; + + private File _homeDir; + private File _tempDir; + private String _encoding; + + /* Display components. */ + + private Box _mainBox; + + private JTable _modTable; + private JTable _hanTable; + private AbstractTableModel _modTableModel; + private AbstractTableModel _hanTableModel; + private JLabel _homeLabel; + private JLabel _tempDirLabel; + private NumericField _bufSizeBox; + private JTextField _encodingBox; + + static final Color _tableColor = new Color(235, 230, 210); + static final Font _pathFont = new Font(SANS_SERIF, Font.PLAIN, 10); + static final Font _infoFont = new Font(SANS_SERIF, Font.PLAIN, 12); + + static final int HEIGHT = 640; + + /** + * Constructor. + * + * @param configFile The file which was opened for configuration information, or null to start + * with a clean slate. + * @param handler A ConfigHandler which has already processed the configuration file, or null if + * configFile is null. + */ + public ConfigWindow(JFrame parent, File configFile, ConfigHandler handler) { + super(parent, "Jhove Configuration", true); + setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE); + _configFile = configFile; + if (handler != null) { + _modules = handler.getModule(); + _handlers = handler.getHandler(); + _bufferSize = handler.getBufferSize(); + String dir = handler.getJhoveHome(); + if (dir != null) { + _homeDir = new File(dir); + } + dir = handler.getTempDir(); + if (dir != null) { + _tempDir = new File(dir); + } + _encoding = handler.getEncoding(); + } else { + // Set up defaults + _modules = new ArrayList<>(10); + _handlers = new ArrayList<>(5); + _bufferSize = -1; + _homeDir = null; + _tempDir = null; + _encoding = null; } - - - - /* Create a JTable of two columns which lets the - * user add and delete modules, and add it to - * the window. - * Called by the constructor. - */ - private void addModuleTable () - { - JPanel panel = new JPanel (); - panel.setLayout (new BorderLayout ()); - _mainBox.add (panel); - // Use an anonymous class to implement the TableModel - _modTableModel = new AbstractTableModel () { - @Override - public int getRowCount () - { - return _modules.size (); - } - @Override - public int getColumnCount () - { - return 2; - } - @Override - public boolean isCellEditable(int row, int col) - { - return true; - } - @Override - public Object getValueAt (int row, int column) - { - ModuleInfo modInfo = _modules.get (row); - String[] tuple = { modInfo.clas, modInfo.init}; - return tuple[column]; + + // Set up a Box container for the window top level + _mainBox = Box.createVerticalBox(); + getContentPane().setLayout(new BorderLayout()); + getContentPane().add(_mainBox, CENTER); + _mainBox.setBorder(BorderFactory.createLineBorder(Color.BLACK)); + + // Keep its size reasonable, taking screen size into account + java.awt.Rectangle screenRect = MainScreen.mainBounds(); + int maxHeight = screenRect.height - 200; + if (maxHeight > HEIGHT) { + maxHeight = HEIGHT; + } + _mainBox.setMaximumSize(new Dimension(500, maxHeight)); + _mainBox.setPreferredSize(new Dimension(400, maxHeight)); + + addModuleTable(); + _mainBox.add(Box.createRigidArea(new Dimension(0, 6))); + addHandlerTable(); + addHomeDir(); + addTempDir(); + addEncoding(); + addBufferSize(); + addSaveCancel(); + pack(); + } + + /* Create a JTable of two columns which lets the + * user add and delete modules, and add it to + * the window. + * Called by the constructor. + */ + private void addModuleTable() { + JPanel panel = new JPanel(); + panel.setLayout(new BorderLayout()); + _mainBox.add(panel); + // Use an anonymous class to implement the TableModel + _modTableModel = + new AbstractTableModel() { + @Override + public int getRowCount() { + return _modules.size(); + } + + @Override + public int getColumnCount() { + return 2; + } + + @Override + public boolean isCellEditable(int row, int col) { + return true; + } + + @Override + public Object getValueAt(int row, int column) { + ModuleInfo modInfo = _modules.get(row); + String[] tuple = {modInfo.clas, modInfo.init}; + return tuple[column]; + } + + @Override + public void setValueAt(Object obj, int row, int column) { + ModuleInfo modInfo = _modules.get(row); + if (column == 0 && obj instanceof String) { + modInfo.clas = (String) obj; } - @Override - public void setValueAt (Object obj, int row, int column) - { - ModuleInfo modInfo = _modules.get(row); - if (column == 0 && obj instanceof String) { - modInfo.clas = (String) obj; - } - if (column == 1 && obj instanceof String) { - modInfo.init = (String) obj; - } + if (column == 1 && obj instanceof String) { + modInfo.init = (String) obj; } + } }; - - _modTable = new JTable (_modTableModel); -// _modTable.setMaximumSize(new Dimension (250, 150)); - _modTable.setSelectionMode (ListSelectionModel.SINGLE_SELECTION); - _modTable.setCellSelectionEnabled (true); - _modTable.setBackground (_tableColor); - JScrollPane modScrollPane = new JScrollPane (_modTable); - - TableColumnModel colMod = _modTable.getColumnModel(); - colMod.getColumn(0).setHeaderValue(CLASS); - colMod.getColumn(1).setHeaderValue("Init"); - _modTable.doLayout (); - // Add a panel with the modules caption and a couple of buttons. - JPanel topPanel = new JPanel (); - topPanel.setLayout (new BorderLayout ()); - topPanel.add (new JLabel ("Modules:"), WEST); - - // Squeeze the buttons over to the right - JPanel rightPanel = new JPanel (); - topPanel.add (rightPanel, EAST); - JButton addButton = new JButton (ADD); - addButton.addActionListener ( - new ActionListener () { - @Override - public void actionPerformed (ActionEvent e) - { - addModule (); - } - } - ); - rightPanel.add (addButton); - JButton delButton = new JButton (DELETE); - delButton.addActionListener ( - new ActionListener () { - @Override - public void actionPerformed (ActionEvent e) - { - deleteModule (); - } - } - ); - - // Make both buttons the same size - addButton.setMinimumSize (delButton.getMinimumSize ()); - addButton.setPreferredSize (delButton.getPreferredSize ()); - - rightPanel.add (delButton); - panel.add (topPanel, NORTH); - panel.add (modScrollPane, CENTER); - } - - - /* Create a JTable of one column which lets the - * user add and delete handlers, and add it to - * the window. - * Called by the constructor. - */ - private void addHandlerTable () - { - JPanel panel = new JPanel (); - panel.setLayout (new BorderLayout ()); - _mainBox.add (panel); - // Use an anonymous class to implement the TableModel - _hanTableModel = new AbstractTableModel () { - @Override - public int getRowCount () - { - return _handlers.size (); - } - @Override - public int getColumnCount () - { - return 1; - } - @Override - public boolean isCellEditable(int row, int col) - { - return true; + _modTable = new JTable(_modTableModel); + // _modTable.setMaximumSize(new Dimension (250, 150)); + _modTable.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); + _modTable.setCellSelectionEnabled(true); + _modTable.setBackground(_tableColor); + JScrollPane modScrollPane = new JScrollPane(_modTable); + + TableColumnModel colMod = _modTable.getColumnModel(); + colMod.getColumn(0).setHeaderValue(CLASS); + colMod.getColumn(1).setHeaderValue("Init"); + _modTable.doLayout(); + // Add a panel with the modules caption and a couple of buttons. + JPanel topPanel = new JPanel(); + topPanel.setLayout(new BorderLayout()); + topPanel.add(new JLabel("Modules:"), WEST); + + // Squeeze the buttons over to the right + JPanel rightPanel = new JPanel(); + topPanel.add(rightPanel, EAST); + JButton addButton = new JButton(ADD); + addButton.addActionListener( + new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + addModule(); + } + }); + rightPanel.add(addButton); + JButton delButton = new JButton(DELETE); + delButton.addActionListener( + new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + deleteModule(); + } + }); + + // Make both buttons the same size + addButton.setMinimumSize(delButton.getMinimumSize()); + addButton.setPreferredSize(delButton.getPreferredSize()); + + rightPanel.add(delButton); + panel.add(topPanel, NORTH); + panel.add(modScrollPane, CENTER); + } + + /* Create a JTable of one column which lets the + * user add and delete handlers, and add it to + * the window. + * Called by the constructor. + */ + private void addHandlerTable() { + JPanel panel = new JPanel(); + panel.setLayout(new BorderLayout()); + _mainBox.add(panel); + // Use an anonymous class to implement the TableModel + _hanTableModel = + new AbstractTableModel() { + @Override + public int getRowCount() { + return _handlers.size(); + } + + @Override + public int getColumnCount() { + return 1; + } + + @Override + public boolean isCellEditable(int row, int col) { + return true; + } + + @Override + public Object getValueAt(int row, int column) { + String[] tuple = _handlers.get(row); + if (tuple != null) { + return tuple[0]; } - @Override - public Object getValueAt (int row, int column) - { - String[] tuple = _handlers.get (row); - if (tuple != null) { - return tuple[0]; - } - return ""; - } - @Override - public void setValueAt (Object obj, int row, int column) - { - if (obj instanceof String) { - String[] stuff = new String[] { (String) obj }; - _handlers.set (row, stuff); - } + return ""; + } + + @Override + public void setValueAt(Object obj, int row, int column) { + if (obj instanceof String) { + String[] stuff = new String[] {(String) obj}; + _handlers.set(row, stuff); } + } }; - - _hanTable = new JTable (_hanTableModel); - _hanTable.setSelectionMode (ListSelectionModel.SINGLE_SELECTION); - _hanTable.setCellSelectionEnabled (true); - _hanTable.setBackground (_tableColor); - JScrollPane hanScrollPane = new JScrollPane (_hanTable); - - TableColumnModel colMod = _hanTable.getColumnModel(); - colMod.getColumn(0).setHeaderValue(CLASS); - _hanTable.doLayout (); - // Add a panel with the modules caption and a couple of buttons. - JPanel topPanel = new JPanel (); - topPanel.setLayout (new BorderLayout ()); - topPanel.add (new JLabel ("Handlers:"), WEST); - - // Squeeze the buttons over to the right - JPanel rightPanel = new JPanel (); - topPanel.add (rightPanel, EAST); - JButton addButton = new JButton (ADD); - addButton.addActionListener ( - new ActionListener () { - @Override - public void actionPerformed (ActionEvent e) - { - addHandler (); - } - } - ); - rightPanel.add (addButton); - JButton delButton = new JButton (DELETE); - delButton.addActionListener ( - new ActionListener () { - @Override - public void actionPerformed (ActionEvent e) - { - deleteHandler (); - } - } - ); - - // Make both buttons the same size - addButton.setMinimumSize (delButton.getMinimumSize ()); - addButton.setPreferredSize (delButton.getPreferredSize ()); - rightPanel.add (delButton); - panel.add (topPanel, NORTH); - panel.add (hanScrollPane, CENTER); + _hanTable = new JTable(_hanTableModel); + _hanTable.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); + _hanTable.setCellSelectionEnabled(true); + _hanTable.setBackground(_tableColor); + JScrollPane hanScrollPane = new JScrollPane(_hanTable); + + TableColumnModel colMod = _hanTable.getColumnModel(); + colMod.getColumn(0).setHeaderValue(CLASS); + _hanTable.doLayout(); + // Add a panel with the modules caption and a couple of buttons. + JPanel topPanel = new JPanel(); + topPanel.setLayout(new BorderLayout()); + topPanel.add(new JLabel("Handlers:"), WEST); + + // Squeeze the buttons over to the right + JPanel rightPanel = new JPanel(); + topPanel.add(rightPanel, EAST); + JButton addButton = new JButton(ADD); + addButton.addActionListener( + new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + addHandler(); + } + }); + rightPanel.add(addButton); + JButton delButton = new JButton(DELETE); + delButton.addActionListener( + new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + deleteHandler(); + } + }); + + // Make both buttons the same size + addButton.setMinimumSize(delButton.getMinimumSize()); + addButton.setPreferredSize(delButton.getPreferredSize()); + + rightPanel.add(delButton); + panel.add(topPanel, NORTH); + panel.add(hanScrollPane, CENTER); + } + + /* Add a label and text edit box for the encoding */ + private void addEncoding() { + JPanel panel = new JPanel(); + _mainBox.add(panel); + panel.add(new JLabel("Default encoding: ")); + _encodingBox = new JTextField(_encoding == null ? "" : _encoding, 14); + panel.add(_encodingBox); + } + + /* Add a label and text edit box for the buffer size */ + private void addBufferSize() { + JPanel panel = new JPanel(); + _mainBox.add(panel); + panel.add(new JLabel("Buffer size (-1 for default): ")); + _bufSizeBox = new NumericField(_bufferSize); + panel.add(_bufSizeBox); + } + + /* Add a button and file path string for home directory. + * A home directory is required, but there may not be one + * initially. + * Called by the constructor. */ + private void addHomeDir() { + JPanel panel = new JPanel(); + _mainBox.add(panel); + panel.setLayout(new GridLayout(1, 2)); + JButton homeButton = new JButton("Home directory..."); + String homeText = ""; + if (_homeDir != null) { + homeText = _homeDir.getPath(); } - - - - - /* Add a label and text edit box for the encoding */ - private void addEncoding () - { - JPanel panel = new JPanel (); - _mainBox.add (panel); - panel.add (new JLabel ("Default encoding: ")); - _encodingBox = new JTextField (_encoding == null ? "" : _encoding, 14); - panel.add (_encodingBox); + _homeLabel = new JLabel(homeText); + _homeLabel.setFont(_pathFont); + // Standard trick for not making button take up all available space + JPanel bpan = new JPanel(); + // panel.add (bpan); + panel.add(bpan); + bpan.add(homeButton); + homeButton.addActionListener( + new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + chooseHomeDir(); + } + }); + panel.add(_homeLabel); + // Both of these probably need to be class variables + } + + /* Add a button and file path string for temporary directory. + * Called by the constructor. */ + private void addTempDir() { + JPanel panel = new JPanel(); + _mainBox.add(panel); + panel.setLayout(new GridLayout(1, 2)); + JButton tempDirButton = new JButton("Temp directory..."); + String tempDirText = tempDirDefault; + if (_tempDir != null) { + tempDirText = _tempDir.getPath(); } - - - - /* Add a label and text edit box for the buffer size */ - private void addBufferSize () - { - JPanel panel = new JPanel (); - _mainBox.add (panel); - panel.add (new JLabel ("Buffer size (-1 for default): ")); - _bufSizeBox = new NumericField (_bufferSize); - panel.add (_bufSizeBox); - } - - /* Add a button and file path string for home directory. - * A home directory is required, but there may not be one - * initially. - * Called by the constructor. */ - private void addHomeDir () - { - JPanel panel = new JPanel (); - _mainBox.add (panel); - panel.setLayout (new GridLayout (1, 2)); - JButton homeButton = new JButton ("Home directory..."); - String homeText = ""; - if (_homeDir != null) { - homeText = _homeDir.getPath(); - } - _homeLabel = new JLabel(homeText); - _homeLabel.setFont (_pathFont); - // Standard trick for not making button take up all available space - JPanel bpan = new JPanel (); - //panel.add (bpan); - panel.add (bpan); - bpan.add (homeButton); - homeButton.addActionListener ( - new ActionListener () { - @Override - public void actionPerformed (ActionEvent e) - { - chooseHomeDir (); - } - } - ); - panel.add (_homeLabel); - // Both of these probably need to be class variables + _tempDirLabel = new JLabel(tempDirText); + _tempDirLabel.setFont(_pathFont); + // Standard trick for not making button take up all available space + JPanel bpan = new JPanel(); + panel.add(bpan); + bpan.add(tempDirButton); + tempDirButton.addActionListener( + new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + chooseTempDir(); + } + }); + panel.add(_tempDirLabel); + } + + /* Add OK and cancel buttons, and a little information */ + private void addSaveCancel() { + final ConfigWindow thiscw = this; + JPanel buttonPanel = new JPanel(); + getContentPane().add(buttonPanel, "South"); + buttonPanel.setLayout(new BorderLayout()); + JLabel changesLabel = new JLabel("Changes take effect on relaunch"); + changesLabel.setFont(_infoFont); + buttonPanel.add(changesLabel, NORTH); + + JPanel panel = new JPanel(); + buttonPanel.add(panel, CENTER); + panel.setLayout(new GridLayout(1, 3)); + + // Blank panel for positioning + JPanel bpan = new JPanel(); + panel.add(bpan); + + // Add OK button + bpan = new JPanel(); + JButton saveButton = new JButton("OK"); + getRootPane().setDefaultButton(saveButton); + saveButton.addActionListener( + new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + doSave(); + } + }); + bpan.add(saveButton); + panel.add(bpan); + + // Add cancel button + bpan = new JPanel(); + JButton cancelButton = new JButton("Cancel"); + cancelButton.addActionListener( + new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + thiscw.dispose(); + } + }); + + // Make both buttons the same size + saveButton.setMinimumSize(cancelButton.getMinimumSize()); + saveButton.setPreferredSize(cancelButton.getPreferredSize()); + bpan.add(cancelButton); + panel.add(bpan); + } + + /* Carry out the action of the "Save" button. */ + private void doSave() { + // Splitting off the output to a ConfigFileWriter class + // makes sense. + try { + // Update values from the editing components + try { + _bufSizeBox.commitEdit(); + } catch (ParseException e) { + return; // refuse to save if value is invalid + } + // Object bufSizeValue = _bufSizeBox.getValue (); + _bufferSize = ((Long) _bufSizeBox.getValue()).intValue(); + + _encoding = _encodingBox.getText(); + + // *** Writes to temp file for debugging + // ConfigWriter cw = new ConfigWriter (new File (_homeDir, "temp")); + ConfigWriter cw = new ConfigWriter(_configFile, this); + cw.writeFile(_modules, _handlers, _homeDir, _tempDir, _encoding, _bufferSize); + } catch (IOException e) { + JOptionPane.showMessageDialog( + this, e.getMessage(), "Can't create config", JOptionPane.ERROR_MESSAGE); } + // Close the window + dispose(); + } - /* Add a button and file path string for temporary directory. - * Called by the constructor. */ - private void addTempDir () - { - JPanel panel = new JPanel (); - _mainBox.add (panel); - panel.setLayout (new GridLayout (1, 2)); - JButton tempDirButton = new JButton ("Temp directory..."); - String tempDirText = tempDirDefault; - if (_tempDir != null) { - tempDirText = _tempDir.getPath(); - } - _tempDirLabel = new JLabel(tempDirText); - _tempDirLabel.setFont (_pathFont); - // Standard trick for not making button take up all available space - JPanel bpan = new JPanel (); - panel.add (bpan); - bpan.add (tempDirButton); - tempDirButton.addActionListener ( - new ActionListener () { - @Override - public void actionPerformed (ActionEvent e) - { - chooseTempDir (); - } - } - ); - panel.add (_tempDirLabel); + /* Choose the home directory. Since there must be one, + * there is no "default" button. */ + private void chooseHomeDir() { + JFileChooser chooser = new JFileChooser(); + if (_homeDir != null) { + chooser.setCurrentDirectory(_homeDir); } - - - /* Add OK and cancel buttons, and a little information */ - private void addSaveCancel () - { - final ConfigWindow thiscw = this; - JPanel buttonPanel = new JPanel (); - getContentPane().add (buttonPanel, "South"); - buttonPanel.setLayout (new BorderLayout ()); - JLabel changesLabel = new JLabel - ("Changes take effect on relaunch"); - changesLabel.setFont (_infoFont); - buttonPanel.add (changesLabel, NORTH); - - JPanel panel = new JPanel (); - buttonPanel.add (panel, CENTER); - panel.setLayout (new GridLayout (1, 3)); - - // Blank panel for positioning - JPanel bpan = new JPanel (); - panel.add (bpan); - - // Add OK button - bpan = new JPanel (); - JButton saveButton = new JButton ("OK"); - getRootPane().setDefaultButton (saveButton); - saveButton.addActionListener ( - new ActionListener () { - @Override - public void actionPerformed (ActionEvent e) - { - doSave (); - } - } - ); - bpan.add (saveButton); - panel.add (bpan); - - // Add cancel button - bpan = new JPanel (); - JButton cancelButton = new JButton ("Cancel"); - cancelButton.addActionListener ( - new ActionListener () { - @Override - public void actionPerformed (ActionEvent e) - { - thiscw.dispose (); - } - } - ); - - // Make both buttons the same size - saveButton.setMinimumSize (cancelButton.getMinimumSize ()); - saveButton.setPreferredSize (cancelButton.getPreferredSize ()); - bpan.add (cancelButton); - panel.add (bpan); - + chooser.setDialogTitle("Select Home Directory"); + chooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY); + if (chooser.showOpenDialog(this) == JFileChooser.APPROVE_OPTION) { + _homeDir = chooser.getSelectedFile(); + _homeLabel.setText(_homeDir.getPath()); } + } - - /* Carry out the action of the "Save" button. */ - private void doSave () - { - // Splitting off the output to a ConfigFileWriter class - // makes sense. - try { - // Update values from the editing components - try { - _bufSizeBox.commitEdit (); - } - catch (ParseException e) { - return; // refuse to save if value is invalid - } - //Object bufSizeValue = _bufSizeBox.getValue (); - _bufferSize = ((Long) _bufSizeBox.getValue ()).intValue (); - - _encoding = _encodingBox.getText (); - - // *** Writes to temp file for debugging - //ConfigWriter cw = new ConfigWriter (new File (_homeDir, "temp")); - ConfigWriter cw = new ConfigWriter (_configFile, this); - cw.writeFile (_modules, _handlers, - _homeDir, _tempDir, _encoding, _bufferSize); - } - catch (IOException e) { - JOptionPane.showMessageDialog(this, - e.getMessage (), - "Can't create config", - JOptionPane.ERROR_MESSAGE); - } - - // Close the window - dispose (); + /* Choose the temp directory. Allow a "default" selection. */ + private void chooseTempDir() { + final JFileChooser chooser = new JFileChooser(); + if (_homeDir != null) { + chooser.setCurrentDirectory(_tempDir); } - - /* Choose the home directory. Since there must be one, - * there is no "default" button. */ - private void chooseHomeDir () - { - JFileChooser chooser = new JFileChooser (); - if (_homeDir != null) { - chooser.setCurrentDirectory (_homeDir); - } - chooser.setDialogTitle ("Select Home Directory"); - chooser.setFileSelectionMode (JFileChooser.DIRECTORIES_ONLY); - if (chooser.showOpenDialog (this) == JFileChooser.APPROVE_OPTION) { - _homeDir = chooser.getSelectedFile(); - _homeLabel.setText (_homeDir.getPath ()); - } - } - - - /* Choose the temp directory. Allow a "default" selection. */ - private void chooseTempDir () - { - final JFileChooser chooser = new JFileChooser (); - if (_homeDir != null) { - chooser.setCurrentDirectory (_tempDir); - } - chooser.setDialogTitle ("Select Temporary Directory"); - // Create a custom panel so we can add the Default button. - JPanel accessory = new JPanel (); - accessory.setPreferredSize(new Dimension (160, 40)); - JButton defaultButton = new JButton ("System Default"); - defaultButton.addActionListener ( - new ActionListener () { - @Override - public void actionPerformed (ActionEvent e) - { - // Exit the dialog and clear _tempDir - _tempDir = null; - _tempDirLabel.setText (tempDirDefault); - chooser.cancelSelection (); - } - } - ); - accessory.add (defaultButton); - chooser.setAccessory (accessory); - - chooser.setFileSelectionMode (JFileChooser.DIRECTORIES_ONLY); - if (chooser.showOpenDialog (this) == JFileChooser.APPROVE_OPTION) { - _tempDir = chooser.getSelectedFile(); - _tempDirLabel.setText (_tempDir.getPath ()); - } + chooser.setDialogTitle("Select Temporary Directory"); + // Create a custom panel so we can add the Default button. + JPanel accessory = new JPanel(); + accessory.setPreferredSize(new Dimension(160, 40)); + JButton defaultButton = new JButton("System Default"); + defaultButton.addActionListener( + new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + // Exit the dialog and clear _tempDir + _tempDir = null; + _tempDirLabel.setText(tempDirDefault); + chooser.cancelSelection(); + } + }); + accessory.add(defaultButton); + chooser.setAccessory(accessory); + + chooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY); + if (chooser.showOpenDialog(this) == JFileChooser.APPROVE_OPTION) { + _tempDir = chooser.getSelectedFile(); + _tempDirLabel.setText(_tempDir.getPath()); } - - /* Add a blank item for a new module */ - private void addModule () - { - ListSelectionModel ls = _modTable.getSelectionModel (); - int selRow = ls.getMinSelectionIndex(); - // If there's no selection, append to the end - if (selRow < 0) { - selRow = _modules.size (); - } -// String[] tuple = new String[2]; -// tuple[0] = ""; // class -// tuple[1] = null; // init - ModuleInfo modInfo = new ModuleInfo ("", null); - _modules.add (selRow, modInfo); - _modTableModel.fireTableRowsInserted(selRow, selRow); + } + + /* Add a blank item for a new module */ + private void addModule() { + ListSelectionModel ls = _modTable.getSelectionModel(); + int selRow = ls.getMinSelectionIndex(); + // If there's no selection, append to the end + if (selRow < 0) { + selRow = _modules.size(); } - - - - /* Delete the selected module line */ - private void deleteModule () - { - ListSelectionModel ls = _modTable.getSelectionModel (); - int selRow = ls.getMinSelectionIndex(); - if (selRow < 0) { - return; // no selection - } - _modules.remove (selRow); - _modTableModel.fireTableRowsDeleted (selRow, selRow); + // String[] tuple = new String[2]; + // tuple[0] = ""; // class + // tuple[1] = null; // init + ModuleInfo modInfo = new ModuleInfo("", null); + _modules.add(selRow, modInfo); + _modTableModel.fireTableRowsInserted(selRow, selRow); + } + + /* Delete the selected module line */ + private void deleteModule() { + ListSelectionModel ls = _modTable.getSelectionModel(); + int selRow = ls.getMinSelectionIndex(); + if (selRow < 0) { + return; // no selection } - - - /* Add a blank item for a new handler */ - private void addHandler () - { - ListSelectionModel ls = _hanTable.getSelectionModel (); - int selRow = ls.getMinSelectionIndex(); - // If there's no selection, append to the end - if (selRow < 0) { - selRow = _handlers.size (); - } - String[] tuple = { "" }; - _handlers.add (selRow, tuple); - _hanTableModel.fireTableRowsInserted(selRow, selRow); + _modules.remove(selRow); + _modTableModel.fireTableRowsDeleted(selRow, selRow); + } + + /* Add a blank item for a new handler */ + private void addHandler() { + ListSelectionModel ls = _hanTable.getSelectionModel(); + int selRow = ls.getMinSelectionIndex(); + // If there's no selection, append to the end + if (selRow < 0) { + selRow = _handlers.size(); } - - - - /* Delete the selected handler line */ - private void deleteHandler () - { - ListSelectionModel ls = _hanTable.getSelectionModel (); - int selRow = ls.getMinSelectionIndex(); - if (selRow < 0) { - return; // no selection - } - _handlers.remove (selRow); - _hanTableModel.fireTableRowsDeleted (selRow, selRow); + String[] tuple = {""}; + _handlers.add(selRow, tuple); + _hanTableModel.fireTableRowsInserted(selRow, selRow); + } + + /* Delete the selected handler line */ + private void deleteHandler() { + ListSelectionModel ls = _hanTable.getSelectionModel(); + int selRow = ls.getMinSelectionIndex(); + if (selRow < 0) { + return; // no selection } - - - - /* When the user chooses to close and save, or just save, - * we come here. - * - * @param configFile The location to save the file, or none - * if a simple "save" was selected. If - * configFile is non-null, it becomes the - * new default location. - */ -// private void saveConfig (File configFile) -// { -// if (configFile != null) { -// _configFile = configFile; -// } -// -// // _configFile may be null. In that case we must put up -// // a dialog to select the config file here, and exit the function -// // if the user cancels. -// try { -// FileOutputStream ostrm = new FileOutputStream (configFile); -// } -// catch (IOException e) { -// JOptionPane.showMessageDialog (this, -// e.getMessage(), "Error saving config", -// JOptionPane.ERROR_MESSAGE); -// -// } -// } - -// private File doConfigFileDialog () -// { -// JFileChooser chooser = new JFileChooser (); -// chooser.setFileSelectionMode (JFileChooser.DIRECTORIES_ONLY); -// if (chooser.showOpenDialog (this) == JFileChooser.APPROVE_OPTION) { -// return chooser.getSelectedFile(); -// } -// -// return null; // placeholder -// } + _handlers.remove(selRow); + _hanTableModel.fireTableRowsDeleted(selRow, selRow); + } + + /* When the user chooses to close and save, or just save, + * we come here. + * + * @param configFile The location to save the file, or none + * if a simple "save" was selected. If + * configFile is non-null, it becomes the + * new default location. + */ + // private void saveConfig (File configFile) + // { + // if (configFile != null) { + // _configFile = configFile; + // } + // + // // _configFile may be null. In that case we must put up + // // a dialog to select the config file here, and exit the function + // // if the user cancels. + // try { + // FileOutputStream ostrm = new FileOutputStream (configFile); + // } + // catch (IOException e) { + // JOptionPane.showMessageDialog (this, + // e.getMessage(), "Error saving config", + // JOptionPane.ERROR_MESSAGE); + // + // } + // } + + // private File doConfigFileDialog () + // { + // JFileChooser chooser = new JFileChooser (); + // chooser.setFileSelectionMode (JFileChooser.DIRECTORIES_ONLY); + // if (chooser.showOpenDialog (this) == JFileChooser.APPROVE_OPTION) { + // return chooser.getSelectedFile(); + // } + // + // return null; // placeholder + // } } diff --git a/jhove-apps/src/main/java/edu/harvard/hul/ois/jhove/viewer/InfoWindow.java b/jhove-apps/src/main/java/edu/harvard/hul/ois/jhove/viewer/InfoWindow.java index b9798ee0f..a8e0e1578 100644 --- a/jhove-apps/src/main/java/edu/harvard/hul/ois/jhove/viewer/InfoWindow.java +++ b/jhove-apps/src/main/java/edu/harvard/hul/ois/jhove/viewer/InfoWindow.java @@ -1,262 +1,230 @@ -/********************************************************************** - * Jhove - JSTOR/Harvard Object Validation Environment - * Copyright 2003 by JSTOR and the President and Fellows of Harvard College - **********************************************************************/ - +/** + * ******************************************************************** Jhove - JSTOR/Harvard Object + * Validation Environment Copyright 2003 by JSTOR and the President and Fellows of Harvard College + * ******************************************************************** + */ package edu.harvard.hul.ois.jhove.viewer; -import java.awt.HeadlessException; - -import java.util.*; -import java.io.*; +import edu.harvard.hul.ois.jhove.*; import java.awt.Dimension; +import java.awt.HeadlessException; import java.awt.Toolkit; -import java.awt.event.KeyEvent; -import java.awt.event.ActionListener; import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.KeyEvent; +import java.io.*; import java.text.SimpleDateFormat; +import java.util.*; import javax.swing.*; -import edu.harvard.hul.ois.jhove.*; /** - * An abstract superclass for windows that display information - * and can be saved to a file. - * + * An abstract superclass for windows that display information and can be saved to a file. + * * @author Gary McGath */ -public abstract class InfoWindow extends JFrame -{ - protected App _app; - private JhoveBase _base; - private JMenuItem _saveItem; - private JMenuItem _closeItem; - private JComboBox _handlerBox; - private JComboBox _encodingBox; - private static String _lastEncoding; - private static String _lastHandler; - - protected static final String eol = System.getProperty("line.separator"); - private static final String[] encodings = - { "UTF-8", "ISO-8859-1", "Cp1252", "MacRoman"}; - protected SimpleDateFormat _dateFmt; +public abstract class InfoWindow extends JFrame { + protected App _app; + private JhoveBase _base; + private JMenuItem _saveItem; + private JMenuItem _closeItem; + private JComboBox _handlerBox; + private JComboBox _encodingBox; + private static String _lastEncoding; + private static String _lastHandler; - private static final String FILE_NOT_SAVED = "File not saved"; - - /** - * - * Constructor. - * The window is created with a File menu that has - * "Save as" and "Close" items. - * - * @param title Window title. Will be truncated to 32 characters. - * @param app The associated App object. - * @param base The associated JhoveBase object. - * - * @throws java.awt.HeadlessException - */ - public InfoWindow(String title, App app, JhoveBase base) - throws HeadlessException { - super(title); + protected static final String eol = System.getProperty("line.separator"); + private static final String[] encodings = {"UTF-8", "ISO-8859-1", "Cp1252", "MacRoman"}; + protected SimpleDateFormat _dateFmt; - // Avoid silly window titles from excessively long URI's - if (title.length () > 32) { - setTitle(title.substring(0,29) + "..."); - } - _app = app; - _base = base; - JMenuBar menuBar = new JMenuBar (); - JMenu fileMenu = new JMenu ("File"); - menuBar.add (fileMenu); - _saveItem = new JMenuItem ("Save as..."); - fileMenu.add (_saveItem); - - _closeItem = new JMenuItem ("Close"); - fileMenu.add (_closeItem); - // Make mnemonic control-W, command-W, or whatever-W - _closeItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_W, - Toolkit.getDefaultToolkit().getMenuShortcutKeyMask())); - _closeItem.addActionListener (new ActionListener() { - @Override - public void actionPerformed (ActionEvent e) { - closeFromMenu (); - } + private static final String FILE_NOT_SAVED = "File not saved"; + + /** + * Constructor. The window is created with a File menu that has "Save as" and "Close" items. + * + * @param title Window title. Will be truncated to 32 characters. + * @param app The associated App object. + * @param base The associated JhoveBase object. + * @throws java.awt.HeadlessException + */ + public InfoWindow(String title, App app, JhoveBase base) throws HeadlessException { + super(title); + + // Avoid silly window titles from excessively long URI's + if (title.length() > 32) { + setTitle(title.substring(0, 29) + "..."); + } + _app = app; + _base = base; + JMenuBar menuBar = new JMenuBar(); + JMenu fileMenu = new JMenu("File"); + menuBar.add(fileMenu); + _saveItem = new JMenuItem("Save as..."); + fileMenu.add(_saveItem); + + _closeItem = new JMenuItem("Close"); + fileMenu.add(_closeItem); + // Make mnemonic control-W, command-W, or whatever-W + _closeItem.setAccelerator( + KeyStroke.getKeyStroke( + KeyEvent.VK_W, Toolkit.getDefaultToolkit().getMenuShortcutKeyMask())); + _closeItem.addActionListener( + new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + closeFromMenu(); + } }); - - setDefaultCloseOperation (WindowConstants.HIDE_ON_CLOSE); - setJMenuBar (menuBar); - _dateFmt = new SimpleDateFormat ("yyyy-MM-dd"); + + setDefaultCloseOperation(WindowConstants.HIDE_ON_CLOSE); + setJMenuBar(menuBar); + _dateFmt = new SimpleDateFormat("yyyy-MM-dd"); + } + + /** + * Sets the ActionListener for the "Save as" menu item. Subclasses need to call this with an + * appropriate ActionListener in order to make the menu item functional. + */ + protected void setSaveActionListener(ActionListener listener) { + _saveItem.addActionListener(listener); + } + + /** + * Puts up a dialog to save the file. If the user requests a file, deletes any old file with the + * same name, creates the new file, and returns a PrintWriter to the file. The save dialog is + * customized with two JComboBoxes. One lets the user select a character encoding, which is used + * by the PrintWriter; the other lets the user choose an OutputHandler to control the output + * format (e.g., text or HTML). The encodings shown in a JComboBox are UTF-8, ISO-8859-1, Cp1252, + * MacRoman, and the default encoding for the locale (if different from the above), but the user + * can type in other encodings. If an unknown encoding is selected, an error dialog will be + * displayed. OutputHandlers other than the ones known to the application can't be specified (what + * would the application do with them?) + */ + protected PrintWriter doSaveDialog() { + JFileChooser saver = new JFileChooser(); + // On Mac OS, make packages and .apps opaque. + JhoveWindow.makeChooserOpaque(saver); + + File lastDir = _base.getSaveDirectory(); + if (lastDir != null) { + saver.setCurrentDirectory(lastDir); } - /** Sets the ActionListener for the "Save as" menu item. - * Subclasses need to call this with an appropriate - * ActionListener in order to make the menu item - * functional. - */ - protected void setSaveActionListener (ActionListener listener) - { - _saveItem.addActionListener (listener); + // Create a custom panel so we can set options. + JPanel accessory = new JPanel(); + accessory.setPreferredSize(new Dimension(180, 120)); + + // Build list of handlers into a popup menu + Vector handlerItems = new Vector<>(10); + for (OutputHandler han : _base.getHandlerList()) { + handlerItems.add(han.getName()); } - - /** Puts up a dialog to save the file. - * If the user requests a file, deletes any old file - * with the same name, creates the new file, and - * returns a PrintWriter to the file. - * The save dialog is customized with two - * JComboBoxes. One lets the user - * select a character encoding, which is used by - * the PrintWriter; the other lets the user - * choose an OutputHandler to control the output - * format (e.g., text or HTML). - * The encodings shown in a JComboBox are - * UTF-8, ISO-8859-1, Cp1252, MacRoman, and - * the default encoding for the locale (if different - * from the above), but the user can type in other - * encodings. If an unknown encoding is selected, - * an error dialog will be displayed. - * OutputHandlers other than the ones known - * to the application can't be specified (what would - * the application do with them?) - */ - protected PrintWriter doSaveDialog () - { - JFileChooser saver = new JFileChooser (); - // On Mac OS, make packages and .apps opaque. - JhoveWindow.makeChooserOpaque (saver); - - File lastDir = _base.getSaveDirectory (); - if (lastDir != null) { - saver.setCurrentDirectory(lastDir); - } - - // Create a custom panel so we can set options. - JPanel accessory = new JPanel (); - accessory.setPreferredSize(new Dimension (180, 120)); - - // Build list of handlers into a popup menu - Vector handlerItems = new Vector<> (10); - for (OutputHandler han : _base.getHandlerList()) { - handlerItems.add (han.getName ()); - } - _handlerBox = new JComboBox<>(handlerItems); - _handlerBox.setSize (120, 20); - accessory.add (new JLabel ("Choose output handler")); - if (_lastHandler != null) { - _handlerBox.setSelectedItem (_lastHandler); - } - accessory.add (_handlerBox); - - // Build a list of encodings into a popup menu. - // The default encoding must be the first. - Vector encItems = new Vector<> (5); - String defEnc = _base.getEncoding (); - if (defEnc != null) { - encItems.add (defEnc); - } - for (int i = 0; i < encodings.length; i++) { - String enc = encodings[i]; - if (!enc.equals (defEnc)) { - encItems.add (enc); - } + _handlerBox = new JComboBox<>(handlerItems); + _handlerBox.setSize(120, 20); + accessory.add(new JLabel("Choose output handler")); + if (_lastHandler != null) { + _handlerBox.setSelectedItem(_lastHandler); + } + accessory.add(_handlerBox); + + // Build a list of encodings into a popup menu. + // The default encoding must be the first. + Vector encItems = new Vector<>(5); + String defEnc = _base.getEncoding(); + if (defEnc != null) { + encItems.add(defEnc); + } + for (int i = 0; i < encodings.length; i++) { + String enc = encodings[i]; + if (!enc.equals(defEnc)) { + encItems.add(enc); + } + } + _encodingBox = new JComboBox<>(encItems); + if (_lastEncoding != null) { + _encodingBox.setSelectedItem(_lastEncoding); + } + _encodingBox.setSize(120, 20); + _encodingBox.setEditable(true); // Let user type in any encoding + accessory.add(new JLabel("Select encoding")); + accessory.add(_encodingBox); + + saver.setAccessory(accessory); + saver.setDialogTitle("Save information to file"); + int retval = saver.showSaveDialog(this); + if (retval == JFileChooser.APPROVE_OPTION) { + FileOutputStream os = null; + File file = null; + try { + _base.setSaveDirectory(saver.getCurrentDirectory()); + file = saver.getSelectedFile(); + if (file.exists()) { + int opt = + JOptionPane.showConfirmDialog( + this, + "That file already exists. Replace?", + "Replace", + JOptionPane.OK_CANCEL_OPTION); + if (opt != JOptionPane.OK_OPTION) { + return null; + } + // User requested replacement. Delete the old file. + if (!file.delete()) { + JOptionPane.showMessageDialog( + this, "Could not delete file", "File not deleted", JOptionPane.ERROR_MESSAGE); + return null; + } } - _encodingBox = new JComboBox<> (encItems); - if (_lastEncoding != null) { - _encodingBox.setSelectedItem (_lastEncoding); + file.createNewFile(); + String encoding = (String) _encodingBox.getSelectedItem(); + os = new FileOutputStream(file); + OutputStreamWriter writer = new OutputStreamWriter(os, encoding); + return new PrintWriter(writer); + } catch (UnsupportedEncodingException e) { + JOptionPane.showMessageDialog( + this, "Unknown encoding ", FILE_NOT_SAVED, JOptionPane.ERROR_MESSAGE); + // Get rid of the file + try { + if (os != null) { + os.close(); + } + file.delete(); + } catch (Exception f) { } - _encodingBox.setSize (120, 20); - _encodingBox.setEditable (true); // Let user type in any encoding - accessory.add (new JLabel ("Select encoding")); - accessory.add (_encodingBox); - saver.setAccessory(accessory); - saver.setDialogTitle("Save information to file"); - int retval = saver.showSaveDialog (this); - if (retval == JFileChooser.APPROVE_OPTION) { - FileOutputStream os = null; - File file = null; - try { - _base.setSaveDirectory (saver.getCurrentDirectory ()); - file = saver.getSelectedFile(); - if (file.exists ()) { - int opt = JOptionPane.showConfirmDialog - (this, "That file already exists. Replace?", - "Replace", - JOptionPane.OK_CANCEL_OPTION); - if (opt != JOptionPane.OK_OPTION) { - return null; - } - // User requested replacement. Delete the old file. - if (!file.delete ()) { - JOptionPane.showMessageDialog(this, - "Could not delete file", - "File not deleted", - JOptionPane.ERROR_MESSAGE); - return null; - } - } - file.createNewFile(); - String encoding = (String) _encodingBox.getSelectedItem(); - os = new FileOutputStream (file); - OutputStreamWriter writer = new OutputStreamWriter (os, encoding); - return new PrintWriter (writer); - } - catch (UnsupportedEncodingException e) { - JOptionPane.showMessageDialog(this, - "Unknown encoding ", - FILE_NOT_SAVED, - JOptionPane.ERROR_MESSAGE); - // Get rid of the file - try { - if (os != null) { - os.close (); - } - file.delete (); - } - catch (Exception f) {} - - return null; - } - catch (IOException e) { - JOptionPane.showMessageDialog(this, - e.getMessage (), - FILE_NOT_SAVED, - JOptionPane.ERROR_MESSAGE); - // Get rid of the file - try { - if (os != null) { - os.close (); - } - file.delete (); - } - catch (Exception f) {} - return null; - } + return null; + } catch (IOException e) { + JOptionPane.showMessageDialog( + this, e.getMessage(), FILE_NOT_SAVED, JOptionPane.ERROR_MESSAGE); + // Get rid of the file + try { + if (os != null) { + os.close(); + } + file.delete(); + } catch (Exception f) { } - return null; - } - - /** Sets up the OutputHandler from the JComboBox - * and returns it. Subclasses should call - * selectHandler to obtain an - * OutputHandler with which to produce data. - */ - protected OutputHandler selectHandler () - { - _lastHandler = (String) _handlerBox.getSelectedItem (); - OutputHandler handler = - _base.getHandlerMap().get (_lastHandler.toLowerCase ()); - _lastEncoding = (String) _encodingBox.getSelectedItem (); - handler.setEncoding (_lastEncoding); - handler.setApp (_app); - handler.setBase (_base); - return handler; - } - - /** - * Handler for the "Close" menu item. - * Simply hides the window without deleting it. - */ - protected void closeFromMenu () - { - setVisible (false); + return null; + } } + return null; + } + + /** + * Sets up the OutputHandler from the JComboBox and returns it. Subclasses should call + * selectHandler to obtain an OutputHandler with which to produce data. + */ + protected OutputHandler selectHandler() { + _lastHandler = (String) _handlerBox.getSelectedItem(); + OutputHandler handler = _base.getHandlerMap().get(_lastHandler.toLowerCase()); + _lastEncoding = (String) _encodingBox.getSelectedItem(); + handler.setEncoding(_lastEncoding); + handler.setApp(_app); + handler.setBase(_base); + return handler; + } + + /** Handler for the "Close" menu item. Simply hides the window without deleting it. */ + protected void closeFromMenu() { + setVisible(false); + } } diff --git a/jhove-apps/src/main/java/edu/harvard/hul/ois/jhove/viewer/JhoveWindow.java b/jhove-apps/src/main/java/edu/harvard/hul/ois/jhove/viewer/JhoveWindow.java index 3c4a56633..1e70c3789 100644 --- a/jhove-apps/src/main/java/edu/harvard/hul/ois/jhove/viewer/JhoveWindow.java +++ b/jhove-apps/src/main/java/edu/harvard/hul/ois/jhove/viewer/JhoveWindow.java @@ -1,25 +1,28 @@ -/********************************************************************** - * Jhove - JSTOR/Harvard Object Validation Environment - * Copyright 2003-2005 by JSTOR and the President and Fellows of Harvard College +/** + * ******************************************************************** Jhove - JSTOR/Harvard Object + * Validation Environment Copyright 2003-2005 by JSTOR and the President and Fellows of Harvard + * College * - * This program 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 2 of the License, or (at - * your option) any later version. - * - * This program 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. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 - * USA - **********************************************************************/ - + *

This program 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 2 + * of the License, or (at your option) any later version. + * + *

This program 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. + * + *

You should have received a copy of the GNU Lesser General Public License along with this + * program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA ******************************************************************** + */ package edu.harvard.hul.ois.jhove.viewer; +import edu.harvard.hul.ois.jhove.App; +import edu.harvard.hul.ois.jhove.Callback; +import edu.harvard.hul.ois.jhove.ConfigHandler; +import edu.harvard.hul.ois.jhove.JhoveBase; +import edu.harvard.hul.ois.jhove.MacStuff; +import edu.harvard.hul.ois.jhove.Module; import java.awt.Color; import java.awt.Container; import java.awt.Toolkit; @@ -44,7 +47,6 @@ import java.util.List; import java.util.Vector; import java.util.logging.Logger; - import javax.swing.ButtonGroup; import javax.swing.ImageIcon; import javax.swing.JFileChooser; @@ -59,800 +61,788 @@ import javax.swing.KeyStroke; import javax.xml.parsers.ParserConfigurationException; import javax.xml.parsers.SAXParserFactory; - import org.xml.sax.SAXException; import org.xml.sax.XMLReader; import org.xml.sax.helpers.XMLReaderFactory; -import edu.harvard.hul.ois.jhove.App; -import edu.harvard.hul.ois.jhove.Callback; -import edu.harvard.hul.ois.jhove.ConfigHandler; -import edu.harvard.hul.ois.jhove.JhoveBase; -import edu.harvard.hul.ois.jhove.MacStuff; -import edu.harvard.hul.ois.jhove.Module; - -/** - * Main window of JHoveViewer application. - */ +/** Main window of JHoveViewer application. */ public class JhoveWindow extends JFrame implements Callback, DropTargetListener { - private App _app; - private JhoveBase _base; - private AppInfoWindow _appInfoWin; - private ModuleInfoWindow _moduleInfoWin; - private JMenu _moduleSubmenu; - private JMenuItem _openFileItem; - private JMenuItem _openURLItem; - private JMenuItem _closeAllItem; - private ButtonGroup _moduleGroup; - private String syncStr = ""; // object just for synchronizing - private boolean _rawOutput; - private boolean _doChecksum; - - private ProgressWindow _progWind; - // private ConfigWindow _configWind; - private PrefsWindow _prefsWindow; - - private File _lastDir; - private String _selectedModule; - private ActionListener _moduleMenuListener; - private JPanel logo; - private ViewHandler _viewHandler; - - // Initial position for view windows. - // Stagger them by adding an increment each time. - // private static int viewWinXPos = 24; - // private static int viewWinYPos = 24; - // Original positions for cycling back to. - // private static final int viewWinOrigXPos = 24; - // private static final int viewWinOrigYPos = 24; - // private static final int viewWinXInc = 25; - // private static final int viewWinYInc = 22; - private static final int appInfoWinXPos = 50; - private static final int appInfoWinYPos = 45; - private static final int moduleInfoWinXPos = 100; - private static final int moduleInfoWinYPos = 90; - - private static final String NEVER = "never"; - private static final String CONFIG_ERROR = "Config Error"; - - /** Logger for a module class. */ - protected Logger _logger; - - /* Static instance of InvisibleFilenameFilter */ - private final InvisibleFilenameFilter invisibleFilter = new JhoveWindow.InvisibleFilenameFilter(); - - public JhoveWindow(App app, JhoveBase base) { - super("Jhove"); - _logger = Logger.getLogger("edu.harvard.hul.ois.jhove.viewer"); - _app = app; - _base = base; - _moduleMenuListener = new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { - _selectedModule = e.getActionCommand(); - } - }; - - _lastDir = null; - _moduleGroup = new ButtonGroup(); - addMenus(); - Container rootPane = getContentPane(); - // rootPane.setLayout (new GridLayout (4, 2)); - setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); - - // Define a Comparator function for Modules - Comparator modListComparator = new Comparator() { - @Override - public int compare(Module o1, Module o2) { - Module m1 = o1; - Module m2 = o2; - String name1 = m1.getName(); - String name2 = m2.getName(); - return String.CASE_INSENSITIVE_ORDER.compare(name1, name2); - } - }; - - // Build combo box of available modules - Vector moduleItems = new Vector<>(10); - java.util.List moduleList = base.getModuleList(); - // Clone the list so we can display it in sorted order - // without munging the app's preferred order - java.util.List menuModuleList = new ArrayList<>( - moduleList.size()); - menuModuleList.addAll(moduleList); - Collections.sort(menuModuleList, modListComparator); - Iterator iter = menuModuleList.iterator(); - moduleItems.add("(None selected)"); - JRadioButtonMenuItem modItem = null; - String itemName = null; - - while (iter.hasNext()) { - Module mod = iter.next(); - itemName = mod.getName(); - modItem = new JRadioButtonMenuItem(itemName); - modItem.setActionCommand(itemName); - modItem.addActionListener(_moduleMenuListener); - _moduleSubmenu.add(modItem); - _moduleGroup.add(modItem); - // moduleItems.add (mod.getName ()); - } - - logo = new JPanel(); - - // Add the image, which should be in jhove-logo.gif in - // the viewer directory - URL logoURL = JhoveWindow.class.getResource("jhovelogo.png"); - if (logoURL != null) { - ImageIcon icn = new ImageIcon(logoURL); - icn.setDescription("Jhove logo"); - setNormalBackground(); - JLabel logoLabel = new JLabel(icn); - logo.add(logoLabel); - } - - // Allow files to be dragged to the logo pane. - DropTarget dt = new DropTarget(logo, this); - - rootPane.add(logo); - pack(); - - // Set up a companion progress window. This will - // be hidden and displayed as needed. - ActionListener listener = new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { - _base.abort(); - } - }; - _progWind = new ProgressWindow(listener); - - // Set up a Handler which is tailored to this application. - _viewHandler = new ViewHandler(this, _app, _base); - } - - /** - * Set up the menu bar and menus. - */ - final private void addMenus() { - final JhoveWindow jthis = this; - JMenuBar menuBar = new JMenuBar(); - JMenu fileMenu = new JMenu("File"); - menuBar.add(fileMenu); - _openFileItem = new JMenuItem("Open file..."); - fileMenu.add(_openFileItem); - // The following allows accelerator modifier keys to be set which are - // appropriate to the host OS - _openFileItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_O, - Toolkit.getDefaultToolkit().getMenuShortcutKeyMask())); - - _openFileItem.addActionListener(new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { - pickAndAnalyzeFile(); - } - }); - _openURLItem = new JMenuItem("Open URL..."); - fileMenu.add(_openURLItem); - _openURLItem.addActionListener(new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { - pickAndAnalyzeURL(); - } - }); - - _closeAllItem = new JMenuItem("Close all document windows"); - fileMenu.add(_closeAllItem); - // Action listeners are added by document windows - - if (!MacStuff.isMacintosh()) { - // Token attempt at Mac friendliness: the Exit item - // in the File menu is redundant under OS X - JMenuItem quitItem = new JMenuItem("Exit"); - fileMenu.add(quitItem); - quitItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_Q, - Toolkit.getDefaultToolkit().getMenuShortcutKeyMask())); - quitItem.addActionListener(new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { - System.exit(0); - } - }); - } - - JMenu editMenu = new JMenu("Edit"); - menuBar.add(editMenu); - _moduleSubmenu = new JMenu("Select module"); - editMenu.add(_moduleSubmenu); - JRadioButtonMenuItem noModuleItem = new JRadioButtonMenuItem("(Any)"); - noModuleItem.setActionCommand(""); - noModuleItem.setSelected(true); - noModuleItem.addActionListener(_moduleMenuListener); - _moduleSubmenu.add(noModuleItem); - _moduleGroup.add(noModuleItem); - _selectedModule = ""; - // Modules will be added later - - JMenuItem editConfigItem = new JMenuItem("Edit configuration..."); - editMenu.add(editConfigItem); - editConfigItem.addActionListener(new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { - openConfigWindow(); - } - }); - - JMenuItem prefItem = new JMenuItem("Preferences..."); - editMenu.add(prefItem); - prefItem.addActionListener(new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { - if (_prefsWindow == null) { - _prefsWindow = new PrefsWindow(jthis); - _prefsWindow.setLocation(180, 160); - _prefsWindow.pack(); - } - _prefsWindow.saveAndShow(); - } - }); - - JMenu helpMenu = new JMenu("Help"); - menuBar.add(helpMenu); - JMenuItem aboutModuleItem = new JMenuItem("About module..."); - helpMenu.add(aboutModuleItem); - aboutModuleItem.addActionListener(new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { - showModuleInfo(); - } - }); - JMenuItem aboutAppItem = new JMenuItem("About Jhove..."); - helpMenu.add(aboutAppItem); - aboutAppItem.addActionListener(new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { - showAppInfo(); - } - }); - - setJMenuBar(menuBar); - } - - /** Set the normal background color. */ - private void setNormalBackground() { - logo.setBackground(new Color(255, 255, 255)); - } - - /** Set the background color for drag-over. */ - private void setDragBackground() { - logo.setBackground(new Color(180, 240, 140)); - } - - /** - * Here we let the user pick a file, then analyze it. - */ - public void pickAndAnalyzeFile() { - // Only one thread can be associated with a JhoveBase. - // Make sure we can't have concurrent threads. - _openFileItem.setEnabled(false); - _openURLItem.setEnabled(false); - File file = null; - synchronized (syncStr) { - JFileChooser chooser = new JFileChooser(); - makeChooserOpaque(chooser); - if (_lastDir != null) { - chooser.setCurrentDirectory(_lastDir); - } - chooser.setDialogTitle("Pick a file to analyze"); - int ok = chooser.showOpenDialog(this); - if (ok == JFileChooser.APPROVE_OPTION) { - file = chooser.getSelectedFile(); - _lastDir = chooser.getCurrentDirectory(); - ParseThread thr = new ParseThread(this); - thr.setFile(file); - thr.setModule(getSelectedModule()); - thr.start(); - _base.setCurrentThread(thr); - } else { - _openFileItem.setEnabled(true); - _openURLItem.setEnabled(true); - return; - } - } - } - - /** - * Makes a JFileChooser dialog treat packages and applications as opaque - * entities. Has no effect on other platforms. - */ - public static void makeChooserOpaque(JFileChooser chooser) { - // Apple TN 2042 LIES; we need to set both properties. - chooser.putClientProperty("JFileChooser.appBundleIsTraversable", - NEVER); - chooser.putClientProperty("JFileChooser.packageIsTraversable", NEVER); - } - - /** - * This method does the actual work of pickAndAnalyzeFile, called from a - * thread so it can run asynchronously. - */ - public void pickAndAnalyzeFile1(File file, Module module) { - String name = file.getName(); - _base.resetAbort(); - _progWind.setDocName(name, false); - _progWind.setProgressState(ProgressWindow.PROCESSING, false); - _progWind.setByteCount(-1, true); - _progWind.setVisible(true); - - // RepInfo info = new RepInfo (name); - try { - List files = new ArrayList<>(1); - files.add(file); - openAndParse(files, module); - } catch (ThreadDeath d) { - _openFileItem.setEnabled(true); - _openURLItem.setEnabled(true); - throw d; - } - _openFileItem.setEnabled(true); - _openURLItem.setEnabled(true); - } - - /** This is called to analyze a List of files. */ - public void pickAndAnalyzeFileList1(List files, Module module) { - if (files.isEmpty()) { - return; - } - // Set up progress window for the first file - File file = files.get(0); - String name = file.getName(); - _base.resetAbort(); - _progWind.setDocName(name, false); - _progWind.setProgressState(ProgressWindow.PROCESSING, false); - _progWind.setByteCount(-1, true); - _progWind.setVisible(true); - - try { - openAndParse(files, module); - } catch (ThreadDeath d) { - _openFileItem.setEnabled(true); - _openURLItem.setEnabled(true); - throw d; - } - _openFileItem.setEnabled(true); - _openURLItem.setEnabled(true); - } - - /** - * This method opens a directory, recursing through multiple levels if - * possible, and feeding individual files to pickAndAnalyzeFile1. - */ - public void analyzeDirectory(File file, Module module) { - // Construct list, excluding files that start with "." - String[] subfiles = file.list(invisibleFilter); - if (subfiles != null) { - // Walk through the directory - for (int i = 0; i < subfiles.length; i++) { - File subfile = new File(file, subfiles[i]); - if (subfile != null) { - if (subfile.isDirectory()) { - // Recurse through subdirectories - analyzeDirectory(subfile, module); - } else { - pickAndAnalyzeFile1(subfile, module); - } - } - } - } - } - - /* Here we let the user pick a URL, then analyze it. */ - public void pickAndAnalyzeURL() { - // There are multithreading issues which haven't been resolved. - // Rather than do a serious rewrite of the code, it's sufficient - // to make sure there can't be more than one file being processed - // at a time. - _openFileItem.setEnabled(false); - _openURLItem.setEnabled(false); - - String uri = null; - synchronized (syncStr) { - String urlStr = (String) JOptionPane.showInputDialog(this, - "Choose a URL to analyze", "Select URL", - JOptionPane.PLAIN_MESSAGE, null, null, "http://"); - - if (urlStr == null) { - _openFileItem.setEnabled(true); - _openURLItem.setEnabled(true); - return; // user cancelled - } - uri = urlStr.trim(); - } - ParseThread thr = new ParseThread(this); - thr.setURI(uri); - thr.setModule(getSelectedModule()); - thr.start(); - } - - /** - * This method does the actual work of pickAndAnalyzeURL, called from a - * thread so it can run asynchronously. - */ - public void pickAndAnalyzeURL1(String uri, Module module) { - _progWind.setDocName(uri.toString(), false); - _progWind.setProgressState(ProgressWindow.DOWNLOADING, false); - _progWind.setContentLength(0, false); - _progWind.setByteCount(0, true); - // _progWind.show (); - _progWind.setVisible(true); - try { - _base.dispatch(_app, module, null, // AboutHandler - _viewHandler, null, // output file - new String[] { uri }); - } catch (Exception e) { - reportError("Error processing URL", e.getMessage()); - } - // _progWind.hide (); - _progWind.setVisible(false); - _openFileItem.setEnabled(true); - _openURLItem.setEnabled(true); - } - - /** - * Implementation of Callback.callback. - * - * @param selector - * 1 signifies update of byte count. 2 signifies change of URI. - * Other values result in no action. - * @param parm - * If selector = 1, must be a Long that evaluates to the number - * of bytes processed to date. If selector = 2, must be a String - * naming the object being processed. Will be truncated at the - * left if longer than 64 characters. - */ - @Override - public int callback(int selector, Object parm) { - switch (selector) { - case 1: - long bytecnt = ((Long) parm).longValue(); - _progWind.setByteCount(bytecnt, true); - break; - case 2: - String name = (String) parm; - if (name.length() > 48) { - name = "..." - + name.substring(name.length() - 48, name.length()); - } - _progWind.setDocName(name, true); - break; - default: - break; - } - return 0; - } - - /** - * Sets the raw output flag. If set to true, raw numeric values are - * displayed; if false, explanatory text may be substituted. - */ - public void setRawOutput(boolean rawOutput) { - _rawOutput = rawOutput; - } - - /** - * Sets the checksum flag. If set to true, checksums are reported. - */ - public void setDoChecksum(boolean checksum) { - _doChecksum = checksum; - } - - private void openAndParse(List files, /* RepInfo info, */Module module) { - // InputStream stream = null; - // long lastModified = 0; - - // Turn a list of files into an array of strings. - String[] paths = new String[files.size()]; - Iterator iter = files.iterator(); - for (int i = 0; iter.hasNext(); i++) { - File fil = iter.next(); - paths[i] = fil.getAbsolutePath(); - if (!fil.exists()) { - _progWind.setVisible(false); - return; // shouldn't happen -- we just picked it! - } - if (!fil.canRead()) { - _progWind.setVisible(false); - reportError("File not readable", fil.getName()); - return; - } - } - - _base.setShowRawFlag(_rawOutput); - _base.setChecksumFlag(_doChecksum); - - /* - * With the new defaults for the PDF module being maximum information, - * it no longer makes sense to set maximum verbosity, since that would - * make all parameter settings ineffective. In fact, verbosity may be a - * kludge now that the config file handles parameters. - */ - // if (module != null) { - // module.setVerbosity (Module.MAXIMUM_VERBOSITY); - // // A problem (which I think we've always had): - // // If no particular module is specified, we don't - // // set its verbosity as we should. - // } - /****************************************************** - * Parse formatted object. - ******************************************************/ - - try { - _base.dispatch(_app, module, null, // AboutHandler - _viewHandler, null, // output file - paths); - } catch (Exception e) { - // Do SOMETHING useful here. - _logger.warning(e.toString()); - } - - _progWind.setVisible(false); - } - - /* Open a configuration dialog */ - private void openConfigWindow() { - String configFile = _base.getConfigFile(); - ConfigHandler configHandler = new ConfigHandler(); - XMLReader parser = null; - String saxClass = _base.getSaxClass(); - try { - if (saxClass != null) { - parser = XMLReaderFactory.createXMLReader(saxClass); - } else { - SAXParserFactory factory = SAXParserFactory.newInstance(); - factory.setNamespaceAware(true); - parser = factory.newSAXParser().getXMLReader(); - } - // Need to do this carefully to keep all parsers happy - File config = new File(configFile); - String canonicalPath = config.getCanonicalPath(); - String fileURL = "file://"; - if (canonicalPath.charAt(0) != '/') { - fileURL += '/'; - } - fileURL += canonicalPath; - parser.setContentHandler(configHandler); - parser.setEntityResolver(configHandler); - parser.setFeature("http://xml.org/sax/features/validation", true); - parser.parse(fileURL); - } catch (IOException e) { - reportError(CONFIG_ERROR, "Cannot read configuration file"); - return; - } catch (SAXException e) { - reportError(CONFIG_ERROR, "SAX parser not found: " + saxClass); - return; - } catch (ParserConfigurationException e) { - reportError(CONFIG_ERROR, "ParserConfigurationException"); - } - ConfigWindow confWin = new ConfigWindow(this, new File(configFile), - configHandler); - confWin.setLocation(120, 40); - confWin.setVisible(true); - } - - private void showModuleInfo() { - Module module = getSelectedModule(); - if (_moduleInfoWin == null) { - _moduleInfoWin = new ModuleInfoWindow(_app, _base, module); - _moduleInfoWin.setLocation(moduleInfoWinXPos, moduleInfoWinYPos); - } else { - _moduleInfoWin.showModule(module); - } - _moduleInfoWin.setVisible(true); - } - - private void showAppInfo() { - if (_appInfoWin == null) { - _appInfoWin = new AppInfoWindow(_app, _base); - _appInfoWin.setLocation(appInfoWinXPos, appInfoWinYPos); - } - _appInfoWin.setVisible(true); - } - - private Module getSelectedModule() { - if ("".equals(_selectedModule)) { - return null; - } - return (Module) _base.getModuleMap().get(_selectedModule.toLowerCase()); - } - - private void reportError(String title, String msg) { - synchronized (syncStr) { - JOptionPane.showMessageDialog(this, msg, title, - JOptionPane.ERROR_MESSAGE); - } - } - - /* DropTargetListener methods. */ - - /** - * Invoked when the drag enters the component. Accepts the drag if it's a - * file which is being dragged, and changes the background color to give - * visual feedback. - */ - @Override - public void dragEnter(DropTargetDragEvent dtde) { - DataFlavor[] flavors = dtde.getCurrentDataFlavors(); - if (dataFlavorOK(flavors)) { - setDragBackground(); - dtde.acceptDrag(dtde.getDropAction()); - } else { - dtde.rejectDrag(); - } - } - - /** - * Invoked when the drag leaves the component. Restores the default - * background color. - */ - @Override - public void dragExit(DropTargetEvent dte) { - setNormalBackground(); - } - - /** - * Does nothing. - */ - @Override - public void dragOver(DropTargetDragEvent dtde) { - } - - /** - * Called when the thingy is dropped on the component. This causes the file - * to be opened. The default background color will be restored; - * theoretically this should already have happened, but Windows appears to - * require it be done here. - */ - @Override - public void drop(DropTargetDropEvent dtde) { - DataFlavor[] flavors = dtde.getCurrentDataFlavors(); - if (dataFlavorOK(flavors)) { - dtde.acceptDrop(dtde.getDropAction()); - - // Now get the file(s) and open it (them) - Transferable thingy = dtde.getTransferable(); - try { - List fileList = (List) thingy - .getTransferData(DataFlavor.javaFileListFlavor); - ParseThread thr = new ParseThread(this); - thr.setFileList(fileList); - thr.setModule(getSelectedModule()); - thr.start(); - _base.setCurrentThread(thr); - dtde.dropComplete(true); - } catch (Exception e) { - // Really shouldn't happen - dtde.dropComplete(false); - return; - } - } else { - dtde.rejectDrop(); - } - setNormalBackground(); - } - - /** - * Called if the drop action changes during the drag (e.g., by changing the - * modifier keys). Does nothing, as we treat copy and move identically. - */ - @Override - public void dropActionChanged(DropTargetDragEvent dtde) { - - } - - /** - * Returns the "Close all document windows" menu item. This allows document - * windows to add themselves as listeners. - */ - protected JMenuItem getCloseAllItem() { - return _closeAllItem; - } - - /* Called to see if the DropTargetEvent's data flavor is OK */ - private boolean dataFlavorOK(DataFlavor[] flavors) { - // boolean haveFileFlavor = false; - for (int i = 0; i < flavors.length; i++) { - if (flavors[i].isFlavorJavaFileListType()) { - return true; - } - } - return false; - } - - /** - * A local class for creating threads. - */ - class ParseThread extends Thread { - - private JhoveWindow _win; - private String _uri; - private File _file; - private List _fileList; - private Module _module; - - /** Constructor. */ - protected ParseThread(JhoveWindow win) { - _win = win; - } - - /** - * The method invoked by running the thread. Analyzes the URI, file, or - * file list provided to this thread object. - */ - @Override - public void run() { - _base.resetAbort(); - try { - if (_uri != null) { - _win.pickAndAnalyzeURL1(_uri, _module); - } else if (_file != null) { - if (_file.isDirectory()) { - analyzeDirectory(_file, _module); - } else { - _win.pickAndAnalyzeFile1(_file, _module); - } - } else if (_fileList != null) { - _win.pickAndAnalyzeFileList1(_fileList, _module); - } - _base.setCurrentThread(null); - } catch (ThreadDeath d) { - _progWind.setVisible(false); - throw d; - } - } - - /** - * Designates a URI to parse. Only one of setURI, setFile, and - * setFileList should be called for a given thread. - */ - protected void setURI(String uri) { - _uri = uri; - } - - /** - * Designates a file to parse. Only one of setURI, setFile, and - * setFileList should be called for a given thread. - */ - protected void setFile(File file) { - _file = file; - } - - /** - * Designates a list of files to parse sequentially. Only one of setURI, - * setFile, and setFileList should be called for a given thread. - */ - protected void setFileList(List fileList) { - _fileList = fileList; - } - - /** - * Set the module. This is called at the start of thread setup, in case - * the user changes the module selection while the thread's running. - */ - protected void setModule(Module module) { - _module = module; - } - - } - - /** - * Class to filter out filenames that start with a period. These are - * "invisible" file names, at least under Unix, and generally shouldn't be - * included when walking through a directory. - */ - protected class InvisibleFilenameFilter implements FilenameFilter { - @Override - public boolean accept(File dir, String name) { - return (!name.startsWith(".")); - } - } + private App _app; + private JhoveBase _base; + private AppInfoWindow _appInfoWin; + private ModuleInfoWindow _moduleInfoWin; + private JMenu _moduleSubmenu; + private JMenuItem _openFileItem; + private JMenuItem _openURLItem; + private JMenuItem _closeAllItem; + private ButtonGroup _moduleGroup; + private String syncStr = ""; // object just for synchronizing + private boolean _rawOutput; + private boolean _doChecksum; + + private ProgressWindow _progWind; + // private ConfigWindow _configWind; + private PrefsWindow _prefsWindow; + + private File _lastDir; + private String _selectedModule; + private ActionListener _moduleMenuListener; + private JPanel logo; + private ViewHandler _viewHandler; + + // Initial position for view windows. + // Stagger them by adding an increment each time. + // private static int viewWinXPos = 24; + // private static int viewWinYPos = 24; + // Original positions for cycling back to. + // private static final int viewWinOrigXPos = 24; + // private static final int viewWinOrigYPos = 24; + // private static final int viewWinXInc = 25; + // private static final int viewWinYInc = 22; + private static final int appInfoWinXPos = 50; + private static final int appInfoWinYPos = 45; + private static final int moduleInfoWinXPos = 100; + private static final int moduleInfoWinYPos = 90; + + private static final String NEVER = "never"; + private static final String CONFIG_ERROR = "Config Error"; + + /** Logger for a module class. */ + protected Logger _logger; + + /* Static instance of InvisibleFilenameFilter */ + private final InvisibleFilenameFilter invisibleFilter = new JhoveWindow.InvisibleFilenameFilter(); + + public JhoveWindow(App app, JhoveBase base) { + super("Jhove"); + _logger = Logger.getLogger("edu.harvard.hul.ois.jhove.viewer"); + _app = app; + _base = base; + _moduleMenuListener = + new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + _selectedModule = e.getActionCommand(); + } + }; + + _lastDir = null; + _moduleGroup = new ButtonGroup(); + addMenus(); + Container rootPane = getContentPane(); + // rootPane.setLayout (new GridLayout (4, 2)); + setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); + + // Define a Comparator function for Modules + Comparator modListComparator = + new Comparator() { + @Override + public int compare(Module o1, Module o2) { + Module m1 = o1; + Module m2 = o2; + String name1 = m1.getName(); + String name2 = m2.getName(); + return String.CASE_INSENSITIVE_ORDER.compare(name1, name2); + } + }; + + // Build combo box of available modules + Vector moduleItems = new Vector<>(10); + java.util.List moduleList = base.getModuleList(); + // Clone the list so we can display it in sorted order + // without munging the app's preferred order + java.util.List menuModuleList = new ArrayList<>(moduleList.size()); + menuModuleList.addAll(moduleList); + Collections.sort(menuModuleList, modListComparator); + Iterator iter = menuModuleList.iterator(); + moduleItems.add("(None selected)"); + JRadioButtonMenuItem modItem = null; + String itemName = null; + + while (iter.hasNext()) { + Module mod = iter.next(); + itemName = mod.getName(); + modItem = new JRadioButtonMenuItem(itemName); + modItem.setActionCommand(itemName); + modItem.addActionListener(_moduleMenuListener); + _moduleSubmenu.add(modItem); + _moduleGroup.add(modItem); + // moduleItems.add (mod.getName ()); + } + + logo = new JPanel(); + + // Add the image, which should be in jhove-logo.gif in + // the viewer directory + URL logoURL = JhoveWindow.class.getResource("jhovelogo.png"); + if (logoURL != null) { + ImageIcon icn = new ImageIcon(logoURL); + icn.setDescription("Jhove logo"); + setNormalBackground(); + JLabel logoLabel = new JLabel(icn); + logo.add(logoLabel); + } + + // Allow files to be dragged to the logo pane. + DropTarget dt = new DropTarget(logo, this); + + rootPane.add(logo); + pack(); + + // Set up a companion progress window. This will + // be hidden and displayed as needed. + ActionListener listener = + new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + _base.abort(); + } + }; + _progWind = new ProgressWindow(listener); + + // Set up a Handler which is tailored to this application. + _viewHandler = new ViewHandler(this, _app, _base); + } + + /** Set up the menu bar and menus. */ + private final void addMenus() { + final JhoveWindow jthis = this; + JMenuBar menuBar = new JMenuBar(); + JMenu fileMenu = new JMenu("File"); + menuBar.add(fileMenu); + _openFileItem = new JMenuItem("Open file..."); + fileMenu.add(_openFileItem); + // The following allows accelerator modifier keys to be set which are + // appropriate to the host OS + _openFileItem.setAccelerator( + KeyStroke.getKeyStroke( + KeyEvent.VK_O, Toolkit.getDefaultToolkit().getMenuShortcutKeyMask())); + + _openFileItem.addActionListener( + new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + pickAndAnalyzeFile(); + } + }); + _openURLItem = new JMenuItem("Open URL..."); + fileMenu.add(_openURLItem); + _openURLItem.addActionListener( + new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + pickAndAnalyzeURL(); + } + }); + + _closeAllItem = new JMenuItem("Close all document windows"); + fileMenu.add(_closeAllItem); + // Action listeners are added by document windows + + if (!MacStuff.isMacintosh()) { + // Token attempt at Mac friendliness: the Exit item + // in the File menu is redundant under OS X + JMenuItem quitItem = new JMenuItem("Exit"); + fileMenu.add(quitItem); + quitItem.setAccelerator( + KeyStroke.getKeyStroke( + KeyEvent.VK_Q, Toolkit.getDefaultToolkit().getMenuShortcutKeyMask())); + quitItem.addActionListener( + new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + System.exit(0); + } + }); + } + + JMenu editMenu = new JMenu("Edit"); + menuBar.add(editMenu); + _moduleSubmenu = new JMenu("Select module"); + editMenu.add(_moduleSubmenu); + JRadioButtonMenuItem noModuleItem = new JRadioButtonMenuItem("(Any)"); + noModuleItem.setActionCommand(""); + noModuleItem.setSelected(true); + noModuleItem.addActionListener(_moduleMenuListener); + _moduleSubmenu.add(noModuleItem); + _moduleGroup.add(noModuleItem); + _selectedModule = ""; + // Modules will be added later + + JMenuItem editConfigItem = new JMenuItem("Edit configuration..."); + editMenu.add(editConfigItem); + editConfigItem.addActionListener( + new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + openConfigWindow(); + } + }); + + JMenuItem prefItem = new JMenuItem("Preferences..."); + editMenu.add(prefItem); + prefItem.addActionListener( + new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + if (_prefsWindow == null) { + _prefsWindow = new PrefsWindow(jthis); + _prefsWindow.setLocation(180, 160); + _prefsWindow.pack(); + } + _prefsWindow.saveAndShow(); + } + }); + + JMenu helpMenu = new JMenu("Help"); + menuBar.add(helpMenu); + JMenuItem aboutModuleItem = new JMenuItem("About module..."); + helpMenu.add(aboutModuleItem); + aboutModuleItem.addActionListener( + new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + showModuleInfo(); + } + }); + JMenuItem aboutAppItem = new JMenuItem("About Jhove..."); + helpMenu.add(aboutAppItem); + aboutAppItem.addActionListener( + new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + showAppInfo(); + } + }); + + setJMenuBar(menuBar); + } + + /** Set the normal background color. */ + private void setNormalBackground() { + logo.setBackground(new Color(255, 255, 255)); + } + + /** Set the background color for drag-over. */ + private void setDragBackground() { + logo.setBackground(new Color(180, 240, 140)); + } + + /** Here we let the user pick a file, then analyze it. */ + public void pickAndAnalyzeFile() { + // Only one thread can be associated with a JhoveBase. + // Make sure we can't have concurrent threads. + _openFileItem.setEnabled(false); + _openURLItem.setEnabled(false); + File file = null; + synchronized (syncStr) { + JFileChooser chooser = new JFileChooser(); + makeChooserOpaque(chooser); + if (_lastDir != null) { + chooser.setCurrentDirectory(_lastDir); + } + chooser.setDialogTitle("Pick a file to analyze"); + int ok = chooser.showOpenDialog(this); + if (ok == JFileChooser.APPROVE_OPTION) { + file = chooser.getSelectedFile(); + _lastDir = chooser.getCurrentDirectory(); + ParseThread thr = new ParseThread(this); + thr.setFile(file); + thr.setModule(getSelectedModule()); + thr.start(); + _base.setCurrentThread(thr); + } else { + _openFileItem.setEnabled(true); + _openURLItem.setEnabled(true); + return; + } + } + } + + /** + * Makes a JFileChooser dialog treat packages and applications as opaque entities. Has no effect + * on other platforms. + */ + public static void makeChooserOpaque(JFileChooser chooser) { + // Apple TN 2042 LIES; we need to set both properties. + chooser.putClientProperty("JFileChooser.appBundleIsTraversable", NEVER); + chooser.putClientProperty("JFileChooser.packageIsTraversable", NEVER); + } + + /** + * This method does the actual work of pickAndAnalyzeFile, called from a thread so it can run + * asynchronously. + */ + public void pickAndAnalyzeFile1(File file, Module module) { + String name = file.getName(); + _base.resetAbort(); + _progWind.setDocName(name, false); + _progWind.setProgressState(ProgressWindow.PROCESSING, false); + _progWind.setByteCount(-1, true); + _progWind.setVisible(true); + + // RepInfo info = new RepInfo (name); + try { + List files = new ArrayList<>(1); + files.add(file); + openAndParse(files, module); + } catch (ThreadDeath d) { + _openFileItem.setEnabled(true); + _openURLItem.setEnabled(true); + throw d; + } + _openFileItem.setEnabled(true); + _openURLItem.setEnabled(true); + } + + /** This is called to analyze a List of files. */ + public void pickAndAnalyzeFileList1(List files, Module module) { + if (files.isEmpty()) { + return; + } + // Set up progress window for the first file + File file = files.get(0); + String name = file.getName(); + _base.resetAbort(); + _progWind.setDocName(name, false); + _progWind.setProgressState(ProgressWindow.PROCESSING, false); + _progWind.setByteCount(-1, true); + _progWind.setVisible(true); + + try { + openAndParse(files, module); + } catch (ThreadDeath d) { + _openFileItem.setEnabled(true); + _openURLItem.setEnabled(true); + throw d; + } + _openFileItem.setEnabled(true); + _openURLItem.setEnabled(true); + } + + /** + * This method opens a directory, recursing through multiple levels if possible, and feeding + * individual files to pickAndAnalyzeFile1. + */ + public void analyzeDirectory(File file, Module module) { + // Construct list, excluding files that start with "." + String[] subfiles = file.list(invisibleFilter); + if (subfiles != null) { + // Walk through the directory + for (int i = 0; i < subfiles.length; i++) { + File subfile = new File(file, subfiles[i]); + if (subfile != null) { + if (subfile.isDirectory()) { + // Recurse through subdirectories + analyzeDirectory(subfile, module); + } else { + pickAndAnalyzeFile1(subfile, module); + } + } + } + } + } + + /* Here we let the user pick a URL, then analyze it. */ + public void pickAndAnalyzeURL() { + // There are multithreading issues which haven't been resolved. + // Rather than do a serious rewrite of the code, it's sufficient + // to make sure there can't be more than one file being processed + // at a time. + _openFileItem.setEnabled(false); + _openURLItem.setEnabled(false); + + String uri = null; + synchronized (syncStr) { + String urlStr = + (String) + JOptionPane.showInputDialog( + this, + "Choose a URL to analyze", + "Select URL", + JOptionPane.PLAIN_MESSAGE, + null, + null, + "http://"); + + if (urlStr == null) { + _openFileItem.setEnabled(true); + _openURLItem.setEnabled(true); + return; // user cancelled + } + uri = urlStr.trim(); + } + ParseThread thr = new ParseThread(this); + thr.setURI(uri); + thr.setModule(getSelectedModule()); + thr.start(); + } + + /** + * This method does the actual work of pickAndAnalyzeURL, called from a thread so it can run + * asynchronously. + */ + public void pickAndAnalyzeURL1(String uri, Module module) { + _progWind.setDocName(uri.toString(), false); + _progWind.setProgressState(ProgressWindow.DOWNLOADING, false); + _progWind.setContentLength(0, false); + _progWind.setByteCount(0, true); + // _progWind.show (); + _progWind.setVisible(true); + try { + _base.dispatch( + _app, + module, + null, // AboutHandler + _viewHandler, + null, // output file + new String[] {uri}); + } catch (Exception e) { + reportError("Error processing URL", e.getMessage()); + } + // _progWind.hide (); + _progWind.setVisible(false); + _openFileItem.setEnabled(true); + _openURLItem.setEnabled(true); + } + + /** + * Implementation of Callback.callback. + * + * @param selector 1 signifies update of byte count. 2 signifies change of URI. Other values + * result in no action. + * @param parm If selector = 1, must be a Long that evaluates to the number of bytes processed to + * date. If selector = 2, must be a String naming the object being processed. Will be + * truncated at the left if longer than 64 characters. + */ + @Override + public int callback(int selector, Object parm) { + switch (selector) { + case 1: + long bytecnt = ((Long) parm).longValue(); + _progWind.setByteCount(bytecnt, true); + break; + case 2: + String name = (String) parm; + if (name.length() > 48) { + name = "..." + name.substring(name.length() - 48, name.length()); + } + _progWind.setDocName(name, true); + break; + default: + break; + } + return 0; + } + + /** + * Sets the raw output flag. If set to true, raw numeric values are displayed; if false, + * explanatory text may be substituted. + */ + public void setRawOutput(boolean rawOutput) { + _rawOutput = rawOutput; + } + + /** Sets the checksum flag. If set to true, checksums are reported. */ + public void setDoChecksum(boolean checksum) { + _doChecksum = checksum; + } + + private void openAndParse(List files, /* RepInfo info, */ Module module) { + // InputStream stream = null; + // long lastModified = 0; + + // Turn a list of files into an array of strings. + String[] paths = new String[files.size()]; + Iterator iter = files.iterator(); + for (int i = 0; iter.hasNext(); i++) { + File fil = iter.next(); + paths[i] = fil.getAbsolutePath(); + if (!fil.exists()) { + _progWind.setVisible(false); + return; // shouldn't happen -- we just picked it! + } + if (!fil.canRead()) { + _progWind.setVisible(false); + reportError("File not readable", fil.getName()); + return; + } + } + + _base.setShowRawFlag(_rawOutput); + _base.setChecksumFlag(_doChecksum); + + /* + * With the new defaults for the PDF module being maximum information, + * it no longer makes sense to set maximum verbosity, since that would + * make all parameter settings ineffective. In fact, verbosity may be a + * kludge now that the config file handles parameters. + */ + // if (module != null) { + // module.setVerbosity (Module.MAXIMUM_VERBOSITY); + // // A problem (which I think we've always had): + // // If no particular module is specified, we don't + // // set its verbosity as we should. + // } + /** + * **************************************************** Parse formatted object. + * **************************************************** + */ + try { + _base.dispatch( + _app, + module, + null, // AboutHandler + _viewHandler, + null, // output file + paths); + } catch (Exception e) { + // Do SOMETHING useful here. + _logger.warning(e.toString()); + } + + _progWind.setVisible(false); + } + + /* Open a configuration dialog */ + private void openConfigWindow() { + String configFile = _base.getConfigFile(); + ConfigHandler configHandler = new ConfigHandler(); + XMLReader parser = null; + String saxClass = _base.getSaxClass(); + try { + if (saxClass != null) { + parser = XMLReaderFactory.createXMLReader(saxClass); + } else { + SAXParserFactory factory = SAXParserFactory.newInstance(); + factory.setNamespaceAware(true); + parser = factory.newSAXParser().getXMLReader(); + } + // Need to do this carefully to keep all parsers happy + File config = new File(configFile); + String canonicalPath = config.getCanonicalPath(); + String fileURL = "file://"; + if (canonicalPath.charAt(0) != '/') { + fileURL += '/'; + } + fileURL += canonicalPath; + parser.setContentHandler(configHandler); + parser.setEntityResolver(configHandler); + parser.setFeature("http://xml.org/sax/features/validation", true); + parser.parse(fileURL); + } catch (IOException e) { + reportError(CONFIG_ERROR, "Cannot read configuration file"); + return; + } catch (SAXException e) { + reportError(CONFIG_ERROR, "SAX parser not found: " + saxClass); + return; + } catch (ParserConfigurationException e) { + reportError(CONFIG_ERROR, "ParserConfigurationException"); + } + ConfigWindow confWin = new ConfigWindow(this, new File(configFile), configHandler); + confWin.setLocation(120, 40); + confWin.setVisible(true); + } + + private void showModuleInfo() { + Module module = getSelectedModule(); + if (_moduleInfoWin == null) { + _moduleInfoWin = new ModuleInfoWindow(_app, _base, module); + _moduleInfoWin.setLocation(moduleInfoWinXPos, moduleInfoWinYPos); + } else { + _moduleInfoWin.showModule(module); + } + _moduleInfoWin.setVisible(true); + } + + private void showAppInfo() { + if (_appInfoWin == null) { + _appInfoWin = new AppInfoWindow(_app, _base); + _appInfoWin.setLocation(appInfoWinXPos, appInfoWinYPos); + } + _appInfoWin.setVisible(true); + } + + private Module getSelectedModule() { + if ("".equals(_selectedModule)) { + return null; + } + return (Module) _base.getModuleMap().get(_selectedModule.toLowerCase()); + } + + private void reportError(String title, String msg) { + synchronized (syncStr) { + JOptionPane.showMessageDialog(this, msg, title, JOptionPane.ERROR_MESSAGE); + } + } + + /* DropTargetListener methods. */ + + /** + * Invoked when the drag enters the component. Accepts the drag if it's a file which is being + * dragged, and changes the background color to give visual feedback. + */ + @Override + public void dragEnter(DropTargetDragEvent dtde) { + DataFlavor[] flavors = dtde.getCurrentDataFlavors(); + if (dataFlavorOK(flavors)) { + setDragBackground(); + dtde.acceptDrag(dtde.getDropAction()); + } else { + dtde.rejectDrag(); + } + } + + /** Invoked when the drag leaves the component. Restores the default background color. */ + @Override + public void dragExit(DropTargetEvent dte) { + setNormalBackground(); + } + + /** Does nothing. */ + @Override + public void dragOver(DropTargetDragEvent dtde) {} + + /** + * Called when the thingy is dropped on the component. This causes the file to be opened. The + * default background color will be restored; theoretically this should already have happened, but + * Windows appears to require it be done here. + */ + @Override + public void drop(DropTargetDropEvent dtde) { + DataFlavor[] flavors = dtde.getCurrentDataFlavors(); + if (dataFlavorOK(flavors)) { + dtde.acceptDrop(dtde.getDropAction()); + + // Now get the file(s) and open it (them) + Transferable thingy = dtde.getTransferable(); + try { + List fileList = (List) thingy.getTransferData(DataFlavor.javaFileListFlavor); + ParseThread thr = new ParseThread(this); + thr.setFileList(fileList); + thr.setModule(getSelectedModule()); + thr.start(); + _base.setCurrentThread(thr); + dtde.dropComplete(true); + } catch (Exception e) { + // Really shouldn't happen + dtde.dropComplete(false); + return; + } + } else { + dtde.rejectDrop(); + } + setNormalBackground(); + } + + /** + * Called if the drop action changes during the drag (e.g., by changing the modifier keys). Does + * nothing, as we treat copy and move identically. + */ + @Override + public void dropActionChanged(DropTargetDragEvent dtde) {} + + /** + * Returns the "Close all document windows" menu item. This allows document windows to add + * themselves as listeners. + */ + protected JMenuItem getCloseAllItem() { + return _closeAllItem; + } + + /* Called to see if the DropTargetEvent's data flavor is OK */ + private boolean dataFlavorOK(DataFlavor[] flavors) { + // boolean haveFileFlavor = false; + for (int i = 0; i < flavors.length; i++) { + if (flavors[i].isFlavorJavaFileListType()) { + return true; + } + } + return false; + } + + /** A local class for creating threads. */ + class ParseThread extends Thread { + + private JhoveWindow _win; + private String _uri; + private File _file; + private List _fileList; + private Module _module; + + /** Constructor. */ + protected ParseThread(JhoveWindow win) { + _win = win; + } + + /** + * The method invoked by running the thread. Analyzes the URI, file, or file list provided to + * this thread object. + */ + @Override + public void run() { + _base.resetAbort(); + try { + if (_uri != null) { + _win.pickAndAnalyzeURL1(_uri, _module); + } else if (_file != null) { + if (_file.isDirectory()) { + analyzeDirectory(_file, _module); + } else { + _win.pickAndAnalyzeFile1(_file, _module); + } + } else if (_fileList != null) { + _win.pickAndAnalyzeFileList1(_fileList, _module); + } + _base.setCurrentThread(null); + } catch (ThreadDeath d) { + _progWind.setVisible(false); + throw d; + } + } + + /** + * Designates a URI to parse. Only one of setURI, setFile, and setFileList should be called for + * a given thread. + */ + protected void setURI(String uri) { + _uri = uri; + } + + /** + * Designates a file to parse. Only one of setURI, setFile, and setFileList should be called for + * a given thread. + */ + protected void setFile(File file) { + _file = file; + } + + /** + * Designates a list of files to parse sequentially. Only one of setURI, setFile, and + * setFileList should be called for a given thread. + */ + protected void setFileList(List fileList) { + _fileList = fileList; + } + + /** + * Set the module. This is called at the start of thread setup, in case the user changes the + * module selection while the thread's running. + */ + protected void setModule(Module module) { + _module = module; + } + } + + /** + * Class to filter out filenames that start with a period. These are "invisible" file names, at + * least under Unix, and generally shouldn't be included when walking through a directory. + */ + protected class InvisibleFilenameFilter implements FilenameFilter { + @Override + public boolean accept(File dir, String name) { + return (!name.startsWith(".")); + } + } } diff --git a/jhove-apps/src/main/java/edu/harvard/hul/ois/jhove/viewer/MainScreen.java b/jhove-apps/src/main/java/edu/harvard/hul/ois/jhove/viewer/MainScreen.java index bff7645bc..d39c0e979 100644 --- a/jhove-apps/src/main/java/edu/harvard/hul/ois/jhove/viewer/MainScreen.java +++ b/jhove-apps/src/main/java/edu/harvard/hul/ois/jhove/viewer/MainScreen.java @@ -1,75 +1,56 @@ -/********************************************************************** - * Jhove - JSTOR/Harvard Object Validation Environment - * Copyright 2003 by JSTOR and the President and Fellows of Harvard College +/** + * ******************************************************************** Jhove - JSTOR/Harvard Object + * Validation Environment Copyright 2003 by JSTOR and the President and Fellows of Harvard College * - **********************************************************************/ - + *

******************************************************************** + */ package edu.harvard.hul.ois.jhove.viewer; import java.awt.*; /** * Static methods for positioning windows on the main screen. - * - * @author Gary McGath * + * @author Gary McGath */ public class MainScreen { - /** - * Private constructor to prevent instantiation - */ - private MainScreen () - { - + /** Private constructor to prevent instantiation */ + private MainScreen() {} + + /** Center the window on the main screen. */ + public static void centerWindow(Window win) { + Rectangle devBounds = mainBounds(); + Rectangle winBounds = win.getBounds(); + int lmargin = (devBounds.width - winBounds.width) / 2; + int tmargin = (devBounds.height - winBounds.height) / 2; + // Don't go off the edge + if (lmargin < 0) { + lmargin = 0; } - - - /** - * Center the window on the main screen. - */ - public static void centerWindow (Window win) - { - Rectangle devBounds = mainBounds (); - Rectangle winBounds = win.getBounds (); - int lmargin = (devBounds.width - winBounds.width) / 2; - int tmargin = (devBounds.height - winBounds.height) / 2; - // Don't go off the edge - if (lmargin < 0) { - lmargin = 0; - } - if (tmargin < 0) { - tmargin = 0; - } - win.setLocation (lmargin, tmargin); + if (tmargin < 0) { + tmargin = 0; } + win.setLocation(lmargin, tmargin); + } - - /** - * Center the window at the top of the main screen. - */ - public static void centerTopWindow (Window win) - { - Rectangle devBounds = mainBounds (); - Rectangle winBounds = win.getBounds (); - int lmargin = (devBounds.width - winBounds.width) / 2; - // Don't go off the edge - if (lmargin < 0) { - lmargin = 0; - } - win.setLocation (lmargin, 0); - } - - - /** - * Returns the bounds of the main monitor device. - */ - public static Rectangle mainBounds () - { - GraphicsEnvironment ge = GraphicsEnvironment. - getLocalGraphicsEnvironment(); - GraphicsDevice dev = ge.getDefaultScreenDevice(); - GraphicsConfiguration conf = dev.getDefaultConfiguration (); - return conf.getBounds (); + /** Center the window at the top of the main screen. */ + public static void centerTopWindow(Window win) { + Rectangle devBounds = mainBounds(); + Rectangle winBounds = win.getBounds(); + int lmargin = (devBounds.width - winBounds.width) / 2; + // Don't go off the edge + if (lmargin < 0) { + lmargin = 0; } + win.setLocation(lmargin, 0); + } + + /** Returns the bounds of the main monitor device. */ + public static Rectangle mainBounds() { + GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment(); + GraphicsDevice dev = ge.getDefaultScreenDevice(); + GraphicsConfiguration conf = dev.getDefaultConfiguration(); + return conf.getBounds(); + } } diff --git a/jhove-apps/src/main/java/edu/harvard/hul/ois/jhove/viewer/ModuleInfoWindow.java b/jhove-apps/src/main/java/edu/harvard/hul/ois/jhove/viewer/ModuleInfoWindow.java index 47a86520c..2bba18c20 100644 --- a/jhove-apps/src/main/java/edu/harvard/hul/ois/jhove/viewer/ModuleInfoWindow.java +++ b/jhove-apps/src/main/java/edu/harvard/hul/ois/jhove/viewer/ModuleInfoWindow.java @@ -1,22 +1,10 @@ -/********************************************************************** - * JhoveView - JSTOR/Harvard Object Validation Environment - * Copyright 2003 by JSTOR and the President and Fellows of Harvard College - **********************************************************************/ - +/** + * ******************************************************************** JhoveView - JSTOR/Harvard + * Object Validation Environment Copyright 2003 by JSTOR and the President and Fellows of Harvard + * College ******************************************************************** + */ package edu.harvard.hul.ois.jhove.viewer; -import java.awt.Dimension; -import java.awt.Font; -import java.awt.Rectangle; -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; -import java.io.PrintWriter; - -import javax.swing.JOptionPane; -import javax.swing.JPanel; -import javax.swing.JScrollPane; -import javax.swing.JTextArea; - import edu.harvard.hul.ois.jhove.Agent; import edu.harvard.hul.ois.jhove.App; import edu.harvard.hul.ois.jhove.Document; @@ -27,276 +15,276 @@ import edu.harvard.hul.ois.jhove.OutputHandler; import edu.harvard.hul.ois.jhove.Signature; import edu.harvard.hul.ois.jhove.SignatureType; +import java.awt.Dimension; +import java.awt.Font; +import java.awt.Rectangle; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.io.PrintWriter; +import javax.swing.JOptionPane; +import javax.swing.JPanel; +import javax.swing.JScrollPane; +import javax.swing.JTextArea; /** - * This window is for presenting information about the selected module. If no - * module is selected, a brief message is put into the window. + * This window is for presenting information about the selected module. If no module is selected, a + * brief message is put into the window. */ public class ModuleInfoWindow extends InfoWindow { - private JTextArea texta; - private int _level; - private Module _module; - - private static final String DATE = "Date: "; - private static final String NOTE = "Note: "; - private static final String TYPE = "Type: "; + private JTextArea texta; + private int _level; + private Module _module; - /** - * Constructor. - * - * @param app - * The associated App object. - * @param base - * The associated JhoveBase object. - * @param module - * The Module whose information is to be presented. - */ - public ModuleInfoWindow(App app, JhoveBase base, Module module) { - super("Module Info", app, base); - _module = module; - setSaveActionListener(new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { - saveInfo(); - } - }); + private static final String DATE = "Date: "; + private static final String NOTE = "Note: "; + private static final String TYPE = "Type: "; - texta = new JTextArea(); - texta.setColumns(72); - JScrollPane scrollpane = new JScrollPane(texta); - texta.setFont(new Font("sansserif", Font.PLAIN, 10)); - texta.setLineWrap(true); - texta.setWrapStyleWord(true); - // Getting Swing to accept what you want for dimensions - // apparently requires setting as many dimension restrictions - // as possible, and hoping it will pay attention to some - // of them. - scrollpane.setMinimumSize(new Dimension(240, 240)); - scrollpane.setMaximumSize(new Dimension(600, 500)); - scrollpane.setPreferredSize(new Dimension(600, 500)); - getContentPane().add(scrollpane, "Center"); + /** + * Constructor. + * + * @param app The associated App object. + * @param base The associated JhoveBase object. + * @param module The Module whose information is to be presented. + */ + public ModuleInfoWindow(App app, JhoveBase base, Module module) { + super("Module Info", app, base); + _module = module; + setSaveActionListener( + new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + saveInfo(); + } + }); - // Add a small panel at the bottom, since on some OS's there - // may be stuff near the bottom of a window which will conflict - // with the scroll bar. - JPanel panel = new JPanel(); - panel.setMinimumSize(new Dimension(8, 8)); - getContentPane().add(panel, "South"); + texta = new JTextArea(); + texta.setColumns(72); + JScrollPane scrollpane = new JScrollPane(texta); + texta.setFont(new Font("sansserif", Font.PLAIN, 10)); + texta.setLineWrap(true); + texta.setWrapStyleWord(true); + // Getting Swing to accept what you want for dimensions + // apparently requires setting as many dimension restrictions + // as possible, and hoping it will pay attention to some + // of them. + scrollpane.setMinimumSize(new Dimension(240, 240)); + scrollpane.setMaximumSize(new Dimension(600, 500)); + scrollpane.setPreferredSize(new Dimension(600, 500)); + getContentPane().add(scrollpane, "Center"); - showModule(module); - pack(); + // Add a small panel at the bottom, since on some OS's there + // may be stuff near the bottom of a window which will conflict + // with the scroll bar. + JPanel panel = new JPanel(); + panel.setMinimumSize(new Dimension(8, 8)); + getContentPane().add(panel, "South"); - } + showModule(module); + pack(); + } - /** - * Formats and presents the module information in the window. - */ - public void showModule(Module module) { - _module = module; - if (module == null) { - texta.setText("(No module selected)"); - } else { - _level = 0; - texta.setText(""); - String margin = getIndent(++_level); - texta.append(margin + "Module: " + module.getName() + eol); - texta.append(margin + "Release: " + module.getRelease() + eol); - texta.append(margin + DATE + _dateFmt.format(module.getDate()) - + eol); - String[] ss = module.getFormat(); - if (ss.length > 0) { - texta.append(margin + "Format: " + ss[0]); - for (int i = 1; i < ss.length; i++) { - texta.append(", " + ss[i]); - } - texta.append(eol); - } + /** Formats and presents the module information in the window. */ + public void showModule(Module module) { + _module = module; + if (module == null) { + texta.setText("(No module selected)"); + } else { + _level = 0; + texta.setText(""); + String margin = getIndent(++_level); + texta.append(margin + "Module: " + module.getName() + eol); + texta.append(margin + "Release: " + module.getRelease() + eol); + texta.append(margin + DATE + _dateFmt.format(module.getDate()) + eol); + String[] ss = module.getFormat(); + if (ss.length > 0) { + texta.append(margin + "Format: " + ss[0]); + for (int i = 1; i < ss.length; i++) { + texta.append(", " + ss[i]); + } + texta.append(eol); + } - String s = module.getCoverage(); - if (s != null) { - texta.append(margin + "Coverage: " + s + eol); - } - ss = module.getMimeType(); - if (ss.length > 0) { - texta.append(margin + "MIMEtype: " + ss[0]); - for (int i = 1; i < ss.length; i++) { - texta.append(", " + ss[i]); - } - ; - texta.append(eol); - } - for (Signature sig : module.getSignature()) { - showSignature(sig); - } - for (Document spec : module.getSpecification()) { - showDocument(spec, "Specification"); - } - texta.append(margin + " Features:\n"); - for (String feature : module.getFeatures()) { - texta.append(margin + " " + feature + "\n"); - } - texta.append(margin + "Methodology:\n"); - if ((s = module.getWellFormedNote()) != null) { - texta.append(margin + "Well-formed: " + s + eol); - } - if ((s = module.getValidityNote()) != null) { - texta.append(margin + "Validity: " + s + eol); - } - if ((s = module.getRepInfoNote()) != null) { - texta.append(margin + "RepresentationInformation: " + s + eol); - } - Agent vendor = module.getVendor(); - if (vendor != null) { - showAgent(vendor, "Vendor"); - } - if ((s = module.getNote()) != null) { - texta.append(margin + NOTE + s + eol); - } - if ((s = module.getRights()) != null) { - texta.append(margin + "Rights: " + s + eol); - } - } - // Scroll to the top. - texta.setEditable(false); - texta.select(0, 0); - Rectangle r = new Rectangle(0, 0, 1, 1); - texta.scrollRectToVisible(r); - } + String s = module.getCoverage(); + if (s != null) { + texta.append(margin + "Coverage: " + s + eol); + } + ss = module.getMimeType(); + if (ss.length > 0) { + texta.append(margin + "MIMEtype: " + ss[0]); + for (int i = 1; i < ss.length; i++) { + texta.append(", " + ss[i]); + } + ; + texta.append(eol); + } + for (Signature sig : module.getSignature()) { + showSignature(sig); + } + for (Document spec : module.getSpecification()) { + showDocument(spec, "Specification"); + } + texta.append(margin + " Features:\n"); + for (String feature : module.getFeatures()) { + texta.append(margin + " " + feature + "\n"); + } + texta.append(margin + "Methodology:\n"); + if ((s = module.getWellFormedNote()) != null) { + texta.append(margin + "Well-formed: " + s + eol); + } + if ((s = module.getValidityNote()) != null) { + texta.append(margin + "Validity: " + s + eol); + } + if ((s = module.getRepInfoNote()) != null) { + texta.append(margin + "RepresentationInformation: " + s + eol); + } + Agent vendor = module.getVendor(); + if (vendor != null) { + showAgent(vendor, "Vendor"); + } + if ((s = module.getNote()) != null) { + texta.append(margin + NOTE + s + eol); + } + if ((s = module.getRights()) != null) { + texta.append(margin + "Rights: " + s + eol); + } + } + // Scroll to the top. + texta.setEditable(false); + texta.select(0, 0); + Rectangle r = new Rectangle(0, 0, 1, 1); + texta.scrollRectToVisible(r); + } - private void showSignature(Signature signature) { - String margin = getIndent(++_level); + private void showSignature(Signature signature) { + String margin = getIndent(++_level); - String sigValue; - if (signature.isStringValue()) { - sigValue = signature.getValueString(); - } else { - sigValue = signature.getValueHexString(); - } - texta.append(margin + signature.getType().toString() + ": " + sigValue - + eol); - if (signature.getType().equals(SignatureType.MAGIC)) { - if (((InternalSignature) signature).hasFixedOffset()) { - texta.append(margin + "Offset: " - + ((InternalSignature) signature).getOffset() + eol); - } - } - String note = signature.getNote(); - if (note != null) { - texta.append(margin + NOTE + note + eol); - } - String use = signature.getUse().toString(); - if (use != null) { - texta.append(margin + "Use: " + use + eol); - } - --_level; - } + String sigValue; + if (signature.isStringValue()) { + sigValue = signature.getValueString(); + } else { + sigValue = signature.getValueHexString(); + } + texta.append(margin + signature.getType().toString() + ": " + sigValue + eol); + if (signature.getType().equals(SignatureType.MAGIC)) { + if (((InternalSignature) signature).hasFixedOffset()) { + texta.append(margin + "Offset: " + ((InternalSignature) signature).getOffset() + eol); + } + } + String note = signature.getNote(); + if (note != null) { + texta.append(margin + NOTE + note + eol); + } + String use = signature.getUse().toString(); + if (use != null) { + texta.append(margin + "Use: " + use + eol); + } + --_level; + } - private void showDocument(Document document, String label) { - String margin = getIndent(++_level); + private void showDocument(Document document, String label) { + String margin = getIndent(++_level); - texta.append(margin + label + ": " + document.getTitle() + eol); - texta.append(margin + TYPE + document.getType() + eol); - for (Agent agent : document.getAuthor()) { - showAgent(agent, "Author"); - } - for (Agent publisher : document.getPublisher()) { - showAgent(publisher, "Publisher"); - } - String s = document.getEdition(); - if (s != null) { - texta.append(margin + "Edition: " + s + eol); - } - if ((s = document.getDate()) != null) { - texta.append(margin + DATE + s + eol); - } - if ((s = document.getEnumeration()) != null) { - texta.append(margin + "Enumeration: " + s + eol); - } - if ((s = document.getPages()) != null) { - texta.append(margin + "Pages: " + s + eol); - } - for (Identifier id : document.getIdentifier()) { - showIdentifier(id); - } - if ((s = document.getNote()) != null) { - texta.append(margin + NOTE + s + eol); - } - _level--; - } + texta.append(margin + label + ": " + document.getTitle() + eol); + texta.append(margin + TYPE + document.getType() + eol); + for (Agent agent : document.getAuthor()) { + showAgent(agent, "Author"); + } + for (Agent publisher : document.getPublisher()) { + showAgent(publisher, "Publisher"); + } + String s = document.getEdition(); + if (s != null) { + texta.append(margin + "Edition: " + s + eol); + } + if ((s = document.getDate()) != null) { + texta.append(margin + DATE + s + eol); + } + if ((s = document.getEnumeration()) != null) { + texta.append(margin + "Enumeration: " + s + eol); + } + if ((s = document.getPages()) != null) { + texta.append(margin + "Pages: " + s + eol); + } + for (Identifier id : document.getIdentifier()) { + showIdentifier(id); + } + if ((s = document.getNote()) != null) { + texta.append(margin + NOTE + s + eol); + } + _level--; + } - private void showAgent(Agent agent, String label) { - String margin = getIndent(++_level); + private void showAgent(Agent agent, String label) { + String margin = getIndent(++_level); - texta.append(margin + label + ": " + agent.getName() + eol); - texta.append(margin + TYPE + agent.getType().toString() + eol); - String s = agent.getAddress(); - if (s != null) { - texta.append(margin + "Address: " + s + eol); - } - if ((s = agent.getTelephone()) != null) { - texta.append(margin + "Telephone: " + s + eol); - } - if ((s = agent.getFax()) != null) { - texta.append(margin + "Fax: " + s + eol); - } - if ((s = agent.getEmail()) != null) { - texta.append(margin + "Email: " + s + eol); - } - if ((s = agent.getWeb()) != null) { - texta.append(margin + "Web: " + s + eol); - } - _level--; - } + texta.append(margin + label + ": " + agent.getName() + eol); + texta.append(margin + TYPE + agent.getType().toString() + eol); + String s = agent.getAddress(); + if (s != null) { + texta.append(margin + "Address: " + s + eol); + } + if ((s = agent.getTelephone()) != null) { + texta.append(margin + "Telephone: " + s + eol); + } + if ((s = agent.getFax()) != null) { + texta.append(margin + "Fax: " + s + eol); + } + if ((s = agent.getEmail()) != null) { + texta.append(margin + "Email: " + s + eol); + } + if ((s = agent.getWeb()) != null) { + texta.append(margin + "Web: " + s + eol); + } + _level--; + } - private void showIdentifier(Identifier identifier) { - String margin = getIndent(++_level); + private void showIdentifier(Identifier identifier) { + String margin = getIndent(++_level); - texta.append(margin + "Identifier: " + identifier.getValue() + eol); - texta.append(margin + TYPE + identifier.getType().toString() + eol); - String note = identifier.getNote(); - if (note != null) { - texta.append(margin + NOTE + note + eol); - } - _level--; - } + texta.append(margin + "Identifier: " + identifier.getValue() + eol); + texta.append(margin + TYPE + identifier.getType().toString() + eol); + String note = identifier.getNote(); + if (note != null) { + texta.append(margin + NOTE + note + eol); + } + _level--; + } - private String getIndent(int lev) { - switch (lev) { - case 1: - return " "; - case 2: - return " "; - case 3: - return " "; - case 4: - return " "; - default: - return ""; - } - } + private String getIndent(int lev) { + switch (lev) { + case 1: + return " "; + case 2: + return " "; + case 3: + return " "; + case 4: + return " "; + default: + return ""; + } + } - /** - * Saves the information to a file - */ - private void saveInfo() { - if (_module == null) { - JOptionPane.showMessageDialog(this, "No module selected", - "Can't save", JOptionPane.INFORMATION_MESSAGE); - return; - } - PrintWriter wtr = doSaveDialog(); - if (wtr == null) { - return; - } - OutputHandler handler = selectHandler(); - try { - handler.setWriter(wtr); - handler.show(_module); - wtr.close(); - } catch (Exception e) { - JOptionPane.showMessageDialog(this, e.getMessage(), - "Error writing file", JOptionPane.ERROR_MESSAGE); - } - } + /** Saves the information to a file */ + private void saveInfo() { + if (_module == null) { + JOptionPane.showMessageDialog( + this, "No module selected", "Can't save", JOptionPane.INFORMATION_MESSAGE); + return; + } + PrintWriter wtr = doSaveDialog(); + if (wtr == null) { + return; + } + OutputHandler handler = selectHandler(); + try { + handler.setWriter(wtr); + handler.show(_module); + wtr.close(); + } catch (Exception e) { + JOptionPane.showMessageDialog( + this, e.getMessage(), "Error writing file", JOptionPane.ERROR_MESSAGE); + } + } } diff --git a/jhove-apps/src/main/java/edu/harvard/hul/ois/jhove/viewer/NoConfAlert.java b/jhove-apps/src/main/java/edu/harvard/hul/ois/jhove/viewer/NoConfAlert.java index f290f2899..a515267f7 100644 --- a/jhove-apps/src/main/java/edu/harvard/hul/ois/jhove/viewer/NoConfAlert.java +++ b/jhove-apps/src/main/java/edu/harvard/hul/ois/jhove/viewer/NoConfAlert.java @@ -1,60 +1,55 @@ -/********************************************************************** - * JhoveView - JSTOR/Harvard Object Validation Environment - * Copyright 2003 by JSTOR and the President and Fellows of Harvard College - **********************************************************************/ - +/** + * ******************************************************************** JhoveView - JSTOR/Harvard + * Object Validation Environment Copyright 2003 by JSTOR and the President and Fellows of Harvard + * College ******************************************************************** + */ package edu.harvard.hul.ois.jhove.viewer; import java.awt.*; import java.awt.event.*; import javax.swing.*; -/** - * This class implements an alert which is posted when no - * configuration file can be found. - */ -public class NoConfAlert extends JDialog -{ - public NoConfAlert (JFrame owner) - { - super (owner, "Fatal Error", true); +/** This class implements an alert which is posted when no configuration file can be found. */ +public class NoConfAlert extends JDialog { + public NoConfAlert(JFrame owner) { + super(owner, "Fatal Error", true); + + // Create a panel with an informative message. + JPanel mainPanel = new JPanel(); + getContentPane().add(mainPanel, "Center"); + String infoString = + new String( + "Jhove could not find " + + "a configuration file. You must have one of the " + + "following in jhove/conf under your home directory:\n\n" + + "(1) A properties file called jhove.properties " + + "with a property named edu.harvard.hul.ois.jhove.config " + + "having the path to your configuration file as its " + + "value; or\n\n" + + "(2) A configuration file named jhove.conf\n\n " + + "Note: Under Windows, your home directory is the directory " + + "with your username under Documents and Settings.\n"); - // Create a panel with an informative message. - JPanel mainPanel = new JPanel (); - getContentPane ().add (mainPanel, "Center"); - String infoString = new String ("Jhove could not find " + - "a configuration file. You must have one of the " + - "following in jhove/conf under your home directory:\n\n" + - "(1) A properties file called jhove.properties " + - "with a property named edu.harvard.hul.ois.jhove.config " + - "having the path to your configuration file as its " + - "value; or\n\n" + - "(2) A configuration file named jhove.conf\n\n " + - "Note: Under Windows, your home directory is the directory " + - "with your username under Documents and Settings.\n"); + JTextArea infoArea = new JTextArea(infoString); + infoArea.setLineWrap(true); + infoArea.setWrapStyleWord(true); + Dimension prefSize = new Dimension(250, 300); + infoArea.setMinimumSize(prefSize); + infoArea.setPreferredSize(prefSize); + mainPanel.add(infoArea); - JTextArea infoArea = new JTextArea (infoString); - infoArea.setLineWrap (true); - infoArea.setWrapStyleWord (true); - Dimension prefSize = new Dimension (250, 300); - infoArea.setMinimumSize (prefSize); - infoArea.setPreferredSize (prefSize); - mainPanel.add (infoArea); - - // Create a panel with a button for quitting. - JPanel bottomPanel = new JPanel (); - getContentPane ().add (bottomPanel, "South"); - JButton exitButton = new JButton ("Quit"); - bottomPanel.add (exitButton); - exitButton.addActionListener ( - new ActionListener () { - @Override - public void actionPerformed (ActionEvent e) - { - dispose (); - } - } - ); - pack (); - } + // Create a panel with a button for quitting. + JPanel bottomPanel = new JPanel(); + getContentPane().add(bottomPanel, "South"); + JButton exitButton = new JButton("Quit"); + bottomPanel.add(exitButton); + exitButton.addActionListener( + new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + dispose(); + } + }); + pack(); + } } diff --git a/jhove-apps/src/main/java/edu/harvard/hul/ois/jhove/viewer/NumericField.java b/jhove-apps/src/main/java/edu/harvard/hul/ois/jhove/viewer/NumericField.java index 6670aa6e7..c285d3ee4 100644 --- a/jhove-apps/src/main/java/edu/harvard/hul/ois/jhove/viewer/NumericField.java +++ b/jhove-apps/src/main/java/edu/harvard/hul/ois/jhove/viewer/NumericField.java @@ -1,31 +1,27 @@ -/********************************************************************** - * Jhove - JSTOR/Harvard Object Validation Environment - * Copyright 2004 by JSTOR and the President and Fellows of Harvard College - **********************************************************************/ - +/** + * ******************************************************************** Jhove - JSTOR/Harvard Object + * Validation Environment Copyright 2004 by JSTOR and the President and Fellows of Harvard College + * ******************************************************************** + */ package edu.harvard.hul.ois.jhove.viewer; -import javax.swing.*; import java.text.NumberFormat; +import javax.swing.*; /** - * * A JTextField which permits only decimal digits - * - * @author Gary McGath * + * @author Gary McGath */ public class NumericField extends JFormattedTextField { - // Initializer for number format - private static NumberFormat numFormat = - NumberFormat.getIntegerInstance (); - - public NumericField (int init) - { - super (numFormat); - numFormat.setGroupingUsed (false); - numFormat.setParseIntegerOnly (true); - setValue (new Integer (init)); - } + // Initializer for number format + private static NumberFormat numFormat = NumberFormat.getIntegerInstance(); + + public NumericField(int init) { + super(numFormat); + numFormat.setGroupingUsed(false); + numFormat.setParseIntegerOnly(true); + setValue(new Integer(init)); + } } diff --git a/jhove-apps/src/main/java/edu/harvard/hul/ois/jhove/viewer/PrefsWindow.java b/jhove-apps/src/main/java/edu/harvard/hul/ois/jhove/viewer/PrefsWindow.java index e344c54c8..c4b9af8b4 100644 --- a/jhove-apps/src/main/java/edu/harvard/hul/ois/jhove/viewer/PrefsWindow.java +++ b/jhove-apps/src/main/java/edu/harvard/hul/ois/jhove/viewer/PrefsWindow.java @@ -1,120 +1,101 @@ -/********************************************************************** - * Jhove - JSTOR/Harvard Object Validation Environment - * Copyright 2003 by JSTOR and the President and Fellows of Harvard College - **********************************************************************/ - +/** + * ******************************************************************** Jhove - JSTOR/Harvard Object + * Validation Environment Copyright 2003 by JSTOR and the President and Fellows of Harvard College + * ******************************************************************** + */ package edu.harvard.hul.ois.jhove.viewer; import java.awt.*; import java.awt.event.*; import javax.swing.*; - /** * Window for setting preferences for the Jhove viewer. * * @author Gary McGath - * */ -public class PrefsWindow extends JDialog -{ - private JhoveWindow jhoveWin; - - private JCheckBox rawCheckBox; - private JCheckBox checksumCheckBox; - - /* State saving information */ - private boolean saveRawOutput; - private boolean saveChecksum; - - /** - * Constructor. - * - */ - public PrefsWindow (JhoveWindow owner) - { - super (owner, "Jhove Preferences", true); - addWindowListener (new PrefsWindowListener (this)); - jhoveWin = owner; - JPanel mainPanel = new JPanel (new GridLayout (4, 1)); - getContentPane ().add (mainPanel, BorderLayout.CENTER); - rawCheckBox = new JCheckBox ("Raw data", false); - mainPanel.add (rawCheckBox); - checksumCheckBox = new JCheckBox ("Calculate checksums", false); - mainPanel.add (checksumCheckBox); - - JPanel bottomPanel = new JPanel (new GridLayout (1, 3)); - getContentPane ().add (bottomPanel, BorderLayout.SOUTH); - JButton okButton = new JButton ("OK"); - okButton.addActionListener ( - new ActionListener () { - @Override - public void actionPerformed (ActionEvent e) - { - setPrefsFromDialog (); - hide (); - } - } - ); - JButton cancelButton = new JButton ("Cancel"); - cancelButton.addActionListener ( - new ActionListener () { - @Override - public void actionPerformed (ActionEvent e) - { - hide (); - restore (); - } - } - ); - bottomPanel.add (new JLabel ("")); - bottomPanel.add (cancelButton); - bottomPanel.add (okButton); - } - - /** - * This is called when the window is made visible. - * (For efficiency, it is hidden rather than being - * disposed when the user clicks OK or cancel.) The - * state of the dialog is saved, then it is made visible. - * If the user clicks Cancel, the state of the dialog - * will be restored. - */ - public void saveAndShow () - { - saveRawOutput = rawCheckBox.isSelected (); - saveChecksum = checksumCheckBox.isSelected (); - show (); - } - - private void restore () - { - rawCheckBox.setSelected (saveRawOutput); - checksumCheckBox.setSelected (saveChecksum); - } - - private void setPrefsFromDialog () - { - jhoveWin.setRawOutput (rawCheckBox.isSelected ()); - jhoveWin.setDoChecksum(checksumCheckBox.isSelected ()); +public class PrefsWindow extends JDialog { + private JhoveWindow jhoveWin; + + private JCheckBox rawCheckBox; + private JCheckBox checksumCheckBox; + + /* State saving information */ + private boolean saveRawOutput; + private boolean saveChecksum; + + /** Constructor. */ + public PrefsWindow(JhoveWindow owner) { + super(owner, "Jhove Preferences", true); + addWindowListener(new PrefsWindowListener(this)); + jhoveWin = owner; + JPanel mainPanel = new JPanel(new GridLayout(4, 1)); + getContentPane().add(mainPanel, BorderLayout.CENTER); + rawCheckBox = new JCheckBox("Raw data", false); + mainPanel.add(rawCheckBox); + checksumCheckBox = new JCheckBox("Calculate checksums", false); + mainPanel.add(checksumCheckBox); + + JPanel bottomPanel = new JPanel(new GridLayout(1, 3)); + getContentPane().add(bottomPanel, BorderLayout.SOUTH); + JButton okButton = new JButton("OK"); + okButton.addActionListener( + new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + setPrefsFromDialog(); + hide(); + } + }); + JButton cancelButton = new JButton("Cancel"); + cancelButton.addActionListener( + new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + hide(); + restore(); + } + }); + bottomPanel.add(new JLabel("")); + bottomPanel.add(cancelButton); + bottomPanel.add(okButton); + } + + /** + * This is called when the window is made visible. (For efficiency, it is hidden rather than being + * disposed when the user clicks OK or cancel.) The state of the dialog is saved, then it is made + * visible. If the user clicks Cancel, the state of the dialog will be restored. + */ + public void saveAndShow() { + saveRawOutput = rawCheckBox.isSelected(); + saveChecksum = checksumCheckBox.isSelected(); + show(); + } + + private void restore() { + rawCheckBox.setSelected(saveRawOutput); + checksumCheckBox.setSelected(saveChecksum); + } + + private void setPrefsFromDialog() { + jhoveWin.setRawOutput(rawCheckBox.isSelected()); + jhoveWin.setDoChecksum(checksumCheckBox.isSelected()); + } + + /** + * ****************************************************** WindowAdapter subclass for handling + * window closing ****************************************************** + */ + private class PrefsWindowListener extends WindowAdapter { + private PrefsWindow prefsWin; + + public PrefsWindowListener(PrefsWindow w) { + prefsWin = w; } - - /******************************************************** - * WindowAdapter subclass for handling window closing - ********************************************************/ - - private class PrefsWindowListener extends WindowAdapter - { - private PrefsWindow prefsWin; - - public PrefsWindowListener (PrefsWindow w) { - prefsWin = w; - } - - - @Override - public void windowClosing (WindowEvent e) { - prefsWin.restore (); - } + + @Override + public void windowClosing(WindowEvent e) { + prefsWin.restore(); } + } } diff --git a/jhove-apps/src/main/java/edu/harvard/hul/ois/jhove/viewer/ProgressWindow.java b/jhove-apps/src/main/java/edu/harvard/hul/ois/jhove/viewer/ProgressWindow.java index b488f1c3a..521ac4fe4 100644 --- a/jhove-apps/src/main/java/edu/harvard/hul/ois/jhove/viewer/ProgressWindow.java +++ b/jhove-apps/src/main/java/edu/harvard/hul/ois/jhove/viewer/ProgressWindow.java @@ -1,176 +1,155 @@ -/********************************************************************** - * JhoveView - JSTOR/Harvard Object Validation Environment - * Copyright 2003 by JSTOR and the President and Fellows of Harvard College - **********************************************************************/ - +/** + * ******************************************************************** JhoveView - JSTOR/Harvard + * Object Validation Environment Copyright 2003 by JSTOR and the President and Fellows of Harvard + * College ******************************************************************** + */ package edu.harvard.hul.ois.jhove.viewer; import java.awt.*; -import javax.swing.*; import java.awt.event.*; +import javax.swing.*; /** - * Window for showing progress of file processing. - * We may or may not have the total length available; - * if we do, we show the total length as part of the - * information. - * Normally we keep one window alive for the whole application, - * showing and hiding it as needed. + * Window for showing progress of file processing. We may or may not have the total length + * available; if we do, we show the total length as part of the information. Normally we keep one + * window alive for the whole application, showing and hiding it as needed. */ -public class ProgressWindow extends JFrame{ - - private long _contentLength; - private long _byteCount; - private String _docName; - private JLabel _progressLabel; - private JLabel _docNameLabel; - private int _progressState; - - /** - * Progress state: Indeterminate or not yet started. - */ - public final static int UNKNOWN = 0; - /** - * Progress state: URI is being downloaded. - */ - public final static int DOWNLOADING = 1; - /** - * Progress state: Processing the document. - */ - public final static int PROCESSING = 2; - - private final static String DIALOG = "Dialog"; - - - /** - * Constructor. - * - * @param canceler An ActionListener which responds to the - * Cancel button. - */ - public ProgressWindow (ActionListener canceler) - { - Font fileFont = new Font (DIALOG, Font.PLAIN, 14); - Font progFont = new Font (DIALOG, Font.PLAIN, 12); - Dimension labelDim = new Dimension (460, 24); - _docNameLabel = new JLabel (); - _docNameLabel.setHorizontalAlignment (SwingConstants.CENTER); - _docNameLabel.setFont (fileFont); - _docNameLabel.setMinimumSize (labelDim); - _docNameLabel.setPreferredSize (labelDim); - getContentPane ().add (_docNameLabel, BorderLayout.NORTH); - - _progressLabel = new JLabel (); - _progressLabel.setHorizontalAlignment (SwingConstants.CENTER); - _progressLabel.setFont (progFont); - _progressLabel.setMinimumSize (labelDim); - _progressLabel.setPreferredSize (labelDim); - getContentPane ().add (_progressLabel, BorderLayout.CENTER); - - // Put the Cancel button inside a panel so it doesn't - // fill the whole space. - JPanel botPanel = new JPanel (); - getContentPane ().add (botPanel, BorderLayout.SOUTH); - JButton cancelButton = new JButton ("Cancel"); - botPanel.add (cancelButton); - cancelButton.addActionListener (canceler); - - setTitle ("Progress"); - setDefaultCloseOperation (WindowConstants.DO_NOTHING_ON_CLOSE); - pack (); - MainScreen.centerTopWindow (this); - - _contentLength = -1; - _byteCount = -1; - _progressState = UNKNOWN; +public class ProgressWindow extends JFrame { + + private long _contentLength; + private long _byteCount; + private String _docName; + private JLabel _progressLabel; + private JLabel _docNameLabel; + private int _progressState; + + /** Progress state: Indeterminate or not yet started. */ + public static final int UNKNOWN = 0; + /** Progress state: URI is being downloaded. */ + public static final int DOWNLOADING = 1; + /** Progress state: Processing the document. */ + public static final int PROCESSING = 2; + + private static final String DIALOG = "Dialog"; + + /** + * Constructor. + * + * @param canceler An ActionListener which responds to the Cancel button. + */ + public ProgressWindow(ActionListener canceler) { + Font fileFont = new Font(DIALOG, Font.PLAIN, 14); + Font progFont = new Font(DIALOG, Font.PLAIN, 12); + Dimension labelDim = new Dimension(460, 24); + _docNameLabel = new JLabel(); + _docNameLabel.setHorizontalAlignment(SwingConstants.CENTER); + _docNameLabel.setFont(fileFont); + _docNameLabel.setMinimumSize(labelDim); + _docNameLabel.setPreferredSize(labelDim); + getContentPane().add(_docNameLabel, BorderLayout.NORTH); + + _progressLabel = new JLabel(); + _progressLabel.setHorizontalAlignment(SwingConstants.CENTER); + _progressLabel.setFont(progFont); + _progressLabel.setMinimumSize(labelDim); + _progressLabel.setPreferredSize(labelDim); + getContentPane().add(_progressLabel, BorderLayout.CENTER); + + // Put the Cancel button inside a panel so it doesn't + // fill the whole space. + JPanel botPanel = new JPanel(); + getContentPane().add(botPanel, BorderLayout.SOUTH); + JButton cancelButton = new JButton("Cancel"); + botPanel.add(cancelButton); + cancelButton.addActionListener(canceler); + + setTitle("Progress"); + setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE); + pack(); + MainScreen.centerTopWindow(this); + + _contentLength = -1; + _byteCount = -1; + _progressState = UNKNOWN; + } + + /** + * Set the total length to be displayed. If this is set to a positive number, then the display + * will show "xxxx bytes of yyyy". If it is not set, or is set to a zero or negative number, the + * display will show "xxxx bytes". + * + * @param length The length to display + * @param update If true, requests a display update. + */ + public void setContentLength(long length, boolean update) { + _contentLength = length; + if (update) { + updateDisplay(); } - - /** - * Set the total length to be displayed. If this is set - * to a positive number, then the display will show - * "xxxx bytes of yyyy". If it is not set, or is set - * to a zero or negative number, the display will show - * "xxxx bytes". - * - * @param length The length to display - * @param update If true, requests a display update. - */ - public void setContentLength (long length, boolean update) - { - _contentLength = length; - if (update) { - updateDisplay (); - } + } + + /** + * Set the progress state. + * + * @param state The state value to assign. Valid values are UNKNOWN, DOWNLOADING, and PROCESSING. + * @param update If true, requests a display update + */ + public void setProgressState(int state, boolean update) { + _progressState = state; + if (update) { + updateDisplay(); } - - /** Set the progress state. - * - * @param state The state value to assign. Valid values are - * UNKNOWN, DOWNLOADING, and PROCESSING. - * @param update If true, requests a display update - */ - public void setProgressState (int state, boolean update) { - _progressState = state; - if (update) { - updateDisplay (); - } + } + + /** + * Set the name of the document being displayed. + * + * @param name The file name or URL to display + * @param update If true, requests a display update + */ + public void setDocName(String name, boolean update) { + _docName = name; + if (update) { + updateDisplay(); } - - - /** - * Set the name of the document being displayed. - * - * @param name The file name or URL to display - * @param update If true, requests a display update - */ - public void setDocName (String name, boolean update) - { - _docName = name; - if (update) { - updateDisplay (); - } + } + + /** + * Update the byte count. Setting the count to a negative number blanks the byte count pane. + * + * @param count The new byte count value + * @param update If true, requests a display update + */ + public void setByteCount(long count, boolean update) { + _byteCount = count; + if (update) { + updateDisplay(); } - - - /** - * Update the byte count. Setting the count to a negative number blanks - * the byte count pane. - * - * @param count The new byte count value - * @param update If true, requests a display update - */ - public void setByteCount (long count, boolean update) - { - _byteCount = count; - if (update) { - updateDisplay (); - } + } + + private void updateDisplay() { + String progString = ""; + String txt; + switch (_progressState) { + case DOWNLOADING: + txt = "Downloading " + _docName; + break; + case PROCESSING: + txt = "Processing " + _docName; + break; + default: + txt = _docName; + break; } - - private void updateDisplay () - { - String progString = ""; - String txt; - switch (_progressState) { - case DOWNLOADING: - txt = "Downloading " + _docName; - break; - case PROCESSING: - txt = "Processing " + _docName; - break; - default: - txt = _docName; - break; - } - _docNameLabel.setText (txt); - if (_byteCount >= 0) { - progString = Long.toString (_byteCount) + " bytes"; - if (_contentLength > 0) { - progString += " out of " + Long.toString (_contentLength); - } - } - _progressLabel.setText (progString); - Container content = getContentPane (); - content.update (content.getGraphics ()); + _docNameLabel.setText(txt); + if (_byteCount >= 0) { + progString = Long.toString(_byteCount) + " bytes"; + if (_contentLength > 0) { + progString += " out of " + Long.toString(_contentLength); + } } + _progressLabel.setText(progString); + Container content = getContentPane(); + content.update(content.getGraphics()); + } } diff --git a/jhove-apps/src/main/java/edu/harvard/hul/ois/jhove/viewer/RepTreeRoot.java b/jhove-apps/src/main/java/edu/harvard/hul/ois/jhove/viewer/RepTreeRoot.java index 13cabad83..d9e98645a 100644 --- a/jhove-apps/src/main/java/edu/harvard/hul/ois/jhove/viewer/RepTreeRoot.java +++ b/jhove-apps/src/main/java/edu/harvard/hul/ois/jhove/viewer/RepTreeRoot.java @@ -1,20 +1,10 @@ -/********************************************************************** - * Jhove - JSTOR/Harvard Object Validation Environment - * Copyright 2003-2005 by JSTOR and the President and Fellows of Harvard College - **********************************************************************/ - +/** + * ******************************************************************** Jhove - JSTOR/Harvard Object + * Validation Environment Copyright 2003-2005 by JSTOR and the President and Fellows of Harvard + * College ******************************************************************** + */ package edu.harvard.hul.ois.jhove.viewer; -import java.text.DateFormat; -import java.util.Arrays; -import java.util.Date; -import java.util.Iterator; -import java.util.List; -import java.util.Map; -import java.util.Set; - -import javax.swing.tree.DefaultMutableTreeNode; - import edu.harvard.hul.ois.jhove.AESAudioMetadata; import edu.harvard.hul.ois.jhove.Checksum; import edu.harvard.hul.ois.jhove.ErrorMessage; @@ -29,1490 +19,1374 @@ import edu.harvard.hul.ois.jhove.Rational; import edu.harvard.hul.ois.jhove.RepInfo; import edu.harvard.hul.ois.jhove.TextMDMetadata; +import java.text.DateFormat; +import java.util.Arrays; +import java.util.Date; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Set; +import javax.swing.tree.DefaultMutableTreeNode; /** - * This subclass of DefaultMutableTreeNode simply adds a method for constructing - * the tree. All nodes in the tree except for the root will be plain - * DefaultMutablereeNodes. + * This subclass of DefaultMutableTreeNode simply adds a method for constructing the tree. All nodes + * in the tree except for the root will be plain DefaultMutablereeNodes. */ public class RepTreeRoot extends DefaultMutableTreeNode { - /** - * Serialisation identifier - */ - private static final long serialVersionUID = -4409152022584715925L; - private RepInfo _info; - private JhoveBase _base; - private boolean _rawOutput; - private DateFormat _dateFmt; - - /* Sample rate. */ - private double _sampleRate; - - /** - * Constructor. - * - * @param info - * The RepInfo object whose contents are to be displayed. - * @param base - * The JHOVE base on which we're operating. - */ - public RepTreeRoot(RepInfo info, JhoveBase base) { - super(info.getUri()); - _info = info; - _base = base; - _rawOutput = _base.getShowRawFlag(); - - // Set the DateFormat for displaying the module date. - _dateFmt = DateFormat.getDateInstance(); - - // Snarf everything up into the tree. - - snarfRepInfo(); - } - - private static final String TEXT_MD_METADTA = "TextMDMetadata"; - private static final String FORMAT = "Format: "; - private static final String VERSION = "Version: "; - private static final String BYTE_ORDER = "ByteOrder: "; - private static final String FRAME_COUNT_30 = "FrameCount: 30"; - private static final String TIME_BASE_1000 = "TimeBase: 1000"; - private static final String VIDEO_FIELD_FIELD_1 = "VideoField: FIELD_1"; - private static final String COUNTING_MODE_NTSC_NON_DROP_FRAME= "CountingMode: NTSC_NON_DROP_FRAME"; - private static final String HOURS = "Hours: "; - private static final String MINUTES = "Minutes: "; - private static final String SECONDS = "Seconds: "; - private static final String FRAMES = "Frames: "; - private static final String SAMPLES = "Samples"; - private static final String NUMBER_OF_SAMPLES = "NumberOfSamples: "; - private static final String FILM_FRAMING = "FilmFraming"; - private static final String FRAMING_NOT_APPLICABLE = "Framing: NOT_APPLICABLE"; - private static final String NTSC_FILM_FRAMING_TYPE = "Type: ntscFilmFramingType"; - - - - /** - * Constructs a DefaultMutableTreeNode representing a property - */ - private DefaultMutableTreeNode propToNode(Property pProp) { - PropertyArity arity = pProp.getArity(); - PropertyType typ = pProp.getType(); - Object pValue = pProp.getValue(); - if (arity == PropertyArity.SCALAR) { - if (null == typ) { - // Simple types: just use name plus string value. - DefaultMutableTreeNode val = new DefaultMutableTreeNode( - pProp.getName() + ": " + pValue.toString()); - return val; - } else { - TextMDMetadata tData; - DefaultMutableTreeNode val; - switch (typ) { - case NISOIMAGEMETADATA: - // NISO Image metadata is a world of its own. - NisoImageMetadata nData = (NisoImageMetadata) pValue; - return nisoToNode(nData); - case AESAUDIOMETADATA: - // AES audio metadata is another world. - AESAudioMetadata aData = (AESAudioMetadata) pValue; - return aesToNode(aData); - case TEXTMDMETADATA: - // textMD metadata is another world. - tData = (TextMDMetadata) pValue; - return textMDToNode(tData); - case PROPERTY: - - if (TEXT_MD_METADTA.equals(pProp.getName())) { - tData = (TextMDMetadata) pValue; - return textMDToNode(tData); - } - // A scalar property of type Property -- seems - // pointless, but we handle it. - val = new DefaultMutableTreeNode(pProp.getName()); - val.add(propToNode((Property) pValue)); - return val; - - default: - - // Simple types: just use name plus string value. - val = new DefaultMutableTreeNode(pProp.getName() + ": " - + pValue.toString()); - return val; - - } - } - } - // Compound properties. The text of the node is the - // property name. - DefaultMutableTreeNode val = new DefaultMutableTreeNode(pProp.getName()); - if (null != arity) - switch (arity) { - case ARRAY: - addArrayMembers(val, pProp); - break; - case LIST: - addListMembers(val, pProp); - break; - case MAP: - addMapMembers(val, pProp); - break; - case SET: - addSetMembers(val, pProp); - break; - default: - break; - } - return val; - } - - /** - * Find the index of an object in its parent. Understands the Jhove property - * structure. - */ - public int getIndexOfChild(Object parent, Object child) { - Property pProp = (Property) parent; - PropertyArity arity = pProp.getArity(); - // For Lists, Maps, and Sets we construct an Iterator. - Iterator iter = null; - if (arity == PropertyArity.SET || arity == PropertyArity.LIST - || arity == PropertyArity.MAP) { - if (null == arity) { - List list = (List) pProp.getValue(); - iter = list.iterator(); - } else - switch (arity) { - case SET: - Set set = (Set) pProp.getValue(); - iter = set.iterator(); - break; - case MAP: - Map map = (Map) pProp.getValue(); - iter = map.values().iterator(); - break; - default: - List list = (List) pProp.getValue(); - iter = list.iterator(); - break; - } - for (int i = 0;; i++) { - if (!iter.hasNext()) { - return 0; // Should never happen - } else if (iter.next() == child) { - return i; - } - } - } - // OK, that was the easy one. Now for that damn array arity. - // In the case of non-object types, we can't actually tell which - // position matches the object, so we return 0 and hope it doesn't - // mess things up too much. - PropertyType propType = pProp.getType(); - java.util.Date[] dateArray = null; - Property[] propArray = null; - Rational[] rationalArray = null; - Object[] objArray = null; - int n = 0; - - if (null == propType) { - return 0; // non-object array type - } else - // if (child instanceof LeafHolder) { - // return ((LeafHolder) child).getPosition (); - // } - // else - switch (propType) { - case DATE: - dateArray = (java.util.Date[]) pProp.getValue(); - n = dateArray.length; - break; - case OBJECT: - objArray = (Object[]) pProp.getValue(); - n = objArray.length; - break; - case RATIONAL: - rationalArray = (Rational[]) pProp.getValue(); - n = rationalArray.length; - break; - case PROPERTY: - propArray = (Property[]) pProp.getValue(); - n = propArray.length; - break; - default: - return 0; // non-object array type - } - - for (int i = 0; i < n; i++) { - Object elem = null; - switch (propType) { - case DATE: - elem = dateArray[i]; - break; - case OBJECT: - elem = objArray[i]; - break; - case RATIONAL: - elem = rationalArray[i]; - break; - case PROPERTY: - elem = propArray[i]; - break; - default: - break; - } - if (elem == child) { - return i; - } - } - return 0; // somehow fell through - } - - private void snarfRepInfo() { - // This node has two children, for the module and the RepInfo - - Module module = _info.getModule(); - if (module != null) { - // Create a subnode for the module, which has three - // leaf children. - DefaultMutableTreeNode moduleNode = new DefaultMutableTreeNode( - "Module"); - moduleNode.add(new DefaultMutableTreeNode(module.getName(), false)); - moduleNode.add(new DefaultMutableTreeNode("Release: " - + module.getRelease(), false)); - moduleNode.add(new DefaultMutableTreeNode("Date: " - + _dateFmt.format(module.getDate()), false)); - add(moduleNode); - } - - DefaultMutableTreeNode infoNode = new DefaultMutableTreeNode("RepInfo"); - infoNode.add(new DefaultMutableTreeNode("URI: " + _info.getUri(), false)); - Date dt = _info.getCreated(); - if (dt != null) { - infoNode.add(new DefaultMutableTreeNode( - "Created: " + dt.toString(), false)); - } - dt = _info.getLastModified(); - if (dt != null) { - infoNode.add(new DefaultMutableTreeNode("LastModified: " - + dt.toString(), false)); - } - long sz = _info.getSize(); - if (sz != -1) { - infoNode.add(new DefaultMutableTreeNode("Size: " - + Long.toString(sz), false)); - } - String s = _info.getFormat(); - if (s != null) { - infoNode.add(new DefaultMutableTreeNode(FORMAT + s, false)); - } - s = _info.getVersion(); - if (s != null) { - infoNode.add(new DefaultMutableTreeNode(VERSION + s, false)); - } - String wfStr; - switch (_info.getWellFormed()) { - case RepInfo.TRUE: - wfStr = "Well-Formed"; - break; - case RepInfo.FALSE: - wfStr = "Not well-formed"; - break; - default: - wfStr = "Unknown"; - break; - } - if (_info.getWellFormed() == RepInfo.TRUE) { - switch (_info.getValid()) { - case RepInfo.TRUE: - wfStr += " and valid"; - break; - - case RepInfo.FALSE: - wfStr += ", but not valid"; - break; - - // case UNDETERMINED: add nothing - } - } - infoNode.add(new DefaultMutableTreeNode("Status: " + wfStr, false)); - - // Report modules that said their signatures match - List sigList = _info.getSigMatch(); - if (sigList != null && sigList.size() > 0) { - DefaultMutableTreeNode sigNode = new DefaultMutableTreeNode( - "SignatureMatches"); - infoNode.add(sigNode); - for (int i = 0; i < sigList.size(); i++) { - DefaultMutableTreeNode sNode = new DefaultMutableTreeNode( - sigList.get(i)); - sigNode.add(sNode); - } - } - // Compile a list of messages and offsets into a subtree - List messageList = _info.getMessage(); - if (messageList != null && messageList.size() > 0) { - DefaultMutableTreeNode msgNode = new DefaultMutableTreeNode( - "Messages"); - infoNode.add(msgNode); - int i; - for (i = 0; i < messageList.size(); i++) { - Message msg = messageList.get(i); - String prefix; - if (msg instanceof InfoMessage) { - prefix = "InfoMessage: "; - } else if (msg instanceof ErrorMessage) { - prefix = "ErrorMessage: "; - } else { - prefix = "Message: "; - } - DefaultMutableTreeNode mNode = new DefaultMutableTreeNode( - prefix + msg.getMessage()); - - if (msg.getId() != null && !msg.getId().isEmpty()) { - mNode.add(new DefaultMutableTreeNode("ID: " - + msg.getId())); - } - - String subMessage = msg.getSubMessage(); - if (subMessage != null) { - mNode.add(new DefaultMutableTreeNode("SubMessage: " - + subMessage)); - } - long offset = -1; - if (msg instanceof ErrorMessage) { - offset = ((ErrorMessage) msg).getOffset(); - } - // - // If the offset is positive, we give the message node - // a child with the offset value. - if (offset >= 0) { - mNode.add(new DefaultMutableTreeNode("Offset: " - + Long.toString(offset))); - } - msgNode.add(mNode); - } - } - - s = _info.getMimeType(); - if (s != null) { - infoNode.add(new DefaultMutableTreeNode("MimeType: " + s, false)); - } - - // Compile a list of profile strings into a string list - List profileList = _info.getProfile(); - if (profileList != null && profileList.size() > 0) { - DefaultMutableTreeNode profNode = new DefaultMutableTreeNode( - "Profiles"); - infoNode.add(profNode); - int i; - for (i = 0; i < profileList.size(); i++) { - profNode.add(new DefaultMutableTreeNode(profileList.get(i), - false)); - } - } - - // Here we come to the property map. We have to walk - // through all the properties recursively, turning - // each into a leaf or subtree. - Map map = _info.getProperty(); - if (map != null) { - Iterator iter = map.keySet().iterator(); - while (iter.hasNext()) { - String key = iter.next(); - Property property = _info.getProperty(key); - infoNode.add(propToNode(property)); - } - } - - List cksumList = _info.getChecksum(); - if (cksumList != null && !cksumList.isEmpty()) { - DefaultMutableTreeNode ckNode = new DefaultMutableTreeNode( - "Checksums"); - infoNode.add(ckNode); - // List cPropList = new LinkedList (); - for (Checksum cksum : cksumList) { - DefaultMutableTreeNode csNode = new DefaultMutableTreeNode( - "Checksum"); - ckNode.add(csNode); - csNode.add(new DefaultMutableTreeNode("Type:" - + cksum.getType().toString(), false)); - csNode.add(new DefaultMutableTreeNode("Checksum: " - + cksum.getValue(), false)); - } - } - - s = _info.getNote(); - if (s != null) { - infoNode.add(new DefaultMutableTreeNode("Note: " + s, false)); - } - add(infoNode); - } - - /* - * Add the members of an array property to a node. The property must be of - * arity ARRAY. - */ - private void addArrayMembers(DefaultMutableTreeNode node, Property p) { - Object pVal = p.getValue(); - PropertyType typ = p.getType(); - if (null != typ) - switch (typ) { - case INTEGER: { - if (pVal instanceof int[]) { - Integer[] vals = Arrays.stream((int[])pVal) // IntStream - .boxed() // Stream - .toArray(Integer[]::new); - addToNode(node, vals); - } else { - addToNode(node, (Integer[]) pVal); - } - break; - } - case LONG: { - if (pVal instanceof long[]) { - Long[] vals = Arrays.stream((long[])pVal) // IntStream - .boxed() // Stream - .toArray(Long[]::new); - addToNode(node, vals); - } else { - addToNode(node, (Long[]) pVal); - } - break; - } - case BOOLEAN: { - addToNode(node, (Boolean[]) pVal); - break; - } - case CHARACTER: { - addToNode(node, (Character[]) pVal); - break; - } - case DOUBLE: { - addToNode(node, (Double[]) pVal); - break; - } - case FLOAT: { - addToNode(node, (Float[]) pVal); - break; - } - case SHORT: { - addToNode(node, (Short[]) pVal); - break; - } - case BYTE: { - addToNode(node, (Byte[]) pVal); - break; - } - case STRING: { - addToNode(node, (String[]) pVal); - break; - } - case RATIONAL: { - addToNode(node, (Rational[]) pVal); - break; - } - case PROPERTY: { - addToNode(node, (Property[]) pVal); - break; - } - case NISOIMAGEMETADATA: { - addToNode(node, (NisoImageMetadata[]) pVal); - break; - } - case OBJECT: { - addToNode(node, (Object[]) pVal); - break; - } - default: - break; - } - } - - /* - * Add the members of a list property to a node. The property must be of - * arity LIST. - */ - private void addListMembers(DefaultMutableTreeNode node, Property p) { - List l = (List) p.getValue(); - PropertyType ptyp = p.getType(); - boolean canHaveChildren = Boolean.FALSE; - l.forEach(item -> node.add(getDefaultMutableTreeNode(ptyp, item, - canHaveChildren))); - } - - /* - * Add the members of a set property to a node. The property must be of - * arity SET. - */ - private void addSetMembers(DefaultMutableTreeNode node, Property p) { - Set s = (Set) p.getValue(); - PropertyType ptyp = p.getType(); - boolean canHaveChildren = Boolean.FALSE; - Iterator iter = s.iterator(); - while (iter.hasNext()) { - Object item = iter.next(); - node.add(getDefaultMutableTreeNode(ptyp, item, canHaveChildren)); - } - } - - /* - * Add the members of a map property to a node. The property must be of - * arity MAP. - */ - private void addMapMembers(DefaultMutableTreeNode node, Property p) { - Map m = (Map) p.getValue(); - PropertyType ptyp = p.getType(); - Boolean canHaveChildren = Boolean.TRUE; - // Iterator iter = m.values ().iterator (); - Iterator iter = m.keySet().iterator(); - while (iter.hasNext()) { - DefaultMutableTreeNode itemNode; - String key = (String) iter.next(); - Object item = m.get(key); - itemNode = getDefaultMutableTreeNode(ptyp, item, canHaveChildren); - node.add(itemNode); - - // Add a subnode for the key - itemNode.setAllowsChildren(true); - itemNode.add(new DefaultMutableTreeNode("Key: " + key, false)); - } - } - - /* Function for turning the AES metadata into a subtree. */ - private DefaultMutableTreeNode aesToNode(AESAudioMetadata aes) { - _sampleRate = aes.getSampleRate(); - - DefaultMutableTreeNode val = new DefaultMutableTreeNode( - "AESAudioMetadata", true); - String s = aes.getAnalogDigitalFlag(); - if (s != null) { - val.add(new DefaultMutableTreeNode("AnalogDigitalFlag: " + s, false)); - // The "false" argument signifies this will have no subnodes - } - s = aes.getSchemaVersion(); - if (s != null) { - val.add(new DefaultMutableTreeNode("SchemaVersion: " + s, false)); - } - s = aes.getFormat(); - if (s != null) { - DefaultMutableTreeNode fmt = new DefaultMutableTreeNode(FORMAT - + s, true); - val.add(fmt); - String v = aes.getSpecificationVersion(); - if (v != null) { - fmt.add(new DefaultMutableTreeNode( - "SpecificationVersion: " + v, false)); - } - } - s = aes.getAppSpecificData(); - if (s != null) { - val.add(new DefaultMutableTreeNode("AppSpecificData: " + s, false)); - } - s = aes.getAudioDataEncoding(); - if (s != null) { - val.add(new DefaultMutableTreeNode("AudioDataEncoding: " + s, false)); - } - int in = aes.getByteOrder(); - if (in != AESAudioMetadata.NULL) { - val.add(new DefaultMutableTreeNode(BYTE_ORDER - + (in == AESAudioMetadata.BIG_ENDIAN ? "BIG_ENDIAN" - : "LITTLE_ENDIAN"))); - } - long lin = aes.getFirstSampleOffset(); - if (lin != AESAudioMetadata.NULL) { - val.add(new DefaultMutableTreeNode("FirstSampleOffset: " - + Long.toString(lin))); - } - String[] use = aes.getUse(); - if (use != null) { - DefaultMutableTreeNode u = new DefaultMutableTreeNode("Use", true); - val.add(u); - u.add(new DefaultMutableTreeNode("UseType: " + use[0], false)); - u.add(new DefaultMutableTreeNode("OtherType: " + use[1], false)); - } - s = aes.getPrimaryIdentifier(); - if (s != null) { - String t = aes.getPrimaryIdentifierType(); - DefaultMutableTreeNode pi = new DefaultMutableTreeNode( - "PrimaryIdentifier: " + s, true); - val.add(pi); - if (t != null) { - pi.add(new DefaultMutableTreeNode("IdentifierType: " + t)); - } - } - // Add the face information, which is mostly filler. - // In the general case, it can contain multiple Faces; - // this isn't supported yet. - List facelist = aes.getFaceList(); - if (!facelist.isEmpty()) { - AESAudioMetadata.Face f = facelist.get(0); - - DefaultMutableTreeNode face = new DefaultMutableTreeNode("Face", - true); - DefaultMutableTreeNode timeline = new DefaultMutableTreeNode( - "TimeLine", true); - AESAudioMetadata.TimeDesc startTime = f.getStartTime(); - if (startTime != null) { - addAESTimeRange(timeline, startTime, f.getDuration()); - } - face.add(timeline); - - // For the present, assume just one face region - AESAudioMetadata.FaceRegion facergn = f.getFaceRegion(0); - DefaultMutableTreeNode region = new DefaultMutableTreeNode( - "Region", true); - timeline = new DefaultMutableTreeNode("TimeRange", true); - addAESTimeRange(timeline, facergn.getStartTime(), - facergn.getDuration()); - region.add(timeline); - int nchan = aes.getNumChannels(); - if (nchan != AESAudioMetadata.NULL) { - String[] locs = aes.getMapLocations(); - region.add(new DefaultMutableTreeNode("NumChannels: " - + Integer.toString(nchan), false)); - for (String loc : locs) { - // write a stream element for each channel - DefaultMutableTreeNode stream = new DefaultMutableTreeNode( - "Stream", true); - region.add(stream); - stream.add(new DefaultMutableTreeNode("ChannelAssignment: " - + loc, false)); - } - } - face.add(region); - val.add(face); - } - // In the general case, a FormatList can contain multiple - // FormatRegions. This doesn't happen with any of the current - // modules; if it's needed in the future, simply set up an - // iteration loop on formatList. - List flist = aes.getFormatList(); - if (!flist.isEmpty()) { - AESAudioMetadata.FormatRegion rgn = flist.get(0); - int bitDepth = rgn.getBitDepth(); - double sampleRate = rgn.getSampleRate(); - int wordSize = rgn.getWordSize(); - String[] bitRed = rgn.getBitrateReduction(); - // Build a FormatRegion subtree if at least one piece of data - // that goes into it is present. - if (bitDepth != AESAudioMetadata.NULL - || sampleRate != AESAudioMetadata.NILL - || wordSize != AESAudioMetadata.NULL) { - DefaultMutableTreeNode formatList = new DefaultMutableTreeNode( - "FormatList", true); - DefaultMutableTreeNode formatRegion = new DefaultMutableTreeNode( - "FormatRegion", true); - if (bitDepth != AESAudioMetadata.NULL) { - formatRegion.add(new DefaultMutableTreeNode("BitDepth: " - + Integer.toString(bitDepth), false)); - } - if (sampleRate != AESAudioMetadata.NILL) { - formatRegion.add(new DefaultMutableTreeNode("SampleRate: " - + Double.toString(sampleRate), false)); - } - if (wordSize != AESAudioMetadata.NULL) { - formatRegion.add(new DefaultMutableTreeNode("WordSize: " - + Integer.toString(bitDepth), false)); - } - if (bitRed != null) { - DefaultMutableTreeNode br = new DefaultMutableTreeNode( - "BitrateReduction", true); - br.add(new DefaultMutableTreeNode( - "codecName: " + bitRed[0], false)); - br.add(new DefaultMutableTreeNode("codecNameVersion: " - + bitRed[1], false)); - br.add(new DefaultMutableTreeNode( - "codecCreatorApplication: " + bitRed[2], false)); - br.add(new DefaultMutableTreeNode( - "codecCreatorApplicationVersion: " + bitRed[3], - false)); - br.add(new DefaultMutableTreeNode("codecQuality: " - + bitRed[4], false)); - br.add(new DefaultMutableTreeNode("dataRate: " + bitRed[5], - false)); - br.add(new DefaultMutableTreeNode("dataRateMode: " - + bitRed[6], false)); - formatRegion.add(br); - } - formatList.add(formatRegion); - val.add(formatList); - } - } - - return val; - } - - private void addAESTimeRange(DefaultMutableTreeNode parent, - AESAudioMetadata.TimeDesc start, AESAudioMetadata.TimeDesc duration) { - // Put the start time in - DefaultMutableTreeNode node = new DefaultMutableTreeNode("Start", true); - // Put in boilerplate to match the AES schema - node.add(new DefaultMutableTreeNode(FRAME_COUNT_30, false)); - node.add(new DefaultMutableTreeNode(TIME_BASE_1000)); - node.add(new DefaultMutableTreeNode(VIDEO_FIELD_FIELD_1)); - node.add(new DefaultMutableTreeNode( - COUNTING_MODE_NTSC_NON_DROP_FRAME, false)); - node.add(new DefaultMutableTreeNode(HOURS + start.getHours(), false)); - node.add(new DefaultMutableTreeNode(MINUTES + start.getMinutes(), - false)); - node.add(new DefaultMutableTreeNode(SECONDS + start.getSeconds(), - false)); - node.add(new DefaultMutableTreeNode(FRAMES + start.getFrames(), - false)); - - // Do samples a bit more elaborately than is really necessary, - // to maintain parallelism with the xml schema. - DefaultMutableTreeNode snode = new DefaultMutableTreeNode(SAMPLES, - true); - double sr = start.getSampleRate(); - if (sr == 1.0) { - sr = _sampleRate; - } - snode.add(new DefaultMutableTreeNode("SampleRate: S" - + Integer.toString((int) sr), false)); - snode.add(new DefaultMutableTreeNode(NUMBER_OF_SAMPLES - + start.getSamples(), false)); - node.add(snode); - - snode = new DefaultMutableTreeNode(FILM_FRAMING, true); - snode.add(new DefaultMutableTreeNode(FRAMING_NOT_APPLICABLE, false)); - snode.add(new DefaultMutableTreeNode(NTSC_FILM_FRAMING_TYPE, false)); - node.add(snode); - parent.add(node); - - // Duration is optional. - if (duration != null) { - node = new DefaultMutableTreeNode("Duration", true); - // Put in boilerplate to match the AES schema - node.add(new DefaultMutableTreeNode(FRAME_COUNT_30, false)); - node.add(new DefaultMutableTreeNode(TIME_BASE_1000)); - node.add(new DefaultMutableTreeNode(VIDEO_FIELD_FIELD_1)); - node.add(new DefaultMutableTreeNode( - COUNTING_MODE_NTSC_NON_DROP_FRAME, false)); - node.add(new DefaultMutableTreeNode( - HOURS + duration.getHours(), false)); - node.add(new DefaultMutableTreeNode(MINUTES - + duration.getMinutes(), false)); - node.add(new DefaultMutableTreeNode(SECONDS - + duration.getSeconds(), false)); - node.add(new DefaultMutableTreeNode(FRAMES - + duration.getFrames(), false)); - - // Do samples a bit more elaborately than is really necessary, - // to maintain parallelism with the xml schema. - snode = new DefaultMutableTreeNode(SAMPLES, true); - sr = duration.getSampleRate(); - if (sr == 1.0) { - sr = _sampleRate; - } - snode.add(new DefaultMutableTreeNode("SamplesRate S" - + Integer.toString((int) sr), false)); - snode.add(new DefaultMutableTreeNode(NUMBER_OF_SAMPLES - + duration.getSamples(), false)); - node.add(snode); - - snode = new DefaultMutableTreeNode(FILM_FRAMING, true); - snode.add(new DefaultMutableTreeNode(FRAMING_NOT_APPLICABLE, - false)); - snode.add(new DefaultMutableTreeNode(NTSC_FILM_FRAMING_TYPE, - false)); - node.add(snode); - parent.add(node); - } - } - - /* Function for turning the textMD metadata into a subtree. */ - private DefaultMutableTreeNode textMDToNode(TextMDMetadata textMD) { - DefaultMutableTreeNode val = new DefaultMutableTreeNode( - TEXT_MD_METADTA, true); - - DefaultMutableTreeNode u = new DefaultMutableTreeNode("Character_info", - true); - val.add(u); - - String s = textMD.getCharset(); - if (s != null) { - u.add(new DefaultMutableTreeNode("Charset: " + s, false)); - } - s = textMD.getByte_orderString(); - if (s != null) { - u.add(new DefaultMutableTreeNode("Byte_order: " + s, false)); - } - s = textMD.getByte_size(); - if (s != null) { - u.add(new DefaultMutableTreeNode("Byte_size: " + s, false)); - } - s = textMD.getCharacter_size(); - if (s != null) { - u.add(new DefaultMutableTreeNode("Character_size: " + s, false)); - } - s = textMD.getLinebreakString(); - if (s != null) { - u.add(new DefaultMutableTreeNode("Linebreak: " + s, false)); - } - s = textMD.getLanguage(); - if (s != null) { - val.add(new DefaultMutableTreeNode("Language: " + s, false)); - } - s = textMD.getMarkup_basis(); - if (s != null) { - DefaultMutableTreeNode basis = new DefaultMutableTreeNode( - "Markup_basis: " + s, true); - val.add(basis); - s = textMD.getMarkup_basis_version(); - if (s != null) { - basis.add(new DefaultMutableTreeNode(VERSION + s, false)); - } - } - s = textMD.getMarkup_language(); - if (s != null) { - DefaultMutableTreeNode language = new DefaultMutableTreeNode( - "Markup_language: " + s, true); - val.add(language); - s = textMD.getMarkup_language_version(); - if (s != null) { - language.add(new DefaultMutableTreeNode(VERSION + s, false)); - } - } - return val; - } - - /* Function for turning the Niso metadata into a subtree. */ - private DefaultMutableTreeNode nisoToNode(NisoImageMetadata niso) { - DefaultMutableTreeNode val = new DefaultMutableTreeNode( - "NisoImageMetadata", true); - String s = niso.getMimeType(); - if (s != null) { - val.add(new DefaultMutableTreeNode("MIMEType: " + s, false)); - } - s = niso.getByteOrder(); - if (s != null) { - val.add(new DefaultMutableTreeNode(BYTE_ORDER + s, false)); - } - - int n = niso.getCompressionScheme(); - if (n != NisoImageMetadata.NULL) { - val.add(new DefaultMutableTreeNode("CompressionScheme: " - + integerRepresentation(n, - NisoImageMetadata.COMPRESSION_SCHEME, - NisoImageMetadata.COMPRESSION_SCHEME_INDEX), false)); - } - if ((n = niso.getCompressionLevel()) != NisoImageMetadata.NULL) { - val.add(new DefaultMutableTreeNode("CompressionLevel: " - + Integer.toString(n), false)); - } - if ((n = niso.getColorSpace()) != NisoImageMetadata.NULL) { - val.add(new DefaultMutableTreeNode("ColorSpace: " - + integerRepresentation(n, NisoImageMetadata.COLORSPACE, - NisoImageMetadata.COLORSPACE_INDEX), false)); - } - if ((s = niso.getProfileName()) != null) { - val.add(new DefaultMutableTreeNode("ProfileName: " + s, false)); - } - if ((s = niso.getProfileURL()) != null) { - val.add(new DefaultMutableTreeNode("ProfileURL: " + s, false)); - } - int[] iarray = niso.getYCbCrSubSampling(); - if (iarray != null) { - DefaultMutableTreeNode nod = (new DefaultMutableTreeNode( - "YCbCrSubSampling")); - val.add(nod); - for (int i = 0; i < iarray.length; i++) { - nod.add(new DefaultMutableTreeNode(Integer.toString(iarray[i]), - false)); - } - } - if ((n = niso.getYCbCrPositioning()) != NisoImageMetadata.NULL) { - val.add(new DefaultMutableTreeNode("YCbCrPositioning: " - + integerRepresentation(n, - NisoImageMetadata.YCBCR_POSITIONING), false)); - } - Rational[] rarray = niso.getYCbCrCoefficients(); - if (rarray != null) { - DefaultMutableTreeNode nod = (new DefaultMutableTreeNode( - "YCbCrCoefficients", true)); - val.add(nod); - for (int i = 0; i < rarray.length; i++) { - nod.add(new DefaultMutableTreeNode(rarray[i].toString(), false)); - } - } - rarray = niso.getReferenceBlackWhite(); - if (rarray != null) { - DefaultMutableTreeNode nod = (new DefaultMutableTreeNode( - "ReferenceBlackWhite", true)); - val.add(nod); - for (int i = 0; i < rarray.length; i++) { - nod.add(new DefaultMutableTreeNode(rarray[i].toString(), false)); - } - } - if ((n = niso.getSegmentType()) != NisoImageMetadata.NULL) { - val.add(new DefaultMutableTreeNode("YSegmentType: " - + integerRepresentation(n, NisoImageMetadata.SEGMENT_TYPE), - false)); - } - long[] larray = niso.getStripOffsets(); - if (larray != null) { - DefaultMutableTreeNode nod = (new DefaultMutableTreeNode( - "StripOffsets", true)); - val.add(nod); - for (int i = 0; i < larray.length; i++) { - nod.add(new DefaultMutableTreeNode(Long.toString(larray[i]), - false)); - } - } - long ln = niso.getRowsPerStrip(); - if (ln != NisoImageMetadata.NULL) { - val.add(new DefaultMutableTreeNode("RowsPerStrip: " - + Long.toString(ln), false)); - } - if ((larray = niso.getStripByteCounts()) != null) { - DefaultMutableTreeNode nod = (new DefaultMutableTreeNode( - "StripByteCounts", true)); - val.add(nod); - for (int i = 0; i < larray.length; i++) { - nod.add(new DefaultMutableTreeNode(Long.toString(larray[i]), - false)); - } - } - if ((ln = niso.getTileWidth()) != NisoImageMetadata.NULL) { - val.add(new DefaultMutableTreeNode("TileWidth: " - + Long.toString(ln))); - } - if ((ln = niso.getTileLength()) != NisoImageMetadata.NULL) { - val.add(new DefaultMutableTreeNode("TileLength: " - + Long.toString(ln))); - } - if ((larray = niso.getTileOffsets()) != null) { - DefaultMutableTreeNode nod = (new DefaultMutableTreeNode( - "TileOffsets", true)); - val.add(nod); - for (int i = 0; i < larray.length; i++) { - nod.add(new DefaultMutableTreeNode(Long.toString(larray[i]), - false)); - } - } - if ((larray = niso.getTileByteCounts()) != null) { - DefaultMutableTreeNode nod = (new DefaultMutableTreeNode( - "TileByteCounts", true)); - val.add(nod); - for (int i = 0; i < larray.length; i++) { - nod.add(new DefaultMutableTreeNode(Long.toString(larray[i]), - false)); - } - } - if ((n = niso.getPlanarConfiguration()) != NisoImageMetadata.NULL) { - val.add(new DefaultMutableTreeNode("PlanarConfiguration: " - + integerRepresentation(n, - NisoImageMetadata.PLANAR_CONFIGURATION), false)); - } - if ((s = niso.getImageIdentifier()) != null) { - val.add(new DefaultMutableTreeNode("ImageIdentifier: " + s, false)); - } - if ((s = niso.getImageIdentifierLocation()) != null) { - val.add(new DefaultMutableTreeNode("ImageIdentifierLocation: " + s, - false)); - } - if ((ln = niso.getFileSize()) != NisoImageMetadata.NULL) { - val.add(new DefaultMutableTreeNode( - "FileSize: " + Long.toString(ln), false)); - } - if ((n = niso.getChecksumMethod()) != NisoImageMetadata.NULL) { - val.add(new DefaultMutableTreeNode("ChecksumMethod: " - + integerRepresentation(n, - NisoImageMetadata.CHECKSUM_METHOD), false)); - } - if ((s = niso.getChecksumValue()) != null) { - val.add(new DefaultMutableTreeNode("ChecksumValue: " + s, false)); - } - if ((n = niso.getOrientation()) != NisoImageMetadata.NULL) { - val.add(new DefaultMutableTreeNode("Orientation: " - + integerRepresentation(n, NisoImageMetadata.ORIENTATION), - false)); - } - if ((n = niso.getDisplayOrientation()) != NisoImageMetadata.NULL) { - val.add(new DefaultMutableTreeNode("DisplayOrientation: " - + integerRepresentation(n, - NisoImageMetadata.DISPLAY_ORIENTATION), false)); - } - if ((ln = niso.getXTargetedDisplayAR()) != NisoImageMetadata.NULL) { - val.add(new DefaultMutableTreeNode("XTargetedDisplayAR: " - + Long.toString(ln), false)); - } - if ((ln = niso.getYTargetedDisplayAR()) != NisoImageMetadata.NULL) { - val.add(new DefaultMutableTreeNode("YTargetedDisplayAR: " - + Long.toString(ln), false)); - } - if ((s = niso.getPreferredPresentation()) != null) { - val.add(new DefaultMutableTreeNode("PreferredPresentation: " + s, - false)); - } - if ((s = niso.getSourceType()) != null) { - val.add(new DefaultMutableTreeNode("SourceType: " + s, false)); - } - if ((s = niso.getImageProducer()) != null) { - val.add(new DefaultMutableTreeNode("ImageProducer: " + s, false)); - } - if ((s = niso.getHostComputer()) != null) { - val.add(new DefaultMutableTreeNode("HostComputer: " + s, false)); - } - if ((s = niso.getOS()) != null) { - val.add(new DefaultMutableTreeNode("OperatingSystem: " + s, false)); - } - if ((s = niso.getOSVersion()) != null) { - val.add(new DefaultMutableTreeNode("OSVersion: " + s, false)); - } - if ((s = niso.getDeviceSource()) != null) { - val.add(new DefaultMutableTreeNode("DeviceSource: " + s, false)); - } - if ((s = niso.getScannerManufacturer()) != null) { - val.add(new DefaultMutableTreeNode("ScannerManufacturer: " + s, - false)); - } - if ((s = niso.getScannerModelName()) != null) { - val.add(new DefaultMutableTreeNode("ScannerModelName: " + s, false)); - } - if ((s = niso.getScannerModelNumber()) != null) { - val.add(new DefaultMutableTreeNode("ScannerModelNumber: " + s, - false)); - } - if ((s = niso.getScannerModelSerialNo()) != null) { - val.add(new DefaultMutableTreeNode("ScannerModelSerialNo: " + s, - false)); - } - if ((s = niso.getScanningSoftware()) != null) { - val.add(new DefaultMutableTreeNode("ScanningSoftware: " + s, false)); - } - if ((s = niso.getScanningSoftwareVersionNo()) != null) { - val.add(new DefaultMutableTreeNode("ScanningSoftwareVersionNo: " - + s, false)); - } - double d = niso.getPixelSize(); - if (d != NisoImageMetadata.NILL) { - val.add(new DefaultMutableTreeNode("PixelSize: " - + Double.toString(d), false)); - } - if ((d = niso.getXPhysScanResolution()) != NisoImageMetadata.NILL) { - val.add(new DefaultMutableTreeNode("XPhysScanResolution: " - + Double.toString(d), false)); - } - if ((d = niso.getYPhysScanResolution()) != NisoImageMetadata.NILL) { - val.add(new DefaultMutableTreeNode("YPhysScanResolution: " - + Double.toString(d), false)); - } - - if ((s = niso.getDigitalCameraManufacturer()) != null) { - val.add(new DefaultMutableTreeNode("DigitalCameraManufacturer: " - + s, false)); - } - if ((s = niso.getDigitalCameraModelName()) != null) { - val.add(new DefaultMutableTreeNode("DigitalCameraModelName: " + s, - false)); - } - if ((s = niso.getDigitalCameraModelNumber()) != null) { - val.add(new DefaultMutableTreeNode( - "DigitalCameraModelNumber: " + s, false)); - } - if ((s = niso.getDigitalCameraModelSerialNo()) != null) { - val.add(new DefaultMutableTreeNode("DigitalCameraModelSerialNo: " - + s, false)); - } - if ((d = niso.getFNumber()) != NisoImageMetadata.NILL) { - val.add(new DefaultMutableTreeNode( - "FNumber: " + Double.toString(d), false)); - } - if ((d = niso.getExposureTime()) != NisoImageMetadata.NILL) { - val.add(new DefaultMutableTreeNode("ExposureTime: " - + Double.toString(d), false)); - } - if ((n = niso.getExposureProgram()) != NisoImageMetadata.NULL) { - val.add(new DefaultMutableTreeNode("ExposureProgram: " - + Integer.toString(n), false)); - } - if ((s = niso.getExifVersion()) != null) { - val.add(new DefaultMutableTreeNode("ExifVersion: " + s, false)); - } - Rational r; - if ((r = niso.getBrightness()) != null) { - val.add(new DefaultMutableTreeNode("Brightness: " + r.toString(), - false)); - } - if ((r = niso.getExposureBias()) != null) { - val.add(new DefaultMutableTreeNode("ExposureBias: " + r.toString(), - false)); - } - - double[] darray = niso.getSubjectDistance(); - if (darray != null) { - DefaultMutableTreeNode nod = new DefaultMutableTreeNode( - "SubjectDistance", true); - val.add(nod); - for (int i = 0; i < darray.length; i++) { - nod.add(new DefaultMutableTreeNode(Double.toString(darray[i]), - false)); - } - } - if ((n = niso.getMeteringMode()) != NisoImageMetadata.NULL) { - val.add(new DefaultMutableTreeNode("MeteringMode: " - + Integer.toString(n), false)); - } - if ((n = niso.getSceneIlluminant()) != NisoImageMetadata.NULL) { - val.add(new DefaultMutableTreeNode("SceneIlluminant: " - + Integer.toString(n), false)); - } - if ((d = niso.getColorTemp()) != NisoImageMetadata.NILL) { - val.add(new DefaultMutableTreeNode("ColorTemp: " - + Double.toString(d), false)); - } - if ((d = niso.getFocalLength()) != NisoImageMetadata.NILL) { - val.add(new DefaultMutableTreeNode("FocalLength: " - + Double.toString(d), false)); - } - if ((n = niso.getFlash()) != NisoImageMetadata.NULL) { - // First bit (0 = Flash did not fire, 1 = Flash fired) - val.add(new DefaultMutableTreeNode("Flash: " - + integerRepresentation(n & 1, NisoImageMetadata.FLASH_20), - false)); - } - if ((r = niso.getFlashEnergy()) != null) { - val.add(new DefaultMutableTreeNode("FlashEnergy: " + r.toString(), - false)); - } - if ((n = niso.getFlashReturn()) != NisoImageMetadata.NULL) { - val.add(new DefaultMutableTreeNode("FlashReturn: " - + integerRepresentation(n, NisoImageMetadata.FLASH_RETURN), - false)); - } - if ((n = niso.getBackLight()) != NisoImageMetadata.NULL) { - val.add(new DefaultMutableTreeNode("BackLight: " - + integerRepresentation(n, NisoImageMetadata.BACKLIGHT), - false)); - } - if ((d = niso.getExposureIndex()) != NisoImageMetadata.NILL) { - val.add(new DefaultMutableTreeNode("ExposureIndex: " - + Double.toString(d), false)); - } - if ((n = niso.getAutoFocus()) != NisoImageMetadata.NULL) { - val.add(new DefaultMutableTreeNode("AutoFocus: " - + Integer.toString(n), false)); - // NisoImageMetadata.AUTOFOCUS - } - if ((d = niso.getXPrintAspectRatio()) != NisoImageMetadata.NILL) { - val.add(new DefaultMutableTreeNode("XPrintAspectRatio: " - + Double.toString(d), false)); - } - if ((d = niso.getYPrintAspectRatio()) != NisoImageMetadata.NILL) { - val.add(new DefaultMutableTreeNode("YPrintAspectRatio: " - + Double.toString(d), false)); - } - - if ((n = niso.getSensor()) != NisoImageMetadata.NULL) { - val.add(new DefaultMutableTreeNode("Sensor: " - + integerRepresentation(n, NisoImageMetadata.SENSOR), false)); - } - if ((s = niso.getDateTimeCreated()) != null) { - val.add(new DefaultMutableTreeNode("DateTimeCreated: " + s, false)); - } - if ((s = niso.getMethodology()) != null) { - val.add(new DefaultMutableTreeNode("Methodology: " + s, false)); - } - if ((n = niso.getSamplingFrequencyPlane()) != NisoImageMetadata.NULL) { - val.add(new DefaultMutableTreeNode("SamplingFrequencyPlane: " - + integerRepresentation(n, - NisoImageMetadata.SAMPLING_FREQUENCY_PLANE), false)); - } - if ((n = niso.getSamplingFrequencyUnit()) != NisoImageMetadata.NULL) { - val.add(new DefaultMutableTreeNode("SamplingFrequencyUnit: " - + integerRepresentation(n, - NisoImageMetadata.SAMPLING_FREQUENCY_UNIT), false)); - } - Rational rat = niso.getXSamplingFrequency(); - if (rat != null) { - val.add(new DefaultMutableTreeNode("XSamplingFrequency: " - + rat.toString(), false)); - } - rat = niso.getYSamplingFrequency(); - if (rat != null) { - val.add(new DefaultMutableTreeNode("YSamplingFrequency: " - + rat.toString(), false)); - } - if ((ln = niso.getImageWidth()) != NisoImageMetadata.NULL) { - val.add(new DefaultMutableTreeNode("ImageWidth: " - + Long.toString(ln), false)); - } - if ((ln = niso.getImageLength()) != NisoImageMetadata.NULL) { - val.add(new DefaultMutableTreeNode("ImageLength: " - + Long.toString(ln), false)); - } - if ((d = niso.getSourceXDimension()) != NisoImageMetadata.NILL) { - val.add(new DefaultMutableTreeNode("SourceXDimension: " - + Double.toString(d), false)); - } - if ((n = niso.getSourceXDimensionUnit()) != NisoImageMetadata.NULL) { - val.add(new DefaultMutableTreeNode("SourceXDimensionUnit: " - + integerRepresentation(n, - NisoImageMetadata.SOURCE_DIMENSION_UNIT), false)); - } - if ((d = niso.getSourceYDimension()) != NisoImageMetadata.NILL) { - val.add(new DefaultMutableTreeNode("SourceYDimension: " - + Double.toString(d), false)); - } - if ((n = niso.getSourceYDimensionUnit()) != NisoImageMetadata.NULL) { - val.add(new DefaultMutableTreeNode("SourceYDimensionUnit: " - + integerRepresentation(n, - NisoImageMetadata.SOURCE_DIMENSION_UNIT), false)); - } - if ((iarray = niso.getBitsPerSample()) != null) { - DefaultMutableTreeNode nod = (new DefaultMutableTreeNode( - "BitsPerSample")); - val.add(nod); - for (int i = 0; i < iarray.length; i++) { - nod.add(new DefaultMutableTreeNode(Integer.toString(iarray[i]), - false)); - } - } - if ((n = niso.getSamplesPerPixel()) != NisoImageMetadata.NULL) { - val.add(new DefaultMutableTreeNode("SamplesPerPixel: " - + Integer.toString(n), false)); - } - if ((iarray = niso.getExtraSamples()) != null) { - DefaultMutableTreeNode nod = (new DefaultMutableTreeNode( - "ExtraSamples")); - val.add(nod); - for (int i = 0; i < iarray.length; i++) { - nod.add(new DefaultMutableTreeNode(integerRepresentation( - iarray[i], NisoImageMetadata.EXTRA_SAMPLES), false)); - } - } - if ((s = niso.getColormapReference()) != null) { - val.add(new DefaultMutableTreeNode("ColormapReference: " + s)); - } - if ((iarray = niso.getColormapBitCodeValue()) != null) { - DefaultMutableTreeNode nod = (new DefaultMutableTreeNode( - "ColormapBitCodeValue")); - val.add(nod); - for (int i = 0; i < iarray.length; i++) { - nod.add(new DefaultMutableTreeNode(Integer.toString(iarray[i]), - false)); - } - } - if ((iarray = niso.getColormapRedValue()) != null) { - DefaultMutableTreeNode nod = (new DefaultMutableTreeNode( - "ColormapRedValue")); - val.add(nod); - for (int i = 0; i < iarray.length; i++) { - nod.add(new DefaultMutableTreeNode(Integer.toString(iarray[i]), - false)); - } - } - if ((iarray = niso.getColormapGreenValue()) != null) { - DefaultMutableTreeNode nod = (new DefaultMutableTreeNode( - "ColormapGreenValue")); - val.add(nod); - for (int i = 0; i < iarray.length; i++) { - nod.add(new DefaultMutableTreeNode(Integer.toString(iarray[i]), - false)); - } - } - if ((iarray = niso.getColormapBlueValue()) != null) { - DefaultMutableTreeNode nod = (new DefaultMutableTreeNode( - "ColormapBlueValue")); - val.add(nod); - for (int i = 0; i < iarray.length; i++) { - nod.add(new DefaultMutableTreeNode(Integer.toString(iarray[i]), - false)); - } - } - if ((iarray = niso.getGrayResponseCurve()) != null) { - DefaultMutableTreeNode nod = (new DefaultMutableTreeNode( - "GrayResponseCurve")); - val.add(nod); - for (int i = 0; i < iarray.length; i++) { - nod.add(new DefaultMutableTreeNode(Integer.toString(iarray[i]), - false)); - } - } - if ((n = niso.getGrayResponseUnit()) != NisoImageMetadata.NULL) { - val.add(new DefaultMutableTreeNode("GrayResponseUnit: " - + Integer.toString(n), false)); - } - r = niso.getWhitePointXValue(); - if (r != null) { - val.add(new DefaultMutableTreeNode("WhitePointXValue: " - + r.toString(), false)); - } - if ((r = niso.getWhitePointYValue()) != null) { - val.add(new DefaultMutableTreeNode("WhitePointYValue: " - + r.toString(), false)); - } - if ((r = niso.getPrimaryChromaticitiesRedX()) != null) { - val.add(new DefaultMutableTreeNode("PrimaryChromaticitiesRedX: " - + r.toString(), false)); - } - if ((r = niso.getPrimaryChromaticitiesRedY()) != null) { - val.add(new DefaultMutableTreeNode("PrimaryChromaticitiesRedY: " - + r.toString(), false)); - } - if ((r = niso.getPrimaryChromaticitiesGreenX()) != null) { - val.add(new DefaultMutableTreeNode("PrimaryChromaticitiesGreenX: " - + r.toString(), false)); - } - if ((r = niso.getPrimaryChromaticitiesGreenY()) != null) { - val.add(new DefaultMutableTreeNode("PrimaryChromaticitiesGreenY: " - + r.toString(), false)); - } - if ((r = niso.getPrimaryChromaticitiesBlueX()) != null) { - val.add(new DefaultMutableTreeNode("PrimaryChromaticitiesBlueX: " - + r.toString())); - } - if ((r = niso.getPrimaryChromaticitiesBlueY()) != null) { - val.add(new DefaultMutableTreeNode("PrimaryChromaticitiesBlueY: " - + r.toString())); - } - if ((n = niso.getTargetType()) != NisoImageMetadata.NULL) { - val.add(new DefaultMutableTreeNode("TargetType: " - + integerRepresentation(n, NisoImageMetadata.TARGET_TYPE), - false)); - } - if ((s = niso.getTargetIDManufacturer()) != null) { - val.add(new DefaultMutableTreeNode("TargetIDManufacturer: " + s, - false)); - } - if ((s = niso.getTargetIDName()) != null) { - val.add(new DefaultMutableTreeNode("TargetIDName: " + s, false)); - } - if ((s = niso.getTargetIDNo()) != null) { - val.add(new DefaultMutableTreeNode("TargetIDNo: " + s, false)); - } - if ((s = niso.getTargetIDMedia()) != null) { - val.add(new DefaultMutableTreeNode("TargetIDMedia: " + s, false)); - } - if ((s = niso.getImageData()) != null) { - val.add(new DefaultMutableTreeNode("ImageData: " + s, false)); - } - if ((s = niso.getPerformanceData()) != null) { - val.add(new DefaultMutableTreeNode("PerformanceData: " + s, false)); - } - if ((s = niso.getProfiles()) != null) { - val.add(new DefaultMutableTreeNode("Profiles: " + s, false)); - } - if ((s = niso.getDateTimeProcessed()) != null) { - val.add(new DefaultMutableTreeNode("DateTimeProcessed: " + s, false)); - } - if ((s = niso.getSourceData()) != null) { - val.add(new DefaultMutableTreeNode("SourceData: " + s, false)); - } - if ((s = niso.getProcessingAgency()) != null) { - val.add(new DefaultMutableTreeNode("ProcessingAgency: " + s, false)); - } - if ((s = niso.getProcessingSoftwareName()) != null) { - val.add(new DefaultMutableTreeNode("ProcessingSoftwareName: " + s, - false)); - } - if ((s = niso.getProcessingSoftwareVersion()) != null) { - val.add(new DefaultMutableTreeNode("ProcessingSoftwareVersion: " - + s, false)); - } - String[] sarray = niso.getProcessingActions(); - if (sarray != null) { - DefaultMutableTreeNode nod = new DefaultMutableTreeNode( - "ProcessingActions", true); - val.add(nod); - for (int i = 1; i < sarray.length; i++) { - nod.add(new DefaultMutableTreeNode(sarray[i], false)); - } - } - return val; - } - - /* - * Return the string equivalent of an integer if raw output isn't selected, - * or its literal representation if it is. - */ - private String integerRepresentation(int n, String[] labels) { - if (_rawOutput) { - return Integer.toString(n); - } - try { - return labels[n]; - } catch (Exception e) { - return Integer.toString(n); - } - } - - private String integerRepresentation(int n, String[] labels, int[] index) { - if (_rawOutput) { - return Integer.toString(n); - } - try { - int idx = -1; - for (int i = 0; i < index.length; i++) { - if (n == index[i]) { - idx = i; - break; - } - } - if (idx > -1) { - return labels[idx]; - } - } catch (Exception e) { - } - // If we don't get a match, or do get an exception - return Integer.toString(n); - } - - /** - * This method returns a member of a property list based on it's - * PropertyType - * - * @param ptyp - * the propertytype - * @param item - * the item of the list - * @param allowsChildren - * @return - */ - private DefaultMutableTreeNode getDefaultMutableTreeNode(PropertyType ptyp, - Object item, boolean allowsChildren) { - - DefaultMutableTreeNode itemNode; - - if (null == ptyp) { - // Simple objects just need a leaf. - itemNode = (new DefaultMutableTreeNode(item, allowsChildren)); - } else - // Object item = iter.next (); - switch (ptyp) { - case PROPERTY: - itemNode = (propToNode((Property) item)); - break; - case NISOIMAGEMETADATA: - itemNode = (nisoToNode((NisoImageMetadata) item)); - break; - default: - // Simple objects just need a leaf. - itemNode = (new DefaultMutableTreeNode(item, allowsChildren)); - break; - } - - return itemNode; - } - - /** - * Method to add an array to a node - * - * @param - * generic method, can be used for arrays of different types - * @param node - * the node to add the elements of the array - * @param array - * the array to be added to the node - */ - private void addToNode(DefaultMutableTreeNode node, E[] array) { - for (E element : array) { - if (element instanceof Property) { - node.add(propToNode((Property) element)); - } else { - node.add(new DefaultMutableTreeNode(element)); - } - } - } + /** Serialisation identifier */ + private static final long serialVersionUID = -4409152022584715925L; + + private RepInfo _info; + private JhoveBase _base; + private boolean _rawOutput; + private DateFormat _dateFmt; + + /* Sample rate. */ + private double _sampleRate; + + /** + * Constructor. + * + * @param info The RepInfo object whose contents are to be displayed. + * @param base The JHOVE base on which we're operating. + */ + public RepTreeRoot(RepInfo info, JhoveBase base) { + super(info.getUri()); + _info = info; + _base = base; + _rawOutput = _base.getShowRawFlag(); + + // Set the DateFormat for displaying the module date. + _dateFmt = DateFormat.getDateInstance(); + + // Snarf everything up into the tree. + + snarfRepInfo(); + } + + private static final String TEXT_MD_METADTA = "TextMDMetadata"; + private static final String FORMAT = "Format: "; + private static final String VERSION = "Version: "; + private static final String BYTE_ORDER = "ByteOrder: "; + private static final String FRAME_COUNT_30 = "FrameCount: 30"; + private static final String TIME_BASE_1000 = "TimeBase: 1000"; + private static final String VIDEO_FIELD_FIELD_1 = "VideoField: FIELD_1"; + private static final String COUNTING_MODE_NTSC_NON_DROP_FRAME = + "CountingMode: NTSC_NON_DROP_FRAME"; + private static final String HOURS = "Hours: "; + private static final String MINUTES = "Minutes: "; + private static final String SECONDS = "Seconds: "; + private static final String FRAMES = "Frames: "; + private static final String SAMPLES = "Samples"; + private static final String NUMBER_OF_SAMPLES = "NumberOfSamples: "; + private static final String FILM_FRAMING = "FilmFraming"; + private static final String FRAMING_NOT_APPLICABLE = "Framing: NOT_APPLICABLE"; + private static final String NTSC_FILM_FRAMING_TYPE = "Type: ntscFilmFramingType"; + + /** Constructs a DefaultMutableTreeNode representing a property */ + private DefaultMutableTreeNode propToNode(Property pProp) { + PropertyArity arity = pProp.getArity(); + PropertyType typ = pProp.getType(); + Object pValue = pProp.getValue(); + if (arity == PropertyArity.SCALAR) { + if (null == typ) { + // Simple types: just use name plus string value. + DefaultMutableTreeNode val = + new DefaultMutableTreeNode(pProp.getName() + ": " + pValue.toString()); + return val; + } else { + TextMDMetadata tData; + DefaultMutableTreeNode val; + switch (typ) { + case NISOIMAGEMETADATA: + // NISO Image metadata is a world of its own. + NisoImageMetadata nData = (NisoImageMetadata) pValue; + return nisoToNode(nData); + case AESAUDIOMETADATA: + // AES audio metadata is another world. + AESAudioMetadata aData = (AESAudioMetadata) pValue; + return aesToNode(aData); + case TEXTMDMETADATA: + // textMD metadata is another world. + tData = (TextMDMetadata) pValue; + return textMDToNode(tData); + case PROPERTY: + if (TEXT_MD_METADTA.equals(pProp.getName())) { + tData = (TextMDMetadata) pValue; + return textMDToNode(tData); + } + // A scalar property of type Property -- seems + // pointless, but we handle it. + val = new DefaultMutableTreeNode(pProp.getName()); + val.add(propToNode((Property) pValue)); + return val; + + default: + + // Simple types: just use name plus string value. + val = new DefaultMutableTreeNode(pProp.getName() + ": " + pValue.toString()); + return val; + } + } + } + // Compound properties. The text of the node is the + // property name. + DefaultMutableTreeNode val = new DefaultMutableTreeNode(pProp.getName()); + if (null != arity) + switch (arity) { + case ARRAY: + addArrayMembers(val, pProp); + break; + case LIST: + addListMembers(val, pProp); + break; + case MAP: + addMapMembers(val, pProp); + break; + case SET: + addSetMembers(val, pProp); + break; + default: + break; + } + return val; + } + + /** Find the index of an object in its parent. Understands the Jhove property structure. */ + public int getIndexOfChild(Object parent, Object child) { + Property pProp = (Property) parent; + PropertyArity arity = pProp.getArity(); + // For Lists, Maps, and Sets we construct an Iterator. + Iterator iter = null; + if (arity == PropertyArity.SET || arity == PropertyArity.LIST || arity == PropertyArity.MAP) { + if (null == arity) { + List list = (List) pProp.getValue(); + iter = list.iterator(); + } else + switch (arity) { + case SET: + Set set = (Set) pProp.getValue(); + iter = set.iterator(); + break; + case MAP: + Map map = (Map) pProp.getValue(); + iter = map.values().iterator(); + break; + default: + List list = (List) pProp.getValue(); + iter = list.iterator(); + break; + } + for (int i = 0; ; i++) { + if (!iter.hasNext()) { + return 0; // Should never happen + } else if (iter.next() == child) { + return i; + } + } + } + // OK, that was the easy one. Now for that damn array arity. + // In the case of non-object types, we can't actually tell which + // position matches the object, so we return 0 and hope it doesn't + // mess things up too much. + PropertyType propType = pProp.getType(); + java.util.Date[] dateArray = null; + Property[] propArray = null; + Rational[] rationalArray = null; + Object[] objArray = null; + int n = 0; + + if (null == propType) { + return 0; // non-object array type + } else + // if (child instanceof LeafHolder) { + // return ((LeafHolder) child).getPosition (); + // } + // else + switch (propType) { + case DATE: + dateArray = (java.util.Date[]) pProp.getValue(); + n = dateArray.length; + break; + case OBJECT: + objArray = (Object[]) pProp.getValue(); + n = objArray.length; + break; + case RATIONAL: + rationalArray = (Rational[]) pProp.getValue(); + n = rationalArray.length; + break; + case PROPERTY: + propArray = (Property[]) pProp.getValue(); + n = propArray.length; + break; + default: + return 0; // non-object array type + } + + for (int i = 0; i < n; i++) { + Object elem = null; + switch (propType) { + case DATE: + elem = dateArray[i]; + break; + case OBJECT: + elem = objArray[i]; + break; + case RATIONAL: + elem = rationalArray[i]; + break; + case PROPERTY: + elem = propArray[i]; + break; + default: + break; + } + if (elem == child) { + return i; + } + } + return 0; // somehow fell through + } + + private void snarfRepInfo() { + // This node has two children, for the module and the RepInfo + + Module module = _info.getModule(); + if (module != null) { + // Create a subnode for the module, which has three + // leaf children. + DefaultMutableTreeNode moduleNode = new DefaultMutableTreeNode("Module"); + moduleNode.add(new DefaultMutableTreeNode(module.getName(), false)); + moduleNode.add(new DefaultMutableTreeNode("Release: " + module.getRelease(), false)); + moduleNode.add( + new DefaultMutableTreeNode("Date: " + _dateFmt.format(module.getDate()), false)); + add(moduleNode); + } + + DefaultMutableTreeNode infoNode = new DefaultMutableTreeNode("RepInfo"); + infoNode.add(new DefaultMutableTreeNode("URI: " + _info.getUri(), false)); + Date dt = _info.getCreated(); + if (dt != null) { + infoNode.add(new DefaultMutableTreeNode("Created: " + dt.toString(), false)); + } + dt = _info.getLastModified(); + if (dt != null) { + infoNode.add(new DefaultMutableTreeNode("LastModified: " + dt.toString(), false)); + } + long sz = _info.getSize(); + if (sz != -1) { + infoNode.add(new DefaultMutableTreeNode("Size: " + Long.toString(sz), false)); + } + String s = _info.getFormat(); + if (s != null) { + infoNode.add(new DefaultMutableTreeNode(FORMAT + s, false)); + } + s = _info.getVersion(); + if (s != null) { + infoNode.add(new DefaultMutableTreeNode(VERSION + s, false)); + } + String wfStr; + switch (_info.getWellFormed()) { + case RepInfo.TRUE: + wfStr = "Well-Formed"; + break; + case RepInfo.FALSE: + wfStr = "Not well-formed"; + break; + default: + wfStr = "Unknown"; + break; + } + if (_info.getWellFormed() == RepInfo.TRUE) { + switch (_info.getValid()) { + case RepInfo.TRUE: + wfStr += " and valid"; + break; + + case RepInfo.FALSE: + wfStr += ", but not valid"; + break; + + // case UNDETERMINED: add nothing + } + } + infoNode.add(new DefaultMutableTreeNode("Status: " + wfStr, false)); + + // Report modules that said their signatures match + List sigList = _info.getSigMatch(); + if (sigList != null && sigList.size() > 0) { + DefaultMutableTreeNode sigNode = new DefaultMutableTreeNode("SignatureMatches"); + infoNode.add(sigNode); + for (int i = 0; i < sigList.size(); i++) { + DefaultMutableTreeNode sNode = new DefaultMutableTreeNode(sigList.get(i)); + sigNode.add(sNode); + } + } + // Compile a list of messages and offsets into a subtree + List messageList = _info.getMessage(); + if (messageList != null && messageList.size() > 0) { + DefaultMutableTreeNode msgNode = new DefaultMutableTreeNode("Messages"); + infoNode.add(msgNode); + int i; + for (i = 0; i < messageList.size(); i++) { + Message msg = messageList.get(i); + String prefix; + if (msg instanceof InfoMessage) { + prefix = "InfoMessage: "; + } else if (msg instanceof ErrorMessage) { + prefix = "ErrorMessage: "; + } else { + prefix = "Message: "; + } + DefaultMutableTreeNode mNode = new DefaultMutableTreeNode(prefix + msg.getMessage()); + + if (msg.getId() != null && !msg.getId().isEmpty()) { + mNode.add(new DefaultMutableTreeNode("ID: " + msg.getId())); + } + + String subMessage = msg.getSubMessage(); + if (subMessage != null) { + mNode.add(new DefaultMutableTreeNode("SubMessage: " + subMessage)); + } + long offset = -1; + if (msg instanceof ErrorMessage) { + offset = ((ErrorMessage) msg).getOffset(); + } + // + // If the offset is positive, we give the message node + // a child with the offset value. + if (offset >= 0) { + mNode.add(new DefaultMutableTreeNode("Offset: " + Long.toString(offset))); + } + msgNode.add(mNode); + } + } + + s = _info.getMimeType(); + if (s != null) { + infoNode.add(new DefaultMutableTreeNode("MimeType: " + s, false)); + } + + // Compile a list of profile strings into a string list + List profileList = _info.getProfile(); + if (profileList != null && profileList.size() > 0) { + DefaultMutableTreeNode profNode = new DefaultMutableTreeNode("Profiles"); + infoNode.add(profNode); + int i; + for (i = 0; i < profileList.size(); i++) { + profNode.add(new DefaultMutableTreeNode(profileList.get(i), false)); + } + } + + // Here we come to the property map. We have to walk + // through all the properties recursively, turning + // each into a leaf or subtree. + Map map = _info.getProperty(); + if (map != null) { + Iterator iter = map.keySet().iterator(); + while (iter.hasNext()) { + String key = iter.next(); + Property property = _info.getProperty(key); + infoNode.add(propToNode(property)); + } + } + + List cksumList = _info.getChecksum(); + if (cksumList != null && !cksumList.isEmpty()) { + DefaultMutableTreeNode ckNode = new DefaultMutableTreeNode("Checksums"); + infoNode.add(ckNode); + // List cPropList = new LinkedList (); + for (Checksum cksum : cksumList) { + DefaultMutableTreeNode csNode = new DefaultMutableTreeNode("Checksum"); + ckNode.add(csNode); + csNode.add(new DefaultMutableTreeNode("Type:" + cksum.getType().toString(), false)); + csNode.add(new DefaultMutableTreeNode("Checksum: " + cksum.getValue(), false)); + } + } + + s = _info.getNote(); + if (s != null) { + infoNode.add(new DefaultMutableTreeNode("Note: " + s, false)); + } + add(infoNode); + } + + /* + * Add the members of an array property to a node. The property must be of + * arity ARRAY. + */ + private void addArrayMembers(DefaultMutableTreeNode node, Property p) { + Object pVal = p.getValue(); + PropertyType typ = p.getType(); + if (null != typ) + switch (typ) { + case INTEGER: + { + if (pVal instanceof int[]) { + Integer[] vals = + Arrays.stream((int[]) pVal) // IntStream + .boxed() // Stream + .toArray(Integer[]::new); + addToNode(node, vals); + } else { + addToNode(node, (Integer[]) pVal); + } + break; + } + case LONG: + { + if (pVal instanceof long[]) { + Long[] vals = + Arrays.stream((long[]) pVal) // IntStream + .boxed() // Stream + .toArray(Long[]::new); + addToNode(node, vals); + } else { + addToNode(node, (Long[]) pVal); + } + break; + } + case BOOLEAN: + { + addToNode(node, (Boolean[]) pVal); + break; + } + case CHARACTER: + { + addToNode(node, (Character[]) pVal); + break; + } + case DOUBLE: + { + addToNode(node, (Double[]) pVal); + break; + } + case FLOAT: + { + addToNode(node, (Float[]) pVal); + break; + } + case SHORT: + { + addToNode(node, (Short[]) pVal); + break; + } + case BYTE: + { + addToNode(node, (Byte[]) pVal); + break; + } + case STRING: + { + addToNode(node, (String[]) pVal); + break; + } + case RATIONAL: + { + addToNode(node, (Rational[]) pVal); + break; + } + case PROPERTY: + { + addToNode(node, (Property[]) pVal); + break; + } + case NISOIMAGEMETADATA: + { + addToNode(node, (NisoImageMetadata[]) pVal); + break; + } + case OBJECT: + { + addToNode(node, (Object[]) pVal); + break; + } + default: + break; + } + } + + /* + * Add the members of a list property to a node. The property must be of + * arity LIST. + */ + private void addListMembers(DefaultMutableTreeNode node, Property p) { + List l = (List) p.getValue(); + PropertyType ptyp = p.getType(); + boolean canHaveChildren = Boolean.FALSE; + l.forEach(item -> node.add(getDefaultMutableTreeNode(ptyp, item, canHaveChildren))); + } + + /* + * Add the members of a set property to a node. The property must be of + * arity SET. + */ + private void addSetMembers(DefaultMutableTreeNode node, Property p) { + Set s = (Set) p.getValue(); + PropertyType ptyp = p.getType(); + boolean canHaveChildren = Boolean.FALSE; + Iterator iter = s.iterator(); + while (iter.hasNext()) { + Object item = iter.next(); + node.add(getDefaultMutableTreeNode(ptyp, item, canHaveChildren)); + } + } + + /* + * Add the members of a map property to a node. The property must be of + * arity MAP. + */ + private void addMapMembers(DefaultMutableTreeNode node, Property p) { + Map m = (Map) p.getValue(); + PropertyType ptyp = p.getType(); + Boolean canHaveChildren = Boolean.TRUE; + // Iterator iter = m.values ().iterator (); + Iterator iter = m.keySet().iterator(); + while (iter.hasNext()) { + DefaultMutableTreeNode itemNode; + String key = (String) iter.next(); + Object item = m.get(key); + itemNode = getDefaultMutableTreeNode(ptyp, item, canHaveChildren); + node.add(itemNode); + + // Add a subnode for the key + itemNode.setAllowsChildren(true); + itemNode.add(new DefaultMutableTreeNode("Key: " + key, false)); + } + } + + /* Function for turning the AES metadata into a subtree. */ + private DefaultMutableTreeNode aesToNode(AESAudioMetadata aes) { + _sampleRate = aes.getSampleRate(); + + DefaultMutableTreeNode val = new DefaultMutableTreeNode("AESAudioMetadata", true); + String s = aes.getAnalogDigitalFlag(); + if (s != null) { + val.add(new DefaultMutableTreeNode("AnalogDigitalFlag: " + s, false)); + // The "false" argument signifies this will have no subnodes + } + s = aes.getSchemaVersion(); + if (s != null) { + val.add(new DefaultMutableTreeNode("SchemaVersion: " + s, false)); + } + s = aes.getFormat(); + if (s != null) { + DefaultMutableTreeNode fmt = new DefaultMutableTreeNode(FORMAT + s, true); + val.add(fmt); + String v = aes.getSpecificationVersion(); + if (v != null) { + fmt.add(new DefaultMutableTreeNode("SpecificationVersion: " + v, false)); + } + } + s = aes.getAppSpecificData(); + if (s != null) { + val.add(new DefaultMutableTreeNode("AppSpecificData: " + s, false)); + } + s = aes.getAudioDataEncoding(); + if (s != null) { + val.add(new DefaultMutableTreeNode("AudioDataEncoding: " + s, false)); + } + int in = aes.getByteOrder(); + if (in != AESAudioMetadata.NULL) { + val.add( + new DefaultMutableTreeNode( + BYTE_ORDER + (in == AESAudioMetadata.BIG_ENDIAN ? "BIG_ENDIAN" : "LITTLE_ENDIAN"))); + } + long lin = aes.getFirstSampleOffset(); + if (lin != AESAudioMetadata.NULL) { + val.add(new DefaultMutableTreeNode("FirstSampleOffset: " + Long.toString(lin))); + } + String[] use = aes.getUse(); + if (use != null) { + DefaultMutableTreeNode u = new DefaultMutableTreeNode("Use", true); + val.add(u); + u.add(new DefaultMutableTreeNode("UseType: " + use[0], false)); + u.add(new DefaultMutableTreeNode("OtherType: " + use[1], false)); + } + s = aes.getPrimaryIdentifier(); + if (s != null) { + String t = aes.getPrimaryIdentifierType(); + DefaultMutableTreeNode pi = new DefaultMutableTreeNode("PrimaryIdentifier: " + s, true); + val.add(pi); + if (t != null) { + pi.add(new DefaultMutableTreeNode("IdentifierType: " + t)); + } + } + // Add the face information, which is mostly filler. + // In the general case, it can contain multiple Faces; + // this isn't supported yet. + List facelist = aes.getFaceList(); + if (!facelist.isEmpty()) { + AESAudioMetadata.Face f = facelist.get(0); + + DefaultMutableTreeNode face = new DefaultMutableTreeNode("Face", true); + DefaultMutableTreeNode timeline = new DefaultMutableTreeNode("TimeLine", true); + AESAudioMetadata.TimeDesc startTime = f.getStartTime(); + if (startTime != null) { + addAESTimeRange(timeline, startTime, f.getDuration()); + } + face.add(timeline); + + // For the present, assume just one face region + AESAudioMetadata.FaceRegion facergn = f.getFaceRegion(0); + DefaultMutableTreeNode region = new DefaultMutableTreeNode("Region", true); + timeline = new DefaultMutableTreeNode("TimeRange", true); + addAESTimeRange(timeline, facergn.getStartTime(), facergn.getDuration()); + region.add(timeline); + int nchan = aes.getNumChannels(); + if (nchan != AESAudioMetadata.NULL) { + String[] locs = aes.getMapLocations(); + region.add(new DefaultMutableTreeNode("NumChannels: " + Integer.toString(nchan), false)); + for (String loc : locs) { + // write a stream element for each channel + DefaultMutableTreeNode stream = new DefaultMutableTreeNode("Stream", true); + region.add(stream); + stream.add(new DefaultMutableTreeNode("ChannelAssignment: " + loc, false)); + } + } + face.add(region); + val.add(face); + } + // In the general case, a FormatList can contain multiple + // FormatRegions. This doesn't happen with any of the current + // modules; if it's needed in the future, simply set up an + // iteration loop on formatList. + List flist = aes.getFormatList(); + if (!flist.isEmpty()) { + AESAudioMetadata.FormatRegion rgn = flist.get(0); + int bitDepth = rgn.getBitDepth(); + double sampleRate = rgn.getSampleRate(); + int wordSize = rgn.getWordSize(); + String[] bitRed = rgn.getBitrateReduction(); + // Build a FormatRegion subtree if at least one piece of data + // that goes into it is present. + if (bitDepth != AESAudioMetadata.NULL + || sampleRate != AESAudioMetadata.NILL + || wordSize != AESAudioMetadata.NULL) { + DefaultMutableTreeNode formatList = new DefaultMutableTreeNode("FormatList", true); + DefaultMutableTreeNode formatRegion = new DefaultMutableTreeNode("FormatRegion", true); + if (bitDepth != AESAudioMetadata.NULL) { + formatRegion.add( + new DefaultMutableTreeNode("BitDepth: " + Integer.toString(bitDepth), false)); + } + if (sampleRate != AESAudioMetadata.NILL) { + formatRegion.add( + new DefaultMutableTreeNode("SampleRate: " + Double.toString(sampleRate), false)); + } + if (wordSize != AESAudioMetadata.NULL) { + formatRegion.add( + new DefaultMutableTreeNode("WordSize: " + Integer.toString(bitDepth), false)); + } + if (bitRed != null) { + DefaultMutableTreeNode br = new DefaultMutableTreeNode("BitrateReduction", true); + br.add(new DefaultMutableTreeNode("codecName: " + bitRed[0], false)); + br.add(new DefaultMutableTreeNode("codecNameVersion: " + bitRed[1], false)); + br.add(new DefaultMutableTreeNode("codecCreatorApplication: " + bitRed[2], false)); + br.add(new DefaultMutableTreeNode("codecCreatorApplicationVersion: " + bitRed[3], false)); + br.add(new DefaultMutableTreeNode("codecQuality: " + bitRed[4], false)); + br.add(new DefaultMutableTreeNode("dataRate: " + bitRed[5], false)); + br.add(new DefaultMutableTreeNode("dataRateMode: " + bitRed[6], false)); + formatRegion.add(br); + } + formatList.add(formatRegion); + val.add(formatList); + } + } + + return val; + } + + private void addAESTimeRange( + DefaultMutableTreeNode parent, + AESAudioMetadata.TimeDesc start, + AESAudioMetadata.TimeDesc duration) { + // Put the start time in + DefaultMutableTreeNode node = new DefaultMutableTreeNode("Start", true); + // Put in boilerplate to match the AES schema + node.add(new DefaultMutableTreeNode(FRAME_COUNT_30, false)); + node.add(new DefaultMutableTreeNode(TIME_BASE_1000)); + node.add(new DefaultMutableTreeNode(VIDEO_FIELD_FIELD_1)); + node.add(new DefaultMutableTreeNode(COUNTING_MODE_NTSC_NON_DROP_FRAME, false)); + node.add(new DefaultMutableTreeNode(HOURS + start.getHours(), false)); + node.add(new DefaultMutableTreeNode(MINUTES + start.getMinutes(), false)); + node.add(new DefaultMutableTreeNode(SECONDS + start.getSeconds(), false)); + node.add(new DefaultMutableTreeNode(FRAMES + start.getFrames(), false)); + + // Do samples a bit more elaborately than is really necessary, + // to maintain parallelism with the xml schema. + DefaultMutableTreeNode snode = new DefaultMutableTreeNode(SAMPLES, true); + double sr = start.getSampleRate(); + if (sr == 1.0) { + sr = _sampleRate; + } + snode.add(new DefaultMutableTreeNode("SampleRate: S" + Integer.toString((int) sr), false)); + snode.add(new DefaultMutableTreeNode(NUMBER_OF_SAMPLES + start.getSamples(), false)); + node.add(snode); + + snode = new DefaultMutableTreeNode(FILM_FRAMING, true); + snode.add(new DefaultMutableTreeNode(FRAMING_NOT_APPLICABLE, false)); + snode.add(new DefaultMutableTreeNode(NTSC_FILM_FRAMING_TYPE, false)); + node.add(snode); + parent.add(node); + + // Duration is optional. + if (duration != null) { + node = new DefaultMutableTreeNode("Duration", true); + // Put in boilerplate to match the AES schema + node.add(new DefaultMutableTreeNode(FRAME_COUNT_30, false)); + node.add(new DefaultMutableTreeNode(TIME_BASE_1000)); + node.add(new DefaultMutableTreeNode(VIDEO_FIELD_FIELD_1)); + node.add(new DefaultMutableTreeNode(COUNTING_MODE_NTSC_NON_DROP_FRAME, false)); + node.add(new DefaultMutableTreeNode(HOURS + duration.getHours(), false)); + node.add(new DefaultMutableTreeNode(MINUTES + duration.getMinutes(), false)); + node.add(new DefaultMutableTreeNode(SECONDS + duration.getSeconds(), false)); + node.add(new DefaultMutableTreeNode(FRAMES + duration.getFrames(), false)); + + // Do samples a bit more elaborately than is really necessary, + // to maintain parallelism with the xml schema. + snode = new DefaultMutableTreeNode(SAMPLES, true); + sr = duration.getSampleRate(); + if (sr == 1.0) { + sr = _sampleRate; + } + snode.add(new DefaultMutableTreeNode("SamplesRate S" + Integer.toString((int) sr), false)); + snode.add(new DefaultMutableTreeNode(NUMBER_OF_SAMPLES + duration.getSamples(), false)); + node.add(snode); + + snode = new DefaultMutableTreeNode(FILM_FRAMING, true); + snode.add(new DefaultMutableTreeNode(FRAMING_NOT_APPLICABLE, false)); + snode.add(new DefaultMutableTreeNode(NTSC_FILM_FRAMING_TYPE, false)); + node.add(snode); + parent.add(node); + } + } + + /* Function for turning the textMD metadata into a subtree. */ + private DefaultMutableTreeNode textMDToNode(TextMDMetadata textMD) { + DefaultMutableTreeNode val = new DefaultMutableTreeNode(TEXT_MD_METADTA, true); + + DefaultMutableTreeNode u = new DefaultMutableTreeNode("Character_info", true); + val.add(u); + + String s = textMD.getCharset(); + if (s != null) { + u.add(new DefaultMutableTreeNode("Charset: " + s, false)); + } + s = textMD.getByte_orderString(); + if (s != null) { + u.add(new DefaultMutableTreeNode("Byte_order: " + s, false)); + } + s = textMD.getByte_size(); + if (s != null) { + u.add(new DefaultMutableTreeNode("Byte_size: " + s, false)); + } + s = textMD.getCharacter_size(); + if (s != null) { + u.add(new DefaultMutableTreeNode("Character_size: " + s, false)); + } + s = textMD.getLinebreakString(); + if (s != null) { + u.add(new DefaultMutableTreeNode("Linebreak: " + s, false)); + } + s = textMD.getLanguage(); + if (s != null) { + val.add(new DefaultMutableTreeNode("Language: " + s, false)); + } + s = textMD.getMarkup_basis(); + if (s != null) { + DefaultMutableTreeNode basis = new DefaultMutableTreeNode("Markup_basis: " + s, true); + val.add(basis); + s = textMD.getMarkup_basis_version(); + if (s != null) { + basis.add(new DefaultMutableTreeNode(VERSION + s, false)); + } + } + s = textMD.getMarkup_language(); + if (s != null) { + DefaultMutableTreeNode language = new DefaultMutableTreeNode("Markup_language: " + s, true); + val.add(language); + s = textMD.getMarkup_language_version(); + if (s != null) { + language.add(new DefaultMutableTreeNode(VERSION + s, false)); + } + } + return val; + } + + /* Function for turning the Niso metadata into a subtree. */ + private DefaultMutableTreeNode nisoToNode(NisoImageMetadata niso) { + DefaultMutableTreeNode val = new DefaultMutableTreeNode("NisoImageMetadata", true); + String s = niso.getMimeType(); + if (s != null) { + val.add(new DefaultMutableTreeNode("MIMEType: " + s, false)); + } + s = niso.getByteOrder(); + if (s != null) { + val.add(new DefaultMutableTreeNode(BYTE_ORDER + s, false)); + } + + int n = niso.getCompressionScheme(); + if (n != NisoImageMetadata.NULL) { + val.add( + new DefaultMutableTreeNode( + "CompressionScheme: " + + integerRepresentation( + n, + NisoImageMetadata.COMPRESSION_SCHEME, + NisoImageMetadata.COMPRESSION_SCHEME_INDEX), + false)); + } + if ((n = niso.getCompressionLevel()) != NisoImageMetadata.NULL) { + val.add(new DefaultMutableTreeNode("CompressionLevel: " + Integer.toString(n), false)); + } + if ((n = niso.getColorSpace()) != NisoImageMetadata.NULL) { + val.add( + new DefaultMutableTreeNode( + "ColorSpace: " + + integerRepresentation( + n, NisoImageMetadata.COLORSPACE, NisoImageMetadata.COLORSPACE_INDEX), + false)); + } + if ((s = niso.getProfileName()) != null) { + val.add(new DefaultMutableTreeNode("ProfileName: " + s, false)); + } + if ((s = niso.getProfileURL()) != null) { + val.add(new DefaultMutableTreeNode("ProfileURL: " + s, false)); + } + int[] iarray = niso.getYCbCrSubSampling(); + if (iarray != null) { + DefaultMutableTreeNode nod = (new DefaultMutableTreeNode("YCbCrSubSampling")); + val.add(nod); + for (int i = 0; i < iarray.length; i++) { + nod.add(new DefaultMutableTreeNode(Integer.toString(iarray[i]), false)); + } + } + if ((n = niso.getYCbCrPositioning()) != NisoImageMetadata.NULL) { + val.add( + new DefaultMutableTreeNode( + "YCbCrPositioning: " + integerRepresentation(n, NisoImageMetadata.YCBCR_POSITIONING), + false)); + } + Rational[] rarray = niso.getYCbCrCoefficients(); + if (rarray != null) { + DefaultMutableTreeNode nod = (new DefaultMutableTreeNode("YCbCrCoefficients", true)); + val.add(nod); + for (int i = 0; i < rarray.length; i++) { + nod.add(new DefaultMutableTreeNode(rarray[i].toString(), false)); + } + } + rarray = niso.getReferenceBlackWhite(); + if (rarray != null) { + DefaultMutableTreeNode nod = (new DefaultMutableTreeNode("ReferenceBlackWhite", true)); + val.add(nod); + for (int i = 0; i < rarray.length; i++) { + nod.add(new DefaultMutableTreeNode(rarray[i].toString(), false)); + } + } + if ((n = niso.getSegmentType()) != NisoImageMetadata.NULL) { + val.add( + new DefaultMutableTreeNode( + "YSegmentType: " + integerRepresentation(n, NisoImageMetadata.SEGMENT_TYPE), false)); + } + long[] larray = niso.getStripOffsets(); + if (larray != null) { + DefaultMutableTreeNode nod = (new DefaultMutableTreeNode("StripOffsets", true)); + val.add(nod); + for (int i = 0; i < larray.length; i++) { + nod.add(new DefaultMutableTreeNode(Long.toString(larray[i]), false)); + } + } + long ln = niso.getRowsPerStrip(); + if (ln != NisoImageMetadata.NULL) { + val.add(new DefaultMutableTreeNode("RowsPerStrip: " + Long.toString(ln), false)); + } + if ((larray = niso.getStripByteCounts()) != null) { + DefaultMutableTreeNode nod = (new DefaultMutableTreeNode("StripByteCounts", true)); + val.add(nod); + for (int i = 0; i < larray.length; i++) { + nod.add(new DefaultMutableTreeNode(Long.toString(larray[i]), false)); + } + } + if ((ln = niso.getTileWidth()) != NisoImageMetadata.NULL) { + val.add(new DefaultMutableTreeNode("TileWidth: " + Long.toString(ln))); + } + if ((ln = niso.getTileLength()) != NisoImageMetadata.NULL) { + val.add(new DefaultMutableTreeNode("TileLength: " + Long.toString(ln))); + } + if ((larray = niso.getTileOffsets()) != null) { + DefaultMutableTreeNode nod = (new DefaultMutableTreeNode("TileOffsets", true)); + val.add(nod); + for (int i = 0; i < larray.length; i++) { + nod.add(new DefaultMutableTreeNode(Long.toString(larray[i]), false)); + } + } + if ((larray = niso.getTileByteCounts()) != null) { + DefaultMutableTreeNode nod = (new DefaultMutableTreeNode("TileByteCounts", true)); + val.add(nod); + for (int i = 0; i < larray.length; i++) { + nod.add(new DefaultMutableTreeNode(Long.toString(larray[i]), false)); + } + } + if ((n = niso.getPlanarConfiguration()) != NisoImageMetadata.NULL) { + val.add( + new DefaultMutableTreeNode( + "PlanarConfiguration: " + + integerRepresentation(n, NisoImageMetadata.PLANAR_CONFIGURATION), + false)); + } + if ((s = niso.getImageIdentifier()) != null) { + val.add(new DefaultMutableTreeNode("ImageIdentifier: " + s, false)); + } + if ((s = niso.getImageIdentifierLocation()) != null) { + val.add(new DefaultMutableTreeNode("ImageIdentifierLocation: " + s, false)); + } + if ((ln = niso.getFileSize()) != NisoImageMetadata.NULL) { + val.add(new DefaultMutableTreeNode("FileSize: " + Long.toString(ln), false)); + } + if ((n = niso.getChecksumMethod()) != NisoImageMetadata.NULL) { + val.add( + new DefaultMutableTreeNode( + "ChecksumMethod: " + integerRepresentation(n, NisoImageMetadata.CHECKSUM_METHOD), + false)); + } + if ((s = niso.getChecksumValue()) != null) { + val.add(new DefaultMutableTreeNode("ChecksumValue: " + s, false)); + } + if ((n = niso.getOrientation()) != NisoImageMetadata.NULL) { + val.add( + new DefaultMutableTreeNode( + "Orientation: " + integerRepresentation(n, NisoImageMetadata.ORIENTATION), false)); + } + if ((n = niso.getDisplayOrientation()) != NisoImageMetadata.NULL) { + val.add( + new DefaultMutableTreeNode( + "DisplayOrientation: " + + integerRepresentation(n, NisoImageMetadata.DISPLAY_ORIENTATION), + false)); + } + if ((ln = niso.getXTargetedDisplayAR()) != NisoImageMetadata.NULL) { + val.add(new DefaultMutableTreeNode("XTargetedDisplayAR: " + Long.toString(ln), false)); + } + if ((ln = niso.getYTargetedDisplayAR()) != NisoImageMetadata.NULL) { + val.add(new DefaultMutableTreeNode("YTargetedDisplayAR: " + Long.toString(ln), false)); + } + if ((s = niso.getPreferredPresentation()) != null) { + val.add(new DefaultMutableTreeNode("PreferredPresentation: " + s, false)); + } + if ((s = niso.getSourceType()) != null) { + val.add(new DefaultMutableTreeNode("SourceType: " + s, false)); + } + if ((s = niso.getImageProducer()) != null) { + val.add(new DefaultMutableTreeNode("ImageProducer: " + s, false)); + } + if ((s = niso.getHostComputer()) != null) { + val.add(new DefaultMutableTreeNode("HostComputer: " + s, false)); + } + if ((s = niso.getOS()) != null) { + val.add(new DefaultMutableTreeNode("OperatingSystem: " + s, false)); + } + if ((s = niso.getOSVersion()) != null) { + val.add(new DefaultMutableTreeNode("OSVersion: " + s, false)); + } + if ((s = niso.getDeviceSource()) != null) { + val.add(new DefaultMutableTreeNode("DeviceSource: " + s, false)); + } + if ((s = niso.getScannerManufacturer()) != null) { + val.add(new DefaultMutableTreeNode("ScannerManufacturer: " + s, false)); + } + if ((s = niso.getScannerModelName()) != null) { + val.add(new DefaultMutableTreeNode("ScannerModelName: " + s, false)); + } + if ((s = niso.getScannerModelNumber()) != null) { + val.add(new DefaultMutableTreeNode("ScannerModelNumber: " + s, false)); + } + if ((s = niso.getScannerModelSerialNo()) != null) { + val.add(new DefaultMutableTreeNode("ScannerModelSerialNo: " + s, false)); + } + if ((s = niso.getScanningSoftware()) != null) { + val.add(new DefaultMutableTreeNode("ScanningSoftware: " + s, false)); + } + if ((s = niso.getScanningSoftwareVersionNo()) != null) { + val.add(new DefaultMutableTreeNode("ScanningSoftwareVersionNo: " + s, false)); + } + double d = niso.getPixelSize(); + if (d != NisoImageMetadata.NILL) { + val.add(new DefaultMutableTreeNode("PixelSize: " + Double.toString(d), false)); + } + if ((d = niso.getXPhysScanResolution()) != NisoImageMetadata.NILL) { + val.add(new DefaultMutableTreeNode("XPhysScanResolution: " + Double.toString(d), false)); + } + if ((d = niso.getYPhysScanResolution()) != NisoImageMetadata.NILL) { + val.add(new DefaultMutableTreeNode("YPhysScanResolution: " + Double.toString(d), false)); + } + + if ((s = niso.getDigitalCameraManufacturer()) != null) { + val.add(new DefaultMutableTreeNode("DigitalCameraManufacturer: " + s, false)); + } + if ((s = niso.getDigitalCameraModelName()) != null) { + val.add(new DefaultMutableTreeNode("DigitalCameraModelName: " + s, false)); + } + if ((s = niso.getDigitalCameraModelNumber()) != null) { + val.add(new DefaultMutableTreeNode("DigitalCameraModelNumber: " + s, false)); + } + if ((s = niso.getDigitalCameraModelSerialNo()) != null) { + val.add(new DefaultMutableTreeNode("DigitalCameraModelSerialNo: " + s, false)); + } + if ((d = niso.getFNumber()) != NisoImageMetadata.NILL) { + val.add(new DefaultMutableTreeNode("FNumber: " + Double.toString(d), false)); + } + if ((d = niso.getExposureTime()) != NisoImageMetadata.NILL) { + val.add(new DefaultMutableTreeNode("ExposureTime: " + Double.toString(d), false)); + } + if ((n = niso.getExposureProgram()) != NisoImageMetadata.NULL) { + val.add(new DefaultMutableTreeNode("ExposureProgram: " + Integer.toString(n), false)); + } + if ((s = niso.getExifVersion()) != null) { + val.add(new DefaultMutableTreeNode("ExifVersion: " + s, false)); + } + Rational r; + if ((r = niso.getBrightness()) != null) { + val.add(new DefaultMutableTreeNode("Brightness: " + r.toString(), false)); + } + if ((r = niso.getExposureBias()) != null) { + val.add(new DefaultMutableTreeNode("ExposureBias: " + r.toString(), false)); + } + + double[] darray = niso.getSubjectDistance(); + if (darray != null) { + DefaultMutableTreeNode nod = new DefaultMutableTreeNode("SubjectDistance", true); + val.add(nod); + for (int i = 0; i < darray.length; i++) { + nod.add(new DefaultMutableTreeNode(Double.toString(darray[i]), false)); + } + } + if ((n = niso.getMeteringMode()) != NisoImageMetadata.NULL) { + val.add(new DefaultMutableTreeNode("MeteringMode: " + Integer.toString(n), false)); + } + if ((n = niso.getSceneIlluminant()) != NisoImageMetadata.NULL) { + val.add(new DefaultMutableTreeNode("SceneIlluminant: " + Integer.toString(n), false)); + } + if ((d = niso.getColorTemp()) != NisoImageMetadata.NILL) { + val.add(new DefaultMutableTreeNode("ColorTemp: " + Double.toString(d), false)); + } + if ((d = niso.getFocalLength()) != NisoImageMetadata.NILL) { + val.add(new DefaultMutableTreeNode("FocalLength: " + Double.toString(d), false)); + } + if ((n = niso.getFlash()) != NisoImageMetadata.NULL) { + // First bit (0 = Flash did not fire, 1 = Flash fired) + val.add( + new DefaultMutableTreeNode( + "Flash: " + integerRepresentation(n & 1, NisoImageMetadata.FLASH_20), false)); + } + if ((r = niso.getFlashEnergy()) != null) { + val.add(new DefaultMutableTreeNode("FlashEnergy: " + r.toString(), false)); + } + if ((n = niso.getFlashReturn()) != NisoImageMetadata.NULL) { + val.add( + new DefaultMutableTreeNode( + "FlashReturn: " + integerRepresentation(n, NisoImageMetadata.FLASH_RETURN), false)); + } + if ((n = niso.getBackLight()) != NisoImageMetadata.NULL) { + val.add( + new DefaultMutableTreeNode( + "BackLight: " + integerRepresentation(n, NisoImageMetadata.BACKLIGHT), false)); + } + if ((d = niso.getExposureIndex()) != NisoImageMetadata.NILL) { + val.add(new DefaultMutableTreeNode("ExposureIndex: " + Double.toString(d), false)); + } + if ((n = niso.getAutoFocus()) != NisoImageMetadata.NULL) { + val.add(new DefaultMutableTreeNode("AutoFocus: " + Integer.toString(n), false)); + // NisoImageMetadata.AUTOFOCUS + } + if ((d = niso.getXPrintAspectRatio()) != NisoImageMetadata.NILL) { + val.add(new DefaultMutableTreeNode("XPrintAspectRatio: " + Double.toString(d), false)); + } + if ((d = niso.getYPrintAspectRatio()) != NisoImageMetadata.NILL) { + val.add(new DefaultMutableTreeNode("YPrintAspectRatio: " + Double.toString(d), false)); + } + + if ((n = niso.getSensor()) != NisoImageMetadata.NULL) { + val.add( + new DefaultMutableTreeNode( + "Sensor: " + integerRepresentation(n, NisoImageMetadata.SENSOR), false)); + } + if ((s = niso.getDateTimeCreated()) != null) { + val.add(new DefaultMutableTreeNode("DateTimeCreated: " + s, false)); + } + if ((s = niso.getMethodology()) != null) { + val.add(new DefaultMutableTreeNode("Methodology: " + s, false)); + } + if ((n = niso.getSamplingFrequencyPlane()) != NisoImageMetadata.NULL) { + val.add( + new DefaultMutableTreeNode( + "SamplingFrequencyPlane: " + + integerRepresentation(n, NisoImageMetadata.SAMPLING_FREQUENCY_PLANE), + false)); + } + if ((n = niso.getSamplingFrequencyUnit()) != NisoImageMetadata.NULL) { + val.add( + new DefaultMutableTreeNode( + "SamplingFrequencyUnit: " + + integerRepresentation(n, NisoImageMetadata.SAMPLING_FREQUENCY_UNIT), + false)); + } + Rational rat = niso.getXSamplingFrequency(); + if (rat != null) { + val.add(new DefaultMutableTreeNode("XSamplingFrequency: " + rat.toString(), false)); + } + rat = niso.getYSamplingFrequency(); + if (rat != null) { + val.add(new DefaultMutableTreeNode("YSamplingFrequency: " + rat.toString(), false)); + } + if ((ln = niso.getImageWidth()) != NisoImageMetadata.NULL) { + val.add(new DefaultMutableTreeNode("ImageWidth: " + Long.toString(ln), false)); + } + if ((ln = niso.getImageLength()) != NisoImageMetadata.NULL) { + val.add(new DefaultMutableTreeNode("ImageLength: " + Long.toString(ln), false)); + } + if ((d = niso.getSourceXDimension()) != NisoImageMetadata.NILL) { + val.add(new DefaultMutableTreeNode("SourceXDimension: " + Double.toString(d), false)); + } + if ((n = niso.getSourceXDimensionUnit()) != NisoImageMetadata.NULL) { + val.add( + new DefaultMutableTreeNode( + "SourceXDimensionUnit: " + + integerRepresentation(n, NisoImageMetadata.SOURCE_DIMENSION_UNIT), + false)); + } + if ((d = niso.getSourceYDimension()) != NisoImageMetadata.NILL) { + val.add(new DefaultMutableTreeNode("SourceYDimension: " + Double.toString(d), false)); + } + if ((n = niso.getSourceYDimensionUnit()) != NisoImageMetadata.NULL) { + val.add( + new DefaultMutableTreeNode( + "SourceYDimensionUnit: " + + integerRepresentation(n, NisoImageMetadata.SOURCE_DIMENSION_UNIT), + false)); + } + if ((iarray = niso.getBitsPerSample()) != null) { + DefaultMutableTreeNode nod = (new DefaultMutableTreeNode("BitsPerSample")); + val.add(nod); + for (int i = 0; i < iarray.length; i++) { + nod.add(new DefaultMutableTreeNode(Integer.toString(iarray[i]), false)); + } + } + if ((n = niso.getSamplesPerPixel()) != NisoImageMetadata.NULL) { + val.add(new DefaultMutableTreeNode("SamplesPerPixel: " + Integer.toString(n), false)); + } + if ((iarray = niso.getExtraSamples()) != null) { + DefaultMutableTreeNode nod = (new DefaultMutableTreeNode("ExtraSamples")); + val.add(nod); + for (int i = 0; i < iarray.length; i++) { + nod.add( + new DefaultMutableTreeNode( + integerRepresentation(iarray[i], NisoImageMetadata.EXTRA_SAMPLES), false)); + } + } + if ((s = niso.getColormapReference()) != null) { + val.add(new DefaultMutableTreeNode("ColormapReference: " + s)); + } + if ((iarray = niso.getColormapBitCodeValue()) != null) { + DefaultMutableTreeNode nod = (new DefaultMutableTreeNode("ColormapBitCodeValue")); + val.add(nod); + for (int i = 0; i < iarray.length; i++) { + nod.add(new DefaultMutableTreeNode(Integer.toString(iarray[i]), false)); + } + } + if ((iarray = niso.getColormapRedValue()) != null) { + DefaultMutableTreeNode nod = (new DefaultMutableTreeNode("ColormapRedValue")); + val.add(nod); + for (int i = 0; i < iarray.length; i++) { + nod.add(new DefaultMutableTreeNode(Integer.toString(iarray[i]), false)); + } + } + if ((iarray = niso.getColormapGreenValue()) != null) { + DefaultMutableTreeNode nod = (new DefaultMutableTreeNode("ColormapGreenValue")); + val.add(nod); + for (int i = 0; i < iarray.length; i++) { + nod.add(new DefaultMutableTreeNode(Integer.toString(iarray[i]), false)); + } + } + if ((iarray = niso.getColormapBlueValue()) != null) { + DefaultMutableTreeNode nod = (new DefaultMutableTreeNode("ColormapBlueValue")); + val.add(nod); + for (int i = 0; i < iarray.length; i++) { + nod.add(new DefaultMutableTreeNode(Integer.toString(iarray[i]), false)); + } + } + if ((iarray = niso.getGrayResponseCurve()) != null) { + DefaultMutableTreeNode nod = (new DefaultMutableTreeNode("GrayResponseCurve")); + val.add(nod); + for (int i = 0; i < iarray.length; i++) { + nod.add(new DefaultMutableTreeNode(Integer.toString(iarray[i]), false)); + } + } + if ((n = niso.getGrayResponseUnit()) != NisoImageMetadata.NULL) { + val.add(new DefaultMutableTreeNode("GrayResponseUnit: " + Integer.toString(n), false)); + } + r = niso.getWhitePointXValue(); + if (r != null) { + val.add(new DefaultMutableTreeNode("WhitePointXValue: " + r.toString(), false)); + } + if ((r = niso.getWhitePointYValue()) != null) { + val.add(new DefaultMutableTreeNode("WhitePointYValue: " + r.toString(), false)); + } + if ((r = niso.getPrimaryChromaticitiesRedX()) != null) { + val.add(new DefaultMutableTreeNode("PrimaryChromaticitiesRedX: " + r.toString(), false)); + } + if ((r = niso.getPrimaryChromaticitiesRedY()) != null) { + val.add(new DefaultMutableTreeNode("PrimaryChromaticitiesRedY: " + r.toString(), false)); + } + if ((r = niso.getPrimaryChromaticitiesGreenX()) != null) { + val.add(new DefaultMutableTreeNode("PrimaryChromaticitiesGreenX: " + r.toString(), false)); + } + if ((r = niso.getPrimaryChromaticitiesGreenY()) != null) { + val.add(new DefaultMutableTreeNode("PrimaryChromaticitiesGreenY: " + r.toString(), false)); + } + if ((r = niso.getPrimaryChromaticitiesBlueX()) != null) { + val.add(new DefaultMutableTreeNode("PrimaryChromaticitiesBlueX: " + r.toString())); + } + if ((r = niso.getPrimaryChromaticitiesBlueY()) != null) { + val.add(new DefaultMutableTreeNode("PrimaryChromaticitiesBlueY: " + r.toString())); + } + if ((n = niso.getTargetType()) != NisoImageMetadata.NULL) { + val.add( + new DefaultMutableTreeNode( + "TargetType: " + integerRepresentation(n, NisoImageMetadata.TARGET_TYPE), false)); + } + if ((s = niso.getTargetIDManufacturer()) != null) { + val.add(new DefaultMutableTreeNode("TargetIDManufacturer: " + s, false)); + } + if ((s = niso.getTargetIDName()) != null) { + val.add(new DefaultMutableTreeNode("TargetIDName: " + s, false)); + } + if ((s = niso.getTargetIDNo()) != null) { + val.add(new DefaultMutableTreeNode("TargetIDNo: " + s, false)); + } + if ((s = niso.getTargetIDMedia()) != null) { + val.add(new DefaultMutableTreeNode("TargetIDMedia: " + s, false)); + } + if ((s = niso.getImageData()) != null) { + val.add(new DefaultMutableTreeNode("ImageData: " + s, false)); + } + if ((s = niso.getPerformanceData()) != null) { + val.add(new DefaultMutableTreeNode("PerformanceData: " + s, false)); + } + if ((s = niso.getProfiles()) != null) { + val.add(new DefaultMutableTreeNode("Profiles: " + s, false)); + } + if ((s = niso.getDateTimeProcessed()) != null) { + val.add(new DefaultMutableTreeNode("DateTimeProcessed: " + s, false)); + } + if ((s = niso.getSourceData()) != null) { + val.add(new DefaultMutableTreeNode("SourceData: " + s, false)); + } + if ((s = niso.getProcessingAgency()) != null) { + val.add(new DefaultMutableTreeNode("ProcessingAgency: " + s, false)); + } + if ((s = niso.getProcessingSoftwareName()) != null) { + val.add(new DefaultMutableTreeNode("ProcessingSoftwareName: " + s, false)); + } + if ((s = niso.getProcessingSoftwareVersion()) != null) { + val.add(new DefaultMutableTreeNode("ProcessingSoftwareVersion: " + s, false)); + } + String[] sarray = niso.getProcessingActions(); + if (sarray != null) { + DefaultMutableTreeNode nod = new DefaultMutableTreeNode("ProcessingActions", true); + val.add(nod); + for (int i = 1; i < sarray.length; i++) { + nod.add(new DefaultMutableTreeNode(sarray[i], false)); + } + } + return val; + } + + /* + * Return the string equivalent of an integer if raw output isn't selected, + * or its literal representation if it is. + */ + private String integerRepresentation(int n, String[] labels) { + if (_rawOutput) { + return Integer.toString(n); + } + try { + return labels[n]; + } catch (Exception e) { + return Integer.toString(n); + } + } + + private String integerRepresentation(int n, String[] labels, int[] index) { + if (_rawOutput) { + return Integer.toString(n); + } + try { + int idx = -1; + for (int i = 0; i < index.length; i++) { + if (n == index[i]) { + idx = i; + break; + } + } + if (idx > -1) { + return labels[idx]; + } + } catch (Exception e) { + } + // If we don't get a match, or do get an exception + return Integer.toString(n); + } + + /** + * This method returns a member of a property list based on it's PropertyType + * + * @param ptyp the propertytype + * @param item the item of the list + * @param allowsChildren + * @return + */ + private DefaultMutableTreeNode getDefaultMutableTreeNode( + PropertyType ptyp, Object item, boolean allowsChildren) { + + DefaultMutableTreeNode itemNode; + + if (null == ptyp) { + // Simple objects just need a leaf. + itemNode = (new DefaultMutableTreeNode(item, allowsChildren)); + } else + // Object item = iter.next (); + switch (ptyp) { + case PROPERTY: + itemNode = (propToNode((Property) item)); + break; + case NISOIMAGEMETADATA: + itemNode = (nisoToNode((NisoImageMetadata) item)); + break; + default: + // Simple objects just need a leaf. + itemNode = (new DefaultMutableTreeNode(item, allowsChildren)); + break; + } + + return itemNode; + } + + /** + * Method to add an array to a node + * + * @param generic method, can be used for arrays of different types + * @param node the node to add the elements of the array + * @param array the array to be added to the node + */ + private void addToNode(DefaultMutableTreeNode node, E[] array) { + for (E element : array) { + if (element instanceof Property) { + node.add(propToNode((Property) element)); + } else { + node.add(new DefaultMutableTreeNode(element)); + } + } + } } diff --git a/jhove-apps/src/main/java/edu/harvard/hul/ois/jhove/viewer/ViewHandler.java b/jhove-apps/src/main/java/edu/harvard/hul/ois/jhove/viewer/ViewHandler.java index 5ca0d5708..b9fef0ffd 100644 --- a/jhove-apps/src/main/java/edu/harvard/hul/ois/jhove/viewer/ViewHandler.java +++ b/jhove-apps/src/main/java/edu/harvard/hul/ois/jhove/viewer/ViewHandler.java @@ -1,8 +1,8 @@ -/********************************************************************** - * Jhove - JSTOR/Harvard Object Validation Environment - * Copyright 2004 by JSTOR and the President and Fellows of Harvard College - **********************************************************************/ - +/** + * ******************************************************************** Jhove - JSTOR/Harvard Object + * Validation Environment Copyright 2004 by JSTOR and the President and Fellows of Harvard College + * ******************************************************************** + */ package edu.harvard.hul.ois.jhove.viewer; import edu.harvard.hul.ois.jhove.Agent; @@ -14,134 +14,126 @@ import edu.harvard.hul.ois.jhove.RepInfo; /** - * This is an output handler which connects JHOVE output to the Swing interface - * of the viewer application. It is responsible for creating appropriate windows - * and making them known to (?). + * This is an output handler which connects JHOVE output to the Swing interface of the viewer + * application. It is responsible for creating appropriate windows and making them known to (?). * * @author Gary McGath - * */ public class ViewHandler extends HandlerBase { - /****************************************************************** - * PRIVATE CLASS FIELDS. - ******************************************************************/ - - private static final String NAME = "VIEW"; - private static final String RELEASE = "1.0"; - private static final int[] DATE = { 2004, 11, 2 }; - private static final String NOTE = "This is the JHOVE Viewer output " - + "handler"; - private static final String RIGHTS = "Copyright 2004 by JSTOR and " - + "the President and Fellows of Harvard College. " - + "Released under the terms of the GNU Lesser General Public License."; - - private JhoveWindow _jhwin; - - private ViewWindow _viewWin; - private String syncStr = "anyoldtext"; // object just for synchronizing - - // Initial position for view windows. - // Stagger them by adding an increment each time. - private static int viewWinXPos = 24; - private static int viewWinYPos = 24; - // Original positions for cycling back to. - private static final int viewWinOrigXPos = 24; - private static final int viewWinOrigYPos = 24; - private static final int viewWinXInc = 25; - private static final int viewWinYInc = 22; - - private int nDocs; - - /****************************************************************** - * CLASS CONSTRUCTOR. - ******************************************************************/ - - /** - * Creates a ViewHandler. - * - * @param jhwin - * The JhoveWindow which acts as the parent to output windows. - */ - public ViewHandler(JhoveWindow jhwin, App app, JhoveBase base) { - super(NAME, RELEASE, DATE, NOTE, RIGHTS); - _app = app; - _base = base; - base.setCallback(jhwin); - _vendor = Agent.harvardInstance(); - _jhwin = jhwin; - - } - - /** Do the initial output. This needs to set up the window. */ - @Override - public void showHeader() { - _viewWin = new ViewWindow(_app, _base, _jhwin); - nDocs = 0; - } - - /** - * Outputs the information contained in a RepInfo object. showHeader must be - * called to set up the ViewWindow before this is called. - * - * I need to break out part of the ViewWindow code to here so it can produce - * the output for one file. - */ - @Override - public void show(RepInfo info) { - _viewWin.addRepInfo(info, _base); - ++nDocs; - } - - @Override - public void show() { - } - - @Override - public void show(App app) { - } - - @Override - public void show(Module module) { - } - - /** Complete the output. Does this have to do anything? */ - @Override - public void showFooter() { - // If no files were processed, just discard the window. */ - if (nDocs == 0) { - _viewWin.dispose(); - } - // We synchronize this against the modal dialogs, - // since otherwise the application is apt to put up - // viewWin as a frozen window over the modal dialog. - synchronized (syncStr) { - // MainScreen.centerWindow (viewWin); - _viewWin.setLocation(viewWinXPos, viewWinYPos); - viewWinXPos += viewWinXInc; - viewWinYPos += viewWinYInc; - - // After a while, cycle back to the original positions - // After a while, cycle back to the original positions - if (viewWinXPos > viewWinOrigXPos + 320) { - viewWinXPos = viewWinOrigXPos; - } - if (viewWinYPos > viewWinOrigYPos + 160) { - viewWinYPos = viewWinOrigYPos; - } - _viewWin.setVisible(true); - } - _viewWin.expandRows(); - _viewWin.setVisible(true); - } - - /** - * Outputs information about the OutputHandler specified in the parameter. - * Since this never should occur in a normal list of handlers, it's - * unnecessary to do anything (I think). - */ - @Override - public void show(OutputHandler handler) { - } - + /** + * **************************************************************** PRIVATE CLASS FIELDS. + * **************************************************************** + */ + private static final String NAME = "VIEW"; + + private static final String RELEASE = "1.0"; + private static final int[] DATE = {2004, 11, 2}; + private static final String NOTE = "This is the JHOVE Viewer output " + "handler"; + private static final String RIGHTS = + "Copyright 2004 by JSTOR and " + + "the President and Fellows of Harvard College. " + + "Released under the terms of the GNU Lesser General Public License."; + + private JhoveWindow _jhwin; + + private ViewWindow _viewWin; + private String syncStr = "anyoldtext"; // object just for synchronizing + + // Initial position for view windows. + // Stagger them by adding an increment each time. + private static int viewWinXPos = 24; + private static int viewWinYPos = 24; + // Original positions for cycling back to. + private static final int viewWinOrigXPos = 24; + private static final int viewWinOrigYPos = 24; + private static final int viewWinXInc = 25; + private static final int viewWinYInc = 22; + + private int nDocs; + + /** + * **************************************************************** CLASS CONSTRUCTOR. + * **************************************************************** + */ + + /** + * Creates a ViewHandler. + * + * @param jhwin The JhoveWindow which acts as the parent to output windows. + */ + public ViewHandler(JhoveWindow jhwin, App app, JhoveBase base) { + super(NAME, RELEASE, DATE, NOTE, RIGHTS); + _app = app; + _base = base; + base.setCallback(jhwin); + _vendor = Agent.harvardInstance(); + _jhwin = jhwin; + } + + /** Do the initial output. This needs to set up the window. */ + @Override + public void showHeader() { + _viewWin = new ViewWindow(_app, _base, _jhwin); + nDocs = 0; + } + + /** + * Outputs the information contained in a RepInfo object. showHeader must be called to set up the + * ViewWindow before this is called. + * + *

I need to break out part of the ViewWindow code to here so it can produce the output for one + * file. + */ + @Override + public void show(RepInfo info) { + _viewWin.addRepInfo(info, _base); + ++nDocs; + } + + @Override + public void show() {} + + @Override + public void show(App app) {} + + @Override + public void show(Module module) {} + + /** Complete the output. Does this have to do anything? */ + @Override + public void showFooter() { + // If no files were processed, just discard the window. */ + if (nDocs == 0) { + _viewWin.dispose(); + } + // We synchronize this against the modal dialogs, + // since otherwise the application is apt to put up + // viewWin as a frozen window over the modal dialog. + synchronized (syncStr) { + // MainScreen.centerWindow (viewWin); + _viewWin.setLocation(viewWinXPos, viewWinYPos); + viewWinXPos += viewWinXInc; + viewWinYPos += viewWinYInc; + + // After a while, cycle back to the original positions + // After a while, cycle back to the original positions + if (viewWinXPos > viewWinOrigXPos + 320) { + viewWinXPos = viewWinOrigXPos; + } + if (viewWinYPos > viewWinOrigYPos + 160) { + viewWinYPos = viewWinOrigYPos; + } + _viewWin.setVisible(true); + } + _viewWin.expandRows(); + _viewWin.setVisible(true); + } + + /** + * Outputs information about the OutputHandler specified in the parameter. Since this never should + * occur in a normal list of handlers, it's unnecessary to do anything (I think). + */ + @Override + public void show(OutputHandler handler) {} } diff --git a/jhove-apps/src/main/java/edu/harvard/hul/ois/jhove/viewer/ViewWindow.java b/jhove-apps/src/main/java/edu/harvard/hul/ois/jhove/viewer/ViewWindow.java index 2425c92fa..ee0d12e7e 100644 --- a/jhove-apps/src/main/java/edu/harvard/hul/ois/jhove/viewer/ViewWindow.java +++ b/jhove-apps/src/main/java/edu/harvard/hul/ois/jhove/viewer/ViewWindow.java @@ -1,175 +1,158 @@ -/********************************************************************** - * Jhove - JSTOR/Harvard Object Validation Environment - * Copyright 2004-2012 by JSTOR and the President and Fellows of Harvard College - **********************************************************************/ - - +/** + * ******************************************************************** Jhove - JSTOR/Harvard Object + * Validation Environment Copyright 2004-2012 by JSTOR and the President and Fellows of Harvard + * College ******************************************************************** + */ package edu.harvard.hul.ois.jhove.viewer; +import edu.harvard.hul.ois.jhove.*; import java.awt.Dimension; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; import java.io.*; +import java.util.*; import javax.swing.*; import javax.swing.tree.*; -import java.awt.event.ActionListener; -import java.awt.event.ActionEvent; -import java.util.*; -import edu.harvard.hul.ois.jhove.*; /** - * This is the main window for viewing the results of a file analysis. - * Multiple instances of InfoWindow are allowed; they are deleted - * when closed. The URI of the analyzed object is used as the window - * title. A JTree is used to display the information. - * - * The earlier version of this class displayed a single file. This - * version is controlled by a ViewHandler, which displays multiple - * files within a single window. There can still be multiple - * ViewWindows, but each will come from a separate operation. - * Dragging a directory to the main window will produce a single - * ViewWindow; dragging a group of files will produce - * multiple windows. + * This is the main window for viewing the results of a file analysis. Multiple instances of + * InfoWindow are allowed; they are deleted when closed. The URI of the analyzed object is used as + * the window title. A JTree is used to display the information. * + *

The earlier version of this class displayed a single file. This version is controlled by a + * ViewHandler, which displays multiple files within a single window. There can still be multiple + * ViewWindows, but each will come from a separate operation. Dragging a directory to the main + * window will produce a single ViewWindow; dragging a group of files will produce multiple windows. */ public class ViewWindow extends InfoWindow { - private java.util.List _info; - private JMenuItem _closeAllItem; - private ActionListener _closeAllListener; - private DefaultMutableTreeNode _rootNode; - private JTree tree; - - /** - * Constructor. - * - * @param app The associated App object. - * @param base The JhoveBase object for the application. - * @param jhwin The main JhoveWindow. - */ - public ViewWindow (App app, JhoveBase base, JhoveWindow jhwin) - { - // Give the window a temporary title. The title should probably - // be changed to the top-level directory, or else should be - // given a sequential number for each new window. - super ("RepInfo", app, base); - setSaveActionListener ( - new ActionListener() { - @Override - public void actionPerformed (ActionEvent e) { - saveInfo (); - } - }); + private java.util.List _info; + private JMenuItem _closeAllItem; + private ActionListener _closeAllListener; + private DefaultMutableTreeNode _rootNode; + private JTree tree; - _info = new LinkedList<> (); - // The root element should no longer be a - // RepTreeRoot, but some other flavor of - // DefaultMutableTreeNode. It will have RepTreeRoots - // (now somewhat misleadingly named) added to it. - DefaultMutableTreeNode root = new DefaultMutableTreeNode("Documents"); - _rootNode = root; - TreeModel treeModel = new DefaultTreeModel (root); - tree = new JTree (); - tree.setModel (treeModel); - tree.setShowsRootHandles (true); - //tree.setCellRenderer (new ViewCellRenderer ()); - TreeCellRenderer rend = tree.getCellRenderer (); - if (rend instanceof DefaultTreeCellRenderer) { - // it should be - DefaultTreeCellRenderer trend = - (DefaultTreeCellRenderer) rend; - trend.setOpenIcon (null); - trend.setClosedIcon (null); - trend.setLeafIcon (null); - } - JScrollPane scrollPane = new JScrollPane (tree); - getContentPane ().add (scrollPane, "Center"); - - // Add a small panel at the bottom, since on some OS's there - // may be stuff near the bottom of a window which will conflict - // with the scroll bar. - JPanel panel = new JPanel (); - panel.setMinimumSize (new Dimension (8, 8)); - getContentPane ().add (panel, "South"); - - setDefaultCloseOperation (WindowConstants.DISPOSE_ON_CLOSE); - setSize (400, 600); - - // Set up to handle "close all documents" from main window - if (jhwin != null) { - _closeAllItem = jhwin.getCloseAllItem (); - _closeAllListener = new ActionListener() { - @Override - public void actionPerformed (ActionEvent e) { - closeFromMenu (); - } - }; - _closeAllItem.addActionListener (_closeAllListener); + /** + * Constructor. + * + * @param app The associated App object. + * @param base The JhoveBase object for the application. + * @param jhwin The main JhoveWindow. + */ + public ViewWindow(App app, JhoveBase base, JhoveWindow jhwin) { + // Give the window a temporary title. The title should probably + // be changed to the top-level directory, or else should be + // given a sequential number for each new window. + super("RepInfo", app, base); + setSaveActionListener( + new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + saveInfo(); + } + }); - } + _info = new LinkedList<>(); + // The root element should no longer be a + // RepTreeRoot, but some other flavor of + // DefaultMutableTreeNode. It will have RepTreeRoots + // (now somewhat misleadingly named) added to it. + DefaultMutableTreeNode root = new DefaultMutableTreeNode("Documents"); + _rootNode = root; + TreeModel treeModel = new DefaultTreeModel(root); + tree = new JTree(); + tree.setModel(treeModel); + tree.setShowsRootHandles(true); + // tree.setCellRenderer (new ViewCellRenderer ()); + TreeCellRenderer rend = tree.getCellRenderer(); + if (rend instanceof DefaultTreeCellRenderer) { + // it should be + DefaultTreeCellRenderer trend = (DefaultTreeCellRenderer) rend; + trend.setOpenIcon(null); + trend.setClosedIcon(null); + trend.setLeafIcon(null); } + JScrollPane scrollPane = new JScrollPane(tree); + getContentPane().add(scrollPane, "Center"); + + // Add a small panel at the bottom, since on some OS's there + // may be stuff near the bottom of a window which will conflict + // with the scroll bar. + JPanel panel = new JPanel(); + panel.setMinimumSize(new Dimension(8, 8)); + getContentPane().add(panel, "South"); - /** Appends the representation of a RepInfo object to the - * tree. The RepInfo object is saved into a list so that - * the window contents can be saved to a file later. - */ - public void addRepInfo (RepInfo info, JhoveBase base) - { - _info.add (info); - RepTreeRoot node = new RepTreeRoot (info, base); - _rootNode.add (node); + setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE); + setSize(400, 600); + + // Set up to handle "close all documents" from main window + if (jhwin != null) { + _closeAllItem = jhwin.getCloseAllItem(); + _closeAllListener = + new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + closeFromMenu(); + } + }; + _closeAllItem.addActionListener(_closeAllListener); } - - /** Expands the tree appropriately when everything is build. */ - public void expandRows () - { - tree.expandRow (0); - if (tree.getRowCount() == 2) { - // Just one file -- expand it - tree.expandRow (1); - } + } + + /** + * Appends the representation of a RepInfo object to the tree. The RepInfo object is saved into a + * list so that the window contents can be saved to a file later. + */ + public void addRepInfo(RepInfo info, JhoveBase base) { + _info.add(info); + RepTreeRoot node = new RepTreeRoot(info, base); + _rootNode.add(node); + } + + /** Expands the tree appropriately when everything is build. */ + public void expandRows() { + tree.expandRow(0); + if (tree.getRowCount() == 2) { + // Just one file -- expand it + tree.expandRow(1); } + } - /** - * Saves the information to a file specified by the user. - */ - private void saveInfo () - { - PrintWriter wtr = doSaveDialog (); - if (wtr == null) { - return; - } - OutputHandler handler; - try { - handler = selectHandler (); - handler.reset (); - handler.setWriter (wtr); - handler.showHeader (); - Iterator iter = _info.iterator (); - while (iter.hasNext ()) { - RepInfo info = iter.next (); - handler.show (info); - } - handler.showFooter (); - wtr.close (); - } - catch (Exception e) { - JOptionPane.showMessageDialog - (this, - e.getMessage(), - "Error writing file", - JOptionPane.ERROR_MESSAGE); - } + /** Saves the information to a file specified by the user. */ + private void saveInfo() { + PrintWriter wtr = doSaveDialog(); + if (wtr == null) { + return; } - - /** Invoked when the "Close" menu item is selected. - * Overrides the parent class's method to delete - * the window rather than hiding it. */ - @Override - protected void closeFromMenu () - { - super.closeFromMenu (); - if (_closeAllItem != null) { - _closeAllItem.removeActionListener (_closeAllListener); - } - dispose (); + OutputHandler handler; + try { + handler = selectHandler(); + handler.reset(); + handler.setWriter(wtr); + handler.showHeader(); + Iterator iter = _info.iterator(); + while (iter.hasNext()) { + RepInfo info = iter.next(); + handler.show(info); + } + handler.showFooter(); + wtr.close(); + } catch (Exception e) { + JOptionPane.showMessageDialog( + this, e.getMessage(), "Error writing file", JOptionPane.ERROR_MESSAGE); + } + } + + /** + * Invoked when the "Close" menu item is selected. Overrides the parent class's method to delete + * the window rather than hiding it. + */ + @Override + protected void closeFromMenu() { + super.closeFromMenu(); + if (_closeAllItem != null) { + _closeAllItem.removeActionListener(_closeAllListener); } + dispose(); + } } diff --git a/jhove-apps/src/main/java/edu/harvard/hul/ois/jhove/viewer/package-info.java b/jhove-apps/src/main/java/edu/harvard/hul/ois/jhove/viewer/package-info.java index eab812e6b..7766694b1 100644 --- a/jhove-apps/src/main/java/edu/harvard/hul/ois/jhove/viewer/package-info.java +++ b/jhove-apps/src/main/java/edu/harvard/hul/ois/jhove/viewer/package-info.java @@ -1,6 +1,5 @@ /** - * Contains the classes for the JHOVE GUI. - * These are based on Swing classes and work with a variety of windowing - * environments. + * Contains the classes for the JHOVE GUI. These are based on Swing classes and work with a variety + * of windowing environments. */ -package edu.harvard.hul.ois.jhove.viewer; \ No newline at end of file +package edu.harvard.hul.ois.jhove.viewer; diff --git a/jhove-core/src/main/java/edu/harvard/hul/ois/jhove/AESAudioMetadata.java b/jhove-core/src/main/java/edu/harvard/hul/ois/jhove/AESAudioMetadata.java index d8795178b..90a89bc1f 100644 --- a/jhove-core/src/main/java/edu/harvard/hul/ois/jhove/AESAudioMetadata.java +++ b/jhove-core/src/main/java/edu/harvard/hul/ois/jhove/AESAudioMetadata.java @@ -1,9 +1,10 @@ -/********************************************************************** - * Jhove - JSTOR/Harvard Object Validation Environment - * Copyright 2004-2005 by JSTOR and the President and Fellows of Harvard College +/** + * ******************************************************************** Jhove - JSTOR/Harvard Object + * Validation Environment Copyright 2004-2005 by JSTOR and the President and Fellows of Harvard + * College * - **********************************************************************/ - + *

******************************************************************** + */ package edu.harvard.hul.ois.jhove; import java.util.*; @@ -12,959 +13,866 @@ * Encapsulation of the AES Metadata for Audio documents * * @author Gary McGath - * */ -public class AESAudioMetadata -{ - /****************************************************************** - * PUBLIC CLASS FIELDS. - ******************************************************************/ - - /** Big-endian constant. */ - public static final int BIG_ENDIAN = 0; - - /** Little-endian constant. */ - public static final int LITTLE_ENDIAN = 1; - - /** Analog / digital labels. */ - public static final String [] A_D = { - "ANALOG", "PHYS_DIGITAL", "FILE_DIGITAL" - }; - - /** Values for primary identifier type */ - public static final String - FILE_NAME = "FILE_NAME", - OTHER = "OTHER"; - - /** Constant for an undefined integer value. */ - public static final int NULL = -1; - /** Constant for an undefined floating-point value. */ - public static final double NILL = -1.0; - - /****************************************************************** - * PRIVATE INSTANCE FIELDS. +public class AESAudioMetadata { + /** + * **************************************************************** PUBLIC CLASS FIELDS. + * **************************************************************** + */ + + /** Big-endian constant. */ + public static final int BIG_ENDIAN = 0; + + /** Little-endian constant. */ + public static final int LITTLE_ENDIAN = 1; + + /** Analog / digital labels. */ + public static final String[] A_D = {"ANALOG", "PHYS_DIGITAL", "FILE_DIGITAL"}; + + /** Values for primary identifier type */ + public static final String FILE_NAME = "FILE_NAME", OTHER = "OTHER"; + + /** Constant for an undefined integer value. */ + public static final int NULL = -1; + /** Constant for an undefined floating-point value. */ + public static final double NILL = -1.0; + + /** + * **************************************************************** PRIVATE INSTANCE FIELDS. + * + *

**************************************************************** + */ + + /** Constant value for the SchemaVersion field */ + public static final String SCHEMA_VERSION = "1.02b"; + + /** Constant value for the disposition field */ + private static final String DEFAULT_DISPOSITION = "validation"; + + private String _analogDigitalFlag; + private String _appSpecificData; + private String _audioDataEncoding; + private int _byteOrder; + private String _disposition; + private List _faceList; + private long _firstSampleOffset; + private String _format; + private List _formatList; + private int _numChannels; + private String _primaryIdentifier; + private String _primaryIdentifierType; + private String _primaryIdentifierOtherType; + private String _schemaVersion; + private String _specificationVersion; + private String[] _use; + + /* Most recently added FormatRegion */ + private FormatRegion _curFormatRegion; + /* Most recently added Face */ + private Face _curFace; + + /** + * **************************************************************** CLASS CONSTRUCTOR. + * **************************************************************** + */ + + /** Instantiate a NisoImageMetadata object. */ + public AESAudioMetadata() { + _schemaVersion = SCHEMA_VERSION; + _disposition = DEFAULT_DISPOSITION; + _analogDigitalFlag = null; + _format = null; + _specificationVersion = null; + _audioDataEncoding = null; + _primaryIdentifier = null; + _primaryIdentifierType = null; + _use = null; + + // We add one format region to get started. In practice, + // that one is all we're likely to need. but more can be + // added if necessary. + _formatList = new LinkedList<>(); + _faceList = new LinkedList<>(); + addFormatRegion(); + addFace(); + _numChannels = NULL; + _byteOrder = NULL; + _firstSampleOffset = NULL; + } + + /** + * **************************************************************** PUBLIC STATIC INTERFACES. + * + *

**************************************************************** + */ + /** + * Public interface to the nested FormatRegion object. Instances of this should be created only by + * addFormatRegion, but can be accessed through the public methods of this interface. + */ + public static interface FormatRegion { + /** Returns the bit depth. */ + public int getBitDepth(); + /** + * Returns the bitrate reduction (compression information). This will be an array of seven + * strings (which may be empty, but should never be null) interpreted as follows: * - ******************************************************************/ - - /** Constant value for the SchemaVersion field */ - public static final String SCHEMA_VERSION = "1.02b"; - - /** Constant value for the disposition field */ - private static final String DEFAULT_DISPOSITION = "validation"; - - private String _analogDigitalFlag; - private String _appSpecificData; - private String _audioDataEncoding; - private int _byteOrder; - private String _disposition; - private List _faceList; - private long _firstSampleOffset; - private String _format; - private List _formatList; - private int _numChannels; - private String _primaryIdentifier; - private String _primaryIdentifierType; - private String _primaryIdentifierOtherType; - private String _schemaVersion; - private String _specificationVersion; - private String[] _use; - - /* Most recently added FormatRegion */ - private FormatRegion _curFormatRegion; - /* Most recently added Face */ - private Face _curFace; - - /****************************************************************** - * CLASS CONSTRUCTOR. - ******************************************************************/ - - /** Instantiate a NisoImageMetadata object. + *

*/ - public AESAudioMetadata () - { - _schemaVersion = SCHEMA_VERSION; - _disposition = DEFAULT_DISPOSITION; - _analogDigitalFlag = null; - _format = null; - _specificationVersion = null; - _audioDataEncoding = null; - _primaryIdentifier = null; - _primaryIdentifierType = null; - _use = null; - - // We add one format region to get started. In practice, - // that one is all we're likely to need. but more can be - // added if necessary. - _formatList = new LinkedList<> (); - _faceList = new LinkedList<> (); - addFormatRegion (); - addFace (); - _numChannels = NULL; - _byteOrder = NULL; - _firstSampleOffset = NULL; - } - - /****************************************************************** - * PUBLIC STATIC INTERFACES. - * - ******************************************************************/ + public String[] getBitrateReduction(); + /** Returns the sample rate. */ + public double getSampleRate(); + /** Returns the word size. */ + int getWordSize(); + /** Returns true if the region is empty. */ + public boolean isEmpty(); + /** Sets the bit depth value. */ + public void setBitDepth(int bitDepth); + /** Sets the bitrate reduction information to null (no compression). */ + public void clearBitrateReduction(); + /** Sets the bitrate reduction (aka compression type). */ + public void setBitrateReduction( + String codecName, + String codecNameVersion, + String codecCreatorApplication, + String codecCreatorApplicationVersion, + String codecQuality, + String dataRate, + String dataRateMode); + /** Sets the sample rate. */ + public void setSampleRate(double sampleRate); + /** Sets the word size. */ + public void setWordSize(int wordSize); + } + + /** + * Public interface to the nested TimeDesc object. Instances of this should be created only by + * appropriate methods, but can be accessed through the public methods of this interface. + */ + public static interface TimeDesc { + /** Returns the hours component. */ + public long getHours(); + /** Returns the minutes component. */ + public long getMinutes(); + /** Returns the seconds component. */ + public long getSeconds(); /** - * Public interface to the nested FormatRegion object. Instances - * of this should be created only by addFormatRegion, but can be - * accessed through the public methods of this interface. + * Returns the frames component of the fraction of a second. We always consider frames to be + * thirtieths of a second. */ - public static interface FormatRegion { - /** Returns the bit depth. */ - public int getBitDepth (); - /** Returns the bitrate reduction (compression information). - * This will be an array of seven strings (which may be - * empty, but should never be null) interpreted as follows: - * - */ - public String[] getBitrateReduction (); - /** Returns the sample rate. */ - public double getSampleRate (); - /** Returns the word size. */ - int getWordSize (); - /** Returns true if the region is empty. */ - public boolean isEmpty (); - /** Sets the bit depth value. */ - public void setBitDepth (int bitDepth); - /** Sets the bitrate reduction information to null (no compression). */ - public void clearBitrateReduction (); - /** Sets the bitrate reduction (aka compression type). */ - public void setBitrateReduction (String codecName, - String codecNameVersion, - String codecCreatorApplication, - String codecCreatorApplicationVersion, - String codecQuality, - String dataRate, - String dataRateMode); - /** Sets the sample rate. */ - public void setSampleRate (double sampleRate); - /** Sets the word size. */ - public void setWordSize (int wordSize); - } + public long getFrames(); + /** Returns the samples remaining after the frames part of the fractional second. */ + public long getSamples(); + /** Returns the sample rate on which the samples remainder is based. */ + public double getSampleRate(); + } + + /** + * Public interface to the nested Face object. Instances of this should be created only by + * appropriate methods, but can be accessed through the public methods of this interface. + */ + public static interface Face { + /** Returns an indexed FaceRegion. */ + public FaceRegion getFaceRegion(int i); + + /** Adds a FaceRegion. This may be called repeatedly to add multiple FaceRegions. */ + public void addFaceRegion(); + + /** Returns the starting time. */ + public TimeDesc getStartTime(); + + /** Returns the duration. */ + public TimeDesc getDuration(); + + /** Returns the direction. */ + public String getDirection(); + + /** Sets the starting time. This will be converted into a TimeDesc. */ + public void setStartTime(long samples); + + /** Sets the duration. This will be converted into a TimeDesc. */ + public void setDuration(long samples); /** - * Public interface to the nested TimeDesc object. Instances - * of this should be created only by appropriate methods, but can be - * accessed through the public methods of this interface. - */ - public static interface TimeDesc { - /** Returns the hours component. */ - public long getHours (); - /** Returns the minutes component. */ - public long getMinutes (); - /** Returns the seconds component. */ - public long getSeconds (); - /** Returns the frames component of the fraction of a second. - * We always consider frames to be thirtieths of a second. */ - public long getFrames (); - /** Returns the samples remaining after the frames part of - * the fractional second. */ - public long getSamples (); - /** Returns the sample rate on which the samples remainder - * is based. */ - public double getSampleRate (); - } - - /** Public interface to the nested Face object. Instances - * of this should be created only by appropriate methods, but can be - * accessed through the public methods of this interface. */ - public static interface Face { - /** Returns an indexed FaceRegion. */ - public FaceRegion getFaceRegion (int i); - - /** Adds a FaceRegion. This may be called repeatedly to - * add multiple FaceRegions. */ - public void addFaceRegion (); - - /** Returns the starting time. */ - public TimeDesc getStartTime (); - - /** Returns the duration. */ - public TimeDesc getDuration (); - - /** Returns the direction. */ - public String getDirection (); - - /** Sets the starting time. This will be converted - * into a TimeDesc. */ - public void setStartTime (long samples); - - /** Sets the duration. This will be converted - * into a TimeDesc. */ - public void setDuration (long samples); - - /** Sets the direction. This must be one of the - * directionTypes. FORWARD is recommended for most - * or all cases. - */ - public void setDirection (String direction); - - /* End of interface Face */ - } - - /** Public interface to the nested FaceRegion object. Instances - * of this should be created only by appropriate methods, but can be - * accessed through the public methods of this interface. */ - public static interface FaceRegion { - /** Returns the starting time. */ - public TimeDesc getStartTime (); - - /** Returns the duration. */ - public TimeDesc getDuration (); - - /** Returns the channel map locations. The array length must - * equal the number of channels. */ - public String[] getMapLocations (); - - /** Sets the starting time. */ - public void setStartTime (long samples); - - /** Sets the duration. */ - public void setDuration (long samples); - - /** Sets the channel map locations. The array length must - * equal the number of channels. */ - public void setMapLocations (String[] locations); - - /* End of interface FaceRegion */ - } - - /****************************************************************** - * STATIC MEMBER CLASSES. - * - ******************************************************************/ - /** The implementation of the FormatRegion interface. The combination - * of a public interface and a private implementation is suggested - * in _Java in a Nutshell_. - */ - class FormatRegionImpl implements FormatRegion { - - private int _bitDepth; - private double _sampleRate; - private int _wordSize; - private String[] _bitrateReduction; - - public FormatRegionImpl () { - _bitDepth = NULL; - _sampleRate = NILL; - _wordSize = NULL; - _bitrateReduction = null; - } - - /** Returns bit depth. */ - @Override - public int getBitDepth () - { - return _bitDepth; - } - - /** Returns the bitrate reduction (compression information). - * This will be an array of seven strings (which may be - * empty but not null) interpreted respectively as follows: - * - */ - @Override - public String[] getBitrateReduction () - { - return _bitrateReduction; - } - - /** Returns sample rate. */ - @Override - public double getSampleRate () - { - return _sampleRate; - } - - /** Returns word size. */ - @Override - public int getWordSize () - { - return _wordSize; - } - - /** Returns true if the FormatRegion contains only - * default values. */ - @Override - public boolean isEmpty () - { - return _bitDepth == NULL && - _sampleRate == NILL && - _wordSize == NULL; - } - - /** Sets bit depth. */ - @Override - public void setBitDepth (int bitDepth) - { - _bitDepth = bitDepth; - } - - /** Sets the bitrate reduction information to null (no compression). */ - @Override - public void clearBitrateReduction () - { - _bitrateReduction = null; - } - - /** Sets the bitrate reduction (compression type). */ - @Override - public void setBitrateReduction (String codecName, - String codecNameVersion, - String codecCreatorApplication, - String codecCreatorApplicationVersion, - String codecQuality, - String dataRate, - String dataRateMode) - { - _bitrateReduction = new String[7]; - _bitrateReduction[0] = codecName; - _bitrateReduction[1] = codecNameVersion; - _bitrateReduction[2] = codecCreatorApplication; - _bitrateReduction[3] = codecCreatorApplicationVersion; - _bitrateReduction[4] = codecQuality; - _bitrateReduction[5] = dataRate; - _bitrateReduction[6] = dataRateMode; - } - - /** Sets sample rate. */ - @Override - public void setSampleRate (double sampleRate) - { - _sampleRate = sampleRate; - } - - - /** Sets word size. */ - @Override - public void setWordSize (int wordSize) - { - _wordSize = wordSize; - } - - /* End of FormatRegionImpl */ - } - - /** The implementation of the TimeDesc interface. The combination - * of a public interface and a private implementation is suggested - * in _Java in a Nutshell_. - */ - class TimeDescImpl implements TimeDesc - { - private long _hours; - private long _minutes; - private long _seconds; - private long _frames; - private long _samples; - private double _sampleRate; - private long _frameCount; - - /* Constructor rewritten to avoid rounding errors when converting to - * TCF. Now uses integer remainder math instead of floating point. - * Changed the base unit from a double representing seconds to a long - * representing samples. Changed all existing calls (that I could find) - * to this method to accomodate this change. - * - * @author David Ackerman - */ - public TimeDescImpl (long samples) - { - long _sample_count = samples; - _frameCount = 30; - _sampleRate = _curFormatRegion.getSampleRate (); - - /* It seems that this method is initially called before a valid - * sample rate has been established, causing a divide by zero - * error. - */ - if (_sampleRate < 0) { - _sampleRate = 44100.0; //reasonable default value - } - - long sample_in_1_frame = (long)(_sampleRate/_frameCount); - long sample_in_1_second = sample_in_1_frame * _frameCount; - long sample_in_1_minute = sample_in_1_frame * _frameCount * 60; - long sample_in_1_hour = sample_in_1_frame * _frameCount * 60 * 60; - long sample_in_1_day = sample_in_1_frame * _frameCount * 60 * 60 * 24; - - // BWF allows for a negative timestamp but tcf does not, so adjust - // time accordingly - // this might be a good place to report a warning during validation - if (_sample_count < 0) { - _sample_count += sample_in_1_day; - _sample_count = (_sample_count % sample_in_1_day); - } - - _hours = _sample_count / sample_in_1_hour; - _sample_count -= _hours * sample_in_1_hour; - _minutes = _sample_count / sample_in_1_minute; - _sample_count -= _minutes * sample_in_1_minute; - _seconds = _sample_count / sample_in_1_second; - _sample_count -= _seconds * sample_in_1_second; - _frames = _sample_count / sample_in_1_frame; - _sample_count -= _frames * sample_in_1_frame; - _samples = _sample_count; - - /* At present TCF does not have the ability to handle time stamps - * > midnight. Industry practice is to roll the clock forward to - * zero or back to 23:59:59:29... when crossing this boundary - * condition. - */ - _hours = _hours % 24; - } - - /** Returns the hours component. */ - @Override - public long getHours () { - return _hours; - } - - /** Returns the minutes component. */ - @Override - public long getMinutes () { - return _minutes; - } - - /** Returns the seconds component. */ - @Override - public long getSeconds () { - return _seconds; - } - - /** Returns the frames component of the fraction of a second. - * We always consider frames to be thirtieths of a second. */ - @Override - public long getFrames () { - return _frames; - } - - /** Returns the samples remaining after the frames part of - * the fractional second. */ - @Override - public long getSamples () { - return _samples; - } - - /** Returns the sample rate on which the samples remainder - * is based. */ - @Override - public double getSampleRate () { - return _sampleRate; - } - } /* End of TimeDescImpl */ - - /** The implementation of the Face interface. The combination - * of a public interface and a private implementation is suggested - * in _Java in a Nutshell_. + * Sets the direction. This must be one of the directionTypes. FORWARD is recommended for most + * or all cases. */ - class FaceImpl implements Face { - private List _regionList; - private TimeDesc _startTime; - private TimeDesc _duration; - private String _direction; - - - /** Constructor. Initially the duration is set - * to null, indicating unknown value. */ - public FaceImpl () - { - _regionList = new ArrayList<>(); - _startTime = new TimeDescImpl (0); - _duration = null; - } - - - /** Returns an indexed FaceRegion. */ - @Override - public FaceRegion getFaceRegion (int i) { - return _regionList.get (i); - } - - /** Adds a FaceRegion. This may be called repeatedly to - * add multiple FaceRegions. */ - @Override - public void addFaceRegion () { - _regionList.add (new FaceRegionImpl ()); - } - - /** Returns the starting time. Will be zero if not - * explicitly specified. */ - @Override - public TimeDesc getStartTime () { - return _startTime; - } - - /** Returns the duration. May be null if the duration - * is unspecified. */ - @Override - public TimeDesc getDuration () { - return _duration; - } - - /** Returns the direction. */ - @Override - public String getDirection () - { - return _direction; - } - - /** Sets the starting time. This will be converted - * into a TimeDesc. */ - @Override - public void setStartTime (long samples) - { - _startTime = new TimeDescImpl (samples); - } - - /** Sets the duration. This will be converted - * into a TimeDesc. */ - @Override - public void setDuration (long samples) - { - _duration = new TimeDescImpl (samples); - } - - /** Sets the direction. This must be one of the - * directionTypes. FORWARD is recommended for most - * or all cases. - */ - @Override - public void setDirection (String direction) - { - _direction = direction; - } - - /* End of FaceImpl */ - } - - - /** The implementation of the Face interface. The combination - * of a public interface and a private implementation is suggested - * in _Java in a Nutshell_. - */ - class FaceRegionImpl implements FaceRegion { - private TimeDesc _startTime; - private TimeDesc _duration; - private String[] _mapLocations; - - public FaceRegionImpl () - { - _startTime = new TimeDescImpl (0); - _duration = null; - } - - /** Returns the starting time. */ - @Override - public TimeDesc getStartTime () { - return _startTime; - } - - /** Returns the duration. */ - @Override - public TimeDesc getDuration () { - return _duration; - } - - /** Returns the channel map locations. The array length - * will equal the number of channels. */ - @Override - public String[] getMapLocations () - { - return _mapLocations; - } - - /** Sets the duration. This will be converted - * into a TimeDesc. */ - @Override - public void setStartTime (long samples) { - _startTime = new TimeDescImpl (samples); - } - - /** Sets the duration. This will be converted - * into a TimeDesc. */ - @Override - public void setDuration (long samples) - { - _duration = new TimeDescImpl (samples); - } - - /** Sets the channel map locations. The array length must - * equal the number of channels. */ - @Override - public void setMapLocations (String[] locations) - { - _mapLocations = locations; - } - /* End of FaceRegionImpl */ - } - - /* End of inner classes */ - - /****************************************************************** - * PUBLIC INSTANCE METHODS. - * - * Accessor methods. - ******************************************************************/ - - /** Returns analog/digital flag. Value will always be - * "FILE_DIGITAL" in practice. */ - public String getAnalogDigitalFlag () - { - return _analogDigitalFlag; - } - - /** Returns application-specific data. We assume this is - * representable in String format. - */ - public String getAppSpecificData () - { - return _appSpecificData; - } - - /** Returns audio data encoding. */ - public String getAudioDataEncoding () - { - return _audioDataEncoding; - } - - /** Returns the bitrate reduction (compression information). - * This will be an array of seven strings (which may be - * empty, but should never be null) interpreted as follows: - * - */ - public String[] getBitrateReduction () - { - return _curFormatRegion.getBitrateReduction(); - } - - /* Returns the sample rate. */ - public double getSampleRate () - { - return _curFormatRegion.getSampleRate (); - } - - /** Return the byte order: 0 = big-endian; 1 = little-endian. */ - public int getByteOrder () - { - return _byteOrder; - } - - /** Returns disposition. */ - public String getDisposition () - { - return _disposition; - } - - /** Gets the list of Faces. Normally there will be only one face - * in a digital file. */ - public List getFaceList () - { - return _faceList; - } - - /** Return the offset of the first byte of sample data. */ - public long getFirstSampleOffset () - { - return _firstSampleOffset; - } - - /** Returns format name. */ - public String getFormat () - { - return _format; - } - - /** Gets the list of Format Regions. Since one is created - * automatically on initialization, it's possible that the - * list will contain a Format Region with only default values. - * This should be checked with isEmpty (). - */ - public List getFormatList () - { - return _formatList; - } - - /** Returns the names of the map locations. - * The returned - * value is an array whose length equals the number of - * channels and whose elements correspond to channels 0, 1, - * etc. - */ - public String[] getMapLocations() { - return _curFace.getFaceRegion(0).getMapLocations(); - } - - /** Returns number of channels. */ - public int getNumChannels () - { - return _numChannels; - } + public void setDirection(String direction); + + /* End of interface Face */ + } + + /** + * Public interface to the nested FaceRegion object. Instances of this should be created only by + * appropriate methods, but can be accessed through the public methods of this interface. + */ + public static interface FaceRegion { + /** Returns the starting time. */ + public TimeDesc getStartTime(); + + /** Returns the duration. */ + public TimeDesc getDuration(); + + /** Returns the channel map locations. The array length must equal the number of channels. */ + public String[] getMapLocations(); + + /** Sets the starting time. */ + public void setStartTime(long samples); + + /** Sets the duration. */ + public void setDuration(long samples); + + /** Sets the channel map locations. The array length must equal the number of channels. */ + public void setMapLocations(String[] locations); + + /* End of interface FaceRegion */ + } + + /** + * **************************************************************** STATIC MEMBER CLASSES. + * + *

**************************************************************** + */ + /** + * The implementation of the FormatRegion interface. The combination of a public interface and a + * private implementation is suggested in _Java in a Nutshell_. + */ + class FormatRegionImpl implements FormatRegion { - /** Returns primary identifier. */ - public String getPrimaryIdentifier () - { - return _primaryIdentifier; + private int _bitDepth; + private double _sampleRate; + private int _wordSize; + private String[] _bitrateReduction; + + public FormatRegionImpl() { + _bitDepth = NULL; + _sampleRate = NILL; + _wordSize = NULL; + _bitrateReduction = null; } - /** Returns primary identifier type. */ - public String getPrimaryIdentifierType () - { - return _primaryIdentifierType; + /** Returns bit depth. */ + @Override + public int getBitDepth() { + return _bitDepth; } - /** Returns schema version. */ - public String getSchemaVersion () - { - return _schemaVersion; + /** + * Returns the bitrate reduction (compression information). This will be an array of seven + * strings (which may be empty but not null) interpreted respectively as follows: + * + *

    + *
  • 0: codecName + *
  • 1: codecNameVersion + *
  • 2: codecCreatorApplication + *
  • 3: codecCreatorApplicationVersion + *
  • 4: codecQuality + *
  • 5: dataRate + *
  • 6: dataRateMode + *
+ */ + @Override + public String[] getBitrateReduction() { + return _bitrateReduction; } - - /** Returns specification version of the document format. */ - public String getSpecificationVersion () - { - return _specificationVersion; + + /** Returns sample rate. */ + @Override + public double getSampleRate() { + return _sampleRate; } - - /** Returns the use (role of the document). - * The value returned is an array of two strings, - * the useType and the otherType. */ - public String[] getUse () - { - return _use; + + /** Returns word size. */ + @Override + public int getWordSize() { + return _wordSize; } - - + /** Returns true if the FormatRegion contains only default values. */ + @Override + public boolean isEmpty() { + return _bitDepth == NULL && _sampleRate == NILL && _wordSize == NULL; + } + /** Sets bit depth. */ + @Override + public void setBitDepth(int bitDepth) { + _bitDepth = bitDepth; + } - /****************************************************************** - * Mutator methods. - ******************************************************************/ - - /** Sets the analog/digital flag. The value set should always - * be "FILE_DIGITAL". */ - public void setAnalogDigitalFlag (String flagType) - { - _analogDigitalFlag = flagType; + /** Sets the bitrate reduction information to null (no compression). */ + @Override + public void clearBitrateReduction() { + _bitrateReduction = null; } /** Sets the bitrate reduction (compression type). */ - public void setBitrateReduction (String codecName, - String codecNameVersion, - String codecCreatorApplication, - String codecCreatorApplicationVersion, - String codecQuality, - String dataRate, - String dataRateMode) - { - _curFormatRegion.setBitrateReduction (codecName, - codecNameVersion, codecCreatorApplication, - codecCreatorApplicationVersion, - codecQuality, dataRate, dataRateMode); - } - - /** Set the bitrate reduction information to null (no compression). */ - public void clearBitrateReduction () - { - _curFormatRegion.clearBitrateReduction (); - } - - /** Sets the byte order. - * @param order Byte order: 0 = big-endian, 1 = little-endian + @Override + public void setBitrateReduction( + String codecName, + String codecNameVersion, + String codecCreatorApplication, + String codecCreatorApplicationVersion, + String codecQuality, + String dataRate, + String dataRateMode) { + _bitrateReduction = new String[7]; + _bitrateReduction[0] = codecName; + _bitrateReduction[1] = codecNameVersion; + _bitrateReduction[2] = codecCreatorApplication; + _bitrateReduction[3] = codecCreatorApplicationVersion; + _bitrateReduction[4] = codecQuality; + _bitrateReduction[5] = dataRate; + _bitrateReduction[6] = dataRateMode; + } + + /** Sets sample rate. */ + @Override + public void setSampleRate(double sampleRate) { + _sampleRate = sampleRate; + } + + /** Sets word size. */ + @Override + public void setWordSize(int wordSize) { + _wordSize = wordSize; + } + + /* End of FormatRegionImpl */ + } + + /** + * The implementation of the TimeDesc interface. The combination of a public interface and a + * private implementation is suggested in _Java in a Nutshell_. + */ + class TimeDescImpl implements TimeDesc { + private long _hours; + private long _minutes; + private long _seconds; + private long _frames; + private long _samples; + private double _sampleRate; + private long _frameCount; + + /* Constructor rewritten to avoid rounding errors when converting to + * TCF. Now uses integer remainder math instead of floating point. + * Changed the base unit from a double representing seconds to a long + * representing samples. Changed all existing calls (that I could find) + * to this method to accomodate this change. + * + * @author David Ackerman */ - public void setByteOrder (int order) - { - _byteOrder = order; + public TimeDescImpl(long samples) { + long _sample_count = samples; + _frameCount = 30; + _sampleRate = _curFormatRegion.getSampleRate(); + + /* It seems that this method is initially called before a valid + * sample rate has been established, causing a divide by zero + * error. + */ + if (_sampleRate < 0) { + _sampleRate = 44100.0; // reasonable default value + } + + long sample_in_1_frame = (long) (_sampleRate / _frameCount); + long sample_in_1_second = sample_in_1_frame * _frameCount; + long sample_in_1_minute = sample_in_1_frame * _frameCount * 60; + long sample_in_1_hour = sample_in_1_frame * _frameCount * 60 * 60; + long sample_in_1_day = sample_in_1_frame * _frameCount * 60 * 60 * 24; + + // BWF allows for a negative timestamp but tcf does not, so adjust + // time accordingly + // this might be a good place to report a warning during validation + if (_sample_count < 0) { + _sample_count += sample_in_1_day; + _sample_count = (_sample_count % sample_in_1_day); + } + + _hours = _sample_count / sample_in_1_hour; + _sample_count -= _hours * sample_in_1_hour; + _minutes = _sample_count / sample_in_1_minute; + _sample_count -= _minutes * sample_in_1_minute; + _seconds = _sample_count / sample_in_1_second; + _sample_count -= _seconds * sample_in_1_second; + _frames = _sample_count / sample_in_1_frame; + _sample_count -= _frames * sample_in_1_frame; + _samples = _sample_count; + + /* At present TCF does not have the ability to handle time stamps + * > midnight. Industry practice is to roll the clock forward to + * zero or back to 23:59:59:29... when crossing this boundary + * condition. + */ + _hours = _hours % 24; + } + + /** Returns the hours component. */ + @Override + public long getHours() { + return _hours; + } + + /** Returns the minutes component. */ + @Override + public long getMinutes() { + return _minutes; + } + + /** Returns the seconds component. */ + @Override + public long getSeconds() { + return _seconds; } - /** Sets the byte order. - */ - public void setByteOrder (String order) - { - if (order.substring (0, 3).toLowerCase ().equals ("big")) { - _byteOrder = BIG_ENDIAN; - } - else if (order.substring (0, 6).toLowerCase ().equals ("little")) { - _byteOrder = LITTLE_ENDIAN; - } - } - - /** Sets the audio data encoding. */ - public void setAudioDataEncoding (String audioDataEncoding) - { - _audioDataEncoding = audioDataEncoding; - } - - /** Set the application-specific data. For present purposes, - * we assume this is representable as a text string. */ - public void setAppSpecificData (String data) - { - _appSpecificData = data; - } - - /** Sets the bit depth. */ - public void setBitDepth (int bitDepth) - { - _curFormatRegion.setBitDepth (bitDepth); - } - - /** Sets the disposition. */ - public void setDisposition (String disposition) - { - _disposition = disposition; - } - - /** Sets the direction. - * This must be one of the values - * FORWARD, REVERSE, A_WIND, B_WIND, C_WIND, D_WIND, - * FRONT, BACK. FORWARD may be the only one that - * makes sense for digital formats. + /** + * Returns the frames component of the fraction of a second. We always consider frames to be + * thirtieths of a second. */ - public void setDirection (String direction) - { - _curFace.setDirection (direction); + @Override + public long getFrames() { + return _frames; } - - /** Sets the duration in samples. - * This affects the current face and its first FaceRegion. - */ - public void setDuration (long duration) - { - _curFace.setDuration (duration); - _curFace.getFaceRegion(0).setDuration (duration); + + /** Returns the samples remaining after the frames part of the fractional second. */ + @Override + public long getSamples() { + return _samples; } - /** Sets the offset of the first byte of sample data. */ - public void setFirstSampleOffset (long offset) - { - _firstSampleOffset = offset; + /** Returns the sample rate on which the samples remainder is based. */ + @Override + public double getSampleRate() { + return _sampleRate; } + } /* End of TimeDescImpl */ - /** Sets the format name. */ - public void setFormat (String format) - { - _format = format; + /** + * The implementation of the Face interface. The combination of a public interface and a private + * implementation is suggested in _Java in a Nutshell_. + */ + class FaceImpl implements Face { + private List _regionList; + private TimeDesc _startTime; + private TimeDesc _duration; + private String _direction; + + /** Constructor. Initially the duration is set to null, indicating unknown value. */ + public FaceImpl() { + _regionList = new ArrayList<>(); + _startTime = new TimeDescImpl(0); + _duration = null; } - - /** Sets the array of channel map locations. The length - * of the array must equal the number of channels. */ - public void setMapLocations (String[] locations) { - _curFace.getFaceRegion(0).setMapLocations (locations); + + /** Returns an indexed FaceRegion. */ + @Override + public FaceRegion getFaceRegion(int i) { + return _regionList.get(i); } - /** Sets the number of channels. */ - public void setNumChannels (int numChannels) - { - _numChannels = numChannels; + /** Adds a FaceRegion. This may be called repeatedly to add multiple FaceRegions. */ + @Override + public void addFaceRegion() { + _regionList.add(new FaceRegionImpl()); } - /** Sets the primary identifier. */ - public void setPrimaryIdentifier (String primaryIdentifier) - { - _primaryIdentifier = primaryIdentifier; + /** Returns the starting time. Will be zero if not explicitly specified. */ + @Override + public TimeDesc getStartTime() { + return _startTime; } - - /** Sets the primary identifier type. If the primary identifier - * type is OTHER, use setOtherPrimaryIdentifierType instead. - */ - public void setPrimaryIdentifierType (String primaryIdentifierType) - { - _primaryIdentifierType = primaryIdentifierType; + + /** Returns the duration. May be null if the duration is unspecified. */ + @Override + public TimeDesc getDuration() { + return _duration; } - /** Sets the primary identifier type as "OTHER", and - * set the otherType. - */ - public void setOtherPrimaryIdentifierType (String otherType) - { - _primaryIdentifierType = "OTHER"; - _primaryIdentifierOtherType = otherType; + /** Returns the direction. */ + @Override + public String getDirection() { + return _direction; } - - /** Sets the sample rate. */ - public void setSampleRate (double sampleRate) - { - _curFormatRegion.setSampleRate (sampleRate); + + /** Sets the starting time. This will be converted into a TimeDesc. */ + @Override + public void setStartTime(long samples) { + _startTime = new TimeDescImpl(samples); } - - /** Sets the specification version of the document format.*/ - public void setSpecificationVersion (String specificationVersion) - { - _specificationVersion = specificationVersion; + + /** Sets the duration. This will be converted into a TimeDesc. */ + @Override + public void setDuration(long samples) { + _duration = new TimeDescImpl(samples); } - /** Sets the start time in samples. - * This affects the current face and its first FaceRegion. - */ - public void setStartTime (long samples) - { - _curFace.setStartTime (samples); - _curFace.getFaceRegion(0).setStartTime (samples); - } - - /** Sets the role of the document. Permitted values are - * ORIGINAL_MASTER, PRESERVATION_MASTER, PRODUCTION_MASTER, - * SERVICE, PREVIEW, or OTHER. - * If useType is "OTHER", then otherType - * is significant. Since OTHER is the only meaningful - * value for a digital document, the code assumes this will always - * be the case and uses otherType. */ - public void setUse (String useType, String otherType) - { - _use = new String[] {useType, otherType}; - } - - /** Sets the word size. */ - public void setWordSize (int wordSize) - { - _curFormatRegion.setWordSize (wordSize); - } - - /** Adds a FormatRegion object to a FormatSize list. - * The most recently added FormatRegion object will - * be filled in by setBitDepth, setSampleRate, and - * setWordSize. + /** + * Sets the direction. This must be one of the directionTypes. FORWARD is recommended for most + * or all cases. */ - public void addFormatRegion () - { - _curFormatRegion = new FormatRegionImpl (); - _formatList.add (_curFormatRegion); + @Override + public void setDirection(String direction) { + _direction = direction; } - - /** Adds a Face. - */ - public void addFace () - { - _curFace = new FaceImpl (); - _faceList.add (_curFace); - _curFace.addFaceRegion(); + + /* End of FaceImpl */ + } + + /** + * The implementation of the Face interface. The combination of a public interface and a private + * implementation is suggested in _Java in a Nutshell_. + */ + class FaceRegionImpl implements FaceRegion { + private TimeDesc _startTime; + private TimeDesc _duration; + private String[] _mapLocations; + + public FaceRegionImpl() { + _startTime = new TimeDescImpl(0); + _duration = null; + } + + /** Returns the starting time. */ + @Override + public TimeDesc getStartTime() { + return _startTime; } - + + /** Returns the duration. */ + @Override + public TimeDesc getDuration() { + return _duration; + } + + /** Returns the channel map locations. The array length will equal the number of channels. */ + @Override + public String[] getMapLocations() { + return _mapLocations; + } + + /** Sets the duration. This will be converted into a TimeDesc. */ + @Override + public void setStartTime(long samples) { + _startTime = new TimeDescImpl(samples); + } + + /** Sets the duration. This will be converted into a TimeDesc. */ + @Override + public void setDuration(long samples) { + _duration = new TimeDescImpl(samples); + } + + /** Sets the channel map locations. The array length must equal the number of channels. */ + @Override + public void setMapLocations(String[] locations) { + _mapLocations = locations; + } + /* End of FaceRegionImpl */ + } + + /* End of inner classes */ + + /** + * **************************************************************** PUBLIC INSTANCE METHODS. + * + *

Accessor methods. **************************************************************** + */ + + /** Returns analog/digital flag. Value will always be "FILE_DIGITAL" in practice. */ + public String getAnalogDigitalFlag() { + return _analogDigitalFlag; + } + + /** Returns application-specific data. We assume this is representable in String format. */ + public String getAppSpecificData() { + return _appSpecificData; + } + + /** Returns audio data encoding. */ + public String getAudioDataEncoding() { + return _audioDataEncoding; + } + + /** + * Returns the bitrate reduction (compression information). This will be an array of seven strings + * (which may be empty, but should never be null) interpreted as follows: + * + *

    + *
  • 0: codecName + *
  • 1: codecNameVersion + *
  • 2: codecCreatorApplication + *
  • 3: codecCreatorApplicationVersion + *
  • 4: codecQuality + *
  • 5: dataRate + *
  • 6: dataRateMode + *
+ */ + public String[] getBitrateReduction() { + return _curFormatRegion.getBitrateReduction(); + } + + /* Returns the sample rate. */ + public double getSampleRate() { + return _curFormatRegion.getSampleRate(); + } + + /** Return the byte order: 0 = big-endian; 1 = little-endian. */ + public int getByteOrder() { + return _byteOrder; + } + + /** Returns disposition. */ + public String getDisposition() { + return _disposition; + } + + /** Gets the list of Faces. Normally there will be only one face in a digital file. */ + public List getFaceList() { + return _faceList; + } + + /** Return the offset of the first byte of sample data. */ + public long getFirstSampleOffset() { + return _firstSampleOffset; + } + + /** Returns format name. */ + public String getFormat() { + return _format; + } + + /** + * Gets the list of Format Regions. Since one is created automatically on initialization, it's + * possible that the list will contain a Format Region with only default values. This should be + * checked with isEmpty (). + */ + public List getFormatList() { + return _formatList; + } + + /** + * Returns the names of the map locations. The returned value is an array whose length equals the + * number of channels and whose elements correspond to channels 0, 1, etc. + */ + public String[] getMapLocations() { + return _curFace.getFaceRegion(0).getMapLocations(); + } + + /** Returns number of channels. */ + public int getNumChannels() { + return _numChannels; + } + + /** Returns primary identifier. */ + public String getPrimaryIdentifier() { + return _primaryIdentifier; + } + + /** Returns primary identifier type. */ + public String getPrimaryIdentifierType() { + return _primaryIdentifierType; + } + + /** Returns schema version. */ + public String getSchemaVersion() { + return _schemaVersion; + } + + /** Returns specification version of the document format. */ + public String getSpecificationVersion() { + return _specificationVersion; + } + + /** + * Returns the use (role of the document). The value returned is an array of two strings, the + * useType and the otherType. + */ + public String[] getUse() { + return _use; + } + + /** + * **************************************************************** Mutator methods. + * **************************************************************** + */ + + /** Sets the analog/digital flag. The value set should always be "FILE_DIGITAL". */ + public void setAnalogDigitalFlag(String flagType) { + _analogDigitalFlag = flagType; + } + + /** Sets the bitrate reduction (compression type). */ + public void setBitrateReduction( + String codecName, + String codecNameVersion, + String codecCreatorApplication, + String codecCreatorApplicationVersion, + String codecQuality, + String dataRate, + String dataRateMode) { + _curFormatRegion.setBitrateReduction( + codecName, + codecNameVersion, + codecCreatorApplication, + codecCreatorApplicationVersion, + codecQuality, + dataRate, + dataRateMode); + } + + /** Set the bitrate reduction information to null (no compression). */ + public void clearBitrateReduction() { + _curFormatRegion.clearBitrateReduction(); + } + + /** + * Sets the byte order. + * + * @param order Byte order: 0 = big-endian, 1 = little-endian + */ + public void setByteOrder(int order) { + _byteOrder = order; + } + + /** Sets the byte order. */ + public void setByteOrder(String order) { + if (order.substring(0, 3).toLowerCase().equals("big")) { + _byteOrder = BIG_ENDIAN; + } else if (order.substring(0, 6).toLowerCase().equals("little")) { + _byteOrder = LITTLE_ENDIAN; + } + } + + /** Sets the audio data encoding. */ + public void setAudioDataEncoding(String audioDataEncoding) { + _audioDataEncoding = audioDataEncoding; + } + + /** + * Set the application-specific data. For present purposes, we assume this is representable as a + * text string. + */ + public void setAppSpecificData(String data) { + _appSpecificData = data; + } + + /** Sets the bit depth. */ + public void setBitDepth(int bitDepth) { + _curFormatRegion.setBitDepth(bitDepth); + } + + /** Sets the disposition. */ + public void setDisposition(String disposition) { + _disposition = disposition; + } + + /** + * Sets the direction. This must be one of the values FORWARD, REVERSE, A_WIND, B_WIND, C_WIND, + * D_WIND, FRONT, BACK. FORWARD may be the only one that makes sense for digital formats. + */ + public void setDirection(String direction) { + _curFace.setDirection(direction); + } + + /** Sets the duration in samples. This affects the current face and its first FaceRegion. */ + public void setDuration(long duration) { + _curFace.setDuration(duration); + _curFace.getFaceRegion(0).setDuration(duration); + } + + /** Sets the offset of the first byte of sample data. */ + public void setFirstSampleOffset(long offset) { + _firstSampleOffset = offset; + } + + /** Sets the format name. */ + public void setFormat(String format) { + _format = format; + } + + /** + * Sets the array of channel map locations. The length of the array must equal the number of + * channels. + */ + public void setMapLocations(String[] locations) { + _curFace.getFaceRegion(0).setMapLocations(locations); + } + + /** Sets the number of channels. */ + public void setNumChannels(int numChannels) { + _numChannels = numChannels; + } + + /** Sets the primary identifier. */ + public void setPrimaryIdentifier(String primaryIdentifier) { + _primaryIdentifier = primaryIdentifier; + } + + /** + * Sets the primary identifier type. If the primary identifier type is OTHER, use + * setOtherPrimaryIdentifierType instead. + */ + public void setPrimaryIdentifierType(String primaryIdentifierType) { + _primaryIdentifierType = primaryIdentifierType; + } + + /** Sets the primary identifier type as "OTHER", and set the otherType. */ + public void setOtherPrimaryIdentifierType(String otherType) { + _primaryIdentifierType = "OTHER"; + _primaryIdentifierOtherType = otherType; + } + + /** Sets the sample rate. */ + public void setSampleRate(double sampleRate) { + _curFormatRegion.setSampleRate(sampleRate); + } + + /** Sets the specification version of the document format. */ + public void setSpecificationVersion(String specificationVersion) { + _specificationVersion = specificationVersion; + } + + /** Sets the start time in samples. This affects the current face and its first FaceRegion. */ + public void setStartTime(long samples) { + _curFace.setStartTime(samples); + _curFace.getFaceRegion(0).setStartTime(samples); + } + + /** + * Sets the role of the document. Permitted values are ORIGINAL_MASTER, PRESERVATION_MASTER, + * PRODUCTION_MASTER, SERVICE, PREVIEW, or OTHER. If useType is "OTHER", then otherType is + * significant. Since OTHER is the only meaningful value for a digital document, the code assumes + * this will always be the case and uses otherType. + */ + public void setUse(String useType, String otherType) { + _use = new String[] {useType, otherType}; + } + + /** Sets the word size. */ + public void setWordSize(int wordSize) { + _curFormatRegion.setWordSize(wordSize); + } + + /** + * Adds a FormatRegion object to a FormatSize list. The most recently added FormatRegion object + * will be filled in by setBitDepth, setSampleRate, and setWordSize. + */ + public void addFormatRegion() { + _curFormatRegion = new FormatRegionImpl(); + _formatList.add(_curFormatRegion); + } + + /** Adds a Face. */ + public void addFace() { + _curFace = new FaceImpl(); + _faceList.add(_curFace); + _curFace.addFaceRegion(); + } } diff --git a/jhove-core/src/main/java/edu/harvard/hul/ois/jhove/Agent.java b/jhove-core/src/main/java/edu/harvard/hul/ois/jhove/Agent.java index 483207ecc..9903c305d 100644 --- a/jhove-core/src/main/java/edu/harvard/hul/ois/jhove/Agent.java +++ b/jhove-core/src/main/java/edu/harvard/hul/ois/jhove/Agent.java @@ -1,322 +1,281 @@ -/********************************************************************** - * Jhove - JSTOR/Harvard Object Validation Environment Copyright 2003 by JSTOR - * and the President and Fellows of Harvard College - **********************************************************************/ - -package edu.harvard.hul.ois.jhove; - /** - * Encapsulates information about agents, either individual persons or corporate - * bodies. + * ******************************************************************** Jhove - JSTOR/Harvard Object + * Validation Environment Copyright 2003 by JSTOR and the President and Fellows of Harvard College + * ******************************************************************** */ +package edu.harvard.hul.ois.jhove; + +/** Encapsulates information about agents, either individual persons or corporate bodies. */ public final class Agent { - private static final Agent HARVARD = new Builder( - "Harvard University Library", AgentType.EDUCATIONAL) + private static final Agent HARVARD = + new Builder("Harvard University Library", AgentType.EDUCATIONAL) + .address( + "Office for Information Systems, " + "90 Mt. Auburn St., " + "Cambridge, MA 02138") + .telephone("+1 (617) 495-3724") + .email("jhove-support@hulmail.harvard.edu") + .build(); + + private static final Agent OPF = + new Builder("Open Preservation Foundation", AgentType.NONPROFIT) + .web("http://openpreservation.org") + .email("jhove@openpreservation.org") + .build(); + + private static final Agent BNF = + new Builder("Bibliothèque nationale de France", AgentType.EDUCATIONAL) + .web("http://www.bnf.fr") + .build(); + + /** + * **************************************************************** PRIVATE INSTANCE FIELDS. + * **************************************************************** + */ + private final String _name; + + private final AgentType _type; + private final String _address; + private final String _telephone; + private final String _fax; + private final String _email; + private final String _web; + private final String _note; + + /** + * **************************************************************** CLASS CONSTRUCTOR. + * **************************************************************** + */ + + /** Creates an Agent given a name and an AgentType. */ + private Agent( + final String name, + final AgentType type, + final String address, + final String telephone, + final String fax, + final String email, + final String web, + final String note) { + _name = name; + _type = type; + this._address = address; + this._telephone = telephone; + this._fax = fax; + this._email = email; + this._web = web; + this._note = note; + } + + /** + * **************************************************************** PUBLIC INSTANCE METHODS. + * **************************************************************** + */ + + /** Returns the value of the address property. */ + public String getAddress() { + return _address; + } + + /** Returns the value of the email property. */ + public String getEmail() { + return _email; + } + + /** Returns the value of the fax property. */ + public String getFax() { + return _fax; + } + + /** Returns the value of the name property. */ + public String getName() { + return _name; + } + + /** Returns the value of the note property. */ + public String getNote() { + return _note; + } + + /** Returns the value of the telephone property. */ + public String getTelephone() { + return _telephone; + } + + /** Returns the value of the type property. */ + public AgentType getType() { + return _type; + } + + /** Returns the value of the web property. */ + public String getWeb() { + return _web; + } + + public static final Agent harvardInstance() { + return HARVARD; + } + + public static final Agent opfInstance() { + return OPF; + } + + public static final Agent bnfInstance() { + return BNF; + } + + /** { @inheritDoc } */ + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((_address == null) ? 0 : _address.hashCode()); + result = prime * result + ((_email == null) ? 0 : _email.hashCode()); + result = prime * result + ((_fax == null) ? 0 : _fax.hashCode()); + result = prime * result + ((_name == null) ? 0 : _name.hashCode()); + result = prime * result + ((_note == null) ? 0 : _note.hashCode()); + result = prime * result + ((_telephone == null) ? 0 : _telephone.hashCode()); + result = prime * result + ((_type == null) ? 0 : _type.hashCode()); + result = prime * result + ((_web == null) ? 0 : _web.hashCode()); + return result; + } + + /** { @inheritDoc } */ + @Override + public boolean equals(Object obj) { + if (this == obj) return true; + if (obj == null) return false; + if (getClass() != obj.getClass()) return false; + Agent other = (Agent) obj; + if (_address == null) { + if (other._address != null) return false; + } else if (!_address.equals(other._address)) return false; + if (_email == null) { + if (other._email != null) return false; + } else if (!_email.equals(other._email)) return false; + if (_fax == null) { + if (other._fax != null) return false; + } else if (!_fax.equals(other._fax)) return false; + if (_name == null) { + if (other._name != null) return false; + } else if (!_name.equals(other._name)) return false; + if (_note == null) { + if (other._note != null) return false; + } else if (!_note.equals(other._note)) return false; + if (_telephone == null) { + if (other._telephone != null) return false; + } else if (!_telephone.equals(other._telephone)) return false; + if (_type == null) { + if (other._type != null) return false; + } else if (!_type.equals(other._type)) return false; + if (_web == null) { + if (other._web != null) return false; + } else if (!_web.equals(other._web)) return false; + return true; + } + + public static final Agent newW3CInstance() { + Builder builder = + new Builder("Word Wide Web Consortium", AgentType.NONPROFIT) .address( - "Office for Information Systems, " + "90 Mt. Auburn St., " - + "Cambridge, MA 02138") - .telephone("+1 (617) 495-3724") - .email("jhove-support@hulmail.harvard.edu").build(); - - private static final Agent OPF = new Builder( - "Open Preservation Foundation", AgentType.NONPROFIT) - .web("http://openpreservation.org") - .email("jhove@openpreservation.org").build(); - - private static final Agent BNF = new Builder( - "Bibliothèque nationale de France", AgentType.EDUCATIONAL) - .web("http://www.bnf.fr").build(); - - /****************************************************************** - * PRIVATE INSTANCE FIELDS. - ******************************************************************/ - - private final String _name; - private final AgentType _type; - private final String _address; - private final String _telephone; - private final String _fax; - private final String _email; - private final String _web; - private final String _note; - - /****************************************************************** - * CLASS CONSTRUCTOR. - ******************************************************************/ - - /** - * Creates an Agent given a name and an AgentType. - */ - private Agent(final String name, final AgentType type, - final String address, final String telephone, final String fax, - final String email, final String web, final String note) { - _name = name; - _type = type; - this._address = address; - this._telephone = telephone; - this._fax = fax; - this._email = email; - this._web = web; - this._note = note; - } - - /****************************************************************** - * PUBLIC INSTANCE METHODS. - ******************************************************************/ - - /** - * Returns the value of the address property. - */ - public String getAddress() { - return _address; - } - - /** - * Returns the value of the email property. - */ - public String getEmail() { - return _email; - } - - /** - * Returns the value of the fax property. - */ - public String getFax() { - return _fax; - } - - /** - * Returns the value of the name property. - */ - public String getName() { - return _name; - } - - /** - * Returns the value of the note property. - */ - public String getNote() { - return _note; - } - - /** - * Returns the value of the telephone property. - */ - public String getTelephone() { - return _telephone; - } - - /** - * Returns the value of the type property. - */ - public AgentType getType() { - return _type; - } - - /** - * Returns the value of the web property. - */ - public String getWeb() { - return _web; - } - - public static final Agent harvardInstance() { - return HARVARD; + "Massachusetts Institute of Technology, " + + "Computer Science and Artificial Intelligence Laboratory, " + + "32 Vassar Street, Room 32-G515, " + + "Cambridge, MA 02139") + .telephone("(617) 253-2613") + .fax("(617) 258-5999") + .web("http://www.w3.org/"); + return builder.build(); + } + + public static final Agent newIsoInstance() { + Builder builder = + new Builder("ISO", AgentType.STANDARD) + .address("1, rue de Varembe, Casa postale 56, " + "CH-1211, Geneva 20, Switzerland") + .telephone("+41 22 749 01 11") + .fax("+41 22 733 34 30") + .email("iso@iso.ch") + .web("http://www.iso.org/"); + return builder.build(); + } + + public static final Agent newAdobeInstance() { + Builder builder = + new Builder("Adobe Systems, Inc.", AgentType.COMMERCIAL) + .address("345 Park Avenue, San Jose, California 95110-2704") + .telephone("+1 (408) 536-6000") + .fax("+1 (408) 537-6000") + .web("http://www.adobe.com/"); + return builder.build(); + } + + @SuppressWarnings("hiding") + public static class Builder { + private String name; + private AgentType type = AgentType.EDUCATIONAL; + private String address; + private String telephone; + private String fax; + private String email; + private String web; + private String note; + + public Builder(final String name, final AgentType type) { + this.name = name; + this.type = type; } - public static final Agent opfInstance() { - return OPF; + /** Sets the value of the address property. */ + public Builder address(final String address) { + this.address = address; + return this; } - public static final Agent bnfInstance() { - return BNF; + /** Sets the value of the email property. */ + public Builder email(final String email) { + this.email = email; + return this; } - /** - * { @inheritDoc } - */ - @Override - public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result - + ((_address == null) ? 0 : _address.hashCode()); - result = prime * result + ((_email == null) ? 0 : _email.hashCode()); - result = prime * result + ((_fax == null) ? 0 : _fax.hashCode()); - result = prime * result + ((_name == null) ? 0 : _name.hashCode()); - result = prime * result + ((_note == null) ? 0 : _note.hashCode()); - result = prime * result - + ((_telephone == null) ? 0 : _telephone.hashCode()); - result = prime * result + ((_type == null) ? 0 : _type.hashCode()); - result = prime * result + ((_web == null) ? 0 : _web.hashCode()); - return result; + /** Sets the value of the fax property. */ + public Builder fax(final String fax) { + this.fax = fax; + return this; } - /** - * { @inheritDoc } - */ - @Override - public boolean equals(Object obj) { - if (this == obj) - return true; - if (obj == null) - return false; - if (getClass() != obj.getClass()) - return false; - Agent other = (Agent) obj; - if (_address == null) { - if (other._address != null) - return false; - } else if (!_address.equals(other._address)) - return false; - if (_email == null) { - if (other._email != null) - return false; - } else if (!_email.equals(other._email)) - return false; - if (_fax == null) { - if (other._fax != null) - return false; - } else if (!_fax.equals(other._fax)) - return false; - if (_name == null) { - if (other._name != null) - return false; - } else if (!_name.equals(other._name)) - return false; - if (_note == null) { - if (other._note != null) - return false; - } else if (!_note.equals(other._note)) - return false; - if (_telephone == null) { - if (other._telephone != null) - return false; - } else if (!_telephone.equals(other._telephone)) - return false; - if (_type == null) { - if (other._type != null) - return false; - } else if (!_type.equals(other._type)) - return false; - if (_web == null) { - if (other._web != null) - return false; - } else if (!_web.equals(other._web)) - return false; - return true; + /** Sets the value of the name property. */ + public Builder name(final String name) { + this.name = name; + return this; } - public static final Agent newW3CInstance() { - Builder builder = new Builder("Word Wide Web Consortium", - AgentType.NONPROFIT) - .address( - "Massachusetts Institute of Technology, " - + "Computer Science and Artificial Intelligence Laboratory, " - + "32 Vassar Street, Room 32-G515, " - + "Cambridge, MA 02139") - .telephone("(617) 253-2613").fax("(617) 258-5999") - .web("http://www.w3.org/"); - return builder.build(); + /** Sets the value of the note property. */ + public Builder note(final String note) { + this.note = note; + return this; } - public static final Agent newIsoInstance() { - Builder builder = new Builder("ISO", AgentType.STANDARD) - .address( - "1, rue de Varembe, Casa postale 56, " - + "CH-1211, Geneva 20, Switzerland") - .telephone("+41 22 749 01 11").fax("+41 22 733 34 30") - .email("iso@iso.ch").web("http://www.iso.org/"); - return builder.build(); + /** Sets the value of the telephone property. */ + public Builder telephone(final String telephone) { + this.telephone = telephone; + return this; } - public static final Agent newAdobeInstance() { - Builder builder = new Builder("Adobe Systems, Inc.", - AgentType.COMMERCIAL) - .address("345 Park Avenue, San Jose, California 95110-2704") - .telephone("+1 (408) 536-6000").fax("+1 (408) 537-6000") - .web("http://www.adobe.com/"); - return builder.build(); + /** Sets the value of the web property. */ + public Builder web(final String web) { + this.web = web; + return this; } - @SuppressWarnings("hiding") - public static class Builder { - private String name; - private AgentType type = AgentType.EDUCATIONAL; - private String address; - private String telephone; - private String fax; - private String email; - private String web; - private String note; - - public Builder(final String name, final AgentType type) { - this.name = name; - this.type = type; - } - - /** - * Sets the value of the address property. - */ - public Builder address(final String address) { - this.address = address; - return this; - } - - /** - * Sets the value of the email property. - */ - public Builder email(final String email) { - this.email = email; - return this; - } - - /** - * Sets the value of the fax property. - */ - public Builder fax(final String fax) { - this.fax = fax; - return this; - } - - /** - * Sets the value of the name property. - */ - public Builder name(final String name) { - this.name = name; - return this; - } - - /** - * Sets the value of the note property. - */ - public Builder note(final String note) { - this.note = note; - return this; - } - - /** - * Sets the value of the telephone property. - */ - public Builder telephone(final String telephone) { - this.telephone = telephone; - return this; - } - - /** - * Sets the value of the web property. - */ - public Builder web(final String web) { - this.web = web; - return this; - } - - /** - * @return the immutable Agent instance build from the builder contents - */ - public Agent build() { - return new Agent(this.name, this.type, this.address, - this.telephone, this.fax, this.email, this.web, this.note); - } + /** @return the immutable Agent instance build from the builder contents */ + public Agent build() { + return new Agent( + this.name, + this.type, + this.address, + this.telephone, + this.fax, + this.email, + this.web, + this.note); } + } } diff --git a/jhove-core/src/main/java/edu/harvard/hul/ois/jhove/AgentType.java b/jhove-core/src/main/java/edu/harvard/hul/ois/jhove/AgentType.java index a77173d59..e93f47aaa 100644 --- a/jhove-core/src/main/java/edu/harvard/hul/ois/jhove/AgentType.java +++ b/jhove-core/src/main/java/edu/harvard/hul/ois/jhove/AgentType.java @@ -1,40 +1,39 @@ -/********************************************************************** - * Jhove - JSTOR/Harvard Object Validation Environment - * Copyright 2003 by JSTOR and the President and Fellows of Harvard College - **********************************************************************/ - +/** + * ******************************************************************** Jhove - JSTOR/Harvard Object + * Validation Environment Copyright 2003 by JSTOR and the President and Fellows of Harvard College + * ******************************************************************** + */ package edu.harvard.hul.ois.jhove; /** - * This class defines enumerated types for an Agent. - * Applications will not create or modify AgentTypes, but will - * use one of the predefined AgentType instances COMMERCIAL, GOVERNMENT, + * This class defines enumerated types for an Agent. Applications will not create or modify + * AgentTypes, but will use one of the predefined AgentType instances COMMERCIAL, GOVERNMENT, * EDUCATIONAL, NONPROFIT, STANDARD, or OTHER. * * @see Agent */ public enum AgentType { - /** Commercial organisation **/ - COMMERCIAL("Commercial"), - /** Commercial organisation **/ - GOVERNMENT("Government"), - /** Educational Institution **/ - EDUCATIONAL("Educational"), - /** Not for profit organisation **/ - NONPROFIT("Non-profit"), - /** Standardisation body, e.g ANSI, ISO **/ - STANDARD("Standards body"), - /** None of the above categories **/ - OTHER("Other"); - /** A String name for the type, used for reporting. */ - public final String name; + /** Commercial organisation * */ + COMMERCIAL("Commercial"), + /** Commercial organisation * */ + GOVERNMENT("Government"), + /** Educational Institution * */ + EDUCATIONAL("Educational"), + /** Not for profit organisation * */ + NONPROFIT("Non-profit"), + /** Standardisation body, e.g ANSI, ISO * */ + STANDARD("Standards body"), + /** None of the above categories * */ + OTHER("Other"); + /** A String name for the type, used for reporting. */ + public final String name; - private AgentType(final String name) { - this.name = name; - } + private AgentType(final String name) { + this.name = name; + } - @Override - public String toString() { - return this.name; - } + @Override + public String toString() { + return this.name; + } } diff --git a/jhove-core/src/main/java/edu/harvard/hul/ois/jhove/AnalogDigitalFlagType.java b/jhove-core/src/main/java/edu/harvard/hul/ois/jhove/AnalogDigitalFlagType.java index 7fb493957..a2b6ee508 100644 --- a/jhove-core/src/main/java/edu/harvard/hul/ois/jhove/AnalogDigitalFlagType.java +++ b/jhove-core/src/main/java/edu/harvard/hul/ois/jhove/AnalogDigitalFlagType.java @@ -1,33 +1,30 @@ -/********************************************************************** - * Jhove - JSTOR/Harvard Object Validation Environment - * Copyright 2004 by JSTOR and the President and Fellows of Harvard College +/** + * ******************************************************************** Jhove - JSTOR/Harvard Object + * Validation Environment Copyright 2004 by JSTOR and the President and Fellows of Harvard College * - **********************************************************************/ - + *

******************************************************************** + */ package edu.harvard.hul.ois.jhove; /** - * This class defines enumerated types for the analog/digital - * flag of AESAudioMetadata. - * Applications will not create or modify instances of this class, but will - * use one of the predefined AnalogDigitalFlagType instances. - * - * @author Gary McGath + * This class defines enumerated types for the analog/digital flag of AESAudioMetadata. Applications + * will not create or modify instances of this class, but will use one of the predefined + * AnalogDigitalFlagType instances. * + * @author Gary McGath */ public enum AnalogDigitalFlagType { - /** Enumeration instance for analog data */ - ANALOG("ANALOG"), - /** Enumeration instance for physical digital data */ - PHYS_DIGITAL("PHYS_DIGITAL"), - /** Enumeration instance for FILE digital data */ - FILE_DIGITAL("FILE_DIGITAL"); - /** A String value for the type */ - public final String value; - - private AnalogDigitalFlagType(final String value) { - this.value = value; - } + /** Enumeration instance for analog data */ + ANALOG("ANALOG"), + /** Enumeration instance for physical digital data */ + PHYS_DIGITAL("PHYS_DIGITAL"), + /** Enumeration instance for FILE digital data */ + FILE_DIGITAL("FILE_DIGITAL"); + /** A String value for the type */ + public final String value; + private AnalogDigitalFlagType(final String value) { + this.value = value; + } } diff --git a/jhove-core/src/main/java/edu/harvard/hul/ois/jhove/App.java b/jhove-core/src/main/java/edu/harvard/hul/ois/jhove/App.java index 4a1b504d5..00707249f 100644 --- a/jhove-core/src/main/java/edu/harvard/hul/ois/jhove/App.java +++ b/jhove-core/src/main/java/edu/harvard/hul/ois/jhove/App.java @@ -1,8 +1,8 @@ -/********************************************************************** - * Jhove - JSTOR/Harvard Object Validation Environment Copyright 2003 by JSTOR - * and the President and Fellows of Harvard College - **********************************************************************/ - +/** + * ******************************************************************** Jhove - JSTOR/Harvard Object + * Validation Environment Copyright 2003 by JSTOR and the President and Fellows of Harvard College + * ******************************************************************** + */ package edu.harvard.hul.ois.jhove; import java.util.Calendar; @@ -11,136 +11,125 @@ import org.openpreservation.jhove.ReleaseDetails; /** - * The application class for JHOVE. One instance of this class is generated by - * the application. Various utility and informational methods are contained - * here. + * The application class for JHOVE. One instance of this class is generated by the application. + * Various utility and informational methods are contained here. */ public final class App { - private static final String NAME = "JHOVE"; - - /** Application invocation syntax. */ - private static final String USAGE = "java " + NAME + " [-c config] " + - "[-m module] [-h handler] [-e encoding] [-H handler] [-o output] " + - "[-x saxclass] [-t tempdir] [-b bufsize] [-l loglevel] [[-krs] " + - "dir-file-or-uri [...]]"; - - - /****************************************************************** - * PRIVATE INSTANCE FIELDS. - ******************************************************************/ - - /** Application build date. */ - private final Date _date; - - /** Application name. */ - private final String _name; - - /** Application release identifier. */ - private String _release; - - /** Application rights statement. */ - private final String _rights; - - /** Application invocation syntax. */ - private final String _usage; - - /****************************************************************** - * CLASS CONSTRUCTOR. - ******************************************************************/ - - /** - * Creates an App object. - * - * @param name - * Name of the application. - * @param release - * Release identifier. - * @param date - * Last modification date of the application code, in the form of - * an array of three numbers. date[0] is the year, - * date[1] the month, and date[2] the - * day. - * @param usage - * String summarizing command line usage, to output when app name - * is typed with no arguments or flags. - * @param rights - * Copyright or other rights statement. - */ - public App(String name, String release, int[] date, String usage, - String rights) { - Calendar calendar = new GregorianCalendar(); - calendar.set(date[0], date[1] - 1, date[2]); - - _name = name; - _release = release; - _date = calendar.getTime(); - _usage = usage; - _rights = rights; - } - - private App(final String name, final ReleaseDetails details) { - _name = name; - _release = details.getVersion(); - _date = details.getBuildDate(); - _usage = USAGE; - _rights = details.getRights(); - } - - public static App newAppWithName(final String name) { - return new App(name, ReleaseDetails.getInstance()); - } - - /****************************************************************** - * PUBLIC INSTANCE METHODS. - * - * Accessor methods. - ******************************************************************/ - - /** - * Returns application code creation date - */ - public Date getDate() { - return _date; - } - - /** - * Returns the name of this application - */ - public String getName() { - return _name; - } - - /** - * Returns the release identifier for this application. - */ - public String getRelease() { - return _release; - } - - /** - * Returns the rights string. - */ - public String getRights() { - return _rights; - } - - /** - * Returns the usage string, which should be output when the user enters a - * command line with only the application name. - */ - public String getUsage() { - return _usage; - } - - /****************************************************************** - * Serialization methods. - ******************************************************************/ - - /** - * Outputs detailed information about the application, including - * configuration, available modules and handlers, etc. - */ - public void show(OutputHandler handler) { - handler.show(this); - } + private static final String NAME = "JHOVE"; + + /** Application invocation syntax. */ + private static final String USAGE = + "java " + + NAME + + " [-c config] " + + "[-m module] [-h handler] [-e encoding] [-H handler] [-o output] " + + "[-x saxclass] [-t tempdir] [-b bufsize] [-l loglevel] [[-krs] " + + "dir-file-or-uri [...]]"; + + /** + * **************************************************************** PRIVATE INSTANCE FIELDS. + * **************************************************************** + */ + + /** Application build date. */ + private final Date _date; + + /** Application name. */ + private final String _name; + + /** Application release identifier. */ + private String _release; + + /** Application rights statement. */ + private final String _rights; + + /** Application invocation syntax. */ + private final String _usage; + + /** + * **************************************************************** CLASS CONSTRUCTOR. + * **************************************************************** + */ + + /** + * Creates an App object. + * + * @param name Name of the application. + * @param release Release identifier. + * @param date Last modification date of the application code, in the form of an array of three + * numbers. date[0] is the year, date[1] the month, and + * date[2] the day. + * @param usage String summarizing command line usage, to output when app name is typed with no + * arguments or flags. + * @param rights Copyright or other rights statement. + */ + public App(String name, String release, int[] date, String usage, String rights) { + Calendar calendar = new GregorianCalendar(); + calendar.set(date[0], date[1] - 1, date[2]); + + _name = name; + _release = release; + _date = calendar.getTime(); + _usage = usage; + _rights = rights; + } + + private App(final String name, final ReleaseDetails details) { + _name = name; + _release = details.getVersion(); + _date = details.getBuildDate(); + _usage = USAGE; + _rights = details.getRights(); + } + + public static App newAppWithName(final String name) { + return new App(name, ReleaseDetails.getInstance()); + } + + /** + * **************************************************************** PUBLIC INSTANCE METHODS. + * + *

Accessor methods. **************************************************************** + */ + + /** Returns application code creation date */ + public Date getDate() { + return _date; + } + + /** Returns the name of this application */ + public String getName() { + return _name; + } + + /** Returns the release identifier for this application. */ + public String getRelease() { + return _release; + } + + /** Returns the rights string. */ + public String getRights() { + return _rights; + } + + /** + * Returns the usage string, which should be output when the user enters a command line with only + * the application name. + */ + public String getUsage() { + return _usage; + } + + /** + * **************************************************************** Serialization methods. + * **************************************************************** + */ + + /** + * Outputs detailed information about the application, including configuration, available modules + * and handlers, etc. + */ + public void show(OutputHandler handler) { + handler.show(this); + } } diff --git a/jhove-core/src/main/java/edu/harvard/hul/ois/jhove/ByteArrayXMPSource.java b/jhove-core/src/main/java/edu/harvard/hul/ois/jhove/ByteArrayXMPSource.java index d7e182f95..14dfadc16 100644 --- a/jhove-core/src/main/java/edu/harvard/hul/ois/jhove/ByteArrayXMPSource.java +++ b/jhove-core/src/main/java/edu/harvard/hul/ois/jhove/ByteArrayXMPSource.java @@ -1,66 +1,54 @@ -/********************************************************************** - * Jhove - JSTOR/Harvard Object Validation Environment - * Copyright 2003 by JSTOR and the President and Fellows of Harvard College +/** + * ******************************************************************** Jhove - JSTOR/Harvard Object + * Validation Environment Copyright 2003 by JSTOR and the President and Fellows of Harvard College * - **********************************************************************/ - + *

******************************************************************** + */ package edu.harvard.hul.ois.jhove; import java.io.*; /** - * Class for providing an InputSource to XMPHandler, - * with a ByteArrayInputStream as the basis of the - * InputSource. This is suitable for a number of modules. - * - * @author Gary McGath + * Class for providing an InputSource to XMPHandler, with a ByteArrayInputStream as the basis of the + * InputSource. This is suitable for a number of modules. * + * @author Gary McGath */ public class ByteArrayXMPSource extends XMPSource { - /* The underlying ByteArrayInputStream. */ - ByteArrayInputStream _instrm; - - /** - * Constructor based on ByteArrayInputStream. - * - * @param instrm ByteArrayInputStream containing the XMP - */ - public ByteArrayXMPSource (ByteArrayInputStream instrm) - { - super (new InputStreamReader - (new XMLWrapperStream (instrm))); - _instrm = instrm; - // Prepare for resetting. - instrm.mark (instrm.available ()); - } - - - /** - * Constructor based on ByteArrayInputStream with encoding. - * - * @param instrm ByteArrayInputStream containing the XMP - */ - public ByteArrayXMPSource (ByteArrayInputStream instrm, - String encoding) - throws IOException - { - super (new InputStreamReader - (new XMLWrapperStream (instrm), encoding)); - _instrm = instrm; - // Prepare for resetting. - instrm.mark (instrm.available ()); - } - - - - /* (non-Javadoc) - * @see edu.harvard.hul.ois.jhove.XMPSource#resetReader() - */ - @Override - protected void resetReader() { - _instrm.reset (); - _reader = new InputStreamReader (_instrm); - } - + /* The underlying ByteArrayInputStream. */ + ByteArrayInputStream _instrm; + + /** + * Constructor based on ByteArrayInputStream. + * + * @param instrm ByteArrayInputStream containing the XMP + */ + public ByteArrayXMPSource(ByteArrayInputStream instrm) { + super(new InputStreamReader(new XMLWrapperStream(instrm))); + _instrm = instrm; + // Prepare for resetting. + instrm.mark(instrm.available()); + } + + /** + * Constructor based on ByteArrayInputStream with encoding. + * + * @param instrm ByteArrayInputStream containing the XMP + */ + public ByteArrayXMPSource(ByteArrayInputStream instrm, String encoding) throws IOException { + super(new InputStreamReader(new XMLWrapperStream(instrm), encoding)); + _instrm = instrm; + // Prepare for resetting. + instrm.mark(instrm.available()); + } + + /* (non-Javadoc) + * @see edu.harvard.hul.ois.jhove.XMPSource#resetReader() + */ + @Override + protected void resetReader() { + _instrm.reset(); + _reader = new InputStreamReader(_instrm); + } } diff --git a/jhove-core/src/main/java/edu/harvard/hul/ois/jhove/Callback.java b/jhove-core/src/main/java/edu/harvard/hul/ois/jhove/Callback.java index 708026c6f..9a8703e13 100644 --- a/jhove-core/src/main/java/edu/harvard/hul/ois/jhove/Callback.java +++ b/jhove-core/src/main/java/edu/harvard/hul/ois/jhove/Callback.java @@ -1,25 +1,20 @@ -/********************************************************************** - * Jhove - JSTOR/Harvard Object Validation Environment - * Copyright 2003 by JSTOR and the President and Fellows of Harvard College - **********************************************************************/ - -package edu.harvard.hul.ois.jhove; - /** - * An interface for supporting a general, simple callback function. + * ******************************************************************** Jhove - JSTOR/Harvard Object + * Validation Environment Copyright 2003 by JSTOR and the President and Fellows of Harvard College + * ******************************************************************** */ -public interface Callback -{ - /** - * A generic callback function. Any class which needs to support - * callback can implement callback and pass a reference to itself - * to the function that does the callback. - * - * @param selector An indicator of the function to be performed. - * Interpretation is determined by the implementing class. - * @param parm Whatever data may be appropriate to the callback. - * - * @return As specified by the implementing class. - */ - public int callback (int selector, Object parm); +package edu.harvard.hul.ois.jhove; + +/** An interface for supporting a general, simple callback function. */ +public interface Callback { + /** + * A generic callback function. Any class which needs to support callback can implement callback + * and pass a reference to itself to the function that does the callback. + * + * @param selector An indicator of the function to be performed. Interpretation is determined by + * the implementing class. + * @param parm Whatever data may be appropriate to the callback. + * @return As specified by the implementing class. + */ + public int callback(int selector, Object parm); } diff --git a/jhove-core/src/main/java/edu/harvard/hul/ois/jhove/Checksum.java b/jhove-core/src/main/java/edu/harvard/hul/ois/jhove/Checksum.java index a7be0ffaf..0fb13baa1 100644 --- a/jhove-core/src/main/java/edu/harvard/hul/ois/jhove/Checksum.java +++ b/jhove-core/src/main/java/edu/harvard/hul/ois/jhove/Checksum.java @@ -1,93 +1,75 @@ -/********************************************************************** - * Jhove - JSTOR/Harvard Object Validation Environment - * Copyright 2003 by JSTOR and the President and Fellows of Harvard College - **********************************************************************/ - +/** + * ******************************************************************** Jhove - JSTOR/Harvard Object + * Validation Environment Copyright 2003 by JSTOR and the President and Fellows of Harvard College + * ******************************************************************** + */ package edu.harvard.hul.ois.jhove; - /** - * This class encapsulates the result of calculations which provide a greater - * or lesser degree of confirmation of the integrity of a digital - * object's content, including checksums, CRC's, message digests, - * etc. + * This class encapsulates the result of calculations which provide a greater or lesser degree of + * confirmation of the integrity of a digital object's content, including checksums, CRC's, message + * digests, etc. * - * @see ChecksumType - * @see Checksummer + * @see ChecksumType + * @see Checksummer */ -public class Checksum -{ - /****************************************************************** - * PRIVATE INSTANCE FIELDS. - ******************************************************************/ - - private ChecksumType _type; - private String _value; +public class Checksum { + /** + * **************************************************************** PRIVATE INSTANCE FIELDS. + * **************************************************************** + */ + private ChecksumType _type; - /****************************************************************** - * CLASS CONSTRUCTOR. - ******************************************************************/ + private String _value; - /** - * Creates a Checksum with a given value and type - */ - public Checksum (String value, ChecksumType type) - { - _value = value; - _type = type; - } + /** + * **************************************************************** CLASS CONSTRUCTOR. + * **************************************************************** + */ + /** Creates a Checksum with a given value and type */ + public Checksum(String value, ChecksumType type) { + _value = value; + _type = type; + } - /****************************************************************** - * PUBLIC INSTANCE METHODS. - * - * Accessor methods. - ******************************************************************/ + /** + * **************************************************************** PUBLIC INSTANCE METHODS. + * + *

Accessor methods. **************************************************************** + */ - /** - * Returns this Checksum's type - */ - public ChecksumType getType () - { - return _type; - } + /** Returns this Checksum's type */ + public ChecksumType getType() { + return _type; + } - /** - * Returns this Checksum's value - */ - public String getValue () - { - return _value; - } + /** Returns this Checksum's value */ + public String getValue() { + return _value; + } - /****************************************************************** - * Mutator methods. - ******************************************************************/ + /** + * **************************************************************** Mutator methods. + * **************************************************************** + */ - /** - * Sets the type of this Checksum - */ - public void setType (ChecksumType type) - { - _type = type; - } + /** Sets the type of this Checksum */ + public void setType(ChecksumType type) { + _type = type; + } - /** - * Sets the value of this Checksum - */ - public void setValue (String value) - { - _value = value; - } + /** Sets the value of this Checksum */ + public void setValue(String value) { + _value = value; + } - /****************************************************************** - * Put here as a convenience for checksum calculators - *****************************************************************/ - /** - * Maps unsigned byte value (0 to 256) to signed byte value (-128 to 127). - */ - public static byte unsignedByteToByte (int value) - { - return (byte) ((value < 127) ? value : value - 256); - } + /** + * **************************************************************** Put here as a convenience for + * checksum calculators *************************************************************** + */ + /** Maps unsigned byte value (0 to 256) to signed byte value (-128 to 127). */ + public static byte unsignedByteToByte(int value) { + return (byte) ((value < 127) ? value : value - 256); + } } diff --git a/jhove-core/src/main/java/edu/harvard/hul/ois/jhove/ChecksumInputStream.java b/jhove-core/src/main/java/edu/harvard/hul/ois/jhove/ChecksumInputStream.java index 3fdb7fe14..e62678fff 100644 --- a/jhove-core/src/main/java/edu/harvard/hul/ois/jhove/ChecksumInputStream.java +++ b/jhove-core/src/main/java/edu/harvard/hul/ois/jhove/ChecksumInputStream.java @@ -1,150 +1,127 @@ -/********************************************************************** - * Jhove - JSTOR/Harvard Object Validation Environment - * Copyright 2003-2006 by JSTOR and the President and Fellows of Harvard College - **********************************************************************/ - +/** + * ******************************************************************** Jhove - JSTOR/Harvard Object + * Validation Environment Copyright 2003-2006 by JSTOR and the President and Fellows of Harvard + * College ******************************************************************** + */ package edu.harvard.hul.ois.jhove; -import java.io.IOException; import java.io.*; +import java.io.IOException; /** - * A ChecksumInputStream is a FilterInputStream with the added - * functionality of calculating checksums as it goes. - * - * The idea of replacing this with java.util.zip.CheckedInputStream - * looks very tempting, but we need the byte count, which - * CheckedInputStream doesn't provide. - * - * @author Gary McGath + * A ChecksumInputStream is a FilterInputStream with the added functionality of calculating + * checksums as it goes. + * + *

The idea of replacing this with java.util.zip.CheckedInputStream looks very tempting, but we + * need the byte count, which CheckedInputStream doesn't provide. * + * @author Gary McGath */ public class ChecksumInputStream extends FilterInputStream { - private InputStream subsumedStream; - private Checksummer _cksummer; - private long _nBytes; - - /** - * Constructor. - * - * @param stream Stream to be filtered - * @param cksummer Object to calculate checksum on the bytes - * as they are read - */ - public ChecksumInputStream(InputStream stream, Checksummer cksummer) { - super (stream); - subsumedStream = stream; - _cksummer = cksummer; - _nBytes = 0; - } + private InputStream subsumedStream; + private Checksummer _cksummer; + private long _nBytes; - /** - * Reads a byte from the subsumed stream, updating - * the byte count and the checksums. - */ - @Override - public int read() throws IOException { - int ch = subsumedStream.read (); - if (ch >= 0) { - _nBytes++; - if (_cksummer != null) { - _cksummer.update (ch); - } - } - return ch; - } + /** + * Constructor. + * + * @param stream Stream to be filtered + * @param cksummer Object to calculate checksum on the bytes as they are read + */ + public ChecksumInputStream(InputStream stream, Checksummer cksummer) { + super(stream); + subsumedStream = stream; + _cksummer = cksummer; + _nBytes = 0; + } - /** - * Reads some number of bytes from the input stream and - * stores them into the buffer array b. The number of - * bytes actually read is returned as an integer. - * - * All bytes read are fed through the checksummer. - */ - @Override - public int read(byte[] b) throws IOException - { - int len = subsumedStream.read (b); - // Careful here -- don't want to add -1 bytes at EOF - if (len > 0) { - if (_cksummer != null) { - _cksummer.update (b); - } - _nBytes += len; - } - return len; - } - - /** - * Reads up to len bytes of data from the input stream - * into an array of bytes. An attempt is made to read as - * many as len bytes, but a smaller number may be read, - * possibly zero. The number of bytes actually read is - * returned as an integer. - * - * All bytes read are fed through the checksummer. - */ - @Override - public int read(byte[] b, int off, int len) throws IOException - { - len = subsumedStream.read (b, off, len); - // Careful here -- don't want to add -1 bytes at EOF - if (len > 0) { - if (_cksummer != null) { - _cksummer.update (b, off, len); - } - _nBytes += len; - } - return len; - } - - /** - * Skips n bytes. - * Reads them and feeds them through the checksummer. - */ - @Override - public long skip (long n) throws IOException { - long nret = 0; - while (n > 0) { - // grab the data in reasonable buffer-sized chunks. - int bufsize = (int) (n > 8192 ? 8192 : n); - byte[] buf = new byte[bufsize]; - int nread = read (buf); - if (nread <= 0) { - break; - } - nret += nread; - n -= nread; - } - return nret; + /** Reads a byte from the subsumed stream, updating the byte count and the checksums. */ + @Override + public int read() throws IOException { + int ch = subsumedStream.read(); + if (ch >= 0) { + _nBytes++; + if (_cksummer != null) { + _cksummer.update(ch); + } } - - /** - * Closes the subsumed stream. - */ - @Override - public void close () throws IOException - { - subsumedStream.close(); + return ch; + } + + /** + * Reads some number of bytes from the input stream and stores them into the buffer array b. The + * number of bytes actually read is returned as an integer. + * + *

All bytes read are fed through the checksummer. + */ + @Override + public int read(byte[] b) throws IOException { + int len = subsumedStream.read(b); + // Careful here -- don't want to add -1 bytes at EOF + if (len > 0) { + if (_cksummer != null) { + _cksummer.update(b); + } + _nBytes += len; } - - /** - * Returns the byte count to date on the stream. - * This returns the number of bytes read. - * Because of buffering, this is not a reliable - * indicator of how many bytes have actually been processed. - */ - public long getNBytes () - { - return _nBytes; + return len; + } + + /** + * Reads up to len bytes of data from the input stream into an array of bytes. An attempt is made + * to read as many as len bytes, but a smaller number may be read, possibly zero. The number of + * bytes actually read is returned as an integer. + * + *

All bytes read are fed through the checksummer. + */ + @Override + public int read(byte[] b, int off, int len) throws IOException { + len = subsumedStream.read(b, off, len); + // Careful here -- don't want to add -1 bytes at EOF + if (len > 0) { + if (_cksummer != null) { + _cksummer.update(b, off, len); + } + _nBytes += len; } - - /** - * Returns the Checksummer object. - */ - public Checksummer getChecksummer () - { - return _cksummer; + return len; + } + + /** Skips n bytes. Reads them and feeds them through the checksummer. */ + @Override + public long skip(long n) throws IOException { + long nret = 0; + while (n > 0) { + // grab the data in reasonable buffer-sized chunks. + int bufsize = (int) (n > 8192 ? 8192 : n); + byte[] buf = new byte[bufsize]; + int nread = read(buf); + if (nread <= 0) { + break; + } + nret += nread; + n -= nread; } + return nret; + } + + /** Closes the subsumed stream. */ + @Override + public void close() throws IOException { + subsumedStream.close(); + } + + /** + * Returns the byte count to date on the stream. This returns the number of bytes read. Because of + * buffering, this is not a reliable indicator of how many bytes have actually been processed. + */ + public long getNBytes() { + return _nBytes; + } + + /** Returns the Checksummer object. */ + public Checksummer getChecksummer() { + return _cksummer; + } } diff --git a/jhove-core/src/main/java/edu/harvard/hul/ois/jhove/ChecksumType.java b/jhove-core/src/main/java/edu/harvard/hul/ois/jhove/ChecksumType.java index 50a4119ea..657761fb8 100644 --- a/jhove-core/src/main/java/edu/harvard/hul/ois/jhove/ChecksumType.java +++ b/jhove-core/src/main/java/edu/harvard/hul/ois/jhove/ChecksumType.java @@ -1,37 +1,35 @@ -/********************************************************************** - * Jhove - JSTOR/Harvard Object Validation Environment - * Copyright 2003 by JSTOR and the President and Fellows of Harvard College - **********************************************************************/ - +/** + * ******************************************************************** Jhove - JSTOR/Harvard Object + * Validation Environment Copyright 2003 by JSTOR and the President and Fellows of Harvard College + * ******************************************************************** + */ package edu.harvard.hul.ois.jhove; /** - * This class defines enumerated types for a Checksum on a content - * stream or file. - * Applications will not create or modify ChecksumTypes, but will - * use one of the predefined ChecksumType instances + * This class defines enumerated types for a Checksum on a content stream or file. Applications will + * not create or modify ChecksumTypes, but will use one of the predefined ChecksumType instances * CRC32, MD5, SHA1, or SHA256. * * @see Checksum */ public enum ChecksumType { - /** 32-bit Cyclical Redundancy Checksum. */ - CRC32("CRC32"), - /** 128-bit Message Digest 5. */ - MD5("MD5"), - /** 160-bit Secure Hash Algorithm. */ - SHA1("SHA-1"), - /** 256-bit Secure Hash Algorithm. */ - SHA256("SHA-256"); - /** A String name for the type, used for reporting. */ - public final String name; + /** 32-bit Cyclical Redundancy Checksum. */ + CRC32("CRC32"), + /** 128-bit Message Digest 5. */ + MD5("MD5"), + /** 160-bit Secure Hash Algorithm. */ + SHA1("SHA-1"), + /** 256-bit Secure Hash Algorithm. */ + SHA256("SHA-256"); + /** A String name for the type, used for reporting. */ + public final String name; - private ChecksumType(final String name) { - this.name = name; - } + private ChecksumType(final String name) { + this.name = name; + } - @Override - public String toString() { - return this.name; - } + @Override + public String toString() { + return this.name; + } } diff --git a/jhove-core/src/main/java/edu/harvard/hul/ois/jhove/Checksummer.java b/jhove-core/src/main/java/edu/harvard/hul/ois/jhove/Checksummer.java index 8c6a0dbe0..550e97c4d 100644 --- a/jhove-core/src/main/java/edu/harvard/hul/ois/jhove/Checksummer.java +++ b/jhove-core/src/main/java/edu/harvard/hul/ois/jhove/Checksummer.java @@ -1,213 +1,182 @@ -/********************************************************************** - * Jhove - JSTOR/Harvard Object Validation Environment - * Copyright 2003-2006 by JSTOR and the President and Fellows of Harvard College - **********************************************************************/ - +/** + * ******************************************************************** Jhove - JSTOR/Harvard Object + * Validation Environment Copyright 2003-2006 by JSTOR and the President and Fellows of Harvard + * College ******************************************************************** + */ package edu.harvard.hul.ois.jhove; + import java.security.*; import java.util.zip.*; /** - * The Checksummer class encapsulates the calculation of the - * CRC32, MD5, SHA-1 and SHA-256 checksums. + * The Checksummer class encapsulates the calculation of the CRC32, MD5, SHA-1 and SHA-256 + * checksums. */ -public class Checksummer implements java.util.zip.Checksum -{ - /****************************************************************** - * PRIVATE INSTANCE FIELDS. - ******************************************************************/ - - /** Byte count. */ - protected long _nByte; - /** CRC32 object. */ - private CRC32 _crc32; - /** MD5 message digest. */ - private MessageDigest _md5; - /** SHA-1 message digest. */ - private MessageDigest _sha1; - /** SHA-256 message digest. */ - private MessageDigest _sha256; - - /** - * Creates a Checksummer, with instances of each of - * CRC32, MD5, SHA-1 and SHA-256 MessageDigest. - * If one or both of the MessageDigests aren't supported - * on the current platform, they are left as null. - * - * @see CRC32 - * @see MessageDigest - */ - public Checksummer () - { - reset (); - } - - /** Resets all checksums and the byte count to their - * initial values. - */ - @Override - public void reset () - { - _nByte = 0; - _crc32 = new CRC32 (); - try { - _md5 = MessageDigest.getInstance ("MD5"); - _sha1 = MessageDigest.getInstance ("SHA-1"); - _sha256 = MessageDigest.getInstance ("SHA-256"); - } - catch (NoSuchAlgorithmException e) { - throw new IllegalStateException("Missing checksum algorithm.", e); - } - } - - /** getValue is required by the Checksum interface, but - * we can return only one of the three values. We - * return the CRC32 value, since that's the one which - * is guaranteed to be available. - */ - @Override - public long getValue () - { - return _crc32.getValue (); - } - - /** - * Updates the checksum with the argument. - * Called when a signed byte is available. - */ - public void update (byte b) - { - _crc32.update (b); - if (_md5 != null) { - _md5.update (b); - } +public class Checksummer implements java.util.zip.Checksum { + /** + * **************************************************************** PRIVATE INSTANCE FIELDS. + * **************************************************************** + */ + + /** Byte count. */ + protected long _nByte; + /** CRC32 object. */ + private CRC32 _crc32; + /** MD5 message digest. */ + private MessageDigest _md5; + /** SHA-1 message digest. */ + private MessageDigest _sha1; + /** SHA-256 message digest. */ + private MessageDigest _sha256; + + /** + * Creates a Checksummer, with instances of each of CRC32, MD5, SHA-1 and SHA-256 MessageDigest. + * If one or both of the MessageDigests aren't supported on the current platform, they are left as + * null. + * + * @see CRC32 + * @see MessageDigest + */ + public Checksummer() { + reset(); + } + + /** Resets all checksums and the byte count to their initial values. */ + @Override + public void reset() { + _nByte = 0; + _crc32 = new CRC32(); + try { + _md5 = MessageDigest.getInstance("MD5"); + _sha1 = MessageDigest.getInstance("SHA-1"); + _sha256 = MessageDigest.getInstance("SHA-256"); + } catch (NoSuchAlgorithmException e) { + throw new IllegalStateException("Missing checksum algorithm.", e); + } + } + + /** + * getValue is required by the Checksum interface, but we can return only one of the three values. + * We return the CRC32 value, since that's the one which is guaranteed to be available. + */ + @Override + public long getValue() { + return _crc32.getValue(); + } + + /** Updates the checksum with the argument. Called when a signed byte is available. */ + public void update(byte b) { + _crc32.update(b); + if (_md5 != null) { + _md5.update(b); + } if (_sha1 != null) { - _sha1.update (b); - } + _sha1.update(b); + } if (_sha256 != null) { - _sha256.update (b); - } - } - - /** - * Updates the checksum with the argument. - * Called when an unsigned byte is available. - */ - @Override - public void update (int b) - { - byte sb; - if (b > 127) { - sb = (byte) (b - 256); - } - else { - sb = (byte) b; - } - update (sb); - } - - /** - * Updates the checksum with the argument. - * Called when a byte array is available. - */ - public void update (byte[] b) - { - _crc32.update (b); - if (_md5 != null) { - _md5.update (b); - } - if (_sha1 != null) { - _sha1.update (b); - } - if (_sha256 != null) { - _sha256.update (b); - } - } - - /** - * Updates the checksum with the argument. - * Called when a byte array is available. - */ - @Override - public void update (byte[] b, int off, int len) - { - _crc32.update (b, off, len); - if (_md5 != null) { - _md5.update (b, off, len); - } + _sha256.update(b); + } + } + + /** Updates the checksum with the argument. Called when an unsigned byte is available. */ + @Override + public void update(int b) { + byte sb; + if (b > 127) { + sb = (byte) (b - 256); + } else { + sb = (byte) b; + } + update(sb); + } + + /** Updates the checksum with the argument. Called when a byte array is available. */ + public void update(byte[] b) { + _crc32.update(b); + if (_md5 != null) { + _md5.update(b); + } if (_sha1 != null) { - _sha1.update (b, off, len); - } + _sha1.update(b); + } if (_sha256 != null) { - _sha256.update (b, off, len); - } - } - - /** - * Returns the value of the CRC32 as a hex string. - */ - public String getCRC32 () - { - return padLeadingZeroes - (Long.toHexString (_crc32.getValue ()), 8); - } - - /** - * Returns the value of the MD5 digest as a hex string. - * Returns null if the digest is not available. - */ - public String getMD5 () - { - if (_md5 == null) { - return null; - } - return convertToHex(_md5.digest()); - } - - /** - * Returns the value of the SHA-1 digest as a hex string. - * Returns null if the digest is not available. - */ - public String getSHA1 () - { - if (_sha1 == null) { - return null; - } - return convertToHex(_sha1.digest()); - } - - /** - * Returns the value of the SHA-256 digest as a hex string. - * Returns null if the digest is not available. - */ - public String getSHA256 () - { - if (_sha256 == null) { - return null; - } - return convertToHex(_sha256.digest()); - } - - private String convertToHex(final byte [] digest) { - StringBuffer buffer = new StringBuffer(); - for (int i=0; i= 0) ? digest[i] : 256 + digest[i]; - buffer.append(padLeadingZeroes(Integer.toHexString(un), 2)); - } - return buffer.toString(); - } - - /** Pad a hexadecimal (or other numeric) string out to - * the specified length with leading zeroes. */ - private static String padLeadingZeroes (String str, int len) - { - StringBuffer buff = new StringBuffer(); - int padLen = len - str.length(); - while (padLen > 0) { - buff.append("0"); - padLen--; - } - buff.append(str); - return buff.toString(); + _sha256.update(b); + } + } + + /** Updates the checksum with the argument. Called when a byte array is available. */ + @Override + public void update(byte[] b, int off, int len) { + _crc32.update(b, off, len); + if (_md5 != null) { + _md5.update(b, off, len); + } + if (_sha1 != null) { + _sha1.update(b, off, len); + } + if (_sha256 != null) { + _sha256.update(b, off, len); + } + } + + /** Returns the value of the CRC32 as a hex string. */ + public String getCRC32() { + return padLeadingZeroes(Long.toHexString(_crc32.getValue()), 8); + } + + /** + * Returns the value of the MD5 digest as a hex string. Returns null if the digest is not + * available. + */ + public String getMD5() { + if (_md5 == null) { + return null; + } + return convertToHex(_md5.digest()); + } + + /** + * Returns the value of the SHA-1 digest as a hex string. Returns null if the digest is not + * available. + */ + public String getSHA1() { + if (_sha1 == null) { + return null; + } + return convertToHex(_sha1.digest()); + } + + /** + * Returns the value of the SHA-256 digest as a hex string. Returns null if the digest is not + * available. + */ + public String getSHA256() { + if (_sha256 == null) { + return null; + } + return convertToHex(_sha256.digest()); + } + + private String convertToHex(final byte[] digest) { + StringBuffer buffer = new StringBuffer(); + for (int i = 0; i < digest.length; i++) { + int un = (digest[i] >= 0) ? digest[i] : 256 + digest[i]; + buffer.append(padLeadingZeroes(Integer.toHexString(un), 2)); + } + return buffer.toString(); + } + + /** + * Pad a hexadecimal (or other numeric) string out to the specified length with leading zeroes. + */ + private static String padLeadingZeroes(String str, int len) { + StringBuffer buff = new StringBuffer(); + int padLen = len - str.length(); + while (padLen > 0) { + buff.append("0"); + padLen--; } + buff.append(str); + return buff.toString(); + } } diff --git a/jhove-core/src/main/java/edu/harvard/hul/ois/jhove/ConfigHandler.java b/jhove-core/src/main/java/edu/harvard/hul/ois/jhove/ConfigHandler.java index 0ccbc289f..9efc8cb14 100644 --- a/jhove-core/src/main/java/edu/harvard/hul/ois/jhove/ConfigHandler.java +++ b/jhove-core/src/main/java/edu/harvard/hul/ois/jhove/ConfigHandler.java @@ -1,8 +1,8 @@ -/********************************************************************** - * Jhove - JSTOR/Harvard Object Validation Environment - * Copyright 2003 by JSTOR and the President and Fellows of Harvard College - **********************************************************************/ - +/** + * ******************************************************************** Jhove - JSTOR/Harvard Object + * Validation Environment Copyright 2003 by JSTOR and the President and Fellows of Harvard College + * ******************************************************************** + */ package edu.harvard.hul.ois.jhove; import java.io.IOException; @@ -11,373 +11,315 @@ import java.util.*; import org.xml.sax.*; -/** - * SAX Parser for the configuration file. - */ -public class ConfigHandler - extends org.xml.sax.helpers.DefaultHandler -{ - /****************************************************************** - * PRIVATE INSTANCE FIELDS. - ******************************************************************/ - - private String _class; - protected StringBuffer _content; - - /** The schema name */ - private final static String configSchemaName = - "jhoveConfig.xsd"; - - /** The list of handlers. Each element in the List is an - * array of two Strings representing the class and the initialization - * string. */ - private List _handler; - - /** The list of handler parameters. Each element in the List is - * a List of Strings (which may be empty but not null) representing - * parameters to be passed to the module. List elements are in - * one-to-one correspondence with _handler. - */ - protected List> _handlerParams; - private String _init; - private List _param; - private String _tempDir; - private String _mixVsn; - private String _encoding; - private String _logLevel; - private int _bufferSize; - private String _jhoveHome; - private int _sigBytes; - - protected boolean _isHandler; - - /* _isModule is protected rather than private so that subclasses - * can add elements to the module element. */ - protected boolean _isModule; - - private boolean _isTempDir; - private boolean _isMixVsn; - private boolean _isEncoding; - private boolean _isBufferSize; - private boolean _isJhoveHome; - private boolean _isLogLevel; - private boolean _isSigBytes; - - /** The list of modules. Each element in the List is an - * array of two Strings representing the class and the initialization - * string. */ - protected List _module; - - /** The list of module parameters. Each element in the List is - * a List of Strings (which may be empty but not null) representing - * parameters to be passed to the module. List elements are in - * one-to-one correspondence with _module. - */ - protected List> _modParams; - - /****************************************************************** - * CLASS CONSTRUCTOR. - ******************************************************************/ - - /** - * Creates a ConfigHandler. - */ - public ConfigHandler () - { - _module = new ArrayList<> (); - _handler = new ArrayList<> (); - _modParams = new ArrayList<> (); - _handlerParams = new ArrayList<> (); - - _isModule = false; - _isHandler = false; - _isTempDir = false; - _isEncoding = false; - _isBufferSize = false; - _isJhoveHome = false; - _isLogLevel = false; - - _bufferSize = -1; - _encoding = null; - _tempDir = null; - _mixVsn = null; - _sigBytes = 1024; - _logLevel = null; - } +/** SAX Parser for the configuration file. */ +public class ConfigHandler extends org.xml.sax.helpers.DefaultHandler { + /** + * **************************************************************** PRIVATE INSTANCE FIELDS. + * **************************************************************** + */ + private String _class; - /****************************************************************** - * PUBLIC INSTANCE METHODS. - * - * Accessor methods. - ******************************************************************/ - - /** - * Returns the List of Modules specified by the config file. - * Each element of the List is a String[2] whose elements are - * the module class name and initialization value. - * - * @see Module - */ - public List getModule () - { - return _module; - } - - /** - * Returns the List of module parameters specified by the config file. - * Each element of the List is a List (possibly empty) of Strings - * whose elements are parameters to pass to the module. The - * values returned by getModuleParams() are in - * one-to-one correspondence with those return by getModule(). - */ - public List> getModuleParams () - { - return _modParams; - } + protected StringBuffer _content; - /** - * Returns the List of handler parameters specified by the config file. - * Each element of the List is a List (possibly empty) of Strings - * whose elements are parameters to pass to the output handler. The - * values returned by getHandlerParams() are in - * one-to-one correspondence with those return by getHandler(). - */ - public List> getHandlerParams () - { - return _handlerParams; - } + /** The schema name */ + private static final String configSchemaName = "jhoveConfig.xsd"; - /** - * Returns the List of OutputHandlers specified by the config file. - * - * @see OutputHandler - */ - public List getHandler () - { - return _handler; - } + /** + * The list of handlers. Each element in the List is an array of two Strings representing the + * class and the initialization string. + */ + private List _handler; - /** - * Returns the temporary directory path specified by the config file, - * with final path separator. - */ - public String getTempDir () - { - return _tempDir; - } - - /** Returns the MIX schema version specified by the config file. - * Acceptable values are "0.2" and "1.0" and "2.0". - */ - public String getMixVsn () - { - return _mixVsn; - } - - /** Returns the number of bytes to examine when looking for an - * indefinitely positioned signature, or checking the first - * sigBytes bytes of a file in lieu of a signature. - */ - public int getSigBytes () - { - return _sigBytes; - } + /** + * The list of handler parameters. Each element in the List is a List of Strings (which may be + * empty but not null) representing parameters to be passed to the module. List elements are in + * one-to-one correspondence with _handler. + */ + protected List> _handlerParams; - /** - * Returns the character encoding specified by the config file. - */ - public String getEncoding () - { - return _encoding; - } + private String _init; + private List _param; + private String _tempDir; + private String _mixVsn; + private String _encoding; + private String _logLevel; + private int _bufferSize; + private String _jhoveHome; + private int _sigBytes; - /** - * Returns the buffer size specified in the config file. - * - * @return the buffer size, or -1 if none specified - */ - public int getBufferSize () - { - return _bufferSize; - } + protected boolean _isHandler; - /** - * Returns the path to the application's home directory, - * with final path separator. - */ - public String getJhoveHome () - { - return _jhoveHome; - } - - /** - * Returns the name of the desired log level. This should be the - * name of one of the predefined values of java.util.logging.Level, - * e.g., "WARNING", "INFO", "ALL". The default level is SEVERE. - */ - public String getLogLevel () - { - return _logLevel; - } + /* _isModule is protected rather than private so that subclasses + * can add elements to the module element. */ + protected boolean _isModule; + private boolean _isTempDir; + private boolean _isMixVsn; + private boolean _isEncoding; + private boolean _isBufferSize; + private boolean _isJhoveHome; + private boolean _isLogLevel; + private boolean _isSigBytes; - /****************************************************************** - * SAX parser methods. - ******************************************************************/ - - /** - * SAX parser callback method. - */ - @Override - public void startElement (String namespaceURI, String localName, - String rawName, Attributes atts) - { - _content = new StringBuffer (); - - if ("module".equals (rawName)) { - _isModule = true; - _init = null; - _param = new ArrayList<> (1); - _class = null; - } - else if ("outputHandler".equals (rawName)) { - _isHandler = true; - _init = null; - _param = new ArrayList<> (1); - _class = null; - } - else if ("tempDirectory".equals (rawName)) { - _isTempDir = true; - } - else if ("mixVersion".equals (rawName)) { - _isMixVsn = true; - } - else if ("defaultEncoding".equals (rawName)) { - _isEncoding = true; - } - else if ("bufferSize".equals (rawName)) { - _isBufferSize = true; - } - else if ("jhoveHome".equals (rawName)) { - _isJhoveHome = true; - } - else if ("logLevel".equals (rawName)) { - _isLogLevel = true; - } - else if ("sigBytes".equals (rawName)) { - _isSigBytes = true; - } - } + /** + * The list of modules. Each element in the List is an array of two Strings representing the class + * and the initialization string. + */ + protected List _module; + + /** + * The list of module parameters. Each element in the List is a List of Strings (which may be + * empty but not null) representing parameters to be passed to the module. List elements are in + * one-to-one correspondence with _module. + */ + protected List> _modParams; + + /** + * **************************************************************** CLASS CONSTRUCTOR. + * **************************************************************** + */ - /** - * SAX parser callback method. - */ - @Override - public void characters (char [] ch, int start, int length) - { - _content.append (ch, start, length); + /** Creates a ConfigHandler. */ + public ConfigHandler() { + _module = new ArrayList<>(); + _handler = new ArrayList<>(); + _modParams = new ArrayList<>(); + _handlerParams = new ArrayList<>(); + + _isModule = false; + _isHandler = false; + _isTempDir = false; + _isEncoding = false; + _isBufferSize = false; + _isJhoveHome = false; + _isLogLevel = false; + + _bufferSize = -1; + _encoding = null; + _tempDir = null; + _mixVsn = null; + _sigBytes = 1024; + _logLevel = null; + } + + /** + * **************************************************************** PUBLIC INSTANCE METHODS. + * + *

Accessor methods. **************************************************************** + */ + + /** + * Returns the List of Modules specified by the config file. Each element of the List is a + * String[2] whose elements are the module class name and initialization value. + * + * @see Module + */ + public List getModule() { + return _module; + } + + /** + * Returns the List of module parameters specified by the config file. Each element of the List is + * a List (possibly empty) of Strings whose elements are parameters to pass to the module. The + * values returned by getModuleParams() are in one-to-one correspondence with those + * return by getModule(). + */ + public List> getModuleParams() { + return _modParams; + } + + /** + * Returns the List of handler parameters specified by the config file. Each element of the List + * is a List (possibly empty) of Strings whose elements are parameters to pass to the output + * handler. The values returned by getHandlerParams() are in one-to-one + * correspondence with those return by getHandler(). + */ + public List> getHandlerParams() { + return _handlerParams; + } + + /** + * Returns the List of OutputHandlers specified by the config file. + * + * @see OutputHandler + */ + public List getHandler() { + return _handler; + } + + /** + * Returns the temporary directory path specified by the config file, with final path separator. + */ + public String getTempDir() { + return _tempDir; + } + + /** + * Returns the MIX schema version specified by the config file. Acceptable values are "0.2" and + * "1.0" and "2.0". + */ + public String getMixVsn() { + return _mixVsn; + } + + /** + * Returns the number of bytes to examine when looking for an indefinitely positioned signature, + * or checking the first sigBytes bytes of a file in lieu of a signature. + */ + public int getSigBytes() { + return _sigBytes; + } + + /** Returns the character encoding specified by the config file. */ + public String getEncoding() { + return _encoding; + } + + /** + * Returns the buffer size specified in the config file. + * + * @return the buffer size, or -1 if none specified + */ + public int getBufferSize() { + return _bufferSize; + } + + /** Returns the path to the application's home directory, with final path separator. */ + public String getJhoveHome() { + return _jhoveHome; + } + + /** + * Returns the name of the desired log level. This should be the name of one of the predefined + * values of java.util.logging.Level, e.g., "WARNING", "INFO", "ALL". The default level is SEVERE. + */ + public String getLogLevel() { + return _logLevel; + } + + /** + * **************************************************************** SAX parser methods. + * **************************************************************** + */ + + /** SAX parser callback method. */ + @Override + public void startElement(String namespaceURI, String localName, String rawName, Attributes atts) { + _content = new StringBuffer(); + + if ("module".equals(rawName)) { + _isModule = true; + _init = null; + _param = new ArrayList<>(1); + _class = null; + } else if ("outputHandler".equals(rawName)) { + _isHandler = true; + _init = null; + _param = new ArrayList<>(1); + _class = null; + } else if ("tempDirectory".equals(rawName)) { + _isTempDir = true; + } else if ("mixVersion".equals(rawName)) { + _isMixVsn = true; + } else if ("defaultEncoding".equals(rawName)) { + _isEncoding = true; + } else if ("bufferSize".equals(rawName)) { + _isBufferSize = true; + } else if ("jhoveHome".equals(rawName)) { + _isJhoveHome = true; + } else if ("logLevel".equals(rawName)) { + _isLogLevel = true; + } else if ("sigBytes".equals(rawName)) { + _isSigBytes = true; } + } + + /** SAX parser callback method. */ + @Override + public void characters(char[] ch, int start, int length) { + _content.append(ch, start, length); + } - /** - * SAX parser callback method. - */ - @Override - public void endElement (String namespaceURI, String localName, - String rawName) - { - if (_isModule) { - if ("class".equals (rawName)) { - _class = _content.toString (); - } - else if ("init".equals (rawName)) { - _init = _content.toString (); - } - else if ("param".equals (rawName)) { - _param.add (_content.toString ()); - } - else if ("module".equals (rawName)) { - ModuleInfo modInfo = new ModuleInfo( _class, _init); - _module.add (modInfo); - _modParams.add (_param); - _isModule = false; - } - } - else if (_isHandler) { - if ("class".equals (rawName)) { - _class = _content.toString (); - } - else if ("init".equals (rawName)) { - _init = _content.toString (); - } - else if ("param".equals (rawName)) { - _param.add (_content.toString ()); - } - else if ("outputHandler".equals (rawName)) { - String [] tuple = { _class, _init }; - _handler.add (tuple); - _handlerParams.add (_param); - _isHandler = false; - } - } - else if (_isTempDir) { - _tempDir = _content.toString ().trim (); - _isTempDir = false; - } - else if (_isMixVsn) { - _mixVsn = _content.toString ().trim (); - _isMixVsn = false; - } - else if (_isSigBytes) { - try { - _sigBytes = Integer.parseInt (_content.toString ().trim ()); - } - catch (NumberFormatException e) {} - _isSigBytes = false; - } - else if (_isEncoding) { - _encoding = _content.toString ().trim (); - _isEncoding = false; - } - else if (_isJhoveHome) { - _jhoveHome = _content.toString ().trim (); - _isJhoveHome = false; - } - else if (_isLogLevel) { - _logLevel = _content.toString ().trim (); - _isLogLevel = false; - } - else if (_isBufferSize) { - try { - _bufferSize = Integer.parseInt (_content.toString ().trim ()); - } - catch (NumberFormatException e) { - /* Just ignore a malformed number */ - } - _isBufferSize = false; - } + /** SAX parser callback method. */ + @Override + public void endElement(String namespaceURI, String localName, String rawName) { + if (_isModule) { + if ("class".equals(rawName)) { + _class = _content.toString(); + } else if ("init".equals(rawName)) { + _init = _content.toString(); + } else if ("param".equals(rawName)) { + _param.add(_content.toString()); + } else if ("module".equals(rawName)) { + ModuleInfo modInfo = new ModuleInfo(_class, _init); + _module.add(modInfo); + _modParams.add(_param); + _isModule = false; + } + } else if (_isHandler) { + if ("class".equals(rawName)) { + _class = _content.toString(); + } else if ("init".equals(rawName)) { + _init = _content.toString(); + } else if ("param".equals(rawName)) { + _param.add(_content.toString()); + } else if ("outputHandler".equals(rawName)) { + String[] tuple = {_class, _init}; + _handler.add(tuple); + _handlerParams.add(_param); + _isHandler = false; + } + } else if (_isTempDir) { + _tempDir = _content.toString().trim(); + _isTempDir = false; + } else if (_isMixVsn) { + _mixVsn = _content.toString().trim(); + _isMixVsn = false; + } else if (_isSigBytes) { + try { + _sigBytes = Integer.parseInt(_content.toString().trim()); + } catch (NumberFormatException e) { + } + _isSigBytes = false; + } else if (_isEncoding) { + _encoding = _content.toString().trim(); + _isEncoding = false; + } else if (_isJhoveHome) { + _jhoveHome = _content.toString().trim(); + _isJhoveHome = false; + } else if (_isLogLevel) { + _logLevel = _content.toString().trim(); + _isLogLevel = false; + } else if (_isBufferSize) { + try { + _bufferSize = Integer.parseInt(_content.toString().trim()); + } catch (NumberFormatException e) { + /* Just ignore a malformed number */ + } + _isBufferSize = false; } - - /** EntityResolver designed to locate the config schema. It tries to find it - * as a local resource. - * - * It appears that not all SAX implementations will actually call this - * function for schema resolution, so this isn't a guarantee that the - * schema in the config file won't be called directly. But hopefully - * it will cut down on the burden on the server with the official - * schema copy. - */ - @Override - public InputSource resolveEntity (String publicId, String systemId) - throws SAXException, IOException { - if (systemId.endsWith (configSchemaName)) { - try { - URL resURL = this.getClass().getResource(configSchemaName); - InputStream strm = resURL.openStream (); - return new InputSource (strm); - } - catch (Exception e) {} - } - // If we couldn't get the local resource, use default location methods - return super.resolveEntity (publicId, systemId); + } + + /** + * EntityResolver designed to locate the config schema. It tries to find it as a local resource. + * + *

It appears that not all SAX implementations will actually call this function for schema + * resolution, so this isn't a guarantee that the schema in the config file won't be called + * directly. But hopefully it will cut down on the burden on the server with the official schema + * copy. + */ + @Override + public InputSource resolveEntity(String publicId, String systemId) + throws SAXException, IOException { + if (systemId.endsWith(configSchemaName)) { + try { + URL resURL = this.getClass().getResource(configSchemaName); + InputStream strm = resURL.openStream(); + return new InputSource(strm); + } catch (Exception e) { + } } + // If we couldn't get the local resource, use default location methods + return super.resolveEntity(publicId, systemId); + } } diff --git a/jhove-core/src/main/java/edu/harvard/hul/ois/jhove/CoreMessageConstants.java b/jhove-core/src/main/java/edu/harvard/hul/ois/jhove/CoreMessageConstants.java index 6654c05f8..4242ffd0d 100644 --- a/jhove-core/src/main/java/edu/harvard/hul/ois/jhove/CoreMessageConstants.java +++ b/jhove-core/src/main/java/edu/harvard/hul/ois/jhove/CoreMessageConstants.java @@ -1,43 +1,43 @@ package edu.harvard.hul.ois.jhove; /** - * @author Carl Wilson - * carlwilson AT github - * + * @author Carl Wilson carlwilson AT github * @version 0.1 - * - * Created 2 Nov 2017:09:24:33 + *

Created 2 Nov 2017:09:24:33 */ - public enum CoreMessageConstants { - INSTANCE; + INSTANCE; - public static final String EXC_CHAR_ENC_UNSPPTD = "Unsupported character encoding: "; - public static final String EXC_CONF_FILE_LOC_MISS = "Initialization exception; location not specified for configuration file."; - public static final String EXC_CONF_FILE_INVAL = "Use -c to specify a configuration file. Path not found or not readable: "; - public static final String EXC_CONF_FILE_UNRDBL = "Cannot read configuration file: "; - public static final String EXC_CONF_FILE_UNPRS = "Error parsing configuration file: "; - public static final String EXC_FILE_OPEN = "Cannot open output file: "; - public static final String EXC_HNDL_INST_FAIL = "Cannot instantiate handler: "; - public static final String EXC_JAVA_VER_INCMPT = "Java 1.8 or higher is required"; - public static final String EXC_MODL_INST_FAIL = "Cannot instantiate module: "; - public static final String EXC_PRV_CNSTRCT = "Entered private constructor for: "; - public static final String EXC_PROP_VAL_NULL = "Null value not permitted for property: "; - public static final String EXC_SAX_PRSR_MISS = "SAX parser not found: "; - public static final String EXC_PROP_CLSS_INCMPT = "Incompatible class for property: "; - public static final String EXC_SCL_PROP_CLSS_INCMPT = "Scalar."; - public static final String EXC_MAP_PROP_CLSS_INCMPT = "Map."; - public static final String EXC_SET_PROP_CLSS_INCMPT = "Set."; - public static final String EXC_LIST_PROP_CLSS_INCMPT = "List."; - public static final String EXC_TEMP_FILE_CRT = "Cannot create temporary file"; - public static final String EXC_URI_CONV_FAIL = "Cannot convert URI to URL: "; - public static final String EXC_URL_NOT_FND = "URL not found: "; - public static final String EXC_UNEXPECTED = "Validation ended prematurely due to an unhandled exception."; + public static final String EXC_CHAR_ENC_UNSPPTD = "Unsupported character encoding: "; + public static final String EXC_CONF_FILE_LOC_MISS = + "Initialization exception; location not specified for configuration file."; + public static final String EXC_CONF_FILE_INVAL = + "Use -c to specify a configuration file. Path not found or not readable: "; + public static final String EXC_CONF_FILE_UNRDBL = "Cannot read configuration file: "; + public static final String EXC_CONF_FILE_UNPRS = "Error parsing configuration file: "; + public static final String EXC_FILE_OPEN = "Cannot open output file: "; + public static final String EXC_HNDL_INST_FAIL = "Cannot instantiate handler: "; + public static final String EXC_JAVA_VER_INCMPT = "Java 1.8 or higher is required"; + public static final String EXC_MODL_INST_FAIL = "Cannot instantiate module: "; + public static final String EXC_PRV_CNSTRCT = "Entered private constructor for: "; + public static final String EXC_PROP_VAL_NULL = "Null value not permitted for property: "; + public static final String EXC_SAX_PRSR_MISS = "SAX parser not found: "; + public static final String EXC_PROP_CLSS_INCMPT = "Incompatible class for property: "; + public static final String EXC_SCL_PROP_CLSS_INCMPT = "Scalar."; + public static final String EXC_MAP_PROP_CLSS_INCMPT = "Map."; + public static final String EXC_SET_PROP_CLSS_INCMPT = "Set."; + public static final String EXC_LIST_PROP_CLSS_INCMPT = "List."; + public static final String EXC_TEMP_FILE_CRT = "Cannot create temporary file"; + public static final String EXC_URI_CONV_FAIL = "Cannot convert URI to URL: "; + public static final String EXC_URL_NOT_FND = "URL not found: "; + public static final String EXC_UNEXPECTED = + "Validation ended prematurely due to an unhandled exception."; - public static final String ERR_APP_PROP_MISS = "No application properties found for: "; - public static final String ERR_ICC_PRFL_DESC_MISS = "No description in ICC profile v4"; - public static final String ERR_FILE_NOT_FOUND = "File not found"; - public static final String ERR_FILE_READ = "File cannot be read"; + public static final String ERR_APP_PROP_MISS = "No application properties found for: "; + public static final String ERR_ICC_PRFL_DESC_MISS = "No description in ICC profile v4"; + public static final String ERR_FILE_NOT_FOUND = "File not found"; + public static final String ERR_FILE_READ = "File cannot be read"; - public static final String INF_FILE_EMPTY = "Zero-length file"; + public static final String INF_FILE_EMPTY = "Zero-length file"; } diff --git a/jhove-core/src/main/java/edu/harvard/hul/ois/jhove/CountedInputStream.java b/jhove-core/src/main/java/edu/harvard/hul/ois/jhove/CountedInputStream.java index dc7682c77..3535ff34b 100644 --- a/jhove-core/src/main/java/edu/harvard/hul/ois/jhove/CountedInputStream.java +++ b/jhove-core/src/main/java/edu/harvard/hul/ois/jhove/CountedInputStream.java @@ -1,116 +1,98 @@ -/********************************************************************** - * Jhove - JSTOR/Harvard Object Validation Environment - * Copyright 2004 by JSTOR and the President and Fellows of Harvard College - **********************************************************************/ - +/** + * ******************************************************************** Jhove - JSTOR/Harvard Object + * Validation Environment Copyright 2004 by JSTOR and the President and Fellows of Harvard College + * ******************************************************************** + */ package edu.harvard.hul.ois.jhove; import java.io.*; /** - * A FilterInputStream which passes only a specified - * number of bytes, then returns an EOF condition. + * A FilterInputStream which passes only a specified number of bytes, then returns an EOF condition. * * @author Gary McGath - * */ public class CountedInputStream extends FilterInputStream { - private int bytesLeft; - - /** - * @param instrm The InputStream being counted - * @param count The number of bytes to be allowed - */ - public CountedInputStream(InputStream instrm, int count) { - super(instrm); - bytesLeft = count; - } + private int bytesLeft; + /** + * @param instrm The InputStream being counted + * @param count The number of bytes to be allowed + */ + public CountedInputStream(InputStream instrm, int count) { + super(instrm); + bytesLeft = count; + } - /** Reads a single byte from the stream and decrements - * the count of remaining bytes. If the count - * is exhausted, returns -1 to signify end of file. */ - @Override - public int read() throws IOException { - if (bytesLeft <= 0) { - return -1; - } - int ch = super.read (); - if (ch != -1) { - --bytesLeft; - } - return ch; + /** + * Reads a single byte from the stream and decrements the count of remaining bytes. If the count + * is exhausted, returns -1 to signify end of file. + */ + @Override + public int read() throws IOException { + if (bytesLeft <= 0) { + return -1; } - - - /** - * Reads some number of bytes from the input stream and - * stores them into the buffer array b. The number of - * bytes actually read is returned as an integer. - * - * The number of bytes read will not exceed the number - * of bytes remaining in the count. The count is - * decremented by the number of bytes actually read. - */ - @Override - public int read(byte[] b) throws IOException - { - int len = b.length; - int bytesRead; - if (len <= bytesLeft) { - // Limit doesn't affect us, do normal read - bytesRead = super.read (b); - } - else { - // Limit the read to bytesLeft - bytesRead = super.read (b, 0, bytesLeft); - } - bytesLeft -= bytesRead; - return bytesRead; + int ch = super.read(); + if (ch != -1) { + --bytesLeft; } + return ch; + } + /** + * Reads some number of bytes from the input stream and stores them into the buffer array b. The + * number of bytes actually read is returned as an integer. + * + *

The number of bytes read will not exceed the number of bytes remaining in the count. The + * count is decremented by the number of bytes actually read. + */ + @Override + public int read(byte[] b) throws IOException { + int len = b.length; + int bytesRead; + if (len <= bytesLeft) { + // Limit doesn't affect us, do normal read + bytesRead = super.read(b); + } else { + // Limit the read to bytesLeft + bytesRead = super.read(b, 0, bytesLeft); + } + bytesLeft -= bytesRead; + return bytesRead; + } - /** - * Reads up to len bytes of data from the input stream - * into an array of bytes. An attempt is made to read as - * many as len bytes, but a smaller number may be read, - * possibly zero. The number of bytes actually read is - * returned as an integer. - * - * The number of bytes read will not exceed the number - * of bytes remaining in the count. The count is - * decremented by the number of bytes actually read. - */ - @Override - public int read(byte[] b, int off, int len) throws IOException - { - int bytesRead; - if (len <= bytesLeft) { - // Limit doesn't affect us, do normal read - bytesRead = super.read (b, off, len); - } - else { - bytesRead = super.read (b, off, bytesLeft); - } - bytesLeft -= bytesRead; - return bytesRead; + /** + * Reads up to len bytes of data from the input stream into an array of bytes. An attempt is made + * to read as many as len bytes, but a smaller number may be read, possibly zero. The number of + * bytes actually read is returned as an integer. + * + *

The number of bytes read will not exceed the number of bytes remaining in the count. The + * count is decremented by the number of bytes actually read. + */ + @Override + public int read(byte[] b, int off, int len) throws IOException { + int bytesRead; + if (len <= bytesLeft) { + // Limit doesn't affect us, do normal read + bytesRead = super.read(b, off, len); + } else { + bytesRead = super.read(b, off, bytesLeft); } + bytesLeft -= bytesRead; + return bytesRead; + } - /** - * Skips n bytes. - * Decrements the count by the number of bytes - * actually skipped. - */ - @Override - public long skip (long n) throws IOException { - long bytesRead = super.skip (n); - if (bytesLeft < bytesRead) { - bytesLeft = 0; - } - else { - bytesLeft -= bytesRead; - } - return bytesRead; + /** Skips n bytes. Decrements the count by the number of bytes actually skipped. */ + @Override + public long skip(long n) throws IOException { + long bytesRead = super.skip(n); + if (bytesLeft < bytesRead) { + bytesLeft = 0; + } else { + bytesLeft -= bytesRead; } + return bytesRead; + } } diff --git a/jhove-core/src/main/java/edu/harvard/hul/ois/jhove/Document.java b/jhove-core/src/main/java/edu/harvard/hul/ois/jhove/Document.java index bf3e6b46f..c3538807e 100644 --- a/jhove-core/src/main/java/edu/harvard/hul/ois/jhove/Document.java +++ b/jhove-core/src/main/java/edu/harvard/hul/ois/jhove/Document.java @@ -1,194 +1,137 @@ -/********************************************************************** - * Jhove - JSTOR/Harvard Object Validation Environment - * Copyright 2003 by JSTOR and the President and Fellows of Harvard College - **********************************************************************/ - +/** + * ******************************************************************** Jhove - JSTOR/Harvard Object + * Validation Environment Copyright 2003 by JSTOR and the President and Fellows of Harvard College + * ******************************************************************** + */ package edu.harvard.hul.ois.jhove; import java.util.*; -/** - * This class encapsulates information about format specification documents. +/** + * This class encapsulates information about format specification documents. * - * @see DocumentType + * @see DocumentType */ -public class Document -{ - private List _author; - private String _date; - private String _edition; - private String _enum; - private List _identifier; - private String _note; - private String _pages; - private List _publisher; - private String _title; - private DocumentType _type; - - /** - * Creates a Document with a given title and one of the predefined - * DocumentTypes. - */ - public Document (String title, DocumentType type) - { +public class Document { + private List _author; + private String _date; + private String _edition; + private String _enum; + private List _identifier; + private String _note; + private String _pages; + private List _publisher; + private String _title; + private DocumentType _type; + + /** Creates a Document with a given title and one of the predefined DocumentTypes. */ + public Document(String title, DocumentType type) { _title = title; - _type = type; - - _author = new ArrayList<> (); - _identifier = new ArrayList<> (); - _publisher = new ArrayList<> (); - } - - /** - * Returns a List of Agents, each representing an author of this - * Document. - * Returns an empty list if no authors have been listed. - * - * @see Agent - */ - public List getAuthor () - { + _type = type; + + _author = new ArrayList<>(); + _identifier = new ArrayList<>(); + _publisher = new ArrayList<>(); + } + + /** + * Returns a List of Agents, each representing an author of this Document. Returns an empty list + * if no authors have been listed. + * + * @see Agent + */ + public List getAuthor() { return _author; - } - - /** - * Returns the date of this Document - */ - public String getDate () - { + } + + /** Returns the date of this Document */ + public String getDate() { return _date; - } + } - /** - * Returns informaton on the edition of this Document - */ - public String getEdition () - { + /** Returns informaton on the edition of this Document */ + public String getEdition() { return _edition; - } - - /** - * Returns the enumeration (e.g., serial volume and number) - * of this Document - */ - public String getEnumeration () - { + } + + /** Returns the enumeration (e.g., serial volume and number) of this Document */ + public String getEnumeration() { return _enum; - } - - /** - * Returns the list of formal Identifiers for this Document. - * If no Identifiers are given, returns an empty list. - */ - public List getIdentifier () - { + } + + /** + * Returns the list of formal Identifiers for this Document. If no Identifiers are given, returns + * an empty list. + */ + public List getIdentifier() { return _identifier; - } + } - /** - * Returns the note associated with this Document - */ - public String getNote () - { + /** Returns the note associated with this Document */ + public String getNote() { return _note; - } + } - /** - * Returns pagination information for this Document - */ - public String getPages () - { + /** Returns pagination information for this Document */ + public String getPages() { return _pages; - } - - /** - * Returns a List of Agents, each representing a publisher of this - * Document. If no publishers are listed, returns an empty list. - */ - public List getPublisher () - { + } + + /** + * Returns a List of Agents, each representing a publisher of this Document. If no publishers are + * listed, returns an empty list. + */ + public List getPublisher() { return _publisher; - } + } - /** - * Returns the title of this Document - */ - public String getTitle () - { + /** Returns the title of this Document */ + public String getTitle() { return _title; - } - - /** - * Returns one of the predefined DocumentTypes as the type of - * this Document - */ - public DocumentType getType () - { + } + + /** Returns one of the predefined DocumentTypes as the type of this Document */ + public DocumentType getType() { return _type; - } - - /** - * Adds an author to the list of authors - */ - public void setAuthor (Agent author) - { - _author.add (author); - } - - /** - * Sets the date of this Document - */ - public void setDate (String date) - { + } + + /** Adds an author to the list of authors */ + public void setAuthor(Agent author) { + _author.add(author); + } + + /** Sets the date of this Document */ + public void setDate(String date) { _date = date; - } + } - /** - * Sets edition information for this Document - */ - public void setEdition (String edition) - { + /** Sets edition information for this Document */ + public void setEdition(String edition) { _edition = edition; - } - - /** - * Sets enumeration information (e.g., serial volume and number) - * for this Document - */ - public void setEnumeration (String enm) - { + } + + /** Sets enumeration information (e.g., serial volume and number) for this Document */ + public void setEnumeration(String enm) { _enum = enm; - } - - /** - * Adds an Identifier to the list of identifiers - */ - public void setIdentifier (Identifier identifier) - { - _identifier.add (identifier); - } - - /** - * Sets a note giving additional information about this Document - */ - public void setNote (String note) - { + } + + /** Adds an Identifier to the list of identifiers */ + public void setIdentifier(Identifier identifier) { + _identifier.add(identifier); + } + + /** Sets a note giving additional information about this Document */ + public void setNote(String note) { _note = note; - } + } - /** - * Sets pagination information for this Document - */ - public void setPages (String pages) - { + /** Sets pagination information for this Document */ + public void setPages(String pages) { _pages = pages; - } - - /** - * Adds a publisher to the list of publishers - */ - public void setPublisher (Agent publisher) - { - _publisher.add (publisher); - } + } + + /** Adds a publisher to the list of publishers */ + public void setPublisher(Agent publisher) { + _publisher.add(publisher); + } } diff --git a/jhove-core/src/main/java/edu/harvard/hul/ois/jhove/DocumentType.java b/jhove-core/src/main/java/edu/harvard/hul/ois/jhove/DocumentType.java index a2cba390a..0ba16d8b2 100644 --- a/jhove-core/src/main/java/edu/harvard/hul/ois/jhove/DocumentType.java +++ b/jhove-core/src/main/java/edu/harvard/hul/ois/jhove/DocumentType.java @@ -1,43 +1,41 @@ -/********************************************************************** - * Jhove - JSTOR/Harvard Object Validation Environment - * Copyright 2003 by JSTOR and the President and Fellows of Harvard College - **********************************************************************/ - +/** + * ******************************************************************** Jhove - JSTOR/Harvard Object + * Validation Environment Copyright 2003 by JSTOR and the President and Fellows of Harvard College + * ******************************************************************** + */ package edu.harvard.hul.ois.jhove; /** - * This class defines enumerated types for a Document. - * Applications will not create or modify DocumentTypes, but will - * use one of the predefined DocumentType instances - * ARTICLE, BOOK, REPORT, RFC, STANDARD, WEB, or OTHER. + * This class defines enumerated types for a Document. Applications will not create or modify + * DocumentTypes, but will use one of the predefined DocumentType instances ARTICLE, BOOK, REPORT, + * RFC, STANDARD, WEB, or OTHER. * * @see Document - * */ public enum DocumentType { - /** Document type for a printed article. */ - ARTICLE("Article"), - /** Document type for an book. */ - BOOK("Book"), - /** Document type for a report. */ - REPORT("Report"), - /** Document type for an IETF Request for Comment. */ - RFC("RFC"), - /** Document type for a standards body publication. */ - STANDARD("Standard"), - /** Document type for a Web page. */ - WEB("Web"), - /** Document type that doesn't fit the other categories. */ - OTHER("Other"); - /** A String name for the type, used for reporting. */ - public final String name; + /** Document type for a printed article. */ + ARTICLE("Article"), + /** Document type for an book. */ + BOOK("Book"), + /** Document type for a report. */ + REPORT("Report"), + /** Document type for an IETF Request for Comment. */ + RFC("RFC"), + /** Document type for a standards body publication. */ + STANDARD("Standard"), + /** Document type for a Web page. */ + WEB("Web"), + /** Document type that doesn't fit the other categories. */ + OTHER("Other"); + /** A String name for the type, used for reporting. */ + public final String name; - private DocumentType(final String name) { - this.name = name; - } + private DocumentType(final String name) { + this.name = name; + } - @Override - public String toString() { - return this.name; - } + @Override + public String toString() { + return this.name; + } } diff --git a/jhove-core/src/main/java/edu/harvard/hul/ois/jhove/Dump.java b/jhove-core/src/main/java/edu/harvard/hul/ois/jhove/Dump.java index 990886ead..bf9912630 100644 --- a/jhove-core/src/main/java/edu/harvard/hul/ois/jhove/Dump.java +++ b/jhove-core/src/main/java/edu/harvard/hul/ois/jhove/Dump.java @@ -1,134 +1,126 @@ -/********************************************************************** - * JHOVE - JSTOR/Harvard Object Validation Environment - * Copyright 2004 by the President and Fellows of Harvard College +/** + * ******************************************************************** JHOVE - JSTOR/Harvard Object + * Validation Environment Copyright 2004 by the President and Fellows of Harvard College * - * This program 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 2 of the License, or (at - * your option) any later version. - * - * This program 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. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 - * USA - **********************************************************************/ - + *

This program 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 2 + * of the License, or (at your option) any later version. + * + *

This program 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. + * + *

You should have received a copy of the GNU Lesser General Public License along with this + * program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA ******************************************************************** + */ package edu.harvard.hul.ois.jhove; import java.io.*; -/** - * Common methods for dump utilities. - */ -public class Dump -{ - /****************************************************************** - * CLASS CONSTRUCTOR. - ******************************************************************/ - public Dump () - { - } +/** Common methods for dump utilities. */ +public class Dump { + /** + * **************************************************************** CLASS CONSTRUCTOR. + * **************************************************************** + */ + public Dump() {} - /****************************************************************** - * PUBLIC CLASS METHODS. - ******************************************************************/ + /** + * **************************************************************** PUBLIC CLASS METHODS. + * **************************************************************** + */ - /** - * Return leading characters to pad out the byte offset to field width. - * @param os Byte offset - * @param width Field width - * @return String of leading zeros - */ - protected static String leading (int os, int width) - { - return leading (os, width, '0'); - } + /** + * Return leading characters to pad out the byte offset to field width. + * + * @param os Byte offset + * @param width Field width + * @return String of leading zeros + */ + protected static String leading(int os, int width) { + return leading(os, width, '0'); + } - /** - * Return leading characters to pad out the byte offset to field width. - * @param os Byte offset - * @param width Field width - * @return String of leading zeros - */ - protected static String leading (long os, int width) - { - return leading (os, width, '0'); - } + /** + * Return leading characters to pad out the byte offset to field width. + * + * @param os Byte offset + * @param width Field width + * @return String of leading zeros + */ + protected static String leading(long os, int width) { + return leading(os, width, '0'); + } - /** - * Return leading characters to pad out the byte offset to field width. - * @param os Byte offset - * @param width Field width - * @param pad Padding character - * @return String of leading characters - */ - protected static String leading (int os, int width, char pad) - { - return leading ((long) os, width, pad); - } + /** + * Return leading characters to pad out the byte offset to field width. + * + * @param os Byte offset + * @param width Field width + * @param pad Padding character + * @return String of leading characters + */ + protected static String leading(int os, int width, char pad) { + return leading((long) os, width, pad); + } - /** - * Return leading characters to pad out the byte offset to field width. - * @param os Byte offset - * @param width Field width - * @param pad Padding character - * @return String of leading characters - */ - protected static String leading (long os, int width, char pad) - { - String ss = Long.toString (os); - StringBuffer buffer = new StringBuffer (); - for (int j=0; j _defaultParams; - /** JHOVE engine. */ - protected JhoveBase _je; - /** Indentation level */ - protected int _level; - /** Handler name */ - protected String _name; - /** Handler note */ - protected String _note; - /** Handler release description. */ - protected String _release; - /** Handler-specific parameter. */ - protected String _param; - /** Copyright notice */ - protected String _rights; - /** Handler specification document list */ - protected List _specification; - /** Handler vendor */ - protected Agent _vendor; - /** Writer for doing output */ - protected PrintWriter _writer; - /** Logger for a handler class. */ - protected Logger _logger; - - /****************************************************************** - * CLASS CONSTRUCTOR. - ******************************************************************/ - - /** - * Constructors of all subclasses of HandlerBase should call - * this as a super constructor. - * - * @param name Name of the handler - * @param release Release identifier - * @param date Last modification date of the handler code, - * in the form of an array of three numbers. - * date[0] is the year, - * date[1] the month, and - * date[2] the day. - * @param note Additional information about the handler - * (may be null) - * @param rights Copyright notice for the handler - */ - protected HandlerBase (String name, String release, int [] date, - String note, String rights) - { - // Though we're actually in the jhove package, all the related - // action logically belongs in the handler package, so we name - // this logger accordingly. - _logger = Logger.getLogger ("edu.harvard.hul.ois.jhove.handler"); - _logger.info ("Initializing " + name); - _name = name; - _release = release; - _encoding = "UTF-8"; - - Calendar calendar = new GregorianCalendar (); - calendar.set (date[0], date[1]-1, date[2]); - _date = calendar.getTime (); - - _note = note; - _rights = rights; - _specification = new ArrayList<> (); - - _level = -1; - } - - /****************************************************************** - * PUBLIC INSTANCE METHODS. - * - * Initialization methods. - ******************************************************************/ - - /** - * Reset the handler. This needs to be called before each invocation. - */ - @Override - public void reset () { - _level = -1; - } - - /** - * Set a a List of default parameters for the module. - * - * @param params A List whose elements are Strings. - * May be empty. - */ - @Override - public void setDefaultParams (List params) - { - _defaultParams = params; - } - - /** - * Applies the default parameters. - * Calling this clears any prior parameters. - */ - @Override - public void applyDefaultParams () - { - resetParams (); - Iterator iter = _defaultParams.iterator (); - while (iter.hasNext ()) { - String parm = iter.next (); - param (parm); - } - } - - /** Reset parameter settings. - * Returns to a default state without any parameters. - * The default method clears the saved parameter. - */ - @Override - public void resetParams () - { - _param = null; - } - - - /** - * Per-instantiation initialization. - * The default method does nothing. - */ - @Override - public void init (String init) - { - _init = init; - } - - /** - * Per-action initialization. - * The default method does nothing. - */ - @Override - public void param (String param) - { - _param = param; - } - - /****************************************************************** - * Accessor methods. - ******************************************************************/ - - /** - * Return the last modification date of this OutputHandler, as a - * Java Date object - */ - @Override - public final Date getDate () - { - return _date; - } - - /** - * Return the OutputHandler name - */ - @Override - public final String getName () - { - return _name; - } - - /** - * Return the OutputHandler note - */ - @Override - public final String getNote () - { - return _note; - } - - /** - * Return the release identifier - */ - @Override - public final String getRelease () - { - return _release; - } - - /** - * Return the copyright information string - */ - @Override - public final String getRights () - { - return _rights; - } - - /** - * Returns a list of Document objects (one for each - * specification document). The specification - * list is generated by the OutputHandler, and specifications cannot - * be added by callers. - * - * @see Document - */ - @Override - public final List getSpecification () - { - return _specification; - } - - /** - * Return the vendor information - */ - @Override - public final Agent getVendor () - { - return _vendor; - } - - /** - * Returns this handler's encoding. - */ - @Override - public String getEncoding () - { - return _encoding; - } - - /****************************************************************** - * Mutator methods. - ******************************************************************/ - - /** - * Pass the associated App object to this Module. - * The App makes various services available. - */ - @Override - public final void setApp (App app) - { - _app = app; - } - - /** - * Assigns the JHOVE engine object to provide services to this handler - */ - @Override - public final void setBase (JhoveBase je) - { - _je = je; - } - - /** - * Assigns the encoding to be used by this OutputHandler - */ - @Override - public void setEncoding (String encoding) - { - _encoding = encoding; - } - - /** - * Assigns a PrintWriter to do output for this OutputHandler - */ - @Override - public final void setWriter (PrintWriter writer) - { - _writer = writer; - } - - /****************************************************************** - * Serialization methods. - ******************************************************************/ - - /** - * Callback allowing post-parse, pre-show analysis of object - * representation information. - * @param info Object representation information - */ - @Override - public void analyze (RepInfo info) - { - /* Do nothing, which is sufficient for most handlers. */ +public abstract class HandlerBase implements OutputHandler { + /** + * **************************************************************** PUBLIC CLASS FIELDS. + * **************************************************************** + */ + + /** A DateFormat for representing a Date in yyyy-MM-dd (e.g., 2003-07-31) format. */ + public static SynchronizedDateFormat date = new SynchronizedDateFormat("yyyy-MM-dd"); + + /** + * A DateFormat for representing a Date in yyyy-MM-dd HH:mm:ss z (e.g., 2003-07-31 15:31:12 EDT) + * format. + */ + public static SynchronizedDateFormat dateTime = + new SynchronizedDateFormat("yyyy-MM-dd HH:mm:ss z"); + + /** + * A DateFormat for representing a Date in ISO 8601 (e.g., 2003-07-31T15:31:12-0400) format. We + * subclass SimpleDateFormat to make it thread-safe. + */ + public static SynchronizedDateFormat iso8601 = + new SynchronizedDateFormat("yyyy-MM-dd'T'HH:mm:ssZ"); + + /** + * **************************************************************** PRIVATE INSTANCE FIELDS. + * **************************************************************** + */ + + /** The application object */ + protected App _app; + /** The Jhove engine */ + protected JhoveBase _base; + /** Handler last modification date */ + protected Date _date; + /** Character encoding for writer */ + protected String _encoding; + /** Initialization value. */ + protected String _init; + /** List of default parameters. */ + protected List _defaultParams; + /** JHOVE engine. */ + protected JhoveBase _je; + /** Indentation level */ + protected int _level; + /** Handler name */ + protected String _name; + /** Handler note */ + protected String _note; + /** Handler release description. */ + protected String _release; + /** Handler-specific parameter. */ + protected String _param; + /** Copyright notice */ + protected String _rights; + /** Handler specification document list */ + protected List _specification; + /** Handler vendor */ + protected Agent _vendor; + /** Writer for doing output */ + protected PrintWriter _writer; + /** Logger for a handler class. */ + protected Logger _logger; + + /** + * **************************************************************** CLASS CONSTRUCTOR. + * **************************************************************** + */ + + /** + * Constructors of all subclasses of HandlerBase should call this as a super + * constructor. + * + * @param name Name of the handler + * @param release Release identifier + * @param date Last modification date of the handler code, in the form of an array of three + * numbers. date[0] is the year, date[1] the month, and + * date[2] the day. + * @param note Additional information about the handler (may be null) + * @param rights Copyright notice for the handler + */ + protected HandlerBase(String name, String release, int[] date, String note, String rights) { + // Though we're actually in the jhove package, all the related + // action logically belongs in the handler package, so we name + // this logger accordingly. + _logger = Logger.getLogger("edu.harvard.hul.ois.jhove.handler"); + _logger.info("Initializing " + name); + _name = name; + _release = release; + _encoding = "UTF-8"; + + Calendar calendar = new GregorianCalendar(); + calendar.set(date[0], date[1] - 1, date[2]); + _date = calendar.getTime(); + + _note = note; + _rights = rights; + _specification = new ArrayList<>(); + + _level = -1; + } + + /** + * **************************************************************** PUBLIC INSTANCE METHODS. + * + *

Initialization methods. **************************************************************** + */ + + /** Reset the handler. This needs to be called before each invocation. */ + @Override + public void reset() { + _level = -1; + } + + /** + * Set a a List of default parameters for the module. + * + * @param params A List whose elements are Strings. May be empty. + */ + @Override + public void setDefaultParams(List params) { + _defaultParams = params; + } + + /** Applies the default parameters. Calling this clears any prior parameters. */ + @Override + public void applyDefaultParams() { + resetParams(); + Iterator iter = _defaultParams.iterator(); + while (iter.hasNext()) { + String parm = iter.next(); + param(parm); + } + } + + /** + * Reset parameter settings. Returns to a default state without any parameters. The default method + * clears the saved parameter. + */ + @Override + public void resetParams() { + _param = null; + } + + /** Per-instantiation initialization. The default method does nothing. */ + @Override + public void init(String init) { + _init = init; + } + + /** Per-action initialization. The default method does nothing. */ + @Override + public void param(String param) { + _param = param; + } + + /** + * **************************************************************** Accessor methods. + * **************************************************************** + */ + + /** Return the last modification date of this OutputHandler, as a Java Date object */ + @Override + public final Date getDate() { + return _date; + } + + /** Return the OutputHandler name */ + @Override + public final String getName() { + return _name; + } + + /** Return the OutputHandler note */ + @Override + public final String getNote() { + return _note; + } + + /** Return the release identifier */ + @Override + public final String getRelease() { + return _release; + } + + /** Return the copyright information string */ + @Override + public final String getRights() { + return _rights; + } + + /** + * Returns a list of Document objects (one for each specification document). The + * specification list is generated by the OutputHandler, and specifications cannot be added by + * callers. + * + * @see Document + */ + @Override + public final List getSpecification() { + return _specification; + } + + /** Return the vendor information */ + @Override + public final Agent getVendor() { + return _vendor; + } + + /** Returns this handler's encoding. */ + @Override + public String getEncoding() { + return _encoding; + } + + /** + * **************************************************************** Mutator methods. + * **************************************************************** + */ + + /** Pass the associated App object to this Module. The App makes various services available. */ + @Override + public final void setApp(App app) { + _app = app; + } + + /** Assigns the JHOVE engine object to provide services to this handler */ + @Override + public final void setBase(JhoveBase je) { + _je = je; + } + + /** Assigns the encoding to be used by this OutputHandler */ + @Override + public void setEncoding(String encoding) { + _encoding = encoding; + } + + /** Assigns a PrintWriter to do output for this OutputHandler */ + @Override + public final void setWriter(PrintWriter writer) { + _writer = writer; + } + + /** + * **************************************************************** Serialization methods. + * **************************************************************** + */ + + /** + * Callback allowing post-parse, pre-show analysis of object representation information. + * + * @param info Object representation information + */ + @Override + public void analyze(RepInfo info) { + /* Do nothing, which is sufficient for most handlers. */ + } + + /** Callback indicating a directory is finished being processed. */ + @Override + public void endDirectory() { + /* Do nothing, which is sufficient for most handlers. */ + } + + /** + * Callback to give the handler the opportunity to decide whether or not to process a file. Most + * handlers will always return true. + * + * @param filepath File pathname + */ + @Override + public boolean okToProcess(String filepath) { + return true; + } + + /** Outputs information about a Module */ + @Override + public abstract void show(Module module); + + /** Outputs the information contained in a RepInfo object */ + @Override + public abstract void show(RepInfo info); + + /** Outputs information about the OutputHandler specified in the parameter */ + @Override + public abstract void show(OutputHandler handler); + + /** Outputs minimal information about the application */ + @Override + public abstract void show(); + + /** + * Outputs detailed information about the application, including configuration, available modules + * and handlers, etc. + */ + @Override + public abstract void show(App app); + + /** + * Do the initial output. This should be in a suitable format for including multiple files between + * the header and the footer. + */ + @Override + public abstract void showHeader(); + + /** + * Do the final output. This should be in a suitable format for including multiple files between + * the header and the footer. + */ + @Override + public abstract void showFooter(); + + /** Close the writer after all output has been done. */ + @Override + public void close() { + _writer.close(); + } + + /** + * Callback indicating a new directory is being processed. + * + * @param directory Directory path + */ + @Override + public void startDirectory(String directory) { + /* Do nothing, which is sufficient for most handlers. */ + } + + /** + * **************************************************************** PRIVATE CLASS METHODS. + * + *

XML methods. **************************************************************** + */ + + /** + * Return the XML DOCTYPE instruction. + * + * @param root Root element of the DTD + * @param uri URI of the DTD + */ + protected static String doctype(String root, String uri) { + return doctype(root, null, uri); + } + + /** + * Return the XML DOCTYPE instruction. + * + * @param root Root element of the DTD + * @param name Public name of the DTD + * @param uri URI of the DTD + */ + protected static String doctype(String root, String name, String uri) { + StringBuffer s = new StringBuffer(""); + + return s.toString(); + } + + /** + * Returns, as a String, an empty XML. + * + * @param tag XML tag + */ + protected static String element(String tag) { + return "<" + tag + "/>"; + } + + /** + * Returns, as a String, an XML element with a given tag and content + * + * @param tag An XML tag + * @param content Content string. Characters requiring conversion to entitites will be converted. + */ + protected static String element(String tag, String content) { + return elementStart(tag) + Utils.encodeContent(content) + elementEnd(tag); + } + + /** + * Returns, as a String, an XML element with a given tag and attributes + * + * @param tag An XML tag + * @param attrs An array of String[2] elements, where for each element, attrs[i][0] is the + * attribute key and attrs[i][1] is the attribute value. Null values are skipped. + */ + protected static String element(String tag, String[][] attrs) { + StringBuffer buffer = new StringBuffer("<"); + buffer.append(tag); + for (int i = 0; i < attrs.length; i++) { + if (attrs[i][0] != null && attrs[i][1] != null) { + buffer.append(" "); + buffer.append(attrs[i][0]); + buffer.append("=\""); + buffer.append(Utils.encodeValue(attrs[i][1])); + buffer.append("\""); + } } - - /** - * Callback indicating a directory is finished being processed. - */ - @Override - public void endDirectory () - { - /* Do nothing, which is sufficient for most handlers. */ + buffer.append("/>"); + + return buffer.toString(); + } + + /** + * Returns, as a String, an XML element with a given tag, content and attributes + * + * @param tag An XML tag + * @param content Content string. Characters requiring conversion to entitites will be converted. + * @param attrs An array of String[2] elements, where for each element, attrs[i][0] is the + * attribute key and attrs[i][1] is the attribute value. Null values are skipped. + */ + protected static String element(String tag, String[][] attrs, String content) { + StringBuffer buffer = new StringBuffer("<"); + buffer.append(tag); + for (int i = 0; i < attrs.length; i++) { + if (attrs[i][0] != null && attrs[i][1] != null) { + buffer.append(" "); + buffer.append(attrs[i][0]); + buffer.append("=\""); + buffer.append(Utils.encodeValue(attrs[i][1])); + buffer.append("\""); + } } - - /** - * Callback to give the handler the opportunity to decide whether or - * not to process a file. Most handlers will always return true. - * @param filepath File pathname - */ - @Override - public boolean okToProcess (String filepath) - { - return true; + buffer.append(">"); + buffer.append(Utils.encodeContent(content)); + buffer.append(elementEnd(tag)); + + return buffer.toString(); + } + + /** + * Returns, as a String, the closing tag of an element. No checking is done that opening and + * closing tags match. + * + * @param tag An XML tag + */ + protected static String elementEnd(String tag) { + return ""; + } + + /** + * Returns, as a String, the opening tag of an element. + * + * @param tag An XML tag + */ + protected static String elementStart(String tag) { + return "<" + tag + ">"; + } + + /** + * Returns, as a String, the opening tag of an element with specified attributes. + * + * @param tag An XML tag + * @param attrs An array of String[2] elements, where for each element, attrs[i][0] is the + * attribute key and attrs[i][1] is the attribute value. + */ + protected static String elementStart(String tag, String[][] attrs) { + StringBuffer buffer = new StringBuffer("<"); + buffer.append(tag); + for (int i = 0; i < attrs.length; i++) { + buffer.append(" "); + buffer.append(attrs[i][0]); + buffer.append("=\""); + buffer.append(Utils.encodeValue(attrs[i][1])); + buffer.append("\""); + } + buffer.append(">"); + + return buffer.toString(); + } + + /** Return a canonical XML declaration with default encoding. */ + protected static String xmlDecl() { + return ""; + } + + /** Return a canonical XML declaration with specified encoding. */ + protected static String xmlDecl(String encoding) { + return ""; + } + + /** + * **************************************************************** Nesting level methods. + * **************************************************************** + */ + + /** Returns a String containing a number of spaces equal to the current indent level. */ + protected static String getIndent(int level) { + StringBuffer s = new StringBuffer(); + for (int i = 0; i < level; i++) { + s.append(" "); + } + + return s.toString(); + } + + /** + * **************************************************************** Array methods. + * **************************************************************** + */ + + /** Return String representation of an integer array. */ + protected static String integerArray(int[] iarray) { + return integerArray(iarray, ' '); + } + + /** Return String representation of an integer array with specified separator. */ + protected static String integerArray(int[] iarray, char separator) { + StringBuffer buffer = new StringBuffer(); + for (int i = 0; i < iarray.length; i++) { + if (i > 0) { + buffer.append(separator); + } + buffer.append(Integer.toString(iarray[i])); } + return buffer.toString(); + } - /** - * Outputs information about a Module - */ - @Override - public abstract void show (Module module); - - /** - * Outputs the information contained in a RepInfo object - */ - @Override - public abstract void show (RepInfo info); - - /** - * Outputs information about the OutputHandler specified - * in the parameter - */ - @Override - public abstract void show (OutputHandler handler); - - /** - * Outputs minimal information about the application - */ - @Override - public abstract void show (); - - /** - * Outputs detailed information about the application, - * including configuration, available modules and handlers, - * etc. - */ - @Override - public abstract void show (App app); - - /** - * Do the initial output. This should be in a suitable format - * for including multiple files between the header and the footer. - */ - @Override - public abstract void showHeader (); - - /** - * Do the final output. This should be in a suitable format - * for including multiple files between the header and the footer. - */ - @Override - public abstract void showFooter (); - - /** - * Close the writer after all output has been done. - */ - @Override - public void close () - { - _writer.close (); + /** Return String representation of an array of long with space separator. */ + protected static String longArray(long[] larray) { + StringBuffer buffer = new StringBuffer(); + for (int i = 0; i < larray.length; i++) { + if (i > 0) { + buffer.append(" "); + } + buffer.append(Long.toString(larray[i])); + } + return buffer.toString(); + } + + /** + * Return String representation of an array of Rational, each evaluated as a double, with space + * separator. + */ + protected static String rationalArray(Rational[] rarray) { + StringBuffer buffer = new StringBuffer(); + for (int i = 0; i < rarray.length; i++) { + if (i > 0) { + buffer.append(" "); + } + buffer.append(rarray[i].toDouble()); + } + return buffer.toString(); + } + + /** + * Return String representation of an array of Rational, each as two integers, with space + * separator. + */ + protected static String rationalArray10(Rational[] rarray) { + StringBuffer buffer = new StringBuffer(); + for (int i = 0; i < rarray.length; i++) { + if (i > 0) { + buffer.append(" "); + } + buffer.append(rarray[i].getNumerator()); + buffer.append(" "); + buffer.append(rarray[i].getNumerator()); + } + return buffer.toString(); + } + + /** Return String representation of an array of double. */ + protected static String doubleArray(double[] darray) { + StringBuffer buffer = new StringBuffer(); + for (int i = 0; i < darray.length; i++) { + if (i > 0) { + buffer.append(" "); + } + buffer.append(Double.toString(darray[i])); + } + return buffer.toString(); + } + + /* Text formatting methods. */ + /* Convert a date to the dateTime format used by the + * XML schema. This is ISO 8610 with a colon between + * the hour and minute fields of the time zone. Unfortunately, + * SimpleDateFormat generates the time zone without the + * colon; this also conforms to 8601, but doesn't conform + * to the schema, so we have to diddle it. + */ + protected String toDateTime(Date date) { + String isoStr = iso8601.format(date); + // We can't directly use a SimpleDateFormat, because + // the 'z' field gives us the colonless time zone. + int len = isoStr.length(); + // Add the colon before the last two characters. + return isoStr.substring(0, len - 2) + ":" + isoStr.substring(len - 2); + } + + /** A DateFormat class to address an issue of thread safety. */ + public static class SynchronizedDateFormat extends SimpleDateFormat { + public SynchronizedDateFormat(String pattern) { + super(pattern); } - /** - * Callback indicating a new directory is being processed. - * @param directory Directory path - */ @Override - public void startDirectory (String directory) - { - /* Do nothing, which is sufficient for most handlers. */ - } - - /****************************************************************** - * PRIVATE CLASS METHODS. - * - * XML methods. - ******************************************************************/ - - /** - * Return the XML DOCTYPE instruction. - * @param root Root element of the DTD - * @param uri URI of the DTD - */ - protected static String doctype (String root, String uri) - { - return doctype (root, null, uri); - } - - /** - * Return the XML DOCTYPE instruction. - * @param root Root element of the DTD - * @param name Public name of the DTD - * @param uri URI of the DTD - */ - protected static String doctype (String root, String name, String uri) - { - StringBuffer s = new StringBuffer (""); - - return s.toString (); - } - - /** - * Returns, as a String, an empty XML. - * - * @param tag XML tag - */ - protected static String element (String tag) - { - return "<" + tag + "/>"; - } - - /** - * Returns, as a String, an XML element with a given tag and content - * - * @param tag An XML tag - * @param content Content string. Characters requiring - * conversion to entitites will be converted. - */ - protected static String element (String tag, String content) - { - return elementStart (tag) + Utils.encodeContent (content) + elementEnd (tag); - } - - /** - * Returns, as a String, - * an XML element with a given tag and attributes - * - * @param tag An XML tag - * @param attrs An array of String[2] elements, where for each - * element, attrs[i][0] is the attribute key and - * attrs[i][1] is the attribute value. - * Null values are skipped. - */ - protected static String element (String tag, String [][] attrs) - { - StringBuffer buffer = new StringBuffer ("<"); - buffer.append (tag); - for (int i=0; i"); - - return buffer.toString (); + public synchronized StringBuffer format(Date date, StringBuffer toAppendTo, FieldPosition pos) { + return super.format(date, toAppendTo, pos); } - - /** - * Returns, as a String, - * an XML element with a given tag, content and attributes - * - * @param tag An XML tag - * @param content Content string. Characters requiring - * conversion to entitites will be converted. - * @param attrs An array of String[2] elements, where for each - * element, attrs[i][0] is the attribute key and - * attrs[i][1] is the attribute value. - * Null values are skipped. - * - */ - protected static String element (String tag, String [][] attrs, - String content) - { - StringBuffer buffer = new StringBuffer ("<"); - buffer.append (tag); - for (int i=0; i"); - buffer.append (Utils.encodeContent (content)); - buffer.append (elementEnd (tag)); - - return buffer.toString (); - } - - /** - * Returns, as a String, the closing tag of an element. - * No checking is done that opening and closing tags match. - * - * @param tag An XML tag - */ - protected static String elementEnd (String tag) - { - return ""; - } - - /** - * Returns, as a String, the opening tag of an element. - * - * @param tag An XML tag - */ - protected static String elementStart (String tag) - { - return "<" + tag + ">"; - } - - /** - * Returns, as a String, the opening tag of an element with - * specified attributes. - * - * @param tag An XML tag - * @param attrs An array of String[2] elements, where for each - * element, attrs[i][0] is the attribute key and - * attrs[i][1] is the attribute value. - */ - protected static String elementStart (String tag, String [][] attrs) - { - StringBuffer buffer = new StringBuffer ("<"); - buffer.append (tag); - for (int i=0; i"); - - return buffer.toString (); - } - - /** - * Return a canonical XML declaration with default encoding. - */ - protected static String xmlDecl () - { - return ""; - } - - /** - * Return a canonical XML declaration with specified encoding. - */ - protected static String xmlDecl (String encoding) - { - return ""; - } - - /****************************************************************** - * Nesting level methods. - ******************************************************************/ - - /** - * Returns a String containing a number of spaces equal - * to the current indent level. - */ - protected static String getIndent (int level) - { - StringBuffer s = new StringBuffer (); - for (int i=0; i 0) { - buffer.append (separator); - } - buffer.append (Integer.toString (iarray[i])); - } - return buffer.toString (); - } - - /** - * Return String representation of an array of long with - * space separator. - */ - protected static String longArray (long [] larray) - { - StringBuffer buffer = new StringBuffer (); - for (int i=0; i 0) { - buffer.append (" "); - } - buffer.append (Long.toString (larray[i])); - } - return buffer.toString (); - } - - /** - * Return String representation of an array of Rational, each evaluated - * as a double, with space separator. - */ - protected static String rationalArray (Rational [] rarray) - { - StringBuffer buffer = new StringBuffer (); - for (int i=0; i 0) { - buffer.append (" "); - } - buffer.append (rarray[i].toDouble ()); - } - return buffer.toString (); - } - - - /** - * Return String representation of an array of Rational, each as - * two integers, with space separator. - */ - protected static String rationalArray10 (Rational [] rarray) - { - StringBuffer buffer = new StringBuffer (); - for (int i=0; i 0) { - buffer.append (" "); - } - buffer.append (rarray[i].getNumerator ()); - buffer.append (" "); - buffer.append (rarray[i].getNumerator ()); - } - return buffer.toString (); - } - - /** - * Return String representation of an array of double. - */ - protected static String doubleArray (double [] darray) - { - StringBuffer buffer = new StringBuffer (); - for (int i=0; i 0) { - buffer.append (" "); - } - buffer.append (Double.toString (darray[i])); - } - return buffer.toString (); - } - - - /* Text formatting methods. */ - /* Convert a date to the dateTime format used by the - * XML schema. This is ISO 8610 with a colon between - * the hour and minute fields of the time zone. Unfortunately, - * SimpleDateFormat generates the time zone without the - * colon; this also conforms to 8601, but doesn't conform - * to the schema, so we have to diddle it. - */ - protected String toDateTime (Date date) - { - String isoStr = iso8601.format (date); - // We can't directly use a SimpleDateFormat, because - // the 'z' field gives us the colonless time zone. - int len = isoStr.length (); - // Add the colon before the last two characters. - return isoStr.substring (0, len - 2) + - ":" + - isoStr.substring (len - 2); - } - - /** A DateFormat class to address an issue of thread safety. */ - public static class SynchronizedDateFormat extends SimpleDateFormat - { - public SynchronizedDateFormat(String pattern) { - super(pattern); - } - @Override - public synchronized StringBuffer format(Date date, - StringBuffer toAppendTo, FieldPosition pos) { - return super.format(date, toAppendTo, pos); - } - } - + } } diff --git a/jhove-core/src/main/java/edu/harvard/hul/ois/jhove/Identifier.java b/jhove-core/src/main/java/edu/harvard/hul/ois/jhove/Identifier.java index 4cc41e03c..d3e06dee9 100644 --- a/jhove-core/src/main/java/edu/harvard/hul/ois/jhove/Identifier.java +++ b/jhove-core/src/main/java/edu/harvard/hul/ois/jhove/Identifier.java @@ -1,64 +1,51 @@ -/********************************************************************** - * Jhove - JSTOR/Harvard Object Validation Environment - * Copyright 2003 by JSTOR and the President and Fellows of Harvard College - **********************************************************************/ - -package edu.harvard.hul.ois.jhove; - /** - * This class encapsulates information about an identifier - * for a specification document. + * ******************************************************************** Jhove - JSTOR/Harvard Object + * Validation Environment Copyright 2003 by JSTOR and the President and Fellows of Harvard College + * ******************************************************************** */ -public class Identifier -{ - private IdentifierType _type; - private String _value; - private String _note; +package edu.harvard.hul.ois.jhove; + +/** This class encapsulates information about an identifier for a specification document. */ +public class Identifier { + private IdentifierType _type; + private String _value; + private String _note; - /** - * Create an Identifier. - * @param value The text displayed for this Identifier. - * @param type The type of identification. - */ - public Identifier (String value, IdentifierType type) - { - _value = value; - _type = type; - } + /** + * Create an Identifier. + * + * @param value The text displayed for this Identifier. + * @param type The type of identification. + */ + public Identifier(String value, IdentifierType type) { + _value = value; + _type = type; + } - /** - * Create an Identifier. - * @param value The text displayed for this Identifier. - * @param type The type of identification. - * @param note A note giving supplementary information. - */ - public Identifier (String value, IdentifierType type, String note) - { - this (value, type); - _note = note; - } + /** + * Create an Identifier. + * + * @param value The text displayed for this Identifier. + * @param type The type of identification. + * @param note A note giving supplementary information. + */ + public Identifier(String value, IdentifierType type, String note) { + this(value, type); + _note = note; + } - /** - * Return the identifier type. - */ - public IdentifierType getType () - { - return _type; - } + /** Return the identifier type. */ + public IdentifierType getType() { + return _type; + } - /** - * Return the displayable string. - */ - public String getValue () - { - return _value; - } + /** Return the displayable string. */ + public String getValue() { + return _value; + } - /** - * Return the note, which will be null if none was specified. - */ - public String getNote() - { - return _note; - } + /** Return the note, which will be null if none was specified. */ + public String getNote() { + return _note; + } } diff --git a/jhove-core/src/main/java/edu/harvard/hul/ois/jhove/IdentifierType.java b/jhove-core/src/main/java/edu/harvard/hul/ois/jhove/IdentifierType.java index 3d20b5f87..dd2cbfa50 100644 --- a/jhove-core/src/main/java/edu/harvard/hul/ois/jhove/IdentifierType.java +++ b/jhove-core/src/main/java/edu/harvard/hul/ois/jhove/IdentifierType.java @@ -1,73 +1,68 @@ -/********************************************************************** - * Jhove - JSTOR/Harvard Object Validation Environment - * Copyright 2003 by JSTOR and the President and Fellows of Harvard College - **********************************************************************/ - +/** + * ******************************************************************** Jhove - JSTOR/Harvard Object + * Validation Environment Copyright 2003 by JSTOR and the President and Fellows of Harvard College + * ******************************************************************** + */ package edu.harvard.hul.ois.jhove; /** - * This class defines enumerated types for an Identifier of a - * format specification document. - * Applications will not create or modify IdentifierTypes, but will - * use one of the predefined IdentifierType instances - * ANSI, DDC, DOI, ECMA, HANDLE, ISO, ISBN, LC, LCCN, - * NISO, PII, RFC, SICI, URI, URL, URN, or OTHER. + * This class defines enumerated types for an Identifier of a format specification document. + * Applications will not create or modify IdentifierTypes, but will use one of the predefined + * IdentifierType instances ANSI, DDC, DOI, ECMA, HANDLE, ISO, ISBN, LC, LCCN, NISO, PII, RFC, SICI, + * URI, URL, URN, or OTHER. * * @see Identifier */ public enum IdentifierType { - /** Identifier type for American National Standards Institute. */ - ANSI("ANSI"), - /** Identifier type for Dewey Decimal Classification. */ - DDC("DDC"), - /** Identifier type for Digital Object Identifier. */ - DOI("DOI"), - /** Identifier type for ECMA. */ - ECMA("ECMA"), - /** Identifier type for CNRI Handle. */ - HANDLE("Handle"), - /** Identifier type for International Standards Organization. */ - ISO("ISO"), - /** Identifier type for International Standard Book Number. */ - ISBN("ISBN"), - /** Identifier type for Library of Congress classification. */ - LC("LC"), - /** Identifier type for Library of Congress catalogue number. */ - LCCN("LCCN"), - /** Identifier type for NISO standard number. */ - NISO("NISO"), - /** Identifier type for Publisher Item Identifier. */ - PII("PII"), - /** Identifier type for IETF Request for Comment. */ - RFC("RFC"), - /** Identifier type for Serial Item and Contribution Identifier. */ - SICI("SICI"), - /** Identifier type for Uniform Resource Identifier. */ - URI("URI"), - /** Identifier type for Uniform Resource Locator. */ - URL("URL"), - /** Identifier type for Uniform Resource Name. */ - URN("URN"), - /** Identifier type for CCITT. */ - CCITT("CCITT"), - /** Identifier type for International Telecommunication Union. */ - ITU("ITU"), - /** - * Identifier type for Japan Electronics and Information Technology - * Industries Association. - */ - JEITA("JEITA"), - /** Identifier type for whatever doesn't fit other categories. */ - OTHER("Other"); - /** A String name for the type, used for reporting. */ - public final String name; + /** Identifier type for American National Standards Institute. */ + ANSI("ANSI"), + /** Identifier type for Dewey Decimal Classification. */ + DDC("DDC"), + /** Identifier type for Digital Object Identifier. */ + DOI("DOI"), + /** Identifier type for ECMA. */ + ECMA("ECMA"), + /** Identifier type for CNRI Handle. */ + HANDLE("Handle"), + /** Identifier type for International Standards Organization. */ + ISO("ISO"), + /** Identifier type for International Standard Book Number. */ + ISBN("ISBN"), + /** Identifier type for Library of Congress classification. */ + LC("LC"), + /** Identifier type for Library of Congress catalogue number. */ + LCCN("LCCN"), + /** Identifier type for NISO standard number. */ + NISO("NISO"), + /** Identifier type for Publisher Item Identifier. */ + PII("PII"), + /** Identifier type for IETF Request for Comment. */ + RFC("RFC"), + /** Identifier type for Serial Item and Contribution Identifier. */ + SICI("SICI"), + /** Identifier type for Uniform Resource Identifier. */ + URI("URI"), + /** Identifier type for Uniform Resource Locator. */ + URL("URL"), + /** Identifier type for Uniform Resource Name. */ + URN("URN"), + /** Identifier type for CCITT. */ + CCITT("CCITT"), + /** Identifier type for International Telecommunication Union. */ + ITU("ITU"), + /** Identifier type for Japan Electronics and Information Technology Industries Association. */ + JEITA("JEITA"), + /** Identifier type for whatever doesn't fit other categories. */ + OTHER("Other"); + /** A String name for the type, used for reporting. */ + public final String name; - private IdentifierType(final String name) { - this.name = name; - } + private IdentifierType(final String name) { + this.name = name; + } - @Override - public String toString() { - return this.name; - } + @Override + public String toString() { + return this.name; + } } diff --git a/jhove-core/src/main/java/edu/harvard/hul/ois/jhove/InfoMessage.java b/jhove-core/src/main/java/edu/harvard/hul/ois/jhove/InfoMessage.java index 036b8cfef..37ce66826 100644 --- a/jhove-core/src/main/java/edu/harvard/hul/ois/jhove/InfoMessage.java +++ b/jhove-core/src/main/java/edu/harvard/hul/ois/jhove/InfoMessage.java @@ -1,123 +1,102 @@ -/********************************************************************** - * Jhove - JSTOR/Harvard Object Validation Environment - * Copyright 2003 by JSTOR and the President and Fellows of Harvard College - **********************************************************************/ - +/** + * ******************************************************************** Jhove - JSTOR/Harvard Object + * Validation Environment Copyright 2003 by JSTOR and the President and Fellows of Harvard College + * ******************************************************************** + */ package edu.harvard.hul.ois.jhove; import edu.harvard.hul.ois.jhove.messages.JhoveMessage; /** - * This class encapsulates an informational message from a Module, giving - * information (not necessarily a problem) about the content being analyzed - * or the way that JHOVE deals with it. + * This class encapsulates an informational message from a Module, giving information (not + * necessarily a problem) about the content being analyzed or the way that JHOVE deals with it. */ public class InfoMessage extends Message { - private static final String prefix = "Info"; + private static final String prefix = "Info"; - /** - * Creates an InfoMessage. - * - * @param message - * Human-readable message giving the information. - */ - public InfoMessage(String message) { - super(message); - } + /** + * Creates an InfoMessage. + * + * @param message Human-readable message giving the information. + */ + public InfoMessage(String message) { + super(message); + } - /** - * Creates an InfoMessage with an identifier. - * - * @param message - * The message text and its identifier. - */ - public InfoMessage(JhoveMessage message) { - super(message); - } + /** + * Creates an InfoMessage with an identifier. + * + * @param message The message text and its identifier. + */ + public InfoMessage(JhoveMessage message) { + super(message); + } - /** - * Creates an InfoMessage. - * - * @param message - * Human-readable message giving the information. - * @param offset - * The offset in the file relevant to the - * situation being described. - */ - public InfoMessage(String message, long offset) { - super(message, offset); - } + /** + * Creates an InfoMessage. + * + * @param message Human-readable message giving the information. + * @param offset The offset in the file relevant to the situation being described. + */ + public InfoMessage(String message, long offset) { + super(message, offset); + } - /** - * Creates an InfoMessage with an identifier. - * - * @param message - * The message text and its identifier. - * @param offset - * The offset in the file relevant to the - * situation being described. - */ - public InfoMessage(JhoveMessage message, long offset) { - super(message, offset); - } + /** + * Creates an InfoMessage with an identifier. + * + * @param message The message text and its identifier. + * @param offset The offset in the file relevant to the situation being described. + */ + public InfoMessage(JhoveMessage message, long offset) { + super(message, offset); + } - /** - * Creates an InfoMessage. - * - * @param message - * Human-readable message giving the information. - * @param subMessage - * Human-readable additional information. - */ - public InfoMessage(String message, String subMessage) { - super(message, subMessage); - } + /** + * Creates an InfoMessage. + * + * @param message Human-readable message giving the information. + * @param subMessage Human-readable additional information. + */ + public InfoMessage(String message, String subMessage) { + super(message, subMessage); + } - /** - * Creates an InfoMessage with an identifier. - * - * @param message - * The message text and its identifier. - * @param subMessage - * Human-readable additional information. - */ - public InfoMessage(JhoveMessage message, String subMessage) { - super(message, subMessage); - } + /** + * Creates an InfoMessage with an identifier. + * + * @param message The message text and its identifier. + * @param subMessage Human-readable additional information. + */ + public InfoMessage(JhoveMessage message, String subMessage) { + super(message, subMessage); + } - /** - * Creates an InfoMessage. - * - * @param message - * Human-readable message giving the information. - * @param subMessage - * Human-readable additional information. - * @param offset - * The offset in the file relevant to the - * situation being described. - */ - public InfoMessage(String message, String subMessage, long offset) { - super(message, subMessage, offset); - } + /** + * Creates an InfoMessage. + * + * @param message Human-readable message giving the information. + * @param subMessage Human-readable additional information. + * @param offset The offset in the file relevant to the situation being described. + */ + public InfoMessage(String message, String subMessage, long offset) { + super(message, subMessage, offset); + } - /** - * Creates an InfoMessage with an identifier. - * - * @param message - * The message text and its identifier. - * @param subMessage - * Human-readable additional information. - * @param offset - * The offset in the file relevant to the - * situation being described. - */ - public InfoMessage(JhoveMessage message, String subMessage, long offset) { - super(message, subMessage, offset); - } + /** + * Creates an InfoMessage with an identifier. + * + * @param message The message text and its identifier. + * @param subMessage Human-readable additional information. + * @param offset The offset in the file relevant to the situation being described. + */ + public InfoMessage(JhoveMessage message, String subMessage, long offset) { + super(message, subMessage, offset); + } - @Override - public String getPrefix() { - return prefix; - } + @Override + public String getPrefix() { + return prefix; + } } diff --git a/jhove-core/src/main/java/edu/harvard/hul/ois/jhove/InternalSignature.java b/jhove-core/src/main/java/edu/harvard/hul/ois/jhove/InternalSignature.java index 0f6eeb989..9e08c6545 100644 --- a/jhove-core/src/main/java/edu/harvard/hul/ois/jhove/InternalSignature.java +++ b/jhove-core/src/main/java/edu/harvard/hul/ois/jhove/InternalSignature.java @@ -1,140 +1,106 @@ -/********************************************************************** - * Jhove - JSTOR/Harvard Object Validation Environment - * Copyright 2003 by JSTOR and the President and Fellows of Harvard College - **********************************************************************/ - +/** + * ******************************************************************** Jhove - JSTOR/Harvard Object + * Validation Environment Copyright 2003 by JSTOR and the President and Fellows of Harvard College + * ******************************************************************** + */ package edu.harvard.hul.ois.jhove; /** - * This class encapsulates information about internal format signatures. - * The value of a Signature may be either a String or a byte array - * (stored as an int array to avoid signed byte problems). + * This class encapsulates information about internal format signatures. The value of a Signature + * may be either a String or a byte array (stored as an int array to avoid signed byte problems). */ -public class InternalSignature - extends Signature -{ - private boolean _hasFixedOffset; - private int _offset; +public class InternalSignature extends Signature { + private boolean _hasFixedOffset; + private int _offset; - /** - * Creates an InternalSignature given a string value, a type, - * and a use requirement. An InternalSignature created with - * this constructor does not have a fixed byte offset. - */ - public InternalSignature (String value, SignatureType type, - SignatureUseType use) - { - super (value, type, use); - _hasFixedOffset = false; - } + /** + * Creates an InternalSignature given a string value, a type, and a use requirement. An + * InternalSignature created with this constructor does not have a fixed byte offset. + */ + public InternalSignature(String value, SignatureType type, SignatureUseType use) { + super(value, type, use); + _hasFixedOffset = false; + } - /** - * Creates an InternalSignature given a byte array, a type, - * and a use requirement. An InternalSignature created with - * this constructor does not have a fixed byte offset. - */ - public InternalSignature (int[] value, SignatureType type, - SignatureUseType use) - { - super (value, type, use); - _hasFixedOffset = false; - } + /** + * Creates an InternalSignature given a byte array, a type, and a use requirement. An + * InternalSignature created with this constructor does not have a fixed byte offset. + */ + public InternalSignature(int[] value, SignatureType type, SignatureUseType use) { + super(value, type, use); + _hasFixedOffset = false; + } - /** - * Creates an InternalSignature given a string value, a type, - * a use requirement, and a byte offset. An InternalSignature - * created with this constructor has a fixed byte offset. - */ - public InternalSignature (String value, SignatureType type, - SignatureUseType use, int offset) - { - super (value, type, use); - _offset = offset; - _hasFixedOffset = true; - } + /** + * Creates an InternalSignature given a string value, a type, a use requirement, and a byte + * offset. An InternalSignature created with this constructor has a fixed byte offset. + */ + public InternalSignature(String value, SignatureType type, SignatureUseType use, int offset) { + super(value, type, use); + _offset = offset; + _hasFixedOffset = true; + } - /** - * Creates an InternalSignature given a byte array, a type, - * a use requirement, and a byte offset. An InternalSignature - * created with this constructor has a fixed byte offset. - */ - public InternalSignature (int[] value, SignatureType type, - SignatureUseType use, int offset) - { - super (value, type, use); - _offset = offset; - _hasFixedOffset = true; - } + /** + * Creates an InternalSignature given a byte array, a type, a use requirement, and a byte offset. + * An InternalSignature created with this constructor has a fixed byte offset. + */ + public InternalSignature(int[] value, SignatureType type, SignatureUseType use, int offset) { + super(value, type, use); + _offset = offset; + _hasFixedOffset = true; + } - /** - * Creates an InternalSignature given a string value, a type, - * a use requirement, and a note. An InternalSignature created with - * this constructor does not have a fixed byte offset. - */ - public InternalSignature (String value, SignatureType type, - SignatureUseType use, String note) - { - super (value, type, use, note); - _hasFixedOffset = false; - } + /** + * Creates an InternalSignature given a string value, a type, a use requirement, and a note. An + * InternalSignature created with this constructor does not have a fixed byte offset. + */ + public InternalSignature(String value, SignatureType type, SignatureUseType use, String note) { + super(value, type, use, note); + _hasFixedOffset = false; + } - /** - * Creates an InternalSignature given a byte array, a type, - * a use requirement, and a note. An InternalSignature created with - * this constructor does not have a fixed byte offset. - */ - public InternalSignature (int[] value, SignatureType type, - SignatureUseType use, String note) - { - super (value, type, use, note); - _hasFixedOffset = false; - } + /** + * Creates an InternalSignature given a byte array, a type, a use requirement, and a note. An + * InternalSignature created with this constructor does not have a fixed byte offset. + */ + public InternalSignature(int[] value, SignatureType type, SignatureUseType use, String note) { + super(value, type, use, note); + _hasFixedOffset = false; + } - /** - * Creates an InternalSignature given a string value, a type, - * a use requirement, a byte offset, and a note. - * An InternalSignature created with - * this constructor has a fixed byte offset. - */ - public InternalSignature (String value, SignatureType type, - SignatureUseType use, int offset, - String note) - { - super (value, type, use, note); - _offset = offset; - _hasFixedOffset = true; - } + /** + * Creates an InternalSignature given a string value, a type, a use requirement, a byte offset, + * and a note. An InternalSignature created with this constructor has a fixed byte offset. + */ + public InternalSignature( + String value, SignatureType type, SignatureUseType use, int offset, String note) { + super(value, type, use, note); + _offset = offset; + _hasFixedOffset = true; + } - /** - * Creates an InternalSignature given a string value, a type, - * a use requirement, a byte offset, and a note. - * An InternalSignature created with - * this constructor has a fixed byte offset. - */ - public InternalSignature (int[] value, SignatureType type, - SignatureUseType use, int offset, - String note) - { - super (value, type, use, note); - _offset = offset; - _hasFixedOffset = true; - } + /** + * Creates an InternalSignature given a string value, a type, a use requirement, a byte offset, + * and a note. An InternalSignature created with this constructor has a fixed byte offset. + */ + public InternalSignature( + int[] value, SignatureType type, SignatureUseType use, int offset, String note) { + super(value, type, use, note); + _offset = offset; + _hasFixedOffset = true; + } - /** - * Returns the byte offset. This value is meaningful only - * if this InternalSignature has a fixed byte offset. - */ - public int getOffset () - { - return _offset; - } + /** + * Returns the byte offset. This value is meaningful only if this InternalSignature has a fixed + * byte offset. + */ + public int getOffset() { + return _offset; + } - /** - * Returns true if this InternalSignature - * has a fixed byte offset. - */ - public boolean hasFixedOffset () - { - return _hasFixedOffset; - } + /** Returns true if this InternalSignature has a fixed byte offset. */ + public boolean hasFixedOffset() { + return _hasFixedOffset; + } } diff --git a/jhove-core/src/main/java/edu/harvard/hul/ois/jhove/JhoveBase.java b/jhove-core/src/main/java/edu/harvard/hul/ois/jhove/JhoveBase.java index ec07f59d2..bd01eb8cd 100644 --- a/jhove-core/src/main/java/edu/harvard/hul/ois/jhove/JhoveBase.java +++ b/jhove-core/src/main/java/edu/harvard/hul/ois/jhove/JhoveBase.java @@ -1,8 +1,8 @@ -/********************************************************************** - * JHOVE - JSTOR/Harvard Object Validation Environment - * Copyright 2005-2007 by the President and Fellows of Harvard College - **********************************************************************/ - +/** + * ******************************************************************** JHOVE - JSTOR/Harvard Object + * Validation Environment Copyright 2005-2007 by the President and Fellows of Harvard College + * ******************************************************************** + */ package edu.harvard.hul.ois.jhove; import edu.harvard.hul.ois.jhove.handler.AuditHandler; @@ -10,12 +10,6 @@ import edu.harvard.hul.ois.jhove.handler.TextHandler; import edu.harvard.hul.ois.jhove.handler.XmlHandler; import edu.harvard.hul.ois.jhove.module.BytestreamModule; -import org.openpreservation.jhove.ReleaseDetails; -import org.xml.sax.SAXException; -import org.xml.sax.XMLReader; -import org.xml.sax.helpers.XMLReaderFactory; -import javax.net.ssl.*; -import javax.xml.parsers.SAXParserFactory; import java.io.*; import java.net.URI; import java.net.URL; @@ -30,6 +24,7 @@ import java.util.TreeMap; import java.util.logging.Level; import java.util.logging.Logger; +import javax.net.ssl.*; import javax.net.ssl.HostnameVerifier; import javax.net.ssl.HttpsURLConnection; import javax.net.ssl.SSLContext; @@ -37,1218 +32,1115 @@ import javax.net.ssl.SSLSocketFactory; import javax.net.ssl.TrustManager; import javax.net.ssl.X509TrustManager; +import javax.xml.parsers.SAXParserFactory; +import org.openpreservation.jhove.ReleaseDetails; +import org.xml.sax.SAXException; +import org.xml.sax.XMLReader; +import org.xml.sax.helpers.XMLReaderFactory; /** - * The JHOVE engine, providing all base services necessary to build an - * application. + * The JHOVE engine, providing all base services necessary to build an application. * - * More than one JhoveBase may be instantiated and process files in concurrent - * threads. Any one instance must not be multithreaded. + *

More than one JhoveBase may be instantiated and process files in concurrent threads. Any one + * instance must not be multithreaded. */ public class JhoveBase { - public static final String _name = "JhoveBase"; - - private static final ReleaseDetails RELEASE_DETAILS = - ReleaseDetails.getInstance(); - - private static final String JAVA_TEMP_DIR_PROP_KEY = "java.io.tmpdir"; - private static final String HUL_PROPERTY_PREFIX = "edu.harvard.hul.ois."; - private static final String JHOVE_PROPERTY_PREFIX = HUL_PROPERTY_PREFIX - + "jhove."; - - /** JHOVE buffer size property. */ - private static final String BUFFER_PROPERTY = JHOVE_PROPERTY_PREFIX - + "bufferSize"; - - /** JHOVE home directory */ - private static final String JHOVE_DIR = "jhove"; - - /** JHOVE configuration directory */ - private static final String CONFIG_DIR = "conf"; - - /** JHOVE configuration file property. */ - private static final String CONFIG_PROPERTY = JHOVE_PROPERTY_PREFIX - + "config"; - - /** JHOVE default buffer size. */ - private static final int DEFAULT_BUFFER = 131072; - - /** JHOVE default character encoding. */ - private static final String DEFAULT_ENCODING = "utf-8"; - - /** Default temporary directory. */ - private static final String DEFAULT_TEMP = - System.getProperty(JAVA_TEMP_DIR_PROP_KEY); - - /** JHOVE encoding property. */ - private static final String ENCODING_PROPERTY = JHOVE_PROPERTY_PREFIX - + "encoding"; - - /** JHOVE SAX parser class property. */ - private static final String SAX_PROPERTY = JHOVE_PROPERTY_PREFIX - + "saxClass"; - - /** JHOVE temporary directory property. */ - private static final String TEMPDIR_PROPERTY = JHOVE_PROPERTY_PREFIX - + "tempDirectory"; - - /** Flag for aborting activity. */ - protected boolean _abort; - /** Buffer size for buffered I/O. */ - protected int _bufferSize; - protected boolean _checksum; - /** Configuration file pathname. */ - protected String _configFile; - /** Selected encoding. */ - protected String _encoding; - /** Ordered list of output handlers. */ - protected List _handlerList; - /** Map of output handlers (for fast access by name). */ - protected Map _handlerMap; - /** JHOVE home directory. */ - protected String _jhoveHome; - /** Ordered list of modules. */ - protected List _moduleList; - /** Map of modules (for fast access by name). */ - protected Map _moduleMap; - protected String _outputFile; - /** SAX parser class. */ - protected String _saxClass; - protected boolean _showRaw; - protected boolean _signature; - /** Temporary directory. */ - protected String _tempDir; - /** MIX version. */ - protected String _mixVsn; - /** Number of bytes for fake signature checking. */ - protected int _sigBytes; - /** Directory for saving files. */ - protected File _saveDir; - /** Byte count for digital object */ - protected long _nByte; - /** Callback function to check for termination. */ - Callback _callback; - /** Current URL connection. */ - protected URLConnection _conn; - /** Thread currently parsing a document. */ - protected Thread _currentThread; - - /** Logger for this class. */ - protected Logger _logger; - /** Logger resource bundle. */ - protected String _logLevel; - - /** - * Class constructor. - * - * Instantiates a JhoveBase object. - * - * @throws JhoveException - * if invoked with a JVM lower than 1.8 - */ - public JhoveBase() throws JhoveException { - - _logger = Logger.getLogger("edu.harvard.hul.ois.jhove"); - _logger.setLevel(Level.SEVERE); // May be changed by config file - - // Make sure we have a satisfactory version of Java. - String version = System.getProperty("java.vm.version"); - if (version.compareTo("1.8.0") < 0) { - _logger.severe(CoreMessageConstants.EXC_JAVA_VER_INCMPT); - throw new JhoveException(CoreMessageConstants.EXC_JAVA_VER_INCMPT); - } + public static final String _name = "JhoveBase"; + + private static final ReleaseDetails RELEASE_DETAILS = ReleaseDetails.getInstance(); + + private static final String JAVA_TEMP_DIR_PROP_KEY = "java.io.tmpdir"; + private static final String HUL_PROPERTY_PREFIX = "edu.harvard.hul.ois."; + private static final String JHOVE_PROPERTY_PREFIX = HUL_PROPERTY_PREFIX + "jhove."; + + /** JHOVE buffer size property. */ + private static final String BUFFER_PROPERTY = JHOVE_PROPERTY_PREFIX + "bufferSize"; + + /** JHOVE home directory */ + private static final String JHOVE_DIR = "jhove"; + + /** JHOVE configuration directory */ + private static final String CONFIG_DIR = "conf"; + + /** JHOVE configuration file property. */ + private static final String CONFIG_PROPERTY = JHOVE_PROPERTY_PREFIX + "config"; + + /** JHOVE default buffer size. */ + private static final int DEFAULT_BUFFER = 131072; + + /** JHOVE default character encoding. */ + private static final String DEFAULT_ENCODING = "utf-8"; + + /** Default temporary directory. */ + private static final String DEFAULT_TEMP = System.getProperty(JAVA_TEMP_DIR_PROP_KEY); + + /** JHOVE encoding property. */ + private static final String ENCODING_PROPERTY = JHOVE_PROPERTY_PREFIX + "encoding"; + + /** JHOVE SAX parser class property. */ + private static final String SAX_PROPERTY = JHOVE_PROPERTY_PREFIX + "saxClass"; + + /** JHOVE temporary directory property. */ + private static final String TEMPDIR_PROPERTY = JHOVE_PROPERTY_PREFIX + "tempDirectory"; + + /** Flag for aborting activity. */ + protected boolean _abort; + /** Buffer size for buffered I/O. */ + protected int _bufferSize; + + protected boolean _checksum; + /** Configuration file pathname. */ + protected String _configFile; + /** Selected encoding. */ + protected String _encoding; + /** Ordered list of output handlers. */ + protected List _handlerList; + /** Map of output handlers (for fast access by name). */ + protected Map _handlerMap; + /** JHOVE home directory. */ + protected String _jhoveHome; + /** Ordered list of modules. */ + protected List _moduleList; + /** Map of modules (for fast access by name). */ + protected Map _moduleMap; + + protected String _outputFile; + /** SAX parser class. */ + protected String _saxClass; + + protected boolean _showRaw; + protected boolean _signature; + /** Temporary directory. */ + protected String _tempDir; + /** MIX version. */ + protected String _mixVsn; + /** Number of bytes for fake signature checking. */ + protected int _sigBytes; + /** Directory for saving files. */ + protected File _saveDir; + /** Byte count for digital object */ + protected long _nByte; + /** Callback function to check for termination. */ + Callback _callback; + /** Current URL connection. */ + protected URLConnection _conn; + /** Thread currently parsing a document. */ + protected Thread _currentThread; + + /** Logger for this class. */ + protected Logger _logger; + /** Logger resource bundle. */ + protected String _logLevel; + + /** + * Class constructor. + * + *

Instantiates a JhoveBase object. + * + * @throws JhoveException if invoked with a JVM lower than 1.8 + */ + public JhoveBase() throws JhoveException { + + _logger = Logger.getLogger("edu.harvard.hul.ois.jhove"); + _logger.setLevel(Level.SEVERE); // May be changed by config file + + // Make sure we have a satisfactory version of Java. + String version = System.getProperty("java.vm.version"); + if (version.compareTo("1.8.0") < 0) { + _logger.severe(CoreMessageConstants.EXC_JAVA_VER_INCMPT); + throw new JhoveException(CoreMessageConstants.EXC_JAVA_VER_INCMPT); + } - // Tell any HTTPS connections to be accepted automatically. - HttpsURLConnection.setDefaultHostnameVerifier( - new NaiveHostnameVerifier()); + // Tell any HTTPS connections to be accepted automatically. + HttpsURLConnection.setDefaultHostnameVerifier(new NaiveHostnameVerifier()); + + // Initialize the engine. + _moduleList = new ArrayList<>(20); + _moduleMap = new TreeMap<>(); + + _handlerList = new ArrayList<>(); + _handlerMap = new TreeMap<>(); + + _abort = false; + _bufferSize = -1; + _checksum = false; + _showRaw = false; + _signature = false; + _callback = null; + } + + /** + * Initializes the JHOVE engine. + * + *

This method parses the configuration file and initialises the JHOVE engine based on the + * values parsed. + * + *

Version 1.11 would create the configuration file if not found. Version 1.12 changes this + * behaviour. The file path supplied must be resolvable to an existing JHOVE config file. + * + * @param configFile Configuration file pathname + * @param saxClass a SAX parser class, will use JVM default if not supplied + * @throws JhoveException when anything goes wrong + */ + public void init(String configFile, String saxClass) throws JhoveException { + + if (configFile == null) { + throw new JhoveException(CoreMessageConstants.EXC_CONF_FILE_LOC_MISS); + } + _configFile = configFile; - // Initialize the engine. - _moduleList = new ArrayList<>(20); - _moduleMap = new TreeMap<>(); + File config = new File(_configFile); - _handlerList = new ArrayList<>(); - _handlerMap = new TreeMap<>(); + if (!config.exists() || !config.isFile()) { + throw new JhoveException(config.getAbsolutePath() + CoreMessageConstants.EXC_CONF_FILE_INVAL); + } - _abort = false; - _bufferSize = -1; - _checksum = false; - _showRaw = false; - _signature = false; - _callback = null; + _saxClass = saxClass; + XMLReader parser = null; + + try { + if (saxClass == null) { + // Use Java 1.4 methods to create default parser. + SAXParserFactory factory = SAXParserFactory.newInstance(); + factory.setNamespaceAware(true); + parser = factory.newSAXParser().getXMLReader(); + } else { + parser = XMLReaderFactory.createXMLReader(saxClass); + } + } catch (Exception e) { + // If we can't get a SAX parser, we're stuck. + throw new JhoveException(CoreMessageConstants.EXC_SAX_PRSR_MISS + saxClass, e); } - /** - * Initializes the JHOVE engine. - * - * This method parses the configuration file and initialises the JHOVE - * engine based on the values parsed. - * - * Version 1.11 would create the configuration file if not found. Version - * 1.12 changes this behaviour. The file path supplied must be resolvable - * to an existing JHOVE config file. - * - * @param configFile - * Configuration file pathname - * @param saxClass - * a SAX parser class, will use JVM default if not supplied - * @throws JhoveException - * when anything goes wrong - */ - public void init(String configFile, String saxClass) throws JhoveException { - - if (configFile == null) { - throw new JhoveException(CoreMessageConstants.EXC_CONF_FILE_LOC_MISS); - } - _configFile = configFile; + _logger.info("Using SAX parser " + parser.getClass().getName()); + ConfigHandler configHandler = new ConfigHandler(); + parser.setContentHandler(configHandler); + parser.setEntityResolver(configHandler); + + // Attempt to set schema awareness to avoid validation errors. + try { + parser.setFeature("http://xml.org/sax/features/validation", true); + parser.setProperty( + "http://java.sun.com/xml/jaxp/" + "properties/schemaLanguage", + "http://www.w3.org/2001/XMLSchema"); + } catch (SAXException saxe) { + } - File config = new File(_configFile); + try { + String canonicalPath = config.getCanonicalPath(); + String fileURL = "file://"; + if (canonicalPath.charAt(0) != '/') { + fileURL += Character.toString('/'); + } + fileURL += canonicalPath; + parser.parse(fileURL); + } catch (IOException ioe) { + throw new JhoveException(CoreMessageConstants.EXC_CONF_FILE_UNRDBL + configFile, ioe); + } catch (SAXException saxe) { + throw new JhoveException(CoreMessageConstants.EXC_CONF_FILE_UNPRS + saxe.getMessage(), saxe); + } - if (!config.exists() || !config.isFile()) { - throw new JhoveException(config.getAbsolutePath() + - CoreMessageConstants.EXC_CONF_FILE_INVAL); - } + // Update the application state to reflect the configuration file, + // if necessary. + _jhoveHome = configHandler.getJhoveHome(); - _saxClass = saxClass; - XMLReader parser = null; + _encoding = configHandler.getEncoding(); + if (_encoding == null) { + _encoding = getFromProperties(ENCODING_PROPERTY); + if (_encoding == null) { + _encoding = DEFAULT_ENCODING; + } + } - try { - if (saxClass == null) { - // Use Java 1.4 methods to create default parser. - SAXParserFactory factory = SAXParserFactory.newInstance(); - factory.setNamespaceAware(true); - parser = factory.newSAXParser().getXMLReader(); - } else { - parser = XMLReaderFactory.createXMLReader(saxClass); - } - } catch (Exception e) { - // If we can't get a SAX parser, we're stuck. - throw new JhoveException(CoreMessageConstants.EXC_SAX_PRSR_MISS + saxClass, e); - } + _tempDir = configHandler.getTempDir(); + if (_tempDir == null) { + _tempDir = getFromProperties(TEMPDIR_PROPERTY); + if (_tempDir == null) { + _tempDir = DEFAULT_TEMP; + } + } - _logger.info("Using SAX parser " + parser.getClass().getName()); - ConfigHandler configHandler = new ConfigHandler(); - parser.setContentHandler(configHandler); - parser.setEntityResolver(configHandler); + // Get the MIX version. If not specified, defaults to 2.0. + _mixVsn = configHandler.getMixVsn(); + if (_mixVsn == null) { + _mixVsn = "2.0"; + } - // Attempt to set schema awareness to avoid validation errors. - try { - parser.setFeature("http://xml.org/sax/features/validation", true); - parser.setProperty("http://java.sun.com/xml/jaxp/" - + "properties/schemaLanguage", - "http://www.w3.org/2001/XMLSchema"); - } catch (SAXException saxe) { - } + // Get the maximum number of bytes to examine when doing + // pseudo-signature checking + _sigBytes = configHandler.getSigBytes(); + // If a log level was specified in the config file, + // attempt to set it, unless it was already explicitly set. + if (_logLevel == null) { + _logLevel = configHandler.getLogLevel(); + if (_logLevel != null) { try { - String canonicalPath = config.getCanonicalPath(); - String fileURL = "file://"; - if (canonicalPath.charAt(0) != '/') { - fileURL += Character.toString('/'); - } - fileURL += canonicalPath; - parser.parse(fileURL); - } catch (IOException ioe) { - throw new JhoveException(CoreMessageConstants.EXC_CONF_FILE_UNRDBL - + configFile, ioe); - } catch (SAXException saxe) { - throw new JhoveException(CoreMessageConstants.EXC_CONF_FILE_UNPRS - + saxe.getMessage(), saxe); - } - - // Update the application state to reflect the configuration file, - // if necessary. - _jhoveHome = configHandler.getJhoveHome(); - - _encoding = configHandler.getEncoding(); - if (_encoding == null) { - _encoding = getFromProperties(ENCODING_PROPERTY); - if (_encoding == null) { - _encoding = DEFAULT_ENCODING; - } - } - - _tempDir = configHandler.getTempDir(); - if (_tempDir == null) { - _tempDir = getFromProperties(TEMPDIR_PROPERTY); - if (_tempDir == null) { - _tempDir = DEFAULT_TEMP; - } - } - - // Get the MIX version. If not specified, defaults to 2.0. - _mixVsn = configHandler.getMixVsn(); - if (_mixVsn == null) { - _mixVsn = "2.0"; - } - - // Get the maximum number of bytes to examine when doing - // pseudo-signature checking - _sigBytes = configHandler.getSigBytes(); - - // If a log level was specified in the config file, - // attempt to set it, unless it was already explicitly set. - if (_logLevel == null) { - _logLevel = configHandler.getLogLevel(); - if (_logLevel != null) { - try { - _logger.setLevel(Level.parse(_logLevel)); - } catch (SecurityException se) { - // Can't set the logger level due to security exception - } - } - } - - _bufferSize = configHandler.getBufferSize(); - if (_bufferSize < 0) { - String size = getFromProperties(BUFFER_PROPERTY); - if (size != null) { - try { - _bufferSize = Integer.parseInt(size); - } catch (Exception e) { - } - } - if (_bufferSize < 0) { - _bufferSize = DEFAULT_BUFFER; - } - } - - // Retrieve the ordered lists of modules and output handlers - List modList = configHandler.getModule(); - List> params = configHandler.getModuleParams(); - for (int i = 0; i < modList.size(); i++) { - ModuleInfo modInfo = modList.get(i); - List param = params.get(i); - try { - Class cl = Class.forName(modInfo.clas); - Module module = (Module) cl.newInstance(); - module.init(modInfo.init); - module.setDefaultParams(param); - - _moduleList.add(module); - _moduleMap.put(module.getName().toLowerCase(), module); - _logger.info("Initialized " + module.getName()); - } catch (Exception e) { - throw new JhoveException(CoreMessageConstants.EXC_MODL_INST_FAIL - + modInfo.clas, e); - } + _logger.setLevel(Level.parse(_logLevel)); + } catch (SecurityException se) { + // Can't set the logger level due to security exception } + } + } - List hanList = configHandler.getHandler(); - params = configHandler.getHandlerParams(); - for (int i = 0; i < hanList.size(); i++) { - String[] tuple = hanList.get(i); - List param = params.get(i); - try { - Class cl = Class.forName(tuple[0]); - OutputHandler handler = (OutputHandler) cl.newInstance(); - handler.setDefaultParams(param); - - _handlerList.add(handler); - _handlerMap.put(handler.getName().toLowerCase(), handler); - } catch (Exception e) { - throw new JhoveException(CoreMessageConstants.EXC_HNDL_INST_FAIL - + tuple[0], e); - } + _bufferSize = configHandler.getBufferSize(); + if (_bufferSize < 0) { + String size = getFromProperties(BUFFER_PROPERTY); + if (size != null) { + try { + _bufferSize = Integer.parseInt(size); + } catch (Exception e) { } + } + if (_bufferSize < 0) { + _bufferSize = DEFAULT_BUFFER; + } + } - // The Bytestream module and the Text, XML, and Audit output handlers - // are always statically loaded. + // Retrieve the ordered lists of modules and output handlers + List modList = configHandler.getModule(); + List> params = configHandler.getModuleParams(); + for (int i = 0; i < modList.size(); i++) { + ModuleInfo modInfo = modList.get(i); + List param = params.get(i); + try { + Class cl = Class.forName(modInfo.clas); + Module module = (Module) cl.newInstance(); + module.init(modInfo.init); + module.setDefaultParams(param); - Module module = new BytestreamModule(); - module.setDefaultParams(new ArrayList()); _moduleList.add(module); _moduleMap.put(module.getName().toLowerCase(), module); + _logger.info("Initialized " + module.getName()); + } catch (Exception e) { + throw new JhoveException(CoreMessageConstants.EXC_MODL_INST_FAIL + modInfo.clas, e); + } + } - OutputHandler handler = new TextHandler(); - handler.setDefaultParams(new ArrayList()); - _handlerList.add(handler); - _handlerMap.put(handler.getName().toLowerCase(), handler); - - handler = new XmlHandler(); - handler.setDefaultParams(new ArrayList()); - _handlerList.add(handler); - _handlerMap.put(handler.getName().toLowerCase(), handler); - - handler = new JsonHandler(); - handler.setDefaultParams(new ArrayList()); - _handlerList.add(handler); - _handlerMap.put(handler.getName().toLowerCase(), handler); + List hanList = configHandler.getHandler(); + params = configHandler.getHandlerParams(); + for (int i = 0; i < hanList.size(); i++) { + String[] tuple = hanList.get(i); + List param = params.get(i); + try { + Class cl = Class.forName(tuple[0]); + OutputHandler handler = (OutputHandler) cl.newInstance(); + handler.setDefaultParams(param); - handler = new AuditHandler(); - handler.setDefaultParams(new ArrayList()); _handlerList.add(handler); _handlerMap.put(handler.getName().toLowerCase(), handler); + } catch (Exception e) { + throw new JhoveException(CoreMessageConstants.EXC_HNDL_INST_FAIL + tuple[0], e); + } } - /** - * Sets a callback object for tracking progress. - * By default, the callback is null. - */ - public void setCallback(Callback callback) { - _callback = callback; + // The Bytestream module and the Text, XML, and Audit output handlers + // are always statically loaded. + + Module module = new BytestreamModule(); + module.setDefaultParams(new ArrayList()); + _moduleList.add(module); + _moduleMap.put(module.getName().toLowerCase(), module); + + OutputHandler handler = new TextHandler(); + handler.setDefaultParams(new ArrayList()); + _handlerList.add(handler); + _handlerMap.put(handler.getName().toLowerCase(), handler); + + handler = new XmlHandler(); + handler.setDefaultParams(new ArrayList()); + _handlerList.add(handler); + _handlerMap.put(handler.getName().toLowerCase(), handler); + + handler = new JsonHandler(); + handler.setDefaultParams(new ArrayList()); + _handlerList.add(handler); + _handlerMap.put(handler.getName().toLowerCase(), handler); + + handler = new AuditHandler(); + handler.setDefaultParams(new ArrayList()); + _handlerList.add(handler); + _handlerMap.put(handler.getName().toLowerCase(), handler); + } + + /** + * Sets a callback object for tracking progress. By default, the callback is null. + */ + public void setCallback(Callback callback) { + _callback = callback; + } + + /** + * Processes a file or directory, or outputs information. If dirFileOrUri is null, + * Does one of the following: + * + *

    + *
  • If module is non-null, provides information about the module. + *
  • Otherwise if aboutHandler is non-null, provides information about that + * handler. + *
  • If they're both null, provides information about the application. + *
+ * + * @param app The App object for the application + * @param module The module to be used + * @param aboutHandler If specified, the handler about which info is requested + * @param handler The handler for processing the output + * @param outputFile Name of the file to which output should go + * @param dirFileOrUri One or more file names or URI's to be analyzed + */ + public void dispatch( + App app, + Module module, + OutputHandler aboutHandler, + OutputHandler handler, + String outputFile, + String[] dirFileOrUri) + throws Exception { + + _abort = false; + + // If no handler is specified, use the default TEXT handler. + if (handler == null) { + handler = _handlerMap.get("text"); } + handler.reset(); + _outputFile = outputFile; - /** - * Processes a file or directory, or outputs information. If - * dirFileOrUri is null, Does one of the following: - *
    - *
  • If module is non-null, provides information about the module. - *
  • Otherwise if aboutHandler is non-null, provides - * information about that handler. - *
  • If they're both null, provides information about the application. - *
- * - * @param app - * The App object for the application - * @param module - * The module to be used - * @param aboutHandler - * If specified, the handler about which info is requested - * @param handler - * The handler for processing the output - * @param outputFile - * Name of the file to which output should go - * @param dirFileOrUri - * One or more file names or URI's to be analyzed - */ - public void dispatch(App app, Module module, OutputHandler aboutHandler, - OutputHandler handler, String outputFile, String[] dirFileOrUri) - throws Exception { - - _abort = false; - - // If no handler is specified, use the default TEXT handler. - if (handler == null) { - handler = _handlerMap.get("text"); - } - handler.reset(); - _outputFile = outputFile; - - handler.setApp(app); - handler.setBase(this); - _logger.info("Handler " + handler.getClass().getName() - + " preparing to write to " + _outputFile); - handler.setWriter(makeWriter(_outputFile, _encoding)); - - handler.showHeader(); - - if (dirFileOrUri == null) { - if (module != null) { - // Show info about module. - module.applyDefaultParams(); - module.show(handler); - } else if (aboutHandler != null) { - // Show info about handler. - handler.show(aboutHandler); - } else { - // Show info about application - app.show(handler); - } - } else { - for (String aDirFileOrUri : dirFileOrUri) { - if (!process(app, module, handler, aDirFileOrUri)) { - break; - } - } - } - - handler.showFooter(); - handler.close(); - } - - /** - * Returns false if processing should be aborted. - * Calls itself recursively for directories. - */ - public boolean process(App app, Module module, OutputHandler handler, - String dirFileOrUri) throws Exception { - - if (_abort) { - return false; - } - _logger.info("Entering JhoveBase.process, file/uri = " - + dirFileOrUri); - File file = null; - boolean isTemp = false; - long lastModified = -1; - - // First see if we have a URI, if not it is a directory or a file. - URI uri = null; - try { - uri = new URI(dirFileOrUri); - } catch (Exception e) { - // We may get an exception on Windows paths, - // if so then fall through and try for a file. - } - RepInfo info = new RepInfo(dirFileOrUri); - if (uri != null && uri.isAbsolute()) { - URL url = null; - try { - url = uri.toURL(); - } catch (Exception e) { - throw new JhoveException(CoreMessageConstants.EXC_URI_CONV_FAIL - + dirFileOrUri); - } - URLConnection conn = url.openConnection(); - _conn = conn; - if (conn instanceof HttpsURLConnection) { - try { - TrustManager[] tm = { new RelaxedX509TrustManager() }; - SSLContext sslContext = SSLContext.getInstance("SSL"); - sslContext.init(null, tm, new java.security.SecureRandom()); - SSLSocketFactory sf = sslContext.getSocketFactory(); - ((HttpsURLConnection) conn).setSSLSocketFactory(sf); - int code = ((HttpsURLConnection) conn).getResponseCode(); - if (200 > code || code >= 300) { - throw new JhoveException(CoreMessageConstants.EXC_URL_NOT_FND - + dirFileOrUri); - } - } catch (Exception e) { - throw new JhoveException(CoreMessageConstants.EXC_URL_NOT_FND + dirFileOrUri); - } - } - lastModified = conn.getLastModified(); - - // Convert the URI to a temporary file and use for the input stream. - try { - file = connToTempFile(conn, info); - if (file == null) { - // User aborted - return false; - } - isTemp = true; - } catch (IOException ioe) { - _conn = null; - String message = "Cannot read URL: " + dirFileOrUri; - _logger.info(message); - String ioeMessage = ioe.getMessage(); - if (ioeMessage != null) { - message += " (" + ioeMessage + ")"; - } - throw new JhoveException(message); - } - if (conn instanceof HttpsURLConnection) { - ((HttpsURLConnection) conn).disconnect(); - } - _conn = null; - } else { - file = new File(dirFileOrUri); - } - - if (file.isDirectory()) { - File[] files = file.listFiles(); - info = null; // Free up unused RepInfo before recursing + handler.setApp(app); + handler.setBase(this); + _logger.info( + "Handler " + handler.getClass().getName() + " preparing to write to " + _outputFile); + handler.setWriter(makeWriter(_outputFile, _encoding)); - // Sort the files in ascending order by filename. - Arrays.sort(files); + handler.showHeader(); - handler.startDirectory(file.getCanonicalPath()); - for (int i = 0; i < files.length; i++) { - if (!process(app, module, handler, files[i].getCanonicalPath())) { - return false; - } - } - handler.endDirectory(); - } else { - - if (!file.exists()) { - _logger.info("File not found"); - info.setMessage(new ErrorMessage(CoreMessageConstants.ERR_FILE_NOT_FOUND)); - info.setWellFormed(RepInfo.UNDETERMINED); - info.show(handler); - } else if (!file.isFile() || !file.canRead()) { - _logger.info("File cannot be read"); - info.setMessage(new ErrorMessage(CoreMessageConstants.ERR_FILE_READ)); - info.setWellFormed(RepInfo.UNDETERMINED); - info.show(handler); - } else if (handler.okToProcess(dirFileOrUri)) { - info.setSize(file.length()); - if (lastModified < 0) { - lastModified = file.lastModified(); - } - info.setLastModified(new Date(lastModified)); - - if (module != null) { - - // Invoke the specified module. - _logger.info("Processing " + file.getName() - + " with module " + module.getClass().getName()); - try { - if (!processFile(app, module, false, file, info)) { - return false; - } - } catch (Exception e) { - info.setMessage(new ErrorMessage(CoreMessageConstants.EXC_UNEXPECTED)); - info.setWellFormed(RepInfo.UNDETERMINED); - _logger.log(Level.SEVERE, CoreMessageConstants.EXC_UNEXPECTED, e); - } - } else { - - // Invoke all modules until one returns well-formed. If a - // module doesn't know how to validate, we don't want to - // throw arbitrary files at it, so we'll skip it. - for (Module mod : _moduleList) { - RepInfo infc = (RepInfo) info.clone(); - - if (mod.hasFeature("edu.harvard.hul.ois.jhove.canValidate")) { - try { - if (!processFile(app, mod, false, file, infc)) { - return false; - } - if (infc.getWellFormed() == RepInfo.TRUE) { - info.copy(infc); - break; - } - // We want to know what modules matched the - // signature, so we force the sigMatch - // property to be persistent. - info.setSigMatch(infc.getSigMatch()); - - } catch (Exception e) { - // The assumption is that in trying to analyze - // the wrong type of file, the module may go off - // its track and throw an exception, so we just - // continue on to the next module. - _logger.info("JHOVE caught exception: " - + e.getClass().getName()); - } - } - } - } - info.show(handler); - } - } - if (file != null && isTemp) { - file.delete(); + if (dirFileOrUri == null) { + if (module != null) { + // Show info about module. + module.applyDefaultParams(); + module.show(handler); + } else if (aboutHandler != null) { + // Show info about handler. + handler.show(aboutHandler); + } else { + // Show info about application + app.show(handler); + } + } else { + for (String aDirFileOrUri : dirFileOrUri) { + if (!process(app, module, handler, aDirFileOrUri)) { + break; } - return true; + } } - /** - * Saves a URLConnection's data stream to a temporary file. This may be - * interrupted asynchronously by calling abort, in which - * case it will delete the temporary file and return null. - */ - public File connToTempFile(URLConnection conn, RepInfo info) - throws IOException { + handler.showFooter(); + handler.close(); + } - File tempFile; - try { - tempFile = newTempFile(); - } catch (IOException ioe) { - // Throw a more meaningful exception - throw new IOException(CoreMessageConstants.EXC_TEMP_FILE_CRT); - } + /** + * Returns false if processing should be aborted. Calls itself recursively for + * directories. + */ + public boolean process(App app, Module module, OutputHandler handler, String dirFileOrUri) + throws Exception { - OutputStream outstrm = null; - DataInputStream instrm = null; - if (_bufferSize > 0) { - outstrm = new BufferedOutputStream(new FileOutputStream(tempFile), - _bufferSize); - } else { - outstrm = new BufferedOutputStream(new FileOutputStream(tempFile)); - } + if (_abort) { + return false; + } + _logger.info("Entering JhoveBase.process, file/uri = " + dirFileOrUri); + File file = null; + boolean isTemp = false; + long lastModified = -1; + + // First see if we have a URI, if not it is a directory or a file. + URI uri = null; + try { + uri = new URI(dirFileOrUri); + } catch (Exception e) { + // We may get an exception on Windows paths, + // if so then fall through and try for a file. + } + RepInfo info = new RepInfo(dirFileOrUri); + if (uri != null && uri.isAbsolute()) { + URL url = null; + try { + url = uri.toURL(); + } catch (Exception e) { + throw new JhoveException(CoreMessageConstants.EXC_URI_CONV_FAIL + dirFileOrUri); + } + URLConnection conn = url.openConnection(); + _conn = conn; + if (conn instanceof HttpsURLConnection) { try { - if (_bufferSize > 0) { - instrm = new DataInputStream(new BufferedInputStream( - conn.getInputStream(), _bufferSize)); - } else { - instrm = new DataInputStream(new BufferedInputStream( - conn.getInputStream())); - } - } catch (UnknownHostException uhe) { - tempFile.delete(); - throw new IOException(uhe.toString()); - } catch (IOException ioe) { - // IOExceptions other than UnknownHostException - tempFile.delete(); - throw ioe; + TrustManager[] tm = {new RelaxedX509TrustManager()}; + SSLContext sslContext = SSLContext.getInstance("SSL"); + sslContext.init(null, tm, new java.security.SecureRandom()); + SSLSocketFactory sf = sslContext.getSocketFactory(); + ((HttpsURLConnection) conn).setSSLSocketFactory(sf); + int code = ((HttpsURLConnection) conn).getResponseCode(); + if (200 > code || code >= 300) { + throw new JhoveException(CoreMessageConstants.EXC_URL_NOT_FND + dirFileOrUri); + } } catch (Exception e) { - // Arbitrary URL's may throw unpredictable expressions; - // treat them as IOExceptions - tempFile.delete(); - throw new IOException(e.toString()); + throw new JhoveException(CoreMessageConstants.EXC_URL_NOT_FND + dirFileOrUri); } - - Checksummer ckSummer = null; - if (_checksum) { - ckSummer = new Checksummer(); + } + lastModified = conn.getLastModified(); + + // Convert the URI to a temporary file and use for the input stream. + try { + file = connToTempFile(conn, info); + if (file == null) { + // User aborted + return false; } - _nByte = 0; - - int appModulo = 4000; - - // Copy the connection stream to the file. - // While we're here, calculate the checksums. - try { - byte by; - for (;;) { - // Make sure other threads can get in occasionally to cancel - if ((_nByte % appModulo) == 0) { - Thread.yield(); - if (_callback != null) { - _callback.callback(1, _nByte); - } - // In order to avoid doing too many callbacks, limit - // the checking to a number of bytes at least 1/10 of - // the bytes read so far. - if (appModulo * 10 < _nByte) { - appModulo = (int) (_nByte / 10); - } - } - if (_abort) { - // Asynchronous abort requested. Clean up. - instrm.close(); - outstrm.close(); - tempFile.delete(); - return null; - } - int ch = instrm.readUnsignedByte(); - if (ckSummer != null) { - ckSummer.update(ch); - } - by = Checksum.unsignedByteToByte(ch); - _nByte++; - outstrm.write(by); - } - } catch (EOFException eofe) { - // This is the normal way for detecting we're done. + isTemp = true; + } catch (IOException ioe) { + _conn = null; + String message = "Cannot read URL: " + dirFileOrUri; + _logger.info(message); + String ioeMessage = ioe.getMessage(); + if (ioeMessage != null) { + message += " (" + ioeMessage + ")"; } + throw new JhoveException(message); + } + if (conn instanceof HttpsURLConnection) { + ((HttpsURLConnection) conn).disconnect(); + } + _conn = null; + } else { + file = new File(dirFileOrUri); + } - // The caller is responsible for disconnecting conn. - instrm.close(); - outstrm.close(); + if (file.isDirectory()) { + File[] files = file.listFiles(); + info = null; // Free up unused RepInfo before recursing - // Update RepInfo - info.setSize(_nByte); - if (ckSummer != null) { - info.setChecksum(new Checksum(ckSummer.getCRC32(), - ChecksumType.CRC32)); - String value = ckSummer.getMD5(); - if (value != null) { - info.setChecksum(new Checksum(value, ChecksumType.MD5)); - } - value = ckSummer.getSHA1(); - if (value != null) { - info.setChecksum(new Checksum(value, ChecksumType.SHA1)); - } - value = ckSummer.getSHA256(); - if (value != null) { - info.setChecksum(new Checksum(value, ChecksumType.SHA256)); - } - } - return tempFile; - } + // Sort the files in ascending order by filename. + Arrays.sort(files); - /** - * Aborts an activity. This simply sets a flag; whether anything is aborted - * depends on what activity is happening. - */ - public void abort() { - _abort = true; - HttpsURLConnection conn = null; - if (_conn instanceof HttpsURLConnection) { - conn = (HttpsURLConnection) _conn; - } - // If we're stuck in socket I/O, then there is no way - // to kill the thread cleanly. Wait a few seconds, - // and if we're still not terminated, pull the plug on - // the socket. - try { - Thread.sleep(4000); - } catch (InterruptedException ie) { + handler.startDirectory(file.getCanonicalPath()); + for (int i = 0; i < files.length; i++) { + if (!process(app, module, handler, files[i].getCanonicalPath())) { + return false; } - if (conn != null) { - // This is a non-deprecated way of bringing the connection - // to a screeching halt. disconnect will (we hope) close - // the underlying socket, killing any hanging I/O. - conn.disconnect(); + } + handler.endDirectory(); + } else { + + if (!file.exists()) { + _logger.info("File not found"); + info.setMessage(new ErrorMessage(CoreMessageConstants.ERR_FILE_NOT_FOUND)); + info.setWellFormed(RepInfo.UNDETERMINED); + info.show(handler); + } else if (!file.isFile() || !file.canRead()) { + _logger.info("File cannot be read"); + info.setMessage(new ErrorMessage(CoreMessageConstants.ERR_FILE_READ)); + info.setWellFormed(RepInfo.UNDETERMINED); + info.show(handler); + } else if (handler.okToProcess(dirFileOrUri)) { + info.setSize(file.length()); + if (lastModified < 0) { + lastModified = file.lastModified(); } - } + info.setLastModified(new Date(lastModified)); - /** - * Processes the file. Returns false if aborted, or if - * the module is incapable of validation. This shouldn't be called if - * the module doesn't have the validation feature. - */ - public boolean processFile(App app, Module module, boolean verbose, - File file, RepInfo info) throws Exception { + if (module != null) { - if (!module.hasFeature("edu.harvard.hul.ois.jhove.canValidate")) { - return false; - } - if (_callback != null) { - _callback.callback(2, info.getUri()); - } - module.setApp(app); - module.setBase(this); - module.setVerbosity(verbose ? Module.MAXIMUM_VERBOSITY - : Module.MINIMUM_VERBOSITY); - module.applyDefaultParams(); - if (module.isRandomAccess()) { - - // Module needs random access input. - RandomAccessFile raf = new RandomAccessFile(file, "r"); - try { - if (_signature) { - module.checkSignatures(file, raf, info); - } else { - module.parse(raf, info); - } - } finally { - raf.close(); + // Invoke the specified module. + _logger.info( + "Processing " + file.getName() + " with module " + module.getClass().getName()); + try { + if (!processFile(app, module, false, file, info)) { + return false; } + } catch (Exception e) { + info.setMessage(new ErrorMessage(CoreMessageConstants.EXC_UNEXPECTED)); + info.setWellFormed(RepInfo.UNDETERMINED); + _logger.log(Level.SEVERE, CoreMessageConstants.EXC_UNEXPECTED, e); + } } else { - // Module accepts stream input. - InputStream stream = new FileInputStream(file); - try { - if (_signature) { - module.checkSignatures(file, stream, info); - } else { - int parseIndex = module.parse(stream, info, 0); - // If parse returns non-zero, reparse with a fresh stream. - while (parseIndex != 0) { - stream.close(); - stream = new FileInputStream(file); - parseIndex = module.parse(stream, info, parseIndex); - } + // Invoke all modules until one returns well-formed. If a + // module doesn't know how to validate, we don't want to + // throw arbitrary files at it, so we'll skip it. + for (Module mod : _moduleList) { + RepInfo infc = (RepInfo) info.clone(); + + if (mod.hasFeature("edu.harvard.hul.ois.jhove.canValidate")) { + try { + if (!processFile(app, mod, false, file, infc)) { + return false; } - } finally { - stream.close(); + if (infc.getWellFormed() == RepInfo.TRUE) { + info.copy(infc); + break; + } + // We want to know what modules matched the + // signature, so we force the sigMatch + // property to be persistent. + info.setSigMatch(infc.getSigMatch()); + + } catch (Exception e) { + // The assumption is that in trying to analyze + // the wrong type of file, the module may go off + // its track and throw an exception, so we just + // continue on to the next module. + _logger.info("JHOVE caught exception: " + e.getClass().getName()); + } } + } } - return true; // Successful processing - } - - /** - * Creates a temporary file with a unique name. The file will be deleted - * when the application exits. - */ - public File tempFile() throws IOException { - File file; - - // If no temporary directory has been specified, - // use Java's default temporary directory. - if (_tempDir == null) { - file = File.createTempFile("JHOV", ""); - } else { - File dir = new File(_tempDir); - file = File.createTempFile("JHOV", "", dir); - } - file.deleteOnExit(); - - return file; + info.show(handler); + } } - - /** - * Returns the abort flag. - */ - public boolean getAbort() { - return _abort; + if (file != null && isTemp) { + file.delete(); } - - /** - * Returns buffer size. A value of -1 signifies that the invoking code - * should assume the default buffer size. - */ - public int getBufferSize() { - return _bufferSize; + return true; + } + + /** + * Saves a URLConnection's data stream to a temporary file. This may be interrupted asynchronously + * by calling abort, in which case it will delete the temporary file and return + * null. + */ + public File connToTempFile(URLConnection conn, RepInfo info) throws IOException { + + File tempFile; + try { + tempFile = newTempFile(); + } catch (IOException ioe) { + // Throw a more meaningful exception + throw new IOException(CoreMessageConstants.EXC_TEMP_FILE_CRT); } - /** - * Returns the configuration file. - */ - public String getConfigFile() { - return _configFile; + OutputStream outstrm = null; + DataInputStream instrm = null; + if (_bufferSize > 0) { + outstrm = new BufferedOutputStream(new FileOutputStream(tempFile), _bufferSize); + } else { + outstrm = new BufferedOutputStream(new FileOutputStream(tempFile)); } - - /** - * Returns the engine date (the date at which this instance was created). - */ - public Date getDate() { - return RELEASE_DETAILS.getBuildDate(); + try { + if (_bufferSize > 0) { + instrm = new DataInputStream(new BufferedInputStream(conn.getInputStream(), _bufferSize)); + } else { + instrm = new DataInputStream(new BufferedInputStream(conn.getInputStream())); + } + } catch (UnknownHostException uhe) { + tempFile.delete(); + throw new IOException(uhe.toString()); + } catch (IOException ioe) { + // IOExceptions other than UnknownHostException + tempFile.delete(); + throw ioe; + } catch (Exception e) { + // Arbitrary URL's may throw unpredictable expressions; + // treat them as IOExceptions + tempFile.delete(); + throw new IOException(e.toString()); } - /** - * Returns the output encoding. - */ - public String getEncoding() { - return _encoding; + Checksummer ckSummer = null; + if (_checksum) { + ckSummer = new Checksummer(); } - - /** - * Returns a handler by name. - */ - public OutputHandler getHandler(String name) { - OutputHandler handler = null; - if (name != null) { - handler = _handlerMap.get(name.toLowerCase()); + _nByte = 0; + + int appModulo = 4000; + + // Copy the connection stream to the file. + // While we're here, calculate the checksums. + try { + byte by; + for (; ; ) { + // Make sure other threads can get in occasionally to cancel + if ((_nByte % appModulo) == 0) { + Thread.yield(); + if (_callback != null) { + _callback.callback(1, _nByte); + } + // In order to avoid doing too many callbacks, limit + // the checking to a number of bytes at least 1/10 of + // the bytes read so far. + if (appModulo * 10 < _nByte) { + appModulo = (int) (_nByte / 10); + } } - return handler; - } - - /** - * Returns map of handler names to handlers. - */ - public Map getHandlerMap() { - return _handlerMap; - } - - /** - * Returns the list of handlers. - */ - public List getHandlerList() { - return _handlerList; - } - - /** - * Returns the JHOVE home directory. - */ - public String getJhoveHome() { - return _jhoveHome; - } - - /** - * Returns a module by name. - */ - public Module getModule(String name) { - Module module = null; - if (name != null) { - module = _moduleMap.get(name.toLowerCase()); + if (_abort) { + // Asynchronous abort requested. Clean up. + instrm.close(); + outstrm.close(); + tempFile.delete(); + return null; } - return module; - } - - /** - * Returns the map of module names to modules. - */ - public Map getModuleMap() { - return _moduleMap; - } - - /** - * Returns the list of modules. - */ - public List getModuleList() { - return _moduleList; - } - - /** - * Returns the engine name. - */ - public String getName() { - return _name; - } - - /** - * Returns the output file. - */ - public String getOuputFile() { - return _outputFile; - } - - /** - * Returns the engine release. - */ - public String getRelease() { - return RELEASE_DETAILS.getVersion(); - } - - /** - * Returns the engine rights statement. - */ - public String getRights() { - return RELEASE_DETAILS.getRights(); - } - - /** - * Returns the SAX class. - */ - public String getSaxClass() { - return _saxClass; + int ch = instrm.readUnsignedByte(); + if (ckSummer != null) { + ckSummer.update(ch); + } + by = Checksum.unsignedByteToByte(ch); + _nByte++; + outstrm.write(by); + } + } catch (EOFException eofe) { + // This is the normal way for detecting we're done. } - /** - * Returns the temporary directory path. - */ - public String getTempDirectory() { - return _tempDir; + // The caller is responsible for disconnecting conn. + instrm.close(); + outstrm.close(); + + // Update RepInfo + info.setSize(_nByte); + if (ckSummer != null) { + info.setChecksum(new Checksum(ckSummer.getCRC32(), ChecksumType.CRC32)); + String value = ckSummer.getMD5(); + if (value != null) { + info.setChecksum(new Checksum(value, ChecksumType.MD5)); + } + value = ckSummer.getSHA1(); + if (value != null) { + info.setChecksum(new Checksum(value, ChecksumType.SHA1)); + } + value = ckSummer.getSHA256(); + if (value != null) { + info.setChecksum(new Checksum(value, ChecksumType.SHA256)); + } } - - /** - * Returns the maximum number of bytes to check, for modules that look for - * an indefinitely positioned signature or check the first sigBytes bytes - * in lieu of a signature. - */ - public int getSigBytes() { - return _sigBytes; + return tempFile; + } + + /** + * Aborts an activity. This simply sets a flag; whether anything is aborted depends on what + * activity is happening. + */ + public void abort() { + _abort = true; + HttpsURLConnection conn = null; + if (_conn instanceof HttpsURLConnection) { + conn = (HttpsURLConnection) _conn; } - - /** - * Returns the directory designated for saving files. This is simply the - * directory most recently set by setSaveDirectory. - */ - public File getSaveDirectory() { - return _saveDir; + // If we're stuck in socket I/O, then there is no way + // to kill the thread cleanly. Wait a few seconds, + // and if we're still not terminated, pull the plug on + // the socket. + try { + Thread.sleep(4000); + } catch (InterruptedException ie) { } - - /** - * Returns true if checksums are requested. - */ - public boolean getChecksumFlag() { - return _checksum; + if (conn != null) { + // This is a non-deprecated way of bringing the connection + // to a screeching halt. disconnect will (we hope) close + // the underlying socket, killing any hanging I/O. + conn.disconnect(); } + } - /** - * Returns true if raw output is requested. Raw output means - * numeric rather than symbolic output; its exact interpretation is up to - * the module, but generally applies to named flags. - */ - public boolean getShowRawFlag() { - return _showRaw; - } + /** + * Processes the file. Returns false if aborted, or if the module is incapable of + * validation. This shouldn't be called if the module doesn't have the validation feature. + */ + public boolean processFile(App app, Module module, boolean verbose, File file, RepInfo info) + throws Exception { - /** - * Returns the "check signature only" flag. - */ - public boolean getSignatureFlag() { - return _signature; + if (!module.hasFeature("edu.harvard.hul.ois.jhove.canValidate")) { + return false; } - - /** - * Returns the requested MIX schema version. - */ - public String getMixVersion() { - return _mixVsn; + if (_callback != null) { + _callback.callback(2, info.getUri()); } - - /** - * Sets the buffer size. A value of -1 signifies that the invoking code will - * assume the default buffer size. - * - * Any non-negative value less than 1024 will result in a buffer size of - * 1024. - */ - public void setBufferSize(int bufferSize) { - if (bufferSize >= 0 && bufferSize < 1024) { - _bufferSize = 1024; + module.setApp(app); + module.setBase(this); + module.setVerbosity(verbose ? Module.MAXIMUM_VERBOSITY : Module.MINIMUM_VERBOSITY); + module.applyDefaultParams(); + if (module.isRandomAccess()) { + + // Module needs random access input. + RandomAccessFile raf = new RandomAccessFile(file, "r"); + try { + if (_signature) { + module.checkSignatures(file, raf, info); } else { - _bufferSize = bufferSize; + module.parse(raf, info); } - } - - /** - * Sets the output encoding. - */ - public void setEncoding(String encoding) { - _encoding = encoding; - } - - /** - * Sets the temporary directory path. - */ - public void setTempDirectory(String tempDir) { - _tempDir = tempDir; - } - - /** - * Sets the log level. The value should be the name of a predefined instance - * of java.util.logging.Level, e.g., "WARNING", "INFO", "ALL". This will - * override the config file setting. - */ - public void setLogLevel(String level) { - _logLevel = level; - if (level != null) { - try { - _logger.setLevel(Level.parse(_logLevel)); - } catch (Exception e) { - } + } finally { + raf.close(); + } + } else { + + // Module accepts stream input. + InputStream stream = new FileInputStream(file); + try { + if (_signature) { + module.checkSignatures(file, stream, info); + } else { + int parseIndex = module.parse(stream, info, 0); + // If parse returns non-zero, reparse with a fresh stream. + while (parseIndex != 0) { + stream.close(); + stream = new FileInputStream(file); + parseIndex = module.parse(stream, info, parseIndex); + } } + } finally { + stream.close(); + } } - - /** - * Sets the value to be returned by doChecksum. - */ - public void setChecksumFlag(boolean checksum) { - _checksum = checksum; + return true; // Successful processing + } + + /** + * Creates a temporary file with a unique name. The file will be deleted when the application + * exits. + */ + public File tempFile() throws IOException { + File file; + + // If no temporary directory has been specified, + // use Java's default temporary directory. + if (_tempDir == null) { + file = File.createTempFile("JHOV", ""); + } else { + File dir = new File(_tempDir); + file = File.createTempFile("JHOV", "", dir); } - - /** - * Sets the value to be returned by getShowRawFlag, which - * determines if only raw numeric values should be output. - */ - public void setShowRawFlag(boolean raw) { - _showRaw = raw; + file.deleteOnExit(); + + return file; + } + + /** Returns the abort flag. */ + public boolean getAbort() { + return _abort; + } + + /** + * Returns buffer size. A value of -1 signifies that the invoking code should assume the default + * buffer size. + */ + public int getBufferSize() { + return _bufferSize; + } + + /** Returns the configuration file. */ + public String getConfigFile() { + return _configFile; + } + + /** Returns the engine date (the date at which this instance was created). */ + public Date getDate() { + return RELEASE_DETAILS.getBuildDate(); + } + + /** Returns the output encoding. */ + public String getEncoding() { + return _encoding; + } + + /** Returns a handler by name. */ + public OutputHandler getHandler(String name) { + OutputHandler handler = null; + if (name != null) { + handler = _handlerMap.get(name.toLowerCase()); } - - /** - * Sets the "check signature only" flag. - */ - public void setSignatureFlag(boolean signature) { - _signature = signature; + return handler; + } + + /** Returns map of handler names to handlers. */ + public Map getHandlerMap() { + return _handlerMap; + } + + /** Returns the list of handlers. */ + public List getHandlerList() { + return _handlerList; + } + + /** Returns the JHOVE home directory. */ + public String getJhoveHome() { + return _jhoveHome; + } + + /** Returns a module by name. */ + public Module getModule(String name) { + Module module = null; + if (name != null) { + module = _moduleMap.get(name.toLowerCase()); } - - /** - * Sets the default directory for subsequent save operations. - */ - public void setSaveDirectory(File dir) { - _saveDir = dir; + return module; + } + + /** Returns the map of module names to modules. */ + public Map getModuleMap() { + return _moduleMap; + } + + /** Returns the list of modules. */ + public List getModuleList() { + return _moduleList; + } + + /** Returns the engine name. */ + public String getName() { + return _name; + } + + /** Returns the output file. */ + public String getOuputFile() { + return _outputFile; + } + + /** Returns the engine release. */ + public String getRelease() { + return RELEASE_DETAILS.getVersion(); + } + + /** Returns the engine rights statement. */ + public String getRights() { + return RELEASE_DETAILS.getRights(); + } + + /** Returns the SAX class. */ + public String getSaxClass() { + return _saxClass; + } + + /** Returns the temporary directory path. */ + public String getTempDirectory() { + return _tempDir; + } + + /** + * Returns the maximum number of bytes to check, for modules that look for an indefinitely + * positioned signature or check the first sigBytes bytes in lieu of a signature. + */ + public int getSigBytes() { + return _sigBytes; + } + + /** + * Returns the directory designated for saving files. This is simply the directory most recently + * set by setSaveDirectory. + */ + public File getSaveDirectory() { + return _saveDir; + } + + /** Returns true if checksums are requested. */ + public boolean getChecksumFlag() { + return _checksum; + } + + /** + * Returns true if raw output is requested. Raw output means numeric rather than + * symbolic output; its exact interpretation is up to the module, but generally applies to named + * flags. + */ + public boolean getShowRawFlag() { + return _showRaw; + } + + /** Returns the "check signature only" flag. */ + public boolean getSignatureFlag() { + return _signature; + } + + /** Returns the requested MIX schema version. */ + public String getMixVersion() { + return _mixVsn; + } + + /** + * Sets the buffer size. A value of -1 signifies that the invoking code will assume the default + * buffer size. + * + *

Any non-negative value less than 1024 will result in a buffer size of 1024. + */ + public void setBufferSize(int bufferSize) { + if (bufferSize >= 0 && bufferSize < 1024) { + _bufferSize = 1024; + } else { + _bufferSize = bufferSize; } - - /** - * Sets the current thread for parsing. - */ - public void setCurrentThread(Thread t) { - _currentThread = t; + } + + /** Sets the output encoding. */ + public void setEncoding(String encoding) { + _encoding = encoding; + } + + /** Sets the temporary directory path. */ + public void setTempDirectory(String tempDir) { + _tempDir = tempDir; + } + + /** + * Sets the log level. The value should be the name of a predefined instance of + * java.util.logging.Level, e.g., "WARNING", "INFO", "ALL". This will override the config file + * setting. + */ + public void setLogLevel(String level) { + _logLevel = level; + if (level != null) { + try { + _logger.setLevel(Level.parse(_logLevel)); + } catch (Exception e) { + } } - - /** - * Resets the abort flag. This must be called at the beginning of any - * activity for which the abort flag may subsequently be set. - */ - public void resetAbort() { - _abort = false; + } + + /** Sets the value to be returned by doChecksum. */ + public void setChecksumFlag(boolean checksum) { + _checksum = checksum; + } + + /** + * Sets the value to be returned by getShowRawFlag, which determines if only raw + * numeric values should be output. + */ + public void setShowRawFlag(boolean raw) { + _showRaw = raw; + } + + /** Sets the "check signature only" flag. */ + public void setSignatureFlag(boolean signature) { + _signature = signature; + } + + /** Sets the default directory for subsequent save operations. */ + public void setSaveDirectory(File dir) { + _saveDir = dir; + } + + /** Sets the current thread for parsing. */ + public void setCurrentThread(Thread t) { + _currentThread = t; + } + + /** + * Resets the abort flag. This must be called at the beginning of any activity for which the abort + * flag may subsequently be set. + */ + public void resetAbort() { + _abort = false; + } + + /** + * Uses the user.home property to locate the configuration file. The file is expected to be in the + * subdirectory named by CONFIG_DIR under the home directory, and to be named jhove.conf + * . Returns null if no such file is found. + */ + public static String getConfigFileFromProperties() { + String configFile = getFromProperties(CONFIG_PROPERTY); + if (configFile == null) { + try { + String fs = System.getProperty("file.separator"); + configFile = + System.getProperty("user.home") + fs + JHOVE_DIR + fs + CONFIG_DIR + fs + "jhove.conf"; + } catch (Exception e) { + } } - - /** - * Uses the user.home property to locate the configuration file. The file is - * expected to be in the subdirectory named by CONFIG_DIR under the home - * directory, and to be named jhove.conf. Returns - * null if no such file is found. - */ - public static String getConfigFileFromProperties() { - String configFile = getFromProperties(CONFIG_PROPERTY); - if (configFile == null) { - try { - String fs = System.getProperty("file.separator"); - configFile = System.getProperty("user.home") + fs + JHOVE_DIR - + fs + CONFIG_DIR + fs + "jhove.conf"; - } catch (Exception e) { - } - } - return configFile; + return configFile; + } + + /** + * Returns the value of the property edu.harvard.hul.ois.jhove.saxClass, which should + * be the name of the main SAX class. Returns null if no such property has been set + * up. + */ + public static String getSaxClassFromProperties() { + return getFromProperties(SAX_PROPERTY); + } + + /** Returns a named value from the properties file. */ + public static String getFromProperties(String name) { + String value = null; + try { + String fs = System.getProperty("file.separator"); + Properties props = new Properties(); + String propsFile = System.getProperty("user.home") + fs + JHOVE_DIR + fs + "jhove.properties"; + FileInputStream stream = new FileInputStream(propsFile); + props.load(stream); + stream.close(); + + value = props.getProperty(name); + } catch (Exception e) { } - - /** - * Returns the value of the property - * edu.harvard.hul.ois.jhove.saxClass, which should be the name - * of the main SAX class. Returns null if no such property has - * been set up. - */ - public static String getSaxClassFromProperties() { - return getFromProperties(SAX_PROPERTY); + return value; + } + + /** + * Creates an output PrintWriter. + * + * @param outputFile Output filepath. If null, writer goes to System.out. + * @param encoding Character encoding. Must not be null. + */ + protected static PrintWriter makeWriter(String outputFile, String encoding) + throws JhoveException { + + PrintWriter output = null; + OutputStreamWriter osw = null; + if (outputFile != null) { + try { + FileOutputStream stream = new FileOutputStream(outputFile); + osw = new OutputStreamWriter(stream, encoding); + output = new PrintWriter(osw); + } catch (UnsupportedEncodingException uee) { + throw new JhoveException(CoreMessageConstants.EXC_CHAR_ENC_UNSPPTD + encoding); + } catch (FileNotFoundException fnfe) { + throw new JhoveException(CoreMessageConstants.EXC_FILE_OPEN + outputFile); + } } - - /** - * Returns a named value from the properties file. - */ - public static String getFromProperties(String name) { - String value = null; - try { - String fs = System.getProperty("file.separator"); - Properties props = new Properties(); - String propsFile = System.getProperty("user.home") + fs + JHOVE_DIR - + fs + "jhove.properties"; - FileInputStream stream = new FileInputStream(propsFile); - props.load(stream); - stream.close(); - - value = props.getProperty(name); - } catch (Exception e) { - } - return value; + if (output == null) { + try { + osw = new OutputStreamWriter(System.out, encoding); + } catch (UnsupportedEncodingException uee) { + throw new JhoveException(CoreMessageConstants.EXC_CHAR_ENC_UNSPPTD + encoding); + } + output = new PrintWriter(osw); } - - /** - * Creates an output PrintWriter. - * - * @param outputFile - * Output filepath. If null, writer goes to System.out. - * @param encoding - * Character encoding. Must not be null. - */ - protected static PrintWriter makeWriter(String outputFile, String encoding) - throws JhoveException { - - PrintWriter output = null; - OutputStreamWriter osw = null; - if (outputFile != null) { - try { - FileOutputStream stream = new FileOutputStream(outputFile); - osw = new OutputStreamWriter(stream, encoding); - output = new PrintWriter(osw); - } catch (UnsupportedEncodingException uee) { - throw new JhoveException(CoreMessageConstants.EXC_CHAR_ENC_UNSPPTD - + encoding); - } catch (FileNotFoundException fnfe) { - throw new JhoveException(CoreMessageConstants.EXC_FILE_OPEN - + outputFile); - } - } - if (output == null) { - try { - osw = new OutputStreamWriter(System.out, encoding); - } catch (UnsupportedEncodingException uee) { - throw new JhoveException(CoreMessageConstants.EXC_CHAR_ENC_UNSPPTD - + encoding); - } - output = new PrintWriter(osw); - } - return output; + return output; + } + + /** + * Creates a temporary file with a unique name. The file will be deleted when the application + * exits. + */ + public File newTempFile() throws IOException { + return tempFile(); + } + + /** A HostnameVerifier for HTTPS connections that will never ask for certificates. */ + private class NaiveHostnameVerifier implements HostnameVerifier { + public boolean verify(String hostname, SSLSession session) { + return true; } + } - /** - * Creates a temporary file with a unique name. The file will be deleted - * when the application exits. - */ - public File newTempFile() throws IOException { - return tempFile(); + /** A TrustManager which should accept all certificates. */ + private class RelaxedX509TrustManager implements X509TrustManager { + public boolean isClientTrusted(java.security.cert.X509Certificate[] chain) { + return true; } - /** - * A HostnameVerifier for HTTPS connections that will never ask for - * certificates. - */ - private class NaiveHostnameVerifier implements HostnameVerifier { - public boolean verify(String hostname, SSLSession session) { - return true; - } + public boolean isServerTrusted(java.security.cert.X509Certificate[] chain) { + return true; } - /** - * A TrustManager which should accept all certificates. - */ - private class RelaxedX509TrustManager implements X509TrustManager { - public boolean isClientTrusted( - java.security.cert.X509Certificate[] chain) { - return true; - } - - public boolean isServerTrusted( - java.security.cert.X509Certificate[] chain) { - return true; - } - - public java.security.cert.X509Certificate[] getAcceptedIssuers() { - return null; - } + public java.security.cert.X509Certificate[] getAcceptedIssuers() { + return null; + } - public void checkClientTrusted( - java.security.cert.X509Certificate[] chain) { - } + public void checkClientTrusted(java.security.cert.X509Certificate[] chain) {} - public void checkClientTrusted( - java.security.cert.X509Certificate[] chain, String s) { - } + public void checkClientTrusted(java.security.cert.X509Certificate[] chain, String s) {} - public void checkServerTrusted( - java.security.cert.X509Certificate[] chain) { - } + public void checkServerTrusted(java.security.cert.X509Certificate[] chain) {} - public void checkServerTrusted( - java.security.cert.X509Certificate[] chain, String s) { - } - } + public void checkServerTrusted(java.security.cert.X509Certificate[] chain, String s) {} + } } diff --git a/jhove-core/src/main/java/edu/harvard/hul/ois/jhove/JhoveException.java b/jhove-core/src/main/java/edu/harvard/hul/ois/jhove/JhoveException.java index 9f6a0e71d..965a2230a 100644 --- a/jhove-core/src/main/java/edu/harvard/hul/ois/jhove/JhoveException.java +++ b/jhove-core/src/main/java/edu/harvard/hul/ois/jhove/JhoveException.java @@ -1,20 +1,20 @@ -/********************************************************************** - * Jhove - JSTOR/Harvard Object Validation Environment Copyright 2004 by JSTOR - * and the President and Fellows of Harvard College - **********************************************************************/ - +/** + * ******************************************************************** Jhove - JSTOR/Harvard Object + * Validation Environment Copyright 2004 by JSTOR and the President and Fellows of Harvard College + * ******************************************************************** + */ package edu.harvard.hul.ois.jhove; public class JhoveException extends Exception { - public JhoveException() { - super(); - } + public JhoveException() { + super(); + } - public JhoveException(String message) { - super(message); - } + public JhoveException(String message) { + super(message); + } - public JhoveException(String message, Throwable cause) { - super(message, cause); - } + public JhoveException(String message, Throwable cause) { + super(message, cause); + } } diff --git a/jhove-core/src/main/java/edu/harvard/hul/ois/jhove/MacStuff.java b/jhove-core/src/main/java/edu/harvard/hul/ois/jhove/MacStuff.java index d6a40d7b3..fdaf0f8b5 100644 --- a/jhove-core/src/main/java/edu/harvard/hul/ois/jhove/MacStuff.java +++ b/jhove-core/src/main/java/edu/harvard/hul/ois/jhove/MacStuff.java @@ -1,88 +1,77 @@ -/********************************************************************** - * Jhove - JSTOR/Harvard Object Validation Environment - * Copyright 2003 by JSTOR and the President and Fellows of Harvard College - **********************************************************************/ - +/** + * ******************************************************************** Jhove - JSTOR/Harvard Object + * Validation Environment Copyright 2003 by JSTOR and the President and Fellows of Harvard College + * ******************************************************************** + */ package edu.harvard.hul.ois.jhove; import java.io.*; import java.lang.reflect.*; -//import com.apple.eio.FileManager; +// import com.apple.eio.FileManager; /** - * Code specific to Macintosh Java. This class consists of static - * methods, and should not be instantiated. Its methods should be - * called only on the Macintosh OS X platform. It requires the + * Code specific to Macintosh Java. This class consists of static methods, and should not be + * instantiated. Its methods should be called only on the Macintosh OS X platform. It requires the * package {@code com.apple.eio.FileManager}. * * @author Gary McGath */ public class MacStuff { - /** Private constructor to prevent instantiation.*/ - private MacStuff() { - } + /** Private constructor to prevent instantiation. */ + private MacStuff() {} - /** - * Determines if we're running on a Macintosh, so appropriate UI - * adjustments can be made. In accordance with Apple's recommendations, - * this checks for the existence of the {@code mrj.version} property - * rather than checking the {@code os.name} property. - */ - public static boolean isMacintosh() { - return (System.getProperty("mrj.version") != null); - } + /** + * Determines if we're running on a Macintosh, so appropriate UI adjustments can be made. In + * accordance with Apple's recommendations, this checks for the existence of the {@code + * mrj.version} property rather than checking the {@code os.name} property. + */ + public static boolean isMacintosh() { + return (System.getProperty("mrj.version") != null); + } - /** - * Returns {@code true} if a file has the given file type. - * This method uses {@code FileManager} in a dynamic way, so - * that it will merely throw a {@code ClassNotFound} exception - * if it fails. - *

- * Currently this code isn't actually used, since the - * Jhove application is specified as checking only internal - * signatures. Should some future version or add-on code - * wish to use it, the code should look something like this: - *

-	 * {@code
-     *     try {
-     *         if (sig.getType() == SignatureType.FILETYPE &&
-     *                 MacStuff.isMacintosh()) {
-     *             if (!MacStuff.fileHasType(file, sig.getValueString())) {
-     *                 info.setConsistent(false);
-     *             }
-     *         }
-     *     }
-     *     catch (ClassNotFoundException cnfe) {
-     *         // Mac classes missing -- can't check filetype.
-     *     }
-	 * }
-     * 
- */ - public static boolean fileHasType(File file, String type) - throws ClassNotFoundException - { - // Need to make this completely dynamic. - //return type.equals (FileManager.getFileType (file)); - try { - if (type == null) { - return false; - } - Class fmClass = Class.forName("com.apple.eio.FileManager"); - Class[] params = new Class[1]; - params[0] = Class.forName("java.io.File"); - Object[] args = new Object[1]; - args[0] = file; - Method method = fmClass.getMethod("getFileType", params); - String fType = (String) method.invoke(null, args); - return (type.equals(fType)); - } - catch (ClassNotFoundException cnfe) { - throw cnfe; - } - catch (Exception e) { - return false; - } + /** + * Returns {@code true} if a file has the given file type. This method uses {@code FileManager} in + * a dynamic way, so that it will merely throw a {@code ClassNotFound} exception if it fails. + * + *

Currently this code isn't actually used, since the Jhove application is specified as + * checking only internal signatures. Should some future version or add-on code wish to use it, + * the code should look something like this: + * + *

{@code
+   * try {
+   *     if (sig.getType() == SignatureType.FILETYPE &&
+   *             MacStuff.isMacintosh()) {
+   *         if (!MacStuff.fileHasType(file, sig.getValueString())) {
+   *             info.setConsistent(false);
+   *         }
+   *     }
+   * }
+   * catch (ClassNotFoundException cnfe) {
+   *     // Mac classes missing -- can't check filetype.
+   * }
+   * }
+ */ + public static boolean fileHasType(File file, String type) throws ClassNotFoundException { + // Need to make this completely dynamic. + // return type.equals (FileManager.getFileType (file)); + try { + if (type == null) { + return false; + } + Class fmClass = Class.forName("com.apple.eio.FileManager"); + Class[] params = new Class[1]; + params[0] = Class.forName("java.io.File"); + Object[] args = new Object[1]; + args[0] = file; + Method method = fmClass.getMethod("getFileType", params); + String fType = (String) method.invoke(null, args); + return (type.equals(fType)); + } catch (ClassNotFoundException cnfe) { + throw cnfe; + } catch (Exception e) { + return false; } + } } diff --git a/jhove-core/src/main/java/edu/harvard/hul/ois/jhove/Message.java b/jhove-core/src/main/java/edu/harvard/hul/ois/jhove/Message.java index 8e8f13090..cba3476d4 100644 --- a/jhove-core/src/main/java/edu/harvard/hul/ois/jhove/Message.java +++ b/jhove-core/src/main/java/edu/harvard/hul/ois/jhove/Message.java @@ -1,217 +1,170 @@ -/********************************************************************** - * Jhove - JSTOR/Harvard Object Validation Environment - * Copyright 2003 by JSTOR and the President and Fellows of Harvard College - **********************************************************************/ - +/** + * ******************************************************************** Jhove - JSTOR/Harvard Object + * Validation Environment Copyright 2003 by JSTOR and the President and Fellows of Harvard College + * ******************************************************************** + */ package edu.harvard.hul.ois.jhove; import edu.harvard.hul.ois.jhove.messages.JhoveMessage; import edu.harvard.hul.ois.jhove.messages.JhoveMessages; -/** - * This class encapsulates a message to be displayed. - */ +/** This class encapsulates a message to be displayed. */ public abstract class Message { - /****************************************************************** - * PUBLIC CLASS FIELDS. - ******************************************************************/ - - /** Value indicating a null offset. */ - public static final long NULL = -1; - - /****************************************************************** - * PRIVATE INSTANCE FIELDS. - ******************************************************************/ - - protected final JhoveMessage message; - - /** Additional information. */ - protected final String _subMessage; - - /** Byte offset to which message applies. */ - protected final long _offset; - - /****************************************************************** - * CLASS CONSTRUCTOR. - ******************************************************************/ - - /** - * Creates a Message with an unknown identifier for backward compatibility. - * This constructor cannot be invoked directly, since Message is abstract. - * - * @param message - * Human-readable message text. - */ - protected Message(final String message) { - this(JhoveMessages.getMessageInstance(JhoveMessages.NO_ID, message)); - } - - /** - * Creates a Message with an identifier. - * This constructor cannot be invoked directly, since Message is abstract. - * - * @param message - * The message text and its identifier. - */ - protected Message(final JhoveMessage message) { - this(message, message.getSubMessage(), NULL); - } - - /** - * Creates a Message. - * This constructor cannot be invoked directly, - * since Message is abstract. The second argument - * adds secondary details to the primary message; - * the message will typically be displayed in the - * form "message: subMessage". - * - * @param message - * Human-readable message text. - * @param subMessage - * Human-readable additional information. - */ - protected Message(final String message, final String subMessage) { - this(JhoveMessages.getMessageInstance(JhoveMessages.NO_ID, message), - subMessage); - } - - /** - * Creates a Message with an identifier. - * This constructor cannot be invoked directly, - * since Message is abstract. The second argument - * adds secondary details to the primary message; - * the message will typically be displayed in the - * form "message: subMessage". - * - * @param message - * The message text and its identifier. - * @param subMessage - * Human-readable additional information. - */ - protected Message(final JhoveMessage message, final String subMessage) { - this(message, subMessage, NULL); - } - - /** - * Creates a Message. - * This constructor cannot be invoked directly, - * since Message is abstract. The second argument - * adds secondary details to the primary message; - * the message will typically be displayed in the - * form "message: subMessage". - * - * @param message - * Human-readable message text. - * @param offset - * Byte offset associated with the message. - */ - protected Message(final String message, final long offset) { - this(JhoveMessages.getMessageInstance(JhoveMessages.NO_ID, message), - offset); - } - - /** - * Creates a Message with an identifier. - * This constructor cannot be invoked directly, - * since Message is abstract. The second argument - * adds secondary details to the primary message; - * the message will typically be displayed in the - * form "message: subMessage". - * - * @param message - * The message text and its identifier. - * @param offset - * Byte offset associated with the message. - */ - protected Message(final JhoveMessage message, final long offset) { - this(message, message.getSubMessage(), offset); - } - - /** - * Creates a Message. - * This constructor cannot be invoked directly, - * since Message is abstract. The second argument - * adds secondary details to the primary message; - * the message will typically be displayed in the - * form "message: subMessage". - * - * @param message - * Human-readable message text. - * @param subMessage - * Human-readable additional information. - * @param offset - * Byte offset associated with the message. - */ - protected Message(final String message, final String subMessage, - final long offset) { - this(JhoveMessages.getMessageInstance(JhoveMessages.NO_ID, message), - subMessage, offset); - } - - /** - * Creates a Message with an identifier. - * This constructor cannot be invoked directly, - * since Message is abstract. The second argument - * adds secondary details to the primary message; - * the message will typically be displayed in the - * form "message: subMessage". - * - * @param message - * The message text and its identifier. - * @param subMessage - * Human-readable additional information. - * @param offset - * Byte offset associated with the message. - */ - protected Message(final JhoveMessage message, String subMessage, - long offset) { - super(); - this.message = message; - this._subMessage = (subMessage.isEmpty()) ? null : subMessage; - this._offset = offset; - } - - /****************************************************************** - * PUBLIC INSTANCE METHODS. - * - * Accessor methods. - ******************************************************************/ - - /** - * Returns the message text. - */ - public String getMessage() { - return this.message.getMessage(); - } - - /** - * Returns the submessage text. - */ - public String getSubMessage() { - return this._subMessage; - } - - /** - * Returns the offset to which this message is related. - */ - public long getOffset() { - return this._offset; - } - - /** - * Returns the message's identifier. - */ - public String getId() { - return this.message.getId(); - } - - public JhoveMessage getJhoveMessage() { - return this.message; - } - - @SuppressWarnings("static-method") - public String getPrefix() { - return ""; - } + /** + * **************************************************************** PUBLIC CLASS FIELDS. + * **************************************************************** + */ + + /** Value indicating a null offset. */ + public static final long NULL = -1; + + /** + * **************************************************************** PRIVATE INSTANCE FIELDS. + * **************************************************************** + */ + protected final JhoveMessage message; + + /** Additional information. */ + protected final String _subMessage; + + /** Byte offset to which message applies. */ + protected final long _offset; + + /** + * **************************************************************** CLASS CONSTRUCTOR. + * **************************************************************** + */ + + /** + * Creates a Message with an unknown identifier for backward compatibility. This constructor + * cannot be invoked directly, since Message is abstract. + * + * @param message Human-readable message text. + */ + protected Message(final String message) { + this(JhoveMessages.getMessageInstance(JhoveMessages.NO_ID, message)); + } + + /** + * Creates a Message with an identifier. This constructor cannot be invoked directly, since + * Message is abstract. + * + * @param message The message text and its identifier. + */ + protected Message(final JhoveMessage message) { + this(message, message.getSubMessage(), NULL); + } + + /** + * Creates a Message. This constructor cannot be invoked directly, since Message is abstract. The + * second argument adds secondary details to the primary message; the message will typically be + * displayed in the form "message: subMessage". + * + * @param message Human-readable message text. + * @param subMessage Human-readable additional information. + */ + protected Message(final String message, final String subMessage) { + this(JhoveMessages.getMessageInstance(JhoveMessages.NO_ID, message), subMessage); + } + + /** + * Creates a Message with an identifier. This constructor cannot be invoked directly, since + * Message is abstract. The second argument adds secondary details to the primary message; the + * message will typically be displayed in the form "message: subMessage". + * + * @param message The message text and its identifier. + * @param subMessage Human-readable additional information. + */ + protected Message(final JhoveMessage message, final String subMessage) { + this(message, subMessage, NULL); + } + + /** + * Creates a Message. This constructor cannot be invoked directly, since Message is abstract. The + * second argument adds secondary details to the primary message; the message will typically be + * displayed in the form "message: subMessage". + * + * @param message Human-readable message text. + * @param offset Byte offset associated with the message. + */ + protected Message(final String message, final long offset) { + this(JhoveMessages.getMessageInstance(JhoveMessages.NO_ID, message), offset); + } + + /** + * Creates a Message with an identifier. This constructor cannot be invoked directly, since + * Message is abstract. The second argument adds secondary details to the primary message; the + * message will typically be displayed in the form "message: subMessage". + * + * @param message The message text and its identifier. + * @param offset Byte offset associated with the message. + */ + protected Message(final JhoveMessage message, final long offset) { + this(message, message.getSubMessage(), offset); + } + + /** + * Creates a Message. This constructor cannot be invoked directly, since Message is abstract. The + * second argument adds secondary details to the primary message; the message will typically be + * displayed in the form "message: subMessage". + * + * @param message Human-readable message text. + * @param subMessage Human-readable additional information. + * @param offset Byte offset associated with the message. + */ + protected Message(final String message, final String subMessage, final long offset) { + this(JhoveMessages.getMessageInstance(JhoveMessages.NO_ID, message), subMessage, offset); + } + + /** + * Creates a Message with an identifier. This constructor cannot be invoked directly, since + * Message is abstract. The second argument adds secondary details to the primary message; the + * message will typically be displayed in the form "message: subMessage". + * + * @param message The message text and its identifier. + * @param subMessage Human-readable additional information. + * @param offset Byte offset associated with the message. + */ + protected Message(final JhoveMessage message, String subMessage, long offset) { + super(); + this.message = message; + this._subMessage = (subMessage.isEmpty()) ? null : subMessage; + this._offset = offset; + } + + /** + * **************************************************************** PUBLIC INSTANCE METHODS. + * + *

Accessor methods. **************************************************************** + */ + + /** Returns the message text. */ + public String getMessage() { + return this.message.getMessage(); + } + + /** Returns the submessage text. */ + public String getSubMessage() { + return this._subMessage; + } + + /** Returns the offset to which this message is related. */ + public long getOffset() { + return this._offset; + } + + /** Returns the message's identifier. */ + public String getId() { + return this.message.getId(); + } + + public JhoveMessage getJhoveMessage() { + return this.message; + } + + @SuppressWarnings("static-method") + public String getPrefix() { + return ""; + } } diff --git a/jhove-core/src/main/java/edu/harvard/hul/ois/jhove/Module.java b/jhove-core/src/main/java/edu/harvard/hul/ois/jhove/Module.java index c7ef75580..ed6efac50 100644 --- a/jhove-core/src/main/java/edu/harvard/hul/ois/jhove/Module.java +++ b/jhove-core/src/main/java/edu/harvard/hul/ois/jhove/Module.java @@ -1,8 +1,8 @@ -/********************************************************************** - * Jhove - JSTOR/Harvard Object Validation Environment - * Copyright 2003-4 by JSTOR and the President and Fellows of Harvard College - **********************************************************************/ - +/** + * ******************************************************************** Jhove - JSTOR/Harvard Object + * Validation Environment Copyright 2003-4 by JSTOR and the President and Fellows of Harvard College + * ******************************************************************** + */ package edu.harvard.hul.ois.jhove; import java.io.*; @@ -10,255 +10,172 @@ /** * Public interface for Jhove format-specific plug-in modules. - * - * All format modules must implement the Module interface; - * usually the best way to do this will be to subclass ModuleBase. - * Modules must be declared in the configuration file and present - * in the Classpath to be recognized by Jhove. * - * @see ModuleBase + *

All format modules must implement the Module interface; usually the best way to do this will + * be to subclass ModuleBase. Modules must be declared in the configuration file and present in the + * Classpath to be recognized by Jhove. + * + * @see ModuleBase */ -public interface Module -{ - public static final int - MAXIMUM_VERBOSITY = 1, - MINIMUM_VERBOSITY = 2; - - /** - * Per-instantiation initialization. - * - * @param init Initialization parameter. This is typically obtained - * from the configuration file. - */ - public void init (String init) - throws Exception; - - /** - * Sets list of default parameters. - * - * @param params A List whose elements are Strings. - * May be empty. - */ - public void setDefaultParams (List params); - - - /** - * Applies the default parameters. - */ - public void applyDefaultParams () - throws Exception; - - /** Reset parameter settings. - * Returns to a default state without any parameters. - */ - public void resetParams () - throws Exception; - - /** - * Per-action initialization. - * May be called multiple times. - * - * @param param Initialization parameter. - */ - public void param (String param) - throws Exception; - - /** - * Pass the associated App object to this Module. - * The App makes various services available. - */ - public void setApp (App app); - - /** - * Pass the JHOVE engine object to this Module. - */ - public void setBase (JhoveBase je); - - /** - * Set the degree of verbosity desired from the module. - * - * @param verbosity The requested verbosity value. - */ - public void setVerbosity (int verbosity); - - /** - * Return the name of this Module. - */ - public String getName (); - - /** - * Return the release identifier - */ - public String getRelease (); - - /** - * Return the last modification date of the Module, as a - * Java Date object - */ - public Date getDate (); - - /** - * Return the vendor information - */ - public Agent getVendor (); - - /** - * Return the copyright information string - */ - public String getRights (); - - /** - * Return the Module note - */ - public String getNote (); - - /** - * Return the array of format names supported by this Module - */ - public String [] getFormat (); - - /** - * Return the array of MIME type strings for formats supported - * by this Module - */ - public String [] getMimeType (); - - /** - * Return details as to the specific format versions or - * variants that are supported by this Module - */ - public String getCoverage (); - - /** - * Return the List of Signatures recognized by this Module - */ - public List getSignature (); - - /** - * Returns a list of Document objects (one for each - * specification document of the format). The specification - * list is generated by the Module, and specifications cannot - * be added by callers. - * - * @see Document - */ - public List getSpecification (); - - /** - * Return the string describing well-formedness criteria - */ - public String getWellFormedNote (); - - /** - * Returns the RepInfo note - */ - public String getRepInfoNote (); - - /** - * Returns the string describing validity criteria - */ - public String getValidityNote (); - - /** - * Returns the random access flag (true if the module operates - * on random access files, false if it operates on streams) - */ - public boolean isRandomAccess (); - - /** - * Returns true if the module supports a given - * named feature, and false if the feature is - * unsupported or unknown. - */ - public boolean hasFeature (String feature); - - /** - * Returns the full list of features. - */ - public List getFeatures (); - - /** - * Returns the list of default parameters. - */ - public List getDefaultParams (); - - - - /** - * Parse the content of a stream digital object and store the - * results in RepInfo. - * A given Module will normally implement only one of the two - * parse methods, leaving the other as a stub. - * - * @param stream An InputStream, positioned at its beginning, - * which is generated from the object to be parsed. - * If multiple calls to parse are made - * on the basis of a nonzero value being returned, - * a new InputStream must be provided each time. - * - * @param info A fresh (on the first call) RepInfo object - * which will be modified - * to reflect the results of the parsing - * If multiple calls to parse are made - * on the basis of a nonzero value being returned, - * the same RepInfo object should be passed with each - * call. - * - * @param parseIndex Must be 0 in first call to parse. If - * parse returns a nonzero value, it must be - * called again with parseIndex - * equal to that return value. - */ - public int parse (InputStream stream, RepInfo info, int parseIndex) - throws IOException; - - /** - * Parse the content of a random access digital object and store the - * results in RepInfo. - * A given Module will normally implement only one of the two - * parse methods, leaving the other as a stub. - * - * @param file A RandomAccessFile, positioned at its beginning, - * which is generated from the object to be parsed - * @param info A fresh RepInfo object which will be modified - * to reflect the results of the parsing - */ - public void parse (RandomAccessFile file, RepInfo info) - throws IOException; - - /** - * Check if the digital object conforms to this Module's - * internal signature information. - * - * @param file A File object for the object being parsed - * @param stream An InputStream, positioned at its beginning, - * which is generated from the object to be parsed - * @param info A fresh RepInfo object which will be modified - * to reflect the results of the test - */ - public void checkSignatures (File file, - InputStream stream, - RepInfo info) - throws IOException; - - /** - * Check if the digital object conforms to this Module's - * internal signature information. - * - * @param file A File object for the object being parsed - * @param raf A RandomAccessFile, positioned at its beginning, - * which is generated from the object to be parsed - * @param info A fresh RepInfo object which will be modified - * to reflect the results of the test - */ - public void checkSignatures (File file, - RandomAccessFile raf, - RepInfo info) - throws IOException; - - /** - * Generates information about this Module. - * The format of the output depends on the OutputHandler. - */ - public void show (OutputHandler handler); +public interface Module { + public static final int MAXIMUM_VERBOSITY = 1, MINIMUM_VERBOSITY = 2; + + /** + * Per-instantiation initialization. + * + * @param init Initialization parameter. This is typically obtained from the configuration file. + */ + public void init(String init) throws Exception; + + /** + * Sets list of default parameters. + * + * @param params A List whose elements are Strings. May be empty. + */ + public void setDefaultParams(List params); + + /** Applies the default parameters. */ + public void applyDefaultParams() throws Exception; + + /** Reset parameter settings. Returns to a default state without any parameters. */ + public void resetParams() throws Exception; + + /** + * Per-action initialization. May be called multiple times. + * + * @param param Initialization parameter. + */ + public void param(String param) throws Exception; + + /** Pass the associated App object to this Module. The App makes various services available. */ + public void setApp(App app); + + /** Pass the JHOVE engine object to this Module. */ + public void setBase(JhoveBase je); + + /** + * Set the degree of verbosity desired from the module. + * + * @param verbosity The requested verbosity value. + */ + public void setVerbosity(int verbosity); + + /** Return the name of this Module. */ + public String getName(); + + /** Return the release identifier */ + public String getRelease(); + + /** Return the last modification date of the Module, as a Java Date object */ + public Date getDate(); + + /** Return the vendor information */ + public Agent getVendor(); + + /** Return the copyright information string */ + public String getRights(); + + /** Return the Module note */ + public String getNote(); + + /** Return the array of format names supported by this Module */ + public String[] getFormat(); + + /** Return the array of MIME type strings for formats supported by this Module */ + public String[] getMimeType(); + + /** + * Return details as to the specific format versions or variants that are supported by this Module + */ + public String getCoverage(); + + /** Return the List of Signatures recognized by this Module */ + public List getSignature(); + + /** + * Returns a list of Document objects (one for each specification document of the + * format). The specification list is generated by the Module, and specifications cannot be added + * by callers. + * + * @see Document + */ + public List getSpecification(); + + /** Return the string describing well-formedness criteria */ + public String getWellFormedNote(); + + /** Returns the RepInfo note */ + public String getRepInfoNote(); + + /** Returns the string describing validity criteria */ + public String getValidityNote(); + + /** + * Returns the random access flag (true if the module operates on random access files, false if it + * operates on streams) + */ + public boolean isRandomAccess(); + + /** + * Returns true if the module supports a given named feature, and false + * if the feature is unsupported or unknown. + */ + public boolean hasFeature(String feature); + + /** Returns the full list of features. */ + public List getFeatures(); + + /** Returns the list of default parameters. */ + public List getDefaultParams(); + + /** + * Parse the content of a stream digital object and store the results in RepInfo. A given Module + * will normally implement only one of the two parse methods, leaving the other as a stub. + * + * @param stream An InputStream, positioned at its beginning, which is generated from the object + * to be parsed. If multiple calls to parse are made on the basis of a nonzero + * value being returned, a new InputStream must be provided each time. + * @param info A fresh (on the first call) RepInfo object which will be modified to reflect the + * results of the parsing If multiple calls to parse are made on the basis of a + * nonzero value being returned, the same RepInfo object should be passed with each call. + * @param parseIndex Must be 0 in first call to parse. If parse returns + * a nonzero value, it must be called again with parseIndex equal to that return + * value. + */ + public int parse(InputStream stream, RepInfo info, int parseIndex) throws IOException; + + /** + * Parse the content of a random access digital object and store the results in RepInfo. A given + * Module will normally implement only one of the two parse methods, leaving the other as a stub. + * + * @param file A RandomAccessFile, positioned at its beginning, which is generated from the object + * to be parsed + * @param info A fresh RepInfo object which will be modified to reflect the results of the parsing + */ + public void parse(RandomAccessFile file, RepInfo info) throws IOException; + + /** + * Check if the digital object conforms to this Module's internal signature information. + * + * @param file A File object for the object being parsed + * @param stream An InputStream, positioned at its beginning, which is generated from the object + * to be parsed + * @param info A fresh RepInfo object which will be modified to reflect the results of the test + */ + public void checkSignatures(File file, InputStream stream, RepInfo info) throws IOException; + + /** + * Check if the digital object conforms to this Module's internal signature information. + * + * @param file A File object for the object being parsed + * @param raf A RandomAccessFile, positioned at its beginning, which is generated from the object + * to be parsed + * @param info A fresh RepInfo object which will be modified to reflect the results of the test + */ + public void checkSignatures(File file, RandomAccessFile raf, RepInfo info) throws IOException; + + /** + * Generates information about this Module. The format of the output depends on the OutputHandler. + */ + public void show(OutputHandler handler); } diff --git a/jhove-core/src/main/java/edu/harvard/hul/ois/jhove/ModuleBase.java b/jhove-core/src/main/java/edu/harvard/hul/ois/jhove/ModuleBase.java index a9268d0e2..9a68a0e33 100644 --- a/jhove-core/src/main/java/edu/harvard/hul/ois/jhove/ModuleBase.java +++ b/jhove-core/src/main/java/edu/harvard/hul/ois/jhove/ModuleBase.java @@ -1,1478 +1,1286 @@ -/********************************************************************** - * Jhove - JSTOR/Harvard Object Validation Environment - * Copyright 2003-4 by JSTOR and the President and Fellows of Harvard College - **********************************************************************/ - +/** + * ******************************************************************** Jhove - JSTOR/Harvard Object + * Validation Environment Copyright 2003-4 by JSTOR and the President and Fellows of Harvard College + * ******************************************************************** + */ package edu.harvard.hul.ois.jhove; import java.io.*; import java.security.*; import java.util.*; -import java.util.zip.*; - import java.util.logging.*; +import java.util.zip.*; /** - * This class is an abstract implementation of the Module interface. - * It contains all the methods required for a Module, but doesn't - * do anything by itself. A subclass should provide a functional - * implementation of {@code parse(InputStream, RepInfo, int)} - * if it is not random access, or {@code parse(RandomAccessFile, RepInfo)} - * if it is random access. + * This class is an abstract implementation of the Module interface. It contains all the methods + * required for a Module, but doesn't do anything by itself. A subclass should provide a functional + * implementation of {@code parse(InputStream, RepInfo, int)} if it is not random access, or {@code + * parse(RandomAccessFile, RepInfo)} if it is random access. */ public abstract class ModuleBase implements Module { - /****************************************************************** - * PROTECTED INSTANCE FIELDS. - ******************************************************************/ - - /** The application object */ - protected App _app; - /** Coverage information */ - protected String _coverage; - /** Module last modification date */ - protected Date _date; - /** Formats recognized by this Module */ - protected String[] _format; - /** Initialization value. */ - protected String _init; - /** List of default parameters. */ - protected final List _defaultParams = new ArrayList<>(); - /** JHOVE engine. */ - protected JhoveBase _je; - /** MIME types supported by this Module */ - protected String[] _mimeType; - /** Module name */ - protected String _name; - /** Module note */ - protected String _note; - /** Module-specific parameter. */ - protected String _param; - /** Module release description */ - protected String _release; - /** RepInfo note */ - protected String _repInfoNote; - /** Copyright notice */ - protected String _rights; - /** Module Signature list */ - protected List _signature; - /** Module specification document list */ - protected List _specification; - /** Module vendor */ - protected Agent _vendor; - /** Well-formedness criteria */ - protected String _wellFormedNote; - /** Validity criteria */ - protected String _validityNote; - /** Random access flag */ - protected boolean _isRandomAccess; - /** Byte count of content object */ - protected long _nByte; - /** CRC32 calculated on content object */ - protected CRC32 _crc32; - /** MD5 digest calculated on content object */ - protected MessageDigest _md5; - /** SHA-1 digest calculated on content object */ - protected MessageDigest _sha1; - /** SHA-256 digest calculated on content object */ - protected MessageDigest _sha256; - /** Flag indicating valid checksum information set */ - protected boolean _checksumFinished; - /** Indicator of how much data to report */ - protected int _verbosity; - /** Flag to indicate read routines should count the stream */ - protected boolean _countStream; - /** The dominant "endianness" of the Module. */ - protected boolean _bigEndian; - /** The list of supported features. */ - protected List _features; - /** Logger for a module class. */ - protected Logger _logger; - /* Checksummer object */ - protected Checksummer _ckSummer; - - /* Input stream wrapper which handles checksums */ - protected ChecksumInputStream _cstream; - - /* Data input stream wrapped around _cstream */ - protected DataInputStream _dstream; - - /****************************************************************** - * CLASS CONSTRUCTOR. - ******************************************************************/ - - /** - * Constructors of all subclasses of ModuleBase should call - * this as a {@code super} constructor. - * - * @param name - * Name of the module - * @param release - * Release identifier - * @param date - * Last modification date of the module code, - * in the form of an array of three numbers. - * {@code date[0]} is the year, - * {@code date[1]} the month, and - * {@code date[2]} the day. - * @param format - * Array of format names supported by the module - * @param coverage - * Details as to the specific format versions or - * variants that are supported by the module - * @param mimeType - * Array of MIME type strings for formats - * supported by the module - * @param wellFormedNote - * Brief explanation of what constitutes - * well-formed content - * @param validityNote - * Brief explanation of what constitutes - * valid content - * @param repInfoNote - * Note pertaining to RepInfo (may be null) - * @param note - * Additional information about the module - * (may be null) - * @param rights - * Copyright notice for the module - * @param isRandomAccess - * {@code true} if the module treats content as - * random-access data, {@code false} if it treats content - * as stream data - */ - protected ModuleBase(String name, String release, int[] date, - String[] format, String coverage, String[] mimeType, - String wellFormedNote, String validityNote, String repInfoNote, - String note, String rights, boolean isRandomAccess) { - // Though we're actually in the jhove package, all the related - // action logically belongs in the module package, so we name - // this logger accordingly. - _logger = Logger.getLogger("edu.harvard.hul.ois.jhove.module"); - _logger.info("Initializing " + name); - _name = name; - _release = release; - - Calendar calendar = new GregorianCalendar(); - calendar.set(date[0], date[1] - 1, date[2]); - _date = calendar.getTime(); - - _format = format; - _coverage = coverage; - _mimeType = mimeType; - _signature = new ArrayList<>(); - _specification = new ArrayList<>(); - _wellFormedNote = wellFormedNote; - _repInfoNote = repInfoNote; - _validityNote = validityNote; - _note = note; - _rights = rights; - _isRandomAccess = isRandomAccess; - - _verbosity = MINIMUM_VERBOSITY; - initFeatures(); - } - - /****************************************************************** - * PUBLIC INSTANCE METHODS. - * - * Initialization methods. - ******************************************************************/ - - /** - * Initializes the feature list. - * This method puts the following features in the list: - *

    - *
  • edu.harvard.hul.ois.canValidate - *
  • edu.harvard.hul.ois.canIdentify - *
- */ - public void initFeatures() { - _features = new ArrayList<>(2); - _features.add("edu.harvard.hul.ois.jhove.canValidate"); - _features.add("edu.harvard.hul.ois.jhove.canCharacterize"); - } - - /** - * Per-instantiation initialization. - * The default method does nothing but save its parameter. - */ - @Override - public void init(String init) { - _init = init; - } - - /** - * Set a a List of default parameters for the module. - * - * @param params - * A List whose elements are Strings. - * May be empty. - */ - @Override - public void setDefaultParams(List params) { - _defaultParams.clear(); - _defaultParams.addAll(params); - } - - /** - * Applies the default parameters. - * Calling this clears any prior parameters. - */ - @SuppressWarnings("unused") - @Override - public void applyDefaultParams() throws Exception { - resetParams(); - for (String parm : _defaultParams) { - param(parm); - } - } - - /** - * Reset parameter settings. - * Returns to a default state without any parameters. - * The default method clears the saved parameter. - */ - @Override - public void resetParams() { - _param = null; - } - - /** - * Per-action initialization. May be called multiple times. - * The default method does nothing but save its parameter. - */ - @Override - public void param(String param) { - _param = param; - } - - /****************************************************************** - * Accessor methods. - ******************************************************************/ - - /** - * Returns the App object. - */ - public App getApp() { - return _app; - } - - /** - * Returns the JHOVE engine object. - */ - public JhoveBase getBase() { - return _je; - } - - /** - * Returns the value of _nByte. - * Meaningful only for modules that use a counted InputStream. - */ - public long getNByte() { - return _nByte; - } - - /** - * Returns true if the dominant "endianness" of the - * module, or the current file being processed, - * is big-endian, otherwise false. This does not guarantee - * that all numbers in the module follow the dominant endianness, - * particularly as formats sometimes incorporate data stored in - * a previously defined format. For some formats, e.g., TIFF, the - * endianness depends on the file being processed. - * - * Every module must initialize the value of _bigEndian for this - * function, or else assign its value when parsing a file, - * to return a meaningful result. For some modules (e.g., - * ASCII, endianness has no meaning. - */ - public boolean isBigEndian() { - return _bigEndian; - } - - /** - * Return details as to the specific format versions or - * variants that are supported by this module - */ - @Override - public final String getCoverage() { - return _coverage; - } - - /** - * Return the last modification date of this Module, as a - * Java Date object - */ - @Override - public final Date getDate() { - return _date; - } - - /** - * Return the array of format names supported by this Module - */ - @Override - public final String[] getFormat() { - return _format; - } - - /** - * Return the array of MIME type strings for formats supported - * by this Module - */ - @Override - public final String[] getMimeType() { - return _mimeType; - } - - /** - * Return the module name - */ - @Override - public final String getName() { - return _name; - } - - /** - * Return the module note - */ - @Override - public final String getNote() { - return _note; - } - - /** - * Return the release identifier - */ - @Override - public final String getRelease() { - return _release; - } - - /** - * Return the RepInfo note - */ - @Override - public final String getRepInfoNote() { - return _repInfoNote; - } - - /** - * Return the copyright information string - */ - @Override - public final String getRights() { - return _rights; - } - - /** - * Return the List of Signatures recognized by this Module - */ - @Override - public final List getSignature() { - return _signature; - } - - /** - * Returns a list of Document objects (one for each - * specification document of the format). The specification - * list is generated by the Module, and specifications cannot - * be added by callers. - * - * @see Document - */ - @Override - public final List getSpecification() { - return _specification; - } - - /** - * Return the vendor information - */ - @Override - public final Agent getVendor() { - return _vendor; - } - - /** - * Return the string describing well-formedness criteria - */ - @Override - public final String getWellFormedNote() { - return _wellFormedNote; - } - - /** - * Return the string describing validity criteria - */ - @Override - public final String getValidityNote() { - return _validityNote; - } - - /** - * Return the random access flag (true if the module operates - * on random access files, false if it operates on streams) - */ - @Override - public final boolean isRandomAccess() { - return _isRandomAccess; - } - - /** - * Returns true if the module supports a given - * named feature, and false if the feature is - * unsupported or unknown. Feature names are case sensitive. - * - * It is recommended that features be named using package - * nomenclature. The following features are, by default, - * supported by the modules developed by OIS: - * - *
    - *
  • edu.harvard.hul.ois.canValidate - *
  • edu.harvard.hul.ois.canIdentify - *
- */ - @Override - public boolean hasFeature(String feature) { - if (_features == null) { - // dubious, but check it - return false; - } - Iterator iter = _features.iterator(); - while (iter.hasNext()) { - String f = iter.next(); - if (f.equals(feature)) { - return true; - } - } - return false; - } - - /** - * Returns the full list of features. - */ - @Override - public List getFeatures() { - return _features; - } - - /** - * Returns the list of default parameters. - */ - @Override - public List getDefaultParams() { - return Collections.unmodifiableList(_defaultParams); - } - - /****************************************************************** - * Mutator methods. - ******************************************************************/ - - /** - * Pass the associated App object to this Module. - * The App makes various services available. - */ - @Override - public final void setApp(App app) { - _app = app; - } - - /** - * Pass the JHOVE engine object to this Module. - */ - @Override - public final void setBase(JhoveBase je) { - _je = je; - } - - /** - * Set the value of the validityNote property, which - * briefly explains the validity criteria of this Module. - */ - public final void setValidityNote(String validityNote) { - _validityNote = validityNote; - } - - /** - * Set the value of the CRC32 calculated for the content object. - * The checksum-like functions can be set by the caller. - * Setting any of these creates the assumption that the - * calculation is already done, and sets the checksumFinished - * flag to inhibit recalculation. - */ - public final void setCRC32(CRC32 crc32) { - _crc32 = crc32; - _checksumFinished = true; - } - - /** - * Set the degree of verbosity desired from the module. The setting - * of param can override the verbosity setting. - * It does not affect whether raw data are reported or not, only - * which data are reported. - * - * - * @param verbosity - * The requested verbosity value. Recognized - * values are Module.MINIMUM_VERBOSITY and - * Module.MAXIMUM_VERBOSITY. - * The interpretation of the value depends on the module, and - * the module may choose not to use this setting. However, - * modules should treat MAXIMUM_VERBOSITY as a request for - * all the data available from the module. - */ - @Override - public void setVerbosity(int verbosity) { - _verbosity = verbosity; - } - - /** - * Sets the byte count for the content object, and sets - * the checksumFinished flag. - */ - public final void setNByte(long nByte) { - _nByte = nByte; - _checksumFinished = true; - } - - /** - * Sets the MD5 calculated digest for the content object, and sets - * the checksumFinished flag. - */ - public final void setMD5(MessageDigest md5) { - _md5 = md5; - _checksumFinished = true; - } - - /** - * Sets the SHA-1 calculated digest for the content object, and sets - * the checksumFinished flag. - */ - public final void setSHA1(MessageDigest sha1) { - _sha1 = sha1; - _checksumFinished = true; - } - - - /** - * Sets the SHA-256 calculated digest for the content object, and sets - * the checksumFinished flag. - */ - public final void setSHA256(MessageDigest sha256) { - _sha256 = sha256; - _checksumFinished = true; - } - - /****************************************************************** - * Parsing methods. - ******************************************************************/ - - /** - * Parse the content of a stream digital object and store the - * results in RepInfo. - * A given Module will normally override only one of the two - * parse methods; the default method does nothing. - * - * @param stream - * An InputStream, positioned at its beginning, - * which is generated from the object to be parsed. - * If multiple calls to parse are made - * on the basis of a nonzero value being returned, - * a new InputStream must be provided each time. - * - * @param info - * A fresh (on the first call) RepInfo object - * which will be modified - * to reflect the results of the parsing - * If multiple calls to parse are made - * on the basis of a nonzero value being returned, - * the same RepInfo object should be passed with each - * call. - * - * @param parseIndex - * Must be 0 in first call to parse. If - * parse returns a nonzero value, it must be - * called again with parseIndex - * equal to that return value. - */ - @SuppressWarnings("unused") - @Override - public int parse(InputStream stream, RepInfo info, int parseIndex) - throws IOException { - return 0; - } - - /** - * Parse the content of a random access digital object and store the - * results in RepInfo. - * A given Module will normally override only one of the two - * parse methods; the default method does nothing. - * - * @param file - * A RandomAccessFile, positioned at its beginning, - * which is generated from the object to be parsed - * @param info - * A fresh RepInfo object which will be modified - * to reflect the results of the parsing - */ - @SuppressWarnings("unused") - @Override - public void parse(RandomAccessFile file, RepInfo info) throws IOException { - } - - /** - * Check if the digital object conforms to this Module's - * internal signature information. - * This function checks the file against the list of predefined - * signatures for the module. If there are no predefined - * signatures, it calls parse with the arguments passed to it. - * Override this for modules that check digital signatures in - * some other way. Any module for which the signature may be located - * other than at the beginning of the file must override. - * - * @param file - * A File object for the object being parsed - * @param stream - * An InputStream, positioned at its beginning, - * which is generated from the object to be parsed - * @param info - * A fresh RepInfo object which will be modified - * to reflect the results of the test - */ - @Override - public void checkSignatures(File file, InputStream stream, RepInfo info) - throws IOException { - info.setFormat(_format[0]); - info.setMimeType(_mimeType[0]); - info.setModule(this); - int sigsChecked = 0; - if (_signature.size() > 0) { - /* - * Get each of the internal sigs defined for the module - * and test it. All sigs must be present. If there are - * no internal signatures, this test is meaningless. - */ - byte[] sigBuf = new byte[1024]; - stream.read(sigBuf); - stream.close(); - ListIterator iter = _signature.listIterator(); - while (iter.hasNext()) { - Signature sig = (iter.next()); - if (sig instanceof InternalSignature) { - InternalSignature isig = (InternalSignature) sig; - int[] sigValue = isig.getValue(); - int offset = isig.getOffset(); - boolean match = true; - for (int i = 0; i < sigValue.length; i++) { - if (sigBuf[offset + i] != sigValue[i]) { - match = false; - break; - } - } - if (!match && isig.getUse() - .equals(SignatureUseType.MANDATORY)) { - info.setWellFormed(false); - return; - } - if (match) { - // Only count optional signatures if they match. - ++sigsChecked; - } - } - } - } - if (sigsChecked == 0) { - // No internal sigs defined, parse the file. - int parseIndex = parse(stream, info, 0); - while (parseIndex != 0) { - stream.close(); - if (file != null) { - stream = new FileInputStream(file); - parseIndex = parse(stream, info, parseIndex); - } else { - parseIndex = 0; - } - } - } else if (info.getWellFormed() == RepInfo.TRUE) { - info.setSigMatch(_name); - } - } - - /** - * Check if the digital object conforms to this Module's - * internal signature information. - * - * @param file - * A File object representing the object to be - * parsed - * - * @param raf - * A RandomAccessFile, positioned at its beginning, - * which is generated from the object to be parsed - * - * @param info - * A fresh RepInfo object which will be modified - * to reflect the results of the test - */ - @SuppressWarnings("unused") - @Override - public void checkSignatures(File file, RandomAccessFile raf, RepInfo info) - throws IOException { - info.setFormat(_format[0]); - info.setMimeType(_mimeType[0]); - info.setModule(this); - int sigsChecked = 0; - /* - * Get each of the internal sigs defined for the module - * and test it. - */ - ListIterator iter = _signature.listIterator(); - try { - while (iter.hasNext()) { - Signature sig = (iter.next()); - if (sig instanceof InternalSignature) { - InternalSignature isig = (InternalSignature) sig; - /* What about non-fixed offset? */ - raf.seek(isig.getOffset()); - int[] sigValue = isig.getValue(); - boolean match = true; - for (int i = 0; i < sigValue.length; i++) { - if (readUnsignedByte(raf) != sigValue[i]) { - match = false; - break; - } - } - if (!match && isig.getUse() - .equals(SignatureUseType.MANDATORY)) { - info.setWellFormed(false); - break; - } - if (match) { - // Only count optional signatures if they match. - ++sigsChecked; - } - } - } - } catch (Exception e) { - // We may get here on a short file. - info.setWellFormed(false); - return; - } - // Must match at least one signature. - if (sigsChecked == 0) { - info.setWellFormed(false); - } else if (info.getWellFormed() == RepInfo.TRUE) { - info.setSigMatch(_name); - } - } - - /** - * Initializes the state of the module for parsing. This should be - * called early in each module's parse() method. If a module - * overrides it to provide additional functionality, the module's - * initParse() should call super.initParse(). - */ - protected void initParse() { - _logger.info(_name + " called initParse"); - _checksumFinished = false; - _nByte = 0; - _crc32 = new CRC32(); - try { - _md5 = MessageDigest.getInstance("MD5"); - _sha1 = MessageDigest.getInstance("SHA-1"); - _sha256 = MessageDigest.getInstance("SHA-256"); - } catch (NoSuchAlgorithmException e) { - throw new IllegalStateException("Missing checksum algorithm.", e); - } - } - - protected void initInfo(final RepInfo info) { - info.setModule(this); - info.setFormat(this._format[0]); - info.setMimeType(this._mimeType[0]); - } - - /** - * Calculates the checksums for a module that uses a - * random access file. - */ - protected void calcRAChecksum(Checksummer ckSummer, RandomAccessFile raf) - throws IOException { - if (ckSummer == null) { - return; - } - - raf.seek(0); - byte[] buffer = new byte[_je.getBufferSize()]; - int n = -1; - try { - while ((n = raf.read(buffer)) != -1) { - if (n > 0) { - ckSummer.update(buffer, 0, n); - } - } - } catch (Exception e) { - } - } - - /** - * Set the checksum values. - * - * @param ckSummer - * Checksummer object - * @param info - * RepInfo object - */ - protected static void setChecksums(Checksummer ckSummer, RepInfo info) { - if (ckSummer != null) { - info.setChecksum( - new Checksum(ckSummer.getCRC32(), ChecksumType.CRC32)); - String value = ckSummer.getMD5(); - if (value != null) { - info.setChecksum(new Checksum(value, ChecksumType.MD5)); - } - if ((value = ckSummer.getSHA1()) != null) { - info.setChecksum(new Checksum(value, ChecksumType.SHA1)); - } - if ((value = ckSummer.getSHA256()) != null) { - info.setChecksum(new Checksum(value, ChecksumType.SHA256)); - } - } - } - - /** - * Generates information about this Module. - * The format of the output depends on the OutputHandler. - */ - @Override - public void show(OutputHandler handler) { - handler.show(this); - } - - /****************************************************************** - * PRIVATE INSTANCE METHODS. - ******************************************************************/ - - /** - * Returns the hex string representation of the CRC32 result. - */ - protected String getCRC32() { - return Long.toHexString(_crc32.getValue()); - } - - /****************************************************************** - * PUBLIC INSTANCE METHODS. - ******************************************************************/ - - /** - * Returns a Property representing an integer value. - * If raw output is specified for the module, returns - * an INTEGER property, and labels and - * index are unused. Otherwise, - * returns a STRING property, with the - * string being the element of labels - * whose index is the index of - * value in index. - */ - public Property addIntegerProperty(String name, int value, String[] labels, - int[] index) { - boolean rawOutput = _je.getShowRawFlag(); - Property prop = null; - if (!rawOutput) { - int n = -1; - for (int i = 0; i < index.length; i++) { - if (value == index[i]) { - n = i; - break; - } - } - if (n > -1) { - prop = new Property(name, PropertyType.STRING, labels[n]); - } - } - if (prop == null) { - prop = new Property(name, PropertyType.INTEGER, new Integer(value)); - } - - return prop; - } - - /** - * Returns a Property representing an integer value. - * If raw output is specified for the module, returns - * an INTEGER property, and labels and - * index are unused. Otherwise, - * returns a STRING property, with the - * string being the element of labels - * whose index is value. - */ - public Property addIntegerProperty(String name, int value, - String[] labels) { - if (!_je.getShowRawFlag()) { - try { - return new Property(name, PropertyType.STRING, labels[value]); - } catch (Exception e) { - // fall through - } - } - return new Property(name, PropertyType.INTEGER, new Integer(value)); - } - - /** - * Reads an unsigned byte from a DataInputStream. - * - * @param stream - * Stream to read - */ - public static int readUnsignedByte(DataInputStream stream) - throws IOException { - return readUnsignedByte(stream, null); - } - - /** - * Reads an unsigned byte from a DataInputStream. - * - * @param stream - * Stream to read - * @param counted - * If non-null, module for which value of _nByte - * shall be incremented appropriately - */ - public static int readUnsignedByte(DataInputStream stream, - ModuleBase counted) throws IOException { - int val = stream.readUnsignedByte(); - if (counted != null) { - counted._nByte++; - } - return val; - } - - /** - * Reads an unsigned byte from a RandomAccessFile. - */ - public static int readUnsignedByte(RandomAccessFile file) - throws IOException { - return file.readUnsignedByte(); - } - - /** - * Reads into a byte buffer from a DataInputStream. - * - * @param stream - * Stream to read from - * @param buf - * Byte buffer to fill up - * @param counted - * If non-null, module for which value of _nByte - * shall be incremented appropriately - */ - public static int readByteBuf(DataInputStream stream, byte[] buf, - ModuleBase counted) throws IOException { - int bytesRead = stream.read(buf); - if (counted != null && bytesRead > 0) { - counted._nByte += bytesRead; - } - return bytesRead; - } - - /** - * Reads two bytes as an unsigned short value from a DataInputStream. - * - * @param stream - * The stream to read from. - * @param bigEndian - * If true, interpret the first byte as the high - * byte, otherwise interpret the first byte as - * the low byte. - */ - public static int readUnsignedShort(DataInputStream stream, - boolean bigEndian) throws IOException { - return readUnsignedShort(stream, bigEndian, null); - } - - /** - * Reads two bytes as an unsigned short value from a DataInputStream. - * - * @param stream - * The stream to read from. - * @param bigEndian - * If true, interpret the first byte as the high - * byte, otherwise interpret the first byte as - * the low byte. - */ - public static int readUnsignedShort(DataInputStream stream, - boolean bigEndian, ModuleBase counted) throws IOException { - int n = 0; - if (bigEndian) { - n = stream.readUnsignedShort(); - } else { - int b1 = stream.readUnsignedByte(); - int b0 = stream.readUnsignedByte(); - n = (b0 << 8) | b1; - } - if (counted != null) { - counted._nByte += 2; - } - return n; - } - - /** - * Reads two bytes as an unsigned short value from a - * RandomAccessFile. - * - * @param file - * The file to read from. - * @param bigEndian - * If true, interpret the first byte as the high - * byte, otherwise interpret the first byte as - * the low byte. - */ - public static int readUnsignedShort(RandomAccessFile file, - boolean bigEndian) throws IOException { - int n = 0; - if (bigEndian) { - n = file.readUnsignedShort(); - } else { - int b1 = file.readUnsignedByte(); - int b0 = file.readUnsignedByte(); - n = (b0 << 8) | b1; - } - return n; - } - - /** - * Reads four bytes as an unsigned 32-bit value from a - * DataInputStream. - * - * @param stream - * The stream to read from. - * @param bigEndian - * If true, interpret the first byte as the high - * byte, otherwise interpret the first byte as - * the low byte. - */ - public static long readUnsignedInt(DataInputStream stream, - boolean bigEndian) throws IOException { - return readUnsignedInt(stream, bigEndian, null); - } - - /** - * Reads four bytes as an unsigned 32-bit value from a - * DataInputStream. - * - * @param stream - * The stream to read from. - * @param bigEndian - * If true, interpret the first byte as the high - * byte, otherwise interpret the first byte as - * the low byte. - */ - public static long readUnsignedInt(DataInputStream stream, - boolean bigEndian, ModuleBase counted) throws IOException { - long n = 0; - if (bigEndian) { - n = stream.readInt(); /* This is a signed value. */ - if (n < 0) { - // n = 2147483648L + n; - n = n & 0XFFFFFFFFL; - } - } else { - long b3 = stream.readUnsignedByte(); - long b2 = stream.readUnsignedByte(); - long b1 = stream.readUnsignedByte(); - long b0 = stream.readUnsignedByte(); - n = (b0 << 24) | (b1 << 16) | (b2 << 8) | b3; - } - if (counted != null) { - counted._nByte += 4; - } - return n; - } - - /** - * Reads four bytes as an unsigned 32-bit value from a - * RandomAccessFile. - * - * @param file - * The file to read from. - * @param bigEndian - * If true, interpret the first byte as the high - * byte, otherwise interpret the first byte as - * the low byte. - */ - public static long readUnsignedInt(RandomAccessFile file, boolean bigEndian) - throws IOException { - long n = 0; - if (bigEndian) { - n = file.readInt(); /* This is a signed value. */ - if (n < 0) { - // n = 2147483648L + n; - n = n & 0XFFFFFFFFL; - } - } else { - // For efficiency, do one read rather than four - byte buf[] = new byte[4]; - file.read(buf); - long b3 = buf[0] & 0XFFL; - long b2 = buf[1] & 0XFFL; - long b1 = buf[2] & 0XFFL; - long b0 = buf[3] & 0XFFL; - n = (b0 << 24) | (b1 << 16) | (b2 << 8) | b3; - } - return n; - } - - /** - * Reads eight bytes as a signed 64-bit value from a DataInputStream. - * - * @param stream - * The stream to read from. - * @param bigEndian - * If true, interpret the first byte as the high - * byte, otherwise interpret the first byte as - * the low byte. - */ - public static long readSignedLong(DataInputStream stream, boolean bigEndian, - ModuleBase counted) throws IOException { - long n = 0; - if (bigEndian) { - n = stream.readLong(); /* This is a signed value. */ - } else { - long b7 = stream.readUnsignedByte(); - long b6 = stream.readUnsignedByte(); - long b5 = stream.readUnsignedByte(); - long b4 = stream.readUnsignedByte(); - long b3 = stream.readUnsignedByte(); - long b2 = stream.readUnsignedByte(); - long b1 = stream.readUnsignedByte(); - long b0 = stream.readUnsignedByte(); - n = (b0 << 56) | (b1 << 48) | (b2 << 40) | (b3 << 32) | (b4 << 24) - | (b5 << 16) | (b6 << 8) | b7; - } - if (counted != null) { - counted._nByte += 8; - } - return n; - } - - public static Rational readUnsignedRational(DataInputStream stream, - boolean endian) throws IOException { - return readUnsignedRational(stream, endian, null); - } - - public static Rational readUnsignedRational(DataInputStream stream, - boolean endian, ModuleBase counted) throws IOException { - long n = readUnsignedInt(stream, endian, counted); - long d = readUnsignedInt(stream, endian, counted); - return new Rational(n, d); - } - - public static Rational readUnsignedRational(RandomAccessFile file, - boolean endian) throws IOException { - long n = readUnsignedInt(file, endian); - long d = readUnsignedInt(file, endian); - return new Rational(n, d); - } - - public static Rational readSignedRational(DataInputStream stream, - boolean endian, ModuleBase counted) throws IOException { - long n = readSignedInt(stream, endian, counted); - long d = readSignedInt(stream, endian, counted); - return new Rational(n, d); - } - - public static Rational readSignedRational(RandomAccessFile file, - boolean endian) throws IOException { - long n = readSignedInt(file, endian); - long d = readSignedInt(file, endian); - return new Rational(n, d); - } - - public static int readSignedByte(RandomAccessFile file) throws IOException { - return file.readByte(); - } - - public static int readSignedShort(RandomAccessFile file, boolean endian) - throws IOException { - int b = readUnsignedShort(file, endian); - if ((b & 0X8000) != 0) { - b |= ~0XFFFF; - } - return b; - } - - public static int readSignedInt(RandomAccessFile file, boolean endian) - throws IOException { - long b = readUnsignedInt(file, endian); - if ((b & 0X80000000L) != 0) { - b |= ~0XFFFFFFFFL; - } - return (int) b; - } - - public static int readSignedByte(DataInputStream stream) - throws IOException { - return readSignedByte(stream, null); - } - - public static int readSignedByte(DataInputStream stream, ModuleBase counted) - throws IOException { - int val = stream.readByte(); - if (counted != null) { - counted._nByte++; - } - return val; - } - - public static int readSignedShort(DataInputStream stream, boolean endian) - throws IOException { - return readSignedShort(stream, endian, null); - } - - public static int readSignedShort(DataInputStream stream, boolean endian, - ModuleBase counted) throws IOException { - int b = readUnsignedShort(stream, endian, counted); - if ((b & 0X8000) != 0) { - b |= ~0XFFFF; - } - return b; - } - - public static int readSignedInt(DataInputStream stream, boolean endian) - throws IOException { - return readSignedInt(stream, endian, null); - } - - public static int readSignedInt(DataInputStream stream, boolean endian, - ModuleBase counted) throws IOException { - long b = readUnsignedInt(stream, endian, counted); - if ((b & 0X80000000L) != 0) { - b |= ~0XFFFFFFFFL; - } - return (int) b; - } - - public static float readFloat(RandomAccessFile file, boolean endian) - throws IOException { - float f = 0.0F; - if (endian) { - f = file.readFloat(); - } else { - // For efficiency, do one read rather than four - byte buf[] = new byte[4]; - file.read(buf); - int b3 = buf[0] & 0XFF; - int b2 = buf[1] & 0XFF; - int b1 = buf[2] & 0XFF; - int b0 = buf[3] & 0XFF; - f = Float.intBitsToFloat(b0 << 24 | b1 << 16 | b2 << 8 | b3); - } - return f; - } - - public static float readFloat(DataInputStream stream, boolean endian, - ModuleBase counted) throws IOException { - float f = 0.0F; - if (endian) { - f = stream.readFloat(); - } else { - int b3 = stream.readUnsignedByte(); - int b2 = stream.readUnsignedByte(); - int b1 = stream.readUnsignedByte(); - int b0 = stream.readUnsignedByte(); - f = Float.intBitsToFloat(b0 << 24 | b1 << 16 | b2 << 8 | b3); - } - if (counted != null) { - counted._nByte += 4; - } - return f; - } - - public static double readDouble(RandomAccessFile file, boolean endian) - throws IOException { - double f = 0.0F; - if (endian) { - f = file.readDouble(); - } else { - // For efficiency, do one read rather than eight - byte buf[] = new byte[8]; - file.read(buf); - long b7 = buf[0] & 0XFFL; - long b6 = buf[1] & 0XFFL; - long b5 = buf[2] & 0XFFL; - long b4 = buf[3] & 0XFFL; - long b3 = buf[4] & 0XFFL; - long b2 = buf[5] & 0XFFL; - long b1 = buf[6] & 0XFFL; - long b0 = buf[7] & 0XFFL; - - f = Double.longBitsToDouble(b0 << 56 | b1 << 48 | b2 << 40 - | b3 << 32 | b4 << 24 | b5 << 16 | b6 << 8 | b7); - } - return f; - } - - public static double readDouble(DataInputStream stream, boolean endian) - throws IOException { - return readDouble(stream, endian, null); - } - - public static double readDouble(DataInputStream stream, boolean endian, - ModuleBase counted) throws IOException { - double f = 0.0F; - if (endian) { - f = stream.readDouble(); - } else { - long b7 = stream.readUnsignedByte(); - long b6 = stream.readUnsignedByte(); - long b5 = stream.readUnsignedByte(); - long b4 = stream.readUnsignedByte(); - long b3 = stream.readUnsignedByte(); - long b2 = stream.readUnsignedByte(); - long b1 = stream.readUnsignedByte(); - long b0 = stream.readUnsignedByte(); - f = Double.longBitsToDouble(b0 << 56 | b1 << 48 | b2 << 40 - | b3 << 32 | b4 << 24 | b5 << 16 | b6 << 8 | b7); - } - if (counted != null) { - counted._nByte += 8; - } - return f; - } - - /** Skip over some bytes. Return number of bytes skipped. */ - public long skipBytes(DataInputStream stream, long bytesToSkip) - throws IOException { - return skipBytes(stream, bytesToSkip, null); - } - - /** Skip over some bytes. Return number of bytes skipped. */ - public long skipBytes(DataInputStream stream, long bytesToSkip, - ModuleBase counted) throws IOException { - long totalBytesSkipped = 0; - while (bytesToSkip > 0) { - long bytesSkipped; - long availableBytes = stream.available(); - if (availableBytes == 0) - break; - // The count of skipped bytes returned by FileInputStream's 'skip' - // method is unreliable and may be greater than what was available - // to skip. So we need to monitor and correct any discrepancies - // ourselves. Luckily the 'available' method seems to accurately - // report the number of bytes available to be skipped, but because - // it returns an int, we're limited to skipping Integer.MAX_VALUE - // each call lest we skip more bytes than we can verify. - if (bytesToSkip > availableBytes) { - bytesSkipped = stream.skip(availableBytes); - } else { - bytesSkipped = stream.skip(bytesToSkip); - } - // If it reports skipping more bytes than were available, - // correct it to the most it could have skipped. - if (bytesSkipped > availableBytes) { - bytesSkipped = availableBytes; - } - totalBytesSkipped += bytesSkipped; - bytesToSkip -= bytesSkipped; - } - if (counted != null) { - counted._nByte += totalBytesSkipped; - } - return totalBytesSkipped; - } - - /** - * A convenience method for getting a buffered DataInputStream - * from a module's InputStream. If the size specified is 0 or - * less, the default buffer size is used. - */ - public static DataInputStream getBufferedDataStream(InputStream stream, - int size) { - BufferedInputStream bis; - if (size <= 0) { - bis = new BufferedInputStream(stream); - } else { - bis = new BufferedInputStream(stream, size); - } - return new DataInputStream(bis); - } - - /** - * A utility for converting a Vector of Properties to an - * Array. It can be simpler to build a Vector and then - * call VectorToPropArray than to allocate an array and - * drop all the Properites into the correct indices. - * All the members of the Vector must be of type Property, - * or a ClassCastException will be thrown. - */ - protected Property[] vectorToPropArray(Vector vec) { - Property[] prop = new Property[vec.size()]; - for (int i = 0; i < vec.size(); i++) { - prop[i] = (Property) vec.elementAt(i); - } - return prop; - } - - protected void setupDataStream(final InputStream stream, - final RepInfo info) { - /* - * Are checksums requested and ensure they're not already calculated - * when handling a temp file. - */ - if (_je.getChecksumFlag() && info.getChecksum().isEmpty()) { - _ckSummer = new Checksummer(); - _cstream = new ChecksumInputStream(stream, _ckSummer); - _dstream = getBufferedDataStream(_cstream, - _app != null ? _je.getBufferSize() : 0); - } else { - _dstream = getBufferedDataStream(stream, - _app != null ? _je.getBufferSize() : 0); - } - } - - protected void checksumIfRafNotCopied(final RepInfo info, - final RandomAccessFile raf) throws IOException { - /* - * We may have already done the checksums while converting a - * temporary file. - */ - _ckSummer = null; - if (_je != null && _je.getChecksumFlag() - && info.getChecksum().isEmpty()) { - _ckSummer = new Checksummer(); - calcRAChecksum(_ckSummer, raf); - setChecksums(_ckSummer, info); - } - } - - protected boolean isParamInDefaults(final String paramVal) { - if (paramVal == null) - return false; - for (String param : _defaultParams) { - if (paramVal.equalsIgnoreCase(param)) { - return true; - } - } - return false; - } - - protected void skipDstreamToEnd(final RepInfo info) { - /* - * We may not have actually hit the end of file. If we're calculating - * checksums on the fly, we have to read and discard whatever is - * left, so it will get checksummed. - */ - for (;;) { - try { - long n = skipBytes(_dstream, 2048, this); - if (n == 0) { - break; - } - } catch (Exception e) { - break; - } - } - info.setSize(_cstream.getNBytes()); - } + /** + * **************************************************************** PROTECTED INSTANCE FIELDS. + * **************************************************************** + */ + + /** The application object */ + protected App _app; + /** Coverage information */ + protected String _coverage; + /** Module last modification date */ + protected Date _date; + /** Formats recognized by this Module */ + protected String[] _format; + /** Initialization value. */ + protected String _init; + /** List of default parameters. */ + protected final List _defaultParams = new ArrayList<>(); + /** JHOVE engine. */ + protected JhoveBase _je; + /** MIME types supported by this Module */ + protected String[] _mimeType; + /** Module name */ + protected String _name; + /** Module note */ + protected String _note; + /** Module-specific parameter. */ + protected String _param; + /** Module release description */ + protected String _release; + /** RepInfo note */ + protected String _repInfoNote; + /** Copyright notice */ + protected String _rights; + /** Module Signature list */ + protected List _signature; + /** Module specification document list */ + protected List _specification; + /** Module vendor */ + protected Agent _vendor; + /** Well-formedness criteria */ + protected String _wellFormedNote; + /** Validity criteria */ + protected String _validityNote; + /** Random access flag */ + protected boolean _isRandomAccess; + /** Byte count of content object */ + protected long _nByte; + /** CRC32 calculated on content object */ + protected CRC32 _crc32; + /** MD5 digest calculated on content object */ + protected MessageDigest _md5; + /** SHA-1 digest calculated on content object */ + protected MessageDigest _sha1; + /** SHA-256 digest calculated on content object */ + protected MessageDigest _sha256; + /** Flag indicating valid checksum information set */ + protected boolean _checksumFinished; + /** Indicator of how much data to report */ + protected int _verbosity; + /** Flag to indicate read routines should count the stream */ + protected boolean _countStream; + /** The dominant "endianness" of the Module. */ + protected boolean _bigEndian; + /** The list of supported features. */ + protected List _features; + /** Logger for a module class. */ + protected Logger _logger; + /* Checksummer object */ + protected Checksummer _ckSummer; + + /* Input stream wrapper which handles checksums */ + protected ChecksumInputStream _cstream; + + /* Data input stream wrapped around _cstream */ + protected DataInputStream _dstream; + + /** + * **************************************************************** CLASS CONSTRUCTOR. + * **************************************************************** + */ + + /** + * Constructors of all subclasses of ModuleBase should call this as a {@code super} constructor. + * + * @param name Name of the module + * @param release Release identifier + * @param date Last modification date of the module code, in the form of an array of three + * numbers. {@code date[0]} is the year, {@code date[1]} the month, and {@code date[2]} the + * day. + * @param format Array of format names supported by the module + * @param coverage Details as to the specific format versions or variants that are supported by + * the module + * @param mimeType Array of MIME type strings for formats supported by the module + * @param wellFormedNote Brief explanation of what constitutes well-formed content + * @param validityNote Brief explanation of what constitutes valid content + * @param repInfoNote Note pertaining to RepInfo (may be null) + * @param note Additional information about the module (may be null) + * @param rights Copyright notice for the module + * @param isRandomAccess {@code true} if the module treats content as random-access data, {@code + * false} if it treats content as stream data + */ + protected ModuleBase( + String name, + String release, + int[] date, + String[] format, + String coverage, + String[] mimeType, + String wellFormedNote, + String validityNote, + String repInfoNote, + String note, + String rights, + boolean isRandomAccess) { + // Though we're actually in the jhove package, all the related + // action logically belongs in the module package, so we name + // this logger accordingly. + _logger = Logger.getLogger("edu.harvard.hul.ois.jhove.module"); + _logger.info("Initializing " + name); + _name = name; + _release = release; + + Calendar calendar = new GregorianCalendar(); + calendar.set(date[0], date[1] - 1, date[2]); + _date = calendar.getTime(); + + _format = format; + _coverage = coverage; + _mimeType = mimeType; + _signature = new ArrayList<>(); + _specification = new ArrayList<>(); + _wellFormedNote = wellFormedNote; + _repInfoNote = repInfoNote; + _validityNote = validityNote; + _note = note; + _rights = rights; + _isRandomAccess = isRandomAccess; + + _verbosity = MINIMUM_VERBOSITY; + initFeatures(); + } + + /** + * **************************************************************** PUBLIC INSTANCE METHODS. + * + *

Initialization methods. **************************************************************** + */ + + /** + * Initializes the feature list. This method puts the following features in the list: + * + *

    + *
  • edu.harvard.hul.ois.canValidate + *
  • edu.harvard.hul.ois.canIdentify + *
+ */ + public void initFeatures() { + _features = new ArrayList<>(2); + _features.add("edu.harvard.hul.ois.jhove.canValidate"); + _features.add("edu.harvard.hul.ois.jhove.canCharacterize"); + } + + /** Per-instantiation initialization. The default method does nothing but save its parameter. */ + @Override + public void init(String init) { + _init = init; + } + + /** + * Set a a List of default parameters for the module. + * + * @param params A List whose elements are Strings. May be empty. + */ + @Override + public void setDefaultParams(List params) { + _defaultParams.clear(); + _defaultParams.addAll(params); + } + + /** Applies the default parameters. Calling this clears any prior parameters. */ + @SuppressWarnings("unused") + @Override + public void applyDefaultParams() throws Exception { + resetParams(); + for (String parm : _defaultParams) { + param(parm); + } + } + + /** + * Reset parameter settings. Returns to a default state without any parameters. The default method + * clears the saved parameter. + */ + @Override + public void resetParams() { + _param = null; + } + + /** + * Per-action initialization. May be called multiple times. The default method does nothing but + * save its parameter. + */ + @Override + public void param(String param) { + _param = param; + } + + /** + * **************************************************************** Accessor methods. + * **************************************************************** + */ + + /** Returns the App object. */ + public App getApp() { + return _app; + } + + /** Returns the JHOVE engine object. */ + public JhoveBase getBase() { + return _je; + } + + /** Returns the value of _nByte. Meaningful only for modules that use a counted InputStream. */ + public long getNByte() { + return _nByte; + } + + /** + * Returns true if the dominant "endianness" of the module, or the current file being + * processed, is big-endian, otherwise false. This does not guarantee that all numbers in the + * module follow the dominant endianness, particularly as formats sometimes incorporate data + * stored in a previously defined format. For some formats, e.g., TIFF, the endianness depends on + * the file being processed. + * + *

Every module must initialize the value of _bigEndian for this function, or else assign its + * value when parsing a file, to return a meaningful result. For some modules (e.g., ASCII, + * endianness has no meaning. + */ + public boolean isBigEndian() { + return _bigEndian; + } + + /** + * Return details as to the specific format versions or variants that are supported by this module + */ + @Override + public final String getCoverage() { + return _coverage; + } + + /** Return the last modification date of this Module, as a Java Date object */ + @Override + public final Date getDate() { + return _date; + } + + /** Return the array of format names supported by this Module */ + @Override + public final String[] getFormat() { + return _format; + } + + /** Return the array of MIME type strings for formats supported by this Module */ + @Override + public final String[] getMimeType() { + return _mimeType; + } + + /** Return the module name */ + @Override + public final String getName() { + return _name; + } + + /** Return the module note */ + @Override + public final String getNote() { + return _note; + } + + /** Return the release identifier */ + @Override + public final String getRelease() { + return _release; + } + + /** Return the RepInfo note */ + @Override + public final String getRepInfoNote() { + return _repInfoNote; + } + + /** Return the copyright information string */ + @Override + public final String getRights() { + return _rights; + } + + /** Return the List of Signatures recognized by this Module */ + @Override + public final List getSignature() { + return _signature; + } + + /** + * Returns a list of Document objects (one for each specification document of the + * format). The specification list is generated by the Module, and specifications cannot be added + * by callers. + * + * @see Document + */ + @Override + public final List getSpecification() { + return _specification; + } + + /** Return the vendor information */ + @Override + public final Agent getVendor() { + return _vendor; + } + + /** Return the string describing well-formedness criteria */ + @Override + public final String getWellFormedNote() { + return _wellFormedNote; + } + + /** Return the string describing validity criteria */ + @Override + public final String getValidityNote() { + return _validityNote; + } + + /** + * Return the random access flag (true if the module operates on random access files, false if it + * operates on streams) + */ + @Override + public final boolean isRandomAccess() { + return _isRandomAccess; + } + + /** + * Returns true if the module supports a given named feature, and false + * if the feature is unsupported or unknown. Feature names are case sensitive. + * + *

It is recommended that features be named using package nomenclature. The following features + * are, by default, supported by the modules developed by OIS: + * + *

    + *
  • edu.harvard.hul.ois.canValidate + *
  • edu.harvard.hul.ois.canIdentify + *
+ */ + @Override + public boolean hasFeature(String feature) { + if (_features == null) { + // dubious, but check it + return false; + } + Iterator iter = _features.iterator(); + while (iter.hasNext()) { + String f = iter.next(); + if (f.equals(feature)) { + return true; + } + } + return false; + } + + /** Returns the full list of features. */ + @Override + public List getFeatures() { + return _features; + } + + /** Returns the list of default parameters. */ + @Override + public List getDefaultParams() { + return Collections.unmodifiableList(_defaultParams); + } + + /** + * **************************************************************** Mutator methods. + * **************************************************************** + */ + + /** Pass the associated App object to this Module. The App makes various services available. */ + @Override + public final void setApp(App app) { + _app = app; + } + + /** Pass the JHOVE engine object to this Module. */ + @Override + public final void setBase(JhoveBase je) { + _je = je; + } + + /** + * Set the value of the validityNote property, which briefly explains the validity criteria of + * this Module. + */ + public final void setValidityNote(String validityNote) { + _validityNote = validityNote; + } + + /** + * Set the value of the CRC32 calculated for the content object. The checksum-like functions can + * be set by the caller. Setting any of these creates the assumption that the calculation is + * already done, and sets the checksumFinished flag to inhibit recalculation. + */ + public final void setCRC32(CRC32 crc32) { + _crc32 = crc32; + _checksumFinished = true; + } + + /** + * Set the degree of verbosity desired from the module. The setting of param can + * override the verbosity setting. It does not affect whether raw data are reported or not, only + * which data are reported. + * + * @param verbosity The requested verbosity value. Recognized values are Module.MINIMUM_VERBOSITY + * and Module.MAXIMUM_VERBOSITY. The interpretation of the value depends on the module, and + * the module may choose not to use this setting. However, modules should treat + * MAXIMUM_VERBOSITY as a request for all the data available from the module. + */ + @Override + public void setVerbosity(int verbosity) { + _verbosity = verbosity; + } + + /** Sets the byte count for the content object, and sets the checksumFinished flag. */ + public final void setNByte(long nByte) { + _nByte = nByte; + _checksumFinished = true; + } + + /** Sets the MD5 calculated digest for the content object, and sets the checksumFinished flag. */ + public final void setMD5(MessageDigest md5) { + _md5 = md5; + _checksumFinished = true; + } + + /** + * Sets the SHA-1 calculated digest for the content object, and sets the checksumFinished flag. + */ + public final void setSHA1(MessageDigest sha1) { + _sha1 = sha1; + _checksumFinished = true; + } + + /** + * Sets the SHA-256 calculated digest for the content object, and sets the checksumFinished flag. + */ + public final void setSHA256(MessageDigest sha256) { + _sha256 = sha256; + _checksumFinished = true; + } + + /** + * **************************************************************** Parsing methods. + * **************************************************************** + */ + + /** + * Parse the content of a stream digital object and store the results in RepInfo. A given Module + * will normally override only one of the two parse methods; the default method does nothing. + * + * @param stream An InputStream, positioned at its beginning, which is generated from the object + * to be parsed. If multiple calls to parse are made on the basis of a nonzero + * value being returned, a new InputStream must be provided each time. + * @param info A fresh (on the first call) RepInfo object which will be modified to reflect the + * results of the parsing If multiple calls to parse are made on the basis of a + * nonzero value being returned, the same RepInfo object should be passed with each call. + * @param parseIndex Must be 0 in first call to parse. If parse returns + * a nonzero value, it must be called again with parseIndex equal to that return + * value. + */ + @SuppressWarnings("unused") + @Override + public int parse(InputStream stream, RepInfo info, int parseIndex) throws IOException { + return 0; + } + + /** + * Parse the content of a random access digital object and store the results in RepInfo. A given + * Module will normally override only one of the two parse methods; the default method does + * nothing. + * + * @param file A RandomAccessFile, positioned at its beginning, which is generated from the object + * to be parsed + * @param info A fresh RepInfo object which will be modified to reflect the results of the parsing + */ + @SuppressWarnings("unused") + @Override + public void parse(RandomAccessFile file, RepInfo info) throws IOException {} + + /** + * Check if the digital object conforms to this Module's internal signature information. This + * function checks the file against the list of predefined signatures for the module. If there are + * no predefined signatures, it calls parse with the arguments passed to it. Override this for + * modules that check digital signatures in some other way. Any module for which the signature may + * be located other than at the beginning of the file must override. + * + * @param file A File object for the object being parsed + * @param stream An InputStream, positioned at its beginning, which is generated from the object + * to be parsed + * @param info A fresh RepInfo object which will be modified to reflect the results of the test + */ + @Override + public void checkSignatures(File file, InputStream stream, RepInfo info) throws IOException { + info.setFormat(_format[0]); + info.setMimeType(_mimeType[0]); + info.setModule(this); + int sigsChecked = 0; + if (_signature.size() > 0) { + /* + * Get each of the internal sigs defined for the module + * and test it. All sigs must be present. If there are + * no internal signatures, this test is meaningless. + */ + byte[] sigBuf = new byte[1024]; + stream.read(sigBuf); + stream.close(); + ListIterator iter = _signature.listIterator(); + while (iter.hasNext()) { + Signature sig = (iter.next()); + if (sig instanceof InternalSignature) { + InternalSignature isig = (InternalSignature) sig; + int[] sigValue = isig.getValue(); + int offset = isig.getOffset(); + boolean match = true; + for (int i = 0; i < sigValue.length; i++) { + if (sigBuf[offset + i] != sigValue[i]) { + match = false; + break; + } + } + if (!match && isig.getUse().equals(SignatureUseType.MANDATORY)) { + info.setWellFormed(false); + return; + } + if (match) { + // Only count optional signatures if they match. + ++sigsChecked; + } + } + } + } + if (sigsChecked == 0) { + // No internal sigs defined, parse the file. + int parseIndex = parse(stream, info, 0); + while (parseIndex != 0) { + stream.close(); + if (file != null) { + stream = new FileInputStream(file); + parseIndex = parse(stream, info, parseIndex); + } else { + parseIndex = 0; + } + } + } else if (info.getWellFormed() == RepInfo.TRUE) { + info.setSigMatch(_name); + } + } + + /** + * Check if the digital object conforms to this Module's internal signature information. + * + * @param file A File object representing the object to be parsed + * @param raf A RandomAccessFile, positioned at its beginning, which is generated from the object + * to be parsed + * @param info A fresh RepInfo object which will be modified to reflect the results of the test + */ + @SuppressWarnings("unused") + @Override + public void checkSignatures(File file, RandomAccessFile raf, RepInfo info) throws IOException { + info.setFormat(_format[0]); + info.setMimeType(_mimeType[0]); + info.setModule(this); + int sigsChecked = 0; + /* + * Get each of the internal sigs defined for the module + * and test it. + */ + ListIterator iter = _signature.listIterator(); + try { + while (iter.hasNext()) { + Signature sig = (iter.next()); + if (sig instanceof InternalSignature) { + InternalSignature isig = (InternalSignature) sig; + /* What about non-fixed offset? */ + raf.seek(isig.getOffset()); + int[] sigValue = isig.getValue(); + boolean match = true; + for (int i = 0; i < sigValue.length; i++) { + if (readUnsignedByte(raf) != sigValue[i]) { + match = false; + break; + } + } + if (!match && isig.getUse().equals(SignatureUseType.MANDATORY)) { + info.setWellFormed(false); + break; + } + if (match) { + // Only count optional signatures if they match. + ++sigsChecked; + } + } + } + } catch (Exception e) { + // We may get here on a short file. + info.setWellFormed(false); + return; + } + // Must match at least one signature. + if (sigsChecked == 0) { + info.setWellFormed(false); + } else if (info.getWellFormed() == RepInfo.TRUE) { + info.setSigMatch(_name); + } + } + + /** + * Initializes the state of the module for parsing. This should be called early in each module's + * parse() method. If a module overrides it to provide additional functionality, the module's + * initParse() should call super.initParse(). + */ + protected void initParse() { + _logger.info(_name + " called initParse"); + _checksumFinished = false; + _nByte = 0; + _crc32 = new CRC32(); + try { + _md5 = MessageDigest.getInstance("MD5"); + _sha1 = MessageDigest.getInstance("SHA-1"); + _sha256 = MessageDigest.getInstance("SHA-256"); + } catch (NoSuchAlgorithmException e) { + throw new IllegalStateException("Missing checksum algorithm.", e); + } + } + + protected void initInfo(final RepInfo info) { + info.setModule(this); + info.setFormat(this._format[0]); + info.setMimeType(this._mimeType[0]); + } + + /** Calculates the checksums for a module that uses a random access file. */ + protected void calcRAChecksum(Checksummer ckSummer, RandomAccessFile raf) throws IOException { + if (ckSummer == null) { + return; + } + + raf.seek(0); + byte[] buffer = new byte[_je.getBufferSize()]; + int n = -1; + try { + while ((n = raf.read(buffer)) != -1) { + if (n > 0) { + ckSummer.update(buffer, 0, n); + } + } + } catch (Exception e) { + } + } + + /** + * Set the checksum values. + * + * @param ckSummer Checksummer object + * @param info RepInfo object + */ + protected static void setChecksums(Checksummer ckSummer, RepInfo info) { + if (ckSummer != null) { + info.setChecksum(new Checksum(ckSummer.getCRC32(), ChecksumType.CRC32)); + String value = ckSummer.getMD5(); + if (value != null) { + info.setChecksum(new Checksum(value, ChecksumType.MD5)); + } + if ((value = ckSummer.getSHA1()) != null) { + info.setChecksum(new Checksum(value, ChecksumType.SHA1)); + } + if ((value = ckSummer.getSHA256()) != null) { + info.setChecksum(new Checksum(value, ChecksumType.SHA256)); + } + } + } + + /** + * Generates information about this Module. The format of the output depends on the OutputHandler. + */ + @Override + public void show(OutputHandler handler) { + handler.show(this); + } + + /** + * **************************************************************** PRIVATE INSTANCE METHODS. + * **************************************************************** + */ + + /** Returns the hex string representation of the CRC32 result. */ + protected String getCRC32() { + return Long.toHexString(_crc32.getValue()); + } + + /** + * **************************************************************** PUBLIC INSTANCE METHODS. + * **************************************************************** + */ + + /** + * Returns a Property representing an integer value. If raw output is specified for the module, + * returns an INTEGER property, and labels and index are unused. + * Otherwise, returns a STRING property, with the string being the element of labels + * whose index is the index of value in index. + */ + public Property addIntegerProperty(String name, int value, String[] labels, int[] index) { + boolean rawOutput = _je.getShowRawFlag(); + Property prop = null; + if (!rawOutput) { + int n = -1; + for (int i = 0; i < index.length; i++) { + if (value == index[i]) { + n = i; + break; + } + } + if (n > -1) { + prop = new Property(name, PropertyType.STRING, labels[n]); + } + } + if (prop == null) { + prop = new Property(name, PropertyType.INTEGER, new Integer(value)); + } + + return prop; + } + + /** + * Returns a Property representing an integer value. If raw output is specified for the module, + * returns an INTEGER property, and labels and index are unused. + * Otherwise, returns a STRING property, with the string being the element of labels + * whose index is value. + */ + public Property addIntegerProperty(String name, int value, String[] labels) { + if (!_je.getShowRawFlag()) { + try { + return new Property(name, PropertyType.STRING, labels[value]); + } catch (Exception e) { + // fall through + } + } + return new Property(name, PropertyType.INTEGER, new Integer(value)); + } + + /** + * Reads an unsigned byte from a DataInputStream. + * + * @param stream Stream to read + */ + public static int readUnsignedByte(DataInputStream stream) throws IOException { + return readUnsignedByte(stream, null); + } + + /** + * Reads an unsigned byte from a DataInputStream. + * + * @param stream Stream to read + * @param counted If non-null, module for which value of _nByte shall be incremented appropriately + */ + public static int readUnsignedByte(DataInputStream stream, ModuleBase counted) + throws IOException { + int val = stream.readUnsignedByte(); + if (counted != null) { + counted._nByte++; + } + return val; + } + + /** Reads an unsigned byte from a RandomAccessFile. */ + public static int readUnsignedByte(RandomAccessFile file) throws IOException { + return file.readUnsignedByte(); + } + + /** + * Reads into a byte buffer from a DataInputStream. + * + * @param stream Stream to read from + * @param buf Byte buffer to fill up + * @param counted If non-null, module for which value of _nByte shall be incremented appropriately + */ + public static int readByteBuf(DataInputStream stream, byte[] buf, ModuleBase counted) + throws IOException { + int bytesRead = stream.read(buf); + if (counted != null && bytesRead > 0) { + counted._nByte += bytesRead; + } + return bytesRead; + } + + /** + * Reads two bytes as an unsigned short value from a DataInputStream. + * + * @param stream The stream to read from. + * @param bigEndian If true, interpret the first byte as the high byte, otherwise interpret the + * first byte as the low byte. + */ + public static int readUnsignedShort(DataInputStream stream, boolean bigEndian) + throws IOException { + return readUnsignedShort(stream, bigEndian, null); + } + + /** + * Reads two bytes as an unsigned short value from a DataInputStream. + * + * @param stream The stream to read from. + * @param bigEndian If true, interpret the first byte as the high byte, otherwise interpret the + * first byte as the low byte. + */ + public static int readUnsignedShort(DataInputStream stream, boolean bigEndian, ModuleBase counted) + throws IOException { + int n = 0; + if (bigEndian) { + n = stream.readUnsignedShort(); + } else { + int b1 = stream.readUnsignedByte(); + int b0 = stream.readUnsignedByte(); + n = (b0 << 8) | b1; + } + if (counted != null) { + counted._nByte += 2; + } + return n; + } + + /** + * Reads two bytes as an unsigned short value from a RandomAccessFile. + * + * @param file The file to read from. + * @param bigEndian If true, interpret the first byte as the high byte, otherwise interpret the + * first byte as the low byte. + */ + public static int readUnsignedShort(RandomAccessFile file, boolean bigEndian) throws IOException { + int n = 0; + if (bigEndian) { + n = file.readUnsignedShort(); + } else { + int b1 = file.readUnsignedByte(); + int b0 = file.readUnsignedByte(); + n = (b0 << 8) | b1; + } + return n; + } + + /** + * Reads four bytes as an unsigned 32-bit value from a DataInputStream. + * + * @param stream The stream to read from. + * @param bigEndian If true, interpret the first byte as the high byte, otherwise interpret the + * first byte as the low byte. + */ + public static long readUnsignedInt(DataInputStream stream, boolean bigEndian) throws IOException { + return readUnsignedInt(stream, bigEndian, null); + } + + /** + * Reads four bytes as an unsigned 32-bit value from a DataInputStream. + * + * @param stream The stream to read from. + * @param bigEndian If true, interpret the first byte as the high byte, otherwise interpret the + * first byte as the low byte. + */ + public static long readUnsignedInt(DataInputStream stream, boolean bigEndian, ModuleBase counted) + throws IOException { + long n = 0; + if (bigEndian) { + n = stream.readInt(); /* This is a signed value. */ + if (n < 0) { + // n = 2147483648L + n; + n = n & 0XFFFFFFFFL; + } + } else { + long b3 = stream.readUnsignedByte(); + long b2 = stream.readUnsignedByte(); + long b1 = stream.readUnsignedByte(); + long b0 = stream.readUnsignedByte(); + n = (b0 << 24) | (b1 << 16) | (b2 << 8) | b3; + } + if (counted != null) { + counted._nByte += 4; + } + return n; + } + + /** + * Reads four bytes as an unsigned 32-bit value from a RandomAccessFile. + * + * @param file The file to read from. + * @param bigEndian If true, interpret the first byte as the high byte, otherwise interpret the + * first byte as the low byte. + */ + public static long readUnsignedInt(RandomAccessFile file, boolean bigEndian) throws IOException { + long n = 0; + if (bigEndian) { + n = file.readInt(); /* This is a signed value. */ + if (n < 0) { + // n = 2147483648L + n; + n = n & 0XFFFFFFFFL; + } + } else { + // For efficiency, do one read rather than four + byte buf[] = new byte[4]; + file.read(buf); + long b3 = buf[0] & 0XFFL; + long b2 = buf[1] & 0XFFL; + long b1 = buf[2] & 0XFFL; + long b0 = buf[3] & 0XFFL; + n = (b0 << 24) | (b1 << 16) | (b2 << 8) | b3; + } + return n; + } + + /** + * Reads eight bytes as a signed 64-bit value from a DataInputStream. + * + * @param stream The stream to read from. + * @param bigEndian If true, interpret the first byte as the high byte, otherwise interpret the + * first byte as the low byte. + */ + public static long readSignedLong(DataInputStream stream, boolean bigEndian, ModuleBase counted) + throws IOException { + long n = 0; + if (bigEndian) { + n = stream.readLong(); /* This is a signed value. */ + } else { + long b7 = stream.readUnsignedByte(); + long b6 = stream.readUnsignedByte(); + long b5 = stream.readUnsignedByte(); + long b4 = stream.readUnsignedByte(); + long b3 = stream.readUnsignedByte(); + long b2 = stream.readUnsignedByte(); + long b1 = stream.readUnsignedByte(); + long b0 = stream.readUnsignedByte(); + n = + (b0 << 56) + | (b1 << 48) + | (b2 << 40) + | (b3 << 32) + | (b4 << 24) + | (b5 << 16) + | (b6 << 8) + | b7; + } + if (counted != null) { + counted._nByte += 8; + } + return n; + } + + public static Rational readUnsignedRational(DataInputStream stream, boolean endian) + throws IOException { + return readUnsignedRational(stream, endian, null); + } + + public static Rational readUnsignedRational( + DataInputStream stream, boolean endian, ModuleBase counted) throws IOException { + long n = readUnsignedInt(stream, endian, counted); + long d = readUnsignedInt(stream, endian, counted); + return new Rational(n, d); + } + + public static Rational readUnsignedRational(RandomAccessFile file, boolean endian) + throws IOException { + long n = readUnsignedInt(file, endian); + long d = readUnsignedInt(file, endian); + return new Rational(n, d); + } + + public static Rational readSignedRational( + DataInputStream stream, boolean endian, ModuleBase counted) throws IOException { + long n = readSignedInt(stream, endian, counted); + long d = readSignedInt(stream, endian, counted); + return new Rational(n, d); + } + + public static Rational readSignedRational(RandomAccessFile file, boolean endian) + throws IOException { + long n = readSignedInt(file, endian); + long d = readSignedInt(file, endian); + return new Rational(n, d); + } + + public static int readSignedByte(RandomAccessFile file) throws IOException { + return file.readByte(); + } + + public static int readSignedShort(RandomAccessFile file, boolean endian) throws IOException { + int b = readUnsignedShort(file, endian); + if ((b & 0X8000) != 0) { + b |= ~0XFFFF; + } + return b; + } + + public static int readSignedInt(RandomAccessFile file, boolean endian) throws IOException { + long b = readUnsignedInt(file, endian); + if ((b & 0X80000000L) != 0) { + b |= ~0XFFFFFFFFL; + } + return (int) b; + } + + public static int readSignedByte(DataInputStream stream) throws IOException { + return readSignedByte(stream, null); + } + + public static int readSignedByte(DataInputStream stream, ModuleBase counted) throws IOException { + int val = stream.readByte(); + if (counted != null) { + counted._nByte++; + } + return val; + } + + public static int readSignedShort(DataInputStream stream, boolean endian) throws IOException { + return readSignedShort(stream, endian, null); + } + + public static int readSignedShort(DataInputStream stream, boolean endian, ModuleBase counted) + throws IOException { + int b = readUnsignedShort(stream, endian, counted); + if ((b & 0X8000) != 0) { + b |= ~0XFFFF; + } + return b; + } + + public static int readSignedInt(DataInputStream stream, boolean endian) throws IOException { + return readSignedInt(stream, endian, null); + } + + public static int readSignedInt(DataInputStream stream, boolean endian, ModuleBase counted) + throws IOException { + long b = readUnsignedInt(stream, endian, counted); + if ((b & 0X80000000L) != 0) { + b |= ~0XFFFFFFFFL; + } + return (int) b; + } + + public static float readFloat(RandomAccessFile file, boolean endian) throws IOException { + float f = 0.0F; + if (endian) { + f = file.readFloat(); + } else { + // For efficiency, do one read rather than four + byte buf[] = new byte[4]; + file.read(buf); + int b3 = buf[0] & 0XFF; + int b2 = buf[1] & 0XFF; + int b1 = buf[2] & 0XFF; + int b0 = buf[3] & 0XFF; + f = Float.intBitsToFloat(b0 << 24 | b1 << 16 | b2 << 8 | b3); + } + return f; + } + + public static float readFloat(DataInputStream stream, boolean endian, ModuleBase counted) + throws IOException { + float f = 0.0F; + if (endian) { + f = stream.readFloat(); + } else { + int b3 = stream.readUnsignedByte(); + int b2 = stream.readUnsignedByte(); + int b1 = stream.readUnsignedByte(); + int b0 = stream.readUnsignedByte(); + f = Float.intBitsToFloat(b0 << 24 | b1 << 16 | b2 << 8 | b3); + } + if (counted != null) { + counted._nByte += 4; + } + return f; + } + + public static double readDouble(RandomAccessFile file, boolean endian) throws IOException { + double f = 0.0F; + if (endian) { + f = file.readDouble(); + } else { + // For efficiency, do one read rather than eight + byte buf[] = new byte[8]; + file.read(buf); + long b7 = buf[0] & 0XFFL; + long b6 = buf[1] & 0XFFL; + long b5 = buf[2] & 0XFFL; + long b4 = buf[3] & 0XFFL; + long b3 = buf[4] & 0XFFL; + long b2 = buf[5] & 0XFFL; + long b1 = buf[6] & 0XFFL; + long b0 = buf[7] & 0XFFL; + + f = + Double.longBitsToDouble( + b0 << 56 | b1 << 48 | b2 << 40 | b3 << 32 | b4 << 24 | b5 << 16 | b6 << 8 | b7); + } + return f; + } + + public static double readDouble(DataInputStream stream, boolean endian) throws IOException { + return readDouble(stream, endian, null); + } + + public static double readDouble(DataInputStream stream, boolean endian, ModuleBase counted) + throws IOException { + double f = 0.0F; + if (endian) { + f = stream.readDouble(); + } else { + long b7 = stream.readUnsignedByte(); + long b6 = stream.readUnsignedByte(); + long b5 = stream.readUnsignedByte(); + long b4 = stream.readUnsignedByte(); + long b3 = stream.readUnsignedByte(); + long b2 = stream.readUnsignedByte(); + long b1 = stream.readUnsignedByte(); + long b0 = stream.readUnsignedByte(); + f = + Double.longBitsToDouble( + b0 << 56 | b1 << 48 | b2 << 40 | b3 << 32 | b4 << 24 | b5 << 16 | b6 << 8 | b7); + } + if (counted != null) { + counted._nByte += 8; + } + return f; + } + + /** Skip over some bytes. Return number of bytes skipped. */ + public long skipBytes(DataInputStream stream, long bytesToSkip) throws IOException { + return skipBytes(stream, bytesToSkip, null); + } + + /** Skip over some bytes. Return number of bytes skipped. */ + public long skipBytes(DataInputStream stream, long bytesToSkip, ModuleBase counted) + throws IOException { + long totalBytesSkipped = 0; + while (bytesToSkip > 0) { + long bytesSkipped; + long availableBytes = stream.available(); + if (availableBytes == 0) break; + // The count of skipped bytes returned by FileInputStream's 'skip' + // method is unreliable and may be greater than what was available + // to skip. So we need to monitor and correct any discrepancies + // ourselves. Luckily the 'available' method seems to accurately + // report the number of bytes available to be skipped, but because + // it returns an int, we're limited to skipping Integer.MAX_VALUE + // each call lest we skip more bytes than we can verify. + if (bytesToSkip > availableBytes) { + bytesSkipped = stream.skip(availableBytes); + } else { + bytesSkipped = stream.skip(bytesToSkip); + } + // If it reports skipping more bytes than were available, + // correct it to the most it could have skipped. + if (bytesSkipped > availableBytes) { + bytesSkipped = availableBytes; + } + totalBytesSkipped += bytesSkipped; + bytesToSkip -= bytesSkipped; + } + if (counted != null) { + counted._nByte += totalBytesSkipped; + } + return totalBytesSkipped; + } + + /** + * A convenience method for getting a buffered DataInputStream from a module's InputStream. If the + * size specified is 0 or less, the default buffer size is used. + */ + public static DataInputStream getBufferedDataStream(InputStream stream, int size) { + BufferedInputStream bis; + if (size <= 0) { + bis = new BufferedInputStream(stream); + } else { + bis = new BufferedInputStream(stream, size); + } + return new DataInputStream(bis); + } + + /** + * A utility for converting a Vector of Properties to an Array. It can be simpler to build a + * Vector and then call VectorToPropArray than to allocate an array and drop all the Properites + * into the correct indices. All the members of the Vector must be of type Property, or a + * ClassCastException will be thrown. + */ + protected Property[] vectorToPropArray(Vector vec) { + Property[] prop = new Property[vec.size()]; + for (int i = 0; i < vec.size(); i++) { + prop[i] = (Property) vec.elementAt(i); + } + return prop; + } + + protected void setupDataStream(final InputStream stream, final RepInfo info) { + /* + * Are checksums requested and ensure they're not already calculated + * when handling a temp file. + */ + if (_je.getChecksumFlag() && info.getChecksum().isEmpty()) { + _ckSummer = new Checksummer(); + _cstream = new ChecksumInputStream(stream, _ckSummer); + _dstream = getBufferedDataStream(_cstream, _app != null ? _je.getBufferSize() : 0); + } else { + _dstream = getBufferedDataStream(stream, _app != null ? _je.getBufferSize() : 0); + } + } + + protected void checksumIfRafNotCopied(final RepInfo info, final RandomAccessFile raf) + throws IOException { + /* + * We may have already done the checksums while converting a + * temporary file. + */ + _ckSummer = null; + if (_je != null && _je.getChecksumFlag() && info.getChecksum().isEmpty()) { + _ckSummer = new Checksummer(); + calcRAChecksum(_ckSummer, raf); + setChecksums(_ckSummer, info); + } + } + + protected boolean isParamInDefaults(final String paramVal) { + if (paramVal == null) return false; + for (String param : _defaultParams) { + if (paramVal.equalsIgnoreCase(param)) { + return true; + } + } + return false; + } + + protected void skipDstreamToEnd(final RepInfo info) { + /* + * We may not have actually hit the end of file. If we're calculating + * checksums on the fly, we have to read and discard whatever is + * left, so it will get checksummed. + */ + for (; ; ) { + try { + long n = skipBytes(_dstream, 2048, this); + if (n == 0) { + break; + } + } catch (Exception e) { + break; + } + } + info.setSize(_cstream.getNBytes()); + } } diff --git a/jhove-core/src/main/java/edu/harvard/hul/ois/jhove/ModuleInfo.java b/jhove-core/src/main/java/edu/harvard/hul/ois/jhove/ModuleInfo.java index f286b3d9b..c74458286 100644 --- a/jhove-core/src/main/java/edu/harvard/hul/ois/jhove/ModuleInfo.java +++ b/jhove-core/src/main/java/edu/harvard/hul/ois/jhove/ModuleInfo.java @@ -2,16 +2,16 @@ /** A small class to hold information about a module. */ public class ModuleInfo { - public String clas; - public String init; - public String[] params; - - public ModuleInfo (String className) { - clas = className; - } - - public ModuleInfo (String className, String init) { - clas = className; - this.init = init; - } + public String clas; + public String init; + public String[] params; + + public ModuleInfo(String className) { + clas = className; + } + + public ModuleInfo(String className, String init) { + clas = className; + this.init = init; + } } diff --git a/jhove-core/src/main/java/edu/harvard/hul/ois/jhove/NisoImageMetadata.java b/jhove-core/src/main/java/edu/harvard/hul/ois/jhove/NisoImageMetadata.java index 2d86d3d65..4bc9b2e25 100644 --- a/jhove-core/src/main/java/edu/harvard/hul/ois/jhove/NisoImageMetadata.java +++ b/jhove-core/src/main/java/edu/harvard/hul/ois/jhove/NisoImageMetadata.java @@ -1,8 +1,8 @@ -/********************************************************************** - * Jhove - JSTOR/Harvard Object Validation Environment - * Copyright 2003 by JSTOR and the President and Fellows of Harvard College - **********************************************************************/ - +/** + * ******************************************************************** Jhove - JSTOR/Harvard Object + * Validation Environment Copyright 2003 by JSTOR and the President and Fellows of Harvard College + * ******************************************************************** + */ package edu.harvard.hul.ois.jhove; import java.awt.color.ICC_Profile; @@ -10,2311 +10,2280 @@ import java.nio.charset.StandardCharsets; import java.util.logging.Logger; - /** - * Encapsulation of the NISO Z39.87-2002 / AIIM 20-2002 Data Dictionary -- - * Technical Metadata for Digital Still Images + * Encapsulation of the NISO Z39.87-2002 / AIIM 20-2002 Data Dictionary -- Technical Metadata for + * Digital Still Images */ -public class NisoImageMetadata -{ - /** Logger for this class. */ - private static final Logger LOGGER = Logger.getLogger(NisoImageMetadata.class.getCanonicalName()); - - /** private String constants. */ - private static final String YES = "Yes"; - private static final String NO = "No"; - private static final String TIME_SEP = ":"; - - /****************************************************************** - * PUBLIC CLASS FIELDS. - ******************************************************************/ - - /** 7.7.3.15 auto focus value labels. */ - public static final String [] AUTOFOCUS = { - "unidentified", "auto focus used", "auto focus interrupted", - "near focused", "soft focus", "manual" - }; - - /** 7.7.3.13 back light value labels. */ - public static final String [] BACKLIGHT = { - "front light", "backlight 1", "backlight 2" - }; - - /** 6.1.2 byte order value labels. */ - public static final String [] BYTEORDER = { - "big-endian", "little-endian" - }; - - /** 6.2.3.1 Checksum method value labels. */ - public static final String [] CHECKSUM_METHOD = { - "CRC32", "MD5", "SHA-1", "SHA-256" - }; - - /** 6.1.4.1 Color space value labels. */ - public static final String [] COLORSPACE = { - "white is zero", "black is zero", "RGB", "palette color", - "transparency mask", "CMYK", "YCbCr", "CIE L*a*b*", "ICC L*a*b*", +public class NisoImageMetadata { + /** Logger for this class. */ + private static final Logger LOGGER = Logger.getLogger(NisoImageMetadata.class.getCanonicalName()); + + /** private String constants. */ + private static final String YES = "Yes"; + + private static final String NO = "No"; + private static final String TIME_SEP = ":"; + + /** + * **************************************************************** PUBLIC CLASS FIELDS. + * **************************************************************** + */ + + /** 7.7.3.15 auto focus value labels. */ + public static final String[] AUTOFOCUS = { + "unidentified", "auto focus used", "auto focus interrupted", + "near focused", "soft focus", "manual" + }; + + /** 7.7.3.13 back light value labels. */ + public static final String[] BACKLIGHT = {"front light", "backlight 1", "backlight 2"}; + + /** 6.1.2 byte order value labels. */ + public static final String[] BYTEORDER = {"big-endian", "little-endian"}; + + /** 6.2.3.1 Checksum method value labels. */ + public static final String[] CHECKSUM_METHOD = {"CRC32", "MD5", "SHA-1", "SHA-256"}; + + /** 6.1.4.1 Color space value labels. */ + public static final String[] COLORSPACE = { + "white is zero", + "black is zero", + "RGB", + "palette color", + "transparency mask", + "CMYK", + "YCbCr", + "CIE L*a*b*", + "ICC L*a*b*", "ITU L*a*b*", "CFA", "CIE Log2(L)", "CIE Log2(L)(u',v')", "LinearRaw", "YCCK" - }; - - /** Index for 6.1.4.1 color space value labels. */ - public static final int [] COLORSPACE_INDEX = { - 0, 1, 2, 3, - 4, 5, 6, 8, 9, - 10, - 32803, - 32844, - 32845, - 34892, - 65535 - }; - - /** 6.1.3.1 Compression scheme value labels. */ - /* Taken from the TIFFTAG_COMPRESSION */ - public static final String [] COMPRESSION_SCHEME = { - "uncompressed", "CCITT 1D", "CCITT Group 3", "CCITT Group 4", /* 1-4 */ - "LZW", "JPEG", "ISO JPEG", "Deflate", /* 5-8 */ - "JBIG", /* 32661 */ - "RLE with word alignment", /* 32771 */ - "PackBits", "NeXT 2-bit encoding", "ThunderScan 4-bit encoding", /* 32773- */ - "RasterPadding in CT or MP", /* 32895 */ - "RLE for LW", "RLE for HC", "RLE for BL", /* 32896-8 */ - "Pixar 10-bit LZW", /* 32908 */ - "Pixar companded 11-bit ZIP encoding", /* 32909 */ - "PKZIP-style Deflate encoding", /* 32946 */ - "Kodak DCS", /* 32947 */ - "SGI 32-bit Log Luminance encoding", /* 34676 */ - "SGI 24-bit Log Luminance encoding", /* 34677 */ - "JPEG 2000", /* 34712 */ - "JPEG2000 Lossy", /* 34713 non standard !!! */ - "JPEG2000 Lossless", /* 34714 non standard !!! */ - "LZMA" /* 34925 */ - }; - /** Index for 6.1.3.1 compression scheme value labels. */ - public static final int COMPRESSION_JPEG2000_LOSSY = 34713; - public static final int COMPRESSION_JPEG2000_LOSSLESS = 34714; - public static final int [] COMPRESSION_SCHEME_INDEX = { - 1, 2, 3, 4, - 5, 6, 7, 8, - 32661, - 32771, - 32773, 32766, 32809, - 32895, - 32896, 32897, 32898, - 32908, - 32909, - 32946, - 32947, - 34676, - 34677, - 34712, - 34713, /* non standard !!! */ - 34714, /* non standard !!! */ - 34925 - }; - - /** 6.2.5 display orientation value labels. */ - public static final String [] DISPLAY_ORIENTATION = { - "portrait", "landscape" - }; - - public static final String [] EXTRA_SAMPLES = { - "unspecified", "associated alpha", "unassociated alpha", - "range or depth" - }; - - public static final String [] EXPOSURE_PROGRAM = { - "Not defined", " Manual", "Normal program", "Aperture priority", - "Shutter priority", "Creative program (biased toward depth of field)", - "Action program (biased toward fast shutter speed)", - "Portrait mode (for closeup photos with the background out of focus)", - "Landscape mode (for landscape photos with the background in focus)" - }; - - /** 7.7.3.10 flash value labels. */ - public static final String [] FLASH = { - NO, YES - }; - public static final String [] FLASH_20 = { - "Flash did not fire", "Flash fired" - }; - - /** 7.7.3.12 flash return value labels. */ - public static final String [] FLASH_RETURN = { - YES, NO - }; - - /** 8.2.6 gray response unit value labels for version 0.2. */ - public static final String [] GRAY_RESPONSE_UNIT_02 = { - "", "tenths of a unit", "hundredths of a unit", - "thousandths of a unit", "ten-thousandths of a unit", - "hundred-thousandths of a unit" - }; - - /** Gray response unit value for version 2.0 of MIX, corresponding - * to NISO values of 1-5 */ - public static final String [] GRAY_RESPONSE_UNIT_20 = { - "Number represents tenths of a unit", - "Number represents hundredths of a unit", - "Number represents thousandths of a unit", - "Number represents ten-thousandths of a unit", - "Number represents hundred-thousandths of a unit" - }; - - /** extra sample value for version 2.0 of MIX, corresponding - * to NISO values of 0-3 **/ - public static final String [] EXTRA_SAMPLE_20 = { - "unspecified data", - "associated alpha data (with pre-multiplied color)", - "unassociated alpha data", - "range or depth data" - }; - - /** 7.7.3.6 metering mode value labels. */ - public static final String [] METERING_MODE = { - "unidentified", "average", "center weighted average", "spot", - "multispot", "pattern", "partial" - }; - - /** 6.2.4 orientation value labels. */ - public static final String [] ORIENTATION = { - "", "normal", "reflected horiz", "rotated 180 deg", "reflected vert", - "left top", "rotated cw 90 deg", "Right bottom", "Rotated ccw 90 deg", - "Unknown" - }; - - /** 6.1.6 planar configuration value labels. */ - public static final String [] PLANAR_CONFIGURATION = { - "", "chunky", "planar" - }; - - /** 8.1.1 sampling frequency plane value labels. */ - public static final String [] SAMPLING_FREQUENCY_PLANE = { - "", "camera/scanner focal plane", "object plane", "source object plane" - }; - - /** 8.1.2 sampling frequency unit value labels. */ - public static final String [] SAMPLING_FREQUENCY_UNIT = { - "", "no absolute unit", "inch", "centimeter" - }; - - /** 7.7.3.7 scene illuminant value labels. */ - public static final String [] SCENE_ILLUMINANT = { - "unidentified", "daylight", "fluorescent", "tungsten lamp", - "flash", "standard illuminant A", "standard illuminat B", - "standard illuminant C", "D55 illuminant", "D65 illuminant", - "D75 illuminant" - }; - /** Index for 7.7.3.7 scene illuminant value labels. */ - public static final int [] SCENE_ILLUMINANT_INDEX = { - 0, 1, 2, 3, 10, 17, 18, 19, 20, 21, 22 - }; - - /** 6.1.5.1 segment type value labels. */ - public static final String [] SEGMENT_TYPE = { - "strips", "tiles" - }; - - /** 7.8 sensor value labels. */ - public static final String [] SENSOR = { - "Undefined", "MonochromeArea", "OneChipColorArea", "TwoChipColorArea", - "ThreeChipColorArea", "ColorSequentialArea", "MonochromeLinear", - "ColorTriLinear", "ColorSequentialLinear" - }; - - /** 8.1.7.1 (8.1.8.1) source dimension unit. */ - public static final String [] SOURCE_DIMENSION_UNIT = { - "inches", "mm" - }; - - /** 6.1.4.4 YCbCr positioning value labels. */ - public static final String [] YCBCR_POSITIONING = { - "", "centered", "cosited" - }; - - /** 8.3.1 TargetType. */ - public static final String [] TARGET_TYPE = { - "external", "internal" - }; - - /** Undefined value. */ - public static final int NULL = -1; - public static final double NILL = -1.0; - - /****************************************************************** - * PRIVATE INSTANCE FIELDS. - * - * 6 Basic image parameters - ******************************************************************/ - - /** 6.1.1 MIME type */ - private String _mimeType; - - /** 6.1.2 Byte order */ - private String _byteOrder; - - /** 6.1.3.1 Compression scheme */ - private int _compressionScheme; - /** 6.1.3.2 Compression level */ - private int _compressionLevel; - - /** 6.1.4.1 Color space */ - private int _colorSpace; - - /** 6.1.4.2.1 ICC profile name */ - private String _profileName; - /** 6.1.4.2.2 ICC profile url */ - private String _profileURL; - - /** 6.1.4.3 YCbCr sub-sampling */ - private int [] _yCbCrSubSampling; - /** 6.1.4.4 YCbCr positioning */ - private int _yCbCrPositioning; - /** 6.1.4.5 YCbCr coefficients */ - private Rational [] _yCbCrCoefficients; - /** 6.1.4.6 Reference black and white */ - private Rational [] _referenceBlackWhite; - - /** 6.1.5.1 Segment type */ - private int _segmentType; - /** 6.1.5.2 Strip offsets */ - private long [] _stripOffsets; - /** 6.1.5.3 Rows per strip */ - private long _rowsPerStrip; - /** 6.1.5.4 Strip byte counts */ - private long [] _stripByteCounts; - /** 6.1.5.5 Tile width */ - private long _tileWidth; - /** 6.1.5.6 Tile length */ - private long _tileLength; - /** 6.1.5.7 Tile offsets */ - private long [] _tileOffsets; - /** 6.1.5.8 Tile byte counts */ - private long [] _tileByteCounts; - - /** 6.1.6 Planar configuration */ - private int _planarConfiguration; - - /** 6.2.1 Image identifier */ - private String _imageIdentifier; - /** 6.2.1.1 Image identifier location */ - private String _imageIdentifierLocation; - - /** 6.2.2 File size */ - private long _fileSize; - /** 6.2.3.1 Checksum method */ - private int _checksumMethod; - /** 6.2.3.2 Checksum value */ - private String _checksumValue; - - /** 6.2.4 orientation */ - private int _orientation; - - /** 6.2.5 Display orientation */ - private int _displayOrientation; - - /** 6.2.6.1 X targeted display aspect ratio */ - private long _xTargetedDisplayAR; - /** 6.2.6.2 Y targeted display aspect ratio */ - private long _yTargetedDisplayAR; - - /** 6.3 Preferred presentation */ - private String _preferredPresentation; - - /****************************************************************** - * Special Format Characteristics - * From Data Dictionary - Technical Metadata for Digital Still Images - * (ANSI/NISO Z39.87-2006) - * Only used for JPEG2000 format - ******************************************************************/ - /** 7.2.1.2.1 tiles */ - private String _jp2Tiles; - /** 7.2.1.2.2 qualityLayers */ - private int _jp2Layers; - /** 7.2.1.2.3resolutionLevels */ - private int _jp2ResolutionLevels; - - /****************************************************************** - * 7 Image creation - ******************************************************************/ - - /** 7.1 Source type */ - private String _sourceType; - - /** 7.2 Source ID */ - private String _sourceID; - - /** 7.3 Image producer */ - private String _imageProducer; - - /** 7.4 Host computer */ - private String _hostComputer; - /** 7.4.1 Operating system */ - private String _os; - /** 7.4.2 OS version */ - private String _osVersion; - - /** 7.5 Device source */ - private String _deviceSource; - - /** 7.6.1.1 Scanner system manufacturer */ - private String _scannerManufacturer; - /** 7.6.1.2.1 Scanner model name */ - private String _scannerModelName; - /** 7.6.1.2.2 Scanner model number */ - private String _scannerModelNumber; - /** 7.6.1.2.3 Scanner model serial number */ - private String _scannerModelSerialNo; - /** 7.6.2.1 Scanning software */ - private String _scanningSoftware; - /** 7.6.2.2 Scanning software version number */ - private String _scanningSoftwareVersionNo; - - /** 7.6.3 Pixel size (in meters) */ - private double _pixelSize; - - /** 7.6.3.2.1 X physical scan resolution */ - private double _xPhysScanResolution; - /** 7.6.3.2.2 Y physical scan resolution */ - private double _yPhysScanResolution; - - /** 7.7.1 Digital camera manufacturer */ - private String _digitalCameraManufacturer; - /** 7.7.2 Digital camera model name */ - private String _digitalCameraModelName; - /** 7.7.2 Digital camera model number */ - private String _digitalCameraModelNumber; - /** 7.7.2 Digital camera model serial number*/ - private String _digitalCameraModelSerialNo; - - /** 7.7.3.1 F number */ - private double _fNumber; - /** 7.7.3.2 Exposure time */ - private double _exposureTime; - - private int _exposureProgram; - private String _exifVersion; - private Rational _maxApertureValue; - - /** 7.7.3.3 Brightness */ - private Rational _brightness; - /** 7.7.3.4 Exposure bias */ - private Rational _exposureBias; - /** 7.7.3.5 Subject distance */ - private double [] _subjectDistance; - /** 7.7.3.6 Metering mode */ - private int _meteringMode; - /** 7.7.3.7 Scene illuminant */ - private int _sceneIlluminant; - /** 7.7.3.8 Color temperature */ - private double _colorTemp; - /** 7.7.3.9 Focal length (in meters) */ - private double _focalLength; - /** 7.7.3.10 Flash */ - private int _flash; - /** 7.7.3.11 Flash energy */ - private Rational _flashEnergy; - /** 7.7.3.12 Flash return */ - private int _flashReturn; - /** 7.7.3.13 Back light */ - private int _backLight; - /** 7.7.3.14 Exposure index */ - private double _exposureIndex; - /** 7.7.3.15 Auto focus */ - private int _autoFocus; - /** 7.7.3.16.1 X print aspect ratio */ - private double _xPrintAspectRatio; - /** 7.7.3.16.2 Y print aspect ratio */ - private double _yPrintAspectRatio; - - /** 7.8 Sensor */ - private int _sensor; - - /** 7.9 Date/time created */ - private String _dateTimeCreated; - - /** 7.10 Methodology */ - private String _methodology; - - /****************************************************************** - * Imaging performance assessment - ******************************************************************/ - - /** 8.1.1 Sampling frequency plane */ - private int _samplingFrequencyPlane; - /** 8.1.2 Sampling frequency unit */ - private int _samplingFrequencyUnit; - /** 8.1.3 X sampling frequency */ - private Rational _xSamplingFrequency; - /** 8.1.4 Y sampling frequency */ - private Rational _ySamplingFrequency; - /** 8.1.5 Image width */ - private long _imageWidth; - /** 8.1.6 Image Length */ - private long _imageLength; - /** 8.1.7 Source X dimension */ - private double _sourceXDimension; - /** 8.1.8 Source X dimension unit */ - private int _sourceXDimensionUnit; - /** 8.1.9 Source Y dimension */ - private double _sourceYDimension; - /** 8.1.10 Source Y dimension unit */ - private int _sourceYDimensionUnit; - - /** 8.2.1 Bits per sample */ - private int [] _bitsPerSample; - /** 8.2.2 Samples per pixel */ - private int _samplesPerPixel; - /** 8.2.3 Extra samples */ - private int [] _extraSamples; - - /** 8.2.4.1 Colormap reference */ - private String _colormapReference; - /** 8.2.4.2 Colormap bit code value */ - private int [] _colormapBitCodeValue; - /** 8.2.4.3 Colormap red value */ - private int [] _colormapRedValue; - /** 8.2.4.4 Colormap green value */ - private int [] _colormapGreenValue; - /** 8.2.4.5 Colormap blue value */ - private int [] _colormapBlueValue; - - /** 8.2.5 Gray response curve */ - private int [] _grayResponseCurve; - /** 8.2.6 Gray response unit */ - private int _grayResponseUnit; - - /** 8.2.7.1 Whitepoint X value */ - private Rational _whitePointXValue; - /** 8.2.7.2 Whitepoint Y value */ - private Rational _whitePointYValue; - - /** 8.2.8.1 Primary chromaticities Red X */ - private Rational _primaryChromaticitiesRedX; - /** 8.2.8.2 Primary chromaticities Red Y */ - private Rational _primaryChromaticitiesRedY; - /** 8.2.8.3 Primary chromaticities Green X */ - private Rational _primaryChromaticitiesGreenX; - /** 8.2.8.4 Primary chromaticities Green Y */ - private Rational _primaryChromaticitiesGreenY; - /** 8.2.8.5 Primary chromaticities Blue X */ - private Rational _primaryChromaticitiesBlueX; - /** 8.2.8.6 Primary chromaticities Blue Y */ - private Rational _primaryChromaticitiesBlueY; - - /* 8.3 Target data */ - /** 8.3.1 Target Type */ - private int _targetType; - /** 8.3.2.1 TargetIDManufacturer */ - private String _targetIDManufacturer; - /** 8.3.2.2 TargetIDName */ - private String _targetIDName; - /** 8.3.2.3 TargetIDNo */ - private String _targetIDNo; - /** 8.3.2.4 TargetIDMedia */ - private String _targetIDMedia; - /** 8.3.3 ImageData */ - private String _imageData; - /** 8.3.4 PerformanceData */ - private String _performanceData; - /** 8.3.5 Profiles */ - private String _profiles; - - /* 9 Change history */ - /** 9.1.1 DateTimeProcessed */ - private String _dateTimeProcessed; - /** 9.1.2 SourceData */ - private String _sourceData; - /** 9.1.3 ProcessingAgency */ - private String _processingAgency; - /** 9.1.4.1 ProcessingSoftwareName */ - private String _processingSoftwareName; - /** 9.1.4.2 ProcessingSoftwareVersion */ - private String _processingSoftwareVersion; - /** 9.1.5 ProcessingActions */ - private String[] _processingActions; - - /* 9.2 PreviousImageMetadata -- not currently supported */ - - /* Data for Swing-based viewer. */ - private Property _viewerData; - - /****************************************************************** - * CLASS CONSTRUCTOR. - ******************************************************************/ - - /** Instantiate a NisoImageMetadata object. - */ - public NisoImageMetadata () - { - _autoFocus = NULL; - _backLight = NULL; - _brightness = null; - _checksumMethod = NULL; - _colorSpace = NULL; - _colorTemp = NILL; - _compressionLevel = NULL; - _compressionScheme = NULL; - _dateTimeProcessed = null; - _digitalCameraManufacturer = null; - _digitalCameraModelName = null; - _digitalCameraModelNumber = null; - _digitalCameraModelSerialNo = null; - _displayOrientation = NULL; - _exifVersion = null; - _exposureBias = null; - _exposureIndex = NILL; - _exposureProgram = NULL; - _exposureTime = NILL; - _fileSize = NULL; - _flash = NULL; - _flashEnergy = null; - _flashReturn = NULL; - _fNumber = NILL; - _focalLength = NILL; - _grayResponseUnit = NULL; - _imageData = null; - _imageLength = NULL; - _imageWidth = NULL; - _jp2Layers = NULL; - _jp2ResolutionLevels = NULL; - _jp2Tiles = null; - _maxApertureValue = null; - _meteringMode = NULL; - _orientation = NULL; - _performanceData = null; - _pixelSize = NILL; - _planarConfiguration = NULL; - _processingActions = null; - _processingAgency = null; - _processingSoftwareName = null; - _processingSoftwareVersion = null; - _profiles = null; - _rowsPerStrip = NULL; - _scannerManufacturer = null; - _scannerModelName = null; - _scannerModelNumber = null; - _scannerModelSerialNo = null; - _samplesPerPixel = NULL; - _samplingFrequencyPlane = NULL; - _samplingFrequencyUnit = NULL; - _sceneIlluminant = NULL; - _segmentType = NULL; - _sensor = NULL; - _sourceData = null; - _sourceXDimension = NILL; - _sourceXDimensionUnit = NULL; - _sourceYDimension = NILL; - _sourceYDimensionUnit = NULL; - _tileLength = NULL; - _tileWidth = NULL; - _targetIDManufacturer = null; - _targetIDMedia = null; - _targetIDName = null; - _targetIDNo = null; - _targetType = NULL; - _xPhysScanResolution = NILL; - _xPrintAspectRatio = NILL; - _xSamplingFrequency = null; - _xTargetedDisplayAR = NULL; - _yCbCrPositioning = NULL; - _yPhysScanResolution = NILL; - _yPrintAspectRatio = NILL; - _ySamplingFrequency = null; - _yTargetedDisplayAR = NULL; - _viewerData = null; - } - - /****************************************************************** - * PUBLIC INSTANCE METHODS. - * - * Accessor methods. - ******************************************************************/ - - /** Get 7.7.3.15 auto focus. */ - public int getAutoFocus () - { - return _autoFocus; - } - - /** Get 7.7.3.13 back light. */ - public int getBackLight () - { - return _backLight; - } - - /** Get 8.2.1 bits per sample. */ - public int [] getBitsPerSample () - { - return _bitsPerSample; - } - - /** Get 7.7.3.3 Brightness. */ - public Rational getBrightness () - { - return _brightness; - } - - /** Get 6.1.2 byte order. */ - public String getByteOrder () - { - return _byteOrder; - } - - /** Get 6.2.3.1 Checksum method. */ - public int getChecksumMethod () - { - return _checksumMethod; - } - - /** Get 6.2.3.2 Checksum value. */ - public String getChecksumValue () - { - return _checksumValue; - } - - /** Get 8.2.4.2 colormap bit code value. */ - public int [] getColormapBitCodeValue () - { - return _colormapBitCodeValue; - } - - /** Get 8.2.4.5 colormap blue value. */ - public int [] getColormapBlueValue () - { - return _colormapBlueValue; - } - - /** Get 8.2.4.4 colormap green value. */ - public int [] getColormapGreenValue () - { - return _colormapGreenValue; - } - - /** Get 8.2.4.3 colormap red value. */ - public int [] getColormapRedValue () - { - return _colormapRedValue; - } - - /** Get 8.2.4.1 colormap reference. */ - public String getColormapReference () - { - return _colormapReference; - } - - /** Get 6.1.4.1 color space. */ - public int getColorSpace () - { - return _colorSpace; - } - - /** Get 7.7.3.8 color temperature. */ - public double getColorTemp () - { - return _colorTemp; - } - - /** Get 6.1.3.2 compression level. */ - public int getCompressionLevel () - { - return _compressionLevel; - } - - /** Get 6.1.3.1 compression scheme. */ - public int getCompressionScheme () - { - return _compressionScheme; - } - - /** Get 7.9 date/time created. */ - public String getDateTimeCreated () - { - return _dateTimeCreated; - } - - /** Get 9.1.1 DateTimeProcessed */ - public String getDateTimeProcessed () - { - return _dateTimeProcessed; - } - - /** Get 7.5 device source. */ - public String getDeviceSource () - { - return _deviceSource; - } - - /** Get 7.7.1 digital camera manufacturer. */ - public String getDigitalCameraManufacturer () - { - return _digitalCameraManufacturer; - } - - /** Get 7.7.2 digital camera model. */ - public String getDigitalCameraModelName () - { - return _digitalCameraModelName; - } - public String getDigitalCameraModelNumber () - { - return _digitalCameraModelNumber; - } - public String getDigitalCameraModelSerialNo() - { - return _digitalCameraModelSerialNo; - } - - /** Get 6.2.5 Display orientation. */ - public int getDisplayOrientation () - { - return _displayOrientation; - } - - public String getExifVersion () - { - return _exifVersion; - } - - /** Get 7.7.3.4 exposure bias. */ - public Rational getExposureBias () - { - return _exposureBias; - } - - /** Get 7.7.3.14 exposure index. */ - public double getExposureIndex () - { - return _exposureIndex; - } - - public int getExposureProgram () - { - return _exposureProgram; - } - - /** Get 7.7.3.2 exposure time. */ - public double getExposureTime () - { - return _exposureTime; - } - - /** Get 8.2.3 extra samples. */ - public int [] getExtraSamples () - { - return _extraSamples; - } - - /** Get 6.2.2 file size. */ - public long getFileSize () - { - return _fileSize; - } - - /** Get 7.7.3.10 flash. */ - public int getFlash () - { - return _flash; - } - - /** Get 7.7.3.11 flash energy. */ - public Rational getFlashEnergy () - { - return _flashEnergy; - } - - /** Get 7.7.3.12 flash return. */ - public int getFlashReturn () - { - return _flashReturn; - } - - /** Get 7.7.3.1 F number. */ - public double getFNumber () - { - return _fNumber; - } - - /** Get 7.7.3.9 focal length. */ - public double getFocalLength () - { - return _focalLength; - } - - /** Get 8.2.5 gray response curve. */ - public int [] getGrayResponseCurve () - { - return _grayResponseCurve; - } - - /** Get 8.2.6 gray response unit. */ - public int getGrayResponseUnit () - { - return _grayResponseUnit; - } - - /** Get 7.4 host computer. */ - public String getHostComputer () - { - return _hostComputer; - } - - /** Get 8.3.3 ImageData */ - public String getImageData () - { - return _imageData; - } - - /** Get 6.2.1 Image identifier. */ - public String getImageIdentifier () - { - return _imageIdentifier; - } - - /** Get 6.2.1.1 Image identifier location. */ - public String getImageIdentifierLocation () - { - return _imageIdentifierLocation; - } - - /** Get 8.1.6 image length. */ - public long getImageLength () - { - return _imageLength; - } - - /** Get 7.3 Image producer. */ - public String getImageProducer () - { - return _imageProducer; - } - - /** Get 8.1.5 image width. */ - public long getImageWidth () - { - return _imageWidth; - } - - public Rational getMaxApertureValue () - { - return _maxApertureValue; - } - - /** Get 7.7.3.6 metering mode. */ - public int getMeteringMode () - { - return _meteringMode; - } - - /** Get 7.10 methodology. */ - public String getMethodology () - { - return _methodology; - } - - /** Get 6.1.1 MIME type. */ - public String getMimeType () - { - return _mimeType; - } - - /** Get 6.2.4 Orientation. */ - public int getOrientation () - { - return _orientation; - } - - /** Get 7.4.1 OS (operating system). */ - public String getOS () - { - return _os; - } - - /** Get 7.4.2 OS version. */ - public String getOSVersion () - { - return _osVersion; - } - - /** Get 8.3.4 PerformanceData. */ - public String getPerformanceData () - { - return _performanceData; - } - - /** Get 7.6.3.1 pixel size. */ - public double getPixelSize () - { - return _pixelSize; - } - - /** Get 6.1.6 Planar configuration. */ - public int getPlanarConfiguration () - { - return _planarConfiguration; - } - - /** Get 6.3 preferred presentation. */ - public String getPreferredPresentation () - { - return _preferredPresentation; - } - - public String getJp2Tiles() { - return _jp2Tiles; - } - - public void setJp2Tiles(String jp2Tiles) { - this._jp2Tiles = jp2Tiles; - } - - public int getJp2Layers() { - return _jp2Layers; - } - - public void setJp2Layers(int jp2Layers) { - this._jp2Layers = jp2Layers; - } - - public int getJp2ResolutionLevels() { - return _jp2ResolutionLevels; - } - - public void setJp2ResolutionLevels(int jp2ResolutionLevels) { - this._jp2ResolutionLevels = jp2ResolutionLevels; - } - - /** Get 8.2.8.5 primary chromaticities blue X. */ - public Rational getPrimaryChromaticitiesBlueX () - { - return _primaryChromaticitiesBlueX; - } - - /** Get 8.2.8.6 primary chromaticities blue Y. */ - public Rational getPrimaryChromaticitiesBlueY () - { - return _primaryChromaticitiesBlueY; - } - - /** Get 8.2.8.3 primary chromaticities green X. */ - public Rational getPrimaryChromaticitiesGreenX () - { - return _primaryChromaticitiesGreenX; - } - - /** Get 8.2.8.4 primary chromaticities green Y. */ - public Rational getPrimaryChromaticitiesGreenY () - { - return _primaryChromaticitiesGreenY; - } - - /** Get 8.2.8.1 primary chromaticities red X. */ - public Rational getPrimaryChromaticitiesRedX () - { - return _primaryChromaticitiesRedX; - } - - /** Get 8.2.8.2 primary chromaticities red Y. */ - public Rational getPrimaryChromaticitiesRedY () - { - return _primaryChromaticitiesRedY; - } - - /** Get 9.1.5 ProcessingActions. */ - public String[] getProcessingActions () - { - return _processingActions; - } - - /** Get 9.1.3 ProcessingAgency. */ - public String getProcessingAgency () - { - return _processingAgency; - } - - /** Get 9.1.4.1 ProcessingSoftwareName */ - public String getProcessingSoftwareName () - { - return _processingSoftwareName; - } - - /** Get 9.1.4.2 ProcessingSoftwareVersion */ - public String getProcessingSoftwareVersion () - { - return _processingSoftwareVersion; - } - - /** Get 6.1.4.2.1 ICC profile name. */ - public String getProfileName () - { - return _profileName; - } - - /** Get 8.3.5 Profiles */ - public String getProfiles () - { - return _profiles; - } - - /** Get 6.1.4.2.2 ICC profile URL. */ - public String getProfileURL () - { - return _profileURL; - } - - /** Get 6.1.4.6 Reference black and white. */ - public Rational [] getReferenceBlackWhite () - { - return _referenceBlackWhite; - } - - /** Get 6.1.5.3 Rows per strip. */ - public long getRowsPerStrip () - { - return _rowsPerStrip; - } - - /** Get 8.2.2 samples per pixel. */ - public int getSamplesPerPixel () - { - return _samplesPerPixel; - } - - /** Get 8.1.1 sampling frequency plane. */ - public int getSamplingFrequencyPlane () - { - return _samplingFrequencyPlane; - } - - /** Get 8.1.2 sampling frequency unit. */ - public int getSamplingFrequencyUnit () - { - return _samplingFrequencyUnit; - } - - /** Get 7.6.1.1 scanner manufacturer. */ - public String getScannerManufacturer () - { - return _scannerManufacturer; - } - - /** Get 7.6.1.2.1 scanner model name. */ - public String getScannerModelName () - { - return _scannerModelName; - } - - /** Get 7.6.1.2.2 scanner model number. */ - public String getScannerModelNumber () - { - return _scannerModelNumber; - } - - /** Get 7.6.1.2.3 scanner model serial number. */ - public String getScannerModelSerialNo () - { - return _scannerModelSerialNo; - } - - /** Get 7.6.2.1 scanning software. */ - public String getScanningSoftware () - { - return _scanningSoftware; - } - - /** Get 7.6.2.2 scanning software version number. */ - public String getScanningSoftwareVersionNo () - { - return _scanningSoftwareVersionNo; - } - - /** Get 7.7.3.7 scene illuminant. */ - public int getSceneIlluminant () - { - return _sceneIlluminant; - } - - /** Get 6.1.5.1 segment type. */ - public int getSegmentType () - { - return _segmentType; - } - - /** Get 7.8 sensor. */ - public int getSensor () - { - return _sensor; - } - - /** Get 9.1.2 SourceData. */ - public String getSourceData () - { - return _sourceData; - } - - /** Get 7.2 source ID. */ - public String getSourceID () - { - return _sourceID; - } - - /** Get 7.1 Source type. */ - public String getSourceType () - { - return _sourceType; - } - - public double getSourceXDimension () - { - return _sourceXDimension; - } - - public int getSourceXDimensionUnit () - { - return _sourceXDimensionUnit; - } - - public double getSourceYDimension () - { - return _sourceYDimension; - } - - public int getSourceYDimensionUnit () - { - return _sourceYDimensionUnit; - } - - /** Get 6.1.5.4 Strip byte counts. */ - public long [] getStripByteCounts () - { - return _stripByteCounts; - } - - /** Get 6.1.5.2 Strip offsets. */ - public long [] getStripOffsets () - { - return _stripOffsets; - } - - /** Get 7.7.3.5 Subject distance. */ - public double [] getSubjectDistance () - { - return _subjectDistance; - } - - /** Get 8.3.2.1 TargetIDManufacturer */ - public String getTargetIDManufacturer () - { - return _targetIDManufacturer; - } - - /** Get 8.3.2.3 TargetIDMedia */ - public String getTargetIDMedia () - { - return _targetIDMedia; - } - - /** Get 8.3.2.2 TargetIDName */ - public String getTargetIDName () - { - return _targetIDName; - } - - /** Get 8.3.2.3 TargetIDNo */ - public String getTargetIDNo () - { - return _targetIDNo; - } - - /** Get 8.3.1 Target Type */ - public int getTargetType () - { - return _targetType; - } - - /** Get 6.1.5.8 Tile byte counts. */ - public long [] getTileByteCounts () - { - return _tileByteCounts; - } - - /** Get 6.1.5.6 Tile length. */ - public long getTileLength () - { - return _tileLength; - } - - /** Get 6.1.5.7 Tile offsets. */ - public long [] getTileOffsets () - { - return _tileOffsets; - } - - /** Get 6.1.5.5 Tile width. */ - public long getTileWidth () - { - return _tileWidth; - } - - /** Get 8.2.7.1 white point X value. */ - public Rational getWhitePointXValue () - { - return _whitePointXValue; - } - - /** Get 8.2.7.2 white point Y value. */ - public Rational getWhitePointYValue () - { - return _whitePointYValue; - } - - /** Get 7.7.3.16.1 X print aspect ratio. */ - public double getXPrintAspectRatio () - { - return _xPrintAspectRatio; - } - - /** Get 7.6.3.2.1 X physcal scanning resolution. */ - public double getXPhysScanResolution () - { - return _xPhysScanResolution; - } - - /** Get 8.1.3 X sampling frequency. */ - public Rational getXSamplingFrequency () - { - return _xSamplingFrequency; - } - - /** Get 6.2.6 X targeted display aspect ratio. */ - public long getXTargetedDisplayAR () - { - return _xTargetedDisplayAR; - } - - /** Get 6.1.4.5 YCbCr coefficients. */ - public Rational [] getYCbCrCoefficients () - { - return _yCbCrCoefficients; - } - - /** Get 6.1.4.4 YCbCr positioning. */ - public int getYCbCrPositioning () - { - return _yCbCrPositioning; - } - - /** Get 6.1.4.3 YCbCr subsampling. */ - public int [] getYCbCrSubSampling () - { - return _yCbCrSubSampling; - } - - /** Get 7.6.3.2.2 Y physcal scanning resolution. */ - public double getYPhysScanResolution () - { - return _yPhysScanResolution; - } - - /** Get 7.7.3.16.2 Y print aspect ratio. */ - public double getYPrintAspectRatio () - { - return _yPrintAspectRatio; - } - - /** Get 8.1.4 Y sampling frequency. */ - public Rational getYSamplingFrequency () - { - return _ySamplingFrequency; - } - - /** Get 6.2.7 Y targeted display aspect ratio. */ - public long getYTargetedDisplayAR () - { - return _yTargetedDisplayAR; - } - - /** Get data for Swing GUI viewer. */ - public Property getViewerData () - { - return _viewerData; - } - - - /****************************************************************** - * Mutator methods. - ******************************************************************/ - - /** Set 7.7.3.15 auto focus. - * @param focus Auto focus - */ - public void setAutoFocus (int focus) - { - _autoFocus = focus; - } - - /** Set 7.7.3.13 back light. - * @param light Back light - */ - public void setBackLight (int light) - { - _backLight = light; - } - - /** Set 8.2.1 bits per sample. - * @param bits Bits per sample - */ - public void setBitsPerSample (int [] bits) - { - _bitsPerSample = bits; - } - - /** Set 7.7.3.3 brightness. - * @param brightness Brightness - */ - public void setBrightness (Rational brightness) - { - _brightness = brightness; - } - - /** Set 6.1.2 byte order. - * @param order Byte order - */ - public void setByteOrder (String order) - { - _byteOrder = order; - } - - /** Set 8.2.4.2 colormap bit code value. - * @param value Bit code value - */ - public void setColormapBitCodeValue (int [] value) - { - _colormapBitCodeValue = value; - } - - /** Set 8.2.4.4 colormap blue value. - * @param value Blue value - */ - public void setColormapBlueValue (int [] value) - { - _colormapBlueValue = value; - } - - /** Set 8.2.4.3 colormap green value. - * @param value Green value - */ - public void setColormapGreenValue (int [] value) - { - _colormapGreenValue = value; - } - - /** Set 8.2.4.2 colormap red value. - * @param value Red value - */ - public void setColormapRedValue (int [] value) - { - _colormapRedValue = value; - } - - /** Set 8.2.4.1 colormap reference. - * @param reference Colormap reference - */ - public void setColormapReference (String reference) - { - _colormapReference = reference; - } - - /** Set 6.1.4.1 color space - * @param space Color space - */ - public void setColorSpace (int space) - { - _colorSpace = space; - } - - /** Set 7.7.3.8 color temperature. - * @param temp Color temperature - */ - public void setColorTemp (double temp) - { - _colorTemp = temp; - } - - /** Set 6.1.3.2 compression level. - * @param level Compression level - */ - public void setCompressionLevel (int level) - { - _compressionLevel = level; - } - - /** Set 6.1.3.1 compression scheme. - * @param scheme Compression scheme - */ - public void setCompressionScheme (int scheme) - { - _compressionScheme = scheme; - } - - /** Set 7.9 date/time created. - * TIFF dates get converted to ISO 8601 format. - * @param date Date/time created - */ - public void setDateTimeCreated (String date) - { - _dateTimeCreated = make8601Valid (date); - } - - /** Set 9.1.1 DateTimeProcessed. - * TIFF dates get converted to ISO 8601 format. - * @param date Date/time processed - */ - public void setDateTimeProcessed (String date) - { - _dateTimeProcessed = make8601Valid (date); - } - - /** Set 7.5 Device source. - * @param source Device source - */ - public void setDeviceSource (String source) - { - _deviceSource = source; - } - - /** Set 7.7.1 digital camera manufacturer. - * @param manufacturer Camera manufacturer - */ - public void setDigitalCameraManufacturer (String manufacturer) - { - _digitalCameraManufacturer = manufacturer; - } - - /** Set 7.7.2 digital camera model. - * @param modelName Camera model - */ - public void setDigitalCameraModelName (String modelName) - { - _digitalCameraModelName = modelName; - } - public void setDigitalCameraModelNumber (String modelNumber) - { - _digitalCameraModelNumber = modelNumber; - } - public void setDigitalCameraModelSerialNo (String modelSerialNo) - { - _digitalCameraModelSerialNo = modelSerialNo; - } - - /** Set 6.2.5 display orientation. - * @param orientation Display orientation - */ - public void setDisplayOrientation (int orientation) - { - _displayOrientation = orientation; - } - - public void setExifVersion (String version) - { - _exifVersion = version; - } - - /** Set 7.2.3.4 exposure bias. - * @param bias Exposure bias - */ - public void setExposureBias (Rational bias) - { - _exposureBias = bias; - } - - /** Set 7.2.3.14 exposure index. - * @param index Exposure index - */ - public void setExposureIndex (double index) - { - _exposureIndex = index; - } - - public void setExposureProgram (int program) - { - _exposureProgram = program; - } - - /** Set 7.7.3.2 exposure time. - * @param time Exposure time - */ - public void setExposureTime (double time) - { - _exposureTime = time; - } - - /** Set 8.2.3 extra samples. - * @param extra Extra samples - */ - public void setExtraSamples (int [] extra) - { - _extraSamples = extra; - } - - /** Set 6.2.2 file size. - * @param size File size - */ - public void setFileSize (long size) - { - _fileSize = size; - } - - /** Set 7.7.3.1 F number. - * @param f F number - */ - public void setFNumber (double f) - { - _fNumber = f; - } - - /** Set 7.7.3.11 flash energy. - * @param energy Flash energy - */ - public void setFlashEnergy (Rational energy) - { - _flashEnergy = energy; - } - - /** Set 7.7.3.12 flash return. - * @param ret Flash return - */ - public void setFlashReturn (int ret) - { - _flashReturn = ret; - } - - /** Set 7.7.3.10 flash. - * @param flash Flash - */ - public void setFlash (int flash) - { - _flash = flash; - } - - /** Set 7.7.3.9 focal length (double meters). - * @param length Focal length - */ - public void setFocalLength (double length) - { - _focalLength = length; - } - - /** Set 8.2.5 gray response curve. - * @param curve Gray response curve - */ - public void setGrayResponseCurve (int [] curve) - { - _grayResponseCurve = curve; - } - - /** Set 8.2.6 gray response unit. - * @param unit Gray response unit - */ - public void setGrayResponseUnit (int unit) - { - _grayResponseUnit = unit; - } - - /** Set 7.4 host computer. - * @param computer Host computer - */ - public void setHostComputer (String computer) - { - _hostComputer = computer; - } - - /** Set 8.3.3 ImageData. - * @param imageData Image Data filename or URN - */ - public void setImageData (String imageData) - { - _imageData = imageData; - } - - /** Set 6.2.1 Image identifier. - * @param identifier Image identifier - */ - public void setImageIdentifier (String identifier) - { - _imageIdentifier = identifier; - } - - /** Set 6.2.1 Image identifier location. - * @param location identifier location - */ - public void setImageIdentifierLocation (String location) - { - _imageIdentifierLocation = location; - } - - /** Set 8.1.6 image length. - * @param length Image length - */ - public void setImageLength (long length) - { - _imageLength = length; - } - - /** Set 7.3 image producer. - * @param producer Image producer - */ - public void setImageProducer (String producer) - { - _imageProducer = producer; - } - - /** Set 8.1.5 image width. - * @param width Image width - */ - public void setImageWidth (long width) - { - _imageWidth = width; - } - - public void setMaxApertureValue (Rational value) - { - _maxApertureValue = value; - } - - /** Set 7.7.3.6 metering mode. - * @param mode Metering mode - */ - public void setMeteringMode (int mode) - { - _meteringMode = mode; - } - - /** Set 7.10 methodology. - * @param methodology Methodology - */ - public void setMethodology (String methodology) - { - _methodology = methodology; - } - - /** Set 6.1.1 MIME type. - * @param type MIME type - */ - public void setMimeType (String type) - { - _mimeType = type; - } - - /** Set 6.2.4 orientation. - * @param orientation Orientation - */ - public void setOrientation (int orientation) - { - _orientation = orientation; - } - - /* Set 7.4.1 OS (operating system). - * @param os Operating system - */ - public void setOS (String os) - { - _os = os; - } - - /** Set 7.4.2 OS version. - * @param version OS version - */ - public void setOSVersion (String version) - { - _osVersion = version; - } - - /** Set 8.3.4 PerformanceData. - * @param performanceData Performance data filename or URN - */ - public void setPerformanceData (String performanceData) - { - _performanceData = performanceData; - } - - /** Set 7.6.3.1 pixel size. - * @param size Pixel size - */ - public void setPixelSize (double size) - { - _pixelSize = size; - } - - /** Set 6.1.6 Planar configuration. - * @param configuration Planar configuration - */ - public void setPlanarConfiguration (int configuration) - { - _planarConfiguration = configuration; - } - - /** Set 6.3 preferred presentation. - * @param presentation Preferred presentation - */ - public void setPreferredPresentation (String presentation) - { - _preferredPresentation = presentation; - } - - /** Set 8.2.8.5 primary chromaticities blue X. - * @param x Blue x - */ - public void setPrimaryChromaticitiesBlueX (Rational x) - { - _primaryChromaticitiesBlueX = x; - } - - /** Set 8.2.8.6 primary chromaticities blue Y. - * @param y Blue y - */ - public void setPrimaryChromaticitiesBlueY (Rational y) - { - _primaryChromaticitiesBlueY = y; - } - - /** Set 8.2.8.3 primary chromaticities green X. - * @param x Green x - */ - public void setPrimaryChromaticitiesGreenX (Rational x) - { - _primaryChromaticitiesGreenX = x; - } - - /** Set 8.2.8.4 primary chromaticities green Y. - * @param y Green y - */ - public void setPrimaryChromaticitiesGreenY (Rational y) - { - _primaryChromaticitiesGreenY = y; - } - - /** Set 8.2.8.1 primary chromaticities red X. - * @param x Red x - */ - public void setPrimaryChromaticitiesRedX (Rational x) - { - _primaryChromaticitiesRedX = x; - } - - /** Set 8.2.8.2 primary chromaticities red Y. - * @param y Red y - */ - public void setPrimaryChromaticitiesRedY (Rational y) - { - _primaryChromaticitiesRedY = y; - } - - /** Set 9.1.5 ProcessingActions. - * @param actions Array of strings giving image processing steps - */ - public void setProcessingActions (String[] actions) - { - _processingActions = actions; - } - - /** Set 9.1.3 ProcessingAgency. - * @param processingAgency Identifier of producing organization - */ - public void setProcessingAgency (String processingAgency) - { - _processingAgency = processingAgency; - } - - /** Set 9.1.4.1 ProcessingSoftwareName - * @param name Name of the image processing software - */ - public void setProcessingSoftwareName (String name) - { - _processingSoftwareName = name; - } - - /** Set 9.1.4.2 ProcessingSoftwareVersion - * @param version Version number of the processing software - */ - public void setProcessingSoftwareVersion (String version) - { - _processingSoftwareVersion = version; - } - - /** Set 6.1.4.1 ICC profile name. - * @param name Profile name - */ - public void setProfileName (String name) - { - _profileName = name; - } - - /** Set 8.3.5 Profiles. - * @param profiles Color profile filename or URN - */ - public void setProfiles (String profiles) - { - _profiles = profiles; - } - - /** Set 6.1.4.2 ICC profile URL. - * @param URL Profile URL - */ - public void setProfileURL (String URL) - { - _profileURL = URL; - } - - /** Set 6.1.4.6 reference black and white. - * @param reference Reference - */ - public void setReferenceBlackWhite (Rational [] reference) - { - _referenceBlackWhite = reference; - } - - /** Set 6.1.5.3 Rows per strip. - * @param rows Rows per strip - */ - public void setRowsPerStrip (long rows) - { - _rowsPerStrip = rows; - } - - /** Set 8.1.1 sampling frequency plane. - * @param plane Sampling frequency plane - */ - public void setSamplingFrequencyPlane (int plane) - { - _samplingFrequencyPlane = plane; - } - - /** Set 8.2.2 samples per pixel. - * @param samples Samples per pixel - */ - public void setSamplesPerPixel (int samples) - { - _samplesPerPixel = samples; - } - - /** Set 8.1.2 sampling frequency unit. - * @param unit Sampling frequency unit - */ - public void setSamplingFrequencyUnit (int unit) - { - _samplingFrequencyUnit = unit; - } - - /** Set 7.6.1.1 scanner manufacturer. - * @param manufacturer Scanner manufacturer - */ - public void setScannerManufacturer (String manufacturer) - { - _scannerManufacturer = manufacturer; - } - - /** Set 7.6.1.2.1 scanner model name. - * @param name Scanner model name - */ - public void setScannerModelName (String name) - { - _scannerModelName = name; - } - - /** Set 7.6.1.2.2 scanner model number. - * @param number Scanner model number - */ - public void setScannerModelNumber (String number) - { - _scannerModelNumber = number; - } - - /** Set 7.6.1.2.3 scanner model serial number. - * @param number Scanner model serial number - */ - public void setScannerModelSerialNo (String number) - { - _scannerModelSerialNo = number; - } - - /** Set 7.6.2.1 scanning software. - * @param software Scanning software - */ - public void setScanningSoftware (String software) - { - _scanningSoftware = software; - } - - /** Set 7.6.2.2 scanning software version number. - * @param number Scanning software version number - */ - public void setScanningSoftwareVersionNo (String number) - { - _scanningSoftwareVersionNo = number; - } - - /** Set 7.7.3.7 scene illuminant. - * @param illuminant Scene illuminant - */ - public void setSceneIlluminant (int illuminant) - { - _sceneIlluminant = illuminant; - } - - /** Set 7.8 sensor. - * @param sensor Sensor - */ - public void setSensor (int sensor) - { - _sensor = sensor; - } - - /** Set 9.1.2 SourceData. - * @param sourceData Source data identifier - */ - public void setSourceData (String sourceData) - { - _sourceData = sourceData; - } - - /** Set 7.2 source ID. - * @param id Source ID - */ - public void setSourceID (String id) - { - _sourceID = id; - } - - /** Set 7.1 source type. - * @param type Source type - */ - public void setSourceType (String type) - { - _sourceType = type; - } - - /** Set 8.1.7 source X dimension. - * @param x X dimension - */ - public void setSourceXDimension (double x) - { - _sourceXDimension = x; - } - - /** Set 8.1.7.1 source X dimension unit. - * @param unit X dimension unit - */ - public void setSourceXDimensionUnit (int unit) - { - _sourceXDimensionUnit = unit; - } - - /** Set 8.1.8 source Y dimension. - * @param y Y dimension - */ - public void setSourceYDimension (double y) - { - _sourceYDimension = y; - } - - /** Set 8.1.8.1 source Y dimension unit. - * @param unit Y dimension unit - */ - public void setSourceYDimensionUnit (int unit) - { - _sourceYDimensionUnit = unit; - } - - /** Set 6.1.5.4 Strip byte counts. - * @param counts Byte counts - */ - public void setStripByteCounts (long [] counts) - { - _stripByteCounts = counts; - } - - /** Set 6.1.5.2 Strip offsets. - * @param offsets Strip offsets - */ - public void setStripOffsets (long [] offsets) - { - _stripOffsets = offsets; - } - - /** Set 7.7.3.5 Subject distance - * @param distance Subject distance - */ - public void setSubjectDistance (double [] distance) - { - _subjectDistance = distance; - } - - /** Set 8.3.2.1 TargetIDManufacturer */ - public void setTargetIDManufacturer (String targetIDManufacturer) - { - _targetIDManufacturer = targetIDManufacturer; - } - - /** Set 8.3.2.4 TargetIDMedia */ - public void setTargetIDMedia (String targetIDMedia) - { - _targetIDMedia = targetIDMedia; - } - - /** Set 8.3.2.2 TargetIDName */ - public void setTargetIDName (String targetIDName) - { - _targetIDName = targetIDName; - } - - /** Set 8.3.2.3 TargetIDNo */ - public void setTargetIDNo (String targetIDNo) - { - _targetIDNo = targetIDNo; - } - - /** Set 8.3.1 TargetType */ - public void setTargetType (int targetType) - { - _targetType = targetType; - } - - /** Set 6.1.5.8 Tile byte counts. - * @param counts Byte counts - */ - public void setTileByteCounts (long [] counts) - { - _tileByteCounts = counts; - } - - /** Set 6.1.5.6 Tile length. - * @param length Tile length - */ - public void setTileLength (long length) - { - _tileLength = length; - } - - /** Set 6.1.5.7 Tile offsets. - * @param offsets tile offsets - */ - public void setTileOffsets (long [] offsets) - { - _tileOffsets = offsets; - } - - /** Set 6.1.5.5 Tile width. - * @param width Tile width - */ - public void setTileWidth (long width) - { - _tileWidth = width; - } - - /** Set 8.2.7.1 white point X value. - * @param x White point X - */ - public void setWhitePointXValue (Rational x) - { - _whitePointXValue = x; - } - - /** Set 8.2.7.2 white point Y value. - * @param y White point Y - */ - public void setWhitePointYValue (Rational y) - { - _whitePointYValue = y; - } - - /** Set 7.6.3.2.1 X physical scanning resolution. - * @param x X physical scanning resolution - */ - public void setXPhysScanResolution (double x) - { - _xPhysScanResolution = x; - } - - /** Set 7.7.3.16.1 X print aspect ratio. - * @param x X aspect ratio - */ - public void setXPrintAspectRatio (double x) - { - _xPrintAspectRatio = x; - } - - /** Set 8.1.3 X sampling frequency. - * @param x X sampling frequency - */ - public void setXSamplingFrequency (Rational x) - { - _xSamplingFrequency = x; - } - - /** Set 6.2.6.1 X targeted display aspect ratio. - * @param x X units - */ - public void setXTargetedDisplayAspectRatio (long x) - { - _xTargetedDisplayAR = x; - } - - /** Set 6.1.4.5 YCbCr coefficients. - * @param coefficients Coefficients - */ - public void setYCbCrCoefficients (Rational [] coefficients) - { - _yCbCrCoefficients = coefficients; - } - - /** Set 6.1.4.4 YCbCr positioning. - * @param positioning Positioning - */ - public void setYCbCrPositioning (int positioning) - { - _yCbCrPositioning = positioning; - } - - /** Set 6.1.4.3 YCbCr Sub-sampling. - * @param sampling Sub-sampling - */ - public void setYCbCrSubSampling (int [] sampling) - { - _yCbCrSubSampling = sampling; - } - - /** Set 7.6.3.2.2 Y physical scanning resolution. - * @param y Y physical scanning resolution - */ - public void setYPhysScanResolution (double y) - { - _yPhysScanResolution = y; - } - - /** Set 7.7.3.16.2 Y print aspect ratio. - * @param y Y aspect ratio - */ - public void setYPrintAspectRatio (double y) - { - _yPrintAspectRatio = y; - } - - /** Set 8.1.4 Y sampling frequency. - * @param y Y sampling frequency - */ - public void setYSamplingFrequency (Rational y) - { - _ySamplingFrequency = y; - } - - /** Set 6.2.6.2 Y targeted display aspect ratio. - * @param y Y units - */ - public void setYTargetedDisplayAspectRatio (long y) - { - _yTargetedDisplayAR = y; - } - - /** Set information for Swing GUI viewer. - * @param viewerData Private data for RepTreeModel - */ - public void setViewerData (Property viewerData) - { - _viewerData = viewerData; - } - - /* Canonicizes (canonizes? whatever) a date to ISO - * 8601 format. Returns null if it can't make sense of - * it. Returns the date unchanged if it's already - * canonical. Initially this converts TIFF dates to ISO. - */ - private String make8601Valid (String date) - { - try { - if (date.charAt (4) == ':') { - // It's a TIFF date, or a good imitation of one. - // TIFF dates have exact offsets, making things easy. - String yr = date.substring (0, 4); - String mo = date.substring (5, 7); - String da = date.substring (8, 10); - String hr = date.substring (11, 13); - String mi = date.substring (14, 16); - String se = date.substring (17, 19); - return yr + "-" + mo + "-" + da + "T" + - hr + TIME_SEP + mi + TIME_SEP + se; - } - return date; // default - } - catch (Exception e) { - // Malformed date - return null; - } - } - - public static String extractIccProfileDescription(byte[] data) throws IllegalArgumentException { - // Validate the ICCProfile with the java library - ICC_Profile profile = ICC_Profile.getInstance(data); - // Extract the 'desc' record (cf http://www.color.org/ICC1-V41.pdf) - byte[] dataProf = profile.getData(ICC_Profile.icSigProfileDescriptionTag); - if (dataProf == null) { - return null; - } - - String description = null; - ByteBuffer bb = ByteBuffer.wrap(dataProf).asReadOnlyBuffer(); - int majorVersion = profile.getMajorVersion(); - int minorVersion = profile.getMinorVersion(); - LOGGER.fine("Version " + majorVersion + "." + minorVersion); - - int beginTag = bb.getInt(0); - if (beginTag == ICC_Profile.icSigProfileDescriptionTag) { - // Invariant option - // ICC v2 http://www.color.org/ICC_Minor_Revision_for_Web.pdf - // Read only the ASCII form (cf 6.5.17 ICC v2) - final int OFFSET_LENGTH = 8; - final int OFFSET_DESC = OFFSET_LENGTH + 4; - - int lengthAscii = bb.getInt(OFFSET_LENGTH); - - // readString - byte[] asciiDesc = new byte[lengthAscii -1]; - bb.position(OFFSET_DESC); - bb.get(asciiDesc); - - description = new String(asciiDesc, StandardCharsets.US_ASCII); - } else { - // ICC v4 http://www.color.org/ICC1-V41.pdf - final int OFFSET_NUMBER = 8; - final int MLUC_TAG = 0x6D6C7563; - final int OFFSET_NAME_LENGTH = 20; - final int OFFSET_NAME_OFFSET = 24; - - // 6.2 segment tag table definition - int tagMluc = bb.getInt(0); - - // Read the 1st mulc form (cf 6.5.12 ICC v4) - int nb = bb.getInt(OFFSET_NUMBER); - if (tagMluc != MLUC_TAG || nb < 1) { - throw new IllegalArgumentException(CoreMessageConstants.ERR_ICC_PRFL_DESC_MISS); - } - int firstNameLength = bb.getInt(OFFSET_NAME_LENGTH); - int firstNameOffset = bb.getInt(OFFSET_NAME_OFFSET); - // readString - byte[] desc = new byte[firstNameLength]; - bb.position(firstNameOffset); - bb.get(desc); - - // The Unicode strings in storage are encoded as 16-bit - // big-endian, UTF-16BE, and should not be NULL terminated. - description = new String(desc, StandardCharsets.UTF_16BE); - } - return description; - } - + }; + + /** Index for 6.1.4.1 color space value labels. */ + public static final int[] COLORSPACE_INDEX = { + 0, 1, 2, 3, 4, 5, 6, 8, 9, 10, 32803, 32844, 32845, 34892, 65535 + }; + + /** 6.1.3.1 Compression scheme value labels. */ + /* Taken from the TIFFTAG_COMPRESSION */ + public static final String[] COMPRESSION_SCHEME = { + "uncompressed", + "CCITT 1D", + "CCITT Group 3", + "CCITT Group 4", /* 1-4 */ + "LZW", + "JPEG", + "ISO JPEG", + "Deflate", /* 5-8 */ + "JBIG", /* 32661 */ + "RLE with word alignment", /* 32771 */ + "PackBits", + "NeXT 2-bit encoding", + "ThunderScan 4-bit encoding", /* 32773- */ + "RasterPadding in CT or MP", /* 32895 */ + "RLE for LW", + "RLE for HC", + "RLE for BL", /* 32896-8 */ + "Pixar 10-bit LZW", /* 32908 */ + "Pixar companded 11-bit ZIP encoding", /* 32909 */ + "PKZIP-style Deflate encoding", /* 32946 */ + "Kodak DCS", /* 32947 */ + "SGI 32-bit Log Luminance encoding", /* 34676 */ + "SGI 24-bit Log Luminance encoding", /* 34677 */ + "JPEG 2000", /* 34712 */ + "JPEG2000 Lossy", /* 34713 non standard !!! */ + "JPEG2000 Lossless", /* 34714 non standard !!! */ + "LZMA" /* 34925 */ + }; + /** Index for 6.1.3.1 compression scheme value labels. */ + public static final int COMPRESSION_JPEG2000_LOSSY = 34713; + + public static final int COMPRESSION_JPEG2000_LOSSLESS = 34714; + public static final int[] COMPRESSION_SCHEME_INDEX = { + 1, 2, 3, 4, 5, 6, 7, 8, 32661, 32771, 32773, 32766, 32809, 32895, 32896, 32897, 32898, 32908, + 32909, 32946, 32947, 34676, 34677, 34712, 34713, /* non standard !!! */ + 34714, /* non standard !!! */ 34925 + }; + + /** 6.2.5 display orientation value labels. */ + public static final String[] DISPLAY_ORIENTATION = {"portrait", "landscape"}; + + public static final String[] EXTRA_SAMPLES = { + "unspecified", "associated alpha", "unassociated alpha", "range or depth" + }; + + public static final String[] EXPOSURE_PROGRAM = { + "Not defined", + " Manual", + "Normal program", + "Aperture priority", + "Shutter priority", + "Creative program (biased toward depth of field)", + "Action program (biased toward fast shutter speed)", + "Portrait mode (for closeup photos with the background out of focus)", + "Landscape mode (for landscape photos with the background in focus)" + }; + + /** 7.7.3.10 flash value labels. */ + public static final String[] FLASH = {NO, YES}; + + public static final String[] FLASH_20 = {"Flash did not fire", "Flash fired"}; + + /** 7.7.3.12 flash return value labels. */ + public static final String[] FLASH_RETURN = {YES, NO}; + + /** 8.2.6 gray response unit value labels for version 0.2. */ + public static final String[] GRAY_RESPONSE_UNIT_02 = { + "", + "tenths of a unit", + "hundredths of a unit", + "thousandths of a unit", + "ten-thousandths of a unit", + "hundred-thousandths of a unit" + }; + + /** Gray response unit value for version 2.0 of MIX, corresponding to NISO values of 1-5 */ + public static final String[] GRAY_RESPONSE_UNIT_20 = { + "Number represents tenths of a unit", + "Number represents hundredths of a unit", + "Number represents thousandths of a unit", + "Number represents ten-thousandths of a unit", + "Number represents hundred-thousandths of a unit" + }; + + /** extra sample value for version 2.0 of MIX, corresponding to NISO values of 0-3 * */ + public static final String[] EXTRA_SAMPLE_20 = { + "unspecified data", + "associated alpha data (with pre-multiplied color)", + "unassociated alpha data", + "range or depth data" + }; + + /** 7.7.3.6 metering mode value labels. */ + public static final String[] METERING_MODE = { + "unidentified", "average", "center weighted average", "spot", "multispot", "pattern", "partial" + }; + + /** 6.2.4 orientation value labels. */ + public static final String[] ORIENTATION = { + "", + "normal", + "reflected horiz", + "rotated 180 deg", + "reflected vert", + "left top", + "rotated cw 90 deg", + "Right bottom", + "Rotated ccw 90 deg", + "Unknown" + }; + + /** 6.1.6 planar configuration value labels. */ + public static final String[] PLANAR_CONFIGURATION = {"", "chunky", "planar"}; + + /** 8.1.1 sampling frequency plane value labels. */ + public static final String[] SAMPLING_FREQUENCY_PLANE = { + "", "camera/scanner focal plane", "object plane", "source object plane" + }; + + /** 8.1.2 sampling frequency unit value labels. */ + public static final String[] SAMPLING_FREQUENCY_UNIT = { + "", "no absolute unit", "inch", "centimeter" + }; + + /** 7.7.3.7 scene illuminant value labels. */ + public static final String[] SCENE_ILLUMINANT = { + "unidentified", + "daylight", + "fluorescent", + "tungsten lamp", + "flash", + "standard illuminant A", + "standard illuminat B", + "standard illuminant C", + "D55 illuminant", + "D65 illuminant", + "D75 illuminant" + }; + /** Index for 7.7.3.7 scene illuminant value labels. */ + public static final int[] SCENE_ILLUMINANT_INDEX = {0, 1, 2, 3, 10, 17, 18, 19, 20, 21, 22}; + + /** 6.1.5.1 segment type value labels. */ + public static final String[] SEGMENT_TYPE = {"strips", "tiles"}; + + /** 7.8 sensor value labels. */ + public static final String[] SENSOR = { + "Undefined", + "MonochromeArea", + "OneChipColorArea", + "TwoChipColorArea", + "ThreeChipColorArea", + "ColorSequentialArea", + "MonochromeLinear", + "ColorTriLinear", + "ColorSequentialLinear" + }; + + /** 8.1.7.1 (8.1.8.1) source dimension unit. */ + public static final String[] SOURCE_DIMENSION_UNIT = {"inches", "mm"}; + + /** 6.1.4.4 YCbCr positioning value labels. */ + public static final String[] YCBCR_POSITIONING = {"", "centered", "cosited"}; + + /** 8.3.1 TargetType. */ + public static final String[] TARGET_TYPE = {"external", "internal"}; + + /** Undefined value. */ + public static final int NULL = -1; + + public static final double NILL = -1.0; + + /** + * **************************************************************** PRIVATE INSTANCE FIELDS. + * + *

6 Basic image parameters **************************************************************** + */ + + /** 6.1.1 MIME type */ + private String _mimeType; + + /** 6.1.2 Byte order */ + private String _byteOrder; + + /** 6.1.3.1 Compression scheme */ + private int _compressionScheme; + /** 6.1.3.2 Compression level */ + private int _compressionLevel; + + /** 6.1.4.1 Color space */ + private int _colorSpace; + + /** 6.1.4.2.1 ICC profile name */ + private String _profileName; + /** 6.1.4.2.2 ICC profile url */ + private String _profileURL; + + /** 6.1.4.3 YCbCr sub-sampling */ + private int[] _yCbCrSubSampling; + /** 6.1.4.4 YCbCr positioning */ + private int _yCbCrPositioning; + /** 6.1.4.5 YCbCr coefficients */ + private Rational[] _yCbCrCoefficients; + /** 6.1.4.6 Reference black and white */ + private Rational[] _referenceBlackWhite; + + /** 6.1.5.1 Segment type */ + private int _segmentType; + /** 6.1.5.2 Strip offsets */ + private long[] _stripOffsets; + /** 6.1.5.3 Rows per strip */ + private long _rowsPerStrip; + /** 6.1.5.4 Strip byte counts */ + private long[] _stripByteCounts; + /** 6.1.5.5 Tile width */ + private long _tileWidth; + /** 6.1.5.6 Tile length */ + private long _tileLength; + /** 6.1.5.7 Tile offsets */ + private long[] _tileOffsets; + /** 6.1.5.8 Tile byte counts */ + private long[] _tileByteCounts; + + /** 6.1.6 Planar configuration */ + private int _planarConfiguration; + + /** 6.2.1 Image identifier */ + private String _imageIdentifier; + /** 6.2.1.1 Image identifier location */ + private String _imageIdentifierLocation; + + /** 6.2.2 File size */ + private long _fileSize; + /** 6.2.3.1 Checksum method */ + private int _checksumMethod; + /** 6.2.3.2 Checksum value */ + private String _checksumValue; + + /** 6.2.4 orientation */ + private int _orientation; + + /** 6.2.5 Display orientation */ + private int _displayOrientation; + + /** 6.2.6.1 X targeted display aspect ratio */ + private long _xTargetedDisplayAR; + /** 6.2.6.2 Y targeted display aspect ratio */ + private long _yTargetedDisplayAR; + + /** 6.3 Preferred presentation */ + private String _preferredPresentation; + + /** + * **************************************************************** Special Format Characteristics + * From Data Dictionary - Technical Metadata for Digital Still Images (ANSI/NISO Z39.87-2006) Only + * used for JPEG2000 format **************************************************************** + */ + /** 7.2.1.2.1 tiles */ + private String _jp2Tiles; + /** 7.2.1.2.2 qualityLayers */ + private int _jp2Layers; + /** 7.2.1.2.3resolutionLevels */ + private int _jp2ResolutionLevels; + + /** + * **************************************************************** 7 Image creation + * **************************************************************** + */ + + /** 7.1 Source type */ + private String _sourceType; + + /** 7.2 Source ID */ + private String _sourceID; + + /** 7.3 Image producer */ + private String _imageProducer; + + /** 7.4 Host computer */ + private String _hostComputer; + /** 7.4.1 Operating system */ + private String _os; + /** 7.4.2 OS version */ + private String _osVersion; + + /** 7.5 Device source */ + private String _deviceSource; + + /** 7.6.1.1 Scanner system manufacturer */ + private String _scannerManufacturer; + /** 7.6.1.2.1 Scanner model name */ + private String _scannerModelName; + /** 7.6.1.2.2 Scanner model number */ + private String _scannerModelNumber; + /** 7.6.1.2.3 Scanner model serial number */ + private String _scannerModelSerialNo; + /** 7.6.2.1 Scanning software */ + private String _scanningSoftware; + /** 7.6.2.2 Scanning software version number */ + private String _scanningSoftwareVersionNo; + + /** 7.6.3 Pixel size (in meters) */ + private double _pixelSize; + + /** 7.6.3.2.1 X physical scan resolution */ + private double _xPhysScanResolution; + /** 7.6.3.2.2 Y physical scan resolution */ + private double _yPhysScanResolution; + + /** 7.7.1 Digital camera manufacturer */ + private String _digitalCameraManufacturer; + /** 7.7.2 Digital camera model name */ + private String _digitalCameraModelName; + /** 7.7.2 Digital camera model number */ + private String _digitalCameraModelNumber; + /** 7.7.2 Digital camera model serial number */ + private String _digitalCameraModelSerialNo; + + /** 7.7.3.1 F number */ + private double _fNumber; + /** 7.7.3.2 Exposure time */ + private double _exposureTime; + + private int _exposureProgram; + private String _exifVersion; + private Rational _maxApertureValue; + + /** 7.7.3.3 Brightness */ + private Rational _brightness; + /** 7.7.3.4 Exposure bias */ + private Rational _exposureBias; + /** 7.7.3.5 Subject distance */ + private double[] _subjectDistance; + /** 7.7.3.6 Metering mode */ + private int _meteringMode; + /** 7.7.3.7 Scene illuminant */ + private int _sceneIlluminant; + /** 7.7.3.8 Color temperature */ + private double _colorTemp; + /** 7.7.3.9 Focal length (in meters) */ + private double _focalLength; + /** 7.7.3.10 Flash */ + private int _flash; + /** 7.7.3.11 Flash energy */ + private Rational _flashEnergy; + /** 7.7.3.12 Flash return */ + private int _flashReturn; + /** 7.7.3.13 Back light */ + private int _backLight; + /** 7.7.3.14 Exposure index */ + private double _exposureIndex; + /** 7.7.3.15 Auto focus */ + private int _autoFocus; + /** 7.7.3.16.1 X print aspect ratio */ + private double _xPrintAspectRatio; + /** 7.7.3.16.2 Y print aspect ratio */ + private double _yPrintAspectRatio; + + /** 7.8 Sensor */ + private int _sensor; + + /** 7.9 Date/time created */ + private String _dateTimeCreated; + + /** 7.10 Methodology */ + private String _methodology; + + /** + * **************************************************************** Imaging performance assessment + * **************************************************************** + */ + + /** 8.1.1 Sampling frequency plane */ + private int _samplingFrequencyPlane; + /** 8.1.2 Sampling frequency unit */ + private int _samplingFrequencyUnit; + /** 8.1.3 X sampling frequency */ + private Rational _xSamplingFrequency; + /** 8.1.4 Y sampling frequency */ + private Rational _ySamplingFrequency; + /** 8.1.5 Image width */ + private long _imageWidth; + /** 8.1.6 Image Length */ + private long _imageLength; + /** 8.1.7 Source X dimension */ + private double _sourceXDimension; + /** 8.1.8 Source X dimension unit */ + private int _sourceXDimensionUnit; + /** 8.1.9 Source Y dimension */ + private double _sourceYDimension; + /** 8.1.10 Source Y dimension unit */ + private int _sourceYDimensionUnit; + + /** 8.2.1 Bits per sample */ + private int[] _bitsPerSample; + /** 8.2.2 Samples per pixel */ + private int _samplesPerPixel; + /** 8.2.3 Extra samples */ + private int[] _extraSamples; + + /** 8.2.4.1 Colormap reference */ + private String _colormapReference; + /** 8.2.4.2 Colormap bit code value */ + private int[] _colormapBitCodeValue; + /** 8.2.4.3 Colormap red value */ + private int[] _colormapRedValue; + /** 8.2.4.4 Colormap green value */ + private int[] _colormapGreenValue; + /** 8.2.4.5 Colormap blue value */ + private int[] _colormapBlueValue; + + /** 8.2.5 Gray response curve */ + private int[] _grayResponseCurve; + /** 8.2.6 Gray response unit */ + private int _grayResponseUnit; + + /** 8.2.7.1 Whitepoint X value */ + private Rational _whitePointXValue; + /** 8.2.7.2 Whitepoint Y value */ + private Rational _whitePointYValue; + + /** 8.2.8.1 Primary chromaticities Red X */ + private Rational _primaryChromaticitiesRedX; + /** 8.2.8.2 Primary chromaticities Red Y */ + private Rational _primaryChromaticitiesRedY; + /** 8.2.8.3 Primary chromaticities Green X */ + private Rational _primaryChromaticitiesGreenX; + /** 8.2.8.4 Primary chromaticities Green Y */ + private Rational _primaryChromaticitiesGreenY; + /** 8.2.8.5 Primary chromaticities Blue X */ + private Rational _primaryChromaticitiesBlueX; + /** 8.2.8.6 Primary chromaticities Blue Y */ + private Rational _primaryChromaticitiesBlueY; + + /* 8.3 Target data */ + /** 8.3.1 Target Type */ + private int _targetType; + /** 8.3.2.1 TargetIDManufacturer */ + private String _targetIDManufacturer; + /** 8.3.2.2 TargetIDName */ + private String _targetIDName; + /** 8.3.2.3 TargetIDNo */ + private String _targetIDNo; + /** 8.3.2.4 TargetIDMedia */ + private String _targetIDMedia; + /** 8.3.3 ImageData */ + private String _imageData; + /** 8.3.4 PerformanceData */ + private String _performanceData; + /** 8.3.5 Profiles */ + private String _profiles; + + /* 9 Change history */ + /** 9.1.1 DateTimeProcessed */ + private String _dateTimeProcessed; + /** 9.1.2 SourceData */ + private String _sourceData; + /** 9.1.3 ProcessingAgency */ + private String _processingAgency; + /** 9.1.4.1 ProcessingSoftwareName */ + private String _processingSoftwareName; + /** 9.1.4.2 ProcessingSoftwareVersion */ + private String _processingSoftwareVersion; + /** 9.1.5 ProcessingActions */ + private String[] _processingActions; + + /* 9.2 PreviousImageMetadata -- not currently supported */ + + /* Data for Swing-based viewer. */ + private Property _viewerData; + + /** + * **************************************************************** CLASS CONSTRUCTOR. + * **************************************************************** + */ + + /** Instantiate a NisoImageMetadata object. */ + public NisoImageMetadata() { + _autoFocus = NULL; + _backLight = NULL; + _brightness = null; + _checksumMethod = NULL; + _colorSpace = NULL; + _colorTemp = NILL; + _compressionLevel = NULL; + _compressionScheme = NULL; + _dateTimeProcessed = null; + _digitalCameraManufacturer = null; + _digitalCameraModelName = null; + _digitalCameraModelNumber = null; + _digitalCameraModelSerialNo = null; + _displayOrientation = NULL; + _exifVersion = null; + _exposureBias = null; + _exposureIndex = NILL; + _exposureProgram = NULL; + _exposureTime = NILL; + _fileSize = NULL; + _flash = NULL; + _flashEnergy = null; + _flashReturn = NULL; + _fNumber = NILL; + _focalLength = NILL; + _grayResponseUnit = NULL; + _imageData = null; + _imageLength = NULL; + _imageWidth = NULL; + _jp2Layers = NULL; + _jp2ResolutionLevels = NULL; + _jp2Tiles = null; + _maxApertureValue = null; + _meteringMode = NULL; + _orientation = NULL; + _performanceData = null; + _pixelSize = NILL; + _planarConfiguration = NULL; + _processingActions = null; + _processingAgency = null; + _processingSoftwareName = null; + _processingSoftwareVersion = null; + _profiles = null; + _rowsPerStrip = NULL; + _scannerManufacturer = null; + _scannerModelName = null; + _scannerModelNumber = null; + _scannerModelSerialNo = null; + _samplesPerPixel = NULL; + _samplingFrequencyPlane = NULL; + _samplingFrequencyUnit = NULL; + _sceneIlluminant = NULL; + _segmentType = NULL; + _sensor = NULL; + _sourceData = null; + _sourceXDimension = NILL; + _sourceXDimensionUnit = NULL; + _sourceYDimension = NILL; + _sourceYDimensionUnit = NULL; + _tileLength = NULL; + _tileWidth = NULL; + _targetIDManufacturer = null; + _targetIDMedia = null; + _targetIDName = null; + _targetIDNo = null; + _targetType = NULL; + _xPhysScanResolution = NILL; + _xPrintAspectRatio = NILL; + _xSamplingFrequency = null; + _xTargetedDisplayAR = NULL; + _yCbCrPositioning = NULL; + _yPhysScanResolution = NILL; + _yPrintAspectRatio = NILL; + _ySamplingFrequency = null; + _yTargetedDisplayAR = NULL; + _viewerData = null; + } + + /** + * **************************************************************** PUBLIC INSTANCE METHODS. + * + *

Accessor methods. **************************************************************** + */ + + /** Get 7.7.3.15 auto focus. */ + public int getAutoFocus() { + return _autoFocus; + } + + /** Get 7.7.3.13 back light. */ + public int getBackLight() { + return _backLight; + } + + /** Get 8.2.1 bits per sample. */ + public int[] getBitsPerSample() { + return _bitsPerSample; + } + + /** Get 7.7.3.3 Brightness. */ + public Rational getBrightness() { + return _brightness; + } + + /** Get 6.1.2 byte order. */ + public String getByteOrder() { + return _byteOrder; + } + + /** Get 6.2.3.1 Checksum method. */ + public int getChecksumMethod() { + return _checksumMethod; + } + + /** Get 6.2.3.2 Checksum value. */ + public String getChecksumValue() { + return _checksumValue; + } + + /** Get 8.2.4.2 colormap bit code value. */ + public int[] getColormapBitCodeValue() { + return _colormapBitCodeValue; + } + + /** Get 8.2.4.5 colormap blue value. */ + public int[] getColormapBlueValue() { + return _colormapBlueValue; + } + + /** Get 8.2.4.4 colormap green value. */ + public int[] getColormapGreenValue() { + return _colormapGreenValue; + } + + /** Get 8.2.4.3 colormap red value. */ + public int[] getColormapRedValue() { + return _colormapRedValue; + } + + /** Get 8.2.4.1 colormap reference. */ + public String getColormapReference() { + return _colormapReference; + } + + /** Get 6.1.4.1 color space. */ + public int getColorSpace() { + return _colorSpace; + } + + /** Get 7.7.3.8 color temperature. */ + public double getColorTemp() { + return _colorTemp; + } + + /** Get 6.1.3.2 compression level. */ + public int getCompressionLevel() { + return _compressionLevel; + } + + /** Get 6.1.3.1 compression scheme. */ + public int getCompressionScheme() { + return _compressionScheme; + } + + /** Get 7.9 date/time created. */ + public String getDateTimeCreated() { + return _dateTimeCreated; + } + + /** Get 9.1.1 DateTimeProcessed */ + public String getDateTimeProcessed() { + return _dateTimeProcessed; + } + + /** Get 7.5 device source. */ + public String getDeviceSource() { + return _deviceSource; + } + + /** Get 7.7.1 digital camera manufacturer. */ + public String getDigitalCameraManufacturer() { + return _digitalCameraManufacturer; + } + + /** Get 7.7.2 digital camera model. */ + public String getDigitalCameraModelName() { + return _digitalCameraModelName; + } + + public String getDigitalCameraModelNumber() { + return _digitalCameraModelNumber; + } + + public String getDigitalCameraModelSerialNo() { + return _digitalCameraModelSerialNo; + } + + /** Get 6.2.5 Display orientation. */ + public int getDisplayOrientation() { + return _displayOrientation; + } + + public String getExifVersion() { + return _exifVersion; + } + + /** Get 7.7.3.4 exposure bias. */ + public Rational getExposureBias() { + return _exposureBias; + } + + /** Get 7.7.3.14 exposure index. */ + public double getExposureIndex() { + return _exposureIndex; + } + + public int getExposureProgram() { + return _exposureProgram; + } + + /** Get 7.7.3.2 exposure time. */ + public double getExposureTime() { + return _exposureTime; + } + + /** Get 8.2.3 extra samples. */ + public int[] getExtraSamples() { + return _extraSamples; + } + + /** Get 6.2.2 file size. */ + public long getFileSize() { + return _fileSize; + } + + /** Get 7.7.3.10 flash. */ + public int getFlash() { + return _flash; + } + + /** Get 7.7.3.11 flash energy. */ + public Rational getFlashEnergy() { + return _flashEnergy; + } + + /** Get 7.7.3.12 flash return. */ + public int getFlashReturn() { + return _flashReturn; + } + + /** Get 7.7.3.1 F number. */ + public double getFNumber() { + return _fNumber; + } + + /** Get 7.7.3.9 focal length. */ + public double getFocalLength() { + return _focalLength; + } + + /** Get 8.2.5 gray response curve. */ + public int[] getGrayResponseCurve() { + return _grayResponseCurve; + } + + /** Get 8.2.6 gray response unit. */ + public int getGrayResponseUnit() { + return _grayResponseUnit; + } + + /** Get 7.4 host computer. */ + public String getHostComputer() { + return _hostComputer; + } + + /** Get 8.3.3 ImageData */ + public String getImageData() { + return _imageData; + } + + /** Get 6.2.1 Image identifier. */ + public String getImageIdentifier() { + return _imageIdentifier; + } + + /** Get 6.2.1.1 Image identifier location. */ + public String getImageIdentifierLocation() { + return _imageIdentifierLocation; + } + + /** Get 8.1.6 image length. */ + public long getImageLength() { + return _imageLength; + } + + /** Get 7.3 Image producer. */ + public String getImageProducer() { + return _imageProducer; + } + + /** Get 8.1.5 image width. */ + public long getImageWidth() { + return _imageWidth; + } + + public Rational getMaxApertureValue() { + return _maxApertureValue; + } + + /** Get 7.7.3.6 metering mode. */ + public int getMeteringMode() { + return _meteringMode; + } + + /** Get 7.10 methodology. */ + public String getMethodology() { + return _methodology; + } + + /** Get 6.1.1 MIME type. */ + public String getMimeType() { + return _mimeType; + } + + /** Get 6.2.4 Orientation. */ + public int getOrientation() { + return _orientation; + } + + /** Get 7.4.1 OS (operating system). */ + public String getOS() { + return _os; + } + + /** Get 7.4.2 OS version. */ + public String getOSVersion() { + return _osVersion; + } + + /** Get 8.3.4 PerformanceData. */ + public String getPerformanceData() { + return _performanceData; + } + + /** Get 7.6.3.1 pixel size. */ + public double getPixelSize() { + return _pixelSize; + } + + /** Get 6.1.6 Planar configuration. */ + public int getPlanarConfiguration() { + return _planarConfiguration; + } + + /** Get 6.3 preferred presentation. */ + public String getPreferredPresentation() { + return _preferredPresentation; + } + + public String getJp2Tiles() { + return _jp2Tiles; + } + + public void setJp2Tiles(String jp2Tiles) { + this._jp2Tiles = jp2Tiles; + } + + public int getJp2Layers() { + return _jp2Layers; + } + + public void setJp2Layers(int jp2Layers) { + this._jp2Layers = jp2Layers; + } + + public int getJp2ResolutionLevels() { + return _jp2ResolutionLevels; + } + + public void setJp2ResolutionLevels(int jp2ResolutionLevels) { + this._jp2ResolutionLevels = jp2ResolutionLevels; + } + + /** Get 8.2.8.5 primary chromaticities blue X. */ + public Rational getPrimaryChromaticitiesBlueX() { + return _primaryChromaticitiesBlueX; + } + + /** Get 8.2.8.6 primary chromaticities blue Y. */ + public Rational getPrimaryChromaticitiesBlueY() { + return _primaryChromaticitiesBlueY; + } + + /** Get 8.2.8.3 primary chromaticities green X. */ + public Rational getPrimaryChromaticitiesGreenX() { + return _primaryChromaticitiesGreenX; + } + + /** Get 8.2.8.4 primary chromaticities green Y. */ + public Rational getPrimaryChromaticitiesGreenY() { + return _primaryChromaticitiesGreenY; + } + + /** Get 8.2.8.1 primary chromaticities red X. */ + public Rational getPrimaryChromaticitiesRedX() { + return _primaryChromaticitiesRedX; + } + + /** Get 8.2.8.2 primary chromaticities red Y. */ + public Rational getPrimaryChromaticitiesRedY() { + return _primaryChromaticitiesRedY; + } + + /** Get 9.1.5 ProcessingActions. */ + public String[] getProcessingActions() { + return _processingActions; + } + + /** Get 9.1.3 ProcessingAgency. */ + public String getProcessingAgency() { + return _processingAgency; + } + + /** Get 9.1.4.1 ProcessingSoftwareName */ + public String getProcessingSoftwareName() { + return _processingSoftwareName; + } + + /** Get 9.1.4.2 ProcessingSoftwareVersion */ + public String getProcessingSoftwareVersion() { + return _processingSoftwareVersion; + } + + /** Get 6.1.4.2.1 ICC profile name. */ + public String getProfileName() { + return _profileName; + } + + /** Get 8.3.5 Profiles */ + public String getProfiles() { + return _profiles; + } + + /** Get 6.1.4.2.2 ICC profile URL. */ + public String getProfileURL() { + return _profileURL; + } + + /** Get 6.1.4.6 Reference black and white. */ + public Rational[] getReferenceBlackWhite() { + return _referenceBlackWhite; + } + + /** Get 6.1.5.3 Rows per strip. */ + public long getRowsPerStrip() { + return _rowsPerStrip; + } + + /** Get 8.2.2 samples per pixel. */ + public int getSamplesPerPixel() { + return _samplesPerPixel; + } + + /** Get 8.1.1 sampling frequency plane. */ + public int getSamplingFrequencyPlane() { + return _samplingFrequencyPlane; + } + + /** Get 8.1.2 sampling frequency unit. */ + public int getSamplingFrequencyUnit() { + return _samplingFrequencyUnit; + } + + /** Get 7.6.1.1 scanner manufacturer. */ + public String getScannerManufacturer() { + return _scannerManufacturer; + } + + /** Get 7.6.1.2.1 scanner model name. */ + public String getScannerModelName() { + return _scannerModelName; + } + + /** Get 7.6.1.2.2 scanner model number. */ + public String getScannerModelNumber() { + return _scannerModelNumber; + } + + /** Get 7.6.1.2.3 scanner model serial number. */ + public String getScannerModelSerialNo() { + return _scannerModelSerialNo; + } + + /** Get 7.6.2.1 scanning software. */ + public String getScanningSoftware() { + return _scanningSoftware; + } + + /** Get 7.6.2.2 scanning software version number. */ + public String getScanningSoftwareVersionNo() { + return _scanningSoftwareVersionNo; + } + + /** Get 7.7.3.7 scene illuminant. */ + public int getSceneIlluminant() { + return _sceneIlluminant; + } + + /** Get 6.1.5.1 segment type. */ + public int getSegmentType() { + return _segmentType; + } + + /** Get 7.8 sensor. */ + public int getSensor() { + return _sensor; + } + + /** Get 9.1.2 SourceData. */ + public String getSourceData() { + return _sourceData; + } + + /** Get 7.2 source ID. */ + public String getSourceID() { + return _sourceID; + } + + /** Get 7.1 Source type. */ + public String getSourceType() { + return _sourceType; + } + + public double getSourceXDimension() { + return _sourceXDimension; + } + + public int getSourceXDimensionUnit() { + return _sourceXDimensionUnit; + } + + public double getSourceYDimension() { + return _sourceYDimension; + } + + public int getSourceYDimensionUnit() { + return _sourceYDimensionUnit; + } + + /** Get 6.1.5.4 Strip byte counts. */ + public long[] getStripByteCounts() { + return _stripByteCounts; + } + + /** Get 6.1.5.2 Strip offsets. */ + public long[] getStripOffsets() { + return _stripOffsets; + } + + /** Get 7.7.3.5 Subject distance. */ + public double[] getSubjectDistance() { + return _subjectDistance; + } + + /** Get 8.3.2.1 TargetIDManufacturer */ + public String getTargetIDManufacturer() { + return _targetIDManufacturer; + } + + /** Get 8.3.2.3 TargetIDMedia */ + public String getTargetIDMedia() { + return _targetIDMedia; + } + + /** Get 8.3.2.2 TargetIDName */ + public String getTargetIDName() { + return _targetIDName; + } + + /** Get 8.3.2.3 TargetIDNo */ + public String getTargetIDNo() { + return _targetIDNo; + } + + /** Get 8.3.1 Target Type */ + public int getTargetType() { + return _targetType; + } + + /** Get 6.1.5.8 Tile byte counts. */ + public long[] getTileByteCounts() { + return _tileByteCounts; + } + + /** Get 6.1.5.6 Tile length. */ + public long getTileLength() { + return _tileLength; + } + + /** Get 6.1.5.7 Tile offsets. */ + public long[] getTileOffsets() { + return _tileOffsets; + } + + /** Get 6.1.5.5 Tile width. */ + public long getTileWidth() { + return _tileWidth; + } + + /** Get 8.2.7.1 white point X value. */ + public Rational getWhitePointXValue() { + return _whitePointXValue; + } + + /** Get 8.2.7.2 white point Y value. */ + public Rational getWhitePointYValue() { + return _whitePointYValue; + } + + /** Get 7.7.3.16.1 X print aspect ratio. */ + public double getXPrintAspectRatio() { + return _xPrintAspectRatio; + } + + /** Get 7.6.3.2.1 X physcal scanning resolution. */ + public double getXPhysScanResolution() { + return _xPhysScanResolution; + } + + /** Get 8.1.3 X sampling frequency. */ + public Rational getXSamplingFrequency() { + return _xSamplingFrequency; + } + + /** Get 6.2.6 X targeted display aspect ratio. */ + public long getXTargetedDisplayAR() { + return _xTargetedDisplayAR; + } + + /** Get 6.1.4.5 YCbCr coefficients. */ + public Rational[] getYCbCrCoefficients() { + return _yCbCrCoefficients; + } + + /** Get 6.1.4.4 YCbCr positioning. */ + public int getYCbCrPositioning() { + return _yCbCrPositioning; + } + + /** Get 6.1.4.3 YCbCr subsampling. */ + public int[] getYCbCrSubSampling() { + return _yCbCrSubSampling; + } + + /** Get 7.6.3.2.2 Y physcal scanning resolution. */ + public double getYPhysScanResolution() { + return _yPhysScanResolution; + } + + /** Get 7.7.3.16.2 Y print aspect ratio. */ + public double getYPrintAspectRatio() { + return _yPrintAspectRatio; + } + + /** Get 8.1.4 Y sampling frequency. */ + public Rational getYSamplingFrequency() { + return _ySamplingFrequency; + } + + /** Get 6.2.7 Y targeted display aspect ratio. */ + public long getYTargetedDisplayAR() { + return _yTargetedDisplayAR; + } + + /** Get data for Swing GUI viewer. */ + public Property getViewerData() { + return _viewerData; + } + + /** + * **************************************************************** Mutator methods. + * **************************************************************** + */ + + /** + * Set 7.7.3.15 auto focus. + * + * @param focus Auto focus + */ + public void setAutoFocus(int focus) { + _autoFocus = focus; + } + + /** + * Set 7.7.3.13 back light. + * + * @param light Back light + */ + public void setBackLight(int light) { + _backLight = light; + } + + /** + * Set 8.2.1 bits per sample. + * + * @param bits Bits per sample + */ + public void setBitsPerSample(int[] bits) { + _bitsPerSample = bits; + } + + /** + * Set 7.7.3.3 brightness. + * + * @param brightness Brightness + */ + public void setBrightness(Rational brightness) { + _brightness = brightness; + } + + /** + * Set 6.1.2 byte order. + * + * @param order Byte order + */ + public void setByteOrder(String order) { + _byteOrder = order; + } + + /** + * Set 8.2.4.2 colormap bit code value. + * + * @param value Bit code value + */ + public void setColormapBitCodeValue(int[] value) { + _colormapBitCodeValue = value; + } + + /** + * Set 8.2.4.4 colormap blue value. + * + * @param value Blue value + */ + public void setColormapBlueValue(int[] value) { + _colormapBlueValue = value; + } + + /** + * Set 8.2.4.3 colormap green value. + * + * @param value Green value + */ + public void setColormapGreenValue(int[] value) { + _colormapGreenValue = value; + } + + /** + * Set 8.2.4.2 colormap red value. + * + * @param value Red value + */ + public void setColormapRedValue(int[] value) { + _colormapRedValue = value; + } + + /** + * Set 8.2.4.1 colormap reference. + * + * @param reference Colormap reference + */ + public void setColormapReference(String reference) { + _colormapReference = reference; + } + + /** + * Set 6.1.4.1 color space + * + * @param space Color space + */ + public void setColorSpace(int space) { + _colorSpace = space; + } + + /** + * Set 7.7.3.8 color temperature. + * + * @param temp Color temperature + */ + public void setColorTemp(double temp) { + _colorTemp = temp; + } + + /** + * Set 6.1.3.2 compression level. + * + * @param level Compression level + */ + public void setCompressionLevel(int level) { + _compressionLevel = level; + } + + /** + * Set 6.1.3.1 compression scheme. + * + * @param scheme Compression scheme + */ + public void setCompressionScheme(int scheme) { + _compressionScheme = scheme; + } + + /** + * Set 7.9 date/time created. TIFF dates get converted to ISO 8601 format. + * + * @param date Date/time created + */ + public void setDateTimeCreated(String date) { + _dateTimeCreated = make8601Valid(date); + } + + /** + * Set 9.1.1 DateTimeProcessed. TIFF dates get converted to ISO 8601 format. + * + * @param date Date/time processed + */ + public void setDateTimeProcessed(String date) { + _dateTimeProcessed = make8601Valid(date); + } + + /** + * Set 7.5 Device source. + * + * @param source Device source + */ + public void setDeviceSource(String source) { + _deviceSource = source; + } + + /** + * Set 7.7.1 digital camera manufacturer. + * + * @param manufacturer Camera manufacturer + */ + public void setDigitalCameraManufacturer(String manufacturer) { + _digitalCameraManufacturer = manufacturer; + } + + /** + * Set 7.7.2 digital camera model. + * + * @param modelName Camera model + */ + public void setDigitalCameraModelName(String modelName) { + _digitalCameraModelName = modelName; + } + + public void setDigitalCameraModelNumber(String modelNumber) { + _digitalCameraModelNumber = modelNumber; + } + + public void setDigitalCameraModelSerialNo(String modelSerialNo) { + _digitalCameraModelSerialNo = modelSerialNo; + } + + /** + * Set 6.2.5 display orientation. + * + * @param orientation Display orientation + */ + public void setDisplayOrientation(int orientation) { + _displayOrientation = orientation; + } + + public void setExifVersion(String version) { + _exifVersion = version; + } + + /** + * Set 7.2.3.4 exposure bias. + * + * @param bias Exposure bias + */ + public void setExposureBias(Rational bias) { + _exposureBias = bias; + } + + /** + * Set 7.2.3.14 exposure index. + * + * @param index Exposure index + */ + public void setExposureIndex(double index) { + _exposureIndex = index; + } + + public void setExposureProgram(int program) { + _exposureProgram = program; + } + + /** + * Set 7.7.3.2 exposure time. + * + * @param time Exposure time + */ + public void setExposureTime(double time) { + _exposureTime = time; + } + + /** + * Set 8.2.3 extra samples. + * + * @param extra Extra samples + */ + public void setExtraSamples(int[] extra) { + _extraSamples = extra; + } + + /** + * Set 6.2.2 file size. + * + * @param size File size + */ + public void setFileSize(long size) { + _fileSize = size; + } + + /** + * Set 7.7.3.1 F number. + * + * @param f F number + */ + public void setFNumber(double f) { + _fNumber = f; + } + + /** + * Set 7.7.3.11 flash energy. + * + * @param energy Flash energy + */ + public void setFlashEnergy(Rational energy) { + _flashEnergy = energy; + } + + /** + * Set 7.7.3.12 flash return. + * + * @param ret Flash return + */ + public void setFlashReturn(int ret) { + _flashReturn = ret; + } + + /** + * Set 7.7.3.10 flash. + * + * @param flash Flash + */ + public void setFlash(int flash) { + _flash = flash; + } + + /** + * Set 7.7.3.9 focal length (double meters). + * + * @param length Focal length + */ + public void setFocalLength(double length) { + _focalLength = length; + } + + /** + * Set 8.2.5 gray response curve. + * + * @param curve Gray response curve + */ + public void setGrayResponseCurve(int[] curve) { + _grayResponseCurve = curve; + } + + /** + * Set 8.2.6 gray response unit. + * + * @param unit Gray response unit + */ + public void setGrayResponseUnit(int unit) { + _grayResponseUnit = unit; + } + + /** + * Set 7.4 host computer. + * + * @param computer Host computer + */ + public void setHostComputer(String computer) { + _hostComputer = computer; + } + + /** + * Set 8.3.3 ImageData. + * + * @param imageData Image Data filename or URN + */ + public void setImageData(String imageData) { + _imageData = imageData; + } + + /** + * Set 6.2.1 Image identifier. + * + * @param identifier Image identifier + */ + public void setImageIdentifier(String identifier) { + _imageIdentifier = identifier; + } + + /** + * Set 6.2.1 Image identifier location. + * + * @param location identifier location + */ + public void setImageIdentifierLocation(String location) { + _imageIdentifierLocation = location; + } + + /** + * Set 8.1.6 image length. + * + * @param length Image length + */ + public void setImageLength(long length) { + _imageLength = length; + } + + /** + * Set 7.3 image producer. + * + * @param producer Image producer + */ + public void setImageProducer(String producer) { + _imageProducer = producer; + } + + /** + * Set 8.1.5 image width. + * + * @param width Image width + */ + public void setImageWidth(long width) { + _imageWidth = width; + } + + public void setMaxApertureValue(Rational value) { + _maxApertureValue = value; + } + + /** + * Set 7.7.3.6 metering mode. + * + * @param mode Metering mode + */ + public void setMeteringMode(int mode) { + _meteringMode = mode; + } + + /** + * Set 7.10 methodology. + * + * @param methodology Methodology + */ + public void setMethodology(String methodology) { + _methodology = methodology; + } + + /** + * Set 6.1.1 MIME type. + * + * @param type MIME type + */ + public void setMimeType(String type) { + _mimeType = type; + } + + /** + * Set 6.2.4 orientation. + * + * @param orientation Orientation + */ + public void setOrientation(int orientation) { + _orientation = orientation; + } + + /* Set 7.4.1 OS (operating system). + * @param os Operating system + */ + public void setOS(String os) { + _os = os; + } + + /** + * Set 7.4.2 OS version. + * + * @param version OS version + */ + public void setOSVersion(String version) { + _osVersion = version; + } + + /** + * Set 8.3.4 PerformanceData. + * + * @param performanceData Performance data filename or URN + */ + public void setPerformanceData(String performanceData) { + _performanceData = performanceData; + } + + /** + * Set 7.6.3.1 pixel size. + * + * @param size Pixel size + */ + public void setPixelSize(double size) { + _pixelSize = size; + } + + /** + * Set 6.1.6 Planar configuration. + * + * @param configuration Planar configuration + */ + public void setPlanarConfiguration(int configuration) { + _planarConfiguration = configuration; + } + + /** + * Set 6.3 preferred presentation. + * + * @param presentation Preferred presentation + */ + public void setPreferredPresentation(String presentation) { + _preferredPresentation = presentation; + } + + /** + * Set 8.2.8.5 primary chromaticities blue X. + * + * @param x Blue x + */ + public void setPrimaryChromaticitiesBlueX(Rational x) { + _primaryChromaticitiesBlueX = x; + } + + /** + * Set 8.2.8.6 primary chromaticities blue Y. + * + * @param y Blue y + */ + public void setPrimaryChromaticitiesBlueY(Rational y) { + _primaryChromaticitiesBlueY = y; + } + + /** + * Set 8.2.8.3 primary chromaticities green X. + * + * @param x Green x + */ + public void setPrimaryChromaticitiesGreenX(Rational x) { + _primaryChromaticitiesGreenX = x; + } + + /** + * Set 8.2.8.4 primary chromaticities green Y. + * + * @param y Green y + */ + public void setPrimaryChromaticitiesGreenY(Rational y) { + _primaryChromaticitiesGreenY = y; + } + + /** + * Set 8.2.8.1 primary chromaticities red X. + * + * @param x Red x + */ + public void setPrimaryChromaticitiesRedX(Rational x) { + _primaryChromaticitiesRedX = x; + } + + /** + * Set 8.2.8.2 primary chromaticities red Y. + * + * @param y Red y + */ + public void setPrimaryChromaticitiesRedY(Rational y) { + _primaryChromaticitiesRedY = y; + } + + /** + * Set 9.1.5 ProcessingActions. + * + * @param actions Array of strings giving image processing steps + */ + public void setProcessingActions(String[] actions) { + _processingActions = actions; + } + + /** + * Set 9.1.3 ProcessingAgency. + * + * @param processingAgency Identifier of producing organization + */ + public void setProcessingAgency(String processingAgency) { + _processingAgency = processingAgency; + } + + /** + * Set 9.1.4.1 ProcessingSoftwareName + * + * @param name Name of the image processing software + */ + public void setProcessingSoftwareName(String name) { + _processingSoftwareName = name; + } + + /** + * Set 9.1.4.2 ProcessingSoftwareVersion + * + * @param version Version number of the processing software + */ + public void setProcessingSoftwareVersion(String version) { + _processingSoftwareVersion = version; + } + + /** + * Set 6.1.4.1 ICC profile name. + * + * @param name Profile name + */ + public void setProfileName(String name) { + _profileName = name; + } + + /** + * Set 8.3.5 Profiles. + * + * @param profiles Color profile filename or URN + */ + public void setProfiles(String profiles) { + _profiles = profiles; + } + + /** + * Set 6.1.4.2 ICC profile URL. + * + * @param URL Profile URL + */ + public void setProfileURL(String URL) { + _profileURL = URL; + } + + /** + * Set 6.1.4.6 reference black and white. + * + * @param reference Reference + */ + public void setReferenceBlackWhite(Rational[] reference) { + _referenceBlackWhite = reference; + } + + /** + * Set 6.1.5.3 Rows per strip. + * + * @param rows Rows per strip + */ + public void setRowsPerStrip(long rows) { + _rowsPerStrip = rows; + } + + /** + * Set 8.1.1 sampling frequency plane. + * + * @param plane Sampling frequency plane + */ + public void setSamplingFrequencyPlane(int plane) { + _samplingFrequencyPlane = plane; + } + + /** + * Set 8.2.2 samples per pixel. + * + * @param samples Samples per pixel + */ + public void setSamplesPerPixel(int samples) { + _samplesPerPixel = samples; + } + + /** + * Set 8.1.2 sampling frequency unit. + * + * @param unit Sampling frequency unit + */ + public void setSamplingFrequencyUnit(int unit) { + _samplingFrequencyUnit = unit; + } + + /** + * Set 7.6.1.1 scanner manufacturer. + * + * @param manufacturer Scanner manufacturer + */ + public void setScannerManufacturer(String manufacturer) { + _scannerManufacturer = manufacturer; + } + + /** + * Set 7.6.1.2.1 scanner model name. + * + * @param name Scanner model name + */ + public void setScannerModelName(String name) { + _scannerModelName = name; + } + + /** + * Set 7.6.1.2.2 scanner model number. + * + * @param number Scanner model number + */ + public void setScannerModelNumber(String number) { + _scannerModelNumber = number; + } + + /** + * Set 7.6.1.2.3 scanner model serial number. + * + * @param number Scanner model serial number + */ + public void setScannerModelSerialNo(String number) { + _scannerModelSerialNo = number; + } + + /** + * Set 7.6.2.1 scanning software. + * + * @param software Scanning software + */ + public void setScanningSoftware(String software) { + _scanningSoftware = software; + } + + /** + * Set 7.6.2.2 scanning software version number. + * + * @param number Scanning software version number + */ + public void setScanningSoftwareVersionNo(String number) { + _scanningSoftwareVersionNo = number; + } + + /** + * Set 7.7.3.7 scene illuminant. + * + * @param illuminant Scene illuminant + */ + public void setSceneIlluminant(int illuminant) { + _sceneIlluminant = illuminant; + } + + /** + * Set 7.8 sensor. + * + * @param sensor Sensor + */ + public void setSensor(int sensor) { + _sensor = sensor; + } + + /** + * Set 9.1.2 SourceData. + * + * @param sourceData Source data identifier + */ + public void setSourceData(String sourceData) { + _sourceData = sourceData; + } + + /** + * Set 7.2 source ID. + * + * @param id Source ID + */ + public void setSourceID(String id) { + _sourceID = id; + } + + /** + * Set 7.1 source type. + * + * @param type Source type + */ + public void setSourceType(String type) { + _sourceType = type; + } + + /** + * Set 8.1.7 source X dimension. + * + * @param x X dimension + */ + public void setSourceXDimension(double x) { + _sourceXDimension = x; + } + + /** + * Set 8.1.7.1 source X dimension unit. + * + * @param unit X dimension unit + */ + public void setSourceXDimensionUnit(int unit) { + _sourceXDimensionUnit = unit; + } + + /** + * Set 8.1.8 source Y dimension. + * + * @param y Y dimension + */ + public void setSourceYDimension(double y) { + _sourceYDimension = y; + } + + /** + * Set 8.1.8.1 source Y dimension unit. + * + * @param unit Y dimension unit + */ + public void setSourceYDimensionUnit(int unit) { + _sourceYDimensionUnit = unit; + } + + /** + * Set 6.1.5.4 Strip byte counts. + * + * @param counts Byte counts + */ + public void setStripByteCounts(long[] counts) { + _stripByteCounts = counts; + } + + /** + * Set 6.1.5.2 Strip offsets. + * + * @param offsets Strip offsets + */ + public void setStripOffsets(long[] offsets) { + _stripOffsets = offsets; + } + + /** + * Set 7.7.3.5 Subject distance + * + * @param distance Subject distance + */ + public void setSubjectDistance(double[] distance) { + _subjectDistance = distance; + } + + /** Set 8.3.2.1 TargetIDManufacturer */ + public void setTargetIDManufacturer(String targetIDManufacturer) { + _targetIDManufacturer = targetIDManufacturer; + } + + /** Set 8.3.2.4 TargetIDMedia */ + public void setTargetIDMedia(String targetIDMedia) { + _targetIDMedia = targetIDMedia; + } + + /** Set 8.3.2.2 TargetIDName */ + public void setTargetIDName(String targetIDName) { + _targetIDName = targetIDName; + } + + /** Set 8.3.2.3 TargetIDNo */ + public void setTargetIDNo(String targetIDNo) { + _targetIDNo = targetIDNo; + } + + /** Set 8.3.1 TargetType */ + public void setTargetType(int targetType) { + _targetType = targetType; + } + + /** + * Set 6.1.5.8 Tile byte counts. + * + * @param counts Byte counts + */ + public void setTileByteCounts(long[] counts) { + _tileByteCounts = counts; + } + + /** + * Set 6.1.5.6 Tile length. + * + * @param length Tile length + */ + public void setTileLength(long length) { + _tileLength = length; + } + + /** + * Set 6.1.5.7 Tile offsets. + * + * @param offsets tile offsets + */ + public void setTileOffsets(long[] offsets) { + _tileOffsets = offsets; + } + + /** + * Set 6.1.5.5 Tile width. + * + * @param width Tile width + */ + public void setTileWidth(long width) { + _tileWidth = width; + } + + /** + * Set 8.2.7.1 white point X value. + * + * @param x White point X + */ + public void setWhitePointXValue(Rational x) { + _whitePointXValue = x; + } + + /** + * Set 8.2.7.2 white point Y value. + * + * @param y White point Y + */ + public void setWhitePointYValue(Rational y) { + _whitePointYValue = y; + } + + /** + * Set 7.6.3.2.1 X physical scanning resolution. + * + * @param x X physical scanning resolution + */ + public void setXPhysScanResolution(double x) { + _xPhysScanResolution = x; + } + + /** + * Set 7.7.3.16.1 X print aspect ratio. + * + * @param x X aspect ratio + */ + public void setXPrintAspectRatio(double x) { + _xPrintAspectRatio = x; + } + + /** + * Set 8.1.3 X sampling frequency. + * + * @param x X sampling frequency + */ + public void setXSamplingFrequency(Rational x) { + _xSamplingFrequency = x; + } + + /** + * Set 6.2.6.1 X targeted display aspect ratio. + * + * @param x X units + */ + public void setXTargetedDisplayAspectRatio(long x) { + _xTargetedDisplayAR = x; + } + + /** + * Set 6.1.4.5 YCbCr coefficients. + * + * @param coefficients Coefficients + */ + public void setYCbCrCoefficients(Rational[] coefficients) { + _yCbCrCoefficients = coefficients; + } + + /** + * Set 6.1.4.4 YCbCr positioning. + * + * @param positioning Positioning + */ + public void setYCbCrPositioning(int positioning) { + _yCbCrPositioning = positioning; + } + + /** + * Set 6.1.4.3 YCbCr Sub-sampling. + * + * @param sampling Sub-sampling + */ + public void setYCbCrSubSampling(int[] sampling) { + _yCbCrSubSampling = sampling; + } + + /** + * Set 7.6.3.2.2 Y physical scanning resolution. + * + * @param y Y physical scanning resolution + */ + public void setYPhysScanResolution(double y) { + _yPhysScanResolution = y; + } + + /** + * Set 7.7.3.16.2 Y print aspect ratio. + * + * @param y Y aspect ratio + */ + public void setYPrintAspectRatio(double y) { + _yPrintAspectRatio = y; + } + + /** + * Set 8.1.4 Y sampling frequency. + * + * @param y Y sampling frequency + */ + public void setYSamplingFrequency(Rational y) { + _ySamplingFrequency = y; + } + + /** + * Set 6.2.6.2 Y targeted display aspect ratio. + * + * @param y Y units + */ + public void setYTargetedDisplayAspectRatio(long y) { + _yTargetedDisplayAR = y; + } + + /** + * Set information for Swing GUI viewer. + * + * @param viewerData Private data for RepTreeModel + */ + public void setViewerData(Property viewerData) { + _viewerData = viewerData; + } + + /* Canonicizes (canonizes? whatever) a date to ISO + * 8601 format. Returns null if it can't make sense of + * it. Returns the date unchanged if it's already + * canonical. Initially this converts TIFF dates to ISO. + */ + private String make8601Valid(String date) { + try { + if (date.charAt(4) == ':') { + // It's a TIFF date, or a good imitation of one. + // TIFF dates have exact offsets, making things easy. + String yr = date.substring(0, 4); + String mo = date.substring(5, 7); + String da = date.substring(8, 10); + String hr = date.substring(11, 13); + String mi = date.substring(14, 16); + String se = date.substring(17, 19); + return yr + "-" + mo + "-" + da + "T" + hr + TIME_SEP + mi + TIME_SEP + se; + } + return date; // default + } catch (Exception e) { + // Malformed date + return null; + } + } + + public static String extractIccProfileDescription(byte[] data) throws IllegalArgumentException { + // Validate the ICCProfile with the java library + ICC_Profile profile = ICC_Profile.getInstance(data); + // Extract the 'desc' record (cf http://www.color.org/ICC1-V41.pdf) + byte[] dataProf = profile.getData(ICC_Profile.icSigProfileDescriptionTag); + if (dataProf == null) { + return null; + } + + String description = null; + ByteBuffer bb = ByteBuffer.wrap(dataProf).asReadOnlyBuffer(); + int majorVersion = profile.getMajorVersion(); + int minorVersion = profile.getMinorVersion(); + LOGGER.fine("Version " + majorVersion + "." + minorVersion); + + int beginTag = bb.getInt(0); + if (beginTag == ICC_Profile.icSigProfileDescriptionTag) { + // Invariant option + // ICC v2 http://www.color.org/ICC_Minor_Revision_for_Web.pdf + // Read only the ASCII form (cf 6.5.17 ICC v2) + final int OFFSET_LENGTH = 8; + final int OFFSET_DESC = OFFSET_LENGTH + 4; + + int lengthAscii = bb.getInt(OFFSET_LENGTH); + + // readString + byte[] asciiDesc = new byte[lengthAscii - 1]; + bb.position(OFFSET_DESC); + bb.get(asciiDesc); + + description = new String(asciiDesc, StandardCharsets.US_ASCII); + } else { + // ICC v4 http://www.color.org/ICC1-V41.pdf + final int OFFSET_NUMBER = 8; + final int MLUC_TAG = 0x6D6C7563; + final int OFFSET_NAME_LENGTH = 20; + final int OFFSET_NAME_OFFSET = 24; + + // 6.2 segment tag table definition + int tagMluc = bb.getInt(0); + + // Read the 1st mulc form (cf 6.5.12 ICC v4) + int nb = bb.getInt(OFFSET_NUMBER); + if (tagMluc != MLUC_TAG || nb < 1) { + throw new IllegalArgumentException(CoreMessageConstants.ERR_ICC_PRFL_DESC_MISS); + } + int firstNameLength = bb.getInt(OFFSET_NAME_LENGTH); + int firstNameOffset = bb.getInt(OFFSET_NAME_OFFSET); + // readString + byte[] desc = new byte[firstNameLength]; + bb.position(firstNameOffset); + bb.get(desc); + + // The Unicode strings in storage are encoded as 16-bit + // big-endian, UTF-16BE, and should not be NULL terminated. + description = new String(desc, StandardCharsets.UTF_16BE); + } + return description; + } } diff --git a/jhove-core/src/main/java/edu/harvard/hul/ois/jhove/ObjectIdentifier.java b/jhove-core/src/main/java/edu/harvard/hul/ois/jhove/ObjectIdentifier.java index 9dad6acc8..95cdcdc60 100644 --- a/jhove-core/src/main/java/edu/harvard/hul/ois/jhove/ObjectIdentifier.java +++ b/jhove-core/src/main/java/edu/harvard/hul/ois/jhove/ObjectIdentifier.java @@ -1,118 +1,105 @@ -/********************************************************************** - * Jhove - JSTOR/Harvard Object Validation Environment - * Copyright 2003-4 by JSTOR and the President and Fellows of Harvard College - **********************************************************************/ - +/** + * ******************************************************************** Jhove - JSTOR/Harvard Object + * Validation Environment Copyright 2003-4 by JSTOR and the President and Fellows of Harvard College + * ******************************************************************** + */ package edu.harvard.hul.ois.jhove; import java.io.*; import java.util.*; /** - * Module for identification of a document. "Identification" - * means determining, by querying modules successively, what the format - * of a document is. The Bytestream module is always queried last, - * so a document will by identified as a Bytestream if all else fails. + * Module for identification of a document. "Identification" means determining, by querying modules + * successively, what the format of a document is. The Bytestream module is always queried last, so + * a document will by identified as a Bytestream if all else fails. */ -public class ObjectIdentifier -{ - /****************************************************************** - * PRIVATE INSTANCE FIELDS. - ******************************************************************/ - - private List _moduleList; - - /****************************************************************** - * CLASS CONSTRUCTOR. - ******************************************************************/ - public ObjectIdentifier (List moduleList) - { - _moduleList = moduleList; - } +public class ObjectIdentifier { + /** + * **************************************************************** PRIVATE INSTANCE FIELDS. + * **************************************************************** + */ + private List _moduleList; + /** + * **************************************************************** CLASS CONSTRUCTOR. + * **************************************************************** + */ + public ObjectIdentifier(List moduleList) { + _moduleList = moduleList; + } - /****************************************************************** - * PUBLIC INSTANCE METHODS. - * - * Processing methods. - * - ******************************************************************/ + /** + * **************************************************************** PUBLIC INSTANCE METHODS. + * + *

Processing methods. + * + *

**************************************************************** + */ + /** + * Perform identification on a file. The file is parsed by each of the modules in the module list + * until one declares that the file is well-formed. It is assumed that there is a module in the + * list (normally Bytestream at the end) which will always consider a file well-formed. + */ + public void identify(File file, RepInfo info, String parm, boolean verbose, boolean shortCheck) { /** - * Perform identification on a file. The file is parsed by - * each of the modules in the module list until one - * declares that the file is well-formed. It is assumed - * that there is a module in the list (normally Bytestream - * at the end) which will always consider a file well-formed. + * **************************************************** Go through all modules, in the order in + * the config file, calling the parse method till we find one which matches. + * **************************************************** */ - public void identify (File file, RepInfo info, - String parm, boolean verbose, - boolean shortCheck) - { - /****************************************************** - * Go through all modules, in the order in the config - * file, calling the parse method till we find one - * which matches. - ******************************************************/ + ListIterator modIter = _moduleList.listIterator(); + while (modIter.hasNext()) { + /* We need clean RepInfo for each run */ + RepInfo info1; + info1 = (RepInfo) info.clone(); - ListIterator modIter = _moduleList.listIterator(); - while (modIter.hasNext ()) { - /* We need clean RepInfo for each run */ - RepInfo info1; - info1 = (RepInfo) info.clone (); - - Module mod = modIter.next (); - try { - if (!mod.hasFeature("edu.harvard.hul.ois.jhove.canValidate")) { - continue; + Module mod = modIter.next(); + try { + if (!mod.hasFeature("edu.harvard.hul.ois.jhove.canValidate")) { + continue; + } + if (mod.isRandomAccess()) { + try (RandomAccessFile raf = new RandomAccessFile(file, "r")) { + mod.param(parm); + if (verbose) { + mod.setVerbosity(Module.MAXIMUM_VERBOSITY); } - if (mod.isRandomAccess ()) { - try (RandomAccessFile raf = - new RandomAccessFile (file, "r")) { - mod.param (parm); - if (verbose) { - mod.setVerbosity (Module.MAXIMUM_VERBOSITY); - } - if (shortCheck) { - mod.checkSignatures (file, raf, info1); - } - else { - mod.parse (raf, info1); - } - } + if (shortCheck) { + mod.checkSignatures(file, raf, info1); + } else { + mod.parse(raf, info1); } - else { - mod.param (parm); - if (shortCheck) { - try (InputStream stream = new FileInputStream (file)) { - mod.checkSignatures (file, stream, info1); - } - } - else { - int parseIndex = 0; - try (InputStream stream = new FileInputStream (file)) { - parseIndex = mod.parse (stream, info1, 0); - } - while (parseIndex != 0) { - try (InputStream stream = new FileInputStream (file)) { - parseIndex = mod.parse (stream, info1, parseIndex); - } - } - } + } + } else { + mod.param(parm); + if (shortCheck) { + try (InputStream stream = new FileInputStream(file)) { + mod.checkSignatures(file, stream, info1); } + } else { + int parseIndex = 0; + try (InputStream stream = new FileInputStream(file)) { + parseIndex = mod.parse(stream, info1, 0); + } + while (parseIndex != 0) { + try (InputStream stream = new FileInputStream(file)) { + parseIndex = mod.parse(stream, info1, parseIndex); + } + } + } } - catch (Exception e) { - /* The assumption is that in trying to analyze - the wrong type of file, the module may go - off its track and throw an exception, so we - just continue on to the next module. - */ - continue; - } - if (info1.getWellFormed () == RepInfo.TRUE) { - info.copy (info1); - break; - } - } + } catch (Exception e) { + /* The assumption is that in trying to analyze + the wrong type of file, the module may go + off its track and throw an exception, so we + just continue on to the next module. + */ + continue; + } + if (info1.getWellFormed() == RepInfo.TRUE) { + info.copy(info1); + break; + } } + } } diff --git a/jhove-core/src/main/java/edu/harvard/hul/ois/jhove/OutputHandler.java b/jhove-core/src/main/java/edu/harvard/hul/ois/jhove/OutputHandler.java index 9667f137e..fb8e22095 100644 --- a/jhove-core/src/main/java/edu/harvard/hul/ois/jhove/OutputHandler.java +++ b/jhove-core/src/main/java/edu/harvard/hul/ois/jhove/OutputHandler.java @@ -1,196 +1,143 @@ -/********************************************************************** - * Jhove - JSTOR/Harvard Object Validation Environment - * Copyright 2003 by JSTOR and the President and Fellows of Harvard College - **********************************************************************/ - +/** + * ******************************************************************** Jhove - JSTOR/Harvard Object + * Validation Environment Copyright 2003 by JSTOR and the President and Fellows of Harvard College + * ******************************************************************** + */ package edu.harvard.hul.ois.jhove; import java.io.*; import java.util.*; /** - * Public interface for Jhove output handlers. - * All output handlers must implement OutputHandler, and in - * normal cases should subclass HandlerBase. + * Public interface for Jhove output handlers. All output handlers must implement OutputHandler, and + * in normal cases should subclass HandlerBase. */ -public interface OutputHandler -{ - - /** - * Reset the handler. This needs to be called before each invocation. - */ - public void reset (); - - /** - * Callback allowing post-parse, pre-show analysis of object - * representation information. - * @param info Object representation information - */ - public void analyze (RepInfo info); - - /** - * Callback indicating a directory is finished being processed. - * Most handlers will do nothing. - */ - public void endDirectory (); - - /** - * Returns the name of this handler - */ - public String getName (); - /** - * Returns release information for this handler - */ - public String getRelease (); - - /** - * Returns the last modification date of this handler - */ - public Date getDate (); - - /** - * Returns a List of Document objects giving the format - * specification documentation - * - * @see Document - */ - public List getSpecification (); - - /** - * Returns a List of Agent objects giving the vendor(s) - * of this handler. - */ - public Agent getVendor (); - - /** - * Returns this handler's note - */ - public String getNote (); - - /** - * Returns this handler's copyright information - */ - public String getRights (); - - /** - * Returns this handler's encoding. - */ - public String getEncoding (); - - /** - * Per-instantiation initialization. - * - * @param init Initialization parameter. This is typically obtained - * from the configuration file. - */ - public void init (String init) - throws Exception; - - /** - * Callback to give the handler the opportunity to decide whether or - * not to process a file. Most handlers will always return true. - * @param filepath File pathname - */ - public boolean okToProcess (String filepath); - - /** - * Sets list of default parameters. - * - * @param params A List whose elements are Strings. - * May be empty. - */ - public void setDefaultParams (List params); - - - /** - * Applies the default parameters. - */ - public void applyDefaultParams () - throws Exception; - - /** Reset parameter settings. - * Returns to a default state without any parameters. - */ - public void resetParams () - throws Exception; - - /** - * Per-action initialization. - * - * @param param Initialization parameter. - */ - public void param (String param) - throws Exception; - - /** - * Assigns an application object to provide services to this handler - */ - public void setApp (App app); - - /** - * Assigns the JHOVE engine object to provide services to this handler - */ - public void setBase (JhoveBase je); - - /** - * Assigns the encoding to be used by this OutputHandler - */ - public void setEncoding (String encoding); - - /** - * Assigns a PrintWriter to do output for this OutputHandler - */ - public void setWriter (PrintWriter output); - - /** - * Outputs information about a Module - */ - public void show (Module module); - - /** - * Outputs the information contained in a RepInfo object - */ - public void show (RepInfo info); - - /** - * Outputs information about the OutputHandler specified - * in the parameter - */ - public void show (OutputHandler handler); - - /** - * Outputs minimal information about the application - */ - public void show (); - - /** - * Outputs detailed information about the application, - * including configuration, available modules and handlers, - * etc. - */ - public void show (App app); - - /** - * Do the initial output. This should be in a suitable format - * for including multiple files between the header and the footer. - */ - public void showHeader (); - - /** - * Do the final output. This should be in a suitable format - * for including multiple files between the header and the footer. - */ - public void showFooter (); - - /** - * Do appropriate finalization after all output is complete. - */ - public void close (); - - /** - * Callback indicating a new directory is being processed. - * Most handlers will do nothing. - * @param directory Directory path - */ - public void startDirectory (String directory); +public interface OutputHandler { + + /** Reset the handler. This needs to be called before each invocation. */ + public void reset(); + + /** + * Callback allowing post-parse, pre-show analysis of object representation information. + * + * @param info Object representation information + */ + public void analyze(RepInfo info); + + /** Callback indicating a directory is finished being processed. Most handlers will do nothing. */ + public void endDirectory(); + + /** Returns the name of this handler */ + public String getName(); + /** Returns release information for this handler */ + public String getRelease(); + + /** Returns the last modification date of this handler */ + public Date getDate(); + + /** + * Returns a List of Document objects giving the format specification documentation + * + * @see Document + */ + public List getSpecification(); + + /** Returns a List of Agent objects giving the vendor(s) of this handler. */ + public Agent getVendor(); + + /** Returns this handler's note */ + public String getNote(); + + /** Returns this handler's copyright information */ + public String getRights(); + + /** Returns this handler's encoding. */ + public String getEncoding(); + + /** + * Per-instantiation initialization. + * + * @param init Initialization parameter. This is typically obtained from the configuration file. + */ + public void init(String init) throws Exception; + + /** + * Callback to give the handler the opportunity to decide whether or not to process a file. Most + * handlers will always return true. + * + * @param filepath File pathname + */ + public boolean okToProcess(String filepath); + + /** + * Sets list of default parameters. + * + * @param params A List whose elements are Strings. May be empty. + */ + public void setDefaultParams(List params); + + /** Applies the default parameters. */ + public void applyDefaultParams() throws Exception; + + /** Reset parameter settings. Returns to a default state without any parameters. */ + public void resetParams() throws Exception; + + /** + * Per-action initialization. + * + * @param param Initialization parameter. + */ + public void param(String param) throws Exception; + + /** Assigns an application object to provide services to this handler */ + public void setApp(App app); + + /** Assigns the JHOVE engine object to provide services to this handler */ + public void setBase(JhoveBase je); + + /** Assigns the encoding to be used by this OutputHandler */ + public void setEncoding(String encoding); + + /** Assigns a PrintWriter to do output for this OutputHandler */ + public void setWriter(PrintWriter output); + + /** Outputs information about a Module */ + public void show(Module module); + + /** Outputs the information contained in a RepInfo object */ + public void show(RepInfo info); + + /** Outputs information about the OutputHandler specified in the parameter */ + public void show(OutputHandler handler); + + /** Outputs minimal information about the application */ + public void show(); + + /** + * Outputs detailed information about the application, including configuration, available modules + * and handlers, etc. + */ + public void show(App app); + + /** + * Do the initial output. This should be in a suitable format for including multiple files between + * the header and the footer. + */ + public void showHeader(); + + /** + * Do the final output. This should be in a suitable format for including multiple files between + * the header and the footer. + */ + public void showFooter(); + + /** Do appropriate finalization after all output is complete. */ + public void close(); + + /** + * Callback indicating a new directory is being processed. Most handlers will do nothing. + * + * @param directory Directory path + */ + public void startDirectory(String directory); } diff --git a/jhove-core/src/main/java/edu/harvard/hul/ois/jhove/Property.java b/jhove-core/src/main/java/edu/harvard/hul/ois/jhove/Property.java index b3832cf5a..5bd9ad529 100644 --- a/jhove-core/src/main/java/edu/harvard/hul/ois/jhove/Property.java +++ b/jhove-core/src/main/java/edu/harvard/hul/ois/jhove/Property.java @@ -1,206 +1,183 @@ -/********************************************************************** - * Jhove - JSTOR/Harvard Object Validation Environment - * Copyright 2003 by JSTOR and the President and Fellows of Harvard College - **********************************************************************/ - +/** + * ******************************************************************** Jhove - JSTOR/Harvard Object + * Validation Environment Copyright 2003 by JSTOR and the President and Fellows of Harvard College + * ******************************************************************** + */ package edu.harvard.hul.ois.jhove; import java.util.*; /** - * This class encapsulates arbitrary format-specific properties. - * A Property's value can be a simple object or a structure. - * If it is a simple object, it has arity SCALAR. If it is - * a structure, it must be a Map, List, Set, or array, with - * the corresponding arity. The simple object (in the case of - * arity SCALAR) or the components of the structure must have a - * type corresponding to one of the enumerations given by - * PropertyType. + * This class encapsulates arbitrary format-specific properties. A Property's value can be a simple + * object or a structure. If it is a simple object, it has arity SCALAR. If it is a structure, it + * must be a Map, List, Set, or array, with the corresponding arity. The simple object (in the case + * of arity SCALAR) or the components of the structure must have a type corresponding to one of the + * enumerations given by PropertyType. * - * The components of a Property may themselves be Property - * objects, allowing nested structures. + *

The components of a Property may themselves be Property objects, allowing nested structures. * - * @see PropertyType - * @see PropertyArity + * @see PropertyType + * @see PropertyArity */ -public class Property -{ - /****************************************************************** - * PRIVATE INSTANCE FIELDS. - ******************************************************************/ - - private String _name; - private PropertyType _type; - private PropertyArity _arity; - private Object _value; - - - /****************************************************************** - * CLASS CONSTRUCTOR. - ******************************************************************/ - - /** - * Creates a Property with arity SCALAR. - * - * @param name The displayable name of the property - * @param type The type of property - * @param value The value of the property. The type of the - * parameter must agree with type. - */ - public Property (String name, PropertyType type, Object value) - { - init (name, type, PropertyArity.SCALAR, value); - } - - /** - * Creates a Property with a given name, type, arity and value. - * - * @param name The displayable name of the property. - * @param type The type of property. - * @param arity The arity of the property. - * @param value The value of the property. If the arity is - * SCALAR, the type of this parameter must agree - * with type. Otherwise, the arity - * must agree with arity, and its - * components must agree with type. - */ - public Property (String name, PropertyType type, PropertyArity arity, - Object value) - { - init (name, type, arity, value); - } - - private void init (String name, PropertyType type, PropertyArity arity, - Object value) - { - /* Some limited type checking. Checking for mismatched - types here may help avoid difficult chasing down - of the bugs such mismatches cause. */ - if (value == null) { - throw new NullPointerException (CoreMessageConstants.EXC_PROP_VAL_NULL); - } - if ((arity == PropertyArity.SCALAR) && !isObjScalarProp(value)) { - throw new IncompatibleClassChangeError - (String.format(CoreMessageConstants.EXC_SCL_PROP_CLSS_INCMPT, CoreMessageConstants.EXC_PROP_CLSS_INCMPT)); - } - else if ((arity == PropertyArity.MAP) && (!(value instanceof Map))) { - throw new IncompatibleClassChangeError - (String.format(CoreMessageConstants.EXC_MAP_PROP_CLSS_INCMPT, CoreMessageConstants.EXC_PROP_CLSS_INCMPT)); - } - else if ((arity == PropertyArity.SET) && (!(value instanceof Set))) { - throw new IncompatibleClassChangeError - (String.format(CoreMessageConstants.EXC_SET_PROP_CLSS_INCMPT, CoreMessageConstants.EXC_PROP_CLSS_INCMPT)); - } - else if ((arity == PropertyArity.LIST) && (!(value instanceof List))) { - throw new IncompatibleClassChangeError - (String.format(CoreMessageConstants.EXC_LIST_PROP_CLSS_INCMPT, CoreMessageConstants.EXC_PROP_CLSS_INCMPT)); - } - - _name = name; - _type = type; - _arity = arity; - _value = value; - } - - private static boolean isObjScalarProp(Object toTest) { - return !(toTest instanceof List || - toTest instanceof Map || - toTest instanceof Set); +public class Property { + /** + * **************************************************************** PRIVATE INSTANCE FIELDS. + * **************************************************************** + */ + private String _name; + + private PropertyType _type; + private PropertyArity _arity; + private Object _value; + + /** + * **************************************************************** CLASS CONSTRUCTOR. + * **************************************************************** + */ + + /** + * Creates a Property with arity SCALAR. + * + * @param name The displayable name of the property + * @param type The type of property + * @param value The value of the property. The type of the parameter must agree with type + * . + */ + public Property(String name, PropertyType type, Object value) { + init(name, type, PropertyArity.SCALAR, value); + } + + /** + * Creates a Property with a given name, type, arity and value. + * + * @param name The displayable name of the property. + * @param type The type of property. + * @param arity The arity of the property. + * @param value The value of the property. If the arity is SCALAR, the type of this parameter must + * agree with type. Otherwise, the arity must agree with arity, and + * its components must agree with type. + */ + public Property(String name, PropertyType type, PropertyArity arity, Object value) { + init(name, type, arity, value); + } + + private void init(String name, PropertyType type, PropertyArity arity, Object value) { + /* Some limited type checking. Checking for mismatched + types here may help avoid difficult chasing down + of the bugs such mismatches cause. */ + if (value == null) { + throw new NullPointerException(CoreMessageConstants.EXC_PROP_VAL_NULL); } - /****************************************************************** - * PUBLIC INSTANCE METHODS. - * - * Accessor methods. - ******************************************************************/ - - /** - * Returns the arity (type of structure) of this Property. - */ - public PropertyArity getArity () - { - return _arity; + if ((arity == PropertyArity.SCALAR) && !isObjScalarProp(value)) { + throw new IncompatibleClassChangeError( + String.format( + CoreMessageConstants.EXC_SCL_PROP_CLSS_INCMPT, + CoreMessageConstants.EXC_PROP_CLSS_INCMPT)); + } else if ((arity == PropertyArity.MAP) && (!(value instanceof Map))) { + throw new IncompatibleClassChangeError( + String.format( + CoreMessageConstants.EXC_MAP_PROP_CLSS_INCMPT, + CoreMessageConstants.EXC_PROP_CLSS_INCMPT)); + } else if ((arity == PropertyArity.SET) && (!(value instanceof Set))) { + throw new IncompatibleClassChangeError( + String.format( + CoreMessageConstants.EXC_SET_PROP_CLSS_INCMPT, + CoreMessageConstants.EXC_PROP_CLSS_INCMPT)); + } else if ((arity == PropertyArity.LIST) && (!(value instanceof List))) { + throw new IncompatibleClassChangeError( + String.format( + CoreMessageConstants.EXC_LIST_PROP_CLSS_INCMPT, + CoreMessageConstants.EXC_PROP_CLSS_INCMPT)); } - /** - * Return a property by its name, regardless of its position in the - * structural hierarchy of properties. - * @param name Property name - * @return Named property (or null) - */ - public Property getByName (String name) - { - if (_name.equals (name)) { - return this; - } - - if (!_arity.equals (PropertyArity.SCALAR) && - _type.equals (PropertyType.PROPERTY)) { - if (_arity.equals (PropertyArity.ARRAY)) { - Property [] array = (Property []) _value; - for (int i=0; i list = (List) _value; - int len = list.size (); - for (int i=0; i coll = ((Map) _value).values (); - Iterator iter = coll.iterator (); - while (iter.hasNext ()) { - Property prop = iter.next ().getByName (name); - if (prop != null) { - return prop; - } - } - } - else if (_arity.equals (PropertyArity.SET)) { - Iterator iter = ((Set) _value).iterator (); - while (iter.hasNext ()) { - Property prop = iter.next ().getByName (name); - if (prop != null) { - return prop; - } - } - } - } - - return null; + _name = name; + _type = type; + _arity = arity; + _value = value; + } + + private static boolean isObjScalarProp(Object toTest) { + return !(toTest instanceof List || toTest instanceof Map || toTest instanceof Set); + } + /** + * **************************************************************** PUBLIC INSTANCE METHODS. + * + *

Accessor methods. **************************************************************** + */ + + /** Returns the arity (type of structure) of this Property. */ + public PropertyArity getArity() { + return _arity; + } + + /** + * Return a property by its name, regardless of its position in the structural hierarchy of + * properties. + * + * @param name Property name + * @return Named property (or null) + */ + public Property getByName(String name) { + if (_name.equals(name)) { + return this; } - /** - * Returns the displayable name of this Property. - */ - public String getName () - { - return _name; + if (!_arity.equals(PropertyArity.SCALAR) && _type.equals(PropertyType.PROPERTY)) { + if (_arity.equals(PropertyArity.ARRAY)) { + Property[] array = (Property[]) _value; + for (int i = 0; i < array.length; i++) { + Property prop = array[i].getByName(name); + if (prop != null) { + return prop; + } + } + } else if (_arity.equals(PropertyArity.LIST)) { + List list = (List) _value; + int len = list.size(); + for (int i = 0; i < len; i++) { + Property prop = list.get(i).getByName(name); + if (prop != null) { + return prop; + } + } + } else if (_arity.equals(PropertyArity.MAP)) { + Collection coll = ((Map) _value).values(); + Iterator iter = coll.iterator(); + while (iter.hasNext()) { + Property prop = iter.next().getByName(name); + if (prop != null) { + return prop; + } + } + } else if (_arity.equals(PropertyArity.SET)) { + Iterator iter = ((Set) _value).iterator(); + while (iter.hasNext()) { + Property prop = iter.next().getByName(name); + if (prop != null) { + return prop; + } + } + } } - /** - * Returns the type of this Property. - * If the arity is other than SCALAR, the type refers to the - * compononents of the Property structure. - */ - public PropertyType getType () - { - return _type; - } - - /** - * Returns the Object which is the Property's value. - */ - public Object getValue () - { - return _value; - } + return null; + } + + /** Returns the displayable name of this Property. */ + public String getName() { + return _name; + } + + /** + * Returns the type of this Property. If the arity is other than SCALAR, the type refers to the + * compononents of the Property structure. + */ + public PropertyType getType() { + return _type; + } + + /** Returns the Object which is the Property's value. */ + public Object getValue() { + return _value; + } } diff --git a/jhove-core/src/main/java/edu/harvard/hul/ois/jhove/PropertyArity.java b/jhove-core/src/main/java/edu/harvard/hul/ois/jhove/PropertyArity.java index 4d0dfd416..d6883708a 100644 --- a/jhove-core/src/main/java/edu/harvard/hul/ois/jhove/PropertyArity.java +++ b/jhove-core/src/main/java/edu/harvard/hul/ois/jhove/PropertyArity.java @@ -1,60 +1,49 @@ -/********************************************************************** - * Jhove - JSTOR/Harvard Object Validation Environment - * Copyright 2003 by JSTOR and the President and Fellows of Harvard College - **********************************************************************/ - +/** + * ******************************************************************** Jhove - JSTOR/Harvard Object + * Validation Environment Copyright 2003 by JSTOR and the President and Fellows of Harvard College + * ******************************************************************** + */ package edu.harvard.hul.ois.jhove; /** - * This class represents the arity (structure type) of a Property. - * Applications will not create or modify PropertyArities, but will - * use one of the predefined PropertyArity instances - * ARRAY, LIST, MAP, SCALAR, or SET. + * This class represents the arity (structure type) of a Property. Applications will not create or + * modify PropertyArities, but will use one of the predefined PropertyArity instances ARRAY, LIST, + * MAP, SCALAR, or SET. * * @see Property */ public enum PropertyArity { - /** - * An Arity corresponding to a Java array. For the Java types - * Integer, Boolean, Byte, - * Character, Double, - * Float, Long, and - * Short, a Property with arity ARRAY is an array of primitive - * Java types rather than Objects (e.g., int - * rather than Integer). - */ - ARRAY("Array"), - - /** - * An Arity corresponding to java.util.List or any of its derived classes. - */ - LIST("List"), - - /** - * An Arity corresponding to java.util.Map or any of its derived classes. - */ - MAP("Map"), - - /** - * An Arity corresponding to a simple object, which must be of a - * type corresponding to one of the instances of - * PropertyType. - */ - SCALAR("Scalar"), - - /** - * An Arity corresponding to java.util.Set or any of its derived classes. - */ - SET("Set"); - /** A String name for the Arity, used for reporting. */ - public final String name; - - private PropertyArity(final String name) { - this.name = name; - } - - @Override - public String toString() { - return this.name; - } + /** + * An Arity corresponding to a Java array. For the Java types Integer, Boolean + * , Byte, Character, Double, Float, + * Long, and Short, a Property with arity ARRAY is an array of primitive + * Java types rather than Objects (e.g., int rather than Integer). + */ + ARRAY("Array"), + + /** An Arity corresponding to java.util.List or any of its derived classes. */ + LIST("List"), + + /** An Arity corresponding to java.util.Map or any of its derived classes. */ + MAP("Map"), + + /** + * An Arity corresponding to a simple object, which must be of a type corresponding to one of the + * instances of PropertyType. + */ + SCALAR("Scalar"), + + /** An Arity corresponding to java.util.Set or any of its derived classes. */ + SET("Set"); + /** A String name for the Arity, used for reporting. */ + public final String name; + + private PropertyArity(final String name) { + this.name = name; + } + + @Override + public String toString() { + return this.name; + } } diff --git a/jhove-core/src/main/java/edu/harvard/hul/ois/jhove/PropertyType.java b/jhove-core/src/main/java/edu/harvard/hul/ois/jhove/PropertyType.java index 7e7649a86..bad15ebf1 100644 --- a/jhove-core/src/main/java/edu/harvard/hul/ois/jhove/PropertyType.java +++ b/jhove-core/src/main/java/edu/harvard/hul/ois/jhove/PropertyType.java @@ -1,104 +1,76 @@ -/********************************************************************** - * Jhove - JSTOR/Harvard Object Validation Environment - * Copyright 2003-2009 by JSTOR and the President and Fellows of Harvard College - **********************************************************************/ - +/** + * ******************************************************************** Jhove - JSTOR/Harvard Object + * Validation Environment Copyright 2003-2009 by JSTOR and the President and Fellows of Harvard + * College ******************************************************************** + */ package edu.harvard.hul.ois.jhove; /** - * This class defines enumerated types for an Property of - * some given content. - * Applications will not create or modify PropertyTypes, but will - * use one of the predefined PropertyType instances - * BOOLEAN, BYTE, CHARACTER, DATE, DOUBLE, FLOAT, INTEGER, - * LONG, OBJECT, PROPERTY, SHORT, STRING, RATIONAL, or - * NISOIMAGEMETADATA. + * This class defines enumerated types for an Property of some given content. Applications will not + * create or modify PropertyTypes, but will use one of the predefined PropertyType instances + * BOOLEAN, BYTE, CHARACTER, DATE, DOUBLE, FLOAT, INTEGER, LONG, OBJECT, PROPERTY, SHORT, STRING, + * RATIONAL, or NISOIMAGEMETADATA. * * @see Property */ public enum PropertyType { - /** - * Property type for a Boolean object, or a - * boolean if the Arity is Array. - */ - BOOLEAN("Boolean"), - /** - * Property type for a Byte object, or a byte - * if the Arity is Array. - */ - BYTE("Byte"), - /** - * Property type for a Character object, or a - * char if the Arity is Array. - */ - CHARACTER("Character"), - /** - * Property type for a Date object. - */ - DATE("Date"), - /** - * Property type for a Double object, or - * a double if the Arity is Array. - */ - DOUBLE("Double"), - /** - * Property type for a Float object, or a - * float if the Arity is Array. - */ - FLOAT("Float"), - /** - * Property type for an Integer object, or an - * integer if the Arity is Array. - */ - INTEGER("Integer"), - /** - * Property type for a Long object, or a - * long if the Arity is Array. - */ - LONG("Long"), - /** - * Property type for an Object. - */ - OBJECT("Object"), - /** - * Property type for an AESAudioMetadata. - */ - AESAUDIOMETADATA("AESAudioMetadata"), - /** - * Property type for a NisoImageMetadata. - */ - NISOIMAGEMETADATA("NISOImageMetadata"), - /** - * Property type for a TextMDMetadata. - */ - TEXTMDMETADATA("TextMDMetadata"), - /** - * Property type for a Property object. - */ - PROPERTY("Property"), - /** - * Property type for a Short object, or a - * short if the Arity is Array. - */ - SHORT("Short"), - /** - * Property type for a String object. - */ - STRING("String"), - /** - * Property type for a Rational object. - */ - RATIONAL("Rational"); + /** + * Property type for a Boolean object, or a boolean if the Arity is + * Array. + */ + BOOLEAN("Boolean"), + /** Property type for a Byte object, or a byte if the Arity is Array. */ + BYTE("Byte"), + /** + * Property type for a Character object, or a char if the Arity is + * Array. + */ + CHARACTER("Character"), + /** Property type for a Date object. */ + DATE("Date"), + /** + * Property type for a Double object, or a double if the Arity is Array. + */ + DOUBLE("Double"), + /** + * Property type for a Float object, or a float if the Arity is Array. + */ + FLOAT("Float"), + /** + * Property type for an Integer object, or an integer if the Arity is + * Array. + */ + INTEGER("Integer"), + /** Property type for a Long object, or a long if the Arity is Array. */ + LONG("Long"), + /** Property type for an Object. */ + OBJECT("Object"), + /** Property type for an AESAudioMetadata. */ + AESAUDIOMETADATA("AESAudioMetadata"), + /** Property type for a NisoImageMetadata. */ + NISOIMAGEMETADATA("NISOImageMetadata"), + /** Property type for a TextMDMetadata. */ + TEXTMDMETADATA("TextMDMetadata"), + /** Property type for a Property object. */ + PROPERTY("Property"), + /** + * Property type for a Short object, or a short if the Arity is Array. + */ + SHORT("Short"), + /** Property type for a String object. */ + STRING("String"), + /** Property type for a Rational object. */ + RATIONAL("Rational"); - /** A String name for the type, used for reporting. */ - public final String name; + /** A String name for the type, used for reporting. */ + public final String name; - private PropertyType(final String name) { - this.name = name; - } + private PropertyType(final String name) { + this.name = name; + } - @Override - public String toString() { - return this.name; - } + @Override + public String toString() { + return this.name; + } } diff --git a/jhove-core/src/main/java/edu/harvard/hul/ois/jhove/RAFInputStream.java b/jhove-core/src/main/java/edu/harvard/hul/ois/jhove/RAFInputStream.java index f0bf11092..b58d2c923 100644 --- a/jhove-core/src/main/java/edu/harvard/hul/ois/jhove/RAFInputStream.java +++ b/jhove-core/src/main/java/edu/harvard/hul/ois/jhove/RAFInputStream.java @@ -1,8 +1,8 @@ -/********************************************************************** - * JHOVE - JSTOR/Harvard Object Validation Environment - * Copyright 2004 by JSTOR and the President and Fellows of Harvard College - **********************************************************************/ - +/** + * ******************************************************************** JHOVE - JSTOR/Harvard Object + * Validation Environment Copyright 2004 by JSTOR and the President and Fellows of Harvard College + * ******************************************************************** + */ package edu.harvard.hul.ois.jhove; import java.io.IOException; @@ -10,276 +10,244 @@ import java.io.RandomAccessFile; /** - * An InputStream layered on top of a RandomAccessFile. - * This is useful for a Module which has requirements that - * force it to use a RandomAccessFile, but is usually - * accessed sequentially. - * - * An RAFInputStream maintains its own position information - * in the file, so multiple RAFInputStreams in the same file - * will work without interference. However, this class is - * not thread-safe. - * + * An InputStream layered on top of a RandomAccessFile. This is useful for a Module which has + * requirements that force it to use a RandomAccessFile, but is usually accessed sequentially. + * + *

An RAFInputStream maintains its own position information in the file, so multiple + * RAFInputStreams in the same file will work without interference. However, this class is not + * thread-safe. + * * @author Gary McGath */ public class RAFInputStream extends InputStream { - /** Default file buffer size */ - private static int DEFAULT_BUFFER_SIZE = 65536; - - /** The file on which the stream is based */ - private RandomAccessFile _raf; - - /** Size of fileBuf */ - private int fileBufSize; - - /** Buffer for reading from the file */ - private byte[] fileBuf; - - /** Offset for reading next byte from fileBuf */ - private int fileBufPos; - - /** Number of valid bytes in fileBuf */ - private int fileBufBytes; - - /** Position in file for next read from RandomAccessFile */ - private long fileOffset; - - /** End-of-file flag */ - private boolean eof; - - /** - * Constructor with default buffer size. - * - * The stream starts at the current position of the RandomAccessFile. - * - * @param raf the file on which the stream is to be based. - */ - public RAFInputStream(RandomAccessFile raf) - { - super(); - _raf = raf; - fileBufSize = DEFAULT_BUFFER_SIZE; - init(); + /** Default file buffer size */ + private static int DEFAULT_BUFFER_SIZE = 65536; + + /** The file on which the stream is based */ + private RandomAccessFile _raf; + + /** Size of fileBuf */ + private int fileBufSize; + + /** Buffer for reading from the file */ + private byte[] fileBuf; + + /** Offset for reading next byte from fileBuf */ + private int fileBufPos; + + /** Number of valid bytes in fileBuf */ + private int fileBufBytes; + + /** Position in file for next read from RandomAccessFile */ + private long fileOffset; + + /** End-of-file flag */ + private boolean eof; + + /** + * Constructor with default buffer size. + * + *

The stream starts at the current position of the RandomAccessFile. + * + * @param raf the file on which the stream is to be based. + */ + public RAFInputStream(RandomAccessFile raf) { + super(); + _raf = raf; + fileBufSize = DEFAULT_BUFFER_SIZE; + init(); + } + + /** + * Constructor with buffer size. + * + *

The stream starts at the current position of the RandomAccessFile. + * + * @param raf the file on which the stream is to be based. + * @param bufferSize the buffer size to be used. If less than or equal to 0, the default buffer + * size is used. + */ + public RAFInputStream(RandomAccessFile raf, int bufferSize) { + super(); + _raf = raf; + fileBufSize = (bufferSize <= 0 ? DEFAULT_BUFFER_SIZE : bufferSize); + init(); + } + + private void init() { + fileBufBytes = 0; + fileBufPos = 0; + fileBuf = new byte[fileBufSize]; + try { + fileOffset = _raf.getFilePointer(); + } catch (IOException e) { } - - /** - * Constructor with buffer size. - * - * The stream starts at the current position of the RandomAccessFile. - * - * @param raf the file on which the stream is to be based. - * - * @param bufferSize the buffer size to be used. If less than or - * equal to 0, the default buffer size is used. - */ - public RAFInputStream(RandomAccessFile raf, int bufferSize) - { - super(); - _raf = raf; - fileBufSize = (bufferSize <= 0 ? DEFAULT_BUFFER_SIZE : bufferSize); - init(); + eof = false; + } + + /** + * Seeks to the next offset and fills the file buffer. Sets eof to true + * if the offset to read is beyond the end of file. + */ + private void fillFileBuffer() throws IOException { + _raf.seek(fileOffset); + fileBufBytes = _raf.read(fileBuf); + fileBufPos = 0; + if (fileBufBytes <= 0) { + eof = true; } - - private void init() - { - fileBufBytes = 0; - fileBufPos = 0; - fileBuf = new byte[fileBufSize]; - try { - fileOffset = _raf.getFilePointer(); - } - catch (IOException e) {} - eof = false; + fileOffset += fileBufBytes; + } + + /** Reads a single byte from the file. */ + @Override + public int read() throws IOException { + if (eof) { + return -1; } - - /** - * Seeks to the next offset and fills the file buffer. Sets eof - * to true if the offset to read is beyond the end of file. - */ - private void fillFileBuffer() throws IOException - { - _raf.seek(fileOffset); - fileBufBytes = _raf.read(fileBuf); - fileBufPos = 0; - if (fileBufBytes <= 0) { - eof = true; - } - fileOffset += fileBufBytes; + if (fileBufPos >= fileBufBytes) { + // Need to read another bufferful + fillFileBuffer(); + if (eof) { + return -1; + } } - - /** - * Reads a single byte from the file. - */ - @Override - public int read() throws IOException - { + return (fileBuf[fileBufPos++] & 0xFF); + } + + /** + * Reads some number of bytes from the input stream and stores them into the buffer array b. The + * number of bytes actually read is returned as an integer. + */ + @Override + public int read(byte[] b) throws IOException { + int bytesToRead = b.length; + int bytesRead = 0; + for (; ; ) { + // See how many bytes are available in fileBuf. + int fbAvail = fileBufBytes - fileBufPos; + if (fbAvail <= 0) { + // Need to read another bufferful + fillFileBuffer(); if (eof) { - return -1; - } - if (fileBufPos >= fileBufBytes) { - // Need to read another bufferful - fillFileBuffer(); - if (eof) { - return -1; - } - } - return (fileBuf[fileBufPos++] & 0xFF); - } - - /** - * Reads some number of bytes from the input stream and - * stores them into the buffer array b. The number of - * bytes actually read is returned as an integer. - */ - @Override - public int read(byte[] b) throws IOException - { - int bytesToRead = b.length; - int bytesRead = 0; - for (;;) { - // See how many bytes are available in fileBuf. - int fbAvail = fileBufBytes - fileBufPos; - if (fbAvail <= 0) { - // Need to read another bufferful - fillFileBuffer(); - if (eof) { - // No more in file -- return what we have - return bytesRead; - } - fbAvail = fileBufBytes; - } - if (fbAvail > bytesToRead) { - // We have more than enough bytes. - fbAvail = bytesToRead; - } - for (int i = 0; i < fbAvail; i++) { - b[bytesRead++] = fileBuf[fileBufPos++]; - bytesToRead--; - } - if (bytesToRead == 0) { - return bytesRead; - } + // No more in file -- return what we have + return bytesRead; } + fbAvail = fileBufBytes; + } + if (fbAvail > bytesToRead) { + // We have more than enough bytes. + fbAvail = bytesToRead; + } + for (int i = 0; i < fbAvail; i++) { + b[bytesRead++] = fileBuf[fileBufPos++]; + bytesToRead--; + } + if (bytesToRead == 0) { + return bytesRead; + } } - - /** - * Reads up to len bytes of data from the input stream - * into an array of bytes. An attempt is made to read as - * many as len bytes, but a smaller number may be read, - * possibly zero. The number of bytes actually read is - * returned as an integer. - */ - @Override - public int read(byte[] b, int off, int len) throws IOException - { - int bytesToRead = len; - int bytesRead = 0; - for (;;) { - // See how many bytes are available in fileBuf. - int fbAvail = fileBufBytes - fileBufPos; - if (fbAvail <= 0) { - // Need to read another bufferful - fillFileBuffer(); - if (eof) { - // No more in file -- return what we have - return bytesRead; - } - fbAvail = fileBufBytes; - } - if (fbAvail > bytesToRead) { - // We have more than enough bytes. - fbAvail = bytesToRead; - } - for (int i = 0; i < fbAvail; i++) { - b[off + bytesRead++] = fileBuf[fileBufPos++]; - bytesToRead--; - } - if (bytesToRead == 0) { - return bytesRead; - } - } - } - - /** - * Skips some number of bytes. - * - * @return the number of bytes actually skipped. - */ - @Override - public long skip(long n) throws IOException - { - // If the range of the skip lies within the current buffer, - // we simply adjust our position in the buffer. - int bytesLeft = fileBufBytes - fileBufPos; - if (bytesLeft > n) { - fileBufPos += (int) n; - } - else { - // The bytes to skip don't lie within the current buffer, - // so we set the next file seek location instead. - if (fileOffset + n - bytesLeft > _raf.length()) { - fileOffset = _raf.length(); - } - else { - fileOffset += n - bytesLeft; - } - fileBufBytes = 0; // Invalidate current buffer - } - return n; - } - - /** - * Returns an estimate of the number of bytes that can be read (or - * skipped over) from this input stream without blocking. A single read or - * skip of this many bytes will not block, but may read or skip fewer bytes. - * - * @return an estimate of the number of bytes that can be read (or skipped - * over) from this input stream without blocking, or 0 - * when it reaches the end of the input stream. - */ - @Override - public int available() throws IOException - { - if (eof) return 0; - if (fileBufPos >= fileBufBytes) { - // The buffer has been read through and needs refilling - // before we can check for any remaining available bytes. - fillFileBuffer(); - if (eof) return 0; + } + + /** + * Reads up to len bytes of data from the input stream into an array of bytes. An attempt is made + * to read as many as len bytes, but a smaller number may be read, possibly zero. The number of + * bytes actually read is returned as an integer. + */ + @Override + public int read(byte[] b, int off, int len) throws IOException { + int bytesToRead = len; + int bytesRead = 0; + for (; ; ) { + // See how many bytes are available in fileBuf. + int fbAvail = fileBufBytes - fileBufPos; + if (fbAvail <= 0) { + // Need to read another bufferful + fillFileBuffer(); + if (eof) { + // No more in file -- return what we have + return bytesRead; } - return fileBufBytes - fileBufPos; - } - - /** - * Returns the RandomAccessFile object. - */ - public RandomAccessFile getRAF() - { - return _raf; + fbAvail = fileBufBytes; + } + if (fbAvail > bytesToRead) { + // We have more than enough bytes. + fbAvail = bytesToRead; + } + for (int i = 0; i < fbAvail; i++) { + b[off + bytesRead++] = fileBuf[fileBufPos++]; + bytesToRead--; + } + if (bytesToRead == 0) { + return bytesRead; + } } - - /** - * Positions the stream to a different point in the file, - * invalidating the buffer. - */ - public void seek(long offset) throws IOException - { - _raf.seek(offset); - fileBufBytes = 0; - fileBufPos = 0; - eof = false; + } + + /** + * Skips some number of bytes. + * + * @return the number of bytes actually skipped. + */ + @Override + public long skip(long n) throws IOException { + // If the range of the skip lies within the current buffer, + // we simply adjust our position in the buffer. + int bytesLeft = fileBufBytes - fileBufPos; + if (bytesLeft > n) { + fileBufPos += (int) n; + } else { + // The bytes to skip don't lie within the current buffer, + // so we set the next file seek location instead. + if (fileOffset + n - bytesLeft > _raf.length()) { + fileOffset = _raf.length(); + } else { + fileOffset += n - bytesLeft; + } + fileBufBytes = 0; // Invalidate current buffer } - - /** - * Returns the current position in the file. - * What is reported is the position of the byte - * in the file which was last extracted from - * the buffer. - */ - public long getFilePos() throws IOException - { - return _raf.getFilePointer() - - (fileBufBytes - fileBufPos); + return n; + } + + /** + * Returns an estimate of the number of bytes that can be read (or skipped over) from this input + * stream without blocking. A single read or skip of this many bytes will not block, but may read + * or skip fewer bytes. + * + * @return an estimate of the number of bytes that can be read (or skipped over) from this input + * stream without blocking, or 0 when it reaches the end of the input stream. + */ + @Override + public int available() throws IOException { + if (eof) return 0; + if (fileBufPos >= fileBufBytes) { + // The buffer has been read through and needs refilling + // before we can check for any remaining available bytes. + fillFileBuffer(); + if (eof) return 0; } + return fileBufBytes - fileBufPos; + } + + /** Returns the RandomAccessFile object. */ + public RandomAccessFile getRAF() { + return _raf; + } + + /** Positions the stream to a different point in the file, invalidating the buffer. */ + public void seek(long offset) throws IOException { + _raf.seek(offset); + fileBufBytes = 0; + fileBufPos = 0; + eof = false; + } + + /** + * Returns the current position in the file. What is reported is the position of the byte in the + * file which was last extracted from the buffer. + */ + public long getFilePos() throws IOException { + return _raf.getFilePointer() - (fileBufBytes - fileBufPos); + } } diff --git a/jhove-core/src/main/java/edu/harvard/hul/ois/jhove/RFC1766Lang.java b/jhove-core/src/main/java/edu/harvard/hul/ois/jhove/RFC1766Lang.java index 147371646..21a1d307e 100644 --- a/jhove-core/src/main/java/edu/harvard/hul/ois/jhove/RFC1766Lang.java +++ b/jhove-core/src/main/java/edu/harvard/hul/ois/jhove/RFC1766Lang.java @@ -1,88 +1,73 @@ - -/********************************************************************** - * Jhove - JSTOR/Harvard Object Validation Environment - * Copyright 2003 by JSTOR and the President and Fellows of Harvard College - **********************************************************************/ - - +/** + * ******************************************************************** Jhove - JSTOR/Harvard Object + * Validation Environment Copyright 2003 by JSTOR and the President and Fellows of Harvard College + * ******************************************************************** + */ package edu.harvard.hul.ois.jhove; +/** Class encapsulating RFC-1766 language codes. */ +public final class RFC1766Lang { -/** - * Class encapsulating RFC-1766 language codes. - */ -public final class RFC1766Lang -{ + String _langCode; - String _langCode; + /** + * Constructor. + * + * @param str The ASCII string for the language code. + */ + public RFC1766Lang(String str) { + _langCode = str; + } - /** - * Constructor. - * - * @param str The ASCII string for the language code. - */ - public RFC1766Lang (String str) - { - _langCode = str; - } + /** Returns the language code string. */ + public String getLangCode() { + return _langCode; + } - /** - * Returns the language code string. - */ - public String getLangCode () - { - return _langCode; + /** + * Returns true if the language code string is syntactically compliant. The primary + * tag must be either a two-letter code, the letter i, or the letter x (case insensitive); no + * checking is done against registry lists. + */ + public boolean isSyntaxCorrect() { + int i; + if (_langCode == null) { + return false; } + char[] chrs = _langCode.toLowerCase().toCharArray(); + char firstChar = '\0'; - /** - * Returns true if the language code - * string is syntactically compliant. - * The primary tag must be either a two-letter code, - * the letter i, or the letter x (case insensitive); - * no checking is done against registry lists. - */ - public boolean isSyntaxCorrect () - { - int i; - if (_langCode == null) { - return false; - } - char[] chrs = _langCode.toLowerCase().toCharArray(); - char firstChar = '\0'; - - int ntags = 0; - int taglength = 0; - for (i = 0; i < chrs.length; i++) { - char ch = chrs[i]; - if (i == 0) { - firstChar = ch; - } - if (!Character.isLetter (ch) && ch != '-') { - return false; - } - if (ch == '-') { - taglength = 0; + int ntags = 0; + int taglength = 0; + for (i = 0; i < chrs.length; i++) { + char ch = chrs[i]; + if (i == 0) { + firstChar = ch; + } + if (!Character.isLetter(ch) && ch != '-') { + return false; + } + if (ch == '-') { + taglength = 0; - // If this is the primary tag, do some checks - if (ntags++ == 0) { - if (taglength == 1) { - if (firstChar != 'i' && firstChar != 'x') { - return false; - } - else if (taglength != 2) { - return false; - } - } - } - } - else { - taglength++; - if (taglength > 8) { - return false; - } - } - } - // A dangling hyphen at the end isn't allowed. - return taglength != 0; + // If this is the primary tag, do some checks + if (ntags++ == 0) { + if (taglength == 1) { + if (firstChar != 'i' && firstChar != 'x') { + return false; + } else if (taglength != 2) { + return false; + } + } + } + } else { + taglength++; + if (taglength > 8) { + return false; + } + } } + // A dangling hyphen at the end isn't allowed. + return taglength != 0; + } } diff --git a/jhove-core/src/main/java/edu/harvard/hul/ois/jhove/Rational.java b/jhove-core/src/main/java/edu/harvard/hul/ois/jhove/Rational.java index b9203765c..156dc9f10 100644 --- a/jhove-core/src/main/java/edu/harvard/hul/ois/jhove/Rational.java +++ b/jhove-core/src/main/java/edu/harvard/hul/ois/jhove/Rational.java @@ -1,101 +1,83 @@ -/********************************************************************** - * Jhove - JSTOR/Harvard Object Validation Environment - * Copyright 2003 by JSTOR and the President and Fellows of Harvard College - **********************************************************************/ - +/** + * ******************************************************************** Jhove - JSTOR/Harvard Object + * Validation Environment Copyright 2003 by JSTOR and the President and Fellows of Harvard College + * ******************************************************************** + */ package edu.harvard.hul.ois.jhove; /** - * This class encapsulates a number which is defined as the ratio - * of two 32-bit unsigned integers, in accordance with the TIFF - * specification. + * This class encapsulates a number which is defined as the ratio of two 32-bit unsigned integers, + * in accordance with the TIFF specification. */ -public class Rational -{ - /****************************************************************** - * PRIVATE INSTANCE FIELDS. - ******************************************************************/ +public class Rational { + /** + * **************************************************************** PRIVATE INSTANCE FIELDS. + * **************************************************************** + */ + + /** Numerator of ratio. */ + private long _numerator; + /** Denominator of ratio. */ + private long _denominator; + + /** + * **************************************************************** CLASS CONSTRUCTOR. + * **************************************************************** + */ + + /** + * The arguments to this constructor are long in order to represent all possible 32-bit unsigned + * integers. Parameters greater than 2 ^ 32 - 1 are not meaningful. + * + * @param numerator numerator of the Rational value + * @param denominator denominator of the Rational value + */ + public Rational(long numerator, long denominator) { + _numerator = numerator; + _denominator = denominator; + } + + /** + * The arguments to the int constructor are treated as 32-bit unsigned integers. + * + * @param numerator numerator of the Rational value + * @param denominator denominator of the Rational value + */ + public Rational(int numerator, int denominator) { + _numerator = (long) numerator & 0XFFFFFFFF; + _denominator = (long) denominator & 0XFFFFFFFF; + } - /** Numerator of ratio. */ - private long _numerator; - /** Denominator of ratio. */ - private long _denominator; + /** + * **************************************************************** PUBLIC INSTANCE METHODS. + * **************************************************************** + */ - /****************************************************************** - * CLASS CONSTRUCTOR. - ******************************************************************/ - - /** - * The arguments to this constructor are long in order to - * represent all possible 32-bit unsigned integers. Parameters - * greater than 2 ^ 32 - 1 are not meaningful. - * @param numerator numerator of the Rational value - * @param denominator denominator of the Rational value - */ - public Rational (long numerator, long denominator) - { - _numerator = numerator; - _denominator = denominator; - } - - /** - * The arguments to the int constructor are treated as - * 32-bit unsigned integers. - * @param numerator numerator of the Rational value - * @param denominator denominator of the Rational value - */ - public Rational (int numerator, int denominator) - { - _numerator = (long) numerator & 0XFFFFFFFF; - _denominator = (long) denominator & 0XFFFFFFFF; - } + /** Returns the Numerator property. */ + public long getNumerator() { + return _numerator; + } - /****************************************************************** - * PUBLIC INSTANCE METHODS. - ******************************************************************/ - - /** - * Returns the Numerator property. - */ - public long getNumerator () - { - return _numerator; - } - - /** - * Returns the Denominator property. - */ - public long getDenominator() - { - return _denominator; - } + /** Returns the Denominator property. */ + public long getDenominator() { + return _denominator; + } - /** - * Converts to a floating-point value (numerator/denominator). - * May throw an ArithmeticException. - **/ - public double toDouble () - { - return ((double) _numerator / (double) _denominator); - } + /** + * Converts to a floating-point value (numerator/denominator). May throw an ArithmeticException. + */ + public double toDouble() { + return ((double) _numerator / (double) _denominator); + } - /** - * Converts to a long value (numerator/denominator). - * May throw an ArithmeticException. - **/ - public long toLong () - { - return (long) ((double) _numerator / (double) _denominator); - } + /** Converts to a long value (numerator/denominator). May throw an ArithmeticException. */ + public long toLong() { + return (long) ((double) _numerator / (double) _denominator); + } - /** - * Represents the Rational as a String in the form of - * "numerator/denominator". - */ - @Override - public String toString () - { - return Long.toString (_numerator) + "/" + - Long.toString (_denominator); - } + /** Represents the Rational as a String in the form of "numerator/denominator". */ + @Override + public String toString() { + return Long.toString(_numerator) + "/" + Long.toString(_denominator); + } } diff --git a/jhove-core/src/main/java/edu/harvard/hul/ois/jhove/RepInfo.java b/jhove-core/src/main/java/edu/harvard/hul/ois/jhove/RepInfo.java index 682530c0e..6d00aa8cf 100644 --- a/jhove-core/src/main/java/edu/harvard/hul/ois/jhove/RepInfo.java +++ b/jhove-core/src/main/java/edu/harvard/hul/ois/jhove/RepInfo.java @@ -1,600 +1,480 @@ -/********************************************************************** - * Jhove - JSTOR/Harvard Object Validation Environment - * Copyright 2003-2005 by JSTOR and the President and Fellows of Harvard College - **********************************************************************/ - +/** + * ******************************************************************** Jhove - JSTOR/Harvard Object + * Validation Environment Copyright 2003-2005 by JSTOR and the President and Fellows of Harvard + * College ******************************************************************** + */ package edu.harvard.hul.ois.jhove; import java.util.*; /** - * This class encapsulates representation information, as defined - * by ISO/IEC 14721, about a content stream. + * This class encapsulates representation information, as defined by ISO/IEC 14721, about a content + * stream. * - * @see ISO/IEC - * 14721 (PDF) + * @see ISO/IEC 14721 + * (PDF) */ -public class RepInfo implements Cloneable -{ - /****************************************************************** - * PRIVATE INSTANCE FIELDS. - ******************************************************************/ - - /** List of checksums. */ - private List _checksum; - - /** Consistency flag. */ - private boolean _consistent; - - /** Validity flag. A ternary variable which can have a value - * of TRUE, FALSE, or UNDETERMINED. */ - private int _valid; - - /** Values for _valid */ - public final static int - TRUE = 1, - FALSE = 0, - UNDETERMINED = -1; - - /** Creation date. */ - private Date _created; - - /** External representation information. */ - private RepInfo _external; - - /** Format identifier. */ - private String _format; - - /** Modification date. */ - private Date _lastModified; - - /** List of diagnostic and informative messages. */ - private List _message; - - /** MIME media type. */ - private String _mimeType; - - /** The module used to populate this representation information. */ - private Module _module; - - /** List of conforming format profiles. */ - private List _profile; - - /** List of modules for which signature matches. */ - private List _sigMatch; - - /** Associative map of module-specific representation information. */ - private Map _property; - - /** Object size. */ - private long _size; - - /** Object file pathname or URI. */ - private String _uri; - - /** Flag indicating _uri is a URL if true. */ - private boolean _urlFlag; - - /** Well-formed flag. A ternary variable which can have a value - * of TRUE, FALSE, or UNDETERMINED. */ - private int _wellFormed; - - /** Version of format which applies. */ - private String _version; - - /** Note. */ - private String _note; - - /****************************************************************** - * CLASS CONSTRUCTOR. - ******************************************************************/ - - /** - * Creates a RepInfo with a URI reference - * - * @param uri Object file pathname or URI - */ - public RepInfo (String uri) - { - init (uri); - } - - /** - * Creates a RepInfo with a URI reference and an external RepInfo. - * - * By default, urlFlag is false. - * - * @param uri Object file pathname or URI - * @param external External representation information - */ - public RepInfo (String uri, RepInfo external) - { - init (uri); - _external = external; - } - - private void init (String uri) - { - _uri = uri; - _size = -1; - _wellFormed = TRUE; - _consistent = true; - _urlFlag = false; - _valid = TRUE; - - _checksum = new ArrayList<> (); - _message = new ArrayList<> (); - _profile = new ArrayList<> (); - _property = new TreeMap<> (); - _sigMatch = new ArrayList<> (); - } - - /****************************************************************** - * PUBLIC INSTANCE METHODS. - ******************************************************************/ - - /** - * Clones the RepInfo one level deep, making fresh copies - * of the checksum, message, profile, signature match, - * and property fields. - * The external RepInfo (if any) is not cloned, but - * is attached directly to the clone. - */ - @Override - public Object clone () - { - RepInfo newri; - try { - newri = (RepInfo) super.clone (); - } - catch (CloneNotSupportedException e) { - return null; // should never happen - } - - newri._checksum = new ArrayList<> (_checksum); - newri._message = new ArrayList<>(_message); - newri._profile = new ArrayList<> (_profile); - newri._sigMatch = new ArrayList<> (_sigMatch); - newri._property = new TreeMap<> (_property); - - return newri; - } - - /** - * Copies all the information out of the parameter object. - * This is a "shallow" copy; it is assumed that the parameter - * object is a temporary one that will not be further modified. - */ - public void copy (RepInfo info) - { - _checksum = info._checksum; - _consistent = info._consistent; - _created = info._created; - _external = info._external; - _format = info._format; - _lastModified = info._lastModified; - _message = info._message; - _mimeType = info._mimeType; - _profile = info._profile; - _property = info._property; - _size = info._size; - _uri = info._uri; - _urlFlag = info._urlFlag; - _wellFormed = info._wellFormed; - _valid = info._valid; - _version = info._version; - _note = info._note; - _module = info._module; - _sigMatch = info._sigMatch; - } - - /****************************************************************** - * - * Accessor methods. - ******************************************************************/ - - /** - * Returns this object's list of Checksums - */ - public List getChecksum () - { - return _checksum; - } - - /** - * Returns the creation date stored in this object. A creation - * date is not automatically generated, but must be explicitly - * stored. - **/ - public Date getCreated () - { - return _created; - } - - /** - * Return the format identifier - */ - public String getFormat () - { - return _format; - } - - /** - * Returns the last modified date stored in this object. A - * date is not automatically generated, but must be explicitly - * stored. - **/ - public Date getLastModified () - { - return _lastModified; - } - - /** - * Returns the message list stored in this object - */ - public List getMessage () - { - return _message; - } - - /** - * Returns the MIME type string stored in this object - */ - public String getMimeType () - { - return _mimeType; - } - - /** - * Return the module. - */ - public Module getModule () - { - return _module; - } - - /** - * Returns the list of profiles (Strings) stored in this object - */ - public List getProfile () - { - return _profile; - } - - /** - * Returns the Property map stored in this object. The - * Property map contains key-value pairs whose key is a - * String and whose value is a Property. - */ - public Map getProperty () - { - return _property; - } - - /** - * Returns a named Property from the Property map - * - * @param name The name of the Property. - */ - public Property getProperty (String name) - { - Property property = null; - if (_property.size () > 0) { - property = _property.get (name); - } - - return property; - } - - /** - * Returns the size property stored in this object. - */ - public long getSize () - { - return _size; - } - - /** - * Returns the URI property stored in this object. - */ - public String getUri () - { - return _uri; - } - - /** - * Returns a flag which, if true, indicates - * the object is a URL. - */ - public boolean getURLFlag () - { - return _urlFlag; - } - - /** - * Returns the value of the consistency flag. - */ - public boolean isConsistent () - { - return _consistent; - } - - /** - * Returns the value of the well-formed flag. - * Can return TRUE, FALSE, or UNDETERMINED. - */ - public int getWellFormed () - { - return _wellFormed; - } - - /** - * Returns the value of the validity flag. - * Can return TRUE, FALSE, or UNDETERMINED. - */ - public int getValid () - { - return _valid; - } - - /** - * Returns the version property stored in this object - */ - public String getVersion () - { - return _version; - } - - /** - * Returns the note property stored in this object - */ - public String getNote () - { - return _note; - } - - /** - * Returns the list of matching signatures. - * JhoveBase will make this value persistent across - * module invocations for a given document, so the list - * returned will reflect all modules that have looked - * at the document so far. - */ - public List getSigMatch () - { - return _sigMatch; - } - - /** - * Return property by name, regardless of its position in the - * property hierarchy. - * @param name Property name - * @return Named property (or null) - */ - public Property getByName (String name) - { - Property prop = null; - - Collection coll = _property.values (); - Iterator iter = coll.iterator (); - while (iter.hasNext ()) { - prop = iter.next (); - if ((prop = prop.getByName (name)) != null) { - break; - } - } - - return prop; - } - - /****************************************************************** - * Mutator methods. - ******************************************************************/ - - /** - * Append a Checksum object to the checksum list. - */ - public void setChecksum (Checksum checksum) - { - _checksum.add (checksum); - } - - /** - * Set the value of the consistency flag - */ - public void setConsistent (boolean consistent) - { - _consistent = consistent; - } - - /** - * Set the creation date - */ - public void setCreated (Date created) - { - _created = created; - } - - /** - * Set the format identifier - */ - public void setFormat (String format) - { - _format = format; - } - - /** - * Set the last modified date - */ - public void setLastModified (Date lastModified) - { - _lastModified = lastModified; - } - - /** - * Append a Message object to the message list - */ - public void setMessage (Message message) - { - _message.add (message); - } - - /** - * Set the MIME type string - */ - public void setMimeType (String mimeType) - { - _mimeType = mimeType; - } - - /** - * Add the module. - */ - public void setModule (Module module) - { - _module = module; - } - - /** - * Append a profile String to the profile list - */ - public void setProfile (String profile) - { - _profile.add (profile); - } - - /** - * Add a Property to the property map. The name of the Property - * becomes its key in the map. - */ - public void setProperty (Property property) - { - _property.put (property.getName (), property); - } - - /** - * Set the size property - */ - public void setSize (long size) - { - _size = size; - } - - /** - * Set the flag to indicate whether this is a URL (true) - * or a file (false) - */ - public void setURLFlag (boolean flag) - { - _urlFlag = flag; - } - - /** - * Set the well-formed flag - * - * @param wellFormed Boolean argument that maps to - * an integer value: - * true maps to TRUE, and false to FALSE. - */ - public void setWellFormed (boolean wellFormed) - { - _wellFormed = wellFormed ? TRUE : FALSE; - if (!wellFormed) { - _consistent = false; - _valid = FALSE; - } - } - - /** - * Set the wellFormed flag. - * Setting wellFormed to false forces the consistent and - * valid flags to be false as well. - */ - public void setWellFormed (int wellFormed) - { - _wellFormed = wellFormed; - if (wellFormed == FALSE) { - _consistent = false; - _valid = FALSE; - } - if (wellFormed == UNDETERMINED) { - _valid = UNDETERMINED; - } - } - - /** - * Set the validity flag - * - * @param valid Boolean argument that maps to - * an integer value: - * true maps to TRUE, and false to FALSE. - */ - public void setValid (boolean valid) - { - _valid = valid ? TRUE : FALSE; - } - - /** - * Set the validity flag - * - * @param valid Permitted values are TRUE, FALSE, AND - * UNDETERMINED. The effect of using - * other values is undefined. - */ - public void setValid (int valid) - { - _valid = valid; - } - - /** - * Set the version string - */ - public void setVersion (String version) - { - _version = version; - } - - /** - * Set the note string - */ - public void setNote (String note) - { - _note = note; - } - - /** Adds the name of a module, signifying that the document - * signature matched the module's requirements. - * JhoveBase will make this value persistent across - * module invocations for a given document. - */ - public void setSigMatch (String modname) - { - _sigMatch.add (modname); - } - - /** Adds a list of module names, signifying that the document - * signature matched the module's requirements. - * Any previous list is lost. - * JhoveBase will make this value persistent across - * module invocations for a given document. - */ - public void setSigMatch (List modnames) - { - _sigMatch = modnames; - } - - /****************************************************************** - * Serialization methods. - ******************************************************************/ - - /** - * Output the information in this object. The format and - * destination of the output are determined by the - * OutputHandler. - */ - public void show (OutputHandler handler) - { - handler.analyze (this); - handler.show (this); - } +public class RepInfo implements Cloneable { + /** + * **************************************************************** PRIVATE INSTANCE FIELDS. + * **************************************************************** + */ + + /** List of checksums. */ + private List _checksum; + + /** Consistency flag. */ + private boolean _consistent; + + /** Validity flag. A ternary variable which can have a value of TRUE, FALSE, or UNDETERMINED. */ + private int _valid; + + /** Values for _valid */ + public static final int TRUE = 1, FALSE = 0, UNDETERMINED = -1; + + /** Creation date. */ + private Date _created; + + /** External representation information. */ + private RepInfo _external; + + /** Format identifier. */ + private String _format; + + /** Modification date. */ + private Date _lastModified; + + /** List of diagnostic and informative messages. */ + private List _message; + + /** MIME media type. */ + private String _mimeType; + + /** The module used to populate this representation information. */ + private Module _module; + + /** List of conforming format profiles. */ + private List _profile; + + /** List of modules for which signature matches. */ + private List _sigMatch; + + /** Associative map of module-specific representation information. */ + private Map _property; + + /** Object size. */ + private long _size; + + /** Object file pathname or URI. */ + private String _uri; + + /** Flag indicating _uri is a URL if true. */ + private boolean _urlFlag; + + /** + * Well-formed flag. A ternary variable which can have a value of TRUE, FALSE, or UNDETERMINED. + */ + private int _wellFormed; + + /** Version of format which applies. */ + private String _version; + + /** Note. */ + private String _note; + + /** + * **************************************************************** CLASS CONSTRUCTOR. + * **************************************************************** + */ + + /** + * Creates a RepInfo with a URI reference + * + * @param uri Object file pathname or URI + */ + public RepInfo(String uri) { + init(uri); + } + + /** + * Creates a RepInfo with a URI reference and an external RepInfo. + * + *

By default, urlFlag is false. + * + * @param uri Object file pathname or URI + * @param external External representation information + */ + public RepInfo(String uri, RepInfo external) { + init(uri); + _external = external; + } + + private void init(String uri) { + _uri = uri; + _size = -1; + _wellFormed = TRUE; + _consistent = true; + _urlFlag = false; + _valid = TRUE; + + _checksum = new ArrayList<>(); + _message = new ArrayList<>(); + _profile = new ArrayList<>(); + _property = new TreeMap<>(); + _sigMatch = new ArrayList<>(); + } + + /** + * **************************************************************** PUBLIC INSTANCE METHODS. + * **************************************************************** + */ + + /** + * Clones the RepInfo one level deep, making fresh copies of the checksum, message, profile, + * signature match, and property fields. The external RepInfo (if any) is not cloned, but is + * attached directly to the clone. + */ + @Override + public Object clone() { + RepInfo newri; + try { + newri = (RepInfo) super.clone(); + } catch (CloneNotSupportedException e) { + return null; // should never happen + } + + newri._checksum = new ArrayList<>(_checksum); + newri._message = new ArrayList<>(_message); + newri._profile = new ArrayList<>(_profile); + newri._sigMatch = new ArrayList<>(_sigMatch); + newri._property = new TreeMap<>(_property); + + return newri; + } + + /** + * Copies all the information out of the parameter object. This is a "shallow" copy; it is assumed + * that the parameter object is a temporary one that will not be further modified. + */ + public void copy(RepInfo info) { + _checksum = info._checksum; + _consistent = info._consistent; + _created = info._created; + _external = info._external; + _format = info._format; + _lastModified = info._lastModified; + _message = info._message; + _mimeType = info._mimeType; + _profile = info._profile; + _property = info._property; + _size = info._size; + _uri = info._uri; + _urlFlag = info._urlFlag; + _wellFormed = info._wellFormed; + _valid = info._valid; + _version = info._version; + _note = info._note; + _module = info._module; + _sigMatch = info._sigMatch; + } + + /** + * **************************************************************** + * + *

Accessor methods. **************************************************************** + */ + + /** Returns this object's list of Checksums */ + public List getChecksum() { + return _checksum; + } + + /** + * Returns the creation date stored in this object. A creation date is not automatically + * generated, but must be explicitly stored. + */ + public Date getCreated() { + return _created; + } + + /** Return the format identifier */ + public String getFormat() { + return _format; + } + + /** + * Returns the last modified date stored in this object. A date is not automatically generated, + * but must be explicitly stored. + */ + public Date getLastModified() { + return _lastModified; + } + + /** Returns the message list stored in this object */ + public List getMessage() { + return _message; + } + + /** Returns the MIME type string stored in this object */ + public String getMimeType() { + return _mimeType; + } + + /** Return the module. */ + public Module getModule() { + return _module; + } + + /** Returns the list of profiles (Strings) stored in this object */ + public List getProfile() { + return _profile; + } + + /** + * Returns the Property map stored in this object. The Property map contains key-value pairs whose + * key is a String and whose value is a Property. + */ + public Map getProperty() { + return _property; + } + + /** + * Returns a named Property from the Property map + * + * @param name The name of the Property. + */ + public Property getProperty(String name) { + Property property = null; + if (_property.size() > 0) { + property = _property.get(name); + } + + return property; + } + + /** Returns the size property stored in this object. */ + public long getSize() { + return _size; + } + + /** Returns the URI property stored in this object. */ + public String getUri() { + return _uri; + } + + /** Returns a flag which, if true, indicates the object is a URL. */ + public boolean getURLFlag() { + return _urlFlag; + } + + /** Returns the value of the consistency flag. */ + public boolean isConsistent() { + return _consistent; + } + + /** Returns the value of the well-formed flag. Can return TRUE, FALSE, or UNDETERMINED. */ + public int getWellFormed() { + return _wellFormed; + } + + /** Returns the value of the validity flag. Can return TRUE, FALSE, or UNDETERMINED. */ + public int getValid() { + return _valid; + } + + /** Returns the version property stored in this object */ + public String getVersion() { + return _version; + } + + /** Returns the note property stored in this object */ + public String getNote() { + return _note; + } + + /** + * Returns the list of matching signatures. JhoveBase will make this value persistent across + * module invocations for a given document, so the list returned will reflect all modules that + * have looked at the document so far. + */ + public List getSigMatch() { + return _sigMatch; + } + + /** + * Return property by name, regardless of its position in the property hierarchy. + * + * @param name Property name + * @return Named property (or null) + */ + public Property getByName(String name) { + Property prop = null; + + Collection coll = _property.values(); + Iterator iter = coll.iterator(); + while (iter.hasNext()) { + prop = iter.next(); + if ((prop = prop.getByName(name)) != null) { + break; + } + } + + return prop; + } + + /** + * **************************************************************** Mutator methods. + * **************************************************************** + */ + + /** Append a Checksum object to the checksum list. */ + public void setChecksum(Checksum checksum) { + _checksum.add(checksum); + } + + /** Set the value of the consistency flag */ + public void setConsistent(boolean consistent) { + _consistent = consistent; + } + + /** Set the creation date */ + public void setCreated(Date created) { + _created = created; + } + + /** Set the format identifier */ + public void setFormat(String format) { + _format = format; + } + + /** Set the last modified date */ + public void setLastModified(Date lastModified) { + _lastModified = lastModified; + } + + /** Append a Message object to the message list */ + public void setMessage(Message message) { + _message.add(message); + } + + /** Set the MIME type string */ + public void setMimeType(String mimeType) { + _mimeType = mimeType; + } + + /** Add the module. */ + public void setModule(Module module) { + _module = module; + } + + /** Append a profile String to the profile list */ + public void setProfile(String profile) { + _profile.add(profile); + } + + /** Add a Property to the property map. The name of the Property becomes its key in the map. */ + public void setProperty(Property property) { + _property.put(property.getName(), property); + } + + /** Set the size property */ + public void setSize(long size) { + _size = size; + } + + /** Set the flag to indicate whether this is a URL (true) or a file (false) */ + public void setURLFlag(boolean flag) { + _urlFlag = flag; + } + + /** + * Set the well-formed flag + * + * @param wellFormed Boolean argument that maps to an integer value: true maps to TRUE, and false + * to FALSE. + */ + public void setWellFormed(boolean wellFormed) { + _wellFormed = wellFormed ? TRUE : FALSE; + if (!wellFormed) { + _consistent = false; + _valid = FALSE; + } + } + + /** + * Set the wellFormed flag. Setting wellFormed to false forces the consistent and valid flags to + * be false as well. + */ + public void setWellFormed(int wellFormed) { + _wellFormed = wellFormed; + if (wellFormed == FALSE) { + _consistent = false; + _valid = FALSE; + } + if (wellFormed == UNDETERMINED) { + _valid = UNDETERMINED; + } + } + + /** + * Set the validity flag + * + * @param valid Boolean argument that maps to an integer value: true maps to TRUE, and false to + * FALSE. + */ + public void setValid(boolean valid) { + _valid = valid ? TRUE : FALSE; + } + + /** + * Set the validity flag + * + * @param valid Permitted values are TRUE, FALSE, AND UNDETERMINED. The effect of using other + * values is undefined. + */ + public void setValid(int valid) { + _valid = valid; + } + + /** Set the version string */ + public void setVersion(String version) { + _version = version; + } + + /** Set the note string */ + public void setNote(String note) { + _note = note; + } + + /** + * Adds the name of a module, signifying that the document signature matched the module's + * requirements. JhoveBase will make this value persistent across module invocations for a given + * document. + */ + public void setSigMatch(String modname) { + _sigMatch.add(modname); + } + + /** + * Adds a list of module names, signifying that the document signature matched the module's + * requirements. Any previous list is lost. JhoveBase will make this value persistent across + * module invocations for a given document. + */ + public void setSigMatch(List modnames) { + _sigMatch = modnames; + } + + /** + * **************************************************************** Serialization methods. + * **************************************************************** + */ + + /** + * Output the information in this object. The format and destination of the output are determined + * by the OutputHandler. + */ + public void show(OutputHandler handler) { + handler.analyze(this); + handler.show(this); + } } diff --git a/jhove-core/src/main/java/edu/harvard/hul/ois/jhove/Signature.java b/jhove-core/src/main/java/edu/harvard/hul/ois/jhove/Signature.java index 9a70ff99f..a4c16f629 100644 --- a/jhove-core/src/main/java/edu/harvard/hul/ois/jhove/Signature.java +++ b/jhove-core/src/main/java/edu/harvard/hul/ois/jhove/Signature.java @@ -1,157 +1,121 @@ -/********************************************************************** - * Jhove - JSTOR/Harvard Object Validation Environment - * Copyright 2003 by JSTOR and the President and Fellows of Harvard College - **********************************************************************/ - +/** + * ******************************************************************** Jhove - JSTOR/Harvard Object + * Validation Environment Copyright 2003 by JSTOR and the President and Fellows of Harvard College + * ******************************************************************** + */ package edu.harvard.hul.ois.jhove; /** - * This class encapsulates information about format signatures, - * both internal and external. - * The value of a Signature may be either a String or a byte array - * (stored as an int array to avoid signed byte problems). + * This class encapsulates information about format signatures, both internal and external. The + * value of a Signature may be either a String or a byte array (stored as an int array to avoid + * signed byte problems). */ -public abstract class Signature -{ - private int[] _value; - private String _stringValue; - private SignatureType _type; - private SignatureUseType _use; - private String _note; +public abstract class Signature { + private int[] _value; + private String _stringValue; + private SignatureType _type; + private SignatureUseType _use; + private String _note; - /** - * A Signature cannot be created directly; this constructor - * can be called as the superclass constructor from a subclass. - * This constructor uses a String value. - */ - protected Signature (String value, SignatureType type, - SignatureUseType use) - { - this (new int[value.length ()], type, use); - int len = value.length (); + /** + * A Signature cannot be created directly; this constructor can be called as the superclass + * constructor from a subclass. This constructor uses a String value. + */ + protected Signature(String value, SignatureType type, SignatureUseType use) { + this(new int[value.length()], type, use); + int len = value.length(); _stringValue = value; for (int i = 0; i < len; i++) { - _value[i] = value.charAt(i); - } + _value[i] = value.charAt(i); } + } - /** - * A Signature cannot be created directly; this constructor - * can be called as the superclass constructor from a subclass. - * This constructor uses a byte array (stored as an int array) value. - */ - protected Signature (int[] value, SignatureType type, - SignatureUseType use) - { + /** + * A Signature cannot be created directly; this constructor can be called as the superclass + * constructor from a subclass. This constructor uses a byte array (stored as an int array) value. + */ + protected Signature(int[] value, SignatureType type, SignatureUseType use) { _value = value; - _type = type; - _use = use; + _type = type; + _use = use; _stringValue = null; - } + } - /** - * A Signature cannot be created directly; this constructor - * can be called as the superclass constructor from a subclass. - * This constructor uses a String value and allows specification - * of a note. - */ - protected Signature (String value, SignatureType type, - SignatureUseType use, - String note) - { - this (new int[value.length ()], type, use, note); - int len = value.length (); + /** + * A Signature cannot be created directly; this constructor can be called as the superclass + * constructor from a subclass. This constructor uses a String value and allows specification of a + * note. + */ + protected Signature(String value, SignatureType type, SignatureUseType use, String note) { + this(new int[value.length()], type, use, note); + int len = value.length(); for (int i = 0; i < len; i++) { - _value[i] = value.charAt(i); + _value[i] = value.charAt(i); } _stringValue = value; - } + } - /** - * A Signature cannot be created directly; this constructor - * can be called as the superclass constructor from a subclass. - * This constructor uses a byte array (stored as an int array) value - * and allows specification of a note. - */ - protected Signature (int[] value, SignatureType type, - SignatureUseType use, - String note) - { - this (value, type, use); + /** + * A Signature cannot be created directly; this constructor can be called as the superclass + * constructor from a subclass. This constructor uses a byte array (stored as an int array) value + * and allows specification of a note. + */ + protected Signature(int[] value, SignatureType type, SignatureUseType use, String note) { + this(value, type, use); _note = note; - } + } - /** - * Returns the type of this Signature - */ - public SignatureType getType () - { + /** Returns the type of this Signature */ + public SignatureType getType() { return _type; - } + } - /** - * Returns the use requirement for this Signature - */ - public SignatureUseType getUse () - { + /** Returns the use requirement for this Signature */ + public SignatureUseType getUse() { return _use; - } + } - /** - * Returns the byte array value for this Signature. - * If this Signature was constructed from a String, it - * returns the characters of the String as the bytes of - * the array. - */ - public int[] getValue () - { + /** + * Returns the byte array value for this Signature. If this Signature was constructed from a + * String, it returns the characters of the String as the bytes of the array. + */ + public int[] getValue() { return _value; - } + } - /** - * Returns the note specified for this Signature, or null - * if no note was specified. - */ - public String getNote () - { + /** Returns the note specified for this Signature, or null if no note was specified. */ + public String getNote() { return _note; - } + } - /** - * Returns true if this Signature's value was provided as a - * String, false if as an array. - */ - public boolean isStringValue () - { + /** Returns true if this Signature's value was provided as a String, false if as an array. */ + public boolean isStringValue() { return (_stringValue != null); - } + } - /** - * Returns the string value of this Signature. Returns null - * if this Signature was constructed with an array. - */ - public String getValueString () - { + /** + * Returns the string value of this Signature. Returns null if this Signature was constructed with + * an array. + */ + public String getValueString() { return _stringValue; - } + } - /** - * Returns the value of this Signature as a hexadecimal string. - * The length of the string is twice the length of the array - * or string from which this Signature was created, and all - * alphabetic characters are lower case. - */ - public String getValueHexString () - { - StringBuffer valBuf = new StringBuffer ("0x"); + /** + * Returns the value of this Signature as a hexadecimal string. The length of the string is twice + * the length of the array or string from which this Signature was created, and all alphabetic + * characters are lower case. + */ + public String getValueHexString() { + StringBuffer valBuf = new StringBuffer("0x"); for (int i = 0; i < _value.length; i++) { - /* Make each byte exactly two digits */ - int b = _value[i]; - if (b < 16) { - valBuf.append ('0'); - } - valBuf.append (Integer.toHexString (b)); - } - return valBuf.toString (); + /* Make each byte exactly two digits */ + int b = _value[i]; + if (b < 16) { + valBuf.append('0'); + } + valBuf.append(Integer.toHexString(b)); } + return valBuf.toString(); + } } diff --git a/jhove-core/src/main/java/edu/harvard/hul/ois/jhove/SignatureType.java b/jhove-core/src/main/java/edu/harvard/hul/ois/jhove/SignatureType.java index 500993a29..5b0456b01 100644 --- a/jhove-core/src/main/java/edu/harvard/hul/ois/jhove/SignatureType.java +++ b/jhove-core/src/main/java/edu/harvard/hul/ois/jhove/SignatureType.java @@ -1,42 +1,39 @@ -/********************************************************************** - * Jhove - JSTOR/Harvard Object Validation Environment - * Copyright 2003 by JSTOR and the President and Fellows of Harvard College - **********************************************************************/ - +/** + * ******************************************************************** Jhove - JSTOR/Harvard Object + * Validation Environment Copyright 2003 by JSTOR and the President and Fellows of Harvard College + * ******************************************************************** + */ package edu.harvard.hul.ois.jhove; /** - * This class defines enumerated types for a Signature in a module. - * Applications will not create or modify SignatureTypes, but will - * use one of the predefined SignatureType instances - * EXTENSION, FILETYPE, or MAGIC. + * This class defines enumerated types for a Signature in a module. Applications will not create or + * modify SignatureTypes, but will use one of the predefined SignatureType instances EXTENSION, + * FILETYPE, or MAGIC. * * @see Signature */ public enum SignatureType { - /** - * Signature type for a file extension, i.e., a sequence of - * characters following a period character in a file name. - */ - EXTENSION("File extension"), - /** - * Signature type for a Macintosh OS file type. This applies - * only to Mac OS files, and is always a four-character code. - */ - FILETYPE("Mac OS file type"), - /** - * Signature type for a "magic number" stored in the file. - */ - MAGIC("Magic number"); - /** A String name for the type, used for reporting. */ - public final String name; + /** + * Signature type for a file extension, i.e., a sequence of characters following a period + * character in a file name. + */ + EXTENSION("File extension"), + /** + * Signature type for a Macintosh OS file type. This applies only to Mac OS files, and is always a + * four-character code. + */ + FILETYPE("Mac OS file type"), + /** Signature type for a "magic number" stored in the file. */ + MAGIC("Magic number"); + /** A String name for the type, used for reporting. */ + public final String name; - private SignatureType(final String name) { - this.name = name; - } + private SignatureType(final String name) { + this.name = name; + } - @Override - public String toString() { - return this.name; - } + @Override + public String toString() { + return this.name; + } } diff --git a/jhove-core/src/main/java/edu/harvard/hul/ois/jhove/SignatureUseType.java b/jhove-core/src/main/java/edu/harvard/hul/ois/jhove/SignatureUseType.java index a49bd0772..ea9df655f 100644 --- a/jhove-core/src/main/java/edu/harvard/hul/ois/jhove/SignatureUseType.java +++ b/jhove-core/src/main/java/edu/harvard/hul/ois/jhove/SignatureUseType.java @@ -1,36 +1,34 @@ -/********************************************************************** - * Jhove - JSTOR/Harvard Object Validation Environment - * Copyright 2003 by JSTOR and the President and Fellows of Harvard College - **********************************************************************/ - +/** + * ******************************************************************** Jhove - JSTOR/Harvard Object + * Validation Environment Copyright 2003 by JSTOR and the President and Fellows of Harvard College + * ******************************************************************** + */ package edu.harvard.hul.ois.jhove; /** - * This class defines enumerated use types for a Signature in a module. - * These give information on whether a signature is required in - * valid content. - * Applications will not create or modify SignatureUseTypes, but will - * use one of the predefined SignatureUseType instances - * MANDATORY, MANDATORY_IF_APPLICABLE, or OPTIONAL. + * This class defines enumerated use types for a Signature in a module. These give information on + * whether a signature is required in valid content. Applications will not create or modify + * SignatureUseTypes, but will use one of the predefined SignatureUseType instances MANDATORY, + * MANDATORY_IF_APPLICABLE, or OPTIONAL. * * @see Signature */ public enum SignatureUseType { - /** Use type for a required signature */ - MANDATORY("Mandatory"), - /** Use type for a conditionally required signature. */ - MANDATORY_IF_APPLICABLE("Mandatory if applicable"), - /** Use type for an optional signature. */ - OPTIONAL("Optional"); - /** A String name for the type, used for reporting. */ - public final String name; + /** Use type for a required signature */ + MANDATORY("Mandatory"), + /** Use type for a conditionally required signature. */ + MANDATORY_IF_APPLICABLE("Mandatory if applicable"), + /** Use type for an optional signature. */ + OPTIONAL("Optional"); + /** A String name for the type, used for reporting. */ + public final String name; - private SignatureUseType(final String name) { - this.name = name; - } + private SignatureUseType(final String name) { + this.name = name; + } - @Override - public String toString() { - return this.name; - } + @Override + public String toString() { + return this.name; + } } diff --git a/jhove-core/src/main/java/edu/harvard/hul/ois/jhove/TextMDMetadata.java b/jhove-core/src/main/java/edu/harvard/hul/ois/jhove/TextMDMetadata.java index ce77bc076..99e391c1e 100644 --- a/jhove-core/src/main/java/edu/harvard/hul/ois/jhove/TextMDMetadata.java +++ b/jhove-core/src/main/java/edu/harvard/hul/ois/jhove/TextMDMetadata.java @@ -9,429 +9,477 @@ import java.util.Set; /** - * Encapsulation of the textMD metadata for text files. - * See http://www.loc.gov/standards/textMd for more information. + * Encapsulation of the textMD metadata for text files. See http://www.loc.gov/standards/textMd for + * more information. * * @author Thomas Ledoux - * */ public class TextMDMetadata { - /** - * textMD namespace and version - */ - public static final String NAMESPACE = "info:lc/xmlns/textMD-v3"; - public static final String DEFAULT_LOCATION = - "http://www.loc.gov/standards/textMD/textMD-v3.01a.xsd"; - public static final String VERSION = "3.0"; - - /** - * Uses enumerated values of 'big', 'little', and 'middle' endian. - */ - public static final String[] BYTE_ORDER = { - "big", "little", "middle" - }; - public static final int BYTE_ORDER_BIG = 0; - public static final int BYTE_ORDER_LITTLE = 1; - public static final int BYTE_ORDER_MIDDLE= 2; - - /** - * Uses enumerated values of 'CR', 'LF' and 'CR/LF' for the idenntification of the linebreak. - */ - public static final String[] LINEBREAK = { - "CR", "LF", "CR/LF" - }; - public static final int LINEBREAK_CR = 0; - public static final int LINEBREAK_LF = 1; - public static final int LINEBREAK_CRLF = 2; - - /** - * Array of textMD charsets unknown by java.nio.charset.Charsets - */ - protected static final String[] UNKNOWN_JAVA_CHARSET = - new String[] { - "ISO-10646-UTF-1", "ISO_646.basic:1983", "INVARIANT", "BS_4730", "NATS-SEFI", - "NATS-SEFI-ADD", "NATS-DANO", "NATS-DANO-ADD", "SEN_850200_B", "SEN_850200_C", "ISO-2022-CN-EXT", - "JIS_C6220-1969-jp", "JIS_C6220-1969-ro", "IT", "PT", "ES", "greek7-old", "latin-greek", - "DIN_66003", "NF_Z_62-010_(1973)", "latin-greek-1", "ISO_5427", "JIS_C6226-1978", "BS_viewdata", - "INIS", "INIS-8", "INIS-cyrillic", "ISO_5427:1981", "ISO_5428:1980", "GB_1988-80", "GB_2312-80", - "NS_4551-1", "NS_4551-2", "NF_Z_62-010", "videotex-suppl", "PT2", "ES2", "MSZ_7795.3", "greek7", - "ASMO_449", "iso-ir-90", "JIS_C6229-1984-a", "JIS_C6229-1984-b", "JIS_C6229-1984-b-add", - "JIS_C6229-1984-hand", "JIS_C6229-1984-hand-add", "JIS_C6229-1984-kana", "ISO_2033-1983", - "ANSI_X3.110-1983", "T.61-7bit", "ECMA-cyrillic", "CSA_Z243.4-1985-1", "CSA_Z243.4-1985-2", - "CSA_Z243.4-1985-gr", "ISO_8859-6-E", "ISO_8859-6-I", "T.101-G2", "ISO_8859-8-E", "ISO_8859-8-I", - "CSN_369103", "JUS_I.B1.002", "ISO_6937-2-add", "IEC_P27-1", "JUS_I.B1.003-serb", - "JUS_I.B1.003-mac", "greek-ccitt", "NC_NC00-10:81", "ISO_6937-2-25", "GOST_19768-74", - "ISO_8859-supp", "ISO_10367-box", "ISO-8859-10", "latin-lap", "DS_2089", "us-dk", "dk-us", - "KSC5636", "ISO-10646-UCS-4", "DEC-MCS", "hp-roman8", "macintosh", "IBM038", "IBM274", "IBM275", - "IBM281", "IBM290", "IBM423", "IBM851", "IBM880", "IBM891", "IBM903", "IBM904", "IBM905", - "EBCDIC-AT-DE", "EBCDIC-AT-DE-A", "EBCDIC-CA-FR", "EBCDIC-DK-NO", "EBCDIC-DK-NO-A", "EBCDIC-FI-SE", - "EBCDIC-FI-SE-A", "EBCDIC-FR", "EBCDIC-IT", "EBCDIC-PT", "EBCDIC-ES", "EBCDIC-ES-A", "EBCDIC-ES-S", - "EBCDIC-UK", "EBCDIC-US", "UNKNOWN-8BIT", "MNEMONIC", "MNEM", "VISCII", "VIQR", "IBM00924", - "UNICODE-1-1", "SCSU", "UTF-7", "CESU-8", "UNICODE-1-1-UTF-7", "ISO-8859-14", "ISO-8859-16", - "Extended_UNIX_Code_Fixed_Width_for_Japanese", "ISO-10646-UCS-Basic", "ISO-10646-Unicode-Latin1", - "ISO-10646-J-1", "ISO-Unicode-IBM-1268", "ISO-Unicode-IBM-1276", "ISO-Unicode-IBM-1264", - "ISO-Unicode-IBM-1265", "ISO-8859-1-Windows-3.0-Latin-1", "ISO-8859-1-Windows-3.1-Latin-1", - "ISO-8859-2-Windows-Latin-2", "ISO-8859-9-Windows-Latin-5", "Adobe-Standard-Encoding", - "Ventura-US", "Ventura-International", "PC8-Danish-Norwegian", "PC8-Turkish", "IBM-Symbols", - "HP-Legal", "HP-Pi-font", "HP-Math8", "Adobe-Symbol-Encoding", "HP-DeskTop", "Ventura-Math", - "Microsoft-Publishing", "HZ-GB-2312", }; - /** - * Set of unknown charsets in Java - */ - protected static Set setOfUnknownJavaCharset; - - /** - * Map from ISO 639/2 T to ISO 639/2 B - */ - protected static Map fromISO_639_2_T2B; - - public static final String CHARSET_ASCII = "US-ASCII"; - public static final String CHARSET_UTF8 = "UTF-8"; - public static final String CHARSET_ISO8859_1 = "ISO-8859-1"; - - /** - * To represent the unknown - */ - public static final int NILL = -1; - - static { - setOfUnknownJavaCharset = new HashSet(Arrays.asList(UNKNOWN_JAVA_CHARSET)); - - // Map to transform from the terminology code to the bibliographic one - fromISO_639_2_T2B = new HashMap(); - fromISO_639_2_T2B.put("sqi", "alb"); - fromISO_639_2_T2B.put("hye", "arm"); - fromISO_639_2_T2B.put("eus", "baq"); - fromISO_639_2_T2B.put("mya", "bur"); - fromISO_639_2_T2B.put("zho", "chi"); - fromISO_639_2_T2B.put("ces", "cze"); - fromISO_639_2_T2B.put("nld", "dut"); - fromISO_639_2_T2B.put("fra", "fre"); - fromISO_639_2_T2B.put("kat", "geo"); - fromISO_639_2_T2B.put("deu", "ger"); - fromISO_639_2_T2B.put("ell", "gre"); - fromISO_639_2_T2B.put("isl", "ice"); - fromISO_639_2_T2B.put("mkd", "mac"); - fromISO_639_2_T2B.put("mri", "mao"); - fromISO_639_2_T2B.put("msa", "may"); - fromISO_639_2_T2B.put("fas", "per"); - fromISO_639_2_T2B.put("ron", "rum"); - fromISO_639_2_T2B.put("slk", "slo"); - fromISO_639_2_T2B.put("bod", "tib"); - fromISO_639_2_T2B.put("cym", "wel"); - } - - /** - charset - Usage: The character set employed by the text. Controlled vocab using IANA names for character sets. - Attributes: none. - Contains: none. - Contained by: character_info. + /** textMD namespace and version */ + public static final String NAMESPACE = "info:lc/xmlns/textMD-v3"; + + public static final String DEFAULT_LOCATION = + "http://www.loc.gov/standards/textMD/textMD-v3.01a.xsd"; + public static final String VERSION = "3.0"; + + /** Uses enumerated values of 'big', 'little', and 'middle' endian. */ + public static final String[] BYTE_ORDER = {"big", "little", "middle"}; + + public static final int BYTE_ORDER_BIG = 0; + public static final int BYTE_ORDER_LITTLE = 1; + public static final int BYTE_ORDER_MIDDLE = 2; + + /** Uses enumerated values of 'CR', 'LF' and 'CR/LF' for the idenntification of the linebreak. */ + public static final String[] LINEBREAK = {"CR", "LF", "CR/LF"}; + + public static final int LINEBREAK_CR = 0; + public static final int LINEBREAK_LF = 1; + public static final int LINEBREAK_CRLF = 2; + + /** Array of textMD charsets unknown by java.nio.charset.Charsets */ + protected static final String[] UNKNOWN_JAVA_CHARSET = + new String[] { + "ISO-10646-UTF-1", + "ISO_646.basic:1983", + "INVARIANT", + "BS_4730", + "NATS-SEFI", + "NATS-SEFI-ADD", + "NATS-DANO", + "NATS-DANO-ADD", + "SEN_850200_B", + "SEN_850200_C", + "ISO-2022-CN-EXT", + "JIS_C6220-1969-jp", + "JIS_C6220-1969-ro", + "IT", + "PT", + "ES", + "greek7-old", + "latin-greek", + "DIN_66003", + "NF_Z_62-010_(1973)", + "latin-greek-1", + "ISO_5427", + "JIS_C6226-1978", + "BS_viewdata", + "INIS", + "INIS-8", + "INIS-cyrillic", + "ISO_5427:1981", + "ISO_5428:1980", + "GB_1988-80", + "GB_2312-80", + "NS_4551-1", + "NS_4551-2", + "NF_Z_62-010", + "videotex-suppl", + "PT2", + "ES2", + "MSZ_7795.3", + "greek7", + "ASMO_449", + "iso-ir-90", + "JIS_C6229-1984-a", + "JIS_C6229-1984-b", + "JIS_C6229-1984-b-add", + "JIS_C6229-1984-hand", + "JIS_C6229-1984-hand-add", + "JIS_C6229-1984-kana", + "ISO_2033-1983", + "ANSI_X3.110-1983", + "T.61-7bit", + "ECMA-cyrillic", + "CSA_Z243.4-1985-1", + "CSA_Z243.4-1985-2", + "CSA_Z243.4-1985-gr", + "ISO_8859-6-E", + "ISO_8859-6-I", + "T.101-G2", + "ISO_8859-8-E", + "ISO_8859-8-I", + "CSN_369103", + "JUS_I.B1.002", + "ISO_6937-2-add", + "IEC_P27-1", + "JUS_I.B1.003-serb", + "JUS_I.B1.003-mac", + "greek-ccitt", + "NC_NC00-10:81", + "ISO_6937-2-25", + "GOST_19768-74", + "ISO_8859-supp", + "ISO_10367-box", + "ISO-8859-10", + "latin-lap", + "DS_2089", + "us-dk", + "dk-us", + "KSC5636", + "ISO-10646-UCS-4", + "DEC-MCS", + "hp-roman8", + "macintosh", + "IBM038", + "IBM274", + "IBM275", + "IBM281", + "IBM290", + "IBM423", + "IBM851", + "IBM880", + "IBM891", + "IBM903", + "IBM904", + "IBM905", + "EBCDIC-AT-DE", + "EBCDIC-AT-DE-A", + "EBCDIC-CA-FR", + "EBCDIC-DK-NO", + "EBCDIC-DK-NO-A", + "EBCDIC-FI-SE", + "EBCDIC-FI-SE-A", + "EBCDIC-FR", + "EBCDIC-IT", + "EBCDIC-PT", + "EBCDIC-ES", + "EBCDIC-ES-A", + "EBCDIC-ES-S", + "EBCDIC-UK", + "EBCDIC-US", + "UNKNOWN-8BIT", + "MNEMONIC", + "MNEM", + "VISCII", + "VIQR", + "IBM00924", + "UNICODE-1-1", + "SCSU", + "UTF-7", + "CESU-8", + "UNICODE-1-1-UTF-7", + "ISO-8859-14", + "ISO-8859-16", + "Extended_UNIX_Code_Fixed_Width_for_Japanese", + "ISO-10646-UCS-Basic", + "ISO-10646-Unicode-Latin1", + "ISO-10646-J-1", + "ISO-Unicode-IBM-1268", + "ISO-Unicode-IBM-1276", + "ISO-Unicode-IBM-1264", + "ISO-Unicode-IBM-1265", + "ISO-8859-1-Windows-3.0-Latin-1", + "ISO-8859-1-Windows-3.1-Latin-1", + "ISO-8859-2-Windows-Latin-2", + "ISO-8859-9-Windows-Latin-5", + "Adobe-Standard-Encoding", + "Ventura-US", + "Ventura-International", + "PC8-Danish-Norwegian", + "PC8-Turkish", + "IBM-Symbols", + "HP-Legal", + "HP-Pi-font", + "HP-Math8", + "Adobe-Symbol-Encoding", + "HP-DeskTop", + "Ventura-Math", + "Microsoft-Publishing", + "HZ-GB-2312", + }; + /** Set of unknown charsets in Java */ + protected static Set setOfUnknownJavaCharset; + + /** Map from ISO 639/2 T to ISO 639/2 B */ + protected static Map fromISO_639_2_T2B; + + public static final String CHARSET_ASCII = "US-ASCII"; + public static final String CHARSET_UTF8 = "UTF-8"; + public static final String CHARSET_ISO8859_1 = "ISO-8859-1"; + + /** To represent the unknown */ + public static final int NILL = -1; + + static { + setOfUnknownJavaCharset = new HashSet(Arrays.asList(UNKNOWN_JAVA_CHARSET)); + + // Map to transform from the terminology code to the bibliographic one + fromISO_639_2_T2B = new HashMap(); + fromISO_639_2_T2B.put("sqi", "alb"); + fromISO_639_2_T2B.put("hye", "arm"); + fromISO_639_2_T2B.put("eus", "baq"); + fromISO_639_2_T2B.put("mya", "bur"); + fromISO_639_2_T2B.put("zho", "chi"); + fromISO_639_2_T2B.put("ces", "cze"); + fromISO_639_2_T2B.put("nld", "dut"); + fromISO_639_2_T2B.put("fra", "fre"); + fromISO_639_2_T2B.put("kat", "geo"); + fromISO_639_2_T2B.put("deu", "ger"); + fromISO_639_2_T2B.put("ell", "gre"); + fromISO_639_2_T2B.put("isl", "ice"); + fromISO_639_2_T2B.put("mkd", "mac"); + fromISO_639_2_T2B.put("mri", "mao"); + fromISO_639_2_T2B.put("msa", "may"); + fromISO_639_2_T2B.put("fas", "per"); + fromISO_639_2_T2B.put("ron", "rum"); + fromISO_639_2_T2B.put("slk", "slo"); + fromISO_639_2_T2B.put("bod", "tib"); + fromISO_639_2_T2B.put("cym", "wel"); + } + + /** + * charset Usage: The character set employed by the text. Controlled vocab using IANA names for + * character sets. Attributes: none. Contains: none. Contained by: character_info. + */ + private String charset; + + /** + * byte_order Usage: Byte order, primarily useful for cases where it's not clear just by + * specifying an IANA character set. Uses enumerated values of big, little, and middle' endian. + * Attributes: none. Contains: none. Contained by: character_info. + */ + private int byte_order = NILL; + + /** + * byte_size Usage: The size of an individual byte within the expressed as a number of bits (as + * integer). This does not necessarily equal the character size, as a character may have more than + * one, or a variable number of bytes per character. Attributes: none. Contains: none. Contained + * by: character_info. + */ + private String byte_size; + + /** + * character_size Usage: The size of an individual character within the character set as a number + * of bytes of the size expressed in the byte_size. In the case of variable encodings, such as + * UTF-8 for Unicode, the character_size element should state "variable" and also identify the + * specific variable character set encoding in the encoding attribute. Attributes: encoding. + * Contains: none. Contained by: character_info. + */ + private String character_size; + + /** + * linebreak Usage: How line breaks are represented in current file (which may differ from how + * they were originally encoded). Either carriage return, line feed, or carriage return/line feed. + * Attributes: none. Contains: none. Contained by: character_info. + */ + private int linebreak = NILL; + + /** + * language Usage: Language(s) used in work. Use ISO 639-2 codes, which are enumerated in the + * schema as valid text values. Attributes: none. Contains: none. Contained by: textMD. + */ + private String language; + + /** + * markup_basis Usage: The metalanguage used to create the markup language, such as SGML, XML, + * GML, etc. Attributes: version. Contains: none. Contained by: textMD. + */ + private String markup_basis; + /** + * version Usage: Used to record the version number (as a string) for a given piece of software, a + * markup language, or a schema version. + */ + private String markup_basis_version; + + /** + * markup_language Usage: Markup language employed on the text (i.e., the specific schema or dtd). + * May be a URI for schema or dtd, but not mandatory. Attributes: version. Contains: none. + * Contained by: textMD. + */ + private String markup_language; + + /** + * version Usage: Used to record the version number (as a string) for a given piece of software, a + * markup language, or a schema version. */ - private String charset; - - /** - byte_order - Usage: Byte order, primarily useful for cases where it's not clear just by specifying an IANA character set. - Uses enumerated values of big, little, and middle' endian. - Attributes: none. - Contains: none. - Contained by: character_info. - */ - private int byte_order = NILL; - - /** - byte_size - Usage: The size of an individual byte within the expressed as a number of bits (as integer). This does not necessarily equal the character size, as a character may have more than one, or a variable number of bytes per character. - Attributes: none. - Contains: none. - Contained by: character_info. - */ - private String byte_size; - - /** - character_size - Usage: The size of an individual character within the character set as a number of bytes of the size expressed in the byte_size. In the case of variable encodings, such as UTF-8 for Unicode, the character_size element should state "variable" and also identify the specific variable character set encoding in the encoding attribute. - Attributes: encoding. - Contains: none. - Contained by: character_info. - */ - private String character_size; - - /** - linebreak - Usage: How line breaks are represented in current file (which may differ from how they were originally encoded). Either carriage return, line feed, or carriage return/line feed. - Attributes: none. - Contains: none. - Contained by: character_info. - */ - private int linebreak = NILL; - - /** - language - Usage: Language(s) used in work. Use ISO 639-2 codes, which are enumerated in the schema as valid text values. - Attributes: none. - Contains: none. - Contained by: textMD. - */ - private String language; - - /** - markup_basis - Usage: The metalanguage used to create the markup language, such as SGML, XML, GML, etc. - Attributes: version. - Contains: none. - Contained by: textMD. - */ - private String markup_basis; - /** - version - Usage: Used to record the version number (as a string) for a given piece of software, a markup language, or a schema version. - */ - private String markup_basis_version; - - /** - markup_language - Usage: Markup language employed on the text (i.e., the specific schema or dtd). May be a URI for schema or dtd, but not mandatory. - Attributes: version. - Contains: none. - Contained by: textMD. - */ - private String markup_language; - - /** - version - Usage: Used to record the version number (as a string) for a given piece of software, a markup language, or a schema version. - */ - private String markup_language_version; - - /** - * @return the charset - */ - public String getCharset() { - return charset; - } - - /** - * @param charset the charset to set - */ - public void setCharset(String charset) { - this.charset = toTextMDCharset(charset); - } - - /** - * @return the byte_order - */ - public int getByte_order() { - return byte_order; - } - public String getByte_orderString() { - if (byte_order == NILL) { - return BYTE_ORDER[BYTE_ORDER_BIG]; // default !!! - } - return BYTE_ORDER[byte_order]; + private String markup_language_version; + + /** @return the charset */ + public String getCharset() { + return charset; + } + + /** @param charset the charset to set */ + public void setCharset(String charset) { + this.charset = toTextMDCharset(charset); + } + + /** @return the byte_order */ + public int getByte_order() { + return byte_order; + } + + public String getByte_orderString() { + if (byte_order == NILL) { + return BYTE_ORDER[BYTE_ORDER_BIG]; // default !!! } - - /** - * @param byte_order the byte_order to set - */ - public void setByte_order(int byte_order) { - this.byte_order = byte_order; - } - - /** - * @return the byte_size - */ - public String getByte_size() { - return byte_size; - } - - /** - * @param byte_size the byte_size to set - */ - public void setByte_size(String byte_size) { - this.byte_size = byte_size; - } - - /** - * @return the character_size - */ - public String getCharacter_size() { - return character_size; - } - - /** - * @param character_size the character_size to set - */ - public void setCharacter_size(String character_size) { - this.character_size = character_size; - } - - /** - * @return the linebreak - */ - public int getLinebreak() { - return linebreak; - } - - /** - * @return the linebreak in String form - */ - public String getLinebreakString() { - if (linebreak == NILL) { - return LINEBREAK[LINEBREAK_CRLF]; // default !!! - } - return LINEBREAK[linebreak]; + return BYTE_ORDER[byte_order]; + } + + /** @param byte_order the byte_order to set */ + public void setByte_order(int byte_order) { + this.byte_order = byte_order; + } + + /** @return the byte_size */ + public String getByte_size() { + return byte_size; + } + + /** @param byte_size the byte_size to set */ + public void setByte_size(String byte_size) { + this.byte_size = byte_size; + } + + /** @return the character_size */ + public String getCharacter_size() { + return character_size; + } + + /** @param character_size the character_size to set */ + public void setCharacter_size(String character_size) { + this.character_size = character_size; + } + + /** @return the linebreak */ + public int getLinebreak() { + return linebreak; + } + + /** @return the linebreak in String form */ + public String getLinebreakString() { + if (linebreak == NILL) { + return LINEBREAK[LINEBREAK_CRLF]; // default !!! } - - /** - * @param linebreak the linebreak to set - */ - public void setLinebreak(int linebreak) { - this.linebreak = linebreak; - } - - /** - * @return the language - */ - public String getLanguage() { - return language; - } - - /** - * @param language the language to set - */ - public void setLanguage(String language) { - this.language = toISO_639_2(language); - } - - /** - * @return the markup_basis - */ - public String getMarkup_basis() { - return markup_basis; - } - - /** - * @param markup_basis the markup_basis to set - */ - public void setMarkup_basis(String markup_basis) { - this.markup_basis = markup_basis; - } - - /** - * @return the markup_basis_version - */ - public String getMarkup_basis_version() { - return markup_basis_version; - } - - /** - * @param markup_basis_version the markup_basis_version to set - */ - public void setMarkup_basis_version(String markup_basis_version) { - this.markup_basis_version = markup_basis_version; - } - - /** - * @return the markup_language - */ - public String getMarkup_language() { - return markup_language; - } - - /** - * @param markup_language the markup_language to set - */ - public void setMarkup_language(String markup_language) { - this.markup_language = markup_language; - } - - /** - * @return the markup_language_version - */ - public String getMarkup_language_version() { - return markup_language_version; - } - - /** - * @param markup_language_version the markup_language_version to set - */ - public void setMarkup_language_version(String markup_language_version) { - this.markup_language_version = markup_language_version; - } - - /** - * Transform a given charset in the "authorized" list given in the textMD schema enumeration. - * From the schema documentation on charset (http://www.loc.gov/standards/textMD/elementSet/index.html#element_charset). - * The character set employed by the text. Controlled vocab using IANA names for character sets: - * http://www.iana.org/assignments/character-sets. - * The problem arises because the java Charset uses the (preferred MIME name) where textMD uses the Name ... - * @param srcCharset charset from the file - * @return normalized charset - */ - public static String toTextMDCharset(String srcCharset) { - if (srcCharset == null) return null; - - Charset cs = null; - String textMDCharset = null; - try { - cs = Charset.forName(srcCharset); - textMDCharset = cs.name(); - } catch (Exception e) { - // Try a unknown one - if (setOfUnknownJavaCharset.contains(srcCharset)) { - textMDCharset = srcCharset; - } else { - // Downgrade to default - textMDCharset = CHARSET_ISO8859_1; - } - } - if (textMDCharset != null) { - return textMDCharset; - } + return LINEBREAK[linebreak]; + } + + /** @param linebreak the linebreak to set */ + public void setLinebreak(int linebreak) { + this.linebreak = linebreak; + } + + /** @return the language */ + public String getLanguage() { + return language; + } + + /** @param language the language to set */ + public void setLanguage(String language) { + this.language = toISO_639_2(language); + } + + /** @return the markup_basis */ + public String getMarkup_basis() { + return markup_basis; + } + + /** @param markup_basis the markup_basis to set */ + public void setMarkup_basis(String markup_basis) { + this.markup_basis = markup_basis; + } + + /** @return the markup_basis_version */ + public String getMarkup_basis_version() { + return markup_basis_version; + } + + /** @param markup_basis_version the markup_basis_version to set */ + public void setMarkup_basis_version(String markup_basis_version) { + this.markup_basis_version = markup_basis_version; + } + + /** @return the markup_language */ + public String getMarkup_language() { + return markup_language; + } + + /** @param markup_language the markup_language to set */ + public void setMarkup_language(String markup_language) { + this.markup_language = markup_language; + } + + /** @return the markup_language_version */ + public String getMarkup_language_version() { + return markup_language_version; + } + + /** @param markup_language_version the markup_language_version to set */ + public void setMarkup_language_version(String markup_language_version) { + this.markup_language_version = markup_language_version; + } + + /** + * Transform a given charset in the "authorized" list given in the textMD schema enumeration. From + * the schema documentation on charset + * (http://www.loc.gov/standards/textMD/elementSet/index.html#element_charset). The character set + * employed by the text. Controlled vocab using IANA names for character sets: + * http://www.iana.org/assignments/character-sets. The problem arises because the java Charset + * uses the (preferred MIME name) where textMD uses the Name ... + * + * @param srcCharset charset from the file + * @return normalized charset + */ + public static String toTextMDCharset(String srcCharset) { + if (srcCharset == null) return null; + + Charset cs = null; + String textMDCharset = null; + try { + cs = Charset.forName(srcCharset); + textMDCharset = cs.name(); + } catch (Exception e) { + // Try a unknown one + if (setOfUnknownJavaCharset.contains(srcCharset)) { + textMDCharset = srcCharset; + } else { // Downgrade to default - return CHARSET_ISO8859_1; + textMDCharset = CHARSET_ISO8859_1; + } + } + if (textMDCharset != null) { + return textMDCharset; } - - /** - * Transform a language to the ISO_639-2 language (only enumeration allowed in textMD schema). - * @param srcLang language in the file - * @return normalized language in 3 letters (except qaa-qtz) - */ - public static String toISO_639_2(String srcLang) { - if (srcLang == null) return null; - if ("qaa-qtz".equals(srcLang)) return srcLang; - - String textMDLang = null; - if (srcLang.length() == 3) { - textMDLang = srcLang; - } - else if (srcLang.length() == 2) { - try { - Locale loc = new Locale(srcLang); - textMDLang = loc.getISO3Language(); - } catch (Exception e) { - // Unknown language - } - } - else if (srcLang.length() > 3) { - // Just try with the first 2 characters - try { - Locale loc = new Locale(srcLang.substring(0, 2)); - srcLang = loc.getISO3Language(); - } catch (Exception e) { - // Unknown language - } - } - if (textMDLang != null && textMDLang.length() == 3) { - // From ISO 639-2/T to ISO 639-2/B - if (fromISO_639_2_T2B.containsKey(textMDLang)) { - textMDLang = (String)fromISO_639_2_T2B.get(textMDLang); - } - return textMDLang; - } - return null; + // Downgrade to default + return CHARSET_ISO8859_1; + } + /** + * Transform a language to the ISO_639-2 language (only enumeration allowed in textMD schema). + * + * @param srcLang language in the file + * @return normalized language in 3 letters (except qaa-qtz) + */ + public static String toISO_639_2(String srcLang) { + if (srcLang == null) return null; + if ("qaa-qtz".equals(srcLang)) return srcLang; + + String textMDLang = null; + if (srcLang.length() == 3) { + textMDLang = srcLang; + } else if (srcLang.length() == 2) { + try { + Locale loc = new Locale(srcLang); + textMDLang = loc.getISO3Language(); + } catch (Exception e) { + // Unknown language + } + } else if (srcLang.length() > 3) { + // Just try with the first 2 characters + try { + Locale loc = new Locale(srcLang.substring(0, 2)); + srcLang = loc.getISO3Language(); + } catch (Exception e) { + // Unknown language + } + } + if (textMDLang != null && textMDLang.length() == 3) { + // From ISO 639-2/T to ISO 639-2/B + if (fromISO_639_2_T2B.containsKey(textMDLang)) { + textMDLang = (String) fromISO_639_2_T2B.get(textMDLang); + } + return textMDLang; } + return null; + } } diff --git a/jhove-core/src/main/java/edu/harvard/hul/ois/jhove/Utils.java b/jhove-core/src/main/java/edu/harvard/hul/ois/jhove/Utils.java index 6881b000c..16edfd28b 100644 --- a/jhove-core/src/main/java/edu/harvard/hul/ois/jhove/Utils.java +++ b/jhove-core/src/main/java/edu/harvard/hul/ois/jhove/Utils.java @@ -1,197 +1,195 @@ -/** - * - */ +/** */ package edu.harvard.hul.ois.jhove; import java.util.List; import java.util.Map; import java.util.Set; -/** - * @author cfw - * - */ +/** @author cfw */ public enum Utils { - INSTANCE; - private static final String amp = "&"; - private static final String ampsym = "amp;"; - private static final String lt = "<"; - private static final String ltsym = "<"; - private static final String gt = ">"; - private static final String gtsym = ">"; - private static final String qut = "\""; - private static final String qutsym = """; + INSTANCE; + private static final String amp = "&"; + private static final String ampsym = "amp;"; + private static final String lt = "<"; + private static final String ltsym = "<"; + private static final String gt = ">"; + private static final String gtsym = ">"; + private static final String qut = "\""; + private static final String qutsym = """; - /** - * Encodes a content String in XML-clean form, converting characters to entities - * as necessary and removing control characters disallowed by XML. The null - * string will be converted to an empty string. - */ - public static String encodeContent(String content) { - StringBuffer buffer = (content == null) ? new StringBuffer("") : new StringBuffer(content); + /** + * Encodes a content String in XML-clean form, converting characters to entities as necessary and + * removing control characters disallowed by XML. The null string will be converted to an empty + * string. + */ + public static String encodeContent(String content) { + StringBuffer buffer = (content == null) ? new StringBuffer("") : new StringBuffer(content); - /* Remove disallowed control characters from the content string. */ - int n = buffer.length(); - for (int i = 0; i < n; i++) { - char ch = buffer.charAt(i); - if ((0x00 <= ch && ch <= 0x08) || (0x0b <= ch && ch <= 0x0c) || (0x0e <= ch && ch <= 0x1f) || 0x7f == ch) { - buffer.deleteCharAt(i--); - n--; - } - } - n = 0; - while ((n = buffer.indexOf(amp, n)) > -1) { - buffer.insert(n + 1, ampsym); - n += ampsym.length(); - } - n = 0; - while ((n = buffer.indexOf(lt, n)) > -1) { - buffer.replace(n, n + 1, ltsym); - n += ltsym.length(); - } - n = 0; - while ((n = buffer.indexOf(gt, n)) > -1) { - buffer.replace(n, n + 1, gtsym); - n += gtsym.length(); - } + /* Remove disallowed control characters from the content string. */ + int n = buffer.length(); + for (int i = 0; i < n; i++) { + char ch = buffer.charAt(i); + if ((0x00 <= ch && ch <= 0x08) + || (0x0b <= ch && ch <= 0x0c) + || (0x0e <= ch && ch <= 0x1f) + || 0x7f == ch) { + buffer.deleteCharAt(i--); + n--; + } + } + n = 0; + while ((n = buffer.indexOf(amp, n)) > -1) { + buffer.insert(n + 1, ampsym); + n += ampsym.length(); + } + n = 0; + while ((n = buffer.indexOf(lt, n)) > -1) { + buffer.replace(n, n + 1, ltsym); + n += ltsym.length(); + } + n = 0; + while ((n = buffer.indexOf(gt, n)) > -1) { + buffer.replace(n, n + 1, gtsym); + n += gtsym.length(); + } - return buffer.toString(); - } + return buffer.toString(); + } - /** - * Encodes an attribute value String in XML-clean form, converting quote - * characters to entities and removing control characters disallowed by XML. - */ - public static String encodeValue(String value) { - StringBuffer buffer = new StringBuffer(value); + /** + * Encodes an attribute value String in XML-clean form, converting quote characters to entities + * and removing control characters disallowed by XML. + */ + public static String encodeValue(String value) { + StringBuffer buffer = new StringBuffer(value); - /* Remove disallowed control characters from the value string. */ - int n = buffer.length(); - for (int i = 0; i < n; i++) { - char ch = buffer.charAt(i); - if ((0x00 <= ch && ch <= 0x08) || (0x0b <= ch && ch <= 0x0c) || (0x0e <= ch && ch <= 0x1f) || 0x7f == ch) { - buffer.deleteCharAt(i--); - n--; - } - } - // [CC], escape &, < and > characters which are disallowed in xml - n = 0; - while ((n = buffer.indexOf(amp, n)) > -1) { - buffer.insert(n + 1, ampsym); - n += ampsym.length(); - } - n = 0; - while ((n = buffer.indexOf(lt, n)) > -1) { - buffer.replace(n, n + 1, ltsym); - n += ltsym.length(); - } - n = 0; - while ((n = buffer.indexOf(gt, n)) > -1) { - buffer.replace(n, n + 1, gtsym); - n += gtsym.length(); - } - n = 0; - while ((n = buffer.indexOf(qut, n)) > -1) { - // [LP] fix for invalid escaping, "" quotes were accidentally left in place. - buffer.replace(n, n + 1, qutsym); - n += qutsym.length(); - } + /* Remove disallowed control characters from the value string. */ + int n = buffer.length(); + for (int i = 0; i < n; i++) { + char ch = buffer.charAt(i); + if ((0x00 <= ch && ch <= 0x08) + || (0x0b <= ch && ch <= 0x0c) + || (0x0e <= ch && ch <= 0x1f) + || 0x7f == ch) { + buffer.deleteCharAt(i--); + n--; + } + } + // [CC], escape &, < and > characters which are disallowed in xml + n = 0; + while ((n = buffer.indexOf(amp, n)) > -1) { + buffer.insert(n + 1, ampsym); + n += ampsym.length(); + } + n = 0; + while ((n = buffer.indexOf(lt, n)) > -1) { + buffer.replace(n, n + 1, ltsym); + n += ltsym.length(); + } + n = 0; + while ((n = buffer.indexOf(gt, n)) > -1) { + buffer.replace(n, n + 1, gtsym); + n += gtsym.length(); + } + n = 0; + while ((n = buffer.indexOf(qut, n)) > -1) { + // [LP] fix for invalid escaping, "" quotes were accidentally left in place. + buffer.replace(n, n + 1, qutsym); + n += qutsym.length(); + } - return buffer.toString(); - } + return buffer.toString(); + } - /** - * Checks if a property would produce an empty XML element, and returns true if - * it would. - */ - public static boolean isPropertyEmpty(Property property, PropertyArity arity) { - try { - if (arity.equals(PropertyArity.SET)) { - Set propSet = (Set) property.getValue(); - return (propSet.isEmpty()); - } else if (arity.equals(PropertyArity.LIST)) { - List propList = (List) property.getValue(); - return (propList.isEmpty()); - } else if (arity.equals(PropertyArity.MAP)) { - Map propMap = (Map) property.getValue(); - return (propMap.isEmpty()); - } else if (arity.equals(PropertyArity.ARRAY)) { - // Ack! Is there any easy way to do this? - boolean[] boolArray = null; - byte[] byteArray = null; - char[] charArray = null; - java.util.Date[] dateArray = null; - double[] doubleArray = null; - float[] floatArray = null; - int[] intArray = null; - long[] longArray = null; - Object[] objArray = null; - Property[] propArray = null; - short[] shortArray = null; - String[] stringArray = null; - Rational[] rationalArray = null; - NisoImageMetadata[] nisoArray = null; - AESAudioMetadata[] aesArray = null; - TextMDMetadata[] textMDArray = null; - int n = 0; + /** Checks if a property would produce an empty XML element, and returns true if it would. */ + public static boolean isPropertyEmpty(Property property, PropertyArity arity) { + try { + if (arity.equals(PropertyArity.SET)) { + Set propSet = (Set) property.getValue(); + return (propSet.isEmpty()); + } else if (arity.equals(PropertyArity.LIST)) { + List propList = (List) property.getValue(); + return (propList.isEmpty()); + } else if (arity.equals(PropertyArity.MAP)) { + Map propMap = (Map) property.getValue(); + return (propMap.isEmpty()); + } else if (arity.equals(PropertyArity.ARRAY)) { + // Ack! Is there any easy way to do this? + boolean[] boolArray = null; + byte[] byteArray = null; + char[] charArray = null; + java.util.Date[] dateArray = null; + double[] doubleArray = null; + float[] floatArray = null; + int[] intArray = null; + long[] longArray = null; + Object[] objArray = null; + Property[] propArray = null; + short[] shortArray = null; + String[] stringArray = null; + Rational[] rationalArray = null; + NisoImageMetadata[] nisoArray = null; + AESAudioMetadata[] aesArray = null; + TextMDMetadata[] textMDArray = null; + int n = 0; - PropertyType propType = property.getType(); - if (PropertyType.BOOLEAN.equals(propType)) { - boolArray = (boolean[]) property.getValue(); - n = boolArray.length; - } else if (PropertyType.BYTE.equals(propType)) { - byteArray = (byte[]) property.getValue(); - n = byteArray.length; - } else if (PropertyType.CHARACTER.equals(propType)) { - charArray = (char[]) property.getValue(); - n = charArray.length; - } else if (PropertyType.DATE.equals(propType)) { - dateArray = (java.util.Date[]) property.getValue(); - n = dateArray.length; - } else if (PropertyType.DOUBLE.equals(propType)) { - doubleArray = (double[]) property.getValue(); - n = doubleArray.length; - } else if (PropertyType.FLOAT.equals(propType)) { - floatArray = (float[]) property.getValue(); - n = floatArray.length; - } else if (PropertyType.INTEGER.equals(propType)) { - intArray = (int[]) property.getValue(); - n = intArray.length; - } else if (PropertyType.LONG.equals(propType)) { - longArray = (long[]) property.getValue(); - n = longArray.length; - } else if (PropertyType.OBJECT.equals(propType)) { - objArray = (Object[]) property.getValue(); - n = objArray.length; - } else if (PropertyType.SHORT.equals(propType)) { - shortArray = (short[]) property.getValue(); - n = shortArray.length; - } else if (PropertyType.STRING.equals(propType)) { - stringArray = (String[]) property.getValue(); - n = stringArray.length; - } else if (PropertyType.RATIONAL.equals(propType)) { - rationalArray = (Rational[]) property.getValue(); - n = rationalArray.length; - } else if (PropertyType.PROPERTY.equals(propType)) { - propArray = (Property[]) property.getValue(); - n = propArray.length; - } else if (PropertyType.NISOIMAGEMETADATA.equals(propType)) { - nisoArray = (NisoImageMetadata[]) property.getValue(); - n = nisoArray.length; - } else if (PropertyType.AESAUDIOMETADATA.equals(propType)) { - aesArray = (AESAudioMetadata[]) property.getValue(); - n = aesArray.length; - } else if (PropertyType.TEXTMDMETADATA.equals(propType)) { - textMDArray = (TextMDMetadata[]) property.getValue(); - n = textMDArray.length; - } - return (n == 0); - } else { - return property.getValue().toString().length() == 0; - } - } catch (Exception e) { - // If something goes seriously wrong, return true to punt the property - return true; - } - } + PropertyType propType = property.getType(); + if (PropertyType.BOOLEAN.equals(propType)) { + boolArray = (boolean[]) property.getValue(); + n = boolArray.length; + } else if (PropertyType.BYTE.equals(propType)) { + byteArray = (byte[]) property.getValue(); + n = byteArray.length; + } else if (PropertyType.CHARACTER.equals(propType)) { + charArray = (char[]) property.getValue(); + n = charArray.length; + } else if (PropertyType.DATE.equals(propType)) { + dateArray = (java.util.Date[]) property.getValue(); + n = dateArray.length; + } else if (PropertyType.DOUBLE.equals(propType)) { + doubleArray = (double[]) property.getValue(); + n = doubleArray.length; + } else if (PropertyType.FLOAT.equals(propType)) { + floatArray = (float[]) property.getValue(); + n = floatArray.length; + } else if (PropertyType.INTEGER.equals(propType)) { + intArray = (int[]) property.getValue(); + n = intArray.length; + } else if (PropertyType.LONG.equals(propType)) { + longArray = (long[]) property.getValue(); + n = longArray.length; + } else if (PropertyType.OBJECT.equals(propType)) { + objArray = (Object[]) property.getValue(); + n = objArray.length; + } else if (PropertyType.SHORT.equals(propType)) { + shortArray = (short[]) property.getValue(); + n = shortArray.length; + } else if (PropertyType.STRING.equals(propType)) { + stringArray = (String[]) property.getValue(); + n = stringArray.length; + } else if (PropertyType.RATIONAL.equals(propType)) { + rationalArray = (Rational[]) property.getValue(); + n = rationalArray.length; + } else if (PropertyType.PROPERTY.equals(propType)) { + propArray = (Property[]) property.getValue(); + n = propArray.length; + } else if (PropertyType.NISOIMAGEMETADATA.equals(propType)) { + nisoArray = (NisoImageMetadata[]) property.getValue(); + n = nisoArray.length; + } else if (PropertyType.AESAUDIOMETADATA.equals(propType)) { + aesArray = (AESAudioMetadata[]) property.getValue(); + n = aesArray.length; + } else if (PropertyType.TEXTMDMETADATA.equals(propType)) { + textMDArray = (TextMDMetadata[]) property.getValue(); + n = textMDArray.length; + } + return (n == 0); + } else { + return property.getValue().toString().length() == 0; + } + } catch (Exception e) { + // If something goes seriously wrong, return true to punt the property + return true; + } + } } diff --git a/jhove-core/src/main/java/edu/harvard/hul/ois/jhove/XMLWrapperStream.java b/jhove-core/src/main/java/edu/harvard/hul/ois/jhove/XMLWrapperStream.java index dfa3942b7..32f398e66 100644 --- a/jhove-core/src/main/java/edu/harvard/hul/ois/jhove/XMLWrapperStream.java +++ b/jhove-core/src/main/java/edu/harvard/hul/ois/jhove/XMLWrapperStream.java @@ -1,160 +1,142 @@ -/********************************************************************** - * Jhove - JSTOR/Harvard Object Validation Environment - * Copyright 2003 by JSTOR and the President and Fellows of Harvard College +/** + * ******************************************************************** Jhove - JSTOR/Harvard Object + * Validation Environment Copyright 2003 by JSTOR and the President and Fellows of Harvard College * - **********************************************************************/ - + *

******************************************************************** + */ package edu.harvard.hul.ois.jhove; import java.io.IOException; import java.io.InputStream; /** - * This class serves to provide an InputStream for an XML - * fragment (e.g., embedded XMP metadata). - * + * This class serves to provide an InputStream for an XML fragment (e.g., embedded XMP metadata). + * * @author Gary McGath */ -public class XMLWrapperStream extends InputStream -{ - - private String xmlDecl; - private String rootStart; - private String rootEnd; - private int strIndex; - private int state; +public class XMLWrapperStream extends InputStream { + + private String xmlDecl; + private String rootStart; + private String rootEnd; + private int strIndex; + private int state; - /* Values for state */ - private final static int DECL = 0, - ROOT_START = 1, - CONTENT = 2, - ROOT_END = 3; + /* Values for state */ + private static final int DECL = 0, ROOT_START = 1, CONTENT = 2, ROOT_END = 3; - private InputStream _wrappedStream; + private InputStream _wrappedStream; + + /** + * Constructor. + * + * @param wrappedStream The stream which this stream will subsume. + * @param rootName The name of the root element. May be null if wrappedStream already + * contains a root element. + * @param version The XML version. Should be null or "1.0" unless there's a really good reason. + * @param encoding The name of the character encoding. May be null. + * @param standalone The value of the standalone attribute. May be null. + */ + public XMLWrapperStream( + InputStream wrappedStream, + String rootName, + String version, + String encoding, + String standalone) { + _wrappedStream = wrappedStream; + xmlDecl = ""; + state = DECL; + strIndex = 0; - /** - * Constructor. - * - * @param wrappedStream The stream which this stream will subsume. - * @param rootName The name of the root element. May be null - * if wrappedStream already contains a root element. - * @param version The XML version. Should be null or "1.0" unless - * there's a really good reason. - * @param encoding The name of the character encoding. May be null. - * @param standalone The value of the standalone attribute. May be null. - */ - public XMLWrapperStream (InputStream wrappedStream, - String rootName, - String version, - String encoding, - String standalone) - { - _wrappedStream = wrappedStream; - xmlDecl = ""; - state = DECL; + if (rootName != null) { + rootStart = "<" + rootName + ">"; + rootEnd = ""; + } else { + rootStart = ""; + rootEnd = ""; + } + } + + /** + * Constructor. Equivalent to XMLWrapperStream (wrappedStream, null, null, null, null) + * + * + * @param wrappedStream The stream which this stream will subsume. + */ + public XMLWrapperStream(InputStream wrappedStream) { + this(wrappedStream, null, null, null, null); + } + + /** + * Constructor. Equivalent to XMLWrapperStream (wrappedStream, rootName, null, null, null) + * + * + * @param wrappedStream The stream which this stream will subsume. + * @param rootName The name of the root element. May be null. + */ + public XMLWrapperStream(InputStream wrappedStream, String rootName) { + this(wrappedStream, rootName, null, null, null); + } + + /** + * Get a byte. Successive calls will return the XML declaration, then the wrapped stream. + * + * @see java.io.InputStream#read() + */ + @Override + public int read() throws IOException { + int retval; + if (state == DECL) { + if (strIndex >= xmlDecl.length()) { + // We have finished the declaration string now. + state = ROOT_START; strIndex = 0; - - if (rootName != null) { - rootStart = "<" + rootName + ">"; - rootEnd = ""; - } - else { - rootStart = ""; - rootEnd = ""; - } + } else { + // We haven't finished returning the declaration string. + return xmlDecl.charAt(strIndex++); + } } - - /** - * Constructor. Equivalent to - * XMLWrapperStream (wrappedStream, null, null, null, null) - * - * @param wrappedStream The stream which this stream will subsume. - */ - public XMLWrapperStream (InputStream wrappedStream) - { - this (wrappedStream, null, null, null, null); + + // Each state can fall through to the next + + if (state == ROOT_START) { + if (strIndex >= rootStart.length()) { + // We have finished the root element start now. + state = CONTENT; + } else { + return rootStart.charAt(strIndex++); + } } - /** - * Constructor. Equivalent to - * XMLWrapperStream (wrappedStream, rootName, null, null, null) - * - * @param wrappedStream The stream which this stream will subsume. - * @param rootName The name of the root element. May be null. - */ - public XMLWrapperStream (InputStream wrappedStream, String rootName) - { - this (wrappedStream, rootName, null, null, null); + if (state == CONTENT) { + // Metadata alleged stream doesn't look remotely like metadata -- + // probably looking at wrong part of file! + retval = _wrappedStream.read(); + if (retval == -1) { + state = ROOT_END; + strIndex = 0; + } else { + return retval; + } } - - - /** - * Get a byte. Successive calls will return the - * XML declaration, then the wrapped stream. - * - * @see java.io.InputStream#read() - */ - @Override - public int read() throws IOException - { - int retval; - if (state == DECL) { - if (strIndex >= xmlDecl.length()) { - // We have finished the declaration string now. - state = ROOT_START; - strIndex = 0; - } - else { - // We haven't finished returning the declaration string. - return xmlDecl.charAt(strIndex++); - } - } - - // Each state can fall through to the next - - if (state == ROOT_START) { - if (strIndex >= rootStart.length()) { - // We have finished the root element start now. - state = CONTENT; - } - else { - return rootStart.charAt(strIndex++); - } - } - - if (state == CONTENT) { - // Metadata alleged stream doesn't look remotely like metadata -- - // probably looking at wrong part of file! - retval = _wrappedStream.read (); - if (retval == -1) { - state = ROOT_END; - strIndex = 0; - } - else { - return retval; - } - } - - // Must be ROOT_END if it gets here - if (strIndex >= rootEnd.length()) { - // We have finished the root element end and the document now. - return -1; - } - return rootEnd.charAt(strIndex++); - - } + // Must be ROOT_END if it gets here + if (strIndex >= rootEnd.length()) { + // We have finished the root element end and the document now. + return -1; + } + return rootEnd.charAt(strIndex++); + } } diff --git a/jhove-core/src/main/java/edu/harvard/hul/ois/jhove/XMPHandler.java b/jhove-core/src/main/java/edu/harvard/hul/ois/jhove/XMPHandler.java index 0e09e6aae..4277b1f3a 100644 --- a/jhove-core/src/main/java/edu/harvard/hul/ois/jhove/XMPHandler.java +++ b/jhove-core/src/main/java/edu/harvard/hul/ois/jhove/XMPHandler.java @@ -1,144 +1,122 @@ -/********************************************************************** - * Jhove - JSTOR/Harvard Object Validation Environment - * Copyright 2003 by JSTOR and the President and Fellows of Harvard College - **********************************************************************/ - +/** + * ******************************************************************** Jhove - JSTOR/Harvard Object + * Validation Environment Copyright 2003 by JSTOR and the President and Fellows of Harvard College + * ******************************************************************** + */ package edu.harvard.hul.ois.jhove; - import org.xml.sax.*; /** - * This class encapsulates XMP metadata within a file. It makes use - * of an InputStream as a data source. + * This class encapsulates XMP metadata within a file. It makes use of an InputStream as a data + * source. * - * This differs from normal XML handling in that it's necessary to - * process the xpacket processing instruction in order to determine - * the encoding of the XML. the processingInstruction function looks - * for xpacket, and throws a special SAXException if it's necessary - * to change encoding. + *

This differs from normal XML handling in that it's necessary to process the xpacket processing + * instruction in order to determine the encoding of the XML. the processingInstruction function + * looks for xpacket, and throws a special SAXException if it's necessary to change encoding. * - * We don't actually extract any information from the XMP, but - * simply check it for well-formedness. By convention, XMPHandler - * should be invoked on an XMPSource (TBW), which provides the - * ability to recapture the InputStream from which the XMP was - * obtained and put it into a property once it's verified here. - * + *

We don't actually extract any information from the XMP, but simply check it for + * well-formedness. By convention, XMPHandler should be invoked on an XMPSource (TBW), which + * provides the ability to recapture the InputStream from which the XMP was obtained and put it into + * a property once it's verified here. */ public class XMPHandler extends org.xml.sax.helpers.DefaultHandler { - /* URI strings */ - private final static String xmpBasicSchema = - "http://ns.adobe.com/xap/1.0/"; -// private final static String xmpRightsSchema = -// "http://ns.adobe.com/xap/1.0/rights/"; -// private final static String dublinCoreSchema = -// "http://purl.org/dc/elements/1.1/"; -// private final static String adobePDFSchema = -// "http://ns.adobe.com/pdf/1.3/"; -// private final static String photoshopSchema = -// "http://ns.adobe.com/photoshop/1.0/"; + /* URI strings */ + private static final String xmpBasicSchema = "http://ns.adobe.com/xap/1.0/"; + // private final static String xmpRightsSchema = + // "http://ns.adobe.com/xap/1.0/rights/"; + // private final static String dublinCoreSchema = + // "http://purl.org/dc/elements/1.1/"; + // private final static String adobePDFSchema = + // "http://ns.adobe.com/pdf/1.3/"; + // private final static String photoshopSchema = + // "http://ns.adobe.com/photoshop/1.0/"; - private boolean pdfaCompliant; - - public XMPHandler () - { - super (); - pdfaCompliant = true; // compliance is presumed till disproven - } + private boolean pdfaCompliant; + public XMPHandler() { + super(); + pdfaCompliant = true; // compliance is presumed till disproven + } - /** Returns true if no violations of PDF/A compliance have been found, - * false if a problem was detected. */ - public boolean isPdfaCompliant () { - return pdfaCompliant; - } - - - @Override - public void processingInstruction (String target, String data) - throws SAXException - { - if ("xpacket".equals (target)) { - // We assume that the data will be non-endian (i.e., simply - // a stream of bytes) unless we find a valid endian code. - boolean bigEndian = false; - boolean noEndian = true; - // a Processing Instruction can't really have attributes, - // so we have to parse the data string ourselves. The order - // of the attributes is guaranteed, fortunately. - int idx = data.indexOf ("begin="); - idx = data.indexOf ('"', idx + 1); - if (data.length () >= idx + 2) { - int char1 = data.charAt (idx + 1); - int char2 = data.charAt (idx + 2); - if (char1 == 0XFF && char2 == 0XFE) { - noEndian = false; - bigEndian = false; - } - else if (char1 == 0XFE && char2 == 0XFF) { - noEndian = false; - bigEndian = true; - } - // EF BB B8 signifies UTF-8, but that's the default anyway. - } - // Check the bytes attribute. We don't do anything with it except - // note that it isn't allowed with PDF/A. - idx = data.indexOf("bytes="); - if (idx > 0) { - pdfaCompliant = false; - } - // Next find encoding, which is optional. - idx = data.indexOf ("encoding="); - if (idx > 0) { - pdfaCompliant = false; // not allowed in PDF/A - idx = data.indexOf ('"', idx + 1); - int encEnd = data.indexOf ('"', idx + 1 ); - String encoding = data.substring (idx + 1, encEnd); - // Throw a SAXException which consists of - // "ENC=,", where - // endian is either 'B' (big), 'L' (little) or space (none), and - // enc is the encoding attribute. - // This is an expected exception, not an error. - String exText = "ENC="; - if (noEndian) { - exText += " ,"; - } - else if (bigEndian) { - exText += "B,"; - } - else { - exText += "L,"; - } - exText += encoding; - throw new SAXException (exText); - } + /** + * Returns true if no violations of PDF/A compliance have been found, false if a problem was + * detected. + */ + public boolean isPdfaCompliant() { + return pdfaCompliant; + } + + @Override + public void processingInstruction(String target, String data) throws SAXException { + if ("xpacket".equals(target)) { + // We assume that the data will be non-endian (i.e., simply + // a stream of bytes) unless we find a valid endian code. + boolean bigEndian = false; + boolean noEndian = true; + // a Processing Instruction can't really have attributes, + // so we have to parse the data string ourselves. The order + // of the attributes is guaranteed, fortunately. + int idx = data.indexOf("begin="); + idx = data.indexOf('"', idx + 1); + if (data.length() >= idx + 2) { + int char1 = data.charAt(idx + 1); + int char2 = data.charAt(idx + 2); + if (char1 == 0XFF && char2 == 0XFE) { + noEndian = false; + bigEndian = false; + } else if (char1 == 0XFE && char2 == 0XFF) { + noEndian = false; + bigEndian = true; } - } - - - /** - * Catches the end of an element. - */ - @Override - public void endElement (String namespaceURI, String localName, - String rawName) - { - if (xmpBasicSchema.equals (namespaceURI)) { - // Check for the end of an XMP structure - if ("Bag".equals (rawName)|| - "Seq".equals (rawName) || - "Alt".equals (rawName)) { - } + // EF BB B8 signifies UTF-8, but that's the default anyway. + } + // Check the bytes attribute. We don't do anything with it except + // note that it isn't allowed with PDF/A. + idx = data.indexOf("bytes="); + if (idx > 0) { + pdfaCompliant = false; + } + // Next find encoding, which is optional. + idx = data.indexOf("encoding="); + if (idx > 0) { + pdfaCompliant = false; // not allowed in PDF/A + idx = data.indexOf('"', idx + 1); + int encEnd = data.indexOf('"', idx + 1); + String encoding = data.substring(idx + 1, encEnd); + // Throw a SAXException which consists of + // "ENC=,", where + // endian is either 'B' (big), 'L' (little) or space (none), and + // enc is the encoding attribute. + // This is an expected exception, not an error. + String exText = "ENC="; + if (noEndian) { + exText += " ,"; + } else if (bigEndian) { + exText += "B,"; + } else { + exText += "L,"; } + exText += encoding; + throw new SAXException(exText); + } } - - /** Catch a fatal error. This is put here because the default - * behavior is to report a "fatal error" to standard output, - * which is harmless but scary. - */ - @Override - public void fatalError(SAXParseException exception) - { + } + + /** Catches the end of an element. */ + @Override + public void endElement(String namespaceURI, String localName, String rawName) { + if (xmpBasicSchema.equals(namespaceURI)) { + // Check for the end of an XMP structure + if ("Bag".equals(rawName) || "Seq".equals(rawName) || "Alt".equals(rawName)) {} } + } + + /** + * Catch a fatal error. This is put here because the default behavior is to report a "fatal error" + * to standard output, which is harmless but scary. + */ + @Override + public void fatalError(SAXParseException exception) {} } diff --git a/jhove-core/src/main/java/edu/harvard/hul/ois/jhove/XMPSource.java b/jhove-core/src/main/java/edu/harvard/hul/ois/jhove/XMPSource.java index 878696f34..b42806951 100644 --- a/jhove-core/src/main/java/edu/harvard/hul/ois/jhove/XMPSource.java +++ b/jhove-core/src/main/java/edu/harvard/hul/ois/jhove/XMPSource.java @@ -1,118 +1,99 @@ -/********************************************************************** - * Jhove - JSTOR/Harvard Object Validation Environment - * Copyright 2003 by JSTOR and the President and Fellows of Harvard College - **********************************************************************/ - +/** + * ******************************************************************** Jhove - JSTOR/Harvard Object + * Validation Environment Copyright 2003 by JSTOR and the President and Fellows of Harvard College + * ******************************************************************** + */ package edu.harvard.hul.ois.jhove; -//import java.io.InputStream; +// import java.io.InputStream; +import java.io.IOException; import java.io.Reader; - import org.xml.sax.InputSource; -import java.io.IOException; /** - * Class for providing an InputSource to XMPHandler. - * Only an InputSource based on a Reader is supported. - * - * Each module can provide its own subclass of XMPSource. - * The subclass must provide a way to - * reset to the beginning and reread the data when makeProperty - * is called. - * - * @author Gary McGath + * Class for providing an InputSource to XMPHandler. Only an InputSource based on a Reader is + * supported. + * + *

Each module can provide its own subclass of XMPSource. The subclass must provide a way to + * reset to the beginning and reread the data when makeProperty is called. * + * @author Gary McGath */ public abstract class XMPSource extends InputSource { - /** The Reader on which the InputSource is based. */ - protected Reader _reader; - - - /** - * Constructor with Reader. - */ - public XMPSource(Reader rdr) { - super(rdr); - _reader = rdr; - } + /** The Reader on which the InputSource is based. */ + protected Reader _reader; + /** Constructor with Reader. */ + public XMPSource(Reader rdr) { + super(rdr); + _reader = rdr; + } - /** - * Generates a property from the underlying data. - * The beginning and ending processing instructions are - * stripped out. - */ - public Property makeProperty () throws IOException - { - boolean maybePI = false; // set to true for ?xpacket partial match - boolean seenStart = false; // set to true after initial xpacket - resetReader (); // go back to the beginning - StringBuffer textBuf = new StringBuffer (); - StringBuffer xpacBuf = new StringBuffer (); - for (;;) { - int ch = _reader.read(); - if (ch < 0) { + /** + * Generates a property from the underlying data. The beginning and ending processing instructions + * are stripped out. + */ + public Property makeProperty() throws IOException { + boolean maybePI = false; // set to true for ?xpacket partial match + boolean seenStart = false; // set to true after initial xpacket + resetReader(); // go back to the beginning + StringBuffer textBuf = new StringBuffer(); + StringBuffer xpacBuf = new StringBuffer(); + for (; ; ) { + int ch = _reader.read(); + if (ch < 0) { + break; + } + if (maybePI) { + xpacBuf.append((char) ch); + if (" + seenStart = true; + xpacBuf.setLength(0); + maybePI = false; + int prevCh = 0; + for (; ; ) { + ch = _reader.read(); + if (ch < 0 || (prevCh == '?' && ch == '>')) { break; + } + prevCh = ch; } - if (maybePI) { - xpacBuf.append((char) ch); - if (" - seenStart = true; - xpacBuf.setLength (0); - maybePI = false; - int prevCh = 0; - for (;;) { - ch = _reader.read (); - if (ch < 0 || - (prevCh == '?' && ch == '>')) { - break; - } - prevCh = ch; - } - } - else { - // This is the ending xpacket. Discard it. - // We're done. - break; - } - } - if (!". - // Start buffering. - maybePI = true; - xpacBuf.append ((char) ch); - } - else { - // Just plain text. Append it. - textBuf.append ((char) ch); - } - } + } else { + // This is the ending xpacket. Discard it. + // We're done. + break; + } + } + if (!". + // Start buffering. + maybePI = true; + xpacBuf.append((char) ch); + } else { + // Just plain text. Append it. + textBuf.append((char) ch); } - // Some XMP's end with lots of white space, so give - // it a trim before returning. - return new Property ("XMP", - PropertyType.STRING, - textBuf.toString ().trim ()); + } } - - - /** - * Causes reading to begin from the start again. - * Typically this means creating a new value for - * _reader that will start over. - */ - protected abstract void resetReader (); + // Some XMP's end with lots of white space, so give + // it a trim before returning. + return new Property("XMP", PropertyType.STRING, textBuf.toString().trim()); + } + /** + * Causes reading to begin from the start again. Typically this means creating a new value for + * _reader that will start over. + */ + protected abstract void resetReader(); } diff --git a/jhove-core/src/main/java/edu/harvard/hul/ois/jhove/handler/AuditHandler.java b/jhove-core/src/main/java/edu/harvard/hul/ois/jhove/handler/AuditHandler.java index 2f7b475fb..7cfa3d2b2 100644 --- a/jhove-core/src/main/java/edu/harvard/hul/ois/jhove/handler/AuditHandler.java +++ b/jhove-core/src/main/java/edu/harvard/hul/ois/jhove/handler/AuditHandler.java @@ -1,23 +1,19 @@ -/********************************************************************** - * Audit output handler +/** + * ******************************************************************** Audit output handler * Copyright 2004 by the President and Fellows of Harvard College * - * This program 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 2 of the License, or (at - * your option) any later version. + *

This program 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 2 + * of the License, or (at your option) any later version. * - * This program 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. + *

This program 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. * - * You should have received a copy of the GNU Lesser General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 - * USA - **********************************************************************/ - + *

You should have received a copy of the GNU Lesser General Public License along with this + * program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA ******************************************************************** + */ package edu.harvard.hul.ois.jhove.handler; import edu.harvard.hul.ois.jhove.Checksum; @@ -25,358 +21,390 @@ import edu.harvard.hul.ois.jhove.RepInfo; import edu.harvard.hul.ois.jhove.handler.audit.AuditCount; import edu.harvard.hul.ois.jhove.handler.audit.AuditState; - import java.util.*; /** - * JHOVE audit output handler, derived from the standard JHOVE XML handler. - * It is expected that this class will be used as the parent for other, more - * interesting output handlers. Subclasses should override the implementations - * of the Impl methods, e.g. endDirectoryImpl(). + * JHOVE audit output handler, derived from the standard JHOVE XML handler. It is expected that this + * class will be used as the parent for other, more interesting output handlers. Subclasses should + * override the implementations of the Impl methods, e.g. endDirectoryImpl(). */ public class AuditHandler extends XmlHandler { - /** Audit output handler name. */ - private static final String NAME = "Audit"; - - /** Audit output handler release ID. */ - private static final String RELEASE = "1.1"; - - /** Audit output handler release date. */ - private static final int[] DATE = {2005, 4, 22}; - - /** Audit output handler informative note. */ - private static final String NOTE = - "This output handler is derived from the standard JHOVE XML output " + - "handler. It is intended to be used as the parent class for other, " + - "more interesting handlers."; - - /** Audit output handler rights statement. */ - private static final String RIGHTS = - "Copyright 2004-2005 by the President and Fellows of Harvard College. " + - "Released under the GNU LGPL license."; - - /** Home directory of the audit. */ - protected String _home; - - /** Number of files processed by MIME type. */ - protected Map _mimeType; - - /** State map. */ - protected Map _stateMap; - - /** State stack. */ - protected Stack _stateStack; - - /** Initial time. */ - protected long _t0; - - /** Number of files audited. */ - protected int _nAudit; - - /** - * Instantiate an AuditHandler object. - */ - public AuditHandler() { - super(NAME, RELEASE, DATE, NOTE, RIGHTS); - - // Define the standard output handler properties. - _name = NAME; - _release = RELEASE; - Calendar calendar = new GregorianCalendar(); - calendar.set(DATE[0], DATE[1] - 1, DATE[2]); - _date = calendar.getTime(); - _note = NOTE; - _rights = RIGHTS; - - // Initialize the handler. - _mimeType = new TreeMap<>(Comparator.nullsFirst(Comparator.naturalOrder())); - _stateMap = new TreeMap<>(); - _stateStack = new Stack<>(); - _nAudit = 0; - } - - /** - * Callback indicating a directory is finished being processed. Pop the - * state stack and place the current directory file count into the directory - * hash. - */ - @Override - public final void endDirectory() { - AuditState state = _stateStack.pop(); - _stateMap.put(state.getDirectory(), state); - - endDirectoryImpl(); - } - - /** - * Local extension to the standard callback indicating a directory is - * finished being processed. - */ - public void endDirectoryImpl() { - } - - /** - * Outputs the information contained in a RepInfo object. - * - * @param info Object representation information - */ - @Override - public void show(RepInfo info) { - AuditState state = _stateStack.peek(); - - String mime = info.getMimeType(); - AuditCount count = _mimeType.get(mime); - if (count == null) count = new AuditCount(); - - if (info.getWellFormed() == RepInfo.TRUE) { - if (info.getValid() == RepInfo.TRUE) { - state.setValid(state.getValid() + 1); - count.setValid(count.getValid() + 1); - } else { - state.setWellFormed(state.getWellFormed() + 1); - count.setWellFormed(count.getWellFormed() + 1); - } - } else if (info.getWellFormed() == RepInfo.FALSE) { - state.setNotWellFormed(state.getNotWellFormed() + 1); - count.setNotWellFormed(count.getNotWellFormed() + 1); - } else { - state.setUndetermined(state.getUndetermined() + 1); - count.setUndetermined(count.getUndetermined() + 1); - } - - _mimeType.put(mime, count); - - showImpl(info); - } - - /** - * Local extension to the standard callback that outputs the information - * contained in a RepInfo object. - * - * @param info Object representation information - */ - public void showImpl(RepInfo info) { - - String status; - switch (info.getWellFormed()) { - - case RepInfo.TRUE: - if (info.getValid() == RepInfo.TRUE) - status = "valid"; - else - status = "well-formed"; - break; - - case RepInfo.FALSE: - status = "not well-formed"; - break; - - default: - status = "unknown"; - } - - // Retrieve the MD5 checksum, if available. - String md5 = null; - for (Checksum checksum : info.getChecksum()) { - if (checksum.getType().equals(ChecksumType.MD5)) { - md5 = checksum.getValue(); - break; - } - } - - if (_nAudit == 0) { - String margin = getIndent(++_level); - - String[][] attrs = {{"home", _home}}; - _writer.println(margin + elementStart("audit", attrs)); - } - - String margn2 = getIndent(_level) + " "; - - String uri = info.getUri(); - - // Change the URI to a relative path by removing the home - // directory prefix. - int n = uri.indexOf(_home); - if (n == 0) { - uri = uri.substring(_home.length() + 1); - } - - String mime = info.getMimeType(); - - String[][] attrs = {{"mime", mime}, {"status", status}, {"md5", md5}}; - _writer.println(margn2 + element("file", attrs, uri)); - _nAudit++; - } - - /** - * Do the final output. This should be in a suitable format for including - * multiple files between the header and the footer, and the XML of the - * header and footer must balance out. - */ - @Override - public void showFooter() { - AuditState state = _stateStack.pop(); - if (state.getTotal() > 0) { - _stateMap.put(state.getDirectory(), state); - } - - showFooterImpl(); - - _writer.println(""); - - _writer.println(""); - - // Update the elapsed time. - long dt = (System.currentTimeMillis() - _t0 + 999) / 1000; - - long ss = dt % 60; - long dm = dt / 60; - long mm = dm % 60; - long hh = dm / 60; - - _writer.println(""); - _writer.flush(); - } - - /** - * Local extension to the standard callback that does the final output. This - * should be in a suitable format for including multiple files between the - * header and the footer, and the XML of the header and footer must balance - * out. - */ - public void showFooterImpl() { - if (_nAudit > 0) { - String margin = getIndent(_level--); - _writer.println(margin + elementEnd("audit")); - } - super.showFooter(); - } - - /** - * Do the initial output. This should be in a suitable format for including - * multiple files between the header and the footer, and the XML of the - * header and footer must balance out. - */ - @Override - public void showHeader() { - - // Initialize the handler. - _mimeType = new TreeMap<>(Comparator.nullsFirst(Comparator.naturalOrder())); - _stateMap = new TreeMap<>(); - _stateStack = new Stack<>(); - _nAudit = 0; - - _t0 = System.currentTimeMillis(); - - // Instantiate a state object and initialize with the values - // of the global configuration file. - AuditState state = showHeaderImpl("."); - _stateStack.push(state); - _home = state.getDirectory(); - } - - /** - * Local extension to the standard callback that does the initial output. - * This should be in a suitable format for including multiple files between - * the header and the footer, and the XML of the header and footer must - * balance out. - * - * @param directory Current directory filepath - */ - public AuditState showHeaderImpl(String directory) { - super.showHeader(); - return new AuditState(directory); - } - - /** - * Callback indicating a new directory is being processed. - * - * Additional state information can be added to the AuditState object - * in the showHeaderImpl() method before it is pushed onto the stack. - */ - @Override - public void startDirectory(String directory) { - try { - AuditState state = (AuditState) - _stateStack.peek().clone(directory); - - startDirectoryImpl(state); - _stateStack.push(state); - } catch (CloneNotSupportedException cnse) { - cnse.printStackTrace(System.err); - System.exit(-1); - } - } - - /** - * Local extension to the standard callback indicating a new directory is - * being processed. - * - * @param state Audit handler state - */ - public void startDirectoryImpl(AuditState state) { - } + /** Audit output handler name. */ + private static final String NAME = "Audit"; + + /** Audit output handler release ID. */ + private static final String RELEASE = "1.1"; + + /** Audit output handler release date. */ + private static final int[] DATE = {2005, 4, 22}; + + /** Audit output handler informative note. */ + private static final String NOTE = + "This output handler is derived from the standard JHOVE XML output " + + "handler. It is intended to be used as the parent class for other, " + + "more interesting handlers."; + + /** Audit output handler rights statement. */ + private static final String RIGHTS = + "Copyright 2004-2005 by the President and Fellows of Harvard College. " + + "Released under the GNU LGPL license."; + + /** Home directory of the audit. */ + protected String _home; + + /** Number of files processed by MIME type. */ + protected Map _mimeType; + + /** State map. */ + protected Map _stateMap; + + /** State stack. */ + protected Stack _stateStack; + + /** Initial time. */ + protected long _t0; + + /** Number of files audited. */ + protected int _nAudit; + + /** Instantiate an AuditHandler object. */ + public AuditHandler() { + super(NAME, RELEASE, DATE, NOTE, RIGHTS); + + // Define the standard output handler properties. + _name = NAME; + _release = RELEASE; + Calendar calendar = new GregorianCalendar(); + calendar.set(DATE[0], DATE[1] - 1, DATE[2]); + _date = calendar.getTime(); + _note = NOTE; + _rights = RIGHTS; + + // Initialize the handler. + _mimeType = new TreeMap<>(Comparator.nullsFirst(Comparator.naturalOrder())); + _stateMap = new TreeMap<>(); + _stateStack = new Stack<>(); + _nAudit = 0; + } + + /** + * Callback indicating a directory is finished being processed. Pop the state stack and place the + * current directory file count into the directory hash. + */ + @Override + public final void endDirectory() { + AuditState state = _stateStack.pop(); + _stateMap.put(state.getDirectory(), state); + + endDirectoryImpl(); + } + + /** + * Local extension to the standard callback indicating a directory is finished being processed. + */ + public void endDirectoryImpl() {} + + /** + * Outputs the information contained in a RepInfo object. + * + * @param info Object representation information + */ + @Override + public void show(RepInfo info) { + AuditState state = _stateStack.peek(); + + String mime = info.getMimeType(); + AuditCount count = _mimeType.get(mime); + if (count == null) count = new AuditCount(); + + if (info.getWellFormed() == RepInfo.TRUE) { + if (info.getValid() == RepInfo.TRUE) { + state.setValid(state.getValid() + 1); + count.setValid(count.getValid() + 1); + } else { + state.setWellFormed(state.getWellFormed() + 1); + count.setWellFormed(count.getWellFormed() + 1); + } + } else if (info.getWellFormed() == RepInfo.FALSE) { + state.setNotWellFormed(state.getNotWellFormed() + 1); + count.setNotWellFormed(count.getNotWellFormed() + 1); + } else { + state.setUndetermined(state.getUndetermined() + 1); + count.setUndetermined(count.getUndetermined() + 1); + } + + _mimeType.put(mime, count); + + showImpl(info); + } + + /** + * Local extension to the standard callback that outputs the information contained in a RepInfo + * object. + * + * @param info Object representation information + */ + public void showImpl(RepInfo info) { + + String status; + switch (info.getWellFormed()) { + case RepInfo.TRUE: + if (info.getValid() == RepInfo.TRUE) status = "valid"; + else status = "well-formed"; + break; + + case RepInfo.FALSE: + status = "not well-formed"; + break; + + default: + status = "unknown"; + } + + // Retrieve the MD5 checksum, if available. + String md5 = null; + for (Checksum checksum : info.getChecksum()) { + if (checksum.getType().equals(ChecksumType.MD5)) { + md5 = checksum.getValue(); + break; + } + } + + if (_nAudit == 0) { + String margin = getIndent(++_level); + + String[][] attrs = {{"home", _home}}; + _writer.println(margin + elementStart("audit", attrs)); + } + + String margn2 = getIndent(_level) + " "; + + String uri = info.getUri(); + + // Change the URI to a relative path by removing the home + // directory prefix. + int n = uri.indexOf(_home); + if (n == 0) { + uri = uri.substring(_home.length() + 1); + } + + String mime = info.getMimeType(); + + String[][] attrs = {{"mime", mime}, {"status", status}, {"md5", md5}}; + _writer.println(margn2 + element("file", attrs, uri)); + _nAudit++; + } + + /** + * Do the final output. This should be in a suitable format for including multiple files between + * the header and the footer, and the XML of the header and footer must balance out. + */ + @Override + public void showFooter() { + AuditState state = _stateStack.pop(); + if (state.getTotal() > 0) { + _stateMap.put(state.getDirectory(), state); + } + + showFooterImpl(); + + _writer.println(""); + + _writer.println(""); + + // Update the elapsed time. + long dt = (System.currentTimeMillis() - _t0 + 999) / 1000; + + long ss = dt % 60; + long dm = dt / 60; + long mm = dm % 60; + long hh = dm / 60; + + _writer.println( + ""); + _writer.flush(); + } + + /** + * Local extension to the standard callback that does the final output. This should be in a + * suitable format for including multiple files between the header and the footer, and the XML of + * the header and footer must balance out. + */ + public void showFooterImpl() { + if (_nAudit > 0) { + String margin = getIndent(_level--); + _writer.println(margin + elementEnd("audit")); + } + super.showFooter(); + } + + /** + * Do the initial output. This should be in a suitable format for including multiple files between + * the header and the footer, and the XML of the header and footer must balance out. + */ + @Override + public void showHeader() { + + // Initialize the handler. + _mimeType = new TreeMap<>(Comparator.nullsFirst(Comparator.naturalOrder())); + _stateMap = new TreeMap<>(); + _stateStack = new Stack<>(); + _nAudit = 0; + + _t0 = System.currentTimeMillis(); + + // Instantiate a state object and initialize with the values + // of the global configuration file. + AuditState state = showHeaderImpl("."); + _stateStack.push(state); + _home = state.getDirectory(); + } + + /** + * Local extension to the standard callback that does the initial output. This should be in a + * suitable format for including multiple files between the header and the footer, and the XML of + * the header and footer must balance out. + * + * @param directory Current directory filepath + */ + public AuditState showHeaderImpl(String directory) { + super.showHeader(); + return new AuditState(directory); + } + + /** + * Callback indicating a new directory is being processed. + * + *

Additional state information can be added to the AuditState object in the showHeaderImpl() + * method before it is pushed onto the stack. + */ + @Override + public void startDirectory(String directory) { + try { + AuditState state = (AuditState) _stateStack.peek().clone(directory); + + startDirectoryImpl(state); + _stateStack.push(state); + } catch (CloneNotSupportedException cnse) { + cnse.printStackTrace(System.err); + System.exit(-1); + } + } + + /** + * Local extension to the standard callback indicating a new directory is being processed. + * + * @param state Audit handler state + */ + public void startDirectoryImpl(AuditState state) {} } diff --git a/jhove-core/src/main/java/edu/harvard/hul/ois/jhove/handler/JsonHandler.java b/jhove-core/src/main/java/edu/harvard/hul/ois/jhove/handler/JsonHandler.java index 5b2f8dc38..7935722f4 100644 --- a/jhove-core/src/main/java/edu/harvard/hul/ois/jhove/handler/JsonHandler.java +++ b/jhove-core/src/main/java/edu/harvard/hul/ois/jhove/handler/JsonHandler.java @@ -1,38 +1,21 @@ -/********************************************************************** - * Jhove - JSTOR/Harvard Object Validation Environment +/** + * ******************************************************************** Jhove - JSTOR/Harvard Object + * Validation Environment * - * This program 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 2 of the License, or (at - * your option) any later version. + *

This program 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 2 + * of the License, or (at your option) any later version. * - * This program 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. + *

This program 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. * - * You should have received a copy of the GNU Lesser General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 - * USA - **********************************************************************/ - + *

You should have received a copy of the GNU Lesser General Public License along with this + * program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA ******************************************************************** + */ package edu.harvard.hul.ois.jhove.handler; -import java.util.Date; -import java.util.Iterator; -import java.util.List; -import java.util.ListIterator; -import java.util.Locale; -import java.util.Map; -import java.util.Set; - -import javax.json.Json; -import javax.json.JsonArrayBuilder; -import javax.json.JsonObject; -import javax.json.JsonObjectBuilder; -import javax.json.JsonWriter; - import edu.harvard.hul.ois.jhove.AESAudioMetadata; import edu.harvard.hul.ois.jhove.Agent; import edu.harvard.hul.ois.jhove.App; @@ -54,2199 +37,2257 @@ import edu.harvard.hul.ois.jhove.SignatureType; import edu.harvard.hul.ois.jhove.TextMDMetadata; import edu.harvard.hul.ois.jhove.messages.JhoveMessages; +import java.util.Date; +import java.util.Iterator; +import java.util.List; +import java.util.ListIterator; +import java.util.Locale; +import java.util.Map; +import java.util.Set; +import javax.json.Json; +import javax.json.JsonArrayBuilder; +import javax.json.JsonObject; +import javax.json.JsonObjectBuilder; +import javax.json.JsonWriter; -/** - * OutputHandler for JSON output. - * - */ +/** OutputHandler for JSON output. */ public class JsonHandler extends HandlerBase { - /****************************************************************** - * PRIVATE CLASS FIELDS. - ******************************************************************/ - - /** Handler name. */ - private static final String NAME = "JSON"; - - /** Handler release identifier. */ - private static final String RELEASE = "1.0"; - - /** Handler release date. */ - private static final int[] DATE = { 2019, 10, 18 }; - - /** Handler informative note. */ - private static final String NOTE = ""; - - /** Handler rights statement. */ - private static final String RIGHTS = "Version 1.0 release by Open Preservation Foundation. " - + "Released under the GNU Lesser General Public License."; - - /****************************************************************** - * PRIVATE INSTANCE FIELDS. - ******************************************************************/ - - /** Main JSON builder */ - private JsonObjectBuilder jhoveBuilder; - - /* Sample rate. */ - private double _sampleRate; - - /****************************************************************** - * CLASS CONSTRUCTOR. - ******************************************************************/ - - /** - * Creates an JsonHandler. - */ - public JsonHandler() { - super(NAME, RELEASE, DATE, NOTE, RIGHTS); - _vendor = Agent.bnfInstance(); - } - - /** Constructor for use by subclasses. */ - public JsonHandler(String name, String release, int[] date, String note, String rights) { - super(name, release, date, note, rights); - _vendor = Agent.bnfInstance(); - } - - /****************************************************************** - * PUBLIC INSTANCE METHODS. - ******************************************************************/ - - /** - * Outputs minimal information about the application - */ - @Override - public void show() { - _level--; - } - - /** - * Outputs detailed information about the application, including configuration, - * available modules and handlers, etc. - */ - @Override - public void show(App app) { - JsonObjectBuilder appBuilder = Json.createObjectBuilder(); - - JsonObjectBuilder apiBuilder = Json.createObjectBuilder(); - apiBuilder.add("version", app.getRelease()); - apiBuilder.add("date", date.format(_je.getDate())); - appBuilder.add("api", apiBuilder); - - String configFile = _je.getConfigFile(); - if (configFile != null) { - appBuilder.add("configuration", configFile); - } - String s = _je.getSaxClass(); - if (s != null) { - appBuilder.add("saxParser", s); - } - s = _je.getJhoveHome(); - if (s != null) { - appBuilder.add("jhoveHome", s); - } - s = _je.getEncoding(); - if (s != null) { - appBuilder.add("encoding", s); - } - s = _je.getTempDirectory(); - if (s != null) { - appBuilder.add("tempDirectory", s); - } - appBuilder.add("bufferSize", _je.getBufferSize()); - - JsonArrayBuilder modulesBuilder = Json.createArrayBuilder(); - for (String modKey : _je.getModuleMap().keySet()) { - Module module = _je.getModule(modKey); - modulesBuilder.add( - Json.createObjectBuilder().add("module", module.getName()).add("release", module.getRelease())); - - } - appBuilder.add("modules", modulesBuilder); - - JsonArrayBuilder oHandlersBuilder = Json.createArrayBuilder(); - for (String handlerKey : _je.getHandlerMap().keySet()) { - OutputHandler handler = _je.getHandler(handlerKey); - oHandlersBuilder.add(Json.createObjectBuilder().add("outputHandler", handler.getName()).add("release", - handler.getRelease())); - } - appBuilder.add("outputHandlers", oHandlersBuilder); - - appBuilder.add("usage", app.getUsage()); - appBuilder.add("rights", app.getRights()); - - jhoveBuilder.add("app", appBuilder); - } - - /** - * Outputs information about the OutputHandler specified in the parameter - */ - @Override - public void show(OutputHandler handler) { - JsonObjectBuilder outputHandlerBuilder = Json.createObjectBuilder(); - outputHandlerBuilder.add("name", handler.getName()); - outputHandlerBuilder.add("release", handler.getRelease()); - outputHandlerBuilder.add("date", date.format(handler.getDate())); - List list = handler.getSpecification(); - int n = list.size(); - if (n > 0) { - JsonArrayBuilder specBuilder = Json.createArrayBuilder(); - for (int i = 0; i < n; i++) { - specBuilder.add(showDocument(list.get(i))); - } - outputHandlerBuilder.add("specifications", specBuilder); - } - Agent vendor = handler.getVendor(); - if (vendor != null) { - outputHandlerBuilder.add("vendor", showAgent(vendor, "Vendor")); - } - String s; - if ((s = handler.getNote()) != null) { - outputHandlerBuilder.add("note", s); - } - if ((s = handler.getRights()) != null) { - outputHandlerBuilder.add("rights", s); - } - - jhoveBuilder.add("handler", outputHandlerBuilder); - } - - /** - * Outputs information about a Module - */ - @Override - public void show(Module module) { - JsonObjectBuilder modBuilder = Json.createObjectBuilder(); - modBuilder.add("name", module.getName()); - modBuilder.add("release", module.getRelease()); - modBuilder.add("date", HandlerBase.date.format(module.getDate())); - - String[] ss = module.getFormat(); - if (ss.length > 0) { - modBuilder.add("formats", showArray(ss)); - } - String s = module.getCoverage(); - if (s != null) { - modBuilder.add("coverage", s); - } - ss = module.getMimeType(); - if (ss.length > 0) { - modBuilder.add("mimeTypes", showArray(ss)); - } - List list1 = module.getSignature(); - if (list1 != null && !list1.isEmpty()) { - JsonArrayBuilder sigBuilder = Json.createArrayBuilder(); - for (Signature sig : list1) { - sigBuilder.add(showSignature(sig)); - } - modBuilder.add("signatures", sigBuilder); - } - List list2 = module.getSpecification(); - if (list2 != null && !list2.isEmpty()) { - JsonArrayBuilder docBuilder = Json.createArrayBuilder(); - for (Document doc : list2) { - docBuilder.add(showDocument(doc)); - } - modBuilder.add("specifications", docBuilder); - } - List ftr = module.getFeatures(); - if (ftr != null && !ftr.isEmpty()) { - JsonArrayBuilder featuresBuilder = Json.createArrayBuilder(); - for (String f : ftr) { - featuresBuilder.add(f); - } - modBuilder.add("features", featuresBuilder); - } - - JsonObjectBuilder methodBuilder = Json.createObjectBuilder(); - if ((s = module.getWellFormedNote()) != null) { - methodBuilder.add("wellFormed", s); - } - if ((s = module.getValidityNote()) != null) { - methodBuilder.add("validity", s); - } - if ((s = module.getRepInfoNote()) != null) { - methodBuilder.add("repInfo", s); - } - modBuilder.add("methodology", methodBuilder); - - Agent vendor = module.getVendor(); - if (vendor != null) { - modBuilder.add("vendor", showAgent(vendor, "Vendor")); - } - if ((s = module.getNote()) != null) { - modBuilder.add("note", s); - } - if ((s = module.getRights()) != null) { - modBuilder.add("rights", s); - } - - jhoveBuilder.add("module", modBuilder); - } - - /** - * Outputs the information contained in a RepInfo object - */ - @Override - public void show(RepInfo info) { - JsonObjectBuilder infoBuilder = Json.createObjectBuilder(); - - Module module = info.getModule(); - _logger.info("Reporting RepInfo"); - if (_je.getSignatureFlag()) { - _logger.info("Checking signatures only"); - } - infoBuilder.add("uri", info.getUri()); - - if (module != null) { - infoBuilder.add("reportingModule", Json.createObjectBuilder().add("name", module.getName()) - .add("release", module.getRelease()).add("date", date.format(module.getDate()))); - } - Date date = info.getCreated(); - if (date != null) { - infoBuilder.add("created", toDateTime(date)); - } - date = info.getLastModified(); - if (date != null) { - infoBuilder.add("lastModified", toDateTime(date)); - } - long size = info.getSize(); - if (size > -1) { - infoBuilder.add("size", size); - } - String s = info.getFormat(); - if (s != null) { - infoBuilder.add("format", s); - } - if ((s = info.getVersion()) != null) { - infoBuilder.add("version", s); - } - String wfStr; - if (!_je.getSignatureFlag()) { - switch (info.getWellFormed()) { - case RepInfo.TRUE: - wfStr = "Well-Formed"; - break; - - case RepInfo.FALSE: - wfStr = "Not well-formed"; - break; - - default: - wfStr = "Unknown"; - break; - } - // If it's well-formed, append validity information - if (info.getWellFormed() == RepInfo.TRUE) { - switch (info.getValid()) { - case RepInfo.TRUE: - wfStr += " and valid"; - break; - - case RepInfo.FALSE: - wfStr += ", but not valid"; - break; - - default: - // case UNDETERMINED: add nothing - break; - } - } - _logger.info("Validity/WF status: " + wfStr); - infoBuilder.add("status", wfStr); - } else { - // If we aren't checking signatures, we still need to say something. - switch (info.getWellFormed()) { - case RepInfo.TRUE: - wfStr = "Well-Formed"; - break; - - default: - wfStr = "Not well-formed"; - break; - } - infoBuilder.add("status", wfStr); - } - - List list1 = info.getSigMatch(); - if (list1 != null && !list1.isEmpty()) { - JsonArrayBuilder sigBuilder = Json.createArrayBuilder(); - for (String sigm : list1) { - sigBuilder.add(sigm); - } - infoBuilder.add("sigMatch", sigBuilder); - } - - List list2 = info.getMessage(); - if (list2 != null && !list2.isEmpty()) { - JsonArrayBuilder msgBuilder = Json.createArrayBuilder(); - for (Message msg : list2) { - msgBuilder.add(showMessage(msg)); - } - infoBuilder.add("messages", msgBuilder); - } - - s = info.getMimeType(); - if (s != null) { - infoBuilder.add("mimeType", s); - } - - List list3 = info.getProfile(); - if (list3 != null && !list3.isEmpty()) { - JsonArrayBuilder profBuilder = Json.createArrayBuilder(); - for (String prof : list3) { - profBuilder.add(prof); - } - infoBuilder.add("profiles", profBuilder); - } - - Map map = info.getProperty(); - if (map != null && !map.isEmpty()) { - JsonArrayBuilder propBuilder = Json.createArrayBuilder(); - for (String key : map.keySet()) { - Property property = info.getProperty(key); - propBuilder.add(showProperty(property)); - } - infoBuilder.add("properties", propBuilder); - } - - List list4 = info.getChecksum(); - if (list4 != null && !list4.isEmpty()) { - JsonArrayBuilder checksumBuilder = Json.createArrayBuilder(); - for (Checksum ck : list4) { - checksumBuilder.add(showChecksum(ck)); - } - infoBuilder.add("properties", checksumBuilder); - } - - if ((s = info.getNote()) != null) { - infoBuilder.add("note", s); - } - - jhoveBuilder.add("repInfo", infoBuilder); - } - - /****************************************************************** - * PRIVATE INSTANCE METHODS. - ******************************************************************/ - protected JsonObjectBuilder showAgent(Agent agent, String label) { - JsonObjectBuilder agentBuilder = Json.createObjectBuilder(); - agentBuilder.add("kind", label); - agentBuilder.add("name", agent.getName()); - agentBuilder.add("type", agent.getType().toString()); - String s = agent.getAddress(); - if (s != null) { - agentBuilder.add("address", s); - } - if ((s = agent.getTelephone()) != null) { - agentBuilder.add("telephone", s); - } - if ((s = agent.getFax()) != null) { - agentBuilder.add("fax", s); - } - if ((s = agent.getEmail()) != null) { - agentBuilder.add("email", s); - } - if ((s = agent.getWeb()) != null) { - agentBuilder.add("web", s); - } - return agentBuilder; - } - - protected JsonObjectBuilder showChecksum(Checksum checksum) { - return Json.createObjectBuilder().add("checksum", checksum.getValue()).add("type", - checksum.getType().toString()); - } - - protected JsonObjectBuilder showDocument(Document document) { - JsonObjectBuilder docBuilder = Json.createObjectBuilder(); - docBuilder.add("title", document.getTitle()); - docBuilder.add("type", document.getType().toString()); - List list1 = document.getAuthor(); - if (list1 != null && !list1.isEmpty()) { - JsonArrayBuilder autBuilder = Json.createArrayBuilder(); - for (Agent ag : list1) { - autBuilder.add(showAgent(ag, "Author")); - } - docBuilder.add("authors", autBuilder); - } - List list2 = document.getPublisher(); - if (list2 != null && !list2.isEmpty()) { - JsonArrayBuilder pubBuilder = Json.createArrayBuilder(); - for (Agent ag : list2) { - pubBuilder.add(showAgent(ag, "Publisher")); - } - docBuilder.add("publishers", pubBuilder); - } - String s = document.getEdition(); - if (s != null) { - docBuilder.add("edition", s); - } - if ((s = document.getDate()) != null) { - docBuilder.add("date", s); - } - if ((s = document.getEnumeration()) != null) { - docBuilder.add("enumeration", s); - } - if ((s = document.getPages()) != null) { - docBuilder.add("pages", s); - } - List list3 = document.getIdentifier(); - if (list3 != null && !list3.isEmpty()) { - JsonArrayBuilder idBuilder = Json.createArrayBuilder(); - for (Identifier id : list3) { - idBuilder.add(showIdentifier(id)); - } - docBuilder.add("identifiers", idBuilder); - } - if ((s = document.getNote()) != null) { - docBuilder.add("note", s); - } - return docBuilder; - } - - /** - * Do the final output. This should be in a suitable format for including - * multiple files between the header and the footer, and the XML of the header - * and footer must balance out. - */ - @Override - public void showFooter() { - JsonObjectBuilder mainBuilder = Json.createObjectBuilder(); - mainBuilder.add("jhove", jhoveBuilder); - JsonObject jsonObject = mainBuilder.build(); - - JsonWriter jsonWriter = Json.createWriter(_writer); - jsonWriter.writeObject(jsonObject); - jsonWriter.close(); - } - - /** - * Do the initial output. This should be in a suitable format for including - * multiple files between the header and the footer, and the XML of the header - * and footer must balance out. - */ - @Override - public void showHeader() { - jhoveBuilder = Json.createObjectBuilder(); - jhoveBuilder.add("name", _app.getName()); - jhoveBuilder.add("release", _app.getRelease()); - jhoveBuilder.add("date", HandlerBase.date.format(_app.getDate())); - jhoveBuilder.add("executionTime", toDateTime(new Date())); - } - - protected JsonObjectBuilder showIdentifier(Identifier identifier) { - JsonObjectBuilder idBuilder = Json.createObjectBuilder(); - idBuilder.add("value", identifier.getValue()); - idBuilder.add("type", identifier.getType().toString()); - String note = identifier.getNote(); - if (note != null) { - idBuilder.add("note", note); - } - return idBuilder; - } - - protected JsonObjectBuilder showMessage(Message message) { - JsonObjectBuilder msgBuilder = Json.createObjectBuilder(); - - msgBuilder.add("message", message.getMessage()); - String submsg = message.getSubMessage(); - if (submsg != null) { - msgBuilder.add("subMessage", submsg); - } - long offset = message.getOffset(); - if (offset > -1) { - msgBuilder.add("offset", offset); - } - if (!message.getPrefix().isEmpty()) { - msgBuilder.add("severity", message.getPrefix().toLowerCase()); - } - String id = message.getJhoveMessage().getId(); - if (!(id == null || id.isEmpty() || JhoveMessages.NO_ID.equals(id))) { - msgBuilder.add("id", message.getId()); - } - return msgBuilder; - } - - protected JsonObjectBuilder showSignature(Signature signature) { - JsonObjectBuilder sigBuilder = Json.createObjectBuilder(); - - String sigValue; - if (signature.isStringValue()) { - sigValue = signature.getValueString(); - } else { - sigValue = signature.getValueHexString(); - } - sigBuilder.add("type", signature.getType().toString()); - sigBuilder.add("value", sigValue); - if (SignatureType.MAGIC.equals(signature.getType()) && ((InternalSignature) signature).hasFixedOffset()) { - sigBuilder.add("offset", "0x" + Integer.toHexString(((InternalSignature) signature).getOffset())); - } - String note = signature.getNote(); - if (note != null) { - sigBuilder.add("note", note); - - } - String use = signature.getUse().toString(); - if (use != null) { - sigBuilder.add("use", use); - } - return sigBuilder; - } - - /* Do special conversions on values as needed. */ - protected String valueToString(Object obj) { - if (obj instanceof Date) { - return toDateTime((Date) obj); - } - return obj.toString(); - } - - protected JsonObjectBuilder showProperty(Property property) { - JsonObjectBuilder propBuilder = Json.createObjectBuilder(); - - PropertyArity arity = property.getArity(); - switch (arity) { - case SCALAR: - return showScalarProperty(property); - case ARRAY: - return showArrayProperty(property); - case LIST: - return showListProperty(property); - case MAP: - return showMapProperty(property); - case SET: - return showSetProperty(property); - default: - return propBuilder; - } - } - - protected JsonObjectBuilder showScalarProperty(Property property) { - JsonObjectBuilder propBuilder = Json.createObjectBuilder(); - - PropertyType propType = property.getType(); - switch (propType) { - case BOOLEAN: - Boolean b = (Boolean) property.getValue(); - propBuilder.add(property.getName(), b.booleanValue()); - break; - case BYTE: - case CHARACTER: - case OBJECT: - propBuilder.add(property.getName(), property.getValue().toString()); - break; - case DATE: - Date dt = (Date) property.getValue(); - propBuilder.add(property.getName(), toDateTime(dt)); - break; - case DOUBLE: - Double d = (Double) property.getValue(); - propBuilder.add(property.getName(), d.doubleValue()); - break; - case FLOAT: - Float f = (Float) property.getValue(); - propBuilder.add(property.getName(), f.floatValue()); - break; - case INTEGER: - Integer i = (Integer) property.getValue(); - propBuilder.add(property.getName(), i.intValue()); - break; - case LONG: - Long l = (Long) property.getValue(); - propBuilder.add(property.getName(), l.longValue()); - break; - case AESAUDIOMETADATA: - propBuilder.add(property.getName(), showAESAudioMetadata((AESAudioMetadata) property.getValue())); - break; - case NISOIMAGEMETADATA: - propBuilder.add(property.getName(), showNisoImageMetadata((NisoImageMetadata) property.getValue())); - break; - case TEXTMDMETADATA: - propBuilder.add(property.getName(), showTextMDMetadata((TextMDMetadata) property.getValue())); - break; - case SHORT: - Short s = (Short) property.getValue(); - propBuilder.add(property.getName(), s.shortValue()); - break; - case STRING: - propBuilder.add(property.getName(), (String) property.getValue()); - break; - case RATIONAL: - propBuilder.add(property.getName(), showRational((Rational) property.getValue())); - break; - case PROPERTY: - Property property2 = (Property) property.getValue(); - propBuilder.add(property.getName(), showProperty(property2)); - break; - default: - propBuilder.add(property.getName(), property.getValue().toString()); - break; - } - return propBuilder; - } - - protected JsonObjectBuilder showListProperty(Property property) { - JsonObjectBuilder propBuilder = Json.createObjectBuilder(); - List propList = (List) property.getValue(); - JsonArrayBuilder lPropBuilder = Json.createArrayBuilder(); - PropertyType type = property.getType(); - - ListIterator iter = propList.listIterator(); - while (iter.hasNext()) { - Object val = iter.next(); - switch (type) { - case BOOLEAN: - lPropBuilder.add(((Boolean) val).booleanValue()); - break; - case BYTE: - lPropBuilder.add(valueToString(val)); - break; - case CHARACTER: - lPropBuilder.add(valueToString(val)); - break; - case DATE: - lPropBuilder.add(valueToString(val)); - break; - case DOUBLE: - lPropBuilder.add(((Double) val).doubleValue()); - break; - case FLOAT: - lPropBuilder.add(((Float) val).floatValue()); - break; - case INTEGER: - lPropBuilder.add(((Integer) val).intValue()); - break; - case LONG: - lPropBuilder.add(((Long) val).longValue()); - break; - case OBJECT: - lPropBuilder.add(valueToString(val)); - break; - case SHORT: - lPropBuilder.add(((Short) val).shortValue()); - break; - case STRING: - lPropBuilder.add(valueToString(val)); - break; - case RATIONAL: - lPropBuilder.add(showRational((Rational) val)); - break; - case PROPERTY: - lPropBuilder.add(showProperty((Property) val)); - break; - case NISOIMAGEMETADATA: - lPropBuilder.add(showNisoImageMetadata((NisoImageMetadata) property.getValue())); - break; - case AESAUDIOMETADATA: - lPropBuilder.add(showAESAudioMetadata((AESAudioMetadata) property.getValue())); - break; - case TEXTMDMETADATA: - lPropBuilder.add(showTextMDMetadata((TextMDMetadata) property.getValue())); - break; - default: - break; - } - } - propBuilder.add(property.getName(), lPropBuilder); - return propBuilder; - } - - protected JsonObjectBuilder showSetProperty(Property property) { - JsonObjectBuilder propBuilder = Json.createObjectBuilder(); - Set propSet = (Set) property.getValue(); - JsonArrayBuilder lPropBuilder = Json.createArrayBuilder(); - PropertyType type = property.getType(); - - Iterator iter = propSet.iterator(); - while (iter.hasNext()) { - Object val = iter.next(); - switch (type) { - case BOOLEAN: - lPropBuilder.add(((Boolean) val).booleanValue()); - break; - case BYTE: - lPropBuilder.add(valueToString(val)); - break; - case CHARACTER: - lPropBuilder.add(valueToString(val)); - break; - case DATE: - lPropBuilder.add(valueToString(val)); - break; - case DOUBLE: - lPropBuilder.add(((Double) val).doubleValue()); - break; - case FLOAT: - lPropBuilder.add(((Float) val).floatValue()); - break; - case INTEGER: - lPropBuilder.add(((Integer) val).intValue()); - break; - case LONG: - lPropBuilder.add(((Long) val).longValue()); - break; - case OBJECT: - lPropBuilder.add(valueToString(val)); - break; - case SHORT: - lPropBuilder.add(((Short) val).shortValue()); - break; - case STRING: - lPropBuilder.add(valueToString(val)); - break; - case RATIONAL: - lPropBuilder.add(showRational((Rational) val)); - break; - case PROPERTY: - lPropBuilder.add(showProperty((Property) val)); - break; - case NISOIMAGEMETADATA: - lPropBuilder.add(showNisoImageMetadata((NisoImageMetadata) property.getValue())); - break; - case AESAUDIOMETADATA: - lPropBuilder.add(showAESAudioMetadata((AESAudioMetadata) property.getValue())); - break; - case TEXTMDMETADATA: - lPropBuilder.add(showTextMDMetadata((TextMDMetadata) property.getValue())); - break; - default: - break; - } - } - propBuilder.add(property.getName(), lPropBuilder); - return propBuilder; - } - - protected JsonObjectBuilder showMapProperty(Property property) { - JsonObjectBuilder propBuilder = Json.createObjectBuilder(); - JsonObjectBuilder lPropBuilder = Json.createObjectBuilder(); - Map propMap = (Map) property.getValue(); - PropertyType type = property.getType(); - Iterator keyIter = propMap.keySet().iterator(); - while (keyIter.hasNext()) { - Object key = keyIter.next(); - String keystr = key.toString(); - Object val = propMap.get(key); - switch (type) { - case BOOLEAN: - lPropBuilder.add(keystr, ((Boolean) val).booleanValue()); - break; - case BYTE: - lPropBuilder.add(keystr, valueToString(val)); - break; - case CHARACTER: - lPropBuilder.add(keystr, valueToString(val)); - break; - case DATE: - lPropBuilder.add(keystr, valueToString(val)); - break; - case DOUBLE: - lPropBuilder.add(keystr, ((Double) val).doubleValue()); - break; - case FLOAT: - lPropBuilder.add(keystr, ((Float) val).floatValue()); - break; - case INTEGER: - lPropBuilder.add(keystr, ((Integer) val).intValue()); - break; - case LONG: - lPropBuilder.add(keystr, ((Long) val).longValue()); - break; - case OBJECT: - lPropBuilder.add(keystr, valueToString(val)); - break; - case SHORT: - lPropBuilder.add(keystr, ((Short) val).shortValue()); - break; - case STRING: - lPropBuilder.add(keystr, valueToString(val)); - break; - case RATIONAL: - lPropBuilder.add(keystr, showRational((Rational) val)); - break; - case PROPERTY: - lPropBuilder.add(keystr, showProperty((Property) val)); - break; - case NISOIMAGEMETADATA: - lPropBuilder.add(keystr, showNisoImageMetadata((NisoImageMetadata) property.getValue())); - break; - case AESAUDIOMETADATA: - lPropBuilder.add(keystr, showAESAudioMetadata((AESAudioMetadata) property.getValue())); - break; - case TEXTMDMETADATA: - lPropBuilder.add(keystr, showTextMDMetadata((TextMDMetadata) property.getValue())); - break; - default: - break; - } - } - propBuilder.add(property.getName(), lPropBuilder); - return propBuilder; - } - - /** - * Gives the length (number of elements) of a property - */ - protected int propertyLength(Property property) { - int n = 0; - try { - switch (property.getArity()) { - case SET: - Set propSet = (Set) property.getValue(); - n = propSet.size(); - break; - case LIST: - List propList = (List) property.getValue(); - n = propList.size(); - break; - case MAP: - Map propMap = (Map) property.getValue(); - n = propMap.size(); - break; - case ARRAY: - // Ack! Is there any easy way to do this? - switch (property.getType()) { - case BOOLEAN: - boolean[] boolArray = (boolean[]) property.getValue(); - n = boolArray.length; - break; - case BYTE: - byte[] byteArray = (byte[]) property.getValue(); - n = byteArray.length; - break; - case CHARACTER: - char[] charArray = (char[]) property.getValue(); - n = charArray.length; - break; - case DATE: - Date[] dateArray = (Date[]) property.getValue(); - n = dateArray.length; - break; - case DOUBLE: - double[] doubleArray = (double[]) property.getValue(); - n = doubleArray.length; - break; - case FLOAT: - float[] floatArray = (float[]) property.getValue(); - n = floatArray.length; - break; - case INTEGER: - int[] intArray = (int[]) property.getValue(); - n = intArray.length; - break; - case LONG: - long[] longArray = (long[]) property.getValue(); - n = longArray.length; - break; - case OBJECT: - Object[] objArray = (Object[]) property.getValue(); - n = objArray.length; - break; - case SHORT: - short[] shortArray = (short[]) property.getValue(); - n = shortArray.length; - break; - case STRING: - String[] stringArray = (String[]) property.getValue(); - n = stringArray.length; - break; - case RATIONAL: - Rational[] rationalArray = (Rational[]) property.getValue(); - n = rationalArray.length; - break; - case PROPERTY: - Property[] propArray = (Property[]) property.getValue(); - n = propArray.length; - break; - case NISOIMAGEMETADATA: - NisoImageMetadata[] nisoArray = (NisoImageMetadata[]) property.getValue(); - n = nisoArray.length; - break; - case AESAUDIOMETADATA: - AESAudioMetadata[] aesArray = (AESAudioMetadata[]) property.getValue(); - n = aesArray.length; - break; - case TEXTMDMETADATA: - TextMDMetadata[] textMDArray = (TextMDMetadata[]) property.getValue(); - n = textMDArray.length; - break; - default: - Object[] array2 = (Object[]) property.getValue(); - n = array2.length; - break; - } - break; - default: - if (property.getValue().toString().length() == 0) { - n = 0; - } else { - n = 1; - } - break; - } - } catch (Exception e) { - // If something goes seriously wrong, return true to punt the property - return 0; - } - return n; - } - - /* - * The array property has so many special cases of its own that we break it out - * of showProperty - */ - protected JsonObjectBuilder showArrayProperty(Property property) { - boolean[] boolArray = null; - byte[] byteArray = null; - char[] charArray = null; - java.util.Date[] dateArray = null; - double[] doubleArray = null; - float[] floatArray = null; - int[] intArray = null; - long[] longArray = null; - Object[] objArray = null; - Property[] propArray = null; - short[] shortArray = null; - String[] stringArray = null; - Rational[] rationalArray = null; - NisoImageMetadata[] nisoArray = null; - AESAudioMetadata[] aesArray = null; - TextMDMetadata[] textMDArray = null; - int n = 0; - - PropertyType propType = property.getType(); - switch (propType) { - case BOOLEAN: - boolArray = (boolean[]) property.getValue(); - n = boolArray.length; - break; - case BYTE: - byteArray = (byte[]) property.getValue(); - n = byteArray.length; - break; - case CHARACTER: - charArray = (char[]) property.getValue(); - n = charArray.length; - break; - case DATE: - dateArray = (Date[]) property.getValue(); - n = dateArray.length; - break; - case DOUBLE: - doubleArray = (double[]) property.getValue(); - n = doubleArray.length; - break; - case FLOAT: - floatArray = (float[]) property.getValue(); - n = floatArray.length; - break; - case INTEGER: - intArray = (int[]) property.getValue(); - n = intArray.length; - break; - case LONG: - longArray = (long[]) property.getValue(); - n = longArray.length; - break; - case OBJECT: - objArray = (Object[]) property.getValue(); - n = objArray.length; - break; - case SHORT: - shortArray = (short[]) property.getValue(); - n = shortArray.length; - break; - case STRING: - stringArray = (String[]) property.getValue(); - n = stringArray.length; - break; - case RATIONAL: - rationalArray = (Rational[]) property.getValue(); - n = rationalArray.length; - break; - case PROPERTY: - propArray = (Property[]) property.getValue(); - n = propArray.length; - break; - case NISOIMAGEMETADATA: - nisoArray = (NisoImageMetadata[]) property.getValue(); - n = nisoArray.length; - break; - case AESAUDIOMETADATA: - aesArray = (AESAudioMetadata[]) property.getValue(); - n = aesArray.length; - break; - case TEXTMDMETADATA: - textMDArray = (TextMDMetadata[]) property.getValue(); - n = textMDArray.length; - break; - default: - break; - } - - JsonArrayBuilder arrayBuilder = Json.createArrayBuilder(); - for (int i = 0; i < n; i++) { - switch (propType) { - case BOOLEAN: - arrayBuilder.add(boolArray[i]); - break; - case BYTE: - arrayBuilder.add(String.valueOf(byteArray[i])); - break; - case CHARACTER: - arrayBuilder.add(String.valueOf(charArray[i])); - break; - case DATE: - arrayBuilder.add(dateArray[i].toString()); - break; - case DOUBLE: - arrayBuilder.add(doubleArray[i]); - break; - case FLOAT: - arrayBuilder.add(floatArray[i]); - break; - case INTEGER: - arrayBuilder.add(intArray[i]); - break; - case LONG: - arrayBuilder.add(longArray[i]); - break; - case OBJECT: - arrayBuilder.add(valueToString(objArray[i])); - break; - case SHORT: - arrayBuilder.add(shortArray[i]); - break; - case STRING: - arrayBuilder.add(stringArray[i]); - break; - case RATIONAL: - arrayBuilder.add(rationalArray[i].toString()); - break; - case PROPERTY: - arrayBuilder.add(showProperty(propArray[i])); - break; - case NISOIMAGEMETADATA: - arrayBuilder.add(showNisoImageMetadata(nisoArray[i])); - break; - case AESAUDIOMETADATA: - arrayBuilder.add(showAESAudioMetadata(aesArray[i])); - break; - case TEXTMDMETADATA: - arrayBuilder.add(showTextMDMetadata(textMDArray[i])); - break; - default: - arrayBuilder.add(""); - break; - } - } - return Json.createObjectBuilder().add(property.getName(), arrayBuilder); - } - - /* - * Output the textMD metadata, which is its own special kind of property. - */ - protected JsonObjectBuilder showTextMDMetadata(TextMDMetadata textMD) { - JsonObjectBuilder textmdBuilder = Json.createObjectBuilder(); - - String s = textMD.getCharset(); - if (s != null) { - textmdBuilder.add("textmd:charset", s); - } - if ((s = textMD.getByte_orderString()) != null) { - textmdBuilder.add("textmd:byte_order", s); - } - if ((s = textMD.getByte_size()) != null) { - textmdBuilder.add("textmd:byte_size", s); - } - if ((s = textMD.getCharacter_size()) != null) { - textmdBuilder.add("textmd:character_size", s); - } - if ((s = textMD.getLinebreakString()) != null) { - textmdBuilder.add("textmd:linebreak", s); - } - if ((s = textMD.getLanguage()) != null) { - textmdBuilder.add("textmd:language", s); - } - if ((s = textMD.getMarkup_basis()) != null) { - textmdBuilder.add("textmd:markup_basis", s); - } - if ((s = textMD.getMarkup_basis_version()) != null) { - textmdBuilder.add("textmd:markup_basis_version", s); - } - if ((s = textMD.getMarkup_language()) != null) { - textmdBuilder.add("textmd:markup_language", s); - } - if ((s = textMD.getMarkup_language_version()) != null) { - textmdBuilder.add("textmd:markup_language_version", s); - } - return textmdBuilder; - } - - /** - * Display the NISO image metadata formatted according to the MIX schema. The - * schema which is used may be 0.2 or 1.0 or 2.0, depending on the module - * parameters. - * - * @param niso NISO image metadata - */ - protected JsonObjectBuilder showNisoImageMetadata(NisoImageMetadata niso) { - if ("0.2".equals(_je.getMixVersion())) { - return null; // don't handle mix version 0.2 - } else if ("1.0".equals(_je.getMixVersion())) { - return showNisoImageMetadata(niso, true); - } else { - return showNisoImageMetadata(niso, false); - } - } - - /** - * Display the NISO image metadata formatted according to the MIX 1.0 schema. - */ - protected JsonObjectBuilder showNisoImageMetadata(NisoImageMetadata niso, boolean bMix10) { - JsonObjectBuilder mixBuilder = Json.createObjectBuilder(); - - JsonObjectBuilder ob = showNisoBasicDigitalObjectInformation(niso, bMix10); - if (ob != null) { - mixBuilder.add("mix:BasicDigitalObjectInformation", ob); - } - ob = showNisoBasicImageInformation(niso, bMix10); - if (ob != null) { - mixBuilder.add("mix:BasicImageInformation", ob); - } - ob = showNisoImageCaptureMetadata(niso, bMix10); - if (ob != null) { - mixBuilder.add("mix:ImageCaptureMetadata", ob); - } - ob = showNisoImageAssessmentMetadata(niso, bMix10); - if (ob != null) { - mixBuilder.add("mix:ImageAssessmentMetadata", ob); - } - ob = showChangeHistory(niso, bMix10); - if (ob != null) { - mixBuilder.add("mix:ChangeHistory", ob); - } - return mixBuilder; - } - - /* - * The NISO Metadata output is split into multiple functions so that they're - * merely outrageously big rather than disgustingly big - */ - /* Top level element 1 of 5: BasicDigitalObjectInformation */ - protected JsonObjectBuilder showNisoBasicDigitalObjectInformation(NisoImageMetadata niso, boolean bMix10) { - JsonObjectBuilder mixBuilder = Json.createObjectBuilder(); - - String s = niso.getImageIdentifier(); - if (s != null) { - mixBuilder.add("mix:ObjectIdentifier", Json.createObjectBuilder().add("mix:objectIdentifierType", "JHOVE") - .add("mix:objectIdentifierValue", s)); - } - long ln = niso.getFileSize(); - if (ln != NisoImageMetadata.NULL) { - mixBuilder.add("mix:fileSize", ln); - } - if ((s = niso.getMimeType()) != null) { - mixBuilder.add("mix:formatName", s); - } - if ((s = niso.getByteOrder()) != null) { - // Convert strings to MIX 1.0 form - if (s.startsWith("big")) { - s = bMix10 ? "big_endian" : "big endian"; - } else if (s.startsWith("little")) { - s = bMix10 ? "little_endian" : "little endian"; - } - mixBuilder.add("mix:byteOrder", s); - } - int comp = niso.getCompressionScheme(); - int level = niso.getCompressionLevel(); - String compStr; - switch (comp) { - case 1: - compStr = "Uncompressed"; - break; - case 2: - compStr = "CCITT 1D"; - break; - case 3: - compStr = "Group 3 Fax"; - break; - case 4: - compStr = "Group 4 Fax"; - break; - case 5: - compStr = "LZW"; - break; - case 6: - compStr = "JPEG"; - break; - case 32773: - compStr = "PackBits"; - break; - case 34713: - compStr = "JPEG2000 Lossy"; - break; - case 34714: - compStr = "JPEG2000 Lossless"; - break; - default: - compStr = "Unknown"; - break; - } - if (comp != NisoImageMetadata.NULL) { - if (comp == 34713 || comp == 34714) { - mixBuilder.add("mix:compressionScheme", compressionSchemeToString(comp)); - if (level != NisoImageMetadata.NULL) { - mixBuilder.add("mix:compressionRatio", level); - } - } else if (bMix10) { - mixBuilder.add("mix:compressionScheme", Integer.toString(comp)); - } else { - mixBuilder.add("mix:compressionScheme", compStr); - } - } - int n = niso.getChecksumMethod(); - s = niso.getChecksumValue(); - if (n != NisoImageMetadata.NULL || s != null) { - if (n != NisoImageMetadata.NULL) { - mixBuilder.add("mix:messageDigestAlgorithm", n); - } - if (s != null) { - mixBuilder.add("mix:messageDigest", s); - } - } - return mixBuilder; - } - - /* 1.0, Top level element 2 of 5: BasicImageInformation */ - protected JsonObjectBuilder showNisoBasicImageInformation(NisoImageMetadata niso, boolean bMix10) { - JsonObjectBuilder mixBuilder = Json.createObjectBuilder(); - boolean hasBuilder = false; - long ln = niso.getImageWidth(); - if (ln != NisoImageMetadata.NULL) { - mixBuilder.add("mix:imageWidth", ln); - hasBuilder = true; - } - ln = niso.getImageLength(); - if (ln != NisoImageMetadata.NULL) { - mixBuilder.add("mix:imageHeight", ln); - hasBuilder = true; - } - int n = niso.getColorSpace(); - if (n != NisoImageMetadata.NULL) { - if (bMix10) { - mixBuilder.add("mix:colorSpace", n); - } else { - mixBuilder.add("mix:colorSpace", photometricInterpretationToString(n)); - } - hasBuilder = true; - } - String s = niso.getProfileName(); - String s2 = niso.getProfileURL(); - if (s != null || s2 != null) { - JsonObjectBuilder iccBuilder = Json.createObjectBuilder(); - if (s != null) { - iccBuilder.add("mix:iccProfileName", s); - } - if (s2 != null) { - iccBuilder.add("mix:iccProfileURL", s2); - } - mixBuilder.add("mix:IccProfile", iccBuilder); - hasBuilder = true; - } - int[] iarray = niso.getYCbCrSubSampling(); - n = niso.getYCbCrPositioning(); - Rational[] rarray = niso.getYCbCrCoefficients(); - if (iarray != null || n != NisoImageMetadata.NULL || rarray != null) { - JsonObjectBuilder yccBuilder = Json.createObjectBuilder(); - if (iarray != null && iarray.length >= 2) { - yccBuilder.add("mix:YCbCrSubSampling", Json.createObjectBuilder() - .add("mix:yCbCrSubsampleHoriz", iarray[0]).add("mix:yCbCrSubsampleVert", iarray[1])); - } - if (n != NisoImageMetadata.NULL) { - yccBuilder.add("mix:yCbCrPositioning", n); - } - if (rarray != null) { - if (bMix10) { - yccBuilder.add("mix:yCbCrCoefficients", showArray(rarray)); - } else { - yccBuilder.add("mix:yCbCrCoefficients", - Json.createObjectBuilder().add("mix:lumaRed", showRational(rarray[0])) - .add("mix:lumaGreen", showRational(rarray[1])) - .add("mix:lumaBlue", showRational(rarray[2]))); - } - } - mixBuilder.add("mix:YCbCr", yccBuilder); - hasBuilder = true; - } - rarray = niso.getReferenceBlackWhite(); - if (rarray != null) { - if (bMix10) { - mixBuilder.add("mix:referenceBlackWhite", showArray(rarray)); - } else { - JsonArrayBuilder aBuilder = Json.createArrayBuilder(); - for (int i = 0; i < rarray.length - 1; i += 2) { - JsonObjectBuilder cBuilder = Json.createObjectBuilder(); - // Tricky here. - // The reference BW might be given as either RGB or yCbCr. - String pi; - if (niso.getColorSpace() == 6) { // yCbCr - switch (i) { - case 0: - pi = "Y"; - break; - case 2: - pi = "Cb"; - break; - case 4: - default: - pi = "Cr"; - break; - } - } else { - switch (i) { // otherwise assume RGB - case 0: - pi = "R"; - break; - case 2: - pi = "G"; - break; - case 4: - default: - pi = "B"; - break; - } - } - cBuilder.add("mix:componentPhotometricInterpretation", pi); - cBuilder.add("mix:footroom", showRational(rarray[i])); - cBuilder.add("mix:headroom", showRational(rarray[i + 1])); - aBuilder.add(cBuilder); - } - mixBuilder.add("mix:ReferenceBlackWhite", aBuilder); - } - hasBuilder = true; - } - // SpecialFormatCharacteristics limited to JPEG2000 - int lay = niso.getJp2Layers(); - int lev = niso.getJp2ResolutionLevels(); - String sizTiles = niso.getJp2Tiles(); - if (sizTiles != null || lay != NisoImageMetadata.NULL || lev != NisoImageMetadata.NULL) { - JsonObjectBuilder jp2Builder = Json.createObjectBuilder(); - if (sizTiles != null) { - if (bMix10) { - jp2Builder.add("mix:tiles", sizTiles); - } else { - String[] sizes = sizTiles.split("x"); - jp2Builder.add("mix:tiles", showArray(sizes)); - } - } - if (lay != NisoImageMetadata.NULL) { - jp2Builder.add("mix:qualityLayers", lay); - } - if (lev != NisoImageMetadata.NULL) { - jp2Builder.add("mix:resolutionLevels", lev); - } - mixBuilder.add("mix:JPEG2000", jp2Builder); - hasBuilder = true; - } - - if (hasBuilder) { - return mixBuilder; - } else { - return null; - } - } - - /* 1.0, Top level element 3 of 5: ImageCaptureMetadata */ - protected JsonObjectBuilder showNisoImageCaptureMetadata(NisoImageMetadata niso, boolean bMix10) { - JsonObjectBuilder mixBuilder = Json.createObjectBuilder(); - boolean hasBuilder = false; - - String s = niso.getSourceType(); - if (s != null) { - mixBuilder.add("mix:sourceType", s); - hasBuilder = true; - } - s = niso.getSourceID(); - if (s != null) { - mixBuilder.add("mix:sourceIDValue", s); - hasBuilder = true; - } - double d = niso.getSourceXDimension(); - int n = niso.getSourceXDimensionUnit(); - if (d != NisoImageMetadata.NILL || n != NisoImageMetadata.NULL) { - // Assume that both X and Y exist, or neither - if (d != NisoImageMetadata.NILL) { - mixBuilder.add("mix:sourceXDimensionValue", d); - } - if (n != NisoImageMetadata.NULL) { - mixBuilder.add("mix:sourceXDimensionUnit", n); - } - d = niso.getSourceYDimension(); - n = niso.getSourceYDimensionUnit(); - if (d != NisoImageMetadata.NILL || n != NisoImageMetadata.NULL) { - if (d != NisoImageMetadata.NILL) { - mixBuilder.add("mix:sourceYDimensionValue", d); - } - if (n != NisoImageMetadata.NULL) { - mixBuilder.add("mix:sourceYDimensionUnit", n); - } - } - hasBuilder = true; - } - - s = niso.getDateTimeCreated(); - if (s != null) { - mixBuilder.add("mix:dateTimeCreated", s); - hasBuilder = true; - } - s = niso.getImageProducer(); - if (s != null) { - mixBuilder.add("mix:imageProducer", s); - hasBuilder = true; - } - - s = niso.getDeviceSource(); - if (s != null) { - mixBuilder.add("mix:captureDevice", s); - hasBuilder = true; - } - - // Here's a chunk of XML for scanners. - String mfg = niso.getScannerManufacturer(); - if (mfg != null) { - mixBuilder.add("mix:scannerManufacturer", mfg); - hasBuilder = true; - } - String model = niso.getScannerModelName(); - String modelNum = niso.getScannerModelNumber(); - String serNum = niso.getScannerModelSerialNo(); - if (model != null || modelNum != null || serNum != null) { - hasBuilder = true; - JsonObjectBuilder smBuilder = Json.createObjectBuilder(); - if (model != null) { - smBuilder.add("mix:scannerModelName", model); - } - if (modelNum != null) { - smBuilder.add("mix:scannerModelNumber", modelNum); - } - if (serNum != null) { - smBuilder.add("mix:scannerModelSerialNo", serNum); - } - mixBuilder.add("mix:ScannerModel", smBuilder); - } - double xres = niso.getXPhysScanResolution(); - double yres = niso.getYPhysScanResolution(); - if (xres != NisoImageMetadata.NULL && yres != NisoImageMetadata.NULL) { - double res = (xres > yres ? xres : yres); - if (bMix10) { - mixBuilder.add("mix:maximumOpticalResolution", res); - } else { - mixBuilder.add("mix:MaximumOpticalResolution", - Json.createObjectBuilder().add("mix:xOpticalResolution", xres) - .add("mix:yOpticalResolution", yres).add("mix:resolutionUnit", ".in")); - } - - hasBuilder = true; - } - s = niso.getScanningSoftware(); - if (s != null) { - hasBuilder = true; - mixBuilder.add("mix:scanningSoftwareName", s); - s = niso.getScanningSoftwareVersionNo(); - if (s != null) { - mixBuilder.add("mix:scanningSoftwareVersionNo", s); - } - } - - // Now we'll hear from the digital cameras. - s = niso.getDigitalCameraManufacturer(); - if (s != null) { - mixBuilder.add("mix:digitalCameraManufacturer", s); - hasBuilder = true; - } - String dcmodel = niso.getDigitalCameraModelName(); - String dcmodelNum = niso.getDigitalCameraModelNumber(); - String dcserNum = niso.getDigitalCameraModelSerialNo(); - if (dcmodel != null || dcmodelNum != null || dcserNum != null) { - hasBuilder = true; - JsonObjectBuilder smBuilder = Json.createObjectBuilder(); - if (dcmodel != null) { - smBuilder.add("mix:digitalCameraModelName", dcmodel); - } - if (dcmodelNum != null) { - smBuilder.add("mix:digitalCameraModelNumber", dcmodelNum); - } - if (dcserNum != null) { - smBuilder.add("mix:mix:digitalCameraModelSerialNo", dcserNum); - } - mixBuilder.add("mix:DigitalCameraModel", smBuilder); - } - - // Nest a buffer for CameraCaptureSettings - JsonObjectBuilder ccsBuilder = Json.createObjectBuilder(); - boolean useCcSetBuf = false; - d = niso.getFNumber(); - if (d != NisoImageMetadata.NULL) { - ccsBuilder.add("mix:fNumber", d); - useCcSetBuf = true; - } - d = niso.getExposureTime(); - if (d != NisoImageMetadata.NULL) { - ccsBuilder.add("mix:exposureTime", d); - useCcSetBuf = true; - } - n = niso.getExposureProgram(); - if (n != NisoImageMetadata.NULL) { - if (bMix10) { - ccsBuilder.add("mix:exposureProgram", n); - } else { - if (n > 8 || n < 0) { - n = 0; // force "Not defined" for bad value - } - ccsBuilder.add("mix:exposureProgram", NisoImageMetadata.EXPOSURE_PROGRAM[n]); - } - useCcSetBuf = true; - } - s = niso.getExifVersion(); - if (s != null) { - ccsBuilder.add("mix:exifVersion", s); - useCcSetBuf = true; - } - Rational r = niso.getBrightness(); - if (r != null) { - ccsBuilder.add("mix:brightnessValue", showRational(r)); - useCcSetBuf = true; - } - r = niso.getExposureBias(); - if (r != null) { - ccsBuilder.add("mix:exposureBiasValue", showRational(r)); - useCcSetBuf = true; - } - r = niso.getMaxApertureValue(); - if (r != null) { - ccsBuilder.add("mix:maxApertureValue", showRational(r)); - useCcSetBuf = true; - } - double[] darray = niso.getSubjectDistance(); - if (darray != null) { - ccsBuilder.add("mix:subjectDistance", showArray(darray)); - useCcSetBuf = true; - } - n = niso.getMeteringMode(); - if (n != NisoImageMetadata.NULL) { - if (bMix10) { - ccsBuilder.add("mix:meteringMode", n); - } else { - s = meteringModeToString(n); - if (s.startsWith("Center weighted")) { - s = "Center weighted Average"; - } - ccsBuilder.add("mix:MeteringMode", s); - } - useCcSetBuf = true; - } - n = niso.getFlash(); - if (n != NisoImageMetadata.NULL) { - // First bit (0 = Flash did not fire, 1 = Flash fired) - int firstBit = n & 1; - ccsBuilder.add("mix:flash", NisoImageMetadata.FLASH_20[firstBit]); - useCcSetBuf = true; - } - d = niso.getFocalLength(); - if (d != NisoImageMetadata.NULL) { - ccsBuilder.add("mix:focalLength", d); - useCcSetBuf = true; - } - r = niso.getFlashEnergy(); - if (r != null) { - ccsBuilder.add("mix:flashEnergy", showRational(r)); - useCcSetBuf = true; - } - n = niso.getBackLight(); - if (n != NisoImageMetadata.NULL) { - ccsBuilder.add("mix:backLight", n); - useCcSetBuf = true; - } - d = niso.getExposureIndex(); - if (d != NisoImageMetadata.NULL) { - ccsBuilder.add("mix:exposureIndex", d); - useCcSetBuf = true; - } - n = niso.getAutoFocus(); - if (n != NisoImageMetadata.NULL) { - ccsBuilder.add("mix:autoFocus", n); - useCcSetBuf = true; - } - d = niso.getXPrintAspectRatio(); - double d2 = niso.getYPrintAspectRatio(); - if (d != NisoImageMetadata.NULL) { - ccsBuilder.add("mix:xPrintAspectRatio", d); - useCcSetBuf = true; - } - if (d2 != NisoImageMetadata.NULL) { - ccsBuilder.add("mix:yPrintAspectRatio", d); - useCcSetBuf = true; - } - if (useCcSetBuf) { - mixBuilder.add("mix:CameraCaptureSettings", ccsBuilder); - - hasBuilder = true; - } - - n = niso.getOrientation(); - if (n != NisoImageMetadata.NULL) { - if (bMix10) { - mixBuilder.add("mix:orientation", n); - } else { - final String[] orient = { "unknown", "normal*", "normal, image flipped", "normal, rotated 180\u00B0", - "normal, image flipped, rotated 180\u00B0", "normal, image flipped, rotated cw 90\u00B0", - "normal, rotated ccw 90\u00B0", "normal, image flipped, rotated ccw 90\u00B0", - "normal, rotated cw 90\u00B0" }; - if (n > 8 || n < 0) { - n = 0; // force "unknown" for bad value - } - mixBuilder.add("mix:orientation", orient[n]); - } - hasBuilder = true; - } - s = niso.getMethodology(); - if (s != null) { - mixBuilder.add("mix:methodology", s); - hasBuilder = true; - } - if (hasBuilder) { - return mixBuilder; - } else { - return null; - } - } - - /* 1.0, Top level element 4 of 5: ImageAssessmentMetadata */ - protected JsonObjectBuilder showNisoImageAssessmentMetadata(NisoImageMetadata niso, boolean bMix10) { - JsonObjectBuilder mixBuilder = Json.createObjectBuilder(); - boolean hasBuilder = false; - - JsonObjectBuilder smBuilder = Json.createObjectBuilder(); - boolean useMetricsBuf = false; - - int n = niso.getSamplingFrequencyPlane(); - if (n != NisoImageMetadata.NULL) { - smBuilder.add("mix:samplingFrequencyPlane", n); - useMetricsBuf = true; - } - n = niso.getSamplingFrequencyUnit(); - if (n != NisoImageMetadata.NULL) { - if (bMix10) { - smBuilder.add("mix:samplingFrequencyUnit", n); - } else { - final String sfu[] = { null, "no absolute unit of measurement", "in.", "cm" }; - if (n < 1 || n > 3) { - n = 1; - } - smBuilder.add("mix:samplingFrequencyUnit", sfu[n]); - } - useMetricsBuf = true; - } - Rational r = niso.getXSamplingFrequency(); - if (r != null) { - smBuilder.add("mix:xSamplingFrequency", showRational(r)); - useMetricsBuf = true; - } - r = niso.getYSamplingFrequency(); - if (r != null) { - smBuilder.add("mix:ySamplingFrequency", showRational(r)); - useMetricsBuf = true; - } - if (useMetricsBuf) { - mixBuilder.add("mix:SpatialMetrics", smBuilder); - hasBuilder = true; - } - - JsonObjectBuilder imeBuilder = Json.createObjectBuilder(); - boolean useColorEncBuf = false; - - int[] iarray = niso.getBitsPerSample(); - if (iarray != null) { - imeBuilder.add("mix:bitsPerSample", showArray(iarray)); - imeBuilder.add("mix:bitsPerSampleUnit", "integer"); - // bitsPerSampleUnit can also be floating point. Don't ask me why. - useColorEncBuf = true; - } - n = niso.getSamplesPerPixel(); - if (n != NisoImageMetadata.NULL) { - imeBuilder.add("mix:samplesPerPixel", n); - useColorEncBuf = true; - } - - iarray = niso.getExtraSamples(); - if (iarray != null) { - // extraSamples must be limited to - // 0, 1, 2, or 3. - n = iarray[0]; - if (n >= 0 && n <= 3) { - if (bMix10) { - imeBuilder.add("mix:extraSamples", showArray(iarray)); - } else { - String[] sarray = new String[iarray.length]; - for (int ii = 0; ii < iarray.length; ii++) { - sarray[ii] = NisoImageMetadata.EXTRA_SAMPLE_20[iarray[ii]]; - imeBuilder.add("mix:extraSamples", showArray(sarray)); - } - - } - useColorEncBuf = true; - } - } - - String s = niso.getColormapReference(); - if (s != null) { - imeBuilder.add("mix:colormapReference", s); - useColorEncBuf = true; - } - - // This is complete nonsense, but it's what the spec says - iarray = niso.getGrayResponseCurve(); - if (iarray != null) { - imeBuilder.add("mix:grayResponseCurve", showArray(iarray)); - useColorEncBuf = true; - } - - n = niso.getGrayResponseUnit(); - if (n != NisoImageMetadata.NULL) { - if (bMix10) { - imeBuilder.add("mix:grayResponseUnit", n); - } else if (n > 0 && n <= 5) { - // Convert integer to text value; only values 1-5 are legal - imeBuilder.add("mix:grayResponseUnit", NisoImageMetadata.GRAY_RESPONSE_UNIT_20[n - 1]); - } - useColorEncBuf = true; - } - - r = niso.getWhitePointXValue(); - Rational r2 = niso.getWhitePointYValue(); - if (r != null && r2 != null) { - imeBuilder.add("mix:WhitePoint", Json.createObjectBuilder().add("mix:whitePointXValue", showRational(r)) - .add("mix:whitePointYValue", showRational(r2))); - useColorEncBuf = true; - } - - // A chromaticities buffer to go in the color encoding buffer. - JsonObjectBuilder pcBuilder = Json.createObjectBuilder(); - boolean useChromaBuf = false; - r = niso.getPrimaryChromaticitiesRedX(); - if (r != null) { - pcBuilder.add("mix:primaryChromaticitiesRedX", showRational(r)); - useChromaBuf = true; - } - r = niso.getPrimaryChromaticitiesRedY(); - if (r != null) { - pcBuilder.add("mix:primaryChromaticitiesRedY", showRational(r)); - useChromaBuf = true; - } - r = niso.getPrimaryChromaticitiesGreenX(); - if (r != null) { - pcBuilder.add("mix:primaryChromaticitiesGreenX", showRational(r)); - useChromaBuf = true; - } - r = niso.getPrimaryChromaticitiesGreenY(); - if (r != null) { - pcBuilder.add("mix:primaryChromaticitiesGreenY", showRational(r)); - useChromaBuf = true; - } - r = niso.getPrimaryChromaticitiesBlueX(); - if (r != null) { - pcBuilder.add("mix:primaryChromaticitiesBlueX", showRational(r)); - useChromaBuf = true; - } - r = niso.getPrimaryChromaticitiesBlueY(); - if (r != null) { - pcBuilder.add("mix:primaryChromaticitiesBlueY", showRational(r)); - useChromaBuf = true; - } - if (useChromaBuf) { - imeBuilder.add("mix:PrimaryChromaticities", pcBuilder); - useColorEncBuf = true; - } - - if (useColorEncBuf) { - mixBuilder.add("mix:ImageColorEncoding", imeBuilder); - hasBuilder = true; - } - - JsonObjectBuilder tdBuilder = Json.createObjectBuilder(); - boolean useTargetBuf = false; - n = niso.getTargetType(); - if (n != NisoImageMetadata.NULL) { - tdBuilder.add("mix:targetType", n); - useTargetBuf = true; - } - s = niso.getTargetIDManufacturer(); - if (s != null) { - tdBuilder.add("mix:targetManufacturer", s); - useTargetBuf = true; - } - s = niso.getTargetIDName(); - if (s != null) { - tdBuilder.add("mix:targetName", s); - useTargetBuf = true; - } - s = niso.getTargetIDNo(); - if (s != null) { - tdBuilder.add("mix:targetNo", s); - useTargetBuf = true; - } - s = niso.getTargetIDMedia(); - if (s != null) { - tdBuilder.add("mix:targetMedia", s); - useTargetBuf = true; - } - s = niso.getImageData(); - if (s != null) { - tdBuilder.add("mix:externalTarget", s); - useTargetBuf = true; - } - s = niso.getPerformanceData(); - if (s != null) { - tdBuilder.add("mix:performanceData", s); - useTargetBuf = true; - } - - if (useTargetBuf) { - mixBuilder.add("mix:TargetData", tdBuilder); - hasBuilder = true; - } - - if (hasBuilder) { - return mixBuilder; - } else { - return null; - } - } - - /* 1.0, Top level element 5 of 5: ChangeHistory (without time travel) */ - protected JsonObjectBuilder showChangeHistory(NisoImageMetadata niso, boolean bMix10) { - JsonObjectBuilder mixBuilder = Json.createObjectBuilder(); - boolean hasBuilder = false; - - String s = niso.getSourceData(); - if (s != null) { - mixBuilder.add("mix:sourceData", s); - hasBuilder = true; - } - s = niso.getProcessingAgency(); - if (s != null) { - mixBuilder.add("mix:processingAgency", s); - hasBuilder = true; - } - - JsonObjectBuilder psBuilder = Json.createObjectBuilder(); - boolean useSftwBuf = false; - s = niso.getProcessingSoftwareName(); - if (s != null) { - psBuilder.add("mix:processingSoftwareName", s); - useSftwBuf = true; - } - s = niso.getProcessingSoftwareVersion(); - if (s != null) { - psBuilder.add("mix:processingSoftwareVersion", s); - useSftwBuf = true; - } - s = niso.getOS(); - if (s != null) { - psBuilder.add("mix:processingOperatingSystemName", s); - useSftwBuf = true; - } - s = niso.getOSVersion(); - if (s != null) { - psBuilder.add("mix:processingOperatingSystemVersion", s); - useSftwBuf = true; - } - if (useSftwBuf) { - mixBuilder.add("mix:ProcessingSoftware", psBuilder); - hasBuilder = true; - } - String[] sarray = niso.getProcessingActions(); - if (sarray != null) { - mixBuilder.add("mix:processingActions", showArray(sarray)); - hasBuilder = true; - } - - if (hasBuilder) { - return mixBuilder; - } else { - return null; - } - } - - /** - * Convert the metering mode value to one of the suggested values for MIX 2.0 - */ - private String meteringModeToString(int n) { - String s = NisoImageMetadata.METERING_MODE[1]; - if (n >= 1 && n <= 6) { - s = NisoImageMetadata.METERING_MODE[n]; - } - // Capitalize first letter - return s.substring(0, 1).toUpperCase(Locale.ROOT) + s.substring(1); - } - - /** - * Convert the color space value (which is based on the TIFF - * PhotometricInterpretation convention) to one of the suggested values for MIX - * 2.0 - */ - private String photometricInterpretationToString(int n) { - switch (n) { - case 0: - return "WhiteIsZero"; - case 1: - return "BlackIsZero"; - case 2: - return "RGB"; - case 3: - return "PaletteColor"; - case 4: - return "TransparencyMask"; - case 5: - return "CMYK"; - case 6: - return "YCbCr"; - case 8: - return "CIELab"; - case 9: - return "ICCLab"; - case 10: - return "ITULab"; - case 32803: - return "CFA"; - case 34892: - return "LinearRaw"; - default: - return "Unknown"; - } - } - - /** - * Convert compression scheme value (based on the TIFF compression convention) - * to a label - */ - private String compressionSchemeToString(int n) { - for (int i = 0; i < NisoImageMetadata.COMPRESSION_SCHEME_INDEX.length; i++) { - if (n == NisoImageMetadata.COMPRESSION_SCHEME_INDEX[i]) - return NisoImageMetadata.COMPRESSION_SCHEME[i]; - } - return Integer.toString(n); - } - - /** - * Display the audio metadata formatted according to the AES schema. - * - * @param aes AES audio metadata - */ - protected JsonObjectBuilder showAESAudioMetadata(AESAudioMetadata aes) { - JsonObjectBuilder aesBuilder = Json.createObjectBuilder(); - - _sampleRate = aes.getSampleRate(); - - String s = aes.getAnalogDigitalFlag(); - if (s != null) { - aesBuilder.add("aes:analogDigitalFlag", s); - } - s = aes.getSchemaVersion(); - if (s != null) { - aesBuilder.add("aes:schemaVersion", s); - } - s = aes.getFormat(); - if (s != null) { - aesBuilder.add("aes:format", s); - } - s = aes.getSpecificationVersion(); - if (s != null) { - aesBuilder.add("aes:specificationVersion", s); - } - s = aes.getAppSpecificData(); - if (s != null) { - aesBuilder.add("aes:appSpecificData", s); - } - s = aes.getAudioDataEncoding(); - if (s != null) { - aesBuilder.add("aes:audioDataEncoding", s); - } - int in = aes.getByteOrder(); - if (in != AESAudioMetadata.NULL) { - aesBuilder.add("aes:byteOrder", (in == AESAudioMetadata.BIG_ENDIAN ? "BIG_ENDIAN" : "LITTLE_ENDIAN")); - } - long lin = aes.getFirstSampleOffset(); - if (lin != AESAudioMetadata.NULL) { - aesBuilder.add("aes:firstSampleOffset", lin); - } - String[] use = aes.getUse(); - if (use != null) { - aesBuilder.add("aes:use", - Json.createObjectBuilder().add("aes:useType", use[0]).add("aes:otherType", use[1])); - } - s = aes.getPrimaryIdentifier(); - if (s != null) { - String t = aes.getPrimaryIdentifierType(); - aesBuilder.add("aes:primaryIdentifier", s); - if (t != null) { - aesBuilder.add("aes:primaryIdentifierType", t); - } - } - List facelist = aes.getFaceList(); - if (facelist != null && !facelist.isEmpty()) { - // Add the face information, which is mostly filler. - AESAudioMetadata.Face f = facelist.get(0); - JsonObjectBuilder faceBuilder = Json.createObjectBuilder(); - - AESAudioMetadata.TimeDesc startTime = f.getStartTime(); - if (startTime != null) { - faceBuilder.add("aes:timeline", writeAESTimeRange(startTime, f.getDuration())); - } - int nchan = aes.getNumChannels(); - if (nchan != AESAudioMetadata.NULL) { - faceBuilder.add("aes:numChannels", nchan); - } - String[] locs = aes.getMapLocations(); - JsonArrayBuilder streamsBuilder = Json.createArrayBuilder(); - for (int ch = 0; ch < nchan; ch++) { - // write a stream description for each channel - streamsBuilder - .add(Json.createObjectBuilder().add("aes:channelNum", ch).add("aes:mapLocation", locs[ch])); - } - faceBuilder.add("aes:streams", streamsBuilder); - aesBuilder.add("aes:face", faceBuilder); - } - - // In the general case, a FormatList can contain multiple - // FormatRegions. This doesn't happen with any of the current - // modules; if it's needed in the future, simply set up an - // iteration loop on formatList. - List flist = aes.getFormatList(); - if (flist != null && !flist.isEmpty()) { - AESAudioMetadata.FormatRegion rgn = flist.get(0); - int bitDepth = rgn.getBitDepth(); - double sampleRate = rgn.getSampleRate(); - int wordSize = rgn.getWordSize(); - String[] bitRed = rgn.getBitrateReduction(); - // Build a FormatRegion subtree if at least one piece of data - // that goes into it is present. - JsonArrayBuilder formatListBuilder = Json.createArrayBuilder(); - JsonObjectBuilder formatRegionBuilder = Json.createObjectBuilder(); - if (bitDepth != AESAudioMetadata.NULL || sampleRate != AESAudioMetadata.NILL - || wordSize != AESAudioMetadata.NULL) { - if (bitDepth != AESAudioMetadata.NULL) { - formatRegionBuilder.add("aes:bitDepth", bitDepth); - } - if (sampleRate != AESAudioMetadata.NILL) { - formatRegionBuilder.add("aes:sampleRate", sampleRate); - } - if (wordSize != AESAudioMetadata.NULL) { - formatRegionBuilder.add("aes:wordSize", wordSize); - } - if (bitRed != null) { - formatRegionBuilder.add("aes:bitrateReduction", Json.createObjectBuilder() - .add("aes:codecName", bitRed[0]).add("aes:codecNameVersion", bitRed[1]) - .add("aes:codecCreatorApplication", bitRed[2]) - .add("aes:codecCreatorApplicationVersion", bitRed[3]).add("aes:codecQuality", bitRed[4]) - .add("aes:dataRate", bitRed[5]).add("aes:dataRateMode", bitRed[6])); - } - - formatListBuilder.add(formatRegionBuilder); - aesBuilder.add("aes:formatList", formatListBuilder); - } - } - return aesBuilder; - } - - /* - * Break out the writing of a timeRangeType element. This always gives a start - * time of 0. This is all FAKE DATA for the moment. - */ - private JsonObjectBuilder writeAESTimeRange(AESAudioMetadata.TimeDesc start, AESAudioMetadata.TimeDesc duration) { - double sr = start.getSampleRate(); - if (sr == 1.0) { - sr = _sampleRate; - } - - JsonObjectBuilder timerangeBuilder = Json.createObjectBuilder(); - timerangeBuilder.add("tcf:startTime", - Json.createObjectBuilder().add("tcf:frameCount", 30).add("tcf:timeBase", 1000) - .add("tcf:videoField", "FIELD_1").add("tcf:countingMode", "NTSC_NON_DROP_FRAME") - .add("tcf:hours", start.getHours()).add("tcf:minutes", start.getMinutes()) - .add("tcf:seconds", start.getSeconds()).add("tcf:frames", start.getFrames()) - .add("tcf:samples", - Json.createObjectBuilder().add("tcf:sampleRate", "S" + Integer.toString((int) sr)) - .add("tcf:numberOfSamples", start.getSamples())) - .add("tcf:filmFraming", Json.createObjectBuilder().add("tcf:framing", "NOT_APPLICABLE") - .add("tcf:framingType", "tcf:ntscFilmFramingType"))); - if (duration != null) { - sr = duration.getSampleRate(); - if (sr == 1.0) { - sr = _sampleRate; - } - timerangeBuilder.add("tcf:duration", - Json.createObjectBuilder().add("tcf:frameCount", 30).add("tcf:timeBase", 1000) - .add("tcf:videoField", "FIELD_1").add("tcf:countingMode", "NTSC_NON_DROP_FRAME") - .add("tcf:hours", duration.getHours()).add("tcf:minutes", duration.getMinutes()) - .add("tcf:seconds", duration.getSeconds()).add("tcf:frames", duration.getFrames()) - .add("tcf:samples", - Json.createObjectBuilder().add("tcf:sampleRate", "S" + Integer.toString((int) sr)) - .add("tcf:numberOfSamples", duration.getSamples())) - .add("tcf:filmFraming", Json.createObjectBuilder().add("tcf:framing", "NOT_APPLICABLE") - .add("tcf:framingType", "tcf:ntscFilmFramingType"))); - } - return timerangeBuilder; - } - - protected JsonArrayBuilder showArray(int[] iarray) { - JsonArrayBuilder aBuilder = Json.createArrayBuilder(); - for (int i : iarray) { - aBuilder.add(i); - } - return aBuilder; - } - - protected JsonArrayBuilder showArray(double[] darray) { - JsonArrayBuilder dBuilder = Json.createArrayBuilder(); - for (double d : darray) { - dBuilder.add(d); - } - return dBuilder; - } - - protected JsonArrayBuilder showArray(String[] sarray) { - JsonArrayBuilder sBuilder = Json.createArrayBuilder(); - for (String s : sarray) { - if (s == null) { - sBuilder.addNull(); - } else { - sBuilder.add(s); - } - } - return sBuilder; - } - - protected JsonArrayBuilder showArray(Rational[] rarray) { - JsonArrayBuilder rBuilder = Json.createArrayBuilder(); - for (Rational r : rarray) { - if (r == null) { - rBuilder.addNull(); - } else { - rBuilder.add(showRational(r)); - } - } - return rBuilder; - } - - protected JsonArrayBuilder showRational(Rational r) { - JsonArrayBuilder rationalBuilder = Json.createArrayBuilder(); - long numer = r.getNumerator(); - long denom = r.getDenominator(); - rationalBuilder.add(numer); - rationalBuilder.add(denom); - return rationalBuilder; - } + /** + * **************************************************************** PRIVATE CLASS FIELDS. + * **************************************************************** + */ + + /** Handler name. */ + private static final String NAME = "JSON"; + + /** Handler release identifier. */ + private static final String RELEASE = "1.0"; + + /** Handler release date. */ + private static final int[] DATE = {2019, 10, 18}; + + /** Handler informative note. */ + private static final String NOTE = ""; + + /** Handler rights statement. */ + private static final String RIGHTS = + "Version 1.0 release by Open Preservation Foundation. " + + "Released under the GNU Lesser General Public License."; + + /** + * **************************************************************** PRIVATE INSTANCE FIELDS. + * **************************************************************** + */ + + /** Main JSON builder */ + private JsonObjectBuilder jhoveBuilder; + + /* Sample rate. */ + private double _sampleRate; + + /** + * **************************************************************** CLASS CONSTRUCTOR. + * **************************************************************** + */ + + /** Creates an JsonHandler. */ + public JsonHandler() { + super(NAME, RELEASE, DATE, NOTE, RIGHTS); + _vendor = Agent.bnfInstance(); + } + + /** Constructor for use by subclasses. */ + public JsonHandler(String name, String release, int[] date, String note, String rights) { + super(name, release, date, note, rights); + _vendor = Agent.bnfInstance(); + } + + /** + * **************************************************************** PUBLIC INSTANCE METHODS. + * **************************************************************** + */ + + /** Outputs minimal information about the application */ + @Override + public void show() { + _level--; + } + + /** + * Outputs detailed information about the application, including configuration, available modules + * and handlers, etc. + */ + @Override + public void show(App app) { + JsonObjectBuilder appBuilder = Json.createObjectBuilder(); + + JsonObjectBuilder apiBuilder = Json.createObjectBuilder(); + apiBuilder.add("version", app.getRelease()); + apiBuilder.add("date", date.format(_je.getDate())); + appBuilder.add("api", apiBuilder); + + String configFile = _je.getConfigFile(); + if (configFile != null) { + appBuilder.add("configuration", configFile); + } + String s = _je.getSaxClass(); + if (s != null) { + appBuilder.add("saxParser", s); + } + s = _je.getJhoveHome(); + if (s != null) { + appBuilder.add("jhoveHome", s); + } + s = _je.getEncoding(); + if (s != null) { + appBuilder.add("encoding", s); + } + s = _je.getTempDirectory(); + if (s != null) { + appBuilder.add("tempDirectory", s); + } + appBuilder.add("bufferSize", _je.getBufferSize()); + + JsonArrayBuilder modulesBuilder = Json.createArrayBuilder(); + for (String modKey : _je.getModuleMap().keySet()) { + Module module = _je.getModule(modKey); + modulesBuilder.add( + Json.createObjectBuilder() + .add("module", module.getName()) + .add("release", module.getRelease())); + } + appBuilder.add("modules", modulesBuilder); + + JsonArrayBuilder oHandlersBuilder = Json.createArrayBuilder(); + for (String handlerKey : _je.getHandlerMap().keySet()) { + OutputHandler handler = _je.getHandler(handlerKey); + oHandlersBuilder.add( + Json.createObjectBuilder() + .add("outputHandler", handler.getName()) + .add("release", handler.getRelease())); + } + appBuilder.add("outputHandlers", oHandlersBuilder); + + appBuilder.add("usage", app.getUsage()); + appBuilder.add("rights", app.getRights()); + + jhoveBuilder.add("app", appBuilder); + } + + /** Outputs information about the OutputHandler specified in the parameter */ + @Override + public void show(OutputHandler handler) { + JsonObjectBuilder outputHandlerBuilder = Json.createObjectBuilder(); + outputHandlerBuilder.add("name", handler.getName()); + outputHandlerBuilder.add("release", handler.getRelease()); + outputHandlerBuilder.add("date", date.format(handler.getDate())); + List list = handler.getSpecification(); + int n = list.size(); + if (n > 0) { + JsonArrayBuilder specBuilder = Json.createArrayBuilder(); + for (int i = 0; i < n; i++) { + specBuilder.add(showDocument(list.get(i))); + } + outputHandlerBuilder.add("specifications", specBuilder); + } + Agent vendor = handler.getVendor(); + if (vendor != null) { + outputHandlerBuilder.add("vendor", showAgent(vendor, "Vendor")); + } + String s; + if ((s = handler.getNote()) != null) { + outputHandlerBuilder.add("note", s); + } + if ((s = handler.getRights()) != null) { + outputHandlerBuilder.add("rights", s); + } + + jhoveBuilder.add("handler", outputHandlerBuilder); + } + + /** Outputs information about a Module */ + @Override + public void show(Module module) { + JsonObjectBuilder modBuilder = Json.createObjectBuilder(); + modBuilder.add("name", module.getName()); + modBuilder.add("release", module.getRelease()); + modBuilder.add("date", HandlerBase.date.format(module.getDate())); + + String[] ss = module.getFormat(); + if (ss.length > 0) { + modBuilder.add("formats", showArray(ss)); + } + String s = module.getCoverage(); + if (s != null) { + modBuilder.add("coverage", s); + } + ss = module.getMimeType(); + if (ss.length > 0) { + modBuilder.add("mimeTypes", showArray(ss)); + } + List list1 = module.getSignature(); + if (list1 != null && !list1.isEmpty()) { + JsonArrayBuilder sigBuilder = Json.createArrayBuilder(); + for (Signature sig : list1) { + sigBuilder.add(showSignature(sig)); + } + modBuilder.add("signatures", sigBuilder); + } + List list2 = module.getSpecification(); + if (list2 != null && !list2.isEmpty()) { + JsonArrayBuilder docBuilder = Json.createArrayBuilder(); + for (Document doc : list2) { + docBuilder.add(showDocument(doc)); + } + modBuilder.add("specifications", docBuilder); + } + List ftr = module.getFeatures(); + if (ftr != null && !ftr.isEmpty()) { + JsonArrayBuilder featuresBuilder = Json.createArrayBuilder(); + for (String f : ftr) { + featuresBuilder.add(f); + } + modBuilder.add("features", featuresBuilder); + } + + JsonObjectBuilder methodBuilder = Json.createObjectBuilder(); + if ((s = module.getWellFormedNote()) != null) { + methodBuilder.add("wellFormed", s); + } + if ((s = module.getValidityNote()) != null) { + methodBuilder.add("validity", s); + } + if ((s = module.getRepInfoNote()) != null) { + methodBuilder.add("repInfo", s); + } + modBuilder.add("methodology", methodBuilder); + + Agent vendor = module.getVendor(); + if (vendor != null) { + modBuilder.add("vendor", showAgent(vendor, "Vendor")); + } + if ((s = module.getNote()) != null) { + modBuilder.add("note", s); + } + if ((s = module.getRights()) != null) { + modBuilder.add("rights", s); + } + + jhoveBuilder.add("module", modBuilder); + } + + /** Outputs the information contained in a RepInfo object */ + @Override + public void show(RepInfo info) { + JsonObjectBuilder infoBuilder = Json.createObjectBuilder(); + + Module module = info.getModule(); + _logger.info("Reporting RepInfo"); + if (_je.getSignatureFlag()) { + _logger.info("Checking signatures only"); + } + infoBuilder.add("uri", info.getUri()); + + if (module != null) { + infoBuilder.add( + "reportingModule", + Json.createObjectBuilder() + .add("name", module.getName()) + .add("release", module.getRelease()) + .add("date", date.format(module.getDate()))); + } + Date date = info.getCreated(); + if (date != null) { + infoBuilder.add("created", toDateTime(date)); + } + date = info.getLastModified(); + if (date != null) { + infoBuilder.add("lastModified", toDateTime(date)); + } + long size = info.getSize(); + if (size > -1) { + infoBuilder.add("size", size); + } + String s = info.getFormat(); + if (s != null) { + infoBuilder.add("format", s); + } + if ((s = info.getVersion()) != null) { + infoBuilder.add("version", s); + } + String wfStr; + if (!_je.getSignatureFlag()) { + switch (info.getWellFormed()) { + case RepInfo.TRUE: + wfStr = "Well-Formed"; + break; + + case RepInfo.FALSE: + wfStr = "Not well-formed"; + break; + + default: + wfStr = "Unknown"; + break; + } + // If it's well-formed, append validity information + if (info.getWellFormed() == RepInfo.TRUE) { + switch (info.getValid()) { + case RepInfo.TRUE: + wfStr += " and valid"; + break; + + case RepInfo.FALSE: + wfStr += ", but not valid"; + break; + + default: + // case UNDETERMINED: add nothing + break; + } + } + _logger.info("Validity/WF status: " + wfStr); + infoBuilder.add("status", wfStr); + } else { + // If we aren't checking signatures, we still need to say something. + switch (info.getWellFormed()) { + case RepInfo.TRUE: + wfStr = "Well-Formed"; + break; + + default: + wfStr = "Not well-formed"; + break; + } + infoBuilder.add("status", wfStr); + } + + List list1 = info.getSigMatch(); + if (list1 != null && !list1.isEmpty()) { + JsonArrayBuilder sigBuilder = Json.createArrayBuilder(); + for (String sigm : list1) { + sigBuilder.add(sigm); + } + infoBuilder.add("sigMatch", sigBuilder); + } + + List list2 = info.getMessage(); + if (list2 != null && !list2.isEmpty()) { + JsonArrayBuilder msgBuilder = Json.createArrayBuilder(); + for (Message msg : list2) { + msgBuilder.add(showMessage(msg)); + } + infoBuilder.add("messages", msgBuilder); + } + + s = info.getMimeType(); + if (s != null) { + infoBuilder.add("mimeType", s); + } + + List list3 = info.getProfile(); + if (list3 != null && !list3.isEmpty()) { + JsonArrayBuilder profBuilder = Json.createArrayBuilder(); + for (String prof : list3) { + profBuilder.add(prof); + } + infoBuilder.add("profiles", profBuilder); + } + + Map map = info.getProperty(); + if (map != null && !map.isEmpty()) { + JsonArrayBuilder propBuilder = Json.createArrayBuilder(); + for (String key : map.keySet()) { + Property property = info.getProperty(key); + propBuilder.add(showProperty(property)); + } + infoBuilder.add("properties", propBuilder); + } + + List list4 = info.getChecksum(); + if (list4 != null && !list4.isEmpty()) { + JsonArrayBuilder checksumBuilder = Json.createArrayBuilder(); + for (Checksum ck : list4) { + checksumBuilder.add(showChecksum(ck)); + } + infoBuilder.add("properties", checksumBuilder); + } + + if ((s = info.getNote()) != null) { + infoBuilder.add("note", s); + } + + jhoveBuilder.add("repInfo", infoBuilder); + } + + /** + * **************************************************************** PRIVATE INSTANCE METHODS. + * **************************************************************** + */ + protected JsonObjectBuilder showAgent(Agent agent, String label) { + JsonObjectBuilder agentBuilder = Json.createObjectBuilder(); + agentBuilder.add("kind", label); + agentBuilder.add("name", agent.getName()); + agentBuilder.add("type", agent.getType().toString()); + String s = agent.getAddress(); + if (s != null) { + agentBuilder.add("address", s); + } + if ((s = agent.getTelephone()) != null) { + agentBuilder.add("telephone", s); + } + if ((s = agent.getFax()) != null) { + agentBuilder.add("fax", s); + } + if ((s = agent.getEmail()) != null) { + agentBuilder.add("email", s); + } + if ((s = agent.getWeb()) != null) { + agentBuilder.add("web", s); + } + return agentBuilder; + } + + protected JsonObjectBuilder showChecksum(Checksum checksum) { + return Json.createObjectBuilder() + .add("checksum", checksum.getValue()) + .add("type", checksum.getType().toString()); + } + + protected JsonObjectBuilder showDocument(Document document) { + JsonObjectBuilder docBuilder = Json.createObjectBuilder(); + docBuilder.add("title", document.getTitle()); + docBuilder.add("type", document.getType().toString()); + List list1 = document.getAuthor(); + if (list1 != null && !list1.isEmpty()) { + JsonArrayBuilder autBuilder = Json.createArrayBuilder(); + for (Agent ag : list1) { + autBuilder.add(showAgent(ag, "Author")); + } + docBuilder.add("authors", autBuilder); + } + List list2 = document.getPublisher(); + if (list2 != null && !list2.isEmpty()) { + JsonArrayBuilder pubBuilder = Json.createArrayBuilder(); + for (Agent ag : list2) { + pubBuilder.add(showAgent(ag, "Publisher")); + } + docBuilder.add("publishers", pubBuilder); + } + String s = document.getEdition(); + if (s != null) { + docBuilder.add("edition", s); + } + if ((s = document.getDate()) != null) { + docBuilder.add("date", s); + } + if ((s = document.getEnumeration()) != null) { + docBuilder.add("enumeration", s); + } + if ((s = document.getPages()) != null) { + docBuilder.add("pages", s); + } + List list3 = document.getIdentifier(); + if (list3 != null && !list3.isEmpty()) { + JsonArrayBuilder idBuilder = Json.createArrayBuilder(); + for (Identifier id : list3) { + idBuilder.add(showIdentifier(id)); + } + docBuilder.add("identifiers", idBuilder); + } + if ((s = document.getNote()) != null) { + docBuilder.add("note", s); + } + return docBuilder; + } + + /** + * Do the final output. This should be in a suitable format for including multiple files between + * the header and the footer, and the XML of the header and footer must balance out. + */ + @Override + public void showFooter() { + JsonObjectBuilder mainBuilder = Json.createObjectBuilder(); + mainBuilder.add("jhove", jhoveBuilder); + JsonObject jsonObject = mainBuilder.build(); + + JsonWriter jsonWriter = Json.createWriter(_writer); + jsonWriter.writeObject(jsonObject); + jsonWriter.close(); + } + + /** + * Do the initial output. This should be in a suitable format for including multiple files between + * the header and the footer, and the XML of the header and footer must balance out. + */ + @Override + public void showHeader() { + jhoveBuilder = Json.createObjectBuilder(); + jhoveBuilder.add("name", _app.getName()); + jhoveBuilder.add("release", _app.getRelease()); + jhoveBuilder.add("date", HandlerBase.date.format(_app.getDate())); + jhoveBuilder.add("executionTime", toDateTime(new Date())); + } + + protected JsonObjectBuilder showIdentifier(Identifier identifier) { + JsonObjectBuilder idBuilder = Json.createObjectBuilder(); + idBuilder.add("value", identifier.getValue()); + idBuilder.add("type", identifier.getType().toString()); + String note = identifier.getNote(); + if (note != null) { + idBuilder.add("note", note); + } + return idBuilder; + } + + protected JsonObjectBuilder showMessage(Message message) { + JsonObjectBuilder msgBuilder = Json.createObjectBuilder(); + + msgBuilder.add("message", message.getMessage()); + String submsg = message.getSubMessage(); + if (submsg != null) { + msgBuilder.add("subMessage", submsg); + } + long offset = message.getOffset(); + if (offset > -1) { + msgBuilder.add("offset", offset); + } + if (!message.getPrefix().isEmpty()) { + msgBuilder.add("severity", message.getPrefix().toLowerCase()); + } + String id = message.getJhoveMessage().getId(); + if (!(id == null || id.isEmpty() || JhoveMessages.NO_ID.equals(id))) { + msgBuilder.add("id", message.getId()); + } + return msgBuilder; + } + + protected JsonObjectBuilder showSignature(Signature signature) { + JsonObjectBuilder sigBuilder = Json.createObjectBuilder(); + + String sigValue; + if (signature.isStringValue()) { + sigValue = signature.getValueString(); + } else { + sigValue = signature.getValueHexString(); + } + sigBuilder.add("type", signature.getType().toString()); + sigBuilder.add("value", sigValue); + if (SignatureType.MAGIC.equals(signature.getType()) + && ((InternalSignature) signature).hasFixedOffset()) { + sigBuilder.add( + "offset", "0x" + Integer.toHexString(((InternalSignature) signature).getOffset())); + } + String note = signature.getNote(); + if (note != null) { + sigBuilder.add("note", note); + } + String use = signature.getUse().toString(); + if (use != null) { + sigBuilder.add("use", use); + } + return sigBuilder; + } + + /* Do special conversions on values as needed. */ + protected String valueToString(Object obj) { + if (obj instanceof Date) { + return toDateTime((Date) obj); + } + return obj.toString(); + } + + protected JsonObjectBuilder showProperty(Property property) { + JsonObjectBuilder propBuilder = Json.createObjectBuilder(); + + PropertyArity arity = property.getArity(); + switch (arity) { + case SCALAR: + return showScalarProperty(property); + case ARRAY: + return showArrayProperty(property); + case LIST: + return showListProperty(property); + case MAP: + return showMapProperty(property); + case SET: + return showSetProperty(property); + default: + return propBuilder; + } + } + + protected JsonObjectBuilder showScalarProperty(Property property) { + JsonObjectBuilder propBuilder = Json.createObjectBuilder(); + + PropertyType propType = property.getType(); + switch (propType) { + case BOOLEAN: + Boolean b = (Boolean) property.getValue(); + propBuilder.add(property.getName(), b.booleanValue()); + break; + case BYTE: + case CHARACTER: + case OBJECT: + propBuilder.add(property.getName(), property.getValue().toString()); + break; + case DATE: + Date dt = (Date) property.getValue(); + propBuilder.add(property.getName(), toDateTime(dt)); + break; + case DOUBLE: + Double d = (Double) property.getValue(); + propBuilder.add(property.getName(), d.doubleValue()); + break; + case FLOAT: + Float f = (Float) property.getValue(); + propBuilder.add(property.getName(), f.floatValue()); + break; + case INTEGER: + Integer i = (Integer) property.getValue(); + propBuilder.add(property.getName(), i.intValue()); + break; + case LONG: + Long l = (Long) property.getValue(); + propBuilder.add(property.getName(), l.longValue()); + break; + case AESAUDIOMETADATA: + propBuilder.add( + property.getName(), showAESAudioMetadata((AESAudioMetadata) property.getValue())); + break; + case NISOIMAGEMETADATA: + propBuilder.add( + property.getName(), showNisoImageMetadata((NisoImageMetadata) property.getValue())); + break; + case TEXTMDMETADATA: + propBuilder.add( + property.getName(), showTextMDMetadata((TextMDMetadata) property.getValue())); + break; + case SHORT: + Short s = (Short) property.getValue(); + propBuilder.add(property.getName(), s.shortValue()); + break; + case STRING: + propBuilder.add(property.getName(), (String) property.getValue()); + break; + case RATIONAL: + propBuilder.add(property.getName(), showRational((Rational) property.getValue())); + break; + case PROPERTY: + Property property2 = (Property) property.getValue(); + propBuilder.add(property.getName(), showProperty(property2)); + break; + default: + propBuilder.add(property.getName(), property.getValue().toString()); + break; + } + return propBuilder; + } + + protected JsonObjectBuilder showListProperty(Property property) { + JsonObjectBuilder propBuilder = Json.createObjectBuilder(); + List propList = (List) property.getValue(); + JsonArrayBuilder lPropBuilder = Json.createArrayBuilder(); + PropertyType type = property.getType(); + + ListIterator iter = propList.listIterator(); + while (iter.hasNext()) { + Object val = iter.next(); + switch (type) { + case BOOLEAN: + lPropBuilder.add(((Boolean) val).booleanValue()); + break; + case BYTE: + lPropBuilder.add(valueToString(val)); + break; + case CHARACTER: + lPropBuilder.add(valueToString(val)); + break; + case DATE: + lPropBuilder.add(valueToString(val)); + break; + case DOUBLE: + lPropBuilder.add(((Double) val).doubleValue()); + break; + case FLOAT: + lPropBuilder.add(((Float) val).floatValue()); + break; + case INTEGER: + lPropBuilder.add(((Integer) val).intValue()); + break; + case LONG: + lPropBuilder.add(((Long) val).longValue()); + break; + case OBJECT: + lPropBuilder.add(valueToString(val)); + break; + case SHORT: + lPropBuilder.add(((Short) val).shortValue()); + break; + case STRING: + lPropBuilder.add(valueToString(val)); + break; + case RATIONAL: + lPropBuilder.add(showRational((Rational) val)); + break; + case PROPERTY: + lPropBuilder.add(showProperty((Property) val)); + break; + case NISOIMAGEMETADATA: + lPropBuilder.add(showNisoImageMetadata((NisoImageMetadata) property.getValue())); + break; + case AESAUDIOMETADATA: + lPropBuilder.add(showAESAudioMetadata((AESAudioMetadata) property.getValue())); + break; + case TEXTMDMETADATA: + lPropBuilder.add(showTextMDMetadata((TextMDMetadata) property.getValue())); + break; + default: + break; + } + } + propBuilder.add(property.getName(), lPropBuilder); + return propBuilder; + } + + protected JsonObjectBuilder showSetProperty(Property property) { + JsonObjectBuilder propBuilder = Json.createObjectBuilder(); + Set propSet = (Set) property.getValue(); + JsonArrayBuilder lPropBuilder = Json.createArrayBuilder(); + PropertyType type = property.getType(); + + Iterator iter = propSet.iterator(); + while (iter.hasNext()) { + Object val = iter.next(); + switch (type) { + case BOOLEAN: + lPropBuilder.add(((Boolean) val).booleanValue()); + break; + case BYTE: + lPropBuilder.add(valueToString(val)); + break; + case CHARACTER: + lPropBuilder.add(valueToString(val)); + break; + case DATE: + lPropBuilder.add(valueToString(val)); + break; + case DOUBLE: + lPropBuilder.add(((Double) val).doubleValue()); + break; + case FLOAT: + lPropBuilder.add(((Float) val).floatValue()); + break; + case INTEGER: + lPropBuilder.add(((Integer) val).intValue()); + break; + case LONG: + lPropBuilder.add(((Long) val).longValue()); + break; + case OBJECT: + lPropBuilder.add(valueToString(val)); + break; + case SHORT: + lPropBuilder.add(((Short) val).shortValue()); + break; + case STRING: + lPropBuilder.add(valueToString(val)); + break; + case RATIONAL: + lPropBuilder.add(showRational((Rational) val)); + break; + case PROPERTY: + lPropBuilder.add(showProperty((Property) val)); + break; + case NISOIMAGEMETADATA: + lPropBuilder.add(showNisoImageMetadata((NisoImageMetadata) property.getValue())); + break; + case AESAUDIOMETADATA: + lPropBuilder.add(showAESAudioMetadata((AESAudioMetadata) property.getValue())); + break; + case TEXTMDMETADATA: + lPropBuilder.add(showTextMDMetadata((TextMDMetadata) property.getValue())); + break; + default: + break; + } + } + propBuilder.add(property.getName(), lPropBuilder); + return propBuilder; + } + + protected JsonObjectBuilder showMapProperty(Property property) { + JsonObjectBuilder propBuilder = Json.createObjectBuilder(); + JsonObjectBuilder lPropBuilder = Json.createObjectBuilder(); + Map propMap = (Map) property.getValue(); + PropertyType type = property.getType(); + Iterator keyIter = propMap.keySet().iterator(); + while (keyIter.hasNext()) { + Object key = keyIter.next(); + String keystr = key.toString(); + Object val = propMap.get(key); + switch (type) { + case BOOLEAN: + lPropBuilder.add(keystr, ((Boolean) val).booleanValue()); + break; + case BYTE: + lPropBuilder.add(keystr, valueToString(val)); + break; + case CHARACTER: + lPropBuilder.add(keystr, valueToString(val)); + break; + case DATE: + lPropBuilder.add(keystr, valueToString(val)); + break; + case DOUBLE: + lPropBuilder.add(keystr, ((Double) val).doubleValue()); + break; + case FLOAT: + lPropBuilder.add(keystr, ((Float) val).floatValue()); + break; + case INTEGER: + lPropBuilder.add(keystr, ((Integer) val).intValue()); + break; + case LONG: + lPropBuilder.add(keystr, ((Long) val).longValue()); + break; + case OBJECT: + lPropBuilder.add(keystr, valueToString(val)); + break; + case SHORT: + lPropBuilder.add(keystr, ((Short) val).shortValue()); + break; + case STRING: + lPropBuilder.add(keystr, valueToString(val)); + break; + case RATIONAL: + lPropBuilder.add(keystr, showRational((Rational) val)); + break; + case PROPERTY: + lPropBuilder.add(keystr, showProperty((Property) val)); + break; + case NISOIMAGEMETADATA: + lPropBuilder.add(keystr, showNisoImageMetadata((NisoImageMetadata) property.getValue())); + break; + case AESAUDIOMETADATA: + lPropBuilder.add(keystr, showAESAudioMetadata((AESAudioMetadata) property.getValue())); + break; + case TEXTMDMETADATA: + lPropBuilder.add(keystr, showTextMDMetadata((TextMDMetadata) property.getValue())); + break; + default: + break; + } + } + propBuilder.add(property.getName(), lPropBuilder); + return propBuilder; + } + + /** Gives the length (number of elements) of a property */ + protected int propertyLength(Property property) { + int n = 0; + try { + switch (property.getArity()) { + case SET: + Set propSet = (Set) property.getValue(); + n = propSet.size(); + break; + case LIST: + List propList = (List) property.getValue(); + n = propList.size(); + break; + case MAP: + Map propMap = (Map) property.getValue(); + n = propMap.size(); + break; + case ARRAY: + // Ack! Is there any easy way to do this? + switch (property.getType()) { + case BOOLEAN: + boolean[] boolArray = (boolean[]) property.getValue(); + n = boolArray.length; + break; + case BYTE: + byte[] byteArray = (byte[]) property.getValue(); + n = byteArray.length; + break; + case CHARACTER: + char[] charArray = (char[]) property.getValue(); + n = charArray.length; + break; + case DATE: + Date[] dateArray = (Date[]) property.getValue(); + n = dateArray.length; + break; + case DOUBLE: + double[] doubleArray = (double[]) property.getValue(); + n = doubleArray.length; + break; + case FLOAT: + float[] floatArray = (float[]) property.getValue(); + n = floatArray.length; + break; + case INTEGER: + int[] intArray = (int[]) property.getValue(); + n = intArray.length; + break; + case LONG: + long[] longArray = (long[]) property.getValue(); + n = longArray.length; + break; + case OBJECT: + Object[] objArray = (Object[]) property.getValue(); + n = objArray.length; + break; + case SHORT: + short[] shortArray = (short[]) property.getValue(); + n = shortArray.length; + break; + case STRING: + String[] stringArray = (String[]) property.getValue(); + n = stringArray.length; + break; + case RATIONAL: + Rational[] rationalArray = (Rational[]) property.getValue(); + n = rationalArray.length; + break; + case PROPERTY: + Property[] propArray = (Property[]) property.getValue(); + n = propArray.length; + break; + case NISOIMAGEMETADATA: + NisoImageMetadata[] nisoArray = (NisoImageMetadata[]) property.getValue(); + n = nisoArray.length; + break; + case AESAUDIOMETADATA: + AESAudioMetadata[] aesArray = (AESAudioMetadata[]) property.getValue(); + n = aesArray.length; + break; + case TEXTMDMETADATA: + TextMDMetadata[] textMDArray = (TextMDMetadata[]) property.getValue(); + n = textMDArray.length; + break; + default: + Object[] array2 = (Object[]) property.getValue(); + n = array2.length; + break; + } + break; + default: + if (property.getValue().toString().length() == 0) { + n = 0; + } else { + n = 1; + } + break; + } + } catch (Exception e) { + // If something goes seriously wrong, return true to punt the property + return 0; + } + return n; + } + + /* + * The array property has so many special cases of its own that we break it out + * of showProperty + */ + protected JsonObjectBuilder showArrayProperty(Property property) { + boolean[] boolArray = null; + byte[] byteArray = null; + char[] charArray = null; + java.util.Date[] dateArray = null; + double[] doubleArray = null; + float[] floatArray = null; + int[] intArray = null; + long[] longArray = null; + Object[] objArray = null; + Property[] propArray = null; + short[] shortArray = null; + String[] stringArray = null; + Rational[] rationalArray = null; + NisoImageMetadata[] nisoArray = null; + AESAudioMetadata[] aesArray = null; + TextMDMetadata[] textMDArray = null; + int n = 0; + + PropertyType propType = property.getType(); + switch (propType) { + case BOOLEAN: + boolArray = (boolean[]) property.getValue(); + n = boolArray.length; + break; + case BYTE: + byteArray = (byte[]) property.getValue(); + n = byteArray.length; + break; + case CHARACTER: + charArray = (char[]) property.getValue(); + n = charArray.length; + break; + case DATE: + dateArray = (Date[]) property.getValue(); + n = dateArray.length; + break; + case DOUBLE: + doubleArray = (double[]) property.getValue(); + n = doubleArray.length; + break; + case FLOAT: + floatArray = (float[]) property.getValue(); + n = floatArray.length; + break; + case INTEGER: + intArray = (int[]) property.getValue(); + n = intArray.length; + break; + case LONG: + longArray = (long[]) property.getValue(); + n = longArray.length; + break; + case OBJECT: + objArray = (Object[]) property.getValue(); + n = objArray.length; + break; + case SHORT: + shortArray = (short[]) property.getValue(); + n = shortArray.length; + break; + case STRING: + stringArray = (String[]) property.getValue(); + n = stringArray.length; + break; + case RATIONAL: + rationalArray = (Rational[]) property.getValue(); + n = rationalArray.length; + break; + case PROPERTY: + propArray = (Property[]) property.getValue(); + n = propArray.length; + break; + case NISOIMAGEMETADATA: + nisoArray = (NisoImageMetadata[]) property.getValue(); + n = nisoArray.length; + break; + case AESAUDIOMETADATA: + aesArray = (AESAudioMetadata[]) property.getValue(); + n = aesArray.length; + break; + case TEXTMDMETADATA: + textMDArray = (TextMDMetadata[]) property.getValue(); + n = textMDArray.length; + break; + default: + break; + } + + JsonArrayBuilder arrayBuilder = Json.createArrayBuilder(); + for (int i = 0; i < n; i++) { + switch (propType) { + case BOOLEAN: + arrayBuilder.add(boolArray[i]); + break; + case BYTE: + arrayBuilder.add(String.valueOf(byteArray[i])); + break; + case CHARACTER: + arrayBuilder.add(String.valueOf(charArray[i])); + break; + case DATE: + arrayBuilder.add(dateArray[i].toString()); + break; + case DOUBLE: + arrayBuilder.add(doubleArray[i]); + break; + case FLOAT: + arrayBuilder.add(floatArray[i]); + break; + case INTEGER: + arrayBuilder.add(intArray[i]); + break; + case LONG: + arrayBuilder.add(longArray[i]); + break; + case OBJECT: + arrayBuilder.add(valueToString(objArray[i])); + break; + case SHORT: + arrayBuilder.add(shortArray[i]); + break; + case STRING: + arrayBuilder.add(stringArray[i]); + break; + case RATIONAL: + arrayBuilder.add(rationalArray[i].toString()); + break; + case PROPERTY: + arrayBuilder.add(showProperty(propArray[i])); + break; + case NISOIMAGEMETADATA: + arrayBuilder.add(showNisoImageMetadata(nisoArray[i])); + break; + case AESAUDIOMETADATA: + arrayBuilder.add(showAESAudioMetadata(aesArray[i])); + break; + case TEXTMDMETADATA: + arrayBuilder.add(showTextMDMetadata(textMDArray[i])); + break; + default: + arrayBuilder.add(""); + break; + } + } + return Json.createObjectBuilder().add(property.getName(), arrayBuilder); + } + + /* + * Output the textMD metadata, which is its own special kind of property. + */ + protected JsonObjectBuilder showTextMDMetadata(TextMDMetadata textMD) { + JsonObjectBuilder textmdBuilder = Json.createObjectBuilder(); + + String s = textMD.getCharset(); + if (s != null) { + textmdBuilder.add("textmd:charset", s); + } + if ((s = textMD.getByte_orderString()) != null) { + textmdBuilder.add("textmd:byte_order", s); + } + if ((s = textMD.getByte_size()) != null) { + textmdBuilder.add("textmd:byte_size", s); + } + if ((s = textMD.getCharacter_size()) != null) { + textmdBuilder.add("textmd:character_size", s); + } + if ((s = textMD.getLinebreakString()) != null) { + textmdBuilder.add("textmd:linebreak", s); + } + if ((s = textMD.getLanguage()) != null) { + textmdBuilder.add("textmd:language", s); + } + if ((s = textMD.getMarkup_basis()) != null) { + textmdBuilder.add("textmd:markup_basis", s); + } + if ((s = textMD.getMarkup_basis_version()) != null) { + textmdBuilder.add("textmd:markup_basis_version", s); + } + if ((s = textMD.getMarkup_language()) != null) { + textmdBuilder.add("textmd:markup_language", s); + } + if ((s = textMD.getMarkup_language_version()) != null) { + textmdBuilder.add("textmd:markup_language_version", s); + } + return textmdBuilder; + } + + /** + * Display the NISO image metadata formatted according to the MIX schema. The schema which is used + * may be 0.2 or 1.0 or 2.0, depending on the module parameters. + * + * @param niso NISO image metadata + */ + protected JsonObjectBuilder showNisoImageMetadata(NisoImageMetadata niso) { + if ("0.2".equals(_je.getMixVersion())) { + return null; // don't handle mix version 0.2 + } else if ("1.0".equals(_je.getMixVersion())) { + return showNisoImageMetadata(niso, true); + } else { + return showNisoImageMetadata(niso, false); + } + } + + /** Display the NISO image metadata formatted according to the MIX 1.0 schema. */ + protected JsonObjectBuilder showNisoImageMetadata(NisoImageMetadata niso, boolean bMix10) { + JsonObjectBuilder mixBuilder = Json.createObjectBuilder(); + + JsonObjectBuilder ob = showNisoBasicDigitalObjectInformation(niso, bMix10); + if (ob != null) { + mixBuilder.add("mix:BasicDigitalObjectInformation", ob); + } + ob = showNisoBasicImageInformation(niso, bMix10); + if (ob != null) { + mixBuilder.add("mix:BasicImageInformation", ob); + } + ob = showNisoImageCaptureMetadata(niso, bMix10); + if (ob != null) { + mixBuilder.add("mix:ImageCaptureMetadata", ob); + } + ob = showNisoImageAssessmentMetadata(niso, bMix10); + if (ob != null) { + mixBuilder.add("mix:ImageAssessmentMetadata", ob); + } + ob = showChangeHistory(niso, bMix10); + if (ob != null) { + mixBuilder.add("mix:ChangeHistory", ob); + } + return mixBuilder; + } + + /* + * The NISO Metadata output is split into multiple functions so that they're + * merely outrageously big rather than disgustingly big + */ + /* Top level element 1 of 5: BasicDigitalObjectInformation */ + protected JsonObjectBuilder showNisoBasicDigitalObjectInformation( + NisoImageMetadata niso, boolean bMix10) { + JsonObjectBuilder mixBuilder = Json.createObjectBuilder(); + + String s = niso.getImageIdentifier(); + if (s != null) { + mixBuilder.add( + "mix:ObjectIdentifier", + Json.createObjectBuilder() + .add("mix:objectIdentifierType", "JHOVE") + .add("mix:objectIdentifierValue", s)); + } + long ln = niso.getFileSize(); + if (ln != NisoImageMetadata.NULL) { + mixBuilder.add("mix:fileSize", ln); + } + if ((s = niso.getMimeType()) != null) { + mixBuilder.add("mix:formatName", s); + } + if ((s = niso.getByteOrder()) != null) { + // Convert strings to MIX 1.0 form + if (s.startsWith("big")) { + s = bMix10 ? "big_endian" : "big endian"; + } else if (s.startsWith("little")) { + s = bMix10 ? "little_endian" : "little endian"; + } + mixBuilder.add("mix:byteOrder", s); + } + int comp = niso.getCompressionScheme(); + int level = niso.getCompressionLevel(); + String compStr; + switch (comp) { + case 1: + compStr = "Uncompressed"; + break; + case 2: + compStr = "CCITT 1D"; + break; + case 3: + compStr = "Group 3 Fax"; + break; + case 4: + compStr = "Group 4 Fax"; + break; + case 5: + compStr = "LZW"; + break; + case 6: + compStr = "JPEG"; + break; + case 32773: + compStr = "PackBits"; + break; + case 34713: + compStr = "JPEG2000 Lossy"; + break; + case 34714: + compStr = "JPEG2000 Lossless"; + break; + default: + compStr = "Unknown"; + break; + } + if (comp != NisoImageMetadata.NULL) { + if (comp == 34713 || comp == 34714) { + mixBuilder.add("mix:compressionScheme", compressionSchemeToString(comp)); + if (level != NisoImageMetadata.NULL) { + mixBuilder.add("mix:compressionRatio", level); + } + } else if (bMix10) { + mixBuilder.add("mix:compressionScheme", Integer.toString(comp)); + } else { + mixBuilder.add("mix:compressionScheme", compStr); + } + } + int n = niso.getChecksumMethod(); + s = niso.getChecksumValue(); + if (n != NisoImageMetadata.NULL || s != null) { + if (n != NisoImageMetadata.NULL) { + mixBuilder.add("mix:messageDigestAlgorithm", n); + } + if (s != null) { + mixBuilder.add("mix:messageDigest", s); + } + } + return mixBuilder; + } + + /* 1.0, Top level element 2 of 5: BasicImageInformation */ + protected JsonObjectBuilder showNisoBasicImageInformation( + NisoImageMetadata niso, boolean bMix10) { + JsonObjectBuilder mixBuilder = Json.createObjectBuilder(); + boolean hasBuilder = false; + long ln = niso.getImageWidth(); + if (ln != NisoImageMetadata.NULL) { + mixBuilder.add("mix:imageWidth", ln); + hasBuilder = true; + } + ln = niso.getImageLength(); + if (ln != NisoImageMetadata.NULL) { + mixBuilder.add("mix:imageHeight", ln); + hasBuilder = true; + } + int n = niso.getColorSpace(); + if (n != NisoImageMetadata.NULL) { + if (bMix10) { + mixBuilder.add("mix:colorSpace", n); + } else { + mixBuilder.add("mix:colorSpace", photometricInterpretationToString(n)); + } + hasBuilder = true; + } + String s = niso.getProfileName(); + String s2 = niso.getProfileURL(); + if (s != null || s2 != null) { + JsonObjectBuilder iccBuilder = Json.createObjectBuilder(); + if (s != null) { + iccBuilder.add("mix:iccProfileName", s); + } + if (s2 != null) { + iccBuilder.add("mix:iccProfileURL", s2); + } + mixBuilder.add("mix:IccProfile", iccBuilder); + hasBuilder = true; + } + int[] iarray = niso.getYCbCrSubSampling(); + n = niso.getYCbCrPositioning(); + Rational[] rarray = niso.getYCbCrCoefficients(); + if (iarray != null || n != NisoImageMetadata.NULL || rarray != null) { + JsonObjectBuilder yccBuilder = Json.createObjectBuilder(); + if (iarray != null && iarray.length >= 2) { + yccBuilder.add( + "mix:YCbCrSubSampling", + Json.createObjectBuilder() + .add("mix:yCbCrSubsampleHoriz", iarray[0]) + .add("mix:yCbCrSubsampleVert", iarray[1])); + } + if (n != NisoImageMetadata.NULL) { + yccBuilder.add("mix:yCbCrPositioning", n); + } + if (rarray != null) { + if (bMix10) { + yccBuilder.add("mix:yCbCrCoefficients", showArray(rarray)); + } else { + yccBuilder.add( + "mix:yCbCrCoefficients", + Json.createObjectBuilder() + .add("mix:lumaRed", showRational(rarray[0])) + .add("mix:lumaGreen", showRational(rarray[1])) + .add("mix:lumaBlue", showRational(rarray[2]))); + } + } + mixBuilder.add("mix:YCbCr", yccBuilder); + hasBuilder = true; + } + rarray = niso.getReferenceBlackWhite(); + if (rarray != null) { + if (bMix10) { + mixBuilder.add("mix:referenceBlackWhite", showArray(rarray)); + } else { + JsonArrayBuilder aBuilder = Json.createArrayBuilder(); + for (int i = 0; i < rarray.length - 1; i += 2) { + JsonObjectBuilder cBuilder = Json.createObjectBuilder(); + // Tricky here. + // The reference BW might be given as either RGB or yCbCr. + String pi; + if (niso.getColorSpace() == 6) { // yCbCr + switch (i) { + case 0: + pi = "Y"; + break; + case 2: + pi = "Cb"; + break; + case 4: + default: + pi = "Cr"; + break; + } + } else { + switch (i) { // otherwise assume RGB + case 0: + pi = "R"; + break; + case 2: + pi = "G"; + break; + case 4: + default: + pi = "B"; + break; + } + } + cBuilder.add("mix:componentPhotometricInterpretation", pi); + cBuilder.add("mix:footroom", showRational(rarray[i])); + cBuilder.add("mix:headroom", showRational(rarray[i + 1])); + aBuilder.add(cBuilder); + } + mixBuilder.add("mix:ReferenceBlackWhite", aBuilder); + } + hasBuilder = true; + } + // SpecialFormatCharacteristics limited to JPEG2000 + int lay = niso.getJp2Layers(); + int lev = niso.getJp2ResolutionLevels(); + String sizTiles = niso.getJp2Tiles(); + if (sizTiles != null || lay != NisoImageMetadata.NULL || lev != NisoImageMetadata.NULL) { + JsonObjectBuilder jp2Builder = Json.createObjectBuilder(); + if (sizTiles != null) { + if (bMix10) { + jp2Builder.add("mix:tiles", sizTiles); + } else { + String[] sizes = sizTiles.split("x"); + jp2Builder.add("mix:tiles", showArray(sizes)); + } + } + if (lay != NisoImageMetadata.NULL) { + jp2Builder.add("mix:qualityLayers", lay); + } + if (lev != NisoImageMetadata.NULL) { + jp2Builder.add("mix:resolutionLevels", lev); + } + mixBuilder.add("mix:JPEG2000", jp2Builder); + hasBuilder = true; + } + + if (hasBuilder) { + return mixBuilder; + } else { + return null; + } + } + + /* 1.0, Top level element 3 of 5: ImageCaptureMetadata */ + protected JsonObjectBuilder showNisoImageCaptureMetadata(NisoImageMetadata niso, boolean bMix10) { + JsonObjectBuilder mixBuilder = Json.createObjectBuilder(); + boolean hasBuilder = false; + + String s = niso.getSourceType(); + if (s != null) { + mixBuilder.add("mix:sourceType", s); + hasBuilder = true; + } + s = niso.getSourceID(); + if (s != null) { + mixBuilder.add("mix:sourceIDValue", s); + hasBuilder = true; + } + double d = niso.getSourceXDimension(); + int n = niso.getSourceXDimensionUnit(); + if (d != NisoImageMetadata.NILL || n != NisoImageMetadata.NULL) { + // Assume that both X and Y exist, or neither + if (d != NisoImageMetadata.NILL) { + mixBuilder.add("mix:sourceXDimensionValue", d); + } + if (n != NisoImageMetadata.NULL) { + mixBuilder.add("mix:sourceXDimensionUnit", n); + } + d = niso.getSourceYDimension(); + n = niso.getSourceYDimensionUnit(); + if (d != NisoImageMetadata.NILL || n != NisoImageMetadata.NULL) { + if (d != NisoImageMetadata.NILL) { + mixBuilder.add("mix:sourceYDimensionValue", d); + } + if (n != NisoImageMetadata.NULL) { + mixBuilder.add("mix:sourceYDimensionUnit", n); + } + } + hasBuilder = true; + } + + s = niso.getDateTimeCreated(); + if (s != null) { + mixBuilder.add("mix:dateTimeCreated", s); + hasBuilder = true; + } + s = niso.getImageProducer(); + if (s != null) { + mixBuilder.add("mix:imageProducer", s); + hasBuilder = true; + } + + s = niso.getDeviceSource(); + if (s != null) { + mixBuilder.add("mix:captureDevice", s); + hasBuilder = true; + } + + // Here's a chunk of XML for scanners. + String mfg = niso.getScannerManufacturer(); + if (mfg != null) { + mixBuilder.add("mix:scannerManufacturer", mfg); + hasBuilder = true; + } + String model = niso.getScannerModelName(); + String modelNum = niso.getScannerModelNumber(); + String serNum = niso.getScannerModelSerialNo(); + if (model != null || modelNum != null || serNum != null) { + hasBuilder = true; + JsonObjectBuilder smBuilder = Json.createObjectBuilder(); + if (model != null) { + smBuilder.add("mix:scannerModelName", model); + } + if (modelNum != null) { + smBuilder.add("mix:scannerModelNumber", modelNum); + } + if (serNum != null) { + smBuilder.add("mix:scannerModelSerialNo", serNum); + } + mixBuilder.add("mix:ScannerModel", smBuilder); + } + double xres = niso.getXPhysScanResolution(); + double yres = niso.getYPhysScanResolution(); + if (xres != NisoImageMetadata.NULL && yres != NisoImageMetadata.NULL) { + double res = (xres > yres ? xres : yres); + if (bMix10) { + mixBuilder.add("mix:maximumOpticalResolution", res); + } else { + mixBuilder.add( + "mix:MaximumOpticalResolution", + Json.createObjectBuilder() + .add("mix:xOpticalResolution", xres) + .add("mix:yOpticalResolution", yres) + .add("mix:resolutionUnit", ".in")); + } + + hasBuilder = true; + } + s = niso.getScanningSoftware(); + if (s != null) { + hasBuilder = true; + mixBuilder.add("mix:scanningSoftwareName", s); + s = niso.getScanningSoftwareVersionNo(); + if (s != null) { + mixBuilder.add("mix:scanningSoftwareVersionNo", s); + } + } + + // Now we'll hear from the digital cameras. + s = niso.getDigitalCameraManufacturer(); + if (s != null) { + mixBuilder.add("mix:digitalCameraManufacturer", s); + hasBuilder = true; + } + String dcmodel = niso.getDigitalCameraModelName(); + String dcmodelNum = niso.getDigitalCameraModelNumber(); + String dcserNum = niso.getDigitalCameraModelSerialNo(); + if (dcmodel != null || dcmodelNum != null || dcserNum != null) { + hasBuilder = true; + JsonObjectBuilder smBuilder = Json.createObjectBuilder(); + if (dcmodel != null) { + smBuilder.add("mix:digitalCameraModelName", dcmodel); + } + if (dcmodelNum != null) { + smBuilder.add("mix:digitalCameraModelNumber", dcmodelNum); + } + if (dcserNum != null) { + smBuilder.add("mix:mix:digitalCameraModelSerialNo", dcserNum); + } + mixBuilder.add("mix:DigitalCameraModel", smBuilder); + } + + // Nest a buffer for CameraCaptureSettings + JsonObjectBuilder ccsBuilder = Json.createObjectBuilder(); + boolean useCcSetBuf = false; + d = niso.getFNumber(); + if (d != NisoImageMetadata.NULL) { + ccsBuilder.add("mix:fNumber", d); + useCcSetBuf = true; + } + d = niso.getExposureTime(); + if (d != NisoImageMetadata.NULL) { + ccsBuilder.add("mix:exposureTime", d); + useCcSetBuf = true; + } + n = niso.getExposureProgram(); + if (n != NisoImageMetadata.NULL) { + if (bMix10) { + ccsBuilder.add("mix:exposureProgram", n); + } else { + if (n > 8 || n < 0) { + n = 0; // force "Not defined" for bad value + } + ccsBuilder.add("mix:exposureProgram", NisoImageMetadata.EXPOSURE_PROGRAM[n]); + } + useCcSetBuf = true; + } + s = niso.getExifVersion(); + if (s != null) { + ccsBuilder.add("mix:exifVersion", s); + useCcSetBuf = true; + } + Rational r = niso.getBrightness(); + if (r != null) { + ccsBuilder.add("mix:brightnessValue", showRational(r)); + useCcSetBuf = true; + } + r = niso.getExposureBias(); + if (r != null) { + ccsBuilder.add("mix:exposureBiasValue", showRational(r)); + useCcSetBuf = true; + } + r = niso.getMaxApertureValue(); + if (r != null) { + ccsBuilder.add("mix:maxApertureValue", showRational(r)); + useCcSetBuf = true; + } + double[] darray = niso.getSubjectDistance(); + if (darray != null) { + ccsBuilder.add("mix:subjectDistance", showArray(darray)); + useCcSetBuf = true; + } + n = niso.getMeteringMode(); + if (n != NisoImageMetadata.NULL) { + if (bMix10) { + ccsBuilder.add("mix:meteringMode", n); + } else { + s = meteringModeToString(n); + if (s.startsWith("Center weighted")) { + s = "Center weighted Average"; + } + ccsBuilder.add("mix:MeteringMode", s); + } + useCcSetBuf = true; + } + n = niso.getFlash(); + if (n != NisoImageMetadata.NULL) { + // First bit (0 = Flash did not fire, 1 = Flash fired) + int firstBit = n & 1; + ccsBuilder.add("mix:flash", NisoImageMetadata.FLASH_20[firstBit]); + useCcSetBuf = true; + } + d = niso.getFocalLength(); + if (d != NisoImageMetadata.NULL) { + ccsBuilder.add("mix:focalLength", d); + useCcSetBuf = true; + } + r = niso.getFlashEnergy(); + if (r != null) { + ccsBuilder.add("mix:flashEnergy", showRational(r)); + useCcSetBuf = true; + } + n = niso.getBackLight(); + if (n != NisoImageMetadata.NULL) { + ccsBuilder.add("mix:backLight", n); + useCcSetBuf = true; + } + d = niso.getExposureIndex(); + if (d != NisoImageMetadata.NULL) { + ccsBuilder.add("mix:exposureIndex", d); + useCcSetBuf = true; + } + n = niso.getAutoFocus(); + if (n != NisoImageMetadata.NULL) { + ccsBuilder.add("mix:autoFocus", n); + useCcSetBuf = true; + } + d = niso.getXPrintAspectRatio(); + double d2 = niso.getYPrintAspectRatio(); + if (d != NisoImageMetadata.NULL) { + ccsBuilder.add("mix:xPrintAspectRatio", d); + useCcSetBuf = true; + } + if (d2 != NisoImageMetadata.NULL) { + ccsBuilder.add("mix:yPrintAspectRatio", d); + useCcSetBuf = true; + } + if (useCcSetBuf) { + mixBuilder.add("mix:CameraCaptureSettings", ccsBuilder); + + hasBuilder = true; + } + + n = niso.getOrientation(); + if (n != NisoImageMetadata.NULL) { + if (bMix10) { + mixBuilder.add("mix:orientation", n); + } else { + final String[] orient = { + "unknown", + "normal*", + "normal, image flipped", + "normal, rotated 180\u00B0", + "normal, image flipped, rotated 180\u00B0", + "normal, image flipped, rotated cw 90\u00B0", + "normal, rotated ccw 90\u00B0", + "normal, image flipped, rotated ccw 90\u00B0", + "normal, rotated cw 90\u00B0" + }; + if (n > 8 || n < 0) { + n = 0; // force "unknown" for bad value + } + mixBuilder.add("mix:orientation", orient[n]); + } + hasBuilder = true; + } + s = niso.getMethodology(); + if (s != null) { + mixBuilder.add("mix:methodology", s); + hasBuilder = true; + } + if (hasBuilder) { + return mixBuilder; + } else { + return null; + } + } + + /* 1.0, Top level element 4 of 5: ImageAssessmentMetadata */ + protected JsonObjectBuilder showNisoImageAssessmentMetadata( + NisoImageMetadata niso, boolean bMix10) { + JsonObjectBuilder mixBuilder = Json.createObjectBuilder(); + boolean hasBuilder = false; + + JsonObjectBuilder smBuilder = Json.createObjectBuilder(); + boolean useMetricsBuf = false; + + int n = niso.getSamplingFrequencyPlane(); + if (n != NisoImageMetadata.NULL) { + smBuilder.add("mix:samplingFrequencyPlane", n); + useMetricsBuf = true; + } + n = niso.getSamplingFrequencyUnit(); + if (n != NisoImageMetadata.NULL) { + if (bMix10) { + smBuilder.add("mix:samplingFrequencyUnit", n); + } else { + final String sfu[] = {null, "no absolute unit of measurement", "in.", "cm"}; + if (n < 1 || n > 3) { + n = 1; + } + smBuilder.add("mix:samplingFrequencyUnit", sfu[n]); + } + useMetricsBuf = true; + } + Rational r = niso.getXSamplingFrequency(); + if (r != null) { + smBuilder.add("mix:xSamplingFrequency", showRational(r)); + useMetricsBuf = true; + } + r = niso.getYSamplingFrequency(); + if (r != null) { + smBuilder.add("mix:ySamplingFrequency", showRational(r)); + useMetricsBuf = true; + } + if (useMetricsBuf) { + mixBuilder.add("mix:SpatialMetrics", smBuilder); + hasBuilder = true; + } + + JsonObjectBuilder imeBuilder = Json.createObjectBuilder(); + boolean useColorEncBuf = false; + + int[] iarray = niso.getBitsPerSample(); + if (iarray != null) { + imeBuilder.add("mix:bitsPerSample", showArray(iarray)); + imeBuilder.add("mix:bitsPerSampleUnit", "integer"); + // bitsPerSampleUnit can also be floating point. Don't ask me why. + useColorEncBuf = true; + } + n = niso.getSamplesPerPixel(); + if (n != NisoImageMetadata.NULL) { + imeBuilder.add("mix:samplesPerPixel", n); + useColorEncBuf = true; + } + + iarray = niso.getExtraSamples(); + if (iarray != null) { + // extraSamples must be limited to + // 0, 1, 2, or 3. + n = iarray[0]; + if (n >= 0 && n <= 3) { + if (bMix10) { + imeBuilder.add("mix:extraSamples", showArray(iarray)); + } else { + String[] sarray = new String[iarray.length]; + for (int ii = 0; ii < iarray.length; ii++) { + sarray[ii] = NisoImageMetadata.EXTRA_SAMPLE_20[iarray[ii]]; + imeBuilder.add("mix:extraSamples", showArray(sarray)); + } + } + useColorEncBuf = true; + } + } + + String s = niso.getColormapReference(); + if (s != null) { + imeBuilder.add("mix:colormapReference", s); + useColorEncBuf = true; + } + + // This is complete nonsense, but it's what the spec says + iarray = niso.getGrayResponseCurve(); + if (iarray != null) { + imeBuilder.add("mix:grayResponseCurve", showArray(iarray)); + useColorEncBuf = true; + } + + n = niso.getGrayResponseUnit(); + if (n != NisoImageMetadata.NULL) { + if (bMix10) { + imeBuilder.add("mix:grayResponseUnit", n); + } else if (n > 0 && n <= 5) { + // Convert integer to text value; only values 1-5 are legal + imeBuilder.add("mix:grayResponseUnit", NisoImageMetadata.GRAY_RESPONSE_UNIT_20[n - 1]); + } + useColorEncBuf = true; + } + + r = niso.getWhitePointXValue(); + Rational r2 = niso.getWhitePointYValue(); + if (r != null && r2 != null) { + imeBuilder.add( + "mix:WhitePoint", + Json.createObjectBuilder() + .add("mix:whitePointXValue", showRational(r)) + .add("mix:whitePointYValue", showRational(r2))); + useColorEncBuf = true; + } + + // A chromaticities buffer to go in the color encoding buffer. + JsonObjectBuilder pcBuilder = Json.createObjectBuilder(); + boolean useChromaBuf = false; + r = niso.getPrimaryChromaticitiesRedX(); + if (r != null) { + pcBuilder.add("mix:primaryChromaticitiesRedX", showRational(r)); + useChromaBuf = true; + } + r = niso.getPrimaryChromaticitiesRedY(); + if (r != null) { + pcBuilder.add("mix:primaryChromaticitiesRedY", showRational(r)); + useChromaBuf = true; + } + r = niso.getPrimaryChromaticitiesGreenX(); + if (r != null) { + pcBuilder.add("mix:primaryChromaticitiesGreenX", showRational(r)); + useChromaBuf = true; + } + r = niso.getPrimaryChromaticitiesGreenY(); + if (r != null) { + pcBuilder.add("mix:primaryChromaticitiesGreenY", showRational(r)); + useChromaBuf = true; + } + r = niso.getPrimaryChromaticitiesBlueX(); + if (r != null) { + pcBuilder.add("mix:primaryChromaticitiesBlueX", showRational(r)); + useChromaBuf = true; + } + r = niso.getPrimaryChromaticitiesBlueY(); + if (r != null) { + pcBuilder.add("mix:primaryChromaticitiesBlueY", showRational(r)); + useChromaBuf = true; + } + if (useChromaBuf) { + imeBuilder.add("mix:PrimaryChromaticities", pcBuilder); + useColorEncBuf = true; + } + + if (useColorEncBuf) { + mixBuilder.add("mix:ImageColorEncoding", imeBuilder); + hasBuilder = true; + } + + JsonObjectBuilder tdBuilder = Json.createObjectBuilder(); + boolean useTargetBuf = false; + n = niso.getTargetType(); + if (n != NisoImageMetadata.NULL) { + tdBuilder.add("mix:targetType", n); + useTargetBuf = true; + } + s = niso.getTargetIDManufacturer(); + if (s != null) { + tdBuilder.add("mix:targetManufacturer", s); + useTargetBuf = true; + } + s = niso.getTargetIDName(); + if (s != null) { + tdBuilder.add("mix:targetName", s); + useTargetBuf = true; + } + s = niso.getTargetIDNo(); + if (s != null) { + tdBuilder.add("mix:targetNo", s); + useTargetBuf = true; + } + s = niso.getTargetIDMedia(); + if (s != null) { + tdBuilder.add("mix:targetMedia", s); + useTargetBuf = true; + } + s = niso.getImageData(); + if (s != null) { + tdBuilder.add("mix:externalTarget", s); + useTargetBuf = true; + } + s = niso.getPerformanceData(); + if (s != null) { + tdBuilder.add("mix:performanceData", s); + useTargetBuf = true; + } + + if (useTargetBuf) { + mixBuilder.add("mix:TargetData", tdBuilder); + hasBuilder = true; + } + + if (hasBuilder) { + return mixBuilder; + } else { + return null; + } + } + + /* 1.0, Top level element 5 of 5: ChangeHistory (without time travel) */ + protected JsonObjectBuilder showChangeHistory(NisoImageMetadata niso, boolean bMix10) { + JsonObjectBuilder mixBuilder = Json.createObjectBuilder(); + boolean hasBuilder = false; + + String s = niso.getSourceData(); + if (s != null) { + mixBuilder.add("mix:sourceData", s); + hasBuilder = true; + } + s = niso.getProcessingAgency(); + if (s != null) { + mixBuilder.add("mix:processingAgency", s); + hasBuilder = true; + } + + JsonObjectBuilder psBuilder = Json.createObjectBuilder(); + boolean useSftwBuf = false; + s = niso.getProcessingSoftwareName(); + if (s != null) { + psBuilder.add("mix:processingSoftwareName", s); + useSftwBuf = true; + } + s = niso.getProcessingSoftwareVersion(); + if (s != null) { + psBuilder.add("mix:processingSoftwareVersion", s); + useSftwBuf = true; + } + s = niso.getOS(); + if (s != null) { + psBuilder.add("mix:processingOperatingSystemName", s); + useSftwBuf = true; + } + s = niso.getOSVersion(); + if (s != null) { + psBuilder.add("mix:processingOperatingSystemVersion", s); + useSftwBuf = true; + } + if (useSftwBuf) { + mixBuilder.add("mix:ProcessingSoftware", psBuilder); + hasBuilder = true; + } + String[] sarray = niso.getProcessingActions(); + if (sarray != null) { + mixBuilder.add("mix:processingActions", showArray(sarray)); + hasBuilder = true; + } + + if (hasBuilder) { + return mixBuilder; + } else { + return null; + } + } + + /** Convert the metering mode value to one of the suggested values for MIX 2.0 */ + private String meteringModeToString(int n) { + String s = NisoImageMetadata.METERING_MODE[1]; + if (n >= 1 && n <= 6) { + s = NisoImageMetadata.METERING_MODE[n]; + } + // Capitalize first letter + return s.substring(0, 1).toUpperCase(Locale.ROOT) + s.substring(1); + } + + /** + * Convert the color space value (which is based on the TIFF PhotometricInterpretation convention) + * to one of the suggested values for MIX 2.0 + */ + private String photometricInterpretationToString(int n) { + switch (n) { + case 0: + return "WhiteIsZero"; + case 1: + return "BlackIsZero"; + case 2: + return "RGB"; + case 3: + return "PaletteColor"; + case 4: + return "TransparencyMask"; + case 5: + return "CMYK"; + case 6: + return "YCbCr"; + case 8: + return "CIELab"; + case 9: + return "ICCLab"; + case 10: + return "ITULab"; + case 32803: + return "CFA"; + case 34892: + return "LinearRaw"; + default: + return "Unknown"; + } + } + + /** Convert compression scheme value (based on the TIFF compression convention) to a label */ + private String compressionSchemeToString(int n) { + for (int i = 0; i < NisoImageMetadata.COMPRESSION_SCHEME_INDEX.length; i++) { + if (n == NisoImageMetadata.COMPRESSION_SCHEME_INDEX[i]) + return NisoImageMetadata.COMPRESSION_SCHEME[i]; + } + return Integer.toString(n); + } + + /** + * Display the audio metadata formatted according to the AES schema. + * + * @param aes AES audio metadata + */ + protected JsonObjectBuilder showAESAudioMetadata(AESAudioMetadata aes) { + JsonObjectBuilder aesBuilder = Json.createObjectBuilder(); + + _sampleRate = aes.getSampleRate(); + + String s = aes.getAnalogDigitalFlag(); + if (s != null) { + aesBuilder.add("aes:analogDigitalFlag", s); + } + s = aes.getSchemaVersion(); + if (s != null) { + aesBuilder.add("aes:schemaVersion", s); + } + s = aes.getFormat(); + if (s != null) { + aesBuilder.add("aes:format", s); + } + s = aes.getSpecificationVersion(); + if (s != null) { + aesBuilder.add("aes:specificationVersion", s); + } + s = aes.getAppSpecificData(); + if (s != null) { + aesBuilder.add("aes:appSpecificData", s); + } + s = aes.getAudioDataEncoding(); + if (s != null) { + aesBuilder.add("aes:audioDataEncoding", s); + } + int in = aes.getByteOrder(); + if (in != AESAudioMetadata.NULL) { + aesBuilder.add( + "aes:byteOrder", (in == AESAudioMetadata.BIG_ENDIAN ? "BIG_ENDIAN" : "LITTLE_ENDIAN")); + } + long lin = aes.getFirstSampleOffset(); + if (lin != AESAudioMetadata.NULL) { + aesBuilder.add("aes:firstSampleOffset", lin); + } + String[] use = aes.getUse(); + if (use != null) { + aesBuilder.add( + "aes:use", + Json.createObjectBuilder().add("aes:useType", use[0]).add("aes:otherType", use[1])); + } + s = aes.getPrimaryIdentifier(); + if (s != null) { + String t = aes.getPrimaryIdentifierType(); + aesBuilder.add("aes:primaryIdentifier", s); + if (t != null) { + aesBuilder.add("aes:primaryIdentifierType", t); + } + } + List facelist = aes.getFaceList(); + if (facelist != null && !facelist.isEmpty()) { + // Add the face information, which is mostly filler. + AESAudioMetadata.Face f = facelist.get(0); + JsonObjectBuilder faceBuilder = Json.createObjectBuilder(); + + AESAudioMetadata.TimeDesc startTime = f.getStartTime(); + if (startTime != null) { + faceBuilder.add("aes:timeline", writeAESTimeRange(startTime, f.getDuration())); + } + int nchan = aes.getNumChannels(); + if (nchan != AESAudioMetadata.NULL) { + faceBuilder.add("aes:numChannels", nchan); + } + String[] locs = aes.getMapLocations(); + JsonArrayBuilder streamsBuilder = Json.createArrayBuilder(); + for (int ch = 0; ch < nchan; ch++) { + // write a stream description for each channel + streamsBuilder.add( + Json.createObjectBuilder().add("aes:channelNum", ch).add("aes:mapLocation", locs[ch])); + } + faceBuilder.add("aes:streams", streamsBuilder); + aesBuilder.add("aes:face", faceBuilder); + } + + // In the general case, a FormatList can contain multiple + // FormatRegions. This doesn't happen with any of the current + // modules; if it's needed in the future, simply set up an + // iteration loop on formatList. + List flist = aes.getFormatList(); + if (flist != null && !flist.isEmpty()) { + AESAudioMetadata.FormatRegion rgn = flist.get(0); + int bitDepth = rgn.getBitDepth(); + double sampleRate = rgn.getSampleRate(); + int wordSize = rgn.getWordSize(); + String[] bitRed = rgn.getBitrateReduction(); + // Build a FormatRegion subtree if at least one piece of data + // that goes into it is present. + JsonArrayBuilder formatListBuilder = Json.createArrayBuilder(); + JsonObjectBuilder formatRegionBuilder = Json.createObjectBuilder(); + if (bitDepth != AESAudioMetadata.NULL + || sampleRate != AESAudioMetadata.NILL + || wordSize != AESAudioMetadata.NULL) { + if (bitDepth != AESAudioMetadata.NULL) { + formatRegionBuilder.add("aes:bitDepth", bitDepth); + } + if (sampleRate != AESAudioMetadata.NILL) { + formatRegionBuilder.add("aes:sampleRate", sampleRate); + } + if (wordSize != AESAudioMetadata.NULL) { + formatRegionBuilder.add("aes:wordSize", wordSize); + } + if (bitRed != null) { + formatRegionBuilder.add( + "aes:bitrateReduction", + Json.createObjectBuilder() + .add("aes:codecName", bitRed[0]) + .add("aes:codecNameVersion", bitRed[1]) + .add("aes:codecCreatorApplication", bitRed[2]) + .add("aes:codecCreatorApplicationVersion", bitRed[3]) + .add("aes:codecQuality", bitRed[4]) + .add("aes:dataRate", bitRed[5]) + .add("aes:dataRateMode", bitRed[6])); + } + + formatListBuilder.add(formatRegionBuilder); + aesBuilder.add("aes:formatList", formatListBuilder); + } + } + return aesBuilder; + } + + /* + * Break out the writing of a timeRangeType element. This always gives a start + * time of 0. This is all FAKE DATA for the moment. + */ + private JsonObjectBuilder writeAESTimeRange( + AESAudioMetadata.TimeDesc start, AESAudioMetadata.TimeDesc duration) { + double sr = start.getSampleRate(); + if (sr == 1.0) { + sr = _sampleRate; + } + + JsonObjectBuilder timerangeBuilder = Json.createObjectBuilder(); + timerangeBuilder.add( + "tcf:startTime", + Json.createObjectBuilder() + .add("tcf:frameCount", 30) + .add("tcf:timeBase", 1000) + .add("tcf:videoField", "FIELD_1") + .add("tcf:countingMode", "NTSC_NON_DROP_FRAME") + .add("tcf:hours", start.getHours()) + .add("tcf:minutes", start.getMinutes()) + .add("tcf:seconds", start.getSeconds()) + .add("tcf:frames", start.getFrames()) + .add( + "tcf:samples", + Json.createObjectBuilder() + .add("tcf:sampleRate", "S" + Integer.toString((int) sr)) + .add("tcf:numberOfSamples", start.getSamples())) + .add( + "tcf:filmFraming", + Json.createObjectBuilder() + .add("tcf:framing", "NOT_APPLICABLE") + .add("tcf:framingType", "tcf:ntscFilmFramingType"))); + if (duration != null) { + sr = duration.getSampleRate(); + if (sr == 1.0) { + sr = _sampleRate; + } + timerangeBuilder.add( + "tcf:duration", + Json.createObjectBuilder() + .add("tcf:frameCount", 30) + .add("tcf:timeBase", 1000) + .add("tcf:videoField", "FIELD_1") + .add("tcf:countingMode", "NTSC_NON_DROP_FRAME") + .add("tcf:hours", duration.getHours()) + .add("tcf:minutes", duration.getMinutes()) + .add("tcf:seconds", duration.getSeconds()) + .add("tcf:frames", duration.getFrames()) + .add( + "tcf:samples", + Json.createObjectBuilder() + .add("tcf:sampleRate", "S" + Integer.toString((int) sr)) + .add("tcf:numberOfSamples", duration.getSamples())) + .add( + "tcf:filmFraming", + Json.createObjectBuilder() + .add("tcf:framing", "NOT_APPLICABLE") + .add("tcf:framingType", "tcf:ntscFilmFramingType"))); + } + return timerangeBuilder; + } + + protected JsonArrayBuilder showArray(int[] iarray) { + JsonArrayBuilder aBuilder = Json.createArrayBuilder(); + for (int i : iarray) { + aBuilder.add(i); + } + return aBuilder; + } + + protected JsonArrayBuilder showArray(double[] darray) { + JsonArrayBuilder dBuilder = Json.createArrayBuilder(); + for (double d : darray) { + dBuilder.add(d); + } + return dBuilder; + } + + protected JsonArrayBuilder showArray(String[] sarray) { + JsonArrayBuilder sBuilder = Json.createArrayBuilder(); + for (String s : sarray) { + if (s == null) { + sBuilder.addNull(); + } else { + sBuilder.add(s); + } + } + return sBuilder; + } + + protected JsonArrayBuilder showArray(Rational[] rarray) { + JsonArrayBuilder rBuilder = Json.createArrayBuilder(); + for (Rational r : rarray) { + if (r == null) { + rBuilder.addNull(); + } else { + rBuilder.add(showRational(r)); + } + } + return rBuilder; + } + + protected JsonArrayBuilder showRational(Rational r) { + JsonArrayBuilder rationalBuilder = Json.createArrayBuilder(); + long numer = r.getNumerator(); + long denom = r.getDenominator(); + rationalBuilder.add(numer); + rationalBuilder.add(denom); + return rationalBuilder; + } } diff --git a/jhove-core/src/main/java/edu/harvard/hul/ois/jhove/handler/TextHandler.java b/jhove-core/src/main/java/edu/harvard/hul/ois/jhove/handler/TextHandler.java index 66d605543..fcf88ab4d 100644 --- a/jhove-core/src/main/java/edu/harvard/hul/ois/jhove/handler/TextHandler.java +++ b/jhove-core/src/main/java/edu/harvard/hul/ois/jhove/handler/TextHandler.java @@ -1,33 +1,22 @@ -/********************************************************************** - * Jhove - JSTOR/Harvard Object Validation Environment - * Copyright 2003-2004 by JSTOR and the President and Fellows of Harvard College +/** + * ******************************************************************** Jhove - JSTOR/Harvard Object + * Validation Environment Copyright 2003-2004 by JSTOR and the President and Fellows of Harvard + * College * - * This program 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 2 of the License, or (at - * your option) any later version. + *

This program 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 2 + * of the License, or (at your option) any later version. * - * This program 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. + *

This program 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. * - * You should have received a copy of the GNU Lesser General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 - * USA - **********************************************************************/ - + *

You should have received a copy of the GNU Lesser General Public License along with this + * program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA ******************************************************************** + */ package edu.harvard.hul.ois.jhove.handler; -import java.text.NumberFormat; -import java.util.Date; -import java.util.Iterator; -import java.util.List; -import java.util.Locale; -import java.util.Map; -import java.util.Set; - import edu.harvard.hul.ois.jhove.AESAudioMetadata; import edu.harvard.hul.ois.jhove.Agent; import edu.harvard.hul.ois.jhove.App; @@ -49,1969 +38,1890 @@ import edu.harvard.hul.ois.jhove.SignatureType; import edu.harvard.hul.ois.jhove.TextMDMetadata; import edu.harvard.hul.ois.jhove.messages.JhoveMessages; +import java.text.NumberFormat; +import java.util.Date; +import java.util.Iterator; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.Set; -/** - * OutputHandler for plain text output. - */ +/** OutputHandler for plain text output. */ public class TextHandler extends HandlerBase { - /****************************************************************** - * PRIVATE CLASS FIELDS. - ******************************************************************/ - - private static final String NAME = "TEXT"; - private static final String RELEASE = "1.6"; - private static final int[] DATE = { 2018, 03, 29 }; - private static final String NOTE = "This is the default JHOVE output " - + "handler"; - private static final String RIGHTS = "Derived from software Copyright 2004-2011 " - + "by the President and Fellows of Harvard College. " - + "Version 1.6 release by Open Preservation Foundation. " - + "Released under the GNU Lesser General Public License."; - - private NumberFormat _format; - - /****************************************************************** - * PRIVATE INSTANCE FIELDS. - ******************************************************************/ - - /* Sample rate. */ - private double _sampleRate; - - /****************************************************************** - * CLASS CONSTRUCTOR. - ******************************************************************/ - - /** - * Creates a TextHandler. - */ - public TextHandler() { - super(NAME, RELEASE, DATE, NOTE, RIGHTS); - _vendor = Agent.harvardInstance(); - - _format = NumberFormat.getInstance(Locale.ROOT); - _format.setGroupingUsed(false); - _format.setMinimumFractionDigits(0); - } - - /****************************************************************** - * PUBLIC INSTANCE METHODS. - ******************************************************************/ - - /** - * Outputs minimal information about the application - */ - @Override - public void show() { - _level--; - } - - /** - * Outputs detailed information about the application, including - * configuration, available modules and handlers, etc. - */ - @Override - public void show(App app) { - String margin = getIndent(++_level); - - _writer.println(margin + "App:"); - _writer.println(margin + " API: " + app.getRelease() + ", " - + HandlerBase.date.format(_je.getDate())); - String configFile = _je.getConfigFile(); - if (configFile != null) { - _writer.println(margin + " Configuration: " + configFile); - } - String s = _je.getSaxClass(); - if (s != null) { - _writer.println(margin + " SAXparser: " + s); - } - s = _je.getJhoveHome(); - if (s != null) { - _writer.println(margin + " JhoveHome: " + s); - } - s = _je.getEncoding(); - if (s != null) { - _writer.println(margin + " Encoding: " + s); - } - s = _je.getTempDirectory(); - if (s != null) { - _writer.println(margin + " TempDirectory: " + s); - } - _writer.println(margin + " BufferSize: " + _je.getBufferSize()); - Iterator iter = _je.getModuleMap().keySet().iterator(); - while (iter.hasNext()) { - Module module = _je.getModule(iter.next()); - _writer.println(margin + " Module: " + module.getName() + " " - + module.getRelease()); - } - iter = _je.getHandlerMap().keySet().iterator(); - while (iter.hasNext()) { - OutputHandler handler = _je.getHandler(iter.next()); - _writer.println(margin + " OutputHandler: " + handler.getName() - + " " + handler.getRelease()); - } - - _writer.println(margin + " Usage: " + app.getUsage()); - _writer.println(margin + " Rights: " + app.getRights()); - - _level--; - } - - /** - * Outputs information about the OutputHandler specified in the parameter - */ - @Override - public void show(OutputHandler handler) { - String margin = getIndent(++_level); - - _writer.println(margin + "Handler: " + handler.getName()); - _writer.println(margin + " Release: " + handler.getRelease()); - _writer.println(margin + " Date: " - + HandlerBase.date.format(handler.getDate())); - List list = handler.getSpecification(); - int n = list.size(); - for (int i = 0; i < n; i++) { - showDocument(list.get(i), "Specification"); - } - Agent vendor = handler.getVendor(); - if (vendor != null) { - showAgent(vendor, "Vendor"); - } - String s; - if ((s = handler.getNote()) != null) { - _writer.println(margin + " Note: " + s); - } - if ((s = handler.getRights()) != null) { - _writer.println(margin + " Rights: " + s); - } - } - - /** - * Outputs information about a Module - */ - @Override - public void show(Module module) { - String margin = getIndent(++_level); - - _writer.println(margin + "Module: " + module.getName()); - _writer.println(margin + " Release: " + module.getRelease()); - _writer.println(margin + " Date: " - + HandlerBase.date.format(module.getDate())); - String[] ss = module.getFormat(); - if (ss.length > 0) { - _writer.print(margin + " Format: " + ss[0]); - for (int i = 1; i < ss.length; i++) { - _writer.print(", " + ss[i]); - } - _writer.println(); - } - String s = module.getCoverage(); - if (s != null) { - _writer.println(margin + " Coverage: " + s); - } - ss = module.getMimeType(); - if (ss.length > 0) { - _writer.print(margin + " MIMEtype: " + ss[0]); - for (int i = 1; i < ss.length; i++) { - _writer.print(", " + ss[i]); - } - ; - _writer.println(); - } - for (Signature sig : module.getSignature()) { - showSignature(sig); - } - for (Document spec : module.getSpecification()) { - showDocument(spec, "Specification"); - } - for (String feature : module.getFeatures()) { - _writer.println(margin + " Feature: " + feature); - } - - _writer.println(margin + " Methodology:"); - if ((s = module.getWellFormedNote()) != null) { - _writer.println(margin + " Well-formed: " + s); - } - if ((s = module.getValidityNote()) != null) { - _writer.println(margin + " Validity: " + s); - } - if ((s = module.getRepInfoNote()) != null) { - _writer.println(margin + " RepresentationInformation: " + s); - } - Agent vendor = module.getVendor(); - if (vendor != null) { - showAgent(vendor, "Vendor"); - } - if ((s = module.getNote()) != null) { - _writer.println(margin + " Note: " + s); - } - if ((s = module.getRights()) != null) { - _writer.println(margin + " Rights: " + s); - } - - _level--; - } - - /** - * Outputs the information contained in a RepInfo object - */ - @Override - public void show(RepInfo info) { - String margin = getIndent(++_level); - - Module module = info.getModule(); - _writer.println(margin + "RepresentationInformation: " + info.getUri()); - if (module != null) { - _writer.println(margin + " ReportingModule: " + module.getName() - + ", Rel. " + module.getRelease() + " (" - + date.format(module.getDate()) + ")"); - } - - Date date = info.getCreated(); - if (date != null) { - _writer.println(margin + " Created: " + dateTime.format(date)); - } - date = info.getLastModified(); - if (date != null) { - _writer.println(margin + " LastModified: " + dateTime.format(date)); - } - long size = info.getSize(); - if (size > -1) { - _writer.println(margin + " Size: " + size); - } - String s = info.getFormat(); - if (s != null) { - _writer.println(margin + " Format: " + s); - } - s = info.getVersion(); - if (s != null) { - _writer.println(margin + " Version: " + s); - } - if (!_je.getSignatureFlag()) { - _writer.print(margin + " Status: "); - switch (info.getWellFormed()) { - case RepInfo.TRUE: - s = "Well-Formed"; - break; - - case RepInfo.FALSE: - s = "Not well-formed"; - break; - - default: - s = "Unknown"; - break; - } - if (info.getWellFormed() == RepInfo.TRUE) { - switch (info.getValid()) { - - case RepInfo.TRUE: - s += " and valid"; - break; - - case RepInfo.FALSE: - s += ", but not valid"; - break; - - // case UNDETERMINED: add nothing - } - } - _writer.println(s); - } else { - // If we aren't checking signatures, we still need to say something. - _writer.print(margin + " Status: "); - switch (info.getWellFormed()) { - case RepInfo.TRUE: - s = "Well-Formed"; - break; - - default: - s = "Not well-formed"; - break; - } - _writer.println(s); - } - List list1 = info.getSigMatch(); - int n = list1.size(); - if (n > 0) { - _writer.println(margin + " SignatureMatches:"); - for (int i = 0; i < n; i++) { - _writer.println(margin + " " + list1.get(i)); - } - } - - List list2 = info.getMessage(); - n = list2.size(); - for (int i = 0; i < n; i++) { - showMessage(list2.get(i)); - } - s = info.getMimeType(); - if (s != null) { - _writer.println(margin + " MIMEtype: " + s); - } - - List list3 = info.getProfile(); - n = list3.size(); - if (n > 0) { - _writer.print(margin + " Profile: " + list3.get(0)); - for (int i = 1; i < n; i++) { - _writer.print(", " + list3.get(i)); - } - _writer.println(); - } - - Map map = info.getProperty(); - if (map != null) { - Iterator iter = map.keySet().iterator(); - while (iter.hasNext()) { - String key = iter.next(); - showProperty(info.getProperty(key), key, margin); - } - } - - List list4 = info.getChecksum(); - n = list4.size(); - for (int i = 0; i < n; i++) { - showChecksum((Checksum) list4.get(i)); - } - - _level--; - } - - /****************************************************************** - * PRIVATE INSTANCE METHODS. - ******************************************************************/ - - private void showAgent(Agent agent, String label) { - String margin = getIndent(++_level); - - _writer.println(margin + label + ": " + agent.getName()); - _writer.println(margin + " Type: " + agent.getType().toString()); - String s = agent.getAddress(); - if (s != null) { - _writer.println(margin + " Address: " + s); - } - if ((s = agent.getTelephone()) != null) { - _writer.println(margin + " Telephone: " + s); - } - if ((s = agent.getFax()) != null) { - _writer.println(margin + " Fax: " + s); - } - if ((s = agent.getEmail()) != null) { - _writer.println(margin + " Email: " + s); - } - if ((s = agent.getWeb()) != null) { - _writer.println(margin + " Web: " + s); - } - _level--; - } - - private void showChecksum(Checksum checksum) { - String margin = getIndent(++_level); - - _writer.println(margin + "Checksum: " + checksum.getValue()); - _writer.println(margin + " Type: " + checksum.getType().toString()); - - _level--; - } - - private void showDocument(Document document, String label) { - String margin = getIndent(++_level); - - _writer.println(margin + label + ": " + document.getTitle()); - _writer.println(margin + " Type: " + document.getType()); - List list1 = document.getAuthor(); - int n = list1.size(); - for (int i = 0; i < n; i++) { - showAgent(list1.get(i), "Author"); - } - List list2 = document.getPublisher(); - n = list2.size(); - for (int i = 0; i < n; i++) { - showAgent(list2.get(i), "Publisher"); - } - String s = document.getEdition(); - if (s != null) { - _writer.println(margin + " Edition: " + s); - } - if ((s = document.getDate()) != null) { - _writer.println(margin + " Date: " + s); - } - if ((s = document.getEnumeration()) != null) { - _writer.println(margin + " Enumeration: " + s); - } - if ((s = document.getPages()) != null) { - _writer.println(margin + " Pages: " + s); - } - List list3 = document.getIdentifier(); - n = list3.size(); - for (int i = 0; i < n; i++) { - showIdentifier(list3.get(i)); - } - if ((s = document.getNote()) != null) { - _writer.println(margin + " Note: " + s); - } - _level--; - } - - /** - * Do the final output. This should be in a suitable format for including - * multiple files between the header and the footer. - */ - @Override - public void showFooter() { - _level--; - - _writer.flush(); - } - - /** - * Do the initial output. This should be in a suitable format for including - * multiple files between the header and the footer. - */ - @Override - public void showHeader() { - String margin = getIndent(++_level); - - _writer.println(margin + _app.getName() + " (Rel. " + _app.getRelease() - + ", " + HandlerBase.date.format(_app.getDate()) + ")"); - _writer.println(margin + " Date: " - + HandlerBase.dateTime.format(new Date())); - } - - private void showIdentifier(Identifier identifier) { - String margin = getIndent(++_level); - - _writer.println(margin + "Identifier: " + identifier.getValue()); - _writer.println(margin + " Type: " + identifier.getType().toString()); - String note = identifier.getNote(); - if (note != null) { - _writer.println(margin + " Note: " + note); - } - _level--; - } - - private void showMessage(Message message) { - String margin = getIndent(++_level); - String prefix = message.getPrefix() + "Message: "; - String str = message.getMessage(); - // Append submessage, if any, after a colon. - String submsg = message.getSubMessage(); - if (submsg != null) { - str += ": " + submsg; - } - _writer.println(margin + prefix + str); - String id = message.getId(); - if (!(id == null || id.isEmpty() || id.equals(JhoveMessages.NO_ID))) { - _writer.println(margin + " ID: " + id); - } - long offset = message.getOffset(); - if (offset > -1) { - _writer.println(margin + " Offset: " + offset); - } - _level--; - } - - private void showSignature(Signature signature) { - String margin = getIndent(++_level); - - String sigValue; - if (signature.isStringValue()) { - sigValue = signature.getValueString(); - } else { - sigValue = signature.getValueHexString(); - } - _writer.println(margin + signature.getType().toString() + ": " - + sigValue); - if ((signature.getType().equals(SignatureType.MAGIC)) - && (((InternalSignature) signature).hasFixedOffset())) { - _writer.println(margin + " Offset: " - + ((InternalSignature) signature).getOffset()); - } - String note = signature.getNote(); - if (note != null) { - _writer.println(margin + " Note: " + note); - } - String use = signature.getUse().toString(); - if (use != null) { - _writer.println(margin + " Use: " + use); - } - _level--; - } - - /* showProperty may be called recursively. */ - private void showProperty(Property property, String key, String margin) { - PropertyArity arity = property.getArity(); - - if (key == null) { - _writer.print(margin + " "); - } else { - _writer.print(margin + " " + key + ": "); - } - if (arity.equals(PropertyArity.SCALAR)) { - showScalarProperty(property, margin); - } else if (arity.equals(PropertyArity.LIST)) { - showListProperty(property, margin); - } else if (arity.equals(PropertyArity.MAP)) { - showMapProperty(property, margin); - } else if (arity.equals(PropertyArity.SET)) { - showSetProperty(property, margin); - } else if (arity.equals(PropertyArity.ARRAY)) { - showArrayProperty(property, margin); - } else { - _writer.println(); - } - } - - private void showScalarProperty(Property property, String margin) { - PropertyType type = property.getType(); - if (PropertyType.PROPERTY.equals(type)) { - _writer.println(); - Property prop = (Property) property.getValue(); - showProperty(prop, prop.getName(), margin + " "); - // _writer.println (); // Does this improve things? - } else if (PropertyType.NISOIMAGEMETADATA.equals(type)) { - showNisoImageMetadata((NisoImageMetadata) property.getValue(), - margin + " ", _je.getShowRawFlag()); - } else if (PropertyType.AESAUDIOMETADATA.equals(type)) { - showAESAudioMetadata((AESAudioMetadata) property.getValue(), margin - + " ", _je.getShowRawFlag()); - } else if (PropertyType.TEXTMDMETADATA.equals(type)) { - showTextMDMetadata((TextMDMetadata) property.getValue(), margin - + " ", _je.getShowRawFlag()); - } else { - _writer.println(property.getValue().toString()); - } - } - - private void showListProperty(Property property, String margin) { - PropertyType type = property.getType(); - boolean valueIsProperty = PropertyType.PROPERTY.equals(type); - boolean valueIsNiso = PropertyType.NISOIMAGEMETADATA.equals(type); - boolean valueIsTextMD = PropertyType.TEXTMDMETADATA.equals(type); - - List list = (List) property.getValue(); - - int n = list.size(); - int i; - if (n > 0) { - // Put a blank line after the name of the property list. - if (valueIsProperty) { - _writer.println(); - } - for (i = 0; i < n; i++) { - if (valueIsProperty) { - Property pval = (Property) list.get(i); - showProperty(pval, pval.getName(), margin + " "); - } else if (valueIsNiso) { - showNisoImageMetadata((NisoImageMetadata) list.get(i), - margin + " ", _je.getShowRawFlag()); - } else if (valueIsTextMD) { - showTextMDMetadata((TextMDMetadata) list.get(i), margin - + " ", _je.getShowRawFlag()); - } else { - Object val = list.get(i); - if (i == 0) { - _writer.print(val); - } else { - _writer.print(", " + val); - } - } - } - } - if (!valueIsProperty || n == 0) { - _writer.println(); - } - } - - private void showMapProperty(Property property, String margin) { - /* - * Map output looks like key : mapkey1 / mapval1, mapkey2 / mapval2, ... - */ - PropertyType type = property.getType(); - boolean valueIsProperty = PropertyType.PROPERTY.equals(type); - boolean valueIsNiso = PropertyType.NISOIMAGEMETADATA.equals(type); - boolean valueIsTextMD = PropertyType.TEXTMDMETADATA.equals(type); - - Map propmap = (Map) property.getValue(); - Set keys = propmap.keySet(); - Iterator propiter = keys.iterator(); - while (propiter.hasNext()) { - Object propkey = propiter.next(); - Object val = propmap.get(propkey); - if (valueIsProperty) { - Property pval = (Property) val; - showProperty(pval, pval.getName(), margin + " "); - String propkeyStr = propkey.toString(); - if (!(pval.getName().equals(propkeyStr))) { - _writer.println(" Key: " + propkeyStr); - } - } else if (valueIsNiso) { - showNisoImageMetadata((NisoImageMetadata) val, margin + " ", - _je.getShowRawFlag()); - } else if (valueIsTextMD) { - showTextMDMetadata((TextMDMetadata) val, margin + " ", - _je.getShowRawFlag()); - } else { - _writer.println(" " + val.toString()); - _writer.println(" Key: " + propkey.toString()); - } - } - } - - private void showSetProperty(Property property, String margin) { - PropertyType type = property.getType(); - boolean valueIsProperty = PropertyType.PROPERTY.equals(type); - boolean valueIsNiso = PropertyType.NISOIMAGEMETADATA.equals(type); - boolean valueIsTextMD = PropertyType.TEXTMDMETADATA.equals(type); - - Set propset = (Set) property.getValue(); - Iterator propiter = propset.iterator(); - boolean first = true; - while (propiter.hasNext()) { - Object val = propiter.next(); - if (valueIsProperty) { - Property pval = (Property) val; - showProperty(pval, pval.getName(), margin + " "); - } else if (valueIsNiso) { - showNisoImageMetadata((NisoImageMetadata) val, margin + " ", - _je.getShowRawFlag()); - } else if (valueIsTextMD) { - showTextMDMetadata((TextMDMetadata) val, margin + " ", - _je.getShowRawFlag()); - } else { - if (first) { - _writer.print(val.toString()); - first = false; - } else { - _writer.print(", " + val.toString()); - } - } - } - _writer.println(); - } - - private void showArrayProperty(Property property, String margin) { - boolean[] boolArray = null; - byte[] byteArray = null; - char[] charArray = null; - java.util.Date[] dateArray = null; - double[] doubleArray = null; - float[] floatArray = null; - int[] intArray = null; - long[] longArray = null; - Object[] objArray = null; - Property[] propArray = null; - short[] shortArray = null; - String[] stringArray = null; - Rational[] rationalArray = null; - NisoImageMetadata[] nisoArray = null; - TextMDMetadata[] textMDArray = null; - int n = 0; - - PropertyType propType = property.getType(); - if (PropertyType.BOOLEAN.equals(propType)) { - boolArray = (boolean[]) property.getValue(); - n = boolArray.length; - } else if (PropertyType.BYTE.equals(propType)) { - byteArray = (byte[]) property.getValue(); - n = byteArray.length; - } else if (PropertyType.CHARACTER.equals(propType)) { - charArray = (char[]) property.getValue(); - n = charArray.length; - } else if (PropertyType.DATE.equals(propType)) { - dateArray = (java.util.Date[]) property.getValue(); - n = dateArray.length; - } else if (PropertyType.DOUBLE.equals(propType)) { - doubleArray = (double[]) property.getValue(); - n = doubleArray.length; - } else if (PropertyType.FLOAT.equals(propType)) { - floatArray = (float[]) property.getValue(); - n = floatArray.length; - } else if (PropertyType.INTEGER.equals(propType)) { - intArray = (int[]) property.getValue(); - n = intArray.length; - } else if (PropertyType.LONG.equals(propType)) { - longArray = (long[]) property.getValue(); - n = longArray.length; - } else if (PropertyType.OBJECT.equals(propType)) { - objArray = (Object[]) property.getValue(); - n = objArray.length; - } else if (PropertyType.SHORT.equals(propType)) { - shortArray = (short[]) property.getValue(); - n = shortArray.length; - } else if (PropertyType.STRING.equals(propType)) { - stringArray = (String[]) property.getValue(); - n = stringArray.length; - } else if (PropertyType.RATIONAL.equals(propType)) { - rationalArray = (Rational[]) property.getValue(); - n = rationalArray.length; - } else if (PropertyType.PROPERTY.equals(propType)) { - propArray = (Property[]) property.getValue(); - n = propArray.length; - } else if (PropertyType.NISOIMAGEMETADATA.equals(propType)) { - nisoArray = (NisoImageMetadata[]) property.getValue(); - n = nisoArray.length; - } else if (PropertyType.TEXTMDMETADATA.equals(propType)) { - textMDArray = (TextMDMetadata[]) property.getValue(); - n = textMDArray.length; - } - - for (int i = 0; i < n; i++) { - String elem; - if (PropertyType.BOOLEAN.equals(propType)) { - elem = String.valueOf(boolArray[i]); - } else if (PropertyType.BYTE.equals(propType)) { - elem = String.valueOf(byteArray[i]); - } else if (PropertyType.CHARACTER.equals(propType)) { - elem = String.valueOf(charArray[i]); - } else if (PropertyType.DATE.equals(propType)) { - elem = dateArray[i].toString(); - } else if (PropertyType.DOUBLE.equals(propType)) { - elem = String.valueOf(doubleArray[i]); - } else if (PropertyType.FLOAT.equals(propType)) { - elem = String.valueOf(floatArray[i]); - } else if (PropertyType.INTEGER.equals(propType)) { - elem = String.valueOf(intArray[i]); - } else if (PropertyType.LONG.equals(propType)) { - elem = String.valueOf(longArray[i]); - } else if (PropertyType.OBJECT.equals(propType)) { - elem = objArray[i].toString(); - } else if (PropertyType.SHORT.equals(propType)) { - elem = String.valueOf(shortArray[i]); - } else if (PropertyType.STRING.equals(propType)) { - elem = stringArray[i]; - } else if (PropertyType.RATIONAL.equals(propType)) { - elem = rationalArray[i].toString(); - } else if (PropertyType.NISOIMAGEMETADATA.equals(propType)) { - if (i == 0) { - _writer.println(); - } - NisoImageMetadata niso = nisoArray[i]; - showNisoImageMetadata(niso, margin + " ", _je.getShowRawFlag()); - continue; - } else if (PropertyType.TEXTMDMETADATA.equals(propType)) { - if (i == 0) { - _writer.println(); - } - showTextMDMetadata(textMDArray[i], margin + " ", - _je.getShowRawFlag()); - continue; - } else if (PropertyType.PROPERTY.equals(propType)) { - if (i == 0) { - _writer.println(); - } - Property pval = propArray[i]; - showProperty(pval, pval.getName(), margin + " "); - continue; - } else - elem = ""; - if (i == 0) { - _writer.print(elem); - } else { - _writer.print(", " + elem); - } - } - if (propType != PropertyType.PROPERTY - && propType != PropertyType.NISOIMAGEMETADATA) { - _writer.println(); - } - } - - /* - * Output the textMD metadata, which is its own special kind of property. - */ - private void showTextMDMetadata(TextMDMetadata textMD, String margin, - boolean rawOutput) { - String margn2 = margin + " "; - String margn3 = margn2 + " "; - - _writer.println(); - _writer.println(margn2 + "Character_info:"); - String s = textMD.getCharset(); - if (s != null) { - _writer.println(margn3 + "Charset: " + s); - } - if ((s = textMD.getByte_orderString()) != null) { - _writer.println(margn3 + "Byte_order: " + s); - } - if ((s = textMD.getByte_size()) != null) { - _writer.println(margn3 + "Byte_size: " + s); - } - if ((s = textMD.getCharacter_size()) != null) { - _writer.println(margn3 + "Character_size: " + s); - } - if ((s = textMD.getLinebreakString()) != null) { - _writer.println(margn3 + "Linebreak: " + s); - } - - if ((s = textMD.getLanguage()) != null) { - _writer.println(margn2 + "Language: " + s); - } - if ((s = textMD.getMarkup_basis()) != null) { - _writer.println(margn2 + "Markup_basis: " + s); - } - if ((s = textMD.getMarkup_basis_version()) != null) { - _writer.println(margn2 + "Markup_basis_version: " + s); - } - if ((s = textMD.getMarkup_language()) != null) { - _writer.println(margn2 + "Markup_language: " + s); - } - if ((s = textMD.getMarkup_language_version()) != null) { - _writer.println(margn2 + "Markup_language_version: " + s); - } - } - - /* - * Output the AES audio metadata, which is its own special kind of property. - */ - private void showAESAudioMetadata(AESAudioMetadata aes, String margin, - boolean rawOutput) { - String margn2 = margin + " "; - String margn3 = margn2 + " "; - String margn4 = margn3 + " "; - String margn5 = margn4 + " "; - - _sampleRate = aes.getSampleRate(); - - _writer.println(); - String s = aes.getAnalogDigitalFlag(); - if (s != null) { - _writer.println(margn2 + "AnalogDigitalFlag: " + s); - } - s = aes.getSchemaVersion(); - if (s != null) { - _writer.println(margn2 + "SchemaVersion: " + s); - } - s = aes.getFormat(); - if (s != null) { - _writer.println(margn2 + "Format: " + s); - } - s = aes.getSpecificationVersion(); - if (s != null) { - _writer.println(margn2 + "SpecificationVersion: " + s); - } - s = aes.getAppSpecificData(); - if (s != null) { - _writer.println(margn2 + "AppSpecificData: " + s); - } - s = aes.getAudioDataEncoding(); - if (s != null) { - _writer.println(margn2 + "AudioDataEncoding: " + s); - } - int in = aes.getByteOrder(); - if (in != AESAudioMetadata.NULL) { - _writer.println(margn2 - + "ByteOrder: " - + (in == AESAudioMetadata.BIG_ENDIAN ? "BIG_ENDIAN" - : "LITTLE_ENDIAN")); - } - long lin = aes.getFirstSampleOffset(); - if (lin != AESAudioMetadata.NULL) { - _writer.println(margn2 + "FirstSampleOffset: " + Long.toString(lin)); - } - String[] use = aes.getUse(); - if (use != null) { - _writer.println(margn2 + "Use:"); - _writer.println(margn3 + "UseType: " + use[0]); - _writer.println(margn3 + "OtherType: " + use[1]); - } - s = aes.getPrimaryIdentifier(); - if (s != null) { - String t = aes.getPrimaryIdentifierType(); - _writer.println(margn2 + "PrimaryIdentifier: " + s); - if (t != null) { - _writer.println(margn3 + "IdentifierType: " + t); - } - } - List facelist = aes.getFaceList(); - if (!facelist.isEmpty()) { - // Add the face information, which is mostly filler. - AESAudioMetadata.Face f = (AESAudioMetadata.Face) facelist.get(0); - _writer.println(margn2 + "Face: "); - _writer.println(margn3 + "TimeLine: "); - AESAudioMetadata.TimeDesc startTime = f.getStartTime(); - if (startTime != null) { - writeAESTimeRange(margn3, startTime, f.getDuration()); - } - int nchan = aes.getNumChannels(); - if (nchan != AESAudioMetadata.NULL) { - _writer.println(margn4 + "NumChannels: " - + Integer.toString(nchan)); - } - String[] locs = aes.getMapLocations(); - for (int ch = 0; ch < nchan; ch++) { - // write a stream description for each channel - _writer.println(margn4 + "Stream:"); - _writer.println(margn5 + "ChannelNum: " + Integer.toString(ch)); - _writer.println(margn5 + "ChannelAssignment: " + locs[ch]); - } - } - - // In the general case, a FormatList can contain multiple - // FormatRegions. This doesn't happen with any of the current - // modules; if it's needed in the future, simply set up an - // iteration loop on formatList. - List flist = aes.getFormatList(); - if (!flist.isEmpty()) { - AESAudioMetadata.FormatRegion rgn = (AESAudioMetadata.FormatRegion) flist - .get(0); - int bitDepth = rgn.getBitDepth(); - double sampleRate = rgn.getSampleRate(); - int wordSize = rgn.getWordSize(); - String[] bitRed = rgn.getBitrateReduction(); - // Build a FormatRegion subtree if at least one piece of data - // that goes into it is present. - if (bitDepth != AESAudioMetadata.NULL - || sampleRate != AESAudioMetadata.NILL - || wordSize != AESAudioMetadata.NULL) { - _writer.println(margn2 + "FormatList:"); - _writer.println(margn3 + "FormatRegion:"); - if (bitDepth != AESAudioMetadata.NULL) { - _writer.println(margn4 + "BitDepth: " - + Integer.toString(bitDepth)); - } - if (sampleRate != AESAudioMetadata.NILL) { - _writer.println(margn4 + "SampleRate: " - + Double.toString(sampleRate)); - } - if (wordSize != AESAudioMetadata.NULL) { - _writer.println(margn4 + "WordSize: " - + Integer.toString(wordSize)); - } - if (bitRed != null) { - _writer.println(margn4 + "BitrateReduction"); - _writer.println(margn5 + "CodecName: " + bitRed[0]); - _writer.println(margn5 + "codecNameVersion: " + bitRed[1]); - _writer.println(margn5 + "codecCreatorApplication: " - + bitRed[2]); - _writer.println(margn5 + "codecCreatorApplicationVersion: " - + bitRed[3]); - _writer.println(margn5 + "codecQuality: " + bitRed[4]); - _writer.println(margn5 + "dataRate: " + bitRed[5]); - _writer.println(margn5 + "dataRateMode: " + bitRed[6]); - } - } - } - } - - /* start must be non-null, but duration may be null */ - private void writeAESTimeRange(String baseIndent, - AESAudioMetadata.TimeDesc start, AESAudioMetadata.TimeDesc duration) { - final String margn1 = baseIndent + " "; - final String margn2 = margn1 + " "; - final String margn3 = margn2 + " "; - _writer.println(margn1 + "StartTime:"); - _writer.println(margn2 + "FrameCount: 30"); - _writer.println(margn2 + "TimeBase: 1000"); - _writer.println(margn2 + "VideoField: FIELD_1"); - _writer.println(margn2 + "CountingMode: NTSC_NON_DROP_FRAME"); - _writer.println(margn2 + "Hours: " + Long.toString(start.getHours())); - _writer.println(margn2 + "Minutes: " - + Long.toString(start.getMinutes())); - _writer.println(margn2 + "Seconds: " - + Long.toString(start.getSeconds())); - _writer.println(margn2 + "Frames: " + Long.toString(start.getFrames())); - _writer.println(margn2 + "Samples: "); - double sr = start.getSampleRate(); - if (sr == 1.0) { - sr = _sampleRate; - } - _writer.println(margn3 + "SampleRate: S" + Integer.toString((int) sr)); - _writer.println(margn3 + "NumberOfSamples: " - + Long.toString(start.getSamples())); - _writer.println(margn2 + "FilmFraming: NOT_APPLICABLE"); - _writer.println(margn3 + "Type: ntscFilmFramingType"); - - if (duration != null) { - _writer.println(margn1 + "Duration:"); - _writer.println(margn2 + "FrameCount: 30"); - _writer.println(margn2 + "TimeBase: 1000"); - _writer.println(margn2 + "VideoField: FIELD_1"); - _writer.println(margn2 + "CountingMode: NTSC_NON_DROP_FRAME"); - _writer.println(margn2 + "Hours: " - + Long.toString(duration.getHours())); - _writer.println(margn2 + "Minutes: " - + Long.toString(duration.getMinutes())); - _writer.println(margn2 + "Seconds: " - + Long.toString(duration.getSeconds())); - _writer.println(margn2 + "Frames: " - + Long.toString(duration.getFrames())); - _writer.println(margn2 + "Samples: "); - sr = duration.getSampleRate(); - if (sr == 1.0) { - sr = _sampleRate; - } - _writer.println(margn3 + "SampleRate: S" - + Integer.toString((int) sr)); - _writer.println(margn3 + "NumberOfSamples: " - + Long.toString(duration.getSamples())); - _writer.println(margn2 + "FilmFraming: NOT_APPLICABLE"); - _writer.println(margn3 + "Type: ntscFilmFramingType"); - } - } - - /** - * Display the NISO image metadata formatted according to the MIX schema. - * The schema which is used may be 0.2 or 1.0, depending on the module - * parameters. - * - * @param niso - * NISO image metadata - */ - protected void showNisoImageMetadata(NisoImageMetadata niso, String margin, - boolean rawOutput) { - if ("0.2".equals(_je.getMixVersion())) { - showNisoImageMetadata02(niso, margin, rawOutput); - } else { - showNisoImageMetadata10(niso, margin, rawOutput); - } - } - - /* - * Output the Niso image metadata, which is its own special kind of - * property. This provides a text approximation to MIX 0.2. - */ - private void showNisoImageMetadata02(NisoImageMetadata niso, String margin, - boolean rawOutput) { - String margn2 = margin + " "; - - _writer.println(); - String s = niso.getMimeType(); - if (s != null) { - _writer.println(margn2 + "MIMEType: " + s); - } - if ((s = niso.getByteOrder()) != null) { - _writer.println(margn2 + "ByteOrder: " + s); - } - int n = niso.getCompressionScheme(); - if (n != NisoImageMetadata.NULL) { - _writer.println(margn2 - + "CompressionScheme: " - + addIntegerValue(n, NisoImageMetadata.COMPRESSION_SCHEME, - NisoImageMetadata.COMPRESSION_SCHEME_INDEX, - rawOutput)); - } - if ((n = niso.getCompressionLevel()) != NisoImageMetadata.NULL) { - _writer.println(margn2 + "CompressionLevel: " + n); - } - if ((n = niso.getColorSpace()) != NisoImageMetadata.NULL) { - _writer.println(margn2 - + "ColorSpace: " - + addIntegerValue(n, NisoImageMetadata.COLORSPACE, - NisoImageMetadata.COLORSPACE_INDEX, rawOutput)); - } - if ((s = niso.getProfileName()) != null) { - _writer.println(margn2 + "ProfileName: " + s); - } - if ((s = niso.getProfileURL()) != null) { - _writer.println(margn2 + "ProfileURL: " + s); - } - int[] iarray = niso.getYCbCrSubSampling(); - if (iarray != null) { - _writer.print(margn2 + "YCbCrSubSampling: " + iarray[0]); - for (int i = 1; i < iarray.length; i++) { - _writer.print(", " + iarray[i]); - } - _writer.println(); - } - if ((n = niso.getYCbCrPositioning()) != NisoImageMetadata.NULL) { - _writer.println(margn2 - + "YCbCrPositioning: " - + addIntegerValue(n, NisoImageMetadata.YCBCR_POSITIONING, - rawOutput)); - } - Rational[] rarray = niso.getYCbCrCoefficients(); - if (rarray != null) { - _writer.print(margn2 + "YCbCrCoefficients: " - + addRationalValue(rarray[0], rawOutput)); - for (int i = 1; i < rarray.length; i++) { - _writer.print(", " + addRationalValue(rarray[i], rawOutput)); - } - _writer.println(); - } - rarray = niso.getReferenceBlackWhite(); - if (rarray != null) { - _writer.print(margn2 + "ReferenceBlackWhite: " - + addRationalValue(rarray[0], rawOutput)); - for (int i = 1; i < rarray.length; i++) { - _writer.print(", " + addRationalValue(rarray[i], rawOutput)); - } - _writer.println(); - } - if ((n = niso.getSegmentType()) != NisoImageMetadata.NULL) { - _writer.println(margn2 - + "SegmentType: " - + addIntegerValue(n, NisoImageMetadata.SEGMENT_TYPE, - rawOutput)); - } - long[] larray = niso.getStripOffsets(); - if (larray != null) { - _writer.print(margn2 + "StripOffsets: " + larray[0]); - for (int i = 1; i < larray.length; i++) { - _writer.print(", " + larray[i]); - } - _writer.println(); - } - long ln = niso.getRowsPerStrip(); - if (ln != NisoImageMetadata.NULL) { - _writer.println(margn2 + "RowsPerStrip: " + ln); - } - if ((larray = niso.getStripByteCounts()) != null) { - _writer.print(margn2 + "StripByteCounts: " + larray[0]); - for (int i = 1; i < larray.length; i++) { - _writer.print(", " + larray[i]); - } - _writer.println(); - } - if ((ln = niso.getTileWidth()) != NisoImageMetadata.NULL) { - _writer.println(margn2 + "TileWidth: " + ln); - } - if ((ln = niso.getTileLength()) != NisoImageMetadata.NULL) { - _writer.println(margn2 + "TileLength: " + ln); - } - if ((larray = niso.getTileOffsets()) != null) { - _writer.print(margn2 + "TileOffsets: " + larray[0]); - for (int i = 1; i < larray.length; i++) { - _writer.print(", " + larray[i]); - } - _writer.println(); - } - if ((larray = niso.getTileByteCounts()) != null) { - _writer.print(margn2 + "TileByteCounts: " + larray[0]); - for (int i = 1; i < larray.length; i++) { - _writer.print(", " + larray[i]); - } - _writer.println(); - } - if ((n = niso.getPlanarConfiguration()) != NisoImageMetadata.NULL) { - _writer.println(margn2 - + "PlanarConfiguration: " - + addIntegerValue(n, - NisoImageMetadata.PLANAR_CONFIGURATION, rawOutput)); - } - if ((s = niso.getImageIdentifier()) != null) { - _writer.println(margn2 + "ImageIdentifier: " + s); - } - if ((s = niso.getImageIdentifierLocation()) != null) { - _writer.println(margn2 + "ImageIdentifierLocation: " + s); - } - if ((ln = niso.getFileSize()) != NisoImageMetadata.NULL) { - _writer.println(margn2 + "FileSize: " + ln); - } - if ((n = niso.getChecksumMethod()) != NisoImageMetadata.NULL) { - _writer.println(margn2 - + "ChecksumMethod: " - + addIntegerValue(n, NisoImageMetadata.CHECKSUM_METHOD, - rawOutput)); - } - if ((s = niso.getChecksumValue()) != null) { - _writer.println(margn2 + "ChecksumValue: " + s); - } - if ((n = niso.getOrientation()) != NisoImageMetadata.NULL) { - _writer.println(margn2 - + "Orientation: " - + addIntegerValue(n, NisoImageMetadata.ORIENTATION, - rawOutput)); - } - if ((n = niso.getDisplayOrientation()) != NisoImageMetadata.NULL) { - _writer.println(margn2 - + "DisplayOrientation: " - + addIntegerValue(n, NisoImageMetadata.DISPLAY_ORIENTATION, - rawOutput)); - } - if ((ln = niso.getXTargetedDisplayAR()) != NisoImageMetadata.NULL) { - _writer.println(margn2 + "XTargetedDisplayAR: " + ln); - } - if ((ln = niso.getYTargetedDisplayAR()) != NisoImageMetadata.NULL) { - _writer.println(margn2 + "YTargetedDisplayAR: " + ln); - } - if ((s = niso.getPreferredPresentation()) != null) { - _writer.println(margn2 + "PreferredPresentation: " + s); - } - if ((s = niso.getSourceType()) != null) { - _writer.println(margn2 + "SourceType: " + s); - } - if ((s = niso.getImageProducer()) != null) { - _writer.println(margn2 + "ImageProducer: " + s); - } - if ((s = niso.getHostComputer()) != null) { - _writer.println(margn2 + "HostComputer: " + s); - } - if ((s = niso.getOS()) != null) { - _writer.println(margn2 + "OperatingSystem: " + s); - } - if ((s = niso.getOSVersion()) != null) { - _writer.println(margn2 + "OSVersion: " + s); - } - if ((s = niso.getDeviceSource()) != null) { - _writer.println(margn2 + "DeviceSource: " + s); - } - if ((s = niso.getScannerManufacturer()) != null) { - _writer.println(margn2 + "ScannerManufacturer: " + s); - } - if ((s = niso.getScannerModelName()) != null) { - _writer.println(margn2 + "ScannerModelName: " + s); - } - if ((s = niso.getScannerModelNumber()) != null) { - _writer.println(margn2 + "ScannerModelNumber: " + s); - } - if ((s = niso.getScannerModelSerialNo()) != null) { - _writer.println(margn2 + "ScannerModelSerialNo: " + s); - } - if ((s = niso.getScanningSoftware()) != null) { - _writer.println(margn2 + "ScanningSoftware: " + s); - } - if ((s = niso.getScanningSoftwareVersionNo()) != null) { - _writer.println(margn2 + "ScanningSoftwareVersionNo: " + s); - } - double d = niso.getPixelSize(); - if (d != NisoImageMetadata.NILL) { - _writer.println(margn2 + "PixelSize: " + d); - } - if ((d = niso.getXPhysScanResolution()) != NisoImageMetadata.NILL) { - _writer.println(margn2 + "XPhysScanResolution: " + d); - } - if ((d = niso.getYPhysScanResolution()) != NisoImageMetadata.NILL) { - _writer.println(margn2 + "YPhysScanResolution: " + d); - } - - if ((s = niso.getDigitalCameraManufacturer()) != null) { - _writer.println(margn2 + "DigitalCameraManufacturer: " + s); - } - if ((s = niso.getDigitalCameraModelName()) != null) { - _writer.println(margn2 + "DigitalCameraModelName: " + s); - } - if ((s = niso.getDigitalCameraModelNumber()) != null) { - _writer.println(margn2 + "DigitalCameraModelNumber: " + s); - } - if ((s = niso.getDigitalCameraModelSerialNo()) != null) { - _writer.println(margn2 + "DigitalCameraModelSerialNo: " + s); - } - if ((d = niso.getFNumber()) != NisoImageMetadata.NILL) { - _writer.println(margn2 + "FNumber: " + d); - } - if ((d = niso.getExposureTime()) != NisoImageMetadata.NILL) { - _writer.println(margn2 + "ExposureTime: " + d); - } - Rational r; - if ((r = niso.getBrightness()) != null) { - d = r.toDouble(); - _writer.println(margn2 + "Brightness: " + d); - } - if ((r = niso.getExposureBias()) != null) { - d = r.toDouble(); - _writer.println(margn2 + "ExposureBias: " + d); - } - - double[] darray = niso.getSubjectDistance(); - if (darray != null) { - _writer.print(margn2 + "SubjectDistance: " + darray[0]); - for (int i = 1; i < darray.length; i++) { - _writer.print(", " + darray[i]); - } - _writer.println(); - } - if ((n = niso.getMeteringMode()) != NisoImageMetadata.NULL) { - _writer.println(margn2 - + "MeteringMode: " - + addIntegerValue(n, NisoImageMetadata.METERING_MODE, - rawOutput)); - } - if ((n = niso.getSceneIlluminant()) != NisoImageMetadata.NULL) { - _writer.println(margn2 - + "SceneIlluminant: " - + addIntegerValue(n, NisoImageMetadata.METERING_MODE, - rawOutput)); - } - if ((d = niso.getColorTemp()) != NisoImageMetadata.NILL) { - _writer.println(margn2 + "ColorTemp: " + d); - } - if ((d = niso.getFocalLength()) != NisoImageMetadata.NILL) { - _writer.println(margn2 + "FocalLength: " + d); - } - if ((n = niso.getFlash()) != NisoImageMetadata.NULL) { - _writer.println(margn2 + "Flash: " - + addIntegerValue(n, NisoImageMetadata.FLASH, rawOutput)); - } - if ((r = niso.getFlashEnergy()) != null) { - d = r.toDouble(); - _writer.println(margn2 + "FlashEnergy: " + d); - } - if ((n = niso.getFlashReturn()) != NisoImageMetadata.NULL) { - _writer.println(margn2 - + "FlashReturn: " - + addIntegerValue(n, NisoImageMetadata.FLASH_RETURN, - rawOutput)); - } - if ((n = niso.getBackLight()) != NisoImageMetadata.NULL) { - _writer.println(margn2 - + "BackLight: " - + addIntegerValue(n, NisoImageMetadata.BACKLIGHT, rawOutput)); - } - if ((d = niso.getExposureIndex()) != NisoImageMetadata.NILL) { - _writer.println(margn2 + "ExposureIndex: " + d); - } - if ((n = niso.getAutoFocus()) != NisoImageMetadata.NULL) { - _writer.println(margn2 - + "AutoFocus: " - + addIntegerValue(n, NisoImageMetadata.AUTOFOCUS, rawOutput)); - } - if ((d = niso.getXPrintAspectRatio()) != NisoImageMetadata.NILL) { - _writer.println(margn2 + "XPrintAspectRatio: " + d); - } - if ((d = niso.getYPrintAspectRatio()) != NisoImageMetadata.NILL) { - _writer.println(margn2 + "YPrintAspectRatio: " + d); - } - - if ((n = niso.getSensor()) != NisoImageMetadata.NULL) { - _writer.println(margn2 + "Sensor: " - + addIntegerValue(n, NisoImageMetadata.SENSOR, rawOutput)); - } - if ((s = niso.getDateTimeCreated()) != null) { - _writer.println(margn2 + "DateTimeCreated: " + s); - } - if ((s = niso.getMethodology()) != null) { - _writer.println(margn2 + "Methodology: " + s); - } - if ((n = niso.getSamplingFrequencyPlane()) != NisoImageMetadata.NULL) { - _writer.println(margn2 - + "SamplingFrequencyPlane: " - + addIntegerValue(n, - NisoImageMetadata.SAMPLING_FREQUENCY_PLANE, - rawOutput)); - } - if ((n = niso.getSamplingFrequencyUnit()) != NisoImageMetadata.NULL) { - _writer.println(margn2 - + "SamplingFrequencyUnit: " - + addIntegerValue(n, - NisoImageMetadata.SAMPLING_FREQUENCY_UNIT, - rawOutput)); - } - r = niso.getXSamplingFrequency(); - if (r != null) { - _writer.println(margn2 + "XSamplingFrequency: " - + addRationalValue(r, rawOutput)); - - } - r = niso.getYSamplingFrequency(); - if (r != null) { - _writer.println(margn2 + "YSamplingFrequency: " - + addRationalValue(r, rawOutput)); - } - if ((ln = niso.getImageWidth()) != NisoImageMetadata.NULL) { - _writer.println(margn2 + "ImageWidth: " + ln); - } - if ((ln = niso.getImageLength()) != NisoImageMetadata.NULL) { - _writer.println(margn2 + "ImageLength: " + ln); - } - if ((d = niso.getSourceXDimension()) != NisoImageMetadata.NILL) { - _writer.println(margn2 + "SourceXDimension: " + d); - } - if ((n = niso.getSourceXDimensionUnit()) != NisoImageMetadata.NULL) { - _writer.println(margn2 - + "SourceXDimensionUnit: " - + addIntegerValue(n, - NisoImageMetadata.SOURCE_DIMENSION_UNIT, rawOutput)); - } - if ((d = niso.getSourceYDimension()) != NisoImageMetadata.NILL) { - _writer.println(margn2 + "SourceYDimension: " + ln); - } - if ((n = niso.getSourceYDimensionUnit()) != NisoImageMetadata.NULL) { - _writer.println(margn2 - + "SourceYDimensionUnit: " - + addIntegerValue(n, - NisoImageMetadata.SOURCE_DIMENSION_UNIT, rawOutput)); - } - if ((iarray = niso.getBitsPerSample()) != null) { - _writer.print(margn2 + "BitsPerSample: " + iarray[0]); - for (int i = 1; i < iarray.length; i++) { - _writer.print(", " + iarray[i]); - } - _writer.println(); - } - if ((n = niso.getSamplesPerPixel()) != NisoImageMetadata.NULL) { - _writer.println(margn2 + "SamplesPerPixel: " + n); - } - if ((iarray = niso.getExtraSamples()) != null) { - _writer.print(margn2 - + "ExtraSamples: " - + addIntegerValue(iarray[0], - NisoImageMetadata.EXTRA_SAMPLES, rawOutput)); - for (int i = 1; i < iarray.length; i++) { - _writer.print(", " - + addIntegerValue(iarray[i], - NisoImageMetadata.EXTRA_SAMPLES, rawOutput)); - } - _writer.println(); - } - if ((s = niso.getColormapReference()) != null) { - _writer.println(margn2 + "ColormapReference: " + s); - } - if ((iarray = niso.getColormapBitCodeValue()) != null) { - _writer.print(margn2 + "ColormapBitCodeValue: " + iarray[0]); - for (int i = 1; i < iarray.length; i++) { - _writer.print(", " + iarray[i]); - } - _writer.println(); - } - if ((iarray = niso.getColormapRedValue()) != null) { - _writer.print(margn2 + "ColormapRedValue: " + iarray[0]); - for (int i = 1; i < iarray.length; i++) { - _writer.print(", " + iarray[i]); - } - _writer.println(); - } - if ((iarray = niso.getColormapGreenValue()) != null) { - _writer.print(margn2 + "ColormapGreenValue: " + iarray[0]); - for (int i = 1; i < iarray.length; i++) { - _writer.print(", " + iarray[i]); - } - _writer.println(); - } - if ((iarray = niso.getColormapBlueValue()) != null) { - _writer.print(margn2 + "ColormapBlueValue: " + iarray[0]); - for (int i = 1; i < iarray.length; i++) { - _writer.print(", " + iarray[i]); - } - _writer.println(); - } - if ((iarray = niso.getGrayResponseCurve()) != null) { - _writer.print(margn2 + "GrayResponseCurve: " + iarray[0]); - for (int i = 1; i < iarray.length; i++) { - _writer.print(", " + iarray[i]); - } - _writer.println(); - } - if ((n = niso.getGrayResponseUnit()) != NisoImageMetadata.NULL) { - _writer.println(margn2 - + "GrayResponseUnit: " - + addIntegerValue(n, - NisoImageMetadata.GRAY_RESPONSE_UNIT_02, rawOutput)); - } - r = niso.getWhitePointXValue(); - if (r != null) { - _writer.println(margn2 + "WhitePointXValue: " - + addRationalValue(r, rawOutput)); - } - if ((r = niso.getWhitePointYValue()) != null) { - _writer.println(margn2 + "WhitePointXValue: " - + addRationalValue(r, rawOutput)); - } - if ((r = niso.getPrimaryChromaticitiesRedX()) != null) { - _writer.println(margn2 + "PrimaryChromaticitiesRedX: " - + addRationalValue(r, rawOutput)); - } - if ((r = niso.getPrimaryChromaticitiesRedY()) != null) { - _writer.println(margn2 + "PrimaryChromaticitiesRedY: " - + addRationalValue(r, rawOutput)); - } - if ((r = niso.getPrimaryChromaticitiesGreenX()) != null) { - _writer.println(margn2 + "PrimaryChromaticitiesGreenX: " - + addRationalValue(r, rawOutput)); - } - if ((r = niso.getPrimaryChromaticitiesGreenY()) != null) { - _writer.println(margn2 + "PrimaryChromaticitiesGreenY: " - + addRationalValue(r, rawOutput)); - } - if ((r = niso.getPrimaryChromaticitiesBlueX()) != null) { - _writer.println(margn2 + "PrimaryChromaticitiesBlueX: " - + addRationalValue(r, rawOutput)); - } - if ((r = niso.getPrimaryChromaticitiesBlueY()) != null) { - _writer.println(margn2 + "PrimaryChromaticitiesBlueY: " - + addRationalValue(r, rawOutput)); - } - if ((n = niso.getTargetType()) != NisoImageMetadata.NULL) { - _writer.println(margn2 - + "TargetType: " - + addIntegerValue(n, NisoImageMetadata.TARGET_TYPE, - rawOutput)); - } - if ((s = niso.getTargetIDManufacturer()) != null) { - _writer.println(margn2 + "TargetIDManufacturer: " + s); - } - if ((s = niso.getTargetIDName()) != null) { - _writer.println(margn2 + "TargetIDName: " + s); - } - if ((s = niso.getTargetIDNo()) != null) { - _writer.println(margn2 + "TargetIDNo: " + s); - } - if ((s = niso.getTargetIDMedia()) != null) { - _writer.println(margn2 + "TargetIDMedia: " + s); - } - if ((s = niso.getImageData()) != null) { - _writer.println(margn2 + "ImageData: " + s); - } - if ((s = niso.getPerformanceData()) != null) { - _writer.println(margn2 + "PerformanceData: " + s); - } - if ((s = niso.getProfiles()) != null) { - _writer.println(margn2 + "Profiles: " + s); - } - if ((s = niso.getDateTimeProcessed()) != null) { - _writer.println(margn2 + "DateTimeProcessed: " + s); - } - if ((s = niso.getSourceData()) != null) { - _writer.println(margn2 + "SourceData: " + s); - } - if ((s = niso.getProcessingAgency()) != null) { - _writer.println(margn2 + "ProcessingAgency: " + s); - } - if ((s = niso.getProcessingSoftwareName()) != null) { - _writer.println(margn2 + "ProcessingSoftwareName: " + s); - } - if ((s = niso.getProcessingSoftwareVersion()) != null) { - _writer.println(margn2 + "ProcessingSoftwareVersion: " + s); - } - String[] sarray = niso.getProcessingActions(); - if (sarray != null) { - _writer.print(margn2 + "ProcessingActions: " + sarray[0]); - for (int i = 1; i < sarray.length; i++) { - _writer.print(", " + sarray[i]); - } - _writer.println(); - } - - } - - private void showNisoImageMetadata10(NisoImageMetadata niso, String margin, - boolean rawOutput) { - String margn2 = margin + " "; - String s; - long ln; - _writer.println(); - if ((s = niso.getImageIdentifier()) != null) { - _writer.println(margn2 + "ImageIdentifier: " + s); - } - if ((ln = niso.getFileSize()) != NisoImageMetadata.NULL) { - _writer.println(margn2 + "FileSize: " + ln); - } - if ((s = niso.getMimeType()) != null) { - _writer.println(margn2 + "FormatName: " + s); - } - if ((s = niso.getByteOrder()) != null) { - // Convert strings to MIX 1.0 form - if (s.startsWith("big")) { - s = "big_endian"; - } else if (s.startsWith("little")) { - s = "little_endian"; - } - _writer.println(margn2 + "ByteOrder: " + s); - } - int n = niso.getCompressionScheme(); - if (n != NisoImageMetadata.NULL) { - _writer.println(margn2 - + "CompressionScheme: " - + addIntegerValue(n, NisoImageMetadata.COMPRESSION_SCHEME, - NisoImageMetadata.COMPRESSION_SCHEME_INDEX, - rawOutput)); - } - if ((n = niso.getCompressionLevel()) != NisoImageMetadata.NULL) { - _writer.println(margn2 + "CompressionLevel: " + n); - } - if ((n = niso.getChecksumMethod()) != NisoImageMetadata.NULL) { - _writer.println(margn2 - + "ChecksumMethod: " - + addIntegerValue(n, NisoImageMetadata.CHECKSUM_METHOD, - rawOutput)); - } - if ((s = niso.getChecksumValue()) != null) { - _writer.println(margn2 + "ChecksumValue: " + s); - } - if ((ln = niso.getImageWidth()) != NisoImageMetadata.NULL) { - _writer.println(margn2 + "ImageWidth: " + ln); - } - if ((ln = niso.getImageLength()) != NisoImageMetadata.NULL) { - _writer.println(margn2 + "ImageHeight: " + ln); - } - if ((n = niso.getColorSpace()) != NisoImageMetadata.NULL) { - _writer.println(margn2 - + "ColorSpace: " - + addIntegerValue(n, NisoImageMetadata.COLORSPACE, - NisoImageMetadata.COLORSPACE_INDEX, rawOutput)); - } - if ((s = niso.getProfileName()) != null) { - _writer.println(margn2 + "ICCProfileName: " + s); - } - if ((s = niso.getProfileURL()) != null) { - _writer.println(margn2 + "ICCProfileURL: " + s); - } - int[] iarray = niso.getYCbCrSubSampling(); - if (iarray != null) { - _writer.print(margn2 + "YCbCrSubSampling: " + iarray[0]); - for (int i = 1; i < iarray.length; i++) { - _writer.print(", " + iarray[i]); - } - _writer.println(); - } - Rational[] rarray = niso.getYCbCrCoefficients(); - if (rarray != null) { - _writer.print(margn2 + "YCbCrCoefficients: " - + addRationalValue(rarray[0], rawOutput)); - for (int i = 1; i < rarray.length; i++) { - _writer.print(", " + addRationalValue(rarray[i], rawOutput)); - } - _writer.println(); - } - rarray = niso.getReferenceBlackWhite(); - if (rarray != null) { - _writer.print(margn2 + "ReferenceBlackWhite: " - + addRationalValue(rarray[0], rawOutput)); - for (int i = 1; i < rarray.length; i++) { - _writer.print(", " + addRationalValue(rarray[i], rawOutput)); - } - _writer.println(); - } - if ((s = niso.getSourceType()) != null) { - _writer.println(margn2 + "SourceType: " + s); - } - s = niso.getSourceID(); - if (s != null) { - _writer.println(margn2 + "SourceID" + s); - } - double d; - if ((d = niso.getSourceXDimension()) != NisoImageMetadata.NILL) { - _writer.println(margn2 + "SourceXDimension: " + d); - } - if ((n = niso.getSourceXDimensionUnit()) != NisoImageMetadata.NULL) { - _writer.println(margn2 - + "SourceXDimensionUnit: " - + addIntegerValue(n, - NisoImageMetadata.SOURCE_DIMENSION_UNIT, rawOutput)); - } - if ((d = niso.getSourceYDimension()) != NisoImageMetadata.NILL) { - _writer.println(margn2 + "SourceYDimension: " + ln); - } - if ((n = niso.getSourceYDimensionUnit()) != NisoImageMetadata.NULL) { - _writer.println(margn2 - + "SourceYDimensionUnit: " - + addIntegerValue(n, - NisoImageMetadata.SOURCE_DIMENSION_UNIT, rawOutput)); - } - if ((s = niso.getDateTimeCreated()) != null) { - _writer.println(margn2 + "DateTimeCreated: " + s); - } - if ((s = niso.getImageProducer()) != null) { - _writer.println(margn2 + "ImageProducer: " + s); - } - if ((s = niso.getDeviceSource()) != null) { - _writer.println(margn2 + "CaptureDevice: " + s); - } - if ((s = niso.getScannerManufacturer()) != null) { - _writer.println(margn2 + "ScannerManufacturer: " + s); - } - if ((s = niso.getScannerModelName()) != null) { - _writer.println(margn2 + "ScannerModelName: " + s); - } - if ((s = niso.getScannerModelNumber()) != null) { - _writer.println(margn2 + "ScannerModelNumber: " + s); - } - if ((s = niso.getScannerModelSerialNo()) != null) { - _writer.println(margn2 + "ScannerModelSerialNo: " + s); - } - double xres = niso.getXPhysScanResolution(); - double yres = niso.getYPhysScanResolution(); - if (xres != NisoImageMetadata.NULL && yres != NisoImageMetadata.NULL) { - double res = (xres > yres ? xres : yres); - _writer.println(margn2 + "MaximumOpticalResolution: " - + Double.toString(res)); - } - if ((s = niso.getScanningSoftware()) != null) { - _writer.println(margn2 + "ScanningSoftware: " + s); - } - if ((s = niso.getScanningSoftwareVersionNo()) != null) { - _writer.println(margn2 + "ScanningSoftwareVersionNo: " + s); - } - if ((s = niso.getDigitalCameraManufacturer()) != null) { - _writer.println(margn2 + "DigitalCameraManufacturer: " + s); - } - if ((s = niso.getDigitalCameraModelName()) != null) { - _writer.println(margn2 + "DigitalCameraModelName: " + s); - } - if ((s = niso.getDigitalCameraModelNumber()) != null) { - _writer.println(margn2 + "DigitalCameraModelNumber: " + s); - } - if ((s = niso.getDigitalCameraModelSerialNo()) != null) { - _writer.println(margn2 + "DigitalCameraModelSerialNo: " + s); - } - if ((d = niso.getFNumber()) != NisoImageMetadata.NILL) { - _writer.println(margn2 + "FNumber: " + _format.format(d)); - } - if ((s = niso.getExifVersion()) != null) { - _writer.println(margn2 + "ExifVersion: " + s); - } - Rational r; - if ((r = niso.getMaxApertureValue()) != null) { - _writer.println(margn2 + "MaxApertureValue: " - + addRationalValue(r, rawOutput)); - } - if ((d = niso.getExposureTime()) != NisoImageMetadata.NILL) { - _writer.println(margn2 + "ExposureTime: " + _format.format(d)); - } - if ((n = niso.getExposureProgram()) != NisoImageMetadata.NULL) { - _writer.println(margn2 - + "ExposureProgram: " - + addIntegerValue(n, NisoImageMetadata.EXPOSURE_PROGRAM, - rawOutput)); - } - if ((r = niso.getBrightness()) != null) { - _writer.println(margn2 + "BrightnessValue: " - + addRationalValue(r, rawOutput)); - } - if ((r = niso.getExposureBias()) != null) { - _writer.println(margn2 + "ExposureBiasValue: " - + addRationalValue(r, rawOutput)); - } - double[] darray = niso.getSubjectDistance(); - if (darray != null) { - _writer.print(margn2 + "SubjectDistance: " + darray[0]); - for (int i = 1; i < darray.length; i++) { - _writer.print(", " + darray[i]); - } - _writer.println(); - } - if ((n = niso.getMeteringMode()) != NisoImageMetadata.NULL) { - _writer.println(margn2 - + "MeteringMode: " - + addIntegerValue(n, NisoImageMetadata.METERING_MODE, - rawOutput)); - } - if ((n = niso.getFlash()) != NisoImageMetadata.NULL) { - _writer.println(margn2 + "Flash: " - + addIntegerValue(n, NisoImageMetadata.FLASH, rawOutput)); - } - if ((d = niso.getFocalLength()) != NisoImageMetadata.NILL) { - _writer.println(margn2 + "FocalLength: " + d); - } - if ((r = niso.getFlashEnergy()) != null) { - _writer.println(margn2 + "FlashEnergy: " - + addRationalValue(r, rawOutput)); - } - if ((n = niso.getBackLight()) != NisoImageMetadata.NULL) { - _writer.println(margn2 - + "BackLight: " - + addIntegerValue(n, NisoImageMetadata.BACKLIGHT, rawOutput)); - } - if ((d = niso.getExposureIndex()) != NisoImageMetadata.NILL) { - _writer.println(margn2 + "ExposureIndex: " + d); - } - if ((n = niso.getAutoFocus()) != NisoImageMetadata.NULL) { - _writer.println(margn2 - + "AutoFocus: " - + addIntegerValue(n, NisoImageMetadata.AUTOFOCUS, rawOutput)); - } - if ((d = niso.getXPrintAspectRatio()) != NisoImageMetadata.NILL) { - _writer.println(margn2 + "XPrintAspectRatio: " + d); - } - if ((d = niso.getYPrintAspectRatio()) != NisoImageMetadata.NILL) { - _writer.println(margn2 + "YPrintAspectRatio: " + d); - } - if ((n = niso.getOrientation()) != NisoImageMetadata.NULL) { - _writer.println(margn2 - + "Orientation: " - + addIntegerValue(n, NisoImageMetadata.ORIENTATION, - rawOutput)); - } - if ((s = niso.getMethodology()) != null) { - _writer.println(margn2 + "Methodology: " + s); - } - if ((n = niso.getSamplingFrequencyPlane()) != NisoImageMetadata.NULL) { - _writer.println(margn2 - + "SamplingFrequencyPlane: " - + addIntegerValue(n, - NisoImageMetadata.SAMPLING_FREQUENCY_PLANE, - rawOutput)); - } - if ((n = niso.getSamplingFrequencyUnit()) != NisoImageMetadata.NULL) { - _writer.println(margn2 - + "SamplingFrequencyUnit: " - + addIntegerValue(n, - NisoImageMetadata.SAMPLING_FREQUENCY_UNIT, - rawOutput)); - } - r = niso.getXSamplingFrequency(); - if (r != null) { - _writer.println(margn2 + "XSamplingFrequency: " - + addRationalValue(r, rawOutput)); - - } - r = niso.getYSamplingFrequency(); - if (r != null) { - _writer.println(margn2 + "YSamplingFrequency: " - + addRationalValue(r, rawOutput)); - } - if ((iarray = niso.getBitsPerSample()) != null) { - _writer.print(margn2 + "BitsPerSample: " + iarray[0]); - for (int i = 1; i < iarray.length; i++) { - _writer.print(", " + iarray[i]); - } - _writer.println(); - _writer.println(margn2 + "BitsPerSampleUnit: integer"); - } - if ((n = niso.getSamplesPerPixel()) != NisoImageMetadata.NULL) { - _writer.println(margn2 + "SamplesPerPixel: " + n); - } - if ((iarray = niso.getExtraSamples()) != null) { - _writer.print(margn2 - + "ExtraSamples: " - + addIntegerValue(iarray[0], - NisoImageMetadata.EXTRA_SAMPLES, rawOutput)); - for (int i = 1; i < iarray.length; i++) { - _writer.print(", " - + addIntegerValue(iarray[i], - NisoImageMetadata.EXTRA_SAMPLES, rawOutput)); - } - _writer.println(); - } - if ((s = niso.getColormapReference()) != null) { - _writer.println(margn2 + "ColormapReference: " + s); - } - // The MIX 1.0 schema requires the letter "N" as the value of the - // gray response curve, which is clearly a bug. We deviate from - // the bug here. - if ((iarray = niso.getGrayResponseCurve()) != null) { - _writer.print(margn2 + "GrayResponseCurve: " + iarray[0]); - for (int i = 1; i < iarray.length; i++) { - _writer.print(", " + iarray[i]); - } - _writer.println(); - } - if ((n = niso.getGrayResponseUnit()) != NisoImageMetadata.NULL) { - _writer.println(margn2 - + "GrayResponseUnit: " - + addIntegerValue(n, - NisoImageMetadata.GRAY_RESPONSE_UNIT_02, rawOutput)); - } - r = niso.getWhitePointXValue(); - if (r != null) { - _writer.println(margn2 + "WhitePointXValue: " - + addRationalValue(r, rawOutput)); - } - if ((r = niso.getWhitePointYValue()) != null) { - _writer.println(margn2 + "WhitePointXValue: " - + addRationalValue(r, rawOutput)); - } - if ((r = niso.getPrimaryChromaticitiesRedX()) != null) { - _writer.println(margn2 + "PrimaryChromaticitiesRedX: " - + addRationalValue(r, rawOutput)); - } - if ((r = niso.getPrimaryChromaticitiesRedY()) != null) { - _writer.println(margn2 + "PrimaryChromaticitiesRedY: " - + addRationalValue(r, rawOutput)); - } - if ((r = niso.getPrimaryChromaticitiesGreenX()) != null) { - _writer.println(margn2 + "PrimaryChromaticitiesGreenX: " - + addRationalValue(r, rawOutput)); - } - if ((r = niso.getPrimaryChromaticitiesGreenY()) != null) { - _writer.println(margn2 + "PrimaryChromaticitiesGreenY: " - + addRationalValue(r, rawOutput)); - } - if ((r = niso.getPrimaryChromaticitiesBlueX()) != null) { - _writer.println(margn2 + "PrimaryChromaticitiesBlueX: " - + addRationalValue(r, rawOutput)); - } - if ((r = niso.getPrimaryChromaticitiesBlueY()) != null) { - _writer.println(margn2 + "PrimaryChromaticitiesBlueY: " - + addRationalValue(r, rawOutput)); - } - if ((n = niso.getTargetType()) != NisoImageMetadata.NULL) { - _writer.println(margn2 - + "TargetType: " - + addIntegerValue(n, NisoImageMetadata.TARGET_TYPE, - rawOutput)); - } - if ((s = niso.getTargetIDManufacturer()) != null) { - _writer.println(margn2 + "TargetIDManufacturer: " + s); - } - if ((s = niso.getTargetIDName()) != null) { - _writer.println(margn2 + "TargetIDName: " + s); - } - if ((s = niso.getTargetIDNo()) != null) { - _writer.println(margn2 + "TargetIDNo: " + s); - } - if ((s = niso.getTargetIDMedia()) != null) { - _writer.println(margn2 + "TargetIDMedia: " + s); - } - if ((s = niso.getImageData()) != null) { - _writer.println(margn2 + "ExternalTarget: " + s); - } - if ((s = niso.getPerformanceData()) != null) { - _writer.println(margn2 + "PerformanceData: " + s); - } - if ((s = niso.getSourceData()) != null) { - _writer.println(margn2 + "SourceData: " + s); - } - if ((s = niso.getProcessingAgency()) != null) { - _writer.println(margn2 + "ProcessingAgency: " + s); - } - if ((s = niso.getProcessingSoftwareName()) != null) { - _writer.println(margn2 + "ProcessingSoftwareName: " + s); - } - if ((s = niso.getProcessingSoftwareVersion()) != null) { - _writer.println(margn2 + "ProcessingSoftwareVersion: " + s); - } - if ((s = niso.getOS()) != null) { - _writer.println(margn2 + "OperatingSystem: " + s); - } - if ((s = niso.getOSVersion()) != null) { - _writer.println(margn2 + "OSVersion: " + s); - } - String[] sarray = niso.getProcessingActions(); - if (sarray != null) { - _writer.print(margn2 + "ProcessingActions: " + sarray[0]); - for (int i = 1; i < sarray.length; i++) { - _writer.print(", " + sarray[i]); - } - _writer.println(); - } - - } - - private String addIntegerValue(int value, String[] labels, boolean rawOutput) { - String s = null; - if (!rawOutput && 0 <= value && value < labels.length) { - s = labels[value]; - } else { - s = Integer.toString(value); - } - - return s; - } - - private String addIntegerValue(int value, String[] labels, int[] index, - boolean rawOutput) { - String s = null; - boolean outOfRange = false; - if (!rawOutput) { - int n = -1; - for (int i = 0; i < index.length; i++) { - if (value == index[i]) { - n = i; - break; - } - } - if (n > -1) { - s = labels[n]; - } else { - outOfRange = true; - } - } - if (rawOutput || outOfRange) { - s = Integer.toString(value); - } - - return s; - } - - private String addRationalValue(Rational r, boolean rawOutput) { - String s = null; - if (!rawOutput) { - s = _format.format(r.toDouble()); - } else { - s = r.toString(); - } - - return s; - } + /** + * **************************************************************** PRIVATE CLASS FIELDS. + * **************************************************************** + */ + private static final String NAME = "TEXT"; + + private static final String RELEASE = "1.6"; + private static final int[] DATE = {2018, 03, 29}; + private static final String NOTE = "This is the default JHOVE output " + "handler"; + private static final String RIGHTS = + "Derived from software Copyright 2004-2011 " + + "by the President and Fellows of Harvard College. " + + "Version 1.6 release by Open Preservation Foundation. " + + "Released under the GNU Lesser General Public License."; + + private NumberFormat _format; + + /** + * **************************************************************** PRIVATE INSTANCE FIELDS. + * **************************************************************** + */ + + /* Sample rate. */ + private double _sampleRate; + + /** + * **************************************************************** CLASS CONSTRUCTOR. + * **************************************************************** + */ + + /** Creates a TextHandler. */ + public TextHandler() { + super(NAME, RELEASE, DATE, NOTE, RIGHTS); + _vendor = Agent.harvardInstance(); + + _format = NumberFormat.getInstance(Locale.ROOT); + _format.setGroupingUsed(false); + _format.setMinimumFractionDigits(0); + } + + /** + * **************************************************************** PUBLIC INSTANCE METHODS. + * **************************************************************** + */ + + /** Outputs minimal information about the application */ + @Override + public void show() { + _level--; + } + + /** + * Outputs detailed information about the application, including configuration, available modules + * and handlers, etc. + */ + @Override + public void show(App app) { + String margin = getIndent(++_level); + + _writer.println(margin + "App:"); + _writer.println( + margin + " API: " + app.getRelease() + ", " + HandlerBase.date.format(_je.getDate())); + String configFile = _je.getConfigFile(); + if (configFile != null) { + _writer.println(margin + " Configuration: " + configFile); + } + String s = _je.getSaxClass(); + if (s != null) { + _writer.println(margin + " SAXparser: " + s); + } + s = _je.getJhoveHome(); + if (s != null) { + _writer.println(margin + " JhoveHome: " + s); + } + s = _je.getEncoding(); + if (s != null) { + _writer.println(margin + " Encoding: " + s); + } + s = _je.getTempDirectory(); + if (s != null) { + _writer.println(margin + " TempDirectory: " + s); + } + _writer.println(margin + " BufferSize: " + _je.getBufferSize()); + Iterator iter = _je.getModuleMap().keySet().iterator(); + while (iter.hasNext()) { + Module module = _je.getModule(iter.next()); + _writer.println(margin + " Module: " + module.getName() + " " + module.getRelease()); + } + iter = _je.getHandlerMap().keySet().iterator(); + while (iter.hasNext()) { + OutputHandler handler = _je.getHandler(iter.next()); + _writer.println(margin + " OutputHandler: " + handler.getName() + " " + handler.getRelease()); + } + + _writer.println(margin + " Usage: " + app.getUsage()); + _writer.println(margin + " Rights: " + app.getRights()); + + _level--; + } + + /** Outputs information about the OutputHandler specified in the parameter */ + @Override + public void show(OutputHandler handler) { + String margin = getIndent(++_level); + + _writer.println(margin + "Handler: " + handler.getName()); + _writer.println(margin + " Release: " + handler.getRelease()); + _writer.println(margin + " Date: " + HandlerBase.date.format(handler.getDate())); + List list = handler.getSpecification(); + int n = list.size(); + for (int i = 0; i < n; i++) { + showDocument(list.get(i), "Specification"); + } + Agent vendor = handler.getVendor(); + if (vendor != null) { + showAgent(vendor, "Vendor"); + } + String s; + if ((s = handler.getNote()) != null) { + _writer.println(margin + " Note: " + s); + } + if ((s = handler.getRights()) != null) { + _writer.println(margin + " Rights: " + s); + } + } + + /** Outputs information about a Module */ + @Override + public void show(Module module) { + String margin = getIndent(++_level); + + _writer.println(margin + "Module: " + module.getName()); + _writer.println(margin + " Release: " + module.getRelease()); + _writer.println(margin + " Date: " + HandlerBase.date.format(module.getDate())); + String[] ss = module.getFormat(); + if (ss.length > 0) { + _writer.print(margin + " Format: " + ss[0]); + for (int i = 1; i < ss.length; i++) { + _writer.print(", " + ss[i]); + } + _writer.println(); + } + String s = module.getCoverage(); + if (s != null) { + _writer.println(margin + " Coverage: " + s); + } + ss = module.getMimeType(); + if (ss.length > 0) { + _writer.print(margin + " MIMEtype: " + ss[0]); + for (int i = 1; i < ss.length; i++) { + _writer.print(", " + ss[i]); + } + ; + _writer.println(); + } + for (Signature sig : module.getSignature()) { + showSignature(sig); + } + for (Document spec : module.getSpecification()) { + showDocument(spec, "Specification"); + } + for (String feature : module.getFeatures()) { + _writer.println(margin + " Feature: " + feature); + } + + _writer.println(margin + " Methodology:"); + if ((s = module.getWellFormedNote()) != null) { + _writer.println(margin + " Well-formed: " + s); + } + if ((s = module.getValidityNote()) != null) { + _writer.println(margin + " Validity: " + s); + } + if ((s = module.getRepInfoNote()) != null) { + _writer.println(margin + " RepresentationInformation: " + s); + } + Agent vendor = module.getVendor(); + if (vendor != null) { + showAgent(vendor, "Vendor"); + } + if ((s = module.getNote()) != null) { + _writer.println(margin + " Note: " + s); + } + if ((s = module.getRights()) != null) { + _writer.println(margin + " Rights: " + s); + } + + _level--; + } + + /** Outputs the information contained in a RepInfo object */ + @Override + public void show(RepInfo info) { + String margin = getIndent(++_level); + + Module module = info.getModule(); + _writer.println(margin + "RepresentationInformation: " + info.getUri()); + if (module != null) { + _writer.println( + margin + + " ReportingModule: " + + module.getName() + + ", Rel. " + + module.getRelease() + + " (" + + date.format(module.getDate()) + + ")"); + } + + Date date = info.getCreated(); + if (date != null) { + _writer.println(margin + " Created: " + dateTime.format(date)); + } + date = info.getLastModified(); + if (date != null) { + _writer.println(margin + " LastModified: " + dateTime.format(date)); + } + long size = info.getSize(); + if (size > -1) { + _writer.println(margin + " Size: " + size); + } + String s = info.getFormat(); + if (s != null) { + _writer.println(margin + " Format: " + s); + } + s = info.getVersion(); + if (s != null) { + _writer.println(margin + " Version: " + s); + } + if (!_je.getSignatureFlag()) { + _writer.print(margin + " Status: "); + switch (info.getWellFormed()) { + case RepInfo.TRUE: + s = "Well-Formed"; + break; + + case RepInfo.FALSE: + s = "Not well-formed"; + break; + + default: + s = "Unknown"; + break; + } + if (info.getWellFormed() == RepInfo.TRUE) { + switch (info.getValid()) { + case RepInfo.TRUE: + s += " and valid"; + break; + + case RepInfo.FALSE: + s += ", but not valid"; + break; + + // case UNDETERMINED: add nothing + } + } + _writer.println(s); + } else { + // If we aren't checking signatures, we still need to say something. + _writer.print(margin + " Status: "); + switch (info.getWellFormed()) { + case RepInfo.TRUE: + s = "Well-Formed"; + break; + + default: + s = "Not well-formed"; + break; + } + _writer.println(s); + } + List list1 = info.getSigMatch(); + int n = list1.size(); + if (n > 0) { + _writer.println(margin + " SignatureMatches:"); + for (int i = 0; i < n; i++) { + _writer.println(margin + " " + list1.get(i)); + } + } + + List list2 = info.getMessage(); + n = list2.size(); + for (int i = 0; i < n; i++) { + showMessage(list2.get(i)); + } + s = info.getMimeType(); + if (s != null) { + _writer.println(margin + " MIMEtype: " + s); + } + + List list3 = info.getProfile(); + n = list3.size(); + if (n > 0) { + _writer.print(margin + " Profile: " + list3.get(0)); + for (int i = 1; i < n; i++) { + _writer.print(", " + list3.get(i)); + } + _writer.println(); + } + + Map map = info.getProperty(); + if (map != null) { + Iterator iter = map.keySet().iterator(); + while (iter.hasNext()) { + String key = iter.next(); + showProperty(info.getProperty(key), key, margin); + } + } + + List list4 = info.getChecksum(); + n = list4.size(); + for (int i = 0; i < n; i++) { + showChecksum((Checksum) list4.get(i)); + } + + _level--; + } + + /** + * **************************************************************** PRIVATE INSTANCE METHODS. + * **************************************************************** + */ + private void showAgent(Agent agent, String label) { + String margin = getIndent(++_level); + + _writer.println(margin + label + ": " + agent.getName()); + _writer.println(margin + " Type: " + agent.getType().toString()); + String s = agent.getAddress(); + if (s != null) { + _writer.println(margin + " Address: " + s); + } + if ((s = agent.getTelephone()) != null) { + _writer.println(margin + " Telephone: " + s); + } + if ((s = agent.getFax()) != null) { + _writer.println(margin + " Fax: " + s); + } + if ((s = agent.getEmail()) != null) { + _writer.println(margin + " Email: " + s); + } + if ((s = agent.getWeb()) != null) { + _writer.println(margin + " Web: " + s); + } + _level--; + } + + private void showChecksum(Checksum checksum) { + String margin = getIndent(++_level); + + _writer.println(margin + "Checksum: " + checksum.getValue()); + _writer.println(margin + " Type: " + checksum.getType().toString()); + + _level--; + } + + private void showDocument(Document document, String label) { + String margin = getIndent(++_level); + + _writer.println(margin + label + ": " + document.getTitle()); + _writer.println(margin + " Type: " + document.getType()); + List list1 = document.getAuthor(); + int n = list1.size(); + for (int i = 0; i < n; i++) { + showAgent(list1.get(i), "Author"); + } + List list2 = document.getPublisher(); + n = list2.size(); + for (int i = 0; i < n; i++) { + showAgent(list2.get(i), "Publisher"); + } + String s = document.getEdition(); + if (s != null) { + _writer.println(margin + " Edition: " + s); + } + if ((s = document.getDate()) != null) { + _writer.println(margin + " Date: " + s); + } + if ((s = document.getEnumeration()) != null) { + _writer.println(margin + " Enumeration: " + s); + } + if ((s = document.getPages()) != null) { + _writer.println(margin + " Pages: " + s); + } + List list3 = document.getIdentifier(); + n = list3.size(); + for (int i = 0; i < n; i++) { + showIdentifier(list3.get(i)); + } + if ((s = document.getNote()) != null) { + _writer.println(margin + " Note: " + s); + } + _level--; + } + + /** + * Do the final output. This should be in a suitable format for including multiple files between + * the header and the footer. + */ + @Override + public void showFooter() { + _level--; + + _writer.flush(); + } + + /** + * Do the initial output. This should be in a suitable format for including multiple files between + * the header and the footer. + */ + @Override + public void showHeader() { + String margin = getIndent(++_level); + + _writer.println( + margin + + _app.getName() + + " (Rel. " + + _app.getRelease() + + ", " + + HandlerBase.date.format(_app.getDate()) + + ")"); + _writer.println(margin + " Date: " + HandlerBase.dateTime.format(new Date())); + } + + private void showIdentifier(Identifier identifier) { + String margin = getIndent(++_level); + + _writer.println(margin + "Identifier: " + identifier.getValue()); + _writer.println(margin + " Type: " + identifier.getType().toString()); + String note = identifier.getNote(); + if (note != null) { + _writer.println(margin + " Note: " + note); + } + _level--; + } + + private void showMessage(Message message) { + String margin = getIndent(++_level); + String prefix = message.getPrefix() + "Message: "; + String str = message.getMessage(); + // Append submessage, if any, after a colon. + String submsg = message.getSubMessage(); + if (submsg != null) { + str += ": " + submsg; + } + _writer.println(margin + prefix + str); + String id = message.getId(); + if (!(id == null || id.isEmpty() || id.equals(JhoveMessages.NO_ID))) { + _writer.println(margin + " ID: " + id); + } + long offset = message.getOffset(); + if (offset > -1) { + _writer.println(margin + " Offset: " + offset); + } + _level--; + } + + private void showSignature(Signature signature) { + String margin = getIndent(++_level); + + String sigValue; + if (signature.isStringValue()) { + sigValue = signature.getValueString(); + } else { + sigValue = signature.getValueHexString(); + } + _writer.println(margin + signature.getType().toString() + ": " + sigValue); + if ((signature.getType().equals(SignatureType.MAGIC)) + && (((InternalSignature) signature).hasFixedOffset())) { + _writer.println(margin + " Offset: " + ((InternalSignature) signature).getOffset()); + } + String note = signature.getNote(); + if (note != null) { + _writer.println(margin + " Note: " + note); + } + String use = signature.getUse().toString(); + if (use != null) { + _writer.println(margin + " Use: " + use); + } + _level--; + } + + /* showProperty may be called recursively. */ + private void showProperty(Property property, String key, String margin) { + PropertyArity arity = property.getArity(); + + if (key == null) { + _writer.print(margin + " "); + } else { + _writer.print(margin + " " + key + ": "); + } + if (arity.equals(PropertyArity.SCALAR)) { + showScalarProperty(property, margin); + } else if (arity.equals(PropertyArity.LIST)) { + showListProperty(property, margin); + } else if (arity.equals(PropertyArity.MAP)) { + showMapProperty(property, margin); + } else if (arity.equals(PropertyArity.SET)) { + showSetProperty(property, margin); + } else if (arity.equals(PropertyArity.ARRAY)) { + showArrayProperty(property, margin); + } else { + _writer.println(); + } + } + + private void showScalarProperty(Property property, String margin) { + PropertyType type = property.getType(); + if (PropertyType.PROPERTY.equals(type)) { + _writer.println(); + Property prop = (Property) property.getValue(); + showProperty(prop, prop.getName(), margin + " "); + // _writer.println (); // Does this improve things? + } else if (PropertyType.NISOIMAGEMETADATA.equals(type)) { + showNisoImageMetadata( + (NisoImageMetadata) property.getValue(), margin + " ", _je.getShowRawFlag()); + } else if (PropertyType.AESAUDIOMETADATA.equals(type)) { + showAESAudioMetadata( + (AESAudioMetadata) property.getValue(), margin + " ", _je.getShowRawFlag()); + } else if (PropertyType.TEXTMDMETADATA.equals(type)) { + showTextMDMetadata((TextMDMetadata) property.getValue(), margin + " ", _je.getShowRawFlag()); + } else { + _writer.println(property.getValue().toString()); + } + } + + private void showListProperty(Property property, String margin) { + PropertyType type = property.getType(); + boolean valueIsProperty = PropertyType.PROPERTY.equals(type); + boolean valueIsNiso = PropertyType.NISOIMAGEMETADATA.equals(type); + boolean valueIsTextMD = PropertyType.TEXTMDMETADATA.equals(type); + + List list = (List) property.getValue(); + + int n = list.size(); + int i; + if (n > 0) { + // Put a blank line after the name of the property list. + if (valueIsProperty) { + _writer.println(); + } + for (i = 0; i < n; i++) { + if (valueIsProperty) { + Property pval = (Property) list.get(i); + showProperty(pval, pval.getName(), margin + " "); + } else if (valueIsNiso) { + showNisoImageMetadata( + (NisoImageMetadata) list.get(i), margin + " ", _je.getShowRawFlag()); + } else if (valueIsTextMD) { + showTextMDMetadata((TextMDMetadata) list.get(i), margin + " ", _je.getShowRawFlag()); + } else { + Object val = list.get(i); + if (i == 0) { + _writer.print(val); + } else { + _writer.print(", " + val); + } + } + } + } + if (!valueIsProperty || n == 0) { + _writer.println(); + } + } + + private void showMapProperty(Property property, String margin) { + /* + * Map output looks like key : mapkey1 / mapval1, mapkey2 / mapval2, ... + */ + PropertyType type = property.getType(); + boolean valueIsProperty = PropertyType.PROPERTY.equals(type); + boolean valueIsNiso = PropertyType.NISOIMAGEMETADATA.equals(type); + boolean valueIsTextMD = PropertyType.TEXTMDMETADATA.equals(type); + + Map propmap = (Map) property.getValue(); + Set keys = propmap.keySet(); + Iterator propiter = keys.iterator(); + while (propiter.hasNext()) { + Object propkey = propiter.next(); + Object val = propmap.get(propkey); + if (valueIsProperty) { + Property pval = (Property) val; + showProperty(pval, pval.getName(), margin + " "); + String propkeyStr = propkey.toString(); + if (!(pval.getName().equals(propkeyStr))) { + _writer.println(" Key: " + propkeyStr); + } + } else if (valueIsNiso) { + showNisoImageMetadata((NisoImageMetadata) val, margin + " ", _je.getShowRawFlag()); + } else if (valueIsTextMD) { + showTextMDMetadata((TextMDMetadata) val, margin + " ", _je.getShowRawFlag()); + } else { + _writer.println(" " + val.toString()); + _writer.println(" Key: " + propkey.toString()); + } + } + } + + private void showSetProperty(Property property, String margin) { + PropertyType type = property.getType(); + boolean valueIsProperty = PropertyType.PROPERTY.equals(type); + boolean valueIsNiso = PropertyType.NISOIMAGEMETADATA.equals(type); + boolean valueIsTextMD = PropertyType.TEXTMDMETADATA.equals(type); + + Set propset = (Set) property.getValue(); + Iterator propiter = propset.iterator(); + boolean first = true; + while (propiter.hasNext()) { + Object val = propiter.next(); + if (valueIsProperty) { + Property pval = (Property) val; + showProperty(pval, pval.getName(), margin + " "); + } else if (valueIsNiso) { + showNisoImageMetadata((NisoImageMetadata) val, margin + " ", _je.getShowRawFlag()); + } else if (valueIsTextMD) { + showTextMDMetadata((TextMDMetadata) val, margin + " ", _je.getShowRawFlag()); + } else { + if (first) { + _writer.print(val.toString()); + first = false; + } else { + _writer.print(", " + val.toString()); + } + } + } + _writer.println(); + } + + private void showArrayProperty(Property property, String margin) { + boolean[] boolArray = null; + byte[] byteArray = null; + char[] charArray = null; + java.util.Date[] dateArray = null; + double[] doubleArray = null; + float[] floatArray = null; + int[] intArray = null; + long[] longArray = null; + Object[] objArray = null; + Property[] propArray = null; + short[] shortArray = null; + String[] stringArray = null; + Rational[] rationalArray = null; + NisoImageMetadata[] nisoArray = null; + TextMDMetadata[] textMDArray = null; + int n = 0; + + PropertyType propType = property.getType(); + if (PropertyType.BOOLEAN.equals(propType)) { + boolArray = (boolean[]) property.getValue(); + n = boolArray.length; + } else if (PropertyType.BYTE.equals(propType)) { + byteArray = (byte[]) property.getValue(); + n = byteArray.length; + } else if (PropertyType.CHARACTER.equals(propType)) { + charArray = (char[]) property.getValue(); + n = charArray.length; + } else if (PropertyType.DATE.equals(propType)) { + dateArray = (java.util.Date[]) property.getValue(); + n = dateArray.length; + } else if (PropertyType.DOUBLE.equals(propType)) { + doubleArray = (double[]) property.getValue(); + n = doubleArray.length; + } else if (PropertyType.FLOAT.equals(propType)) { + floatArray = (float[]) property.getValue(); + n = floatArray.length; + } else if (PropertyType.INTEGER.equals(propType)) { + intArray = (int[]) property.getValue(); + n = intArray.length; + } else if (PropertyType.LONG.equals(propType)) { + longArray = (long[]) property.getValue(); + n = longArray.length; + } else if (PropertyType.OBJECT.equals(propType)) { + objArray = (Object[]) property.getValue(); + n = objArray.length; + } else if (PropertyType.SHORT.equals(propType)) { + shortArray = (short[]) property.getValue(); + n = shortArray.length; + } else if (PropertyType.STRING.equals(propType)) { + stringArray = (String[]) property.getValue(); + n = stringArray.length; + } else if (PropertyType.RATIONAL.equals(propType)) { + rationalArray = (Rational[]) property.getValue(); + n = rationalArray.length; + } else if (PropertyType.PROPERTY.equals(propType)) { + propArray = (Property[]) property.getValue(); + n = propArray.length; + } else if (PropertyType.NISOIMAGEMETADATA.equals(propType)) { + nisoArray = (NisoImageMetadata[]) property.getValue(); + n = nisoArray.length; + } else if (PropertyType.TEXTMDMETADATA.equals(propType)) { + textMDArray = (TextMDMetadata[]) property.getValue(); + n = textMDArray.length; + } + + for (int i = 0; i < n; i++) { + String elem; + if (PropertyType.BOOLEAN.equals(propType)) { + elem = String.valueOf(boolArray[i]); + } else if (PropertyType.BYTE.equals(propType)) { + elem = String.valueOf(byteArray[i]); + } else if (PropertyType.CHARACTER.equals(propType)) { + elem = String.valueOf(charArray[i]); + } else if (PropertyType.DATE.equals(propType)) { + elem = dateArray[i].toString(); + } else if (PropertyType.DOUBLE.equals(propType)) { + elem = String.valueOf(doubleArray[i]); + } else if (PropertyType.FLOAT.equals(propType)) { + elem = String.valueOf(floatArray[i]); + } else if (PropertyType.INTEGER.equals(propType)) { + elem = String.valueOf(intArray[i]); + } else if (PropertyType.LONG.equals(propType)) { + elem = String.valueOf(longArray[i]); + } else if (PropertyType.OBJECT.equals(propType)) { + elem = objArray[i].toString(); + } else if (PropertyType.SHORT.equals(propType)) { + elem = String.valueOf(shortArray[i]); + } else if (PropertyType.STRING.equals(propType)) { + elem = stringArray[i]; + } else if (PropertyType.RATIONAL.equals(propType)) { + elem = rationalArray[i].toString(); + } else if (PropertyType.NISOIMAGEMETADATA.equals(propType)) { + if (i == 0) { + _writer.println(); + } + NisoImageMetadata niso = nisoArray[i]; + showNisoImageMetadata(niso, margin + " ", _je.getShowRawFlag()); + continue; + } else if (PropertyType.TEXTMDMETADATA.equals(propType)) { + if (i == 0) { + _writer.println(); + } + showTextMDMetadata(textMDArray[i], margin + " ", _je.getShowRawFlag()); + continue; + } else if (PropertyType.PROPERTY.equals(propType)) { + if (i == 0) { + _writer.println(); + } + Property pval = propArray[i]; + showProperty(pval, pval.getName(), margin + " "); + continue; + } else elem = ""; + if (i == 0) { + _writer.print(elem); + } else { + _writer.print(", " + elem); + } + } + if (propType != PropertyType.PROPERTY && propType != PropertyType.NISOIMAGEMETADATA) { + _writer.println(); + } + } + + /* + * Output the textMD metadata, which is its own special kind of property. + */ + private void showTextMDMetadata(TextMDMetadata textMD, String margin, boolean rawOutput) { + String margn2 = margin + " "; + String margn3 = margn2 + " "; + + _writer.println(); + _writer.println(margn2 + "Character_info:"); + String s = textMD.getCharset(); + if (s != null) { + _writer.println(margn3 + "Charset: " + s); + } + if ((s = textMD.getByte_orderString()) != null) { + _writer.println(margn3 + "Byte_order: " + s); + } + if ((s = textMD.getByte_size()) != null) { + _writer.println(margn3 + "Byte_size: " + s); + } + if ((s = textMD.getCharacter_size()) != null) { + _writer.println(margn3 + "Character_size: " + s); + } + if ((s = textMD.getLinebreakString()) != null) { + _writer.println(margn3 + "Linebreak: " + s); + } + + if ((s = textMD.getLanguage()) != null) { + _writer.println(margn2 + "Language: " + s); + } + if ((s = textMD.getMarkup_basis()) != null) { + _writer.println(margn2 + "Markup_basis: " + s); + } + if ((s = textMD.getMarkup_basis_version()) != null) { + _writer.println(margn2 + "Markup_basis_version: " + s); + } + if ((s = textMD.getMarkup_language()) != null) { + _writer.println(margn2 + "Markup_language: " + s); + } + if ((s = textMD.getMarkup_language_version()) != null) { + _writer.println(margn2 + "Markup_language_version: " + s); + } + } + + /* + * Output the AES audio metadata, which is its own special kind of property. + */ + private void showAESAudioMetadata(AESAudioMetadata aes, String margin, boolean rawOutput) { + String margn2 = margin + " "; + String margn3 = margn2 + " "; + String margn4 = margn3 + " "; + String margn5 = margn4 + " "; + + _sampleRate = aes.getSampleRate(); + + _writer.println(); + String s = aes.getAnalogDigitalFlag(); + if (s != null) { + _writer.println(margn2 + "AnalogDigitalFlag: " + s); + } + s = aes.getSchemaVersion(); + if (s != null) { + _writer.println(margn2 + "SchemaVersion: " + s); + } + s = aes.getFormat(); + if (s != null) { + _writer.println(margn2 + "Format: " + s); + } + s = aes.getSpecificationVersion(); + if (s != null) { + _writer.println(margn2 + "SpecificationVersion: " + s); + } + s = aes.getAppSpecificData(); + if (s != null) { + _writer.println(margn2 + "AppSpecificData: " + s); + } + s = aes.getAudioDataEncoding(); + if (s != null) { + _writer.println(margn2 + "AudioDataEncoding: " + s); + } + int in = aes.getByteOrder(); + if (in != AESAudioMetadata.NULL) { + _writer.println( + margn2 + + "ByteOrder: " + + (in == AESAudioMetadata.BIG_ENDIAN ? "BIG_ENDIAN" : "LITTLE_ENDIAN")); + } + long lin = aes.getFirstSampleOffset(); + if (lin != AESAudioMetadata.NULL) { + _writer.println(margn2 + "FirstSampleOffset: " + Long.toString(lin)); + } + String[] use = aes.getUse(); + if (use != null) { + _writer.println(margn2 + "Use:"); + _writer.println(margn3 + "UseType: " + use[0]); + _writer.println(margn3 + "OtherType: " + use[1]); + } + s = aes.getPrimaryIdentifier(); + if (s != null) { + String t = aes.getPrimaryIdentifierType(); + _writer.println(margn2 + "PrimaryIdentifier: " + s); + if (t != null) { + _writer.println(margn3 + "IdentifierType: " + t); + } + } + List facelist = aes.getFaceList(); + if (!facelist.isEmpty()) { + // Add the face information, which is mostly filler. + AESAudioMetadata.Face f = (AESAudioMetadata.Face) facelist.get(0); + _writer.println(margn2 + "Face: "); + _writer.println(margn3 + "TimeLine: "); + AESAudioMetadata.TimeDesc startTime = f.getStartTime(); + if (startTime != null) { + writeAESTimeRange(margn3, startTime, f.getDuration()); + } + int nchan = aes.getNumChannels(); + if (nchan != AESAudioMetadata.NULL) { + _writer.println(margn4 + "NumChannels: " + Integer.toString(nchan)); + } + String[] locs = aes.getMapLocations(); + for (int ch = 0; ch < nchan; ch++) { + // write a stream description for each channel + _writer.println(margn4 + "Stream:"); + _writer.println(margn5 + "ChannelNum: " + Integer.toString(ch)); + _writer.println(margn5 + "ChannelAssignment: " + locs[ch]); + } + } + + // In the general case, a FormatList can contain multiple + // FormatRegions. This doesn't happen with any of the current + // modules; if it's needed in the future, simply set up an + // iteration loop on formatList. + List flist = aes.getFormatList(); + if (!flist.isEmpty()) { + AESAudioMetadata.FormatRegion rgn = (AESAudioMetadata.FormatRegion) flist.get(0); + int bitDepth = rgn.getBitDepth(); + double sampleRate = rgn.getSampleRate(); + int wordSize = rgn.getWordSize(); + String[] bitRed = rgn.getBitrateReduction(); + // Build a FormatRegion subtree if at least one piece of data + // that goes into it is present. + if (bitDepth != AESAudioMetadata.NULL + || sampleRate != AESAudioMetadata.NILL + || wordSize != AESAudioMetadata.NULL) { + _writer.println(margn2 + "FormatList:"); + _writer.println(margn3 + "FormatRegion:"); + if (bitDepth != AESAudioMetadata.NULL) { + _writer.println(margn4 + "BitDepth: " + Integer.toString(bitDepth)); + } + if (sampleRate != AESAudioMetadata.NILL) { + _writer.println(margn4 + "SampleRate: " + Double.toString(sampleRate)); + } + if (wordSize != AESAudioMetadata.NULL) { + _writer.println(margn4 + "WordSize: " + Integer.toString(wordSize)); + } + if (bitRed != null) { + _writer.println(margn4 + "BitrateReduction"); + _writer.println(margn5 + "CodecName: " + bitRed[0]); + _writer.println(margn5 + "codecNameVersion: " + bitRed[1]); + _writer.println(margn5 + "codecCreatorApplication: " + bitRed[2]); + _writer.println(margn5 + "codecCreatorApplicationVersion: " + bitRed[3]); + _writer.println(margn5 + "codecQuality: " + bitRed[4]); + _writer.println(margn5 + "dataRate: " + bitRed[5]); + _writer.println(margn5 + "dataRateMode: " + bitRed[6]); + } + } + } + } + + /* start must be non-null, but duration may be null */ + private void writeAESTimeRange( + String baseIndent, AESAudioMetadata.TimeDesc start, AESAudioMetadata.TimeDesc duration) { + final String margn1 = baseIndent + " "; + final String margn2 = margn1 + " "; + final String margn3 = margn2 + " "; + _writer.println(margn1 + "StartTime:"); + _writer.println(margn2 + "FrameCount: 30"); + _writer.println(margn2 + "TimeBase: 1000"); + _writer.println(margn2 + "VideoField: FIELD_1"); + _writer.println(margn2 + "CountingMode: NTSC_NON_DROP_FRAME"); + _writer.println(margn2 + "Hours: " + Long.toString(start.getHours())); + _writer.println(margn2 + "Minutes: " + Long.toString(start.getMinutes())); + _writer.println(margn2 + "Seconds: " + Long.toString(start.getSeconds())); + _writer.println(margn2 + "Frames: " + Long.toString(start.getFrames())); + _writer.println(margn2 + "Samples: "); + double sr = start.getSampleRate(); + if (sr == 1.0) { + sr = _sampleRate; + } + _writer.println(margn3 + "SampleRate: S" + Integer.toString((int) sr)); + _writer.println(margn3 + "NumberOfSamples: " + Long.toString(start.getSamples())); + _writer.println(margn2 + "FilmFraming: NOT_APPLICABLE"); + _writer.println(margn3 + "Type: ntscFilmFramingType"); + + if (duration != null) { + _writer.println(margn1 + "Duration:"); + _writer.println(margn2 + "FrameCount: 30"); + _writer.println(margn2 + "TimeBase: 1000"); + _writer.println(margn2 + "VideoField: FIELD_1"); + _writer.println(margn2 + "CountingMode: NTSC_NON_DROP_FRAME"); + _writer.println(margn2 + "Hours: " + Long.toString(duration.getHours())); + _writer.println(margn2 + "Minutes: " + Long.toString(duration.getMinutes())); + _writer.println(margn2 + "Seconds: " + Long.toString(duration.getSeconds())); + _writer.println(margn2 + "Frames: " + Long.toString(duration.getFrames())); + _writer.println(margn2 + "Samples: "); + sr = duration.getSampleRate(); + if (sr == 1.0) { + sr = _sampleRate; + } + _writer.println(margn3 + "SampleRate: S" + Integer.toString((int) sr)); + _writer.println(margn3 + "NumberOfSamples: " + Long.toString(duration.getSamples())); + _writer.println(margn2 + "FilmFraming: NOT_APPLICABLE"); + _writer.println(margn3 + "Type: ntscFilmFramingType"); + } + } + + /** + * Display the NISO image metadata formatted according to the MIX schema. The schema which is used + * may be 0.2 or 1.0, depending on the module parameters. + * + * @param niso NISO image metadata + */ + protected void showNisoImageMetadata(NisoImageMetadata niso, String margin, boolean rawOutput) { + if ("0.2".equals(_je.getMixVersion())) { + showNisoImageMetadata02(niso, margin, rawOutput); + } else { + showNisoImageMetadata10(niso, margin, rawOutput); + } + } + + /* + * Output the Niso image metadata, which is its own special kind of + * property. This provides a text approximation to MIX 0.2. + */ + private void showNisoImageMetadata02(NisoImageMetadata niso, String margin, boolean rawOutput) { + String margn2 = margin + " "; + + _writer.println(); + String s = niso.getMimeType(); + if (s != null) { + _writer.println(margn2 + "MIMEType: " + s); + } + if ((s = niso.getByteOrder()) != null) { + _writer.println(margn2 + "ByteOrder: " + s); + } + int n = niso.getCompressionScheme(); + if (n != NisoImageMetadata.NULL) { + _writer.println( + margn2 + + "CompressionScheme: " + + addIntegerValue( + n, + NisoImageMetadata.COMPRESSION_SCHEME, + NisoImageMetadata.COMPRESSION_SCHEME_INDEX, + rawOutput)); + } + if ((n = niso.getCompressionLevel()) != NisoImageMetadata.NULL) { + _writer.println(margn2 + "CompressionLevel: " + n); + } + if ((n = niso.getColorSpace()) != NisoImageMetadata.NULL) { + _writer.println( + margn2 + + "ColorSpace: " + + addIntegerValue( + n, NisoImageMetadata.COLORSPACE, NisoImageMetadata.COLORSPACE_INDEX, rawOutput)); + } + if ((s = niso.getProfileName()) != null) { + _writer.println(margn2 + "ProfileName: " + s); + } + if ((s = niso.getProfileURL()) != null) { + _writer.println(margn2 + "ProfileURL: " + s); + } + int[] iarray = niso.getYCbCrSubSampling(); + if (iarray != null) { + _writer.print(margn2 + "YCbCrSubSampling: " + iarray[0]); + for (int i = 1; i < iarray.length; i++) { + _writer.print(", " + iarray[i]); + } + _writer.println(); + } + if ((n = niso.getYCbCrPositioning()) != NisoImageMetadata.NULL) { + _writer.println( + margn2 + + "YCbCrPositioning: " + + addIntegerValue(n, NisoImageMetadata.YCBCR_POSITIONING, rawOutput)); + } + Rational[] rarray = niso.getYCbCrCoefficients(); + if (rarray != null) { + _writer.print(margn2 + "YCbCrCoefficients: " + addRationalValue(rarray[0], rawOutput)); + for (int i = 1; i < rarray.length; i++) { + _writer.print(", " + addRationalValue(rarray[i], rawOutput)); + } + _writer.println(); + } + rarray = niso.getReferenceBlackWhite(); + if (rarray != null) { + _writer.print(margn2 + "ReferenceBlackWhite: " + addRationalValue(rarray[0], rawOutput)); + for (int i = 1; i < rarray.length; i++) { + _writer.print(", " + addRationalValue(rarray[i], rawOutput)); + } + _writer.println(); + } + if ((n = niso.getSegmentType()) != NisoImageMetadata.NULL) { + _writer.println( + margn2 + "SegmentType: " + addIntegerValue(n, NisoImageMetadata.SEGMENT_TYPE, rawOutput)); + } + long[] larray = niso.getStripOffsets(); + if (larray != null) { + _writer.print(margn2 + "StripOffsets: " + larray[0]); + for (int i = 1; i < larray.length; i++) { + _writer.print(", " + larray[i]); + } + _writer.println(); + } + long ln = niso.getRowsPerStrip(); + if (ln != NisoImageMetadata.NULL) { + _writer.println(margn2 + "RowsPerStrip: " + ln); + } + if ((larray = niso.getStripByteCounts()) != null) { + _writer.print(margn2 + "StripByteCounts: " + larray[0]); + for (int i = 1; i < larray.length; i++) { + _writer.print(", " + larray[i]); + } + _writer.println(); + } + if ((ln = niso.getTileWidth()) != NisoImageMetadata.NULL) { + _writer.println(margn2 + "TileWidth: " + ln); + } + if ((ln = niso.getTileLength()) != NisoImageMetadata.NULL) { + _writer.println(margn2 + "TileLength: " + ln); + } + if ((larray = niso.getTileOffsets()) != null) { + _writer.print(margn2 + "TileOffsets: " + larray[0]); + for (int i = 1; i < larray.length; i++) { + _writer.print(", " + larray[i]); + } + _writer.println(); + } + if ((larray = niso.getTileByteCounts()) != null) { + _writer.print(margn2 + "TileByteCounts: " + larray[0]); + for (int i = 1; i < larray.length; i++) { + _writer.print(", " + larray[i]); + } + _writer.println(); + } + if ((n = niso.getPlanarConfiguration()) != NisoImageMetadata.NULL) { + _writer.println( + margn2 + + "PlanarConfiguration: " + + addIntegerValue(n, NisoImageMetadata.PLANAR_CONFIGURATION, rawOutput)); + } + if ((s = niso.getImageIdentifier()) != null) { + _writer.println(margn2 + "ImageIdentifier: " + s); + } + if ((s = niso.getImageIdentifierLocation()) != null) { + _writer.println(margn2 + "ImageIdentifierLocation: " + s); + } + if ((ln = niso.getFileSize()) != NisoImageMetadata.NULL) { + _writer.println(margn2 + "FileSize: " + ln); + } + if ((n = niso.getChecksumMethod()) != NisoImageMetadata.NULL) { + _writer.println( + margn2 + + "ChecksumMethod: " + + addIntegerValue(n, NisoImageMetadata.CHECKSUM_METHOD, rawOutput)); + } + if ((s = niso.getChecksumValue()) != null) { + _writer.println(margn2 + "ChecksumValue: " + s); + } + if ((n = niso.getOrientation()) != NisoImageMetadata.NULL) { + _writer.println( + margn2 + "Orientation: " + addIntegerValue(n, NisoImageMetadata.ORIENTATION, rawOutput)); + } + if ((n = niso.getDisplayOrientation()) != NisoImageMetadata.NULL) { + _writer.println( + margn2 + + "DisplayOrientation: " + + addIntegerValue(n, NisoImageMetadata.DISPLAY_ORIENTATION, rawOutput)); + } + if ((ln = niso.getXTargetedDisplayAR()) != NisoImageMetadata.NULL) { + _writer.println(margn2 + "XTargetedDisplayAR: " + ln); + } + if ((ln = niso.getYTargetedDisplayAR()) != NisoImageMetadata.NULL) { + _writer.println(margn2 + "YTargetedDisplayAR: " + ln); + } + if ((s = niso.getPreferredPresentation()) != null) { + _writer.println(margn2 + "PreferredPresentation: " + s); + } + if ((s = niso.getSourceType()) != null) { + _writer.println(margn2 + "SourceType: " + s); + } + if ((s = niso.getImageProducer()) != null) { + _writer.println(margn2 + "ImageProducer: " + s); + } + if ((s = niso.getHostComputer()) != null) { + _writer.println(margn2 + "HostComputer: " + s); + } + if ((s = niso.getOS()) != null) { + _writer.println(margn2 + "OperatingSystem: " + s); + } + if ((s = niso.getOSVersion()) != null) { + _writer.println(margn2 + "OSVersion: " + s); + } + if ((s = niso.getDeviceSource()) != null) { + _writer.println(margn2 + "DeviceSource: " + s); + } + if ((s = niso.getScannerManufacturer()) != null) { + _writer.println(margn2 + "ScannerManufacturer: " + s); + } + if ((s = niso.getScannerModelName()) != null) { + _writer.println(margn2 + "ScannerModelName: " + s); + } + if ((s = niso.getScannerModelNumber()) != null) { + _writer.println(margn2 + "ScannerModelNumber: " + s); + } + if ((s = niso.getScannerModelSerialNo()) != null) { + _writer.println(margn2 + "ScannerModelSerialNo: " + s); + } + if ((s = niso.getScanningSoftware()) != null) { + _writer.println(margn2 + "ScanningSoftware: " + s); + } + if ((s = niso.getScanningSoftwareVersionNo()) != null) { + _writer.println(margn2 + "ScanningSoftwareVersionNo: " + s); + } + double d = niso.getPixelSize(); + if (d != NisoImageMetadata.NILL) { + _writer.println(margn2 + "PixelSize: " + d); + } + if ((d = niso.getXPhysScanResolution()) != NisoImageMetadata.NILL) { + _writer.println(margn2 + "XPhysScanResolution: " + d); + } + if ((d = niso.getYPhysScanResolution()) != NisoImageMetadata.NILL) { + _writer.println(margn2 + "YPhysScanResolution: " + d); + } + + if ((s = niso.getDigitalCameraManufacturer()) != null) { + _writer.println(margn2 + "DigitalCameraManufacturer: " + s); + } + if ((s = niso.getDigitalCameraModelName()) != null) { + _writer.println(margn2 + "DigitalCameraModelName: " + s); + } + if ((s = niso.getDigitalCameraModelNumber()) != null) { + _writer.println(margn2 + "DigitalCameraModelNumber: " + s); + } + if ((s = niso.getDigitalCameraModelSerialNo()) != null) { + _writer.println(margn2 + "DigitalCameraModelSerialNo: " + s); + } + if ((d = niso.getFNumber()) != NisoImageMetadata.NILL) { + _writer.println(margn2 + "FNumber: " + d); + } + if ((d = niso.getExposureTime()) != NisoImageMetadata.NILL) { + _writer.println(margn2 + "ExposureTime: " + d); + } + Rational r; + if ((r = niso.getBrightness()) != null) { + d = r.toDouble(); + _writer.println(margn2 + "Brightness: " + d); + } + if ((r = niso.getExposureBias()) != null) { + d = r.toDouble(); + _writer.println(margn2 + "ExposureBias: " + d); + } + + double[] darray = niso.getSubjectDistance(); + if (darray != null) { + _writer.print(margn2 + "SubjectDistance: " + darray[0]); + for (int i = 1; i < darray.length; i++) { + _writer.print(", " + darray[i]); + } + _writer.println(); + } + if ((n = niso.getMeteringMode()) != NisoImageMetadata.NULL) { + _writer.println( + margn2 + + "MeteringMode: " + + addIntegerValue(n, NisoImageMetadata.METERING_MODE, rawOutput)); + } + if ((n = niso.getSceneIlluminant()) != NisoImageMetadata.NULL) { + _writer.println( + margn2 + + "SceneIlluminant: " + + addIntegerValue(n, NisoImageMetadata.METERING_MODE, rawOutput)); + } + if ((d = niso.getColorTemp()) != NisoImageMetadata.NILL) { + _writer.println(margn2 + "ColorTemp: " + d); + } + if ((d = niso.getFocalLength()) != NisoImageMetadata.NILL) { + _writer.println(margn2 + "FocalLength: " + d); + } + if ((n = niso.getFlash()) != NisoImageMetadata.NULL) { + _writer.println(margn2 + "Flash: " + addIntegerValue(n, NisoImageMetadata.FLASH, rawOutput)); + } + if ((r = niso.getFlashEnergy()) != null) { + d = r.toDouble(); + _writer.println(margn2 + "FlashEnergy: " + d); + } + if ((n = niso.getFlashReturn()) != NisoImageMetadata.NULL) { + _writer.println( + margn2 + "FlashReturn: " + addIntegerValue(n, NisoImageMetadata.FLASH_RETURN, rawOutput)); + } + if ((n = niso.getBackLight()) != NisoImageMetadata.NULL) { + _writer.println( + margn2 + "BackLight: " + addIntegerValue(n, NisoImageMetadata.BACKLIGHT, rawOutput)); + } + if ((d = niso.getExposureIndex()) != NisoImageMetadata.NILL) { + _writer.println(margn2 + "ExposureIndex: " + d); + } + if ((n = niso.getAutoFocus()) != NisoImageMetadata.NULL) { + _writer.println( + margn2 + "AutoFocus: " + addIntegerValue(n, NisoImageMetadata.AUTOFOCUS, rawOutput)); + } + if ((d = niso.getXPrintAspectRatio()) != NisoImageMetadata.NILL) { + _writer.println(margn2 + "XPrintAspectRatio: " + d); + } + if ((d = niso.getYPrintAspectRatio()) != NisoImageMetadata.NILL) { + _writer.println(margn2 + "YPrintAspectRatio: " + d); + } + + if ((n = niso.getSensor()) != NisoImageMetadata.NULL) { + _writer.println( + margn2 + "Sensor: " + addIntegerValue(n, NisoImageMetadata.SENSOR, rawOutput)); + } + if ((s = niso.getDateTimeCreated()) != null) { + _writer.println(margn2 + "DateTimeCreated: " + s); + } + if ((s = niso.getMethodology()) != null) { + _writer.println(margn2 + "Methodology: " + s); + } + if ((n = niso.getSamplingFrequencyPlane()) != NisoImageMetadata.NULL) { + _writer.println( + margn2 + + "SamplingFrequencyPlane: " + + addIntegerValue(n, NisoImageMetadata.SAMPLING_FREQUENCY_PLANE, rawOutput)); + } + if ((n = niso.getSamplingFrequencyUnit()) != NisoImageMetadata.NULL) { + _writer.println( + margn2 + + "SamplingFrequencyUnit: " + + addIntegerValue(n, NisoImageMetadata.SAMPLING_FREQUENCY_UNIT, rawOutput)); + } + r = niso.getXSamplingFrequency(); + if (r != null) { + _writer.println(margn2 + "XSamplingFrequency: " + addRationalValue(r, rawOutput)); + } + r = niso.getYSamplingFrequency(); + if (r != null) { + _writer.println(margn2 + "YSamplingFrequency: " + addRationalValue(r, rawOutput)); + } + if ((ln = niso.getImageWidth()) != NisoImageMetadata.NULL) { + _writer.println(margn2 + "ImageWidth: " + ln); + } + if ((ln = niso.getImageLength()) != NisoImageMetadata.NULL) { + _writer.println(margn2 + "ImageLength: " + ln); + } + if ((d = niso.getSourceXDimension()) != NisoImageMetadata.NILL) { + _writer.println(margn2 + "SourceXDimension: " + d); + } + if ((n = niso.getSourceXDimensionUnit()) != NisoImageMetadata.NULL) { + _writer.println( + margn2 + + "SourceXDimensionUnit: " + + addIntegerValue(n, NisoImageMetadata.SOURCE_DIMENSION_UNIT, rawOutput)); + } + if ((d = niso.getSourceYDimension()) != NisoImageMetadata.NILL) { + _writer.println(margn2 + "SourceYDimension: " + ln); + } + if ((n = niso.getSourceYDimensionUnit()) != NisoImageMetadata.NULL) { + _writer.println( + margn2 + + "SourceYDimensionUnit: " + + addIntegerValue(n, NisoImageMetadata.SOURCE_DIMENSION_UNIT, rawOutput)); + } + if ((iarray = niso.getBitsPerSample()) != null) { + _writer.print(margn2 + "BitsPerSample: " + iarray[0]); + for (int i = 1; i < iarray.length; i++) { + _writer.print(", " + iarray[i]); + } + _writer.println(); + } + if ((n = niso.getSamplesPerPixel()) != NisoImageMetadata.NULL) { + _writer.println(margn2 + "SamplesPerPixel: " + n); + } + if ((iarray = niso.getExtraSamples()) != null) { + _writer.print( + margn2 + + "ExtraSamples: " + + addIntegerValue(iarray[0], NisoImageMetadata.EXTRA_SAMPLES, rawOutput)); + for (int i = 1; i < iarray.length; i++) { + _writer.print( + ", " + addIntegerValue(iarray[i], NisoImageMetadata.EXTRA_SAMPLES, rawOutput)); + } + _writer.println(); + } + if ((s = niso.getColormapReference()) != null) { + _writer.println(margn2 + "ColormapReference: " + s); + } + if ((iarray = niso.getColormapBitCodeValue()) != null) { + _writer.print(margn2 + "ColormapBitCodeValue: " + iarray[0]); + for (int i = 1; i < iarray.length; i++) { + _writer.print(", " + iarray[i]); + } + _writer.println(); + } + if ((iarray = niso.getColormapRedValue()) != null) { + _writer.print(margn2 + "ColormapRedValue: " + iarray[0]); + for (int i = 1; i < iarray.length; i++) { + _writer.print(", " + iarray[i]); + } + _writer.println(); + } + if ((iarray = niso.getColormapGreenValue()) != null) { + _writer.print(margn2 + "ColormapGreenValue: " + iarray[0]); + for (int i = 1; i < iarray.length; i++) { + _writer.print(", " + iarray[i]); + } + _writer.println(); + } + if ((iarray = niso.getColormapBlueValue()) != null) { + _writer.print(margn2 + "ColormapBlueValue: " + iarray[0]); + for (int i = 1; i < iarray.length; i++) { + _writer.print(", " + iarray[i]); + } + _writer.println(); + } + if ((iarray = niso.getGrayResponseCurve()) != null) { + _writer.print(margn2 + "GrayResponseCurve: " + iarray[0]); + for (int i = 1; i < iarray.length; i++) { + _writer.print(", " + iarray[i]); + } + _writer.println(); + } + if ((n = niso.getGrayResponseUnit()) != NisoImageMetadata.NULL) { + _writer.println( + margn2 + + "GrayResponseUnit: " + + addIntegerValue(n, NisoImageMetadata.GRAY_RESPONSE_UNIT_02, rawOutput)); + } + r = niso.getWhitePointXValue(); + if (r != null) { + _writer.println(margn2 + "WhitePointXValue: " + addRationalValue(r, rawOutput)); + } + if ((r = niso.getWhitePointYValue()) != null) { + _writer.println(margn2 + "WhitePointXValue: " + addRationalValue(r, rawOutput)); + } + if ((r = niso.getPrimaryChromaticitiesRedX()) != null) { + _writer.println(margn2 + "PrimaryChromaticitiesRedX: " + addRationalValue(r, rawOutput)); + } + if ((r = niso.getPrimaryChromaticitiesRedY()) != null) { + _writer.println(margn2 + "PrimaryChromaticitiesRedY: " + addRationalValue(r, rawOutput)); + } + if ((r = niso.getPrimaryChromaticitiesGreenX()) != null) { + _writer.println(margn2 + "PrimaryChromaticitiesGreenX: " + addRationalValue(r, rawOutput)); + } + if ((r = niso.getPrimaryChromaticitiesGreenY()) != null) { + _writer.println(margn2 + "PrimaryChromaticitiesGreenY: " + addRationalValue(r, rawOutput)); + } + if ((r = niso.getPrimaryChromaticitiesBlueX()) != null) { + _writer.println(margn2 + "PrimaryChromaticitiesBlueX: " + addRationalValue(r, rawOutput)); + } + if ((r = niso.getPrimaryChromaticitiesBlueY()) != null) { + _writer.println(margn2 + "PrimaryChromaticitiesBlueY: " + addRationalValue(r, rawOutput)); + } + if ((n = niso.getTargetType()) != NisoImageMetadata.NULL) { + _writer.println( + margn2 + "TargetType: " + addIntegerValue(n, NisoImageMetadata.TARGET_TYPE, rawOutput)); + } + if ((s = niso.getTargetIDManufacturer()) != null) { + _writer.println(margn2 + "TargetIDManufacturer: " + s); + } + if ((s = niso.getTargetIDName()) != null) { + _writer.println(margn2 + "TargetIDName: " + s); + } + if ((s = niso.getTargetIDNo()) != null) { + _writer.println(margn2 + "TargetIDNo: " + s); + } + if ((s = niso.getTargetIDMedia()) != null) { + _writer.println(margn2 + "TargetIDMedia: " + s); + } + if ((s = niso.getImageData()) != null) { + _writer.println(margn2 + "ImageData: " + s); + } + if ((s = niso.getPerformanceData()) != null) { + _writer.println(margn2 + "PerformanceData: " + s); + } + if ((s = niso.getProfiles()) != null) { + _writer.println(margn2 + "Profiles: " + s); + } + if ((s = niso.getDateTimeProcessed()) != null) { + _writer.println(margn2 + "DateTimeProcessed: " + s); + } + if ((s = niso.getSourceData()) != null) { + _writer.println(margn2 + "SourceData: " + s); + } + if ((s = niso.getProcessingAgency()) != null) { + _writer.println(margn2 + "ProcessingAgency: " + s); + } + if ((s = niso.getProcessingSoftwareName()) != null) { + _writer.println(margn2 + "ProcessingSoftwareName: " + s); + } + if ((s = niso.getProcessingSoftwareVersion()) != null) { + _writer.println(margn2 + "ProcessingSoftwareVersion: " + s); + } + String[] sarray = niso.getProcessingActions(); + if (sarray != null) { + _writer.print(margn2 + "ProcessingActions: " + sarray[0]); + for (int i = 1; i < sarray.length; i++) { + _writer.print(", " + sarray[i]); + } + _writer.println(); + } + } + + private void showNisoImageMetadata10(NisoImageMetadata niso, String margin, boolean rawOutput) { + String margn2 = margin + " "; + String s; + long ln; + _writer.println(); + if ((s = niso.getImageIdentifier()) != null) { + _writer.println(margn2 + "ImageIdentifier: " + s); + } + if ((ln = niso.getFileSize()) != NisoImageMetadata.NULL) { + _writer.println(margn2 + "FileSize: " + ln); + } + if ((s = niso.getMimeType()) != null) { + _writer.println(margn2 + "FormatName: " + s); + } + if ((s = niso.getByteOrder()) != null) { + // Convert strings to MIX 1.0 form + if (s.startsWith("big")) { + s = "big_endian"; + } else if (s.startsWith("little")) { + s = "little_endian"; + } + _writer.println(margn2 + "ByteOrder: " + s); + } + int n = niso.getCompressionScheme(); + if (n != NisoImageMetadata.NULL) { + _writer.println( + margn2 + + "CompressionScheme: " + + addIntegerValue( + n, + NisoImageMetadata.COMPRESSION_SCHEME, + NisoImageMetadata.COMPRESSION_SCHEME_INDEX, + rawOutput)); + } + if ((n = niso.getCompressionLevel()) != NisoImageMetadata.NULL) { + _writer.println(margn2 + "CompressionLevel: " + n); + } + if ((n = niso.getChecksumMethod()) != NisoImageMetadata.NULL) { + _writer.println( + margn2 + + "ChecksumMethod: " + + addIntegerValue(n, NisoImageMetadata.CHECKSUM_METHOD, rawOutput)); + } + if ((s = niso.getChecksumValue()) != null) { + _writer.println(margn2 + "ChecksumValue: " + s); + } + if ((ln = niso.getImageWidth()) != NisoImageMetadata.NULL) { + _writer.println(margn2 + "ImageWidth: " + ln); + } + if ((ln = niso.getImageLength()) != NisoImageMetadata.NULL) { + _writer.println(margn2 + "ImageHeight: " + ln); + } + if ((n = niso.getColorSpace()) != NisoImageMetadata.NULL) { + _writer.println( + margn2 + + "ColorSpace: " + + addIntegerValue( + n, NisoImageMetadata.COLORSPACE, NisoImageMetadata.COLORSPACE_INDEX, rawOutput)); + } + if ((s = niso.getProfileName()) != null) { + _writer.println(margn2 + "ICCProfileName: " + s); + } + if ((s = niso.getProfileURL()) != null) { + _writer.println(margn2 + "ICCProfileURL: " + s); + } + int[] iarray = niso.getYCbCrSubSampling(); + if (iarray != null) { + _writer.print(margn2 + "YCbCrSubSampling: " + iarray[0]); + for (int i = 1; i < iarray.length; i++) { + _writer.print(", " + iarray[i]); + } + _writer.println(); + } + Rational[] rarray = niso.getYCbCrCoefficients(); + if (rarray != null) { + _writer.print(margn2 + "YCbCrCoefficients: " + addRationalValue(rarray[0], rawOutput)); + for (int i = 1; i < rarray.length; i++) { + _writer.print(", " + addRationalValue(rarray[i], rawOutput)); + } + _writer.println(); + } + rarray = niso.getReferenceBlackWhite(); + if (rarray != null) { + _writer.print(margn2 + "ReferenceBlackWhite: " + addRationalValue(rarray[0], rawOutput)); + for (int i = 1; i < rarray.length; i++) { + _writer.print(", " + addRationalValue(rarray[i], rawOutput)); + } + _writer.println(); + } + if ((s = niso.getSourceType()) != null) { + _writer.println(margn2 + "SourceType: " + s); + } + s = niso.getSourceID(); + if (s != null) { + _writer.println(margn2 + "SourceID" + s); + } + double d; + if ((d = niso.getSourceXDimension()) != NisoImageMetadata.NILL) { + _writer.println(margn2 + "SourceXDimension: " + d); + } + if ((n = niso.getSourceXDimensionUnit()) != NisoImageMetadata.NULL) { + _writer.println( + margn2 + + "SourceXDimensionUnit: " + + addIntegerValue(n, NisoImageMetadata.SOURCE_DIMENSION_UNIT, rawOutput)); + } + if ((d = niso.getSourceYDimension()) != NisoImageMetadata.NILL) { + _writer.println(margn2 + "SourceYDimension: " + ln); + } + if ((n = niso.getSourceYDimensionUnit()) != NisoImageMetadata.NULL) { + _writer.println( + margn2 + + "SourceYDimensionUnit: " + + addIntegerValue(n, NisoImageMetadata.SOURCE_DIMENSION_UNIT, rawOutput)); + } + if ((s = niso.getDateTimeCreated()) != null) { + _writer.println(margn2 + "DateTimeCreated: " + s); + } + if ((s = niso.getImageProducer()) != null) { + _writer.println(margn2 + "ImageProducer: " + s); + } + if ((s = niso.getDeviceSource()) != null) { + _writer.println(margn2 + "CaptureDevice: " + s); + } + if ((s = niso.getScannerManufacturer()) != null) { + _writer.println(margn2 + "ScannerManufacturer: " + s); + } + if ((s = niso.getScannerModelName()) != null) { + _writer.println(margn2 + "ScannerModelName: " + s); + } + if ((s = niso.getScannerModelNumber()) != null) { + _writer.println(margn2 + "ScannerModelNumber: " + s); + } + if ((s = niso.getScannerModelSerialNo()) != null) { + _writer.println(margn2 + "ScannerModelSerialNo: " + s); + } + double xres = niso.getXPhysScanResolution(); + double yres = niso.getYPhysScanResolution(); + if (xres != NisoImageMetadata.NULL && yres != NisoImageMetadata.NULL) { + double res = (xres > yres ? xres : yres); + _writer.println(margn2 + "MaximumOpticalResolution: " + Double.toString(res)); + } + if ((s = niso.getScanningSoftware()) != null) { + _writer.println(margn2 + "ScanningSoftware: " + s); + } + if ((s = niso.getScanningSoftwareVersionNo()) != null) { + _writer.println(margn2 + "ScanningSoftwareVersionNo: " + s); + } + if ((s = niso.getDigitalCameraManufacturer()) != null) { + _writer.println(margn2 + "DigitalCameraManufacturer: " + s); + } + if ((s = niso.getDigitalCameraModelName()) != null) { + _writer.println(margn2 + "DigitalCameraModelName: " + s); + } + if ((s = niso.getDigitalCameraModelNumber()) != null) { + _writer.println(margn2 + "DigitalCameraModelNumber: " + s); + } + if ((s = niso.getDigitalCameraModelSerialNo()) != null) { + _writer.println(margn2 + "DigitalCameraModelSerialNo: " + s); + } + if ((d = niso.getFNumber()) != NisoImageMetadata.NILL) { + _writer.println(margn2 + "FNumber: " + _format.format(d)); + } + if ((s = niso.getExifVersion()) != null) { + _writer.println(margn2 + "ExifVersion: " + s); + } + Rational r; + if ((r = niso.getMaxApertureValue()) != null) { + _writer.println(margn2 + "MaxApertureValue: " + addRationalValue(r, rawOutput)); + } + if ((d = niso.getExposureTime()) != NisoImageMetadata.NILL) { + _writer.println(margn2 + "ExposureTime: " + _format.format(d)); + } + if ((n = niso.getExposureProgram()) != NisoImageMetadata.NULL) { + _writer.println( + margn2 + + "ExposureProgram: " + + addIntegerValue(n, NisoImageMetadata.EXPOSURE_PROGRAM, rawOutput)); + } + if ((r = niso.getBrightness()) != null) { + _writer.println(margn2 + "BrightnessValue: " + addRationalValue(r, rawOutput)); + } + if ((r = niso.getExposureBias()) != null) { + _writer.println(margn2 + "ExposureBiasValue: " + addRationalValue(r, rawOutput)); + } + double[] darray = niso.getSubjectDistance(); + if (darray != null) { + _writer.print(margn2 + "SubjectDistance: " + darray[0]); + for (int i = 1; i < darray.length; i++) { + _writer.print(", " + darray[i]); + } + _writer.println(); + } + if ((n = niso.getMeteringMode()) != NisoImageMetadata.NULL) { + _writer.println( + margn2 + + "MeteringMode: " + + addIntegerValue(n, NisoImageMetadata.METERING_MODE, rawOutput)); + } + if ((n = niso.getFlash()) != NisoImageMetadata.NULL) { + _writer.println(margn2 + "Flash: " + addIntegerValue(n, NisoImageMetadata.FLASH, rawOutput)); + } + if ((d = niso.getFocalLength()) != NisoImageMetadata.NILL) { + _writer.println(margn2 + "FocalLength: " + d); + } + if ((r = niso.getFlashEnergy()) != null) { + _writer.println(margn2 + "FlashEnergy: " + addRationalValue(r, rawOutput)); + } + if ((n = niso.getBackLight()) != NisoImageMetadata.NULL) { + _writer.println( + margn2 + "BackLight: " + addIntegerValue(n, NisoImageMetadata.BACKLIGHT, rawOutput)); + } + if ((d = niso.getExposureIndex()) != NisoImageMetadata.NILL) { + _writer.println(margn2 + "ExposureIndex: " + d); + } + if ((n = niso.getAutoFocus()) != NisoImageMetadata.NULL) { + _writer.println( + margn2 + "AutoFocus: " + addIntegerValue(n, NisoImageMetadata.AUTOFOCUS, rawOutput)); + } + if ((d = niso.getXPrintAspectRatio()) != NisoImageMetadata.NILL) { + _writer.println(margn2 + "XPrintAspectRatio: " + d); + } + if ((d = niso.getYPrintAspectRatio()) != NisoImageMetadata.NILL) { + _writer.println(margn2 + "YPrintAspectRatio: " + d); + } + if ((n = niso.getOrientation()) != NisoImageMetadata.NULL) { + _writer.println( + margn2 + "Orientation: " + addIntegerValue(n, NisoImageMetadata.ORIENTATION, rawOutput)); + } + if ((s = niso.getMethodology()) != null) { + _writer.println(margn2 + "Methodology: " + s); + } + if ((n = niso.getSamplingFrequencyPlane()) != NisoImageMetadata.NULL) { + _writer.println( + margn2 + + "SamplingFrequencyPlane: " + + addIntegerValue(n, NisoImageMetadata.SAMPLING_FREQUENCY_PLANE, rawOutput)); + } + if ((n = niso.getSamplingFrequencyUnit()) != NisoImageMetadata.NULL) { + _writer.println( + margn2 + + "SamplingFrequencyUnit: " + + addIntegerValue(n, NisoImageMetadata.SAMPLING_FREQUENCY_UNIT, rawOutput)); + } + r = niso.getXSamplingFrequency(); + if (r != null) { + _writer.println(margn2 + "XSamplingFrequency: " + addRationalValue(r, rawOutput)); + } + r = niso.getYSamplingFrequency(); + if (r != null) { + _writer.println(margn2 + "YSamplingFrequency: " + addRationalValue(r, rawOutput)); + } + if ((iarray = niso.getBitsPerSample()) != null) { + _writer.print(margn2 + "BitsPerSample: " + iarray[0]); + for (int i = 1; i < iarray.length; i++) { + _writer.print(", " + iarray[i]); + } + _writer.println(); + _writer.println(margn2 + "BitsPerSampleUnit: integer"); + } + if ((n = niso.getSamplesPerPixel()) != NisoImageMetadata.NULL) { + _writer.println(margn2 + "SamplesPerPixel: " + n); + } + if ((iarray = niso.getExtraSamples()) != null) { + _writer.print( + margn2 + + "ExtraSamples: " + + addIntegerValue(iarray[0], NisoImageMetadata.EXTRA_SAMPLES, rawOutput)); + for (int i = 1; i < iarray.length; i++) { + _writer.print( + ", " + addIntegerValue(iarray[i], NisoImageMetadata.EXTRA_SAMPLES, rawOutput)); + } + _writer.println(); + } + if ((s = niso.getColormapReference()) != null) { + _writer.println(margn2 + "ColormapReference: " + s); + } + // The MIX 1.0 schema requires the letter "N" as the value of the + // gray response curve, which is clearly a bug. We deviate from + // the bug here. + if ((iarray = niso.getGrayResponseCurve()) != null) { + _writer.print(margn2 + "GrayResponseCurve: " + iarray[0]); + for (int i = 1; i < iarray.length; i++) { + _writer.print(", " + iarray[i]); + } + _writer.println(); + } + if ((n = niso.getGrayResponseUnit()) != NisoImageMetadata.NULL) { + _writer.println( + margn2 + + "GrayResponseUnit: " + + addIntegerValue(n, NisoImageMetadata.GRAY_RESPONSE_UNIT_02, rawOutput)); + } + r = niso.getWhitePointXValue(); + if (r != null) { + _writer.println(margn2 + "WhitePointXValue: " + addRationalValue(r, rawOutput)); + } + if ((r = niso.getWhitePointYValue()) != null) { + _writer.println(margn2 + "WhitePointXValue: " + addRationalValue(r, rawOutput)); + } + if ((r = niso.getPrimaryChromaticitiesRedX()) != null) { + _writer.println(margn2 + "PrimaryChromaticitiesRedX: " + addRationalValue(r, rawOutput)); + } + if ((r = niso.getPrimaryChromaticitiesRedY()) != null) { + _writer.println(margn2 + "PrimaryChromaticitiesRedY: " + addRationalValue(r, rawOutput)); + } + if ((r = niso.getPrimaryChromaticitiesGreenX()) != null) { + _writer.println(margn2 + "PrimaryChromaticitiesGreenX: " + addRationalValue(r, rawOutput)); + } + if ((r = niso.getPrimaryChromaticitiesGreenY()) != null) { + _writer.println(margn2 + "PrimaryChromaticitiesGreenY: " + addRationalValue(r, rawOutput)); + } + if ((r = niso.getPrimaryChromaticitiesBlueX()) != null) { + _writer.println(margn2 + "PrimaryChromaticitiesBlueX: " + addRationalValue(r, rawOutput)); + } + if ((r = niso.getPrimaryChromaticitiesBlueY()) != null) { + _writer.println(margn2 + "PrimaryChromaticitiesBlueY: " + addRationalValue(r, rawOutput)); + } + if ((n = niso.getTargetType()) != NisoImageMetadata.NULL) { + _writer.println( + margn2 + "TargetType: " + addIntegerValue(n, NisoImageMetadata.TARGET_TYPE, rawOutput)); + } + if ((s = niso.getTargetIDManufacturer()) != null) { + _writer.println(margn2 + "TargetIDManufacturer: " + s); + } + if ((s = niso.getTargetIDName()) != null) { + _writer.println(margn2 + "TargetIDName: " + s); + } + if ((s = niso.getTargetIDNo()) != null) { + _writer.println(margn2 + "TargetIDNo: " + s); + } + if ((s = niso.getTargetIDMedia()) != null) { + _writer.println(margn2 + "TargetIDMedia: " + s); + } + if ((s = niso.getImageData()) != null) { + _writer.println(margn2 + "ExternalTarget: " + s); + } + if ((s = niso.getPerformanceData()) != null) { + _writer.println(margn2 + "PerformanceData: " + s); + } + if ((s = niso.getSourceData()) != null) { + _writer.println(margn2 + "SourceData: " + s); + } + if ((s = niso.getProcessingAgency()) != null) { + _writer.println(margn2 + "ProcessingAgency: " + s); + } + if ((s = niso.getProcessingSoftwareName()) != null) { + _writer.println(margn2 + "ProcessingSoftwareName: " + s); + } + if ((s = niso.getProcessingSoftwareVersion()) != null) { + _writer.println(margn2 + "ProcessingSoftwareVersion: " + s); + } + if ((s = niso.getOS()) != null) { + _writer.println(margn2 + "OperatingSystem: " + s); + } + if ((s = niso.getOSVersion()) != null) { + _writer.println(margn2 + "OSVersion: " + s); + } + String[] sarray = niso.getProcessingActions(); + if (sarray != null) { + _writer.print(margn2 + "ProcessingActions: " + sarray[0]); + for (int i = 1; i < sarray.length; i++) { + _writer.print(", " + sarray[i]); + } + _writer.println(); + } + } + + private String addIntegerValue(int value, String[] labels, boolean rawOutput) { + String s = null; + if (!rawOutput && 0 <= value && value < labels.length) { + s = labels[value]; + } else { + s = Integer.toString(value); + } + + return s; + } + + private String addIntegerValue(int value, String[] labels, int[] index, boolean rawOutput) { + String s = null; + boolean outOfRange = false; + if (!rawOutput) { + int n = -1; + for (int i = 0; i < index.length; i++) { + if (value == index[i]) { + n = i; + break; + } + } + if (n > -1) { + s = labels[n]; + } else { + outOfRange = true; + } + } + if (rawOutput || outOfRange) { + s = Integer.toString(value); + } + + return s; + } + + private String addRationalValue(Rational r, boolean rawOutput) { + String s = null; + if (!rawOutput) { + s = _format.format(r.toDouble()); + } else { + s = r.toString(); + } + + return s; + } } diff --git a/jhove-core/src/main/java/edu/harvard/hul/ois/jhove/handler/XmlHandler.java b/jhove-core/src/main/java/edu/harvard/hul/ois/jhove/handler/XmlHandler.java index 83ce7e00a..765c93714 100644 --- a/jhove-core/src/main/java/edu/harvard/hul/ois/jhove/handler/XmlHandler.java +++ b/jhove-core/src/main/java/edu/harvard/hul/ois/jhove/handler/XmlHandler.java @@ -1,34 +1,22 @@ -/********************************************************************** - * Jhove - JSTOR/Harvard Object Validation Environment - * Copyright 2003-2009 by JSTOR and the President and Fellows of Harvard College +/** + * ******************************************************************** Jhove - JSTOR/Harvard Object + * Validation Environment Copyright 2003-2009 by JSTOR and the President and Fellows of Harvard + * College * - * This program 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 2 of the License, or (at - * your option) any later version. + *

This program 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 2 + * of the License, or (at your option) any later version. * - * This program 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. + *

This program 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. * - * You should have received a copy of the GNU Lesser General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 - * USA - **********************************************************************/ - + *

You should have received a copy of the GNU Lesser General Public License along with this + * program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA ******************************************************************** + */ package edu.harvard.hul.ois.jhove.handler; -import java.text.NumberFormat; -import java.util.Date; -import java.util.Iterator; -import java.util.List; -import java.util.ListIterator; -import java.util.Locale; -import java.util.Map; -import java.util.Set; - import edu.harvard.hul.ois.jhove.AESAudioMetadata; import edu.harvard.hul.ois.jhove.Agent; import edu.harvard.hul.ois.jhove.App; @@ -51,4392 +39,3943 @@ import edu.harvard.hul.ois.jhove.TextMDMetadata; import edu.harvard.hul.ois.jhove.Utils; import edu.harvard.hul.ois.jhove.messages.JhoveMessages; +import java.text.NumberFormat; +import java.util.Date; +import java.util.Iterator; +import java.util.List; +import java.util.ListIterator; +import java.util.Locale; +import java.util.Map; +import java.util.Set; /** * OutputHandler for XML output. * - * @see Schema - * for JHOVE XML output + * @see Schema for JHOVE + * XML output */ -public class XmlHandler extends edu.harvard.hul.ois.jhove.HandlerBase - -{ - /****************************************************************** - * PRIVATE CLASS FIELDS. - ******************************************************************/ - - /** Thread safe formatter for doubles */ - private static final ThreadLocal formatters = new ThreadLocal() { - @Override - protected NumberFormat initialValue() { - NumberFormat _format = NumberFormat.getInstance(Locale.ROOT); - _format.setGroupingUsed(false); - _format.setMinimumFractionDigits(0); - - return _format; - } - }; - - /** Handler name. */ - private static final String NAME = "XML"; - - /** Handler release identifier. */ - private static final String RELEASE = "1.9"; - - /** Handler release date. */ - private static final int[] DATE = { 2019, 10, 18 }; - - /** Handler informative note. */ - private static final String NOTE = "This output handler is defined by the XML Schema " - + "https://schema.openpreservation.org/ois/xml/xsd/jhove/jhove.xsd"; - - /** Handler rights statement. */ - private static final String RIGHTS = "Derived from software Copyright 2004-2011 " - + "by the President and Fellows of Harvard College. " - + "Version 1.8 release by Open Preservation Foundation. " - + "Released under the GNU Lesser General Public License."; - - /** Localized line separator character. */ - private final static String EOL = System.getProperty("line.separator"); - - /** Schema version. */ - private static final String SCHEMA_VERSION = "1.8"; - - /****************************************************************** - * PRIVATE INSTANCE FIELDS. - ******************************************************************/ - - /* Sample rate. */ - private double _sampleRate; - - /****************************************************************** - * CLASS CONSTRUCTOR. - ******************************************************************/ - - /** - * Creates an XmlHandler. - */ - public XmlHandler() { - super(NAME, RELEASE, DATE, NOTE, RIGHTS); - _vendor = Agent.harvardInstance(); - } - - /** Constructor for use by subclasses. */ - public XmlHandler(String name, String release, int[] date, String note, - String rights) { - super(name, release, date, note, rights); - _vendor = Agent.harvardInstance(); - } - - /****************************************************************** - * PUBLIC INSTANCE METHODS. - ******************************************************************/ - - /** - * Outputs minimal information about the application - */ - @Override - public void show() { - _level--; - } - - /** - * Outputs detailed information about the application, including - * configuration, available modules and handlers, etc. - */ - @Override - public void show(App app) { - String margin = getIndent(++_level); - String margn2 = margin + " "; - String margn3 = margn2 + " "; - - _writer.println(margin + elementStart("app")); - String[][] attrs = { { "date", date.format(_je.getDate()) } }; - _writer.println(margn2 + element("api", attrs, app.getRelease())); - String configFile = _je.getConfigFile(); - if (configFile != null) { - _writer.println(margn2 + element("configuration", configFile)); - } - String s = _je.getSaxClass(); - if (s != null) { - _writer.println(margn2 + element("saxParser", s)); - } - s = _je.getJhoveHome(); - if (s != null) { - _writer.println(margn2 + element("jhoveHome", s)); - } - s = _je.getEncoding(); - if (s != null) { - _writer.println(margn2 + element("encoding", s)); - } - s = _je.getTempDirectory(); - if (s != null) { - _writer.println(margn2 + element("tempDirectory", s)); - } - _writer.println(margn2 - + element("bufferSize", Integer.toString(_je.getBufferSize()))); - _writer.println(margn2 + elementStart("modules")); - Iterator iter = _je.getModuleMap().keySet().iterator(); - while (iter.hasNext()) { - Module module = _je.getModule(iter.next()); - String[][] attr2 = { { "release", module.getRelease() } }; - _writer.println(margn3 + element("module", attr2, module.getName())); - } - _writer.println(margn2 + elementEnd("modules")); - _writer.println(margn2 + elementStart("outputHandlers")); - iter = _je.getHandlerMap().keySet().iterator(); - while (iter.hasNext()) { - OutputHandler handler = _je.getHandler(iter.next()); - String[][] attr2 = { { "release", handler.getRelease() } }; - _writer.println(margn3 - + element("outputHandler", attr2, handler.getName())); - } - _writer.println(margn2 + elementEnd("outputHandlers")); - _writer.println(margn2 + element("usage", app.getUsage())); - _writer.println(margn2 + element("rights", app.getRights())); - _writer.println(margin + elementEnd("app")); - _level--; - } - - /** - * Outputs information about the OutputHandler specified in the parameter - */ - @Override - public void show(OutputHandler handler) { - String margin = getIndent(++_level); - String margn2 = margin + " "; - _writer.println(margin + elementStart("handler")); - _writer.println(margn2 + element("name", handler.getName())); - _writer.println(margn2 + element("release", handler.getRelease())); - _writer.println(margn2 - + element("date", date.format(handler.getDate()))); - List list = handler.getSpecification(); - int n = list.size(); - if (n > 0) { - _writer.println(margn2 + elementStart("specifications")); - ++_level; - for (int i = 0; i < n; i++) { - showDocument(list.get(i)); - } - --_level; - _writer.println(margn2 + elementEnd("specifications")); - } - Agent vendor = handler.getVendor(); - if (vendor != null) { - showAgent(vendor, "Vendor"); - } - String s; - if ((s = handler.getNote()) != null) { - _writer.println(margn2 + element("note", s)); - } - if ((s = handler.getRights()) != null) { - _writer.println(margn2 + element("rights", s)); - } - _writer.println(margin + elementEnd("handler")); - _level--; - } - - /** - * Outputs information about a Module - */ - @Override - public void show(Module module) { - String margin = getIndent(++_level); - String margn2 = margin + " "; - String margn3 = margn2 + " "; - - _writer.println(margin + elementStart("module")); - _writer.println(margn2 + element("name", module.getName())); - _writer.println(margn2 + element("release", module.getRelease())); - _writer.println(margn2 - + element("date", HandlerBase.date.format(module.getDate()))); - String[] ss = module.getFormat(); - if (ss.length > 0) { - _writer.println(margn2 + elementStart("formats")); - for (int i = 0; i < ss.length; i++) { - _writer.println(margn3 + element("format", ss[i])); - } - _writer.println(margn2 + elementEnd("formats")); - } - String s = module.getCoverage(); - if (s != null) { - _writer.println(margn2 + element("coverage", s)); - } - ss = module.getMimeType(); - if (ss.length > 0) { - _writer.println(margn2 + elementStart("mimeTypes")); - for (int i = 0; i < ss.length; i++) { - _writer.println(margn3 + element("mimeType", ss[i])); - } - ; - _writer.println(margn2 + elementEnd("mimeTypes")); - } - List list1 = module.getSignature(); - int n = list1.size(); - if (n > 0) { - _writer.println(margn2 + elementStart("signatures")); - ++_level; - for (int i = 0; i < n; i++) { - showSignature(list1.get(i)); - } - _level--; - _writer.println(margn2 + elementEnd("signatures")); - } - List list2 = module.getSpecification(); - n = list2.size(); - if (n > 0) { - _writer.println(margn2 + elementStart("specifications")); - ++_level; - for (int i = 0; i < n; i++) { - showDocument(list2.get(i)); - } - --_level; - _writer.println(margn2 + elementEnd("specifications")); - } - List ftr = module.getFeatures(); - if (ftr != null && !ftr.isEmpty()) { - _writer.println(margn2 + elementStart("features")); - Iterator iter = ftr.iterator(); - while (iter.hasNext()) { - s = iter.next(); - _writer.println(margn3 + element("feature", s)); - } - _writer.println(margn2 + elementEnd("features")); - } - _writer.println(margn2 + elementStart("methodology")); - if ((s = module.getWellFormedNote()) != null) { - _writer.println(margn3 + element("wellFormed", s)); - } - if ((s = module.getValidityNote()) != null) { - _writer.println(margn3 + element("validity", s)); - } - if ((s = module.getRepInfoNote()) != null) { - _writer.println(margn3 + element("repInfo", s)); - } - _writer.println(margn2 + elementEnd("methodology")); - Agent vendor = module.getVendor(); - if (vendor != null) { - showAgent(vendor, "Vendor"); - } - if ((s = module.getNote()) != null) { - _writer.println(margn2 + element("note", s)); - } - if ((s = module.getRights()) != null) { - _writer.println(margn2 + element("rights", s)); - } - _writer.println(margin + elementEnd("module")); - _level--; - } - - /** - * Outputs the information contained in a RepInfo object - */ - @Override - public void show(RepInfo info) { - String margin = getIndent(++_level); - String margn2 = margin + " "; - String margn3 = margn2 + " "; - - Module module = info.getModule(); - _logger.info("Reporting RepInfo"); - if (_je.getSignatureFlag()) { - _logger.info("Checking signatures only"); - } - String[][] attrs = { { "uri", cleanURIString(info.getUri()) } }; - _writer.println(margin + elementStart("repInfo", attrs)); - if (module != null) { - String[][] attr2 = { { "release", module.getRelease() }, - { "date", date.format(module.getDate()) } }; - _writer.println(margn2 - + element("reportingModule", attr2, module.getName())); - } - /* - * else { String [][] attr2 = { {"severity", "error"} }; _writer.println - * (margn2 + element ("message", attr2, - * "file not found or not readable")); } - */ - Date date = info.getCreated(); - if (date != null) { - _writer.println(margn2 + element("created", toDateTime(date))); - } - date = info.getLastModified(); - if (date != null) { - _writer.println(margn2 + element("lastModified", toDateTime(date))); - } - long size = info.getSize(); - if (size > -1) { - _writer.println(margn2 + element("size", Long.toString(size))); - } - String s = info.getFormat(); - if (s != null) { - _writer.println(margn2 + element("format", s)); - } - s = info.getVersion(); - if (s != null) { - _writer.println(margn2 + element("version", s)); - } - String wfStr; - if (!_je.getSignatureFlag()) { - switch (info.getWellFormed()) { - case RepInfo.TRUE: - wfStr = "Well-Formed"; - break; - - case RepInfo.FALSE: - wfStr = "Not well-formed"; - break; - - default: - wfStr = "Unknown"; - break; - } - // If it's well-formed, append validity information - if (info.getWellFormed() == RepInfo.TRUE) { - switch (info.getValid()) { - case RepInfo.TRUE: - wfStr += " and valid"; - break; - - case RepInfo.FALSE: - wfStr += ", but not valid"; - break; - - // case UNDETERMINED: add nothing - } - } - _logger.info("Validity/WF status: " + wfStr); - _writer.println(margn2 + element("status", wfStr)); - } else { - // If we aren't checking signatures, we still need to say something. - switch (info.getWellFormed()) { - case RepInfo.TRUE: - wfStr = "Well-Formed"; - break; - - default: - wfStr = "Not well-formed"; - break; - } - _writer.println(margn2 + element("status", wfStr)); - } - - List list1 = info.getSigMatch(); - int n = list1.size(); - if (n > 0) { - _writer.println(margn2 + elementStart("sigMatch")); - _level++; - for (int i = 0; i < n; i++) { - _writer.println(margn2 - + element("module", list1.get(i))); - } - _level--; - _writer.println(margn2 + elementEnd("sigMatch")); - } - - List list2 = info.getMessage(); - n = list2.size(); - if (n > 0) { - _writer.println(margn2 + elementStart("messages")); - _level++; - for (int i = 0; i < n; i++) { - showMessage(list2.get(i)); - } - _level--; - _writer.println(margn2 + elementEnd("messages")); - } - s = info.getMimeType(); - if (s != null) { - _writer.println(margn2 + element("mimeType", s)); - } - - List list3 = info.getProfile(); - n = list3.size(); - if (n > 0) { - _writer.println(margn2 + elementStart("profiles")); - for (int i = 0; i < n; i++) { - _writer.println(margn3 - + element("profile", list3.get(i))); - } - _writer.println(margn2 + elementEnd("profiles")); - } - - Map map = info.getProperty(); - if (map != null) { - if (map.size() > 0) { - _writer.println(margn2 + elementStart("properties")); - Iterator iter = map.keySet().iterator(); - while (iter.hasNext()) { - String key = iter.next(); - Property property = info.getProperty(key); - showProperty(property); - } - _writer.println(margn2 + elementEnd("properties")); - } - } - - List list4 = info.getChecksum(); - n = list4.size(); - if (n > 0) { - _writer.println(margn2 + elementStart("checksums")); - _level++; - for (int i = 0; i < n; i++) { - showChecksum(list4.get(i)); - } - _level--; - _writer.println(margn2 + elementEnd("checksums")); - } - - s = info.getNote(); - if (s != null) { - _writer.println(margn2 + element("note", s)); - } - - _writer.println(margin + elementEnd("repInfo")); - _level--; - } - - /****************************************************************** - * PRIVATE INSTANCE METHODS. - ******************************************************************/ - - protected void showAgent(Agent agent, String label) { - String margin = getIndent(++_level); - String margn2 = margin + " "; - - String[][] attrs = { { "type", label } }; - _writer.println(margin + elementStart("agent", attrs)); - _writer.println(margn2 + element("name", agent.getName())); - _writer.println(margn2 + element("type", agent.getType().toString())); - String s = agent.getAddress(); - if (s != null) { - _writer.println(margn2 + element("address", s)); - } - if ((s = agent.getTelephone()) != null) { - _writer.println(margn2 + element("telephone", s)); - } - if ((s = agent.getFax()) != null) { - _writer.println(margn2 + element("fax", s)); - } - if ((s = agent.getEmail()) != null) { - _writer.println(margn2 + element("email", s)); - } - if ((s = agent.getWeb()) != null) { - _writer.println(margn2 + element("web", s)); - } - _writer.println(margin + elementEnd("agent")); - _level--; - } - - protected void showChecksum(Checksum checksum) { - String margin = getIndent(++_level); - - String[][] attrs = { { "type", checksum.getType().toString() } }; - _writer.println(margin - + element("checksum", attrs, checksum.getValue())); - - _level--; - } - - protected void showDocument(Document document) { - String margin = getIndent(++_level); - String margn2 = margin + " "; - - // String [][] attrs = { {"type", label} }; - _writer.println(margin + elementStart("specification")); - _writer.println(margn2 + element("title", document.getTitle())); - _writer.println(margn2 + element("type", document.getType().toString())); - List list1 = document.getAuthor(); - int n = list1.size(); - if (n > 0) { - _writer.println(margn2 + elementStart("authors")); - ++_level; - for (int i = 0; i < n; i++) { - showAgent(list1.get(i), "Author"); - } - _level--; - _writer.println(margn2 + elementEnd("authors")); - } - List list2 = document.getPublisher(); - n = list2.size(); - if (n > 0) { - ++_level; - _writer.println(margn2 + elementStart("publishers")); - for (int i = 0; i < n; i++) { - showAgent(list2.get(i), "Publisher"); - } - _writer.println(margn2 + elementEnd("publishers")); - _level--; - } - String s = document.getEdition(); - if (s != null) { - _writer.println(margn2 + element("edition", s)); - } - if ((s = document.getDate()) != null) { - _writer.println(margn2 + element("date", s)); - } - if ((s = document.getEnumeration()) != null) { - _writer.println(margn2 + element("enumeration", s)); - } - if ((s = document.getPages()) != null) { - _writer.println(margn2 + element("pages", s)); - } - List list3 = document.getIdentifier(); - n = list3.size(); - if (n > 0) { - _writer.println(margn2 + elementStart("identifiers")); - ++_level; - for (int i = 0; i < n; i++) { - showIdentifier(list3.get(i)); - } - _level--; - _writer.println(margn2 + elementEnd("identifiers")); - } - if ((s = document.getNote()) != null) { - _writer.println(margn2 + element("note", s)); - } - _writer.println(margin + elementEnd("specification")); - _level--; - } - - /** - * Do the final output. This should be in a suitable format for including - * multiple files between the header and the footer, and the XML of the - * header and footer must balance out. - */ - @Override - public void showFooter() { - String margin = getIndent(_level--); - _writer.println(margin + elementEnd("jhove")); - - _writer.flush(); - } - - /** - * Do the initial output. This should be in a suitable format for including - * multiple files between the header and the footer, and the XML of the - * header and footer must balance out. - */ - @Override - public void showHeader() { - String margin = getIndent(++_level); - String margn2 = margin + " "; - - if (_encoding != null) { - _writer.println(margin + xmlDecl(_encoding)); - } else { - _writer.println(margin + xmlDecl()); - } - - String[][] attrs = { - { "xmlns:xsi", "http://www.w3.org/2001/XMLSchema-instance" }, - { "xmlns", - "http://schema.openpreservation.org/ois/xml/ns/jhove" }, - { - "xsi:schemaLocation", - "http://schema.openpreservation.org/ois/xml/ns/jhove " - + "https://schema.openpreservation.org/ois/xml/xsd/jhove/" - + SCHEMA_VERSION + "/jhove.xsd" }, - { "name", _app.getName() }, { "release", _app.getRelease() }, - { "date", HandlerBase.date.format(_app.getDate()) } }; - _writer.println(margin + elementStart("jhove", attrs)); - _writer.println(margn2 + element("date", toDateTime(new Date()))); - } - - protected void showIdentifier(Identifier identifier) { - String margin = getIndent(++_level); - String margn2 = margin + " "; - - _writer.println(margin + elementStart("identifier")); - _writer.println(margn2 + element("value", identifier.getValue())); - _writer.println(margn2 - + element("type", identifier.getType().toString())); - String note = identifier.getNote(); - if (note != null) { - _writer.println(margn2 + element("note", note)); - } - _writer.println(margin + elementEnd("identifier")); - _level--; - } - - protected void showMessage(Message message) { - String margin = getIndent(++_level); - String[][] attrs = new String[4][]; - boolean hasAttr = false; - attrs[0] = new String[] { "subMessage", null }; - attrs[1] = new String[] { "offset", null }; - attrs[2] = new String[] { "severity", null }; - attrs[3] = new String[] { "id", null }; - - String submsg = message.getSubMessage(); - if (submsg != null) { - attrs[0][1] = submsg; - hasAttr = true; - } - long offset = message.getOffset(); - if (offset > -1) { - attrs[1][1] = Long.toString(offset); - hasAttr = true; - } - if (!message.getPrefix().isEmpty()) { - attrs[2][1] = message.getPrefix().toLowerCase(); - hasAttr = true; - } - String id = message.getJhoveMessage().getId(); - if (!(id == null || id.isEmpty() || id.equals(JhoveMessages.NO_ID))) { - attrs[3][1] = message.getId(); - hasAttr = true; - } - if (hasAttr) { - _writer.println(margin - + element("message", attrs, message.getMessage())); - } else { - _writer.println(margin + element("message", message.getMessage())); - } - _level--; - } - - protected void showSignature(Signature signature) { - String margin = getIndent(++_level); - String margin1 = margin + " "; - String sigValue; - - _writer.println(margin + elementStart("signature")); - if (signature.isStringValue()) { - sigValue = signature.getValueString(); - } else { - sigValue = signature.getValueHexString(); - } - _writer.println(margin1 - + element("type", signature.getType().toString())); - _writer.println(margin1 + element("value", sigValue)); - if (signature.getType().equals(SignatureType.MAGIC)) { - if (((InternalSignature) signature).hasFixedOffset()) { - _writer.println(margin1 - + element( - "offset", - "0x" - + Integer - .toHexString(((InternalSignature) signature) - .getOffset()))); - } - } - String note = signature.getNote(); - if (note != null) { - _writer.println(margin1 + element("note", note)); - - } - String use = signature.getUse().toString(); - if (use != null) { - _writer.println(margin1 + element("use", use)); - } - _writer.println(margin + elementEnd("signature")); - _level--; - } - - /* Do special conversions on values as needed. */ - protected String valueToString(Object obj) { - if (obj instanceof Date) { - return toDateTime((Date) obj); - } - return obj.toString(); - } - - protected void showProperty(Property property) { - String margin = getIndent(++_level); - String margn2 = margin + " "; - String margn3 = margn2 + " "; - String margn4 = margn3 + " "; - - PropertyArity arity = property.getArity(); - PropertyType type = property.getType(); - - // If the property would generate an empty element, don't output it, - // as this could result in a schema violation. - if (Utils.isPropertyEmpty(property, arity)) - return; - - boolean valueIsProperty = PropertyType.PROPERTY.equals(type); - boolean valueIsNiso = PropertyType.NISOIMAGEMETADATA.equals(type); - boolean valueIsAes = PropertyType.AESAUDIOMETADATA.equals(type); - boolean valueIsTextMD = PropertyType.TEXTMDMETADATA.equals(type); - - String[][] propAttrs = new String[2][]; - propAttrs[0] = new String[] { "arity", arity.toString() }; - propAttrs[1] = new String[] { "type", type.toString() }; - _writer.println(margn2 + elementStart("property")); - _writer.println(margn3 + element("name", property.getName())); - _writer.println(margn3 + elementStart("values", propAttrs)); - if (arity.equals(PropertyArity.SCALAR)) { - /* Just a single value */ - if (valueIsProperty) { - showProperty((Property) property.getValue()); - } else if (valueIsNiso) { - _writer.println(margn4 + elementStart("value")); - showNisoImageMetadata((NisoImageMetadata) property.getValue()); - _writer.println(margn4 + elementEnd("value")); - } else if (valueIsAes) { - _writer.println(margn4 + elementStart("value")); - showAESAudioMetadata((AESAudioMetadata) property.getValue()); - _writer.println(margn4 + elementEnd("value")); - } else if (valueIsTextMD) { - _writer.println(margn4 + elementStart("value")); - showTextMDMetadata((TextMDMetadata) property.getValue()); - _writer.println(margn4 + elementEnd("value")); - } else { - _writer.println(margn4 - + element("value", property.getValue().toString())); - } - } else if (arity.equals(PropertyArity.LIST)) { - List propList = (List) property.getValue(); - ListIterator iter = propList.listIterator(); - while (iter.hasNext()) { - Object val = iter.next(); - if (valueIsProperty) { - showProperty((Property) val); - } else if (valueIsNiso) { - _writer.println(margn4 + elementStart("value")); - showNisoImageMetadata((NisoImageMetadata) property - .getValue()); - _writer.println(margn4 + elementEnd("value")); - } else if (valueIsAes) { - _writer.println(margn4 + elementStart("value")); - showAESAudioMetadata((AESAudioMetadata) property.getValue()); - _writer.println(margn4 + elementEnd("value")); - } else if (valueIsTextMD) { - _writer.println(margn4 + elementStart("value")); - showTextMDMetadata((TextMDMetadata) property.getValue()); - _writer.println(margn4 + elementEnd("value")); - } else { - _writer.println(margn4 - + element("value", valueToString(val))); - } - } - } else if (arity.equals(PropertyArity.MAP)) { - /* - * For a map, the key is the "key" attribute of its corresponding - * value - */ - Map propMap = (Map) property.getValue(); - Iterator keyIter = propMap.keySet().iterator(); - while (keyIter.hasNext()) { - Object key = keyIter.next(); - String keystr = key.toString(); - Object val = propMap.get(key); - String[][] attrs = new String[1][]; - String[] keyAttr = new String[2]; - keyAttr[0] = "key"; - keyAttr[1] = keystr; - attrs[0] = keyAttr; - if (valueIsProperty) { - Property pval = (Property) val; - // If the key equals the property name, suppress the key - if (pval.getName().equals(keystr)) { - _writer.print(margn4 + elementStart("value")); - } else { - _writer.print(margn4 + elementStart("value", attrs)); - } - showProperty(pval); - _writer.println(margn4 + elementEnd("value")); - } else if (valueIsNiso) { - _writer.println(margn4 + elementStart("value")); - showNisoImageMetadata((NisoImageMetadata) val); - _writer.println(margn4 + elementEnd("value")); - } else if (valueIsAes) { - _writer.println(margn4 + elementStart("value")); - showAESAudioMetadata((AESAudioMetadata) val); - _writer.println(margn4 + elementEnd("value")); - } else if (valueIsTextMD) { - _writer.println(margn4 + elementStart("value")); - showTextMDMetadata((TextMDMetadata) val); - _writer.println(margn4 + elementEnd("value")); - } else { - _writer.println(margn4 - + element("value", attrs, valueToString(val))); - } - } - } else if (arity.equals(PropertyArity.SET)) { - Set propSet = (Set) property.getValue(); - Iterator iter = propSet.iterator(); - while (iter.hasNext()) { - Object val = iter.next(); - if (valueIsProperty) { - showProperty((Property) val); - } else { - _writer.println(margn4 - + element("value", valueToString(val))); - } - } - } else if (arity.equals(PropertyArity.ARRAY)) { - showArrayProperty(property, margn4); - } - _writer.println(margn3 + elementEnd("values")); - _writer.println(margn2 + elementEnd("property")); - --_level; - } - - /* - * The array property has so many special cases of its own that we break it - * out of showProperty - */ - protected void showArrayProperty(Property property, String margin) { - boolean[] boolArray = null; - byte[] byteArray = null; - char[] charArray = null; - java.util.Date[] dateArray = null; - double[] doubleArray = null; - float[] floatArray = null; - int[] intArray = null; - long[] longArray = null; - Object[] objArray = null; - Property[] propArray = null; - short[] shortArray = null; - String[] stringArray = null; - Rational[] rationalArray = null; - NisoImageMetadata[] nisoArray = null; - AESAudioMetadata[] aesArray = null; - TextMDMetadata[] textMDArray = null; - int n = 0; - - PropertyType propType = property.getType(); - if (PropertyType.BOOLEAN.equals(propType)) { - boolArray = (boolean[]) property.getValue(); - n = boolArray.length; - } else if (PropertyType.BYTE.equals(propType)) { - byteArray = (byte[]) property.getValue(); - n = byteArray.length; - } else if (PropertyType.CHARACTER.equals(propType)) { - charArray = (char[]) property.getValue(); - n = charArray.length; - } else if (PropertyType.DATE.equals(propType)) { - dateArray = (java.util.Date[]) property.getValue(); - n = dateArray.length; - } else if (PropertyType.DOUBLE.equals(propType)) { - doubleArray = (double[]) property.getValue(); - n = doubleArray.length; - } else if (PropertyType.FLOAT.equals(propType)) { - floatArray = (float[]) property.getValue(); - n = floatArray.length; - } else if (PropertyType.INTEGER.equals(propType)) { - intArray = (int[]) property.getValue(); - n = intArray.length; - } else if (PropertyType.LONG.equals(propType)) { - longArray = (long[]) property.getValue(); - n = longArray.length; - } else if (PropertyType.OBJECT.equals(propType)) { - objArray = (Object[]) property.getValue(); - n = objArray.length; - } else if (PropertyType.SHORT.equals(propType)) { - shortArray = (short[]) property.getValue(); - n = shortArray.length; - } else if (PropertyType.STRING.equals(propType)) { - stringArray = (String[]) property.getValue(); - n = stringArray.length; - } else if (PropertyType.RATIONAL.equals(propType)) { - rationalArray = (Rational[]) property.getValue(); - n = rationalArray.length; - } else if (PropertyType.PROPERTY.equals(propType)) { - propArray = (Property[]) property.getValue(); - n = propArray.length; - } else if (PropertyType.NISOIMAGEMETADATA.equals(propType)) { - nisoArray = (NisoImageMetadata[]) property.getValue(); - n = nisoArray.length; - } else if (PropertyType.AESAUDIOMETADATA.equals(propType)) { - aesArray = (AESAudioMetadata[]) property.getValue(); - n = aesArray.length; - } else if (PropertyType.TEXTMDMETADATA.equals(propType)) { - textMDArray = (TextMDMetadata[]) property.getValue(); - n = textMDArray.length; - } - - for (int i = 0; i < n; i++) { - String elem; - if (PropertyType.BOOLEAN.equals(propType)) { - elem = String.valueOf(boolArray[i]); - } else if (PropertyType.BYTE.equals(propType)) { - elem = String.valueOf(byteArray[i]); - } else if (PropertyType.CHARACTER.equals(propType)) { - elem = String.valueOf(charArray[i]); - } else if (PropertyType.DATE.equals(propType)) { - elem = dateArray[i].toString(); - } else if (PropertyType.DOUBLE.equals(propType)) { - elem = String.valueOf(doubleArray[i]); - } else if (PropertyType.FLOAT.equals(propType)) { - elem = String.valueOf(floatArray[i]); - } else if (PropertyType.INTEGER.equals(propType)) { - elem = String.valueOf(intArray[i]); - } else if (PropertyType.LONG.equals(propType)) { - elem = String.valueOf(longArray[i]); - } else if (PropertyType.OBJECT.equals(propType)) { - elem = valueToString(objArray[i]); - } else if (PropertyType.SHORT.equals(propType)) { - elem = String.valueOf(shortArray[i]); - } else if (PropertyType.STRING.equals(propType)) { - elem = stringArray[i]; - } else if (PropertyType.RATIONAL.equals(propType)) { - elem = rationalArray[i].toString(); - } else if (PropertyType.PROPERTY.equals(propType)) { - showProperty(propArray[i]); - continue; - } else if (PropertyType.NISOIMAGEMETADATA.equals(propType)) { - showNisoImageMetadata(nisoArray[i]); - continue; - } else if (PropertyType.AESAUDIOMETADATA.equals(propType)) { - showAESAudioMetadata(aesArray[i]); - continue; - } else if (PropertyType.TEXTMDMETADATA.equals(propType)) { - showTextMDMetadata(textMDArray[i]); - continue; - } else - elem = ""; - _writer.println(margin + element("value", elem)); - } - } - - /** - * Display the text metadata formatted according to the textMD schema (see - * http://www.loc.gov/standards/textMD). - * - * @param textMD - * textMD text metadata - */ - protected void showTextMDMetadata(TextMDMetadata textMD) { - String margin = getIndent(++_level); - String margn2 = margin + " "; - String margn3 = margn2 + " "; - - String[][] attrs = { - { "xmlns:textmd", TextMDMetadata.NAMESPACE }, - { "xmlns:xsi", "http://www.w3.org/2001/XMLSchema-instance" }, - { - "xsi:schemaLocation", - TextMDMetadata.NAMESPACE + " " - + TextMDMetadata.DEFAULT_LOCATION }, }; - _writer.println(margin + elementStart("textmd:textMD", attrs)); - _writer.println(margn2 + elementStart("textmd:character_info")); - _writer.println(margn3 + element("textmd:charset", textMD.getCharset())); - _writer.println(margn3 - + element("textmd:byte_order", textMD.getByte_orderString())); - _writer.println(margn3 - + element("textmd:byte_size", textMD.getByte_size())); - if ("variable".equals(textMD.getCharacter_size())) { - String[][] attrs1 = { { "encoding", textMD.getCharset() } }; - _writer.println(margn3 - + element("textmd:character_size", attrs1, "variable")); - } else if (textMD.getCharacter_size() != null - && textMD.getCharacter_size().length() != 0) { - _writer.println(margn3 - + element("textmd:character_size", - textMD.getCharacter_size())); - } - _writer.println(margn3 - + element("textmd:linebreak", textMD.getLinebreakString())); - _writer.println(margn2 + elementEnd("textmd:character_info")); - if (textMD.getLanguage() != null && textMD.getLanguage().length() != 0) { - _writer.println(margn2 - + element("textmd:language", textMD.getLanguage())); - } - if (textMD.getMarkup_basis() != null - && textMD.getMarkup_basis().length() != 0) { - if (textMD.getMarkup_basis_version() != null) { - String[][] attrs1 = { { "version", - textMD.getMarkup_basis_version() } }; - _writer.println(margn2 - + element("textmd:markup_basis", attrs1, - textMD.getMarkup_basis())); - } else { - _writer.println(margn2 - + element("textmd:markup_basis", - textMD.getMarkup_basis())); - } - } - if (textMD.getMarkup_language() != null - && textMD.getMarkup_language().length() != 0) { - if (textMD.getMarkup_language_version() != null) { - String[][] attrs1 = { { "version", - textMD.getMarkup_language_version() } }; - _writer.println(margn2 - + element("textmd:markup_language", attrs1, - textMD.getMarkup_language())); - } else { - _writer.println(margn2 - + element("textmd:markup_language", - textMD.getMarkup_language())); - } - } - _writer.println(margin + elementEnd("textmd:textMD")); - _level--; - - } - - /** - * Display the NISO image metadata formatted according to the MIX schema. - * The schema which is used may be 0.2 or 1.0 or 2.0, depending on the - * module parameters. - * - * @param niso - * NISO image metadata - */ - protected void showNisoImageMetadata(NisoImageMetadata niso) { - if ("0.2".equals(_je.getMixVersion())) { - showNisoImageMetadata02(niso); - } else if ("1.0".equals(_je.getMixVersion())) { - showNisoImageMetadata10(niso); - } else { - showNisoImageMetadata20(niso); - } - } - - /** - * Display the NISO image metadata formatted according to the MIX 0.2 - * schema. - */ - protected void showNisoImageMetadata02(NisoImageMetadata niso) { - String margin = getIndent(++_level); - - String[][] attrs = { - { "xmlns:mix", "http://www.loc.gov/mix/" }, - { "xmlns:xsi", "http://www.w3.org/2001/XMLSchema-instance" }, - { "xsi:schemaLocation", - "http://www.loc.gov/mix/ http://www.loc.gov/mix/mix02.xsd" } }; - _writer.println(margin + elementStart("mix:mix", attrs)); - - showNisoBasicImageParameters02(niso, margin); - showNisoImageCreation02(niso, margin); - showNisoImagingPerformanceAssessment02(niso, margin); - showNisoChangeHistory02(niso, margin); - - _writer.println(margin + elementEnd("mix:mix")); - - _level--; - } - - /* - * The NISO Metadata output is split into multiple functions so that they're - * merely outrageously big rather than disgustingly big - */ - /* Top level element 1 of 4: BasicImageParameters */ - protected void showNisoBasicImageParameters02(NisoImageMetadata niso, - String margin) { - String margn2 = margin + " "; - String margn3 = margn2 + " "; - String margn4 = margn3 + " "; - String margn5 = margn4 + " "; - String margn6 = margn5 + " "; - - _writer.println(margn2 + elementStart("mix:BasicImageParameters")); - - // Here things get rather deeply nested, and we want to be able - // to back out if it turns out that we really have no content to - // deliver. So rather than writing directly to the writer, we - // write to a StringBuffer that may or may not get used. - StringBuffer fBuf = new StringBuffer(margn3 - + elementStart("mix:Format") + EOL); - boolean useFBuf = false; // set to true if some data show up - String s = niso.getMimeType(); - if (s != null) { - fBuf.append(margn4 + element("mix:MIMEType", s) + EOL); - useFBuf = true; - } - if ((s = niso.getByteOrder()) != null) { - fBuf.append(margn4 + element("mix:ByteOrder", s) + EOL); - useFBuf = true; - } - int comp = niso.getCompressionScheme(); - int level = niso.getCompressionLevel(); - if (comp != NisoImageMetadata.NULL || level != NisoImageMetadata.NULL) { - fBuf.append(margn4 + elementStart("mix:Compression") + EOL); - if (comp != NisoImageMetadata.NULL) { - fBuf.append(margn5 - + element("mix:CompressionScheme", - Integer.toString(comp)) + EOL); - } - if (level != NisoImageMetadata.NULL) { - fBuf.append(margn5 - + element("mix:CompressionLevel", - Integer.toString(level)) + EOL); - } - fBuf.append(margn4 + elementEnd("mix:Compression") + EOL); - useFBuf = true; - } - // Nest photometric interpretation tentative buffer in fBuf - StringBuffer piBuf = new StringBuffer(margn4 - + elementStart("mix:PhotometricInterpretation") + EOL); - boolean usePIBuf = false; - int n = niso.getColorSpace(); - if (n != NisoImageMetadata.NULL) { - piBuf.append(margn5 - + element("mix:ColorSpace", Integer.toString(n)) + EOL); - usePIBuf = true; - } - String s2 = niso.getProfileURL(); - if ((s = niso.getProfileName()) != null || s2 != null) { - piBuf.append(margn5 + elementStart("mix:ICCProfile") + EOL); - if (s != null) { - piBuf.append(margn6 + element("mix:ProfileName", s) + EOL); - } - if (s2 != null) { - piBuf.append(margn6 + element("mix:ProfileURL", s2) + EOL); - } - piBuf.append(margn5 + elementEnd("mix:ICCProfile") + EOL); - usePIBuf = true; - } - int[] iarray = niso.getYCbCrSubSampling(); - if (iarray != null) { - piBuf.append(margn5 - + element("mix:YCbCrSubSampling", integerArray(iarray)) - + EOL); - usePIBuf = true; - } - if ((n = niso.getYCbCrPositioning()) != NisoImageMetadata.NULL) { - piBuf.append(margn5 - + element("mix:YCbCrPositioning", Integer.toString(n)) - + EOL); - usePIBuf = true; - } - Rational[] rarray = niso.getYCbCrCoefficients(); - if (rarray != null) { - piBuf.append(margn5 - + element("mix:YCbCrCoefficients", rationalArray(rarray)) - + EOL); - usePIBuf = true; - } - if ((rarray = niso.getReferenceBlackWhite()) != null) { - piBuf.append(margn5 - + element("mix:ReferenceBlackWhite", rationalArray(rarray)) - + EOL); - usePIBuf = true; - } - piBuf.append(margn4 + elementEnd("mix:PhotometricInterpretation") + EOL); - if (usePIBuf) { - fBuf.append(piBuf); - useFBuf = true; - } - - // Now a tentative buffer for the Segments element - StringBuffer segBuf = new StringBuffer(margn4 - + elementStart("mix:Segments") + EOL); - boolean useSegBuf = false; - if ((n = niso.getSegmentType()) != NisoImageMetadata.NULL) { - segBuf.append(margn5 - + element("mix:SegmentType", Integer.toString(n)) + EOL); - useSegBuf = true; - } - long[] larray = niso.getStripOffsets(); - if (larray != null) { - segBuf.append(margn5 - + element("mix:StripOffsets", longArray(larray)) + EOL); - useSegBuf = true; - } - long ln = niso.getRowsPerStrip(); - if (ln != NisoImageMetadata.NULL) { - segBuf.append(margn5 - + element("mix:RowsPerStrip", Long.toString(ln)) + EOL); - useSegBuf = true; - } - if ((larray = niso.getStripByteCounts()) != null) { - segBuf.append(margn5 - + element("mix:StripByteCounts", longArray(larray)) + EOL); - useSegBuf = true; - } - if ((ln = niso.getTileWidth()) != NisoImageMetadata.NULL) { - segBuf.append(margn5 + element("mix:TileWidth", Long.toString(ln)) - + EOL); - useSegBuf = true; - } - if ((ln = niso.getTileLength()) != NisoImageMetadata.NULL) { - segBuf.append(margn5 + element("mix:TileLength", Long.toString(ln)) - + EOL); - useSegBuf = true; - } - if ((larray = niso.getTileOffsets()) != null) { - segBuf.append(margn5 - + element("mix:TileOffsets", longArray(larray)) + EOL); - useSegBuf = true; - } - if ((larray = niso.getTileByteCounts()) != null) { - segBuf.append(margn5 - + element("mix:TileByteCounts", longArray(larray)) + EOL); - useSegBuf = true; - } - segBuf.append(margn4 + elementEnd("mix:Segments") + EOL); - if (useSegBuf) { - fBuf.append(segBuf); - useFBuf = true; - } - if ((n = niso.getPlanarConfiguration()) != NisoImageMetadata.NULL) { - fBuf.append(margn4 - + element("mix:PlanarConfiguration", Integer.toString(n)) - + EOL); - } - fBuf.append(margn3 + elementEnd("mix:Format") + EOL); - if (useFBuf) { - _writer.print(fBuf.toString()); - } - - // Now a tentative buffer for the File element. It's actually - // pretty safe that this will have some content, but I'd rather - // be consistent. Besides, it's a good test of the pattern. - StringBuffer fileBuf = new StringBuffer(margn3 - + elementStart("mix:File") + EOL); - boolean useFileBuf = false; - s = niso.getImageIdentifier(); - if (s != null) { - fileBuf.append(margn4 + element("mix:ImageIdentifier", s) + EOL); - useFileBuf = true; - } - ln = niso.getFileSize(); - if (ln != NisoImageMetadata.NULL) { - fileBuf.append(margn4 + element("mix:FileSize", Long.toString(ln)) - + EOL); - useFileBuf = true; - } - n = niso.getChecksumMethod(); - s = niso.getChecksumValue(); - if (n != NisoImageMetadata.NULL || s != null) { - fileBuf.append(margn4 + elementStart("mix:Checksum") + EOL); - if (n != NisoImageMetadata.NULL) { - fileBuf.append(margn5 - + element("mix:ChecksumMethod", Integer.toString(n)) - + EOL); - } - if (s != null) { - fileBuf.append(margn5 + element("mix:ChecksumValue", s) + EOL); - } - fileBuf.append(margn4 + elementEnd("mix:Checksum") + EOL); - useFileBuf = true; - } - n = niso.getOrientation(); - if (n != NisoImageMetadata.NULL) { - fileBuf.append(margn4 - + element("mix:Orientation", Integer.toString(n)) + EOL); - useFileBuf = true; - } - n = niso.getDisplayOrientation(); - if (n != NisoImageMetadata.NULL) { - fileBuf.append(margn4 - + element("mix:DisplayOrientation", Integer.toString(n)) - + EOL); - useFileBuf = true; - } - ln = niso.getXTargetedDisplayAR(); - long ln2 = niso.getYTargetedDisplayAR(); - if (ln != NisoImageMetadata.NULL || ln2 != NisoImageMetadata.NULL) { - fileBuf.append(margn4 + elementStart("mix:TargetedDisplayAR") + EOL); - if (ln != NisoImageMetadata.NULL) { - fileBuf.append(margn5 - + element("mix:XTargetedDisplayAR", Long.toString(ln)) - + EOL); - } - if (ln2 != NisoImageMetadata.NULL) { - fileBuf.append(margn5 - + element("mix:YTargetedDisplayAR", Long.toString(ln2)) - + EOL); - } - fileBuf.append(margn4 + elementEnd("mix:TargetedDisplayAR") + EOL); - useFileBuf = true; - } - - fileBuf.append(margn3 + elementEnd("mix:File") + EOL); - if (useFileBuf) { - _writer.print(fileBuf.toString()); - } - _writer.println(margn2 + elementEnd("mix:BasicImageParameters")); - - } - - /* Top level element 2 of 4: ImageCreation */ - protected void showNisoImageCreation02(NisoImageMetadata niso, String margin) { - String margn2 = margin + " "; - String margn3 = margn2 + " "; - String margn4 = margn3 + " "; - String margn5 = margn4 + " "; - String margn6 = margn5 + " "; - - _writer.println(margn2 + elementStart("mix:ImageCreation")); - String s = niso.getSourceType(); - if (s != null) { - _writer.println(margn3 + element("mix:SourceType", s)); - } - s = niso.getSourceID(); - if (s != null) { - _writer.println(margn3 + element("mix:SourceID", s)); - } - s = niso.getImageProducer(); - if (s != null) { - _writer.println(margn3 + element("mix:ImageProducer", s)); - } - - StringBuffer hostBuf = new StringBuffer(margn3 - + elementStart("mix:Host") + EOL); - boolean useHostBuf = false; - s = niso.getHostComputer(); - if (s != null) { - hostBuf.append(margn4 + element("mix:HostComputer", s) + EOL); - useHostBuf = true; - } - s = niso.getOS(); - if (s != null) { - hostBuf.append(margn4 + element("mix:OperatingSystem", s) + EOL); - useHostBuf = true; - } - s = niso.getOSVersion(); - if (s != null) { - hostBuf.append(margn4 + element("mix:OSVersion", s) + EOL); - useHostBuf = true; - } - hostBuf.append(margn3 + elementEnd("mix:Host") + EOL); - if (useHostBuf) { - _writer.print(hostBuf); - } - - s = niso.getDeviceSource(); - if (s != null) { - _writer.println(margn3 + element("mix:DeviceSource", s)); - } - - // Here things get rather deeply nested, and we want to be able - // to back out if it turns out that we really have no content to - // deliver. So rather than writing directly to the writer, we - // write to a StringBuffer that may or may not get used. - StringBuffer sscBuf = new StringBuffer(margn3 - + elementStart("mix:ScanningSystemCapture") + EOL); - boolean useSSCBuf = false; // set to true if we find interesting data. - StringBuffer sshBuf = new StringBuffer(margn4 - + elementStart("mix:ScanningSystemHardware") + EOL); - boolean useSSHBuf = false; - s = niso.getScannerManufacturer(); - if (s != null) { - sshBuf.append(margn5 + element("mix:ScannerManufacturer", s) + EOL); - useSSHBuf = true; - } - s = niso.getScannerModelName(); - String s1 = niso.getScannerModelNumber(); - String s2 = niso.getScannerModelSerialNo(); - if (s != null || s1 != null || s2 != null) { - sshBuf.append(margn5 + elementStart("mix:ScannerModel") + EOL); - if (s != null) { - sshBuf.append(margn6 + element("mix:ScannerModelName", s) + EOL); - useSSHBuf = true; - } - if (s1 != null) { - sshBuf.append(margn6 + element("mix:ScannerModelNumber", s1) - + EOL); - useSSHBuf = true; - } - if (s2 != null) { - sshBuf.append(margn6 + element("mix:ScannerModelSerialNo ", s2) - + EOL); - useSSHBuf = true; - } - sshBuf.append(margn5 + elementEnd("mix:ScannerModel") + EOL); - } - sshBuf.append(margn4 + elementEnd("mix:ScanningSystemHardware") + EOL); - if (useSSHBuf) { - // There's some ScanningSystemHardware content, and therefore some - // ScanningSystemCapture content; keep the element. - sscBuf.append(sshBuf); - useSSCBuf = true; - } - - // Build a tentative buffer for scanning system software - StringBuffer sssBuf = new StringBuffer(margn4 - + elementStart("mix:ScanningSystemSoftware") + EOL); - boolean useSSSBuf = false; - s = niso.getScanningSoftware(); - if (s != null) { - sssBuf.append(margn5 + element("mix:ScanningSoftware", s) + EOL); - useSSSBuf = true; - } - s = niso.getScanningSoftwareVersionNo(); - if (s != null) { - sssBuf.append(margn5 + element("mix:ScanningSoftwareVersionNo", s) - + EOL); - useSSSBuf = true; - } - sssBuf.append(margn4 + elementEnd("mix:ScanningSystemSoftware") + EOL); - if (useSSSBuf) { - // There's some ScanningSystemSoftware content, and therefore some - // ScanningSystemCapture content; keep the element. - sscBuf.append(sssBuf); - useSSCBuf = true; - } - - // Build a tentative buffer for scanner capture settings - StringBuffer scsBuf = new StringBuffer(margn4 - + elementStart("mix:ScannerCaptureSettings") + EOL); - boolean useSCSBuf = false; - double d = niso.getPixelSize(); - if (d != NisoImageMetadata.NILL) { - scsBuf.append(margn5 - + element("mix:PixelSize", formatters.get().format(d)) - + EOL); - useSCSBuf = true; - } - d = niso.getXPhysScanResolution(); - double d1 = niso.getYPhysScanResolution(); - if (d != NisoImageMetadata.NILL || d1 != NisoImageMetadata.NILL) { - scsBuf.append(margn5 + elementStart("mix:PhysScanResolution") + EOL); - if (d != NisoImageMetadata.NILL) { - scsBuf.append(margn6 - + element("mix:XphysScanResolution", formatters.get() - .format(d)) + EOL); - } - if (d1 != NisoImageMetadata.NILL) { - scsBuf.append(margn6 - + element("mix:YphysScanResolution", formatters.get() - .format(d1)) + EOL); - } - scsBuf.append(margn5 + elementEnd("mix:PhysScanResolution") + EOL); - useSCSBuf = true; - } - scsBuf.append(margn4 + elementEnd("mix:ScannerCaptureSettings") + EOL); - if (useSCSBuf) { - sscBuf.append(scsBuf); - useSSCBuf = true; - } - sscBuf.append(margn3 + elementEnd("mix:ScanningSystemCapture") + EOL); - - // Finally! Do we use any of this stuff we just went through? - if (useSSCBuf) { - _writer.print(sscBuf.toString()); - } - - // Same deal for digital camera capture; put the element in a tentative - // StringBuffer and then decide if it's non-trivial. - StringBuffer dccBuf = new StringBuffer(margn3 - + elementStart("mix:DigitalCameraCapture") + EOL); - boolean useDCCBuf = false; - s = niso.getDigitalCameraManufacturer(); - if (s != null) { - dccBuf.append(margn4 + element("mix:DigitalCameraManufacturer", s) - + EOL); - useDCCBuf = true; - } - s = niso.getDigitalCameraModelName(); - if (s != null) { - dccBuf.append(margn4 + element("mix:DigitalCameraModel", s) + EOL); - useDCCBuf = true; - } - - dccBuf.append(margn3 + elementEnd("mix:DigitalCameraCapture") + EOL); - if (useDCCBuf) { - _writer.print(dccBuf.toString()); - } - - // Same tentative buffer deal for camera capture settings. - StringBuffer ccsBuf = new StringBuffer(margn3 - + elementStart("mix:CameraCaptureSettings") + EOL); - boolean useCCSBuf = false; - d = niso.getFNumber(); - if (d != NisoImageMetadata.NILL) { - ccsBuf.append(margn4 - + element("mix:FNumber", formatters.get().format(d)) + EOL); - useCCSBuf = true; - } - d = niso.getExposureTime(); - if (d != NisoImageMetadata.NILL) { - ccsBuf.append(margn4 - + element("mix:ExposureTime", formatters.get().format(d)) - + EOL); - useCCSBuf = true; - } - Rational r = niso.getBrightness(); - if (r != null) { - d = r.toDouble(); - ccsBuf.append(margn4 - + element("mix:Brightness", formatters.get().format(d)) - + EOL); - useCCSBuf = true; - } - r = niso.getExposureBias(); - if (r != null) { - d = r.toDouble(); - ccsBuf.append(margn4 - + element("mix:ExposureBias", formatters.get().format(d)) - + EOL); - useCCSBuf = true; - } - double[] darray = niso.getSubjectDistance(); - if (darray != null) { - ccsBuf.append(margn4 - + element("mix:SubjectDistance", doubleArray(darray)) + EOL); - useCCSBuf = true; - } - int n = niso.getMeteringMode(); - if (n != NisoImageMetadata.NULL) { - s = meteringModeToString(n); - if (s.startsWith("Center weighted")) { - s = "Center weighted Average"; - } - ccsBuf.append(margn4 + element("mix:MeteringMode", s) + EOL); - useCCSBuf = true; - } - n = niso.getSceneIlluminant(); - if (n != NisoImageMetadata.NULL) { - ccsBuf.append(margn4 - + element("mix:SceneIlluminant", Integer.toString(n)) + EOL); - useCCSBuf = true; - } - d = niso.getColorTemp(); - if (d != NisoImageMetadata.NILL) { - ccsBuf.append(margn4 - + element("mix:ColorTemp", formatters.get().format(d)) - + EOL); - useCCSBuf = true; - } - d = niso.getFocalLength(); - if (d != NisoImageMetadata.NILL) { - ccsBuf.append(margn4 - + element("mix:FocalLength", formatters.get().format(d)) - + EOL); - useCCSBuf = true; - } - n = niso.getFlash(); - if (n != NisoImageMetadata.NULL) { - // First bit (0 = Flash did not fire, 1 = Flash fired) - int firstBit = n & 1; - ccsBuf.append(margn4 - + element("mix:Flash", NisoImageMetadata.FLASH[firstBit]) - + EOL); - useCCSBuf = true; - } - r = niso.getFlashEnergy(); - if (r != null) { - d = r.toDouble(); - ccsBuf.append(margn4 - + element("mix:FlashEnergy", formatters.get().format(d)) - + EOL); - useCCSBuf = true; - } - n = niso.getFlashReturn(); - if (n != NisoImageMetadata.NULL) { - ccsBuf.append(margn4 - + element("mix:FlashReturn", Integer.toString(n)) + EOL); - useCCSBuf = true; - } - n = niso.getBackLight(); - if (n != NisoImageMetadata.NULL) { - ccsBuf.append(margn4 - + element("mix:BackLight", Integer.toString(n)) + EOL); - useCCSBuf = true; - } - d = niso.getExposureIndex(); - if (d != NisoImageMetadata.NILL) { - ccsBuf.append(margn4 - + element("mix:ExposureIndex", formatters.get().format(d)) - + EOL); - useCCSBuf = true; - } - n = niso.getAutoFocus(); - if (n != NisoImageMetadata.NULL) { - ccsBuf.append(margn4 - + element("mix:AutoFocus", Integer.toString(n)) + EOL); - useCCSBuf = true; - } - d = niso.getXPrintAspectRatio(); - d1 = niso.getYPrintAspectRatio(); - if (d != NisoImageMetadata.NILL || d1 != NisoImageMetadata.NILL) { - ccsBuf.append(margn4 + elementStart("mix:PrintAspectRatio") + EOL); - if (d != NisoImageMetadata.NILL) { - ccsBuf.append(margn5 - + element("mix:XPrintAspectRatio", formatters.get() - .format(d)) + EOL); - } - if (d1 != NisoImageMetadata.NILL) { - ccsBuf.append(margn5 - + element("mix:YPrintAspectRatio", formatters.get() - .format(d1)) + EOL); - ccsBuf.append(margn4 + elementEnd("mix:PrintAspectRatio") + EOL); - useCCSBuf = true; - } - } - ccsBuf.append(margn3 + elementEnd("mix:CameraCaptureSettings") + EOL); - if (useCCSBuf) { - _writer.print(ccsBuf.toString()); - } - - // Finally we get a relative breather without having to check nested - // elements. - n = niso.getSensor(); - if (n != NisoImageMetadata.NULL) { - _writer.println(margn3 + element("mix:Sensor", Integer.toString(n))); - } - s = niso.getDateTimeCreated(); - if (s != null) { - _writer.println(margn3 + element("mix:DateTimeCreated", s)); - } - s = niso.getMethodology(); - if (s != null) { - _writer.println(margn3 + element("mix:Methodology", s)); - } - - _writer.println(margn2 + elementEnd("mix:ImageCreation")); - } - - /* Top level element 3 of 4: ImagingPerformanceAssessment */ - protected void showNisoImagingPerformanceAssessment02( - NisoImageMetadata niso, String margin) { - String margn2 = margin + " "; - String margn3 = margn2 + " "; - String margn4 = margn3 + " "; - String margn5 = margn4 + " "; - String margn6 = margn5 + " "; - String margn7 = margn6 + " "; - - StringBuffer ipaBuf = new StringBuffer(margn3 - + elementStart("mix:ImagingPerformanceAssessment") + EOL); - boolean useIPABuf = false; // set to true if we find interesting data. - StringBuffer smBuf = new StringBuffer(margn4 - + elementStart("mix:SpatialMetrics") + EOL); - boolean useSMBuf = false; - - int n = niso.getSamplingFrequencyPlane(); - if (n != NisoImageMetadata.NULL) { - smBuf.append(margn5 - + element("mix:SamplingFrequencyPlane", Integer.toString(n)) - + EOL); - useSMBuf = true; - } - n = niso.getSamplingFrequencyUnit(); - if (n != NisoImageMetadata.NULL) { - smBuf.append(margn5 - + element("mix:SamplingFrequencyUnit", Integer.toString(n)) - + EOL); - useSMBuf = true; - } - Rational r = niso.getXSamplingFrequency(); - if (r != null) { - smBuf.append(margn5 - + element("mix:XSamplingFrequency", - Long.toString(r.toLong())) + EOL); - useSMBuf = true; - } - r = niso.getYSamplingFrequency(); - if (r != null) { - smBuf.append(margn5 - + element("mix:YSamplingFrequency", - Long.toString(r.toLong())) + EOL); - useSMBuf = true; - } - long ln = niso.getImageWidth(); - if (ln != NisoImageMetadata.NULL) { - smBuf.append(margn5 + element("mix:ImageWidth", Long.toString(ln)) - + EOL); - useSMBuf = true; - } - ln = niso.getImageLength(); - if (ln != NisoImageMetadata.NULL) { - smBuf.append(margn5 + element("mix:ImageLength", Long.toString(ln)) - + EOL); - useSMBuf = true; - } - - double d = niso.getSourceXDimension(); - n = niso.getSourceXDimensionUnit(); - if (d != NisoImageMetadata.NILL || n != NisoImageMetadata.NULL) { - smBuf.append(margn5 + elementStart("mix:Source_X") + EOL); - if (d != NisoImageMetadata.NILL) { - smBuf.append(margn6 - + element("mix:Source_Xdimension", formatters.get() - .format(d)) + EOL); - } - if (n != NisoImageMetadata.NULL) { - smBuf.append(margn6 - + element("mix:Source_XdimensionUnit", - Integer.toString(n)) + EOL); - } - smBuf.append(margn5 + elementEnd("mix:Source_X") + EOL); - useSMBuf = true; - } - - d = niso.getSourceYDimension(); - n = niso.getSourceYDimensionUnit(); - if (d != NisoImageMetadata.NILL || n != NisoImageMetadata.NULL) { - smBuf.append(margn4 + elementStart("mix:Source_Y") + EOL); - if (d != NisoImageMetadata.NILL) { - smBuf.append(margn5 - + element("mix:Source_Ydimension", formatters.get() - .format(d)) + EOL); - } - if (n != NisoImageMetadata.NULL) { - smBuf.append(margn5 - + element("mix:Source_YdimensionUnit", - Integer.toString(n)) + EOL); - } - smBuf.append(margn4 + elementEnd("mix:Source_Y") + EOL); - useSMBuf = true; - } - smBuf.append(margn3 + elementEnd("mix:SpatialMetrics") + EOL); - if (useSMBuf) { - ipaBuf.append(smBuf); - useIPABuf = true; - } - - // Now a tentative buffer for the Energetics element - StringBuffer eBuf = new StringBuffer(margn3 - + elementStart("mix:Energetics") + EOL); - boolean useEBuf = false; - int[] iarray = niso.getBitsPerSample(); - if (iarray != null) { - eBuf.append(margn4 - + element("mix:BitsPerSample", integerArray(iarray, ',')) - + EOL); - useEBuf = true; - } - n = niso.getSamplesPerPixel(); - if (n != NisoImageMetadata.NULL) { - eBuf.append(margn4 - + element("mix:SamplesPerPixel", Integer.toString(n)) + EOL); - useEBuf = true; - } - iarray = niso.getExtraSamples(); - if (iarray != null) { - // extraSamples can only be an integer, so the best we can do is - // snag the first value from the array. It also must be limited to - // 0, 1, 2, or 3. - n = iarray[0]; - if (n >= 0 && n <= 3) { - eBuf.append(margn4 - + element("mix:ExtraSamples", Integer.toString(n)) - + EOL); - useEBuf = true; - } - // This is what we'd really like to do, but it violates the schema. - // Keep this code around in the event the schema is fixed in the - // future. - // eBuf.append (margn4 + element ("mix:ExtraSamples", - // integerArray (iarray)) + EOL); - // useEBuf = true; - } - // Tentative buffer for colormap element within Energetics - StringBuffer cmBuf = new StringBuffer(margn4 - + elementStart("mix:Colormap") + EOL); - boolean useCMBuf = false; - String s = niso.getColormapReference(); - if (s != null) { - cmBuf.append(margn5 + element("mix:Reference", s) + EOL); - useCMBuf = true; - } - iarray = niso.getColormapRedValue(); - if (iarray != null) { - cmBuf.append(margn5 + elementStart("mix:Wrap")); - // If the red array is there, assume the others are too, and are - // equal in length. - int[] bcarray = niso.getColormapBitCodeValue(); - int[] garray = niso.getColormapGreenValue(); - int[] barray = niso.getColormapBlueValue(); - try { - for (int i = 0; i < iarray.length; i++) { - cmBuf.append(margn6 + elementStart("mix:Color") + EOL); - n = bcarray[i]; - if (n != NisoImageMetadata.NULL) { - cmBuf.append(margn7 - + element("mix:BitCodeValue", - Integer.toString(n)) + EOL); - } - n = iarray[i]; - if (n != NisoImageMetadata.NULL) { - cmBuf.append(margn7 - + element("mix:RedValue", Integer.toString(n)) - + EOL); - } - n = garray[i]; - if (n != NisoImageMetadata.NULL) { - cmBuf.append(margn7 - + element("mix:GreenValue", Integer.toString(n)) - + EOL); - } - n = barray[i]; - if (n != NisoImageMetadata.NULL) { - cmBuf.append(margn7 - + element("mix:BlueValue", Integer.toString(n)) - + EOL); - } - - cmBuf.append(margn6 + elementEnd("mix:Color") + EOL); - } - } catch (Exception e) { - // If the assumption mentioned above is wrong, - // we'll get broken XML, but at least won't die here. - } - cmBuf.append(margn5 + elementEnd("mix:Wrap") + EOL); - useCMBuf = true; - } - cmBuf.append(margn4 + elementEnd("mix:Colormap")); - if (useCMBuf) { - eBuf.append(cmBuf); - useEBuf = true; - } - - iarray = niso.getGrayResponseCurve(); - n = niso.getGrayResponseUnit(); - if (iarray != null || n != NisoImageMetadata.NULL) { - eBuf.append(margn4 + elementStart("mix:GrayResponse") + EOL); - if (iarray != null) { - eBuf.append(margn5 - + element("mix:GrayResponseCurve", integerArray(iarray)) - + EOL); - } - if (n != NisoImageMetadata.NULL) { - eBuf.append(margn5 - + element("mix:GrayResponseUnit", Integer.toString(n)) - + EOL); - } - eBuf.append(margn4 + elementEnd("mix:GrayResponse") + EOL); - useEBuf = true; - } - r = niso.getWhitePointXValue(); - Rational r1 = niso.getWhitePointYValue(); - if (r != null || r1 != null) { - // These are specified in the scheme as CIExyType. All the - // schema tells us about this type is that it's a string. - eBuf.append(margn4 + elementStart("mix:WhitePoint") + EOL); - if (r != null) { - eBuf.append(margn5 - + element("mix:WhitePoint_Xvalue", r.toString()) + EOL); - } - if (r1 != null) { - eBuf.append(margn5 - + element("mix:WhitePoint_Yvalue", r1.toString()) + EOL); - } - eBuf.append(margn4 + elementEnd("mix:WhitePoint") + EOL); - useEBuf = true; - } - - r = niso.getPrimaryChromaticitiesRedX(); - // For simplicity, we check only the red x in deciding whether - // to incorporate this element. A partial set of chromaticities - // would be meaningless anyway. - if (r != null) { - eBuf.append(margn4 + elementStart("mix:PrimaryChromaticities") - + EOL); - eBuf.append(margn5 - + element("mix:PrimaryChromaticities_RedX", r.toString()) - + EOL); - r = niso.getPrimaryChromaticitiesRedY(); - if (r != null) { - eBuf.append(margn5 - + element("mix:PrimaryChromaticities_RedY", - r.toString()) + EOL); - } - r = niso.getPrimaryChromaticitiesGreenX(); - if (r != null) { - eBuf.append(margn5 - + element("mix:PrimaryChromaticities_GreenX", - r.toString()) + EOL); - } - r = niso.getPrimaryChromaticitiesGreenY(); - if (r != null) { - eBuf.append(margn5 - + element("mix:PrimaryChromaticities_GreenY", - r.toString()) + EOL); - } - r = niso.getPrimaryChromaticitiesBlueX(); - if (r != null) { - eBuf.append(margn5 - + element("mix:PrimaryChromaticities_BlueX", - r.toString()) + EOL); - } - r = niso.getPrimaryChromaticitiesBlueY(); - if (r != null) { - eBuf.append(margn5 - + element("mix:PrimaryChromaticities_BlueY", - r.toString()) + EOL); - } - eBuf.append(margn4 + elementEnd("mix:PrimaryChromaticities") + EOL); - useEBuf = true; - } - - eBuf.append(margn3 + elementEnd("mix:Energetics") + EOL); - if (useEBuf) { - ipaBuf.append(eBuf); - useIPABuf = true; - } - - // Another tentative buffer for TargetData - StringBuffer tdBuf = new StringBuffer(margn3 - + elementStart("mix:TargetData") + EOL); - boolean useTDBuf = false; - n = niso.getTargetType(); - if (n != NisoImageMetadata.NULL) { - tdBuf.append(margn4 - + element("mix:TargetType", Integer.toString(n)) + EOL); - useTDBuf = true; - } - - // Nest a TargetID tentative buffer in the TargetData buffer - StringBuffer tiBuf = new StringBuffer(margn4 - + elementStart("mix:TargetID") + EOL); - boolean useTIBuf = false; - s = niso.getTargetIDManufacturer(); - if (s != null) { - tiBuf.append(margn5 + element("mix:TargetIDManufacturer", s) + EOL); - useTIBuf = true; - } - s = niso.getTargetIDName(); - if (s != null) { - tiBuf.append(margn5 + element("mix:TargetIDName", s) + EOL); - useTIBuf = true; - } - s = niso.getTargetIDNo(); - if (s != null) { - tiBuf.append(margn5 + element("mix:TargetIDNo", s) + EOL); - useTIBuf = true; - } - s = niso.getTargetIDMedia(); - if (s != null) { - tiBuf.append(margn5 + element("mix:TargetIDMedia", s) + EOL); - useTIBuf = true; - } - tiBuf.append(margn4 + elementEnd("mix:TargetID") + EOL); - if (useTIBuf) { - tdBuf.append(tiBuf); - useTDBuf = true; - } - - s = niso.getImageData(); - if (s != null) { - tdBuf.append(margn5 + element("mix:ImageData", s) + EOL); - useTDBuf = true; - } - s = niso.getPerformanceData(); - if (s != null) { - tdBuf.append(margn5 + element("mix:PerformanceData", s) + EOL); - useTDBuf = true; - } - s = niso.getProfiles(); - if (s != null) { - tdBuf.append(margn5 + element("mix:Profiles", s) + EOL); - useTDBuf = true; - } - tdBuf.append(margn3 + elementEnd("mix:TargetData") + EOL); - if (useTDBuf) { - ipaBuf.append(tdBuf); - useIPABuf = true; - } - - ipaBuf.append(margn2 + elementEnd("mix:ImagingPerformanceAssessment") - + EOL); - if (useIPABuf) { - _writer.print(ipaBuf.toString()); - } - } - - /* Top level element 4 of 4: ChangeHistory */ - protected void showNisoChangeHistory02(NisoImageMetadata niso, String margin) { - String margn2 = margin + " "; - String margn3 = margn2 + " "; - String margn4 = margn3 + " "; - String margn5 = margn4 + " "; - - // Yet again, build elements in tentative buffers and throw them - // away if they prove trivial. - StringBuffer chBuf = new StringBuffer(margn2 - + elementStart("mix:ChangeHistory") + EOL); - boolean useCHBuf = false; - StringBuffer ipBuf = new StringBuffer(margn3 - + elementStart("mix:ImageProcessing") + EOL); - boolean useIPBuf = false; - - String s = niso.getDateTimeProcessed(); - if (s != null) { - ipBuf.append(margn4 + element("DateTimeProcessed", s) + EOL); - useIPBuf = true; - } - s = niso.getSourceData(); - if (s != null) { - ipBuf.append(margn4 + element("SourceData", s) + EOL); - useIPBuf = true; - } - s = niso.getProcessingAgency(); - if (s != null) { - ipBuf.append(margn4 + element("ProcessingAgency", s) + EOL); - useIPBuf = true; - } - - // Third-level nesting of tentative buffer! - StringBuffer psBuf = new StringBuffer(margn4 - + elementStart("ProcessingSoftware") + EOL); - boolean usePSBuf = false; - s = niso.getProcessingSoftwareName(); - if (s != null) { - psBuf.append(margn5 + element("ProcessingSoftwareName", s) + EOL); - usePSBuf = true; - } - s = niso.getProcessingSoftwareVersion(); - if (s != null) { - psBuf.append(margn5 + element("ProcessingSoftwareVersion", s) + EOL); - usePSBuf = true; - } - psBuf.append(margn4 + elementEnd("ProcessingSoftware") + EOL); - if (usePSBuf) { - ipBuf.append(psBuf); - useIPBuf = true; - } - - // Hard to say, but I think the intent is that there be - // one ProcessingActions element per processing action. - String[] sarray = niso.getProcessingActions(); - if (sarray != null) { - for (int i = 0; i < sarray.length; i++) { - ipBuf.append(margn4 + element("ProcessingActions", sarray[i]) - + EOL); - } - useIPBuf = true; - } - ipBuf.append(margn3 + elementEnd("mix:ImageProcessing") + EOL); - if (useIPBuf) { - chBuf.append(ipBuf); - useCHBuf = true; - } - chBuf.append(margn2 + elementEnd("mix:ChangeHistory") + EOL); - if (useCHBuf) { - _writer.print(chBuf.toString()); - } - } - - /** - * Display the NISO image metadata formatted according to the MIX 1.0 - * schema. - */ - protected void showNisoImageMetadata10(NisoImageMetadata niso) { - String margin = getIndent(++_level); - - String[][] attrs = { - { "xmlns:mix", "http://www.loc.gov/mix/v10" }, - { "xmlns:xsi", "http://www.w3.org/2001/XMLSchema-instance" }, - { "xsi:schemaLocation", - "http://www.loc.gov/mix/v10 http://www.loc.gov/standards/mix/mix10/mix10.xsd" } }; - _writer.println(margin + elementStart("mix:mix", attrs)); - - showNisoBasicDigitalObjectInformation10(niso, margin); - showNisoBasicImageInformation10(niso, margin); - showNisoImageCaptureMetadata10(niso, margin); - showNisoImageAssessmentMetadata10(niso, margin); - showChangeHistory10(niso, margin); - - _writer.println(margin + elementEnd("mix:mix")); - - _level--; - - } - - /* - * The NISO Metadata output (1.0 now) is split into multiple functions so - * that they're merely outrageously big rather than disgustingly big - */ - /* Top level element 1 of 5: BasicDigitalObjectInformation */ - protected void showNisoBasicDigitalObjectInformation10( - NisoImageMetadata niso, String margin) { - String margn2 = margin + " "; - String margn3 = margn2 + " "; - String margn4 = margn3 + " "; - String margn5 = margn4 + " "; - - _writer.println(margn2 - + elementStart("mix:BasicDigitalObjectInformation")); - - StringBuffer objIDBuf = new StringBuffer(margn3 - + elementStart("mix:ObjectIdentifier") + EOL); - boolean useObjIDBuf = false; - objIDBuf.append(margn4 + element("mix:objectIdentifierType", "JHOVE") - + EOL); - String s = niso.getImageIdentifier(); - if (s != null) { - objIDBuf.append(margn4 + element("mix:objectIdentifierValue", s) - + EOL); - useObjIDBuf = true; - } - objIDBuf.append(margn3 + elementEnd("mix:ObjectIdentifier") + EOL); - if (useObjIDBuf) { - _writer.print(objIDBuf.toString()); - } - long ln = niso.getFileSize(); - if (ln != NisoImageMetadata.NULL) { - _writer.print(margn4 + element("mix:fileSize", Long.toString(ln)) - + EOL); - } - - // TODO we really should output a FormatDesignation, but it isn't in the - // NisoImageMetadata class yet. - // TODO If we output a FormatDesignation, we should output a - // FormatRegistry. - - if ((s = niso.getByteOrder()) != null) { - // Convert strings to MIX 1.0 form - if (s.startsWith("big")) { - s = "big_endian"; - } else if (s.startsWith("little")) { - s = "little_endian"; - } - _writer.print(margn4 + element("mix:byteOrder", s) + EOL); - } - int comp = niso.getCompressionScheme(); - int level = niso.getCompressionLevel(); - if (comp != NisoImageMetadata.NULL || level != NisoImageMetadata.NULL) { - _writer.print(margn4 + elementStart("mix:Compression") + EOL); - if (comp != NisoImageMetadata.NULL) { - if (comp == 34713 || comp == 34714) { - _writer.print(margn5 - + element("mix:compressionScheme", - compressionSchemeToString(comp)) + EOL); - if (level != NisoImageMetadata.NULL) { - _writer.print(margn5 - + element("mix:compressionRatio", - Integer.toString(level)) + EOL); - } - } else { - _writer.print(margn5 - + element("mix:compressionScheme", - Integer.toString(comp)) + EOL); - } - } - // TODO it isn't clear how to get from compression level to - // compression ratio - - _writer.print(margn4 + elementEnd("mix:Compression") + EOL); - } - int n = niso.getChecksumMethod(); - s = niso.getChecksumValue(); - if (n != NisoImageMetadata.NULL || s != null) { - _writer.print(margn4 + elementStart("mix:Fixity") + EOL); - if (n != NisoImageMetadata.NULL) { - _writer.print(margn5 - + element("mix:messageDigestAlgorithm", - Integer.toString(n)) + EOL); - } - if (s != null) { - _writer.print(margn5 + element("mix:messageDigest", s) + EOL); - } - _writer.println(margn4 + elementEnd("mix:Fixity")); - } - _writer.println(margn3 - + elementEnd("mix:BasicDigitalObjectInformation")); - } - - /* 1.0, Top level element 2 of 5: BasicImageInformation */ - protected void showNisoBasicImageInformation10(NisoImageMetadata niso, - String margin) { - String margn2 = margin + " "; - String margn3 = margn2 + " "; - String margn4 = margn3 + " "; - String margn5 = margn4 + " "; - String margn6 = margn5 + " "; - String margn7 = margn6 + " "; - _writer.println(margn2 + elementStart("mix:BasicImageInformation")); - StringBuffer basCharBuf = new StringBuffer(margn3 - + elementStart("mix:BasicImageCharacteristics") + EOL); - boolean useBasCharBuf = false; - long ln = niso.getImageWidth(); - if (ln != NisoImageMetadata.NULL) { - basCharBuf.append(margn4 - + element("mix:imageWidth", Long.toString(ln)) + EOL); - useBasCharBuf = true; - } - ln = niso.getImageLength(); - if (ln != NisoImageMetadata.NULL) { - basCharBuf.append(margn4 - + element("mix:imageHeight", Long.toString(ln)) + EOL); - useBasCharBuf = true; - } - // Nest photometric interpretation tentative buffer in basCharBuf - StringBuffer piBuf = new StringBuffer(margn4 - + elementStart("mix:PhotometricInterpretation") + EOL); - boolean usePIBuf = false; - int n = niso.getColorSpace(); - if (n != NisoImageMetadata.NULL) { - piBuf.append(margn5 - + element("mix:colorSpace", Integer.toString(n)) + EOL); - usePIBuf = true; - } - String s = niso.getProfileName(); - String s2 = niso.getProfileURL(); - if (s != null || s2 != null) { - piBuf.append(margn5 + elementStart("mix:ColorProfile") + EOL); - piBuf.append(margn6 + elementStart("mix:IccProfile") + EOL); - if (s != null) { - piBuf.append(margn7 + element("mix:iccProfileName", s) + EOL); - } - if (s2 != null) { - piBuf.append(margn7 + element("mix:iccProfileURL", s2) + EOL); - } - piBuf.append(margn6 + elementEnd("mix:IccProfile") + EOL); - piBuf.append(margn5 + elementEnd("mix:ColorProfile") + EOL); - usePIBuf = true; - } - int[] iarray = niso.getYCbCrSubSampling(); - n = niso.getYCbCrPositioning(); - Rational[] rarray = niso.getYCbCrCoefficients(); - if (iarray != null || n != NisoImageMetadata.NULL || rarray != null) { - piBuf.append(margn5 + elementStart("mix:YCbCr") + EOL); - usePIBuf = true; - if (iarray != null && iarray.length >= 2) { - piBuf.append(margn6 + elementStart("mix:YCbCrSubSampling") - + EOL); - piBuf.append(margn7 - + element("mix:yCbCrSubsampleHoriz", - Integer.toString(iarray[0])) + EOL); - piBuf.append(margn7 - + element("mix:yCbCrSubsampleVert", - Integer.toString(iarray[1])) + EOL); - piBuf.append(margn6 + elementEnd("mix:YCbCrSubSampling") + EOL); - } - if (n != NisoImageMetadata.NULL) { - piBuf.append(margn6 - + element("mix:yCbCrPositioning", Integer.toString(n)) - + EOL); - } - if (rarray != null) { - piBuf.append(margn6 - + element("mix:yCbCrCoefficients", - rationalArray10(rarray)) + EOL); - } - piBuf.append(margn5 + elementEnd("mix:YCbCr") + EOL); - } - rarray = niso.getReferenceBlackWhite(); - if (rarray != null) { - piBuf.append(margn6 - + element("mix:referenceBlackWhite", - rationalArray10(rarray)) + EOL); - usePIBuf = true; - } - piBuf.append(margn4 + elementEnd("mix:PhotometricInterpretation") + EOL); - if (usePIBuf) { - basCharBuf.append(piBuf); - useBasCharBuf = true; - } - basCharBuf.append(margn3 + elementEnd("mix:BasicImageCharacteristics")); - - if (useBasCharBuf) { - _writer.println(basCharBuf); - } - // SpecialFormatCharacteristics limited to JPEG2000 - StringBuffer speCharBuf = new StringBuffer(margn3 - + elementStart("mix:SpecialFormatCharacteristics") + EOL); - boolean useSpeCharBuf = false; - int lay = niso.getJp2Layers(); - int lev = niso.getJp2ResolutionLevels(); - String sizTiles = niso.getJp2Tiles(); - if (sizTiles != null || lay != NisoImageMetadata.NULL - || lev != NisoImageMetadata.NULL) { - - useSpeCharBuf = true; - speCharBuf.append(margn4 + elementStart("mix:JPEG2000") + EOL); - speCharBuf.append(margn5 + elementStart("mix:EncodingOptions") - + EOL); - if (sizTiles != null) { - speCharBuf - .append(margn6 + element("mix:tiles", sizTiles) + EOL); - } - if (lay != NisoImageMetadata.NULL) { - speCharBuf.append(margn6 - + element("mix:qualityLayers", Integer.toString(lay)) - + EOL); - } - if (sizTiles != null) { - speCharBuf - .append(margn6 - + element("mix:resolutionLevels", - Integer.toString(lev)) + EOL); - } - speCharBuf.append(margn5 + elementEnd("mix:EncodingOptions") + EOL); - speCharBuf.append(margn4 + elementEnd("mix:JPEG2000") + EOL); - } - - speCharBuf.append(margn3 - + elementEnd("mix:SpecialFormatCharacteristics")); - if (useSpeCharBuf) { - _writer.println(speCharBuf); - } - - _writer.println(margn2 + elementEnd("mix:BasicImageInformation")); - } - - /* 1.0, Top level element 3 of 5: ImageCaptureMetadata */ - protected void showNisoImageCaptureMetadata10(NisoImageMetadata niso, - String margin) { - String margn2 = margin + " "; - String margn3 = margn2 + " "; - String margn4 = margn3 + " "; - String margn5 = margn4 + " "; - String margn6 = margn5 + " "; - String margn7 = margn6 + " "; - - StringBuffer captureBuffer = new StringBuffer(); - boolean useCaptureBuffer = false; - - String s = niso.getSourceType(); - if (s != null) { - captureBuffer.append(margn3 + element("mix:sourceType", s)); - useCaptureBuffer = true; - } - s = niso.getSourceID(); - if (s != null) { - captureBuffer.append(margn3 + elementStart("mix:SourceID")); - captureBuffer.append(margn3 + element("mix:sourceIDValue", s)); - captureBuffer.append(margn3 + elementEnd("mix:sourceID")); - useCaptureBuffer = true; - } - double d = niso.getSourceXDimension(); - int n = niso.getSourceXDimensionUnit(); - if (d != NisoImageMetadata.NILL || n != NisoImageMetadata.NULL) { - // Assume that both X and Y exist, or neither - captureBuffer.append(margn3 + elementStart("mix:SourceSize")); - captureBuffer.append(margn4 + elementStart("mix:SourceXDimension") - + EOL); - if (d != NisoImageMetadata.NILL) { - captureBuffer.append(margn5 - + element("mix:sourceXDimensionValue", formatters.get() - .format(d)) + EOL); - } - if (n != NisoImageMetadata.NULL) { - captureBuffer.append(margn5 - + element("mix:sourceXDimensionUnit", - Integer.toString(n)) + EOL); - } - captureBuffer.append(margn4 + elementEnd("mix:SourceXDimension") - + EOL); - - d = niso.getSourceYDimension(); - n = niso.getSourceYDimensionUnit(); - if (d != NisoImageMetadata.NILL || n != NisoImageMetadata.NULL) { - captureBuffer.append(margn4 - + elementStart("mix:SourceYDimension") + EOL); - if (d != NisoImageMetadata.NILL) { - captureBuffer.append(margn5 - + element("mix:sourceYDimensionValue", formatters - .get().format(d)) + EOL); - } - if (n != NisoImageMetadata.NULL) { - captureBuffer.append(margn5 - + element("mix:sourceYDimensionUnit", - Integer.toString(n)) + EOL); - } - captureBuffer.append(margn4 - + elementEnd("mix:SourceYDimension") + EOL); - } - captureBuffer.append(margn3 + elementEnd("mix:SourceSize") + EOL); - useCaptureBuffer = true; - } - StringBuffer genCapBuf = new StringBuffer(margn3 - + elementStart("mix:GeneralCaptureInformation") + EOL); - boolean useGenCapBuf = false; - - s = niso.getDateTimeCreated(); - if (s != null) { - genCapBuf.append(margn3 + element("mix:dateTimeCreated", s) + EOL); - useGenCapBuf = true; - } - s = niso.getImageProducer(); - if (s != null) { - genCapBuf.append(margn3 + element("mix:imageProducer", s) + EOL); - useGenCapBuf = true; - } - - s = niso.getDeviceSource(); - if (s != null) { - genCapBuf.append(margn3 + element("mix:captureDevice", s) + EOL); - /* - * This has a restricted set of values. Does the setting code - * conform? - */ - } - - genCapBuf.append(margn3 + elementEnd("mix:GeneralCaptureInformation") - + EOL); - if (useGenCapBuf) { - captureBuffer.append(genCapBuf); - useCaptureBuffer = true; - } - - // Here's a chunk of XML for scanners. - StringBuffer scanCapBuf = new StringBuffer(margn3 - + elementStart("mix:ScannerCapture") + EOL); - boolean useScanCapBuf = false; - String mfg = niso.getScannerManufacturer(); - if (mfg != null) { - scanCapBuf.append(margn4 + element("mix:scannerManufacturer", mfg) - + EOL); - useScanCapBuf = true; - } - String model = niso.getScannerModelName(); - String modelNum = niso.getScannerModelNumber(); - String serNum = niso.getScannerModelSerialNo(); - if (model != null || modelNum != null || serNum != null) { - useScanCapBuf = true; - scanCapBuf.append(margn4 + elementStart("mix:ScannerModel") + EOL); - if (model != null) { - scanCapBuf.append(margn5 - + element("mix:scannerModelName", model) + EOL); - } - if (modelNum != null) { - scanCapBuf.append(margn5 - + element("mix:scannerModelNumber", modelNum) + EOL); - } - if (serNum != null) { - scanCapBuf.append(margn5 - + element("mix:scannerModelSerialNo", serNum) + EOL); - } - scanCapBuf.append(margn4 + elementEnd("mix:ScannerModel") + EOL); - } - double xres = niso.getXPhysScanResolution(); - double yres = niso.getYPhysScanResolution(); - if (xres != NisoImageMetadata.NULL && yres != NisoImageMetadata.NULL) { - double res = (xres > yres ? xres : yres); - scanCapBuf.append(margn4 - + element("mix:maximumOpticalResolution", formatters.get() - .format(res)) + EOL); - } - s = niso.getScanningSoftware(); - if (s != null) { - useScanCapBuf = true; - scanCapBuf.append(margn4 - + elementStart("mix:ScanningSystemSoftware") + EOL); - scanCapBuf.append(margn5 + element("mix:scanningSoftwareName", s) - + EOL); - s = niso.getScanningSoftwareVersionNo(); - if (s != null) { - scanCapBuf.append(margn5 - + element("mix:scanningSoftwareVersionNo", s) + EOL); - } - scanCapBuf.append(margn4 + elementEnd("mix:ScanningSystemSoftware") - + EOL); - } - scanCapBuf.append(margn3 + elementEnd("mix:ScannerCapture") + EOL); - if (useScanCapBuf) { - captureBuffer.append(scanCapBuf); - useCaptureBuffer = true; - } - - // Now we'll hear from the digital cameras. - StringBuffer digCamBuf = new StringBuffer(margn3 - + elementStart("mix:DigitalCameraCapture") + EOL); - boolean useDigCamBuf = false; - - s = niso.getDigitalCameraManufacturer(); - if (s != null) { - digCamBuf.append(margn4 - + element("mix:digitalCameraManufacturer", s) + EOL); - useDigCamBuf = true; - } - String dcmodel = niso.getDigitalCameraModelName(); - String dcmodelNum = niso.getDigitalCameraModelNumber(); - String dcserNum = niso.getDigitalCameraModelSerialNo(); - if (dcmodel != null || dcmodelNum != null || dcserNum != null) { - useDigCamBuf = true; - digCamBuf.append(margn4 + elementStart("mix:DigitalCameraModel") - + EOL); - if (dcmodel != null) { - digCamBuf.append(margn5 - + element("mix:digitalCameraModelName", dcmodel) + EOL); - } - if (dcmodelNum != null) { - digCamBuf.append(margn5 - + element("mix:digitalCameraModelNumber", dcmodelNum) - + EOL); - } - if (dcserNum != null) { - digCamBuf.append(margn5 - + element("mix:mix:digitalCameraModelSerialNo", - dcserNum) + EOL); - } - digCamBuf.append(margn4 + elementEnd("mix:DigitalCameraModel") - + EOL); - } - - // Nest a buffer for CameraCaptureSettings - StringBuffer ccSetBuf = new StringBuffer(margn4 - + elementStart("mix:CameraCaptureSettings") + EOL); - boolean useCcSetBuf = false; - // CameraCaptureSettings consists only of an ImageData element, so we - // don't need another use flag here. - ccSetBuf.append(margn5 + elementStart("mix:ImageData") + EOL); - d = niso.getFNumber(); - if (d != NisoImageMetadata.NULL) { - ccSetBuf.append(margn6 - + element("mix:fNumber", formatters.get().format(d)) + EOL); - useCcSetBuf = true; - } - d = niso.getExposureTime(); - if (d != NisoImageMetadata.NULL) { - ccSetBuf.append(margn6 - + element("mix:exposureTime", formatters.get().format(d)) - + EOL); - useCcSetBuf = true; - } - n = niso.getExposureProgram(); - if (n != NisoImageMetadata.NULL) { - ccSetBuf.append(margn6 - + element("mix:exposureProgram", Integer.toString(n)) + EOL); - useCcSetBuf = true; - } - s = niso.getExifVersion(); - if ("0220".equals(s)) { // Only valid value - ccSetBuf.append(margn6 + element("mix:exifVersion", s) + EOL); - useCcSetBuf = true; - } - Rational r = niso.getBrightness(); - if (r != null) { - rationalToString(ccSetBuf, "mix:brightnessValue", margn6, r); - useCcSetBuf = true; - } - r = niso.getExposureBias(); - if (r != null) { - rationalToString(ccSetBuf, "mix:exposureBiasValue", margn6, r); - useCcSetBuf = true; - } - r = niso.getMaxApertureValue(); - if (r != null) { - rationalToString(ccSetBuf, "mix:maxApertureValue", margn6, r); - useCcSetBuf = true; - } - double[] darray = niso.getSubjectDistance(); - if (darray != null) { - // For the old schema, we dumped out the whole array, but the 1.0 - // schema clearly says a non-negative number is expected. - // So just use darray[0]. - ccSetBuf.append(margn6 - + element("mix:subjectDistance", - formatters.get().format(darray[0])) + EOL); - useCcSetBuf = true; - } - n = niso.getMeteringMode(); - if (n != NisoImageMetadata.NULL) { - ccSetBuf.append(margn6 - + element("mix:meteringMode", meteringModeToString(n)) - + EOL); - useCcSetBuf = true; - } - n = niso.getFlash(); - if (n != NisoImageMetadata.NULL) { - // First bit (0 = Flash did not fire, 1 = Flash fired) - int firstBit = n & 1; - ccSetBuf.append(margn6 - + element("mix:flash", NisoImageMetadata.FLASH_20[firstBit]) - + EOL); - useCcSetBuf = true; - } - d = niso.getFocalLength(); - if (d != NisoImageMetadata.NULL) { - ccSetBuf.append(margn6 - + element("mix:focalLength", formatters.get().format(d)) - + EOL); - useCcSetBuf = true; - } - r = niso.getFlashEnergy(); - if (r != null) { - rationalToString(ccSetBuf, "mix:flashEnergy", margn6, r); - useCcSetBuf = true; - } - n = niso.getBackLight(); - if (n != NisoImageMetadata.NULL) { - ccSetBuf.append(margn6 - + element("mix:backLight", Integer.toString(n)) + EOL); - useCcSetBuf = true; - } - d = niso.getExposureIndex(); - if (d != NisoImageMetadata.NULL) { - ccSetBuf.append(margn6 - + element("mix:exposureIndex", formatters.get().format(d)) - + EOL); - useCcSetBuf = true; - } - n = niso.getAutoFocus(); - if (n != NisoImageMetadata.NULL) { - ccSetBuf.append(margn6 - + element("mix:autoFocus", Integer.toString(n)) + EOL); - useCcSetBuf = true; - } - d = niso.getXPrintAspectRatio(); - double d2 = niso.getYPrintAspectRatio(); - if (d != NisoImageMetadata.NULL || d2 != NisoImageMetadata.NULL) { - ccSetBuf.append(margn6 + elementStart("mix:PrintAspectRatio") + EOL); - if (d != NisoImageMetadata.NULL) { - ccSetBuf.append(margn7 - + element("mix:xPrintAspectRatio", formatters.get() - .format(d)) + EOL); - } - if (d2 != NisoImageMetadata.NULL) { - ccSetBuf.append(margn7 - + element("mix:yPrintAspectRatio", formatters.get() - .format(d)) + EOL); - } - - ccSetBuf.append(margn6 + elementEnd("mix:PrintAspectRatio") + EOL); - } - - ccSetBuf.append(margn5 + elementEnd("mix:ImageData") + EOL); - ccSetBuf.append(margn4 + elementEnd("mix:CameraCaptureSettings") + EOL); - if (useCcSetBuf) { - digCamBuf.append(ccSetBuf); - useDigCamBuf = true; - } - digCamBuf.append(margn3 + elementEnd("mix:DigitalCameraCapture") + EOL); - if (useDigCamBuf) { - captureBuffer.append(digCamBuf); - useCaptureBuffer = true; - } - - n = niso.getOrientation(); - if (n != NisoImageMetadata.NULL) { - captureBuffer.append(margn3 - + element("mix:orientation", Integer.toString(n)) + EOL); - useCaptureBuffer = true; - } - s = niso.getMethodology(); - if (s != null) { - captureBuffer.append(margn3 + element("mix:methodology", s) + EOL); - } - if (useCaptureBuffer) { - _writer.println(margn2 + elementStart("mix:ImageCaptureMetadata")); - _writer.print(captureBuffer.toString()); - _writer.println(margn2 + elementEnd("mix:ImageCaptureMetadata")); - } - } - - /* 1.0, Top level element 4 of 5: ImageAssessmentMetadata */ - protected void showNisoImageAssessmentMetadata10(NisoImageMetadata niso, - String margin) { - String margn2 = margin + " "; - String margn3 = margn2 + " "; - String margn4 = margn3 + " "; - String margn5 = margn4 + " "; - - _writer.println(margn2 + elementStart("mix:ImageAssessmentMetadata")); - StringBuffer metricsBuf = new StringBuffer(margn3 - + elementStart("mix:SpatialMetrics") + EOL); - boolean useMetricsBuf = false; - - int n = niso.getSamplingFrequencyPlane(); - if (n != NisoImageMetadata.NULL) { - metricsBuf - .append(margn4 - + element("mix:samplingFrequencyPlane", - Integer.toString(n)) + EOL); - useMetricsBuf = true; - } - n = niso.getSamplingFrequencyUnit(); - if (n != NisoImageMetadata.NULL) { - metricsBuf.append(margn4 - + element("mix:samplingFrequencyUnit", Integer.toString(n)) - + EOL); - useMetricsBuf = true; - } - Rational r = niso.getXSamplingFrequency(); - if (r != null) { - rationalToString(metricsBuf, "mix:xSamplingFrequency", margn4, r); - } - r = niso.getYSamplingFrequency(); - if (r != null) { - rationalToString(metricsBuf, "mix:ySamplingFrequency", margn4, r); - } - metricsBuf.append(margn3 + elementEnd("mix:SpatialMetrics")); - if (useMetricsBuf) { - _writer.println(metricsBuf); - } - - StringBuffer colorEncBuf = new StringBuffer(margn3 - + elementStart("mix:ImageColorEncoding") + EOL); - boolean useColorEncBuf = false; - - int[] iarray = niso.getBitsPerSample(); - if (iarray != null) { - colorEncBuf - .append(margn4 + elementStart("mix:bitsPerSample") + EOL); - colorEncBuf.append(margn5 - + element("mix:bitsPerSampleValue", - integerArray(iarray, ',')) + EOL); - colorEncBuf.append(margn5 - + element("mix:bitsPerSampleUnit", "integer") + EOL); - // bitsPerSampleUnit can also be floating point. Don't ask me why. - colorEncBuf.append(margn4 + elementEnd("mix:bitsPerSample") + EOL); - useColorEncBuf = true; - } - n = niso.getSamplesPerPixel(); - if (n != NisoImageMetadata.NULL) { - colorEncBuf - .append(margn4 - + element("mix:samplesPerPixel", - Integer.toString(n)) + EOL); - useColorEncBuf = true; - } - - iarray = niso.getExtraSamples(); - if (iarray != null) { - // extraSamples can only be an integer, so the best we can do is - // snag the first value from the array. It also must be limited to - // 0, 1, 2, or 3. - n = iarray[0]; - if (n >= 0 && n <= 3) { - colorEncBuf.append(margn4 - + element("mix:extraSamples", Integer.toString(n)) - + EOL); - useColorEncBuf = true; - } - } - - String s = niso.getColormapReference(); - if (s != null) { - colorEncBuf.append(margn4 + elementStart("mix:Colormap") + EOL); - colorEncBuf.append(margn5 + element("mix:colormapReference", s) - + EOL); - colorEncBuf.append(margn4 + elementEnd("mix:Colormap") + EOL); - useColorEncBuf = true; - } - - // This is complete nonsense, but it's what the spec says - iarray = niso.getGrayResponseCurve(); - if (iarray != null) { - colorEncBuf.append(margn4 + element("mix:grayResponseCurve", "N") - + EOL); - useColorEncBuf = true; - } - - n = niso.getGrayResponseUnit(); - if (n != NisoImageMetadata.NULL) { - colorEncBuf.append(margn4 - + element("mix:grayResponseUnit", Integer.toString(n)) - + EOL); - useColorEncBuf = true; - } - - r = niso.getWhitePointXValue(); - Rational r2 = niso.getWhitePointYValue(); - if (r != null || r2 != null) { - colorEncBuf.append(margn4 + elementStart("mix:WhitePoint") + EOL); - if (r != null) { - colorEncBuf.append(margn5 - + element("mix:whitePointXValue", r.toString()) + EOL); - } - if (r2 != null) { - colorEncBuf.append(margn5 - + element("mix:whitePointYValue", r2.toString()) + EOL); - } - colorEncBuf.append(margn4 + elementEnd("mix:WhitePoint") + EOL); - useColorEncBuf = true; - } - - // A chromaticities buffer to go in the color encoding buffer. - StringBuffer chromaBuf = new StringBuffer(margn4 - + elementStart("mix:PrimaryChromaticities") + EOL); - boolean useChromaBuf = false; - r = niso.getPrimaryChromaticitiesRedX(); - if (r != null) { - chromaBuf.append(margn5 - + element("mix:primaryChromaticitiesRedX", r.toString()) - + EOL); - useChromaBuf = true; - } - r = niso.getPrimaryChromaticitiesRedY(); - if (r != null) { - chromaBuf.append(margn5 - + element("mix:primaryChromaticitiesRedY", r.toString()) - + EOL); - useChromaBuf = true; - } - r = niso.getPrimaryChromaticitiesGreenX(); - if (r != null) { - chromaBuf.append(margn5 - + element("mix:primaryChromaticitiesGreenX", r.toString()) - + EOL); - useChromaBuf = true; - } - r = niso.getPrimaryChromaticitiesGreenY(); - if (r != null) { - chromaBuf.append(margn5 - + element("mix:primaryChromaticitiesGreenY", r.toString()) - + EOL); - useChromaBuf = true; - } - r = niso.getPrimaryChromaticitiesBlueX(); - if (r != null) { - chromaBuf.append(margn5 - + element("mix:primaryChromaticitiesBlueX", r.toString()) - + EOL); - useChromaBuf = true; - } - r = niso.getPrimaryChromaticitiesBlueY(); - if (r != null) { - chromaBuf.append(margn5 - + element("mix:primaryChromaticitiesBlueY", r.toString()) - + EOL); - useChromaBuf = true; - } - chromaBuf - .append(margn4 + elementEnd("mix:PrimaryChromaticities") + EOL); - if (useChromaBuf) { - colorEncBuf.append(chromaBuf); - useColorEncBuf = true; - } - - colorEncBuf.append(margn3 + elementEnd("mix:ImageColorEncoding") + EOL); - if (useColorEncBuf) { - _writer.print(colorEncBuf); - } - - StringBuffer targetBuf = new StringBuffer(margn3 - + elementStart("mix:TargetData") + EOL); - boolean useTargetBuf = false; - n = niso.getTargetType(); - if (n != NisoImageMetadata.NULL) { - targetBuf.append(margn4 - + element("mix:targetType", Integer.toString(n)) + EOL); - useTargetBuf = true; - } - - // Now a nested buffer for TargetID. - StringBuffer targetIDBuf = new StringBuffer(margn4 - + elementStart("mix:TargetID") + EOL); - boolean useTargetIDBuf = false; - - s = niso.getTargetIDManufacturer(); - if (s != null) { - targetIDBuf.append(margn5 + element("mix:targetManufacturer", s) - + EOL); - useTargetIDBuf = true; - } - s = niso.getTargetIDName(); - if (s != null) { - targetIDBuf.append(margn5 + element("mix:targetName", s) + EOL); - useTargetIDBuf = true; - } - s = niso.getTargetIDNo(); - if (s != null) { - targetIDBuf.append(margn5 + element("mix:targetNo", s) + EOL); - useTargetIDBuf = true; - } - s = niso.getTargetIDMedia(); - if (s != null) { - targetIDBuf.append(margn5 + element("mix:targetMedia", s) + EOL); - useTargetIDBuf = true; - } - targetIDBuf.append(margn4 + elementEnd("mix:TargetID") + EOL); - - if (useTargetIDBuf) { - targetBuf.append(targetIDBuf); - useTargetBuf = true; - } - s = niso.getImageData(); - if (s != null) { - targetBuf.append(margn4 + element("mix:externalTarget", s) + EOL); - useTargetBuf = true; - } - s = niso.getPerformanceData(); - if (s != null) { - targetBuf.append(margn4 + element("mix:performanceData", s) + EOL); - useTargetBuf = true; - } - - targetBuf.append(margn3 + elementEnd("mix:TargetData") + EOL); - - if (useTargetBuf) { - _writer.print(targetBuf); - } - _writer.println(margn2 + elementEnd("mix:ImageAssessmentMetadata")); - } - - /* 1.0, Top level element 5 of 5: ChangeHistory (without time travel) */ - protected void showChangeHistory10(NisoImageMetadata niso, String margin) { - String margn2 = margin + " "; - String margn3 = margn2 + " "; - String margn4 = margn3 + " "; - String margn5 = margn4 + " "; - - // There may be nothing at all to write. Put the whole thing in a - // buffer. - StringBuffer chBuf = new StringBuffer(margn2 - + elementStart("mix:ChangeHistory") + EOL); - boolean useChBuf = false; - - chBuf.append(margn3 + elementStart("mix:ImageProcessing") + EOL); - - String s = niso.getSourceData(); - if (s != null) { - chBuf.append(margn4 + element("mix:sourceData", s) + EOL); - useChBuf = true; - } - s = niso.getProcessingAgency(); - if (s != null) { - chBuf.append(margn4 + element("mix:processingAgency", s) + EOL); - useChBuf = true; - } - StringBuffer sftwBuf = new StringBuffer(margn4 - + elementStart("mix:ProcessingSoftware") + EOL); - boolean useSftwBuf = false; - s = niso.getProcessingSoftwareName(); - if (s != null) { - sftwBuf.append(margn5 + element("mix:processingSoftwareName", s) - + EOL); - useSftwBuf = true; - } - s = niso.getProcessingSoftwareVersion(); - if (s != null) { - sftwBuf.append(margn5 + element("mix:processingSoftwareVersion", s) - + EOL); - useSftwBuf = true; - } - s = niso.getOS(); - if (s != null) { - sftwBuf.append(margn5 - + element("mix:processingOperatingSystemName", s) + EOL); - useSftwBuf = true; - } - s = niso.getOSVersion(); - if (s != null) { - sftwBuf.append(margn5 - + element("mix:processingOperatingSystemVersion", s) + EOL); - useSftwBuf = true; - } - sftwBuf.append(margn4 + elementEnd("mix:ProcessingSoftware") + EOL); - if (useSftwBuf) { - chBuf.append(sftwBuf); - useChBuf = true; - } - - String[] sarray = niso.getProcessingActions(); - if (sarray != null) { - for (int i = 0; i < sarray.length; i++) { - chBuf.append(margn4 - + element("mix:processingActions", sarray[i]) + EOL); - } - useChBuf = true; - } - - chBuf.append(margn3 + elementEnd("mix:ImageProcessing") + EOL); - chBuf.append(margn2 + elementEnd("mix:ChangeHistory") + EOL); - if (useChBuf) { - _writer.println(chBuf); - } - - } - - /** - * Display the NISO image metadata formatted according to the MIX 2.0 - * schema. - */ - protected void showNisoImageMetadata20(NisoImageMetadata niso) { - String margin = getIndent(++_level); - - String[][] attrs = { - { "xmlns:mix", "http://www.loc.gov/mix/v20" }, - { "xmlns:xsi", "http://www.w3.org/2001/XMLSchema-instance" }, - { "xsi:schemaLocation", - "http://www.loc.gov/mix/v20 http://www.loc.gov/standards/mix/mix20/mix20.xsd" } }; - _writer.println(margin + elementStart("mix:mix", attrs)); - - showNisoBasicDigitalObjectInformation20(niso, margin); - showNisoBasicImageInformation20(niso, margin); - showNisoImageCaptureMetadata20(niso, margin); - showNisoImageAssessmentMetadata20(niso, margin); - showChangeHistory20(niso, margin); - - _writer.println(margin + elementEnd("mix:mix")); - - _level--; - - } - - /* - * The NISO Metadata output for version 2.0. Top level element 1 of 6: - * BasicDigitalObjectInformation - */ - protected void showNisoBasicDigitalObjectInformation20( - NisoImageMetadata niso, String margin) { - String margn2 = margin + " "; - String margn3 = margn2 + " "; - String margn4 = margn3 + " "; - String margn5 = margn4 + " "; - String margn6 = margn5 + " "; - - _writer.println(margn2 - + elementStart("mix:BasicDigitalObjectInformation")); - - StringBuffer objIDBuf = new StringBuffer(margn3 - + elementStart("mix:ObjectIdentifier") + EOL); - objIDBuf.append(margn4 + element("mix:objectIdentifierType", "JHOVE") - + EOL); - String s = niso.getImageIdentifier(); - if (s != null) { - objIDBuf.append(margn4 + element("mix:objectIdentifierValue", s) - + EOL); - } - objIDBuf.append(margn3 + elementEnd("mix:ObjectIdentifier") + EOL); - _writer.print(objIDBuf.toString()); - long ln = niso.getFileSize(); - if (ln != NisoImageMetadata.NULL) { - _writer.print(margn3 + element("mix:fileSize", Long.toString(ln)) - + EOL); - } - - String mime = niso.getMimeType(); - if (mime != null) { - _writer.println(margn3 + elementStart("mix:FormatDesignation")); - _writer.print(margn4 + element("mix:formatName", mime) + EOL); - _writer.println(margn3 + elementEnd("mix:FormatDesignation")); - } - - if ((s = niso.getByteOrder()) != null) { - // Convert strings to MIX 1.0 form - if (s.startsWith("big")) { - s = "big endian"; - } else if (s.startsWith("little")) { - s = "little endian"; - } - _writer.print(margn3 + element("mix:byteOrder", s) + EOL); - } - - int comp = niso.getCompressionScheme(); - int level = niso.getCompressionLevel(); - String compStr; - switch (comp) { - case 1: - compStr = "Uncompressed"; - break; - case 2: - compStr = "CCITT 1D"; - break; - case 3: - compStr = "Group 3 Fax"; - break; - case 4: - compStr = "Group 4 Fax"; - break; - case 5: - compStr = "LZW"; - break; - case 6: - compStr = "JPEG"; - break; - case 32773: - compStr = "PackBits"; - break; - case 34713: - compStr = "JPEG2000 Lossy"; - break; - case 34714: - compStr = "JPEG2000 Lossless"; - break; - default: - compStr = "Unknown"; - break; - } - if (comp != NisoImageMetadata.NULL || level != NisoImageMetadata.NULL) { - _writer.print(margn3 + elementStart("mix:Compression") + EOL); - if (comp != NisoImageMetadata.NULL) { - _writer.print(margn4 - + element("mix:compressionScheme", compStr) + EOL); - } - // TODO it isn't clear how to get from compression level to - // compression ratio - if (level != NisoImageMetadata.NULL - && (comp == 34713 || comp == 34714)) { - _writer.print(margn5 + elementStart("mix:compressionRatio") - + EOL); - _writer.print(margn6 - + element("mix:numerator", Integer.toString(level)) - + EOL); - _writer.print(margn5 + elementEnd("mix:compressionRatio") + EOL); - } - - _writer.print(margn3 + elementEnd("mix:Compression") + EOL); - } - - // NOTE: Checksum method and value are never set currently. If they are, - // the - // values set will need to be converted to meaningful MIX values. This - // code is left - // here just as a reminder. - int n = niso.getChecksumMethod(); - s = niso.getChecksumValue(); - if (n != NisoImageMetadata.NULL || s != null) { - _writer.print(margn4 + elementStart("mix:Fixity") + EOL); - if (n != NisoImageMetadata.NULL) { - _writer.print(margn5 - + element("mix:messageDigestAlgorithm", - Integer.toString(n)) + EOL); - } - if (s != null) { - _writer.print(margn5 + element("mix:messageDigest", s) + EOL); - } - _writer.println(margn4 + elementEnd("mix:Fixity")); - } - - _writer.print(margn2 + elementEnd("mix:BasicDigitalObjectInformation") - + EOL); - } - - /* MIX/NISO 2.0, Top level element 2 of 5: BasicImageInformation */ - protected void showNisoBasicImageInformation20(NisoImageMetadata niso, - String margin) { - String margn2 = margin + " "; - String margn3 = margn2 + " "; - String margn4 = margn3 + " "; - String margn5 = margn4 + " "; - String margn6 = margn5 + " "; - String margn7 = margn6 + " "; - _writer.println(margn2 + elementStart("mix:BasicImageInformation")); - StringBuffer basCharBuf = new StringBuffer(margn3 - + elementStart("mix:BasicImageCharacteristics") + EOL); - boolean useBasCharBuf = false; - long ln = niso.getImageWidth(); - if (ln != NisoImageMetadata.NULL) { - basCharBuf.append(margn4 - + element("mix:imageWidth", Long.toString(ln)) + EOL); - useBasCharBuf = true; - } - ln = niso.getImageLength(); - if (ln != NisoImageMetadata.NULL) { - basCharBuf.append(margn4 - + element("mix:imageHeight", Long.toString(ln)) + EOL); - useBasCharBuf = true; - } - // Nest photometric interpretation tentative buffer in basCharBuf - StringBuffer piBuf = new StringBuffer(margn4 - + elementStart("mix:PhotometricInterpretation") + EOL); - boolean usePIBuf = false; - int n = niso.getColorSpace(); - if (n != NisoImageMetadata.NULL) { - piBuf.append(margn5 - + element("mix:colorSpace", - photometricInterpretationToString(n)) + EOL); - usePIBuf = true; - } - String s = niso.getProfileName(); - String s2 = niso.getProfileURL(); - if (s != null || s2 != null) { - piBuf.append(margn5 + elementStart("mix:ColorProfile") + EOL); - piBuf.append(margn6 + elementStart("mix:IccProfile") + EOL); - if (s != null) { - piBuf.append(margn7 + element("mix:iccProfileName", s) + EOL); - } - if (s2 != null) { - piBuf.append(margn7 + element("mix:iccProfileURI", s2) + EOL); - } - piBuf.append(margn6 + elementEnd("mix:IccProfile") + EOL); - // MIX 2.0 also allows embedded and local profiles. We don't - // currently support that. - piBuf.append(margn5 + elementEnd("mix:ColorProfile") + EOL); - usePIBuf = true; - } - int[] iarray = niso.getYCbCrSubSampling(); - n = niso.getYCbCrPositioning(); - Rational[] rarray = niso.getYCbCrCoefficients(); - if (iarray != null || n != NisoImageMetadata.NULL || rarray != null) { - piBuf.append(margn5 + elementStart("mix:YCbCr") + EOL); - usePIBuf = true; - if (iarray != null && iarray.length >= 2) { - piBuf.append(margn6 + elementStart("mix:YCbCrSubSampling") - + EOL); - piBuf.append(margn7 - + element("mix:yCbCrSubsampleHoriz", - Integer.toString(iarray[0])) + EOL); - piBuf.append(margn7 - + element("mix:yCbCrSubsampleVert", - Integer.toString(iarray[1])) + EOL); - piBuf.append(margn6 + elementEnd("mix:YCbCrSubSampling") + EOL); - } - if (n != NisoImageMetadata.NULL) { - piBuf.append(margn6 - + element("mix:yCbCrPositioning", Integer.toString(n)) - + EOL); - } - if (rarray != null && rarray.length >= 3) { - piBuf.append(margn6 + elementStart("mix:YCbCrCoefficients") - + EOL); - rationalToString(piBuf, "mix:lumaRed", margn7, rarray[0]); - rationalToString(piBuf, "mix:lumaGreen", margn7, rarray[1]); - rationalToString(piBuf, "mix:lumaBlue", margn7, rarray[2]); - piBuf.append(margn6 + elementEnd("mix:YCbCrCoefficients") + EOL); - } - piBuf.append(margn5 + elementEnd("mix:YCbCr") + EOL); - } - - rarray = niso.getReferenceBlackWhite(); - if (rarray != null) { - piBuf.append(margn5 + elementStart("mix:ReferenceBlackWhite") + EOL); - for (int i = 0; i < rarray.length - 1; i += 2) { - piBuf.append(margn6 + elementStart("mix:Component") + EOL); - piBuf.append(margn7 - + elementStart("mix:componentPhotometricInterpretation")); - // Tricky here. The reference BW might be given as either RGB or - // yCbCr. - String pi; - if (niso.getColorSpace() == 6) { // yCbCr - switch (i) { - case 0: - pi = "Y"; - break; - case 2: - pi = "Cb"; - break; - case 4: - default: - pi = "Cr"; - break; - } - } else { - switch (i) { // otherwise assume RGB - case 0: - pi = "R"; - break; - case 2: - pi = "G"; - break; - case 4: - default: - pi = "B"; - break; - } - - } - piBuf.append(pi - + elementEnd("mix:componentPhotometricInterpretation") - + EOL); - rationalToString(piBuf, "mix:footroom", margn7, rarray[i]); - rationalToString(piBuf, "mix:headroom", margn7, rarray[i + 1]); - piBuf.append(margn7 + elementEnd("mix:Component") + EOL); - } - piBuf.append(margn6 + elementEnd("mix:ReferenceBlackWhite") + EOL); - usePIBuf = true; - } - piBuf.append(margn4 + elementEnd("mix:PhotometricInterpretation") + EOL); - if (usePIBuf) { - basCharBuf.append(piBuf); - useBasCharBuf = true; - } - basCharBuf.append(margn3 + elementEnd("mix:BasicImageCharacteristics")); - - if (useBasCharBuf) { - _writer.println(basCharBuf); - } - - // TODO SpecialFormatCharacteristics would be nice to have here, - // Limited to JPEG2000 - StringBuffer speCharBuf = new StringBuffer(margn3 - + elementStart("mix:SpecialFormatCharacteristics") + EOL); - boolean useSpeCharBuf = false; - int lay = niso.getJp2Layers(); - int lev = niso.getJp2ResolutionLevels(); - String sizTiles = niso.getJp2Tiles(); - if (sizTiles != null || lay != NisoImageMetadata.NULL - || lev != NisoImageMetadata.NULL) { - - useSpeCharBuf = true; - speCharBuf.append(margn4 + elementStart("mix:JPEG2000") + EOL); - speCharBuf.append(margn5 + elementStart("mix:EncodingOptions") - + EOL); - if (sizTiles != null) { - String[] sizes = sizTiles.split("x"); - speCharBuf.append(margn6 + elementStart("mix:Tiles") + EOL); - speCharBuf.append(margn7 + element("mix:tileWidth", sizes[0]) - + EOL); - speCharBuf.append(margn7 + element("mix:tileHeight", sizes[1]) - + EOL); - speCharBuf.append(margn6 + elementEnd("mix:Tiles") + EOL); - } - if (lay != NisoImageMetadata.NULL) { - speCharBuf.append(margn6 - + element("mix:qualityLayers", Integer.toString(lay)) - + EOL); - } - if (sizTiles != null) { - speCharBuf - .append(margn6 - + element("mix:resolutionLevels", - Integer.toString(lev)) + EOL); - } - speCharBuf.append(margn5 + elementEnd("mix:EncodingOptions") + EOL); - speCharBuf.append(margn4 + elementEnd("mix:JPEG2000") + EOL); - } - - speCharBuf.append(margn3 - + elementEnd("mix:SpecialFormatCharacteristics")); - if (useSpeCharBuf) { - _writer.println(speCharBuf); - } - - _writer.println(margn2 + elementEnd("mix:BasicImageInformation")); - } - - /* 2.0, Top level element 3 of 5: ImageCaptureMetadata */ - protected void showNisoImageCaptureMetadata20(NisoImageMetadata niso, - String margin) { - String margn2 = margin + " "; - String margn3 = margn2 + " "; - String margn4 = margn3 + " "; - String margn5 = margn4 + " "; - String margn6 = margn5 + " "; - String margn7 = margn6 + " "; - String margn8 = margn7 + " "; - - // We don't start with an ImageCaptureMetadata element, because the - // whole element is conditional on having some content. - StringBuffer captureBuffer = new StringBuffer(); - boolean useCaptureBuffer = false; - int n; - - String s = niso.getSourceType(); - String si = niso.getSourceID(); - double d = niso.getSourceXDimension(); - if (s != null || si != null || d != NisoImageMetadata.NILL) { - captureBuffer.append(margn3 + element("mix:SourceInformation", s)); - useCaptureBuffer = true; - if (s != null) { - captureBuffer.append(margn4 + element("mix:sourceType", s)); - } - if (si != null) { - captureBuffer.append(margn4 + elementStart("mix:SourceID")); - captureBuffer.append(margn4 + element("mix:sourceIDValue", si)); - captureBuffer.append(margn4 + elementEnd("mix:sourceID")); - } - n = niso.getSourceXDimensionUnit(); - if (d != NisoImageMetadata.NILL || n != NisoImageMetadata.NULL) { - // Assume that both X and Y exist, or neither - captureBuffer.append(margn4 + elementStart("mix:SourceSize")); - captureBuffer.append(margn5 - + elementStart("mix:SourceXDimension") + EOL); - if (d != NisoImageMetadata.NILL) { - captureBuffer.append(margn6 - + element("mix:sourceXDimensionValue", formatters - .get().format(d)) + EOL); - } - if (n != NisoImageMetadata.NULL) { - captureBuffer.append(margn6 - + element("mix:sourceXDimensionUnit", - Integer.toString(n)) + EOL); - } - captureBuffer.append(margn5 - + elementEnd("mix:SourceXDimension") + EOL); - - d = niso.getSourceYDimension(); - n = niso.getSourceYDimensionUnit(); - if (d != NisoImageMetadata.NILL || n != NisoImageMetadata.NULL) { - captureBuffer.append(margn5 - + elementStart("mix:SourceYDimension") + EOL); - if (d != NisoImageMetadata.NILL) { - captureBuffer.append(margn6 - + element("mix:sourceYDimensionValue", - formatters.get().format(d)) + EOL); - } - if (n != NisoImageMetadata.NULL) { - captureBuffer.append(margn6 - + element("mix:sourceYDimensionUnit", - Integer.toString(n)) + EOL); - } - captureBuffer.append(margn5 - + elementEnd("mix:SourceYDimension") + EOL); - } - captureBuffer.append(margn4 + elementEnd("mix:SourceSize") - + EOL); - } - captureBuffer.append(margn3 + elementEnd("mix:SourceInformation") - + EOL); - } - StringBuffer genCapBuf = new StringBuffer(margn3 - + elementStart("mix:GeneralCaptureInformation") + EOL); - boolean useGenCapBuf = false; - - s = niso.getDateTimeCreated(); - if (s != null) { - genCapBuf.append(margn3 + element("mix:dateTimeCreated", s) + EOL); - useGenCapBuf = true; - } - s = niso.getImageProducer(); - if (s != null) { - genCapBuf.append(margn3 + element("mix:imageProducer", s) + EOL); - useGenCapBuf = true; - } - - s = niso.getDeviceSource(); - if (s != null) { - genCapBuf.append(margn3 + element("mix:captureDevice", s) + EOL); - /* - * This has a restricted set of values. Does the setting code - * conform? - */ - } - - genCapBuf.append(margn3 + elementEnd("mix:GeneralCaptureInformation") - + EOL); - if (useGenCapBuf) { - captureBuffer.append(genCapBuf); - useCaptureBuffer = true; - } - - // Here's a chunk of XML for scanners. - StringBuffer scanCapBuf = new StringBuffer(margn3 - + elementStart("mix:ScannerCapture") + EOL); - boolean useScanCapBuf = false; - String mfg = niso.getScannerManufacturer(); - if (mfg != null) { - scanCapBuf.append(margn4 + element("mix:scannerManufacturer", mfg) - + EOL); - useScanCapBuf = true; - } - String model = niso.getScannerModelName(); - String modelNum = niso.getScannerModelNumber(); - String serNum = niso.getScannerModelSerialNo(); - if (model != null || modelNum != null || serNum != null) { - useScanCapBuf = true; - scanCapBuf.append(margn4 + elementStart("mix:ScannerModel") + EOL); - if (model != null) { - scanCapBuf.append(margn5 - + element("mix:scannerModelName", model) + EOL); - } - if (modelNum != null) { - scanCapBuf.append(margn5 - + element("mix:scannerModelNumber", modelNum) + EOL); - } - if (serNum != null) { - scanCapBuf.append(margn5 - + element("mix:scannerModelSerialNo", serNum) + EOL); - } - scanCapBuf.append(margn4 + elementEnd("mix:ScannerModel") + EOL); - } - double xres = niso.getXPhysScanResolution(); - double yres = niso.getYPhysScanResolution(); - if (xres != NisoImageMetadata.NULL && yres != NisoImageMetadata.NULL) { - scanCapBuf.append(margn4 - + elementStart("mix:MaximumOpticalResolution") + EOL); - scanCapBuf.append(margn5 - + element("mix:xOpticalResolution", formatters.get() - .format(xres)) + EOL); - scanCapBuf.append(margn5 - + element("mix:yOpticalResolution", formatters.get() - .format(yres)) + EOL); - scanCapBuf.append(margn5 + element("mix:resolutionUnit", "in.") - + EOL); // is this a safe assumption? - scanCapBuf.append(margn4 - + elementEnd("mix:MaximumOpticalResolution")); - } - s = niso.getScanningSoftware(); - if (s != null) { - useScanCapBuf = true; - scanCapBuf.append(margn4 - + elementStart("mix:ScanningSystemSoftware") + EOL); - scanCapBuf.append(margn5 + element("mix:scanningSoftwareName", s) - + EOL); - s = niso.getScanningSoftwareVersionNo(); - if (s != null) { - scanCapBuf.append(margn5 - + element("mix:scanningSoftwareVersionNo", s) + EOL); - } - scanCapBuf.append(margn4 + elementEnd("mix:ScanningSystemSoftware") - + EOL); - } - scanCapBuf.append(margn3 + elementEnd("mix:ScannerCapture") + EOL); - if (useScanCapBuf) { - captureBuffer.append(scanCapBuf); - useCaptureBuffer = true; - } - - // Now we'll hear from the digital cameras. - StringBuffer digCamBuf = new StringBuffer(margn3 - + elementStart("mix:DigitalCameraCapture") + EOL); - boolean useDigCamBuf = false; - - s = niso.getDigitalCameraManufacturer(); - if (s != null) { - digCamBuf.append(margn4 - + element("mix:digitalCameraManufacturer", s) + EOL); - useDigCamBuf = true; - } - String dcmodel = niso.getDigitalCameraModelName(); - String dcmodelNum = niso.getDigitalCameraModelNumber(); - String dcserNum = niso.getDigitalCameraModelSerialNo(); - if (dcmodel != null || dcmodelNum != null || dcserNum != null) { - useDigCamBuf = true; - digCamBuf.append(margn4 + elementStart("mix:DigitalCameraModel") - + EOL); - if (dcmodel != null) { - digCamBuf.append(margn5 - + element("mix:digitalCameraModelName", dcmodel) + EOL); - } - if (dcmodelNum != null) { - digCamBuf.append(margn5 - + element("mix:digitalCameraModelNumber", dcmodelNum) - + EOL); - } - if (dcserNum != null) { - digCamBuf.append(margn5 - + element("mix:mix:digitalCameraModelSerialNo", - dcserNum) + EOL); - } - digCamBuf.append(margn4 + elementEnd("mix:DigitalCameraModel") - + EOL); - } - - // Nest a buffer for CameraCaptureSettings - StringBuffer ccSetBuf = new StringBuffer(margn4 - + elementStart("mix:CameraCaptureSettings") + EOL); - boolean useCcSetBuf = false; - // CameraCaptureSettings consists only of an ImageData element, so we - // don't need another use flag here. - ccSetBuf.append(margn5 + elementStart("mix:ImageData") + EOL); - d = niso.getFNumber(); - if (d != NisoImageMetadata.NULL) { - ccSetBuf.append(margn6 - + element("mix:fNumber", formatters.get().format(d)) + EOL); - useCcSetBuf = true; - } - d = niso.getExposureTime(); - if (d != NisoImageMetadata.NULL) { - ccSetBuf.append(margn6 - + element("mix:exposureTime", formatters.get().format(d)) - + EOL); - useCcSetBuf = true; - } - n = niso.getExposureProgram(); - if (n != NisoImageMetadata.NULL) { - if (n > 8 || n < 0) { - n = 0; // force "Not defined" for bad value - } - - ccSetBuf.append(margn6 - + element("mix:exposureProgram", - NisoImageMetadata.EXPOSURE_PROGRAM[n]) + EOL); - useCcSetBuf = true; - } - if (niso.getExifVersion() != null) { - ccSetBuf.append(margn6 - + element("mix:exifVersion", niso.getExifVersion()) + EOL); - useCcSetBuf = true; - } - Rational r = niso.getBrightness(); - if (r != null) { - rationalToString(ccSetBuf, "mix:brightnessValue", margn6, r); - useCcSetBuf = true; - } - r = niso.getExposureBias(); - if (r != null) { - rationalToString(ccSetBuf, "mix:exposureBiasValue", margn6, r); - useCcSetBuf = true; - } - r = niso.getMaxApertureValue(); - if (r != null) { - rationalToString(ccSetBuf, "mix:maxApertureValue", margn6, r); - useCcSetBuf = true; - } - double[] darray = niso.getSubjectDistance(); - if (darray != null) { - // darray has two values. If they're equal, set "distance". - // Otherwise, - // set the min and max. - ccSetBuf.append(margn6 + elementStart("mix:SubjectDistance") + EOL); - useCcSetBuf = true; - if (darray[0] == darray[1]) { - ccSetBuf.append(margn7 - + element("mix:distance", - formatters.get().format(darray[0])) + EOL); - } else { - ccSetBuf.append(margn7 + elementStart("mix:MinMaxDistance") - + EOL); - ccSetBuf.append(margn8 - + element("mix:minDistance", - formatters.get().format(darray[0])) + EOL); - ccSetBuf.append(margn8 - + element("mix:maxDistance", - formatters.get().format(darray[1])) + EOL); - ccSetBuf.append(margn7 + elementEnd("mix:MinMaxDistance") + EOL); - } - ccSetBuf.append(margn6 + elementEnd("mix:SubjectDistance") + EOL); - - } - n = niso.getMeteringMode(); - if (n != NisoImageMetadata.NULL) { - ccSetBuf.append(margn6 - + element("mix:meteringMode", meteringModeToString(n)) - + EOL); - useCcSetBuf = true; - } - n = niso.getFlash(); - if (n != NisoImageMetadata.NULL) { - // First bit (0 = Flash did not fire, 1 = Flash fired) - int firstBit = n & 1; - ccSetBuf.append(margn6 - + element("mix:flash", NisoImageMetadata.FLASH_20[firstBit]) - + EOL); - useCcSetBuf = true; - } - d = niso.getFocalLength(); - if (d != NisoImageMetadata.NULL) { - ccSetBuf.append(margn6 - + element("mix:focalLength", formatters.get().format(d)) - + EOL); - useCcSetBuf = true; - } - r = niso.getFlashEnergy(); - if (r != null) { - rationalToString(ccSetBuf, "mix:flashEnergy", margn6, r); - useCcSetBuf = true; - } - n = niso.getBackLight(); - if (n != NisoImageMetadata.NULL) { - ccSetBuf.append(margn6 - + element("mix:backLight", Integer.toString(n)) + EOL); - useCcSetBuf = true; - } - d = niso.getExposureIndex(); - if (d != NisoImageMetadata.NULL) { - ccSetBuf.append(margn6 - + element("mix:exposureIndex", formatters.get().format(d)) - + EOL); - useCcSetBuf = true; - } - n = niso.getAutoFocus(); - if (n != NisoImageMetadata.NULL) { - ccSetBuf.append(margn6 - + element("mix:autoFocus", Integer.toString(n)) + EOL); - useCcSetBuf = true; - } - d = niso.getXPrintAspectRatio(); - double d2 = niso.getYPrintAspectRatio(); - if (d != NisoImageMetadata.NULL || d2 != NisoImageMetadata.NULL) { - ccSetBuf.append(margn6 + elementStart("mix:PrintAspectRatio") + EOL); - if (d != NisoImageMetadata.NULL) { - ccSetBuf.append(margn7 - + element("mix:xPrintAspectRatio", formatters.get() - .format(d)) + EOL); - } - if (d2 != NisoImageMetadata.NULL) { - ccSetBuf.append(margn7 - + element("mix:yPrintAspectRatio", formatters.get() - .format(d2)) + EOL); - } - - ccSetBuf.append(margn6 + elementEnd("mix:PrintAspectRatio") + EOL); - } - - ccSetBuf.append(margn5 + elementEnd("mix:ImageData") + EOL); - ccSetBuf.append(margn4 + elementEnd("mix:CameraCaptureSettings") + EOL); - if (useCcSetBuf) { - digCamBuf.append(ccSetBuf); - useDigCamBuf = true; - } - digCamBuf.append(margn3 + elementEnd("mix:DigitalCameraCapture") + EOL); - if (useDigCamBuf) { - captureBuffer.append(digCamBuf); - useCaptureBuffer = true; - } - - n = niso.getOrientation(); - if (n != NisoImageMetadata.NULL) { - final String[] orient = { "unknown", "normal*", - "normal, image flipped", "normal, rotated 180\u00B0", - "normal, image flipped, rotated 180\u00B0", - "normal, image flipped, rotated cw 90\u00B0", - "normal, rotated ccw 90\u00B0", - "normal, image flipped, rotated ccw 90\u00B0", - "normal, rotated cw 90\u00B0" }; - if (n > 8 || n < 0) { - n = 0; // force "unknown" for bad value - } - captureBuffer.append(margn3 + element("mix:orientation", orient[n]) - + EOL); - useCaptureBuffer = true; - } - s = niso.getMethodology(); - if (s != null) { - captureBuffer.append(margn3 + element("mix:methodology", s) + EOL); - } - if (useCaptureBuffer) { - _writer.println(margn2 + elementStart("mix:ImageCaptureMetadata")); - _writer.print(captureBuffer.toString()); - _writer.println(margn2 + elementEnd("mix:ImageCaptureMetadata")); - } - } - - /* 2.0, Top level element 4 of 5: ImageAssessmentMetadata */ - protected void showNisoImageAssessmentMetadata20(NisoImageMetadata niso, - String margin) { - String margn2 = margin + " "; - String margn3 = margn2 + " "; - String margn4 = margn3 + " "; - String margn5 = margn4 + " "; - - _writer.println(margn2 + elementStart("mix:ImageAssessmentMetadata")); - StringBuffer metricsBuf = new StringBuffer(margn3 - + elementStart("mix:SpatialMetrics") + EOL); - boolean useMetricsBuf = false; - - int n = niso.getSamplingFrequencyPlane(); - if (n != NisoImageMetadata.NULL) { - metricsBuf - .append(margn4 - + element("mix:samplingFrequencyPlane", - Integer.toString(n)) + EOL); - useMetricsBuf = true; - } - n = niso.getSamplingFrequencyUnit(); - if (n != NisoImageMetadata.NULL) { - final String sfu[] = { null, "no absolute unit of measurement", - "in.", "cm" }; - if (n < 1 || n > 3) { - n = 1; - } - metricsBuf.append(margn4 - + element("mix:samplingFrequencyUnit", sfu[n]) + EOL); - useMetricsBuf = true; - } - Rational r = niso.getXSamplingFrequency(); - if (r != null) { - rationalToString(metricsBuf, "mix:xSamplingFrequency", margn4, r); - } - r = niso.getYSamplingFrequency(); - if (r != null) { - rationalToString(metricsBuf, "mix:ySamplingFrequency", margn4, r); - } - metricsBuf.append(margn3 + elementEnd("mix:SpatialMetrics")); - if (useMetricsBuf) { - _writer.println(metricsBuf); - } - - StringBuffer colorEncBuf = new StringBuffer(margn3 - + elementStart("mix:ImageColorEncoding") + EOL); - boolean useColorEncBuf = false; - - int[] iarray = niso.getBitsPerSample(); - if (iarray != null) { - colorEncBuf - .append(margn4 + elementStart("mix:BitsPerSample") + EOL); - for (int ii = 0; ii < iarray.length; ii++) { - colorEncBuf.append(margn5 - + element("mix:bitsPerSampleValue", - Integer.toString(iarray[ii])) + EOL); - } - colorEncBuf.append(margn5 - + element("mix:bitsPerSampleUnit", "integer") + EOL); - // bitsPerSampleUnit can also be floating point. Don't ask me why. - colorEncBuf.append(margn4 + elementEnd("mix:BitsPerSample") + EOL); - useColorEncBuf = true; - } - n = niso.getSamplesPerPixel(); - if (n != NisoImageMetadata.NULL) { - colorEncBuf - .append(margn4 - + element("mix:samplesPerPixel", - Integer.toString(n)) + EOL); - useColorEncBuf = true; - } - - iarray = niso.getExtraSamples(); - if (iarray != null) { - for (int ii = 0; ii < iarray.length; ii++) { - n = iarray[ii]; - if (n >= 0 && n <= 3) { - colorEncBuf - .append(margn4 - + element( - "mix:extraSamples", - NisoImageMetadata.EXTRA_SAMPLE_20[n]) - + EOL); - useColorEncBuf = true; - } - } - } - - String s = niso.getColormapReference(); - if (s != null) { - colorEncBuf.append(margn4 + elementStart("mix:Colormap") + EOL); - colorEncBuf.append(margn5 + element("mix:colormapReference", s) - + EOL); - colorEncBuf.append(margn4 + elementEnd("mix:Colormap") + EOL); - useColorEncBuf = true; - } - - iarray = niso.getGrayResponseCurve(); - n = niso.getGrayResponseUnit(); - if (iarray != null || n != NisoImageMetadata.NULL) { - StringBuffer grayRespBuf = new StringBuffer(margn4 - + elementStart("mix:GrayResponse") + EOL); - if (iarray != null) { - for (int ii = 0; ii < iarray.length; ii++) { - grayRespBuf.append(margn5 - + element("mix:grayResponseCurve", - Integer.toString(iarray[ii])) + EOL); - } - } - - if (n != NisoImageMetadata.NULL && n > 0 && n <= 5) { - // Convert integer to text value; only values 1-5 are legal - grayRespBuf.append(margn5 - + element("mix:grayResponseUnit", - NisoImageMetadata.GRAY_RESPONSE_UNIT_20[n - 1]) - + EOL); - } - grayRespBuf.append(margn4 + elementEnd("mix:GrayResponse") + EOL); - colorEncBuf.append(grayRespBuf); - useColorEncBuf = true; - } - - r = niso.getWhitePointXValue(); - Rational r2 = niso.getWhitePointYValue(); - if (r != null || r2 != null) { - colorEncBuf.append(margn4 + elementStart("mix:WhitePoint") + EOL); - if (r != null) { - rationalToString(colorEncBuf, "mix:whitePointXValue", margn5, r); - } - if (r2 != null) { - rationalToString(colorEncBuf, "mix:whitePointYValue", margn5, - r2); - } - colorEncBuf.append(margn4 + elementEnd("mix:WhitePoint") + EOL); - useColorEncBuf = true; - } - - // A chromaticities buffer to go in the color encoding buffer. - StringBuffer chromaBuf = new StringBuffer(margn4 - + elementStart("mix:PrimaryChromaticities") + EOL); - boolean useChromaBuf = false; - r = niso.getPrimaryChromaticitiesRedX(); - if (r != null) { - rationalToString(chromaBuf, "mix:primaryChromaticitiesRedX", - margn5, r); - useChromaBuf = true; - } - r = niso.getPrimaryChromaticitiesRedY(); - if (r != null) { - rationalToString(chromaBuf, "mix:primaryChromaticitiesRedY", - margn5, r); - useChromaBuf = true; - } - r = niso.getPrimaryChromaticitiesGreenX(); - if (r != null) { - rationalToString(chromaBuf, "mix:primaryChromaticitiesGreenX", - margn5, r); - useChromaBuf = true; - } - r = niso.getPrimaryChromaticitiesGreenY(); - if (r != null) { - rationalToString(chromaBuf, "mix:primaryChromaticitiesGreenY", - margn5, r); - useChromaBuf = true; - } - r = niso.getPrimaryChromaticitiesBlueX(); - if (r != null) { - rationalToString(chromaBuf, "mix:primaryChromaticitiesBlueX", - margn5, r); - useChromaBuf = true; - } - r = niso.getPrimaryChromaticitiesBlueY(); - if (r != null) { - rationalToString(chromaBuf, "mix:primaryChromaticitiesBlueY", - margn5, r); - useChromaBuf = true; - } - chromaBuf - .append(margn4 + elementEnd("mix:PrimaryChromaticities") + EOL); - if (useChromaBuf) { - colorEncBuf.append(chromaBuf); - useColorEncBuf = true; - } - - colorEncBuf.append(margn3 + elementEnd("mix:ImageColorEncoding") + EOL); - if (useColorEncBuf) { - _writer.print(colorEncBuf); - } - - StringBuffer targetBuf = new StringBuffer(margn3 - + elementStart("mix:TargetData") + EOL); - boolean useTargetBuf = false; - n = niso.getTargetType(); - if (n != NisoImageMetadata.NULL) { - targetBuf.append(margn4 - + element("mix:targetType", Integer.toString(n)) + EOL); - useTargetBuf = true; - } - - // Now a nested buffer for TargetID. - StringBuffer targetIDBuf = new StringBuffer(margn4 - + elementStart("mix:TargetID") + EOL); - boolean useTargetIDBuf = false; - - s = niso.getTargetIDManufacturer(); - if (s != null) { - targetIDBuf.append(margn5 + element("mix:targetManufacturer", s) - + EOL); - useTargetIDBuf = true; - } - s = niso.getTargetIDName(); - if (s != null) { - targetIDBuf.append(margn5 + element("mix:targetName", s) + EOL); - useTargetIDBuf = true; - } - s = niso.getTargetIDNo(); - if (s != null) { - targetIDBuf.append(margn5 + element("mix:targetNo", s) + EOL); - useTargetIDBuf = true; - } - s = niso.getTargetIDMedia(); - if (s != null) { - targetIDBuf.append(margn5 + element("mix:targetMedia", s) + EOL); - useTargetIDBuf = true; - } - targetIDBuf.append(margn4 + elementEnd("mix:TargetID") + EOL); - - if (useTargetIDBuf) { - targetBuf.append(targetIDBuf); - useTargetBuf = true; - } - s = niso.getImageData(); - if (s != null) { - targetBuf.append(margn4 + element("mix:externalTarget", s) + EOL); - useTargetBuf = true; - } - s = niso.getPerformanceData(); - if (s != null) { - targetBuf.append(margn4 + element("mix:performanceData", s) + EOL); - useTargetBuf = true; - } - - targetBuf.append(margn3 + elementEnd("mix:TargetData") + EOL); - - if (useTargetBuf) { - _writer.print(targetBuf); - } - _writer.println(margn2 + elementEnd("mix:ImageAssessmentMetadata")); - } - - /* 2.0, Top level element 5 of 5: ChangeHistory */ - protected void showChangeHistory20(NisoImageMetadata niso, String margin) { - String margn2 = margin + " "; - String margn3 = margn2 + " "; - String margn4 = margn3 + " "; - String margn5 = margn4 + " "; - // String margn6 = margn5 + " "; - - // There may be nothing at all to write. Put the whole thing in a - // buffer. - StringBuffer chBuf = new StringBuffer(margn2 - + elementStart("mix:ChangeHistory") + EOL); - boolean useChBuf = false; - - chBuf.append(margn3 + elementStart("mix:ImageProcessing") + EOL); - - String s = niso.getSourceData(); - if (s != null) { - chBuf.append(margn4 + element("mix:sourceData", s) + EOL); - useChBuf = true; - } - s = niso.getProcessingAgency(); - if (s != null) { - chBuf.append(margn4 + element("mix:processingAgency", s) + EOL); - useChBuf = true; - } - StringBuffer sftwBuf = new StringBuffer(margn4 - + elementStart("mix:ProcessingSoftware") + EOL); - boolean useSftwBuf = false; - s = niso.getProcessingSoftwareName(); - if (s != null) { - sftwBuf.append(margn5 + element("mix:processingSoftwareName", s) - + EOL); - useSftwBuf = true; - } - s = niso.getProcessingSoftwareVersion(); - if (s != null) { - sftwBuf.append(margn5 + element("mix:processingSoftwareVersion", s) - + EOL); - useSftwBuf = true; - } - s = niso.getOS(); - if (s != null) { - sftwBuf.append(margn5 - + element("mix:processingOperatingSystemName", s) + EOL); - useSftwBuf = true; - } - s = niso.getOSVersion(); - if (s != null) { - sftwBuf.append(margn5 - + element("mix:processingOperatingSystemVersion", s) + EOL); - useSftwBuf = true; - } - sftwBuf.append(margn4 + elementEnd("mix:ProcessingSoftware") + EOL); - if (useSftwBuf) { - chBuf.append(sftwBuf); - useChBuf = true; - } - - String[] sarray = niso.getProcessingActions(); - if (sarray != null) { - for (int i = 0; i < sarray.length; i++) { - chBuf.append(margn4 - + element("mix:processingActions", sarray[i]) + EOL); - } - useChBuf = true; - } - - chBuf.append(margn3 + elementEnd("mix:ImageProcessing") + EOL); - chBuf.append(margn2 + elementEnd("mix:ChangeHistory") + EOL); - if (useChBuf) { - _writer.println(chBuf); - } - - } - - /** - * Convert the metering mode value to one of the suggested values for MIX - * 2.0 - */ - private String meteringModeToString(int n) { - String s = NisoImageMetadata.METERING_MODE[1]; - if (n >= 1 && n <= 6) { - s = NisoImageMetadata.METERING_MODE[n]; - } - // Capitalize first letter - return s.substring(0, 1).toUpperCase(Locale.ROOT) + s.substring(1); - } - - /** Convert the color space value (which is based on the TIFF - * PhotometricInterpretation convention) to one of the suggested - * values for MIX 2.0 */ - private String photometricInterpretationToString (int n) { - String s = "Unknown"; - switch (n) { - case 0: s = "WhiteIsZero"; break; - case 1: s = "BlackIsZero"; break; - case 2: s = "RGB"; break; - case 3: s = "PaletteColor"; break; - case 4: s = "TransparencyMask"; break; - case 5: s = "CMYK"; break; - case 6: s = "YCbCr"; break; - case 8: s = "CIELab"; break; - case 9: s = "ICCLab"; break; - case 10: s = "ITULab"; break; - case 32803: s = "CFA"; break; // used by DNG - case 34892: s = "LinearRaw"; break; // used by DNG - case 65535: s = "YCCK"; break; // used by Adobe JPEG - default: break; +public class XmlHandler extends edu.harvard.hul.ois.jhove.HandlerBase { + + /** + * **************************************************************** PRIVATE CLASS FIELDS. + * **************************************************************** + */ + + /** Thread safe formatter for doubles */ + private static final ThreadLocal formatters = + new ThreadLocal() { + @Override + protected NumberFormat initialValue() { + NumberFormat _format = NumberFormat.getInstance(Locale.ROOT); + _format.setGroupingUsed(false); + _format.setMinimumFractionDigits(0); + + return _format; } - return s; + }; + + /** Handler name. */ + private static final String NAME = "XML"; + + /** Handler release identifier. */ + private static final String RELEASE = "1.9"; + + /** Handler release date. */ + private static final int[] DATE = {2019, 10, 18}; + + /** Handler informative note. */ + private static final String NOTE = + "This output handler is defined by the XML Schema " + + "https://schema.openpreservation.org/ois/xml/xsd/jhove/jhove.xsd"; + + /** Handler rights statement. */ + private static final String RIGHTS = + "Derived from software Copyright 2004-2011 " + + "by the President and Fellows of Harvard College. " + + "Version 1.8 release by Open Preservation Foundation. " + + "Released under the GNU Lesser General Public License."; + + /** Localized line separator character. */ + private static final String EOL = System.getProperty("line.separator"); + + /** Schema version. */ + private static final String SCHEMA_VERSION = "1.8"; + + /** + * **************************************************************** PRIVATE INSTANCE FIELDS. + * **************************************************************** + */ + + /* Sample rate. */ + private double _sampleRate; + + /** + * **************************************************************** CLASS CONSTRUCTOR. + * **************************************************************** + */ + + /** Creates an XmlHandler. */ + public XmlHandler() { + super(NAME, RELEASE, DATE, NOTE, RIGHTS); + _vendor = Agent.harvardInstance(); + } + + /** Constructor for use by subclasses. */ + public XmlHandler(String name, String release, int[] date, String note, String rights) { + super(name, release, date, note, rights); + _vendor = Agent.harvardInstance(); + } + + /** + * **************************************************************** PUBLIC INSTANCE METHODS. + * **************************************************************** + */ + + /** Outputs minimal information about the application */ + @Override + public void show() { + _level--; + } + + /** + * Outputs detailed information about the application, including configuration, available modules + * and handlers, etc. + */ + @Override + public void show(App app) { + String margin = getIndent(++_level); + String margn2 = margin + " "; + String margn3 = margn2 + " "; + + _writer.println(margin + elementStart("app")); + String[][] attrs = {{"date", date.format(_je.getDate())}}; + _writer.println(margn2 + element("api", attrs, app.getRelease())); + String configFile = _je.getConfigFile(); + if (configFile != null) { + _writer.println(margn2 + element("configuration", configFile)); + } + String s = _je.getSaxClass(); + if (s != null) { + _writer.println(margn2 + element("saxParser", s)); + } + s = _je.getJhoveHome(); + if (s != null) { + _writer.println(margn2 + element("jhoveHome", s)); + } + s = _je.getEncoding(); + if (s != null) { + _writer.println(margn2 + element("encoding", s)); + } + s = _je.getTempDirectory(); + if (s != null) { + _writer.println(margn2 + element("tempDirectory", s)); + } + _writer.println(margn2 + element("bufferSize", Integer.toString(_je.getBufferSize()))); + _writer.println(margn2 + elementStart("modules")); + Iterator iter = _je.getModuleMap().keySet().iterator(); + while (iter.hasNext()) { + Module module = _je.getModule(iter.next()); + String[][] attr2 = {{"release", module.getRelease()}}; + _writer.println(margn3 + element("module", attr2, module.getName())); + } + _writer.println(margn2 + elementEnd("modules")); + _writer.println(margn2 + elementStart("outputHandlers")); + iter = _je.getHandlerMap().keySet().iterator(); + while (iter.hasNext()) { + OutputHandler handler = _je.getHandler(iter.next()); + String[][] attr2 = {{"release", handler.getRelease()}}; + _writer.println(margn3 + element("outputHandler", attr2, handler.getName())); + } + _writer.println(margn2 + elementEnd("outputHandlers")); + _writer.println(margn2 + element("usage", app.getUsage())); + _writer.println(margn2 + element("rights", app.getRights())); + _writer.println(margin + elementEnd("app")); + _level--; + } + + /** Outputs information about the OutputHandler specified in the parameter */ + @Override + public void show(OutputHandler handler) { + String margin = getIndent(++_level); + String margn2 = margin + " "; + _writer.println(margin + elementStart("handler")); + _writer.println(margn2 + element("name", handler.getName())); + _writer.println(margn2 + element("release", handler.getRelease())); + _writer.println(margn2 + element("date", date.format(handler.getDate()))); + List list = handler.getSpecification(); + int n = list.size(); + if (n > 0) { + _writer.println(margn2 + elementStart("specifications")); + ++_level; + for (int i = 0; i < n; i++) { + showDocument(list.get(i)); + } + --_level; + _writer.println(margn2 + elementEnd("specifications")); + } + Agent vendor = handler.getVendor(); + if (vendor != null) { + showAgent(vendor, "Vendor"); + } + String s; + if ((s = handler.getNote()) != null) { + _writer.println(margn2 + element("note", s)); + } + if ((s = handler.getRights()) != null) { + _writer.println(margn2 + element("rights", s)); + } + _writer.println(margin + elementEnd("handler")); + _level--; + } + + /** Outputs information about a Module */ + @Override + public void show(Module module) { + String margin = getIndent(++_level); + String margn2 = margin + " "; + String margn3 = margn2 + " "; + + _writer.println(margin + elementStart("module")); + _writer.println(margn2 + element("name", module.getName())); + _writer.println(margn2 + element("release", module.getRelease())); + _writer.println(margn2 + element("date", HandlerBase.date.format(module.getDate()))); + String[] ss = module.getFormat(); + if (ss.length > 0) { + _writer.println(margn2 + elementStart("formats")); + for (int i = 0; i < ss.length; i++) { + _writer.println(margn3 + element("format", ss[i])); + } + _writer.println(margn2 + elementEnd("formats")); + } + String s = module.getCoverage(); + if (s != null) { + _writer.println(margn2 + element("coverage", s)); + } + ss = module.getMimeType(); + if (ss.length > 0) { + _writer.println(margn2 + elementStart("mimeTypes")); + for (int i = 0; i < ss.length; i++) { + _writer.println(margn3 + element("mimeType", ss[i])); + } + ; + _writer.println(margn2 + elementEnd("mimeTypes")); + } + List list1 = module.getSignature(); + int n = list1.size(); + if (n > 0) { + _writer.println(margn2 + elementStart("signatures")); + ++_level; + for (int i = 0; i < n; i++) { + showSignature(list1.get(i)); + } + _level--; + _writer.println(margn2 + elementEnd("signatures")); + } + List list2 = module.getSpecification(); + n = list2.size(); + if (n > 0) { + _writer.println(margn2 + elementStart("specifications")); + ++_level; + for (int i = 0; i < n; i++) { + showDocument(list2.get(i)); + } + --_level; + _writer.println(margn2 + elementEnd("specifications")); + } + List ftr = module.getFeatures(); + if (ftr != null && !ftr.isEmpty()) { + _writer.println(margn2 + elementStart("features")); + Iterator iter = ftr.iterator(); + while (iter.hasNext()) { + s = iter.next(); + _writer.println(margn3 + element("feature", s)); + } + _writer.println(margn2 + elementEnd("features")); + } + _writer.println(margn2 + elementStart("methodology")); + if ((s = module.getWellFormedNote()) != null) { + _writer.println(margn3 + element("wellFormed", s)); + } + if ((s = module.getValidityNote()) != null) { + _writer.println(margn3 + element("validity", s)); + } + if ((s = module.getRepInfoNote()) != null) { + _writer.println(margn3 + element("repInfo", s)); + } + _writer.println(margn2 + elementEnd("methodology")); + Agent vendor = module.getVendor(); + if (vendor != null) { + showAgent(vendor, "Vendor"); + } + if ((s = module.getNote()) != null) { + _writer.println(margn2 + element("note", s)); + } + if ((s = module.getRights()) != null) { + _writer.println(margn2 + element("rights", s)); + } + _writer.println(margin + elementEnd("module")); + _level--; + } + + /** Outputs the information contained in a RepInfo object */ + @Override + public void show(RepInfo info) { + String margin = getIndent(++_level); + String margn2 = margin + " "; + String margn3 = margn2 + " "; + + Module module = info.getModule(); + _logger.info("Reporting RepInfo"); + if (_je.getSignatureFlag()) { + _logger.info("Checking signatures only"); + } + String[][] attrs = {{"uri", cleanURIString(info.getUri())}}; + _writer.println(margin + elementStart("repInfo", attrs)); + if (module != null) { + String[][] attr2 = { + {"release", module.getRelease()}, {"date", date.format(module.getDate())} + }; + _writer.println(margn2 + element("reportingModule", attr2, module.getName())); + } + /* + * else { String [][] attr2 = { {"severity", "error"} }; _writer.println + * (margn2 + element ("message", attr2, + * "file not found or not readable")); } + */ + Date date = info.getCreated(); + if (date != null) { + _writer.println(margn2 + element("created", toDateTime(date))); + } + date = info.getLastModified(); + if (date != null) { + _writer.println(margn2 + element("lastModified", toDateTime(date))); + } + long size = info.getSize(); + if (size > -1) { + _writer.println(margn2 + element("size", Long.toString(size))); + } + String s = info.getFormat(); + if (s != null) { + _writer.println(margn2 + element("format", s)); + } + s = info.getVersion(); + if (s != null) { + _writer.println(margn2 + element("version", s)); + } + String wfStr; + if (!_je.getSignatureFlag()) { + switch (info.getWellFormed()) { + case RepInfo.TRUE: + wfStr = "Well-Formed"; + break; + + case RepInfo.FALSE: + wfStr = "Not well-formed"; + break; + + default: + wfStr = "Unknown"; + break; + } + // If it's well-formed, append validity information + if (info.getWellFormed() == RepInfo.TRUE) { + switch (info.getValid()) { + case RepInfo.TRUE: + wfStr += " and valid"; + break; + + case RepInfo.FALSE: + wfStr += ", but not valid"; + break; + + // case UNDETERMINED: add nothing + } + } + _logger.info("Validity/WF status: " + wfStr); + _writer.println(margn2 + element("status", wfStr)); + } else { + // If we aren't checking signatures, we still need to say something. + switch (info.getWellFormed()) { + case RepInfo.TRUE: + wfStr = "Well-Formed"; + break; + + default: + wfStr = "Not well-formed"; + break; + } + _writer.println(margn2 + element("status", wfStr)); } - /** - * Convert compression scheme value (based on the TIFF compression convention) - * to a label - */ - private String compressionSchemeToString (int n) { - for (int i = 0; i < NisoImageMetadata.COMPRESSION_SCHEME_INDEX.length; i++) { - if (n == NisoImageMetadata.COMPRESSION_SCHEME_INDEX[i]) - return NisoImageMetadata.COMPRESSION_SCHEME[i]; + List list1 = info.getSigMatch(); + int n = list1.size(); + if (n > 0) { + _writer.println(margn2 + elementStart("sigMatch")); + _level++; + for (int i = 0; i < n; i++) { + _writer.println(margn2 + element("module", list1.get(i))); + } + _level--; + _writer.println(margn2 + elementEnd("sigMatch")); + } + + List list2 = info.getMessage(); + n = list2.size(); + if (n > 0) { + _writer.println(margn2 + elementStart("messages")); + _level++; + for (int i = 0; i < n; i++) { + showMessage(list2.get(i)); + } + _level--; + _writer.println(margn2 + elementEnd("messages")); + } + s = info.getMimeType(); + if (s != null) { + _writer.println(margn2 + element("mimeType", s)); + } + + List list3 = info.getProfile(); + n = list3.size(); + if (n > 0) { + _writer.println(margn2 + elementStart("profiles")); + for (int i = 0; i < n; i++) { + _writer.println(margn3 + element("profile", list3.get(i))); + } + _writer.println(margn2 + elementEnd("profiles")); + } + + Map map = info.getProperty(); + if (map != null) { + if (map.size() > 0) { + _writer.println(margn2 + elementStart("properties")); + Iterator iter = map.keySet().iterator(); + while (iter.hasNext()) { + String key = iter.next(); + Property property = info.getProperty(key); + showProperty(property); } - return Integer.toString(n); + _writer.println(margn2 + elementEnd("properties")); + } } - /** - * Display the audio metadata formatted according to - * the AES schema. - * @param aes AES audio metadata - */ - protected void showAESAudioMetadata (AESAudioMetadata aes) - { - _level += 3; - final String margin = getIndent (_level); - final String margn2 = margin + " "; - final String margn3 = margn2 + " "; - final String margn4 = margn3 + " "; - final String margn5 = margn4 + " "; - //final String margn6 = margn5 + " "; - - // ID strings. These are arbitrary, but must be unique - // within the document. - final String formatRegionID = "J1"; - final String faceRegionID = "J2"; - final String faceID = "J3"; - final String audioObjectID = "J4"; - final String streamIDBase = "J9"; - - _sampleRate = aes.getSampleRate (); - - final String [][] attrs = {{"xmlns:aes", "http://www.aes.org/audioObject"}, - {"xmlns:tcf", "http://www.aes.org/tcf"}, - {"xmlns:xsi", - "http://www.w3.org/2001/XMLSchema-instance"}, - {"ID", audioObjectID }, - {"analogDigitalFlag", - aes.getAnalogDigitalFlag ()}, - {"disposition", - "Validated by JHOVE"}, - {"schemaVersion","1.02b"}}; - _writer.println (margin + elementStart ("aes:audioObject", attrs)); - String s = aes.getFormat (); - if (s != null) { - String v = aes.getSpecificationVersion (); - String[][] fmattrs = new String[1][2]; - fmattrs[0][0] = "specificationVersion"; - if (v != null) { - fmattrs[0][1] = v; - } - else { - // Shouldn't happen - fmattrs[0][1] = ""; - } - _writer.println (margn2 + element - ("aes:format", fmattrs, s)); + List list4 = info.getChecksum(); + n = list4.size(); + if (n > 0) { + _writer.println(margn2 + elementStart("checksums")); + _level++; + for (int i = 0; i < n; i++) { + showChecksum(list4.get(i)); + } + _level--; + _writer.println(margn2 + elementEnd("checksums")); + } + + s = info.getNote(); + if (s != null) { + _writer.println(margn2 + element("note", s)); + } + + _writer.println(margin + elementEnd("repInfo")); + _level--; + } + + /** + * **************************************************************** PRIVATE INSTANCE METHODS. + * **************************************************************** + */ + protected void showAgent(Agent agent, String label) { + String margin = getIndent(++_level); + String margn2 = margin + " "; + + String[][] attrs = {{"type", label}}; + _writer.println(margin + elementStart("agent", attrs)); + _writer.println(margn2 + element("name", agent.getName())); + _writer.println(margn2 + element("type", agent.getType().toString())); + String s = agent.getAddress(); + if (s != null) { + _writer.println(margn2 + element("address", s)); + } + if ((s = agent.getTelephone()) != null) { + _writer.println(margn2 + element("telephone", s)); + } + if ((s = agent.getFax()) != null) { + _writer.println(margn2 + element("fax", s)); + } + if ((s = agent.getEmail()) != null) { + _writer.println(margn2 + element("email", s)); + } + if ((s = agent.getWeb()) != null) { + _writer.println(margn2 + element("web", s)); + } + _writer.println(margin + elementEnd("agent")); + _level--; + } + + protected void showChecksum(Checksum checksum) { + String margin = getIndent(++_level); + + String[][] attrs = {{"type", checksum.getType().toString()}}; + _writer.println(margin + element("checksum", attrs, checksum.getValue())); + + _level--; + } + + protected void showDocument(Document document) { + String margin = getIndent(++_level); + String margn2 = margin + " "; + + // String [][] attrs = { {"type", label} }; + _writer.println(margin + elementStart("specification")); + _writer.println(margn2 + element("title", document.getTitle())); + _writer.println(margn2 + element("type", document.getType().toString())); + List list1 = document.getAuthor(); + int n = list1.size(); + if (n > 0) { + _writer.println(margn2 + elementStart("authors")); + ++_level; + for (int i = 0; i < n; i++) { + showAgent(list1.get(i), "Author"); + } + _level--; + _writer.println(margn2 + elementEnd("authors")); + } + List list2 = document.getPublisher(); + n = list2.size(); + if (n > 0) { + ++_level; + _writer.println(margn2 + elementStart("publishers")); + for (int i = 0; i < n; i++) { + showAgent(list2.get(i), "Publisher"); + } + _writer.println(margn2 + elementEnd("publishers")); + _level--; + } + String s = document.getEdition(); + if (s != null) { + _writer.println(margn2 + element("edition", s)); + } + if ((s = document.getDate()) != null) { + _writer.println(margn2 + element("date", s)); + } + if ((s = document.getEnumeration()) != null) { + _writer.println(margn2 + element("enumeration", s)); + } + if ((s = document.getPages()) != null) { + _writer.println(margn2 + element("pages", s)); + } + List list3 = document.getIdentifier(); + n = list3.size(); + if (n > 0) { + _writer.println(margn2 + elementStart("identifiers")); + ++_level; + for (int i = 0; i < n; i++) { + showIdentifier(list3.get(i)); + } + _level--; + _writer.println(margn2 + elementEnd("identifiers")); + } + if ((s = document.getNote()) != null) { + _writer.println(margn2 + element("note", s)); + } + _writer.println(margin + elementEnd("specification")); + _level--; + } + + /** + * Do the final output. This should be in a suitable format for including multiple files between + * the header and the footer, and the XML of the header and footer must balance out. + */ + @Override + public void showFooter() { + String margin = getIndent(_level--); + _writer.println(margin + elementEnd("jhove")); + + _writer.flush(); + } + + /** + * Do the initial output. This should be in a suitable format for including multiple files between + * the header and the footer, and the XML of the header and footer must balance out. + */ + @Override + public void showHeader() { + String margin = getIndent(++_level); + String margn2 = margin + " "; + + if (_encoding != null) { + _writer.println(margin + xmlDecl(_encoding)); + } else { + _writer.println(margin + xmlDecl()); + } + + String[][] attrs = { + {"xmlns:xsi", "http://www.w3.org/2001/XMLSchema-instance"}, + {"xmlns", "http://schema.openpreservation.org/ois/xml/ns/jhove"}, + { + "xsi:schemaLocation", + "http://schema.openpreservation.org/ois/xml/ns/jhove " + + "https://schema.openpreservation.org/ois/xml/xsd/jhove/" + + SCHEMA_VERSION + + "/jhove.xsd" + }, + {"name", _app.getName()}, + {"release", _app.getRelease()}, + {"date", HandlerBase.date.format(_app.getDate())} + }; + _writer.println(margin + elementStart("jhove", attrs)); + _writer.println(margn2 + element("date", toDateTime(new Date()))); + } + + protected void showIdentifier(Identifier identifier) { + String margin = getIndent(++_level); + String margn2 = margin + " "; + + _writer.println(margin + elementStart("identifier")); + _writer.println(margn2 + element("value", identifier.getValue())); + _writer.println(margn2 + element("type", identifier.getType().toString())); + String note = identifier.getNote(); + if (note != null) { + _writer.println(margn2 + element("note", note)); + } + _writer.println(margin + elementEnd("identifier")); + _level--; + } + + protected void showMessage(Message message) { + String margin = getIndent(++_level); + String[][] attrs = new String[4][]; + boolean hasAttr = false; + attrs[0] = new String[] {"subMessage", null}; + attrs[1] = new String[] {"offset", null}; + attrs[2] = new String[] {"severity", null}; + attrs[3] = new String[] {"id", null}; + + String submsg = message.getSubMessage(); + if (submsg != null) { + attrs[0][1] = submsg; + hasAttr = true; + } + long offset = message.getOffset(); + if (offset > -1) { + attrs[1][1] = Long.toString(offset); + hasAttr = true; + } + if (!message.getPrefix().isEmpty()) { + attrs[2][1] = message.getPrefix().toLowerCase(); + hasAttr = true; + } + String id = message.getJhoveMessage().getId(); + if (!(id == null || id.isEmpty() || id.equals(JhoveMessages.NO_ID))) { + attrs[3][1] = message.getId(); + hasAttr = true; + } + if (hasAttr) { + _writer.println(margin + element("message", attrs, message.getMessage())); + } else { + _writer.println(margin + element("message", message.getMessage())); + } + _level--; + } + + protected void showSignature(Signature signature) { + String margin = getIndent(++_level); + String margin1 = margin + " "; + String sigValue; + + _writer.println(margin + elementStart("signature")); + if (signature.isStringValue()) { + sigValue = signature.getValueString(); + } else { + sigValue = signature.getValueHexString(); + } + _writer.println(margin1 + element("type", signature.getType().toString())); + _writer.println(margin1 + element("value", sigValue)); + if (signature.getType().equals(SignatureType.MAGIC)) { + if (((InternalSignature) signature).hasFixedOffset()) { + _writer.println( + margin1 + + element( + "offset", + "0x" + Integer.toHexString(((InternalSignature) signature).getOffset()))); + } + } + String note = signature.getNote(); + if (note != null) { + _writer.println(margin1 + element("note", note)); + } + String use = signature.getUse().toString(); + if (use != null) { + _writer.println(margin1 + element("use", use)); + } + _writer.println(margin + elementEnd("signature")); + _level--; + } + + /* Do special conversions on values as needed. */ + protected String valueToString(Object obj) { + if (obj instanceof Date) { + return toDateTime((Date) obj); + } + return obj.toString(); + } + + protected void showProperty(Property property) { + String margin = getIndent(++_level); + String margn2 = margin + " "; + String margn3 = margn2 + " "; + String margn4 = margn3 + " "; + + PropertyArity arity = property.getArity(); + PropertyType type = property.getType(); + + // If the property would generate an empty element, don't output it, + // as this could result in a schema violation. + if (Utils.isPropertyEmpty(property, arity)) return; + + boolean valueIsProperty = PropertyType.PROPERTY.equals(type); + boolean valueIsNiso = PropertyType.NISOIMAGEMETADATA.equals(type); + boolean valueIsAes = PropertyType.AESAUDIOMETADATA.equals(type); + boolean valueIsTextMD = PropertyType.TEXTMDMETADATA.equals(type); + + String[][] propAttrs = new String[2][]; + propAttrs[0] = new String[] {"arity", arity.toString()}; + propAttrs[1] = new String[] {"type", type.toString()}; + _writer.println(margn2 + elementStart("property")); + _writer.println(margn3 + element("name", property.getName())); + _writer.println(margn3 + elementStart("values", propAttrs)); + if (arity.equals(PropertyArity.SCALAR)) { + /* Just a single value */ + if (valueIsProperty) { + showProperty((Property) property.getValue()); + } else if (valueIsNiso) { + _writer.println(margn4 + elementStart("value")); + showNisoImageMetadata((NisoImageMetadata) property.getValue()); + _writer.println(margn4 + elementEnd("value")); + } else if (valueIsAes) { + _writer.println(margn4 + elementStart("value")); + showAESAudioMetadata((AESAudioMetadata) property.getValue()); + _writer.println(margn4 + elementEnd("value")); + } else if (valueIsTextMD) { + _writer.println(margn4 + elementStart("value")); + showTextMDMetadata((TextMDMetadata) property.getValue()); + _writer.println(margn4 + elementEnd("value")); + } else { + _writer.println(margn4 + element("value", property.getValue().toString())); + } + } else if (arity.equals(PropertyArity.LIST)) { + List propList = (List) property.getValue(); + ListIterator iter = propList.listIterator(); + while (iter.hasNext()) { + Object val = iter.next(); + if (valueIsProperty) { + showProperty((Property) val); + } else if (valueIsNiso) { + _writer.println(margn4 + elementStart("value")); + showNisoImageMetadata((NisoImageMetadata) property.getValue()); + _writer.println(margn4 + elementEnd("value")); + } else if (valueIsAes) { + _writer.println(margn4 + elementStart("value")); + showAESAudioMetadata((AESAudioMetadata) property.getValue()); + _writer.println(margn4 + elementEnd("value")); + } else if (valueIsTextMD) { + _writer.println(margn4 + elementStart("value")); + showTextMDMetadata((TextMDMetadata) property.getValue()); + _writer.println(margn4 + elementEnd("value")); + } else { + _writer.println(margn4 + element("value", valueToString(val))); } - s = aes.getAppSpecificData(); - if (s != null) { - _writer.println (margn2 + element - ("aes:appSpecificData", s)); + } + } else if (arity.equals(PropertyArity.MAP)) { + /* + * For a map, the key is the "key" attribute of its corresponding + * value + */ + Map propMap = (Map) property.getValue(); + Iterator keyIter = propMap.keySet().iterator(); + while (keyIter.hasNext()) { + Object key = keyIter.next(); + String keystr = key.toString(); + Object val = propMap.get(key); + String[][] attrs = new String[1][]; + String[] keyAttr = new String[2]; + keyAttr[0] = "key"; + keyAttr[1] = keystr; + attrs[0] = keyAttr; + if (valueIsProperty) { + Property pval = (Property) val; + // If the key equals the property name, suppress the key + if (pval.getName().equals(keystr)) { + _writer.print(margn4 + elementStart("value")); + } else { + _writer.print(margn4 + elementStart("value", attrs)); + } + showProperty(pval); + _writer.println(margn4 + elementEnd("value")); + } else if (valueIsNiso) { + _writer.println(margn4 + elementStart("value")); + showNisoImageMetadata((NisoImageMetadata) val); + _writer.println(margn4 + elementEnd("value")); + } else if (valueIsAes) { + _writer.println(margn4 + elementStart("value")); + showAESAudioMetadata((AESAudioMetadata) val); + _writer.println(margn4 + elementEnd("value")); + } else if (valueIsTextMD) { + _writer.println(margn4 + elementStart("value")); + showTextMDMetadata((TextMDMetadata) val); + _writer.println(margn4 + elementEnd("value")); + } else { + _writer.println(margn4 + element("value", attrs, valueToString(val))); } - s = aes.getAudioDataEncoding (); - if (s != null) { - _writer.println (margn2 + element - ("aes:audioDataEncoding", s)); + } + } else if (arity.equals(PropertyArity.SET)) { + Set propSet = (Set) property.getValue(); + Iterator iter = propSet.iterator(); + while (iter.hasNext()) { + Object val = iter.next(); + if (valueIsProperty) { + showProperty((Property) val); + } else { + _writer.println(margn4 + element("value", valueToString(val))); } - int in = aes.getByteOrder (); - if (in != AESAudioMetadata.NULL) { - _writer.println (margn2 + element ("aes:byteOrder", - in == AESAudioMetadata.BIG_ENDIAN ? - "BIG_ENDIAN" : "LITTLE_ENDIAN")); + } + } else if (arity.equals(PropertyArity.ARRAY)) { + showArrayProperty(property, margn4); + } + _writer.println(margn3 + elementEnd("values")); + _writer.println(margn2 + elementEnd("property")); + --_level; + } + + /* + * The array property has so many special cases of its own that we break it + * out of showProperty + */ + protected void showArrayProperty(Property property, String margin) { + boolean[] boolArray = null; + byte[] byteArray = null; + char[] charArray = null; + java.util.Date[] dateArray = null; + double[] doubleArray = null; + float[] floatArray = null; + int[] intArray = null; + long[] longArray = null; + Object[] objArray = null; + Property[] propArray = null; + short[] shortArray = null; + String[] stringArray = null; + Rational[] rationalArray = null; + NisoImageMetadata[] nisoArray = null; + AESAudioMetadata[] aesArray = null; + TextMDMetadata[] textMDArray = null; + int n = 0; + + PropertyType propType = property.getType(); + if (PropertyType.BOOLEAN.equals(propType)) { + boolArray = (boolean[]) property.getValue(); + n = boolArray.length; + } else if (PropertyType.BYTE.equals(propType)) { + byteArray = (byte[]) property.getValue(); + n = byteArray.length; + } else if (PropertyType.CHARACTER.equals(propType)) { + charArray = (char[]) property.getValue(); + n = charArray.length; + } else if (PropertyType.DATE.equals(propType)) { + dateArray = (java.util.Date[]) property.getValue(); + n = dateArray.length; + } else if (PropertyType.DOUBLE.equals(propType)) { + doubleArray = (double[]) property.getValue(); + n = doubleArray.length; + } else if (PropertyType.FLOAT.equals(propType)) { + floatArray = (float[]) property.getValue(); + n = floatArray.length; + } else if (PropertyType.INTEGER.equals(propType)) { + intArray = (int[]) property.getValue(); + n = intArray.length; + } else if (PropertyType.LONG.equals(propType)) { + longArray = (long[]) property.getValue(); + n = longArray.length; + } else if (PropertyType.OBJECT.equals(propType)) { + objArray = (Object[]) property.getValue(); + n = objArray.length; + } else if (PropertyType.SHORT.equals(propType)) { + shortArray = (short[]) property.getValue(); + n = shortArray.length; + } else if (PropertyType.STRING.equals(propType)) { + stringArray = (String[]) property.getValue(); + n = stringArray.length; + } else if (PropertyType.RATIONAL.equals(propType)) { + rationalArray = (Rational[]) property.getValue(); + n = rationalArray.length; + } else if (PropertyType.PROPERTY.equals(propType)) { + propArray = (Property[]) property.getValue(); + n = propArray.length; + } else if (PropertyType.NISOIMAGEMETADATA.equals(propType)) { + nisoArray = (NisoImageMetadata[]) property.getValue(); + n = nisoArray.length; + } else if (PropertyType.AESAUDIOMETADATA.equals(propType)) { + aesArray = (AESAudioMetadata[]) property.getValue(); + n = aesArray.length; + } else if (PropertyType.TEXTMDMETADATA.equals(propType)) { + textMDArray = (TextMDMetadata[]) property.getValue(); + n = textMDArray.length; + } + + for (int i = 0; i < n; i++) { + String elem; + if (PropertyType.BOOLEAN.equals(propType)) { + elem = String.valueOf(boolArray[i]); + } else if (PropertyType.BYTE.equals(propType)) { + elem = String.valueOf(byteArray[i]); + } else if (PropertyType.CHARACTER.equals(propType)) { + elem = String.valueOf(charArray[i]); + } else if (PropertyType.DATE.equals(propType)) { + elem = dateArray[i].toString(); + } else if (PropertyType.DOUBLE.equals(propType)) { + elem = String.valueOf(doubleArray[i]); + } else if (PropertyType.FLOAT.equals(propType)) { + elem = String.valueOf(floatArray[i]); + } else if (PropertyType.INTEGER.equals(propType)) { + elem = String.valueOf(intArray[i]); + } else if (PropertyType.LONG.equals(propType)) { + elem = String.valueOf(longArray[i]); + } else if (PropertyType.OBJECT.equals(propType)) { + elem = valueToString(objArray[i]); + } else if (PropertyType.SHORT.equals(propType)) { + elem = String.valueOf(shortArray[i]); + } else if (PropertyType.STRING.equals(propType)) { + elem = stringArray[i]; + } else if (PropertyType.RATIONAL.equals(propType)) { + elem = rationalArray[i].toString(); + } else if (PropertyType.PROPERTY.equals(propType)) { + showProperty(propArray[i]); + continue; + } else if (PropertyType.NISOIMAGEMETADATA.equals(propType)) { + showNisoImageMetadata(nisoArray[i]); + continue; + } else if (PropertyType.AESAUDIOMETADATA.equals(propType)) { + showAESAudioMetadata(aesArray[i]); + continue; + } else if (PropertyType.TEXTMDMETADATA.equals(propType)) { + showTextMDMetadata(textMDArray[i]); + continue; + } else elem = ""; + _writer.println(margin + element("value", elem)); + } + } + + /** + * Display the text metadata formatted according to the textMD schema (see + * http://www.loc.gov/standards/textMD). + * + * @param textMD textMD text metadata + */ + protected void showTextMDMetadata(TextMDMetadata textMD) { + String margin = getIndent(++_level); + String margn2 = margin + " "; + String margn3 = margn2 + " "; + + String[][] attrs = { + {"xmlns:textmd", TextMDMetadata.NAMESPACE}, + {"xmlns:xsi", "http://www.w3.org/2001/XMLSchema-instance"}, + {"xsi:schemaLocation", TextMDMetadata.NAMESPACE + " " + TextMDMetadata.DEFAULT_LOCATION}, + }; + _writer.println(margin + elementStart("textmd:textMD", attrs)); + _writer.println(margn2 + elementStart("textmd:character_info")); + _writer.println(margn3 + element("textmd:charset", textMD.getCharset())); + _writer.println(margn3 + element("textmd:byte_order", textMD.getByte_orderString())); + _writer.println(margn3 + element("textmd:byte_size", textMD.getByte_size())); + if ("variable".equals(textMD.getCharacter_size())) { + String[][] attrs1 = {{"encoding", textMD.getCharset()}}; + _writer.println(margn3 + element("textmd:character_size", attrs1, "variable")); + } else if (textMD.getCharacter_size() != null && textMD.getCharacter_size().length() != 0) { + _writer.println(margn3 + element("textmd:character_size", textMD.getCharacter_size())); + } + _writer.println(margn3 + element("textmd:linebreak", textMD.getLinebreakString())); + _writer.println(margn2 + elementEnd("textmd:character_info")); + if (textMD.getLanguage() != null && textMD.getLanguage().length() != 0) { + _writer.println(margn2 + element("textmd:language", textMD.getLanguage())); + } + if (textMD.getMarkup_basis() != null && textMD.getMarkup_basis().length() != 0) { + if (textMD.getMarkup_basis_version() != null) { + String[][] attrs1 = {{"version", textMD.getMarkup_basis_version()}}; + _writer.println(margn2 + element("textmd:markup_basis", attrs1, textMD.getMarkup_basis())); + } else { + _writer.println(margn2 + element("textmd:markup_basis", textMD.getMarkup_basis())); + } + } + if (textMD.getMarkup_language() != null && textMD.getMarkup_language().length() != 0) { + if (textMD.getMarkup_language_version() != null) { + String[][] attrs1 = {{"version", textMD.getMarkup_language_version()}}; + _writer.println( + margn2 + element("textmd:markup_language", attrs1, textMD.getMarkup_language())); + } else { + _writer.println(margn2 + element("textmd:markup_language", textMD.getMarkup_language())); + } + } + _writer.println(margin + elementEnd("textmd:textMD")); + _level--; + } + + /** + * Display the NISO image metadata formatted according to the MIX schema. The schema which is used + * may be 0.2 or 1.0 or 2.0, depending on the module parameters. + * + * @param niso NISO image metadata + */ + protected void showNisoImageMetadata(NisoImageMetadata niso) { + if ("0.2".equals(_je.getMixVersion())) { + showNisoImageMetadata02(niso); + } else if ("1.0".equals(_je.getMixVersion())) { + showNisoImageMetadata10(niso); + } else { + showNisoImageMetadata20(niso); + } + } + + /** Display the NISO image metadata formatted according to the MIX 0.2 schema. */ + protected void showNisoImageMetadata02(NisoImageMetadata niso) { + String margin = getIndent(++_level); + + String[][] attrs = { + {"xmlns:mix", "http://www.loc.gov/mix/"}, + {"xmlns:xsi", "http://www.w3.org/2001/XMLSchema-instance"}, + {"xsi:schemaLocation", "http://www.loc.gov/mix/ http://www.loc.gov/mix/mix02.xsd"} + }; + _writer.println(margin + elementStart("mix:mix", attrs)); + + showNisoBasicImageParameters02(niso, margin); + showNisoImageCreation02(niso, margin); + showNisoImagingPerformanceAssessment02(niso, margin); + showNisoChangeHistory02(niso, margin); + + _writer.println(margin + elementEnd("mix:mix")); + + _level--; + } + + /* + * The NISO Metadata output is split into multiple functions so that they're + * merely outrageously big rather than disgustingly big + */ + /* Top level element 1 of 4: BasicImageParameters */ + protected void showNisoBasicImageParameters02(NisoImageMetadata niso, String margin) { + String margn2 = margin + " "; + String margn3 = margn2 + " "; + String margn4 = margn3 + " "; + String margn5 = margn4 + " "; + String margn6 = margn5 + " "; + + _writer.println(margn2 + elementStart("mix:BasicImageParameters")); + + // Here things get rather deeply nested, and we want to be able + // to back out if it turns out that we really have no content to + // deliver. So rather than writing directly to the writer, we + // write to a StringBuffer that may or may not get used. + StringBuffer fBuf = new StringBuffer(margn3 + elementStart("mix:Format") + EOL); + boolean useFBuf = false; // set to true if some data show up + String s = niso.getMimeType(); + if (s != null) { + fBuf.append(margn4 + element("mix:MIMEType", s) + EOL); + useFBuf = true; + } + if ((s = niso.getByteOrder()) != null) { + fBuf.append(margn4 + element("mix:ByteOrder", s) + EOL); + useFBuf = true; + } + int comp = niso.getCompressionScheme(); + int level = niso.getCompressionLevel(); + if (comp != NisoImageMetadata.NULL || level != NisoImageMetadata.NULL) { + fBuf.append(margn4 + elementStart("mix:Compression") + EOL); + if (comp != NisoImageMetadata.NULL) { + fBuf.append(margn5 + element("mix:CompressionScheme", Integer.toString(comp)) + EOL); + } + if (level != NisoImageMetadata.NULL) { + fBuf.append(margn5 + element("mix:CompressionLevel", Integer.toString(level)) + EOL); + } + fBuf.append(margn4 + elementEnd("mix:Compression") + EOL); + useFBuf = true; + } + // Nest photometric interpretation tentative buffer in fBuf + StringBuffer piBuf = + new StringBuffer(margn4 + elementStart("mix:PhotometricInterpretation") + EOL); + boolean usePIBuf = false; + int n = niso.getColorSpace(); + if (n != NisoImageMetadata.NULL) { + piBuf.append(margn5 + element("mix:ColorSpace", Integer.toString(n)) + EOL); + usePIBuf = true; + } + String s2 = niso.getProfileURL(); + if ((s = niso.getProfileName()) != null || s2 != null) { + piBuf.append(margn5 + elementStart("mix:ICCProfile") + EOL); + if (s != null) { + piBuf.append(margn6 + element("mix:ProfileName", s) + EOL); + } + if (s2 != null) { + piBuf.append(margn6 + element("mix:ProfileURL", s2) + EOL); + } + piBuf.append(margn5 + elementEnd("mix:ICCProfile") + EOL); + usePIBuf = true; + } + int[] iarray = niso.getYCbCrSubSampling(); + if (iarray != null) { + piBuf.append(margn5 + element("mix:YCbCrSubSampling", integerArray(iarray)) + EOL); + usePIBuf = true; + } + if ((n = niso.getYCbCrPositioning()) != NisoImageMetadata.NULL) { + piBuf.append(margn5 + element("mix:YCbCrPositioning", Integer.toString(n)) + EOL); + usePIBuf = true; + } + Rational[] rarray = niso.getYCbCrCoefficients(); + if (rarray != null) { + piBuf.append(margn5 + element("mix:YCbCrCoefficients", rationalArray(rarray)) + EOL); + usePIBuf = true; + } + if ((rarray = niso.getReferenceBlackWhite()) != null) { + piBuf.append(margn5 + element("mix:ReferenceBlackWhite", rationalArray(rarray)) + EOL); + usePIBuf = true; + } + piBuf.append(margn4 + elementEnd("mix:PhotometricInterpretation") + EOL); + if (usePIBuf) { + fBuf.append(piBuf); + useFBuf = true; + } + + // Now a tentative buffer for the Segments element + StringBuffer segBuf = new StringBuffer(margn4 + elementStart("mix:Segments") + EOL); + boolean useSegBuf = false; + if ((n = niso.getSegmentType()) != NisoImageMetadata.NULL) { + segBuf.append(margn5 + element("mix:SegmentType", Integer.toString(n)) + EOL); + useSegBuf = true; + } + long[] larray = niso.getStripOffsets(); + if (larray != null) { + segBuf.append(margn5 + element("mix:StripOffsets", longArray(larray)) + EOL); + useSegBuf = true; + } + long ln = niso.getRowsPerStrip(); + if (ln != NisoImageMetadata.NULL) { + segBuf.append(margn5 + element("mix:RowsPerStrip", Long.toString(ln)) + EOL); + useSegBuf = true; + } + if ((larray = niso.getStripByteCounts()) != null) { + segBuf.append(margn5 + element("mix:StripByteCounts", longArray(larray)) + EOL); + useSegBuf = true; + } + if ((ln = niso.getTileWidth()) != NisoImageMetadata.NULL) { + segBuf.append(margn5 + element("mix:TileWidth", Long.toString(ln)) + EOL); + useSegBuf = true; + } + if ((ln = niso.getTileLength()) != NisoImageMetadata.NULL) { + segBuf.append(margn5 + element("mix:TileLength", Long.toString(ln)) + EOL); + useSegBuf = true; + } + if ((larray = niso.getTileOffsets()) != null) { + segBuf.append(margn5 + element("mix:TileOffsets", longArray(larray)) + EOL); + useSegBuf = true; + } + if ((larray = niso.getTileByteCounts()) != null) { + segBuf.append(margn5 + element("mix:TileByteCounts", longArray(larray)) + EOL); + useSegBuf = true; + } + segBuf.append(margn4 + elementEnd("mix:Segments") + EOL); + if (useSegBuf) { + fBuf.append(segBuf); + useFBuf = true; + } + if ((n = niso.getPlanarConfiguration()) != NisoImageMetadata.NULL) { + fBuf.append(margn4 + element("mix:PlanarConfiguration", Integer.toString(n)) + EOL); + } + fBuf.append(margn3 + elementEnd("mix:Format") + EOL); + if (useFBuf) { + _writer.print(fBuf.toString()); + } + + // Now a tentative buffer for the File element. It's actually + // pretty safe that this will have some content, but I'd rather + // be consistent. Besides, it's a good test of the pattern. + StringBuffer fileBuf = new StringBuffer(margn3 + elementStart("mix:File") + EOL); + boolean useFileBuf = false; + s = niso.getImageIdentifier(); + if (s != null) { + fileBuf.append(margn4 + element("mix:ImageIdentifier", s) + EOL); + useFileBuf = true; + } + ln = niso.getFileSize(); + if (ln != NisoImageMetadata.NULL) { + fileBuf.append(margn4 + element("mix:FileSize", Long.toString(ln)) + EOL); + useFileBuf = true; + } + n = niso.getChecksumMethod(); + s = niso.getChecksumValue(); + if (n != NisoImageMetadata.NULL || s != null) { + fileBuf.append(margn4 + elementStart("mix:Checksum") + EOL); + if (n != NisoImageMetadata.NULL) { + fileBuf.append(margn5 + element("mix:ChecksumMethod", Integer.toString(n)) + EOL); + } + if (s != null) { + fileBuf.append(margn5 + element("mix:ChecksumValue", s) + EOL); + } + fileBuf.append(margn4 + elementEnd("mix:Checksum") + EOL); + useFileBuf = true; + } + n = niso.getOrientation(); + if (n != NisoImageMetadata.NULL) { + fileBuf.append(margn4 + element("mix:Orientation", Integer.toString(n)) + EOL); + useFileBuf = true; + } + n = niso.getDisplayOrientation(); + if (n != NisoImageMetadata.NULL) { + fileBuf.append(margn4 + element("mix:DisplayOrientation", Integer.toString(n)) + EOL); + useFileBuf = true; + } + ln = niso.getXTargetedDisplayAR(); + long ln2 = niso.getYTargetedDisplayAR(); + if (ln != NisoImageMetadata.NULL || ln2 != NisoImageMetadata.NULL) { + fileBuf.append(margn4 + elementStart("mix:TargetedDisplayAR") + EOL); + if (ln != NisoImageMetadata.NULL) { + fileBuf.append(margn5 + element("mix:XTargetedDisplayAR", Long.toString(ln)) + EOL); + } + if (ln2 != NisoImageMetadata.NULL) { + fileBuf.append(margn5 + element("mix:YTargetedDisplayAR", Long.toString(ln2)) + EOL); + } + fileBuf.append(margn4 + elementEnd("mix:TargetedDisplayAR") + EOL); + useFileBuf = true; + } + + fileBuf.append(margn3 + elementEnd("mix:File") + EOL); + if (useFileBuf) { + _writer.print(fileBuf.toString()); + } + _writer.println(margn2 + elementEnd("mix:BasicImageParameters")); + } + + /* Top level element 2 of 4: ImageCreation */ + protected void showNisoImageCreation02(NisoImageMetadata niso, String margin) { + String margn2 = margin + " "; + String margn3 = margn2 + " "; + String margn4 = margn3 + " "; + String margn5 = margn4 + " "; + String margn6 = margn5 + " "; + + _writer.println(margn2 + elementStart("mix:ImageCreation")); + String s = niso.getSourceType(); + if (s != null) { + _writer.println(margn3 + element("mix:SourceType", s)); + } + s = niso.getSourceID(); + if (s != null) { + _writer.println(margn3 + element("mix:SourceID", s)); + } + s = niso.getImageProducer(); + if (s != null) { + _writer.println(margn3 + element("mix:ImageProducer", s)); + } + + StringBuffer hostBuf = new StringBuffer(margn3 + elementStart("mix:Host") + EOL); + boolean useHostBuf = false; + s = niso.getHostComputer(); + if (s != null) { + hostBuf.append(margn4 + element("mix:HostComputer", s) + EOL); + useHostBuf = true; + } + s = niso.getOS(); + if (s != null) { + hostBuf.append(margn4 + element("mix:OperatingSystem", s) + EOL); + useHostBuf = true; + } + s = niso.getOSVersion(); + if (s != null) { + hostBuf.append(margn4 + element("mix:OSVersion", s) + EOL); + useHostBuf = true; + } + hostBuf.append(margn3 + elementEnd("mix:Host") + EOL); + if (useHostBuf) { + _writer.print(hostBuf); + } + + s = niso.getDeviceSource(); + if (s != null) { + _writer.println(margn3 + element("mix:DeviceSource", s)); + } + + // Here things get rather deeply nested, and we want to be able + // to back out if it turns out that we really have no content to + // deliver. So rather than writing directly to the writer, we + // write to a StringBuffer that may or may not get used. + StringBuffer sscBuf = + new StringBuffer(margn3 + elementStart("mix:ScanningSystemCapture") + EOL); + boolean useSSCBuf = false; // set to true if we find interesting data. + StringBuffer sshBuf = + new StringBuffer(margn4 + elementStart("mix:ScanningSystemHardware") + EOL); + boolean useSSHBuf = false; + s = niso.getScannerManufacturer(); + if (s != null) { + sshBuf.append(margn5 + element("mix:ScannerManufacturer", s) + EOL); + useSSHBuf = true; + } + s = niso.getScannerModelName(); + String s1 = niso.getScannerModelNumber(); + String s2 = niso.getScannerModelSerialNo(); + if (s != null || s1 != null || s2 != null) { + sshBuf.append(margn5 + elementStart("mix:ScannerModel") + EOL); + if (s != null) { + sshBuf.append(margn6 + element("mix:ScannerModelName", s) + EOL); + useSSHBuf = true; + } + if (s1 != null) { + sshBuf.append(margn6 + element("mix:ScannerModelNumber", s1) + EOL); + useSSHBuf = true; + } + if (s2 != null) { + sshBuf.append(margn6 + element("mix:ScannerModelSerialNo ", s2) + EOL); + useSSHBuf = true; + } + sshBuf.append(margn5 + elementEnd("mix:ScannerModel") + EOL); + } + sshBuf.append(margn4 + elementEnd("mix:ScanningSystemHardware") + EOL); + if (useSSHBuf) { + // There's some ScanningSystemHardware content, and therefore some + // ScanningSystemCapture content; keep the element. + sscBuf.append(sshBuf); + useSSCBuf = true; + } + + // Build a tentative buffer for scanning system software + StringBuffer sssBuf = + new StringBuffer(margn4 + elementStart("mix:ScanningSystemSoftware") + EOL); + boolean useSSSBuf = false; + s = niso.getScanningSoftware(); + if (s != null) { + sssBuf.append(margn5 + element("mix:ScanningSoftware", s) + EOL); + useSSSBuf = true; + } + s = niso.getScanningSoftwareVersionNo(); + if (s != null) { + sssBuf.append(margn5 + element("mix:ScanningSoftwareVersionNo", s) + EOL); + useSSSBuf = true; + } + sssBuf.append(margn4 + elementEnd("mix:ScanningSystemSoftware") + EOL); + if (useSSSBuf) { + // There's some ScanningSystemSoftware content, and therefore some + // ScanningSystemCapture content; keep the element. + sscBuf.append(sssBuf); + useSSCBuf = true; + } + + // Build a tentative buffer for scanner capture settings + StringBuffer scsBuf = + new StringBuffer(margn4 + elementStart("mix:ScannerCaptureSettings") + EOL); + boolean useSCSBuf = false; + double d = niso.getPixelSize(); + if (d != NisoImageMetadata.NILL) { + scsBuf.append(margn5 + element("mix:PixelSize", formatters.get().format(d)) + EOL); + useSCSBuf = true; + } + d = niso.getXPhysScanResolution(); + double d1 = niso.getYPhysScanResolution(); + if (d != NisoImageMetadata.NILL || d1 != NisoImageMetadata.NILL) { + scsBuf.append(margn5 + elementStart("mix:PhysScanResolution") + EOL); + if (d != NisoImageMetadata.NILL) { + scsBuf.append( + margn6 + element("mix:XphysScanResolution", formatters.get().format(d)) + EOL); + } + if (d1 != NisoImageMetadata.NILL) { + scsBuf.append( + margn6 + element("mix:YphysScanResolution", formatters.get().format(d1)) + EOL); + } + scsBuf.append(margn5 + elementEnd("mix:PhysScanResolution") + EOL); + useSCSBuf = true; + } + scsBuf.append(margn4 + elementEnd("mix:ScannerCaptureSettings") + EOL); + if (useSCSBuf) { + sscBuf.append(scsBuf); + useSSCBuf = true; + } + sscBuf.append(margn3 + elementEnd("mix:ScanningSystemCapture") + EOL); + + // Finally! Do we use any of this stuff we just went through? + if (useSSCBuf) { + _writer.print(sscBuf.toString()); + } + + // Same deal for digital camera capture; put the element in a tentative + // StringBuffer and then decide if it's non-trivial. + StringBuffer dccBuf = new StringBuffer(margn3 + elementStart("mix:DigitalCameraCapture") + EOL); + boolean useDCCBuf = false; + s = niso.getDigitalCameraManufacturer(); + if (s != null) { + dccBuf.append(margn4 + element("mix:DigitalCameraManufacturer", s) + EOL); + useDCCBuf = true; + } + s = niso.getDigitalCameraModelName(); + if (s != null) { + dccBuf.append(margn4 + element("mix:DigitalCameraModel", s) + EOL); + useDCCBuf = true; + } + + dccBuf.append(margn3 + elementEnd("mix:DigitalCameraCapture") + EOL); + if (useDCCBuf) { + _writer.print(dccBuf.toString()); + } + + // Same tentative buffer deal for camera capture settings. + StringBuffer ccsBuf = + new StringBuffer(margn3 + elementStart("mix:CameraCaptureSettings") + EOL); + boolean useCCSBuf = false; + d = niso.getFNumber(); + if (d != NisoImageMetadata.NILL) { + ccsBuf.append(margn4 + element("mix:FNumber", formatters.get().format(d)) + EOL); + useCCSBuf = true; + } + d = niso.getExposureTime(); + if (d != NisoImageMetadata.NILL) { + ccsBuf.append(margn4 + element("mix:ExposureTime", formatters.get().format(d)) + EOL); + useCCSBuf = true; + } + Rational r = niso.getBrightness(); + if (r != null) { + d = r.toDouble(); + ccsBuf.append(margn4 + element("mix:Brightness", formatters.get().format(d)) + EOL); + useCCSBuf = true; + } + r = niso.getExposureBias(); + if (r != null) { + d = r.toDouble(); + ccsBuf.append(margn4 + element("mix:ExposureBias", formatters.get().format(d)) + EOL); + useCCSBuf = true; + } + double[] darray = niso.getSubjectDistance(); + if (darray != null) { + ccsBuf.append(margn4 + element("mix:SubjectDistance", doubleArray(darray)) + EOL); + useCCSBuf = true; + } + int n = niso.getMeteringMode(); + if (n != NisoImageMetadata.NULL) { + s = meteringModeToString(n); + if (s.startsWith("Center weighted")) { + s = "Center weighted Average"; + } + ccsBuf.append(margn4 + element("mix:MeteringMode", s) + EOL); + useCCSBuf = true; + } + n = niso.getSceneIlluminant(); + if (n != NisoImageMetadata.NULL) { + ccsBuf.append(margn4 + element("mix:SceneIlluminant", Integer.toString(n)) + EOL); + useCCSBuf = true; + } + d = niso.getColorTemp(); + if (d != NisoImageMetadata.NILL) { + ccsBuf.append(margn4 + element("mix:ColorTemp", formatters.get().format(d)) + EOL); + useCCSBuf = true; + } + d = niso.getFocalLength(); + if (d != NisoImageMetadata.NILL) { + ccsBuf.append(margn4 + element("mix:FocalLength", formatters.get().format(d)) + EOL); + useCCSBuf = true; + } + n = niso.getFlash(); + if (n != NisoImageMetadata.NULL) { + // First bit (0 = Flash did not fire, 1 = Flash fired) + int firstBit = n & 1; + ccsBuf.append(margn4 + element("mix:Flash", NisoImageMetadata.FLASH[firstBit]) + EOL); + useCCSBuf = true; + } + r = niso.getFlashEnergy(); + if (r != null) { + d = r.toDouble(); + ccsBuf.append(margn4 + element("mix:FlashEnergy", formatters.get().format(d)) + EOL); + useCCSBuf = true; + } + n = niso.getFlashReturn(); + if (n != NisoImageMetadata.NULL) { + ccsBuf.append(margn4 + element("mix:FlashReturn", Integer.toString(n)) + EOL); + useCCSBuf = true; + } + n = niso.getBackLight(); + if (n != NisoImageMetadata.NULL) { + ccsBuf.append(margn4 + element("mix:BackLight", Integer.toString(n)) + EOL); + useCCSBuf = true; + } + d = niso.getExposureIndex(); + if (d != NisoImageMetadata.NILL) { + ccsBuf.append(margn4 + element("mix:ExposureIndex", formatters.get().format(d)) + EOL); + useCCSBuf = true; + } + n = niso.getAutoFocus(); + if (n != NisoImageMetadata.NULL) { + ccsBuf.append(margn4 + element("mix:AutoFocus", Integer.toString(n)) + EOL); + useCCSBuf = true; + } + d = niso.getXPrintAspectRatio(); + d1 = niso.getYPrintAspectRatio(); + if (d != NisoImageMetadata.NILL || d1 != NisoImageMetadata.NILL) { + ccsBuf.append(margn4 + elementStart("mix:PrintAspectRatio") + EOL); + if (d != NisoImageMetadata.NILL) { + ccsBuf.append(margn5 + element("mix:XPrintAspectRatio", formatters.get().format(d)) + EOL); + } + if (d1 != NisoImageMetadata.NILL) { + ccsBuf.append(margn5 + element("mix:YPrintAspectRatio", formatters.get().format(d1)) + EOL); + ccsBuf.append(margn4 + elementEnd("mix:PrintAspectRatio") + EOL); + useCCSBuf = true; + } + } + ccsBuf.append(margn3 + elementEnd("mix:CameraCaptureSettings") + EOL); + if (useCCSBuf) { + _writer.print(ccsBuf.toString()); + } + + // Finally we get a relative breather without having to check nested + // elements. + n = niso.getSensor(); + if (n != NisoImageMetadata.NULL) { + _writer.println(margn3 + element("mix:Sensor", Integer.toString(n))); + } + s = niso.getDateTimeCreated(); + if (s != null) { + _writer.println(margn3 + element("mix:DateTimeCreated", s)); + } + s = niso.getMethodology(); + if (s != null) { + _writer.println(margn3 + element("mix:Methodology", s)); + } + + _writer.println(margn2 + elementEnd("mix:ImageCreation")); + } + + /* Top level element 3 of 4: ImagingPerformanceAssessment */ + protected void showNisoImagingPerformanceAssessment02(NisoImageMetadata niso, String margin) { + String margn2 = margin + " "; + String margn3 = margn2 + " "; + String margn4 = margn3 + " "; + String margn5 = margn4 + " "; + String margn6 = margn5 + " "; + String margn7 = margn6 + " "; + + StringBuffer ipaBuf = + new StringBuffer(margn3 + elementStart("mix:ImagingPerformanceAssessment") + EOL); + boolean useIPABuf = false; // set to true if we find interesting data. + StringBuffer smBuf = new StringBuffer(margn4 + elementStart("mix:SpatialMetrics") + EOL); + boolean useSMBuf = false; + + int n = niso.getSamplingFrequencyPlane(); + if (n != NisoImageMetadata.NULL) { + smBuf.append(margn5 + element("mix:SamplingFrequencyPlane", Integer.toString(n)) + EOL); + useSMBuf = true; + } + n = niso.getSamplingFrequencyUnit(); + if (n != NisoImageMetadata.NULL) { + smBuf.append(margn5 + element("mix:SamplingFrequencyUnit", Integer.toString(n)) + EOL); + useSMBuf = true; + } + Rational r = niso.getXSamplingFrequency(); + if (r != null) { + smBuf.append(margn5 + element("mix:XSamplingFrequency", Long.toString(r.toLong())) + EOL); + useSMBuf = true; + } + r = niso.getYSamplingFrequency(); + if (r != null) { + smBuf.append(margn5 + element("mix:YSamplingFrequency", Long.toString(r.toLong())) + EOL); + useSMBuf = true; + } + long ln = niso.getImageWidth(); + if (ln != NisoImageMetadata.NULL) { + smBuf.append(margn5 + element("mix:ImageWidth", Long.toString(ln)) + EOL); + useSMBuf = true; + } + ln = niso.getImageLength(); + if (ln != NisoImageMetadata.NULL) { + smBuf.append(margn5 + element("mix:ImageLength", Long.toString(ln)) + EOL); + useSMBuf = true; + } + + double d = niso.getSourceXDimension(); + n = niso.getSourceXDimensionUnit(); + if (d != NisoImageMetadata.NILL || n != NisoImageMetadata.NULL) { + smBuf.append(margn5 + elementStart("mix:Source_X") + EOL); + if (d != NisoImageMetadata.NILL) { + smBuf.append(margn6 + element("mix:Source_Xdimension", formatters.get().format(d)) + EOL); + } + if (n != NisoImageMetadata.NULL) { + smBuf.append(margn6 + element("mix:Source_XdimensionUnit", Integer.toString(n)) + EOL); + } + smBuf.append(margn5 + elementEnd("mix:Source_X") + EOL); + useSMBuf = true; + } + + d = niso.getSourceYDimension(); + n = niso.getSourceYDimensionUnit(); + if (d != NisoImageMetadata.NILL || n != NisoImageMetadata.NULL) { + smBuf.append(margn4 + elementStart("mix:Source_Y") + EOL); + if (d != NisoImageMetadata.NILL) { + smBuf.append(margn5 + element("mix:Source_Ydimension", formatters.get().format(d)) + EOL); + } + if (n != NisoImageMetadata.NULL) { + smBuf.append(margn5 + element("mix:Source_YdimensionUnit", Integer.toString(n)) + EOL); + } + smBuf.append(margn4 + elementEnd("mix:Source_Y") + EOL); + useSMBuf = true; + } + smBuf.append(margn3 + elementEnd("mix:SpatialMetrics") + EOL); + if (useSMBuf) { + ipaBuf.append(smBuf); + useIPABuf = true; + } + + // Now a tentative buffer for the Energetics element + StringBuffer eBuf = new StringBuffer(margn3 + elementStart("mix:Energetics") + EOL); + boolean useEBuf = false; + int[] iarray = niso.getBitsPerSample(); + if (iarray != null) { + eBuf.append(margn4 + element("mix:BitsPerSample", integerArray(iarray, ',')) + EOL); + useEBuf = true; + } + n = niso.getSamplesPerPixel(); + if (n != NisoImageMetadata.NULL) { + eBuf.append(margn4 + element("mix:SamplesPerPixel", Integer.toString(n)) + EOL); + useEBuf = true; + } + iarray = niso.getExtraSamples(); + if (iarray != null) { + // extraSamples can only be an integer, so the best we can do is + // snag the first value from the array. It also must be limited to + // 0, 1, 2, or 3. + n = iarray[0]; + if (n >= 0 && n <= 3) { + eBuf.append(margn4 + element("mix:ExtraSamples", Integer.toString(n)) + EOL); + useEBuf = true; + } + // This is what we'd really like to do, but it violates the schema. + // Keep this code around in the event the schema is fixed in the + // future. + // eBuf.append (margn4 + element ("mix:ExtraSamples", + // integerArray (iarray)) + EOL); + // useEBuf = true; + } + // Tentative buffer for colormap element within Energetics + StringBuffer cmBuf = new StringBuffer(margn4 + elementStart("mix:Colormap") + EOL); + boolean useCMBuf = false; + String s = niso.getColormapReference(); + if (s != null) { + cmBuf.append(margn5 + element("mix:Reference", s) + EOL); + useCMBuf = true; + } + iarray = niso.getColormapRedValue(); + if (iarray != null) { + cmBuf.append(margn5 + elementStart("mix:Wrap")); + // If the red array is there, assume the others are too, and are + // equal in length. + int[] bcarray = niso.getColormapBitCodeValue(); + int[] garray = niso.getColormapGreenValue(); + int[] barray = niso.getColormapBlueValue(); + try { + for (int i = 0; i < iarray.length; i++) { + cmBuf.append(margn6 + elementStart("mix:Color") + EOL); + n = bcarray[i]; + if (n != NisoImageMetadata.NULL) { + cmBuf.append(margn7 + element("mix:BitCodeValue", Integer.toString(n)) + EOL); + } + n = iarray[i]; + if (n != NisoImageMetadata.NULL) { + cmBuf.append(margn7 + element("mix:RedValue", Integer.toString(n)) + EOL); + } + n = garray[i]; + if (n != NisoImageMetadata.NULL) { + cmBuf.append(margn7 + element("mix:GreenValue", Integer.toString(n)) + EOL); + } + n = barray[i]; + if (n != NisoImageMetadata.NULL) { + cmBuf.append(margn7 + element("mix:BlueValue", Integer.toString(n)) + EOL); + } + + cmBuf.append(margn6 + elementEnd("mix:Color") + EOL); } - long lin = aes.getFirstSampleOffset (); - if (lin != AESAudioMetadata.NULL) { - _writer.println (margn2 + element ("aes:firstSampleOffset", - Long.toString (lin))); + } catch (Exception e) { + // If the assumption mentioned above is wrong, + // we'll get broken XML, but at least won't die here. + } + cmBuf.append(margn5 + elementEnd("mix:Wrap") + EOL); + useCMBuf = true; + } + cmBuf.append(margn4 + elementEnd("mix:Colormap")); + if (useCMBuf) { + eBuf.append(cmBuf); + useEBuf = true; + } + + iarray = niso.getGrayResponseCurve(); + n = niso.getGrayResponseUnit(); + if (iarray != null || n != NisoImageMetadata.NULL) { + eBuf.append(margn4 + elementStart("mix:GrayResponse") + EOL); + if (iarray != null) { + eBuf.append(margn5 + element("mix:GrayResponseCurve", integerArray(iarray)) + EOL); + } + if (n != NisoImageMetadata.NULL) { + eBuf.append(margn5 + element("mix:GrayResponseUnit", Integer.toString(n)) + EOL); + } + eBuf.append(margn4 + elementEnd("mix:GrayResponse") + EOL); + useEBuf = true; + } + r = niso.getWhitePointXValue(); + Rational r1 = niso.getWhitePointYValue(); + if (r != null || r1 != null) { + // These are specified in the scheme as CIExyType. All the + // schema tells us about this type is that it's a string. + eBuf.append(margn4 + elementStart("mix:WhitePoint") + EOL); + if (r != null) { + eBuf.append(margn5 + element("mix:WhitePoint_Xvalue", r.toString()) + EOL); + } + if (r1 != null) { + eBuf.append(margn5 + element("mix:WhitePoint_Yvalue", r1.toString()) + EOL); + } + eBuf.append(margn4 + elementEnd("mix:WhitePoint") + EOL); + useEBuf = true; + } + + r = niso.getPrimaryChromaticitiesRedX(); + // For simplicity, we check only the red x in deciding whether + // to incorporate this element. A partial set of chromaticities + // would be meaningless anyway. + if (r != null) { + eBuf.append(margn4 + elementStart("mix:PrimaryChromaticities") + EOL); + eBuf.append(margn5 + element("mix:PrimaryChromaticities_RedX", r.toString()) + EOL); + r = niso.getPrimaryChromaticitiesRedY(); + if (r != null) { + eBuf.append(margn5 + element("mix:PrimaryChromaticities_RedY", r.toString()) + EOL); + } + r = niso.getPrimaryChromaticitiesGreenX(); + if (r != null) { + eBuf.append(margn5 + element("mix:PrimaryChromaticities_GreenX", r.toString()) + EOL); + } + r = niso.getPrimaryChromaticitiesGreenY(); + if (r != null) { + eBuf.append(margn5 + element("mix:PrimaryChromaticities_GreenY", r.toString()) + EOL); + } + r = niso.getPrimaryChromaticitiesBlueX(); + if (r != null) { + eBuf.append(margn5 + element("mix:PrimaryChromaticities_BlueX", r.toString()) + EOL); + } + r = niso.getPrimaryChromaticitiesBlueY(); + if (r != null) { + eBuf.append(margn5 + element("mix:PrimaryChromaticities_BlueY", r.toString()) + EOL); + } + eBuf.append(margn4 + elementEnd("mix:PrimaryChromaticities") + EOL); + useEBuf = true; + } + + eBuf.append(margn3 + elementEnd("mix:Energetics") + EOL); + if (useEBuf) { + ipaBuf.append(eBuf); + useIPABuf = true; + } + + // Another tentative buffer for TargetData + StringBuffer tdBuf = new StringBuffer(margn3 + elementStart("mix:TargetData") + EOL); + boolean useTDBuf = false; + n = niso.getTargetType(); + if (n != NisoImageMetadata.NULL) { + tdBuf.append(margn4 + element("mix:TargetType", Integer.toString(n)) + EOL); + useTDBuf = true; + } + + // Nest a TargetID tentative buffer in the TargetData buffer + StringBuffer tiBuf = new StringBuffer(margn4 + elementStart("mix:TargetID") + EOL); + boolean useTIBuf = false; + s = niso.getTargetIDManufacturer(); + if (s != null) { + tiBuf.append(margn5 + element("mix:TargetIDManufacturer", s) + EOL); + useTIBuf = true; + } + s = niso.getTargetIDName(); + if (s != null) { + tiBuf.append(margn5 + element("mix:TargetIDName", s) + EOL); + useTIBuf = true; + } + s = niso.getTargetIDNo(); + if (s != null) { + tiBuf.append(margn5 + element("mix:TargetIDNo", s) + EOL); + useTIBuf = true; + } + s = niso.getTargetIDMedia(); + if (s != null) { + tiBuf.append(margn5 + element("mix:TargetIDMedia", s) + EOL); + useTIBuf = true; + } + tiBuf.append(margn4 + elementEnd("mix:TargetID") + EOL); + if (useTIBuf) { + tdBuf.append(tiBuf); + useTDBuf = true; + } + + s = niso.getImageData(); + if (s != null) { + tdBuf.append(margn5 + element("mix:ImageData", s) + EOL); + useTDBuf = true; + } + s = niso.getPerformanceData(); + if (s != null) { + tdBuf.append(margn5 + element("mix:PerformanceData", s) + EOL); + useTDBuf = true; + } + s = niso.getProfiles(); + if (s != null) { + tdBuf.append(margn5 + element("mix:Profiles", s) + EOL); + useTDBuf = true; + } + tdBuf.append(margn3 + elementEnd("mix:TargetData") + EOL); + if (useTDBuf) { + ipaBuf.append(tdBuf); + useIPABuf = true; + } + + ipaBuf.append(margn2 + elementEnd("mix:ImagingPerformanceAssessment") + EOL); + if (useIPABuf) { + _writer.print(ipaBuf.toString()); + } + } + + /* Top level element 4 of 4: ChangeHistory */ + protected void showNisoChangeHistory02(NisoImageMetadata niso, String margin) { + String margn2 = margin + " "; + String margn3 = margn2 + " "; + String margn4 = margn3 + " "; + String margn5 = margn4 + " "; + + // Yet again, build elements in tentative buffers and throw them + // away if they prove trivial. + StringBuffer chBuf = new StringBuffer(margn2 + elementStart("mix:ChangeHistory") + EOL); + boolean useCHBuf = false; + StringBuffer ipBuf = new StringBuffer(margn3 + elementStart("mix:ImageProcessing") + EOL); + boolean useIPBuf = false; + + String s = niso.getDateTimeProcessed(); + if (s != null) { + ipBuf.append(margn4 + element("DateTimeProcessed", s) + EOL); + useIPBuf = true; + } + s = niso.getSourceData(); + if (s != null) { + ipBuf.append(margn4 + element("SourceData", s) + EOL); + useIPBuf = true; + } + s = niso.getProcessingAgency(); + if (s != null) { + ipBuf.append(margn4 + element("ProcessingAgency", s) + EOL); + useIPBuf = true; + } + + // Third-level nesting of tentative buffer! + StringBuffer psBuf = new StringBuffer(margn4 + elementStart("ProcessingSoftware") + EOL); + boolean usePSBuf = false; + s = niso.getProcessingSoftwareName(); + if (s != null) { + psBuf.append(margn5 + element("ProcessingSoftwareName", s) + EOL); + usePSBuf = true; + } + s = niso.getProcessingSoftwareVersion(); + if (s != null) { + psBuf.append(margn5 + element("ProcessingSoftwareVersion", s) + EOL); + usePSBuf = true; + } + psBuf.append(margn4 + elementEnd("ProcessingSoftware") + EOL); + if (usePSBuf) { + ipBuf.append(psBuf); + useIPBuf = true; + } + + // Hard to say, but I think the intent is that there be + // one ProcessingActions element per processing action. + String[] sarray = niso.getProcessingActions(); + if (sarray != null) { + for (int i = 0; i < sarray.length; i++) { + ipBuf.append(margn4 + element("ProcessingActions", sarray[i]) + EOL); + } + useIPBuf = true; + } + ipBuf.append(margn3 + elementEnd("mix:ImageProcessing") + EOL); + if (useIPBuf) { + chBuf.append(ipBuf); + useCHBuf = true; + } + chBuf.append(margn2 + elementEnd("mix:ChangeHistory") + EOL); + if (useCHBuf) { + _writer.print(chBuf.toString()); + } + } + + /** Display the NISO image metadata formatted according to the MIX 1.0 schema. */ + protected void showNisoImageMetadata10(NisoImageMetadata niso) { + String margin = getIndent(++_level); + + String[][] attrs = { + {"xmlns:mix", "http://www.loc.gov/mix/v10"}, + {"xmlns:xsi", "http://www.w3.org/2001/XMLSchema-instance"}, + { + "xsi:schemaLocation", + "http://www.loc.gov/mix/v10 http://www.loc.gov/standards/mix/mix10/mix10.xsd" + } + }; + _writer.println(margin + elementStart("mix:mix", attrs)); + + showNisoBasicDigitalObjectInformation10(niso, margin); + showNisoBasicImageInformation10(niso, margin); + showNisoImageCaptureMetadata10(niso, margin); + showNisoImageAssessmentMetadata10(niso, margin); + showChangeHistory10(niso, margin); + + _writer.println(margin + elementEnd("mix:mix")); + + _level--; + } + + /* + * The NISO Metadata output (1.0 now) is split into multiple functions so + * that they're merely outrageously big rather than disgustingly big + */ + /* Top level element 1 of 5: BasicDigitalObjectInformation */ + protected void showNisoBasicDigitalObjectInformation10(NisoImageMetadata niso, String margin) { + String margn2 = margin + " "; + String margn3 = margn2 + " "; + String margn4 = margn3 + " "; + String margn5 = margn4 + " "; + + _writer.println(margn2 + elementStart("mix:BasicDigitalObjectInformation")); + + StringBuffer objIDBuf = new StringBuffer(margn3 + elementStart("mix:ObjectIdentifier") + EOL); + boolean useObjIDBuf = false; + objIDBuf.append(margn4 + element("mix:objectIdentifierType", "JHOVE") + EOL); + String s = niso.getImageIdentifier(); + if (s != null) { + objIDBuf.append(margn4 + element("mix:objectIdentifierValue", s) + EOL); + useObjIDBuf = true; + } + objIDBuf.append(margn3 + elementEnd("mix:ObjectIdentifier") + EOL); + if (useObjIDBuf) { + _writer.print(objIDBuf.toString()); + } + long ln = niso.getFileSize(); + if (ln != NisoImageMetadata.NULL) { + _writer.print(margn4 + element("mix:fileSize", Long.toString(ln)) + EOL); + } + + // TODO we really should output a FormatDesignation, but it isn't in the + // NisoImageMetadata class yet. + // TODO If we output a FormatDesignation, we should output a + // FormatRegistry. + + if ((s = niso.getByteOrder()) != null) { + // Convert strings to MIX 1.0 form + if (s.startsWith("big")) { + s = "big_endian"; + } else if (s.startsWith("little")) { + s = "little_endian"; + } + _writer.print(margn4 + element("mix:byteOrder", s) + EOL); + } + int comp = niso.getCompressionScheme(); + int level = niso.getCompressionLevel(); + if (comp != NisoImageMetadata.NULL || level != NisoImageMetadata.NULL) { + _writer.print(margn4 + elementStart("mix:Compression") + EOL); + if (comp != NisoImageMetadata.NULL) { + if (comp == 34713 || comp == 34714) { + _writer.print( + margn5 + element("mix:compressionScheme", compressionSchemeToString(comp)) + EOL); + if (level != NisoImageMetadata.NULL) { + _writer.print(margn5 + element("mix:compressionRatio", Integer.toString(level)) + EOL); + } + } else { + _writer.print(margn5 + element("mix:compressionScheme", Integer.toString(comp)) + EOL); } - String[] use = aes.getUse (); - if (use != null) { - String[][] uattrs = new String [][] - { { "useType", use[0] }, - { "otherType", use[1] }}; - _writer.println (margn2 + element ("aes:use", uattrs)); + } + // TODO it isn't clear how to get from compression level to + // compression ratio + + _writer.print(margn4 + elementEnd("mix:Compression") + EOL); + } + int n = niso.getChecksumMethod(); + s = niso.getChecksumValue(); + if (n != NisoImageMetadata.NULL || s != null) { + _writer.print(margn4 + elementStart("mix:Fixity") + EOL); + if (n != NisoImageMetadata.NULL) { + _writer.print(margn5 + element("mix:messageDigestAlgorithm", Integer.toString(n)) + EOL); + } + if (s != null) { + _writer.print(margn5 + element("mix:messageDigest", s) + EOL); + } + _writer.println(margn4 + elementEnd("mix:Fixity")); + } + _writer.println(margn3 + elementEnd("mix:BasicDigitalObjectInformation")); + } + + /* 1.0, Top level element 2 of 5: BasicImageInformation */ + protected void showNisoBasicImageInformation10(NisoImageMetadata niso, String margin) { + String margn2 = margin + " "; + String margn3 = margn2 + " "; + String margn4 = margn3 + " "; + String margn5 = margn4 + " "; + String margn6 = margn5 + " "; + String margn7 = margn6 + " "; + _writer.println(margn2 + elementStart("mix:BasicImageInformation")); + StringBuffer basCharBuf = + new StringBuffer(margn3 + elementStart("mix:BasicImageCharacteristics") + EOL); + boolean useBasCharBuf = false; + long ln = niso.getImageWidth(); + if (ln != NisoImageMetadata.NULL) { + basCharBuf.append(margn4 + element("mix:imageWidth", Long.toString(ln)) + EOL); + useBasCharBuf = true; + } + ln = niso.getImageLength(); + if (ln != NisoImageMetadata.NULL) { + basCharBuf.append(margn4 + element("mix:imageHeight", Long.toString(ln)) + EOL); + useBasCharBuf = true; + } + // Nest photometric interpretation tentative buffer in basCharBuf + StringBuffer piBuf = + new StringBuffer(margn4 + elementStart("mix:PhotometricInterpretation") + EOL); + boolean usePIBuf = false; + int n = niso.getColorSpace(); + if (n != NisoImageMetadata.NULL) { + piBuf.append(margn5 + element("mix:colorSpace", Integer.toString(n)) + EOL); + usePIBuf = true; + } + String s = niso.getProfileName(); + String s2 = niso.getProfileURL(); + if (s != null || s2 != null) { + piBuf.append(margn5 + elementStart("mix:ColorProfile") + EOL); + piBuf.append(margn6 + elementStart("mix:IccProfile") + EOL); + if (s != null) { + piBuf.append(margn7 + element("mix:iccProfileName", s) + EOL); + } + if (s2 != null) { + piBuf.append(margn7 + element("mix:iccProfileURL", s2) + EOL); + } + piBuf.append(margn6 + elementEnd("mix:IccProfile") + EOL); + piBuf.append(margn5 + elementEnd("mix:ColorProfile") + EOL); + usePIBuf = true; + } + int[] iarray = niso.getYCbCrSubSampling(); + n = niso.getYCbCrPositioning(); + Rational[] rarray = niso.getYCbCrCoefficients(); + if (iarray != null || n != NisoImageMetadata.NULL || rarray != null) { + piBuf.append(margn5 + elementStart("mix:YCbCr") + EOL); + usePIBuf = true; + if (iarray != null && iarray.length >= 2) { + piBuf.append(margn6 + elementStart("mix:YCbCrSubSampling") + EOL); + piBuf.append( + margn7 + element("mix:yCbCrSubsampleHoriz", Integer.toString(iarray[0])) + EOL); + piBuf.append(margn7 + element("mix:yCbCrSubsampleVert", Integer.toString(iarray[1])) + EOL); + piBuf.append(margn6 + elementEnd("mix:YCbCrSubSampling") + EOL); + } + if (n != NisoImageMetadata.NULL) { + piBuf.append(margn6 + element("mix:yCbCrPositioning", Integer.toString(n)) + EOL); + } + if (rarray != null) { + piBuf.append(margn6 + element("mix:yCbCrCoefficients", rationalArray10(rarray)) + EOL); + } + piBuf.append(margn5 + elementEnd("mix:YCbCr") + EOL); + } + rarray = niso.getReferenceBlackWhite(); + if (rarray != null) { + piBuf.append(margn6 + element("mix:referenceBlackWhite", rationalArray10(rarray)) + EOL); + usePIBuf = true; + } + piBuf.append(margn4 + elementEnd("mix:PhotometricInterpretation") + EOL); + if (usePIBuf) { + basCharBuf.append(piBuf); + useBasCharBuf = true; + } + basCharBuf.append(margn3 + elementEnd("mix:BasicImageCharacteristics")); + + if (useBasCharBuf) { + _writer.println(basCharBuf); + } + // SpecialFormatCharacteristics limited to JPEG2000 + StringBuffer speCharBuf = + new StringBuffer(margn3 + elementStart("mix:SpecialFormatCharacteristics") + EOL); + boolean useSpeCharBuf = false; + int lay = niso.getJp2Layers(); + int lev = niso.getJp2ResolutionLevels(); + String sizTiles = niso.getJp2Tiles(); + if (sizTiles != null || lay != NisoImageMetadata.NULL || lev != NisoImageMetadata.NULL) { + + useSpeCharBuf = true; + speCharBuf.append(margn4 + elementStart("mix:JPEG2000") + EOL); + speCharBuf.append(margn5 + elementStart("mix:EncodingOptions") + EOL); + if (sizTiles != null) { + speCharBuf.append(margn6 + element("mix:tiles", sizTiles) + EOL); + } + if (lay != NisoImageMetadata.NULL) { + speCharBuf.append(margn6 + element("mix:qualityLayers", Integer.toString(lay)) + EOL); + } + if (sizTiles != null) { + speCharBuf.append(margn6 + element("mix:resolutionLevels", Integer.toString(lev)) + EOL); + } + speCharBuf.append(margn5 + elementEnd("mix:EncodingOptions") + EOL); + speCharBuf.append(margn4 + elementEnd("mix:JPEG2000") + EOL); + } + + speCharBuf.append(margn3 + elementEnd("mix:SpecialFormatCharacteristics")); + if (useSpeCharBuf) { + _writer.println(speCharBuf); + } + + _writer.println(margn2 + elementEnd("mix:BasicImageInformation")); + } + + /* 1.0, Top level element 3 of 5: ImageCaptureMetadata */ + protected void showNisoImageCaptureMetadata10(NisoImageMetadata niso, String margin) { + String margn2 = margin + " "; + String margn3 = margn2 + " "; + String margn4 = margn3 + " "; + String margn5 = margn4 + " "; + String margn6 = margn5 + " "; + String margn7 = margn6 + " "; + + StringBuffer captureBuffer = new StringBuffer(); + boolean useCaptureBuffer = false; + + String s = niso.getSourceType(); + if (s != null) { + captureBuffer.append(margn3 + element("mix:sourceType", s)); + useCaptureBuffer = true; + } + s = niso.getSourceID(); + if (s != null) { + captureBuffer.append(margn3 + elementStart("mix:SourceID")); + captureBuffer.append(margn3 + element("mix:sourceIDValue", s)); + captureBuffer.append(margn3 + elementEnd("mix:sourceID")); + useCaptureBuffer = true; + } + double d = niso.getSourceXDimension(); + int n = niso.getSourceXDimensionUnit(); + if (d != NisoImageMetadata.NILL || n != NisoImageMetadata.NULL) { + // Assume that both X and Y exist, or neither + captureBuffer.append(margn3 + elementStart("mix:SourceSize")); + captureBuffer.append(margn4 + elementStart("mix:SourceXDimension") + EOL); + if (d != NisoImageMetadata.NILL) { + captureBuffer.append( + margn5 + element("mix:sourceXDimensionValue", formatters.get().format(d)) + EOL); + } + if (n != NisoImageMetadata.NULL) { + captureBuffer.append( + margn5 + element("mix:sourceXDimensionUnit", Integer.toString(n)) + EOL); + } + captureBuffer.append(margn4 + elementEnd("mix:SourceXDimension") + EOL); + + d = niso.getSourceYDimension(); + n = niso.getSourceYDimensionUnit(); + if (d != NisoImageMetadata.NILL || n != NisoImageMetadata.NULL) { + captureBuffer.append(margn4 + elementStart("mix:SourceYDimension") + EOL); + if (d != NisoImageMetadata.NILL) { + captureBuffer.append( + margn5 + element("mix:sourceYDimensionValue", formatters.get().format(d)) + EOL); } - s = aes.getPrimaryIdentifier(); - if (s != null) { - String t= aes.getPrimaryIdentifierType (); - String[][] idattrs = new String[1][2]; - idattrs[0][0] = "identifierType"; - if (t != null) { - idattrs[0][1] = t; - } - else { - // Shouldn't happen - idattrs[0][1] = ""; - } - _writer.println (margn2 + element - ("aes:primaryIdentifier", idattrs, s)); + if (n != NisoImageMetadata.NULL) { + captureBuffer.append( + margn5 + element("mix:sourceYDimensionUnit", Integer.toString(n)) + EOL); } + captureBuffer.append(margn4 + elementEnd("mix:SourceYDimension") + EOL); + } + captureBuffer.append(margn3 + elementEnd("mix:SourceSize") + EOL); + useCaptureBuffer = true; + } + StringBuffer genCapBuf = + new StringBuffer(margn3 + elementStart("mix:GeneralCaptureInformation") + EOL); + boolean useGenCapBuf = false; + + s = niso.getDateTimeCreated(); + if (s != null) { + genCapBuf.append(margn3 + element("mix:dateTimeCreated", s) + EOL); + useGenCapBuf = true; + } + s = niso.getImageProducer(); + if (s != null) { + genCapBuf.append(margn3 + element("mix:imageProducer", s) + EOL); + useGenCapBuf = true; + } + + s = niso.getDeviceSource(); + if (s != null) { + genCapBuf.append(margn3 + element("mix:captureDevice", s) + EOL); + /* + * This has a restricted set of values. Does the setting code + * conform? + */ + } + + genCapBuf.append(margn3 + elementEnd("mix:GeneralCaptureInformation") + EOL); + if (useGenCapBuf) { + captureBuffer.append(genCapBuf); + useCaptureBuffer = true; + } + + // Here's a chunk of XML for scanners. + StringBuffer scanCapBuf = new StringBuffer(margn3 + elementStart("mix:ScannerCapture") + EOL); + boolean useScanCapBuf = false; + String mfg = niso.getScannerManufacturer(); + if (mfg != null) { + scanCapBuf.append(margn4 + element("mix:scannerManufacturer", mfg) + EOL); + useScanCapBuf = true; + } + String model = niso.getScannerModelName(); + String modelNum = niso.getScannerModelNumber(); + String serNum = niso.getScannerModelSerialNo(); + if (model != null || modelNum != null || serNum != null) { + useScanCapBuf = true; + scanCapBuf.append(margn4 + elementStart("mix:ScannerModel") + EOL); + if (model != null) { + scanCapBuf.append(margn5 + element("mix:scannerModelName", model) + EOL); + } + if (modelNum != null) { + scanCapBuf.append(margn5 + element("mix:scannerModelNumber", modelNum) + EOL); + } + if (serNum != null) { + scanCapBuf.append(margn5 + element("mix:scannerModelSerialNo", serNum) + EOL); + } + scanCapBuf.append(margn4 + elementEnd("mix:ScannerModel") + EOL); + } + double xres = niso.getXPhysScanResolution(); + double yres = niso.getYPhysScanResolution(); + if (xres != NisoImageMetadata.NULL && yres != NisoImageMetadata.NULL) { + double res = (xres > yres ? xres : yres); + scanCapBuf.append( + margn4 + element("mix:maximumOpticalResolution", formatters.get().format(res)) + EOL); + } + s = niso.getScanningSoftware(); + if (s != null) { + useScanCapBuf = true; + scanCapBuf.append(margn4 + elementStart("mix:ScanningSystemSoftware") + EOL); + scanCapBuf.append(margn5 + element("mix:scanningSoftwareName", s) + EOL); + s = niso.getScanningSoftwareVersionNo(); + if (s != null) { + scanCapBuf.append(margn5 + element("mix:scanningSoftwareVersionNo", s) + EOL); + } + scanCapBuf.append(margn4 + elementEnd("mix:ScanningSystemSoftware") + EOL); + } + scanCapBuf.append(margn3 + elementEnd("mix:ScannerCapture") + EOL); + if (useScanCapBuf) { + captureBuffer.append(scanCapBuf); + useCaptureBuffer = true; + } + + // Now we'll hear from the digital cameras. + StringBuffer digCamBuf = + new StringBuffer(margn3 + elementStart("mix:DigitalCameraCapture") + EOL); + boolean useDigCamBuf = false; + + s = niso.getDigitalCameraManufacturer(); + if (s != null) { + digCamBuf.append(margn4 + element("mix:digitalCameraManufacturer", s) + EOL); + useDigCamBuf = true; + } + String dcmodel = niso.getDigitalCameraModelName(); + String dcmodelNum = niso.getDigitalCameraModelNumber(); + String dcserNum = niso.getDigitalCameraModelSerialNo(); + if (dcmodel != null || dcmodelNum != null || dcserNum != null) { + useDigCamBuf = true; + digCamBuf.append(margn4 + elementStart("mix:DigitalCameraModel") + EOL); + if (dcmodel != null) { + digCamBuf.append(margn5 + element("mix:digitalCameraModelName", dcmodel) + EOL); + } + if (dcmodelNum != null) { + digCamBuf.append(margn5 + element("mix:digitalCameraModelNumber", dcmodelNum) + EOL); + } + if (dcserNum != null) { + digCamBuf.append(margn5 + element("mix:mix:digitalCameraModelSerialNo", dcserNum) + EOL); + } + digCamBuf.append(margn4 + elementEnd("mix:DigitalCameraModel") + EOL); + } + + // Nest a buffer for CameraCaptureSettings + StringBuffer ccSetBuf = + new StringBuffer(margn4 + elementStart("mix:CameraCaptureSettings") + EOL); + boolean useCcSetBuf = false; + // CameraCaptureSettings consists only of an ImageData element, so we + // don't need another use flag here. + ccSetBuf.append(margn5 + elementStart("mix:ImageData") + EOL); + d = niso.getFNumber(); + if (d != NisoImageMetadata.NULL) { + ccSetBuf.append(margn6 + element("mix:fNumber", formatters.get().format(d)) + EOL); + useCcSetBuf = true; + } + d = niso.getExposureTime(); + if (d != NisoImageMetadata.NULL) { + ccSetBuf.append(margn6 + element("mix:exposureTime", formatters.get().format(d)) + EOL); + useCcSetBuf = true; + } + n = niso.getExposureProgram(); + if (n != NisoImageMetadata.NULL) { + ccSetBuf.append(margn6 + element("mix:exposureProgram", Integer.toString(n)) + EOL); + useCcSetBuf = true; + } + s = niso.getExifVersion(); + if ("0220".equals(s)) { // Only valid value + ccSetBuf.append(margn6 + element("mix:exifVersion", s) + EOL); + useCcSetBuf = true; + } + Rational r = niso.getBrightness(); + if (r != null) { + rationalToString(ccSetBuf, "mix:brightnessValue", margn6, r); + useCcSetBuf = true; + } + r = niso.getExposureBias(); + if (r != null) { + rationalToString(ccSetBuf, "mix:exposureBiasValue", margn6, r); + useCcSetBuf = true; + } + r = niso.getMaxApertureValue(); + if (r != null) { + rationalToString(ccSetBuf, "mix:maxApertureValue", margn6, r); + useCcSetBuf = true; + } + double[] darray = niso.getSubjectDistance(); + if (darray != null) { + // For the old schema, we dumped out the whole array, but the 1.0 + // schema clearly says a non-negative number is expected. + // So just use darray[0]. + ccSetBuf.append( + margn6 + element("mix:subjectDistance", formatters.get().format(darray[0])) + EOL); + useCcSetBuf = true; + } + n = niso.getMeteringMode(); + if (n != NisoImageMetadata.NULL) { + ccSetBuf.append(margn6 + element("mix:meteringMode", meteringModeToString(n)) + EOL); + useCcSetBuf = true; + } + n = niso.getFlash(); + if (n != NisoImageMetadata.NULL) { + // First bit (0 = Flash did not fire, 1 = Flash fired) + int firstBit = n & 1; + ccSetBuf.append(margn6 + element("mix:flash", NisoImageMetadata.FLASH_20[firstBit]) + EOL); + useCcSetBuf = true; + } + d = niso.getFocalLength(); + if (d != NisoImageMetadata.NULL) { + ccSetBuf.append(margn6 + element("mix:focalLength", formatters.get().format(d)) + EOL); + useCcSetBuf = true; + } + r = niso.getFlashEnergy(); + if (r != null) { + rationalToString(ccSetBuf, "mix:flashEnergy", margn6, r); + useCcSetBuf = true; + } + n = niso.getBackLight(); + if (n != NisoImageMetadata.NULL) { + ccSetBuf.append(margn6 + element("mix:backLight", Integer.toString(n)) + EOL); + useCcSetBuf = true; + } + d = niso.getExposureIndex(); + if (d != NisoImageMetadata.NULL) { + ccSetBuf.append(margn6 + element("mix:exposureIndex", formatters.get().format(d)) + EOL); + useCcSetBuf = true; + } + n = niso.getAutoFocus(); + if (n != NisoImageMetadata.NULL) { + ccSetBuf.append(margn6 + element("mix:autoFocus", Integer.toString(n)) + EOL); + useCcSetBuf = true; + } + d = niso.getXPrintAspectRatio(); + double d2 = niso.getYPrintAspectRatio(); + if (d != NisoImageMetadata.NULL || d2 != NisoImageMetadata.NULL) { + ccSetBuf.append(margn6 + elementStart("mix:PrintAspectRatio") + EOL); + if (d != NisoImageMetadata.NULL) { + ccSetBuf.append( + margn7 + element("mix:xPrintAspectRatio", formatters.get().format(d)) + EOL); + } + if (d2 != NisoImageMetadata.NULL) { + ccSetBuf.append( + margn7 + element("mix:yPrintAspectRatio", formatters.get().format(d)) + EOL); + } + + ccSetBuf.append(margn6 + elementEnd("mix:PrintAspectRatio") + EOL); + } + + ccSetBuf.append(margn5 + elementEnd("mix:ImageData") + EOL); + ccSetBuf.append(margn4 + elementEnd("mix:CameraCaptureSettings") + EOL); + if (useCcSetBuf) { + digCamBuf.append(ccSetBuf); + useDigCamBuf = true; + } + digCamBuf.append(margn3 + elementEnd("mix:DigitalCameraCapture") + EOL); + if (useDigCamBuf) { + captureBuffer.append(digCamBuf); + useCaptureBuffer = true; + } + + n = niso.getOrientation(); + if (n != NisoImageMetadata.NULL) { + captureBuffer.append(margn3 + element("mix:orientation", Integer.toString(n)) + EOL); + useCaptureBuffer = true; + } + s = niso.getMethodology(); + if (s != null) { + captureBuffer.append(margn3 + element("mix:methodology", s) + EOL); + } + if (useCaptureBuffer) { + _writer.println(margn2 + elementStart("mix:ImageCaptureMetadata")); + _writer.print(captureBuffer.toString()); + _writer.println(margn2 + elementEnd("mix:ImageCaptureMetadata")); + } + } + + /* 1.0, Top level element 4 of 5: ImageAssessmentMetadata */ + protected void showNisoImageAssessmentMetadata10(NisoImageMetadata niso, String margin) { + String margn2 = margin + " "; + String margn3 = margn2 + " "; + String margn4 = margn3 + " "; + String margn5 = margn4 + " "; + + _writer.println(margn2 + elementStart("mix:ImageAssessmentMetadata")); + StringBuffer metricsBuf = new StringBuffer(margn3 + elementStart("mix:SpatialMetrics") + EOL); + boolean useMetricsBuf = false; + + int n = niso.getSamplingFrequencyPlane(); + if (n != NisoImageMetadata.NULL) { + metricsBuf.append(margn4 + element("mix:samplingFrequencyPlane", Integer.toString(n)) + EOL); + useMetricsBuf = true; + } + n = niso.getSamplingFrequencyUnit(); + if (n != NisoImageMetadata.NULL) { + metricsBuf.append(margn4 + element("mix:samplingFrequencyUnit", Integer.toString(n)) + EOL); + useMetricsBuf = true; + } + Rational r = niso.getXSamplingFrequency(); + if (r != null) { + rationalToString(metricsBuf, "mix:xSamplingFrequency", margn4, r); + } + r = niso.getYSamplingFrequency(); + if (r != null) { + rationalToString(metricsBuf, "mix:ySamplingFrequency", margn4, r); + } + metricsBuf.append(margn3 + elementEnd("mix:SpatialMetrics")); + if (useMetricsBuf) { + _writer.println(metricsBuf); + } + + StringBuffer colorEncBuf = + new StringBuffer(margn3 + elementStart("mix:ImageColorEncoding") + EOL); + boolean useColorEncBuf = false; + + int[] iarray = niso.getBitsPerSample(); + if (iarray != null) { + colorEncBuf.append(margn4 + elementStart("mix:bitsPerSample") + EOL); + colorEncBuf.append( + margn5 + element("mix:bitsPerSampleValue", integerArray(iarray, ',')) + EOL); + colorEncBuf.append(margn5 + element("mix:bitsPerSampleUnit", "integer") + EOL); + // bitsPerSampleUnit can also be floating point. Don't ask me why. + colorEncBuf.append(margn4 + elementEnd("mix:bitsPerSample") + EOL); + useColorEncBuf = true; + } + n = niso.getSamplesPerPixel(); + if (n != NisoImageMetadata.NULL) { + colorEncBuf.append(margn4 + element("mix:samplesPerPixel", Integer.toString(n)) + EOL); + useColorEncBuf = true; + } + + iarray = niso.getExtraSamples(); + if (iarray != null) { + // extraSamples can only be an integer, so the best we can do is + // snag the first value from the array. It also must be limited to + // 0, 1, 2, or 3. + n = iarray[0]; + if (n >= 0 && n <= 3) { + colorEncBuf.append(margn4 + element("mix:extraSamples", Integer.toString(n)) + EOL); + useColorEncBuf = true; + } + } + + String s = niso.getColormapReference(); + if (s != null) { + colorEncBuf.append(margn4 + elementStart("mix:Colormap") + EOL); + colorEncBuf.append(margn5 + element("mix:colormapReference", s) + EOL); + colorEncBuf.append(margn4 + elementEnd("mix:Colormap") + EOL); + useColorEncBuf = true; + } + + // This is complete nonsense, but it's what the spec says + iarray = niso.getGrayResponseCurve(); + if (iarray != null) { + colorEncBuf.append(margn4 + element("mix:grayResponseCurve", "N") + EOL); + useColorEncBuf = true; + } + + n = niso.getGrayResponseUnit(); + if (n != NisoImageMetadata.NULL) { + colorEncBuf.append(margn4 + element("mix:grayResponseUnit", Integer.toString(n)) + EOL); + useColorEncBuf = true; + } - // Add the face information, which is mostly filler. - // In the general case, it can contain multiple Faces; - // this isn't supported yet. - List facelist = aes.getFaceList (); - if (!facelist.isEmpty ()) { - final String [] [] faceRegionAttrs = { - { "ID", faceRegionID }, - { "formatRef", formatRegionID }, - { "faceRef", faceID }, - { "label", "BuiltByJHOVE" } - }; - final String [] [] faceAttrs = { - { "direction", null }, - { "ID", faceID }, - { "audioObjectRef", audioObjectID }, - { "label", "Face" } - }; - AESAudioMetadata.Face f = - (AESAudioMetadata.Face) facelist.get(0); - faceAttrs[0] [1] = f.getDirection(); - _writer.println (margn2 + elementStart ("aes:face", faceAttrs)); - // Fill in a minimal time range. - AESAudioMetadata.TimeDesc startTime = f.getStartTime(); - if (startTime != null) { - _writer.println (margn3 + elementStart ("aes:timeline")); - writeAESTimeRange (margn3, startTime, f.getDuration()); - _writer.println (margn3 + elementEnd ("aes:timeline")); - } - - // For the present, assume just one face region - AESAudioMetadata.FaceRegion facergn = f.getFaceRegion (0); - _writer.println (margn3 + elementStart ("aes:region", faceRegionAttrs)); - _writer.println (margn4 + elementStart ("aes:timeRange")); - writeAESTimeRange (margn3, - facergn.getStartTime (), facergn.getDuration ()); - _writer.println (margn4 + elementEnd ("aes:timeRange")); - int nchan = aes.getNumChannels (); - if (nchan != AESAudioMetadata.NULL) { - _writer.println (margn4 + element ("aes:numChannels", - Integer.toString (nchan))); - } - String[] locs = aes.getMapLocations (); - for (int ch = 0; ch < nchan; ch++) { - // write a stream element for each channel - String [] [] streamAttrs = { - { "ID", streamIDBase + Integer.toString (ch) }, - { "label", "JHOVE" }, - { "faceRegionRef", faceRegionID } - - }; - _writer.println (margn4 + elementStart ("aes:stream", streamAttrs)); - String [] [] chanAttrs = { - { "channelNum", Integer.toString(ch) }, - { "mapLocation", locs[ch] } - }; - _writer.println (margn5 + element ("aes:channelAssignment", chanAttrs)); - _writer.println (margn4 + elementEnd ("aes:stream")); - } - _writer.println (margn3 + elementEnd ("aes:region")); - _writer.println (margn2+ elementEnd ("aes:face")); + r = niso.getWhitePointXValue(); + Rational r2 = niso.getWhitePointYValue(); + if (r != null || r2 != null) { + colorEncBuf.append(margn4 + elementStart("mix:WhitePoint") + EOL); + if (r != null) { + colorEncBuf.append(margn5 + element("mix:whitePointXValue", r.toString()) + EOL); + } + if (r2 != null) { + colorEncBuf.append(margn5 + element("mix:whitePointYValue", r2.toString()) + EOL); + } + colorEncBuf.append(margn4 + elementEnd("mix:WhitePoint") + EOL); + useColorEncBuf = true; + } + + // A chromaticities buffer to go in the color encoding buffer. + StringBuffer chromaBuf = + new StringBuffer(margn4 + elementStart("mix:PrimaryChromaticities") + EOL); + boolean useChromaBuf = false; + r = niso.getPrimaryChromaticitiesRedX(); + if (r != null) { + chromaBuf.append(margn5 + element("mix:primaryChromaticitiesRedX", r.toString()) + EOL); + useChromaBuf = true; + } + r = niso.getPrimaryChromaticitiesRedY(); + if (r != null) { + chromaBuf.append(margn5 + element("mix:primaryChromaticitiesRedY", r.toString()) + EOL); + useChromaBuf = true; + } + r = niso.getPrimaryChromaticitiesGreenX(); + if (r != null) { + chromaBuf.append(margn5 + element("mix:primaryChromaticitiesGreenX", r.toString()) + EOL); + useChromaBuf = true; + } + r = niso.getPrimaryChromaticitiesGreenY(); + if (r != null) { + chromaBuf.append(margn5 + element("mix:primaryChromaticitiesGreenY", r.toString()) + EOL); + useChromaBuf = true; + } + r = niso.getPrimaryChromaticitiesBlueX(); + if (r != null) { + chromaBuf.append(margn5 + element("mix:primaryChromaticitiesBlueX", r.toString()) + EOL); + useChromaBuf = true; + } + r = niso.getPrimaryChromaticitiesBlueY(); + if (r != null) { + chromaBuf.append(margn5 + element("mix:primaryChromaticitiesBlueY", r.toString()) + EOL); + useChromaBuf = true; + } + chromaBuf.append(margn4 + elementEnd("mix:PrimaryChromaticities") + EOL); + if (useChromaBuf) { + colorEncBuf.append(chromaBuf); + useColorEncBuf = true; + } + + colorEncBuf.append(margn3 + elementEnd("mix:ImageColorEncoding") + EOL); + if (useColorEncBuf) { + _writer.print(colorEncBuf); + } + + StringBuffer targetBuf = new StringBuffer(margn3 + elementStart("mix:TargetData") + EOL); + boolean useTargetBuf = false; + n = niso.getTargetType(); + if (n != NisoImageMetadata.NULL) { + targetBuf.append(margn4 + element("mix:targetType", Integer.toString(n)) + EOL); + useTargetBuf = true; + } + + // Now a nested buffer for TargetID. + StringBuffer targetIDBuf = new StringBuffer(margn4 + elementStart("mix:TargetID") + EOL); + boolean useTargetIDBuf = false; + + s = niso.getTargetIDManufacturer(); + if (s != null) { + targetIDBuf.append(margn5 + element("mix:targetManufacturer", s) + EOL); + useTargetIDBuf = true; + } + s = niso.getTargetIDName(); + if (s != null) { + targetIDBuf.append(margn5 + element("mix:targetName", s) + EOL); + useTargetIDBuf = true; + } + s = niso.getTargetIDNo(); + if (s != null) { + targetIDBuf.append(margn5 + element("mix:targetNo", s) + EOL); + useTargetIDBuf = true; + } + s = niso.getTargetIDMedia(); + if (s != null) { + targetIDBuf.append(margn5 + element("mix:targetMedia", s) + EOL); + useTargetIDBuf = true; + } + targetIDBuf.append(margn4 + elementEnd("mix:TargetID") + EOL); + + if (useTargetIDBuf) { + targetBuf.append(targetIDBuf); + useTargetBuf = true; + } + s = niso.getImageData(); + if (s != null) { + targetBuf.append(margn4 + element("mix:externalTarget", s) + EOL); + useTargetBuf = true; + } + s = niso.getPerformanceData(); + if (s != null) { + targetBuf.append(margn4 + element("mix:performanceData", s) + EOL); + useTargetBuf = true; + } + + targetBuf.append(margn3 + elementEnd("mix:TargetData") + EOL); + + if (useTargetBuf) { + _writer.print(targetBuf); + } + _writer.println(margn2 + elementEnd("mix:ImageAssessmentMetadata")); + } + + /* 1.0, Top level element 5 of 5: ChangeHistory (without time travel) */ + protected void showChangeHistory10(NisoImageMetadata niso, String margin) { + String margn2 = margin + " "; + String margn3 = margn2 + " "; + String margn4 = margn3 + " "; + String margn5 = margn4 + " "; + + // There may be nothing at all to write. Put the whole thing in a + // buffer. + StringBuffer chBuf = new StringBuffer(margn2 + elementStart("mix:ChangeHistory") + EOL); + boolean useChBuf = false; + + chBuf.append(margn3 + elementStart("mix:ImageProcessing") + EOL); + + String s = niso.getSourceData(); + if (s != null) { + chBuf.append(margn4 + element("mix:sourceData", s) + EOL); + useChBuf = true; + } + s = niso.getProcessingAgency(); + if (s != null) { + chBuf.append(margn4 + element("mix:processingAgency", s) + EOL); + useChBuf = true; + } + StringBuffer sftwBuf = new StringBuffer(margn4 + elementStart("mix:ProcessingSoftware") + EOL); + boolean useSftwBuf = false; + s = niso.getProcessingSoftwareName(); + if (s != null) { + sftwBuf.append(margn5 + element("mix:processingSoftwareName", s) + EOL); + useSftwBuf = true; + } + s = niso.getProcessingSoftwareVersion(); + if (s != null) { + sftwBuf.append(margn5 + element("mix:processingSoftwareVersion", s) + EOL); + useSftwBuf = true; + } + s = niso.getOS(); + if (s != null) { + sftwBuf.append(margn5 + element("mix:processingOperatingSystemName", s) + EOL); + useSftwBuf = true; + } + s = niso.getOSVersion(); + if (s != null) { + sftwBuf.append(margn5 + element("mix:processingOperatingSystemVersion", s) + EOL); + useSftwBuf = true; + } + sftwBuf.append(margn4 + elementEnd("mix:ProcessingSoftware") + EOL); + if (useSftwBuf) { + chBuf.append(sftwBuf); + useChBuf = true; + } + + String[] sarray = niso.getProcessingActions(); + if (sarray != null) { + for (int i = 0; i < sarray.length; i++) { + chBuf.append(margn4 + element("mix:processingActions", sarray[i]) + EOL); + } + useChBuf = true; + } + + chBuf.append(margn3 + elementEnd("mix:ImageProcessing") + EOL); + chBuf.append(margn2 + elementEnd("mix:ChangeHistory") + EOL); + if (useChBuf) { + _writer.println(chBuf); + } + } + + /** Display the NISO image metadata formatted according to the MIX 2.0 schema. */ + protected void showNisoImageMetadata20(NisoImageMetadata niso) { + String margin = getIndent(++_level); + + String[][] attrs = { + {"xmlns:mix", "http://www.loc.gov/mix/v20"}, + {"xmlns:xsi", "http://www.w3.org/2001/XMLSchema-instance"}, + { + "xsi:schemaLocation", + "http://www.loc.gov/mix/v20 http://www.loc.gov/standards/mix/mix20/mix20.xsd" + } + }; + _writer.println(margin + elementStart("mix:mix", attrs)); + + showNisoBasicDigitalObjectInformation20(niso, margin); + showNisoBasicImageInformation20(niso, margin); + showNisoImageCaptureMetadata20(niso, margin); + showNisoImageAssessmentMetadata20(niso, margin); + showChangeHistory20(niso, margin); + + _writer.println(margin + elementEnd("mix:mix")); + + _level--; + } + + /* + * The NISO Metadata output for version 2.0. Top level element 1 of 6: + * BasicDigitalObjectInformation + */ + protected void showNisoBasicDigitalObjectInformation20(NisoImageMetadata niso, String margin) { + String margn2 = margin + " "; + String margn3 = margn2 + " "; + String margn4 = margn3 + " "; + String margn5 = margn4 + " "; + String margn6 = margn5 + " "; + + _writer.println(margn2 + elementStart("mix:BasicDigitalObjectInformation")); + + StringBuffer objIDBuf = new StringBuffer(margn3 + elementStart("mix:ObjectIdentifier") + EOL); + objIDBuf.append(margn4 + element("mix:objectIdentifierType", "JHOVE") + EOL); + String s = niso.getImageIdentifier(); + if (s != null) { + objIDBuf.append(margn4 + element("mix:objectIdentifierValue", s) + EOL); + } + objIDBuf.append(margn3 + elementEnd("mix:ObjectIdentifier") + EOL); + _writer.print(objIDBuf.toString()); + long ln = niso.getFileSize(); + if (ln != NisoImageMetadata.NULL) { + _writer.print(margn3 + element("mix:fileSize", Long.toString(ln)) + EOL); + } + + String mime = niso.getMimeType(); + if (mime != null) { + _writer.println(margn3 + elementStart("mix:FormatDesignation")); + _writer.print(margn4 + element("mix:formatName", mime) + EOL); + _writer.println(margn3 + elementEnd("mix:FormatDesignation")); + } + + if ((s = niso.getByteOrder()) != null) { + // Convert strings to MIX 1.0 form + if (s.startsWith("big")) { + s = "big endian"; + } else if (s.startsWith("little")) { + s = "little endian"; + } + _writer.print(margn3 + element("mix:byteOrder", s) + EOL); + } + + int comp = niso.getCompressionScheme(); + int level = niso.getCompressionLevel(); + String compStr; + switch (comp) { + case 1: + compStr = "Uncompressed"; + break; + case 2: + compStr = "CCITT 1D"; + break; + case 3: + compStr = "Group 3 Fax"; + break; + case 4: + compStr = "Group 4 Fax"; + break; + case 5: + compStr = "LZW"; + break; + case 6: + compStr = "JPEG"; + break; + case 32773: + compStr = "PackBits"; + break; + case 34713: + compStr = "JPEG2000 Lossy"; + break; + case 34714: + compStr = "JPEG2000 Lossless"; + break; + default: + compStr = "Unknown"; + break; + } + if (comp != NisoImageMetadata.NULL || level != NisoImageMetadata.NULL) { + _writer.print(margn3 + elementStart("mix:Compression") + EOL); + if (comp != NisoImageMetadata.NULL) { + _writer.print(margn4 + element("mix:compressionScheme", compStr) + EOL); + } + // TODO it isn't clear how to get from compression level to + // compression ratio + if (level != NisoImageMetadata.NULL && (comp == 34713 || comp == 34714)) { + _writer.print(margn5 + elementStart("mix:compressionRatio") + EOL); + _writer.print(margn6 + element("mix:numerator", Integer.toString(level)) + EOL); + _writer.print(margn5 + elementEnd("mix:compressionRatio") + EOL); + } + + _writer.print(margn3 + elementEnd("mix:Compression") + EOL); + } + + // NOTE: Checksum method and value are never set currently. If they are, + // the + // values set will need to be converted to meaningful MIX values. This + // code is left + // here just as a reminder. + int n = niso.getChecksumMethod(); + s = niso.getChecksumValue(); + if (n != NisoImageMetadata.NULL || s != null) { + _writer.print(margn4 + elementStart("mix:Fixity") + EOL); + if (n != NisoImageMetadata.NULL) { + _writer.print(margn5 + element("mix:messageDigestAlgorithm", Integer.toString(n)) + EOL); + } + if (s != null) { + _writer.print(margn5 + element("mix:messageDigest", s) + EOL); + } + _writer.println(margn4 + elementEnd("mix:Fixity")); + } + + _writer.print(margn2 + elementEnd("mix:BasicDigitalObjectInformation") + EOL); + } + + /* MIX/NISO 2.0, Top level element 2 of 5: BasicImageInformation */ + protected void showNisoBasicImageInformation20(NisoImageMetadata niso, String margin) { + String margn2 = margin + " "; + String margn3 = margn2 + " "; + String margn4 = margn3 + " "; + String margn5 = margn4 + " "; + String margn6 = margn5 + " "; + String margn7 = margn6 + " "; + _writer.println(margn2 + elementStart("mix:BasicImageInformation")); + StringBuffer basCharBuf = + new StringBuffer(margn3 + elementStart("mix:BasicImageCharacteristics") + EOL); + boolean useBasCharBuf = false; + long ln = niso.getImageWidth(); + if (ln != NisoImageMetadata.NULL) { + basCharBuf.append(margn4 + element("mix:imageWidth", Long.toString(ln)) + EOL); + useBasCharBuf = true; + } + ln = niso.getImageLength(); + if (ln != NisoImageMetadata.NULL) { + basCharBuf.append(margn4 + element("mix:imageHeight", Long.toString(ln)) + EOL); + useBasCharBuf = true; + } + // Nest photometric interpretation tentative buffer in basCharBuf + StringBuffer piBuf = + new StringBuffer(margn4 + elementStart("mix:PhotometricInterpretation") + EOL); + boolean usePIBuf = false; + int n = niso.getColorSpace(); + if (n != NisoImageMetadata.NULL) { + piBuf.append(margn5 + element("mix:colorSpace", photometricInterpretationToString(n)) + EOL); + usePIBuf = true; + } + String s = niso.getProfileName(); + String s2 = niso.getProfileURL(); + if (s != null || s2 != null) { + piBuf.append(margn5 + elementStart("mix:ColorProfile") + EOL); + piBuf.append(margn6 + elementStart("mix:IccProfile") + EOL); + if (s != null) { + piBuf.append(margn7 + element("mix:iccProfileName", s) + EOL); + } + if (s2 != null) { + piBuf.append(margn7 + element("mix:iccProfileURI", s2) + EOL); + } + piBuf.append(margn6 + elementEnd("mix:IccProfile") + EOL); + // MIX 2.0 also allows embedded and local profiles. We don't + // currently support that. + piBuf.append(margn5 + elementEnd("mix:ColorProfile") + EOL); + usePIBuf = true; + } + int[] iarray = niso.getYCbCrSubSampling(); + n = niso.getYCbCrPositioning(); + Rational[] rarray = niso.getYCbCrCoefficients(); + if (iarray != null || n != NisoImageMetadata.NULL || rarray != null) { + piBuf.append(margn5 + elementStart("mix:YCbCr") + EOL); + usePIBuf = true; + if (iarray != null && iarray.length >= 2) { + piBuf.append(margn6 + elementStart("mix:YCbCrSubSampling") + EOL); + piBuf.append( + margn7 + element("mix:yCbCrSubsampleHoriz", Integer.toString(iarray[0])) + EOL); + piBuf.append(margn7 + element("mix:yCbCrSubsampleVert", Integer.toString(iarray[1])) + EOL); + piBuf.append(margn6 + elementEnd("mix:YCbCrSubSampling") + EOL); + } + if (n != NisoImageMetadata.NULL) { + piBuf.append(margn6 + element("mix:yCbCrPositioning", Integer.toString(n)) + EOL); + } + if (rarray != null && rarray.length >= 3) { + piBuf.append(margn6 + elementStart("mix:YCbCrCoefficients") + EOL); + rationalToString(piBuf, "mix:lumaRed", margn7, rarray[0]); + rationalToString(piBuf, "mix:lumaGreen", margn7, rarray[1]); + rationalToString(piBuf, "mix:lumaBlue", margn7, rarray[2]); + piBuf.append(margn6 + elementEnd("mix:YCbCrCoefficients") + EOL); + } + piBuf.append(margn5 + elementEnd("mix:YCbCr") + EOL); + } + + rarray = niso.getReferenceBlackWhite(); + if (rarray != null) { + piBuf.append(margn5 + elementStart("mix:ReferenceBlackWhite") + EOL); + for (int i = 0; i < rarray.length - 1; i += 2) { + piBuf.append(margn6 + elementStart("mix:Component") + EOL); + piBuf.append(margn7 + elementStart("mix:componentPhotometricInterpretation")); + // Tricky here. The reference BW might be given as either RGB or + // yCbCr. + String pi; + if (niso.getColorSpace() == 6) { // yCbCr + switch (i) { + case 0: + pi = "Y"; + break; + case 2: + pi = "Cb"; + break; + case 4: + default: + pi = "Cr"; + break; + } + } else { + switch (i) { // otherwise assume RGB + case 0: + pi = "R"; + break; + case 2: + pi = "G"; + break; + case 4: + default: + pi = "B"; + break; + } } + piBuf.append(pi + elementEnd("mix:componentPhotometricInterpretation") + EOL); + rationalToString(piBuf, "mix:footroom", margn7, rarray[i]); + rationalToString(piBuf, "mix:headroom", margn7, rarray[i + 1]); + piBuf.append(margn7 + elementEnd("mix:Component") + EOL); + } + piBuf.append(margn6 + elementEnd("mix:ReferenceBlackWhite") + EOL); + usePIBuf = true; + } + piBuf.append(margn4 + elementEnd("mix:PhotometricInterpretation") + EOL); + if (usePIBuf) { + basCharBuf.append(piBuf); + useBasCharBuf = true; + } + basCharBuf.append(margn3 + elementEnd("mix:BasicImageCharacteristics")); + + if (useBasCharBuf) { + _writer.println(basCharBuf); + } - // In the general case, a FormatList can contain multiple - // FormatRegions. This doesn't happen with any of the current - // modules; if it's needed in the future, simply set up an - // iteration loop on formatList. - List flist = aes.getFormatList (); - if (!flist.isEmpty ()) { - AESAudioMetadata.FormatRegion rgn = - (AESAudioMetadata.FormatRegion) flist.get(0); - int bitDepth = rgn.getBitDepth (); - double sampleRate = rgn.getSampleRate (); - int wordSize = rgn.getWordSize (); - String[] bitRed = rgn.getBitrateReduction (); - // Build a FormatRegion subtree if at least one piece of data - // that goes into it is present. - if (bitDepth != AESAudioMetadata.NULL || - sampleRate != AESAudioMetadata.NILL || - wordSize != AESAudioMetadata.NULL) { - _writer.println (margn2 + elementStart ("aes:formatList")); - String[] [] frAttr = { { "ID", formatRegionID } }; - _writer.println (margn3 + elementStart ("aes:formatRegion", frAttr)); - if (bitDepth != AESAudioMetadata.NULL) { - _writer.println (margn4 + element ("aes:bitDepth", - Integer.toString (bitDepth))); - } - if (sampleRate != AESAudioMetadata.NILL) { - _writer.println (margn4 + element ("aes:sampleRate", - formatters.get().format (sampleRate))); - } - if (wordSize != AESAudioMetadata.NULL) { - _writer.println (margn4 + element ("aes:wordSize", - Integer.toString (wordSize))); - } - if (bitRed != null) { - _writer.println (margn4 + elementStart ("aes:bitrateReduction")); - _writer.println (margn5 + element - ("aes:codecName", bitRed[0])); - _writer.println (margn5 + element - ("aes:codecNameVersion", bitRed[1])); - _writer.println (margn5 + element - ("aes:codecCreatorApplication", bitRed[2])); - _writer.println (margn5 + element - ("aes:codecCreatorApplicationVersion", bitRed[3])); - _writer.println (margn5 + element - ("aes:codecQuality", bitRed[4])); - _writer.println (margn5 + element - ("aes:dataRate", bitRed[5])); - _writer.println (margn5 + element - ("aes:dataRateMode", bitRed[6])); - _writer.println (margn4 + elementEnd ("aes:bitrateReduction")); - } - _writer.println (margn3 + elementEnd ("aes:formatRegion")); - _writer.println (margn2 + elementEnd ("aes:formatList")); - } + // TODO SpecialFormatCharacteristics would be nice to have here, + // Limited to JPEG2000 + StringBuffer speCharBuf = + new StringBuffer(margn3 + elementStart("mix:SpecialFormatCharacteristics") + EOL); + boolean useSpeCharBuf = false; + int lay = niso.getJp2Layers(); + int lev = niso.getJp2ResolutionLevels(); + String sizTiles = niso.getJp2Tiles(); + if (sizTiles != null || lay != NisoImageMetadata.NULL || lev != NisoImageMetadata.NULL) { + + useSpeCharBuf = true; + speCharBuf.append(margn4 + elementStart("mix:JPEG2000") + EOL); + speCharBuf.append(margn5 + elementStart("mix:EncodingOptions") + EOL); + if (sizTiles != null) { + String[] sizes = sizTiles.split("x"); + speCharBuf.append(margn6 + elementStart("mix:Tiles") + EOL); + speCharBuf.append(margn7 + element("mix:tileWidth", sizes[0]) + EOL); + speCharBuf.append(margn7 + element("mix:tileHeight", sizes[1]) + EOL); + speCharBuf.append(margn6 + elementEnd("mix:Tiles") + EOL); + } + if (lay != NisoImageMetadata.NULL) { + speCharBuf.append(margn6 + element("mix:qualityLayers", Integer.toString(lay)) + EOL); + } + if (sizTiles != null) { + speCharBuf.append(margn6 + element("mix:resolutionLevels", Integer.toString(lev)) + EOL); + } + speCharBuf.append(margn5 + elementEnd("mix:EncodingOptions") + EOL); + speCharBuf.append(margn4 + elementEnd("mix:JPEG2000") + EOL); + } + + speCharBuf.append(margn3 + elementEnd("mix:SpecialFormatCharacteristics")); + if (useSpeCharBuf) { + _writer.println(speCharBuf); + } + + _writer.println(margn2 + elementEnd("mix:BasicImageInformation")); + } + + /* 2.0, Top level element 3 of 5: ImageCaptureMetadata */ + protected void showNisoImageCaptureMetadata20(NisoImageMetadata niso, String margin) { + String margn2 = margin + " "; + String margn3 = margn2 + " "; + String margn4 = margn3 + " "; + String margn5 = margn4 + " "; + String margn6 = margn5 + " "; + String margn7 = margn6 + " "; + String margn8 = margn7 + " "; + + // We don't start with an ImageCaptureMetadata element, because the + // whole element is conditional on having some content. + StringBuffer captureBuffer = new StringBuffer(); + boolean useCaptureBuffer = false; + int n; + + String s = niso.getSourceType(); + String si = niso.getSourceID(); + double d = niso.getSourceXDimension(); + if (s != null || si != null || d != NisoImageMetadata.NILL) { + captureBuffer.append(margn3 + element("mix:SourceInformation", s)); + useCaptureBuffer = true; + if (s != null) { + captureBuffer.append(margn4 + element("mix:sourceType", s)); + } + if (si != null) { + captureBuffer.append(margn4 + elementStart("mix:SourceID")); + captureBuffer.append(margn4 + element("mix:sourceIDValue", si)); + captureBuffer.append(margn4 + elementEnd("mix:sourceID")); + } + n = niso.getSourceXDimensionUnit(); + if (d != NisoImageMetadata.NILL || n != NisoImageMetadata.NULL) { + // Assume that both X and Y exist, or neither + captureBuffer.append(margn4 + elementStart("mix:SourceSize")); + captureBuffer.append(margn5 + elementStart("mix:SourceXDimension") + EOL); + if (d != NisoImageMetadata.NILL) { + captureBuffer.append( + margn6 + element("mix:sourceXDimensionValue", formatters.get().format(d)) + EOL); + } + if (n != NisoImageMetadata.NULL) { + captureBuffer.append( + margn6 + element("mix:sourceXDimensionUnit", Integer.toString(n)) + EOL); + } + captureBuffer.append(margn5 + elementEnd("mix:SourceXDimension") + EOL); + + d = niso.getSourceYDimension(); + n = niso.getSourceYDimensionUnit(); + if (d != NisoImageMetadata.NILL || n != NisoImageMetadata.NULL) { + captureBuffer.append(margn5 + elementStart("mix:SourceYDimension") + EOL); + if (d != NisoImageMetadata.NILL) { + captureBuffer.append( + margn6 + element("mix:sourceYDimensionValue", formatters.get().format(d)) + EOL); + } + if (n != NisoImageMetadata.NULL) { + captureBuffer.append( + margn6 + element("mix:sourceYDimensionUnit", Integer.toString(n)) + EOL); + } + captureBuffer.append(margn5 + elementEnd("mix:SourceYDimension") + EOL); + } + captureBuffer.append(margn4 + elementEnd("mix:SourceSize") + EOL); + } + captureBuffer.append(margn3 + elementEnd("mix:SourceInformation") + EOL); + } + StringBuffer genCapBuf = + new StringBuffer(margn3 + elementStart("mix:GeneralCaptureInformation") + EOL); + boolean useGenCapBuf = false; + + s = niso.getDateTimeCreated(); + if (s != null) { + genCapBuf.append(margn3 + element("mix:dateTimeCreated", s) + EOL); + useGenCapBuf = true; + } + s = niso.getImageProducer(); + if (s != null) { + genCapBuf.append(margn3 + element("mix:imageProducer", s) + EOL); + useGenCapBuf = true; + } + + s = niso.getDeviceSource(); + if (s != null) { + genCapBuf.append(margn3 + element("mix:captureDevice", s) + EOL); + /* + * This has a restricted set of values. Does the setting code + * conform? + */ + } + + genCapBuf.append(margn3 + elementEnd("mix:GeneralCaptureInformation") + EOL); + if (useGenCapBuf) { + captureBuffer.append(genCapBuf); + useCaptureBuffer = true; + } + + // Here's a chunk of XML for scanners. + StringBuffer scanCapBuf = new StringBuffer(margn3 + elementStart("mix:ScannerCapture") + EOL); + boolean useScanCapBuf = false; + String mfg = niso.getScannerManufacturer(); + if (mfg != null) { + scanCapBuf.append(margn4 + element("mix:scannerManufacturer", mfg) + EOL); + useScanCapBuf = true; + } + String model = niso.getScannerModelName(); + String modelNum = niso.getScannerModelNumber(); + String serNum = niso.getScannerModelSerialNo(); + if (model != null || modelNum != null || serNum != null) { + useScanCapBuf = true; + scanCapBuf.append(margn4 + elementStart("mix:ScannerModel") + EOL); + if (model != null) { + scanCapBuf.append(margn5 + element("mix:scannerModelName", model) + EOL); + } + if (modelNum != null) { + scanCapBuf.append(margn5 + element("mix:scannerModelNumber", modelNum) + EOL); + } + if (serNum != null) { + scanCapBuf.append(margn5 + element("mix:scannerModelSerialNo", serNum) + EOL); + } + scanCapBuf.append(margn4 + elementEnd("mix:ScannerModel") + EOL); + } + double xres = niso.getXPhysScanResolution(); + double yres = niso.getYPhysScanResolution(); + if (xres != NisoImageMetadata.NULL && yres != NisoImageMetadata.NULL) { + scanCapBuf.append(margn4 + elementStart("mix:MaximumOpticalResolution") + EOL); + scanCapBuf.append( + margn5 + element("mix:xOpticalResolution", formatters.get().format(xres)) + EOL); + scanCapBuf.append( + margn5 + element("mix:yOpticalResolution", formatters.get().format(yres)) + EOL); + scanCapBuf.append( + margn5 + element("mix:resolutionUnit", "in.") + EOL); // is this a safe assumption? + scanCapBuf.append(margn4 + elementEnd("mix:MaximumOpticalResolution")); + } + s = niso.getScanningSoftware(); + if (s != null) { + useScanCapBuf = true; + scanCapBuf.append(margn4 + elementStart("mix:ScanningSystemSoftware") + EOL); + scanCapBuf.append(margn5 + element("mix:scanningSoftwareName", s) + EOL); + s = niso.getScanningSoftwareVersionNo(); + if (s != null) { + scanCapBuf.append(margn5 + element("mix:scanningSoftwareVersionNo", s) + EOL); + } + scanCapBuf.append(margn4 + elementEnd("mix:ScanningSystemSoftware") + EOL); + } + scanCapBuf.append(margn3 + elementEnd("mix:ScannerCapture") + EOL); + if (useScanCapBuf) { + captureBuffer.append(scanCapBuf); + useCaptureBuffer = true; + } + + // Now we'll hear from the digital cameras. + StringBuffer digCamBuf = + new StringBuffer(margn3 + elementStart("mix:DigitalCameraCapture") + EOL); + boolean useDigCamBuf = false; + + s = niso.getDigitalCameraManufacturer(); + if (s != null) { + digCamBuf.append(margn4 + element("mix:digitalCameraManufacturer", s) + EOL); + useDigCamBuf = true; + } + String dcmodel = niso.getDigitalCameraModelName(); + String dcmodelNum = niso.getDigitalCameraModelNumber(); + String dcserNum = niso.getDigitalCameraModelSerialNo(); + if (dcmodel != null || dcmodelNum != null || dcserNum != null) { + useDigCamBuf = true; + digCamBuf.append(margn4 + elementStart("mix:DigitalCameraModel") + EOL); + if (dcmodel != null) { + digCamBuf.append(margn5 + element("mix:digitalCameraModelName", dcmodel) + EOL); + } + if (dcmodelNum != null) { + digCamBuf.append(margn5 + element("mix:digitalCameraModelNumber", dcmodelNum) + EOL); + } + if (dcserNum != null) { + digCamBuf.append(margn5 + element("mix:mix:digitalCameraModelSerialNo", dcserNum) + EOL); + } + digCamBuf.append(margn4 + elementEnd("mix:DigitalCameraModel") + EOL); + } + + // Nest a buffer for CameraCaptureSettings + StringBuffer ccSetBuf = + new StringBuffer(margn4 + elementStart("mix:CameraCaptureSettings") + EOL); + boolean useCcSetBuf = false; + // CameraCaptureSettings consists only of an ImageData element, so we + // don't need another use flag here. + ccSetBuf.append(margn5 + elementStart("mix:ImageData") + EOL); + d = niso.getFNumber(); + if (d != NisoImageMetadata.NULL) { + ccSetBuf.append(margn6 + element("mix:fNumber", formatters.get().format(d)) + EOL); + useCcSetBuf = true; + } + d = niso.getExposureTime(); + if (d != NisoImageMetadata.NULL) { + ccSetBuf.append(margn6 + element("mix:exposureTime", formatters.get().format(d)) + EOL); + useCcSetBuf = true; + } + n = niso.getExposureProgram(); + if (n != NisoImageMetadata.NULL) { + if (n > 8 || n < 0) { + n = 0; // force "Not defined" for bad value + } + + ccSetBuf.append( + margn6 + element("mix:exposureProgram", NisoImageMetadata.EXPOSURE_PROGRAM[n]) + EOL); + useCcSetBuf = true; + } + if (niso.getExifVersion() != null) { + ccSetBuf.append(margn6 + element("mix:exifVersion", niso.getExifVersion()) + EOL); + useCcSetBuf = true; + } + Rational r = niso.getBrightness(); + if (r != null) { + rationalToString(ccSetBuf, "mix:brightnessValue", margn6, r); + useCcSetBuf = true; + } + r = niso.getExposureBias(); + if (r != null) { + rationalToString(ccSetBuf, "mix:exposureBiasValue", margn6, r); + useCcSetBuf = true; + } + r = niso.getMaxApertureValue(); + if (r != null) { + rationalToString(ccSetBuf, "mix:maxApertureValue", margn6, r); + useCcSetBuf = true; + } + double[] darray = niso.getSubjectDistance(); + if (darray != null) { + // darray has two values. If they're equal, set "distance". + // Otherwise, + // set the min and max. + ccSetBuf.append(margn6 + elementStart("mix:SubjectDistance") + EOL); + useCcSetBuf = true; + if (darray[0] == darray[1]) { + ccSetBuf.append(margn7 + element("mix:distance", formatters.get().format(darray[0])) + EOL); + } else { + ccSetBuf.append(margn7 + elementStart("mix:MinMaxDistance") + EOL); + ccSetBuf.append( + margn8 + element("mix:minDistance", formatters.get().format(darray[0])) + EOL); + ccSetBuf.append( + margn8 + element("mix:maxDistance", formatters.get().format(darray[1])) + EOL); + ccSetBuf.append(margn7 + elementEnd("mix:MinMaxDistance") + EOL); + } + ccSetBuf.append(margn6 + elementEnd("mix:SubjectDistance") + EOL); + } + n = niso.getMeteringMode(); + if (n != NisoImageMetadata.NULL) { + ccSetBuf.append(margn6 + element("mix:meteringMode", meteringModeToString(n)) + EOL); + useCcSetBuf = true; + } + n = niso.getFlash(); + if (n != NisoImageMetadata.NULL) { + // First bit (0 = Flash did not fire, 1 = Flash fired) + int firstBit = n & 1; + ccSetBuf.append(margn6 + element("mix:flash", NisoImageMetadata.FLASH_20[firstBit]) + EOL); + useCcSetBuf = true; + } + d = niso.getFocalLength(); + if (d != NisoImageMetadata.NULL) { + ccSetBuf.append(margn6 + element("mix:focalLength", formatters.get().format(d)) + EOL); + useCcSetBuf = true; + } + r = niso.getFlashEnergy(); + if (r != null) { + rationalToString(ccSetBuf, "mix:flashEnergy", margn6, r); + useCcSetBuf = true; + } + n = niso.getBackLight(); + if (n != NisoImageMetadata.NULL) { + ccSetBuf.append(margn6 + element("mix:backLight", Integer.toString(n)) + EOL); + useCcSetBuf = true; + } + d = niso.getExposureIndex(); + if (d != NisoImageMetadata.NULL) { + ccSetBuf.append(margn6 + element("mix:exposureIndex", formatters.get().format(d)) + EOL); + useCcSetBuf = true; + } + n = niso.getAutoFocus(); + if (n != NisoImageMetadata.NULL) { + ccSetBuf.append(margn6 + element("mix:autoFocus", Integer.toString(n)) + EOL); + useCcSetBuf = true; + } + d = niso.getXPrintAspectRatio(); + double d2 = niso.getYPrintAspectRatio(); + if (d != NisoImageMetadata.NULL || d2 != NisoImageMetadata.NULL) { + ccSetBuf.append(margn6 + elementStart("mix:PrintAspectRatio") + EOL); + if (d != NisoImageMetadata.NULL) { + ccSetBuf.append( + margn7 + element("mix:xPrintAspectRatio", formatters.get().format(d)) + EOL); + } + if (d2 != NisoImageMetadata.NULL) { + ccSetBuf.append( + margn7 + element("mix:yPrintAspectRatio", formatters.get().format(d2)) + EOL); + } + + ccSetBuf.append(margn6 + elementEnd("mix:PrintAspectRatio") + EOL); + } + + ccSetBuf.append(margn5 + elementEnd("mix:ImageData") + EOL); + ccSetBuf.append(margn4 + elementEnd("mix:CameraCaptureSettings") + EOL); + if (useCcSetBuf) { + digCamBuf.append(ccSetBuf); + useDigCamBuf = true; + } + digCamBuf.append(margn3 + elementEnd("mix:DigitalCameraCapture") + EOL); + if (useDigCamBuf) { + captureBuffer.append(digCamBuf); + useCaptureBuffer = true; + } + + n = niso.getOrientation(); + if (n != NisoImageMetadata.NULL) { + final String[] orient = { + "unknown", + "normal*", + "normal, image flipped", + "normal, rotated 180\u00B0", + "normal, image flipped, rotated 180\u00B0", + "normal, image flipped, rotated cw 90\u00B0", + "normal, rotated ccw 90\u00B0", + "normal, image flipped, rotated ccw 90\u00B0", + "normal, rotated cw 90\u00B0" + }; + if (n > 8 || n < 0) { + n = 0; // force "unknown" for bad value + } + captureBuffer.append(margn3 + element("mix:orientation", orient[n]) + EOL); + useCaptureBuffer = true; + } + s = niso.getMethodology(); + if (s != null) { + captureBuffer.append(margn3 + element("mix:methodology", s) + EOL); + } + if (useCaptureBuffer) { + _writer.println(margn2 + elementStart("mix:ImageCaptureMetadata")); + _writer.print(captureBuffer.toString()); + _writer.println(margn2 + elementEnd("mix:ImageCaptureMetadata")); + } + } + + /* 2.0, Top level element 4 of 5: ImageAssessmentMetadata */ + protected void showNisoImageAssessmentMetadata20(NisoImageMetadata niso, String margin) { + String margn2 = margin + " "; + String margn3 = margn2 + " "; + String margn4 = margn3 + " "; + String margn5 = margn4 + " "; + + _writer.println(margn2 + elementStart("mix:ImageAssessmentMetadata")); + StringBuffer metricsBuf = new StringBuffer(margn3 + elementStart("mix:SpatialMetrics") + EOL); + boolean useMetricsBuf = false; + + int n = niso.getSamplingFrequencyPlane(); + if (n != NisoImageMetadata.NULL) { + metricsBuf.append(margn4 + element("mix:samplingFrequencyPlane", Integer.toString(n)) + EOL); + useMetricsBuf = true; + } + n = niso.getSamplingFrequencyUnit(); + if (n != NisoImageMetadata.NULL) { + final String sfu[] = {null, "no absolute unit of measurement", "in.", "cm"}; + if (n < 1 || n > 3) { + n = 1; + } + metricsBuf.append(margn4 + element("mix:samplingFrequencyUnit", sfu[n]) + EOL); + useMetricsBuf = true; + } + Rational r = niso.getXSamplingFrequency(); + if (r != null) { + rationalToString(metricsBuf, "mix:xSamplingFrequency", margn4, r); + } + r = niso.getYSamplingFrequency(); + if (r != null) { + rationalToString(metricsBuf, "mix:ySamplingFrequency", margn4, r); + } + metricsBuf.append(margn3 + elementEnd("mix:SpatialMetrics")); + if (useMetricsBuf) { + _writer.println(metricsBuf); + } + + StringBuffer colorEncBuf = + new StringBuffer(margn3 + elementStart("mix:ImageColorEncoding") + EOL); + boolean useColorEncBuf = false; + + int[] iarray = niso.getBitsPerSample(); + if (iarray != null) { + colorEncBuf.append(margn4 + elementStart("mix:BitsPerSample") + EOL); + for (int ii = 0; ii < iarray.length; ii++) { + colorEncBuf.append( + margn5 + element("mix:bitsPerSampleValue", Integer.toString(iarray[ii])) + EOL); + } + colorEncBuf.append(margn5 + element("mix:bitsPerSampleUnit", "integer") + EOL); + // bitsPerSampleUnit can also be floating point. Don't ask me why. + colorEncBuf.append(margn4 + elementEnd("mix:BitsPerSample") + EOL); + useColorEncBuf = true; + } + n = niso.getSamplesPerPixel(); + if (n != NisoImageMetadata.NULL) { + colorEncBuf.append(margn4 + element("mix:samplesPerPixel", Integer.toString(n)) + EOL); + useColorEncBuf = true; + } + + iarray = niso.getExtraSamples(); + if (iarray != null) { + for (int ii = 0; ii < iarray.length; ii++) { + n = iarray[ii]; + if (n >= 0 && n <= 3) { + colorEncBuf.append( + margn4 + element("mix:extraSamples", NisoImageMetadata.EXTRA_SAMPLE_20[n]) + EOL); + useColorEncBuf = true; } - /* This should go somewhere, but where? */ -// int nchan = aes.getNumChannels (); -// if (nchan != AESAudioMetadata.NULL) { -// _writer.println (margn2 + element ("aes:numChannels", -// Integer.toString (nchan))); -// } - - _writer.println (margin + elementEnd ("aes:audioObject")); - - _level -= 3; - } - - /* Break out the writing of a timeRangeType element. - * This always gives a start time of 0. This is all - * FAKE DATA for the moment. */ - private void writeAESTimeRange (String baseIndent, - AESAudioMetadata.TimeDesc start, - AESAudioMetadata.TimeDesc duration) - { - final String margn1 = baseIndent + " "; - final String margn2 = margn1 + " "; - final String [] [] attrs = { - { "tcf:frameCount", "30" }, - { "tcf:timeBase", "1000" }, - { "tcf:videoField", "FIELD_1" }, - { "tcf:countingMode", "NTSC_NON_DROP_FRAME" } + } + } + + String s = niso.getColormapReference(); + if (s != null) { + colorEncBuf.append(margn4 + elementStart("mix:Colormap") + EOL); + colorEncBuf.append(margn5 + element("mix:colormapReference", s) + EOL); + colorEncBuf.append(margn4 + elementEnd("mix:Colormap") + EOL); + useColorEncBuf = true; + } + + iarray = niso.getGrayResponseCurve(); + n = niso.getGrayResponseUnit(); + if (iarray != null || n != NisoImageMetadata.NULL) { + StringBuffer grayRespBuf = new StringBuffer(margn4 + elementStart("mix:GrayResponse") + EOL); + if (iarray != null) { + for (int ii = 0; ii < iarray.length; ii++) { + grayRespBuf.append( + margn5 + element("mix:grayResponseCurve", Integer.toString(iarray[ii])) + EOL); + } + } + + if (n != NisoImageMetadata.NULL && n > 0 && n <= 5) { + // Convert integer to text value; only values 1-5 are legal + grayRespBuf.append( + margn5 + + element("mix:grayResponseUnit", NisoImageMetadata.GRAY_RESPONSE_UNIT_20[n - 1]) + + EOL); + } + grayRespBuf.append(margn4 + elementEnd("mix:GrayResponse") + EOL); + colorEncBuf.append(grayRespBuf); + useColorEncBuf = true; + } + + r = niso.getWhitePointXValue(); + Rational r2 = niso.getWhitePointYValue(); + if (r != null || r2 != null) { + colorEncBuf.append(margn4 + elementStart("mix:WhitePoint") + EOL); + if (r != null) { + rationalToString(colorEncBuf, "mix:whitePointXValue", margn5, r); + } + if (r2 != null) { + rationalToString(colorEncBuf, "mix:whitePointYValue", margn5, r2); + } + colorEncBuf.append(margn4 + elementEnd("mix:WhitePoint") + EOL); + useColorEncBuf = true; + } + + // A chromaticities buffer to go in the color encoding buffer. + StringBuffer chromaBuf = + new StringBuffer(margn4 + elementStart("mix:PrimaryChromaticities") + EOL); + boolean useChromaBuf = false; + r = niso.getPrimaryChromaticitiesRedX(); + if (r != null) { + rationalToString(chromaBuf, "mix:primaryChromaticitiesRedX", margn5, r); + useChromaBuf = true; + } + r = niso.getPrimaryChromaticitiesRedY(); + if (r != null) { + rationalToString(chromaBuf, "mix:primaryChromaticitiesRedY", margn5, r); + useChromaBuf = true; + } + r = niso.getPrimaryChromaticitiesGreenX(); + if (r != null) { + rationalToString(chromaBuf, "mix:primaryChromaticitiesGreenX", margn5, r); + useChromaBuf = true; + } + r = niso.getPrimaryChromaticitiesGreenY(); + if (r != null) { + rationalToString(chromaBuf, "mix:primaryChromaticitiesGreenY", margn5, r); + useChromaBuf = true; + } + r = niso.getPrimaryChromaticitiesBlueX(); + if (r != null) { + rationalToString(chromaBuf, "mix:primaryChromaticitiesBlueX", margn5, r); + useChromaBuf = true; + } + r = niso.getPrimaryChromaticitiesBlueY(); + if (r != null) { + rationalToString(chromaBuf, "mix:primaryChromaticitiesBlueY", margn5, r); + useChromaBuf = true; + } + chromaBuf.append(margn4 + elementEnd("mix:PrimaryChromaticities") + EOL); + if (useChromaBuf) { + colorEncBuf.append(chromaBuf); + useColorEncBuf = true; + } + + colorEncBuf.append(margn3 + elementEnd("mix:ImageColorEncoding") + EOL); + if (useColorEncBuf) { + _writer.print(colorEncBuf); + } + + StringBuffer targetBuf = new StringBuffer(margn3 + elementStart("mix:TargetData") + EOL); + boolean useTargetBuf = false; + n = niso.getTargetType(); + if (n != NisoImageMetadata.NULL) { + targetBuf.append(margn4 + element("mix:targetType", Integer.toString(n)) + EOL); + useTargetBuf = true; + } + + // Now a nested buffer for TargetID. + StringBuffer targetIDBuf = new StringBuffer(margn4 + elementStart("mix:TargetID") + EOL); + boolean useTargetIDBuf = false; + + s = niso.getTargetIDManufacturer(); + if (s != null) { + targetIDBuf.append(margn5 + element("mix:targetManufacturer", s) + EOL); + useTargetIDBuf = true; + } + s = niso.getTargetIDName(); + if (s != null) { + targetIDBuf.append(margn5 + element("mix:targetName", s) + EOL); + useTargetIDBuf = true; + } + s = niso.getTargetIDNo(); + if (s != null) { + targetIDBuf.append(margn5 + element("mix:targetNo", s) + EOL); + useTargetIDBuf = true; + } + s = niso.getTargetIDMedia(); + if (s != null) { + targetIDBuf.append(margn5 + element("mix:targetMedia", s) + EOL); + useTargetIDBuf = true; + } + targetIDBuf.append(margn4 + elementEnd("mix:TargetID") + EOL); + + if (useTargetIDBuf) { + targetBuf.append(targetIDBuf); + useTargetBuf = true; + } + s = niso.getImageData(); + if (s != null) { + targetBuf.append(margn4 + element("mix:externalTarget", s) + EOL); + useTargetBuf = true; + } + s = niso.getPerformanceData(); + if (s != null) { + targetBuf.append(margn4 + element("mix:performanceData", s) + EOL); + useTargetBuf = true; + } + + targetBuf.append(margn3 + elementEnd("mix:TargetData") + EOL); + + if (useTargetBuf) { + _writer.print(targetBuf); + } + _writer.println(margn2 + elementEnd("mix:ImageAssessmentMetadata")); + } + + /* 2.0, Top level element 5 of 5: ChangeHistory */ + protected void showChangeHistory20(NisoImageMetadata niso, String margin) { + String margn2 = margin + " "; + String margn3 = margn2 + " "; + String margn4 = margn3 + " "; + String margn5 = margn4 + " "; + // String margn6 = margn5 + " "; + + // There may be nothing at all to write. Put the whole thing in a + // buffer. + StringBuffer chBuf = new StringBuffer(margn2 + elementStart("mix:ChangeHistory") + EOL); + boolean useChBuf = false; + + chBuf.append(margn3 + elementStart("mix:ImageProcessing") + EOL); + + String s = niso.getSourceData(); + if (s != null) { + chBuf.append(margn4 + element("mix:sourceData", s) + EOL); + useChBuf = true; + } + s = niso.getProcessingAgency(); + if (s != null) { + chBuf.append(margn4 + element("mix:processingAgency", s) + EOL); + useChBuf = true; + } + StringBuffer sftwBuf = new StringBuffer(margn4 + elementStart("mix:ProcessingSoftware") + EOL); + boolean useSftwBuf = false; + s = niso.getProcessingSoftwareName(); + if (s != null) { + sftwBuf.append(margn5 + element("mix:processingSoftwareName", s) + EOL); + useSftwBuf = true; + } + s = niso.getProcessingSoftwareVersion(); + if (s != null) { + sftwBuf.append(margn5 + element("mix:processingSoftwareVersion", s) + EOL); + useSftwBuf = true; + } + s = niso.getOS(); + if (s != null) { + sftwBuf.append(margn5 + element("mix:processingOperatingSystemName", s) + EOL); + useSftwBuf = true; + } + s = niso.getOSVersion(); + if (s != null) { + sftwBuf.append(margn5 + element("mix:processingOperatingSystemVersion", s) + EOL); + useSftwBuf = true; + } + sftwBuf.append(margn4 + elementEnd("mix:ProcessingSoftware") + EOL); + if (useSftwBuf) { + chBuf.append(sftwBuf); + useChBuf = true; + } + + String[] sarray = niso.getProcessingActions(); + if (sarray != null) { + for (int i = 0; i < sarray.length; i++) { + chBuf.append(margn4 + element("mix:processingActions", sarray[i]) + EOL); + } + useChBuf = true; + } + + chBuf.append(margn3 + elementEnd("mix:ImageProcessing") + EOL); + chBuf.append(margn2 + elementEnd("mix:ChangeHistory") + EOL); + if (useChBuf) { + _writer.println(chBuf); + } + } + + /** Convert the metering mode value to one of the suggested values for MIX 2.0 */ + private String meteringModeToString(int n) { + String s = NisoImageMetadata.METERING_MODE[1]; + if (n >= 1 && n <= 6) { + s = NisoImageMetadata.METERING_MODE[n]; + } + // Capitalize first letter + return s.substring(0, 1).toUpperCase(Locale.ROOT) + s.substring(1); + } + + /** + * Convert the color space value (which is based on the TIFF PhotometricInterpretation convention) + * to one of the suggested values for MIX 2.0 + */ + private String photometricInterpretationToString(int n) { + String s = "Unknown"; + switch (n) { + case 0: + s = "WhiteIsZero"; + break; + case 1: + s = "BlackIsZero"; + break; + case 2: + s = "RGB"; + break; + case 3: + s = "PaletteColor"; + break; + case 4: + s = "TransparencyMask"; + break; + case 5: + s = "CMYK"; + break; + case 6: + s = "YCbCr"; + break; + case 8: + s = "CIELab"; + break; + case 9: + s = "ICCLab"; + break; + case 10: + s = "ITULab"; + break; + case 32803: + s = "CFA"; + break; // used by DNG + case 34892: + s = "LinearRaw"; + break; // used by DNG + case 65535: + s = "YCCK"; + break; // used by Adobe JPEG + default: + break; + } + return s; + } + + /** Convert compression scheme value (based on the TIFF compression convention) to a label */ + private String compressionSchemeToString(int n) { + for (int i = 0; i < NisoImageMetadata.COMPRESSION_SCHEME_INDEX.length; i++) { + if (n == NisoImageMetadata.COMPRESSION_SCHEME_INDEX[i]) + return NisoImageMetadata.COMPRESSION_SCHEME[i]; + } + return Integer.toString(n); + } + + /** + * Display the audio metadata formatted according to the AES schema. + * + * @param aes AES audio metadata + */ + protected void showAESAudioMetadata(AESAudioMetadata aes) { + _level += 3; + final String margin = getIndent(_level); + final String margn2 = margin + " "; + final String margn3 = margn2 + " "; + final String margn4 = margn3 + " "; + final String margn5 = margn4 + " "; + // final String margn6 = margn5 + " "; + + // ID strings. These are arbitrary, but must be unique + // within the document. + final String formatRegionID = "J1"; + final String faceRegionID = "J2"; + final String faceID = "J3"; + final String audioObjectID = "J4"; + final String streamIDBase = "J9"; + + _sampleRate = aes.getSampleRate(); + + final String[][] attrs = { + {"xmlns:aes", "http://www.aes.org/audioObject"}, + {"xmlns:tcf", "http://www.aes.org/tcf"}, + {"xmlns:xsi", "http://www.w3.org/2001/XMLSchema-instance"}, + {"ID", audioObjectID}, + {"analogDigitalFlag", aes.getAnalogDigitalFlag()}, + {"disposition", "Validated by JHOVE"}, + {"schemaVersion", "1.02b"} + }; + _writer.println(margin + elementStart("aes:audioObject", attrs)); + String s = aes.getFormat(); + if (s != null) { + String v = aes.getSpecificationVersion(); + String[][] fmattrs = new String[1][2]; + fmattrs[0][0] = "specificationVersion"; + if (v != null) { + fmattrs[0][1] = v; + } else { + // Shouldn't happen + fmattrs[0][1] = ""; + } + _writer.println(margn2 + element("aes:format", fmattrs, s)); + } + s = aes.getAppSpecificData(); + if (s != null) { + _writer.println(margn2 + element("aes:appSpecificData", s)); + } + s = aes.getAudioDataEncoding(); + if (s != null) { + _writer.println(margn2 + element("aes:audioDataEncoding", s)); + } + int in = aes.getByteOrder(); + if (in != AESAudioMetadata.NULL) { + _writer.println( + margn2 + + element( + "aes:byteOrder", + in == AESAudioMetadata.BIG_ENDIAN ? "BIG_ENDIAN" : "LITTLE_ENDIAN")); + } + long lin = aes.getFirstSampleOffset(); + if (lin != AESAudioMetadata.NULL) { + _writer.println(margn2 + element("aes:firstSampleOffset", Long.toString(lin))); + } + String[] use = aes.getUse(); + if (use != null) { + String[][] uattrs = + new String[][] { + {"useType", use[0]}, + {"otherType", use[1]} + }; + _writer.println(margn2 + element("aes:use", uattrs)); + } + s = aes.getPrimaryIdentifier(); + if (s != null) { + String t = aes.getPrimaryIdentifierType(); + String[][] idattrs = new String[1][2]; + idattrs[0][0] = "identifierType"; + if (t != null) { + idattrs[0][1] = t; + } else { + // Shouldn't happen + idattrs[0][1] = ""; + } + _writer.println(margn2 + element("aes:primaryIdentifier", idattrs, s)); + } + + // Add the face information, which is mostly filler. + // In the general case, it can contain multiple Faces; + // this isn't supported yet. + List facelist = aes.getFaceList(); + if (!facelist.isEmpty()) { + final String[][] faceRegionAttrs = { + {"ID", faceRegionID}, + {"formatRef", formatRegionID}, + {"faceRef", faceID}, + {"label", "BuiltByJHOVE"} + }; + final String[][] faceAttrs = { + {"direction", null}, + {"ID", faceID}, + {"audioObjectRef", audioObjectID}, + {"label", "Face"} + }; + AESAudioMetadata.Face f = (AESAudioMetadata.Face) facelist.get(0); + faceAttrs[0][1] = f.getDirection(); + _writer.println(margn2 + elementStart("aes:face", faceAttrs)); + // Fill in a minimal time range. + AESAudioMetadata.TimeDesc startTime = f.getStartTime(); + if (startTime != null) { + _writer.println(margn3 + elementStart("aes:timeline")); + writeAESTimeRange(margn3, startTime, f.getDuration()); + _writer.println(margn3 + elementEnd("aes:timeline")); + } + + // For the present, assume just one face region + AESAudioMetadata.FaceRegion facergn = f.getFaceRegion(0); + _writer.println(margn3 + elementStart("aes:region", faceRegionAttrs)); + _writer.println(margn4 + elementStart("aes:timeRange")); + writeAESTimeRange(margn3, facergn.getStartTime(), facergn.getDuration()); + _writer.println(margn4 + elementEnd("aes:timeRange")); + int nchan = aes.getNumChannels(); + if (nchan != AESAudioMetadata.NULL) { + _writer.println(margn4 + element("aes:numChannels", Integer.toString(nchan))); + } + String[] locs = aes.getMapLocations(); + for (int ch = 0; ch < nchan; ch++) { + // write a stream element for each channel + String[][] streamAttrs = { + {"ID", streamIDBase + Integer.toString(ch)}, + {"label", "JHOVE"}, + {"faceRegionRef", faceRegionID} + }; + _writer.println(margn4 + elementStart("aes:stream", streamAttrs)); + String[][] chanAttrs = { + {"channelNum", Integer.toString(ch)}, + {"mapLocation", locs[ch]} }; - _writer.println (margn1 + elementStart ("tcf:startTime", attrs)); - _writer.println (margn2 + element ("tcf:hours", - Long.toString (start.getHours ()))); - _writer.println (margn2 + element ("tcf:minutes", - Long.toString (start.getMinutes ()))); - _writer.println (margn2 + element ("tcf:seconds", - Long.toString (start.getSeconds ()))); - _writer.println (margn2 + element ("tcf:frames", - Long.toString (start.getFrames ()) )); - _writer.println (margn1 + elementEnd("tcf:startTime")); - double sr = start.getSampleRate (); - if (sr == 1.0) { - sr = _sampleRate; - } - } - /* - * Clean up a URI string by escaping forbidden characters. We assume - * (perhaps dangerously) that a % is the start of an already escaped - * hexadecimal sequence. - */ - private String cleanURIString(String uri) { - StringBuffer sb = new StringBuffer(uri.length() * 2); - boolean change = false; - for (int i = 0; i < uri.length(); i++) { - char c = uri.charAt(i); - if ((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') - || (c >= '0' && c <= '9') || (c == '%') || // assume it's an - // escape - ("-_.!~*'();/?:@=+$,".indexOf(c) >= 0)) { - sb.append(c); - } else { - int cval = c; - - // More significant hex digit - int mshd = (cval >> 4); - if (mshd >= 10) { - mshd += 'A' - 10; - } else { - mshd += '0'; - } - sb.append('%'); - sb.append((char) mshd); - - // Less significant hex digit - int lshd = (cval & 0X0F); - if (lshd >= 10) { - lshd += 'A' - 10; - } else { - lshd += '0'; - } - sb.append((char) lshd); - change = true; - } - } - // For efficiency, return the original string - // if nothing changed. - if (change) { - return sb.toString(); - } - return uri; - } - - /** Appends a Rational value to a StringBuffer */ - public void rationalToString(StringBuffer buf, String tag, String margin, - Rational r) { - String margn2 = margin + " "; - - long numer = r.getNumerator(); - long denom = r.getDenominator(); - buf.append(margin + elementStart(tag) + EOL); - buf.append(margn2 + element("mix:numerator", Long.toString(numer)) - + EOL); - if (denom != 1L) { - buf.append(margn2 - + element("mix:denominator", Long.toString(denom)) + EOL); - } - buf.append(margin + elementEnd(tag) + EOL); - } + _writer.println(margn5 + element("aes:channelAssignment", chanAttrs)); + _writer.println(margn4 + elementEnd("aes:stream")); + } + _writer.println(margn3 + elementEnd("aes:region")); + _writer.println(margn2 + elementEnd("aes:face")); + } + + // In the general case, a FormatList can contain multiple + // FormatRegions. This doesn't happen with any of the current + // modules; if it's needed in the future, simply set up an + // iteration loop on formatList. + List flist = aes.getFormatList(); + if (!flist.isEmpty()) { + AESAudioMetadata.FormatRegion rgn = (AESAudioMetadata.FormatRegion) flist.get(0); + int bitDepth = rgn.getBitDepth(); + double sampleRate = rgn.getSampleRate(); + int wordSize = rgn.getWordSize(); + String[] bitRed = rgn.getBitrateReduction(); + // Build a FormatRegion subtree if at least one piece of data + // that goes into it is present. + if (bitDepth != AESAudioMetadata.NULL + || sampleRate != AESAudioMetadata.NILL + || wordSize != AESAudioMetadata.NULL) { + _writer.println(margn2 + elementStart("aes:formatList")); + String[][] frAttr = {{"ID", formatRegionID}}; + _writer.println(margn3 + elementStart("aes:formatRegion", frAttr)); + if (bitDepth != AESAudioMetadata.NULL) { + _writer.println(margn4 + element("aes:bitDepth", Integer.toString(bitDepth))); + } + if (sampleRate != AESAudioMetadata.NILL) { + _writer.println(margn4 + element("aes:sampleRate", formatters.get().format(sampleRate))); + } + if (wordSize != AESAudioMetadata.NULL) { + _writer.println(margn4 + element("aes:wordSize", Integer.toString(wordSize))); + } + if (bitRed != null) { + _writer.println(margn4 + elementStart("aes:bitrateReduction")); + _writer.println(margn5 + element("aes:codecName", bitRed[0])); + _writer.println(margn5 + element("aes:codecNameVersion", bitRed[1])); + _writer.println(margn5 + element("aes:codecCreatorApplication", bitRed[2])); + _writer.println(margn5 + element("aes:codecCreatorApplicationVersion", bitRed[3])); + _writer.println(margn5 + element("aes:codecQuality", bitRed[4])); + _writer.println(margn5 + element("aes:dataRate", bitRed[5])); + _writer.println(margn5 + element("aes:dataRateMode", bitRed[6])); + _writer.println(margn4 + elementEnd("aes:bitrateReduction")); + } + _writer.println(margn3 + elementEnd("aes:formatRegion")); + _writer.println(margn2 + elementEnd("aes:formatList")); + } + } + /* This should go somewhere, but where? */ + // int nchan = aes.getNumChannels (); + // if (nchan != AESAudioMetadata.NULL) { + // _writer.println (margn2 + element ("aes:numChannels", + // Integer.toString (nchan))); + // } + + _writer.println(margin + elementEnd("aes:audioObject")); + + _level -= 3; + } + + /* Break out the writing of a timeRangeType element. + * This always gives a start time of 0. This is all + * FAKE DATA for the moment. */ + private void writeAESTimeRange( + String baseIndent, AESAudioMetadata.TimeDesc start, AESAudioMetadata.TimeDesc duration) { + final String margn1 = baseIndent + " "; + final String margn2 = margn1 + " "; + final String[][] attrs = { + {"tcf:frameCount", "30"}, + {"tcf:timeBase", "1000"}, + {"tcf:videoField", "FIELD_1"}, + {"tcf:countingMode", "NTSC_NON_DROP_FRAME"} + }; + _writer.println(margn1 + elementStart("tcf:startTime", attrs)); + _writer.println(margn2 + element("tcf:hours", Long.toString(start.getHours()))); + _writer.println(margn2 + element("tcf:minutes", Long.toString(start.getMinutes()))); + _writer.println(margn2 + element("tcf:seconds", Long.toString(start.getSeconds()))); + _writer.println(margn2 + element("tcf:frames", Long.toString(start.getFrames()))); + _writer.println(margn1 + elementEnd("tcf:startTime")); + double sr = start.getSampleRate(); + if (sr == 1.0) { + sr = _sampleRate; + } + } + /* + * Clean up a URI string by escaping forbidden characters. We assume + * (perhaps dangerously) that a % is the start of an already escaped + * hexadecimal sequence. + */ + private String cleanURIString(String uri) { + StringBuffer sb = new StringBuffer(uri.length() * 2); + boolean change = false; + for (int i = 0; i < uri.length(); i++) { + char c = uri.charAt(i); + if ((c >= 'A' && c <= 'Z') + || (c >= 'a' && c <= 'z') + || (c >= '0' && c <= '9') + || (c == '%') + || // assume it's an + // escape + ("-_.!~*'();/?:@=+$,".indexOf(c) >= 0)) { + sb.append(c); + } else { + int cval = c; + + // More significant hex digit + int mshd = (cval >> 4); + if (mshd >= 10) { + mshd += 'A' - 10; + } else { + mshd += '0'; + } + sb.append('%'); + sb.append((char) mshd); + + // Less significant hex digit + int lshd = (cval & 0X0F); + if (lshd >= 10) { + lshd += 'A' - 10; + } else { + lshd += '0'; + } + sb.append((char) lshd); + change = true; + } + } + // For efficiency, return the original string + // if nothing changed. + if (change) { + return sb.toString(); + } + return uri; + } + + /** Appends a Rational value to a StringBuffer */ + public void rationalToString(StringBuffer buf, String tag, String margin, Rational r) { + String margn2 = margin + " "; + + long numer = r.getNumerator(); + long denom = r.getDenominator(); + buf.append(margin + elementStart(tag) + EOL); + buf.append(margn2 + element("mix:numerator", Long.toString(numer)) + EOL); + if (denom != 1L) { + buf.append(margn2 + element("mix:denominator", Long.toString(denom)) + EOL); + } + buf.append(margin + elementEnd(tag) + EOL); + } } diff --git a/jhove-core/src/main/java/edu/harvard/hul/ois/jhove/handler/audit/AuditCount.java b/jhove-core/src/main/java/edu/harvard/hul/ois/jhove/handler/audit/AuditCount.java index eaa339955..6c6808c3c 100644 --- a/jhove-core/src/main/java/edu/harvard/hul/ois/jhove/handler/audit/AuditCount.java +++ b/jhove-core/src/main/java/edu/harvard/hul/ois/jhove/handler/audit/AuditCount.java @@ -1,94 +1,86 @@ -/********************************************************************** - * Audit output handler +/** + * ******************************************************************** Audit output handler * Copyright 2004 by the President and Fellows of Harvard College * - * This program 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 2 of the License, or (at - * your option) any later version. + *

This program 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 2 + * of the License, or (at your option) any later version. * - * This program 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. + *

This program 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. * - * You should have received a copy of the GNU Lesser General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 - * USA - **********************************************************************/ - + *

You should have received a copy of the GNU Lesser General Public License along with this + * program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA ******************************************************************** + */ package edu.harvard.hul.ois.jhove.handler.audit; -/** - * Count object for the JHOVE Audit output handler. - */ +/** Count object for the JHOVE Audit output handler. */ public class AuditCount { - /** Number of valid files. */ - protected int _valid; - - /** Number of well-formed files. */ - protected int _wellFormed; - - /** Number of not-well-formed files. */ - protected int _notWellFormed; - - /** Number of undetermined files. */ - protected int _undetermined; - - /** - * Instantiates an AuditCount object. - */ - public AuditCount() { - _valid = 0; - _wellFormed = 0; - _notWellFormed = 0; - _undetermined = 0; - } - - /** Returns the total number of processed files. */ - public int getTotal() { - return _valid + _wellFormed + _notWellFormed + _undetermined; - } - - /** Returns the total number of valid files. */ - public int getValid() { - return _valid; - } - - /** Returns the total number of well-formed files. */ - public int getWellFormed() { - return _wellFormed; - } - - /** Returns the count of not-well-formed files. */ - public int getNotWellFormed() { - return _notWellFormed; - } - - /** Returns the count of undetermined files. */ - public int getUndetermined() { - return _undetermined; - } - - /** Sets the count of valid files. */ - public void setValid(int valid) { - _valid = valid; - } - - /** Sets the count of well-formed files. */ - public void setWellFormed(int wellFormed) { - _wellFormed = wellFormed; - } - - /** Sets the count of not-well-formed files. */ - public void setNotWellFormed(int notWellFormed) { - _notWellFormed = notWellFormed; - } - - /** Sets the count of undetermined files. */ - public void setUndetermined(int undetermined) { - _undetermined = undetermined; - } + /** Number of valid files. */ + protected int _valid; + + /** Number of well-formed files. */ + protected int _wellFormed; + + /** Number of not-well-formed files. */ + protected int _notWellFormed; + + /** Number of undetermined files. */ + protected int _undetermined; + + /** Instantiates an AuditCount object. */ + public AuditCount() { + _valid = 0; + _wellFormed = 0; + _notWellFormed = 0; + _undetermined = 0; + } + + /** Returns the total number of processed files. */ + public int getTotal() { + return _valid + _wellFormed + _notWellFormed + _undetermined; + } + + /** Returns the total number of valid files. */ + public int getValid() { + return _valid; + } + + /** Returns the total number of well-formed files. */ + public int getWellFormed() { + return _wellFormed; + } + + /** Returns the count of not-well-formed files. */ + public int getNotWellFormed() { + return _notWellFormed; + } + + /** Returns the count of undetermined files. */ + public int getUndetermined() { + return _undetermined; + } + + /** Sets the count of valid files. */ + public void setValid(int valid) { + _valid = valid; + } + + /** Sets the count of well-formed files. */ + public void setWellFormed(int wellFormed) { + _wellFormed = wellFormed; + } + + /** Sets the count of not-well-formed files. */ + public void setNotWellFormed(int notWellFormed) { + _notWellFormed = notWellFormed; + } + + /** Sets the count of undetermined files. */ + public void setUndetermined(int undetermined) { + _undetermined = undetermined; + } } diff --git a/jhove-core/src/main/java/edu/harvard/hul/ois/jhove/handler/audit/AuditState.java b/jhove-core/src/main/java/edu/harvard/hul/ois/jhove/handler/audit/AuditState.java index 5eeb32b95..70e3b1a73 100644 --- a/jhove-core/src/main/java/edu/harvard/hul/ois/jhove/handler/audit/AuditState.java +++ b/jhove-core/src/main/java/edu/harvard/hul/ois/jhove/handler/audit/AuditState.java @@ -1,83 +1,70 @@ -/********************************************************************** - * Audit output handler +/** + * ******************************************************************** Audit output handler * Copyright 2004 by the President and Fellows of Harvard College * - * This program 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 2 of the License, or (at - * your option) any later version. + *

This program 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 2 + * of the License, or (at your option) any later version. * - * This program 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. + *

This program 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. * - * You should have received a copy of the GNU Lesser General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 - * USA - **********************************************************************/ - + *

You should have received a copy of the GNU Lesser General Public License along with this + * program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA ******************************************************************** + */ package edu.harvard.hul.ois.jhove.handler.audit; import java.io.File; -/** - * State object for the JHOVE Audit output handler. - */ +/** State object for the JHOVE Audit output handler. */ public class AuditState extends AuditCount implements Cloneable { - /** Directory pathname. */ - protected String _directory; + /** Directory pathname. */ + protected String _directory; - /** - * Instantiates an AuditState object. - */ - public AuditState(String directory) { - super(); - init(directory); - } + /** Instantiates an AuditState object. */ + public AuditState(String directory) { + super(); + init(directory); + } - /** - * Initializes to a specified directory and clears counters. - */ - protected void init(String directory) { - try { - File file = new File(directory); - _directory = file.getCanonicalPath(); - } catch (Exception e) { - _directory = directory; - } + /** Initializes to a specified directory and clears counters. */ + protected void init(String directory) { + try { + File file = new File(directory); + _directory = file.getCanonicalPath(); + } catch (Exception e) { + _directory = directory; + } - _valid = 0; - _wellFormed = 0; - _notWellFormed = 0; - _undetermined = 0; - } + _valid = 0; + _wellFormed = 0; + _notWellFormed = 0; + _undetermined = 0; + } - /** - * Creates and returns a copy of this object. - */ - public Object clone(String directory) - throws CloneNotSupportedException { - AuditState state = (AuditState) super.clone(); - state.init(directory); + /** Creates and returns a copy of this object. */ + public Object clone(String directory) throws CloneNotSupportedException { + AuditState state = (AuditState) super.clone(); + state.init(directory); - return state; - } + return state; + } - /** Returns the directory path. */ - public String getDirectory() { - return _directory; - } + /** Returns the directory path. */ + public String getDirectory() { + return _directory; + } - /** Sets the directory path. */ - public void setDirectory(String directory) { - try { - File file = new File(directory); - _directory = file.getCanonicalPath(); - } catch (Exception e) { - _directory = directory; - } - } + /** Sets the directory path. */ + public void setDirectory(String directory) { + try { + File file = new File(directory); + _directory = file.getCanonicalPath(); + } catch (Exception e) { + _directory = directory; + } + } } diff --git a/jhove-core/src/main/java/edu/harvard/hul/ois/jhove/handler/audit/package-info.java b/jhove-core/src/main/java/edu/harvard/hul/ois/jhove/handler/audit/package-info.java index dd29b345f..d5709840c 100644 --- a/jhove-core/src/main/java/edu/harvard/hul/ois/jhove/handler/audit/package-info.java +++ b/jhove-core/src/main/java/edu/harvard/hul/ois/jhove/handler/audit/package-info.java @@ -1,4 +1,2 @@ -/** - * Contains the supporting classes for the JHOVE audit output handler. - */ -package edu.harvard.hul.ois.jhove.handler.audit; \ No newline at end of file +/** Contains the supporting classes for the JHOVE audit output handler. */ +package edu.harvard.hul.ois.jhove.handler.audit; diff --git a/jhove-core/src/main/java/edu/harvard/hul/ois/jhove/handler/package-info.java b/jhove-core/src/main/java/edu/harvard/hul/ois/jhove/handler/package-info.java index 92c03a1f9..c61d0b1cb 100644 --- a/jhove-core/src/main/java/edu/harvard/hul/ois/jhove/handler/package-info.java +++ b/jhove-core/src/main/java/edu/harvard/hul/ois/jhove/handler/package-info.java @@ -1,5 +1,5 @@ /** - * Contains the main classes for JHOVE output handlers. - * All module classes are subclasses of HandlerBase. + * Contains the main classes for JHOVE output handlers. All module classes are subclasses of + * HandlerBase. */ -package edu.harvard.hul.ois.jhove.handler; \ No newline at end of file +package edu.harvard.hul.ois.jhove.handler; diff --git a/jhove-core/src/main/java/edu/harvard/hul/ois/jhove/messages/JhoveMessage.java b/jhove-core/src/main/java/edu/harvard/hul/ois/jhove/messages/JhoveMessage.java index 3e9ebfd8c..6f7c0e39a 100644 --- a/jhove-core/src/main/java/edu/harvard/hul/ois/jhove/messages/JhoveMessage.java +++ b/jhove-core/src/main/java/edu/harvard/hul/ois/jhove/messages/JhoveMessage.java @@ -1,41 +1,38 @@ package edu.harvard.hul.ois.jhove.messages; /** - * Interface that defines behaviour of JhoveMessages. - * These messages have a unique string identifier as well as - * the previous message and sub-message strings. - * - * @author Carl Wilson - * carlwilson AT github + * Interface that defines behaviour of JhoveMessages. These messages have a unique string identifier + * as well as the previous message and sub-message strings. + * + * @author Carl Wilson carlwilson AT github */ - public interface JhoveMessage { - /** - * Get the unique, persistent message identifier. - * - * @return the String message id. - */ - public String getId(); - - /** - * Get the main message - * - * @return the String message - */ - public String getMessage(); + /** + * Get the unique, persistent message identifier. + * + * @return the String message id. + */ + public String getId(); - /** - * Test whether the message has a sub-message - * - * @return true if the message has a sub-message - */ - public boolean hasSubMessage(); + /** + * Get the main message + * + * @return the String message + */ + public String getMessage(); - /** - * Get the sub-message - * - * @return the String sub-message - */ - public String getSubMessage(); + /** + * Test whether the message has a sub-message + * + * @return true if the message has a sub-message + */ + public boolean hasSubMessage(); + /** + * Get the sub-message + * + * @return the String sub-message + */ + public String getSubMessage(); } diff --git a/jhove-core/src/main/java/edu/harvard/hul/ois/jhove/messages/JhoveMessageFactImpl.java b/jhove-core/src/main/java/edu/harvard/hul/ois/jhove/messages/JhoveMessageFactImpl.java index 7d724dea0..0a1eda1d1 100644 --- a/jhove-core/src/main/java/edu/harvard/hul/ois/jhove/messages/JhoveMessageFactImpl.java +++ b/jhove-core/src/main/java/edu/harvard/hul/ois/jhove/messages/JhoveMessageFactImpl.java @@ -3,31 +3,27 @@ import java.util.ResourceBundle; /** - * @author Carl Wilson - * carlwilson AT github - * + * @author Carl Wilson carlwilson AT github * @version 0.1 - * - * Created 1 Mar 2019:16:52:25 + *

Created 1 Mar 2019:16:52:25 */ - final class JhoveMessageFactImpl implements JhoveMessageFactory { - private final ResourceBundle messageBundle; + private final ResourceBundle messageBundle; - private JhoveMessageFactImpl(final ResourceBundle messageBundle) { - this.messageBundle = messageBundle; - } + private JhoveMessageFactImpl(final ResourceBundle messageBundle) { + this.messageBundle = messageBundle; + } - @Override - public JhoveMessage getMessage(final String id) { - String message = this.messageBundle.getString(id); - return JhoveMessages.getMessageInstance(id, message); - } + @Override + public JhoveMessage getMessage(final String id) { + String message = this.messageBundle.getString(id); + return JhoveMessages.getMessageInstance(id, message); + } - static JhoveMessageFactImpl getInstance( - final ResourceBundle messageBundle) throws IllegalArgumentException { - if (messageBundle == null) - throw new IllegalArgumentException("messageBundle cannot be null"); - return new JhoveMessageFactImpl(messageBundle); - } + static JhoveMessageFactImpl getInstance(final ResourceBundle messageBundle) + throws IllegalArgumentException { + if (messageBundle == null) throw new IllegalArgumentException("messageBundle cannot be null"); + return new JhoveMessageFactImpl(messageBundle); + } } diff --git a/jhove-core/src/main/java/edu/harvard/hul/ois/jhove/messages/JhoveMessageFactory.java b/jhove-core/src/main/java/edu/harvard/hul/ois/jhove/messages/JhoveMessageFactory.java index 2781c60ad..a50c9cc2f 100644 --- a/jhove-core/src/main/java/edu/harvard/hul/ois/jhove/messages/JhoveMessageFactory.java +++ b/jhove-core/src/main/java/edu/harvard/hul/ois/jhove/messages/JhoveMessageFactory.java @@ -4,22 +4,18 @@ /** * Factory interface for JhoveMessage creation - * - * @author Carl Wilson - * carlwilson AT github + * + * @author Carl Wilson carlwilson AT github */ - public interface JhoveMessageFactory { - /** - * Retrieve JhoveMessage by unique persistent id - * - * @param id - * the id of the message to be retrieved - * @return the message with persistent id equal to id - * @throws NoSuchElementException - * if no message with id can be retrieved - */ - public JhoveMessage getMessage(final String id) - throws NoSuchElementException; + /** + * Retrieve JhoveMessage by unique persistent id + * + * @param id the id of the message to be retrieved + * @return the message with persistent id equal to id + * @throws NoSuchElementException if no message with id can be retrieved + */ + public JhoveMessage getMessage(final String id) throws NoSuchElementException; } diff --git a/jhove-core/src/main/java/edu/harvard/hul/ois/jhove/messages/JhoveMessageImpl.java b/jhove-core/src/main/java/edu/harvard/hul/ois/jhove/messages/JhoveMessageImpl.java index e2e3e1786..095a8a767 100644 --- a/jhove-core/src/main/java/edu/harvard/hul/ois/jhove/messages/JhoveMessageImpl.java +++ b/jhove-core/src/main/java/edu/harvard/hul/ois/jhove/messages/JhoveMessageImpl.java @@ -1,109 +1,103 @@ package edu.harvard.hul.ois.jhove.messages; /** - * @author Carl Wilson - * carlwilson AT github - * + * @author Carl Wilson carlwilson AT github * @version 0.1 - * - * Created 20 Feb 2019:16:46:19 + *

Created 20 Feb 2019:16:46:19 */ - final class JhoveMessageImpl implements JhoveMessage { - private final String id; - private final String message; - private final String subMessage; + private final String id; + private final String message; + private final String subMessage; - private JhoveMessageImpl(final String id, final String message, final String subMessage) { - this.id = id; - this.message = message; - this.subMessage = subMessage; - } + private JhoveMessageImpl(final String id, final String message, final String subMessage) { + this.id = id; + this.message = message; + this.subMessage = subMessage; + } - static JhoveMessage getInstance(final String id, final String message, final String subMessage) { - return new JhoveMessageImpl(id, message, subMessage); - } + static JhoveMessage getInstance(final String id, final String message, final String subMessage) { + return new JhoveMessageImpl(id, message, subMessage); + } - @Override - public String getId() { - return this.id; - } + @Override + public String getId() { + return this.id; + } - @Override - public String getMessage() { - return this.message; - } + @Override + public String getMessage() { + return this.message; + } - @Override - public boolean hasSubMessage() { - return (this.subMessage == null || this.subMessage.isEmpty()); - } + @Override + public boolean hasSubMessage() { + return (this.subMessage == null || this.subMessage.isEmpty()); + } - @Override - public String getSubMessage() { - return this.subMessage; - } + @Override + public String getSubMessage() { + return this.subMessage; + } - /** - * @see java.lang.Object#toString() - */ - @Override - public String toString() { - return "JhoveMessageImpl [id=" + this.id + ", message=" + this.message - + ", subMessage=" + this.subMessage + "]"; - } + /** @see java.lang.Object#toString() */ + @Override + public String toString() { + return "JhoveMessageImpl [id=" + + this.id + + ", message=" + + this.message + + ", subMessage=" + + this.subMessage + + "]"; + } - /** - * @see java.lang.Object#hashCode() - */ - @Override - public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result + ((this.id == null) ? 0 : this.id.hashCode()); - result = prime * result - + ((this.message == null) ? 0 : this.message.hashCode()); - result = prime * result - + ((this.subMessage == null) ? 0 : this.subMessage.hashCode()); - return result; - } + /** @see java.lang.Object#hashCode() */ + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((this.id == null) ? 0 : this.id.hashCode()); + result = prime * result + ((this.message == null) ? 0 : this.message.hashCode()); + result = prime * result + ((this.subMessage == null) ? 0 : this.subMessage.hashCode()); + return result; + } - /** - * @see java.lang.Object#equals(java.lang.Object) - */ - @Override - public boolean equals(Object obj) { - if (this == obj) { - return true; - } - if (obj == null) { - return false; - } - if (!(obj instanceof JhoveMessageImpl)) { - return false; - } - JhoveMessageImpl other = (JhoveMessageImpl) obj; - if (this.id == null) { - if (other.id != null) { - return false; - } - } else if (!this.id.equals(other.id)) { - return false; - } - if (this.message == null) { - if (other.message != null) { - return false; - } - } else if (!this.message.equals(other.message)) { - return false; - } - if (this.subMessage == null) { - if (other.subMessage != null) { - return false; - } - } else if (!this.subMessage.equals(other.subMessage)) { - return false; - } - return true; - } + /** @see java.lang.Object#equals(java.lang.Object) */ + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (!(obj instanceof JhoveMessageImpl)) { + return false; + } + JhoveMessageImpl other = (JhoveMessageImpl) obj; + if (this.id == null) { + if (other.id != null) { + return false; + } + } else if (!this.id.equals(other.id)) { + return false; + } + if (this.message == null) { + if (other.message != null) { + return false; + } + } else if (!this.message.equals(other.message)) { + return false; + } + if (this.subMessage == null) { + if (other.subMessage != null) { + return false; + } + } else if (!this.subMessage.equals(other.subMessage)) { + return false; + } + return true; + } } diff --git a/jhove-core/src/main/java/edu/harvard/hul/ois/jhove/messages/JhoveMessages.java b/jhove-core/src/main/java/edu/harvard/hul/ois/jhove/messages/JhoveMessages.java index 0e9f03bd5..cc9338166 100644 --- a/jhove-core/src/main/java/edu/harvard/hul/ois/jhove/messages/JhoveMessages.java +++ b/jhove-core/src/main/java/edu/harvard/hul/ois/jhove/messages/JhoveMessages.java @@ -5,117 +5,90 @@ /** * Utility class that handles creation of Message type instances, etc. - * - * @author Carl Wilson - * carlwilson AT github + * + * @author Carl Wilson carlwilson AT github */ - public enum JhoveMessages { - INSTANCE; + INSTANCE; - public static final String NO_ID = "NO-ID"; - public static final String EMPTY_MESSAGE = ""; - public static final JhoveMessage DEFAULT_MESSAGE = getMessageInstance(NO_ID, - EMPTY_MESSAGE); + public static final String NO_ID = "NO-ID"; + public static final String EMPTY_MESSAGE = ""; + public static final JhoveMessage DEFAULT_MESSAGE = getMessageInstance(NO_ID, EMPTY_MESSAGE); - /** - * Create a new message instance with a DEFAULT_ID - * - * @param message - * the message of the new message - * @return the new message instance - * @throws IllegalArgumentException - * if the id or message is null or empty - */ - public static JhoveMessage getMessageInstance(final String message) - throws IllegalArgumentException { - return getMessageInstance(NO_ID, message); - } + /** + * Create a new message instance with a DEFAULT_ID + * + * @param message the message of the new message + * @return the new message instance + * @throws IllegalArgumentException if the id or message is null or empty + */ + public static JhoveMessage getMessageInstance(final String message) + throws IllegalArgumentException { + return getMessageInstance(NO_ID, message); + } - /** - * Create a JhoveMessage instance with the give id and message value - * - * @param id - * the id of the new message - * @param message - * the message of the new message - * @return the new message instance - * @throws IllegalArgumentException - * if the id or message is null or empty - */ - public static JhoveMessage getMessageInstance(final String id, - final String message) throws IllegalArgumentException { - return getMessageInstance(id, message, EMPTY_MESSAGE); - } + /** + * Create a JhoveMessage instance with the give id and message value + * + * @param id the id of the new message + * @param message the message of the new message + * @return the new message instance + * @throws IllegalArgumentException if the id or message is null or empty + */ + public static JhoveMessage getMessageInstance(final String id, final String message) + throws IllegalArgumentException { + return getMessageInstance(id, message, EMPTY_MESSAGE); + } - /** - * Create a new JhoveMessage instance with the given id, message and - * sub-message - * - * @param id - * the id of the new message - * @param message - * the message of the new message - * @param subMessage - * the sub-message of the new message - * @return the new message instance - * @throws IllegalArgumentException - * if the id or message is null or empty - */ - public static JhoveMessage getMessageInstance(final String id, - final String message, final String subMessage) - throws IllegalArgumentException { - if (id == null || id.isEmpty()) - throw new IllegalArgumentException( - "id cannot be null or an empty string."); - if (message == null) - throw new IllegalArgumentException( - "message cannot be null."); - return JhoveMessageImpl.getInstance(id, message, subMessage); - } + /** + * Create a new JhoveMessage instance with the given id, message and sub-message + * + * @param id the id of the new message + * @param message the message of the new message + * @param subMessage the sub-message of the new message + * @return the new message instance + * @throws IllegalArgumentException if the id or message is null or empty + */ + public static JhoveMessage getMessageInstance( + final String id, final String message, final String subMessage) + throws IllegalArgumentException { + if (id == null || id.isEmpty()) + throw new IllegalArgumentException("id cannot be null or an empty string."); + if (message == null) throw new IllegalArgumentException("message cannot be null."); + return JhoveMessageImpl.getInstance(id, message, subMessage); + } - /** - * Get a JhoveMessageFactory instance with the requested property based - * bundle name and the default user locale - * - * @param bundleName - * the fully qualified resource path for the message bundle - * property file - * @return a new JhoveMessageFactory instance backed by the bundle property - * file. - * @throws IllegalArgumentException - * if the bundle name is empty or the message bundle can't be - * located - */ - public static JhoveMessageFactory getInstance(final String bundleName) - throws IllegalArgumentException { - return getInstance(bundleName, Locale.getDefault()); - } + /** + * Get a JhoveMessageFactory instance with the requested property based bundle name and the + * default user locale + * + * @param bundleName the fully qualified resource path for the message bundle property file + * @return a new JhoveMessageFactory instance backed by the bundle property file. + * @throws IllegalArgumentException if the bundle name is empty or the message bundle can't be + * located + */ + public static JhoveMessageFactory getInstance(final String bundleName) + throws IllegalArgumentException { + return getInstance(bundleName, Locale.getDefault()); + } - /** - * Get a JhoveMessageFactory instance with the requested property based - * bundle name and a specific locale - * - * @param bundleName - * the fully qualified resource path for the message bundle - * property file - * @param locale - * the locale for the message bundle - * @return a new JhoveMessageFactory instance backed by the bundle property - * file. - * @throws IllegalArgumentException - * if the bundle name is empty or the message bundle can't be - * located - */ - public static JhoveMessageFactory getInstance(final String bundleName, - final Locale locale) throws IllegalArgumentException { - if (bundleName == null || bundleName.isEmpty()) - throw new IllegalArgumentException( - "bundleName cannot be null or empty"); - if (locale == null) - throw new IllegalArgumentException("locale cannot be null."); - ResourceBundle messageBundle = ResourceBundle.getBundle(bundleName, - locale); - return JhoveMessageFactImpl.getInstance(messageBundle); - } + /** + * Get a JhoveMessageFactory instance with the requested property based bundle name and a specific + * locale + * + * @param bundleName the fully qualified resource path for the message bundle property file + * @param locale the locale for the message bundle + * @return a new JhoveMessageFactory instance backed by the bundle property file. + * @throws IllegalArgumentException if the bundle name is empty or the message bundle can't be + * located + */ + public static JhoveMessageFactory getInstance(final String bundleName, final Locale locale) + throws IllegalArgumentException { + if (bundleName == null || bundleName.isEmpty()) + throw new IllegalArgumentException("bundleName cannot be null or empty"); + if (locale == null) throw new IllegalArgumentException("locale cannot be null."); + ResourceBundle messageBundle = ResourceBundle.getBundle(bundleName, locale); + return JhoveMessageFactImpl.getInstance(messageBundle); + } } diff --git a/jhove-core/src/main/java/edu/harvard/hul/ois/jhove/module/BytestreamModule.java b/jhove-core/src/main/java/edu/harvard/hul/ois/jhove/module/BytestreamModule.java index b49b4229c..12dcd344b 100644 --- a/jhove-core/src/main/java/edu/harvard/hul/ois/jhove/module/BytestreamModule.java +++ b/jhove-core/src/main/java/edu/harvard/hul/ois/jhove/module/BytestreamModule.java @@ -1,23 +1,20 @@ -/********************************************************************** - * Jhove - JSTOR/Harvard Object Validation Environment - * Copyright 2003-2007 by JSTOR and the President and Fellows of Harvard College +/** + * ******************************************************************** Jhove - JSTOR/Harvard Object + * Validation Environment Copyright 2003-2007 by JSTOR and the President and Fellows of Harvard + * College * - * This program 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 2 of the License, or (at - * your option) any later version. - * - * This program 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. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 - * USA - **********************************************************************/ - + *

This program 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 2 + * of the License, or (at your option) any later version. + * + *

This program 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. + * + *

You should have received a copy of the GNU Lesser General Public License along with this + * program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA ******************************************************************** + */ package edu.harvard.hul.ois.jhove.module; import edu.harvard.hul.ois.jhove.*; @@ -25,105 +22,107 @@ import java.util.logging.Level; /** - * Module for analysis of content as a byte stream. - * This is the module of last resort, accepting any content as - * valid and well-formed. + * Module for analysis of content as a byte stream. This is the module of last resort, accepting any + * content as valid and well-formed. */ -public final class BytestreamModule - extends ModuleBase -{ +public final class BytestreamModule extends ModuleBase { - /****************************************************************** - * PRIVATE CLASS FIELDS. - ******************************************************************/ + /** + * **************************************************************** PRIVATE CLASS FIELDS. + * **************************************************************** + */ + private static final String NAME = "BYTESTREAM"; - private static final String NAME = "BYTESTREAM"; - private static final String RELEASE = "1.4"; - private static final int [] DATE = {2018, 10, 1}; - private static final String [] FORMAT = {"bytestream"}; - private static final String COVERAGE = null; - private static final String [] MIMETYPE = {"application/octet-stream"}; - private static final String WELLFORMED = "All bytestreams are well-formed"; - private static final String VALIDITY = null; - private static final String REPINFO = null; - private static final String NOTE = "This is the default format"; - private static final String RIGHTS = "Copyright 2003-2007 by JSTOR and " + - "the President and Fellows of Harvard College. " + - "Released under the GNU Lesser General Public License."; + private static final String RELEASE = "1.4"; + private static final int[] DATE = {2018, 10, 1}; + private static final String[] FORMAT = {"bytestream"}; + private static final String COVERAGE = null; + private static final String[] MIMETYPE = {"application/octet-stream"}; + private static final String WELLFORMED = "All bytestreams are well-formed"; + private static final String VALIDITY = null; + private static final String REPINFO = null; + private static final String NOTE = "This is the default format"; + private static final String RIGHTS = + "Copyright 2003-2007 by JSTOR and " + + "the President and Fellows of Harvard College. " + + "Released under the GNU Lesser General Public License."; - /****************************************************************** - * CLASS CONSTRUCTOR. - ******************************************************************/ + /** + * **************************************************************** CLASS CONSTRUCTOR. + * **************************************************************** + */ - /** - * Creates a BytestreamModule. - */ - public BytestreamModule () - { - super (NAME, RELEASE, DATE, FORMAT, COVERAGE, MIMETYPE, WELLFORMED, - VALIDITY, REPINFO, NOTE, RIGHTS, false); + /** Creates a BytestreamModule. */ + public BytestreamModule() { + super( + NAME, + RELEASE, + DATE, + FORMAT, + COVERAGE, + MIMETYPE, + WELLFORMED, + VALIDITY, + REPINFO, + NOTE, + RIGHTS, + false); - this._vendor = Agent.harvardInstance(); - } + this._vendor = Agent.harvardInstance(); + } - /****************************************************************** - * PUBLIC INSTANCE METHODS. - * - * Parsing methods. - ******************************************************************/ + /** + * **************************************************************** PUBLIC INSTANCE METHODS. + * + *

Parsing methods. **************************************************************** + */ - /** - * Parse the content of a stream digital object and store the - * results in RepInfo. - * Any arbitrary bytestream is considered well-formed. - */ - @Override - public final int parse (InputStream stream, RepInfo info, int parseIndex) - throws IOException - { - initParse (); - initInfo(info); + /** + * Parse the content of a stream digital object and store the results in RepInfo. Any arbitrary + * bytestream is considered well-formed. + */ + @Override + public final int parse(InputStream stream, RepInfo info, int parseIndex) throws IOException { + initParse(); + initInfo(info); - // Setup the data stream, will determine if we use checksum stream - setupDataStream(stream, info); + // Setup the data stream, will determine if we use checksum stream + setupDataStream(stream, info); - boolean eof = false; - this._nByte = 0; - byte[] byteBuf = new byte[4096]; - while (!eof) { - try { - // All the calculations are done down in ChecksumInputStream - int n = readByteBuf (this._dstream, byteBuf, this); - if (n <= 0) { - break; - } - } - catch (EOFException e) { - _logger.log(Level.FINEST, "End of file exception when parsing.", e); - eof = true; - } - } - info.setSize (this._nByte); - if (this._nByte == 0) { - info.setMessage (new InfoMessage (CoreMessageConstants.INF_FILE_EMPTY)); + boolean eof = false; + this._nByte = 0; + byte[] byteBuf = new byte[4096]; + while (!eof) { + try { + // All the calculations are done down in ChecksumInputStream + int n = readByteBuf(this._dstream, byteBuf, this); + if (n <= 0) { + break; } - // Set the checksums in the report if they're calculated - setChecksums(this._ckSummer, info); - return 0; + } catch (EOFException e) { + _logger.log(Level.FINEST, "End of file exception when parsing.", e); + eof = true; + } } - - /** - * Check signature. Bytestreams have no signatures, but since any - * byte stream is considered a valid Bytestream, return immediately - * doing nothing. The RepInfo._consistent flag will remain true. - */ - @Override - public void checkSignatures (File file, InputStream stream, - RepInfo info) - { - info.setFormat (this._format[0]); - info.setMimeType (this._mimeType[0]); - info.setModule (this); - info.setSigMatch(this._name); + info.setSize(this._nByte); + if (this._nByte == 0) { + info.setMessage(new InfoMessage(CoreMessageConstants.INF_FILE_EMPTY)); } + // Set the checksums in the report if they're calculated + setChecksums(this._ckSummer, info); + return 0; + } + + /** + * Check signature. Bytestreams have no signatures, but since any byte stream is considered a + * valid Bytestream, return immediately doing nothing. The RepInfo._consistent flag will remain + * true. + */ + @Override + public void checkSignatures(File file, InputStream stream, RepInfo info) { + info.setFormat(this._format[0]); + info.setMimeType(this._mimeType[0]); + info.setModule(this); + info.setSigMatch(this._name); + } } diff --git a/jhove-core/src/main/java/edu/harvard/hul/ois/jhove/module/package-info.java b/jhove-core/src/main/java/edu/harvard/hul/ois/jhove/module/package-info.java index 36d90b950..9b492dd94 100644 --- a/jhove-core/src/main/java/edu/harvard/hul/ois/jhove/module/package-info.java +++ b/jhove-core/src/main/java/edu/harvard/hul/ois/jhove/module/package-info.java @@ -1,8 +1,7 @@ /** - * Contains the main classes for JHOVE modules. - * All module classes are subclasses of ModuleBase. - *
- * Additional classes for some modules are found in subpackages of - * edu.harvard.hul.ois.jhove.module + * Contains the main classes for JHOVE modules. All module classes are subclasses of + * ModuleBase.
+ * Additional classes for some modules are found in subpackages of + * edu.harvard.hul.ois.jhove.module */ -package edu.harvard.hul.ois.jhove.module; \ No newline at end of file +package edu.harvard.hul.ois.jhove.module; diff --git a/jhove-core/src/main/java/edu/harvard/hul/ois/jhove/package-info.java b/jhove-core/src/main/java/edu/harvard/hul/ois/jhove/package-info.java index 14957c67e..5214a464e 100644 --- a/jhove-core/src/main/java/edu/harvard/hul/ois/jhove/package-info.java +++ b/jhove-core/src/main/java/edu/harvard/hul/ois/jhove/package-info.java @@ -1,13 +1,12 @@ /** - * Contains the classes needed for building a JHOVE application. - * This package must be used with a top-level class, with one or more - * output handlers or viewers, and with one or more modules for specific - * file formats. - *

- * For overviews, tutorials, examples, guides, and tool documentation, - * please see: + * Contains the classes needed for building a JHOVE application. This package must be used with a + * top-level class, with one or more output handlers or viewers, and with one or more modules for + * specific file formats. + * + *

For overviews, tutorials, examples, guides, and tool documentation, please see: + * *

*/ -package edu.harvard.hul.ois.jhove; \ No newline at end of file +package edu.harvard.hul.ois.jhove; diff --git a/jhove-core/src/main/java/org/openpreservation/jhove/ReleaseDetails.java b/jhove-core/src/main/java/org/openpreservation/jhove/ReleaseDetails.java index 5225d842e..fe2b57b6f 100644 --- a/jhove-core/src/main/java/org/openpreservation/jhove/ReleaseDetails.java +++ b/jhove-core/src/main/java/org/openpreservation/jhove/ReleaseDetails.java @@ -1,8 +1,7 @@ -/** - * - */ +/** */ package org.openpreservation.jhove; +import edu.harvard.hul.ois.jhove.CoreMessageConstants; import java.io.IOException; import java.io.InputStream; import java.text.ParseException; @@ -10,151 +9,122 @@ import java.util.Date; import java.util.Properties; -import edu.harvard.hul.ois.jhove.CoreMessageConstants; - /** - * Immutable class that reads a properties file containing JHOVE release - * details. The version number, build date and format are kept up to date by - * Maven, which filters the properties as part of the build process. - * - * - * @author Carl Wilson + * Immutable class that reads a properties file containing JHOVE release details. The version + * number, build date and format are kept up to date by Maven, which filters the properties as part + * of the build process. * + * @author Carl Wilson */ public final class ReleaseDetails { - private static final String APPLICATION_PROPERTIES_PATH = "org/openpreservation/jhove/jhove.properties"; - private static final String RIGHTS = "Derived from software Copyright 2004-2011 " - + "by the President and Fellows of Harvard College. " - + "Version 1.7 to 1.11 independently released. " - + "Version 1.12 onwards released by Open Preservation Foundation. " - + "Released under the GNU Lesser General Public License."; - - private static final ReleaseDetails INSTANCE = fromPropertyResource(APPLICATION_PROPERTIES_PATH); - - private final String version; - private final Date buildDate; - - private ReleaseDetails() { - throw new AssertionError(CoreMessageConstants.EXC_PRV_CNSTRCT + this.getClass().getName()); - } - - private ReleaseDetails(final String version, final Date buildDate) { - this.version = version; - this.buildDate = new Date(buildDate.getTime()); + private static final String APPLICATION_PROPERTIES_PATH = + "org/openpreservation/jhove/jhove.properties"; + private static final String RIGHTS = + "Derived from software Copyright 2004-2011 " + + "by the President and Fellows of Harvard College. " + + "Version 1.7 to 1.11 independently released. " + + "Version 1.12 onwards released by Open Preservation Foundation. " + + "Released under the GNU Lesser General Public License."; + + private static final ReleaseDetails INSTANCE = fromPropertyResource(APPLICATION_PROPERTIES_PATH); + + private final String version; + private final Date buildDate; + + private ReleaseDetails() { + throw new AssertionError(CoreMessageConstants.EXC_PRV_CNSTRCT + this.getClass().getName()); + } + + private ReleaseDetails(final String version, final Date buildDate) { + this.version = version; + this.buildDate = new Date(buildDate.getTime()); + } + + /** @return the JHOVE software version number */ + public String getVersion() { + return this.version; + } + + /** @return the JHOVE software build date */ + public Date getBuildDate() { + return new Date(this.buildDate.getTime()); + } + + /** @return the JHOVE software rights statement */ + @SuppressWarnings("static-method") + public String getRights() { + return RIGHTS; + } + + /** { @inheritDoc } */ + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((this.buildDate == null) ? 0 : this.buildDate.hashCode()); + result = prime * result + ((this.version == null) ? 0 : this.version.hashCode()); + return result; + } + + /** { @inheritDoc } */ + @Override + public boolean equals(Object obj) { + if (this == obj) return true; + if (obj == null) return false; + if (getClass() != obj.getClass()) return false; + ReleaseDetails other = (ReleaseDetails) obj; + if (this.buildDate == null) { + if (other.buildDate != null) return false; + } else if (!this.buildDate.equals(other.buildDate)) return false; + if (this.version == null) { + if (other.version != null) return false; + } else if (!this.version.equals(other.version)) return false; + return true; + } + + /** { @inheritDoc } */ + @Override + public String toString() { + return "ReleaseDetails [version=" + this.version + ", buildDate=" + this.buildDate + "]"; + } + + /** @return the static immutable ReleaseDetails instance */ + public static ReleaseDetails getInstance() { + return INSTANCE; + } + + private static ReleaseDetails fromPropertyResource(final String propertyResourceName) { + Properties props = new Properties(); + try (InputStream is = + ReleaseDetails.class.getClassLoader().getResourceAsStream(propertyResourceName)) { + if (is == null) { + throw new IllegalStateException( + CoreMessageConstants.ERR_APP_PROP_MISS + propertyResourceName); + } + try { + props.load(is); + } catch (IOException e) { + is.close(); + throw new IllegalStateException( + CoreMessageConstants.ERR_APP_PROP_MISS + propertyResourceName, e); + } + is.close(); + } catch (IOException e) { + // Problem closing, ignore and move on } - - /** - * @return the JHOVE software version number - */ - public String getVersion() { - return this.version; - } - - /** - * @return the JHOVE software build date - */ - public Date getBuildDate() { - return new Date(this.buildDate.getTime()); - } - - /** - * @return the JHOVE software rights statement - */ - @SuppressWarnings("static-method") - public String getRights() { - return RIGHTS; - } - - /** - * { @inheritDoc } - */ - @Override - public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result - + ((this.buildDate == null) ? 0 : this.buildDate.hashCode()); - result = prime * result + ((this.version == null) ? 0 : this.version.hashCode()); - return result; - } - - /** - * { @inheritDoc } - */ - @Override - public boolean equals(Object obj) { - if (this == obj) - return true; - if (obj == null) - return false; - if (getClass() != obj.getClass()) - return false; - ReleaseDetails other = (ReleaseDetails) obj; - if (this.buildDate == null) { - if (other.buildDate != null) - return false; - } else if (!this.buildDate.equals(other.buildDate)) - return false; - if (this.version == null) { - if (other.version != null) - return false; - } else if (!this.version.equals(other.version)) - return false; - return true; - } - - /** - * { @inheritDoc } - */ - @Override - public String toString() { - return "ReleaseDetails [version=" + this.version + ", buildDate=" - + this.buildDate + "]"; - } - - /** - * @return the static immutable ReleaseDetails instance - */ - public static ReleaseDetails getInstance() { - return INSTANCE; - } - - private static ReleaseDetails fromPropertyResource( - final String propertyResourceName) { - Properties props = new Properties(); - try (InputStream is = ReleaseDetails.class.getClassLoader() - .getResourceAsStream(propertyResourceName)) { - if (is == null) { - throw new IllegalStateException(CoreMessageConstants.ERR_APP_PROP_MISS - + propertyResourceName); - } - try { - props.load(is); - } catch (IOException e) { - is.close(); - throw new IllegalStateException( - CoreMessageConstants.ERR_APP_PROP_MISS - + propertyResourceName, e); - } - is.close(); - } catch (IOException e) { - // Problem closing, ignore and move on - } - return fromProperties(props); - } - - private static ReleaseDetails fromProperties(final Properties props) { - String release = props.getProperty("jhove.release.version"); - String dateFormat = props.getProperty("jhove.date.format"); - SimpleDateFormat formatter = new SimpleDateFormat(dateFormat); - Date date = new Date(); - try { - date = formatter.parse(props.getProperty("jhove.release.date")); - } catch (ParseException e) { - /** - * Safe to ignore this exception as release simply set to new date. - */ - } - return new ReleaseDetails(release, date); + return fromProperties(props); + } + + private static ReleaseDetails fromProperties(final Properties props) { + String release = props.getProperty("jhove.release.version"); + String dateFormat = props.getProperty("jhove.date.format"); + SimpleDateFormat formatter = new SimpleDateFormat(dateFormat); + Date date = new Date(); + try { + date = formatter.parse(props.getProperty("jhove.release.date")); + } catch (ParseException e) { + /** Safe to ignore this exception as release simply set to new date. */ } + return new ReleaseDetails(release, date); + } } diff --git a/jhove-core/src/test/java/edu/harvard/hul/ois/jhove/HandlerBaseTest.java b/jhove-core/src/test/java/edu/harvard/hul/ois/jhove/HandlerBaseTest.java index 908d52a4d..216c6db14 100644 --- a/jhove-core/src/test/java/edu/harvard/hul/ois/jhove/HandlerBaseTest.java +++ b/jhove-core/src/test/java/edu/harvard/hul/ois/jhove/HandlerBaseTest.java @@ -3,7 +3,6 @@ import static org.junit.Assert.assertEquals; import java.util.logging.Logger; - import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; @@ -13,17 +12,13 @@ public class HandlerBaseTest { private static final Logger LOGGER = Logger.getLogger(HandlerBaseTest.class.getName()); private static final String GIVES = " => "; private static final String DUMMY = "dummy"; - + @Test public void testEncodeContent() { /* Test values */ - final String[] VALUES = { - DUMMY, "<<>>\"\"''&&" - }; + final String[] VALUES = {DUMMY, "<<>>\"\"''&&"}; - final String[] EXPECTED = { - DUMMY, "<<>>\"\"''&&" - }; + final String[] EXPECTED = {DUMMY, "<<>>\"\"''&&"}; String encodeValue; for (int i = 0; i < VALUES.length; i++) { @@ -36,12 +31,8 @@ public void testEncodeContent() { @Test public void testEncodeValue() { /* Test values */ - final String[] VALUES = { - DUMMY, "<<>>\"'&", "" + (char)0xf + "\"\"" - }; - final String[] EXPECTED = { - DUMMY, "<<>>"'&", """" - }; + final String[] VALUES = {DUMMY, "<<>>\"'&", "" + (char) 0xf + "\"\""}; + final String[] EXPECTED = {DUMMY, "<<>>"'&", """"}; String encodeValue; for (int i = 0; i < VALUES.length; i++) { @@ -50,5 +41,4 @@ public void testEncodeValue() { assertEquals(EXPECTED[i], encodeValue); } } - } diff --git a/jhove-core/src/test/java/edu/harvard/hul/ois/jhove/NisoImageMetadataTest.java b/jhove-core/src/test/java/edu/harvard/hul/ois/jhove/NisoImageMetadataTest.java index b3c1d0a8a..67c8e06b8 100644 --- a/jhove-core/src/test/java/edu/harvard/hul/ois/jhove/NisoImageMetadataTest.java +++ b/jhove-core/src/test/java/edu/harvard/hul/ois/jhove/NisoImageMetadataTest.java @@ -5,67 +5,59 @@ import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; - import org.junit.Test; public class NisoImageMetadataTest { - private final static String ERROR_TEST = "Not a icc profile "; - - @Test - public void testExtractIccProfileDescriptionBad() { - final byte ANY_BYTE_1 = (byte) 0xFF; - final byte ANY_BYTE_2 = (byte) 0xFE; - final byte[] BAD_ICC = new byte[] { ANY_BYTE_1, ANY_BYTE_2, ANY_BYTE_1, - ANY_BYTE_2 }; - try { - NisoImageMetadata.extractIccProfileDescription(BAD_ICC); - fail(ERROR_TEST); - } catch (IllegalArgumentException iae) { - assertNotNull(iae); // Should always be true - } - } - - @Test - public void testExtractIccProfileDescriptionGoodv2() throws IOException { - - try (InputStream is = this.getClass().getResourceAsStream("sRGB2014.icc")) { - byte[] iccData = toByteArray(is); - String profileName = NisoImageMetadata - .extractIccProfileDescription(iccData); - assertEquals("sRGB2014", profileName); - } catch (IllegalArgumentException iae) { - fail(ERROR_TEST + iae.getMessage()); - } - } - - @Test - public void testExtractIccProfileDescriptionGoodv4() throws IOException { - - - try (InputStream is = this.getClass().getResourceAsStream( - "sRGB_v4_ICC_preference.icc")) { - byte[] iccData = toByteArray(is); - String profileName = NisoImageMetadata - .extractIccProfileDescription(iccData); - assertEquals("sRGB v4 ICC preference perceptual intent beta", - profileName); - } catch (IllegalArgumentException iae) { - fail(ERROR_TEST + iae.getMessage()); - } - } - - private static byte[] toByteArray(InputStream is) throws IOException { - final int NB_READ = 16384; - ByteArrayOutputStream buffer = new ByteArrayOutputStream(); - - int nRead; - byte[] data = new byte[NB_READ]; - - while ((nRead = is.read(data, 0, data.length)) != -1) { - buffer.write(data, 0, nRead); - } - buffer.flush(); - return buffer.toByteArray(); - - } + private static final String ERROR_TEST = "Not a icc profile "; + + @Test + public void testExtractIccProfileDescriptionBad() { + final byte ANY_BYTE_1 = (byte) 0xFF; + final byte ANY_BYTE_2 = (byte) 0xFE; + final byte[] BAD_ICC = new byte[] {ANY_BYTE_1, ANY_BYTE_2, ANY_BYTE_1, ANY_BYTE_2}; + try { + NisoImageMetadata.extractIccProfileDescription(BAD_ICC); + fail(ERROR_TEST); + } catch (IllegalArgumentException iae) { + assertNotNull(iae); // Should always be true + } + } + + @Test + public void testExtractIccProfileDescriptionGoodv2() throws IOException { + + try (InputStream is = this.getClass().getResourceAsStream("sRGB2014.icc")) { + byte[] iccData = toByteArray(is); + String profileName = NisoImageMetadata.extractIccProfileDescription(iccData); + assertEquals("sRGB2014", profileName); + } catch (IllegalArgumentException iae) { + fail(ERROR_TEST + iae.getMessage()); + } + } + + @Test + public void testExtractIccProfileDescriptionGoodv4() throws IOException { + + try (InputStream is = this.getClass().getResourceAsStream("sRGB_v4_ICC_preference.icc")) { + byte[] iccData = toByteArray(is); + String profileName = NisoImageMetadata.extractIccProfileDescription(iccData); + assertEquals("sRGB v4 ICC preference perceptual intent beta", profileName); + } catch (IllegalArgumentException iae) { + fail(ERROR_TEST + iae.getMessage()); + } + } + + private static byte[] toByteArray(InputStream is) throws IOException { + final int NB_READ = 16384; + ByteArrayOutputStream buffer = new ByteArrayOutputStream(); + + int nRead; + byte[] data = new byte[NB_READ]; + + while ((nRead = is.read(data, 0, data.length)) != -1) { + buffer.write(data, 0, nRead); + } + buffer.flush(); + return buffer.toByteArray(); + } } diff --git a/jhove-core/src/test/java/edu/harvard/hul/ois/jhove/handler/JsonHandlerTest.java b/jhove-core/src/test/java/edu/harvard/hul/ois/jhove/handler/JsonHandlerTest.java index c3dabebac..cf37913eb 100644 --- a/jhove-core/src/test/java/edu/harvard/hul/ois/jhove/handler/JsonHandlerTest.java +++ b/jhove-core/src/test/java/edu/harvard/hul/ois/jhove/handler/JsonHandlerTest.java @@ -2,6 +2,21 @@ import static org.junit.Assert.assertEquals; +import edu.harvard.hul.ois.jhove.AESAudioMetadata; +import edu.harvard.hul.ois.jhove.Agent; +import edu.harvard.hul.ois.jhove.App; +import edu.harvard.hul.ois.jhove.Checksum; +import edu.harvard.hul.ois.jhove.ChecksumType; +import edu.harvard.hul.ois.jhove.JhoveBase; +import edu.harvard.hul.ois.jhove.JhoveException; +import edu.harvard.hul.ois.jhove.Module; +import edu.harvard.hul.ois.jhove.OutputHandler; +import edu.harvard.hul.ois.jhove.Property; +import edu.harvard.hul.ois.jhove.PropertyArity; +import edu.harvard.hul.ois.jhove.PropertyType; +import edu.harvard.hul.ois.jhove.Rational; +import edu.harvard.hul.ois.jhove.RepInfo; +import edu.harvard.hul.ois.jhove.TextMDMetadata; import java.io.File; import java.io.IOException; import java.io.PrintWriter; @@ -13,13 +28,11 @@ import java.util.Map; import java.util.Set; import java.util.logging.Logger; - import javax.json.Json; import javax.json.JsonArrayBuilder; import javax.json.JsonObject; import javax.json.JsonObjectBuilder; import javax.json.JsonWriter; - import org.junit.After; import org.junit.Before; import org.junit.BeforeClass; @@ -27,437 +40,438 @@ import org.junit.runner.RunWith; import org.junit.runners.JUnit4; -import edu.harvard.hul.ois.jhove.AESAudioMetadata; -import edu.harvard.hul.ois.jhove.Agent; -import edu.harvard.hul.ois.jhove.App; -import edu.harvard.hul.ois.jhove.Checksum; -import edu.harvard.hul.ois.jhove.ChecksumType; -import edu.harvard.hul.ois.jhove.JhoveBase; -import edu.harvard.hul.ois.jhove.JhoveException; -import edu.harvard.hul.ois.jhove.Module; -import edu.harvard.hul.ois.jhove.OutputHandler; -import edu.harvard.hul.ois.jhove.Property; -import edu.harvard.hul.ois.jhove.PropertyArity; -import edu.harvard.hul.ois.jhove.PropertyType; -import edu.harvard.hul.ois.jhove.Rational; -import edu.harvard.hul.ois.jhove.RepInfo; -import edu.harvard.hul.ois.jhove.TextMDMetadata; - @RunWith(JUnit4.class) public class JsonHandlerTest { - private static final Logger LOGGER = Logger.getLogger(JsonHandlerTest.class - .getName()); - - private static final String TIME_PATTERN = "\"[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}(\\+[0-9]{2}:[0-9]{2})?\""; - private static final String DATE_PATTERN = "\"date\":\"[^\"]+\""; - private static final String DATE_REPLACEMENT = "\"date\":\"2010-01-01\""; - private static final String RELEASE_PATTERN = "\"release\":\"[^\"]+\""; - private static final String RELEASE_REPLACEMENT = "\"release\":\"DUMMY\""; - private static final String DIR_PATTERN = "\"tempDirectory\":\"[^\"]+\""; - private static final String DIR_REPLACEMENT = "\"tempDirectory\":\"DUMMY\""; - private static final String CONF_PATTERN = "\"configuration\":\"[^\"]+\""; - private static final String CONF_REPLACEMENT = "\"configuration\":\"DUMMY\""; - private static final String RIGHTS_PATTERN = "\"rights\":\"[^\"]+\""; - private static final String RIGHTS_REPLACEMENT = "\"rights\":\"DUMMY\""; - private static final String VENDOR_PATTERN = "\"vendor\":\\{[^\\}]+\\}"; - private static final String VENDOR_REPLACEMENT = "\"vendor\":{\"kind\":\"Vendor\"}"; - private static final String DUMMY = "\"DUMMY\""; - private static final String DUMMY_CK = "8747e564eb53cb2f1dcb9aae0779c2aa"; - private static final String APP_JSON = - "\"name\":\"TEST\",\"release\":\"DUMMY\",\"date\":\"2010-01-01\",\"executionTime\":\"DUMMY\""; - private static final String API_JSON = - "\"app\":{\"api\":{\"version\":\"1.0\",\"date\":\"2010-01-01\"}," + - "\"configuration\":\"DUMMY\",\"jhoveHome\":\"TEST\",\"encoding\":\"utf-8\",\"tempDirectory\":\"DUMMY\"," + - "\"bufferSize\":131072,\"modules\":[{\"module\":\"BYTESTREAM\",\"release\":\"DUMMY\"}]," + - "\"outputHandlers\":[{\"outputHandler\":\"Audit\",\"release\":\"DUMMY\"}," + - "{\"outputHandler\":\"JSON\",\"release\":\"DUMMY\"},{\"outputHandler\":\"TEXT\",\"release\":\"DUMMY\"}," + - "{\"outputHandler\":\"XML\",\"release\":\"DUMMY\"}],\"usage\":\"usage\",\"rights\":\"DUMMY\"}"; - private static final String HANDLER_JSON = - "\"handler\":{\"name\":\"JSON\",\"release\":\"DUMMY\",\"date\":\"2010-01-01\"," + - "\"vendor\":{\"kind\":\"Vendor\",\"name\":\"Bibliothèque nationale de France\",\"type\":\"Educational\"," + - "\"web\":\"http://www.bnf.fr\"},\"note\":\"\"," + - "\"rights\":\"DUMMY\"}"; - private static final String MODULE_JSON = - "\"module\":{\"name\":\"BYTESTREAM\",\"release\":\"DUMMY\",\"date\":\"2010-01-01\"," + - "\"formats\":[\"bytestream\"],\"mimeTypes\":[\"application/octet-stream\"]," + - "\"features\":[\"edu.harvard.hul.ois.jhove.canValidate\",\"edu.harvard.hul.ois.jhove.canCharacterize\"]," + - "\"methodology\":{\"wellFormed\":\"All bytestreams are well-formed\"}," + - "\"vendor\":{\"kind\":\"Vendor\"}," + - "\"note\":\"This is the default format\",\"rights\":\"DUMMY\"}"; - private static final String INFO_JSON = - "\"repInfo\":{\"uri\":\"file://dummy.file\"," + - "\"reportingModule\":{\"name\":\"BYTESTREAM\",\"release\":\"DUMMY\",\"date\":\"2010-01-01\"}," + - "\"size\":1,\"format\":\"bytestream\",\"status\":\"Well-Formed and valid\",\"sigMatch\":[\"BYTESTREAM\"]," + - "\"mimeType\":\"application/octet-stream\",\"properties\":[{\"checksum\":\"" + - DUMMY_CK + "\",\"type\":\"MD5\"}]}"; - /** Handler string "Find: " */ - private static final String FIND = "Find: "; - - private static App mockApp; - private static JhoveBase je; - - private File outputFile; - private StringWriter outString; - private PrintWriter writer; - private JsonHandler handler; - - @BeforeClass - public static void setUpBeforeClass() throws JhoveException { - mockApp = new App("TEST", "1.0", new int[]{2019,10,28}, "usage", "rights"); - je = new JhoveBase(); - je.setLogLevel("INFO"); - String fileConf = JsonHandlerTest.class.getResource("/jhove_test.conf").getPath(); - LOGGER.info("jhove.conf in:[" + fileConf + "]"); - je.init(fileConf, null); - } - - @Before - public void setUp() throws IOException { - // Prepare for a new test - this.outputFile = File.createTempFile("jhove_", ".json"); - - outString = new StringWriter(); - writer = new PrintWriter(outString); - // PrintWriter writer = new PrintWriter(outputFile); - - this.handler = new JsonHandler(); - this.handler.setApp(mockApp); - this.handler.setBase(je); - this.handler.setWriter(writer); - } - - @After - public void tearDown() { - if (this.outputFile != null && this.outputFile.exists()) { - this.outputFile.delete(); - } - } - - public void buildJson(JsonObjectBuilder builder) { - JsonObject jsonObject = builder.build(); - JsonWriter jsonWriter = Json.createWriter(writer); - jsonWriter.writeObject(jsonObject); - } - - public void buildJson(JsonArrayBuilder builder) { - JsonObjectBuilder job = Json.createObjectBuilder().add("ARRAY", builder); - JsonObject jsonObject = job.build(); - JsonWriter jsonWriter = Json.createWriter(writer); - jsonWriter.writeObject(jsonObject); - } - - @Test - public void testShow() { - handler.showHeader(); - handler.show(); - handler.showFooter(); - handler.close(); - - String result = outString.toString().replaceAll(TIME_PATTERN, DUMMY) - .replaceAll(DATE_PATTERN, DATE_REPLACEMENT) - .replaceAll(RELEASE_PATTERN, RELEASE_REPLACEMENT); - assertEquals( - "{\"jhove\":{" + APP_JSON + "}}", result); - - } - - @Test - public void testShowApp() { - handler.showHeader(); - handler.show(mockApp); - handler.showFooter(); - handler.close(); - - String result = outString.toString().replaceAll(TIME_PATTERN, DUMMY) - .replaceAll(DATE_PATTERN, DATE_REPLACEMENT) - .replaceAll(RELEASE_PATTERN, RELEASE_REPLACEMENT) - .replaceAll(CONF_PATTERN, CONF_REPLACEMENT) - .replaceAll(RIGHTS_PATTERN, RIGHTS_REPLACEMENT) - .replaceAll(DIR_PATTERN, DIR_REPLACEMENT); - LOGGER.info(FIND + result); - String expected = "{\"jhove\":{" + APP_JSON + "," + API_JSON + "}}"; - - assertEquals(expected, result); - } - - @Test - public void testShowOutputHandler() { - OutputHandler jsonHandler = je.getHandler("JSON"); - - handler.showHeader(); - handler.show(jsonHandler); - handler.showFooter(); - handler.close(); - - String result = outString.toString().replaceAll(TIME_PATTERN, DUMMY) - .replaceAll(DATE_PATTERN, DATE_REPLACEMENT) - .replaceAll(RELEASE_PATTERN, RELEASE_REPLACEMENT) - .replaceAll(CONF_PATTERN, CONF_REPLACEMENT) - .replaceAll(RIGHTS_PATTERN, RIGHTS_REPLACEMENT) - .replaceAll(DIR_PATTERN, DIR_REPLACEMENT); - LOGGER.info(FIND + result); - String expected = "{\"jhove\":{" + APP_JSON + "," + HANDLER_JSON + "}}"; - - assertEquals(expected, result); - } - - @Test - public void testShowModule() { - Module module = je.getModule("BYTESTREAM"); - - handler.showHeader(); - handler.show(module); - handler.showFooter(); - handler.close(); - - String result = outString.toString().replaceAll(TIME_PATTERN, DUMMY) - .replaceAll(DATE_PATTERN, DATE_REPLACEMENT) - .replaceAll(RELEASE_PATTERN, RELEASE_REPLACEMENT) - .replaceAll(CONF_PATTERN, CONF_REPLACEMENT) - .replaceAll(RIGHTS_PATTERN, RIGHTS_REPLACEMENT) - .replaceAll(DIR_PATTERN, DIR_REPLACEMENT) - .replaceAll(VENDOR_PATTERN, VENDOR_REPLACEMENT); - LOGGER.info(FIND + result); - String expected = "{\"jhove\":{" + APP_JSON + "," + MODULE_JSON + "}}"; - - assertEquals(expected, result); - } - - @Test - public void testShowRepInfo() { - Module module = je.getModule("BYTESTREAM"); - RepInfo info = new RepInfo("file://dummy.file"); - info.setModule(module); - info.setFormat(module.getFormat()[0]); - info.setMimeType(module.getMimeType()[0]); - info.setSigMatch(module.getName()); - info.setChecksum(new Checksum(DUMMY_CK, ChecksumType.MD5)); - info.setSize(1); - - handler.showHeader(); - handler.show(info); - handler.showFooter(); - handler.close(); - - String result = outString.toString().replaceAll(TIME_PATTERN, DUMMY) - .replaceAll(DATE_PATTERN, DATE_REPLACEMENT) - .replaceAll(RELEASE_PATTERN, RELEASE_REPLACEMENT) - .replaceAll(CONF_PATTERN, CONF_REPLACEMENT) - .replaceAll(RIGHTS_PATTERN, RIGHTS_REPLACEMENT) - .replaceAll(DIR_PATTERN, DIR_REPLACEMENT) - .replaceAll(VENDOR_PATTERN, VENDOR_REPLACEMENT); - LOGGER.info(FIND + result); - String expected = "{\"jhove\":{" + APP_JSON + "," + INFO_JSON + "}}"; - - assertEquals(expected, result); - } - - @Test - public void testShowVendor() { - OutputHandler jsonHandler = je.getHandler("JSON"); - Agent v = jsonHandler.getVendor(); - - JsonObjectBuilder json = handler.showAgent(v, "OTHER"); - buildJson(json); - handler.close(); - - String result = outString.toString(); - LOGGER.info(FIND + result); - final String expected = "{\"kind\":\"OTHER\",\"name\":\"Bibliothèque nationale de France\"," + - "\"type\":\"Educational\",\"web\":\"http://www.bnf.fr\"}"; - - assertEquals(expected, result); - } - - @Test - public void testShowScalarProperty() throws IOException { - final Property prop = new Property("test", PropertyType.INTEGER, PropertyArity.SCALAR, 2); - JsonObjectBuilder b = this.handler.showScalarProperty(prop); - buildJson(b); - handler.close(); - - String result = outString.toString(); - LOGGER.info(FIND + result); - final String expected = "{\"test\":2}"; - - assertEquals(expected, result); - } - - @Test - public void testShowListProperty() throws IOException { - final List testList = Arrays.asList(new Double[]{1.0, 2.0}); - Property prop = new Property("test", PropertyType.DOUBLE, PropertyArity.LIST, testList); - JsonObjectBuilder b = this.handler.showListProperty(prop); - buildJson(b); - handler.close(); - - String result = outString.toString(); - LOGGER.info(FIND + result); - final String expected = "{\"test\":[1.0,2.0]}"; - - assertEquals(expected, result); - } - - @Test - public void testShowSetProperty() throws IOException { - // use a LinkedHashSet to be sure of the output order... - final Set testSet = new LinkedHashSet<>( - Arrays.asList(new Rational[]{new Rational(300, 1), new Rational(4, 2)})); - Property prop = new Property("test", PropertyType.RATIONAL, PropertyArity.SET, testSet); - JsonObjectBuilder b = this.handler.showSetProperty(prop); - buildJson(b); - handler.close(); - - String result = outString.toString(); - LOGGER.info(FIND + result); - final String expected = "{\"test\":[[300,1],[4,2]]}"; - - assertEquals(expected, result); - } - - @Test - public void testShowMapProperty() throws IOException { - final Map testMap = Collections.singletonMap("mykey", "myvalue"); - Property prop = new Property("test", PropertyType.STRING, PropertyArity.MAP, testMap); - JsonObjectBuilder b = this.handler.showMapProperty(prop); - buildJson(b); - handler.close(); - - String result = outString.toString(); - LOGGER.info(FIND + result); - final String expected = "{\"test\":{\"mykey\":\"myvalue\"}}"; - - assertEquals(expected, result); - } - - @Test - public void testShowArrayMapProperty() throws IOException { - final boolean[] testArray = new boolean[]{true, false, true}; - Property prop = new Property("test", PropertyType.BOOLEAN, PropertyArity.ARRAY, testArray); - JsonObjectBuilder b = this.handler.showArrayProperty(prop); - buildJson(b); - handler.close(); - - String result = outString.toString(); - LOGGER.info(FIND + result); - final String expected = "{\"test\":[true,false,true]}"; - - assertEquals(expected, result); - } - - @Test - public void testShowTextMD() throws IOException { - final TextMDMetadata textMD = new TextMDMetadata(); - textMD.setCharset("UTF-8"); - textMD.setLanguage("fr"); - - JsonObjectBuilder b = this.handler.showTextMDMetadata(textMD); - buildJson(b); - handler.close(); - - String result = outString.toString(); - LOGGER.info(FIND + result); - final String expected = "{\"textmd:charset\":\"UTF-8\",\"textmd:byte_order\":\"big\"," + - "\"textmd:linebreak\":\"CR/LF\",\"textmd:language\":\"fre\"}"; - - assertEquals(expected, result); - } - - @Test - public void testShowAESAudioMetadata() throws IOException { - final AESAudioMetadata aes = new AESAudioMetadata(); - aes.setFormat("audio/wav"); - - JsonObjectBuilder b = this.handler.showAESAudioMetadata(aes); - buildJson(b); - handler.close(); - - String result = outString.toString(); - LOGGER.info(FIND + result); - final String expected = "{\"aes:schemaVersion\":\"1.02b\",\"aes:format\":\"audio/wav\"," + - "\"aes:face\":{\"aes:timeline\":{\"tcf:startTime\":{\"tcf:frameCount\":30,\"tcf:timeBase\":1000," + - "\"tcf:videoField\":\"FIELD_1\",\"tcf:countingMode\":\"NTSC_NON_DROP_FRAME\",\"tcf:hours\":0," + - "\"tcf:minutes\":0,\"tcf:seconds\":0,\"tcf:frames\":0," + - "\"tcf:samples\":{\"tcf:sampleRate\":\"S44100\",\"tcf:numberOfSamples\":0}," + - "\"tcf:filmFraming\":{\"tcf:framing\":\"NOT_APPLICABLE\",\"tcf:framingType\":\"tcf:ntscFilmFramingType\"}}}," + - "\"aes:streams\":[]}}"; - - assertEquals(expected, result); - } - - @Test - public void testShowArrayInt() throws IOException { - final int[] iArrayTest = { 1, 2, 3 }; - JsonArrayBuilder b = this.handler.showArray(iArrayTest); - - buildJson(b); - handler.close(); - - String result = outString.toString(); - LOGGER.info(FIND + result); - final String expected = "{\"ARRAY\":[1,2,3]}"; - - assertEquals(expected, result); - } - - @Test - public void testShowArrayDouble() throws IOException { - final double[] dArrayTest = { -1.0, 0, 1.0 }; - JsonArrayBuilder b = this.handler.showArray(dArrayTest); - - buildJson(b); - handler.close(); - - String result = outString.toString(); - LOGGER.info(FIND + result); - final String expected = "{\"ARRAY\":[-1.0,0.0,1.0]}"; - - assertEquals(expected, result); - } - - @Test - public void testShowArrayString() throws IOException { - final String[] sArrayTest = { null, "", "DUMMY" }; - JsonArrayBuilder b = this.handler.showArray(sArrayTest); - - buildJson(b); - handler.close(); - - String result = outString.toString(); - LOGGER.info(FIND + result); - final String expected = "{\"ARRAY\":[null,\"\",\"DUMMY\"]}"; - - assertEquals(expected, result); - } - - - @Test - public void testShowArrayRational() throws IOException { - final Rational[] rArrayTest = { new Rational(1L,1L), new Rational(-1, 2) }; - JsonArrayBuilder b = this.handler.showArray(rArrayTest); - - buildJson(b); - handler.close(); - - String result = outString.toString(); - LOGGER.info(FIND + result); - final String expected = "{\"ARRAY\":[[1,1],[-1,2]]}"; - - assertEquals(expected, result); - } - - @Test - public void testShowRational() throws IOException { - final Rational r = new Rational(123456,43211); - JsonArrayBuilder b = this.handler.showRational(r); - - buildJson(b); - handler.close(); - - String result = outString.toString(); - LOGGER.info(FIND + result); - final String expected = "{\"ARRAY\":[123456,43211]}"; - - assertEquals(expected, result); - } + private static final Logger LOGGER = Logger.getLogger(JsonHandlerTest.class.getName()); + + private static final String TIME_PATTERN = + "\"[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}(\\+[0-9]{2}:[0-9]{2})?\""; + private static final String DATE_PATTERN = "\"date\":\"[^\"]+\""; + private static final String DATE_REPLACEMENT = "\"date\":\"2010-01-01\""; + private static final String RELEASE_PATTERN = "\"release\":\"[^\"]+\""; + private static final String RELEASE_REPLACEMENT = "\"release\":\"DUMMY\""; + private static final String DIR_PATTERN = "\"tempDirectory\":\"[^\"]+\""; + private static final String DIR_REPLACEMENT = "\"tempDirectory\":\"DUMMY\""; + private static final String CONF_PATTERN = "\"configuration\":\"[^\"]+\""; + private static final String CONF_REPLACEMENT = "\"configuration\":\"DUMMY\""; + private static final String RIGHTS_PATTERN = "\"rights\":\"[^\"]+\""; + private static final String RIGHTS_REPLACEMENT = "\"rights\":\"DUMMY\""; + private static final String VENDOR_PATTERN = "\"vendor\":\\{[^\\}]+\\}"; + private static final String VENDOR_REPLACEMENT = "\"vendor\":{\"kind\":\"Vendor\"}"; + private static final String DUMMY = "\"DUMMY\""; + private static final String DUMMY_CK = "8747e564eb53cb2f1dcb9aae0779c2aa"; + private static final String APP_JSON = + "\"name\":\"TEST\",\"release\":\"DUMMY\",\"date\":\"2010-01-01\",\"executionTime\":\"DUMMY\""; + private static final String API_JSON = + "\"app\":{\"api\":{\"version\":\"1.0\",\"date\":\"2010-01-01\"}," + + "\"configuration\":\"DUMMY\",\"jhoveHome\":\"TEST\",\"encoding\":\"utf-8\",\"tempDirectory\":\"DUMMY\"," + + "\"bufferSize\":131072,\"modules\":[{\"module\":\"BYTESTREAM\",\"release\":\"DUMMY\"}]," + + "\"outputHandlers\":[{\"outputHandler\":\"Audit\",\"release\":\"DUMMY\"}," + + "{\"outputHandler\":\"JSON\",\"release\":\"DUMMY\"},{\"outputHandler\":\"TEXT\",\"release\":\"DUMMY\"}," + + "{\"outputHandler\":\"XML\",\"release\":\"DUMMY\"}],\"usage\":\"usage\",\"rights\":\"DUMMY\"}"; + private static final String HANDLER_JSON = + "\"handler\":{\"name\":\"JSON\",\"release\":\"DUMMY\",\"date\":\"2010-01-01\"," + + "\"vendor\":{\"kind\":\"Vendor\",\"name\":\"Bibliothèque nationale de France\",\"type\":\"Educational\"," + + "\"web\":\"http://www.bnf.fr\"},\"note\":\"\"," + + "\"rights\":\"DUMMY\"}"; + private static final String MODULE_JSON = + "\"module\":{\"name\":\"BYTESTREAM\",\"release\":\"DUMMY\",\"date\":\"2010-01-01\"," + + "\"formats\":[\"bytestream\"],\"mimeTypes\":[\"application/octet-stream\"]," + + "\"features\":[\"edu.harvard.hul.ois.jhove.canValidate\",\"edu.harvard.hul.ois.jhove.canCharacterize\"]," + + "\"methodology\":{\"wellFormed\":\"All bytestreams are well-formed\"}," + + "\"vendor\":{\"kind\":\"Vendor\"}," + + "\"note\":\"This is the default format\",\"rights\":\"DUMMY\"}"; + private static final String INFO_JSON = + "\"repInfo\":{\"uri\":\"file://dummy.file\"," + + "\"reportingModule\":{\"name\":\"BYTESTREAM\",\"release\":\"DUMMY\",\"date\":\"2010-01-01\"}," + + "\"size\":1,\"format\":\"bytestream\",\"status\":\"Well-Formed and valid\",\"sigMatch\":[\"BYTESTREAM\"]," + + "\"mimeType\":\"application/octet-stream\",\"properties\":[{\"checksum\":\"" + + DUMMY_CK + + "\",\"type\":\"MD5\"}]}"; + /** Handler string "Find: " */ + private static final String FIND = "Find: "; + + private static App mockApp; + private static JhoveBase je; + + private File outputFile; + private StringWriter outString; + private PrintWriter writer; + private JsonHandler handler; + + @BeforeClass + public static void setUpBeforeClass() throws JhoveException { + mockApp = new App("TEST", "1.0", new int[] {2019, 10, 28}, "usage", "rights"); + je = new JhoveBase(); + je.setLogLevel("INFO"); + String fileConf = JsonHandlerTest.class.getResource("/jhove_test.conf").getPath(); + LOGGER.info("jhove.conf in:[" + fileConf + "]"); + je.init(fileConf, null); + } + + @Before + public void setUp() throws IOException { + // Prepare for a new test + this.outputFile = File.createTempFile("jhove_", ".json"); + + outString = new StringWriter(); + writer = new PrintWriter(outString); + // PrintWriter writer = new PrintWriter(outputFile); + + this.handler = new JsonHandler(); + this.handler.setApp(mockApp); + this.handler.setBase(je); + this.handler.setWriter(writer); + } + + @After + public void tearDown() { + if (this.outputFile != null && this.outputFile.exists()) { + this.outputFile.delete(); + } + } + + public void buildJson(JsonObjectBuilder builder) { + JsonObject jsonObject = builder.build(); + JsonWriter jsonWriter = Json.createWriter(writer); + jsonWriter.writeObject(jsonObject); + } + + public void buildJson(JsonArrayBuilder builder) { + JsonObjectBuilder job = Json.createObjectBuilder().add("ARRAY", builder); + JsonObject jsonObject = job.build(); + JsonWriter jsonWriter = Json.createWriter(writer); + jsonWriter.writeObject(jsonObject); + } + + @Test + public void testShow() { + handler.showHeader(); + handler.show(); + handler.showFooter(); + handler.close(); + + String result = + outString + .toString() + .replaceAll(TIME_PATTERN, DUMMY) + .replaceAll(DATE_PATTERN, DATE_REPLACEMENT) + .replaceAll(RELEASE_PATTERN, RELEASE_REPLACEMENT); + assertEquals("{\"jhove\":{" + APP_JSON + "}}", result); + } + + @Test + public void testShowApp() { + handler.showHeader(); + handler.show(mockApp); + handler.showFooter(); + handler.close(); + + String result = + outString + .toString() + .replaceAll(TIME_PATTERN, DUMMY) + .replaceAll(DATE_PATTERN, DATE_REPLACEMENT) + .replaceAll(RELEASE_PATTERN, RELEASE_REPLACEMENT) + .replaceAll(CONF_PATTERN, CONF_REPLACEMENT) + .replaceAll(RIGHTS_PATTERN, RIGHTS_REPLACEMENT) + .replaceAll(DIR_PATTERN, DIR_REPLACEMENT); + LOGGER.info(FIND + result); + String expected = "{\"jhove\":{" + APP_JSON + "," + API_JSON + "}}"; + + assertEquals(expected, result); + } + + @Test + public void testShowOutputHandler() { + OutputHandler jsonHandler = je.getHandler("JSON"); + + handler.showHeader(); + handler.show(jsonHandler); + handler.showFooter(); + handler.close(); + + String result = + outString + .toString() + .replaceAll(TIME_PATTERN, DUMMY) + .replaceAll(DATE_PATTERN, DATE_REPLACEMENT) + .replaceAll(RELEASE_PATTERN, RELEASE_REPLACEMENT) + .replaceAll(CONF_PATTERN, CONF_REPLACEMENT) + .replaceAll(RIGHTS_PATTERN, RIGHTS_REPLACEMENT) + .replaceAll(DIR_PATTERN, DIR_REPLACEMENT); + LOGGER.info(FIND + result); + String expected = "{\"jhove\":{" + APP_JSON + "," + HANDLER_JSON + "}}"; + + assertEquals(expected, result); + } + + @Test + public void testShowModule() { + Module module = je.getModule("BYTESTREAM"); + + handler.showHeader(); + handler.show(module); + handler.showFooter(); + handler.close(); + + String result = + outString + .toString() + .replaceAll(TIME_PATTERN, DUMMY) + .replaceAll(DATE_PATTERN, DATE_REPLACEMENT) + .replaceAll(RELEASE_PATTERN, RELEASE_REPLACEMENT) + .replaceAll(CONF_PATTERN, CONF_REPLACEMENT) + .replaceAll(RIGHTS_PATTERN, RIGHTS_REPLACEMENT) + .replaceAll(DIR_PATTERN, DIR_REPLACEMENT) + .replaceAll(VENDOR_PATTERN, VENDOR_REPLACEMENT); + LOGGER.info(FIND + result); + String expected = "{\"jhove\":{" + APP_JSON + "," + MODULE_JSON + "}}"; + + assertEquals(expected, result); + } + + @Test + public void testShowRepInfo() { + Module module = je.getModule("BYTESTREAM"); + RepInfo info = new RepInfo("file://dummy.file"); + info.setModule(module); + info.setFormat(module.getFormat()[0]); + info.setMimeType(module.getMimeType()[0]); + info.setSigMatch(module.getName()); + info.setChecksum(new Checksum(DUMMY_CK, ChecksumType.MD5)); + info.setSize(1); + + handler.showHeader(); + handler.show(info); + handler.showFooter(); + handler.close(); + + String result = + outString + .toString() + .replaceAll(TIME_PATTERN, DUMMY) + .replaceAll(DATE_PATTERN, DATE_REPLACEMENT) + .replaceAll(RELEASE_PATTERN, RELEASE_REPLACEMENT) + .replaceAll(CONF_PATTERN, CONF_REPLACEMENT) + .replaceAll(RIGHTS_PATTERN, RIGHTS_REPLACEMENT) + .replaceAll(DIR_PATTERN, DIR_REPLACEMENT) + .replaceAll(VENDOR_PATTERN, VENDOR_REPLACEMENT); + LOGGER.info(FIND + result); + String expected = "{\"jhove\":{" + APP_JSON + "," + INFO_JSON + "}}"; + + assertEquals(expected, result); + } + + @Test + public void testShowVendor() { + OutputHandler jsonHandler = je.getHandler("JSON"); + Agent v = jsonHandler.getVendor(); + + JsonObjectBuilder json = handler.showAgent(v, "OTHER"); + buildJson(json); + handler.close(); + + String result = outString.toString(); + LOGGER.info(FIND + result); + final String expected = + "{\"kind\":\"OTHER\",\"name\":\"Bibliothèque nationale de France\"," + + "\"type\":\"Educational\",\"web\":\"http://www.bnf.fr\"}"; + + assertEquals(expected, result); + } + + @Test + public void testShowScalarProperty() throws IOException { + final Property prop = new Property("test", PropertyType.INTEGER, PropertyArity.SCALAR, 2); + JsonObjectBuilder b = this.handler.showScalarProperty(prop); + buildJson(b); + handler.close(); + + String result = outString.toString(); + LOGGER.info(FIND + result); + final String expected = "{\"test\":2}"; + + assertEquals(expected, result); + } + + @Test + public void testShowListProperty() throws IOException { + final List testList = Arrays.asList(new Double[] {1.0, 2.0}); + Property prop = new Property("test", PropertyType.DOUBLE, PropertyArity.LIST, testList); + JsonObjectBuilder b = this.handler.showListProperty(prop); + buildJson(b); + handler.close(); + + String result = outString.toString(); + LOGGER.info(FIND + result); + final String expected = "{\"test\":[1.0,2.0]}"; + + assertEquals(expected, result); + } + + @Test + public void testShowSetProperty() throws IOException { + // use a LinkedHashSet to be sure of the output order... + final Set testSet = + new LinkedHashSet<>( + Arrays.asList(new Rational[] {new Rational(300, 1), new Rational(4, 2)})); + Property prop = new Property("test", PropertyType.RATIONAL, PropertyArity.SET, testSet); + JsonObjectBuilder b = this.handler.showSetProperty(prop); + buildJson(b); + handler.close(); + + String result = outString.toString(); + LOGGER.info(FIND + result); + final String expected = "{\"test\":[[300,1],[4,2]]}"; + + assertEquals(expected, result); + } + + @Test + public void testShowMapProperty() throws IOException { + final Map testMap = Collections.singletonMap("mykey", "myvalue"); + Property prop = new Property("test", PropertyType.STRING, PropertyArity.MAP, testMap); + JsonObjectBuilder b = this.handler.showMapProperty(prop); + buildJson(b); + handler.close(); + + String result = outString.toString(); + LOGGER.info(FIND + result); + final String expected = "{\"test\":{\"mykey\":\"myvalue\"}}"; + + assertEquals(expected, result); + } + + @Test + public void testShowArrayMapProperty() throws IOException { + final boolean[] testArray = new boolean[] {true, false, true}; + Property prop = new Property("test", PropertyType.BOOLEAN, PropertyArity.ARRAY, testArray); + JsonObjectBuilder b = this.handler.showArrayProperty(prop); + buildJson(b); + handler.close(); + + String result = outString.toString(); + LOGGER.info(FIND + result); + final String expected = "{\"test\":[true,false,true]}"; + + assertEquals(expected, result); + } + + @Test + public void testShowTextMD() throws IOException { + final TextMDMetadata textMD = new TextMDMetadata(); + textMD.setCharset("UTF-8"); + textMD.setLanguage("fr"); + + JsonObjectBuilder b = this.handler.showTextMDMetadata(textMD); + buildJson(b); + handler.close(); + + String result = outString.toString(); + LOGGER.info(FIND + result); + final String expected = + "{\"textmd:charset\":\"UTF-8\",\"textmd:byte_order\":\"big\"," + + "\"textmd:linebreak\":\"CR/LF\",\"textmd:language\":\"fre\"}"; + + assertEquals(expected, result); + } + + @Test + public void testShowAESAudioMetadata() throws IOException { + final AESAudioMetadata aes = new AESAudioMetadata(); + aes.setFormat("audio/wav"); + + JsonObjectBuilder b = this.handler.showAESAudioMetadata(aes); + buildJson(b); + handler.close(); + + String result = outString.toString(); + LOGGER.info(FIND + result); + final String expected = + "{\"aes:schemaVersion\":\"1.02b\",\"aes:format\":\"audio/wav\"," + + "\"aes:face\":{\"aes:timeline\":{\"tcf:startTime\":{\"tcf:frameCount\":30,\"tcf:timeBase\":1000," + + "\"tcf:videoField\":\"FIELD_1\",\"tcf:countingMode\":\"NTSC_NON_DROP_FRAME\",\"tcf:hours\":0," + + "\"tcf:minutes\":0,\"tcf:seconds\":0,\"tcf:frames\":0," + + "\"tcf:samples\":{\"tcf:sampleRate\":\"S44100\",\"tcf:numberOfSamples\":0}," + + "\"tcf:filmFraming\":{\"tcf:framing\":\"NOT_APPLICABLE\",\"tcf:framingType\":\"tcf:ntscFilmFramingType\"}}}," + + "\"aes:streams\":[]}}"; + + assertEquals(expected, result); + } + + @Test + public void testShowArrayInt() throws IOException { + final int[] iArrayTest = {1, 2, 3}; + JsonArrayBuilder b = this.handler.showArray(iArrayTest); + + buildJson(b); + handler.close(); + + String result = outString.toString(); + LOGGER.info(FIND + result); + final String expected = "{\"ARRAY\":[1,2,3]}"; + + assertEquals(expected, result); + } + + @Test + public void testShowArrayDouble() throws IOException { + final double[] dArrayTest = {-1.0, 0, 1.0}; + JsonArrayBuilder b = this.handler.showArray(dArrayTest); + + buildJson(b); + handler.close(); + + String result = outString.toString(); + LOGGER.info(FIND + result); + final String expected = "{\"ARRAY\":[-1.0,0.0,1.0]}"; + + assertEquals(expected, result); + } + + @Test + public void testShowArrayString() throws IOException { + final String[] sArrayTest = {null, "", "DUMMY"}; + JsonArrayBuilder b = this.handler.showArray(sArrayTest); + + buildJson(b); + handler.close(); + + String result = outString.toString(); + LOGGER.info(FIND + result); + final String expected = "{\"ARRAY\":[null,\"\",\"DUMMY\"]}"; + + assertEquals(expected, result); + } + + @Test + public void testShowArrayRational() throws IOException { + final Rational[] rArrayTest = {new Rational(1L, 1L), new Rational(-1, 2)}; + JsonArrayBuilder b = this.handler.showArray(rArrayTest); + + buildJson(b); + handler.close(); + + String result = outString.toString(); + LOGGER.info(FIND + result); + final String expected = "{\"ARRAY\":[[1,1],[-1,2]]}"; + + assertEquals(expected, result); + } + + @Test + public void testShowRational() throws IOException { + final Rational r = new Rational(123456, 43211); + JsonArrayBuilder b = this.handler.showRational(r); + + buildJson(b); + handler.close(); + + String result = outString.toString(); + LOGGER.info(FIND + result); + final String expected = "{\"ARRAY\":[123456,43211]}"; + + assertEquals(expected, result); + } } diff --git a/jhove-core/src/test/java/edu/harvard/hul/ois/jhove/handler/XmlHandlerTest.java b/jhove-core/src/test/java/edu/harvard/hul/ois/jhove/handler/XmlHandlerTest.java index a8ca80992..905b4f0c9 100644 --- a/jhove-core/src/test/java/edu/harvard/hul/ois/jhove/handler/XmlHandlerTest.java +++ b/jhove-core/src/test/java/edu/harvard/hul/ois/jhove/handler/XmlHandlerTest.java @@ -3,13 +3,15 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; +import edu.harvard.hul.ois.jhove.NisoImageMetadata; +import edu.harvard.hul.ois.jhove.Rational; +import edu.harvard.hul.ois.jhove.TextMDMetadata; import java.io.BufferedReader; import java.io.File; import java.io.FileReader; import java.io.IOException; import java.io.PrintWriter; import java.util.logging.Logger; - import org.junit.After; import org.junit.Before; import org.junit.BeforeClass; @@ -17,393 +19,356 @@ import org.junit.runner.RunWith; import org.junit.runners.JUnit4; -import edu.harvard.hul.ois.jhove.NisoImageMetadata; -import edu.harvard.hul.ois.jhove.Rational; -import edu.harvard.hul.ois.jhove.TextMDMetadata; - @RunWith(JUnit4.class) public class XmlHandlerTest { - private static final Logger LOGGER = Logger.getLogger(XmlHandlerTest.class - .getName()); - - /* Values needed to generate the expected output */ - private static final int EXPECTED_IMAGE_WIDTH = 8192; - private static final int EXPECTED_IMAGE_RESOLUTION = 564; - private static final int EXPECTED_LAST_BYTE = 255; - private static final int EXPECTED_BYTE_SIZE = 8; - private static final int EXPECTED_NUMBER_OF_BYTES = 3; - private static final long EXPECTED_DENOMINATOR = 4294967295L; - private static final long EXPECTED_WP_NUMENATOR = 1343036288L; - private static final long EXPECTED_REDX_NUMENATOR = 2748779008L; - private static final long EXPECTED_REDY_NUMENATOR = 1417339264L; - private static final long EXPECTED_GREENX_NUMENATOR = 1288490240L; - private static final long EXPECTED_GREENY_NUMENATOR = 2576980480L; - private static final long EXPECTED_BLUEX_NUMENATOR = 644245120L; - private static final long EXPECTED_BLUEY_NUMENATOR = 257698032L; - - private static final int EXPECTED_IMAGE2_WIDTH = 1136; - private static final int EXPECTED_IMAGE2_LENGTH = 1846; - private static final double EXPECTED_IMAGE2_FNUMBER = 7.1; - private static final double EXPECTED_IMAGE2_EXPOSURE_TIME = 0.167; - private static final int EXPECTED_IMAGE2_EXPOSURE_PROGRAM = 3; - private static final int EXPECTED_IMAGE2_APERTURE_NUM = 925; - private static final int EXPECTED_IMAGE2_APERTURE_DEN = 256; - private static final double EXPECTED_IMAGE2_FOCAL = 14; - private static final int EXPECTED_IMAGE2_FLASH_NUM = 15; - private static final int EXPECTED_IMAGE2_FLASH_DEN = 100; - private static final int EXPECTED_IMAGE2_IMAGE_RESOLUTION = 180; - - private static final int EXPECTED_JPEG_COMPRESSION = 6; - private static final int EXPECTED_EXIF_COLORSPACE = 6; - private static final int EXPECTED_EXIF_IMAGE_LENGTH = 2322; - private static final int EXPECTED_EXIF_IMAGE_WIDTH = 4128; - private static final int EXPECTED_EXIF_IMAGE_RESOLUTION = 72; - private static final String EXPECTED_EXIF_VERSION = "0220"; - private static final double EXPECTED_EXIF_FOCAL = 4.13; - private static final int EXPECTED_EXIF_APERTURE_NUM = 228; - private static final int EXPECTED_EXIF_APERTURE_DEN = 100; - private static final double EXPECTED_EXIT_FNUMBER = 2.2; - - /* Test instances to be serialized */ - protected static NisoImageMetadata TEST_NISO_IMAGE_MD; - protected static NisoImageMetadata TEST_NISO_IMAGE2_MD; - protected static NisoImageMetadata TEST_NISO_EXIF_MD; - protected static TextMDMetadata TEST_TEXTMD; - - private File outputFile; - private XmlHandler handler; - - @BeforeClass - public static void setUpBeforeClass() { - // Define the test instance for NisoImageMetadata to be serialized - TEST_NISO_IMAGE_MD = new NisoImageMetadata(); - TEST_NISO_IMAGE_MD.setByteOrder(NisoImageMetadata.BYTEORDER[1]); - TEST_NISO_IMAGE_MD.setCompressionScheme(1); - TEST_NISO_IMAGE_MD.setImageWidth(EXPECTED_IMAGE_WIDTH); - TEST_NISO_IMAGE_MD.setImageLength(EXPECTED_IMAGE_WIDTH); - TEST_NISO_IMAGE_MD.setColorSpace(2); - TEST_NISO_IMAGE_MD.setProfileName("Adobe RGB (1998)"); - Rational r0 = new Rational(0, 1); - Rational r255 = new Rational(EXPECTED_LAST_BYTE, 1); - TEST_NISO_IMAGE_MD.setReferenceBlackWhite(new Rational[] { r0, r255, - r0, r255, r0, r255 }); - TEST_NISO_IMAGE_MD.setDateTimeCreated("2015-07-15T16:47:26"); - TEST_NISO_IMAGE_MD.setImageProducer("BnF"); - TEST_NISO_IMAGE_MD.setScanningSoftware("Archiver"); - TEST_NISO_IMAGE_MD.setOrientation(1); - TEST_NISO_IMAGE_MD.setSamplingFrequencyUnit(2); - TEST_NISO_IMAGE_MD.setXSamplingFrequency(new Rational( - EXPECTED_IMAGE_RESOLUTION, 1)); - TEST_NISO_IMAGE_MD.setYSamplingFrequency(new Rational( - EXPECTED_IMAGE_RESOLUTION, 1)); - TEST_NISO_IMAGE_MD.setBitsPerSample(new int[] { EXPECTED_BYTE_SIZE, - EXPECTED_BYTE_SIZE, EXPECTED_BYTE_SIZE }); - TEST_NISO_IMAGE_MD.setSamplesPerPixel(EXPECTED_NUMBER_OF_BYTES); - TEST_NISO_IMAGE_MD.setWhitePointXValue(new Rational( - EXPECTED_WP_NUMENATOR, EXPECTED_DENOMINATOR)); - TEST_NISO_IMAGE_MD.setWhitePointYValue(new Rational( - EXPECTED_WP_NUMENATOR, EXPECTED_DENOMINATOR)); - TEST_NISO_IMAGE_MD.setPrimaryChromaticitiesRedX(new Rational( - EXPECTED_REDX_NUMENATOR, EXPECTED_DENOMINATOR)); - TEST_NISO_IMAGE_MD.setPrimaryChromaticitiesRedY(new Rational( - EXPECTED_REDY_NUMENATOR, EXPECTED_DENOMINATOR)); - TEST_NISO_IMAGE_MD.setPrimaryChromaticitiesGreenX(new Rational( - EXPECTED_GREENX_NUMENATOR, EXPECTED_DENOMINATOR)); - TEST_NISO_IMAGE_MD.setPrimaryChromaticitiesGreenY(new Rational( - EXPECTED_GREENY_NUMENATOR, EXPECTED_DENOMINATOR)); - TEST_NISO_IMAGE_MD.setPrimaryChromaticitiesBlueX(new Rational( - EXPECTED_BLUEX_NUMENATOR, EXPECTED_DENOMINATOR)); - TEST_NISO_IMAGE_MD.setPrimaryChromaticitiesBlueY(new Rational( - EXPECTED_BLUEY_NUMENATOR, EXPECTED_DENOMINATOR)); - - // Define the test instance for NisoImageMetadata to be serialized - TEST_NISO_IMAGE2_MD = new NisoImageMetadata(); - TEST_NISO_IMAGE2_MD.setByteOrder(NisoImageMetadata.BYTEORDER[0]); - TEST_NISO_IMAGE2_MD.setCompressionScheme(EXPECTED_JPEG_COMPRESSION); - TEST_NISO_IMAGE2_MD.setImageWidth(EXPECTED_IMAGE2_WIDTH); - TEST_NISO_IMAGE2_MD.setImageLength(EXPECTED_IMAGE2_LENGTH); - TEST_NISO_IMAGE2_MD.setDateTimeCreated("2017-03-28T12:58:30"); - TEST_NISO_IMAGE2_MD.setDigitalCameraManufacturer("Panasonic"); - TEST_NISO_IMAGE2_MD.setDigitalCameraModelName("DMC-G6"); - TEST_NISO_IMAGE2_MD.setFNumber(EXPECTED_IMAGE2_FNUMBER); - TEST_NISO_IMAGE2_MD.setExposureTime(EXPECTED_IMAGE2_EXPOSURE_TIME); - TEST_NISO_IMAGE2_MD - .setExposureProgram(EXPECTED_IMAGE2_EXPOSURE_PROGRAM); - TEST_NISO_IMAGE2_MD.setBrightness(r0); - TEST_NISO_IMAGE2_MD.setExposureBias(r0); - Rational r925 = new Rational(EXPECTED_IMAGE2_APERTURE_NUM, - EXPECTED_IMAGE2_APERTURE_DEN); - TEST_NISO_IMAGE2_MD.setMaxApertureValue(r925); - TEST_NISO_IMAGE2_MD.setMeteringMode(2); - TEST_NISO_IMAGE2_MD.setFocalLength(EXPECTED_IMAGE2_FOCAL); - TEST_NISO_IMAGE2_MD.setFlash(1); - Rational r15 = new Rational(EXPECTED_IMAGE2_FLASH_NUM, - EXPECTED_IMAGE2_FLASH_DEN); - TEST_NISO_IMAGE2_MD.setFlashEnergy(r15); - TEST_NISO_IMAGE2_MD.setSamplingFrequencyUnit(2); - Rational r180 = new Rational(EXPECTED_IMAGE2_IMAGE_RESOLUTION, 1); - TEST_NISO_IMAGE2_MD.setXSamplingFrequency(r180); - TEST_NISO_IMAGE2_MD.setYSamplingFrequency(r180); - TEST_NISO_IMAGE2_MD.setBitsPerSample(new int[] { EXPECTED_BYTE_SIZE, - EXPECTED_BYTE_SIZE, EXPECTED_BYTE_SIZE }); - TEST_NISO_IMAGE2_MD.setSamplesPerPixel(EXPECTED_NUMBER_OF_BYTES); - - // Define the test instance of Exif for NisoImageMetadata to be - // serialized - TEST_NISO_EXIF_MD = new NisoImageMetadata(); - TEST_NISO_EXIF_MD.setByteOrder(NisoImageMetadata.BYTEORDER[0]); - TEST_NISO_EXIF_MD.setCompressionScheme(EXPECTED_JPEG_COMPRESSION); - TEST_NISO_EXIF_MD.setImageWidth(EXPECTED_EXIF_IMAGE_WIDTH); - TEST_NISO_EXIF_MD.setImageLength(EXPECTED_EXIF_IMAGE_LENGTH); - TEST_NISO_EXIF_MD.setColorSpace(EXPECTED_EXIF_COLORSPACE); - TEST_NISO_EXIF_MD.setYCbCrPositioning(1); - TEST_NISO_EXIF_MD.setDateTimeCreated("2015-02-13T14:06:36"); - TEST_NISO_EXIF_MD.setDigitalCameraManufacturer("SAMSUNG"); - TEST_NISO_EXIF_MD.setDigitalCameraModelName("SM-N9005"); - TEST_NISO_EXIF_MD.setFNumber(EXPECTED_EXIT_FNUMBER); - TEST_NISO_EXIF_MD.setExposureProgram(2); - TEST_NISO_EXIF_MD.setExifVersion(EXPECTED_EXIF_VERSION); - Rational r228 = new Rational(EXPECTED_EXIF_APERTURE_NUM, - EXPECTED_EXIF_APERTURE_DEN); - TEST_NISO_EXIF_MD.setMaxApertureValue(r228); - TEST_NISO_EXIF_MD.setMeteringMode(2); - TEST_NISO_EXIF_MD.setFocalLength(EXPECTED_EXIF_FOCAL); - TEST_NISO_EXIF_MD.setSamplingFrequencyUnit(2); - Rational r72 = new Rational(EXPECTED_EXIF_IMAGE_RESOLUTION, 1); - TEST_NISO_EXIF_MD.setXSamplingFrequency(r72); - TEST_NISO_EXIF_MD.setYSamplingFrequency(r72); - TEST_NISO_EXIF_MD.setBitsPerSample(new int[] { EXPECTED_BYTE_SIZE, - EXPECTED_BYTE_SIZE, EXPECTED_BYTE_SIZE }); - TEST_NISO_EXIF_MD.setSamplesPerPixel(EXPECTED_NUMBER_OF_BYTES); - - // Define the test instance for TextMD to be serialized - TEST_TEXTMD = new TextMDMetadata(); - TEST_TEXTMD.setCharset(TextMDMetadata.CHARSET_UTF8); - TEST_TEXTMD.setByte_order(TextMDMetadata.BYTE_ORDER_LITTLE); - TEST_TEXTMD.setByte_size("" + EXPECTED_BYTE_SIZE); - TEST_TEXTMD.setCharacter_size("variable"); - TEST_TEXTMD.setLinebreak(TextMDMetadata.LINEBREAK_LF); - TEST_TEXTMD.setMarkup_basis("XML"); - TEST_TEXTMD.setMarkup_basis_version("1.0"); - TEST_TEXTMD - .setMarkup_language("http://www.web3d.org/specifications/x3d-3.1.xsd"); - TEST_TEXTMD.setMarkup_language_version("3.1"); - } - - @Before - public void setUp() throws IOException { - // Restore the sample - Rational r0 = new Rational(0, 1); - TEST_NISO_IMAGE2_MD.setBrightness(r0); - - // Prepare for a new test - this.outputFile = File.createTempFile("jhove_", ".xml"); - PrintWriter writer = new PrintWriter(outputFile); - this.handler = new XmlHandler(); - this.handler.setWriter(writer); - } - - @After - public void tearDown() { - if (this.outputFile != null && this.outputFile.exists()) { - this.outputFile.delete(); - } - } - - @Test - public void testShowNisoImageMetadata02() throws IOException { - File mix02File = new File(this.getClass() - .getResource("mix02_output.xml").getPath()); - LOGGER.info("testShowNisoImageMetadata02 with file " + mix02File); - assertTrue(mix02File.isFile()); - String expectedMix02 = readXmlFile(mix02File); - - this.handler.showNisoImageMetadata02(TEST_NISO_IMAGE_MD); - this.handler.close(); - - String generatedMix = readXmlFile(outputFile); - assertEquals("Mix v0.2 generated not conformant", expectedMix02, - generatedMix); - } - - @Test - public void testShowNisoImageMetadata10() throws IOException { - File mix10File = new File(this.getClass() - .getResource("mix10_output.xml").getPath()); - LOGGER.info("testShowNisoImageMetadata10 with file " + mix10File); - assertTrue(mix10File.isFile()); - String expectedMix10 = readXmlFile(mix10File); - - this.handler.showNisoImageMetadata10(TEST_NISO_IMAGE_MD); - this.handler.close(); - - String generatedMix = readXmlFile(outputFile); - assertEquals("Mix v1.0 generated not conformant", expectedMix10, - generatedMix); - } - - @Test - public void testShowNisoImageMetadata20() throws IOException { - File mix20File = new File(this.getClass() - .getResource("mix20_output.xml").getPath()); - LOGGER.info("testShowNisoImageMetadata20 with file " + mix20File); - assertTrue(mix20File.isFile()); - String expectedMix20 = readXmlFile(mix20File); - - this.handler.showNisoImageMetadata20(TEST_NISO_IMAGE_MD); - this.handler.close(); - - String generatedMix = readXmlFile(outputFile); - assertEquals("Mix v2.0 generated not conformant", expectedMix20, - generatedMix); - } - - @Test - public void testShowNisoImage2Metadata02() throws IOException { - File mix02File = new File(this.getClass() - .getResource("mix02_output2.xml").getPath()); - LOGGER.info("testShowNisoImage2Metadata02 with file " + mix02File); - assertTrue(mix02File.isFile()); - String expectedMix02 = readXmlFile(mix02File); - - this.handler.showNisoImageMetadata02(TEST_NISO_IMAGE2_MD); - this.handler.close(); - - String generatedMix = readXmlFile(outputFile); - assertEquals("Mix v0.2 generated not conformant", expectedMix02, - generatedMix); - } - - @Test - public void testShowNisoImage2Metadata10() throws IOException { - File mix10File = new File(this.getClass() - .getResource("mix10_output2.xml").getPath()); - LOGGER.info("testShowNisoImage2Metadata10 with file " + mix10File); - assertTrue(mix10File.isFile()); - String expectedMix10 = readXmlFile(mix10File); - - this.handler.showNisoImageMetadata10(TEST_NISO_IMAGE2_MD); - this.handler.close(); - - String generatedMix = readXmlFile(outputFile); - assertEquals("Mix v1.0 generated not conformant", expectedMix10, - generatedMix); - } - - @Test - public void testShowNisoImage2Metadata20() throws IOException { - File mix20File = new File(this.getClass() - .getResource("mix20_output2.xml").getPath()); - LOGGER.info("testShowNisoImage2Metadata20 with file " + mix20File); - assertTrue(mix20File.isFile()); - String expectedMix20 = readXmlFile(mix20File); - - this.handler.showNisoImageMetadata20(TEST_NISO_IMAGE2_MD); - this.handler.close(); - - String generatedMix = readXmlFile(outputFile); - assertEquals("Mix v2.0 generated not conformant", expectedMix20, - generatedMix); - } - - @Test - public void testShowNisoImage2Issue502() throws IOException { - File mix20File = new File(this.getClass() - .getResource("mix20_output3.xml").getPath()); - LOGGER.info("testShowNisoImage2Issue502 with file " + mix20File); - assertTrue(mix20File.isFile()); - String expectedMix20 = readXmlFile(mix20File); - TEST_NISO_IMAGE2_MD.setBrightness(null); - this.handler.showNisoImageMetadata20(TEST_NISO_IMAGE2_MD); - this.handler.close(); - - String generatedMix = readXmlFile(outputFile); - assertEquals("Mix v2.0 generated not conformant", expectedMix20, - generatedMix); - } - - @Test - public void testShowNisoExifMetadata02() throws IOException { - File mix02File = new File(this.getClass().getResource("exif_mix02.xml") - .getPath()); - LOGGER.info("testShowNisoExifMetadata02 with file " + mix02File); - assertTrue(mix02File.isFile()); - String expectedMix02 = readXmlFile(mix02File); - - this.handler.showNisoImageMetadata02(TEST_NISO_EXIF_MD); - this.handler.close(); - - String generatedMix = readXmlFile(outputFile); - assertEquals("Exif Mix v0.2 generated not conformant", expectedMix02, - generatedMix); - } - - @Test - public void testShowNisoExifMetadata10() throws IOException { - File mix10File = new File(this.getClass().getResource("exif_mix10.xml") - .getPath()); - LOGGER.info("testShowNisoExifMetadata10 with file " + mix10File); - assertTrue(mix10File.isFile()); - String expectedMix10 = readXmlFile(mix10File); - - this.handler.showNisoImageMetadata10(TEST_NISO_EXIF_MD); - this.handler.close(); - - String generatedMix = readXmlFile(outputFile); - assertEquals("Exif Mix v1.0 generated not conformant", expectedMix10, - generatedMix); - } - - @Test - public void testShowNisoExifMetadata20() throws IOException { - File mix20File = new File(this.getClass().getResource("exif_mix20.xml") - .getPath()); - LOGGER.info("testShowNisoExifMetadata20 with file " + mix20File); - assertTrue(mix20File.isFile()); - String expectedMix20 = readXmlFile(mix20File); - - this.handler.showNisoImageMetadata20(TEST_NISO_EXIF_MD); - this.handler.close(); - - String generatedMix = readXmlFile(outputFile); - assertEquals("Exif Mix v2.0 generated not conformant", expectedMix20, - generatedMix); - } - - @Test - public void testShowTextMDMetadata() throws IOException { - File textMD30File = new File(this.getClass() - .getResource("text30_output.xml").getPath()); - LOGGER.info("testShowTextMDMetadata with file " + textMD30File); - assertTrue(textMD30File.isFile()); - String expectedText30 = readXmlFile(textMD30File); - - this.handler.showTextMDMetadata(TEST_TEXTMD); - this.handler.close(); - - String generatedTextMD = readXmlFile(outputFile); - assertEquals("TextMD v3.0 generated not conformant", expectedText30, - generatedTextMD); - } - - /** - * Reads an XML file into an one line string and eliminates the multiple - * spaces. - * - * @param f - * the xml file - * @return one line with the content - * @throws IOException - */ - private static String readXmlFile(File f) throws IOException { - StringBuilder sb = new StringBuilder(); - try (BufferedReader br = new BufferedReader(new FileReader(f))) { - String line = br.readLine(); - while (line != null) { - sb.append(line); - line = br.readLine(); - } - } - return sb.toString().replaceAll("\\s+", " "); - } + private static final Logger LOGGER = Logger.getLogger(XmlHandlerTest.class.getName()); + + /* Values needed to generate the expected output */ + private static final int EXPECTED_IMAGE_WIDTH = 8192; + private static final int EXPECTED_IMAGE_RESOLUTION = 564; + private static final int EXPECTED_LAST_BYTE = 255; + private static final int EXPECTED_BYTE_SIZE = 8; + private static final int EXPECTED_NUMBER_OF_BYTES = 3; + private static final long EXPECTED_DENOMINATOR = 4294967295L; + private static final long EXPECTED_WP_NUMENATOR = 1343036288L; + private static final long EXPECTED_REDX_NUMENATOR = 2748779008L; + private static final long EXPECTED_REDY_NUMENATOR = 1417339264L; + private static final long EXPECTED_GREENX_NUMENATOR = 1288490240L; + private static final long EXPECTED_GREENY_NUMENATOR = 2576980480L; + private static final long EXPECTED_BLUEX_NUMENATOR = 644245120L; + private static final long EXPECTED_BLUEY_NUMENATOR = 257698032L; + + private static final int EXPECTED_IMAGE2_WIDTH = 1136; + private static final int EXPECTED_IMAGE2_LENGTH = 1846; + private static final double EXPECTED_IMAGE2_FNUMBER = 7.1; + private static final double EXPECTED_IMAGE2_EXPOSURE_TIME = 0.167; + private static final int EXPECTED_IMAGE2_EXPOSURE_PROGRAM = 3; + private static final int EXPECTED_IMAGE2_APERTURE_NUM = 925; + private static final int EXPECTED_IMAGE2_APERTURE_DEN = 256; + private static final double EXPECTED_IMAGE2_FOCAL = 14; + private static final int EXPECTED_IMAGE2_FLASH_NUM = 15; + private static final int EXPECTED_IMAGE2_FLASH_DEN = 100; + private static final int EXPECTED_IMAGE2_IMAGE_RESOLUTION = 180; + + private static final int EXPECTED_JPEG_COMPRESSION = 6; + private static final int EXPECTED_EXIF_COLORSPACE = 6; + private static final int EXPECTED_EXIF_IMAGE_LENGTH = 2322; + private static final int EXPECTED_EXIF_IMAGE_WIDTH = 4128; + private static final int EXPECTED_EXIF_IMAGE_RESOLUTION = 72; + private static final String EXPECTED_EXIF_VERSION = "0220"; + private static final double EXPECTED_EXIF_FOCAL = 4.13; + private static final int EXPECTED_EXIF_APERTURE_NUM = 228; + private static final int EXPECTED_EXIF_APERTURE_DEN = 100; + private static final double EXPECTED_EXIT_FNUMBER = 2.2; + + /* Test instances to be serialized */ + protected static NisoImageMetadata TEST_NISO_IMAGE_MD; + protected static NisoImageMetadata TEST_NISO_IMAGE2_MD; + protected static NisoImageMetadata TEST_NISO_EXIF_MD; + protected static TextMDMetadata TEST_TEXTMD; + + private File outputFile; + private XmlHandler handler; + + @BeforeClass + public static void setUpBeforeClass() { + // Define the test instance for NisoImageMetadata to be serialized + TEST_NISO_IMAGE_MD = new NisoImageMetadata(); + TEST_NISO_IMAGE_MD.setByteOrder(NisoImageMetadata.BYTEORDER[1]); + TEST_NISO_IMAGE_MD.setCompressionScheme(1); + TEST_NISO_IMAGE_MD.setImageWidth(EXPECTED_IMAGE_WIDTH); + TEST_NISO_IMAGE_MD.setImageLength(EXPECTED_IMAGE_WIDTH); + TEST_NISO_IMAGE_MD.setColorSpace(2); + TEST_NISO_IMAGE_MD.setProfileName("Adobe RGB (1998)"); + Rational r0 = new Rational(0, 1); + Rational r255 = new Rational(EXPECTED_LAST_BYTE, 1); + TEST_NISO_IMAGE_MD.setReferenceBlackWhite(new Rational[] {r0, r255, r0, r255, r0, r255}); + TEST_NISO_IMAGE_MD.setDateTimeCreated("2015-07-15T16:47:26"); + TEST_NISO_IMAGE_MD.setImageProducer("BnF"); + TEST_NISO_IMAGE_MD.setScanningSoftware("Archiver"); + TEST_NISO_IMAGE_MD.setOrientation(1); + TEST_NISO_IMAGE_MD.setSamplingFrequencyUnit(2); + TEST_NISO_IMAGE_MD.setXSamplingFrequency(new Rational(EXPECTED_IMAGE_RESOLUTION, 1)); + TEST_NISO_IMAGE_MD.setYSamplingFrequency(new Rational(EXPECTED_IMAGE_RESOLUTION, 1)); + TEST_NISO_IMAGE_MD.setBitsPerSample( + new int[] {EXPECTED_BYTE_SIZE, EXPECTED_BYTE_SIZE, EXPECTED_BYTE_SIZE}); + TEST_NISO_IMAGE_MD.setSamplesPerPixel(EXPECTED_NUMBER_OF_BYTES); + TEST_NISO_IMAGE_MD.setWhitePointXValue( + new Rational(EXPECTED_WP_NUMENATOR, EXPECTED_DENOMINATOR)); + TEST_NISO_IMAGE_MD.setWhitePointYValue( + new Rational(EXPECTED_WP_NUMENATOR, EXPECTED_DENOMINATOR)); + TEST_NISO_IMAGE_MD.setPrimaryChromaticitiesRedX( + new Rational(EXPECTED_REDX_NUMENATOR, EXPECTED_DENOMINATOR)); + TEST_NISO_IMAGE_MD.setPrimaryChromaticitiesRedY( + new Rational(EXPECTED_REDY_NUMENATOR, EXPECTED_DENOMINATOR)); + TEST_NISO_IMAGE_MD.setPrimaryChromaticitiesGreenX( + new Rational(EXPECTED_GREENX_NUMENATOR, EXPECTED_DENOMINATOR)); + TEST_NISO_IMAGE_MD.setPrimaryChromaticitiesGreenY( + new Rational(EXPECTED_GREENY_NUMENATOR, EXPECTED_DENOMINATOR)); + TEST_NISO_IMAGE_MD.setPrimaryChromaticitiesBlueX( + new Rational(EXPECTED_BLUEX_NUMENATOR, EXPECTED_DENOMINATOR)); + TEST_NISO_IMAGE_MD.setPrimaryChromaticitiesBlueY( + new Rational(EXPECTED_BLUEY_NUMENATOR, EXPECTED_DENOMINATOR)); + + // Define the test instance for NisoImageMetadata to be serialized + TEST_NISO_IMAGE2_MD = new NisoImageMetadata(); + TEST_NISO_IMAGE2_MD.setByteOrder(NisoImageMetadata.BYTEORDER[0]); + TEST_NISO_IMAGE2_MD.setCompressionScheme(EXPECTED_JPEG_COMPRESSION); + TEST_NISO_IMAGE2_MD.setImageWidth(EXPECTED_IMAGE2_WIDTH); + TEST_NISO_IMAGE2_MD.setImageLength(EXPECTED_IMAGE2_LENGTH); + TEST_NISO_IMAGE2_MD.setDateTimeCreated("2017-03-28T12:58:30"); + TEST_NISO_IMAGE2_MD.setDigitalCameraManufacturer("Panasonic"); + TEST_NISO_IMAGE2_MD.setDigitalCameraModelName("DMC-G6"); + TEST_NISO_IMAGE2_MD.setFNumber(EXPECTED_IMAGE2_FNUMBER); + TEST_NISO_IMAGE2_MD.setExposureTime(EXPECTED_IMAGE2_EXPOSURE_TIME); + TEST_NISO_IMAGE2_MD.setExposureProgram(EXPECTED_IMAGE2_EXPOSURE_PROGRAM); + TEST_NISO_IMAGE2_MD.setBrightness(r0); + TEST_NISO_IMAGE2_MD.setExposureBias(r0); + Rational r925 = new Rational(EXPECTED_IMAGE2_APERTURE_NUM, EXPECTED_IMAGE2_APERTURE_DEN); + TEST_NISO_IMAGE2_MD.setMaxApertureValue(r925); + TEST_NISO_IMAGE2_MD.setMeteringMode(2); + TEST_NISO_IMAGE2_MD.setFocalLength(EXPECTED_IMAGE2_FOCAL); + TEST_NISO_IMAGE2_MD.setFlash(1); + Rational r15 = new Rational(EXPECTED_IMAGE2_FLASH_NUM, EXPECTED_IMAGE2_FLASH_DEN); + TEST_NISO_IMAGE2_MD.setFlashEnergy(r15); + TEST_NISO_IMAGE2_MD.setSamplingFrequencyUnit(2); + Rational r180 = new Rational(EXPECTED_IMAGE2_IMAGE_RESOLUTION, 1); + TEST_NISO_IMAGE2_MD.setXSamplingFrequency(r180); + TEST_NISO_IMAGE2_MD.setYSamplingFrequency(r180); + TEST_NISO_IMAGE2_MD.setBitsPerSample( + new int[] {EXPECTED_BYTE_SIZE, EXPECTED_BYTE_SIZE, EXPECTED_BYTE_SIZE}); + TEST_NISO_IMAGE2_MD.setSamplesPerPixel(EXPECTED_NUMBER_OF_BYTES); + + // Define the test instance of Exif for NisoImageMetadata to be + // serialized + TEST_NISO_EXIF_MD = new NisoImageMetadata(); + TEST_NISO_EXIF_MD.setByteOrder(NisoImageMetadata.BYTEORDER[0]); + TEST_NISO_EXIF_MD.setCompressionScheme(EXPECTED_JPEG_COMPRESSION); + TEST_NISO_EXIF_MD.setImageWidth(EXPECTED_EXIF_IMAGE_WIDTH); + TEST_NISO_EXIF_MD.setImageLength(EXPECTED_EXIF_IMAGE_LENGTH); + TEST_NISO_EXIF_MD.setColorSpace(EXPECTED_EXIF_COLORSPACE); + TEST_NISO_EXIF_MD.setYCbCrPositioning(1); + TEST_NISO_EXIF_MD.setDateTimeCreated("2015-02-13T14:06:36"); + TEST_NISO_EXIF_MD.setDigitalCameraManufacturer("SAMSUNG"); + TEST_NISO_EXIF_MD.setDigitalCameraModelName("SM-N9005"); + TEST_NISO_EXIF_MD.setFNumber(EXPECTED_EXIT_FNUMBER); + TEST_NISO_EXIF_MD.setExposureProgram(2); + TEST_NISO_EXIF_MD.setExifVersion(EXPECTED_EXIF_VERSION); + Rational r228 = new Rational(EXPECTED_EXIF_APERTURE_NUM, EXPECTED_EXIF_APERTURE_DEN); + TEST_NISO_EXIF_MD.setMaxApertureValue(r228); + TEST_NISO_EXIF_MD.setMeteringMode(2); + TEST_NISO_EXIF_MD.setFocalLength(EXPECTED_EXIF_FOCAL); + TEST_NISO_EXIF_MD.setSamplingFrequencyUnit(2); + Rational r72 = new Rational(EXPECTED_EXIF_IMAGE_RESOLUTION, 1); + TEST_NISO_EXIF_MD.setXSamplingFrequency(r72); + TEST_NISO_EXIF_MD.setYSamplingFrequency(r72); + TEST_NISO_EXIF_MD.setBitsPerSample( + new int[] {EXPECTED_BYTE_SIZE, EXPECTED_BYTE_SIZE, EXPECTED_BYTE_SIZE}); + TEST_NISO_EXIF_MD.setSamplesPerPixel(EXPECTED_NUMBER_OF_BYTES); + + // Define the test instance for TextMD to be serialized + TEST_TEXTMD = new TextMDMetadata(); + TEST_TEXTMD.setCharset(TextMDMetadata.CHARSET_UTF8); + TEST_TEXTMD.setByte_order(TextMDMetadata.BYTE_ORDER_LITTLE); + TEST_TEXTMD.setByte_size("" + EXPECTED_BYTE_SIZE); + TEST_TEXTMD.setCharacter_size("variable"); + TEST_TEXTMD.setLinebreak(TextMDMetadata.LINEBREAK_LF); + TEST_TEXTMD.setMarkup_basis("XML"); + TEST_TEXTMD.setMarkup_basis_version("1.0"); + TEST_TEXTMD.setMarkup_language("http://www.web3d.org/specifications/x3d-3.1.xsd"); + TEST_TEXTMD.setMarkup_language_version("3.1"); + } + + @Before + public void setUp() throws IOException { + // Restore the sample + Rational r0 = new Rational(0, 1); + TEST_NISO_IMAGE2_MD.setBrightness(r0); + + // Prepare for a new test + this.outputFile = File.createTempFile("jhove_", ".xml"); + PrintWriter writer = new PrintWriter(outputFile); + this.handler = new XmlHandler(); + this.handler.setWriter(writer); + } + + @After + public void tearDown() { + if (this.outputFile != null && this.outputFile.exists()) { + this.outputFile.delete(); + } + } + + @Test + public void testShowNisoImageMetadata02() throws IOException { + File mix02File = new File(this.getClass().getResource("mix02_output.xml").getPath()); + LOGGER.info("testShowNisoImageMetadata02 with file " + mix02File); + assertTrue(mix02File.isFile()); + String expectedMix02 = readXmlFile(mix02File); + + this.handler.showNisoImageMetadata02(TEST_NISO_IMAGE_MD); + this.handler.close(); + + String generatedMix = readXmlFile(outputFile); + assertEquals("Mix v0.2 generated not conformant", expectedMix02, generatedMix); + } + + @Test + public void testShowNisoImageMetadata10() throws IOException { + File mix10File = new File(this.getClass().getResource("mix10_output.xml").getPath()); + LOGGER.info("testShowNisoImageMetadata10 with file " + mix10File); + assertTrue(mix10File.isFile()); + String expectedMix10 = readXmlFile(mix10File); + + this.handler.showNisoImageMetadata10(TEST_NISO_IMAGE_MD); + this.handler.close(); + + String generatedMix = readXmlFile(outputFile); + assertEquals("Mix v1.0 generated not conformant", expectedMix10, generatedMix); + } + + @Test + public void testShowNisoImageMetadata20() throws IOException { + File mix20File = new File(this.getClass().getResource("mix20_output.xml").getPath()); + LOGGER.info("testShowNisoImageMetadata20 with file " + mix20File); + assertTrue(mix20File.isFile()); + String expectedMix20 = readXmlFile(mix20File); + + this.handler.showNisoImageMetadata20(TEST_NISO_IMAGE_MD); + this.handler.close(); + + String generatedMix = readXmlFile(outputFile); + assertEquals("Mix v2.0 generated not conformant", expectedMix20, generatedMix); + } + + @Test + public void testShowNisoImage2Metadata02() throws IOException { + File mix02File = new File(this.getClass().getResource("mix02_output2.xml").getPath()); + LOGGER.info("testShowNisoImage2Metadata02 with file " + mix02File); + assertTrue(mix02File.isFile()); + String expectedMix02 = readXmlFile(mix02File); + + this.handler.showNisoImageMetadata02(TEST_NISO_IMAGE2_MD); + this.handler.close(); + + String generatedMix = readXmlFile(outputFile); + assertEquals("Mix v0.2 generated not conformant", expectedMix02, generatedMix); + } + + @Test + public void testShowNisoImage2Metadata10() throws IOException { + File mix10File = new File(this.getClass().getResource("mix10_output2.xml").getPath()); + LOGGER.info("testShowNisoImage2Metadata10 with file " + mix10File); + assertTrue(mix10File.isFile()); + String expectedMix10 = readXmlFile(mix10File); + + this.handler.showNisoImageMetadata10(TEST_NISO_IMAGE2_MD); + this.handler.close(); + + String generatedMix = readXmlFile(outputFile); + assertEquals("Mix v1.0 generated not conformant", expectedMix10, generatedMix); + } + + @Test + public void testShowNisoImage2Metadata20() throws IOException { + File mix20File = new File(this.getClass().getResource("mix20_output2.xml").getPath()); + LOGGER.info("testShowNisoImage2Metadata20 with file " + mix20File); + assertTrue(mix20File.isFile()); + String expectedMix20 = readXmlFile(mix20File); + + this.handler.showNisoImageMetadata20(TEST_NISO_IMAGE2_MD); + this.handler.close(); + + String generatedMix = readXmlFile(outputFile); + assertEquals("Mix v2.0 generated not conformant", expectedMix20, generatedMix); + } + + @Test + public void testShowNisoImage2Issue502() throws IOException { + File mix20File = new File(this.getClass().getResource("mix20_output3.xml").getPath()); + LOGGER.info("testShowNisoImage2Issue502 with file " + mix20File); + assertTrue(mix20File.isFile()); + String expectedMix20 = readXmlFile(mix20File); + TEST_NISO_IMAGE2_MD.setBrightness(null); + this.handler.showNisoImageMetadata20(TEST_NISO_IMAGE2_MD); + this.handler.close(); + + String generatedMix = readXmlFile(outputFile); + assertEquals("Mix v2.0 generated not conformant", expectedMix20, generatedMix); + } + + @Test + public void testShowNisoExifMetadata02() throws IOException { + File mix02File = new File(this.getClass().getResource("exif_mix02.xml").getPath()); + LOGGER.info("testShowNisoExifMetadata02 with file " + mix02File); + assertTrue(mix02File.isFile()); + String expectedMix02 = readXmlFile(mix02File); + + this.handler.showNisoImageMetadata02(TEST_NISO_EXIF_MD); + this.handler.close(); + + String generatedMix = readXmlFile(outputFile); + assertEquals("Exif Mix v0.2 generated not conformant", expectedMix02, generatedMix); + } + + @Test + public void testShowNisoExifMetadata10() throws IOException { + File mix10File = new File(this.getClass().getResource("exif_mix10.xml").getPath()); + LOGGER.info("testShowNisoExifMetadata10 with file " + mix10File); + assertTrue(mix10File.isFile()); + String expectedMix10 = readXmlFile(mix10File); + + this.handler.showNisoImageMetadata10(TEST_NISO_EXIF_MD); + this.handler.close(); + + String generatedMix = readXmlFile(outputFile); + assertEquals("Exif Mix v1.0 generated not conformant", expectedMix10, generatedMix); + } + + @Test + public void testShowNisoExifMetadata20() throws IOException { + File mix20File = new File(this.getClass().getResource("exif_mix20.xml").getPath()); + LOGGER.info("testShowNisoExifMetadata20 with file " + mix20File); + assertTrue(mix20File.isFile()); + String expectedMix20 = readXmlFile(mix20File); + + this.handler.showNisoImageMetadata20(TEST_NISO_EXIF_MD); + this.handler.close(); + + String generatedMix = readXmlFile(outputFile); + assertEquals("Exif Mix v2.0 generated not conformant", expectedMix20, generatedMix); + } + + @Test + public void testShowTextMDMetadata() throws IOException { + File textMD30File = new File(this.getClass().getResource("text30_output.xml").getPath()); + LOGGER.info("testShowTextMDMetadata with file " + textMD30File); + assertTrue(textMD30File.isFile()); + String expectedText30 = readXmlFile(textMD30File); + + this.handler.showTextMDMetadata(TEST_TEXTMD); + this.handler.close(); + + String generatedTextMD = readXmlFile(outputFile); + assertEquals("TextMD v3.0 generated not conformant", expectedText30, generatedTextMD); + } + + /** + * Reads an XML file into an one line string and eliminates the multiple spaces. + * + * @param f the xml file + * @return one line with the content + * @throws IOException + */ + private static String readXmlFile(File f) throws IOException { + StringBuilder sb = new StringBuilder(); + try (BufferedReader br = new BufferedReader(new FileReader(f))) { + String line = br.readLine(); + while (line != null) { + sb.append(line); + line = br.readLine(); + } + } + return sb.toString().replaceAll("\\s+", " "); + } } diff --git a/jhove-core/src/test/java/org/openpreservation/jhove/ReleaseDetailsTest.java b/jhove-core/src/test/java/org/openpreservation/jhove/ReleaseDetailsTest.java index 76d2cc771..4c800f9ab 100644 --- a/jhove-core/src/test/java/org/openpreservation/jhove/ReleaseDetailsTest.java +++ b/jhove-core/src/test/java/org/openpreservation/jhove/ReleaseDetailsTest.java @@ -1,58 +1,45 @@ -/** - * - */ +/** */ package org.openpreservation.jhove; -import nl.jqno.equalsverifier.EqualsVerifier; -import org.junit.Test; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertSame; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.Date; +import nl.jqno.equalsverifier.EqualsVerifier; +import org.junit.Test; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertSame; - -/** - * @author Carl Wilson - */ +/** @author Carl Wilson */ public class ReleaseDetailsTest { - /** - * Test method for {@link ReleaseDetails#getInstance()}. - */ - @Test - public final void testGetInstance() { - ReleaseDetails instance = ReleaseDetails.getInstance(); - ReleaseDetails secondInstance = ReleaseDetails.getInstance(); - assertSame(instance, secondInstance); - } - - /** - * Test method for {@link ReleaseDetails#getVersion()}. - */ - @Test - public final void testGetVersion() { - ReleaseDetails instance = ReleaseDetails.getInstance(); - assertEquals(instance.getVersion(), "0.1.2-TESTER"); - } - - /** - * Test method for {@link ReleaseDetails#getBuildDate()}. - */ - @Test - public final void testGetBuildDate() throws ParseException { - ReleaseDetails instance = ReleaseDetails.getInstance(); - SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd"); - Date date = formatter.parse("2011-07-31"); - assertEquals(instance.getBuildDate(), date); - } - - /** - * Test the hash and equals contract for the class using EqualsVerifier. - */ - @Test - public void testEqualsContract() { - EqualsVerifier.forClass(ReleaseDetails.class).verify(); - } + /** Test method for {@link ReleaseDetails#getInstance()}. */ + @Test + public final void testGetInstance() { + ReleaseDetails instance = ReleaseDetails.getInstance(); + ReleaseDetails secondInstance = ReleaseDetails.getInstance(); + assertSame(instance, secondInstance); + } + + /** Test method for {@link ReleaseDetails#getVersion()}. */ + @Test + public final void testGetVersion() { + ReleaseDetails instance = ReleaseDetails.getInstance(); + assertEquals(instance.getVersion(), "0.1.2-TESTER"); + } + + /** Test method for {@link ReleaseDetails#getBuildDate()}. */ + @Test + public final void testGetBuildDate() throws ParseException { + ReleaseDetails instance = ReleaseDetails.getInstance(); + SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd"); + Date date = formatter.parse("2011-07-31"); + assertEquals(instance.getBuildDate(), date); + } + + /** Test the hash and equals contract for the class using EqualsVerifier. */ + @Test + public void testEqualsContract() { + EqualsVerifier.forClass(ReleaseDetails.class).verify(); + } } diff --git a/jhove-ext-modules/src/main/java/com/mcgath/jhove/module/PngModule.java b/jhove-ext-modules/src/main/java/com/mcgath/jhove/module/PngModule.java index 60fdd07cc..e956f62a9 100644 --- a/jhove-ext-modules/src/main/java/com/mcgath/jhove/module/PngModule.java +++ b/jhove-ext-modules/src/main/java/com/mcgath/jhove/module/PngModule.java @@ -1,5 +1,9 @@ package com.mcgath.jhove.module; +import com.mcgath.jhove.module.png.*; +import com.mcgath.jhove.module.png.IdatChunk; +import com.mcgath.jhove.module.png.PNGChunk; +import edu.harvard.hul.ois.jhove.*; import java.io.*; import java.util.ArrayList; import java.util.HashSet; @@ -7,503 +11,441 @@ import java.util.List; import java.util.Set; -import com.mcgath.jhove.module.png.IdatChunk; -import com.mcgath.jhove.module.png.PNGChunk; - -import com.mcgath.jhove.module.png.*; -//import java.util.Vector; - -import edu.harvard.hul.ois.jhove.*; - /** - * Module for validation and metadata extraction on PNG files. - * - * @author Gary McGath - * - * An earlier PNG module was submitted by Gian Uberto Lauri. - * A few bits of code are copied from it. - * It validates files but doesn't do much metadata - * extraction. I noticed it only after making a significant - * start on my own version. -- GDM + * Module for validation and metadata extraction on PNG files. + * + * @author Gary McGath + *

An earlier PNG module was submitted by Gian Uberto Lauri. A few bits of code are copied + * from it. It validates files but doesn't do much metadata extraction. I noticed it only after + * making a significant start on my own version. -- GDM */ public class PngModule extends ModuleBase { - /** - * What would constitute a well-formed but invalid PNG? - */ - /****************************************************************** - * DEBUGGING FIELDS. - * All debugging fields should be set to false for release code. - ******************************************************************/ - - - /****************************************************************** - * PRIVATE CLASS FIELDS. - ******************************************************************/ - - private static final String NAME = "PNG-gdm"; - private static final String RELEASE = "1.0"; - private static final int [] DATE = {2016, 2, 25}; - private static final String [] FORMAT = { - "PNG", " ISO/IEC 15948:2003", "Portable Network Graphics" - }; - private static final String COVERAGE = - "PNG (ISO/IEC 15948:2003)"; - private static final String [] MIMETYPE = {"image/png"}; - //private static final String [] ALT_MIMETYPE = {"image/x-png"}; - private static final String WELLFORMED = "Put well-formedness criteria here"; - private static final String VALIDITY = "Put validity criteria here"; - private static final String REPINFO = "Put repinfo note here"; - private static final String NOTE = null; - private static final String RIGHTS = "Copyright 2016 by Gary McGath. " + - "Released under the GNU Lesser General Public License."; - private static final String NISO_IMAGE_MD = "NisoImageMetadata"; - - /* Checksummer object */ - protected Checksummer _ckSummer; - - /* Top-level property list */ - protected List _propList; - - /* List of keyword properties. */ - protected List _keywordPropList; - - /* List of suggested palette properties. */ - protected List _spltList; - - /* Input stream wrapper which handles checksums */ - protected ChecksumInputStream _cstream; - - /* Data input stream wrapped around _cstream */ - protected DataInputStream _dstream; - - /* Top-level metadata property */ - protected Property _metadata; - - /* NISO metadata for the image */ - NisoImageMetadata _nisoData; - - /* Color type. Some chunks need to know this. */ - protected int _colorType; - - /* Set of ancillary chunks which aren't allowed to be duplicated - * and have been encountered. - */ - private Set _ancillaryChunks; - - - /* Critical chunk flags. */ - private boolean ihdrSeen; // IHDR chunk has been seen - private boolean plteSeen; // PLTE chunk has been seen - private boolean idatSeen; // at least one IDAT chunk has been seen - private boolean idatFinished; // a different chunk has been seen after an IDAT chunk - private boolean iendSeen; // IEND chunk has been seen - - /* File signature bytes, found at the beginning of every well-formed PNG files. */ - private final static int _sigBytes[] = { 137, 80, 78, 71, 13, 10, 26, 10 }; - - /****************************************************************** - * CLASS CONSTRUCTOR. - ******************************************************************/ - /** - * Instantiate a PngModule object. - */ - public PngModule() { - super (NAME, RELEASE, DATE, FORMAT, COVERAGE, MIMETYPE, WELLFORMED, - VALIDITY, REPINFO, NOTE, RIGHTS, false); - Signature sig = - new InternalSignature (_sigBytes, SignatureType.MAGIC, - SignatureUseType.MANDATORY, 0, - ""); - _signature.add (sig); - sig = new ExternalSignature (".png", SignatureType.EXTENSION, - SignatureUseType.OPTIONAL); - _signature.add (sig); - } - - /****************************************************************** - * Parsing methods. - ******************************************************************/ - - /** - * Check if the digital object conforms to this Module's - * internal signature information. - * - * @param file A RandomAccessFile, positioned at its beginning, - * which is generated from the object to be parsed - * @param stream An InputStream, positioned at its beginning, - * which is generated from the object to be parsed - * @param info A fresh RepInfo object which will be modified - * to reflect the results of the test - */ - @Override - public void checkSignatures (File file, InputStream stream, RepInfo info) - { - int i; - int ch; - _dstream = getBufferedDataStream (stream, _je != null ? - _je.getBufferSize () : 0); - for (i = 0; i < 8; i++) { - try { - ch = readUnsignedByte(_dstream, this); - } - catch (Exception e) { - ch = -1; - } - if (ch != _sigBytes[i]) { - info.setWellFormed (false); - return; - } - } - info.setModule (this); - info.setFormat (_format[0]); - info.setMimeType (_mimeType[0]); - info.setSigMatch(_name); + /** What would constitute a well-formed but invalid PNG? */ + /** + * **************************************************************** DEBUGGING FIELDS. All + * debugging fields should be set to false for release code. + * **************************************************************** + */ + + /** + * **************************************************************** PRIVATE CLASS FIELDS. + * **************************************************************** + */ + private static final String NAME = "PNG-gdm"; + + private static final String RELEASE = "1.0"; + private static final int[] DATE = {2016, 2, 25}; + private static final String[] FORMAT = { + "PNG", " ISO/IEC 15948:2003", "Portable Network Graphics" + }; + private static final String COVERAGE = "PNG (ISO/IEC 15948:2003)"; + private static final String[] MIMETYPE = {"image/png"}; + // private static final String [] ALT_MIMETYPE = {"image/x-png"}; + private static final String WELLFORMED = "Put well-formedness criteria here"; + private static final String VALIDITY = "Put validity criteria here"; + private static final String REPINFO = "Put repinfo note here"; + private static final String NOTE = null; + private static final String RIGHTS = + "Copyright 2016 by Gary McGath. " + "Released under the GNU Lesser General Public License."; + private static final String NISO_IMAGE_MD = "NisoImageMetadata"; + + /* Checksummer object */ + protected Checksummer _ckSummer; + + /* Top-level property list */ + protected List _propList; + + /* List of keyword properties. */ + protected List _keywordPropList; + + /* List of suggested palette properties. */ + protected List _spltList; + + /* Input stream wrapper which handles checksums */ + protected ChecksumInputStream _cstream; + + /* Data input stream wrapped around _cstream */ + protected DataInputStream _dstream; + + /* Top-level metadata property */ + protected Property _metadata; + + /* NISO metadata for the image */ + NisoImageMetadata _nisoData; + + /* Color type. Some chunks need to know this. */ + protected int _colorType; + + /* Set of ancillary chunks which aren't allowed to be duplicated + * and have been encountered. + */ + private Set _ancillaryChunks; + + /* Critical chunk flags. */ + private boolean ihdrSeen; // IHDR chunk has been seen + private boolean plteSeen; // PLTE chunk has been seen + private boolean idatSeen; // at least one IDAT chunk has been seen + private boolean idatFinished; // a different chunk has been seen after an IDAT chunk + private boolean iendSeen; // IEND chunk has been seen + + /* File signature bytes, found at the beginning of every well-formed PNG files. */ + private static final int _sigBytes[] = {137, 80, 78, 71, 13, 10, 26, 10}; + + /** + * **************************************************************** CLASS CONSTRUCTOR. + * **************************************************************** + */ + /** Instantiate a PngModule object. */ + public PngModule() { + super( + NAME, + RELEASE, + DATE, + FORMAT, + COVERAGE, + MIMETYPE, + WELLFORMED, + VALIDITY, + REPINFO, + NOTE, + RIGHTS, + false); + Signature sig = + new InternalSignature(_sigBytes, SignatureType.MAGIC, SignatureUseType.MANDATORY, 0, ""); + _signature.add(sig); + sig = new ExternalSignature(".png", SignatureType.EXTENSION, SignatureUseType.OPTIONAL); + _signature.add(sig); + } + + /** + * **************************************************************** Parsing methods. + * **************************************************************** + */ + + /** + * Check if the digital object conforms to this Module's internal signature information. + * + * @param file A RandomAccessFile, positioned at its beginning, which is generated from the object + * to be parsed + * @param stream An InputStream, positioned at its beginning, which is generated from the object + * to be parsed + * @param info A fresh RepInfo object which will be modified to reflect the results of the test + */ + @Override + public void checkSignatures(File file, InputStream stream, RepInfo info) { + int i; + int ch; + _dstream = getBufferedDataStream(stream, _je != null ? _je.getBufferSize() : 0); + for (i = 0; i < 8; i++) { + try { + ch = readUnsignedByte(_dstream, this); + } catch (Exception e) { + ch = -1; + } + if (ch != _sigBytes[i]) { + info.setWellFormed(false); + return; + } } + info.setModule(this); + info.setFormat(_format[0]); + info.setMimeType(_mimeType[0]); + info.setSigMatch(_name); + } - - /** - * Parse the content of a purported JPEG stream digital object and store the - * results in RepInfo. - * - * This function uses the JPEG-L method of detecting a marker following - * a data stream, checking for a 0 high bit rather than an entire 0 - * byte. So long at no JPEG markers are defined with a value from 0 - * through 7F, this is valid for all JPEG files. - * - * @param stream An InputStream, positioned at its beginning, - * which is generated from the object to be parsed - * @param info A fresh RepInfo object which will be modified - * to reflect the results of the parsing - * @param parseIndex Must be 0 in first call to parse. If - * parse returns a nonzero value, it must be - * called again with parseIndex - * equal to that return value. - */ - @Override - public int parse (InputStream stream, RepInfo info, int parseIndex) - throws IOException - { - initParse (); - info.setFormat (_format[0]); - info.setMimeType (_mimeType[0]); - info.setModule (this); - /* We may have already done the checksums while converting a - temporary file. */ - _ckSummer = null; - if (_je != null && _je.getChecksumFlag () && - info.getChecksum ().size () == 0) { - _ckSummer = new Checksummer (); - _cstream = new ChecksumInputStream (stream, _ckSummer); - _dstream = getBufferedDataStream (_cstream, _je != null ? - _je.getBufferSize () : 0); - } - else { - _dstream = getBufferedDataStream (stream, _je != null ? - _je.getBufferSize () : 0); - } - _propList = new LinkedList<> (); - _metadata = new Property ("PNGMetadata", - PropertyType.PROPERTY, - PropertyArity.LIST, - _propList); - _nisoData = new NisoImageMetadata(); - Property nisoProp = new Property(NISO_IMAGE_MD, - PropertyType.NISOIMAGEMETADATA, _nisoData); - _propList.add(nisoProp); - _keywordPropList = new LinkedList<> (); - _spltList = new LinkedList<> (); - ErrorMessage msg; - - // Check that the file header matching the PNG magic numbers - for (int i = 0; i < _sigBytes.length; i++) { - int byt = readUnsignedByte (_dstream); - if (byt != _sigBytes[i]) { - msg = new ErrorMessage(MessageConstants.PNG_GDM_66); - info.setMessage(msg); - info.setWellFormed(false); - return 0; - } - } - - // Loop through the chunks - try { - for (;;) { - PNGChunk chunk = readChunkHead(_dstream); - if (chunk == null) { - break; - } - if (iendSeen) { - msg = new ErrorMessage (MessageConstants.PNG_GDM_67); - info.setMessage (msg); - info.setWellFormed (false); - return 0; - } - if (idatSeen && !(chunk instanceof IdatChunk)) { - idatFinished = true; - } - chunk.setModule(this); - chunk.setInputStream(_dstream); - chunk.setNisoMetadata(_nisoData); - chunk.setPropertyList(_propList); - chunk.processChunk(info); - long storedCRC = chunk.readCRC(); - long calculatedCRC = chunk.getCRC(); - if (storedCRC != calculatedCRC) { - msg = new ErrorMessage(String.format ( - MessageConstants.PNG_GDM_68.getMessage() , - chunk.chunkTypeString())); - info.setMessage(msg); - info.setWellFormed(false); - return 0; - } - } - } - catch (PNGException e) { - // We've already reported a problem, so we just clean up here. - return 0; - } - catch (EOFException e) { - msg = new ErrorMessage ( - String.format ( - MessageConstants.PNG_GDM_69.getMessage(), - _nByte)); - info.setMessage (msg); - info.setWellFormed (false); - return 0; - } - catch (Exception e) { - // Miscellaneous exceptions really shouldn't come here. - // But it's better to catch them than let them fall through. - // Treat them as bugs. - msg = new ErrorMessage ( - String.format ( - MessageConstants.PNG_GDM_70.getMessage() , - e.getClass().getName())); - info.setMessage (msg); - info.setWellFormed (false); - return 0; - } - - /* Check for required chunks. */ - boolean criticalMissing = false; - if (!ihdrSeen) { - msg = new ErrorMessage(MessageConstants.PNG_GDM_71); - info.setMessage (msg); - criticalMissing = true; - } - if (!idatSeen) { - msg = new ErrorMessage(MessageConstants.PNG_GDM_72); - info.setMessage (msg); - criticalMissing = true; + /** + * Parse the content of a purported JPEG stream digital object and store the results in RepInfo. + * + *

This function uses the JPEG-L method of detecting a marker following a data stream, checking + * for a 0 high bit rather than an entire 0 byte. So long at no JPEG markers are defined with a + * value from 0 through 7F, this is valid for all JPEG files. + * + * @param stream An InputStream, positioned at its beginning, which is generated from the object + * to be parsed + * @param info A fresh RepInfo object which will be modified to reflect the results of the parsing + * @param parseIndex Must be 0 in first call to parse. If parse returns + * a nonzero value, it must be called again with parseIndex equal to that return + * value. + */ + @Override + public int parse(InputStream stream, RepInfo info, int parseIndex) throws IOException { + initParse(); + info.setFormat(_format[0]); + info.setMimeType(_mimeType[0]); + info.setModule(this); + /* We may have already done the checksums while converting a + temporary file. */ + _ckSummer = null; + if (_je != null && _je.getChecksumFlag() && info.getChecksum().size() == 0) { + _ckSummer = new Checksummer(); + _cstream = new ChecksumInputStream(stream, _ckSummer); + _dstream = getBufferedDataStream(_cstream, _je != null ? _je.getBufferSize() : 0); + } else { + _dstream = getBufferedDataStream(stream, _je != null ? _je.getBufferSize() : 0); + } + _propList = new LinkedList<>(); + _metadata = new Property("PNGMetadata", PropertyType.PROPERTY, PropertyArity.LIST, _propList); + _nisoData = new NisoImageMetadata(); + Property nisoProp = new Property(NISO_IMAGE_MD, PropertyType.NISOIMAGEMETADATA, _nisoData); + _propList.add(nisoProp); + _keywordPropList = new LinkedList<>(); + _spltList = new LinkedList<>(); + ErrorMessage msg; + + // Check that the file header matching the PNG magic numbers + for (int i = 0; i < _sigBytes.length; i++) { + int byt = readUnsignedByte(_dstream); + if (byt != _sigBytes[i]) { + msg = new ErrorMessage(MessageConstants.PNG_GDM_66); + info.setMessage(msg); + info.setWellFormed(false); + return 0; + } + } + + // Loop through the chunks + try { + for (; ; ) { + PNGChunk chunk = readChunkHead(_dstream); + if (chunk == null) { + break; } - if (!iendSeen) { - msg = new ErrorMessage(MessageConstants.PNG_GDM_73); - info.setMessage (msg); - criticalMissing = true; + if (iendSeen) { + msg = new ErrorMessage(MessageConstants.PNG_GDM_67); + info.setMessage(msg); + info.setWellFormed(false); + return 0; } - if (criticalMissing) { - info.setWellFormed (false); - return 0; - } - - /** PLTE is required with color type 3 and forbidden with types 0 and 4 */ - if (_colorType == 3 && !plteSeen) { - msg = new ErrorMessage (MessageConstants.PNG_GDM_74); - info.setMessage (msg); - info.setWellFormed (false); - return 0; + if (idatSeen && !(chunk instanceof IdatChunk)) { + idatFinished = true; } - - if ((_colorType == 0 || _colorType == 4) && plteSeen) { - msg = new ErrorMessage (MessageConstants.PNG_GDM_75); - info.setMessage (msg); - info.setWellFormed (false); - return 0; + chunk.setModule(this); + chunk.setInputStream(_dstream); + chunk.setNisoMetadata(_nisoData); + chunk.setPropertyList(_propList); + chunk.processChunk(info); + long storedCRC = chunk.readCRC(); + long calculatedCRC = chunk.getCRC(); + if (storedCRC != calculatedCRC) { + msg = + new ErrorMessage( + String.format(MessageConstants.PNG_GDM_68.getMessage(), chunk.chunkTypeString())); + info.setMessage(msg); + info.setWellFormed(false); + return 0; } + } + } catch (PNGException e) { + // We've already reported a problem, so we just clean up here. + return 0; + } catch (EOFException e) { + msg = new ErrorMessage(String.format(MessageConstants.PNG_GDM_69.getMessage(), _nByte)); + info.setMessage(msg); + info.setWellFormed(false); + return 0; + } catch (Exception e) { + // Miscellaneous exceptions really shouldn't come here. + // But it's better to catch them than let them fall through. + // Treat them as bugs. + msg = + new ErrorMessage( + String.format(MessageConstants.PNG_GDM_70.getMessage(), e.getClass().getName())); + info.setMessage(msg); + info.setWellFormed(false); + return 0; + } - // Add the keyword property list if it isn't empty. - if (!_keywordPropList.isEmpty()) { - _propList.add(new Property ("Keywords", - PropertyType.PROPERTY, - PropertyArity.LIST, - _keywordPropList)); - - } - - // Add the splat property list if it isn't empty. - if (!_spltList.isEmpty()) { - _propList.add(new Property ("Suggested palettes", - PropertyType.PROPERTY, - PropertyArity.LIST, - _spltList)); - } - info.setProperty (_metadata); - return 0; + /* Check for required chunks. */ + boolean criticalMissing = false; + if (!ihdrSeen) { + msg = new ErrorMessage(MessageConstants.PNG_GDM_71); + info.setMessage(msg); + criticalMissing = true; + } + if (!idatSeen) { + msg = new ErrorMessage(MessageConstants.PNG_GDM_72); + info.setMessage(msg); + criticalMissing = true; + } + if (!iendSeen) { + msg = new ErrorMessage(MessageConstants.PNG_GDM_73); + info.setMessage(msg); + criticalMissing = true; } - - /** This lets the module skip over the remainder of a chunk, not - * including the name, length,and CRC. It updates the CRC. */ - public void eatChunk(PNGChunk chnk) throws IOException { - chnk.skipBytes ((int) chnk.getLength()); + if (criticalMissing) { + info.setWellFormed(false); + return 0; } - /** Add a keyword and value. Creating arbitrary properties - * on the fly doesn't go well with JHOVE's approach, so - * we make each property a Map, with keys Keyword, Value, - * and optionally Language. - */ - public void addKeyword(String keywd, String val) { - addKeyword (keywd, null, val, null); - } - - /** Add a keyword, value, and language. */ - public void addKeyword(String keywd, String translatedKeywd, String val, String language) { - //HashMap map = new HashMap(); - List props = new ArrayList<>(); - Property prop = new Property ("Keyword", - PropertyType.PROPERTY, - PropertyArity.LIST, - props); - props.add (new Property ("Key", - PropertyType.STRING, - keywd)); - props.add (new Property ("Value", - PropertyType.STRING, - val)); - if (language != null) { - props.add (new Property ("Language", - PropertyType.STRING, - language)); - } - if (translatedKeywd != null) { - props.add (new Property ("Translated key", - PropertyType.STRING, - translatedKeywd)); - } - _keywordPropList.add(prop); - } - - /** Add a suggested palette */ - public void addSplt (String name, int sampleDepth, int numSamples) { - List props = new ArrayList<>(); - Property prop = new Property ("Suggested palette", - PropertyType.PROPERTY, - PropertyArity.LIST, - props); - props.add (new Property ("Name", - PropertyType.STRING, - name)); - props.add (new Property ("Sample depth", - PropertyType.INTEGER, - Integer.valueOf(sampleDepth))); - props.add (new Property ("Number of samples", - PropertyType.INTEGER, - Integer.valueOf(numSamples))); - _spltList.add (prop); - } - - - /** - * Initializes the state of the module for parsing. - */ - @Override - protected void initParse () - { - super.initParse (); - ihdrSeen = false; - plteSeen = false; - idatSeen = false; - idatFinished = false; - iendSeen = false; - _ancillaryChunks = new HashSet<>(); + /** PLTE is required with color type 3 and forbidden with types 0 and 4 */ + if (_colorType == 3 && !plteSeen) { + msg = new ErrorMessage(MessageConstants.PNG_GDM_74); + info.setMessage(msg); + info.setWellFormed(false); + return 0; } - - /* readChunkHead reads the type and length of a chunk and - * leaves the rest to the specific chunk processing. */ - private PNGChunk readChunkHead(DataInputStream dstrm) throws IOException { - long chunkLength; - //String typeStr; - - try { - chunkLength = readUnsignedInt(dstrm, true); - } - catch (EOFException e) { - // If we get an EOF here, we're done reading chunks. - return null; - } - int sig = (int) readUnsignedInt(dstrm, true); - - return PNGChunk.makePNGChunk(chunkLength, sig); + + if ((_colorType == 0 || _colorType == 4) && plteSeen) { + msg = new ErrorMessage(MessageConstants.PNG_GDM_75); + info.setMessage(msg); + info.setWellFormed(false); + return 0; } - - /********************************************************************** - * Chunk-specific functions. - **********************************************************************/ + // Add the keyword property list if it isn't empty. + if (!_keywordPropList.isEmpty()) { + _propList.add( + new Property("Keywords", PropertyType.PROPERTY, PropertyArity.LIST, _keywordPropList)); + } - /** Note that an IHDR chunk has been seen */ - public void setIhdrSeen (boolean b) { - ihdrSeen = b; + // Add the splat property list if it isn't empty. + if (!_spltList.isEmpty()) { + _propList.add( + new Property("Suggested palettes", PropertyType.PROPERTY, PropertyArity.LIST, _spltList)); } - - /** Returns true if IHDR chunk has been seen */ - public boolean isIhdrSeen () { - return ihdrSeen; + info.setProperty(_metadata); + return 0; + } + + /** + * This lets the module skip over the remainder of a chunk, not including the name, length,and + * CRC. It updates the CRC. + */ + public void eatChunk(PNGChunk chnk) throws IOException { + chnk.skipBytes((int) chnk.getLength()); + } + + /** + * Add a keyword and value. Creating arbitrary properties on the fly doesn't go well with JHOVE's + * approach, so we make each property a Map, with keys Keyword, Value, and optionally Language. + */ + public void addKeyword(String keywd, String val) { + addKeyword(keywd, null, val, null); + } + + /** Add a keyword, value, and language. */ + public void addKeyword(String keywd, String translatedKeywd, String val, String language) { + // HashMap map = new HashMap(); + List props = new ArrayList<>(); + Property prop = new Property("Keyword", PropertyType.PROPERTY, PropertyArity.LIST, props); + props.add(new Property("Key", PropertyType.STRING, keywd)); + props.add(new Property("Value", PropertyType.STRING, val)); + if (language != null) { + props.add(new Property("Language", PropertyType.STRING, language)); } - - /** Note that an IDAT chunk has been seen */ - public void setIdatSeen (boolean b) { - idatSeen = b; + if (translatedKeywd != null) { + props.add(new Property("Translated key", PropertyType.STRING, translatedKeywd)); } - - /** Note that a PLTE chunk has been seen */ - public void setPlteSeen (boolean b) { - plteSeen = b; - } - - /** Note that an IEND chunk has been seen */ - public void setIendSeen (boolean b) { - iendSeen = b; - } - - /** Return true if IDAT chunk has been seen */ - public boolean isIdatSeen () { - return idatSeen; - } - - /** Return true if a non-IDAT chunk has been seen after an IDAT chunk */ - public boolean isIdatFinished () { - return idatFinished; - } - - /** Return true if PLTE chunk has been seen */ - public boolean isPlteSeen () { - return plteSeen; - } - - /* Record that an ancillary chunk has been seen. This is used - * only for chunks whose names start with a lower-case letter. */ - public void setChunkSeen (int chunkType) { - _ancillaryChunks.add (chunkType); - } - - /* Return true if setChunkSeen has been called for the specified - * chunk type. */ - public boolean isChunkSeen (int chunkType) { - return _ancillaryChunks.contains(chunkType); - } - - /** Set the color type. The IHDR processing will set this for - * the benefit of chunks that need it. - */ - public void setColorType (int ct) { - _colorType = ct; - } - - /** Get the color type that was recorded from the IHDR chunk. */ - public int getColorType () { - return _colorType; + _keywordPropList.add(prop); + } + + /** Add a suggested palette */ + public void addSplt(String name, int sampleDepth, int numSamples) { + List props = new ArrayList<>(); + Property prop = + new Property("Suggested palette", PropertyType.PROPERTY, PropertyArity.LIST, props); + props.add(new Property("Name", PropertyType.STRING, name)); + props.add(new Property("Sample depth", PropertyType.INTEGER, Integer.valueOf(sampleDepth))); + props.add(new Property("Number of samples", PropertyType.INTEGER, Integer.valueOf(numSamples))); + _spltList.add(prop); + } + + /** Initializes the state of the module for parsing. */ + @Override + protected void initParse() { + super.initParse(); + ihdrSeen = false; + plteSeen = false; + idatSeen = false; + idatFinished = false; + iendSeen = false; + _ancillaryChunks = new HashSet<>(); + } + + /* readChunkHead reads the type and length of a chunk and + * leaves the rest to the specific chunk processing. */ + private PNGChunk readChunkHead(DataInputStream dstrm) throws IOException { + long chunkLength; + // String typeStr; + + try { + chunkLength = readUnsignedInt(dstrm, true); + } catch (EOFException e) { + // If we get an EOF here, we're done reading chunks. + return null; } - -} \ No newline at end of file + int sig = (int) readUnsignedInt(dstrm, true); + + return PNGChunk.makePNGChunk(chunkLength, sig); + } + + /** + * ******************************************************************** Chunk-specific functions. + * ******************************************************************** + */ + + /** Note that an IHDR chunk has been seen */ + public void setIhdrSeen(boolean b) { + ihdrSeen = b; + } + + /** Returns true if IHDR chunk has been seen */ + public boolean isIhdrSeen() { + return ihdrSeen; + } + + /** Note that an IDAT chunk has been seen */ + public void setIdatSeen(boolean b) { + idatSeen = b; + } + + /** Note that a PLTE chunk has been seen */ + public void setPlteSeen(boolean b) { + plteSeen = b; + } + + /** Note that an IEND chunk has been seen */ + public void setIendSeen(boolean b) { + iendSeen = b; + } + + /** Return true if IDAT chunk has been seen */ + public boolean isIdatSeen() { + return idatSeen; + } + + /** Return true if a non-IDAT chunk has been seen after an IDAT chunk */ + public boolean isIdatFinished() { + return idatFinished; + } + + /** Return true if PLTE chunk has been seen */ + public boolean isPlteSeen() { + return plteSeen; + } + + /* Record that an ancillary chunk has been seen. This is used + * only for chunks whose names start with a lower-case letter. */ + public void setChunkSeen(int chunkType) { + _ancillaryChunks.add(chunkType); + } + + /* Return true if setChunkSeen has been called for the specified + * chunk type. */ + public boolean isChunkSeen(int chunkType) { + return _ancillaryChunks.contains(chunkType); + } + + /** + * Set the color type. The IHDR processing will set this for the benefit of chunks that need it. + */ + public void setColorType(int ct) { + _colorType = ct; + } + + /** Get the color type that was recorded from the IHDR chunk. */ + public int getColorType() { + return _colorType; + } +} diff --git a/jhove-ext-modules/src/main/java/com/mcgath/jhove/module/png/BkgdChunk.java b/jhove-ext-modules/src/main/java/com/mcgath/jhove/module/png/BkgdChunk.java index 07a2a99d4..bc6144e2c 100644 --- a/jhove-ext-modules/src/main/java/com/mcgath/jhove/module/png/BkgdChunk.java +++ b/jhove-ext-modules/src/main/java/com/mcgath/jhove/module/png/BkgdChunk.java @@ -8,101 +8,93 @@ /** The bKGD (background color) chunk */ public class BkgdChunk extends PNGChunk { - /** Constructor - * @param sig: the chunktype - * @param leng: the length - */ - public BkgdChunk(int sig, long leng) { - chunkType = sig; - length = leng; - ancillary = true; - duplicateAllowed = false; - } + /** + * Constructor + * + * @param sig: the chunktype + * @param leng: the length + */ + public BkgdChunk(int sig, long leng) { + chunkType = sig; + length = leng; + ancillary = true; + duplicateAllowed = false; + } - /** Process the data in the chunk. All we do is note the - * presence of the chunk in a property. - * - * The greyscale, RGB, and palette backgrounds are all - * different kinds of data, so make them three different - * properties so that processing software doesn't get confused. - * @param info: RepInfo object - * @throws Exception - */ - @Override - public void processChunk(RepInfo info) throws Exception { - processChunkCommon(info); - ErrorMessage msg = null; - int colorType = _module.getColorType(); - // Make sure there are enough bytes - int minLength = 0; - switch (colorType) { - case 0: - case 4: - minLength = 2; - break; - case 2: - case 6: - minLength = 6; - break; - case 3: - minLength = 1; - break; - default : - break; - } - if (_module.isIdatSeen()) { - msg = new ErrorMessage (MessageConstants.PNG_GDM_1); - } - else if (length < minLength) { - msg = new ErrorMessage (MessageConstants.PNG_GDM_2); - } - else { - switch (colorType) { - case 0: - case 4: - int grayBkgd = readUnsignedShort(); - Property grayProp = new Property ("Gray background value", - PropertyType.INTEGER, - grayBkgd); - info.setProperty (grayProp); - break; - case 2: - case 6: - int redBkgd = readUnsignedShort(); - int greenBkgd = readUnsignedShort(); - int blueBkgd = readUnsignedShort(); - Property redProp = new Property ("Red background value", - PropertyType.INTEGER, - redBkgd); - info.setProperty (redProp); - Property greenProp = new Property ("Green background value", - PropertyType.INTEGER, - greenBkgd); - info.setProperty (greenProp); - Property blueProp = new Property ("Blue background value", - PropertyType.INTEGER, - blueBkgd); - info.setProperty (blueProp); - break; - case 3: - int bkgdIndex = readUnsignedByte(); - Property bkgdProp = new Property ("Background palette index", - PropertyType.INTEGER, - bkgdIndex); - info.setProperty (bkgdProp); - break; - default : - break; - } - // Throw away extra bytes - for (int i = 0; i < length - minLength; i++) { - readUnsignedByte(); - } - } - if (msg != null) { - info.setMessage (msg); - info.setWellFormed(false); - throw new PNGException (MessageConstants.PNG_GDM_3); - } - } + /** + * Process the data in the chunk. All we do is note the presence of the chunk in a property. + * + *

The greyscale, RGB, and palette backgrounds are all different kinds of data, so make them + * three different properties so that processing software doesn't get confused. + * + * @param info: RepInfo object + * @throws Exception + */ + @Override + public void processChunk(RepInfo info) throws Exception { + processChunkCommon(info); + ErrorMessage msg = null; + int colorType = _module.getColorType(); + // Make sure there are enough bytes + int minLength = 0; + switch (colorType) { + case 0: + case 4: + minLength = 2; + break; + case 2: + case 6: + minLength = 6; + break; + case 3: + minLength = 1; + break; + default: + break; + } + if (_module.isIdatSeen()) { + msg = new ErrorMessage(MessageConstants.PNG_GDM_1); + } else if (length < minLength) { + msg = new ErrorMessage(MessageConstants.PNG_GDM_2); + } else { + switch (colorType) { + case 0: + case 4: + int grayBkgd = readUnsignedShort(); + Property grayProp = new Property("Gray background value", PropertyType.INTEGER, grayBkgd); + info.setProperty(grayProp); + break; + case 2: + case 6: + int redBkgd = readUnsignedShort(); + int greenBkgd = readUnsignedShort(); + int blueBkgd = readUnsignedShort(); + Property redProp = new Property("Red background value", PropertyType.INTEGER, redBkgd); + info.setProperty(redProp); + Property greenProp = + new Property("Green background value", PropertyType.INTEGER, greenBkgd); + info.setProperty(greenProp); + Property blueProp = new Property("Blue background value", PropertyType.INTEGER, blueBkgd); + info.setProperty(blueProp); + break; + case 3: + int bkgdIndex = readUnsignedByte(); + Property bkgdProp = + new Property("Background palette index", PropertyType.INTEGER, bkgdIndex); + info.setProperty(bkgdProp); + break; + default: + break; + } + // Throw away extra bytes + for (int i = 0; i < length - minLength; i++) { + readUnsignedByte(); + } + } + if (msg != null) { + info.setMessage(msg); + info.setWellFormed(false); + throw new PNGException(MessageConstants.PNG_GDM_3); + } + } } diff --git a/jhove-ext-modules/src/main/java/com/mcgath/jhove/module/png/ChrmChunk.java b/jhove-ext-modules/src/main/java/com/mcgath/jhove/module/png/ChrmChunk.java index ec3440e68..10baa785f 100644 --- a/jhove-ext-modules/src/main/java/com/mcgath/jhove/module/png/ChrmChunk.java +++ b/jhove-ext-modules/src/main/java/com/mcgath/jhove/module/png/ChrmChunk.java @@ -7,65 +7,66 @@ /* The cHRM (primary chromaticities and white point) chunk */ public class ChrmChunk extends PNGChunk { - /* White point X and Y */ - private long whitePtX; - private long whitePtY; - - /* Primary chromaticities */ - private long redX; - private long redY; - private long greenX; - private long greenY; - private long blueX; - private long blueY; - - /** Constructor */ - public ChrmChunk(int sig, long leng) { - chunkType = sig; - length = leng; - ancillary = true; - duplicateAllowed = false; - } - - /** The IHDR chunk contains image information in a fixed format. - * I don't think the spec says it can't have extra bytes - * which would just be padding. */ - @Override - public void processChunk(RepInfo info) throws Exception { - processChunkCommon(info); - if (_module.isPlteSeen() || _module.isIdatSeen()) { - ErrorMessage msg = new ErrorMessage (MessageConstants.PNG_GDM_4); - info.setMessage (msg); - info.setWellFormed (false); - throw new PNGException (MessageConstants.PNG_GDM_5); - } - if (length < 32) { - ErrorMessage msg = new ErrorMessage(MessageConstants.PNG_GDM_6); - info.setMessage(msg); - info.setWellFormed(false); - throw new PNGException (MessageConstants.PNG_GDM_5); - } - whitePtX = readUnsignedInt(); - whitePtY = readUnsignedInt(); - redX = readUnsignedInt(); - redY = readUnsignedInt(); - greenX = readUnsignedInt(); - greenY = readUnsignedInt(); - blueX = readUnsignedInt(); - blueY = readUnsignedInt(); - - _nisoMetadata.setWhitePointXValue(new Rational(whitePtX, 100000)); - _nisoMetadata.setWhitePointYValue(new Rational(whitePtY, 100000)); - _nisoMetadata.setPrimaryChromaticitiesRedX(new Rational(redX, 100000)); - _nisoMetadata.setPrimaryChromaticitiesRedY(new Rational(redY, 100000)); - _nisoMetadata.setPrimaryChromaticitiesGreenX(new Rational(greenX, 100000)); - _nisoMetadata.setPrimaryChromaticitiesGreenY(new Rational(greenY, 100000)); - _nisoMetadata.setPrimaryChromaticitiesBlueX(new Rational(blueX, 100000)); - _nisoMetadata.setPrimaryChromaticitiesBlueY(new Rational(blueY, 100000)); - - // Discard any excess - for (int i = 0; i 0) { - ErrorMessage msg = new ErrorMessage(MessageConstants.PNG_GDM_21); - info.setMessage (msg); - info.setValid (false); - for (int i = 0; i < length; i++) { - readUnsignedByte(); - } - } - } + /** Constructor */ + public IendChunk(int sig, long leng) { + chunkType = sig; + length = leng; + ancillary = false; + } + @Override + public void processChunk(RepInfo info) throws Exception { + processChunkCommon(info); + _module.setIendSeen(true); + // This chunk is supposed to have a length of 0. + // If it's bigger, eat the extra bytes and declare the + // file invalid but not ill-formed. + if (length > 0) { + ErrorMessage msg = new ErrorMessage(MessageConstants.PNG_GDM_21); + info.setMessage(msg); + info.setValid(false); + for (int i = 0; i < length; i++) { + readUnsignedByte(); + } + } + } } diff --git a/jhove-ext-modules/src/main/java/com/mcgath/jhove/module/png/IhdrChunk.java b/jhove-ext-modules/src/main/java/com/mcgath/jhove/module/png/IhdrChunk.java index 6d88323b2..8fba7c63a 100644 --- a/jhove-ext-modules/src/main/java/com/mcgath/jhove/module/png/IhdrChunk.java +++ b/jhove-ext-modules/src/main/java/com/mcgath/jhove/module/png/IhdrChunk.java @@ -4,201 +4,192 @@ import edu.harvard.hul.ois.jhove.Property; import edu.harvard.hul.ois.jhove.PropertyType; import edu.harvard.hul.ois.jhove.RepInfo; -//import edu.harvard.hul.ois.jhove.module.PngModule; + +// import edu.harvard.hul.ois.jhove.module.PngModule; /** Representation of the IHDR (header) chunk */ public class IhdrChunk extends PNGChunk { - /** Color type constants */ - final private static int COLOR_GRAYSCALE = 0; - final private static int COLOR_TRUE = 2; // RGB - final private static int COLOR_INDEXED = 3; // Palette - final private static int COLOR_GRAYSCALE_ALPHA = 4; - final private static int COLOR_TRUE_ALPHA = 6; - - /* Color type names. Follows the spelling in the PNG spec. */ - final private static String[] colorTypeNames = { - "Greyscale", - "", // undefined - "Truecolour", - "Indexed-colour", - "Greyscale with alpha", - "", // undefined - "Truecolour with alpha" - }; - - /* Index of allowed bit depths, by color type */ - final private static int[][] allowedBitDepths = { - { 1, 2, 4, 8, 16}, // grayscale - { }, // invalid - { 8, 16}, // truecolour - { 1, 2, 4, 8}, // indexed color - { 8, 16}, // grayscale with alpha - { }, // invalid - { 8, 16} // truecolour with alpha - }; - - /* Image width */ - private long width; - - /* Image height */ - private long height; - - /* Bit depth */ - private int bitDepth; - - /* Colour (sic) type, should be one of the color type constants */ - private int colorType; - - /* Compression method */ - private int compression; - - /* Filter method */ - private int filter; - - /* Interlace method */ - private int interlace; - - - /** Constructor */ - public IhdrChunk(int sig, long leng) { - chunkType = sig; - length = leng; - ancillary = false; - } - - /** The IHDR chunk contains image information in a fixed format. - * I don't think the spec says it can't have extra bytes - * which would just be padding. */ - @Override - public void processChunk(RepInfo info) throws Exception { - boolean badChunk = false; - processChunkCommon(info); - if (_module.isIhdrSeen ()) { - ErrorMessage msg = new ErrorMessage(MessageConstants.PNG_GDM_22); - info.setMessage (msg); - info.setWellFormed (false); - throw new PNGException (MessageConstants.PNG_GDM_23); - } - _module.setIhdrSeen(true); - if (length < 13) { - ErrorMessage msg = new ErrorMessage (MessageConstants.PNG_GDM_24); - info.setMessage(msg); - throw new PNGException (MessageConstants.PNG_GDM_25); - } - width = readUnsignedInt(); - height = readUnsignedInt(); - bitDepth = readUnsignedByte(); - colorType = readUnsignedByte(); - compression = readUnsignedByte(); - filter = readUnsignedByte(); - interlace = readUnsignedByte(); - - // Extra bytes (assume they're allowed) - for (int i = 0; i < length - 13; i++) { - readUnsignedByte(); - } - - - _nisoMetadata.setImageWidth (width); - _nisoMetadata.setImageLength (height); - int[] bits = { bitDepth }; - _nisoMetadata.setBitsPerSample (bits); - boolean ctErr = false; - try { - _nisoMetadata.setColorSpace(colorTypeToNiso (colorType)); - _module.setColorType (colorType); - } catch (PNGException e) { - ctErr = true; - } - if (ctErr || colorType == 1 || colorType == 5 || colorType > 6) { - ErrorMessage msg = - new ErrorMessage("Invalid color type " + colorType); - info.setMessage(msg); - info.setWellFormed(false); - badChunk = true; - } else { - if (!colorAndDepthOK(colorType, bitDepth)) { - ErrorMessage msg = - new ErrorMessage(MessageConstants.PNG_GDM_26, - String.format(MessageConstants.PNG_GDM_26_SUB.getMessage(), - colorType, - bitDepth)); - info.setMessage(msg); - info.setWellFormed(false); - badChunk = true; - } - _propList.add(new Property("ColorType", - PropertyType.STRING, - colorTypeNames[colorType])); - } - // Deflate is the only defined compression method. - // Report other methods as numbers. - String compressionStr; - if (compression == 0) { - compressionStr = "Deflate"; - } else { - compressionStr = Integer.toString(compression); - } - _propList.add (new Property("Compression", - PropertyType.STRING, - compressionStr)); - - _propList.add (new Property("Filter type", - PropertyType.INTEGER, - Integer.valueOf(filter))); - - String interlaceStr; - switch (interlace) { - case 0: - interlaceStr = "None"; - break; - case 1: - interlaceStr = "Adam7"; - break; - default: - interlaceStr = Integer.toString(interlace); - break; - } - _propList.add (new Property("Interlace", - PropertyType.STRING, - interlaceStr)); - if (badChunk) { - throw new PNGException (MessageConstants.PNG_GDM_27); - } - } - - /* Convert PNG colour type to NISO color space */ - private static int colorTypeToNiso (int typ) throws PNGException { - int val = 0; - switch (typ) { - case COLOR_GRAYSCALE: - case COLOR_GRAYSCALE_ALPHA: - val = 0; - break; - case COLOR_TRUE: - case COLOR_TRUE_ALPHA: - val = 2; - break; - case COLOR_INDEXED: - val = 3; - break; - default: - throw new PNGException (MessageConstants.PNG_GDM_28); - } - return val; - } - - /* Check if the combination of color type and bit depth is allowed. - * Color must be in the range 0-6. */ - private static boolean colorAndDepthOK(int color, int depth) { - int[] allowedDepths = allowedBitDepths[color]; - boolean ok = false; - for (int d : allowedDepths) { - if (d == depth) { - ok = true; - } - } - return ok; - } + /** Color type constants */ + private static final int COLOR_GRAYSCALE = 0; + + private static final int COLOR_TRUE = 2; // RGB + private static final int COLOR_INDEXED = 3; // Palette + private static final int COLOR_GRAYSCALE_ALPHA = 4; + private static final int COLOR_TRUE_ALPHA = 6; + + /* Color type names. Follows the spelling in the PNG spec. */ + private static final String[] colorTypeNames = { + "Greyscale", + "", // undefined + "Truecolour", + "Indexed-colour", + "Greyscale with alpha", + "", // undefined + "Truecolour with alpha" + }; + + /* Index of allowed bit depths, by color type */ + private static final int[][] allowedBitDepths = { + {1, 2, 4, 8, 16}, // grayscale + {}, // invalid + {8, 16}, // truecolour + {1, 2, 4, 8}, // indexed color + {8, 16}, // grayscale with alpha + {}, // invalid + {8, 16} // truecolour with alpha + }; + + /* Image width */ + private long width; + + /* Image height */ + private long height; + + /* Bit depth */ + private int bitDepth; + + /* Colour (sic) type, should be one of the color type constants */ + private int colorType; + + /* Compression method */ + private int compression; + + /* Filter method */ + private int filter; + + /* Interlace method */ + private int interlace; + + /** Constructor */ + public IhdrChunk(int sig, long leng) { + chunkType = sig; + length = leng; + ancillary = false; + } + + /** + * The IHDR chunk contains image information in a fixed format. I don't think the spec says it + * can't have extra bytes which would just be padding. + */ + @Override + public void processChunk(RepInfo info) throws Exception { + boolean badChunk = false; + processChunkCommon(info); + if (_module.isIhdrSeen()) { + ErrorMessage msg = new ErrorMessage(MessageConstants.PNG_GDM_22); + info.setMessage(msg); + info.setWellFormed(false); + throw new PNGException(MessageConstants.PNG_GDM_23); + } + _module.setIhdrSeen(true); + if (length < 13) { + ErrorMessage msg = new ErrorMessage(MessageConstants.PNG_GDM_24); + info.setMessage(msg); + throw new PNGException(MessageConstants.PNG_GDM_25); + } + width = readUnsignedInt(); + height = readUnsignedInt(); + bitDepth = readUnsignedByte(); + colorType = readUnsignedByte(); + compression = readUnsignedByte(); + filter = readUnsignedByte(); + interlace = readUnsignedByte(); + + // Extra bytes (assume they're allowed) + for (int i = 0; i < length - 13; i++) { + readUnsignedByte(); + } + + _nisoMetadata.setImageWidth(width); + _nisoMetadata.setImageLength(height); + int[] bits = {bitDepth}; + _nisoMetadata.setBitsPerSample(bits); + boolean ctErr = false; + try { + _nisoMetadata.setColorSpace(colorTypeToNiso(colorType)); + _module.setColorType(colorType); + } catch (PNGException e) { + ctErr = true; + } + if (ctErr || colorType == 1 || colorType == 5 || colorType > 6) { + ErrorMessage msg = new ErrorMessage("Invalid color type " + colorType); + info.setMessage(msg); + info.setWellFormed(false); + badChunk = true; + } else { + if (!colorAndDepthOK(colorType, bitDepth)) { + ErrorMessage msg = + new ErrorMessage( + MessageConstants.PNG_GDM_26, + String.format(MessageConstants.PNG_GDM_26_SUB.getMessage(), colorType, bitDepth)); + info.setMessage(msg); + info.setWellFormed(false); + badChunk = true; + } + _propList.add(new Property("ColorType", PropertyType.STRING, colorTypeNames[colorType])); + } + // Deflate is the only defined compression method. + // Report other methods as numbers. + String compressionStr; + if (compression == 0) { + compressionStr = "Deflate"; + } else { + compressionStr = Integer.toString(compression); + } + _propList.add(new Property("Compression", PropertyType.STRING, compressionStr)); + + _propList.add(new Property("Filter type", PropertyType.INTEGER, Integer.valueOf(filter))); + + String interlaceStr; + switch (interlace) { + case 0: + interlaceStr = "None"; + break; + case 1: + interlaceStr = "Adam7"; + break; + default: + interlaceStr = Integer.toString(interlace); + break; + } + _propList.add(new Property("Interlace", PropertyType.STRING, interlaceStr)); + if (badChunk) { + throw new PNGException(MessageConstants.PNG_GDM_27); + } + } + + /* Convert PNG colour type to NISO color space */ + private static int colorTypeToNiso(int typ) throws PNGException { + int val = 0; + switch (typ) { + case COLOR_GRAYSCALE: + case COLOR_GRAYSCALE_ALPHA: + val = 0; + break; + case COLOR_TRUE: + case COLOR_TRUE_ALPHA: + val = 2; + break; + case COLOR_INDEXED: + val = 3; + break; + default: + throw new PNGException(MessageConstants.PNG_GDM_28); + } + return val; + } + + /* Check if the combination of color type and bit depth is allowed. + * Color must be in the range 0-6. */ + private static boolean colorAndDepthOK(int color, int depth) { + int[] allowedDepths = allowedBitDepths[color]; + boolean ok = false; + for (int d : allowedDepths) { + if (d == depth) { + ok = true; + } + } + return ok; + } } diff --git a/jhove-ext-modules/src/main/java/com/mcgath/jhove/module/png/ItxtChunk.java b/jhove-ext-modules/src/main/java/com/mcgath/jhove/module/png/ItxtChunk.java index 501df84f1..dccb23c71 100644 --- a/jhove-ext-modules/src/main/java/com/mcgath/jhove/module/png/ItxtChunk.java +++ b/jhove-ext-modules/src/main/java/com/mcgath/jhove/module/png/ItxtChunk.java @@ -7,104 +7,104 @@ * Representation of the iTXt (internationalized text) chunk * * @see https://www.w3.org/TR/PNG/#11iTXt - **/ + */ public class ItxtChunk extends GeneralTextChunk { - /** Constructor */ - public ItxtChunk(int sig, long leng) { - chunkType = sig; - length = leng; - ancillary = true; - duplicateAllowed = true; - } + /** Constructor */ + public ItxtChunk(int sig, long leng) { + chunkType = sig; + length = leng; + ancillary = true; + duplicateAllowed = true; + } - /** Process the data portion of the chunk. */ - @Override - public void processChunk(RepInfo info) throws Exception { - processChunkCommon(info); + /** Process the data portion of the chunk. */ + @Override + public void processChunk(RepInfo info) throws Exception { + processChunkCommon(info); - //iTXt chunks may have either compressed or uncompressed values. + // iTXt chunks may have either compressed or uncompressed values. - // state values: - // 0 = getting keyword, - // 1 = compression flag - // 2 = compression type - // 3 = language - // 4 = translated keyword - // 5 = value - int state = 0; - int compressionFlag = 0; - int compressionType = 0; - String keyword = null; - String translatedKeyword = null; - String language = null; - byte[] valueData = null; - StringBuilder sb = new StringBuilder(); - int valueIdx = 0; - for (int i = 0; i - * Sam Alloing AT github - * @author Jacob Takema - * Jacob Takema AT github + * @author Sam Alloing Sam Alloing AT github + * @author Jacob Takema Jacob Takema AT github */ - public enum MessageConstants { + INSTANCE; + private static JhoveMessageFactory messageFactory = + JhoveMessages.getInstance("com.mcgath.jhove.module.png.ErrorMessages"); - INSTANCE; - private static JhoveMessageFactory messageFactory = JhoveMessages - .getInstance( - "com.mcgath.jhove.module.png.ErrorMessages"); - - public static final JhoveMessage PNG_GDM_1 = messageFactory - .getMessage("PNG-GDM-1"); - public static final JhoveMessage PNG_GDM_2 = messageFactory - .getMessage("PNG-GDM-2"); - public static final JhoveMessage PNG_GDM_3 = messageFactory - .getMessage("PNG-GDM-3"); - public static final JhoveMessage PNG_GDM_4 = messageFactory - .getMessage("PNG-GDM-4"); - public static final JhoveMessage PNG_GDM_5 = messageFactory - .getMessage("PNG-GDM-5"); - public static final JhoveMessage PNG_GDM_6 = messageFactory - .getMessage("PNG-GDM-6"); - public static final JhoveMessage PNG_GDM_7 = messageFactory - .getMessage("PNG-GDM-7"); - public static final JhoveMessage PNG_GDM_8 = messageFactory - .getMessage("PNG-GDM-8"); - public static final JhoveMessage PNG_GDM_9 = messageFactory - .getMessage("PNG-GDM-9"); - public static final JhoveMessage PNG_GDM_9_SUB = messageFactory - .getMessage("PNG-GDM-9-SUB"); - public static final JhoveMessage PNG_GDM_10 = messageFactory - .getMessage("PNG-GDM-10"); - public static final JhoveMessage PNG_GDM_11 = messageFactory - .getMessage("PNG-GDM-11"); - public static final JhoveMessage PNG_GDM_12 = messageFactory - .getMessage("PNG-GDM-12"); - public static final JhoveMessage PNG_GDM_13 = messageFactory - .getMessage("PNG-GDM-13"); - public static final JhoveMessage PNG_GDM_14 = messageFactory - .getMessage("PNG-GDM-14"); - public static final JhoveMessage PNG_GDM_15 = messageFactory - .getMessage("PNG-GDM-15"); - public static final JhoveMessage PNG_GDM_16 = messageFactory - .getMessage("PNG-GDM-16"); - public static final JhoveMessage PNG_GDM_17 = messageFactory - .getMessage("PNG-GDM-17"); - public static final JhoveMessage PNG_GDM_18 = messageFactory - .getMessage("PNG-GDM-18"); - public static final JhoveMessage PNG_GDM_19 = messageFactory - .getMessage("PNG-GDM-19"); - public static final JhoveMessage PNG_GDM_20 = messageFactory - .getMessage("PNG-GDM-20"); - public static final JhoveMessage PNG_GDM_21 = messageFactory - .getMessage("PNG-GDM-21"); - public static final JhoveMessage PNG_GDM_22 = messageFactory - .getMessage("PNG-GDM-22"); - public static final JhoveMessage PNG_GDM_23 = messageFactory - .getMessage("PNG-GDM-23"); - public static final JhoveMessage PNG_GDM_24 = messageFactory - .getMessage("PNG-GDM-24"); - public static final JhoveMessage PNG_GDM_25 = messageFactory - .getMessage("PNG-GDM-25"); - public static final JhoveMessage PNG_GDM_26 = messageFactory - .getMessage("PNG-GDM-26"); - public static final JhoveMessage PNG_GDM_26_SUB = messageFactory - .getMessage("PNG-GDM-26-SUB"); - public static final JhoveMessage PNG_GDM_27 = messageFactory - .getMessage("PNG-GDM-27"); - public static final JhoveMessage PNG_GDM_28 = messageFactory - .getMessage("PNG-GDM-28"); - public static final JhoveMessage PNG_GDM_29 = messageFactory - .getMessage("PNG-GDM-29"); - public static final JhoveMessage PNG_GDM_29_SUB = messageFactory - .getMessage("PNG-GDM-29-SUB"); - public static final JhoveMessage PNG_GDM_30 = messageFactory - .getMessage("PNG-GDM-30"); - public static final JhoveMessage PNG_GDM_31 = messageFactory - .getMessage("PNG-GDM-31"); - public static final JhoveMessage PNG_GDM_32 = messageFactory - .getMessage("PNG-GDM-32"); - public static final JhoveMessage PNG_GDM_33 = messageFactory - .getMessage("PNG-GDM-33"); - public static final JhoveMessage PNG_GDM_34 = messageFactory - .getMessage("PNG-GDM-34"); - public static final JhoveMessage PNG_GDM_35 = messageFactory - .getMessage("PNG-GDM-35"); - public static final JhoveMessage PNG_GDM_36 = messageFactory - .getMessage("PNG-GDM-36"); - public static final JhoveMessage PNG_GDM_37 = messageFactory - .getMessage("PNG-GDM-37"); - public static final JhoveMessage PNG_GDM_38 = messageFactory - .getMessage("PNG-GDM-38"); - public static final JhoveMessage PNG_GDM_38_SUB = messageFactory - .getMessage("PNG-GDM-38-SUB"); - public static final JhoveMessage PNG_GDM_39 = messageFactory - .getMessage("PNG-GDM-39"); - public static final JhoveMessage PNG_GDM_40 = messageFactory - .getMessage("PNG-GDM-40"); - public static final JhoveMessage PNG_GDM_41 = messageFactory - .getMessage("PNG-GDM-41"); - public static final JhoveMessage PNG_GDM_42 = messageFactory - .getMessage("PNG-GDM-42"); - public static final JhoveMessage PNG_GDM_43 = messageFactory - .getMessage("PNG-GDM-43"); - public static final JhoveMessage PNG_GDM_44 = messageFactory - .getMessage("PNG-GDM-44"); - public static final JhoveMessage PNG_GDM_45 = messageFactory - .getMessage("PNG-GDM-45"); - public static final JhoveMessage PNG_GDM_46 = messageFactory - .getMessage("PNG-GDM-46"); - public static final JhoveMessage PNG_GDM_46_SUB = messageFactory - .getMessage("PNG-GDM-46-SUB"); - public static final JhoveMessage PNG_GDM_47 = messageFactory - .getMessage("PNG-GDM-47"); - public static final JhoveMessage PNG_GDM_48 = messageFactory - .getMessage("PNG-GDM-48"); - public static final JhoveMessage PNG_GDM_49 = messageFactory - .getMessage("PNG-GDM-49"); - public static final JhoveMessage PNG_GDM_50 = messageFactory - .getMessage("PNG-GDM-50"); - public static final JhoveMessage PNG_GDM_51 = messageFactory - .getMessage("PNG-GDM-51"); - public static final JhoveMessage PNG_GDM_52 = messageFactory - .getMessage("PNG-GDM-52"); - public static final JhoveMessage PNG_GDM_53 = messageFactory - .getMessage("PNG-GDM-53"); - public static final JhoveMessage PNG_GDM_54 = messageFactory - .getMessage("PNG-GDM-54"); - public static final JhoveMessage PNG_GDM_55 = messageFactory - .getMessage("PNG-GDM-55"); - public static final JhoveMessage PNG_GDM_56 = messageFactory - .getMessage("PNG-GDM-56"); - public static final JhoveMessage PNG_GDM_57 = messageFactory - .getMessage("PNG-GDM-57"); - public static final JhoveMessage PNG_GDM_58 = messageFactory - .getMessage("PNG-GDM-58"); - public static final JhoveMessage PNG_GDM_59 = messageFactory - .getMessage("PNG-GDM-59"); - public static final JhoveMessage PNG_GDM_60 = messageFactory - .getMessage("PNG-GDM-60"); - public static final JhoveMessage PNG_GDM_61 = messageFactory - .getMessage("PNG-GDM-61"); - public static final JhoveMessage PNG_GDM_62 = messageFactory - .getMessage("PNG-GDM-62"); - public static final JhoveMessage PNG_GDM_63 = messageFactory - .getMessage("PNG-GDM-63"); - public static final JhoveMessage PNG_GDM_63_SUB = messageFactory - .getMessage("PNG-GDM-63-SUB"); - public static final JhoveMessage PNG_GDM_64 = messageFactory - .getMessage("PNG-GDM-64"); - public static final JhoveMessage PNG_GDM_65 = messageFactory - .getMessage("PNG-GDM-65"); - public static final JhoveMessage PNG_GDM_66 = messageFactory - .getMessage("PNG-GDM-66"); - public static final JhoveMessage PNG_GDM_67 = messageFactory - .getMessage("PNG-GDM-67"); - public static final JhoveMessage PNG_GDM_68 = messageFactory - .getMessage("PNG-GDM-68"); - public static final JhoveMessage PNG_GDM_69 = messageFactory - .getMessage("PNG-GDM-69"); - public static final JhoveMessage PNG_GDM_70 = messageFactory - .getMessage("PNG-GDM-70"); - public static final JhoveMessage PNG_GDM_71 = messageFactory - .getMessage("PNG-GDM-71"); - public static final JhoveMessage PNG_GDM_72 = messageFactory - .getMessage("PNG-GDM-72"); - public static final JhoveMessage PNG_GDM_73 = messageFactory - .getMessage("PNG-GDM-73"); - public static final JhoveMessage PNG_GDM_74 = messageFactory - .getMessage("PNG-GDM-74"); - public static final JhoveMessage PNG_GDM_75 = messageFactory - .getMessage("PNG-GDM-75"); + public static final JhoveMessage PNG_GDM_1 = messageFactory.getMessage("PNG-GDM-1"); + public static final JhoveMessage PNG_GDM_2 = messageFactory.getMessage("PNG-GDM-2"); + public static final JhoveMessage PNG_GDM_3 = messageFactory.getMessage("PNG-GDM-3"); + public static final JhoveMessage PNG_GDM_4 = messageFactory.getMessage("PNG-GDM-4"); + public static final JhoveMessage PNG_GDM_5 = messageFactory.getMessage("PNG-GDM-5"); + public static final JhoveMessage PNG_GDM_6 = messageFactory.getMessage("PNG-GDM-6"); + public static final JhoveMessage PNG_GDM_7 = messageFactory.getMessage("PNG-GDM-7"); + public static final JhoveMessage PNG_GDM_8 = messageFactory.getMessage("PNG-GDM-8"); + public static final JhoveMessage PNG_GDM_9 = messageFactory.getMessage("PNG-GDM-9"); + public static final JhoveMessage PNG_GDM_9_SUB = messageFactory.getMessage("PNG-GDM-9-SUB"); + public static final JhoveMessage PNG_GDM_10 = messageFactory.getMessage("PNG-GDM-10"); + public static final JhoveMessage PNG_GDM_11 = messageFactory.getMessage("PNG-GDM-11"); + public static final JhoveMessage PNG_GDM_12 = messageFactory.getMessage("PNG-GDM-12"); + public static final JhoveMessage PNG_GDM_13 = messageFactory.getMessage("PNG-GDM-13"); + public static final JhoveMessage PNG_GDM_14 = messageFactory.getMessage("PNG-GDM-14"); + public static final JhoveMessage PNG_GDM_15 = messageFactory.getMessage("PNG-GDM-15"); + public static final JhoveMessage PNG_GDM_16 = messageFactory.getMessage("PNG-GDM-16"); + public static final JhoveMessage PNG_GDM_17 = messageFactory.getMessage("PNG-GDM-17"); + public static final JhoveMessage PNG_GDM_18 = messageFactory.getMessage("PNG-GDM-18"); + public static final JhoveMessage PNG_GDM_19 = messageFactory.getMessage("PNG-GDM-19"); + public static final JhoveMessage PNG_GDM_20 = messageFactory.getMessage("PNG-GDM-20"); + public static final JhoveMessage PNG_GDM_21 = messageFactory.getMessage("PNG-GDM-21"); + public static final JhoveMessage PNG_GDM_22 = messageFactory.getMessage("PNG-GDM-22"); + public static final JhoveMessage PNG_GDM_23 = messageFactory.getMessage("PNG-GDM-23"); + public static final JhoveMessage PNG_GDM_24 = messageFactory.getMessage("PNG-GDM-24"); + public static final JhoveMessage PNG_GDM_25 = messageFactory.getMessage("PNG-GDM-25"); + public static final JhoveMessage PNG_GDM_26 = messageFactory.getMessage("PNG-GDM-26"); + public static final JhoveMessage PNG_GDM_26_SUB = messageFactory.getMessage("PNG-GDM-26-SUB"); + public static final JhoveMessage PNG_GDM_27 = messageFactory.getMessage("PNG-GDM-27"); + public static final JhoveMessage PNG_GDM_28 = messageFactory.getMessage("PNG-GDM-28"); + public static final JhoveMessage PNG_GDM_29 = messageFactory.getMessage("PNG-GDM-29"); + public static final JhoveMessage PNG_GDM_29_SUB = messageFactory.getMessage("PNG-GDM-29-SUB"); + public static final JhoveMessage PNG_GDM_30 = messageFactory.getMessage("PNG-GDM-30"); + public static final JhoveMessage PNG_GDM_31 = messageFactory.getMessage("PNG-GDM-31"); + public static final JhoveMessage PNG_GDM_32 = messageFactory.getMessage("PNG-GDM-32"); + public static final JhoveMessage PNG_GDM_33 = messageFactory.getMessage("PNG-GDM-33"); + public static final JhoveMessage PNG_GDM_34 = messageFactory.getMessage("PNG-GDM-34"); + public static final JhoveMessage PNG_GDM_35 = messageFactory.getMessage("PNG-GDM-35"); + public static final JhoveMessage PNG_GDM_36 = messageFactory.getMessage("PNG-GDM-36"); + public static final JhoveMessage PNG_GDM_37 = messageFactory.getMessage("PNG-GDM-37"); + public static final JhoveMessage PNG_GDM_38 = messageFactory.getMessage("PNG-GDM-38"); + public static final JhoveMessage PNG_GDM_38_SUB = messageFactory.getMessage("PNG-GDM-38-SUB"); + public static final JhoveMessage PNG_GDM_39 = messageFactory.getMessage("PNG-GDM-39"); + public static final JhoveMessage PNG_GDM_40 = messageFactory.getMessage("PNG-GDM-40"); + public static final JhoveMessage PNG_GDM_41 = messageFactory.getMessage("PNG-GDM-41"); + public static final JhoveMessage PNG_GDM_42 = messageFactory.getMessage("PNG-GDM-42"); + public static final JhoveMessage PNG_GDM_43 = messageFactory.getMessage("PNG-GDM-43"); + public static final JhoveMessage PNG_GDM_44 = messageFactory.getMessage("PNG-GDM-44"); + public static final JhoveMessage PNG_GDM_45 = messageFactory.getMessage("PNG-GDM-45"); + public static final JhoveMessage PNG_GDM_46 = messageFactory.getMessage("PNG-GDM-46"); + public static final JhoveMessage PNG_GDM_46_SUB = messageFactory.getMessage("PNG-GDM-46-SUB"); + public static final JhoveMessage PNG_GDM_47 = messageFactory.getMessage("PNG-GDM-47"); + public static final JhoveMessage PNG_GDM_48 = messageFactory.getMessage("PNG-GDM-48"); + public static final JhoveMessage PNG_GDM_49 = messageFactory.getMessage("PNG-GDM-49"); + public static final JhoveMessage PNG_GDM_50 = messageFactory.getMessage("PNG-GDM-50"); + public static final JhoveMessage PNG_GDM_51 = messageFactory.getMessage("PNG-GDM-51"); + public static final JhoveMessage PNG_GDM_52 = messageFactory.getMessage("PNG-GDM-52"); + public static final JhoveMessage PNG_GDM_53 = messageFactory.getMessage("PNG-GDM-53"); + public static final JhoveMessage PNG_GDM_54 = messageFactory.getMessage("PNG-GDM-54"); + public static final JhoveMessage PNG_GDM_55 = messageFactory.getMessage("PNG-GDM-55"); + public static final JhoveMessage PNG_GDM_56 = messageFactory.getMessage("PNG-GDM-56"); + public static final JhoveMessage PNG_GDM_57 = messageFactory.getMessage("PNG-GDM-57"); + public static final JhoveMessage PNG_GDM_58 = messageFactory.getMessage("PNG-GDM-58"); + public static final JhoveMessage PNG_GDM_59 = messageFactory.getMessage("PNG-GDM-59"); + public static final JhoveMessage PNG_GDM_60 = messageFactory.getMessage("PNG-GDM-60"); + public static final JhoveMessage PNG_GDM_61 = messageFactory.getMessage("PNG-GDM-61"); + public static final JhoveMessage PNG_GDM_62 = messageFactory.getMessage("PNG-GDM-62"); + public static final JhoveMessage PNG_GDM_63 = messageFactory.getMessage("PNG-GDM-63"); + public static final JhoveMessage PNG_GDM_63_SUB = messageFactory.getMessage("PNG-GDM-63-SUB"); + public static final JhoveMessage PNG_GDM_64 = messageFactory.getMessage("PNG-GDM-64"); + public static final JhoveMessage PNG_GDM_65 = messageFactory.getMessage("PNG-GDM-65"); + public static final JhoveMessage PNG_GDM_66 = messageFactory.getMessage("PNG-GDM-66"); + public static final JhoveMessage PNG_GDM_67 = messageFactory.getMessage("PNG-GDM-67"); + public static final JhoveMessage PNG_GDM_68 = messageFactory.getMessage("PNG-GDM-68"); + public static final JhoveMessage PNG_GDM_69 = messageFactory.getMessage("PNG-GDM-69"); + public static final JhoveMessage PNG_GDM_70 = messageFactory.getMessage("PNG-GDM-70"); + public static final JhoveMessage PNG_GDM_71 = messageFactory.getMessage("PNG-GDM-71"); + public static final JhoveMessage PNG_GDM_72 = messageFactory.getMessage("PNG-GDM-72"); + public static final JhoveMessage PNG_GDM_73 = messageFactory.getMessage("PNG-GDM-73"); + public static final JhoveMessage PNG_GDM_74 = messageFactory.getMessage("PNG-GDM-74"); + public static final JhoveMessage PNG_GDM_75 = messageFactory.getMessage("PNG-GDM-75"); } diff --git a/jhove-ext-modules/src/main/java/com/mcgath/jhove/module/png/PNGChunk.java b/jhove-ext-modules/src/main/java/com/mcgath/jhove/module/png/PNGChunk.java index 83d17a489..4cfd9e89d 100644 --- a/jhove-ext-modules/src/main/java/com/mcgath/jhove/module/png/PNGChunk.java +++ b/jhove-ext-modules/src/main/java/com/mcgath/jhove/module/png/PNGChunk.java @@ -1,266 +1,257 @@ package com.mcgath.jhove.module.png; -import java.io.DataInputStream; -import java.io.IOException; -import java.util.List; -import java.util.zip.CRC32; - import com.mcgath.jhove.module.PngModule; - import edu.harvard.hul.ois.jhove.ErrorMessage; import edu.harvard.hul.ois.jhove.ModuleBase; import edu.harvard.hul.ois.jhove.NisoImageMetadata; import edu.harvard.hul.ois.jhove.Property; import edu.harvard.hul.ois.jhove.RepInfo; +import java.io.DataInputStream; +import java.io.IOException; +import java.util.List; +import java.util.zip.CRC32; public abstract class PNGChunk { - protected long length; // length of the data portion - protected int chunkType; // chunk type as 32-bit value - protected char chunkData[]; // data portion, can be any length including 0 - protected CRC32 crc; // 4-byte CRC - protected boolean ancillary; // if true, an ancillary chunk - protected boolean duplicateAllowed; // ancillary chunks only -- if false, no duplicates of this type allowed - - protected NisoImageMetadata _nisoMetadata; - - /** The invoking module */ - protected PngModule _module; - - /** The invoking module's input stream */ - protected DataInputStream _dstream; - - protected List _propList; - - /* - * Chunk signatures. - * - * Java *IS* Big Endian, PNG chunk signatures are 4 byte strings we - * *CAN* read into an int variable since all of them have bit 7 - * set to 0. - * - * Therefore we can check each chunk signature against int - * constants (one opcode executed, no loops). - * - * About names: these name violate the Java naming rules for - * constants, but I prefer to keep the PNG chunk name cases, since - * they are meaningful for the properties of each chunk. - */ - protected final static int IHDR_HEAD_SIG = 0x49484452; - protected final static int PLTE_HEAD_SIG = 0x504c5445; - protected final static int IDAT_HEAD_SIG = 0x49444154; - protected final static int IEND_HEAD_SIG = 0x49454e44; - protected final static int cHRM_HEAD_SIG = 0x6348524d; - protected final static int gAMA_HEAD_SIG = 0x67414d41; - protected final static int iCCP_HEAD_SIG = 0x69434350; - protected final static int sBIT_HEAD_SIG = 0x73424954; - protected final static int sRGB_HEAD_SIG = 0x73524742; - protected final static int tEXt_HEAD_SIG = 0x74455874; - protected final static int zTXt_HEAD_SIG = 0x7a545874; - protected final static int iTXt_HEAD_SIG = 0x69545874; - protected final static int bKGD_HEAD_SIG = 0x624b4744; - protected final static int hIST_HEAD_SIG = 0x68495354; - protected final static int pHYs_HEAD_SIG = 0x70485973; - protected final static int sPLT_HEAD_SIG = 0x73504c54; - protected final static int tIME_HEAD_SIG = 0x74494d45; - protected final static int tRNS_HEAD_SIG = 0x74524e53; - - public PNGChunk() { - length = 0; - chunkType = 0; - chunkData = null; // Not populated till we know the length - this.crc = new CRC32(); - } - - - /** Construct a PNGChunk object of the appropriate subtype - * based on the chunk type. */ - public static PNGChunk makePNGChunk (long length, int sig) { - switch (sig) { - case IHDR_HEAD_SIG: - return new IhdrChunk (sig, length); - case IDAT_HEAD_SIG: - return new IdatChunk (sig, length); - case IEND_HEAD_SIG: - return new IendChunk (sig, length); - case PLTE_HEAD_SIG: - return new PlteChunk (sig, length); + protected long length; // length of the data portion + protected int chunkType; // chunk type as 32-bit value + protected char chunkData[]; // data portion, can be any length including 0 + protected CRC32 crc; // 4-byte CRC + protected boolean ancillary; // if true, an ancillary chunk + protected boolean + duplicateAllowed; // ancillary chunks only -- if false, no duplicates of this type allowed + + protected NisoImageMetadata _nisoMetadata; + + /** The invoking module */ + protected PngModule _module; + + /** The invoking module's input stream */ + protected DataInputStream _dstream; + + protected List _propList; + + /* + * Chunk signatures. + * + * Java *IS* Big Endian, PNG chunk signatures are 4 byte strings we + * *CAN* read into an int variable since all of them have bit 7 + * set to 0. + * + * Therefore we can check each chunk signature against int + * constants (one opcode executed, no loops). + * + * About names: these name violate the Java naming rules for + * constants, but I prefer to keep the PNG chunk name cases, since + * they are meaningful for the properties of each chunk. + */ + protected static final int IHDR_HEAD_SIG = 0x49484452; + protected static final int PLTE_HEAD_SIG = 0x504c5445; + protected static final int IDAT_HEAD_SIG = 0x49444154; + protected static final int IEND_HEAD_SIG = 0x49454e44; + protected static final int cHRM_HEAD_SIG = 0x6348524d; + protected static final int gAMA_HEAD_SIG = 0x67414d41; + protected static final int iCCP_HEAD_SIG = 0x69434350; + protected static final int sBIT_HEAD_SIG = 0x73424954; + protected static final int sRGB_HEAD_SIG = 0x73524742; + protected static final int tEXt_HEAD_SIG = 0x74455874; + protected static final int zTXt_HEAD_SIG = 0x7a545874; + protected static final int iTXt_HEAD_SIG = 0x69545874; + protected static final int bKGD_HEAD_SIG = 0x624b4744; + protected static final int hIST_HEAD_SIG = 0x68495354; + protected static final int pHYs_HEAD_SIG = 0x70485973; + protected static final int sPLT_HEAD_SIG = 0x73504c54; + protected static final int tIME_HEAD_SIG = 0x74494d45; + protected static final int tRNS_HEAD_SIG = 0x74524e53; + + public PNGChunk() { + length = 0; + chunkType = 0; + chunkData = null; // Not populated till we know the length + this.crc = new CRC32(); + } + + /** Construct a PNGChunk object of the appropriate subtype based on the chunk type. */ + public static PNGChunk makePNGChunk(long length, int sig) { + switch (sig) { + case IHDR_HEAD_SIG: + return new IhdrChunk(sig, length); + case IDAT_HEAD_SIG: + return new IdatChunk(sig, length); + case IEND_HEAD_SIG: + return new IendChunk(sig, length); + case PLTE_HEAD_SIG: + return new PlteChunk(sig, length); + + case bKGD_HEAD_SIG: + return new BkgdChunk(sig, length); + case cHRM_HEAD_SIG: + return new ChrmChunk(sig, length); + case gAMA_HEAD_SIG: + return new GamaChunk(sig, length); + case hIST_HEAD_SIG: + return new HistChunk(sig, length); + case iCCP_HEAD_SIG: + return new IccpChunk(sig, length); + case iTXt_HEAD_SIG: + return new ItxtChunk(sig, length); + case pHYs_HEAD_SIG: + return new PhysChunk(sig, length); + case sBIT_HEAD_SIG: + return new SbitChunk(sig, length); + case sPLT_HEAD_SIG: + return new SpltChunk(sig, length); + case sRGB_HEAD_SIG: + return new SrgbChunk(sig, length); + case tEXt_HEAD_SIG: + return new TextChunk(sig, length); + case tIME_HEAD_SIG: + return new TimeChunk(sig, length); + case tRNS_HEAD_SIG: + return new TrnsChunk(sig, length); + case zTXt_HEAD_SIG: + return new ZtxtChunk(sig, length); + default: + return new UnknownChunk(sig, length); + } + } + + /** Hand the chunk the NISO metadata object if it needs to put information into it. */ + public void setNisoMetadata(NisoImageMetadata nmd) { + _nisoMetadata = nmd; + } + + /** Hand the main property list to the chunk */ + public void setPropertyList(List lst) { + _propList = lst; + } + + /** Give the chunk a reference to the PNG module. */ + public void setModule(PngModule mdl) { + _module = mdl; + } + + /** Give the chunk a reference to the data stream. */ + public void setInputStream(DataInputStream dstrm) { + _dstream = dstrm; + } + + public long getLength() { + return length; + } + + public int getChunkType() { + return chunkType; + } + + public char[] getChunkData() { + return chunkData; + } + + public long getCRC() { + return crc.getValue(); + } + + /** + * Process a chunk. When this is called, the input stream needs to have read the type and length + * and be positioned at the start of the data. + * + *

The default behavior is to eat the chunk. This should be the behavior only for UnknownChunk + * when we're done. + */ + public void processChunk(RepInfo info) throws Exception { + processChunkCommon(info); + _module.eatChunk(this); // TODO temporary + } + + /** Common code to call at the start of every processChunk method. */ + public void processChunkCommon(RepInfo info) throws PNGException { + if (ancillary && !duplicateAllowed) { + if (_module.isChunkSeen(chunkType)) { + ErrorMessage msg = + new ErrorMessage( + MessageConstants.PNG_GDM_38, + String.format(MessageConstants.PNG_GDM_38_SUB.getMessage(), chunkTypeString())); + info.setMessage(msg); + info.setWellFormed(false); + throw new PNGException(MessageConstants.PNG_GDM_39); + } + _module.setChunkSeen(chunkType); + } + int[] chunkTypeVal = chunkTypeBytes(); + for (int i = 0; i < 4; i++) { + crc.update(chunkTypeVal[i]); + } + } + + /* Use these methods exclusively to read the data portion + * (and nothing else), so that the CRC is calculated. + */ + + /** Read a 4-byte unsigned integer and update the CRC */ + public long readUnsignedInt() throws IOException { + long val = 0; + for (int i = 0; i < 4; i++) { + int b = _dstream.readUnsignedByte(); + val = (val << 8) | b; + crc.update(b); + } + return val; + } + + /** Read a 2-byte unsigned integer and update the CRC */ + public int readUnsignedShort() throws IOException { + int val = 0; + for (int i = 0; i < 2; i++) { + int b = _dstream.readUnsignedByte(); + val = (val << 8) | b; + crc.update(b); + } + return val; + } - case bKGD_HEAD_SIG: - return new BkgdChunk (sig, length); - case cHRM_HEAD_SIG: - return new ChrmChunk (sig, length); - case gAMA_HEAD_SIG: - return new GamaChunk (sig, length); - case hIST_HEAD_SIG: - return new HistChunk (sig, length); - case iCCP_HEAD_SIG: - return new IccpChunk (sig, length); - case iTXt_HEAD_SIG: - return new ItxtChunk (sig, length); - case pHYs_HEAD_SIG: - return new PhysChunk (sig, length); - case sBIT_HEAD_SIG: - return new SbitChunk (sig, length); - case sPLT_HEAD_SIG: - return new SpltChunk (sig, length); - case sRGB_HEAD_SIG: - return new SrgbChunk (sig, length); - case tEXt_HEAD_SIG: - return new TextChunk (sig, length); - case tIME_HEAD_SIG: - return new TimeChunk (sig, length); - case tRNS_HEAD_SIG: - return new TrnsChunk (sig, length); - case zTXt_HEAD_SIG: - return new ZtxtChunk (sig, length); - default: - return new UnknownChunk (sig, length); - } - } - - /** Hand the chunk the NISO metadata object if it needs to - * put information into it. */ - public void setNisoMetadata (NisoImageMetadata nmd) { - _nisoMetadata = nmd; - } - - /** Hand the main property list to the chunk */ - public void setPropertyList (List lst) { - _propList = lst; - } - - /** Give the chunk a reference to the PNG module. */ - public void setModule (PngModule mdl) { - _module = mdl; - } - - /** Give the chunk a reference to the data stream. */ - public void setInputStream (DataInputStream dstrm) { - _dstream = dstrm; - } - - public long getLength () { - return length; - } - - public int getChunkType () { - return chunkType; - } - - public char[] getChunkData() { - return chunkData; - } - - public long getCRC () { - return crc.getValue(); - } - - /** Process a chunk. When this is called, the input stream needs - * to have read the type and length and be positioned at - * the start of the data. - * - * The default behavior is to eat the chunk. This should - * be the behavior only for UnknownChunk when we're done. - */ - public void processChunk(RepInfo info) throws Exception { - processChunkCommon(info); - _module.eatChunk(this); // TODO temporary - } + /** Read a single byte and update the CRC */ + public int readUnsignedByte() throws IOException { + int b = _dstream.readUnsignedByte(); + crc.update(b); + return b; + } - /** Common code to call at the start of every processChunk method. - */ - public void processChunkCommon (RepInfo info) throws PNGException { - if (ancillary && !duplicateAllowed) { - if (_module.isChunkSeen(chunkType)) { - ErrorMessage msg = new ErrorMessage - (MessageConstants.PNG_GDM_38, - String.format(MessageConstants.PNG_GDM_38_SUB.getMessage(), - chunkTypeString())); - info.setMessage (msg); - info.setWellFormed (false); - throw new PNGException (MessageConstants.PNG_GDM_39); - } - _module.setChunkSeen (chunkType); - } - int[] chunkTypeVal = chunkTypeBytes(); - for (int i = 0; i < 4; i++) { - crc.update(chunkTypeVal[i]); - } - } - - /* Use these methods exclusively to read the data portion - * (and nothing else), so that the CRC is calculated. - */ - - /** Read a 4-byte unsigned integer and update the CRC */ - public long readUnsignedInt() throws IOException { - long val = 0; - for (int i = 0; i < 4; i++) { - int b = _dstream.readUnsignedByte(); - val = (val << 8) | b; - crc.update(b); - } - return val; - } - - /** Read a 2-byte unsigned integer and update the CRC */ - public int readUnsignedShort() throws IOException { - int val = 0; - for (int i = 0; i < 2; i++) { - int b = _dstream.readUnsignedByte(); - val = (val << 8) | b; - crc.update(b); - } - return val; - } - - /** Read a single byte and update the CRC */ - public int readUnsignedByte() throws IOException { - int b = _dstream.readUnsignedByte(); - crc.update(b); - return b; - } + /** Skip over all the bytes, updating the CRC */ + public void skipBytes(int count) throws IOException { + for (int i = 0; i < count; i++) { + int b = _dstream.readUnsignedByte(); + crc.update(b); + } + } - /** Skip over all the bytes, updating the CRC */ - public void skipBytes(int count) throws IOException { - for (int i = 0; i < count; i++) { - int b = _dstream.readUnsignedByte(); - crc.update(b); - } - } - - /** Read the CRC itself. Naturally, this doesn't update - * the CRC. - */ - public long readCRC() throws IOException { - long c = ModuleBase.readUnsignedInt (_dstream, true); - return c; - } + /** Read the CRC itself. Naturally, this doesn't update the CRC. */ + public long readCRC() throws IOException { + long c = ModuleBase.readUnsignedInt(_dstream, true); + return c; + } + /* Convert chunk type to string value. */ + public String chunkTypeString() { + int[] bytes = chunkTypeBytes(); + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < 4; i++) { + int b = bytes[i]; + if (b < 32) { + sb.append("?"); + } else { + sb.append((char) b); + } + } + return sb.toString(); + } - - /* Convert chunk type to string value. */ - public String chunkTypeString() { - int[] bytes = chunkTypeBytes(); - StringBuilder sb = new StringBuilder(); - for (int i = 0; i < 4; i++) { - int b = bytes[i]; - if (b < 32) { - sb.append("?"); - } else { - sb.append ((char) b); - } - } - return sb.toString(); - } - - /* Convert chunk type to byte array. */ - private int[] chunkTypeBytes() { - int[] bytes = new int[4]; - bytes[0] = (chunkType >> 24) & 0X7F; - bytes[1] = (chunkType >> 16) & 0X7F; - bytes[2] = (chunkType >> 8) & 0X7F; - bytes[3] = chunkType & 0X7F; - return bytes; - } + /* Convert chunk type to byte array. */ + private int[] chunkTypeBytes() { + int[] bytes = new int[4]; + bytes[0] = (chunkType >> 24) & 0X7F; + bytes[1] = (chunkType >> 16) & 0X7F; + bytes[2] = (chunkType >> 8) & 0X7F; + bytes[3] = chunkType & 0X7F; + return bytes; + } } diff --git a/jhove-ext-modules/src/main/java/com/mcgath/jhove/module/png/PNGException.java b/jhove-ext-modules/src/main/java/com/mcgath/jhove/module/png/PNGException.java index e172524b7..dbec426a7 100644 --- a/jhove-ext-modules/src/main/java/com/mcgath/jhove/module/png/PNGException.java +++ b/jhove-ext-modules/src/main/java/com/mcgath/jhove/module/png/PNGException.java @@ -4,17 +4,13 @@ /** Exception class specific to the PNG module */ public class PNGException extends Exception { - - /** - * Just to keep the compiler happy - */ - private static final long serialVersionUID = 1L; - public PNGException (String msg) { - - } + /** Just to keep the compiler happy */ + private static final long serialVersionUID = 1L; - public PNGException(JhoveMessage msg) { - super (msg.getMessage()); - } + public PNGException(String msg) {} + + public PNGException(JhoveMessage msg) { + super(msg.getMessage()); + } } diff --git a/jhove-ext-modules/src/main/java/com/mcgath/jhove/module/png/PhysChunk.java b/jhove-ext-modules/src/main/java/com/mcgath/jhove/module/png/PhysChunk.java index 15f4e4c4c..7d05402d4 100644 --- a/jhove-ext-modules/src/main/java/com/mcgath/jhove/module/png/PhysChunk.java +++ b/jhove-ext-modules/src/main/java/com/mcgath/jhove/module/png/PhysChunk.java @@ -5,71 +5,62 @@ import edu.harvard.hul.ois.jhove.PropertyType; import edu.harvard.hul.ois.jhove.RepInfo; -/** The pHYS chunk, which indicates pixel aspect ratio or size. - * - */ +/** The pHYS chunk, which indicates pixel aspect ratio or size. */ public class PhysChunk extends PNGChunk { - /** Constructor */ - public PhysChunk(int sig, long leng) { - chunkType = sig; - length = leng; - ancillary = true; - duplicateAllowed = false; - } - - /** Process the data in the chunk. - * - */ - @Override - public void processChunk(RepInfo info) throws Exception { - - processChunkCommon(info); - ErrorMessage msg = null; - if (_module.isIdatSeen()) { - msg = new ErrorMessage (MessageConstants.PNG_GDM_31); - } - if (length < 9) { - msg = new ErrorMessage (MessageConstants.PNG_GDM_32); - } - if (msg != null) { - info.setMessage (msg); - info.setWellFormed (false); - throw new PNGException (MessageConstants.PNG_GDM_33); - } - long xPixelsPerUnit = readUnsignedInt(); - long yPixelsPerUnit = readUnsignedInt(); - int unit = readUnsignedByte(); - Property prop = new Property ("X pixels per unit", - PropertyType.INTEGER, - Long.valueOf(xPixelsPerUnit)); - info.setProperty (prop); - prop = new Property ("Y pixels per unit", - PropertyType.INTEGER, - Long.valueOf(yPixelsPerUnit)); - info.setProperty (prop); - String unitStr; - switch (unit) { - case 0: - unitStr = "Undefined"; - break; - case 1: - // Stick with British spelling, as in the spec - unitStr = "Metre"; - break; - default: - // Should this even be allowed? - unitStr = Integer.toString (unit); - break; - } - prop = new Property ("Pixel unit", - PropertyType.STRING, - unitStr); - info.setProperty (prop); - - // Discard any extra - for (int i = 0; i < length - 9; i++) { - readUnsignedByte(); - } - } + /** Constructor */ + public PhysChunk(int sig, long leng) { + chunkType = sig; + length = leng; + ancillary = true; + duplicateAllowed = false; + } + + /** Process the data in the chunk. */ + @Override + public void processChunk(RepInfo info) throws Exception { + + processChunkCommon(info); + ErrorMessage msg = null; + if (_module.isIdatSeen()) { + msg = new ErrorMessage(MessageConstants.PNG_GDM_31); + } + if (length < 9) { + msg = new ErrorMessage(MessageConstants.PNG_GDM_32); + } + if (msg != null) { + info.setMessage(msg); + info.setWellFormed(false); + throw new PNGException(MessageConstants.PNG_GDM_33); + } + long xPixelsPerUnit = readUnsignedInt(); + long yPixelsPerUnit = readUnsignedInt(); + int unit = readUnsignedByte(); + Property prop = + new Property("X pixels per unit", PropertyType.INTEGER, Long.valueOf(xPixelsPerUnit)); + info.setProperty(prop); + prop = new Property("Y pixels per unit", PropertyType.INTEGER, Long.valueOf(yPixelsPerUnit)); + info.setProperty(prop); + String unitStr; + switch (unit) { + case 0: + unitStr = "Undefined"; + break; + case 1: + // Stick with British spelling, as in the spec + unitStr = "Metre"; + break; + default: + // Should this even be allowed? + unitStr = Integer.toString(unit); + break; + } + prop = new Property("Pixel unit", PropertyType.STRING, unitStr); + info.setProperty(prop); + + // Discard any extra + for (int i = 0; i < length - 9; i++) { + readUnsignedByte(); + } + } } diff --git a/jhove-ext-modules/src/main/java/com/mcgath/jhove/module/png/PlteChunk.java b/jhove-ext-modules/src/main/java/com/mcgath/jhove/module/png/PlteChunk.java index 8ee14d188..cff47ddc1 100644 --- a/jhove-ext-modules/src/main/java/com/mcgath/jhove/module/png/PlteChunk.java +++ b/jhove-ext-modules/src/main/java/com/mcgath/jhove/module/png/PlteChunk.java @@ -3,43 +3,40 @@ import edu.harvard.hul.ois.jhove.ErrorMessage; import edu.harvard.hul.ois.jhove.RepInfo; -/** Representation of the PLTE chunk - */ +/** Representation of the PLTE chunk */ public class PlteChunk extends PNGChunk { - /** Constructor */ - public PlteChunk(int sig, long leng) { - chunkType = sig; - length = leng; - ancillary = false; - } - - @Override - public void processChunk(RepInfo info) throws Exception { - ErrorMessage msg = null; - processChunkCommon(info); - if (_module.isPlteSeen()) { - msg = new ErrorMessage (MessageConstants.PNG_GDM_34); - } - _module.setPlteSeen(true); + /** Constructor */ + public PlteChunk(int sig, long leng) { + chunkType = sig; + length = leng; + ancillary = false; + } - if (_module.isIdatSeen()) { - msg = new ErrorMessage(MessageConstants.PNG_GDM_35); - } - if ((length % 3) != 0) { - // must be a multiple of 3 bytes - msg = new ErrorMessage(String.format(MessageConstants.PNG_GDM_36.getMessage(), - length)); - } - if (msg != null) { - info.setMessage(msg); - info.setWellFormed(false); - throw new PNGException (MessageConstants.PNG_GDM_37); - } - for (int i = 0; i 4) { - ErrorMessage msg = new ErrorMessage (MessageConstants.PNG_GDM_42); - info.setMessage (msg); - info.setWellFormed (false); - throw new PNGException (MessageConstants.PNG_GDM_41); - } - int[] sbitVal = new int[(int) length]; - for (int i = 0; i < length; i++) { - sbitVal[i] = readUnsignedByte(); - } - Property prop = new Property ("Significant bits", - PropertyType.INTEGER, - PropertyArity.ARRAY, - sbitVal); - _propList.add (prop); - } + /** Constructor */ + public SbitChunk(int sig, long leng) { + chunkType = sig; + length = leng; + ancillary = true; + duplicateAllowed = false; + } + + /** + * The SBIT chunk contains 1 to 4 bytes of information giving the number of significant bits per + * color. Ideally, we should check this against the color model. + */ + @Override + public void processChunk(RepInfo info) throws Exception { + processChunkCommon(info); + if (_module.isPlteSeen() || _module.isIdatSeen()) { + ErrorMessage msg = new ErrorMessage(MessageConstants.PNG_GDM_40); + info.setMessage(msg); + info.setWellFormed(false); + throw new PNGException(MessageConstants.PNG_GDM_41); + } + if (length == 0 || length > 4) { + ErrorMessage msg = new ErrorMessage(MessageConstants.PNG_GDM_42); + info.setMessage(msg); + info.setWellFormed(false); + throw new PNGException(MessageConstants.PNG_GDM_41); + } + int[] sbitVal = new int[(int) length]; + for (int i = 0; i < length; i++) { + sbitVal[i] = readUnsignedByte(); + } + Property prop = + new Property("Significant bits", PropertyType.INTEGER, PropertyArity.ARRAY, sbitVal); + _propList.add(prop); + } } diff --git a/jhove-ext-modules/src/main/java/com/mcgath/jhove/module/png/SpltChunk.java b/jhove-ext-modules/src/main/java/com/mcgath/jhove/module/png/SpltChunk.java index 43f267454..d55732633 100644 --- a/jhove-ext-modules/src/main/java/com/mcgath/jhove/module/png/SpltChunk.java +++ b/jhove-ext-modules/src/main/java/com/mcgath/jhove/module/png/SpltChunk.java @@ -6,87 +6,89 @@ /** The suggested palette (sPLT) chunk */ public class SpltChunk extends PNGChunk { - /** Constructor */ - public SpltChunk(int sig, long leng) { - chunkType = sig; - length = leng; - ancillary = true; - duplicateAllowed = true; - } - - /** Process the chunk. We add a property for the suggested - * palette by adding it to the module's list of sPLT's. - */ - @Override - public void processChunk(RepInfo info) throws Exception { - String paletteName = null; - processChunkCommon(info); - if (_module.isIdatSeen()) { - ErrorMessage msg = new ErrorMessage (MessageConstants.PNG_GDM_43); - info.setMessage (msg); - info.setWellFormed (false); - throw new PNGException (MessageConstants.PNG_GDM_44); - } - int lengthLeft = (int) length; - - // Read the name. - int maxNameLen; - if (length > 80) { - maxNameLen = 80; - } else { - maxNameLen = (int) length; - } - StringBuilder sb = new StringBuilder(); - for (int i = 0; i < maxNameLen; i++) { - char c = (char) readUnsignedByte(); - --lengthLeft; - if (c == 0) { - paletteName = sb.toString(); - break; - } - sb.append(c); - } - if (paletteName == null) { - // No null seen to terminate name - ErrorMessage msg = new ErrorMessage (MessageConstants.PNG_GDM_45); - info.setMessage (msg); - info.setWellFormed (false); - throw new PNGException (MessageConstants.PNG_GDM_44); - } - - // Sample depth must be 8 or 16 bits - int sampleDepth = readUnsignedByte(); - --lengthLeft; - if (sampleDepth != 8 && sampleDepth != 16) { - ErrorMessage msg = new ErrorMessage (MessageConstants.PNG_GDM_46.getMessage(), - String.format(MessageConstants.PNG_GDM_46_SUB.getMessage(), - sampleDepth)); - info.setMessage (msg); - info.setWellFormed (false); - throw new PNGException (MessageConstants.PNG_GDM_44); - } - - // The rest of the chunk is RGBA sample values plus frequency, - // with each sample being 10 bytes if the sample depth is 16 - // and 6 bytes if it's 8. We don't care about the content - // but have to make sure the size is properly divisible, and - // we report the sample count. - if ((sampleDepth == 8 && (lengthLeft % 6) != 0) || - (sampleDepth == 16 && (lengthLeft % 10) != 0)) { - ErrorMessage msg = new ErrorMessage (MessageConstants.PNG_GDM_47); - info.setMessage (msg); - info.setWellFormed (false); - throw new PNGException (MessageConstants.PNG_GDM_44); - } - int nSamples; - if (sampleDepth == 8) { - nSamples = lengthLeft / 6; - } else { - nSamples = lengthLeft / 10; - } - _module.addSplt(paletteName, sampleDepth, nSamples); - for (int i = 0; i < lengthLeft; i++) { - readUnsignedByte(); - } - } + /** Constructor */ + public SpltChunk(int sig, long leng) { + chunkType = sig; + length = leng; + ancillary = true; + duplicateAllowed = true; + } + + /** + * Process the chunk. We add a property for the suggested palette by adding it to the module's + * list of sPLT's. + */ + @Override + public void processChunk(RepInfo info) throws Exception { + String paletteName = null; + processChunkCommon(info); + if (_module.isIdatSeen()) { + ErrorMessage msg = new ErrorMessage(MessageConstants.PNG_GDM_43); + info.setMessage(msg); + info.setWellFormed(false); + throw new PNGException(MessageConstants.PNG_GDM_44); + } + int lengthLeft = (int) length; + + // Read the name. + int maxNameLen; + if (length > 80) { + maxNameLen = 80; + } else { + maxNameLen = (int) length; + } + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < maxNameLen; i++) { + char c = (char) readUnsignedByte(); + --lengthLeft; + if (c == 0) { + paletteName = sb.toString(); + break; + } + sb.append(c); + } + if (paletteName == null) { + // No null seen to terminate name + ErrorMessage msg = new ErrorMessage(MessageConstants.PNG_GDM_45); + info.setMessage(msg); + info.setWellFormed(false); + throw new PNGException(MessageConstants.PNG_GDM_44); + } + + // Sample depth must be 8 or 16 bits + int sampleDepth = readUnsignedByte(); + --lengthLeft; + if (sampleDepth != 8 && sampleDepth != 16) { + ErrorMessage msg = + new ErrorMessage( + MessageConstants.PNG_GDM_46.getMessage(), + String.format(MessageConstants.PNG_GDM_46_SUB.getMessage(), sampleDepth)); + info.setMessage(msg); + info.setWellFormed(false); + throw new PNGException(MessageConstants.PNG_GDM_44); + } + + // The rest of the chunk is RGBA sample values plus frequency, + // with each sample being 10 bytes if the sample depth is 16 + // and 6 bytes if it's 8. We don't care about the content + // but have to make sure the size is properly divisible, and + // we report the sample count. + if ((sampleDepth == 8 && (lengthLeft % 6) != 0) + || (sampleDepth == 16 && (lengthLeft % 10) != 0)) { + ErrorMessage msg = new ErrorMessage(MessageConstants.PNG_GDM_47); + info.setMessage(msg); + info.setWellFormed(false); + throw new PNGException(MessageConstants.PNG_GDM_44); + } + int nSamples; + if (sampleDepth == 8) { + nSamples = lengthLeft / 6; + } else { + nSamples = lengthLeft / 10; + } + _module.addSplt(paletteName, sampleDepth, nSamples); + for (int i = 0; i < lengthLeft; i++) { + readUnsignedByte(); + } + } } diff --git a/jhove-ext-modules/src/main/java/com/mcgath/jhove/module/png/SrgbChunk.java b/jhove-ext-modules/src/main/java/com/mcgath/jhove/module/png/SrgbChunk.java index 7f66e9caf..b30ee0c3e 100644 --- a/jhove-ext-modules/src/main/java/com/mcgath/jhove/module/png/SrgbChunk.java +++ b/jhove-ext-modules/src/main/java/com/mcgath/jhove/module/png/SrgbChunk.java @@ -8,61 +8,51 @@ /** The sRGB chunk, specifying sRGB color intent */ public class SrgbChunk extends PNGChunk { - private static final String intents[] = { - "Perceptual", - "Relative colorimetric", - "Saturation", - "Absolute colorimetric" - }; - - /** Constructor */ - public SrgbChunk(int sig, long leng) { - chunkType = sig; - length = leng; - ancillary = true; - duplicateAllowed = false; - } - - /** Process the data in the chunk. */ - @Override - public void processChunk(RepInfo info) throws Exception { - processChunkCommon(info); - ErrorMessage msg = null; - int colorIntent = 0; - if (_module.isPlteSeen()) { - msg = new ErrorMessage (MessageConstants.PNG_GDM_48); - } - else if (_module.isIdatSeen()) { - msg = new ErrorMessage (MessageConstants.PNG_GDM_49); - } - else if (_module.isChunkSeen(PNGChunk.iCCP_HEAD_SIG)) { - msg = new ErrorMessage (MessageConstants.PNG_GDM_50); - } - else if (length == 0) { - msg = new ErrorMessage (MessageConstants.PNG_GDM_51); - } - else { - colorIntent = readUnsignedByte(); - if (colorIntent > 3) { - msg = new ErrorMessage ( - String.format(MessageConstants.PNG_GDM_52.getMessage(), - colorIntent)); - } - } - if (msg != null) { - info.setMessage (msg); - info.setWellFormed (false); - throw new PNGException (MessageConstants.PNG_GDM_53); - } - Property prop = new Property ("SRGB rendering intent", - PropertyType.STRING, - intents[colorIntent]); - info.setProperty (prop); - - // Eat any extra bytes - for (int i = 0; i 3) { + msg = + new ErrorMessage(String.format(MessageConstants.PNG_GDM_52.getMessage(), colorIntent)); + } + } + if (msg != null) { + info.setMessage(msg); + info.setWellFormed(false); + throw new PNGException(MessageConstants.PNG_GDM_53); + } + Property prop = + new Property("SRGB rendering intent", PropertyType.STRING, intents[colorIntent]); + info.setProperty(prop); + + // Eat any extra bytes + for (int i = 0; i < length - 1; i++) { + readUnsignedByte(); + } + } } diff --git a/jhove-ext-modules/src/main/java/com/mcgath/jhove/module/png/TextChunk.java b/jhove-ext-modules/src/main/java/com/mcgath/jhove/module/png/TextChunk.java index ebd1db4d7..c082dfdc4 100644 --- a/jhove-ext-modules/src/main/java/com/mcgath/jhove/module/png/TextChunk.java +++ b/jhove-ext-modules/src/main/java/com/mcgath/jhove/module/png/TextChunk.java @@ -1,54 +1,48 @@ package com.mcgath.jhove.module.png; import edu.harvard.hul.ois.jhove.ErrorMessage; -//import edu.harvard.hul.ois.jhove.Property; -//import edu.harvard.hul.ois.jhove.PropertyType; import edu.harvard.hul.ois.jhove.RepInfo; /** Representation of the tEXt (plain text) chunk */ public class TextChunk extends GeneralTextChunk { + /** Constructor */ + public TextChunk(int sig, long leng) { + chunkType = sig; + length = leng; + ancillary = true; + duplicateAllowed = true; + } - - /** Constructor */ - public TextChunk(int sig, long leng) { - chunkType = sig; - length = leng; - ancillary = true; - duplicateAllowed = true; - } - - @Override - public void processChunk(RepInfo info) throws Exception { - processChunkCommon(info); - - // The tEXt chunk consists of a keyword, a null, and a value. - // There needs to be exactly one null in the data. - StringBuilder sb = new StringBuilder(); - String keyword = null; - String value = null; - ErrorMessage msg; - for (int i = 0; i 12 || - day == 0 || day > 31 || - hour > 23 || - minute > 59 || - second > 60) { // 60 can be valid with a leap second - ErrorMessage msg = new ErrorMessage (MessageConstants.PNG_GDM_58); - info.setMessage(msg); - info.setValid(false); // just call this invalid, not ill-formed - return; - } - // Java Calendar is based January as 0. - Calendar cal = Calendar.getInstance(TimeZone.getTimeZone("GMT")); - cal.set(year, - month - 1, - day, - hour, - minute, - second); - Property prop = new Property ("Time modified", - PropertyType.DATE, - cal.getTime()); - _propList.add(prop); - } + /** Constructor */ + public TimeChunk(int sig, long leng) { + chunkType = sig; + length = leng; + ancillary = true; + duplicateAllowed = false; + } + + /** + * The tIME chunk has the following: 2 bytes year (short), 1 byte month, 1 byte day, 1 byte hour, + * 1 byte minute, 1 byte second. It's supposed to be the UTC time. + */ + @Override + public void processChunk(RepInfo info) throws Exception { + processChunkCommon(info); + if (length < 7) { + ErrorMessage msg = new ErrorMessage(MessageConstants.PNG_GDM_56); + info.setMessage(msg); + info.setWellFormed(false); + throw new PNGException(MessageConstants.PNG_GDM_57); + } + int year = readUnsignedShort(); + int month = readUnsignedByte(); + int day = readUnsignedByte(); + int hour = readUnsignedByte(); + int minute = readUnsignedByte(); + int second = readUnsignedByte(); + // Sanity checks. + if (month == 0 + || month > 12 + || day == 0 + || day > 31 + || hour > 23 + || minute > 59 + || second > 60) { // 60 can be valid with a leap second + ErrorMessage msg = new ErrorMessage(MessageConstants.PNG_GDM_58); + info.setMessage(msg); + info.setValid(false); // just call this invalid, not ill-formed + return; + } + // Java Calendar is based January as 0. + Calendar cal = Calendar.getInstance(TimeZone.getTimeZone("GMT")); + cal.set(year, month - 1, day, hour, minute, second); + Property prop = new Property("Time modified", PropertyType.DATE, cal.getTime()); + _propList.add(prop); + } } diff --git a/jhove-ext-modules/src/main/java/com/mcgath/jhove/module/png/TrnsChunk.java b/jhove-ext-modules/src/main/java/com/mcgath/jhove/module/png/TrnsChunk.java index 120583f8d..5b7eb9735 100644 --- a/jhove-ext-modules/src/main/java/com/mcgath/jhove/module/png/TrnsChunk.java +++ b/jhove-ext-modules/src/main/java/com/mcgath/jhove/module/png/TrnsChunk.java @@ -6,115 +6,107 @@ import edu.harvard.hul.ois.jhove.PropertyType; import edu.harvard.hul.ois.jhove.RepInfo; -/** The Transparency chunk. - * - * The interpretation of the Transparency chunk depends on - * the color type. - * - * For color type 0 (greyscale) there is one two-byte integer, - * representing the gray sample value. - * - * For color type 2 (RGB) there are three two-byte integers, - * representing the red, blue, and green sample values respectively. - * - * For color type 3 (palette), the values are bytes giving the alpha value - * for the palette index. The number of values may be less than or - * equal to the length of the palette. +/** + * The Transparency chunk. + * + *

The interpretation of the Transparency chunk depends on the color type. + * + *

For color type 0 (greyscale) there is one two-byte integer, representing the gray sample + * value. + * + *

For color type 2 (RGB) there are three two-byte integers, representing the red, blue, and + * green sample values respectively. + * + *

For color type 3 (palette), the values are bytes giving the alpha value for the palette index. + * The number of values may be less than or equal to the length of the palette. */ public class TrnsChunk extends PNGChunk { - /** Constructor */ - public TrnsChunk(int sig, long leng) { - chunkType = sig; - length = leng; - ancillary = true; - duplicateAllowed = false; - } - - /** Process the data in the chunk. */ - @Override - public void processChunk(RepInfo info) throws Exception { - processChunkCommon(info); - ErrorMessage msg = null; - if (_module.isIdatSeen()) { - msg = new ErrorMessage (MessageConstants.PNG_GDM_59); - } - - int colorType = _module.getColorType(); - int lengthLeft = (int) length; + /** Constructor */ + public TrnsChunk(int sig, long leng) { + chunkType = sig; + length = leng; + ancillary = true; + duplicateAllowed = false; + } - // Make sure there are enough bytes - if ((colorType == 0 && length < 2) || (colorType == 2 && length < 6)) { - msg = new ErrorMessage (MessageConstants.PNG_GDM_60); - } - - // tRNS chunk allowed only with certain color types - if (colorType != 0 && colorType != 2 && colorType != 3) { - msg = new ErrorMessage ( - String.format( - MessageConstants.PNG_GDM_61.getMessage(), - colorType)); - } - - if (msg != null) { - info.setMessage (msg); - info.setWellFormed(false); - throw new PNGException (MessageConstants.PNG_GDM_62); - } + /** Process the data in the chunk. */ + @Override + public void processChunk(RepInfo info) throws Exception { + processChunkCommon(info); + ErrorMessage msg = null; + if (_module.isIdatSeen()) { + msg = new ErrorMessage(MessageConstants.PNG_GDM_59); + } - switch (colorType) { - case 0: - // Grayscale, one short value - int transGray = readUnsignedShort(); - info.setProperty (new Property ("Transparent grey value", - PropertyType.INTEGER, - Integer.valueOf(transGray))); - lengthLeft = (int) length - 2; - break; - - case 2: - // RGB color, three short values - int transRed = readUnsignedShort(); - int transGreen = readUnsignedShort(); - int transBlue = readUnsignedShort(); - info.setProperty (new Property ("Transparent red value", - PropertyType.INTEGER, - Integer.valueOf(transRed))); - info.setProperty (new Property ("Transparent green value", - PropertyType.INTEGER, - Integer.valueOf(transGreen))); - info.setProperty (new Property ("Transparent blue value", - PropertyType.INTEGER, - Integer.valueOf(transBlue))); - lengthLeft = (int) length - 6; - break; - - case 3: - // Palette color, variable number of byte values - int nTrans = (int) length; - if (nTrans > 256) { - nTrans = 256; - lengthLeft = (int) length - 256; - } - else { - lengthLeft = 0; - } - int[] alpha = new int[nTrans]; - for (int i = 0; i < nTrans; i++) { - alpha[i] = readUnsignedByte(); - } - info.setProperty (new Property ("Alpha for palette index", - PropertyType.INTEGER, - PropertyArity.ARRAY, - alpha)); - break; - default: - // We've already made sure this is unreachable - break; - } - for (int i = 0; i < lengthLeft; i++) { - readUnsignedByte(); - } - } - + int colorType = _module.getColorType(); + int lengthLeft = (int) length; + + // Make sure there are enough bytes + if ((colorType == 0 && length < 2) || (colorType == 2 && length < 6)) { + msg = new ErrorMessage(MessageConstants.PNG_GDM_60); + } + + // tRNS chunk allowed only with certain color types + if (colorType != 0 && colorType != 2 && colorType != 3) { + msg = new ErrorMessage(String.format(MessageConstants.PNG_GDM_61.getMessage(), colorType)); + } + + if (msg != null) { + info.setMessage(msg); + info.setWellFormed(false); + throw new PNGException(MessageConstants.PNG_GDM_62); + } + + switch (colorType) { + case 0: + // Grayscale, one short value + int transGray = readUnsignedShort(); + info.setProperty( + new Property( + "Transparent grey value", PropertyType.INTEGER, Integer.valueOf(transGray))); + lengthLeft = (int) length - 2; + break; + + case 2: + // RGB color, three short values + int transRed = readUnsignedShort(); + int transGreen = readUnsignedShort(); + int transBlue = readUnsignedShort(); + info.setProperty( + new Property("Transparent red value", PropertyType.INTEGER, Integer.valueOf(transRed))); + info.setProperty( + new Property( + "Transparent green value", PropertyType.INTEGER, Integer.valueOf(transGreen))); + info.setProperty( + new Property( + "Transparent blue value", PropertyType.INTEGER, Integer.valueOf(transBlue))); + lengthLeft = (int) length - 6; + break; + + case 3: + // Palette color, variable number of byte values + int nTrans = (int) length; + if (nTrans > 256) { + nTrans = 256; + lengthLeft = (int) length - 256; + } else { + lengthLeft = 0; + } + int[] alpha = new int[nTrans]; + for (int i = 0; i < nTrans; i++) { + alpha[i] = readUnsignedByte(); + } + info.setProperty( + new Property( + "Alpha for palette index", PropertyType.INTEGER, PropertyArity.ARRAY, alpha)); + break; + default: + // We've already made sure this is unreachable + break; + } + for (int i = 0; i < lengthLeft; i++) { + readUnsignedByte(); + } + } } diff --git a/jhove-ext-modules/src/main/java/com/mcgath/jhove/module/png/UnknownChunk.java b/jhove-ext-modules/src/main/java/com/mcgath/jhove/module/png/UnknownChunk.java index c21a70f48..0ccbc7845 100644 --- a/jhove-ext-modules/src/main/java/com/mcgath/jhove/module/png/UnknownChunk.java +++ b/jhove-ext-modules/src/main/java/com/mcgath/jhove/module/png/UnknownChunk.java @@ -1,17 +1,13 @@ package com.mcgath.jhove.module.png; -//import edu.harvard.hul.ois.jhove.RepInfo; +// import edu.harvard.hul.ois.jhove.RepInfo; -/** Class for chunks of unknown type. These might be errors - * or extensions. - */ +/** Class for chunks of unknown type. These might be errors or extensions. */ public class UnknownChunk extends PNGChunk { - - /** Constructor */ - public UnknownChunk(int sig, long leng) { - chunkType = sig; - length = leng; - } - + /** Constructor */ + public UnknownChunk(int sig, long leng) { + chunkType = sig; + length = leng; + } } diff --git a/jhove-ext-modules/src/main/java/com/mcgath/jhove/module/png/ZtxtChunk.java b/jhove-ext-modules/src/main/java/com/mcgath/jhove/module/png/ZtxtChunk.java index 30ea4d81d..abca97491 100644 --- a/jhove-ext-modules/src/main/java/com/mcgath/jhove/module/png/ZtxtChunk.java +++ b/jhove-ext-modules/src/main/java/com/mcgath/jhove/module/png/ZtxtChunk.java @@ -1,83 +1,84 @@ package com.mcgath.jhove.module.png; -import java.util.zip.DataFormatException; - import edu.harvard.hul.ois.jhove.ErrorMessage; import edu.harvard.hul.ois.jhove.RepInfo; +import java.util.zip.DataFormatException; /** Representation of the zTXt (compressed text) chunk */ public class ZtxtChunk extends GeneralTextChunk { - /** Constructor - * @param sig: int representing chunktype - * @param leng: long representing length - */ - public ZtxtChunk(int sig, long leng) { - chunkType = sig; - length = leng; - ancillary = true; - duplicateAllowed = true; - } - - @Override - public void processChunk(RepInfo info) throws Exception { - processChunkCommon(info); + /** + * Constructor + * + * @param sig: int representing chunktype + * @param leng: long representing length + */ + public ZtxtChunk(int sig, long leng) { + chunkType = sig; + length = leng; + ancillary = true; + duplicateAllowed = true; + } - // The tEXt chunk consists of a keyword, a null, a compression type, - // and a value. - // There needs to be exactly one null in the data. - StringBuilder sb = new StringBuilder(); - String keyword = null; - String value = null; - ErrorMessage msg; - int cmprsIdx = 0; - // State values: 0 = keyword, 1 = compression type, 2 = compressed data - int state = 0; - byte[] compressedData = new byte[(int) length]; - for (int i = 0; i - * For overviews, tutorials, examples, guides, and tool documentation, - * please see: + * Contains the classes needed for building a JHOVE application. This package must be used with a + * top-level class, with one or more output handlers or viewers, and with one or more modules for + * specific file formats. + * + *

For overviews, tutorials, examples, guides, and tool documentation, please see: + * *

*/ package com.mcgath.jhove.module.png; diff --git a/jhove-ext-modules/src/main/java/edu/harvard/hul/ois/jhove/module/GzipModule.java b/jhove-ext-modules/src/main/java/edu/harvard/hul/ois/jhove/module/GzipModule.java index b929a729c..380f05aed 100644 --- a/jhove-ext-modules/src/main/java/edu/harvard/hul/ois/jhove/module/GzipModule.java +++ b/jhove-ext-modules/src/main/java/edu/harvard/hul/ois/jhove/module/GzipModule.java @@ -1,21 +1,5 @@ package edu.harvard.hul.ois.jhove.module; -import java.io.EOFException; -import java.io.File; -import java.io.IOException; -import java.io.InputStream; -import java.io.RandomAccessFile; -import java.util.ArrayList; -import java.util.List; - -import org.jwat.common.ByteCountingPushBackInputStream; -import org.jwat.common.Diagnosis; -import org.jwat.common.Diagnostics; -import org.jwat.common.InputStreamNoSkip; -import org.jwat.common.RandomAccessFileInputStream; -import org.jwat.gzip.GzipEntry; -import org.jwat.gzip.GzipReader; - import edu.harvard.hul.ois.jhove.Agent; import edu.harvard.hul.ois.jhove.Agent.Builder; import edu.harvard.hul.ois.jhove.AgentType; @@ -37,239 +21,256 @@ import edu.harvard.hul.ois.jhove.SignatureUseType; import edu.harvard.hul.ois.jhove.module.gzip.GzipEntryProperties; import edu.harvard.hul.ois.jhove.module.gzip.MessageConstants; +import java.io.EOFException; +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.io.RandomAccessFile; +import java.util.ArrayList; +import java.util.List; +import org.jwat.common.ByteCountingPushBackInputStream; +import org.jwat.common.Diagnosis; +import org.jwat.common.Diagnostics; +import org.jwat.common.InputStreamNoSkip; +import org.jwat.common.RandomAccessFileInputStream; +import org.jwat.gzip.GzipEntry; +import org.jwat.gzip.GzipReader; /** - * JHOVE module for identifying, validating and characterizing GZIP files. - * Ported from the JHOVE2 GZIP module, created by lbihanic, selghissassi, nicl - * - * JHOVE2 GZip module. This module parses and validates GZip files - * in compliance with - * RFC 1952 (GZIP - * file format specification version 4.3) and supports multiple member - * GZIP files. - *

- * This is a non-recursive validation. It only validates the GZIP file format, - * not the actual content within the WARC records. - * + * JHOVE module for identifying, validating and characterizing GZIP files. Ported from the JHOVE2 + * GZIP module, created by lbihanic, selghissassi, nicl + * + *

JHOVE2 GZip module. This module parses and validates GZip files in compliance with RFC 1952 (GZIP file format specification version + * 4.3) and supports multiple member GZIP files. + * + *

This is a non-recursive validation. It only validates the GZIP file format, not the actual + * content within the WARC records. + * * @author jolf@kb.dk */ public class GzipModule extends ModuleBase { - /*------------ MODULE DEFINITIONS ---------------*/ - private static final Agent KB_AGENT = new Builder( - "Royal Library of Denmark", AgentType.STANDARD) - .address("Søren Kierkegaards Plads 1, 1219 København K, Denmark") - .fax("+45 3393 2218") - .web("http://kb.dk").build(); + /*------------ MODULE DEFINITIONS ---------------*/ + private static final Agent KB_AGENT = + new Builder("Royal Library of Denmark", AgentType.STANDARD) + .address("Søren Kierkegaards Plads 1, 1219 København K, Denmark") + .fax("+45 3393 2218") + .web("http://kb.dk") + .build(); - private static final String NAME = "GZIP-kb"; - private static final String RELEASE = "0.1"; - private static final int[] DATE = {2015, 12, 8}; - private static final String[] FORMAT = {"GZIP"}; - private static final String COVERAGE = "GZIP, https://tools.ietf.org/html/rfc1952"; - private static final String[] MIMETYPE = {"application/gzip", "application/x-gzip"}; - private static final String WELLFORMED = ""; - private static final String VALIDITY = "The file is well-formed"; - private static final String REPINFO = ""; - private static final String NOTE = ""; - private static final String RIGHTS = "Copyright 2015 by The Royal Library of Denmark. " + - "Released under the GNU Lesser General Public License."; + private static final String NAME = "GZIP-kb"; + private static final String RELEASE = "0.1"; + private static final int[] DATE = {2015, 12, 8}; + private static final String[] FORMAT = {"GZIP"}; + private static final String COVERAGE = "GZIP, https://tools.ietf.org/html/rfc1952"; + private static final String[] MIMETYPE = {"application/gzip", "application/x-gzip"}; + private static final String WELLFORMED = ""; + private static final String VALIDITY = "The file is well-formed"; + private static final String REPINFO = ""; + private static final String NOTE = ""; + private static final String RIGHTS = + "Copyright 2015 by The Royal Library of Denmark. " + + "Released under the GNU Lesser General Public License."; - private static final String EXTENSION = ".gz"; + private static final String EXTENSION = ".gz"; - /** - * List of Property elements for the entry of the GZIP-file. - * Each Property contains a map of all properties for a given entry. - */ - private List entryProperties; + /** + * List of Property elements for the entry of the GZIP-file. Each Property contains a map of all + * properties for a given entry. + */ + private List entryProperties; - /** - * Constructor. - */ - public GzipModule() { - super(NAME, RELEASE, DATE, FORMAT, COVERAGE, MIMETYPE, WELLFORMED, - VALIDITY, REPINFO, NOTE, RIGHTS, false); - setVendorAndSpecification(); - initialiseVariables(); - } - - /** - * Sets the vendor and specification for this module. - */ - private void setVendorAndSpecification() { - _vendor = KB_AGENT; + /** Constructor. */ + public GzipModule() { + super( + NAME, + RELEASE, + DATE, + FORMAT, + COVERAGE, + MIMETYPE, + WELLFORMED, + VALIDITY, + REPINFO, + NOTE, + RIGHTS, + false); + setVendorAndSpecification(); + initialiseVariables(); + } - Document doc = new Document(FORMAT[0], - DocumentType.RFC); - // Should probably have IIPC and others as authors - Agent ietfAgent = new Agent.Builder("IETF", AgentType.STANDARD).web( - "http://www.ietf.org").build(); - doc.setPublisher(ietfAgent); - doc.setDate("1996"); - doc.setIdentifier(new Identifier("https://www.ietf.org/rfc/rfc1952.txt", - IdentifierType.RFC)); - _specification.add(doc); + /** Sets the vendor and specification for this module. */ + private void setVendorAndSpecification() { + _vendor = KB_AGENT; - // Add optional external signature (.gz) - Signature sig = new ExternalSignature (EXTENSION, SignatureType.EXTENSION, - SignatureUseType.OPTIONAL); - _signature.add (sig); - - } + Document doc = new Document(FORMAT[0], DocumentType.RFC); + // Should probably have IIPC and others as authors + Agent ietfAgent = + new Agent.Builder("IETF", AgentType.STANDARD).web("http://www.ietf.org").build(); + doc.setPublisher(ietfAgent); + doc.setDate("1996"); + doc.setIdentifier(new Identifier("https://www.ietf.org/rfc/rfc1952.txt", IdentifierType.RFC)); + _specification.add(doc); - /** - * Initializes the variables. - */ - private void initialiseVariables() { - entryProperties = new ArrayList<>(); - } + // Add optional external signature (.gz) + Signature sig = + new ExternalSignature(EXTENSION, SignatureType.EXTENSION, SignatureUseType.OPTIONAL); + _signature.add(sig); + } - /** - * Resets parameter settings. - * Returns to a default state without any parameters. - */ - @Override - public void resetParams() { - initialiseVariables(); - } - - @Override - public void checkSignatures (File file, - InputStream stream, - RepInfo info) - throws IOException { - info.setFormat (_format[0]); - info.setMimeType (_mimeType[0]); - info.setModule (this); - - boolean checkIsGzip = GzipReader.isGzipped(new ByteCountingPushBackInputStream(stream, GzipReader.DEFAULT_INPUT_BUFFER_SIZE)); - if (checkIsGzip) { - info.setSigMatch(_name); - } else { - info.setWellFormed (false); - } - - } - - @Override - public void checkSignatures (File file, - RandomAccessFile raf, - RepInfo info) throws IOException { - try (InputStream stream = new RandomAccessFileInputStream(raf)) { - checkSignatures(file, stream, info); - } + /** Initializes the variables. */ + private void initialiseVariables() { + entryProperties = new ArrayList<>(); + } + + /** Resets parameter settings. Returns to a default state without any parameters. */ + @Override + public void resetParams() { + initialiseVariables(); + } + + @Override + public void checkSignatures(File file, InputStream stream, RepInfo info) throws IOException { + info.setFormat(_format[0]); + info.setMimeType(_mimeType[0]); + info.setModule(this); + + boolean checkIsGzip = + GzipReader.isGzipped( + new ByteCountingPushBackInputStream(stream, GzipReader.DEFAULT_INPUT_BUFFER_SIZE)); + if (checkIsGzip) { + info.setSigMatch(_name); + } else { + info.setWellFormed(false); } - - @Override - public void parse(RandomAccessFile file, RepInfo info) { - InputStream stream = new RandomAccessFileInputStream(file); - parse(stream, info, 0); + } + + @Override + public void checkSignatures(File file, RandomAccessFile raf, RepInfo info) throws IOException { + try (InputStream stream = new RandomAccessFileInputStream(raf)) { + checkSignatures(file, stream, info); } + } + + @Override + public void parse(RandomAccessFile file, RepInfo info) { + InputStream stream = new RandomAccessFileInputStream(file); + parse(stream, info, 0); + } + + @Override + public int parse(InputStream stream, RepInfo info, int parseIndex) { - @Override - public int parse(InputStream stream, RepInfo info, int parseIndex) { - - try (GzipReader reader = new GzipReader(new InputStreamNoSkip(stream), 8192)) { - info.setFormat(_format[0]); - info.setVersion("4.3"); // Is it really version 4.3? - info.setMimeType(_mimeType[0]); - info.setModule(this); + try (GzipReader reader = new GzipReader(new InputStreamNoSkip(stream), 8192)) { + info.setFormat(_format[0]); + info.setVersion("4.3"); // Is it really version 4.3? + info.setMimeType(_mimeType[0]); + info.setModule(this); - parseRecords(reader); + parseRecords(reader); - info.setValid(reader.isCompliant()); - info.setWellFormed(reader.isCompliant()); + info.setValid(reader.isCompliant()); + info.setWellFormed(reader.isCompliant()); - reportResults(reader, info); + reportResults(reader, info); - if (reader.isCompliant()) { - info.setSigMatch(_name); - } - } catch (Exception e) { - info.setMessage(new ErrorMessage(e.getMessage())); - info.setValid(false); - info.setWellFormed(false); - } - return 0; + if (reader.isCompliant()) { + info.setSigMatch(_name); + } + } catch (Exception e) { + info.setMessage(new ErrorMessage(e.getMessage())); + info.setValid(false); + info.setWellFormed(false); } - - /** - * Parses GZIP entries. Parsing should be straight forward with all records accessible through the same source. - * @param reader GZIP reader used to parse records - * @throws EOFException if EOF occurs prematurely - * @throws IOException if an IO error occurs while processing - * @throws JhoveException if a serious problem needs to be reported - */ - protected void parseRecords(GzipReader reader) throws EOFException, IOException, JhoveException { - if (reader != null) { - GzipEntry entry; - while ((entry = reader.getNextEntry()) != null) { - processEntry(entry); - reader.diagnostics.addAll(entry.diagnostics); - } - } else { - throw new JhoveException(MessageConstants.ERR_RECORD_NULL); - } + return 0; + } + + /** + * Parses GZIP entries. Parsing should be straight forward with all records accessible through the + * same source. + * + * @param reader GZIP reader used to parse records + * @throws EOFException if EOF occurs prematurely + * @throws IOException if an IO error occurs while processing + * @throws JhoveException if a serious problem needs to be reported + */ + protected void parseRecords(GzipReader reader) throws EOFException, IOException, JhoveException { + if (reader != null) { + GzipEntry entry; + while ((entry = reader.getNextEntry()) != null) { + processEntry(entry); + reader.diagnostics.addAll(entry.diagnostics); + } + } else { + throw new JhoveException(MessageConstants.ERR_RECORD_NULL); } + } - /** - * Processes a GZIP entry. - * Extracts all the properties of the entry into a map, and puts this map on the list. - * @param entry GZIP entry from GZIP reader - * @throws EOFException if EOF occurs prematurely - * @throws IOException if an IO error occurs while processing - */ - protected void processEntry(GzipEntry entry) throws EOFException, IOException { - GzipEntryProperties properties = new GzipEntryProperties(entry); - Property p = new Property("Record", PropertyType.STRING, PropertyArity.MAP, properties.getProperties()); - - entryProperties.add(p); + /** + * Processes a GZIP entry. Extracts all the properties of the entry into a map, and puts this map + * on the list. + * + * @param entry GZIP entry from GZIP reader + * @throws EOFException if EOF occurs prematurely + * @throws IOException if an IO error occurs while processing + */ + protected void processEntry(GzipEntry entry) throws EOFException, IOException { + GzipEntryProperties properties = new GzipEntryProperties(entry); + Property p = + new Property("Record", PropertyType.STRING, PropertyArity.MAP, properties.getProperties()); - entry.close(); - } - - /** - * Reports the results of the characterization. - * @param reader The GZIP reader, which has read the GZIP-file. - * @param repInfo The representation info, where to report the results. - */ - private void reportResults(GzipReader reader, RepInfo repInfo) { - Diagnostics diagnostics = reader.diagnostics; - if (diagnostics.hasErrors()) { - for (Diagnosis d : diagnostics.getErrors()) { - repInfo.setMessage(new ErrorMessage(extractDiagnosisType(d), extractDiagnosisMessage(d))); - } - repInfo.setConsistent(false); - } - if (diagnostics.hasWarnings()) { - // Report warnings on source object. - for (Diagnosis d : diagnostics.getWarnings()) { - repInfo.setMessage(new InfoMessage(extractDiagnosisType(d), extractDiagnosisMessage(d))); - } - } - repInfo.setProperty(new Property("Records", PropertyType.PROPERTY, PropertyArity.LIST, entryProperties)); - repInfo.setSize(reader.getConsumed()); + entryProperties.add(p); + + entry.close(); + } + + /** + * Reports the results of the characterization. + * + * @param reader The GZIP reader, which has read the GZIP-file. + * @param repInfo The representation info, where to report the results. + */ + private void reportResults(GzipReader reader, RepInfo repInfo) { + Diagnostics diagnostics = reader.diagnostics; + if (diagnostics.hasErrors()) { + for (Diagnosis d : diagnostics.getErrors()) { + repInfo.setMessage(new ErrorMessage(extractDiagnosisType(d), extractDiagnosisMessage(d))); + } + repInfo.setConsistent(false); } - - /** - * Extracts the diagnosis type. - * @param d The diagnosis whose type should be extracted - * @return The type of diagnosis - */ - private static String extractDiagnosisType(Diagnosis d) { - return d.type.name(); + if (diagnostics.hasWarnings()) { + // Report warnings on source object. + for (Diagnosis d : diagnostics.getWarnings()) { + repInfo.setMessage(new InfoMessage(extractDiagnosisType(d), extractDiagnosisMessage(d))); + } } + repInfo.setProperty( + new Property("Records", PropertyType.PROPERTY, PropertyArity.LIST, entryProperties)); + repInfo.setSize(reader.getConsumed()); + } + + /** + * Extracts the diagnosis type. + * + * @param d The diagnosis whose type should be extracted + * @return The type of diagnosis + */ + private static String extractDiagnosisType(Diagnosis d) { + return d.type.name(); + } - /** - * Extracts the message from the diagnosis. - * @param d The diagnosis - * @return The message containing entity and information. - */ - private static String extractDiagnosisMessage(Diagnosis d) { - StringBuilder res = new StringBuilder(); - res.append("Entity: ").append(d.entity); - for(String i : d.information) { - res.append(", ").append(i); - } - return res.toString(); + /** + * Extracts the message from the diagnosis. + * + * @param d The diagnosis + * @return The message containing entity and information. + */ + private static String extractDiagnosisMessage(Diagnosis d) { + StringBuilder res = new StringBuilder(); + res.append("Entity: ").append(d.entity); + for (String i : d.information) { + res.append(", ").append(i); } + return res.toString(); + } } diff --git a/jhove-ext-modules/src/main/java/edu/harvard/hul/ois/jhove/module/WarcModule.java b/jhove-ext-modules/src/main/java/edu/harvard/hul/ois/jhove/module/WarcModule.java index 456609311..a424c117f 100644 --- a/jhove-ext-modules/src/main/java/edu/harvard/hul/ois/jhove/module/WarcModule.java +++ b/jhove-ext-modules/src/main/java/edu/harvard/hul/ois/jhove/module/WarcModule.java @@ -1,26 +1,5 @@ package edu.harvard.hul.ois.jhove.module; -import java.io.File; -import java.io.IOException; -import java.io.InputStream; -import java.io.RandomAccessFile; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Map.Entry; - -import org.jwat.common.ByteCountingPushBackInputStream; -import org.jwat.common.Diagnosis; -import org.jwat.common.Diagnostics; -import org.jwat.common.InputStreamNoSkip; -import org.jwat.common.RandomAccessFileInputStream; -import org.jwat.common.UriProfile; -import org.jwat.gzip.GzipReader; -import org.jwat.warc.WarcReader; -import org.jwat.warc.WarcReaderFactory; -import org.jwat.warc.WarcRecord; - import edu.harvard.hul.ois.jhove.Agent; import edu.harvard.hul.ois.jhove.Agent.Builder; import edu.harvard.hul.ois.jhove.AgentType; @@ -42,334 +21,358 @@ import edu.harvard.hul.ois.jhove.SignatureUseType; import edu.harvard.hul.ois.jhove.module.warc.MessageConstants; import edu.harvard.hul.ois.jhove.module.warc.WarcRecordProperties; +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.io.RandomAccessFile; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import org.jwat.common.ByteCountingPushBackInputStream; +import org.jwat.common.Diagnosis; +import org.jwat.common.Diagnostics; +import org.jwat.common.InputStreamNoSkip; +import org.jwat.common.RandomAccessFileInputStream; +import org.jwat.common.UriProfile; +import org.jwat.gzip.GzipReader; +import org.jwat.warc.WarcReader; +import org.jwat.warc.WarcReaderFactory; +import org.jwat.warc.WarcRecord; /** - * JHOVE module for identifying, validating and characterizing WARC files. - * Ported from the JHOVE2 WARC module and based on the JWAT-tool, both - * created by nicl@kb.dk (nclarkekb@git). + * JHOVE module for identifying, validating and characterizing WARC files. Ported from the JHOVE2 + * WARC module and based on the JWAT-tool, both created by nicl@kb.dk (nclarkekb@git). * - * This is a non-recursive validation. It only validates the WARC file format - * and WARC headers, not the actual payload of the WARC records. + *

This is a non-recursive validation. It only validates the WARC file format and WARC headers, + * not the actual payload of the WARC records. * * @author jolf@kb.dk */ public class WarcModule extends ModuleBase { - /*------------ MODULE DEFINITIONS ---------------*/ - private static final Agent KB_AGENT = new Builder( - "Royal Library of Denmark", AgentType.STANDARD) - .address("Søren Kierkegaards Plads 1, 1219 København K, Denmark") - .fax("+45 3393 2218") - .web("http://kb.dk").build(); - - private static final String NAME = "WARC-kb"; - private static final String RELEASE = "1.0"; - private static final int[] DATE = {2015, 12, 07}; - private static final String[] FORMAT = { - "WARC", "WARC, Web ARChive file format" - }; - private static final String COVERAGE = "WARC, 28500:2009"; - private static final String[] MIMETYPE = {"application/warc", "application/warc-fields"}; - private static final String WELLFORMED = ""; - private static final String VALIDITY = "The file is well-formed"; - private static final String REPINFO = ""; - private static final String NOTE = ""; - private static final String RIGHTS = "Copyright 2015 by The Royal Library of Denmark. " + - "Released under the GNU Lesser General Public License."; - - /* DEFAULT VALUES */ - private static final Boolean DEFAULT_COMPUTE_BLOCK_DIGEST = Boolean.TRUE; - private static final String DEFAULT_BLOCK_DIGEST_ALGORITHM = "sha1"; - private static final String DEFAULT_BLOCK_DIGEST_ENCODING = "base32"; - private static final Boolean DEFAULT_COMPUTE_PAYLOAD_DIGEST = Boolean.TRUE; - private static final String DEFAULT_PAYLOAD_DIGEST_ALGORITHM = "sha1"; - private static final String DEFAULT_PAYLOAD_DIGEST_ENCODING = "base32"; - private static final Boolean DEFAULT_STRICT_TARGET_URI_VALIDATION = Boolean.FALSE; - private static final Boolean DEFAULT_STRICT_URI_VALIDATION = Boolean.FALSE; - - /*-------------- Local variables --------------*/ - - private boolean bComputeBlockDigest; - private String blockDigestAlgorithm; - private String blockDigestEncoding; - - private boolean bComputePayloadDigest; - private String payloadDigestAlgorithm; - private String payloadDigestEncoding; - - private boolean bStrictTargetUriValidation; - private boolean bStrictUriValidation; - - /** - * Map of the WARC record versions and their count. - * Used for reporting the most seen version, as the version for the WARC file. - */ - private Map versions; - /** - * List of Property elements for the records of the WARC-file. - * Each Property contains a map of all properties for a given record. - */ - private List recordProperties; - - /** - * Constructor. - */ - public WarcModule() { - super(NAME, RELEASE, DATE, FORMAT, COVERAGE, MIMETYPE, WELLFORMED, - VALIDITY, REPINFO, NOTE, RIGHTS, false); - setVendorAndSpecification(); - initialiseVariables(); + /*------------ MODULE DEFINITIONS ---------------*/ + private static final Agent KB_AGENT = + new Builder("Royal Library of Denmark", AgentType.STANDARD) + .address("Søren Kierkegaards Plads 1, 1219 København K, Denmark") + .fax("+45 3393 2218") + .web("http://kb.dk") + .build(); + + private static final String NAME = "WARC-kb"; + private static final String RELEASE = "1.0"; + private static final int[] DATE = {2015, 12, 07}; + private static final String[] FORMAT = {"WARC", "WARC, Web ARChive file format"}; + private static final String COVERAGE = "WARC, 28500:2009"; + private static final String[] MIMETYPE = {"application/warc", "application/warc-fields"}; + private static final String WELLFORMED = ""; + private static final String VALIDITY = "The file is well-formed"; + private static final String REPINFO = ""; + private static final String NOTE = ""; + private static final String RIGHTS = + "Copyright 2015 by The Royal Library of Denmark. " + + "Released under the GNU Lesser General Public License."; + + /* DEFAULT VALUES */ + private static final Boolean DEFAULT_COMPUTE_BLOCK_DIGEST = Boolean.TRUE; + private static final String DEFAULT_BLOCK_DIGEST_ALGORITHM = "sha1"; + private static final String DEFAULT_BLOCK_DIGEST_ENCODING = "base32"; + private static final Boolean DEFAULT_COMPUTE_PAYLOAD_DIGEST = Boolean.TRUE; + private static final String DEFAULT_PAYLOAD_DIGEST_ALGORITHM = "sha1"; + private static final String DEFAULT_PAYLOAD_DIGEST_ENCODING = "base32"; + private static final Boolean DEFAULT_STRICT_TARGET_URI_VALIDATION = Boolean.FALSE; + private static final Boolean DEFAULT_STRICT_URI_VALIDATION = Boolean.FALSE; + + /*-------------- Local variables --------------*/ + + private boolean bComputeBlockDigest; + private String blockDigestAlgorithm; + private String blockDigestEncoding; + + private boolean bComputePayloadDigest; + private String payloadDigestAlgorithm; + private String payloadDigestEncoding; + + private boolean bStrictTargetUriValidation; + private boolean bStrictUriValidation; + + /** + * Map of the WARC record versions and their count. Used for reporting the most seen version, as + * the version for the WARC file. + */ + private Map versions; + /** + * List of Property elements for the records of the WARC-file. Each Property contains a map of all + * properties for a given record. + */ + private List recordProperties; + + /** Constructor. */ + public WarcModule() { + super( + NAME, + RELEASE, + DATE, + FORMAT, + COVERAGE, + MIMETYPE, + WELLFORMED, + VALIDITY, + REPINFO, + NOTE, + RIGHTS, + false); + setVendorAndSpecification(); + initialiseVariables(); + } + + /** Sets the vendor and specification for this module. */ + private void setVendorAndSpecification() { + _vendor = KB_AGENT; + + Document doc = new Document("WARC (Web ARChive) file format", DocumentType.WEB); + // Should probably have IIPC and others as authors + doc.setPublisher(Agent.newIsoInstance()); + doc.setDate("2009"); + doc.setIdentifier(new Identifier("28500:2009", IdentifierType.ISO)); + _specification.add(doc); + + // Add optional external signatures (.warc or .warc.gz) + Signature sig = + new ExternalSignature(".warc", SignatureType.EXTENSION, SignatureUseType.OPTIONAL); + _signature.add(sig); + sig = + new ExternalSignature( + ".warc.gz", SignatureType.EXTENSION, SignatureUseType.OPTIONAL, "when compressed"); + _signature.add(sig); + } + + /** Initializes the variables. */ + private void initialiseVariables() { + versions = new HashMap<>(); + recordProperties = new ArrayList<>(); + + bComputeBlockDigest = DEFAULT_COMPUTE_BLOCK_DIGEST; + blockDigestAlgorithm = DEFAULT_BLOCK_DIGEST_ALGORITHM; + blockDigestEncoding = DEFAULT_BLOCK_DIGEST_ENCODING; + + bComputePayloadDigest = DEFAULT_COMPUTE_PAYLOAD_DIGEST; + payloadDigestAlgorithm = DEFAULT_PAYLOAD_DIGEST_ALGORITHM; + payloadDigestEncoding = DEFAULT_PAYLOAD_DIGEST_ENCODING; + + bStrictTargetUriValidation = DEFAULT_STRICT_TARGET_URI_VALIDATION; + bStrictUriValidation = DEFAULT_STRICT_URI_VALIDATION; + } + + /** Reset parameter settings. Returns to a default state without any parameters. */ + @Override + public void resetParams() { + initialiseVariables(); + } + + @Override + public void checkSignatures(File file, InputStream stream, RepInfo info) throws IOException { + info.setFormat(_format[0]); + info.setMimeType(_mimeType[0]); + info.setModule(this); + + ByteCountingPushBackInputStream pbin = + new ByteCountingPushBackInputStream(stream, GzipReader.DEFAULT_INPUT_BUFFER_SIZE); + // First try warc uncompressed + boolean checkIsWarc = WarcReaderFactory.isWarcFile(pbin); + if (checkIsWarc) { + info.setSigMatch(_name); + return; } - - /** - * Sets the vendor and specification for this module. - */ - private void setVendorAndSpecification() { - _vendor = KB_AGENT; - - Document doc = new Document("WARC (Web ARChive) file format", - DocumentType.WEB); - // Should probably have IIPC and others as authors - doc.setPublisher(Agent.newIsoInstance()); - doc.setDate("2009"); - doc.setIdentifier(new Identifier("28500:2009", - IdentifierType.ISO)); - _specification.add(doc); - - // Add optional external signatures (.warc or .warc.gz) - Signature sig = new ExternalSignature (".warc", SignatureType.EXTENSION, - SignatureUseType.OPTIONAL); - _signature.add (sig); - sig = new ExternalSignature (".warc.gz", SignatureType.EXTENSION, - SignatureUseType.OPTIONAL, "when compressed"); - _signature.add (sig); + // Then try warc compressed + boolean checkIsGzip = GzipReader.isGzipped(pbin); + if (checkIsGzip) { + info.setSigMatch(_name); + return; } - - /** - * Initializes the variables. - */ - private void initialiseVariables() { - versions = new HashMap<>(); - recordProperties = new ArrayList<>(); - - bComputeBlockDigest = DEFAULT_COMPUTE_BLOCK_DIGEST; - blockDigestAlgorithm = DEFAULT_BLOCK_DIGEST_ALGORITHM; - blockDigestEncoding = DEFAULT_BLOCK_DIGEST_ENCODING; - - bComputePayloadDigest = DEFAULT_COMPUTE_PAYLOAD_DIGEST; - payloadDigestAlgorithm = DEFAULT_PAYLOAD_DIGEST_ALGORITHM; - payloadDigestEncoding = DEFAULT_PAYLOAD_DIGEST_ENCODING; - - bStrictTargetUriValidation = DEFAULT_STRICT_TARGET_URI_VALIDATION; - bStrictUriValidation = DEFAULT_STRICT_URI_VALIDATION; + // Not a warc or a gzip + info.setWellFormed(false); + } + + @Override + public void checkSignatures(File file, RandomAccessFile raf, RepInfo info) throws IOException { + try (InputStream stream = new RandomAccessFileInputStream(raf)) { + checkSignatures(file, stream, info); } + } - /** Reset parameter settings. - * Returns to a default state without any parameters. - */ - @Override - public void resetParams() { - initialiseVariables(); + @Override + public void parse(RandomAccessFile file, RepInfo info) throws IOException { + try (InputStream stream = new RandomAccessFileInputStream(file)) { + parse(stream, info, 0); } - - @Override - public void checkSignatures (File file, - InputStream stream, - RepInfo info) - throws IOException { - info.setFormat (_format[0]); - info.setMimeType (_mimeType[0]); - info.setModule (this); - - ByteCountingPushBackInputStream pbin = new ByteCountingPushBackInputStream(stream, GzipReader.DEFAULT_INPUT_BUFFER_SIZE); - // First try warc uncompressed - boolean checkIsWarc = WarcReaderFactory.isWarcFile(pbin); - if (checkIsWarc) { - info.setSigMatch(_name); - return; - } - // Then try warc compressed - boolean checkIsGzip = GzipReader.isGzipped(pbin); - if (checkIsGzip) { - info.setSigMatch(_name); - return; - } - // Not a warc or a gzip - info.setWellFormed (false); + } + + @Override + public int parse(InputStream stream, RepInfo info, int parseIndex) throws IOException { + WarcReader reader = WarcReaderFactory.getReader(new InputStreamNoSkip(stream), 8192); + try { + info.setFormat(_format[0]); + info.setMimeType(_mimeType[0]); + info.setModule(this); + + setReaderOptions(reader); + parseRecords(reader); + + info.setValid(reader.isCompliant()); + info.setWellFormed(reader.isCompliant()); + + reportResults(reader, info); + + if (reader.isCompliant()) { + info.setSigMatch(_name); + } + } catch (JhoveException e) { + info.setMessage(new ErrorMessage(e.getMessage())); + info.setValid(false); + info.setWellFormed(false); + } finally { + if (reader != null) { + reader.close(); + reader = null; + } } - - @Override - public void checkSignatures (File file, - RandomAccessFile raf, - RepInfo info) throws IOException { - try (InputStream stream = new RandomAccessFileInputStream(raf)) { - checkSignatures(file, stream, info); - } + return 0; + } + + /** + * Set digest options for WARC reader. + * + * @param reader WARC reader instance + */ + protected void setReaderOptions(WarcReader reader) throws JhoveException { + reader.setBlockDigestEnabled(bComputeBlockDigest); + reader.setPayloadDigestEnabled(bComputePayloadDigest); + if (!reader.setBlockDigestAlgorithm(blockDigestAlgorithm)) { + throw new JhoveException(MessageConstants.ERR_BLOCK_DIGEST_INVALID + blockDigestAlgorithm); } - - - @Override - public void parse(RandomAccessFile file, RepInfo info) throws IOException { - try (InputStream stream = new RandomAccessFileInputStream(file)) { - parse(stream, info, 0); - } + if (!reader.setPayloadDigestAlgorithm(payloadDigestAlgorithm)) { + throw new JhoveException( + MessageConstants.ERR_PAYLOAD_DIGEST_INVALID + payloadDigestAlgorithm); } - - @Override - public int parse(InputStream stream, RepInfo info, int parseIndex) throws IOException { - WarcReader reader = WarcReaderFactory.getReader(new InputStreamNoSkip(stream), 8192); - try { - info.setFormat(_format[0]); - info.setMimeType(_mimeType[0]); - info.setModule(this); - - setReaderOptions(reader); - parseRecords(reader); - - info.setValid(reader.isCompliant()); - info.setWellFormed(reader.isCompliant()); - - reportResults(reader, info); - - if (reader.isCompliant()) { - info.setSigMatch(_name); - } - } catch (JhoveException e) { - info.setMessage(new ErrorMessage(e.getMessage())); - info.setValid(false); - info.setWellFormed(false); - } finally { - if(reader != null) { - reader.close(); - reader = null; - } - } - return 0; + reader.setBlockDigestEncoding(blockDigestEncoding); + reader.setPayloadDigestEncoding(payloadDigestEncoding); + if (bStrictTargetUriValidation) { + reader.setWarcTargetUriProfile(UriProfile.RFC3986); + } else { + reader.setWarcTargetUriProfile(UriProfile.RFC3986_ABS_16BIT_LAX); } - - /** - * Set digest options for WARC reader. - * @param reader WARC reader instance - */ - protected void setReaderOptions(WarcReader reader) throws JhoveException { - reader.setBlockDigestEnabled(bComputeBlockDigest); - reader.setPayloadDigestEnabled(bComputePayloadDigest); - if (!reader.setBlockDigestAlgorithm(blockDigestAlgorithm)) { - throw new JhoveException(MessageConstants.ERR_BLOCK_DIGEST_INVALID + blockDigestAlgorithm); - } - if (!reader.setPayloadDigestAlgorithm(payloadDigestAlgorithm)) { - throw new JhoveException(MessageConstants.ERR_PAYLOAD_DIGEST_INVALID + payloadDigestAlgorithm); - } - reader.setBlockDigestEncoding(blockDigestEncoding); - reader.setPayloadDigestEncoding(payloadDigestEncoding); - if (bStrictTargetUriValidation) { - reader.setWarcTargetUriProfile(UriProfile.RFC3986); - } else { - reader.setWarcTargetUriProfile(UriProfile.RFC3986_ABS_16BIT_LAX); - } - if (bStrictUriValidation) { - reader.setUriProfile(UriProfile.RFC3986); - } else { - reader.setUriProfile(UriProfile.RFC3986_ABS_16BIT_LAX); - } + if (bStrictUriValidation) { + reader.setUriProfile(UriProfile.RFC3986); + } else { + reader.setUriProfile(UriProfile.RFC3986_ABS_16BIT_LAX); } - - /** - * Parse WARC records. Parsing should be straight forward with all records accessible through the same source. - * @param reader WARC reader used to parse records - * @throws IOException if an IO error occurs while processing - * @throws JhoveException if a serious problem needs to be reported - */ - protected void parseRecords(WarcReader reader) throws IOException, JhoveException { - if (reader != null) { - WarcRecord record; - while ((record = reader.getNextRecord()) != null) { - processRecord(record); - reader.diagnostics.addAll(record.diagnostics); - } - } else { - throw new JhoveException(MessageConstants.ERR_RECORD_NULL); - } + } + + /** + * Parse WARC records. Parsing should be straight forward with all records accessible through the + * same source. + * + * @param reader WARC reader used to parse records + * @throws IOException if an IO error occurs while processing + * @throws JhoveException if a serious problem needs to be reported + */ + protected void parseRecords(WarcReader reader) throws IOException, JhoveException { + if (reader != null) { + WarcRecord record; + while ((record = reader.getNextRecord()) != null) { + processRecord(record); + reader.diagnostics.addAll(record.diagnostics); + } + } else { + throw new JhoveException(MessageConstants.ERR_RECORD_NULL); } - - /** - * Process a WARC record. - * Does not characterize the record payload. - * @param record WARC record from WARC reader - * @throws IOException if an IO error occurs while processing - */ - protected void processRecord(WarcRecord record) throws IOException { - if (record.header.bValidVersionFormat) { - Integer count = versions.get(record.header.versionStr); - if (count == null) { - count = 0; - } - ++count; - versions.put(record.header.versionStr, count); - } - - WarcRecordProperties properties = new WarcRecordProperties(record); - Property p = new Property("Record", PropertyType.STRING, PropertyArity.MAP, properties.getProperties()); - - recordProperties.add(p); - - record.close(); + } + + /** + * Process a WARC record. Does not characterize the record payload. + * + * @param record WARC record from WARC reader + * @throws IOException if an IO error occurs while processing + */ + protected void processRecord(WarcRecord record) throws IOException { + if (record.header.bValidVersionFormat) { + Integer count = versions.get(record.header.versionStr); + if (count == null) { + count = 0; + } + ++count; + versions.put(record.header.versionStr, count); } - /** - * Report the results of the characterization. - * @param reader The WARC reader, which has read the WARC-file. - * @param repInfo The representation info, where to report the results. - */ - private void reportResults(WarcReader reader, RepInfo repInfo) { - Diagnostics diagnostics = reader.diagnostics; - if (diagnostics.hasErrors()) { - for (Diagnosis d : diagnostics.getErrors()) { - repInfo.setMessage(new ErrorMessage(extractDiagnosisType(d), extractDiagnosisMessage(d))); - } - repInfo.setConsistent(false); - } - if (diagnostics.hasWarnings()) { - // Report warnings on source object. - for (Diagnosis d : diagnostics.getWarnings()) { - repInfo.setMessage(new InfoMessage(extractDiagnosisType(d), extractDiagnosisMessage(d))); - } - } - - int maxCount = -1; - for(Entry e : versions.entrySet()) { - if(e.getValue() > maxCount) { - maxCount = e.getValue(); - repInfo.setVersion(e.getKey()); - } - - _features.add(e.getValue() + " WARC records of version " + e.getKey()); - } - - repInfo.setProperty(new Property("Records", PropertyType.PROPERTY, PropertyArity.LIST, recordProperties)); - repInfo.setSize(reader.getConsumed()); + WarcRecordProperties properties = new WarcRecordProperties(record); + Property p = + new Property("Record", PropertyType.STRING, PropertyArity.MAP, properties.getProperties()); + + recordProperties.add(p); + + record.close(); + } + + /** + * Report the results of the characterization. + * + * @param reader The WARC reader, which has read the WARC-file. + * @param repInfo The representation info, where to report the results. + */ + private void reportResults(WarcReader reader, RepInfo repInfo) { + Diagnostics diagnostics = reader.diagnostics; + if (diagnostics.hasErrors()) { + for (Diagnosis d : diagnostics.getErrors()) { + repInfo.setMessage(new ErrorMessage(extractDiagnosisType(d), extractDiagnosisMessage(d))); + } + repInfo.setConsistent(false); + } + if (diagnostics.hasWarnings()) { + // Report warnings on source object. + for (Diagnosis d : diagnostics.getWarnings()) { + repInfo.setMessage(new InfoMessage(extractDiagnosisType(d), extractDiagnosisMessage(d))); + } } - /** - * Extracts the diagnosis type. - * @param d The diagnosis whose type should be extracted - * @return The type of diagnosis - */ - private static String extractDiagnosisType(Diagnosis d) { - return d.type.name(); + int maxCount = -1; + for (Entry e : versions.entrySet()) { + if (e.getValue() > maxCount) { + maxCount = e.getValue(); + repInfo.setVersion(e.getKey()); + } + + _features.add(e.getValue() + " WARC records of version " + e.getKey()); } - /** - * Extracts the message from the diagnosis. - * @param d The diagnosis - * @return The message containing entity and informations. - */ - private static String extractDiagnosisMessage(Diagnosis d) { - StringBuilder res = new StringBuilder(); - res.append("Entity: ").append(d.entity); - for(String i : d.information) { - res.append(", ").append(i); - } - return res.toString(); + repInfo.setProperty( + new Property("Records", PropertyType.PROPERTY, PropertyArity.LIST, recordProperties)); + repInfo.setSize(reader.getConsumed()); + } + + /** + * Extracts the diagnosis type. + * + * @param d The diagnosis whose type should be extracted + * @return The type of diagnosis + */ + private static String extractDiagnosisType(Diagnosis d) { + return d.type.name(); + } + + /** + * Extracts the message from the diagnosis. + * + * @param d The diagnosis + * @return The message containing entity and informations. + */ + private static String extractDiagnosisMessage(Diagnosis d) { + StringBuilder res = new StringBuilder(); + res.append("Entity: ").append(d.entity); + for (String i : d.information) { + res.append(", ").append(i); } + return res.toString(); + } } diff --git a/jhove-ext-modules/src/main/java/edu/harvard/hul/ois/jhove/module/gzip/CompressionMethod.java b/jhove-ext-modules/src/main/java/edu/harvard/hul/ois/jhove/module/gzip/CompressionMethod.java index 78ef72574..ec5cf2a14 100644 --- a/jhove-ext-modules/src/main/java/edu/harvard/hul/ois/jhove/module/gzip/CompressionMethod.java +++ b/jhove-ext-modules/src/main/java/edu/harvard/hul/ois/jhove/module/gzip/CompressionMethod.java @@ -1,39 +1,32 @@ /** * JHOVE2 - Next-generation architecture for format-aware characterization * - * Copyright (c) 2009 by The Regents of the University of California, - * Ithaka Harbors, Inc., and The Board of Trustees of the Leland Stanford - * Junior University. - * All rights reserved. + *

Copyright (c) 2009 by The Regents of the University of California, Ithaka Harbors, Inc., and + * The Board of Trustees of the Leland Stanford Junior University. All rights reserved. * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: + *

Redistribution and use in source and binary forms, with or without modification, are permitted + * provided that the following conditions are met: * - * o Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. + *

o Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. * - * o Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. + *

o Redistributions in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other materials provided with + * the distribution. * - * o Neither the name of the University of California/California Digital - * Library, Ithaka Harbors/Portico, or Stanford University, nor the names of - * its contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. + *

o Neither the name of the University of California/California Digital Library, Ithaka + * Harbors/Portico, or Stanford University, nor the names of its contributors may be used to endorse + * or promote products derived from this software without specific prior written permission. * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. + *

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY + * WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ - package edu.harvard.hul.ois.jhove.module.gzip; import java.util.HashMap; @@ -41,56 +34,54 @@ /** * Enumerated type for GZip supported compression methods. - * - * Converted from JHOVE 2. + * + *

Converted from JHOVE 2. */ public class CompressionMethod { - /** The list of valid values. */ - private static Map values = new HashMap<>(); + /** The list of valid values. */ + private static Map values = new HashMap<>(); - /** The deflate compression method.*/ - public static final CompressionMethod DEFLATE = new CompressionMethod(8, "Deflate", true); - - /** - * Initializes the valid values. - */ - static { - values.put(Integer.valueOf(DEFLATE.value), DEFLATE); - } - - /** The integer value for the enum instance. */ - public final int value; - /** The value description. */ - public final String label; - /** Whether the value is valid. */ - public final boolean valid; + /** The deflate compression method. */ + public static final CompressionMethod DEFLATE = new CompressionMethod(8, "Deflate", true); - /** - * Constructor. - * @param value The compression method value. - * @param label The name of the compression method. - * @param valid If it is a valid compression method. - */ - protected CompressionMethod(int value, String label, boolean valid) { - this.value = value; - this.label = label; - this.valid = valid; - } - - /** - * Returns the enumerated value object corresponding to the - * specified integer value. If the integer value is unknown, an - * instance marked as not valid is returned. - * @param n the integer value to map. - * - * @return a compression method object, valid if n is - * one of the defined valid values. - */ - public static CompressionMethod fromValue(int n) { - CompressionMethod v = values.get(Integer.valueOf(n)); - if (v == null) { - v = new CompressionMethod(n, null, false); - } - return v; + /** Initializes the valid values. */ + static { + values.put(Integer.valueOf(DEFLATE.value), DEFLATE); + } + + /** The integer value for the enum instance. */ + public final int value; + /** The value description. */ + public final String label; + /** Whether the value is valid. */ + public final boolean valid; + + /** + * Constructor. + * + * @param value The compression method value. + * @param label The name of the compression method. + * @param valid If it is a valid compression method. + */ + protected CompressionMethod(int value, String label, boolean valid) { + this.value = value; + this.label = label; + this.valid = valid; + } + + /** + * Returns the enumerated value object corresponding to the specified integer value. If the + * integer value is unknown, an instance marked as not valid is returned. + * + * @param n the integer value to map. + * @return a compression method object, valid if n is one of the defined valid + * values. + */ + public static CompressionMethod fromValue(int n) { + CompressionMethod v = values.get(Integer.valueOf(n)); + if (v == null) { + v = new CompressionMethod(n, null, false); } + return v; + } } diff --git a/jhove-ext-modules/src/main/java/edu/harvard/hul/ois/jhove/module/gzip/CompressionType.java b/jhove-ext-modules/src/main/java/edu/harvard/hul/ois/jhove/module/gzip/CompressionType.java index 2686ff958..96de29636 100644 --- a/jhove-ext-modules/src/main/java/edu/harvard/hul/ois/jhove/module/gzip/CompressionType.java +++ b/jhove-ext-modules/src/main/java/edu/harvard/hul/ois/jhove/module/gzip/CompressionType.java @@ -1,98 +1,87 @@ /** * JHOVE2 - Next-generation architecture for format-aware characterization * - * Copyright (c) 2009 by The Regents of the University of California, - * Ithaka Harbors, Inc., and The Board of Trustees of the Leland Stanford - * Junior University. - * All rights reserved. + *

Copyright (c) 2009 by The Regents of the University of California, Ithaka Harbors, Inc., and + * The Board of Trustees of the Leland Stanford Junior University. All rights reserved. * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: + *

Redistribution and use in source and binary forms, with or without modification, are permitted + * provided that the following conditions are met: * - * o Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. + *

o Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. * - * o Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. + *

o Redistributions in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other materials provided with + * the distribution. * - * o Neither the name of the University of California/California Digital - * Library, Ithaka Harbors/Portico, or Stanford University, nor the names of - * its contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. + *

o Neither the name of the University of California/California Digital Library, Ithaka + * Harbors/Portico, or Stanford University, nor the names of its contributors may be used to endorse + * or promote products derived from this software without specific prior written permission. * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. + *

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY + * WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ - package edu.harvard.hul.ois.jhove.module.gzip; import java.util.HashMap; import java.util.Map; -/** - * Enumerated type for GZip supported compression types (extra flags - * in GZip header). -*/ +/** Enumerated type for GZip supported compression types (extra flags in GZip header). */ public class CompressionType { - /** The list of valid values. */ - private static Map values = new HashMap<>(); + /** The list of valid values. */ + private static Map values = new HashMap<>(); - /** GZip extra flag value for maximum compression. */ - public final static CompressionType MAXIMUM_COMPRESSION = new CompressionType(2, "Maximum compression", true); - /** GZip extra flag value for fastest algorithm. */ - public final static CompressionType FASTEST_ALGORITHM = new CompressionType(4, "Fastest algorithm", true); + /** GZip extra flag value for maximum compression. */ + public static final CompressionType MAXIMUM_COMPRESSION = + new CompressionType(2, "Maximum compression", true); + /** GZip extra flag value for fastest algorithm. */ + public static final CompressionType FASTEST_ALGORITHM = + new CompressionType(4, "Fastest algorithm", true); - /** - * Initializes the valid values. - */ - static { - values.put(Integer.valueOf(MAXIMUM_COMPRESSION.value), MAXIMUM_COMPRESSION); - values.put(Integer.valueOf(FASTEST_ALGORITHM.value), FASTEST_ALGORITHM); - } - - /** The integer value for the enum instance. */ - public final int value; - /** The value description. */ - public final String label; - /** Whether the value is valid. */ - public final boolean valid; + /** Initializes the valid values. */ + static { + values.put(Integer.valueOf(MAXIMUM_COMPRESSION.value), MAXIMUM_COMPRESSION); + values.put(Integer.valueOf(FASTEST_ALGORITHM.value), FASTEST_ALGORITHM); + } - /** - * Constructor. - * @param value The compression type value. - * @param label The name of the compression type. - * @param valid If it is a valid compression type. - */ - protected CompressionType(int value, String label, boolean valid) { - this.value = value; - this.label = label; - this.valid = valid; - } + /** The integer value for the enum instance. */ + public final int value; + /** The value description. */ + public final String label; + /** Whether the value is valid. */ + public final boolean valid; + + /** + * Constructor. + * + * @param value The compression type value. + * @param label The name of the compression type. + * @param valid If it is a valid compression type. + */ + protected CompressionType(int value, String label, boolean valid) { + this.value = value; + this.label = label; + this.valid = valid; + } - /** - * Returns the enumerated value object corresponding to the - * specified integer value. If the integer value is unknown, an - * instance marked as not valid is returned. - * @param n the integer value to map. - * - * @return a compression type objet, valid if n is - * one of the defined valid values. - */ - public static CompressionType fromValue(int n) { - CompressionType v = values.get(Integer.valueOf(n)); - if (v == null) { - v = new CompressionType(n, null, false); - } - return v; + /** + * Returns the enumerated value object corresponding to the specified integer value. If the + * integer value is unknown, an instance marked as not valid is returned. + * + * @param n the integer value to map. + * @return a compression type objet, valid if n is one of the defined valid values. + */ + public static CompressionType fromValue(int n) { + CompressionType v = values.get(Integer.valueOf(n)); + if (v == null) { + v = new CompressionType(n, null, false); } + return v; + } } diff --git a/jhove-ext-modules/src/main/java/edu/harvard/hul/ois/jhove/module/gzip/GzipEntryData.java b/jhove-ext-modules/src/main/java/edu/harvard/hul/ois/jhove/module/gzip/GzipEntryData.java index b96a1081f..0c0f2bf61 100644 --- a/jhove-ext-modules/src/main/java/edu/harvard/hul/ois/jhove/module/gzip/GzipEntryData.java +++ b/jhove-ext-modules/src/main/java/edu/harvard/hul/ois/jhove/module/gzip/GzipEntryData.java @@ -1,122 +1,110 @@ /** * JHOVE2 - Next-generation architecture for format-aware characterization * - * Copyright (c) 2009 by The Regents of the University of California, - * Ithaka Harbors, Inc., and The Board of Trustees of the Leland Stanford - * Junior University. - * All rights reserved. + *

Copyright (c) 2009 by The Regents of the University of California, Ithaka Harbors, Inc., and + * The Board of Trustees of the Leland Stanford Junior University. All rights reserved. * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: + *

Redistribution and use in source and binary forms, with or without modification, are permitted + * provided that the following conditions are met: * - * o Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. + *

o Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. * - * o Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. + *

o Redistributions in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other materials provided with + * the distribution. * - * o Neither the name of the University of California/California Digital - * Library, Ithaka Harbors/Portico, or Stanford University, nor the names of - * its contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. + *

o Neither the name of the University of California/California Digital Library, Ithaka + * Harbors/Portico, or Stanford University, nor the names of its contributors may be used to endorse + * or promote products derived from this software without specific prior written permission. * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. + *

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY + * WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ - package edu.harvard.hul.ois.jhove.module.gzip; import java.util.Date; - import org.jwat.gzip.GzipEntry; /** - * This class is a wrapper for the information available in a GZip entry. - * Since the GZip reader is not persistent its data must be moved to a simpler - * data class which can be persisted instead. + * This class is a wrapper for the information available in a GZip entry. Since the GZip reader is + * not persistent its data must be moved to a simpler data class which can be persisted instead. * * @author nicl */ public class GzipEntryData { - /** Boolean indicating whether header is non compliant. */ - protected boolean isNonCompliant; - /** Offset of entry in input stream. */ - protected long offset; - /** Compression methods read from header. */ - protected CompressionMethod method; - /** Compression type read from extra flags in header. */ - protected CompressionType extraFlags; - /** Optional filename from header. */ - protected String fileName; - /** Operating System from header converted to a field. */ - protected OperatingSystem os; - /** Optional comment from header. */ - protected String comment; - /** Ascii bit set in header. */ - protected boolean asciiFlag; - /** Optional CRC16 read from header. */ - protected Integer readCrc16; - /** CRC16 computed as verification to optional value found in header. */ - protected int computedCrc16; + /** Boolean indicating whether header is non compliant. */ + protected boolean isNonCompliant; + /** Offset of entry in input stream. */ + protected long offset; + /** Compression methods read from header. */ + protected CompressionMethod method; + /** Compression type read from extra flags in header. */ + protected CompressionType extraFlags; + /** Optional filename from header. */ + protected String fileName; + /** Operating System from header converted to a field. */ + protected OperatingSystem os; + /** Optional comment from header. */ + protected String comment; + /** Ascii bit set in header. */ + protected boolean asciiFlag; + /** Optional CRC16 read from header. */ + protected Integer readCrc16; + /** CRC16 computed as verification to optional value found in header. */ + protected int computedCrc16; - /** Date read from header. */ - protected Date date; + /** Date read from header. */ + protected Date date; - /** Uncompressed size of entry data. */ - protected long size = -1L; - /** Compressed size of entry data. */ - protected long csize = -1L; - /** ISIZE read from trailing header. */ - protected long readISize = -1L; - /** ISIZE computed as verification to value found in trailing header. */ - protected long computedISize = -1; - /** CRC32 read from trailing header. */ - protected int readCrc32; - /** CRC32 computed as verification to value found in trailing header. */ - protected int computedCrc32; + /** Uncompressed size of entry data. */ + protected long size = -1L; + /** Compressed size of entry data. */ + protected long csize = -1L; + /** ISIZE read from trailing header. */ + protected long readISize = -1L; + /** ISIZE computed as verification to value found in trailing header. */ + protected long computedISize = -1; + /** CRC32 read from trailing header. */ + protected int readCrc32; + /** CRC32 computed as verification to value found in trailing header. */ + protected int computedCrc32; - /** - * Constructor required by the persistence layer. - */ - public GzipEntryData() { - } + /** Constructor required by the persistence layer. */ + public GzipEntryData() {} - /** - * Given a GZip entry, transfer the data to this object so it can be - * persisted. - * @param entry GZip entry data - */ - public GzipEntryData(GzipEntry entry) { - if (entry == null) { - throw new IllegalArgumentException("'entry' should never be null"); - } - this.isNonCompliant = !entry.isCompliant(); - this.offset = entry.getStartOffset(); - this.method = CompressionMethod.fromValue(entry.cm); - this.date = entry.date; - this.extraFlags = CompressionType.fromValue(entry.xfl); - this.fileName = entry.fname; - this.os = OperatingSystem.fromValue(entry.os); - this.comment = entry.fcomment; - this.asciiFlag = entry.bFText; - this.readCrc16 = entry.crc16; - this.computedCrc16 = entry.comp_crc16; - this.csize = entry.compressed_size; - this.size = entry.uncompressed_size; - this.readCrc32 = entry.crc32; - this.computedCrc32 = entry.comp_crc32; - this.readISize = entry.isize; - this.computedISize = entry.comp_isize; + /** + * Given a GZip entry, transfer the data to this object so it can be persisted. + * + * @param entry GZip entry data + */ + public GzipEntryData(GzipEntry entry) { + if (entry == null) { + throw new IllegalArgumentException("'entry' should never be null"); } + this.isNonCompliant = !entry.isCompliant(); + this.offset = entry.getStartOffset(); + this.method = CompressionMethod.fromValue(entry.cm); + this.date = entry.date; + this.extraFlags = CompressionType.fromValue(entry.xfl); + this.fileName = entry.fname; + this.os = OperatingSystem.fromValue(entry.os); + this.comment = entry.fcomment; + this.asciiFlag = entry.bFText; + this.readCrc16 = entry.crc16; + this.computedCrc16 = entry.comp_crc16; + this.csize = entry.compressed_size; + this.size = entry.uncompressed_size; + this.readCrc32 = entry.crc32; + this.computedCrc32 = entry.comp_crc32; + this.readISize = entry.isize; + this.computedISize = entry.comp_isize; + } } diff --git a/jhove-ext-modules/src/main/java/edu/harvard/hul/ois/jhove/module/gzip/GzipEntryProperties.java b/jhove-ext-modules/src/main/java/edu/harvard/hul/ois/jhove/module/gzip/GzipEntryProperties.java index 570da2bd4..be3c1d54e 100644 --- a/jhove-ext-modules/src/main/java/edu/harvard/hul/ois/jhove/module/gzip/GzipEntryProperties.java +++ b/jhove-ext-modules/src/main/java/edu/harvard/hul/ois/jhove/module/gzip/GzipEntryProperties.java @@ -1,159 +1,148 @@ /** * JHOVE2 - Next-generation architecture for format-aware characterization * - * Copyright (c) 2009 by The Regents of the University of California, - * Ithaka Harbors, Inc., and The Board of Trustees of the Leland Stanford - * Junior University. - * All rights reserved. + *

Copyright (c) 2009 by The Regents of the University of California, Ithaka Harbors, Inc., and + * The Board of Trustees of the Leland Stanford Junior University. All rights reserved. * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: + *

Redistribution and use in source and binary forms, with or without modification, are permitted + * provided that the following conditions are met: * - * o Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. + *

o Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. * - * o Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. + *

o Redistributions in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other materials provided with + * the distribution. * - * o Neither the name of the University of California/California Digital - * Library, Ithaka Harbors/Portico, or Stanford University, nor the names of - * its contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. + *

o Neither the name of the University of California/California Digital Library, Ithaka + * Harbors/Portico, or Stanford University, nor the names of its contributors may be used to endorse + * or promote products derived from this software without specific prior written permission. * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. + *

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY + * WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ - package edu.harvard.hul.ois.jhove.module.gzip; - import java.util.Date; import java.util.LinkedHashMap; import java.util.Map; - import org.jwat.gzip.GzipEntry; - -/** - * A GZip file entry. - * Based on the property elements from the GZIP module of JHOVE2. - */ +/** A GZip file entry. Based on the property elements from the GZIP module of JHOVE2. */ public class GzipEntryProperties { - /** WARC record data container. */ - protected GzipEntryData data; - /** The map for the properties of the GZIP entry.*/ - private Map properties; + /** WARC record data container. */ + protected GzipEntryData data; + /** The map for the properties of the GZIP entry. */ + private Map properties; - /** - * Construct GZip entry base property instance with the supplied data. - * @param entry GZip entry data - */ - public GzipEntryProperties(GzipEntry entry) { - this.data = new GzipEntryData(entry); - } - - /** - * Retrieves the GZIP entry properties as a map. - * @return A map of the properties of the GZIP entry. - */ - public Map getProperties() { - properties = new LinkedHashMap<>(); - setProperty(Boolean.valueOf(data.isNonCompliant), "Is non compliant."); - setProperty(Long.valueOf(data.offset), "Offset value."); - setProperty(data.fileName, "GZip entry name."); - setProperty(data.comment, "GZip entry comment."); - setProperty(data.date, "GZip entry date."); - setProperty(data.method.label, "GZip entry compression method."); - setProperty(data.os.label, "GZip entry operating system."); - setProperty(getCrc16(), "GZip entry header crc16."); - setProperty("0x" + Integer.toHexString(data.readCrc32), "GZip entry crc32."); - setProperty(Long.valueOf(data.readISize), "GZip entry extracted size (ISIZE) value."); - setProperty(Long.valueOf(data.size), "GZip entry (computed) uncompressed size, in bytes."); - setProperty(Long.valueOf(data.csize), "GZip entry (computed) compressed size, in bytes."); - setProperty(getCompressionRatio(), "GZip entry (computed) compression ratio."); - return properties; - } + /** + * Construct GZip entry base property instance with the supplied data. + * + * @param entry GZip entry data + */ + public GzipEntryProperties(GzipEntry entry) { + this.data = new GzipEntryData(entry); + } + + /** + * Retrieves the GZIP entry properties as a map. + * + * @return A map of the properties of the GZIP entry. + */ + public Map getProperties() { + properties = new LinkedHashMap<>(); + setProperty(Boolean.valueOf(data.isNonCompliant), "Is non compliant."); + setProperty(Long.valueOf(data.offset), "Offset value."); + setProperty(data.fileName, "GZip entry name."); + setProperty(data.comment, "GZip entry comment."); + setProperty(data.date, "GZip entry date."); + setProperty(data.method.label, "GZip entry compression method."); + setProperty(data.os.label, "GZip entry operating system."); + setProperty(getCrc16(), "GZip entry header crc16."); + setProperty("0x" + Integer.toHexString(data.readCrc32), "GZip entry crc32."); + setProperty(Long.valueOf(data.readISize), "GZip entry extracted size (ISIZE) value."); + setProperty(Long.valueOf(data.size), "GZip entry (computed) uncompressed size, in bytes."); + setProperty(Long.valueOf(data.csize), "GZip entry (computed) compressed size, in bytes."); + setProperty(getCompressionRatio(), "GZip entry (computed) compression ratio."); + return properties; + } - /** - * @return The crc16 element, converted into a HEX. Or null, if it not set. - */ - private String getCrc16() { - String crc16; - if (data.readCrc16 != null) { - crc16 = "0x" + Integer.toHexString(data.readCrc16.intValue() & 0xffff); - } else { - crc16 = null; - } - return crc16; + /** @return The crc16 element, converted into a HEX. Or null, if it not set. */ + private String getCrc16() { + String crc16; + if (data.readCrc16 != null) { + crc16 = "0x" + Integer.toHexString(data.readCrc16.intValue() & 0xffff); + } else { + crc16 = null; } + return crc16; + } - /** - * @return The calculated compressionRatio. - */ - private String getCompressionRatio() { - Double ratio = Double.valueOf(-1.0); - long size = data.size; - long csize = data.csize; - if ((size > 0L) && (csize > 0L)) { - // Compute compression ratio with 2 decimals only. - long l = ((size - csize) * 10000L) / size; - ratio = Double.valueOf(l / 100.00); - } - return ratio.toString(); + /** @return The calculated compressionRatio. */ + private String getCompressionRatio() { + Double ratio = Double.valueOf(-1.0); + long size = data.size; + long csize = data.csize; + if ((size > 0L) && (csize > 0L)) { + // Compute compression ratio with 2 decimals only. + long l = ((size - csize) * 10000L) / size; + ratio = Double.valueOf(l / 100.00); } + return ratio.toString(); + } - /** - * Sets the given string property, if it has a valid value (not null and not empty). - * @param variable The value for the property. - * @param description The description and key of the property. - */ - private void setProperty(String variable, String description) { - if(variable != null && !variable.isEmpty()) { - properties.put(description, variable); - } + /** + * Sets the given string property, if it has a valid value (not null and not empty). + * + * @param variable The value for the property. + * @param description The description and key of the property. + */ + private void setProperty(String variable, String description) { + if (variable != null && !variable.isEmpty()) { + properties.put(description, variable); } + } - /** - * Sets the given long property, if it has a valid value (not null and not empty). - * @param variable The value for the property. - * @param description The description and key of the property. - */ - private void setProperty(Long variable, String description) { - if(variable != null ) { - properties.put(description, variable.toString()); - } + /** + * Sets the given long property, if it has a valid value (not null and not empty). + * + * @param variable The value for the property. + * @param description The description and key of the property. + */ + private void setProperty(Long variable, String description) { + if (variable != null) { + properties.put(description, variable.toString()); } + } - /** - * Sets the given boolean property, if it has a valid value (not null and not empty). - * @param variable The value for the property. - * @param description The description and key of the property. - */ - private void setProperty(Boolean variable, String description) { - if(variable != null) { - properties.put(description, variable.toString()); - } + /** + * Sets the given boolean property, if it has a valid value (not null and not empty). + * + * @param variable The value for the property. + * @param description The description and key of the property. + */ + private void setProperty(Boolean variable, String description) { + if (variable != null) { + properties.put(description, variable.toString()); } + } - /** - * Sets the given date property, if it has a valid value (not null and not empty). - * @param variable The value for the property. - * @param description The description and key of the property. - */ - private void setProperty(Date variable, String description) { - if(variable != null) { - properties.put(description, variable.toString()); - } + /** + * Sets the given date property, if it has a valid value (not null and not empty). + * + * @param variable The value for the property. + * @param description The description and key of the property. + */ + private void setProperty(Date variable, String description) { + if (variable != null) { + properties.put(description, variable.toString()); } + } } diff --git a/jhove-ext-modules/src/main/java/edu/harvard/hul/ois/jhove/module/gzip/MessageConstants.java b/jhove-ext-modules/src/main/java/edu/harvard/hul/ois/jhove/module/gzip/MessageConstants.java index 0b2530f0a..e48314fa5 100644 --- a/jhove-ext-modules/src/main/java/edu/harvard/hul/ois/jhove/module/gzip/MessageConstants.java +++ b/jhove-ext-modules/src/main/java/edu/harvard/hul/ois/jhove/module/gzip/MessageConstants.java @@ -1,12 +1,8 @@ package edu.harvard.hul.ois.jhove.module.gzip; public enum MessageConstants { - - INSTANCE; - - /** - * Error messages - */ - public static final String ERR_RECORD_NULL = "GzipReader has not been properly instantiated."; - + INSTANCE; + + /** Error messages */ + public static final String ERR_RECORD_NULL = "GzipReader has not been properly instantiated."; } diff --git a/jhove-ext-modules/src/main/java/edu/harvard/hul/ois/jhove/module/gzip/OperatingSystem.java b/jhove-ext-modules/src/main/java/edu/harvard/hul/ois/jhove/module/gzip/OperatingSystem.java index 32d49d68a..5a5a05f3f 100644 --- a/jhove-ext-modules/src/main/java/edu/harvard/hul/ois/jhove/module/gzip/OperatingSystem.java +++ b/jhove-ext-modules/src/main/java/edu/harvard/hul/ois/jhove/module/gzip/OperatingSystem.java @@ -1,136 +1,126 @@ /** * JHOVE2 - Next-generation architecture for format-aware characterization * - * Copyright (c) 2009 by The Regents of the University of California, - * Ithaka Harbors, Inc., and The Board of Trustees of the Leland Stanford - * Junior University. - * All rights reserved. + *

Copyright (c) 2009 by The Regents of the University of California, Ithaka Harbors, Inc., and + * The Board of Trustees of the Leland Stanford Junior University. All rights reserved. * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: + *

Redistribution and use in source and binary forms, with or without modification, are permitted + * provided that the following conditions are met: * - * o Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. + *

o Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. * - * o Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. + *

o Redistributions in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other materials provided with + * the distribution. * - * o Neither the name of the University of California/California Digital - * Library, Ithaka Harbors/Portico, or Stanford University, nor the names of - * its contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. + *

o Neither the name of the University of California/California Digital Library, Ithaka + * Harbors/Portico, or Stanford University, nor the names of its contributors may be used to endorse + * or promote products derived from this software without specific prior written permission. * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. + *

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY + * WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ - package edu.harvard.hul.ois.jhove.module.gzip; import java.util.HashMap; import java.util.Map; -/** - * Enumerated type for GZip supported operating systems. - */ +/** Enumerated type for GZip supported operating systems. */ public final class OperatingSystem { - /** The list of valid values. */ - private static Map values = new HashMap<>(); + /** The list of valid values. */ + private static Map values = new HashMap<>(); - /** The FAT filesystem (MS-DOS, OS/2, NT/Win32). */ - public static final OperatingSystem FAT_FILESYSTEM = new OperatingSystem(0, "FAT filesystem (MS-DOS, OS/2, NT/Win32)", true); - /** Amiga. */ - public static final OperatingSystem AMIGA = new OperatingSystem(1, "Amiga", true); - /** VMS (or OpenVMS). */ - public static final OperatingSystem VMS = new OperatingSystem(2, "VMS (or OpenVMS)", true); - /** Unix. */ - public static final OperatingSystem UNIX = new OperatingSystem(3, "Unix", true); - /** VM/CMS. */ - public static final OperatingSystem VM_CMS = new OperatingSystem(4, "VM/CMS", true); - /** Atari TOS.*/ - public static final OperatingSystem ATARI_TOS = new OperatingSystem(5, "Atari TOS", true); - /** HPFS filesystem (0S/2, NT). */ - public static final OperatingSystem HPFS = new OperatingSystem(6, "HPFS filesystem (OS/2, NT)", true); - /** Macintosh. */ - public static final OperatingSystem MACINTOSH = new OperatingSystem(7, "Macintosh)", true); - /** Z-System. */ - public static final OperatingSystem Z_SYSTEM = new OperatingSystem(8, "Z-System", true); - /** CP/M. */ - public static final OperatingSystem CP_M = new OperatingSystem(9, "CP/M", true); - /** TOPS-20. */ - public static final OperatingSystem TOPS_20 = new OperatingSystem(10, "TOPS-20", true); - /** NTFS filesystem (NT). */ - public static final OperatingSystem NTFS = new OperatingSystem(11, "NTFS filesystem (NT)", true); - /** QDOS. */ - public static final OperatingSystem QDOS = new OperatingSystem(12, "QDOS", true); - /** Acorn RISCOS. */ - public static final OperatingSystem ACORN_RISCOS = new OperatingSystem(13, "Acorn RISCOS", true); - /** Unknown. */ - public static final OperatingSystem UNKNOWN = new OperatingSystem(255, "Unknown", true); - - /** - * Initializes the valid values. - */ - static { - values.put(Integer.valueOf(FAT_FILESYSTEM.value), FAT_FILESYSTEM); - values.put(Integer.valueOf(AMIGA.value), AMIGA); - values.put(Integer.valueOf(VMS.value), VMS); - values.put(Integer.valueOf(UNIX.value), UNIX); - values.put(Integer.valueOf(VM_CMS.value), VM_CMS); - values.put(Integer.valueOf(ATARI_TOS.value), ATARI_TOS); - values.put(Integer.valueOf(HPFS.value), HPFS); - values.put(Integer.valueOf(MACINTOSH.value), MACINTOSH); - values.put(Integer.valueOf(Z_SYSTEM.value), Z_SYSTEM); - values.put(Integer.valueOf(CP_M.value), CP_M); - values.put(Integer.valueOf(TOPS_20.value), TOPS_20); - values.put(Integer.valueOf(NTFS.value), NTFS); - values.put(Integer.valueOf(QDOS.value), QDOS); - values.put(Integer.valueOf(ACORN_RISCOS.value), ACORN_RISCOS); - values.put(Integer.valueOf(UNKNOWN.value), UNKNOWN); - } - - /** The integer value for the enum instance. */ - public final int value; - /** The value description. */ - public final String label; - /** Whether the value is valid. */ - public final boolean valid; - - /** - * Constructor. - * @param value The compression type value. - * @param label The name of the compression type. - * @param valid If it is a valid compression type. - */ - protected OperatingSystem(int value, String label, boolean valid) { - this.value = value; - this.label = label; - this.valid = valid; } + /** The FAT filesystem (MS-DOS, OS/2, NT/Win32). */ + public static final OperatingSystem FAT_FILESYSTEM = + new OperatingSystem(0, "FAT filesystem (MS-DOS, OS/2, NT/Win32)", true); + /** Amiga. */ + public static final OperatingSystem AMIGA = new OperatingSystem(1, "Amiga", true); + /** VMS (or OpenVMS). */ + public static final OperatingSystem VMS = new OperatingSystem(2, "VMS (or OpenVMS)", true); + /** Unix. */ + public static final OperatingSystem UNIX = new OperatingSystem(3, "Unix", true); + /** VM/CMS. */ + public static final OperatingSystem VM_CMS = new OperatingSystem(4, "VM/CMS", true); + /** Atari TOS. */ + public static final OperatingSystem ATARI_TOS = new OperatingSystem(5, "Atari TOS", true); + /** HPFS filesystem (0S/2, NT). */ + public static final OperatingSystem HPFS = + new OperatingSystem(6, "HPFS filesystem (OS/2, NT)", true); + /** Macintosh. */ + public static final OperatingSystem MACINTOSH = new OperatingSystem(7, "Macintosh)", true); + /** Z-System. */ + public static final OperatingSystem Z_SYSTEM = new OperatingSystem(8, "Z-System", true); + /** CP/M. */ + public static final OperatingSystem CP_M = new OperatingSystem(9, "CP/M", true); + /** TOPS-20. */ + public static final OperatingSystem TOPS_20 = new OperatingSystem(10, "TOPS-20", true); + /** NTFS filesystem (NT). */ + public static final OperatingSystem NTFS = new OperatingSystem(11, "NTFS filesystem (NT)", true); + /** QDOS. */ + public static final OperatingSystem QDOS = new OperatingSystem(12, "QDOS", true); + /** Acorn RISCOS. */ + public static final OperatingSystem ACORN_RISCOS = new OperatingSystem(13, "Acorn RISCOS", true); + /** Unknown. */ + public static final OperatingSystem UNKNOWN = new OperatingSystem(255, "Unknown", true); - /** - * Returns the enumerated value object corresponding to the - * specified integer value. If the integer value is unknown, an - * instance marked as not valid is returned. - * @param n the integer value to map. - * - * @return a operating system object, valid if n is - * one of the defined valid values. - */ - public static OperatingSystem fromValue(int n) { - OperatingSystem v = values.get(Integer.valueOf(n)); - if (v == null) { - v = new OperatingSystem(n, null, false); - } - return v; - } + /** Initializes the valid values. */ + static { + values.put(Integer.valueOf(FAT_FILESYSTEM.value), FAT_FILESYSTEM); + values.put(Integer.valueOf(AMIGA.value), AMIGA); + values.put(Integer.valueOf(VMS.value), VMS); + values.put(Integer.valueOf(UNIX.value), UNIX); + values.put(Integer.valueOf(VM_CMS.value), VM_CMS); + values.put(Integer.valueOf(ATARI_TOS.value), ATARI_TOS); + values.put(Integer.valueOf(HPFS.value), HPFS); + values.put(Integer.valueOf(MACINTOSH.value), MACINTOSH); + values.put(Integer.valueOf(Z_SYSTEM.value), Z_SYSTEM); + values.put(Integer.valueOf(CP_M.value), CP_M); + values.put(Integer.valueOf(TOPS_20.value), TOPS_20); + values.put(Integer.valueOf(NTFS.value), NTFS); + values.put(Integer.valueOf(QDOS.value), QDOS); + values.put(Integer.valueOf(ACORN_RISCOS.value), ACORN_RISCOS); + values.put(Integer.valueOf(UNKNOWN.value), UNKNOWN); + } + + /** The integer value for the enum instance. */ + public final int value; + /** The value description. */ + public final String label; + /** Whether the value is valid. */ + public final boolean valid; + /** + * Constructor. + * + * @param value The compression type value. + * @param label The name of the compression type. + * @param valid If it is a valid compression type. + */ + protected OperatingSystem(int value, String label, boolean valid) { + this.value = value; + this.label = label; + this.valid = valid; + } + + /** + * Returns the enumerated value object corresponding to the specified integer value. If the + * integer value is unknown, an instance marked as not valid is returned. + * + * @param n the integer value to map. + * @return a operating system object, valid if n is one of the defined valid values. + */ + public static OperatingSystem fromValue(int n) { + OperatingSystem v = values.get(Integer.valueOf(n)); + if (v == null) { + v = new OperatingSystem(n, null, false); + } + return v; + } } diff --git a/jhove-ext-modules/src/main/java/edu/harvard/hul/ois/jhove/module/gzip/package-info.java b/jhove-ext-modules/src/main/java/edu/harvard/hul/ois/jhove/module/gzip/package-info.java index 722738d5a..f27eea4a5 100644 --- a/jhove-ext-modules/src/main/java/edu/harvard/hul/ois/jhove/module/gzip/package-info.java +++ b/jhove-ext-modules/src/main/java/edu/harvard/hul/ois/jhove/module/gzip/package-info.java @@ -1,13 +1,12 @@ /** - * Contains the classes needed for building a JHOVE application. - * This package must be used with a top-level class, with one or more - * output handlers or viewers, and with one or more modules for specific - * file formats. - *

- * For overviews, tutorials, examples, guides, and tool documentation, - * please see: + * Contains the classes needed for building a JHOVE application. This package must be used with a + * top-level class, with one or more output handlers or viewers, and with one or more modules for + * specific file formats. + * + *

For overviews, tutorials, examples, guides, and tool documentation, please see: + * *

*/ package hul.ois.jhove.module.gzip; diff --git a/jhove-ext-modules/src/main/java/edu/harvard/hul/ois/jhove/module/warc/MessageConstants.java b/jhove-ext-modules/src/main/java/edu/harvard/hul/ois/jhove/module/warc/MessageConstants.java index e7f61151b..d5218dc3b 100644 --- a/jhove-ext-modules/src/main/java/edu/harvard/hul/ois/jhove/module/warc/MessageConstants.java +++ b/jhove-ext-modules/src/main/java/edu/harvard/hul/ois/jhove/module/warc/MessageConstants.java @@ -1,15 +1,12 @@ package edu.harvard.hul.ois.jhove.module.warc; public enum MessageConstants { - - INSTANCE; + INSTANCE; - /** - * Error messages - */ - - public static final String ERR_RECORD_DATA_NULL = "'record' should never be null"; - public static final String ERR_BLOCK_DIGEST_INVALID = "Invalid block digest algorithm: "; - public static final String ERR_PAYLOAD_DIGEST_INVALID = "Invalid payload digest algorithm: "; - public static final String ERR_RECORD_NULL = "WarcReader has not been properly instantiated."; + /** Error messages */ + public static final String ERR_RECORD_DATA_NULL = "'record' should never be null"; + + public static final String ERR_BLOCK_DIGEST_INVALID = "Invalid block digest algorithm: "; + public static final String ERR_PAYLOAD_DIGEST_INVALID = "Invalid payload digest algorithm: "; + public static final String ERR_RECORD_NULL = "WarcReader has not been properly instantiated."; } diff --git a/jhove-ext-modules/src/main/java/edu/harvard/hul/ois/jhove/module/warc/WarcRecordData.java b/jhove-ext-modules/src/main/java/edu/harvard/hul/ois/jhove/module/warc/WarcRecordData.java index ecc9989a1..e8124952b 100644 --- a/jhove-ext-modules/src/main/java/edu/harvard/hul/ois/jhove/module/warc/WarcRecordData.java +++ b/jhove-ext-modules/src/main/java/edu/harvard/hul/ois/jhove/module/warc/WarcRecordData.java @@ -2,7 +2,6 @@ import java.util.LinkedList; import java.util.List; - import org.jwat.common.HeaderLine; import org.jwat.common.HttpHeader; import org.jwat.common.Payload; @@ -13,301 +12,293 @@ /** * Copied from JHOVE2 WARC module. - * - * This class is a wrapper for the information available in an WARC record. - * Since the WARC reader is not persistent its data must be moved to a simpler - * data class which can be persisted instead. * - * Note: Some populate methods currently do not include any functionality. - * However they are included for backwards compatibility in case the ISO - * standard changes and extra properties are required. + *

This class is a wrapper for the information available in an WARC record. Since the WARC reader + * is not persistent its data must be moved to a simpler data class which can be persisted instead. + * + *

Note: Some populate methods currently do not include any functionality. However they are + * included for backwards compatibility in case the ISO standard changes and extra properties are + * required. * * @author nicl */ public class WarcRecordData { - /** Start offset of record in input stream. */ - protected Long startOffset; - /** Number of bytes consumed validating record. */ - protected Long consumed; + /** Start offset of record in input stream. */ + protected Long startOffset; + /** Number of bytes consumed validating record. */ + protected Long consumed; + + /** WARC version read from header. */ + protected String warcVersionStr; - /** WARC version read from header. */ - protected String warcVersionStr; + /** WARC-Type read from header. */ + protected String warcType; + /** WARC-Filename read from header. */ + protected String warcFilename; + /** WARC-Record-Id read from header. */ + protected String warcRecordId; + /** WARC-Date read from header. */ + protected String warcDate; + /** Content-Length read from header. */ + protected String contentLength; + /** Content-type read from header. */ + protected String contentType; + /** WARC-Truncated read from header. */ + protected String warcTruncated; + /** WARC-IP-Address read from header. */ + protected String warcIpAddress; + /** List of WARC-Concurrent-To read from header. */ + protected List warcConcurrentToList; + /** WARC-Refers-To read from header. */ + protected String warcRefersTo; + /** WARC-Target-URI read from header. */ + protected String warcTargetUri; + /** WARC-Warcinfo-ID read from header. */ + protected String warcWarcinfoId; + /** WARC-Identified-Payload-Type read from header. */ + protected String warcIdentifiedPayloadType; + /** WARC-Profile read from header. */ + protected String warcProfile; + /** WARC-Segment-Number read from header. */ + protected String warcSegmentNumber; + /** WARC-Segment-Origin-ID read from header. */ + protected String warcSegmentOriginId; + /** WARC-Segment-Total-Length read from header. */ + protected String warcSegmentTotalLength; - /** WARC-Type read from header. */ - protected String warcType; - /** WARC-Filename read from header. */ - protected String warcFilename; - /** WARC-Record-Id read from header. */ - protected String warcRecordId; - /** WARC-Date read from header. */ - protected String warcDate; - /** Content-Length read from header. */ - protected String contentLength; - /** Content-type read from header. */ - protected String contentType; - /** WARC-Truncated read from header. */ - protected String warcTruncated; - /** WARC-IP-Address read from header. */ - protected String warcIpAddress; - /** List of WARC-Concurrent-To read from header. */ - protected List warcConcurrentToList; - /** WARC-Refers-To read from header. */ - protected String warcRefersTo; - /** WARC-Target-URI read from header. */ - protected String warcTargetUri; - /** WARC-Warcinfo-ID read from header. */ - protected String warcWarcinfoId; - /** WARC-Identified-Payload-Type read from header. */ - protected String warcIdentifiedPayloadType; - /** WARC-Profile read from header. */ - protected String warcProfile; - /** WARC-Segment-Number read from header. */ - protected String warcSegmentNumber; - /** WARC-Segment-Origin-ID read from header. */ - protected String warcSegmentOriginId; - /** WARC-Segment-Total-Length read from header. */ - protected String warcSegmentTotalLength; + /** Block digest read from header. */ + protected String warcBlockDigest; + /** Block digest algorithm read from header. */ + protected String warcBlockDigestAlgorithm; + /** Block digest encoding auto-detected from digest and algorithm. */ + protected String warcBlockDigestEncoding; + /** Payload digest read from header. */ + protected String warcPayloadDigest; + /** Payload digest algorithm read from header. */ + protected String warcPayloadDigestAlgorithm; + /** Payload digest encoding auto-detected from digest and algorithm. */ + protected String warcPayloadDigestEncoding; - /** Block digest read from header. */ - protected String warcBlockDigest; - /** Block digest algorithm read from header. */ - protected String warcBlockDigestAlgorithm; - /** Block digest encoding auto-detected from digest and algorithm. */ - protected String warcBlockDigestEncoding; - /** Payload digest read from header. */ - protected String warcPayloadDigest; - /** Payload digest algorithm read from header. */ - protected String warcPayloadDigestAlgorithm; - /** Payload digest encoding auto-detected from digest and algorithm. */ - protected String warcPayloadDigestEncoding; + /** Computed block digest. */ + protected String computedBlockDigest; + /** Computed block digest algorithm. */ + protected String computedBlockDigestAlgorithm; + /** Computed block digest encoding. */ + protected String computedBlockDigestEncoding; + /** Computed payload digest, if applicable. */ + protected String computedPayloadDigest; + /** Computed payload digest algorithm, if applicable. */ + protected String computedPayloadDigestAlgorithm; + /** Computed payload digest encoding, if applicable. */ + protected String computedPayloadDigestEncoding; - /** Computed block digest. */ - protected String computedBlockDigest; - /** Computed block digest algorithm. */ - protected String computedBlockDigestAlgorithm; - /** Computed block digest encoding. */ - protected String computedBlockDigestEncoding; - /** Computed payload digest, if applicable. */ - protected String computedPayloadDigest; - /** Computed payload digest algorithm, if applicable. */ - protected String computedPayloadDigestAlgorithm; - /** Computed payload digest encoding, if applicable. */ - protected String computedPayloadDigestEncoding; + /** WARC-Record-Id scheme used. */ + protected String recordIdScheme; - /** WARC-Record-Id scheme used. */ - protected String recordIdScheme; + /** Boolean indicating whether this record is compliant or not. */ + protected Boolean bIsNonCompliant; + /** Boolean indicating whether the block digest is valid or not. */ + protected Boolean isValidBlockDigest; + /** Boolean indicating whether the payload digest is valid or not. */ + protected Boolean isValidPayloadDigest; - /** Boolean indicating whether this record is compliant or not. */ - protected Boolean bIsNonCompliant; - /** Boolean indicating whether the block digest is valid or not. */ - protected Boolean isValidBlockDigest; - /** Boolean indicating whether the payload digest is valid or not. */ - protected Boolean isValidPayloadDigest; + /* Does the record have a payload. */ + protected Boolean bHasPayload; + /** Payload length, without payload header (version block/HTTP header). */ + protected String payloadLength; - /* Does the record have a payload. */ - protected Boolean bHasPayload; - /** Payload length, without payload header (version block/HTTP header). */ - protected String payloadLength; + /** IP vresion of WARC-IP-Address (4 or 6). */ + protected String ipVersion; - /** IP vresion of WARC-IP-Address (4 or 6). */ - protected String ipVersion; + /** Result-code read from HTTP header, if present. */ + protected String resultCode; + /** Protocol version read from HTTP header, if present. */ + protected String protocolVersion; + /** Content-type read from HTTP header, if present. */ + protected String protocolContentType; + /** Server header entry read from HTTP header, if present. */ + protected String protocolServer; + /** User-Agent header entry read from HTTP header, if present. */ + protected String protocolUserAgent; - /** Result-code read from HTTP header, if present. */ - protected String resultCode; - /** Protocol version read from HTTP header, if present. */ - protected String protocolVersion; - /** Content-type read from HTTP header, if present. */ - protected String protocolContentType; - /** Server header entry read from HTTP header, if present. */ - protected String protocolServer; - /** User-Agent header entry read from HTTP header, if present. */ - protected String protocolUserAgent; + /** Constructor required by the persistence layer. */ + public WarcRecordData() {} - /** - * Constructor required by the persistence layer. + /** + * Constructs an object using the data in the WarcRecord object. + * + * @param record parsed WARC record + */ + public WarcRecordData(WarcRecord record) { + if (record == null) { + throw new IllegalArgumentException(MessageConstants.ERR_RECORD_DATA_NULL); + } + WarcHeader header = record.header; + startOffset = record.getStartOffset(); + consumed = record.getConsumed(); + if (header.bValidVersionFormat) { + this.warcVersionStr = header.versionStr; + } + this.warcType = header.warcTypeStr; + this.warcFilename = header.warcFilename; + this.warcRecordId = header.warcRecordIdStr; + this.warcDate = header.warcDateStr; + this.contentLength = header.contentLengthStr; + this.contentType = header.contentTypeStr; + this.warcTruncated = header.warcTruncatedStr; + this.warcIpAddress = header.warcIpAddress; + // TODO Clone List in WarcRecord's getter at some point. + if (header.warcConcurrentToList != null && header.warcConcurrentToList.size() > 0) { + this.warcConcurrentToList = new LinkedList<>(); + WarcConcurrentTo warcConcurrentTo; + for (int i = 0; i < header.warcConcurrentToList.size(); ++i) { + warcConcurrentTo = header.warcConcurrentToList.get(i); + if (warcConcurrentTo.warcConcurrentToStr != null) { + this.warcConcurrentToList.add(warcConcurrentTo.warcConcurrentToStr); + } + } + } + this.warcRefersTo = header.warcRefersToStr; + this.warcTargetUri = header.warcTargetUriStr; + this.warcWarcinfoId = header.warcWarcinfoIdStr; + this.warcIdentifiedPayloadType = header.warcIdentifiedPayloadTypeStr; + this.warcProfile = header.warcProfileStr; + this.warcSegmentNumber = header.warcSegmentNumberStr; + this.warcSegmentOriginId = header.warcSegmentOriginIdStr; + this.warcSegmentTotalLength = header.warcSegmentTotalLengthStr; + /* + * Warc-Block-Digest. */ - public WarcRecordData() { + if (header.warcBlockDigest != null) { + if (header.warcBlockDigest.digestString != null + && header.warcBlockDigest.digestString.length() > 0) { + warcBlockDigest = header.warcBlockDigest.digestString; + } + if (header.warcBlockDigest.algorithm != null + && header.warcBlockDigest.algorithm.length() > 0) { + warcBlockDigestAlgorithm = header.warcBlockDigest.algorithm; + } + if (header.warcBlockDigest.encoding != null && header.warcBlockDigest.encoding.length() > 0) { + warcBlockDigestEncoding = header.warcBlockDigest.encoding; + } } - - /** - * Constructs an object using the data in the WarcRecord - * object. - * @param record parsed WARC record + /* + * Warc-Payload-Digest. */ - public WarcRecordData(WarcRecord record) { - if (record == null) { - throw new IllegalArgumentException(MessageConstants.ERR_RECORD_DATA_NULL); - } - WarcHeader header = record.header; - startOffset = record.getStartOffset(); - consumed = record.getConsumed(); - if (header.bValidVersionFormat) { - this.warcVersionStr = header.versionStr; - } - this.warcType = header.warcTypeStr; - this.warcFilename = header.warcFilename; - this.warcRecordId = header.warcRecordIdStr; - this.warcDate = header.warcDateStr; - this.contentLength = header.contentLengthStr; - this.contentType = header.contentTypeStr; - this.warcTruncated = header.warcTruncatedStr; - this.warcIpAddress = header.warcIpAddress; - // TODO Clone List in WarcRecord's getter at some point. - if (header.warcConcurrentToList != null && header.warcConcurrentToList.size() > 0) { - this.warcConcurrentToList = new LinkedList<>(); - WarcConcurrentTo warcConcurrentTo; - for (int i=0; i 0) { - warcBlockDigest = header.warcBlockDigest.digestString; - } - if (header.warcBlockDigest.algorithm != null - && header.warcBlockDigest.algorithm.length() > 0) { - warcBlockDigestAlgorithm = header.warcBlockDigest.algorithm; - } - if (header.warcBlockDigest.encoding != null - && header.warcBlockDigest.encoding.length() > 0) { - warcBlockDigestEncoding = header.warcBlockDigest.encoding; - } - } - /* - * Warc-Payload-Digest. - */ - if (header.warcPayloadDigest != null) { - if (header.warcPayloadDigest.digestString != null - && header.warcPayloadDigest.digestString.length() > 0) { - warcPayloadDigest = header.warcPayloadDigest.digestString; - } - if (header.warcPayloadDigest.algorithm != null - && header.warcPayloadDigest.algorithm.length() > 0) { - warcPayloadDigestAlgorithm = header.warcPayloadDigest.algorithm; - } - if (header.warcPayloadDigest.encoding != null - && header.warcPayloadDigest.encoding.length() > 0) { - warcPayloadDigestEncoding = header.warcPayloadDigest.encoding; - } - } - /* - * Computed-Block-Digest. - */ - if (record.computedBlockDigest != null) { - if ( record.computedBlockDigest.digestString != null - && record.computedBlockDigest.digestString.length() > 0) { - computedBlockDigest = record.computedBlockDigest.digestString; - } - if (record.computedBlockDigest.algorithm != null - && record.computedBlockDigest.algorithm.length() > 0) { - computedBlockDigestAlgorithm = record.computedBlockDigest.algorithm; - } - if (record.computedBlockDigest.encoding != null - && record.computedBlockDigest.encoding.length() > 0) { - computedBlockDigestEncoding = record.computedBlockDigest.encoding; - } - } - /* - * Computed-Payload-Digest. - */ - if (record.computedPayloadDigest != null) { - if (record.computedPayloadDigest.digestString != null - && record.computedPayloadDigest.digestString.length() > 0) { - computedPayloadDigest = record.computedPayloadDigest.digestString; - } - if (record.computedPayloadDigest.algorithm != null - && record.computedPayloadDigest.algorithm.length() > 0) { - computedPayloadDigestAlgorithm = record.computedPayloadDigest.algorithm; - } - if (record.computedPayloadDigest.encoding != null - && record.computedPayloadDigest.encoding.length() > 0) { - computedPayloadDigestEncoding = record.computedPayloadDigest.encoding; - } - } - /* - * Record-Id scheme. - */ - if (warcRecordId != null) { - int idx = warcRecordId.indexOf(':'); - if (idx >= 0) { - if (warcRecordId.startsWith("<")) { - recordIdScheme = warcRecordId.substring(1, idx); - } - else { - recordIdScheme = warcRecordId.substring(0, idx); - } - } - } - /* - * Compliance. - */ - bIsNonCompliant = !record.isCompliant(); - isValidBlockDigest = record.isValidBlockDigest; - isValidPayloadDigest = record.isValidPayloadDigest; - /* - * Payload. - */ - bHasPayload = record.hasPayload(); - Payload payload = record.getPayload(); - HeaderLine headerLine; - if (payload != null) { - PayloadWithHeaderAbstract payloadHeaderWrapped = payload.getPayloadHeaderWrapped(); - HttpHeader httpHeader = null; - if (payloadHeaderWrapped instanceof HttpHeader) { - httpHeader = (HttpHeader)payloadHeaderWrapped; - } - if (httpHeader != null) { - payloadLength = Long.toString(httpHeader.getPayloadLength()); - protocolVersion = httpHeader.httpVersion; - switch (httpHeader.headerType) { - case HttpHeader.HT_RESPONSE: - resultCode = httpHeader.statusCodeStr; - protocolContentType = httpHeader.contentType; - headerLine = httpHeader.getHeader("server"); - if (headerLine != null && headerLine.value != null) { - protocolServer = headerLine.value; - } - break; - case HttpHeader.HT_REQUEST: - headerLine = httpHeader.getHeader("user-agent"); - if (headerLine != null && headerLine.value != null) { - protocolUserAgent = headerLine.value; - } - break; - default: - break; - } - } - else { - payloadLength = Long.toString(payload.getTotalLength()); - } + if (header.warcPayloadDigest != null) { + if (header.warcPayloadDigest.digestString != null + && header.warcPayloadDigest.digestString.length() > 0) { + warcPayloadDigest = header.warcPayloadDigest.digestString; + } + if (header.warcPayloadDigest.algorithm != null + && header.warcPayloadDigest.algorithm.length() > 0) { + warcPayloadDigestAlgorithm = header.warcPayloadDigest.algorithm; + } + if (header.warcPayloadDigest.encoding != null + && header.warcPayloadDigest.encoding.length() > 0) { + warcPayloadDigestEncoding = header.warcPayloadDigest.encoding; + } + } + /* + * Computed-Block-Digest. + */ + if (record.computedBlockDigest != null) { + if (record.computedBlockDigest.digestString != null + && record.computedBlockDigest.digestString.length() > 0) { + computedBlockDigest = record.computedBlockDigest.digestString; + } + if (record.computedBlockDigest.algorithm != null + && record.computedBlockDigest.algorithm.length() > 0) { + computedBlockDigestAlgorithm = record.computedBlockDigest.algorithm; + } + if (record.computedBlockDigest.encoding != null + && record.computedBlockDigest.encoding.length() > 0) { + computedBlockDigestEncoding = record.computedBlockDigest.encoding; + } + } + /* + * Computed-Payload-Digest. + */ + if (record.computedPayloadDigest != null) { + if (record.computedPayloadDigest.digestString != null + && record.computedPayloadDigest.digestString.length() > 0) { + computedPayloadDigest = record.computedPayloadDigest.digestString; + } + if (record.computedPayloadDigest.algorithm != null + && record.computedPayloadDigest.algorithm.length() > 0) { + computedPayloadDigestAlgorithm = record.computedPayloadDigest.algorithm; + } + if (record.computedPayloadDigest.encoding != null + && record.computedPayloadDigest.encoding.length() > 0) { + computedPayloadDigestEncoding = record.computedPayloadDigest.encoding; + } + } + /* + * Record-Id scheme. + */ + if (warcRecordId != null) { + int idx = warcRecordId.indexOf(':'); + if (idx >= 0) { + if (warcRecordId.startsWith("<")) { + recordIdScheme = warcRecordId.substring(1, idx); + } else { + recordIdScheme = warcRecordId.substring(0, idx); } - /* - * IpVersion, common for several record properties. - */ - if (header.warcInetAddress != null) { - if (header.warcInetAddress.getAddress().length == 4) { - ipVersion = "4"; + } + } + /* + * Compliance. + */ + bIsNonCompliant = !record.isCompliant(); + isValidBlockDigest = record.isValidBlockDigest; + isValidPayloadDigest = record.isValidPayloadDigest; + /* + * Payload. + */ + bHasPayload = record.hasPayload(); + Payload payload = record.getPayload(); + HeaderLine headerLine; + if (payload != null) { + PayloadWithHeaderAbstract payloadHeaderWrapped = payload.getPayloadHeaderWrapped(); + HttpHeader httpHeader = null; + if (payloadHeaderWrapped instanceof HttpHeader) { + httpHeader = (HttpHeader) payloadHeaderWrapped; + } + if (httpHeader != null) { + payloadLength = Long.toString(httpHeader.getPayloadLength()); + protocolVersion = httpHeader.httpVersion; + switch (httpHeader.headerType) { + case HttpHeader.HT_RESPONSE: + resultCode = httpHeader.statusCodeStr; + protocolContentType = httpHeader.contentType; + headerLine = httpHeader.getHeader("server"); + if (headerLine != null && headerLine.value != null) { + protocolServer = headerLine.value; } - else { - ipVersion = "6"; + break; + case HttpHeader.HT_REQUEST: + headerLine = httpHeader.getHeader("user-agent"); + if (headerLine != null && headerLine.value != null) { + protocolUserAgent = headerLine.value; } + break; + default: + break; } + } else { + payloadLength = Long.toString(payload.getTotalLength()); + } + } + /* + * IpVersion, common for several record properties. + */ + if (header.warcInetAddress != null) { + if (header.warcInetAddress.getAddress().length == 4) { + ipVersion = "4"; + } else { + ipVersion = "6"; + } } + } } diff --git a/jhove-ext-modules/src/main/java/edu/harvard/hul/ois/jhove/module/warc/WarcRecordProperties.java b/jhove-ext-modules/src/main/java/edu/harvard/hul/ois/jhove/module/warc/WarcRecordProperties.java index 79d5a9989..2f1663568 100644 --- a/jhove-ext-modules/src/main/java/edu/harvard/hul/ois/jhove/module/warc/WarcRecordProperties.java +++ b/jhove-ext-modules/src/main/java/edu/harvard/hul/ois/jhove/module/warc/WarcRecordProperties.java @@ -3,242 +3,227 @@ import java.util.LinkedHashMap; import java.util.List; import java.util.Map; - import org.jwat.warc.WarcConstants; import org.jwat.warc.WarcRecord; /** * Retrieves the WARC record data and delivers its properties as a map. - * - * Based on the WARC property classes from JHOVE2. - * - * @author jolf * + *

Based on the WARC property classes from JHOVE2. + * + * @author jolf */ public class WarcRecordProperties { - /** The WARC record data.*/ - private final WarcRecordData data; - /** The map for the properties of the WARC record.*/ - private Map properties; - - /** - * Constructor. - * @param record The record to extract the properties from. - */ - public WarcRecordProperties(WarcRecord record) { - data = new WarcRecordData(record); - } - - private final static String DATE_HEADER= "Warc-Date header value."; - private final static String TARGET_URI_HEADER = "Warc-Target-URI header value."; - private final static String WARCINFO_ID_HEADER= "Warc-Warcinfo-ID header value."; - private final static String REFERS_TO_HEADER= "Warc-Refers-To header value."; - private final static String CONCURRENT_TO_HEADER= "Warc-Concurrent-To header value."; - private final static String IP_ADDRESS_HEADER= "Warc-IP-Address header value."; - private final static String IP_ADDRESS_VERSION= "Ip-Address version."; - private final static String PROTOCOL_VERSION_HEADER="ProtocolVersion header value."; - - /** - * Retrieves the WARC record properties as a map. - * Starts by extracting the base properties for all WARC records, - * then adds the specific properties for the given type of WARC record. - * @return A map of the properties of the WARC record. - */ - public Map getProperties() { - properties = new LinkedHashMap<>(); - - setBaseProperties(); - - // Would be better, if we could use java 1.7 string switch... - if(data.warcType == null) { - // Ignore. - } else if(data.warcType.equalsIgnoreCase(WarcConstants.RT_CONTINUATION)) { - setContinuationRecordProperties(); - } else if(data.warcType.equalsIgnoreCase(WarcConstants.RT_CONVERSION)) { - setConversionRecordProperties(); - } else if(data.warcType.equalsIgnoreCase(WarcConstants.RT_METADATA)) { - setMetadataRecordProperties(); - } else if(data.warcType.equalsIgnoreCase(WarcConstants.RT_REQUEST)) { - setRequestRecordProperties(); - } else if(data.warcType.equalsIgnoreCase(WarcConstants.RT_RESOURCE)) { - setResourceRecordProperties(); - } else if(data.warcType.equalsIgnoreCase(WarcConstants.RT_RESPONSE)) { - setResponseRecordProperties(); - } else if(data.warcType.equalsIgnoreCase(WarcConstants.RT_REVISIT)) { - setRevisitRecordProperties(); - } else if(data.warcType.equalsIgnoreCase(WarcConstants.RT_WARCINFO)) { - setWarcInfoRecordProperties(); - } - - return properties; - } - - /** - * Set the base properties for all WARC records. - */ - private void setBaseProperties() { - setProperty(data.startOffset, "Record offset in WARC file."); - setProperty(data.warcVersionStr, DATE_HEADER); - setProperty(data.warcDate, DATE_HEADER); - setProperty(data.warcRecordId, "Warc-Record-ID header value."); - setProperty(data.recordIdScheme, "Record-ID-Scheme value."); - setProperty(data.contentType, "Content-Type header value."); - setProperty(data.contentLength, "Content-Length header value."); - setProperty(data.warcType, "Warc-Type header value."); - setProperty(data.warcBlockDigest, "Warc-Block-Digest header value."); - setProperty(data.warcBlockDigestAlgorithm, "Block-Digest-Algorithm value."); - - setProperty(data.warcBlockDigestEncoding, "Block-Digest-Encoding value."); - setProperty(data.isValidBlockDigest, "isValidBlockDigest boolean value."); - setProperty(data.warcPayloadDigest, "Warc-Payload-Digest header value."); - setProperty(data.warcPayloadDigestAlgorithm, "Payload-Digest-Algorithm value."); - setProperty(data.warcPayloadDigestEncoding, "Payload-Digest-Encoding value."); - setProperty(data.isValidPayloadDigest, "isValidPayloadDigest boolean value."); - setProperty(data.warcTruncated, "Warc-Truncated header value."); - setProperty(data.bHasPayload, "hasPayload value."); - setProperty(data.payloadLength, "PayloadLength value."); - setProperty(data.warcIdentifiedPayloadType, "Warc-Identified-Payload-Type header value."); - - setProperty(data.warcSegmentNumber, "Warc-Segment-Number header value."); - setProperty(data.bIsNonCompliant, "isNonCompliant value."); - setProperty(data.computedBlockDigest, "Computed Block-Digest header value."); - setProperty(data.computedBlockDigestAlgorithm, "Computed Block-Digest-Algorithm value."); - setProperty(data.computedBlockDigestEncoding, "Computed Block-Digest-Encoding value."); - setProperty(data.computedPayloadDigest, "Computed Payload-Digest header value."); - setProperty(data.computedPayloadDigestAlgorithm, "Computed Payload-Digest-Algorithm value."); - setProperty(data.computedPayloadDigestEncoding, "Computed Payload-Digest-Encoding value."); - } - - /** - * Set the properties for the Continuation WARC record. - */ - private void setContinuationRecordProperties() { - setProperty(data.warcTargetUri, TARGET_URI_HEADER); - setProperty(data.warcSegmentOriginId, "Warc-Segment-Origin-ID header value."); - setProperty(data.warcSegmentTotalLength, "Warc-Segment-Total-Length header value."); - setProperty(data.warcWarcinfoId, WARCINFO_ID_HEADER); - } - - /** - * Set the properties for the Conversion WARC record. - */ - private void setConversionRecordProperties() { - setProperty(data.warcTargetUri, TARGET_URI_HEADER); - setProperty(data.warcRefersTo, REFERS_TO_HEADER); - setProperty(data.warcWarcinfoId, WARCINFO_ID_HEADER); - } - - /** - * Set the properties for the Metadata WARC record. - */ - private void setMetadataRecordProperties() { - setProperty(data.warcTargetUri, TARGET_URI_HEADER); - setProperty(data.warcConcurrentToList, CONCURRENT_TO_HEADER); - setProperty(data.warcRefersTo, REFERS_TO_HEADER); - setProperty(data.warcIpAddress, IP_ADDRESS_HEADER); - setProperty(data.ipVersion, IP_ADDRESS_VERSION); - setProperty(data.warcWarcinfoId, WARCINFO_ID_HEADER); - } - - /** - * Set the properties for the Request WARC record. - */ - private void setRequestRecordProperties() { - setProperty(data.warcTargetUri, TARGET_URI_HEADER); - setProperty(data.warcConcurrentToList, CONCURRENT_TO_HEADER); - setProperty(data.warcIpAddress, IP_ADDRESS_HEADER); - setProperty(data.ipVersion, IP_ADDRESS_VERSION); - setProperty(data.warcWarcinfoId, WARCINFO_ID_HEADER); - setProperty(data.protocolVersion, PROTOCOL_VERSION_HEADER); - setProperty(data.protocolUserAgent, "ProtocolUserAgent header value."); - } - - /** - * Set the properties for the Resource WARC record. - */ - private void setResourceRecordProperties() { - setProperty(data.warcTargetUri, TARGET_URI_HEADER); - setProperty(data.warcConcurrentToList, CONCURRENT_TO_HEADER); - setProperty(data.warcIpAddress, IP_ADDRESS_HEADER); - setProperty(data.ipVersion, IP_ADDRESS_VERSION); - setProperty(data.warcWarcinfoId, WARCINFO_ID_HEADER); - } + /** The WARC record data. */ + private final WarcRecordData data; + /** The map for the properties of the WARC record. */ + private Map properties; - /** - * Set the properties for the Response WARC record. - */ - private void setResponseRecordProperties() { - setProperty(data.warcTargetUri, TARGET_URI_HEADER); - setProperty(data.warcConcurrentToList, CONCURRENT_TO_HEADER); - setProperty(data.warcIpAddress, IP_ADDRESS_HEADER); - setProperty(data.ipVersion, IP_ADDRESS_VERSION); - setProperty(data.warcWarcinfoId, WARCINFO_ID_HEADER); - setProperty(data.resultCode, "ProtocolResultCode header value."); - setProperty(data.protocolVersion, PROTOCOL_VERSION_HEADER); - setProperty(data.protocolContentType, "ProtocolContentType header value."); - setProperty(data.protocolServer, "ServerName header value."); - } + /** + * Constructor. + * + * @param record The record to extract the properties from. + */ + public WarcRecordProperties(WarcRecord record) { + data = new WarcRecordData(record); + } - /** - * Set the properties for the Revisit WARC record. - */ - private void setRevisitRecordProperties() { - setProperty(data.warcTargetUri, TARGET_URI_HEADER); - setProperty(data.warcProfile, "Warc-Profile header value."); - setProperty(data.warcRefersTo, REFERS_TO_HEADER); - setProperty(data.warcIpAddress, IP_ADDRESS_HEADER); - setProperty(data.ipVersion, IP_ADDRESS_VERSION); - setProperty(data.warcWarcinfoId, WARCINFO_ID_HEADER); - } - - /** - * Set the properties for the WarcInfo WARC record. - */ - private void setWarcInfoRecordProperties() { - setProperty(data.warcFilename, "WarcFilename header value."); + private static final String DATE_HEADER = "Warc-Date header value."; + private static final String TARGET_URI_HEADER = "Warc-Target-URI header value."; + private static final String WARCINFO_ID_HEADER = "Warc-Warcinfo-ID header value."; + private static final String REFERS_TO_HEADER = "Warc-Refers-To header value."; + private static final String CONCURRENT_TO_HEADER = "Warc-Concurrent-To header value."; + private static final String IP_ADDRESS_HEADER = "Warc-IP-Address header value."; + private static final String IP_ADDRESS_VERSION = "Ip-Address version."; + private static final String PROTOCOL_VERSION_HEADER = "ProtocolVersion header value."; + + /** + * Retrieves the WARC record properties as a map. Starts by extracting the base properties for all + * WARC records, then adds the specific properties for the given type of WARC record. + * + * @return A map of the properties of the WARC record. + */ + public Map getProperties() { + properties = new LinkedHashMap<>(); + + setBaseProperties(); + + // Would be better, if we could use java 1.7 string switch... + if (data.warcType == null) { + // Ignore. + } else if (data.warcType.equalsIgnoreCase(WarcConstants.RT_CONTINUATION)) { + setContinuationRecordProperties(); + } else if (data.warcType.equalsIgnoreCase(WarcConstants.RT_CONVERSION)) { + setConversionRecordProperties(); + } else if (data.warcType.equalsIgnoreCase(WarcConstants.RT_METADATA)) { + setMetadataRecordProperties(); + } else if (data.warcType.equalsIgnoreCase(WarcConstants.RT_REQUEST)) { + setRequestRecordProperties(); + } else if (data.warcType.equalsIgnoreCase(WarcConstants.RT_RESOURCE)) { + setResourceRecordProperties(); + } else if (data.warcType.equalsIgnoreCase(WarcConstants.RT_RESPONSE)) { + setResponseRecordProperties(); + } else if (data.warcType.equalsIgnoreCase(WarcConstants.RT_REVISIT)) { + setRevisitRecordProperties(); + } else if (data.warcType.equalsIgnoreCase(WarcConstants.RT_WARCINFO)) { + setWarcInfoRecordProperties(); } - - /** - * Sets the given string property, if it has a valid value (not null and not empty). - * @param variable The value for the property. - * @param description The description and key of the property. - */ - private void setProperty(String variable, String description) { - if(variable != null && !variable.isEmpty()) { - properties.put(description, variable); - } + + return properties; + } + + /** Set the base properties for all WARC records. */ + private void setBaseProperties() { + setProperty(data.startOffset, "Record offset in WARC file."); + setProperty(data.warcVersionStr, DATE_HEADER); + setProperty(data.warcDate, DATE_HEADER); + setProperty(data.warcRecordId, "Warc-Record-ID header value."); + setProperty(data.recordIdScheme, "Record-ID-Scheme value."); + setProperty(data.contentType, "Content-Type header value."); + setProperty(data.contentLength, "Content-Length header value."); + setProperty(data.warcType, "Warc-Type header value."); + setProperty(data.warcBlockDigest, "Warc-Block-Digest header value."); + setProperty(data.warcBlockDigestAlgorithm, "Block-Digest-Algorithm value."); + + setProperty(data.warcBlockDigestEncoding, "Block-Digest-Encoding value."); + setProperty(data.isValidBlockDigest, "isValidBlockDigest boolean value."); + setProperty(data.warcPayloadDigest, "Warc-Payload-Digest header value."); + setProperty(data.warcPayloadDigestAlgorithm, "Payload-Digest-Algorithm value."); + setProperty(data.warcPayloadDigestEncoding, "Payload-Digest-Encoding value."); + setProperty(data.isValidPayloadDigest, "isValidPayloadDigest boolean value."); + setProperty(data.warcTruncated, "Warc-Truncated header value."); + setProperty(data.bHasPayload, "hasPayload value."); + setProperty(data.payloadLength, "PayloadLength value."); + setProperty(data.warcIdentifiedPayloadType, "Warc-Identified-Payload-Type header value."); + + setProperty(data.warcSegmentNumber, "Warc-Segment-Number header value."); + setProperty(data.bIsNonCompliant, "isNonCompliant value."); + setProperty(data.computedBlockDigest, "Computed Block-Digest header value."); + setProperty(data.computedBlockDigestAlgorithm, "Computed Block-Digest-Algorithm value."); + setProperty(data.computedBlockDigestEncoding, "Computed Block-Digest-Encoding value."); + setProperty(data.computedPayloadDigest, "Computed Payload-Digest header value."); + setProperty(data.computedPayloadDigestAlgorithm, "Computed Payload-Digest-Algorithm value."); + setProperty(data.computedPayloadDigestEncoding, "Computed Payload-Digest-Encoding value."); + } + + /** Set the properties for the Continuation WARC record. */ + private void setContinuationRecordProperties() { + setProperty(data.warcTargetUri, TARGET_URI_HEADER); + setProperty(data.warcSegmentOriginId, "Warc-Segment-Origin-ID header value."); + setProperty(data.warcSegmentTotalLength, "Warc-Segment-Total-Length header value."); + setProperty(data.warcWarcinfoId, WARCINFO_ID_HEADER); + } + + /** Set the properties for the Conversion WARC record. */ + private void setConversionRecordProperties() { + setProperty(data.warcTargetUri, TARGET_URI_HEADER); + setProperty(data.warcRefersTo, REFERS_TO_HEADER); + setProperty(data.warcWarcinfoId, WARCINFO_ID_HEADER); + } + + /** Set the properties for the Metadata WARC record. */ + private void setMetadataRecordProperties() { + setProperty(data.warcTargetUri, TARGET_URI_HEADER); + setProperty(data.warcConcurrentToList, CONCURRENT_TO_HEADER); + setProperty(data.warcRefersTo, REFERS_TO_HEADER); + setProperty(data.warcIpAddress, IP_ADDRESS_HEADER); + setProperty(data.ipVersion, IP_ADDRESS_VERSION); + setProperty(data.warcWarcinfoId, WARCINFO_ID_HEADER); + } + + /** Set the properties for the Request WARC record. */ + private void setRequestRecordProperties() { + setProperty(data.warcTargetUri, TARGET_URI_HEADER); + setProperty(data.warcConcurrentToList, CONCURRENT_TO_HEADER); + setProperty(data.warcIpAddress, IP_ADDRESS_HEADER); + setProperty(data.ipVersion, IP_ADDRESS_VERSION); + setProperty(data.warcWarcinfoId, WARCINFO_ID_HEADER); + setProperty(data.protocolVersion, PROTOCOL_VERSION_HEADER); + setProperty(data.protocolUserAgent, "ProtocolUserAgent header value."); + } + + /** Set the properties for the Resource WARC record. */ + private void setResourceRecordProperties() { + setProperty(data.warcTargetUri, TARGET_URI_HEADER); + setProperty(data.warcConcurrentToList, CONCURRENT_TO_HEADER); + setProperty(data.warcIpAddress, IP_ADDRESS_HEADER); + setProperty(data.ipVersion, IP_ADDRESS_VERSION); + setProperty(data.warcWarcinfoId, WARCINFO_ID_HEADER); + } + + /** Set the properties for the Response WARC record. */ + private void setResponseRecordProperties() { + setProperty(data.warcTargetUri, TARGET_URI_HEADER); + setProperty(data.warcConcurrentToList, CONCURRENT_TO_HEADER); + setProperty(data.warcIpAddress, IP_ADDRESS_HEADER); + setProperty(data.ipVersion, IP_ADDRESS_VERSION); + setProperty(data.warcWarcinfoId, WARCINFO_ID_HEADER); + setProperty(data.resultCode, "ProtocolResultCode header value."); + setProperty(data.protocolVersion, PROTOCOL_VERSION_HEADER); + setProperty(data.protocolContentType, "ProtocolContentType header value."); + setProperty(data.protocolServer, "ServerName header value."); + } + + /** Set the properties for the Revisit WARC record. */ + private void setRevisitRecordProperties() { + setProperty(data.warcTargetUri, TARGET_URI_HEADER); + setProperty(data.warcProfile, "Warc-Profile header value."); + setProperty(data.warcRefersTo, REFERS_TO_HEADER); + setProperty(data.warcIpAddress, IP_ADDRESS_HEADER); + setProperty(data.ipVersion, IP_ADDRESS_VERSION); + setProperty(data.warcWarcinfoId, WARCINFO_ID_HEADER); + } + + /** Set the properties for the WarcInfo WARC record. */ + private void setWarcInfoRecordProperties() { + setProperty(data.warcFilename, "WarcFilename header value."); + } + + /** + * Sets the given string property, if it has a valid value (not null and not empty). + * + * @param variable The value for the property. + * @param description The description and key of the property. + */ + private void setProperty(String variable, String description) { + if (variable != null && !variable.isEmpty()) { + properties.put(description, variable); } + } - /** - * Sets the given boolean property, if it has a valid value (not null). - * @param variable The value for the property. - * @param description The description and key of the property. - */ - private void setProperty(Boolean variable, String description) { - if(variable != null) { - properties.put(description, variable.toString()); - } + /** + * Sets the given boolean property, if it has a valid value (not null). + * + * @param variable The value for the property. + * @param description The description and key of the property. + */ + private void setProperty(Boolean variable, String description) { + if (variable != null) { + properties.put(description, variable.toString()); } + } - /** - * Sets the given string-list property, if it has a valid value (not null and not empty). - * @param variable The value for the property. - * @param description The description and key of the property. - */ - private void setProperty(List variable, String description) { - if(variable != null && !variable.isEmpty()) { - properties.put(description, variable.toString()); - } + /** + * Sets the given string-list property, if it has a valid value (not null and not empty). + * + * @param variable The value for the property. + * @param description The description and key of the property. + */ + private void setProperty(List variable, String description) { + if (variable != null && !variable.isEmpty()) { + properties.put(description, variable.toString()); } + } - /** - * Sets the given long property, if it has a valid value (not null and not empty). - * @param variable The value for the property. - * @param description The description and key of the property. - */ - private void setProperty(Long variable, String description) { - if(variable != null) { - properties.put(description, variable.toString()); - } + /** + * Sets the given long property, if it has a valid value (not null and not empty). + * + * @param variable The value for the property. + * @param description The description and key of the property. + */ + private void setProperty(Long variable, String description) { + if (variable != null) { + properties.put(description, variable.toString()); } + } } diff --git a/jhove-ext-modules/src/main/java/edu/harvard/hul/ois/jhove/module/warc/package-info.java b/jhove-ext-modules/src/main/java/edu/harvard/hul/ois/jhove/module/warc/package-info.java index c918dcb08..6435fb9c2 100644 --- a/jhove-ext-modules/src/main/java/edu/harvard/hul/ois/jhove/module/warc/package-info.java +++ b/jhove-ext-modules/src/main/java/edu/harvard/hul/ois/jhove/module/warc/package-info.java @@ -1,13 +1,12 @@ /** - * Contains the classes needed for building a JHOVE application. - * This package must be used with a top-level class, with one or more - * output handlers or viewers, and with one or more modules for specific - * file formats. - *

- * For overviews, tutorials, examples, guides, and tool documentation, - * please see: + * Contains the classes needed for building a JHOVE application. This package must be used with a + * top-level class, with one or more output handlers or viewers, and with one or more modules for + * specific file formats. + * + *

For overviews, tutorials, examples, guides, and tool documentation, please see: + * *

*/ package hul.ois.jhove.module.warc; diff --git a/jhove-ext-modules/src/main/java/org/ithaka/portico/jhove/module/EpubModule.java b/jhove-ext-modules/src/main/java/org/ithaka/portico/jhove/module/EpubModule.java index 942ee6650..239ded5d1 100644 --- a/jhove-ext-modules/src/main/java/org/ithaka/portico/jhove/module/EpubModule.java +++ b/jhove-ext-modules/src/main/java/org/ithaka/portico/jhove/module/EpubModule.java @@ -21,30 +21,11 @@ import static org.ithaka.portico.jhove.module.epub.ReportPropertyNames.PROPNAME_SUBJECTS; import static org.ithaka.portico.jhove.module.epub.ReportPropertyNames.PROPNAME_TITLE; -import java.io.File; -import java.io.IOException; -import java.io.InputStream; -import java.io.RandomAccessFile; -import java.nio.charset.StandardCharsets; -import java.util.ArrayList; -import java.util.Collections; -import java.util.Comparator; -import java.util.LinkedList; -import java.util.List; -import java.util.Set; -import java.util.TreeSet; -import java.util.stream.Collectors; - -import org.ithaka.portico.jhove.module.epub.JhoveRepInfoReport; -import org.ithaka.portico.jhove.module.epub.MessageConstants; -import org.jwat.common.RandomAccessFileInputStream; - import com.adobe.epubcheck.api.EPUBLocation; import com.adobe.epubcheck.api.EpubCheck; import com.adobe.epubcheck.messages.Severity; import com.adobe.epubcheck.reporting.CheckMessage; import com.adobe.epubcheck.util.PathUtil; - import edu.harvard.hul.ois.jhove.Agent; import edu.harvard.hul.ois.jhove.Agent.Builder; import edu.harvard.hul.ois.jhove.AgentType; @@ -67,547 +48,609 @@ import edu.harvard.hul.ois.jhove.SignatureUseType; import edu.harvard.hul.ois.jhove.messages.JhoveMessage; import edu.harvard.hul.ois.jhove.messages.JhoveMessages; +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.io.RandomAccessFile; +import java.nio.charset.StandardCharsets; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.LinkedList; +import java.util.List; +import java.util.Set; +import java.util.TreeSet; +import java.util.stream.Collectors; +import org.ithaka.portico.jhove.module.epub.JhoveRepInfoReport; +import org.ithaka.portico.jhove.module.epub.MessageConstants; +import org.jwat.common.RandomAccessFileInputStream; /** - * Module for validation and metadata extraction on EPUB files. Validation is - * performed by EPUBCheck https://github.com/w3c/epubcheck/ This module uses the - * metadata produced by EPUBCheck to produce a JHOVE report. + * Module for validation and metadata extraction on EPUB files. Validation is performed by EPUBCheck + * https://github.com/w3c/epubcheck/ This module uses the metadata produced by EPUBCheck to produce + * a JHOVE report. * * @author Karen Hanson - * */ public class EpubModule extends ModuleBase { - private static final String EPUB_MEDIATYPE = "application/epub+zip"; - private static final String FORMATNAME = "EPUB"; - - private static final String NAME = "EPUB-ptc"; - private static final String RELEASE = "1.0"; - private static final int[] DATE = {2019, 5, 15}; - private static final String RIGHTS_YEAR = "2019"; - private static final String[] FORMAT = { FORMATNAME }; - private static final String COVERAGE = FORMATNAME; - private static final String[] MIMETYPE = { EPUB_MEDIATYPE }; - private static final String WELLFORMED = ""; - private static final String VALIDITY = ""; - private static final String REPINFO = ""; - private static final String NOTE = "This module uses EPUBCheck for testing of EPUB files."; - - //EPUB agent information - private static final String EPUB_AGENTNAME = "International Digital Publishing Forum"; - private static final AgentType EPUB_AGENTTYPE = AgentType.STANDARD; - private static final String EPUB_AGENTADDRESS = "International Digital Publishing Forum (IDPF), " - + "113 Cherry Street, Suite 70-719, Seattle, WA 98104"; - private static final String EPUB_AGENTWEBSITE = "http://idpf.org"; - private static final String EPUB_AGENTEMAIL = "membership@idpf.org"; - private static final String EPUB_AGENTPHONE = "+1-206-451-7250"; - - //EPUB format doc information - private static final String EPUB_FORMATDOCTITLE = FORMATNAME; - private static final String EPUB_FORMATDOCDATE = "2019-05-15"; - private static final String EPUB_FORMATDOCURL = "http://www.idpf.org/epub/dir/"; - - //Signatures - private static final String EPUB_EXTENSION = ".epub"; - private static final int FIRST_SIG_POSITION = 0; - private static final String FIRST_SIG_VALUE = "PK"; - private static final int SECOND_SIG_POSITION = 30; - private static final String SECOND_SIG_VALUE = "mimetype"; - private static final int THIRD_SIG_POSITION = 38; - private static final String THIRD_SIG_VALUE = EPUB_MEDIATYPE; - - /* - * FATAL errors automatically set the status to not-well-formed. Non-fatal - * "ERROR"s are more of a mixed bag. Some relate to e.g. XHTML or CSS validity - * but others are issues with e.g. package structure. Here you can configure - * error codes or error code prefixes that will indicate that a file is - * not-well-formed. For example, any package-related ERROR will indicate the - * EPUB is not well formed. - */ - public static final String[] NOTWELLFORMED_ERRCODES = new String[] { "PKG-" }; - - /* Top-level property list */ - protected List _propList; - - /* Top-level metadata property */ - protected Property _metadata; - - /** - * **************************************************************** - * CLASS CONSTRUCTOR. - * **************************************************************** - */ - /** - * Instantiate a EpubModule object using default properties - */ - public EpubModule() { - super(NAME, RELEASE, DATE, FORMAT, COVERAGE, MIMETYPE, WELLFORMED, - VALIDITY, REPINFO, NOTE, PorticoConstants.porticoRightsStmt(RIGHTS_YEAR), false); - - initializeInstance(PorticoConstants.porticoAgent()); + private static final String EPUB_MEDIATYPE = "application/epub+zip"; + private static final String FORMATNAME = "EPUB"; + + private static final String NAME = "EPUB-ptc"; + private static final String RELEASE = "1.0"; + private static final int[] DATE = {2019, 5, 15}; + private static final String RIGHTS_YEAR = "2019"; + private static final String[] FORMAT = {FORMATNAME}; + private static final String COVERAGE = FORMATNAME; + private static final String[] MIMETYPE = {EPUB_MEDIATYPE}; + private static final String WELLFORMED = ""; + private static final String VALIDITY = ""; + private static final String REPINFO = ""; + private static final String NOTE = "This module uses EPUBCheck for testing of EPUB files."; + + // EPUB agent information + private static final String EPUB_AGENTNAME = "International Digital Publishing Forum"; + private static final AgentType EPUB_AGENTTYPE = AgentType.STANDARD; + private static final String EPUB_AGENTADDRESS = + "International Digital Publishing Forum (IDPF), " + + "113 Cherry Street, Suite 70-719, Seattle, WA 98104"; + private static final String EPUB_AGENTWEBSITE = "http://idpf.org"; + private static final String EPUB_AGENTEMAIL = "membership@idpf.org"; + private static final String EPUB_AGENTPHONE = "+1-206-451-7250"; + + // EPUB format doc information + private static final String EPUB_FORMATDOCTITLE = FORMATNAME; + private static final String EPUB_FORMATDOCDATE = "2019-05-15"; + private static final String EPUB_FORMATDOCURL = "http://www.idpf.org/epub/dir/"; + + // Signatures + private static final String EPUB_EXTENSION = ".epub"; + private static final int FIRST_SIG_POSITION = 0; + private static final String FIRST_SIG_VALUE = "PK"; + private static final int SECOND_SIG_POSITION = 30; + private static final String SECOND_SIG_VALUE = "mimetype"; + private static final int THIRD_SIG_POSITION = 38; + private static final String THIRD_SIG_VALUE = EPUB_MEDIATYPE; + + /* + * FATAL errors automatically set the status to not-well-formed. Non-fatal + * "ERROR"s are more of a mixed bag. Some relate to e.g. XHTML or CSS validity + * but others are issues with e.g. package structure. Here you can configure + * error codes or error code prefixes that will indicate that a file is + * not-well-formed. For example, any package-related ERROR will indicate the + * EPUB is not well formed. + */ + public static final String[] NOTWELLFORMED_ERRCODES = new String[] {"PKG-"}; + + /* Top-level property list */ + protected List _propList; + + /* Top-level metadata property */ + protected Property _metadata; + + /** + * **************************************************************** CLASS CONSTRUCTOR. + * **************************************************************** + */ + /** Instantiate a EpubModule object using default properties */ + public EpubModule() { + super( + NAME, + RELEASE, + DATE, + FORMAT, + COVERAGE, + MIMETYPE, + WELLFORMED, + VALIDITY, + REPINFO, + NOTE, + PorticoConstants.porticoRightsStmt(RIGHTS_YEAR), + false); + + initializeInstance(PorticoConstants.porticoAgent()); + } + + /** + * Instantiate a EpubModule object using constructor arguments + * + * @param name + * @param release + * @param date + * @param format + * @param coverage + * @param mimetype + * @param wellformedNote + * @param validityNote + * @param repinfoNote + * @param note + * @param rights + * @param isRandomAccess + * @param moduleAgent + */ + public EpubModule( + String name, + String release, + int[] date, + String[] format, + String coverage, + String[] mimetype, + String wellformedNote, + String validityNote, + String repinfoNote, + String note, + String rights, + boolean isRandomAccess, + Agent moduleAgent) { + super( + name, + release, + date, + format, + coverage, + mimetype, + wellformedNote, + validityNote, + repinfoNote, + note, + rights, + isRandomAccess); + initializeInstance(moduleAgent); + } + + /** + * Initialize core properties - module {@link Agent}, format specification {@link Document}, and + * format {@link Signature} + * + * @param agentName + * @param agentType + * @param agentAddress + * @param agentTelephone + * @param agentEmail + */ + protected void initializeInstance(Agent moduleAgent) { + + _vendor = moduleAgent; + + Agent formatDocAgent = + new Builder(EPUB_AGENTNAME, EPUB_AGENTTYPE) + .address(EPUB_AGENTADDRESS) + .telephone(EPUB_AGENTPHONE) + .web(EPUB_AGENTWEBSITE) + .email(EPUB_AGENTEMAIL) + .build(); + + Document doc = new Document(EPUB_FORMATDOCTITLE, DocumentType.STANDARD); + doc.setPublisher(formatDocAgent); + doc.setDate(EPUB_FORMATDOCDATE); + doc.setIdentifier(new Identifier(EPUB_FORMATDOCURL, IdentifierType.URL)); + _specification.add(doc); + + Signature sig = + new ExternalSignature(EPUB_EXTENSION, SignatureType.EXTENSION, SignatureUseType.OPTIONAL); + _signature.add(sig); + + // Signature matching based on: + // https://www.loc.gov/preservation/digital/formats/fdd/fdd000308.shtml#sign + // and https://www.loc.gov/preservation/digital/formats/fdd/fdd000278.shtml#sign + + // this first one will also match other kinds of zip files + sig = + new InternalSignature( + FIRST_SIG_VALUE, + SignatureType.MAGIC, + SignatureUseType.MANDATORY, + FIRST_SIG_POSITION, + ""); + _signature.add(sig); + + // "mimetype" at 30 + sig = + new InternalSignature( + SECOND_SIG_VALUE, + SignatureType.MAGIC, + SignatureUseType.MANDATORY, + SECOND_SIG_POSITION, + ""); + _signature.add(sig); + + // this will also match other kinds of zip files + sig = + new InternalSignature( + THIRD_SIG_VALUE, + SignatureType.MAGIC, + SignatureUseType.MANDATORY, + THIRD_SIG_POSITION, + ""); + _signature.add(sig); + } + + // overriding to handle when _je is null, which is not handled in ModuleBase + @Override + protected void setupDataStream(final InputStream stream, final RepInfo info) { + if (_je != null) { + super.setupDataStream(stream, info); + } else { + _dstream = getBufferedDataStream(stream, 0); } + } + + @Override + public void checkSignatures(File file, InputStream stream, RepInfo info) throws IOException { + super.checkSignatures(file, stream, info); + // validity is not determined in this signature check, set to undetermined. + info.setValid(RepInfo.UNDETERMINED); + } + + /** + * Parse the content of a purported EPUB file and store the results in RepInfo. + * + * @param stream An InputStream, positioned at its beginning, which is generated from the object + * to be parsed. If multiple calls to parse are made on the basis of a nonzero + * value being returned, a new InputStream must be provided each time. + * @param info A fresh (on the first call) RepInfo object which will be modified to reflect the + * results of the parsing If multiple calls to parse are made on the basis of a + * nonzero value being returned, the same RepInfo object should be passed with each call. + * @param parseIndex Must be 0 in first call to parse. If parse returns + * a nonzero value, it must be called again with parseIndex equal to that return + * value. + * @return + * @throws java.io.IOException + */ + @Override + public int parse(InputStream stream, RepInfo info, int parseIndex) throws IOException { + + initParse(); + + info.setModule(this); + info.setFormat(_format[0]); + info.setWellFormed(false); + info.setValid(false); + + _propList = new LinkedList<>(); + _metadata = + new Property(PROPNAME_EPUB_METADATA, PropertyType.PROPERTY, PropertyArity.LIST, _propList); + + // loads stream to _dstream so it can be checksummed when flag enabled + _ckSummer = null; + setupDataStream(stream, info); + + // Call tool and calculate stats + try { + JhoveRepInfoReport report = new JhoveRepInfoReport(info.getUri()); + EpubCheck epubCheck = new EpubCheck(_dstream, report, info.getUri()); + epubCheck.doValidate(); + + info.setCreated(report.getCreationDate()); + info.setLastModified(report.getLastModifiedDate()); + info.setVersion(report.getVersion()); + + List epubMessages = report.getAllMessages(); + + // check if any of the messages are on the customized not-well-formed + // list of errors + int notWellFormedErrors = + epubMessages.stream() + .filter(c -> triggerNotWellFormed(c)) + .collect(Collectors.toSet()) + .size(); + + info.setWellFormed(report.getFatalErrorCount() == 0 && notWellFormedErrors == 0); + info.setValid(info.getWellFormed() == RepInfo.TRUE && report.getErrorCount() == 0); + + Set msgs = new TreeSet(new MessageComparator()); + for (CheckMessage msg : report.getAllMessages()) { + msgs.addAll(toJhoveMessages(msg)); + } + msgs.forEach(jhoveMsg -> info.setMessage(jhoveMsg)); - /** - * Instantiate a EpubModule object using constructor arguments - * - * @param name - * @param release - * @param date - * @param format - * @param coverage - * @param mimetype - * @param wellformedNote - * @param validityNote - * @param repinfoNote - * @param note - * @param rights - * @param isRandomAccess - * @param moduleAgent - */ - public EpubModule(String name, String release, int[] date, - String[] format, String coverage, - String[] mimetype, String wellformedNote, - String validityNote, String repinfoNote, String note, - String rights, boolean isRandomAccess, Agent moduleAgent) { - super(name, release, date, format, coverage, mimetype, wellformedNote, validityNote, repinfoNote, - note, rights, isRandomAccess); - initializeInstance(moduleAgent); - } + info.setMimeType(report.getFormat()); - /** - * Initialize core properties - module {@link Agent}, format specification - * {@link Document}, and format {@link Signature} - * - * @param agentName - * @param agentType - * @param agentAddress - * @param agentTelephone - * @param agentEmail - */ - protected void initializeInstance(Agent moduleAgent) { - - _vendor = moduleAgent; - - Agent formatDocAgent = new Builder(EPUB_AGENTNAME, EPUB_AGENTTYPE) - .address(EPUB_AGENTADDRESS) - .telephone(EPUB_AGENTPHONE) - .web(EPUB_AGENTWEBSITE) - .email(EPUB_AGENTEMAIL) - .build(); - - Document doc = new Document(EPUB_FORMATDOCTITLE, DocumentType.STANDARD); - doc.setPublisher(formatDocAgent); - doc.setDate(EPUB_FORMATDOCDATE); - doc.setIdentifier(new Identifier(EPUB_FORMATDOCURL, IdentifierType.URL)); - _specification.add(doc); - - Signature sig = new ExternalSignature(EPUB_EXTENSION, SignatureType.EXTENSION, - SignatureUseType.OPTIONAL); - _signature.add(sig); - - // Signature matching based on: - // https://www.loc.gov/preservation/digital/formats/fdd/fdd000308.shtml#sign - // and https://www.loc.gov/preservation/digital/formats/fdd/fdd000278.shtml#sign - - // this first one will also match other kinds of zip files - sig = new InternalSignature(FIRST_SIG_VALUE, SignatureType.MAGIC, SignatureUseType.MANDATORY, FIRST_SIG_POSITION, ""); - _signature.add(sig); - - // "mimetype" at 30 - sig = new InternalSignature(SECOND_SIG_VALUE, SignatureType.MAGIC, SignatureUseType.MANDATORY, SECOND_SIG_POSITION, ""); - _signature.add(sig); - - // this will also match other kinds of zip files - sig = new InternalSignature(THIRD_SIG_VALUE, SignatureType.MAGIC, SignatureUseType.MANDATORY, THIRD_SIG_POSITION, ""); - _signature.add(sig); + generateProperties(report).forEach(prop -> _propList.add(prop)); + } catch (Exception f) { + f.printStackTrace(); + if (f.getMessage() != null) { + info.setMessage(new ErrorMessage(f.getMessage())); + } else { + info.setMessage(new ErrorMessage(MessageConstants.ERR_UNKNOWN)); + } + info.setWellFormed(false); // may not be the file's fault + return 0; } - // overriding to handle when _je is null, which is not handled in ModuleBase - @Override - protected void setupDataStream(final InputStream stream, final RepInfo info) { - if (_je != null) { - super.setupDataStream(stream, info); - } else { - _dstream = getBufferedDataStream(stream, 0); - } + // Check if user has aborted + if (_je != null && _je.getAbort()) { + return 0; } - @Override - public void checkSignatures(File file, InputStream stream, RepInfo info) throws IOException { - super.checkSignatures(file, stream, info); - // validity is not determined in this signature check, set to undetermined. - info.setValid(RepInfo.UNDETERMINED); - } + // We parsed it. Now assemble the properties. + info.setProperty(_metadata); - /** - * Parse the content of a purported EPUB file and store the results in RepInfo. - * - * @param stream An InputStream, positioned at its beginning, which is - * generated from the object to be parsed. If multiple calls - * to parse are made on the basis of a nonzero - * value being returned, a new InputStream must be provided - * each time. - * - * @param info A fresh (on the first call) RepInfo object which will be - * modified to reflect the results of the parsing If multiple - * calls to parse are made on the basis of a - * nonzero value being returned, the same RepInfo object - * should be passed with each call. - * - * @param parseIndex Must be 0 in first call to parse. If - * parse returns a nonzero value, it must be - * called again with parseIndex equal to that - * return value. - * @return - * @throws java.io.IOException - */ - @Override - public int parse(InputStream stream, RepInfo info, int parseIndex) throws IOException { - - initParse(); - - info.setModule(this); - info.setFormat(_format[0]); - info.setWellFormed(false); - info.setValid(false); - - _propList = new LinkedList<>(); - _metadata = new Property(PROPNAME_EPUB_METADATA, PropertyType.PROPERTY, PropertyArity.LIST, _propList); - - // loads stream to _dstream so it can be checksummed when flag enabled - _ckSummer = null; - setupDataStream(stream, info); - - //Call tool and calculate stats - try { - JhoveRepInfoReport report = new JhoveRepInfoReport(info.getUri()); - EpubCheck epubCheck = new EpubCheck(_dstream, report, info.getUri()); - epubCheck.doValidate(); - - info.setCreated(report.getCreationDate()); - info.setLastModified(report.getLastModifiedDate()); - info.setVersion(report.getVersion()); - - List epubMessages = report.getAllMessages(); - - // check if any of the messages are on the customized not-well-formed - // list of errors - int notWellFormedErrors = epubMessages.stream().filter(c -> triggerNotWellFormed(c)) - .collect(Collectors.toSet()).size(); - - info.setWellFormed(report.getFatalErrorCount() == 0 && notWellFormedErrors == 0); - info.setValid(info.getWellFormed() == RepInfo.TRUE && report.getErrorCount() == 0); - - Set msgs = new TreeSet(new MessageComparator()); - for (CheckMessage msg : report.getAllMessages()) { - msgs.addAll(toJhoveMessages(msg)); - } - msgs.forEach(jhoveMsg -> info.setMessage(jhoveMsg)); - - info.setMimeType(report.getFormat()); - - generateProperties(report).forEach(prop -> _propList.add(prop)); - - } catch (Exception f) { - f.printStackTrace(); - if (f.getMessage() != null) { - info.setMessage(new ErrorMessage(f.getMessage())); - } else { - info.setMessage(new ErrorMessage(MessageConstants.ERR_UNKNOWN)); - } - info.setWellFormed(false); // may not be the file's fault - return 0; - } + setChecksums(_ckSummer, info); - // Check if user has aborted - if (_je != null && _je.getAbort()) { - return 0; - } + return 0; + } - // We parsed it. Now assemble the properties. - info.setProperty(_metadata); - - setChecksums(_ckSummer, info); - - return 0; + @Override + public void parse(RandomAccessFile file, RepInfo info) throws IOException { + try (InputStream stream = new RandomAccessFileInputStream(file)) { + parse(stream, info, 0); } - - - @Override - public void parse(RandomAccessFile file, RepInfo info) throws IOException { - try (InputStream stream = new RandomAccessFileInputStream(file)) { - parse(stream, info, 0); - } + } + + /** + * Generates a set of properties collected using EPUBCheck to be added to the JHOVE report + * + * @param report + * @return + */ + private List generateProperties(JhoveRepInfoReport report) { + List properties = new ArrayList(); + + properties.add(generateProperty(PROPNAME_PAGECOUNT, report.getPageCount(), true)); + properties.add(generateProperty(PROPNAME_CHARCOUNT, report.getCharacterCount(), true)); + properties.add(generateProperty(PROPNAME_LANGUAGE, report.getLanguage())); + + Set infoProperties = new TreeSet(new PropertyComparator()); + addProperty(infoProperties, generateProperty(PROPNAME_IDENTIFIER, report.getIdentifier())); + addProperty(infoProperties, generateProperty(PROPNAME_TITLE, report.getTitles())); + addProperty(infoProperties, generateProperty(PROPNAME_CREATOR, report.getCreators())); + addProperty(infoProperties, generateProperty(PROPNAME_CONTRIBUTOR, report.getContributors())); + addProperty(infoProperties, generateProperty(PROPNAME_DATE, report.getDate())); + addProperty(infoProperties, generateProperty(PROPNAME_PUBLISHER, report.getPublisher())); + addProperty(infoProperties, generateProperty(PROPNAME_SUBJECTS, report.getSubjects())); + addProperty(infoProperties, generateProperty(PROPNAME_RIGHTS, report.getRights())); + + properties.add(generateProperty(PROPNAME_INFO, infoProperties)); + + Set fontList = generateFontProps(report.getEmbeddedFonts(), true); + fontList.addAll(generateFontProps(report.getRefFonts(), false)); + properties.add(generateProperty(PROPNAME_FONTS, fontList)); + + properties.add(generateProperty(PROPNAME_REFERENCES, report.getReferences())); + properties.add(generateProperty(PROPNAME_RESOURCES, report.getResources())); + properties.add(generateProperty(PROPNAME_MEDIATYPES, report.getMediaTypes())); + + for (String feature : report.getFeatures()) { + properties.add(generateProperty(feature, true)); } - /** - * Generates a set of properties collected using EPUBCheck to be added to the JHOVE report - * @param report - * @return - */ - private List generateProperties(JhoveRepInfoReport report) { - List properties = new ArrayList(); - - properties.add(generateProperty(PROPNAME_PAGECOUNT, report.getPageCount(), true)); - properties.add(generateProperty(PROPNAME_CHARCOUNT, report.getCharacterCount(), true)); - properties.add(generateProperty(PROPNAME_LANGUAGE, report.getLanguage())); - - Set infoProperties = new TreeSet(new PropertyComparator()); - addProperty(infoProperties, generateProperty(PROPNAME_IDENTIFIER, report.getIdentifier())); - addProperty(infoProperties, generateProperty(PROPNAME_TITLE, report.getTitles())); - addProperty(infoProperties, generateProperty(PROPNAME_CREATOR, report.getCreators())); - addProperty(infoProperties, generateProperty(PROPNAME_CONTRIBUTOR, report.getContributors())); - addProperty(infoProperties, generateProperty(PROPNAME_DATE, report.getDate())); - addProperty(infoProperties, generateProperty(PROPNAME_PUBLISHER, report.getPublisher())); - addProperty(infoProperties, generateProperty(PROPNAME_SUBJECTS, report.getSubjects())); - addProperty(infoProperties, generateProperty(PROPNAME_RIGHTS, report.getRights())); - - properties.add(generateProperty(PROPNAME_INFO, infoProperties)); - - Set fontList = generateFontProps(report.getEmbeddedFonts(), true); - fontList.addAll(generateFontProps(report.getRefFonts(), false)); - properties.add(generateProperty(PROPNAME_FONTS, fontList)); - - properties.add(generateProperty(PROPNAME_REFERENCES, report.getReferences())); - properties.add(generateProperty(PROPNAME_RESOURCES, report.getResources())); - properties.add(generateProperty(PROPNAME_MEDIATYPES, report.getMediaTypes())); - - for (String feature : report.getFeatures()) { - properties.add(generateProperty(feature, true)); - } + properties.removeAll(Collections.singletonList(null)); - properties.removeAll(Collections.singletonList(null)); + return properties; + } - return properties; + private static void addProperty(Set props, Property prop) { + if (prop == null) { + return; } - - private static void addProperty(Set props, Property prop) { - if (prop == null) { - return; - } - props.add(prop); + props.add(prop); + } + + /** + * Returns true if a message is either (a) a fatal error or (b) non-fatal error whose error id is + * listed in NOTWELLFORMED_ERRCODES + * + * @param id + * @return + */ + private boolean triggerNotWellFormed(CheckMessage msg) { + if (msg.getSeverity().equals(Severity.FATAL)) { + return true; } - - /** - * Returns true if a message is either (a) a fatal error or (b) non-fatal error - * whose error id is listed in NOTWELLFORMED_ERRCODES - * - * @param id - * @return - */ - private boolean triggerNotWellFormed(CheckMessage msg) { - if (msg.getSeverity().equals(Severity.FATAL)) { - return true; - } - if (msg.getSeverity().equals(Severity.ERROR)) { - for (String s : NOTWELLFORMED_ERRCODES) { - if (msg.getID().startsWith(s)) { - return true; - } - } + if (msg.getSeverity().equals(Severity.ERROR)) { + for (String s : NOTWELLFORMED_ERRCODES) { + if (msg.getID().startsWith(s)) { + return true; } - return false; + } } - - /** - * Generates the a set of font properties to be included in the JHOVE report - * - * @param fonts List of fonts found in the EPUB - * @param fontFile true if the font file is embedded in the EPUB - * @return - */ - private Set generateFontProps(Set fonts, boolean fontFile) { - - Set fontPropertiesList = new TreeSet(new PropertyComparator()); - - if (fonts != null && fonts.size() > 0) { - for (String font : fonts) { - Set fontProps = new TreeSet(new PropertyComparator()); - addProperty(fontProps, generateProperty(PROPNAME_FONTNAME, font)); - addProperty(fontProps, generateProperty(PROPNAME_FONTFILE, fontFile)); - - fontPropertiesList.add(generateProperty(PROPNAME_FONT, fontProps)); - } - } - return fontPropertiesList; - + return false; + } + + /** + * Generates the a set of font properties to be included in the JHOVE report + * + * @param fonts List of fonts found in the EPUB + * @param fontFile true if the font file is embedded in the EPUB + * @return + */ + private Set generateFontProps(Set fonts, boolean fontFile) { + + Set fontPropertiesList = new TreeSet(new PropertyComparator()); + + if (fonts != null && fonts.size() > 0) { + for (String font : fonts) { + Set fontProps = new TreeSet(new PropertyComparator()); + addProperty(fontProps, generateProperty(PROPNAME_FONTNAME, font)); + addProperty(fontProps, generateProperty(PROPNAME_FONTFILE, fontFile)); + + fontPropertiesList.add(generateProperty(PROPNAME_FONT, fontProps)); + } } - - - - /** - * Generate a JHOVE String {@link Property} - * @param name - * @param value - * @return - */ - private Property generateProperty(String name, String value) { - if (value != null && value.trim().length() > 0) { - return new Property(name, PropertyType.STRING, toUtf8(value)); - } - return null; + return fontPropertiesList; + } + + /** + * Generate a JHOVE String {@link Property} + * + * @param name + * @param value + * @return + */ + private Property generateProperty(String name, String value) { + if (value != null && value.trim().length() > 0) { + return new Property(name, PropertyType.STRING, toUtf8(value)); } - - /** - * Generate a JHOVE Long {@link Property} - * @param name - * @param value - * @param hideIfZero when true the method will return null if the value is zero - * @return - */ - private Property generateProperty(String name, Long value, boolean hideIfZero) { - if (value != null && (!value.equals(0L) || !hideIfZero)) { - return new Property(name, PropertyType.LONG, value); - } - return null; + return null; + } + + /** + * Generate a JHOVE Long {@link Property} + * + * @param name + * @param value + * @param hideIfZero when true the method will return null if the value is zero + * @return + */ + private Property generateProperty(String name, Long value, boolean hideIfZero) { + if (value != null && (!value.equals(0L) || !hideIfZero)) { + return new Property(name, PropertyType.LONG, value); } - - /** - * Generate JHOVE boolean {@link Property} - * @param name - * @param value - * @return - */ - private Property generateProperty(String name, boolean value) { - return new Property(name, PropertyType.BOOLEAN, value); + return null; + } + + /** + * Generate JHOVE boolean {@link Property} + * + * @param name + * @param value + * @return + */ + private Property generateProperty(String name, boolean value) { + return new Property(name, PropertyType.BOOLEAN, value); + } + + /** + * Generate a JHOVE String Array {@link Property}. If the array length=1, the property will be a + * SCALAR rather than an ARRAY. + * + * @param name + * @param value + * @return + */ + private Property generateProperty(String name, String[] value) { + if (value != null && value.length > 0) { + for (int i = 0; i < value.length; i++) { + value[i] = toUtf8(value[i]); + } + if (value.length == 1) { + return new Property(name, PropertyType.STRING, PropertyArity.SCALAR, value[0]); + } else { + return new Property(name, PropertyType.STRING, PropertyArity.ARRAY, value); + } } - - /** - * Generate a JHOVE String Array {@link Property}. If the array length=1, the - * property will be a SCALAR rather than an ARRAY. - * @param name - * @param value - * @return - */ - private Property generateProperty(String name, String[] value) { - if (value != null && value.length > 0) { - for (int i = 0; i < value.length; i++) { - value[i] = toUtf8(value[i]); - } - if (value.length == 1) { - return new Property(name, PropertyType.STRING, PropertyArity.SCALAR, value[0]); - } else { - return new Property(name, PropertyType.STRING, PropertyArity.ARRAY, value); - - } - } - return null; + return null; + } + + /** + * Generate a JHOVE Set {@link Property} + * + * @param name + * @param value + * @return + */ + private Property generateProperty(String name, Set value) { + if (value != null && value.size() > 0) { + return new Property(name, PropertyType.PROPERTY, PropertyArity.SET, value); } - - /** - * Generate a JHOVE Set {@link Property} - * @param name - * @param value - * @return - */ - private Property generateProperty(String name, Set value) { - if (value != null && value.size() > 0) { - return new Property(name, PropertyType.PROPERTY, PropertyArity.SET, value); - } - return null; + return null; + } + + /** + * Convert the {@link CheckMessage} format received from the EPUBCheck module to a set of JHOVE + * {@link Message}s. ERROR, FATAL, or WARNING messages will be converted to {@link ErrorMessage}s + * while all other messages will be converted to {@link InfoMessage}s + * + * @param msg The message from EPUBCheck + * @return A JHOVE Message + */ + private Set toJhoveMessages(CheckMessage msg) { + Set msgs = new TreeSet(new MessageComparator()); + if (msg == null) { + return msgs; } + Severity severity = msg.getSeverity(); + String msgText = msg.getMessage(); + String msgId = msg.getID(); - /** - * Convert the {@link CheckMessage} format received from the EPUBCheck module to - * a set of JHOVE {@link Message}s. ERROR, FATAL, or WARNING messages will be - * converted to {@link ErrorMessage}s while all other messages will be converted - * to {@link InfoMessage}s - * - * @param msg The message from EPUBCheck - * @return A JHOVE Message - */ - private Set toJhoveMessages(CheckMessage msg) { - Set msgs = new TreeSet(new MessageComparator()); - if (msg == null) { - return msgs; - } - Severity severity = msg.getSeverity(); - String msgText = msg.getMessage(); - String msgId = msg.getID(); - - if (msg.getLocations().size() > 0) { - for (EPUBLocation location : msg.getLocations()) { - addJhoveMessage(msgs, toJhoveMessage(msgId, msgText, severity, location)); - } - } else { - addJhoveMessage(msgs, toJhoveMessage(msgId, msgText, severity, null)); - } - return msgs; + if (msg.getLocations().size() > 0) { + for (EPUBLocation location : msg.getLocations()) { + addJhoveMessage(msgs, toJhoveMessage(msgId, msgText, severity, location)); + } + } else { + addJhoveMessage(msgs, toJhoveMessage(msgId, msgText, severity, null)); } + return msgs; + } - private static void addJhoveMessage(Set msgs, Message msg) { - if (msg == null) { - return; - } - msgs.add(msg); + private static void addJhoveMessage(Set msgs, Message msg) { + if (msg == null) { + return; } - - /** - * Convert the properties of a {@link CheckMessage} from the EPUBCheck module to - * a JHOVE {@link Message}. ERROR, FATAL, or WARNING messages will be converted - * to {@link ErrorMessage}s while all other messages will be converted to - * {@link InfoMessage}s - * - * @param msgId Message ID string from EPUBCheck - * @param messageText Message text from EPUBCheck - * @param severity Severity level from EPUBCheck - * @param location EPUBLocation from EPUBCheck - * @return A JHOVE {@link Message} - */ - private Message toJhoveMessage(String msgId, String messageText, Severity severity, EPUBLocation location) { - final String divider = ", "; - - String severityText = severity.toString(); - if (severity.equals(Severity.WARNING)) { - severityText = "WARN"; - } - String msgText = msgId + divider + severityText + divider + "[" + messageText + "]"; - - // append location if there is one - if (location != null) { - String loc = ""; - if (location.getLine() > 0 || location.getColumn() > 0) { - loc = " (" + location.getLine() + "-" + location.getColumn() + ")"; - } - msgText = msgText + divider + PathUtil.removeWorkingDirectory(location.getPath()) + loc; - } - - JhoveMessage msg = JhoveMessages.getMessageInstance(msgId, msgText); - - if (severity == Severity.ERROR || severity == Severity.FATAL || severity == Severity.WARNING) { - return new ErrorMessage(msg); - } else { - return new InfoMessage(msg); - } + msgs.add(msg); + } + + /** + * Convert the properties of a {@link CheckMessage} from the EPUBCheck module to a JHOVE {@link + * Message}. ERROR, FATAL, or WARNING messages will be converted to {@link ErrorMessage}s while + * all other messages will be converted to {@link InfoMessage}s + * + * @param msgId Message ID string from EPUBCheck + * @param messageText Message text from EPUBCheck + * @param severity Severity level from EPUBCheck + * @param location EPUBLocation from EPUBCheck + * @return A JHOVE {@link Message} + */ + private Message toJhoveMessage( + String msgId, String messageText, Severity severity, EPUBLocation location) { + final String divider = ", "; + + String severityText = severity.toString(); + if (severity.equals(Severity.WARNING)) { + severityText = "WARN"; } + String msgText = msgId + divider + severityText + divider + "[" + messageText + "]"; - /** - * Make sure the string contains valid UTF-8 characters - * @param inputString - * @return escaped String - */ - public static String toUtf8(String inputString) { - byte[] b = inputString.getBytes(StandardCharsets.UTF_8); - return new String(b, StandardCharsets.UTF_8); + // append location if there is one + if (location != null) { + String loc = ""; + if (location.getLine() > 0 || location.getColumn() > 0) { + loc = " (" + location.getLine() + "-" + location.getColumn() + ")"; + } + msgText = msgText + divider + PathUtil.removeWorkingDirectory(location.getPath()) + loc; } - @Override - protected void initParse() { - super.initParse(); - } + JhoveMessage msg = JhoveMessages.getMessageInstance(msgId, msgText); - static class PropertyComparator implements Comparator { - public int compare(final Property firstProp, final Property secondProp) { - int compVal = firstProp.getName().compareTo(secondProp.getName()); - return (compVal == 0) ? firstProp.getValue().toString().compareTo(secondProp.getValue().toString()) : compVal; - } + if (severity == Severity.ERROR || severity == Severity.FATAL || severity == Severity.WARNING) { + return new ErrorMessage(msg); + } else { + return new InfoMessage(msg); } + } + + /** + * Make sure the string contains valid UTF-8 characters + * + * @param inputString + * @return escaped String + */ + public static String toUtf8(String inputString) { + byte[] b = inputString.getBytes(StandardCharsets.UTF_8); + return new String(b, StandardCharsets.UTF_8); + } + + @Override + protected void initParse() { + super.initParse(); + } + + static class PropertyComparator implements Comparator { + public int compare(final Property firstProp, final Property secondProp) { + int compVal = firstProp.getName().compareTo(secondProp.getName()); + return (compVal == 0) + ? firstProp.getValue().toString().compareTo(secondProp.getValue().toString()) + : compVal; + } + } - static class MessageComparator implements Comparator { - public int compare(final Message firstMess, final Message secondMess) { - int compVal = firstMess.getId().compareTo(secondMess.getId()) ; - return (compVal == 0) ? firstMess.getMessage().compareTo(secondMess.getMessage()) : compVal; - } + static class MessageComparator implements Comparator { + public int compare(final Message firstMess, final Message secondMess) { + int compVal = firstMess.getId().compareTo(secondMess.getId()); + return (compVal == 0) ? firstMess.getMessage().compareTo(secondMess.getMessage()) : compVal; } + } } diff --git a/jhove-ext-modules/src/main/java/org/ithaka/portico/jhove/module/PorticoConstants.java b/jhove-ext-modules/src/main/java/org/ithaka/portico/jhove/module/PorticoConstants.java index 6c8e0aef5..a360b5254 100644 --- a/jhove-ext-modules/src/main/java/org/ithaka/portico/jhove/module/PorticoConstants.java +++ b/jhove-ext-modules/src/main/java/org/ithaka/portico/jhove/module/PorticoConstants.java @@ -6,39 +6,39 @@ public class PorticoConstants { - // Module agent information - public static final String PORTICOVENDORNAME = "Portico"; - public static final AgentType PORTICOAGENTTYPE = AgentType.EDUCATIONAL; - public static final String PORTICOAGENTADDRESS = "Portico, " - + "100 Campus Drive, Suite 100, " - + "Princeton, NJ 08540"; - public static final String PORTICOAGENTTELEPHONE = "+1 (609) 986-2222"; - public static final String PORTICOAGENTEMAIL = "support@portico.org"; - public static final String RIGHTS_STR1 = "Copyright "; - public static final String RIGHTS_STR2 = " by Portico. Released under the GNU Lesser General Public License."; + // Module agent information + public static final String PORTICOVENDORNAME = "Portico"; + public static final AgentType PORTICOAGENTTYPE = AgentType.EDUCATIONAL; + public static final String PORTICOAGENTADDRESS = + "Portico, " + "100 Campus Drive, Suite 100, " + "Princeton, NJ 08540"; + public static final String PORTICOAGENTTELEPHONE = "+1 (609) 986-2222"; + public static final String PORTICOAGENTEMAIL = "support@portico.org"; + public static final String RIGHTS_STR1 = "Copyright "; + public static final String RIGHTS_STR2 = + " by Portico. Released under the GNU Lesser General Public License."; - private PorticoConstants() { - // hide the constructor - } + private PorticoConstants() { + // hide the constructor + } - /** - * Constructs rights statement using (String) year value from each PTC module - * - * @param strYear Copyright year for each module, as String - * @return rights statement - */ - public static String porticoRightsStmt(String strYear) { - StringBuffer sb = new StringBuffer(RIGHTS_STR1); - sb.append(strYear); - sb.append(RIGHTS_STR2); - return sb.toString(); - } - - public static Agent porticoAgent() { - return new Builder(PORTICOVENDORNAME, PORTICOAGENTTYPE) - .address(PORTICOAGENTADDRESS) - .telephone(PORTICOAGENTTELEPHONE) - .email(PORTICOAGENTEMAIL).build(); - } + /** + * Constructs rights statement using (String) year value from each PTC module + * + * @param strYear Copyright year for each module, as String + * @return rights statement + */ + public static String porticoRightsStmt(String strYear) { + StringBuffer sb = new StringBuffer(RIGHTS_STR1); + sb.append(strYear); + sb.append(RIGHTS_STR2); + return sb.toString(); + } + public static Agent porticoAgent() { + return new Builder(PORTICOVENDORNAME, PORTICOAGENTTYPE) + .address(PORTICOAGENTADDRESS) + .telephone(PORTICOAGENTTELEPHONE) + .email(PORTICOAGENTEMAIL) + .build(); + } } diff --git a/jhove-ext-modules/src/main/java/org/ithaka/portico/jhove/module/epub/JhoveRepInfoReport.java b/jhove-ext-modules/src/main/java/org/ithaka/portico/jhove/module/epub/JhoveRepInfoReport.java index 75c4abc60..559d7d676 100644 --- a/jhove-ext-modules/src/main/java/org/ithaka/portico/jhove/module/epub/JhoveRepInfoReport.java +++ b/jhove-ext-modules/src/main/java/org/ithaka/portico/jhove/module/epub/JhoveRepInfoReport.java @@ -7,14 +7,6 @@ import static org.ithaka.portico.jhove.module.epub.ReportPropertyNames.FEATURE_HASSIGNATURES; import static org.ithaka.portico.jhove.module.epub.ReportPropertyNames.FEATURE_HASVIDEO; -import java.text.ParseException; -import java.text.SimpleDateFormat; -import java.util.ArrayList; -import java.util.Date; -import java.util.List; -import java.util.Set; -import java.util.TreeSet; - import com.adobe.epubcheck.api.EPUBLocation; import com.adobe.epubcheck.api.MasterReport; import com.adobe.epubcheck.messages.Message; @@ -22,450 +14,462 @@ import com.adobe.epubcheck.reporting.CheckMessage; import com.adobe.epubcheck.util.FeatureEnum; import com.adobe.epubcheck.util.PathUtil; +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.Date; +import java.util.List; +import java.util.Set; +import java.util.TreeSet; /** - * This is a custom report to extend the EPUBCheck {@link MasterReport}. Its - * purpose is to collect the properties needed to producing a JHOVE RepInfo - * object while the EPUB is being validated. + * This is a custom report to extend the EPUBCheck {@link MasterReport}. Its purpose is to collect + * the properties needed to producing a JHOVE RepInfo object while the EPUB is being validated. * * @author Karen Hanson - * */ public class JhoveRepInfoReport extends MasterReport { - protected String generationDate; - - protected String creationDate; - protected String lastModifiedDate; - protected String identifier; - protected Set titles = new TreeSet(); - protected Set creators = new TreeSet(); - protected Set contributors = new TreeSet(); - protected Set subjects = new TreeSet(); - protected String publisher; - protected Set rights = new TreeSet(); - protected String date; - protected Set mediaTypes = new TreeSet(); - - protected String formatName; - protected String formatVersion; - protected long pagesCount; - protected long charsCount; - protected String language; - protected Set embeddedFonts = new TreeSet(); - protected Set refFonts = new TreeSet(); - protected Set references = new TreeSet(); - protected Set resources = new TreeSet(); - protected boolean hasEncryption; - protected boolean hasSignatures; - protected boolean hasAudio; - protected boolean hasVideo; - protected boolean hasFixedLayout; - protected boolean hasScripts; - - protected List warns = new ArrayList(); - protected List errors = new ArrayList(); - protected List fatalErrors = new ArrayList(); - protected List hints = new ArrayList(); - - protected static final String ISO_DATE_PATTERN = "yyyy-MM-dd'T'HH:mm:ss'Z'"; - protected static final String FALLBACK_FORMAT = "application/octet-stream"; - - public JhoveRepInfoReport(String ePubName) { - this.setEpubFileName(PathUtil.removeWorkingDirectory(ePubName)); - - } - - @Override - public void message(Message message, EPUBLocation location, Object... args) { - Severity s = message.getSeverity(); - switch (s) { - case FATAL: - CheckMessage.addCheckMessage(fatalErrors, message, location, args); - break; - case ERROR: - CheckMessage.addCheckMessage(errors, message, location, args); - break; - case WARNING: - CheckMessage.addCheckMessage(warns, message, location, args); - break; - case USAGE: - CheckMessage.addCheckMessage(hints, message, location, args); - break; - case INFO: - break; - case SUPPRESSED: - break; - default: - break; - } - } - - @Override - public void info(String resource, FeatureEnum feature, String value) { - // Dont store 'null' values - if (value == null) - return; - - switch (feature) { - case FORMAT_NAME: - this.formatName = value; - break; - case FORMAT_VERSION: - this.formatVersion = value; - break; - case CREATION_DATE: - this.creationDate = value; - break; - case MODIFIED_DATE: - this.lastModifiedDate = value; - break; - case PAGES_COUNT: - this.pagesCount = Long.parseLong(value); - break; - case CHARS_COUNT: - this.charsCount += Long.parseLong(value); - break; - case DECLARED_MIMETYPE: - mediaTypes.add(value); - if (value != null && value.startsWith("audio/")) { - this.hasAudio = true; - } else if (value != null && value.startsWith("video/")) { - this.hasVideo = true; - } - break; - case FONT_EMBEDDED: - this.embeddedFonts.add(value); - break; - case FONT_REFERENCE: - this.refFonts.add(value); - break; - case REFERENCE: - this.references.add(value); - break; - case RESOURCE: - this.resources.add(value); - break; - case DC_LANGUAGE: - this.language = value; - break; - case DC_TITLE: - this.titles.add(value); - break; - case DC_CREATOR: - this.creators.add(value); - break; - case DC_CONTRIBUTOR: - this.contributors.add(value); - break; - case DC_PUBLISHER: - this.publisher = value; - break; - case DC_SUBJECT: - this.subjects.add(value); - break; - case DC_RIGHTS: - this.rights.add(value); - break; - case DC_DATE: - this.date = value; - break; - case UNIQUE_IDENT: - if (resource == null) { - this.identifier = value; - } - break; - case HAS_SIGNATURES: - this.hasSignatures = true; - break; - case HAS_ENCRYPTION: - this.hasEncryption = true; - break; - case HAS_FIXED_LAYOUT: - this.hasFixedLayout = true; - break; - case HAS_SCRIPTS: - this.hasScripts = true; - break; - case SPINE_INDEX: - break; - default: - break; + protected String generationDate; + + protected String creationDate; + protected String lastModifiedDate; + protected String identifier; + protected Set titles = new TreeSet(); + protected Set creators = new TreeSet(); + protected Set contributors = new TreeSet(); + protected Set subjects = new TreeSet(); + protected String publisher; + protected Set rights = new TreeSet(); + protected String date; + protected Set mediaTypes = new TreeSet(); + + protected String formatName; + protected String formatVersion; + protected long pagesCount; + protected long charsCount; + protected String language; + protected Set embeddedFonts = new TreeSet(); + protected Set refFonts = new TreeSet(); + protected Set references = new TreeSet(); + protected Set resources = new TreeSet(); + protected boolean hasEncryption; + protected boolean hasSignatures; + protected boolean hasAudio; + protected boolean hasVideo; + protected boolean hasFixedLayout; + protected boolean hasScripts; + + protected List warns = new ArrayList(); + protected List errors = new ArrayList(); + protected List fatalErrors = new ArrayList(); + protected List hints = new ArrayList(); + + protected static final String ISO_DATE_PATTERN = "yyyy-MM-dd'T'HH:mm:ss'Z'"; + protected static final String FALLBACK_FORMAT = "application/octet-stream"; + + public JhoveRepInfoReport(String ePubName) { + this.setEpubFileName(PathUtil.removeWorkingDirectory(ePubName)); + } + + @Override + public void message(Message message, EPUBLocation location, Object... args) { + Severity s = message.getSeverity(); + switch (s) { + case FATAL: + CheckMessage.addCheckMessage(fatalErrors, message, location, args); + break; + case ERROR: + CheckMessage.addCheckMessage(errors, message, location, args); + break; + case WARNING: + CheckMessage.addCheckMessage(warns, message, location, args); + break; + case USAGE: + CheckMessage.addCheckMessage(hints, message, location, args); + break; + case INFO: + break; + case SUPPRESSED: + break; + default: + break; + } + } + + @Override + public void info(String resource, FeatureEnum feature, String value) { + // Dont store 'null' values + if (value == null) return; + + switch (feature) { + case FORMAT_NAME: + this.formatName = value; + break; + case FORMAT_VERSION: + this.formatVersion = value; + break; + case CREATION_DATE: + this.creationDate = value; + break; + case MODIFIED_DATE: + this.lastModifiedDate = value; + break; + case PAGES_COUNT: + this.pagesCount = Long.parseLong(value); + break; + case CHARS_COUNT: + this.charsCount += Long.parseLong(value); + break; + case DECLARED_MIMETYPE: + mediaTypes.add(value); + if (value != null && value.startsWith("audio/")) { + this.hasAudio = true; + } else if (value != null && value.startsWith("video/")) { + this.hasVideo = true; } - } - - /** - * Get EPUB file creation date - * @return - */ - public Date getCreationDate() { - return toDate(this.creationDate); - } - - /** - * Get EPUB file last modified date - * @return - */ - public Date getLastModifiedDate() { - return toDate(this.lastModifiedDate); - } - - /** - * Get format of file validated (application/octet-stream if not an EPUB) - * @return - */ - public String getFormat() { - if (formatName == null) { - return FALLBACK_FORMAT; - } else { - return formatName; // application/epub+zip + break; + case FONT_EMBEDDED: + this.embeddedFonts.add(value); + break; + case FONT_REFERENCE: + this.refFonts.add(value); + break; + case REFERENCE: + this.references.add(value); + break; + case RESOURCE: + this.resources.add(value); + break; + case DC_LANGUAGE: + this.language = value; + break; + case DC_TITLE: + this.titles.add(value); + break; + case DC_CREATOR: + this.creators.add(value); + break; + case DC_CONTRIBUTOR: + this.contributors.add(value); + break; + case DC_PUBLISHER: + this.publisher = value; + break; + case DC_SUBJECT: + this.subjects.add(value); + break; + case DC_RIGHTS: + this.rights.add(value); + break; + case DC_DATE: + this.date = value; + break; + case UNIQUE_IDENT: + if (resource == null) { + this.identifier = value; } - } - - /** - * Get EPUB version validated against - EPUBcheck will use the most recent minor - * release version for the EPUB's major version e.g. for EPUBCheck 4.2.0, all - * EPUB 2s will be validated against the 2.0.1 spec, all EPUB 3s will be - * validated against the EPUB 3.2 spec. - * - * @return version validated as - */ - public String getVersion() { - return formatVersion; - } - - /** - * Get all messages generated during validation - * @return - */ - public List getAllMessages() { - List messages = new ArrayList(); - messages.addAll(fatalErrors); - messages.addAll(errors); - messages.addAll(warns); - messages.addAll(hints); - return messages; - } - - /** - * Get EPUB page count. - * @return - */ - public long getPageCount() { - return pagesCount; - } - - /** - * Get EPUB character count. - * @return - */ - public long getCharacterCount() { - return charsCount; - } - - /** - * Get EPUB language. - * @return - */ - public String getLanguage() { - return language; - } - - /** - * Get EPUB identifier. - * @return - */ - public String getIdentifier() { - return identifier; - } - - /** - * Get EPUB titles as string array - * @return - */ - public String[] getTitles() { - return toStringArray(titles); - } - - /** - * Get EPUB creators as string array - * @return - */ - public String[] getCreators() { - return toStringArray(creators); - } - - /** - * Get EPUB contributors as string array. - * @return - */ - public String[] getContributors() { - return toStringArray(contributors); - } - - /** - * Get EPUB publication date - * @return - */ - public String getDate() { - return date; - } - - /** - * Get EPUB publisher - * @return - */ - public String getPublisher() { - return publisher; - } - - /** - * Get EPUB subject headings - * @return - */ - public String[] getSubjects() { - return toStringArray(subjects); - } - - /** - * Get EPUB rights statements as string array - * @return - */ - public String[] getRights() { - return toStringArray(rights); - } - - /** - * Get EPUB embedded fonts - * @return - */ - public Set getEmbeddedFonts() { - return embeddedFonts; - } - - /** - * Get EPUB referenced fonts - these fonts are not embedded - * @return - */ - public Set getRefFonts() { - return refFonts; - } - - /** - * Get all EPUB references - this array includes both referenced and embedded - * material. - * - * @return - */ - public String[] getReferences() { - return toStringArray(references); - } - - /** - * Get a list of all EPUB resources - files that are either stored in the EPUB - * package or else stored outside of the EPUB but used as a key component e.g. - * embedded video or audio. - * - * @return - */ - public String[] getResources() { - return toStringArray(resources); - } - - /** - * Get list of media types present in the EPUB package - * - * @return - */ - public String[] getMediaTypes() { - return toStringArray(mediaTypes); - } - - /** - * Get set of features identified in the EPUB - * @return - */ - public Set getFeatures() { - Set features = new TreeSet(); - if (hasEncryption) - features.add(FEATURE_HASENCRYPTION); - if (hasSignatures) - features.add(FEATURE_HASSIGNATURES); - if (hasAudio) - features.add(FEATURE_HASAUDIO); - if (hasVideo) - features.add(FEATURE_HASVIDEO); - if (hasFixedLayout) - features.add(FEATURE_HASFIXEDLAYOUT); - if (hasScripts) - features.add(FEATURE_HASSCRIPTS); - return features; - } - - private String[] toStringArray(Set set) { - if (set != null && !set.isEmpty()) { - String[] arr = new String[set.size()]; - return set.toArray(arr); - } else { - return null; - } - } - - private static Date toDate(String isoDate) { - if (isoDate == null || isoDate.length() == 0) { - return null; - } - - SimpleDateFormat simpleDateFormat = new SimpleDateFormat(ISO_DATE_PATTERN); - Date date; - try { - date = simpleDateFormat.parse(isoDate); - } catch (ParseException e) { - throw new IllegalArgumentException("Invalid ISO date provided: " + isoDate, e); - } - return date; - } - - /* - * Because of the way message(MessageId, EPUBLocation, Args) is overridden in - * MasterReport and message(Message, EPUBLocation, Args) is overridden in this - * report, messages that are passed as a Message rather than a MessageId may not - * get included in the message count. These overrides count the number of - * messages listed instead since all messages travel through message(Message, - * EPUBLocation, Args) - */ - - @Override - public int getErrorCount() { - return errors.size(); - } - - @Override - public int getWarningCount() { - return warns.size(); - } - - @Override - public int getFatalErrorCount() { - return fatalErrors.size(); - } - - @Override - public int getUsageCount() { - return hints.size(); - } - - /* - * This report has no output, it is used to harvest properties for a JHOVE - * report - */ - @Override - public int generate() { - return 0; - } - - @Override - public void initialize() { - // no initialization code required - } + break; + case HAS_SIGNATURES: + this.hasSignatures = true; + break; + case HAS_ENCRYPTION: + this.hasEncryption = true; + break; + case HAS_FIXED_LAYOUT: + this.hasFixedLayout = true; + break; + case HAS_SCRIPTS: + this.hasScripts = true; + break; + case SPINE_INDEX: + break; + default: + break; + } + } + + /** + * Get EPUB file creation date + * + * @return + */ + public Date getCreationDate() { + return toDate(this.creationDate); + } + + /** + * Get EPUB file last modified date + * + * @return + */ + public Date getLastModifiedDate() { + return toDate(this.lastModifiedDate); + } + + /** + * Get format of file validated (application/octet-stream if not an EPUB) + * + * @return + */ + public String getFormat() { + if (formatName == null) { + return FALLBACK_FORMAT; + } else { + return formatName; // application/epub+zip + } + } + + /** + * Get EPUB version validated against - EPUBcheck will use the most recent minor release version + * for the EPUB's major version e.g. for EPUBCheck 4.2.0, all EPUB 2s will be validated against + * the 2.0.1 spec, all EPUB 3s will be validated against the EPUB 3.2 spec. + * + * @return version validated as + */ + public String getVersion() { + return formatVersion; + } + + /** + * Get all messages generated during validation + * + * @return + */ + public List getAllMessages() { + List messages = new ArrayList(); + messages.addAll(fatalErrors); + messages.addAll(errors); + messages.addAll(warns); + messages.addAll(hints); + return messages; + } + + /** + * Get EPUB page count. + * + * @return + */ + public long getPageCount() { + return pagesCount; + } + + /** + * Get EPUB character count. + * + * @return + */ + public long getCharacterCount() { + return charsCount; + } + + /** + * Get EPUB language. + * + * @return + */ + public String getLanguage() { + return language; + } + + /** + * Get EPUB identifier. + * + * @return + */ + public String getIdentifier() { + return identifier; + } + + /** + * Get EPUB titles as string array + * + * @return + */ + public String[] getTitles() { + return toStringArray(titles); + } + + /** + * Get EPUB creators as string array + * + * @return + */ + public String[] getCreators() { + return toStringArray(creators); + } + + /** + * Get EPUB contributors as string array. + * + * @return + */ + public String[] getContributors() { + return toStringArray(contributors); + } + + /** + * Get EPUB publication date + * + * @return + */ + public String getDate() { + return date; + } + + /** + * Get EPUB publisher + * + * @return + */ + public String getPublisher() { + return publisher; + } + + /** + * Get EPUB subject headings + * + * @return + */ + public String[] getSubjects() { + return toStringArray(subjects); + } + + /** + * Get EPUB rights statements as string array + * + * @return + */ + public String[] getRights() { + return toStringArray(rights); + } + + /** + * Get EPUB embedded fonts + * + * @return + */ + public Set getEmbeddedFonts() { + return embeddedFonts; + } + + /** + * Get EPUB referenced fonts - these fonts are not embedded + * + * @return + */ + public Set getRefFonts() { + return refFonts; + } + + /** + * Get all EPUB references - this array includes both referenced and embedded material. + * + * @return + */ + public String[] getReferences() { + return toStringArray(references); + } + + /** + * Get a list of all EPUB resources - files that are either stored in the EPUB package or else + * stored outside of the EPUB but used as a key component e.g. embedded video or audio. + * + * @return + */ + public String[] getResources() { + return toStringArray(resources); + } + + /** + * Get list of media types present in the EPUB package + * + * @return + */ + public String[] getMediaTypes() { + return toStringArray(mediaTypes); + } + + /** + * Get set of features identified in the EPUB + * + * @return + */ + public Set getFeatures() { + Set features = new TreeSet(); + if (hasEncryption) features.add(FEATURE_HASENCRYPTION); + if (hasSignatures) features.add(FEATURE_HASSIGNATURES); + if (hasAudio) features.add(FEATURE_HASAUDIO); + if (hasVideo) features.add(FEATURE_HASVIDEO); + if (hasFixedLayout) features.add(FEATURE_HASFIXEDLAYOUT); + if (hasScripts) features.add(FEATURE_HASSCRIPTS); + return features; + } + + private String[] toStringArray(Set set) { + if (set != null && !set.isEmpty()) { + String[] arr = new String[set.size()]; + return set.toArray(arr); + } else { + return null; + } + } + + private static Date toDate(String isoDate) { + if (isoDate == null || isoDate.length() == 0) { + return null; + } + + SimpleDateFormat simpleDateFormat = new SimpleDateFormat(ISO_DATE_PATTERN); + Date date; + try { + date = simpleDateFormat.parse(isoDate); + } catch (ParseException e) { + throw new IllegalArgumentException("Invalid ISO date provided: " + isoDate, e); + } + return date; + } + + /* + * Because of the way message(MessageId, EPUBLocation, Args) is overridden in + * MasterReport and message(Message, EPUBLocation, Args) is overridden in this + * report, messages that are passed as a Message rather than a MessageId may not + * get included in the message count. These overrides count the number of + * messages listed instead since all messages travel through message(Message, + * EPUBLocation, Args) + */ + + @Override + public int getErrorCount() { + return errors.size(); + } + + @Override + public int getWarningCount() { + return warns.size(); + } + + @Override + public int getFatalErrorCount() { + return fatalErrors.size(); + } + + @Override + public int getUsageCount() { + return hints.size(); + } + + /* + * This report has no output, it is used to harvest properties for a JHOVE + * report + */ + @Override + public int generate() { + return 0; + } + + @Override + public void initialize() { + // no initialization code required + } } diff --git a/jhove-ext-modules/src/main/java/org/ithaka/portico/jhove/module/epub/MessageConstants.java b/jhove-ext-modules/src/main/java/org/ithaka/portico/jhove/module/epub/MessageConstants.java index 66b4b92be..b73e34c4e 100644 --- a/jhove-ext-modules/src/main/java/org/ithaka/portico/jhove/module/epub/MessageConstants.java +++ b/jhove-ext-modules/src/main/java/org/ithaka/portico/jhove/module/epub/MessageConstants.java @@ -1,12 +1,10 @@ package org.ithaka.portico.jhove.module.epub; public enum MessageConstants { + INSTANCE; - INSTANCE; - - /** - * Error messages - */ - public static final String ERR_UNKNOWN = "FATAL, [An undefined error occurred. This may " - + "be caused by a system error or indicative of a problem with the file provided.]"; + /** Error messages */ + public static final String ERR_UNKNOWN = + "FATAL, [An undefined error occurred. This may " + + "be caused by a system error or indicative of a problem with the file provided.]"; } diff --git a/jhove-ext-modules/src/main/java/org/ithaka/portico/jhove/module/epub/ReportPropertyNames.java b/jhove-ext-modules/src/main/java/org/ithaka/portico/jhove/module/epub/ReportPropertyNames.java index bbc6327a5..580f5d0ae 100644 --- a/jhove-ext-modules/src/main/java/org/ithaka/portico/jhove/module/epub/ReportPropertyNames.java +++ b/jhove-ext-modules/src/main/java/org/ithaka/portico/jhove/module/epub/ReportPropertyNames.java @@ -1,44 +1,41 @@ package org.ithaka.portico.jhove.module.epub; -/*** - * Constants representing each of the properties that might appear in the EPUB - * RepInfo report +/** + * * Constants representing each of the properties that might appear in the EPUB RepInfo report * * @author khanson - * */ public class ReportPropertyNames { - public static final String PROPNAME_EPUB_METADATA = "EPUBMetadata"; - public static final String PROPNAME_PAGECOUNT = "PageCount"; - public static final String PROPNAME_CHARCOUNT = "CharacterCount"; - public static final String PROPNAME_LANGUAGE = "Language"; - public static final String PROPNAME_INFO = "Info"; - public static final String PROPNAME_IDENTIFIER = "Identifier"; - public static final String PROPNAME_TITLE = "Title"; - public static final String PROPNAME_CREATOR = "Creator"; - public static final String PROPNAME_CONTRIBUTOR = "Contributor"; - public static final String PROPNAME_DATE = "Date"; - public static final String PROPNAME_PUBLISHER = "Publisher"; - public static final String PROPNAME_SUBJECTS = "Subject"; - public static final String PROPNAME_RIGHTS = "Rights"; - public static final String PROPNAME_FONTS = "Fonts"; - public static final String PROPNAME_FONT = "Font"; - public static final String PROPNAME_FONTNAME = "FontName"; - public static final String PROPNAME_FONTFILE = "FontFile"; - public static final String PROPNAME_REFERENCES = "References"; - public static final String PROPNAME_RESOURCES = "Resources"; - public static final String PROPNAME_MEDIATYPES = "MediaTypes"; - - public static final String FEATURE_HASENCRYPTION = "hasEncryption"; - public static final String FEATURE_HASSIGNATURES = "hasSignatures"; - public static final String FEATURE_HASAUDIO = "hasAudio"; - public static final String FEATURE_HASVIDEO = "hasVideo"; - public static final String FEATURE_HASFIXEDLAYOUT = "hasFixedLayout"; - public static final String FEATURE_HASSCRIPTS = "hasScripts"; + public static final String PROPNAME_EPUB_METADATA = "EPUBMetadata"; + public static final String PROPNAME_PAGECOUNT = "PageCount"; + public static final String PROPNAME_CHARCOUNT = "CharacterCount"; + public static final String PROPNAME_LANGUAGE = "Language"; + public static final String PROPNAME_INFO = "Info"; + public static final String PROPNAME_IDENTIFIER = "Identifier"; + public static final String PROPNAME_TITLE = "Title"; + public static final String PROPNAME_CREATOR = "Creator"; + public static final String PROPNAME_CONTRIBUTOR = "Contributor"; + public static final String PROPNAME_DATE = "Date"; + public static final String PROPNAME_PUBLISHER = "Publisher"; + public static final String PROPNAME_SUBJECTS = "Subject"; + public static final String PROPNAME_RIGHTS = "Rights"; + public static final String PROPNAME_FONTS = "Fonts"; + public static final String PROPNAME_FONT = "Font"; + public static final String PROPNAME_FONTNAME = "FontName"; + public static final String PROPNAME_FONTFILE = "FontFile"; + public static final String PROPNAME_REFERENCES = "References"; + public static final String PROPNAME_RESOURCES = "Resources"; + public static final String PROPNAME_MEDIATYPES = "MediaTypes"; - private ReportPropertyNames() { - // hide constructor - } + public static final String FEATURE_HASENCRYPTION = "hasEncryption"; + public static final String FEATURE_HASSIGNATURES = "hasSignatures"; + public static final String FEATURE_HASAUDIO = "hasAudio"; + public static final String FEATURE_HASVIDEO = "hasVideo"; + public static final String FEATURE_HASFIXEDLAYOUT = "hasFixedLayout"; + public static final String FEATURE_HASSCRIPTS = "hasScripts"; + private ReportPropertyNames() { + // hide constructor + } } diff --git a/jhove-ext-modules/src/main/java/org/ithaka/portico/jhove/module/epub/package-info.java b/jhove-ext-modules/src/main/java/org/ithaka/portico/jhove/module/epub/package-info.java index 2274002fe..edb2167b7 100644 --- a/jhove-ext-modules/src/main/java/org/ithaka/portico/jhove/module/epub/package-info.java +++ b/jhove-ext-modules/src/main/java/org/ithaka/portico/jhove/module/epub/package-info.java @@ -1,13 +1,12 @@ /** - * Contains the classes needed for building a JHOVE application. - * This package must be used with a top-level class, with one or more - * output handlers or viewers, and with one or more modules for specific - * file formats. - *

- * For overviews, tutorials, examples, guides, and tool documentation, - * please see: + * Contains the classes needed for building a JHOVE application. This package must be used with a + * top-level class, with one or more output handlers or viewers, and with one or more modules for + * specific file formats. + * + *

For overviews, tutorials, examples, guides, and tool documentation, please see: + * *

*/ package org.ithaka.portico.jhove.module.epub; diff --git a/jhove-ext-modules/src/test/java/com/mcgath/jhove/module/PngModuleTest.java b/jhove-ext-modules/src/test/java/com/mcgath/jhove/module/PngModuleTest.java index 1083373b9..92c1e3d93 100644 --- a/jhove-ext-modules/src/test/java/com/mcgath/jhove/module/PngModuleTest.java +++ b/jhove-ext-modules/src/test/java/com/mcgath/jhove/module/PngModuleTest.java @@ -1,79 +1,74 @@ package com.mcgath.jhove.module; - import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; +import edu.harvard.hul.ois.jhove.Property; +import edu.harvard.hul.ois.jhove.RepInfo; import java.io.File; import java.io.FileInputStream; import java.util.HashSet; import java.util.LinkedList; import java.util.Set; - import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; -import edu.harvard.hul.ois.jhove.Property; -import edu.harvard.hul.ois.jhove.RepInfo; - /** - *

* Tests for PngModule. - *

* * @author Karen Hanson */ @RunWith(JUnit4.class) public class PngModuleTest { - private static final String ITXTCHUNK_PNG_FILEPATH = "src/test/resources/png/FileWithiTXtChunks.png"; - private static final String PNG_MIMETYPE = "image/png"; + private static final String ITXTCHUNK_PNG_FILEPATH = + "src/test/resources/png/FileWithiTXtChunks.png"; + private static final String PNG_MIMETYPE = "image/png"; - /** - * Parses valid PNG file with an iTXt Chunk and checks Author value is picked - * up. This test relates to an NPE issue (#148) in v.1.24: Test file is - * "cten0g04.png", copied from PNG test suite at http://www.schaik.com/pngsuite/ - * - * @see https://github.com/openpreserve/jhove/issues/148 - * @see http://www.schaik.com/pngsuite/ - * - * @throws Exception - */ - @Test - public void parsePngWithItxtChunk() throws Exception { - final String EXPECTED_ITXTTITLE_VALUE = "PngSuite"; - final String EXPECTED_ITXTDISCLAIMER_VALUE = "Freeware."; - final String PROP_KEYWORDS = "Keywords"; - final String PROP_VALUE = "Value"; + /** + * Parses valid PNG file with an iTXt Chunk and checks Author value is picked up. This test + * relates to an NPE issue (#148) in v.1.24: Test file is "cten0g04.png", copied from PNG test + * suite at http://www.schaik.com/pngsuite/ + * + * @see https://github.com/openpreserve/jhove/issues/148 + * @see http://www.schaik.com/pngsuite/ + * @throws Exception + */ + @Test + public void parsePngWithItxtChunk() throws Exception { + final String EXPECTED_ITXTTITLE_VALUE = "PngSuite"; + final String EXPECTED_ITXTDISCLAIMER_VALUE = "Freeware."; + final String PROP_KEYWORDS = "Keywords"; + final String PROP_VALUE = "Value"; - File pngFile = new File(ITXTCHUNK_PNG_FILEPATH); + File pngFile = new File(ITXTCHUNK_PNG_FILEPATH); - RepInfo info = new RepInfo(pngFile.getAbsolutePath()); - PngModule pngMod = new PngModule(); - pngMod.parse(new FileInputStream(pngFile), info, 0); + RepInfo info = new RepInfo(pngFile.getAbsolutePath()); + PngModule pngMod = new PngModule(); + pngMod.parse(new FileInputStream(pngFile), info, 0); - int wellformed = info.getWellFormed(); - int valid = info.getValid(); - String type = info.getMimeType(); + int wellformed = info.getWellFormed(); + int valid = info.getValid(); + String type = info.getMimeType(); - // General checks - assertEquals(1, wellformed); - assertEquals(1, valid); - assertEquals(PNG_MIMETYPE, type); + // General checks + assertEquals(1, wellformed); + assertEquals(1, valid); + assertEquals(PNG_MIMETYPE, type); - // Check the value of the first iTXt is as expected - Set keywordValues = new HashSet(); - @SuppressWarnings("unchecked") - LinkedList iTxtKeywordProperties = (LinkedList) info.getByName(PROP_KEYWORDS).getValue(); - for (Property prop : iTxtKeywordProperties) { - keywordValues.add(prop.getByName(PROP_VALUE).getValue().toString()); - } - - assertEquals(6, keywordValues.size()); - assertTrue(keywordValues.contains(EXPECTED_ITXTTITLE_VALUE)); - assertTrue(keywordValues.contains(EXPECTED_ITXTDISCLAIMER_VALUE)); + // Check the value of the first iTXt is as expected + Set keywordValues = new HashSet(); + @SuppressWarnings("unchecked") + LinkedList iTxtKeywordProperties = + (LinkedList) info.getByName(PROP_KEYWORDS).getValue(); + for (Property prop : iTxtKeywordProperties) { + keywordValues.add(prop.getByName(PROP_VALUE).getValue().toString()); } + + assertEquals(6, keywordValues.size()); + assertTrue(keywordValues.contains(EXPECTED_ITXTTITLE_VALUE)); + assertTrue(keywordValues.contains(EXPECTED_ITXTDISCLAIMER_VALUE)); + } } diff --git a/jhove-ext-modules/src/test/java/edu/harvard/hul/ois/jhove/module/GzipModuleTest.java b/jhove-ext-modules/src/test/java/edu/harvard/hul/ois/jhove/module/GzipModuleTest.java index e617c774c..b45b2de20 100644 --- a/jhove-ext-modules/src/test/java/edu/harvard/hul/ois/jhove/module/GzipModuleTest.java +++ b/jhove-ext-modules/src/test/java/edu/harvard/hul/ois/jhove/module/GzipModuleTest.java @@ -5,6 +5,11 @@ import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; +import edu.harvard.hul.ois.jhove.Message; +import edu.harvard.hul.ois.jhove.Property; +import edu.harvard.hul.ois.jhove.PropertyArity; +import edu.harvard.hul.ois.jhove.PropertyType; +import edu.harvard.hul.ois.jhove.RepInfo; import java.io.File; import java.io.FileInputStream; import java.util.Arrays; @@ -14,214 +19,210 @@ import java.util.List; import java.util.Map; import java.util.zip.DataFormatException; - import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; import org.jwat.common.DiagnosisType; -import edu.harvard.hul.ois.jhove.Message; -import edu.harvard.hul.ois.jhove.Property; -import edu.harvard.hul.ois.jhove.PropertyArity; -import edu.harvard.hul.ois.jhove.PropertyType; -import edu.harvard.hul.ois.jhove.RepInfo; - @RunWith(JUnit4.class) public class GzipModuleTest { - - private static final String TXT_GZIP_SAMPLE_FILE = "src/test/resources/gzip/sample.txt.gz"; - private static final String RECORDS = "Records"; - private static final String THREE_FILES_GZIP_SAMPLE_FILE = "src/test/resources/gzip/three-files.gz"; - - @Test - public void parseSampleTextGzipFile() throws Exception { - File gzipFile = new File(TXT_GZIP_SAMPLE_FILE); - assertTrue( gzipFile.isFile() ); - - GzipModule gzm = new GzipModule(); - RepInfo info = new RepInfo(gzipFile.getAbsolutePath()); - gzm.parse(new FileInputStream(gzipFile), info, 0); - - assertEquals(RepInfo.TRUE, info.getWellFormed()); - assertEquals(RepInfo.TRUE, info.getValid()); - assertEquals(Arrays.asList(gzm.getName()), info.getSigMatch()); - - assertEquals(0, info.getMessage().size()); - - // Validate that it creates properties for each record - assertEquals(1, info.getProperty().size()); - assertNotNull(info.getProperty().get(RECORDS)); - assertEquals(PropertyArity.LIST, info.getProperty().get(RECORDS).getArity()); - assertEquals(PropertyType.PROPERTY, info.getProperty().get(RECORDS).getType()); - assertEquals(1, ((List)info.getProperty().get(RECORDS).getValue()).size()); - } - - @Test - public void checkSignatureSampleTextGzipFile() throws Exception { - File gzipFile = new File(TXT_GZIP_SAMPLE_FILE); - - GzipModule gzm = new GzipModule(); - RepInfo info = new RepInfo(gzipFile.getAbsolutePath()); - gzm.checkSignatures(null, new FileInputStream(gzipFile), info); - - assertEquals(RepInfo.TRUE, info.getWellFormed()); - assertEquals(GzipModule.class, info.getModule().getClass()); - assertEquals(Arrays.asList(gzm.getName()), info.getSigMatch()); - } - - @Test - public void parseThreeFilesGzipFile() throws Exception { - File gzipFile = new File(THREE_FILES_GZIP_SAMPLE_FILE); - assertTrue( gzipFile.isFile() ); - - GzipModule gzm = new GzipModule(); - RepInfo info = new RepInfo(gzipFile.getAbsolutePath()); - gzm.parse(new FileInputStream(gzipFile), info, 0); - - assertEquals(RepInfo.TRUE, info.getWellFormed()); - assertEquals(RepInfo.TRUE, info.getValid()); - assertEquals(Arrays.asList(gzm.getName()), info.getSigMatch()); - - assertEquals(0, info.getMessage().size()); - - // Validate that it creates properties for each record - assertEquals(1, info.getProperty().size()); - assertNotNull(info.getProperty().get(RECORDS)); - assertEquals(3, ((List)info.getProperty().get(RECORDS).getValue()).size()); - } - @Test - public void checkSignaturThreeFilesGzipFile() throws Exception { - File gzipFile = new File(THREE_FILES_GZIP_SAMPLE_FILE); - - GzipModule gzm = new GzipModule(); - RepInfo info = new RepInfo(gzipFile.getAbsolutePath()); - gzm.checkSignatures(null, new FileInputStream(gzipFile), info); - - assertEquals(RepInfo.TRUE, info.getWellFormed()); - assertEquals(GzipModule.class, info.getModule().getClass()); - assertEquals(Arrays.asList(gzm.getName()), info.getSigMatch()); - } - - @Test - public void parseInvalidCompressionGzipFile() throws Exception { - File gzipFile = new File("src/test/resources/gzip/invalid-compression.gz"); - assertTrue( gzipFile.isFile() ); - - GzipModule gzm = new GzipModule(); - RepInfo info = new RepInfo(gzipFile.getAbsolutePath()); - gzm.parse(new FileInputStream(gzipFile), info, 0); - - assertEquals(RepInfo.FALSE, info.getWellFormed()); - assertEquals(RepInfo.FALSE, info.getValid()); - assertTrue(info.getSigMatch().isEmpty()); - - // Validate the failures. - assertEquals(1, info.getMessage().size()); - Map messages = extractMessages(info.getMessage()); - assertEquals(1, messages.size()); - assertEquals(1, messages.get(DiagnosisType.INVALID_EXPECTED.name()).intValue()); - } - - @Test - public void parseInvalidEmptyGzipFile() throws Exception { - File gzipFile = new File("src/test/resources/gzip/invalid-empty.gzip"); - assertTrue( gzipFile.isFile() ); - - GzipModule gzm = new GzipModule(); - RepInfo info = new RepInfo(gzipFile.getAbsolutePath()); - gzm.parse(new FileInputStream(gzipFile), info, 0); - - assertEquals(RepInfo.FALSE, info.getWellFormed()); - assertEquals(RepInfo.FALSE, info.getValid()); - assertTrue(info.getSigMatch().isEmpty()); - - // Validate the failures. - assertEquals(1, info.getMessage().size()); - Map messages = extractMessages(info.getMessage()); - assertEquals(1, messages.size()); - assertEquals(1, messages.get(DiagnosisType.ERROR_EXPECTED.name()).intValue()); + private static final String TXT_GZIP_SAMPLE_FILE = "src/test/resources/gzip/sample.txt.gz"; + private static final String RECORDS = "Records"; + private static final String THREE_FILES_GZIP_SAMPLE_FILE = + "src/test/resources/gzip/three-files.gz"; + + @Test + public void parseSampleTextGzipFile() throws Exception { + File gzipFile = new File(TXT_GZIP_SAMPLE_FILE); + assertTrue(gzipFile.isFile()); + + GzipModule gzm = new GzipModule(); + RepInfo info = new RepInfo(gzipFile.getAbsolutePath()); + gzm.parse(new FileInputStream(gzipFile), info, 0); + + assertEquals(RepInfo.TRUE, info.getWellFormed()); + assertEquals(RepInfo.TRUE, info.getValid()); + assertEquals(Arrays.asList(gzm.getName()), info.getSigMatch()); + + assertEquals(0, info.getMessage().size()); + + // Validate that it creates properties for each record + assertEquals(1, info.getProperty().size()); + assertNotNull(info.getProperty().get(RECORDS)); + assertEquals(PropertyArity.LIST, info.getProperty().get(RECORDS).getArity()); + assertEquals(PropertyType.PROPERTY, info.getProperty().get(RECORDS).getType()); + assertEquals(1, ((List) info.getProperty().get(RECORDS).getValue()).size()); + } + + @Test + public void checkSignatureSampleTextGzipFile() throws Exception { + File gzipFile = new File(TXT_GZIP_SAMPLE_FILE); + + GzipModule gzm = new GzipModule(); + RepInfo info = new RepInfo(gzipFile.getAbsolutePath()); + gzm.checkSignatures(null, new FileInputStream(gzipFile), info); + + assertEquals(RepInfo.TRUE, info.getWellFormed()); + assertEquals(GzipModule.class, info.getModule().getClass()); + assertEquals(Arrays.asList(gzm.getName()), info.getSigMatch()); + } + + @Test + public void parseThreeFilesGzipFile() throws Exception { + File gzipFile = new File(THREE_FILES_GZIP_SAMPLE_FILE); + assertTrue(gzipFile.isFile()); + + GzipModule gzm = new GzipModule(); + RepInfo info = new RepInfo(gzipFile.getAbsolutePath()); + gzm.parse(new FileInputStream(gzipFile), info, 0); + + assertEquals(RepInfo.TRUE, info.getWellFormed()); + assertEquals(RepInfo.TRUE, info.getValid()); + assertEquals(Arrays.asList(gzm.getName()), info.getSigMatch()); + + assertEquals(0, info.getMessage().size()); + + // Validate that it creates properties for each record + assertEquals(1, info.getProperty().size()); + assertNotNull(info.getProperty().get(RECORDS)); + assertEquals(3, ((List) info.getProperty().get(RECORDS).getValue()).size()); + } + + @Test + public void checkSignaturThreeFilesGzipFile() throws Exception { + File gzipFile = new File(THREE_FILES_GZIP_SAMPLE_FILE); + + GzipModule gzm = new GzipModule(); + RepInfo info = new RepInfo(gzipFile.getAbsolutePath()); + gzm.checkSignatures(null, new FileInputStream(gzipFile), info); + + assertEquals(RepInfo.TRUE, info.getWellFormed()); + assertEquals(GzipModule.class, info.getModule().getClass()); + assertEquals(Arrays.asList(gzm.getName()), info.getSigMatch()); + } + + @Test + public void parseInvalidCompressionGzipFile() throws Exception { + File gzipFile = new File("src/test/resources/gzip/invalid-compression.gz"); + assertTrue(gzipFile.isFile()); + + GzipModule gzm = new GzipModule(); + RepInfo info = new RepInfo(gzipFile.getAbsolutePath()); + gzm.parse(new FileInputStream(gzipFile), info, 0); + + assertEquals(RepInfo.FALSE, info.getWellFormed()); + assertEquals(RepInfo.FALSE, info.getValid()); + assertTrue(info.getSigMatch().isEmpty()); + + // Validate the failures. + assertEquals(1, info.getMessage().size()); + Map messages = extractMessages(info.getMessage()); + assertEquals(1, messages.size()); + assertEquals(1, messages.get(DiagnosisType.INVALID_EXPECTED.name()).intValue()); + } + + @Test + public void parseInvalidEmptyGzipFile() throws Exception { + File gzipFile = new File("src/test/resources/gzip/invalid-empty.gzip"); + assertTrue(gzipFile.isFile()); + + GzipModule gzm = new GzipModule(); + RepInfo info = new RepInfo(gzipFile.getAbsolutePath()); + gzm.parse(new FileInputStream(gzipFile), info, 0); + + assertEquals(RepInfo.FALSE, info.getWellFormed()); + assertEquals(RepInfo.FALSE, info.getValid()); + assertTrue(info.getSigMatch().isEmpty()); + + // Validate the failures. + assertEquals(1, info.getMessage().size()); + Map messages = extractMessages(info.getMessage()); + assertEquals(1, messages.size()); + assertEquals(1, messages.get(DiagnosisType.ERROR_EXPECTED.name()).intValue()); + } + + @Test + public void parseInvalidEntriesGzipFile() throws Exception { + File gzipFile = new File("src/test/resources/gzip/invalid-entries.gz"); + assertTrue(gzipFile.isFile()); + + GzipModule gzm = new GzipModule(); + RepInfo info = new RepInfo(gzipFile.getAbsolutePath()); + gzm.parse(new FileInputStream(gzipFile), info, 0); + + assertEquals(RepInfo.FALSE, info.getWellFormed()); + assertEquals(RepInfo.FALSE, info.getValid()); + assertTrue(info.getSigMatch().isEmpty()); + + // Validate the failures. + assertEquals(5, info.getMessage().size()); + Map messages = extractMessages(info.getMessage()); + assertEquals(3, messages.size()); + assertEquals(2, messages.get(DiagnosisType.INVALID_EXPECTED.name()).intValue()); + assertEquals(2, messages.get(DiagnosisType.RESERVED.name()).intValue()); + assertEquals(1, messages.get(DiagnosisType.UNKNOWN.name()).intValue()); + } + + @Test + public void parseInvalidMagicGzipFile() throws Exception { + File gzipFile = new File("src/test/resources/gzip/invalid-magic.gz"); + assertTrue(gzipFile.isFile()); + + GzipModule gzm = new GzipModule(); + RepInfo info = new RepInfo(gzipFile.getAbsolutePath()); + gzm.parse(new FileInputStream(gzipFile), info, 0); + + assertEquals(RepInfo.FALSE, info.getWellFormed()); + assertEquals(RepInfo.FALSE, info.getValid()); + assertTrue(info.getSigMatch().isEmpty()); + + // Validate the failures. + assertEquals(1, info.getMessage().size()); + Map messages = extractMessages(info.getMessage()); + assertEquals(1, messages.size()); + assertEquals(1, messages.get(DiagnosisType.INVALID_EXPECTED.name()).intValue()); + } + + @Test + public void parseInvalidTruncatedGzipFile() throws Exception { + File gzipFile = new File("src/test/resources/gzip/invalid-truncated.gz"); + assertTrue(gzipFile.isFile()); + + GzipModule gzm = new GzipModule(); + RepInfo info = new RepInfo(gzipFile.getAbsolutePath()); + gzm.parse(new FileInputStream(gzipFile), info, 0); + + assertEquals(RepInfo.FALSE, info.getWellFormed()); + assertEquals(RepInfo.FALSE, info.getValid()); + assertTrue(info.getSigMatch().isEmpty()); + + // Validate the failures. + assertEquals(1, info.getMessage().size()); + Map messages = extractMessages(info.getMessage()); + for (Map.Entry e : messages.entrySet()) { + System.out.println(e.getKey() + " : " + e.getValue()); } - - @Test - public void parseInvalidEntriesGzipFile() throws Exception { - File gzipFile = new File("src/test/resources/gzip/invalid-entries.gz"); - assertTrue( gzipFile.isFile() ); - - GzipModule gzm = new GzipModule(); - RepInfo info = new RepInfo(gzipFile.getAbsolutePath()); - gzm.parse(new FileInputStream(gzipFile), info, 0); - - assertEquals(RepInfo.FALSE, info.getWellFormed()); - assertEquals(RepInfo.FALSE, info.getValid()); - assertTrue(info.getSigMatch().isEmpty()); - - // Validate the failures. - assertEquals(5, info.getMessage().size()); - Map messages = extractMessages(info.getMessage()); - assertEquals(3, messages.size()); - assertEquals(2, messages.get(DiagnosisType.INVALID_EXPECTED.name()).intValue()); - assertEquals(2, messages.get(DiagnosisType.RESERVED.name()).intValue()); - assertEquals(1, messages.get(DiagnosisType.UNKNOWN.name()).intValue()); - } - - @Test - public void parseInvalidMagicGzipFile() throws Exception { - File gzipFile = new File("src/test/resources/gzip/invalid-magic.gz"); - assertTrue( gzipFile.isFile() ); - - GzipModule gzm = new GzipModule(); - RepInfo info = new RepInfo(gzipFile.getAbsolutePath()); - gzm.parse(new FileInputStream(gzipFile), info, 0); - - assertEquals(RepInfo.FALSE, info.getWellFormed()); - assertEquals(RepInfo.FALSE, info.getValid()); - assertTrue(info.getSigMatch().isEmpty()); - - // Validate the failures. - assertEquals(1, info.getMessage().size()); - Map messages = extractMessages(info.getMessage()); - assertEquals(1, messages.size()); - assertEquals(1, messages.get(DiagnosisType.INVALID_EXPECTED.name()).intValue()); - } - - @Test - public void parseInvalidTruncatedGzipFile() throws Exception { - File gzipFile = new File("src/test/resources/gzip/invalid-truncated.gz"); - assertTrue( gzipFile.isFile() ); - - GzipModule gzm = new GzipModule(); - RepInfo info = new RepInfo(gzipFile.getAbsolutePath()); - gzm.parse(new FileInputStream(gzipFile), info, 0); - - assertEquals(RepInfo.FALSE, info.getWellFormed()); - assertEquals(RepInfo.FALSE, info.getValid()); - assertTrue(info.getSigMatch().isEmpty()); - - // Validate the failures. - assertEquals(1, info.getMessage().size()); - Map messages = extractMessages(info.getMessage()); - for(Map.Entry e : messages.entrySet()) { - System.out.println(e.getKey() + " : " + e.getValue()); - } - assertEquals(1, messages.size()); - Iterator messageKeyIterator = messages.keySet().iterator(); - String errorMessage = messageKeyIterator.next(); - assertNotNull(errorMessage); -// assertTrue("java.util.zip.DataFormatException"); - assertTrue(errorMessage + " should contain " + DataFormatException.class.toString(), errorMessage.contains(DataFormatException.class.getCanonicalName())); - assertFalse(messageKeyIterator.hasNext()); + assertEquals(1, messages.size()); + Iterator messageKeyIterator = messages.keySet().iterator(); + String errorMessage = messageKeyIterator.next(); + assertNotNull(errorMessage); + // assertTrue("java.util.zip.DataFormatException"); + assertTrue( + errorMessage + " should contain " + DataFormatException.class.toString(), + errorMessage.contains(DataFormatException.class.getCanonicalName())); + assertFalse(messageKeyIterator.hasNext()); + } + + private static Map extractMessages(Collection messages) { + Map res = new HashMap<>(); + for (Message m : messages) { + if (res.containsKey(m.getMessage())) { + res.put(m.getMessage(), Integer.valueOf(res.get(m.getMessage()).intValue() + 1)); + } else { + res.put(m.getMessage(), Integer.valueOf(1)); + } } - - private static Map extractMessages(Collection messages) { - Map res = new HashMap<>(); - for(Message m : messages) { - if(res.containsKey(m.getMessage())) { - res.put(m.getMessage(), Integer.valueOf(res.get(m.getMessage()).intValue() + 1)); - } else { - res.put(m.getMessage(), Integer.valueOf(1)); - } - } - return res; - } + return res; + } } diff --git a/jhove-ext-modules/src/test/java/edu/harvard/hul/ois/jhove/module/WarcModuleTest.java b/jhove-ext-modules/src/test/java/edu/harvard/hul/ois/jhove/module/WarcModuleTest.java index 2565e64c2..f4ccfabad 100644 --- a/jhove-ext-modules/src/test/java/edu/harvard/hul/ois/jhove/module/WarcModuleTest.java +++ b/jhove-ext-modules/src/test/java/edu/harvard/hul/ois/jhove/module/WarcModuleTest.java @@ -2,6 +2,8 @@ import static org.junit.Assert.*; +import edu.harvard.hul.ois.jhove.Message; +import edu.harvard.hul.ois.jhove.RepInfo; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; @@ -11,1041 +13,1039 @@ import java.util.Collection; import java.util.HashMap; import java.util.Map; - import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; import org.jwat.common.DiagnosisType; -import edu.harvard.hul.ois.jhove.Message; -import edu.harvard.hul.ois.jhove.RepInfo; - @RunWith(JUnit4.class) public class WarcModuleTest { - @Test - public void parseValidUTF8File() throws Exception { - File warcFile = new File("src/test/resources/warc/valid-warcfile-utf8.warc"); - assertTrue( warcFile.isFile() ); - - WarcModule wm = new WarcModule(); - RepInfo info = new RepInfo(warcFile.getAbsolutePath()); - wm.parse(new FileInputStream(warcFile), info, 0); - - wellFormedCheck(info, wm); - - assertEquals(0, info.getMessage().size()); - } + @Test + public void parseValidUTF8File() throws Exception { + File warcFile = new File("src/test/resources/warc/valid-warcfile-utf8.warc"); + assertTrue(warcFile.isFile()); + + WarcModule wm = new WarcModule(); + RepInfo info = new RepInfo(warcFile.getAbsolutePath()); + wm.parse(new FileInputStream(warcFile), info, 0); + + wellFormedCheck(info, wm); + + assertEquals(0, info.getMessage().size()); + } + + @Test + public void checkSignatureValidUTF8File() throws Exception { + File warcFile = new File("src/test/resources/warc/valid-warcfile-utf8.warc"); + + WarcModule wm = new WarcModule(); + RepInfo info = new RepInfo(warcFile.getAbsolutePath()); + wm.checkSignatures(null, new FileInputStream(warcFile), info); + + assertEquals(RepInfo.TRUE, info.getWellFormed()); + assertEquals(WarcModule.class, info.getModule().getClass()); + assertEquals(Arrays.asList(wm.getName()), info.getSigMatch()); + } + + @Test + public void parseInvalidEmptyFile() throws Exception { + File warcFile = new File("src/test/resources/warc/invalid-empty.warc"); + + WarcModule wm = new WarcModule(); + RepInfo info = new RepInfo(warcFile.getAbsolutePath()); + wm.parse(new FileInputStream(warcFile), info, 0); + + invalidCheck(info); + + assertEquals(1, info.getMessage().size()); + Map messages = extractMessages(info.getMessage()); + assertEquals(1, messages.size()); + assertEquals(1, messages.get(DiagnosisType.ERROR_EXPECTED.name()).intValue()); + } + + @Test + public void checkSignatureInvalidEmptyFile() throws Exception { + File warcFile = new File("src/test/resources/warc/invalid-empty.warc"); + assertNotNull("Checking Empty file is invalid"); + invalidWithClassCheck(warcFile); + } + + @Test + public void checkSignatureCompressWarc() throws Exception { + File warcFile = new File("src/test/resources/warc/valid-warcfile-upper-lower-case.warc.gz"); + + WarcModule wm = new WarcModule(); + RepInfo info = new RepInfo(warcFile.getAbsolutePath()); + wm.checkSignatures(null, new FileInputStream(warcFile), info); + + assertEquals(RepInfo.TRUE, info.getWellFormed()); + assertEquals(WarcModule.class, info.getModule().getClass()); + assertEquals(Arrays.asList(wm.getName()), info.getSigMatch()); + } + + @Test + public void checkSignatureWavNotWarc() throws Exception { + File warcFile = new File("src/test/resources/warc/sample3.wav"); + assertNotNull("Checking WAV doesn't trigger WARC sig"); + + invalidWithClassCheck(warcFile); + } + + @Test + public void parseInvalidWarcFileContentTypeRecommended() throws Exception { + File warcFile = + new File("src/test/resources/warc/invalid-warcfile-contenttype-recommended.warc"); + + RepInfo info = generalInvalidChecks(warcFile); + + assertEquals(7, info.getMessage().size()); + Map messages = extractMessages(info.getMessage()); + assertEquals(1, messages.size()); + assertEquals(7, messages.get(DiagnosisType.RECOMMENDED_MISSING.name()).intValue()); + } + + @Test + public void parseInvalidWarcFileContentTypeWarcInfoRecommended() throws Exception { + File warcFile = + new File("src/test/resources/warc/invalid-warcfile-contenttype-warcinfo-recommended.warc"); + + RepInfo info = generalInvalidChecks(warcFile); + + assertEquals(1, info.getMessage().size()); + Map messages = extractMessages(info.getMessage()); + assertEquals(1, messages.size()); + assertEquals(1, messages.get(DiagnosisType.RECOMMENDED.name()).intValue()); + } + + @Test + public void parseInvalidWarcFileDigestField() throws Exception { + File warcFile = new File("src/test/resources/warc/invalid-warcfile-digest-fields.warc"); + + RepInfo info = generalInvalidChecks(warcFile); + + assertEquals(8, info.getMessage().size()); + Map messages = extractMessages(info.getMessage()); + assertEquals(1, messages.size()); + assertEquals(8, messages.get(DiagnosisType.INVALID_EXPECTED.name()).intValue()); + } + + @Test + public void parseInvalidWarcFileDuplicateField() throws Exception { + File warcFile = new File("src/test/resources/warc/invalid-warcfile-duplicate-fields.warc"); + + RepInfo info = generalInvalidChecks(warcFile); + + assertEquals(6, info.getMessage().size()); + Map messages = extractMessages(info.getMessage()); + assertEquals(1, messages.size()); + assertEquals(6, messages.get(DiagnosisType.DUPLICATE.name()).intValue()); + } + + @Test + public void parseInvalidWarcFileFieldsEmpty() throws Exception { + File warcFile = new File("src/test/resources/warc/invalid-warcfile-fields-empty.warc"); + + RepInfo info = generalInvalidChecks(warcFile); + + assertEquals(16, info.getMessage().size()); + Map messages = extractMessages(info.getMessage()); + assertEquals(4, messages.size()); + assertEquals(4, messages.get(DiagnosisType.REQUIRED_INVALID.name()).intValue()); + assertEquals(1, messages.get(DiagnosisType.INVALID_EXPECTED.name()).intValue()); + assertEquals(2, messages.get(DiagnosisType.INVALID.name()).intValue()); + assertEquals(9, messages.get(DiagnosisType.EMPTY.name()).intValue()); + } + + @Test + public void parseInvalidWarcFileFieldsInvalidFormat() throws Exception { + File warcFile = new File("src/test/resources/warc/invalid-warcfile-fields-invalidformat.warc"); + + RepInfo info = generalInvalidChecks(warcFile); + + assertEquals(15, info.getMessage().size()); + Map messages = extractMessages(info.getMessage()); + assertEquals(3, messages.size()); + assertEquals(4, messages.get(DiagnosisType.REQUIRED_INVALID.name()).intValue()); + assertEquals(9, messages.get(DiagnosisType.INVALID_EXPECTED.name()).intValue()); + assertEquals(2, messages.get(DiagnosisType.INVALID.name()).intValue()); + } + + @Test + public void parseInvalidWarcFileFieldsMissing() throws Exception { + File warcFile = new File("src/test/resources/warc/invalid-warcfile-fields-missing.warc"); + + RepInfo info = generalInvalidChecks(warcFile); + + assertEquals(24, info.getMessage().size()); + Map messages = extractMessages(info.getMessage()); + assertEquals(3, messages.size()); + assertEquals(4, messages.get(DiagnosisType.REQUIRED_INVALID.name()).intValue()); + assertEquals(1, messages.get(DiagnosisType.INVALID_EXPECTED.name()).intValue()); + assertEquals(19, messages.get(DiagnosisType.EMPTY.name()).intValue()); + } + + @Test + public void parseInvalidWarcFileLonelyContinuation() throws Exception { + File warcFile = new File("src/test/resources/warc/invalid-warcfile-lonely-continuation.warc"); + + RepInfo info = generalInvalidChecks(warcFile); + + assertEquals(6, info.getMessage().size()); + Map messages = extractMessages(info.getMessage()); + assertEquals(1, messages.size()); + assertEquals(6, messages.get(DiagnosisType.REQUIRED_INVALID.name()).intValue()); + } + + @Test + public void parseInvalidWarcFileLonelyMonkeysLfLineEndings() throws Exception { + File warcFile = + new File("src/test/resources/warc/invalid-warcfile-lonely-monkeys-lf-line-endings.warc"); + + RepInfo info = generalInvalidChecks(warcFile); + + assertEquals(5, info.getMessage().size()); + Map messages = extractMessages(info.getMessage()); + assertEquals(3, messages.size()); + assertEquals(3, messages.get(DiagnosisType.REQUIRED_INVALID.name()).intValue()); + assertEquals(1, messages.get(DiagnosisType.UNKNOWN.name()).intValue()); + assertEquals(1, messages.get(DiagnosisType.ERROR_EXPECTED.name()).intValue()); + } + + @Test + public void parseInvalidWarcFileLonelyRequestResponseResourceConversion() throws Exception { + File warcFile = + new File( + "src/test/resources/warc/invalid-warcfile-lonely-request-response-resource-conversion.warc"); + + RepInfo info = generalInvalidChecks(warcFile); + + assertEquals(16, info.getMessage().size()); + Map messages = extractMessages(info.getMessage()); + assertEquals(1, messages.size()); + assertEquals(16, messages.get(DiagnosisType.REQUIRED_INVALID.name()).intValue()); + } + + @Test + public void parseInvalidWarcFileLonelyRevisit() throws Exception { + File warcFile = new File("src/test/resources/warc/invalid-warcfile-lonely-revisit.warc"); + + RepInfo info = generalInvalidChecks(warcFile); + + assertEquals(5, info.getMessage().size()); + Map messages = extractMessages(info.getMessage()); + assertEquals(1, messages.size()); + assertEquals(5, messages.get(DiagnosisType.REQUIRED_INVALID.name()).intValue()); + } + + @Test + public void parseInvalidWarcFileLonelyWarcInfoMetadata() throws Exception { + File warcFile = + new File("src/test/resources/warc/invalid-warcfile-lonely-warcinfo-metadata.warc"); + + RepInfo info = generalInvalidChecks(warcFile); + + assertEquals(6, info.getMessage().size()); + Map messages = extractMessages(info.getMessage()); + assertEquals(1, messages.size()); + assertEquals(6, messages.get(DiagnosisType.REQUIRED_INVALID.name()).intValue()); + } + + @Test + public void parseInvalidWarcFileSegmentNumberContinuation() throws Exception { + File warcFile = + new File("src/test/resources/warc/invalid-warcfile-segment-number-continuation.warc"); + + RepInfo info = generalInvalidChecks(warcFile); + + assertEquals(1, info.getMessage().size()); + Map messages = extractMessages(info.getMessage()); + assertEquals(1, messages.size()); + assertEquals(1, messages.get(DiagnosisType.INVALID_EXPECTED.name()).intValue()); + } + + @Test + public void parseInvalidWarcFileSegmentNumberResponse() throws Exception { + File warcFile = + new File("src/test/resources/warc/invalid-warcfile-segment-number-response.warc"); + + RepInfo info = generalInvalidChecks(warcFile); + + assertEquals(3, info.getMessage().size()); + Map messages = extractMessages(info.getMessage()); + assertEquals(1, messages.size()); + assertEquals(3, messages.get(DiagnosisType.INVALID_EXPECTED.name()).intValue()); + } + + @Test + public void parseInvalidWarcHeaderFieldPolicy1() throws Exception { + File warcFile = new File("src/test/resources/warc/invalid-warcheaderfieldpolicy-1.warc"); + + RepInfo info = generalInvalidChecks(warcFile); + + assertEquals(5, info.getMessage().size()); + Map messages = extractMessages(info.getMessage()); + assertEquals(3, messages.size()); + assertEquals(1, messages.get(DiagnosisType.UNKNOWN.name()).intValue()); + assertEquals(3, messages.get(DiagnosisType.REQUIRED_INVALID.name()).intValue()); + assertEquals(1, messages.get(DiagnosisType.INVALID_EXPECTED.name()).intValue()); + } + + @Test + public void parseInvalidWarcHeaderFieldPolicy2() throws Exception { + File warcFile = new File("src/test/resources/warc/invalid-warcheaderfieldpolicy-2.warc"); + + RepInfo info = generalInvalidChecks(warcFile); + + assertEquals(1, info.getMessage().size()); + Map messages = extractMessages(info.getMessage()); + assertEquals(1, messages.size()); + assertEquals(1, messages.get(DiagnosisType.INVALID_EXPECTED.name()).intValue()); + } + + @Test + public void parseInvalidWarcHeaderFieldPolicy3() throws Exception { + File warcFile = new File("src/test/resources/warc/invalid-warcheaderfieldpolicy-3.warc"); + + RepInfo info = generalInvalidChecks(warcFile); + + assertEquals(3, info.getMessage().size()); + Map messages = extractMessages(info.getMessage()); + assertEquals(3, messages.size()); + assertEquals(1, messages.get(DiagnosisType.ERROR.name()).intValue()); + invalidDataCheck(messages); + } + + @Test + public void parseInvalidWarcHeaderFieldPolicy4() throws Exception { + File warcFile = new File("src/test/resources/warc/invalid-warcheaderfieldpolicy-4.warc"); + + RepInfo info = generalInvalidChecks(warcFile); + + assertEquals(6, info.getMessage().size()); + Map messages = extractMessages(info.getMessage()); + assertEquals(5, messages.size()); + assertEquals(1, messages.get(DiagnosisType.ERROR.name()).intValue()); + assertEquals(1, messages.get(DiagnosisType.UNKNOWN.name()).intValue()); + assertEquals(1, messages.get(DiagnosisType.REQUIRED_INVALID.name()).intValue()); + assertEquals(1, messages.get(DiagnosisType.INVALID_DATA.name()).intValue()); + assertEquals(2, messages.get(DiagnosisType.INVALID_EXPECTED.name()).intValue()); + } + + @Test + public void parseInvalidWarcHeaderFieldPolicy5() throws Exception { + File warcFile = new File("src/test/resources/warc/invalid-warcheaderfieldpolicy-5.warc"); + + RepInfo info = generalInvalidChecks(warcFile); + + assertEquals(3, info.getMessage().size()); + Map messages = extractMessages(info.getMessage()); + assertEquals(3, messages.size()); + assertEquals(1, messages.get(DiagnosisType.RECOMMENDED_MISSING.name()).intValue()); + invalidDataCheck(messages); + } + + @Test + public void parseInvalidWarcHeaderFieldPolicy6() throws Exception { + File warcFile = new File("src/test/resources/warc/invalid-warcheaderfieldpolicy-6.warc"); + + RepInfo info = generalInvalidChecks(warcFile); + + assertEquals(2, info.getMessage().size()); + Map messages = extractMessages(info.getMessage()); + assertEquals(2, messages.size()); + invalidDataCheck(messages); + } + + @Test + public void parseInvalidWarcHeaderFieldPolicy7() throws Exception { + File warcFile = new File("src/test/resources/warc/invalid-warcheaderfieldpolicy-7.warc"); + + RepInfo info = generalInvalidChecks(warcFile); + + assertEquals(4, info.getMessage().size()); + Map messages = extractMessages(info.getMessage()); + assertEquals(4, messages.size()); + assertEquals(1, messages.get(DiagnosisType.REQUIRED_INVALID.name()).intValue()); + assertEquals(1, messages.get(DiagnosisType.RECOMMENDED_MISSING.name()).intValue()); + invalidDataCheck(messages); + } + + @Test + public void parseInvalidWarcHeaderFieldPolicy8() throws Exception { + File warcFile = new File("src/test/resources/warc/invalid-warcheaderfieldpolicy-8.warc"); + + RepInfo info = generalInvalidChecks(warcFile); + + assertEquals(5, info.getMessage().size()); + Map messages = extractMessages(info.getMessage()); + assertEquals(5, messages.size()); + assertEquals(1, messages.get(DiagnosisType.REQUIRED_INVALID.name()).intValue()); + invalidDataCheck(messages); + assertEquals(1, messages.get(DiagnosisType.EMPTY.name()).intValue()); + assertEquals(1, messages.get(DiagnosisType.RECOMMENDED_MISSING.name()).intValue()); + } + + @Test + public void parseInvalidWarcHeaderFieldPolicy9() throws Exception { + File warcFile = new File("src/test/resources/warc/invalid-warcheaderfieldpolicy-9.warc"); + + RepInfo info = generalInvalidChecks(warcFile); + + assertEquals(2, info.getMessage().size()); + Map messages = extractMessages(info.getMessage()); + assertEquals(2, messages.size()); + assertEquals(1, messages.get(DiagnosisType.RECOMMENDED.name()).intValue()); + assertEquals(1, messages.get(DiagnosisType.INVALID_EXPECTED.name()).intValue()); + } + + @Test + public void parseInvalidWarcHeaderFieldPolicy10() throws Exception { + File warcFile = new File("src/test/resources/warc/invalid-warcheaderfieldpolicy-10.warc"); + + RepInfo info = generalInvalidChecks(warcFile); + + assertEquals(2, info.getMessage().size()); + Map messages = extractMessages(info.getMessage()); + assertEquals(2, messages.size()); + assertEquals(1, messages.get(DiagnosisType.RECOMMENDED.name()).intValue()); + assertEquals(1, messages.get(DiagnosisType.INVALID_EXPECTED.name()).intValue()); + } + + @Test + public void parseInvalidWarcHeaderFieldPolicy11() throws Exception { + File warcFile = new File("src/test/resources/warc/invalid-warcheaderfieldpolicy-11.warc"); + + RepInfo info = generalInvalidChecks(warcFile); + + assertEquals(3, info.getMessage().size()); + Map messages = extractMessages(info.getMessage()); + assertEquals(3, messages.size()); + assertEquals(1, messages.get(DiagnosisType.ERROR.name()).intValue()); + invalidDataCheck(messages); + } + + @Test + public void parseInvalidWarcHeaderFieldPolicy12() throws Exception { + File warcFile = new File("src/test/resources/warc/invalid-warcheaderfieldpolicy-12.warc"); + + RepInfo info = generalInvalidChecks(warcFile); + + assertEquals(3, info.getMessage().size()); + Map messages = extractMessages(info.getMessage()); + assertEquals(3, messages.size()); + assertEquals(1, messages.get(DiagnosisType.ERROR.name()).intValue()); + invalidDataCheck(messages); + } + + @Test + public void parseInvalidWarcHeaderFieldPolicy13() throws Exception { + File warcFile = new File("src/test/resources/warc/invalid-warcheaderfieldpolicy-13.warc"); + + RepInfo info = generalInvalidChecks(warcFile); + + assertEquals(4, info.getMessage().size()); + Map messages = extractMessages(info.getMessage()); + assertEquals(3, messages.size()); + assertEquals(1, messages.get(DiagnosisType.ERROR.name()).intValue()); + assertEquals(1, messages.get(DiagnosisType.INVALID_DATA.name()).intValue()); + assertEquals(2, messages.get(DiagnosisType.INVALID_EXPECTED.name()).intValue()); + } + + @Test + public void parseInvalidWarcHeaderFieldPolicy14() throws Exception { + File warcFile = new File("src/test/resources/warc/invalid-warcheaderfieldpolicy-14.warc"); + + RepInfo info = generalInvalidChecks(warcFile); + + assertEquals(3, info.getMessage().size()); + Map messages = extractMessages(info.getMessage()); + assertEquals(3, messages.size()); + invalidDataCheck(messages); + } + + @Test + public void parseInvalidWarcHeaderFieldPolicy15() throws Exception { + File warcFile = new File("src/test/resources/warc/invalid-warcheaderfieldpolicy-15.warc"); + + RepInfo info = generalInvalidChecks(warcFile); + + assertEquals(3, info.getMessage().size()); + Map messages = extractMessages(info.getMessage()); + assertEquals(2, messages.size()); + assertEquals(1, messages.get(DiagnosisType.INVALID_DATA.name()).intValue()); + assertEquals(2, messages.get(DiagnosisType.INVALID_EXPECTED.name()).intValue()); + } + + @Test + public void parseInvalidWarcHeaderFieldPolicy16() throws Exception { + File warcFile = new File("src/test/resources/warc/invalid-warcheaderfieldpolicy-16.warc"); + + RepInfo info = generalInvalidChecks(warcFile); + + assertEquals(2, info.getMessage().size()); + Map messages = extractMessages(info.getMessage()); + assertEquals(2, messages.size()); + invalidDataCheck(messages); + } + + @Test + public void parseInvalidWarcHeaderFieldPolicy17() throws Exception { + File warcFile = new File("src/test/resources/warc/invalid-warcheaderfieldpolicy-17.warc"); + + RepInfo info = generalInvalidChecks(warcFile); + + assertEquals(2, info.getMessage().size()); + Map messages = extractMessages(info.getMessage()); + assertEquals(2, messages.size()); + invalidDataCheck(messages); + } + + @Test + public void parseInvalidWarcHeaderVersion1() throws Exception { + File warcFile = new File("src/test/resources/warc/invalid-warcheaderversion-1.warc"); - @Test - public void checkSignatureValidUTF8File() throws Exception { - File warcFile = new File("src/test/resources/warc/valid-warcfile-utf8.warc"); - - WarcModule wm = new WarcModule(); - RepInfo info = new RepInfo(warcFile.getAbsolutePath()); - wm.checkSignatures(null, new FileInputStream(warcFile), info); - - assertEquals(RepInfo.TRUE, info.getWellFormed()); - assertEquals(WarcModule.class, info.getModule().getClass()); - assertEquals(Arrays.asList(wm.getName()), info.getSigMatch()); - } - - @Test - public void parseInvalidEmptyFile() throws Exception { - File warcFile = new File("src/test/resources/warc/invalid-empty.warc"); - - WarcModule wm = new WarcModule(); - RepInfo info = new RepInfo(warcFile.getAbsolutePath()); - wm.parse(new FileInputStream(warcFile), info, 0); - - invalidCheck(info); - - assertEquals(1, info.getMessage().size()); - Map messages = extractMessages(info.getMessage()); - assertEquals(1, messages.size()); - assertEquals(1, messages.get(DiagnosisType.ERROR_EXPECTED.name()).intValue()); - } - - @Test - public void checkSignatureInvalidEmptyFile() throws Exception { - File warcFile = new File("src/test/resources/warc/invalid-empty.warc"); - assertNotNull("Checking Empty file is invalid"); - invalidWithClassCheck(warcFile); - } - - @Test - public void checkSignatureCompressWarc() throws Exception { - File warcFile = new File("src/test/resources/warc/valid-warcfile-upper-lower-case.warc.gz"); - - WarcModule wm = new WarcModule(); - RepInfo info = new RepInfo(warcFile.getAbsolutePath()); - wm.checkSignatures(null, new FileInputStream(warcFile), info); - - assertEquals(RepInfo.TRUE, info.getWellFormed()); - assertEquals(WarcModule.class, info.getModule().getClass()); - assertEquals(Arrays.asList(wm.getName()), info.getSigMatch()); - } - - @Test - public void checkSignatureWavNotWarc() throws Exception { - File warcFile = new File("src/test/resources/warc/sample3.wav"); - assertNotNull("Checking WAV doesn't trigger WARC sig"); - - invalidWithClassCheck(warcFile); - } - - @Test - public void parseInvalidWarcFileContentTypeRecommended() throws Exception { - File warcFile = new File("src/test/resources/warc/invalid-warcfile-contenttype-recommended.warc"); - - RepInfo info = generalInvalidChecks(warcFile); - - assertEquals(7, info.getMessage().size()); - Map messages = extractMessages(info.getMessage()); - assertEquals(1, messages.size()); - assertEquals(7, messages.get(DiagnosisType.RECOMMENDED_MISSING.name()).intValue()); - } - - @Test - public void parseInvalidWarcFileContentTypeWarcInfoRecommended() throws Exception { - File warcFile = new File("src/test/resources/warc/invalid-warcfile-contenttype-warcinfo-recommended.warc"); - - RepInfo info = generalInvalidChecks(warcFile); - - assertEquals(1, info.getMessage().size()); - Map messages = extractMessages(info.getMessage()); - assertEquals(1, messages.size()); - assertEquals(1, messages.get(DiagnosisType.RECOMMENDED.name()).intValue()); - } - - @Test - public void parseInvalidWarcFileDigestField() throws Exception { - File warcFile = new File("src/test/resources/warc/invalid-warcfile-digest-fields.warc"); - - RepInfo info = generalInvalidChecks(warcFile); - - assertEquals(8, info.getMessage().size()); - Map messages = extractMessages(info.getMessage()); - assertEquals(1, messages.size()); - assertEquals(8, messages.get(DiagnosisType.INVALID_EXPECTED.name()).intValue()); - } - - @Test - public void parseInvalidWarcFileDuplicateField() throws Exception { - File warcFile = new File("src/test/resources/warc/invalid-warcfile-duplicate-fields.warc"); - - RepInfo info = generalInvalidChecks(warcFile); - - assertEquals(6, info.getMessage().size()); - Map messages = extractMessages(info.getMessage()); - assertEquals(1, messages.size()); - assertEquals(6, messages.get(DiagnosisType.DUPLICATE.name()).intValue()); - } - - @Test - public void parseInvalidWarcFileFieldsEmpty() throws Exception { - File warcFile = new File("src/test/resources/warc/invalid-warcfile-fields-empty.warc"); - - RepInfo info = generalInvalidChecks(warcFile); - - assertEquals(16, info.getMessage().size()); - Map messages = extractMessages(info.getMessage()); - assertEquals(4, messages.size()); - assertEquals(4, messages.get(DiagnosisType.REQUIRED_INVALID.name()).intValue()); - assertEquals(1, messages.get(DiagnosisType.INVALID_EXPECTED.name()).intValue()); - assertEquals(2, messages.get(DiagnosisType.INVALID.name()).intValue()); - assertEquals(9, messages.get(DiagnosisType.EMPTY.name()).intValue()); - } - - @Test - public void parseInvalidWarcFileFieldsInvalidFormat() throws Exception { - File warcFile = new File("src/test/resources/warc/invalid-warcfile-fields-invalidformat.warc"); - - RepInfo info = generalInvalidChecks(warcFile); - - assertEquals(15, info.getMessage().size()); - Map messages = extractMessages(info.getMessage()); - assertEquals(3, messages.size()); - assertEquals(4, messages.get(DiagnosisType.REQUIRED_INVALID.name()).intValue()); - assertEquals(9, messages.get(DiagnosisType.INVALID_EXPECTED.name()).intValue()); - assertEquals(2, messages.get(DiagnosisType.INVALID.name()).intValue()); - } - - @Test - public void parseInvalidWarcFileFieldsMissing() throws Exception { - File warcFile = new File("src/test/resources/warc/invalid-warcfile-fields-missing.warc"); - - RepInfo info = generalInvalidChecks(warcFile); - - assertEquals(24, info.getMessage().size()); - Map messages = extractMessages(info.getMessage()); - assertEquals(3, messages.size()); - assertEquals(4, messages.get(DiagnosisType.REQUIRED_INVALID.name()).intValue()); - assertEquals(1, messages.get(DiagnosisType.INVALID_EXPECTED.name()).intValue()); - assertEquals(19, messages.get(DiagnosisType.EMPTY.name()).intValue()); - } - - @Test - public void parseInvalidWarcFileLonelyContinuation() throws Exception { - File warcFile = new File("src/test/resources/warc/invalid-warcfile-lonely-continuation.warc"); - - RepInfo info = generalInvalidChecks(warcFile); - - assertEquals(6, info.getMessage().size()); - Map messages = extractMessages(info.getMessage()); - assertEquals(1, messages.size()); - assertEquals(6, messages.get(DiagnosisType.REQUIRED_INVALID.name()).intValue()); - } - - @Test - public void parseInvalidWarcFileLonelyMonkeysLfLineEndings() throws Exception { - File warcFile = new File("src/test/resources/warc/invalid-warcfile-lonely-monkeys-lf-line-endings.warc"); - - RepInfo info = generalInvalidChecks(warcFile); - - assertEquals(5, info.getMessage().size()); - Map messages = extractMessages(info.getMessage()); - assertEquals(3, messages.size()); - assertEquals(3, messages.get(DiagnosisType.REQUIRED_INVALID.name()).intValue()); - assertEquals(1, messages.get(DiagnosisType.UNKNOWN.name()).intValue()); - assertEquals(1, messages.get(DiagnosisType.ERROR_EXPECTED.name()).intValue()); - } - - @Test - public void parseInvalidWarcFileLonelyRequestResponseResourceConversion() throws Exception { - File warcFile = new File("src/test/resources/warc/invalid-warcfile-lonely-request-response-resource-conversion.warc"); - - RepInfo info = generalInvalidChecks(warcFile); - - assertEquals(16, info.getMessage().size()); - Map messages = extractMessages(info.getMessage()); - assertEquals(1, messages.size()); - assertEquals(16, messages.get(DiagnosisType.REQUIRED_INVALID.name()).intValue()); - } - - @Test - public void parseInvalidWarcFileLonelyRevisit() throws Exception { - File warcFile = new File("src/test/resources/warc/invalid-warcfile-lonely-revisit.warc"); - - RepInfo info = generalInvalidChecks(warcFile); - - assertEquals(5, info.getMessage().size()); - Map messages = extractMessages(info.getMessage()); - assertEquals(1, messages.size()); - assertEquals(5, messages.get(DiagnosisType.REQUIRED_INVALID.name()).intValue()); - } - - @Test - public void parseInvalidWarcFileLonelyWarcInfoMetadata() throws Exception { - File warcFile = new File("src/test/resources/warc/invalid-warcfile-lonely-warcinfo-metadata.warc"); - - RepInfo info = generalInvalidChecks(warcFile); - - assertEquals(6, info.getMessage().size()); - Map messages = extractMessages(info.getMessage()); - assertEquals(1, messages.size()); - assertEquals(6, messages.get(DiagnosisType.REQUIRED_INVALID.name()).intValue()); - } - - @Test - public void parseInvalidWarcFileSegmentNumberContinuation() throws Exception { - File warcFile = new File("src/test/resources/warc/invalid-warcfile-segment-number-continuation.warc"); - - RepInfo info = generalInvalidChecks(warcFile); - - assertEquals(1, info.getMessage().size()); - Map messages = extractMessages(info.getMessage()); - assertEquals(1, messages.size()); - assertEquals(1, messages.get(DiagnosisType.INVALID_EXPECTED.name()).intValue()); - } - - @Test - public void parseInvalidWarcFileSegmentNumberResponse() throws Exception { - File warcFile = new File("src/test/resources/warc/invalid-warcfile-segment-number-response.warc"); - - RepInfo info = generalInvalidChecks(warcFile); - - assertEquals(3, info.getMessage().size()); - Map messages = extractMessages(info.getMessage()); - assertEquals(1, messages.size()); - assertEquals(3, messages.get(DiagnosisType.INVALID_EXPECTED.name()).intValue()); - } - - @Test - public void parseInvalidWarcHeaderFieldPolicy1() throws Exception { - File warcFile = new File("src/test/resources/warc/invalid-warcheaderfieldpolicy-1.warc"); - - RepInfo info = generalInvalidChecks(warcFile); - - assertEquals(5, info.getMessage().size()); - Map messages = extractMessages(info.getMessage()); - assertEquals(3, messages.size()); - assertEquals(1, messages.get(DiagnosisType.UNKNOWN.name()).intValue()); - assertEquals(3, messages.get(DiagnosisType.REQUIRED_INVALID.name()).intValue()); - assertEquals(1, messages.get(DiagnosisType.INVALID_EXPECTED.name()).intValue()); - } - - @Test - public void parseInvalidWarcHeaderFieldPolicy2() throws Exception { - File warcFile = new File("src/test/resources/warc/invalid-warcheaderfieldpolicy-2.warc"); - - RepInfo info = generalInvalidChecks(warcFile); - - assertEquals(1, info.getMessage().size()); - Map messages = extractMessages(info.getMessage()); - assertEquals(1, messages.size()); - assertEquals(1, messages.get(DiagnosisType.INVALID_EXPECTED.name()).intValue()); - } - - @Test - public void parseInvalidWarcHeaderFieldPolicy3() throws Exception { - File warcFile = new File("src/test/resources/warc/invalid-warcheaderfieldpolicy-3.warc"); - - RepInfo info = generalInvalidChecks(warcFile); - - assertEquals(3, info.getMessage().size()); - Map messages = extractMessages(info.getMessage()); - assertEquals(3, messages.size()); - assertEquals(1, messages.get(DiagnosisType.ERROR.name()).intValue()); - invalidDataCheck(messages); - } - - @Test - public void parseInvalidWarcHeaderFieldPolicy4() throws Exception { - File warcFile = new File("src/test/resources/warc/invalid-warcheaderfieldpolicy-4.warc"); - - RepInfo info = generalInvalidChecks(warcFile); - - assertEquals(6, info.getMessage().size()); - Map messages = extractMessages(info.getMessage()); - assertEquals(5, messages.size()); - assertEquals(1, messages.get(DiagnosisType.ERROR.name()).intValue()); - assertEquals(1, messages.get(DiagnosisType.UNKNOWN.name()).intValue()); - assertEquals(1, messages.get(DiagnosisType.REQUIRED_INVALID.name()).intValue()); - assertEquals(1, messages.get(DiagnosisType.INVALID_DATA.name()).intValue()); - assertEquals(2, messages.get(DiagnosisType.INVALID_EXPECTED.name()).intValue()); - } - - @Test - public void parseInvalidWarcHeaderFieldPolicy5() throws Exception { - File warcFile = new File("src/test/resources/warc/invalid-warcheaderfieldpolicy-5.warc"); - - RepInfo info = generalInvalidChecks(warcFile); - - assertEquals(3, info.getMessage().size()); - Map messages = extractMessages(info.getMessage()); - assertEquals(3, messages.size()); - assertEquals(1, messages.get(DiagnosisType.RECOMMENDED_MISSING.name()).intValue()); - invalidDataCheck(messages); - } - - @Test - public void parseInvalidWarcHeaderFieldPolicy6() throws Exception { - File warcFile = new File("src/test/resources/warc/invalid-warcheaderfieldpolicy-6.warc"); - - RepInfo info = generalInvalidChecks(warcFile); - - assertEquals(2, info.getMessage().size()); - Map messages = extractMessages(info.getMessage()); - assertEquals(2, messages.size()); - invalidDataCheck(messages); - } - - @Test - public void parseInvalidWarcHeaderFieldPolicy7() throws Exception { - File warcFile = new File("src/test/resources/warc/invalid-warcheaderfieldpolicy-7.warc"); - - RepInfo info = generalInvalidChecks(warcFile); - - assertEquals(4, info.getMessage().size()); - Map messages = extractMessages(info.getMessage()); - assertEquals(4, messages.size()); - assertEquals(1, messages.get(DiagnosisType.REQUIRED_INVALID.name()).intValue()); - assertEquals(1, messages.get(DiagnosisType.RECOMMENDED_MISSING.name()).intValue()); - invalidDataCheck(messages); - } - - @Test - public void parseInvalidWarcHeaderFieldPolicy8() throws Exception { - File warcFile = new File("src/test/resources/warc/invalid-warcheaderfieldpolicy-8.warc"); - - RepInfo info = generalInvalidChecks(warcFile); - - assertEquals(5, info.getMessage().size()); - Map messages = extractMessages(info.getMessage()); - assertEquals(5, messages.size()); - assertEquals(1, messages.get(DiagnosisType.REQUIRED_INVALID.name()).intValue()); - invalidDataCheck(messages); - assertEquals(1, messages.get(DiagnosisType.EMPTY.name()).intValue()); - assertEquals(1, messages.get(DiagnosisType.RECOMMENDED_MISSING.name()).intValue()); - } - - @Test - public void parseInvalidWarcHeaderFieldPolicy9() throws Exception { - File warcFile = new File("src/test/resources/warc/invalid-warcheaderfieldpolicy-9.warc"); - - RepInfo info = generalInvalidChecks(warcFile); - - assertEquals(2, info.getMessage().size()); - Map messages = extractMessages(info.getMessage()); - assertEquals(2, messages.size()); - assertEquals(1, messages.get(DiagnosisType.RECOMMENDED.name()).intValue()); - assertEquals(1, messages.get(DiagnosisType.INVALID_EXPECTED.name()).intValue()); - } - - @Test - public void parseInvalidWarcHeaderFieldPolicy10() throws Exception { - File warcFile = new File("src/test/resources/warc/invalid-warcheaderfieldpolicy-10.warc"); - - RepInfo info = generalInvalidChecks(warcFile); - - assertEquals(2, info.getMessage().size()); - Map messages = extractMessages(info.getMessage()); - assertEquals(2, messages.size()); - assertEquals(1, messages.get(DiagnosisType.RECOMMENDED.name()).intValue()); - assertEquals(1, messages.get(DiagnosisType.INVALID_EXPECTED.name()).intValue()); - } - - @Test - public void parseInvalidWarcHeaderFieldPolicy11() throws Exception { - File warcFile = new File("src/test/resources/warc/invalid-warcheaderfieldpolicy-11.warc"); - - RepInfo info = generalInvalidChecks(warcFile); - - assertEquals(3, info.getMessage().size()); - Map messages = extractMessages(info.getMessage()); - assertEquals(3, messages.size()); - assertEquals(1, messages.get(DiagnosisType.ERROR.name()).intValue()); - invalidDataCheck(messages); - } - - @Test - public void parseInvalidWarcHeaderFieldPolicy12() throws Exception { - File warcFile = new File("src/test/resources/warc/invalid-warcheaderfieldpolicy-12.warc"); - - RepInfo info = generalInvalidChecks(warcFile); - - assertEquals(3, info.getMessage().size()); - Map messages = extractMessages(info.getMessage()); - assertEquals(3, messages.size()); - assertEquals(1, messages.get(DiagnosisType.ERROR.name()).intValue()); - invalidDataCheck(messages); - } - - @Test - public void parseInvalidWarcHeaderFieldPolicy13() throws Exception { - File warcFile = new File("src/test/resources/warc/invalid-warcheaderfieldpolicy-13.warc"); - - RepInfo info = generalInvalidChecks(warcFile); - - assertEquals(4, info.getMessage().size()); - Map messages = extractMessages(info.getMessage()); - assertEquals(3, messages.size()); - assertEquals(1, messages.get(DiagnosisType.ERROR.name()).intValue()); - assertEquals(1, messages.get(DiagnosisType.INVALID_DATA.name()).intValue()); - assertEquals(2, messages.get(DiagnosisType.INVALID_EXPECTED.name()).intValue()); - } - - @Test - public void parseInvalidWarcHeaderFieldPolicy14() throws Exception { - File warcFile = new File("src/test/resources/warc/invalid-warcheaderfieldpolicy-14.warc"); - - RepInfo info = generalInvalidChecks(warcFile); - - assertEquals(3, info.getMessage().size()); - Map messages = extractMessages(info.getMessage()); - assertEquals(3, messages.size()); - invalidDataCheck(messages); - } - - @Test - public void parseInvalidWarcHeaderFieldPolicy15() throws Exception { - File warcFile = new File("src/test/resources/warc/invalid-warcheaderfieldpolicy-15.warc"); - - RepInfo info = generalInvalidChecks(warcFile); - - assertEquals(3, info.getMessage().size()); - Map messages = extractMessages(info.getMessage()); - assertEquals(2, messages.size()); - assertEquals(1, messages.get(DiagnosisType.INVALID_DATA.name()).intValue()); - assertEquals(2, messages.get(DiagnosisType.INVALID_EXPECTED.name()).intValue()); - } - - @Test - public void parseInvalidWarcHeaderFieldPolicy16() throws Exception { - File warcFile = new File("src/test/resources/warc/invalid-warcheaderfieldpolicy-16.warc"); - - RepInfo info = generalInvalidChecks(warcFile); - - assertEquals(2, info.getMessage().size()); - Map messages = extractMessages(info.getMessage()); - assertEquals(2, messages.size()); - invalidDataCheck(messages); - } - - @Test - public void parseInvalidWarcHeaderFieldPolicy17() throws Exception { - File warcFile = new File("src/test/resources/warc/invalid-warcheaderfieldpolicy-17.warc"); - - RepInfo info = generalInvalidChecks(warcFile); - - assertEquals(2, info.getMessage().size()); - Map messages = extractMessages(info.getMessage()); - assertEquals(2, messages.size()); - invalidDataCheck(messages); - } - - @Test - public void parseInvalidWarcHeaderVersion1() throws Exception { - File warcFile = new File("src/test/resources/warc/invalid-warcheaderversion-1.warc"); - - RepInfo info = generalInvalidChecks(warcFile); - - unknownRequiredInvalidCheck(info); - } - - @Test - public void parseInvalidWarcHeaderVersion2() throws Exception { - File warcFile = new File("src/test/resources/warc/invalid-warcheaderversion-2.warc"); - - RepInfo info = generalInvalidChecks(warcFile); - - assertEquals(5, info.getMessage().size()); - Map messages = extractMessages(info.getMessage()); - assertEquals(2, messages.size()); - assertEquals(4, messages.get(DiagnosisType.REQUIRED_INVALID.name()).intValue()); - assertEquals(1, messages.get(DiagnosisType.INVALID_EXPECTED.name()).intValue()); - } - - @Test - public void parseInvalidWarcHeaderVersion3() throws Exception { - File warcFile = new File("src/test/resources/warc/invalid-warcheaderversion-3.warc"); - - RepInfo info = generalInvalidChecks(warcFile); - - assertEquals(5, info.getMessage().size()); - Map messages = extractMessages(info.getMessage()); - assertEquals(2, messages.size()); - assertEquals(4, messages.get(DiagnosisType.REQUIRED_INVALID.name()).intValue()); - assertEquals(1, messages.get(DiagnosisType.INVALID_EXPECTED.name()).intValue()); - } - - @Test - public void parseInvalidWarcHeaderVersion4() throws Exception { - File warcFile = new File("src/test/resources/warc/invalid-warcheaderversion-4.warc"); - - RepInfo info = generalInvalidChecks(warcFile); + RepInfo info = generalInvalidChecks(warcFile); - unknownRequiredInvalidCheck(info); - } - - @Test - public void parseInvalidWarcHeaderVersion5() throws Exception { - File warcFile = new File("src/test/resources/warc/invalid-warcheaderversion-5.warc"); + unknownRequiredInvalidCheck(info); + } - RepInfo info = generalInvalidChecks(warcFile); + @Test + public void parseInvalidWarcHeaderVersion2() throws Exception { + File warcFile = new File("src/test/resources/warc/invalid-warcheaderversion-2.warc"); - unknownRequiredInvalidCheck(info); - } + RepInfo info = generalInvalidChecks(warcFile); - @Test - public void parseInvalidWarcHeaderVersion6() throws Exception { - File warcFile = new File("src/test/resources/warc/invalid-warcheaderversion-6.warc"); + assertEquals(5, info.getMessage().size()); + Map messages = extractMessages(info.getMessage()); + assertEquals(2, messages.size()); + assertEquals(4, messages.get(DiagnosisType.REQUIRED_INVALID.name()).intValue()); + assertEquals(1, messages.get(DiagnosisType.INVALID_EXPECTED.name()).intValue()); + } - RepInfo info = generalInvalidChecks(warcFile); + @Test + public void parseInvalidWarcHeaderVersion3() throws Exception { + File warcFile = new File("src/test/resources/warc/invalid-warcheaderversion-3.warc"); - assertEquals(5, info.getMessage().size()); - Map messages = extractMessages(info.getMessage()); - assertEquals(2, messages.size()); - assertEquals(4, messages.get(DiagnosisType.REQUIRED_INVALID.name()).intValue()); - assertEquals(1, messages.get(DiagnosisType.INVALID_EXPECTED.name()).intValue()); - } + RepInfo info = generalInvalidChecks(warcFile); - @Test - public void parseInvalidWarcHeaderVersion7() throws Exception { - File warcFile = new File("src/test/resources/warc/invalid-warcheaderversion-7.warc"); + assertEquals(5, info.getMessage().size()); + Map messages = extractMessages(info.getMessage()); + assertEquals(2, messages.size()); + assertEquals(4, messages.get(DiagnosisType.REQUIRED_INVALID.name()).intValue()); + assertEquals(1, messages.get(DiagnosisType.INVALID_EXPECTED.name()).intValue()); + } - RepInfo info = generalInvalidChecks(warcFile); + @Test + public void parseInvalidWarcHeaderVersion4() throws Exception { + File warcFile = new File("src/test/resources/warc/invalid-warcheaderversion-4.warc"); - unknownRequiredInvalidCheck(info); - } + RepInfo info = generalInvalidChecks(warcFile); - @Test - public void parseInvalidWarcHeaderVersion8() throws Exception { - File warcFile = new File("src/test/resources/warc/invalid-warcheaderversion-8.warc"); + unknownRequiredInvalidCheck(info); + } - RepInfo info = generalInvalidChecks(warcFile); + @Test + public void parseInvalidWarcHeaderVersion5() throws Exception { + File warcFile = new File("src/test/resources/warc/invalid-warcheaderversion-5.warc"); - unknownRequiredInvalidCheck(info); - } + RepInfo info = generalInvalidChecks(warcFile); - @Test - public void parseInvalidWarcHeaderVersion9() throws Exception { - File warcFile = new File("src/test/resources/warc/invalid-warcheaderversion-9.warc"); + unknownRequiredInvalidCheck(info); + } - RepInfo info = generalInvalidChecks(warcFile); + @Test + public void parseInvalidWarcHeaderVersion6() throws Exception { + File warcFile = new File("src/test/resources/warc/invalid-warcheaderversion-6.warc"); - unknownRequiredInvalidCheck(info); - } + RepInfo info = generalInvalidChecks(warcFile); - @Test - public void parseInvalidWarcHeaderVersion10() throws Exception { - File warcFile = new File("src/test/resources/warc/invalid-warcheaderversion-10.warc"); + assertEquals(5, info.getMessage().size()); + Map messages = extractMessages(info.getMessage()); + assertEquals(2, messages.size()); + assertEquals(4, messages.get(DiagnosisType.REQUIRED_INVALID.name()).intValue()); + assertEquals(1, messages.get(DiagnosisType.INVALID_EXPECTED.name()).intValue()); + } - RepInfo info = generalInvalidChecks(warcFile); + @Test + public void parseInvalidWarcHeaderVersion7() throws Exception { + File warcFile = new File("src/test/resources/warc/invalid-warcheaderversion-7.warc"); - assertEquals(6, info.getMessage().size()); - Map messages = extractMessages(info.getMessage()); - invalidRequiredDataCheck(messages); - } + RepInfo info = generalInvalidChecks(warcFile); - @Test - public void parseInvalidWarcHeaderVersion11() throws Exception { - File warcFile = new File("src/test/resources/warc/invalid-warcheaderversion-11.warc"); + unknownRequiredInvalidCheck(info); + } - RepInfo info = generalInvalidChecks(warcFile); + @Test + public void parseInvalidWarcHeaderVersion8() throws Exception { + File warcFile = new File("src/test/resources/warc/invalid-warcheaderversion-8.warc"); - assertEquals(6, info.getMessage().size()); - Map messages = extractMessages(info.getMessage()); - invalidRequiredDataCheck(messages); - } + RepInfo info = generalInvalidChecks(warcFile); - @Test - public void parseInvalidWarcHeaderVersion12() throws Exception { - File warcFile = new File("src/test/resources/warc/invalid-warcheaderversion-12.warc"); + unknownRequiredInvalidCheck(info); + } - RepInfo info = generalInvalidChecks(warcFile); + @Test + public void parseInvalidWarcHeaderVersion9() throws Exception { + File warcFile = new File("src/test/resources/warc/invalid-warcheaderversion-9.warc"); - assertEquals(6, info.getMessage().size()); - Map messages = extractMessages(info.getMessage()); - invalidRequiredDataCheck(messages); - } + RepInfo info = generalInvalidChecks(warcFile); - @Test - public void parseInvalidWarcHeaderVersion13() throws Exception { - File warcFile = new File("src/test/resources/warc/invalid-warcheaderversion-13.warc"); + unknownRequiredInvalidCheck(info); + } - RepInfo info = generalInvalidChecks(warcFile); + @Test + public void parseInvalidWarcHeaderVersion10() throws Exception { + File warcFile = new File("src/test/resources/warc/invalid-warcheaderversion-10.warc"); - assertEquals(6, info.getMessage().size()); - Map messages = extractMessages(info.getMessage()); - invalidRequiredDataCheck(messages); - } + RepInfo info = generalInvalidChecks(warcFile); - @Test - public void parseInvalidWarcHeaderVersion14() throws Exception { - File warcFile = new File("src/test/resources/warc/invalid-warcheaderversion-14.warc"); + assertEquals(6, info.getMessage().size()); + Map messages = extractMessages(info.getMessage()); + invalidRequiredDataCheck(messages); + } - RepInfo info = generalInvalidChecks(warcFile); + @Test + public void parseInvalidWarcHeaderVersion11() throws Exception { + File warcFile = new File("src/test/resources/warc/invalid-warcheaderversion-11.warc"); - assertEquals(6, info.getMessage().size()); - Map messages = extractMessages(info.getMessage()); - invalidRequiredDataCheck(messages); - } + RepInfo info = generalInvalidChecks(warcFile); - @Test - public void parseInvalidWarcHeaderVersion15() throws Exception { - File warcFile = new File("src/test/resources/warc/invalid-warcheaderversion-15.warc"); + assertEquals(6, info.getMessage().size()); + Map messages = extractMessages(info.getMessage()); + invalidRequiredDataCheck(messages); + } - RepInfo info = generalInvalidChecks(warcFile); + @Test + public void parseInvalidWarcHeaderVersion12() throws Exception { + File warcFile = new File("src/test/resources/warc/invalid-warcheaderversion-12.warc"); - assertEquals(6, info.getMessage().size()); - Map messages = extractMessages(info.getMessage()); - invalidRequiredDataCheck(messages); - } + RepInfo info = generalInvalidChecks(warcFile); - @Test - public void parseInvalidWarcHeaderVersion16() throws Exception { - File warcFile = new File("src/test/resources/warc/invalid-warcheaderversion-16.warc"); + assertEquals(6, info.getMessage().size()); + Map messages = extractMessages(info.getMessage()); + invalidRequiredDataCheck(messages); + } - RepInfo info = generalInvalidChecks(warcFile); + @Test + public void parseInvalidWarcHeaderVersion13() throws Exception { + File warcFile = new File("src/test/resources/warc/invalid-warcheaderversion-13.warc"); - invalidErrorExpectedCheck(info); - } + RepInfo info = generalInvalidChecks(warcFile); - @Test - public void parseInvalidWarcHeaderVersion17() throws Exception { - File warcFile = new File("src/test/resources/warc/invalid-warcheaderversion-17.warc"); + assertEquals(6, info.getMessage().size()); + Map messages = extractMessages(info.getMessage()); + invalidRequiredDataCheck(messages); + } - RepInfo info = generalInvalidChecks(warcFile); + @Test + public void parseInvalidWarcHeaderVersion14() throws Exception { + File warcFile = new File("src/test/resources/warc/invalid-warcheaderversion-14.warc"); - invalidErrorExpectedCheck(info); - } + RepInfo info = generalInvalidChecks(warcFile); - @Test - public void parseInvalidWarcHeaderVersion18() throws Exception { - File warcFile = new File("src/test/resources/warc/invalid-warcheaderversion-18.warc"); + assertEquals(6, info.getMessage().size()); + Map messages = extractMessages(info.getMessage()); + invalidRequiredDataCheck(messages); + } - RepInfo info = generalInvalidChecks(warcFile); + @Test + public void parseInvalidWarcHeaderVersion15() throws Exception { + File warcFile = new File("src/test/resources/warc/invalid-warcheaderversion-15.warc"); - invalidErrorExpectedCheck(info); - } + RepInfo info = generalInvalidChecks(warcFile); - @Test - public void parseInvalidWarcHeaderVersion19() throws Exception { - File warcFile = new File("src/test/resources/warc/invalid-warcheaderversion-19.warc"); + assertEquals(6, info.getMessage().size()); + Map messages = extractMessages(info.getMessage()); + invalidRequiredDataCheck(messages); + } - RepInfo info = generalInvalidChecks(warcFile); + @Test + public void parseInvalidWarcHeaderVersion16() throws Exception { + File warcFile = new File("src/test/resources/warc/invalid-warcheaderversion-16.warc"); - invalidErrorExpectedCheck(info); - } + RepInfo info = generalInvalidChecks(warcFile); - @Test - public void parseInvalidWarcHeaderVersion20() throws Exception { - File warcFile = new File("src/test/resources/warc/invalid-warcheaderversion-20.warc"); + invalidErrorExpectedCheck(info); + } - RepInfo info = generalInvalidChecks(warcFile); + @Test + public void parseInvalidWarcHeaderVersion17() throws Exception { + File warcFile = new File("src/test/resources/warc/invalid-warcheaderversion-17.warc"); - invalidErrorExpectedCheck(info); - } + RepInfo info = generalInvalidChecks(warcFile); - @Test - public void parseInvalidWarcHeaderVersion21() throws Exception { - File warcFile = new File("src/test/resources/warc/invalid-warcheaderversion-21.warc"); + invalidErrorExpectedCheck(info); + } - RepInfo info = generalInvalidChecks(warcFile); + @Test + public void parseInvalidWarcHeaderVersion18() throws Exception { + File warcFile = new File("src/test/resources/warc/invalid-warcheaderversion-18.warc"); - assertEquals(5, info.getMessage().size()); - Map messages = extractMessages(info.getMessage()); - assertEquals(2, messages.size()); - assertEquals(4, messages.get(DiagnosisType.REQUIRED_INVALID.name()).intValue()); - assertEquals(1, messages.get(DiagnosisType.INVALID_EXPECTED.name()).intValue()); - } + RepInfo info = generalInvalidChecks(warcFile); - @Test - public void parseInvalidWarcHeaderVersion22() throws Exception { - File warcFile = new File("src/test/resources/warc/invalid-warcheaderversion-22.warc"); + invalidErrorExpectedCheck(info); + } - RepInfo info = generalInvalidChecks(warcFile); + @Test + public void parseInvalidWarcHeaderVersion19() throws Exception { + File warcFile = new File("src/test/resources/warc/invalid-warcheaderversion-19.warc"); - assertEquals(5, info.getMessage().size()); - Map messages = extractMessages(info.getMessage()); - assertEquals(2, messages.size()); - assertEquals(4, messages.get(DiagnosisType.REQUIRED_INVALID.name()).intValue()); - assertEquals(1, messages.get(DiagnosisType.INVALID_EXPECTED.name()).intValue()); - } + RepInfo info = generalInvalidChecks(warcFile); - @Test - public void parseInvalidWarcHeaderVersion23() throws Exception { - File warcFile = new File("src/test/resources/warc/invalid-warcheaderversion-23.warc"); + invalidErrorExpectedCheck(info); + } - RepInfo info = generalInvalidChecks(warcFile); + @Test + public void parseInvalidWarcHeaderVersion20() throws Exception { + File warcFile = new File("src/test/resources/warc/invalid-warcheaderversion-20.warc"); - assertEquals(5, info.getMessage().size()); - Map messages = extractMessages(info.getMessage()); - assertEquals(2, messages.size()); - assertEquals(4, messages.get(DiagnosisType.REQUIRED_INVALID.name()).intValue()); - assertEquals(1, messages.get(DiagnosisType.INVALID_EXPECTED.name()).intValue()); - } + RepInfo info = generalInvalidChecks(warcFile); - @Test - public void parseInvalidWarcHeaderVersion24() throws Exception { - File warcFile = new File("src/test/resources/warc/invalid-warcheaderversion-24.warc"); + invalidErrorExpectedCheck(info); + } - RepInfo info = generalInvalidChecks(warcFile); - - assertEquals(6, info.getMessage().size()); - Map messages = extractMessages(info.getMessage()); - assertEquals(3, messages.size()); - assertEquals(4, messages.get(DiagnosisType.REQUIRED_INVALID.name()).intValue()); - assertEquals(1, messages.get(DiagnosisType.INVALID_EXPECTED.name()).intValue()); - assertEquals(1, messages.get(DiagnosisType.UNKNOWN.name()).intValue()); - } + @Test + public void parseInvalidWarcHeaderVersion21() throws Exception { + File warcFile = new File("src/test/resources/warc/invalid-warcheaderversion-21.warc"); - @Test - public void parseInvalidWarcHeaderVersion25() throws Exception { - File warcFile = new File("src/test/resources/warc/invalid-warcheaderversion-25.warc"); - - RepInfo info = generalInvalidChecks(warcFile); + RepInfo info = generalInvalidChecks(warcFile); - assertEquals(6, info.getMessage().size()); - Map messages = extractMessages(info.getMessage()); - assertEquals(3, messages.size()); - assertEquals(4, messages.get(DiagnosisType.REQUIRED_INVALID.name()).intValue()); - assertEquals(1, messages.get(DiagnosisType.INVALID_EXPECTED.name()).intValue()); - assertEquals(1, messages.get(DiagnosisType.EMPTY.name()).intValue()); - } - - @Test - public void parseInvalidWarcHeaderVersion26() throws Exception { - File warcFile = new File("src/test/resources/warc/invalid-warcheaderversion-26.warc"); - - RepInfo info = generalInvalidChecks(warcFile); - - assertEquals(5, info.getMessage().size()); - Map messages = extractMessages(info.getMessage()); - assertEquals(2, messages.size()); - assertEquals(4, messages.get(DiagnosisType.REQUIRED_INVALID.name()).intValue()); - assertEquals(1, messages.get(DiagnosisType.INVALID_EXPECTED.name()).intValue()); - } - - @Test - public void parseInvalidWarcReaderDiagnosis1() throws Exception { - File warcFile = new File("src/test/resources/warc/invalid-warcreader-diagnosis-1.warc"); - - RepInfo info = generalInvalidChecks(warcFile); - - assertEquals(3, info.getMessage().size()); - Map messages = extractMessages(info.getMessage()); - assertEquals(2, messages.size()); - assertEquals(2, messages.get(DiagnosisType.INVALID.name()).intValue()); - assertEquals(1, messages.get(DiagnosisType.ERROR_EXPECTED.name()).intValue()); - } - - @Test - public void parseInvalidWarcRecord1() throws Exception { - File warcFile = new File("src/test/resources/warc/invalid-warcrecord-1.warc"); - - RepInfo info = generalInvalidChecks(warcFile); - - assertEquals(7, info.getMessage().size()); - Map messages = extractMessages(info.getMessage()); - assertEquals(5, messages.size()); - assertEquals(2, messages.get(DiagnosisType.INVALID.name()).intValue()); - assertEquals(1, messages.get(DiagnosisType.INVALID_EXPECTED.name()).intValue()); - assertEquals(2, messages.get(DiagnosisType.REQUIRED_INVALID.name()).intValue()); - assertEquals(1, messages.get(DiagnosisType.RECOMMENDED_MISSING.name()).intValue()); - assertEquals(1, messages.get(DiagnosisType.RECOMMENDED.name()).intValue()); - } - - @Test - public void parseInvalidWarcRecord2() throws Exception { - File warcFile = new File("src/test/resources/warc/invalid-warcrecord-2.warc"); - - RepInfo info = generalInvalidChecks(warcFile); - - assertEquals(2, info.getMessage().size()); - Map messages = extractMessages(info.getMessage()); - assertEquals(1, messages.size()); - assertEquals(2, messages.get(DiagnosisType.ERROR.name()).intValue()); - } - - @Test - public void parseInvalidWarcRecord3() throws Exception { - File warcFile = new File("src/test/resources/warc/invalid-warcrecord-3.warc"); - - RepInfo info = generalInvalidChecks(warcFile); - - assertEquals(8, info.getMessage().size()); - Map messages = extractMessages(info.getMessage()); - assertEquals(3, messages.size()); - assertEquals(4, messages.get(DiagnosisType.INVALID.name()).intValue()); - assertEquals(3, messages.get(DiagnosisType.INVALID_EXPECTED.name()).intValue()); - assertEquals(1, messages.get(DiagnosisType.INVALID_DATA.name()).intValue()); - } - - @Test - public void parseInvalidWarcRecordDigests1() throws Exception { - File warcFile = new File("src/test/resources/warc/invalid-warcrecorddigests-1.warc"); - - RepInfo info = generalInvalidChecks(warcFile); - - assertEquals(12, info.getMessage().size()); - Map messages = extractMessages(info.getMessage()); - assertEquals(1, messages.size()); - assertEquals(12, messages.get(DiagnosisType.UNKNOWN.name()).intValue()); - } - - @Test - public void parseInvalidWarcRecordDigests2() throws Exception { - File warcFile = new File("src/test/resources/warc/invalid-warcrecorddigests-2.warc"); - - RepInfo info = generalInvalidChecks(warcFile); - - assertEquals(4, info.getMessage().size()); - Map messages = extractMessages(info.getMessage()); - assertEquals(1, messages.size()); - assertEquals(4, messages.get(DiagnosisType.UNKNOWN.name()).intValue()); - } - - @Test - public void parseInvalidWarcRecordDigests3() throws Exception { - File warcFile = new File("src/test/resources/warc/invalid-warcrecorddigests-3.warc"); - - RepInfo info = generalInvalidChecks(warcFile); - - assertEquals(4, info.getMessage().size()); - Map messages = extractMessages(info.getMessage()); - assertEquals(1, messages.size()); - assertEquals(4, messages.get(DiagnosisType.INVALID_EXPECTED.name()).intValue()); - } - - @Test - public void parseValidWarcFileContentTypeContinuation() throws Exception { - File warcFile = new File("src/test/resources/warc/valid-warcfile-contenttype-continuation.warc"); - - generalWellFormedChecks(warcFile); - } - - @Test - public void parseValidWarcFileDuplicateConcurrentTo() throws Exception { - File warcFile = new File("src/test/resources/warc/valid-warcfile-duplicate-concurrentto.warc"); - assertNotNull("Checking duplicate concurrent is valid"); - generalWellFormedChecks(warcFile); - } - - @Test - public void parseValidWarcFileFieldsContinuation() throws Exception { - File warcFile = new File("src/test/resources/warc/valid-warcfile-fields-continuation.warc"); - - generalWellFormedChecks(warcFile); - } - - @Test - public void parseValidWarcFileFieldsMetaInfo() throws Exception { - File warcFile = new File("src/test/resources/warc/valid-warcfile-fields-metainfo.warc"); - - generalWellFormedChecks(warcFile); - } - - @Test - public void parseValidWarcFileFieldsWarcInfo() throws Exception { - File warcFile = new File("src/test/resources/warc/valid-warcfile-fields-warcinfo.warc"); - - generalWellFormedChecks(warcFile); - } - - @Test - public void parseValidWarcFileNonWarcHeaders() throws Exception { - File warcFile = new File("src/test/resources/warc/valid-warcfile-non-warc-headers.warc"); - - generalWellFormedChecks(warcFile); - } - - @Test - public void parseValidWarcFileUpperLowerCase() throws Exception { - File warcFile = new File("src/test/resources/warc/valid-warcfile-upper-lower-case.warc"); - - generalWellFormedChecks(warcFile); - } - - @Test - public void parseValidWarcRecordDigests1() throws Exception { - File warcFile = new File("src/test/resources/warc/valid-warcrecorddigests-1.warc"); - - generalWellFormedChecks(warcFile); - } - - @Test - public void parseValidWarcRecordDigests2() throws Exception { - File warcFile = new File("src/test/resources/warc/valid-warcrecorddigests-2.warc"); - - generalWellFormedChecks(warcFile); - } - - /** - * @param info - */ - private static void unknownRequiredInvalidCheck(RepInfo info) { - assertEquals(6, info.getMessage().size()); - Map messages = extractMessages(info.getMessage()); - assertEquals(3, messages.size()); - assertEquals(1, messages.get(DiagnosisType.UNKNOWN.name()).intValue()); - assertEquals(4, messages.get(DiagnosisType.REQUIRED_INVALID.name()).intValue()); - assertEquals(1, messages.get(DiagnosisType.INVALID_EXPECTED.name()).intValue()); - } - - /** - * @param info - */ - private static void invalidErrorExpectedCheck(RepInfo info) { - assertEquals(2, info.getMessage().size()); - Map messages = extractMessages(info.getMessage()); - assertEquals(2, messages.size()); - assertEquals(1, messages.get(DiagnosisType.INVALID.name()).intValue()); - assertEquals(1, messages.get(DiagnosisType.ERROR_EXPECTED.name()).intValue()); - } - - /** - * @param messages - */ - private static void invalidRequiredDataCheck(Map messages) { - assertEquals(3, messages.size()); - assertEquals(1, messages.get(DiagnosisType.INVALID_DATA.name()).intValue()); - assertEquals(4, messages.get(DiagnosisType.REQUIRED_INVALID.name()).intValue()); - assertEquals(1, messages.get(DiagnosisType.INVALID_EXPECTED.name()).intValue()); - } - - /** - * @param messages - */ - private static void invalidDataCheck(Map messages) { - assertEquals(1, messages.get(DiagnosisType.INVALID_DATA.name()).intValue()); - assertEquals(1, messages.get(DiagnosisType.INVALID_EXPECTED.name()).intValue()); - } - - /** - * @param warcFile - * @throws IOException - * @throws FileNotFoundException - */ - private static void invalidWithClassCheck(File warcFile) throws IOException, FileNotFoundException { - WarcModule wm = new WarcModule(); - RepInfo info = new RepInfo(warcFile.getAbsolutePath()); - wm.checkSignatures(null, new FileInputStream(warcFile), info); - - // The WARC module MUST find WAV files as not well-formed WARC files - assertEquals(RepInfo.FALSE, info.getWellFormed()); - assertEquals(WarcModule.class, info.getModule().getClass()); - assertTrue(info.getSigMatch().isEmpty()); - } - - /** - * @param warcFile - * @return - * @throws IOException - * @throws FileNotFoundException - */ - private static RepInfo generalInvalidChecks(File warcFile) throws IOException, FileNotFoundException { - WarcModule wm = new WarcModule(); - RepInfo info = new RepInfo(warcFile.getAbsolutePath()); - wm.parse(new RandomAccessFile(warcFile, "r"), info); - - invalidCheck(info); - return info; - } - - /** - * @param warcFile - * @throws IOException - * @throws FileNotFoundException - */ - private static void generalWellFormedChecks(File warcFile) throws IOException, FileNotFoundException { - WarcModule wm = new WarcModule(); - RepInfo info = new RepInfo(warcFile.getAbsolutePath()); - wm.parse(new RandomAccessFile(warcFile, "r"), info); - - wellFormedCheck(info, wm); - assertEquals(0, info.getMessage().size()); - } - - /** - * @param info - * @param wm - */ - private static void wellFormedCheck(RepInfo info, WarcModule wm) { - assertEquals(RepInfo.TRUE, info.getWellFormed()); - assertEquals(RepInfo.TRUE, info.getValid()); - assertEquals(Arrays.asList(wm.getName()), info.getSigMatch()); - } - - /** - * @param info - */ - private static void invalidCheck(RepInfo info) { - assertEquals(RepInfo.FALSE, info.getWellFormed()); - assertEquals(RepInfo.FALSE, info.getValid()); - assertTrue(info.getSigMatch().isEmpty()); - } - - private static Map extractMessages(Collection messages) { - Map res = new HashMap(); - for(Message m : messages) { - if(res.containsKey(m.getMessage())) { - res.put(m.getMessage(), res.get(m.getMessage()) + 1); - } else { - res.put(m.getMessage(), 1); - } - } - return res; - } + assertEquals(5, info.getMessage().size()); + Map messages = extractMessages(info.getMessage()); + assertEquals(2, messages.size()); + assertEquals(4, messages.get(DiagnosisType.REQUIRED_INVALID.name()).intValue()); + assertEquals(1, messages.get(DiagnosisType.INVALID_EXPECTED.name()).intValue()); + } + + @Test + public void parseInvalidWarcHeaderVersion22() throws Exception { + File warcFile = new File("src/test/resources/warc/invalid-warcheaderversion-22.warc"); + + RepInfo info = generalInvalidChecks(warcFile); + + assertEquals(5, info.getMessage().size()); + Map messages = extractMessages(info.getMessage()); + assertEquals(2, messages.size()); + assertEquals(4, messages.get(DiagnosisType.REQUIRED_INVALID.name()).intValue()); + assertEquals(1, messages.get(DiagnosisType.INVALID_EXPECTED.name()).intValue()); + } + + @Test + public void parseInvalidWarcHeaderVersion23() throws Exception { + File warcFile = new File("src/test/resources/warc/invalid-warcheaderversion-23.warc"); + + RepInfo info = generalInvalidChecks(warcFile); + + assertEquals(5, info.getMessage().size()); + Map messages = extractMessages(info.getMessage()); + assertEquals(2, messages.size()); + assertEquals(4, messages.get(DiagnosisType.REQUIRED_INVALID.name()).intValue()); + assertEquals(1, messages.get(DiagnosisType.INVALID_EXPECTED.name()).intValue()); + } + + @Test + public void parseInvalidWarcHeaderVersion24() throws Exception { + File warcFile = new File("src/test/resources/warc/invalid-warcheaderversion-24.warc"); + + RepInfo info = generalInvalidChecks(warcFile); + + assertEquals(6, info.getMessage().size()); + Map messages = extractMessages(info.getMessage()); + assertEquals(3, messages.size()); + assertEquals(4, messages.get(DiagnosisType.REQUIRED_INVALID.name()).intValue()); + assertEquals(1, messages.get(DiagnosisType.INVALID_EXPECTED.name()).intValue()); + assertEquals(1, messages.get(DiagnosisType.UNKNOWN.name()).intValue()); + } + + @Test + public void parseInvalidWarcHeaderVersion25() throws Exception { + File warcFile = new File("src/test/resources/warc/invalid-warcheaderversion-25.warc"); + + RepInfo info = generalInvalidChecks(warcFile); + + assertEquals(6, info.getMessage().size()); + Map messages = extractMessages(info.getMessage()); + assertEquals(3, messages.size()); + assertEquals(4, messages.get(DiagnosisType.REQUIRED_INVALID.name()).intValue()); + assertEquals(1, messages.get(DiagnosisType.INVALID_EXPECTED.name()).intValue()); + assertEquals(1, messages.get(DiagnosisType.EMPTY.name()).intValue()); + } + + @Test + public void parseInvalidWarcHeaderVersion26() throws Exception { + File warcFile = new File("src/test/resources/warc/invalid-warcheaderversion-26.warc"); + + RepInfo info = generalInvalidChecks(warcFile); + + assertEquals(5, info.getMessage().size()); + Map messages = extractMessages(info.getMessage()); + assertEquals(2, messages.size()); + assertEquals(4, messages.get(DiagnosisType.REQUIRED_INVALID.name()).intValue()); + assertEquals(1, messages.get(DiagnosisType.INVALID_EXPECTED.name()).intValue()); + } + + @Test + public void parseInvalidWarcReaderDiagnosis1() throws Exception { + File warcFile = new File("src/test/resources/warc/invalid-warcreader-diagnosis-1.warc"); + + RepInfo info = generalInvalidChecks(warcFile); + + assertEquals(3, info.getMessage().size()); + Map messages = extractMessages(info.getMessage()); + assertEquals(2, messages.size()); + assertEquals(2, messages.get(DiagnosisType.INVALID.name()).intValue()); + assertEquals(1, messages.get(DiagnosisType.ERROR_EXPECTED.name()).intValue()); + } + + @Test + public void parseInvalidWarcRecord1() throws Exception { + File warcFile = new File("src/test/resources/warc/invalid-warcrecord-1.warc"); + + RepInfo info = generalInvalidChecks(warcFile); + + assertEquals(7, info.getMessage().size()); + Map messages = extractMessages(info.getMessage()); + assertEquals(5, messages.size()); + assertEquals(2, messages.get(DiagnosisType.INVALID.name()).intValue()); + assertEquals(1, messages.get(DiagnosisType.INVALID_EXPECTED.name()).intValue()); + assertEquals(2, messages.get(DiagnosisType.REQUIRED_INVALID.name()).intValue()); + assertEquals(1, messages.get(DiagnosisType.RECOMMENDED_MISSING.name()).intValue()); + assertEquals(1, messages.get(DiagnosisType.RECOMMENDED.name()).intValue()); + } + + @Test + public void parseInvalidWarcRecord2() throws Exception { + File warcFile = new File("src/test/resources/warc/invalid-warcrecord-2.warc"); + + RepInfo info = generalInvalidChecks(warcFile); + + assertEquals(2, info.getMessage().size()); + Map messages = extractMessages(info.getMessage()); + assertEquals(1, messages.size()); + assertEquals(2, messages.get(DiagnosisType.ERROR.name()).intValue()); + } + + @Test + public void parseInvalidWarcRecord3() throws Exception { + File warcFile = new File("src/test/resources/warc/invalid-warcrecord-3.warc"); + + RepInfo info = generalInvalidChecks(warcFile); + + assertEquals(8, info.getMessage().size()); + Map messages = extractMessages(info.getMessage()); + assertEquals(3, messages.size()); + assertEquals(4, messages.get(DiagnosisType.INVALID.name()).intValue()); + assertEquals(3, messages.get(DiagnosisType.INVALID_EXPECTED.name()).intValue()); + assertEquals(1, messages.get(DiagnosisType.INVALID_DATA.name()).intValue()); + } + + @Test + public void parseInvalidWarcRecordDigests1() throws Exception { + File warcFile = new File("src/test/resources/warc/invalid-warcrecorddigests-1.warc"); + + RepInfo info = generalInvalidChecks(warcFile); + + assertEquals(12, info.getMessage().size()); + Map messages = extractMessages(info.getMessage()); + assertEquals(1, messages.size()); + assertEquals(12, messages.get(DiagnosisType.UNKNOWN.name()).intValue()); + } + + @Test + public void parseInvalidWarcRecordDigests2() throws Exception { + File warcFile = new File("src/test/resources/warc/invalid-warcrecorddigests-2.warc"); + + RepInfo info = generalInvalidChecks(warcFile); + + assertEquals(4, info.getMessage().size()); + Map messages = extractMessages(info.getMessage()); + assertEquals(1, messages.size()); + assertEquals(4, messages.get(DiagnosisType.UNKNOWN.name()).intValue()); + } + + @Test + public void parseInvalidWarcRecordDigests3() throws Exception { + File warcFile = new File("src/test/resources/warc/invalid-warcrecorddigests-3.warc"); + + RepInfo info = generalInvalidChecks(warcFile); + + assertEquals(4, info.getMessage().size()); + Map messages = extractMessages(info.getMessage()); + assertEquals(1, messages.size()); + assertEquals(4, messages.get(DiagnosisType.INVALID_EXPECTED.name()).intValue()); + } + + @Test + public void parseValidWarcFileContentTypeContinuation() throws Exception { + File warcFile = + new File("src/test/resources/warc/valid-warcfile-contenttype-continuation.warc"); + + generalWellFormedChecks(warcFile); + } + + @Test + public void parseValidWarcFileDuplicateConcurrentTo() throws Exception { + File warcFile = new File("src/test/resources/warc/valid-warcfile-duplicate-concurrentto.warc"); + assertNotNull("Checking duplicate concurrent is valid"); + generalWellFormedChecks(warcFile); + } + + @Test + public void parseValidWarcFileFieldsContinuation() throws Exception { + File warcFile = new File("src/test/resources/warc/valid-warcfile-fields-continuation.warc"); + + generalWellFormedChecks(warcFile); + } + + @Test + public void parseValidWarcFileFieldsMetaInfo() throws Exception { + File warcFile = new File("src/test/resources/warc/valid-warcfile-fields-metainfo.warc"); + + generalWellFormedChecks(warcFile); + } + + @Test + public void parseValidWarcFileFieldsWarcInfo() throws Exception { + File warcFile = new File("src/test/resources/warc/valid-warcfile-fields-warcinfo.warc"); + + generalWellFormedChecks(warcFile); + } + + @Test + public void parseValidWarcFileNonWarcHeaders() throws Exception { + File warcFile = new File("src/test/resources/warc/valid-warcfile-non-warc-headers.warc"); + + generalWellFormedChecks(warcFile); + } + + @Test + public void parseValidWarcFileUpperLowerCase() throws Exception { + File warcFile = new File("src/test/resources/warc/valid-warcfile-upper-lower-case.warc"); + + generalWellFormedChecks(warcFile); + } + + @Test + public void parseValidWarcRecordDigests1() throws Exception { + File warcFile = new File("src/test/resources/warc/valid-warcrecorddigests-1.warc"); + + generalWellFormedChecks(warcFile); + } + + @Test + public void parseValidWarcRecordDigests2() throws Exception { + File warcFile = new File("src/test/resources/warc/valid-warcrecorddigests-2.warc"); + + generalWellFormedChecks(warcFile); + } + + /** @param info */ + private static void unknownRequiredInvalidCheck(RepInfo info) { + assertEquals(6, info.getMessage().size()); + Map messages = extractMessages(info.getMessage()); + assertEquals(3, messages.size()); + assertEquals(1, messages.get(DiagnosisType.UNKNOWN.name()).intValue()); + assertEquals(4, messages.get(DiagnosisType.REQUIRED_INVALID.name()).intValue()); + assertEquals(1, messages.get(DiagnosisType.INVALID_EXPECTED.name()).intValue()); + } + + /** @param info */ + private static void invalidErrorExpectedCheck(RepInfo info) { + assertEquals(2, info.getMessage().size()); + Map messages = extractMessages(info.getMessage()); + assertEquals(2, messages.size()); + assertEquals(1, messages.get(DiagnosisType.INVALID.name()).intValue()); + assertEquals(1, messages.get(DiagnosisType.ERROR_EXPECTED.name()).intValue()); + } + + /** @param messages */ + private static void invalidRequiredDataCheck(Map messages) { + assertEquals(3, messages.size()); + assertEquals(1, messages.get(DiagnosisType.INVALID_DATA.name()).intValue()); + assertEquals(4, messages.get(DiagnosisType.REQUIRED_INVALID.name()).intValue()); + assertEquals(1, messages.get(DiagnosisType.INVALID_EXPECTED.name()).intValue()); + } + + /** @param messages */ + private static void invalidDataCheck(Map messages) { + assertEquals(1, messages.get(DiagnosisType.INVALID_DATA.name()).intValue()); + assertEquals(1, messages.get(DiagnosisType.INVALID_EXPECTED.name()).intValue()); + } + + /** + * @param warcFile + * @throws IOException + * @throws FileNotFoundException + */ + private static void invalidWithClassCheck(File warcFile) + throws IOException, FileNotFoundException { + WarcModule wm = new WarcModule(); + RepInfo info = new RepInfo(warcFile.getAbsolutePath()); + wm.checkSignatures(null, new FileInputStream(warcFile), info); + + // The WARC module MUST find WAV files as not well-formed WARC files + assertEquals(RepInfo.FALSE, info.getWellFormed()); + assertEquals(WarcModule.class, info.getModule().getClass()); + assertTrue(info.getSigMatch().isEmpty()); + } + + /** + * @param warcFile + * @return + * @throws IOException + * @throws FileNotFoundException + */ + private static RepInfo generalInvalidChecks(File warcFile) + throws IOException, FileNotFoundException { + WarcModule wm = new WarcModule(); + RepInfo info = new RepInfo(warcFile.getAbsolutePath()); + wm.parse(new RandomAccessFile(warcFile, "r"), info); + + invalidCheck(info); + return info; + } + + /** + * @param warcFile + * @throws IOException + * @throws FileNotFoundException + */ + private static void generalWellFormedChecks(File warcFile) + throws IOException, FileNotFoundException { + WarcModule wm = new WarcModule(); + RepInfo info = new RepInfo(warcFile.getAbsolutePath()); + wm.parse(new RandomAccessFile(warcFile, "r"), info); + + wellFormedCheck(info, wm); + assertEquals(0, info.getMessage().size()); + } + + /** + * @param info + * @param wm + */ + private static void wellFormedCheck(RepInfo info, WarcModule wm) { + assertEquals(RepInfo.TRUE, info.getWellFormed()); + assertEquals(RepInfo.TRUE, info.getValid()); + assertEquals(Arrays.asList(wm.getName()), info.getSigMatch()); + } + + /** @param info */ + private static void invalidCheck(RepInfo info) { + assertEquals(RepInfo.FALSE, info.getWellFormed()); + assertEquals(RepInfo.FALSE, info.getValid()); + assertTrue(info.getSigMatch().isEmpty()); + } + + private static Map extractMessages(Collection messages) { + Map res = new HashMap(); + for (Message m : messages) { + if (res.containsKey(m.getMessage())) { + res.put(m.getMessage(), res.get(m.getMessage()) + 1); + } else { + res.put(m.getMessage(), 1); + } + } + return res; + } } diff --git a/jhove-ext-modules/src/test/java/org/ithaka/portico/jhove/module/EpubModuleTest.java b/jhove-ext-modules/src/test/java/org/ithaka/portico/jhove/module/EpubModuleTest.java index 87f1d9003..9b48e56cb 100644 --- a/jhove-ext-modules/src/test/java/org/ithaka/portico/jhove/module/EpubModuleTest.java +++ b/jhove-ext-modules/src/test/java/org/ithaka/portico/jhove/module/EpubModuleTest.java @@ -1,12 +1,17 @@ package org.ithaka.portico.jhove.module; import static org.ithaka.portico.jhove.module.epub.ReportPropertyNames.*; - import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; +import edu.harvard.hul.ois.jhove.ChecksumType; +import edu.harvard.hul.ois.jhove.ErrorMessage; +import edu.harvard.hul.ois.jhove.JhoveBase; +import edu.harvard.hul.ois.jhove.Message; +import edu.harvard.hul.ois.jhove.Property; +import edu.harvard.hul.ois.jhove.PropertyArity; +import edu.harvard.hul.ois.jhove.RepInfo; import java.io.File; import java.io.FileInputStream; import java.util.Arrays; @@ -15,728 +20,733 @@ import java.util.List; import java.util.Map; import java.util.Set; - import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; -import edu.harvard.hul.ois.jhove.ChecksumType; -import edu.harvard.hul.ois.jhove.ErrorMessage; -import edu.harvard.hul.ois.jhove.JhoveBase; -import edu.harvard.hul.ois.jhove.Message; -import edu.harvard.hul.ois.jhove.Property; -import edu.harvard.hul.ois.jhove.PropertyArity; -import edu.harvard.hul.ois.jhove.RepInfo; - /** - *

- * Tests for EpubModule. Although EPUBCheck has not been mocked for these tests, - * tests focus on functionality in the EpubModule as it generates the JHOVE info - * rather than testing the functionality of EPUBCheck. Sample EPUBs sourced - * from: + * Tests for EpubModule. Although EPUBCheck has not been mocked for these tests, tests focus on + * functionality in the EpubModule as it generates the JHOVE info rather than testing the + * functionality of EPUBCheck. Sample EPUBs sourced from: + * *

    - *
  • IDPF's EPUB 3 samples and test suite on GitHub
  • - *
  • National Library of the Netherlands / Research epubPolicyTests on - * GitHub
  • + *
  • IDPF's EPUB 3 samples and test suite on GitHub + *
  • National Library of the Netherlands / Research epubPolicyTests on GitHub *
+ * * Some of the sample EPUBs were modified slightly for specific tests. - *

* + * @see https://github.com/IDPF/epub3-samples + * @see https://github.com/IDPF/epub-testsuite * @see https://github.com/IDPF/epub3-samples - * @see https://github.com/IDPF/epub-testsuite - * @see https://github.com/KBNLresearch/epubPolicyTests - * + * "https://github.com/KBNLresearch/epubPolicyTests">https://github.com/KBNLresearch/epubPolicyTests * @author Karen Hanson */ @SuppressWarnings("unchecked") @RunWith(JUnit4.class) public class EpubModuleTest { - private static final String EPUBMETADATA_KEY = "EPUBMetadata"; - - private static final String CHILDRENSLIT_SRC_FILEPATH = "src/test/resources/epub/epub3-valid-childrens-literature.epub"; - private static final String MINIMAL_EPUB_FILEPATH = "src/test/resources/epub/epub2-minimal.epub"; - private static final String WRONG_EXT_NOT_AN_EPUB_FILEPATH = "src/test/resources/epub/not-an-epub.docx"; - private static final String RIGHT_EXT_NOT_AN_EPUB_FILEPATH = "src/test/resources/epub/not-an-epub.epub"; - private static final String ZIPPED_EPUB_FILEPATH = "src/test/resources/epub/epub3-zipped-childrens-literature.epub"; - private static final String EPUB2_WITH_WARNING_FILEPATH = "src/test/resources/epub/epub2-with-warning-minimal.epub"; - private static final String EPUB3_WITH_MULTIMEDIA_FILEPATH = "src/test/resources/epub/epub3-valid-multimedia.epub"; - private static final String EMPTY_EPUB_FILEPATH = "src/test/resources/epub/empty.epub"; - private static final String EPUB_WRONG_EXT_FILEPATH = "src/test/resources/epub/epub3-wrong-ext-childrens-literature.wrong"; - private static final String EPUB_MISSING_FONT_FILEPATH = "src/test/resources/epub/epub2-missing-fontresource.epub"; - private static final String EPUB_OBFUSCATED_FONT_FILEPATH = "src/test/resources/epub/epub3-font-obfuscated-wasteland.epub"; - private static final String EPUB2_WITH_ERROR_FILEPATH = "src/test/resources/epub/epub2-with-error-minimal.epub"; - private static final String EPUB2_MISSING_OPF_FILEPATH = "src/test/resources/epub/epub2-no-opf-minimal.epub"; - private static final String EPUB3_FIXED_LAYOUT_FILEPATH = "src/test/resources/epub/epub3-valid-fixedlayout-page-blanche.epub"; - private static final String EPUB2_ENCRYPTION = "src/test/resources/epub/epub2-valid-minimal-encryption.epub"; - private static final String EPUB3_TITLE_ENCODING = "src/test/resources/epub/epub3-multiple-renditions.epub"; - - private static final String EXPECTED_MEDIATYPE = "application/epub+zip"; - private static final String EXPECTED_VERSION_3_2 = "3.2"; - private static final String PNG_MIMETYPE = "image/png"; - private static final String XHTML_MIMETYPE = "application/xhtml+xml"; - private static final String NCX_MIMETYPE = "application/x-dtbncx+xml"; - private static final String JPG_MIMETYPE = "image/jpeg"; - private static final String CSS_MIMETYPE = "text/css"; - private static final String OCTET_MIMETYPE = "application/octet-stream"; - - private static final String EN_LANGUAGE = "en"; - - /** - * Test parses a valid EPUB3 file does thorough verification that properties are - * as expected. Properties not checked here will be covered in other tests. Test - * file source: - * https://github.com/IDPF/epub3-samples/tree/master/30/childrens-literature - * - * @throws Exception - */ - @Test - public void parseValidEpub3PropertiesTest() throws Exception { - File epubFile = new File(CHILDRENSLIT_SRC_FILEPATH); - RepInfo info = parseAndCheckValidity(epubFile, RepInfo.TRUE, RepInfo.TRUE); - assertEquals(0, info.getMessage().size()); // no errors - assertEquals("EPUB", info.getFormat()); - assertEquals(EXPECTED_MEDIATYPE, info.getMimeType()); - assertEquals(EXPECTED_VERSION_3_2, info.getVersion()); - // these may change, so just check they aren't null - assertNotNull(info.getCreated()); - assertNotNull(info.getLastModified()); - - Property metadata = info.getProperty(EPUBMETADATA_KEY); - assertEquals(PropertyArity.LIST, metadata.getArity()); - Map props = toMap(metadata); - - final long charcount = 324066L; - assertEquals(charcount, props.get(PROPNAME_CHARCOUNT)); - assertEquals(EN_LANGUAGE, props.get(PROPNAME_LANGUAGE)); - assertEquals(true, props.get(FEATURE_HASSCRIPTS)); - assertEquals(null, props.get(FEATURE_HASAUDIO)); - assertEquals(null, props.get(FEATURE_HASVIDEO)); - - Set mediaTypes = new HashSet(Arrays.asList((String[]) props.get(PROPNAME_MEDIATYPES))); - final int expectedNumMediaTypes = 4; - assertEquals(expectedNumMediaTypes, mediaTypes.size()); - assertTrue(mediaTypes.contains(PNG_MIMETYPE)); - assertTrue(mediaTypes.contains(CSS_MIMETYPE)); - assertTrue(mediaTypes.contains(XHTML_MIMETYPE)); - assertTrue(mediaTypes.contains(NCX_MIMETYPE)); - - Set resources = new HashSet(Arrays.asList((String[]) props.get(PROPNAME_RESOURCES))); - final int expectedNumResources = 5; - assertEquals(expectedNumResources, resources.size()); - assertTrue(resources.contains("EPUB/images/cover.png")); - assertTrue(resources.contains("EPUB/css/nav.css")); - - Set infoPropsSet = (Set) props.get(PROPNAME_INFO); - - Map infoProps = new HashMap(); - infoPropsSet.forEach(p -> infoProps.put(p.getName(), p.getValue())); - - assertEquals("http://www.gutenberg.org/ebooks/25545", infoProps.get(PROPNAME_IDENTIFIER)); - assertEquals("2008-05-20", infoProps.get(PROPNAME_DATE)); - assertEquals("Public domain in the USA.", infoProps.get(PROPNAME_RIGHTS)); - - Set titles = new HashSet(Arrays.asList((String[]) infoProps.get(PROPNAME_TITLE))); - assertEquals(2, titles.size()); - assertTrue(titles.contains("Children's Literature")); - assertTrue(titles.contains("A Textbook of Sources for Teachers and Teacher-Training Classes")); - - Set creators = new HashSet(Arrays.asList((String[]) infoProps.get(PROPNAME_CREATOR))); - assertEquals(2, creators.size()); - assertTrue(creators.contains("Charles Madison Curry")); - assertTrue(creators.contains("Erle Elsworth Clippinger")); - - Set subjects = new HashSet(Arrays.asList((String[]) infoProps.get(PROPNAME_SUBJECTS))); - assertEquals(2, subjects.size()); - assertTrue(subjects.contains("Children -- Books and reading")); - assertTrue(subjects.contains("Children's literature -- Study and teaching")); - - } - - /** - * Valid EPUB3 file and the checksum. Test file source: - * https://github.com/IDPF/epub3-samples/tree/master/30/childrens-literature - * - * @throws Exception - */ - @Test - public void parseValidEpub3ChecksumTest() throws Exception { - File epubFile = new File(CHILDRENSLIT_SRC_FILEPATH); - - EpubModule em = new EpubModule(); - JhoveBase je = new JhoveBase(); - je.setChecksumFlag(true); - em.setBase(je); - RepInfo info = new RepInfo(epubFile.getAbsolutePath()); - em.parse(new FileInputStream(epubFile), info, 0); - - Map checksums = new HashMap(); - info.getChecksum().forEach(cs -> checksums.put(cs.getType(), cs.getValue())); - assertEquals("da79cb9b", checksums.get(ChecksumType.CRC32)); - assertEquals("4c2dee43162e40690ba05926b9f42522", checksums.get(ChecksumType.MD5)); - assertEquals("72bcf1b71f4dd9b902a5fcd614601d5e488003a0", checksums.get(ChecksumType.SHA1)); - assertEquals("a1da72ef94de43a70a97538d5e789f74bf7c29bd99f8059697ae75a6b85a75f7", checksums.get(ChecksumType.SHA256)); - final int expectedNumChecksums = 4; - assertEquals(expectedNumChecksums, info.getChecksum().size()); - } - - /** - * Check signatures for valid EPUB3 file. Test file source: - * https://github.com/IDPF/epub3-samples/tree/master/30/childrens-literature - * - * @throws Exception - */ - @Test - public void checkSignaturesValidEpub3Test() throws Exception { - File epubFile = new File(CHILDRENSLIT_SRC_FILEPATH); - assertTrue(checkSignatureMatch(epubFile, RepInfo.TRUE)); - } - - /** - * Valid EPUB3 file with remote and local multimedia embedded. Test file source: - * https://github.com/IDPF/epub-testsuite/tree/master/content/30/epub30-test-0100 - * - * @throws Exception - */ - @Test - public void parseValidEpub3WithRemoteResourcesTest() throws Exception { - String remoteMp3Url = "http://epubtest.org/media/remote/allison64-remote.mp3"; - String remoteMp4Url = "http://epubtest.org/media/remote/allison64-remote.mp4"; - - File epubFile = new File(EPUB3_WITH_MULTIMEDIA_FILEPATH); - RepInfo info = parseAndCheckValidity(epubFile, RepInfo.TRUE, RepInfo.TRUE); - - Property metadata = info.getProperty(EPUBMETADATA_KEY); - Map props = toMap(metadata); - - assertEquals(true, props.get(FEATURE_HASAUDIO)); - assertEquals(true, props.get(FEATURE_HASVIDEO)); - assertEquals(true, props.get(FEATURE_HASSCRIPTS)); - - Set references = new HashSet(Arrays.asList((String[]) props.get(PROPNAME_REFERENCES))); - final int expectedNumReferences = 8; - assertEquals(expectedNumReferences, references.size()); - // spot check a few - assertTrue(references.contains("http://idpf.org/")); - assertTrue( - references.contains("http://idpf.org/epub/30/spec/epub30-contentdocs.html#sec-xhtml-content-switch")); - assertTrue(references.contains(remoteMp3Url)); - - Set resources = new HashSet(Arrays.asList((String[]) props.get(PROPNAME_RESOURCES))); - final int expectedNumResources = 51; - assertEquals(expectedNumResources, resources.size()); - // spot check a few - assertTrue(resources.contains(remoteMp4Url)); - assertTrue(resources.contains(remoteMp3Url)); - assertTrue(resources.contains("EPUB/img/mathml-01-020-styling.png")); - assertTrue(resources.contains("EPUB/img/check.jpg")); - } - - /** - * Basic EPUB2 check. Test file source: - * https://github.com/KBNLresearch/epubPolicyTests/tree/master/content/epub20_minimal - * - * @throws Exception - */ - @Test - public void parseValidEpub2PropertiesTest() throws Exception { - File epubFile = new File(MINIMAL_EPUB_FILEPATH); - RepInfo info = parseAndCheckValidity(epubFile, RepInfo.TRUE, RepInfo.TRUE); - assertEquals(0, info.getMessage().size()); // no errors - assertEquals(EXPECTED_MEDIATYPE, info.getMimeType()); - assertEquals("2.0.1", info.getVersion()); - // may change, so just check it isn't null - assertNotNull(info.getCreated()); - - Property metadata = info.getProperty(EPUBMETADATA_KEY); - Map props = toMap(metadata); - - final long expectedCharCount = 4520L; - assertEquals(expectedCharCount, props.get(PROPNAME_CHARCOUNT)); - assertEquals(EN_LANGUAGE, props.get(PROPNAME_LANGUAGE)); - - Set mediaTypes = new HashSet(Arrays.asList((String[]) props.get(PROPNAME_MEDIATYPES))); - final int expectedNumMediaTypes = 4; - assertEquals(expectedNumMediaTypes, mediaTypes.size()); - assertTrue(mediaTypes.contains(PNG_MIMETYPE)); - assertTrue(mediaTypes.contains(JPG_MIMETYPE)); - assertTrue(mediaTypes.contains(XHTML_MIMETYPE)); - assertTrue(mediaTypes.contains(NCX_MIMETYPE)); - - Set resources = new HashSet(Arrays.asList((String[]) props.get(PROPNAME_RESOURCES))); - final int expectedNumResources = 4; - assertEquals(expectedNumResources, resources.size()); - assertTrue(resources.contains("OEBPS/Text/pdfMigration.html")); - assertTrue(resources.contains("OEBPS/Text/cover.xhtml")); - - Set references = new HashSet(Arrays.asList((String[]) props.get(PROPNAME_REFERENCES))); - final int expectedNumReferences = 7; - assertEquals(expectedNumReferences, references.size()); - assertTrue(references.contains("http://acroeng.adobe.com/PDFReference/ISO32000/PDF32000-Adobe.pdf")); - assertTrue(references.contains( - "http://qanda.digipres.org/19/what-are-the-benefits-and-risks-of-using-the-pdf-a-file-format?show=21#a21")); - - Set infoPropsSet = (Set) props.get(PROPNAME_INFO); - - Map infoProps = new HashMap(); - infoPropsSet.forEach(p -> infoProps.put(p.getName(), p.getValue())); - - assertEquals("urn:uuid:f930f4b3-cba2-42ba-ab26-d49438ab00d6", infoProps.get(PROPNAME_IDENTIFIER)); - assertEquals("2015-03-03", infoProps.get(PROPNAME_DATE)); - assertEquals("When (not) to migrate a PDF to PDF/A", infoProps.get(PROPNAME_TITLE)); - assertEquals("Johan van der Knijff", infoProps.get(PROPNAME_CREATOR)); - - } - - /** - * Valid EPUB2 file and the checksum. Test file source: - * https://github.com/KBNLresearch/epubPolicyTests/tree/master/content/epub20_minimal - * - * @throws Exception - */ - @Test - public void parseValidEpub2ChecksumTest() throws Exception { - File epubFile = new File(MINIMAL_EPUB_FILEPATH); - - EpubModule em = new EpubModule(); - JhoveBase je = new JhoveBase(); - je.setChecksumFlag(true); - em.setBase(je); - RepInfo info = new RepInfo(epubFile.getAbsolutePath()); - em.parse(new FileInputStream(epubFile), info, 0); - - Map checksums = new HashMap(); - info.getChecksum().forEach(cs -> checksums.put(cs.getType(), cs.getValue())); - assertEquals("8b80b526", checksums.get(ChecksumType.CRC32)); - assertEquals("b2110219d62c3c6ef1683c645636fd38", checksums.get(ChecksumType.MD5)); - assertEquals("79f20f6a499a640019a9bb0334652edbb954c3c9", checksums.get(ChecksumType.SHA1)); - assertEquals("4776bb33b1cce8598b31996ed0b4daf36e4b74e379b811a27e51efa15315744a", checksums.get(ChecksumType.SHA256)); - final int expectedNumChecksum = 4; - assertEquals(expectedNumChecksum, info.getChecksum().size()); - } - - /** - * Check signatures for valid EPUB2 file. Test file source: - * https://github.com/KBNLresearch/epubPolicyTests/tree/master/content/epub20_minimal - * - * @throws Exception - */ - @Test - public void checkSignaturesValidEpub2Test() throws Exception { - File epubFile = new File(MINIMAL_EPUB_FILEPATH); - assertTrue(checkSignatureMatch(epubFile, RepInfo.TRUE)); - } - - /** - * Attempt to parse a file with an epub extension that is actually an empty file - * and not a zip at all. - * - * @throws Exception - */ - @Test - public void parseEmptyFileTest() throws Exception { - File epubFile = new File(EMPTY_EPUB_FILEPATH); - RepInfo info = parseAndCheckValidity(epubFile, RepInfo.FALSE, RepInfo.FALSE); - assertEquals(2, info.getMessage().size()); - assertEquals(OCTET_MIMETYPE, info.getMimeType()); - } - - /** - * Attempt to check signature of a file with an epub extension that is actually - * an empty file and not a zip at all. - * - * @throws Exception - */ - @Test - public void checkSignaturesEmptyFileTest() throws Exception { - File epubFile = new File(EMPTY_EPUB_FILEPATH); - assertTrue(checkSignatureMatch(epubFile, RepInfo.FALSE)); + private static final String EPUBMETADATA_KEY = "EPUBMetadata"; + + private static final String CHILDRENSLIT_SRC_FILEPATH = + "src/test/resources/epub/epub3-valid-childrens-literature.epub"; + private static final String MINIMAL_EPUB_FILEPATH = "src/test/resources/epub/epub2-minimal.epub"; + private static final String WRONG_EXT_NOT_AN_EPUB_FILEPATH = + "src/test/resources/epub/not-an-epub.docx"; + private static final String RIGHT_EXT_NOT_AN_EPUB_FILEPATH = + "src/test/resources/epub/not-an-epub.epub"; + private static final String ZIPPED_EPUB_FILEPATH = + "src/test/resources/epub/epub3-zipped-childrens-literature.epub"; + private static final String EPUB2_WITH_WARNING_FILEPATH = + "src/test/resources/epub/epub2-with-warning-minimal.epub"; + private static final String EPUB3_WITH_MULTIMEDIA_FILEPATH = + "src/test/resources/epub/epub3-valid-multimedia.epub"; + private static final String EMPTY_EPUB_FILEPATH = "src/test/resources/epub/empty.epub"; + private static final String EPUB_WRONG_EXT_FILEPATH = + "src/test/resources/epub/epub3-wrong-ext-childrens-literature.wrong"; + private static final String EPUB_MISSING_FONT_FILEPATH = + "src/test/resources/epub/epub2-missing-fontresource.epub"; + private static final String EPUB_OBFUSCATED_FONT_FILEPATH = + "src/test/resources/epub/epub3-font-obfuscated-wasteland.epub"; + private static final String EPUB2_WITH_ERROR_FILEPATH = + "src/test/resources/epub/epub2-with-error-minimal.epub"; + private static final String EPUB2_MISSING_OPF_FILEPATH = + "src/test/resources/epub/epub2-no-opf-minimal.epub"; + private static final String EPUB3_FIXED_LAYOUT_FILEPATH = + "src/test/resources/epub/epub3-valid-fixedlayout-page-blanche.epub"; + private static final String EPUB2_ENCRYPTION = + "src/test/resources/epub/epub2-valid-minimal-encryption.epub"; + private static final String EPUB3_TITLE_ENCODING = + "src/test/resources/epub/epub3-multiple-renditions.epub"; + + private static final String EXPECTED_MEDIATYPE = "application/epub+zip"; + private static final String EXPECTED_VERSION_3_2 = "3.2"; + private static final String PNG_MIMETYPE = "image/png"; + private static final String XHTML_MIMETYPE = "application/xhtml+xml"; + private static final String NCX_MIMETYPE = "application/x-dtbncx+xml"; + private static final String JPG_MIMETYPE = "image/jpeg"; + private static final String CSS_MIMETYPE = "text/css"; + private static final String OCTET_MIMETYPE = "application/octet-stream"; + + private static final String EN_LANGUAGE = "en"; + + /** + * Test parses a valid EPUB3 file does thorough verification that properties are as expected. + * Properties not checked here will be covered in other tests. Test file source: + * https://github.com/IDPF/epub3-samples/tree/master/30/childrens-literature + * + * @throws Exception + */ + @Test + public void parseValidEpub3PropertiesTest() throws Exception { + File epubFile = new File(CHILDRENSLIT_SRC_FILEPATH); + RepInfo info = parseAndCheckValidity(epubFile, RepInfo.TRUE, RepInfo.TRUE); + assertEquals(0, info.getMessage().size()); // no errors + assertEquals("EPUB", info.getFormat()); + assertEquals(EXPECTED_MEDIATYPE, info.getMimeType()); + assertEquals(EXPECTED_VERSION_3_2, info.getVersion()); + // these may change, so just check they aren't null + assertNotNull(info.getCreated()); + assertNotNull(info.getLastModified()); + + Property metadata = info.getProperty(EPUBMETADATA_KEY); + assertEquals(PropertyArity.LIST, metadata.getArity()); + Map props = toMap(metadata); + + final long charcount = 324066L; + assertEquals(charcount, props.get(PROPNAME_CHARCOUNT)); + assertEquals(EN_LANGUAGE, props.get(PROPNAME_LANGUAGE)); + assertEquals(true, props.get(FEATURE_HASSCRIPTS)); + assertEquals(null, props.get(FEATURE_HASAUDIO)); + assertEquals(null, props.get(FEATURE_HASVIDEO)); + + Set mediaTypes = + new HashSet(Arrays.asList((String[]) props.get(PROPNAME_MEDIATYPES))); + final int expectedNumMediaTypes = 4; + assertEquals(expectedNumMediaTypes, mediaTypes.size()); + assertTrue(mediaTypes.contains(PNG_MIMETYPE)); + assertTrue(mediaTypes.contains(CSS_MIMETYPE)); + assertTrue(mediaTypes.contains(XHTML_MIMETYPE)); + assertTrue(mediaTypes.contains(NCX_MIMETYPE)); + + Set resources = + new HashSet(Arrays.asList((String[]) props.get(PROPNAME_RESOURCES))); + final int expectedNumResources = 5; + assertEquals(expectedNumResources, resources.size()); + assertTrue(resources.contains("EPUB/images/cover.png")); + assertTrue(resources.contains("EPUB/css/nav.css")); + + Set infoPropsSet = (Set) props.get(PROPNAME_INFO); + + Map infoProps = new HashMap(); + infoPropsSet.forEach(p -> infoProps.put(p.getName(), p.getValue())); + + assertEquals("http://www.gutenberg.org/ebooks/25545", infoProps.get(PROPNAME_IDENTIFIER)); + assertEquals("2008-05-20", infoProps.get(PROPNAME_DATE)); + assertEquals("Public domain in the USA.", infoProps.get(PROPNAME_RIGHTS)); + + Set titles = + new HashSet(Arrays.asList((String[]) infoProps.get(PROPNAME_TITLE))); + assertEquals(2, titles.size()); + assertTrue(titles.contains("Children's Literature")); + assertTrue(titles.contains("A Textbook of Sources for Teachers and Teacher-Training Classes")); + + Set creators = + new HashSet(Arrays.asList((String[]) infoProps.get(PROPNAME_CREATOR))); + assertEquals(2, creators.size()); + assertTrue(creators.contains("Charles Madison Curry")); + assertTrue(creators.contains("Erle Elsworth Clippinger")); + + Set subjects = + new HashSet(Arrays.asList((String[]) infoProps.get(PROPNAME_SUBJECTS))); + assertEquals(2, subjects.size()); + assertTrue(subjects.contains("Children -- Books and reading")); + assertTrue(subjects.contains("Children's literature -- Study and teaching")); + } + + /** + * Valid EPUB3 file and the checksum. Test file source: + * https://github.com/IDPF/epub3-samples/tree/master/30/childrens-literature + * + * @throws Exception + */ + @Test + public void parseValidEpub3ChecksumTest() throws Exception { + File epubFile = new File(CHILDRENSLIT_SRC_FILEPATH); + + EpubModule em = new EpubModule(); + JhoveBase je = new JhoveBase(); + je.setChecksumFlag(true); + em.setBase(je); + RepInfo info = new RepInfo(epubFile.getAbsolutePath()); + em.parse(new FileInputStream(epubFile), info, 0); + + Map checksums = new HashMap(); + info.getChecksum().forEach(cs -> checksums.put(cs.getType(), cs.getValue())); + assertEquals("da79cb9b", checksums.get(ChecksumType.CRC32)); + assertEquals("4c2dee43162e40690ba05926b9f42522", checksums.get(ChecksumType.MD5)); + assertEquals("72bcf1b71f4dd9b902a5fcd614601d5e488003a0", checksums.get(ChecksumType.SHA1)); + assertEquals( + "a1da72ef94de43a70a97538d5e789f74bf7c29bd99f8059697ae75a6b85a75f7", + checksums.get(ChecksumType.SHA256)); + final int expectedNumChecksums = 4; + assertEquals(expectedNumChecksums, info.getChecksum().size()); + } + + /** + * Check signatures for valid EPUB3 file. Test file source: + * https://github.com/IDPF/epub3-samples/tree/master/30/childrens-literature + * + * @throws Exception + */ + @Test + public void checkSignaturesValidEpub3Test() throws Exception { + File epubFile = new File(CHILDRENSLIT_SRC_FILEPATH); + assertTrue(checkSignatureMatch(epubFile, RepInfo.TRUE)); + } + + /** + * Valid EPUB3 file with remote and local multimedia embedded. Test file source: + * https://github.com/IDPF/epub-testsuite/tree/master/content/30/epub30-test-0100 + * + * @throws Exception + */ + @Test + public void parseValidEpub3WithRemoteResourcesTest() throws Exception { + String remoteMp3Url = "http://epubtest.org/media/remote/allison64-remote.mp3"; + String remoteMp4Url = "http://epubtest.org/media/remote/allison64-remote.mp4"; + + File epubFile = new File(EPUB3_WITH_MULTIMEDIA_FILEPATH); + RepInfo info = parseAndCheckValidity(epubFile, RepInfo.TRUE, RepInfo.TRUE); + + Property metadata = info.getProperty(EPUBMETADATA_KEY); + Map props = toMap(metadata); + + assertEquals(true, props.get(FEATURE_HASAUDIO)); + assertEquals(true, props.get(FEATURE_HASVIDEO)); + assertEquals(true, props.get(FEATURE_HASSCRIPTS)); + + Set references = + new HashSet(Arrays.asList((String[]) props.get(PROPNAME_REFERENCES))); + final int expectedNumReferences = 8; + assertEquals(expectedNumReferences, references.size()); + // spot check a few + assertTrue(references.contains("http://idpf.org/")); + assertTrue( + references.contains( + "http://idpf.org/epub/30/spec/epub30-contentdocs.html#sec-xhtml-content-switch")); + assertTrue(references.contains(remoteMp3Url)); + + Set resources = + new HashSet(Arrays.asList((String[]) props.get(PROPNAME_RESOURCES))); + final int expectedNumResources = 51; + assertEquals(expectedNumResources, resources.size()); + // spot check a few + assertTrue(resources.contains(remoteMp4Url)); + assertTrue(resources.contains(remoteMp3Url)); + assertTrue(resources.contains("EPUB/img/mathml-01-020-styling.png")); + assertTrue(resources.contains("EPUB/img/check.jpg")); + } + + /** + * Basic EPUB2 check. Test file source: + * https://github.com/KBNLresearch/epubPolicyTests/tree/master/content/epub20_minimal + * + * @throws Exception + */ + @Test + public void parseValidEpub2PropertiesTest() throws Exception { + File epubFile = new File(MINIMAL_EPUB_FILEPATH); + RepInfo info = parseAndCheckValidity(epubFile, RepInfo.TRUE, RepInfo.TRUE); + assertEquals(0, info.getMessage().size()); // no errors + assertEquals(EXPECTED_MEDIATYPE, info.getMimeType()); + assertEquals("2.0.1", info.getVersion()); + // may change, so just check it isn't null + assertNotNull(info.getCreated()); + + Property metadata = info.getProperty(EPUBMETADATA_KEY); + Map props = toMap(metadata); + + final long expectedCharCount = 4520L; + assertEquals(expectedCharCount, props.get(PROPNAME_CHARCOUNT)); + assertEquals(EN_LANGUAGE, props.get(PROPNAME_LANGUAGE)); + + Set mediaTypes = + new HashSet(Arrays.asList((String[]) props.get(PROPNAME_MEDIATYPES))); + final int expectedNumMediaTypes = 4; + assertEquals(expectedNumMediaTypes, mediaTypes.size()); + assertTrue(mediaTypes.contains(PNG_MIMETYPE)); + assertTrue(mediaTypes.contains(JPG_MIMETYPE)); + assertTrue(mediaTypes.contains(XHTML_MIMETYPE)); + assertTrue(mediaTypes.contains(NCX_MIMETYPE)); + + Set resources = + new HashSet(Arrays.asList((String[]) props.get(PROPNAME_RESOURCES))); + final int expectedNumResources = 4; + assertEquals(expectedNumResources, resources.size()); + assertTrue(resources.contains("OEBPS/Text/pdfMigration.html")); + assertTrue(resources.contains("OEBPS/Text/cover.xhtml")); + + Set references = + new HashSet(Arrays.asList((String[]) props.get(PROPNAME_REFERENCES))); + final int expectedNumReferences = 7; + assertEquals(expectedNumReferences, references.size()); + assertTrue( + references.contains("http://acroeng.adobe.com/PDFReference/ISO32000/PDF32000-Adobe.pdf")); + assertTrue( + references.contains( + "http://qanda.digipres.org/19/what-are-the-benefits-and-risks-of-using-the-pdf-a-file-format?show=21#a21")); + + Set infoPropsSet = (Set) props.get(PROPNAME_INFO); + + Map infoProps = new HashMap(); + infoPropsSet.forEach(p -> infoProps.put(p.getName(), p.getValue())); + + assertEquals( + "urn:uuid:f930f4b3-cba2-42ba-ab26-d49438ab00d6", infoProps.get(PROPNAME_IDENTIFIER)); + assertEquals("2015-03-03", infoProps.get(PROPNAME_DATE)); + assertEquals("When (not) to migrate a PDF to PDF/A", infoProps.get(PROPNAME_TITLE)); + assertEquals("Johan van der Knijff", infoProps.get(PROPNAME_CREATOR)); + } + + /** + * Valid EPUB2 file and the checksum. Test file source: + * https://github.com/KBNLresearch/epubPolicyTests/tree/master/content/epub20_minimal + * + * @throws Exception + */ + @Test + public void parseValidEpub2ChecksumTest() throws Exception { + File epubFile = new File(MINIMAL_EPUB_FILEPATH); + + EpubModule em = new EpubModule(); + JhoveBase je = new JhoveBase(); + je.setChecksumFlag(true); + em.setBase(je); + RepInfo info = new RepInfo(epubFile.getAbsolutePath()); + em.parse(new FileInputStream(epubFile), info, 0); + + Map checksums = new HashMap(); + info.getChecksum().forEach(cs -> checksums.put(cs.getType(), cs.getValue())); + assertEquals("8b80b526", checksums.get(ChecksumType.CRC32)); + assertEquals("b2110219d62c3c6ef1683c645636fd38", checksums.get(ChecksumType.MD5)); + assertEquals("79f20f6a499a640019a9bb0334652edbb954c3c9", checksums.get(ChecksumType.SHA1)); + assertEquals( + "4776bb33b1cce8598b31996ed0b4daf36e4b74e379b811a27e51efa15315744a", + checksums.get(ChecksumType.SHA256)); + final int expectedNumChecksum = 4; + assertEquals(expectedNumChecksum, info.getChecksum().size()); + } + + /** + * Check signatures for valid EPUB2 file. Test file source: + * https://github.com/KBNLresearch/epubPolicyTests/tree/master/content/epub20_minimal + * + * @throws Exception + */ + @Test + public void checkSignaturesValidEpub2Test() throws Exception { + File epubFile = new File(MINIMAL_EPUB_FILEPATH); + assertTrue(checkSignatureMatch(epubFile, RepInfo.TRUE)); + } + + /** + * Attempt to parse a file with an epub extension that is actually an empty file and not a zip at + * all. + * + * @throws Exception + */ + @Test + public void parseEmptyFileTest() throws Exception { + File epubFile = new File(EMPTY_EPUB_FILEPATH); + RepInfo info = parseAndCheckValidity(epubFile, RepInfo.FALSE, RepInfo.FALSE); + assertEquals(2, info.getMessage().size()); + assertEquals(OCTET_MIMETYPE, info.getMimeType()); + } + + /** + * Attempt to check signature of a file with an epub extension that is actually an empty file and + * not a zip at all. + * + * @throws Exception + */ + @Test + public void checkSignaturesEmptyFileTest() throws Exception { + File epubFile = new File(EMPTY_EPUB_FILEPATH); + assertTrue(checkSignatureMatch(epubFile, RepInfo.FALSE)); + } + + /** + * It's a valid EPUB but the file extension is wrong - this is not considered an issue by the + * EPUBCheck. Test file derived from: + * https://github.com/IDPF/epub3-samples/tree/master/30/childrens-literature + * + * @throws Exception + */ + @Test + public void parseEpubWithWrongExtensionTest() throws Exception { + File epubFile = new File(EPUB_WRONG_EXT_FILEPATH); + RepInfo info = parseAndCheckValidity(epubFile, RepInfo.TRUE, RepInfo.TRUE); + assertEquals(0, info.getMessage().size()); + } + + /** + * Attempt to check signature of a file that is a valid epub with a non-standard extension This + * should pass the signature test, since .epub extension not mandatory. + * + * @throws Exception + */ + @Test + public void checkSignaturesWrongExtensionTest() throws Exception { + File epubFile = new File(EPUB_WRONG_EXT_FILEPATH); + assertTrue(checkSignatureMatch(epubFile, RepInfo.TRUE)); + } + + /** + * Contents of the EPUB are valid but it was not compressed properly - it was zipped and renamed. + * Test file derived from: + * https://github.com/IDPF/epub3-samples/tree/master/30/childrens-literature + * + * @throws Exception + */ + @Test + public void parseImproperlyCompressedEpubTest() throws Exception { + File epubFile = new File(ZIPPED_EPUB_FILEPATH); + RepInfo info = parseAndCheckValidity(epubFile, RepInfo.FALSE, RepInfo.FALSE); + assertEquals(EXPECTED_MEDIATYPE, info.getMimeType()); + assertEquals(EXPECTED_VERSION_3_2, info.getVersion()); + assertEquals(1, info.getMessage().size()); + assertEquals("PKG-006", info.getMessage().get(0).getId()); + } + + /** + * Contents of the EPUB are valid but it was not compressed properly - it was zipped and renamed. + * Test file derived from: + * https://github.com/IDPF/epub3-samples/tree/master/30/childrens-literature + * + * @throws Exception + */ + @Test + public void checkSignaturesImproperlyCompressedEpubTest() throws Exception { + File epubFile = new File(ZIPPED_EPUB_FILEPATH); + assertTrue(checkSignatureMatch(epubFile, RepInfo.FALSE)); + } + + /** + * Parse shows not well formed / invalid when file is not an EPUB + * + * @throws Exception + */ + @Test + public void parseNonEpubTest() throws Exception { + File epubFile = new File(WRONG_EXT_NOT_AN_EPUB_FILEPATH); + RepInfo info = parseAndCheckValidity(epubFile, RepInfo.FALSE, RepInfo.FALSE); + List msgs = info.getMessage(); + assertEquals(1, msgs.size()); + } + + /** + * Check signature shows not well formed / invalid when file is not an EPUB + * + * @throws Exception + */ + @Test + public void checkSignaturesNonEpubTest() throws Exception { + File epubFile = new File(WRONG_EXT_NOT_AN_EPUB_FILEPATH); + assertTrue(checkSignatureMatch(epubFile, RepInfo.FALSE)); + } + + /** + * File is not an EPUB even though it has the correct extension + * + * @throws Exception + */ + @Test + public void parseNonEpubWithEpubExtensionTest() throws Exception { + File epubFile = new File(RIGHT_EXT_NOT_AN_EPUB_FILEPATH); + RepInfo info = parseAndCheckValidity(epubFile, RepInfo.FALSE, RepInfo.FALSE); + assertEquals(OCTET_MIMETYPE, info.getMimeType()); + List msgs = info.getMessage(); + final int expectedNumMessages = 3; + assertEquals(expectedNumMessages, msgs.size()); + } + + /** + * Check signature shows not well formed / invalid when file is not an EPUB + * + * @throws Exception + */ + @Test + public void checkSignaturesNonEpubWithEpubExtensionTest() throws Exception { + File epubFile = new File(RIGHT_EXT_NOT_AN_EPUB_FILEPATH); + assertTrue(checkSignatureMatch(epubFile, RepInfo.FALSE)); + } + + /** + * This epub has been modified to generate a single warning. Should be well formed and valid. File + * derived from: + * https://github.com/KBNLresearch/epubPolicyTests/tree/master/content/epub20_minimal + * + *

throws @Exception + */ + @Test + public void parseEpubWithWarningNoErrorsTest() throws Exception { + File epubFile = new File(EPUB2_WITH_WARNING_FILEPATH); + RepInfo info = parseAndCheckValidity(epubFile, RepInfo.TRUE, RepInfo.TRUE); + // check for the one warning error: + assertEquals(1, info.getMessage().size()); + Message msg = info.getMessage().get(0); + assertEquals("PKG-010", msg.getId()); + assertTrue(msg.getMessage().contains("WARN")); + // Do NOT compare strings because of translated messages + // assertTrue(msg.getMessage().contains("Filename contains spaces")); + assertTrue(msg instanceof ErrorMessage); + } + + /** + * This epub has missing fonts. Should be well formed but not valid. File derived from: + * https://github.com/KBNLresearch/epubPolicyTests/tree/master/content/epub20_missingfontresource + * + *

throws @Exception + */ + @Test + public void parseEpubWithMissingFontsTest() throws Exception { + File epubFile = new File(EPUB_MISSING_FONT_FILEPATH); + RepInfo info = parseAndCheckValidity(epubFile, RepInfo.TRUE, RepInfo.FALSE); + Property metadata = info.getProperty(EPUBMETADATA_KEY); + Map props = toMap(metadata); + Set fonts = (Set) props.get(PROPNAME_FONTS); + assertEquals(1, fonts.size()); + + Set font = (Set) fonts.iterator().next().getValue(); + Map fontinfo = new HashMap(); + font.forEach(f -> fontinfo.put(f.getName(), f.getValue())); + + // only one font in this file, listed but missing. + assertEquals("Courier", fontinfo.get(PROPNAME_FONTNAME)); + assertEquals(true, fontinfo.get(PROPNAME_FONTFILE)); + + // check for could not find referenced resource error. + assertEquals("RSC-007", info.getMessage().get(0).getId()); + } + + /** + * This EPUB3 has obfuscated fonts. It is valid, but there should be a flag signaling there is an + * encrypted file. Source of EPUB: + * https://github.com/IDPF/epub3-samples/tree/master/30/wasteland-woff-obf + */ + @Test + public void parseEpubWithObfuscatedFontsTest() throws Exception { + File epubFile = new File(EPUB_OBFUSCATED_FONT_FILEPATH); + RepInfo info = parseAndCheckValidity(epubFile, RepInfo.TRUE, RepInfo.TRUE); + Property metadata = info.getProperty(EPUBMETADATA_KEY); + Map props = toMap(metadata); + assertEquals(true, props.get(FEATURE_HASENCRYPTION)); + + Set fonts = (Set) props.get(PROPNAME_FONTS); + final int expectedNumFonts = 3; + assertEquals(expectedNumFonts, fonts.size()); + Set fontNames = new HashSet(); + for (Property font : fonts) { + Set fontinfo = (Set) font.getValue(); + Map map = new HashMap(); + fontinfo.forEach(f -> map.put(f.getName(), f.getValue())); + assertEquals(true, map.get(PROPNAME_FONTFILE)); + fontNames.add(map.get(PROPNAME_FONTNAME).toString()); } - - /** - * It's a valid EPUB but the file extension is wrong - this is not considered an - * issue by the EPUBCheck. Test file derived from: - * https://github.com/IDPF/epub3-samples/tree/master/30/childrens-literature - * - * @throws Exception - */ - @Test - public void parseEpubWithWrongExtensionTest() throws Exception { - File epubFile = new File(EPUB_WRONG_EXT_FILEPATH); - RepInfo info = parseAndCheckValidity(epubFile, RepInfo.TRUE, RepInfo.TRUE); - assertEquals(0, info.getMessage().size()); - } - - /** - * Attempt to check signature of a file that is a valid epub with a non-standard - * extension This should pass the signature test, since .epub extension not - * mandatory. - * - * @throws Exception - */ - @Test - public void checkSignaturesWrongExtensionTest() throws Exception { - File epubFile = new File(EPUB_WRONG_EXT_FILEPATH); - assertTrue(checkSignatureMatch(epubFile, RepInfo.TRUE)); - } - - /** - * Contents of the EPUB are valid but it was not compressed properly - it was - * zipped and renamed. Test file derived from: - * https://github.com/IDPF/epub3-samples/tree/master/30/childrens-literature - * - * @throws Exception - */ - @Test - public void parseImproperlyCompressedEpubTest() throws Exception { - File epubFile = new File(ZIPPED_EPUB_FILEPATH); - RepInfo info = parseAndCheckValidity(epubFile, RepInfo.FALSE, RepInfo.FALSE); - assertEquals(EXPECTED_MEDIATYPE, info.getMimeType()); - assertEquals(EXPECTED_VERSION_3_2, info.getVersion()); - assertEquals(1, info.getMessage().size()); - assertEquals("PKG-006", info.getMessage().get(0).getId()); - } - - /** - * Contents of the EPUB are valid but it was not compressed properly - it was - * zipped and renamed. Test file derived from: - * https://github.com/IDPF/epub3-samples/tree/master/30/childrens-literature - * - * @throws Exception - */ - @Test - public void checkSignaturesImproperlyCompressedEpubTest() throws Exception { - File epubFile = new File(ZIPPED_EPUB_FILEPATH); - assertTrue(checkSignatureMatch(epubFile, RepInfo.FALSE)); - } - - /** - * Parse shows not well formed / invalid when file is not an EPUB - * - * @throws Exception - */ - @Test - public void parseNonEpubTest() throws Exception { - File epubFile = new File(WRONG_EXT_NOT_AN_EPUB_FILEPATH); - RepInfo info = parseAndCheckValidity(epubFile, RepInfo.FALSE, RepInfo.FALSE); - List msgs = info.getMessage(); - assertEquals(1, msgs.size()); - } - - /** - * Check signature shows not well formed / invalid when file is not an EPUB - * - * @throws Exception - */ - @Test - public void checkSignaturesNonEpubTest() throws Exception { - File epubFile = new File(WRONG_EXT_NOT_AN_EPUB_FILEPATH); - assertTrue(checkSignatureMatch(epubFile, RepInfo.FALSE)); - } - - /** - * File is not an EPUB even though it has the correct extension - * - * @throws Exception - */ - @Test - public void parseNonEpubWithEpubExtensionTest() throws Exception { - File epubFile = new File(RIGHT_EXT_NOT_AN_EPUB_FILEPATH); - RepInfo info = parseAndCheckValidity(epubFile, RepInfo.FALSE, RepInfo.FALSE); - assertEquals(OCTET_MIMETYPE, info.getMimeType()); - List msgs = info.getMessage(); - final int expectedNumMessages = 3; - assertEquals(expectedNumMessages, msgs.size()); - } - - /** - * Check signature shows not well formed / invalid when file is not an EPUB - * - * @throws Exception - */ - @Test - public void checkSignaturesNonEpubWithEpubExtensionTest() throws Exception { - File epubFile = new File(RIGHT_EXT_NOT_AN_EPUB_FILEPATH); - assertTrue(checkSignatureMatch(epubFile, RepInfo.FALSE)); - } - - - /** - * This epub has been modified to generate a single warning. Should be well - * formed and valid. File derived from: - * https://github.com/KBNLresearch/epubPolicyTests/tree/master/content/epub20_minimal - * - * throws @Exception - */ - @Test - public void parseEpubWithWarningNoErrorsTest() throws Exception { - File epubFile = new File(EPUB2_WITH_WARNING_FILEPATH); - RepInfo info = parseAndCheckValidity(epubFile, RepInfo.TRUE, RepInfo.TRUE); - // check for the one warning error: - assertEquals(1, info.getMessage().size()); - Message msg = info.getMessage().get(0); - assertEquals("PKG-010", msg.getId()); - assertTrue(msg.getMessage().contains("WARN")); - // Do NOT compare strings because of translated messages - // assertTrue(msg.getMessage().contains("Filename contains spaces")); - assertTrue(msg instanceof ErrorMessage); - } - - /** - * This epub has missing fonts. Should be well formed but not valid. File - * derived from: - * https://github.com/KBNLresearch/epubPolicyTests/tree/master/content/epub20_missingfontresource - * - * throws @Exception - */ - @Test - public void parseEpubWithMissingFontsTest() throws Exception { - File epubFile = new File(EPUB_MISSING_FONT_FILEPATH); - RepInfo info = parseAndCheckValidity(epubFile, RepInfo.TRUE, RepInfo.FALSE); - Property metadata = info.getProperty(EPUBMETADATA_KEY); - Map props = toMap(metadata); - Set fonts = (Set) props.get(PROPNAME_FONTS); - assertEquals(1, fonts.size()); - - Set font = (Set) fonts.iterator().next().getValue(); - Map fontinfo = new HashMap(); - font.forEach(f -> fontinfo.put(f.getName(), f.getValue())); - - // only one font in this file, listed but missing. - assertEquals("Courier", fontinfo.get(PROPNAME_FONTNAME)); - assertEquals(true, fontinfo.get(PROPNAME_FONTFILE)); - - // check for could not find referenced resource error. - assertEquals("RSC-007", info.getMessage().get(0).getId()); + assertEquals(expectedNumFonts, fontNames.size()); + assertTrue(fontNames.contains("OldStandard")); + assertTrue(fontNames.contains("OldStandard,bold")); + assertTrue(fontNames.contains("OldStandard,italic")); + } + + /** + * This EPUB has been modified to have a single non-fatal error. The error complains about a + * remote resource not being listed in the OCF (RSC-006). Test file derived from: + * https://github.com/KBNLresearch/epubPolicyTests/tree/master/content/epub20_minimal + * + * @throws Exception + */ + @Test + public void parseEpubWithNonFatalErrorTest() throws Exception { + File epubFile = new File(EPUB2_WITH_ERROR_FILEPATH); + RepInfo info = parseAndCheckValidity(epubFile, RepInfo.TRUE, RepInfo.FALSE); + assertEquals(1, info.getMessage().size()); + Message msg = info.getMessage().get(0); + assertEquals("RSC-006", msg.getId()); + assertTrue(msg instanceof ErrorMessage); + } + + /** + * EPUBs require an OPF file. In this test EPUB it has been removed. The test confirms that the + * result is a FATAL error that flags the file as not well formed. EPUBCheck also returns a + * non-fatal ERROR with similar text. File derived from: + * https://github.com/KBNLresearch/epubPolicyTests/tree/master/content/epub20_minimal + * + * @throws Exception + */ + @Test + public void parseEpubMissingOpfTest() throws Exception { + File epubFile = new File(EPUB2_MISSING_OPF_FILEPATH); + RepInfo info = parseAndCheckValidity(epubFile, RepInfo.FALSE, RepInfo.FALSE); + + assertEquals(OCTET_MIMETYPE, info.getMimeType()); + + Set msgCodes = new HashSet(); + assertEquals(2, info.getMessage().size()); + Message msg1 = info.getMessage().get(0); + Message msg2 = info.getMessage().get(1); + assertTrue(msg1 instanceof ErrorMessage); + msgCodes.add(msg1.getId()); + assertTrue(msg2 instanceof ErrorMessage); + msgCodes.add(msg2.getId()); + assertTrue(msgCodes.contains("OPF-002")); + assertTrue(msgCodes.contains("RSC-001")); + } + + /** + * This EPUB3 has a fixed layout. This test confirms that the hasFixedLayout flag is present in + * the JHOVE properties section. File source: + * https://github.com/IDPF/epub3-samples/tree/master/30/page-blanche + * + * @throws Exception + */ + @Test + public void parseEpubWithFixedLayoutTest() throws Exception { + File epubFile = new File(EPUB3_FIXED_LAYOUT_FILEPATH); + RepInfo info = parseAndCheckValidity(epubFile, RepInfo.TRUE, RepInfo.TRUE); + Property metadata = info.getProperty(EPUBMETADATA_KEY); + Map props = toMap(metadata); + assertEquals(true, props.get(FEATURE_HASFIXEDLAYOUT)); + assertEquals(0, info.getMessage().size()); // no issues + } + + /** + * EPUB file simulates encryption - should return hasEncryption. Note that checkSignatures returns + * well formed and valid, while parse on same file returns invalid. Test file source: + * https://github.com/KBNLresearch/epubPolicyTests/tree/master/content/epub20_minimal_encryption + * + * @throws Exception + */ + @Test + public void parseEpub2WithEncryptionTest() throws Exception { + File epubFile = new File(EPUB2_ENCRYPTION); + // well formed but not valid because can't parse encrypted file to confirm + RepInfo info = parseAndCheckValidity(epubFile, RepInfo.TRUE, RepInfo.FALSE); + + Property metadata = info.getProperty(PROPNAME_EPUB_METADATA); + Map props = toMap(metadata); + assertEquals(true, props.get(FEATURE_HASENCRYPTION)); + + boolean bFoundRsc4 = false; + for (Message msg : info.getMessage()) { + if ("RSC-004".equals(msg.getId())) { + // Do NOT compare strings because of translations... + // assertTrue(msg.getMessage().contains("could not be decrypted")); + bFoundRsc4 = true; + break; + } } - - /** - * This EPUB3 has obfuscated fonts. It is valid, but there should be a flag - * signaling there is an encrypted file. Source of EPUB: - * https://github.com/IDPF/epub3-samples/tree/master/30/wasteland-woff-obf - * - */ - @Test - public void parseEpubWithObfuscatedFontsTest() throws Exception { - File epubFile = new File(EPUB_OBFUSCATED_FONT_FILEPATH); - RepInfo info = parseAndCheckValidity(epubFile, RepInfo.TRUE, RepInfo.TRUE); - Property metadata = info.getProperty(EPUBMETADATA_KEY); - Map props = toMap(metadata); - assertEquals(true, props.get(FEATURE_HASENCRYPTION)); - - Set fonts = (Set) props.get(PROPNAME_FONTS); - final int expectedNumFonts = 3; - assertEquals(expectedNumFonts, fonts.size()); - Set fontNames = new HashSet(); - for (Property font : fonts) { - Set fontinfo = (Set) font.getValue(); - Map map = new HashMap(); - fontinfo.forEach(f -> map.put(f.getName(), f.getValue())); - assertEquals(true, map.get(PROPNAME_FONTFILE)); - fontNames.add(map.get(PROPNAME_FONTNAME).toString()); - } - assertEquals(expectedNumFonts, fontNames.size()); - assertTrue(fontNames.contains("OldStandard")); - assertTrue(fontNames.contains("OldStandard,bold")); - assertTrue(fontNames.contains("OldStandard,italic")); - } - - /** - * This EPUB has been modified to have a single non-fatal error. The error - * complains about a remote resource not being listed in the OCF (RSC-006). Test - * file derived from: - * https://github.com/KBNLresearch/epubPolicyTests/tree/master/content/epub20_minimal - * - * @throws Exception - */ - @Test - public void parseEpubWithNonFatalErrorTest() throws Exception { - File epubFile = new File(EPUB2_WITH_ERROR_FILEPATH); - RepInfo info = parseAndCheckValidity(epubFile, RepInfo.TRUE, RepInfo.FALSE); - assertEquals(1, info.getMessage().size()); - Message msg = info.getMessage().get(0); - assertEquals("RSC-006", msg.getId()); - assertTrue(msg instanceof ErrorMessage); - } - - /** - * EPUBs require an OPF file. In this test EPUB it has been removed. The test - * confirms that the result is a FATAL error that flags the file as not well - * formed. EPUBCheck also returns a non-fatal ERROR with similar text. File - * derived from: - * https://github.com/KBNLresearch/epubPolicyTests/tree/master/content/epub20_minimal - * - * @throws Exception - */ - @Test - public void parseEpubMissingOpfTest() throws Exception { - File epubFile = new File(EPUB2_MISSING_OPF_FILEPATH); - RepInfo info = parseAndCheckValidity(epubFile, RepInfo.FALSE, RepInfo.FALSE); - - assertEquals(OCTET_MIMETYPE, info.getMimeType()); - - Set msgCodes = new HashSet(); - assertEquals(2, info.getMessage().size()); - Message msg1 = info.getMessage().get(0); - Message msg2 = info.getMessage().get(1); - assertTrue(msg1 instanceof ErrorMessage); - msgCodes.add(msg1.getId()); - assertTrue(msg2 instanceof ErrorMessage); - msgCodes.add(msg2.getId()); - assertTrue(msgCodes.contains("OPF-002")); - assertTrue(msgCodes.contains("RSC-001")); - } - - /** - * This EPUB3 has a fixed layout. This test confirms that the hasFixedLayout - * flag is present in the JHOVE properties section. File source: - * https://github.com/IDPF/epub3-samples/tree/master/30/page-blanche - * - * @throws Exception - */ - @Test - public void parseEpubWithFixedLayoutTest() throws Exception { - File epubFile = new File(EPUB3_FIXED_LAYOUT_FILEPATH); - RepInfo info = parseAndCheckValidity(epubFile, RepInfo.TRUE, RepInfo.TRUE); - Property metadata = info.getProperty(EPUBMETADATA_KEY); - Map props = toMap(metadata); - assertEquals(true, props.get(FEATURE_HASFIXEDLAYOUT)); - assertEquals(0, info.getMessage().size()); // no issues - } - - /** - * EPUB file simulates encryption - should return hasEncryption. Note that - * checkSignatures returns well formed and valid, while parse on same file - * returns invalid. Test file source: - * https://github.com/KBNLresearch/epubPolicyTests/tree/master/content/epub20_minimal_encryption - * - * @throws Exception - */ - @Test - public void parseEpub2WithEncryptionTest() throws Exception { - File epubFile = new File(EPUB2_ENCRYPTION); - // well formed but not valid because can't parse encrypted file to confirm - RepInfo info = parseAndCheckValidity(epubFile, RepInfo.TRUE, RepInfo.FALSE); - - Property metadata = info.getProperty(PROPNAME_EPUB_METADATA); - Map props = toMap(metadata); - assertEquals(true, props.get(FEATURE_HASENCRYPTION)); - - boolean bFoundRsc4 = false; - for (Message msg : info.getMessage()) { - if ("RSC-004".equals(msg.getId())) { - // Do NOT compare strings because of translations... - // assertTrue(msg.getMessage().contains("could not be decrypted")); - bFoundRsc4 = true; - break; - } - } - assertEquals("RSC-004 error was not found", true, bFoundRsc4); - } - - /** - * EPUB file simulates encryption. This is a case where signature will show it - * is well formed and valid but parsing will reveal encryption. Test file - * source: - * https://github.com/KBNLresearch/epubPolicyTests/tree/master/content/epub20_minimal_encryption - * - * @throws Exception - */ - @Test - public void checkSignaturesEpub2WithEncryptionTest() throws Exception { - File epubFile = new File(EPUB2_ENCRYPTION); - assertTrue(checkSignatureMatch(epubFile, RepInfo.TRUE)); - } - - /** - * EPUB file has title sensitive to incorrect encoding. This tests parses and - * confirms title is properly encoded in output source: - * https://github.com/w3c/epubcheck/blob/master/src/test/resources/30/epub/valid/edupub-multiple-renditions.epub - * - * @throws Exception - */ - @Test - public void parseEpub3TitleEncodingTest() throws Exception { - File epubFile = new File(EPUB3_TITLE_ENCODING); - String expectedTitle = "महाभारत"; - // well formed and valid - RepInfo info = parseAndCheckValidity(epubFile, RepInfo.TRUE, RepInfo.TRUE); - - Property metadata = info.getProperty(PROPNAME_EPUB_METADATA); - Map props = toMap(metadata); - Set infoPropsSet = (Set) props.get(PROPNAME_INFO); - Map infoProps = new HashMap(); - infoPropsSet.forEach(p -> infoProps.put(p.getName(), p.getValue())); - String title = (String) infoProps.get(PROPNAME_TITLE); - assertEquals(expectedTitle, title); - } - - - /** - * Passes file to checkSignatures(), checks wellformed, valid, and sigmatch - * values are as expected. - * - * @param epubFile - * @param expectedWellFormedValue RepInfo.TRUE/FALSE/UNDETERMINED. - * @return - * @throws Exception - */ - private boolean checkSignatureMatch(File epubFile, int expectedWellFormedValue) throws Exception { - RepInfo info = new RepInfo(epubFile.getAbsolutePath()); - EpubModule em = new EpubModule(); - em.checkSignatures(epubFile, new FileInputStream(epubFile), info); - assertEquals(expectedWellFormedValue, info.getWellFormed()); - // valid should always be undetermined in sig match - assertEquals(RepInfo.UNDETERMINED, info.getValid()); - if (expectedWellFormedValue == RepInfo.TRUE) { - List sigmatch = info.getSigMatch(); - assertEquals("EPUB-ptc", sigmatch.get(0)); - } else { - assertEquals(0, info.getSigMatch().size()); - } - // all fine? return true. - return true; + assertEquals("RSC-004 error was not found", true, bFoundRsc4); + } + + /** + * EPUB file simulates encryption. This is a case where signature will show it is well formed and + * valid but parsing will reveal encryption. Test file source: + * https://github.com/KBNLresearch/epubPolicyTests/tree/master/content/epub20_minimal_encryption + * + * @throws Exception + */ + @Test + public void checkSignaturesEpub2WithEncryptionTest() throws Exception { + File epubFile = new File(EPUB2_ENCRYPTION); + assertTrue(checkSignatureMatch(epubFile, RepInfo.TRUE)); + } + + /** + * EPUB file has title sensitive to incorrect encoding. This tests parses and confirms title is + * properly encoded in output source: + * https://github.com/w3c/epubcheck/blob/master/src/test/resources/30/epub/valid/edupub-multiple-renditions.epub + * + * @throws Exception + */ + @Test + public void parseEpub3TitleEncodingTest() throws Exception { + File epubFile = new File(EPUB3_TITLE_ENCODING); + String expectedTitle = "महाभारत"; + // well formed and valid + RepInfo info = parseAndCheckValidity(epubFile, RepInfo.TRUE, RepInfo.TRUE); + + Property metadata = info.getProperty(PROPNAME_EPUB_METADATA); + Map props = toMap(metadata); + Set infoPropsSet = (Set) props.get(PROPNAME_INFO); + Map infoProps = new HashMap(); + infoPropsSet.forEach(p -> infoProps.put(p.getName(), p.getValue())); + String title = (String) infoProps.get(PROPNAME_TITLE); + assertEquals(expectedTitle, title); + } + + /** + * Passes file to checkSignatures(), checks wellformed, valid, and sigmatch values are as + * expected. + * + * @param epubFile + * @param expectedWellFormedValue RepInfo.TRUE/FALSE/UNDETERMINED. + * @return + * @throws Exception + */ + private boolean checkSignatureMatch(File epubFile, int expectedWellFormedValue) throws Exception { + RepInfo info = new RepInfo(epubFile.getAbsolutePath()); + EpubModule em = new EpubModule(); + em.checkSignatures(epubFile, new FileInputStream(epubFile), info); + assertEquals(expectedWellFormedValue, info.getWellFormed()); + // valid should always be undetermined in sig match + assertEquals(RepInfo.UNDETERMINED, info.getValid()); + if (expectedWellFormedValue == RepInfo.TRUE) { + List sigmatch = info.getSigMatch(); + assertEquals("EPUB-ptc", sigmatch.get(0)); + } else { + assertEquals(0, info.getSigMatch().size()); } - - /** - * Passes file to parse() checks well formed and valid status against expected - * values then returns RepInfo for further analy - * - * @param epubFile file to parse - * @param expectedWellFormedValue RepInfo.TRUE/FALSE/UNDETERMINED. - * @param expectedValidityValue RepInfo.TRUE/FALSE/UNDETERMINED. - * @return - * @throws Exception - */ - private RepInfo parseAndCheckValidity(File epubFile, int expectedWellFormedValue, int expectedValidityValue) - throws Exception { - RepInfo info = new RepInfo(epubFile.getAbsolutePath()); - EpubModule em = new EpubModule(); - em.parse(new FileInputStream(epubFile), info, 0); - assertEquals(expectedWellFormedValue, info.getWellFormed()); - assertEquals(expectedValidityValue, info.getValid()); - return info; + // all fine? return true. + return true; + } + + /** + * Passes file to parse() checks well formed and valid status against expected values then returns + * RepInfo for further analy + * + * @param epubFile file to parse + * @param expectedWellFormedValue RepInfo.TRUE/FALSE/UNDETERMINED. + * @param expectedValidityValue RepInfo.TRUE/FALSE/UNDETERMINED. + * @return + * @throws Exception + */ + private RepInfo parseAndCheckValidity( + File epubFile, int expectedWellFormedValue, int expectedValidityValue) throws Exception { + RepInfo info = new RepInfo(epubFile.getAbsolutePath()); + EpubModule em = new EpubModule(); + em.parse(new FileInputStream(epubFile), info, 0); + assertEquals(expectedWellFormedValue, info.getWellFormed()); + assertEquals(expectedValidityValue, info.getValid()); + return info; + } + + /** + * Utility to convert metadata property to a Map. Property passed in must have a list as its + * value. + * + * @param propertymap + * @return + */ + private static Map toMap(Property metadata) { + if (!(metadata.getValue() instanceof List)) { + throw new IllegalArgumentException( + "\"metadata\" property must be a List in order for it to be converted to a map"); } - - /** - * Utility to convert metadata property to a Map. Property passed in must have a - * list as its value. - * - * @param propertymap - * @return - */ - private static Map toMap(Property metadata) { - if (!(metadata.getValue() instanceof List)) { - throw new IllegalArgumentException( - "\"metadata\" property must be a List in order for it to be converted to a map"); - } - Map propertymap = new HashMap(); - List propList = (List) metadata.getValue(); - propList.forEach(p -> propertymap.put(p.getName(), p.getValue())); - return propertymap; - } - + Map propertymap = new HashMap(); + List propList = (List) metadata.getValue(); + propList.forEach(p -> propertymap.put(p.getName(), p.getValue())); + return propertymap; + } } diff --git a/jhove-ext-modules/src/test/java/org/ithaka/portico/jhove/module/epub/JhoveRepInfoReportTest.java b/jhove-ext-modules/src/test/java/org/ithaka/portico/jhove/module/epub/JhoveRepInfoReportTest.java index c2c70dd30..baa6dd8ae 100644 --- a/jhove-ext-modules/src/test/java/org/ithaka/portico/jhove/module/epub/JhoveRepInfoReportTest.java +++ b/jhove-ext-modules/src/test/java/org/ithaka/portico/jhove/module/epub/JhoveRepInfoReportTest.java @@ -4,22 +4,20 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; +import com.adobe.epubcheck.api.EPUBLocation; +import com.adobe.epubcheck.messages.Message; +import com.adobe.epubcheck.messages.MessageId; +import com.adobe.epubcheck.messages.Severity; +import com.adobe.epubcheck.reporting.CheckMessage; +import com.adobe.epubcheck.util.FeatureEnum; import java.text.SimpleDateFormat; import java.util.Arrays; import java.util.Date; import java.util.List; - import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; -import com.adobe.epubcheck.api.EPUBLocation; -import com.adobe.epubcheck.messages.Message; -import com.adobe.epubcheck.messages.MessageId; -import com.adobe.epubcheck.messages.Severity; -import com.adobe.epubcheck.reporting.CheckMessage; -import com.adobe.epubcheck.util.FeatureEnum; - /** * Unit tests for JhoveRepInfoReport * @@ -28,386 +26,392 @@ @RunWith(JUnit4.class) public class JhoveRepInfoReportTest { - private static final String FAKE_EPUB_FILEPATH = "/fake/filepath.epub"; - - private static final MessageId FATAL_MSG_ID_1 = MessageId.OPF_001; - private static final String FATAL_MSG_1 = "Houston, we've had a problem here."; - private static final String FATAL_MSG_SUGGEST_1 = "Close the hatch."; - - private static final MessageId FATAL_MSG_ID_2 = MessageId.OPF_002; - private static final String FATAL_MSG_2 = "It's a fatal error."; - private static final String FATAL_MSG_SUGGEST_2 = "Needs to be fixed."; - - private static final MessageId ERROR_MSG_ID = MessageId.ACC_001; - private static final String ERROR_MSG = "There has been an non-fatal error"; - private static final String ERROR_MSG_SUGGEST = "Report it"; - - private static final MessageId WARN_MSG_ID = MessageId.CHK_001; - private static final String WARN_MSG = "Consider yourself warned"; - private static final String WARN_MSG_SUGGEST = "Don't do it again!"; - - private EPUBLocation messageLoc = EPUBLocation.create("epub.opf"); - private EPUBLocation messageLoc2 = EPUBLocation.create("content.xhtml"); - private String messageArg = "fakearg"; - - - /** - * Sets a single fatal message then confirms the properties are - * correct when you getAllMessages() - * @throws Exception - */ - @Test - public void setAndGetMessageTest() throws Exception { - JhoveRepInfoReport report = new JhoveRepInfoReport(FAKE_EPUB_FILEPATH); - report.message(fatalMsg1(), messageLoc, messageArg); - List allMsgs = report.getAllMessages(); - assertEquals(1, allMsgs.size()); - CheckMessage fatalmsg = allMsgs.get(0); - assertEquals(FATAL_MSG_ID_1.toString(), fatalmsg.getID()); - assertEquals(Severity.FATAL, fatalmsg.getSeverity()); - assertEquals(FATAL_MSG_1, fatalmsg.getMessage()); - assertEquals(FATAL_MSG_SUGGEST_1, fatalmsg.getSuggestion()); - assertEquals(1, fatalmsg.getLocations().size()); - assertEquals(messageLoc, fatalmsg.getLocations().get(0)); - assertEquals(1, report.getFatalErrorCount()); - assertEquals(0, report.getErrorCount()); - assertEquals(0, report.getWarningCount()); - } - - /** - * Adds 2 different messages to report and confirms they are returned by - * getAllMessages() - * @throws Exception - */ - @Test - public void setAndGet2DifferentMessagesTest() throws Exception { - JhoveRepInfoReport report = new JhoveRepInfoReport(FAKE_EPUB_FILEPATH); - report.message(fatalMsg1(), messageLoc, messageArg); - report.message(warnMsg(), messageLoc, messageArg); - List allMsgs = report.getAllMessages(); - assertEquals(2, allMsgs.size()); - - CheckMessage fatalmsg = allMsgs.get(0); - CheckMessage warnmsg = allMsgs.get(1); - //make sure these are assigned correctly, if not switch them - if (allMsgs.get(0).getSeverity().equals(Severity.WARNING)) { - fatalmsg = allMsgs.get(1); - warnmsg = allMsgs.get(0); - } - - assertEquals(FATAL_MSG_ID_1.toString(), fatalmsg.getID()); - assertEquals(Severity.FATAL, fatalmsg.getSeverity()); - assertEquals(FATAL_MSG_1, fatalmsg.getMessage()); - assertEquals(FATAL_MSG_SUGGEST_1, fatalmsg.getSuggestion()); - assertEquals(1, fatalmsg.getLocations().size()); - assertEquals(messageLoc, fatalmsg.getLocations().get(0)); - - assertEquals(WARN_MSG_ID.toString(), warnmsg.getID()); - assertEquals(Severity.WARNING, warnmsg.getSeverity()); - assertEquals(WARN_MSG, warnmsg.getMessage()); - assertEquals(WARN_MSG_SUGGEST, warnmsg.getSuggestion()); - assertEquals(1, warnmsg.getLocations().size()); - assertEquals(messageLoc, warnmsg.getLocations().get(0)); - - assertEquals(1, report.getFatalErrorCount()); - assertEquals(0, report.getErrorCount()); - assertEquals(1, report.getWarningCount()); - } - - /** - * Adds 2 messages to report that are the same ID but have different locations - * and confirms they are returned as 1 message by getAllMessages() - * @throws Exception - */ - @Test - public void setAndGet2MessagesWithSameIdTest() throws Exception { - JhoveRepInfoReport report = new JhoveRepInfoReport(FAKE_EPUB_FILEPATH); - report.message(errorMsg(), messageLoc, messageArg); - report.message(errorMsg(), messageLoc2, messageArg); - List allMsgs = report.getAllMessages(); - assertEquals(1, allMsgs.size()); - - CheckMessage errormsg = allMsgs.get(0); - assertEquals(ERROR_MSG_ID.toString(), errormsg.getID()); - assertEquals(Severity.ERROR, errormsg.getSeverity()); - assertEquals(ERROR_MSG, errormsg.getMessage()); - assertEquals(ERROR_MSG_SUGGEST, errormsg.getSuggestion()); - assertEquals(2, errormsg.getLocations().size()); - assertTrue(errormsg.getLocations().contains(messageLoc)); - assertTrue(errormsg.getLocations().contains(messageLoc2)); - - assertEquals(0, report.getFatalErrorCount()); - assertEquals(1, report.getErrorCount()); - assertEquals(0, report.getWarningCount()); - } - - - /** - * Adds multiple messages and confirms the count is accurate afterwards - * @throws Exception - */ - @Test - public void setAndGetMultipleMessagesTest() throws Exception { - JhoveRepInfoReport report = new JhoveRepInfoReport(FAKE_EPUB_FILEPATH); - report.message(fatalMsg1(), messageLoc, messageArg); - report.message(fatalMsg2(), messageLoc, messageArg); - report.message(fatalMsg2(), messageLoc2, messageArg); - report.message(errorMsg(), messageLoc, messageArg); - report.message(warnMsg(), messageLoc, messageArg); - //added 5 messages, but two are the same message id, so there should be 4 total - List allMsgs = report.getAllMessages(); - final int expectedMessagesSize = 4; - assertEquals(expectedMessagesSize, allMsgs.size()); - assertEquals(2, report.getFatalErrorCount()); - assertEquals(1, report.getErrorCount()); - assertEquals(1, report.getWarningCount()); - } - - /** - * Checks all default values for report in which no properties were populated - * @throws Exception - */ - @Test - public void reportWithNoInfoTest() throws Exception { - JhoveRepInfoReport report = new JhoveRepInfoReport(FAKE_EPUB_FILEPATH); - assertEquals(0, report.getAllMessages().size()); - assertEquals(0, report.getCharacterCount()); - assertArrayEquals(null, report.getContributors()); - assertEquals(null, report.getCreationDate()); - assertArrayEquals(null, report.getCreators()); - assertEquals(null, report.getDate()); - assertEquals(0, report.getEmbeddedFonts().size()); - assertEquals(FAKE_EPUB_FILEPATH, report.getEpubFileName()); - assertEquals(0, report.getFatalErrorCount()); - assertEquals(0, report.getFeatures().size()); - assertEquals("application/octet-stream", report.getFormat()); - assertEquals(null, report.getIdentifier()); - assertEquals(0, report.getInfoCount()); - assertEquals(null, report.getLanguage()); - assertEquals(null, report.getLastModifiedDate()); - assertArrayEquals(null, report.getMediaTypes()); - assertEquals(0, report.getPageCount()); - assertEquals(null, report.getPublisher()); - assertArrayEquals(null, report.getReferences()); - assertEquals(0, report.getRefFonts().size()); - assertArrayEquals(null, report.getRights()); - assertArrayEquals(null, report.getSubjects()); - assertArrayEquals(null, report.getTitles()); - assertEquals(0, report.getUsageCount()); - assertEquals(null, report.getVersion()); - assertEquals(0, report.getWarningCount()); - } - - /** - * Checks that lists of local and remote resources are correctly managed - * - * @throws Exception - */ - @Test - public void remoteAndLocalResourcesTest() throws Exception { - final String localResource1 = "EPUB/image.jpg"; - final String localResource2 = "EPUB/code.xhtml"; - final String localResource3 = "EPUB/somefile.xhtml"; - final String localResource4 = "EPUB/image2.jpg"; - final String remoteResource1 = "https://example.com/remotepath1"; - final String remoteResource2 = "https://example.com/remotepath2"; - final String remoteResource3 = "https://example.com/remotepath3"; - final String reference1 = "https://example.com/referencepath1"; - final String reference2 = "https://example.com/referencepath2"; - final String reference3 = "https://example.com/referencepath3"; - - JhoveRepInfoReport report = new JhoveRepInfoReport(FAKE_EPUB_FILEPATH); - - // references consist of both citation type references / links - // but also any embedded remote resources that appear to be part of the epub. - report.info(null, FeatureEnum.REFERENCE, reference1); - report.info(null, FeatureEnum.REFERENCE, reference2); - report.info(null, FeatureEnum.REFERENCE, reference3); - - // resources are what appear to be part of the EPUB and may be local or remote - report.info(null, FeatureEnum.RESOURCE, localResource1); - report.info(null, FeatureEnum.RESOURCE, localResource2); - report.info(null, FeatureEnum.RESOURCE, localResource3); - report.info(null, FeatureEnum.RESOURCE, localResource4); - report.info(null, FeatureEnum.RESOURCE, remoteResource1); - report.info(null, FeatureEnum.RESOURCE, remoteResource2); - report.info(null, FeatureEnum.RESOURCE, remoteResource3); - - final int expectedNumReferences = 3; - assertEquals(expectedNumReferences, report.getReferences().length); - String[] expectedReferences = new String[] { reference1, reference2, reference3 }; - assertTrue(arraysSame(expectedReferences, report.getReferences())); - - final int expectedNumResources = 7; - assertEquals(expectedNumResources, report.getResources().length); - String[] expectedResources = new String[] { localResource1, localResource2, localResource3, localResource4, remoteResource1, - remoteResource2, remoteResource3 }; - assertTrue(arraysSame(expectedResources, report.getResources())); - } - - /** - * Checks all report fields are correctly parsed - * - * @throws Exception - */ - @Test - public void reportWithAllInfoTest() throws Exception { - final String DATEFORMAT = "yyyy-MM-dd'T'HH:mm:ss'Z'"; - - final String formatName = "EPUB"; - final String formatVersion = "3.2"; - final String creationDate = "2018-08-12T12:00:12Z"; - final String modifiedDate = "2018-08-12T13:00:13Z"; - final Date dCreationDate = new SimpleDateFormat(DATEFORMAT).parse(creationDate); - final Date dModifiedDate = new SimpleDateFormat(DATEFORMAT).parse(modifiedDate); - final String pagesCount = "22"; - final String charsCount = "77777"; - final String mimetype1 = "text/xml"; - final String mimetype2 = "audio/mpeg"; - final String mimetype3 = "video/ogg"; - final String fontEmbedded = "Arial"; - final String fontReference = "Verdana"; - final String resource1 = "EPUB/imageA.jpg"; - final String resource2 = "EPUB/codeA.xhtml"; - final String reference = "https://example.com/referencepath1A"; - final String language = "en-us"; - final String title = "A Very Good Book"; - final String creator1 = "J Lee"; - final String creator2 = "B Singh"; - final String contributor = "D Rodríguez"; - final String publisher = "Simple Publishing Inc"; - final String subject1 = "Information science--Sociological aspects"; - final String subject2 = "Communication"; - final String rights = "CC0"; - final String sdate = "2018"; - final String identifier = "doi:10.0000/abcez123"; - final String sTrue = "true"; - - JhoveRepInfoReport report = new JhoveRepInfoReport(FAKE_EPUB_FILEPATH); - - - // populate all values - report.info(null, FeatureEnum.CHARS_COUNT, charsCount); - report.info(null, FeatureEnum.CREATION_DATE, creationDate); - report.info(null, FeatureEnum.DC_CONTRIBUTOR, contributor); - report.info(null, FeatureEnum.DC_CREATOR, creator1); - report.info(null, FeatureEnum.DC_CREATOR, creator2); - report.info(null, FeatureEnum.DC_DATE, sdate); - report.info(null, FeatureEnum.DC_LANGUAGE, language); - report.info(null, FeatureEnum.DC_PUBLISHER, publisher); - report.info(null, FeatureEnum.DC_RIGHTS, rights); - report.info(null, FeatureEnum.DC_SUBJECT, subject1); - report.info(null, FeatureEnum.DC_SUBJECT, subject2); - report.info(null, FeatureEnum.DC_TITLE, title); - report.info(null, FeatureEnum.DECLARED_MIMETYPE, mimetype1); - report.info(null, FeatureEnum.DECLARED_MIMETYPE, mimetype2); - report.info(null, FeatureEnum.DECLARED_MIMETYPE, mimetype3); - report.info(null, FeatureEnum.FONT_EMBEDDED, fontEmbedded); - report.info(null, FeatureEnum.FONT_REFERENCE, fontReference); - report.info(null, FeatureEnum.FORMAT_NAME, formatName); - report.info(null, FeatureEnum.FORMAT_VERSION, formatVersion); - report.info(null, FeatureEnum.HAS_ENCRYPTION, sTrue); - report.info(null, FeatureEnum.HAS_FIXED_LAYOUT, sTrue); - report.info(null, FeatureEnum.HAS_SCRIPTS, sTrue); - report.info(null, FeatureEnum.HAS_SIGNATURES, sTrue); - report.info(null, FeatureEnum.MODIFIED_DATE, modifiedDate); - report.info(null, FeatureEnum.PAGES_COUNT, pagesCount); - report.info(null, FeatureEnum.REFERENCE, reference); - report.info(null, FeatureEnum.RESOURCE, resource1); - report.info(null, FeatureEnum.RESOURCE, resource2); - report.info(null, FeatureEnum.UNIQUE_IDENT, identifier); - - assertEquals(FAKE_EPUB_FILEPATH, report.getEpubFileName()); - assertEquals(Long.parseLong(charsCount), report.getCharacterCount()); - assertEquals(dCreationDate, report.getCreationDate()); - - assertEquals(1, report.getContributors().length); - assertEquals(contributor, report.getContributors()[0]); - - assertEquals(2, report.getCreators().length); - assertTrue(arraysSame(new String[] { creator1, creator2 }, report.getCreators())); - - assertEquals(sdate, report.getDate()); - assertEquals(language, report.getLanguage()); - assertEquals(publisher, report.getPublisher()); - - assertEquals(1, report.getRights().length); - assertEquals(rights, report.getRights()[0]); - - assertEquals(2, report.getSubjects().length); - assertTrue(arraysSame(new String[] { subject1, subject2 }, report.getSubjects())); - - assertEquals(1, report.getTitles().length); - assertEquals(title, report.getTitles()[0]); - - final int expectedNumMediaTypes = 3; - assertEquals(expectedNumMediaTypes, report.getMediaTypes().length); - assertTrue(arraysSame(new String[] { mimetype1, mimetype2, mimetype3 }, report.getMediaTypes())); - - assertEquals(1, report.getEmbeddedFonts().size()); - assertEquals(fontEmbedded, report.getEmbeddedFonts().iterator().next()); - - assertEquals(1, report.getRefFonts().size()); - assertEquals(fontReference, report.getRefFonts().iterator().next()); - - assertEquals(formatName, report.getFormat()); - assertEquals(formatVersion, report.getVersion()); - - final int expectedNumFeatures = 6; - assertEquals(expectedNumFeatures, report.getFeatures().size()); - assertTrue(report.getFeatures().contains("hasEncryption")); - assertTrue(report.getFeatures().contains("hasSignatures")); - assertTrue(report.getFeatures().contains("hasAudio")); - assertTrue(report.getFeatures().contains("hasVideo")); - assertTrue(report.getFeatures().contains("hasFixedLayout")); - assertTrue(report.getFeatures().contains("hasScripts")); - - assertEquals(dModifiedDate, report.getLastModifiedDate()); - assertEquals(Long.parseLong(pagesCount), report.getPageCount()); - - assertEquals(1, report.getReferences().length); - assertEquals(reference, report.getReferences()[0]); - - assertEquals(2, report.getResources().length); - assertTrue(arraysSame(new String[] { resource1, resource2 }, report.getResources())); - - assertEquals(identifier, report.getIdentifier()); - - // shouldn't affect message counts - assertEquals(0, report.getFatalErrorCount()); - assertEquals(0, report.getInfoCount()); - assertEquals(0, report.getUsageCount()); - assertEquals(0, report.getWarningCount()); - } - - private static Message fatalMsg1() { - return new Message(FATAL_MSG_ID_1, Severity.FATAL, FATAL_MSG_1, FATAL_MSG_SUGGEST_1); + private static final String FAKE_EPUB_FILEPATH = "/fake/filepath.epub"; + + private static final MessageId FATAL_MSG_ID_1 = MessageId.OPF_001; + private static final String FATAL_MSG_1 = "Houston, we've had a problem here."; + private static final String FATAL_MSG_SUGGEST_1 = "Close the hatch."; + + private static final MessageId FATAL_MSG_ID_2 = MessageId.OPF_002; + private static final String FATAL_MSG_2 = "It's a fatal error."; + private static final String FATAL_MSG_SUGGEST_2 = "Needs to be fixed."; + + private static final MessageId ERROR_MSG_ID = MessageId.ACC_001; + private static final String ERROR_MSG = "There has been an non-fatal error"; + private static final String ERROR_MSG_SUGGEST = "Report it"; + + private static final MessageId WARN_MSG_ID = MessageId.CHK_001; + private static final String WARN_MSG = "Consider yourself warned"; + private static final String WARN_MSG_SUGGEST = "Don't do it again!"; + + private EPUBLocation messageLoc = EPUBLocation.create("epub.opf"); + private EPUBLocation messageLoc2 = EPUBLocation.create("content.xhtml"); + private String messageArg = "fakearg"; + + /** + * Sets a single fatal message then confirms the properties are correct when you getAllMessages() + * + * @throws Exception + */ + @Test + public void setAndGetMessageTest() throws Exception { + JhoveRepInfoReport report = new JhoveRepInfoReport(FAKE_EPUB_FILEPATH); + report.message(fatalMsg1(), messageLoc, messageArg); + List allMsgs = report.getAllMessages(); + assertEquals(1, allMsgs.size()); + CheckMessage fatalmsg = allMsgs.get(0); + assertEquals(FATAL_MSG_ID_1.toString(), fatalmsg.getID()); + assertEquals(Severity.FATAL, fatalmsg.getSeverity()); + assertEquals(FATAL_MSG_1, fatalmsg.getMessage()); + assertEquals(FATAL_MSG_SUGGEST_1, fatalmsg.getSuggestion()); + assertEquals(1, fatalmsg.getLocations().size()); + assertEquals(messageLoc, fatalmsg.getLocations().get(0)); + assertEquals(1, report.getFatalErrorCount()); + assertEquals(0, report.getErrorCount()); + assertEquals(0, report.getWarningCount()); + } + + /** + * Adds 2 different messages to report and confirms they are returned by getAllMessages() + * + * @throws Exception + */ + @Test + public void setAndGet2DifferentMessagesTest() throws Exception { + JhoveRepInfoReport report = new JhoveRepInfoReport(FAKE_EPUB_FILEPATH); + report.message(fatalMsg1(), messageLoc, messageArg); + report.message(warnMsg(), messageLoc, messageArg); + List allMsgs = report.getAllMessages(); + assertEquals(2, allMsgs.size()); + + CheckMessage fatalmsg = allMsgs.get(0); + CheckMessage warnmsg = allMsgs.get(1); + // make sure these are assigned correctly, if not switch them + if (allMsgs.get(0).getSeverity().equals(Severity.WARNING)) { + fatalmsg = allMsgs.get(1); + warnmsg = allMsgs.get(0); } - private static Message fatalMsg2() { - return new Message(FATAL_MSG_ID_2, Severity.FATAL, FATAL_MSG_2, FATAL_MSG_SUGGEST_2); - } - - private static Message errorMsg() { - return new Message(ERROR_MSG_ID, Severity.ERROR, ERROR_MSG, ERROR_MSG_SUGGEST); - } - - private static Message warnMsg() { - return new Message(WARN_MSG_ID, Severity.WARNING, WARN_MSG, WARN_MSG_SUGGEST); - } - - /** - * Compares arrays independent of order - * - * @param arr1 - * @param arr2 - * @return - */ - private static boolean arraysSame(String[] arr1, String[] arr2) { - Arrays.sort(arr1); - Arrays.sort(arr2); - return Arrays.equals(arr1, arr2); - } - - + assertEquals(FATAL_MSG_ID_1.toString(), fatalmsg.getID()); + assertEquals(Severity.FATAL, fatalmsg.getSeverity()); + assertEquals(FATAL_MSG_1, fatalmsg.getMessage()); + assertEquals(FATAL_MSG_SUGGEST_1, fatalmsg.getSuggestion()); + assertEquals(1, fatalmsg.getLocations().size()); + assertEquals(messageLoc, fatalmsg.getLocations().get(0)); + + assertEquals(WARN_MSG_ID.toString(), warnmsg.getID()); + assertEquals(Severity.WARNING, warnmsg.getSeverity()); + assertEquals(WARN_MSG, warnmsg.getMessage()); + assertEquals(WARN_MSG_SUGGEST, warnmsg.getSuggestion()); + assertEquals(1, warnmsg.getLocations().size()); + assertEquals(messageLoc, warnmsg.getLocations().get(0)); + + assertEquals(1, report.getFatalErrorCount()); + assertEquals(0, report.getErrorCount()); + assertEquals(1, report.getWarningCount()); + } + + /** + * Adds 2 messages to report that are the same ID but have different locations and confirms they + * are returned as 1 message by getAllMessages() + * + * @throws Exception + */ + @Test + public void setAndGet2MessagesWithSameIdTest() throws Exception { + JhoveRepInfoReport report = new JhoveRepInfoReport(FAKE_EPUB_FILEPATH); + report.message(errorMsg(), messageLoc, messageArg); + report.message(errorMsg(), messageLoc2, messageArg); + List allMsgs = report.getAllMessages(); + assertEquals(1, allMsgs.size()); + + CheckMessage errormsg = allMsgs.get(0); + assertEquals(ERROR_MSG_ID.toString(), errormsg.getID()); + assertEquals(Severity.ERROR, errormsg.getSeverity()); + assertEquals(ERROR_MSG, errormsg.getMessage()); + assertEquals(ERROR_MSG_SUGGEST, errormsg.getSuggestion()); + assertEquals(2, errormsg.getLocations().size()); + assertTrue(errormsg.getLocations().contains(messageLoc)); + assertTrue(errormsg.getLocations().contains(messageLoc2)); + + assertEquals(0, report.getFatalErrorCount()); + assertEquals(1, report.getErrorCount()); + assertEquals(0, report.getWarningCount()); + } + + /** + * Adds multiple messages and confirms the count is accurate afterwards + * + * @throws Exception + */ + @Test + public void setAndGetMultipleMessagesTest() throws Exception { + JhoveRepInfoReport report = new JhoveRepInfoReport(FAKE_EPUB_FILEPATH); + report.message(fatalMsg1(), messageLoc, messageArg); + report.message(fatalMsg2(), messageLoc, messageArg); + report.message(fatalMsg2(), messageLoc2, messageArg); + report.message(errorMsg(), messageLoc, messageArg); + report.message(warnMsg(), messageLoc, messageArg); + // added 5 messages, but two are the same message id, so there should be 4 total + List allMsgs = report.getAllMessages(); + final int expectedMessagesSize = 4; + assertEquals(expectedMessagesSize, allMsgs.size()); + assertEquals(2, report.getFatalErrorCount()); + assertEquals(1, report.getErrorCount()); + assertEquals(1, report.getWarningCount()); + } + + /** + * Checks all default values for report in which no properties were populated + * + * @throws Exception + */ + @Test + public void reportWithNoInfoTest() throws Exception { + JhoveRepInfoReport report = new JhoveRepInfoReport(FAKE_EPUB_FILEPATH); + assertEquals(0, report.getAllMessages().size()); + assertEquals(0, report.getCharacterCount()); + assertArrayEquals(null, report.getContributors()); + assertEquals(null, report.getCreationDate()); + assertArrayEquals(null, report.getCreators()); + assertEquals(null, report.getDate()); + assertEquals(0, report.getEmbeddedFonts().size()); + assertEquals(FAKE_EPUB_FILEPATH, report.getEpubFileName()); + assertEquals(0, report.getFatalErrorCount()); + assertEquals(0, report.getFeatures().size()); + assertEquals("application/octet-stream", report.getFormat()); + assertEquals(null, report.getIdentifier()); + assertEquals(0, report.getInfoCount()); + assertEquals(null, report.getLanguage()); + assertEquals(null, report.getLastModifiedDate()); + assertArrayEquals(null, report.getMediaTypes()); + assertEquals(0, report.getPageCount()); + assertEquals(null, report.getPublisher()); + assertArrayEquals(null, report.getReferences()); + assertEquals(0, report.getRefFonts().size()); + assertArrayEquals(null, report.getRights()); + assertArrayEquals(null, report.getSubjects()); + assertArrayEquals(null, report.getTitles()); + assertEquals(0, report.getUsageCount()); + assertEquals(null, report.getVersion()); + assertEquals(0, report.getWarningCount()); + } + + /** + * Checks that lists of local and remote resources are correctly managed + * + * @throws Exception + */ + @Test + public void remoteAndLocalResourcesTest() throws Exception { + final String localResource1 = "EPUB/image.jpg"; + final String localResource2 = "EPUB/code.xhtml"; + final String localResource3 = "EPUB/somefile.xhtml"; + final String localResource4 = "EPUB/image2.jpg"; + final String remoteResource1 = "https://example.com/remotepath1"; + final String remoteResource2 = "https://example.com/remotepath2"; + final String remoteResource3 = "https://example.com/remotepath3"; + final String reference1 = "https://example.com/referencepath1"; + final String reference2 = "https://example.com/referencepath2"; + final String reference3 = "https://example.com/referencepath3"; + + JhoveRepInfoReport report = new JhoveRepInfoReport(FAKE_EPUB_FILEPATH); + + // references consist of both citation type references / links + // but also any embedded remote resources that appear to be part of the epub. + report.info(null, FeatureEnum.REFERENCE, reference1); + report.info(null, FeatureEnum.REFERENCE, reference2); + report.info(null, FeatureEnum.REFERENCE, reference3); + + // resources are what appear to be part of the EPUB and may be local or remote + report.info(null, FeatureEnum.RESOURCE, localResource1); + report.info(null, FeatureEnum.RESOURCE, localResource2); + report.info(null, FeatureEnum.RESOURCE, localResource3); + report.info(null, FeatureEnum.RESOURCE, localResource4); + report.info(null, FeatureEnum.RESOURCE, remoteResource1); + report.info(null, FeatureEnum.RESOURCE, remoteResource2); + report.info(null, FeatureEnum.RESOURCE, remoteResource3); + + final int expectedNumReferences = 3; + assertEquals(expectedNumReferences, report.getReferences().length); + String[] expectedReferences = new String[] {reference1, reference2, reference3}; + assertTrue(arraysSame(expectedReferences, report.getReferences())); + + final int expectedNumResources = 7; + assertEquals(expectedNumResources, report.getResources().length); + String[] expectedResources = + new String[] { + localResource1, + localResource2, + localResource3, + localResource4, + remoteResource1, + remoteResource2, + remoteResource3 + }; + assertTrue(arraysSame(expectedResources, report.getResources())); + } + + /** + * Checks all report fields are correctly parsed + * + * @throws Exception + */ + @Test + public void reportWithAllInfoTest() throws Exception { + final String DATEFORMAT = "yyyy-MM-dd'T'HH:mm:ss'Z'"; + + final String formatName = "EPUB"; + final String formatVersion = "3.2"; + final String creationDate = "2018-08-12T12:00:12Z"; + final String modifiedDate = "2018-08-12T13:00:13Z"; + final Date dCreationDate = new SimpleDateFormat(DATEFORMAT).parse(creationDate); + final Date dModifiedDate = new SimpleDateFormat(DATEFORMAT).parse(modifiedDate); + final String pagesCount = "22"; + final String charsCount = "77777"; + final String mimetype1 = "text/xml"; + final String mimetype2 = "audio/mpeg"; + final String mimetype3 = "video/ogg"; + final String fontEmbedded = "Arial"; + final String fontReference = "Verdana"; + final String resource1 = "EPUB/imageA.jpg"; + final String resource2 = "EPUB/codeA.xhtml"; + final String reference = "https://example.com/referencepath1A"; + final String language = "en-us"; + final String title = "A Very Good Book"; + final String creator1 = "J Lee"; + final String creator2 = "B Singh"; + final String contributor = "D Rodríguez"; + final String publisher = "Simple Publishing Inc"; + final String subject1 = "Information science--Sociological aspects"; + final String subject2 = "Communication"; + final String rights = "CC0"; + final String sdate = "2018"; + final String identifier = "doi:10.0000/abcez123"; + final String sTrue = "true"; + + JhoveRepInfoReport report = new JhoveRepInfoReport(FAKE_EPUB_FILEPATH); + + // populate all values + report.info(null, FeatureEnum.CHARS_COUNT, charsCount); + report.info(null, FeatureEnum.CREATION_DATE, creationDate); + report.info(null, FeatureEnum.DC_CONTRIBUTOR, contributor); + report.info(null, FeatureEnum.DC_CREATOR, creator1); + report.info(null, FeatureEnum.DC_CREATOR, creator2); + report.info(null, FeatureEnum.DC_DATE, sdate); + report.info(null, FeatureEnum.DC_LANGUAGE, language); + report.info(null, FeatureEnum.DC_PUBLISHER, publisher); + report.info(null, FeatureEnum.DC_RIGHTS, rights); + report.info(null, FeatureEnum.DC_SUBJECT, subject1); + report.info(null, FeatureEnum.DC_SUBJECT, subject2); + report.info(null, FeatureEnum.DC_TITLE, title); + report.info(null, FeatureEnum.DECLARED_MIMETYPE, mimetype1); + report.info(null, FeatureEnum.DECLARED_MIMETYPE, mimetype2); + report.info(null, FeatureEnum.DECLARED_MIMETYPE, mimetype3); + report.info(null, FeatureEnum.FONT_EMBEDDED, fontEmbedded); + report.info(null, FeatureEnum.FONT_REFERENCE, fontReference); + report.info(null, FeatureEnum.FORMAT_NAME, formatName); + report.info(null, FeatureEnum.FORMAT_VERSION, formatVersion); + report.info(null, FeatureEnum.HAS_ENCRYPTION, sTrue); + report.info(null, FeatureEnum.HAS_FIXED_LAYOUT, sTrue); + report.info(null, FeatureEnum.HAS_SCRIPTS, sTrue); + report.info(null, FeatureEnum.HAS_SIGNATURES, sTrue); + report.info(null, FeatureEnum.MODIFIED_DATE, modifiedDate); + report.info(null, FeatureEnum.PAGES_COUNT, pagesCount); + report.info(null, FeatureEnum.REFERENCE, reference); + report.info(null, FeatureEnum.RESOURCE, resource1); + report.info(null, FeatureEnum.RESOURCE, resource2); + report.info(null, FeatureEnum.UNIQUE_IDENT, identifier); + + assertEquals(FAKE_EPUB_FILEPATH, report.getEpubFileName()); + assertEquals(Long.parseLong(charsCount), report.getCharacterCount()); + assertEquals(dCreationDate, report.getCreationDate()); + + assertEquals(1, report.getContributors().length); + assertEquals(contributor, report.getContributors()[0]); + + assertEquals(2, report.getCreators().length); + assertTrue(arraysSame(new String[] {creator1, creator2}, report.getCreators())); + + assertEquals(sdate, report.getDate()); + assertEquals(language, report.getLanguage()); + assertEquals(publisher, report.getPublisher()); + + assertEquals(1, report.getRights().length); + assertEquals(rights, report.getRights()[0]); + + assertEquals(2, report.getSubjects().length); + assertTrue(arraysSame(new String[] {subject1, subject2}, report.getSubjects())); + + assertEquals(1, report.getTitles().length); + assertEquals(title, report.getTitles()[0]); + + final int expectedNumMediaTypes = 3; + assertEquals(expectedNumMediaTypes, report.getMediaTypes().length); + assertTrue(arraysSame(new String[] {mimetype1, mimetype2, mimetype3}, report.getMediaTypes())); + + assertEquals(1, report.getEmbeddedFonts().size()); + assertEquals(fontEmbedded, report.getEmbeddedFonts().iterator().next()); + + assertEquals(1, report.getRefFonts().size()); + assertEquals(fontReference, report.getRefFonts().iterator().next()); + + assertEquals(formatName, report.getFormat()); + assertEquals(formatVersion, report.getVersion()); + + final int expectedNumFeatures = 6; + assertEquals(expectedNumFeatures, report.getFeatures().size()); + assertTrue(report.getFeatures().contains("hasEncryption")); + assertTrue(report.getFeatures().contains("hasSignatures")); + assertTrue(report.getFeatures().contains("hasAudio")); + assertTrue(report.getFeatures().contains("hasVideo")); + assertTrue(report.getFeatures().contains("hasFixedLayout")); + assertTrue(report.getFeatures().contains("hasScripts")); + + assertEquals(dModifiedDate, report.getLastModifiedDate()); + assertEquals(Long.parseLong(pagesCount), report.getPageCount()); + + assertEquals(1, report.getReferences().length); + assertEquals(reference, report.getReferences()[0]); + + assertEquals(2, report.getResources().length); + assertTrue(arraysSame(new String[] {resource1, resource2}, report.getResources())); + + assertEquals(identifier, report.getIdentifier()); + + // shouldn't affect message counts + assertEquals(0, report.getFatalErrorCount()); + assertEquals(0, report.getInfoCount()); + assertEquals(0, report.getUsageCount()); + assertEquals(0, report.getWarningCount()); + } + + private static Message fatalMsg1() { + return new Message(FATAL_MSG_ID_1, Severity.FATAL, FATAL_MSG_1, FATAL_MSG_SUGGEST_1); + } + + private static Message fatalMsg2() { + return new Message(FATAL_MSG_ID_2, Severity.FATAL, FATAL_MSG_2, FATAL_MSG_SUGGEST_2); + } + + private static Message errorMsg() { + return new Message(ERROR_MSG_ID, Severity.ERROR, ERROR_MSG, ERROR_MSG_SUGGEST); + } + + private static Message warnMsg() { + return new Message(WARN_MSG_ID, Severity.WARNING, WARN_MSG, WARN_MSG_SUGGEST); + } + + /** + * Compares arrays independent of order + * + * @param arr1 + * @param arr2 + * @return + */ + private static boolean arraysSame(String[] arr1, String[] arr2) { + Arrays.sort(arr1); + Arrays.sort(arr2); + return Arrays.equals(arr1, arr2); + } } diff --git a/jhove-modules/aiff-hul/src/main/java/ADump.java b/jhove-modules/aiff-hul/src/main/java/ADump.java index f200f415d..4252123b3 100644 --- a/jhove-modules/aiff-hul/src/main/java/ADump.java +++ b/jhove-modules/aiff-hul/src/main/java/ADump.java @@ -1,234 +1,216 @@ -/********************************************************************** - * JDump - JSTOR/Harvard Object Validation Environment - * Copyright 2004-2005 by the President and Fellows of Harvard College +/** + * ******************************************************************** JDump - JSTOR/Harvard Object + * Validation Environment Copyright 2004-2005 by the President and Fellows of Harvard College * - * This program 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 2 of the License, or (at - * your option) any later version. - * - * This program 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. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 - * USA - **********************************************************************/ - + *

This program 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 2 + * of the License, or (at your option) any later version. + * + *

This program 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. + * + *

You should have received a copy of the GNU Lesser General Public License along with this + * program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA ******************************************************************** + */ import edu.harvard.hul.ois.jhove.*; import edu.harvard.hul.ois.jhove.module.aiff.*; import java.io.*; /** * Dump contents of AIFF file in human-readable format. + * * @author Gary McGath */ public class ADump extends Dump { - /* Fixed value for first 4 bytes */ - private static final int[] sigByte = - { 0X46, 0X4F, 0X52, 0X4D }; - private static final boolean ENDIAN = true; /* bigEndian */ + /* Fixed value for first 4 bytes */ + private static final int[] sigByte = {0X46, 0X4F, 0X52, 0X4D}; + private static final boolean ENDIAN = true; /* bigEndian */ - /****************************************************************** - * MAIN ENTRY POINT. - ******************************************************************/ + /** + * **************************************************************** MAIN ENTRY POINT. + * **************************************************************** + */ - /** - * Main entry point. - * @param args Command line arguments - */ - public static void main (String [] args) - { - if (args.length < 1) { - System.err.println ("usage: java ADump file"); - System.exit (-1); + /** + * Main entry point. + * + * @param args Command line arguments + */ + public static void main(String[] args) { + if (args.length < 1) { + System.err.println("usage: java ADump file"); + System.exit(-1); + } + try { + FileInputStream file = new FileInputStream(args[0]); + BufferedInputStream buffer = new BufferedInputStream(file); + DataInputStream stream = new DataInputStream(buffer); + long os = 0; + for (int i = 0; i < 4; i++) { + int ch; + ch = stream.readUnsignedByte(); + if (ch != sigByte[i]) { + System.out.println("No AIFF FORM header"); + System.exit(-2); } + } + os += 4; + long ckSize = ModuleBase.readUnsignedInt(stream, ENDIAN); + + // Read the file type + StringBuffer formType = new StringBuffer(4); + for (int i = 0; i < 4; i++) { + int ch = ModuleBase.readUnsignedByte(stream); + formType.append((char) ch); + } + System.out.println("00000000: FORM " + ckSize + ": " + formType); + + boolean aiff_c = false; + if ("AIFC".equals(formType.toString())) { + aiff_c = true; + } + + StringBuffer sbuf = new StringBuffer(); + boolean endOfFile = false; + while (!endOfFile) { + // Read chunks try { - FileInputStream file = new FileInputStream (args[0]); - BufferedInputStream buffer = new BufferedInputStream (file); - DataInputStream stream = new DataInputStream (buffer); - long os = 0; - for (int i=0; i<4; i++) { - int ch; - ch = stream.readUnsignedByte(); - if (ch != sigByte[i]) { - System.out.println ("No AIFF FORM header"); - System.exit (-2); - } + sbuf.setLength(0); + // Read chunk name. + for (int i = 0; i < 4; i++) { + int ch = ModuleBase.readUnsignedByte(stream); + sbuf.append((char) ch); + } + String ckID = sbuf.toString(); + // Read size (excluding chunk name and size fields) + ckSize = ModuleBase.readUnsignedInt(stream, ENDIAN); + System.out.print(leading(os, 8) + os + ": " + ckID + " " + ckSize); + long alreadyRead = 0; + if ("AESD".equals(ckID)) { + int[] aes = new int[24]; + for (int i = 0; i < 24; i++) { + aes[i] = ModuleBase.readUnsignedByte(stream); } - os += 4; - long ckSize = ModuleBase.readUnsignedInt (stream, ENDIAN); - - // Read the file type - StringBuffer formType = new StringBuffer (4); - for (int i=0; i<4; i++) { - int ch = ModuleBase.readUnsignedByte(stream); - formType.append((char) ch); + System.out.print(": " + aes[0]); + for (int i = 1; i < 24; i++) { + System.out.print("," + aes[i]); } - System.out.println ("00000000: FORM " + ckSize + ": " + formType); - - boolean aiff_c = false; - if ("AIFC".equals (formType.toString ())) { - aiff_c = true; - } - - StringBuffer sbuf = new StringBuffer (); - boolean endOfFile = false; - while (!endOfFile) { - // Read chunks - try { - sbuf.setLength(0); - // Read chunk name. - for (int i=0; i<4; i++) { - int ch = ModuleBase.readUnsignedByte(stream); - sbuf.append((char) ch); - } - String ckID = sbuf.toString (); - // Read size (excluding chunk name and size fields) - ckSize = ModuleBase.readUnsignedInt (stream, ENDIAN); - System.out.print (leading (os, 8) + os + ": " + ckID + - " " + ckSize); - long alreadyRead = 0; - if ("AESD".equals (ckID)) { - int [] aes = new int[24]; - for (int i=0; i<24; i++) { - aes[i] = ModuleBase.readUnsignedByte (stream); - - } - System.out.print (": " + aes[0]); - for (int i=1; i<24; i++) { - System.out.print ("," + aes[i]); - } - alreadyRead = 24; - } - else if ("ANNO".equals (ckID) || - "AUTH".equals (ckID) || - "(c) ".equals (ckID) || - "NAME".equals (ckID)) { - sbuf.setLength (0); - for (int i=0; i 0); at most one instance of each of the " + - "following optional chunks: Marker, Instrument, Audio Recording, " + - "Comments, Name, Author, Copyright; all chunks required by a given " + - "profile exist in the file; all chunk structures are well-formed: " + - "a four ASCII character ID, followed by a 32 signed integer size, " + - "followed by a size byte data block (if size is odd, then the data " + - "block includes a final padding byte of value 0x00); and no data " + - "exist before the first byte of the chunk or after the " + - "last byte of the last chunk"; - private static final String VALIDITY = "The file is well-formed"; - private static final String REPINFO = - "Properties capturing the technical attributes of the audio " + - "from all chunks"; - private static final String NOTE = null; - private static final String RIGHTS = "Copyright 2004-2007 by JSTOR and " + - "the President and Fellows of Harvard College. " + - "Released under the GNU Lesser General Public License."; - - /****************************************************************** - * CLASS CONSTRUCTOR. - ******************************************************************/ - /** - * Instantiates an AiffModule object. - */ - public AiffModule () - { - super (NAME, RELEASE, DATE, FORMAT, COVERAGE, MIMETYPE, WELLFORMED, - VALIDITY, REPINFO, NOTE, RIGHTS, false); - _vendor = Agent.harvardInstance(); - - Document doc = new Document ("Audio Interchange File Format: " + - "\"AIFF\", A Standard for Sampled Sound " + - "Files, Version 1.3", DocumentType.REPORT); - Builder builder = new Agent.Builder ("Apple Computer, Inc.", - AgentType.COMMERCIAL).address ("1 Infinite Loop, Cupertino, CA 95014").telephone("(408) 996-1010").web("http://www.apple.com/"); - Agent appleAgent = builder.build(); - doc.setAuthor (appleAgent); - doc.setDate ("1989-01-04"); - doc.setIdentifier (new Identifier ("http://developer.apple.com/documentation/QuickTime/INMAC/SOUND/imsoundmgr.30.htm#pgfId=3190", - IdentifierType.URL)); - _specification.add (doc); - - doc = new Document ("Audio Interchange File Format AIFF-C: " + - "A revision to include compressed audio data", - DocumentType.REPORT); - doc.setAuthor (appleAgent); - doc.setDate ("1991-08-26"); - doc.setNote ("*** DRAFT ***"); // Asterisks as in the printed document - _specification.add (doc); - - Signature sig = new ExternalSignature ("AIFF", SignatureType.FILETYPE, - SignatureUseType.OPTIONAL); - _signature.add (sig); - sig = new ExternalSignature ("AIFC", SignatureType.FILETYPE, - SignatureUseType.OPTIONAL); - _signature.add (sig); - sig = new ExternalSignature (".aif", SignatureType.EXTENSION, - SignatureUseType.OPTIONAL); - _signature.add (sig); - sig = new ExternalSignature (".aifc", SignatureType.EXTENSION, - SignatureUseType.OPTIONAL, - "For AIFF-C profile"); - _signature.add (sig); - - sig = new InternalSignature ("FORM", SignatureType.MAGIC, - SignatureUseType.MANDATORY, 0); - _signature.add (sig); - - sig = new InternalSignature ("AIFF", SignatureType.MAGIC, - SignatureUseType.OPTIONAL, 8, - "For AIFF profile"); - _signature.add (sig); - - sig = new InternalSignature ("AIFC", SignatureType.MAGIC, - SignatureUseType.OPTIONAL, 0, - "For AIFF-C profile"); - _signature.add (sig); - - _bigEndian = true; -} - - /** - * Parses the content of a purported AIFF digital object and stores the - * results in RepInfo. - * - * - * @param stream An InputStream, positioned at its beginning, - * which is generated from the object to be parsed - * @param info A fresh RepInfo object which will be modified - * to reflect the results of the parsing - * @param parseIndex Must be 0 in first call to parse. If - * parse returns a nonzero value, it must be - * called again with parseIndex - * equal to that return value. - */ - @Override - public int parse (InputStream stream, RepInfo info, int parseIndex) - throws IOException - { - initParse (); - info.setFormat (_format[0]); - info.setMimeType (_mimeType[0]); - info.setModule (this); - _aesMetadata.setPrimaryIdentifier(info.getUri()); - if (info.getURLFlag ()) { - _aesMetadata.setOtherPrimaryIdentifierType("URI"); - } - else { - _aesMetadata.setPrimaryIdentifierType(AESAudioMetadata.FILE_NAME); - } - - setupDataStream(stream, info); - - try { - // Check the start of the file for the right opening bytes - for (int i = 0; i < 4; i++) { - int ch = readUnsignedByte(_dstream, this); - if (ch != sigByte[i]) { - info.setMessage(new ErrorMessage(MessageConstants.AIFF_HUL_1, 0)); - info.setWellFormed (RepInfo.FALSE); - return 0; - } - } - /* If we got this far, take note that the signature is OK. */ - info.setSigMatch(_name); - - // Get the length of the Form chunk. This includes all - // the subsequent chunks in the file, but excludes the - // header ("FORM" and the length itself). -// bytesRemaining = readUnsignedInt (_dstream, _bigEndian, this); - bytesRemaining = readUnsignedInt (_dstream, BIGENDIAN, this); - - // Read the file type. - if (!readFileType (info)) { - return 0; - } - - while (bytesRemaining > 0) { - if (!readChunk (info)) { - break; - } - } - } - catch (EOFException e) { - info.setWellFormed (RepInfo.FALSE); - info.setMessage (new ErrorMessage - (MessageConstants.AIFF_HUL_8, _nByte)); - return 0; - } - - if (!commonChunkSeen) { - info.setWellFormed (RepInfo.FALSE); - info.setMessage (new ErrorMessage - (MessageConstants.AIFF_HUL_2)); - } - if (fileType == AIFCTYPE && !formatVersionChunkSeen) { - info.setWellFormed (RepInfo.FALSE); - info.setMessage (new ErrorMessage - (MessageConstants.AIFF_HUL_3)); - } - if (info.getWellFormed () != RepInfo.TRUE) { - return 0; - } - - /* This file looks OK. */ - if (_ckSummer != null){ - skipDstreamToEnd(info); - // Set the checksums in the report if they're calculated - setChecksums(this._ckSummer, info); - } - - if (fileType == AIFFTYPE) { - info.setProfile("AIFF"); - } - else if (fileType == AIFCTYPE) { - info.setProfile ("AIFF-C"); - } - - _aesMetadata.setByteOrder (thisFileBigEndian ? AESAudioMetadata.BIG_ENDIAN : - AESAudioMetadata.LITTLE_ENDIAN); - - // Most properties were added by the Chunks. The Annotations, Saxel - // and MIDIData properties could have come from multiple chunks, - // and these were added to lists which we now make into Properties - // if there's anything to report. - if (!_annotationList.isEmpty ()) { - _propList.add (new Property ("Annotations", - PropertyType.PROPERTY, - PropertyArity.LIST, - _annotationList)); - } - if (!_midiList.isEmpty ()) { - _propList.add (new Property ("MIDIData", - PropertyType.PROPERTY, - PropertyArity.LIST, - _midiList)); - } - if (!_saxelList.isEmpty ()) { - _propList.add (new Property ("Saxels", - PropertyType.PROPERTY, - PropertyArity.LIST, - _saxelList)); - } - info.setProperty (_metadata); - - return 0; - } - - /** Sets the endian-ness. true indicates - * big-endian, and false means little-endian. - * This is needed because chunk data can change the - * usual little-endian byte order to big-endian. - */ - public void setEndian (boolean bigEndian) - { - thisFileBigEndian = bigEndian; +public class AiffModule extends ModuleBase { + /** + * **************************************************************** PRIVATE Instance FIELDS. + * **************************************************************** + */ + + /* Top-level metadata property */ + protected Property _metadata; + + /* Top-level property list */ + protected List _propList; + + /* AES audio metadata to go into AIFF metadata */ + protected AESAudioMetadata _aesMetadata; + + /* List of Annotation Chunk properties */ + protected List _annotationList; + + /* List of MIDI Chunk properties */ + protected List _midiList; + + /* List of Saxel properties */ + protected List _saxelList; + + /* Bytes remaining to be read. */ + protected long bytesRemaining; + + /* Flag to check for multiple sound chunks */ + protected boolean soundChunkSeen; + + /* Flag to check for exactly one format version chunk */ + protected boolean formatVersionChunkSeen; + + /* Flag to check for exactly one instrument chunk */ + protected boolean instrumentChunkSeen; + + /* Flag to check for exactly one common chunk */ + protected boolean commonChunkSeen; + + /* Flag to check for exactly one comments chunk */ + protected boolean commentsChunkSeen; + + /* Flag to check for exactly one name chunk */ + protected boolean nameChunkSeen; + + /* Flag to check for exactly one author chunk */ + protected boolean authorChunkSeen; + + /* Flag to check for exactly one copyright chunk */ + protected boolean copyrightChunkSeen; + + /* Flag to check for exactly one marker chunk */ + protected boolean markerChunkSeen; + + /* Flag to check for exactly one audio recording chunk */ + protected boolean audioRecChunkSeen; + + /* Flag to note that first sample offset has been recorded */ + protected boolean firstSampleOffsetMarked; + + /* File type */ + protected int fileType; + + /* Endianness for current file (not necessarily same as _bigEndian) */ + protected boolean thisFileBigEndian; + + /** + * **************************************************************** PRIVATE CLASS FIELDS. + * **************************************************************** + */ + + /* Chunk data orientation is big-endian. */ + private static final boolean BIGENDIAN = true; + + /* Values for fileType */ + public static final int AIFFTYPE = 1, AIFCTYPE = 2; + + /* Fixed value for first 4 bytes */ + private static final int[] sigByte = {0X46, 0X4F, 0X52, 0X4D}; + + private static final String NAME = "AIFF-hul"; + private static final String RELEASE = "1.6.1"; + private static final int[] DATE = {2019, 12, 10}; + private static final String[] FORMAT = {"AIFF", "Audio Interchange File Format"}; + private static final String COVERAGE = "AIFF 1.3, AIFF-C"; + private static final String[] MIMETYPE = {"audio/x-aiff", "application/aiff"}; + private static final String WELLFORMED = + "Magic number: \"FORM\" at byte offset 0; \"AIFF\"" + + "(for AIFF) or \"AIFC\" (for AIFF-C) at offset 8; one Form chunk " + + "containing one Common chunk and at most one Sound Data chunk " + + "(if numSampleFrames > 0); at most one instance of each of the " + + "following optional chunks: Marker, Instrument, Audio Recording, " + + "Comments, Name, Author, Copyright; all chunks required by a given " + + "profile exist in the file; all chunk structures are well-formed: " + + "a four ASCII character ID, followed by a 32 signed integer size, " + + "followed by a size byte data block (if size is odd, then the data " + + "block includes a final padding byte of value 0x00); and no data " + + "exist before the first byte of the chunk or after the " + + "last byte of the last chunk"; + private static final String VALIDITY = "The file is well-formed"; + private static final String REPINFO = + "Properties capturing the technical attributes of the audio " + "from all chunks"; + private static final String NOTE = null; + private static final String RIGHTS = + "Copyright 2004-2007 by JSTOR and " + + "the President and Fellows of Harvard College. " + + "Released under the GNU Lesser General Public License."; + + /** + * **************************************************************** CLASS CONSTRUCTOR. + * **************************************************************** + */ + /** Instantiates an AiffModule object. */ + public AiffModule() { + super( + NAME, + RELEASE, + DATE, + FORMAT, + COVERAGE, + MIMETYPE, + WELLFORMED, + VALIDITY, + REPINFO, + NOTE, + RIGHTS, + false); + _vendor = Agent.harvardInstance(); + + Document doc = + new Document( + "Audio Interchange File Format: " + + "\"AIFF\", A Standard for Sampled Sound " + + "Files, Version 1.3", + DocumentType.REPORT); + Builder builder = + new Agent.Builder("Apple Computer, Inc.", AgentType.COMMERCIAL) + .address("1 Infinite Loop, Cupertino, CA 95014") + .telephone("(408) 996-1010") + .web("http://www.apple.com/"); + Agent appleAgent = builder.build(); + doc.setAuthor(appleAgent); + doc.setDate("1989-01-04"); + doc.setIdentifier( + new Identifier( + "http://developer.apple.com/documentation/QuickTime/INMAC/SOUND/imsoundmgr.30.htm#pgfId=3190", + IdentifierType.URL)); + _specification.add(doc); + + doc = + new Document( + "Audio Interchange File Format AIFF-C: " + + "A revision to include compressed audio data", + DocumentType.REPORT); + doc.setAuthor(appleAgent); + doc.setDate("1991-08-26"); + doc.setNote("*** DRAFT ***"); // Asterisks as in the printed document + _specification.add(doc); + + Signature sig = + new ExternalSignature("AIFF", SignatureType.FILETYPE, SignatureUseType.OPTIONAL); + _signature.add(sig); + sig = new ExternalSignature("AIFC", SignatureType.FILETYPE, SignatureUseType.OPTIONAL); + _signature.add(sig); + sig = new ExternalSignature(".aif", SignatureType.EXTENSION, SignatureUseType.OPTIONAL); + _signature.add(sig); + sig = + new ExternalSignature( + ".aifc", SignatureType.EXTENSION, SignatureUseType.OPTIONAL, "For AIFF-C profile"); + _signature.add(sig); + + sig = new InternalSignature("FORM", SignatureType.MAGIC, SignatureUseType.MANDATORY, 0); + _signature.add(sig); + + sig = + new InternalSignature( + "AIFF", SignatureType.MAGIC, SignatureUseType.OPTIONAL, 8, "For AIFF profile"); + _signature.add(sig); + + sig = + new InternalSignature( + "AIFC", SignatureType.MAGIC, SignatureUseType.OPTIONAL, 0, "For AIFF-C profile"); + _signature.add(sig); + + _bigEndian = true; + } + + /** + * Parses the content of a purported AIFF digital object and stores the results in RepInfo. + * + * @param stream An InputStream, positioned at its beginning, which is generated from the object + * to be parsed + * @param info A fresh RepInfo object which will be modified to reflect the results of the parsing + * @param parseIndex Must be 0 in first call to parse. If parse returns + * a nonzero value, it must be called again with parseIndex equal to that return + * value. + */ + @Override + public int parse(InputStream stream, RepInfo info, int parseIndex) throws IOException { + initParse(); + info.setFormat(_format[0]); + info.setMimeType(_mimeType[0]); + info.setModule(this); + _aesMetadata.setPrimaryIdentifier(info.getUri()); + if (info.getURLFlag()) { + _aesMetadata.setOtherPrimaryIdentifierType("URI"); + } else { + _aesMetadata.setPrimaryIdentifierType(AESAudioMetadata.FILE_NAME); } - /** Adds a Property to the AIFF metadata. */ - public void addAiffProperty (Property prop) - { - _propList.add (prop); - } + setupDataStream(stream, info); - /** Adds an Annotation Property to the annotation list. - * This will get put into an Annotations Property. */ - public void addAnnotation (Property prop) - { - _annotationList.add (prop); + try { + // Check the start of the file for the right opening bytes + for (int i = 0; i < 4; i++) { + int ch = readUnsignedByte(_dstream, this); + if (ch != sigByte[i]) { + info.setMessage(new ErrorMessage(MessageConstants.AIFF_HUL_1, 0)); + info.setWellFormed(RepInfo.FALSE); + return 0; + } + } + /* If we got this far, take note that the signature is OK. */ + info.setSigMatch(_name); + + // Get the length of the Form chunk. This includes all + // the subsequent chunks in the file, but excludes the + // header ("FORM" and the length itself). + // bytesRemaining = readUnsignedInt (_dstream, _bigEndian, this); + bytesRemaining = readUnsignedInt(_dstream, BIGENDIAN, this); + + // Read the file type. + if (!readFileType(info)) { + return 0; + } + + while (bytesRemaining > 0) { + if (!readChunk(info)) { + break; + } + } + } catch (EOFException e) { + info.setWellFormed(RepInfo.FALSE); + info.setMessage(new ErrorMessage(MessageConstants.AIFF_HUL_8, _nByte)); + return 0; } - /** Adds a Saxel Property to the saxel list. - * This will get put into a Saxels Property. */ - public void addSaxel (Property prop) - { - _saxelList.add (prop); + if (!commonChunkSeen) { + info.setWellFormed(RepInfo.FALSE); + info.setMessage(new ErrorMessage(MessageConstants.AIFF_HUL_2)); } - - /** Adds a MIDI Property to the MIDI list. - * This will get put into a MIDIData Property. */ - public void addMidi (Property prop) - { - _midiList.add (prop); + if (fileType == AIFCTYPE && !formatVersionChunkSeen) { + info.setWellFormed(RepInfo.FALSE); + info.setMessage(new ErrorMessage(MessageConstants.AIFF_HUL_3)); } - - /** - * Initializes the state of the module for parsing. - */ - @Override - protected void initParse() - { - super.initParse (); - - thisFileBigEndian = _bigEndian; - _propList = new LinkedList (); - _metadata = new Property ("AIFFMetadata", - PropertyType.PROPERTY, - PropertyArity.LIST, - _propList); - - firstSampleOffsetMarked = false; - _aesMetadata = new AESAudioMetadata (); - _aesMetadata.setAnalogDigitalFlag("FILE_DIGITAL"); - _aesMetadata.setFormat ("AIFF"); // Further data may modify this value - _aesMetadata.setSpecificationVersion("1.3 (1989-01-04)"); // Ditto - _aesMetadata.setAudioDataEncoding ("PCM"); -// _aesMetadata.setBitrateReduction ("PCM", "", "", "", -// "LOSSY", "UNKNOWN", "FIXED"); - // Per Bugzilla 772, default is to omit bitrateReduction info - _aesMetadata.clearBitrateReduction(); - _aesMetadata.setUse ("OTHER", "JHOVE_validation"); - _aesMetadata.setDirection ("NONE"); - - _propList.add (new Property ("AESAudioMetadata", - PropertyType.AESAUDIOMETADATA, - _aesMetadata)); - - // Create a List for accumulating properties from Annotation Chunks - _annotationList = new LinkedList (); - - // Create a List for accumulating properties from MIDI Chunks - _midiList = new LinkedList (); - - // Create a List for accumulating properties from SAXL chunks - _saxelList = new LinkedList (); - - // Most chunk types are allowed to occur only once, - // and a few must occur exactly once. - // Clear flags for whether they have been seen. - soundChunkSeen = false; - commonChunkSeen = false; - markerChunkSeen = false; - formatVersionChunkSeen = false; - instrumentChunkSeen = false; - commentsChunkSeen = false; - nameChunkSeen = false; - authorChunkSeen = false; - audioRecChunkSeen = false; - copyrightChunkSeen = false; + if (info.getWellFormed() != RepInfo.TRUE) { + return 0; } - /** One-argument version of readUnsignedInt. */ - public long readUnsignedInt (DataInputStream stream) - throws IOException - { - return readUnsignedInt (stream, BIGENDIAN, this); + /* This file looks OK. */ + if (_ckSummer != null) { + skipDstreamToEnd(info); + // Set the checksums in the report if they're calculated + setChecksums(this._ckSummer, info); } - /** One-argument version of readUnsignedShort. - */ - public int readUnsignedShort (DataInputStream stream) - throws IOException - { - return readUnsignedShort (stream, BIGENDIAN, this); + if (fileType == AIFFTYPE) { + info.setProfile("AIFF"); + } else if (fileType == AIFCTYPE) { + info.setProfile("AIFF-C"); } - /** One-argument version of readSignedShort. - */ - public int readSignedShort (DataInputStream stream) - throws IOException - { - return readSignedShort (stream, true, this); - } + _aesMetadata.setByteOrder( + thisFileBigEndian ? AESAudioMetadata.BIG_ENDIAN : AESAudioMetadata.LITTLE_ENDIAN); - /** This reads an 80-bit SANE number, aka IEEE 754 - * extended double. - */ - public double read80BitDouble (DataInputStream stream) - throws IOException - { - byte[] buf = new byte[10]; - readByteBuf(_dstream, buf, this); - ExtDouble xd = new ExtDouble (buf); - return xd.toDouble(); + // Most properties were added by the Chunks. The Annotations, Saxel + // and MIDIData properties could have come from multiple chunks, + // and these were added to lists which we now make into Properties + // if there's anything to report. + if (!_annotationList.isEmpty()) { + _propList.add( + new Property("Annotations", PropertyType.PROPERTY, PropertyArity.LIST, _annotationList)); } - - /** - * Reads 4 bytes and concatenates them into a String. - * This pattern is used for ID's of various kinds. - */ - public String read4Chars(DataInputStream stream) throws IOException - { - StringBuffer sbuf = new StringBuffer(4); - for (int i = 0; i < 4; i++) { - int ch = readUnsignedByte(stream, this); - sbuf.append((char) ch); - } - return sbuf.toString(); + if (!_midiList.isEmpty()) { + _propList.add(new Property("MIDIData", PropertyType.PROPERTY, PropertyArity.LIST, _midiList)); } - - /** Reads a Pascal string. - * A Pascal string is one whose count is given in the first - * byte. The count is exclusive of the count byte itself. - * A Pascal string can have a maximum of 255 characters. - * If the count of a Pascal string is even (meaning the total - * number of bytes is odd), there will be a pad byte to skip, - * so that the next item can start on an even boundary. - * - * We assume the string will be in ASCII or Macintosh encoding. - */ - public String readPascalString (DataInputStream stream) throws IOException - { - int byteCnt = readUnsignedByte (stream, this); - byte[] byteBuf = new byte[byteCnt]; - readByteBuf (_dstream, byteBuf, this); - if ((byteCnt & 1) == 0) { - skipBytes (_dstream, 1, this); - } - return new String (byteBuf, "MacRoman"); + if (!_saxelList.isEmpty()) { + _propList.add(new Property("Saxels", PropertyType.PROPERTY, PropertyArity.LIST, _saxelList)); } - - /** Converts a Macintosh-style timestamp (seconds since - * January 1, 1904) into a Java date. The timestamp is - * treated as a time in the default localization. - * Depending on that localization, - * there may be some variation in the exact hour of the date - * returned, e.g., due to daylight savings time. - * - */ - public Date timestampToDate (long timestamp) - { - Calendar cal = Calendar.getInstance (); - cal.set (1904, 0, 1, 0, 0, 0); - - // If we add the seconds directly, we'll truncate the long - // value when converting to int. So convert to hours plus - // residual seconds. - int hours = (int) (timestamp / 3600); - int seconds = (int) (timestamp - (long) hours * 3600L); - cal.add (Calendar.HOUR_OF_DAY, hours); - cal.add (Calendar.SECOND, seconds); - return cal.getTime (); + info.setProperty(_metadata); + + return 0; + } + + /** + * Sets the endian-ness. true indicates big-endian, and false means + * little-endian. This is needed because chunk data can change the usual little-endian byte order + * to big-endian. + */ + public void setEndian(boolean bigEndian) { + thisFileBigEndian = bigEndian; + } + + /** Adds a Property to the AIFF metadata. */ + public void addAiffProperty(Property prop) { + _propList.add(prop); + } + + /** + * Adds an Annotation Property to the annotation list. This will get put into an Annotations + * Property. + */ + public void addAnnotation(Property prop) { + _annotationList.add(prop); + } + + /** Adds a Saxel Property to the saxel list. This will get put into a Saxels Property. */ + public void addSaxel(Property prop) { + _saxelList.add(prop); + } + + /** Adds a MIDI Property to the MIDI list. This will get put into a MIDIData Property. */ + public void addMidi(Property prop) { + _midiList.add(prop); + } + + /** Initializes the state of the module for parsing. */ + @Override + protected void initParse() { + super.initParse(); + + thisFileBigEndian = _bigEndian; + _propList = new LinkedList(); + _metadata = new Property("AIFFMetadata", PropertyType.PROPERTY, PropertyArity.LIST, _propList); + + firstSampleOffsetMarked = false; + _aesMetadata = new AESAudioMetadata(); + _aesMetadata.setAnalogDigitalFlag("FILE_DIGITAL"); + _aesMetadata.setFormat("AIFF"); // Further data may modify this value + _aesMetadata.setSpecificationVersion("1.3 (1989-01-04)"); // Ditto + _aesMetadata.setAudioDataEncoding("PCM"); + // _aesMetadata.setBitrateReduction ("PCM", "", "", "", + // "LOSSY", "UNKNOWN", "FIXED"); + // Per Bugzilla 772, default is to omit bitrateReduction info + _aesMetadata.clearBitrateReduction(); + _aesMetadata.setUse("OTHER", "JHOVE_validation"); + _aesMetadata.setDirection("NONE"); + + _propList.add(new Property("AESAudioMetadata", PropertyType.AESAUDIOMETADATA, _aesMetadata)); + + // Create a List for accumulating properties from Annotation Chunks + _annotationList = new LinkedList(); + + // Create a List for accumulating properties from MIDI Chunks + _midiList = new LinkedList(); + + // Create a List for accumulating properties from SAXL chunks + _saxelList = new LinkedList(); + + // Most chunk types are allowed to occur only once, + // and a few must occur exactly once. + // Clear flags for whether they have been seen. + soundChunkSeen = false; + commonChunkSeen = false; + markerChunkSeen = false; + formatVersionChunkSeen = false; + instrumentChunkSeen = false; + commentsChunkSeen = false; + nameChunkSeen = false; + authorChunkSeen = false; + audioRecChunkSeen = false; + copyrightChunkSeen = false; + } + + /** One-argument version of readUnsignedInt. */ + public long readUnsignedInt(DataInputStream stream) throws IOException { + return readUnsignedInt(stream, BIGENDIAN, this); + } + + /** One-argument version of readUnsignedShort. */ + public int readUnsignedShort(DataInputStream stream) throws IOException { + return readUnsignedShort(stream, BIGENDIAN, this); + } + + /** One-argument version of readSignedShort. */ + public int readSignedShort(DataInputStream stream) throws IOException { + return readSignedShort(stream, true, this); + } + + /** This reads an 80-bit SANE number, aka IEEE 754 extended double. */ + public double read80BitDouble(DataInputStream stream) throws IOException { + byte[] buf = new byte[10]; + readByteBuf(_dstream, buf, this); + ExtDouble xd = new ExtDouble(buf); + return xd.toDouble(); + } + + /** + * Reads 4 bytes and concatenates them into a String. This pattern is used for ID's of various + * kinds. + */ + public String read4Chars(DataInputStream stream) throws IOException { + StringBuffer sbuf = new StringBuffer(4); + for (int i = 0; i < 4; i++) { + int ch = readUnsignedByte(stream, this); + sbuf.append((char) ch); } - - /** Returns the filetype, which is AIFFTYPE or AIFCTYPE. */ - public int getFileType () - { - return fileType; + return sbuf.toString(); + } + + /** + * Reads a Pascal string. A Pascal string is one whose count is given in the first byte. The count + * is exclusive of the count byte itself. A Pascal string can have a maximum of 255 characters. If + * the count of a Pascal string is even (meaning the total number of bytes is odd), there will be + * a pad byte to skip, so that the next item can start on an even boundary. + * + *

We assume the string will be in ASCII or Macintosh encoding. + */ + public String readPascalString(DataInputStream stream) throws IOException { + int byteCnt = readUnsignedByte(stream, this); + byte[] byteBuf = new byte[byteCnt]; + readByteBuf(_dstream, byteBuf, this); + if ((byteCnt & 1) == 0) { + skipBytes(_dstream, 1, this); } - - /** Marks the first sample offset as the current byte position, - * if it hasn't already been marked. - * The SSND chunk offset value must be added to the current - * byte offset for a correct value. - */ - public void markFirstSampleOffset (long offset) - { - if (!firstSampleOffsetMarked) { - firstSampleOffsetMarked = true; - _aesMetadata.setFirstSampleOffset (_nByte + offset); - } + return new String(byteBuf, "MacRoman"); + } + + /** + * Converts a Macintosh-style timestamp (seconds since January 1, 1904) into a Java date. The + * timestamp is treated as a time in the default localization. Depending on that localization, + * there may be some variation in the exact hour of the date returned, e.g., due to daylight + * savings time. + */ + public Date timestampToDate(long timestamp) { + Calendar cal = Calendar.getInstance(); + cal.set(1904, 0, 1, 0, 0, 0); + + // If we add the seconds directly, we'll truncate the long + // value when converting to int. So convert to hours plus + // residual seconds. + int hours = (int) (timestamp / 3600); + int seconds = (int) (timestamp - (long) hours * 3600L); + cal.add(Calendar.HOUR_OF_DAY, hours); + cal.add(Calendar.SECOND, seconds); + return cal.getTime(); + } + + /** Returns the filetype, which is AIFFTYPE or AIFCTYPE. */ + public int getFileType() { + return fileType; + } + + /** + * Marks the first sample offset as the current byte position, if it hasn't already been marked. + * The SSND chunk offset value must be added to the current byte offset for a correct value. + */ + public void markFirstSampleOffset(long offset) { + if (!firstSampleOffsetMarked) { + firstSampleOffsetMarked = true; + _aesMetadata.setFirstSampleOffset(_nByte + offset); } - - /** Reads the file type. - * Broken out from parse(). - * If it is not a valid file type, returns false. - */ - protected boolean readFileType (RepInfo info) throws IOException - { - String typ = read4Chars (_dstream); - bytesRemaining -= 4; - if ("AIFF".equals (typ)) { - fileType = AIFFTYPE; - return true; - } - else if ("AIFC".equals (typ)) { - fileType = AIFCTYPE; - _aesMetadata.setFormat ("AIFF-C"); - _aesMetadata.setSpecificationVersion ("Draft 1991-08-26"); - return true; - } - else { - info.setMessage (new ErrorMessage - (MessageConstants.AIFF_HUL_4, _nByte)); - info.setWellFormed (RepInfo.FALSE); - return false; - } + } + + /** + * Reads the file type. Broken out from parse(). If it is not a valid file type, returns false. + */ + protected boolean readFileType(RepInfo info) throws IOException { + String typ = read4Chars(_dstream); + bytesRemaining -= 4; + if ("AIFF".equals(typ)) { + fileType = AIFFTYPE; + return true; + } else if ("AIFC".equals(typ)) { + fileType = AIFCTYPE; + _aesMetadata.setFormat("AIFF-C"); + _aesMetadata.setSpecificationVersion("Draft 1991-08-26"); + return true; + } else { + info.setMessage(new ErrorMessage(MessageConstants.AIFF_HUL_4, _nByte)); + info.setWellFormed(RepInfo.FALSE); + return false; } - - /** Reads an AIFF Chunk. - * - */ - protected boolean readChunk (RepInfo info) throws IOException - { - Chunk chunk = null; - ChunkHeader chunkh = new ChunkHeader (this, info); - if (!chunkh.readHeader(_dstream)) { - return false; - } - int chunkSize = (int) chunkh.getSize (); - bytesRemaining -= chunkSize + 8; - - String id = chunkh.getID (); - if ("FVER".equals (id)) { - if (formatVersionChunkSeen) { - dupChunkError (info, "Format Version"); - } - chunk = new FormatVersionChunk (this, chunkh, _dstream); - formatVersionChunkSeen = true; - } - else if ("APPL".equals (id)) { - chunk = new ApplicationChunk (this, chunkh, _dstream); - // Any number of application chunks is ok - } - else if ("COMM".equals (id)) { - if (commonChunkSeen) { - dupChunkError (info, "Common"); - } - chunk = new CommonChunk (this, chunkh, _dstream); - commonChunkSeen = true; - } - else if ("SSND".equals (id)) { - // Watch for multiple sound chunks - if (soundChunkSeen) { - dupChunkError (info, "Sound"); - } - else { - chunk = new SoundDataChunk (this, chunkh, _dstream); - soundChunkSeen = true; - } - } - else if ("COMT".equals (id)) { - if (commentsChunkSeen) { - dupChunkError (info, "Comments"); - } - chunk = new CommentsChunk (this, chunkh, _dstream); - commentsChunkSeen = true; - } - else if ("INST".equals (id)) { - if (instrumentChunkSeen) { - dupChunkError (info, "Instrument"); - } - chunk = new InstrumentChunk (this, chunkh, _dstream); - instrumentChunkSeen = true; - } - else if ("MARK".equals (id)) { - if (markerChunkSeen) { - dupChunkError (info, "Marker"); - } - else { - chunk = new MarkerChunk (this, chunkh, _dstream); - markerChunkSeen = true; - } - } - else if ("MIDI".equals (id)) { - chunk = new MidiChunk (this, chunkh, _dstream); - // Any number of MIDI chunks are allowed - } - else if ("NAME".equals (id)) { - if (nameChunkSeen) { - dupChunkError (info, "Name"); - } - else { - chunk = new NameChunk (this, chunkh, _dstream); - nameChunkSeen = true; - } - } - else if ("AUTH".equals (id)) { - if (authorChunkSeen) { - dupChunkError (info, "Author"); - } - else { - chunk = new AuthorChunk (this, chunkh, _dstream); - authorChunkSeen = true; - } - } - else if ("(c) ".equals (id)) { - if (copyrightChunkSeen) { - dupChunkError (info, "Copyright"); - } - else { - chunk = new CopyrightChunk (this, chunkh, _dstream); - copyrightChunkSeen = true; - } - } - else if ("AESD".equals (id)) { - if (audioRecChunkSeen) { - dupChunkError (info, "Audio Recording"); - } - else { - chunk = new AudioRecChunk (this, chunkh, _dstream); - audioRecChunkSeen = true; - } - } - else if ("SAXL".equals (id)) { - chunk = new SaxelChunk (this, chunkh, _dstream); - // Multiple saxel chunks are ok - } - else if ("ANNO".equals (id)) { - chunk = new AnnotationChunk (this, chunkh, _dstream); - // Multiple annotations are OK - } - else { - info.setMessage (new InfoMessage - (MessageConstants.AIFF_HUL_9, id, _nByte)); - } - if (chunk != null) { - if (!chunk.readChunk (info)) { - return false; - } - } - else { - // Other chunk types are legal, just skip over them - skipBytes (_dstream, chunkSize, this); - } - if ((chunkSize & 1) != 0) { - // Must come out to an even byte boundary - skipBytes (_dstream, 1, this); - --bytesRemaining; - } - return true; - } - - /** Returns the module's AES metadata. */ - public AESAudioMetadata getAESMetadata () - { - return _aesMetadata; + } + + /** Reads an AIFF Chunk. */ + protected boolean readChunk(RepInfo info) throws IOException { + Chunk chunk = null; + ChunkHeader chunkh = new ChunkHeader(this, info); + if (!chunkh.readHeader(_dstream)) { + return false; } - - /* Factor out the reporting of duplicate chunks. */ - protected void dupChunkError (RepInfo info, String chunkName) - { - JhoveMessage mess = MessageConstants.AIFF_HUL_5; - String message = String.format(mess.getMessage(), chunkName); - info.setMessage (new ErrorMessage - (JhoveMessages.getMessageInstance(mess.getId(), message), - _nByte)); - info.setValid (false); + int chunkSize = (int) chunkh.getSize(); + bytesRemaining -= chunkSize + 8; + + String id = chunkh.getID(); + if ("FVER".equals(id)) { + if (formatVersionChunkSeen) { + dupChunkError(info, "Format Version"); + } + chunk = new FormatVersionChunk(this, chunkh, _dstream); + formatVersionChunkSeen = true; + } else if ("APPL".equals(id)) { + chunk = new ApplicationChunk(this, chunkh, _dstream); + // Any number of application chunks is ok + } else if ("COMM".equals(id)) { + if (commonChunkSeen) { + dupChunkError(info, "Common"); + } + chunk = new CommonChunk(this, chunkh, _dstream); + commonChunkSeen = true; + } else if ("SSND".equals(id)) { + // Watch for multiple sound chunks + if (soundChunkSeen) { + dupChunkError(info, "Sound"); + } else { + chunk = new SoundDataChunk(this, chunkh, _dstream); + soundChunkSeen = true; + } + } else if ("COMT".equals(id)) { + if (commentsChunkSeen) { + dupChunkError(info, "Comments"); + } + chunk = new CommentsChunk(this, chunkh, _dstream); + commentsChunkSeen = true; + } else if ("INST".equals(id)) { + if (instrumentChunkSeen) { + dupChunkError(info, "Instrument"); + } + chunk = new InstrumentChunk(this, chunkh, _dstream); + instrumentChunkSeen = true; + } else if ("MARK".equals(id)) { + if (markerChunkSeen) { + dupChunkError(info, "Marker"); + } else { + chunk = new MarkerChunk(this, chunkh, _dstream); + markerChunkSeen = true; + } + } else if ("MIDI".equals(id)) { + chunk = new MidiChunk(this, chunkh, _dstream); + // Any number of MIDI chunks are allowed + } else if ("NAME".equals(id)) { + if (nameChunkSeen) { + dupChunkError(info, "Name"); + } else { + chunk = new NameChunk(this, chunkh, _dstream); + nameChunkSeen = true; + } + } else if ("AUTH".equals(id)) { + if (authorChunkSeen) { + dupChunkError(info, "Author"); + } else { + chunk = new AuthorChunk(this, chunkh, _dstream); + authorChunkSeen = true; + } + } else if ("(c) ".equals(id)) { + if (copyrightChunkSeen) { + dupChunkError(info, "Copyright"); + } else { + chunk = new CopyrightChunk(this, chunkh, _dstream); + copyrightChunkSeen = true; + } + } else if ("AESD".equals(id)) { + if (audioRecChunkSeen) { + dupChunkError(info, "Audio Recording"); + } else { + chunk = new AudioRecChunk(this, chunkh, _dstream); + audioRecChunkSeen = true; + } + } else if ("SAXL".equals(id)) { + chunk = new SaxelChunk(this, chunkh, _dstream); + // Multiple saxel chunks are ok + } else if ("ANNO".equals(id)) { + chunk = new AnnotationChunk(this, chunkh, _dstream); + // Multiple annotations are OK + } else { + info.setMessage(new InfoMessage(MessageConstants.AIFF_HUL_9, id, _nByte)); + } + if (chunk != null) { + if (!chunk.readChunk(info)) { + return false; + } + } else { + // Other chunk types are legal, just skip over them + skipBytes(_dstream, chunkSize, this); + } + if ((chunkSize & 1) != 0) { + // Must come out to an even byte boundary + skipBytes(_dstream, 1, this); + --bytesRemaining; } + return true; + } + + /** Returns the module's AES metadata. */ + public AESAudioMetadata getAESMetadata() { + return _aesMetadata; + } + + /* Factor out the reporting of duplicate chunks. */ + protected void dupChunkError(RepInfo info, String chunkName) { + JhoveMessage mess = MessageConstants.AIFF_HUL_5; + String message = String.format(mess.getMessage(), chunkName); + info.setMessage( + new ErrorMessage(JhoveMessages.getMessageInstance(mess.getId(), message), _nByte)); + info.setValid(false); + } } diff --git a/jhove-modules/aiff-hul/src/main/java/edu/harvard/hul/ois/jhove/module/aiff/AiffStrings.java b/jhove-modules/aiff-hul/src/main/java/edu/harvard/hul/ois/jhove/module/aiff/AiffStrings.java index d801c9b8f..01c2c4565 100644 --- a/jhove-modules/aiff-hul/src/main/java/edu/harvard/hul/ois/jhove/module/aiff/AiffStrings.java +++ b/jhove-modules/aiff-hul/src/main/java/edu/harvard/hul/ois/jhove/module/aiff/AiffStrings.java @@ -1,29 +1,22 @@ -/********************************************************************** - * Jhove - JSTOR/Harvard Object Validation Environment - * Copyright 2004 by JSTOR and the President and Fellows of Harvard College - **********************************************************************/ - +/** + * ******************************************************************** Jhove - JSTOR/Harvard Object + * Validation Environment Copyright 2004 by JSTOR and the President and Fellows of Harvard College + * ******************************************************************** + */ package edu.harvard.hul.ois.jhove.module.aiff; /** - * A class for holding arrays of informative strings that will go into - * properties of an AIFF object. + * A class for holding arrays of informative strings that will go into properties of an AIFF object. * * @author Gary McGath - * */ public class AiffStrings { - - /** Strings for looping types in the Instrument Chunk */ - public final static String[] LOOP_TYPE = - { "No looping", - "Forward looping", - "Forward/backward looping" }; - /** A private constructor just to make sure nobody - instantiates the class by mistake. */ - private AiffStrings () - { - } + /** Strings for looping types in the Instrument Chunk */ + public static final String[] LOOP_TYPE = { + "No looping", "Forward looping", "Forward/backward looping" + }; + /** A private constructor just to make sure nobody instantiates the class by mistake. */ + private AiffStrings() {} } diff --git a/jhove-modules/aiff-hul/src/main/java/edu/harvard/hul/ois/jhove/module/aiff/AnnotationChunk.java b/jhove-modules/aiff-hul/src/main/java/edu/harvard/hul/ois/jhove/module/aiff/AnnotationChunk.java index 7df6d4f7c..140c201ec 100644 --- a/jhove-modules/aiff-hul/src/main/java/edu/harvard/hul/ois/jhove/module/aiff/AnnotationChunk.java +++ b/jhove-modules/aiff-hul/src/main/java/edu/harvard/hul/ois/jhove/module/aiff/AnnotationChunk.java @@ -1,54 +1,44 @@ -/********************************************************************** - * Jhove - JSTOR/Harvard Object Validation Environment - * Copyright 2004 by JSTOR and the President and Fellows of Harvard College - **********************************************************************/ - +/** + * ******************************************************************** Jhove - JSTOR/Harvard Object + * Validation Environment Copyright 2004 by JSTOR and the President and Fellows of Harvard College + * ******************************************************************** + */ package edu.harvard.hul.ois.jhove.module.aiff; -import java.io.*; import edu.harvard.hul.ois.jhove.*; import edu.harvard.hul.ois.jhove.module.AiffModule; import edu.harvard.hul.ois.jhove.module.iff.*; +import java.io.*; /** * Implementation of the AIFF Annotation Chunk. * * @author Gary McGath - * */ public class AnnotationChunk extends TextChunk { - /** - * Constructor. - * - * @param module The AIFFModule under which this was called - * @param hdr The header for this chunk - * @param dstrm The stream from which the AIFF data are being read - */ - public AnnotationChunk( - AiffModule module, - ChunkHeader hdr, - DataInputStream dstrm) { - super(module, hdr, dstrm); - } - - - /** Reads a chunk and adds an Annotation property to the - * module's list of annotations. - * - * There can be multiple Annotation Chunks, so we don't - * create a property here directly. - * - * @return false if the chunk is structurally - * invalid, otherwise true - */ - @Override - public boolean readChunk(RepInfo info) throws IOException { - String name = readText (); - ((AiffModule) _module).addAnnotation (new Property (propName, - PropertyType.STRING, - name)); - return true; - } + /** + * Constructor. + * + * @param module The AIFFModule under which this was called + * @param hdr The header for this chunk + * @param dstrm The stream from which the AIFF data are being read + */ + public AnnotationChunk(AiffModule module, ChunkHeader hdr, DataInputStream dstrm) { + super(module, hdr, dstrm); + } + /** + * Reads a chunk and adds an Annotation property to the module's list of annotations. + * + *

There can be multiple Annotation Chunks, so we don't create a property here directly. + * + * @return false if the chunk is structurally invalid, otherwise true + */ + @Override + public boolean readChunk(RepInfo info) throws IOException { + String name = readText(); + ((AiffModule) _module).addAnnotation(new Property(propName, PropertyType.STRING, name)); + return true; + } } diff --git a/jhove-modules/aiff-hul/src/main/java/edu/harvard/hul/ois/jhove/module/aiff/ApplicationChunk.java b/jhove-modules/aiff-hul/src/main/java/edu/harvard/hul/ois/jhove/module/aiff/ApplicationChunk.java index 97a94cad1..69ee121ed 100644 --- a/jhove-modules/aiff-hul/src/main/java/edu/harvard/hul/ois/jhove/module/aiff/ApplicationChunk.java +++ b/jhove-modules/aiff-hul/src/main/java/edu/harvard/hul/ois/jhove/module/aiff/ApplicationChunk.java @@ -1,85 +1,66 @@ -/********************************************************************** - * Jhove - JSTOR/Harvard Object Validation Environment - * Copyright 2004 by JSTOR and the President and Fellows of Harvard College - **********************************************************************/ - +/** + * ******************************************************************** Jhove - JSTOR/Harvard Object + * Validation Environment Copyright 2004 by JSTOR and the President and Fellows of Harvard College + * ******************************************************************** + */ package edu.harvard.hul.ois.jhove.module.aiff; -import java.io.DataInputStream; -import java.io.IOException; - import edu.harvard.hul.ois.jhove.*; import edu.harvard.hul.ois.jhove.module.AiffModule; import edu.harvard.hul.ois.jhove.module.iff.Chunk; import edu.harvard.hul.ois.jhove.module.iff.ChunkHeader; +import java.io.DataInputStream; +import java.io.IOException; /** * Implementation of the AIFF Application Chunk. * * @author Gary McGath - * */ public class ApplicationChunk extends Chunk { - /** - * Constructor. - * - * @param module The AIFFModule under which this was called - * @param hdr The header for this chunk - * @param dstrm The stream from which the AIFF data are being read - */ - public ApplicationChunk( - AiffModule module, - ChunkHeader hdr, - DataInputStream dstrm) { - super(module, hdr, dstrm); - } + /** + * Constructor. + * + * @param module The AIFFModule under which this was called + * @param hdr The header for this chunk + * @param dstrm The stream from which the AIFF data are being read + */ + public ApplicationChunk(AiffModule module, ChunkHeader hdr, DataInputStream dstrm) { + super(module, hdr, dstrm); + } - /** Reads a chunk and puts an Application property into - * the RepInfo object. - * - * @return false if the chunk is structurally - * invalid, otherwise true - */ - @Override - public boolean readChunk(RepInfo info) throws IOException - { - AiffModule module = (AiffModule) _module; - String applicationSignature = module.read4Chars (_dstream); - byte[] data = new byte[(int) (bytesLeft - 4)]; - ModuleBase.readByteBuf (_dstream, data, _module); - Property[] propArr = new Property[2]; - propArr[0] = new Property ("ApplicationSignature", - PropertyType.STRING, - applicationSignature); - AESAudioMetadata aes = module.getAESMetadata (); - aes.setAppSpecificData(applicationSignature); - // If the application signature is 'pdos' or 'stoc', - // then the beginning of the data area is a Pascal - // string naming the application. Otherwise, we - // just report the raw data. ('pdos' is for Apple II - // applications, 'stoc' for the entire non-Apple world.) - if ("stoc".equals (applicationSignature) || - "pdos".equals (applicationSignature)) { - String appName = module.readPascalString(_dstream); - bytesLeft -= appName.length() + 1; - module.skipBytes (_dstream, (int) bytesLeft, module); - propArr[1] = new Property ("ApplicationName", - PropertyType.STRING, - appName); - } - else { - propArr[1] = new Property ("Data", - PropertyType.BYTE, - PropertyArity.ARRAY, - data); - } - module.addAiffProperty (new Property ("Application", - PropertyType.PROPERTY, - PropertyArity.ARRAY, - propArr)); - - return true; + /** + * Reads a chunk and puts an Application property into the RepInfo object. + * + * @return false if the chunk is structurally invalid, otherwise true + */ + @Override + public boolean readChunk(RepInfo info) throws IOException { + AiffModule module = (AiffModule) _module; + String applicationSignature = module.read4Chars(_dstream); + byte[] data = new byte[(int) (bytesLeft - 4)]; + ModuleBase.readByteBuf(_dstream, data, _module); + Property[] propArr = new Property[2]; + propArr[0] = new Property("ApplicationSignature", PropertyType.STRING, applicationSignature); + AESAudioMetadata aes = module.getAESMetadata(); + aes.setAppSpecificData(applicationSignature); + // If the application signature is 'pdos' or 'stoc', + // then the beginning of the data area is a Pascal + // string naming the application. Otherwise, we + // just report the raw data. ('pdos' is for Apple II + // applications, 'stoc' for the entire non-Apple world.) + if ("stoc".equals(applicationSignature) || "pdos".equals(applicationSignature)) { + String appName = module.readPascalString(_dstream); + bytesLeft -= appName.length() + 1; + module.skipBytes(_dstream, (int) bytesLeft, module); + propArr[1] = new Property("ApplicationName", PropertyType.STRING, appName); + } else { + propArr[1] = new Property("Data", PropertyType.BYTE, PropertyArity.ARRAY, data); } + module.addAiffProperty( + new Property("Application", PropertyType.PROPERTY, PropertyArity.ARRAY, propArr)); + return true; + } } diff --git a/jhove-modules/aiff-hul/src/main/java/edu/harvard/hul/ois/jhove/module/aiff/AudioRecChunk.java b/jhove-modules/aiff-hul/src/main/java/edu/harvard/hul/ois/jhove/module/aiff/AudioRecChunk.java index e517785c9..a1c642a94 100644 --- a/jhove-modules/aiff-hul/src/main/java/edu/harvard/hul/ois/jhove/module/aiff/AudioRecChunk.java +++ b/jhove-modules/aiff-hul/src/main/java/edu/harvard/hul/ois/jhove/module/aiff/AudioRecChunk.java @@ -1,69 +1,57 @@ -/********************************************************************** - * Jhove - JSTOR/Harvard Object Validation Environment - * Copyright 2004 by JSTOR and the President and Fellows of Harvard College - **********************************************************************/ - +/** + * ******************************************************************** Jhove - JSTOR/Harvard Object + * Validation Environment Copyright 2004 by JSTOR and the President and Fellows of Harvard College + * ******************************************************************** + */ package edu.harvard.hul.ois.jhove.module.aiff; -import java.io.DataInputStream; -import java.io.IOException; import edu.harvard.hul.ois.jhove.*; import edu.harvard.hul.ois.jhove.module.AiffModule; import edu.harvard.hul.ois.jhove.module.iff.Chunk; import edu.harvard.hul.ois.jhove.module.iff.ChunkHeader; +import java.io.DataInputStream; +import java.io.IOException; /** * Implementation of the AIFF Audio Recording Chunk. * - * The data bytes are put into an uninterpreted byte array - * Property. These are specified in the AES Recommended - * Practice for Digital Audio Engineering - Serial Transmission - * Format for Linearly Represented Digital Audio Data, - * Section 7.1, Channel Status Data. - * - * @author Gary McGath + *

The data bytes are put into an uninterpreted byte array Property. These are specified in the + * AES Recommended Practice for Digital Audio Engineering - Serial Transmission Format for Linearly + * Represented Digital Audio Data, Section 7.1, Channel Status Data. * + * @author Gary McGath */ public class AudioRecChunk extends Chunk { - /** - * Constructor. - * - * @param module The AIFFModule under which this was called - * @param hdr The header for this chunk - * @param dstrm The stream from which the AIFF data are being read - */ - public AudioRecChunk( - AiffModule module, - ChunkHeader hdr, - DataInputStream dstrm) { - super(module, hdr, dstrm); - } + /** + * Constructor. + * + * @param module The AIFFModule under which this was called + * @param hdr The header for this chunk + * @param dstrm The stream from which the AIFF data are being read + */ + public AudioRecChunk(AiffModule module, ChunkHeader hdr, DataInputStream dstrm) { + super(module, hdr, dstrm); + } - /** Reads a chunk and puts an AudioRecording property into - * the RepInfo object. - * - * @return false if the chunk is structurally - * invalid, otherwise true - */ - @Override - public boolean readChunk(RepInfo info) throws IOException { - AiffModule module = (AiffModule) _module; - if (bytesLeft != 24) { - // This chunk must always have exactly 24 bytes data - info.setMessage (new ErrorMessage - (MessageConstants.AIFF_HUL_6, - module.getNByte ())); - info.setWellFormed (false); - return false; - } - byte[] buf = new byte[24]; - ModuleBase.readByteBuf (_dstream, buf, module); - module.addAiffProperty (new Property ("AudioRecording", - PropertyType.BYTE, - PropertyArity.ARRAY, - buf)); - return true; + /** + * Reads a chunk and puts an AudioRecording property into the RepInfo object. + * + * @return false if the chunk is structurally invalid, otherwise true + */ + @Override + public boolean readChunk(RepInfo info) throws IOException { + AiffModule module = (AiffModule) _module; + if (bytesLeft != 24) { + // This chunk must always have exactly 24 bytes data + info.setMessage(new ErrorMessage(MessageConstants.AIFF_HUL_6, module.getNByte())); + info.setWellFormed(false); + return false; } - + byte[] buf = new byte[24]; + ModuleBase.readByteBuf(_dstream, buf, module); + module.addAiffProperty( + new Property("AudioRecording", PropertyType.BYTE, PropertyArity.ARRAY, buf)); + return true; + } } diff --git a/jhove-modules/aiff-hul/src/main/java/edu/harvard/hul/ois/jhove/module/aiff/AuthorChunk.java b/jhove-modules/aiff-hul/src/main/java/edu/harvard/hul/ois/jhove/module/aiff/AuthorChunk.java index 87c9707bf..5d84b8f52 100644 --- a/jhove-modules/aiff-hul/src/main/java/edu/harvard/hul/ois/jhove/module/aiff/AuthorChunk.java +++ b/jhove-modules/aiff-hul/src/main/java/edu/harvard/hul/ois/jhove/module/aiff/AuthorChunk.java @@ -1,37 +1,30 @@ -/********************************************************************** - * Jhove - JSTOR/Harvard Object Validation Environment - * Copyright 2004 by JSTOR and the President and Fellows of Harvard College - **********************************************************************/ - +/** + * ******************************************************************** Jhove - JSTOR/Harvard Object + * Validation Environment Copyright 2004 by JSTOR and the President and Fellows of Harvard College + * ******************************************************************** + */ package edu.harvard.hul.ois.jhove.module.aiff; -import java.io.DataInputStream; import edu.harvard.hul.ois.jhove.module.AiffModule; import edu.harvard.hul.ois.jhove.module.iff.*; +import java.io.DataInputStream; /** * Implementation of the AIFF Author Chunk. * * @author Gary McGath - * */ public class AuthorChunk extends TextChunk { - /** - * Constructor. - * - * @param module The AIFFModule under which this was called - * @param hdr The header for this chunk - * @param dstrm The stream from which the AIFF data are being read - */ - public AuthorChunk( - AiffModule module, - ChunkHeader hdr, - DataInputStream dstrm) - { - super(module, hdr, dstrm); - propName = "Author"; - } - - + /** + * Constructor. + * + * @param module The AIFFModule under which this was called + * @param hdr The header for this chunk + * @param dstrm The stream from which the AIFF data are being read + */ + public AuthorChunk(AiffModule module, ChunkHeader hdr, DataInputStream dstrm) { + super(module, hdr, dstrm); + propName = "Author"; + } } diff --git a/jhove-modules/aiff-hul/src/main/java/edu/harvard/hul/ois/jhove/module/aiff/CommentsChunk.java b/jhove-modules/aiff-hul/src/main/java/edu/harvard/hul/ois/jhove/module/aiff/CommentsChunk.java index 1c3098fa9..e2cb37064 100644 --- a/jhove-modules/aiff-hul/src/main/java/edu/harvard/hul/ois/jhove/module/aiff/CommentsChunk.java +++ b/jhove-modules/aiff-hul/src/main/java/edu/harvard/hul/ois/jhove/module/aiff/CommentsChunk.java @@ -1,92 +1,77 @@ -/********************************************************************** - * Jhove - JSTOR/Harvard Object Validation Environment - * Copyright 2004 by JSTOR and the President and Fellows of Harvard College - **********************************************************************/ - +/** + * ******************************************************************** Jhove - JSTOR/Harvard Object + * Validation Environment Copyright 2004 by JSTOR and the President and Fellows of Harvard College + * ******************************************************************** + */ package edu.harvard.hul.ois.jhove.module.aiff; -import java.io.DataInputStream; -import java.io.IOException; -import java.util.*; import edu.harvard.hul.ois.jhove.*; import edu.harvard.hul.ois.jhove.module.AiffModule; import edu.harvard.hul.ois.jhove.module.iff.*; +import java.io.DataInputStream; +import java.io.IOException; +import java.util.*; /** * Implementation of the AIFF Comments Chunk. * * @author Gary McGath - * */ public class CommentsChunk extends Chunk { - /** - * Constructor. - * - * @param module The AIFFModule under which this was called - * @param hdr The header for this chunk - * @param dstrm The stream from which the AIFF data are being read - */ - public CommentsChunk (AiffModule module, ChunkHeader hdr, - DataInputStream dstrm) - { - super(module, hdr, dstrm); - } + /** + * Constructor. + * + * @param module The AIFFModule under which this was called + * @param hdr The header for this chunk + * @param dstrm The stream from which the AIFF data are being read + */ + public CommentsChunk(AiffModule module, ChunkHeader hdr, DataInputStream dstrm) { + super(module, hdr, dstrm); + } - /** Reads a chunk and puts a Comments property into - * the RepInfo object. - * - * @return false if the chunk is structurally - * invalid, otherwise true - */ - @Override - public boolean readChunk(RepInfo info) - throws IOException - { - AiffModule module = (AiffModule) _module; - int numComments = module.readUnsignedShort (_dstream); - bytesLeft -= 2; - if (numComments == 0) { - return true; // trivial case - } - // Create a List of comment properties - List comments = new ArrayList<> (numComments); - for (int i = 0; i < numComments; i++) { - long timestamp = module.readUnsignedInt (_dstream); - Date jTimestamp = module.timestampToDate (timestamp); - // Skip marker short - module.readSignedShort (_dstream); - int count = module.readUnsignedShort (_dstream); - bytesLeft -= 8; - byte[] buf = new byte[count]; - ModuleBase.readByteBuf(_dstream, buf, module); - bytesLeft -= count; - /* Ensure that each byt is a printable ASCII character. */ - for (int j=0; j 127) { - buf[j] = 32; - } - } - String comment = new String (buf, "ASCII"); - - // Build the property for one comment - Property[] comAr = new Property[2]; - comAr[0] = new Property ("Timestamp", - PropertyType.DATE, - jTimestamp); - comAr[1] = new Property ("CommentText", - PropertyType.STRING, - comment); - comments.add (new Property ("Comment", - PropertyType.PROPERTY, - PropertyArity.ARRAY, - comAr)); + /** + * Reads a chunk and puts a Comments property into the RepInfo object. + * + * @return false if the chunk is structurally invalid, otherwise true + */ + @Override + public boolean readChunk(RepInfo info) throws IOException { + AiffModule module = (AiffModule) _module; + int numComments = module.readUnsignedShort(_dstream); + bytesLeft -= 2; + if (numComments == 0) { + return true; // trivial case + } + // Create a List of comment properties + List comments = new ArrayList<>(numComments); + for (int i = 0; i < numComments; i++) { + long timestamp = module.readUnsignedInt(_dstream); + Date jTimestamp = module.timestampToDate(timestamp); + // Skip marker short + module.readSignedShort(_dstream); + int count = module.readUnsignedShort(_dstream); + bytesLeft -= 8; + byte[] buf = new byte[count]; + ModuleBase.readByteBuf(_dstream, buf, module); + bytesLeft -= count; + /* Ensure that each byt is a printable ASCII character. */ + for (int j = 0; j < buf.length; j++) { + if (buf[j] < 32 || buf[j] > 127) { + buf[j] = 32; } - module.addAiffProperty(new Property ("Comments", - PropertyType.PROPERTY, - PropertyArity.LIST, - comments)); + } + String comment = new String(buf, "ASCII"); - return true; + // Build the property for one comment + Property[] comAr = new Property[2]; + comAr[0] = new Property("Timestamp", PropertyType.DATE, jTimestamp); + comAr[1] = new Property("CommentText", PropertyType.STRING, comment); + comments.add(new Property("Comment", PropertyType.PROPERTY, PropertyArity.ARRAY, comAr)); } + module.addAiffProperty( + new Property("Comments", PropertyType.PROPERTY, PropertyArity.LIST, comments)); + + return true; + } } diff --git a/jhove-modules/aiff-hul/src/main/java/edu/harvard/hul/ois/jhove/module/aiff/CommonChunk.java b/jhove-modules/aiff-hul/src/main/java/edu/harvard/hul/ois/jhove/module/aiff/CommonChunk.java index 37db9d42e..968677b05 100644 --- a/jhove-modules/aiff-hul/src/main/java/edu/harvard/hul/ois/jhove/module/aiff/CommonChunk.java +++ b/jhove-modules/aiff-hul/src/main/java/edu/harvard/hul/ois/jhove/module/aiff/CommonChunk.java @@ -1,184 +1,169 @@ -/********************************************************************** - * Jhove - JSTOR/Harvard Object Validation Environment - * Copyright 2004 by JSTOR and the President and Fellows of Harvard College +/** + * ******************************************************************** Jhove - JSTOR/Harvard Object + * Validation Environment Copyright 2004 by JSTOR and the President and Fellows of Harvard College * - **********************************************************************/ - + *

******************************************************************** + */ package edu.harvard.hul.ois.jhove.module.aiff; import edu.harvard.hul.ois.jhove.*; -import java.io.DataInputStream; -import java.io.IOException; - import edu.harvard.hul.ois.jhove.module.AiffModule; import edu.harvard.hul.ois.jhove.module.iff.*; +import java.io.DataInputStream; +import java.io.IOException; /** * The AIFF Common chunk. - * - * @author Gary McGath * + * @author Gary McGath */ public class CommonChunk extends Chunk { - /** - * Constructor. - * - * @param module The AIFFModule under which this was called - * @param hdr The header for this chunk - * @param dstrm The stream from which the AIFF data are being read - */ - public CommonChunk( - AiffModule module, - ChunkHeader hdr, - DataInputStream dstrm) { - super(module, hdr, dstrm); + /** + * Constructor. + * + * @param module The AIFFModule under which this was called + * @param hdr The header for this chunk + * @param dstrm The stream from which the AIFF data are being read + */ + public CommonChunk(AiffModule module, ChunkHeader hdr, DataInputStream dstrm) { + super(module, hdr, dstrm); + } + + /** + * Reads a chunk and puts various properties into the RepInfo object. + * + * @return false if the chunk is structurally invalid, otherwise true + */ + @Override + public boolean readChunk(RepInfo info) throws IOException { + AiffModule module = (AiffModule) _module; + int numChannels = module.readUnsignedShort(_dstream); + long numSampleFrames = module.readUnsignedInt(_dstream); + int sampleSize = module.readUnsignedShort(_dstream); + bytesLeft -= 8; + + String compressionType = null; + String compressionName = null; + + double sampleRate = module.read80BitDouble(_dstream); + bytesLeft -= 10; + + if (module.getFileType() == AiffModule.AIFCTYPE) { + if (bytesLeft == 0) { + // This is a rather special case, but testing did turn up + // a file that misbehaved in this way. + info.setMessage(new ErrorMessage(MessageConstants.AIFF_HUL_7, module.getNByte())); + info.setWellFormed(false); + return false; + } + compressionType = module.read4Chars(_dstream); + bytesLeft -= 4; + compressionName = module.readPascalString(_dstream); + bytesLeft -= compressionName.length() + 1; } - /** Reads a chunk and puts various properties into - * the RepInfo object. - * - * @return false if the chunk is structurally - * invalid, otherwise true - */ - @Override - public boolean readChunk(RepInfo info) throws IOException { - AiffModule module = (AiffModule) _module; - int numChannels = module.readUnsignedShort (_dstream); - long numSampleFrames = module.readUnsignedInt (_dstream); - int sampleSize = module.readUnsignedShort (_dstream); - bytesLeft -= 8; - - String compressionType = null; - String compressionName = null; - - double sampleRate = module.read80BitDouble (_dstream); - bytesLeft -= 10; - - if (module.getFileType () == AiffModule.AIFCTYPE) { - if (bytesLeft == 0) { - // This is a rather special case, but testing did turn up - // a file that misbehaved in this way. - info.setMessage (new ErrorMessage - (MessageConstants.AIFF_HUL_7, - module.getNByte())); - info.setWellFormed (false); - return false; - } - compressionType = module.read4Chars (_dstream); - bytesLeft -= 4; - compressionName = module.readPascalString (_dstream); - bytesLeft -= compressionName.length () + 1; - } - - AESAudioMetadata aes = module.getAESMetadata (); - aes.setBitDepth (sampleSize); - aes.setSampleRate (sampleRate); - aes.setNumChannels (numChannels); - setChannelLocations (aes, numChannels); - //aes.setDuration ((double) numSampleFrames / sampleRate); - aes.setDuration (numSampleFrames); - module.addAiffProperty (new Property ("SampleFrames", - PropertyType.LONG, - new Long (numSampleFrames))); - // Proper handling of compression type should depend - // on whether raw output is set - if (compressionType != null) { - module.addAiffProperty (new Property ("CompressionType", - PropertyType.STRING, - compressionType)); - switch (compressionType) { - case "NONE": - break; - case "raw ": - aes.setAudioDataEncoding("PCM 8-bit offset-binary"); - break; - case "twos": - aes.setAudioDataEncoding("PCM 16-bit twos-complement big-endian"); - break; - case "sowt": - aes.setAudioDataEncoding("PCM 16-bit twos-complement little-endian"); - // According to David Ackerman, the compression type can - // change the endianness of the document. - module.setEndian (false); // little-endian - break; - case "fl32": - aes.setAudioDataEncoding("PCM 32-bit integer"); - break; - case "fl64": - aes.setAudioDataEncoding("PCM 64-bit floating point"); - break; - case "in24": - aes.setAudioDataEncoding("PCM 24-bit integer"); - break; - case "in32": - aes.setAudioDataEncoding("PCM 32-bit integer"); - break; - default: - aes.setAudioDataEncoding (compressionName); - // The size of the data after compression isn't available - // from the Common chunk, so we mark it as "unknown." - // With a bit more sophistication, we could combine the - // information from here and the Sound Data chunk to get - // the effective byte rate, but we're about to release. - String name = compressionName; - if (name == null || name.length () == 0) { - name = compressionType; - } aes.setBitrateReduction (compressionName, "", "", "", - "LOSSY", "UNKNOWN", "FIXED"); - break; - } - } - if (compressionName != null && compressionName.length () > 0) { - module.addAiffProperty (new Property ("CompressionName", - PropertyType.STRING, - compressionName)); - } - - return true; + AESAudioMetadata aes = module.getAESMetadata(); + aes.setBitDepth(sampleSize); + aes.setSampleRate(sampleRate); + aes.setNumChannels(numChannels); + setChannelLocations(aes, numChannels); + // aes.setDuration ((double) numSampleFrames / sampleRate); + aes.setDuration(numSampleFrames); + module.addAiffProperty( + new Property("SampleFrames", PropertyType.LONG, new Long(numSampleFrames))); + // Proper handling of compression type should depend + // on whether raw output is set + if (compressionType != null) { + module.addAiffProperty(new Property("CompressionType", PropertyType.STRING, compressionType)); + switch (compressionType) { + case "NONE": + break; + case "raw ": + aes.setAudioDataEncoding("PCM 8-bit offset-binary"); + break; + case "twos": + aes.setAudioDataEncoding("PCM 16-bit twos-complement big-endian"); + break; + case "sowt": + aes.setAudioDataEncoding("PCM 16-bit twos-complement little-endian"); + // According to David Ackerman, the compression type can + // change the endianness of the document. + module.setEndian(false); // little-endian + break; + case "fl32": + aes.setAudioDataEncoding("PCM 32-bit integer"); + break; + case "fl64": + aes.setAudioDataEncoding("PCM 64-bit floating point"); + break; + case "in24": + aes.setAudioDataEncoding("PCM 24-bit integer"); + break; + case "in32": + aes.setAudioDataEncoding("PCM 32-bit integer"); + break; + default: + aes.setAudioDataEncoding(compressionName); + // The size of the data after compression isn't available + // from the Common chunk, so we mark it as "unknown." + // With a bit more sophistication, we could combine the + // information from here and the Sound Data chunk to get + // the effective byte rate, but we're about to release. + String name = compressionName; + if (name == null || name.length() == 0) { + name = compressionType; + } + aes.setBitrateReduction(compressionName, "", "", "", "LOSSY", "UNKNOWN", "FIXED"); + break; + } + } + if (compressionName != null && compressionName.length() > 0) { + module.addAiffProperty(new Property("CompressionName", PropertyType.STRING, compressionName)); } - /* Assign channel locationss according to the number of - * channels and the standard AIFF assignment. */ - @SuppressWarnings("fallthrough") - private static void setChannelLocations - (AESAudioMetadata aes, int numChannels) - { - String[] mapLoc = new String[numChannels]; - switch (numChannels) { - case 1: - mapLoc[0] = "UNKNOWN"; - break; - - // There are two 4-channel alternatives. Pick one. - case 4: - mapLoc[3] = "SURROUND"; - // fall through to case 3 + return true; + } + + /* Assign channel locationss according to the number of + * channels and the standard AIFF assignment. */ + @SuppressWarnings("fallthrough") + private static void setChannelLocations(AESAudioMetadata aes, int numChannels) { + String[] mapLoc = new String[numChannels]; + switch (numChannels) { + case 1: + mapLoc[0] = "UNKNOWN"; + break; + + // There are two 4-channel alternatives. Pick one. + case 4: + mapLoc[3] = "SURROUND"; + // fall through to case 3 + + case 3: + mapLoc[2] = "CENTER"; + // fall through to case 2 + + case 2: + mapLoc[0] = "LEFT"; + mapLoc[1] = "RIGHT"; + break; + + case 6: + mapLoc[0] = "LEFT"; + mapLoc[1] = "LEFT_CENTER"; + mapLoc[2] = "CENTER"; + mapLoc[3] = "RIGHT"; + mapLoc[4] = "RIGHT_CENTER"; + mapLoc[5] = "SURROUND"; + break; - case 3: - mapLoc[2] = "CENTER"; - // fall through to case 2 - - case 2: - mapLoc[0] = "LEFT"; - mapLoc[1] = "RIGHT"; - break; - - case 6: - mapLoc[0] = "LEFT"; - mapLoc[1] = "LEFT_CENTER"; - mapLoc[2] = "CENTER"; - mapLoc[3] = "RIGHT"; - mapLoc[4] = "RIGHT_CENTER"; - mapLoc[5] = "SURROUND"; - break; - - // If we get some other number of channels, punt. - default: - for (int i = 0; i < numChannels; i++) { - mapLoc[i] = "UNKNOWN"; - } + // If we get some other number of channels, punt. + default: + for (int i = 0; i < numChannels; i++) { + mapLoc[i] = "UNKNOWN"; } - aes.setMapLocations(mapLoc); } + aes.setMapLocations(mapLoc); + } } diff --git a/jhove-modules/aiff-hul/src/main/java/edu/harvard/hul/ois/jhove/module/aiff/CopyrightChunk.java b/jhove-modules/aiff-hul/src/main/java/edu/harvard/hul/ois/jhove/module/aiff/CopyrightChunk.java index ff52b7190..d8060881c 100644 --- a/jhove-modules/aiff-hul/src/main/java/edu/harvard/hul/ois/jhove/module/aiff/CopyrightChunk.java +++ b/jhove-modules/aiff-hul/src/main/java/edu/harvard/hul/ois/jhove/module/aiff/CopyrightChunk.java @@ -1,37 +1,30 @@ -/********************************************************************** - * Jhove - JSTOR/Harvard Object Validation Environment - * Copyright 2004 by JSTOR and the President and Fellows of Harvard College - **********************************************************************/ - +/** + * ******************************************************************** Jhove - JSTOR/Harvard Object + * Validation Environment Copyright 2004 by JSTOR and the President and Fellows of Harvard College + * ******************************************************************** + */ package edu.harvard.hul.ois.jhove.module.aiff; -import java.io.DataInputStream; import edu.harvard.hul.ois.jhove.module.AiffModule; import edu.harvard.hul.ois.jhove.module.iff.*; +import java.io.DataInputStream; /** * Implementation of the AIFF Copyright Chunk. * * @author Gary McGath - * */ public class CopyrightChunk extends TextChunk { - /** - * Constructor. - * - * @param module The AIFFModule under which this was called - * @param hdr The header for this chunk - * @param dstrm The stream from which the AIFF data are being read - */ - public CopyrightChunk( - AiffModule module, - ChunkHeader hdr, - DataInputStream dstrm) - { - super(module, hdr, dstrm); - propName = "Copyright"; - } - - + /** + * Constructor. + * + * @param module The AIFFModule under which this was called + * @param hdr The header for this chunk + * @param dstrm The stream from which the AIFF data are being read + */ + public CopyrightChunk(AiffModule module, ChunkHeader hdr, DataInputStream dstrm) { + super(module, hdr, dstrm); + propName = "Copyright"; + } } diff --git a/jhove-modules/aiff-hul/src/main/java/edu/harvard/hul/ois/jhove/module/aiff/ExtDouble.java b/jhove-modules/aiff-hul/src/main/java/edu/harvard/hul/ois/jhove/module/aiff/ExtDouble.java index 422a5f65b..a1af0bca3 100644 --- a/jhove-modules/aiff-hul/src/main/java/edu/harvard/hul/ois/jhove/module/aiff/ExtDouble.java +++ b/jhove-modules/aiff-hul/src/main/java/edu/harvard/hul/ois/jhove/module/aiff/ExtDouble.java @@ -1,82 +1,74 @@ -/********************************************************************** - * Jhove - JSTOR/Harvard Object Validation Environment - * Copyright 2004 by JSTOR and the President and Fellows of Harvard College - **********************************************************************/ - +/** + * ******************************************************************** Jhove - JSTOR/Harvard Object + * Validation Environment Copyright 2004 by JSTOR and the President and Fellows of Harvard College + * ******************************************************************** + */ package edu.harvard.hul.ois.jhove.module.aiff; /** - * Code to deal with the 80-bit floating point (extended double) - * numbers which occur in AIFF files. Should also be applicable - * in general. - * - * Java has no built-in support for IEEE 754 extended double numbers. - * Thus, we have to unpack the number and convert it to a double by - * hand. There is, of course, loss of precision. - * - * This isn't designed for high-precision work; as the standard - * disclaimer says, don't use it for life support systems or nuclear - * power plants. + * Code to deal with the 80-bit floating point (extended double) numbers which occur in AIFF files. + * Should also be applicable in general. * - * @author Gary McGath + *

Java has no built-in support for IEEE 754 extended double numbers. Thus, we have to unpack the + * number and convert it to a double by hand. There is, of course, loss of precision. + * + *

This isn't designed for high-precision work; as the standard disclaimer says, don't use it for + * life support systems or nuclear power plants. * + * @author Gary McGath */ public class ExtDouble { - byte[] _rawData; - - /** - * Constructor. - * - * @param rawData A 10-byte array representing the number - * in the sequence in which it was stored. - */ - public ExtDouble(byte[] rawData) - { - _rawData = rawData; - } + byte[] _rawData; + + /** + * Constructor. + * + * @param rawData A 10-byte array representing the number in the sequence in which it was stored. + */ + public ExtDouble(byte[] rawData) { + _rawData = rawData; + } + + /** + * Convert the value to a Java double. This results in loss of precision. If the number is out of + * range, results aren't guaranteed. + */ + public double toDouble() { + int sign; + int exponent; + long mantissa = 0; + // Extract the sign bit. + sign = _rawData[0] >> 7; + + // Extract the exponent. It's stored with a + // bias of 16383, so subtract that off. + // Also, the mantissa is between 1 and 2 (i.e., + // all but 1 digits are to the right of the binary point, so + // we take 62 (not 63: see below) off the exponent for that. + exponent = (_rawData[0] << 8) | _rawData[1]; + exponent &= 0X7FFF; // strip off sign bit + exponent -= (16383 + 62); // 1 is added to the "real" exponent + + // Extract the mantissa. It's 64 bits of unsigned + // data, but a long is a signed number, so we have to + // discard the LSB. We'll lose more than that converting + // to double anyway. This division by 2 is the reason for + // adding an extra 1 to the exponent above. + int shifter = 55; + for (int i = 2; i < 9; i++) { + mantissa |= (_rawData[i] & 0XFFL) << shifter; + shifter -= 8; + } + mantissa |= _rawData[9] >>> 1; - /** Convert the value to a Java double. This results in - * loss of precision. If the number is out of range, - * results aren't guaranteed. - */ - public double toDouble () - { - int sign; - int exponent; - long mantissa = 0; - - // Extract the sign bit. - sign = _rawData[0] >> 7; - - // Extract the exponent. It's stored with a - // bias of 16383, so subtract that off. - // Also, the mantissa is between 1 and 2 (i.e., - // all but 1 digits are to the right of the binary point, so - // we take 62 (not 63: see below) off the exponent for that. - exponent = (_rawData[0] << 8) | _rawData[1]; - exponent &= 0X7FFF; // strip off sign bit - exponent -= (16383 + 62); // 1 is added to the "real" exponent - - // Extract the mantissa. It's 64 bits of unsigned - // data, but a long is a signed number, so we have to - // discard the LSB. We'll lose more than that converting - // to double anyway. This division by 2 is the reason for - // adding an extra 1 to the exponent above. - int shifter = 55; - for (int i = 2; i < 9; i++) { - mantissa |= (_rawData[i] & 0XFFL) << shifter; - shifter -= 8; - } - mantissa |= _rawData[9] >>> 1; - - // Now put it together in a floating point number. - double val = Math.pow (2, exponent); - val *= mantissa; - if (sign != 0) { - val = -val; - } - return val; + // Now put it together in a floating point number. + double val = Math.pow(2, exponent); + val *= mantissa; + if (sign != 0) { + val = -val; } + return val; + } } diff --git a/jhove-modules/aiff-hul/src/main/java/edu/harvard/hul/ois/jhove/module/aiff/FormatVersionChunk.java b/jhove-modules/aiff-hul/src/main/java/edu/harvard/hul/ois/jhove/module/aiff/FormatVersionChunk.java index 306b1ebcd..fb6466e9d 100644 --- a/jhove-modules/aiff-hul/src/main/java/edu/harvard/hul/ois/jhove/module/aiff/FormatVersionChunk.java +++ b/jhove-modules/aiff-hul/src/main/java/edu/harvard/hul/ois/jhove/module/aiff/FormatVersionChunk.java @@ -1,60 +1,48 @@ -/********************************************************************** - * Jhove - JSTOR/Harvard Object Validation Environment - * Copyright 2004 by JSTOR and the President and Fellows of Harvard College +/** + * ******************************************************************** Jhove - JSTOR/Harvard Object + * Validation Environment Copyright 2004 by JSTOR and the President and Fellows of Harvard College * - **********************************************************************/ - + *

******************************************************************** + */ package edu.harvard.hul.ois.jhove.module.aiff; import edu.harvard.hul.ois.jhove.*; import edu.harvard.hul.ois.jhove.module.AiffModule; import edu.harvard.hul.ois.jhove.module.iff.*; - import java.io.*; import java.util.*; /** - * Implementation of the AIFF Format Version Chunk. - * This chunk occurs only in the AIFF/C variant. - * - * @author Gary McGath + * Implementation of the AIFF Format Version Chunk. This chunk occurs only in the AIFF/C variant. * + * @author Gary McGath */ public class FormatVersionChunk extends Chunk { - - /** - * Constructor. - * - * @param module The AIFFModule under which this was called - * @param hdr The header for this chunk - * @param dstrm The stream from which the AIFF data are being read - */ - public FormatVersionChunk ( - AiffModule module, - ChunkHeader hdr, - DataInputStream dstrm) - { - super (module, hdr, dstrm); - } - - /** Reads a chunk and puts a FormatVersion property into - * the RepInfo object. - * - * @return false if the chunk is structurally - * invalid, otherwise true - */ - @Override - public boolean readChunk (RepInfo info) throws IOException - { - AiffModule module = (AiffModule) _module; - long timestamp = module.readUnsignedInt (_dstream); - // The timestamp is in seconds since January 1, 1904. - // We must convert to Java time. - Date jTimestamp = module.timestampToDate (timestamp); - module.addAiffProperty (new Property ("FormatVersion", - PropertyType.DATE, - jTimestamp)); - return true; - } + /** + * Constructor. + * + * @param module The AIFFModule under which this was called + * @param hdr The header for this chunk + * @param dstrm The stream from which the AIFF data are being read + */ + public FormatVersionChunk(AiffModule module, ChunkHeader hdr, DataInputStream dstrm) { + super(module, hdr, dstrm); + } + + /** + * Reads a chunk and puts a FormatVersion property into the RepInfo object. + * + * @return false if the chunk is structurally invalid, otherwise true + */ + @Override + public boolean readChunk(RepInfo info) throws IOException { + AiffModule module = (AiffModule) _module; + long timestamp = module.readUnsignedInt(_dstream); + // The timestamp is in seconds since January 1, 1904. + // We must convert to Java time. + Date jTimestamp = module.timestampToDate(timestamp); + module.addAiffProperty(new Property("FormatVersion", PropertyType.DATE, jTimestamp)); + return true; + } } diff --git a/jhove-modules/aiff-hul/src/main/java/edu/harvard/hul/ois/jhove/module/aiff/InstrumentChunk.java b/jhove-modules/aiff-hul/src/main/java/edu/harvard/hul/ois/jhove/module/aiff/InstrumentChunk.java index c83a554fe..62078cd2f 100644 --- a/jhove-modules/aiff-hul/src/main/java/edu/harvard/hul/ois/jhove/module/aiff/InstrumentChunk.java +++ b/jhove-modules/aiff-hul/src/main/java/edu/harvard/hul/ois/jhove/module/aiff/InstrumentChunk.java @@ -1,129 +1,94 @@ -/********************************************************************** - * Jhove - JSTOR/Harvard Object Validation Environment - * Copyright 2004 by JSTOR and the President and Fellows of Harvard College - **********************************************************************/ - +/** + * ******************************************************************** Jhove - JSTOR/Harvard Object + * Validation Environment Copyright 2004 by JSTOR and the President and Fellows of Harvard College + * ******************************************************************** + */ package edu.harvard.hul.ois.jhove.module.aiff; -import java.io.DataInputStream; -import java.io.IOException; -import java.util.*; -import edu.harvard.hul.ois.jhove.RepInfo; import edu.harvard.hul.ois.jhove.*; +import edu.harvard.hul.ois.jhove.RepInfo; import edu.harvard.hul.ois.jhove.module.AiffModule; import edu.harvard.hul.ois.jhove.module.iff.*; +import java.io.DataInputStream; +import java.io.IOException; +import java.util.*; /** * Implementation of the AIFF Instrument Chunk. * * @author Gary McGath - * */ public class InstrumentChunk extends Chunk { - /** - * Constructor. - * - * @param module The AIFFModule under which this was called - * @param hdr The header for this chunk - * @param dstrm The stream from which the AIFF data are being read - */ - public InstrumentChunk( - AiffModule module, - ChunkHeader hdr, - DataInputStream dstrm) { - super(module, hdr, dstrm); - } + /** + * Constructor. + * + * @param module The AIFFModule under which this was called + * @param hdr The header for this chunk + * @param dstrm The stream from which the AIFF data are being read + */ + public InstrumentChunk(AiffModule module, ChunkHeader hdr, DataInputStream dstrm) { + super(module, hdr, dstrm); + } - /** Reads a chunk and puts an Instrument property into - * the RepInfo object. - * - * @return false if the chunk is structurally - * invalid, otherwise true - */ - @Override - public boolean readChunk(RepInfo info) throws IOException - { - AiffModule module = (AiffModule) _module; - int baseNote = ModuleBase.readUnsignedByte (_dstream, module); - int detune = ModuleBase.readSignedByte (_dstream, module); - int lowNote = ModuleBase.readUnsignedByte (_dstream, module); - int highNote = ModuleBase.readUnsignedByte (_dstream, module); - int lowVelocity = ModuleBase.readUnsignedByte (_dstream, module); - int highVelocity = ModuleBase.readUnsignedByte (_dstream, module); - int gain = module.readSignedShort (_dstream); - Loop sustainLoop = readLoop (module); - Loop releaseLoop = readLoop (module); - - List propList = new ArrayList (9); - propList.add (new Property ("BaseNote", - PropertyType.INTEGER, - new Integer (baseNote))); - propList.add (new Property ("Detune", - PropertyType.INTEGER, - new Integer (detune))); - propList.add (new Property ("LowNote", - PropertyType.INTEGER, - new Integer (lowNote))); - propList.add (new Property ("HighNote", - PropertyType.INTEGER, - new Integer (highNote))); - propList.add (new Property ("LowVelocity", - PropertyType.INTEGER, - new Integer (lowVelocity))); - propList.add (new Property ("HighVelocity", - PropertyType.INTEGER, - new Integer (highVelocity))); - propList.add (new Property ("Gain", - PropertyType.INTEGER, - new Integer (gain))); - propList.add (sustainLoop.loopProp("SustainLoop")); - propList.add (releaseLoop.loopProp("ReleaseLoop")); - module.addAiffProperty(new Property ("Instrument", - PropertyType.PROPERTY, - PropertyArity.LIST, - propList)); - return true; - } + /** + * Reads a chunk and puts an Instrument property into the RepInfo object. + * + * @return false if the chunk is structurally invalid, otherwise true + */ + @Override + public boolean readChunk(RepInfo info) throws IOException { + AiffModule module = (AiffModule) _module; + int baseNote = ModuleBase.readUnsignedByte(_dstream, module); + int detune = ModuleBase.readSignedByte(_dstream, module); + int lowNote = ModuleBase.readUnsignedByte(_dstream, module); + int highNote = ModuleBase.readUnsignedByte(_dstream, module); + int lowVelocity = ModuleBase.readUnsignedByte(_dstream, module); + int highVelocity = ModuleBase.readUnsignedByte(_dstream, module); + int gain = module.readSignedShort(_dstream); + Loop sustainLoop = readLoop(module); + Loop releaseLoop = readLoop(module); + + List propList = new ArrayList(9); + propList.add(new Property("BaseNote", PropertyType.INTEGER, new Integer(baseNote))); + propList.add(new Property("Detune", PropertyType.INTEGER, new Integer(detune))); + propList.add(new Property("LowNote", PropertyType.INTEGER, new Integer(lowNote))); + propList.add(new Property("HighNote", PropertyType.INTEGER, new Integer(highNote))); + propList.add(new Property("LowVelocity", PropertyType.INTEGER, new Integer(lowVelocity))); + propList.add(new Property("HighVelocity", PropertyType.INTEGER, new Integer(highVelocity))); + propList.add(new Property("Gain", PropertyType.INTEGER, new Integer(gain))); + propList.add(sustainLoop.loopProp("SustainLoop")); + propList.add(releaseLoop.loopProp("ReleaseLoop")); + module.addAiffProperty( + new Property("Instrument", PropertyType.PROPERTY, PropertyArity.LIST, propList)); + return true; + } + private Loop readLoop(AiffModule module) throws IOException { + int playMode = module.readSignedShort(_dstream); + int beginLoop = module.readUnsignedShort(_dstream); + int endLoop = module.readUnsignedShort(_dstream); + return new Loop(playMode, beginLoop, endLoop); + } - private Loop readLoop (AiffModule module) throws IOException - { - int playMode = module.readSignedShort(_dstream); - int beginLoop = module.readUnsignedShort (_dstream); - int endLoop = module.readUnsignedShort (_dstream); - return new Loop (playMode, beginLoop, endLoop); + /* Local class for encapsulating the Loop structure */ + private class Loop { + public int playMode; + public int beginLoop; + public int endLoop; + + public Loop(int playMode, int beginLoop, int endLoop) { + this.playMode = playMode; + this.beginLoop = beginLoop; + this.endLoop = endLoop; } - - - /* Local class for encapsulating the Loop structure */ - private class Loop { - public int playMode; - public int beginLoop; - public int endLoop; - - public Loop (int playMode, int beginLoop, int endLoop) - { - this.playMode = playMode; - this.beginLoop = beginLoop; - this.endLoop = endLoop; - } - - public Property loopProp (String name) - { - Property[] propArr = new Property[3]; - propArr[0] = _module.addIntegerProperty("PlayMode", playMode, - AiffStrings.LOOP_TYPE); - propArr[1] = new Property ("BeginLoop", - PropertyType.INTEGER, - new Integer (beginLoop)); - propArr[2] = new Property ("EndLoop", - PropertyType.INTEGER, - new Integer (endLoop)); - return new Property (name, - PropertyType.PROPERTY, - PropertyArity.ARRAY, - propArr); - } + + public Property loopProp(String name) { + Property[] propArr = new Property[3]; + propArr[0] = _module.addIntegerProperty("PlayMode", playMode, AiffStrings.LOOP_TYPE); + propArr[1] = new Property("BeginLoop", PropertyType.INTEGER, new Integer(beginLoop)); + propArr[2] = new Property("EndLoop", PropertyType.INTEGER, new Integer(endLoop)); + return new Property(name, PropertyType.PROPERTY, PropertyArity.ARRAY, propArr); } + } } diff --git a/jhove-modules/aiff-hul/src/main/java/edu/harvard/hul/ois/jhove/module/aiff/MarkerChunk.java b/jhove-modules/aiff-hul/src/main/java/edu/harvard/hul/ois/jhove/module/aiff/MarkerChunk.java index c240c3c32..252005600 100644 --- a/jhove-modules/aiff-hul/src/main/java/edu/harvard/hul/ois/jhove/module/aiff/MarkerChunk.java +++ b/jhove-modules/aiff-hul/src/main/java/edu/harvard/hul/ois/jhove/module/aiff/MarkerChunk.java @@ -1,78 +1,61 @@ -/********************************************************************** - * Jhove - JSTOR/Harvard Object Validation Environment - * Copyright 2004 by JSTOR and the President and Fellows of Harvard College - **********************************************************************/ - +/** + * ******************************************************************** Jhove - JSTOR/Harvard Object + * Validation Environment Copyright 2004 by JSTOR and the President and Fellows of Harvard College + * ******************************************************************** + */ package edu.harvard.hul.ois.jhove.module.aiff; -import java.io.DataInputStream; -import java.io.IOException; -import java.util.*; import edu.harvard.hul.ois.jhove.*; import edu.harvard.hul.ois.jhove.module.AiffModule; import edu.harvard.hul.ois.jhove.module.iff.*; +import java.io.DataInputStream; +import java.io.IOException; +import java.util.*; /** * The AIFF Marker chunk. * * @author Gary McGath - * */ public class MarkerChunk extends Chunk { - /** - * Constructor. - * - * @param module The AIFFModule under which this was called - * @param hdr The header for this chunk - * @param dstrm The stream from which the AIFF data are being read - */ - public MarkerChunk( - AiffModule module, - ChunkHeader hdr, - DataInputStream dstrm) { - super(module, hdr, dstrm); - } + /** + * Constructor. + * + * @param module The AIFFModule under which this was called + * @param hdr The header for this chunk + * @param dstrm The stream from which the AIFF data are being read + */ + public MarkerChunk(AiffModule module, ChunkHeader hdr, DataInputStream dstrm) { + super(module, hdr, dstrm); + } - /** Reads a chunk and puts a Markers property into - * the RepInfo object. - * - * @return false if the chunk is structurally - * invalid, otherwise true - */ - @Override - public boolean readChunk(RepInfo info) throws IOException { - AiffModule module = (AiffModule) _module; - int numMarkers = module.readUnsignedShort (_dstream); - if (numMarkers == 0) { - return true; // trivial but legal case - } - List markerList = new ArrayList (numMarkers); - for (int i = 0; i < numMarkers; i++) { - int id = module.readUnsignedShort (_dstream); - long position = module.readUnsignedInt (_dstream); - String markerName = module.readPascalString(_dstream); - - Property[] mArr = new Property[3]; - mArr[0] = new Property ("ID", - PropertyType.INTEGER, - new Integer (id)); - mArr[1] = new Property ("Position", - PropertyType.LONG, - new Long (position)); - mArr[2] = new Property ("Name", - PropertyType.STRING, - markerName); - markerList.add (new Property ("Marker", - PropertyType.PROPERTY, - PropertyArity.ARRAY, - mArr)); - } - module.addAiffProperty (new Property ("Markers", - PropertyType.PROPERTY, - PropertyArity.LIST, - markerList)); - return true; + /** + * Reads a chunk and puts a Markers property into the RepInfo object. + * + * @return false if the chunk is structurally invalid, otherwise true + */ + @Override + public boolean readChunk(RepInfo info) throws IOException { + AiffModule module = (AiffModule) _module; + int numMarkers = module.readUnsignedShort(_dstream); + if (numMarkers == 0) { + return true; // trivial but legal case } + List markerList = new ArrayList(numMarkers); + for (int i = 0; i < numMarkers; i++) { + int id = module.readUnsignedShort(_dstream); + long position = module.readUnsignedInt(_dstream); + String markerName = module.readPascalString(_dstream); + Property[] mArr = new Property[3]; + mArr[0] = new Property("ID", PropertyType.INTEGER, new Integer(id)); + mArr[1] = new Property("Position", PropertyType.LONG, new Long(position)); + mArr[2] = new Property("Name", PropertyType.STRING, markerName); + markerList.add(new Property("Marker", PropertyType.PROPERTY, PropertyArity.ARRAY, mArr)); + } + module.addAiffProperty( + new Property("Markers", PropertyType.PROPERTY, PropertyArity.LIST, markerList)); + return true; + } } diff --git a/jhove-modules/aiff-hul/src/main/java/edu/harvard/hul/ois/jhove/module/aiff/MessageConstants.java b/jhove-modules/aiff-hul/src/main/java/edu/harvard/hul/ois/jhove/module/aiff/MessageConstants.java index 77417c951..0cbf782b2 100644 --- a/jhove-modules/aiff-hul/src/main/java/edu/harvard/hul/ois/jhove/module/aiff/MessageConstants.java +++ b/jhove-modules/aiff-hul/src/main/java/edu/harvard/hul/ois/jhove/module/aiff/MessageConstants.java @@ -1,6 +1,4 @@ -/** - * - */ +/** */ package edu.harvard.hul.ois.jhove.module.aiff; import edu.harvard.hul.ois.jhove.messages.JhoveMessage; @@ -8,52 +6,57 @@ import edu.harvard.hul.ois.jhove.messages.JhoveMessages; /** - * Enum used to externalise the PDF modules message Strings. Using an enum - * INSTANCE as a "trick" to ensure a single instance of the class. String - * constants should be prefixed according to their use in the module: + * Enum used to externalise the PDF modules message Strings. Using an enum INSTANCE as a "trick" to + * ensure a single instance of the class. String constants should be prefixed according to their use + * in the module: + * *

    - *
  • WRN_ for warning strings, often logger messages.
  • - *
  • INF_ for informational messages.
  • - *
  • ERR_ for error messages that indicate a file is invalid or not well - * formed.
  • + *
  • WRN_ for warning strings, often logger messages. + *
  • INF_ for informational messages. + *
  • ERR_ for error messages that indicate a file is invalid or not well formed. *
- * When adding new messages try to adopt the following order for the naming - * elements: + * + * When adding new messages try to adopt the following order for the naming elements: + * *
    - *
  1. PREFIX: one of the three prefixes from the list above.
  2. - *
  3. ENTITY_NAME: the name of the PDF entity causing the prohlem, e.g. FONT, - * or DOC.
  4. - *
  5. Problem: a short indicator of the problem type, e.g. MISSING, ILLEGAL, - * etc.
  6. + *
  7. PREFIX: one of the three prefixes from the list above. + *
  8. ENTITY_NAME: the name of the PDF entity causing the prohlem, e.g. FONT, or DOC. + *
  9. Problem: a short indicator of the problem type, e.g. MISSING, ILLEGAL, etc. *
- * The elements should be separated by underscores. The messages currently don't - * follow a consistent vocabulary, that is terms such as invalid, illegal, or - * malformed are used without definition. - * - * @author Carl Wilson - * carlwilson AT github + * + * The elements should be separated by underscores. The messages currently don't follow a consistent + * vocabulary, that is terms such as invalid, illegal, or malformed are used without definition. + * + * @author Carl Wilson carlwilson AT github * @version 0.1 Created 1 Oct 2016:11:38:18 */ - public enum MessageConstants { - INSTANCE; + INSTANCE; + + private static final JhoveMessageFactory messageFactory = + JhoveMessages.getInstance( + "edu.harvard.hul.ois.jhove.module.aiff.ErrorMessages"); //$NON-NLS-1$ + /** Info messages */ + + /** Error messages */ + public static final JhoveMessage AIFF_HUL_1 = + messageFactory.getMessage("AIFF-HUL-1"); // $NON-NLS-1$ - private static final JhoveMessageFactory messageFactory = JhoveMessages.getInstance("edu.harvard.hul.ois.jhove.module.aiff.ErrorMessages"); //$NON-NLS-1$ - /** - * Info messages - */ - - - /** - * Error messages - */ - public static final JhoveMessage AIFF_HUL_1 = messageFactory.getMessage("AIFF-HUL-1"); //$NON-NLS-1$ - public static final JhoveMessage AIFF_HUL_2 = messageFactory.getMessage("AIFF-HUL-2"); //$NON-NLS-1$ - public static final JhoveMessage AIFF_HUL_3 = messageFactory.getMessage("AIFF-HUL-3"); //$NON-NLS-1$ - public static final JhoveMessage AIFF_HUL_4 = messageFactory.getMessage("AIFF-HUL-4"); //$NON-NLS-1$ - public static final JhoveMessage AIFF_HUL_5 = messageFactory.getMessage("AIFF-HUL-5"); //$NON-NLS-1$ - public static final JhoveMessage AIFF_HUL_6 = messageFactory.getMessage("AIFF-HUL-6"); //$NON-NLS-1$ - public static final JhoveMessage AIFF_HUL_7 = messageFactory.getMessage("AIFF-HUL-7"); //$NON-NLS-1$ - public static final JhoveMessage AIFF_HUL_8 = messageFactory.getMessage("AIFF-HUL-8"); //$NON-NLS-1$ - public static final JhoveMessage AIFF_HUL_9 = messageFactory.getMessage("AIFF-HUL-9"); //$NON-NLS-1$ -} \ No newline at end of file + public static final JhoveMessage AIFF_HUL_2 = + messageFactory.getMessage("AIFF-HUL-2"); // $NON-NLS-1$ + public static final JhoveMessage AIFF_HUL_3 = + messageFactory.getMessage("AIFF-HUL-3"); // $NON-NLS-1$ + public static final JhoveMessage AIFF_HUL_4 = + messageFactory.getMessage("AIFF-HUL-4"); // $NON-NLS-1$ + public static final JhoveMessage AIFF_HUL_5 = + messageFactory.getMessage("AIFF-HUL-5"); // $NON-NLS-1$ + public static final JhoveMessage AIFF_HUL_6 = + messageFactory.getMessage("AIFF-HUL-6"); // $NON-NLS-1$ + public static final JhoveMessage AIFF_HUL_7 = + messageFactory.getMessage("AIFF-HUL-7"); // $NON-NLS-1$ + public static final JhoveMessage AIFF_HUL_8 = + messageFactory.getMessage("AIFF-HUL-8"); // $NON-NLS-1$ + public static final JhoveMessage AIFF_HUL_9 = + messageFactory.getMessage("AIFF-HUL-9"); // $NON-NLS-1$ +} diff --git a/jhove-modules/aiff-hul/src/main/java/edu/harvard/hul/ois/jhove/module/aiff/MidiChunk.java b/jhove-modules/aiff-hul/src/main/java/edu/harvard/hul/ois/jhove/module/aiff/MidiChunk.java index f8104a338..9669cd96f 100644 --- a/jhove-modules/aiff-hul/src/main/java/edu/harvard/hul/ois/jhove/module/aiff/MidiChunk.java +++ b/jhove-modules/aiff-hul/src/main/java/edu/harvard/hul/ois/jhove/module/aiff/MidiChunk.java @@ -1,59 +1,49 @@ -/********************************************************************** - * Jhove - JSTOR/Harvard Object Validation Environment - * Copyright 2004 by JSTOR and the President and Fellows of Harvard College - **********************************************************************/ - +/** + * ******************************************************************** Jhove - JSTOR/Harvard Object + * Validation Environment Copyright 2004 by JSTOR and the President and Fellows of Harvard College + * ******************************************************************** + */ package edu.harvard.hul.ois.jhove.module.aiff; -import java.io.DataInputStream; -import java.io.IOException; - import edu.harvard.hul.ois.jhove.*; import edu.harvard.hul.ois.jhove.module.AiffModule; import edu.harvard.hul.ois.jhove.module.iff.Chunk; import edu.harvard.hul.ois.jhove.module.iff.ChunkHeader; +import java.io.DataInputStream; +import java.io.IOException; /** * Implementation of the AIFF MIDI Chunk. * * @author Gary McGath - * */ public class MidiChunk extends Chunk { - /** - * Constructor. - * - * @param module The AIFFModule under which this was called - * @param hdr The header for this chunk - * @param dstrm The stream from which the AIFF data are being read - */ - public MidiChunk( - AiffModule module, - ChunkHeader hdr, - DataInputStream dstrm) { - super(module, hdr, dstrm); - } + /** + * Constructor. + * + * @param module The AIFFModule under which this was called + * @param hdr The header for this chunk + * @param dstrm The stream from which the AIFF data are being read + */ + public MidiChunk(AiffModule module, ChunkHeader hdr, DataInputStream dstrm) { + super(module, hdr, dstrm); + } - /** Reads a chunk and puts an MIDI property into - * the RepInfo object. - * - * @return false if the chunk is structurally - * invalid, otherwise true - */ - @Override - public boolean readChunk(RepInfo info) throws IOException { - AiffModule module = (AiffModule) _module; - if (bytesLeft == 0) { - return true; // dubious, but call it legal - } - byte[] buf = new byte[(int) bytesLeft]; - ModuleBase.readByteBuf (_dstream, buf, _module); - module.addMidi (new Property ("MIDI", - PropertyType.BYTE, - PropertyArity.ARRAY, - buf)); - return true; + /** + * Reads a chunk and puts an MIDI property into the RepInfo object. + * + * @return false if the chunk is structurally invalid, otherwise true + */ + @Override + public boolean readChunk(RepInfo info) throws IOException { + AiffModule module = (AiffModule) _module; + if (bytesLeft == 0) { + return true; // dubious, but call it legal } - + byte[] buf = new byte[(int) bytesLeft]; + ModuleBase.readByteBuf(_dstream, buf, _module); + module.addMidi(new Property("MIDI", PropertyType.BYTE, PropertyArity.ARRAY, buf)); + return true; + } } diff --git a/jhove-modules/aiff-hul/src/main/java/edu/harvard/hul/ois/jhove/module/aiff/NameChunk.java b/jhove-modules/aiff-hul/src/main/java/edu/harvard/hul/ois/jhove/module/aiff/NameChunk.java index 1901df789..355a2e29a 100644 --- a/jhove-modules/aiff-hul/src/main/java/edu/harvard/hul/ois/jhove/module/aiff/NameChunk.java +++ b/jhove-modules/aiff-hul/src/main/java/edu/harvard/hul/ois/jhove/module/aiff/NameChunk.java @@ -1,36 +1,30 @@ -/********************************************************************** - * Jhove - JSTOR/Harvard Object Validation Environment - * Copyright 2004 by JSTOR and the President and Fellows of Harvard College - **********************************************************************/ - +/** + * ******************************************************************** Jhove - JSTOR/Harvard Object + * Validation Environment Copyright 2004 by JSTOR and the President and Fellows of Harvard College + * ******************************************************************** + */ package edu.harvard.hul.ois.jhove.module.aiff; -import java.io.DataInputStream; import edu.harvard.hul.ois.jhove.module.AiffModule; import edu.harvard.hul.ois.jhove.module.iff.*; +import java.io.DataInputStream; /** * Implementation of the AIFF Name Chunk. * * @author Gary McGath - * */ public class NameChunk extends TextChunk { - /** - * Constructor. - * - * @param module The AIFFModule under which this was called - * @param hdr The header for this chunk - * @param dstrm The stream from which the AIFF data are being read - */ - public NameChunk( - AiffModule module, - ChunkHeader hdr, - DataInputStream dstrm) - { - super(module, hdr, dstrm); - propName = "Name"; - } - + /** + * Constructor. + * + * @param module The AIFFModule under which this was called + * @param hdr The header for this chunk + * @param dstrm The stream from which the AIFF data are being read + */ + public NameChunk(AiffModule module, ChunkHeader hdr, DataInputStream dstrm) { + super(module, hdr, dstrm); + propName = "Name"; + } } diff --git a/jhove-modules/aiff-hul/src/main/java/edu/harvard/hul/ois/jhove/module/aiff/SaxelChunk.java b/jhove-modules/aiff-hul/src/main/java/edu/harvard/hul/ois/jhove/module/aiff/SaxelChunk.java index e88eb0f27..d2fa295ea 100644 --- a/jhove-modules/aiff-hul/src/main/java/edu/harvard/hul/ois/jhove/module/aiff/SaxelChunk.java +++ b/jhove-modules/aiff-hul/src/main/java/edu/harvard/hul/ois/jhove/module/aiff/SaxelChunk.java @@ -1,87 +1,69 @@ -/********************************************************************** - * Jhove - JSTOR/Harvard Object Validation Environment - * Copyright 2004 by JSTOR and the President and Fellows of Harvard College - **********************************************************************/ - +/** + * ******************************************************************** Jhove - JSTOR/Harvard Object + * Validation Environment Copyright 2004 by JSTOR and the President and Fellows of Harvard College + * ******************************************************************** + */ package edu.harvard.hul.ois.jhove.module.aiff; -import java.io.DataInputStream; -import java.io.IOException; -//import java.util.*; import edu.harvard.hul.ois.jhove.*; import edu.harvard.hul.ois.jhove.module.AiffModule; import edu.harvard.hul.ois.jhove.module.iff.Chunk; import edu.harvard.hul.ois.jhove.module.iff.ChunkHeader; +import java.io.DataInputStream; +import java.io.IOException; /** * Implementation of the AIFF Saxel (Sound Accelerator) Chunk. - * - * The Saxel chunk has only a tentative and incomplete status in the - * AIFF-C draft of 1991, and apparently nothing further was - * ever done with it. For purposes of extracting parameters, - * we treat the description of the SaxelChunk and Saxels as - * valid, while regarding the SaxelData as opaque. * - * @author Gary McGath + *

The Saxel chunk has only a tentative and incomplete status in the AIFF-C draft of 1991, and + * apparently nothing further was ever done with it. For purposes of extracting parameters, we treat + * the description of the SaxelChunk and Saxels as valid, while regarding the SaxelData as opaque. * + * @author Gary McGath */ public class SaxelChunk extends Chunk { - /** - * Constructor. - * - * @param module The AIFFModule under which this was called - * @param hdr The header for this chunk - * @param dstrm The stream from which the AIFF data are being read - */ - public SaxelChunk( - AiffModule module, - ChunkHeader hdr, - DataInputStream dstrm) { - super(module, hdr, dstrm); - } + /** + * Constructor. + * + * @param module The AIFFModule under which this was called + * @param hdr The header for this chunk + * @param dstrm The stream from which the AIFF data are being read + */ + public SaxelChunk(AiffModule module, ChunkHeader hdr, DataInputStream dstrm) { + super(module, hdr, dstrm); + } - /** Reads a chunk and puts a "Saxels" property into - * the RepInfo object. - * - * @return false if the chunk is structurally - * invalid, otherwise true - * - */ - @Override - public boolean readChunk(RepInfo info) throws IOException { - AiffModule module = (AiffModule) _module; - int numSaxels = module.readUnsignedShort (_dstream); - bytesLeft -= 2; - if (numSaxels == 0) { - return true; // trivial case - } - // Create a List of properties - for (int i = 0; i < numSaxels; i++) { - // Multiple saxel chunks are allowed, of which - // each can have multiple saxels. We put them - // all together into a single saxel list in - // the module. - Property[] propArr = new Property[2]; - int id = module.readUnsignedShort (_dstream); - int size = module.readUnsignedShort (_dstream); - // Just skip the actual data. - module.skipBytes (_dstream, size, module); - - // Build the property to add to the saxel list. - propArr[0] = new Property ("ID", - PropertyType.INTEGER, - new Integer (id)); - propArr[1] = new Property ("Size", - PropertyType.INTEGER, - new Integer (size)); - module.addSaxel (new Property ("Saxel", - PropertyType.PROPERTY, - PropertyArity.ARRAY, - propArr)); - - } - return true; + /** + * Reads a chunk and puts a "Saxels" property into the RepInfo object. + * + * @return false if the chunk is structurally invalid, otherwise true + */ + @Override + public boolean readChunk(RepInfo info) throws IOException { + AiffModule module = (AiffModule) _module; + int numSaxels = module.readUnsignedShort(_dstream); + bytesLeft -= 2; + if (numSaxels == 0) { + return true; // trivial case } + // Create a List of properties + for (int i = 0; i < numSaxels; i++) { + // Multiple saxel chunks are allowed, of which + // each can have multiple saxels. We put them + // all together into a single saxel list in + // the module. + Property[] propArr = new Property[2]; + int id = module.readUnsignedShort(_dstream); + int size = module.readUnsignedShort(_dstream); + // Just skip the actual data. + module.skipBytes(_dstream, size, module); + // Build the property to add to the saxel list. + propArr[0] = new Property("ID", PropertyType.INTEGER, new Integer(id)); + propArr[1] = new Property("Size", PropertyType.INTEGER, new Integer(size)); + module.addSaxel(new Property("Saxel", PropertyType.PROPERTY, PropertyArity.ARRAY, propArr)); + } + return true; + } } diff --git a/jhove-modules/aiff-hul/src/main/java/edu/harvard/hul/ois/jhove/module/aiff/SoundDataChunk.java b/jhove-modules/aiff-hul/src/main/java/edu/harvard/hul/ois/jhove/module/aiff/SoundDataChunk.java index 1c7b19910..1034e794d 100644 --- a/jhove-modules/aiff-hul/src/main/java/edu/harvard/hul/ois/jhove/module/aiff/SoundDataChunk.java +++ b/jhove-modules/aiff-hul/src/main/java/edu/harvard/hul/ois/jhove/module/aiff/SoundDataChunk.java @@ -1,68 +1,55 @@ -/********************************************************************** - * Jhove - JSTOR/Harvard Object Validation Environment - * Copyright 2004 by JSTOR and the President and Fellows of Harvard College - **********************************************************************/ - +/** + * ******************************************************************** Jhove - JSTOR/Harvard Object + * Validation Environment Copyright 2004 by JSTOR and the President and Fellows of Harvard College + * ******************************************************************** + */ package edu.harvard.hul.ois.jhove.module.aiff; import edu.harvard.hul.ois.jhove.*; import edu.harvard.hul.ois.jhove.module.AiffModule; import edu.harvard.hul.ois.jhove.module.iff.*; - import java.io.DataInputStream; import java.io.IOException; - /** * Implementation of the AIFF Sound Data Chunk. * * @author Gary McGath - * */ public class SoundDataChunk extends Chunk { - /** - * Constructor. - * - * @param module The AIFFModule under which this was called - * @param hdr The header for this chunk - * @param dstrm The stream from which the AIFF data are being read - */ - public SoundDataChunk (AiffModule module, ChunkHeader hdr, - DataInputStream dstrm) - { - super(module, hdr, dstrm); - } - - /** Reads a chunk and puts a SoundData property into - * the RepInfo object. - * - * @return false if the chunk is structurally - * invalid, otherwise true - */ - @Override - public boolean readChunk(RepInfo info) - throws IOException - { - AiffModule module = (AiffModule) _module; - Property[] propArray = new Property[3]; - long offset = module.readUnsignedInt (_dstream); - long blockSize = module.readUnsignedInt (_dstream); - propArray[0] = new Property ("Offset", PropertyType.LONG, - new Long (offset)); - propArray[1] = new Property ("BlockSize", PropertyType.LONG, - new Long (blockSize)); - propArray[2] = new Property ("DataLength", PropertyType.LONG, - new Long (bytesLeft - 8)); - module.addAiffProperty(new Property ("SoundData", - PropertyType.PROPERTY, - PropertyArity.ARRAY, - propArray)); - // This must be called precisely at this point in reading the - // data stream to produce an accurate result. - module.markFirstSampleOffset (offset); - module.skipBytes (_dstream, (int) (bytesLeft - 8), module); - - return true; - } + /** + * Constructor. + * + * @param module The AIFFModule under which this was called + * @param hdr The header for this chunk + * @param dstrm The stream from which the AIFF data are being read + */ + public SoundDataChunk(AiffModule module, ChunkHeader hdr, DataInputStream dstrm) { + super(module, hdr, dstrm); + } + + /** + * Reads a chunk and puts a SoundData property into the RepInfo object. + * + * @return false if the chunk is structurally invalid, otherwise true + */ + @Override + public boolean readChunk(RepInfo info) throws IOException { + AiffModule module = (AiffModule) _module; + Property[] propArray = new Property[3]; + long offset = module.readUnsignedInt(_dstream); + long blockSize = module.readUnsignedInt(_dstream); + propArray[0] = new Property("Offset", PropertyType.LONG, new Long(offset)); + propArray[1] = new Property("BlockSize", PropertyType.LONG, new Long(blockSize)); + propArray[2] = new Property("DataLength", PropertyType.LONG, new Long(bytesLeft - 8)); + module.addAiffProperty( + new Property("SoundData", PropertyType.PROPERTY, PropertyArity.ARRAY, propArray)); + // This must be called precisely at this point in reading the + // data stream to produce an accurate result. + module.markFirstSampleOffset(offset); + module.skipBytes(_dstream, (int) (bytesLeft - 8), module); + + return true; + } } diff --git a/jhove-modules/aiff-hul/src/main/java/edu/harvard/hul/ois/jhove/module/aiff/TextChunk.java b/jhove-modules/aiff-hul/src/main/java/edu/harvard/hul/ois/jhove/module/aiff/TextChunk.java index 0e6d524fc..789858a64 100644 --- a/jhove-modules/aiff-hul/src/main/java/edu/harvard/hul/ois/jhove/module/aiff/TextChunk.java +++ b/jhove-modules/aiff-hul/src/main/java/edu/harvard/hul/ois/jhove/module/aiff/TextChunk.java @@ -1,78 +1,65 @@ -/********************************************************************** - * Jhove - JSTOR/Harvard Object Validation Environment - * Copyright 2004 by JSTOR and the President and Fellows of Harvard College - **********************************************************************/ - +/** + * ******************************************************************** Jhove - JSTOR/Harvard Object + * Validation Environment Copyright 2004 by JSTOR and the President and Fellows of Harvard College + * ******************************************************************** + */ package edu.harvard.hul.ois.jhove.module.aiff; -import java.io.DataInputStream; -import java.io.IOException; import edu.harvard.hul.ois.jhove.*; import edu.harvard.hul.ois.jhove.module.AiffModule; import edu.harvard.hul.ois.jhove.module.iff.*; +import java.io.DataInputStream; +import java.io.IOException; /** - * Abstract superclass for the name, author, copyright, - * and annotation chunks, all of which have the same - * format. + * Abstract superclass for the name, author, copyright, and annotation chunks, all of which have the + * same format. * * @author Gary McGath - * */ public abstract class TextChunk extends Chunk { - /** Name of the property. The subclass constructor - * must set this appropriately. */ - protected String propName; - - /** - * Constructor. - * - * @param module The AIFFModule under which this was called - * @param hdr The header for this chunk - * @param dstrm The stream from which the AIFF data are being read - */ - public TextChunk (AiffModule module, ChunkHeader hdr, - DataInputStream dstrm) - { - super(module, hdr, dstrm); - } + /** Name of the property. The subclass constructor must set this appropriately. */ + protected String propName; - /** Reads a chunk and puts appropriate information into - * the RepInfo object. - * - * This method works for TextChunk, CopyrightChunk and - * AuthorChunk. AnnotationChunk overrides it, since there - * can be multiple annotations. - */ - @Override - public boolean readChunk (RepInfo info) - throws IOException - { - AiffModule module = (AiffModule) _module; - String name = readText (); - module.addAiffProperty (new Property (propName, PropertyType.STRING, - name)); - return true; - } + /** + * Constructor. + * + * @param module The AIFFModule under which this was called + * @param hdr The header for this chunk + * @param dstrm The stream from which the AIFF data are being read + */ + public TextChunk(AiffModule module, ChunkHeader hdr, DataInputStream dstrm) { + super(module, hdr, dstrm); + } + + /** + * Reads a chunk and puts appropriate information into the RepInfo object. + * + *

This method works for TextChunk, CopyrightChunk and AuthorChunk. AnnotationChunk overrides + * it, since there can be multiple annotations. + */ + @Override + public boolean readChunk(RepInfo info) throws IOException { + AiffModule module = (AiffModule) _module; + String name = readText(); + module.addAiffProperty(new Property(propName, PropertyType.STRING, name)); + return true; + } - /** - * Reads the chunk's text data. - * All text chunk subclasses consist of a text string - * which takes up the full byte count of the chunk. - * By the specification, the text is required to be ASCII. - */ - protected String readText () - throws IOException - { - byte[] buf = new byte[(int) bytesLeft]; - ModuleBase.readByteBuf (_dstream, buf, _module); - /* Ensure that each byt is a printable ASCII character. */ - for (int i=0; i 127) { - buf[i] = 32; - } - } - return new String (buf, "ASCII"); + /** + * Reads the chunk's text data. All text chunk subclasses consist of a text string which takes up + * the full byte count of the chunk. By the specification, the text is required to be ASCII. + */ + protected String readText() throws IOException { + byte[] buf = new byte[(int) bytesLeft]; + ModuleBase.readByteBuf(_dstream, buf, _module); + /* Ensure that each byt is a printable ASCII character. */ + for (int i = 0; i < buf.length; i++) { + if (buf[i] < 32 || buf[i] > 127) { + buf[i] = 32; + } } + return new String(buf, "ASCII"); + } } diff --git a/jhove-modules/aiff-hul/src/main/java/edu/harvard/hul/ois/jhove/module/aiff/package-info.java b/jhove-modules/aiff-hul/src/main/java/edu/harvard/hul/ois/jhove/module/aiff/package-info.java index af89e5ec6..15daebea8 100644 --- a/jhove-modules/aiff-hul/src/main/java/edu/harvard/hul/ois/jhove/module/aiff/package-info.java +++ b/jhove-modules/aiff-hul/src/main/java/edu/harvard/hul/ois/jhove/module/aiff/package-info.java @@ -1,4 +1,2 @@ -/** - * Contains supporting classes for the AIFF-HUL module. - */ -package edu.harvard.hul.ois.jhove.module.aiff; \ No newline at end of file +/** Contains supporting classes for the AIFF-HUL module. */ +package edu.harvard.hul.ois.jhove.module.aiff; diff --git a/jhove-modules/aiff-hul/src/main/java/edu/harvard/hul/ois/jhove/module/iff/Chunk.java b/jhove-modules/aiff-hul/src/main/java/edu/harvard/hul/ois/jhove/module/iff/Chunk.java index 3d04d7934..fc28c5fc5 100644 --- a/jhove-modules/aiff-hul/src/main/java/edu/harvard/hul/ois/jhove/module/iff/Chunk.java +++ b/jhove-modules/aiff-hul/src/main/java/edu/harvard/hul/ois/jhove/module/iff/Chunk.java @@ -1,13 +1,12 @@ -/********************************************************************** - * JHOVE - JSTOR/Harvard Object Validation Environment - * Copyright 2004 by JSTOR and the President and Fellows of Harvard College - **********************************************************************/ - +/** + * ******************************************************************** JHOVE - JSTOR/Harvard Object + * Validation Environment Copyright 2004 by JSTOR and the President and Fellows of Harvard College + * ******************************************************************** + */ package edu.harvard.hul.ois.jhove.module.iff; import edu.harvard.hul.ois.jhove.ModuleBase; import edu.harvard.hul.ois.jhove.RepInfo; - import java.io.DataInputStream; import java.io.IOException; @@ -18,66 +17,61 @@ */ public abstract class Chunk { - /** Length of chunk ID fields in bytes */ - public static final int ID_LENGTH = 4; + /** Length of chunk ID fields in bytes */ + public static final int ID_LENGTH = 4; - /** Length of chunk size fields in bytes */ - public static final int SIZE_LENGTH = 4; + /** Length of chunk size fields in bytes */ + public static final int SIZE_LENGTH = 4; - /** Length of chunk headers in bytes */ - public static final int HEADER_LENGTH = ID_LENGTH + SIZE_LENGTH; + /** Length of chunk headers in bytes */ + public static final int HEADER_LENGTH = ID_LENGTH + SIZE_LENGTH; - protected ModuleBase _module; - protected DataInputStream _dstream; - protected long bytesLeft; - protected long chunkSize; - protected long chunkOffset; + protected ModuleBase _module; + protected DataInputStream _dstream; + protected long bytesLeft; + protected long chunkSize; + protected long chunkOffset; - /** - * Class constructor. - * - * @param module The Module under which this was called - * @param hdr The header for this chunk - * @param dstrm The stream from which the data are being read - */ - public Chunk(ModuleBase module, ChunkHeader hdr, DataInputStream dstrm) - { - _module = module; - _dstream = dstrm; - chunkSize = hdr.getSize(); - chunkOffset = hdr.getOffset(); - bytesLeft = chunkSize; - } + /** + * Class constructor. + * + * @param module The Module under which this was called + * @param hdr The header for this chunk + * @param dstrm The stream from which the data are being read + */ + public Chunk(ModuleBase module, ChunkHeader hdr, DataInputStream dstrm) { + _module = module; + _dstream = dstrm; + chunkSize = hdr.getSize(); + chunkOffset = hdr.getOffset(); + bytesLeft = chunkSize; + } - /** - * Reads a chunk and puts appropriate information into the RepInfo object. - * - * @param info RepInfo object to receive information - * @return false if the chunk is structurally - * invalid, otherwise true - */ - public abstract boolean readChunk(RepInfo info) throws IOException; + /** + * Reads a chunk and puts appropriate information into the RepInfo object. + * + * @param info RepInfo object to receive information + * @return false if the chunk is structurally invalid, otherwise true + */ + public abstract boolean readChunk(RepInfo info) throws IOException; - /** - * Converts a byte buffer cleanly into an ASCII string. - * This is used for fixed-allocation strings in Broadcast - * WAVE chunks, and might have uses elsewhere. - * - * If a string is shorter than its fixed allocation, we're - * guaranteed only that there is a null terminating the string, - * and noise could follow it. So we can't use the byte buffer - * constructor for a string. - */ - protected String byteBufString(byte[] byteArray) - { - StringBuilder sb = new StringBuilder(byteArray.length); - for (byte b : byteArray) { - // Terminate if we see a null - if (b == 0) { - break; - } - sb.append((char) b); - } - return sb.toString(); + /** + * Converts a byte buffer cleanly into an ASCII string. This is used for fixed-allocation strings + * in Broadcast WAVE chunks, and might have uses elsewhere. + * + *

If a string is shorter than its fixed allocation, we're guaranteed only that there is a null + * terminating the string, and noise could follow it. So we can't use the byte buffer constructor + * for a string. + */ + protected String byteBufString(byte[] byteArray) { + StringBuilder sb = new StringBuilder(byteArray.length); + for (byte b : byteArray) { + // Terminate if we see a null + if (b == 0) { + break; + } + sb.append((char) b); } + return sb.toString(); + } } diff --git a/jhove-modules/aiff-hul/src/main/java/edu/harvard/hul/ois/jhove/module/iff/ChunkHeader.java b/jhove-modules/aiff-hul/src/main/java/edu/harvard/hul/ois/jhove/module/iff/ChunkHeader.java index 187a237d5..0a9a45897 100644 --- a/jhove-modules/aiff-hul/src/main/java/edu/harvard/hul/ois/jhove/module/iff/ChunkHeader.java +++ b/jhove-modules/aiff-hul/src/main/java/edu/harvard/hul/ois/jhove/module/iff/ChunkHeader.java @@ -1,9 +1,9 @@ -/********************************************************************** - * Jhove - JSTOR/Harvard Object Validation Environment - * Copyright 2003 by JSTOR and the President and Fellows of Harvard College +/** + * ******************************************************************** Jhove - JSTOR/Harvard Object + * Validation Environment Copyright 2003 by JSTOR and the President and Fellows of Harvard College * - **********************************************************************/ - + *

******************************************************************** + */ package edu.harvard.hul.ois.jhove.module.iff; import edu.harvard.hul.ois.jhove.ErrorMessage; @@ -11,128 +11,118 @@ import edu.harvard.hul.ois.jhove.RepInfo; import edu.harvard.hul.ois.jhove.messages.JhoveMessage; import edu.harvard.hul.ois.jhove.messages.JhoveMessages; - import java.io.DataInputStream; import java.io.IOException; /** * This class encapsulates an IFF/AIFF chunk header. - * + * * @author Gary McGath */ public class ChunkHeader { - private ModuleBase _module; - private RepInfo _repInfo; - private String _chunkId; // Four-character ID of the chunk - private long _offset; // Offset from the beginning of file - private long _size; // This does not include the 8 bytes of header - - /** - * Constructor. - * - * @param module The module under which the chunk is being read - * @param info The RepInfo object being used by the module - */ - public ChunkHeader(ModuleBase module, RepInfo info) - { - _module = module; - _repInfo = info; - } - - /** - * Reads and validates the header of a chunk. - * - * If {@code _chunkId} is non-null it's assumed to have already been read. - */ - public boolean readHeader(DataInputStream dstrm) throws IOException - { - final int LOWEST_PRINTABLE_ASCII = 32; - final int HIGHEST_PRINTABLE_ASCII = 126; - - _offset = _module.getNByte(); - - boolean idBeginsWithSpace = false; - boolean spacePrecedesPrintableCharacters = false; - StringBuilder id = new StringBuilder(Chunk.ID_LENGTH); - - for (int i = 0; i < Chunk.ID_LENGTH; i++) { - - boolean printableCharacter = false; - int ch = ModuleBase.readUnsignedByte(dstrm, _module); - - // Characters should be in the printable ASCII range - if (ch < LOWEST_PRINTABLE_ASCII || ch > HIGHEST_PRINTABLE_ASCII) { - _repInfo.setMessage(new ErrorMessage( - MessageConstants.IFF_HUL_1, - String.format( - MessageConstants.IFF_HUL_1_SUB.getMessage(), - ch), - _module.getNByte() - 1)); - _repInfo.setWellFormed(false); - return false; - } - - if (ch == ' ') { - if (i == 0) { - idBeginsWithSpace = true; - } - } else { - printableCharacter = true; - } - - if (idBeginsWithSpace && printableCharacter) { - spacePrecedesPrintableCharacters = true; - } - - id.append((char) ch); + private ModuleBase _module; + private RepInfo _repInfo; + private String _chunkId; // Four-character ID of the chunk + private long _offset; // Offset from the beginning of file + private long _size; // This does not include the 8 bytes of header + + /** + * Constructor. + * + * @param module The module under which the chunk is being read + * @param info The RepInfo object being used by the module + */ + public ChunkHeader(ModuleBase module, RepInfo info) { + _module = module; + _repInfo = info; + } + + /** + * Reads and validates the header of a chunk. + * + *

If {@code _chunkId} is non-null it's assumed to have already been read. + */ + public boolean readHeader(DataInputStream dstrm) throws IOException { + final int LOWEST_PRINTABLE_ASCII = 32; + final int HIGHEST_PRINTABLE_ASCII = 126; + + _offset = _module.getNByte(); + + boolean idBeginsWithSpace = false; + boolean spacePrecedesPrintableCharacters = false; + StringBuilder id = new StringBuilder(Chunk.ID_LENGTH); + + for (int i = 0; i < Chunk.ID_LENGTH; i++) { + + boolean printableCharacter = false; + int ch = ModuleBase.readUnsignedByte(dstrm, _module); + + // Characters should be in the printable ASCII range + if (ch < LOWEST_PRINTABLE_ASCII || ch > HIGHEST_PRINTABLE_ASCII) { + _repInfo.setMessage( + new ErrorMessage( + MessageConstants.IFF_HUL_1, + String.format(MessageConstants.IFF_HUL_1_SUB.getMessage(), ch), + _module.getNByte() - 1)); + _repInfo.setWellFormed(false); + return false; + } + + if (ch == ' ') { + if (i == 0) { + idBeginsWithSpace = true; } + } else { + printableCharacter = true; + } - _chunkId = id.toString(); + if (idBeginsWithSpace && printableCharacter) { + spacePrecedesPrintableCharacters = true; + } - // Spaces should not precede printable characters - if (spacePrecedesPrintableCharacters) { - JhoveMessage message = JhoveMessages.getMessageInstance( - MessageConstants.IFF_HUL_2.getId(), String.format( - MessageConstants.IFF_HUL_2.getMessage(), _chunkId)); - _repInfo.setMessage(new ErrorMessage(message, - _module.getNByte() - Chunk.ID_LENGTH)); - _repInfo.setValid(false); - } + id.append((char) ch); + } - _size = ModuleBase.readUnsignedInt(dstrm, _module.isBigEndian(), _module); + _chunkId = id.toString(); - return true; + // Spaces should not precede printable characters + if (spacePrecedesPrintableCharacters) { + JhoveMessage message = + JhoveMessages.getMessageInstance( + MessageConstants.IFF_HUL_2.getId(), + String.format(MessageConstants.IFF_HUL_2.getMessage(), _chunkId)); + _repInfo.setMessage(new ErrorMessage(message, _module.getNByte() - Chunk.ID_LENGTH)); + _repInfo.setValid(false); } + _size = ModuleBase.readUnsignedInt(dstrm, _module.isBigEndian(), _module); - /** Sets the chunk type, which is a 4-character code, directly. */ - public void setID(String id) - { - _chunkId = id; - } + return true; + } - /** Returns the chunk type, which is a 4-character code */ - public String getID() - { - return _chunkId; - } + /** Sets the chunk type, which is a 4-character code, directly. */ + public void setID(String id) { + _chunkId = id; + } - /** Sets the chunk size */ - public void setSize(long size) - { - _size = size; - } + /** Returns the chunk type, which is a 4-character code */ + public String getID() { + return _chunkId; + } - /** Returns the chunk size, which excludes the length of the header. */ - public long getSize() - { - return _size; - } + /** Sets the chunk size */ + public void setSize(long size) { + _size = size; + } - /** Returns the chunk offset in bytes from the beginning of file. */ - public long getOffset() - { - return _offset; - } + /** Returns the chunk size, which excludes the length of the header. */ + public long getSize() { + return _size; + } + + /** Returns the chunk offset in bytes from the beginning of file. */ + public long getOffset() { + return _offset; + } } diff --git a/jhove-modules/aiff-hul/src/main/java/edu/harvard/hul/ois/jhove/module/iff/MessageConstants.java b/jhove-modules/aiff-hul/src/main/java/edu/harvard/hul/ois/jhove/module/iff/MessageConstants.java index 022fcd650..0c9986ae1 100644 --- a/jhove-modules/aiff-hul/src/main/java/edu/harvard/hul/ois/jhove/module/iff/MessageConstants.java +++ b/jhove-modules/aiff-hul/src/main/java/edu/harvard/hul/ois/jhove/module/iff/MessageConstants.java @@ -5,18 +5,21 @@ import edu.harvard.hul.ois.jhove.messages.JhoveMessages; /** - * @author Carl Wilson - * carlwilson AT github - * + * @author Carl Wilson carlwilson AT github * @version 1.0 - * - * Created 31 Oct 2017:15:54:36 + *

Created 31 Oct 2017:15:54:36 */ public enum MessageConstants { - INSTANCE; - private static final JhoveMessageFactory messageFactory = JhoveMessages.getInstance("edu.harvard.hul.ois.jhove.module.iff.ErrorMessages"); //$NON-NLS-1$ + INSTANCE; + private static final JhoveMessageFactory messageFactory = + JhoveMessages.getInstance( + "edu.harvard.hul.ois.jhove.module.iff.ErrorMessages"); //$NON-NLS-1$ - public static final JhoveMessage IFF_HUL_1 = messageFactory.getMessage("IFF-HUL-1"); //$NON-NLS-1$ - public static final JhoveMessage IFF_HUL_1_SUB = messageFactory.getMessage("IFF-HUL-1-SUB"); //$NON-NLS-1$ - public static final JhoveMessage IFF_HUL_2 = messageFactory.getMessage("IFF-HUL-2"); //$NON-NLS-1$ + public static final JhoveMessage IFF_HUL_1 = + messageFactory.getMessage("IFF-HUL-1"); // $NON-NLS-1$ + public static final JhoveMessage IFF_HUL_1_SUB = + messageFactory.getMessage("IFF-HUL-1-SUB"); // $NON-NLS-1$ + public static final JhoveMessage IFF_HUL_2 = + messageFactory.getMessage("IFF-HUL-2"); // $NON-NLS-1$ } diff --git a/jhove-modules/aiff-hul/src/main/java/edu/harvard/hul/ois/jhove/module/iff/Superchunk.java b/jhove-modules/aiff-hul/src/main/java/edu/harvard/hul/ois/jhove/module/iff/Superchunk.java index 57c666363..4950b4cc7 100644 --- a/jhove-modules/aiff-hul/src/main/java/edu/harvard/hul/ois/jhove/module/iff/Superchunk.java +++ b/jhove-modules/aiff-hul/src/main/java/edu/harvard/hul/ois/jhove/module/iff/Superchunk.java @@ -1,60 +1,51 @@ -/********************************************************************** - * Jhove - JSTOR/Harvard Object Validation Environment - * Copyright 2004 by JSTOR and the President and Fellows of Harvard College - **********************************************************************/ - +/** + * ******************************************************************** Jhove - JSTOR/Harvard Object + * Validation Environment Copyright 2004 by JSTOR and the President and Fellows of Harvard College + * ******************************************************************** + */ package edu.harvard.hul.ois.jhove.module.iff; import edu.harvard.hul.ois.jhove.*; import java.io.*; /** - * Abstract class for a chunk that contains other chunks. - * It is assumed that the nested chunks come last in the chunk, - * so that once you start reading chunks, reaching the end of - * the superchunk is the indicator that there are no more chunks - * to read. + * Abstract class for a chunk that contains other chunks. It is assumed that the nested chunks come + * last in the chunk, so that once you start reading chunks, reaching the end of the superchunk is + * the indicator that there are no more chunks to read. * * @author Gary McGath - * */ public abstract class Superchunk extends Chunk { - private RepInfo _repInfo; - - /** - * Constructor. - * - * @param module The WaveModule under which this was called - * @param hdr The header for this chunk - * @param dstrm The stream from which the WAVE data are being read - * @param info RepInfo object for error reporting - */ - public Superchunk (ModuleBase module, - ChunkHeader hdr, - DataInputStream dstrm, - RepInfo info) - { - super (module, hdr, dstrm); - _repInfo = info; + private RepInfo _repInfo; + + /** + * Constructor. + * + * @param module The WaveModule under which this was called + * @param hdr The header for this chunk + * @param dstrm The stream from which the WAVE data are being read + * @param info RepInfo object for error reporting + */ + public Superchunk(ModuleBase module, ChunkHeader hdr, DataInputStream dstrm, RepInfo info) { + super(module, hdr, dstrm); + _repInfo = info; + } + + /** + * Reads and returns the next ChunkHeader within this Chunk, and takes care of byte counting. If + * this Chunk is exhausted, returns null. + */ + public ChunkHeader getNextChunkHeader() throws IOException { + if (bytesLeft <= 0) { + return null; } - - /** - * Reads and returns the next ChunkHeader within this Chunk, - * and takes care of byte counting. If this Chunk is exhausted, - * returns null. - */ - public ChunkHeader getNextChunkHeader () throws IOException - { - if (bytesLeft <= 0) { - return null; - } - ChunkHeader chunkh = new ChunkHeader (_module, _repInfo); - if (!chunkh.readHeader(_dstream)) { - return null; - } - int chunkSize = (int) chunkh.getSize (); - bytesLeft -= chunkSize + 8; - return chunkh; + ChunkHeader chunkh = new ChunkHeader(_module, _repInfo); + if (!chunkh.readHeader(_dstream)) { + return null; } + int chunkSize = (int) chunkh.getSize(); + bytesLeft -= chunkSize + 8; + return chunkh; + } } diff --git a/jhove-modules/aiff-hul/src/main/java/edu/harvard/hul/ois/jhove/module/iff/package-info.java b/jhove-modules/aiff-hul/src/main/java/edu/harvard/hul/ois/jhove/module/iff/package-info.java index f445751c5..f49f1fe05 100644 --- a/jhove-modules/aiff-hul/src/main/java/edu/harvard/hul/ois/jhove/module/iff/package-info.java +++ b/jhove-modules/aiff-hul/src/main/java/edu/harvard/hul/ois/jhove/module/iff/package-info.java @@ -1,7 +1,6 @@ /** - * Contains supporting classes for modules based on the IFF - * specification (EA IFF 85). This may include AIFF, RIFF and - * others. This package may also, at some point, include a generic - * IFF module. + * Contains supporting classes for modules based on the IFF specification (EA IFF 85). This may + * include AIFF, RIFF and others. This package may also, at some point, include a generic IFF + * module. */ -package edu.harvard.hul.ois.jhove.module.iff; \ No newline at end of file +package edu.harvard.hul.ois.jhove.module.iff; diff --git a/jhove-modules/ascii-hul/src/main/java/edu/harvard/hul/ois/jhove/module/AsciiModule.java b/jhove-modules/ascii-hul/src/main/java/edu/harvard/hul/ois/jhove/module/AsciiModule.java index f60c63537..c3d7ab5e3 100644 --- a/jhove-modules/ascii-hul/src/main/java/edu/harvard/hul/ois/jhove/module/AsciiModule.java +++ b/jhove-modules/ascii-hul/src/main/java/edu/harvard/hul/ois/jhove/module/AsciiModule.java @@ -1,22 +1,20 @@ -/********************************************************************** - * Jhove - JSTOR/Harvard Object Validation Environment Copyright 2003-2007 by - * JSTOR and the President and Fellows of Harvard College +/** + * ******************************************************************** Jhove - JSTOR/Harvard Object + * Validation Environment Copyright 2003-2007 by JSTOR and the President and Fellows of Harvard + * College * - * This program 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 2 of the License, or (at your option) any - * later version. + *

This program 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 2 + * of the License, or (at your option) any later version. * - * This program 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. + *

This program 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. * - * You should have received a copy of the GNU Lesser General Public License - * along with this program; if not, write to the Free Software Foundation, Inc., - * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - **********************************************************************/ - + *

You should have received a copy of the GNU Lesser General Public License along with this + * program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA ******************************************************************** + */ package edu.harvard.hul.ois.jhove.module; import edu.harvard.hul.ois.jhove.*; @@ -24,301 +22,317 @@ import edu.harvard.hul.ois.jhove.module.ascii.ControlChar; import edu.harvard.hul.ois.jhove.module.ascii.LineEnding; import edu.harvard.hul.ois.jhove.module.ascii.MessageConstants; - import java.io.*; import java.util.*; -/** - * Module for analysis of content as an ASCII stream. - */ +/** Module for analysis of content as an ASCII stream. */ public class AsciiModule extends ModuleBase { - public final static int MAX_CHAR = 0x7f; - public final static int MIN_PRINTABLE = 0x20; - public final static int MAX_PRINTABLE = 0x7e; - - /****************************************************************** - * PRIVATE CLASS FIELDS. - ******************************************************************/ - private static final String NAME = "ASCII-hul"; - private static final String RELEASE = "1.4.1"; - private static final int [] DATE = { 2019, 04, 17 }; - private static final String[] FORMAT = { "ASCII", "US-ASCII", "ANSI X3.4", - "ISO 646" }; - private static final String COVERAGE = null; - private static final String[] MIMETYPE = { "text/plain; charset=US-ASCII" }; - private static final String WELLFORMED = "An ASCII object is well-formed " - + "if each byte is between 0x00 and 0x7F"; - private static final String VALIDITY = null; - private static final String REPINFO = "Additional representation information includes: line ending and control characters"; - private static final String NOTE = null; - private static final String RIGHTS = "Copyright 2003-2015 by JSTOR and " - + "the President and Fellows of Harvard College. " - + "Copyright 2015-2019 by the Open Preservation Foundation. " - + "Version 1.4 onwards developed by Open Preservation Foundation. " - + "Released under the GNU Lesser General Public License."; - - /****************************************************************** - * PRIVATE INSTANCE FIELDS. - ******************************************************************/ - protected Set usedCtrlChars; - protected Set usedLineEndings; - - /* Flag to know if the property TextMDMetadata is to be added */ - protected boolean _withTextMD = false; - /* Hold the information needed to generate a textMD metadata fragment */ - protected TextMDMetadata _textMD; - - /****************************************************************** - * CLASS CONSTRUCTOR. - ******************************************************************/ - - /** - * Creates an AsciiModule. - */ - public AsciiModule() { - super(NAME, RELEASE, DATE, FORMAT, COVERAGE, MIMETYPE, WELLFORMED, - VALIDITY, REPINFO, NOTE, RIGHTS, false); - - _vendor = Agent.harvardInstance(); - - Document doc = new Document("Information technology -- ISO 7-bit " - + "coded character set for information " + "interchange", - DocumentType.STANDARD); - doc.setPublisher(Agent.newIsoInstance()); - doc.setDate("1991"); - doc.setIdentifier(new Identifier("ISO/IEC 646:1991", IdentifierType.ISO)); - _specification.add(doc); - - doc = new Document("Information Systems -- Coded Character Sets " + public static final int MAX_CHAR = 0x7f; + public static final int MIN_PRINTABLE = 0x20; + public static final int MAX_PRINTABLE = 0x7e; + + /** + * **************************************************************** PRIVATE CLASS FIELDS. + * **************************************************************** + */ + private static final String NAME = "ASCII-hul"; + + private static final String RELEASE = "1.4.1"; + private static final int[] DATE = {2019, 04, 17}; + private static final String[] FORMAT = {"ASCII", "US-ASCII", "ANSI X3.4", "ISO 646"}; + private static final String COVERAGE = null; + private static final String[] MIMETYPE = {"text/plain; charset=US-ASCII"}; + private static final String WELLFORMED = + "An ASCII object is well-formed " + "if each byte is between 0x00 and 0x7F"; + private static final String VALIDITY = null; + private static final String REPINFO = + "Additional representation information includes: line ending and control characters"; + private static final String NOTE = null; + private static final String RIGHTS = + "Copyright 2003-2015 by JSTOR and " + + "the President and Fellows of Harvard College. " + + "Copyright 2015-2019 by the Open Preservation Foundation. " + + "Version 1.4 onwards developed by Open Preservation Foundation. " + + "Released under the GNU Lesser General Public License."; + + /** + * **************************************************************** PRIVATE INSTANCE FIELDS. + * **************************************************************** + */ + protected Set usedCtrlChars; + + protected Set usedLineEndings; + + /* Flag to know if the property TextMDMetadata is to be added */ + protected boolean _withTextMD = false; + /* Hold the information needed to generate a textMD metadata fragment */ + protected TextMDMetadata _textMD; + + /** + * **************************************************************** CLASS CONSTRUCTOR. + * **************************************************************** + */ + + /** Creates an AsciiModule. */ + public AsciiModule() { + super( + NAME, + RELEASE, + DATE, + FORMAT, + COVERAGE, + MIMETYPE, + WELLFORMED, + VALIDITY, + REPINFO, + NOTE, + RIGHTS, + false); + + _vendor = Agent.harvardInstance(); + + Document doc = + new Document( + "Information technology -- ISO 7-bit " + + "coded character set for information " + + "interchange", + DocumentType.STANDARD); + doc.setPublisher(Agent.newIsoInstance()); + doc.setDate("1991"); + doc.setIdentifier(new Identifier("ISO/IEC 646:1991", IdentifierType.ISO)); + _specification.add(doc); + + doc = + new Document( + "Information Systems -- Coded Character Sets " + "7-Bit American National Standard Code for " + "Information Interchange (7-Bit ASCII)", - DocumentType.STANDARD); - Builder builder = new Agent.Builder("ANSI", AgentType.STANDARD) - .address("1819 L Street, NW, Washington, DC 20036") - .telephone("+1 (202) 293-8020").fax("+1 (202) 293-9287") - .email("info@ansi.org").web("http://www.ansi.org/"); - doc.setPublisher(builder.build()); - doc.setDate("1986-12-30"); - doc.setIdentifier(new Identifier("ANSI X3.4-1986", IdentifierType.ANSI)); - _specification.add(doc); - - doc = new Document("7-Bit coded Character Set", DocumentType.STANDARD); - doc.setEdition("6th"); - doc.setDate("1991-12"); - builder = new Agent.Builder("ECMA", AgentType.STANDARD) - .address("114 Rue du Rhone, CH-1204 Geneva, Switzerland") - .telephone("+41 22 849.60.00").fax("+41 22 849.60.01") - .email("helpdesk@ecma.ch") - .web("http://www.ecma-international.org/"); - doc.setPublisher(builder.build()); - doc.setIdentifier(new Identifier("ECMA-6", IdentifierType.ECMA)); - doc.setIdentifier(new Identifier("http://www.ecma-international." - + "org/publications/files/ecma-st/" + "Ecma-006.pdf", - IdentifierType.URL)); - _specification.add(doc); - } - - /****************************************************************** - * PUBLIC INSTANCE METHODS. - * - * Parsing methods. - ******************************************************************/ - - /** - * Parse the content of a stream digital object and store the results in - * RepInfo. - */ - @Override - public final int parse(InputStream stream, RepInfo info, int parseIndex) - throws IOException { - // Test if textMD is to be generated - _withTextMD = isParamInDefaults("withtextmd=true"); - - initParse(); - initInfo(info); - - ControlChar prevChar = null; - usedCtrlChars = new HashSet<>(); - usedLineEndings = new HashSet<>(); - _textMD = new TextMDMetadata(); - - boolean printableChars = false; - - - // Setup the data stream, will determine if we use checksum stream - setupDataStream(stream, info); - - boolean eof = false; - _nByte = 0; - while (!eof) { - try { - int ch = readUnsignedByte(_dstream, this); - - /* Only byte values 0x00 through 0x7f are valid. */ - if (ch > MAX_CHAR) { - ErrorMessage error = new ErrorMessage(MessageConstants.ASCII_HUL_1, - String.format(MessageConstants.ASCII_HUL_1_SUB.getMessage(), - Character.valueOf((char) ch), - Integer.valueOf(ch)), - _nByte - 1); - info.setMessage(error); - info.setWellFormed(RepInfo.FALSE); - return 0; - } - ControlChar ctrlChar = ControlChar.asciiFromInt(ch); - if (ControlChar.isLineEndChar(ctrlChar)) { - // Carry out the line endings test - LineEnding le = LineEnding.fromControlChars(ctrlChar, prevChar); - if (le != null) this.usedLineEndings.add(le); - } else if (ctrlChar != null) { - // The passed char is a control char and not a line ending - this.usedCtrlChars.add(ctrlChar); - } else if (!printableChars) { - // Only byte values 0x20 through 0x7e are printable. - printableChars = (MIN_PRINTABLE <= ch && ch <= MAX_PRINTABLE); - } - if (prevChar == ControlChar.CR && ctrlChar != ControlChar.LF) { - // Carry out the line endings test - LineEnding le = LineEnding.fromControlChars(ctrlChar, prevChar); - if (le != null) this.usedLineEndings.add(le); - } - prevChar = ctrlChar; - } catch (EOFException e) { - eof = true; - /* Catch line endings at very end. */ - LineEnding le = LineEnding.fromControlChars(ControlChar.NUL, prevChar); - if (le != null) usedLineEndings.add(le); - } - } - - /* The object is well-formed ASCII. */ - - // Set the checksums in the report if they're calculated - setChecksums(this._ckSummer, info); - - /* - * Only non-zero-length files are well-formed ASCII. - */ - if (_nByte == 0) { - info.setMessage(new ErrorMessage(MessageConstants.ASCII_HUL_2)); // ASCII-HUL-2 - info.setWellFormed(RepInfo.FALSE); - return 0; - } - - /* Add the textMD information */ - _textMD.setCharset(TextMDMetadata.CHARSET_ASCII); - _textMD.setByte_order(_bigEndian ? TextMDMetadata.BYTE_ORDER_BIG - : TextMDMetadata.BYTE_ORDER_LITTLE); - _textMD.setByte_size("8"); - _textMD.setCharacter_size("1"); - - /* - * Create a metadata property for the module-specific info. (4-Feb-04) - */ - List metadataList = new ArrayList<>(2); - - /* Set property reporting line ending type */ - List propArray = reportLineEndings(); - if (!propArray.isEmpty()) { - Property property = new Property(LineEnding.PROP_NAME, - PropertyType.STRING, PropertyArity.LIST, propArray); - metadataList.add(property); + DocumentType.STANDARD); + Builder builder = + new Agent.Builder("ANSI", AgentType.STANDARD) + .address("1819 L Street, NW, Washington, DC 20036") + .telephone("+1 (202) 293-8020") + .fax("+1 (202) 293-9287") + .email("info@ansi.org") + .web("http://www.ansi.org/"); + doc.setPublisher(builder.build()); + doc.setDate("1986-12-30"); + doc.setIdentifier(new Identifier("ANSI X3.4-1986", IdentifierType.ANSI)); + _specification.add(doc); + + doc = new Document("7-Bit coded Character Set", DocumentType.STANDARD); + doc.setEdition("6th"); + doc.setDate("1991-12"); + builder = + new Agent.Builder("ECMA", AgentType.STANDARD) + .address("114 Rue du Rhone, CH-1204 Geneva, Switzerland") + .telephone("+41 22 849.60.00") + .fax("+41 22 849.60.01") + .email("helpdesk@ecma.ch") + .web("http://www.ecma-international.org/"); + doc.setPublisher(builder.build()); + doc.setIdentifier(new Identifier("ECMA-6", IdentifierType.ECMA)); + doc.setIdentifier( + new Identifier( + "http://www.ecma-international." + "org/publications/files/ecma-st/" + "Ecma-006.pdf", + IdentifierType.URL)); + _specification.add(doc); + } + + /** + * **************************************************************** PUBLIC INSTANCE METHODS. + * + *

Parsing methods. **************************************************************** + */ + + /** Parse the content of a stream digital object and store the results in RepInfo. */ + @Override + public final int parse(InputStream stream, RepInfo info, int parseIndex) throws IOException { + // Test if textMD is to be generated + _withTextMD = isParamInDefaults("withtextmd=true"); + + initParse(); + initInfo(info); + + ControlChar prevChar = null; + usedCtrlChars = new HashSet<>(); + usedLineEndings = new HashSet<>(); + _textMD = new TextMDMetadata(); + + boolean printableChars = false; + + // Setup the data stream, will determine if we use checksum stream + setupDataStream(stream, info); + + boolean eof = false; + _nByte = 0; + while (!eof) { + try { + int ch = readUnsignedByte(_dstream, this); + + /* Only byte values 0x00 through 0x7f are valid. */ + if (ch > MAX_CHAR) { + ErrorMessage error = + new ErrorMessage( + MessageConstants.ASCII_HUL_1, + String.format( + MessageConstants.ASCII_HUL_1_SUB.getMessage(), + Character.valueOf((char) ch), + Integer.valueOf(ch)), + _nByte - 1); + info.setMessage(error); + info.setWellFormed(RepInfo.FALSE); + return 0; } - /* Set property reporting control characters used */ - if (!this.usedCtrlChars.isEmpty()) { - LinkedList propList = new LinkedList<>(); - for (ControlChar ctrlChar : EnumSet.copyOf(this.usedCtrlChars)) { - propList.add(ctrlChar.mnemonic); - } - Property property = new Property(ControlChar.PROP_NAME, - PropertyType.STRING, PropertyArity.LIST, propList); - metadataList.add(property); + ControlChar ctrlChar = ControlChar.asciiFromInt(ch); + if (ControlChar.isLineEndChar(ctrlChar)) { + // Carry out the line endings test + LineEnding le = LineEnding.fromControlChars(ctrlChar, prevChar); + if (le != null) this.usedLineEndings.add(le); + } else if (ctrlChar != null) { + // The passed char is a control char and not a line ending + this.usedCtrlChars.add(ctrlChar); + } else if (!printableChars) { + // Only byte values 0x20 through 0x7e are printable. + printableChars = (MIN_PRINTABLE <= ch && ch <= MAX_PRINTABLE); } - - if (_withTextMD) { - Property property = new Property("TextMDMetadata", - PropertyType.TEXTMDMETADATA, PropertyArity.SCALAR, _textMD); - metadataList.add(property); + if (prevChar == ControlChar.CR && ctrlChar != ControlChar.LF) { + // Carry out the line endings test + LineEnding le = LineEnding.fromControlChars(ctrlChar, prevChar); + if (le != null) this.usedLineEndings.add(le); } + prevChar = ctrlChar; + } catch (EOFException e) { + eof = true; + /* Catch line endings at very end. */ + LineEnding le = LineEnding.fromControlChars(ControlChar.NUL, prevChar); + if (le != null) usedLineEndings.add(le); + } + } - /* Add the ASCII-specific metadata, if it exists. */ - if (!metadataList.isEmpty()) { - info.setProperty(new Property("ASCIIMetadata", - PropertyType.PROPERTY, PropertyArity.LIST, metadataList)); - } + /* The object is well-formed ASCII. */ - if (!printableChars) { - info.setMessage(new InfoMessage(MessageConstants.ASCII_HUL_3)); - } + // Set the checksums in the report if they're calculated + setChecksums(this._ckSummer, info); - return 0; + /* + * Only non-zero-length files are well-formed ASCII. + */ + if (_nByte == 0) { + info.setMessage(new ErrorMessage(MessageConstants.ASCII_HUL_2)); // ASCII-HUL-2 + info.setWellFormed(RepInfo.FALSE); + return 0; } - /** - * Check if the digital object conforms to this Module's internal signature - * information. An ASCII file has no "signature," so in cases like this we - * just check the beginning of the file as a plausible guess. This really - * proves nothing, since a text file could have a single accented character - * dozens of kilobytes into it. But oh well. - * - * @param file - * A File object for the object being parsed - * @param stream - * An InputStream, positioned at its beginning, which is - * generated from the object to be parsed - * @param info - * A fresh RepInfo object which will be modified to reflect the - * results of the test + /* Add the textMD information */ + _textMD.setCharset(TextMDMetadata.CHARSET_ASCII); + _textMD.setByte_order( + _bigEndian ? TextMDMetadata.BYTE_ORDER_BIG : TextMDMetadata.BYTE_ORDER_LITTLE); + _textMD.setByte_size("8"); + _textMD.setCharacter_size("1"); + + /* + * Create a metadata property for the module-specific info. (4-Feb-04) */ - @Override - public void checkSignatures(File file, InputStream stream, RepInfo info) - throws IOException { - info.setFormat(_format[0]); - info.setMimeType(_mimeType[0]); - info.setModule(this); - int sigBytes = getBase().getSigBytes(); - int bytesRead = 0; - boolean eof = false; - DataInputStream dstream = new DataInputStream(stream); - while (!eof && bytesRead < sigBytes) { - try { - int ch = readUnsignedByte(dstream, this); - ++bytesRead; - - /* Only byte values 0x00 through 0x7f are valid. */ - - if (ch > MAX_CHAR) { - info.setWellFormed(false); - return; - } - } catch (EOFException e) { - eof = true; - } - } - // Reject an empty file. - if (bytesRead == 0) { - info.setWellFormed(false); - return; - } - // Do this only after being sure it's OK, as this property - // is sticky. - info.setSigMatch(_name); + List metadataList = new ArrayList<>(2); + /* Set property reporting line ending type */ + List propArray = reportLineEndings(); + if (!propArray.isEmpty()) { + Property property = + new Property(LineEnding.PROP_NAME, PropertyType.STRING, PropertyArity.LIST, propArray); + metadataList.add(property); + } + /* Set property reporting control characters used */ + if (!this.usedCtrlChars.isEmpty()) { + LinkedList propList = new LinkedList<>(); + for (ControlChar ctrlChar : EnumSet.copyOf(this.usedCtrlChars)) { + propList.add(ctrlChar.mnemonic); + } + Property property = + new Property(ControlChar.PROP_NAME, PropertyType.STRING, PropertyArity.LIST, propList); + metadataList.add(property); } - /****************************************************************** - * PRIVATE INSTANCE METHODS. - ******************************************************************/ + if (_withTextMD) { + Property property = + new Property( + "TextMDMetadata", PropertyType.TEXTMDMETADATA, PropertyArity.SCALAR, _textMD); + metadataList.add(property); + } - /* Set property reporting line ending type */ - private List reportLineEndings() { - List retVal = new ArrayList<>(); - if (!this.usedLineEndings.isEmpty()) { - for (LineEnding le : EnumSet.copyOf(this.usedLineEndings)) { - retVal.add(le.toString()); - _textMD.setLinebreak(le.textMdVal); - } + /* Add the ASCII-specific metadata, if it exists. */ + if (!metadataList.isEmpty()) { + info.setProperty( + new Property("ASCIIMetadata", PropertyType.PROPERTY, PropertyArity.LIST, metadataList)); + } + + if (!printableChars) { + info.setMessage(new InfoMessage(MessageConstants.ASCII_HUL_3)); + } + + return 0; + } + + /** + * Check if the digital object conforms to this Module's internal signature information. An ASCII + * file has no "signature," so in cases like this we just check the beginning of the file as a + * plausible guess. This really proves nothing, since a text file could have a single accented + * character dozens of kilobytes into it. But oh well. + * + * @param file A File object for the object being parsed + * @param stream An InputStream, positioned at its beginning, which is generated from the object + * to be parsed + * @param info A fresh RepInfo object which will be modified to reflect the results of the test + */ + @Override + public void checkSignatures(File file, InputStream stream, RepInfo info) throws IOException { + info.setFormat(_format[0]); + info.setMimeType(_mimeType[0]); + info.setModule(this); + int sigBytes = getBase().getSigBytes(); + int bytesRead = 0; + boolean eof = false; + DataInputStream dstream = new DataInputStream(stream); + while (!eof && bytesRead < sigBytes) { + try { + int ch = readUnsignedByte(dstream, this); + ++bytesRead; + + /* Only byte values 0x00 through 0x7f are valid. */ + + if (ch > MAX_CHAR) { + info.setWellFormed(false); + return; } - return retVal; + } catch (EOFException e) { + eof = true; + } + } + // Reject an empty file. + if (bytesRead == 0) { + info.setWellFormed(false); + return; + } + // Do this only after being sure it's OK, as this property + // is sticky. + info.setSigMatch(_name); + } + + /** + * **************************************************************** PRIVATE INSTANCE METHODS. + * **************************************************************** + */ + + /* Set property reporting line ending type */ + private List reportLineEndings() { + List retVal = new ArrayList<>(); + if (!this.usedLineEndings.isEmpty()) { + for (LineEnding le : EnumSet.copyOf(this.usedLineEndings)) { + retVal.add(le.toString()); + _textMD.setLinebreak(le.textMdVal); + } } + return retVal; + } } diff --git a/jhove-modules/ascii-hul/src/main/java/edu/harvard/hul/ois/jhove/module/ascii/ControlChar.java b/jhove-modules/ascii-hul/src/main/java/edu/harvard/hul/ois/jhove/module/ascii/ControlChar.java index d936aa64b..f2dccc690 100644 --- a/jhove-modules/ascii-hul/src/main/java/edu/harvard/hul/ois/jhove/module/ascii/ControlChar.java +++ b/jhove-modules/ascii-hul/src/main/java/edu/harvard/hul/ois/jhove/module/ascii/ControlChar.java @@ -3,148 +3,138 @@ import java.util.EnumSet; /** - * @author Carl Wilson - * carlwilson AT github + * @author Carl Wilson carlwilson AT github */ @SuppressWarnings("nls") public enum ControlChar { - /** - * 7 bit ASCII 0x00 - 0x1F + 0x7F - */ - NUL("NUL", 0x00, "NULL"), - SOH("SOH", 0x01, "START OF HEADING"), - STX("STX", 0x02, "START OF TEXT"), - ETX("ETX", 0x03, "END OF TEXT"), - EOT("EOT", 0x04, "END OF TRANSMISSION"), - ENQ("ENQ", 0x05, "ENQUIRY"), - ACK("ACK", 0x06, "ACKNOWLEDGE"), - BEL("BEL", 0x07, "BELL"), - BS("BS", 0x08, "BACKSPACE"), - HT("HT", 0x09, "HORIZONTAL TABULATION"), - LF("LF", 0x0A, "LINE FEED"), - VT("VT", 0x0B, "VERTICAL TABULATION"), - FF("FF", 0x0C, "FORM FEED"), - CR("CR", 0x0D, "CARRIAGE RETURN"), - SO("SO", 0x0E, "SHIFT-OUT"), - SI("SI", 0x0F, "SHIFT-IN"), - DLE("DLE", 0x10, "DATA LINK ESCAPE"), - DC1("DC1", 0x11, "DEVICE CONTROL ONE"), - DC2("DC2", 0x12, "DEVICE CONTROL TWO"), - DC3("DC3", 0x13, "DEVICE CONTROL THREE"), - DC4("DC4", 0x14, "DEVICE CONTROL FOUR"), - NAK("NAK", 0x15, "NEGATIVE ACKNOWLEDGE"), - SYN("SYN", 0x16, "SYNCHRONOUS IDLE"), - ETB("ETB", 0x17, "END OF TRANSMISSION BLOCK"), - CAN("CAN", 0x18, "CANCEL"), - EM("EM", 0x19, "END OF MEDIUM"), - SUB("SUB", 0x1A, "SUBSTITUTE CHARACTER"), - ESC("ESC", 0x1B, "ESCAPE"), - FS("FS", 0x1C, "FILE SEPARATOR (INFORMATION SEPARATOR FOUR"), - GS("GS", 0x1D, "GROUP SEPARATOR (INFORMATION SEPARATOR THREE"), - RS("RS", 0x1E, "RECORD SEPARATOR (INFORMATION SEPARATOR TWO"), - US("US", 0x1F, "UNIT SEPARATOR (INFORMATION SEPARATOR ONE"), - DEL("DEL", 0x7F, "DELETE"), - /** - * Unicode Extensions 0x80 - 0x9F - * Currently not used - */ - PAD("PAD", 0X80, "PADDING CHARACTER"), - HOP("HOP", 0X81, "HIGH OCTET PRESET"), - BPH("BPH", 0X82, "BREAK PERMITTED HERE"), - NBH("NBH", 0X83, "NO BREAK HERE"), - IND("IND", 0X84, "INDEX"), - NEL("NEL", 0X85, "NEXT LINE"), - SSA("SSA", 0X86, "START OF SELECTED AREA"), - ESA("ESA", 0X87, "END OF SELECTED AREA"), - HTS("HTS", 0X88, "HORIZONTAL TAB SET"), - HTJ("HTJ", 0X89, "HORIZONTAL TAB JUSTIFIED"), - VTS("VTS", 0X8A, "VERTICAL TAB SET"), - PLD("PLD", 0X8B, "PARTIAL LINE FORWARD"), - PLU("PLU", 0X8C, "PARTIAL LINE BACKWARD"), - RI("RI", 0X8D, "REVERSE LINE FEED"), - SS2("SS2", 0X8E, "SINGLE-SHIFT 2"), - SS3("SS3", 0X8F, "SINGLE-SHIFT 2"), - DCS("DCS", 0X90, "DEVICE CONTROL STRING"), - PU1("PU1", 0X91, "PRIVATE USE 1"), - PU2("PU2", 0X92, "PRIVATE USE 2"), - STS("STS", 0X93, "SET TRANSMIT STATE"), - CCH("CCH", 0X94, "CANCEL CHARACTER"), - MW("MW", 0X95, "MESSAGE WAITING"), - SPA("SPA", 0X96, "START OF PROTECTED AREA"), - EPA("EPA", 0X97, "ENDO OF PROTECTED AREA"), - SOS("SOS", 0X98, "START OF STRING"), - SGCI("SCGI", 0X99, "SINGLE GRAPHIC CHAR INTRO"), - SCI("SCI", 0X9A, "SINGLE CHAR INTRO"), - CSI("CSI", 0X9B, "CONTROL SEQUENCE INTRO"), - ST("ST", 0X9C, "STRING TERMINATOR"), - OSC("OSC", 0X9D, "OS COMMAND"), - PM("PM", 0X9E, "PRIVATE MESSAGE"), - APC("APC", 0X9F, "APP PROGRAM COMMAND"); + /** 7 bit ASCII 0x00 - 0x1F + 0x7F */ + NUL("NUL", 0x00, "NULL"), + SOH("SOH", 0x01, "START OF HEADING"), + STX("STX", 0x02, "START OF TEXT"), + ETX("ETX", 0x03, "END OF TEXT"), + EOT("EOT", 0x04, "END OF TRANSMISSION"), + ENQ("ENQ", 0x05, "ENQUIRY"), + ACK("ACK", 0x06, "ACKNOWLEDGE"), + BEL("BEL", 0x07, "BELL"), + BS("BS", 0x08, "BACKSPACE"), + HT("HT", 0x09, "HORIZONTAL TABULATION"), + LF("LF", 0x0A, "LINE FEED"), + VT("VT", 0x0B, "VERTICAL TABULATION"), + FF("FF", 0x0C, "FORM FEED"), + CR("CR", 0x0D, "CARRIAGE RETURN"), + SO("SO", 0x0E, "SHIFT-OUT"), + SI("SI", 0x0F, "SHIFT-IN"), + DLE("DLE", 0x10, "DATA LINK ESCAPE"), + DC1("DC1", 0x11, "DEVICE CONTROL ONE"), + DC2("DC2", 0x12, "DEVICE CONTROL TWO"), + DC3("DC3", 0x13, "DEVICE CONTROL THREE"), + DC4("DC4", 0x14, "DEVICE CONTROL FOUR"), + NAK("NAK", 0x15, "NEGATIVE ACKNOWLEDGE"), + SYN("SYN", 0x16, "SYNCHRONOUS IDLE"), + ETB("ETB", 0x17, "END OF TRANSMISSION BLOCK"), + CAN("CAN", 0x18, "CANCEL"), + EM("EM", 0x19, "END OF MEDIUM"), + SUB("SUB", 0x1A, "SUBSTITUTE CHARACTER"), + ESC("ESC", 0x1B, "ESCAPE"), + FS("FS", 0x1C, "FILE SEPARATOR (INFORMATION SEPARATOR FOUR"), + GS("GS", 0x1D, "GROUP SEPARATOR (INFORMATION SEPARATOR THREE"), + RS("RS", 0x1E, "RECORD SEPARATOR (INFORMATION SEPARATOR TWO"), + US("US", 0x1F, "UNIT SEPARATOR (INFORMATION SEPARATOR ONE"), + DEL("DEL", 0x7F, "DELETE"), + /** Unicode Extensions 0x80 - 0x9F Currently not used */ + PAD("PAD", 0X80, "PADDING CHARACTER"), + HOP("HOP", 0X81, "HIGH OCTET PRESET"), + BPH("BPH", 0X82, "BREAK PERMITTED HERE"), + NBH("NBH", 0X83, "NO BREAK HERE"), + IND("IND", 0X84, "INDEX"), + NEL("NEL", 0X85, "NEXT LINE"), + SSA("SSA", 0X86, "START OF SELECTED AREA"), + ESA("ESA", 0X87, "END OF SELECTED AREA"), + HTS("HTS", 0X88, "HORIZONTAL TAB SET"), + HTJ("HTJ", 0X89, "HORIZONTAL TAB JUSTIFIED"), + VTS("VTS", 0X8A, "VERTICAL TAB SET"), + PLD("PLD", 0X8B, "PARTIAL LINE FORWARD"), + PLU("PLU", 0X8C, "PARTIAL LINE BACKWARD"), + RI("RI", 0X8D, "REVERSE LINE FEED"), + SS2("SS2", 0X8E, "SINGLE-SHIFT 2"), + SS3("SS3", 0X8F, "SINGLE-SHIFT 2"), + DCS("DCS", 0X90, "DEVICE CONTROL STRING"), + PU1("PU1", 0X91, "PRIVATE USE 1"), + PU2("PU2", 0X92, "PRIVATE USE 2"), + STS("STS", 0X93, "SET TRANSMIT STATE"), + CCH("CCH", 0X94, "CANCEL CHARACTER"), + MW("MW", 0X95, "MESSAGE WAITING"), + SPA("SPA", 0X96, "START OF PROTECTED AREA"), + EPA("EPA", 0X97, "ENDO OF PROTECTED AREA"), + SOS("SOS", 0X98, "START OF STRING"), + SGCI("SCGI", 0X99, "SINGLE GRAPHIC CHAR INTRO"), + SCI("SCI", 0X9A, "SINGLE CHAR INTRO"), + CSI("CSI", 0X9B, "CONTROL SEQUENCE INTRO"), + ST("ST", 0X9C, "STRING TERMINATOR"), + OSC("OSC", 0X9D, "OS COMMAND"), + PM("PM", 0X9E, "PRIVATE MESSAGE"), + APC("APC", 0X9F, "APP PROGRAM COMMAND"); - /** JHOVE reporting property name */ - public final static String PROP_NAME = "ControlCharacters"; - /** Set of ASCII 7 bit Control Chars */ - public final static EnumSet ASCII = EnumSet.range(NUL, DEL); - /** Set of Control Chars that are only Unicode */ - public final static EnumSet UNICODE_EXTENSIONS = EnumSet.complementOf(ASCII); - /** Set of Unicode Control Chars, {@code ASCII} + {@code UNICODE_EXTENSIONS} */ - public final static EnumSet UNICODE = EnumSet.allOf(ControlChar.class); + /** JHOVE reporting property name */ + public static final String PROP_NAME = "ControlCharacters"; + /** Set of ASCII 7 bit Control Chars */ + public static final EnumSet ASCII = EnumSet.range(NUL, DEL); + /** Set of Control Chars that are only Unicode */ + public static final EnumSet UNICODE_EXTENSIONS = EnumSet.complementOf(ASCII); + /** Set of Unicode Control Chars, {@code ASCII} + {@code UNICODE_EXTENSIONS} */ + public static final EnumSet UNICODE = EnumSet.allOf(ControlChar.class); + /** The three character code */ + public final String code; + /** The byte value of the character. * */ + public final int value; + /** The control character's ANSI name * */ + public final String ansiName; - /** The three character code */ - public final String code; - /** The byte value of the character. **/ - public final int value; - /** The control character's ANSI name **/ - public final String ansiName; - - /** - * JHOVE's reporting mnemonic, the code followed by the int value in hex. - **/ - public final String mnemonic; + /** JHOVE's reporting mnemonic, the code followed by the int value in hex. */ + public final String mnemonic; - private ControlChar(final String code, final int value, final String ansiName) { - this.code = code; - this.value = value; - this.ansiName = ansiName; - this.mnemonic = String.format("%s (0x%02X)", code, value); - } + private ControlChar(final String code, final int value, final String ansiName) { + this.code = code; + this.value = value; + this.ansiName = ansiName; + this.mnemonic = String.format("%s (0x%02X)", code, value); + } - /** - * Tests whether the passed ControlChar toTest is a line ending character. - * - * @param toTest - * the ControlChar to test - * @return true if toTest is a CR or LF character. - */ - public final static boolean isLineEndChar(final ControlChar toTest) { - return (toTest == ControlChar.CR || toTest == ControlChar.LF); - } + /** + * Tests whether the passed ControlChar toTest is a line ending character. + * + * @param toTest the ControlChar to test + * @return true if toTest is a CR or LF character. + */ + public static final boolean isLineEndChar(final ControlChar toTest) { + return (toTest == ControlChar.CR || toTest == ControlChar.LF); + } - /** - * Returns the appropriate ASCII control character for an int character value. - * - * @param charVal - * the int value of a possible ASCII character - * @return the control character with matching ASCII value or null - * if the value is out of the ASCII control character range. - */ - public final static ControlChar asciiFromInt(final int charVal) { - if (charVal > US.value) { - return (charVal == DEL.value) ? DEL : null; - } - for (ControlChar ctrlChar : ControlChar.values()) { - if (ctrlChar.value == charVal) return ctrlChar; - } - // Must be a negative charVal - return null; - } + /** + * Returns the appropriate ASCII control character for an int character value. + * + * @param charVal the int value of a possible ASCII character + * @return the control character with matching ASCII value or null if the value is out of the + * ASCII control character range. + */ + public static final ControlChar asciiFromInt(final int charVal) { + if (charVal > US.value) { + return (charVal == DEL.value) ? DEL : null; + } + for (ControlChar ctrlChar : ControlChar.values()) { + if (ctrlChar.value == charVal) return ctrlChar; + } + // Must be a negative charVal + return null; + } - public final static ControlChar fromMnemonic(final String mnemonic) { - for (ControlChar ctrlChar : ControlChar.values()) { - if (ctrlChar.mnemonic.equalsIgnoreCase(mnemonic)) return ctrlChar; - } - // Must be a negative charVal - return null; - } + public static final ControlChar fromMnemonic(final String mnemonic) { + for (ControlChar ctrlChar : ControlChar.values()) { + if (ctrlChar.mnemonic.equalsIgnoreCase(mnemonic)) return ctrlChar; + } + // Must be a negative charVal + return null; + } } diff --git a/jhove-modules/ascii-hul/src/main/java/edu/harvard/hul/ois/jhove/module/ascii/LineEnding.java b/jhove-modules/ascii-hul/src/main/java/edu/harvard/hul/ois/jhove/module/ascii/LineEnding.java index a38c85991..41b917595 100644 --- a/jhove-modules/ascii-hul/src/main/java/edu/harvard/hul/ois/jhove/module/ascii/LineEnding.java +++ b/jhove-modules/ascii-hul/src/main/java/edu/harvard/hul/ois/jhove/module/ascii/LineEnding.java @@ -3,48 +3,46 @@ import edu.harvard.hul.ois.jhove.TextMDMetadata; /** - * @author Carl Wilson - * carlwilson AT github + * @author Carl Wilson carlwilson AT github */ - public enum LineEnding { - /** Carriage Return */ - CR(TextMDMetadata.LINEBREAK_CR), - /** Line Feed */ - LF(TextMDMetadata.LINEBREAK_LF), - /** Carriage Return / Line Feed */ - CRLF(TextMDMetadata.LINEBREAK_CRLF); + /** Carriage Return */ + CR(TextMDMetadata.LINEBREAK_CR), + /** Line Feed */ + LF(TextMDMetadata.LINEBREAK_LF), + /** Carriage Return / Line Feed */ + CRLF(TextMDMetadata.LINEBREAK_CRLF); - /** JHOVE reporting property name */ - public static final String PROP_NAME = "LineEndings"; + /** JHOVE reporting property name */ + public static final String PROP_NAME = "LineEndings"; - /** The TextMD line-break type, an int index to a message array */ - public final int textMdVal; + /** The TextMD line-break type, an int index to a message array */ + public final int textMdVal; - private LineEnding(final int textMdVal) { - this.textMdVal = textMdVal; - } + private LineEnding(final int textMdVal) { + this.textMdVal = textMdVal; + } - /** - * Performs JHOVE's line ending type test, using the last 2 read characters. - * - * @param lastChar - * the last character read from stream - * @param prevChar - * the previous character to the lastChar - * @return the appropriate line ending type, or null if the character's - * aren't a line ending combination - */ - public static LineEnding fromControlChars(final ControlChar lastChar, final ControlChar prevChar) { - if (lastChar == ControlChar.LF) { - if (prevChar == ControlChar.CR) { - return CRLF; - } - return LF; - } - if (prevChar == ControlChar.CR) { - return CR; - } - return null; - } + /** + * Performs JHOVE's line ending type test, using the last 2 read characters. + * + * @param lastChar the last character read from stream + * @param prevChar the previous character to the lastChar + * @return the appropriate line ending type, or null if the character's aren't a line ending + * combination + */ + public static LineEnding fromControlChars( + final ControlChar lastChar, final ControlChar prevChar) { + if (lastChar == ControlChar.LF) { + if (prevChar == ControlChar.CR) { + return CRLF; + } + return LF; + } + if (prevChar == ControlChar.CR) { + return CR; + } + return null; + } } diff --git a/jhove-modules/ascii-hul/src/main/java/edu/harvard/hul/ois/jhove/module/ascii/MessageConstants.java b/jhove-modules/ascii-hul/src/main/java/edu/harvard/hul/ois/jhove/module/ascii/MessageConstants.java index 59de8f883..5d402e2bf 100644 --- a/jhove-modules/ascii-hul/src/main/java/edu/harvard/hul/ois/jhove/module/ascii/MessageConstants.java +++ b/jhove-modules/ascii-hul/src/main/java/edu/harvard/hul/ois/jhove/module/ascii/MessageConstants.java @@ -5,22 +5,16 @@ import edu.harvard.hul.ois.jhove.messages.JhoveMessages; /** - * @author Carl Wilson - * carlwilson AT github + * @author Carl Wilson carlwilson AT github */ - public enum MessageConstants { - INSTANCE; - private static JhoveMessageFactory messageFactory = JhoveMessages - .getInstance( - "edu.harvard.hul.ois.jhove.module.ascii.ErrorMessages"); + INSTANCE; + private static JhoveMessageFactory messageFactory = + JhoveMessages.getInstance("edu.harvard.hul.ois.jhove.module.ascii.ErrorMessages"); - public static final JhoveMessage ASCII_HUL_1 = messageFactory - .getMessage("ASCII-HUL-1"); - public static final JhoveMessage ASCII_HUL_1_SUB = messageFactory - .getMessage("ASCII-HUL-1-SUB"); - public static final JhoveMessage ASCII_HUL_2 = messageFactory - .getMessage("ASCII-HUL-2"); - public static final JhoveMessage ASCII_HUL_3 = messageFactory - .getMessage("ASCII-HUL-3"); + public static final JhoveMessage ASCII_HUL_1 = messageFactory.getMessage("ASCII-HUL-1"); + public static final JhoveMessage ASCII_HUL_1_SUB = messageFactory.getMessage("ASCII-HUL-1-SUB"); + public static final JhoveMessage ASCII_HUL_2 = messageFactory.getMessage("ASCII-HUL-2"); + public static final JhoveMessage ASCII_HUL_3 = messageFactory.getMessage("ASCII-HUL-3"); } diff --git a/jhove-modules/ascii-hul/src/test/java/edu/harvard/hul/ois/jhove/module/TestUtils.java b/jhove-modules/ascii-hul/src/test/java/edu/harvard/hul/ois/jhove/module/TestUtils.java index f1b420272..eb4682783 100644 --- a/jhove-modules/ascii-hul/src/test/java/edu/harvard/hul/ois/jhove/module/TestUtils.java +++ b/jhove-modules/ascii-hul/src/test/java/edu/harvard/hul/ois/jhove/module/TestUtils.java @@ -1,5 +1,10 @@ package edu.harvard.hul.ois.jhove.module; +import static org.junit.Assert.*; + +import edu.harvard.hul.ois.jhove.Message; +import edu.harvard.hul.ois.jhove.Module; +import edu.harvard.hul.ois.jhove.RepInfo; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; @@ -8,233 +13,216 @@ import java.io.RandomAccessFile; import java.net.URISyntaxException; -import edu.harvard.hul.ois.jhove.Message; -import edu.harvard.hul.ois.jhove.Module; -import edu.harvard.hul.ois.jhove.RepInfo; - -import static org.junit.Assert.*; - /** - * Convenience methods to test that the result of JHOVE validation are as - * expected. The tests are: + * Convenience methods to test that the result of JHOVE validation are as expected. The tests are: + * *

    - *
  • The well formed result is equal to a pre-defined value.
  • - *
  • The is valid result is equal to a pre-defined value.
  • - *
  • The message list contains an expected message (optionally).
  • + *
  • The well formed result is equal to a pre-defined value. + *
  • The is valid result is equal to a pre-defined value. + *
  • The message list contains an expected message (optionally). *
* - * @author Carl Wilson - * carlwilson AT github + * @author Carl Wilson carlwilson AT github * @version 0.1 Created 14 Mar 2018:20:14:19 */ public final class TestUtils { - public static final String MODULE_RESOURCE_BASE = "/edu/harvard/hul/ois/jhove/module/"; - public static final String EMPTY_FILE_PATH = MODULE_RESOURCE_BASE + "empty"; - - private TestUtils() { - // Keep out - throw new AssertionError("Should never be in constructor."); - } - - /** - * Convenience method that takes the path to a test resource and tests that - * the results of JHOVE are as expected. - * - * @param module - * a {@link Module} instance to validate the resource. - * @param resToTest - * the String path of the resource to validate and test. - * @param expctWllFrmd - * the expected well formed value - * @param expctVld - * the expected is valid value - * @throws URISyntaxException - * when there's an issue converting the resource name to a path - */ - public static RepInfo testValidateResource(final Module module, - final String resToTest, final int expctWllFrmd, final int expctVld) throws URISyntaxException { - return testValidateResource(module, resToTest, expctWllFrmd, expctVld, null); - } - - /** - * - * @param module - * a {@link edu.harvard.hul.ois.jhove.Module} instance - * to use to validate the resource. - * @param resToTest - * the String path of the resource to validate and test. - * @param expctWllFrmd - * the expected well formed value - * @param expctVld - * the expected is valid value - * @param message - * a JHOVE validation string message expected to be found in the - * list of validation messages. If this parameter is null the - * test isn't performed. - * @throws URISyntaxException - * when there's an issue converting the resource name to a path - */ - public static RepInfo testValidateResource(final Module module, - final String resToTest, final int expctWllFrmd, final int expctVld, - final String message) throws URISyntaxException { - return testValidateResource(module, resToTest, expctWllFrmd, expctVld, message, true); - } - - /** - * - * @param module - * a {@link Module} instance to validate the resource. - * @param resToTest - * the String path of the resource to validate and test. - * @param expctWllFrmd - * the expected well formed value - * @param expctVld - * the expected is valid value - * @param expctMessage - * a JHOVE validation string message which MUST be found in the - * list of validation messages if messMustBePresent is true. - * When messMustBePresent is false the message MUST NOT be - * found in the list of validation messages. - * If this parameter is null the test isn't performed. - * @param messMustBePresent - * if message is not null this param dictates whether the - * test expects the validation message to be in the report or not. - * @throws URISyntaxException - * when there's an issue converting the resource name to a path - */ - public static RepInfo testValidateResource(final Module module, - final String resToTest, final int expctWllFrmd, final int expctVld, - final String expctMessage, boolean messMustBePresent ) throws URISyntaxException { - File toTest = new File(TestUtils.class.getResource(resToTest).toURI()); - - return testValidateFile(module, toTest, expctWllFrmd, expctVld, - expctMessage, messMustBePresent); - } - - /** - * Method that takes a file and tests that the results of JHOVE are as - * expected. - * - * @param module - * a {@link Module} instance to validate the resource. - * @param fileToTest - * a Java File instance to validate - * @param expctWllFrmd - * the expected well-formed value - * @param expctVld - * the expected is valid value - */ - public static RepInfo testValidateFile(final Module module, - final File fileToTest, final int expctWllFrmd, final int expctVld) { - return testValidateFile(module, fileToTest, expctWllFrmd, expctVld, null); - } - - public static RepInfo testValidateFile(final Module module, - final File fileToTest, final int expctWllFrmd, final int expctVld, - final String message) { - return testValidateFile(module, fileToTest, expctWllFrmd, expctVld, message, true); - } - - public static RepInfo testValidateFile(final Module module, - final File fileToTest, final int expctWllFrmd, final int expctVld, - final String message, boolean messMustBePresent) { - RepInfo info = parseTestFile(module, fileToTest); - testResult(info, expctWllFrmd, expctVld, message, messMustBePresent); - return info; - } - - private static void testResult(final RepInfo info, final int expctWllFrmd, - final int expctVld, final String message, boolean messMustBePresent) { - testWellFormed(info, expctWllFrmd); - testIsValid(info, expctVld); - if (message == null) { - return; - } - Message jhoveMessage = getMessageIfPresent(info, message); - if (messMustBePresent) { - if (jhoveMessage == null) { - System.out.println(String.format( - "Expected message: %s, not found.", message)); - outputMessages(info); - } - assertNotNull("Expected message: " + message, jhoveMessage); - } else { - if (jhoveMessage != null) { - System.out.println(String.format( - "Unexpected message: %s, found.", jhoveMessage.getMessage())); - outputMessages(info); - } - assertNull("Unexpected message: " + message, jhoveMessage); - } - } - - private static void outputMessages(final RepInfo info) { - System.out.println("Messages in Report Info:"); - for (Message mess : info.getMessage()) { - System.out.println(String.format(" - %s", mess.getMessage())); - } - } - - private static RepInfo parseTestFile(final Module module, - final File toTest) { - if (module.isRandomAccess()) { - return rafModuleTest(module, toTest); - } - return streamModuleTest(module, toTest); - } - - - private static RepInfo streamModuleTest(final Module fisModule, final File toTest) { - RepInfo info = new RepInfo(toTest.getName()); - try (InputStream fis = new FileInputStream(toTest)) { - int index = fisModule.parse(fis, info, 0); - while (index > 0) { - index = fisModule.parse(fis, info, 0); - } - } catch (FileNotFoundException excep) { - excep.printStackTrace(); - fail("Couldn't find file to test: " + toTest.getName()); - } catch (IOException excep) { - excep.printStackTrace(); - fail("IOException Reading: " + toTest.getName()); - } - return info; - } - - private static RepInfo rafModuleTest(final Module rafModule, final File toTest) { - RepInfo info = new RepInfo(toTest.getName()); - try (RandomAccessFile raf = new RandomAccessFile(toTest, "r")) { - rafModule.parse(raf, info); - } catch (FileNotFoundException excep) { - excep.printStackTrace(); - fail("Couldn't find file to test: " + toTest.getName()); - } catch (IOException excep) { - excep.printStackTrace(); - fail("IOException Reading: " + toTest.getName()); - } - return info; - } - - private static void testWellFormed(final RepInfo info, final int expctWllFrmd) { - String message = (expctWllFrmd == RepInfo.TRUE) - ? "Should be well formed." - : "Should NOT be well formed."; - assertEquals(message, expctWllFrmd, info.getWellFormed()); - } - - private static void testIsValid(final RepInfo info, final int expctVld) { - String message = (expctVld == RepInfo.TRUE) ? "Should be valid." - : "Should NOT be valid."; - assertEquals(message, expctVld, info.getValid()); - } - - private static Message getMessageIfPresent(final RepInfo info, final String expctMessage) { - for (Message message : info.getMessage()) { - if (message.getMessage().equals(expctMessage)) { - return message; - } - } - return null; - } + public static final String MODULE_RESOURCE_BASE = "/edu/harvard/hul/ois/jhove/module/"; + public static final String EMPTY_FILE_PATH = MODULE_RESOURCE_BASE + "empty"; + + private TestUtils() { + // Keep out + throw new AssertionError("Should never be in constructor."); + } + + /** + * Convenience method that takes the path to a test resource and tests that the results of JHOVE + * are as expected. + * + * @param module a {@link Module} instance to validate the resource. + * @param resToTest the String path of the resource to validate and test. + * @param expctWllFrmd the expected well formed value + * @param expctVld the expected is valid value + * @throws URISyntaxException when there's an issue converting the resource name to a path + */ + public static RepInfo testValidateResource( + final Module module, final String resToTest, final int expctWllFrmd, final int expctVld) + throws URISyntaxException { + return testValidateResource(module, resToTest, expctWllFrmd, expctVld, null); + } + + /** + * @param module a {@link edu.harvard.hul.ois.jhove.Module} instance to use to validate the + * resource. + * @param resToTest the String path of the resource to validate and test. + * @param expctWllFrmd the expected well formed value + * @param expctVld the expected is valid value + * @param message a JHOVE validation string message expected to be found in the list of validation + * messages. If this parameter is null the test isn't performed. + * @throws URISyntaxException when there's an issue converting the resource name to a path + */ + public static RepInfo testValidateResource( + final Module module, + final String resToTest, + final int expctWllFrmd, + final int expctVld, + final String message) + throws URISyntaxException { + return testValidateResource(module, resToTest, expctWllFrmd, expctVld, message, true); + } + + /** + * @param module a {@link Module} instance to validate the resource. + * @param resToTest the String path of the resource to validate and test. + * @param expctWllFrmd the expected well formed value + * @param expctVld the expected is valid value + * @param expctMessage a JHOVE validation string message which MUST be found in the list of + * validation messages if messMustBePresent is true. When messMustBePresent is false the + * message MUST NOT be found in the list of validation messages. If this parameter is null the + * test isn't performed. + * @param messMustBePresent if message is not null this param dictates whether the test expects + * the validation message to be in the report or not. + * @throws URISyntaxException when there's an issue converting the resource name to a path + */ + public static RepInfo testValidateResource( + final Module module, + final String resToTest, + final int expctWllFrmd, + final int expctVld, + final String expctMessage, + boolean messMustBePresent) + throws URISyntaxException { + File toTest = new File(TestUtils.class.getResource(resToTest).toURI()); + + return testValidateFile( + module, toTest, expctWllFrmd, expctVld, expctMessage, messMustBePresent); + } + + /** + * Method that takes a file and tests that the results of JHOVE are as expected. + * + * @param module a {@link Module} instance to validate the resource. + * @param fileToTest a Java File instance to validate + * @param expctWllFrmd the expected well-formed value + * @param expctVld the expected is valid value + */ + public static RepInfo testValidateFile( + final Module module, final File fileToTest, final int expctWllFrmd, final int expctVld) { + return testValidateFile(module, fileToTest, expctWllFrmd, expctVld, null); + } + + public static RepInfo testValidateFile( + final Module module, + final File fileToTest, + final int expctWllFrmd, + final int expctVld, + final String message) { + return testValidateFile(module, fileToTest, expctWllFrmd, expctVld, message, true); + } + + public static RepInfo testValidateFile( + final Module module, + final File fileToTest, + final int expctWllFrmd, + final int expctVld, + final String message, + boolean messMustBePresent) { + RepInfo info = parseTestFile(module, fileToTest); + testResult(info, expctWllFrmd, expctVld, message, messMustBePresent); + return info; + } + + private static void testResult( + final RepInfo info, + final int expctWllFrmd, + final int expctVld, + final String message, + boolean messMustBePresent) { + testWellFormed(info, expctWllFrmd); + testIsValid(info, expctVld); + if (message == null) { + return; + } + Message jhoveMessage = getMessageIfPresent(info, message); + if (messMustBePresent) { + if (jhoveMessage == null) { + System.out.println(String.format("Expected message: %s, not found.", message)); + outputMessages(info); + } + assertNotNull("Expected message: " + message, jhoveMessage); + } else { + if (jhoveMessage != null) { + System.out.println( + String.format("Unexpected message: %s, found.", jhoveMessage.getMessage())); + outputMessages(info); + } + assertNull("Unexpected message: " + message, jhoveMessage); + } + } + + private static void outputMessages(final RepInfo info) { + System.out.println("Messages in Report Info:"); + for (Message mess : info.getMessage()) { + System.out.println(String.format(" - %s", mess.getMessage())); + } + } + + private static RepInfo parseTestFile(final Module module, final File toTest) { + if (module.isRandomAccess()) { + return rafModuleTest(module, toTest); + } + return streamModuleTest(module, toTest); + } + + private static RepInfo streamModuleTest(final Module fisModule, final File toTest) { + RepInfo info = new RepInfo(toTest.getName()); + try (InputStream fis = new FileInputStream(toTest)) { + int index = fisModule.parse(fis, info, 0); + while (index > 0) { + index = fisModule.parse(fis, info, 0); + } + } catch (FileNotFoundException excep) { + excep.printStackTrace(); + fail("Couldn't find file to test: " + toTest.getName()); + } catch (IOException excep) { + excep.printStackTrace(); + fail("IOException Reading: " + toTest.getName()); + } + return info; + } + + private static RepInfo rafModuleTest(final Module rafModule, final File toTest) { + RepInfo info = new RepInfo(toTest.getName()); + try (RandomAccessFile raf = new RandomAccessFile(toTest, "r")) { + rafModule.parse(raf, info); + } catch (FileNotFoundException excep) { + excep.printStackTrace(); + fail("Couldn't find file to test: " + toTest.getName()); + } catch (IOException excep) { + excep.printStackTrace(); + fail("IOException Reading: " + toTest.getName()); + } + return info; + } + + private static void testWellFormed(final RepInfo info, final int expctWllFrmd) { + String message = + (expctWllFrmd == RepInfo.TRUE) ? "Should be well formed." : "Should NOT be well formed."; + assertEquals(message, expctWllFrmd, info.getWellFormed()); + } + + private static void testIsValid(final RepInfo info, final int expctVld) { + String message = (expctVld == RepInfo.TRUE) ? "Should be valid." : "Should NOT be valid."; + assertEquals(message, expctVld, info.getValid()); + } + + private static Message getMessageIfPresent(final RepInfo info, final String expctMessage) { + for (Message message : info.getMessage()) { + if (message.getMessage().equals(expctMessage)) { + return message; + } + } + return null; + } } diff --git a/jhove-modules/ascii-hul/src/test/java/edu/harvard/hul/ois/jhove/module/ascii/AsciiTests.java b/jhove-modules/ascii-hul/src/test/java/edu/harvard/hul/ois/jhove/module/ascii/AsciiTests.java index 8066ef11b..34c57fbf7 100644 --- a/jhove-modules/ascii-hul/src/test/java/edu/harvard/hul/ois/jhove/module/ascii/AsciiTests.java +++ b/jhove-modules/ascii-hul/src/test/java/edu/harvard/hul/ois/jhove/module/ascii/AsciiTests.java @@ -2,131 +2,157 @@ import static org.junit.Assert.assertTrue; -import java.net.URISyntaxException; -import java.util.HashSet; -import java.util.List; -import java.util.Set; - -import org.junit.Before; -import org.junit.Test; - import edu.harvard.hul.ois.jhove.JhoveBase; import edu.harvard.hul.ois.jhove.Property; import edu.harvard.hul.ois.jhove.PropertyArity; import edu.harvard.hul.ois.jhove.RepInfo; import edu.harvard.hul.ois.jhove.module.AsciiModule; import edu.harvard.hul.ois.jhove.module.TestUtils; +import java.net.URISyntaxException; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import org.junit.Before; +import org.junit.Test; /** - * @author Carl Wilson - * carlwilson AT github + * @author Carl Wilson carlwilson AT github */ - public class AsciiTests { - private static final String asciiResourcePath = TestUtils.MODULE_RESOURCE_BASE - + "ascii/"; - - private static final String nullsOnly = asciiResourcePath - + "nulls-only.txt"; - private static final String allCtrls = asciiResourcePath + "all-ctrls.txt"; - private static final String allGraphics = asciiResourcePath - + "all-graphics.txt"; - private static final String allAscii = asciiResourcePath + "all-ascii.txt"; - private static final String allAsciiInv = asciiResourcePath - + "all-ascii-and-invalid.txt"; - private AsciiModule module; - - /** - * @throws java.lang.Exception - */ - @Before - public void setUp() throws Exception { - this.module = new AsciiModule(); - JhoveBase je = new JhoveBase(); - this.module.setBase(je); - } - - @Test - public final void testEmpty() throws URISyntaxException { - TestUtils.testValidateResource(this.module, TestUtils.EMPTY_FILE_PATH, - RepInfo.FALSE, RepInfo.FALSE, - MessageConstants.ASCII_HUL_2.getMessage()); - } - - @Test - public final void testNullOnly() throws URISyntaxException { - TestUtils.testValidateResource(this.module, nullsOnly, RepInfo.TRUE, - RepInfo.TRUE, MessageConstants.ASCII_HUL_2.getMessage(), - false); - } - - @Test - public final void testNotEmpty() throws URISyntaxException { - TestUtils.testValidateResource(this.module, allAscii, RepInfo.TRUE, - RepInfo.TRUE, MessageConstants.ASCII_HUL_2.getMessage(), - false); - } - - @Test - public final void testCharOutOfRange() throws URISyntaxException { - TestUtils.testValidateResource(this.module, allAsciiInv, RepInfo.FALSE, - RepInfo.FALSE, MessageConstants.ASCII_HUL_1.getMessage()); - } - - @Test - public final void testPrintable() throws URISyntaxException { - TestUtils.testValidateResource(this.module, allAscii, RepInfo.TRUE, - RepInfo.TRUE, MessageConstants.ASCII_HUL_3.getMessage(), false); - } - - @Test - public final void testNoPrintable() throws URISyntaxException { - TestUtils.testValidateResource(this.module, allCtrls, RepInfo.TRUE, - RepInfo.TRUE, MessageConstants.ASCII_HUL_3.getMessage()); - } - - @Test - public final void testControlChars() throws URISyntaxException { - RepInfo info = TestUtils.testValidateResource(this.module, allCtrls, - RepInfo.TRUE, RepInfo.TRUE, MessageConstants.ASCII_HUL_3.getMessage()); - testAllAsciiCtrlCharsDetected(info); - } - - @Test - public final void testControlCharsWithGraphics() throws URISyntaxException { - RepInfo info = TestUtils.testValidateResource(this.module, allAscii, - RepInfo.TRUE, RepInfo.TRUE, MessageConstants.ASCII_HUL_3.getMessage(), - false); - testAllAsciiCtrlCharsDetected(info); - } - - @Test - public final void testControlCharsFalsePos() throws URISyntaxException { - RepInfo info = TestUtils.testValidateResource(this.module, allGraphics, - RepInfo.TRUE, RepInfo.TRUE, MessageConstants.ASCII_HUL_3.getMessage(), - false); - Property asciiProp = info.getProperty("ASCIIMetadata"); - assertTrue(asciiProp == null); - } - - private final static void testAllAsciiCtrlCharsDetected( - final RepInfo info) { - Property ctrlCharProp = info.getProperty("ASCIIMetadata") - .getByName(ControlChar.PROP_NAME); - assertTrue(ctrlCharProp.getArity() == PropertyArity.LIST); - @SuppressWarnings("unchecked") - List mnemonics = (List) ctrlCharProp.getValue(); - Set ctrlCharSet = new HashSet<>(); - for (String mnemonic : mnemonics) { - ControlChar ctrlChar = ControlChar.fromMnemonic(mnemonic); - ctrlCharSet.add(ctrlChar); - } - Integer mnemonicsCount = Integer.valueOf(mnemonics.size()); - Integer asciiCtrlMinusCrLf = Integer - .valueOf(ControlChar.ASCII.size() - 2); - assertTrue( - String.format("Only %d characters found, expecting %d", - mnemonicsCount, asciiCtrlMinusCrLf), - asciiCtrlMinusCrLf.equals(mnemonicsCount)); - } + private static final String asciiResourcePath = TestUtils.MODULE_RESOURCE_BASE + "ascii/"; + + private static final String nullsOnly = asciiResourcePath + "nulls-only.txt"; + private static final String allCtrls = asciiResourcePath + "all-ctrls.txt"; + private static final String allGraphics = asciiResourcePath + "all-graphics.txt"; + private static final String allAscii = asciiResourcePath + "all-ascii.txt"; + private static final String allAsciiInv = asciiResourcePath + "all-ascii-and-invalid.txt"; + private AsciiModule module; + + /** @throws java.lang.Exception */ + @Before + public void setUp() throws Exception { + this.module = new AsciiModule(); + JhoveBase je = new JhoveBase(); + this.module.setBase(je); + } + + @Test + public final void testEmpty() throws URISyntaxException { + TestUtils.testValidateResource( + this.module, + TestUtils.EMPTY_FILE_PATH, + RepInfo.FALSE, + RepInfo.FALSE, + MessageConstants.ASCII_HUL_2.getMessage()); + } + + @Test + public final void testNullOnly() throws URISyntaxException { + TestUtils.testValidateResource( + this.module, + nullsOnly, + RepInfo.TRUE, + RepInfo.TRUE, + MessageConstants.ASCII_HUL_2.getMessage(), + false); + } + + @Test + public final void testNotEmpty() throws URISyntaxException { + TestUtils.testValidateResource( + this.module, + allAscii, + RepInfo.TRUE, + RepInfo.TRUE, + MessageConstants.ASCII_HUL_2.getMessage(), + false); + } + + @Test + public final void testCharOutOfRange() throws URISyntaxException { + TestUtils.testValidateResource( + this.module, + allAsciiInv, + RepInfo.FALSE, + RepInfo.FALSE, + MessageConstants.ASCII_HUL_1.getMessage()); + } + + @Test + public final void testPrintable() throws URISyntaxException { + TestUtils.testValidateResource( + this.module, + allAscii, + RepInfo.TRUE, + RepInfo.TRUE, + MessageConstants.ASCII_HUL_3.getMessage(), + false); + } + + @Test + public final void testNoPrintable() throws URISyntaxException { + TestUtils.testValidateResource( + this.module, + allCtrls, + RepInfo.TRUE, + RepInfo.TRUE, + MessageConstants.ASCII_HUL_3.getMessage()); + } + + @Test + public final void testControlChars() throws URISyntaxException { + RepInfo info = + TestUtils.testValidateResource( + this.module, + allCtrls, + RepInfo.TRUE, + RepInfo.TRUE, + MessageConstants.ASCII_HUL_3.getMessage()); + testAllAsciiCtrlCharsDetected(info); + } + + @Test + public final void testControlCharsWithGraphics() throws URISyntaxException { + RepInfo info = + TestUtils.testValidateResource( + this.module, + allAscii, + RepInfo.TRUE, + RepInfo.TRUE, + MessageConstants.ASCII_HUL_3.getMessage(), + false); + testAllAsciiCtrlCharsDetected(info); + } + + @Test + public final void testControlCharsFalsePos() throws URISyntaxException { + RepInfo info = + TestUtils.testValidateResource( + this.module, + allGraphics, + RepInfo.TRUE, + RepInfo.TRUE, + MessageConstants.ASCII_HUL_3.getMessage(), + false); + Property asciiProp = info.getProperty("ASCIIMetadata"); + assertTrue(asciiProp == null); + } + + private static final void testAllAsciiCtrlCharsDetected(final RepInfo info) { + Property ctrlCharProp = info.getProperty("ASCIIMetadata").getByName(ControlChar.PROP_NAME); + assertTrue(ctrlCharProp.getArity() == PropertyArity.LIST); + @SuppressWarnings("unchecked") + List mnemonics = (List) ctrlCharProp.getValue(); + Set ctrlCharSet = new HashSet<>(); + for (String mnemonic : mnemonics) { + ControlChar ctrlChar = ControlChar.fromMnemonic(mnemonic); + ctrlCharSet.add(ctrlChar); + } + Integer mnemonicsCount = Integer.valueOf(mnemonics.size()); + Integer asciiCtrlMinusCrLf = Integer.valueOf(ControlChar.ASCII.size() - 2); + assertTrue( + String.format("Only %d characters found, expecting %d", mnemonicsCount, asciiCtrlMinusCrLf), + asciiCtrlMinusCrLf.equals(mnemonicsCount)); + } } diff --git a/jhove-modules/gif-hul/src/main/java/GDump.java b/jhove-modules/gif-hul/src/main/java/GDump.java index 1ceac0c2f..b6738be47 100644 --- a/jhove-modules/gif-hul/src/main/java/GDump.java +++ b/jhove-modules/gif-hul/src/main/java/GDump.java @@ -1,263 +1,275 @@ -/********************************************************************** - * GDump - JSTOR/Harvard Object Validation Environment - * Copyright 2004 by the President and Fellows of Harvard College +/** + * ******************************************************************** GDump - JSTOR/Harvard Object + * Validation Environment Copyright 2004 by the President and Fellows of Harvard College * - * This program 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 2 of the License, or (at - * your option) any later version. - * - * This program 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. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 - * USA - **********************************************************************/ - + *

This program 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 2 + * of the License, or (at your option) any later version. + * + *

This program 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. + * + *

You should have received a copy of the GNU Lesser General Public License along with this + * program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA ******************************************************************** + */ import edu.harvard.hul.ois.jhove.*; import java.io.*; -//import java.util.*; -/** - * Dump contents of GIF file in human-readable format. - */ -public class GDump - extends Dump -{ - /****************************************************************** - * MAIN ENTRY POINT. - ******************************************************************/ +// import java.util.*; - /** - * Main entry point. - * @param args Command line arguments - */ - public static void main (String [] args) - { - if (args.length < 1) { - System.err.println ("usage: java GDump file"); - System.exit (-1); - } +/** Dump contents of GIF file in human-readable format. */ +public class GDump extends Dump { + /** + * **************************************************************** MAIN ENTRY POINT. + * **************************************************************** + */ + + /** + * Main entry point. + * + * @param args Command line arguments + */ + public static void main(String[] args) { + if (args.length < 1) { + System.err.println("usage: java GDump file"); + System.exit(-1); + } - try (FileInputStream file = new FileInputStream (args[0]); - BufferedInputStream buffer = new BufferedInputStream (file); - DataInputStream stream = new DataInputStream (buffer)) { - boolean bigEndian = false; + try (FileInputStream file = new FileInputStream(args[0]); + BufferedInputStream buffer = new BufferedInputStream(file); + DataInputStream stream = new DataInputStream(buffer)) { + boolean bigEndian = false; - String signature = readChars (stream, 3); - String version = readChars (stream, 3); - System.out.println ("00000000: \"" + signature + version + "\""); + String signature = readChars(stream, 3); + String version = readChars(stream, 3); + System.out.println("00000000: \"" + signature + version + "\""); - int width = ModuleBase.readUnsignedShort(stream, bigEndian, null); - int height = ModuleBase.readUnsignedShort(stream, bigEndian, null); - int packedFields = stream.readUnsignedByte (); - int backgroundColor = stream.readUnsignedByte (); - int aspectRatio = stream.readUnsignedByte (); - System.out.println ("00000006: LogicalScreenDescriptor: " + - width + "x" + height + " 0x" + - leading (packedFields, 2) + - Integer.toHexString (packedFields) + " " + - backgroundColor + " " + aspectRatio); - long os = 13; + int width = ModuleBase.readUnsignedShort(stream, bigEndian, null); + int height = ModuleBase.readUnsignedShort(stream, bigEndian, null); + int packedFields = stream.readUnsignedByte(); + int backgroundColor = stream.readUnsignedByte(); + int aspectRatio = stream.readUnsignedByte(); + System.out.println( + "00000006: LogicalScreenDescriptor: " + + width + + "x" + + height + + " 0x" + + leading(packedFields, 2) + + Integer.toHexString(packedFields) + + " " + + backgroundColor + + " " + + aspectRatio); + long os = 13; - boolean globalColorTable = (packedFields & 0x80) != 0; - int globalColorTableSize = packedFields & 0x07; + boolean globalColorTable = (packedFields & 0x80) != 0; + int globalColorTableSize = packedFields & 0x07; - if (globalColorTable) { - System.out.println (leading (os, 8) + os + - ": GlobalColorTable:"); - os = colorTable (stream, os, globalColorTableSize); - } + if (globalColorTable) { + System.out.println(leading(os, 8) + os + ": GlobalColorTable:"); + os = colorTable(stream, os, globalColorTableSize); + } - for (int op = 0; (op = stream.readUnsignedByte ()) != -1;) { - if (op == 0x00) { - System.out.println (leading (os, 8) + os + - ": BlockTerminator"); - os++; - } - else if (op == 0x2c) { - int imageLeft = ModuleBase.readUnsignedShort (stream, - bigEndian, - null); - int imageTop = ModuleBase.readUnsignedShort (stream, - bigEndian, - null); - int imageWidth = ModuleBase.readUnsignedShort (stream, - bigEndian, - null); - int imageHeight = ModuleBase.readUnsignedShort (stream, - bigEndian, - null); - packedFields = stream.readUnsignedByte (); - System.out.println (leading (os, 8) + os + - ": ImageDescriptor: " + imageLeft + - "," + imageTop + " " + imageWidth + - "x" + imageHeight + " 0x" + - leading (packedFields, 2) + - Integer.toHexString (packedFields)); - os += 10; + for (int op = 0; (op = stream.readUnsignedByte()) != -1; ) { + if (op == 0x00) { + System.out.println(leading(os, 8) + os + ": BlockTerminator"); + os++; + } else if (op == 0x2c) { + int imageLeft = ModuleBase.readUnsignedShort(stream, bigEndian, null); + int imageTop = ModuleBase.readUnsignedShort(stream, bigEndian, null); + int imageWidth = ModuleBase.readUnsignedShort(stream, bigEndian, null); + int imageHeight = ModuleBase.readUnsignedShort(stream, bigEndian, null); + packedFields = stream.readUnsignedByte(); + System.out.println( + leading(os, 8) + + os + + ": ImageDescriptor: " + + imageLeft + + "," + + imageTop + + " " + + imageWidth + + "x" + + imageHeight + + " 0x" + + leading(packedFields, 2) + + Integer.toHexString(packedFields)); + os += 10; - boolean localColorTable = (packedFields & 0x80) != 0; - int localColorTableSize = packedFields & 0x07; + boolean localColorTable = (packedFields & 0x80) != 0; + int localColorTableSize = packedFields & 0x07; - if (localColorTable) { - System.out.println (leading (os, 8) + os + - ": LocalColorTable:"); - os = colorTable (stream, os, localColorTableSize); - } + if (localColorTable) { + System.out.println(leading(os, 8) + os + ": LocalColorTable:"); + os = colorTable(stream, os, localColorTableSize); + } - int size = stream.readUnsignedByte (); - System.out.println (leading (os, 8) + os + - ": ImageData: " + size); - os = subBlocks (stream, ++os); - } - else if (op == 0x21) { - int label = stream.readUnsignedByte (); + int size = stream.readUnsignedByte(); + System.out.println(leading(os, 8) + os + ": ImageData: " + size); + os = subBlocks(stream, ++os); + } else if (op == 0x21) { + int label = stream.readUnsignedByte(); - if (label == 0x01) { - int blockSize = stream.readUnsignedByte (); - int gridLeft = ModuleBase.readUnsignedShort(stream, - bigEndian, - null); - int gridTop = ModuleBase.readUnsignedShort(stream, - bigEndian, - null); - int gridWidth = ModuleBase.readUnsignedShort(stream, - bigEndian, - null); - int gridHeight= ModuleBase.readUnsignedShort(stream, - bigEndian, - null); - int cellWidth = stream.readUnsignedByte (); - int cellHeight = stream.readUnsignedByte (); - int foreground = stream.readUnsignedByte (); - int background = stream.readUnsignedByte (); - System.out.println (leading (os, 8) + os + - ": PlainTextExtension: " + - blockSize + " " + gridLeft + "," + - gridTop + " " + gridWidth + "x" + - gridHeight + " " + cellWidth + - "x" + cellHeight + " " + - foreground + "," + background); - os += 15; - os = subBlocks (stream, os); - } - else if (label == 0xf9) { - int blockSize = stream.readUnsignedByte (); - packedFields = stream.readUnsignedByte (); - int delayTime = ModuleBase.readUnsignedShort(stream, - bigEndian, - null); - int colorIndex= stream.readUnsignedByte (); - System.out.println (leading (os, 8) + os + - ": GraphicControlExtension: " + - blockSize + " 0x" + - leading (packedFields, 2) + - packedFields + " " + delayTime + - " " + colorIndex); - os += 7; - } - else if (label == 0xfe) { - System.out.println (leading (os, 8) + os + - ": CommentExtension: \"" + - "\""); - os += 2; - os = subBlocks (stream, os); - } - else if (label == 0xff) { - int blockSize = stream.readUnsignedByte (); - String appId = readChars (stream, 8); - int [] authCode = new int [3]; - authCode[0] = stream.readUnsignedByte (); - authCode[1] = stream.readUnsignedByte (); - authCode[2] = stream.readUnsignedByte (); - System.out.println (leading (os, 8) + os + - ": ApplicationExtension: " + - blockSize + " \"" + appId + - "\" " + authCode[0] + "," + - authCode[1] + "," + authCode[2]); - os += 14; - os = subBlocks (stream, os); - } - else { - String hex = Integer.toHexString (label); - System.out.println (leading (os-1, 8) + (os-1) + - ": Unknown extension block: 0x" + - leading (hex, 2) + hex); - os += 2; - } - } - else if (op == 0x3b) { - System.out.println (leading (os, 8) + os + - ": Trailer: 0x3b"); - os++; - break; - } - else { - String hex = Integer.toHexString (op); - System.out.println (leading (os, 8) + os + - ": Unknown block: 0x" + - leading (hex, 2) + hex); - os++; - } - } - stream.close (); - } - catch (Exception e) { - e.printStackTrace (System.err); - System.exit (-2); - } + if (label == 0x01) { + int blockSize = stream.readUnsignedByte(); + int gridLeft = ModuleBase.readUnsignedShort(stream, bigEndian, null); + int gridTop = ModuleBase.readUnsignedShort(stream, bigEndian, null); + int gridWidth = ModuleBase.readUnsignedShort(stream, bigEndian, null); + int gridHeight = ModuleBase.readUnsignedShort(stream, bigEndian, null); + int cellWidth = stream.readUnsignedByte(); + int cellHeight = stream.readUnsignedByte(); + int foreground = stream.readUnsignedByte(); + int background = stream.readUnsignedByte(); + System.out.println( + leading(os, 8) + + os + + ": PlainTextExtension: " + + blockSize + + " " + + gridLeft + + "," + + gridTop + + " " + + gridWidth + + "x" + + gridHeight + + " " + + cellWidth + + "x" + + cellHeight + + " " + + foreground + + "," + + background); + os += 15; + os = subBlocks(stream, os); + } else if (label == 0xf9) { + int blockSize = stream.readUnsignedByte(); + packedFields = stream.readUnsignedByte(); + int delayTime = ModuleBase.readUnsignedShort(stream, bigEndian, null); + int colorIndex = stream.readUnsignedByte(); + System.out.println( + leading(os, 8) + + os + + ": GraphicControlExtension: " + + blockSize + + " 0x" + + leading(packedFields, 2) + + packedFields + + " " + + delayTime + + " " + + colorIndex); + os += 7; + } else if (label == 0xfe) { + System.out.println(leading(os, 8) + os + ": CommentExtension: \"" + "\""); + os += 2; + os = subBlocks(stream, os); + } else if (label == 0xff) { + int blockSize = stream.readUnsignedByte(); + String appId = readChars(stream, 8); + int[] authCode = new int[3]; + authCode[0] = stream.readUnsignedByte(); + authCode[1] = stream.readUnsignedByte(); + authCode[2] = stream.readUnsignedByte(); + System.out.println( + leading(os, 8) + + os + + ": ApplicationExtension: " + + blockSize + + " \"" + + appId + + "\" " + + authCode[0] + + "," + + authCode[1] + + "," + + authCode[2]); + os += 14; + os = subBlocks(stream, os); + } else { + String hex = Integer.toHexString(label); + System.out.println( + leading(os - 1, 8) + + (os - 1) + + ": Unknown extension block: 0x" + + leading(hex, 2) + + hex); + os += 2; + } + } else if (op == 0x3b) { + System.out.println(leading(os, 8) + os + ": Trailer: 0x3b"); + os++; + break; + } else { + String hex = Integer.toHexString(op); + System.out.println(leading(os, 8) + os + ": Unknown block: 0x" + leading(hex, 2) + hex); + os++; + } + } + stream.close(); + } catch (Exception e) { + e.printStackTrace(System.err); + System.exit(-2); } + } - /** - * Read and display a color table. - * @param stream Data input stream - * @param os Current byte offset - * @param size Color table size - * @return Updated byte offset - */ - private static long colorTable (DataInputStream stream, long os, int size) - throws IOException - { - int n = 2<This program 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 2 + * of the License, or (at your option) any later version. * - * This program 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. + *

This program 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. * - * You should have received a copy of the GNU Lesser General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 - * USA - **********************************************************************/ - + *

You should have received a copy of the GNU Lesser General Public License along with this + * program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA ******************************************************************** + */ package edu.harvard.hul.ois.jhove.module; -import java.io.ByteArrayInputStream; -import java.io.DataInputStream; -import java.io.EOFException; -import java.io.File; -import java.io.IOException; -import java.io.InputStream; -import java.io.UnsupportedEncodingException; -import java.util.LinkedList; -import java.util.List; -import java.util.ListIterator; -import java.util.Vector; - -import javax.xml.parsers.SAXParserFactory; - -import org.xml.sax.SAXException; -import org.xml.sax.XMLReader; - import edu.harvard.hul.ois.jhove.Agent; import edu.harvard.hul.ois.jhove.Agent.Builder; import edu.harvard.hul.ois.jhove.AgentType; @@ -61,911 +40,842 @@ import edu.harvard.hul.ois.jhove.XMPHandler; import edu.harvard.hul.ois.jhove.module.gif.GifStrings; import edu.harvard.hul.ois.jhove.module.gif.MessageConstants; +import java.io.ByteArrayInputStream; +import java.io.DataInputStream; +import java.io.EOFException; +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.io.UnsupportedEncodingException; +import java.util.LinkedList; +import java.util.List; +import java.util.ListIterator; +import java.util.Vector; +import javax.xml.parsers.SAXParserFactory; +import org.xml.sax.SAXException; +import org.xml.sax.XMLReader; /** - * Module for identification and validation of GIF files. - * - * @author Gary McGath + * Module for identification and validation of GIF files. * + * @author Gary McGath */ -public class GifModule extends ModuleBase -{ - /****************************************************************** - * DEBUGGING FIELDS. - * All debugging fields should be set to false for release code. - ******************************************************************/ - - /* Set to true to allow application identifiers to be case-insensitive. */ - private static final boolean debug_appIdentCaseInsens = false; - - /****************************************************************** - * PRIVATE CLASS FIELDS. - ******************************************************************/ - - private static final String NAME = "GIF-hul"; - private static final String RELEASE = "1.4.2"; - private static final int [] DATE = { 2019, 12, 10 }; - private static final String [] FORMAT = {"GIF", - "Graphics Interchange Format"}; - private static final String COVERAGE = "GIF87a, GIF89a"; - private static final String [] MIMETYPE = {"image/gif"}; - private static final String WELLFORMED = "A GIF file is well-formed if " + - "it has a header block; a sequence of properly formed control, " + - "graphic-rendering, and special purpose blocks; and a trailer block"; - private static final String VALIDITY = "A GIF file is valid if " + - "well-formed, has at most one global color map, and at most one " + - "graphic control extension preceding an image descriptor or a plain " + - "text extension"; - private static final String REPINFO = "Additional representation " + - "information includes: NISO Z39.87 Digital Still Image Technical " + - "Metadata, and block-specific metadata"; - private static final String NOTE = "'GIF' and 'Graphics Interchange " + - "Format' are trademarks of " + - "Compuserve Interactive Services Inc."; - private static final String RIGHTS = "Copyright 2003-2007 by JSTOR and " + - "the President and Fellows of Harvard College. " + - "Released under the GNU Lesser General Public License."; - - /* Block type values */ - private static final int - EXT_BLOCK = 0X21, - APPLICATION_EXT = 0XFF, - COMMENT_EXT = 0XFE, - GRAPHIC_CONTROL_EXT = 0XF9, - IMAGE_DESC = 0X2C, - PLAIN_TEXT_EXT = 0X01, - TRAILER = 0X3B; - - /****************************************************************** - * PRIVATE INSTANCE FIELDS. - ******************************************************************/ - - /* First 6 bytes of file */ - protected byte _sig[]; - - /* XMP property */ - protected Property _xmpProp; - - /* Flag for presence of global color table */ - protected boolean _globalColorTableFlag; - - /* Size of global color table */ - protected int _globalColorTableSize; - - /* Count of graphic control extensions preceding - * something to modify */ - protected int _gceCounter; - - /* Top-level metadata property */ - protected Property _metadata; - - /* Blocks list property */ - protected List _blocksList; - - /* Total count of graphic and plain text extension blocks */ - protected int _numGraphicBlocks; - - /****************************************************************** - * CLASS CONSTRUCTOR. - ******************************************************************/ - /** - * Instantiate a GifModule object. - */ - public GifModule () - { - super (NAME, RELEASE, DATE, FORMAT, COVERAGE, MIMETYPE, WELLFORMED, - VALIDITY, REPINFO, NOTE, RIGHTS, false); - - _vendor = Agent.harvardInstance(); - - Document doc = new Document ("GIF (Graphics Interchange Format): A " + - "standard defining a mechanism for the " + - "storage and transmission of raster-" + - "based graphics information", - DocumentType.REPORT); - Builder builder = new Agent.Builder("Compuserve Interactive Services Inc.", - AgentType.COMMERCIAL).address ("5000 Arlington Centre Blvd., Columbus, OS 43220").telephone ("(614) 457-8600").web ("http://www.compuserve.com/"); - Agent cmpsrvAgent = builder.build(); - doc.setAuthor (cmpsrvAgent); - doc.setDate ("1987-06-15"); - doc.setIdentifier (new Identifier ("http://www.w3.org/Graphics/GIF/spec-gif87.txt", - IdentifierType.URL)); - _specification.add (doc); - - doc = new Document ("Graphics Interchange Format", - DocumentType.REPORT); - doc.setEdition ("Version 89a"); - doc.setAuthor (cmpsrvAgent); - doc.setDate ("1987-06-15"); - doc.setIdentifier (new Identifier ("http://www.w3.org/Graphics/GIF/spec-gif89a.txt", - IdentifierType.URL)); - _specification.add (doc); - - Signature sig = new InternalSignature ("GIF", SignatureType.MAGIC, - SignatureUseType.MANDATORY, 0); - _signature.add (sig); - sig = new InternalSignature ("87a", SignatureType.MAGIC, - SignatureUseType.MANDATORY_IF_APPLICABLE, - 3, "For version 87a"); - _signature.add (sig); - sig = new InternalSignature ("89a", SignatureType.MAGIC, - SignatureUseType.MANDATORY_IF_APPLICABLE, - 3, "For version 89a"); - _signature.add (sig); - - sig = new ExternalSignature (".gif", SignatureType.EXTENSION, - SignatureUseType.OPTIONAL); - _signature.add (sig); - - _bigEndian = false; +public class GifModule extends ModuleBase { + /** + * **************************************************************** DEBUGGING FIELDS. All + * debugging fields should be set to false for release code. + * **************************************************************** + */ + + /* Set to true to allow application identifiers to be case-insensitive. */ + private static final boolean debug_appIdentCaseInsens = false; + + /** + * **************************************************************** PRIVATE CLASS FIELDS. + * **************************************************************** + */ + private static final String NAME = "GIF-hul"; + + private static final String RELEASE = "1.4.2"; + private static final int[] DATE = {2019, 12, 10}; + private static final String[] FORMAT = {"GIF", "Graphics Interchange Format"}; + private static final String COVERAGE = "GIF87a, GIF89a"; + private static final String[] MIMETYPE = {"image/gif"}; + private static final String WELLFORMED = + "A GIF file is well-formed if " + + "it has a header block; a sequence of properly formed control, " + + "graphic-rendering, and special purpose blocks; and a trailer block"; + private static final String VALIDITY = + "A GIF file is valid if " + + "well-formed, has at most one global color map, and at most one " + + "graphic control extension preceding an image descriptor or a plain " + + "text extension"; + private static final String REPINFO = + "Additional representation " + + "information includes: NISO Z39.87 Digital Still Image Technical " + + "Metadata, and block-specific metadata"; + private static final String NOTE = + "'GIF' and 'Graphics Interchange " + + "Format' are trademarks of " + + "Compuserve Interactive Services Inc."; + private static final String RIGHTS = + "Copyright 2003-2007 by JSTOR and " + + "the President and Fellows of Harvard College. " + + "Released under the GNU Lesser General Public License."; + + /* Block type values */ + private static final int EXT_BLOCK = 0X21, + APPLICATION_EXT = 0XFF, + COMMENT_EXT = 0XFE, + GRAPHIC_CONTROL_EXT = 0XF9, + IMAGE_DESC = 0X2C, + PLAIN_TEXT_EXT = 0X01, + TRAILER = 0X3B; + + /** + * **************************************************************** PRIVATE INSTANCE FIELDS. + * **************************************************************** + */ + + /* First 6 bytes of file */ + protected byte _sig[]; + + /* XMP property */ + protected Property _xmpProp; + + /* Flag for presence of global color table */ + protected boolean _globalColorTableFlag; + + /* Size of global color table */ + protected int _globalColorTableSize; + + /* Count of graphic control extensions preceding + * something to modify */ + protected int _gceCounter; + + /* Top-level metadata property */ + protected Property _metadata; + + /* Blocks list property */ + protected List _blocksList; + + /* Total count of graphic and plain text extension blocks */ + protected int _numGraphicBlocks; + + /** + * **************************************************************** CLASS CONSTRUCTOR. + * **************************************************************** + */ + /** Instantiate a GifModule object. */ + public GifModule() { + super( + NAME, + RELEASE, + DATE, + FORMAT, + COVERAGE, + MIMETYPE, + WELLFORMED, + VALIDITY, + REPINFO, + NOTE, + RIGHTS, + false); + + _vendor = Agent.harvardInstance(); + + Document doc = + new Document( + "GIF (Graphics Interchange Format): A " + + "standard defining a mechanism for the " + + "storage and transmission of raster-" + + "based graphics information", + DocumentType.REPORT); + Builder builder = + new Agent.Builder("Compuserve Interactive Services Inc.", AgentType.COMMERCIAL) + .address("5000 Arlington Centre Blvd., Columbus, OS 43220") + .telephone("(614) 457-8600") + .web("http://www.compuserve.com/"); + Agent cmpsrvAgent = builder.build(); + doc.setAuthor(cmpsrvAgent); + doc.setDate("1987-06-15"); + doc.setIdentifier( + new Identifier("http://www.w3.org/Graphics/GIF/spec-gif87.txt", IdentifierType.URL)); + _specification.add(doc); + + doc = new Document("Graphics Interchange Format", DocumentType.REPORT); + doc.setEdition("Version 89a"); + doc.setAuthor(cmpsrvAgent); + doc.setDate("1987-06-15"); + doc.setIdentifier( + new Identifier("http://www.w3.org/Graphics/GIF/spec-gif89a.txt", IdentifierType.URL)); + _specification.add(doc); + + Signature sig = + new InternalSignature("GIF", SignatureType.MAGIC, SignatureUseType.MANDATORY, 0); + _signature.add(sig); + sig = + new InternalSignature( + "87a", + SignatureType.MAGIC, + SignatureUseType.MANDATORY_IF_APPLICABLE, + 3, + "For version 87a"); + _signature.add(sig); + sig = + new InternalSignature( + "89a", + SignatureType.MAGIC, + SignatureUseType.MANDATORY_IF_APPLICABLE, + 3, + "For version 89a"); + _signature.add(sig); + + sig = new ExternalSignature(".gif", SignatureType.EXTENSION, SignatureUseType.OPTIONAL); + _signature.add(sig); + + _bigEndian = false; + } + + /** + * **************************************************************** Parsing methods. + * **************************************************************** + */ + + /** + * Check if the digital object conforms to this Module's internal signature information. + * + * @param file A File object for the object being parsed + * @param stream An InputStream, positioned at its beginning, which is generated from the object + * to be parsed + * @param info A fresh RepInfo object which will be modified to reflect the results of the test + */ + @Override + public void checkSignatures(File file, InputStream stream, RepInfo info) { + int[] sigBytes = {'G', 'I', 'F', '8', '*', 'a'}; + int i; + int ch; + try { + _dstream = null; + _dstream = getBufferedDataStream(stream, _je != null ? _je.getBufferSize() : 0); + for (i = 0; i < 4; i++) { + ch = readUnsignedByte(_dstream, this); + if (ch != sigBytes[i]) { + info.setWellFormed(false); + return; + } + } + /* Byte 4 can be either 7 or 9 */ + ch = readUnsignedByte(_dstream, this); + if (ch != '7' && ch != '9') { + info.setWellFormed(false); + return; + } + ch = readUnsignedByte(_dstream, this); + if (ch != sigBytes[5]) { + info.setWellFormed(false); + return; + } + info.setModule(this); + info.setFormat(_format[0]); + info.setMimeType(_mimeType[0]); + info.setSigMatch(_name); + } catch (Exception e) { + // Reading a very short file may take us here. + info.setWellFormed(false); + return; } - - /****************************************************************** - * Parsing methods. - ******************************************************************/ - - /** - * Check if the digital object conforms to this Module's - * internal signature information. - * - * @param file A File object for the object being parsed - * @param stream An InputStream, positioned at its beginning, - * which is generated from the object to be parsed - * @param info A fresh RepInfo object which will be modified - * to reflect the results of the test - */ - @Override - public void checkSignatures (File file, - InputStream stream, - RepInfo info) - { - int[] sigBytes = { 'G', 'I', 'F', '8', '*', 'a' }; - int i; - int ch; - try { - _dstream = null; - _dstream = getBufferedDataStream (stream, _je != null ? - _je.getBufferSize () : 0); - for (i = 0; i < 4; i++) { - ch = readUnsignedByte(_dstream, this); - if (ch != sigBytes[i]) { - info.setWellFormed (false); - return; - } - } - /* Byte 4 can be either 7 or 9 */ - ch = readUnsignedByte (_dstream, this); - if (ch != '7' && ch != '9') { - info.setWellFormed (false); - return; - } - ch = readUnsignedByte (_dstream, this); - if (ch != sigBytes[5]) { - info.setWellFormed (false); - return; - } - info.setModule (this); - info.setFormat (_format[0]); - info.setMimeType (_mimeType[0]); - info.setSigMatch(_name); - } - catch (Exception e) { - // Reading a very short file may take us here. - info.setWellFormed (false); - return; - } + } + + /** + * Parse the content of a purported GIF stream digital object and store the results in RepInfo. + * + * @param stream An InputStream, positioned at its beginning, which is generated from the object + * to be parsed + * @param info A fresh RepInfo object which will be modified to reflect the results of the parsing + * @param parseIndex Must be 0 in first call to parse. If parse returns + * a nonzero value, it must be called again with parseIndex equal to that return + * value. + */ + @Override + public int parse(InputStream stream, RepInfo info, int parseIndex) throws IOException { + initParse(); + info.setModule(this); + info.setFormat(_format[0]); + info.setMimeType(_mimeType[0]); + + _blocksList = new LinkedList(); + + Property _blocks = + new Property("Blocks", PropertyType.PROPERTY, PropertyArity.LIST, _blocksList); + + setupDataStream(stream, info); + + if (!readSig(info)) { + return 0; } - /** - * Parse the content of a purported GIF stream digital object and store the - * results in RepInfo. - * - * @param stream An InputStream, positioned at its beginning, - * which is generated from the object to be parsed - * @param info A fresh RepInfo object which will be modified - * to reflect the results of the parsing - * @param parseIndex Must be 0 in first call to parse. If - * parse returns a nonzero value, it must be - * called again with parseIndex - * equal to that return value. - */ - @Override - public int parse (InputStream stream, RepInfo info, int parseIndex) - throws IOException - { - initParse (); - info.setModule (this); - info.setFormat (_format[0]); - info.setMimeType (_mimeType[0]); - - _blocksList = new LinkedList (); - - Property _blocks = new Property ("Blocks", - PropertyType.PROPERTY, - PropertyArity.LIST, - _blocksList); - - setupDataStream(stream, info); - - if (!readSig (info)) { - return 0; - } - - /* If we got this far, take note that the signature is OK. */ - info.setSigMatch(_name); + /* If we got this far, take note that the signature is OK. */ + info.setSigMatch(_name); - if (!readLSD (info)) { - return 0; - } - - boolean moreToCome = true; - while (moreToCome) { - moreToCome = readBlock (info); - if (info.getWellFormed () == RepInfo.FALSE) { - return 0; - } - } + if (!readLSD(info)) { + return 0; + } - if (_ckSummer != null){ - skipDstreamToEnd(info); - // Set the checksums in the report if they're calculated - setChecksums(this._ckSummer, info); - } - Property[] metaArray; - if (_xmpProp != null) { - // Making this an array rather than a list is a pain, but it's policy - metaArray = new Property[3]; - } - else { - metaArray = new Property[2]; - } - _metadata = new Property ("GIFMetadata", - PropertyType.PROPERTY, - PropertyArity.ARRAY, - metaArray); - metaArray[1] = _blocks; // this comes after GraphicRenderingBlocks, - // which we can't calculate yet - metaArray[0] = new Property ("GraphicRenderingBlocks", - PropertyType.INTEGER, - new Integer (_numGraphicBlocks)); - if (_xmpProp != null) { - metaArray[2] = _xmpProp; - } - info.setProperty (_metadata); + boolean moreToCome = true; + while (moreToCome) { + moreToCome = readBlock(info); + if (info.getWellFormed() == RepInfo.FALSE) { return 0; + } } - /** - * Initializes the state of the module for parsing. - */ - @Override - protected void initParse () - { - super.initParse (); - _sig = new byte[6]; - _globalColorTableFlag = false; - _globalColorTableSize = 0; - _gceCounter = 0; - _numGraphicBlocks = 0; + if (_ckSummer != null) { + skipDstreamToEnd(info); + // Set the checksums in the report if they're calculated + setChecksums(this._ckSummer, info); } - - - /* Read the 6-byte signature. */ - protected boolean readSig (RepInfo info) throws IOException - { - int nbyt = 0; - while (nbyt < 6) { - try { - int ch = readUnsignedByte (_dstream, this); - if (nbyt < 6) { - _sig[nbyt] = (byte) ch; - } - nbyt++; - //if (_ckSummer != null) { - // _ckSummer.update (ch); - //} - } - catch (EOFException e) { - info.setMessage(new ErrorMessage (MessageConstants.GIF_HUL_1, 0)); - info.setWellFormed (RepInfo.FALSE); - return false; - } - } - String sigStr = new String (_sig); - if ("GIF89a".equals (sigStr)) { - info.setVersion ("89a"); - info.setProfile ("GIF 89a"); - } - else if ("GIF87a".equals (sigStr)) { - info.setVersion ("87a"); - info.setProfile ("GIF 87a"); - } - else { - info.setMessage(new ErrorMessage (MessageConstants.GIF_HUL_4, 0)); - info.setWellFormed (RepInfo.FALSE); - return false; - } - return true; + Property[] metaArray; + if (_xmpProp != null) { + // Making this an array rather than a list is a pain, but it's policy + metaArray = new Property[3]; + } else { + metaArray = new Property[2]; } - - /* Read the Logical Screen Descriptor. */ - protected boolean readLSD (RepInfo info) throws IOException - { - Vector propVec = new Vector (8); - /* GIF data is always little-endian. */ - int width = readUnsignedShort (_dstream); - propVec.add (new Property ("LogicalScreenWidth", - PropertyType.INTEGER, - new Integer (width))); - int height = readUnsignedShort (_dstream); - propVec.add (new Property ("LogicalScreenHeight", - PropertyType.INTEGER, - new Integer (height))); - int packedFields = readUnsignedByte (_dstream, this); - _globalColorTableFlag = (packedFields & 0X80) != 0; - int bitsPerColor = ((packedFields & 0X70) >> 4) + 1; - propVec.add (new Property ("ColorResolution", - PropertyType.INTEGER, - new Integer (bitsPerColor))); - boolean sortFlag = (packedFields & 0X8) != 0; - int rawGlobalColorTableSize = packedFields & 0X7; - if (_globalColorTableFlag) { - _globalColorTableSize = 3 * - (1 << (rawGlobalColorTableSize + 1)); - } - - int bgColorIndex = readUnsignedByte (_dstream, this); - propVec.add (new Property ("BackgroundColorIndex", - PropertyType.INTEGER, - new Integer (bgColorIndex))); - int pixAspectRatio = readUnsignedByte (_dstream, this); - // The pixel aspect ratio is turned into a real aspect - // ratio by a formula, but we just report the raw number. - propVec.add (new Property ("PixelAspectRatio", - PropertyType.SHORT, - new Short ((short) pixAspectRatio))); - propVec.add (addByteProperty ("GlobalColorTableFlag", - _globalColorTableFlag ? 1 : 0, - GifStrings.GLOBAL_COLOR_TABLE_FLAG)); - propVec.add (addByteProperty ("GlobalColorTableSortFlag", - sortFlag ? 1 : 0, - GifStrings.COLOR_TABLE_SORT_FLAG)); - propVec.add (new Property ("GlobalColorTableSize", - PropertyType.SHORT, - new Short ((short) rawGlobalColorTableSize))); - Property prop = new Property ("LogicalScreenDescriptor", - PropertyType.PROPERTY, - PropertyArity.ARRAY, - vectorToPropArray (propVec)); - _blocksList.add (prop); - - // Make a property with the global color table, if present - if (_globalColorTableFlag) { - short[] gctArray = new short[_globalColorTableSize]; - for (int i = 0; i < _globalColorTableSize; i++) { - gctArray[i] = (short) _dstream.readUnsignedByte (); - } - _blocksList.add (new Property ("GlobalColorTable", - PropertyType.SHORT, - PropertyArity.ARRAY, - gctArray)); - } - return true; + _metadata = new Property("GIFMetadata", PropertyType.PROPERTY, PropertyArity.ARRAY, metaArray); + metaArray[1] = _blocks; // this comes after GraphicRenderingBlocks, + // which we can't calculate yet + metaArray[0] = + new Property( + "GraphicRenderingBlocks", PropertyType.INTEGER, new Integer(_numGraphicBlocks)); + if (_xmpProp != null) { + metaArray[2] = _xmpProp; } - - - /* Read Graphic blocks, Special blocks, and the trailer. - * Return false if we get an error that prevents further - * progress, or if we encounter the Trailer. */ - protected boolean readBlock (RepInfo info) throws IOException - { - int type; - try { - type = readUnsignedByte (_dstream, this); - } - catch (EOFException e) { - // The spec isn't fully clear on whether a trailer is - // required, but seems to imply it is. - info.setWellFormed (RepInfo.FALSE); - info.setMessage (new ErrorMessage ( - MessageConstants.GIF_HUL_9, _nByte)); - return false; - } - try { - switch (type) { - case EXT_BLOCK: - return readExtBlock (info); - case IMAGE_DESC: - return readImage (info); - case TRAILER: - return false; // end of file - default: - info.setWellFormed (RepInfo.FALSE); - info.setMessage (new ErrorMessage - (MessageConstants.GIF_HUL_2, - "Type = " + type, _nByte)); - return false; - } - } - catch (EOFException e) { - // An EOF in the middle of a block is definitely a problem - info.setWellFormed (RepInfo.FALSE); - info.setMessage (new ErrorMessage - (MessageConstants.GIF_HUL_10, _nByte)); - return false; - } + info.setProperty(_metadata); + return 0; + } + + /** Initializes the state of the module for parsing. */ + @Override + protected void initParse() { + super.initParse(); + _sig = new byte[6]; + _globalColorTableFlag = false; + _globalColorTableSize = 0; + _gceCounter = 0; + _numGraphicBlocks = 0; + } + + /* Read the 6-byte signature. */ + protected boolean readSig(RepInfo info) throws IOException { + int nbyt = 0; + while (nbyt < 6) { + try { + int ch = readUnsignedByte(_dstream, this); + if (nbyt < 6) { + _sig[nbyt] = (byte) ch; + } + nbyt++; + // if (_ckSummer != null) { + // _ckSummer.update (ch); + // } + } catch (EOFException e) { + info.setMessage(new ErrorMessage(MessageConstants.GIF_HUL_1, 0)); + info.setWellFormed(RepInfo.FALSE); + return false; + } } - - /* Read an extension block. - */ - protected boolean readExtBlock (RepInfo info) throws IOException - { - int subtype = readUnsignedByte (_dstream, this); - switch (subtype) { - case APPLICATION_EXT: - return readAppExtension (info); - case COMMENT_EXT: - return readCommentExtension (info); - case GRAPHIC_CONTROL_EXT: - return readGraphicsCtlBlock (info); - case PLAIN_TEXT_EXT: - return readPlainTextExtension (info); - default: - info.setWellFormed (RepInfo.FALSE); - info.setMessage (new ErrorMessage - (MessageConstants.GIF_HUL_3, - "Type = " + subtype, - _nByte)); - return false; - } + String sigStr = new String(_sig); + if ("GIF89a".equals(sigStr)) { + info.setVersion("89a"); + info.setProfile("GIF 89a"); + } else if ("GIF87a".equals(sigStr)) { + info.setVersion("87a"); + info.setProfile("GIF 87a"); + } else { + info.setMessage(new ErrorMessage(MessageConstants.GIF_HUL_4, 0)); + info.setWellFormed(RepInfo.FALSE); + return false; + } + return true; + } + + /* Read the Logical Screen Descriptor. */ + protected boolean readLSD(RepInfo info) throws IOException { + Vector propVec = new Vector(8); + /* GIF data is always little-endian. */ + int width = readUnsignedShort(_dstream); + propVec.add(new Property("LogicalScreenWidth", PropertyType.INTEGER, new Integer(width))); + int height = readUnsignedShort(_dstream); + propVec.add(new Property("LogicalScreenHeight", PropertyType.INTEGER, new Integer(height))); + int packedFields = readUnsignedByte(_dstream, this); + _globalColorTableFlag = (packedFields & 0X80) != 0; + int bitsPerColor = ((packedFields & 0X70) >> 4) + 1; + propVec.add(new Property("ColorResolution", PropertyType.INTEGER, new Integer(bitsPerColor))); + boolean sortFlag = (packedFields & 0X8) != 0; + int rawGlobalColorTableSize = packedFields & 0X7; + if (_globalColorTableFlag) { + _globalColorTableSize = 3 * (1 << (rawGlobalColorTableSize + 1)); } - /* Read an image descriptor and the subsequent data. - * We are positioned just after the type byte of the - * image descriptor. - */ - protected boolean readImage (RepInfo info) throws IOException - { - ++_numGraphicBlocks; - Vector propVec = new Vector (7); - NisoImageMetadata niso = new NisoImageMetadata (); - Property nisoProp = new Property ("NisoImageMetadata", - PropertyType.NISOIMAGEMETADATA, niso); - - // GIF doesn't have a lot of options, so several - // NISO properties are constants. - niso.setMimeType ("image/gif"); - niso.setByteOrder ("little-endian"); - niso.setCompressionScheme(5); // LZW - niso.setColorSpace (3); // palette color - niso.setOrientation(1); // normal - niso.setBitsPerSample (new int[] {8}); - - _gceCounter = 0; - int leftPos = readUnsignedShort (_dstream); - propVec.add (new Property ("ImageLeftPosition", - PropertyType.INTEGER, - new Integer (leftPos))); - int topPos = readUnsignedShort (_dstream); - propVec.add (new Property ("ImageTopPosition", - PropertyType.INTEGER, - new Integer (topPos))); - int width = readUnsignedShort (_dstream); - niso.setImageWidth (width); - int height = readUnsignedShort (_dstream); - niso.setImageLength (height); - int packedFields = readUnsignedByte (_dstream, this); - int interlaceFlag = (packedFields & 0X40) >> 6; - propVec.add (addByteProperty ("InterlaceFlag", - interlaceFlag, - GifStrings.INTERLACE_FLAG)); - int localColorTableFlag = (packedFields & 0X80) >> 7; - propVec.add (addByteProperty ("LocalColorTableFlag", - localColorTableFlag, - GifStrings.LOCAL_COLOR_TABLE_FLAG)); - int sortFlag = (packedFields & 0X20) >> 5; - propVec.add (addByteProperty ("LocalColorTableSortFlag", - sortFlag, - GifStrings.COLOR_TABLE_SORT_FLAG)); - int localColorTableSize = 0; - int rawLocalColorTableSize = packedFields & 0X7; - propVec.add (new Property ("LocalColorTableSize", - PropertyType.SHORT, - new Short ((short) rawLocalColorTableSize))); - propVec.add (nisoProp); - if (localColorTableFlag != 0) { - localColorTableSize = - 3 * (1 << (rawLocalColorTableSize + 1)); - skipBytes (_dstream, localColorTableSize, this); - } - Property prop = new Property ("ImageDescriptor", + int bgColorIndex = readUnsignedByte(_dstream, this); + propVec.add( + new Property("BackgroundColorIndex", PropertyType.INTEGER, new Integer(bgColorIndex))); + int pixAspectRatio = readUnsignedByte(_dstream, this); + // The pixel aspect ratio is turned into a real aspect + // ratio by a formula, but we just report the raw number. + propVec.add( + new Property("PixelAspectRatio", PropertyType.SHORT, new Short((short) pixAspectRatio))); + propVec.add( + addByteProperty( + "GlobalColorTableFlag", + _globalColorTableFlag ? 1 : 0, + GifStrings.GLOBAL_COLOR_TABLE_FLAG)); + propVec.add( + addByteProperty( + "GlobalColorTableSortFlag", sortFlag ? 1 : 0, GifStrings.COLOR_TABLE_SORT_FLAG)); + propVec.add( + new Property( + "GlobalColorTableSize", + PropertyType.SHORT, + new Short((short) rawGlobalColorTableSize))); + Property prop = + new Property( + "LogicalScreenDescriptor", PropertyType.PROPERTY, PropertyArity.ARRAY, - vectorToPropArray (propVec)); - _blocksList.add (prop); - - // Skip over the LZW minimum code size - readUnsignedByte (_dstream, this); - // Now read sub-blocks till we get one of zero size. - for (;;) { - int blockSize = readUnsignedByte (_dstream, this); - if (blockSize == 0) { - break; - } - skipBytes (_dstream, blockSize, this); - } - return true; + vectorToPropArray(propVec)); + _blocksList.add(prop); + + // Make a property with the global color table, if present + if (_globalColorTableFlag) { + short[] gctArray = new short[_globalColorTableSize]; + for (int i = 0; i < _globalColorTableSize; i++) { + gctArray[i] = (short) _dstream.readUnsignedByte(); + } + _blocksList.add( + new Property("GlobalColorTable", PropertyType.SHORT, PropertyArity.ARRAY, gctArray)); } - - /* Read an application extension block and fill in the appropriate - * properties */ - protected boolean readAppExtension (RepInfo info) - throws IOException - { - int blockSize = readUnsignedByte (_dstream, this); - if (blockSize != 11) { - info.setMessage (new ErrorMessage - (MessageConstants.GIF_HUL_1, - _nByte)); - info.setWellFormed (RepInfo.FALSE); - return false; - } - Vector propVec = new Vector (3); - StringBuffer appIdent = new StringBuffer (); - int i; - for (i = 0; i < 8; i++) { - appIdent.append((char) readUnsignedByte (_dstream, this)); - } - propVec.add (new Property ("ApplicationIdentifier", - PropertyType.STRING, - appIdent.toString ())); - - short[] appAuth = new short[3]; - for (i = 0; i < 3; i++) { - appAuth[i] = (short) readUnsignedByte (_dstream, this); - } - propVec.add (new Property ("ApplicationAuthenticationCode", - PropertyType.SHORT, - PropertyArity.ARRAY, - appAuth)); - - int appDataSize = 0; - // We are interested in the application extension for XMP. - if (("XMP Data".equals(appIdent.toString()) || - (debug_appIdentCaseInsens && - "xmp data".equalsIgnoreCase(appIdent.toString()))) && - appAuth[0] == (short) 'X' && - appAuth[1] == (short) 'M' && - appAuth[2] == (short) 'P') { - appDataSize = readXMP (); - } - else { - // Zip through the application data blocks, totalling their size - for (;;) { - int subBlockSize = readUnsignedByte (_dstream, this); - appDataSize += subBlockSize + 1; - if (subBlockSize == 0) { - break; - } - skipBytes (_dstream, subBlockSize, this); - } - } - propVec.add (new Property ("ApplicationDataSize", - PropertyType.INTEGER, - new Integer (appDataSize))); - - Property prop = new Property ("ApplicationExtension", - PropertyType.PROPERTY, - PropertyArity.ARRAY, - vectorToPropArray(propVec)); - _blocksList.add (prop); - return true; + return true; + } + + /* Read Graphic blocks, Special blocks, and the trailer. + * Return false if we get an error that prevents further + * progress, or if we encounter the Trailer. */ + protected boolean readBlock(RepInfo info) throws IOException { + int type; + try { + type = readUnsignedByte(_dstream, this); + } catch (EOFException e) { + // The spec isn't fully clear on whether a trailer is + // required, but seems to imply it is. + info.setWellFormed(RepInfo.FALSE); + info.setMessage(new ErrorMessage(MessageConstants.GIF_HUL_9, _nByte)); + return false; } - - /* Read an application extension block and fill in the appropriate - * properties. A comment extension should, by recommendation, - * contain ASCII, but actually can contain anything. Nulls - * are skipped, but everything else is included as is. */ - protected boolean readCommentExtension (RepInfo info) - throws IOException - { - StringBuffer buf = new StringBuffer (); - for (;;) { - int subBlockSize = readUnsignedByte (_dstream, this); - if (subBlockSize == 0) { - break; - } - for (int i = 0; i < subBlockSize; i++) { - int ch = readUnsignedByte (_dstream, this); - if (ch != 0) { - buf.append ((char) ch); - } - } - } - Property prop = new Property ("CommentExtension", - PropertyType.STRING, - buf.toString ()); - return true; + try { + switch (type) { + case EXT_BLOCK: + return readExtBlock(info); + case IMAGE_DESC: + return readImage(info); + case TRAILER: + return false; // end of file + default: + info.setWellFormed(RepInfo.FALSE); + info.setMessage(new ErrorMessage(MessageConstants.GIF_HUL_2, "Type = " + type, _nByte)); + return false; + } + } catch (EOFException e) { + // An EOF in the middle of a block is definitely a problem + info.setWellFormed(RepInfo.FALSE); + info.setMessage(new ErrorMessage(MessageConstants.GIF_HUL_10, _nByte)); + return false; } - - /* Read an application extension block and fill in the appropriate - * properties */ - protected boolean readPlainTextExtension (RepInfo info) - throws IOException - { - ++_numGraphicBlocks; - _gceCounter = 0; - int blockSize = readUnsignedByte (_dstream, this); - if (blockSize != 12) { - info.setMessage (new ErrorMessage - (MessageConstants.GIF_HUL_7, - _nByte)); - info.setWellFormed (RepInfo.FALSE); - return false; - } - - // A plain text extension requires a global color table - if (!_globalColorTableFlag) { - info.setMessage (new ErrorMessage - (MessageConstants.GIF_HUL_8, - _nByte)); - info.setValid (false); - } - Vector propVec = new Vector (9); - int textLeft = readUnsignedShort (_dstream); - propVec.add (new Property ("TextGridLeftPosition", - PropertyType.INTEGER, - new Integer (textLeft))); - - int textTop = readUnsignedShort (_dstream); - propVec.add (new Property ("TextGridTopPosition", - PropertyType.INTEGER, - new Integer (textTop))); - - int textGWidth = readUnsignedShort (_dstream); - propVec.add (new Property ("TextGridWidth", - PropertyType.INTEGER, - new Integer (textGWidth))); - - int textGHeight = readUnsignedShort (_dstream); - propVec.add (new Property ("TextGridHeight", - PropertyType.INTEGER, - new Integer (textGHeight))); - - int charCWidth = readUnsignedByte (_dstream, this); - propVec.add (new Property ("CharacterCellWidth", - PropertyType.SHORT, - new Short ((short) charCWidth))); - - int charCHeight = readUnsignedByte (_dstream, this); - propVec.add (new Property ("CharacterCellHeight", - PropertyType.SHORT, - new Short ((short) charCHeight))); - - int textFgIdx = readUnsignedByte (_dstream, this); - propVec.add (new Property ("TextForegroundColorIndex", - PropertyType.SHORT, - new Short ((short) textFgIdx))); - - int textBgIdx = readUnsignedByte (_dstream, this); - propVec.add (new Property ("TextBackgroundColorIndex", - PropertyType.SHORT, - new Short ((short) textBgIdx))); - - // Read the text data. The GIF recommendation states that - // characters less than 0X20 or greater than 0XF7 (why F7? - // It's on at least 2 independent copies of the spec, so - // apparently it's not a typo, or else is a well-entrenched one) - // should be represented as spaces. - StringBuffer buf = new StringBuffer (); - for (;;) { - int subBlockSize = readUnsignedByte (_dstream, this); - if (subBlockSize == 0) { - break; - } - for (int i = 0; i < subBlockSize; i++) { - int ch = readUnsignedByte (_dstream, this); - if (ch >= 0X20 || ch <= 0XF7) { - buf.append ((char) ch); - } - else { - buf.append (' '); - } - } - } - propVec.add (new Property ("PlainTextData", - PropertyType.STRING, - buf.toString ())); - - Property prop = new Property ("PlainTextExtension", - PropertyType.PROPERTY, - PropertyArity.ARRAY, - vectorToPropArray(propVec)); - _blocksList.add (prop); - return true; + } + + /* Read an extension block. + */ + protected boolean readExtBlock(RepInfo info) throws IOException { + int subtype = readUnsignedByte(_dstream, this); + switch (subtype) { + case APPLICATION_EXT: + return readAppExtension(info); + case COMMENT_EXT: + return readCommentExtension(info); + case GRAPHIC_CONTROL_EXT: + return readGraphicsCtlBlock(info); + case PLAIN_TEXT_EXT: + return readPlainTextExtension(info); + default: + info.setWellFormed(RepInfo.FALSE); + info.setMessage(new ErrorMessage(MessageConstants.GIF_HUL_3, "Type = " + subtype, _nByte)); + return false; + } + } + + /* Read an image descriptor and the subsequent data. + * We are positioned just after the type byte of the + * image descriptor. + */ + protected boolean readImage(RepInfo info) throws IOException { + ++_numGraphicBlocks; + Vector propVec = new Vector(7); + NisoImageMetadata niso = new NisoImageMetadata(); + Property nisoProp = new Property("NisoImageMetadata", PropertyType.NISOIMAGEMETADATA, niso); + + // GIF doesn't have a lot of options, so several + // NISO properties are constants. + niso.setMimeType("image/gif"); + niso.setByteOrder("little-endian"); + niso.setCompressionScheme(5); // LZW + niso.setColorSpace(3); // palette color + niso.setOrientation(1); // normal + niso.setBitsPerSample(new int[] {8}); + + _gceCounter = 0; + int leftPos = readUnsignedShort(_dstream); + propVec.add(new Property("ImageLeftPosition", PropertyType.INTEGER, new Integer(leftPos))); + int topPos = readUnsignedShort(_dstream); + propVec.add(new Property("ImageTopPosition", PropertyType.INTEGER, new Integer(topPos))); + int width = readUnsignedShort(_dstream); + niso.setImageWidth(width); + int height = readUnsignedShort(_dstream); + niso.setImageLength(height); + int packedFields = readUnsignedByte(_dstream, this); + int interlaceFlag = (packedFields & 0X40) >> 6; + propVec.add(addByteProperty("InterlaceFlag", interlaceFlag, GifStrings.INTERLACE_FLAG)); + int localColorTableFlag = (packedFields & 0X80) >> 7; + propVec.add( + addByteProperty( + "LocalColorTableFlag", localColorTableFlag, GifStrings.LOCAL_COLOR_TABLE_FLAG)); + int sortFlag = (packedFields & 0X20) >> 5; + propVec.add( + addByteProperty("LocalColorTableSortFlag", sortFlag, GifStrings.COLOR_TABLE_SORT_FLAG)); + int localColorTableSize = 0; + int rawLocalColorTableSize = packedFields & 0X7; + propVec.add( + new Property( + "LocalColorTableSize", PropertyType.SHORT, new Short((short) rawLocalColorTableSize))); + propVec.add(nisoProp); + if (localColorTableFlag != 0) { + localColorTableSize = 3 * (1 << (rawLocalColorTableSize + 1)); + skipBytes(_dstream, localColorTableSize, this); + } + Property prop = + new Property( + "ImageDescriptor", + PropertyType.PROPERTY, + PropertyArity.ARRAY, + vectorToPropArray(propVec)); + _blocksList.add(prop); + + // Skip over the LZW minimum code size + readUnsignedByte(_dstream, this); + // Now read sub-blocks till we get one of zero size. + for (; ; ) { + int blockSize = readUnsignedByte(_dstream, this); + if (blockSize == 0) { + break; + } + skipBytes(_dstream, blockSize, this); + } + return true; + } + + /* Read an application extension block and fill in the appropriate + * properties */ + protected boolean readAppExtension(RepInfo info) throws IOException { + int blockSize = readUnsignedByte(_dstream, this); + if (blockSize != 11) { + info.setMessage(new ErrorMessage(MessageConstants.GIF_HUL_1, _nByte)); + info.setWellFormed(RepInfo.FALSE); + return false; } + Vector propVec = new Vector(3); + StringBuffer appIdent = new StringBuffer(); + int i; + for (i = 0; i < 8; i++) { + appIdent.append((char) readUnsignedByte(_dstream, this)); + } + propVec.add(new Property("ApplicationIdentifier", PropertyType.STRING, appIdent.toString())); - /* Read a graphics control block and fill in the - * relevant properties */ - protected boolean readGraphicsCtlBlock (RepInfo info) - throws IOException - { - Vector propVec = new Vector (5); - if (++_gceCounter > 1) { - info.setMessage (new ErrorMessage - (MessageConstants.GIF_HUL_5, - _nByte)); - info.setWellFormed (RepInfo.FALSE); - } - int blockSize = readUnsignedByte (_dstream, this); - if (blockSize != 4) { - info.setMessage (new ErrorMessage - (MessageConstants.GIF_HUL_6, - _nByte)); - info.setWellFormed (RepInfo.FALSE); - return false; - } - int packedFields = readUnsignedByte (_dstream, this); - int dispMethod = (packedFields & 0X1C) >> 3; - propVec.add (addByteProperty ("DisposalMethod", - dispMethod, - GifStrings.GCE_DISPOSAL_METHOD)); - int userInputFlag = (packedFields & 2) >> 1; - propVec.add (addByteProperty ("UserInputFlag", - userInputFlag, - GifStrings.GCE_USER_INPUT_FLAG)); - int transparencyFlag = packedFields & 1; - propVec.add (addByteProperty ("TransparencyFlag", - transparencyFlag, - GifStrings.GCE_TRANSPARENCY_FLAG)); - int delayTime = readUnsignedShort(_dstream); - propVec.add (new Property ("DelayTime", - PropertyType.INTEGER, - new Integer (delayTime))); - int transIndex = readUnsignedByte (_dstream, this); - propVec.add (new Property ("TransparentColorIndex", - PropertyType.SHORT, - new Short ((short) transIndex))); - // Skip the block terminator. - readUnsignedByte (_dstream, this); - - Property prop = new Property ("GraphicControlExtension", - PropertyType.PROPERTY, - PropertyArity.ARRAY, - vectorToPropArray(propVec)); - _blocksList.add (prop); - return true; + short[] appAuth = new short[3]; + for (i = 0; i < 3; i++) { + appAuth[i] = (short) readUnsignedByte(_dstream, this); + } + propVec.add( + new Property( + "ApplicationAuthenticationCode", PropertyType.SHORT, PropertyArity.ARRAY, appAuth)); + + int appDataSize = 0; + // We are interested in the application extension for XMP. + if (("XMP Data".equals(appIdent.toString()) + || (debug_appIdentCaseInsens && "xmp data".equalsIgnoreCase(appIdent.toString()))) + && appAuth[0] == (short) 'X' + && appAuth[1] == (short) 'M' + && appAuth[2] == (short) 'P') { + appDataSize = readXMP(); + } else { + // Zip through the application data blocks, totalling their size + for (; ; ) { + int subBlockSize = readUnsignedByte(_dstream, this); + appDataSize += subBlockSize + 1; + if (subBlockSize == 0) { + break; + } + skipBytes(_dstream, subBlockSize, this); + } } + propVec.add( + new Property("ApplicationDataSize", PropertyType.INTEGER, new Integer(appDataSize))); - /* Read and process an XMP block and return the number of - * bytes read. When we reach a 0 byte, we've hit the - * end of the "magic" trailer. - */ - protected int readXMP () throws IOException - { - // Read bytes till we get to the trailer. Annoyingly, - // we don't know how big the byte buffer has to be, - // so we build a List of fixed-size buffers. We don't add - // curBuf to bufList till it's full. - List bufList = new LinkedList (); - final int bufsiz = 4096; - byte[] curBuf = new byte[bufsiz]; - int curBufOff = 0; - - // Fill up buffers till we hit a null. - for (;;) { - int ch = readUnsignedByte (_dstream, this); - if (ch == 0) { - // Read past second null, which concludes trailer - readUnsignedByte (_dstream, this); - break; - } - if (curBufOff == bufsiz) { - bufList.add (curBuf); - curBuf = new byte[bufsiz]; - curBufOff = 0; - } - curBuf[curBufOff++] = (byte) ch; - } + Property prop = + new Property( + "ApplicationExtension", + PropertyType.PROPERTY, + PropertyArity.ARRAY, + vectorToPropArray(propVec)); + _blocksList.add(prop); + return true; + } + + /* Read an application extension block and fill in the appropriate + * properties. A comment extension should, by recommendation, + * contain ASCII, but actually can contain anything. Nulls + * are skipped, but everything else is included as is. */ + protected boolean readCommentExtension(RepInfo info) throws IOException { + StringBuffer buf = new StringBuffer(); + for (; ; ) { + int subBlockSize = readUnsignedByte(_dstream, this); + if (subBlockSize == 0) { + break; + } + for (int i = 0; i < subBlockSize; i++) { + int ch = readUnsignedByte(_dstream, this); + if (ch != 0) { + buf.append((char) ch); + } + } + } + Property prop = new Property("CommentExtension", PropertyType.STRING, buf.toString()); + return true; + } + + /* Read an application extension block and fill in the appropriate + * properties */ + protected boolean readPlainTextExtension(RepInfo info) throws IOException { + ++_numGraphicBlocks; + _gceCounter = 0; + int blockSize = readUnsignedByte(_dstream, this); + if (blockSize != 12) { + info.setMessage(new ErrorMessage(MessageConstants.GIF_HUL_7, _nByte)); + info.setWellFormed(RepInfo.FALSE); + return false; + } - // Consolidate the buffers into one big buffer. - // The magic trailer is 258 bytes long, of which 256 - // bytes were actually read into the buffers, so we - // set our target to 256 bytes less than the total. - int appDataSize = bufList.size() * bufsiz + curBufOff + 2; - int totalSize = appDataSize - 258; - byte[] bigBuf = new byte[totalSize]; - int bigBufOff = 0; - ListIterator iter = bufList.listIterator(); - int i; - l1: - while (iter.hasNext ()) { - byte[] buf = (byte []) iter.next (); - for (i = 0; i < bufsiz; i++) { - bigBuf[bigBufOff++] = buf[i]; - if (bigBufOff >= totalSize) { - break l1; - } - } - } - // Finally curBuf gets added - for (i = 0; i < curBufOff; i++) { - if (bigBufOff >= totalSize) { - break; - } - bigBuf[bigBufOff++] = curBuf[i]; - } + // A plain text extension requires a global color table + if (!_globalColorTableFlag) { + info.setMessage(new ErrorMessage(MessageConstants.GIF_HUL_8, _nByte)); + info.setValid(false); + } + Vector propVec = new Vector(9); + int textLeft = readUnsignedShort(_dstream); + propVec.add(new Property("TextGridLeftPosition", PropertyType.INTEGER, new Integer(textLeft))); + + int textTop = readUnsignedShort(_dstream); + propVec.add(new Property("TextGridTopPosition", PropertyType.INTEGER, new Integer(textTop))); + + int textGWidth = readUnsignedShort(_dstream); + propVec.add(new Property("TextGridWidth", PropertyType.INTEGER, new Integer(textGWidth))); + + int textGHeight = readUnsignedShort(_dstream); + propVec.add(new Property("TextGridHeight", PropertyType.INTEGER, new Integer(textGHeight))); + + int charCWidth = readUnsignedByte(_dstream, this); + propVec.add( + new Property("CharacterCellWidth", PropertyType.SHORT, new Short((short) charCWidth))); + + int charCHeight = readUnsignedByte(_dstream, this); + propVec.add( + new Property("CharacterCellHeight", PropertyType.SHORT, new Short((short) charCHeight))); + + int textFgIdx = readUnsignedByte(_dstream, this); + propVec.add( + new Property("TextForegroundColorIndex", PropertyType.SHORT, new Short((short) textFgIdx))); + + int textBgIdx = readUnsignedByte(_dstream, this); + propVec.add( + new Property("TextBackgroundColorIndex", PropertyType.SHORT, new Short((short) textBgIdx))); + + // Read the text data. The GIF recommendation states that + // characters less than 0X20 or greater than 0XF7 (why F7? + // It's on at least 2 independent copies of the spec, so + // apparently it's not a typo, or else is a well-entrenched one) + // should be represented as spaces. + StringBuffer buf = new StringBuffer(); + for (; ; ) { + int subBlockSize = readUnsignedByte(_dstream, this); + if (subBlockSize == 0) { + break; + } + for (int i = 0; i < subBlockSize; i++) { + int ch = readUnsignedByte(_dstream, this); + if (ch >= 0X20 || ch <= 0XF7) { + buf.append((char) ch); + } else { + buf.append(' '); + } + } + } + propVec.add(new Property("PlainTextData", PropertyType.STRING, buf.toString())); - // OK. All that was just to get the XMP into one big byte - // buffer. Now process it. - final String badMetadata = "Invalid or ill-formed XMP metadata"; - try { - ByteArrayInputStream strm = - new ByteArrayInputStream (bigBuf); - ByteArrayXMPSource src = new ByteArrayXMPSource (strm, "UTF-8"); - - // Create an InputSource to feed the parser. - SAXParserFactory factory = - SAXParserFactory.newInstance(); - factory.setNamespaceAware (true); - XMLReader parser = factory.newSAXParser ().getXMLReader (); - XMPHandler handler = new XMPHandler (); - parser.setContentHandler (handler); - parser.setErrorHandler (handler); - // We have to parse twice. The first time, we may get - // an encoding change as part of an exception thrown. If this - // happens, we create a new InputSource with the encoding, and - // continue. - try { - parser.parse (src); - _xmpProp = src.makeProperty (); - return appDataSize; - } - catch (SAXException se) { - String msg = se.getMessage (); - if (msg != null && msg.startsWith ("ENC=")) { - String encoding = msg.substring (5); - try { - //The only permitted encoding is UTF-8, but - //that may come under various aliased names, - //so we assume the encoding is legitimate. - src = new ByteArrayXMPSource (strm, encoding); - parser.parse (src); - } - catch (UnsupportedEncodingException uee) { - return appDataSize; - } - } - _xmpProp = src.makeProperty (); - return appDataSize; - } - } - catch (Exception e) { - return appDataSize; - } + Property prop = + new Property( + "PlainTextExtension", + PropertyType.PROPERTY, + PropertyArity.ARRAY, + vectorToPropArray(propVec)); + _blocksList.add(prop); + return true; + } + + /* Read a graphics control block and fill in the + * relevant properties */ + protected boolean readGraphicsCtlBlock(RepInfo info) throws IOException { + Vector propVec = new Vector(5); + if (++_gceCounter > 1) { + info.setMessage(new ErrorMessage(MessageConstants.GIF_HUL_5, _nByte)); + info.setWellFormed(RepInfo.FALSE); + } + int blockSize = readUnsignedByte(_dstream, this); + if (blockSize != 4) { + info.setMessage(new ErrorMessage(MessageConstants.GIF_HUL_6, _nByte)); + info.setWellFormed(RepInfo.FALSE); + return false; + } + int packedFields = readUnsignedByte(_dstream, this); + int dispMethod = (packedFields & 0X1C) >> 3; + propVec.add(addByteProperty("DisposalMethod", dispMethod, GifStrings.GCE_DISPOSAL_METHOD)); + int userInputFlag = (packedFields & 2) >> 1; + propVec.add(addByteProperty("UserInputFlag", userInputFlag, GifStrings.GCE_USER_INPUT_FLAG)); + int transparencyFlag = packedFields & 1; + propVec.add( + addByteProperty("TransparencyFlag", transparencyFlag, GifStrings.GCE_TRANSPARENCY_FLAG)); + int delayTime = readUnsignedShort(_dstream); + propVec.add(new Property("DelayTime", PropertyType.INTEGER, new Integer(delayTime))); + int transIndex = readUnsignedByte(_dstream, this); + propVec.add( + new Property("TransparentColorIndex", PropertyType.SHORT, new Short((short) transIndex))); + // Skip the block terminator. + readUnsignedByte(_dstream, this); + + Property prop = + new Property( + "GraphicControlExtension", + PropertyType.PROPERTY, + PropertyArity.ARRAY, + vectorToPropArray(propVec)); + _blocksList.add(prop); + return true; + } + + /* Read and process an XMP block and return the number of + * bytes read. When we reach a 0 byte, we've hit the + * end of the "magic" trailer. + */ + protected int readXMP() throws IOException { + // Read bytes till we get to the trailer. Annoyingly, + // we don't know how big the byte buffer has to be, + // so we build a List of fixed-size buffers. We don't add + // curBuf to bufList till it's full. + List bufList = new LinkedList(); + final int bufsiz = 4096; + byte[] curBuf = new byte[bufsiz]; + int curBufOff = 0; + + // Fill up buffers till we hit a null. + for (; ; ) { + int ch = readUnsignedByte(_dstream, this); + if (ch == 0) { + // Read past second null, which concludes trailer + readUnsignedByte(_dstream, this); + break; + } + if (curBufOff == bufsiz) { + bufList.add(curBuf); + curBuf = new byte[bufsiz]; + curBufOff = 0; + } + curBuf[curBufOff++] = (byte) ch; + } + // Consolidate the buffers into one big buffer. + // The magic trailer is 258 bytes long, of which 256 + // bytes were actually read into the buffers, so we + // set our target to 256 bytes less than the total. + int appDataSize = bufList.size() * bufsiz + curBufOff + 2; + int totalSize = appDataSize - 258; + byte[] bigBuf = new byte[totalSize]; + int bigBufOff = 0; + ListIterator iter = bufList.listIterator(); + int i; + l1: + while (iter.hasNext()) { + byte[] buf = (byte[]) iter.next(); + for (i = 0; i < bufsiz; i++) { + bigBuf[bigBufOff++] = buf[i]; + if (bigBufOff >= totalSize) { + break l1; + } + } + } + // Finally curBuf gets added + for (i = 0; i < curBufOff; i++) { + if (bigBufOff >= totalSize) { + break; + } + bigBuf[bigBufOff++] = curBuf[i]; } - protected Property addByteProperty (String name, int value, - String [] labels) - { - if (!_je.getShowRawFlag ()) { - try { - return new Property (name, PropertyType.STRING, labels[value]); - } - catch (Exception e) { - // fall through - } + // OK. All that was just to get the XMP into one big byte + // buffer. Now process it. + final String badMetadata = "Invalid or ill-formed XMP metadata"; + try { + ByteArrayInputStream strm = new ByteArrayInputStream(bigBuf); + ByteArrayXMPSource src = new ByteArrayXMPSource(strm, "UTF-8"); + + // Create an InputSource to feed the parser. + SAXParserFactory factory = SAXParserFactory.newInstance(); + factory.setNamespaceAware(true); + XMLReader parser = factory.newSAXParser().getXMLReader(); + XMPHandler handler = new XMPHandler(); + parser.setContentHandler(handler); + parser.setErrorHandler(handler); + // We have to parse twice. The first time, we may get + // an encoding change as part of an exception thrown. If this + // happens, we create a new InputSource with the encoding, and + // continue. + try { + parser.parse(src); + _xmpProp = src.makeProperty(); + return appDataSize; + } catch (SAXException se) { + String msg = se.getMessage(); + if (msg != null && msg.startsWith("ENC=")) { + String encoding = msg.substring(5); + try { + // The only permitted encoding is UTF-8, but + // that may come under various aliased names, + // so we assume the encoding is legitimate. + src = new ByteArrayXMPSource(strm, encoding); + parser.parse(src); + } catch (UnsupportedEncodingException uee) { + return appDataSize; + } } - return new Property (name, PropertyType.BYTE, new Byte ((byte) value)); + _xmpProp = src.makeProperty(); + return appDataSize; + } + } catch (Exception e) { + return appDataSize; } - - - /* GIF is always little-endian, so readUnsignedShort can - * unambiguously drop its endian argument */ - protected int readUnsignedShort (DataInputStream stream) - throws IOException - { - return readUnsignedShort (stream, false, this); + } + + protected Property addByteProperty(String name, int value, String[] labels) { + if (!_je.getShowRawFlag()) { + try { + return new Property(name, PropertyType.STRING, labels[value]); + } catch (Exception e) { + // fall through + } } + return new Property(name, PropertyType.BYTE, new Byte((byte) value)); + } + + /* GIF is always little-endian, so readUnsignedShort can + * unambiguously drop its endian argument */ + protected int readUnsignedShort(DataInputStream stream) throws IOException { + return readUnsignedShort(stream, false, this); + } } diff --git a/jhove-modules/gif-hul/src/main/java/edu/harvard/hul/ois/jhove/module/gif/GifStrings.java b/jhove-modules/gif-hul/src/main/java/edu/harvard/hul/ois/jhove/module/gif/GifStrings.java index b453c4650..cd483bbd7 100644 --- a/jhove-modules/gif-hul/src/main/java/edu/harvard/hul/ois/jhove/module/gif/GifStrings.java +++ b/jhove-modules/gif-hul/src/main/java/edu/harvard/hul/ois/jhove/module/gif/GifStrings.java @@ -1,60 +1,52 @@ -/********************************************************************** - * Jhove - JSTOR/Harvard Object Validation Environment - * Copyright 2003 by JSTOR and the President and Fellows of Harvard College - **********************************************************************/ - +/** + * ******************************************************************** Jhove - JSTOR/Harvard Object + * Validation Environment Copyright 2003 by JSTOR and the President and Fellows of Harvard College + * ******************************************************************** + */ package edu.harvard.hul.ois.jhove.module.gif; /** - * A class for holding arrays of informative strings that will go into - * properties of a GIF object. + * A class for holding arrays of informative strings that will go into properties of a GIF object. */ public class GifStrings { - - /** Strings for presence or absence of global color table */ - public final static String[] GLOBAL_COLOR_TABLE_FLAG = - { "No global color table; background color index meaningless", - "Global color table follows; background color index meaningful" }; - - /** Strings for ordering or non-ordering of color table */ - public final static String[] COLOR_TABLE_SORT_FLAG = - { "Not ordered", - "Ordered by decreasing importance" }; - - /** GIF Capabilities Enquiry string: way in which the graphic is to - * be treated after being displayed */ - public final static String[] GCE_DISPOSAL_METHOD = - { "No disposal specified", - "Do not dispose", - "Restore to background color", - "Restore to previous" }; - - /** GIF Capabilities Enquiry string: user input - * expected or not */ - public final static String[] GCE_USER_INPUT_FLAG = - { "User input not expected", - "User input expected" }; - - /** GIF Capabilities Enquiry string: transparency - * index given or not */ - public final static String[] GCE_TRANSPARENCY_FLAG = - { "Transparent index is not given", - "Transparent index given" }; - - /** Local color table present in image or not */ - public final static String[] LOCAL_COLOR_TABLE_FLAG = - { "No local color table; use global table if available", - "Local color table follows" }; - - /** Image is interlaced or not */ - public final static String[] INTERLACE_FLAG = - { "Image is not interlaced", - "Image is interlaced" }; - - /** A private constructor just to make sure nobody - instantiates the class by mistake. */ - private GifStrings () - { - } + /** Strings for presence or absence of global color table */ + public static final String[] GLOBAL_COLOR_TABLE_FLAG = { + "No global color table; background color index meaningless", + "Global color table follows; background color index meaningful" + }; + + /** Strings for ordering or non-ordering of color table */ + public static final String[] COLOR_TABLE_SORT_FLAG = { + "Not ordered", "Ordered by decreasing importance" + }; + + /** + * GIF Capabilities Enquiry string: way in which the graphic is to be treated after being + * displayed + */ + public static final String[] GCE_DISPOSAL_METHOD = { + "No disposal specified", "Do not dispose", "Restore to background color", "Restore to previous" + }; + + /** GIF Capabilities Enquiry string: user input expected or not */ + public static final String[] GCE_USER_INPUT_FLAG = { + "User input not expected", "User input expected" + }; + + /** GIF Capabilities Enquiry string: transparency index given or not */ + public static final String[] GCE_TRANSPARENCY_FLAG = { + "Transparent index is not given", "Transparent index given" + }; + + /** Local color table present in image or not */ + public static final String[] LOCAL_COLOR_TABLE_FLAG = { + "No local color table; use global table if available", "Local color table follows" + }; + + /** Image is interlaced or not */ + public static final String[] INTERLACE_FLAG = {"Image is not interlaced", "Image is interlaced"}; + + /** A private constructor just to make sure nobody instantiates the class by mistake. */ + private GifStrings() {} } diff --git a/jhove-modules/gif-hul/src/main/java/edu/harvard/hul/ois/jhove/module/gif/MessageConstants.java b/jhove-modules/gif-hul/src/main/java/edu/harvard/hul/ois/jhove/module/gif/MessageConstants.java index 064ed1bdf..b93020748 100644 --- a/jhove-modules/gif-hul/src/main/java/edu/harvard/hul/ois/jhove/module/gif/MessageConstants.java +++ b/jhove-modules/gif-hul/src/main/java/edu/harvard/hul/ois/jhove/module/gif/MessageConstants.java @@ -5,31 +5,20 @@ import edu.harvard.hul.ois.jhove.messages.JhoveMessages; public enum MessageConstants { - INSTANCE; - public static final JhoveMessageFactory messageFactory = JhoveMessages - .getInstance("edu.harvard.hul.ois.jhove.module.gif.ErrorMessages"); + INSTANCE; + public static final JhoveMessageFactory messageFactory = + JhoveMessages.getInstance("edu.harvard.hul.ois.jhove.module.gif.ErrorMessages"); - /** - * Error messages - */ - public static final JhoveMessage GIF_HUL_1 = messageFactory - .getMessage("GIF-HUL-1"); - public static final JhoveMessage GIF_HUL_2 = messageFactory - .getMessage("GIF-HUL-2"); - public static final JhoveMessage GIF_HUL_3 = messageFactory - .getMessage("GIF-HUL-3"); - public static final JhoveMessage GIF_HUL_4 = messageFactory - .getMessage("GIF-HUL-4"); - public static final JhoveMessage GIF_HUL_5 = messageFactory - .getMessage("GIF-HUL-5"); - public static final JhoveMessage GIF_HUL_6 = messageFactory - .getMessage("GIF-HUL-6"); - public static final JhoveMessage GIF_HUL_7 = messageFactory - .getMessage("GIF-HUL-7"); - public static final JhoveMessage GIF_HUL_8 = messageFactory - .getMessage("GIF-HUL-8"); - public static final JhoveMessage GIF_HUL_9 = messageFactory - .getMessage("GIF-HUL-9"); - public static final JhoveMessage GIF_HUL_10 = messageFactory - .getMessage("GIF-HUL-10"); + /** Error messages */ + public static final JhoveMessage GIF_HUL_1 = messageFactory.getMessage("GIF-HUL-1"); + + public static final JhoveMessage GIF_HUL_2 = messageFactory.getMessage("GIF-HUL-2"); + public static final JhoveMessage GIF_HUL_3 = messageFactory.getMessage("GIF-HUL-3"); + public static final JhoveMessage GIF_HUL_4 = messageFactory.getMessage("GIF-HUL-4"); + public static final JhoveMessage GIF_HUL_5 = messageFactory.getMessage("GIF-HUL-5"); + public static final JhoveMessage GIF_HUL_6 = messageFactory.getMessage("GIF-HUL-6"); + public static final JhoveMessage GIF_HUL_7 = messageFactory.getMessage("GIF-HUL-7"); + public static final JhoveMessage GIF_HUL_8 = messageFactory.getMessage("GIF-HUL-8"); + public static final JhoveMessage GIF_HUL_9 = messageFactory.getMessage("GIF-HUL-9"); + public static final JhoveMessage GIF_HUL_10 = messageFactory.getMessage("GIF-HUL-10"); } diff --git a/jhove-modules/gif-hul/src/main/java/edu/harvard/hul/ois/jhove/module/gif/package-info.java b/jhove-modules/gif-hul/src/main/java/edu/harvard/hul/ois/jhove/module/gif/package-info.java index c444f4ea8..e518fdcb8 100644 --- a/jhove-modules/gif-hul/src/main/java/edu/harvard/hul/ois/jhove/module/gif/package-info.java +++ b/jhove-modules/gif-hul/src/main/java/edu/harvard/hul/ois/jhove/module/gif/package-info.java @@ -1,4 +1,2 @@ -/** - * Contains supporting classes for the GIF-HUL module. - */ -package edu.harvard.hul.ois.jhove.module.gif; \ No newline at end of file +/** Contains supporting classes for the GIF-HUL module. */ +package edu.harvard.hul.ois.jhove.module.gif; diff --git a/jhove-modules/html-hul/src/main/java/edu/harvard/hul/ois/jhove/module/HtmlModule.java b/jhove-modules/html-hul/src/main/java/edu/harvard/hul/ois/jhove/module/HtmlModule.java index 70cf09a91..9af6a6c9a 100644 --- a/jhove-modules/html-hul/src/main/java/edu/harvard/hul/ois/jhove/module/HtmlModule.java +++ b/jhove-modules/html-hul/src/main/java/edu/harvard/hul/ois/jhove/module/HtmlModule.java @@ -1,33 +1,22 @@ -/********************************************************************** - * Jhove - JSTOR/Harvard Object Validation Environment Copyright 2004-2007 by - * JSTOR and the President and Fellows of Harvard College +/** + * ******************************************************************** Jhove - JSTOR/Harvard Object + * Validation Environment Copyright 2004-2007 by JSTOR and the President and Fellows of Harvard + * College * - * This program 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 2 of the License, or (at your option) any - * later version. + *

This program 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 2 + * of the License, or (at your option) any later version. * - * This program 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. + *

This program 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. * - * You should have received a copy of the GNU Lesser General Public License - * along with this program; if not, write to the Free Software Foundation, Inc., - * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - **********************************************************************/ - + *

You should have received a copy of the GNU Lesser General Public License along with this + * program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA ******************************************************************** + */ package edu.harvard.hul.ois.jhove.module; -import java.io.DataInputStream; -import java.io.EOFException; -import java.io.File; -import java.io.IOException; -import java.io.InputStream; -import java.io.UnsupportedEncodingException; -import java.util.Iterator; -import java.util.List; - import edu.harvard.hul.ois.jhove.Agent; import edu.harvard.hul.ois.jhove.AgentType; import edu.harvard.hul.ois.jhove.Document; @@ -64,675 +53,681 @@ import edu.harvard.hul.ois.jhove.module.html.Token; import edu.harvard.hul.ois.jhove.module.html.TokenMgrError; import edu.harvard.hul.ois.jhove.module.xml.HtmlMetadata; +import java.io.DataInputStream; +import java.io.EOFException; +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.io.UnsupportedEncodingException; +import java.util.Iterator; +import java.util.List; /** * Module for identification and validation of HTML files. * - * HTML is different from most of the other documents in that sloppy - * construction is practically assumed in the specification. This module attempt - * to report as many errors as possible and recover reasonably from errors. To - * do this, there is more heuristic behavior built into this module than into - * the more straightforward ones. + *

HTML is different from most of the other documents in that sloppy construction is practically + * assumed in the specification. This module attempt to report as many errors as possible and + * recover reasonably from errors. To do this, there is more heuristic behavior built into this + * module than into the more straightforward ones. * - * XHTML is recognized by this module, but is handed off to the XML module for - * processing. If the XML module is missing (which it shouldn't be if you've - * installed the JHOVE application without modifications), this won't be able to - * deal with XHTML files. + *

XHTML is recognized by this module, but is handed off to the XML module for processing. If the + * XML module is missing (which it shouldn't be if you've installed the JHOVE application without + * modifications), this won't be able to deal with XHTML files. * - * HTML should be placed ahead of XML in the module order. If the XML module - * sees an XHTML file first, it will recognize it as XHTML, but won't be able to - * report the complete properties. + *

HTML should be placed ahead of XML in the module order. If the XML module sees an XHTML file + * first, it will recognize it as XHTML, but won't be able to report the complete properties. * - * The HTML module uses code created with the JavaCC parser generator and - * lexical analyzer generator. There is apparently a bug in JavaCC which causes - * blank lines not to be counted in certain cases, causing lexical errors to be - * reported with incorrect line numbers. + *

The HTML module uses code created with the JavaCC parser generator and lexical analyzer + * generator. There is apparently a bug in JavaCC which causes blank lines not to be counted in + * certain cases, causing lexical errors to be reported with incorrect line numbers. * * @author Gary McGath - * */ public class HtmlModule extends ModuleBase { - /****************************************************************** - * PRIVATE CLASS FIELDS. - ******************************************************************/ - private static final String TRANSITIONAL = "Transitional"; - private static final String STRICT = "Strict"; - private static final String FRAMESET = "Frameset"; - private static final String HTML_4_0 = "HTML 4.0"; - private static final String HTML_4_01 = "HTML 4.01"; - private static final String XHTML_1_0 = "XHTML 1.0"; - - private static final String NAME = "HTML-hul"; - private static final String RELEASE = "1.4.1"; - private static final int [] DATE = { 2019, 04, 17 }; - private static final String[] FORMAT = { "HTML" }; - private static final String COVERAGE = "HTML 3.2, HTML 4.0 Strict," - + "HTML 4.0 Transitional, HTML 4.0 Frameset, " - + "HTML 4.01 Strict, HTML 4.01 Transitional, HTML 4.01 Frameset" - + "XHTML 1.0 Strict, XHTML 1.0 Transitional, XHTML 1.0 Frameset" - + "XHTML 1.1"; - - private static final String[] MIMETYPE = { "text/html" }; - private static final String WELLFORMED = "An HTML file is well-formed " - + "if it meets the criteria defined in the HTML 3.2 specification " - + "(W3C Recommendation, 14-Jan-1997), " - + "the HTML 4.0 specification (W3C Recommendation, 24-Apr-1998, " - + "the HTML 4.01 specification (W3C Recommendation, 24-Dec-1999, " - + "the XHTML 1.0 specification (W3C Recommendation, 26-Jan-2000, " - + "revised 1-Aug-2002, " - + "or the XHTML 1.1 specification (W3C Recommendation, 31-May-2001"; - private static final String VALIDITY = "An HTML file is valid if it is " - + "well-formed and has a valid DOCTYPE declaration."; - private static final String REPINFO = "Languages, title, META tags, " - + "frames, links, scripts, images, citations, defined terms, " - + "abbreviations, entities, Unicode entity blocks"; - private static final String NOTE = ""; - private static final String RIGHTS = "Copyright 2004-2007 by JSTOR and " - + "the President and Fellows of Harvard College. " - + "Released under the GNU Lesser General Public License."; - - /****************************************************************** - * PRIVATE INSTANCE FIELDS. - ******************************************************************/ - - /* Doctype extracted from document */ - protected String _doctype; - - /* Constants for the recognized flavors of HTML */ - public static final int HTML_3_2 = 1, HTML_4_0_STRICT = 2, - HTML_4_0_FRAMESET = 3, HTML_4_0_TRANSITIONAL = 4, - HTML_4_01_STRICT = 5, HTML_4_01_FRAMESET = 6, - HTML_4_01_TRANSITIONAL = 7, XHTML_1_0_STRICT = 8, - XHTML_1_0_TRANSITIONAL = 9, XHTML_1_0_FRAMESET = 10, XHTML_1_1 = 11; - - /* Profile names, matching the above indices */ - private static final String[] PROFILENAMES = { null, null, // there are no - // profiles for - // HTML 3.2 - STRICT, FRAMESET, TRANSITIONAL, STRICT, FRAMESET, TRANSITIONAL, - STRICT, FRAMESET, TRANSITIONAL, null // there - // are no - // profiles - // for - // XHTML - // 1.1 - }; - - /* Version names, matching the above indices */ - private static final String[] VERSIONNAMES = { null, "HTML 3.2", HTML_4_0, - HTML_4_0, HTML_4_0, HTML_4_01, HTML_4_01, HTML_4_01, XHTML_1_0, - XHTML_1_0, XHTML_1_0, "XHTML 1.1" }; - - /* Flag to know if the property TextMDMetadata is to be added */ - protected boolean _withTextMD = false; - /* Hold the information needed to generate a textMD metadata fragment */ - protected TextMDMetadata _textMD; - - /****************************************************************** - * CLASS CONSTRUCTOR. - ******************************************************************/ - /** - * Instantiate an HtmlModule object. - */ - public HtmlModule() { - super(NAME, RELEASE, DATE, FORMAT, COVERAGE, MIMETYPE, WELLFORMED, - VALIDITY, REPINFO, NOTE, RIGHTS, false); - - _vendor = Agent.harvardInstance(); - - /* HTML 3.2 spec */ - Document doc = new Document("HTML 3.2 Reference Specification", - DocumentType.REPORT); - Agent w3cAgent = Agent.newW3CInstance(); - doc.setPublisher(w3cAgent); - - Agent dRaggett = new Agent.Builder("Dave Raggett", AgentType.OTHER) - .build(); - doc.setAuthor(dRaggett); - - doc.setDate("1997-01-14"); - doc.setIdentifier( - new Identifier("http://www.w3c.org/TR/REC-html32-19970114", - IdentifierType.URL)); - _specification.add(doc); - - /* HTML 4.0 spec */ - doc = new Document("HTML 4.0 Specification", DocumentType.REPORT); - doc.setPublisher(w3cAgent); - doc.setAuthor(dRaggett); - Agent leHors = new Agent.Builder("Arnaud Le Hors", AgentType.OTHER) - .build(); - doc.setAuthor(leHors); - Agent jacobs = new Agent.Builder("Ian Jacobs", AgentType.OTHER).build(); - doc.setAuthor(jacobs); - doc.setDate("1998-04-24"); - doc.setIdentifier( - new Identifier("http://www.w3.org/TR/1998/REC-html40-19980424/", - IdentifierType.URL)); - _specification.add(doc); - - /* HTML 4.01 spec */ - doc = new Document("HTML 4.01 Specification", DocumentType.REPORT); - doc.setPublisher(w3cAgent); - doc.setAuthor(dRaggett); - doc.setAuthor(leHors); - doc.setAuthor(jacobs); - doc.setDate("1999-12-24"); - doc.setIdentifier(new Identifier( - "http://www.w3.org/TR/1999/REC-html401-19991224/", - IdentifierType.URL)); - _specification.add(doc); - - /* XHTML 1.0 spec */ - doc = new Document( - "XHTML(TM) 1.0 The Extensible HyperText Markup Language " - + "(Second Edition)", - DocumentType.REPORT); - doc.setPublisher(w3cAgent); - doc.setDate("01-08-2002"); - doc.setIdentifier(new Identifier("http://www.w3.org/TR/xhtml1/", - IdentifierType.URL)); - _specification.add(doc); - - /* XHTML 1.1 spec */ - doc = new Document(" XHTML(TM) 1.1 - Module-based XHTML", - DocumentType.REPORT); - doc.setPublisher(w3cAgent); - doc.setDate("31-05-2001"); - doc.setIdentifier(new Identifier( - "http://www.w3.org/TR/2001/REC-xhtml11-20010531/", - IdentifierType.URL)); - _specification.add(doc); - - /* - * XHTML 2.0 spec -- NOT included yet; this is presented in - * "conditionalized-out" form just as a note for future expansion. - * if (false) { - * doc = new Document("XHTML 2.0, W3C Working Draft", - * DocumentType.OTHER); - * doc.setPublisher(w3cAgent); - * doc.setDate("22-07-2004"); - * doc.setIdentifier(new Identifier( - * "http://www.w3.org/TR/2004/WD-xhtml2-20040722/", - * IdentifierType.URL)); - * _specification.add(doc); - * } - */ - - Signature sig = new ExternalSignature(".html", SignatureType.EXTENSION, - SignatureUseType.OPTIONAL); - _signature.add(sig); - sig = new ExternalSignature(".htm", SignatureType.EXTENSION, - SignatureUseType.OPTIONAL); - _signature.add(sig); - } - - /** - * Parse the content of a purported HTML stream digital object and store the - * results in RepInfo. - * - * - * @param stream - * An InputStream, positioned at its beginning, which is - * generated from the object to be parsed. If multiple calls to - * parse are made on the basis of a nonzero value - * being returned, a new InputStream must be provided each time. - * - * @param info - * A fresh (on the first call) RepInfo object which will be - * modified to reflect the results of the parsing If multiple - * calls to parse are made on the basis of a nonzero - * value being returned, the same RepInfo object should be passed - * with each call. - * - * @param parseIndex - * Must be 0 in first call to parse. If - * parse returns a nonzero value, it must be called - * again with parseIndex equal to that return value. - * - * @return parseInt - */ - @Override - public int parse(InputStream stream, RepInfo info, int parseIndex) { - if (parseIndex != 0) { - // Coming in with parseIndex = 1 indicates that we've determined - // this is XHTML; so we invoke the XML module to parse it. - // If parseIndex is 100, this is the first invocation of the - // XML module, so we call it with 0; otherwise we call it with - // the value of parseIndex. - if (isXmlAvailable()) { - edu.harvard.hul.ois.jhove.module.XmlModule xmlMod = new edu.harvard.hul.ois.jhove.module.XmlModule(); - if (parseIndex == 100) { - parseIndex = 0; - } - xmlMod.setApp(_app); - xmlMod.setBase(_je); - xmlMod.setDefaultParams(_defaultParams); - try { - xmlMod.applyDefaultParams(); - } catch (Exception e) { - // really shouldn't happen - } - xmlMod.setXhtmlDoctype(_doctype); - return xmlMod.parse(stream, info, parseIndex); - } - // The XML module shouldn't be missing from any installation, - // but someone who really wanted to could remove it. In - // that case, you deserve what you get. - info.setMessage(new ErrorMessage( - MessageConstants.JHOVE_1)); - info.setWellFormed(false); // Treat it as completely wrong - return 0; - } - /* parseIndex = 0, first call only */ - _doctype = null; - // Test if textMD is to be generated - if (_defaultParams != null) { - Iterator iter = _defaultParams.iterator(); - while (iter.hasNext()) { - String param = (String) iter.next(); - if ("withtextmd=true".equalsIgnoreCase(param)) { - _withTextMD = true; - } - } - } - - initParse(); - info.setFormat(_format[0]); - info.setMimeType(_mimeType[0]); - info.setModule(this); - - if (_textMD == null || parseIndex == 0) { - _textMD = new TextMDMetadata(); - } - /* - * We may have already done the checksums while converting a temporary - * file. - */ - setupDataStream(stream, info); - - ParseHtml parser; - HtmlMetadata metadata = null; - HtmlCharStream cstream; - try { - cstream = new HtmlCharStream(_dstream, "ISO-8859-1"); - parser = new ParseHtml(this, cstream); - } catch (UnsupportedEncodingException e) { - info.setMessage(new ErrorMessage( - MessageConstants.JHOVE_2, e.getMessage())); - info.setWellFormed(false); - return 0; // shouldn't happen! - } - int type = 0; - try { - List elements = parser.HtmlDoc(); - if (elements.isEmpty()) { - // Consider an empty document bad - info.setWellFormed(false); - info.setMessage(new ErrorMessage( - MessageConstants.JHOVE_3)); - return 0; - } - type = checkDoctype(elements); - if (type < 0) { - info.setWellFormed(false); - info.setMessage(new ErrorMessage( - MessageConstants.HTML_HUL_15)); - return 0; - } - /* - * Check if there is at least one html, head, body or title tag. A - * plain text document might be interpreted as a single PCDATA, - * which is in some ethereal sense well-formed HTML, but it's - * pointless to consider it such. It might also use angle brackets - * as a text delimiter, and that shouldn't count as HTML either. - */ - boolean hasElements = false; - Iterator iter = elements.iterator(); - while (iter.hasNext()) { - Object o = iter.next(); - if (o instanceof JHOpenTag) { - String name = ((JHOpenTag) o).getName(); - if ("html".equals(name) || "head".equals(name) - || "body".equals(name) || "title".equals(name)) { - hasElements = true; - } - break; - } - } - if (!hasElements) { - info.setMessage(new ErrorMessage( - MessageConstants.HTML_HUL_17)); - info.setWellFormed(false); - return 0; - } - - // CRLF from HtmlCharStream ... - String lineEnd = cstream.getKindOfLineEnd(); - if (lineEnd == null) { - info.setMessage( - new InfoMessage(MessageConstants.INF_EOL_TYPE_UNDET)); - _textMD.setLinebreak(TextMDMetadata.NILL); - } else if ("CR".equalsIgnoreCase(lineEnd)) { - _textMD.setLinebreak(TextMDMetadata.LINEBREAK_CR); - } else if ("LF".equalsIgnoreCase(lineEnd)) { - _textMD.setLinebreak(TextMDMetadata.LINEBREAK_LF); - } else if ("CRLF".equalsIgnoreCase(lineEnd)) { - _textMD.setLinebreak(TextMDMetadata.LINEBREAK_CRLF); - } - - if (type == 0) { - /* - * If we can't find a doctype, it still might be XHTML if the - * elements start with an XML declaration and the root element - * is "html" - */ - switch (seemsToBeXHTML(elements)) { - case 0: // Not XML - break; // fall through - case 1: // XML but not HTML - info.setMessage(new ErrorMessage( - MessageConstants.HTML_HUL_14)); - info.setWellFormed(false); - return 0; - case 2: // probably XHTML - return 100; - default : - break; - } - info.setMessage(new ErrorMessage( - MessageConstants.HTML_HUL_16)); - info.setValid(false); - // But keep going - } - - HtmlDocDesc docDesc = null; - switch (type) { - case HTML_3_2: - - - case HTML_4_0_FRAMESET: - docDesc = new Html4_0FrameDocDesc(); - _textMD.setMarkup_basis("HTML"); - _textMD.setMarkup_basis_version("4.0"); - break; - case HTML_4_0_TRANSITIONAL: - docDesc = new Html4_0TransDocDesc(); - _textMD.setMarkup_basis("HTML"); - _textMD.setMarkup_basis_version("4.0"); - break; - case HTML_4_0_STRICT: - docDesc = new Html4_0StrictDocDesc(); - _textMD.setMarkup_basis("HTML"); - _textMD.setMarkup_basis_version("4.0"); - break; - case HTML_4_01_FRAMESET: - docDesc = new Html4_01FrameDocDesc(); - _textMD.setMarkup_basis("HTML"); - _textMD.setMarkup_basis_version("4.01"); - break; - case HTML_4_01_TRANSITIONAL: - docDesc = new Html4_01TransDocDesc(); - _textMD.setMarkup_basis("HTML"); - _textMD.setMarkup_basis_version("4.01"); - break; - case HTML_4_01_STRICT: - docDesc = new Html4_01StrictDocDesc(); - _textMD.setMarkup_basis("HTML"); - _textMD.setMarkup_basis_version("4.01"); - break; - case XHTML_1_0_STRICT: - case XHTML_1_0_TRANSITIONAL: - case XHTML_1_0_FRAMESET: - case XHTML_1_1: - // Force a second call to parse as XML. 100 is a - // magic code for the first XML call. - return 100; - } - _textMD.setMarkup_language(_doctype); - if (docDesc == null) { - info.setMessage(new InfoMessage( - MessageConstants.INF_HTML_VER_UNSPPRTD)); - docDesc = new Html3_2DocDesc(); - } - docDesc.validate(elements, info); - metadata = docDesc.getMetadata(); - - // Try to get the charset from the meta Content - if (metadata.getCharset() != null) { - _textMD.setCharset(metadata.getCharset()); - } else { - _textMD.setCharset(TextMDMetadata.CHARSET_ISO8859_1); - } - String textMDEncoding = _textMD.getCharset(); - if (textMDEncoding.contains("UTF")) { - _textMD.setByte_order(_bigEndian ? TextMDMetadata.BYTE_ORDER_BIG - : TextMDMetadata.BYTE_ORDER_LITTLE); - _textMD.setByte_size("8"); - _textMD.setCharacter_size("variable"); - } else { - _textMD.setByte_order(_bigEndian ? TextMDMetadata.BYTE_ORDER_BIG - : TextMDMetadata.BYTE_ORDER_LITTLE); - _textMD.setByte_size("8"); - _textMD.setCharacter_size("1"); - } - } catch (ParseException e) { - Token t = e.currentToken; - info.setMessage(new ErrorMessage( - MessageConstants.HTML_HUL_18, - "Line = " + t.beginLine + ", column = " + t.beginColumn)); - info.setWellFormed(false); - } catch (TokenMgrError f) { - info.setMessage(new ErrorMessage( - MessageConstants.HTML_HUL_19, - f.getLocalizedMessage())); - info.setWellFormed(false); - } - - if (info.getWellFormed() == RepInfo.FALSE) { - return 0; - } - - if (type != 0) { - if (PROFILENAMES[type] != null) { - info.setProfile(PROFILENAMES[type]); - } - info.setVersion(VERSIONNAMES[type]); - } - - if (metadata != null) { - Property property = metadata - .toProperty(_withTextMD ? _textMD : null); - if (property != null) { - info.setProperty(property); - } - } - - // Set the checksums in the report if they're calculated - setChecksums(this._ckSummer, info); - - return 0; - } - - /** - * Check if the digital object conforms to this Module's internal signature - * information. - * - * HTML is one of the most ill-defined of any open formats, so checking a - * "signature" really means using some heuristics. The only required tag is - * TITLE, but that could occur well into the file. So we look for any of - * three strings -- taking into account case-independence and white space -- - * within the first sigBytes bytes, and call that a signature check. - * - * @param file - * A File object for the object being parsed - * @param stream - * An InputStream, positioned at its beginning, which is - * generated from the object to be parsed - * @param info - * A fresh RepInfo object which will be modified to reflect the - * results of the test - * - * @throws IOException - */ - @Override - public void checkSignatures(File file, InputStream stream, RepInfo info) - throws IOException { - info.setFormat(_format[0]); - info.setMimeType(_mimeType[0]); - info.setModule(this); - char[][] sigtext = new char[3][]; - sigtext[0] = "= 2) { - firstElem = (JHElement) elements.get(1); - } - if (!(firstElem instanceof JHDoctype)) { - return 0; // no DOCTYPE found - } - List dt = ((JHDoctype) firstElem).getDoctypeElements(); - if (dt.size() < 3) { - return 0; - } - try { - // Is DOCTYPE case sensitive? Assume not. - String str = ((String) dt.get(0)).toUpperCase(); - if (!"HTML".equals(str)) { - // It's not HTML - return -1; - } - str = ((String) dt.get(1)).toUpperCase(); - if (!"PUBLIC".equals(str)) { - return 0; - } - str = stripQuotes(((String) dt.get(2)).toUpperCase()); - _doctype = str; - if (null - != str) switch (str) { - case "-//W3C//DTD HTML 3.2 FINAL//EN": - case "-//W3C//DTD HTML 3.2//EN": - return HTML_3_2; - case "-//W3C//DTD HTML 4.0//EN": - return HTML_4_0_STRICT; - case "-//W3C//DTD HTML 4.0 TRANSITIONAL//EN": - return HTML_4_0_TRANSITIONAL; - case "-//W3C//DTD HTML 4.0 FRAMESET//EN": - return HTML_4_0_FRAMESET; - case "-//W3C//DTD HTML 4.01//EN": - return HTML_4_01_STRICT; - case "-//W3C//DTD HTML 4.01 TRANSITIONAL//EN": - return HTML_4_01_TRANSITIONAL; - case "-//W3C//DTD HTML 4.01 FRAMESET//EN": - return HTML_4_01_FRAMESET; - default: - break; - } - } catch (Exception e) { - // Really shouldn't happen, but if it does we've got - // a bad doctype - return 0; - } - return 0; - } - - /* - * See if this document, even if it lacks a doctype, is most likely XHTML. - * The test is that the document starts with an XML declaration and has - * "html" for its first tag. - * - * Returns: 0 if there's no XML declaration 1 if there's an XML declaration - * but no html tag; in this case it's probably some other kind of XML 2 if - * there's an XML declaration and an html tag - */ - protected int seemsToBeXHTML(List elements) { - JHElement elem; - try { - elem = (JHElement) elements.get(0); - if (!(elem instanceof JHXmlDecl)) { - return 0; - } - Iterator iter = elements.iterator(); - while (iter.hasNext()) { - elem = (JHElement) iter.next(); - if (elem instanceof JHOpenTag) { - JHOpenTag tag = (JHOpenTag) elem; - return ("html".equals(tag.getName()) ? 2 : 1); - } - } - } catch (Exception e) { - return 0; // document must be really empty - } - return 1; - } - - /* - * Remove quotes from the beginning and end of a string. If it doesn't have - * quotes in both places, leave it alone. - */ - protected String stripQuotes(String str) { - int len = str.length(); - if (str.charAt(0) == '"' && str.charAt(len - 1) == '"') { - return str.substring(1, len - 1); - } - return str; - } - - /* - * Checks if the XML module is available. - */ - protected static boolean isXmlAvailable() { - try { - Class.forName("edu.harvard.hul.ois.jhove.module.XmlModule"); - return true; - } catch (Exception e) { - return false; - } - } - + /** + * **************************************************************** PRIVATE CLASS FIELDS. + * **************************************************************** + */ + private static final String TRANSITIONAL = "Transitional"; + + private static final String STRICT = "Strict"; + private static final String FRAMESET = "Frameset"; + private static final String HTML_4_0 = "HTML 4.0"; + private static final String HTML_4_01 = "HTML 4.01"; + private static final String XHTML_1_0 = "XHTML 1.0"; + + private static final String NAME = "HTML-hul"; + private static final String RELEASE = "1.4.1"; + private static final int[] DATE = {2019, 04, 17}; + private static final String[] FORMAT = {"HTML"}; + private static final String COVERAGE = + "HTML 3.2, HTML 4.0 Strict," + + "HTML 4.0 Transitional, HTML 4.0 Frameset, " + + "HTML 4.01 Strict, HTML 4.01 Transitional, HTML 4.01 Frameset" + + "XHTML 1.0 Strict, XHTML 1.0 Transitional, XHTML 1.0 Frameset" + + "XHTML 1.1"; + + private static final String[] MIMETYPE = {"text/html"}; + private static final String WELLFORMED = + "An HTML file is well-formed " + + "if it meets the criteria defined in the HTML 3.2 specification " + + "(W3C Recommendation, 14-Jan-1997), " + + "the HTML 4.0 specification (W3C Recommendation, 24-Apr-1998, " + + "the HTML 4.01 specification (W3C Recommendation, 24-Dec-1999, " + + "the XHTML 1.0 specification (W3C Recommendation, 26-Jan-2000, " + + "revised 1-Aug-2002, " + + "or the XHTML 1.1 specification (W3C Recommendation, 31-May-2001"; + private static final String VALIDITY = + "An HTML file is valid if it is " + "well-formed and has a valid DOCTYPE declaration."; + private static final String REPINFO = + "Languages, title, META tags, " + + "frames, links, scripts, images, citations, defined terms, " + + "abbreviations, entities, Unicode entity blocks"; + private static final String NOTE = ""; + private static final String RIGHTS = + "Copyright 2004-2007 by JSTOR and " + + "the President and Fellows of Harvard College. " + + "Released under the GNU Lesser General Public License."; + + /** + * **************************************************************** PRIVATE INSTANCE FIELDS. + * **************************************************************** + */ + + /* Doctype extracted from document */ + protected String _doctype; + + /* Constants for the recognized flavors of HTML */ + public static final int HTML_3_2 = 1, + HTML_4_0_STRICT = 2, + HTML_4_0_FRAMESET = 3, + HTML_4_0_TRANSITIONAL = 4, + HTML_4_01_STRICT = 5, + HTML_4_01_FRAMESET = 6, + HTML_4_01_TRANSITIONAL = 7, + XHTML_1_0_STRICT = 8, + XHTML_1_0_TRANSITIONAL = 9, + XHTML_1_0_FRAMESET = 10, + XHTML_1_1 = 11; + + /* Profile names, matching the above indices */ + private static final String[] PROFILENAMES = { + null, + null, // there are no + // profiles for + // HTML 3.2 + STRICT, + FRAMESET, + TRANSITIONAL, + STRICT, + FRAMESET, + TRANSITIONAL, + STRICT, + FRAMESET, + TRANSITIONAL, + null // there + // are no + // profiles + // for + // XHTML + // 1.1 + }; + + /* Version names, matching the above indices */ + private static final String[] VERSIONNAMES = { + null, + "HTML 3.2", + HTML_4_0, + HTML_4_0, + HTML_4_0, + HTML_4_01, + HTML_4_01, + HTML_4_01, + XHTML_1_0, + XHTML_1_0, + XHTML_1_0, + "XHTML 1.1" + }; + + /* Flag to know if the property TextMDMetadata is to be added */ + protected boolean _withTextMD = false; + /* Hold the information needed to generate a textMD metadata fragment */ + protected TextMDMetadata _textMD; + + /** + * **************************************************************** CLASS CONSTRUCTOR. + * **************************************************************** + */ + /** Instantiate an HtmlModule object. */ + public HtmlModule() { + super( + NAME, + RELEASE, + DATE, + FORMAT, + COVERAGE, + MIMETYPE, + WELLFORMED, + VALIDITY, + REPINFO, + NOTE, + RIGHTS, + false); + + _vendor = Agent.harvardInstance(); + + /* HTML 3.2 spec */ + Document doc = new Document("HTML 3.2 Reference Specification", DocumentType.REPORT); + Agent w3cAgent = Agent.newW3CInstance(); + doc.setPublisher(w3cAgent); + + Agent dRaggett = new Agent.Builder("Dave Raggett", AgentType.OTHER).build(); + doc.setAuthor(dRaggett); + + doc.setDate("1997-01-14"); + doc.setIdentifier( + new Identifier("http://www.w3c.org/TR/REC-html32-19970114", IdentifierType.URL)); + _specification.add(doc); + + /* HTML 4.0 spec */ + doc = new Document("HTML 4.0 Specification", DocumentType.REPORT); + doc.setPublisher(w3cAgent); + doc.setAuthor(dRaggett); + Agent leHors = new Agent.Builder("Arnaud Le Hors", AgentType.OTHER).build(); + doc.setAuthor(leHors); + Agent jacobs = new Agent.Builder("Ian Jacobs", AgentType.OTHER).build(); + doc.setAuthor(jacobs); + doc.setDate("1998-04-24"); + doc.setIdentifier( + new Identifier("http://www.w3.org/TR/1998/REC-html40-19980424/", IdentifierType.URL)); + _specification.add(doc); + + /* HTML 4.01 spec */ + doc = new Document("HTML 4.01 Specification", DocumentType.REPORT); + doc.setPublisher(w3cAgent); + doc.setAuthor(dRaggett); + doc.setAuthor(leHors); + doc.setAuthor(jacobs); + doc.setDate("1999-12-24"); + doc.setIdentifier( + new Identifier("http://www.w3.org/TR/1999/REC-html401-19991224/", IdentifierType.URL)); + _specification.add(doc); + + /* XHTML 1.0 spec */ + doc = + new Document( + "XHTML(TM) 1.0 The Extensible HyperText Markup Language " + "(Second Edition)", + DocumentType.REPORT); + doc.setPublisher(w3cAgent); + doc.setDate("01-08-2002"); + doc.setIdentifier(new Identifier("http://www.w3.org/TR/xhtml1/", IdentifierType.URL)); + _specification.add(doc); + + /* XHTML 1.1 spec */ + doc = new Document(" XHTML(TM) 1.1 - Module-based XHTML", DocumentType.REPORT); + doc.setPublisher(w3cAgent); + doc.setDate("31-05-2001"); + doc.setIdentifier( + new Identifier("http://www.w3.org/TR/2001/REC-xhtml11-20010531/", IdentifierType.URL)); + _specification.add(doc); + + /* + * XHTML 2.0 spec -- NOT included yet; this is presented in + * "conditionalized-out" form just as a note for future expansion. + * if (false) { + * doc = new Document("XHTML 2.0, W3C Working Draft", + * DocumentType.OTHER); + * doc.setPublisher(w3cAgent); + * doc.setDate("22-07-2004"); + * doc.setIdentifier(new Identifier( + * "http://www.w3.org/TR/2004/WD-xhtml2-20040722/", + * IdentifierType.URL)); + * _specification.add(doc); + * } + */ + + Signature sig = + new ExternalSignature(".html", SignatureType.EXTENSION, SignatureUseType.OPTIONAL); + _signature.add(sig); + sig = new ExternalSignature(".htm", SignatureType.EXTENSION, SignatureUseType.OPTIONAL); + _signature.add(sig); + } + + /** + * Parse the content of a purported HTML stream digital object and store the results in RepInfo. + * + * @param stream An InputStream, positioned at its beginning, which is generated from the object + * to be parsed. If multiple calls to parse are made on the basis of a nonzero + * value being returned, a new InputStream must be provided each time. + * @param info A fresh (on the first call) RepInfo object which will be modified to reflect the + * results of the parsing If multiple calls to parse are made on the basis of a + * nonzero value being returned, the same RepInfo object should be passed with each call. + * @param parseIndex Must be 0 in first call to parse. If parse returns + * a nonzero value, it must be called again with parseIndex equal to that return + * value. + * @return parseInt + */ + @Override + public int parse(InputStream stream, RepInfo info, int parseIndex) { + if (parseIndex != 0) { + // Coming in with parseIndex = 1 indicates that we've determined + // this is XHTML; so we invoke the XML module to parse it. + // If parseIndex is 100, this is the first invocation of the + // XML module, so we call it with 0; otherwise we call it with + // the value of parseIndex. + if (isXmlAvailable()) { + edu.harvard.hul.ois.jhove.module.XmlModule xmlMod = + new edu.harvard.hul.ois.jhove.module.XmlModule(); + if (parseIndex == 100) { + parseIndex = 0; + } + xmlMod.setApp(_app); + xmlMod.setBase(_je); + xmlMod.setDefaultParams(_defaultParams); + try { + xmlMod.applyDefaultParams(); + } catch (Exception e) { + // really shouldn't happen + } + xmlMod.setXhtmlDoctype(_doctype); + return xmlMod.parse(stream, info, parseIndex); + } + // The XML module shouldn't be missing from any installation, + // but someone who really wanted to could remove it. In + // that case, you deserve what you get. + info.setMessage(new ErrorMessage(MessageConstants.JHOVE_1)); + info.setWellFormed(false); // Treat it as completely wrong + return 0; + } + /* parseIndex = 0, first call only */ + _doctype = null; + // Test if textMD is to be generated + if (_defaultParams != null) { + Iterator iter = _defaultParams.iterator(); + while (iter.hasNext()) { + String param = (String) iter.next(); + if ("withtextmd=true".equalsIgnoreCase(param)) { + _withTextMD = true; + } + } + } + + initParse(); + info.setFormat(_format[0]); + info.setMimeType(_mimeType[0]); + info.setModule(this); + + if (_textMD == null || parseIndex == 0) { + _textMD = new TextMDMetadata(); + } + /* + * We may have already done the checksums while converting a temporary + * file. + */ + setupDataStream(stream, info); + + ParseHtml parser; + HtmlMetadata metadata = null; + HtmlCharStream cstream; + try { + cstream = new HtmlCharStream(_dstream, "ISO-8859-1"); + parser = new ParseHtml(this, cstream); + } catch (UnsupportedEncodingException e) { + info.setMessage(new ErrorMessage(MessageConstants.JHOVE_2, e.getMessage())); + info.setWellFormed(false); + return 0; // shouldn't happen! + } + int type = 0; + try { + List elements = parser.HtmlDoc(); + if (elements.isEmpty()) { + // Consider an empty document bad + info.setWellFormed(false); + info.setMessage(new ErrorMessage(MessageConstants.JHOVE_3)); + return 0; + } + type = checkDoctype(elements); + if (type < 0) { + info.setWellFormed(false); + info.setMessage(new ErrorMessage(MessageConstants.HTML_HUL_15)); + return 0; + } + /* + * Check if there is at least one html, head, body or title tag. A + * plain text document might be interpreted as a single PCDATA, + * which is in some ethereal sense well-formed HTML, but it's + * pointless to consider it such. It might also use angle brackets + * as a text delimiter, and that shouldn't count as HTML either. + */ + boolean hasElements = false; + Iterator iter = elements.iterator(); + while (iter.hasNext()) { + Object o = iter.next(); + if (o instanceof JHOpenTag) { + String name = ((JHOpenTag) o).getName(); + if ("html".equals(name) + || "head".equals(name) + || "body".equals(name) + || "title".equals(name)) { + hasElements = true; + } + break; + } + } + if (!hasElements) { + info.setMessage(new ErrorMessage(MessageConstants.HTML_HUL_17)); + info.setWellFormed(false); + return 0; + } + + // CRLF from HtmlCharStream ... + String lineEnd = cstream.getKindOfLineEnd(); + if (lineEnd == null) { + info.setMessage(new InfoMessage(MessageConstants.INF_EOL_TYPE_UNDET)); + _textMD.setLinebreak(TextMDMetadata.NILL); + } else if ("CR".equalsIgnoreCase(lineEnd)) { + _textMD.setLinebreak(TextMDMetadata.LINEBREAK_CR); + } else if ("LF".equalsIgnoreCase(lineEnd)) { + _textMD.setLinebreak(TextMDMetadata.LINEBREAK_LF); + } else if ("CRLF".equalsIgnoreCase(lineEnd)) { + _textMD.setLinebreak(TextMDMetadata.LINEBREAK_CRLF); + } + + if (type == 0) { + /* + * If we can't find a doctype, it still might be XHTML if the + * elements start with an XML declaration and the root element + * is "html" + */ + switch (seemsToBeXHTML(elements)) { + case 0: // Not XML + break; // fall through + case 1: // XML but not HTML + info.setMessage(new ErrorMessage(MessageConstants.HTML_HUL_14)); + info.setWellFormed(false); + return 0; + case 2: // probably XHTML + return 100; + default: + break; + } + info.setMessage(new ErrorMessage(MessageConstants.HTML_HUL_16)); + info.setValid(false); + // But keep going + } + + HtmlDocDesc docDesc = null; + switch (type) { + case HTML_3_2: + + case HTML_4_0_FRAMESET: + docDesc = new Html4_0FrameDocDesc(); + _textMD.setMarkup_basis("HTML"); + _textMD.setMarkup_basis_version("4.0"); + break; + case HTML_4_0_TRANSITIONAL: + docDesc = new Html4_0TransDocDesc(); + _textMD.setMarkup_basis("HTML"); + _textMD.setMarkup_basis_version("4.0"); + break; + case HTML_4_0_STRICT: + docDesc = new Html4_0StrictDocDesc(); + _textMD.setMarkup_basis("HTML"); + _textMD.setMarkup_basis_version("4.0"); + break; + case HTML_4_01_FRAMESET: + docDesc = new Html4_01FrameDocDesc(); + _textMD.setMarkup_basis("HTML"); + _textMD.setMarkup_basis_version("4.01"); + break; + case HTML_4_01_TRANSITIONAL: + docDesc = new Html4_01TransDocDesc(); + _textMD.setMarkup_basis("HTML"); + _textMD.setMarkup_basis_version("4.01"); + break; + case HTML_4_01_STRICT: + docDesc = new Html4_01StrictDocDesc(); + _textMD.setMarkup_basis("HTML"); + _textMD.setMarkup_basis_version("4.01"); + break; + case XHTML_1_0_STRICT: + case XHTML_1_0_TRANSITIONAL: + case XHTML_1_0_FRAMESET: + case XHTML_1_1: + // Force a second call to parse as XML. 100 is a + // magic code for the first XML call. + return 100; + } + _textMD.setMarkup_language(_doctype); + if (docDesc == null) { + info.setMessage(new InfoMessage(MessageConstants.INF_HTML_VER_UNSPPRTD)); + docDesc = new Html3_2DocDesc(); + } + docDesc.validate(elements, info); + metadata = docDesc.getMetadata(); + + // Try to get the charset from the meta Content + if (metadata.getCharset() != null) { + _textMD.setCharset(metadata.getCharset()); + } else { + _textMD.setCharset(TextMDMetadata.CHARSET_ISO8859_1); + } + String textMDEncoding = _textMD.getCharset(); + if (textMDEncoding.contains("UTF")) { + _textMD.setByte_order( + _bigEndian ? TextMDMetadata.BYTE_ORDER_BIG : TextMDMetadata.BYTE_ORDER_LITTLE); + _textMD.setByte_size("8"); + _textMD.setCharacter_size("variable"); + } else { + _textMD.setByte_order( + _bigEndian ? TextMDMetadata.BYTE_ORDER_BIG : TextMDMetadata.BYTE_ORDER_LITTLE); + _textMD.setByte_size("8"); + _textMD.setCharacter_size("1"); + } + } catch (ParseException e) { + Token t = e.currentToken; + info.setMessage( + new ErrorMessage( + MessageConstants.HTML_HUL_18, + "Line = " + t.beginLine + ", column = " + t.beginColumn)); + info.setWellFormed(false); + } catch (TokenMgrError f) { + info.setMessage(new ErrorMessage(MessageConstants.HTML_HUL_19, f.getLocalizedMessage())); + info.setWellFormed(false); + } + + if (info.getWellFormed() == RepInfo.FALSE) { + return 0; + } + + if (type != 0) { + if (PROFILENAMES[type] != null) { + info.setProfile(PROFILENAMES[type]); + } + info.setVersion(VERSIONNAMES[type]); + } + + if (metadata != null) { + Property property = metadata.toProperty(_withTextMD ? _textMD : null); + if (property != null) { + info.setProperty(property); + } + } + + // Set the checksums in the report if they're calculated + setChecksums(this._ckSummer, info); + + return 0; + } + + /** + * Check if the digital object conforms to this Module's internal signature information. + * + *

HTML is one of the most ill-defined of any open formats, so checking a "signature" really + * means using some heuristics. The only required tag is TITLE, but that could occur well into the + * file. So we look for any of three strings -- taking into account case-independence and white + * space -- within the first sigBytes bytes, and call that a signature check. + * + * @param file A File object for the object being parsed + * @param stream An InputStream, positioned at its beginning, which is generated from the object + * to be parsed + * @param info A fresh RepInfo object which will be modified to reflect the results of the test + * @throws IOException + */ + @Override + public void checkSignatures(File file, InputStream stream, RepInfo info) throws IOException { + info.setFormat(_format[0]); + info.setMimeType(_mimeType[0]); + info.setModule(this); + char[][] sigtext = new char[3][]; + sigtext[0] = "= 2) { + firstElem = (JHElement) elements.get(1); + } + if (!(firstElem instanceof JHDoctype)) { + return 0; // no DOCTYPE found + } + List dt = ((JHDoctype) firstElem).getDoctypeElements(); + if (dt.size() < 3) { + return 0; + } + try { + // Is DOCTYPE case sensitive? Assume not. + String str = ((String) dt.get(0)).toUpperCase(); + if (!"HTML".equals(str)) { + // It's not HTML + return -1; + } + str = ((String) dt.get(1)).toUpperCase(); + if (!"PUBLIC".equals(str)) { + return 0; + } + str = stripQuotes(((String) dt.get(2)).toUpperCase()); + _doctype = str; + if (null != str) + switch (str) { + case "-//W3C//DTD HTML 3.2 FINAL//EN": + case "-//W3C//DTD HTML 3.2//EN": + return HTML_3_2; + case "-//W3C//DTD HTML 4.0//EN": + return HTML_4_0_STRICT; + case "-//W3C//DTD HTML 4.0 TRANSITIONAL//EN": + return HTML_4_0_TRANSITIONAL; + case "-//W3C//DTD HTML 4.0 FRAMESET//EN": + return HTML_4_0_FRAMESET; + case "-//W3C//DTD HTML 4.01//EN": + return HTML_4_01_STRICT; + case "-//W3C//DTD HTML 4.01 TRANSITIONAL//EN": + return HTML_4_01_TRANSITIONAL; + case "-//W3C//DTD HTML 4.01 FRAMESET//EN": + return HTML_4_01_FRAMESET; + default: + break; + } + } catch (Exception e) { + // Really shouldn't happen, but if it does we've got + // a bad doctype + return 0; + } + return 0; + } + + /* + * See if this document, even if it lacks a doctype, is most likely XHTML. + * The test is that the document starts with an XML declaration and has + * "html" for its first tag. + * + * Returns: 0 if there's no XML declaration 1 if there's an XML declaration + * but no html tag; in this case it's probably some other kind of XML 2 if + * there's an XML declaration and an html tag + */ + protected int seemsToBeXHTML(List elements) { + JHElement elem; + try { + elem = (JHElement) elements.get(0); + if (!(elem instanceof JHXmlDecl)) { + return 0; + } + Iterator iter = elements.iterator(); + while (iter.hasNext()) { + elem = (JHElement) iter.next(); + if (elem instanceof JHOpenTag) { + JHOpenTag tag = (JHOpenTag) elem; + return ("html".equals(tag.getName()) ? 2 : 1); + } + } + } catch (Exception e) { + return 0; // document must be really empty + } + return 1; + } + + /* + * Remove quotes from the beginning and end of a string. If it doesn't have + * quotes in both places, leave it alone. + */ + protected String stripQuotes(String str) { + int len = str.length(); + if (str.charAt(0) == '"' && str.charAt(len - 1) == '"') { + return str.substring(1, len - 1); + } + return str; + } + + /* + * Checks if the XML module is available. + */ + protected static boolean isXmlAvailable() { + try { + Class.forName("edu.harvard.hul.ois.jhove.module.XmlModule"); + return true; + } catch (Exception e) { + return false; + } + } } diff --git a/jhove-modules/html-hul/src/main/java/edu/harvard/hul/ois/jhove/module/html/CharStream.java b/jhove-modules/html-hul/src/main/java/edu/harvard/hul/ois/jhove/module/html/CharStream.java index 2ef6380a2..68cfbf518 100644 --- a/jhove-modules/html-hul/src/main/java/edu/harvard/hul/ois/jhove/module/html/CharStream.java +++ b/jhove-modules/html-hul/src/main/java/edu/harvard/hul/ois/jhove/module/html/CharStream.java @@ -2,109 +2,96 @@ package edu.harvard.hul.ois.jhove.module.html; /** - * This interface describes a character stream that maintains line and - * column number positions of the characters. It also has the capability - * to backup the stream to some extent. An implementation of this - * interface is used in the TokenManager implementation generated by - * JavaCCParser. + * This interface describes a character stream that maintains line and column number positions of + * the characters. It also has the capability to backup the stream to some extent. An implementation + * of this interface is used in the TokenManager implementation generated by JavaCCParser. * - * All the methods except backup can be implemented in any fashion. backup - * needs to be implemented correctly for the correct operation of the lexer. - * Rest of the methods are all used to get information like line number, - * column number and the String that constitutes a token and are not used - * by the lexer. Hence their implementation won't affect the generated lexer's - * operation. + *

All the methods except backup can be implemented in any fashion. backup needs to be + * implemented correctly for the correct operation of the lexer. Rest of the methods are all used to + * get information like line number, column number and the String that constitutes a token and are + * not used by the lexer. Hence their implementation won't affect the generated lexer's operation. */ - public interface CharStream { /** - * Returns the next character from the selected input. The method - * of selecting the input is the responsibility of the class - * implementing this interface. Can throw any java.io.IOException. + * Returns the next character from the selected input. The method of selecting the input is the + * responsibility of the class implementing this interface. Can throw any java.io.IOException. */ char readChar() throws java.io.IOException; /** * Returns the column position of the character last read. - * @deprecated + * + * @deprecated * @see #getEndColumn */ int getColumn(); /** * Returns the line number of the character last read. - * @deprecated + * + * @deprecated * @see #getEndLine */ int getLine(); /** - * Returns the column number of the last character for current token (being - * matched after the last call to BeginTOken). + * Returns the column number of the last character for current token (being matched after the last + * call to BeginTOken). */ int getEndColumn(); /** - * Returns the line number of the last character for current token (being - * matched after the last call to BeginTOken). + * Returns the line number of the last character for current token (being matched after the last + * call to BeginTOken). */ int getEndLine(); /** - * Returns the column number of the first character for current token (being - * matched after the last call to BeginTOken). + * Returns the column number of the first character for current token (being matched after the + * last call to BeginTOken). */ int getBeginColumn(); /** - * Returns the line number of the first character for current token (being - * matched after the last call to BeginTOken). + * Returns the line number of the first character for current token (being matched after the last + * call to BeginTOken). */ int getBeginLine(); /** - * Backs up the input stream by amount steps. Lexer calls this method if it - * had already read some characters, but could not use them to match a - * (longer) token. So, they will be used again as the prefix of the next - * token and it is the implemetation's responsibility to do this right. + * Backs up the input stream by amount steps. Lexer calls this method if it had already read some + * characters, but could not use them to match a (longer) token. So, they will be used again as + * the prefix of the next token and it is the implemetation's responsibility to do this right. */ void backup(int amount); /** - * Returns the next character that marks the beginning of the next token. - * All characters must remain in the buffer between two successive calls - * to this method to implement backup correctly. + * Returns the next character that marks the beginning of the next token. All characters must + * remain in the buffer between two successive calls to this method to implement backup correctly. */ char BeginToken() throws java.io.IOException; /** - * Returns a string made up of characters from the marked token beginning - * to the current buffer position. Implementations have the choice of returning - * anything that they want to. For example, for efficiency, one might decide - * to just return null, which is a valid implementation. + * Returns a string made up of characters from the marked token beginning to the current buffer + * position. Implementations have the choice of returning anything that they want to. For example, + * for efficiency, one might decide to just return null, which is a valid implementation. */ String GetImage(); /** - * Returns an array of characters that make up the suffix of length 'len' for - * the currently matched token. This is used to build up the matched string - * for use in actions in the case of MORE. A simple and inefficient - * implementation of this is as follows : + * Returns an array of characters that make up the suffix of length 'len' for the currently + * matched token. This is used to build up the matched string for use in actions in the case of + * MORE. A simple and inefficient implementation of this is as follows : * - * { - * String t = GetImage(); - * return t.substring(t.length() - len, t.length()).toCharArray(); - * } + *

{ String t = GetImage(); return t.substring(t.length() - len, t.length()).toCharArray(); } */ char[] GetSuffix(int len); /** - * The lexer calls this function to indicate that it is done with the stream - * and hence implementations can free any resources held by this class. - * Again, the body of this function can be just empty and it will not - * affect the lexer's operation. + * The lexer calls this function to indicate that it is done with the stream and hence + * implementations can free any resources held by this class. Again, the body of this function can + * be just empty and it will not affect the lexer's operation. */ void Done(); - } diff --git a/jhove-modules/html-hul/src/main/java/edu/harvard/hul/ois/jhove/module/html/Html3_2DocDesc.java b/jhove-modules/html-hul/src/main/java/edu/harvard/hul/ois/jhove/module/html/Html3_2DocDesc.java index 610a07593..10c8b6366 100644 --- a/jhove-modules/html-hul/src/main/java/edu/harvard/hul/ois/jhove/module/html/Html3_2DocDesc.java +++ b/jhove-modules/html-hul/src/main/java/edu/harvard/hul/ois/jhove/module/html/Html3_2DocDesc.java @@ -1,8 +1,8 @@ -/********************************************************************** - * Jhove - JSTOR/Harvard Object Validation Environment - * Copyright 2004 by JSTOR and the President and Fellows of Harvard College - **********************************************************************/ - +/** + * ******************************************************************** Jhove - JSTOR/Harvard Object + * Validation Environment Copyright 2004 by JSTOR and the President and Fellows of Harvard College + * ******************************************************************** + */ package edu.harvard.hul.ois.jhove.module.html; import java.util.ArrayList; @@ -14,689 +14,624 @@ * This class describes the requirements of an HTML 3.2 document. * * @author Gary McGath - * */ public class Html3_2DocDesc extends HtmlDocDesc { - /* Static, private map of supported tags. - * For efficiency, we create a static Map - * of supported tags just once, then assign that to stSupportedElements - * in the constructor. */ - private static Map stSupportedElements; - - /* Static initializer. A superclass is initialized before its - * subclass, so we can count on the static initializer of HtmlDocDesc - * to have run already. - * - * It's time to start thinking about how to factor this code. - * Each element can be created separately, with the necessary - * arguments passed for each one. It would be a nice pattern if - * all elements had the same calling sequence, but realistically - * some are going to need extras such as special lists of names. - * The element functions (which will all be static) should be here - * if unique, or in the parent class if they can be used for more - * than one version of HTML. There should be a naming convention - * for the functions in the parent class indicating which names - * they can be used with. - */ - static { - stSupportedElements = new HashMap (280); - String[] fontMarkup = new String[] - { "tt", "i", "b", "u", "strike", "big", "small", "sub", "sup" }; - String[] phraseMarkup = new String[] - { "em", "strong", "dfn", "code", "samp", "kbd", "var", "cite" }; - String[] specialMarkup = new String[] - { "a", "img", "applet", "font", "basefont", "br", "script", "map" }; - String[] formMarkup = new String[] - { "input", "select", "textarea" }; - String[] listMarkup = new String [] - { "ul", "ol", "dir", "menu" }; - - - /* textContent lists all the content types which are permitted in - * the markup elements. For a first cut, strings signify the - * name of the element which is permitted. */ - List textContent = new ArrayList(35); - addStringsToList (fontMarkup, textContent); - addStringsToList (phraseMarkup, textContent); - addStringsToList (specialMarkup, textContent); - addStringsToList (formMarkup, textContent); - textContent.add (HtmlSpecialToken.PCDATA); - - List blockContent = new ArrayList (20); - addStringsToList (listMarkup, blockContent); - String[] blockMisc = new String[] - {"p", "pre", "dl", "div", "center", - "blockquote", "form", "isindex", "hr", "table" }; - addStringsToList (blockMisc, blockContent); - - List flowContent = new ArrayList (30); - flowContent.addAll (blockContent); - flowContent.addAll (textContent); - - int i; - String name; - HtmlTagDesc td; - - /* Text elements */ - for (i = 0; i < fontMarkup.length; i++) { - name = fontMarkup[i]; - td = new HtmlTagDesc (name, true, true, textContent, null); - stSupportedElements.put (name, td); - } - - /* Phrase elements. */ - for (i = 0; i < phraseMarkup.length; i++) { - name = phraseMarkup[i]; - td = new HtmlTagDesc (name, true, true, textContent, null); - stSupportedElements.put (name, td); - } - - addFontElement (textContent); - addBasefontElement (); - addBrElement (); - - /* Content for the BODY element, also used for other elements */ - List bodyContent = new ArrayList (100); - addStringsToList (headings, bodyContent); - bodyContent.addAll (textContent); - bodyContent.addAll (blockContent); - bodyContent.add ("address"); - - addBodyElement (bodyContent); - addAddressElement (textContent); - - HtmlAttributeDesc halignAtt = - new HtmlAttributeDesc ("align", - new String[] { "left", "center", "right" }, - HtmlAttributeDesc.IMPLIED); - /* Caution -- some elements' align attributes have a different - * set of permitted values. Don't use halignAtt for these. */ - - addDivElement (bodyContent, halignAtt); - addCenterElement (bodyContent); - addAElement (textContent); - addMapElement (); - - addAreaElement (); - addLinkElement (); - HtmlAttributeDesc ialignAtt = new HtmlAttributeDesc ("align", - new String[] { "top", "middle", "bottom", "left", "right" }, - HtmlAttributeDesc.IMPLIED); - addImgElement (ialignAtt); - addAppletElement (ialignAtt, textContent); - addParamElement (); - addHrElement (halignAtt); - addPElement (halignAtt, textContent); - - /* The heading (H1-H6) elements */ - List atts = new ArrayList (1); - atts.add (halignAtt); - for (i = 0; i < headings.length; i++) { - name = headings[i]; - td = new HtmlTagDesc (name, true, true, textContent, atts); - stSupportedElements.put (name, td); - } - - addPreElement (textContent); - addBlockquoteElement (bodyContent); - addDlElement (); - addDtElement (textContent); - addDdElement (flowContent); - - List listContent = new ArrayList (1); - listContent.add ("li"); - addOlElement (listContent); - addUlElement (listContent); - addDirElement (listContent); - addMenuElement (listContent); - addLiElement (flowContent); - addFormElement (bodyContent); - addInputElement (ialignAtt); - addSelectElement (); - addOptionElement (); - addTableElement (); - HtmlAttributeDesc valignAtt = - new HtmlAttributeDesc ("valign", - new String[] { "top", "middle", "bottom" }, - HtmlAttributeDesc.IMPLIED); - addTrElement (halignAtt, valignAtt); - - List thtdAtts = new ArrayList (7); // common attribute list for TH and TD - addSelfAttribute (thtdAtts, "nowrap"); - addSimpleAttribute (thtdAtts, "rowspan"); - addSimpleAttribute (thtdAtts, "colspan"); - thtdAtts.add (halignAtt); - thtdAtts.add (valignAtt); - addSimpleAttribute (thtdAtts, "width"); - addSimpleAttribute (thtdAtts, "height"); - - addThElement (bodyContent, thtdAtts); - addTdElement (bodyContent, thtdAtts); - addCaptionElement (textContent, valignAtt); - - addHeadElement (); - addTitleElement (); - addIsindexElement (); - addBaseElement (); - addMetaElement (); - addScriptElement (); - addStyleElement (); - - /* The HTML element */ - name = "html"; - List htmlContent = new ArrayList (2); - htmlContent.add ("head"); - htmlContent.add ("body"); - td = new HtmlTagDesc (name, false, false, htmlContent, null); - td.setAttributes (new String[] {"version" }); - stSupportedElements.put (name, td); - } - - /** Constructor. */ - public Html3_2DocDesc () - { - super(); - // publish stSupportedElements to superclass - supportedElements = stSupportedElements; - init (); - } - - /** Static initializers for each element. If elements are common to more - * than one HTML version, they should be moved into the superclass. - * Different initializers may have different argument lists. */ - private static void addAddressElement (List textContent) - { - String name = "address"; - List addressContent = new ArrayList (36); - addressContent.addAll (textContent); - addressContent.add ("p"); - HtmlTagDesc td = new HtmlTagDesc (name, true, true, addressContent, null); - stSupportedElements.put (name, td); - } - - /* Initializer for "A" (anchor) element. */ - private static void addAElement (List textContent) - { - /* The Anchor (A) element */ - String name = "a"; - HtmlTagDesc td = new HtmlTagDesc (name, true, true, textContent, null); - td.setAttributes (new String[] - {"name", "href", "rel", "rev", "title" }); - td.setExcludedContent(new String[] { "a" }); - stSupportedElements.put (name, td); - - } - - /* Initializer for APPLET element. */ - private static void addAppletElement - (HtmlAttributeDesc ialignAtt, List textContent) - { - String name = "applet"; - List atts = new ArrayList (9); - addSimpleAttribute (atts, "codebase"); - atts.add (new HtmlAttributeDesc - ("code", null, HtmlAttributeDesc.REQUIRED)); - addSimpleAttribute (atts, "alt"); - addSimpleAttribute (atts, "name"); - atts.add (new HtmlAttributeDesc - ("width", null, HtmlAttributeDesc.REQUIRED)); - atts.add (new HtmlAttributeDesc - ("height", null, HtmlAttributeDesc.REQUIRED)); - atts.add (ialignAtt); - addSimpleAttribute (atts, "hspace"); - addSimpleAttribute (atts, "vspace"); - List appletContent = new ArrayList (36); - appletContent.addAll (textContent); - appletContent.add ("param"); - HtmlTagDesc td = new HtmlTagDesc (name, true, false, appletContent, atts); - stSupportedElements.put (name, td); - - } - - private static void addAreaElement () - { - String name = "area"; - List atts = new ArrayList (5); - atts.add (new HtmlAttributeDesc ("shape", - new String[] {"rect", "circle", "poly" }, - HtmlAttributeDesc.REQUIRED)); - addSimpleAttribute (atts, "coords"); - addSimpleAttribute (atts, "href"); - atts.add (new HtmlAttributeDesc ("nohref", - new String[] {"nohref"}, - HtmlAttributeDesc.IMPLIED)); - atts.add (new HtmlAttributeDesc ("alt", - null, - HtmlAttributeDesc.REQUIRED)); - HtmlTagDesc td = new HtmlTagDesc (name, true, false, null, atts); - stSupportedElements.put (name, td); - } - - private static void addBaseElement () - { - String name = "base"; - List atts = new ArrayList (1); - addSimpleAttribute (atts, "href"); - HtmlTagDesc td = new HtmlTagDesc (name, true, false, null, atts); - stSupportedElements.put (name, td); - } - private static void addBasefontElement () - { - String name = "basefont"; - HtmlTagDesc td = new HtmlTagDesc (name, true, false, null, null); - td.setAttributes (new String[] {"size"}); - stSupportedElements.put (name, td); - } - - private static void addBlockquoteElement (List bodyContent) - { - /* The BLOCKQUOTE element */ - String name = "blockquote"; - HtmlTagDesc td = new HtmlTagDesc (name, true, true, bodyContent, null); - stSupportedElements.put (name, td); - } - - private static void addBodyElement (List bodyContent) - { - String name = "body"; - HtmlTagDesc td = new HtmlTagDesc (name, true, true, bodyContent, null); - td.setAttributes (new String [] - {"bgcolor", "text", "link", "vlink", "alink", "background" }); - stSupportedElements.put (name, td); - - } - - private static void addBrElement () - { - String name = "br"; - HtmlTagDesc td = new HtmlTagDesc (name, true, false, null, null); - stSupportedElements.put (name, td); - } - - private static void addCaptionElement (List textContent, - HtmlAttributeDesc valignAtt) - { - String name = "caption"; - List atts = new ArrayList (1); - atts.add (valignAtt); - HtmlTagDesc td = new HtmlTagDesc (name, true, true, textContent, atts); - stSupportedElements.put (name, td); - } - private static void addCenterElement (List bodyContent) - { - String name = "center"; - HtmlTagDesc td = new HtmlTagDesc (name, true, true, bodyContent, null); - stSupportedElements.put (name, td); - } - - private static void addDdElement (List flowContent) - { - String name = "dd"; - HtmlTagDesc td = new HtmlTagDesc (name, true, false, flowContent, null); - stSupportedElements.put (name, td); - } - - private static void addDirElement (List listContent) - { - String name = "dir"; - List atts = new ArrayList (1); - addSelfAttribute (atts, "compact"); - HtmlTagDesc td = new HtmlTagDesc (name, true, true, listContent, atts); - stSupportedElements.put (name, td); - } - - private static void addDivElement (List bodyContent, HtmlAttributeDesc halignAtt) - { - String name = "div"; - List atts = new ArrayList (1); - atts.add (halignAtt); - HtmlTagDesc td = new HtmlTagDesc (name, true, true, bodyContent, atts); - stSupportedElements.put (name, td); - } - - private static void addDlElement () - { - String name = "dl"; - List dlContent = new ArrayList (2); - addStringsToList(new String[] { "dt", "dd" }, dlContent); - List atts = new ArrayList (1); - addSelfAttribute(atts, "compact"); - HtmlTagDesc td = new HtmlTagDesc (name, true, true, dlContent, atts); - stSupportedElements.put (name, td); - } - - private static void addDtElement (List textContent) - { - String name = "dt"; - HtmlTagDesc td = new HtmlTagDesc (name, true, false, textContent, null); - stSupportedElements.put (name, td); - } - - private static void addFontElement (List textContent) - { - String name = "font"; - HtmlTagDesc td = new HtmlTagDesc (name, true, true, textContent, null); - td.setAttributes (new String[] {"size", "color"}); - stSupportedElements.put (name, td); - - } - - private static void addFormElement (List bodyContent) - { - final String name = "form"; - List atts = new ArrayList (3); - addSimpleAttribute (atts, "action"); - atts.add (new HtmlAttributeDesc ("method", null, - HtmlAttributeDesc.OTHER)); - atts.add (new HtmlAttributeDesc ("enctype", null, - HtmlAttributeDesc.OTHER)); - List formContent = new ArrayList (bodyContent.size ()); - formContent.addAll (bodyContent); - removeStringsFromList (formContent, new String[] { "form" }); - HtmlTagDesc td = new HtmlTagDesc (name, true, true, formContent, atts); - stSupportedElements.put (name, td); - } - - private static void addHeadElement () - { - String name = "head"; - String[] headMisc = new String[] - {"script", "style", "meta", "link" }; - List headContent = new ArrayList (7); - headContent.add ("title"); - headContent.add ("isindex"); - headContent.add ("base"); - HtmlTagDesc td = new HtmlTagDesc (name, false, false, headContent, null); - stSupportedElements.put (name, td); - /* Attributes TITLE (required), ISINDEX (optional), and BASE (optional) - * are supposed to come in that order, ahead of anything else. - * For the moment, just toss them in with the rest. */ - addStringsToList (headMisc, headContent); - } - - private static void addHrElement (HtmlAttributeDesc halignAtt) - { - String name = "hr"; - List atts = new ArrayList (4); - - atts.add (halignAtt); - addSelfAttribute (atts, "noshade"); - addSimpleAttribute (atts, "size"); - addSimpleAttribute (atts, "width"); - HtmlTagDesc td = new HtmlTagDesc (name, true, false, null, atts); - stSupportedElements.put (name, td); - } - - private static void addImgElement (HtmlAttributeDesc ialignAtt) - { - String name = "img"; - List atts = new ArrayList (10); - addRequiredAttribute (atts, "src"); - addSimpleAttribute (atts, "alt"); - atts.add (ialignAtt); - addSimpleAttribute (atts, "height"); - addSimpleAttribute (atts, "width"); - addSimpleAttribute (atts, "border"); - addSimpleAttribute (atts, "hspace"); - addSimpleAttribute (atts, "vspace"); - addSimpleAttribute (atts, "usemap"); - addSelfAttribute (atts, "ismap"); - HtmlTagDesc td = new HtmlTagDesc (name, true, false, null, atts); - stSupportedElements.put (name, td); - } - - private static void addInputElement (HtmlAttributeDesc ialignAtt) - { - final String name = "input"; - List atts = new ArrayList (8); - atts.add (new HtmlAttributeDesc ("type", - new String[] {"text", "password", "checkbox", "radio", "submit", - "reset", "file", "hidden", "image"}, + /* Static, private map of supported tags. + * For efficiency, we create a static Map + * of supported tags just once, then assign that to stSupportedElements + * in the constructor. */ + private static Map stSupportedElements; + + /* Static initializer. A superclass is initialized before its + * subclass, so we can count on the static initializer of HtmlDocDesc + * to have run already. + * + * It's time to start thinking about how to factor this code. + * Each element can be created separately, with the necessary + * arguments passed for each one. It would be a nice pattern if + * all elements had the same calling sequence, but realistically + * some are going to need extras such as special lists of names. + * The element functions (which will all be static) should be here + * if unique, or in the parent class if they can be used for more + * than one version of HTML. There should be a naming convention + * for the functions in the parent class indicating which names + * they can be used with. + */ + static { + stSupportedElements = new HashMap(280); + String[] fontMarkup = + new String[] {"tt", "i", "b", "u", "strike", "big", "small", "sub", "sup"}; + String[] phraseMarkup = + new String[] {"em", "strong", "dfn", "code", "samp", "kbd", "var", "cite"}; + String[] specialMarkup = + new String[] {"a", "img", "applet", "font", "basefont", "br", "script", "map"}; + String[] formMarkup = new String[] {"input", "select", "textarea"}; + String[] listMarkup = new String[] {"ul", "ol", "dir", "menu"}; + + /* textContent lists all the content types which are permitted in + * the markup elements. For a first cut, strings signify the + * name of the element which is permitted. */ + List textContent = new ArrayList(35); + addStringsToList(fontMarkup, textContent); + addStringsToList(phraseMarkup, textContent); + addStringsToList(specialMarkup, textContent); + addStringsToList(formMarkup, textContent); + textContent.add(HtmlSpecialToken.PCDATA); + + List blockContent = new ArrayList(20); + addStringsToList(listMarkup, blockContent); + String[] blockMisc = + new String[] { + "p", "pre", "dl", "div", "center", "blockquote", "form", "isindex", "hr", "table" + }; + addStringsToList(blockMisc, blockContent); + + List flowContent = new ArrayList(30); + flowContent.addAll(blockContent); + flowContent.addAll(textContent); + + int i; + String name; + HtmlTagDesc td; + + /* Text elements */ + for (i = 0; i < fontMarkup.length; i++) { + name = fontMarkup[i]; + td = new HtmlTagDesc(name, true, true, textContent, null); + stSupportedElements.put(name, td); + } + + /* Phrase elements. */ + for (i = 0; i < phraseMarkup.length; i++) { + name = phraseMarkup[i]; + td = new HtmlTagDesc(name, true, true, textContent, null); + stSupportedElements.put(name, td); + } + + addFontElement(textContent); + addBasefontElement(); + addBrElement(); + + /* Content for the BODY element, also used for other elements */ + List bodyContent = new ArrayList(100); + addStringsToList(headings, bodyContent); + bodyContent.addAll(textContent); + bodyContent.addAll(blockContent); + bodyContent.add("address"); + + addBodyElement(bodyContent); + addAddressElement(textContent); + + HtmlAttributeDesc halignAtt = + new HtmlAttributeDesc( + "align", new String[] {"left", "center", "right"}, HtmlAttributeDesc.IMPLIED); + /* Caution -- some elements' align attributes have a different + * set of permitted values. Don't use halignAtt for these. */ + + addDivElement(bodyContent, halignAtt); + addCenterElement(bodyContent); + addAElement(textContent); + addMapElement(); + + addAreaElement(); + addLinkElement(); + HtmlAttributeDesc ialignAtt = + new HtmlAttributeDesc( + "align", + new String[] {"top", "middle", "bottom", "left", "right"}, + HtmlAttributeDesc.IMPLIED); + addImgElement(ialignAtt); + addAppletElement(ialignAtt, textContent); + addParamElement(); + addHrElement(halignAtt); + addPElement(halignAtt, textContent); + + /* The heading (H1-H6) elements */ + List atts = new ArrayList(1); + atts.add(halignAtt); + for (i = 0; i < headings.length; i++) { + name = headings[i]; + td = new HtmlTagDesc(name, true, true, textContent, atts); + stSupportedElements.put(name, td); + } + + addPreElement(textContent); + addBlockquoteElement(bodyContent); + addDlElement(); + addDtElement(textContent); + addDdElement(flowContent); + + List listContent = new ArrayList(1); + listContent.add("li"); + addOlElement(listContent); + addUlElement(listContent); + addDirElement(listContent); + addMenuElement(listContent); + addLiElement(flowContent); + addFormElement(bodyContent); + addInputElement(ialignAtt); + addSelectElement(); + addOptionElement(); + addTableElement(); + HtmlAttributeDesc valignAtt = + new HtmlAttributeDesc( + "valign", new String[] {"top", "middle", "bottom"}, HtmlAttributeDesc.IMPLIED); + addTrElement(halignAtt, valignAtt); + + List thtdAtts = new ArrayList(7); // common attribute list for TH and TD + addSelfAttribute(thtdAtts, "nowrap"); + addSimpleAttribute(thtdAtts, "rowspan"); + addSimpleAttribute(thtdAtts, "colspan"); + thtdAtts.add(halignAtt); + thtdAtts.add(valignAtt); + addSimpleAttribute(thtdAtts, "width"); + addSimpleAttribute(thtdAtts, "height"); + + addThElement(bodyContent, thtdAtts); + addTdElement(bodyContent, thtdAtts); + addCaptionElement(textContent, valignAtt); + + addHeadElement(); + addTitleElement(); + addIsindexElement(); + addBaseElement(); + addMetaElement(); + addScriptElement(); + addStyleElement(); + + /* The HTML element */ + name = "html"; + List htmlContent = new ArrayList(2); + htmlContent.add("head"); + htmlContent.add("body"); + td = new HtmlTagDesc(name, false, false, htmlContent, null); + td.setAttributes(new String[] {"version"}); + stSupportedElements.put(name, td); + } + + /** Constructor. */ + public Html3_2DocDesc() { + super(); + // publish stSupportedElements to superclass + supportedElements = stSupportedElements; + init(); + } + + /** + * Static initializers for each element. If elements are common to more than one HTML version, + * they should be moved into the superclass. Different initializers may have different argument + * lists. + */ + private static void addAddressElement(List textContent) { + String name = "address"; + List addressContent = new ArrayList(36); + addressContent.addAll(textContent); + addressContent.add("p"); + HtmlTagDesc td = new HtmlTagDesc(name, true, true, addressContent, null); + stSupportedElements.put(name, td); + } + + /* Initializer for "A" (anchor) element. */ + private static void addAElement(List textContent) { + /* The Anchor (A) element */ + String name = "a"; + HtmlTagDesc td = new HtmlTagDesc(name, true, true, textContent, null); + td.setAttributes(new String[] {"name", "href", "rel", "rev", "title"}); + td.setExcludedContent(new String[] {"a"}); + stSupportedElements.put(name, td); + } + + /* Initializer for APPLET element. */ + private static void addAppletElement(HtmlAttributeDesc ialignAtt, List textContent) { + String name = "applet"; + List atts = new ArrayList(9); + addSimpleAttribute(atts, "codebase"); + atts.add(new HtmlAttributeDesc("code", null, HtmlAttributeDesc.REQUIRED)); + addSimpleAttribute(atts, "alt"); + addSimpleAttribute(atts, "name"); + atts.add(new HtmlAttributeDesc("width", null, HtmlAttributeDesc.REQUIRED)); + atts.add(new HtmlAttributeDesc("height", null, HtmlAttributeDesc.REQUIRED)); + atts.add(ialignAtt); + addSimpleAttribute(atts, "hspace"); + addSimpleAttribute(atts, "vspace"); + List appletContent = new ArrayList(36); + appletContent.addAll(textContent); + appletContent.add("param"); + HtmlTagDesc td = new HtmlTagDesc(name, true, false, appletContent, atts); + stSupportedElements.put(name, td); + } + + private static void addAreaElement() { + String name = "area"; + List atts = new ArrayList(5); + atts.add( + new HtmlAttributeDesc( + "shape", new String[] {"rect", "circle", "poly"}, HtmlAttributeDesc.REQUIRED)); + addSimpleAttribute(atts, "coords"); + addSimpleAttribute(atts, "href"); + atts.add(new HtmlAttributeDesc("nohref", new String[] {"nohref"}, HtmlAttributeDesc.IMPLIED)); + atts.add(new HtmlAttributeDesc("alt", null, HtmlAttributeDesc.REQUIRED)); + HtmlTagDesc td = new HtmlTagDesc(name, true, false, null, atts); + stSupportedElements.put(name, td); + } + + private static void addBaseElement() { + String name = "base"; + List atts = new ArrayList(1); + addSimpleAttribute(atts, "href"); + HtmlTagDesc td = new HtmlTagDesc(name, true, false, null, atts); + stSupportedElements.put(name, td); + } + + private static void addBasefontElement() { + String name = "basefont"; + HtmlTagDesc td = new HtmlTagDesc(name, true, false, null, null); + td.setAttributes(new String[] {"size"}); + stSupportedElements.put(name, td); + } + + private static void addBlockquoteElement(List bodyContent) { + /* The BLOCKQUOTE element */ + String name = "blockquote"; + HtmlTagDesc td = new HtmlTagDesc(name, true, true, bodyContent, null); + stSupportedElements.put(name, td); + } + + private static void addBodyElement(List bodyContent) { + String name = "body"; + HtmlTagDesc td = new HtmlTagDesc(name, true, true, bodyContent, null); + td.setAttributes(new String[] {"bgcolor", "text", "link", "vlink", "alink", "background"}); + stSupportedElements.put(name, td); + } + + private static void addBrElement() { + String name = "br"; + HtmlTagDesc td = new HtmlTagDesc(name, true, false, null, null); + stSupportedElements.put(name, td); + } + + private static void addCaptionElement(List textContent, HtmlAttributeDesc valignAtt) { + String name = "caption"; + List atts = new ArrayList(1); + atts.add(valignAtt); + HtmlTagDesc td = new HtmlTagDesc(name, true, true, textContent, atts); + stSupportedElements.put(name, td); + } + + private static void addCenterElement(List bodyContent) { + String name = "center"; + HtmlTagDesc td = new HtmlTagDesc(name, true, true, bodyContent, null); + stSupportedElements.put(name, td); + } + + private static void addDdElement(List flowContent) { + String name = "dd"; + HtmlTagDesc td = new HtmlTagDesc(name, true, false, flowContent, null); + stSupportedElements.put(name, td); + } + + private static void addDirElement(List listContent) { + String name = "dir"; + List atts = new ArrayList(1); + addSelfAttribute(atts, "compact"); + HtmlTagDesc td = new HtmlTagDesc(name, true, true, listContent, atts); + stSupportedElements.put(name, td); + } + + private static void addDivElement(List bodyContent, HtmlAttributeDesc halignAtt) { + String name = "div"; + List atts = new ArrayList(1); + atts.add(halignAtt); + HtmlTagDesc td = new HtmlTagDesc(name, true, true, bodyContent, atts); + stSupportedElements.put(name, td); + } + + private static void addDlElement() { + String name = "dl"; + List dlContent = new ArrayList(2); + addStringsToList(new String[] {"dt", "dd"}, dlContent); + List atts = new ArrayList(1); + addSelfAttribute(atts, "compact"); + HtmlTagDesc td = new HtmlTagDesc(name, true, true, dlContent, atts); + stSupportedElements.put(name, td); + } + + private static void addDtElement(List textContent) { + String name = "dt"; + HtmlTagDesc td = new HtmlTagDesc(name, true, false, textContent, null); + stSupportedElements.put(name, td); + } + + private static void addFontElement(List textContent) { + String name = "font"; + HtmlTagDesc td = new HtmlTagDesc(name, true, true, textContent, null); + td.setAttributes(new String[] {"size", "color"}); + stSupportedElements.put(name, td); + } + + private static void addFormElement(List bodyContent) { + final String name = "form"; + List atts = new ArrayList(3); + addSimpleAttribute(atts, "action"); + atts.add(new HtmlAttributeDesc("method", null, HtmlAttributeDesc.OTHER)); + atts.add(new HtmlAttributeDesc("enctype", null, HtmlAttributeDesc.OTHER)); + List formContent = new ArrayList(bodyContent.size()); + formContent.addAll(bodyContent); + removeStringsFromList(formContent, new String[] {"form"}); + HtmlTagDesc td = new HtmlTagDesc(name, true, true, formContent, atts); + stSupportedElements.put(name, td); + } + + private static void addHeadElement() { + String name = "head"; + String[] headMisc = new String[] {"script", "style", "meta", "link"}; + List headContent = new ArrayList(7); + headContent.add("title"); + headContent.add("isindex"); + headContent.add("base"); + HtmlTagDesc td = new HtmlTagDesc(name, false, false, headContent, null); + stSupportedElements.put(name, td); + /* Attributes TITLE (required), ISINDEX (optional), and BASE (optional) + * are supposed to come in that order, ahead of anything else. + * For the moment, just toss them in with the rest. */ + addStringsToList(headMisc, headContent); + } + + private static void addHrElement(HtmlAttributeDesc halignAtt) { + String name = "hr"; + List atts = new ArrayList(4); + + atts.add(halignAtt); + addSelfAttribute(atts, "noshade"); + addSimpleAttribute(atts, "size"); + addSimpleAttribute(atts, "width"); + HtmlTagDesc td = new HtmlTagDesc(name, true, false, null, atts); + stSupportedElements.put(name, td); + } + + private static void addImgElement(HtmlAttributeDesc ialignAtt) { + String name = "img"; + List atts = new ArrayList(10); + addRequiredAttribute(atts, "src"); + addSimpleAttribute(atts, "alt"); + atts.add(ialignAtt); + addSimpleAttribute(atts, "height"); + addSimpleAttribute(atts, "width"); + addSimpleAttribute(atts, "border"); + addSimpleAttribute(atts, "hspace"); + addSimpleAttribute(atts, "vspace"); + addSimpleAttribute(atts, "usemap"); + addSelfAttribute(atts, "ismap"); + HtmlTagDesc td = new HtmlTagDesc(name, true, false, null, atts); + stSupportedElements.put(name, td); + } + + private static void addInputElement(HtmlAttributeDesc ialignAtt) { + final String name = "input"; + List atts = new ArrayList(8); + atts.add( + new HtmlAttributeDesc( + "type", + new String[] { + "text", "password", "checkbox", "radio", "submit", "reset", "file", "hidden", "image" + }, HtmlAttributeDesc.OTHER)); - addSimpleAttribute (atts, "name"); - addSimpleAttribute (atts, "value"); - addSelfAttribute (atts, "checked"); - addSimpleAttribute (atts, "size"); - addSimpleAttribute (atts, "maxlength"); - addSimpleAttribute (atts, "src"); - atts.add (ialignAtt); - HtmlTagDesc td = new HtmlTagDesc (name, true, true, null, atts); - stSupportedElements.put (name, td); - } - - private static void addIsindexElement () - { - final String name = "isindex"; - List atts = new ArrayList (1); - addSimpleAttribute (atts, "prompt"); - HtmlTagDesc td = new HtmlTagDesc (name, true, false, null, atts); - stSupportedElements.put (name, td); - } - - private static void addLiElement (List flowContent) - { - final String name = "li"; - List atts = new ArrayList (2); - addSimpleAttribute (atts, "type"); - addSimpleAttribute (atts, "value"); - HtmlTagDesc td = new HtmlTagDesc (name, true, false, flowContent, atts); - stSupportedElements.put (name, td); - } - - private static void addLinkElement () - { - final String name = "link"; - HtmlTagDesc td = new HtmlTagDesc (name, true, false, null, null); - td.setAttributes (new String [] - { "href", "rel", "rev", "title" }); - stSupportedElements.put (name, td); - - } - - private static void addMapElement () - { - final String name = "map"; - List atts = new ArrayList (1); - addSimpleAttribute (atts, "name"); - List mapContent = new ArrayList (1); - mapContent.add ("area"); - HtmlTagDesc td = new HtmlTagDesc (name, true, true, mapContent, atts); - stSupportedElements.put (name, td); - } - - private static void addMenuElement (List listContent) - { - final String name = "menu"; - List atts = new ArrayList (1); - addSelfAttribute (atts, "compact"); - HtmlTagDesc td = new HtmlTagDesc (name, true, true, listContent, atts); - stSupportedElements.put (name, td); - } - - private static void addMetaElement () - { - final String name = "meta"; - List atts = new ArrayList (3); - addSimpleAttribute (atts, "http-equiv"); - addSimpleAttribute (atts, "name"); - addRequiredAttribute (atts, "content"); - HtmlTagDesc td = new HtmlTagDesc (name, true, false, null, atts); - stSupportedElements.put (name, td); - } - - private static void addOlElement (List listContent) - { - final String name = "ol"; - List atts = new ArrayList (3); - addSimpleAttribute (atts, "type"); - addSimpleAttribute (atts, "start"); - addSelfAttribute (atts, "compact"); - HtmlTagDesc td = new HtmlTagDesc (name, true, true, listContent, atts); - stSupportedElements.put (name, td); - } - - private static void addOptionElement () - { - final String name = "option"; - List atts = new ArrayList (2); - addSelfAttribute (atts, "selected"); - addSimpleAttribute (atts,"value"); - List content = new ArrayList (1); - content.add (HtmlSpecialToken.PCDATA); - HtmlTagDesc td = new HtmlTagDesc (name, true, true, content, atts); - stSupportedElements.put (name, td); - } - - private static void addPElement (HtmlAttributeDesc halignAtt, List textContent) - { - final String name = "p"; - List atts = new ArrayList (1); - atts.add (halignAtt); - HtmlTagDesc td = new HtmlTagDesc (name, true, false, textContent, atts); - stSupportedElements.put (name, td); - } - - private static void addParamElement () - { - final String name = "param"; - List atts = new ArrayList (2); - addRequiredAttribute (atts, "name"); - addSimpleAttribute (atts, "value"); - HtmlTagDesc td = new HtmlTagDesc (name, true, false, null, atts); - stSupportedElements.put (name, td); - } - - private static void addPreElement (List textContent) - { - final String name = "pre"; - List atts = new ArrayList (1); - addSimpleAttribute(atts, "width"); - List preContent = new ArrayList (35); - preContent.addAll(textContent); - /* Take out excluded elements */ - removeStringsFromList (preContent, - new String [] - {"img", "big", "small", "sub", "sup", "font"}); - HtmlTagDesc td = new HtmlTagDesc (name, true, true, preContent, atts); - stSupportedElements.put (name, td); - } - - private static void addScriptElement () - { - /* In HTML 3.2, this is just a placeholder */ - final String name = "script"; - List content = new ArrayList (1); - content.add (HtmlSpecialToken.PCDATA); - HtmlTagDesc td = new HtmlTagDesc (name, true, true, content, null); - stSupportedElements.put (name, td); - } - - private static void addSelectElement () - { - final String name = "select"; - List atts = new ArrayList (3); - addSimpleAttribute (atts, "name"); - addSimpleAttribute (atts, "size"); - addSelfAttribute (atts, "multiple"); - List content = new ArrayList (1); - content.add ("option"); - HtmlTagDesc td = new HtmlTagDesc (name, true, true, content, atts); - stSupportedElements.put (name, td); - } - - private static void addStyleElement () - { - /* In HTML 3.2, this is just a placeholder */ - final String name = "style"; - List content = new ArrayList (1); - content.add (HtmlSpecialToken.PCDATA); - HtmlTagDesc td = new HtmlTagDesc (name, true, true, content, null); - stSupportedElements.put (name, td); - } - - private static void addTableElement () - { - final String name = "table"; - List atts = new ArrayList (5); - atts.add (new HtmlAttributeDesc ("align", - new String[] {"left", "center", "right"}, - HtmlAttributeDesc.IMPLIED)); - addSimpleAttribute (atts, "width"); - addSimpleAttribute (atts, "border"); - addSimpleAttribute (atts, "cellspacing"); - addSimpleAttribute (atts, "cellpadding"); - List content = new ArrayList (2); - content.add ("caption"); - content.add ("tr"); - HtmlTagDesc td = new HtmlTagDesc (name, true, true, content, atts); - stSupportedElements.put (name, td); - } - - private static void addTextareaElement () - { - final String name = "textarea"; - List atts = new ArrayList (3); - addSimpleAttribute (atts, "name"); - addSimpleAttribute (atts, "rows"); - addSimpleAttribute (atts, "cols"); - - List content = new ArrayList (1); - content.add (HtmlSpecialToken.PCDATA); - HtmlTagDesc td = new HtmlTagDesc (name, true, true, content, atts); - stSupportedElements.put (name, td); - } - - private static void addTdElement (List bodyContent, List thtdAtts) - { - final String name = "td"; - HtmlTagDesc td = new HtmlTagDesc (name, true, false, bodyContent, thtdAtts); - stSupportedElements.put (name, td); - } - - private static void addThElement (List bodyContent, List thtdAtts) - { - final String name = "th"; - HtmlTagDesc td = new HtmlTagDesc (name, true, false, bodyContent, thtdAtts); - stSupportedElements.put (name, td); - } - - private static void addTitleElement () - { - /* I'm confused by the DTD for this one. - * Content consists only of PCDATA, but certain elements are - * specifically excluded from its content. This seems - * redundant. */ - String name = "title"; - List pcdataContent = new ArrayList (1); - pcdataContent.add (HtmlSpecialToken.PCDATA); - HtmlTagDesc td = new HtmlTagDesc (name, true, true, pcdataContent, null); - stSupportedElements.put (name, td); - } - - private static void addTrElement - (HtmlAttributeDesc halignAtt, HtmlAttributeDesc valignAtt) - { - final String name = "tr"; - List atts = new ArrayList (2); - atts.add (halignAtt); - atts.add (valignAtt); - List content = new ArrayList (2); - content.add ("th"); - content.add ("td"); - HtmlTagDesc td = new HtmlTagDesc (name, true, false, content, atts); - stSupportedElements.put (name, td); - } - - private static void addUlElement (List listContent) - { - final String name = "ul"; - List atts = new ArrayList (2); - addSimpleAttribute (atts, "type"); - addSelfAttribute (atts, "compact"); - HtmlTagDesc td = new HtmlTagDesc (name, true, true, listContent, atts); - stSupportedElements.put (name, td); - } - - + addSimpleAttribute(atts, "name"); + addSimpleAttribute(atts, "value"); + addSelfAttribute(atts, "checked"); + addSimpleAttribute(atts, "size"); + addSimpleAttribute(atts, "maxlength"); + addSimpleAttribute(atts, "src"); + atts.add(ialignAtt); + HtmlTagDesc td = new HtmlTagDesc(name, true, true, null, atts); + stSupportedElements.put(name, td); + } + + private static void addIsindexElement() { + final String name = "isindex"; + List atts = new ArrayList(1); + addSimpleAttribute(atts, "prompt"); + HtmlTagDesc td = new HtmlTagDesc(name, true, false, null, atts); + stSupportedElements.put(name, td); + } + + private static void addLiElement(List flowContent) { + final String name = "li"; + List atts = new ArrayList(2); + addSimpleAttribute(atts, "type"); + addSimpleAttribute(atts, "value"); + HtmlTagDesc td = new HtmlTagDesc(name, true, false, flowContent, atts); + stSupportedElements.put(name, td); + } + + private static void addLinkElement() { + final String name = "link"; + HtmlTagDesc td = new HtmlTagDesc(name, true, false, null, null); + td.setAttributes(new String[] {"href", "rel", "rev", "title"}); + stSupportedElements.put(name, td); + } + + private static void addMapElement() { + final String name = "map"; + List atts = new ArrayList(1); + addSimpleAttribute(atts, "name"); + List mapContent = new ArrayList(1); + mapContent.add("area"); + HtmlTagDesc td = new HtmlTagDesc(name, true, true, mapContent, atts); + stSupportedElements.put(name, td); + } + + private static void addMenuElement(List listContent) { + final String name = "menu"; + List atts = new ArrayList(1); + addSelfAttribute(atts, "compact"); + HtmlTagDesc td = new HtmlTagDesc(name, true, true, listContent, atts); + stSupportedElements.put(name, td); + } + + private static void addMetaElement() { + final String name = "meta"; + List atts = new ArrayList(3); + addSimpleAttribute(atts, "http-equiv"); + addSimpleAttribute(atts, "name"); + addRequiredAttribute(atts, "content"); + HtmlTagDesc td = new HtmlTagDesc(name, true, false, null, atts); + stSupportedElements.put(name, td); + } + + private static void addOlElement(List listContent) { + final String name = "ol"; + List atts = new ArrayList(3); + addSimpleAttribute(atts, "type"); + addSimpleAttribute(atts, "start"); + addSelfAttribute(atts, "compact"); + HtmlTagDesc td = new HtmlTagDesc(name, true, true, listContent, atts); + stSupportedElements.put(name, td); + } + + private static void addOptionElement() { + final String name = "option"; + List atts = new ArrayList(2); + addSelfAttribute(atts, "selected"); + addSimpleAttribute(atts, "value"); + List content = new ArrayList(1); + content.add(HtmlSpecialToken.PCDATA); + HtmlTagDesc td = new HtmlTagDesc(name, true, true, content, atts); + stSupportedElements.put(name, td); + } + + private static void addPElement(HtmlAttributeDesc halignAtt, List textContent) { + final String name = "p"; + List atts = new ArrayList(1); + atts.add(halignAtt); + HtmlTagDesc td = new HtmlTagDesc(name, true, false, textContent, atts); + stSupportedElements.put(name, td); + } + + private static void addParamElement() { + final String name = "param"; + List atts = new ArrayList(2); + addRequiredAttribute(atts, "name"); + addSimpleAttribute(atts, "value"); + HtmlTagDesc td = new HtmlTagDesc(name, true, false, null, atts); + stSupportedElements.put(name, td); + } + + private static void addPreElement(List textContent) { + final String name = "pre"; + List atts = new ArrayList(1); + addSimpleAttribute(atts, "width"); + List preContent = new ArrayList(35); + preContent.addAll(textContent); + /* Take out excluded elements */ + removeStringsFromList(preContent, new String[] {"img", "big", "small", "sub", "sup", "font"}); + HtmlTagDesc td = new HtmlTagDesc(name, true, true, preContent, atts); + stSupportedElements.put(name, td); + } + + private static void addScriptElement() { + /* In HTML 3.2, this is just a placeholder */ + final String name = "script"; + List content = new ArrayList(1); + content.add(HtmlSpecialToken.PCDATA); + HtmlTagDesc td = new HtmlTagDesc(name, true, true, content, null); + stSupportedElements.put(name, td); + } + + private static void addSelectElement() { + final String name = "select"; + List atts = new ArrayList(3); + addSimpleAttribute(atts, "name"); + addSimpleAttribute(atts, "size"); + addSelfAttribute(atts, "multiple"); + List content = new ArrayList(1); + content.add("option"); + HtmlTagDesc td = new HtmlTagDesc(name, true, true, content, atts); + stSupportedElements.put(name, td); + } + + private static void addStyleElement() { + /* In HTML 3.2, this is just a placeholder */ + final String name = "style"; + List content = new ArrayList(1); + content.add(HtmlSpecialToken.PCDATA); + HtmlTagDesc td = new HtmlTagDesc(name, true, true, content, null); + stSupportedElements.put(name, td); + } + + private static void addTableElement() { + final String name = "table"; + List atts = new ArrayList(5); + atts.add( + new HtmlAttributeDesc( + "align", new String[] {"left", "center", "right"}, HtmlAttributeDesc.IMPLIED)); + addSimpleAttribute(atts, "width"); + addSimpleAttribute(atts, "border"); + addSimpleAttribute(atts, "cellspacing"); + addSimpleAttribute(atts, "cellpadding"); + List content = new ArrayList(2); + content.add("caption"); + content.add("tr"); + HtmlTagDesc td = new HtmlTagDesc(name, true, true, content, atts); + stSupportedElements.put(name, td); + } + + private static void addTextareaElement() { + final String name = "textarea"; + List atts = new ArrayList(3); + addSimpleAttribute(atts, "name"); + addSimpleAttribute(atts, "rows"); + addSimpleAttribute(atts, "cols"); + + List content = new ArrayList(1); + content.add(HtmlSpecialToken.PCDATA); + HtmlTagDesc td = new HtmlTagDesc(name, true, true, content, atts); + stSupportedElements.put(name, td); + } + + private static void addTdElement(List bodyContent, List thtdAtts) { + final String name = "td"; + HtmlTagDesc td = new HtmlTagDesc(name, true, false, bodyContent, thtdAtts); + stSupportedElements.put(name, td); + } + + private static void addThElement(List bodyContent, List thtdAtts) { + final String name = "th"; + HtmlTagDesc td = new HtmlTagDesc(name, true, false, bodyContent, thtdAtts); + stSupportedElements.put(name, td); + } + + private static void addTitleElement() { + /* I'm confused by the DTD for this one. + * Content consists only of PCDATA, but certain elements are + * specifically excluded from its content. This seems + * redundant. */ + String name = "title"; + List pcdataContent = new ArrayList(1); + pcdataContent.add(HtmlSpecialToken.PCDATA); + HtmlTagDesc td = new HtmlTagDesc(name, true, true, pcdataContent, null); + stSupportedElements.put(name, td); + } + + private static void addTrElement(HtmlAttributeDesc halignAtt, HtmlAttributeDesc valignAtt) { + final String name = "tr"; + List atts = new ArrayList(2); + atts.add(halignAtt); + atts.add(valignAtt); + List content = new ArrayList(2); + content.add("th"); + content.add("td"); + HtmlTagDesc td = new HtmlTagDesc(name, true, false, content, atts); + stSupportedElements.put(name, td); + } + + private static void addUlElement(List listContent) { + final String name = "ul"; + List atts = new ArrayList(2); + addSimpleAttribute(atts, "type"); + addSelfAttribute(atts, "compact"); + HtmlTagDesc td = new HtmlTagDesc(name, true, true, listContent, atts); + stSupportedElements.put(name, td); + } } diff --git a/jhove-modules/html-hul/src/main/java/edu/harvard/hul/ois/jhove/module/html/Html4DocDesc.java b/jhove-modules/html-hul/src/main/java/edu/harvard/hul/ois/jhove/module/html/Html4DocDesc.java index efcb263c2..3c9b459f1 100644 --- a/jhove-modules/html-hul/src/main/java/edu/harvard/hul/ois/jhove/module/html/Html4DocDesc.java +++ b/jhove-modules/html-hul/src/main/java/edu/harvard/hul/ois/jhove/module/html/Html4DocDesc.java @@ -1,534 +1,484 @@ -/********************************************************************** - * Jhove - JSTOR/Harvard Object Validation Environment - * Copyright 2004 by JSTOR and the President and Fellows of Harvard College - **********************************************************************/ - +/** + * ******************************************************************** Jhove - JSTOR/Harvard Object + * Validation Environment Copyright 2004 by JSTOR and the President and Fellows of Harvard College + * ******************************************************************** + */ package edu.harvard.hul.ois.jhove.module.html; -import java.util.*; -import edu.harvard.hul.ois.jhove.module.HtmlModule; +import java.util.*; /** - * Abstract class for common features of HTML 4.0 and 4.01 - * documents. + * Abstract class for common features of HTML 4.0 and 4.01 documents. * * @author Gary McGath - * */ public abstract class Html4DocDesc extends HtmlDocDesc { - /** Names of font-related elements. */ - protected static String[] fontMarkup; - /** Names of phrase elements. */ - protected static String[] phraseMarkup; - /** Names of special elements. */ - protected static String[] specialMarkup; - /** Names of form elements. */ - protected static String[] formMarkup; - /** Names of list elements. */ - protected static String[] listMarkup; - - /** List of block elements. */ - protected static List blockContent; - /** List of flow elements. */ - protected static List flowContent; - /** List of inline elements. */ - protected static List inlineContent; - /** List consisting of the LI element. */ - protected static List listContent; - /** List of elements for the BODY element and some other elements. */ - protected static List bodyContent; - - - /** Core attributes list. */ - protected static List coreAttrs; - /** Internationalization attributes list. */ - protected static List i18nAttrs; - /** Event attributes list. */ - protected static List eventAttrs; - /** Big attributes list. The dtd calls this "attrs", but here - * it's called lotsaAttrs to avoid confusion with common - * local variables. */ - protected static List bigAttrs; - /** Big attributes plus reserved attributes. */ - protected static List biggerAttrs; - /** Attributes described as "reserved for future use." */ - protected static List reservedAttrs; - - /** Horizontal alignment attribute for cells. */ - protected static HtmlAttributeDesc halignAtt; - /** Vertical alignment attribute for cells. */ - protected static HtmlAttributeDesc valignAtt; - - /** Attributes for TH and TD elements */ - protected static List thtdAtts; - - protected Html4DocDesc() { - super(); - } - - /** Initialization code. This is called from the static initializer - * of our subclasses. */ - protected static void classInit4 (Map stSupportedElements) - { - phraseMarkup = new String[] - { "em", "strong", "dfn", "code", "samp", "kbd", "var", "cite", - "abbr", "acronym" }; - formMarkup = new String[] - { "input", "select", "textarea", "label", "button" }; - - /* Core attrs list, used for various elements */ - coreAttrs = new ArrayList (4); - addSimpleAttribute (coreAttrs, "id"); - addSimpleAttribute (coreAttrs, "class"); - addSimpleAttribute (coreAttrs, "style"); - addSimpleAttribute (coreAttrs, "title"); - - /* Internationalization attrs list */ - i18nAttrs = new ArrayList (2); - addSimpleAttribute (i18nAttrs, "lang"); - i18nAttrs.add (new HtmlAttributeDesc ("id", - new String[] {"ltr", "rtl"}, - HtmlAttributeDesc.IMPLIED)); - - /* Event attrs list */ - eventAttrs = new ArrayList (10); - addSimpleAttribute (eventAttrs, "onclick"); - addSimpleAttribute (eventAttrs, "ondblclick"); - addSimpleAttribute (eventAttrs, "onmousedown"); - addSimpleAttribute (eventAttrs, "onmouseup"); - addSimpleAttribute (eventAttrs, "onmouseover"); - addSimpleAttribute (eventAttrs, "onmousemove"); - addSimpleAttribute (eventAttrs, "onmouseout"); - addSimpleAttribute (eventAttrs, "onkeypress"); - addSimpleAttribute (eventAttrs, "onkeydown"); - addSimpleAttribute (eventAttrs, "onkeyup"); - - bigAttrs = new ArrayList - (coreAttrs.size() + i18nAttrs.size() + eventAttrs.size()); - bigAttrs.addAll (coreAttrs); - bigAttrs.addAll (i18nAttrs); - bigAttrs.addAll (eventAttrs); - - /* Attributes described as "reserved for future use." */ - reservedAttrs = new ArrayList (3); - addSimpleAttribute (reservedAttrs, "datasrc"); - addSimpleAttribute (reservedAttrs, "datafld"); - addSimpleAttribute (reservedAttrs, "dataformatas"); // yes, spelled that way - - /* Big attributes plus reserved attributes. */ - biggerAttrs = new ArrayList (bigAttrs.size() + 3); - biggerAttrs.addAll (bigAttrs); - biggerAttrs.addAll (reservedAttrs); - - /* Reusable attributes for cell alignment. */ - halignAtt = new HtmlAttributeDesc - ("align", - new String [] {"left", "center", "right", "justify", "char"}, - HtmlAttributeDesc.IMPLIED); - valignAtt = - new HtmlAttributeDesc ("valign", - new String[] { "top", "middle", "bottom", "baseline" }, - HtmlAttributeDesc.IMPLIED); - } - - /** Static initializers for each element. If elements are common to more - * than one HTML version, they should be moved into the superclass. - * Different initializers may have different argument lists. */ - - /** Defines the ADDRESS element. */ - protected static void addAddressElement (Map stSupportedElements) - { - String name = "address"; - List addressContent = new ArrayList (36); - addressContent.addAll (inlineContent); - addressContent.add ("p"); - HtmlTagDesc td = - new HtmlTagDesc (name, true, true, addressContent, bigAttrs); - stSupportedElements.put (name, td); - } - - /** Defines the BDO element. */ - protected static void addBdoElement (Map stSupportedElements) - { - String name = "bdo"; - List atts = new ArrayList (coreAttrs.size () + 2); - atts.addAll (coreAttrs); - addSimpleAttribute (atts, "lang"); - atts.add (new HtmlAttributeDesc ("dir", - new String[] { "ltr", "rtl" }, HtmlAttributeDesc.REQUIRED)); - HtmlTagDesc td = new HtmlTagDesc (name, true, true, inlineContent, atts); - stSupportedElements.put (name, td); - } - - /** Defines the BODY element. */ - protected static void addBodyElement (Map stSupportedElements) - { - /* bodyContent is different for transitional and strict, but - * the code in this function is common to both. */ - String name = "body"; - List atts = new ArrayList (bigAttrs.size () + 2); - atts.addAll (bigAttrs); - addSimpleAttribute (atts, "onload"); - addSimpleAttribute (atts, "onunload"); - HtmlTagDesc td = new HtmlTagDesc (name, true, true, bodyContent, atts); - stSupportedElements.put (name, td); - } - - /** Defines the COL element. */ - protected static void addColElement - (Map stSupportedElements, List cellalignAttrs) - { - String name = "col"; - List atts = new ArrayList (bigAttrs.size () + 8); - atts.addAll (bigAttrs); - atts.addAll (cellalignAttrs); - addSimpleAttribute (atts, "span"); - addSimpleAttribute (atts, "width"); - HtmlTagDesc td = new HtmlTagDesc (name, true, false, null, atts); - stSupportedElements.put (name, td); - } - - /** Defines the COLGROUP element. */ - protected static void addColgroupElement - (Map stSupportedElements, List cellalignAttrs) - { - String name = "colgroup"; - List content = new ArrayList (1); - content.add ("col"); - List atts = new ArrayList (bigAttrs.size () + 8); - atts.addAll (bigAttrs); - atts.addAll (cellalignAttrs); - addSimpleAttribute (atts, "span"); - addSimpleAttribute (atts, "width"); - - HtmlTagDesc td = new HtmlTagDesc (name, true, false, content, atts); - stSupportedElements.put (name, td); - } - - /** Defines the DD element. */ - protected static void addDdElement (Map stSupportedElements) - { - String name = "dd"; - HtmlTagDesc td = - new HtmlTagDesc (name, true, false, flowContent, bigAttrs); - stSupportedElements.put (name, td); - } - - /** Defines the DEL element. */ - protected static void addDelElement (Map stSupportedElements) - { - final String name = "del"; - List atts = new ArrayList (bigAttrs.size () + 2); - atts.addAll (bigAttrs); - addSimpleAttribute (atts, "cite"); - addSimpleAttribute (atts, "datetime"); - HtmlTagDesc td = new HtmlTagDesc (name, true, true, flowContent, atts); - stSupportedElements.put (name, td); - } - - /** Defines the DT element. */ - protected static void addDtElement (Map stSupportedElements) - { - String name = "dt"; - HtmlTagDesc td = - new HtmlTagDesc (name, true, false, inlineContent, bigAttrs); - stSupportedElements.put (name, td); - } - - /** Defines the FIELDSET element. */ - protected static void addFieldsetElement (Map stSupportedElements) - { - String name = "fieldset"; - List content = new ArrayList (flowContent.size () + 3); - content.addAll (flowContent); - content.add (HtmlSpecialToken.PCDATA); - content.add ("legend"); - - HtmlTagDesc td = - new HtmlTagDesc (name, true, true, content, bigAttrs); - stSupportedElements.put (name, td); - } - - /** Defines the INS element. */ - protected static void addInsElement (Map stSupportedElements) - { - final String name = "ins"; - List atts = new ArrayList (bigAttrs.size () + 2); - atts.addAll (bigAttrs); - addSimpleAttribute (atts, "cite"); - addSimpleAttribute (atts, "datetime"); - HtmlTagDesc td = new HtmlTagDesc (name, true, true, flowContent, atts); - stSupportedElements.put (name, td); - } - - /** Defines the LABEL element. */ - protected static void addLabelElement - (Map stSupportedElements) - { - final String name = "label"; - List atts = new ArrayList (bigAttrs.size () + 4); - atts.addAll (bigAttrs); - addSimpleAttribute (atts, "for"); - addSimpleAttribute (atts, "accesskey"); - addSimpleAttribute (atts, "onfocus"); - addSimpleAttribute (atts, "onblur"); - HtmlTagDesc td = new HtmlTagDesc (name, true, true, inlineContent, atts); - stSupportedElements.put (name, td); - } - - /** Defines the MAP element. - * HTML 4.0 and 4.01 actually have different definitions here. 4.0 - * allows block content or AREA elements, but not a mix of the two; - * 4.01 allows a mix of the two. The current version of the code - * doesn't allow that distinction to be expressed. (There are no - * differences between Strict and Transitional.) */ - protected static void addMapElement (Map stSupportedElements) - { - final String name = "map"; - List atts = new ArrayList (bigAttrs.size () + 1); - atts.addAll (bigAttrs); - addSimpleAttribute (atts, "name"); - List mapContent = new ArrayList (1); - mapContent.addAll (blockContent); - mapContent.add ("area"); - HtmlTagDesc td = new HtmlTagDesc (name, true, true, mapContent, atts); - stSupportedElements.put (name, td); - } - - /** Defines the META element. */ - protected static void addMetaElement (Map stSupportedElements) - { - final String name = "meta"; - List atts = new ArrayList (3); - addSimpleAttribute (atts, "http-equiv"); - addSimpleAttribute (atts, "name"); - addRequiredAttribute (atts, "content"); - HtmlTagDesc td = new HtmlTagDesc (name, true, false, null, atts); - stSupportedElements.put (name, td); - } - - /** Defines the OPTGROUP (option group) element. */ - protected static void addOptgroupElement - (Map stSupportedElements) - { - final String name = "option"; - List atts = new ArrayList (bigAttrs.size () + 2); - atts.addAll (bigAttrs); - addSelfAttribute (atts, "selected"); - addSimpleAttribute (atts,"label"); - List content = new ArrayList (1); - content.add ("option"); - HtmlTagDesc td = new HtmlTagDesc (name, true, true, content, atts); - stSupportedElements.put (name, td); - } - - /** Defines the OPTION element. */ - protected static void addOptionElement - (Map stSupportedElements) - { - final String name = "option"; - List atts = new ArrayList (bigAttrs.size () + 4); - atts.addAll (bigAttrs); - addSelfAttribute (atts, "selected"); - addSelfAttribute (atts, "disabled"); - addSimpleAttribute (atts, "label"); - addSimpleAttribute (atts, "value"); - List content = new ArrayList (1); - content.add (HtmlSpecialToken.PCDATA); - HtmlTagDesc td = new HtmlTagDesc (name, true, true, content, atts); - stSupportedElements.put (name, td); - } - - /** Defines the PARAM element. */ - protected static void addParamElement (Map stSupportedElements) - { - final String name = "param"; - List atts = new ArrayList (2); - addRequiredAttribute (atts, "name"); - addSimpleAttribute (atts, "value"); - atts.add (new HtmlAttributeDesc ("valuetype", - new String [] { "data", "ref", "object" }, - HtmlAttributeDesc.OTHER )); - addSimpleAttribute (atts, "type"); - HtmlTagDesc td = new HtmlTagDesc (name, true, false, null, atts); - stSupportedElements.put (name, td); - } - - /** Defines the Q (short quote) element. */ - protected static void addQElement (Map stSupportedElements) - { - final String name = "q"; - List atts = new ArrayList (bigAttrs.size () + 1); - atts.addAll (bigAttrs); - addSimpleAttribute (atts, "cite"); - HtmlTagDesc td = new HtmlTagDesc (name, true, true, inlineContent, atts); - stSupportedElements.put (name, td); - } - - /** Defines the SELECT element. */ - protected static void addSelectElement (Map stSupportedElements) - { - final String name = "select"; - List atts = new ArrayList (biggerAttrs.size () + 10); - atts.addAll (biggerAttrs); - addSimpleAttribute (atts, "name"); - addSimpleAttribute (atts, "size"); - addSelfAttribute (atts, "multiple"); - addSelfAttribute (atts, "disabled"); - addSimpleAttribute (atts, "tabindex"); - addSimpleAttribute (atts, "onfocus"); - addSimpleAttribute (atts, "onblur"); - addSimpleAttribute (atts, "onchange"); - List content = new ArrayList (2); - content.add ("option"); - content.add ("optgroup"); - HtmlTagDesc td = new HtmlTagDesc (name, true, true, content, atts); - stSupportedElements.put (name, td); - } - - - /** Defines the SPAN element. */ - protected static void addSpanElement (Map stSupportedElements) - { - final String name = "span"; - HtmlTagDesc td = - new HtmlTagDesc (name, true, true, inlineContent, biggerAttrs); - stSupportedElements.put (name, td); - } - - - /** Defines the STYLE element. */ - protected static void addStyleElement (Map stSupportedElements) - { - final String name = "style"; - List content = new ArrayList (1); - content.add (HtmlSpecialToken.PCDATA); - List atts = new ArrayList (6); - atts.addAll (i18nAttrs); - addSimpleAttribute (atts, "type"); - addSimpleAttribute (atts, "media"); - addSimpleAttribute (atts, "title"); - HtmlTagDesc td = new HtmlTagDesc (name, true, true, content, atts); - stSupportedElements.put (name, td); - } - - /** Defines the SUB (subscript) element. */ - protected static void addSubElement (Map stSupportedElements) - { - final String name = "sub"; - HtmlTagDesc td = new HtmlTagDesc - (name, true, true, inlineContent, bigAttrs); - stSupportedElements.put (name, td); - } - - /** Defines the SUP (superscript) element. */ - protected static void addSupElement (Map stSupportedElements) - { - final String name = "sup"; - HtmlTagDesc td = new HtmlTagDesc - (name, true, true, inlineContent, bigAttrs); - stSupportedElements.put (name, td); - } - - - /** Defines the TEXTAREA element. */ - protected static void addTextareaElement (Map stSupportedElements) - { - final String name = "textarea"; - List atts = new ArrayList (biggerAttrs.size () + 12); - addSimpleAttribute (atts, "name"); - addSimpleAttribute (atts, "rows"); - addSimpleAttribute (atts, "cols"); - addSelfAttribute (atts, "disabled"); - addSelfAttribute (atts, "readonly"); - addSimpleAttribute (atts, "tabindex"); - addSimpleAttribute (atts, "accesskey"); - addSimpleAttribute (atts, "onfocus"); - addSimpleAttribute (atts, "onblur"); - addSimpleAttribute (atts, "onselect"); - addSimpleAttribute (atts, "onchange"); - - List content = new ArrayList (1); - content.add (HtmlSpecialToken.PCDATA); - HtmlTagDesc td = new HtmlTagDesc (name, true, true, content, atts); - stSupportedElements.put (name, td); - } - - /** Defines the TBODY element. */ - protected static void addTbodyElement (Map stSupportedElements, List cellalignAttrs) - { - final String name = "tbody"; - List content = new ArrayList (1); - content.add ("tr"); - List atts = new ArrayList (bigAttrs.size () + 8); - atts.addAll (bigAttrs); - atts.addAll (cellalignAttrs); - HtmlTagDesc td = new HtmlTagDesc (name, false, false, content, atts); - stSupportedElements.put (name, td); - } - - /** Defines the TD element. - * The strict and transitional versions have - * different attribute sets, but this is taken care of by the - * initialization of thtdAtts. */ - protected static void addTdElement (Map stSupportedElements) - { - final String name = "td"; - HtmlTagDesc td = new HtmlTagDesc (name, true, false, flowContent, thtdAtts); - stSupportedElements.put (name, td); - } - - - /** Defines the TFOOT element. */ - protected static void addTfootElement (Map stSupportedElements, List cellalignAttrs) - { - final String name = "tfoot"; - List content = new ArrayList (1); - content.add ("tr"); - List atts = new ArrayList (bigAttrs.size () + 8); - atts.addAll (bigAttrs); - atts.addAll (cellalignAttrs); - HtmlTagDesc td = new HtmlTagDesc (name, true, false, content, atts); - stSupportedElements.put (name, td); - } - - /** Defines the TH element. - * The strict and transitional versions have - * different attribute sets, but this is taken care of by the - * initialization of thtdAtts. */ - protected static void addThElement (Map stSupportedElements) - { - final String name = "th"; - HtmlTagDesc td = new HtmlTagDesc (name, true, false, flowContent, thtdAtts); - stSupportedElements.put (name, td); - } - - /** Defines the THEAD element. */ - protected static void addTheadElement (Map stSupportedElements, List cellalignAttrs) - { - final String name = "thead"; - List content = new ArrayList (1); - content.add ("tr"); - List atts = new ArrayList (bigAttrs.size () + 8); - atts.addAll (bigAttrs); - atts.addAll (cellalignAttrs); - HtmlTagDesc td = new HtmlTagDesc (name, true, false, content, atts); - stSupportedElements.put (name, td); - } - - /** Defines the TITLE element. */ - protected static void addTitleElement (Map stSupportedElements) - { - /* I'm confused by the DTD for this one. - * Content consists only of PCDATA, but certain elements are - * specifically excluded from its content. This seems - * redundant. */ - String name = "title"; - List pcdataContent = new ArrayList (1); - pcdataContent.add (HtmlSpecialToken.PCDATA); - HtmlTagDesc td = new HtmlTagDesc (name, true, true, pcdataContent, i18nAttrs); - stSupportedElements.put (name, td); - } - + /** Names of font-related elements. */ + protected static String[] fontMarkup; + /** Names of phrase elements. */ + protected static String[] phraseMarkup; + /** Names of special elements. */ + protected static String[] specialMarkup; + /** Names of form elements. */ + protected static String[] formMarkup; + /** Names of list elements. */ + protected static String[] listMarkup; + + /** List of block elements. */ + protected static List blockContent; + /** List of flow elements. */ + protected static List flowContent; + /** List of inline elements. */ + protected static List inlineContent; + /** List consisting of the LI element. */ + protected static List listContent; + /** List of elements for the BODY element and some other elements. */ + protected static List bodyContent; + + /** Core attributes list. */ + protected static List coreAttrs; + /** Internationalization attributes list. */ + protected static List i18nAttrs; + /** Event attributes list. */ + protected static List eventAttrs; + /** + * Big attributes list. The dtd calls this "attrs", but here it's called lotsaAttrs to avoid + * confusion with common local variables. + */ + protected static List bigAttrs; + /** Big attributes plus reserved attributes. */ + protected static List biggerAttrs; + /** Attributes described as "reserved for future use." */ + protected static List reservedAttrs; + + /** Horizontal alignment attribute for cells. */ + protected static HtmlAttributeDesc halignAtt; + /** Vertical alignment attribute for cells. */ + protected static HtmlAttributeDesc valignAtt; + + /** Attributes for TH and TD elements */ + protected static List thtdAtts; + + protected Html4DocDesc() { + super(); + } + + /** Initialization code. This is called from the static initializer of our subclasses. */ + protected static void classInit4(Map stSupportedElements) { + phraseMarkup = + new String[] { + "em", "strong", "dfn", "code", "samp", "kbd", "var", "cite", "abbr", "acronym" + }; + formMarkup = new String[] {"input", "select", "textarea", "label", "button"}; + + /* Core attrs list, used for various elements */ + coreAttrs = new ArrayList(4); + addSimpleAttribute(coreAttrs, "id"); + addSimpleAttribute(coreAttrs, "class"); + addSimpleAttribute(coreAttrs, "style"); + addSimpleAttribute(coreAttrs, "title"); + + /* Internationalization attrs list */ + i18nAttrs = new ArrayList(2); + addSimpleAttribute(i18nAttrs, "lang"); + i18nAttrs.add( + new HtmlAttributeDesc("id", new String[] {"ltr", "rtl"}, HtmlAttributeDesc.IMPLIED)); + + /* Event attrs list */ + eventAttrs = new ArrayList(10); + addSimpleAttribute(eventAttrs, "onclick"); + addSimpleAttribute(eventAttrs, "ondblclick"); + addSimpleAttribute(eventAttrs, "onmousedown"); + addSimpleAttribute(eventAttrs, "onmouseup"); + addSimpleAttribute(eventAttrs, "onmouseover"); + addSimpleAttribute(eventAttrs, "onmousemove"); + addSimpleAttribute(eventAttrs, "onmouseout"); + addSimpleAttribute(eventAttrs, "onkeypress"); + addSimpleAttribute(eventAttrs, "onkeydown"); + addSimpleAttribute(eventAttrs, "onkeyup"); + + bigAttrs = new ArrayList(coreAttrs.size() + i18nAttrs.size() + eventAttrs.size()); + bigAttrs.addAll(coreAttrs); + bigAttrs.addAll(i18nAttrs); + bigAttrs.addAll(eventAttrs); + + /* Attributes described as "reserved for future use." */ + reservedAttrs = new ArrayList(3); + addSimpleAttribute(reservedAttrs, "datasrc"); + addSimpleAttribute(reservedAttrs, "datafld"); + addSimpleAttribute(reservedAttrs, "dataformatas"); // yes, spelled that way + + /* Big attributes plus reserved attributes. */ + biggerAttrs = new ArrayList(bigAttrs.size() + 3); + biggerAttrs.addAll(bigAttrs); + biggerAttrs.addAll(reservedAttrs); + + /* Reusable attributes for cell alignment. */ + halignAtt = + new HtmlAttributeDesc( + "align", + new String[] {"left", "center", "right", "justify", "char"}, + HtmlAttributeDesc.IMPLIED); + valignAtt = + new HtmlAttributeDesc( + "valign", + new String[] {"top", "middle", "bottom", "baseline"}, + HtmlAttributeDesc.IMPLIED); + } + + /** + * Static initializers for each element. If elements are common to more than one HTML version, + * they should be moved into the superclass. Different initializers may have different argument + * lists. + */ + + /** Defines the ADDRESS element. */ + protected static void addAddressElement(Map stSupportedElements) { + String name = "address"; + List addressContent = new ArrayList(36); + addressContent.addAll(inlineContent); + addressContent.add("p"); + HtmlTagDesc td = new HtmlTagDesc(name, true, true, addressContent, bigAttrs); + stSupportedElements.put(name, td); + } + + /** Defines the BDO element. */ + protected static void addBdoElement(Map stSupportedElements) { + String name = "bdo"; + List atts = new ArrayList(coreAttrs.size() + 2); + atts.addAll(coreAttrs); + addSimpleAttribute(atts, "lang"); + atts.add(new HtmlAttributeDesc("dir", new String[] {"ltr", "rtl"}, HtmlAttributeDesc.REQUIRED)); + HtmlTagDesc td = new HtmlTagDesc(name, true, true, inlineContent, atts); + stSupportedElements.put(name, td); + } + + /** Defines the BODY element. */ + protected static void addBodyElement(Map stSupportedElements) { + /* bodyContent is different for transitional and strict, but + * the code in this function is common to both. */ + String name = "body"; + List atts = new ArrayList(bigAttrs.size() + 2); + atts.addAll(bigAttrs); + addSimpleAttribute(atts, "onload"); + addSimpleAttribute(atts, "onunload"); + HtmlTagDesc td = new HtmlTagDesc(name, true, true, bodyContent, atts); + stSupportedElements.put(name, td); + } + + /** Defines the COL element. */ + protected static void addColElement(Map stSupportedElements, List cellalignAttrs) { + String name = "col"; + List atts = new ArrayList(bigAttrs.size() + 8); + atts.addAll(bigAttrs); + atts.addAll(cellalignAttrs); + addSimpleAttribute(atts, "span"); + addSimpleAttribute(atts, "width"); + HtmlTagDesc td = new HtmlTagDesc(name, true, false, null, atts); + stSupportedElements.put(name, td); + } + + /** Defines the COLGROUP element. */ + protected static void addColgroupElement(Map stSupportedElements, List cellalignAttrs) { + String name = "colgroup"; + List content = new ArrayList(1); + content.add("col"); + List atts = new ArrayList(bigAttrs.size() + 8); + atts.addAll(bigAttrs); + atts.addAll(cellalignAttrs); + addSimpleAttribute(atts, "span"); + addSimpleAttribute(atts, "width"); + + HtmlTagDesc td = new HtmlTagDesc(name, true, false, content, atts); + stSupportedElements.put(name, td); + } + + /** Defines the DD element. */ + protected static void addDdElement(Map stSupportedElements) { + String name = "dd"; + HtmlTagDesc td = new HtmlTagDesc(name, true, false, flowContent, bigAttrs); + stSupportedElements.put(name, td); + } + + /** Defines the DEL element. */ + protected static void addDelElement(Map stSupportedElements) { + final String name = "del"; + List atts = new ArrayList(bigAttrs.size() + 2); + atts.addAll(bigAttrs); + addSimpleAttribute(atts, "cite"); + addSimpleAttribute(atts, "datetime"); + HtmlTagDesc td = new HtmlTagDesc(name, true, true, flowContent, atts); + stSupportedElements.put(name, td); + } + + /** Defines the DT element. */ + protected static void addDtElement(Map stSupportedElements) { + String name = "dt"; + HtmlTagDesc td = new HtmlTagDesc(name, true, false, inlineContent, bigAttrs); + stSupportedElements.put(name, td); + } + + /** Defines the FIELDSET element. */ + protected static void addFieldsetElement(Map stSupportedElements) { + String name = "fieldset"; + List content = new ArrayList(flowContent.size() + 3); + content.addAll(flowContent); + content.add(HtmlSpecialToken.PCDATA); + content.add("legend"); + + HtmlTagDesc td = new HtmlTagDesc(name, true, true, content, bigAttrs); + stSupportedElements.put(name, td); + } + + /** Defines the INS element. */ + protected static void addInsElement(Map stSupportedElements) { + final String name = "ins"; + List atts = new ArrayList(bigAttrs.size() + 2); + atts.addAll(bigAttrs); + addSimpleAttribute(atts, "cite"); + addSimpleAttribute(atts, "datetime"); + HtmlTagDesc td = new HtmlTagDesc(name, true, true, flowContent, atts); + stSupportedElements.put(name, td); + } + + /** Defines the LABEL element. */ + protected static void addLabelElement(Map stSupportedElements) { + final String name = "label"; + List atts = new ArrayList(bigAttrs.size() + 4); + atts.addAll(bigAttrs); + addSimpleAttribute(atts, "for"); + addSimpleAttribute(atts, "accesskey"); + addSimpleAttribute(atts, "onfocus"); + addSimpleAttribute(atts, "onblur"); + HtmlTagDesc td = new HtmlTagDesc(name, true, true, inlineContent, atts); + stSupportedElements.put(name, td); + } + + /** + * Defines the MAP element. HTML 4.0 and 4.01 actually have different definitions here. 4.0 allows + * block content or AREA elements, but not a mix of the two; 4.01 allows a mix of the two. The + * current version of the code doesn't allow that distinction to be expressed. (There are no + * differences between Strict and Transitional.) + */ + protected static void addMapElement(Map stSupportedElements) { + final String name = "map"; + List atts = new ArrayList(bigAttrs.size() + 1); + atts.addAll(bigAttrs); + addSimpleAttribute(atts, "name"); + List mapContent = new ArrayList(1); + mapContent.addAll(blockContent); + mapContent.add("area"); + HtmlTagDesc td = new HtmlTagDesc(name, true, true, mapContent, atts); + stSupportedElements.put(name, td); + } + + /** Defines the META element. */ + protected static void addMetaElement(Map stSupportedElements) { + final String name = "meta"; + List atts = new ArrayList(3); + addSimpleAttribute(atts, "http-equiv"); + addSimpleAttribute(atts, "name"); + addRequiredAttribute(atts, "content"); + HtmlTagDesc td = new HtmlTagDesc(name, true, false, null, atts); + stSupportedElements.put(name, td); + } + + /** Defines the OPTGROUP (option group) element. */ + protected static void addOptgroupElement(Map stSupportedElements) { + final String name = "option"; + List atts = new ArrayList(bigAttrs.size() + 2); + atts.addAll(bigAttrs); + addSelfAttribute(atts, "selected"); + addSimpleAttribute(atts, "label"); + List content = new ArrayList(1); + content.add("option"); + HtmlTagDesc td = new HtmlTagDesc(name, true, true, content, atts); + stSupportedElements.put(name, td); + } + + /** Defines the OPTION element. */ + protected static void addOptionElement(Map stSupportedElements) { + final String name = "option"; + List atts = new ArrayList(bigAttrs.size() + 4); + atts.addAll(bigAttrs); + addSelfAttribute(atts, "selected"); + addSelfAttribute(atts, "disabled"); + addSimpleAttribute(atts, "label"); + addSimpleAttribute(atts, "value"); + List content = new ArrayList(1); + content.add(HtmlSpecialToken.PCDATA); + HtmlTagDesc td = new HtmlTagDesc(name, true, true, content, atts); + stSupportedElements.put(name, td); + } + + /** Defines the PARAM element. */ + protected static void addParamElement(Map stSupportedElements) { + final String name = "param"; + List atts = new ArrayList(2); + addRequiredAttribute(atts, "name"); + addSimpleAttribute(atts, "value"); + atts.add( + new HtmlAttributeDesc( + "valuetype", new String[] {"data", "ref", "object"}, HtmlAttributeDesc.OTHER)); + addSimpleAttribute(atts, "type"); + HtmlTagDesc td = new HtmlTagDesc(name, true, false, null, atts); + stSupportedElements.put(name, td); + } + + /** Defines the Q (short quote) element. */ + protected static void addQElement(Map stSupportedElements) { + final String name = "q"; + List atts = new ArrayList(bigAttrs.size() + 1); + atts.addAll(bigAttrs); + addSimpleAttribute(atts, "cite"); + HtmlTagDesc td = new HtmlTagDesc(name, true, true, inlineContent, atts); + stSupportedElements.put(name, td); + } + + /** Defines the SELECT element. */ + protected static void addSelectElement(Map stSupportedElements) { + final String name = "select"; + List atts = new ArrayList(biggerAttrs.size() + 10); + atts.addAll(biggerAttrs); + addSimpleAttribute(atts, "name"); + addSimpleAttribute(atts, "size"); + addSelfAttribute(atts, "multiple"); + addSelfAttribute(atts, "disabled"); + addSimpleAttribute(atts, "tabindex"); + addSimpleAttribute(atts, "onfocus"); + addSimpleAttribute(atts, "onblur"); + addSimpleAttribute(atts, "onchange"); + List content = new ArrayList(2); + content.add("option"); + content.add("optgroup"); + HtmlTagDesc td = new HtmlTagDesc(name, true, true, content, atts); + stSupportedElements.put(name, td); + } + + /** Defines the SPAN element. */ + protected static void addSpanElement(Map stSupportedElements) { + final String name = "span"; + HtmlTagDesc td = new HtmlTagDesc(name, true, true, inlineContent, biggerAttrs); + stSupportedElements.put(name, td); + } + + /** Defines the STYLE element. */ + protected static void addStyleElement(Map stSupportedElements) { + final String name = "style"; + List content = new ArrayList(1); + content.add(HtmlSpecialToken.PCDATA); + List atts = new ArrayList(6); + atts.addAll(i18nAttrs); + addSimpleAttribute(atts, "type"); + addSimpleAttribute(atts, "media"); + addSimpleAttribute(atts, "title"); + HtmlTagDesc td = new HtmlTagDesc(name, true, true, content, atts); + stSupportedElements.put(name, td); + } + + /** Defines the SUB (subscript) element. */ + protected static void addSubElement(Map stSupportedElements) { + final String name = "sub"; + HtmlTagDesc td = new HtmlTagDesc(name, true, true, inlineContent, bigAttrs); + stSupportedElements.put(name, td); + } + + /** Defines the SUP (superscript) element. */ + protected static void addSupElement(Map stSupportedElements) { + final String name = "sup"; + HtmlTagDesc td = new HtmlTagDesc(name, true, true, inlineContent, bigAttrs); + stSupportedElements.put(name, td); + } + + /** Defines the TEXTAREA element. */ + protected static void addTextareaElement(Map stSupportedElements) { + final String name = "textarea"; + List atts = new ArrayList(biggerAttrs.size() + 12); + addSimpleAttribute(atts, "name"); + addSimpleAttribute(atts, "rows"); + addSimpleAttribute(atts, "cols"); + addSelfAttribute(atts, "disabled"); + addSelfAttribute(atts, "readonly"); + addSimpleAttribute(atts, "tabindex"); + addSimpleAttribute(atts, "accesskey"); + addSimpleAttribute(atts, "onfocus"); + addSimpleAttribute(atts, "onblur"); + addSimpleAttribute(atts, "onselect"); + addSimpleAttribute(atts, "onchange"); + + List content = new ArrayList(1); + content.add(HtmlSpecialToken.PCDATA); + HtmlTagDesc td = new HtmlTagDesc(name, true, true, content, atts); + stSupportedElements.put(name, td); + } + + /** Defines the TBODY element. */ + protected static void addTbodyElement(Map stSupportedElements, List cellalignAttrs) { + final String name = "tbody"; + List content = new ArrayList(1); + content.add("tr"); + List atts = new ArrayList(bigAttrs.size() + 8); + atts.addAll(bigAttrs); + atts.addAll(cellalignAttrs); + HtmlTagDesc td = new HtmlTagDesc(name, false, false, content, atts); + stSupportedElements.put(name, td); + } + + /** + * Defines the TD element. The strict and transitional versions have different attribute sets, but + * this is taken care of by the initialization of thtdAtts. + */ + protected static void addTdElement(Map stSupportedElements) { + final String name = "td"; + HtmlTagDesc td = new HtmlTagDesc(name, true, false, flowContent, thtdAtts); + stSupportedElements.put(name, td); + } + + /** Defines the TFOOT element. */ + protected static void addTfootElement(Map stSupportedElements, List cellalignAttrs) { + final String name = "tfoot"; + List content = new ArrayList(1); + content.add("tr"); + List atts = new ArrayList(bigAttrs.size() + 8); + atts.addAll(bigAttrs); + atts.addAll(cellalignAttrs); + HtmlTagDesc td = new HtmlTagDesc(name, true, false, content, atts); + stSupportedElements.put(name, td); + } + + /** + * Defines the TH element. The strict and transitional versions have different attribute sets, but + * this is taken care of by the initialization of thtdAtts. + */ + protected static void addThElement(Map stSupportedElements) { + final String name = "th"; + HtmlTagDesc td = new HtmlTagDesc(name, true, false, flowContent, thtdAtts); + stSupportedElements.put(name, td); + } + + /** Defines the THEAD element. */ + protected static void addTheadElement(Map stSupportedElements, List cellalignAttrs) { + final String name = "thead"; + List content = new ArrayList(1); + content.add("tr"); + List atts = new ArrayList(bigAttrs.size() + 8); + atts.addAll(bigAttrs); + atts.addAll(cellalignAttrs); + HtmlTagDesc td = new HtmlTagDesc(name, true, false, content, atts); + stSupportedElements.put(name, td); + } + + /** Defines the TITLE element. */ + protected static void addTitleElement(Map stSupportedElements) { + /* I'm confused by the DTD for this one. + * Content consists only of PCDATA, but certain elements are + * specifically excluded from its content. This seems + * redundant. */ + String name = "title"; + List pcdataContent = new ArrayList(1); + pcdataContent.add(HtmlSpecialToken.PCDATA); + HtmlTagDesc td = new HtmlTagDesc(name, true, true, pcdataContent, i18nAttrs); + stSupportedElements.put(name, td); + } } diff --git a/jhove-modules/html-hul/src/main/java/edu/harvard/hul/ois/jhove/module/html/Html4StrictDocDesc.java b/jhove-modules/html-hul/src/main/java/edu/harvard/hul/ois/jhove/module/html/Html4StrictDocDesc.java index af29e714d..b553250ba 100644 --- a/jhove-modules/html-hul/src/main/java/edu/harvard/hul/ois/jhove/module/html/Html4StrictDocDesc.java +++ b/jhove-modules/html-hul/src/main/java/edu/harvard/hul/ois/jhove/module/html/Html4StrictDocDesc.java @@ -1,8 +1,8 @@ -/********************************************************************** - * Jhove - JSTOR/Harvard Object Validation Environment - * Copyright 2004 by JSTOR and the President and Fellows of Harvard College - **********************************************************************/ - +/** + * ******************************************************************** Jhove - JSTOR/Harvard Object + * Validation Environment Copyright 2004 by JSTOR and the President and Fellows of Harvard College + * ******************************************************************** + */ package edu.harvard.hul.ois.jhove.module.html; import java.util.ArrayList; @@ -10,446 +10,410 @@ import java.util.Map; /** - * Abstract class for common features of HTML 4.0 and 4.01 strict - * documents. The differences between 4.0 and 4.01 are minor, so - * most of the code is found here. + * Abstract class for common features of HTML 4.0 and 4.01 strict documents. The differences between + * 4.0 and 4.01 are minor, so most of the code is found here. * * @author Gary McGath - * */ public abstract class Html4StrictDocDesc extends Html4DocDesc { - protected Html4StrictDocDesc() { - super(); + protected Html4StrictDocDesc() { + super(); + } + /** + * Code which is called from the static initializer of the subclasses. Note that this is called + * before the class is instantiated, and may reference only static fields and subroutines. + */ + protected static void classInit4(Map stSupportedElements) { + Html4DocDesc.classInit4(stSupportedElements); + + String name; + HtmlTagDesc td; + + fontMarkup = new String[] {"tt", "i", "b", "big", "small"}; + listMarkup = new String[] {"ul", "ol"}; + specialMarkup = + new String[] { + "a", "img", "object", "br", "script", "map", "q", "sub", "sup", "span", "bdo" + }; + /* inlineContent lists all the content types which are permitted in + * the markup elements. For a first cut, strings signify the + * name of the element which is permitted. */ + inlineContent = new ArrayList(35); + addStringsToList(fontMarkup, inlineContent); + addStringsToList(phraseMarkup, inlineContent); + addStringsToList(specialMarkup, inlineContent); + addStringsToList(formMarkup, inlineContent); + inlineContent.add(HtmlSpecialToken.PCDATA); + + blockContent = new ArrayList(20); + addStringsToList(headings, blockContent); + addStringsToList(listMarkup, blockContent); + String[] blockMisc = + new String[] { + "p", + "pre", + "dl", + "div", + "noscript", + "blockquote", + "form", + "hr", + "table", + "fieldset", + "address" + }; + addStringsToList(blockMisc, blockContent); + + flowContent = new ArrayList(30); + flowContent.addAll(blockContent); + flowContent.addAll(inlineContent); + + /* Content for the BODY element, also used for other elements */ + bodyContent = new ArrayList(blockContent.size() + 3); + bodyContent.addAll(blockContent); + bodyContent.add("script"); + bodyContent.add("ins"); + bodyContent.add("del"); + + listContent = new ArrayList(1); + listContent.add("li"); + + /* Text elements */ + int i; + for (i = 0; i < fontMarkup.length; i++) { + name = fontMarkup[i]; + td = new HtmlTagDesc(name, true, true, inlineContent, bigAttrs); + stSupportedElements.put(name, td); } - /** Code which is called from the static initializer of the - * subclasses. Note that this is called before the class - * is instantiated, and may reference only static fields - * and subroutines. */ - protected static void classInit4 (Map stSupportedElements) - { - Html4DocDesc.classInit4(stSupportedElements); - - String name; - HtmlTagDesc td; - - fontMarkup = new String[] - { "tt", "i", "b", "big", "small" }; - listMarkup = new String [] - { "ul", "ol" }; - specialMarkup = new String[] - { "a", "img", "object", "br", "script", "map", - "q", "sub", "sup", "span", "bdo" }; - /* inlineContent lists all the content types which are permitted in - * the markup elements. For a first cut, strings signify the - * name of the element which is permitted. */ - inlineContent = new ArrayList(35); - addStringsToList (fontMarkup, inlineContent); - addStringsToList (phraseMarkup, inlineContent); - addStringsToList (specialMarkup, inlineContent); - addStringsToList (formMarkup, inlineContent); - inlineContent.add (HtmlSpecialToken.PCDATA); - - blockContent = new ArrayList (20); - addStringsToList (headings, blockContent); - addStringsToList (listMarkup, blockContent); - String[] blockMisc = new String[] - {"p", "pre", "dl", "div", "noscript", - "blockquote", "form", "hr", "table", "fieldset", "address" }; - addStringsToList (blockMisc, blockContent); - - flowContent = new ArrayList (30); - flowContent.addAll (blockContent); - flowContent.addAll (inlineContent); - - /* Content for the BODY element, also used for other elements */ - bodyContent = new ArrayList (blockContent.size () + 3); - bodyContent.addAll (blockContent); - bodyContent.add ("script"); - bodyContent.add ("ins"); - bodyContent.add ("del"); - - listContent = new ArrayList (1); - listContent.add ("li"); - - /* Text elements */ - int i; - for (i = 0; i < fontMarkup.length; i++) { - name = fontMarkup[i]; - td = new HtmlTagDesc (name, true, true, inlineContent, bigAttrs); - stSupportedElements.put (name, td); - } - - /* Phrase elements. */ - for (i = 0; i < phraseMarkup.length; i++) { - name = phraseMarkup[i]; - td = new HtmlTagDesc (name, true, true, inlineContent, bigAttrs); - stSupportedElements.put (name, td); - } - - thtdAtts = new ArrayList (bigAttrs.size() + 7); // common attribute list for TH and TD - thtdAtts.addAll (bigAttrs); - addSimpleAttribute (thtdAtts, "abbr"); - addSimpleAttribute (thtdAtts, "axis"); - addSimpleAttribute (thtdAtts, "headers"); - addSimpleAttribute (thtdAtts, "scope"); - thtdAtts.add (halignAtt); - thtdAtts.add (valignAtt); - addSimpleAttribute (thtdAtts, "rowspan"); - addSimpleAttribute (thtdAtts, "colspan"); - - } - - /** Static initializers for each element. If elements are common to more - * than one HTML version, they should be moved into the superclass. - * Different initializers may have different argument lists. */ - - /** Defines the A element. */ - protected static void addAElement (Map stSupportedElements) - { - /* The Anchor (A) element */ - String name = "a"; - List atts = new ArrayList (bigAttrs.size () + 14); - atts.addAll (bigAttrs); - addSimpleAttribute (atts, "type"); - addSimpleAttribute (atts, "name"); - addSimpleAttribute (atts, "href"); - addSimpleAttribute (atts, "hreflang"); - addSimpleAttribute (atts, "rel"); - addSimpleAttribute (atts, "rev"); - addSimpleAttribute (atts, "accesskey"); - addSimpleAttribute (atts, "shape"); - addSimpleAttribute (atts, "rect"); - addSimpleAttribute (atts, "coords"); - addSimpleAttribute (atts, "tabindex"); - addSimpleAttribute (atts, "onfocus"); - addSimpleAttribute (atts, "onblur"); - List content = new ArrayList (inlineContent.size ()); - content.addAll (inlineContent); - content.remove ("a"); - - HtmlTagDesc td = new HtmlTagDesc (name, true, true, content, atts); - stSupportedElements.put (name, td); + /* Phrase elements. */ + for (i = 0; i < phraseMarkup.length; i++) { + name = phraseMarkup[i]; + td = new HtmlTagDesc(name, true, true, inlineContent, bigAttrs); + stSupportedElements.put(name, td); } - - /** Defines the ADDRESS element. */ - protected static void addAddressElement (Map stSupportedElements) - { - String name = "address"; - HtmlTagDesc td = - new HtmlTagDesc (name, true, true, inlineContent, bigAttrs); - stSupportedElements.put (name, td); - } - - protected static void addAreaElement - (Map stSupportedElements, HtmlAttributeDesc shapeAtt) - { - String name = "area"; - List atts = new ArrayList (5); - atts.add (shapeAtt); - addSimpleAttribute (atts, "coords"); - addSimpleAttribute (atts, "href"); - atts.add (new HtmlAttributeDesc ("nohref", - new String[] {"nohref"}, + thtdAtts = new ArrayList(bigAttrs.size() + 7); // common attribute list for TH and TD + thtdAtts.addAll(bigAttrs); + addSimpleAttribute(thtdAtts, "abbr"); + addSimpleAttribute(thtdAtts, "axis"); + addSimpleAttribute(thtdAtts, "headers"); + addSimpleAttribute(thtdAtts, "scope"); + thtdAtts.add(halignAtt); + thtdAtts.add(valignAtt); + addSimpleAttribute(thtdAtts, "rowspan"); + addSimpleAttribute(thtdAtts, "colspan"); + } + + /** + * Static initializers for each element. If elements are common to more than one HTML version, + * they should be moved into the superclass. Different initializers may have different argument + * lists. + */ + + /** Defines the A element. */ + protected static void addAElement(Map stSupportedElements) { + /* The Anchor (A) element */ + String name = "a"; + List atts = new ArrayList(bigAttrs.size() + 14); + atts.addAll(bigAttrs); + addSimpleAttribute(atts, "type"); + addSimpleAttribute(atts, "name"); + addSimpleAttribute(atts, "href"); + addSimpleAttribute(atts, "hreflang"); + addSimpleAttribute(atts, "rel"); + addSimpleAttribute(atts, "rev"); + addSimpleAttribute(atts, "accesskey"); + addSimpleAttribute(atts, "shape"); + addSimpleAttribute(atts, "rect"); + addSimpleAttribute(atts, "coords"); + addSimpleAttribute(atts, "tabindex"); + addSimpleAttribute(atts, "onfocus"); + addSimpleAttribute(atts, "onblur"); + List content = new ArrayList(inlineContent.size()); + content.addAll(inlineContent); + content.remove("a"); + + HtmlTagDesc td = new HtmlTagDesc(name, true, true, content, atts); + stSupportedElements.put(name, td); + } + + /** Defines the ADDRESS element. */ + protected static void addAddressElement(Map stSupportedElements) { + String name = "address"; + HtmlTagDesc td = new HtmlTagDesc(name, true, true, inlineContent, bigAttrs); + stSupportedElements.put(name, td); + } + + protected static void addAreaElement(Map stSupportedElements, HtmlAttributeDesc shapeAtt) { + String name = "area"; + List atts = new ArrayList(5); + atts.add(shapeAtt); + addSimpleAttribute(atts, "coords"); + addSimpleAttribute(atts, "href"); + atts.add(new HtmlAttributeDesc("nohref", new String[] {"nohref"}, HtmlAttributeDesc.IMPLIED)); + atts.add(new HtmlAttributeDesc("alt", null, HtmlAttributeDesc.REQUIRED)); + addSimpleAttribute(atts, "tabindex"); + addSimpleAttribute(atts, "accesskey"); + addSimpleAttribute(atts, "onfocus"); + addSimpleAttribute(atts, "onblur"); + HtmlTagDesc td = new HtmlTagDesc(name, true, false, null, atts); + stSupportedElements.put(name, td); + } + + protected static void addBaseElement(Map stSupportedElements) { + String name = "base"; + List atts = new ArrayList(1); + addRequiredAttribute(atts, "href"); + HtmlTagDesc td = new HtmlTagDesc(name, true, false, null, atts); + stSupportedElements.put(name, td); + } + + protected static void addBlockquoteElement(Map stSupportedElements) { + String name = "blockquote"; + List content = new ArrayList(blockContent.size() + 1); + content.addAll(blockContent); + content.add("script"); + List atts = new ArrayList(bigAttrs.size() + 1); + atts.addAll(bigAttrs); + addSimpleAttribute(atts, "cite"); + HtmlTagDesc td = new HtmlTagDesc(name, true, true, content, atts); + stSupportedElements.put(name, td); + } + + protected static void addBrElement(Map stSupportedElements, List coreAttrs) { + String name = "br"; + HtmlTagDesc td = new HtmlTagDesc(name, true, false, null, coreAttrs); + stSupportedElements.put(name, td); + } + + protected static void addButtonElement(Map stSupportedElements) { + List content = new ArrayList(formMarkup.length + 3); + addStringsToList(formMarkup, content); + content.add("form"); + content.add("fieldset"); + List atts = new ArrayList(biggerAttrs.size() + 8); + atts.addAll(biggerAttrs); + addSimpleAttribute(atts, "name"); + addSimpleAttribute(atts, "value"); + atts.add( + new HtmlAttributeDesc( + "type", new String[] {"button", "submit", "reset"}, HtmlAttributeDesc.OTHER)); + addSelfAttribute(atts, "disabled"); + addSimpleAttribute(atts, "tabindex"); + addSimpleAttribute(atts, "accesskey"); + addSimpleAttribute(atts, "onfocus"); + addSimpleAttribute(atts, "onblur"); + } + + protected static void addCaptionElement( + Map stSupportedElements, List inlineContent, HtmlAttributeDesc valignAtt) { + String name = "caption"; + HtmlTagDesc td = new HtmlTagDesc(name, true, true, inlineContent, bigAttrs); + stSupportedElements.put(name, td); + } + + protected static void addDivElement(Map stSupportedElements) { + String name = "div"; + HtmlTagDesc td = new HtmlTagDesc(name, true, true, flowContent, bigAttrs); + stSupportedElements.put(name, td); + } + + protected static void addDlElement(Map stSupportedElements) { + String name = "dl"; + List dlContent = new ArrayList(2); + addStringsToList(new String[] {"dt", "dd"}, dlContent); + HtmlTagDesc td = new HtmlTagDesc(name, true, true, dlContent, bigAttrs); + stSupportedElements.put(name, td); + } + + protected static void addHeadElement(Map stSupportedElements) { + String name = "head"; + String[] headMisc = new String[] {"script", "style", "meta", "link"}; + List headContent = new ArrayList(7); + headContent.add("title"); + headContent.add("base"); + headContent.add("script"); + headContent.add("style"); + headContent.add("meta"); + headContent.add("link"); + headContent.add("object"); + HtmlTagDesc td = new HtmlTagDesc(name, false, false, headContent, null); + stSupportedElements.put(name, td); + /* Attributes TITLE (required), ISINDEX (optional), and BASE (optional) + * are supposed to come in that order, ahead of anything else. + * For the moment, just toss them in with the rest. */ + addStringsToList(headMisc, headContent); + } + + protected static void addLegendElement(Map stSupportedElements) { + final String name = "label"; + List atts = new ArrayList(bigAttrs.size() + 1); + atts.addAll(bigAttrs); + addSimpleAttribute(atts, "accesskey"); + HtmlTagDesc td = new HtmlTagDesc(name, true, true, inlineContent, atts); + stSupportedElements.put(name, td); + } + + protected static void addLiElement(Map stSupportedElements) { + final String name = "li"; + HtmlTagDesc td = new HtmlTagDesc(name, true, false, flowContent, bigAttrs); + stSupportedElements.put(name, td); + } + + protected static void addLinkElement(Map stSupportedElements) { + final String name = "link"; + List atts = new ArrayList(bigAttrs.size() + 8); + atts.addAll(bigAttrs); + addSimpleAttribute(atts, "charset"); + addSimpleAttribute(atts, "href"); + addSimpleAttribute(atts, "hreflang"); + addSimpleAttribute(atts, "type"); + addSimpleAttribute(atts, "rel"); + addSimpleAttribute(atts, "rev"); + addSimpleAttribute(atts, "media"); + HtmlTagDesc td = new HtmlTagDesc(name, true, false, null, atts); + stSupportedElements.put(name, td); + } + + protected static void addNoscriptElement(Map stSupportedElements) { + final String name = "noscript"; + HtmlTagDesc td = new HtmlTagDesc(name, true, false, blockContent, bigAttrs); + stSupportedElements.put(name, td); + } + + protected static void addObjectElement(Map stSupportedElements) { + final String name = "object"; + List atts = new ArrayList(biggerAttrs.size() + 13); + atts.addAll(biggerAttrs); + addSelfAttribute(atts, "declare"); + addSimpleAttribute(atts, "classid"); + addSimpleAttribute(atts, "codebase"); + addSimpleAttribute(atts, "data"); + addSimpleAttribute(atts, "type"); + addSimpleAttribute(atts, "codetype"); + addSimpleAttribute(atts, "archive"); + addSimpleAttribute(atts, "standby"); + addSimpleAttribute(atts, "height"); + addSimpleAttribute(atts, "width"); + addSimpleAttribute(atts, "usemap"); + addSimpleAttribute(atts, "name"); + addSimpleAttribute(atts, "tabindex"); + } + + protected static void addOlElement(Map stSupportedElements) { + final String name = "ol"; + HtmlTagDesc td = new HtmlTagDesc(name, true, true, listContent, bigAttrs); + stSupportedElements.put(name, td); + } + + protected static void addPElement(Map stSupportedElements) { + final String name = "p"; + HtmlTagDesc td = new HtmlTagDesc(name, true, false, inlineContent, bigAttrs); + stSupportedElements.put(name, td); + } + + protected static void addPreElement(Map stSupportedElements) { + final String name = "pre"; + List preContent = new ArrayList(inlineContent.size()); + preContent.addAll(inlineContent); + /* Take out excluded elements */ + removeStringsFromList(preContent, new String[] {"img", "object", "big", "small", "sub", "sup"}); + HtmlTagDesc td = new HtmlTagDesc(name, true, true, preContent, bigAttrs); + stSupportedElements.put(name, td); + } + + protected static void addScriptElement(Map stSupportedElements) { + final String name = "script"; + List content = new ArrayList(1); + content.add(HtmlSpecialToken.PCDATA); + List atts = new ArrayList(6); + addSimpleAttribute(atts, "charset"); + addSimpleAttribute(atts, "type"); + addSimpleAttribute(atts, "src"); + addSelfAttribute(atts, "defer"); + addSimpleAttribute(atts, "event"); + addSimpleAttribute(atts, "for"); + HtmlTagDesc td = new HtmlTagDesc(name, true, true, content, atts); + stSupportedElements.put(name, td); + } + + protected static void addTableElement(Map stSupportedElements) { + final String name = "table"; + List atts = new ArrayList(biggerAttrs.size() + 8); + atts.addAll(biggerAttrs); + addSimpleAttribute(atts, "summary"); + addSimpleAttribute(atts, "width"); + addSimpleAttribute(atts, "border"); + atts.add( + new HtmlAttributeDesc( + "frame", + new String[] { + "void", "above", "below", "hsides", "lhs", "rhs", "vsides", "box", "border" + }, HtmlAttributeDesc.IMPLIED)); - atts.add (new HtmlAttributeDesc ("alt", - null, - HtmlAttributeDesc.REQUIRED)); - addSimpleAttribute (atts, "tabindex"); - addSimpleAttribute (atts, "accesskey"); - addSimpleAttribute (atts, "onfocus"); - addSimpleAttribute (atts, "onblur"); - HtmlTagDesc td = new HtmlTagDesc (name, true, false, null, atts); - stSupportedElements.put (name, td); - } - - protected static void addBaseElement (Map stSupportedElements) - { - String name = "base"; - List atts = new ArrayList (1); - addRequiredAttribute (atts, "href"); - HtmlTagDesc td = new HtmlTagDesc (name, true, false, null, atts); - stSupportedElements.put (name, td); - } - - protected static void addBlockquoteElement (Map stSupportedElements) - { - String name = "blockquote"; - List content = new ArrayList (blockContent.size () + 1); - content.addAll (blockContent); - content.add ("script"); - List atts = new ArrayList (bigAttrs.size () + 1); - atts.addAll (bigAttrs); - addSimpleAttribute (atts, "cite"); - HtmlTagDesc td = new HtmlTagDesc (name, true, true, content, atts); - stSupportedElements.put (name, td); - } - - protected static void addBrElement (Map stSupportedElements, List coreAttrs) - { - String name = "br"; - HtmlTagDesc td = new HtmlTagDesc (name, true, false, null, coreAttrs); - stSupportedElements.put (name, td); - } - - protected static void addButtonElement - (Map stSupportedElements) - { - List content = new ArrayList (formMarkup.length + 3); - addStringsToList(formMarkup, content); - content.add ("form"); - content.add ("fieldset"); - List atts = new ArrayList (biggerAttrs.size () + 8); - atts.addAll (biggerAttrs); - addSimpleAttribute (atts, "name"); - addSimpleAttribute (atts, "value"); - atts.add (new HtmlAttributeDesc ("type", - new String[] {"button", "submit" , "reset"}, - HtmlAttributeDesc.OTHER)); - addSelfAttribute (atts, "disabled"); - addSimpleAttribute (atts, "tabindex"); - addSimpleAttribute (atts, "accesskey"); - addSimpleAttribute (atts, "onfocus"); - addSimpleAttribute (atts, "onblur"); - } - - - protected static void addCaptionElement - (Map stSupportedElements, List inlineContent, - HtmlAttributeDesc valignAtt) - { - String name = "caption"; - HtmlTagDesc td = new HtmlTagDesc (name, true, true, inlineContent, bigAttrs); - stSupportedElements.put (name, td); - } - - protected static void addDivElement (Map stSupportedElements) - { - String name = "div"; - HtmlTagDesc td = - new HtmlTagDesc (name, true, true, flowContent, bigAttrs); - stSupportedElements.put (name, td); - } - - protected static void addDlElement - (Map stSupportedElements) - { - String name = "dl"; - List dlContent = new ArrayList (2); - addStringsToList(new String[] { "dt", "dd" }, dlContent); - HtmlTagDesc td = new HtmlTagDesc (name, true, true, dlContent, bigAttrs); - stSupportedElements.put (name, td); - } - - protected static void addHeadElement (Map stSupportedElements) - { - String name = "head"; - String[] headMisc = new String[] - {"script", "style", "meta", "link" }; - List headContent = new ArrayList (7); - headContent.add ("title"); - headContent.add ("base"); - headContent.add ("script"); - headContent.add ("style"); - headContent.add ("meta"); - headContent.add ("link"); - headContent.add ("object"); - HtmlTagDesc td = new HtmlTagDesc (name, false, false, headContent, null); - stSupportedElements.put (name, td); - /* Attributes TITLE (required), ISINDEX (optional), and BASE (optional) - * are supposed to come in that order, ahead of anything else. - * For the moment, just toss them in with the rest. */ - addStringsToList (headMisc, headContent); - } - - protected static void addLegendElement (Map stSupportedElements) - { - final String name = "label"; - List atts = new ArrayList (bigAttrs.size () + 1); - atts.addAll (bigAttrs); - addSimpleAttribute (atts, "accesskey"); - HtmlTagDesc td = new HtmlTagDesc (name, true, true, inlineContent, atts); - stSupportedElements.put (name, td); - } - - protected static void addLiElement - (Map stSupportedElements) - { - final String name = "li"; - HtmlTagDesc td = - new HtmlTagDesc (name, true, false, flowContent, bigAttrs); - stSupportedElements.put (name, td); - } - - - protected static void addLinkElement - (Map stSupportedElements) - { - final String name = "link"; - List atts = new ArrayList (bigAttrs.size () + 8); - atts.addAll (bigAttrs); - addSimpleAttribute (atts, "charset"); - addSimpleAttribute (atts, "href"); - addSimpleAttribute (atts, "hreflang"); - addSimpleAttribute (atts, "type"); - addSimpleAttribute (atts, "rel"); - addSimpleAttribute (atts, "rev"); - addSimpleAttribute (atts, "media"); - HtmlTagDesc td = new HtmlTagDesc (name, true, false, null, atts); - stSupportedElements.put (name, td); - } - - protected static void addNoscriptElement (Map stSupportedElements) - { - final String name = "noscript"; - HtmlTagDesc td = - new HtmlTagDesc (name, true, false, blockContent, bigAttrs); - stSupportedElements.put (name, td); - } - - protected static void addObjectElement (Map stSupportedElements) - { - final String name = "object"; - List atts = new ArrayList (biggerAttrs.size () + 13); - atts.addAll (biggerAttrs); - addSelfAttribute (atts, "declare"); - addSimpleAttribute (atts, "classid"); - addSimpleAttribute (atts, "codebase"); - addSimpleAttribute (atts, "data"); - addSimpleAttribute (atts, "type"); - addSimpleAttribute (atts, "codetype"); - addSimpleAttribute (atts, "archive"); - addSimpleAttribute (atts, "standby"); - addSimpleAttribute (atts, "height"); - addSimpleAttribute (atts, "width"); - addSimpleAttribute (atts, "usemap"); - addSimpleAttribute (atts, "name"); - addSimpleAttribute (atts, "tabindex"); - } - - - protected static void addOlElement - (Map stSupportedElements) - { - final String name = "ol"; - HtmlTagDesc td = - new HtmlTagDesc (name, true, true, listContent, bigAttrs); - stSupportedElements.put (name, td); - } - - - protected static void addPElement (Map stSupportedElements) - { - final String name = "p"; - HtmlTagDesc td = - new HtmlTagDesc (name, true, false, inlineContent, bigAttrs); - stSupportedElements.put (name, td); - } - - protected static void addPreElement (Map stSupportedElements) - { - final String name = "pre"; - List preContent = new ArrayList (inlineContent.size ()); - preContent.addAll(inlineContent); - /* Take out excluded elements */ - removeStringsFromList (preContent, - new String [] - {"img", "object", "big", "small", "sub", "sup"}); - HtmlTagDesc td = new HtmlTagDesc (name, true, true, preContent, bigAttrs); - stSupportedElements.put (name, td); - } - - protected static void addScriptElement (Map stSupportedElements) - { - final String name = "script"; - List content = new ArrayList (1); - content.add (HtmlSpecialToken.PCDATA); - List atts = new ArrayList (6); - addSimpleAttribute (atts, "charset"); - addSimpleAttribute (atts, "type"); - addSimpleAttribute (atts, "src"); - addSelfAttribute (atts, "defer"); - addSimpleAttribute (atts, "event"); - addSimpleAttribute (atts, "for"); - HtmlTagDesc td = new HtmlTagDesc (name, true, true, content, atts); - stSupportedElements.put (name, td); - } - - protected static void addTableElement (Map stSupportedElements) - { - final String name = "table"; - List atts = new ArrayList (biggerAttrs.size () + 8); - atts.addAll (biggerAttrs); - addSimpleAttribute (atts, "summary"); - addSimpleAttribute (atts, "width"); - addSimpleAttribute (atts, "border"); - atts.add (new HtmlAttributeDesc ("frame", - new String[] { "void", "above", "below", "hsides", - "lhs", "rhs", "vsides", "box", "border" }, - HtmlAttributeDesc.IMPLIED)); - atts.add (new HtmlAttributeDesc ("rules", - new String[] { "none", "groups", "rows", "cols", "all" }, + atts.add( + new HtmlAttributeDesc( + "rules", + new String[] {"none", "groups", "rows", "cols", "all"}, HtmlAttributeDesc.IMPLIED)); - - addSimpleAttribute (atts, "rules"); - addSimpleAttribute (atts, "cellspacing"); - addSimpleAttribute (atts, "cellpadding"); - addSimpleAttribute (atts, "datapagesize"); - List[] contentArray = new List[5]; - int[] contentSequence = new int[] - { HtmlTagDesc.SEQ0_1, - HtmlTagDesc.SEQ0_MANY, - HtmlTagDesc.SEQ0_1, - HtmlTagDesc.SEQ0_1, - HtmlTagDesc.SEQ1_MANY }; - List content = new ArrayList (1); - content.add ("caption"); - contentArray[0] = content; - - content = new ArrayList(2); - content.add ("col"); - content.add ("colgroup"); - contentArray[1] = content; - - content = new ArrayList (1); - content.add ("thead"); - contentArray[2] = content; - - content = new ArrayList (1); - content.add ("tfoot"); - contentArray[3] = content; - - content = new ArrayList (1); - content.add ("tbody"); - contentArray[4] = content; - - HtmlTagDesc td = new HtmlTagDesc - (name, true, true, contentSequence, contentArray, atts); - stSupportedElements.put (name, td); - } - protected static void addTrElement (Map stSupportedElements) - { - final String name = "tr"; - List atts = new ArrayList (bigAttrs.size() + 3); - atts.addAll (bigAttrs); - atts.add (halignAtt); - atts.add (valignAtt); - List content = new ArrayList (2); - content.add ("th"); - content.add ("td"); - HtmlTagDesc td = new HtmlTagDesc (name, true, false, content, atts); - td.addImplicitContainer((HtmlTagDesc) stSupportedElements.get ("tbody")); - stSupportedElements.put (name, td); - } - - protected static void addUlElement (Map stSupportedElements) - { - final String name = "ul"; - HtmlTagDesc td = - new HtmlTagDesc (name, true, true, listContent, bigAttrs); - stSupportedElements.put (name, td); - } + addSimpleAttribute(atts, "rules"); + addSimpleAttribute(atts, "cellspacing"); + addSimpleAttribute(atts, "cellpadding"); + addSimpleAttribute(atts, "datapagesize"); + List[] contentArray = new List[5]; + int[] contentSequence = + new int[] { + HtmlTagDesc.SEQ0_1, + HtmlTagDesc.SEQ0_MANY, + HtmlTagDesc.SEQ0_1, + HtmlTagDesc.SEQ0_1, + HtmlTagDesc.SEQ1_MANY + }; + List content = new ArrayList(1); + content.add("caption"); + contentArray[0] = content; + + content = new ArrayList(2); + content.add("col"); + content.add("colgroup"); + contentArray[1] = content; + + content = new ArrayList(1); + content.add("thead"); + contentArray[2] = content; + + content = new ArrayList(1); + content.add("tfoot"); + contentArray[3] = content; + + content = new ArrayList(1); + content.add("tbody"); + contentArray[4] = content; + + HtmlTagDesc td = new HtmlTagDesc(name, true, true, contentSequence, contentArray, atts); + stSupportedElements.put(name, td); + } + + protected static void addTrElement(Map stSupportedElements) { + final String name = "tr"; + List atts = new ArrayList(bigAttrs.size() + 3); + atts.addAll(bigAttrs); + atts.add(halignAtt); + atts.add(valignAtt); + List content = new ArrayList(2); + content.add("th"); + content.add("td"); + HtmlTagDesc td = new HtmlTagDesc(name, true, false, content, atts); + td.addImplicitContainer((HtmlTagDesc) stSupportedElements.get("tbody")); + stSupportedElements.put(name, td); + } + + protected static void addUlElement(Map stSupportedElements) { + final String name = "ul"; + HtmlTagDesc td = new HtmlTagDesc(name, true, true, listContent, bigAttrs); + stSupportedElements.put(name, td); + } } diff --git a/jhove-modules/html-hul/src/main/java/edu/harvard/hul/ois/jhove/module/html/Html4TFDocDesc.java b/jhove-modules/html-hul/src/main/java/edu/harvard/hul/ois/jhove/module/html/Html4TFDocDesc.java index 90cc5f593..bad439ed2 100644 --- a/jhove-modules/html-hul/src/main/java/edu/harvard/hul/ois/jhove/module/html/Html4TFDocDesc.java +++ b/jhove-modules/html-hul/src/main/java/edu/harvard/hul/ois/jhove/module/html/Html4TFDocDesc.java @@ -1,673 +1,648 @@ -/********************************************************************** - * Jhove - JSTOR/Harvard Object Validation Environment - * Copyright 2004 by JSTOR and the President and Fellows of Harvard College - **********************************************************************/ - +/** + * ******************************************************************** Jhove - JSTOR/Harvard Object + * Validation Environment Copyright 2004 by JSTOR and the President and Fellows of Harvard College + * ******************************************************************** + */ package edu.harvard.hul.ois.jhove.module.html; import edu.harvard.hul.ois.jhove.module.HtmlModule; import java.util.*; /** - * Abstract class for common features of HTML 4.0 and 4.01 transitional and frameset - * documents. The differences between 4.0 and 4.01 are minor, as - * are the differences between transitional and frameset so - * most of the code is found here. + * Abstract class for common features of HTML 4.0 and 4.01 transitional and frameset documents. The + * differences between 4.0 and 4.01 are minor, as are the differences between transitional and + * frameset so most of the code is found here. * * @author Gary McGath - * */ public abstract class Html4TFDocDesc extends Html4DocDesc { - protected Html4TFDocDesc() { - super(); - } - - /** Code which is called from the static initializer of the - * subclasses. Note that this is called before the class - * is instantiated, and may reference only static fields - * and subroutines. */ - protected static void classInit4 (Map stSupportedElements) - { - Html4DocDesc.classInit4(stSupportedElements); - - fontMarkup = new String[] - { "tt", "i", "b", "u", "s", "strike", "big", "small" }; - listMarkup = new String [] - { "ul", "ol", "dir", "menu" }; - specialMarkup = new String[] - { "a", "img", "applet", "object", "font", - "basefont", "br", "script", "map", - "q", "sub", "sup", "span", "bdo", "iframe" }; - /* inlineContent lists all the content types which are permitted in - * the markup elements. For a first cut, strings signify the - * name of the element which is permitted. */ - inlineContent = new ArrayList(35); - addStringsToList (fontMarkup, inlineContent); - addStringsToList (phraseMarkup, inlineContent); - addStringsToList (specialMarkup, inlineContent); - addStringsToList (formMarkup, inlineContent); - inlineContent.add (HtmlSpecialToken.PCDATA); - - blockContent = new ArrayList (20); - addStringsToList (headings, blockContent); - addStringsToList (listMarkup, blockContent); - String[] blockMisc = new String[] - {"p", "pre", "dl", "div", "center", "noscript", - "blockquote", "form", "hr", "table", "fieldset", "address" }; - addStringsToList (blockMisc, blockContent); - - flowContent = new ArrayList (30); - flowContent.addAll (blockContent); - flowContent.addAll (inlineContent); - - /* Content for the BODY element, also used for other elements */ - bodyContent = new ArrayList (flowContent.size () + 3); - bodyContent.addAll (flowContent); - bodyContent.add ("ins"); - bodyContent.add ("del"); - - listContent = new ArrayList (1); - listContent.add ("li"); - - thtdAtts = new ArrayList (bigAttrs.size() + 7); // common attribute list for TH and TD - thtdAtts.addAll (bigAttrs); - addSimpleAttribute (thtdAtts, "abbr"); - addSimpleAttribute (thtdAtts, "axis"); - addSimpleAttribute (thtdAtts, "headers"); - addSimpleAttribute (thtdAtts, "scope"); - thtdAtts.add (halignAtt); - thtdAtts.add (valignAtt); - addSelfAttribute (thtdAtts, "nowrap"); - addSimpleAttribute (thtdAtts, "rowspan"); - addSimpleAttribute (thtdAtts, "colspan"); - addSimpleAttribute (thtdAtts, "bgcolor"); - addSimpleAttribute (thtdAtts, "width"); - addSimpleAttribute (thtdAtts, "height"); - } - - /** Defines the A element. */ - protected static void addAElement (Map stSupportedElements) - { - /* The Anchor (A) element */ - String name = "a"; - List atts = new ArrayList (bigAttrs.size () + 14); - atts.addAll (bigAttrs); - addSimpleAttribute (atts, "type"); - addSimpleAttribute (atts, "name"); - addSimpleAttribute (atts, "href"); - addSimpleAttribute (atts, "hreflang"); - addSimpleAttribute (atts, "target"); // not in strict - addSimpleAttribute (atts, "rel"); - addSimpleAttribute (atts, "rev"); - addSimpleAttribute (atts, "accesskey"); - addSimpleAttribute (atts, "shape"); - addSimpleAttribute (atts, "rect"); - addSimpleAttribute (atts, "coords"); - addSimpleAttribute (atts, "tabindex"); - addSimpleAttribute (atts, "onfocus"); - addSimpleAttribute (atts, "onblur"); - List content = new ArrayList (inlineContent.size ()); - content.addAll (inlineContent); - content.remove ("a"); - - HtmlTagDesc td = new HtmlTagDesc (name, true, true, content, atts); - stSupportedElements.put (name, td); - } - - protected static void addAppletElement - (Map stSupportedElements, HtmlAttributeDesc ialignAtt) - { - String name = "applet"; - List content = new ArrayList (flowContent.size ()); - content.addAll (flowContent); - content.add ("param"); - List atts = new ArrayList (9); - addSimpleAttribute (atts, "codebase"); - addSimpleAttribute (atts, "archive"); - addSimpleAttribute (atts, "code"); - addSimpleAttribute (atts, "object"); - addSimpleAttribute (atts, "alt"); - addSimpleAttribute (atts, "alt"); - addSimpleAttribute (atts, "name"); - addRequiredAttribute (atts, "width"); - addRequiredAttribute (atts, "height"); - atts.add (ialignAtt); - addSimpleAttribute (atts, "hspace"); - addSimpleAttribute (atts, "vspace"); - HtmlTagDesc td = new HtmlTagDesc (name, true, false, content, atts); - stSupportedElements.put (name, td); - - } - - protected static void addAreaElement - (Map stSupportedElements, HtmlAttributeDesc shapeAtt) - { - String name = "area"; - List atts = new ArrayList (10); - atts.add (shapeAtt); - addSimpleAttribute (atts, "coords"); - addSimpleAttribute (atts, "href"); - addSimpleAttribute (atts, "target"); - atts.add (new HtmlAttributeDesc ("nohref", - new String[] {"nohref"}, - HtmlAttributeDesc.IMPLIED)); - atts.add (new HtmlAttributeDesc ("alt", - null, - HtmlAttributeDesc.REQUIRED)); - addSimpleAttribute (atts, "tabindex"); - addSimpleAttribute (atts, "accesskey"); - addSimpleAttribute (atts, "onfocus"); - addSimpleAttribute (atts, "onblur"); - HtmlTagDesc td = new HtmlTagDesc (name, true, false, null, atts); - stSupportedElements.put (name, td); - } - - protected static void addBaseElement (Map stSupportedElements) - { - String name = "base"; - List atts = new ArrayList (2); - addSimpleAttribute (atts, "href"); - addSimpleAttribute (atts, "target"); - HtmlTagDesc td = new HtmlTagDesc (name, true, false, null, atts); - stSupportedElements.put (name, td); - } - - protected static void addBasefontElement (Map stSupportedElements) - { - String name = "basefont"; - List atts = new ArrayList (4); - addSimpleAttribute (atts, "id"); - addSimpleAttribute (atts, "size"); - addSimpleAttribute (atts, "color"); - addSimpleAttribute (atts, "face"); - HtmlTagDesc td = new HtmlTagDesc (name, true, false, null, atts); - stSupportedElements.put (name, td); - } - - protected static void addBlockquoteElement (Map stSupportedElements) - { - String name = "blockquote"; - List content = new ArrayList (blockContent.size () + 1); - content.addAll (blockContent); - content.add ("script"); - List atts = new ArrayList (bigAttrs.size () + 1); - atts.addAll (bigAttrs); - addSimpleAttribute (atts, "cite"); - HtmlTagDesc td = new HtmlTagDesc (name, true, true, flowContent, atts); - stSupportedElements.put (name, td); - } - - protected static void addBrElement (Map stSupportedElements, List coreAttrs) - { - String name = "br"; - List atts = new ArrayList (coreAttrs.size () + 1); - atts.addAll (coreAttrs); - atts.add (new HtmlAttributeDesc ("clear", - new String[] {"left", "all", "right", "none" }, - HtmlAttributeDesc.OTHER)); - HtmlTagDesc td = new HtmlTagDesc (name, true, false, null, atts); - stSupportedElements.put (name, td); - } - - protected static void addButtonElement - (Map stSupportedElements) - { - String name = "button"; - List content = new ArrayList (formMarkup.length + 3); - addStringsToList(formMarkup, content); - content.add ("form"); - content.add ("isindex"); - content.add ("fieldset"); - content.add ("iframe"); - List atts = new ArrayList (biggerAttrs.size () + 8); - atts.addAll (biggerAttrs); - addSimpleAttribute (atts, "name"); - addSimpleAttribute (atts, "value"); - atts.add (new HtmlAttributeDesc ("type", - new String[] {"button", "submit" , "reset"}, - HtmlAttributeDesc.OTHER)); - addSelfAttribute (atts, "disabled"); - addSimpleAttribute (atts, "tabindex"); - addSimpleAttribute (atts, "accesskey"); - addSimpleAttribute (atts, "onfocus"); - addSimpleAttribute (atts, "onblur"); - } - - protected static void addCaptionElement - (Map stSupportedElements, List inlineContent, - HtmlAttributeDesc valignAtt) - { - String name = "caption"; - List atts = new ArrayList (bigAttrs.size () + 1); - atts.add (new HtmlAttributeDesc ("align", - new String[] {"top", "bottom", "left", "right" }, - HtmlAttributeDesc.IMPLIED)); - HtmlTagDesc td = new HtmlTagDesc (name, true, true, inlineContent, atts); - stSupportedElements.put (name, td); - } - - protected static void addCenterElement (Map stSupportedElements) - { - String name = "center"; - HtmlTagDesc td = new HtmlTagDesc (name, true, true, flowContent, bigAttrs); - stSupportedElements.put (name, td); - } - - protected static void addDirElement (Map stSupportedElements) - { - String name = "dir"; - List atts = new ArrayList (1); - addSelfAttribute (atts, "compact"); - HtmlTagDesc td = new HtmlTagDesc (name, true, true, listContent, atts); - stSupportedElements.put (name, td); - } - - protected static void addDivElement (Map stSupportedElements) - { - String name = "div"; - List atts = new ArrayList (bigAttrs.size () + 1); - atts.addAll (bigAttrs); - atts.add (new HtmlAttributeDesc - ("align", - new String [] {"left", "center", "right", "justify"}, - HtmlAttributeDesc.IMPLIED)); - HtmlTagDesc td = - new HtmlTagDesc (name, true, true, flowContent, atts); - stSupportedElements.put (name, td); - } - - protected static void addDlElement - (Map stSupportedElements) - { - String name = "dl"; - List dlContent = new ArrayList (2); - addStringsToList(new String[] { "dt", "dd" }, dlContent); - List atts = new ArrayList (bigAttrs.size () + 1); - addSelfAttribute(atts, "compact"); - HtmlTagDesc td = new HtmlTagDesc (name, true, true, dlContent, atts); - stSupportedElements.put (name, td); - } - - /** Adds the Font element. */ - protected static void addFontElement (Map stSupportedElements) - { - String name = "font"; - List atts = new ArrayList (bigAttrs.size () + 10); - atts.addAll (bigAttrs); - atts.addAll (i18nAttrs); - addSimpleAttribute (atts, "size"); - addSimpleAttribute (atts, "color"); - addSimpleAttribute (atts, "face"); - - HtmlTagDesc td = new HtmlTagDesc (name, true, true, inlineContent, atts); - stSupportedElements.put (name, td); - - } - - /** Adds to Frame element. */ - protected static void addFrameElement (Map stSupportedElements) - { - String name = "frame"; - List atts = new ArrayList (bigAttrs.size () + 8); - atts.addAll (bigAttrs); - addSimpleAttribute (atts, "longdesc"); - addSimpleAttribute (atts, "name"); - addSimpleAttribute (atts, "src"); - addSimpleAttribute (atts, "marginwidth"); - addSimpleAttribute (atts, "marginheight"); - addSelfAttribute (atts, "noresize"); - atts.add (new HtmlAttributeDesc ("scrolling", - new String[] { "yes", "no", "auto" }, - HtmlAttributeDesc.OTHER)); - HtmlTagDesc td = new HtmlTagDesc (name, true, false, null, atts); - stSupportedElements.put (name, td); - } - - /** Adds the Frameset element. This is called only for - * 4.0 and 4.01 Frameset docuemnts. */ - protected static void addFramesetElement - (Map stSupportedElements) - { - String name = "frameset"; - List content = new ArrayList (3); - addStringsToList (new String[] {"frameset", "frame", "noframes" }, - content); - List atts = new ArrayList (coreAttrs.size () + 4); - atts.addAll (coreAttrs); - addSimpleAttribute(atts, "rows"); - addSimpleAttribute(atts, "cols"); - addSimpleAttribute(atts, "onload"); - addSimpleAttribute(atts, "onunload"); - HtmlTagDesc td = new HtmlTagDesc (name, false, false, content, atts); - stSupportedElements.put (name, td); - } - - protected static void addHeadElement (Map stSupportedElements) - { - String name = "head"; - String[] headMisc = new String[] - {"script", "style", "meta", "link" }; - List headContent = new ArrayList (7); - headContent.add ("title"); - headContent.add ("isindex"); - headContent.add ("base"); - headContent.add ("script"); - headContent.add ("style"); - headContent.add ("meta"); - headContent.add ("link"); - headContent.add ("object"); - HtmlTagDesc td = new HtmlTagDesc (name, false, false, headContent, null); - stSupportedElements.put (name, td); - /* Attributes TITLE (required), ISINDEX (optional), and BASE (optional) - * are supposed to come in that order, ahead of anything else. - * For the moment, just toss them in with the rest. */ - addStringsToList (headMisc, headContent); - } - - protected static void addHrElement (Map stSupportedElements) - { - String name = "hr"; - List atts = new ArrayList (bigAttrs.size () + 4); - atts.add (new HtmlAttributeDesc ("align", - new String[] { "left", "center", "right" }, + protected Html4TFDocDesc() { + super(); + } + + /** + * Code which is called from the static initializer of the subclasses. Note that this is called + * before the class is instantiated, and may reference only static fields and subroutines. + */ + protected static void classInit4(Map stSupportedElements) { + Html4DocDesc.classInit4(stSupportedElements); + + fontMarkup = new String[] {"tt", "i", "b", "u", "s", "strike", "big", "small"}; + listMarkup = new String[] {"ul", "ol", "dir", "menu"}; + specialMarkup = + new String[] { + "a", + "img", + "applet", + "object", + "font", + "basefont", + "br", + "script", + "map", + "q", + "sub", + "sup", + "span", + "bdo", + "iframe" + }; + /* inlineContent lists all the content types which are permitted in + * the markup elements. For a first cut, strings signify the + * name of the element which is permitted. */ + inlineContent = new ArrayList(35); + addStringsToList(fontMarkup, inlineContent); + addStringsToList(phraseMarkup, inlineContent); + addStringsToList(specialMarkup, inlineContent); + addStringsToList(formMarkup, inlineContent); + inlineContent.add(HtmlSpecialToken.PCDATA); + + blockContent = new ArrayList(20); + addStringsToList(headings, blockContent); + addStringsToList(listMarkup, blockContent); + String[] blockMisc = + new String[] { + "p", + "pre", + "dl", + "div", + "center", + "noscript", + "blockquote", + "form", + "hr", + "table", + "fieldset", + "address" + }; + addStringsToList(blockMisc, blockContent); + + flowContent = new ArrayList(30); + flowContent.addAll(blockContent); + flowContent.addAll(inlineContent); + + /* Content for the BODY element, also used for other elements */ + bodyContent = new ArrayList(flowContent.size() + 3); + bodyContent.addAll(flowContent); + bodyContent.add("ins"); + bodyContent.add("del"); + + listContent = new ArrayList(1); + listContent.add("li"); + + thtdAtts = new ArrayList(bigAttrs.size() + 7); // common attribute list for TH and TD + thtdAtts.addAll(bigAttrs); + addSimpleAttribute(thtdAtts, "abbr"); + addSimpleAttribute(thtdAtts, "axis"); + addSimpleAttribute(thtdAtts, "headers"); + addSimpleAttribute(thtdAtts, "scope"); + thtdAtts.add(halignAtt); + thtdAtts.add(valignAtt); + addSelfAttribute(thtdAtts, "nowrap"); + addSimpleAttribute(thtdAtts, "rowspan"); + addSimpleAttribute(thtdAtts, "colspan"); + addSimpleAttribute(thtdAtts, "bgcolor"); + addSimpleAttribute(thtdAtts, "width"); + addSimpleAttribute(thtdAtts, "height"); + } + + /** Defines the A element. */ + protected static void addAElement(Map stSupportedElements) { + /* The Anchor (A) element */ + String name = "a"; + List atts = new ArrayList(bigAttrs.size() + 14); + atts.addAll(bigAttrs); + addSimpleAttribute(atts, "type"); + addSimpleAttribute(atts, "name"); + addSimpleAttribute(atts, "href"); + addSimpleAttribute(atts, "hreflang"); + addSimpleAttribute(atts, "target"); // not in strict + addSimpleAttribute(atts, "rel"); + addSimpleAttribute(atts, "rev"); + addSimpleAttribute(atts, "accesskey"); + addSimpleAttribute(atts, "shape"); + addSimpleAttribute(atts, "rect"); + addSimpleAttribute(atts, "coords"); + addSimpleAttribute(atts, "tabindex"); + addSimpleAttribute(atts, "onfocus"); + addSimpleAttribute(atts, "onblur"); + List content = new ArrayList(inlineContent.size()); + content.addAll(inlineContent); + content.remove("a"); + + HtmlTagDesc td = new HtmlTagDesc(name, true, true, content, atts); + stSupportedElements.put(name, td); + } + + protected static void addAppletElement(Map stSupportedElements, HtmlAttributeDesc ialignAtt) { + String name = "applet"; + List content = new ArrayList(flowContent.size()); + content.addAll(flowContent); + content.add("param"); + List atts = new ArrayList(9); + addSimpleAttribute(atts, "codebase"); + addSimpleAttribute(atts, "archive"); + addSimpleAttribute(atts, "code"); + addSimpleAttribute(atts, "object"); + addSimpleAttribute(atts, "alt"); + addSimpleAttribute(atts, "alt"); + addSimpleAttribute(atts, "name"); + addRequiredAttribute(atts, "width"); + addRequiredAttribute(atts, "height"); + atts.add(ialignAtt); + addSimpleAttribute(atts, "hspace"); + addSimpleAttribute(atts, "vspace"); + HtmlTagDesc td = new HtmlTagDesc(name, true, false, content, atts); + stSupportedElements.put(name, td); + } + + protected static void addAreaElement(Map stSupportedElements, HtmlAttributeDesc shapeAtt) { + String name = "area"; + List atts = new ArrayList(10); + atts.add(shapeAtt); + addSimpleAttribute(atts, "coords"); + addSimpleAttribute(atts, "href"); + addSimpleAttribute(atts, "target"); + atts.add(new HtmlAttributeDesc("nohref", new String[] {"nohref"}, HtmlAttributeDesc.IMPLIED)); + atts.add(new HtmlAttributeDesc("alt", null, HtmlAttributeDesc.REQUIRED)); + addSimpleAttribute(atts, "tabindex"); + addSimpleAttribute(atts, "accesskey"); + addSimpleAttribute(atts, "onfocus"); + addSimpleAttribute(atts, "onblur"); + HtmlTagDesc td = new HtmlTagDesc(name, true, false, null, atts); + stSupportedElements.put(name, td); + } + + protected static void addBaseElement(Map stSupportedElements) { + String name = "base"; + List atts = new ArrayList(2); + addSimpleAttribute(atts, "href"); + addSimpleAttribute(atts, "target"); + HtmlTagDesc td = new HtmlTagDesc(name, true, false, null, atts); + stSupportedElements.put(name, td); + } + + protected static void addBasefontElement(Map stSupportedElements) { + String name = "basefont"; + List atts = new ArrayList(4); + addSimpleAttribute(atts, "id"); + addSimpleAttribute(atts, "size"); + addSimpleAttribute(atts, "color"); + addSimpleAttribute(atts, "face"); + HtmlTagDesc td = new HtmlTagDesc(name, true, false, null, atts); + stSupportedElements.put(name, td); + } + + protected static void addBlockquoteElement(Map stSupportedElements) { + String name = "blockquote"; + List content = new ArrayList(blockContent.size() + 1); + content.addAll(blockContent); + content.add("script"); + List atts = new ArrayList(bigAttrs.size() + 1); + atts.addAll(bigAttrs); + addSimpleAttribute(atts, "cite"); + HtmlTagDesc td = new HtmlTagDesc(name, true, true, flowContent, atts); + stSupportedElements.put(name, td); + } + + protected static void addBrElement(Map stSupportedElements, List coreAttrs) { + String name = "br"; + List atts = new ArrayList(coreAttrs.size() + 1); + atts.addAll(coreAttrs); + atts.add( + new HtmlAttributeDesc( + "clear", new String[] {"left", "all", "right", "none"}, HtmlAttributeDesc.OTHER)); + HtmlTagDesc td = new HtmlTagDesc(name, true, false, null, atts); + stSupportedElements.put(name, td); + } + + protected static void addButtonElement(Map stSupportedElements) { + String name = "button"; + List content = new ArrayList(formMarkup.length + 3); + addStringsToList(formMarkup, content); + content.add("form"); + content.add("isindex"); + content.add("fieldset"); + content.add("iframe"); + List atts = new ArrayList(biggerAttrs.size() + 8); + atts.addAll(biggerAttrs); + addSimpleAttribute(atts, "name"); + addSimpleAttribute(atts, "value"); + atts.add( + new HtmlAttributeDesc( + "type", new String[] {"button", "submit", "reset"}, HtmlAttributeDesc.OTHER)); + addSelfAttribute(atts, "disabled"); + addSimpleAttribute(atts, "tabindex"); + addSimpleAttribute(atts, "accesskey"); + addSimpleAttribute(atts, "onfocus"); + addSimpleAttribute(atts, "onblur"); + } + + protected static void addCaptionElement( + Map stSupportedElements, List inlineContent, HtmlAttributeDesc valignAtt) { + String name = "caption"; + List atts = new ArrayList(bigAttrs.size() + 1); + atts.add( + new HtmlAttributeDesc( + "align", new String[] {"top", "bottom", "left", "right"}, HtmlAttributeDesc.IMPLIED)); + HtmlTagDesc td = new HtmlTagDesc(name, true, true, inlineContent, atts); + stSupportedElements.put(name, td); + } + + protected static void addCenterElement(Map stSupportedElements) { + String name = "center"; + HtmlTagDesc td = new HtmlTagDesc(name, true, true, flowContent, bigAttrs); + stSupportedElements.put(name, td); + } + + protected static void addDirElement(Map stSupportedElements) { + String name = "dir"; + List atts = new ArrayList(1); + addSelfAttribute(atts, "compact"); + HtmlTagDesc td = new HtmlTagDesc(name, true, true, listContent, atts); + stSupportedElements.put(name, td); + } + + protected static void addDivElement(Map stSupportedElements) { + String name = "div"; + List atts = new ArrayList(bigAttrs.size() + 1); + atts.addAll(bigAttrs); + atts.add( + new HtmlAttributeDesc( + "align", + new String[] {"left", "center", "right", "justify"}, HtmlAttributeDesc.IMPLIED)); - addSelfAttribute (atts, "noshade"); - addSimpleAttribute (atts, "size"); - addSimpleAttribute (atts, "width"); - HtmlTagDesc td = new HtmlTagDesc (name, true, false, null, atts); - stSupportedElements.put (name, td); - } - - - protected static void addInputElement - (Map stSupportedElements) - { - final String name = "input"; - List atts = new ArrayList (biggerAttrs.size () + 20); - atts.addAll (biggerAttrs); - atts.add (new HtmlAttributeDesc ("type", - new String[] {"text", "password", "checkbox", "radio", "submit", - "reset", "file", "hidden", "image", "button"}, + HtmlTagDesc td = new HtmlTagDesc(name, true, true, flowContent, atts); + stSupportedElements.put(name, td); + } + + protected static void addDlElement(Map stSupportedElements) { + String name = "dl"; + List dlContent = new ArrayList(2); + addStringsToList(new String[] {"dt", "dd"}, dlContent); + List atts = new ArrayList(bigAttrs.size() + 1); + addSelfAttribute(atts, "compact"); + HtmlTagDesc td = new HtmlTagDesc(name, true, true, dlContent, atts); + stSupportedElements.put(name, td); + } + + /** Adds the Font element. */ + protected static void addFontElement(Map stSupportedElements) { + String name = "font"; + List atts = new ArrayList(bigAttrs.size() + 10); + atts.addAll(bigAttrs); + atts.addAll(i18nAttrs); + addSimpleAttribute(atts, "size"); + addSimpleAttribute(atts, "color"); + addSimpleAttribute(atts, "face"); + + HtmlTagDesc td = new HtmlTagDesc(name, true, true, inlineContent, atts); + stSupportedElements.put(name, td); + } + + /** Adds to Frame element. */ + protected static void addFrameElement(Map stSupportedElements) { + String name = "frame"; + List atts = new ArrayList(bigAttrs.size() + 8); + atts.addAll(bigAttrs); + addSimpleAttribute(atts, "longdesc"); + addSimpleAttribute(atts, "name"); + addSimpleAttribute(atts, "src"); + addSimpleAttribute(atts, "marginwidth"); + addSimpleAttribute(atts, "marginheight"); + addSelfAttribute(atts, "noresize"); + atts.add( + new HtmlAttributeDesc( + "scrolling", new String[] {"yes", "no", "auto"}, HtmlAttributeDesc.OTHER)); + HtmlTagDesc td = new HtmlTagDesc(name, true, false, null, atts); + stSupportedElements.put(name, td); + } + + /** Adds the Frameset element. This is called only for 4.0 and 4.01 Frameset docuemnts. */ + protected static void addFramesetElement(Map stSupportedElements) { + String name = "frameset"; + List content = new ArrayList(3); + addStringsToList(new String[] {"frameset", "frame", "noframes"}, content); + List atts = new ArrayList(coreAttrs.size() + 4); + atts.addAll(coreAttrs); + addSimpleAttribute(atts, "rows"); + addSimpleAttribute(atts, "cols"); + addSimpleAttribute(atts, "onload"); + addSimpleAttribute(atts, "onunload"); + HtmlTagDesc td = new HtmlTagDesc(name, false, false, content, atts); + stSupportedElements.put(name, td); + } + + protected static void addHeadElement(Map stSupportedElements) { + String name = "head"; + String[] headMisc = new String[] {"script", "style", "meta", "link"}; + List headContent = new ArrayList(7); + headContent.add("title"); + headContent.add("isindex"); + headContent.add("base"); + headContent.add("script"); + headContent.add("style"); + headContent.add("meta"); + headContent.add("link"); + headContent.add("object"); + HtmlTagDesc td = new HtmlTagDesc(name, false, false, headContent, null); + stSupportedElements.put(name, td); + /* Attributes TITLE (required), ISINDEX (optional), and BASE (optional) + * are supposed to come in that order, ahead of anything else. + * For the moment, just toss them in with the rest. */ + addStringsToList(headMisc, headContent); + } + + protected static void addHrElement(Map stSupportedElements) { + String name = "hr"; + List atts = new ArrayList(bigAttrs.size() + 4); + atts.add( + new HtmlAttributeDesc( + "align", new String[] {"left", "center", "right"}, HtmlAttributeDesc.IMPLIED)); + addSelfAttribute(atts, "noshade"); + addSimpleAttribute(atts, "size"); + addSimpleAttribute(atts, "width"); + HtmlTagDesc td = new HtmlTagDesc(name, true, false, null, atts); + stSupportedElements.put(name, td); + } + + protected static void addInputElement(Map stSupportedElements) { + final String name = "input"; + List atts = new ArrayList(biggerAttrs.size() + 20); + atts.addAll(biggerAttrs); + atts.add( + new HtmlAttributeDesc( + "type", + new String[] { + "text", + "password", + "checkbox", + "radio", + "submit", + "reset", + "file", + "hidden", + "image", + "button" + }, HtmlAttributeDesc.OTHER)); - addSimpleAttribute (atts, "name"); - addSimpleAttribute (atts, "value"); - addSelfAttribute (atts, "checked"); - addSelfAttribute (atts, "disabled"); - addSelfAttribute (atts, "readonly"); - addSimpleAttribute (atts, "size"); - addSimpleAttribute (atts, "maxlength"); - addSimpleAttribute (atts, "src"); - addSimpleAttribute (atts, "alt"); - addSimpleAttribute (atts, "usemap"); - addSimpleAttribute (atts, "tabindex"); - addSimpleAttribute (atts, "accesskey"); - addSimpleAttribute (atts, "onfocus"); - addSimpleAttribute (atts, "onblur"); - addSimpleAttribute (atts, "onselect"); - addSimpleAttribute (atts, "onchange"); - addSimpleAttribute (atts, "accept"); - atts.add (new HtmlAttributeDesc ("align", - new String[] { "top", "middle", "bottom", "left", "right" }, + addSimpleAttribute(atts, "name"); + addSimpleAttribute(atts, "value"); + addSelfAttribute(atts, "checked"); + addSelfAttribute(atts, "disabled"); + addSelfAttribute(atts, "readonly"); + addSimpleAttribute(atts, "size"); + addSimpleAttribute(atts, "maxlength"); + addSimpleAttribute(atts, "src"); + addSimpleAttribute(atts, "alt"); + addSimpleAttribute(atts, "usemap"); + addSimpleAttribute(atts, "tabindex"); + addSimpleAttribute(atts, "accesskey"); + addSimpleAttribute(atts, "onfocus"); + addSimpleAttribute(atts, "onblur"); + addSimpleAttribute(atts, "onselect"); + addSimpleAttribute(atts, "onchange"); + addSimpleAttribute(atts, "accept"); + atts.add( + new HtmlAttributeDesc( + "align", + new String[] {"top", "middle", "bottom", "left", "right"}, HtmlAttributeDesc.IMPLIED)); - HtmlTagDesc td = new HtmlTagDesc (name, true, true, null, atts); - stSupportedElements.put (name, td); - } - - - protected static void addLegendElement (Map stSupportedElements) - { - final String name = "label"; - List atts = new ArrayList (bigAttrs.size () + 2); - atts.addAll (bigAttrs); - addSimpleAttribute (atts, "accesskey"); - atts.add (new HtmlAttributeDesc ("align", - new String[] {"top", "left", "bottom", "right" }, + HtmlTagDesc td = new HtmlTagDesc(name, true, true, null, atts); + stSupportedElements.put(name, td); + } + + protected static void addLegendElement(Map stSupportedElements) { + final String name = "label"; + List atts = new ArrayList(bigAttrs.size() + 2); + atts.addAll(bigAttrs); + addSimpleAttribute(atts, "accesskey"); + atts.add( + new HtmlAttributeDesc( + "align", new String[] {"top", "left", "bottom", "right"}, HtmlAttributeDesc.IMPLIED)); + HtmlTagDesc td = new HtmlTagDesc(name, true, true, inlineContent, atts); + stSupportedElements.put(name, td); + } + + protected static void addLiElement(Map stSupportedElements) { + final String name = "li"; + List atts = new ArrayList(bigAttrs.size() + 2); + atts.addAll(bigAttrs); + addSimpleAttribute(atts, "type"); + addSimpleAttribute(atts, "value"); + HtmlTagDesc td = new HtmlTagDesc(name, true, false, flowContent, atts); + stSupportedElements.put(name, td); + } + + protected static void addLinkElement(Map stSupportedElements) { + final String name = "link"; + List atts = new ArrayList(bigAttrs.size() + 8); + atts.addAll(bigAttrs); + addSimpleAttribute(atts, "charset"); + addSimpleAttribute(atts, "href"); + addSimpleAttribute(atts, "hreflang"); + addSimpleAttribute(atts, "type"); + addSimpleAttribute(atts, "rel"); + addSimpleAttribute(atts, "rev"); + addSimpleAttribute(atts, "media"); + addSimpleAttribute(atts, "target"); + HtmlTagDesc td = new HtmlTagDesc(name, true, false, null, atts); + stSupportedElements.put(name, td); + } + + protected static void addMenuElement(Map stSupportedElements) { + final String name = "menu"; + List atts = new ArrayList(1); + addSelfAttribute(atts, "compact"); + HtmlTagDesc td = new HtmlTagDesc(name, true, true, listContent, atts); + stSupportedElements.put(name, td); + } + + /** Adds the NOFRAMES element. The content differs between Transitional and Frameset DTD's. */ + protected static void addNoframesElement(Map stSupportedElements, int version) { + final String name = "noframes"; + List content; + if (version == HtmlModule.HTML_4_01_FRAMESET || version == HtmlModule.HTML_4_0_FRAMESET) { + content = new ArrayList(1); + // There's something I obviously don't understand about DTD syntax. + // The content is given as (BODY) -(NOFRAMES) + // But if the only allowed element is BODY, it's superfluous to + // exclude NOFRAMES. What am I missing? + content.add("body"); + } else { + content = flowContent; + } + HtmlTagDesc td = new HtmlTagDesc(name, true, true, content, bigAttrs); + stSupportedElements.put(name, td); + } + + protected static void addNoscriptElement(Map stSupportedElements) { + final String name = "noscript"; + HtmlTagDesc td = new HtmlTagDesc(name, true, false, flowContent, bigAttrs); + stSupportedElements.put(name, td); + } + + protected static void addObjectElement(Map stSupportedElements) { + final String name = "object"; + List atts = new ArrayList(biggerAttrs.size() + 13); + atts.addAll(biggerAttrs); + addSelfAttribute(atts, "declare"); + addSimpleAttribute(atts, "classid"); + addSimpleAttribute(atts, "codebase"); + addSimpleAttribute(atts, "data"); + addSimpleAttribute(atts, "type"); + addSimpleAttribute(atts, "codetype"); + addSimpleAttribute(atts, "archive"); + addSimpleAttribute(atts, "standby"); + addSimpleAttribute(atts, "height"); + addSimpleAttribute(atts, "width"); + addSimpleAttribute(atts, "usemap"); + addSimpleAttribute(atts, "name"); + addSimpleAttribute(atts, "tabindex"); + addSimpleAttribute(atts, "align"); + addSimpleAttribute(atts, "border"); + addSimpleAttribute(atts, "hspace"); + addSimpleAttribute(atts, "vspace"); + } + + protected static void addOlElement(Map stSupportedElements) { + final String name = "ol"; + List atts = new ArrayList(bigAttrs.size() + 3); + atts.addAll(bigAttrs); + addSimpleAttribute(atts, "type"); + addSelfAttribute(atts, "compact"); + addSimpleAttribute(atts, "start"); + HtmlTagDesc td = new HtmlTagDesc(name, true, true, listContent, atts); + stSupportedElements.put(name, td); + } + + protected static void addPElement(Map stSupportedElements) { + final String name = "p"; + List atts = new ArrayList(bigAttrs.size() + 1); + atts.addAll(bigAttrs); + atts.add( + new HtmlAttributeDesc( + "align", + new String[] {"left", "center", "right", "justify"}, HtmlAttributeDesc.IMPLIED)); - HtmlTagDesc td = new HtmlTagDesc (name, true, true, inlineContent, atts); - stSupportedElements.put (name, td); - } - - protected static void addLiElement - (Map stSupportedElements) - { - final String name = "li"; - List atts = new ArrayList (bigAttrs.size () + 2); - atts.addAll (bigAttrs); - addSimpleAttribute (atts, "type"); - addSimpleAttribute (atts, "value"); - HtmlTagDesc td = - new HtmlTagDesc (name, true, false, flowContent, atts); - stSupportedElements.put (name, td); - } - - protected static void addLinkElement - (Map stSupportedElements) - { - final String name = "link"; - List atts = new ArrayList (bigAttrs.size () + 8); - atts.addAll (bigAttrs); - addSimpleAttribute (atts, "charset"); - addSimpleAttribute (atts, "href"); - addSimpleAttribute (atts, "hreflang"); - addSimpleAttribute (atts, "type"); - addSimpleAttribute (atts, "rel"); - addSimpleAttribute (atts, "rev"); - addSimpleAttribute (atts, "media"); - addSimpleAttribute (atts, "target"); - HtmlTagDesc td = new HtmlTagDesc (name, true, false, null, atts); - stSupportedElements.put (name, td); - } - - protected static void addMenuElement (Map stSupportedElements) - { - final String name = "menu"; - List atts = new ArrayList (1); - addSelfAttribute (atts, "compact"); - HtmlTagDesc td = new HtmlTagDesc (name, true, true, listContent, atts); - stSupportedElements.put (name, td); - } - - - /** Adds the NOFRAMES element. The content differs between Transitional - * and Frameset DTD's. */ - protected static void addNoframesElement (Map stSupportedElements, int version) - { - final String name = "noframes"; - List content; - if (version == HtmlModule.HTML_4_01_FRAMESET || - version == HtmlModule.HTML_4_0_FRAMESET) { - content = new ArrayList (1); - // There's something I obviously don't understand about DTD syntax. - // The content is given as (BODY) -(NOFRAMES) - // But if the only allowed element is BODY, it's superfluous to - // exclude NOFRAMES. What am I missing? - content.add ("body"); - } - else { - content = flowContent; - } - HtmlTagDesc td = new HtmlTagDesc (name, true, true, content, bigAttrs); - stSupportedElements.put (name, td); - } - - - protected static void addNoscriptElement (Map stSupportedElements) - { - final String name = "noscript"; - HtmlTagDesc td = - new HtmlTagDesc (name, true, false, flowContent, bigAttrs); - stSupportedElements.put (name, td); - } - - protected static void addObjectElement (Map stSupportedElements) - { - final String name = "object"; - List atts = new ArrayList (biggerAttrs.size () + 13); - atts.addAll (biggerAttrs); - addSelfAttribute (atts, "declare"); - addSimpleAttribute (atts, "classid"); - addSimpleAttribute (atts, "codebase"); - addSimpleAttribute (atts, "data"); - addSimpleAttribute (atts, "type"); - addSimpleAttribute (atts, "codetype"); - addSimpleAttribute (atts, "archive"); - addSimpleAttribute (atts, "standby"); - addSimpleAttribute (atts, "height"); - addSimpleAttribute (atts, "width"); - addSimpleAttribute (atts, "usemap"); - addSimpleAttribute (atts, "name"); - addSimpleAttribute (atts, "tabindex"); - addSimpleAttribute (atts, "align"); - addSimpleAttribute (atts, "border"); - addSimpleAttribute (atts, "hspace"); - addSimpleAttribute (atts, "vspace"); - } - - protected static void addOlElement - (Map stSupportedElements) - { - final String name = "ol"; - List atts = new ArrayList (bigAttrs.size () + 3); - atts.addAll (bigAttrs); - addSimpleAttribute (atts, "type"); - addSelfAttribute (atts, "compact"); - addSimpleAttribute (atts, "start"); - HtmlTagDesc td = - new HtmlTagDesc (name, true, true, listContent, atts); - stSupportedElements.put (name, td); - } - - protected static void addPElement (Map stSupportedElements) - { - final String name = "p"; - List atts = new ArrayList (bigAttrs.size () + 1); - atts.addAll (bigAttrs); - atts.add (new HtmlAttributeDesc - ("align", - new String [] {"left", "center", "right", "justify"}, - HtmlAttributeDesc.IMPLIED)); - HtmlTagDesc td = - new HtmlTagDesc (name, true, false, inlineContent, atts); - stSupportedElements.put (name, td); - } - - protected static void addPreElement (Map stSupportedElements) - { - final String name = "pre"; - List preContent = new ArrayList (inlineContent.size ()); - preContent.addAll(inlineContent); - /* Take out excluded elements */ - removeStringsFromList (preContent, - new String [] - {"img", "object", "big", "small", "sub", "sup"}); - List atts = new ArrayList (bigAttrs.size () + 1); - atts.addAll (bigAttrs); - addSimpleAttribute (atts, "width"); - HtmlTagDesc td = new HtmlTagDesc (name, true, true, preContent, atts); - stSupportedElements.put (name, td); - } - - protected static void addScriptElement (Map stSupportedElements) - { - final String name = "script"; - List content = new ArrayList (1); - content.add (HtmlSpecialToken.PCDATA); - List atts = new ArrayList (6); - addSimpleAttribute (atts, "charset"); - addSimpleAttribute (atts, "type"); - addSimpleAttribute (atts, "language"); - addSimpleAttribute (atts, "src"); - addSelfAttribute (atts, "defer"); - addSimpleAttribute (atts, "event"); - addSimpleAttribute (atts, "for"); - HtmlTagDesc td = new HtmlTagDesc (name, true, true, content, atts); - stSupportedElements.put (name, td); - } - - protected static void addTableElement (Map stSupportedElements) - { - final String name = "table"; - List atts = new ArrayList (biggerAttrs.size () + 10); - atts.addAll (biggerAttrs); - addSimpleAttribute (atts, "summary"); - addSimpleAttribute (atts, "width"); - addSimpleAttribute (atts, "border"); - atts.add (new HtmlAttributeDesc ("frame", - new String[] { "void", "above", "below", "hsides", - "lhs", "rhs", "vsides", "box", "border" }, + HtmlTagDesc td = new HtmlTagDesc(name, true, false, inlineContent, atts); + stSupportedElements.put(name, td); + } + + protected static void addPreElement(Map stSupportedElements) { + final String name = "pre"; + List preContent = new ArrayList(inlineContent.size()); + preContent.addAll(inlineContent); + /* Take out excluded elements */ + removeStringsFromList(preContent, new String[] {"img", "object", "big", "small", "sub", "sup"}); + List atts = new ArrayList(bigAttrs.size() + 1); + atts.addAll(bigAttrs); + addSimpleAttribute(atts, "width"); + HtmlTagDesc td = new HtmlTagDesc(name, true, true, preContent, atts); + stSupportedElements.put(name, td); + } + + protected static void addScriptElement(Map stSupportedElements) { + final String name = "script"; + List content = new ArrayList(1); + content.add(HtmlSpecialToken.PCDATA); + List atts = new ArrayList(6); + addSimpleAttribute(atts, "charset"); + addSimpleAttribute(atts, "type"); + addSimpleAttribute(atts, "language"); + addSimpleAttribute(atts, "src"); + addSelfAttribute(atts, "defer"); + addSimpleAttribute(atts, "event"); + addSimpleAttribute(atts, "for"); + HtmlTagDesc td = new HtmlTagDesc(name, true, true, content, atts); + stSupportedElements.put(name, td); + } + + protected static void addTableElement(Map stSupportedElements) { + final String name = "table"; + List atts = new ArrayList(biggerAttrs.size() + 10); + atts.addAll(biggerAttrs); + addSimpleAttribute(atts, "summary"); + addSimpleAttribute(atts, "width"); + addSimpleAttribute(atts, "border"); + atts.add( + new HtmlAttributeDesc( + "frame", + new String[] { + "void", "above", "below", "hsides", "lhs", "rhs", "vsides", "box", "border" + }, HtmlAttributeDesc.IMPLIED)); - atts.add (new HtmlAttributeDesc ("rules", - new String[] { "none", "groups", "rows", "cols", "all" }, + atts.add( + new HtmlAttributeDesc( + "rules", + new String[] {"none", "groups", "rows", "cols", "all"}, HtmlAttributeDesc.IMPLIED)); - - addSimpleAttribute (atts, "rules"); - addSimpleAttribute (atts, "cellspacing"); - addSimpleAttribute (atts, "cellpadding"); - atts.add (new HtmlAttributeDesc ("align", - new String[] { "left", "center", "right" }, - HtmlAttributeDesc.IMPLIED)); - addSimpleAttribute (atts, "bgcolor"); - addSimpleAttribute (atts, "datapagesize"); - List[] contentArray = new List[5]; - int[] contentSequence = new int[] - { HtmlTagDesc.SEQ0_1, - HtmlTagDesc.SEQ0_MANY, - HtmlTagDesc.SEQ0_1, - HtmlTagDesc.SEQ0_1, - HtmlTagDesc.SEQ1_MANY }; - List content = new ArrayList (1); - content.add ("caption"); - contentArray[0] = content; - - content = new ArrayList(2); - content.add ("col"); - content.add ("colgroup"); - contentArray[1] = content; - - content = new ArrayList (1); - content.add ("thead"); - contentArray[2] = content; - - content = new ArrayList (1); - content.add ("tfoot"); - contentArray[3] = content; - - content = new ArrayList (1); - content.add ("tbody"); - contentArray[4] = content; - - HtmlTagDesc td = new HtmlTagDesc - (name, true, true, contentSequence, contentArray, atts); - stSupportedElements.put (name, td); - } - - protected static void addTrElement (Map stSupportedElements) - { - final String name = "tr"; - List atts = new ArrayList (bigAttrs.size() + 3); - atts.addAll (bigAttrs); - atts.add (halignAtt); - atts.add (valignAtt); - addSimpleAttribute (atts, "bgcolor"); - List content = new ArrayList (2); - content.add ("th"); - content.add ("td"); - HtmlTagDesc td = new HtmlTagDesc (name, true, false, content, atts); - td.addImplicitContainer((HtmlTagDesc) stSupportedElements.get ("tbody")); - stSupportedElements.put (name, td); - } - - protected static void addUlElement (Map stSupportedElements) - { - final String name = "ul"; - List atts = new ArrayList (bigAttrs.size () + 2); - atts.addAll (bigAttrs); - addSimpleAttribute (atts, "type"); - addSelfAttribute (atts, "compact"); - HtmlTagDesc td = - new HtmlTagDesc (name, true, true, listContent, atts); - stSupportedElements.put (name, td); - } + addSimpleAttribute(atts, "rules"); + addSimpleAttribute(atts, "cellspacing"); + addSimpleAttribute(atts, "cellpadding"); + atts.add( + new HtmlAttributeDesc( + "align", new String[] {"left", "center", "right"}, HtmlAttributeDesc.IMPLIED)); + addSimpleAttribute(atts, "bgcolor"); + addSimpleAttribute(atts, "datapagesize"); + List[] contentArray = new List[5]; + int[] contentSequence = + new int[] { + HtmlTagDesc.SEQ0_1, + HtmlTagDesc.SEQ0_MANY, + HtmlTagDesc.SEQ0_1, + HtmlTagDesc.SEQ0_1, + HtmlTagDesc.SEQ1_MANY + }; + List content = new ArrayList(1); + content.add("caption"); + contentArray[0] = content; + + content = new ArrayList(2); + content.add("col"); + content.add("colgroup"); + contentArray[1] = content; + + content = new ArrayList(1); + content.add("thead"); + contentArray[2] = content; + + content = new ArrayList(1); + content.add("tfoot"); + contentArray[3] = content; + + content = new ArrayList(1); + content.add("tbody"); + contentArray[4] = content; + + HtmlTagDesc td = new HtmlTagDesc(name, true, true, contentSequence, contentArray, atts); + stSupportedElements.put(name, td); + } + + protected static void addTrElement(Map stSupportedElements) { + final String name = "tr"; + List atts = new ArrayList(bigAttrs.size() + 3); + atts.addAll(bigAttrs); + atts.add(halignAtt); + atts.add(valignAtt); + addSimpleAttribute(atts, "bgcolor"); + List content = new ArrayList(2); + content.add("th"); + content.add("td"); + HtmlTagDesc td = new HtmlTagDesc(name, true, false, content, atts); + td.addImplicitContainer((HtmlTagDesc) stSupportedElements.get("tbody")); + stSupportedElements.put(name, td); + } + + protected static void addUlElement(Map stSupportedElements) { + final String name = "ul"; + List atts = new ArrayList(bigAttrs.size() + 2); + atts.addAll(bigAttrs); + addSimpleAttribute(atts, "type"); + addSelfAttribute(atts, "compact"); + HtmlTagDesc td = new HtmlTagDesc(name, true, true, listContent, atts); + stSupportedElements.put(name, td); + } } diff --git a/jhove-modules/html-hul/src/main/java/edu/harvard/hul/ois/jhove/module/html/Html4_01FrameDocDesc.java b/jhove-modules/html-hul/src/main/java/edu/harvard/hul/ois/jhove/module/html/Html4_01FrameDocDesc.java index ac0f89aef..1a03a449d 100644 --- a/jhove-modules/html-hul/src/main/java/edu/harvard/hul/ois/jhove/module/html/Html4_01FrameDocDesc.java +++ b/jhove-modules/html-hul/src/main/java/edu/harvard/hul/ois/jhove/module/html/Html4_01FrameDocDesc.java @@ -1,8 +1,8 @@ -/********************************************************************** - * Jhove - JSTOR/Harvard Object Validation Environment - * Copyright 2004 by JSTOR and the President and Fellows of Harvard College - **********************************************************************/ - +/** + * ******************************************************************** Jhove - JSTOR/Harvard Object + * Validation Environment Copyright 2004 by JSTOR and the President and Fellows of Harvard College + * ******************************************************************** + */ package edu.harvard.hul.ois.jhove.module.html; import edu.harvard.hul.ois.jhove.module.HtmlModule; @@ -12,29 +12,25 @@ * This class describes the requirements of an HTML 4.01 Frameset document. * * @author Gary McGath - * */ public class Html4_01FrameDocDesc extends Html4_01TFDocDesc { - /* Static, private map of supported tags. - * For efficiency, we create a static Map - * of supported tags just once, then assign that to stSupportedElements - * in the constructor. */ - private static Map stSupportedElements; - - { - stSupportedElements = new HashMap (280); - Html4_01TFDocDesc.classInit4 - (stSupportedElements, HtmlModule.HTML_4_01_FRAMESET); - } + /* Static, private map of supported tags. + * For efficiency, we create a static Map + * of supported tags just once, then assign that to stSupportedElements + * in the constructor. */ + private static Map stSupportedElements; - /** Constructor. */ - public Html4_01FrameDocDesc () - { - super(); - // publish stSupportedElements to superclass - supportedElements = stSupportedElements; - init (); - } + { + stSupportedElements = new HashMap(280); + Html4_01TFDocDesc.classInit4(stSupportedElements, HtmlModule.HTML_4_01_FRAMESET); + } + /** Constructor. */ + public Html4_01FrameDocDesc() { + super(); + // publish stSupportedElements to superclass + supportedElements = stSupportedElements; + init(); + } } diff --git a/jhove-modules/html-hul/src/main/java/edu/harvard/hul/ois/jhove/module/html/Html4_01StrictDocDesc.java b/jhove-modules/html-hul/src/main/java/edu/harvard/hul/ois/jhove/module/html/Html4_01StrictDocDesc.java index 6de041130..485aa4350 100644 --- a/jhove-modules/html-hul/src/main/java/edu/harvard/hul/ois/jhove/module/html/Html4_01StrictDocDesc.java +++ b/jhove-modules/html-hul/src/main/java/edu/harvard/hul/ois/jhove/module/html/Html4_01StrictDocDesc.java @@ -1,226 +1,229 @@ -/********************************************************************** - * Jhove - JSTOR/Harvard Object Validation Environment - * Copyright 2004 by JSTOR and the President and Fellows of Harvard College - **********************************************************************/ - +/** + * ******************************************************************** Jhove - JSTOR/Harvard Object + * Validation Environment Copyright 2004 by JSTOR and the President and Fellows of Harvard College + * ******************************************************************** + */ package edu.harvard.hul.ois.jhove.module.html; -//import edu.harvard.hul.ois.jhove.*; +// import edu.harvard.hul.ois.jhove.*; import java.util.*; -import edu.harvard.hul.ois.jhove.module.HtmlModule; -//import java.io.*; +// import java.io.*; /** * This class describes the requirements of an HTML 4.01 Strict document. * * @author Gary McGath - * */ public class Html4_01StrictDocDesc extends Html4StrictDocDesc { - /* Static, private map of supported tags. - * For efficiency, we create a static Map - * of supported tags just once, then assign that to stSupportedElements - * in the constructor. */ - private static Map stSupportedElements; - - /* Static initializer. A superclass is initialized before its - * subclass, so we can count on the static initializer of HtmlDocDesc - * to have run already. - */ - static { - stSupportedElements = new HashMap (280); - classInit4 (stSupportedElements); - - int i; - String name; - HtmlTagDesc td; - - - addSupElement (stSupportedElements); - addSubElement (stSupportedElements); - addSpanElement (stSupportedElements); - addBdoElement (stSupportedElements); - addBrElement (stSupportedElements, coreAttrs); - - addBodyElement (stSupportedElements); - addAddressElement (stSupportedElements); - addDivElement (stSupportedElements); - addAElement (stSupportedElements); - addMapElement (stSupportedElements); - - HtmlAttributeDesc shapeAtt = new HtmlAttributeDesc ("shape", - new String[] {"rect", "circle", "poly", "default" }, + /* Static, private map of supported tags. + * For efficiency, we create a static Map + * of supported tags just once, then assign that to stSupportedElements + * in the constructor. */ + private static Map stSupportedElements; + + /* Static initializer. A superclass is initialized before its + * subclass, so we can count on the static initializer of HtmlDocDesc + * to have run already. + */ + static { + stSupportedElements = new HashMap(280); + classInit4(stSupportedElements); + + int i; + String name; + HtmlTagDesc td; + + addSupElement(stSupportedElements); + addSubElement(stSupportedElements); + addSpanElement(stSupportedElements); + addBdoElement(stSupportedElements); + addBrElement(stSupportedElements, coreAttrs); + + addBodyElement(stSupportedElements); + addAddressElement(stSupportedElements); + addDivElement(stSupportedElements); + addAElement(stSupportedElements); + addMapElement(stSupportedElements); + + HtmlAttributeDesc shapeAtt = + new HtmlAttributeDesc( + "shape", + new String[] {"rect", "circle", "poly", "default"}, HtmlAttributeDesc.REQUIRED); - addAreaElement (stSupportedElements, shapeAtt); - addLinkElement (stSupportedElements); - addImgElement (stSupportedElements); - addObjectElement (stSupportedElements); - addParamElement (stSupportedElements); - addHrElement (stSupportedElements); - addPElement (stSupportedElements); - - /* The heading (H1-H6) elements */ - for (i = 0; i < headings.length; i++) { - name = headings[i]; - td = new HtmlTagDesc (name, true, true, inlineContent, bigAttrs); - stSupportedElements.put (name, td); - } - - addPreElement (stSupportedElements); - addQElement (stSupportedElements); - addBlockquoteElement (stSupportedElements); - addInsElement (stSupportedElements); - addDelElement (stSupportedElements); - - addDlElement (stSupportedElements); - addDtElement (stSupportedElements); - addDdElement (stSupportedElements); - - addOlElement (stSupportedElements); - addUlElement (stSupportedElements); - addLiElement (stSupportedElements); - addFormElement (stSupportedElements); - addLabelElement (stSupportedElements); - - addInputElement (stSupportedElements); - addSelectElement (stSupportedElements); - addOptgroupElement (stSupportedElements); - addOptionElement (stSupportedElements); - addTextareaElement (stSupportedElements); - addFieldsetElement (stSupportedElements); - addLegendElement (stSupportedElements); - addButtonElement (stSupportedElements); - addTableElement (stSupportedElements); - - HtmlAttributeDesc valignAtt = - new HtmlAttributeDesc ("valign", - new String[] { "top", "middle", "bottom", "baseline" }, - HtmlAttributeDesc.IMPLIED); - List cellalignAttrs = new ArrayList (4); // combine cellhalign and cellvalign - cellalignAttrs.add (new HtmlAttributeDesc ("align", - new String[] {"left", "center", "right", "justify", "char" }, - HtmlAttributeDesc.IMPLIED)); - addSimpleAttribute (cellalignAttrs, "char"); - addSimpleAttribute (cellalignAttrs, "charoff"); - addTheadElement (stSupportedElements, cellalignAttrs); - addTfootElement (stSupportedElements, cellalignAttrs); - addTbodyElement (stSupportedElements, cellalignAttrs); - addTrElement (stSupportedElements); - - addThElement (stSupportedElements); - addTdElement (stSupportedElements); - addCaptionElement - (stSupportedElements, inlineContent, valignAtt); - - addColgroupElement (stSupportedElements, cellalignAttrs); - addColElement (stSupportedElements, cellalignAttrs); - - addHeadElement (stSupportedElements); - addTitleElement (stSupportedElements); - addBaseElement (stSupportedElements); - addMetaElement (stSupportedElements); - addScriptElement (stSupportedElements); - addNoscriptElement (stSupportedElements); - addStyleElement (stSupportedElements); - - /* The HTML element */ - name = "html"; - List htmlContent = new ArrayList (2); - htmlContent.add ("head"); - htmlContent.add ("body"); - td = new HtmlTagDesc (name, false, false, htmlContent, i18nAttrs); - stSupportedElements.put (name, td); - } - - /** - * Constructor. - */ - public Html4_01StrictDocDesc () - { - super(); - // publish stSupportedElements to superclass - supportedElements = stSupportedElements; - init (); - } - - - - private static void addFormElement (Map stSupportedElements) - { - final String name = "form"; - List atts = new ArrayList (bigAttrs.size () + 8); - atts.addAll (bigAttrs); - addSimpleAttribute (atts, "action"); - addSimpleAttribute (atts, "method"); - addSimpleAttribute (atts, "enctype"); - addSimpleAttribute (atts, "accept"); // 4.01 - addSimpleAttribute (atts, "name"); // 4.01 - addSimpleAttribute (atts, "onsubmit"); - addSimpleAttribute (atts, "onreset"); - addSimpleAttribute (atts, "accept-charset"); - List formContent = new ArrayList (blockContent.size ()); - formContent.addAll (blockContent); - formContent.add ("script"); - removeStringsFromList (formContent, new String[] { "form" }); - HtmlTagDesc td = new HtmlTagDesc (name, true, true, formContent, atts); - stSupportedElements.put (name, td); - } + addAreaElement(stSupportedElements, shapeAtt); + addLinkElement(stSupportedElements); + addImgElement(stSupportedElements); + addObjectElement(stSupportedElements); + addParamElement(stSupportedElements); + addHrElement(stSupportedElements); + addPElement(stSupportedElements); - private static void addHrElement (Map stSupportedElements) - { - String name = "hr"; - HtmlTagDesc td = new HtmlTagDesc (name, true, false, null, bigAttrs); - stSupportedElements.put (name, td); + /* The heading (H1-H6) elements */ + for (i = 0; i < headings.length; i++) { + name = headings[i]; + td = new HtmlTagDesc(name, true, true, inlineContent, bigAttrs); + stSupportedElements.put(name, td); } - private static void addImgElement (Map stSupportedElements) - { - String name = "img"; - List atts = new ArrayList (bigAttrs.size () + 10); - atts.addAll (bigAttrs); - addRequiredAttribute (atts, "src"); - addRequiredAttribute (atts, "alt"); - addSimpleAttribute (atts, "longdesc"); - addSimpleAttribute (atts, "name"); // new to 4.01 - addSimpleAttribute (atts, "height"); - addSimpleAttribute (atts, "width"); - addSimpleAttribute (atts, "usemap"); - addSelfAttribute (atts, "ismap"); - HtmlTagDesc td = new HtmlTagDesc (name, true, false, null, atts); - stSupportedElements.put (name, td); - } + addPreElement(stSupportedElements); + addQElement(stSupportedElements); + addBlockquoteElement(stSupportedElements); + addInsElement(stSupportedElements); + addDelElement(stSupportedElements); + + addDlElement(stSupportedElements); + addDtElement(stSupportedElements); + addDdElement(stSupportedElements); - private static void addInputElement - (Map stSupportedElements) - { - final String name = "input"; - List atts = new ArrayList (biggerAttrs.size () + 20); - atts.addAll (biggerAttrs); - atts.add (new HtmlAttributeDesc ("type", - new String[] {"text", "password", "checkbox", "radio", "submit", - "reset", "file", "hidden", "image", "button"}, + addOlElement(stSupportedElements); + addUlElement(stSupportedElements); + addLiElement(stSupportedElements); + addFormElement(stSupportedElements); + addLabelElement(stSupportedElements); + + addInputElement(stSupportedElements); + addSelectElement(stSupportedElements); + addOptgroupElement(stSupportedElements); + addOptionElement(stSupportedElements); + addTextareaElement(stSupportedElements); + addFieldsetElement(stSupportedElements); + addLegendElement(stSupportedElements); + addButtonElement(stSupportedElements); + addTableElement(stSupportedElements); + + HtmlAttributeDesc valignAtt = + new HtmlAttributeDesc( + "valign", + new String[] {"top", "middle", "bottom", "baseline"}, + HtmlAttributeDesc.IMPLIED); + List cellalignAttrs = new ArrayList(4); // combine cellhalign and cellvalign + cellalignAttrs.add( + new HtmlAttributeDesc( + "align", + new String[] {"left", "center", "right", "justify", "char"}, + HtmlAttributeDesc.IMPLIED)); + addSimpleAttribute(cellalignAttrs, "char"); + addSimpleAttribute(cellalignAttrs, "charoff"); + addTheadElement(stSupportedElements, cellalignAttrs); + addTfootElement(stSupportedElements, cellalignAttrs); + addTbodyElement(stSupportedElements, cellalignAttrs); + addTrElement(stSupportedElements); + + addThElement(stSupportedElements); + addTdElement(stSupportedElements); + addCaptionElement(stSupportedElements, inlineContent, valignAtt); + + addColgroupElement(stSupportedElements, cellalignAttrs); + addColElement(stSupportedElements, cellalignAttrs); + + addHeadElement(stSupportedElements); + addTitleElement(stSupportedElements); + addBaseElement(stSupportedElements); + addMetaElement(stSupportedElements); + addScriptElement(stSupportedElements); + addNoscriptElement(stSupportedElements); + addStyleElement(stSupportedElements); + + /* The HTML element */ + name = "html"; + List htmlContent = new ArrayList(2); + htmlContent.add("head"); + htmlContent.add("body"); + td = new HtmlTagDesc(name, false, false, htmlContent, i18nAttrs); + stSupportedElements.put(name, td); + } + + /** Constructor. */ + public Html4_01StrictDocDesc() { + super(); + // publish stSupportedElements to superclass + supportedElements = stSupportedElements; + init(); + } + + private static void addFormElement(Map stSupportedElements) { + final String name = "form"; + List atts = new ArrayList(bigAttrs.size() + 8); + atts.addAll(bigAttrs); + addSimpleAttribute(atts, "action"); + addSimpleAttribute(atts, "method"); + addSimpleAttribute(atts, "enctype"); + addSimpleAttribute(atts, "accept"); // 4.01 + addSimpleAttribute(atts, "name"); // 4.01 + addSimpleAttribute(atts, "onsubmit"); + addSimpleAttribute(atts, "onreset"); + addSimpleAttribute(atts, "accept-charset"); + List formContent = new ArrayList(blockContent.size()); + formContent.addAll(blockContent); + formContent.add("script"); + removeStringsFromList(formContent, new String[] {"form"}); + HtmlTagDesc td = new HtmlTagDesc(name, true, true, formContent, atts); + stSupportedElements.put(name, td); + } + + private static void addHrElement(Map stSupportedElements) { + String name = "hr"; + HtmlTagDesc td = new HtmlTagDesc(name, true, false, null, bigAttrs); + stSupportedElements.put(name, td); + } + + private static void addImgElement(Map stSupportedElements) { + String name = "img"; + List atts = new ArrayList(bigAttrs.size() + 10); + atts.addAll(bigAttrs); + addRequiredAttribute(atts, "src"); + addRequiredAttribute(atts, "alt"); + addSimpleAttribute(atts, "longdesc"); + addSimpleAttribute(atts, "name"); // new to 4.01 + addSimpleAttribute(atts, "height"); + addSimpleAttribute(atts, "width"); + addSimpleAttribute(atts, "usemap"); + addSelfAttribute(atts, "ismap"); + HtmlTagDesc td = new HtmlTagDesc(name, true, false, null, atts); + stSupportedElements.put(name, td); + } + + private static void addInputElement(Map stSupportedElements) { + final String name = "input"; + List atts = new ArrayList(biggerAttrs.size() + 20); + atts.addAll(biggerAttrs); + atts.add( + new HtmlAttributeDesc( + "type", + new String[] { + "text", + "password", + "checkbox", + "radio", + "submit", + "reset", + "file", + "hidden", + "image", + "button" + }, HtmlAttributeDesc.OTHER)); - addSimpleAttribute (atts, "name"); - addSimpleAttribute (atts, "value"); - addSelfAttribute (atts, "checked"); - addSelfAttribute (atts, "disabled"); - addSelfAttribute (atts, "readonly"); - addSimpleAttribute (atts, "size"); - addSimpleAttribute (atts, "maxlength"); - addSimpleAttribute (atts, "src"); - addSimpleAttribute (atts, "alt"); - addSimpleAttribute (atts, "usemap"); - addSelfAttribute (atts, "ismap"); // 4.01 - addSimpleAttribute (atts, "tabindex"); - addSimpleAttribute (atts, "accesskey"); - addSimpleAttribute (atts, "onfocus"); - addSimpleAttribute (atts, "onblur"); - addSimpleAttribute (atts, "onselect"); - addSimpleAttribute (atts, "onchange"); - addSimpleAttribute (atts, "accept"); - HtmlTagDesc td = new HtmlTagDesc (name, true, true, null, atts); - stSupportedElements.put (name, td); - } + addSimpleAttribute(atts, "name"); + addSimpleAttribute(atts, "value"); + addSelfAttribute(atts, "checked"); + addSelfAttribute(atts, "disabled"); + addSelfAttribute(atts, "readonly"); + addSimpleAttribute(atts, "size"); + addSimpleAttribute(atts, "maxlength"); + addSimpleAttribute(atts, "src"); + addSimpleAttribute(atts, "alt"); + addSimpleAttribute(atts, "usemap"); + addSelfAttribute(atts, "ismap"); // 4.01 + addSimpleAttribute(atts, "tabindex"); + addSimpleAttribute(atts, "accesskey"); + addSimpleAttribute(atts, "onfocus"); + addSimpleAttribute(atts, "onblur"); + addSimpleAttribute(atts, "onselect"); + addSimpleAttribute(atts, "onchange"); + addSimpleAttribute(atts, "accept"); + HtmlTagDesc td = new HtmlTagDesc(name, true, true, null, atts); + stSupportedElements.put(name, td); + } } diff --git a/jhove-modules/html-hul/src/main/java/edu/harvard/hul/ois/jhove/module/html/Html4_01TFDocDesc.java b/jhove-modules/html-hul/src/main/java/edu/harvard/hul/ois/jhove/module/html/Html4_01TFDocDesc.java index 56b0d97f8..d0ed4d49e 100644 --- a/jhove-modules/html-hul/src/main/java/edu/harvard/hul/ois/jhove/module/html/Html4_01TFDocDesc.java +++ b/jhove-modules/html-hul/src/main/java/edu/harvard/hul/ois/jhove/module/html/Html4_01TFDocDesc.java @@ -1,210 +1,209 @@ -/********************************************************************** - * Jhove - JSTOR/Harvard Object Validation Environment - * Copyright 2004 by JSTOR and the President and Fellows of Harvard College +/** + * ******************************************************************** Jhove - JSTOR/Harvard Object + * Validation Environment Copyright 2004 by JSTOR and the President and Fellows of Harvard College * - **********************************************************************/ - + *

******************************************************************** + */ package edu.harvard.hul.ois.jhove.module.html; import edu.harvard.hul.ois.jhove.module.HtmlModule; import java.util.*; + /** - * Abstract class for the HTML 4.01 Transitional and Frameset document - * types. These are identical except for one element apiece, so nearly - * all the code is here or in its superclasses. - * - * @author Gary McGath + * Abstract class for the HTML 4.01 Transitional and Frameset document types. These are identical + * except for one element apiece, so nearly all the code is here or in its superclasses. * + * @author Gary McGath */ public abstract class Html4_01TFDocDesc extends Html4TFDocDesc { - protected Html4_01TFDocDesc() { - super(); + protected Html4_01TFDocDesc() { + super(); + } + + /** Initialization code. This is called from the static initializer of our subclasses. */ + protected static void classInit4(Map stSupportedElements, int version) { + Html4TFDocDesc.classInit4(stSupportedElements); + int i; + String name; + HtmlTagDesc td; + + for (i = 0; i < fontMarkup.length; i++) { + name = fontMarkup[i]; + td = new HtmlTagDesc(name, true, true, inlineContent, bigAttrs); + stSupportedElements.put(name, td); } - - /** Initialization code. This is called from the static initializer - * of our subclasses. */ - protected static void classInit4 (Map stSupportedElements, int version) - { - Html4TFDocDesc.classInit4(stSupportedElements); - int i; - String name; - HtmlTagDesc td; - - for (i = 0; i < fontMarkup.length; i++) { - name = fontMarkup[i]; - td = new HtmlTagDesc (name, true, true, inlineContent, bigAttrs); - stSupportedElements.put (name, td); - } - - /* Phrase elements. */ - for (i = 0; i < phraseMarkup.length; i++) { - name = phraseMarkup[i]; - td = new HtmlTagDesc (name, true, true, inlineContent, bigAttrs); - stSupportedElements.put (name, td); - } - - addSupElement (stSupportedElements); - addSubElement (stSupportedElements); - addSpanElement (stSupportedElements); - addBdoElement (stSupportedElements); - addBasefontElement (stSupportedElements); - addFontElement (stSupportedElements); - addBrElement (stSupportedElements, coreAttrs); - - addAddressElement (stSupportedElements); - addDivElement (stSupportedElements); - addCenterElement (stSupportedElements); - addAElement (stSupportedElements); - addMapElement (stSupportedElements); - - HtmlAttributeDesc shapeAtt = new HtmlAttributeDesc ("shape", - new String[] {"rect", "circle", "poly", "default" }, - HtmlAttributeDesc.REQUIRED); - addAreaElement (stSupportedElements, shapeAtt); - addLinkElement (stSupportedElements); - addImgElement (stSupportedElements); - addObjectElement (stSupportedElements); - addParamElement (stSupportedElements); - HtmlAttributeDesc ialignAtt = new HtmlAttributeDesc ("align", - new String[] { "top", "middle", "bottom", "left", "right" }, - HtmlAttributeDesc.IMPLIED); - addAppletElement (stSupportedElements, ialignAtt); - addHrElement (stSupportedElements); - addPElement (stSupportedElements); - - /* The heading (H1-H6) elements */ - for (i = 0; i < headings.length; i++) { - name = headings[i]; - td = new HtmlTagDesc (name, true, true, inlineContent, bigAttrs); - stSupportedElements.put (name, td); - } - - addPreElement (stSupportedElements); - addQElement (stSupportedElements); - addBlockquoteElement (stSupportedElements); - addInsElement (stSupportedElements); - addDelElement (stSupportedElements); - - addDlElement (stSupportedElements); - addDtElement (stSupportedElements); - addDdElement (stSupportedElements); - - addOlElement (stSupportedElements); - addUlElement (stSupportedElements); - addDirElement (stSupportedElements); - addMenuElement (stSupportedElements); - addLiElement (stSupportedElements); - addFormElement (stSupportedElements); - addLabelElement (stSupportedElements); - - addInputElement (stSupportedElements); - addSelectElement (stSupportedElements); - addOptgroupElement (stSupportedElements); - addOptionElement (stSupportedElements); - addTextareaElement (stSupportedElements); - addFieldsetElement (stSupportedElements); - addLegendElement (stSupportedElements); - addButtonElement (stSupportedElements); - addTableElement (stSupportedElements); - - HtmlAttributeDesc valignAtt = - new HtmlAttributeDesc ("valign", - new String[] { "top", "middle", "bottom", "baseline" }, - HtmlAttributeDesc.IMPLIED); - List cellalignAttrs = new ArrayList (4); // combine cellhalign and cellvalign - cellalignAttrs.add (new HtmlAttributeDesc ("align", - new String[] {"left", "center", "right", "justify", "char" }, - HtmlAttributeDesc.IMPLIED)); - addSimpleAttribute (cellalignAttrs, "char"); - addSimpleAttribute (cellalignAttrs, "charoff"); - cellalignAttrs.add (valignAtt); - addTheadElement (stSupportedElements, cellalignAttrs); - addTfootElement (stSupportedElements, cellalignAttrs); - addTbodyElement (stSupportedElements, cellalignAttrs); - addTrElement (stSupportedElements); - - - addThElement (stSupportedElements); - addTdElement (stSupportedElements); - addCaptionElement - (stSupportedElements, inlineContent, valignAtt); - - addColgroupElement (stSupportedElements, cellalignAttrs); - addColElement (stSupportedElements, cellalignAttrs); - - addHeadElement (stSupportedElements); - addTitleElement (stSupportedElements); - addBaseElement (stSupportedElements); - addMetaElement (stSupportedElements); - addScriptElement (stSupportedElements); - addNoscriptElement (stSupportedElements); - addStyleElement (stSupportedElements); - - /* The HTML element */ - name = "html"; - List htmlContent = new ArrayList (2); - htmlContent.add ("head"); - if (version == HtmlModule.HTML_4_01_FRAMESET) { - htmlContent.add ("frameset"); - } - else { - htmlContent.add ("body"); - } - td = new HtmlTagDesc (name, false, false, htmlContent, i18nAttrs); - stSupportedElements.put (name, td); - addNoframesElement (stSupportedElements, version); - if (version == HtmlModule.HTML_4_01_FRAMESET) { - addFramesetElement (stSupportedElements); - addFrameElement (stSupportedElements); - } - addBodyElement (stSupportedElements); + + /* Phrase elements. */ + for (i = 0; i < phraseMarkup.length; i++) { + name = phraseMarkup[i]; + td = new HtmlTagDesc(name, true, true, inlineContent, bigAttrs); + stSupportedElements.put(name, td); } - private static void addFormElement (Map stSupportedElements) - { - final String name = "form"; - List atts = new ArrayList (bigAttrs.size () + 9); - atts.addAll (bigAttrs); - addSimpleAttribute (atts, "action"); - addSimpleAttribute (atts, "method"); - addSimpleAttribute (atts, "enctype"); - addSimpleAttribute (atts, "accept"); - addSimpleAttribute (atts, "name"); - addSimpleAttribute (atts, "onsubmit"); - addSimpleAttribute (atts, "onreset"); - addSimpleAttribute (atts, "target"); - addSimpleAttribute (atts, "accept-charset"); - List formContent = new ArrayList (flowContent.size ()); - formContent.addAll (flowContent); - //formContent.add ("script"); - removeStringsFromList (formContent, new String[] { "form" }); - HtmlTagDesc td = new HtmlTagDesc (name, true, true, formContent, atts); - stSupportedElements.put (name, td); + addSupElement(stSupportedElements); + addSubElement(stSupportedElements); + addSpanElement(stSupportedElements); + addBdoElement(stSupportedElements); + addBasefontElement(stSupportedElements); + addFontElement(stSupportedElements); + addBrElement(stSupportedElements, coreAttrs); + + addAddressElement(stSupportedElements); + addDivElement(stSupportedElements); + addCenterElement(stSupportedElements); + addAElement(stSupportedElements); + addMapElement(stSupportedElements); + + HtmlAttributeDesc shapeAtt = + new HtmlAttributeDesc( + "shape", + new String[] {"rect", "circle", "poly", "default"}, + HtmlAttributeDesc.REQUIRED); + addAreaElement(stSupportedElements, shapeAtt); + addLinkElement(stSupportedElements); + addImgElement(stSupportedElements); + addObjectElement(stSupportedElements); + addParamElement(stSupportedElements); + HtmlAttributeDesc ialignAtt = + new HtmlAttributeDesc( + "align", + new String[] {"top", "middle", "bottom", "left", "right"}, + HtmlAttributeDesc.IMPLIED); + addAppletElement(stSupportedElements, ialignAtt); + addHrElement(stSupportedElements); + addPElement(stSupportedElements); + + /* The heading (H1-H6) elements */ + for (i = 0; i < headings.length; i++) { + name = headings[i]; + td = new HtmlTagDesc(name, true, true, inlineContent, bigAttrs); + stSupportedElements.put(name, td); } + addPreElement(stSupportedElements); + addQElement(stSupportedElements); + addBlockquoteElement(stSupportedElements); + addInsElement(stSupportedElements); + addDelElement(stSupportedElements); + + addDlElement(stSupportedElements); + addDtElement(stSupportedElements); + addDdElement(stSupportedElements); - private static void addImgElement (Map stSupportedElements) - { - String name = "img"; - List atts = new ArrayList (bigAttrs.size () + 10); - atts.addAll (bigAttrs); - addRequiredAttribute (atts, "src"); - addRequiredAttribute (atts, "alt"); - addSimpleAttribute (atts, "longdesc"); - addSimpleAttribute (atts, "name"); // new to 4.01 - addSimpleAttribute (atts, "height"); - addSimpleAttribute (atts, "width"); - addSimpleAttribute (atts, "usemap"); - addSelfAttribute (atts, "ismap"); - atts.add (new HtmlAttributeDesc ("align", - new String[] { "top", "middle", "bottom", "left", "right" }, + addOlElement(stSupportedElements); + addUlElement(stSupportedElements); + addDirElement(stSupportedElements); + addMenuElement(stSupportedElements); + addLiElement(stSupportedElements); + addFormElement(stSupportedElements); + addLabelElement(stSupportedElements); + + addInputElement(stSupportedElements); + addSelectElement(stSupportedElements); + addOptgroupElement(stSupportedElements); + addOptionElement(stSupportedElements); + addTextareaElement(stSupportedElements); + addFieldsetElement(stSupportedElements); + addLegendElement(stSupportedElements); + addButtonElement(stSupportedElements); + addTableElement(stSupportedElements); + + HtmlAttributeDesc valignAtt = + new HtmlAttributeDesc( + "valign", + new String[] {"top", "middle", "bottom", "baseline"}, + HtmlAttributeDesc.IMPLIED); + List cellalignAttrs = new ArrayList(4); // combine cellhalign and cellvalign + cellalignAttrs.add( + new HtmlAttributeDesc( + "align", + new String[] {"left", "center", "right", "justify", "char"}, HtmlAttributeDesc.IMPLIED)); - addSimpleAttribute (atts, "border"); - addSimpleAttribute (atts, "hspace"); - addSimpleAttribute (atts, "vspace"); - HtmlTagDesc td = new HtmlTagDesc (name, true, false, null, atts); - stSupportedElements.put (name, td); + addSimpleAttribute(cellalignAttrs, "char"); + addSimpleAttribute(cellalignAttrs, "charoff"); + cellalignAttrs.add(valignAtt); + addTheadElement(stSupportedElements, cellalignAttrs); + addTfootElement(stSupportedElements, cellalignAttrs); + addTbodyElement(stSupportedElements, cellalignAttrs); + addTrElement(stSupportedElements); + + addThElement(stSupportedElements); + addTdElement(stSupportedElements); + addCaptionElement(stSupportedElements, inlineContent, valignAtt); + + addColgroupElement(stSupportedElements, cellalignAttrs); + addColElement(stSupportedElements, cellalignAttrs); + + addHeadElement(stSupportedElements); + addTitleElement(stSupportedElements); + addBaseElement(stSupportedElements); + addMetaElement(stSupportedElements); + addScriptElement(stSupportedElements); + addNoscriptElement(stSupportedElements); + addStyleElement(stSupportedElements); + + /* The HTML element */ + name = "html"; + List htmlContent = new ArrayList(2); + htmlContent.add("head"); + if (version == HtmlModule.HTML_4_01_FRAMESET) { + htmlContent.add("frameset"); + } else { + htmlContent.add("body"); } + td = new HtmlTagDesc(name, false, false, htmlContent, i18nAttrs); + stSupportedElements.put(name, td); + addNoframesElement(stSupportedElements, version); + if (version == HtmlModule.HTML_4_01_FRAMESET) { + addFramesetElement(stSupportedElements); + addFrameElement(stSupportedElements); + } + addBodyElement(stSupportedElements); + } + + private static void addFormElement(Map stSupportedElements) { + final String name = "form"; + List atts = new ArrayList(bigAttrs.size() + 9); + atts.addAll(bigAttrs); + addSimpleAttribute(atts, "action"); + addSimpleAttribute(atts, "method"); + addSimpleAttribute(atts, "enctype"); + addSimpleAttribute(atts, "accept"); + addSimpleAttribute(atts, "name"); + addSimpleAttribute(atts, "onsubmit"); + addSimpleAttribute(atts, "onreset"); + addSimpleAttribute(atts, "target"); + addSimpleAttribute(atts, "accept-charset"); + List formContent = new ArrayList(flowContent.size()); + formContent.addAll(flowContent); + // formContent.add ("script"); + removeStringsFromList(formContent, new String[] {"form"}); + HtmlTagDesc td = new HtmlTagDesc(name, true, true, formContent, atts); + stSupportedElements.put(name, td); + } + private static void addImgElement(Map stSupportedElements) { + String name = "img"; + List atts = new ArrayList(bigAttrs.size() + 10); + atts.addAll(bigAttrs); + addRequiredAttribute(atts, "src"); + addRequiredAttribute(atts, "alt"); + addSimpleAttribute(atts, "longdesc"); + addSimpleAttribute(atts, "name"); // new to 4.01 + addSimpleAttribute(atts, "height"); + addSimpleAttribute(atts, "width"); + addSimpleAttribute(atts, "usemap"); + addSelfAttribute(atts, "ismap"); + atts.add( + new HtmlAttributeDesc( + "align", + new String[] {"top", "middle", "bottom", "left", "right"}, + HtmlAttributeDesc.IMPLIED)); + addSimpleAttribute(atts, "border"); + addSimpleAttribute(atts, "hspace"); + addSimpleAttribute(atts, "vspace"); + HtmlTagDesc td = new HtmlTagDesc(name, true, false, null, atts); + stSupportedElements.put(name, td); + } } diff --git a/jhove-modules/html-hul/src/main/java/edu/harvard/hul/ois/jhove/module/html/Html4_01TransDocDesc.java b/jhove-modules/html-hul/src/main/java/edu/harvard/hul/ois/jhove/module/html/Html4_01TransDocDesc.java index 1d30353c1..63dda46d3 100644 --- a/jhove-modules/html-hul/src/main/java/edu/harvard/hul/ois/jhove/module/html/Html4_01TransDocDesc.java +++ b/jhove-modules/html-hul/src/main/java/edu/harvard/hul/ois/jhove/module/html/Html4_01TransDocDesc.java @@ -1,9 +1,9 @@ -/********************************************************************** - * Jhove - JSTOR/Harvard Object Validation Environment - * Copyright 2004 by JSTOR and the President and Fellows of Harvard College +/** + * ******************************************************************** Jhove - JSTOR/Harvard Object + * Validation Environment Copyright 2004 by JSTOR and the President and Fellows of Harvard College * - **********************************************************************/ - + *

******************************************************************** + */ package edu.harvard.hul.ois.jhove.module.html; import edu.harvard.hul.ois.jhove.module.HtmlModule; @@ -11,33 +11,27 @@ /** * This class describes the requirements of an HTML 4.01 Transitional document. - * - * @author Gary McGath * + * @author Gary McGath */ public class Html4_01TransDocDesc extends Html4_01TFDocDesc { - /* Static, private map of supported tags. - * For efficiency, we create a static Map - * of supported tags just once, then assign that to stSupportedElements - * in the constructor. */ - private static Map stSupportedElements; - - { - stSupportedElements = new HashMap (280); - Html4_01TFDocDesc.classInit4 - (stSupportedElements, HtmlModule.HTML_4_01_TRANSITIONAL); + /* Static, private map of supported tags. + * For efficiency, we create a static Map + * of supported tags just once, then assign that to stSupportedElements + * in the constructor. */ + private static Map stSupportedElements; - } + { + stSupportedElements = new HashMap(280); + Html4_01TFDocDesc.classInit4(stSupportedElements, HtmlModule.HTML_4_01_TRANSITIONAL); + } - /** - * Constructor. - */ - public Html4_01TransDocDesc () - { - super(); - // publish stSupportedElements to superclass - supportedElements = stSupportedElements; - init (); - } + /** Constructor. */ + public Html4_01TransDocDesc() { + super(); + // publish stSupportedElements to superclass + supportedElements = stSupportedElements; + init(); + } } diff --git a/jhove-modules/html-hul/src/main/java/edu/harvard/hul/ois/jhove/module/html/Html4_0FrameDocDesc.java b/jhove-modules/html-hul/src/main/java/edu/harvard/hul/ois/jhove/module/html/Html4_0FrameDocDesc.java index e0d2846d7..c714f8a09 100644 --- a/jhove-modules/html-hul/src/main/java/edu/harvard/hul/ois/jhove/module/html/Html4_0FrameDocDesc.java +++ b/jhove-modules/html-hul/src/main/java/edu/harvard/hul/ois/jhove/module/html/Html4_0FrameDocDesc.java @@ -1,11 +1,10 @@ -/********************************************************************** - * Jhove - JSTOR/Harvard Object Validation Environment - * Copyright 2004 by JSTOR and the President and Fellows of Harvard College - **********************************************************************/ - +/** + * ******************************************************************** Jhove - JSTOR/Harvard Object + * Validation Environment Copyright 2004 by JSTOR and the President and Fellows of Harvard College + * ******************************************************************** + */ package edu.harvard.hul.ois.jhove.module.html; - import edu.harvard.hul.ois.jhove.module.HtmlModule; import java.util.*; @@ -13,31 +12,25 @@ * This class describes the requirements of an HTML 4.01 Frameset document. * * @author Gary McGath - * */ public class Html4_0FrameDocDesc extends Html4_0TFDocDesc { - /* Static, private map of supported tags. - * For efficiency, we create a static Map - * of supported tags just once, then assign that to stSupportedElements - * in the constructor. */ - private static Map stSupportedElements; - - { - stSupportedElements = new HashMap (280); - Html4_0TFDocDesc.classInit4 - (stSupportedElements, HtmlModule.HTML_4_0_FRAMESET); - } - - /** - * Constructor. - */ - public Html4_0FrameDocDesc () - { - super(); - // publish stSupportedElements to superclass - supportedElements = stSupportedElements; - init (); - } - + /* Static, private map of supported tags. + * For efficiency, we create a static Map + * of supported tags just once, then assign that to stSupportedElements + * in the constructor. */ + private static Map stSupportedElements; + + { + stSupportedElements = new HashMap(280); + Html4_0TFDocDesc.classInit4(stSupportedElements, HtmlModule.HTML_4_0_FRAMESET); + } + + /** Constructor. */ + public Html4_0FrameDocDesc() { + super(); + // publish stSupportedElements to superclass + supportedElements = stSupportedElements; + init(); + } } diff --git a/jhove-modules/html-hul/src/main/java/edu/harvard/hul/ois/jhove/module/html/Html4_0StrictDocDesc.java b/jhove-modules/html-hul/src/main/java/edu/harvard/hul/ois/jhove/module/html/Html4_0StrictDocDesc.java index 04ca8e969..04ad66011 100644 --- a/jhove-modules/html-hul/src/main/java/edu/harvard/hul/ois/jhove/module/html/Html4_0StrictDocDesc.java +++ b/jhove-modules/html-hul/src/main/java/edu/harvard/hul/ois/jhove/module/html/Html4_0StrictDocDesc.java @@ -1,237 +1,235 @@ -/********************************************************************** - * Jhove - JSTOR/Harvard Object Validation Environment - * Copyright 2004 by JSTOR and the President and Fellows of Harvard College - **********************************************************************/ - +/** + * ******************************************************************** Jhove - JSTOR/Harvard Object + * Validation Environment Copyright 2004 by JSTOR and the President and Fellows of Harvard College + * ******************************************************************** + */ package edu.harvard.hul.ois.jhove.module.html; -//import edu.harvard.hul.ois.jhove.*; +// import edu.harvard.hul.ois.jhove.*; import java.util.*; -import edu.harvard.hul.ois.jhove.module.HtmlModule; - /** * This class describes the requirements of an HTML 4.0 Strict document. * * @author Gary McGath - * */ public class Html4_0StrictDocDesc extends Html4StrictDocDesc { - /* Static, private map of supported tags. - * For efficiency, we create a static Map - * of supported tags just once, then assign that to stSupportedElements - * in the constructor. */ - private static Map stSupportedElements; - - /* Static initializer. A superclass is initialized before its - * subclass, so we can count on the static initializer of HtmlDocDesc - * to have run already. - * - * It's time to start thinking about how to factor this code. - * Each element can be created separately, with the necessary - * arguments passed for each one. It would be a nice pattern if - * all elements had the same calling sequence, but realistically - * some are going to need extras such as special lists of names. - * The element functions (which will all be static) should be here - * if unique, or in the parent class if they can be used for more - * than one version of HTML. There should be a naming convention - * for the functions in the parent class indicating which names - * they can be used with. - */ - static { - stSupportedElements = new HashMap (280); - classInit4 (stSupportedElements); - - - int i; - String name; - HtmlTagDesc td; - - addSupElement (stSupportedElements); - addSubElement (stSupportedElements); - addSpanElement (stSupportedElements); - addBdoElement (stSupportedElements); - addBrElement (stSupportedElements, coreAttrs); - - addBodyElement (stSupportedElements); - addAddressElement (stSupportedElements); - addDivElement (stSupportedElements); - addAElement (stSupportedElements); - addMapElement (stSupportedElements); - - HtmlAttributeDesc shapeAtt = new HtmlAttributeDesc ("shape", - new String[] {"rect", "circle", "poly", "default" }, + /* Static, private map of supported tags. + * For efficiency, we create a static Map + * of supported tags just once, then assign that to stSupportedElements + * in the constructor. */ + private static Map stSupportedElements; + + /* Static initializer. A superclass is initialized before its + * subclass, so we can count on the static initializer of HtmlDocDesc + * to have run already. + * + * It's time to start thinking about how to factor this code. + * Each element can be created separately, with the necessary + * arguments passed for each one. It would be a nice pattern if + * all elements had the same calling sequence, but realistically + * some are going to need extras such as special lists of names. + * The element functions (which will all be static) should be here + * if unique, or in the parent class if they can be used for more + * than one version of HTML. There should be a naming convention + * for the functions in the parent class indicating which names + * they can be used with. + */ + static { + stSupportedElements = new HashMap(280); + classInit4(stSupportedElements); + + int i; + String name; + HtmlTagDesc td; + + addSupElement(stSupportedElements); + addSubElement(stSupportedElements); + addSpanElement(stSupportedElements); + addBdoElement(stSupportedElements); + addBrElement(stSupportedElements, coreAttrs); + + addBodyElement(stSupportedElements); + addAddressElement(stSupportedElements); + addDivElement(stSupportedElements); + addAElement(stSupportedElements); + addMapElement(stSupportedElements); + + HtmlAttributeDesc shapeAtt = + new HtmlAttributeDesc( + "shape", + new String[] {"rect", "circle", "poly", "default"}, HtmlAttributeDesc.REQUIRED); - addAreaElement (stSupportedElements, shapeAtt); - addLinkElement (stSupportedElements); - addImgElement (stSupportedElements); - addObjectElement (stSupportedElements); - addParamElement (stSupportedElements); - addHrElement (stSupportedElements); - addPElement (stSupportedElements); - - /* The heading (H1-H6) elements */ - for (i = 0; i < headings.length; i++) { - name = headings[i]; - td = new HtmlTagDesc (name, true, true, inlineContent, bigAttrs); - stSupportedElements.put (name, td); - } - - addPreElement (stSupportedElements); - addQElement (stSupportedElements); - addBlockquoteElement (stSupportedElements); - addInsElement (stSupportedElements); - addDelElement (stSupportedElements); - - addDlElement (stSupportedElements); - addDtElement (stSupportedElements); - addDdElement (stSupportedElements); - - addOlElement (stSupportedElements); - addUlElement (stSupportedElements); - addLiElement (stSupportedElements); - addFormElement (stSupportedElements); - addLabelElement (stSupportedElements); - - addInputElement (stSupportedElements); - addSelectElement (stSupportedElements); - addOptgroupElement (stSupportedElements); - addOptionElement (stSupportedElements); - addTextareaElement (stSupportedElements); - addFieldsetElement (stSupportedElements); - addLegendElement (stSupportedElements); - addButtonElement (stSupportedElements); - addTableElement (stSupportedElements); - - List cellalignAttrs = new ArrayList (4); // combine cellhalign and cellvalign - cellalignAttrs.add (new HtmlAttributeDesc ("align", - new String[] {"left", "center", "right", "justify", "char" }, - HtmlAttributeDesc.IMPLIED)); - addSimpleAttribute (cellalignAttrs, "char"); - addSimpleAttribute (cellalignAttrs, "charoff"); - addTheadElement (stSupportedElements, cellalignAttrs); - addTfootElement (stSupportedElements, cellalignAttrs); - addTbodyElement (stSupportedElements, cellalignAttrs); - addTrElement (stSupportedElements); - - addThElement (stSupportedElements); - addTdElement (stSupportedElements); - addCaptionElement - (stSupportedElements, inlineContent, valignAtt); - - addColgroupElement (stSupportedElements, cellalignAttrs); - addColElement (stSupportedElements, cellalignAttrs); - - addHeadElement (stSupportedElements); - addTitleElement (stSupportedElements); - addBaseElement (stSupportedElements); - addMetaElement (stSupportedElements); - addScriptElement (stSupportedElements); - addNoscriptElement (stSupportedElements); - addStyleElement (stSupportedElements); - - /* The HTML element */ - name = "html"; - List htmlContent = new ArrayList (2); - htmlContent.add ("head"); - htmlContent.add ("body"); - td = new HtmlTagDesc (name, false, false, htmlContent, i18nAttrs); - stSupportedElements.put (name, td); - } - - - /** - * Constructor. - * Most of the initialization work is done in a static code - * block rather than in the constructor, so as to minimize - * overhead on multiple invocations. - */ - public Html4_0StrictDocDesc () - { - super(); - // publish stSupportedElements to superclass - supportedElements = stSupportedElements; - init (); - } - - - private static void addFormElement (Map stSupportedElements) - { - final String name = "form"; - List atts = new ArrayList (bigAttrs.size () + 8); - atts.addAll (bigAttrs); - addSimpleAttribute (atts, "action"); - addSimpleAttribute (atts, "method"); - addSimpleAttribute (atts, "enctype"); - addSimpleAttribute (atts, "onsubmit"); - addSimpleAttribute (atts, "onreset"); - addSimpleAttribute (atts, "accept-charset"); - List formContent = new ArrayList (blockContent.size ()); - formContent.addAll (blockContent); - formContent.add ("script"); - removeStringsFromList (formContent, new String[] { "form" }); - HtmlTagDesc td = new HtmlTagDesc (name, true, true, formContent, atts); - stSupportedElements.put (name, td); + addAreaElement(stSupportedElements, shapeAtt); + addLinkElement(stSupportedElements); + addImgElement(stSupportedElements); + addObjectElement(stSupportedElements); + addParamElement(stSupportedElements); + addHrElement(stSupportedElements); + addPElement(stSupportedElements); + + /* The heading (H1-H6) elements */ + for (i = 0; i < headings.length; i++) { + name = headings[i]; + td = new HtmlTagDesc(name, true, true, inlineContent, bigAttrs); + stSupportedElements.put(name, td); } - private static void addHrElement - (Map stSupportedElements) - { - String name = "hr"; - List atts = new ArrayList (coreAttrs.size () + eventAttrs.size ()); - atts.addAll (coreAttrs); - atts.addAll (eventAttrs); - HtmlTagDesc td = new HtmlTagDesc (name, true, false, null, atts); - stSupportedElements.put (name, td); - } - - private static void addImgElement (Map stSupportedElements) - { - String name = "img"; - List atts = new ArrayList (bigAttrs.size () + 10); - atts.addAll (bigAttrs); - addRequiredAttribute (atts, "src"); - addRequiredAttribute (atts, "alt"); - addSimpleAttribute (atts, "longdesc"); - addSimpleAttribute (atts, "height"); - addSimpleAttribute (atts, "width"); - addSimpleAttribute (atts, "usemap"); - addSelfAttribute (atts, "ismap"); - HtmlTagDesc td = new HtmlTagDesc (name, true, false, null, atts); - stSupportedElements.put (name, td); - } - - private static void addInputElement - (Map stSupportedElements) - { - final String name = "input"; - List atts = new ArrayList (biggerAttrs.size () + 20); - atts.addAll (biggerAttrs); - atts.add (new HtmlAttributeDesc ("type", - new String[] {"text", "password", "checkbox", "radio", "submit", - "reset", "file", "hidden", "image", "button"}, + addPreElement(stSupportedElements); + addQElement(stSupportedElements); + addBlockquoteElement(stSupportedElements); + addInsElement(stSupportedElements); + addDelElement(stSupportedElements); + + addDlElement(stSupportedElements); + addDtElement(stSupportedElements); + addDdElement(stSupportedElements); + + addOlElement(stSupportedElements); + addUlElement(stSupportedElements); + addLiElement(stSupportedElements); + addFormElement(stSupportedElements); + addLabelElement(stSupportedElements); + + addInputElement(stSupportedElements); + addSelectElement(stSupportedElements); + addOptgroupElement(stSupportedElements); + addOptionElement(stSupportedElements); + addTextareaElement(stSupportedElements); + addFieldsetElement(stSupportedElements); + addLegendElement(stSupportedElements); + addButtonElement(stSupportedElements); + addTableElement(stSupportedElements); + + List cellalignAttrs = new ArrayList(4); // combine cellhalign and cellvalign + cellalignAttrs.add( + new HtmlAttributeDesc( + "align", + new String[] {"left", "center", "right", "justify", "char"}, + HtmlAttributeDesc.IMPLIED)); + addSimpleAttribute(cellalignAttrs, "char"); + addSimpleAttribute(cellalignAttrs, "charoff"); + addTheadElement(stSupportedElements, cellalignAttrs); + addTfootElement(stSupportedElements, cellalignAttrs); + addTbodyElement(stSupportedElements, cellalignAttrs); + addTrElement(stSupportedElements); + + addThElement(stSupportedElements); + addTdElement(stSupportedElements); + addCaptionElement(stSupportedElements, inlineContent, valignAtt); + + addColgroupElement(stSupportedElements, cellalignAttrs); + addColElement(stSupportedElements, cellalignAttrs); + + addHeadElement(stSupportedElements); + addTitleElement(stSupportedElements); + addBaseElement(stSupportedElements); + addMetaElement(stSupportedElements); + addScriptElement(stSupportedElements); + addNoscriptElement(stSupportedElements); + addStyleElement(stSupportedElements); + + /* The HTML element */ + name = "html"; + List htmlContent = new ArrayList(2); + htmlContent.add("head"); + htmlContent.add("body"); + td = new HtmlTagDesc(name, false, false, htmlContent, i18nAttrs); + stSupportedElements.put(name, td); + } + + /** + * Constructor. Most of the initialization work is done in a static code block rather than in the + * constructor, so as to minimize overhead on multiple invocations. + */ + public Html4_0StrictDocDesc() { + super(); + // publish stSupportedElements to superclass + supportedElements = stSupportedElements; + init(); + } + + private static void addFormElement(Map stSupportedElements) { + final String name = "form"; + List atts = new ArrayList(bigAttrs.size() + 8); + atts.addAll(bigAttrs); + addSimpleAttribute(atts, "action"); + addSimpleAttribute(atts, "method"); + addSimpleAttribute(atts, "enctype"); + addSimpleAttribute(atts, "onsubmit"); + addSimpleAttribute(atts, "onreset"); + addSimpleAttribute(atts, "accept-charset"); + List formContent = new ArrayList(blockContent.size()); + formContent.addAll(blockContent); + formContent.add("script"); + removeStringsFromList(formContent, new String[] {"form"}); + HtmlTagDesc td = new HtmlTagDesc(name, true, true, formContent, atts); + stSupportedElements.put(name, td); + } + + private static void addHrElement(Map stSupportedElements) { + String name = "hr"; + List atts = new ArrayList(coreAttrs.size() + eventAttrs.size()); + atts.addAll(coreAttrs); + atts.addAll(eventAttrs); + HtmlTagDesc td = new HtmlTagDesc(name, true, false, null, atts); + stSupportedElements.put(name, td); + } + + private static void addImgElement(Map stSupportedElements) { + String name = "img"; + List atts = new ArrayList(bigAttrs.size() + 10); + atts.addAll(bigAttrs); + addRequiredAttribute(atts, "src"); + addRequiredAttribute(atts, "alt"); + addSimpleAttribute(atts, "longdesc"); + addSimpleAttribute(atts, "height"); + addSimpleAttribute(atts, "width"); + addSimpleAttribute(atts, "usemap"); + addSelfAttribute(atts, "ismap"); + HtmlTagDesc td = new HtmlTagDesc(name, true, false, null, atts); + stSupportedElements.put(name, td); + } + + private static void addInputElement(Map stSupportedElements) { + final String name = "input"; + List atts = new ArrayList(biggerAttrs.size() + 20); + atts.addAll(biggerAttrs); + atts.add( + new HtmlAttributeDesc( + "type", + new String[] { + "text", + "password", + "checkbox", + "radio", + "submit", + "reset", + "file", + "hidden", + "image", + "button" + }, HtmlAttributeDesc.OTHER)); - addSimpleAttribute (atts, "name"); - addSimpleAttribute (atts, "value"); - addSelfAttribute (atts, "checked"); - addSelfAttribute (atts, "disabled"); - addSelfAttribute (atts, "readonly"); - addSimpleAttribute (atts, "size"); - addSimpleAttribute (atts, "maxlength"); - addSimpleAttribute (atts, "src"); - addSimpleAttribute (atts, "alt"); - addSimpleAttribute (atts, "usemap"); - addSimpleAttribute (atts, "tabindex"); - addSimpleAttribute (atts, "accesskey"); - addSimpleAttribute (atts, "onfocus"); - addSimpleAttribute (atts, "onblur"); - addSimpleAttribute (atts, "onselect"); - addSimpleAttribute (atts, "onchange"); - addSimpleAttribute (atts, "accept"); - HtmlTagDesc td = new HtmlTagDesc (name, true, true, null, atts); - stSupportedElements.put (name, td); - } - - + addSimpleAttribute(atts, "name"); + addSimpleAttribute(atts, "value"); + addSelfAttribute(atts, "checked"); + addSelfAttribute(atts, "disabled"); + addSelfAttribute(atts, "readonly"); + addSimpleAttribute(atts, "size"); + addSimpleAttribute(atts, "maxlength"); + addSimpleAttribute(atts, "src"); + addSimpleAttribute(atts, "alt"); + addSimpleAttribute(atts, "usemap"); + addSimpleAttribute(atts, "tabindex"); + addSimpleAttribute(atts, "accesskey"); + addSimpleAttribute(atts, "onfocus"); + addSimpleAttribute(atts, "onblur"); + addSimpleAttribute(atts, "onselect"); + addSimpleAttribute(atts, "onchange"); + addSimpleAttribute(atts, "accept"); + HtmlTagDesc td = new HtmlTagDesc(name, true, true, null, atts); + stSupportedElements.put(name, td); + } } diff --git a/jhove-modules/html-hul/src/main/java/edu/harvard/hul/ois/jhove/module/html/Html4_0TFDocDesc.java b/jhove-modules/html-hul/src/main/java/edu/harvard/hul/ois/jhove/module/html/Html4_0TFDocDesc.java index 4c92e6bc0..04f546d51 100644 --- a/jhove-modules/html-hul/src/main/java/edu/harvard/hul/ois/jhove/module/html/Html4_0TFDocDesc.java +++ b/jhove-modules/html-hul/src/main/java/edu/harvard/hul/ois/jhove/module/html/Html4_0TFDocDesc.java @@ -1,201 +1,203 @@ -/********************************************************************** - * Jhove - JSTOR/Harvard Object Validation Environment - * Copyright 2004 by JSTOR and the President and Fellows of Harvard College - **********************************************************************/ - +/** + * ******************************************************************** Jhove - JSTOR/Harvard Object + * Validation Environment Copyright 2004 by JSTOR and the President and Fellows of Harvard College + * ******************************************************************** + */ package edu.harvard.hul.ois.jhove.module.html; import edu.harvard.hul.ois.jhove.module.HtmlModule; import java.util.*; + /** - * Abstract class for the HTML 4.0 Transitional and Frameset document - * types. These are identical except for one element apiece, so nearly - * all the code is here or in its superclasses. - * - * @author Gary McGath + * Abstract class for the HTML 4.0 Transitional and Frameset document types. These are identical + * except for one element apiece, so nearly all the code is here or in its superclasses. * + * @author Gary McGath */ public abstract class Html4_0TFDocDesc extends Html4TFDocDesc { - protected Html4_0TFDocDesc() { - super(); + protected Html4_0TFDocDesc() { + super(); + } + + protected static void classInit4(Map stSupportedElements, int version) { + Html4TFDocDesc.classInit4(stSupportedElements); + int i; + String name; + HtmlTagDesc td; + + for (i = 0; i < fontMarkup.length; i++) { + name = fontMarkup[i]; + td = new HtmlTagDesc(name, true, true, inlineContent, bigAttrs); + stSupportedElements.put(name, td); } - protected static void classInit4 (Map stSupportedElements, int version) - { - Html4TFDocDesc.classInit4(stSupportedElements); - int i; - String name; - HtmlTagDesc td; - - for (i = 0; i < fontMarkup.length; i++) { - name = fontMarkup[i]; - td = new HtmlTagDesc (name, true, true, inlineContent, bigAttrs); - stSupportedElements.put (name, td); - } - - /* Phrase elements. */ - for (i = 0; i < phraseMarkup.length; i++) { - name = phraseMarkup[i]; - td = new HtmlTagDesc (name, true, true, inlineContent, bigAttrs); - stSupportedElements.put (name, td); - } - - addSupElement (stSupportedElements); - addSubElement (stSupportedElements); - addSpanElement (stSupportedElements); - addBdoElement (stSupportedElements); - addBasefontElement (stSupportedElements); - addFontElement (stSupportedElements); - addBrElement (stSupportedElements, coreAttrs); - - addAddressElement (stSupportedElements); - addDivElement (stSupportedElements); - addCenterElement (stSupportedElements); - addAElement (stSupportedElements); - addMapElement (stSupportedElements); - - HtmlAttributeDesc shapeAtt = new HtmlAttributeDesc ("shape", - new String[] {"rect", "circle", "poly", "default" }, - HtmlAttributeDesc.REQUIRED); - addAreaElement (stSupportedElements, shapeAtt); - addLinkElement (stSupportedElements); - addImgElement (stSupportedElements); - addObjectElement (stSupportedElements); - addParamElement (stSupportedElements); - HtmlAttributeDesc ialignAtt = new HtmlAttributeDesc ("align", - new String[] { "top", "middle", "bottom", "left", "right" }, - HtmlAttributeDesc.IMPLIED); - addAppletElement (stSupportedElements, ialignAtt); - addHrElement (stSupportedElements); - addPElement (stSupportedElements); - - /* The heading (H1-H6) elements */ - for (i = 0; i < headings.length; i++) { - name = headings[i]; - td = new HtmlTagDesc (name, true, true, inlineContent, bigAttrs); - stSupportedElements.put (name, td); - } - - addPreElement (stSupportedElements); - addQElement (stSupportedElements); - addBlockquoteElement (stSupportedElements); - addInsElement (stSupportedElements); - addDelElement (stSupportedElements); - - addDlElement (stSupportedElements); - addDtElement (stSupportedElements); - addDdElement (stSupportedElements); - - addOlElement (stSupportedElements); - addUlElement (stSupportedElements); - addDirElement (stSupportedElements); - addLiElement (stSupportedElements); - addFormElement (stSupportedElements); - addLabelElement (stSupportedElements); - - addInputElement (stSupportedElements); - addSelectElement (stSupportedElements); - addOptgroupElement (stSupportedElements); - addOptionElement (stSupportedElements); - addTextareaElement (stSupportedElements); - addFieldsetElement (stSupportedElements); - addLegendElement (stSupportedElements); - addButtonElement (stSupportedElements); - addTableElement (stSupportedElements); - - HtmlAttributeDesc valignAtt = - new HtmlAttributeDesc ("valign", - new String[] { "top", "middle", "bottom", "baseline" }, - HtmlAttributeDesc.IMPLIED); - List cellalignAttrs = new ArrayList (4); // combine cellhalign and cellvalign - cellalignAttrs.add (new HtmlAttributeDesc ("align", - new String[] {"left", "center", "right", "justify", "char" }, - HtmlAttributeDesc.IMPLIED)); - addSimpleAttribute (cellalignAttrs, "char"); - addSimpleAttribute (cellalignAttrs, "charoff"); - cellalignAttrs.add (valignAtt); - addTheadElement (stSupportedElements, cellalignAttrs); - addTfootElement (stSupportedElements, cellalignAttrs); - addTbodyElement (stSupportedElements, cellalignAttrs); - addTrElement (stSupportedElements); - - - addThElement (stSupportedElements); - addTdElement (stSupportedElements); - addCaptionElement - (stSupportedElements, inlineContent, valignAtt); - - addColgroupElement (stSupportedElements, cellalignAttrs); - addColElement (stSupportedElements, cellalignAttrs); - - addHeadElement (stSupportedElements); - addTitleElement (stSupportedElements); - addBaseElement (stSupportedElements); - addMetaElement (stSupportedElements); - addScriptElement (stSupportedElements); - addNoscriptElement (stSupportedElements); - addStyleElement (stSupportedElements); - - /* The HTML element */ - name = "html"; - List htmlContent = new ArrayList (2); - htmlContent.add ("head"); - if (version == HtmlModule.HTML_4_0_FRAMESET) { - htmlContent.add ("frameset"); - } - else { - htmlContent.add ("body"); - } - td = new HtmlTagDesc (name, false, false, htmlContent, i18nAttrs); - stSupportedElements.put (name, td); - addNoframesElement (stSupportedElements, version); - if (version == HtmlModule.HTML_4_0_FRAMESET) { - addFramesetElement (stSupportedElements); - addFrameElement (stSupportedElements); - } - addBodyElement (stSupportedElements); + /* Phrase elements. */ + for (i = 0; i < phraseMarkup.length; i++) { + name = phraseMarkup[i]; + td = new HtmlTagDesc(name, true, true, inlineContent, bigAttrs); + stSupportedElements.put(name, td); } - private static void addFormElement (Map stSupportedElements) - { - final String name = "form"; - List atts = new ArrayList (bigAttrs.size () + 9); - atts.addAll (bigAttrs); - addSimpleAttribute (atts, "action"); - addSimpleAttribute (atts, "method"); - addSimpleAttribute (atts, "enctype"); - addSimpleAttribute (atts, "onsubmit"); - addSimpleAttribute (atts, "onreset"); - addSimpleAttribute (atts, "target"); - addSimpleAttribute (atts, "accept-charset"); - List formContent = new ArrayList (blockContent.size ()); - formContent.addAll (blockContent); - formContent.add ("script"); - removeStringsFromList (formContent, new String[] { "form" }); - HtmlTagDesc td = new HtmlTagDesc (name, true, true, formContent, atts); - stSupportedElements.put (name, td); + addSupElement(stSupportedElements); + addSubElement(stSupportedElements); + addSpanElement(stSupportedElements); + addBdoElement(stSupportedElements); + addBasefontElement(stSupportedElements); + addFontElement(stSupportedElements); + addBrElement(stSupportedElements, coreAttrs); + + addAddressElement(stSupportedElements); + addDivElement(stSupportedElements); + addCenterElement(stSupportedElements); + addAElement(stSupportedElements); + addMapElement(stSupportedElements); + + HtmlAttributeDesc shapeAtt = + new HtmlAttributeDesc( + "shape", + new String[] {"rect", "circle", "poly", "default"}, + HtmlAttributeDesc.REQUIRED); + addAreaElement(stSupportedElements, shapeAtt); + addLinkElement(stSupportedElements); + addImgElement(stSupportedElements); + addObjectElement(stSupportedElements); + addParamElement(stSupportedElements); + HtmlAttributeDesc ialignAtt = + new HtmlAttributeDesc( + "align", + new String[] {"top", "middle", "bottom", "left", "right"}, + HtmlAttributeDesc.IMPLIED); + addAppletElement(stSupportedElements, ialignAtt); + addHrElement(stSupportedElements); + addPElement(stSupportedElements); + + /* The heading (H1-H6) elements */ + for (i = 0; i < headings.length; i++) { + name = headings[i]; + td = new HtmlTagDesc(name, true, true, inlineContent, bigAttrs); + stSupportedElements.put(name, td); } - private static void addImgElement (Map stSupportedElements) - { - String name = "img"; - List atts = new ArrayList (bigAttrs.size () + 10); - atts.addAll (bigAttrs); - addRequiredAttribute (atts, "src"); - addRequiredAttribute (atts, "alt"); - addSimpleAttribute (atts, "longdesc"); - addSimpleAttribute (atts, "height"); - addSimpleAttribute (atts, "width"); - addSimpleAttribute (atts, "usemap"); - addSelfAttribute (atts, "ismap"); - atts.add (new HtmlAttributeDesc ("align", - new String[] { "top", "middle", "bottom", "left", "right" }, + addPreElement(stSupportedElements); + addQElement(stSupportedElements); + addBlockquoteElement(stSupportedElements); + addInsElement(stSupportedElements); + addDelElement(stSupportedElements); + + addDlElement(stSupportedElements); + addDtElement(stSupportedElements); + addDdElement(stSupportedElements); + + addOlElement(stSupportedElements); + addUlElement(stSupportedElements); + addDirElement(stSupportedElements); + addLiElement(stSupportedElements); + addFormElement(stSupportedElements); + addLabelElement(stSupportedElements); + + addInputElement(stSupportedElements); + addSelectElement(stSupportedElements); + addOptgroupElement(stSupportedElements); + addOptionElement(stSupportedElements); + addTextareaElement(stSupportedElements); + addFieldsetElement(stSupportedElements); + addLegendElement(stSupportedElements); + addButtonElement(stSupportedElements); + addTableElement(stSupportedElements); + + HtmlAttributeDesc valignAtt = + new HtmlAttributeDesc( + "valign", + new String[] {"top", "middle", "bottom", "baseline"}, + HtmlAttributeDesc.IMPLIED); + List cellalignAttrs = new ArrayList(4); // combine cellhalign and cellvalign + cellalignAttrs.add( + new HtmlAttributeDesc( + "align", + new String[] {"left", "center", "right", "justify", "char"}, HtmlAttributeDesc.IMPLIED)); - addSimpleAttribute (atts, "border"); - addSimpleAttribute (atts, "hspace"); - addSimpleAttribute (atts, "vspace"); - HtmlTagDesc td = new HtmlTagDesc (name, true, false, null, atts); - stSupportedElements.put (name, td); + addSimpleAttribute(cellalignAttrs, "char"); + addSimpleAttribute(cellalignAttrs, "charoff"); + cellalignAttrs.add(valignAtt); + addTheadElement(stSupportedElements, cellalignAttrs); + addTfootElement(stSupportedElements, cellalignAttrs); + addTbodyElement(stSupportedElements, cellalignAttrs); + addTrElement(stSupportedElements); + + addThElement(stSupportedElements); + addTdElement(stSupportedElements); + addCaptionElement(stSupportedElements, inlineContent, valignAtt); + + addColgroupElement(stSupportedElements, cellalignAttrs); + addColElement(stSupportedElements, cellalignAttrs); + + addHeadElement(stSupportedElements); + addTitleElement(stSupportedElements); + addBaseElement(stSupportedElements); + addMetaElement(stSupportedElements); + addScriptElement(stSupportedElements); + addNoscriptElement(stSupportedElements); + addStyleElement(stSupportedElements); + + /* The HTML element */ + name = "html"; + List htmlContent = new ArrayList(2); + htmlContent.add("head"); + if (version == HtmlModule.HTML_4_0_FRAMESET) { + htmlContent.add("frameset"); + } else { + htmlContent.add("body"); + } + td = new HtmlTagDesc(name, false, false, htmlContent, i18nAttrs); + stSupportedElements.put(name, td); + addNoframesElement(stSupportedElements, version); + if (version == HtmlModule.HTML_4_0_FRAMESET) { + addFramesetElement(stSupportedElements); + addFrameElement(stSupportedElements); } + addBodyElement(stSupportedElements); + } + + private static void addFormElement(Map stSupportedElements) { + final String name = "form"; + List atts = new ArrayList(bigAttrs.size() + 9); + atts.addAll(bigAttrs); + addSimpleAttribute(atts, "action"); + addSimpleAttribute(atts, "method"); + addSimpleAttribute(atts, "enctype"); + addSimpleAttribute(atts, "onsubmit"); + addSimpleAttribute(atts, "onreset"); + addSimpleAttribute(atts, "target"); + addSimpleAttribute(atts, "accept-charset"); + List formContent = new ArrayList(blockContent.size()); + formContent.addAll(blockContent); + formContent.add("script"); + removeStringsFromList(formContent, new String[] {"form"}); + HtmlTagDesc td = new HtmlTagDesc(name, true, true, formContent, atts); + stSupportedElements.put(name, td); + } + + private static void addImgElement(Map stSupportedElements) { + String name = "img"; + List atts = new ArrayList(bigAttrs.size() + 10); + atts.addAll(bigAttrs); + addRequiredAttribute(atts, "src"); + addRequiredAttribute(atts, "alt"); + addSimpleAttribute(atts, "longdesc"); + addSimpleAttribute(atts, "height"); + addSimpleAttribute(atts, "width"); + addSimpleAttribute(atts, "usemap"); + addSelfAttribute(atts, "ismap"); + atts.add( + new HtmlAttributeDesc( + "align", + new String[] {"top", "middle", "bottom", "left", "right"}, + HtmlAttributeDesc.IMPLIED)); + addSimpleAttribute(atts, "border"); + addSimpleAttribute(atts, "hspace"); + addSimpleAttribute(atts, "vspace"); + HtmlTagDesc td = new HtmlTagDesc(name, true, false, null, atts); + stSupportedElements.put(name, td); + } } diff --git a/jhove-modules/html-hul/src/main/java/edu/harvard/hul/ois/jhove/module/html/Html4_0TransDocDesc.java b/jhove-modules/html-hul/src/main/java/edu/harvard/hul/ois/jhove/module/html/Html4_0TransDocDesc.java index aeaabc895..e26afb706 100644 --- a/jhove-modules/html-hul/src/main/java/edu/harvard/hul/ois/jhove/module/html/Html4_0TransDocDesc.java +++ b/jhove-modules/html-hul/src/main/java/edu/harvard/hul/ois/jhove/module/html/Html4_0TransDocDesc.java @@ -1,8 +1,8 @@ -/********************************************************************** - * Jhove - JSTOR/Harvard Object Validation Environment - * Copyright 2004 by JSTOR and the President and Fellows of Harvard College - **********************************************************************/ - +/** + * ******************************************************************** Jhove - JSTOR/Harvard Object + * Validation Environment Copyright 2004 by JSTOR and the President and Fellows of Harvard College + * ******************************************************************** + */ package edu.harvard.hul.ois.jhove.module.html; import edu.harvard.hul.ois.jhove.module.HtmlModule; @@ -10,37 +10,30 @@ /** * This class describes the requirements of an HTML 4.0 Transitional document. - * - * @author Gary McGath * + * @author Gary McGath */ public class Html4_0TransDocDesc extends Html4_0TFDocDesc { - /* Static, private map of supported tags. - * For efficiency, we create a static Map - * of supported tags just once, then assign that to stSupportedElements - * in the constructor. */ - private static Map stSupportedElements; - - { - stSupportedElements = new HashMap (280); - Html4_0TFDocDesc.classInit4 - (stSupportedElements, HtmlModule.HTML_4_0_TRANSITIONAL); - - } - - /** - * Constructor. - * Most of the initialization work is done in a static code - * block rather than in the constructor, so as to minimize - * overhead on multiple invocations. - */ - public Html4_0TransDocDesc () - { - super(); - // publish stSupportedElements to superclass - supportedElements = stSupportedElements; - init (); - } - + /* Static, private map of supported tags. + * For efficiency, we create a static Map + * of supported tags just once, then assign that to stSupportedElements + * in the constructor. */ + private static Map stSupportedElements; + + { + stSupportedElements = new HashMap(280); + Html4_0TFDocDesc.classInit4(stSupportedElements, HtmlModule.HTML_4_0_TRANSITIONAL); + } + + /** + * Constructor. Most of the initialization work is done in a static code block rather than in the + * constructor, so as to minimize overhead on multiple invocations. + */ + public Html4_0TransDocDesc() { + super(); + // publish stSupportedElements to superclass + supportedElements = stSupportedElements; + init(); + } } diff --git a/jhove-modules/html-hul/src/main/java/edu/harvard/hul/ois/jhove/module/html/HtmlAttributeDesc.java b/jhove-modules/html-hul/src/main/java/edu/harvard/hul/ois/jhove/module/html/HtmlAttributeDesc.java index 395e4023a..13f188d67 100644 --- a/jhove-modules/html-hul/src/main/java/edu/harvard/hul/ois/jhove/module/html/HtmlAttributeDesc.java +++ b/jhove-modules/html-hul/src/main/java/edu/harvard/hul/ois/jhove/module/html/HtmlAttributeDesc.java @@ -1,108 +1,84 @@ -/********************************************************************** - * Jhove - JSTOR/Harvard Object Validation Environment - * Copyright 2004 by JSTOR and the President and Fellows of Harvard College - **********************************************************************/ - +/** + * ******************************************************************** Jhove - JSTOR/Harvard Object + * Validation Environment Copyright 2004 by JSTOR and the President and Fellows of Harvard College + * ******************************************************************** + */ package edu.harvard.hul.ois.jhove.module.html; -//import java.util.*; +// import java.util.*; /** * Class representing an abstract attribute of an HTML element. * * @author Gary McGath - * */ public class HtmlAttributeDesc { - - /** Permitted values for _kind */ - public final static int - REQUIRED = 1, // #REQUIRED - CURRENT = 2, // #CURRENT - CONREF = 3, // #CONREF - IMPLIED = 4, // #IMPLIED - OTHER = 5; // Explicit default - - private String _name; - private int _kind; - private String[] _permittedValues; - - - /** - * Constructor. - * - * @param name The name of the attribute. Must be lower case. - * @param permittedValues Specific values allowed for the parameter. If - * null, then any CDATA value is allowed. - * @param kind The kind of parameter. Must be REQUIRED, CURRENT, - * CONREF, or IMPLIED. - */ - public HtmlAttributeDesc (String name, - String[] permittedValues, - int kind) - { - _name = name; - _permittedValues = permittedValues; - _kind = kind; - } - - /** - * Constructor for an attribute that can take any value, with - * kind defaulting to IMPLIED. */ - public HtmlAttributeDesc (String name) - { - _name = name; - _permittedValues = null; - _kind = IMPLIED; - } - - /** - * Returns the attribute's name. - */ - public String getName () - { - return _name; - } - - - /** Returns true if this tag's name - * matches the parameter. */ - public boolean nameMatches(String name) - { - return _name.equals (name); - } - - - /** Returns true if the parameter is a permissible - * value for the attribute. - */ - public boolean valueOK (String name, String value) - { - if (_permittedValues == null) { - return true; - } - else if (value == null) { - // An attribute without a value is permitted only when - // there is only one legal value, and that equals the - // attribute's name. - - return _permittedValues.length == 1 && - _permittedValues[0].equals (name); - } - else { - value = value.toLowerCase (); - for (int i = 0; i < _permittedValues.length; i++) { - if (_permittedValues[i].equals (value)) { - return true; - } - } - return false; // No match + + /** Permitted values for _kind */ + public static final int REQUIRED = 1, // #REQUIRED + CURRENT = 2, // #CURRENT + CONREF = 3, // #CONREF + IMPLIED = 4, // #IMPLIED + OTHER = 5; // Explicit default + + private String _name; + private int _kind; + private String[] _permittedValues; + + /** + * Constructor. + * + * @param name The name of the attribute. Must be lower case. + * @param permittedValues Specific values allowed for the parameter. If null, then any CDATA value + * is allowed. + * @param kind The kind of parameter. Must be REQUIRED, CURRENT, CONREF, or IMPLIED. + */ + public HtmlAttributeDesc(String name, String[] permittedValues, int kind) { + _name = name; + _permittedValues = permittedValues; + _kind = kind; + } + + /** Constructor for an attribute that can take any value, with kind defaulting to IMPLIED. */ + public HtmlAttributeDesc(String name) { + _name = name; + _permittedValues = null; + _kind = IMPLIED; + } + + /** Returns the attribute's name. */ + public String getName() { + return _name; + } + + /** Returns true if this tag's name matches the parameter. */ + public boolean nameMatches(String name) { + return _name.equals(name); + } + + /** Returns true if the parameter is a permissible value for the attribute. */ + public boolean valueOK(String name, String value) { + if (_permittedValues == null) { + return true; + } else if (value == null) { + // An attribute without a value is permitted only when + // there is only one legal value, and that equals the + // attribute's name. + + return _permittedValues.length == 1 && _permittedValues[0].equals(name); + } else { + value = value.toLowerCase(); + for (int i = 0; i < _permittedValues.length; i++) { + if (_permittedValues[i].equals(value)) { + return true; } + } + return false; // No match } - - /** Return true if the attribute is required. */ - public boolean isRequired () - { - return _kind == REQUIRED; - } + } + + /** Return true if the attribute is required. */ + public boolean isRequired() { + return _kind == REQUIRED; + } } diff --git a/jhove-modules/html-hul/src/main/java/edu/harvard/hul/ois/jhove/module/html/HtmlCharStream.java b/jhove-modules/html-hul/src/main/java/edu/harvard/hul/ois/jhove/module/html/HtmlCharStream.java index e0f1e8202..9e214dc2a 100644 --- a/jhove-modules/html-hul/src/main/java/edu/harvard/hul/ois/jhove/module/html/HtmlCharStream.java +++ b/jhove-modules/html-hul/src/main/java/edu/harvard/hul/ois/jhove/module/html/HtmlCharStream.java @@ -4,7 +4,7 @@ * ISO 8859-1. One of the main reasons for this is that some ISO 8859 * characters look like illegal characters in Unicode, making PCDATA * appear invalid just because it has funny characters. */ - + /* 24-Oct-04 GDM: Modified readChar to circumvent a bug which causes * infinite loops when reading binary files (which are never HTML) */ package edu.harvard.hul.ois.jhove.module.html; @@ -12,12 +12,10 @@ import java.io.UnsupportedEncodingException; /** - * An implementation of interface CharStream, where the stream is assumed to - * contain only ASCII characters (without unicode processing). + * An implementation of interface CharStream, where the stream is assumed to contain only ASCII + * characters (without unicode processing). */ - -public class HtmlCharStream implements CharStream -{ +public class HtmlCharStream implements CharStream { public static final boolean staticFlag = false; int bufsize; int available; @@ -36,251 +34,206 @@ public class HtmlCharStream implements CharStream protected boolean _lineEndCR; protected boolean _lineEndLF; protected boolean _lineEndCRLF; - + protected java.io.Reader inputStream; protected char[] buffer; protected int maxNextCharInd = 0; protected int inBuf = 0; - protected void ExpandBuff(boolean wrapAround) - { - char[] newbuffer = new char[bufsize + 2048]; - int newbufline[] = new int[bufsize + 2048]; - int newbufcolumn[] = new int[bufsize + 2048]; - - try - { - if (wrapAround) - { - System.arraycopy(buffer, tokenBegin, newbuffer, 0, bufsize - tokenBegin); - System.arraycopy(buffer, 0, newbuffer, - bufsize - tokenBegin, bufpos); - buffer = newbuffer; - - System.arraycopy(bufline, tokenBegin, newbufline, 0, bufsize - tokenBegin); - System.arraycopy(bufline, 0, newbufline, bufsize - tokenBegin, bufpos); - bufline = newbufline; - - System.arraycopy(bufcolumn, tokenBegin, newbufcolumn, 0, bufsize - tokenBegin); - System.arraycopy(bufcolumn, 0, newbufcolumn, bufsize - tokenBegin, bufpos); - bufcolumn = newbufcolumn; - - maxNextCharInd = (bufpos += (bufsize - tokenBegin)); - } - else - { - System.arraycopy(buffer, tokenBegin, newbuffer, 0, bufsize - tokenBegin); - buffer = newbuffer; - - System.arraycopy(bufline, tokenBegin, newbufline, 0, bufsize - tokenBegin); - bufline = newbufline; - - System.arraycopy(bufcolumn, tokenBegin, newbufcolumn, 0, bufsize - tokenBegin); - bufcolumn = newbufcolumn; - - maxNextCharInd = (bufpos -= tokenBegin); - } - } - catch (Throwable t) - { - throw new Error(t.getMessage()); - } - - - bufsize += 2048; - available = bufsize; - tokenBegin = 0; + protected void ExpandBuff(boolean wrapAround) { + char[] newbuffer = new char[bufsize + 2048]; + int newbufline[] = new int[bufsize + 2048]; + int newbufcolumn[] = new int[bufsize + 2048]; + + try { + if (wrapAround) { + System.arraycopy(buffer, tokenBegin, newbuffer, 0, bufsize - tokenBegin); + System.arraycopy(buffer, 0, newbuffer, bufsize - tokenBegin, bufpos); + buffer = newbuffer; + + System.arraycopy(bufline, tokenBegin, newbufline, 0, bufsize - tokenBegin); + System.arraycopy(bufline, 0, newbufline, bufsize - tokenBegin, bufpos); + bufline = newbufline; + + System.arraycopy(bufcolumn, tokenBegin, newbufcolumn, 0, bufsize - tokenBegin); + System.arraycopy(bufcolumn, 0, newbufcolumn, bufsize - tokenBegin, bufpos); + bufcolumn = newbufcolumn; + + maxNextCharInd = (bufpos += (bufsize - tokenBegin)); + } else { + System.arraycopy(buffer, tokenBegin, newbuffer, 0, bufsize - tokenBegin); + buffer = newbuffer; + + System.arraycopy(bufline, tokenBegin, newbufline, 0, bufsize - tokenBegin); + bufline = newbufline; + + System.arraycopy(bufcolumn, tokenBegin, newbufcolumn, 0, bufsize - tokenBegin); + bufcolumn = newbufcolumn; + + maxNextCharInd = (bufpos -= tokenBegin); + } + } catch (Throwable t) { + throw new Error(t.getMessage()); + } + + bufsize += 2048; + available = bufsize; + tokenBegin = 0; } - protected void FillBuff() throws java.io.IOException - { - if (maxNextCharInd == available) - { - if (available == bufsize) - { - if (tokenBegin > 2048) - { - bufpos = maxNextCharInd = 0; - available = tokenBegin; - } - else if (tokenBegin < 0) - bufpos = maxNextCharInd = 0; - else - ExpandBuff(false); - } - else if (available > tokenBegin) - available = bufsize; - else if ((tokenBegin - available) < 2048) - ExpandBuff(true); - else - available = tokenBegin; - } - - int i; - try { - if ((i = inputStream.read(buffer, maxNextCharInd, - available - maxNextCharInd)) == -1) - { - inputStream.close(); - throw new java.io.IOException(); - } - maxNextCharInd += i; - return; - } - catch(java.io.IOException e) { - --bufpos; - backup(0); - if (tokenBegin == -1) - tokenBegin = bufpos; - throw e; - } + protected void FillBuff() throws java.io.IOException { + if (maxNextCharInd == available) { + if (available == bufsize) { + if (tokenBegin > 2048) { + bufpos = maxNextCharInd = 0; + available = tokenBegin; + } else if (tokenBegin < 0) bufpos = maxNextCharInd = 0; + else ExpandBuff(false); + } else if (available > tokenBegin) available = bufsize; + else if ((tokenBegin - available) < 2048) ExpandBuff(true); + else available = tokenBegin; + } + + int i; + try { + if ((i = inputStream.read(buffer, maxNextCharInd, available - maxNextCharInd)) == -1) { + inputStream.close(); + throw new java.io.IOException(); + } + maxNextCharInd += i; + return; + } catch (java.io.IOException e) { + --bufpos; + backup(0); + if (tokenBegin == -1) tokenBegin = bufpos; + throw e; + } } @Override -public char BeginToken() throws java.io.IOException - { - tokenBegin = -1; - char c = readChar(); - tokenBegin = bufpos; + public char BeginToken() throws java.io.IOException { + tokenBegin = -1; + char c = readChar(); + tokenBegin = bufpos; - return c; + return c; } - protected void UpdateLineColumn(char c) - { - column++; - - if (prevCharIsLF) - { - _lineEndLF = true; - prevCharIsLF = false; - line += (column = 1); - } - else if (prevCharIsCR) - { - if ( c == '\n') { - _lineEndCRLF = true; - } else { - _lineEndCR = true; - } - - prevCharIsCR = false; - if (c == '\n') - { - prevCharIsLF = true; - } - else - line += (column = 1); - } - - switch (c) - { - case '\r' : - prevCharIsCR = true; - break; - case '\n' : - prevCharIsLF = true; - break; + protected void UpdateLineColumn(char c) { + column++; + + if (prevCharIsLF) { + _lineEndLF = true; + prevCharIsLF = false; + line += (column = 1); + } else if (prevCharIsCR) { + if (c == '\n') { + _lineEndCRLF = true; + } else { + _lineEndCR = true; + } + + prevCharIsCR = false; + if (c == '\n') { + prevCharIsLF = true; + } else line += (column = 1); + } + + switch (c) { + case '\r': + prevCharIsCR = true; + break; + case '\n': + prevCharIsLF = true; + break; // GDM 29-Oct-04: It makes more sense to me to consider a tab // as just one character; we have no way to know what // the tab spacing in somebody's editor is. -// case '\t' : -// column--; -// column += (8 - (column & 07)); -// break; - default : - break; - } - - bufline[bufpos] = line; - bufcolumn[bufpos] = column; + // case '\t' : + // column--; + // column += (8 - (column & 07)); + // break; + default: + break; + } + + bufline[bufpos] = line; + bufcolumn[bufpos] = column; } - public char readChar() throws java.io.IOException - { - if (inBuf > 0) - { - --inBuf; + public char readChar() throws java.io.IOException { + if (inBuf > 0) { + --inBuf; - if (++bufpos == bufsize) - bufpos = 0; + if (++bufpos == bufsize) bufpos = 0; - return buffer[bufpos]; - } + return buffer[bufpos]; + } - if (++bufpos >= maxNextCharInd) - FillBuff(); + if (++bufpos >= maxNextCharInd) FillBuff(); - char c = buffer[bufpos]; + char c = buffer[bufpos]; - /** GDM: Check against binary characters which may - * otherwise throw this thing into a loop. */ - if (c >= '\000' && c <= '\010') { - throw new java.io.IOException ("Illegal character read"); - } - UpdateLineColumn(c); - return (c); + /** GDM: Check against binary characters which may otherwise throw this thing into a loop. */ + if (c >= '\000' && c <= '\010') { + throw new java.io.IOException("Illegal character read"); + } + UpdateLineColumn(c); + return (c); } /** - * @deprecated + * @deprecated * @see #getEndColumn */ - public int getColumn() { - return bufcolumn[bufpos]; + return bufcolumn[bufpos]; } /** - * @deprecated + * @deprecated * @see #getEndLine */ - public int getLine() { - return bufline[bufpos]; + return bufline[bufpos]; } public int getEndColumn() { - return bufcolumn[bufpos]; + return bufcolumn[bufpos]; } public int getEndLine() { - return bufline[bufpos]; + return bufline[bufpos]; } public int getBeginColumn() { - /* GDM 24-Oct-04: Catch exceptions which may be thrown - * when parsing binary files */ - try { - return bufcolumn[tokenBegin]; - } - catch (ArrayIndexOutOfBoundsException e) { - return -1; - } + /* GDM 24-Oct-04: Catch exceptions which may be thrown + * when parsing binary files */ + try { + return bufcolumn[tokenBegin]; + } catch (ArrayIndexOutOfBoundsException e) { + return -1; + } } public int getBeginLine() { - /* GDM 24-Oct-04: Catch exceptions which may be thrown - * when parsing binary files */ - try { - return bufline[tokenBegin]; - } - catch (ArrayIndexOutOfBoundsException e) { - return -1; - } + /* GDM 24-Oct-04: Catch exceptions which may be thrown + * when parsing binary files */ + try { + return bufline[tokenBegin]; + } catch (ArrayIndexOutOfBoundsException e) { + return -1; + } } public void backup(int amount) { inBuf += amount; - if ((bufpos -= amount) < 0) - bufpos += bufsize; + if ((bufpos -= amount) < 0) bufpos += bufsize; } - public HtmlCharStream(java.io.Reader dstream, int startline, - int startcolumn, int buffersize) - { + public HtmlCharStream(java.io.Reader dstream, int startline, int startcolumn, int buffersize) { inputStream = dstream; line = startline; column = startcolumn - 1; @@ -291,25 +244,20 @@ public HtmlCharStream(java.io.Reader dstream, int startline, bufcolumn = new int[buffersize]; } - public HtmlCharStream(java.io.Reader dstream, int startline, - int startcolumn) - { - this(dstream, startline, startcolumn, 4096); + public HtmlCharStream(java.io.Reader dstream, int startline, int startcolumn) { + this(dstream, startline, startcolumn, 4096); } - public HtmlCharStream(java.io.Reader dstream) - { - this(dstream, 1, 1, 4096); + public HtmlCharStream(java.io.Reader dstream) { + this(dstream, 1, 1, 4096); } - public void ReInit(java.io.Reader dstream, int startline, - int startcolumn, int buffersize) - { + + public void ReInit(java.io.Reader dstream, int startline, int startcolumn, int buffersize) { inputStream = dstream; line = startline; column = startcolumn - 1; - if (buffer == null || buffersize != buffer.length) - { + if (buffer == null || buffersize != buffer.length) { available = bufsize = buffersize; buffer = new char[buffersize]; bufline = new int[buffersize]; @@ -320,150 +268,121 @@ public void ReInit(java.io.Reader dstream, int startline, bufpos = -1; } - public void ReInit(java.io.Reader dstream, int startline, - int startcolumn) - { - ReInit(dstream, startline, startcolumn, 4096); + public void ReInit(java.io.Reader dstream, int startline, int startcolumn) { + ReInit(dstream, startline, startcolumn, 4096); } - public void ReInit(java.io.Reader dstream) - { - ReInit(dstream, 1, 1, 4096); + public void ReInit(java.io.Reader dstream) { + ReInit(dstream, 1, 1, 4096); } - public HtmlCharStream(java.io.InputStream dstream, int startline, - int startcolumn, int buffersize, String charset) - throws UnsupportedEncodingException - { - this(new java.io.InputStreamReader(dstream, charset), startline, startcolumn, 4096); + + public HtmlCharStream( + java.io.InputStream dstream, int startline, int startcolumn, int buffersize, String charset) + throws UnsupportedEncodingException { + this(new java.io.InputStreamReader(dstream, charset), startline, startcolumn, 4096); } - public HtmlCharStream(java.io.InputStream dstream, int startline, - int startcolumn, String charset) - throws UnsupportedEncodingException - { - this(dstream, startline, startcolumn, 4096, charset); + public HtmlCharStream(java.io.InputStream dstream, int startline, int startcolumn, String charset) + throws UnsupportedEncodingException { + this(dstream, startline, startcolumn, 4096, charset); } - public HtmlCharStream(java.io.InputStream dstream, String charset) - throws UnsupportedEncodingException - { - this(dstream, 1, 1, 4096, charset); + public HtmlCharStream(java.io.InputStream dstream, String charset) + throws UnsupportedEncodingException { + this(dstream, 1, 1, 4096, charset); } - public void ReInit(java.io.InputStream dstream, int startline, - int startcolumn, int buffersize) - { - ReInit(new java.io.InputStreamReader(dstream), startline, startcolumn, 4096); + public void ReInit(java.io.InputStream dstream, int startline, int startcolumn, int buffersize) { + ReInit(new java.io.InputStreamReader(dstream), startline, startcolumn, 4096); } - public void ReInit(java.io.InputStream dstream) - { - ReInit(dstream, 1, 1, 4096); + public void ReInit(java.io.InputStream dstream) { + ReInit(dstream, 1, 1, 4096); } - public void ReInit(java.io.InputStream dstream, int startline, - int startcolumn) - { - ReInit(dstream, startline, startcolumn, 4096); + + public void ReInit(java.io.InputStream dstream, int startline, int startcolumn) { + ReInit(dstream, startline, startcolumn, 4096); } - public String GetImage() - { - if (bufpos >= tokenBegin) - return new String(buffer, tokenBegin, bufpos - tokenBegin + 1); - return new String(buffer, tokenBegin, bufsize - tokenBegin) + - new String(buffer, 0, bufpos + 1); + + public String GetImage() { + if (bufpos >= tokenBegin) return new String(buffer, tokenBegin, bufpos - tokenBegin + 1); + return new String(buffer, tokenBegin, bufsize - tokenBegin) + new String(buffer, 0, bufpos + 1); } - public char[] GetSuffix(int len) - { - char[] ret = new char[len]; + public char[] GetSuffix(int len) { + char[] ret = new char[len]; - if ((bufpos + 1) >= len) - System.arraycopy(buffer, bufpos - len + 1, ret, 0, len); - else - { - System.arraycopy(buffer, bufsize - (len - bufpos - 1), ret, 0, - len - bufpos - 1); - System.arraycopy(buffer, 0, ret, len - bufpos - 1, bufpos + 1); - } + if ((bufpos + 1) >= len) System.arraycopy(buffer, bufpos - len + 1, ret, 0, len); + else { + System.arraycopy(buffer, bufsize - (len - bufpos - 1), ret, 0, len - bufpos - 1); + System.arraycopy(buffer, 0, ret, len - bufpos - 1, bufpos + 1); + } - return ret; + return ret; } - public void Done() - { - buffer = null; - bufline = null; - bufcolumn = null; + public void Done() { + buffer = null; + bufline = null; + bufcolumn = null; } - /** - * Method to adjust line and column numbers for the start of a token. - */ - public void adjustBeginLineColumn(int newLine, int newCol) - { - int start = tokenBegin; - int len; - - if (bufpos >= tokenBegin) - { - len = bufpos - tokenBegin + inBuf + 1; - } - else - { - len = bufsize - tokenBegin + bufpos + 1 + inBuf; - } - - int i = 0; - int j = 0; - int k = 0; - int nextColDiff = 0; - int columnDiff = 0; - - while (i < len && - bufline[j = start % bufsize] == bufline[k = ++start % bufsize]) - { - bufline[j] = newLine; - nextColDiff = columnDiff + bufcolumn[k] - bufcolumn[j]; - bufcolumn[j] = newCol + columnDiff; - columnDiff = nextColDiff; - i++; - } - - if (i < len) - { - bufline[j] = newLine++; - bufcolumn[j] = newCol + columnDiff; - - while (i++ < len) - { - if (bufline[j = start % bufsize] != bufline[++start % bufsize]) - bufline[j] = newLine++; - else - bufline[j] = newLine; - } - } - - line = bufline[j]; - column = bufcolumn[j]; + /** Method to adjust line and column numbers for the start of a token. */ + public void adjustBeginLineColumn(int newLine, int newCol) { + int start = tokenBegin; + int len; + + if (bufpos >= tokenBegin) { + len = bufpos - tokenBegin + inBuf + 1; + } else { + len = bufsize - tokenBegin + bufpos + 1 + inBuf; + } + + int i = 0; + int j = 0; + int k = 0; + int nextColDiff = 0; + int columnDiff = 0; + + while (i < len && bufline[j = start % bufsize] == bufline[k = ++start % bufsize]) { + bufline[j] = newLine; + nextColDiff = columnDiff + bufcolumn[k] - bufcolumn[j]; + bufcolumn[j] = newCol + columnDiff; + columnDiff = nextColDiff; + i++; + } + + if (i < len) { + bufline[j] = newLine++; + bufcolumn[j] = newCol + columnDiff; + + while (i++ < len) { + if (bufline[j = start % bufsize] != bufline[++start % bufsize]) bufline[j] = newLine++; + else bufline[j] = newLine; + } + } + + line = bufline[j]; + column = bufcolumn[j]; } /** - * Retrieve the kind of end of line. + * Retrieve the kind of end of line. + * * @return "CR" or "LF" or "CRLF" */ public String getKindOfLineEnd() { - if (_lineEndCR || _lineEndLF || _lineEndCRLF) { - if (_lineEndCRLF) { - return "CRLF"; - } - if (_lineEndCR) { - return "CR"; - } - if (_lineEndLF) { - return "LF"; - } - } - return null; + if (_lineEndCR || _lineEndLF || _lineEndCRLF) { + if (_lineEndCRLF) { + return "CRLF"; + } + if (_lineEndCR) { + return "CR"; + } + if (_lineEndLF) { + return "LF"; + } } - + return null; + } } diff --git a/jhove-modules/html-hul/src/main/java/edu/harvard/hul/ois/jhove/module/html/HtmlDocDesc.java b/jhove-modules/html-hul/src/main/java/edu/harvard/hul/ois/jhove/module/html/HtmlDocDesc.java index 18b79d430..f7229ec13 100644 --- a/jhove-modules/html-hul/src/main/java/edu/harvard/hul/ois/jhove/module/html/HtmlDocDesc.java +++ b/jhove-modules/html-hul/src/main/java/edu/harvard/hul/ois/jhove/module/html/HtmlDocDesc.java @@ -1,497 +1,501 @@ -/********************************************************************** - * Jhove - JSTOR/Harvard Object Validation Environment - * Copyright 2004 by JSTOR and the President and Fellows of Harvard College - **********************************************************************/ - +/** + * ******************************************************************** Jhove - JSTOR/Harvard Object + * Validation Environment Copyright 2004 by JSTOR and the President and Fellows of Harvard College + * ******************************************************************** + */ package edu.harvard.hul.ois.jhove.module.html; +import edu.harvard.hul.ois.jhove.ErrorMessage; +import edu.harvard.hul.ois.jhove.RepInfo; +import edu.harvard.hul.ois.jhove.module.utf8.Utf8BlockMarker; +import edu.harvard.hul.ois.jhove.module.xml.HtmlMetadata; import java.util.ArrayList; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; -import edu.harvard.hul.ois.jhove.ErrorMessage; -import edu.harvard.hul.ois.jhove.RepInfo; -import edu.harvard.hul.ois.jhove.module.HtmlModule; -import edu.harvard.hul.ois.jhove.module.utf8.Utf8BlockMarker; -import edu.harvard.hul.ois.jhove.module.xml.HtmlMetadata; - /** - * This is an abstract class for processing an HTML document that has - * been parsed into a List of HtmlElements. It defines common behavior - * for all supported versions of HTML except XHTML. Subclasses - * modify this base as needed. + * This is an abstract class for processing an HTML document that has been parsed into a List of + * HtmlElements. It defines common behavior for all supported versions of HTML except XHTML. + * Subclasses modify this base as needed. * * @author Gary McGath - * */ public abstract class HtmlDocDesc { - /** Metadata for this document. */ - private HtmlMetadata metadata; - - /** - * Generic list of supported tags. For efficiency, this is - * generated only once. Subclasses will need to get a copy - * of this list and make additions or deletions as necessary. - * They must not modify any of the existing - * members of the list. - */ - protected static HashMap commonTags; - - /** - * List of supported tags for this version of HTML. The subclass - * is responsible for generating this, typically using commonTags - * as a starting point. - */ - protected Map supportedElements; + /** Metadata for this document. */ + private HtmlMetadata metadata; - /** A representation of the HTML element. */ - protected HtmlTagDesc htmlElement; - /** A representation of the HEAD element. */ - protected HtmlTagDesc headElement; - /** A representation of the BODY element. */ - protected HtmlTagDesc bodyElement; - /** A representation of the FRAMESET element. */ - protected HtmlTagDesc framesetElement; + /** + * Generic list of supported tags. For efficiency, this is generated only once. Subclasses will + * need to get a copy of this list and make additions or deletions as necessary. They must not + * modify any of the existing members of the list. + */ + protected static HashMap commonTags; - private HtmlStack elementStack; + /** + * List of supported tags for this version of HTML. The subclass is responsible for generating + * this, typically using commonTags as a starting point. + */ + protected Map supportedElements; - /** Header tags, which are invariant for all HTML versions. */ - protected static String[] headings = { "h1", "h2", "h3", "h4", "h5", "h6" }; + /** A representation of the HTML element. */ + protected HtmlTagDesc htmlElement; + /** A representation of the HEAD element. */ + protected HtmlTagDesc headElement; + /** A representation of the BODY element. */ + protected HtmlTagDesc bodyElement; + /** A representation of the FRAMESET element. */ + protected HtmlTagDesc framesetElement; - /** - * Validates the document and puts interesting properties into the - * RepInfo. - * - * @param elements - * The element list constructed by the parser - * @param info - * The RepInfo object which will be populated - * with properties - */ - public boolean validate(List elements, RepInfo info) { - // As we get to each open tag, we - // check it against the corresponding HtmlTagDesc. If there isn't one, - // we - // mark the document as invalid but continue anyway; we create a - // temporary - // HtmlTagDesc object for the tag that we find, with the closing tag - // indicated - // as optional. - // For each open tag, we push the HtmlTagDesc object onto the stack. We - // check - // if it's in the allowed content of the enclosing element. If not, we - // report it - // as an error but continue with it anyway. - // - // We special-case HTML, HEAD and BODY, which can be implied. - // If a tag is found which requires the content model for one of - // these, and it isn't on the stack, we just push it. + private HtmlStack elementStack; - metadata = new HtmlMetadata(); - elementStack = new HtmlStack(); - elementStack.setHeadElement(headElement); - elementStack.setBodyElement(bodyElement); - elementStack.setFramesetElement(framesetElement); - Iterator iter = elements.iterator(); - while (iter.hasNext()) { - JHElement elem = (JHElement) iter.next(); - if (elem instanceof JHDoctype) { - // Doctype requires no further processing; grammar - // will have already caught it if it's not at the top - continue; - } else if (elem instanceof JHOpenTag) { - doOpenTag((JHOpenTag) elem, info); - } else if (elem instanceof JHCloseTag) { - doCloseTag((JHCloseTag) elem, info); - } else if (elem instanceof JHErrorElement) { - doErrorElement((JHErrorElement) elem, info); - } else if (elem instanceof JHPCData) { - doPCData((JHPCData) elem, info, metadata); - } - } - // It's a requirement that there be at least a TITLE, - // and thus an implicit or explicit HEAD element. - if (!elementStack.isHeadSeen()) { - info.setMessage(new ErrorMessage( - MessageConstants.HTML_HUL_11)); - info.setValid(false); - } - return true; - } + /** Header tags, which are invariant for all HTML versions. */ + protected static String[] headings = {"h1", "h2", "h3", "h4", "h5", "h6"}; - /** Returns the metadata for this document. */ - public HtmlMetadata getMetadata() { - return metadata; - } + /** + * Validates the document and puts interesting properties into the RepInfo. + * + * @param elements The element list constructed by the parser + * @param info The RepInfo object which will be populated with properties + */ + public boolean validate(List elements, RepInfo info) { + // As we get to each open tag, we + // check it against the corresponding HtmlTagDesc. If there isn't one, + // we + // mark the document as invalid but continue anyway; we create a + // temporary + // HtmlTagDesc object for the tag that we find, with the closing tag + // indicated + // as optional. + // For each open tag, we push the HtmlTagDesc object onto the stack. We + // check + // if it's in the allowed content of the enclosing element. If not, we + // report it + // as an error but continue with it anyway. + // + // We special-case HTML, HEAD and BODY, which can be implied. + // If a tag is found which requires the content model for one of + // these, and it isn't on the stack, we just push it. - /** - * Initialization called by subclass constructors after supportedElements - * has been assigned. - */ - protected void init() { - htmlElement = (HtmlTagDesc) supportedElements.get("html"); - headElement = (HtmlTagDesc) supportedElements.get("head"); - bodyElement = (HtmlTagDesc) supportedElements.get("body"); - } + metadata = new HtmlMetadata(); + elementStack = new HtmlStack(); + elementStack.setHeadElement(headElement); + elementStack.setBodyElement(bodyElement); + elementStack.setFramesetElement(framesetElement); + Iterator iter = elements.iterator(); + while (iter.hasNext()) { + JHElement elem = (JHElement) iter.next(); + if (elem instanceof JHDoctype) { + // Doctype requires no further processing; grammar + // will have already caught it if it's not at the top + continue; + } else if (elem instanceof JHOpenTag) { + doOpenTag((JHOpenTag) elem, info); + } else if (elem instanceof JHCloseTag) { + doCloseTag((JHCloseTag) elem, info); + } else if (elem instanceof JHErrorElement) { + doErrorElement((JHErrorElement) elem, info); + } else if (elem instanceof JHPCData) { + doPCData((JHPCData) elem, info, metadata); + } + } + // It's a requirement that there be at least a TITLE, + // and thus an implicit or explicit HEAD element. + if (!elementStack.isHeadSeen()) { + info.setMessage(new ErrorMessage(MessageConstants.HTML_HUL_11)); + info.setValid(false); + } + return true; + } - /* Break out open tag code */ - private void doOpenTag(JHOpenTag tag, RepInfo info) { - String name = tag.getName().toLowerCase(); - boolean unknownTag = false; + /** Returns the metadata for this document. */ + public HtmlMetadata getMetadata() { + return metadata; + } - String msg = tag.getErrorMessage(); - if (msg != null) { - info.setMessage(new ErrorMessage(msg, "Name = " + name + ", Line = " - + tag.getLine() + ", Column = " + tag.getColumn())); - info.setWellFormed(false); - // But keep going anyway! - } + /** Initialization called by subclass constructors after supportedElements has been assigned. */ + protected void init() { + htmlElement = (HtmlTagDesc) supportedElements.get("html"); + headElement = (HtmlTagDesc) supportedElements.get("head"); + bodyElement = (HtmlTagDesc) supportedElements.get("body"); + } - /* - * If it's anything but an HTML tag, and the stack is empty, - * push an "HTML" element. - */ - if (elementStack.isEmpty() && !"html".equals(name)) { - JHOpenTag fakeTag = new JHOpenTag("html"); - fakeTag.setElement(htmlElement); - elementStack.push(fakeTag); - - } - HtmlTagDesc tagDesc = (HtmlTagDesc) supportedElements.get(name); - if (tagDesc == null) { - unknownTag = true; - } - // Check the context only if it's a known tag; - // otherwise we'll issue a redundant error message. - if (!unknownTag && !checkElementContext(tag, info)) { - String toptag = null; - if (!elementStack.isEmpty()) { - JHOpenTag top = elementStack.top(); - toptag = top.getName(); - } - info.setMessage(new ErrorMessage( - MessageConstants.HTML_HUL_4, - "Name = " + name + ", " - + (toptag != null ? "Container = " + toptag + ", " - : "") - + "Line = " + tag.getLine() + ", Column = " - + tag.getColumn())); - info.setValid(false); - } - if (unknownTag) { - info.setMessage( - new ErrorMessage(MessageConstants.HTML_HUL_6, - "Name = " + name + ", Line = " + tag.getLine() - + ", Column = " + tag.getColumn())); - info.setValid(false); - // Make a temporary tag descriptor - tagDesc = new HtmlTempTagDesc(name); - } - if (!unknownTag && info.getWellFormed() == RepInfo.TRUE) { - /* Check if the attributes are valid */ - List atts = tag.getAttributes(); - Iterator iter = atts.iterator(); - // Create a list to accumulate all attribute names. - List attNames = new ArrayList(atts.size()); - while (iter.hasNext()) { - JHAttribute att = (JHAttribute) iter.next(); - String attName = att.getName(); - attNames.add(attName); - String attVal = att.getValue(); - HtmlAttributeDesc attDesc = tagDesc.namedAttDesc(attName); - if (attDesc == null) { - info.setMessage(new ErrorMessage( - MessageConstants.HTML_HUL_7, - "Name = " + name + ", Attribute = " + attName - + ", Line = " + att.getLine() - + ", Column = " + att.getColumn())); - info.setValid(false); - } else { - /* Check if value is legit */ - if (!attDesc.valueOK(attName, attVal)) { - info.setMessage(new ErrorMessage( - MessageConstants.HTML_HUL_8, - "Element = " + name + ", Attribute = " + attName - + ", Value = " + attVal + ", Line = " - + att.getLine() + ", Column = " - + att.getColumn())); - info.setValid(false); - } - } - // Extract entities from attribute value - if (attVal != null) { - Iterator entIter = tag.getEntities(attVal).iterator(); - Utf8BlockMarker utf8BM = metadata.getUtf8BlockMarker(); - while (entIter.hasNext()) { - String ent = (String) entIter.next(); - metadata.addEntity(ent); - // If it's a numerical entity, note which UTF8 block - // it's in - try { - if (ent.charAt(1) == '#') { - int entval = Integer.parseInt( - ent.substring(2, ent.length() - 1)); - utf8BM.markBlock(entval); - } - } catch (Exception e) { - // Any exception means it's the wrong kind of entity - } - } - } - } - // Check if all required attributes were found. - List missingAtts = tagDesc.missingRequiredAttributes(attNames); - if (!missingAtts.isEmpty()) { - info.setValid(false); - Iterator miter = missingAtts.iterator(); - while (miter.hasNext()) { - String matt = (String) miter.next(); - info.setMessage(new ErrorMessage( - MessageConstants.HTML_HUL_9, - "Tag = " + name + ", Attribute = " + matt - + ", Line = " + tag.getLine() - + ", Column = " + tag.getColumn())); - } - } - } - tag.processElement(metadata); - // If the content is empty, then a closing tag isn't permitted - // (SGML handbook 7.3), so we don't push the open tag. - // But if it's a temporary tag descriptor, we don't know - // anything about it, so all guesses are wild. Push it anyway. - if (tagDesc.isTemp() || !tagDesc.isContentEmpty()) { - tag.setElement(tagDesc); - elementStack.push(tag); - } - } + /* Break out open tag code */ + private void doOpenTag(JHOpenTag tag, RepInfo info) { + String name = tag.getName().toLowerCase(); + boolean unknownTag = false; - private void doCloseTag(JHCloseTag tag, RepInfo info) { - String name = tag.getName(); - // Dig down into the stack till we find an element which - // matches this. If there's none, report the document - // as not well formed. Also allow for the special case - // of an empty body. (An empty head is illegal.) - int idx = elementStack.search(name); - if (idx == -1) { - info.setMessage(new ErrorMessage( - MessageConstants.HTML_HUL_10, - "Name = " + name + ", Line = " + tag.getLine() - + ", Column = " + tag.getColumn())); - info.setValid(false); - } else { - // Pop the stack down to the level of the matching tag. - elementStack.popTo(idx); - } + String msg = tag.getErrorMessage(); + if (msg != null) { + info.setMessage( + new ErrorMessage( + msg, + "Name = " + name + ", Line = " + tag.getLine() + ", Column = " + tag.getColumn())); + info.setWellFormed(false); + // But keep going anyway! + } - } + /* + * If it's anything but an HTML tag, and the stack is empty, + * push an "HTML" element. + */ + if (elementStack.isEmpty() && !"html".equals(name)) { + JHOpenTag fakeTag = new JHOpenTag("html"); + fakeTag.setElement(htmlElement); + elementStack.push(fakeTag); + } + HtmlTagDesc tagDesc = (HtmlTagDesc) supportedElements.get(name); + if (tagDesc == null) { + unknownTag = true; + } + // Check the context only if it's a known tag; + // otherwise we'll issue a redundant error message. + if (!unknownTag && !checkElementContext(tag, info)) { + String toptag = null; + if (!elementStack.isEmpty()) { + JHOpenTag top = elementStack.top(); + toptag = top.getName(); + } + info.setMessage( + new ErrorMessage( + MessageConstants.HTML_HUL_4, + "Name = " + + name + + ", " + + (toptag != null ? "Container = " + toptag + ", " : "") + + "Line = " + + tag.getLine() + + ", Column = " + + tag.getColumn())); + info.setValid(false); + } + if (unknownTag) { + info.setMessage( + new ErrorMessage( + MessageConstants.HTML_HUL_6, + "Name = " + name + ", Line = " + tag.getLine() + ", Column = " + tag.getColumn())); + info.setValid(false); + // Make a temporary tag descriptor + tagDesc = new HtmlTempTagDesc(name); + } + if (!unknownTag && info.getWellFormed() == RepInfo.TRUE) { + /* Check if the attributes are valid */ + List atts = tag.getAttributes(); + Iterator iter = atts.iterator(); + // Create a list to accumulate all attribute names. + List attNames = new ArrayList(atts.size()); + while (iter.hasNext()) { + JHAttribute att = (JHAttribute) iter.next(); + String attName = att.getName(); + attNames.add(attName); + String attVal = att.getValue(); + HtmlAttributeDesc attDesc = tagDesc.namedAttDesc(attName); + if (attDesc == null) { + info.setMessage( + new ErrorMessage( + MessageConstants.HTML_HUL_7, + "Name = " + + name + + ", Attribute = " + + attName + + ", Line = " + + att.getLine() + + ", Column = " + + att.getColumn())); + info.setValid(false); + } else { + /* Check if value is legit */ + if (!attDesc.valueOK(attName, attVal)) { + info.setMessage( + new ErrorMessage( + MessageConstants.HTML_HUL_8, + "Element = " + + name + + ", Attribute = " + + attName + + ", Value = " + + attVal + + ", Line = " + + att.getLine() + + ", Column = " + + att.getColumn())); + info.setValid(false); + } + } + // Extract entities from attribute value + if (attVal != null) { + Iterator entIter = tag.getEntities(attVal).iterator(); + Utf8BlockMarker utf8BM = metadata.getUtf8BlockMarker(); + while (entIter.hasNext()) { + String ent = (String) entIter.next(); + metadata.addEntity(ent); + // If it's a numerical entity, note which UTF8 block + // it's in + try { + if (ent.charAt(1) == '#') { + int entval = Integer.parseInt(ent.substring(2, ent.length() - 1)); + utf8BM.markBlock(entval); + } + } catch (Exception e) { + // Any exception means it's the wrong kind of entity + } + } + } + } + // Check if all required attributes were found. + List missingAtts = tagDesc.missingRequiredAttributes(attNames); + if (!missingAtts.isEmpty()) { + info.setValid(false); + Iterator miter = missingAtts.iterator(); + while (miter.hasNext()) { + String matt = (String) miter.next(); + info.setMessage( + new ErrorMessage( + MessageConstants.HTML_HUL_9, + "Tag = " + + name + + ", Attribute = " + + matt + + ", Line = " + + tag.getLine() + + ", Column = " + + tag.getColumn())); + } + } + } + tag.processElement(metadata); + // If the content is empty, then a closing tag isn't permitted + // (SGML handbook 7.3), so we don't push the open tag. + // But if it's a temporary tag descriptor, we don't know + // anything about it, so all guesses are wild. Push it anyway. + if (tagDesc.isTemp() || !tagDesc.isContentEmpty()) { + tag.setElement(tagDesc); + elementStack.push(tag); + } + } - private static void doErrorElement(JHErrorElement elem, RepInfo info) { - elem.reportError(info); - } + private void doCloseTag(JHCloseTag tag, RepInfo info) { + String name = tag.getName(); + // Dig down into the stack till we find an element which + // matches this. If there's none, report the document + // as not well formed. Also allow for the special case + // of an empty body. (An empty head is illegal.) + int idx = elementStack.search(name); + if (idx == -1) { + info.setMessage( + new ErrorMessage( + MessageConstants.HTML_HUL_10, + "Name = " + name + ", Line = " + tag.getLine() + ", Column = " + tag.getColumn())); + info.setValid(false); + } else { + // Pop the stack down to the level of the matching tag. + elementStack.popTo(idx); + } + } - private void doPCData(JHPCData elem, RepInfo info, HtmlMetadata metadata) { - // Pop any elements that have optional close tags and do not - // allow PCDATA. - if (elementStack.isEmpty()) { - // PCData before any content. This generates an implicit - // html and body if they haven't already been seen. - // It also means the document isn't valid, since the title - // should precede any PCData. - info.setMessage(new ErrorMessage( - MessageConstants.HTML_HUL_12)); - info.setValid(false); - return; - } - HtmlTagDesc top = elementStack.top().getElement(); - if (top.isTemp() || top.allowsPCData()) { - // We assume that PCData is allowed with unknown tags. - elem.processPCData(elementStack, metadata); - return; - } - // If we can pop elements with optional closing tags till we find - // one that allows PCData, we should do that. But popping the - // stack empty, as could happen if we're in a HEAD element, is - // wrong. So we always allow two elements to remain on the stack. - while (!top.isCloseTagRequired()) { - if (elementStack.size() <= 2) { - break; - } - elementStack.popp(); - top = elementStack.top().getElement(); - if (top.allowsPCData()) { - elem.processPCData(elementStack, metadata); - return; - } - } - info.setMessage(new ErrorMessage( - MessageConstants.HTML_HUL_13, - "Line = " + elem.getLine() + ", Column = " + elem.getColumn())); - info.setValid(false); - } + private static void doErrorElement(JHErrorElement elem, RepInfo info) { + elem.reportError(info); + } - /* - * Returns true if the element is permissible at this point. - * This may pop elements off the stack and push implied tags. - */ - private boolean checkElementContext(JHOpenTag elem, RepInfo info) { - /* - * We are guaranteed there's something on the stack - * unless the tag is "html", but Paranoia Is A Virtue - */ - String name = elem.getName(); - if (elementStack.isEmpty()) { - return "html".equals(name); - } - if (elementStack.excludesTag(name)) { - return false; - } - JHOpenTag top = elementStack.top(); - for (;;) { - if (top.canGetMore() && top.allowsTag(name, this)) { - top.countComponent(); - return true; - } - if (!top.canAdvance()) { - /* Can't advance, can't stay put. */ - break; - } - top.advanceIndex(); - } + private void doPCData(JHPCData elem, RepInfo info, HtmlMetadata metadata) { + // Pop any elements that have optional close tags and do not + // allow PCDATA. + if (elementStack.isEmpty()) { + // PCData before any content. This generates an implicit + // html and body if they haven't already been seen. + // It also means the document isn't valid, since the title + // should precede any PCData. + info.setMessage(new ErrorMessage(MessageConstants.HTML_HUL_12)); + info.setValid(false); + return; + } + HtmlTagDesc top = elementStack.top().getElement(); + if (top.isTemp() || top.allowsPCData()) { + // We assume that PCData is allowed with unknown tags. + elem.processPCData(elementStack, metadata); + return; + } + // If we can pop elements with optional closing tags till we find + // one that allows PCData, we should do that. But popping the + // stack empty, as could happen if we're in a HEAD element, is + // wrong. So we always allow two elements to remain on the stack. + while (!top.isCloseTagRequired()) { + if (elementStack.size() <= 2) { + break; + } + elementStack.popp(); + top = elementStack.top().getElement(); + if (top.allowsPCData()) { + elem.processPCData(elementStack, metadata); + return; + } + } + info.setMessage( + new ErrorMessage( + MessageConstants.HTML_HUL_13, + "Line = " + elem.getLine() + ", Column = " + elem.getColumn())); + info.setValid(false); + } - /* Kludgy special-case code for optional tags */ - HtmlTagDesc topElem = top.getElement(); - if (topElem == htmlElement) { - if (!elementStack.isHeadSeen() - && headElement.allowsTag(name, this)) { - JHOpenTag fakeTag = new JHOpenTag("head"); - fakeTag.setElement(headElement); - elementStack.push(fakeTag); - return true; - } - if (!elementStack.isBodySeen() && bodyElement != null - && bodyElement.allowsTag(name, this)) { - JHOpenTag fakeTag = new JHOpenTag("body"); - fakeTag.setElement(bodyElement); - elementStack.push(fakeTag); - return true; - } - return false; - } else if (topElem == headElement) { - if ("body".equals(name) || "frameset".equals(name)) { - // Pop implied head end tag. Is this too much - // special-casing? - elementStack.popp(); - elementStack.push(elem); - return true; - } else if (!elementStack.isBodySeen() && bodyElement != null - && bodyElement.allowsTag(name, this)) { - // Similar to above case except that the head is - // implicitly terminated. - elementStack.popp(); - JHOpenTag fakeTag = new JHOpenTag("body"); - fakeTag.setElement(bodyElement); - elementStack.push(fakeTag); - return true; - } else { - return false; - } - } + /* + * Returns true if the element is permissible at this point. + * This may pop elements off the stack and push implied tags. + */ + private boolean checkElementContext(JHOpenTag elem, RepInfo info) { + /* + * We are guaranteed there's something on the stack + * unless the tag is "html", but Paranoia Is A Virtue + */ + String name = elem.getName(); + if (elementStack.isEmpty()) { + return "html".equals(name); + } + if (elementStack.excludesTag(name)) { + return false; + } + JHOpenTag top = elementStack.top(); + for (; ; ) { + if (top.canGetMore() && top.allowsTag(name, this)) { + top.countComponent(); + return true; + } + if (!top.canAdvance()) { + /* Can't advance, can't stay put. */ + break; + } + top.advanceIndex(); + } - // Pop elements till we find a valid context. If - // the enclosing element doesn't have an optional close - // tag, report an error but pop it anyway. But first - // check if there even is a context to which we can pop things. - boolean complained = false; - boolean searchStack = false; - if (elementStack.size() > 2) { - Iterator iter = elementStack.iterator(); - // Discard html element - iter.next(); - while (iter.hasNext()) { - JHOpenTag otag = (JHOpenTag) iter.next(); - if (otag.allowsTag(name, this)) { - searchStack = true; - break; - } - } - } - if (searchStack) { - // We've established we can pop down to something. - while (elementStack.size() > 2) { - if (!complained) { - top = elementStack.top(); - topElem = top.getElement(); - if (topElem.isCloseTagRequired()) { - info.setValid(false); - info.setMessage(new ErrorMessage( - MessageConstants.HTML_HUL_5, - "Name = " + name + ", " + "Container = " - + top.getName() + ", " + "Line = " - + elem.getLine() + ", Column = " - + elem.getColumn())); - } - } - elementStack.popp(); - top = elementStack.top(); - // topElem = top.getElement (); - if (top.allowsTag(name, this)) { - return true; - } - if (elementStack.isEmpty()) { - break; - } - } - } - return false; - } + /* Kludgy special-case code for optional tags */ + HtmlTagDesc topElem = top.getElement(); + if (topElem == htmlElement) { + if (!elementStack.isHeadSeen() && headElement.allowsTag(name, this)) { + JHOpenTag fakeTag = new JHOpenTag("head"); + fakeTag.setElement(headElement); + elementStack.push(fakeTag); + return true; + } + if (!elementStack.isBodySeen() && bodyElement != null && bodyElement.allowsTag(name, this)) { + JHOpenTag fakeTag = new JHOpenTag("body"); + fakeTag.setElement(bodyElement); + elementStack.push(fakeTag); + return true; + } + return false; + } else if (topElem == headElement) { + if ("body".equals(name) || "frameset".equals(name)) { + // Pop implied head end tag. Is this too much + // special-casing? + elementStack.popp(); + elementStack.push(elem); + return true; + } else if (!elementStack.isBodySeen() + && bodyElement != null + && bodyElement.allowsTag(name, this)) { + // Similar to above case except that the head is + // implicitly terminated. + elementStack.popp(); + JHOpenTag fakeTag = new JHOpenTag("body"); + fakeTag.setElement(bodyElement); + elementStack.push(fakeTag); + return true; + } else { + return false; + } + } - /** Adds all the Strings in an array to the end of a List. */ - protected static void addStringsToList(String[] names, List lst) { - for (int i = 0; i < names.length; i++) { - lst.add(names[i]); - } - } + // Pop elements till we find a valid context. If + // the enclosing element doesn't have an optional close + // tag, report an error but pop it anyway. But first + // check if there even is a context to which we can pop things. + boolean complained = false; + boolean searchStack = false; + if (elementStack.size() > 2) { + Iterator iter = elementStack.iterator(); + // Discard html element + iter.next(); + while (iter.hasNext()) { + JHOpenTag otag = (JHOpenTag) iter.next(); + if (otag.allowsTag(name, this)) { + searchStack = true; + break; + } + } + } + if (searchStack) { + // We've established we can pop down to something. + while (elementStack.size() > 2) { + if (!complained) { + top = elementStack.top(); + topElem = top.getElement(); + if (topElem.isCloseTagRequired()) { + info.setValid(false); + info.setMessage( + new ErrorMessage( + MessageConstants.HTML_HUL_5, + "Name = " + + name + + ", " + + "Container = " + + top.getName() + + ", " + + "Line = " + + elem.getLine() + + ", Column = " + + elem.getColumn())); + } + } + elementStack.popp(); + top = elementStack.top(); + // topElem = top.getElement (); + if (top.allowsTag(name, this)) { + return true; + } + if (elementStack.isEmpty()) { + break; + } + } + } + return false; + } - /** - * Adds an attribute to a List, with unrestricted values and - * type IMPLIED. - */ - protected static void addSimpleAttribute(List atts, String name) { - atts.add(new HtmlAttributeDesc(name)); - } + /** Adds all the Strings in an array to the end of a List. */ + protected static void addStringsToList(String[] names, List lst) { + for (int i = 0; i < names.length; i++) { + lst.add(names[i]); + } + } - /** - * Adds an attribute to a List, with unrestricted values and - * type REQUIRED. - */ - protected static void addRequiredAttribute(List atts, String name) { - atts.add(new HtmlAttributeDesc(name, null, HtmlAttributeDesc.REQUIRED)); - } + /** Adds an attribute to a List, with unrestricted values and type IMPLIED. */ + protected static void addSimpleAttribute(List atts, String name) { + atts.add(new HtmlAttributeDesc(name)); + } - /** - * Adds an attribute to a List, with the only permitted value being - * the name of the attribute. This kind of attribute is normally - * represented in HTML without an explicit value; in fact, some (most?) - * readers won't permit an explicit value. - */ - protected static void addSelfAttribute(List atts, String name) { - atts.add(new HtmlAttributeDesc(name, new String[] { name }, - HtmlAttributeDesc.IMPLIED)); - } + /** Adds an attribute to a List, with unrestricted values and type REQUIRED. */ + protected static void addRequiredAttribute(List atts, String name) { + atts.add(new HtmlAttributeDesc(name, null, HtmlAttributeDesc.REQUIRED)); + } - /** Removes excluded strings from a List. */ - protected static void removeStringsFromList(List lst, String[] strs) { - for (int i = 0; i < strs.length; i++) { - lst.remove(strs[i]); - } - } + /** + * Adds an attribute to a List, with the only permitted value being the name of the attribute. + * This kind of attribute is normally represented in HTML without an explicit value; in fact, some + * (most?) readers won't permit an explicit value. + */ + protected static void addSelfAttribute(List atts, String name) { + atts.add(new HtmlAttributeDesc(name, new String[] {name}, HtmlAttributeDesc.IMPLIED)); + } - /** Pushes an element onto the element stack. */ - protected void pushElementStack(JHOpenTag tag) { - elementStack.push(tag); - } + /** Removes excluded strings from a List. */ + protected static void removeStringsFromList(List lst, String[] strs) { + for (int i = 0; i < strs.length; i++) { + lst.remove(strs[i]); + } + } + /** Pushes an element onto the element stack. */ + protected void pushElementStack(JHOpenTag tag) { + elementStack.push(tag); + } } diff --git a/jhove-modules/html-hul/src/main/java/edu/harvard/hul/ois/jhove/module/html/HtmlSpecialToken.java b/jhove-modules/html-hul/src/main/java/edu/harvard/hul/ois/jhove/module/html/HtmlSpecialToken.java index 7d6198b9e..cdafe8e47 100644 --- a/jhove-modules/html-hul/src/main/java/edu/harvard/hul/ois/jhove/module/html/HtmlSpecialToken.java +++ b/jhove-modules/html-hul/src/main/java/edu/harvard/hul/ois/jhove/module/html/HtmlSpecialToken.java @@ -1,33 +1,27 @@ -/********************************************************************** - * Jhove - JSTOR/Harvard Object Validation Environment - * Copyright 2004 by JSTOR and the President and Fellows of Harvard College - **********************************************************************/ - +/** + * ******************************************************************** Jhove - JSTOR/Harvard Object + * Validation Environment Copyright 2004 by JSTOR and the President and Fellows of Harvard College + * ******************************************************************** + */ package edu.harvard.hul.ois.jhove.module.html; /** - * Class for defining special items in HTML element and attribute - * definitions. This class is never instantiated except by the - * static elements it defines. + * Class for defining special items in HTML element and attribute definitions. This class is never + * instantiated except by the static elements it defines. * * @author Gary McGath - * */ public class HtmlSpecialToken { - String _name; - //public static HtmlSpecialToken EMPTY = new HtmlSpecialToken ("EMPTY"); - /** The PCDATA token. Signifies that PCDATA is permitted in the content - * of an element. */ - public static HtmlSpecialToken PCDATA = new HtmlSpecialToken ("PCDATA"); - - /** Private constructor. This class may not be instantiated. */ - private HtmlSpecialToken () {} - - private HtmlSpecialToken (String name) - { - _name = name; - } - - + String _name; + // public static HtmlSpecialToken EMPTY = new HtmlSpecialToken ("EMPTY"); + /** The PCDATA token. Signifies that PCDATA is permitted in the content of an element. */ + public static HtmlSpecialToken PCDATA = new HtmlSpecialToken("PCDATA"); + + /** Private constructor. This class may not be instantiated. */ + private HtmlSpecialToken() {} + + private HtmlSpecialToken(String name) { + _name = name; + } } diff --git a/jhove-modules/html-hul/src/main/java/edu/harvard/hul/ois/jhove/module/html/HtmlStack.java b/jhove-modules/html-hul/src/main/java/edu/harvard/hul/ois/jhove/module/html/HtmlStack.java index 12d1ee1d1..3ddc2a9e5 100644 --- a/jhove-modules/html-hul/src/main/java/edu/harvard/hul/ois/jhove/module/html/HtmlStack.java +++ b/jhove-modules/html-hul/src/main/java/edu/harvard/hul/ois/jhove/module/html/HtmlStack.java @@ -1,136 +1,120 @@ -/********************************************************************** - * Jhove - JSTOR/Harvard Object Validation Environment - * Copyright 2004 by JSTOR and the President and Fellows of Harvard College - **********************************************************************/ - +/** + * ******************************************************************** Jhove - JSTOR/Harvard Object + * Validation Environment Copyright 2004 by JSTOR and the President and Fellows of Harvard College + * ******************************************************************** + */ package edu.harvard.hul.ois.jhove.module.html; import java.util.*; /** - * A LinkedList dressed up as a stack for processing HTML objects. - * It knows about certain elements and their history on the stack. + * A LinkedList dressed up as a stack for processing HTML objects. It knows about certain elements + * and their history on the stack. * * @author Gary McGath - * */ public class HtmlStack extends LinkedList { - /** Elements which get special treatment. */ - private HtmlTagDesc headElement; - private HtmlTagDesc bodyElement; - private HtmlTagDesc framesetElement; - - private boolean headSeen; - private boolean bodySeen; - - /** Sets the value of the HEAD element for easy comparison */ - protected void setHeadElement (HtmlTagDesc elem) - { - headElement = elem; - } + /** Elements which get special treatment. */ + private HtmlTagDesc headElement; - /** Sets the value of the HEAD element for easy comparison */ - protected void setBodyElement (HtmlTagDesc elem) - { - bodyElement = elem; - } + private HtmlTagDesc bodyElement; + private HtmlTagDesc framesetElement; - /** Sets the value of the HEAD element for easy comparison */ - protected void setFramesetElement (HtmlTagDesc elem) - { - bodyElement = elem; - } + private boolean headSeen; + private boolean bodySeen; - /** Pops top element from element stack. If we ever decide - * to go to a different stack implementation, it's necessary - * only to change these methods. Also, they add some - * type checking. - * - * Name changed from "pop" to "popp" to avoid a conflict in Java 1.6 - * with the List class. - * */ - protected void popp () - { - removeLast (); - } - - /** Pushes an element onto the stack. This should have - * its element field set to function properly. */ - protected void push (JHOpenTag tag) - { - add (tag); - HtmlTagDesc element = tag.getElement (); - if (element == headElement) { - headSeen = true; - } - else if (element == bodyElement) { - bodySeen = true; - } - } + /** Sets the value of the HEAD element for easy comparison */ + protected void setHeadElement(HtmlTagDesc elem) { + headElement = elem; + } - /** Gets the top of the element stack without popping it. */ - protected JHOpenTag top () - { - return (JHOpenTag) getLast(); - } - - /** Searches backwards through the element stack for a - * match to a given tag. Return -1 if no match. */ - protected int search (String tag) - { - /* Supposedly this ListIterator setup works - * for walking backwards. */ - ListIterator liter = listIterator - (size()); - int idx = size () - 1; - while (liter.hasPrevious ()) { - JHOpenTag stackTag = (JHOpenTag) liter.previous(); - HtmlTagDesc elem = stackTag.getElement (); - if (elem.matches (tag)) { - return idx; - } - idx--; - } - - /* No match, return -1 */ - return -1; - } - - /** Pops elements from the stack up to and including the - * one indexed by idx */ - protected void popTo (int idx) - { - int npop = size () - idx; - for (int i = 0; i < npop; i++) { - removeLast(); - } + /** Sets the value of the HEAD element for easy comparison */ + protected void setBodyElement(HtmlTagDesc elem) { + bodyElement = elem; + } + + /** Sets the value of the HEAD element for easy comparison */ + protected void setFramesetElement(HtmlTagDesc elem) { + bodyElement = elem; + } + + /** + * Pops top element from element stack. If we ever decide to go to a different stack + * implementation, it's necessary only to change these methods. Also, they add some type checking. + * + *

Name changed from "pop" to "popp" to avoid a conflict in Java 1.6 with the List class. + */ + protected void popp() { + removeLast(); + } + + /** + * Pushes an element onto the stack. This should have its element field set to function properly. + */ + protected void push(JHOpenTag tag) { + add(tag); + HtmlTagDesc element = tag.getElement(); + if (element == headElement) { + headSeen = true; + } else if (element == bodyElement) { + bodySeen = true; } + } + + /** Gets the top of the element stack without popping it. */ + protected JHOpenTag top() { + return (JHOpenTag) getLast(); + } - /** Returns true if a HEAD element has been - * pushed on the stack. */ - protected boolean isHeadSeen () - { - return headSeen; + /** + * Searches backwards through the element stack for a match to a given tag. Return -1 if no match. + */ + protected int search(String tag) { + /* Supposedly this ListIterator setup works + * for walking backwards. */ + ListIterator liter = listIterator(size()); + int idx = size() - 1; + while (liter.hasPrevious()) { + JHOpenTag stackTag = (JHOpenTag) liter.previous(); + HtmlTagDesc elem = stackTag.getElement(); + if (elem.matches(tag)) { + return idx; + } + idx--; } - - /** Returns true if a BODY element has been - * pushed on the stack. */ - protected boolean isBodySeen () - { - return bodySeen; + + /* No match, return -1 */ + return -1; + } + + /** Pops elements from the stack up to and including the one indexed by idx */ + protected void popTo(int idx) { + int npop = size() - idx; + for (int i = 0; i < npop; i++) { + removeLast(); } - - /** Returns true if any element on the stack - * prohibits the specified tag. */ - protected boolean excludesTag (String tag) { - Iterator iter = iterator (); - while (iter.hasNext ()) { - JHOpenTag stackTag = (JHOpenTag) iter.next (); - if (stackTag.getElement ().excludesTag (tag)) { - return true; - } - } - return false; + } + + /** Returns true if a HEAD element has been pushed on the stack. */ + protected boolean isHeadSeen() { + return headSeen; + } + + /** Returns true if a BODY element has been pushed on the stack. */ + protected boolean isBodySeen() { + return bodySeen; + } + + /** Returns true if any element on the stack prohibits the specified tag. */ + protected boolean excludesTag(String tag) { + Iterator iter = iterator(); + while (iter.hasNext()) { + JHOpenTag stackTag = (JHOpenTag) iter.next(); + if (stackTag.getElement().excludesTag(tag)) { + return true; + } } + return false; + } } diff --git a/jhove-modules/html-hul/src/main/java/edu/harvard/hul/ois/jhove/module/html/HtmlTagDesc.java b/jhove-modules/html-hul/src/main/java/edu/harvard/hul/ois/jhove/module/html/HtmlTagDesc.java index eb0fa0b26..e74095b29 100644 --- a/jhove-modules/html-hul/src/main/java/edu/harvard/hul/ois/jhove/module/html/HtmlTagDesc.java +++ b/jhove-modules/html-hul/src/main/java/edu/harvard/hul/ois/jhove/module/html/HtmlTagDesc.java @@ -1,387 +1,359 @@ -/********************************************************************** - * Jhove - JSTOR/Harvard Object Validation Environment - * Copyright 2004 by JSTOR and the President and Fellows of Harvard College - **********************************************************************/ - +/** + * ******************************************************************** Jhove - JSTOR/Harvard Object + * Validation Environment Copyright 2004 by JSTOR and the President and Fellows of Harvard College + * ******************************************************************** + */ package edu.harvard.hul.ois.jhove.module.html; import java.util.*; /** - * This class defines the permitted behavior of a particular HTML tag. - * The full descriptive power of a DTD description isn't implemented here, - * but content types and sequences of content are implemented. + * This class defines the permitted behavior of a particular HTML tag. The full descriptive power of + * a DTD description isn't implemented here, but content types and sequences of content are + * implemented. * * @author Gary McGath - * */ public class HtmlTagDesc { - - /* Name of element. Constructor forces it to lower case, regardless - * of how it was specified. */ - private String _name; - private boolean _openTagRequired; - private boolean _closeTagRequired; - - - /* Element tags in which this element can be nested. Only tags for - * elements with optional opening tags need to be listed here; - * listing others will just reduce efficiency. If there are no - * applicable elements, this can be left null (which it is by - * default). */ - private List _implicitContainers; - - /* Array of permitted content lists. null indicates an empty element. - * Each element of the array is a list of tag names which are permissible - * at a given point. - */ - private List[] _contentArray; - - /* Array of excluded content strings. Each String in the array is a tag which - * may not be included in any descendant of the element. */ - private String[] _excludedContent; - - /** Array controlling the number of times each element of _contentArray - * may be used. */ - protected int[] _sequence; - - /** Value in _sequence indicating an element may be used 0 or 1 times. */ - public final static int SEQ0_1 = 0; - - /** Value in _sequence indicating an element must be used exactly once. */ - public final static int SEQ1 = 1; - /** Value in _sequence indicating an element may be used 1 or more times. */ - public final static int SEQ1_MANY = 2; + /* Name of element. Constructor forces it to lower case, regardless + * of how it was specified. */ + private String _name; + private boolean _openTagRequired; + private boolean _closeTagRequired; - /** Value in _sequence indicating an element may be used 0 or more times. */ - public final static int SEQ0_MANY = 3; - - /* List of Attributes which are recognized for this tag. - * Will never be null, but may be empty. */ - private List _attributes; - - - /** Constructor for simple case. - * - * @param name Name of the element - * @param openTagRequired true if an opening tag is required - * @param closeTagRequired true if a closing tag is required - * @param content List of permitted tags. But what do I do when - * a particular order is required? Null if element - * is defined at EMPTY. - * @param attributes List of HtmlAttributeDesc elements enumerating - * the permitted attributes. May be null, in which - * case _attributes will be stored as an empty list. - */ - public HtmlTagDesc (String name, - boolean openTagRequired, - boolean closeTagRequired, - List content, - List attributes) - { - _name = name.toLowerCase (); - _openTagRequired = openTagRequired; - _closeTagRequired = closeTagRequired; - _implicitContainers = new LinkedList (); - if (content == null) { - // Empty element, so there's nothing for the content array - _contentArray = null; - } - else { - _contentArray = new List[1]; - _contentArray[0] = content; - _sequence = new int[1]; - _sequence[0] = SEQ0_MANY; // assume most general case - } - if (attributes == null) { - _attributes = new ArrayList (1); - } - else { - _attributes = attributes; - } - } - - /** Constructor for sequenced case. - * - * @param name Name of the element - * @param openTagRequired true if an opening tag is required - * @param closeTagRequired true if a closing tag is required - * @param sequence Array indicating the sequencing of elements in - * content. Must have the same length - * as sequence. - * @param attributes List of HtmlAttributeDesc elements enumerating - * the permitted attributes. May be null, in which - * case _attributes will be stored as an empty list. - */ - public HtmlTagDesc (String name, - boolean openTagRequired, - boolean closeTagRequired, - int[] sequence, - List[] contentArray, - List attributes) - { - _name = name.toLowerCase (); - _openTagRequired = openTagRequired; - _closeTagRequired = closeTagRequired; - _implicitContainers = new LinkedList (); - _sequence = sequence; - _contentArray = contentArray; - if (attributes == null) { - _attributes = new ArrayList (1); - } - else { - _attributes = attributes; - } + /* Element tags in which this element can be nested. Only tags for + * elements with optional opening tags need to be listed here; + * listing others will just reduce efficiency. If there are no + * applicable elements, this can be left null (which it is by + * default). */ + private List _implicitContainers; + + /* Array of permitted content lists. null indicates an empty element. + * Each element of the array is a list of tag names which are permissible + * at a given point. + */ + private List[] _contentArray; + + /* Array of excluded content strings. Each String in the array is a tag which + * may not be included in any descendant of the element. */ + private String[] _excludedContent; + + /** Array controlling the number of times each element of _contentArray may be used. */ + protected int[] _sequence; + + /** Value in _sequence indicating an element may be used 0 or 1 times. */ + public static final int SEQ0_1 = 0; + + /** Value in _sequence indicating an element must be used exactly once. */ + public static final int SEQ1 = 1; + + /** Value in _sequence indicating an element may be used 1 or more times. */ + public static final int SEQ1_MANY = 2; + + /** Value in _sequence indicating an element may be used 0 or more times. */ + public static final int SEQ0_MANY = 3; + + /* List of Attributes which are recognized for this tag. + * Will never be null, but may be empty. */ + private List _attributes; + + /** + * Constructor for simple case. + * + * @param name Name of the element + * @param openTagRequired true if an opening tag is required + * @param closeTagRequired true if a closing tag is required + * @param content List of permitted tags. But what do I do when a particular order is required? + * Null if element is defined at EMPTY. + * @param attributes List of HtmlAttributeDesc elements enumerating the permitted attributes. May + * be null, in which case _attributes will be stored as an empty list. + */ + public HtmlTagDesc( + String name, + boolean openTagRequired, + boolean closeTagRequired, + List content, + List attributes) { + _name = name.toLowerCase(); + _openTagRequired = openTagRequired; + _closeTagRequired = closeTagRequired; + _implicitContainers = new LinkedList(); + if (content == null) { + // Empty element, so there's nothing for the content array + _contentArray = null; + } else { + _contentArray = new List[1]; + _contentArray[0] = content; + _sequence = new int[1]; + _sequence[0] = SEQ0_MANY; // assume most general case } - - /** Specifies tags which may not be included in this - * element or in any element nested at any depth - * within it. Corresponds to the -(content) feature - * of the DTD. */ - public void setExcludedContent (String[] content) - { - _excludedContent = content; + if (attributes == null) { + _attributes = new ArrayList(1); + } else { + _attributes = attributes; } - - /** Returns true if a given tag is excluded - * within this element. It is necessary to call this - * method for each element on the stack to determine if - * it is excluded. */ - public boolean excludesTag (String tag) - { - if (_excludedContent == null) { - return false; - } - for (int i = 0; i < _excludedContent.length; i++) { - if (_excludedContent[i].equals (tag)) { - return true; - } - } - return false; + } + + /** + * Constructor for sequenced case. + * + * @param name Name of the element + * @param openTagRequired true if an opening tag is required + * @param closeTagRequired true if a closing tag is required + * @param sequence Array indicating the sequencing of elements in content. Must have the same + * length as sequence. + * @param attributes List of HtmlAttributeDesc elements enumerating the permitted attributes. May + * be null, in which case _attributes will be stored as an empty list. + */ + public HtmlTagDesc( + String name, + boolean openTagRequired, + boolean closeTagRequired, + int[] sequence, + List[] contentArray, + List attributes) { + _name = name.toLowerCase(); + _openTagRequired = openTagRequired; + _closeTagRequired = closeTagRequired; + _implicitContainers = new LinkedList(); + _sequence = sequence; + _contentArray = contentArray; + if (attributes == null) { + _attributes = new ArrayList(1); + } else { + _attributes = attributes; } + } - /** Alternative way of setting the attribute names. - * This can be used where all the attributes are - * unrestricted. This will replace any previously - * set attributes. */ - public void setAttributes (String[] attributeArray) - { - List atts = new ArrayList (attributeArray.length); - for (int i = 0; i < attributeArray.length; i++) { - HtmlAttributeDesc desc = new HtmlAttributeDesc (attributeArray[i]); - atts.add (desc); - } - _attributes = atts; + /** + * Specifies tags which may not be included in this element or in any element nested at any depth + * within it. Corresponds to the -(content) feature of the DTD. + */ + public void setExcludedContent(String[] content) { + _excludedContent = content; + } + + /** + * Returns true if a given tag is excluded within this element. It is necessary to + * call this method for each element on the stack to determine if it is excluded. + */ + public boolean excludesTag(String tag) { + if (_excludedContent == null) { + return false; } - - - /** Provides the object with an array of element tags in which - * this element can be nested. Only tags for - * elements with optional opening tags may be listed here. - */ - public void addImplicitContainer (HtmlTagDesc container) - { - _implicitContainers.add (container); + for (int i = 0; i < _excludedContent.length; i++) { + if (_excludedContent[i].equals(tag)) { + return true; + } } + return false; + } - public boolean matches (String name) - { - return name.equals (_name); - } - - /** Reports whether this is a temporary tag descriptor. - * Returns false unless overridden. - */ - public boolean isTemp () - { - return false; + /** + * Alternative way of setting the attribute names. This can be used where all the attributes are + * unrestricted. This will replace any previously set attributes. + */ + public void setAttributes(String[] attributeArray) { + List atts = new ArrayList(attributeArray.length); + for (int i = 0; i < attributeArray.length; i++) { + HtmlAttributeDesc desc = new HtmlAttributeDesc(attributeArray[i]); + atts.add(desc); } - + _attributes = atts; + } - /** Reports whether this element allows a given tag name - * in its content, at the specified index. - */ - protected boolean allowsTag (String tag, int index, HtmlDocDesc doc) - { - if (_contentArray == null) { - // null means no content allowed - return false; - } - /* Check for index out of bounds. */ - if (index >= _contentArray.length) { - return false; - } - Iterator iter = _contentArray[index].iterator (); - while (iter.hasNext ()) { - String allowedTag; - try { - allowedTag = (String) iter.next (); - } - catch (Exception e) { - // Catch bad casts here -- any non-strings - // should be ignored. - continue; - } - if (allowedTag.equals (tag)) { - return true; - } - } - - /* We might still be OK if we can construct a set of - * elements with optional opening tags which will fill - * in the gap. */ - HtmlTagDesc tagDesc = (HtmlTagDesc) doc.supportedElements.get (tag); - if (tagDesc != null && - tagDesc._implicitContainers != null) { - Iterator citer = tagDesc._implicitContainers.iterator (); - while (citer.hasNext ()) { - HtmlTagDesc ctnr = (HtmlTagDesc) citer.next (); - // Call self recursively to try to insert the implicit - // tag. There may be more than one level of recursion, - // at least theoretically. - if (allowsTag (ctnr._name, index, doc)) { - JHOpenTag ctnrTag = new JHOpenTag (ctnr._name); - ctnrTag.setElement (ctnr); - doc.pushElementStack(ctnrTag); - return true; - } - } - } - return false; + /** + * Provides the object with an array of element tags in which this element can be nested. Only + * tags for elements with optional opening tags may be listed here. + */ + public void addImplicitContainer(HtmlTagDesc container) { + _implicitContainers.add(container); + } + + public boolean matches(String name) { + return name.equals(_name); + } + + /** + * Reports whether this is a temporary tag descriptor. Returns false unless + * overridden. + */ + public boolean isTemp() { + return false; + } + + /** + * Reports whether this element allows a given tag name in its content, at the specified index. + */ + protected boolean allowsTag(String tag, int index, HtmlDocDesc doc) { + if (_contentArray == null) { + // null means no content allowed + return false; } - - /** Reports whether this element can be implicitly nested - * in an element with a given tag. There may be more than - * one implicit container for a tag; if the DTD is unambiguous, - * there should be only one which is permissible in any - * given context. - */ - protected List implicitContainers (String tag) - { - return _implicitContainers; + /* Check for index out of bounds. */ + if (index >= _contentArray.length) { + return false; } - - /** Reports whether additional elements can be matched - * at the specified content index. The index is assumed - * to be legal. */ - protected boolean canGetMoreAt (int index, int elemCount) - { - switch (_sequence[index]) { - case SEQ0_1: - case SEQ1: - return (elemCount == 0); - case SEQ1_MANY: - case SEQ0_MANY: - return true; - default: - return false; // Should never happen - } + Iterator iter = _contentArray[index].iterator(); + while (iter.hasNext()) { + String allowedTag; + try { + allowedTag = (String) iter.next(); + } catch (Exception e) { + // Catch bad casts here -- any non-strings + // should be ignored. + continue; + } + if (allowedTag.equals(tag)) { + return true; + } } - - /** Reports whether it's legal to advance to the next content - * index. The index is assumed to be legal, but the one - * to which it's trying to advance may not be. */ - protected boolean canAdvanceFrom (int index, int elemCount) - { - if (index == _sequence.length - 1) { - return false; // No more content to match - } - switch (_sequence[index]) { - case SEQ0_1: - case SEQ0_MANY: - return true; - case SEQ1: - return (elemCount == 1); - case SEQ1_MANY: - return (elemCount >= 1); - default: - return false; // Should never happen + + /* We might still be OK if we can construct a set of + * elements with optional opening tags which will fill + * in the gap. */ + HtmlTagDesc tagDesc = (HtmlTagDesc) doc.supportedElements.get(tag); + if (tagDesc != null && tagDesc._implicitContainers != null) { + Iterator citer = tagDesc._implicitContainers.iterator(); + while (citer.hasNext()) { + HtmlTagDesc ctnr = (HtmlTagDesc) citer.next(); + // Call self recursively to try to insert the implicit + // tag. There may be more than one level of recursion, + // at least theoretically. + if (allowsTag(ctnr._name, index, doc)) { + JHOpenTag ctnrTag = new JHOpenTag(ctnr._name); + ctnrTag.setElement(ctnr); + doc.pushElementStack(ctnrTag); + return true; } + } } + return false; + } - /** Reports whether this element allows a given tag name - * in its content. This version should be used only with - * element descriptors that aren't associated with tags, - * for determining if a hypothetical implied element could - * contain the given tag. - */ - protected boolean allowsTag (String tag, HtmlDocDesc doc) - { - return allowsTag (tag, 0, doc); - } + /** + * Reports whether this element can be implicitly nested in an element with a given tag. There may + * be more than one implicit container for a tag; if the DTD is unambiguous, there should be only + * one which is permissible in any given context. + */ + protected List implicitContainers(String tag) { + return _implicitContainers; + } + /** + * Reports whether additional elements can be matched at the specified content index. The index is + * assumed to be legal. + */ + protected boolean canGetMoreAt(int index, int elemCount) { + switch (_sequence[index]) { + case SEQ0_1: + case SEQ1: + return (elemCount == 0); + case SEQ1_MANY: + case SEQ0_MANY: + return true; + default: + return false; // Should never happen + } + } - protected boolean allowsPCData () - { - if (_contentArray == null) { - return false; - } - Iterator iter = _contentArray[0].iterator (); - while (iter.hasNext ()) { - Object contentItem = iter.next (); - if (contentItem == HtmlSpecialToken.PCDATA) { - return true; - } - } - return false; + /** + * Reports whether it's legal to advance to the next content index. The index is assumed to be + * legal, but the one to which it's trying to advance may not be. + */ + protected boolean canAdvanceFrom(int index, int elemCount) { + if (index == _sequence.length - 1) { + return false; // No more content to match } + switch (_sequence[index]) { + case SEQ0_1: + case SEQ0_MANY: + return true; + case SEQ1: + return (elemCount == 1); + case SEQ1_MANY: + return (elemCount >= 1); + default: + return false; // Should never happen + } + } + /** + * Reports whether this element allows a given tag name in its content. This version should be + * used only with element descriptors that aren't associated with tags, for determining if a + * hypothetical implied element could contain the given tag. + */ + protected boolean allowsTag(String tag, HtmlDocDesc doc) { + return allowsTag(tag, 0, doc); + } - /** Returns the attribute with a given name, or null if - * no such attribute is defined for the element */ - protected HtmlAttributeDesc namedAttDesc (String name) - { - Iterator iter = _attributes.iterator (); - while (iter.hasNext ()) { - HtmlAttributeDesc desc = (HtmlAttributeDesc) iter.next (); - if (desc.nameMatches (name)) { - return desc; - } - } - /* No match. */ - return null; + protected boolean allowsPCData() { + if (_contentArray == null) { + return false; } - - /** Accepts a list of attribute names, and returns a List - * of required attribute names which are not present - * in the parameter list. Returns an empty List - * if all required attributes are present. */ - protected List missingRequiredAttributes (List names) - { - List val = new ArrayList (_attributes.size ()); - // Build a list of required attributes, which will - // be whittled away by comparison with the parameter list - Iterator iter = _attributes.iterator (); - while (iter.hasNext ()) { - HtmlAttributeDesc desc = (HtmlAttributeDesc) iter.next (); - if (desc.isRequired ()) { - boolean found = false; - Iterator niter = names.iterator (); - while (niter.hasNext ()) { - String name = (String) niter.next (); - if (desc.nameMatches (name.toLowerCase ())) { - found = true; - break; - } - } - if (!found) { - val.add (desc.getName ()); - } - } - } - return val; + Iterator iter = _contentArray[0].iterator(); + while (iter.hasNext()) { + Object contentItem = iter.next(); + if (contentItem == HtmlSpecialToken.PCDATA) { + return true; + } } - - /** Returns true if the closing tag is required */ - protected boolean isCloseTagRequired () - { - return _closeTagRequired; + return false; + } + + /** + * Returns the attribute with a given name, or null if no such attribute is defined for the + * element + */ + protected HtmlAttributeDesc namedAttDesc(String name) { + Iterator iter = _attributes.iterator(); + while (iter.hasNext()) { + HtmlAttributeDesc desc = (HtmlAttributeDesc) iter.next(); + if (desc.nameMatches(name)) { + return desc; + } } + /* No match. */ + return null; + } - /** Returns true if this element has empty content */ - protected boolean isContentEmpty () - { - return _contentArray == null; + /** + * Accepts a list of attribute names, and returns a List of required attribute names which are not + * present in the parameter list. Returns an empty List if all required attributes are present. + */ + protected List missingRequiredAttributes(List names) { + List val = new ArrayList(_attributes.size()); + // Build a list of required attributes, which will + // be whittled away by comparison with the parameter list + Iterator iter = _attributes.iterator(); + while (iter.hasNext()) { + HtmlAttributeDesc desc = (HtmlAttributeDesc) iter.next(); + if (desc.isRequired()) { + boolean found = false; + Iterator niter = names.iterator(); + while (niter.hasNext()) { + String name = (String) niter.next(); + if (desc.nameMatches(name.toLowerCase())) { + found = true; + break; + } + } + if (!found) { + val.add(desc.getName()); + } + } } + return val; + } + + /** Returns true if the closing tag is required */ + protected boolean isCloseTagRequired() { + return _closeTagRequired; + } + + /** Returns true if this element has empty content */ + protected boolean isContentEmpty() { + return _contentArray == null; + } } diff --git a/jhove-modules/html-hul/src/main/java/edu/harvard/hul/ois/jhove/module/html/HtmlTempTagDesc.java b/jhove-modules/html-hul/src/main/java/edu/harvard/hul/ois/jhove/module/html/HtmlTempTagDesc.java index 3d7bb52f1..d5bd9b14c 100644 --- a/jhove-modules/html-hul/src/main/java/edu/harvard/hul/ois/jhove/module/html/HtmlTempTagDesc.java +++ b/jhove-modules/html-hul/src/main/java/edu/harvard/hul/ois/jhove/module/html/HtmlTempTagDesc.java @@ -1,51 +1,44 @@ -/********************************************************************** - * Jhove - JSTOR/Harvard Object Validation Environment - * Copyright 2004 by JSTOR and the President and Fellows of Harvard College - **********************************************************************/ - +/** + * ******************************************************************** Jhove - JSTOR/Harvard Object + * Validation Environment Copyright 2004 by JSTOR and the President and Fellows of Harvard College + * ******************************************************************** + */ package edu.harvard.hul.ois.jhove.module.html; /** - * Subclass of HtmlTagDesc for temporary tags. This doesn't add - * any functionality to the superclass, but serves as a marker class. + * Subclass of HtmlTagDesc for temporary tags. This doesn't add any functionality to the superclass, + * but serves as a marker class. * * @author Gary McGath - * */ public class HtmlTempTagDesc extends HtmlTagDesc { - /** - * Constructor. - * - * @param name Tag name - */ - public HtmlTempTagDesc (String name) - { - super (name, false, false, null, null); - // To minimize excessive error messages, assume unlimited - // tags can be nested. - _sequence = new int[1]; - _sequence[0] = SEQ0_MANY; - } + /** + * Constructor. + * + * @param name Tag name + */ + public HtmlTempTagDesc(String name) { + super(name, false, false, null, null); + // To minimize excessive error messages, assume unlimited + // tags can be nested. + _sequence = new int[1]; + _sequence[0] = SEQ0_MANY; + } - /** Reports whether this is a temporary tag descriptor. - * Returns true. - */ - @Override - public boolean isTemp () - { - return true; - } + /** Reports whether this is a temporary tag descriptor. Returns true. */ + @Override + public boolean isTemp() { + return true; + } - /** Reports whether this element allows a given tag name - * in its content, at the specified index. Since we know nothing - * about this element, no meaningful answer is possible. Return - * true just to minimize the number of extra error - * messages. - */ - @Override - protected boolean allowsTag (String tag, int index, HtmlDocDesc doc) - { - return true; - } + /** + * Reports whether this element allows a given tag name in its content, at the specified index. + * Since we know nothing about this element, no meaningful answer is possible. Return true + * just to minimize the number of extra error messages. + */ + @Override + protected boolean allowsTag(String tag, int index, HtmlDocDesc doc) { + return true; + } } diff --git a/jhove-modules/html-hul/src/main/java/edu/harvard/hul/ois/jhove/module/html/JHAttribute.java b/jhove-modules/html-hul/src/main/java/edu/harvard/hul/ois/jhove/module/html/JHAttribute.java index b404b0bdc..a5abe4031 100644 --- a/jhove-modules/html-hul/src/main/java/edu/harvard/hul/ois/jhove/module/html/JHAttribute.java +++ b/jhove-modules/html-hul/src/main/java/edu/harvard/hul/ois/jhove/module/html/JHAttribute.java @@ -1,88 +1,76 @@ -/********************************************************************** - * Jhove - JSTOR/Harvard Object Validation Environment - * Copyright 2004 by JSTOR and the President and Fellows of Harvard College - **********************************************************************/ - +/** + * ******************************************************************** Jhove - JSTOR/Harvard Object + * Validation Environment Copyright 2004 by JSTOR and the President and Fellows of Harvard College + * ******************************************************************** + */ package edu.harvard.hul.ois.jhove.module.html; /** - * A description of an attribute within a JHOpenTag. - * This is not a subclass of JHElement, as it isn't part of - * the elements list. It is simply a way to store the information - * about an attribute in a JHOpenTag. + * A description of an attribute within a JHOpenTag. This is not a subclass of JHElement, as it + * isn't part of the elements list. It is simply a way to store the information about an attribute + * in a JHOpenTag. * * @author Gary McGath - * */ public class JHAttribute { - private String _name; - private String _namespace; - private String _value; - private int _line; - private int _column; - - /** - * Constructor. - * - * @param name Name of the attribute. Will be forced to - * lower case as it is stored. Must not be null. - * @param namespace Namespace for the attribute. May be null - * if no namespace is specified. - * @param value Value of the attribute. May be null. - * If it is in quotes, the quotes will be stripped. - * @param line Line number at which the attribute begins. - * @param column Column number at which the attribute begins. - */ - public JHAttribute (String name, String namespace, String value, - int line, int column) - { - _name = name.toLowerCase (); - _namespace = namespace; - _line = line; - _column = column; - // Clean up value if it's quoted - if (value != null && - value.length () >= 2 && - value.charAt (0) == '\"' && - value.charAt (value.length() - 1) == '\"') { - value = value.substring (1, value.length () - 1); - } - _value = value; - } - - /** Returns the attribute's name. This is guaranteed to be - * in lower case. */ - public String getName () - { - return _name; - } - - /** Returns the namespace of the attribute's name. May be null. */ - public String getNamespace () - { - return _namespace; - } + private String _name; + private String _namespace; + private String _value; + private int _line; + private int _column; - /** Returns the attribute's value. May be null. If not null - * and was originally enclosed in double quotes, the return - * value has quotes stripped. */ - public String getValue () - { - return _value; + /** + * Constructor. + * + * @param name Name of the attribute. Will be forced to lower case as it is stored. Must not be + * null. + * @param namespace Namespace for the attribute. May be null if no namespace is specified. + * @param value Value of the attribute. May be null. If it is in quotes, the quotes will be + * stripped. + * @param line Line number at which the attribute begins. + * @param column Column number at which the attribute begins. + */ + public JHAttribute(String name, String namespace, String value, int line, int column) { + _name = name.toLowerCase(); + _namespace = namespace; + _line = line; + _column = column; + // Clean up value if it's quoted + if (value != null + && value.length() >= 2 + && value.charAt(0) == '\"' + && value.charAt(value.length() - 1) == '\"') { + value = value.substring(1, value.length() - 1); } + _value = value; + } - /** Returns the line number of the beginning of the - * attribute definition. */ - public int getLine () - { - return _line; - } + /** Returns the attribute's name. This is guaranteed to be in lower case. */ + public String getName() { + return _name; + } - /** Returns the column number of the beginning of the - * attribute definition. */ - public int getColumn () - { - return _column; - } + /** Returns the namespace of the attribute's name. May be null. */ + public String getNamespace() { + return _namespace; + } + + /** + * Returns the attribute's value. May be null. If not null and was originally enclosed in double + * quotes, the return value has quotes stripped. + */ + public String getValue() { + return _value; + } + + /** Returns the line number of the beginning of the attribute definition. */ + public int getLine() { + return _line; + } + + /** Returns the column number of the beginning of the attribute definition. */ + public int getColumn() { + return _column; + } } diff --git a/jhove-modules/html-hul/src/main/java/edu/harvard/hul/ois/jhove/module/html/JHCloseTag.java b/jhove-modules/html-hul/src/main/java/edu/harvard/hul/ois/jhove/module/html/JHCloseTag.java index 5212d08e3..00b5ffdd7 100644 --- a/jhove-modules/html-hul/src/main/java/edu/harvard/hul/ois/jhove/module/html/JHCloseTag.java +++ b/jhove-modules/html-hul/src/main/java/edu/harvard/hul/ois/jhove/module/html/JHCloseTag.java @@ -1,39 +1,37 @@ -/********************************************************************** - * Jhove - JSTOR/Harvard Object Validation Environment - * Copyright 2004 by JSTOR and the President and Fellows of Harvard College - **********************************************************************/ - +/** + * ******************************************************************** Jhove - JSTOR/Harvard Object + * Validation Environment Copyright 2004 by JSTOR and the President and Fellows of Harvard College + * ******************************************************************** + */ package edu.harvard.hul.ois.jhove.module.html; import java.util.*; /** * Representation of a parsed HTML close tag. - * - * @author Gary McGath * + * @author Gary McGath */ public class JHCloseTag extends JHElement { - public String _name; + public String _name; - /** Constructor. - * - * @param elements The list of parsed elements, to which - * this gets added. - * @param name The name of the tag - * @param line Line number, for information reporting - * @param column Line number, for information reporting - */ - public JHCloseTag (List elements, String name, int line, int column) { - super (elements); - _name = name.toLowerCase (); - _line = line; - _column = column; - } + /** + * Constructor. + * + * @param elements The list of parsed elements, to which this gets added. + * @param name The name of the tag + * @param line Line number, for information reporting + * @param column Line number, for information reporting + */ + public JHCloseTag(List elements, String name, int line, int column) { + super(elements); + _name = name.toLowerCase(); + _line = line; + _column = column; + } - /** Returns the tag's name. */ - public String getName () - { - return _name; - } + /** Returns the tag's name. */ + public String getName() { + return _name; + } } diff --git a/jhove-modules/html-hul/src/main/java/edu/harvard/hul/ois/jhove/module/html/JHComment.java b/jhove-modules/html-hul/src/main/java/edu/harvard/hul/ois/jhove/module/html/JHComment.java index 4d86d91dd..c38ba772a 100644 --- a/jhove-modules/html-hul/src/main/java/edu/harvard/hul/ois/jhove/module/html/JHComment.java +++ b/jhove-modules/html-hul/src/main/java/edu/harvard/hul/ois/jhove/module/html/JHComment.java @@ -1,8 +1,8 @@ -/********************************************************************** - * Jhove - JSTOR/Harvard Object Validation Environment - * Copyright 2004 by JSTOR and the President and Fellows of Harvard College - **********************************************************************/ - +/** + * ******************************************************************** Jhove - JSTOR/Harvard Object + * Validation Environment Copyright 2004 by JSTOR and the President and Fellows of Harvard College + * ******************************************************************** + */ package edu.harvard.hul.ois.jhove.module.html; import java.util.*; @@ -11,14 +11,11 @@ * Representation of a parsed HTML comment. * * @author Gary McGath - * */ public class JHComment extends JHElement { - /** Constructor. - * Just adds the comment to the element list. */ - public JHComment (List elements, String text) { - super (elements); - } - + /** Constructor. Just adds the comment to the element list. */ + public JHComment(List elements, String text) { + super(elements); + } } diff --git a/jhove-modules/html-hul/src/main/java/edu/harvard/hul/ois/jhove/module/html/JHDoctype.java b/jhove-modules/html-hul/src/main/java/edu/harvard/hul/ois/jhove/module/html/JHDoctype.java index 0c110ca0c..c36b91fb7 100644 --- a/jhove-modules/html-hul/src/main/java/edu/harvard/hul/ois/jhove/module/html/JHDoctype.java +++ b/jhove-modules/html-hul/src/main/java/edu/harvard/hul/ois/jhove/module/html/JHDoctype.java @@ -1,33 +1,31 @@ -/********************************************************************** - * Jhove - JSTOR/Harvard Object Validation Environment - * Copyright 2004 by JSTOR and the President and Fellows of Harvard College +/** + * ******************************************************************** Jhove - JSTOR/Harvard Object + * Validation Environment Copyright 2004 by JSTOR and the President and Fellows of Harvard College * - **********************************************************************/ - + *

******************************************************************** + */ package edu.harvard.hul.ois.jhove.module.html; import java.util.*; /** * Representation of a parsed HTML DOCTYPE. - * - * @author Gary McGath * + * @author Gary McGath */ public class JHDoctype extends JHElement { - /** List of tokens in the DOCTYPE. */ - public List _doctypeElements; + /** List of tokens in the DOCTYPE. */ + public List _doctypeElements; + + /** Constructor. */ + public JHDoctype(List elements, List dtElements) { + super(elements); + _doctypeElements = dtElements; + } - /** Constructor. */ - public JHDoctype (List elements, List dtElements) { - super (elements); - _doctypeElements = dtElements; - } - - /** Returns the doctype token list. */ - public List getDoctypeElements () - { - return _doctypeElements; - } + /** Returns the doctype token list. */ + public List getDoctypeElements() { + return _doctypeElements; + } } diff --git a/jhove-modules/html-hul/src/main/java/edu/harvard/hul/ois/jhove/module/html/JHElement.java b/jhove-modules/html-hul/src/main/java/edu/harvard/hul/ois/jhove/module/html/JHElement.java index b40679b70..a475be7ea 100644 --- a/jhove-modules/html-hul/src/main/java/edu/harvard/hul/ois/jhove/module/html/JHElement.java +++ b/jhove-modules/html-hul/src/main/java/edu/harvard/hul/ois/jhove/module/html/JHElement.java @@ -1,73 +1,69 @@ -/********************************************************************** - * Jhove - JSTOR/Harvard Object Validation Environment - * Copyright 2004 by JSTOR and the President and Fellows of Harvard College - **********************************************************************/ - +/** + * ******************************************************************** Jhove - JSTOR/Harvard Object + * Validation Environment Copyright 2004 by JSTOR and the President and Fellows of Harvard College + * ******************************************************************** + */ package edu.harvard.hul.ois.jhove.module.html; import java.util.*; + /** - * Abstract superclass for the representation of portions of - * an HTML file. This is not the same usage of "element" as the - * SGML definition, but refers to a parsed unit, such as an opening - * tag, closing tag, or PCDATA. + * Abstract superclass for the representation of portions of an HTML file. This is not the same + * usage of "element" as the SGML definition, but refers to a parsed unit, such as an opening tag, + * closing tag, or PCDATA. * * @author Gary McGath - * */ public abstract class JHElement { - public int _column; - public int _line; + public int _column; + public int _line; - /** Constructor. - * - * @param elements List of elements representing the document. - * May be null for a stub element which is implied - * rather than being generated by the parser. - */ - public JHElement (List elements) { - if (elements != null) { - elements.add (this); - } + /** + * Constructor. + * + * @param elements List of elements representing the document. May be null for a stub element + * which is implied rather than being generated by the parser. + */ + public JHElement(List elements) { + if (elements != null) { + elements.add(this); } + } + /** Returns the line number for the start of the element. */ + public int getLine() { + return _line; + } - /** Returns the line number for the start of the element. */ - public int getLine () - { - return _line; - } - - /** Returns the column number for the start of the element. */ - public int getColumn () - { - return _column; - } - - /** Extracts entities from a text string and returns them as - * a List of Strings. - * If there are no entities, returns an empty List. */ - protected List getEntities (String text) - { - List lst = new LinkedList (); - int startIdx = 0; - for (;;) { - // Find the ampersand which starts an entity. - int idx = text.indexOf ("&", startIdx); - if (idx < 0) { - break; // no more occurrences - } - // Find the semicolon which ends the entity. - int semiIdx = text.indexOf (";", idx); - if (semiIdx < 0) { - break; // broken entity, no terminator - } - String ent = text.substring(idx, semiIdx + 1); - lst.add (ent); - // Advance to end of this entity - startIdx = semiIdx; - } - return lst; + /** Returns the column number for the start of the element. */ + public int getColumn() { + return _column; + } + + /** + * Extracts entities from a text string and returns them as a List of Strings. If there are no + * entities, returns an empty List. + */ + protected List getEntities(String text) { + List lst = new LinkedList(); + int startIdx = 0; + for (; ; ) { + // Find the ampersand which starts an entity. + int idx = text.indexOf("&", startIdx); + if (idx < 0) { + break; // no more occurrences + } + // Find the semicolon which ends the entity. + int semiIdx = text.indexOf(";", idx); + if (semiIdx < 0) { + break; // broken entity, no terminator + } + String ent = text.substring(idx, semiIdx + 1); + lst.add(ent); + // Advance to end of this entity + startIdx = semiIdx; } + return lst; + } } diff --git a/jhove-modules/html-hul/src/main/java/edu/harvard/hul/ois/jhove/module/html/JHErrorElement.java b/jhove-modules/html-hul/src/main/java/edu/harvard/hul/ois/jhove/module/html/JHErrorElement.java index e29f79319..e5daaa5af 100644 --- a/jhove-modules/html-hul/src/main/java/edu/harvard/hul/ois/jhove/module/html/JHErrorElement.java +++ b/jhove-modules/html-hul/src/main/java/edu/harvard/hul/ois/jhove/module/html/JHErrorElement.java @@ -1,74 +1,64 @@ -/********************************************************************** - * Jhove - JSTOR/Harvard Object Validation Environment - * Copyright 2004-2005 by JSTOR and the President and Fellows of Harvard College - **********************************************************************/ - +/** + * ******************************************************************** Jhove - JSTOR/Harvard Object + * Validation Environment Copyright 2004-2005 by JSTOR and the President and Fellows of Harvard + * College ******************************************************************** + */ package edu.harvard.hul.ois.jhove.module.html; -import java.util.List; - import edu.harvard.hul.ois.jhove.ErrorMessage; import edu.harvard.hul.ois.jhove.RepInfo; import edu.harvard.hul.ois.jhove.messages.JhoveMessage; +import java.util.List; /** * A JHElement which signifies a syntactic error. * * @author Gary McGath - * */ public class JHErrorElement extends JHElement { - private final JhoveMessage _message; - private String _image; - private boolean _illFormed; - - /** Constructor. - * @param elements List of elements representing the document. - * @param message Message to be reported - * @param image Textual representation of the offending portion. - * This will be used as the submessage of a generated - * ErrorMessage. - * @param illFormed true if the error makes the document - * not well-formed, false if it makes - * it only invalid. - */ - public JHErrorElement (List elements, - JhoveMessage message, - String image, - boolean illFormed) { - super (elements); - _message = message; - _image = image; - _illFormed = illFormed; - } - - - public String getImage () - { - return _image; - } + private final JhoveMessage _message; + private String _image; + private boolean _illFormed; - /** Puts the item's error message into the RepInfo - * object, and affects the wellFormed and valid - * flags as required. Once it's determined that - * a document is not well-formed, error elements indicating - * only invalidity will be ignored. However, additional - * messages that indicate the current level of badness - * (not well-formed or invalid) will continue to be reported.*/ - public void reportError (RepInfo info) - { - // If we're already not well-formed and the error element - // is for invalidity, don't bother with it. - if (info.getWellFormed() == RepInfo.FALSE && !_illFormed) { - return; - } - info.setMessage (new ErrorMessage (_message, _image)); - if (_illFormed) { - info.setWellFormed (false); - } - else { - info.setValid(false); - } + /** + * Constructor. + * + * @param elements List of elements representing the document. + * @param message Message to be reported + * @param image Textual representation of the offending portion. This will be used as the + * submessage of a generated ErrorMessage. + * @param illFormed true if the error makes the document not well-formed, false + * if it makes it only invalid. + */ + public JHErrorElement(List elements, JhoveMessage message, String image, boolean illFormed) { + super(elements); + _message = message; + _image = image; + _illFormed = illFormed; + } + + public String getImage() { + return _image; + } + + /** + * Puts the item's error message into the RepInfo object, and affects the wellFormed and valid + * flags as required. Once it's determined that a document is not well-formed, error elements + * indicating only invalidity will be ignored. However, additional messages that indicate the + * current level of badness (not well-formed or invalid) will continue to be reported. + */ + public void reportError(RepInfo info) { + // If we're already not well-formed and the error element + // is for invalidity, don't bother with it. + if (info.getWellFormed() == RepInfo.FALSE && !_illFormed) { + return; + } + info.setMessage(new ErrorMessage(_message, _image)); + if (_illFormed) { + info.setWellFormed(false); + } else { + info.setValid(false); } + } } diff --git a/jhove-modules/html-hul/src/main/java/edu/harvard/hul/ois/jhove/module/html/JHOpenTag.java b/jhove-modules/html-hul/src/main/java/edu/harvard/hul/ois/jhove/module/html/JHOpenTag.java index efb711a4b..1eddc4848 100644 --- a/jhove-modules/html-hul/src/main/java/edu/harvard/hul/ois/jhove/module/html/JHOpenTag.java +++ b/jhove-modules/html-hul/src/main/java/edu/harvard/hul/ois/jhove/module/html/JHOpenTag.java @@ -1,438 +1,355 @@ -/********************************************************************** - * Jhove - JSTOR/Harvard Object Validation Environment - * Copyright 2004 by JSTOR and the President and Fellows of Harvard College +/** + * ******************************************************************** Jhove - JSTOR/Harvard Object + * Validation Environment Copyright 2004 by JSTOR and the President and Fellows of Harvard College * - **********************************************************************/ - + *

******************************************************************** + */ package edu.harvard.hul.ois.jhove.module.html; import edu.harvard.hul.ois.jhove.*; import edu.harvard.hul.ois.jhove.module.xml.HtmlMetadata; - import java.util.*; -/** Representation of a parsed HTML open tag, including its attributes. - * This arguable would better be called an element, but JHElement is - * the name of the abstract superclass. - * - * @author Gary McGath +/** + * Representation of a parsed HTML open tag, including its attributes. This arguable would better be + * called an element, but JHElement is the name of the abstract superclass. * + * @author Gary McGath */ public class JHOpenTag extends JHElement { - /** Element name. - * Fields are made public to avoid overcomplicating the .jj file */ - public String _name; - /** List of element attributes. Each - * attributes is an array of two strings, the - * name and the value. If no explicit value - * was given, attribute[1] is null. If the - * attribute was in quotes, the quotes are still there. - */ - public List _attributes; - /** Description of the abstract element. */ - private HtmlTagDesc _element; - /** Error message generated by parser, or null. */ - private String _errorMessage; - - /* Index into _element's content array to indicate the position - * currently being matched against. */ - private int _contentIdx; - - /* Number of elements matched at the current content index. */ - private int _elementCount; + /** Element name. Fields are made public to avoid overcomplicating the .jj file */ + public String _name; + /** + * List of element attributes. Each attributes is an array of two strings, the name and the value. + * If no explicit value was given, attribute[1] is null. If the attribute was in quotes, the + * quotes are still there. + */ + public List _attributes; + /** Description of the abstract element. */ + private HtmlTagDesc _element; + /** Error message generated by parser, or null. */ + private String _errorMessage; - /** - * Constructor. - * - * @param elements The list of parsed elements, to which - * this gets added. May be null for a stub - * element not generated by the parser. - * @param name The name of the tag - * @param attrs A List of attributes, representing - * the parsed attributes of the tag. Each - * attributes is an array of two strings, the - * name and the value. If no explicit value - * was given, attribute[1] is null. If the - * attribute was in quotes, the quotes are still there. - * @param line Line number, for information reporting - * @param column Line number, for information reporting - */ - public JHOpenTag (List elements, String name, List attrs, int line, int column) - { - super (elements); - _name = name.toLowerCase (); - _attributes = attrs; - _line = line; - _column = column; - //cleanAttributeQuotes (); - _contentIdx = 0; - _elementCount = 0; - } + /* Index into _element's content array to indicate the position + * currently being matched against. */ + private int _contentIdx; + + /* Number of elements matched at the current content index. */ + private int _elementCount; + + /** + * Constructor. + * + * @param elements The list of parsed elements, to which this gets added. May be null for a stub + * element not generated by the parser. + * @param name The name of the tag + * @param attrs A List of attributes, representing the parsed attributes of the tag. Each + * attributes is an array of two strings, the name and the value. If no explicit value was + * given, attribute[1] is null. If the attribute was in quotes, the quotes are still there. + * @param line Line number, for information reporting + * @param column Line number, for information reporting + */ + public JHOpenTag(List elements, String name, List attrs, int line, int column) { + super(elements); + _name = name.toLowerCase(); + _attributes = attrs; + _line = line; + _column = column; + // cleanAttributeQuotes (); + _contentIdx = 0; + _elementCount = 0; + } + + /** + * Constructor with error message. This is used to allow constructs which are erroneous but common + * -- specifically, the closing of a tag with {@code />}. + * + * @param elements The list of parsed elements, to which this gets added. May be null for a stub + * element not generated by the parser. + * @param name The name of the tag + * @param attrs A List of attributes, representing the parsed attributes of the tag. Each + * attributes is an array of two strings, the name and the value. If no explicit value was + * given, attribute[1] is null. If the attribute was in quotes, the quotes are still there. + * @param message An error message indicating that this element isn't well-formed, but we'll take + * it anyway. + */ + public JHOpenTag(List elements, String name, List attrs, int line, int column, String message) { + this(elements, name, attrs, line, column); + _errorMessage = message; + } + + /** + * Constructor for a stub attribute. This shouldn't ever be used by the parser, but only by the + * module for generating implied elements. + */ + public JHOpenTag(String name) { + super(null); + _name = name; + _attributes = new ArrayList(1); + _contentIdx = 0; + } + + /** + * Associates an the tag with an element definition. This is done by the HTML module, not by the + * parser. + */ + public void setElement(HtmlTagDesc element) { + _element = element; + } + + /** Returns the element definition which has been associated with this tag. */ + public HtmlTagDesc getElement() { + return _element; + } + + /** Returns the tag's name. */ + public String getName() { + return _name; + } + + /** + * Returns the tag's attributes. + * + * @return The attributes as a List. Each attributes is an array of two strings, the name and the + * value. If no explicit value was given, attribute[1] is null. If the attribute was in + * quotes, the quotes are still there. + */ + public List getAttributes() { + return _attributes; + } - /** - * Constructor with error message. - * This is used to allow constructs which are erroneous but common -- - * specifically, the closing of a tag with {@code />}. - * - * @param elements The list of parsed elements, to which - * this gets added. May be null for a stub - * element not generated by the parser. - * @param name The name of the tag - * @param attrs A List of attributes, representing - * the parsed attributes of the tag. Each - * attributes is an array of two strings, the - * name and the value. If no explicit value - * was given, attribute[1] is null. If the - * attribute was in quotes, the quotes are still there. - * @param message An error message indicating that this element - * isn't well-formed, but we'll take it anyway. - */ - public JHOpenTag (List elements, String name, List attrs, - int line, int column, String message) - { - this (elements, name, attrs, line, column); - _errorMessage = message; + /** Process the element to extract any available metadata. */ + protected void processElement(HtmlMetadata mdata) { + if ("html".equals(_name)) { + processHtml(mdata); + } else if ("meta".equals(_name)) { + processMeta(mdata); + } else if ("a".equals(_name)) { + processA(mdata); + } else if ("img".equals(_name)) { + processImg(mdata); + } else if ("frame".equals(_name)) { + processFrame(mdata); + } else if ("script".equals(_name)) { + processScript(mdata); } - - /** Constructor for a stub attribute. This shouldn't ever be used - * by the parser, but only by the module for generating implied - * elements. */ - public JHOpenTag (String name) - { - super (null); - _name = name; - _attributes = new ArrayList (1); - _contentIdx = 0; + + /* Look for certain attributes in any tag. */ + Iterator iter = _attributes.iterator(); + while (iter.hasNext()) { + JHAttribute attr = (JHAttribute) iter.next(); + if ("lang".equals(attr.getName()) && attr.getValue() != null) { + mdata.addLanguage(attr.getValue()); + } } - - /** Associates an the tag with an element definition. This is done - * by the HTML module, not by the parser. */ - public void setElement (HtmlTagDesc element) - { - _element = element; + } + + /** Returns true if the tag given in the parameter is allowable in our context. */ + protected boolean allowsTag(String tag, HtmlDocDesc doc) { + return _element.allowsTag(tag, _contentIdx, doc); + } + + /** Checks if we can accept another element at the current content index. */ + protected boolean canGetMore() { + return _element.canGetMoreAt(_contentIdx, _elementCount); + } + + /** Counts off a component at the current index. */ + protected void countComponent() { + _elementCount++; + } + + /** Increments the value of _contentIdx */ + protected void advanceIndex() { + _contentIdx++; + _elementCount = 0; + } + + /** + * Reports whether it's legal to advance to the next content index. The index is assumed to be + * legal, but the one to which it's trying to advance may not be. + */ + protected boolean canAdvance() { + return _element.canAdvanceFrom(_contentIdx, _elementCount); + } + + /** + * Returns the error message associated with this element. If it returns a non-null value, the tag + * is not well-formed, and the error should be reported. + */ + protected String getErrorMessage() { + return _errorMessage; + } + + /** Processes metadata from an HTML tag */ + private void processHtml(HtmlMetadata mdata) { + String lang = null; + Iterator iter = _attributes.iterator(); + while (iter.hasNext()) { + JHAttribute attr = (JHAttribute) iter.next(); + if ("lang".equals(attr.getName())) { + lang = attr.getValue(); + } } - - /** Returns the element definition which has been associated with - * this tag. */ - public HtmlTagDesc getElement () - { - return _element; + if (lang != null) { + mdata.setLanguage(lang); } + } - - /** Returns the tag's name. */ - public String getName () - { - return _name; + /** Processes metadata from a META tag */ + private void processMeta(HtmlMetadata mdata) { + String name = null; + String httpeq = null; + String content = null; + Iterator iter = _attributes.iterator(); + while (iter.hasNext()) { + JHAttribute attr = (JHAttribute) iter.next(); + String attname = attr.getName(); + String attval = attr.getValue(); + if ("name".equals(attname)) { + name = attval; + } + if ("http-equiv".equals(attname)) { + httpeq = attval; + } + if ("content".equals(attname)) { + content = attval; + } } - - /** Returns the tag's attributes. - * - * @return The attributes as a List. Each - * attributes is an array of two strings, the - * name and the value. If no explicit value - * was given, attribute[1] is null. If the - * attribute was in quotes, the quotes are still there. - */ - public List getAttributes () - { - return _attributes; + if (name != null || httpeq != null || content != null) { + List plist = new ArrayList(3); + if (name != null) { + plist.add(new Property("Name", PropertyType.STRING, name)); + } + if (httpeq != null) { + plist.add(new Property("Httpequiv", PropertyType.STRING, httpeq)); + } + if (content != null) { + plist.add(new Property("Content", PropertyType.STRING, content)); + } + mdata.addMeta(new Property("Meta", PropertyType.PROPERTY, PropertyArity.LIST, plist)); } - - /** Process the element to extract any available metadata. */ - protected void processElement (HtmlMetadata mdata) - { - if ("html".equals (_name)) { - processHtml (mdata); - } - else if ("meta".equals (_name)) { - processMeta (mdata); - } - else if ("a".equals (_name)) { - processA (mdata); - } - else if ("img".equals (_name)) { - processImg (mdata); - } - else if ("frame".equals (_name)) { - processFrame (mdata); + } + + /** + * Processes metadata from an A element. Only elements with an HREF attribute are of interest + * here. We ignore links to anchors. + */ + private void processA(HtmlMetadata mdata) { + Iterator iter = _attributes.iterator(); + while (iter.hasNext()) { + JHAttribute attr = (JHAttribute) iter.next(); + if ("href".equals(attr.getName())) { + String link = attr.getValue(); + if (link.length() > 0 && link.charAt(0) != '#') { + mdata.addLink(link); } - else if ("script".equals (_name)) { - processScript (mdata); + break; + } + } + } + + /** Processes metadata from the IMG element. */ + private void processImg(HtmlMetadata mdata) { + String alt = null; + String longdesc = null; + String src = null; + int height = -1; + int width = -1; + Iterator iter = _attributes.iterator(); + while (iter.hasNext()) { + JHAttribute attr = (JHAttribute) iter.next(); + String attname = attr.getName(); + String attval = attr.getValue(); + if ("alt".equals(attname)) { + alt = attval; + } else if ("src".equals(attname)) { + src = attval; + } else if ("longdesc".equals(attname)) { + longdesc = attval; + } else if ("height".equals(attname)) { + try { + height = Integer.parseInt(attval); + } catch (Exception e) { } - - /* Look for certain attributes in any tag. */ - Iterator iter = _attributes.iterator (); - while (iter.hasNext ()) { - JHAttribute attr = (JHAttribute) iter.next (); - if ("lang".equals (attr.getName ()) && attr.getValue () != null) { - mdata.addLanguage (attr.getValue ()); - } + } else if ("width".equals(attname)) { + try { + width = Integer.parseInt(attval); + } catch (Exception e) { } + } } - - - /** Returns true if the tag given in the parameter is - * allowable in our context. */ - protected boolean allowsTag (String tag, HtmlDocDesc doc) - { - return _element.allowsTag (tag, _contentIdx, doc); + List plist = new ArrayList(5); + if (alt != null) { + plist.add(new Property("Alt", PropertyType.STRING, alt)); } - - /** Checks if we can accept another element at the current - * content index. */ - protected boolean canGetMore () - { - return _element.canGetMoreAt (_contentIdx, _elementCount); + if (longdesc != null) { + plist.add(new Property("Longdesc", PropertyType.STRING, longdesc)); } - - /** Counts off a component at the current index. */ - protected void countComponent () - { - _elementCount++; + if (src != null) { + plist.add(new Property("Src", PropertyType.STRING, src)); } - - /** Increments the value of _contentIdx */ - protected void advanceIndex () - { - _contentIdx++; - _elementCount = 0; + if (height >= 0) { + plist.add(new Property("Height", PropertyType.INTEGER, new Integer(height))); } - - - /** Reports whether it's legal to advance to the next content - * index. The index is assumed to be legal, but the one - * to which it's trying to advance may not be. */ - protected boolean canAdvance () - { - return _element.canAdvanceFrom (_contentIdx, _elementCount); + if (width >= 0) { + plist.add(new Property("Width", PropertyType.INTEGER, new Integer(width))); } - - /** Returns the error message associated with this element. - * If it returns a non-null value, the tag is not well-formed, - * and the error should be reported. - */ - protected String getErrorMessage () - { - return _errorMessage; + if (!plist.isEmpty()) { + mdata.addImage(new Property("Image", PropertyType.PROPERTY, PropertyArity.LIST, plist)); } + } - - /** Processes metadata from an HTML tag */ - private void processHtml (HtmlMetadata mdata) - { - String lang = null; - Iterator iter = _attributes.iterator (); - while (iter.hasNext ()) { - JHAttribute attr = (JHAttribute) iter.next (); - if ("lang".equals (attr.getName ())) { - lang = attr.getValue (); - } - } - if (lang != null) { - mdata.setLanguage(lang); - } + /** Processes metadata from the FRAME element. */ + private void processFrame(HtmlMetadata mdata) { + String name = null; + String title = null; + String src = null; + String longdesc = null; + Iterator iter = _attributes.iterator(); + while (iter.hasNext()) { + JHAttribute attr = (JHAttribute) iter.next(); + String attname = attr.getName(); + String attval = attr.getValue(); + if ("name".equals(attname)) { + name = attval; + } else if ("title".equals(attname)) { + title = attval; + } else if ("src".equals(attname)) { + src = attval; + } else if ("longdesc".equals(attname)) { + longdesc = attval; + } } - - - /** Processes metadata from a META tag */ - private void processMeta (HtmlMetadata mdata) - { - String name = null; - String httpeq = null; - String content = null; - Iterator iter = _attributes.iterator (); - while (iter.hasNext ()) { - JHAttribute attr = (JHAttribute) iter.next (); - String attname = attr.getName (); - String attval = attr.getValue (); - if ("name".equals (attname)) { - name = attval; - } - if ("http-equiv".equals (attname)) { - httpeq = attval; - } - if ("content".equals (attname)) { - content = attval; - } - } - if (name != null || httpeq != null || content != null) { - List plist = new ArrayList (3); - if (name != null) { - plist.add (new Property ("Name", - PropertyType.STRING, - name)); - } - if (httpeq != null) { - plist.add (new Property ("Httpequiv", - PropertyType.STRING, - httpeq)); - } - if (content != null) { - plist.add (new Property ("Content", - PropertyType.STRING, - content)); - } - mdata.addMeta (new Property ("Meta", - PropertyType.PROPERTY, - PropertyArity.LIST, - plist)); - } + List plist = new ArrayList(4); + if (name != null) { + plist.add(new Property("Name", PropertyType.STRING, name)); } - - /** Processes metadata from an A element. Only elements with an - * HREF attribute are of interest here. We ignore links - * to anchors. */ - private void processA (HtmlMetadata mdata) - { - Iterator iter = _attributes.iterator (); - while (iter.hasNext ()) { - JHAttribute attr = (JHAttribute) iter.next (); - if ("href".equals (attr.getName ())) { - String link = attr.getValue (); - if (link.length() > 0 && link.charAt (0) != '#') { - mdata.addLink (link); - } - break; - } - } + if (title != null) { + plist.add(new Property("Title", PropertyType.STRING, title)); } - - /** Processes metadata from the IMG element. */ - private void processImg (HtmlMetadata mdata) - { - String alt = null; - String longdesc = null; - String src = null; - int height = -1; - int width = -1; - Iterator iter = _attributes.iterator (); - while (iter.hasNext ()) { - JHAttribute attr = (JHAttribute) iter.next (); - String attname = attr.getName (); - String attval = attr.getValue (); - if ("alt".equals (attname)) { - alt = attval; - } - else if ("src".equals (attname)) { - src = attval; - } - else if ("longdesc".equals (attname)) { - longdesc = attval; - } - else if ("height".equals (attname)) { - try { - height = Integer.parseInt(attval); - } - catch (Exception e) {} - } - else if ("width".equals (attname)) { - try { - width = Integer.parseInt(attval); - } - catch (Exception e) {} - } - } - List plist = new ArrayList (5); - if (alt != null) { - plist.add (new Property ("Alt", - PropertyType.STRING, - alt)); - } - if (longdesc != null) { - plist.add (new Property ("Longdesc", - PropertyType.STRING, - longdesc)); - } - if (src != null) { - plist.add (new Property ("Src", - PropertyType.STRING, - src)); - } - if (height >= 0) { - plist.add (new Property ("Height", - PropertyType.INTEGER, - new Integer (height))); - } - if (width >= 0) { - plist.add (new Property ("Width", - PropertyType.INTEGER, - new Integer (width))); - } - if (!plist.isEmpty ()) { - mdata.addImage(new Property ("Image", - PropertyType.PROPERTY, - PropertyArity.LIST, - plist)); - } + if (longdesc != null) { + plist.add(new Property("Longdesc", PropertyType.STRING, longdesc)); } - - /** Processes metadata from the FRAME element. */ - private void processFrame (HtmlMetadata mdata) - { - String name = null; - String title = null; - String src = null; - String longdesc = null; - Iterator iter = _attributes.iterator (); - while (iter.hasNext ()) { - JHAttribute attr = (JHAttribute) iter.next (); - String attname = attr.getName (); - String attval = attr.getValue (); - if ("name".equals (attname)) { - name = attval; - } - else if ("title".equals (attname)) { - title = attval; - } - else if ("src".equals (attname)) { - src = attval; - } - else if ("longdesc".equals (attname)) { - longdesc = attval; - } - } - List plist = new ArrayList (4); - if (name != null) { - plist.add (new Property ("Name", - PropertyType.STRING, - name)); - } - if (title != null) { - plist.add (new Property ("Title", - PropertyType.STRING, - title)); - } - if (longdesc != null) { - plist.add (new Property ("Longdesc", - PropertyType.STRING, - longdesc)); - } - if (src != null) { - plist.add (new Property ("src", - PropertyType.STRING, - src)); - } - if (!plist.isEmpty ()) { - mdata.addFrame(new Property ("Frame", - PropertyType.PROPERTY, - PropertyArity.LIST, - plist)); - } + if (src != null) { + plist.add(new Property("src", PropertyType.STRING, src)); + } + if (!plist.isEmpty()) { + mdata.addFrame(new Property("Frame", PropertyType.PROPERTY, PropertyArity.LIST, plist)); } + } - /** Processes metadata from the SCRIPT element. */ - private void processScript (HtmlMetadata mdata) - { - Iterator iter = _attributes.iterator (); - while (iter.hasNext ()) { - JHAttribute attr = (JHAttribute) iter.next (); - String attname = attr.getName (); - String attval = attr.getValue (); - if ("type".equals (attname) && attval.length() > 0 ) { - mdata.addScript (attval); - } - } + /** Processes metadata from the SCRIPT element. */ + private void processScript(HtmlMetadata mdata) { + Iterator iter = _attributes.iterator(); + while (iter.hasNext()) { + JHAttribute attr = (JHAttribute) iter.next(); + String attname = attr.getName(); + String attval = attr.getValue(); + if ("type".equals(attname) && attval.length() > 0) { + mdata.addScript(attval); + } } + } } diff --git a/jhove-modules/html-hul/src/main/java/edu/harvard/hul/ois/jhove/module/html/JHPCData.java b/jhove-modules/html-hul/src/main/java/edu/harvard/hul/ois/jhove/module/html/JHPCData.java index 01af818f5..f64e1332d 100644 --- a/jhove-modules/html-hul/src/main/java/edu/harvard/hul/ois/jhove/module/html/JHPCData.java +++ b/jhove-modules/html-hul/src/main/java/edu/harvard/hul/ois/jhove/module/html/JHPCData.java @@ -1,102 +1,85 @@ -/********************************************************************** - * Jhove - JSTOR/Harvard Object Validation Environment - * Copyright 2004 by JSTOR and the President and Fellows of Harvard College +/** + * ******************************************************************** Jhove - JSTOR/Harvard Object + * Validation Environment Copyright 2004 by JSTOR and the President and Fellows of Harvard College * - **********************************************************************/ - + *

******************************************************************** + */ package edu.harvard.hul.ois.jhove.module.html; -import java.util.ArrayList; -import java.util.Iterator; -import java.util.List; - import edu.harvard.hul.ois.jhove.Property; import edu.harvard.hul.ois.jhove.PropertyArity; import edu.harvard.hul.ois.jhove.PropertyType; import edu.harvard.hul.ois.jhove.module.utf8.Utf8BlockMarker; import edu.harvard.hul.ois.jhove.module.xml.HtmlMetadata; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; -/** Representation of parsed HTML PCDATA. - * - * @author Gary McGath +/** + * Representation of parsed HTML PCDATA. * + * @author Gary McGath */ public class JHPCData extends JHElement { - public String _text; - - /** - * Constructor. - * - * @param elements The list of parsed elements, to which - * this gets added. May be null for a stub - * element not generated by the parser. - * @param text The name of the tag - * @param line Line number, for information reporting - * @param column Line number, for information reporting - */ - public JHPCData (List elements, String text, int line, int column) { - super (elements); - _text = text; - _line = line; - _column = column; - } - - /** Extracts metadata and entities from the PCData object - * and its stack context. */ - protected void processPCData (HtmlStack elementStack, HtmlMetadata metadata) - { - JHOpenTag tag = elementStack.top (); - String name = tag.getName(); - if ("title".equals (name)) { - metadata.setTitle (_text); - } - else if ("cite".equals (name)) { - metadata.addCitation (_text); - } - else if ("dfn".equals (name)) { - metadata.addDef (_text); - } - else if ("abbr".equals (name)) { - List abbrList = new ArrayList (2); - abbrList.add( (new Property ("Text", - PropertyType.STRING, - _text))); - Iterator iter = tag.getAttributes().iterator (); - while (iter.hasNext ()) { - String[] attr = (String []) iter.next (); - String attname = attr[0]; - String attval = attr[1]; - if ("title".equals (attname)) { - abbrList.add (new Property ("Title", - PropertyType.STRING, - attval)); - break; - } - } - metadata.addAbbr (new Property ("Abbr", - PropertyType.PROPERTY, - PropertyArity.LIST, - abbrList)); + public String _text; + + /** + * Constructor. + * + * @param elements The list of parsed elements, to which this gets added. May be null for a stub + * element not generated by the parser. + * @param text The name of the tag + * @param line Line number, for information reporting + * @param column Line number, for information reporting + */ + public JHPCData(List elements, String text, int line, int column) { + super(elements); + _text = text; + _line = line; + _column = column; + } + + /** Extracts metadata and entities from the PCData object and its stack context. */ + protected void processPCData(HtmlStack elementStack, HtmlMetadata metadata) { + JHOpenTag tag = elementStack.top(); + String name = tag.getName(); + if ("title".equals(name)) { + metadata.setTitle(_text); + } else if ("cite".equals(name)) { + metadata.addCitation(_text); + } else if ("dfn".equals(name)) { + metadata.addDef(_text); + } else if ("abbr".equals(name)) { + List abbrList = new ArrayList(2); + abbrList.add((new Property("Text", PropertyType.STRING, _text))); + Iterator iter = tag.getAttributes().iterator(); + while (iter.hasNext()) { + String[] attr = (String[]) iter.next(); + String attname = attr[0]; + String attval = attr[1]; + if ("title".equals(attname)) { + abbrList.add(new Property("Title", PropertyType.STRING, attval)); + break; } - // Extract the entities and add them to the metadata - Iterator iter = getEntities (_text).iterator (); - Utf8BlockMarker utf8BM = metadata.getUtf8BlockMarker (); - while (iter.hasNext ()) { - String ent = (String) iter.next (); - metadata.addEntity (ent); - // If it's a numerical entity, note which UTF8 block it's in - try { - if (ent.charAt (1) == '#') { - int entval = Integer.parseInt - (ent.substring (2, ent.length() - 1)); - utf8BM.markBlock(entval); - } - } - catch (Exception e) { - // Any exception means it's the wrong kind of entity - } + } + metadata.addAbbr(new Property("Abbr", PropertyType.PROPERTY, PropertyArity.LIST, abbrList)); + } + // Extract the entities and add them to the metadata + Iterator iter = getEntities(_text).iterator(); + Utf8BlockMarker utf8BM = metadata.getUtf8BlockMarker(); + while (iter.hasNext()) { + String ent = (String) iter.next(); + metadata.addEntity(ent); + // If it's a numerical entity, note which UTF8 block it's in + try { + if (ent.charAt(1) == '#') { + int entval = Integer.parseInt(ent.substring(2, ent.length() - 1)); + utf8BM.markBlock(entval); } + } catch (Exception e) { + // Any exception means it's the wrong kind of entity + } } - + } } diff --git a/jhove-modules/html-hul/src/main/java/edu/harvard/hul/ois/jhove/module/html/JHXmlDecl.java b/jhove-modules/html-hul/src/main/java/edu/harvard/hul/ois/jhove/module/html/JHXmlDecl.java index 2723c4043..bfad3c3ae 100644 --- a/jhove-modules/html-hul/src/main/java/edu/harvard/hul/ois/jhove/module/html/JHXmlDecl.java +++ b/jhove-modules/html-hul/src/main/java/edu/harvard/hul/ois/jhove/module/html/JHXmlDecl.java @@ -1,31 +1,28 @@ -/********************************************************************** - * Jhove - JSTOR/Harvard Object Validation Environment - * Copyright 2004 by JSTOR and the President and Fellows of Harvard College - **********************************************************************/ - +/** + * ******************************************************************** Jhove - JSTOR/Harvard Object + * Validation Environment Copyright 2004 by JSTOR and the President and Fellows of Harvard College + * ******************************************************************** + */ package edu.harvard.hul.ois.jhove.module.html; import java.util.List; /** - * Representation of an XML declaration. This class allows - * XHTML files to be examined without choking. The actual - * work is done by the XML module, but first we have to determine - * that it is XHTML. + * Representation of an XML declaration. This class allows XHTML files to be examined without + * choking. The actual work is done by the XML module, but first we have to determine that it is + * XHTML. * * @author Gary McGath - * */ public class JHXmlDecl extends JHElement { - /** Constructor. We don't really care about the content; this is - * just a placeholder. So it has a minimal constructor. - * - * @param elements The list of parsed elements, to which - * this gets added. - */ - public JHXmlDecl (List elements) - { - super (elements); - } + /** + * Constructor. We don't really care about the content; this is just a placeholder. So it has a + * minimal constructor. + * + * @param elements The list of parsed elements, to which this gets added. + */ + public JHXmlDecl(List elements) { + super(elements); + } } diff --git a/jhove-modules/html-hul/src/main/java/edu/harvard/hul/ois/jhove/module/html/MessageConstants.java b/jhove-modules/html-hul/src/main/java/edu/harvard/hul/ois/jhove/module/html/MessageConstants.java index 801248029..d715edfbc 100644 --- a/jhove-modules/html-hul/src/main/java/edu/harvard/hul/ois/jhove/module/html/MessageConstants.java +++ b/jhove-modules/html-hul/src/main/java/edu/harvard/hul/ois/jhove/module/html/MessageConstants.java @@ -5,71 +5,70 @@ import edu.harvard.hul.ois.jhove.messages.JhoveMessages; /** - * Enum used to externalise the XML module message Strings. Using an enum - * INSTANCE as a "trick" to ensure a single instance of the class. String - * constants should be prefixed according to their use in the module: + * Enum used to externalise the XML module message Strings. Using an enum INSTANCE as a "trick" to + * ensure a single instance of the class. String constants should be prefixed according to their use + * in the module: + * *

    - *
  • WRN_ for warning strings, often logger messages.
  • - *
  • INF_ for informational messages.
  • - *
  • ERR_ for error messages that indicate a file is invalid or not well - * formed.
  • + *
  • WRN_ for warning strings, often logger messages. + *
  • INF_ for informational messages. + *
  • ERR_ for error messages that indicate a file is invalid or not well formed. *
- * When adding new messages try to adopt the following order for the naming - * elements: + * + * When adding new messages try to adopt the following order for the naming elements: + * *
    - *
  1. PREFIX: one of the three prefixes from the list above.
  2. - *
  3. ENTITY_NAME: the name of the entity causing the problem.
  4. - *
  5. Problem: a short indicator of the problem type, e.g. MISSING, ILLEGAL, - * etc.
  6. + *
  7. PREFIX: one of the three prefixes from the list above. + *
  8. ENTITY_NAME: the name of the entity causing the problem. + *
  9. Problem: a short indicator of the problem type, e.g. MISSING, ILLEGAL, etc. *
- * The elements should be separated by underscores. The messages currently don't - * follow a consistent vocabulary, that is terms such as invalid, illegal, or - * malformed are used without definition. + * + * The elements should be separated by underscores. The messages currently don't follow a consistent + * vocabulary, that is terms such as invalid, illegal, or malformed are used without definition. * * @author Thomas Ledoux - * */ - public enum MessageConstants { - INSTANCE; - public static final JhoveMessageFactory messageFactory = JhoveMessages - .getInstance("edu.harvard.hul.ois.jhove.module.html.ErrorMessages"); + INSTANCE; + public static final JhoveMessageFactory messageFactory = + JhoveMessages.getInstance("edu.harvard.hul.ois.jhove.module.html.ErrorMessages"); - // From ParseHtml (beware file ParseHtml.java is derived from - // ParseHtml.jj...) + // From ParseHtml (beware file ParseHtml.java is derived from + // ParseHtml.jj...) - // The "Missing return statement in function" message is generated by jacacc - // !!! - // should never occur once the parser is correct... - // public static final String WRN_INCORRECT_AUTO_CLOSED_TAG = "Construction - // with \"/>\" is incorrect except in XHTML"; - public static final String INF_HTML_VER_UNSPPRTD = "This HTML version is currently not supported, falling back to HTML 3.2"; - public static final String INF_EOL_TYPE_UNDET = "Not able to determine type of end of line"; + // The "Missing return statement in function" message is generated by jacacc + // !!! + // should never occur once the parser is correct... + // public static final String WRN_INCORRECT_AUTO_CLOSED_TAG = "Construction + // with \"/>\" is incorrect except in XHTML"; + public static final String INF_HTML_VER_UNSPPRTD = + "This HTML version is currently not supported, falling back to HTML 3.2"; + public static final String INF_EOL_TYPE_UNDET = "Not able to determine type of end of line"; - public static final JhoveMessage HTML_HUL_1 = messageFactory.getMessage("HTML-HUL-1"); - public static final JhoveMessage HTML_HUL_2 = messageFactory.getMessage("HTML-HUL-2"); - public static final JhoveMessage HTML_HUL_3 = messageFactory.getMessage("HTML-HUL-3"); - public static final JhoveMessage HTML_HUL_4 = messageFactory.getMessage("HTML-HUL-4"); - public static final JhoveMessage HTML_HUL_5 = messageFactory.getMessage("HTML-HUL-5"); - public static final JhoveMessage HTML_HUL_6 = messageFactory.getMessage("HTML-HUL-6"); - public static final JhoveMessage HTML_HUL_7 = messageFactory.getMessage("HTML-HUL-7"); - public static final JhoveMessage HTML_HUL_8 = messageFactory.getMessage("HTML-HUL-8"); - public static final JhoveMessage HTML_HUL_9 = messageFactory.getMessage("HTML-HUL-9"); - public static final JhoveMessage HTML_HUL_10 = messageFactory.getMessage("HTML-HUL-10"); - public static final JhoveMessage HTML_HUL_11 = messageFactory.getMessage("HTML-HUL-11"); - public static final JhoveMessage HTML_HUL_12 = messageFactory.getMessage("HTML-HUL-12"); - public static final JhoveMessage HTML_HUL_13 = messageFactory.getMessage("HTML-HUL-13"); - public static final JhoveMessage HTML_HUL_14 = messageFactory.getMessage("HTML-HUL-14"); - public static final JhoveMessage HTML_HUL_15 = messageFactory.getMessage("HTML-HUL-15"); - public static final JhoveMessage HTML_HUL_16 = messageFactory.getMessage("HTML-HUL-16"); - public static final JhoveMessage HTML_HUL_17 = messageFactory.getMessage("HTML-HUL-17"); - public static final JhoveMessage HTML_HUL_18 = messageFactory.getMessage("HTML-HUL-18"); - // From TokenMgrError - public static final JhoveMessage HTML_HUL_19 = messageFactory.getMessage("HTML-HUL-19"); - public static final JhoveMessage HTML_HUL_20 = messageFactory.getMessage("HTML-HUL-20"); - public static final JhoveMessage HTML_HUL_21 = messageFactory.getMessage("HTML-HUL-21"); + public static final JhoveMessage HTML_HUL_1 = messageFactory.getMessage("HTML-HUL-1"); + public static final JhoveMessage HTML_HUL_2 = messageFactory.getMessage("HTML-HUL-2"); + public static final JhoveMessage HTML_HUL_3 = messageFactory.getMessage("HTML-HUL-3"); + public static final JhoveMessage HTML_HUL_4 = messageFactory.getMessage("HTML-HUL-4"); + public static final JhoveMessage HTML_HUL_5 = messageFactory.getMessage("HTML-HUL-5"); + public static final JhoveMessage HTML_HUL_6 = messageFactory.getMessage("HTML-HUL-6"); + public static final JhoveMessage HTML_HUL_7 = messageFactory.getMessage("HTML-HUL-7"); + public static final JhoveMessage HTML_HUL_8 = messageFactory.getMessage("HTML-HUL-8"); + public static final JhoveMessage HTML_HUL_9 = messageFactory.getMessage("HTML-HUL-9"); + public static final JhoveMessage HTML_HUL_10 = messageFactory.getMessage("HTML-HUL-10"); + public static final JhoveMessage HTML_HUL_11 = messageFactory.getMessage("HTML-HUL-11"); + public static final JhoveMessage HTML_HUL_12 = messageFactory.getMessage("HTML-HUL-12"); + public static final JhoveMessage HTML_HUL_13 = messageFactory.getMessage("HTML-HUL-13"); + public static final JhoveMessage HTML_HUL_14 = messageFactory.getMessage("HTML-HUL-14"); + public static final JhoveMessage HTML_HUL_15 = messageFactory.getMessage("HTML-HUL-15"); + public static final JhoveMessage HTML_HUL_16 = messageFactory.getMessage("HTML-HUL-16"); + public static final JhoveMessage HTML_HUL_17 = messageFactory.getMessage("HTML-HUL-17"); + public static final JhoveMessage HTML_HUL_18 = messageFactory.getMessage("HTML-HUL-18"); + // From TokenMgrError + public static final JhoveMessage HTML_HUL_19 = messageFactory.getMessage("HTML-HUL-19"); + public static final JhoveMessage HTML_HUL_20 = messageFactory.getMessage("HTML-HUL-20"); + public static final JhoveMessage HTML_HUL_21 = messageFactory.getMessage("HTML-HUL-21"); - public static final JhoveMessage JHOVE_1 = messageFactory.getMessage("JHOVE-1"); - public static final JhoveMessage JHOVE_2 = messageFactory.getMessage("JHOVE-2"); - public static final JhoveMessage JHOVE_3 = messageFactory.getMessage("JHOVE-3"); + public static final JhoveMessage JHOVE_1 = messageFactory.getMessage("JHOVE-1"); + public static final JhoveMessage JHOVE_2 = messageFactory.getMessage("JHOVE-2"); + public static final JhoveMessage JHOVE_3 = messageFactory.getMessage("JHOVE-3"); } diff --git a/jhove-modules/html-hul/src/main/java/edu/harvard/hul/ois/jhove/module/html/ParseException.java b/jhove-modules/html-hul/src/main/java/edu/harvard/hul/ois/jhove/module/html/ParseException.java index e0de1eec7..241ecde76 100644 --- a/jhove-modules/html-hul/src/main/java/edu/harvard/hul/ois/jhove/module/html/ParseException.java +++ b/jhove-modules/html-hul/src/main/java/edu/harvard/hul/ois/jhove/module/html/ParseException.java @@ -2,68 +2,56 @@ package edu.harvard.hul.ois.jhove.module.html; /** - * This exception is thrown when parse errors are encountered. - * You can explicitly create objects of this exception type by - * calling the method generateParseException in the generated - * parser. + * This exception is thrown when parse errors are encountered. You can explicitly create objects of + * this exception type by calling the method generateParseException in the generated parser. * - * You can modify this class to customize your error reporting - * mechanisms so long as you retain the public fields. + *

You can modify this class to customize your error reporting mechanisms so long as you retain + * the public fields. */ public class ParseException extends Exception { - - /** - * This variable determines which constructor was used to create - * this object and thereby affects the semantics of the - * "getMessage" method (see below). + + /** + * This variable determines which constructor was used to create this object and thereby affects + * the semantics of the "getMessage" method (see below). */ protected boolean specialConstructor; /** - * This is the last token that has been consumed successfully. If - * this object has been created due to a parse error, the token - * followng this token will (therefore) be the first error token. + * This is the last token that has been consumed successfully. If this object has been created due + * to a parse error, the token followng this token will (therefore) be the first error token. */ public Token currentToken; /** - * Each entry in this array is an array of integers. Each array - * of integers represents a sequence of tokens (by their ordinal - * values) that is expected at this point of the parse. + * Each entry in this array is an array of integers. Each array of integers represents a sequence + * of tokens (by their ordinal values) that is expected at this point of the parse. */ public int[][] expectedTokenSequences; /** - * This is a reference to the "tokenImage" array of the generated - * parser within which the parse error occurred. This array is - * defined in the generated ...Constants interface. + * This is a reference to the "tokenImage" array of the generated parser within which the parse + * error occurred. This array is defined in the generated ...Constants interface. */ public String[] tokenImage; - - /** - * The end of line string for this machine. - */ + + /** The end of line string for this machine. */ protected String eol = System.getProperty("line.separator", "\n"); /** - * This constructor is used by the method {@code generateParseException} - * in the generated parser. Calling this constructor generates - * a new object of this type with the fields {@code currentToken}, - * {@code expectedTokenSequences}, and {@code tokenImage} set. The boolean - * flag {@code specialConstructor} is also set to true to indicate that - * this constructor was used to create this object. - * This constructor calls its super class with the empty string - * to force the {@code toString} method of parent class {@code Throwable} to - * print the error message in the form: + * This constructor is used by the method {@code generateParseException} in the generated parser. + * Calling this constructor generates a new object of this type with the fields {@code + * currentToken}, {@code expectedTokenSequences}, and {@code tokenImage} set. The boolean flag + * {@code specialConstructor} is also set to true to indicate that this constructor was used to + * create this object. This constructor calls its super class with the empty string to force the + * {@code toString} method of parent class {@code Throwable} to print the error message in the + * form: + * *

    *     {@code ParseException: }
    * 
*/ - public ParseException(Token currentTokenVal, - int[][] expectedTokenSequencesVal, - String[] tokenImageVal - ) - { + public ParseException( + Token currentTokenVal, int[][] expectedTokenSequencesVal, String[] tokenImageVal) { super(""); specialConstructor = true; currentToken = currentTokenVal; @@ -72,15 +60,12 @@ public ParseException(Token currentTokenVal, } /** - * The following constructors are for use by you for whatever - * purpose you can think of. Constructing the exception in this - * manner makes the exception behave in the normal way - i.e., as - * documented in the class "Throwable". The fields "errorToken", - * "expectedTokenSequences", and "tokenImage" do not contain - * relevant information. The JavaCC generated code does not use - * these constructors. + * The following constructors are for use by you for whatever purpose you can think of. + * Constructing the exception in this manner makes the exception behave in the normal way - i.e., + * as documented in the class "Throwable". The fields "errorToken", "expectedTokenSequences", and + * "tokenImage" do not contain relevant information. The JavaCC generated code does not use these + * constructors. */ - public ParseException() { super(); specialConstructor = false; @@ -92,17 +77,14 @@ public ParseException(String message) { } /** - * This method has the standard behavior when this object has been - * created using the standard constructors. Otherwise, it uses - * "currentToken" and "expectedTokenSequences" to generate a parse - * error message and returns it. If this object has been created - * due to a parse error, and you do not catch it (it gets thrown - * from the parser), then this method is called during the printing - * of the final stack trace, and hence the correct error message - * gets displayed. + * This method has the standard behavior when this object has been created using the standard + * constructors. Otherwise, it uses "currentToken" and "expectedTokenSequences" to generate a + * parse error message and returns it. If this object has been created due to a parse error, and + * you do not catch it (it gets thrown from the parser), then this method is called during the + * printing of the final stack trace, and hence the correct error message gets displayed. */ @Override -public String getMessage() { + public String getMessage() { if (!specialConstructor) { return super.getMessage(); } @@ -133,9 +115,13 @@ public String getMessage() { break; } retval.append(add_escapes(tok.image)); - tok = tok.next; + tok = tok.next; } - retval.append("\" at line ").append(currentToken.next.beginLine).append(", column ").append(currentToken.next.beginColumn); + retval + .append("\" at line ") + .append(currentToken.next.beginLine) + .append(", column ") + .append(currentToken.next.beginColumn); retval.append(".").append(eol); if (expectedTokenSequences.length == 1) { retval.append("Was expecting:").append(eol).append(" "); @@ -145,55 +131,52 @@ public String getMessage() { retval.append(expected); return retval.toString(); } - + /** - * Used to convert raw characters to their escaped version - * when these raw version cannot be used as part of an ASCII - * string literal. + * Used to convert raw characters to their escaped version when these raw version cannot be used + * as part of an ASCII string literal. */ protected String add_escapes(String str) { - StringBuffer retval = new StringBuffer(); - char ch; - for (int i = 0; i < str.length(); i++) { - switch (str.charAt(i)) - { - case 0 : - continue; - case '\b': - retval.append("\\b"); - continue; - case '\t': - retval.append("\\t"); - continue; - case '\n': - retval.append("\\n"); - continue; - case '\f': - retval.append("\\f"); - continue; - case '\r': - retval.append("\\r"); - continue; - case '\"': - retval.append("\\\""); - continue; - case '\'': - retval.append("\\\'"); - continue; - case '\\': - retval.append("\\\\"); - continue; - default: - if ((ch = str.charAt(i)) < 0x20 || ch > 0x7e) { - String s = "0000" + Integer.toString(ch, 16); - retval.append("\\u" + s.substring(s.length() - 4, s.length())); - } else { - retval.append(ch); - } - continue; - } + StringBuffer retval = new StringBuffer(); + char ch; + for (int i = 0; i < str.length(); i++) { + switch (str.charAt(i)) { + case 0: + continue; + case '\b': + retval.append("\\b"); + continue; + case '\t': + retval.append("\\t"); + continue; + case '\n': + retval.append("\\n"); + continue; + case '\f': + retval.append("\\f"); + continue; + case '\r': + retval.append("\\r"); + continue; + case '\"': + retval.append("\\\""); + continue; + case '\'': + retval.append("\\\'"); + continue; + case '\\': + retval.append("\\\\"); + continue; + default: + if ((ch = str.charAt(i)) < 0x20 || ch > 0x7e) { + String s = "0000" + Integer.toString(ch, 16); + retval.append("\\u" + s.substring(s.length() - 4, s.length())); + } else { + retval.append(ch); + } + continue; } - return retval.toString(); - } - + } + return retval.toString(); + } } diff --git a/jhove-modules/html-hul/src/main/java/edu/harvard/hul/ois/jhove/module/html/ParseHtml.java b/jhove-modules/html-hul/src/main/java/edu/harvard/hul/ois/jhove/module/html/ParseHtml.java index ce08001c0..4c1b995a7 100644 --- a/jhove-modules/html-hul/src/main/java/edu/harvard/hul/ois/jhove/module/html/ParseHtml.java +++ b/jhove-modules/html-hul/src/main/java/edu/harvard/hul/ois/jhove/module/html/ParseHtml.java @@ -1,242 +1,283 @@ /* Generated By:JavaCC: Do not edit this line. ParseHtml.java */ -/** Caution: Changes made by hand to fix bugs. Be sure to reenter - * these fixes if the file is regenerated. +/** + * Caution: Changes made by hand to fix bugs. Be sure to reenter these fixes if the file is + * regenerated. */ package edu.harvard.hul.ois.jhove.module.html; +import edu.harvard.hul.ois.jhove.module.HtmlModule; import java.util.LinkedList; import java.util.List; -import edu.harvard.hul.ois.jhove.module.HtmlModule; - public class ParseHtml implements ParseHtmlConstants { - private List elements; + private List elements; - public List getElements () - { - return elements; - } + public List getElements() { + return elements; + } - final public List HtmlDoc() throws ParseException { - elements = new LinkedList (); + public final List HtmlDoc() throws ParseException { + elements = new LinkedList(); label_1: while (true) { - switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { - case STARTDOCTYPE: - case LABRACKET: - case PCDATA: - ; - break; - default: - jj_la1[0] = jj_gen; - break label_1; + switch ((jj_ntk == -1) ? jj_ntk() : jj_ntk) { + case STARTDOCTYPE: + case LABRACKET: + case PCDATA:; + break; + default: + jj_la1[0] = jj_gen; + break label_1; } Element(elements); } jj_consume_token(0); - {if (true) return elements;} + { + if (true) return elements; + } throw new Error("Missing return statement in function"); } - final public JHElement Element(List elements) { + public final JHElement Element(List elements) { JHElement elem; try { if (jj_2_1(2)) { elem = Doctype(); - {if (true) return elem;} + { + if (true) return elem; + } } else if (jj_2_2(2)) { elem = OpenTag(); - {if (true) return elem;} + { + if (true) return elem; + } } else if (jj_2_3(2)) { elem = CloseTag(); - {if (true) return elem;} + { + if (true) return elem; + } } else { - switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { - case PCDATA: - elem = PCData(); - {if (true) return elem;} - break; - default: - jj_la1[1] = jj_gen; - if (jj_2_4(2)) { - elem = XMLDecl(); - {if (true) return elem;} - } else { - jj_consume_token(-1); - throw new ParseException(); - } + switch ((jj_ntk == -1) ? jj_ntk() : jj_ntk) { + case PCDATA: + elem = PCData(); + { + if (true) return elem; + } + break; + default: + jj_la1[1] = jj_gen; + if (jj_2_4(2)) { + elem = XMLDecl(); + { + if (true) return elem; + } + } else { + jj_consume_token(-1); + throw new ParseException(); + } } } } catch (ParseException e) { - StringBuilder errText = new StringBuilder(); - for (;;) { - token_source.SwitchTo(DEFAULT); - Token tok = getNextToken (); - if (tok.kind == LABRACKET || tok.kind == PCDATA) { - break; - } - errText.append("Text = \"").append(tok.image).append("\", Line = ") - .append(tok.beginLine).append(", Column = ").append(tok.beginColumn); - /****** Added GDM 14-Jun-05 to avoid infinite loop ********/ - if ("".equals (tok.image)) { - break; - } - /******* End Added GDM 14-Jun-05 to avoid infinite loop ********/ + StringBuilder errText = new StringBuilder(); + for (; ; ) { + token_source.SwitchTo(DEFAULT); + Token tok = getNextToken(); + if (tok.kind == LABRACKET || tok.kind == PCDATA) { + break; + } + errText + .append("Text = \"") + .append(tok.image) + .append("\", Line = ") + .append(tok.beginLine) + .append(", Column = ") + .append(tok.beginColumn); + /** **** Added GDM 14-Jun-05 to avoid infinite loop ******* */ + if ("".equals(tok.image)) { + break; } - {if (true) return new JHErrorElement(elements, MessageConstants.HTML_HUL_2, errText.toString(), true);} + /** ***** End Added GDM 14-Jun-05 to avoid infinite loop ******* */ + } + { + if (true) + return new JHErrorElement( + elements, MessageConstants.HTML_HUL_2, errText.toString(), true); + } + } + { + if (true) return elem; } - {if (true) return elem;} throw new Error("Missing return statement in function"); } - final public JHOpenTag OpenTag() throws ParseException { - List attrs = new LinkedList (); + public final JHOpenTag OpenTag() throws ParseException { + List attrs = new LinkedList(); Token name; String slasher; jj_consume_token(LABRACKET); name = Name(); label_2: while (true) { - switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { - case NAME: - ; - break; - default: - jj_la1[2] = jj_gen; - break label_2; + switch ((jj_ntk == -1) ? jj_ntk() : jj_ntk) { + case NAME:; + break; + default: + jj_la1[2] = jj_gen; + break label_2; } Attribute(attrs); } slasher = TagCloser(); - if ("/".equals (slasher)) { - /* This is a special hack so that a tag closed with "/>" will keep - the whole thing from falling apart, yet will generate an error */ - {if (true) return new JHOpenTag (elements, name.image, attrs, - name.beginLine, name.beginColumn, - MessageConstants.HTML_HUL_1.getMessage());} - } - else { - {if (true) return new JHOpenTag (elements, name.image, attrs, - name.beginLine, name.beginColumn);} - } + if ("/".equals(slasher)) { + /* This is a special hack so that a tag closed with "/>" will keep + the whole thing from falling apart, yet will generate an error */ + { + if (true) + return new JHOpenTag( + elements, + name.image, + attrs, + name.beginLine, + name.beginColumn, + MessageConstants.HTML_HUL_1.getMessage()); + } + } else { + { + if (true) + return new JHOpenTag(elements, name.image, attrs, name.beginLine, name.beginColumn); + } + } throw new Error("Missing return statement in function"); } - final public JHXmlDecl XMLDecl() throws ParseException { - List attrs = new LinkedList (); + public final JHXmlDecl XMLDecl() throws ParseException { + List attrs = new LinkedList(); jj_consume_token(LABRACKET); jj_consume_token(QMARK); jj_consume_token(NAME); label_3: while (true) { - switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { - case NAME: - ; - break; - default: - jj_la1[3] = jj_gen; - break label_3; + switch ((jj_ntk == -1) ? jj_ntk() : jj_ntk) { + case NAME:; + break; + default: + jj_la1[3] = jj_gen; + break label_3; } Attribute(attrs); } jj_consume_token(QMARK); jj_consume_token(RABRACKET); - {if (true) return new JHXmlDecl (elements);} + { + if (true) return new JHXmlDecl(elements); + } throw new Error("Missing return statement in function"); } - final public JHCloseTag CloseTag() throws ParseException { + public final JHCloseTag CloseTag() throws ParseException { Token name; jj_consume_token(LABRACKET); jj_consume_token(SLASH); name = Name(); jj_consume_token(RABRACKET); - {if (true) return new JHCloseTag (elements, name.image, - name.beginLine, name.beginColumn);} + { + if (true) return new JHCloseTag(elements, name.image, name.beginLine, name.beginColumn); + } throw new Error("Missing return statement in function"); } - final public JHPCData PCData() throws ParseException { + public final JHPCData PCData() throws ParseException { Token tok = getToken(1); jj_consume_token(PCDATA); - {if (true) return new JHPCData (elements, tok.image, tok.beginLine, tok.beginColumn);} + { + if (true) return new JHPCData(elements, tok.image, tok.beginLine, tok.beginColumn); + } throw new Error("Missing return statement in function"); } - final public JHDoctype Doctype() throws ParseException { - List doctypeElements = new LinkedList (); + public final JHDoctype Doctype() throws ParseException { + List doctypeElements = new LinkedList(); jj_consume_token(STARTDOCTYPE); jj_consume_token(DOCTYPEKEYWORD); label_4: while (true) { - switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { - case NAKEDDTITEM: - case QUOTEDDTITEM: - ; - break; - default: - jj_la1[4] = jj_gen; - break label_4; + switch ((jj_ntk == -1) ? jj_ntk() : jj_ntk) { + case NAKEDDTITEM: + case QUOTEDDTITEM:; + break; + default: + jj_la1[4] = jj_gen; + break label_4; } DoctypeItem(doctypeElements); } jj_consume_token(RABRACKET); - {if (true) return new JHDoctype (elements, doctypeElements);} + { + if (true) return new JHDoctype(elements, doctypeElements); + } throw new Error("Missing return statement in function"); } JHErrorElement ConsumeError() { Token tok = getNextToken(); - return new JHErrorElement (elements, MessageConstants.HTML_HUL_3, tok.image, true); + return new JHErrorElement(elements, MessageConstants.HTML_HUL_3, tok.image, true); } - final public void DoctypeItem(List dtElements) throws ParseException { + public final void DoctypeItem(List dtElements) throws ParseException { Token tok = getToken(1); - switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { - case NAKEDDTITEM: - jj_consume_token(NAKEDDTITEM); - dtElements.add (tok.image); - break; - case QUOTEDDTITEM: - jj_consume_token(QUOTEDDTITEM); - dtElements.add (tok.image); - break; - default: - jj_la1[5] = jj_gen; - jj_consume_token(-1); - throw new ParseException(); + switch ((jj_ntk == -1) ? jj_ntk() : jj_ntk) { + case NAKEDDTITEM: + jj_consume_token(NAKEDDTITEM); + dtElements.add(tok.image); + break; + case QUOTEDDTITEM: + jj_consume_token(QUOTEDDTITEM); + dtElements.add(tok.image); + break; + default: + jj_la1[5] = jj_gen; + jj_consume_token(-1); + throw new ParseException(); } } - final public Token Name() throws ParseException { + public final Token Name() throws ParseException { Token tok = getToken(1); jj_consume_token(NAME); - {if (true) return tok;} + { + if (true) return tok; + } throw new Error("Missing return statement in function"); } - final public String AttrVal() throws ParseException { + public final String AttrVal() throws ParseException { Token tok = getToken(1); - switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { - case NAKEDVALUE: - jj_consume_token(NAKEDVALUE); - {if (true) return tok.image;} - break; - case QUOTEDVALUE: - jj_consume_token(QUOTEDVALUE); - {if (true) return tok.image;} - jj_consume_token(SINGQUOTEDVALUE); - {if (true) return tok.image;} - break; - default: - jj_la1[6] = jj_gen; - jj_consume_token(-1); - throw new ParseException(); + switch ((jj_ntk == -1) ? jj_ntk() : jj_ntk) { + case NAKEDVALUE: + jj_consume_token(NAKEDVALUE); + { + if (true) return tok.image; + } + break; + case QUOTEDVALUE: + jj_consume_token(QUOTEDVALUE); + { + if (true) return tok.image; + } + jj_consume_token(SINGQUOTEDVALUE); + { + if (true) return tok.image; + } + break; + default: + jj_la1[6] = jj_gen; + jj_consume_token(-1); + throw new ParseException(); } throw new Error("Missing return statement in function"); } - final public void Attribute(List attrs) throws ParseException { + public final void Attribute(List attrs) throws ParseException { JHAttribute attval; Token name; Token namespace; @@ -247,28 +288,23 @@ final public void Attribute(List attrs) throws ParseException { name = Name(); jj_consume_token(EQUALS); val = AttrVal(); - attval = new JHAttribute (name.image, namespace.image, val, - name.beginLine, name.beginColumn); + attval = new JHAttribute(name.image, namespace.image, val, name.beginLine, name.beginColumn); attrs.add(attval); } else if (jj_2_6(2)) { namespace = Name(); jj_consume_token(COLON); name = Name(); - attval = new JHAttribute (name.image, namespace.image, - null, - name.beginLine, name.beginColumn); + attval = new JHAttribute(name.image, namespace.image, null, name.beginLine, name.beginColumn); attrs.add(attval); } else if (jj_2_7(2)) { name = Name(); jj_consume_token(EQUALS); val = AttrVal(); - attval = new JHAttribute (name.image, null, val, - name.beginLine, name.beginColumn); + attval = new JHAttribute(name.image, null, val, name.beginLine, name.beginColumn); attrs.add(attval); } else if (jj_2_8(2)) { name = Name(); - attval = new JHAttribute (name.image, null, null, - name.beginLine, name.beginColumn); + attval = new JHAttribute(name.image, null, null, name.beginLine, name.beginColumn); attrs.add(attval); } else { jj_consume_token(-1); @@ -276,141 +312,186 @@ final public void Attribute(List attrs) throws ParseException { } } - final public String TagCloser() throws ParseException { - Token tok = getToken (1); - switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { - case SLASH: - jj_consume_token(SLASH); - jj_consume_token(RABRACKET); - {if (true) return tok.image;} - break; - case RABRACKET: - jj_consume_token(RABRACKET); - {if (true) return tok.image;} - break; - default: - jj_la1[7] = jj_gen; - jj_consume_token(-1); - throw new ParseException(); + public final String TagCloser() throws ParseException { + Token tok = getToken(1); + switch ((jj_ntk == -1) ? jj_ntk() : jj_ntk) { + case SLASH: + jj_consume_token(SLASH); + jj_consume_token(RABRACKET); + { + if (true) return tok.image; + } + break; + case RABRACKET: + jj_consume_token(RABRACKET); + { + if (true) return tok.image; + } + break; + default: + jj_la1[7] = jj_gen; + jj_consume_token(-1); + throw new ParseException(); } throw new Error("Missing return statement in function"); } - final private boolean jj_2_1(int xla) { - jj_la = xla; jj_lastpos = jj_scanpos = token; - try { return !jj_3_1(); } - catch(LookaheadSuccess ls) { return true; } - finally { jj_save(0, xla); } + private final boolean jj_2_1(int xla) { + jj_la = xla; + jj_lastpos = jj_scanpos = token; + try { + return !jj_3_1(); + } catch (LookaheadSuccess ls) { + return true; + } finally { + jj_save(0, xla); + } } - final private boolean jj_2_2(int xla) { - jj_la = xla; jj_lastpos = jj_scanpos = token; - try { return !jj_3_2(); } - catch(LookaheadSuccess ls) { return true; } - finally { jj_save(1, xla); } + private final boolean jj_2_2(int xla) { + jj_la = xla; + jj_lastpos = jj_scanpos = token; + try { + return !jj_3_2(); + } catch (LookaheadSuccess ls) { + return true; + } finally { + jj_save(1, xla); + } } - final private boolean jj_2_3(int xla) { - jj_la = xla; jj_lastpos = jj_scanpos = token; - try { return !jj_3_3(); } - catch(LookaheadSuccess ls) { return true; } - finally { jj_save(2, xla); } + private final boolean jj_2_3(int xla) { + jj_la = xla; + jj_lastpos = jj_scanpos = token; + try { + return !jj_3_3(); + } catch (LookaheadSuccess ls) { + return true; + } finally { + jj_save(2, xla); + } } - final private boolean jj_2_4(int xla) { - jj_la = xla; jj_lastpos = jj_scanpos = token; - try { return !jj_3_4(); } - catch(LookaheadSuccess ls) { return true; } - finally { jj_save(3, xla); } + private final boolean jj_2_4(int xla) { + jj_la = xla; + jj_lastpos = jj_scanpos = token; + try { + return !jj_3_4(); + } catch (LookaheadSuccess ls) { + return true; + } finally { + jj_save(3, xla); + } } - final private boolean jj_2_5(int xla) { - jj_la = xla; jj_lastpos = jj_scanpos = token; - try { return !jj_3_5(); } - catch(LookaheadSuccess ls) { return true; } - finally { jj_save(4, xla); } + private final boolean jj_2_5(int xla) { + jj_la = xla; + jj_lastpos = jj_scanpos = token; + try { + return !jj_3_5(); + } catch (LookaheadSuccess ls) { + return true; + } finally { + jj_save(4, xla); + } } - final private boolean jj_2_6(int xla) { - jj_la = xla; jj_lastpos = jj_scanpos = token; - try { return !jj_3_6(); } - catch(LookaheadSuccess ls) { return true; } - finally { jj_save(5, xla); } + private final boolean jj_2_6(int xla) { + jj_la = xla; + jj_lastpos = jj_scanpos = token; + try { + return !jj_3_6(); + } catch (LookaheadSuccess ls) { + return true; + } finally { + jj_save(5, xla); + } } - final private boolean jj_2_7(int xla) { - jj_la = xla; jj_lastpos = jj_scanpos = token; - try { return !jj_3_7(); } - catch(LookaheadSuccess ls) { return true; } - finally { jj_save(6, xla); } + private final boolean jj_2_7(int xla) { + jj_la = xla; + jj_lastpos = jj_scanpos = token; + try { + return !jj_3_7(); + } catch (LookaheadSuccess ls) { + return true; + } finally { + jj_save(6, xla); + } } - final private boolean jj_2_8(int xla) { - jj_la = xla; jj_lastpos = jj_scanpos = token; - try { return !jj_3_8(); } - catch(LookaheadSuccess ls) { return true; } - finally { jj_save(7, xla); } + private final boolean jj_2_8(int xla) { + jj_la = xla; + jj_lastpos = jj_scanpos = token; + try { + return !jj_3_8(); + } catch (LookaheadSuccess ls) { + return true; + } finally { + jj_save(7, xla); + } } - final private boolean jj_3_6() { + private final boolean jj_3_6() { if (jj_3R_9()) return true; return jj_scan_token(COLON); } - final private boolean jj_3R_9() { + private final boolean jj_3R_9() { return jj_scan_token(NAME); } - final private boolean jj_3R_7() { + private final boolean jj_3R_7() { if (jj_scan_token(LABRACKET)) return true; return jj_scan_token(SLASH); } - final private boolean jj_3_5() { + private final boolean jj_3_5() { if (jj_3R_9()) return true; return jj_scan_token(COLON); } - final private boolean jj_3R_8() { + private final boolean jj_3R_8() { if (jj_scan_token(LABRACKET)) return true; return jj_scan_token(QMARK); } - final private boolean jj_3R_5() { + private final boolean jj_3R_5() { if (jj_scan_token(STARTDOCTYPE)) return true; return jj_scan_token(DOCTYPEKEYWORD); } - final private boolean jj_3R_6() { + private final boolean jj_3R_6() { if (jj_scan_token(LABRACKET)) return true; return jj_3R_9(); } - final private boolean jj_3_8() { + private final boolean jj_3_8() { return jj_3R_9(); } - final private boolean jj_3_4() { + private final boolean jj_3_4() { return jj_3R_8(); } - final private boolean jj_3_3() { + private final boolean jj_3_3() { return jj_3R_7(); } - final private boolean jj_3_2() { + private final boolean jj_3_2() { return jj_3R_6(); } - final private boolean jj_3_7() { + private final boolean jj_3_7() { if (jj_3R_9()) return true; if (jj_scan_token(EQUALS)) return true; return false; } - final private boolean jj_3_1() { + private final boolean jj_3_1() { return jj_3R_5(); } + private final HtmlModule module; public ParseHtmlTokenManager token_source; public Token token, jj_nt; @@ -420,15 +501,21 @@ final private boolean jj_3_1() { public boolean lookingAhead = false; private boolean jj_semLA; private int jj_gen; - final private int[] jj_la1 = new int[8]; - static private int[] jj_la1_0; + private final int[] jj_la1 = new int[8]; + private static int[] jj_la1_0; + static { - jj_la1_0(); - } - private static void jj_la1_0() { - jj_la1_0 = new int[] {0x16,0x10,0x20,0x20,0x18000,0x18000,0x180,0x2800,}; - } - final private JJCalls[] jj_2_rtns = new JJCalls[8]; + jj_la1_0(); + } + + private static void jj_la1_0() { + jj_la1_0 = + new int[] { + 0x16, 0x10, 0x20, 0x20, 0x18000, 0x18000, 0x180, 0x2800, + }; + } + + private final JJCalls[] jj_2_rtns = new JJCalls[8]; private boolean jj_rescan = false; private int jj_gc = 0; @@ -470,7 +557,7 @@ public void ReInit(ParseHtmlTokenManager tm) { for (int i = 0; i < jj_2_rtns.length; i++) jj_2_rtns[i] = new JJCalls(); } - final private Token jj_consume_token(int kind) throws ParseException { + private final Token jj_consume_token(int kind) throws ParseException { Token oldToken; if ((oldToken = token).next != null) token = token.next; else token = token.next = token_source.getNextToken(); @@ -494,9 +581,11 @@ final private Token jj_consume_token(int kind) throws ParseException { throw generateParseException(); } - static private final class LookaheadSuccess extends java.lang.Error { } - final private LookaheadSuccess jj_ls = new LookaheadSuccess(); - final private boolean jj_scan_token(int kind) { + private static final class LookaheadSuccess extends java.lang.Error {} + + private final LookaheadSuccess jj_ls = new LookaheadSuccess(); + + private final boolean jj_scan_token(int kind) { if (jj_scanpos == jj_lastpos) { jj_la--; if (jj_scanpos.next == null) { @@ -508,8 +597,12 @@ final private boolean jj_scan_token(int kind) { jj_scanpos = jj_scanpos.next; } if (jj_rescan) { - int i = 0; Token tok = token; - while (tok != null && tok != jj_scanpos) { i++; tok = tok.next; } + int i = 0; + Token tok = token; + while (tok != null && tok != jj_scanpos) { + i++; + tok = tok.next; + } if (tok != null) jj_add_error_token(kind, i); } if (jj_scanpos.kind != kind) return true; @@ -517,7 +610,7 @@ final private boolean jj_scan_token(int kind) { return false; } - final public Token getNextToken() { + public final Token getNextToken() { if (token.next != null) token = token.next; else token = token.next = token_source.getNextToken(); jj_ntk = -1; @@ -525,7 +618,7 @@ final public Token getNextToken() { return token; } - final public Token getToken(int index) { + public final Token getToken(int index) { Token t = lookingAhead ? jj_scanpos : token; for (int i = 0; i < index; i++) { if (t.next != null) t = t.next; @@ -534,9 +627,9 @@ final public Token getToken(int index) { return t; } - final private int jj_ntk() { - if ((jj_nt=token.next) == null) - return (jj_ntk = (token.next=token_source.getNextToken()).kind); + private final int jj_ntk() { + if ((jj_nt = token.next) == null) + return (jj_ntk = (token.next = token_source.getNextToken()).kind); return (jj_ntk = jj_nt.kind); } @@ -556,8 +649,8 @@ private void jj_add_error_token(int kind, int pos) { jj_expentry[i] = jj_lasttokens[i]; } boolean exists = false; - for (java.util.Enumeration e = jj_expentries.elements(); e.hasMoreElements();) { - int[] oldentry = (int[])(e.nextElement()); + for (java.util.Enumeration e = jj_expentries.elements(); e.hasMoreElements(); ) { + int[] oldentry = (int[]) (e.nextElement()); if (oldentry.length == jj_expentry.length) { exists = true; for (int i = 0; i < jj_expentry.length; i++) { @@ -587,7 +680,7 @@ public ParseException generateParseException() { for (int i = 0; i < 8; i++) { if (jj_la1[i] == jj_gen) { for (int j = 0; j < 32; j++) { - if ((jj_la1_0[i] & (1< jj_gen) { - jj_la = p.arg; jj_lastpos = jj_scanpos = p.first; + jj_la = p.arg; + jj_lastpos = jj_scanpos = p.first; switch (i) { - case 0: jj_3_1(); break; - case 1: jj_3_2(); break; - case 2: jj_3_3(); break; - case 3: jj_3_4(); break; - case 4: jj_3_5(); break; - case 5: jj_3_6(); break; - case 6: jj_3_7(); break; - case 7: jj_3_8(); break; - default : break; + case 0: + jj_3_1(); + break; + case 1: + jj_3_2(); + break; + case 2: + jj_3_3(); + break; + case 3: + jj_3_4(); + break; + case 4: + jj_3_5(); + break; + case 5: + jj_3_6(); + break; + case 6: + jj_3_7(); + break; + case 7: + jj_3_8(); + break; + default: + break; } } p = p.next; @@ -641,13 +750,18 @@ final private void jj_rescan_token() { jj_rescan = false; } - final private void jj_save(int index, int xla) { + private final void jj_save(int index, int xla) { JJCalls p = jj_2_rtns[index]; while (p.gen > jj_gen) { - if (p.next == null) { p = p.next = new JJCalls(); break; } + if (p.next == null) { + p = p.next = new JJCalls(); + break; + } p = p.next; } - p.gen = jj_gen + xla - jj_la; p.first = token; p.arg = xla; + p.gen = jj_gen + xla - jj_la; + p.first = token; + p.arg = xla; } static final class JJCalls { @@ -656,5 +770,4 @@ static final class JJCalls { int arg; JJCalls next; } - } diff --git a/jhove-modules/html-hul/src/main/java/edu/harvard/hul/ois/jhove/module/html/ParseHtmlConstants.java b/jhove-modules/html-hul/src/main/java/edu/harvard/hul/ois/jhove/module/html/ParseHtmlConstants.java index 1263c5b97..33be2d515 100644 --- a/jhove-modules/html-hul/src/main/java/edu/harvard/hul/ois/jhove/module/html/ParseHtmlConstants.java +++ b/jhove-modules/html-hul/src/main/java/edu/harvard/hul/ois/jhove/module/html/ParseHtmlConstants.java @@ -60,5 +60,4 @@ public interface ParseHtmlConstants { "\">\"", "", }; - } diff --git a/jhove-modules/html-hul/src/main/java/edu/harvard/hul/ois/jhove/module/html/ParseHtmlTokenManager.java b/jhove-modules/html-hul/src/main/java/edu/harvard/hul/ois/jhove/module/html/ParseHtmlTokenManager.java index b0f4b30f3..690389e7e 100644 --- a/jhove-modules/html-hul/src/main/java/edu/harvard/hul/ois/jhove/module/html/ParseHtmlTokenManager.java +++ b/jhove-modules/html-hul/src/main/java/edu/harvard/hul/ois/jhove/module/html/ParseHtmlTokenManager.java @@ -1,1162 +1,1058 @@ /* Generated By:JavaCC: Do not edit this line. ParseHtmlTokenManager.java */ package edu.harvard.hul.ois.jhove.module.html; -//import java.util.*; +// import java.util.*; -public class ParseHtmlTokenManager implements ParseHtmlConstants -{ - public java.io.PrintStream debugStream = System.out; - static final long[] jjbitVec0 = { - 0x0L, 0x0L, 0xffffffffffffffffL, 0xffffffffffffffffL -}; +public class ParseHtmlTokenManager implements ParseHtmlConstants { + public java.io.PrintStream debugStream = System.out; + static final long[] jjbitVec0 = {0x0L, 0x0L, 0xffffffffffffffffL, 0xffffffffffffffffL}; static final int[] jjnextStates = { - 2, 3, 5, 6, -}; -public static final String[] jjstrLiteralImages = { -"", "\74\41", "\74", null, null, null, null, null, null, null, "\77", "\57", -"\72", "\76", null, null, null, null, null, null, null, null, null, null, null, null, -null, }; -public static final String[] lexStateNames = { - "DEFAULT", - "IN_PCDATA", - "IN_TAG", - "IN_ATTVALUE", - "IN_DOCTYPE", - "IN_DOCTYPE2", - "IN_COMMENT", - "ENDING_COMMENT", -}; -public static final int[] jjnewLexState = { - -1, 4, 2, 1, 0, -1, 3, 2, 2, 2, -1, -1, -1, 0, 5, -1, -1, -1, -1, -1, -1, -1, 6, 7, -1, - 0, 6, -}; -static final long[] jjtoToken = { - 0x1fff7L, -}; -static final long[] jjtoSkip = { - 0x6be0000L, -}; -static final long[] jjtoSpecial = { - 0x6800000L, -}; -static final long[] jjtoMore = { - 0x1400008L, -}; -protected CharStream input_stream; -private final int[] jjrounds = new int[7]; -private final int[] jjstateSet = new int[14]; -StringBuffer image; -int jjimageLen; -int lengthOfMatch; -protected char curChar; -int curLexState = 0; -int defaultLexState = 0; -int jjnewStateCnt; -int jjround; -int jjmatchedPos; -int jjmatchedKind; - - public void setDebugStream(java.io.PrintStream ds) { debugStream = ds; } -private final int jjStopAtPos(int pos, int kind) -{ - jjmatchedKind = kind; - jjmatchedPos = pos; - return pos + 1; -} -private final int jjMoveStringLiteralDfa0_7() -{ - switch(curChar) - { + 2, 3, 5, 6, + }; + public static final String[] jjstrLiteralImages = { + "", "\74\41", "\74", null, null, null, null, null, null, null, "\77", "\57", "\72", "\76", null, + null, null, null, null, null, null, null, null, null, null, null, null, + }; + public static final String[] lexStateNames = { + "DEFAULT", + "IN_PCDATA", + "IN_TAG", + "IN_ATTVALUE", + "IN_DOCTYPE", + "IN_DOCTYPE2", + "IN_COMMENT", + "ENDING_COMMENT", + }; + public static final int[] jjnewLexState = { + -1, 4, 2, 1, 0, -1, 3, 2, 2, 2, -1, -1, -1, 0, 5, -1, -1, -1, -1, -1, -1, -1, 6, 7, -1, 0, 6, + }; + static final long[] jjtoToken = { + 0x1fff7L, + }; + static final long[] jjtoSkip = { + 0x6be0000L, + }; + static final long[] jjtoSpecial = { + 0x6800000L, + }; + static final long[] jjtoMore = { + 0x1400008L, + }; + protected CharStream input_stream; + private final int[] jjrounds = new int[7]; + private final int[] jjstateSet = new int[14]; + StringBuffer image; + int jjimageLen; + int lengthOfMatch; + protected char curChar; + int curLexState = 0; + int defaultLexState = 0; + int jjnewStateCnt; + int jjround; + int jjmatchedPos; + int jjmatchedKind; + + public void setDebugStream(java.io.PrintStream ds) { + debugStream = ds; + } + + private final int jjStopAtPos(int pos, int kind) { + jjmatchedKind = kind; + jjmatchedPos = pos; + return pos + 1; + } + + private final int jjMoveStringLiteralDfa0_7() { + switch (curChar) { case 62: - return jjStopAtPos(0, 25); - default : - return jjMoveNfa_7(0, 0); - } -} -private final void jjCheckNAdd(int state) -{ - if (jjrounds[state] != jjround) - { + return jjStopAtPos(0, 25); + default: + return jjMoveNfa_7(0, 0); + } + } + + private final void jjCheckNAdd(int state) { + if (jjrounds[state] != jjround) { jjstateSet[jjnewStateCnt++] = state; jjrounds[state] = jjround; - } -} -private final void jjAddStates(int start, int end) -{ - do { + } + } + + private final void jjAddStates(int start, int end) { + do { jjstateSet[jjnewStateCnt++] = jjnextStates[start]; - } while (start++ != end); -} -private final void jjCheckNAddTwoStates(int state1, int state2) -{ - jjCheckNAdd(state1); - jjCheckNAdd(state2); -} + } while (start++ != end); + } -private final int jjMoveNfa_7(int startState, int curPos) -{ - int startsAt = 0; - jjnewStateCnt = 1; - int i = 1; - jjstateSet[0] = startState; - int kind = 0x7fffffff; - for (;;) - { - if (++jjround == 0x7fffffff) - ReInitRounds(); - if (curChar < 64) - { - long l = 1L << curChar; - { - switch(jjstateSet[--i]) - { - case 0: - if ((0xbfffffffffffffffL & l) != 0L) - kind = 26; - break; - default : break; - } - } while(i != startsAt); - } - else if (curChar < 128) - { - { - switch(jjstateSet[--i]) - { - case 0: - kind = 26; - break; - default : break; - } - } while(i != startsAt); - } - else - { - int i2 = (curChar & 0xff) >> 6; - long l2 = 1L << (curChar & 077); - { - switch(jjstateSet[--i]) - { - case 0: - if ((jjbitVec0[i2] & l2) != 0L && kind > 26) - kind = 26; - break; - default : break; - } - } while(i != startsAt); + private final void jjCheckNAddTwoStates(int state1, int state2) { + jjCheckNAdd(state1); + jjCheckNAdd(state2); + } + + private final int jjMoveNfa_7(int startState, int curPos) { + int startsAt = 0; + jjnewStateCnt = 1; + int i = 1; + jjstateSet[0] = startState; + int kind = 0x7fffffff; + for (; ; ) { + if (++jjround == 0x7fffffff) ReInitRounds(); + if (curChar < 64) { + long l = 1L << curChar; + { + switch (jjstateSet[--i]) { + case 0: + if ((0xbfffffffffffffffL & l) != 0L) kind = 26; + break; + default: + break; + } + } + while (i != startsAt) ; + } else if (curChar < 128) { + { + switch (jjstateSet[--i]) { + case 0: + kind = 26; + break; + default: + break; + } + } + while (i != startsAt) ; + } else { + int i2 = (curChar & 0xff) >> 6; + long l2 = 1L << (curChar & 077); + { + switch (jjstateSet[--i]) { + case 0: + if ((jjbitVec0[i2] & l2) != 0L && kind > 26) kind = 26; + break; + default: + break; + } + } + while (i != startsAt) ; } - if (kind != 0x7fffffff) - { - jjmatchedKind = kind; - jjmatchedPos = curPos; - kind = 0x7fffffff; + if (kind != 0x7fffffff) { + jjmatchedKind = kind; + jjmatchedPos = curPos; + kind = 0x7fffffff; } ++curPos; - if ((i = jjnewStateCnt) == (startsAt = 1 - (jjnewStateCnt = startsAt))) - return curPos; - try { curChar = input_stream.readChar(); } - catch(java.io.IOException e) { return curPos; } - } -} -private final static int jjStopStringLiteralDfa_0(int pos, long active0) -{ - switch (pos) - { - default : - return -1; - } -} -private final int jjStartNfa_0(int pos, long active0) -{ - return jjMoveNfa_0(jjStopStringLiteralDfa_0(pos, active0), pos + 1); -} -private final int jjMoveStringLiteralDfa0_0() -{ - switch(curChar) - { + if ((i = jjnewStateCnt) == (startsAt = 1 - (jjnewStateCnt = startsAt))) return curPos; + try { + curChar = input_stream.readChar(); + } catch (java.io.IOException e) { + return curPos; + } + } + } + + private static final int jjStopStringLiteralDfa_0(int pos, long active0) { + switch (pos) { + default: + return -1; + } + } + + private final int jjStartNfa_0(int pos, long active0) { + return jjMoveNfa_0(jjStopStringLiteralDfa_0(pos, active0), pos + 1); + } + + private final int jjMoveStringLiteralDfa0_0() { + switch (curChar) { case 60: - jjmatchedKind = 2; - return jjMoveStringLiteralDfa1_0(0x400002L); - default : - return jjMoveNfa_0(0, 0); - } -} -private final int jjMoveStringLiteralDfa1_0(long active0) -{ - try { curChar = input_stream.readChar(); } - catch(java.io.IOException e) { + jjmatchedKind = 2; + return jjMoveStringLiteralDfa1_0(0x400002L); + default: + return jjMoveNfa_0(0, 0); + } + } + + private final int jjMoveStringLiteralDfa1_0(long active0) { + try { + curChar = input_stream.readChar(); + } catch (java.io.IOException e) { jjStopStringLiteralDfa_0(0, active0); return 1; - } - switch(curChar) - { + } + switch (curChar) { case 33: - if ((active0 & 0x2L) != 0L) - { - jjmatchedKind = 1; - jjmatchedPos = 1; - } - return jjMoveStringLiteralDfa2_0(active0, 0x400000L); - default : - break; - } - return jjStartNfa_0(0, active0); -} -private final int jjMoveStringLiteralDfa2_0(long old0, long active0) -{ - if (((active0 &= old0)) == 0L) - return jjStartNfa_0(0, old0); - try { curChar = input_stream.readChar(); } - catch(java.io.IOException e) { + if ((active0 & 0x2L) != 0L) { + jjmatchedKind = 1; + jjmatchedPos = 1; + } + return jjMoveStringLiteralDfa2_0(active0, 0x400000L); + default: + break; + } + return jjStartNfa_0(0, active0); + } + + private final int jjMoveStringLiteralDfa2_0(long old0, long active0) { + if (((active0 &= old0)) == 0L) return jjStartNfa_0(0, old0); + try { + curChar = input_stream.readChar(); + } catch (java.io.IOException e) { jjStopStringLiteralDfa_0(1, active0); return 2; - } - switch(curChar) - { + } + switch (curChar) { case 45: - return jjMoveStringLiteralDfa3_0(active0, 0x400000L); - default : - break; - } - return jjStartNfa_0(1, active0); -} -private final int jjMoveStringLiteralDfa3_0(long old0, long active0) -{ - if (((active0 &= old0)) == 0L) - return jjStartNfa_0(1, old0); - try { curChar = input_stream.readChar(); } - catch(java.io.IOException e) { + return jjMoveStringLiteralDfa3_0(active0, 0x400000L); + default: + break; + } + return jjStartNfa_0(1, active0); + } + + private final int jjMoveStringLiteralDfa3_0(long old0, long active0) { + if (((active0 &= old0)) == 0L) return jjStartNfa_0(1, old0); + try { + curChar = input_stream.readChar(); + } catch (java.io.IOException e) { jjStopStringLiteralDfa_0(2, active0); return 3; - } - switch(curChar) - { + } + switch (curChar) { case 45: - if ((active0 & 0x400000L) != 0L) - return jjStopAtPos(3, 22); - break; - default : - break; - } - return jjStartNfa_0(2, active0); -} -private final int jjMoveNfa_0(int startState, int curPos) -{ - int startsAt = 0; - jjnewStateCnt = 1; - int i = 1; - jjstateSet[0] = startState; - int kind = 0x7fffffff; - for (;;) - { - if (++jjround == 0x7fffffff) - ReInitRounds(); - if (curChar < 64) - { - long l = 1L << curChar; - { - switch(jjstateSet[--i]) - { - case 0: - if ((0xeffffffeffffc9ffL & l) != 0L) - kind = 3; - break; - default : break; - } - } while(i != startsAt); - } - else if (curChar < 128) - { - { - switch(jjstateSet[--i]) - { - case 0: - kind = 3; - break; - default : break; - } - } while(i != startsAt); - } - else - { - int i2 = (curChar & 0xff) >> 6; - long l2 = 1L << (curChar & 077); - { - switch(jjstateSet[--i]) - { - case 0: - if ((jjbitVec0[i2] & l2) != 0L && kind > 3) - kind = 3; - break; - default : break; - } - } while(i != startsAt); + if ((active0 & 0x400000L) != 0L) return jjStopAtPos(3, 22); + break; + default: + break; + } + return jjStartNfa_0(2, active0); + } + + private final int jjMoveNfa_0(int startState, int curPos) { + int startsAt = 0; + jjnewStateCnt = 1; + int i = 1; + jjstateSet[0] = startState; + int kind = 0x7fffffff; + for (; ; ) { + if (++jjround == 0x7fffffff) ReInitRounds(); + if (curChar < 64) { + long l = 1L << curChar; + { + switch (jjstateSet[--i]) { + case 0: + if ((0xeffffffeffffc9ffL & l) != 0L) kind = 3; + break; + default: + break; + } + } + while (i != startsAt) ; + } else if (curChar < 128) { + { + switch (jjstateSet[--i]) { + case 0: + kind = 3; + break; + default: + break; + } + } + while (i != startsAt) ; + } else { + int i2 = (curChar & 0xff) >> 6; + long l2 = 1L << (curChar & 077); + { + switch (jjstateSet[--i]) { + case 0: + if ((jjbitVec0[i2] & l2) != 0L && kind > 3) kind = 3; + break; + default: + break; + } + } + while (i != startsAt) ; } - if (kind != 0x7fffffff) - { - jjmatchedKind = kind; - jjmatchedPos = curPos; - kind = 0x7fffffff; + if (kind != 0x7fffffff) { + jjmatchedKind = kind; + jjmatchedPos = curPos; + kind = 0x7fffffff; } ++curPos; - if ((i = jjnewStateCnt) == (startsAt = 1 - (jjnewStateCnt = startsAt))) - return curPos; - try { curChar = input_stream.readChar(); } - catch(java.io.IOException e) { return curPos; } - } -} -private final int jjMoveStringLiteralDfa0_4() -{ - switch(curChar) - { + if ((i = jjnewStateCnt) == (startsAt = 1 - (jjnewStateCnt = startsAt))) return curPos; + try { + curChar = input_stream.readChar(); + } catch (java.io.IOException e) { + return curPos; + } + } + } + + private final int jjMoveStringLiteralDfa0_4() { + switch (curChar) { case 47: - return jjStopAtPos(0, 11); + return jjStopAtPos(0, 11); case 58: - return jjStopAtPos(0, 12); + return jjStopAtPos(0, 12); case 63: - return jjStopAtPos(0, 10); + return jjStopAtPos(0, 10); case 68: case 100: - return jjMoveStringLiteralDfa1_4(0x4000L); - default : - return 1; - } -} -private final int jjMoveStringLiteralDfa1_4(long active0) -{ - try { curChar = input_stream.readChar(); } - catch(java.io.IOException e) { + return jjMoveStringLiteralDfa1_4(0x4000L); + default: + return 1; + } + } + + private final int jjMoveStringLiteralDfa1_4(long active0) { + try { + curChar = input_stream.readChar(); + } catch (java.io.IOException e) { return 1; - } - switch(curChar) - { + } + switch (curChar) { case 79: case 111: - return jjMoveStringLiteralDfa2_4(active0, 0x4000L); - default : - return 2; - } -} -private final int jjMoveStringLiteralDfa2_4(long old0, long active0) -{ - if (((active0 &= old0)) == 0L) - return 2; - try { curChar = input_stream.readChar(); } - catch(java.io.IOException e) { + return jjMoveStringLiteralDfa2_4(active0, 0x4000L); + default: + return 2; + } + } + + private final int jjMoveStringLiteralDfa2_4(long old0, long active0) { + if (((active0 &= old0)) == 0L) return 2; + try { + curChar = input_stream.readChar(); + } catch (java.io.IOException e) { return 2; - } - switch(curChar) - { + } + switch (curChar) { case 67: case 99: - return jjMoveStringLiteralDfa3_4(active0, 0x4000L); - default : - return 3; - } -} -private final int jjMoveStringLiteralDfa3_4(long old0, long active0) -{ - if (((active0 &= old0)) == 0L) - return 3; - try { curChar = input_stream.readChar(); } - catch(java.io.IOException e) { + return jjMoveStringLiteralDfa3_4(active0, 0x4000L); + default: + return 3; + } + } + + private final int jjMoveStringLiteralDfa3_4(long old0, long active0) { + if (((active0 &= old0)) == 0L) return 3; + try { + curChar = input_stream.readChar(); + } catch (java.io.IOException e) { return 3; - } - switch(curChar) - { + } + switch (curChar) { case 84: case 116: - return jjMoveStringLiteralDfa4_4(active0, 0x4000L); - default : - return 4; - } -} -private final int jjMoveStringLiteralDfa4_4(long old0, long active0) -{ - if (((active0 &= old0)) == 0L) - return 4; - try { curChar = input_stream.readChar(); } - catch(java.io.IOException e) { + return jjMoveStringLiteralDfa4_4(active0, 0x4000L); + default: + return 4; + } + } + + private final int jjMoveStringLiteralDfa4_4(long old0, long active0) { + if (((active0 &= old0)) == 0L) return 4; + try { + curChar = input_stream.readChar(); + } catch (java.io.IOException e) { return 4; - } - switch(curChar) - { + } + switch (curChar) { case 89: case 121: - return jjMoveStringLiteralDfa5_4(active0, 0x4000L); - default : - return 5; - } -} -private final int jjMoveStringLiteralDfa5_4(long old0, long active0) -{ - if (((active0 &= old0)) == 0L) - return 5; - try { curChar = input_stream.readChar(); } - catch(java.io.IOException e) { + return jjMoveStringLiteralDfa5_4(active0, 0x4000L); + default: + return 5; + } + } + + private final int jjMoveStringLiteralDfa5_4(long old0, long active0) { + if (((active0 &= old0)) == 0L) return 5; + try { + curChar = input_stream.readChar(); + } catch (java.io.IOException e) { return 5; - } - switch(curChar) - { + } + switch (curChar) { case 80: case 112: - return jjMoveStringLiteralDfa6_4(active0, 0x4000L); - default : - return 6; - } -} -private final int jjMoveStringLiteralDfa6_4(long old0, long active0) -{ - if (((active0 &= old0)) == 0L) - return 6; - try { curChar = input_stream.readChar(); } - catch(java.io.IOException e) { + return jjMoveStringLiteralDfa6_4(active0, 0x4000L); + default: + return 6; + } + } + + private final int jjMoveStringLiteralDfa6_4(long old0, long active0) { + if (((active0 &= old0)) == 0L) return 6; + try { + curChar = input_stream.readChar(); + } catch (java.io.IOException e) { return 6; - } - switch(curChar) - { + } + switch (curChar) { case 69: case 101: - if ((active0 & 0x4000L) != 0L) - return jjStopAtPos(6, 14); - break; - default : - return 7; - } - return 7; -} -private final static int jjStopStringLiteralDfa_5(int pos, long active0) -{ - switch (pos) - { - default : - return -1; - } -} -private final int jjStartNfa_5(int pos, long active0) -{ - return jjMoveNfa_5(jjStopStringLiteralDfa_5(pos, active0), pos + 1); -} -private final int jjStartNfaWithStates_5(int pos, int kind, int state) -{ - jjmatchedKind = kind; - jjmatchedPos = pos; - try { curChar = input_stream.readChar(); } - catch(java.io.IOException e) { return pos + 1; } - return jjMoveNfa_5(state, pos + 1); -} -private final int jjMoveStringLiteralDfa0_5() -{ - switch(curChar) - { + if ((active0 & 0x4000L) != 0L) return jjStopAtPos(6, 14); + break; + default: + return 7; + } + return 7; + } + + private static final int jjStopStringLiteralDfa_5(int pos, long active0) { + switch (pos) { + default: + return -1; + } + } + + private final int jjStartNfa_5(int pos, long active0) { + return jjMoveNfa_5(jjStopStringLiteralDfa_5(pos, active0), pos + 1); + } + + private final int jjStartNfaWithStates_5(int pos, int kind, int state) { + jjmatchedKind = kind; + jjmatchedPos = pos; + try { + curChar = input_stream.readChar(); + } catch (java.io.IOException e) { + return pos + 1; + } + return jjMoveNfa_5(state, pos + 1); + } + + private final int jjMoveStringLiteralDfa0_5() { + switch (curChar) { case 62: - return jjStopAtPos(0, 13); - default : - return jjMoveNfa_5(1, 0); - } -} -private final int jjMoveNfa_5(int startState, int curPos) -{ - int[] nextStates; - int startsAt = 0; - jjnewStateCnt = 4; - int i = 1; - jjstateSet[0] = startState; - int j, kind = 0x7fffffff; - for (;;) - { - if (++jjround == 0x7fffffff) - ReInitRounds(); - if (curChar < 64) - { - long l = 1L << curChar; - MatchLoop: do - { - switch(jjstateSet[--i]) - { - case 1: - if ((0xbffffffaffffc9ffL & l) != 0L) - { - if (kind > 15) - kind = 15; - jjCheckNAdd(0); - } - else if (curChar == 34) - jjCheckNAddTwoStates(2, 3); - break; - case 0: - if ((0xbffffffaffffc9ffL & l) == 0L) - break; - if (kind > 15) - kind = 15; - jjCheckNAdd(0); - break; - case 2: - if ((0xfffffffbffffcbffL & l) != 0L) - jjCheckNAddTwoStates(2, 3); - break; - case 3: - if (curChar == 34 && kind > 16) - kind = 16; - break; - default : break; - } - } while(i != startsAt); - } - else if (curChar < 128) - { - long l = 1L << (curChar & 077); - MatchLoop: do - { - switch(jjstateSet[--i]) - { - case 1: - case 0: - if (kind > 15) - kind = 15; - jjCheckNAdd(0); - break; - case 2: - jjAddStates(0, 1); - break; - default : break; - } - } while(i != startsAt); - } - else - { - int i2 = (curChar & 0xff) >> 6; - long l2 = 1L << (curChar & 077); - MatchLoop: do - { - switch(jjstateSet[--i]) - { - case 1: - case 0: - if ((jjbitVec0[i2] & l2) == 0L) - break; - if (kind > 15) - kind = 15; - jjCheckNAdd(0); - break; - case 2: - if ((jjbitVec0[i2] & l2) != 0L) - jjAddStates(0, 1); - break; - default : break; - } - } while(i != startsAt); + return jjStopAtPos(0, 13); + default: + return jjMoveNfa_5(1, 0); + } + } + + private final int jjMoveNfa_5(int startState, int curPos) { + int[] nextStates; + int startsAt = 0; + jjnewStateCnt = 4; + int i = 1; + jjstateSet[0] = startState; + int j, kind = 0x7fffffff; + for (; ; ) { + if (++jjround == 0x7fffffff) ReInitRounds(); + if (curChar < 64) { + long l = 1L << curChar; + MatchLoop: + do { + switch (jjstateSet[--i]) { + case 1: + if ((0xbffffffaffffc9ffL & l) != 0L) { + if (kind > 15) kind = 15; + jjCheckNAdd(0); + } else if (curChar == 34) jjCheckNAddTwoStates(2, 3); + break; + case 0: + if ((0xbffffffaffffc9ffL & l) == 0L) break; + if (kind > 15) kind = 15; + jjCheckNAdd(0); + break; + case 2: + if ((0xfffffffbffffcbffL & l) != 0L) jjCheckNAddTwoStates(2, 3); + break; + case 3: + if (curChar == 34 && kind > 16) kind = 16; + break; + default: + break; + } + } while (i != startsAt); + } else if (curChar < 128) { + long l = 1L << (curChar & 077); + MatchLoop: + do { + switch (jjstateSet[--i]) { + case 1: + case 0: + if (kind > 15) kind = 15; + jjCheckNAdd(0); + break; + case 2: + jjAddStates(0, 1); + break; + default: + break; + } + } while (i != startsAt); + } else { + int i2 = (curChar & 0xff) >> 6; + long l2 = 1L << (curChar & 077); + MatchLoop: + do { + switch (jjstateSet[--i]) { + case 1: + case 0: + if ((jjbitVec0[i2] & l2) == 0L) break; + if (kind > 15) kind = 15; + jjCheckNAdd(0); + break; + case 2: + if ((jjbitVec0[i2] & l2) != 0L) jjAddStates(0, 1); + break; + default: + break; + } + } while (i != startsAt); } - if (kind != 0x7fffffff) - { - jjmatchedKind = kind; - jjmatchedPos = curPos; - kind = 0x7fffffff; + if (kind != 0x7fffffff) { + jjmatchedKind = kind; + jjmatchedPos = curPos; + kind = 0x7fffffff; } ++curPos; - if ((i = jjnewStateCnt) == (startsAt = 4 - (jjnewStateCnt = startsAt))) - return curPos; - try { curChar = input_stream.readChar(); } - catch(java.io.IOException e) { return curPos; } - } -} -private final int jjMoveStringLiteralDfa0_3() -{ - return jjMoveNfa_3(1, 0); -} -private final int jjMoveNfa_3(int startState, int curPos) -{ - int[] nextStates; - int startsAt = 0; - jjnewStateCnt = 7; - int i = 1; - jjstateSet[0] = startState; - int j, kind = 0x7fffffff; - for (;;) - { - if (++jjround == 0x7fffffff) - ReInitRounds(); - if (curChar < 64) - { - long l = 1L << curChar; - MatchLoop: do - { - switch(jjstateSet[--i]) - { - case 1: - if ((0x9fffeffaffffc9ffL & l) != 0L) - { - if (kind > 7) - kind = 7; - jjCheckNAdd(0); - } - else if (curChar == 34) - jjCheckNAddTwoStates(2, 3); - if (curChar == 39) - jjCheckNAddTwoStates(5, 6); - break; - case 0: - if ((0x9fffeffaffffc9ffL & l) == 0L) - break; - if (kind > 7) - kind = 7; - jjCheckNAdd(0); - break; - case 2: - if ((0xfffffffbffffffffL & l) != 0L) - jjCheckNAddTwoStates(2, 3); - break; - case 3: - if (curChar == 34 && kind > 8) - kind = 8; - break; - case 4: - if (curChar == 39) - jjCheckNAddTwoStates(5, 6); - break; - case 5: - if ((0xffffff7fffffffffL & l) != 0L) - jjCheckNAddTwoStates(5, 6); - break; - case 6: - if (curChar == 39 && kind > 9) - kind = 9; - break; - default : break; - } - } while(i != startsAt); + if ((i = jjnewStateCnt) == (startsAt = 4 - (jjnewStateCnt = startsAt))) return curPos; + try { + curChar = input_stream.readChar(); + } catch (java.io.IOException e) { + return curPos; } - else if (curChar < 128) - { - long l = 1L << (curChar & 077); - MatchLoop: do - { - switch(jjstateSet[--i]) - { - case 1: - case 0: - if (kind > 7) - kind = 7; - jjCheckNAdd(0); - break; - case 2: - jjAddStates(0, 1); - break; - case 5: - jjAddStates(2, 3); - break; - default : break; - } - } while(i != startsAt); - } - else - { - int i2 = (curChar & 0xff) >> 6; - long l2 = 1L << (curChar & 077); - MatchLoop: do - { - switch(jjstateSet[--i]) - { - case 1: - case 0: - if ((jjbitVec0[i2] & l2) == 0L) - break; - if (kind > 7) - kind = 7; - jjCheckNAdd(0); - break; - case 2: - if ((jjbitVec0[i2] & l2) != 0L) - jjAddStates(0, 1); - break; - case 5: - if ((jjbitVec0[i2] & l2) != 0L) - jjAddStates(2, 3); - break; - default : break; - } - } while(i != startsAt); + } + } + + private final int jjMoveStringLiteralDfa0_3() { + return jjMoveNfa_3(1, 0); + } + + private final int jjMoveNfa_3(int startState, int curPos) { + int[] nextStates; + int startsAt = 0; + jjnewStateCnt = 7; + int i = 1; + jjstateSet[0] = startState; + int j, kind = 0x7fffffff; + for (; ; ) { + if (++jjround == 0x7fffffff) ReInitRounds(); + if (curChar < 64) { + long l = 1L << curChar; + MatchLoop: + do { + switch (jjstateSet[--i]) { + case 1: + if ((0x9fffeffaffffc9ffL & l) != 0L) { + if (kind > 7) kind = 7; + jjCheckNAdd(0); + } else if (curChar == 34) jjCheckNAddTwoStates(2, 3); + if (curChar == 39) jjCheckNAddTwoStates(5, 6); + break; + case 0: + if ((0x9fffeffaffffc9ffL & l) == 0L) break; + if (kind > 7) kind = 7; + jjCheckNAdd(0); + break; + case 2: + if ((0xfffffffbffffffffL & l) != 0L) jjCheckNAddTwoStates(2, 3); + break; + case 3: + if (curChar == 34 && kind > 8) kind = 8; + break; + case 4: + if (curChar == 39) jjCheckNAddTwoStates(5, 6); + break; + case 5: + if ((0xffffff7fffffffffL & l) != 0L) jjCheckNAddTwoStates(5, 6); + break; + case 6: + if (curChar == 39 && kind > 9) kind = 9; + break; + default: + break; + } + } while (i != startsAt); + } else if (curChar < 128) { + long l = 1L << (curChar & 077); + MatchLoop: + do { + switch (jjstateSet[--i]) { + case 1: + case 0: + if (kind > 7) kind = 7; + jjCheckNAdd(0); + break; + case 2: + jjAddStates(0, 1); + break; + case 5: + jjAddStates(2, 3); + break; + default: + break; + } + } while (i != startsAt); + } else { + int i2 = (curChar & 0xff) >> 6; + long l2 = 1L << (curChar & 077); + MatchLoop: + do { + switch (jjstateSet[--i]) { + case 1: + case 0: + if ((jjbitVec0[i2] & l2) == 0L) break; + if (kind > 7) kind = 7; + jjCheckNAdd(0); + break; + case 2: + if ((jjbitVec0[i2] & l2) != 0L) jjAddStates(0, 1); + break; + case 5: + if ((jjbitVec0[i2] & l2) != 0L) jjAddStates(2, 3); + break; + default: + break; + } + } while (i != startsAt); } - if (kind != 0x7fffffff) - { - jjmatchedKind = kind; - jjmatchedPos = curPos; - kind = 0x7fffffff; + if (kind != 0x7fffffff) { + jjmatchedKind = kind; + jjmatchedPos = curPos; + kind = 0x7fffffff; } ++curPos; - if ((i = jjnewStateCnt) == (startsAt = 7 - (jjnewStateCnt = startsAt))) - return curPos; - try { curChar = input_stream.readChar(); } - catch(java.io.IOException e) { return curPos; } - } -} -private final int jjMoveStringLiteralDfa0_1() -{ - return jjMoveNfa_1(0, 0); -} -private final int jjMoveNfa_1(int startState, int curPos) -{ - int[] nextStates; - int startsAt = 0; - jjnewStateCnt = 1; - int i = 1; - jjstateSet[0] = startState; - int j, kind = 0x7fffffff; - for (;;) - { - if (++jjround == 0x7fffffff) - ReInitRounds(); - if (curChar < 64) - { - long l = 1L << curChar; - MatchLoop: do - { - switch(jjstateSet[--i]) - { - case 0: - if ((0xefffffffffffffffL & l) == 0L) - break; - kind = 4; - jjstateSet[jjnewStateCnt++] = 0; - break; - default : break; - } - } while(i != startsAt); - } - else if (curChar < 128) - { - long l = 1L << (curChar & 077); - MatchLoop: do - { - switch(jjstateSet[--i]) - { - case 0: - kind = 4; - jjstateSet[jjnewStateCnt++] = 0; - break; - default : break; - } - } while(i != startsAt); + if ((i = jjnewStateCnt) == (startsAt = 7 - (jjnewStateCnt = startsAt))) return curPos; + try { + curChar = input_stream.readChar(); + } catch (java.io.IOException e) { + return curPos; } - else - { - int i2 = (curChar & 0xff) >> 6; - long l2 = 1L << (curChar & 077); - MatchLoop: do - { - switch(jjstateSet[--i]) - { - case 0: - if ((jjbitVec0[i2] & l2) == 0L) - break; - if (kind > 4) - kind = 4; - jjstateSet[jjnewStateCnt++] = 0; - break; - default : break; - } - } while(i != startsAt); + } + } + + private final int jjMoveStringLiteralDfa0_1() { + return jjMoveNfa_1(0, 0); + } + + private final int jjMoveNfa_1(int startState, int curPos) { + int[] nextStates; + int startsAt = 0; + jjnewStateCnt = 1; + int i = 1; + jjstateSet[0] = startState; + int j, kind = 0x7fffffff; + for (; ; ) { + if (++jjround == 0x7fffffff) ReInitRounds(); + if (curChar < 64) { + long l = 1L << curChar; + MatchLoop: + do { + switch (jjstateSet[--i]) { + case 0: + if ((0xefffffffffffffffL & l) == 0L) break; + kind = 4; + jjstateSet[jjnewStateCnt++] = 0; + break; + default: + break; + } + } while (i != startsAt); + } else if (curChar < 128) { + long l = 1L << (curChar & 077); + MatchLoop: + do { + switch (jjstateSet[--i]) { + case 0: + kind = 4; + jjstateSet[jjnewStateCnt++] = 0; + break; + default: + break; + } + } while (i != startsAt); + } else { + int i2 = (curChar & 0xff) >> 6; + long l2 = 1L << (curChar & 077); + MatchLoop: + do { + switch (jjstateSet[--i]) { + case 0: + if ((jjbitVec0[i2] & l2) == 0L) break; + if (kind > 4) kind = 4; + jjstateSet[jjnewStateCnt++] = 0; + break; + default: + break; + } + } while (i != startsAt); } - if (kind != 0x7fffffff) - { - jjmatchedKind = kind; - jjmatchedPos = curPos; - kind = 0x7fffffff; + if (kind != 0x7fffffff) { + jjmatchedKind = kind; + jjmatchedPos = curPos; + kind = 0x7fffffff; } ++curPos; - if ((i = jjnewStateCnt) == (startsAt = 1 - (jjnewStateCnt = startsAt))) - return curPos; - try { curChar = input_stream.readChar(); } - catch(java.io.IOException e) { return curPos; } - } -} -private final int jjMoveStringLiteralDfa0_6() -{ - switch(curChar) - { + if ((i = jjnewStateCnt) == (startsAt = 1 - (jjnewStateCnt = startsAt))) return curPos; + try { + curChar = input_stream.readChar(); + } catch (java.io.IOException e) { + return curPos; + } + } + } + + private final int jjMoveStringLiteralDfa0_6() { + switch (curChar) { case 45: - return jjMoveStringLiteralDfa1_6(0x800000L); - default : - return 1; - } -} -private final int jjMoveStringLiteralDfa1_6(long active0) -{ - try { curChar = input_stream.readChar(); } - catch(java.io.IOException e) { + return jjMoveStringLiteralDfa1_6(0x800000L); + default: + return 1; + } + } + + private final int jjMoveStringLiteralDfa1_6(long active0) { + try { + curChar = input_stream.readChar(); + } catch (java.io.IOException e) { return 1; - } - switch(curChar) - { + } + switch (curChar) { case 45: - if ((active0 & 0x800000L) != 0L) - return jjStopAtPos(1, 23); - break; - default : - return 2; - } - return 2; -} -private final int jjStopStringLiteralDfa_2(int pos, long active0) -{ - switch (pos) - { - default : - return -1; - } -} -private final int jjStartNfa_2(int pos, long active0) -{ - return jjMoveNfa_2(jjStopStringLiteralDfa_2(pos, active0), pos + 1); -} -private final int jjStartNfaWithStates_2(int pos, int kind, int state) -{ - jjmatchedKind = kind; - jjmatchedPos = pos; - try { curChar = input_stream.readChar(); } - catch(java.io.IOException e) { return pos + 1; } - return jjMoveNfa_2(state, pos + 1); -} -private final int jjMoveStringLiteralDfa0_2() -{ - switch(curChar) - { + if ((active0 & 0x800000L) != 0L) return jjStopAtPos(1, 23); + break; + default: + return 2; + } + return 2; + } + + private final int jjStopStringLiteralDfa_2(int pos, long active0) { + switch (pos) { + default: + return -1; + } + } + + private final int jjStartNfa_2(int pos, long active0) { + return jjMoveNfa_2(jjStopStringLiteralDfa_2(pos, active0), pos + 1); + } + + private final int jjStartNfaWithStates_2(int pos, int kind, int state) { + jjmatchedKind = kind; + jjmatchedPos = pos; + try { + curChar = input_stream.readChar(); + } catch (java.io.IOException e) { + return pos + 1; + } + return jjMoveNfa_2(state, pos + 1); + } + + private final int jjMoveStringLiteralDfa0_2() { + switch (curChar) { case 47: - return jjStopAtPos(0, 11); + return jjStopAtPos(0, 11); case 58: - return jjStopAtPos(0, 12); + return jjStopAtPos(0, 12); case 62: - return jjStopAtPos(0, 13); + return jjStopAtPos(0, 13); case 63: - return jjStopAtPos(0, 10); - default : - return jjMoveNfa_2(0, 0); - } -} -private final int jjMoveNfa_2(int startState, int curPos) -{ - int[] nextStates; - int startsAt = 0; - jjnewStateCnt = 4; - int i = 1; - jjstateSet[0] = startState; - int j; - int kind = 0x7fffffff; - for (;;) - { - if (++jjround == 0x7fffffff) - ReInitRounds(); - if (curChar < 64) - { - long l = 1L << curChar; - MatchLoop: do - { - switch(jjstateSet[--i]) - { - case 0: - if (curChar != 61) - break; - kind = 6; - jjCheckNAdd(3); - break; - case 1: - if ((0x3ff200000000000L & l) == 0L) - break; - kind = 5; - jjstateSet[jjnewStateCnt++] = 1; - break; - case 3: - if ((0x100002400L & l) == 0L) - break; - kind = 6; - jjCheckNAdd(3); - break; - default : break; - } - } while(i != startsAt); - } - else if (curChar < 128) - { - long l = 1L << (curChar & 077); - MatchLoop: do - { - switch(jjstateSet[--i]) - { - case 0: - case 1: - if ((0x7fffffe07fffffeL & l) == 0L) - break; - if (kind > 5) - kind = 5; - jjCheckNAdd(1); - break; - default : break; - } - } while(i != startsAt); - } - else - { - int i2 = (curChar & 0xff) >> 6; - long l2 = 1L << (curChar & 077); - MatchLoop: do - { - switch(jjstateSet[--i]) - { - default : break; - } - } while(i != startsAt); + return jjStopAtPos(0, 10); + default: + return jjMoveNfa_2(0, 0); + } + } + + private final int jjMoveNfa_2(int startState, int curPos) { + int[] nextStates; + int startsAt = 0; + jjnewStateCnt = 4; + int i = 1; + jjstateSet[0] = startState; + int j; + int kind = 0x7fffffff; + for (; ; ) { + if (++jjround == 0x7fffffff) ReInitRounds(); + if (curChar < 64) { + long l = 1L << curChar; + MatchLoop: + do { + switch (jjstateSet[--i]) { + case 0: + if (curChar != 61) break; + kind = 6; + jjCheckNAdd(3); + break; + case 1: + if ((0x3ff200000000000L & l) == 0L) break; + kind = 5; + jjstateSet[jjnewStateCnt++] = 1; + break; + case 3: + if ((0x100002400L & l) == 0L) break; + kind = 6; + jjCheckNAdd(3); + break; + default: + break; + } + } while (i != startsAt); + } else if (curChar < 128) { + long l = 1L << (curChar & 077); + MatchLoop: + do { + switch (jjstateSet[--i]) { + case 0: + case 1: + if ((0x7fffffe07fffffeL & l) == 0L) break; + if (kind > 5) kind = 5; + jjCheckNAdd(1); + break; + default: + break; + } + } while (i != startsAt); + } else { + int i2 = (curChar & 0xff) >> 6; + long l2 = 1L << (curChar & 077); + MatchLoop: + do { + switch (jjstateSet[--i]) { + default: + break; + } + } while (i != startsAt); } - if (kind != 0x7fffffff) - { - jjmatchedKind = kind; - jjmatchedPos = curPos; - kind = 0x7fffffff; + if (kind != 0x7fffffff) { + jjmatchedKind = kind; + jjmatchedPos = curPos; + kind = 0x7fffffff; } ++curPos; - if ((i = jjnewStateCnt) == (startsAt = 4 - (jjnewStateCnt = startsAt))) - return curPos; - try { curChar = input_stream.readChar(); } - catch(java.io.IOException e) { return curPos; } - } -} + if ((i = jjnewStateCnt) == (startsAt = 4 - (jjnewStateCnt = startsAt))) return curPos; + try { + curChar = input_stream.readChar(); + } catch (java.io.IOException e) { + return curPos; + } + } + } -public ParseHtmlTokenManager(CharStream stream) -{ - input_stream = stream; -} -public ParseHtmlTokenManager(CharStream stream, int lexState) -{ - this(stream); - SwitchTo(lexState); -} -public void ReInit(CharStream stream) -{ - jjmatchedPos = jjnewStateCnt = 0; - curLexState = defaultLexState; - input_stream = stream; - ReInitRounds(); -} -private final void ReInitRounds() -{ - int i; - jjround = 0x80000001; - for (i = 7; i-- > 0;) - jjrounds[i] = 0x80000000; -} -public void ReInit(CharStream stream, int lexState) -{ - ReInit(stream); - SwitchTo(lexState); -} -public void SwitchTo(int lexState) -{ - if (lexState >= 8 || lexState < 0) - throw new TokenMgrError(MessageConstants.HTML_HUL_21.getMessage() + lexState, TokenMgrError.INVALID_LEXICAL_STATE); - curLexState = lexState; -} + public ParseHtmlTokenManager(CharStream stream) { + input_stream = stream; + } + + public ParseHtmlTokenManager(CharStream stream, int lexState) { + this(stream); + SwitchTo(lexState); + } + + public void ReInit(CharStream stream) { + jjmatchedPos = jjnewStateCnt = 0; + curLexState = defaultLexState; + input_stream = stream; + ReInitRounds(); + } + + private final void ReInitRounds() { + int i; + jjround = 0x80000001; + for (i = 7; i-- > 0; ) jjrounds[i] = 0x80000000; + } + + public void ReInit(CharStream stream, int lexState) { + ReInit(stream); + SwitchTo(lexState); + } + + public void SwitchTo(int lexState) { + if (lexState >= 8 || lexState < 0) + throw new TokenMgrError( + MessageConstants.HTML_HUL_21.getMessage() + lexState, + TokenMgrError.INVALID_LEXICAL_STATE); + curLexState = lexState; + } -protected Token jjFillToken() -{ - Token t = Token.newToken(jjmatchedKind); - t.kind = jjmatchedKind; - if (jjmatchedPos < 0) - { - if (image == null) - t.image = ""; - else - t.image = image.toString(); + protected Token jjFillToken() { + Token t = Token.newToken(jjmatchedKind); + t.kind = jjmatchedKind; + if (jjmatchedPos < 0) { + if (image == null) t.image = ""; + else t.image = image.toString(); t.beginLine = t.endLine = input_stream.getBeginLine(); t.beginColumn = t.endColumn = input_stream.getBeginColumn(); - } - else - { + } else { String im = jjstrLiteralImages[jjmatchedKind]; t.image = (im == null) ? input_stream.GetImage() : im; t.beginLine = input_stream.getBeginLine(); t.beginColumn = input_stream.getBeginColumn(); t.endLine = input_stream.getEndLine(); t.endColumn = input_stream.getEndColumn(); - } - return t; -} + } + return t; + } -public Token getNextToken() -{ - int kind; - Token specialToken = null; - Token matchedToken; - int curPos = 0; + public Token getNextToken() { + int kind; + Token specialToken = null; + Token matchedToken; + int curPos = 0; - EOFLoop : - for (;;) - { - try - { - curChar = input_stream.BeginToken(); - } - catch(java.io.IOException e) - { - jjmatchedKind = 0; - matchedToken = jjFillToken(); - matchedToken.specialToken = specialToken; - return matchedToken; - } - image = null; - jjimageLen = 0; + EOFLoop: + for (; ; ) { + try { + curChar = input_stream.BeginToken(); + } catch (java.io.IOException e) { + jjmatchedKind = 0; + matchedToken = jjFillToken(); + matchedToken.specialToken = specialToken; + return matchedToken; + } + image = null; + jjimageLen = 0; - for (;;) - { - switch(curLexState) - { - case 0: - try { input_stream.backup(0); - while (curChar <= 32 && (0x100003600L & (1L << curChar)) != 0L) - curChar = input_stream.BeginToken(); - } - catch (java.io.IOException e1) { continue EOFLoop; } - jjmatchedKind = 0x7fffffff; - jjmatchedPos = 0; - curPos = jjMoveStringLiteralDfa0_0(); - break; - case 1: - jjmatchedKind = 4; - jjmatchedPos = -1; - curPos = 0; - curPos = jjMoveStringLiteralDfa0_1(); - break; - case 2: - try { input_stream.backup(0); - while (curChar <= 32 && (0x100003600L & (1L << curChar)) != 0L) - curChar = input_stream.BeginToken(); - } - catch (java.io.IOException e1) { continue EOFLoop; } - jjmatchedKind = 0x7fffffff; - jjmatchedPos = 0; - curPos = jjMoveStringLiteralDfa0_2(); - break; - case 3: - jjmatchedKind = 0x7fffffff; - jjmatchedPos = 0; - curPos = jjMoveStringLiteralDfa0_3(); - break; - case 4: - try { input_stream.backup(0); - while (curChar <= 32 && (0x100003600L & (1L << curChar)) != 0L) - curChar = input_stream.BeginToken(); - } - catch (java.io.IOException e1) { continue EOFLoop; } - jjmatchedKind = 0x7fffffff; - jjmatchedPos = 0; - curPos = jjMoveStringLiteralDfa0_4(); - break; - case 5: - try { input_stream.backup(0); - while (curChar <= 32 && (0x100003600L & (1L << curChar)) != 0L) - curChar = input_stream.BeginToken(); - } - catch (java.io.IOException e1) { continue EOFLoop; } - jjmatchedKind = 0x7fffffff; - jjmatchedPos = 0; - curPos = jjMoveStringLiteralDfa0_5(); - break; - case 6: - jjmatchedKind = 0x7fffffff; - jjmatchedPos = 0; - curPos = jjMoveStringLiteralDfa0_6(); - if (jjmatchedPos == 0 && jjmatchedKind > 24) - { - jjmatchedKind = 24; - } - break; - case 7: - jjmatchedKind = 0x7fffffff; - jjmatchedPos = 0; - curPos = jjMoveStringLiteralDfa0_7(); - break; - default : - break; - } - if (jjmatchedKind != 0x7fffffff) - { - if (jjmatchedPos + 1 < curPos) - input_stream.backup(curPos - jjmatchedPos - 1); - if ((jjtoToken[jjmatchedKind >> 6] & (1L << (jjmatchedKind & 077))) != 0L) - { - matchedToken = jjFillToken(); - matchedToken.specialToken = specialToken; - TokenLexicalActions(matchedToken); - if (jjnewLexState[jjmatchedKind] != -1) - curLexState = jjnewLexState[jjmatchedKind]; - return matchedToken; + for (; ; ) { + switch (curLexState) { + case 0: + try { + input_stream.backup(0); + while (curChar <= 32 && (0x100003600L & (1L << curChar)) != 0L) + curChar = input_stream.BeginToken(); + } catch (java.io.IOException e1) { + continue EOFLoop; + } + jjmatchedKind = 0x7fffffff; + jjmatchedPos = 0; + curPos = jjMoveStringLiteralDfa0_0(); + break; + case 1: + jjmatchedKind = 4; + jjmatchedPos = -1; + curPos = 0; + curPos = jjMoveStringLiteralDfa0_1(); + break; + case 2: + try { + input_stream.backup(0); + while (curChar <= 32 && (0x100003600L & (1L << curChar)) != 0L) + curChar = input_stream.BeginToken(); + } catch (java.io.IOException e1) { + continue EOFLoop; + } + jjmatchedKind = 0x7fffffff; + jjmatchedPos = 0; + curPos = jjMoveStringLiteralDfa0_2(); + break; + case 3: + jjmatchedKind = 0x7fffffff; + jjmatchedPos = 0; + curPos = jjMoveStringLiteralDfa0_3(); + break; + case 4: + try { + input_stream.backup(0); + while (curChar <= 32 && (0x100003600L & (1L << curChar)) != 0L) + curChar = input_stream.BeginToken(); + } catch (java.io.IOException e1) { + continue EOFLoop; + } + jjmatchedKind = 0x7fffffff; + jjmatchedPos = 0; + curPos = jjMoveStringLiteralDfa0_4(); + break; + case 5: + try { + input_stream.backup(0); + while (curChar <= 32 && (0x100003600L & (1L << curChar)) != 0L) + curChar = input_stream.BeginToken(); + } catch (java.io.IOException e1) { + continue EOFLoop; + } + jjmatchedKind = 0x7fffffff; + jjmatchedPos = 0; + curPos = jjMoveStringLiteralDfa0_5(); + break; + case 6: + jjmatchedKind = 0x7fffffff; + jjmatchedPos = 0; + curPos = jjMoveStringLiteralDfa0_6(); + if (jjmatchedPos == 0 && jjmatchedKind > 24) { + jjmatchedKind = 24; + } + break; + case 7: + jjmatchedKind = 0x7fffffff; + jjmatchedPos = 0; + curPos = jjMoveStringLiteralDfa0_7(); + break; + default: + break; } - else if ((jjtoSkip[jjmatchedKind >> 6] & (1L << (jjmatchedKind & 077))) != 0L) - { - if ((jjtoSpecial[jjmatchedKind >> 6] & (1L << (jjmatchedKind & 077))) != 0L) - { + if (jjmatchedKind != 0x7fffffff) { + if (jjmatchedPos + 1 < curPos) input_stream.backup(curPos - jjmatchedPos - 1); + if ((jjtoToken[jjmatchedKind >> 6] & (1L << (jjmatchedKind & 077))) != 0L) { + matchedToken = jjFillToken(); + matchedToken.specialToken = specialToken; + TokenLexicalActions(matchedToken); + if (jjnewLexState[jjmatchedKind] != -1) curLexState = jjnewLexState[jjmatchedKind]; + return matchedToken; + } else if ((jjtoSkip[jjmatchedKind >> 6] & (1L << (jjmatchedKind & 077))) != 0L) { + if ((jjtoSpecial[jjmatchedKind >> 6] & (1L << (jjmatchedKind & 077))) != 0L) { matchedToken = jjFillToken(); - if (specialToken == null) - specialToken = matchedToken; - else - { - matchedToken.specialToken = specialToken; - specialToken = (specialToken.next = matchedToken); + if (specialToken == null) specialToken = matchedToken; + else { + matchedToken.specialToken = specialToken; + specialToken = (specialToken.next = matchedToken); } SkipLexicalActions(matchedToken); - } - else - SkipLexicalActions(null); - if (jjnewLexState[jjmatchedKind] != -1) - curLexState = jjnewLexState[jjmatchedKind]; - continue EOFLoop; + } else SkipLexicalActions(null); + if (jjnewLexState[jjmatchedKind] != -1) curLexState = jjnewLexState[jjmatchedKind]; + continue EOFLoop; + } + jjimageLen += jjmatchedPos + 1; + if (jjnewLexState[jjmatchedKind] != -1) curLexState = jjnewLexState[jjmatchedKind]; + curPos = 0; + jjmatchedKind = 0x7fffffff; + try { + curChar = input_stream.readChar(); + continue; + } catch (java.io.IOException e1) { + } } - jjimageLen += jjmatchedPos + 1; - if (jjnewLexState[jjmatchedKind] != -1) - curLexState = jjnewLexState[jjmatchedKind]; - curPos = 0; - jjmatchedKind = 0x7fffffff; + int error_line = input_stream.getEndLine(); + int error_column = input_stream.getEndColumn(); + String error_after = null; + boolean EOFSeen = false; try { - curChar = input_stream.readChar(); - continue; + input_stream.readChar(); + input_stream.backup(1); + } catch (java.io.IOException e1) { + EOFSeen = true; + error_after = curPos <= 1 ? "" : input_stream.GetImage(); + if (curChar == '\n' || curChar == '\r') { + error_line++; + error_column = 0; + } else error_column++; } - catch (java.io.IOException e1) { } - } - int error_line = input_stream.getEndLine(); - int error_column = input_stream.getEndColumn(); - String error_after = null; - boolean EOFSeen = false; - try { input_stream.readChar(); input_stream.backup(1); } - catch (java.io.IOException e1) { - EOFSeen = true; - error_after = curPos <= 1 ? "" : input_stream.GetImage(); - if (curChar == '\n' || curChar == '\r') { - error_line++; - error_column = 0; + if (!EOFSeen) { + input_stream.backup(1); + error_after = curPos <= 1 ? "" : input_stream.GetImage(); } - else - error_column++; - } - if (!EOFSeen) { - input_stream.backup(1); - error_after = curPos <= 1 ? "" : input_stream.GetImage(); - } - throw new TokenMgrError(EOFSeen, curLexState, error_line, error_column, error_after, curChar, TokenMgrError.LEXICAL_ERROR); - } + throw new TokenMgrError( + EOFSeen, + curLexState, + error_line, + error_column, + error_after, + curChar, + TokenMgrError.LEXICAL_ERROR); + } + } } -} -void SkipLexicalActions(Token matchedToken) -{ - switch(jjmatchedKind) - { - default : - break; - } -} -void TokenLexicalActions(Token matchedToken) -{ - switch(jjmatchedKind) - { - default : - break; - } -} + void SkipLexicalActions(Token matchedToken) { + switch (jjmatchedKind) { + default: + break; + } + } + + void TokenLexicalActions(Token matchedToken) { + switch (jjmatchedKind) { + default: + break; + } + } } diff --git a/jhove-modules/html-hul/src/main/java/edu/harvard/hul/ois/jhove/module/html/SimpleCharStream.java b/jhove-modules/html-hul/src/main/java/edu/harvard/hul/ois/jhove/module/html/SimpleCharStream.java index ef64b790f..43ac8dfdc 100644 --- a/jhove-modules/html-hul/src/main/java/edu/harvard/hul/ois/jhove/module/html/SimpleCharStream.java +++ b/jhove-modules/html-hul/src/main/java/edu/harvard/hul/ois/jhove/module/html/SimpleCharStream.java @@ -2,12 +2,10 @@ package edu.harvard.hul.ois.jhove.module.html; /** - * An implementation of interface CharStream, where the stream is assumed to - * contain only ASCII characters (without unicode processing). + * An implementation of interface CharStream, where the stream is assumed to contain only ASCII + * characters (without unicode processing). */ - -public class SimpleCharStream -{ +public class SimpleCharStream { public static final boolean staticFlag = false; int bufsize; int available; @@ -28,214 +26,172 @@ public class SimpleCharStream protected int maxNextCharInd = 0; protected int inBuf = 0; - protected void ExpandBuff(boolean wrapAround) - { - char[] newbuffer = new char[bufsize + 2048]; - int newbufline[] = new int[bufsize + 2048]; - int newbufcolumn[] = new int[bufsize + 2048]; - - try - { - if (wrapAround) - { - System.arraycopy(buffer, tokenBegin, newbuffer, 0, bufsize - tokenBegin); - System.arraycopy(buffer, 0, newbuffer, - bufsize - tokenBegin, bufpos); - buffer = newbuffer; - - System.arraycopy(bufline, tokenBegin, newbufline, 0, bufsize - tokenBegin); - System.arraycopy(bufline, 0, newbufline, bufsize - tokenBegin, bufpos); - bufline = newbufline; - - System.arraycopy(bufcolumn, tokenBegin, newbufcolumn, 0, bufsize - tokenBegin); - System.arraycopy(bufcolumn, 0, newbufcolumn, bufsize - tokenBegin, bufpos); - bufcolumn = newbufcolumn; - - maxNextCharInd = (bufpos += (bufsize - tokenBegin)); - } - else - { - System.arraycopy(buffer, tokenBegin, newbuffer, 0, bufsize - tokenBegin); - buffer = newbuffer; - - System.arraycopy(bufline, tokenBegin, newbufline, 0, bufsize - tokenBegin); - bufline = newbufline; - - System.arraycopy(bufcolumn, tokenBegin, newbufcolumn, 0, bufsize - tokenBegin); - bufcolumn = newbufcolumn; - - maxNextCharInd = (bufpos -= tokenBegin); - } - } - catch (Throwable t) - { - throw new Error(t.getMessage()); - } - - - bufsize += 2048; - available = bufsize; - tokenBegin = 0; + protected void ExpandBuff(boolean wrapAround) { + char[] newbuffer = new char[bufsize + 2048]; + int newbufline[] = new int[bufsize + 2048]; + int newbufcolumn[] = new int[bufsize + 2048]; + + try { + if (wrapAround) { + System.arraycopy(buffer, tokenBegin, newbuffer, 0, bufsize - tokenBegin); + System.arraycopy(buffer, 0, newbuffer, bufsize - tokenBegin, bufpos); + buffer = newbuffer; + + System.arraycopy(bufline, tokenBegin, newbufline, 0, bufsize - tokenBegin); + System.arraycopy(bufline, 0, newbufline, bufsize - tokenBegin, bufpos); + bufline = newbufline; + + System.arraycopy(bufcolumn, tokenBegin, newbufcolumn, 0, bufsize - tokenBegin); + System.arraycopy(bufcolumn, 0, newbufcolumn, bufsize - tokenBegin, bufpos); + bufcolumn = newbufcolumn; + + maxNextCharInd = (bufpos += (bufsize - tokenBegin)); + } else { + System.arraycopy(buffer, tokenBegin, newbuffer, 0, bufsize - tokenBegin); + buffer = newbuffer; + + System.arraycopy(bufline, tokenBegin, newbufline, 0, bufsize - tokenBegin); + bufline = newbufline; + + System.arraycopy(bufcolumn, tokenBegin, newbufcolumn, 0, bufsize - tokenBegin); + bufcolumn = newbufcolumn; + + maxNextCharInd = (bufpos -= tokenBegin); + } + } catch (Throwable t) { + throw new Error(t.getMessage()); + } + + bufsize += 2048; + available = bufsize; + tokenBegin = 0; } - protected void FillBuff() throws java.io.IOException - { - if (maxNextCharInd == available) - { - if (available == bufsize) - { - if (tokenBegin > 2048) - { - bufpos = maxNextCharInd = 0; - available = tokenBegin; - } - else if (tokenBegin < 0) - bufpos = maxNextCharInd = 0; - else - ExpandBuff(false); - } - else if (available > tokenBegin) - available = bufsize; - else if ((tokenBegin - available) < 2048) - ExpandBuff(true); - else - available = tokenBegin; - } - - int i; - try { - if ((i = inputStream.read(buffer, maxNextCharInd, - available - maxNextCharInd)) == -1) - { - inputStream.close(); - throw new java.io.IOException(); - } - maxNextCharInd += i; - return; - } - catch(java.io.IOException e) { - --bufpos; - backup(0); - if (tokenBegin == -1) - tokenBegin = bufpos; - throw e; - } + protected void FillBuff() throws java.io.IOException { + if (maxNextCharInd == available) { + if (available == bufsize) { + if (tokenBegin > 2048) { + bufpos = maxNextCharInd = 0; + available = tokenBegin; + } else if (tokenBegin < 0) bufpos = maxNextCharInd = 0; + else ExpandBuff(false); + } else if (available > tokenBegin) available = bufsize; + else if ((tokenBegin - available) < 2048) ExpandBuff(true); + else available = tokenBegin; + } + + int i; + try { + if ((i = inputStream.read(buffer, maxNextCharInd, available - maxNextCharInd)) == -1) { + inputStream.close(); + throw new java.io.IOException(); + } + maxNextCharInd += i; + return; + } catch (java.io.IOException e) { + --bufpos; + backup(0); + if (tokenBegin == -1) tokenBegin = bufpos; + throw e; + } } - public char BeginToken() throws java.io.IOException - { - tokenBegin = -1; - char c = readChar(); - tokenBegin = bufpos; + public char BeginToken() throws java.io.IOException { + tokenBegin = -1; + char c = readChar(); + tokenBegin = bufpos; - return c; + return c; } - protected void UpdateLineColumn(char c) - { - column++; - - if (prevCharIsLF) - { - prevCharIsLF = false; - line += (column = 1); - } - else if (prevCharIsCR) - { - prevCharIsCR = false; - if (c == '\n') - { - prevCharIsLF = true; - } - else - line += (column = 1); - } - - switch (c) - { - case '\r' : - prevCharIsCR = true; - break; - case '\n' : - prevCharIsLF = true; - break; - case '\t' : - column--; - column += (8 - (column & 07)); - break; - default : - break; - } - - bufline[bufpos] = line; - bufcolumn[bufpos] = column; + protected void UpdateLineColumn(char c) { + column++; + + if (prevCharIsLF) { + prevCharIsLF = false; + line += (column = 1); + } else if (prevCharIsCR) { + prevCharIsCR = false; + if (c == '\n') { + prevCharIsLF = true; + } else line += (column = 1); + } + + switch (c) { + case '\r': + prevCharIsCR = true; + break; + case '\n': + prevCharIsLF = true; + break; + case '\t': + column--; + column += (8 - (column & 07)); + break; + default: + break; + } + + bufline[bufpos] = line; + bufcolumn[bufpos] = column; } - public char readChar() throws java.io.IOException - { - if (inBuf > 0) - { - --inBuf; + public char readChar() throws java.io.IOException { + if (inBuf > 0) { + --inBuf; - if (++bufpos == bufsize) - bufpos = 0; + if (++bufpos == bufsize) bufpos = 0; - return buffer[bufpos]; - } + return buffer[bufpos]; + } - if (++bufpos >= maxNextCharInd) - FillBuff(); + if (++bufpos >= maxNextCharInd) FillBuff(); - char c = buffer[bufpos]; + char c = buffer[bufpos]; - UpdateLineColumn(c); - return (c); + UpdateLineColumn(c); + return (c); } /** - * @deprecated + * @deprecated * @see #getEndColumn */ - public int getColumn() { - return bufcolumn[bufpos]; + return bufcolumn[bufpos]; } /** - * @deprecated + * @deprecated * @see #getEndLine */ - public int getLine() { - return bufline[bufpos]; + return bufline[bufpos]; } public int getEndColumn() { - return bufcolumn[bufpos]; + return bufcolumn[bufpos]; } public int getEndLine() { - return bufline[bufpos]; + return bufline[bufpos]; } public int getBeginColumn() { - return bufcolumn[tokenBegin]; + return bufcolumn[tokenBegin]; } public int getBeginLine() { - return bufline[tokenBegin]; + return bufline[tokenBegin]; } public void backup(int amount) { inBuf += amount; - if ((bufpos -= amount) < 0) - bufpos += bufsize; + if ((bufpos -= amount) < 0) bufpos += bufsize; } - public SimpleCharStream(java.io.Reader dstream, int startline, - int startcolumn, int buffersize) - { + public SimpleCharStream(java.io.Reader dstream, int startline, int startcolumn, int buffersize) { inputStream = dstream; line = startline; column = startcolumn - 1; @@ -246,25 +202,20 @@ public SimpleCharStream(java.io.Reader dstream, int startline, bufcolumn = new int[buffersize]; } - public SimpleCharStream(java.io.Reader dstream, int startline, - int startcolumn) - { - this(dstream, startline, startcolumn, 4096); + public SimpleCharStream(java.io.Reader dstream, int startline, int startcolumn) { + this(dstream, startline, startcolumn, 4096); } - public SimpleCharStream(java.io.Reader dstream) - { - this(dstream, 1, 1, 4096); + public SimpleCharStream(java.io.Reader dstream) { + this(dstream, 1, 1, 4096); } - public void ReInit(java.io.Reader dstream, int startline, - int startcolumn, int buffersize) - { + + public void ReInit(java.io.Reader dstream, int startline, int startcolumn, int buffersize) { inputStream = dstream; line = startline; column = startcolumn - 1; - if (buffer == null || buffersize != buffer.length) - { + if (buffer == null || buffersize != buffer.length) { available = bufsize = buffersize; buffer = new char[buffersize]; bufline = new int[buffersize]; @@ -275,128 +226,98 @@ public void ReInit(java.io.Reader dstream, int startline, bufpos = -1; } - public void ReInit(java.io.Reader dstream, int startline, - int startcolumn) - { - ReInit(dstream, startline, startcolumn, 4096); + public void ReInit(java.io.Reader dstream, int startline, int startcolumn) { + ReInit(dstream, startline, startcolumn, 4096); } - public void ReInit(java.io.Reader dstream) - { - ReInit(dstream, 1, 1, 4096); + public void ReInit(java.io.Reader dstream) { + ReInit(dstream, 1, 1, 4096); } - public SimpleCharStream(java.io.InputStream dstream, int startline, - int startcolumn, int buffersize) - { - this(new java.io.InputStreamReader(dstream), startline, startcolumn, 4096); + + public SimpleCharStream( + java.io.InputStream dstream, int startline, int startcolumn, int buffersize) { + this(new java.io.InputStreamReader(dstream), startline, startcolumn, 4096); } - public SimpleCharStream(java.io.InputStream dstream, int startline, - int startcolumn) - { - this(dstream, startline, startcolumn, 4096); + public SimpleCharStream(java.io.InputStream dstream, int startline, int startcolumn) { + this(dstream, startline, startcolumn, 4096); } - public SimpleCharStream(java.io.InputStream dstream) - { - this(dstream, 1, 1, 4096); + public SimpleCharStream(java.io.InputStream dstream) { + this(dstream, 1, 1, 4096); } - public void ReInit(java.io.InputStream dstream, int startline, - int startcolumn, int buffersize) - { - ReInit(new java.io.InputStreamReader(dstream), startline, startcolumn, 4096); + public void ReInit(java.io.InputStream dstream, int startline, int startcolumn, int buffersize) { + ReInit(new java.io.InputStreamReader(dstream), startline, startcolumn, 4096); } - public void ReInit(java.io.InputStream dstream) - { - ReInit(dstream, 1, 1, 4096); + public void ReInit(java.io.InputStream dstream) { + ReInit(dstream, 1, 1, 4096); } - public void ReInit(java.io.InputStream dstream, int startline, - int startcolumn) - { - ReInit(dstream, startline, startcolumn, 4096); + + public void ReInit(java.io.InputStream dstream, int startline, int startcolumn) { + ReInit(dstream, startline, startcolumn, 4096); } - public String GetImage() - { - if (bufpos >= tokenBegin) - return new String(buffer, tokenBegin, bufpos - tokenBegin + 1); - return new String(buffer, tokenBegin, bufsize - tokenBegin) + - new String(buffer, 0, bufpos + 1); + + public String GetImage() { + if (bufpos >= tokenBegin) return new String(buffer, tokenBegin, bufpos - tokenBegin + 1); + return new String(buffer, tokenBegin, bufsize - tokenBegin) + new String(buffer, 0, bufpos + 1); } - public char[] GetSuffix(int len) - { - char[] ret = new char[len]; + public char[] GetSuffix(int len) { + char[] ret = new char[len]; - if ((bufpos + 1) >= len) - System.arraycopy(buffer, bufpos - len + 1, ret, 0, len); - else - { - System.arraycopy(buffer, bufsize - (len - bufpos - 1), ret, 0, - len - bufpos - 1); - System.arraycopy(buffer, 0, ret, len - bufpos - 1, bufpos + 1); - } + if ((bufpos + 1) >= len) System.arraycopy(buffer, bufpos - len + 1, ret, 0, len); + else { + System.arraycopy(buffer, bufsize - (len - bufpos - 1), ret, 0, len - bufpos - 1); + System.arraycopy(buffer, 0, ret, len - bufpos - 1, bufpos + 1); + } - return ret; + return ret; } - public void Done() - { - buffer = null; - bufline = null; - bufcolumn = null; + public void Done() { + buffer = null; + bufline = null; + bufcolumn = null; } - /** - * Method to adjust line and column numbers for the start of a token. - */ - public void adjustBeginLineColumn(int newLine, int newCol) - { - int start = tokenBegin; - int len; - - if (bufpos >= tokenBegin) - { - len = bufpos - tokenBegin + inBuf + 1; - } - else - { - len = bufsize - tokenBegin + bufpos + 1 + inBuf; - } - - int i = 0; - int j = 0; - int k = 0; - int nextColDiff = 0; - int columnDiff = 0; - - while (i < len && - bufline[j = start % bufsize] == bufline[k = ++start % bufsize]) - { - bufline[j] = newLine; - nextColDiff = columnDiff + bufcolumn[k] - bufcolumn[j]; - bufcolumn[j] = newCol + columnDiff; - columnDiff = nextColDiff; - i++; - } - - if (i < len) - { - bufline[j] = newLine++; - bufcolumn[j] = newCol + columnDiff; - - while (i++ < len) - { - if (bufline[j = start % bufsize] != bufline[++start % bufsize]) - bufline[j] = newLine++; - else - bufline[j] = newLine; - } - } - - line = bufline[j]; - column = bufcolumn[j]; - } + /** Method to adjust line and column numbers for the start of a token. */ + public void adjustBeginLineColumn(int newLine, int newCol) { + int start = tokenBegin; + int len; + + if (bufpos >= tokenBegin) { + len = bufpos - tokenBegin + inBuf + 1; + } else { + len = bufsize - tokenBegin + bufpos + 1 + inBuf; + } + + int i = 0; + int j = 0; + int k = 0; + int nextColDiff = 0; + int columnDiff = 0; + + while (i < len && bufline[j = start % bufsize] == bufline[k = ++start % bufsize]) { + bufline[j] = newLine; + nextColDiff = columnDiff + bufcolumn[k] - bufcolumn[j]; + bufcolumn[j] = newCol + columnDiff; + columnDiff = nextColDiff; + i++; + } + if (i < len) { + bufline[j] = newLine++; + bufcolumn[j] = newCol + columnDiff; + + while (i++ < len) { + if (bufline[j = start % bufsize] != bufline[++start % bufsize]) bufline[j] = newLine++; + else bufline[j] = newLine; + } + } + + line = bufline[j]; + column = bufcolumn[j]; + } } diff --git a/jhove-modules/html-hul/src/main/java/edu/harvard/hul/ois/jhove/module/html/Token.java b/jhove-modules/html-hul/src/main/java/edu/harvard/hul/ois/jhove/module/html/Token.java index 52198b38e..0b74fde47 100644 --- a/jhove-modules/html-hul/src/main/java/edu/harvard/hul/ois/jhove/module/html/Token.java +++ b/jhove-modules/html-hul/src/main/java/edu/harvard/hul/ois/jhove/module/html/Token.java @@ -1,82 +1,64 @@ /* Generated By:JavaCC: Do not edit this line. Token.java Version 3.0 */ package edu.harvard.hul.ois.jhove.module.html; -/** - * Describes the input token stream. - */ - +/** Describes the input token stream. */ public class Token { /** - * An integer that describes the kind of this token. This numbering - * system is determined by JavaCCParser, and a table of these numbers is - * stored in the file ...Constants.java. + * An integer that describes the kind of this token. This numbering system is determined by + * JavaCCParser, and a table of these numbers is stored in the file ...Constants.java. */ public int kind; /** - * beginLine and beginColumn describe the position of the first character - * of this token; endLine and endColumn describe the position of the - * last character of this token. + * beginLine and beginColumn describe the position of the first character of this token; endLine + * and endColumn describe the position of the last character of this token. */ public int beginLine, beginColumn, endLine, endColumn; - /** - * The string image of the token. - */ + /** The string image of the token. */ public String image; /** - * A reference to the next regular (non-special) token from the input - * stream. If this is the last token from the input stream, or if the - * token manager has not read tokens beyond this one, this field is - * set to null. This is true only if this token is also a regular - * token. Otherwise, see below for a description of the contents of - * this field. + * A reference to the next regular (non-special) token from the input stream. If this is the last + * token from the input stream, or if the token manager has not read tokens beyond this one, this + * field is set to null. This is true only if this token is also a regular token. Otherwise, see + * below for a description of the contents of this field. */ public Token next; /** - * This field is used to access special tokens that occur prior to this - * token, but after the immediately preceding regular (non-special) token. - * If there are no such special tokens, this field is set to null. - * When there are more than one such special token, this field refers - * to the last of these special tokens, which in turn refers to the next - * previous special token through its specialToken field, and so on - * until the first special token (whose specialToken field is null). - * The next fields of special tokens refer to other special tokens that - * immediately follow it (without an intervening regular token). If there - * is no such token, this field is null. + * This field is used to access special tokens that occur prior to this token, but after the + * immediately preceding regular (non-special) token. If there are no such special tokens, this + * field is set to null. When there are more than one such special token, this field refers to the + * last of these special tokens, which in turn refers to the next previous special token through + * its specialToken field, and so on until the first special token (whose specialToken field is + * null). The next fields of special tokens refer to other special tokens that immediately follow + * it (without an intervening regular token). If there is no such token, this field is null. */ public Token specialToken; - /** - * Returns the image. - */ + /** Returns the image. */ @Override -public String toString() - { - return image; + public String toString() { + return image; } /** - * Returns a new Token object, by default. However, if you want, you - * can create and return subclass objects based on the value of ofKind. - * Simply add the cases to the switch for all those special cases. - * For example, if you have a subclass of Token called IDToken that - * you want to create if ofKind is ID, simlpy add something like : + * Returns a new Token object, by default. However, if you want, you can create and return + * subclass objects based on the value of ofKind. Simply add the cases to the switch for all those + * special cases. For example, if you have a subclass of Token called IDToken that you want to + * create if ofKind is ID, simlpy add something like : * - * case MyParserConstants.ID : return new IDToken(); + *

case MyParserConstants.ID : return new IDToken(); * - * to the following switch statement. Then you can cast matchedToken - * variable to the appropriate type and use it in your lexical actions. + *

to the following switch statement. Then you can cast matchedToken variable to the + * appropriate type and use it in your lexical actions. */ - public static final Token newToken(int ofKind) - { - switch(ofKind) - { - default : return new Token(); - } + public static final Token newToken(int ofKind) { + switch (ofKind) { + default: + return new Token(); + } } - } diff --git a/jhove-modules/html-hul/src/main/java/edu/harvard/hul/ois/jhove/module/html/TokenMgrError.java b/jhove-modules/html-hul/src/main/java/edu/harvard/hul/ois/jhove/module/html/TokenMgrError.java index e92880d79..646703564 100644 --- a/jhove-modules/html-hul/src/main/java/edu/harvard/hul/ois/jhove/module/html/TokenMgrError.java +++ b/jhove-modules/html-hul/src/main/java/edu/harvard/hul/ois/jhove/module/html/TokenMgrError.java @@ -1,139 +1,144 @@ /* Generated By:JavaCC: Do not edit this line. TokenMgrError.java Version 3.0 */ package edu.harvard.hul.ois.jhove.module.html; -public class TokenMgrError extends Error -{ - /* - * Ordinals for various reasons why an Error of this type can be thrown. - */ +public class TokenMgrError extends Error { + /* + * Ordinals for various reasons why an Error of this type can be thrown. + */ - /** - * Lexical error occured. - */ - static final int LEXICAL_ERROR = 0; + /** Lexical error occured. */ + static final int LEXICAL_ERROR = 0; - /** - * An attempt wass made to create a second instance of a static token manager. - */ - static final int STATIC_LEXER_ERROR = 1; + /** An attempt wass made to create a second instance of a static token manager. */ + static final int STATIC_LEXER_ERROR = 1; - /** - * Tried to change to an invalid lexical state. - */ - static final int INVALID_LEXICAL_STATE = 2; + /** Tried to change to an invalid lexical state. */ + static final int INVALID_LEXICAL_STATE = 2; - /** - * Detected (and bailed out of) an infinite loop in the token manager. - */ - static final int LOOP_DETECTED = 3; + /** Detected (and bailed out of) an infinite loop in the token manager. */ + static final int LOOP_DETECTED = 3; - /** - * Indicates the reason why the exception is thrown. It will have - * one of the above 4 values. - */ - int errorCode; + /** Indicates the reason why the exception is thrown. It will have one of the above 4 values. */ + int errorCode; - /** - * Replaces unprintable characters by their espaced (or unicode escaped) - * equivalents in the given string - */ - protected static final String addEscapes(String str) { - StringBuffer retval = new StringBuffer(); - char ch; - for (int i = 0; i < str.length(); i++) { - switch (str.charAt(i)) - { - case 0 : - continue; - case '\b': - retval.append("\\b"); - continue; - case '\t': - retval.append("\\t"); - continue; - case '\n': - retval.append("\\n"); - continue; - case '\f': - retval.append("\\f"); - continue; - case '\r': - retval.append("\\r"); - continue; - case '\"': - retval.append("\\\""); - continue; - case '\'': - retval.append("\\\'"); - continue; - case '\\': - retval.append("\\\\"); - continue; - default: - if ((ch = str.charAt(i)) < 0x20 || ch > 0x7e) { - String s = "0000" + Integer.toString(ch, 16); - retval.append("\\u" + s.substring(s.length() - 4, s.length())); - } else { - retval.append(ch); - } - continue; - } + /** + * Replaces unprintable characters by their espaced (or unicode escaped) equivalents in the given + * string + */ + protected static final String addEscapes(String str) { + StringBuffer retval = new StringBuffer(); + char ch; + for (int i = 0; i < str.length(); i++) { + switch (str.charAt(i)) { + case 0: + continue; + case '\b': + retval.append("\\b"); + continue; + case '\t': + retval.append("\\t"); + continue; + case '\n': + retval.append("\\n"); + continue; + case '\f': + retval.append("\\f"); + continue; + case '\r': + retval.append("\\r"); + continue; + case '\"': + retval.append("\\\""); + continue; + case '\'': + retval.append("\\\'"); + continue; + case '\\': + retval.append("\\\\"); + continue; + default: + if ((ch = str.charAt(i)) < 0x20 || ch > 0x7e) { + String s = "0000" + Integer.toString(ch, 16); + retval.append("\\u" + s.substring(s.length() - 4, s.length())); + } else { + retval.append(ch); + } + continue; } - return retval.toString(); - } + } + return retval.toString(); + } - /** - * Returns a detailed message for the Error when it is thrown by the - * token manager to indicate a lexical error. - * Parameters : - * EOFSeen : indicates if EOF caused the lexicl error - * curLexState : lexical state in which this error occured - * errorLine : line number when the error occured - * errorColumn : column number when the error occured - * errorAfter : prefix that was seen before this error occured - * curchar : the offending character - * Note: You can customize the lexical error message by modifying this method. - */ - protected static String LexicalError(boolean EOFSeen, int lexState, int errorLine, int errorColumn, String errorAfter, char curChar) { - StringBuilder errMsg = new StringBuilder(); - errMsg.append(MessageConstants.HTML_HUL_20); - errMsg.append(" at line ").append(errorLine).append(", column ").append(errorColumn).append(". Encountered: "); - if (EOFSeen) { - errMsg.append(" "); - } else { - errMsg.append("\"").append(addEscapes(String.valueOf(curChar))).append("\" (").append((int)curChar).append("), "); - } - errMsg.append("after : \"").append(addEscapes(errorAfter)).append("\""); - return(errMsg.toString()); + /** + * Returns a detailed message for the Error when it is thrown by the token manager to indicate a + * lexical error. Parameters : EOFSeen : indicates if EOF caused the lexicl error curLexState : + * lexical state in which this error occured errorLine : line number when the error occured + * errorColumn : column number when the error occured errorAfter : prefix that was seen before + * this error occured curchar : the offending character Note: You can customize the lexical error + * message by modifying this method. + */ + protected static String LexicalError( + boolean EOFSeen, + int lexState, + int errorLine, + int errorColumn, + String errorAfter, + char curChar) { + StringBuilder errMsg = new StringBuilder(); + errMsg.append(MessageConstants.HTML_HUL_20); + errMsg + .append(" at line ") + .append(errorLine) + .append(", column ") + .append(errorColumn) + .append(". Encountered: "); + if (EOFSeen) { + errMsg.append(" "); + } else { + errMsg + .append("\"") + .append(addEscapes(String.valueOf(curChar))) + .append("\" (") + .append((int) curChar) + .append("), "); } + errMsg.append("after : \"").append(addEscapes(errorAfter)).append("\""); + return (errMsg.toString()); + } - /** - * You can also modify the body of this method to customize your error messages. - * For example, cases like LOOP_DETECTED and INVALID_LEXICAL_STATE are not - * of end-users concern, so you can return something like : - * - * "Internal Error : Please file a bug report .... " - * - * from this method for such cases in the release version of your parser. - */ - @Override - public String getMessage() { - return super.getMessage(); - } + /** + * You can also modify the body of this method to customize your error messages. For example, + * cases like LOOP_DETECTED and INVALID_LEXICAL_STATE are not of end-users concern, so you can + * return something like : + * + *

"Internal Error : Please file a bug report .... " + * + *

from this method for such cases in the release version of your parser. + */ + @Override + public String getMessage() { + return super.getMessage(); + } - /* - * Constructors of various flavors follow. - */ + /* + * Constructors of various flavors follow. + */ - public TokenMgrError() { - } + public TokenMgrError() {} - public TokenMgrError(String message, int reason) { - super(message); - errorCode = reason; - } + public TokenMgrError(String message, int reason) { + super(message); + errorCode = reason; + } - public TokenMgrError(boolean EOFSeen, int lexState, int errorLine, int errorColumn, String errorAfter, char curChar, int reason) { - this(LexicalError(EOFSeen, lexState, errorLine, errorColumn, errorAfter, curChar), reason); - } + public TokenMgrError( + boolean EOFSeen, + int lexState, + int errorLine, + int errorColumn, + String errorAfter, + char curChar, + int reason) { + this(LexicalError(EOFSeen, lexState, errorLine, errorColumn, errorAfter, curChar), reason); + } } diff --git a/jhove-modules/html-hul/src/main/java/edu/harvard/hul/ois/jhove/module/html/package-info.java b/jhove-modules/html-hul/src/main/java/edu/harvard/hul/ois/jhove/module/html/package-info.java index 65762ccb8..215bd1720 100644 --- a/jhove-modules/html-hul/src/main/java/edu/harvard/hul/ois/jhove/module/html/package-info.java +++ b/jhove-modules/html-hul/src/main/java/edu/harvard/hul/ois/jhove/module/html/package-info.java @@ -1,42 +1,41 @@ /** - * Contains supporting classes for the HTML-HUL module. - *
- *

- * This module uses code generated by - * JavaCC. - * The grammar file is ParseHtml.jj. It can be compiled using - * BuildParser.bat. Compiling it generates the following files: + * Contains supporting classes for the HTML-HUL module.
+ * + *

This module uses code generated by JavaCC. The grammar file is + * ParseHtml.jj. It can be compiled using BuildParser.bat. Compiling it + * generates the following files: + * *

    - *
  • CharStream.java - *
  • ParseException.java - *
  • ParseHtml.java - *
  • ParseHtmlConstants.java - *
  • ParseHtmlTokenManager.java - *
  • SimpleCharStream.java - *
  • Token.java - *
  • TokenMgrError.java + *
  • CharStream.java + *
  • ParseException.java + *
  • ParseHtml.java + *
  • ParseHtmlConstants.java + *
  • ParseHtmlTokenManager.java + *
  • SimpleCharStream.java + *
  • Token.java + *
  • TokenMgrError.java *
- *

- * In addition, HtmlCharStream.java has been created by manually - * modifying CharStream.java. If a future version of JavaCC - * changes CharStream.java, HtmlCharStream.java - * should be changed to match. - *

- * A number of DTD and Entity files have been stored with this package to - * facilitate resolution of Doctypes without having to get them over - * the Internet. These are the W3 Consortium's files, and no rights over - * them are claimed by including them here. - *

- * The included files: + * + *

In addition, HtmlCharStream.java has been created by manually modifying + * CharStream.java. If a future version of JavaCC changes CharStream.java, + * HtmlCharStream.java should be changed to match. + * + *

A number of DTD and Entity files have been stored with this package to facilitate resolution + * of Doctypes without having to get them over the Internet. These are the W3 Consortium's files, + * and no rights over them are claimed by including them here. + * + *

The included files: + * *

    - *
  • xhtml1-frameset.dtd - *
  • xhtml1-strict.dtd - *
  • xhtml1-transitional.dtd - *
  • xhtml-lat1.ent - *
  • xhtml-special.ent - *
  • xhtml-symbol.ent + *
  • xhtml1-frameset.dtd + *
  • xhtml1-strict.dtd + *
  • xhtml1-transitional.dtd + *
  • xhtml-lat1.ent + *
  • xhtml-special.ent + *
  • xhtml-symbol.ent *
- *

- * This module uses the XML-HUL module for validating XHTML files. + * + *

This module uses the XML-HUL module for validating XHTML files. */ -package edu.harvard.hul.ois.jhove.module.html; \ No newline at end of file +package edu.harvard.hul.ois.jhove.module.html; diff --git a/jhove-modules/jpeg-hul/src/main/java/JDump.java b/jhove-modules/jpeg-hul/src/main/java/JDump.java index 915284499..ad82a39e7 100644 --- a/jhove-modules/jpeg-hul/src/main/java/JDump.java +++ b/jhove-modules/jpeg-hul/src/main/java/JDump.java @@ -1,398 +1,351 @@ -/********************************************************************** - * JDump - JSTOR/Harvard Object Validation Environment - * Copyright 2004 by the President and Fellows of Harvard College +/** + * ******************************************************************** JDump - JSTOR/Harvard Object + * Validation Environment Copyright 2004 by the President and Fellows of Harvard College * - * This program 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 2 of the License, or (at - * your option) any later version. - * - * This program 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. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 - * USA - **********************************************************************/ - + *

This program 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 2 + * of the License, or (at your option) any later version. + * + *

This program 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. + * + *

You should have received a copy of the GNU Lesser General Public License along with this + * program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA ******************************************************************** + */ import edu.harvard.hul.ois.jhove.*; import java.io.*; -//import java.util.*; -/** - * Dump contents of JPEG file in human-readable format. - */ -public class JDump - extends Dump -{ - /****************************************************************** - * MAIN ENTRY POINT. - ******************************************************************/ +// import java.util.*; - /** - * Main entry point. - * @param args Command line arguments - */ - public static void main (String [] args) - { - if (args.length < 1) { - System.err.println ("usage: java JDump file"); - System.exit (-1); - } +/** Dump contents of JPEG file in human-readable format. */ +public class JDump extends Dump { + /** + * **************************************************************** MAIN ENTRY POINT. + * **************************************************************** + */ + + /** + * Main entry point. + * + * @param args Command line arguments + */ + public static void main(String[] args) { + if (args.length < 1) { + System.err.println("usage: java JDump file"); + System.exit(-1); + } - try (FileInputStream file = new FileInputStream (args[0]); - BufferedInputStream buffer = new BufferedInputStream (file); - DataInputStream stream = new DataInputStream (buffer)) { - boolean bigEndian = true; + try (FileInputStream file = new FileInputStream(args[0]); + BufferedInputStream buffer = new BufferedInputStream(file); + DataInputStream stream = new DataInputStream(buffer)) { + boolean bigEndian = true; - long os = 0; + long os = 0; - boolean endOfImage = false; - boolean readingECS = false; - boolean haveCode = false; - int nECS = 0; - int code = 0; - while (!endOfImage) { - if (!readingECS) { - if (!haveCode) { - code = stream.readUnsignedByte (); - while(stream.readUnsignedByte () == 0xff) { - System.out.println (leading (os, 8) + os + - ": fill 0xff"); - os++; - } - } - } - else { - boolean ff = false; - int length = 0; - while (true) { - code = stream.readUnsignedByte (); - length++; - if (code == 0xff) { - ff = true; - } - else if (ff) { - if (code != 0x00) { - length -= 2; - break; - } - ff = false; - } - } - System.out.println (leading (os, 8) + os + ": ECS" + - nECS + " " + length + " ..."); - os += length; - nECS++; - readingECS = false; - haveCode = true; - continue; - } + boolean endOfImage = false; + boolean readingECS = false; + boolean haveCode = false; + int nECS = 0; + int code = 0; + while (!endOfImage) { + if (!readingECS) { + if (!haveCode) { + code = stream.readUnsignedByte(); + while (stream.readUnsignedByte() == 0xff) { + System.out.println(leading(os, 8) + os + ": fill 0xff"); + os++; + } + } + } else { + boolean ff = false; + int length = 0; + while (true) { + code = stream.readUnsignedByte(); + length++; + if (code == 0xff) { + ff = true; + } else if (ff) { + if (code != 0x00) { + length -= 2; + break; + } + ff = false; + } + } + System.out.println(leading(os, 8) + os + ": ECS" + nECS + " " + length + " ..."); + os += length; + nECS++; + readingECS = false; + haveCode = true; + continue; + } - if (code == 0x01) { - System.out.println (leading (os, 8) + os + ": TEM"); - } - else if ((code >= 0xc0 && code <= 0xc3) || - (code >= 0xc5 && code <= 0xc7) || - (code >= 0xc9 && code <= 0xcb) || - (code >= 0xcd && code <= 0xcf)) { - int n = code - 0xc0; -/****************** int length = markerSegment (stream, bigEndian); */ - int length = ModuleBase.readUnsignedShort (stream, - bigEndian, - null); - int P = stream.readUnsignedByte (); - int Y = ModuleBase.readUnsignedShort (stream, bigEndian, - null); - int X = ModuleBase.readUnsignedShort (stream, bigEndian, - null); - int Nf = stream.readUnsignedByte (); - int [] Ci = new int [Nf]; - int [] Hi = new int [Nf]; - int [] Tqi = new int [Nf]; - for (int i=0; i= 0xd0 && code <= 0xd7) { - int m = code - 0xd0; - System.out.println (leading (os, 8) + os + ": RST" + m); + if (code == 0x01) { + System.out.println(leading(os, 8) + os + ": TEM"); + } else if ((code >= 0xc0 && code <= 0xc3) + || (code >= 0xc5 && code <= 0xc7) + || (code >= 0xc9 && code <= 0xcb) + || (code >= 0xcd && code <= 0xcf)) { + int n = code - 0xc0; + /** **************** int length = markerSegment (stream, bigEndian); */ + int length = ModuleBase.readUnsignedShort(stream, bigEndian, null); + int P = stream.readUnsignedByte(); + int Y = ModuleBase.readUnsignedShort(stream, bigEndian, null); + int X = ModuleBase.readUnsignedShort(stream, bigEndian, null); + int Nf = stream.readUnsignedByte(); + int[] Ci = new int[Nf]; + int[] Hi = new int[Nf]; + int[] Tqi = new int[Nf]; + for (int i = 0; i < Nf; i++) { + Ci[i] = stream.readUnsignedByte(); + Hi[i] = stream.readUnsignedByte(); + Tqi[i] = stream.readUnsignedByte(); + } + System.out.print( + leading(os, 8) + + os + + ": SOF" + + n + + " " + + length + + ": " + + P + + " " + + X + + "x" + + Y + + " " + + Nf + + ":"); + for (int i = 0; i < Nf; i++) { + System.out.print(" " + Ci[i] + "," + Hi[i] + "," + Tqi[i]); + } + System.out.println(); + os += length; + } else if (code == 0xc4) { + int length = markerSegment(stream, bigEndian); + System.out.println(leading(os, 8) + os + ": DHT" + " " + length + " ..."); + os += length; + } else if (code == 0xc8) { + int length = markerSegment(stream, bigEndian); + System.out.println(leading(os, 8) + os + ": JPG" + " " + length + " ..."); + os += length; + } else if (code == 0xcc) { + int length = markerSegment(stream, bigEndian); + System.out.println(leading(os, 8) + os + ": DAC" + " " + length + " ..."); + os += length; + } else if (code >= 0xd0 && code <= 0xd7) { + int m = code - 0xd0; + System.out.println(leading(os, 8) + os + ": RST" + m); - readingECS = true; - } - else if (code == 0xd8) { - System.out.println (leading (os, 8) + os + ": SOI"); - } - else if (code == 0xd9) { - System.out.println (leading (os, 8) + os + ": EOI"); - endOfImage = true; - break; - } - else if (code == 0xda) { - int length = markerSegment (stream, bigEndian); - System.out.println (leading (os, 8) + os + ": SOS" + " " + - length + " ..."); - os += length; + readingECS = true; + } else if (code == 0xd8) { + System.out.println(leading(os, 8) + os + ": SOI"); + } else if (code == 0xd9) { + System.out.println(leading(os, 8) + os + ": EOI"); + endOfImage = true; + break; + } else if (code == 0xda) { + int length = markerSegment(stream, bigEndian); + System.out.println(leading(os, 8) + os + ": SOS" + " " + length + " ..."); + os += length; - readingECS = true; - } - else if (code == 0xdb) { - int length = markerSegment (stream, bigEndian); - System.out.println (leading (os, 8) + os + ": DQT" + " " + - length + " ..."); - os += length; - } - else if (code == 0xdc) { - int length = markerSegment (stream, bigEndian); - System.out.println (leading (os, 8) + os + ": DNL" + " " + - length + " ..."); - os += length; - } - else if (code == 0xdd) { - int length = markerSegment (stream, bigEndian); - System.out.println (leading (os, 8) + os + ": DRI" + " " + - length + " ..."); - os += length; - } - else if (code == 0xde) { - int length = markerSegment (stream, bigEndian); - System.out.println (leading (os, 8) + os + ": DHP" + " " + - length + " ..."); - os += length; - } - else if (code == 0xdf) { - int length = markerSegment (stream, bigEndian); - System.out.println (leading (os, 8) + os + ": EXP" + " " + - length + " ..."); - os += length; - } - else if (code == 0xe0) { - int length = ModuleBase.readUnsignedShort (stream, - bigEndian, - null); - String id = readChars (stream, 5); - if ("JFIF\0".equals (id)) { - int major = stream.readUnsignedByte (); - int minor = stream.readUnsignedByte (); - int units = stream.readUnsignedByte (); - int xDensity = ModuleBase.readUnsignedShort (stream, - bigEndian, - null); - int yDensity = ModuleBase.readUnsignedShort (stream, - bigEndian, - null); - int xThumbnail = stream.readUnsignedByte (); - int yThumbnail = stream.readUnsignedByte (); - System.out.print (leading (os, 8) + os + ": APP0 " + - "\"" + id + "\" " + major + "." + - minor + " " + units + " " + - xDensity + "x" + yDensity + " " - + xThumbnail + "x" + yThumbnail); - int n = length - 16; - if (n > 0) { - for (int i=0; i 0) { - for (int i=0; i= 0xe1 && code <= 0xef) { - int n = code - 0xe0; - /* - int length = markerSegment (stream, bigEndian); - */ - int length = ModuleBase.readUnsignedShort (stream, - bigEndian, - null); - if ((n == 1 || n == 2) && length >= 8) { - String id = readChars (stream, 4); - int NULL = stream.readUnsignedByte (); - int padding = stream.readUnsignedByte (); - if ("Exif".equals (id) || "FPXR".equals (id)) { - System.out.println (leading (os, 8) + os + - ": APP" + n + " \"" + id + - "\" " + NULL + " " + padding + - " " + (length-8) + ": ..."); - } - else { - System.out.println (leading (os, 8) + os + - ": APP" + n + " " + length + - " ..."); - } - for (int i=8; i 0) { - int [] cap = new int [n]; - for (int i=0; i= 0xf7 && code <= 0xfd) { - int n = code - 0xf0; - int length = markerSegment (stream, bigEndian); - System.out.println (leading (os, 8) + os + ": JPG" + n + - " " + length + " ..."); - os += length; - } - else if (code == 0xfe) { - int length = ModuleBase.readUnsignedShort (stream, - bigEndian, - null); - String comment = readChars (stream, length-2); - System.out.println (leading (os, 8) + os + ": COM \"" + - comment + "\""); - os += length; - } - else { - int length = markerSegment (stream, bigEndian); - String hex = Integer.toHexString (code); - System.out.println (leading (os, 8) + os + ": RES (0x" + - leading (hex, 2) + hex + ") " + - length + " ..."); - } - os += 2; - } + readingECS = true; + } else if (code == 0xdb) { + int length = markerSegment(stream, bigEndian); + System.out.println(leading(os, 8) + os + ": DQT" + " " + length + " ..."); + os += length; + } else if (code == 0xdc) { + int length = markerSegment(stream, bigEndian); + System.out.println(leading(os, 8) + os + ": DNL" + " " + length + " ..."); + os += length; + } else if (code == 0xdd) { + int length = markerSegment(stream, bigEndian); + System.out.println(leading(os, 8) + os + ": DRI" + " " + length + " ..."); + os += length; + } else if (code == 0xde) { + int length = markerSegment(stream, bigEndian); + System.out.println(leading(os, 8) + os + ": DHP" + " " + length + " ..."); + os += length; + } else if (code == 0xdf) { + int length = markerSegment(stream, bigEndian); + System.out.println(leading(os, 8) + os + ": EXP" + " " + length + " ..."); + os += length; + } else if (code == 0xe0) { + int length = ModuleBase.readUnsignedShort(stream, bigEndian, null); + String id = readChars(stream, 5); + if ("JFIF\0".equals(id)) { + int major = stream.readUnsignedByte(); + int minor = stream.readUnsignedByte(); + int units = stream.readUnsignedByte(); + int xDensity = ModuleBase.readUnsignedShort(stream, bigEndian, null); + int yDensity = ModuleBase.readUnsignedShort(stream, bigEndian, null); + int xThumbnail = stream.readUnsignedByte(); + int yThumbnail = stream.readUnsignedByte(); + System.out.print( + leading(os, 8) + + os + + ": APP0 " + + "\"" + + id + + "\" " + + major + + "." + + minor + + " " + + units + + " " + + xDensity + + "x" + + yDensity + + " " + + xThumbnail + + "x" + + yThumbnail); + int n = length - 16; + if (n > 0) { + for (int i = 0; i < n; i++) { + stream.readUnsignedByte(); + } + System.out.print(" " + n + " ..."); + } + System.out.println(); + } else if ("JFXX\0".equals(id)) { + int ext = stream.readUnsignedByte(); + String hex = Integer.toHexString(ext); + System.out.print(leading(os, 8) + os + ": APP0 " + "0x" + leading(hex, 2) + hex); + int n = length - 3; + if (n > 0) { + for (int i = 0; i < n; i++) { + stream.readUnsignedByte(); + } + System.out.print(" " + n + " ..."); + } + System.out.println(); + } else { + System.out.println(leading(os, 8) + os + ": APP0 " + length + " ..."); + for (int i = 2; i < length; i++) { + stream.readUnsignedByte(); + } + } + os += length; + } else if (code >= 0xe1 && code <= 0xef) { + int n = code - 0xe0; + /* + int length = markerSegment (stream, bigEndian); + */ + int length = ModuleBase.readUnsignedShort(stream, bigEndian, null); + if ((n == 1 || n == 2) && length >= 8) { + String id = readChars(stream, 4); + int NULL = stream.readUnsignedByte(); + int padding = stream.readUnsignedByte(); + if ("Exif".equals(id) || "FPXR".equals(id)) { + System.out.println( + leading(os, 8) + + os + + ": APP" + + n + + " \"" + + id + + "\" " + + NULL + + " " + + padding + + " " + + (length - 8) + + ": ..."); + } else { + System.out.println(leading(os, 8) + os + ": APP" + n + " " + length + " ..."); + } + for (int i = 8; i < length; i++) { + stream.readUnsignedByte(); + } + } else { + System.out.println(leading(os, 8) + os + ": APP" + n + " " + length + " ..."); + for (int i = 2; i < length; i++) { + stream.readUnsignedByte(); + } + } + os += length; + } else if (code == 0xf0) { + int length = ModuleBase.readUnsignedShort(stream, bigEndian, null); + int v = stream.readUnsignedByte(); + int rev = stream.readUnsignedByte(); + System.out.print(leading(os, 8) + os + ": VER " + v + "." + rev); + int n = length - 4; + if (n > 0) { + int[] cap = new int[n]; + for (int i = 0; i < n; i++) { + cap[i] = stream.readUnsignedByte(); + System.out.print(((i == 0) ? " " : ",") + cap[i]); + } + } + System.out.println(); + os += length; + } else if (code == 0xf1) { + int length = markerSegment(stream, bigEndian); + System.out.println(leading(os, 8) + os + ": DTI " + length + " ..."); + os += length; + } else if (code == 0xf2) { + int length = markerSegment(stream, bigEndian); + System.out.println(leading(os, 8) + os + ": DTT " + length + " ..."); + os += length; + } else if (code == 0xf3) { + int length = markerSegment(stream, bigEndian); + System.out.println(leading(os, 8) + os + ": SRF " + length + " ..."); + os += length; + } else if (code == 0xf4) { + int length = markerSegment(stream, bigEndian); + System.out.println(leading(os, 8) + os + ": SRS " + length + " ..."); + os += length; + } else if (code == 0xf5) { + int length = markerSegment(stream, bigEndian); + System.out.println(leading(os, 8) + os + ": DCR " + length + " ..."); + os += length; + } else if (code == 0xf6) { + int length = markerSegment(stream, bigEndian); + System.out.println(leading(os, 8) + os + ": DQS " + length + " ..."); + os += length; + } else if (code >= 0xf7 && code <= 0xfd) { + int n = code - 0xf0; + int length = markerSegment(stream, bigEndian); + System.out.println(leading(os, 8) + os + ": JPG" + n + " " + length + " ..."); + os += length; + } else if (code == 0xfe) { + int length = ModuleBase.readUnsignedShort(stream, bigEndian, null); + String comment = readChars(stream, length - 2); + System.out.println(leading(os, 8) + os + ": COM \"" + comment + "\""); + os += length; + } else { + int length = markerSegment(stream, bigEndian); + String hex = Integer.toHexString(code); + System.out.println( + leading(os, 8) + os + ": RES (0x" + leading(hex, 2) + hex + ") " + length + " ..."); + } + os += 2; + } - stream.close (); - } - catch (Exception e) { - e.printStackTrace (System.err); - System.exit (-2); - } + stream.close(); + } catch (Exception e) { + e.printStackTrace(System.err); + System.exit(-2); } + } - /** - * Read marker segment data - * @param stream Data input stream - * @param bigEndian True if big-endian - * @return Length of marker segment - */ - private static int markerSegment (DataInputStream stream, - boolean bigEndian) - throws IOException - { - int length = ModuleBase.readUnsignedShort (stream, - bigEndian, - null); - for (int i=2; iThis program 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 2 + * of the License, or (at your option) any later version. * - * This program 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. + *

This program 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. * - * You should have received a copy of the GNU Lesser General Public License - * along with this program; if not, write to the Free Software Foundation, Inc., - * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - **********************************************************************/ - + *

You should have received a copy of the GNU Lesser General Public License along with this + * program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA ******************************************************************** + */ package edu.harvard.hul.ois.jhove.module; -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; -import java.io.DataInputStream; -import java.io.EOFException; -import java.io.File; -import java.io.IOException; -import java.io.InputStream; -import java.io.UnsupportedEncodingException; -import java.text.NumberFormat; -import java.util.ArrayList; -import java.util.Iterator; -import java.util.LinkedList; -import java.util.List; -import java.util.ListIterator; -import java.util.logging.Logger; - -import javax.xml.parsers.SAXParserFactory; - -import org.xml.sax.SAXException; -import org.xml.sax.XMLReader; - import edu.harvard.hul.ois.jhove.Agent; import edu.harvard.hul.ois.jhove.AgentType; import edu.harvard.hul.ois.jhove.ByteArrayXMPSource; @@ -74,1958 +51,1955 @@ import edu.harvard.hul.ois.jhove.module.jpeg.Spiff; import edu.harvard.hul.ois.jhove.module.jpeg.SpiffDir; import edu.harvard.hul.ois.jhove.module.jpeg.Tiling; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.DataInputStream; +import java.io.EOFException; +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.io.UnsupportedEncodingException; +import java.text.NumberFormat; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; +import java.util.ListIterator; +import java.util.logging.Logger; +import javax.xml.parsers.SAXParserFactory; +import org.xml.sax.SAXException; +import org.xml.sax.XMLReader; /** * Module for identification and validation of JPEG files. * - * General notes: + *

General notes: * - * There is no such thing as a "JPEG file format." There are several commonly - * used file formats which encapsulate JPEG data and conform to the JPEG stream - * format. There are also many formats which can encapsulate JPEG data within - * some larger wrapper; this module does not attempt to recognize them. Only - * JPEG file formats which are JPEG streams are treated here. A JPEG stream - * which isn't one of the known file formats will be regarded as well-formed, - * but not valid. To be valid, a file must conform to one of the following: - * JFIF, SPIFF, and JPEG/Exif. Other formats may be added in the future. + *

There is no such thing as a "JPEG file format." There are several commonly used file formats + * which encapsulate JPEG data and conform to the JPEG stream format. There are also many formats + * which can encapsulate JPEG data within some larger wrapper; this module does not attempt to + * recognize them. Only JPEG file formats which are JPEG streams are treated here. A JPEG stream + * which isn't one of the known file formats will be regarded as well-formed, but not valid. To be + * valid, a file must conform to one of the following: JFIF, SPIFF, and JPEG/Exif. Other formats may + * be added in the future. * - * This module uses the JPEG-L method of detecting a marker following a data - * stream, checking for a 0 high bit rather than an entire 0 byte. So long at no - * JPEG markers are defined with a value from 0 through 7F, this is valid for - * all JPEG files. + *

This module uses the JPEG-L method of detecting a marker following a data stream, checking for + * a 0 high bit rather than an entire 0 byte. So long at no JPEG markers are defined with a value + * from 0 through 7F, this is valid for all JPEG files. * - * * @author Gary McGath + *

* @author Gary McGath */ public class JpegModule extends ModuleBase { - /****************************************************************** - * DEBUGGING FIELDS. All debugging fields should be set to false for release - * code. - ******************************************************************/ - private static final Logger LOGGER = Logger - .getLogger(JpegModule.class.getName()); - - /****************************************************************** - * PRIVATE CLASS FIELDS. - ******************************************************************/ - private static final String NISO_IMAGE_MD = "NisoImageMetadata"; - private static final String NAME = "JPEG-hul"; - private static final String RELEASE = "1.5.2"; - private static final int [] DATE = { 2019, 11, 05 }; - private static final String[] FORMAT = { "JPEG", "ISO/IEC 10918-1:1994", - "Joint Photographic Experts Group", "JFIF", - "JPEG File Interchange Format", "SPIFF", "ISO/IEC 10918-3:1997", - "Still Picture Interchange File Format", "JTIP", - "ISO/IEC 10918-3:1997", "JPEG Tiled Image Pyramid", "JPEG-LS", - "ISO/IEC 14495", "Adobe JPEG", "ISO/IEC 10918-6:2013" }; - private static final String COVERAGE = "JPEG (ISO/IEC 10918-1:1994), JFIF 1.02, " - + "SPIFF (ISO/IEC 10918-3:1997), " - + "Exif 2.0, 2.1 (JEIDA-49-1998), 2.2 (JEITA CP-3451), 2.21 (JEITA CP-3451A), and 2.3 (JEITA CP-3451C), " - + "JTIP (ISO/IEC 10918-3:1997), JPEG-LS (ISO/IEC 14495), Adobe JPEG (ISO/IEC 10918-6:2013)"; - private static final String[] MIMETYPE = { "image/jpeg" }; - private static final String WELLFORMED = "A JPEG file is well-formed if " - + "the first three bytes are 0xFFD8FF, it consists of one or more " - + "correctly formatted segments (using markers 0xC0 through 0xFE), " - + "and the data streams following RSTn and SOS markers are correctly " - + "terminated"; - private static final String VALIDITY = "A JPEG file is valid if " - + "well-formed; the first non-comment segment is APP0 (with " - + "identifier 0x4A46494600, indicating JFIF or JTIP, with 1, 3 or 4 components), " - + "APP1 (with identifier (0x457869660000, indicating Exif, and only 3 components), " - + "APP8 (with identifier 0x545049464600, indicating SPIFF), " - + "or JPG7 (or SOF55, indicating JPEG-LS); " - + "D8 marker occurs only at the beginning of " - + "the file; any DTT segments are preceded by DTI segments; and all " - + "DTI segment tiling type have a value of 0, 1, or 2"; - private static final String REPINFO = "Additional representation " - + "information includes: NISO Z39.87 Digital Still Image Technical " - + "Metadata and segment-specific metadata"; - private static final String NOTE = null; - private static final String RIGHTS = "Copyright 2003-2007 by JSTOR and " - + "the President and Fellows of Harvard College. " - + "Released under the GNU Lesser General Public License."; - - /****************************************************************** - * PRIVATE INSTANCE FIELDS. - ******************************************************************/ - private static final int CS_GRAYSCALE = 1; - private static final int CS_RGB = 2; - private static final int CS_PALETTE = 3; - private static final int CS_YCC = 6; // YcbCr - private static final int CS_CMYK = 5; - private static final int CS_YCCK = 65535; // not existing !!! - - /* - * Profile names. These are just informal identifiers, and probably will be - * formalized later. - */ - protected String jfifProfileName = "JFIF"; - protected String spiffProfileName = "SPIFF"; - protected String exifProfileName = "Exif"; - protected String jpeglProfileName = "JPEG-L"; - protected String adobeProfileName = "Adobe JPEG"; - - /* a NumberFormat for handling the minor part of version numbers */ - protected NumberFormat minorFmt; - - /* Top-level metadata property */ - protected Property _metadata; - - /* - * Property for current image. This should go into a list of image - * properties, which in turn becomes the "image" property of _metadata. - */ - protected Property _imageProp; - - /* Exif property */ - protected Property _exifProp; - - /* XMP property */ - protected Property _xmpProp; - - /* NISO image metadata */ - protected NisoImageMetadata _niso; - - /* Top-level property list */ - protected List _propList; - - /* List of image properties. */ - protected List _imageList; - - /* Tiling information, if a DTI has been seen. */ - protected Tiling _tiling; - - /* List of quantization tables. */ - protected List _quantTables; - - /* List of arithmetic conditioning entries */ - protected List _arithCondTables; - - /* List of SRS entries. */ - protected List _srsList; - - /* Property list for the primary image. */ - protected List _primaryImageList; - - /* Number of segments read */ - protected int _numSegments; - - /* Number of scans in the current image */ - protected int _numScans; - - /* Restart interval */ - protected int _restartInterval; - - /* Flag indicating an APP0 JFIF segment has been read */ - protected boolean _seenJFIF; - - /* Flag indicating an APP0 JFIF segment has been read first */ - protected boolean _seenJFIFFirst; - - /* Flag indicating an APP8 SPIFF segment has been read */ - protected boolean _seenSPIFF; - - /* - * Flag indicating a JPEG-L SOF55 (aka JPG7) segment has been read - */ - protected boolean _seenJPEGL; - - /* Flag to make sure we report signature matching only once. */ - protected boolean _reportedSigMatch; - - /* SPIFF directory information. */ - protected SpiffDir _spiffDir; - - /* Flag indicating an APP1 Exif segment has been read */ - protected boolean _seenExif; - - /* Flag indicating the Exif profile is satisfied */ - protected boolean _exifProfileOK; - - /* Exif profile if one is satisfied, more specific than just Exif */ - protected String _exifProfileText; - - /* Flag indicating an APP14 Adobe segment has been read */ - protected boolean _seenAdobe; - - /* Flag indicating lack of a JFIF segment has been reported */ - protected boolean _reportedJFIF; - - /* Flag indicating the first SOF has been read */ - protected boolean _seenSOF; - - /* List of comment text */ - protected List _commentsList; - - /* List of extensions used */ - protected List _jpegExtsList; - - /* List of application segments used */ - protected List _appSegsList; - - /* - * List of expand reference components markers. Members are boolean[2] - */ - protected List _expList; - - /* Capability 0 byte, from VER segment. -1 if none. */ - protected int _capability0; - - /* Capability 1 byte, from VER segment. -1 if none. */ - protected int _capability1; - - /* Fixed value for first 3 bytes */ - private static final int[] sigByte = { 0XFF, 0XD8, 0XFF }; - - /* Transform flag from the Adobe marker segment. */ - protected byte _transformFlag = -1; - - /* Keep all the chunks of an ICC profile */ - protected ByteArrayOutputStream _baosIccProfile = null; - - /* Resolution units. */ - protected int _units; - - /* X resolution (or pixel aspect ratio X). */ - protected int _xDensity; - - /* Y resolution (or pixel aspect ration Y). */ - protected int _yDensity; - - /****************************************************************** - * CLASS CONSTRUCTOR. - ******************************************************************/ - /** - * Instantiate a JpegModule object. - */ - public JpegModule() { - super(NAME, RELEASE, DATE, FORMAT, COVERAGE, MIMETYPE, WELLFORMED, - VALIDITY, REPINFO, NOTE, RIGHTS, false); - - // Set up a simple NumberFormat for version reporting - minorFmt = NumberFormat.getInstance(); - minorFmt.setMinimumIntegerDigits(2); - - // Define HUL vendor agent - _vendor = Agent.harvardInstance(); - - // Define C-Cube JPEG 1.02 doc - Document doc = new Document( - "Eric Hamilton, JPEG File Interchange Format, " - + "Version 1.02, September 1, 1992", - DocumentType.WEB); - Agent agent = new Agent.Builder("C-Cube Microsystems", - AgentType.COMMERCIAL) - .address("1778 McCarthy Boulevard, Milipitas, CA 95035") - .telephone("+1 (408) 944-6314").fax("+1 (408) 944-6314") - .build(); - doc.setPublisher(agent); - doc.setDate("1992-09-01"); - doc.setIdentifier( - new Identifier("http://www.w3.org/Graphics/JPEG/jfif3.pdf", - IdentifierType.URL)); - _specification.add(doc); - - // Define ISO standard - doc = new Document( - "ISO/IEC 10918-1:1994(E), Information technology -- " - + "Digital compression and coding of continuous-tone " - + "still images: Requirements and guidelines", - DocumentType.STANDARD); - Agent isoAgent = Agent.newIsoInstance(); - doc.setPublisher(isoAgent); - doc.setIdentifier(new Identifier("CCITT REc. T.81 (1992 E)", - IdentifierType.CCITT)); - _specification.add(doc); - - // Define ISO extensions - doc = new Document("ISO/IEC 10918-3:1997(E), Digital compression" - + "and coding of continuous-tone still-images: " + "Extensions", - DocumentType.STANDARD); - doc.setPublisher(isoAgent); - doc.setIdentifier(new Identifier("ITU-T Rec. T.84 (1996 E)", - IdentifierType.CCITT)); - _specification.add(doc); - - // Define ISO lossless baseline - doc = new Document( - "ISO/IEC 14495-1:1999(E), Information technology -- " - + "Lossless and near-lossless compression of " - + "continuous-tone still images: Baseline", - DocumentType.STANDARD); - doc.setPublisher(isoAgent); - _specification.add(doc); - - // Define ISO lossless extensions - doc = new Document( - "ISO/IEC 14495-2:2003(E), Information technology -- " - + "Lossless and near-lossless compression of " - + "continuous-tone still images: Extensions", - DocumentType.STANDARD); - doc.setPublisher(isoAgent); - _specification.add(doc); - - // Define ISO Adobe extensions - doc = new Document( - "ISO/IEC 10918-6:2013(E), Digital compression" - + "and coding of continuous-tone still-images: " - + "Application to printing systems", DocumentType.STANDARD); - doc.setPublisher(isoAgent); - doc.setIdentifier(new Identifier("ITU-T Rec. T.872 (06/12)", - IdentifierType.CCITT)); - _specification.add(doc); - - // Define JEITA Exif 2.3 doc - doc = new Document( - "Exchangeable image file format for digital " - + "still cameras: Exif Version 2.3", - DocumentType.STANDARD); - Agent jeitaAgent = new Agent.Builder( - "Japan Electronics and Information Technology " - + "Industries Association", - AgentType.STANDARD) - .web("http://www.jeita.or.jp/") - .address("Mitsui Sumitomo Kaijo Building Annex, " - + "11, Kanda Surugadai 3-chome, Chiyoda-ku, " - + "Tokyo 101-0062, Japan") - .telephone("+81(03) 3518-6421").fax("+81(03) 3295-8721") - .build(); - doc.setPublisher(jeitaAgent); - doc.setDate("2010-04"); - Identifier ident = new Identifier("JEITA CP-3451C", - IdentifierType.JEITA); - doc.setIdentifier(ident); - ident = new Identifier( - "http://home.jeita.or.jp/tsc/std-pdf/CP3451C.pdf", - IdentifierType.URL); - doc.setIdentifier(ident); - _specification.add(doc); - - // Define JEITA Exif 2.2 doc - doc = new Document( - "Exchangeable image file format for digital " - + "still cameras: Exif Version 2.2", - DocumentType.STANDARD); - doc.setPublisher(jeitaAgent); - doc.setDate("2002-04"); - ident = new Identifier("JEITA CP-3451", IdentifierType.JEITA); - doc.setIdentifier(ident); - ident = new Identifier("http://www.exif.org/Exif2-2.PDF", - IdentifierType.URL); - doc.setIdentifier(ident); - _specification.add(doc); - - // Define Exif 2.1 doc - doc = new Document("Digital Still Camera Image File Format Standard " - + "(Exchangeable image file format for Digital Still Camera:Exif)", - DocumentType.STANDARD); - doc.setPublisher(jeitaAgent); - doc.setDate("1998-12"); - ident = new Identifier("JEITA JEIDA-49-1998", IdentifierType.JEITA); - doc.setIdentifier(ident); - ident = new Identifier("http://www.exif.org/dcf-exif.PDF", - IdentifierType.URL); - doc.setIdentifier(ident); - _specification.add(doc); - - Signature sig = new InternalSignature(sigByte, SignatureType.MAGIC, - SignatureUseType.MANDATORY, 0, ""); - _signature.add(sig); - - sig = new ExternalSignature(".jpg", SignatureType.EXTENSION, - SignatureUseType.OPTIONAL); - _signature.add(sig); - - sig = new ExternalSignature(".jls", SignatureType.EXTENSION, - SignatureUseType.OPTIONAL, - "Generally used for JPEG-LS (ISO/IEC 14495)"); - _signature.add(sig); - - sig = new ExternalSignature(".spf", SignatureType.EXTENSION, - SignatureUseType.OPTIONAL, - "Generally used for SPIFF (ISO/IEC 10918-3:1997)"); - _signature.add(sig); - - _bigEndian = true; - } - - /****************************************************************** - * Parsing methods. - ******************************************************************/ - - /** - * Check if the digital object conforms to this Module's internal signature - * information. - * - * @param file - * A RandomAccessFile, positioned at its beginning, which is - * generated from the object to be parsed - * @param stream - * An InputStream, positioned at its beginning, which is - * generated from the object to be parsed - * @param info - * A fresh RepInfo object which will be modified to reflect the - * results of the test - */ - @Override - public void checkSignatures(File file, InputStream stream, RepInfo info) - throws IOException { - int i; - int ch; - _dstream = getBufferedDataStream(stream, - _je != null ? _je.getBufferSize() : 0); - for (i = 0; i < 3; i++) { - try { - ch = readUnsignedByte(_dstream, this); - } catch (Exception e) { - ch = -1; - } - if (ch != sigByte[i]) { - info.setWellFormed(false); - return; - } - } - info.setModule(this); - info.setFormat(_format[0]); - info.setMimeType(_mimeType[0]); - info.setSigMatch(_name); - } - - /** - * Parse the content of a purported JPEG stream digital object and store the - * results in RepInfo. - * - * This function uses the JPEG-L method of detecting a marker following a - * data stream, checking for a 0 high bit rather than an entire 0 byte. So - * long at no JPEG markers are defined with a value from 0 through 7F, this - * is valid for all JPEG files. - * - * @param stream - * An InputStream, positioned at its beginning, which is - * generated from the object to be parsed - * @param info - * A fresh RepInfo object which will be modified to reflect the - * results of the parsing - * @param parseIndex - * Must be 0 in first call to parse. If - * parse returns a nonzero value, it must be called - * again with parseIndex equal to that return value. - */ - @Override - public int parse(InputStream stream, RepInfo info, int parseIndex) - throws IOException { - initParse(); - info.setFormat(_format[0]); - info.setMimeType(_mimeType[0]); - info.setModule(this); - - /* - * We may have already done the checksums while converting a temporary - * file. - */ - setupDataStream(stream, info); - _propList = new LinkedList(); - _metadata = new Property("JPEGMetadata", PropertyType.PROPERTY, - PropertyArity.LIST, _propList); - if (!readHeader(info)) { - return 0; - } - _niso = new NisoImageMetadata(); - Property nisoProp = new Property(NISO_IMAGE_MD, - PropertyType.NISOIMAGEMETADATA, _niso); - _primaryImageList.add(nisoProp); - initNiso(); - - // Count the number of segments read, exclusive of the APP0 header. - boolean dataPlowing = false; - ErrorMessage msg; - try { - // When true, we have to go through data till we find a marker - loop1: for (;;) { - int dbyt = 0; - boolean sawFF = false; - if (dataPlowing) { - for (;;) { - dbyt = readUnsignedByte(_dstream, this); - if (dbyt == 0xFF) { - sawFF = true; - // multiple FF's count same as one - } else if (sawFF) { - // Note use of JPEG-L check. For a - // standard JPEG check, we would use - // (dbyt != 0) - if ((dbyt & 0x80) != 0) { - dataPlowing = false; - break; - } - // FF followed by 0 is discarded - sawFF = false; - } - } - } else { - dbyt = readUnsignedByte(_dstream, this); - if (dbyt != 0xFF) { - info.setMessage(new ErrorMessage( - MessageConstants.JPEG_HUL_7, - String.format(MessageConstants.JPEG_HUL_7_SUB.getMessage(), dbyt), - _nByte)); - info.setWellFormed(false); - return 0; - } - // There can be padding bytes equal to FF, - // so read till we get one that isn't. - while (dbyt == 0xFF) { - dbyt = readUnsignedByte(_dstream, this); - } - } - _numSegments++; - if (!_seenJFIF && !_seenSPIFF && !_seenExif && !_seenJPEGL - && _numSegments >= 2 && !_reportedJFIF) { - info.setMessage(new ErrorMessage( - MessageConstants.JPEG_HUL_9, - _nByte)); - info.setValid(false); - _reportedJFIF = true; - } - if (dbyt >= 0xD0 && dbyt <= 0xD7) { - // RST[m] -- Restart with modulo 8 count 0-7 - dataPlowing = true; - } else if (dbyt >= 0xF7 && dbyt <= 0xFD) { - // JPGn extension - readJPEGExtension(dbyt, info); - } else - switch (dbyt) { - case 0: - // Byte stuffing -- ignore - break; - - case 0xC0: - case 0xC1: - case 0xC2: - case 0xC3: - case 0xC5: - case 0xC6: - case 0xC7: - case 0xC9: - case 0xCA: - case 0xCB: - case 0xCD: - case 0xCE: - case 0xCF: - // SOF(n) marker; value indicates encoding type - readSOF(dbyt, info); - break; - - case 0xC4: - // DHT -- define Huffman tables - skipSegment(info); - break; - - case 0xCC: - // DAC -- define arithmetic coding conditioning - readDAC(info); - break; - - case 0xD9: - // EOI - break loop1; - - case 0xDA: - // SOS -- start of scan. This is followed by data. - skipSegment(info); - ++_numScans; - dataPlowing = true; - break; - - case 0xDB: - // DQT -- define quantization tables - readDQT(info); - break; - - case 0xDC: - // DNL -- define number of lines - skipSegment(info); - break; - - case 0xDD: - // DRI -- define restart interval - readDRI(info); - break; - - case 0xDE: - // DHP -- define hierarchical progression - readDHP(info); - break; - - case 0xDF: - // EXP -- Expand reference component - readEXP(info); - break; - - case 0xE0: - // APP0 extension - readAPP0(info); - break; - - case 0xE1: - readAPP1(info); - break; - - case 0xE2: - readAPP2(info); - break; - - case 0xE8: - // APP8 extension - readAPP8(info); - break; - - case 0xEE: - readAPP14(info); - break; - - case 0xE3: - case 0xE4: - case 0xE5: - case 0xE6: - case 0xE7: - case 0xE9: - case 0xEA: - case 0xEB: - case 0xEC: - case 0xED: - case 0xEF: - // Appn extensions which we don't handle specially, - // but do report the existence thereof - reportAppExt(dbyt, info); - skipSegment(info); - break; - - case 0XF0: - // VER segment - readVer(info); - break; - - case 0XF1: - // DTI (defined tiled image) segment - readDTI(info); - break; - - case 0XF2: - // DTI (defined tile) segment - readDTT(info); - break; - - case 0XF4: - // SRS (selectively refined scan) segment - readSRS(info); - break; - - case 0XFE: - // comment - --_numSegments; // don't let comment trigger error - readComment(info); - break; - - default: - // Other values don't belong at the top level. - msg = new ErrorMessage( - MessageConstants.JPEG_HUL_6, _nByte); - info.setMessage(msg); - info.setValid(false); - break loop1; - } - } - - } catch (EOFException e) { - msg = new ErrorMessage(MessageConstants.JPEG_HUL_2, - _nByte); - info.setMessage(msg); - info.setWellFormed(false); - return 0; - } - - info.setProperty(_metadata); - - if (_units == 0) { - List list = new ArrayList(); - list.add(new Property("PixelAspectRatioX", PropertyType.INTEGER, _xDensity)); - list.add(new Property("PixelAspectRatioY", PropertyType.INTEGER, _yDensity)); - _primaryImageList.add(new Property("PixelAspectRatio", - PropertyType.PROPERTY, PropertyArity.LIST, list)); - } - - // If there's tiling information, create a property for the - // primary image list. - if (_tiling != null) { - Property tp = buildTilingProp(info); - if (tp != null) { - _primaryImageList.add(tp); - } - } - if (_restartInterval >= 0) { - _primaryImageList.add(new Property("RestartInterval", - PropertyType.INTEGER, _restartInterval)); - } - _primaryImageList.add(new Property("Scans", PropertyType.INTEGER, _numScans)); - if (!_quantTables.isEmpty()) { - List qpl = new LinkedList(); - ListIterator iter = _quantTables.listIterator(); - while (iter.hasNext()) { - QuantizationTable qt = iter.next(); - qpl.add(qt.makeProperty(_je.getShowRawFlag())); - } - _primaryImageList.add(new Property("QuantizationTables", - PropertyType.PROPERTY, PropertyArity.LIST, qpl)); - } - - if (!_arithCondTables.isEmpty()) { - List qpl = new LinkedList(); - ListIterator iter = _arithCondTables - .listIterator(); - while (iter.hasNext()) { - ArithConditioning qt = iter.next(); - qpl.add(qt.makeProperty(_je.getShowRawFlag())); - } - _primaryImageList.add(new Property("ArithmeticConditioning", - PropertyType.PROPERTY, PropertyArity.LIST, qpl)); - } - if (!_srsList.isEmpty()) { - List srsl = new LinkedList(); - ListIterator iter = _srsList.listIterator(); - while (iter.hasNext()) { - SRS s = iter.next(); - srsl.add(s.makeProperty()); - } - _primaryImageList.add(new Property("SelectivelyRefinedScans", - PropertyType.PROPERTY, PropertyArity.LIST, srsl)); - } - - if (_ckSummer != null) { - skipDstreamToEnd(info); - // Set the checksums in the report if they're calculated - setChecksums(this._ckSummer, info); - } - - // Put the primary image in the image list. - _imageList.add(new Property("Image", PropertyType.PROPERTY, - PropertyArity.LIST, _primaryImageList)); - - // Report profiles. - if (_seenJFIF && _seenJFIFFirst) { - info.setProfile(jfifProfileName); - } - if (_seenExif && _exifProfileOK) { - if (_exifProfileText != null) { - info.setProfile(_exifProfileText); - } else { - info.setProfile(exifProfileName); - } - } - if (_seenSPIFF) { - info.setProfile(spiffProfileName); - if (_spiffDir != null) { - // Grab any image properties from the SPIFF directory - // and add them to the image list. - _spiffDir.appendThumbnailProps(_imageList); - } - } - if (_seenJPEGL) { - info.setProfile(jpeglProfileName); - } - if (_seenAdobe) { - info.setProfile(adobeProfileName); - } - /* - * Create a new property list containing the count of the images and the - * list of image properties. - */ - List list = new ArrayList(); - list.add(new Property("Number", PropertyType.INTEGER, - PropertyArity.SCALAR, _imageList.size())); - Iterator iter = _imageList.iterator(); - while (iter.hasNext()) { - Property prop = iter.next(); - list.add(prop); - } - _propList.add(new Property("Images", PropertyType.PROPERTY, - PropertyArity.LIST, list)); - // _imageList)); - if (!_commentsList.isEmpty()) { - _propList.add(new Property("Comments", PropertyType.STRING, - PropertyArity.LIST, _commentsList)); - } - if (!_jpegExtsList.isEmpty()) { - _propList.add(new Property("Extensions", PropertyType.STRING, - PropertyArity.LIST, _jpegExtsList)); - } - if (!_appSegsList.isEmpty()) { - _propList.add(new Property("ApplicationSegments", - PropertyType.STRING, PropertyArity.LIST, _appSegsList)); - } - if (!_expList.isEmpty()) { - _propList.add(buildExpandProp(info)); - } - if (_exifProp != null) { - _primaryImageList.add(_exifProp); - } - if (_xmpProp != null) { - _primaryImageList.add(_xmpProp); - } - return 0; - } - - /** - * One-argument version of readUnsignedShort. JPEG is always - * big-endian, so readUnsignedShort can unambiguously drop its endian - * argument. - */ - public int readUnsignedShort(DataInputStream stream) throws IOException { - return readUnsignedShort(stream, true, this); - } - - /** - * One-argument version of readUnsignedInt. JPEG is always - * big-endian, so readUnsignedInt can unambiguously drop its endian - * argument. - */ - public long readUnsignedInt(DataInputStream stream) throws IOException { - return readUnsignedInt(stream, true, this); - } - - /** - * Initializes the state of the module for parsing. - */ - @Override - protected void initParse() { - super.initParse(); - _imageList = new LinkedList(); - _tiling = null; - _restartInterval = -1; - _seenSOF = false; - _seenJFIF = false; - _seenJFIFFirst = false; - _seenSPIFF = false; - _seenJPEGL = false; - _spiffDir = null; - _seenExif = false; - _seenAdobe = false; - _reportedSigMatch = false; - _exifProfileOK = false; - _exifProfileText = null; - _reportedJFIF = false; - _numSegments = 0; - _numScans = 0; - _commentsList = new LinkedList(); - _jpegExtsList = new LinkedList(); - _appSegsList = new LinkedList(); - _primaryImageList = new LinkedList(); - _quantTables = new LinkedList(); - _arithCondTables = new LinkedList(); - _srsList = new LinkedList(); - _expList = new LinkedList(); - _exifProp = null; - _xmpProp = null; - _capability0 = -1; - _capability1 = -1; - _transformFlag = -1; - _baosIccProfile = new ByteArrayOutputStream(); - } - - /** - * Initializes the constant portions of the niso metadata. - */ - protected void initNiso() { - _niso.setMimeType("image/jpeg"); - _niso.setByteOrder("big-endian"); - _niso.setCompressionScheme(6); // JPEG compression - } - - /* This just reads the initial SOI */ - protected boolean readHeader(RepInfo info) { - int i; - int ch; - boolean valid = true; - try { - for (i = 0; i < 2; i++) { - ch = readUnsignedByte(_dstream, this); - if (ch != sigByte[i]) { - valid = false; - break; - } - } - } catch (IOException e) { - valid = false; - } - if (!valid) { - info.setMessage(new ErrorMessage( - MessageConstants.JPEG_HUL_4, 0)); - info.setWellFormed(false); - return false; - } - return true; - } - - /* - * Reads an APP0 marker segment. We have already read the APP0 marker - * itself. - */ - @SuppressWarnings("fallthrough") - protected void readAPP0(RepInfo info) throws IOException { - // Bytes for JFIF extension APP0 - final int jfxxByte[] = { 0X4A, 0X46, 0X58, 0X58, 0X00 }; - // Bytes for JFIF base APP0 - final int jfifByte[] = { 0X4A, 0X46, 0X49, 0X46, 0X00 }; - - /* Seeing an APP0 segment counts as seeing a signature. */ - if (!_reportedSigMatch) { - info.setSigMatch(_name); - _reportedSigMatch = true; - } - reportAppExt(0XE0, info); - - int[] ident = new int[5]; - int length = readUnsignedShort(_dstream); - - // It appears that a meaningless JFIF marker can be included - // in a valid SPIFF file. Ignore it. - if (_seenSPIFF) { - skipBytes(_dstream, length - 2, this); - return; - } - - for (int i = 0; i < 5; i++) { - ident[i] = readUnsignedByte(_dstream, this); - } - if (equalArray(ident, jfifByte)) { - if (_numSegments > 1) { - if (!_seenExif) { - LOGGER.fine("Seen Exif " + _seenExif + " exif profile ok " - + _exifProfileOK); - // Apparently this is OK in a exif file - info.setMessage(new ErrorMessage( - MessageConstants.JPEG_HUL_5, - _nByte)); - info.setValid(false); - skipBytes(_dstream, length - 7, this); - } - } else { - _seenJFIFFirst = true; - } - // This is a JFIF APP0 marker. It may come only - // at the beginning of a file, except for Exif profiles. - _seenJFIF = true; - int majorVersion = readUnsignedByte(_dstream, this); - int minorVersion = readUnsignedByte(_dstream, this); - // Format version as M.mm - String vsn = Integer.toString(majorVersion) + "." - + minorFmt.format(minorVersion); - info.setVersion(vsn); - _units = readUnsignedByte(_dstream, this); - if (_units >= 0 && _units <= 2) { - // inches, cm, and no specified unit map linearly - // to NISO values - _niso.setSamplingFrequencyUnit(_units + 1); - } - _xDensity = readUnsignedShort(_dstream); - _yDensity = readUnsignedShort(_dstream); - if (_units != 0) { - _niso.setXSamplingFrequency(new Rational(_xDensity, 1)); - _niso.setYSamplingFrequency(new Rational(_yDensity, 1)); - } - int xThumbPix = readUnsignedByte(_dstream, this); - int yThumbPix = readUnsignedByte(_dstream, this); - - // If there is a thumbnail, create a property for it - if (xThumbPix > 0 && yThumbPix > 0) { - NisoImageMetadata thumbNiso = new NisoImageMetadata(); - thumbNiso.setImageWidth(xThumbPix); - thumbNiso.setImageLength(yThumbPix); - thumbNiso.setColorSpace(CS_RGB); // RGB - thumbNiso.setCompressionScheme(1); // uncompressed - thumbNiso.setPixelSize(8); - - List thumbPropList = new LinkedList(); - thumbPropList.add(new Property(NISO_IMAGE_MD, - PropertyType.NISOIMAGEMETADATA, thumbNiso)); - Property thumbProp = new Property("ThumbImage", - PropertyType.PROPERTY, PropertyArity.LIST, - thumbPropList); - _imageList.add(thumbProp); - } - _niso.setColorSpace(CS_YCC); // JFIF header usually implies YCbCr - skipBytes(_dstream, 3 * xThumbPix * yThumbPix, this); - } else if (equalArray(ident, jfxxByte)) { - int extCode = readUnsignedByte(_dstream, this); - switch (extCode) { - // The extension codes 0x10, 0x11, and 0x13 indicate - // different thumbnail formats. - - // 0x10 indicates that the thumbnail is itself a JPEG - // stream! Yech! Have to call the module recursively? - // Skip for now. - case 0x11: - // thumbnail, palette color, 1 byte/pixel (fall through) - case 0x13: - // thumbnail, RGB, 3 bytes/pixel - // Both of these have the same relevant information, the - // width and height. We just grab those and skip the rest. - int xThumbPix = readUnsignedByte(_dstream, this); - int yThumbPix = readUnsignedByte(_dstream, this); - skipBytes(_dstream, length - 10, this); - NisoImageMetadata thumbNiso = new NisoImageMetadata(); - thumbNiso.setImageWidth(xThumbPix); - thumbNiso.setImageLength(yThumbPix); - thumbNiso.setColorSpace(extCode == 0x13 ? CS_RGB : CS_PALETTE); - thumbNiso.setCompressionScheme(1); // uncompressed - thumbNiso.setPixelSize(8); - List thumbPropList = new LinkedList(); - thumbPropList.add(new Property(NISO_IMAGE_MD, - PropertyType.NISOIMAGEMETADATA, thumbNiso)); - Property thumbProp = new Property("ThumbImage", - PropertyType.PROPERTY, PropertyArity.LIST, - thumbPropList); - _imageList.add(thumbProp); - break; - - default: - skipBytes(_dstream, length - 8, this); - break; - - // we may want to do stuff with the JFXX APP0 - } - } else { - skipBytes(_dstream, length - 7, this); - } - } - - /* - * Reads an APP1 marker segment. This may contain Exif data, i.e., a whole - * TIFF file embedded in the segment. - */ - protected void readAPP1(RepInfo info) throws IOException { - final int[] exifByte = { 0x45, 0x78, 0x69, 0x66, 0x00, 0x00 }; - // First 6 bytes of xmpStr - final int[] xmpByte = { 0x68, 0x74, 0x74, 0x70, 0x3A, 0x2F }; - final String xmpStr = "http://ns.adobe.com/xap/1.0/"; - reportAppExt(0XE1, info); - - int[] ident = new int[6]; - int length = readUnsignedShort(_dstream); - if (length < 8) { - // Guard against pathological short packets. - skipBytes(_dstream, length - 2, this); - return; - } - for (int i = 0; i < 6; i++) { - ident[i] = readUnsignedByte(_dstream, this); - } - if (equalArray(ident, exifByte)) { - // Some camera images have only an APP1 segment with - // Exif information to mark them, so count that as - // a "signature." - if (!_reportedSigMatch) { - info.setSigMatch(_name); - _reportedSigMatch = true; - } - - // Theoretically, the TIFF module could be missing, - // in which case we can't do anything, so check - // it first. - _seenExif = true; - if (!JpegExif.isTiffAvailable()) { - info.setMessage(new InfoMessage( - MessageConstants.INF_EXIF_REPORT_REQUIRES_TIFF, - _nByte)); - skipBytes(_dstream, length - 8, this); - return; - } - JpegExif je = new JpegExif(this); - RepInfo exifInfo = je.readExifData(_dstream, _je, length); - if (exifInfo != null) { - /* Copy any EXIF messages into the JPEG info object. */ - List list = exifInfo.getMessage(); - int size = list.size(); - for (int i = 0; i < size; i++) { - Message msg = list.get(i); - // Skip message JPEG deprecated in TIFF !!! - if (msg instanceof InfoMessage) { - InfoMessage imsg = (InfoMessage)msg; - if (!"TIFF-HUL-61".equals(imsg.getId())) { - info.setMessage(imsg); - } - } else { - info.setMessage(msg); - } - } - - _exifProp = exifInfo.getProperty("Exif"); - // We may also have extracted NISO metadata. - Property nisoProp = exifInfo.getProperty(NISO_IMAGE_MD); - if (nisoProp != null) { - extractExifNisoData( - (NisoImageMetadata) nisoProp.getValue()); - } - // Or there is info from the exif IFD - if (je.getExifNiso() != null) { - extractExifNisoData(je.getExifNiso()); - } - } - if (_niso.getSamplesPerPixel() == 3) { // EXIF images only have 3 components - _exifProfileOK = je.isExifProfileOK(); - } - _exifProfileText = je.getProfileText(); - } else if (equalArray(ident, xmpByte) && length >= 32) { - // Check if the rest of xmpStr matches - boolean match = true; - for (int i = 6; i < 28; i++) { - int ch = readUnsignedByte(_dstream, this); - --length; - if (ch != xmpStr.charAt(i)) { - match = false; - break; - } - } - if (!match) { - skipBytes(_dstream, length - 8, this); - return; - } - // This is an XMP packet, and we are now at the XMP - readUnsignedByte(_dstream, this); // skip null - --length; - byte[] xmpBuf = new byte[length - 8]; - readByteBuf(_dstream, xmpBuf, this); - _xmpProp = readXMP(xmpBuf); - } else { - skipBytes(_dstream, length - 8, this); - } - } - - /* - * Reads an APP8 marker segment. This indicates a SPIFF file, if it's found - * at the beginning of the file. If we're already in a SPIFF file, it's a - * directory entry. We have already read the APP8 marker itself. - */ - protected void readAPP8(RepInfo info) throws IOException { - final int[] spiffByte = { 0x53, 0x50, 0x49, 0x46, 0x46, 0x00 }; - - /* Seeing an APP8 segment counts as seeing a signature. */ - if (!_reportedSigMatch) { - info.setSigMatch(_name); - _reportedSigMatch = true; - } - reportAppExt(0xE8, info); - - int length = readUnsignedShort(_dstream); - int[] ident = new int[6]; - if (_spiffDir != null) { - // we've already started a SPIFF file, so this - // should be a directory entry. - _spiffDir.readDirEntry(_dstream, length); - return; - } - for (int i = 0; i < 6; i++) { - ident[i] = readUnsignedByte(_dstream, this); - } - if (equalArray(ident, spiffByte)) { - if (_numSegments > 1) { - info.setMessage(new ErrorMessage( - MessageConstants.JPEG_HUL_8, _nByte)); - info.setValid(false); - } - // This is a SPIFF marker. It may come only - // at the beginning of a file. - _seenSPIFF = true; - _spiffDir = new SpiffDir(this); - int majorVersion = readUnsignedByte(_dstream, this); - int minorVersion = readUnsignedByte(_dstream, this); - // Format version as M.mm - String vsn = Integer.toString(majorVersion) + "." - + minorFmt.format(minorVersion); - info.setVersion(vsn); - readUnsignedByte(_dstream, this); - readUnsignedByte(_dstream, this); - long height = readUnsignedInt(_dstream); - _niso.setImageLength(height); - long width = readUnsignedInt(_dstream); - _niso.setImageWidth(width); - - int colorSpace = readUnsignedByte(_dstream, this); - int nisoCS = Spiff.colorSpaceToNiso(colorSpace); - if (nisoCS >= 0) { - _niso.setColorSpace(nisoCS); - } - @SuppressWarnings("unused") - int bps = readUnsignedByte(_dstream, this); - int compType = readUnsignedByte(_dstream, this); - int nisoCT = Spiff.compressionTypeToNiso(compType); - if (nisoCT >= 0) { - _niso.setCompressionScheme(nisoCT); - } - int units = readUnsignedByte(_dstream, this); - if (units > 0 && units <= 2) { - // inches, cm, and no specified unit map linearly - // to NISO values - _niso.setSamplingFrequencyUnit(units + 1); - } - @SuppressWarnings("unused") - long vRes = readUnsignedInt(_dstream); - @SuppressWarnings("unused") - long hRes = readUnsignedInt(_dstream); - // These are fixed point numbers (does it say where the - // point is?) unless units == 0, in which case there's - // just an aspect ration of vres/hres. - } else { - skipBytes(_dstream, length - 8, this); - } - } - - /* - * Reads an APP2 marker segment. This may include an ICC_PROFILE. - */ - protected void readAPP2(RepInfo info) throws IOException { - final String iccProfileSequence = "ICC_PROFILE\0"; - final int SEQUENCE_LENGTH = 12; - - reportAppExt(0XE2, info); - - // The length field of a JPEG marker is only two bytes long; - // the length of the length field is included in the total. - int length = readUnsignedShort(_dstream); - byte[] ident = new byte[SEQUENCE_LENGTH]; - for (int i = 0; i < SEQUENCE_LENGTH; i++) { - ident[i] = (byte) readUnsignedByte(_dstream, this); - } - String sIdent = new String(ident, "US-ASCII"); - if (!iccProfileSequence.equalsIgnoreCase(sIdent)) { - // This is not a APP2 segment containing an ICC_PROFILE - skipBytes(_dstream, length - SEQUENCE_LENGTH - 2, this); - return; - } - - // See http://www.color.org/ICC1-V41.pdf Annex B.4 - - // The ICC PROFILE can be on multiple chunks - int chunkNumber = readUnsignedByte(_dstream, this); - int numberOfChunks = readUnsignedByte(_dstream, this); - int profileLength = length - SEQUENCE_LENGTH - 2 - 2; - - // Read the iccprofile data - byte[] iccProfile = new byte[profileLength]; - readByteBuf(_dstream, iccProfile, this); - _baosIccProfile.write(iccProfile); - if (chunkNumber == numberOfChunks) { - // Last chunk for the ICC Profile - try { - // Validate and record the name - String desc = NisoImageMetadata.extractIccProfileDescription( - _baosIccProfile.toByteArray()); - if (desc != null) { - _niso.setProfileName(desc); - } - } catch (IllegalArgumentException ie) { - info.setMessage(new ErrorMessage( - MessageConstants.JPEG_HUL_11, ie.getMessage(), - _nByte)); - } - } - } - - /* Read the VER marker, and set version information accordingly */ - protected void readVer(RepInfo info) throws IOException { - int length = readUnsignedShort(_dstream); - int majVersion = readUnsignedByte(_dstream, this); - int minVersion = readUnsignedByte(_dstream, this); - String vsn = Integer.toString(majVersion) + "." - + minorFmt.format(minVersion); - info.setVersion(vsn); - - // The number of capability bytes is equal to - // majVersion + 1. The current code understands - // major versions through 1, and per the specs, - // will ignore these bytes if majVersion is greater - // than 1. - int skip = length - 4; - if (majVersion <= 1) { - _capability0 = readUnsignedByte(_dstream, this); - skip--; - if (majVersion == 1) { - _capability1 = readUnsignedByte(_dstream, this); - skip--; - } - } - skipBytes(_dstream, skip, this); - _seenJPEGL = false; // Not permitted under JPEG-L - } - - /* Read the DTI segment, and begin setting up the tiling property */ - protected void readDTI(RepInfo info) throws IOException { - readUnsignedShort(_dstream); - _tiling = new Tiling(); - _tiling.setTilingType(readUnsignedByte(_dstream, this)); - _tiling.setVertScale(readUnsignedShort(_dstream)); - _tiling.setHorScale(readUnsignedShort(_dstream)); - _tiling.setRefGridHeight(readUnsignedInt(_dstream)); - _tiling.setRefGridWidth(readUnsignedInt(_dstream)); - _seenJPEGL = false; // Not permitted under JPEG-L - } - - /* - * Read the DTT segment. There should already be a tiling property set up. - */ - protected void readDTT(RepInfo info) throws IOException { - readUnsignedShort(_dstream); - if (_tiling == null) { - info.setMessage(new ErrorMessage( - MessageConstants.JPEG_HUL_1, _nByte)); - info.setValid(false); - return; - } - long vertScale = readUnsignedInt(_dstream); - long horScale = readUnsignedInt(_dstream); - long vertOffset = readUnsignedInt(_dstream); - long horOffset = readUnsignedInt(_dstream); - _tiling.addTile(vertScale, horScale, vertOffset, horOffset); - _seenJPEGL = false; // Not permitted under JPEG-L - } - - /* - * Read an SRS segment. This provides information about progressive scans - * and so on. - */ - protected void readSRS(RepInfo info) throws IOException { - readUnsignedShort(_dstream); - int vertOffset = readUnsignedShort(_dstream); - int horOffset = readUnsignedShort(_dstream); - int vertSize = readUnsignedShort(_dstream); - int horSize = readUnsignedShort(_dstream); - _srsList.add(new SRS(vertOffset, horOffset, vertSize, horSize)); - } + /** + * **************************************************************** DEBUGGING FIELDS. All + * debugging fields should be set to false for release code. + * **************************************************************** + */ + private static final Logger LOGGER = Logger.getLogger(JpegModule.class.getName()); + + /** + * **************************************************************** PRIVATE CLASS FIELDS. + * **************************************************************** + */ + private static final String NISO_IMAGE_MD = "NisoImageMetadata"; + + private static final String NAME = "JPEG-hul"; + private static final String RELEASE = "1.5.2"; + private static final int[] DATE = {2019, 11, 05}; + private static final String[] FORMAT = { + "JPEG", + "ISO/IEC 10918-1:1994", + "Joint Photographic Experts Group", + "JFIF", + "JPEG File Interchange Format", + "SPIFF", + "ISO/IEC 10918-3:1997", + "Still Picture Interchange File Format", + "JTIP", + "ISO/IEC 10918-3:1997", + "JPEG Tiled Image Pyramid", + "JPEG-LS", + "ISO/IEC 14495", + "Adobe JPEG", + "ISO/IEC 10918-6:2013" + }; + private static final String COVERAGE = + "JPEG (ISO/IEC 10918-1:1994), JFIF 1.02, " + + "SPIFF (ISO/IEC 10918-3:1997), " + + "Exif 2.0, 2.1 (JEIDA-49-1998), 2.2 (JEITA CP-3451), 2.21 (JEITA CP-3451A), and 2.3 (JEITA CP-3451C), " + + "JTIP (ISO/IEC 10918-3:1997), JPEG-LS (ISO/IEC 14495), Adobe JPEG (ISO/IEC 10918-6:2013)"; + private static final String[] MIMETYPE = {"image/jpeg"}; + private static final String WELLFORMED = + "A JPEG file is well-formed if " + + "the first three bytes are 0xFFD8FF, it consists of one or more " + + "correctly formatted segments (using markers 0xC0 through 0xFE), " + + "and the data streams following RSTn and SOS markers are correctly " + + "terminated"; + private static final String VALIDITY = + "A JPEG file is valid if " + + "well-formed; the first non-comment segment is APP0 (with " + + "identifier 0x4A46494600, indicating JFIF or JTIP, with 1, 3 or 4 components), " + + "APP1 (with identifier (0x457869660000, indicating Exif, and only 3 components), " + + "APP8 (with identifier 0x545049464600, indicating SPIFF), " + + "or JPG7 (or SOF55, indicating JPEG-LS); " + + "D8 marker occurs only at the beginning of " + + "the file; any DTT segments are preceded by DTI segments; and all " + + "DTI segment tiling type have a value of 0, 1, or 2"; + private static final String REPINFO = + "Additional representation " + + "information includes: NISO Z39.87 Digital Still Image Technical " + + "Metadata and segment-specific metadata"; + private static final String NOTE = null; + private static final String RIGHTS = + "Copyright 2003-2007 by JSTOR and " + + "the President and Fellows of Harvard College. " + + "Released under the GNU Lesser General Public License."; + + /** + * **************************************************************** PRIVATE INSTANCE FIELDS. + * **************************************************************** + */ + private static final int CS_GRAYSCALE = 1; + + private static final int CS_RGB = 2; + private static final int CS_PALETTE = 3; + private static final int CS_YCC = 6; // YcbCr + private static final int CS_CMYK = 5; + private static final int CS_YCCK = 65535; // not existing !!! + + /* + * Profile names. These are just informal identifiers, and probably will be + * formalized later. + */ + protected String jfifProfileName = "JFIF"; + protected String spiffProfileName = "SPIFF"; + protected String exifProfileName = "Exif"; + protected String jpeglProfileName = "JPEG-L"; + protected String adobeProfileName = "Adobe JPEG"; + + /* a NumberFormat for handling the minor part of version numbers */ + protected NumberFormat minorFmt; + + /* Top-level metadata property */ + protected Property _metadata; + + /* + * Property for current image. This should go into a list of image + * properties, which in turn becomes the "image" property of _metadata. + */ + protected Property _imageProp; + + /* Exif property */ + protected Property _exifProp; + + /* XMP property */ + protected Property _xmpProp; + + /* NISO image metadata */ + protected NisoImageMetadata _niso; + + /* Top-level property list */ + protected List _propList; + + /* List of image properties. */ + protected List _imageList; + + /* Tiling information, if a DTI has been seen. */ + protected Tiling _tiling; + + /* List of quantization tables. */ + protected List _quantTables; + + /* List of arithmetic conditioning entries */ + protected List _arithCondTables; + + /* List of SRS entries. */ + protected List _srsList; + + /* Property list for the primary image. */ + protected List _primaryImageList; + + /* Number of segments read */ + protected int _numSegments; + + /* Number of scans in the current image */ + protected int _numScans; + + /* Restart interval */ + protected int _restartInterval; + + /* Flag indicating an APP0 JFIF segment has been read */ + protected boolean _seenJFIF; + + /* Flag indicating an APP0 JFIF segment has been read first */ + protected boolean _seenJFIFFirst; + + /* Flag indicating an APP8 SPIFF segment has been read */ + protected boolean _seenSPIFF; + + /* + * Flag indicating a JPEG-L SOF55 (aka JPG7) segment has been read + */ + protected boolean _seenJPEGL; + + /* Flag to make sure we report signature matching only once. */ + protected boolean _reportedSigMatch; + + /* SPIFF directory information. */ + protected SpiffDir _spiffDir; + + /* Flag indicating an APP1 Exif segment has been read */ + protected boolean _seenExif; + + /* Flag indicating the Exif profile is satisfied */ + protected boolean _exifProfileOK; + + /* Exif profile if one is satisfied, more specific than just Exif */ + protected String _exifProfileText; + + /* Flag indicating an APP14 Adobe segment has been read */ + protected boolean _seenAdobe; + + /* Flag indicating lack of a JFIF segment has been reported */ + protected boolean _reportedJFIF; + + /* Flag indicating the first SOF has been read */ + protected boolean _seenSOF; + + /* List of comment text */ + protected List _commentsList; + + /* List of extensions used */ + protected List _jpegExtsList; + + /* List of application segments used */ + protected List _appSegsList; + + /* + * List of expand reference components markers. Members are boolean[2] + */ + protected List _expList; + + /* Capability 0 byte, from VER segment. -1 if none. */ + protected int _capability0; + + /* Capability 1 byte, from VER segment. -1 if none. */ + protected int _capability1; + + /* Fixed value for first 3 bytes */ + private static final int[] sigByte = {0XFF, 0XD8, 0XFF}; + + /* Transform flag from the Adobe marker segment. */ + protected byte _transformFlag = -1; + + /* Keep all the chunks of an ICC profile */ + protected ByteArrayOutputStream _baosIccProfile = null; + + /* Resolution units. */ + protected int _units; + + /* X resolution (or pixel aspect ratio X). */ + protected int _xDensity; + + /* Y resolution (or pixel aspect ration Y). */ + protected int _yDensity; + + /** + * **************************************************************** CLASS CONSTRUCTOR. + * **************************************************************** + */ + /** Instantiate a JpegModule object. */ + public JpegModule() { + super( + NAME, + RELEASE, + DATE, + FORMAT, + COVERAGE, + MIMETYPE, + WELLFORMED, + VALIDITY, + REPINFO, + NOTE, + RIGHTS, + false); + + // Set up a simple NumberFormat for version reporting + minorFmt = NumberFormat.getInstance(); + minorFmt.setMinimumIntegerDigits(2); + + // Define HUL vendor agent + _vendor = Agent.harvardInstance(); + + // Define C-Cube JPEG 1.02 doc + Document doc = + new Document( + "Eric Hamilton, JPEG File Interchange Format, " + "Version 1.02, September 1, 1992", + DocumentType.WEB); + Agent agent = + new Agent.Builder("C-Cube Microsystems", AgentType.COMMERCIAL) + .address("1778 McCarthy Boulevard, Milipitas, CA 95035") + .telephone("+1 (408) 944-6314") + .fax("+1 (408) 944-6314") + .build(); + doc.setPublisher(agent); + doc.setDate("1992-09-01"); + doc.setIdentifier( + new Identifier("http://www.w3.org/Graphics/JPEG/jfif3.pdf", IdentifierType.URL)); + _specification.add(doc); + + // Define ISO standard + doc = + new Document( + "ISO/IEC 10918-1:1994(E), Information technology -- " + + "Digital compression and coding of continuous-tone " + + "still images: Requirements and guidelines", + DocumentType.STANDARD); + Agent isoAgent = Agent.newIsoInstance(); + doc.setPublisher(isoAgent); + doc.setIdentifier(new Identifier("CCITT REc. T.81 (1992 E)", IdentifierType.CCITT)); + _specification.add(doc); + + // Define ISO extensions + doc = + new Document( + "ISO/IEC 10918-3:1997(E), Digital compression" + + "and coding of continuous-tone still-images: " + + "Extensions", + DocumentType.STANDARD); + doc.setPublisher(isoAgent); + doc.setIdentifier(new Identifier("ITU-T Rec. T.84 (1996 E)", IdentifierType.CCITT)); + _specification.add(doc); + + // Define ISO lossless baseline + doc = + new Document( + "ISO/IEC 14495-1:1999(E), Information technology -- " + + "Lossless and near-lossless compression of " + + "continuous-tone still images: Baseline", + DocumentType.STANDARD); + doc.setPublisher(isoAgent); + _specification.add(doc); + + // Define ISO lossless extensions + doc = + new Document( + "ISO/IEC 14495-2:2003(E), Information technology -- " + + "Lossless and near-lossless compression of " + + "continuous-tone still images: Extensions", + DocumentType.STANDARD); + doc.setPublisher(isoAgent); + _specification.add(doc); + + // Define ISO Adobe extensions + doc = + new Document( + "ISO/IEC 10918-6:2013(E), Digital compression" + + "and coding of continuous-tone still-images: " + + "Application to printing systems", + DocumentType.STANDARD); + doc.setPublisher(isoAgent); + doc.setIdentifier(new Identifier("ITU-T Rec. T.872 (06/12)", IdentifierType.CCITT)); + _specification.add(doc); + + // Define JEITA Exif 2.3 doc + doc = + new Document( + "Exchangeable image file format for digital " + "still cameras: Exif Version 2.3", + DocumentType.STANDARD); + Agent jeitaAgent = + new Agent.Builder( + "Japan Electronics and Information Technology " + "Industries Association", + AgentType.STANDARD) + .web("http://www.jeita.or.jp/") + .address( + "Mitsui Sumitomo Kaijo Building Annex, " + + "11, Kanda Surugadai 3-chome, Chiyoda-ku, " + + "Tokyo 101-0062, Japan") + .telephone("+81(03) 3518-6421") + .fax("+81(03) 3295-8721") + .build(); + doc.setPublisher(jeitaAgent); + doc.setDate("2010-04"); + Identifier ident = new Identifier("JEITA CP-3451C", IdentifierType.JEITA); + doc.setIdentifier(ident); + ident = new Identifier("http://home.jeita.or.jp/tsc/std-pdf/CP3451C.pdf", IdentifierType.URL); + doc.setIdentifier(ident); + _specification.add(doc); + + // Define JEITA Exif 2.2 doc + doc = + new Document( + "Exchangeable image file format for digital " + "still cameras: Exif Version 2.2", + DocumentType.STANDARD); + doc.setPublisher(jeitaAgent); + doc.setDate("2002-04"); + ident = new Identifier("JEITA CP-3451", IdentifierType.JEITA); + doc.setIdentifier(ident); + ident = new Identifier("http://www.exif.org/Exif2-2.PDF", IdentifierType.URL); + doc.setIdentifier(ident); + _specification.add(doc); + + // Define Exif 2.1 doc + doc = + new Document( + "Digital Still Camera Image File Format Standard " + + "(Exchangeable image file format for Digital Still Camera:Exif)", + DocumentType.STANDARD); + doc.setPublisher(jeitaAgent); + doc.setDate("1998-12"); + ident = new Identifier("JEITA JEIDA-49-1998", IdentifierType.JEITA); + doc.setIdentifier(ident); + ident = new Identifier("http://www.exif.org/dcf-exif.PDF", IdentifierType.URL); + doc.setIdentifier(ident); + _specification.add(doc); + + Signature sig = + new InternalSignature(sigByte, SignatureType.MAGIC, SignatureUseType.MANDATORY, 0, ""); + _signature.add(sig); + + sig = new ExternalSignature(".jpg", SignatureType.EXTENSION, SignatureUseType.OPTIONAL); + _signature.add(sig); + + sig = + new ExternalSignature( + ".jls", + SignatureType.EXTENSION, + SignatureUseType.OPTIONAL, + "Generally used for JPEG-LS (ISO/IEC 14495)"); + _signature.add(sig); + + sig = + new ExternalSignature( + ".spf", + SignatureType.EXTENSION, + SignatureUseType.OPTIONAL, + "Generally used for SPIFF (ISO/IEC 10918-3:1997)"); + _signature.add(sig); + + _bigEndian = true; + } + + /** + * **************************************************************** Parsing methods. + * **************************************************************** + */ + + /** + * Check if the digital object conforms to this Module's internal signature information. + * + * @param file A RandomAccessFile, positioned at its beginning, which is generated from the object + * to be parsed + * @param stream An InputStream, positioned at its beginning, which is generated from the object + * to be parsed + * @param info A fresh RepInfo object which will be modified to reflect the results of the test + */ + @Override + public void checkSignatures(File file, InputStream stream, RepInfo info) throws IOException { + int i; + int ch; + _dstream = getBufferedDataStream(stream, _je != null ? _je.getBufferSize() : 0); + for (i = 0; i < 3; i++) { + try { + ch = readUnsignedByte(_dstream, this); + } catch (Exception e) { + ch = -1; + } + if (ch != sigByte[i]) { + info.setWellFormed(false); + return; + } + } + info.setModule(this); + info.setFormat(_format[0]); + info.setMimeType(_mimeType[0]); + info.setSigMatch(_name); + } + + /** + * Parse the content of a purported JPEG stream digital object and store the results in RepInfo. + * + *

This function uses the JPEG-L method of detecting a marker following a data stream, checking + * for a 0 high bit rather than an entire 0 byte. So long at no JPEG markers are defined with a + * value from 0 through 7F, this is valid for all JPEG files. + * + * @param stream An InputStream, positioned at its beginning, which is generated from the object + * to be parsed + * @param info A fresh RepInfo object which will be modified to reflect the results of the parsing + * @param parseIndex Must be 0 in first call to parse. If parse returns + * a nonzero value, it must be called again with parseIndex equal to that return + * value. + */ + @Override + public int parse(InputStream stream, RepInfo info, int parseIndex) throws IOException { + initParse(); + info.setFormat(_format[0]); + info.setMimeType(_mimeType[0]); + info.setModule(this); /* - * Reads an APP14 marker segment. This indicates a Adobe file. - * We have already read the APP14 marker itself. + * We may have already done the checksums while converting a temporary + * file. */ - protected void readAPP14(RepInfo info) throws IOException { - final String ADOBE_SEQUENCE = "Adobe\0"; - final int SEQUENCE_LENGTH = 6; - reportAppExt(0xEE, info); - - int length = readUnsignedShort(_dstream); - if (length < 8) { - // Guard against pathological short packets. - skipBytes(_dstream, length - 2, this); - return; + setupDataStream(stream, info); + _propList = new LinkedList(); + _metadata = new Property("JPEGMetadata", PropertyType.PROPERTY, PropertyArity.LIST, _propList); + if (!readHeader(info)) { + return 0; + } + _niso = new NisoImageMetadata(); + Property nisoProp = new Property(NISO_IMAGE_MD, PropertyType.NISOIMAGEMETADATA, _niso); + _primaryImageList.add(nisoProp); + initNiso(); + + // Count the number of segments read, exclusive of the APP0 header. + boolean dataPlowing = false; + ErrorMessage msg; + try { + // When true, we have to go through data till we find a marker + loop1: + for (; ; ) { + int dbyt = 0; + boolean sawFF = false; + if (dataPlowing) { + for (; ; ) { + dbyt = readUnsignedByte(_dstream, this); + if (dbyt == 0xFF) { + sawFF = true; + // multiple FF's count same as one + } else if (sawFF) { + // Note use of JPEG-L check. For a + // standard JPEG check, we would use + // (dbyt != 0) + if ((dbyt & 0x80) != 0) { + dataPlowing = false; + break; + } + // FF followed by 0 is discarded + sawFF = false; + } + } + } else { + dbyt = readUnsignedByte(_dstream, this); + if (dbyt != 0xFF) { + info.setMessage( + new ErrorMessage( + MessageConstants.JPEG_HUL_7, + String.format(MessageConstants.JPEG_HUL_7_SUB.getMessage(), dbyt), + _nByte)); + info.setWellFormed(false); + return 0; + } + // There can be padding bytes equal to FF, + // so read till we get one that isn't. + while (dbyt == 0xFF) { + dbyt = readUnsignedByte(_dstream, this); + } + } + _numSegments++; + if (!_seenJFIF + && !_seenSPIFF + && !_seenExif + && !_seenJPEGL + && _numSegments >= 2 + && !_reportedJFIF) { + info.setMessage(new ErrorMessage(MessageConstants.JPEG_HUL_9, _nByte)); + info.setValid(false); + _reportedJFIF = true; } + if (dbyt >= 0xD0 && dbyt <= 0xD7) { + // RST[m] -- Restart with modulo 8 count 0-7 + dataPlowing = true; + } else if (dbyt >= 0xF7 && dbyt <= 0xFD) { + // JPGn extension + readJPEGExtension(dbyt, info); + } else + switch (dbyt) { + case 0: + // Byte stuffing -- ignore + break; + + case 0xC0: + case 0xC1: + case 0xC2: + case 0xC3: + case 0xC5: + case 0xC6: + case 0xC7: + case 0xC9: + case 0xCA: + case 0xCB: + case 0xCD: + case 0xCE: + case 0xCF: + // SOF(n) marker; value indicates encoding type + readSOF(dbyt, info); + break; + + case 0xC4: + // DHT -- define Huffman tables + skipSegment(info); + break; + + case 0xCC: + // DAC -- define arithmetic coding conditioning + readDAC(info); + break; + + case 0xD9: + // EOI + break loop1; + + case 0xDA: + // SOS -- start of scan. This is followed by data. + skipSegment(info); + ++_numScans; + dataPlowing = true; + break; + + case 0xDB: + // DQT -- define quantization tables + readDQT(info); + break; + + case 0xDC: + // DNL -- define number of lines + skipSegment(info); + break; + + case 0xDD: + // DRI -- define restart interval + readDRI(info); + break; + + case 0xDE: + // DHP -- define hierarchical progression + readDHP(info); + break; + + case 0xDF: + // EXP -- Expand reference component + readEXP(info); + break; + + case 0xE0: + // APP0 extension + readAPP0(info); + break; + + case 0xE1: + readAPP1(info); + break; + + case 0xE2: + readAPP2(info); + break; + + case 0xE8: + // APP8 extension + readAPP8(info); + break; + + case 0xEE: + readAPP14(info); + break; + + case 0xE3: + case 0xE4: + case 0xE5: + case 0xE6: + case 0xE7: + case 0xE9: + case 0xEA: + case 0xEB: + case 0xEC: + case 0xED: + case 0xEF: + // Appn extensions which we don't handle specially, + // but do report the existence thereof + reportAppExt(dbyt, info); + skipSegment(info); + break; + + case 0XF0: + // VER segment + readVer(info); + break; + + case 0XF1: + // DTI (defined tiled image) segment + readDTI(info); + break; + + case 0XF2: + // DTI (defined tile) segment + readDTT(info); + break; + + case 0XF4: + // SRS (selectively refined scan) segment + readSRS(info); + break; + + case 0XFE: + // comment + --_numSegments; // don't let comment trigger error + readComment(info); + break; + + default: + // Other values don't belong at the top level. + msg = new ErrorMessage(MessageConstants.JPEG_HUL_6, _nByte); + info.setMessage(msg); + info.setValid(false); + break loop1; + } + } + + } catch (EOFException e) { + msg = new ErrorMessage(MessageConstants.JPEG_HUL_2, _nByte); + info.setMessage(msg); + info.setWellFormed(false); + return 0; + } + + info.setProperty(_metadata); + + if (_units == 0) { + List list = new ArrayList(); + list.add(new Property("PixelAspectRatioX", PropertyType.INTEGER, _xDensity)); + list.add(new Property("PixelAspectRatioY", PropertyType.INTEGER, _yDensity)); + _primaryImageList.add( + new Property("PixelAspectRatio", PropertyType.PROPERTY, PropertyArity.LIST, list)); + } + + // If there's tiling information, create a property for the + // primary image list. + if (_tiling != null) { + Property tp = buildTilingProp(info); + if (tp != null) { + _primaryImageList.add(tp); + } + } + if (_restartInterval >= 0) { + _primaryImageList.add( + new Property("RestartInterval", PropertyType.INTEGER, _restartInterval)); + } + _primaryImageList.add(new Property("Scans", PropertyType.INTEGER, _numScans)); + if (!_quantTables.isEmpty()) { + List qpl = new LinkedList(); + ListIterator iter = _quantTables.listIterator(); + while (iter.hasNext()) { + QuantizationTable qt = iter.next(); + qpl.add(qt.makeProperty(_je.getShowRawFlag())); + } + _primaryImageList.add( + new Property("QuantizationTables", PropertyType.PROPERTY, PropertyArity.LIST, qpl)); + } - byte[] ident = new byte[SEQUENCE_LENGTH]; - for (int i = 0; i < SEQUENCE_LENGTH; i++) { - ident[i] = (byte)readUnsignedByte(_dstream, this); + if (!_arithCondTables.isEmpty()) { + List qpl = new LinkedList(); + ListIterator iter = _arithCondTables.listIterator(); + while (iter.hasNext()) { + ArithConditioning qt = iter.next(); + qpl.add(qt.makeProperty(_je.getShowRawFlag())); + } + _primaryImageList.add( + new Property("ArithmeticConditioning", PropertyType.PROPERTY, PropertyArity.LIST, qpl)); + } + if (!_srsList.isEmpty()) { + List srsl = new LinkedList(); + ListIterator iter = _srsList.listIterator(); + while (iter.hasNext()) { + SRS s = iter.next(); + srsl.add(s.makeProperty()); + } + _primaryImageList.add( + new Property("SelectivelyRefinedScans", PropertyType.PROPERTY, PropertyArity.LIST, srsl)); + } + + if (_ckSummer != null) { + skipDstreamToEnd(info); + // Set the checksums in the report if they're calculated + setChecksums(this._ckSummer, info); + } + + // Put the primary image in the image list. + _imageList.add( + new Property("Image", PropertyType.PROPERTY, PropertyArity.LIST, _primaryImageList)); + + // Report profiles. + if (_seenJFIF && _seenJFIFFirst) { + info.setProfile(jfifProfileName); + } + if (_seenExif && _exifProfileOK) { + if (_exifProfileText != null) { + info.setProfile(_exifProfileText); + } else { + info.setProfile(exifProfileName); + } + } + if (_seenSPIFF) { + info.setProfile(spiffProfileName); + if (_spiffDir != null) { + // Grab any image properties from the SPIFF directory + // and add them to the image list. + _spiffDir.appendThumbnailProps(_imageList); + } + } + if (_seenJPEGL) { + info.setProfile(jpeglProfileName); + } + if (_seenAdobe) { + info.setProfile(adobeProfileName); + } + /* + * Create a new property list containing the count of the images and the + * list of image properties. + */ + List list = new ArrayList(); + list.add(new Property("Number", PropertyType.INTEGER, PropertyArity.SCALAR, _imageList.size())); + Iterator iter = _imageList.iterator(); + while (iter.hasNext()) { + Property prop = iter.next(); + list.add(prop); + } + _propList.add(new Property("Images", PropertyType.PROPERTY, PropertyArity.LIST, list)); + // _imageList)); + if (!_commentsList.isEmpty()) { + _propList.add( + new Property("Comments", PropertyType.STRING, PropertyArity.LIST, _commentsList)); + } + if (!_jpegExtsList.isEmpty()) { + _propList.add( + new Property("Extensions", PropertyType.STRING, PropertyArity.LIST, _jpegExtsList)); + } + if (!_appSegsList.isEmpty()) { + _propList.add( + new Property( + "ApplicationSegments", PropertyType.STRING, PropertyArity.LIST, _appSegsList)); + } + if (!_expList.isEmpty()) { + _propList.add(buildExpandProp(info)); + } + if (_exifProp != null) { + _primaryImageList.add(_exifProp); + } + if (_xmpProp != null) { + _primaryImageList.add(_xmpProp); + } + return 0; + } + + /** + * One-argument version of readUnsignedShort. JPEG is always big-endian, so + * readUnsignedShort can unambiguously drop its endian argument. + */ + public int readUnsignedShort(DataInputStream stream) throws IOException { + return readUnsignedShort(stream, true, this); + } + + /** + * One-argument version of readUnsignedInt. JPEG is always big-endian, so + * readUnsignedInt can unambiguously drop its endian argument. + */ + public long readUnsignedInt(DataInputStream stream) throws IOException { + return readUnsignedInt(stream, true, this); + } + + /** Initializes the state of the module for parsing. */ + @Override + protected void initParse() { + super.initParse(); + _imageList = new LinkedList(); + _tiling = null; + _restartInterval = -1; + _seenSOF = false; + _seenJFIF = false; + _seenJFIFFirst = false; + _seenSPIFF = false; + _seenJPEGL = false; + _spiffDir = null; + _seenExif = false; + _seenAdobe = false; + _reportedSigMatch = false; + _exifProfileOK = false; + _exifProfileText = null; + _reportedJFIF = false; + _numSegments = 0; + _numScans = 0; + _commentsList = new LinkedList(); + _jpegExtsList = new LinkedList(); + _appSegsList = new LinkedList(); + _primaryImageList = new LinkedList(); + _quantTables = new LinkedList(); + _arithCondTables = new LinkedList(); + _srsList = new LinkedList(); + _expList = new LinkedList(); + _exifProp = null; + _xmpProp = null; + _capability0 = -1; + _capability1 = -1; + _transformFlag = -1; + _baosIccProfile = new ByteArrayOutputStream(); + } + + /** Initializes the constant portions of the niso metadata. */ + protected void initNiso() { + _niso.setMimeType("image/jpeg"); + _niso.setByteOrder("big-endian"); + _niso.setCompressionScheme(6); // JPEG compression + } + + /* This just reads the initial SOI */ + protected boolean readHeader(RepInfo info) { + int i; + int ch; + boolean valid = true; + try { + for (i = 0; i < 2; i++) { + ch = readUnsignedByte(_dstream, this); + if (ch != sigByte[i]) { + valid = false; + break; } - String sIdent = new String(ident, "US-ASCII"); + } + } catch (IOException e) { + valid = false; + } + if (!valid) { + info.setMessage(new ErrorMessage(MessageConstants.JPEG_HUL_4, 0)); + info.setWellFormed(false); + return false; + } + return true; + } + + /* + * Reads an APP0 marker segment. We have already read the APP0 marker + * itself. + */ + @SuppressWarnings("fallthrough") + protected void readAPP0(RepInfo info) throws IOException { + // Bytes for JFIF extension APP0 + final int jfxxByte[] = {0X4A, 0X46, 0X58, 0X58, 0X00}; + // Bytes for JFIF base APP0 + final int jfifByte[] = {0X4A, 0X46, 0X49, 0X46, 0X00}; + + /* Seeing an APP0 segment counts as seeing a signature. */ + if (!_reportedSigMatch) { + info.setSigMatch(_name); + _reportedSigMatch = true; + } + reportAppExt(0XE0, info); - int skip = length - SEQUENCE_LENGTH - 2; - if (!ADOBE_SEQUENCE.equalsIgnoreCase(sIdent)) { - // This is not a APP14 segment containing an Adobe - skipBytes(_dstream, skip, this); - return; + int[] ident = new int[5]; + int length = readUnsignedShort(_dstream); + + // It appears that a meaningless JFIF marker can be included + // in a valid SPIFF file. Ignore it. + if (_seenSPIFF) { + skipBytes(_dstream, length - 2, this); + return; + } + + for (int i = 0; i < 5; i++) { + ident[i] = readUnsignedByte(_dstream, this); + } + if (equalArray(ident, jfifByte)) { + if (_numSegments > 1) { + if (!_seenExif) { + LOGGER.fine("Seen Exif " + _seenExif + " exif profile ok " + _exifProfileOK); + // Apparently this is OK in a exif file + info.setMessage(new ErrorMessage(MessageConstants.JPEG_HUL_5, _nByte)); + info.setValid(false); + skipBytes(_dstream, length - 7, this); + } + } else { + _seenJFIFFirst = true; + } + // This is a JFIF APP0 marker. It may come only + // at the beginning of a file, except for Exif profiles. + _seenJFIF = true; + int majorVersion = readUnsignedByte(_dstream, this); + int minorVersion = readUnsignedByte(_dstream, this); + // Format version as M.mm + String vsn = Integer.toString(majorVersion) + "." + minorFmt.format(minorVersion); + info.setVersion(vsn); + _units = readUnsignedByte(_dstream, this); + if (_units >= 0 && _units <= 2) { + // inches, cm, and no specified unit map linearly + // to NISO values + _niso.setSamplingFrequencyUnit(_units + 1); + } + _xDensity = readUnsignedShort(_dstream); + _yDensity = readUnsignedShort(_dstream); + if (_units != 0) { + _niso.setXSamplingFrequency(new Rational(_xDensity, 1)); + _niso.setYSamplingFrequency(new Rational(_yDensity, 1)); + } + int xThumbPix = readUnsignedByte(_dstream, this); + int yThumbPix = readUnsignedByte(_dstream, this); + + // If there is a thumbnail, create a property for it + if (xThumbPix > 0 && yThumbPix > 0) { + NisoImageMetadata thumbNiso = new NisoImageMetadata(); + thumbNiso.setImageWidth(xThumbPix); + thumbNiso.setImageLength(yThumbPix); + thumbNiso.setColorSpace(CS_RGB); // RGB + thumbNiso.setCompressionScheme(1); // uncompressed + thumbNiso.setPixelSize(8); + + List thumbPropList = new LinkedList(); + thumbPropList.add(new Property(NISO_IMAGE_MD, PropertyType.NISOIMAGEMETADATA, thumbNiso)); + Property thumbProp = + new Property("ThumbImage", PropertyType.PROPERTY, PropertyArity.LIST, thumbPropList); + _imageList.add(thumbProp); + } + _niso.setColorSpace(CS_YCC); // JFIF header usually implies YCbCr + skipBytes(_dstream, 3 * xThumbPix * yThumbPix, this); + } else if (equalArray(ident, jfxxByte)) { + int extCode = readUnsignedByte(_dstream, this); + switch (extCode) { + // The extension codes 0x10, 0x11, and 0x13 indicate + // different thumbnail formats. + + // 0x10 indicates that the thumbnail is itself a JPEG + // stream! Yech! Have to call the module recursively? + // Skip for now. + case 0x11: + // thumbnail, palette color, 1 byte/pixel (fall through) + case 0x13: + // thumbnail, RGB, 3 bytes/pixel + // Both of these have the same relevant information, the + // width and height. We just grab those and skip the rest. + int xThumbPix = readUnsignedByte(_dstream, this); + int yThumbPix = readUnsignedByte(_dstream, this); + skipBytes(_dstream, length - 10, this); + NisoImageMetadata thumbNiso = new NisoImageMetadata(); + thumbNiso.setImageWidth(xThumbPix); + thumbNiso.setImageLength(yThumbPix); + thumbNiso.setColorSpace(extCode == 0x13 ? CS_RGB : CS_PALETTE); + thumbNiso.setCompressionScheme(1); // uncompressed + thumbNiso.setPixelSize(8); + List thumbPropList = new LinkedList(); + thumbPropList.add(new Property(NISO_IMAGE_MD, PropertyType.NISOIMAGEMETADATA, thumbNiso)); + Property thumbProp = + new Property("ThumbImage", PropertyType.PROPERTY, PropertyArity.LIST, thumbPropList); + _imageList.add(thumbProp); + break; + + default: + skipBytes(_dstream, length - 8, this); + break; + + // we may want to do stuff with the JFXX APP0 + } + } else { + skipBytes(_dstream, length - 7, this); + } + } + + /* + * Reads an APP1 marker segment. This may contain Exif data, i.e., a whole + * TIFF file embedded in the segment. + */ + protected void readAPP1(RepInfo info) throws IOException { + final int[] exifByte = {0x45, 0x78, 0x69, 0x66, 0x00, 0x00}; + // First 6 bytes of xmpStr + final int[] xmpByte = {0x68, 0x74, 0x74, 0x70, 0x3A, 0x2F}; + final String xmpStr = "http://ns.adobe.com/xap/1.0/"; + reportAppExt(0XE1, info); + + int[] ident = new int[6]; + int length = readUnsignedShort(_dstream); + if (length < 8) { + // Guard against pathological short packets. + skipBytes(_dstream, length - 2, this); + return; + } + for (int i = 0; i < 6; i++) { + ident[i] = readUnsignedByte(_dstream, this); + } + if (equalArray(ident, exifByte)) { + // Some camera images have only an APP1 segment with + // Exif information to mark them, so count that as + // a "signature." + if (!_reportedSigMatch) { + info.setSigMatch(_name); + _reportedSigMatch = true; + } + + // Theoretically, the TIFF module could be missing, + // in which case we can't do anything, so check + // it first. + _seenExif = true; + if (!JpegExif.isTiffAvailable()) { + info.setMessage(new InfoMessage(MessageConstants.INF_EXIF_REPORT_REQUIRES_TIFF, _nByte)); + skipBytes(_dstream, length - 8, this); + return; + } + JpegExif je = new JpegExif(this); + RepInfo exifInfo = je.readExifData(_dstream, _je, length); + if (exifInfo != null) { + /* Copy any EXIF messages into the JPEG info object. */ + List list = exifInfo.getMessage(); + int size = list.size(); + for (int i = 0; i < size; i++) { + Message msg = list.get(i); + // Skip message JPEG deprecated in TIFF !!! + if (msg instanceof InfoMessage) { + InfoMessage imsg = (InfoMessage) msg; + if (!"TIFF-HUL-61".equals(imsg.getId())) { + info.setMessage(imsg); + } + } else { + info.setMessage(msg); + } } - // This is a Adobe marker. - _seenAdobe = true; - // Skip the head of the segment - while (skip > 1) { - readUnsignedByte(_dstream, this); // version (byte), flags0 (int), flags1 (int) - skip--; + _exifProp = exifInfo.getProperty("Exif"); + // We may also have extracted NISO metadata. + Property nisoProp = exifInfo.getProperty(NISO_IMAGE_MD); + if (nisoProp != null) { + extractExifNisoData((NisoImageMetadata) nisoProp.getValue()); } - _transformFlag = (byte)readUnsignedByte(_dstream, this); - skip--; - if (_transformFlag > 2) { - String mess = String.format(MessageConstants.JPEG_HUL_12.getMessage(), _transformFlag); - JhoveMessage message = JhoveMessages.getMessageInstance(MessageConstants.JPEG_HUL_12.getId(), mess); - info.setMessage(new ErrorMessage(message, _nByte)); - info.setValid(false); + // Or there is info from the exif IFD + if (je.getExifNiso() != null) { + extractExifNisoData(je.getExifNiso()); + } + } + if (_niso.getSamplesPerPixel() == 3) { // EXIF images only have 3 components + _exifProfileOK = je.isExifProfileOK(); + } + _exifProfileText = je.getProfileText(); + } else if (equalArray(ident, xmpByte) && length >= 32) { + // Check if the rest of xmpStr matches + boolean match = true; + for (int i = 6; i < 28; i++) { + int ch = readUnsignedByte(_dstream, this); + --length; + if (ch != xmpStr.charAt(i)) { + match = false; + break; } + } + if (!match) { + skipBytes(_dstream, length - 8, this); + return; + } + // This is an XMP packet, and we are now at the XMP + readUnsignedByte(_dstream, this); // skip null + --length; + byte[] xmpBuf = new byte[length - 8]; + readByteBuf(_dstream, xmpBuf, this); + _xmpProp = readXMP(xmpBuf); + } else { + skipBytes(_dstream, length - 8, this); + } + } + + /* + * Reads an APP8 marker segment. This indicates a SPIFF file, if it's found + * at the beginning of the file. If we're already in a SPIFF file, it's a + * directory entry. We have already read the APP8 marker itself. + */ + protected void readAPP8(RepInfo info) throws IOException { + final int[] spiffByte = {0x53, 0x50, 0x49, 0x46, 0x46, 0x00}; + + /* Seeing an APP8 segment counts as seeing a signature. */ + if (!_reportedSigMatch) { + info.setSigMatch(_name); + _reportedSigMatch = true; + } + reportAppExt(0xE8, info); + + int length = readUnsignedShort(_dstream); + int[] ident = new int[6]; + if (_spiffDir != null) { + // we've already started a SPIFF file, so this + // should be a directory entry. + _spiffDir.readDirEntry(_dstream, length); + return; + } + for (int i = 0; i < 6; i++) { + ident[i] = readUnsignedByte(_dstream, this); + } + if (equalArray(ident, spiffByte)) { + if (_numSegments > 1) { + info.setMessage(new ErrorMessage(MessageConstants.JPEG_HUL_8, _nByte)); + info.setValid(false); + } + // This is a SPIFF marker. It may come only + // at the beginning of a file. + _seenSPIFF = true; + _spiffDir = new SpiffDir(this); + int majorVersion = readUnsignedByte(_dstream, this); + int minorVersion = readUnsignedByte(_dstream, this); + // Format version as M.mm + String vsn = Integer.toString(majorVersion) + "." + minorFmt.format(minorVersion); + info.setVersion(vsn); + readUnsignedByte(_dstream, this); + readUnsignedByte(_dstream, this); + long height = readUnsignedInt(_dstream); + _niso.setImageLength(height); + long width = readUnsignedInt(_dstream); + _niso.setImageWidth(width); + + int colorSpace = readUnsignedByte(_dstream, this); + int nisoCS = Spiff.colorSpaceToNiso(colorSpace); + if (nisoCS >= 0) { + _niso.setColorSpace(nisoCS); + } + @SuppressWarnings("unused") + int bps = readUnsignedByte(_dstream, this); + int compType = readUnsignedByte(_dstream, this); + int nisoCT = Spiff.compressionTypeToNiso(compType); + if (nisoCT >= 0) { + _niso.setCompressionScheme(nisoCT); + } + int units = readUnsignedByte(_dstream, this); + if (units > 0 && units <= 2) { + // inches, cm, and no specified unit map linearly + // to NISO values + _niso.setSamplingFrequencyUnit(units + 1); + } + @SuppressWarnings("unused") + long vRes = readUnsignedInt(_dstream); + @SuppressWarnings("unused") + long hRes = readUnsignedInt(_dstream); + // These are fixed point numbers (does it say where the + // point is?) unless units == 0, in which case there's + // just an aspect ration of vres/hres. + } else { + skipBytes(_dstream, length - 8, this); + } + } + + /* + * Reads an APP2 marker segment. This may include an ICC_PROFILE. + */ + protected void readAPP2(RepInfo info) throws IOException { + final String iccProfileSequence = "ICC_PROFILE\0"; + final int SEQUENCE_LENGTH = 12; + + reportAppExt(0XE2, info); + + // The length field of a JPEG marker is only two bytes long; + // the length of the length field is included in the total. + int length = readUnsignedShort(_dstream); + byte[] ident = new byte[SEQUENCE_LENGTH]; + for (int i = 0; i < SEQUENCE_LENGTH; i++) { + ident[i] = (byte) readUnsignedByte(_dstream, this); + } + String sIdent = new String(ident, "US-ASCII"); + if (!iccProfileSequence.equalsIgnoreCase(sIdent)) { + // This is not a APP2 segment containing an ICC_PROFILE + skipBytes(_dstream, length - SEQUENCE_LENGTH - 2, this); + return; } - /* - * Accumulate reports of APPn segments into a property. It's tempting to - * report information about the segment, since many APPn segments have ASCII - * identifiers, but there's no guarantee of any content beyond the length - * field, so we just report the existence of all APPn segments. No attempt - * is made to weed out duplicates, since multiple instances of the same - * segment number are legitimate and informative. - */ - protected void reportAppExt(int dbyt, RepInfo info) { - String appStr = "APP"; - if (dbyt <= 0xE9) { - // 0-9 - appStr += (char) (dbyt - 0xE0 + 0x30); - } else { - // 10-15 - appStr += "1" + (char) (dbyt - 0xEA + 0x30); - } - _appSegsList.add(appStr); - } - - /* - * Read a SOF segment. The first one is the most interesting, since it - * contains the dimensions for the image. No multi-image support right now; - * this has to be figured out, including the distinction between images and - * frames. - */ - protected void readSOF(int dbyt, RepInfo info) throws IOException { - final int[] UC_RGB = new int[] {82, 71, 66}; - final int[] LC_RGB = new int[] {114, 103, 98}; - - int length = readUnsignedShort(_dstream); - int precision = readUnsignedByte(_dstream, this); - int nLines = readUnsignedShort(_dstream); - int samPerLine = readUnsignedShort(_dstream); - int numComps = readUnsignedByte(_dstream, this); - int[] componentsId = new int[numComps]; - int skip = length - 8; - for (int i = 0; i < numComps; i++) { - componentsId[i] = readUnsignedByte(_dstream, this); - skip--; - readUnsignedByte(_dstream); // samplingFactor - skip--; - readUnsignedByte(_dstream, this); // qtableSelector - skip--; - } - if (skip > 0) { - skipBytes(_dstream, skip, this); + // See http://www.color.org/ICC1-V41.pdf Annex B.4 + + // The ICC PROFILE can be on multiple chunks + int chunkNumber = readUnsignedByte(_dstream, this); + int numberOfChunks = readUnsignedByte(_dstream, this); + int profileLength = length - SEQUENCE_LENGTH - 2 - 2; + + // Read the iccprofile data + byte[] iccProfile = new byte[profileLength]; + readByteBuf(_dstream, iccProfile, this); + _baosIccProfile.write(iccProfile); + if (chunkNumber == numberOfChunks) { + // Last chunk for the ICC Profile + try { + // Validate and record the name + String desc = NisoImageMetadata.extractIccProfileDescription(_baosIccProfile.toByteArray()); + if (desc != null) { + _niso.setProfileName(desc); } - - if (!_seenSOF) { - _niso.setImageLength(nLines); - _niso.setImageWidth(samPerLine); - int[] bps = new int[numComps]; - for (int i = 0; i < numComps; i++) { - bps[i] = precision; - } - _niso.setBitsPerSample(bps); - _niso.setSamplesPerPixel(numComps); - _propList.add(new Property("CompressionType", PropertyType.STRING, - JpegStrings.COMPRESSION_TYPE[dbyt - 0xC0])); - _seenSOF = true; - - // Define the colorspace - switch (numComps) { - case 1: // 1 component - _niso.setColorSpace(CS_GRAYSCALE); // grayscale - break; - case 3: // 3 components - if (_seenJFIF && _seenJFIFFirst) { - _niso.setColorSpace(CS_YCC); // YCbCr + } catch (IllegalArgumentException ie) { + info.setMessage(new ErrorMessage(MessageConstants.JPEG_HUL_11, ie.getMessage(), _nByte)); + } + } + } + + /* Read the VER marker, and set version information accordingly */ + protected void readVer(RepInfo info) throws IOException { + int length = readUnsignedShort(_dstream); + int majVersion = readUnsignedByte(_dstream, this); + int minVersion = readUnsignedByte(_dstream, this); + String vsn = Integer.toString(majVersion) + "." + minorFmt.format(minVersion); + info.setVersion(vsn); + + // The number of capability bytes is equal to + // majVersion + 1. The current code understands + // major versions through 1, and per the specs, + // will ignore these bytes if majVersion is greater + // than 1. + int skip = length - 4; + if (majVersion <= 1) { + _capability0 = readUnsignedByte(_dstream, this); + skip--; + if (majVersion == 1) { + _capability1 = readUnsignedByte(_dstream, this); + skip--; + } + } + skipBytes(_dstream, skip, this); + _seenJPEGL = false; // Not permitted under JPEG-L + } + + /* Read the DTI segment, and begin setting up the tiling property */ + protected void readDTI(RepInfo info) throws IOException { + readUnsignedShort(_dstream); + _tiling = new Tiling(); + _tiling.setTilingType(readUnsignedByte(_dstream, this)); + _tiling.setVertScale(readUnsignedShort(_dstream)); + _tiling.setHorScale(readUnsignedShort(_dstream)); + _tiling.setRefGridHeight(readUnsignedInt(_dstream)); + _tiling.setRefGridWidth(readUnsignedInt(_dstream)); + _seenJPEGL = false; // Not permitted under JPEG-L + } + + /* + * Read the DTT segment. There should already be a tiling property set up. + */ + protected void readDTT(RepInfo info) throws IOException { + readUnsignedShort(_dstream); + if (_tiling == null) { + info.setMessage(new ErrorMessage(MessageConstants.JPEG_HUL_1, _nByte)); + info.setValid(false); + return; + } + long vertScale = readUnsignedInt(_dstream); + long horScale = readUnsignedInt(_dstream); + long vertOffset = readUnsignedInt(_dstream); + long horOffset = readUnsignedInt(_dstream); + _tiling.addTile(vertScale, horScale, vertOffset, horOffset); + _seenJPEGL = false; // Not permitted under JPEG-L + } + + /* + * Read an SRS segment. This provides information about progressive scans + * and so on. + */ + protected void readSRS(RepInfo info) throws IOException { + readUnsignedShort(_dstream); + int vertOffset = readUnsignedShort(_dstream); + int horOffset = readUnsignedShort(_dstream); + int vertSize = readUnsignedShort(_dstream); + int horSize = readUnsignedShort(_dstream); + _srsList.add(new SRS(vertOffset, horOffset, vertSize, horSize)); + } + + /* + * Reads an APP14 marker segment. This indicates a Adobe file. + * We have already read the APP14 marker itself. + */ + protected void readAPP14(RepInfo info) throws IOException { + final String ADOBE_SEQUENCE = "Adobe\0"; + final int SEQUENCE_LENGTH = 6; + reportAppExt(0xEE, info); + + int length = readUnsignedShort(_dstream); + if (length < 8) { + // Guard against pathological short packets. + skipBytes(_dstream, length - 2, this); + return; + } + + byte[] ident = new byte[SEQUENCE_LENGTH]; + for (int i = 0; i < SEQUENCE_LENGTH; i++) { + ident[i] = (byte) readUnsignedByte(_dstream, this); + } + String sIdent = new String(ident, "US-ASCII"); + + int skip = length - SEQUENCE_LENGTH - 2; + if (!ADOBE_SEQUENCE.equalsIgnoreCase(sIdent)) { + // This is not a APP14 segment containing an Adobe + skipBytes(_dstream, skip, this); + return; + } + // This is a Adobe marker. + _seenAdobe = true; + + // Skip the head of the segment + while (skip > 1) { + readUnsignedByte(_dstream, this); // version (byte), flags0 (int), flags1 (int) + skip--; + } + _transformFlag = (byte) readUnsignedByte(_dstream, this); + skip--; + if (_transformFlag > 2) { + String mess = String.format(MessageConstants.JPEG_HUL_12.getMessage(), _transformFlag); + JhoveMessage message = + JhoveMessages.getMessageInstance(MessageConstants.JPEG_HUL_12.getId(), mess); + info.setMessage(new ErrorMessage(message, _nByte)); + info.setValid(false); + } + } + + /* + * Accumulate reports of APPn segments into a property. It's tempting to + * report information about the segment, since many APPn segments have ASCII + * identifiers, but there's no guarantee of any content beyond the length + * field, so we just report the existence of all APPn segments. No attempt + * is made to weed out duplicates, since multiple instances of the same + * segment number are legitimate and informative. + */ + protected void reportAppExt(int dbyt, RepInfo info) { + String appStr = "APP"; + if (dbyt <= 0xE9) { + // 0-9 + appStr += (char) (dbyt - 0xE0 + 0x30); + } else { + // 10-15 + appStr += "1" + (char) (dbyt - 0xEA + 0x30); + } + _appSegsList.add(appStr); + } + + /* + * Read a SOF segment. The first one is the most interesting, since it + * contains the dimensions for the image. No multi-image support right now; + * this has to be figured out, including the distinction between images and + * frames. + */ + protected void readSOF(int dbyt, RepInfo info) throws IOException { + final int[] UC_RGB = new int[] {82, 71, 66}; + final int[] LC_RGB = new int[] {114, 103, 98}; + + int length = readUnsignedShort(_dstream); + int precision = readUnsignedByte(_dstream, this); + int nLines = readUnsignedShort(_dstream); + int samPerLine = readUnsignedShort(_dstream); + int numComps = readUnsignedByte(_dstream, this); + int[] componentsId = new int[numComps]; + int skip = length - 8; + for (int i = 0; i < numComps; i++) { + componentsId[i] = readUnsignedByte(_dstream, this); + skip--; + readUnsignedByte(_dstream); // samplingFactor + skip--; + readUnsignedByte(_dstream, this); // qtableSelector + skip--; + } + if (skip > 0) { + skipBytes(_dstream, skip, this); + } + + if (!_seenSOF) { + _niso.setImageLength(nLines); + _niso.setImageWidth(samPerLine); + int[] bps = new int[numComps]; + for (int i = 0; i < numComps; i++) { + bps[i] = precision; + } + _niso.setBitsPerSample(bps); + _niso.setSamplesPerPixel(numComps); + _propList.add( + new Property( + "CompressionType", PropertyType.STRING, JpegStrings.COMPRESSION_TYPE[dbyt - 0xC0])); + _seenSOF = true; + + // Define the colorspace + switch (numComps) { + case 1: // 1 component + _niso.setColorSpace(CS_GRAYSCALE); // grayscale + break; + case 3: // 3 components + if (_seenJFIF && _seenJFIFFirst) { + _niso.setColorSpace(CS_YCC); // YCbCr + } else { + switch (_transformFlag) { + case -1: + if (equalArray(componentsId, UC_RGB) || equalArray(componentsId, LC_RGB)) { + _niso.setColorSpace(CS_RGB); // RGB } else { - switch (_transformFlag) { - case -1: - if (equalArray(componentsId, UC_RGB) || equalArray(componentsId, LC_RGB)) { - _niso.setColorSpace(CS_RGB); // RGB - } else { - _niso.setColorSpace(CS_YCC); // YCbCr - } - break; - case 0: - _niso.setColorSpace(CS_RGB); // RGB - break; - case 1: - default: - _niso.setColorSpace(CS_YCC); // YCbCr - break; - } + _niso.setColorSpace(CS_YCC); // YCbCr } break; - case 4: // 4 components - switch (_transformFlag) { - case -1: - case 0: - _niso.setColorSpace(CS_CMYK); - break; - case 2: - default: - _niso.setColorSpace(CS_YCCK); - break; - } + case 0: + _niso.setColorSpace(CS_RGB); // RGB break; - default: // others ?!? - String mess = String.format(MessageConstants.JPEG_HUL_13.getMessage(), numComps); - JhoveMessage message = JhoveMessages.getMessageInstance(MessageConstants.JPEG_HUL_13.getId(), mess); - info.setMessage(new ErrorMessage(message, _nByte)); - info.setValid(false); + case 1: + default: + _niso.setColorSpace(CS_YCC); // YCbCr break; } - } - } - - /* Read a DHP segment. This has the same format as SOF. */ - protected void readDHP(RepInfo info) throws IOException { - int length = readUnsignedShort(_dstream); - int precision = readUnsignedByte(_dstream, this); - int nLines = readUnsignedShort(_dstream); - int samPerLine = readUnsignedShort(_dstream); - int numComps = readUnsignedByte(_dstream, this); - skipBytes(_dstream, length - 8, this); - if (!_seenSOF) { - _niso.setImageLength(nLines); - _niso.setImageWidth(samPerLine); - int[] bps = new int[numComps]; - for (int i = 0; i < numComps; i++) { - bps[i] = precision; - } - _niso.setBitsPerSample(bps); - _niso.setSamplesPerPixel(numComps); - _seenSOF = true; - } - } - - /* Read an EXP segment. */ - protected void readEXP(RepInfo info) throws IOException { - readUnsignedShort(_dstream); - int lhlv = readUnsignedByte(_dstream, this); - boolean arr[] = new boolean[2]; - arr[0] = ((lhlv & 0XF0) != 0); - arr[1] = ((lhlv & 0X0F) != 0); - _expList.add(arr); - } - - /* Read a DRI (Data Restart Interval) segment. */ - protected void readDRI(RepInfo info) throws IOException { - readUnsignedShort(_dstream); - _restartInterval = readUnsignedShort(_dstream); - } - - /* - * Read a DQT (Define Quantization Table) segment. (10918-1:1994(E), - * B.2.4.1) - */ - protected void readDQT(RepInfo info) throws IOException { - int length = readUnsignedShort(_dstream); - int pqtq = readUnsignedByte(_dstream, this); - int pq = pqtq >> 4; - int tq = pqtq & 0X0F; - _quantTables.add(new QuantizationTable(pq, tq)); - skipBytes(_dstream, length - 3, this); - _seenJPEGL = false; // Not permitted under JPEG-L - } - - /* - * Read a DAC (Define Arithmetic Conditioning) segment. (10918-1:1994(E), - * B.2.4.1) - */ - protected void readDAC(RepInfo info) throws IOException { - int length = readUnsignedShort(_dstream); - int pqtq = readUnsignedByte(_dstream, this); - int pq = pqtq >> 4; - int tq = pqtq & 0X0F; - _arithCondTables.add(new ArithConditioning(pq, tq)); - skipBytes(_dstream, length - 3, this); - _seenJPEGL = false; // Not permitted under JPEG-L - } - - /* Read a JPGn (JPEG Extension) segment. */ - protected void readJPEGExtension(int dbyt, RepInfo info) - throws IOException { - String ext; - if (dbyt <= 0XF9) { - // 0-9 - ext = "JPG" + (char) (dbyt - 0XF0 + 0X30); - } else { - // 10-15 - ext = "JPG1" + (char) (dbyt - 0XFA + 0X30); - } - _jpegExtsList.add(ext); - - // JPEG extensions other than F7 and F8 are not permitted - // under JPEG-L - if (dbyt != 0XF7 && dbyt != 0XF8) { - _seenJPEGL = false; - } - if (dbyt == 0XF7 && !_seenSPIFF && !_seenJFIF && !_seenExif && !_seenJPEGL) { - // This is probably a JPEG-L file. - if (!_reportedSigMatch) { - info.setSigMatch(_name); - _reportedSigMatch = true; - } - int length = readUnsignedShort(_dstream); - int precision = readUnsignedByte(_dstream, this); - int nLines = readUnsignedShort(_dstream); - int samPerLine = readUnsignedShort(_dstream); - int numComps = readUnsignedByte(_dstream, this); - skipBytes(_dstream, length - 8, this); - _seenJPEGL = true; - _niso.setImageLength(nLines); - _niso.setImageWidth(samPerLine); - int[] bps = new int[numComps]; - for (int i = 0; i < numComps; i++) { - bps[i] = precision; - } - _niso.setBitsPerSample(bps); - _niso.setSamplesPerPixel(numComps); - _seenSOF = true; - return; - } - - int length = readUnsignedShort(_dstream); - skipBytes(_dstream, length - 2, this); - } - - /* - * Read a JPEG comment, and add its text to the comments list. The JPEG spec - * says only that the interpretation of the comment is left to the - * application. For a first shot, everything up to but not including the - * first null, or the entire comment data (whichever comes first) will be - * read into a string. - */ - protected void readComment(RepInfo info) throws IOException { - int length = readUnsignedShort(_dstream); - StringBuffer buf = new StringBuffer(); - boolean getChars = true; - for (int i = 0; i < length - 2; i++) { - int ch = readUnsignedByte(_dstream, this); - if (ch == 0) { - getChars = false; - // but keep reading bytes so we come out right - } - if (getChars) { - buf.append((char) ch); - } - } - if (buf.length() > 0) { - _commentsList.add(buf.toString()); - } - } - - /* - * Build a property based on the capability0 and capability1 bytes. If these - * are both -1 (absent), return null. - */ - protected Property buildCapProp(RepInfo info) { - if (_capability0 < 0) { - return null; - } - - try { - // If we're doing raw output, the capability - // properties will be numbers. If we're doing - // verbose output, they will be strings. - Property cap0Prop; - List capList = new ArrayList(3); - if (_je.getShowRawFlag()) { - cap0Prop = new Property("Version0", PropertyType.INTEGER, _capability0); - } else { - cap0Prop = new Property("Version0", PropertyType.STRING, - JpegStrings.CAPABILITY_V0[_capability0]); - } - capList.add(cap0Prop); - - if (_capability1 >= 0) { - if (_je.getShowRawFlag()) { - Property cap1Prop = new Property("Version1", - PropertyType.INTEGER, _capability1); - capList.add(cap1Prop); - } else { - // Capability 1 entails 2 strings, one for the - // basic capability, and one for tiling. - String[] cap1Str = new String[2]; - cap1Str[0] = JpegStrings.CAPABILITY_V1[_capability1 & 0X1F]; - cap1Str[1] = JpegStrings.TILING_CAPABILITY_V1[_capability1 >> 5]; - Property cap1Prop = new Property("Version1", - PropertyType.STRING, PropertyArity.ARRAY, cap1Str); - capList.add(cap1Prop); - } - } - return new Property("CapabilityIndicator", PropertyType.PROPERTY, - PropertyArity.LIST, capList); - } catch (Exception e) { - // If we get caught on an out-of-bounds value, - // etc., simply don't return the property. - return null; - } - } - - /* Build a property from the tiling information. */ - protected Property buildTilingProp(RepInfo info) { - if (_tiling == null) { - return null; - } - try { - Property[] propArr = new Property[6]; - int tilingType = _tiling.getTilingType(); - if (_je.getShowRawFlag()) { - propArr[0] = new Property("TilingType", PropertyType.INTEGER, tilingType); - } else { - propArr[0] = new Property("TilingType", PropertyType.STRING, - JpegStrings.TILING_TYPE[tilingType]); - } - propArr[1] = new Property("VerticalScale", PropertyType.INTEGER, _tiling.getVertScale()); - propArr[2] = new Property("HorizontalScale", PropertyType.INTEGER, _tiling.getHorScale()); - propArr[3] = new Property("RefGridHeight", PropertyType.LONG, _tiling.getRefGridHeight()); - propArr[4] = new Property("RefGridWidth", PropertyType.LONG, _tiling.getRefGridWidth()); - propArr[5] = _tiling.buildTileListProp(); - return new Property("Tiling", PropertyType.PROPERTY, - PropertyArity.ARRAY, propArr); - } catch (Exception e) { - // Out of bounds value -- punt. - // Should add an error message here. - info.setMessage(new ErrorMessage( - MessageConstants.JPEG_HUL_10)); - info.setValid(false); - return null; - } - } - - protected Property buildExpandProp(RepInfo info) { - List plist = new LinkedList(); - Property prop = new Property("ExpansionSegments", PropertyType.PROPERTY, - PropertyArity.LIST, plist); - ListIterator iter = _expList.listIterator(); - while (iter.hasNext()) { - boolean[] lhlv = iter.next(); - Property[] lhlvProp = new Property[2]; - lhlvProp[0] = new Property("Horizontal", PropertyType.BOOLEAN, lhlv[0]); - lhlvProp[1] = new Property("Vertical", PropertyType.BOOLEAN, lhlv[1]); - plist.add(new Property("Expansion", PropertyType.PROPERTY, - PropertyArity.ARRAY, lhlvProp)); - } - return prop; - } - - /* Read XMP data from the tag, and return as a string. */ - protected Property readXMP(byte[] buf) { - Property xmpProp = null; - // final String badMetadata = "Invalid or ill-formed XMP metadata"; - try { - ByteArrayInputStream strm = new ByteArrayInputStream(buf); - ByteArrayXMPSource src = new ByteArrayXMPSource(strm); - - // Create an InputSource to feed the parser. - SAXParserFactory factory = SAXParserFactory.newInstance(); - factory.setNamespaceAware(true); - XMLReader parser = factory.newSAXParser().getXMLReader(); - XMPHandler handler = new XMPHandler(); - parser.setContentHandler(handler); - parser.setErrorHandler(handler); - // We have to parse twice. The first time, we may get - // an encoding change as part of an exception thrown. If this - // happens, we create a new InputSource with the encoding, and - // continue. - try { - parser.parse(src); - xmpProp = src.makeProperty(); - return xmpProp; - } catch (SAXException se) { - String msg = se.getMessage(); - if (msg != null && msg.startsWith("ENC=")) { - String encoding = msg.substring(5); - try { - // Reader rdr = new InputStreamReader (stream, - // encoding); - src = new ByteArrayXMPSource(strm, encoding); - parser.parse(src); - } catch (UnsupportedEncodingException uee) { - return null; - } - } - xmpProp = src.makeProperty(); - return xmpProp; - } - } catch (Exception e) { - return null; - } - } - - /* - * Extract useful information from the Exif NisoImageMetadata, and put it - * into our NisoImageMetadata. Not all of the Niso information from the Exif - * is meaningful; only that which we think (hope) is is copied. For example, - * the MIME type isn't meaningful, but information describing the camera or - * scanner is. - */ - protected void extractExifNisoData(NisoImageMetadata exifData) { - int NULL = NisoImageMetadata.NULL; // just a shorthand - LOGGER.fine("Copying exif nisoImageMD to principal nisoImageMD"); - if (exifData.getExifVersion() != null) { - _niso.setExifVersion(exifData.getExifVersion()); - } - if (exifData.getAutoFocus() != NULL) { - _niso.setAutoFocus(exifData.getAutoFocus()); - } - if (exifData.getBackLight() != NULL) { - _niso.setBackLight(exifData.getBackLight()); - } - if (exifData.getBrightness() != null) { - _niso.setBrightness(exifData.getBrightness()); - } - if (exifData.getColorTemp() != NULL) { - _niso.setColorTemp(exifData.getColorTemp()); - } - if (exifData.getDeviceSource() != null) { - _niso.setDeviceSource(exifData.getDeviceSource()); - } - if (exifData.getDigitalCameraManufacturer() != null) { - _niso.setDigitalCameraManufacturer( - exifData.getDigitalCameraManufacturer()); - } - if (exifData.getDigitalCameraModelName() != null) { - _niso.setDigitalCameraModelName( - exifData.getDigitalCameraModelName()); - } - if (exifData.getDigitalCameraModelNumber() != null) { - _niso.setDigitalCameraModelNumber( - exifData.getDigitalCameraModelNumber()); - } - if (exifData.getDigitalCameraModelSerialNo() != null) { - _niso.setDigitalCameraModelSerialNo( - exifData.getDigitalCameraModelSerialNo()); - } - if (exifData.getExposureBias() != null) { - _niso.setExposureBias(exifData.getExposureBias()); - } - if (exifData.getExposureIndex() != NULL) { - _niso.setExposureIndex(exifData.getExposureIndex()); - } - if (exifData.getExposureTime() != NULL) { - _niso.setExposureTime(exifData.getExposureTime()); - } - if (exifData.getExposureProgram() != NULL) { - _niso.setExposureProgram(exifData.getExposureProgram()); - } - if (exifData.getFlash() != NULL) { - _niso.setFlash(exifData.getFlash()); - } - if (exifData.getFlashEnergy() != null) { - _niso.setFlashEnergy(exifData.getFlashEnergy()); - } - if (exifData.getFlashReturn() != NULL) { - _niso.setFlashReturn(exifData.getFlashReturn()); - } - if (exifData.getFNumber() != NULL) { - _niso.setFNumber(exifData.getFNumber()); - } - if (exifData.getFocalLength() != NULL) { - _niso.setFocalLength(exifData.getFocalLength()); - } - if (exifData.getHostComputer() != null) { - _niso.setHostComputer(exifData.getHostComputer()); - } - if (exifData.getImageIdentifier() != null) { - _niso.setImageIdentifier(exifData.getImageIdentifier()); - } - if (exifData.getImageProducer() != null) { - _niso.setImageProducer(exifData.getImageProducer()); - } - if (exifData.getMaxApertureValue() != null) { - _niso.setMaxApertureValue(exifData.getMaxApertureValue()); - } - if (exifData.getMeteringMode() != NULL) { - _niso.setMeteringMode(exifData.getMeteringMode()); - } - if (exifData.getOS() != null) { - _niso.setOS(exifData.getOS()); - } - if (exifData.getOSVersion() != null) { - _niso.setOSVersion(exifData.getOSVersion()); - } - if (exifData.getPerformanceData() != null) { - _niso.setPerformanceData(exifData.getPerformanceData()); - } - if (exifData.getProcessingAgency() != null) { - _niso.setProcessingAgency(exifData.getProcessingAgency()); - } - if (exifData.getProcessingSoftwareName() != null) { - _niso.setProcessingSoftwareName( - exifData.getProcessingSoftwareName()); - } - if (exifData.getProcessingSoftwareVersion() != null) { - _niso.setProcessingSoftwareVersion( - exifData.getProcessingSoftwareVersion()); - } - if (exifData.getScannerManufacturer() != null) { - _niso.setScannerManufacturer(exifData.getScannerManufacturer()); - } - if (exifData.getScannerModelName() != null) { - _niso.setScannerModelName(exifData.getScannerModelName()); - } - if (exifData.getScannerModelNumber() != null) { - _niso.setScannerModelNumber(exifData.getScannerModelNumber()); - } - if (exifData.getScannerModelSerialNo() != null) { - _niso.setScannerModelSerialNo(exifData.getScannerModelSerialNo()); - } - if (exifData.getSceneIlluminant() != NULL) { - _niso.setSceneIlluminant(exifData.getSceneIlluminant()); - } - if (exifData.getSubjectDistance() != null) { - _niso.setSubjectDistance(exifData.getSubjectDistance()); - } - // Copy information that could come from alternative sources - if (_niso.getDateTimeCreated() == null - && exifData.getDateTimeCreated() != null) { - _niso.setDateTimeCreated(exifData.getDateTimeCreated()); - } - if (_niso.getXSamplingFrequency() == null - && exifData.getXSamplingFrequency() != null) { - _niso.setXSamplingFrequency(exifData.getXSamplingFrequency()); - _niso.setSamplingFrequencyUnit(exifData.getSamplingFrequencyUnit()); - } - if (_niso.getYSamplingFrequency() == null - && exifData.getYSamplingFrequency() != null) { - _niso.setYSamplingFrequency(exifData.getYSamplingFrequency()); - _niso.setSamplingFrequencyUnit(exifData.getSamplingFrequencyUnit()); - } - if (_niso.getProfileName() == null - && exifData.getProfileName() != null) { - _niso.setProfileName(exifData.getProfileName()); - } - - // If exif FNumber is defined then assume is a camera and not a scanner, - // migrate Scanner info to DigitalCamera info - if (_niso.getFNumber() != NULL) { - if (_niso.getDigitalCameraManufacturer() == null - && _niso.getScannerManufacturer() != null) { - _niso.setDigitalCameraManufacturer( - _niso.getScannerManufacturer()); - } - if (_niso.getDigitalCameraModelName() == null - && _niso.getScannerModelName() != null) { - _niso.setDigitalCameraModelName(_niso.getScannerModelName()); - } - if (_niso.getDigitalCameraModelNumber() == null - && _niso.getScannerModelNumber() != null) { - _niso.setDigitalCameraModelNumber( - _niso.getScannerModelNumber()); - } - if (_niso.getDigitalCameraModelSerialNo() == null - && _niso.getScannerModelSerialNo() != null) { - _niso.setDigitalCameraModelSerialNo( - _niso.getScannerModelSerialNo()); - } - } - } - - /* - * Skip over a segment without doing anything. When this is called, we have - * already read the marker and the stream is ready to read the length. - */ - protected boolean skipSegment(RepInfo info) throws IOException { - int length = readUnsignedShort(_dstream); - skipBytes(_dstream, length - 2, this); - return true; - } - - /* - * Compare two arrays of int for equality. They must be the same length. - */ - protected static boolean equalArray(int[] a, int[] b) { - if (a.length != b.length) { - return false; - } - for (int i = 0; i < a.length; i++) { - if (a[i] != b[i]) { - return false; - } - } - return true; - } + } + break; + case 4: // 4 components + switch (_transformFlag) { + case -1: + case 0: + _niso.setColorSpace(CS_CMYK); + break; + case 2: + default: + _niso.setColorSpace(CS_YCCK); + break; + } + break; + default: // others ?!? + String mess = String.format(MessageConstants.JPEG_HUL_13.getMessage(), numComps); + JhoveMessage message = + JhoveMessages.getMessageInstance(MessageConstants.JPEG_HUL_13.getId(), mess); + info.setMessage(new ErrorMessage(message, _nByte)); + info.setValid(false); + break; + } + } + } + + /* Read a DHP segment. This has the same format as SOF. */ + protected void readDHP(RepInfo info) throws IOException { + int length = readUnsignedShort(_dstream); + int precision = readUnsignedByte(_dstream, this); + int nLines = readUnsignedShort(_dstream); + int samPerLine = readUnsignedShort(_dstream); + int numComps = readUnsignedByte(_dstream, this); + skipBytes(_dstream, length - 8, this); + if (!_seenSOF) { + _niso.setImageLength(nLines); + _niso.setImageWidth(samPerLine); + int[] bps = new int[numComps]; + for (int i = 0; i < numComps; i++) { + bps[i] = precision; + } + _niso.setBitsPerSample(bps); + _niso.setSamplesPerPixel(numComps); + _seenSOF = true; + } + } + + /* Read an EXP segment. */ + protected void readEXP(RepInfo info) throws IOException { + readUnsignedShort(_dstream); + int lhlv = readUnsignedByte(_dstream, this); + boolean arr[] = new boolean[2]; + arr[0] = ((lhlv & 0XF0) != 0); + arr[1] = ((lhlv & 0X0F) != 0); + _expList.add(arr); + } + + /* Read a DRI (Data Restart Interval) segment. */ + protected void readDRI(RepInfo info) throws IOException { + readUnsignedShort(_dstream); + _restartInterval = readUnsignedShort(_dstream); + } + + /* + * Read a DQT (Define Quantization Table) segment. (10918-1:1994(E), + * B.2.4.1) + */ + protected void readDQT(RepInfo info) throws IOException { + int length = readUnsignedShort(_dstream); + int pqtq = readUnsignedByte(_dstream, this); + int pq = pqtq >> 4; + int tq = pqtq & 0X0F; + _quantTables.add(new QuantizationTable(pq, tq)); + skipBytes(_dstream, length - 3, this); + _seenJPEGL = false; // Not permitted under JPEG-L + } + + /* + * Read a DAC (Define Arithmetic Conditioning) segment. (10918-1:1994(E), + * B.2.4.1) + */ + protected void readDAC(RepInfo info) throws IOException { + int length = readUnsignedShort(_dstream); + int pqtq = readUnsignedByte(_dstream, this); + int pq = pqtq >> 4; + int tq = pqtq & 0X0F; + _arithCondTables.add(new ArithConditioning(pq, tq)); + skipBytes(_dstream, length - 3, this); + _seenJPEGL = false; // Not permitted under JPEG-L + } + + /* Read a JPGn (JPEG Extension) segment. */ + protected void readJPEGExtension(int dbyt, RepInfo info) throws IOException { + String ext; + if (dbyt <= 0XF9) { + // 0-9 + ext = "JPG" + (char) (dbyt - 0XF0 + 0X30); + } else { + // 10-15 + ext = "JPG1" + (char) (dbyt - 0XFA + 0X30); + } + _jpegExtsList.add(ext); + + // JPEG extensions other than F7 and F8 are not permitted + // under JPEG-L + if (dbyt != 0XF7 && dbyt != 0XF8) { + _seenJPEGL = false; + } + if (dbyt == 0XF7 && !_seenSPIFF && !_seenJFIF && !_seenExif && !_seenJPEGL) { + // This is probably a JPEG-L file. + if (!_reportedSigMatch) { + info.setSigMatch(_name); + _reportedSigMatch = true; + } + int length = readUnsignedShort(_dstream); + int precision = readUnsignedByte(_dstream, this); + int nLines = readUnsignedShort(_dstream); + int samPerLine = readUnsignedShort(_dstream); + int numComps = readUnsignedByte(_dstream, this); + skipBytes(_dstream, length - 8, this); + _seenJPEGL = true; + _niso.setImageLength(nLines); + _niso.setImageWidth(samPerLine); + int[] bps = new int[numComps]; + for (int i = 0; i < numComps; i++) { + bps[i] = precision; + } + _niso.setBitsPerSample(bps); + _niso.setSamplesPerPixel(numComps); + _seenSOF = true; + return; + } + + int length = readUnsignedShort(_dstream); + skipBytes(_dstream, length - 2, this); + } + + /* + * Read a JPEG comment, and add its text to the comments list. The JPEG spec + * says only that the interpretation of the comment is left to the + * application. For a first shot, everything up to but not including the + * first null, or the entire comment data (whichever comes first) will be + * read into a string. + */ + protected void readComment(RepInfo info) throws IOException { + int length = readUnsignedShort(_dstream); + StringBuffer buf = new StringBuffer(); + boolean getChars = true; + for (int i = 0; i < length - 2; i++) { + int ch = readUnsignedByte(_dstream, this); + if (ch == 0) { + getChars = false; + // but keep reading bytes so we come out right + } + if (getChars) { + buf.append((char) ch); + } + } + if (buf.length() > 0) { + _commentsList.add(buf.toString()); + } + } + + /* + * Build a property based on the capability0 and capability1 bytes. If these + * are both -1 (absent), return null. + */ + protected Property buildCapProp(RepInfo info) { + if (_capability0 < 0) { + return null; + } + + try { + // If we're doing raw output, the capability + // properties will be numbers. If we're doing + // verbose output, they will be strings. + Property cap0Prop; + List capList = new ArrayList(3); + if (_je.getShowRawFlag()) { + cap0Prop = new Property("Version0", PropertyType.INTEGER, _capability0); + } else { + cap0Prop = + new Property("Version0", PropertyType.STRING, JpegStrings.CAPABILITY_V0[_capability0]); + } + capList.add(cap0Prop); + + if (_capability1 >= 0) { + if (_je.getShowRawFlag()) { + Property cap1Prop = new Property("Version1", PropertyType.INTEGER, _capability1); + capList.add(cap1Prop); + } else { + // Capability 1 entails 2 strings, one for the + // basic capability, and one for tiling. + String[] cap1Str = new String[2]; + cap1Str[0] = JpegStrings.CAPABILITY_V1[_capability1 & 0X1F]; + cap1Str[1] = JpegStrings.TILING_CAPABILITY_V1[_capability1 >> 5]; + Property cap1Prop = + new Property("Version1", PropertyType.STRING, PropertyArity.ARRAY, cap1Str); + capList.add(cap1Prop); + } + } + return new Property( + "CapabilityIndicator", PropertyType.PROPERTY, PropertyArity.LIST, capList); + } catch (Exception e) { + // If we get caught on an out-of-bounds value, + // etc., simply don't return the property. + return null; + } + } + + /* Build a property from the tiling information. */ + protected Property buildTilingProp(RepInfo info) { + if (_tiling == null) { + return null; + } + try { + Property[] propArr = new Property[6]; + int tilingType = _tiling.getTilingType(); + if (_je.getShowRawFlag()) { + propArr[0] = new Property("TilingType", PropertyType.INTEGER, tilingType); + } else { + propArr[0] = + new Property("TilingType", PropertyType.STRING, JpegStrings.TILING_TYPE[tilingType]); + } + propArr[1] = new Property("VerticalScale", PropertyType.INTEGER, _tiling.getVertScale()); + propArr[2] = new Property("HorizontalScale", PropertyType.INTEGER, _tiling.getHorScale()); + propArr[3] = new Property("RefGridHeight", PropertyType.LONG, _tiling.getRefGridHeight()); + propArr[4] = new Property("RefGridWidth", PropertyType.LONG, _tiling.getRefGridWidth()); + propArr[5] = _tiling.buildTileListProp(); + return new Property("Tiling", PropertyType.PROPERTY, PropertyArity.ARRAY, propArr); + } catch (Exception e) { + // Out of bounds value -- punt. + // Should add an error message here. + info.setMessage(new ErrorMessage(MessageConstants.JPEG_HUL_10)); + info.setValid(false); + return null; + } + } + + protected Property buildExpandProp(RepInfo info) { + List plist = new LinkedList(); + Property prop = + new Property("ExpansionSegments", PropertyType.PROPERTY, PropertyArity.LIST, plist); + ListIterator iter = _expList.listIterator(); + while (iter.hasNext()) { + boolean[] lhlv = iter.next(); + Property[] lhlvProp = new Property[2]; + lhlvProp[0] = new Property("Horizontal", PropertyType.BOOLEAN, lhlv[0]); + lhlvProp[1] = new Property("Vertical", PropertyType.BOOLEAN, lhlv[1]); + plist.add(new Property("Expansion", PropertyType.PROPERTY, PropertyArity.ARRAY, lhlvProp)); + } + return prop; + } + + /* Read XMP data from the tag, and return as a string. */ + protected Property readXMP(byte[] buf) { + Property xmpProp = null; + // final String badMetadata = "Invalid or ill-formed XMP metadata"; + try { + ByteArrayInputStream strm = new ByteArrayInputStream(buf); + ByteArrayXMPSource src = new ByteArrayXMPSource(strm); + + // Create an InputSource to feed the parser. + SAXParserFactory factory = SAXParserFactory.newInstance(); + factory.setNamespaceAware(true); + XMLReader parser = factory.newSAXParser().getXMLReader(); + XMPHandler handler = new XMPHandler(); + parser.setContentHandler(handler); + parser.setErrorHandler(handler); + // We have to parse twice. The first time, we may get + // an encoding change as part of an exception thrown. If this + // happens, we create a new InputSource with the encoding, and + // continue. + try { + parser.parse(src); + xmpProp = src.makeProperty(); + return xmpProp; + } catch (SAXException se) { + String msg = se.getMessage(); + if (msg != null && msg.startsWith("ENC=")) { + String encoding = msg.substring(5); + try { + // Reader rdr = new InputStreamReader (stream, + // encoding); + src = new ByteArrayXMPSource(strm, encoding); + parser.parse(src); + } catch (UnsupportedEncodingException uee) { + return null; + } + } + xmpProp = src.makeProperty(); + return xmpProp; + } + } catch (Exception e) { + return null; + } + } + + /* + * Extract useful information from the Exif NisoImageMetadata, and put it + * into our NisoImageMetadata. Not all of the Niso information from the Exif + * is meaningful; only that which we think (hope) is is copied. For example, + * the MIME type isn't meaningful, but information describing the camera or + * scanner is. + */ + protected void extractExifNisoData(NisoImageMetadata exifData) { + int NULL = NisoImageMetadata.NULL; // just a shorthand + LOGGER.fine("Copying exif nisoImageMD to principal nisoImageMD"); + if (exifData.getExifVersion() != null) { + _niso.setExifVersion(exifData.getExifVersion()); + } + if (exifData.getAutoFocus() != NULL) { + _niso.setAutoFocus(exifData.getAutoFocus()); + } + if (exifData.getBackLight() != NULL) { + _niso.setBackLight(exifData.getBackLight()); + } + if (exifData.getBrightness() != null) { + _niso.setBrightness(exifData.getBrightness()); + } + if (exifData.getColorTemp() != NULL) { + _niso.setColorTemp(exifData.getColorTemp()); + } + if (exifData.getDeviceSource() != null) { + _niso.setDeviceSource(exifData.getDeviceSource()); + } + if (exifData.getDigitalCameraManufacturer() != null) { + _niso.setDigitalCameraManufacturer(exifData.getDigitalCameraManufacturer()); + } + if (exifData.getDigitalCameraModelName() != null) { + _niso.setDigitalCameraModelName(exifData.getDigitalCameraModelName()); + } + if (exifData.getDigitalCameraModelNumber() != null) { + _niso.setDigitalCameraModelNumber(exifData.getDigitalCameraModelNumber()); + } + if (exifData.getDigitalCameraModelSerialNo() != null) { + _niso.setDigitalCameraModelSerialNo(exifData.getDigitalCameraModelSerialNo()); + } + if (exifData.getExposureBias() != null) { + _niso.setExposureBias(exifData.getExposureBias()); + } + if (exifData.getExposureIndex() != NULL) { + _niso.setExposureIndex(exifData.getExposureIndex()); + } + if (exifData.getExposureTime() != NULL) { + _niso.setExposureTime(exifData.getExposureTime()); + } + if (exifData.getExposureProgram() != NULL) { + _niso.setExposureProgram(exifData.getExposureProgram()); + } + if (exifData.getFlash() != NULL) { + _niso.setFlash(exifData.getFlash()); + } + if (exifData.getFlashEnergy() != null) { + _niso.setFlashEnergy(exifData.getFlashEnergy()); + } + if (exifData.getFlashReturn() != NULL) { + _niso.setFlashReturn(exifData.getFlashReturn()); + } + if (exifData.getFNumber() != NULL) { + _niso.setFNumber(exifData.getFNumber()); + } + if (exifData.getFocalLength() != NULL) { + _niso.setFocalLength(exifData.getFocalLength()); + } + if (exifData.getHostComputer() != null) { + _niso.setHostComputer(exifData.getHostComputer()); + } + if (exifData.getImageIdentifier() != null) { + _niso.setImageIdentifier(exifData.getImageIdentifier()); + } + if (exifData.getImageProducer() != null) { + _niso.setImageProducer(exifData.getImageProducer()); + } + if (exifData.getMaxApertureValue() != null) { + _niso.setMaxApertureValue(exifData.getMaxApertureValue()); + } + if (exifData.getMeteringMode() != NULL) { + _niso.setMeteringMode(exifData.getMeteringMode()); + } + if (exifData.getOS() != null) { + _niso.setOS(exifData.getOS()); + } + if (exifData.getOSVersion() != null) { + _niso.setOSVersion(exifData.getOSVersion()); + } + if (exifData.getPerformanceData() != null) { + _niso.setPerformanceData(exifData.getPerformanceData()); + } + if (exifData.getProcessingAgency() != null) { + _niso.setProcessingAgency(exifData.getProcessingAgency()); + } + if (exifData.getProcessingSoftwareName() != null) { + _niso.setProcessingSoftwareName(exifData.getProcessingSoftwareName()); + } + if (exifData.getProcessingSoftwareVersion() != null) { + _niso.setProcessingSoftwareVersion(exifData.getProcessingSoftwareVersion()); + } + if (exifData.getScannerManufacturer() != null) { + _niso.setScannerManufacturer(exifData.getScannerManufacturer()); + } + if (exifData.getScannerModelName() != null) { + _niso.setScannerModelName(exifData.getScannerModelName()); + } + if (exifData.getScannerModelNumber() != null) { + _niso.setScannerModelNumber(exifData.getScannerModelNumber()); + } + if (exifData.getScannerModelSerialNo() != null) { + _niso.setScannerModelSerialNo(exifData.getScannerModelSerialNo()); + } + if (exifData.getSceneIlluminant() != NULL) { + _niso.setSceneIlluminant(exifData.getSceneIlluminant()); + } + if (exifData.getSubjectDistance() != null) { + _niso.setSubjectDistance(exifData.getSubjectDistance()); + } + // Copy information that could come from alternative sources + if (_niso.getDateTimeCreated() == null && exifData.getDateTimeCreated() != null) { + _niso.setDateTimeCreated(exifData.getDateTimeCreated()); + } + if (_niso.getXSamplingFrequency() == null && exifData.getXSamplingFrequency() != null) { + _niso.setXSamplingFrequency(exifData.getXSamplingFrequency()); + _niso.setSamplingFrequencyUnit(exifData.getSamplingFrequencyUnit()); + } + if (_niso.getYSamplingFrequency() == null && exifData.getYSamplingFrequency() != null) { + _niso.setYSamplingFrequency(exifData.getYSamplingFrequency()); + _niso.setSamplingFrequencyUnit(exifData.getSamplingFrequencyUnit()); + } + if (_niso.getProfileName() == null && exifData.getProfileName() != null) { + _niso.setProfileName(exifData.getProfileName()); + } + + // If exif FNumber is defined then assume is a camera and not a scanner, + // migrate Scanner info to DigitalCamera info + if (_niso.getFNumber() != NULL) { + if (_niso.getDigitalCameraManufacturer() == null && _niso.getScannerManufacturer() != null) { + _niso.setDigitalCameraManufacturer(_niso.getScannerManufacturer()); + } + if (_niso.getDigitalCameraModelName() == null && _niso.getScannerModelName() != null) { + _niso.setDigitalCameraModelName(_niso.getScannerModelName()); + } + if (_niso.getDigitalCameraModelNumber() == null && _niso.getScannerModelNumber() != null) { + _niso.setDigitalCameraModelNumber(_niso.getScannerModelNumber()); + } + if (_niso.getDigitalCameraModelSerialNo() == null + && _niso.getScannerModelSerialNo() != null) { + _niso.setDigitalCameraModelSerialNo(_niso.getScannerModelSerialNo()); + } + } + } + + /* + * Skip over a segment without doing anything. When this is called, we have + * already read the marker and the stream is ready to read the length. + */ + protected boolean skipSegment(RepInfo info) throws IOException { + int length = readUnsignedShort(_dstream); + skipBytes(_dstream, length - 2, this); + return true; + } + + /* + * Compare two arrays of int for equality. They must be the same length. + */ + protected static boolean equalArray(int[] a, int[] b) { + if (a.length != b.length) { + return false; + } + for (int i = 0; i < a.length; i++) { + if (a[i] != b[i]) { + return false; + } + } + return true; + } } diff --git a/jhove-modules/jpeg-hul/src/main/java/edu/harvard/hul/ois/jhove/module/jpeg/ArithConditioning.java b/jhove-modules/jpeg-hul/src/main/java/edu/harvard/hul/ois/jhove/module/jpeg/ArithConditioning.java index 603583525..c65eb954b 100644 --- a/jhove-modules/jpeg-hul/src/main/java/edu/harvard/hul/ois/jhove/module/jpeg/ArithConditioning.java +++ b/jhove-modules/jpeg-hul/src/main/java/edu/harvard/hul/ois/jhove/module/jpeg/ArithConditioning.java @@ -1,62 +1,45 @@ -/********************************************************************** - * Jhove - JSTOR/Harvard Object Validation Environment - * Copyright 2003 by JSTOR and the President and Fellows of Harvard College +/** + * ******************************************************************** Jhove - JSTOR/Harvard Object + * Validation Environment Copyright 2003 by JSTOR and the President and Fellows of Harvard College * - **********************************************************************/ - + *

******************************************************************** + */ package edu.harvard.hul.ois.jhove.module.jpeg; import edu.harvard.hul.ois.jhove.*; /** * Encapsulation of an arithmetic conditioning entry for a JPEG image. - * - * @author Gary McGath * + * @author Gary McGath */ public class ArithConditioning { - private int _tableClass; - private int _destIdentifier; - - - /** - * Constructor. - */ - public ArithConditioning(int tableClass, int destIdentifier) { - _tableClass = tableClass; - _destIdentifier = destIdentifier; + private int _tableClass; + private int _destIdentifier; + + /** Constructor. */ + public ArithConditioning(int tableClass, int destIdentifier) { + _tableClass = tableClass; + _destIdentifier = destIdentifier; + } + + /** Returns a Property defining the conditioning data */ + public Property makeProperty(boolean raw) { + Property[] parray = new Property[2]; + if (raw) { + parray[0] = new Property("TableClass", PropertyType.INTEGER, new Integer(_tableClass)); + } else { + String prec = "Undefined"; + try { + prec = JpegStrings.DAC_CLASS[_tableClass]; + } catch (Exception e) { + } + parray[0] = new Property("Precision", PropertyType.STRING, prec); } - - - /** - * Returns a Property defining the conditioning data - */ - public Property makeProperty (boolean raw) - { - Property[] parray = new Property[2]; - if (raw) { - parray[0] = new Property ("TableClass", - PropertyType.INTEGER, - new Integer (_tableClass)); - } - else { - String prec = "Undefined"; - try { - prec = JpegStrings.DAC_CLASS[_tableClass]; - } - catch (Exception e) {} - parray[0] = new Property ("Precision", - PropertyType.STRING, - prec); - } - parray[1] = new Property ("DestinationIdentifier", - PropertyType.INTEGER, - new Integer (_destIdentifier)); - return new Property ("ArithmeticConditioning", - PropertyType.PROPERTY, - PropertyArity.ARRAY, - parray); - } - + parray[1] = + new Property("DestinationIdentifier", PropertyType.INTEGER, new Integer(_destIdentifier)); + return new Property( + "ArithmeticConditioning", PropertyType.PROPERTY, PropertyArity.ARRAY, parray); + } } diff --git a/jhove-modules/jpeg-hul/src/main/java/edu/harvard/hul/ois/jhove/module/jpeg/JpegExif.java b/jhove-modules/jpeg-hul/src/main/java/edu/harvard/hul/ois/jhove/module/jpeg/JpegExif.java index e94a9b553..1e1789dcc 100644 --- a/jhove-modules/jpeg-hul/src/main/java/edu/harvard/hul/ois/jhove/module/jpeg/JpegExif.java +++ b/jhove-modules/jpeg-hul/src/main/java/edu/harvard/hul/ois/jhove/module/jpeg/JpegExif.java @@ -1,19 +1,10 @@ -/********************************************************************** - * Jhove - JSTOR/Harvard Object Validation Environment - * Copyright 2003 by JSTOR and the President and Fellows of Harvard College - **********************************************************************/ - +/** + * ******************************************************************** Jhove - JSTOR/Harvard Object + * Validation Environment Copyright 2003 by JSTOR and the President and Fellows of Harvard College + * ******************************************************************** + */ package edu.harvard.hul.ois.jhove.module.jpeg; -import java.io.BufferedOutputStream; -import java.io.DataInputStream; -import java.io.File; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.RandomAccessFile; -import java.util.List; -import java.util.ListIterator; - import edu.harvard.hul.ois.jhove.ErrorMessage; import edu.harvard.hul.ois.jhove.JhoveBase; import edu.harvard.hul.ois.jhove.NisoImageMetadata; @@ -26,211 +17,190 @@ import edu.harvard.hul.ois.jhove.module.tiff.TiffIFD; import edu.harvard.hul.ois.jhove.module.tiff.TiffProfileExif; import edu.harvard.hul.ois.jhove.module.tiff.TiffProfileExifIFD; +import java.io.BufferedOutputStream; +import java.io.DataInputStream; +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.RandomAccessFile; +import java.util.List; +import java.util.ListIterator; /** - * Reader of Exif data embedded in a JPEG App1 block. This makes use - * of the TIFF module, since an Exif stream is really an embedded TIFF - * file; but it is designed to fail cleanly if the TIFF module is absent. - * - * @author Gary McGath + * Reader of Exif data embedded in a JPEG App1 block. This makes use of the TIFF module, since an + * Exif stream is really an embedded TIFF file; but it is designed to fail cleanly if the TIFF + * module is absent. * + * @author Gary McGath */ public final class JpegExif { - private boolean _exifProfileOK; - private String _profileText; - private NisoImageMetadata _exifNiso; - private JpegModule module; - - public JpegExif (final JpegModule module) - { - this.module = module; - _exifProfileOK = false; - _profileText = null; - _exifNiso = null; - } + private boolean _exifProfileOK; + private String _profileText; + private NisoImageMetadata _exifNiso; + private JpegModule module; + public JpegExif(final JpegModule module) { + this.module = module; + _exifProfileOK = false; + _profileText = null; + _exifNiso = null; + } + /** Checks if the TIFF module is available. */ + public static boolean isTiffAvailable() { + try { + Class.forName("edu.harvard.hul.ois.jhove.module.TiffModule"); + return true; + } catch (Exception e) { + return false; + } + } - /** Checks if the TIFF module is available. + /** + * Reads the Exif data from the current point at the data stream, puts it into a temporary file, + * and makes a RepInfo object available. This should be called only if isTiffAvailable() has + * returned true. + */ + public RepInfo readExifData(DataInputStream dstream, JhoveBase je, int length) { + File tiffFile = null; + RepInfo info = new RepInfo("tempfile"); + /* We're now at the beginning of the TIFF data. + * Copy it into a temporary file, then parse that + * as a TIFF file. */ - public static boolean isTiffAvailable () - { - try { - Class.forName ("edu.harvard.hul.ois.jhove.module.TiffModule"); - return true; - } - catch (Exception e) { - return false; - } + try { + tiffFile = je.tempFile(); + } catch (IOException e) { + info.setMessage(new ErrorMessage(MessageConstants.JHOVE_1, e.getMessage())); + return info; } - - /** Reads the Exif data from the current point at the data stream, - * puts it into a temporary file, and makes a RepInfo object - * available. This should be called only if isTiffAvailable() - * has returned true. - */ - public RepInfo readExifData (DataInputStream dstream, JhoveBase je, - int length) - { - File tiffFile = null; - RepInfo info = new RepInfo ("tempfile"); - /* We're now at the beginning of the TIFF data. - * Copy it into a temporary file, then parse that - * as a TIFF file. - */ - try { - tiffFile = je.tempFile (); + try (FileOutputStream fos = new FileOutputStream(tiffFile)) { + int bufSize = je.getBufferSize(); + int tiffLen = length - 8; + /* Set a default buffer size if the app doesn't specify one. */ + if (bufSize <= 0) { + bufSize = 32768; + } + if (bufSize > tiffLen) { + // can buffer whole file in one buffer + bufSize = tiffLen; + } + try (BufferedOutputStream bos = new BufferedOutputStream(fos, bufSize)) { + byte[] buf = new byte[bufSize]; + while (tiffLen > 0) { + // int len; + int sz; + if (tiffLen < bufSize) { + sz = tiffLen; + } else { + sz = bufSize; + } + sz = dstream.read(buf, 0, sz); + bos.write(buf, 0, sz); + tiffLen -= sz; } - catch (IOException e) { - info.setMessage (new ErrorMessage - (MessageConstants.JHOVE_1, - e.getMessage ())); - return info; + } + fos.flush(); + edu.harvard.hul.ois.jhove.module.TiffModule tiffMod = + new edu.harvard.hul.ois.jhove.module.TiffModule(); + tiffMod.setByteOffsetValid(true); + // Now parse the file, using a special parsing method. + // Close only after we're all done. + List ifds = null; + try (RandomAccessFile tiffRaf = new RandomAccessFile(tiffFile, "r")) { + ifds = tiffMod.exifParse(tiffRaf, info); + } + if (ifds == null) { + return info; + } + + // Locate the Exif IFD. (We probably also want the + // Interoperability IFD eventually.) + ListIterator iter = ifds.listIterator(); + boolean first = true; + boolean haveNisoMetadata = false; + NisoImageMetadata niso = null; + while (iter.hasNext()) { + Object ifd = iter.next(); + if (ifd instanceof TiffIFD && first) { + // The TIFF IFD has useful information, which gets put + // into its NISO metadata. Make it available to the caller. + TiffIFD tifd = (TiffIFD) ifd; + niso = tifd.getNisoImageMetadata(); + // The first one is presumed to be the interesting one. + info.setProperty(new Property("NisoImageMetadata", PropertyType.NISOIMAGEMETADATA, niso)); + haveNisoMetadata = true; + TiffProfileExif exifProfile = new TiffProfileExif(); + _exifProfileOK = exifProfile.satisfiesProfile(tifd); + if (_exifProfileOK) { + _profileText = exifProfile.getText(); + } } - try (FileOutputStream fos = new FileOutputStream (tiffFile)) { - int bufSize = je.getBufferSize (); - int tiffLen = length - 8; - /* Set a default buffer size if the app doesn't specify one. */ - if (bufSize <= 0) { - bufSize = 32768; - } - if (bufSize > tiffLen) { - // can buffer whole file in one buffer - bufSize = tiffLen; - } - try (BufferedOutputStream bos = new BufferedOutputStream (fos, bufSize)) { - byte[] buf = new byte[bufSize]; - while (tiffLen > 0) { - //int len; - int sz; - if (tiffLen < bufSize) { - sz = tiffLen; - } - else { - sz = bufSize; - } - sz = dstream.read (buf, 0, sz); - bos.write(buf, 0, sz); - tiffLen -= sz; - } - } - fos.flush (); - edu.harvard.hul.ois.jhove.module.TiffModule tiffMod = - new edu.harvard.hul.ois.jhove.module.TiffModule (); - tiffMod.setByteOffsetValid(true); - // Now parse the file, using a special parsing method. - // Close only after we're all done. - List ifds = null; - try (RandomAccessFile tiffRaf = new RandomAccessFile (tiffFile, "r")) { - ifds = tiffMod.exifParse (tiffRaf, info); - } - if (ifds == null) { - return info; + if (ifd instanceof ExifIFD) { + // Now for complicated stuff copying out the appropriate properties. + // Probably I just want to go through them and match interesting + // properties one by one, and copy them directly out. + // Or do I just want to copy the whole Exif property? + ExifIFD eifd = (ExifIFD) ifd; + Property ifdProp = eifd.getProperty((je.getShowRawFlag())); + List exifList = null; + if (ifdProp != null) { + exifList = eifd.exifProps(ifdProp); + } + if (first || _exifProfileOK) { + TiffProfileExifIFD exifIFDProfile = new TiffProfileExifIFD(); + _exifProfileOK = exifIFDProfile.satisfiesProfile(eifd); + if (!_exifProfileOK) { + _profileText = null; } - - // Locate the Exif IFD. (We probably also want the - // Interoperability IFD eventually.) - ListIterator iter = ifds.listIterator(); - boolean first = true; - boolean haveNisoMetadata = false; - NisoImageMetadata niso = null; - while (iter.hasNext()) { - Object ifd = iter.next (); - if (ifd instanceof TiffIFD && first) { - // The TIFF IFD has useful information, which gets put - // into its NISO metadata. Make it available to the caller. - TiffIFD tifd = (TiffIFD)ifd; - niso = tifd.getNisoImageMetadata (); - // The first one is presumed to be the interesting one. - info.setProperty (new Property ("NisoImageMetadata", - PropertyType.NISOIMAGEMETADATA, - niso)); - haveNisoMetadata = true; - TiffProfileExif exifProfile = new TiffProfileExif (); - _exifProfileOK = exifProfile.satisfiesProfile (tifd); - if (_exifProfileOK) { - _profileText = exifProfile.getText(); - } - } - if (ifd instanceof ExifIFD) { - // Now for complicated stuff copying out the appropriate properties. - // Probably I just want to go through them and match interesting - // properties one by one, and copy them directly out. - // Or do I just want to copy the whole Exif property? - ExifIFD eifd = (ExifIFD) ifd; - Property ifdProp = eifd.getProperty( (je.getShowRawFlag ())); - List exifList = null; - if (ifdProp != null) { - exifList = eifd.exifProps (ifdProp); - } - if (first || _exifProfileOK) { - TiffProfileExifIFD exifIFDProfile = new TiffProfileExifIFD (); - _exifProfileOK = exifIFDProfile.satisfiesProfile(eifd); - if (!_exifProfileOK) { - _profileText = null; - } - } - if (exifList != null) { - info.setProperty(new Property ("Exif", - PropertyType.PROPERTY, - PropertyArity.LIST, - exifList)); - } - // See if we have any interesting NISO metadata. If so, and - // we haven't gotten real NISO metadata, use it. - if (!haveNisoMetadata) { - niso = eifd.getNisoImageMetadata (); - info.setProperty (new Property ("NisoImageMetadata", - PropertyType.NISOIMAGEMETADATA, - niso)); - } else { - // Get the exif version - _exifNiso = eifd.getNisoImageMetadata (); - } - } - first = false; - } - } - catch (IOException e) { - info.setMessage (new ErrorMessage - (MessageConstants.JPEG_HUL_3, - e.getMessage ())); - // Maybe should put this directly in the parent's - // RepInfo, otherwise I have to copy the message afterwards. + } + if (exifList != null) { + info.setProperty( + new Property("Exif", PropertyType.PROPERTY, PropertyArity.LIST, exifList)); + } + // See if we have any interesting NISO metadata. If so, and + // we haven't gotten real NISO metadata, use it. + if (!haveNisoMetadata) { + niso = eifd.getNisoImageMetadata(); + info.setProperty( + new Property("NisoImageMetadata", PropertyType.NISOIMAGEMETADATA, niso)); + } else { + // Get the exif version + _exifNiso = eifd.getNisoImageMetadata(); + } } - finally { - if (tiffFile != null) { - try { - tiffFile.delete(); - } - catch (Exception e) {} - } + first = false; + } + } catch (IOException e) { + info.setMessage(new ErrorMessage(MessageConstants.JPEG_HUL_3, e.getMessage())); + // Maybe should put this directly in the parent's + // RepInfo, otherwise I have to copy the message afterwards. + } finally { + if (tiffFile != null) { + try { + tiffFile.delete(); + } catch (Exception e) { } - return info; - } - - - /** Returns true if the Exif IFD is present and satisfies - * the profile requirements. - */ - public boolean isExifProfileOK () - { - return _exifProfileOK; + } } + return info; + } - /** - * Returns the text which describes the exif profile. - */ - public String getProfileText () - { - return _profileText; - } + /** + * Returns true if the Exif IFD is present and satisfies the profile requirements. + */ + public boolean isExifProfileOK() { + return _exifProfileOK; + } - /** - * Returns the NisoImageMetadata from the Exif SubIFD - */ - public NisoImageMetadata getExifNiso() { - return _exifNiso; - } + /** Returns the text which describes the exif profile. */ + public String getProfileText() { + return _profileText; + } + + /** Returns the NisoImageMetadata from the Exif SubIFD */ + public NisoImageMetadata getExifNiso() { + return _exifNiso; + } } diff --git a/jhove-modules/jpeg-hul/src/main/java/edu/harvard/hul/ois/jhove/module/jpeg/JpegStrings.java b/jhove-modules/jpeg-hul/src/main/java/edu/harvard/hul/ois/jhove/module/jpeg/JpegStrings.java index 9da0cbef2..3611d64ae 100644 --- a/jhove-modules/jpeg-hul/src/main/java/edu/harvard/hul/ois/jhove/module/jpeg/JpegStrings.java +++ b/jhove-modules/jpeg-hul/src/main/java/edu/harvard/hul/ois/jhove/module/jpeg/JpegStrings.java @@ -1,136 +1,117 @@ -/********************************************************************** - * Jhove - JSTOR/Harvard Object Validation Environment - * Copyright 2003 by JSTOR and the President and Fellows of Harvard College +/** + * ******************************************************************** Jhove - JSTOR/Harvard Object + * Validation Environment Copyright 2003 by JSTOR and the President and Fellows of Harvard College * - **********************************************************************/ - + *

******************************************************************** + */ package edu.harvard.hul.ois.jhove.module.jpeg; /** - * A class for holding arrays of informative strings that will go into - * properties of a JPEG object. + * A class for holding arrays of informative strings that will go into properties of a JPEG object. */ public class JpegStrings { - - /** JPEG compression types, indexed on marker byte - 0XC0. - * This applies only to marker codes for the primary image; - * thumbnail compression schemes are completely incompatible. */ - public final static String[] COMPRESSION_TYPE = - { "Huffman coding, Baseline DCT", - "Huffman coding, Extemded sequential DCT", - "Huffman coding, Progressive DCT", - "Huffman coding, Lossless (sequential)", - "", - "Huffman coding, Differential sequential DCT", - "Huffman coding, Differential progressive DCT", - "Huffman coding, Differential lossless (sequential)", - "", - "Arithmetic coding, Extended sequential DCT", - "Arithmetic coding, Progressive DCT", - "Arithmetic coding, Lossless (sequential)", - "", - "Arithmetic coding, Differential sequential DCT", - "Arithmetic coding, Differential progressive DCT", - "Arithmetic coding, Differential lossless (sequential)" - }; - - /** Values for capability indicator byte for Version 0 */ - public final static String[] CAPABILITY_V0 = - { - "", // 0 - "Baseline sequential", // 1 - "Extended sequential, Huffman, 8-bits", // 2 - "Extended sequential, arithmetic, 8-bits", // 3 - "Extended sequential, Huffman, 12-bits", // 4 - "Extended sequential, arithmetic, 12-bits", // 5 - "Spectral selection, Huffman, 8-bits", // 6 - "Spectral selection, arithmetic, 8-bits", // 7 - "Full progression, Huffman, 8-bits", // 8 - "Full progression, arithmetic, 8-bits", // 9 - "Spectral selection, Huffman, 12-bits", // 10 - "Spectral selection, arithmetic, 12-bits", // 11 - "Full progression, Huffman, 12-bits", // 12 - "Full progression, arithmetic, 12-bits", // 13 - "Lossless, Huffman", // 14 - "Lossless, arithmetic", // 15 - "Hierarchical, sequential Huffman, 8-bits", // 16 - "Hierarchical, sequential arithmetic, 8-bits", // 17 - "Hierarchical, sequential Huffman, 12-bits", // 18 - "Hierarchical, sequential arithmetic, 12-bits", // 19 - "Hierarchical, Spectral Selection, " + - "Huffman, 8-bits", // 20 - "Hierarchical, Spectral Selection, " + - "arithmetic, 8-bits", // 21 - "Hierarchical, Full progression, " + - "Huffman, 8-bits", // 22 - "Hierarchical, Full progression, " + - "arithmetic, 8-bits", // 23 - "Hierarchical, Spectral Selection, " + - "Huffman, 12-bits", // 24 - "Hierarchical, Spectral Selection, " + - "arithmetic, 12-bits", // 25 - "Hierarchical, Full progression, " + - "Huffman, 12-bits", // 26 - "Hierarchical, Full progression, " + - "arithmetic, 12-bits", // 27 - "Hierarchical, Lossless, Huffman", // 28 - "Hierarchical, Lossless, arithmetic" // 29 - }; - - - /** Values for capability indicator byte for Version 1. - * These are by bit position from right to left. - */ - public final static String[] CAPABILITY_V1 = - { - "10 < blocks per MCU < 20", // 0xxx xxx1 - "Variable quantization", // 0xxx xx1x - "Hierarchical selective refinement", // 0xxx x1xx - "Progressive selective refinement", // 0xxx 1xxx - "Componenet selective refinement", // 0xx1 xxxx - }; - - - /* Values for capability indicator byte, tiling bits, - * for Version 1. These match the indicated masks. - */ - public final static String[] TILING_CAPABILITY_V1 = - { - "No tiling", // 000x xxxx - "Simple tiling", // 001x xxxx - "Pyramidal tiling", // 010x xxxx - "Composite tiling" // 011x xxxx - }; - - /* Values for tiling type, as defined in the DTI segment. - */ - public final static String[] TILING_TYPE = - { - "Simple", // 0 - "Pyramidal", // 1 - "Composite" // 2 - }; - - /* Values for precision in DQT segment. - */ - public final static String[] DQT_PRECISION = - { - "8-bit", // 0 - "16-bit", // 1 - }; + /** + * JPEG compression types, indexed on marker byte - 0XC0. This applies only to marker codes for + * the primary image; thumbnail compression schemes are completely incompatible. + */ + public static final String[] COMPRESSION_TYPE = { + "Huffman coding, Baseline DCT", + "Huffman coding, Extemded sequential DCT", + "Huffman coding, Progressive DCT", + "Huffman coding, Lossless (sequential)", + "", + "Huffman coding, Differential sequential DCT", + "Huffman coding, Differential progressive DCT", + "Huffman coding, Differential lossless (sequential)", + "", + "Arithmetic coding, Extended sequential DCT", + "Arithmetic coding, Progressive DCT", + "Arithmetic coding, Lossless (sequential)", + "", + "Arithmetic coding, Differential sequential DCT", + "Arithmetic coding, Differential progressive DCT", + "Arithmetic coding, Differential lossless (sequential)" + }; + + /** Values for capability indicator byte for Version 0 */ + public static final String[] CAPABILITY_V0 = { + "", // 0 + "Baseline sequential", // 1 + "Extended sequential, Huffman, 8-bits", // 2 + "Extended sequential, arithmetic, 8-bits", // 3 + "Extended sequential, Huffman, 12-bits", // 4 + "Extended sequential, arithmetic, 12-bits", // 5 + "Spectral selection, Huffman, 8-bits", // 6 + "Spectral selection, arithmetic, 8-bits", // 7 + "Full progression, Huffman, 8-bits", // 8 + "Full progression, arithmetic, 8-bits", // 9 + "Spectral selection, Huffman, 12-bits", // 10 + "Spectral selection, arithmetic, 12-bits", // 11 + "Full progression, Huffman, 12-bits", // 12 + "Full progression, arithmetic, 12-bits", // 13 + "Lossless, Huffman", // 14 + "Lossless, arithmetic", // 15 + "Hierarchical, sequential Huffman, 8-bits", // 16 + "Hierarchical, sequential arithmetic, 8-bits", // 17 + "Hierarchical, sequential Huffman, 12-bits", // 18 + "Hierarchical, sequential arithmetic, 12-bits", // 19 + "Hierarchical, Spectral Selection, " + "Huffman, 8-bits", // 20 + "Hierarchical, Spectral Selection, " + "arithmetic, 8-bits", // 21 + "Hierarchical, Full progression, " + "Huffman, 8-bits", // 22 + "Hierarchical, Full progression, " + "arithmetic, 8-bits", // 23 + "Hierarchical, Spectral Selection, " + "Huffman, 12-bits", // 24 + "Hierarchical, Spectral Selection, " + "arithmetic, 12-bits", // 25 + "Hierarchical, Full progression, " + "Huffman, 12-bits", // 26 + "Hierarchical, Full progression, " + "arithmetic, 12-bits", // 27 + "Hierarchical, Lossless, Huffman", // 28 + "Hierarchical, Lossless, arithmetic" // 29 + }; + + /** + * Values for capability indicator byte for Version 1. These are by bit position from right to + * left. + */ + public static final String[] CAPABILITY_V1 = { + "10 < blocks per MCU < 20", // 0xxx xxx1 + "Variable quantization", // 0xxx xx1x + "Hierarchical selective refinement", // 0xxx x1xx + "Progressive selective refinement", // 0xxx 1xxx + "Componenet selective refinement", // 0xx1 xxxx + }; + + /* Values for capability indicator byte, tiling bits, + * for Version 1. These match the indicated masks. + */ + public static final String[] TILING_CAPABILITY_V1 = { + "No tiling", // 000x xxxx + "Simple tiling", // 001x xxxx + "Pyramidal tiling", // 010x xxxx + "Composite tiling" // 011x xxxx + }; + + /* Values for tiling type, as defined in the DTI segment. + */ + public static final String[] TILING_TYPE = { + "Simple", // 0 + "Pyramidal", // 1 + "Composite" // 2 + }; + + /* Values for precision in DQT segment. + */ + public static final String[] DQT_PRECISION = { + "8-bit", // 0 + "16-bit", // 1 + }; - /* Values for precision in DAC segment. - */ - public final static String[] DAC_CLASS = - { - "DC table or iossiess table", // 0 - "AC table", // 1 - }; + /* Values for precision in DAC segment. + */ + public static final String[] DAC_CLASS = { + "DC table or iossiess table", // 0 + "AC table", // 1 + }; - /** A private constructor just to make sure nobody - instantiates the class by mistake. */ - private JpegStrings () - { - } + /** A private constructor just to make sure nobody instantiates the class by mistake. */ + private JpegStrings() {} } diff --git a/jhove-modules/jpeg-hul/src/main/java/edu/harvard/hul/ois/jhove/module/jpeg/MessageConstants.java b/jhove-modules/jpeg-hul/src/main/java/edu/harvard/hul/ois/jhove/module/jpeg/MessageConstants.java index 38b587602..b15a38db5 100644 --- a/jhove-modules/jpeg-hul/src/main/java/edu/harvard/hul/ois/jhove/module/jpeg/MessageConstants.java +++ b/jhove-modules/jpeg-hul/src/main/java/edu/harvard/hul/ois/jhove/module/jpeg/MessageConstants.java @@ -1,6 +1,4 @@ -/** - * - */ +/** */ package edu.harvard.hul.ois.jhove.module.jpeg; import edu.harvard.hul.ois.jhove.messages.JhoveMessage; @@ -8,56 +6,55 @@ import edu.harvard.hul.ois.jhove.messages.JhoveMessages; /** - * Enum used to externalise the JPEG modules message Strings. Using an enum - * INSTANCE as a "trick" to ensure a single instance of the class. String - * constants should be prefixed according to their use in the module: + * Enum used to externalise the JPEG modules message Strings. Using an enum INSTANCE as a "trick" to + * ensure a single instance of the class. String constants should be prefixed according to their use + * in the module: + * *

    - *
  • WRN_ for warning strings, often logger messages.
  • - *
  • INF_ for informational messages.
  • - *
  • ERR_ for error messages that indicate a file is invalid or not well - * formed.
  • + *
  • WRN_ for warning strings, often logger messages. + *
  • INF_ for informational messages. + *
  • ERR_ for error messages that indicate a file is invalid or not well formed. *
- * When adding new messages try to adopt the following order for the naming - * elements: + * + * When adding new messages try to adopt the following order for the naming elements: + * *
    - *
  1. PREFIX: one of the three prefixes from the list above.
  2. - *
  3. ENTITY_NAME: the name of the entity causing the problem.
  4. - *
  5. Problem: a short indicator of the problem type, e.g. MISSING, ILLEGAL, - * etc.
  6. + *
  7. PREFIX: one of the three prefixes from the list above. + *
  8. ENTITY_NAME: the name of the entity causing the problem. + *
  9. Problem: a short indicator of the problem type, e.g. MISSING, ILLEGAL, etc. *
- * The elements should be separated by underscores. The messages currently don't - * follow a consistent vocabulary, that is terms such as invalid, illegal, or - * malformed are used without definition. - * - * @author Carl Wilson - * carlwilson AT github * + * The elements should be separated by underscores. The messages currently don't follow a consistent + * vocabulary, that is terms such as invalid, illegal, or malformed are used without definition. + * + * @author Carl Wilson carlwilson AT github * @version 0.1 - * - * Created 10 Oct 2016:20:54:20 + *

Created 10 Oct 2016:20:54:20 */ - public enum MessageConstants { - INSTANCE; - public static final JhoveMessageFactory messageFactory = JhoveMessages.getInstance( - "edu.harvard.hul.ois.jhove.module.jpeg.ErrorMessages"); - public static final String INF_EXIF_REPORT_REQUIRES_TIFF = "TIFF-HUL module required to report Exif data"; - public static final String INF_EXIF_APP2_MULTI_REPORT = "ICCProfile in multiple APP2 segments; not handled by JPEG-hul"; + INSTANCE; + public static final JhoveMessageFactory messageFactory = + JhoveMessages.getInstance("edu.harvard.hul.ois.jhove.module.jpeg.ErrorMessages"); + public static final String INF_EXIF_REPORT_REQUIRES_TIFF = + "TIFF-HUL module required to report Exif data"; + public static final String INF_EXIF_APP2_MULTI_REPORT = + "ICCProfile in multiple APP2 segments; not handled by JPEG-hul"; - public static final JhoveMessage JPEG_HUL_1 = messageFactory.getMessage("JPEG-HUL-1"); - public static final JhoveMessage JPEG_HUL_2 = messageFactory.getMessage("JPEG-HUL-2"); - public static final JhoveMessage JPEG_HUL_3 = messageFactory.getMessage("JPEG-HUL-3"); - public static final JhoveMessage JPEG_HUL_4 = messageFactory.getMessage("JPEG-HUL-4"); - public static final JhoveMessage JPEG_HUL_5 = messageFactory.getMessage("JPEG-HUL-5"); - public static final JhoveMessage JPEG_HUL_6 = messageFactory.getMessage("JPEG-HUL-6"); - public static final JhoveMessage JPEG_HUL_7 = messageFactory.getMessage("JPEG-HUL-7"); - public static final JhoveMessage JPEG_HUL_7_SUB = messageFactory.getMessage("JPEG-HUL-7-SUB"); - public static final JhoveMessage JPEG_HUL_8 = messageFactory.getMessage("JPEG-HUL-8"); - public static final JhoveMessage JPEG_HUL_9 = messageFactory.getMessage("JPEG-HUL-9"); - public static final JhoveMessage JPEG_HUL_10 = messageFactory.getMessage("JPEG-HUL-10"); - public static final JhoveMessage JPEG_HUL_11 = messageFactory.getMessage("JPEG-HUL-11"); - public static final JhoveMessage JPEG_HUL_12 = messageFactory.getMessage("JPEG-HUL-12"); - public static final JhoveMessage JPEG_HUL_13 = messageFactory.getMessage("JPEG-HUL-13"); + public static final JhoveMessage JPEG_HUL_1 = messageFactory.getMessage("JPEG-HUL-1"); + public static final JhoveMessage JPEG_HUL_2 = messageFactory.getMessage("JPEG-HUL-2"); + public static final JhoveMessage JPEG_HUL_3 = messageFactory.getMessage("JPEG-HUL-3"); + public static final JhoveMessage JPEG_HUL_4 = messageFactory.getMessage("JPEG-HUL-4"); + public static final JhoveMessage JPEG_HUL_5 = messageFactory.getMessage("JPEG-HUL-5"); + public static final JhoveMessage JPEG_HUL_6 = messageFactory.getMessage("JPEG-HUL-6"); + public static final JhoveMessage JPEG_HUL_7 = messageFactory.getMessage("JPEG-HUL-7"); + public static final JhoveMessage JPEG_HUL_7_SUB = messageFactory.getMessage("JPEG-HUL-7-SUB"); + public static final JhoveMessage JPEG_HUL_8 = messageFactory.getMessage("JPEG-HUL-8"); + public static final JhoveMessage JPEG_HUL_9 = messageFactory.getMessage("JPEG-HUL-9"); + public static final JhoveMessage JPEG_HUL_10 = messageFactory.getMessage("JPEG-HUL-10"); + public static final JhoveMessage JPEG_HUL_11 = messageFactory.getMessage("JPEG-HUL-11"); + public static final JhoveMessage JPEG_HUL_12 = messageFactory.getMessage("JPEG-HUL-12"); + public static final JhoveMessage JPEG_HUL_13 = messageFactory.getMessage("JPEG-HUL-13"); - public static final JhoveMessage JHOVE_1 = messageFactory.getMessage("JHOVE-1"); + public static final JhoveMessage JHOVE_1 = messageFactory.getMessage("JHOVE-1"); } diff --git a/jhove-modules/jpeg-hul/src/main/java/edu/harvard/hul/ois/jhove/module/jpeg/QuantizationTable.java b/jhove-modules/jpeg-hul/src/main/java/edu/harvard/hul/ois/jhove/module/jpeg/QuantizationTable.java index 39abca520..5c305c95b 100644 --- a/jhove-modules/jpeg-hul/src/main/java/edu/harvard/hul/ois/jhove/module/jpeg/QuantizationTable.java +++ b/jhove-modules/jpeg-hul/src/main/java/edu/harvard/hul/ois/jhove/module/jpeg/QuantizationTable.java @@ -1,61 +1,44 @@ -/********************************************************************** - * Jhove - JSTOR/Harvard Object Validation Environment - * Copyright 2003 by JSTOR and the President and Fellows of Harvard College +/** + * ******************************************************************** Jhove - JSTOR/Harvard Object + * Validation Environment Copyright 2003 by JSTOR and the President and Fellows of Harvard College * - **********************************************************************/ - + *

******************************************************************** + */ package edu.harvard.hul.ois.jhove.module.jpeg; import edu.harvard.hul.ois.jhove.*; /** * Encapsulation of a quantization table entry for a JPEG image. - * - * @author Gary McGath * + * @author Gary McGath */ public class QuantizationTable { - private int _precision; - private int _destIdentifier; - - - /** - * Constructor. - */ - public QuantizationTable(int precision, int destIdentifier) { - _precision = precision; - _destIdentifier = destIdentifier; - } - - - /** - * Returns a Property defining the quantization table - */ - public Property makeProperty (boolean raw) - { - Property[] parray = new Property[2]; - if (raw) { - parray[0] = new Property ("Precision", - PropertyType.INTEGER, - new Integer (_precision)); - } - else { - String prec = "Undefined"; - try { - prec = JpegStrings.DQT_PRECISION[_precision]; - } - catch (Exception e) {} - parray[0] = new Property ("Precision", - PropertyType.STRING, - prec); - } - parray[1] = new Property ("DestinationIdentifier", - PropertyType.INTEGER, - new Integer (_destIdentifier)); - return new Property ("QuantizationTable", - PropertyType.PROPERTY, - PropertyArity.ARRAY, - parray); + private int _precision; + private int _destIdentifier; + + /** Constructor. */ + public QuantizationTable(int precision, int destIdentifier) { + _precision = precision; + _destIdentifier = destIdentifier; + } + + /** Returns a Property defining the quantization table */ + public Property makeProperty(boolean raw) { + Property[] parray = new Property[2]; + if (raw) { + parray[0] = new Property("Precision", PropertyType.INTEGER, new Integer(_precision)); + } else { + String prec = "Undefined"; + try { + prec = JpegStrings.DQT_PRECISION[_precision]; + } catch (Exception e) { + } + parray[0] = new Property("Precision", PropertyType.STRING, prec); } + parray[1] = + new Property("DestinationIdentifier", PropertyType.INTEGER, new Integer(_destIdentifier)); + return new Property("QuantizationTable", PropertyType.PROPERTY, PropertyArity.ARRAY, parray); + } } diff --git a/jhove-modules/jpeg-hul/src/main/java/edu/harvard/hul/ois/jhove/module/jpeg/SRS.java b/jhove-modules/jpeg-hul/src/main/java/edu/harvard/hul/ois/jhove/module/jpeg/SRS.java index 14a627f9e..4d21664f7 100644 --- a/jhove-modules/jpeg-hul/src/main/java/edu/harvard/hul/ois/jhove/module/jpeg/SRS.java +++ b/jhove-modules/jpeg-hul/src/main/java/edu/harvard/hul/ois/jhove/module/jpeg/SRS.java @@ -1,62 +1,41 @@ -/********************************************************************** - * Jhove - JSTOR/Harvard Object Validation Environment - * Copyright 2003 by JSTOR and the President and Fellows of Harvard College +/** + * ******************************************************************** Jhove - JSTOR/Harvard Object + * Validation Environment Copyright 2003 by JSTOR and the President and Fellows of Harvard College * - **********************************************************************/ - + *

******************************************************************** + */ package edu.harvard.hul.ois.jhove.module.jpeg; import edu.harvard.hul.ois.jhove.*; /** * Encapsulation of an SRS (selectively refined scan) entry for a JPEG image. - * - * @author Gary McGath * + * @author Gary McGath */ public class SRS { - private int _vertOffset; - private int _horOffset; - private int _vertSize; - private int _horSize; - - - /** - * Constructor. - */ - public SRS(int vertOffset, int horOffset, int vertSize, int horSize) - { - _vertOffset = vertOffset; - _horOffset = horOffset; - _vertSize = vertSize; - _horSize = horSize; - } - - - - /** - * Returns a Property defining the SRS - */ - public Property makeProperty () - { - Property[] parray = new Property[4]; - parray[0] = new Property ("VerticalOffset", - PropertyType.INTEGER, - new Integer (_vertOffset)); - parray[1] = new Property ("HorizontalOffset", - PropertyType.INTEGER, - new Integer (_horOffset)); - parray[2] = new Property ("VerticalSize", - PropertyType.INTEGER, - new Integer (_vertSize)); - parray[3] = new Property ("HorizontalSize", - PropertyType.INTEGER, - new Integer (_horSize)); - return new Property ("SelectivelyRefinedScan", - PropertyType.PROPERTY, - PropertyArity.ARRAY, - parray); - } - + private int _vertOffset; + private int _horOffset; + private int _vertSize; + private int _horSize; + + /** Constructor. */ + public SRS(int vertOffset, int horOffset, int vertSize, int horSize) { + _vertOffset = vertOffset; + _horOffset = horOffset; + _vertSize = vertSize; + _horSize = horSize; + } + + /** Returns a Property defining the SRS */ + public Property makeProperty() { + Property[] parray = new Property[4]; + parray[0] = new Property("VerticalOffset", PropertyType.INTEGER, new Integer(_vertOffset)); + parray[1] = new Property("HorizontalOffset", PropertyType.INTEGER, new Integer(_horOffset)); + parray[2] = new Property("VerticalSize", PropertyType.INTEGER, new Integer(_vertSize)); + parray[3] = new Property("HorizontalSize", PropertyType.INTEGER, new Integer(_horSize)); + return new Property( + "SelectivelyRefinedScan", PropertyType.PROPERTY, PropertyArity.ARRAY, parray); + } } diff --git a/jhove-modules/jpeg-hul/src/main/java/edu/harvard/hul/ois/jhove/module/jpeg/Spiff.java b/jhove-modules/jpeg-hul/src/main/java/edu/harvard/hul/ois/jhove/module/jpeg/Spiff.java index 662416697..9e7846fb0 100644 --- a/jhove-modules/jpeg-hul/src/main/java/edu/harvard/hul/ois/jhove/module/jpeg/Spiff.java +++ b/jhove-modules/jpeg-hul/src/main/java/edu/harvard/hul/ois/jhove/module/jpeg/Spiff.java @@ -1,98 +1,88 @@ -/********************************************************************** - * Jhove - JSTOR/Harvard Object Validation Environment - * Copyright 2003 by JSTOR and the President and Fellows of Harvard College +/** + * ******************************************************************** Jhove - JSTOR/Harvard Object + * Validation Environment Copyright 2003 by JSTOR and the President and Fellows of Harvard College * - **********************************************************************/ - + *

******************************************************************** + */ package edu.harvard.hul.ois.jhove.module.jpeg; /** * Static methods and data for SPIFF Jpeg files. - * - * @author Gary McGath * + * @author Gary McGath */ public class Spiff { - /* Definitions of SPIFF tags. */ - public final static int - EOD = 1, - XFER_CHARACTERISTICS = 2, - IMAGE_ORIENTATION = 3, - THUMBNAIL = 4, - IMAGE_TITLE = 5, - IMAGE_DESC = 6, - TIME_STAMP = 7, - VERSION_IDENT = 8, - CREATOR_ID = 9, - PROTECTION_INDICATOR = 0XA, - COPYRIGHT_INFO = 0X0C, - CONTACT_INFO = 0X0D, - TILE_INDEX = 0X0E, - SCAN_INDEX = 0X0F, - SETREF = 0X10; - - /* Color space to NISO mapping array. */ - private final static int[] nisoColor = { - 0, // 0 bilevel, white is 0 - 6, // 1 YCbCr (1) - -1, // 2 other - 6, // 3 YCbCr (2) - 6, // 4 YCbCr (3) - -1, // 5 reserved - -1, // 6 reserved - -1, // 7 reserved - 1, // 8 grayscale (black is 0) - -1, // 9 PhotoYCC - 2, // 10 RGB - -1, // 11 CMY - 5, // 12 CMYK - -1, // 13 YCCK - 8, // 14 CIELab - 1 // 15 bilevel, black is 0 - }; + /* Definitions of SPIFF tags. */ + public static final int EOD = 1, + XFER_CHARACTERISTICS = 2, + IMAGE_ORIENTATION = 3, + THUMBNAIL = 4, + IMAGE_TITLE = 5, + IMAGE_DESC = 6, + TIME_STAMP = 7, + VERSION_IDENT = 8, + CREATOR_ID = 9, + PROTECTION_INDICATOR = 0XA, + COPYRIGHT_INFO = 0X0C, + CONTACT_INFO = 0X0D, + TILE_INDEX = 0X0E, + SCAN_INDEX = 0X0F, + SETREF = 0X10; + /* Color space to NISO mapping array. */ + private static final int[] nisoColor = { + 0, // 0 bilevel, white is 0 + 6, // 1 YCbCr (1) + -1, // 2 other + 6, // 3 YCbCr (2) + 6, // 4 YCbCr (3) + -1, // 5 reserved + -1, // 6 reserved + -1, // 7 reserved + 1, // 8 grayscale (black is 0) + -1, // 9 PhotoYCC + 2, // 10 RGB + -1, // 11 CMY + 5, // 12 CMYK + -1, // 13 YCCK + 8, // 14 CIELab + 1 // 15 bilevel, black is 0 + }; - /* Compression to NISO mapping array. When we don't have - * an exact match, call it JPEG (6). */ - private final static int[] nisoCompScheme = { - 1, // 0 uncompressed - 6, // 1 T.4, MH - 6, // 2 T.4, MR - 6, // 3 T.6, MMR - 32661, // 4 JBIG - 6 // 5 JPEG - }; - - /** - * Private constructor, to prevent instantiation - */ - private Spiff() { - } + /* Compression to NISO mapping array. When we don't have + * an exact match, call it JPEG (6). */ + private static final int[] nisoCompScheme = { + 1, // 0 uncompressed + 6, // 1 T.4, MH + 6, // 2 T.4, MR + 6, // 3 T.6, MMR + 32661, // 4 JBIG + 6 // 5 JPEG + }; + /** Private constructor, to prevent instantiation */ + private Spiff() {} - /** Converts S value to NISO color space. Return -1 if there - * is no matching color space in NISO, or the S value is out - * of bounds. - */ - public static int colorSpaceToNiso (int s) - { - if (s < 0 || s > nisoColor.length) { - return -1; - } - return nisoColor[s]; + /** + * Converts S value to NISO color space. Return -1 if there is no matching color space in NISO, or + * the S value is out of bounds. + */ + public static int colorSpaceToNiso(int s) { + if (s < 0 || s > nisoColor.length) { + return -1; } + return nisoColor[s]; + } - - /** Converts C value to NISO compression scheme. Return -1 if there - * is no matching color space in NISO, or the S value is out - * of bounds. - */ - public static int compressionTypeToNiso (int s) - { - if (s < 0 || s > nisoCompScheme.length) { - return -1; - } - return nisoCompScheme[s]; + /** + * Converts C value to NISO compression scheme. Return -1 if there is no matching color space in + * NISO, or the S value is out of bounds. + */ + public static int compressionTypeToNiso(int s) { + if (s < 0 || s > nisoCompScheme.length) { + return -1; } + return nisoCompScheme[s]; + } } diff --git a/jhove-modules/jpeg-hul/src/main/java/edu/harvard/hul/ois/jhove/module/jpeg/SpiffDir.java b/jhove-modules/jpeg-hul/src/main/java/edu/harvard/hul/ois/jhove/module/jpeg/SpiffDir.java index a0f91ddcf..c8e31aefc 100644 --- a/jhove-modules/jpeg-hul/src/main/java/edu/harvard/hul/ois/jhove/module/jpeg/SpiffDir.java +++ b/jhove-modules/jpeg-hul/src/main/java/edu/harvard/hul/ois/jhove/module/jpeg/SpiffDir.java @@ -1,121 +1,98 @@ -/********************************************************************** - * Jhove - JSTOR/Harvard Object Validation Environment - * Copyright 2003 by JSTOR and the President and Fellows of Harvard College +/** + * ******************************************************************** Jhove - JSTOR/Harvard Object + * Validation Environment Copyright 2003 by JSTOR and the President and Fellows of Harvard College * - **********************************************************************/ - + *

******************************************************************** + */ package edu.harvard.hul.ois.jhove.module.jpeg; -import java.io.*; -import java.util.*; import edu.harvard.hul.ois.jhove.*; import edu.harvard.hul.ois.jhove.module.JpegModule; +import java.io.*; +import java.util.*; /** - * This class represents a SPIFF directory and the tags defined under - * it. A SPIFF directory consists of one or more APP8 segments, and - * may define ancillary images. It is always contained within the + * This class represents a SPIFF directory and the tags defined under it. A SPIFF directory consists + * of one or more APP8 segments, and may define ancillary images. It is always contained within the * primary image stream. - * - * @author Gary McGath * + * @author Gary McGath */ public class SpiffDir { - - private JpegModule _module; - - /* list of thumbnail properties */ - private List _thumbnails; - - /** - * - */ - public SpiffDir(JpegModule module) { - _module = module; - _thumbnails = new LinkedList (); - } + private JpegModule _module; + + /* list of thumbnail properties */ + private List _thumbnails; + + /** */ + public SpiffDir(JpegModule module) { + _module = module; + _thumbnails = new LinkedList(); + } - /** - * Reads a directory entry, starting at the position after - * the APP8 marker and length. If the entry is for a thumbnail, create - * a Property for that thumbnail and add it to the thumbnail - * list. Other tags provide interesting information, some of - * which should go into properties, but for the moment we - * just handle the thumbnail and ignore other tags. - * - * An APP8 segment which is in a SPIFF file, and isn't the - * first APP8 segment (file header), is presumed to be a - * directory entry. These directory entries are a little - * inconvenient, because they can contain offsets to data in - * what we otherwise handle as a stream format. The offsets - * can be either to data within the block, or to faraway - * indirect data blocks. For the present version, we ignore - * offset data, which seems to be used only for the actual - * image bits (e.g., TNDATA). - */ - public void readDirEntry (DataInputStream dstream, int length) - throws IOException - { - int tag = (int) _module.readUnsignedInt (dstream); - switch (tag) { - case Spiff.THUMBNAIL: - readThumbnail (dstream, length); - break; - - default: - _module.skipBytes (dstream, length - 6, _module); - break; - } + /** + * Reads a directory entry, starting at the position after the APP8 marker and length. If the + * entry is for a thumbnail, create a Property for that thumbnail and add it to the thumbnail + * list. Other tags provide interesting information, some of which should go into properties, but + * for the moment we just handle the thumbnail and ignore other tags. + * + *

An APP8 segment which is in a SPIFF file, and isn't the first APP8 segment (file header), is + * presumed to be a directory entry. These directory entries are a little inconvenient, because + * they can contain offsets to data in what we otherwise handle as a stream format. The offsets + * can be either to data within the block, or to faraway indirect data blocks. For the present + * version, we ignore offset data, which seems to be used only for the actual image bits (e.g., + * TNDATA). + */ + public void readDirEntry(DataInputStream dstream, int length) throws IOException { + int tag = (int) _module.readUnsignedInt(dstream); + switch (tag) { + case Spiff.THUMBNAIL: + readThumbnail(dstream, length); + break; + + default: + _module.skipBytes(dstream, length - 6, _module); + break; } - - - /** - * Appends any thumbnail properties that have been collected to - * the provided list. - */ - public void appendThumbnailProps (List imageList) - { - imageList.addAll (_thumbnails); + } + + /** Appends any thumbnail properties that have been collected to the provided list. */ + public void appendThumbnailProps(List imageList) { + imageList.addAll(_thumbnails); + } + + /* Reads a thumbnail entry. A Property is created and added to + * the list of thumbnails. + */ + private void readThumbnail(DataInputStream dstream, int length) throws IOException { + NisoImageMetadata niso = new NisoImageMetadata(); + _module.skipBytes(dstream, 4, _module); // tndata + // Read Unsigned shorts for height and width, we don't need the values + _module.readUnsignedShort(dstream); + _module.readUnsignedShort(dstream); + int tns = ModuleBase.readUnsignedByte(dstream, _module); + int tnbps = ModuleBase.readUnsignedByte(dstream, _module); + int tnc = ModuleBase.readUnsignedByte(dstream, _module); + _module.skipBytes(dstream, length - 13, _module); + + // Fill in NISO data + niso.setMimeType("image/jpeg"); + niso.setByteOrder("big-endian"); + niso.setBitsPerSample(new int[] {tnbps}); + int cs = Spiff.colorSpaceToNiso(tns); + if (cs >= 0) { + niso.setColorSpace(cs); } - - - /* Reads a thumbnail entry. A Property is created and added to - * the list of thumbnails. - */ - private void readThumbnail (DataInputStream dstream, int length) - throws IOException - { - NisoImageMetadata niso = new NisoImageMetadata(); - _module.skipBytes (dstream, 4, _module); // tndata - // Read Unsigned shorts for height and width, we don't need the values - _module.readUnsignedShort (dstream); - _module.readUnsignedShort (dstream); - int tns = ModuleBase.readUnsignedByte (dstream, _module); - int tnbps = ModuleBase.readUnsignedByte (dstream, _module); - int tnc = ModuleBase.readUnsignedByte (dstream, _module); - _module.skipBytes (dstream, length - 13, _module); - - // Fill in NISO data - niso.setMimeType("image/jpeg"); - niso.setByteOrder ("big-endian"); - niso.setBitsPerSample (new int[] {tnbps} ); - int cs = Spiff.colorSpaceToNiso(tns); - if (cs >= 0) { - niso.setColorSpace (cs); - } - int comp = Spiff.compressionTypeToNiso (tnc); - if (comp >= 0) { - niso.setCompressionScheme(comp); - } - Property nisoProp = new Property ("NisoImageMetadata", - PropertyType.NISOIMAGEMETADATA, niso); - List propList = new LinkedList (); - propList.add (nisoProp); - Property imageProp = new Property ("ThumbImage", - PropertyType.PROPERTY, - PropertyArity.LIST, - propList); - _thumbnails.add (imageProp); + int comp = Spiff.compressionTypeToNiso(tnc); + if (comp >= 0) { + niso.setCompressionScheme(comp); } + Property nisoProp = new Property("NisoImageMetadata", PropertyType.NISOIMAGEMETADATA, niso); + List propList = new LinkedList(); + propList.add(nisoProp); + Property imageProp = + new Property("ThumbImage", PropertyType.PROPERTY, PropertyArity.LIST, propList); + _thumbnails.add(imageProp); + } } diff --git a/jhove-modules/jpeg-hul/src/main/java/edu/harvard/hul/ois/jhove/module/jpeg/Tiling.java b/jhove-modules/jpeg-hul/src/main/java/edu/harvard/hul/ois/jhove/module/jpeg/Tiling.java index 57795429b..1de960ab7 100644 --- a/jhove-modules/jpeg-hul/src/main/java/edu/harvard/hul/ois/jhove/module/jpeg/Tiling.java +++ b/jhove-modules/jpeg-hul/src/main/java/edu/harvard/hul/ois/jhove/module/jpeg/Tiling.java @@ -1,151 +1,100 @@ -/********************************************************************** - * Jhove - JSTOR/Harvard Object Validation Environment - * Copyright 2003 by JSTOR and the President and Fellows of Harvard College +/** + * ******************************************************************** Jhove - JSTOR/Harvard Object + * Validation Environment Copyright 2003 by JSTOR and the President and Fellows of Harvard College * - **********************************************************************/ - + *

******************************************************************** + */ package edu.harvard.hul.ois.jhove.module.jpeg; -import java.util.*; import edu.harvard.hul.ois.jhove.*; +import java.util.*; /** * Encapsulation of the tiling information for a JPEG image. - * - * @author Gary McGath * + * @author Gary McGath */ public class Tiling { - /* List of individual tile descriptions */ - private List tileList; - - private int _tilingType; - private int _vertScale; - private int _horScale; - private long _refGridHeight; - private long _refGridWidth; - - - /** - * Constructor. - */ - public Tiling() { - tileList = new LinkedList<> (); - } - - /** - * Adds a tile to the list. - */ - public void addTile (long vertScale, - long horScale, - long vertOffset, - long horOffset) - { - // Represent the tile as an array of 4 longs - long[] tile = new long[4]; - tile[0] = vertScale; - tile[1] = horScale; - tile[2] = vertOffset; - tile[3] = horOffset; - - tileList.add (tile); - } - - /** - * Returns a property listing all the tiles. - */ - public Property buildTileListProp () - { - List tpList = new LinkedList<> (); - ListIterator iter = tileList.listIterator (); - while (iter.hasNext ()) { - long[] tile = iter.next (); - Property[] tProp = new Property[4]; - tProp[0] = new Property ("VerticalScale", - PropertyType.LONG, - new Long (tile[0])); - tProp[1] = new Property ("HorizontalScale", - PropertyType.LONG, - new Long (tile[1])); - tProp[2] = new Property ("VerticalOffsret", - PropertyType.LONG, - new Long (tile[2])); - tProp[3] = new Property ("HorizontalOffset", - PropertyType.LONG, - new Long (tile[3])); - tpList.add (new Property ("Tile", - PropertyType.PROPERTY, - PropertyArity.ARRAY, - tProp)); - } - return new Property ("Tiles", - PropertyType.PROPERTY, - PropertyArity.LIST, - tpList); - } - - + /* List of individual tile descriptions */ + private List tileList; - public void setTilingType (int tilingType) - { - _tilingType = tilingType; - } - - - public void setVertScale (int vertScale) - { - _vertScale = vertScale; - } - - - public void setHorScale (int horScale) - { - _horScale = horScale; - } - - - public void setRefGridHeight (long refGridHeight) - { - _refGridHeight = refGridHeight; - } - - - public void setRefGridWidth (long refGridWidth) - { - _refGridWidth = refGridWidth; - } - - - public int getTilingType () - { - return _tilingType; - } - - - public int getVertScale () - { - return _vertScale; - } - - - public int getHorScale () - { - return _horScale; - } - - - public long getRefGridHeight () - { - return _refGridHeight; - } - - - public long getRefGridWidth () - { - return _refGridWidth; + private int _tilingType; + private int _vertScale; + private int _horScale; + private long _refGridHeight; + private long _refGridWidth; + + /** Constructor. */ + public Tiling() { + tileList = new LinkedList<>(); + } + + /** Adds a tile to the list. */ + public void addTile(long vertScale, long horScale, long vertOffset, long horOffset) { + // Represent the tile as an array of 4 longs + long[] tile = new long[4]; + tile[0] = vertScale; + tile[1] = horScale; + tile[2] = vertOffset; + tile[3] = horOffset; + + tileList.add(tile); + } + + /** Returns a property listing all the tiles. */ + public Property buildTileListProp() { + List tpList = new LinkedList<>(); + ListIterator iter = tileList.listIterator(); + while (iter.hasNext()) { + long[] tile = iter.next(); + Property[] tProp = new Property[4]; + tProp[0] = new Property("VerticalScale", PropertyType.LONG, new Long(tile[0])); + tProp[1] = new Property("HorizontalScale", PropertyType.LONG, new Long(tile[1])); + tProp[2] = new Property("VerticalOffsret", PropertyType.LONG, new Long(tile[2])); + tProp[3] = new Property("HorizontalOffset", PropertyType.LONG, new Long(tile[3])); + tpList.add(new Property("Tile", PropertyType.PROPERTY, PropertyArity.ARRAY, tProp)); } - - + return new Property("Tiles", PropertyType.PROPERTY, PropertyArity.LIST, tpList); + } + + public void setTilingType(int tilingType) { + _tilingType = tilingType; + } + + public void setVertScale(int vertScale) { + _vertScale = vertScale; + } + + public void setHorScale(int horScale) { + _horScale = horScale; + } + + public void setRefGridHeight(long refGridHeight) { + _refGridHeight = refGridHeight; + } + + public void setRefGridWidth(long refGridWidth) { + _refGridWidth = refGridWidth; + } + + public int getTilingType() { + return _tilingType; + } + + public int getVertScale() { + return _vertScale; + } + + public int getHorScale() { + return _horScale; + } + + public long getRefGridHeight() { + return _refGridHeight; + } + public long getRefGridWidth() { + return _refGridWidth; + } } diff --git a/jhove-modules/jpeg-hul/src/main/java/edu/harvard/hul/ois/jhove/module/jpeg/package-info.java b/jhove-modules/jpeg-hul/src/main/java/edu/harvard/hul/ois/jhove/module/jpeg/package-info.java index 971c3588c..665a29bfe 100644 --- a/jhove-modules/jpeg-hul/src/main/java/edu/harvard/hul/ois/jhove/module/jpeg/package-info.java +++ b/jhove-modules/jpeg-hul/src/main/java/edu/harvard/hul/ois/jhove/module/jpeg/package-info.java @@ -1,4 +1,2 @@ -/** - * Contains supporting classes for the JPEG-HUL module. - */ -package edu.harvard.hul.ois.jhove.module.jpeg; \ No newline at end of file +/** Contains supporting classes for the JPEG-HUL module. */ +package edu.harvard.hul.ois.jhove.module.jpeg; diff --git a/jhove-modules/jpeg2000-hul/src/main/java/J2Dump.java b/jhove-modules/jpeg2000-hul/src/main/java/J2Dump.java index 78517000f..73fedd700 100644 --- a/jhove-modules/jpeg2000-hul/src/main/java/J2Dump.java +++ b/jhove-modules/jpeg2000-hul/src/main/java/J2Dump.java @@ -1,213 +1,184 @@ -/********************************************************************** - * JDump - JSTOR/Harvard Object Validation Environment - * Copyright 2004 by the President and Fellows of Harvard College +/** + * ******************************************************************** JDump - JSTOR/Harvard Object + * Validation Environment Copyright 2004 by the President and Fellows of Harvard College * - * This program 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 2 of the License, or (at - * your option) any later version. - * - * This program 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. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 - * USA - **********************************************************************/ - + *

This program 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 2 + * of the License, or (at your option) any later version. + * + *

This program 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. + * + *

You should have received a copy of the GNU Lesser General Public License along with this + * program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA ******************************************************************** + */ import edu.harvard.hul.ois.jhove.*; import java.io.*; import java.util.*; -/** - * Dump contents of JPEG2000 file in human-readable format. - */ +/** Dump contents of JPEG2000 file in human-readable format. */ public class J2Dump extends Dump { - /* Fixed value for first 12 bytes */ - private static final int[] sigByte = - { - 0X00, - 0X00, - 0X00, - 0X0C, - 0X6A, - 0X50, - 0X20, - 0X20, - 0X0D, - 0X0A, - 0X87, - 0X0A }; + /* Fixed value for first 12 bytes */ + private static final int[] sigByte = { + 0X00, 0X00, 0X00, 0X0C, 0X6A, 0X50, 0X20, 0X20, 0X0D, 0X0A, 0X87, 0X0A + }; - private static final boolean ENDIAN = true; /* bigEndian */ - - /****************************************************************** - * MAIN ENTRY POINT. - ******************************************************************/ + private static final boolean ENDIAN = true; /* bigEndian */ - /** - * Main entry point. - * @param args Command line arguments - */ - public static void main (String [] args) - { - if (args.length < 1) { - System.err.println ("usage: java J2Dump file"); - System.exit (-1); + /** + * **************************************************************** MAIN ENTRY POINT. + * **************************************************************** + */ + + /** + * Main entry point. + * + * @param args Command line arguments + */ + public static void main(String[] args) { + if (args.length < 1) { + System.err.println("usage: java J2Dump file"); + System.exit(-1); + } + try { + FileInputStream file = new FileInputStream(args[0]); + BufferedInputStream buffer = new BufferedInputStream(file); + DataInputStream stream = new DataInputStream(buffer); + J2Dump dump = new J2Dump(); // Just to access contained classes + long os = 0; + int i; + for (i = 0; i < 12; i++) { + int ch; + ch = stream.readUnsignedByte(); + if (ch != sigByte[i]) { + System.out.println("No JPEG 2000 header"); + System.exit(-2); } - try { - FileInputStream file = new FileInputStream (args[0]); - BufferedInputStream buffer = new BufferedInputStream (file); - DataInputStream stream = new DataInputStream (buffer); - J2Dump dump = new J2Dump (); // Just to access contained classes - long os = 0; - int i; - for (i = 0; i < 12; i++) { - int ch; - ch = stream.readUnsignedByte(); - if (ch != sigByte[i]) { - System.out.println ("No JPEG 2000 header"); - System.exit (-2); - } - } - os += 12; - - boolean endOfFile = false; - Stack boxStack = new Stack<>(); - while (!endOfFile) { - //If there are boxes on the stack, see if there's space - //left in the top one. - Box boxtop; - for (;;) { - boxtop = null; - if (boxStack.isEmpty ()) { - break; - } - boxtop = boxStack.peek (); - if (boxtop.bytesLeft > 0) { - break; - } - boxStack.pop (); - } - - // Read the header of a JP2 box - Box box = dump.new Box (stream); - try { - box.read (); - } - catch (EOFException e) { - endOfFile = true; - break; - } - os += box.length - box.bytesLeft; - - // If it's contained in a superbox, subtract - // this box from its remaining length - if (boxtop != null) { - boxtop.bytesLeft -= box.length; - } - System.out.println (leading (os, 8) + os + ": " + - stackPrefix (boxStack) + box.type + " " + box.length); - if (box.isSuperbox ()) { - boxStack.push (box); - } - else { - os += box.bytesLeft; - stream.skipBytes((int) box.bytesLeft); - } - - // A "length" of 0 means the box occupies the rest of the file. - if (box.length == 0) { - endOfFile = true; - } - } + } + os += 12; + + boolean endOfFile = false; + Stack boxStack = new Stack<>(); + while (!endOfFile) { + // If there are boxes on the stack, see if there's space + // left in the top one. + Box boxtop; + for (; ; ) { + boxtop = null; + if (boxStack.isEmpty()) { + break; + } + boxtop = boxStack.peek(); + if (boxtop.bytesLeft > 0) { + break; + } + boxStack.pop(); } - catch (Exception e) { - e.printStackTrace (System.err); - System.exit (-2); + + // Read the header of a JP2 box + Box box = dump.new Box(stream); + try { + box.read(); + } catch (EOFException e) { + endOfFile = true; + break; } - } + os += box.length - box.bytesLeft; + // If it's contained in a superbox, subtract + // this box from its remaining length + if (boxtop != null) { + boxtop.bytesLeft -= box.length; + } + System.out.println( + leading(os, 8) + os + ": " + stackPrefix(boxStack) + box.type + " " + box.length); + if (box.isSuperbox()) { + boxStack.push(box); + } else { + os += box.bytesLeft; + stream.skipBytes((int) box.bytesLeft); + } - /* Constructs a qualifying prefix to indicate nested boxes. */ - private static String stackPrefix (Stack boxStack) - { - StringBuffer retval = new StringBuffer (); - // In defiance of gravity, we rummage through the stack - // of boxes starting at the bottom. - for (int i = 0; i < boxStack.size(); i++) { - Box box = boxStack.elementAt (i); - // Remove trailing spaces from types for better readability - retval.append (box.type.trim() + "/"); + // A "length" of 0 means the box occupies the rest of the file. + if (box.length == 0) { + endOfFile = true; } - return retval.toString (); + } + } catch (Exception e) { + e.printStackTrace(System.err); + System.exit(-2); } + } + /* Constructs a qualifying prefix to indicate nested boxes. */ + private static String stackPrefix(Stack boxStack) { + StringBuffer retval = new StringBuffer(); + // In defiance of gravity, we rummage through the stack + // of boxes starting at the bottom. + for (int i = 0; i < boxStack.size(); i++) { + Box box = boxStack.elementAt(i); + // Remove trailing spaces from types for better readability + retval.append(box.type.trim() + "/"); + } + return retval.toString(); + } + /** Local class for defining JPEG2000 boxes. */ + class Box { + public String type; + public long length; + public long bytesLeft; + public boolean hasBoxes; + DataInputStream dstream; - /** Local class for defining JPEG2000 boxes. */ - class Box { - public String type; - public long length; - public long bytesLeft; - public boolean hasBoxes; - DataInputStream dstream; - - public Box (DataInputStream stream) - { - this.dstream = stream; - } - - - /** Reads a box header and sets up for reading contents. */ - public void read () throws IOException - { - length = ModuleBase.readUnsignedInt (dstream, ENDIAN, null); - long headerLength = 8; - type = read4Chars (); - // If the length field is 1, there is an 8-byte extended - // length field. - if (length == 1) { - length = ModuleBase.readSignedLong(dstream, true, null); - headerLength = 16; - } - bytesLeft = length - headerLength; - } + public Box(DataInputStream stream) { + this.dstream = stream; + } + /** Reads a box header and sets up for reading contents. */ + public void read() throws IOException { + length = ModuleBase.readUnsignedInt(dstream, ENDIAN, null); + long headerLength = 8; + type = read4Chars(); + // If the length field is 1, there is an 8-byte extended + // length field. + if (length == 1) { + length = ModuleBase.readSignedLong(dstream, true, null); + headerLength = 16; + } + bytesLeft = length - headerLength; + } - /* Reads a 4-character name */ - private String read4Chars() throws IOException { - StringBuffer sbuf = new StringBuffer(4); - for (int i = 0; i < 4; i++) { - int ch = ModuleBase.readUnsignedByte(dstream, null); - sbuf.append((char) ch); - } - return sbuf.toString(); - } + /* Reads a 4-character name */ + private String read4Chars() throws IOException { + StringBuffer sbuf = new StringBuffer(4); + for (int i = 0; i < 4; i++) { + int ch = ModuleBase.readUnsignedByte(dstream, null); + sbuf.append((char) ch); + } + return sbuf.toString(); + } - /** Returns true if this box contains other boxes. - * At present, we don't deal with the insides of boxes - * that contain both data and boxes (e.g., cref). */ - public boolean isSuperbox () - { - // If it's a known superbox type, we return true. - // If we've left any out, that will merely make us - // lose the subboxes of that type. - String [] supertypes = { "asoc", "cgrp", "comp", "drep", - "ftbl", "jp2h", "jpch", - "jplh", "res ", "uuid" }; - for (int i = 0; i < supertypes.length; i++) { - if (supertypes[i].equals (type)) { - return true; - } - } - return false; + /** + * Returns true if this box contains other boxes. At present, we don't deal with the insides of + * boxes that contain both data and boxes (e.g., cref). + */ + public boolean isSuperbox() { + // If it's a known superbox type, we return true. + // If we've left any out, that will merely make us + // lose the subboxes of that type. + String[] supertypes = { + "asoc", "cgrp", "comp", "drep", "ftbl", "jp2h", "jpch", "jplh", "res ", "uuid" + }; + for (int i = 0; i < supertypes.length; i++) { + if (supertypes[i].equals(type)) { + return true; } + } + return false; } - - + } } diff --git a/jhove-modules/jpeg2000-hul/src/main/java/edu/harvard/hul/ois/jhove/module/Jpeg2000Module.java b/jhove-modules/jpeg2000-hul/src/main/java/edu/harvard/hul/ois/jhove/module/Jpeg2000Module.java index 8383dd7d0..036b224eb 100644 --- a/jhove-modules/jpeg2000-hul/src/main/java/edu/harvard/hul/ois/jhove/module/Jpeg2000Module.java +++ b/jhove-modules/jpeg2000-hul/src/main/java/edu/harvard/hul/ois/jhove/module/Jpeg2000Module.java @@ -1,32 +1,22 @@ -/********************************************************************** - * Jhove - JSTOR/Harvard Object Validation Environment Copyright 2004-2007 by - * JSTOR and the President and Fellows of Harvard College +/** + * ******************************************************************** Jhove - JSTOR/Harvard Object + * Validation Environment Copyright 2004-2007 by JSTOR and the President and Fellows of Harvard + * College * - * This program 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 2 of the License, or (at your option) any - * later version. + *

This program 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 2 + * of the License, or (at your option) any later version. * - * This program 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. + *

This program 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. * - * You should have received a copy of the GNU Lesser General Public License - * along with this program; if not, write to the Free Software Foundation, Inc., - * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - **********************************************************************/ - + *

You should have received a copy of the GNU Lesser General Public License along with this + * program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA ******************************************************************** + */ package edu.harvard.hul.ois.jhove.module; -import java.io.DataInputStream; -import java.io.IOException; -import java.io.RandomAccessFile; -import java.util.ArrayList; -import java.util.LinkedList; -import java.util.List; -import java.util.ListIterator; - import edu.harvard.hul.ois.jhove.Agent; import edu.harvard.hul.ois.jhove.Agent.Builder; import edu.harvard.hul.ois.jhove.AgentType; @@ -53,767 +43,751 @@ import edu.harvard.hul.ois.jhove.module.jpeg2000.JP2Box; import edu.harvard.hul.ois.jhove.module.jpeg2000.MessageConstants; import edu.harvard.hul.ois.jhove.module.jpeg2000.TopLevelBoxHolder; +import java.io.DataInputStream; +import java.io.IOException; +import java.io.RandomAccessFile; +import java.util.ArrayList; +import java.util.LinkedList; +import java.util.List; +import java.util.ListIterator; /** * Module for identification and validation of JPEG 2000 files. * - * Code is included for JPX, but is almost entirely untested due to the lack of - * available sample files that use more than a tiny fraction of the features. - * The current version of the module is based on the typo-laden and inconsisent - * "JPEG 2000 Part II Final Committee Draft" (7 December 2000). The final - * standard (5 May 2004) has just reached our hands, and this code will be - * reviewed against it and revised accordingly in the near future. (All opinions - * expressed in this paragraph are those of the programmer.) + *

Code is included for JPX, but is almost entirely untested due to the lack of available sample + * files that use more than a tiny fraction of the features. The current version of the module is + * based on the typo-laden and inconsisent "JPEG 2000 Part II Final Committee Draft" (7 December + * 2000). The final standard (5 May 2004) has just reached our hands, and this code will be reviewed + * against it and revised accordingly in the near future. (All opinions expressed in this paragraph + * are those of the programmer.) * - * JPEG 2000 format is not JPEG format, and isn't compatible with it. As with - * JPEG, JPEG 2000 is not in itself a file format. It can be encapsulated in JP2 - * or JPX format, which are recognized here. + *

JPEG 2000 format is not JPEG format, and isn't compatible with it. As with JPEG, JPEG 2000 is + * not in itself a file format. It can be encapsulated in JP2 or JPX format, which are recognized + * here. * * @author Gary McGath */ public class Jpeg2000Module extends ModuleBase { - /* - * Some general notes on JPEG 2000 parsing: The format started out as a - * straightforward stream format, which could be parsed as a stream from - * beginning to end. But JPX throws us several curves, requiring random - * access. The Cross Reference Box is a direction to replace itself with - * another box somewhere else in the file (or even in one or more entirely - * different files!), which may be scattered through different parts of the - * file even if it doesn't spread to other files. The Fragment Table Box - * does the same for codestreams. In addition, the Binary Filter Box allows - * boxes to be hidden within its compressed or encrypted data. All this - * makes the concept of "parent box" trickier than it is in unextended JP2, - * since parenthood and containment are not necessarily the same thing. - * - * The way I've chosen deal with this rather chimerical design is to have a - * RandomAccessFile for a base, but have each Box based on a - * DataInputStream. Not every Box has its own separate stream -- that would - * be wasteful -- but there can be multiple box streams active at once. Each - * box (JP2Box) has a parent (BoxHolder), which can be another JP2Box or a - * TopLevelBoxHolder. The extension to multiple files isn't supported here, - * since the job of Jhove is to validate individual files, but it wouldn't - * be difficult to add. - * - * Every BoxHolder (hence every box) implements the Iterator interface, so - * that a box can get its subboxes in a way which is blind to the details of - * their encoding. - */ - - /****************************************************************** - * PRIVATE CLASS FIELDS. - ******************************************************************/ - - private static final String NAME = "JPEG2000-hul"; - private static final String RELEASE = "1.4.2"; - private static final int [] DATE = { 2019, 10, 18 }; - private static final String[] FORMAT = { "JPEG 2000", "JP2", "JPX" }; - private static final String COVERAGE = "JP2 (ISO/IEC 15444-1:2000/" - + "ITU-T Rec. T.800 (200)), JPX (ISO/IEC 15444-2:2004)"; - private static final String[] MIMETYPE = { "image/jp2", "image/jpx" }; - private static final String WELLFORMED = "The required Signature and File Type box structures are the first " - + "two boxes in the file; all boxes required by a given profile exist " - + "in the file; all box structures are well-formed (a four byte " - + "unsigned integer Box Length, followed by a four byte unsigned " - + "integer Box type, followed by a eight byte unsigned integer Box " - + "Length, followed by the Box Contents); no data exist before the " - + "first byte of the first box or after the last byte of the last box"; - private static final String VALIDITY = "The file is well-formed"; - private static final String REPINFO = "Properties capturing the technical attributes of the JPEG 2000 " - + "image from all boxes"; - private static final String NOTE = null; - private static final String RIGHTS = "Copyright 2004-2007 by JSTOR and the " - + "President and Fellows of Harvard College. " - + "Released under the GNU Lesser General Public License."; - - /****************************************************************** - * PRIVATE INSTANCE FIELDS. - ******************************************************************/ - - /* NISO image metadata for the current image */ - protected NisoImageMetadata _niso; - - /* NISO image metadata for default image values */ - protected NisoImageMetadata _defaultNiso; - - /* RandomAccessFile for reading the file */ - protected RandomAccessFile _raf; - - /* RAFInputStream underlying the DataInputStream */ - protected RAFInputStream _rafStream; - - /* Properties which are global to the file */ - protected List _propList; - - /* - * List of codestreams. An entry can be created by either a codestream or a - * codestream header, depending on which is seen first. The elements of the - * List are Codestream objects. - */ - protected List codestreams; - - /* List of Binary Filter properties. */ - protected List binaryFilterProps; - - /* List of Association properties. */ - protected List associationProps; - - /* List of Digital Signature properties. */ - protected List digitalSigProps; - - /* - * Number of Contiguous Codestreams seen. May be less than or equal to the - * size of codestreams. - */ - protected int nCodestreams; - - /* Number of Codestream headers seen */ - protected int nCodestreamHeaders; - - /* List of Color Spec properties */ - protected List colorSpecs; - - /* List of UUIDBox properties */ - protected List uuids; - - /* List of Compositing Layer properties */ - protected List composLayers; - - /* List of UUID Info properties */ - protected List uuidInfos; - - /* List of data (String) extracted from XML boxes */ - protected List xmlList; - - /* Flag for JP2 headerbox detection */ - protected boolean jp2HdrSeen; - - /* Flag for Reader Requirements detection */ - protected boolean rreqSeen; - - /* Flag for Color Specification detection */ - protected boolean colorSpecSeen; - - /* Flag for Image Header Box detection */ - protected boolean imageHeaderSeen; - - /* Flag for JP2 compliance */ - protected boolean jp2Compliant; - - /* Flag for JPX compliance */ - protected boolean jpxCompliant; - - /* - * The Codestream currently being worked on. This is an element of - * codestreams - */ - protected Codestream curCodestream; - - /* - * Flag which is true when we are reading a BinaryFilterBox, which needs - * special handling for byte counts. - */ - protected boolean filterMode; - - /* Fixed value for first 12 bytes */ - private static final int[] sigByte = { 0X00, 0X00, 0X00, 0X0C, 0X6A, 0X50, - 0X20, 0X20, 0X0D, 0X0A, 0X87, 0X0A }; - - /****************************************************************** - * CLASS CONSTRUCTOR. - ******************************************************************/ - /** - * Instantiate a JpegModule object. - */ - public Jpeg2000Module() { - super(NAME, RELEASE, DATE, FORMAT, COVERAGE, MIMETYPE, WELLFORMED, - VALIDITY, REPINFO, NOTE, RIGHTS, true); - - _vendor = Agent.harvardInstance(); - - Document doc = new Document( - "Information technology -- " - + "JPEG 2000 image coding system -- Part 1: Code coding system", - DocumentType.STANDARD); - Agent isoAgent = Agent.newIsoInstance(); - doc.setAuthor(isoAgent); - doc.setIdentifier(new Identifier("ISO/IEC 15444-1:2000", - IdentifierType.ISO)); - doc.setDate("2002-07-31"); - _specification.add(doc); - - doc = new Document("Information technology -- " - + "JPEG 2000 image coding system -- " + "Part 2: Extensions", - DocumentType.STANDARD); - doc.setAuthor(isoAgent); // ISO agent - doc.setIdentifier(new Identifier("ISO/IEC 15444-2:2004", - IdentifierType.ISO)); - doc.setDate("2004-05-15"); - _specification.add(doc); - - doc = new Document("MIME Type Registrations for JPEG 2000 " - + "(ISO/IEC 15444) RFC 3745", DocumentType.RFC); - Agent ietfAgent = new Agent.Builder("IETF", AgentType.STANDARD).web( - "http://www.ietf.org").build(); - doc.setPublisher(ietfAgent); - Agent agent = new Agent.Builder("D. Singer", AgentType.OTHER).build(); - doc.setAuthor(agent); - agent = new Agent.Builder("R. Clark", AgentType.OTHER).build(); - doc.setAuthor(agent); - agent = new Agent.Builder("D. Lee", AgentType.OTHER).build(); - doc.setAuthor(agent); - doc.setDate("2004-04"); - Identifier ident = new Identifier( - "http://www.ietf.org/rfc/rfc3745.txt", IdentifierType.URL); - doc.setIdentifier(ident); - _specification.add(doc); - - doc = new Document("ITU-T Rec. T.800 (2002), Information " + /* + * Some general notes on JPEG 2000 parsing: The format started out as a + * straightforward stream format, which could be parsed as a stream from + * beginning to end. But JPX throws us several curves, requiring random + * access. The Cross Reference Box is a direction to replace itself with + * another box somewhere else in the file (or even in one or more entirely + * different files!), which may be scattered through different parts of the + * file even if it doesn't spread to other files. The Fragment Table Box + * does the same for codestreams. In addition, the Binary Filter Box allows + * boxes to be hidden within its compressed or encrypted data. All this + * makes the concept of "parent box" trickier than it is in unextended JP2, + * since parenthood and containment are not necessarily the same thing. + * + * The way I've chosen deal with this rather chimerical design is to have a + * RandomAccessFile for a base, but have each Box based on a + * DataInputStream. Not every Box has its own separate stream -- that would + * be wasteful -- but there can be multiple box streams active at once. Each + * box (JP2Box) has a parent (BoxHolder), which can be another JP2Box or a + * TopLevelBoxHolder. The extension to multiple files isn't supported here, + * since the job of Jhove is to validate individual files, but it wouldn't + * be difficult to add. + * + * Every BoxHolder (hence every box) implements the Iterator interface, so + * that a box can get its subboxes in a way which is blind to the details of + * their encoding. + */ + + /** + * **************************************************************** PRIVATE CLASS FIELDS. + * **************************************************************** + */ + private static final String NAME = "JPEG2000-hul"; + + private static final String RELEASE = "1.4.2"; + private static final int[] DATE = {2019, 10, 18}; + private static final String[] FORMAT = {"JPEG 2000", "JP2", "JPX"}; + private static final String COVERAGE = + "JP2 (ISO/IEC 15444-1:2000/" + "ITU-T Rec. T.800 (200)), JPX (ISO/IEC 15444-2:2004)"; + private static final String[] MIMETYPE = {"image/jp2", "image/jpx"}; + private static final String WELLFORMED = + "The required Signature and File Type box structures are the first " + + "two boxes in the file; all boxes required by a given profile exist " + + "in the file; all box structures are well-formed (a four byte " + + "unsigned integer Box Length, followed by a four byte unsigned " + + "integer Box type, followed by a eight byte unsigned integer Box " + + "Length, followed by the Box Contents); no data exist before the " + + "first byte of the first box or after the last byte of the last box"; + private static final String VALIDITY = "The file is well-formed"; + private static final String REPINFO = + "Properties capturing the technical attributes of the JPEG 2000 " + "image from all boxes"; + private static final String NOTE = null; + private static final String RIGHTS = + "Copyright 2004-2007 by JSTOR and the " + + "President and Fellows of Harvard College. " + + "Released under the GNU Lesser General Public License."; + + /** + * **************************************************************** PRIVATE INSTANCE FIELDS. + * **************************************************************** + */ + + /* NISO image metadata for the current image */ + protected NisoImageMetadata _niso; + + /* NISO image metadata for default image values */ + protected NisoImageMetadata _defaultNiso; + + /* RandomAccessFile for reading the file */ + protected RandomAccessFile _raf; + + /* RAFInputStream underlying the DataInputStream */ + protected RAFInputStream _rafStream; + + /* Properties which are global to the file */ + protected List _propList; + + /* + * List of codestreams. An entry can be created by either a codestream or a + * codestream header, depending on which is seen first. The elements of the + * List are Codestream objects. + */ + protected List codestreams; + + /* List of Binary Filter properties. */ + protected List binaryFilterProps; + + /* List of Association properties. */ + protected List associationProps; + + /* List of Digital Signature properties. */ + protected List digitalSigProps; + + /* + * Number of Contiguous Codestreams seen. May be less than or equal to the + * size of codestreams. + */ + protected int nCodestreams; + + /* Number of Codestream headers seen */ + protected int nCodestreamHeaders; + + /* List of Color Spec properties */ + protected List colorSpecs; + + /* List of UUIDBox properties */ + protected List uuids; + + /* List of Compositing Layer properties */ + protected List composLayers; + + /* List of UUID Info properties */ + protected List uuidInfos; + + /* List of data (String) extracted from XML boxes */ + protected List xmlList; + + /* Flag for JP2 headerbox detection */ + protected boolean jp2HdrSeen; + + /* Flag for Reader Requirements detection */ + protected boolean rreqSeen; + + /* Flag for Color Specification detection */ + protected boolean colorSpecSeen; + + /* Flag for Image Header Box detection */ + protected boolean imageHeaderSeen; + + /* Flag for JP2 compliance */ + protected boolean jp2Compliant; + + /* Flag for JPX compliance */ + protected boolean jpxCompliant; + + /* + * The Codestream currently being worked on. This is an element of + * codestreams + */ + protected Codestream curCodestream; + + /* + * Flag which is true when we are reading a BinaryFilterBox, which needs + * special handling for byte counts. + */ + protected boolean filterMode; + + /* Fixed value for first 12 bytes */ + private static final int[] sigByte = { + 0X00, 0X00, 0X00, 0X0C, 0X6A, 0X50, 0X20, 0X20, 0X0D, 0X0A, 0X87, 0X0A + }; + + /** + * **************************************************************** CLASS CONSTRUCTOR. + * **************************************************************** + */ + /** Instantiate a JpegModule object. */ + public Jpeg2000Module() { + super( + NAME, + RELEASE, + DATE, + FORMAT, + COVERAGE, + MIMETYPE, + WELLFORMED, + VALIDITY, + REPINFO, + NOTE, + RIGHTS, + true); + + _vendor = Agent.harvardInstance(); + + Document doc = + new Document( + "Information technology -- " + + "JPEG 2000 image coding system -- Part 1: Code coding system", + DocumentType.STANDARD); + Agent isoAgent = Agent.newIsoInstance(); + doc.setAuthor(isoAgent); + doc.setIdentifier(new Identifier("ISO/IEC 15444-1:2000", IdentifierType.ISO)); + doc.setDate("2002-07-31"); + _specification.add(doc); + + doc = + new Document( + "Information technology -- " + + "JPEG 2000 image coding system -- " + + "Part 2: Extensions", + DocumentType.STANDARD); + doc.setAuthor(isoAgent); // ISO agent + doc.setIdentifier(new Identifier("ISO/IEC 15444-2:2004", IdentifierType.ISO)); + doc.setDate("2004-05-15"); + _specification.add(doc); + + doc = + new Document( + "MIME Type Registrations for JPEG 2000 " + "(ISO/IEC 15444) RFC 3745", + DocumentType.RFC); + Agent ietfAgent = + new Agent.Builder("IETF", AgentType.STANDARD).web("http://www.ietf.org").build(); + doc.setPublisher(ietfAgent); + Agent agent = new Agent.Builder("D. Singer", AgentType.OTHER).build(); + doc.setAuthor(agent); + agent = new Agent.Builder("R. Clark", AgentType.OTHER).build(); + doc.setAuthor(agent); + agent = new Agent.Builder("D. Lee", AgentType.OTHER).build(); + doc.setAuthor(agent); + doc.setDate("2004-04"); + Identifier ident = new Identifier("http://www.ietf.org/rfc/rfc3745.txt", IdentifierType.URL); + doc.setIdentifier(ident); + _specification.add(doc); + + doc = + new Document( + "ITU-T Rec. T.800 (2002), Information " + "technology -- JPEG 2000 image coding system: " - + "Core coding system", DocumentType.STANDARD); - Builder ituBuilder = new Agent.Builder("ITU", AgentType.STANDARD) - .address( - "ITU, Place des Nations, " - + "CH-1211 Geneva 20 Switzerland") - .telephone("+41 22 730 51 11").fax("+41 22 730 6500") - .email("itumail@itu.int").web("http://www.itu.int/home/"); - doc.setAuthor(ituBuilder.build()); - doc.setDate("2002-08"); - ident = new Identifier("ITU-T Rec. T.800 (2002)", IdentifierType.ITU); - doc.setIdentifier(ident); - _specification.add(doc); - - Signature sig = new InternalSignature(sigByte, SignatureType.MAGIC, - SignatureUseType.MANDATORY, 0, ""); - _signature.add(sig); - - sig = new ExternalSignature(".jp2", SignatureType.EXTENSION, - SignatureUseType.OPTIONAL); - _signature.add(sig); - - sig = new ExternalSignature(".jpx", SignatureType.EXTENSION, - SignatureUseType.OPTIONAL); - _signature.add(sig); - - sig = new ExternalSignature(".jpf", SignatureType.EXTENSION, - SignatureUseType.OPTIONAL); - _signature.add(sig); - - // Macintosh signature for JP2 files - sig = new ExternalSignature("jp2 ", SignatureType.FILETYPE, - SignatureUseType.OPTIONAL); - _signature.add(sig); - - // Macintosh signature for JPX files - sig = new ExternalSignature("jpx ", SignatureType.FILETYPE, - SignatureUseType.OPTIONAL); - _signature.add(sig); - - _bigEndian = true; - } - - /** - * Parse the content of a stream digital object and store the results in - * RepInfo. - * - * This module is based on a RandomAccessFile because of the requirements of - * the (so far) rarely used fragmented codestream feature. Since just about - * everything else can be done with an InputStream, we use a RAFInputStream - * except on the occasions when random access is needed. We pass the module - * as the counted argment to all read calls, so that we can - * compute relative positions in the stream based on _nByte. - * - * @param raf - * A RandomAccessFile to be parsed. - * - * @param info - * A fresh (on the first call) RepInfo object which will be - * modified to reflect the results of the parsing If multiple - * calls to parse are made on the basis of a nonzero - * value being returned, the same RepInfo object should be passed - * with each call. - */ - @Override - public final void parse(RandomAccessFile raf, RepInfo info) - throws IOException { - initParse(); - _rafStream = new RAFInputStream(raf, _je != null ? _je.getBufferSize() - : 0); - _dstream = new DataInputStream(_rafStream); - info.setFormat(_format[0]); - info.setMimeType(_mimeType[0]); - info.setModule(this); - - _propList = new ArrayList<>(12); - Property metadata = new Property("JPEG2000Metadata", - PropertyType.PROPERTY, PropertyArity.LIST, _propList); - - _raf = raf; - - // A JPEG 2000 file consists of a series of "boxes." - // The signature box must match the signature bytes. - int i = 0; - boolean badhdr = false; - try { - for (i = 0; i < 12; i++) { - int ch; - ch = readUnsignedByte(_dstream, this); - if (ch != sigByte[i]) { - badhdr = true; - break; - } - } - } catch (IOException e) { - badhdr = true; - } - if (badhdr) { - info.setMessage(new - ErrorMessage(MessageConstants.JPEG2000_HUL_35, i)); - info.setWellFormed(false); - return; - } - - /* If we got this far, take note that the signature is OK. */ - info.setSigMatch(_name); - - // Next check the file type box. - if (!readFileTypeBox(info)) { - return; - } - - // Go through the rest of the boxes. - if (!readBoxes(info)) { - return; - } - - if (info.getWellFormed() == RepInfo.FALSE) { - return; - } - - // File has been read successfully; do final processing. - - info.setProperty(metadata); - - // Calculate checksums, if necessary. - // Calculate checksums if not already present - checksumIfRafNotCopied(info, raf); - - // Reader Requirements box is mandatory for JPX - if (!rreqSeen || info.getValid() != RepInfo.TRUE) { - jpxCompliant = false; - } - if (!imageHeaderSeen || !colorSpecSeen - || info.getValid() != RepInfo.TRUE) { - jp2Compliant = false; - } - if (jp2Compliant) { - info.setProfile("JP2"); - } - if (jpxCompliant) { - info.setProfile("JPX"); - String mime = _mimeType[1]; - info.setMimeType(mime); - curCodestream.getNiso().setMimeType(mime); - // This doesn't deal with a case where some - // codestreams are JP2 and others are JPX. - // Can that happen? - _defaultNiso.setMimeType(mime); - } - - // Calculate the compression level - NisoImageMetadata niso = getCurrentNiso(); - int rate = calculateRatio(getFilePos(), // fileSize - niso.getBitsPerSample(), niso.getImageLength(), niso.getImageWidth()); - if (rate != -1) { - niso.setCompressionLevel(rate); - } - - if (!colorSpecs.isEmpty()) { - _propList.add(new Property("ColorSpecs", PropertyType.PROPERTY, - PropertyArity.LIST, colorSpecs)); - } - if (!binaryFilterProps.isEmpty()) { - _propList.add(new Property("BinaryFilters", PropertyType.PROPERTY, - PropertyArity.LIST, binaryFilterProps)); - } - if (!associationProps.isEmpty()) { - _propList.add(new Property("Associations", PropertyType.PROPERTY, - PropertyArity.LIST, associationProps)); - } - - if (!digitalSigProps.isEmpty()) { - _propList - .add(new Property("DigitalSignatures", - PropertyType.PROPERTY, PropertyArity.LIST, - digitalSigProps)); - } - if (!uuids.isEmpty()) { - _propList.add(new Property("UUIDs", PropertyType.PROPERTY, - PropertyArity.LIST, uuids)); - } - if (!composLayers.isEmpty()) { - _propList.add(new Property("CompositingLayers", - PropertyType.PROPERTY, PropertyArity.LIST, composLayers)); - } - if (!uuidInfos.isEmpty()) { - _propList.add(new Property("UUIDInfoBoxes", PropertyType.PROPERTY, - PropertyArity.LIST, uuidInfos)); - } - if (!codestreams.isEmpty()) { - List csProps = new ArrayList(codestreams.size()); - ListIterator csIter = codestreams.listIterator(); - while (csIter.hasNext()) { - Codestream cs = csIter.next(); - csProps.add(cs.makeProperty()); - } - _propList.add(new Property("Codestreams", PropertyType.PROPERTY, - PropertyArity.LIST, csProps)); - - } - if (!xmlList.isEmpty()) { - _propList.add(new Property("XML", PropertyType.STRING, - PropertyArity.LIST, xmlList)); - } - - return; - } - - /** - * Return the compression ratio - */ - public int calculateRatio(long fileSize, int[] bitsPerSample, long length, long width) { - final double BITS_IN_BYTE = 8.0; - long bits = 0; - for (int bit:bitsPerSample) { - bits += bit; - } - double bytes = (double)bits / BITS_IN_BYTE; - double uncompressedSize = (double)length * (double)width * bytes; - if (fileSize <= 0) return -1; - double ratio = uncompressedSize / (double)fileSize; - return (int)Math.round(ratio); - } - - /** - * Return the current position in the module. This is somewhat - * optimistically named; it should be trusted only for relative positions - * when no seek operations occur in between calls to getFilePos. - */ - public long getFilePos() { - try { - return _rafStream.getFilePos(); - } catch (IOException e) { - // really shouldn't happen - return 0; - } - } - - /** Seek to a new file position. */ - public void seek(long pos) throws IOException { - _rafStream.seek(pos); - } - - /** Returns the default NisoImageMetadata object. */ - public NisoImageMetadata getDefaultNiso() { - return _defaultNiso; - } - - /** - * Returns the current NisoImageMetadata object. If curCodestream is null, - * returns the default NisoImageMetadata, otherwise returns the - * NisoImageMetadata of curCodestream. - */ - public NisoImageMetadata getCurrentNiso() { - if (curCodestream == null) { - return _defaultNiso; - } - return curCodestream.getNiso(); - } - - /** - * Returns the nth Codestream, creating it if necessary, and - * make it the current one. The value of nCodestreams or - * nCodestreamHeaders is not affected, even if a new - * Codestream is created. - */ - public Codestream getCodestream(int n) { - Codestream cs; - if (n < codestreams.size()) { - cs = codestreams.get(n); - } else { - cs = new Codestream(); - cs.setDefaultNiso(_defaultNiso); - codestreams.add(cs); - } - curCodestream = cs; - return cs; - } - - /** - * Returns the codestream count. Because items may be added to the - * codestreams list when only the header is seen, this may be less than the - * size of the codestreams list. It will never be more. - */ - public int getNCodestreams() { - return nCodestreams; - } - - /** Returns the codestream header count. */ - public int getNCodestreamHeaders() { - return nCodestreamHeaders; - } - - public boolean isJP2HdrSeen() { - return jp2HdrSeen; - } - - /** - * Sets the codestream count. This affects only the variable, not the size - * of the codestreams list. The codestream count should never be set to a - * value larger than the codestreams list; it signifies the number of - * elements of the list for which codestream boxes have actually been seen. - */ - public void setNCodestreams(int n) { - nCodestreams = n; - } - - /** Sets the codestream header count. */ - public void setNCodestreamHeaders(int n) { - nCodestreamHeaders = n; - } - - /** - * Set the flag indicating that a JP2 header has been seen. - */ - public void setJP2HdrSeen(boolean b) { - jp2HdrSeen = b; - } - - /** - * Set the flag indicating the reader requirements box has been seen. - */ - public void setRReqSeen(boolean b) { - rreqSeen = b; - } - - /** - * Set the flag indicating the color specification box has been seen. - */ - public void setColorSpecSeen(boolean b) { - colorSpecSeen = b; - } - - /** - * Set the flag indicating the color specification box has been seen. - */ - public void setImageHeaderSeen(boolean b) { - imageHeaderSeen = b; - } - - /** - * Sets a flag indicating JP2 compliance. If the flag is set to - * true, and the JPX compliance flag is also true, set the MIME - * type to "image/jpx". - */ - public void setJP2Compliant(boolean b) { - jp2Compliant = b; - if (jp2Compliant && jpxCompliant) { - _defaultNiso.setMimeType(MIMETYPE[1]); + + "Core coding system", + DocumentType.STANDARD); + Builder ituBuilder = + new Agent.Builder("ITU", AgentType.STANDARD) + .address("ITU, Place des Nations, " + "CH-1211 Geneva 20 Switzerland") + .telephone("+41 22 730 51 11") + .fax("+41 22 730 6500") + .email("itumail@itu.int") + .web("http://www.itu.int/home/"); + doc.setAuthor(ituBuilder.build()); + doc.setDate("2002-08"); + ident = new Identifier("ITU-T Rec. T.800 (2002)", IdentifierType.ITU); + doc.setIdentifier(ident); + _specification.add(doc); + + Signature sig = + new InternalSignature(sigByte, SignatureType.MAGIC, SignatureUseType.MANDATORY, 0, ""); + _signature.add(sig); + + sig = new ExternalSignature(".jp2", SignatureType.EXTENSION, SignatureUseType.OPTIONAL); + _signature.add(sig); + + sig = new ExternalSignature(".jpx", SignatureType.EXTENSION, SignatureUseType.OPTIONAL); + _signature.add(sig); + + sig = new ExternalSignature(".jpf", SignatureType.EXTENSION, SignatureUseType.OPTIONAL); + _signature.add(sig); + + // Macintosh signature for JP2 files + sig = new ExternalSignature("jp2 ", SignatureType.FILETYPE, SignatureUseType.OPTIONAL); + _signature.add(sig); + + // Macintosh signature for JPX files + sig = new ExternalSignature("jpx ", SignatureType.FILETYPE, SignatureUseType.OPTIONAL); + _signature.add(sig); + + _bigEndian = true; + } + + /** + * Parse the content of a stream digital object and store the results in RepInfo. + * + *

This module is based on a RandomAccessFile because of the requirements of the (so far) + * rarely used fragmented codestream feature. Since just about everything else can be done with an + * InputStream, we use a RAFInputStream except on the occasions when random access is needed. We + * pass the module as the counted argment to all read calls, so that we can compute + * relative positions in the stream based on _nByte. + * + * @param raf A RandomAccessFile to be parsed. + * @param info A fresh (on the first call) RepInfo object which will be modified to reflect the + * results of the parsing If multiple calls to parse are made on the basis of a + * nonzero value being returned, the same RepInfo object should be passed with each call. + */ + @Override + public final void parse(RandomAccessFile raf, RepInfo info) throws IOException { + initParse(); + _rafStream = new RAFInputStream(raf, _je != null ? _je.getBufferSize() : 0); + _dstream = new DataInputStream(_rafStream); + info.setFormat(_format[0]); + info.setMimeType(_mimeType[0]); + info.setModule(this); + + _propList = new ArrayList<>(12); + Property metadata = + new Property("JPEG2000Metadata", PropertyType.PROPERTY, PropertyArity.LIST, _propList); + + _raf = raf; + + // A JPEG 2000 file consists of a series of "boxes." + // The signature box must match the signature bytes. + int i = 0; + boolean badhdr = false; + try { + for (i = 0; i < 12; i++) { + int ch; + ch = readUnsignedByte(_dstream, this); + if (ch != sigByte[i]) { + badhdr = true; + break; } + } + } catch (IOException e) { + badhdr = true; } - - /** Sets a flag indicating JPX compliance. */ - public void setJPXCompliant(boolean b) { - jpxCompliant = b; - } - - /** Adds a property to the JPEG2000 metadata. */ - public void addProperty(Property p) { - _propList.add(p); - } - - /** Adds a color spec property to the metadata. */ - public void addColorSpec(Property p) { - colorSpecs.add(p); - } - - /** Adds a binary filter property to the metadata. */ - public void addBinaryFilterProp(Property p) { - binaryFilterProps.add(p); - } - - /** Adds an association property to the metadata. */ - public void addAssociationProp(Property p) { - associationProps.add(p); - } - - /** Adds a digital signature property to the metadata. */ - public void addDigitalSignatureProp(Property p) { - digitalSigProps.add(p); - } - - /** - * Adds a UUID property to the list of UUID properties. Called from the - * UUIDBox. - */ - public void addUUID(Property p) { - uuids.add(p); - } - - /** - * Adds a UUIDInfo property to the list of UUIDInfo properties. Called from - * UUIDInfoBox. - */ - public void addUUIDInfo(Property p) { - uuidInfos.add(p); + if (badhdr) { + info.setMessage(new ErrorMessage(MessageConstants.JPEG2000_HUL_35, i)); + info.setWellFormed(false); + return; } - /** - * Adds a Compositing Layer property to the list of Compositing Layer - * properties. Called from the ComposLayerHdrBox. - */ - public void addComposLayer(Property p) { - composLayers.add(p); - } + /* If we got this far, take note that the signature is OK. */ + info.setSigMatch(_name); - /** - * Adds an XML string to the list of XML properties. Called from XMLBox. - */ - public void addXML(String s) { - xmlList.add(s); + // Next check the file type box. + if (!readFileTypeBox(info)) { + return; } - /** - * Reads 4 bytes and concatenates them into a String. - */ - public String read4Chars(DataInputStream stream) throws IOException { - StringBuffer sbuf = new StringBuffer(4); - for (int i = 0; i < 4; i++) { - int ch = readUnsignedByte(stream, this); - sbuf.append((char) ch); - } - return sbuf.toString(); + // Go through the rest of the boxes. + if (!readBoxes(info)) { + return; } - /** - * One-argument version of readUnsignedShort. JPEG2000 is - * always big-endian, so readUnsignedShort can unambiguously drop its endian - * argument. - */ - public int readUnsignedShort(DataInputStream stream) throws IOException { - return readUnsignedShort(stream, true, this); + if (info.getWellFormed() == RepInfo.FALSE) { + return; } - /** - * One-argument version of readUnsignedInt. JPEG2000 is always - * big-endian, so readUnsignedInt can unambiguously drop its endian - * argument. - */ - public long readUnsignedInt(DataInputStream stream) throws IOException { - return readUnsignedInt(stream, true, this); - } - - /** - * One-argument version of readSignedLong. JPEG2000 is always - * big-endian, so readSignedLong can unambiguously drop its endian argument. - */ - public long readSignedLong(DataInputStream stream) throws IOException { - return readSignedLong(stream, true, this); - } + // File has been read successfully; do final processing. - /** - * Initializes the state of the module for parsing. - */ - @Override - protected void initParse() { - super.initParse(); - colorSpecs = new LinkedList(); - binaryFilterProps = new LinkedList(); - associationProps = new LinkedList(); - digitalSigProps = new LinkedList(); - uuids = new LinkedList(); - uuidInfos = new LinkedList(); - composLayers = new LinkedList(); - xmlList = new LinkedList(); - codestreams = new LinkedList(); - curCodestream = null; - nCodestreams = 0; - nCodestreamHeaders = 0; - jp2HdrSeen = false; - rreqSeen = false; - filterMode = false; - _defaultNiso = new NisoImageMetadata(); - _defaultNiso.setByteOrder("big-endian"); - _defaultNiso.setMimeType(MIMETYPE[0]); - - // Compliance flags are innocent till proven guilty - jp2Compliant = true; - jpxCompliant = true; - } + info.setProperty(metadata); - /* - * Dispatcher for reading boxes. Because of the existence of filter boxes, - * any box can come from either _dstream or from a separate input stream - * that is being undeflated. This dispatcher always works from _dstream. We - * must pass a stream to every box function, to allow these multiple - * sources. - */ - protected boolean readBoxes(RepInfo info) throws IOException { - // From here on, boxes may occur with some freedom - // of order. Apparently the only indication that - // we're done is an end-of-file condition. - - TopLevelBoxHolder bh = new TopLevelBoxHolder(this, _raf, info, _dstream); - while (bh.hasNext()) { - JP2Box box = (JP2Box) bh.next(); - // TopLevelBoxHolder.next() may not be reliable about detecting - // that no more boxes are left. - if (box == null) { - break; - } - if (!box.readBox()) { - return false; - } - } + // Calculate checksums, if necessary. + // Calculate checksums if not already present + checksumIfRafNotCopied(info, raf); - return true; + // Reader Requirements box is mandatory for JPX + if (!rreqSeen || info.getValid() != RepInfo.TRUE) { + jpxCompliant = false; } - - /* - * Read the file type box. This assumes that we are actually positioned at - * the file type box, and reads its header. If we get something other than a - * file type box, the file is not well-formed. - */ - protected boolean readFileTypeBox(RepInfo info) throws IOException { - BoxHeader hdr = new BoxHeader(this, _dstream); - hdr.readHeader(); - // 8 bytes have been read - if (!"ftyp".equals(hdr.getType())) { - info.setMessage(new - ErrorMessage(MessageConstants.JPEG2000_HUL_22, - hdr.getType(), _nByte)); - info.setWellFormed(false); - return false; - - } - FileTypeBox box = new FileTypeBox(_raf); - box.setBoxHeader(hdr); - box.setDataInputStream(_dstream); - box.setRandomAccessFile(_raf); - box.setModule(this); - box.setRepInfo(info); - return box.readBox(); + if (!imageHeaderSeen || !colorSpecSeen || info.getValid() != RepInfo.TRUE) { + jp2Compliant = false; + } + if (jp2Compliant) { + info.setProfile("JP2"); } - - /* - * This is called for any box that isn't recognized, or by placeholder - * methods for boxes that haven't yet been coded. - */ - protected boolean skipOverBox(BoxHeader hdr, RepInfo info, - DataInputStream dstrm) throws IOException { - if (hdr.getLength() != 0) { - skipBytes(dstrm, (int) hdr.getDataLength(), this); - } - return true; + if (jpxCompliant) { + info.setProfile("JPX"); + String mime = _mimeType[1]; + info.setMimeType(mime); + curCodestream.getNiso().setMimeType(mime); + // This doesn't deal with a case where some + // codestreams are JP2 and others are JPX. + // Can that happen? + _defaultNiso.setMimeType(mime); } + + // Calculate the compression level + NisoImageMetadata niso = getCurrentNiso(); + int rate = + calculateRatio( + getFilePos(), // fileSize + niso.getBitsPerSample(), + niso.getImageLength(), + niso.getImageWidth()); + if (rate != -1) { + niso.setCompressionLevel(rate); + } + + if (!colorSpecs.isEmpty()) { + _propList.add( + new Property("ColorSpecs", PropertyType.PROPERTY, PropertyArity.LIST, colorSpecs)); + } + if (!binaryFilterProps.isEmpty()) { + _propList.add( + new Property( + "BinaryFilters", PropertyType.PROPERTY, PropertyArity.LIST, binaryFilterProps)); + } + if (!associationProps.isEmpty()) { + _propList.add( + new Property( + "Associations", PropertyType.PROPERTY, PropertyArity.LIST, associationProps)); + } + + if (!digitalSigProps.isEmpty()) { + _propList.add( + new Property( + "DigitalSignatures", PropertyType.PROPERTY, PropertyArity.LIST, digitalSigProps)); + } + if (!uuids.isEmpty()) { + _propList.add(new Property("UUIDs", PropertyType.PROPERTY, PropertyArity.LIST, uuids)); + } + if (!composLayers.isEmpty()) { + _propList.add( + new Property( + "CompositingLayers", PropertyType.PROPERTY, PropertyArity.LIST, composLayers)); + } + if (!uuidInfos.isEmpty()) { + _propList.add( + new Property("UUIDInfoBoxes", PropertyType.PROPERTY, PropertyArity.LIST, uuidInfos)); + } + if (!codestreams.isEmpty()) { + List csProps = new ArrayList(codestreams.size()); + ListIterator csIter = codestreams.listIterator(); + while (csIter.hasNext()) { + Codestream cs = csIter.next(); + csProps.add(cs.makeProperty()); + } + _propList.add( + new Property("Codestreams", PropertyType.PROPERTY, PropertyArity.LIST, csProps)); + } + if (!xmlList.isEmpty()) { + _propList.add(new Property("XML", PropertyType.STRING, PropertyArity.LIST, xmlList)); + } + + return; + } + + /** Return the compression ratio */ + public int calculateRatio(long fileSize, int[] bitsPerSample, long length, long width) { + final double BITS_IN_BYTE = 8.0; + long bits = 0; + for (int bit : bitsPerSample) { + bits += bit; + } + double bytes = (double) bits / BITS_IN_BYTE; + double uncompressedSize = (double) length * (double) width * bytes; + if (fileSize <= 0) return -1; + double ratio = uncompressedSize / (double) fileSize; + return (int) Math.round(ratio); + } + + /** + * Return the current position in the module. This is somewhat optimistically named; it should be + * trusted only for relative positions when no seek operations occur in between calls to + * getFilePos. + */ + public long getFilePos() { + try { + return _rafStream.getFilePos(); + } catch (IOException e) { + // really shouldn't happen + return 0; + } + } + + /** Seek to a new file position. */ + public void seek(long pos) throws IOException { + _rafStream.seek(pos); + } + + /** Returns the default NisoImageMetadata object. */ + public NisoImageMetadata getDefaultNiso() { + return _defaultNiso; + } + + /** + * Returns the current NisoImageMetadata object. If curCodestream is null, returns the default + * NisoImageMetadata, otherwise returns the NisoImageMetadata of curCodestream. + */ + public NisoImageMetadata getCurrentNiso() { + if (curCodestream == null) { + return _defaultNiso; + } + return curCodestream.getNiso(); + } + + /** + * Returns the nth Codestream, creating it if necessary, and make it the current one. + * The value of nCodestreams or nCodestreamHeaders is not affected, even + * if a new Codestream is created. + */ + public Codestream getCodestream(int n) { + Codestream cs; + if (n < codestreams.size()) { + cs = codestreams.get(n); + } else { + cs = new Codestream(); + cs.setDefaultNiso(_defaultNiso); + codestreams.add(cs); + } + curCodestream = cs; + return cs; + } + + /** + * Returns the codestream count. Because items may be added to the codestreams list when only the + * header is seen, this may be less than the size of the codestreams list. It will never be more. + */ + public int getNCodestreams() { + return nCodestreams; + } + + /** Returns the codestream header count. */ + public int getNCodestreamHeaders() { + return nCodestreamHeaders; + } + + public boolean isJP2HdrSeen() { + return jp2HdrSeen; + } + + /** + * Sets the codestream count. This affects only the variable, not the size of the codestreams + * list. The codestream count should never be set to a value larger than the codestreams list; it + * signifies the number of elements of the list for which codestream boxes have actually been + * seen. + */ + public void setNCodestreams(int n) { + nCodestreams = n; + } + + /** Sets the codestream header count. */ + public void setNCodestreamHeaders(int n) { + nCodestreamHeaders = n; + } + + /** Set the flag indicating that a JP2 header has been seen. */ + public void setJP2HdrSeen(boolean b) { + jp2HdrSeen = b; + } + + /** Set the flag indicating the reader requirements box has been seen. */ + public void setRReqSeen(boolean b) { + rreqSeen = b; + } + + /** Set the flag indicating the color specification box has been seen. */ + public void setColorSpecSeen(boolean b) { + colorSpecSeen = b; + } + + /** Set the flag indicating the color specification box has been seen. */ + public void setImageHeaderSeen(boolean b) { + imageHeaderSeen = b; + } + + /** + * Sets a flag indicating JP2 compliance. If the flag is set to true, and the JPX + * compliance flag is also true, set the MIME type to "image/jpx". + */ + public void setJP2Compliant(boolean b) { + jp2Compliant = b; + if (jp2Compliant && jpxCompliant) { + _defaultNiso.setMimeType(MIMETYPE[1]); + } + } + + /** Sets a flag indicating JPX compliance. */ + public void setJPXCompliant(boolean b) { + jpxCompliant = b; + } + + /** Adds a property to the JPEG2000 metadata. */ + public void addProperty(Property p) { + _propList.add(p); + } + + /** Adds a color spec property to the metadata. */ + public void addColorSpec(Property p) { + colorSpecs.add(p); + } + + /** Adds a binary filter property to the metadata. */ + public void addBinaryFilterProp(Property p) { + binaryFilterProps.add(p); + } + + /** Adds an association property to the metadata. */ + public void addAssociationProp(Property p) { + associationProps.add(p); + } + + /** Adds a digital signature property to the metadata. */ + public void addDigitalSignatureProp(Property p) { + digitalSigProps.add(p); + } + + /** Adds a UUID property to the list of UUID properties. Called from the UUIDBox. */ + public void addUUID(Property p) { + uuids.add(p); + } + + /** Adds a UUIDInfo property to the list of UUIDInfo properties. Called from UUIDInfoBox. */ + public void addUUIDInfo(Property p) { + uuidInfos.add(p); + } + + /** + * Adds a Compositing Layer property to the list of Compositing Layer properties. Called from the + * ComposLayerHdrBox. + */ + public void addComposLayer(Property p) { + composLayers.add(p); + } + + /** Adds an XML string to the list of XML properties. Called from XMLBox. */ + public void addXML(String s) { + xmlList.add(s); + } + + /** Reads 4 bytes and concatenates them into a String. */ + public String read4Chars(DataInputStream stream) throws IOException { + StringBuffer sbuf = new StringBuffer(4); + for (int i = 0; i < 4; i++) { + int ch = readUnsignedByte(stream, this); + sbuf.append((char) ch); + } + return sbuf.toString(); + } + + /** + * One-argument version of readUnsignedShort. JPEG2000 is always big-endian, so + * readUnsignedShort can unambiguously drop its endian argument. + */ + public int readUnsignedShort(DataInputStream stream) throws IOException { + return readUnsignedShort(stream, true, this); + } + + /** + * One-argument version of readUnsignedInt. JPEG2000 is always big-endian, so + * readUnsignedInt can unambiguously drop its endian argument. + */ + public long readUnsignedInt(DataInputStream stream) throws IOException { + return readUnsignedInt(stream, true, this); + } + + /** + * One-argument version of readSignedLong. JPEG2000 is always big-endian, so + * readSignedLong can unambiguously drop its endian argument. + */ + public long readSignedLong(DataInputStream stream) throws IOException { + return readSignedLong(stream, true, this); + } + + /** Initializes the state of the module for parsing. */ + @Override + protected void initParse() { + super.initParse(); + colorSpecs = new LinkedList(); + binaryFilterProps = new LinkedList(); + associationProps = new LinkedList(); + digitalSigProps = new LinkedList(); + uuids = new LinkedList(); + uuidInfos = new LinkedList(); + composLayers = new LinkedList(); + xmlList = new LinkedList(); + codestreams = new LinkedList(); + curCodestream = null; + nCodestreams = 0; + nCodestreamHeaders = 0; + jp2HdrSeen = false; + rreqSeen = false; + filterMode = false; + _defaultNiso = new NisoImageMetadata(); + _defaultNiso.setByteOrder("big-endian"); + _defaultNiso.setMimeType(MIMETYPE[0]); + + // Compliance flags are innocent till proven guilty + jp2Compliant = true; + jpxCompliant = true; + } + + /* + * Dispatcher for reading boxes. Because of the existence of filter boxes, + * any box can come from either _dstream or from a separate input stream + * that is being undeflated. This dispatcher always works from _dstream. We + * must pass a stream to every box function, to allow these multiple + * sources. + */ + protected boolean readBoxes(RepInfo info) throws IOException { + // From here on, boxes may occur with some freedom + // of order. Apparently the only indication that + // we're done is an end-of-file condition. + + TopLevelBoxHolder bh = new TopLevelBoxHolder(this, _raf, info, _dstream); + while (bh.hasNext()) { + JP2Box box = (JP2Box) bh.next(); + // TopLevelBoxHolder.next() may not be reliable about detecting + // that no more boxes are left. + if (box == null) { + break; + } + if (!box.readBox()) { + return false; + } + } + + return true; + } + + /* + * Read the file type box. This assumes that we are actually positioned at + * the file type box, and reads its header. If we get something other than a + * file type box, the file is not well-formed. + */ + protected boolean readFileTypeBox(RepInfo info) throws IOException { + BoxHeader hdr = new BoxHeader(this, _dstream); + hdr.readHeader(); + // 8 bytes have been read + if (!"ftyp".equals(hdr.getType())) { + info.setMessage(new ErrorMessage(MessageConstants.JPEG2000_HUL_22, hdr.getType(), _nByte)); + info.setWellFormed(false); + return false; + } + FileTypeBox box = new FileTypeBox(_raf); + box.setBoxHeader(hdr); + box.setDataInputStream(_dstream); + box.setRandomAccessFile(_raf); + box.setModule(this); + box.setRepInfo(info); + return box.readBox(); + } + + /* + * This is called for any box that isn't recognized, or by placeholder + * methods for boxes that haven't yet been coded. + */ + protected boolean skipOverBox(BoxHeader hdr, RepInfo info, DataInputStream dstrm) + throws IOException { + if (hdr.getLength() != 0) { + skipBytes(dstrm, (int) hdr.getDataLength(), this); + } + return true; + } } diff --git a/jhove-modules/jpeg2000-hul/src/main/java/edu/harvard/hul/ois/jhove/module/jpeg2000/AssociationBox.java b/jhove-modules/jpeg2000-hul/src/main/java/edu/harvard/hul/ois/jhove/module/jpeg2000/AssociationBox.java index 64d2bd29e..f632cafc9 100644 --- a/jhove-modules/jpeg2000-hul/src/main/java/edu/harvard/hul/ois/jhove/module/jpeg2000/AssociationBox.java +++ b/jhove-modules/jpeg2000-hul/src/main/java/edu/harvard/hul/ois/jhove/module/jpeg2000/AssociationBox.java @@ -1,9 +1,9 @@ -/********************************************************************** - * Jhove - JSTOR/Harvard Object Validation Environment - * Copyright 2004 by JSTOR and the President and Fellows of Harvard College +/** + * ******************************************************************** Jhove - JSTOR/Harvard Object + * Validation Environment Copyright 2004 by JSTOR and the President and Fellows of Harvard College * - **********************************************************************/ - + *

******************************************************************** + */ package edu.harvard.hul.ois.jhove.module.jpeg2000; import edu.harvard.hul.ois.jhove.*; @@ -12,104 +12,88 @@ /** * Association Box. - * - * An AssociationBox can occur in pretty much any superbox - * or at the top level. It simply establishes an association - * between boxes. * - * See ISO/IEC FCD15444-2: 2000, L.9.11 - * - * @author Gary McGath + *

An AssociationBox can occur in pretty much any superbox or at the top level. It simply + * establishes an association between boxes. + * + *

See ISO/IEC FCD15444-2: 2000, L.9.11 * + * @author Gary McGath */ public class AssociationBox extends JP2Box { + /** + * Constructor with superbox. + * + * @param parent parent superbox of this box or TopLevelBoxHolder + */ + public AssociationBox(RandomAccessFile raf, BoxHolder parent) { + super(raf, parent); + } + /** + * Reads the box, putting appropriate information in the RepInfo object. setModule, setBoxHeader, + * setRepInfo and setDataInputStream must be called before readBox is called. + * readBox must completely consume the box, so that the next byte to be read by the + * DataInputStream is the FF byte of the next Box. + */ + @Override + public boolean readBox() throws IOException { + initBytesRead(); + hasBoxes = true; - /** - * Constructor with superbox. - * - * @param parent parent superbox of this box - * or TopLevelBoxHolder - */ - public AssociationBox(RandomAccessFile raf, BoxHolder parent) { - super(raf, parent); - } - - /** Reads the box, putting appropriate information in - * the RepInfo object. setModule, setBoxHeader, - * setRepInfo and setDataInputStream must be called - * before readBox is called. - * readBox must completely consume the - * box, so that the next byte to be read by the - * DataInputStream is the FF byte of the next Box. - */ - @Override - public boolean readBox() throws IOException { - initBytesRead (); - hasBoxes = true; + // Label and Number List boxes are given as examples, but + // there is actually no restriction; an Association Box + // can associate any arbitrary collection of boxes. + // In order to avoid doubling (squaring?) the complexity + // of the Module, only Image, Number List, Association and XML + // boxes are reported in detail. - // Label and Number List boxes are given as examples, but - // there is actually no restriction; an Association Box - // can associate any arbitrary collection of boxes. - // In order to avoid doubling (squaring?) the complexity - // of the Module, only Image, Number List, Association and XML - // boxes are reported in detail. - - JP2Box box = null; - List boxProps = new LinkedList (); - while (hasNext ()) { - box = (JP2Box) next (); - if (box == null) { - break; - } - if (!box.readBox ()) { - return false; - } - Property sdProp = box.selfDescProperty (); - if (sdProp != null) { - boxProps.add (sdProp); - } - } - // Hierarchically add any association properties to the - // property list. - if (!associations.isEmpty ()) { - boxProps.add (new Property ("Associations", - PropertyType.PROPERTY, - PropertyArity.LIST, - associations)); - } - Property assocProp = new Property - ("Association", - PropertyType.PROPERTY, - PropertyArity.LIST, - boxProps); - if (_parentBox != null) { - _parentBox.addAssociation (assocProp); - } - else { - _module.addAssociationProp (assocProp); - } - finalizeBytesRead (); - return true; + JP2Box box = null; + List boxProps = new LinkedList(); + while (hasNext()) { + box = (JP2Box) next(); + if (box == null) { + break; + } + if (!box.readBox()) { + return false; + } + Property sdProp = box.selfDescProperty(); + if (sdProp != null) { + boxProps.add(sdProp); + } } - - /** Returns a Property which describes the Box, for use - * by Association boxes and perhaps others. - * An Association box can recursively contain other - * Association boxes. Since an Association box adds - * Association properties to its ancestors, This just - * returns null to avoid duplicate reporting. - */ - @Override - protected Property selfDescProperty () { - return null; + // Hierarchically add any association properties to the + // property list. + if (!associations.isEmpty()) { + boxProps.add( + new Property("Associations", PropertyType.PROPERTY, PropertyArity.LIST, associations)); } - - /** Returns the name of the Box. */ - @Override - protected String getSelfPropName () - { - return "Association Box"; + Property assocProp = + new Property("Association", PropertyType.PROPERTY, PropertyArity.LIST, boxProps); + if (_parentBox != null) { + _parentBox.addAssociation(assocProp); + } else { + _module.addAssociationProp(assocProp); } + finalizeBytesRead(); + return true; + } + + /** + * Returns a Property which describes the Box, for use by Association boxes and perhaps others. An + * Association box can recursively contain other Association boxes. Since an Association box adds + * Association properties to its ancestors, This just returns null to avoid duplicate reporting. + */ + @Override + protected Property selfDescProperty() { + return null; + } + + /** Returns the name of the Box. */ + @Override + protected String getSelfPropName() { + return "Association Box"; + } } diff --git a/jhove-modules/jpeg2000-hul/src/main/java/edu/harvard/hul/ois/jhove/module/jpeg2000/BPCCBox.java b/jhove-modules/jpeg2000-hul/src/main/java/edu/harvard/hul/ois/jhove/module/jpeg2000/BPCCBox.java index f5d3157c4..1942eeffc 100644 --- a/jhove-modules/jpeg2000-hul/src/main/java/edu/harvard/hul/ois/jhove/module/jpeg2000/BPCCBox.java +++ b/jhove-modules/jpeg2000-hul/src/main/java/edu/harvard/hul/ois/jhove/module/jpeg2000/BPCCBox.java @@ -1,65 +1,58 @@ -/********************************************************************** - * Jhove - JSTOR/Harvard Object Validation Environment - * Copyright 2004 by JSTOR and the President and Fellows of Harvard College - **********************************************************************/ - +/** + * ******************************************************************** Jhove - JSTOR/Harvard Object + * Validation Environment Copyright 2004 by JSTOR and the President and Fellows of Harvard College + * ******************************************************************** + */ package edu.harvard.hul.ois.jhove.module.jpeg2000; -import java.io.*; import edu.harvard.hul.ois.jhove.*; +import java.io.*; /** - * Bits Per Component box. - * See I.5.3.2 in ISO/IEC 15444-1:2000 + * Bits Per Component box. See I.5.3.2 in ISO/IEC 15444-1:2000 * * @author Gary McGath - * */ public class BPCCBox extends JP2Box { + /** + * Constructor with superbox. + * + * @param parent parent superbox of this box + */ + public BPCCBox(RandomAccessFile raf, BoxHolder parent) { + super(raf, parent); + } - /** - * Constructor with superbox. - * - * @param parent parent superbox of this box - */ - public BPCCBox(RandomAccessFile raf, BoxHolder parent) { - super(raf, parent); + /** + * Reads the box, putting appropriate information in the RepInfo object. setModule, setBoxHeader, + * setRepInfo and setDataInputStream must be called before readBox is called. + * readBox must completely consume the box, so that the next byte to be read by the + * DataInputStream is the FF byte of the next Box. + */ + @Override + public boolean readBox() throws IOException { + if (!(_parentBox instanceof JP2HeaderBox || _parentBox instanceof CodestreamHeaderBox)) { + wrongBoxContext(); + return false; } - - /** Reads the box, putting appropriate information in - * the RepInfo object. setModule, setBoxHeader, - * setRepInfo and setDataInputStream must be called - * before readBox is called. - * readBox must completely consume the - * box, so that the next byte to be read by the - * DataInputStream is the FF byte of the next Box. - */ - @Override - public boolean readBox() throws IOException { - if (!(_parentBox instanceof JP2HeaderBox || - _parentBox instanceof CodestreamHeaderBox)) { - wrongBoxContext(); - return false; - } - initBytesRead (); - int len = (int) _boxHeader.getDataLength (); - int[] bits = new int[len]; - for (int i = 0; i < len; i++) { - bits[i] = ModuleBase.readUnsignedByte (_dstrm, _module); - } - NisoImageMetadata niso; - niso = _module.getCurrentNiso (); - niso.setBitsPerSample (bits); - - finalizeBytesRead (); - return true; + initBytesRead(); + int len = (int) _boxHeader.getDataLength(); + int[] bits = new int[len]; + for (int i = 0; i < len; i++) { + bits[i] = ModuleBase.readUnsignedByte(_dstrm, _module); } + NisoImageMetadata niso; + niso = _module.getCurrentNiso(); + niso.setBitsPerSample(bits); - /** Returns the name of the Box. */ - @Override - protected String getSelfPropName () - { - return "Bits Per Component Box"; - } + finalizeBytesRead(); + return true; + } + + /** Returns the name of the Box. */ + @Override + protected String getSelfPropName() { + return "Bits Per Component Box"; + } } diff --git a/jhove-modules/jpeg2000-hul/src/main/java/edu/harvard/hul/ois/jhove/module/jpeg2000/BinaryFilterBox.java b/jhove-modules/jpeg2000-hul/src/main/java/edu/harvard/hul/ois/jhove/module/jpeg2000/BinaryFilterBox.java index 75a3a4d59..e1c141f65 100644 --- a/jhove-modules/jpeg2000-hul/src/main/java/edu/harvard/hul/ois/jhove/module/jpeg2000/BinaryFilterBox.java +++ b/jhove-modules/jpeg2000-hul/src/main/java/edu/harvard/hul/ois/jhove/module/jpeg2000/BinaryFilterBox.java @@ -1,144 +1,124 @@ -/********************************************************************** - * Jhove - JSTOR/Harvard Object Validation Environment - * Copyright 2004 by JSTOR and the President and Fellows of Harvard College +/** + * ******************************************************************** Jhove - JSTOR/Harvard Object + * Validation Environment Copyright 2004 by JSTOR and the President and Fellows of Harvard College * - **********************************************************************/ - + *

******************************************************************** + */ package edu.harvard.hul.ois.jhove.module.jpeg2000; +import edu.harvard.hul.ois.jhove.*; import java.io.*; import java.util.zip.InflaterInputStream; -import edu.harvard.hul.ois.jhove.*; /** - * Binary Filter box (JPX). - * See ISO/IEC FCD15444-2: 2000, L.9.14 - * - * A Binary Filter Box can subsume any number of - * other boxes, which will look to the module as if they - * simply replace this box. BoxHolder makes a special case - * of BinaryFilterBoxes, calling the getBoxStream method to - * extract the subsumed boxes. - * - * Only Deflate coding, not DES, is supported. - * - * It is assumed that a BinaryFilterBox is never - * encoded inside another BinaryFilterBox. - * - * This is untested code, due to lack of sample files; - * please report any bugs found to HUL/OIS. - * - * @author Gary McGath + * Binary Filter box (JPX). See ISO/IEC FCD15444-2: 2000, L.9.14 + * + *

A Binary Filter Box can subsume any number of other boxes, which will look to the module as if + * they simply replace this box. BoxHolder makes a special case of BinaryFilterBoxes, calling the + * getBoxStream method to extract the subsumed boxes. + * + *

Only Deflate coding, not DES, is supported. * + *

It is assumed that a BinaryFilterBox is never encoded inside another BinaryFilterBox. + * + *

This is untested code, due to lack of sample files; please report any bugs found to HUL/OIS. + * + * @author Gary McGath */ public class BinaryFilterBox extends JP2Box { - private final static int[] gzipuuid = - { 0XEC, 0X34, 0X0B, 0X04, 0X74, 0XC5, 0X11, 0XD4, - 0XA7, 0X29, 0X87, 0X9E, 0XA3, 0X54, 0X8F, 0X0E }; - - private DataInputStream boxStream; - private JP2Box _realParent; - - /** - * Constructor with superbox. - * - * @param parent parent superbox of this box - * or TopLevelBoxHolder - */ - public BinaryFilterBox(RandomAccessFile raf, JP2Box parent) { - super(raf, parent); - _realParent = parent; - } + private static final int[] gzipuuid = { + 0XEC, 0X34, 0X0B, 0X04, 0X74, 0XC5, 0X11, 0XD4, + 0XA7, 0X29, 0X87, 0X9E, 0XA3, 0X54, 0X8F, 0X0E + }; - /** Reads the box, putting appropriate information in - * the RepInfo object. setModule, setBoxHeader, - * setRepInfo and setDataInputStream must be called - * before readBox is called. - * readBox must completely consume the - * box, so that the next byte to be read by the - * DataInputStream is the FF byte of the next Box. - */ - @Override - public boolean readBox() throws IOException { - // Compare the filter type with the GZIP type. - // If it's anything else, just report a property - // and ignore the contents. - byte[] uuidbuf = new byte[16]; - ModuleBase.readByteBuf (_dstrm, uuidbuf, _module); - boolean isGzip = true; - for (int i = 0; i < 16; i++) { - if (uuidbuf[i] != gzipuuid[i]) { - isGzip = false; - } - } - - // Accumulate all binary filter UUIDs into a property. - _module.addBinaryFilterProp (new Property ("BinaryFilter", - PropertyType.BYTE, - PropertyArity.ARRAY, - uuidbuf)); - if (isGzip) { - // report that we've left information unprocessed - _repInfo.setMessage(new InfoMessage - (MessageConstants.INF_BINARY_FILTER_BOX_NOT_GZIP, - _module.getFilePos ())); - } - else { - // We use a CountedInputStream, which will report an - // EOF after streamLimit bytes. - // The caller is responsible for making sure that - // the underlying stream doesn't get mixed up with this - // stream for counting purposes. - // We have to put a DataInputStream on top of the - // InflaterInputStream, which means there are two - // DataInputStreams in the stream stack. Ugly, but - // should still work. - int streamLimit = (int) (_boxHeader.getLength () - 16); - boxStream = new DataInputStream - (new InflaterInputStream - (new CountedInputStream (_dstrm, streamLimit))); - } - - // We report _bytesRead as the total number of bytes in the - // box, including the stream which hasn't actually been read - // yet, because that makes things easier for the caller to - // keep things counted. - return true; + private DataInputStream boxStream; + private JP2Box _realParent; + + /** + * Constructor with superbox. + * + * @param parent parent superbox of this box or TopLevelBoxHolder + */ + public BinaryFilterBox(RandomAccessFile raf, JP2Box parent) { + super(raf, parent); + _realParent = parent; + } + + /** + * Reads the box, putting appropriate information in the RepInfo object. setModule, setBoxHeader, + * setRepInfo and setDataInputStream must be called before readBox is called. + * readBox must completely consume the box, so that the next byte to be read by the + * DataInputStream is the FF byte of the next Box. + */ + @Override + public boolean readBox() throws IOException { + // Compare the filter type with the GZIP type. + // If it's anything else, just report a property + // and ignore the contents. + byte[] uuidbuf = new byte[16]; + ModuleBase.readByteBuf(_dstrm, uuidbuf, _module); + boolean isGzip = true; + for (int i = 0; i < 16; i++) { + if (uuidbuf[i] != gzipuuid[i]) { + isGzip = false; + } } - @Override - public Object next () - { - BoxHeader hdr = new BoxHeader (_module, boxStream); - try { - hdr.readHeader (); - JP2Box box = JP2Box.boxMaker (hdr.getType (), _realParent); - box.setModule(_module); - box.setRepInfo(_repInfo); - box.setRandomAccessFile(_raf); - box.setDataInputStream(boxStream); - return box; - } - catch (IOException e) { - // Will come here when the BoxHeader reaches an EOF - return null; - } + // Accumulate all binary filter UUIDs into a property. + _module.addBinaryFilterProp( + new Property("BinaryFilter", PropertyType.BYTE, PropertyArity.ARRAY, uuidbuf)); + if (isGzip) { + // report that we've left information unprocessed + _repInfo.setMessage( + new InfoMessage(MessageConstants.INF_BINARY_FILTER_BOX_NOT_GZIP, _module.getFilePos())); + } else { + // We use a CountedInputStream, which will report an + // EOF after streamLimit bytes. + // The caller is responsible for making sure that + // the underlying stream doesn't get mixed up with this + // stream for counting purposes. + // We have to put a DataInputStream on top of the + // InflaterInputStream, which means there are two + // DataInputStreams in the stream stack. Ugly, but + // should still work. + int streamLimit = (int) (_boxHeader.getLength() - 16); + boxStream = + new DataInputStream(new InflaterInputStream(new CountedInputStream(_dstrm, streamLimit))); } + // We report _bytesRead as the total number of bytes in the + // box, including the stream which hasn't actually been read + // yet, because that makes things easier for the caller to + // keep things counted. + return true; + } - /** returns the InputStream which will provide the decompressed - * boxes subsumed in this Box. - */ - public DataInputStream getBoxStream () - { - return boxStream; + @Override + public Object next() { + BoxHeader hdr = new BoxHeader(_module, boxStream); + try { + hdr.readHeader(); + JP2Box box = JP2Box.boxMaker(hdr.getType(), _realParent); + box.setModule(_module); + box.setRepInfo(_repInfo); + box.setRandomAccessFile(_raf); + box.setDataInputStream(boxStream); + return box; + } catch (IOException e) { + // Will come here when the BoxHeader reaches an EOF + return null; } + } - /** Returns the name of the Box. */ - @Override - protected String getSelfPropName () - { - return "Binary Filter Box"; - } + /** returns the InputStream which will provide the decompressed boxes subsumed in this Box. */ + public DataInputStream getBoxStream() { + return boxStream; + } + + /** Returns the name of the Box. */ + @Override + protected String getSelfPropName() { + return "Binary Filter Box"; + } } diff --git a/jhove-modules/jpeg2000-hul/src/main/java/edu/harvard/hul/ois/jhove/module/jpeg2000/BoxHeader.java b/jhove-modules/jpeg2000-hul/src/main/java/edu/harvard/hul/ois/jhove/module/jpeg2000/BoxHeader.java index 0db48fdc6..abad0c808 100644 --- a/jhove-modules/jpeg2000-hul/src/main/java/edu/harvard/hul/ois/jhove/module/jpeg2000/BoxHeader.java +++ b/jhove-modules/jpeg2000-hul/src/main/java/edu/harvard/hul/ois/jhove/module/jpeg2000/BoxHeader.java @@ -1,8 +1,8 @@ -/********************************************************************** - * Jhove - JSTOR/Harvard Object Validation Environment - * Copyright 2004 by JSTOR and the President and Fellows of Harvard College - **********************************************************************/ - +/** + * ******************************************************************** Jhove - JSTOR/Harvard Object + * Validation Environment Copyright 2004 by JSTOR and the President and Fellows of Harvard College + * ******************************************************************** + */ package edu.harvard.hul.ois.jhove.module.jpeg2000; import edu.harvard.hul.ois.jhove.*; @@ -10,90 +10,71 @@ import java.io.*; /** - * Encapsulation of a JPEG 2000 box header. + * Encapsulation of a JPEG 2000 box header. * * @author Gary McGath - * */ public class BoxHeader { - - private long _length; - private String _type; - private Jpeg2000Module _module; - private DataInputStream _dstream; - private long _headerLength; - - /** - * Constructor. - * - * @param mod The Module which uses this object - * @param dstrm The DataInputStream reading data for the Module - */ - public BoxHeader (Jpeg2000Module mod, DataInputStream dstrm) - { - _module = mod; - _dstream = dstrm; - } - - /** - * Reads 8 bytes from the beginning of the box and parses - * out the box length and type. - */ - public void readHeader () throws IOException - { - _length = ModuleBase.readUnsignedInt(_dstream, true, _module); - _type = _module.read4Chars (_dstream); - - // If the length field is 1, there is an 8-byte extended - // length field. - if (_length == 1) { - _length = ModuleBase.readSignedLong(_dstream, true, _module); - _headerLength = 16; - } - else { - _headerLength = 8; - } - } - - - /** - * Returns the box length, which includes the length and - * type fields. If the value returned is 0, the length - * of the box is all the remaining data to the end of the file. - */ - public long getLength () - { - return _length; - } - - - /** - * Returns the length of the header. - * This number is equal to the number - * of bytes that have been read by readHeader(). - */ - public long getHeaderLength () - { - return _headerLength; - } - - /** - * Returns the number of bytes in the Box, not including - * the header. This is equivalent to - * getLength() - getHeaderLength(). - * If getLength() would return 0, this value is - * meaningless. - */ - public long getDataLength () - { - return _length - _headerLength; - } - /** - * Returns the box type. - */ - public String getType () - { - return _type; + private long _length; + private String _type; + private Jpeg2000Module _module; + private DataInputStream _dstream; + private long _headerLength; + + /** + * Constructor. + * + * @param mod The Module which uses this object + * @param dstrm The DataInputStream reading data for the Module + */ + public BoxHeader(Jpeg2000Module mod, DataInputStream dstrm) { + _module = mod; + _dstream = dstrm; + } + + /** Reads 8 bytes from the beginning of the box and parses out the box length and type. */ + public void readHeader() throws IOException { + _length = ModuleBase.readUnsignedInt(_dstream, true, _module); + _type = _module.read4Chars(_dstream); + + // If the length field is 1, there is an 8-byte extended + // length field. + if (_length == 1) { + _length = ModuleBase.readSignedLong(_dstream, true, _module); + _headerLength = 16; + } else { + _headerLength = 8; } + } + + /** + * Returns the box length, which includes the length and type fields. If the value returned is 0, + * the length of the box is all the remaining data to the end of the file. + */ + public long getLength() { + return _length; + } + + /** + * Returns the length of the header. This number is equal to the number of bytes that have been + * read by readHeader(). + */ + public long getHeaderLength() { + return _headerLength; + } + + /** + * Returns the number of bytes in the Box, not including the header. This is equivalent to + * getLength() - getHeaderLength(). If getLength() would return 0, this value + * is meaningless. + */ + public long getDataLength() { + return _length - _headerLength; + } + + /** Returns the box type. */ + public String getType() { + return _type; + } } diff --git a/jhove-modules/jpeg2000-hul/src/main/java/edu/harvard/hul/ois/jhove/module/jpeg2000/BoxHolder.java b/jhove-modules/jpeg2000-hul/src/main/java/edu/harvard/hul/ois/jhove/module/jpeg2000/BoxHolder.java index 272e62152..33d4a35a3 100644 --- a/jhove-modules/jpeg2000-hul/src/main/java/edu/harvard/hul/ois/jhove/module/jpeg2000/BoxHolder.java +++ b/jhove-modules/jpeg2000-hul/src/main/java/edu/harvard/hul/ois/jhove/module/jpeg2000/BoxHolder.java @@ -1,195 +1,169 @@ -/********************************************************************** - * Jhove - JSTOR/Harvard Object Validation Environment - * Copyright 2004 by JSTOR and the President and Fellows of Harvard College - **********************************************************************/ - +/** + * ******************************************************************** Jhove - JSTOR/Harvard Object + * Validation Environment Copyright 2004 by JSTOR and the President and Fellows of Harvard College + * ******************************************************************** + */ package edu.harvard.hul.ois.jhove.module.jpeg2000; -import edu.harvard.hul.ois.jhove.module.Jpeg2000Module; import edu.harvard.hul.ois.jhove.*; +import edu.harvard.hul.ois.jhove.module.Jpeg2000Module; import java.io.*; import java.util.*; /** - * A BoxHolder is a container for JPEG 2000 boxes. + * A BoxHolder is a container for JPEG 2000 boxes. * * @author Gary McGath - * */ public class BoxHolder implements Iterator { - protected Jpeg2000Module _module; - protected JP2Box _parentBox; - protected RandomAccessFile _raf; - protected DataInputStream _dstrm; - protected BoxHeader _boxHeader; - protected RepInfo _repInfo; - protected long bytesLeft; - protected long filePos; - protected boolean hasBoxes; - private BinaryFilterBox binaryFilterBox; - - /** - * Constructor. - */ - public BoxHolder(RandomAccessFile raf) { - hasBoxes = false; // subclass which is a superbox should override - _raf = raf; - binaryFilterBox = null; - try { - filePos = raf.getFilePointer (); - } - catch (IOException e) {} - - //makeInputStream (); + protected Jpeg2000Module _module; + protected JP2Box _parentBox; + protected RandomAccessFile _raf; + protected DataInputStream _dstrm; + protected BoxHeader _boxHeader; + protected RepInfo _repInfo; + protected long bytesLeft; + protected long filePos; + protected boolean hasBoxes; + private BinaryFilterBox binaryFilterBox; + + /** Constructor. */ + public BoxHolder(RandomAccessFile raf) { + hasBoxes = false; // subclass which is a superbox should override + _raf = raf; + binaryFilterBox = null; + try { + filePos = raf.getFilePointer(); + } catch (IOException e) { } - - - /** Returns the file position. - * In practice, this means returning the beginning of the Box. */ - protected long getFilePos () - { - // ghaaaaaa ... Maybe the best I can do is report - // the start of the box in the file. the module's - // file position is useless. Of course, for a Binary - // Filter box, even getting the start of the box in - // the file will be tricky. - return filePos; + // makeInputStream (); + } + + /** Returns the file position. In practice, this means returning the beginning of the Box. */ + protected long getFilePos() { + // ghaaaaaa ... Maybe the best I can do is report + // the start of the box in the file. the module's + // file position is useless. Of course, for a Binary + // Filter box, even getting the start of the box in + // the file will be tricky. + return filePos; + } + + /** + * Checks if any more subboxes are available. This class doesn't fully conform to the Iterator + * interface, as there are some cases where the lack of more boxes won't be detected till an EOF + * is encounterd. So callers should call hasNext to avoid reading overruns, and then + * test the value returned by next for nullity. + */ + @Override + public boolean hasNext() { + return (hasBoxes && bytesLeft >= 8); + } + + /* This should return the next Box, if any. */ + @Override + public Object next() { + if (!hasBoxes) { + return null; } - - /** - * Checks if any more subboxes are available. - * This class doesn't fully conform to the Iterator interface, - * as there are some cases where the lack of more boxes - * won't be detected till an EOF is encounterd. So callers - * should call hasNext to avoid reading overruns, and then - * test the value returned by next for nullity. - */ - - @Override - public boolean hasNext () - { - return (hasBoxes && bytesLeft >= 8); - } - - - /* This should return the next Box, if any. */ - @Override - public Object next () - { - if (!hasBoxes) { - return null; + try { + BoxHeader subhdr; + JP2Box nextBox; + // If we've encountered a BinaryFilterBox, it feeds + // us boxes till it's exhausted. When it has no + // more boxes, we set it to null to indicate we + // resume reading ordinary boxes. + if (binaryFilterBox != null) { + if (binaryFilterBox.hasNext()) { + nextBox = (JP2Box) binaryFilterBox.next(); + } else { + binaryFilterBox = null; + // Fall through into normal reading } - - try { - BoxHeader subhdr; - JP2Box nextBox; - // If we've encountered a BinaryFilterBox, it feeds - // us boxes till it's exhausted. When it has no - // more boxes, we set it to null to indicate we - // resume reading ordinary boxes. - if (binaryFilterBox != null) { - if (binaryFilterBox.hasNext ()) { - nextBox = (JP2Box) binaryFilterBox.next (); - } - else { - binaryFilterBox = null; - // Fall through into normal reading - } - } - if (bytesLeft < 8) { - return null; - } - subhdr = new BoxHeader (_module, _dstrm); - subhdr.readHeader (); - bytesLeft -= subhdr.getLength (); - String hType = subhdr.getType (); - if ("bfil".equals (hType)) { - binaryFilterBox = new BinaryFilterBox - (_raf, (this instanceof JP2Box) ? (JP2Box) this : null); - // If I can make the following magic actually - // work correctly, then I'm starting to get somewhere. - - if (binaryFilterBox.hasNext ()) { - return binaryFilterBox.next (); - } - // The "else" is a BinaryFilterBox with no content. - // This seems unlikely, but assume it's legal and - // fall through to the next box. - subhdr.readHeader (); - hType = subhdr.getType (); - } - if ("cref".equals (hType)) { - // A Cross Reference Box is replaced by another box, - // which is found in the DataInputStream it produces. - CrossRefBox xrefBox = new CrossRefBox (_raf, - (this instanceof JP2Box) ? (JP2Box) this : null); - if (!xrefBox.readBox ()) { - return null; - } - BoxHeader xrefhdr = - new BoxHeader (_module, xrefBox.getCrossRefStream()); - xrefhdr.readHeader (); - nextBox = JP2Box.boxMaker (xrefhdr.getType (), - (this instanceof JP2Box) ? (JP2Box) this : null); - return nextBox; - } - nextBox = JP2Box.boxMaker(hType, this); - - nextBox.setModule(_module); - nextBox.setRepInfo(_repInfo); - nextBox.setRandomAccessFile(_raf); - nextBox.setDataInputStream(_dstrm); - nextBox.setBoxHeader (subhdr); - return nextBox; + } + if (bytesLeft < 8) { + return null; + } + subhdr = new BoxHeader(_module, _dstrm); + subhdr.readHeader(); + bytesLeft -= subhdr.getLength(); + String hType = subhdr.getType(); + if ("bfil".equals(hType)) { + binaryFilterBox = + new BinaryFilterBox(_raf, (this instanceof JP2Box) ? (JP2Box) this : null); + // If I can make the following magic actually + // work correctly, then I'm starting to get somewhere. + + if (binaryFilterBox.hasNext()) { + return binaryFilterBox.next(); } - catch (IOException e) { - // Probably I should be reporting an error here - return null; + // The "else" is a BinaryFilterBox with no content. + // This seems unlikely, but assume it's legal and + // fall through to the next box. + subhdr.readHeader(); + hType = subhdr.getType(); + } + if ("cref".equals(hType)) { + // A Cross Reference Box is replaced by another box, + // which is found in the DataInputStream it produces. + CrossRefBox xrefBox = + new CrossRefBox(_raf, (this instanceof JP2Box) ? (JP2Box) this : null); + if (!xrefBox.readBox()) { + return null; } + BoxHeader xrefhdr = new BoxHeader(_module, xrefBox.getCrossRefStream()); + xrefhdr.readHeader(); + nextBox = + JP2Box.boxMaker(xrefhdr.getType(), (this instanceof JP2Box) ? (JP2Box) this : null); + return nextBox; + } + nextBox = JP2Box.boxMaker(hType, this); + + nextBox.setModule(_module); + nextBox.setRepInfo(_repInfo); + nextBox.setRandomAccessFile(_raf); + nextBox.setDataInputStream(_dstrm); + nextBox.setBoxHeader(subhdr); + return nextBox; + } catch (IOException e) { + // Probably I should be reporting an error here + return null; } - - /** Always throws UnsupportedOperationException. */ - @Override - public void remove () throws UnsupportedOperationException - { - throw new UnsupportedOperationException(); - } - - - /** Utility error reporting function for a subbox overrunning - * its superbox. - * Sets the RepInfo's wellFormed flag to false. - */ - protected void superboxOverrun () - { - _repInfo.setMessage (new ErrorMessage - (MessageConstants.JPEG2000_HUL_53, getSelfPropName(), - _module.getFilePos ())); - _repInfo.setWellFormed (false); - } - - - /** Utility error reporting function for a subbox underrunning - * its superbox. - * Sets the RepInfo's wellFormed flag to false. - */ - protected void superboxUnderrun () - { - _repInfo.setMessage (new ErrorMessage - (MessageConstants.JPEG2000_HUL_54, getSelfPropName (), - _module.getFilePos ())); - _repInfo.setWellFormed (false); - } - - - - /** Returns the name of the BoxHolder. All subclasses should - * override this. */ - protected String getSelfPropName () - { - return ""; - } + } + + /** Always throws UnsupportedOperationException. */ + @Override + public void remove() throws UnsupportedOperationException { + throw new UnsupportedOperationException(); + } + + /** + * Utility error reporting function for a subbox overrunning its superbox. Sets the RepInfo's + * wellFormed flag to false. + */ + protected void superboxOverrun() { + _repInfo.setMessage( + new ErrorMessage( + MessageConstants.JPEG2000_HUL_53, getSelfPropName(), _module.getFilePos())); + _repInfo.setWellFormed(false); + } + + /** + * Utility error reporting function for a subbox underrunning its superbox. Sets the RepInfo's + * wellFormed flag to false. + */ + protected void superboxUnderrun() { + _repInfo.setMessage( + new ErrorMessage( + MessageConstants.JPEG2000_HUL_54, getSelfPropName(), _module.getFilePos())); + _repInfo.setWellFormed(false); + } + + /** Returns the name of the BoxHolder. All subclasses should override this. */ + protected String getSelfPropName() { + return ""; + } } diff --git a/jhove-modules/jpeg2000-hul/src/main/java/edu/harvard/hul/ois/jhove/module/jpeg2000/COCMarkerSegment.java b/jhove-modules/jpeg2000-hul/src/main/java/edu/harvard/hul/ois/jhove/module/jpeg2000/COCMarkerSegment.java index ee512d829..b40afd9fb 100644 --- a/jhove-modules/jpeg2000-hul/src/main/java/edu/harvard/hul/ois/jhove/module/jpeg2000/COCMarkerSegment.java +++ b/jhove-modules/jpeg2000-hul/src/main/java/edu/harvard/hul/ois/jhove/module/jpeg2000/COCMarkerSegment.java @@ -1,124 +1,97 @@ -/********************************************************************** - * Jhove - JSTOR/Harvard Object Validation Environment - * Copyright 2004 by JSTOR and the President and Fellows of Harvard College +/** + * ******************************************************************** Jhove - JSTOR/Harvard Object + * Validation Environment Copyright 2004 by JSTOR and the President and Fellows of Harvard College * - **********************************************************************/ - + *

******************************************************************** + */ package edu.harvard.hul.ois.jhove.module.jpeg2000; +import edu.harvard.hul.ois.jhove.*; import java.io.*; import java.util.*; -import edu.harvard.hul.ois.jhove.*; -//import edu.harvard.hul.ois.jhove.module.Jpeg2000Module; + +// import edu.harvard.hul.ois.jhove.module.Jpeg2000Module; /** - * Class for the COC (Coding style component) marker segment. - * May occur in the main or the tile part header. In the - * main header it overrides the COD for the specified - * component. In the tile part header it overrides the - * COD for the component in the tile part. - * - * @author Gary McGath + * Class for the COC (Coding style component) marker segment. May occur in the main or the tile part + * header. In the main header it overrides the COD for the specified component. In the tile part + * header it overrides the COD for the component in the tile part. * + * @author Gary McGath */ public class COCMarkerSegment extends MarkerSegment { - /** - * Constructor. - */ - public COCMarkerSegment() { - super(); - } - - /** Process the marker segment. The DataInputStream - * will be at the point of having read the marker code. The - * process method must consume exactly the number - * of bytes remaining in the marker segment. - * - * @param bytesToEat The number of bytes that must be consumed. - * If it is 0 for a MarkerSegment, the - * number of bytes to consume is unknown. - * - * @return true if segment is well-formed, - * false otherwise. - */ - @Override - protected boolean process(int bytesToEat) throws IOException { - int compIdxBytes = nCompBytes(); - if (compIdxBytes == 0) { - // COC found before SIZ - _repInfo.setMessage (new ErrorMessage - (MessageConstants.JPEG2000_HUL_6)); - return false; - } - int compIdx; - // size of Ccoc field depends on number of components - if (compIdxBytes < 257) { - compIdx = ModuleBase.readUnsignedByte (_dstream, _module); - } - else{ - compIdx = _module.readUnsignedShort (_dstream); - } - int codeStyle = ModuleBase.readUnsignedByte (_dstream, _module); + /** Constructor. */ + public COCMarkerSegment() { + super(); + } - // The SPcoc parameters - int nDecomp = ModuleBase.readUnsignedByte (_dstream, _module); - int codeBlockWid = ModuleBase.readUnsignedByte (_dstream, _module); - int codeBlockHt = ModuleBase.readUnsignedByte (_dstream, _module); - int codeBlockStyle = ModuleBase.readUnsignedByte (_dstream, _module); - int xform = ModuleBase.readUnsignedByte (_dstream, _module); - int precSize[] = null; - if ((codeStyle & 1) != 0) { - // The first parameter (8 bits) corresponds to the - // N(L)LL subband. Each successive parameter corresponds - // to each successive resolution level in order. - // I think that means the number of bytes equals the - // number of resolution levels + 1 -- but where do I get - // the number of resolution levels? Based on the (highly - // confusing) information about the marker segment length, - // that must be the same as the number of decomposition - // levels. - precSize = new int[nDecomp + 1]; - for (int i = 0; i < nDecomp + 1; i++) { - precSize[i] = ModuleBase.readUnsignedByte (_dstream, _module); - } - } - - // Build a property and attach it to the appropriate component. This - // may be a component of the codestream or of a tile part. The - // number of components is apparently established only by the SIZ - // marker segment and never changes for tiles or tile parts. - MainOrTile cs = getMainOrTile (); - List propList = new ArrayList<> (10); - propList.add (new Property ("CodingStyle", - PropertyType.INTEGER, - new Integer (codeStyle))); - propList.add (new Property ("NumberDecompositionLevels", - PropertyType.INTEGER, - new Integer (nDecomp))); - propList.add (new Property ("CodeBlockWidth", - PropertyType.INTEGER, - new Integer (codeBlockWid))); - propList.add (new Property ("CodeBlockHeight", - PropertyType.INTEGER, - new Integer (codeBlockHt))); - propList.add (new Property ("CodeBlockStyle", - PropertyType.INTEGER, - new Integer (codeBlockStyle))); - propList.add (new Property ("Transformation", - PropertyType.INTEGER, - new Integer (xform))); - propList.add (new Property ("PrecinctSize", - PropertyType.INTEGER, - PropertyArity.ARRAY, - precSize)); - cs.setCompProperty (compIdx, - new Property ("COC", - PropertyType.PROPERTY, - PropertyArity.LIST, - propList)); + /** + * Process the marker segment. The DataInputStream will be at the point of having read the marker + * code. The process method must consume exactly the number of bytes remaining in the + * marker segment. + * + * @param bytesToEat The number of bytes that must be consumed. If it is 0 for a MarkerSegment, + * the number of bytes to consume is unknown. + * @return true if segment is well-formed, false otherwise. + */ + @Override + protected boolean process(int bytesToEat) throws IOException { + int compIdxBytes = nCompBytes(); + if (compIdxBytes == 0) { + // COC found before SIZ + _repInfo.setMessage(new ErrorMessage(MessageConstants.JPEG2000_HUL_6)); + return false; + } + int compIdx; + // size of Ccoc field depends on number of components + if (compIdxBytes < 257) { + compIdx = ModuleBase.readUnsignedByte(_dstream, _module); + } else { + compIdx = _module.readUnsignedShort(_dstream); + } + int codeStyle = ModuleBase.readUnsignedByte(_dstream, _module); - return true; + // The SPcoc parameters + int nDecomp = ModuleBase.readUnsignedByte(_dstream, _module); + int codeBlockWid = ModuleBase.readUnsignedByte(_dstream, _module); + int codeBlockHt = ModuleBase.readUnsignedByte(_dstream, _module); + int codeBlockStyle = ModuleBase.readUnsignedByte(_dstream, _module); + int xform = ModuleBase.readUnsignedByte(_dstream, _module); + int precSize[] = null; + if ((codeStyle & 1) != 0) { + // The first parameter (8 bits) corresponds to the + // N(L)LL subband. Each successive parameter corresponds + // to each successive resolution level in order. + // I think that means the number of bytes equals the + // number of resolution levels + 1 -- but where do I get + // the number of resolution levels? Based on the (highly + // confusing) information about the marker segment length, + // that must be the same as the number of decomposition + // levels. + precSize = new int[nDecomp + 1]; + for (int i = 0; i < nDecomp + 1; i++) { + precSize[i] = ModuleBase.readUnsignedByte(_dstream, _module); + } } + // Build a property and attach it to the appropriate component. This + // may be a component of the codestream or of a tile part. The + // number of components is apparently established only by the SIZ + // marker segment and never changes for tiles or tile parts. + MainOrTile cs = getMainOrTile(); + List propList = new ArrayList<>(10); + propList.add(new Property("CodingStyle", PropertyType.INTEGER, new Integer(codeStyle))); + propList.add( + new Property("NumberDecompositionLevels", PropertyType.INTEGER, new Integer(nDecomp))); + propList.add(new Property("CodeBlockWidth", PropertyType.INTEGER, new Integer(codeBlockWid))); + propList.add(new Property("CodeBlockHeight", PropertyType.INTEGER, new Integer(codeBlockHt))); + propList.add(new Property("CodeBlockStyle", PropertyType.INTEGER, new Integer(codeBlockStyle))); + propList.add(new Property("Transformation", PropertyType.INTEGER, new Integer(xform))); + propList.add(new Property("PrecinctSize", PropertyType.INTEGER, PropertyArity.ARRAY, precSize)); + cs.setCompProperty( + compIdx, new Property("COC", PropertyType.PROPERTY, PropertyArity.LIST, propList)); + + return true; + } } diff --git a/jhove-modules/jpeg2000-hul/src/main/java/edu/harvard/hul/ois/jhove/module/jpeg2000/CODMarkerSegment.java b/jhove-modules/jpeg2000-hul/src/main/java/edu/harvard/hul/ois/jhove/module/jpeg2000/CODMarkerSegment.java index e3efabe8c..b160fc333 100644 --- a/jhove-modules/jpeg2000-hul/src/main/java/edu/harvard/hul/ois/jhove/module/jpeg2000/CODMarkerSegment.java +++ b/jhove-modules/jpeg2000-hul/src/main/java/edu/harvard/hul/ois/jhove/module/jpeg2000/CODMarkerSegment.java @@ -1,133 +1,103 @@ -/********************************************************************** - * Jhove - JSTOR/Harvard Object Validation Environment - * Copyright 2004 by JSTOR and the President and Fellows of Harvard College - **********************************************************************/ - +/** + * ******************************************************************** Jhove - JSTOR/Harvard Object + * Validation Environment Copyright 2004 by JSTOR and the President and Fellows of Harvard College + * ******************************************************************** + */ package edu.harvard.hul.ois.jhove.module.jpeg2000; +import edu.harvard.hul.ois.jhove.*; import java.io.*; import java.util.*; -import edu.harvard.hul.ois.jhove.*; -//import edu.harvard.hul.ois.jhove.module.Jpeg2000Module; + +// import edu.harvard.hul.ois.jhove.module.Jpeg2000Module; /** - * Class for the COD (coding style default) marker segment. - * This comes either in the main header or - * after an SOT and describes the functions - * used to code the entire tile. - * - * @author Gary McGath + * Class for the COD (coding style default) marker segment. This comes either in the main header or + * after an SOT and describes the functions used to code the entire tile. * + * @author Gary McGath */ public class CODMarkerSegment extends MarkerSegment { - public CODMarkerSegment () - { - } + public CODMarkerSegment() {} + /** + * Processes the marker segment. The DataInputStream will be at the point of having read the + * marker code. The process method must consume exactly the number of bytes remaining + * in the marker segment. + * + * @param bytesToEat The number of bytes that must be consumed. If it is 0 for a MarkerSegment, + * the number of bytes to consume is unknown. + */ + @Override + protected boolean process(int bytesToEat) throws IOException { + int codeStyle = ModuleBase.readUnsignedByte(_dstream, _module); + // The SGcod parameter, 32 bits + int progOrder = ModuleBase.readUnsignedByte(_dstream, _module); + int nLayers = _module.readUnsignedShort(_dstream); + int mcTrans = ModuleBase.readUnsignedByte(_dstream, _module); + // The SPcod parameters + int nDecomp = ModuleBase.readUnsignedByte(_dstream, _module); + int codeBlockWid = ModuleBase.readUnsignedByte(_dstream, _module); + int codeBlockHt = ModuleBase.readUnsignedByte(_dstream, _module); + int codeBlockStyle = ModuleBase.readUnsignedByte(_dstream, _module); + int xform = ModuleBase.readUnsignedByte(_dstream, _module); + int precSize[] = null; + if ((codeStyle & 1) != 0) { + // The first parameter (8 bits) corresponds to the + // N(L)LL subband. Each successive parameter corresponds + // to each successive resolution level in order. + // I think that means the number of bytes equals the + // number of resolution levels + 1 -- but where do I get + // the number of resolution levels? Based on the (highly + // confusing) information about the marker segment length, + // that must be the same as the number of decomposition + // levels. + precSize = new int[nDecomp + 1]; + for (int i = 0; i < nDecomp + 1; i++) { + precSize[i] = ModuleBase.readUnsignedByte(_dstream, _module); + } + } + MainOrTile cs = getMainOrTile(); - /** - * Processes the marker segment. The DataInputStream - * will be at the point of having read the marker code. The - * process method must consume exactly the number - * of bytes remaining in the marker segment. - * - * @param bytesToEat The number of bytes that must be consumed. - * If it is 0 for a MarkerSegment, the - * number of bytes to consume is unknown. - */ - @Override - protected boolean process (int bytesToEat) throws IOException - { - int codeStyle = ModuleBase.readUnsignedByte (_dstream, _module); - - // The SGcod parameter, 32 bits - int progOrder = ModuleBase.readUnsignedByte (_dstream, _module); - int nLayers = _module.readUnsignedShort (_dstream); - int mcTrans = ModuleBase.readUnsignedByte (_dstream, _module); - - // The SPcod parameters - int nDecomp = ModuleBase.readUnsignedByte (_dstream, _module); - int codeBlockWid = ModuleBase.readUnsignedByte (_dstream, _module); - int codeBlockHt = ModuleBase.readUnsignedByte (_dstream, _module); - int codeBlockStyle = ModuleBase.readUnsignedByte (_dstream, _module); - int xform = ModuleBase.readUnsignedByte (_dstream, _module); - int precSize[] = null; - if ((codeStyle & 1) != 0) { - // The first parameter (8 bits) corresponds to the - // N(L)LL subband. Each successive parameter corresponds - // to each successive resolution level in order. - // I think that means the number of bytes equals the - // number of resolution levels + 1 -- but where do I get - // the number of resolution levels? Based on the (highly - // confusing) information about the marker segment length, - // that must be the same as the number of decomposition - // levels. - precSize = new int[nDecomp + 1]; - for (int i = 0; i < nDecomp + 1; i++) { - precSize[i] = ModuleBase.readUnsignedByte (_dstream, _module); - } - } - MainOrTile cs = getMainOrTile (); - - // Set values for the tile or codestream - List propList = new ArrayList (12); - propList.add (new Property ("CodingStyle", - PropertyType.INTEGER, - new Integer (codeStyle))); - propList.add (new Property ("ProgressionOrder", - PropertyType.INTEGER, - new Integer (progOrder))); - propList.add (new Property ("NumberOfLayers", - PropertyType.INTEGER, - new Integer (nLayers))); - propList.add (new Property ("MultipleComponentTransformation", - PropertyType.INTEGER, - new Integer (mcTrans))); - propList.add (new Property ("NumberDecompositionLevels", - PropertyType.INTEGER, - new Integer (nDecomp))); - propList.add (new Property ("CodeBlockWidth", - PropertyType.INTEGER, - new Integer (codeBlockWid))); - propList.add (new Property ("CodeBlockHeight", - PropertyType.INTEGER, - new Integer (codeBlockHt))); - propList.add (new Property ("CodeBlockStyle", - PropertyType.INTEGER, - new Integer (codeBlockStyle))); - propList.add (new Property ("Transformation", - PropertyType.INTEGER, - new Integer (xform))); - if (precSize != null) { - propList.add (new Property ("PrecinctSize", - PropertyType.INTEGER, - PropertyArity.ARRAY, - precSize)); - } - cs.setCODProperty (new Property ("CodingStyleDefault", - PropertyType.PROPERTY, - PropertyArity.LIST, - propList)); + // Set values for the tile or codestream + List propList = new ArrayList(12); + propList.add(new Property("CodingStyle", PropertyType.INTEGER, new Integer(codeStyle))); + propList.add(new Property("ProgressionOrder", PropertyType.INTEGER, new Integer(progOrder))); + propList.add(new Property("NumberOfLayers", PropertyType.INTEGER, new Integer(nLayers))); + propList.add( + new Property( + "MultipleComponentTransformation", PropertyType.INTEGER, new Integer(mcTrans))); + propList.add( + new Property("NumberDecompositionLevels", PropertyType.INTEGER, new Integer(nDecomp))); + propList.add(new Property("CodeBlockWidth", PropertyType.INTEGER, new Integer(codeBlockWid))); + propList.add(new Property("CodeBlockHeight", PropertyType.INTEGER, new Integer(codeBlockHt))); + propList.add(new Property("CodeBlockStyle", PropertyType.INTEGER, new Integer(codeBlockStyle))); + propList.add(new Property("Transformation", PropertyType.INTEGER, new Integer(xform))); + if (precSize != null) { + propList.add( + new Property("PrecinctSize", PropertyType.INTEGER, PropertyArity.ARRAY, precSize)); + } + cs.setCODProperty( + new Property("CodingStyleDefault", PropertyType.PROPERTY, PropertyArity.LIST, propList)); - // The transformation property is 0 = "9-7 irreversible", 1= "5-3 reversible" - NisoImageMetadata niso = _module.getCurrentNiso (); - switch (xform) { - case 0: - niso.setCompressionScheme(NisoImageMetadata.COMPRESSION_JPEG2000_LOSSY); - break; - case 1: - niso.setCompressionScheme(NisoImageMetadata.COMPRESSION_JPEG2000_LOSSLESS); - break; - default: - // let the nisoDefault works - } - niso.setJp2Layers(nLayers); - niso.setJp2ResolutionLevels(nDecomp); - - return true; + // The transformation property is 0 = "9-7 irreversible", 1= "5-3 reversible" + NisoImageMetadata niso = _module.getCurrentNiso(); + switch (xform) { + case 0: + niso.setCompressionScheme(NisoImageMetadata.COMPRESSION_JPEG2000_LOSSY); + break; + case 1: + niso.setCompressionScheme(NisoImageMetadata.COMPRESSION_JPEG2000_LOSSLESS); + break; + default: + // let the nisoDefault works } + niso.setJp2Layers(nLayers); + niso.setJp2ResolutionLevels(nDecomp); + return true; + } } diff --git a/jhove-modules/jpeg2000-hul/src/main/java/edu/harvard/hul/ois/jhove/module/jpeg2000/CRGMarkerSegment.java b/jhove-modules/jpeg2000-hul/src/main/java/edu/harvard/hul/ois/jhove/module/jpeg2000/CRGMarkerSegment.java index 87fa236bc..83f0c0be7 100644 --- a/jhove-modules/jpeg2000-hul/src/main/java/edu/harvard/hul/ois/jhove/module/jpeg2000/CRGMarkerSegment.java +++ b/jhove-modules/jpeg2000-hul/src/main/java/edu/harvard/hul/ois/jhove/module/jpeg2000/CRGMarkerSegment.java @@ -1,74 +1,57 @@ -/********************************************************************** - * Jhove - JSTOR/Harvard Object Validation Environment - * Copyright 2004 by JSTOR and the President and Fellows of Harvard College - **********************************************************************/ - +/** + * ******************************************************************** Jhove - JSTOR/Harvard Object + * Validation Environment Copyright 2004 by JSTOR and the President and Fellows of Harvard College + * ******************************************************************** + */ package edu.harvard.hul.ois.jhove.module.jpeg2000; import edu.harvard.hul.ois.jhove.*; import java.io.*; /** + * Class for the CRG (component registration) marker segment. * - * Class for the CRG (component registration) - * marker segment. - * * @author Gary McGath - * */ public class CRGMarkerSegment extends MarkerSegment { - /** - * Constructor. - */ - public CRGMarkerSegment() { - super(); - } + /** Constructor. */ + public CRGMarkerSegment() { + super(); + } - - /** - * Processes the marker segment. The DataInputStream - * will be at the point of having read the marker code. The - * process method must consume exactly the number - * of bytes remaining in the marker segment. - * - * @param bytesToEat The number of bytes that must be consumed. - * If it is 0 for a MarkerSegment, the - * number of bytes to consume is unknown. - */ - @Override - protected boolean process(int bytesToEat) throws IOException - { - if (_ccs.getCurTile () != null) { - _repInfo.setMessage (new ErrorMessage - (MessageConstants.JPEG2000_HUL_14)); - return false; - } - int ncomps = _cs.getNumComponents (); - if (ncomps * 4 != bytesToEat) { - _repInfo.setMessage (new ErrorMessage - (MessageConstants.JPEG2000_HUL_15)); - } - int[] horOffsets = new int[ncomps]; - int[] vertOffsets = new int[ncomps]; - for (int i = 0; i < ncomps; i++) { - horOffsets[i] = _module.readUnsignedShort (_dstream); - vertOffsets[i] = _module.readUnsignedShort (_dstream); - } - Property[] props = new Property[2]; - props[0] = new Property ("HorizontalOffsets", - PropertyType.INTEGER, - PropertyArity.ARRAY, - horOffsets); - props[1] = new Property ("VerticalOffsets", - PropertyType.INTEGER, - PropertyArity.ARRAY, - vertOffsets); - _cs.setCRGProperty (new Property ("ComponentRegistration", - PropertyType.PROPERTY, - PropertyArity.ARRAY, - props)); - - return true; + /** + * Processes the marker segment. The DataInputStream will be at the point of having read the + * marker code. The process method must consume exactly the number of bytes remaining + * in the marker segment. + * + * @param bytesToEat The number of bytes that must be consumed. If it is 0 for a MarkerSegment, + * the number of bytes to consume is unknown. + */ + @Override + protected boolean process(int bytesToEat) throws IOException { + if (_ccs.getCurTile() != null) { + _repInfo.setMessage(new ErrorMessage(MessageConstants.JPEG2000_HUL_14)); + return false; + } + int ncomps = _cs.getNumComponents(); + if (ncomps * 4 != bytesToEat) { + _repInfo.setMessage(new ErrorMessage(MessageConstants.JPEG2000_HUL_15)); } + int[] horOffsets = new int[ncomps]; + int[] vertOffsets = new int[ncomps]; + for (int i = 0; i < ncomps; i++) { + horOffsets[i] = _module.readUnsignedShort(_dstream); + vertOffsets[i] = _module.readUnsignedShort(_dstream); + } + Property[] props = new Property[2]; + props[0] = + new Property("HorizontalOffsets", PropertyType.INTEGER, PropertyArity.ARRAY, horOffsets); + props[1] = + new Property("VerticalOffsets", PropertyType.INTEGER, PropertyArity.ARRAY, vertOffsets); + _cs.setCRGProperty( + new Property("ComponentRegistration", PropertyType.PROPERTY, PropertyArity.ARRAY, props)); + + return true; + } } diff --git a/jhove-modules/jpeg2000-hul/src/main/java/edu/harvard/hul/ois/jhove/module/jpeg2000/CaptureResolutionBox.java b/jhove-modules/jpeg2000-hul/src/main/java/edu/harvard/hul/ois/jhove/module/jpeg2000/CaptureResolutionBox.java index 0ffdafd4b..6dc26c7b9 100644 --- a/jhove-modules/jpeg2000-hul/src/main/java/edu/harvard/hul/ois/jhove/module/jpeg2000/CaptureResolutionBox.java +++ b/jhove-modules/jpeg2000-hul/src/main/java/edu/harvard/hul/ois/jhove/module/jpeg2000/CaptureResolutionBox.java @@ -1,96 +1,84 @@ -/********************************************************************** - * Jhove - JSTOR/Harvard Object Validation Environment - * Copyright 2004 by JSTOR and the President and Fellows of Harvard College - **********************************************************************/ - +/** + * ******************************************************************** Jhove - JSTOR/Harvard Object + * Validation Environment Copyright 2004 by JSTOR and the President and Fellows of Harvard College + * ******************************************************************** + */ package edu.harvard.hul.ois.jhove.module.jpeg2000; -import java.io.*; - import edu.harvard.hul.ois.jhove.*; +import java.io.*; /** - * Capture Resolution Box. - * See I.5.3.7.1 in ISO/IEC 15444-1:2000 + * Capture Resolution Box. See I.5.3.7.1 in ISO/IEC 15444-1:2000 * * @author Gary McGath - * */ public class CaptureResolutionBox extends JP2Box { + /** + * Constructor with superbox. + * + * @param parent parent superbox of this box + */ + public CaptureResolutionBox(RandomAccessFile raf, BoxHolder parent) { + super(raf, parent); + } - /** - * Constructor with superbox. - * - * @param parent parent superbox of this box - */ - public CaptureResolutionBox (RandomAccessFile raf, BoxHolder parent) - { - super (raf, parent); + /** + * Reads the box, putting appropriate information in the RepInfo object. setModule, setBoxHeader, + * setRepInfo and setDataInputStream must be called before readBox is called. + * readBox must completely consume the box, so that the next byte to be read by the + * DataInputStream is the FF byte of the next Box. + */ + @Override + public boolean readBox() throws IOException { + if (!(_parentBox instanceof ResolutionBox)) { + wrongBoxContext(); + return false; } + initBytesRead(); + // The information consists of two values, horizontal and + // vertical, with numerator, denominator, and exponent, + // in dots per meter. Not clear whether to present this + // as raw data, turn it into a dots/cm rational, or what. + // I'll put it up as raw data for now. - /** Reads the box, putting appropriate information in - * the RepInfo object. setModule, setBoxHeader, - * setRepInfo and setDataInputStream must be called - * before readBox is called. - * readBox must completely consume the - * box, so that the next byte to be read by the - * DataInputStream is the FF byte of the next Box. - */ - @Override - public boolean readBox() throws IOException { - if (!(_parentBox instanceof ResolutionBox)) { - wrongBoxContext (); - return false; - } - initBytesRead (); - // The information consists of two values, horizontal and - // vertical, with numerator, denominator, and exponent, - // in dots per meter. Not clear whether to present this - // as raw data, turn it into a dots/cm rational, or what. - // I'll put it up as raw data for now. + // Vertical Capture grid resolution num & denom + int vrcNum = _module.readUnsignedShort(_dstrm); + int vrcDenom = _module.readUnsignedShort(_dstrm); - // Vertical Capture grid resolution num & denom - int vrcNum = _module.readUnsignedShort (_dstrm); - int vrcDenom = _module.readUnsignedShort (_dstrm); - - // Horizontal Capture grid resolution num & denom - int hrcNum = _module.readUnsignedShort (_dstrm); - int hrcDenom = _module.readUnsignedShort (_dstrm); - - // Vertical and Horizontal capture grid exponents - int vrcExp = ModuleBase.readUnsignedByte (_dstrm, _module); - int hrcExp = ModuleBase.readUnsignedByte (_dstrm, _module); - - // And the two resolution properties are subsumed into - // one property for the Module. - Property[] topProps = new Property[2]; - topProps[0] = ResolutionBox.makeResolutionProperty("HorizResolution", - hrcNum, hrcDenom, hrcExp); - topProps[1] = ResolutionBox.makeResolutionProperty("VertResolution", - vrcNum, vrcDenom, vrcExp); - _module.addProperty(new Property ("CaptureResolution", - PropertyType.PROPERTY, - PropertyArity.ARRAY, - topProps)); + // Horizontal Capture grid resolution num & denom + int hrcNum = _module.readUnsignedShort(_dstrm); + int hrcDenom = _module.readUnsignedShort(_dstrm); - // Add to NISO - Rational vrc = ResolutionBox.convertToRational(vrcNum, vrcDenom, vrcExp); - Rational hrc = ResolutionBox.convertToRational(hrcNum, hrcDenom, hrcExp); - NisoImageMetadata niso = _module.getCurrentNiso (); - niso.setYSamplingFrequency (vrc); - niso.setXSamplingFrequency (hrc); - final int RESOLUTION_UNIT_CM = 3; - niso.setSamplingFrequencyUnit (RESOLUTION_UNIT_CM); - - finalizeBytesRead (); - return true; - } + // Vertical and Horizontal capture grid exponents + int vrcExp = ModuleBase.readUnsignedByte(_dstrm, _module); + int hrcExp = ModuleBase.readUnsignedByte(_dstrm, _module); - /** Returns the name of the Box. */ - @Override - protected String getSelfPropName () - { - return "Capture Resolution Box"; - } + // And the two resolution properties are subsumed into + // one property for the Module. + Property[] topProps = new Property[2]; + topProps[0] = ResolutionBox.makeResolutionProperty("HorizResolution", hrcNum, hrcDenom, hrcExp); + topProps[1] = ResolutionBox.makeResolutionProperty("VertResolution", vrcNum, vrcDenom, vrcExp); + _module.addProperty( + new Property("CaptureResolution", PropertyType.PROPERTY, PropertyArity.ARRAY, topProps)); + + // Add to NISO + Rational vrc = ResolutionBox.convertToRational(vrcNum, vrcDenom, vrcExp); + Rational hrc = ResolutionBox.convertToRational(hrcNum, hrcDenom, hrcExp); + NisoImageMetadata niso = _module.getCurrentNiso(); + niso.setYSamplingFrequency(vrc); + niso.setXSamplingFrequency(hrc); + final int RESOLUTION_UNIT_CM = 3; + niso.setSamplingFrequencyUnit(RESOLUTION_UNIT_CM); + + finalizeBytesRead(); + return true; + } + + /** Returns the name of the Box. */ + @Override + protected String getSelfPropName() { + return "Capture Resolution Box"; + } } diff --git a/jhove-modules/jpeg2000-hul/src/main/java/edu/harvard/hul/ois/jhove/module/jpeg2000/ChannelDefBox.java b/jhove-modules/jpeg2000-hul/src/main/java/edu/harvard/hul/ois/jhove/module/jpeg2000/ChannelDefBox.java index 8e1b93241..17610ca78 100644 --- a/jhove-modules/jpeg2000-hul/src/main/java/edu/harvard/hul/ois/jhove/module/jpeg2000/ChannelDefBox.java +++ b/jhove-modules/jpeg2000-hul/src/main/java/edu/harvard/hul/ois/jhove/module/jpeg2000/ChannelDefBox.java @@ -1,100 +1,77 @@ -/********************************************************************** - * Jhove - JSTOR/Harvard Object Validation Environment - * Copyright 2004 by JSTOR and the President and Fellows of Harvard College - **********************************************************************/ - +/** + * ******************************************************************** Jhove - JSTOR/Harvard Object + * Validation Environment Copyright 2004 by JSTOR and the President and Fellows of Harvard College + * ******************************************************************** + */ package edu.harvard.hul.ois.jhove.module.jpeg2000; -import java.io.*; import edu.harvard.hul.ois.jhove.*; +import java.io.*; /** - * Channel Definition Box. - * See I.5.3.6 in ISO/IEC 15444-1:2000 - * and ISO/IEC FCD15444-2: 2000, L.9.4.5 + * Channel Definition Box. See I.5.3.6 in ISO/IEC 15444-1:2000 and ISO/IEC FCD15444-2: 2000, L.9.4.5 * * @author Gary McGath - * */ public class ChannelDefBox extends JP2Box { + /** Constructor with superbox. */ + public ChannelDefBox(RandomAccessFile raf, BoxHolder parent) { + super(raf, parent); + } - /** - * Constructor with superbox. - */ - public ChannelDefBox (RandomAccessFile raf, BoxHolder parent) - { - super (raf, parent); + /** + * Reads the box, putting appropriate information in the RepInfo object. setModule, setBoxHeader, + * setRepInfo and setDataInputStream must be called before readBox is called. + * readBox must completely consume the box, so that the next byte to be read by the + * DataInputStream is the FF byte of the next Box. + */ + @Override + public boolean readBox() throws IOException { + if (!(_parentBox instanceof JP2HeaderBox || _parentBox instanceof CodestreamHeaderBox)) { + wrongBoxContext(); + return false; } + initBytesRead(); + int len = (int) _boxHeader.getDataLength(); + int nchan = _module.readUnsignedShort(_dstrm); + len -= 2; + Property[] chans = new Property[nchan]; + for (int i = 0; i < nchan; i++) { + Property[] cprop = new Property[3]; + int cidx = _module.readUnsignedShort(_dstrm); + cprop[0] = new Property("ChannelIndex", PropertyType.INTEGER, new Integer(cidx)); + int typ = _module.readUnsignedShort(_dstrm); + cprop[1] = + _module.addIntegerProperty("ChannelType", typ, JP2Strings.ctypStr, JP2Strings.ctypIdx); + int assoc = _module.readUnsignedShort(_dstrm); + len -= 6; - /** Reads the box, putting appropriate information in - * the RepInfo object. setModule, setBoxHeader, - * setRepInfo and setDataInputStream must be called - * before readBox is called. - * readBox must completely consume the - * box, so that the next byte to be read by the - * DataInputStream is the FF byte of the next Box. - */ - @Override - public boolean readBox() throws IOException { - if (!(_parentBox instanceof JP2HeaderBox || - _parentBox instanceof CodestreamHeaderBox)) { - wrongBoxContext(); - return false; - } - initBytesRead (); - int len = (int) _boxHeader.getDataLength (); + // The interpretation of the assoc field depends + // on the color space, so we just report it as + // an integer. + cprop[2] = new Property("ChannelAssociation", PropertyType.INTEGER, new Integer(assoc)); + chans[i] = new Property("Channel", PropertyType.PROPERTY, PropertyArity.ARRAY, cprop); + } - int nchan = _module.readUnsignedShort (_dstrm); - len -= 2; - Property[] chans = new Property[nchan]; - for (int i = 0; i < nchan; i++) { - Property[] cprop = new Property[3]; - int cidx = _module.readUnsignedShort (_dstrm); - cprop[0] = new Property ("ChannelIndex", - PropertyType.INTEGER, - new Integer (cidx)); - int typ = _module.readUnsignedShort (_dstrm); - cprop[1] = _module.addIntegerProperty ("ChannelType", typ, - JP2Strings.ctypStr, - JP2Strings.ctypIdx); - int assoc = _module.readUnsignedShort (_dstrm); - len -= 6; - - // The interpretation of the assoc field depends - // on the color space, so we just report it as - // an integer. - cprop[2] = new Property ("ChannelAssociation", - PropertyType.INTEGER, - new Integer (assoc)); - chans[i] = new Property ("Channel", - PropertyType.PROPERTY, - PropertyArity.ARRAY, - cprop); - } - - _module.skipBytes (_dstrm, len, _module); + _module.skipBytes(_dstrm, len, _module); - Property prop = new Property ("ChannelDefinition", - PropertyType.PROPERTY, - PropertyArity.ARRAY, - chans); - if (_parentBox instanceof JP2HeaderBox) { - _module.addProperty (prop); - } - else if (_parentBox instanceof ComposLayerHdrBox) { - ((ComposLayerHdrBox) _parentBox).addChannelDef (prop); - } - - finalizeBytesRead (); - return true; + Property prop = + new Property("ChannelDefinition", PropertyType.PROPERTY, PropertyArity.ARRAY, chans); + if (_parentBox instanceof JP2HeaderBox) { + _module.addProperty(prop); + } else if (_parentBox instanceof ComposLayerHdrBox) { + ((ComposLayerHdrBox) _parentBox).addChannelDef(prop); } - /** Returns the name of the Box. */ - @Override - protected String getSelfPropName () - { - return "Channel Definition Box"; - } + finalizeBytesRead(); + return true; + } + + /** Returns the name of the Box. */ + @Override + protected String getSelfPropName() { + return "Channel Definition Box"; + } } diff --git a/jhove-modules/jpeg2000-hul/src/main/java/edu/harvard/hul/ois/jhove/module/jpeg2000/Codestream.java b/jhove-modules/jpeg2000-hul/src/main/java/edu/harvard/hul/ois/jhove/module/jpeg2000/Codestream.java index f78207a82..847d5c621 100644 --- a/jhove-modules/jpeg2000-hul/src/main/java/edu/harvard/hul/ois/jhove/module/jpeg2000/Codestream.java +++ b/jhove-modules/jpeg2000-hul/src/main/java/edu/harvard/hul/ois/jhove/module/jpeg2000/Codestream.java @@ -1,260 +1,216 @@ -/********************************************************************** - * Jhove - JSTOR/Harvard Object Validation Environment - * Copyright 2004 by JSTOR and the President and Fellows of Harvard College +/** + * ******************************************************************** Jhove - JSTOR/Harvard Object + * Validation Environment Copyright 2004 by JSTOR and the President and Fellows of Harvard College * - **********************************************************************/ - + *

******************************************************************** + */ package edu.harvard.hul.ois.jhove.module.jpeg2000; import edu.harvard.hul.ois.jhove.*; import java.util.*; /** - * The information on a codestream, extracted from a Contiguous Codestream - * or Fragment Table, and the corresponding Codestream Header if any. - * @author Gary McGath + * The information on a codestream, extracted from a Contiguous Codestream or Fragment Table, and + * the corresponding Codestream Header if any. * + * @author Gary McGath */ public class Codestream extends MainOrTile { - private NisoImageMetadata _niso; - - /* List of Tile objects associated with the codestream */ - private List _tiles; - - /* List of lengths (Long objects) found in PPM code segments */ - private List _ppmLengthList; - - /* Label property */ - private Property _labelProperty; - - /* Component mapping property */ - private Property _compMapProperty; + private NisoImageMetadata _niso; - /* Property generated by the SIZ marker segment */ - private Property _sizProperty; + /* List of Tile objects associated with the codestream */ + private List _tiles; - /* Property generated by the CRG marker segment */ - private Property _crgProperty; - - /* Property generated by the PaletteBox of a CodestreamHeader */ - private Property _paletteProperty; + /* List of lengths (Long objects) found in PPM code segments */ + private List _ppmLengthList; - /* Property generated by the ROIBox of a CodestreamHeader */ - private Property _roiProperty; - - /* List of tile length properties */ - private List tileLengthList; - - public Codestream () - { - _precSize = null; + /* Label property */ + private Property _labelProperty; - _niso = new NisoImageMetadata (); - } + /* Component mapping property */ + private Property _compMapProperty; + /* Property generated by the SIZ marker segment */ + private Property _sizProperty; - /** Builds a - * Property out of everything we've collected. */ - public Property makeProperty () - { - List propList = new ArrayList (10); - - if (_labelProperty != null) { - propList.add (_labelProperty); - } - if (_compMapProperty != null) { - propList.add (_compMapProperty); - } - if (_paletteProperty != null) { - propList.add (_paletteProperty); - } - if (_roiProperty != null) { - propList.add (_roiProperty); - } - if (_sizProperty != null) { - propList.add (_sizProperty); - } - if (_codProperty != null) { - propList.add (_codProperty); - } - if (_qcdProperty != null) { - propList.add (_qcdProperty); - } - if (_pocProperty != null) { - propList.add (_pocProperty); - } - if (tileLengthList != null && !tileLengthList.isEmpty ()) { - propList.add (new Property ("TileLengths", - PropertyType.PROPERTY, - PropertyArity.LIST, - tileLengthList)); - } - if (_packetLengthList != null && !_packetLengthList.isEmpty ()) { - propList.add (new Property ("PacketLengths", - PropertyType.LONG, - PropertyArity.LIST, - _packetLengthList)); - } - if (_ppmLengthList != null && !_ppmLengthList.isEmpty ()) { - propList.add (new Property ("PackedPacketHeaderLengths", - PropertyType.LONG, - PropertyArity.LIST, - _ppmLengthList)); - } - if (_crgProperty != null) { - propList.add (_crgProperty ); - } - - propList.add (new Property ("NisoImageMetadata", - PropertyType.NISOIMAGEMETADATA, _niso)); - if (_tiles != null && !_tiles.isEmpty ()) { - List tpList = new ArrayList (_tiles.size ()); - ListIterator iter = _tiles.listIterator (); - while (iter.hasNext ()) { - Tile t = (Tile) iter.next (); - tpList.add (t.makeProperty ()); - } - propList.add (new Property ("Tiles", - PropertyType.PROPERTY, - PropertyArity.LIST, - tpList)); - } - if (!_comments.isEmpty ()) { - propList.add (new Property ("Comments", - PropertyType.PROPERTY, - PropertyArity.LIST, - _comments)); - } - if (_components != null) { - // The component array may be only partially populated, or - // not at all, so we reduce it to a List. - List clist = new ArrayList (_components.length); - for (int i = 0; i < _components.length; i++ ) { - Property c = _components[i]; - if (c != null) { - clist.add (c); - } - } - if (!clist.isEmpty ()) { - propList.add (new Property ("Components", - PropertyType.PROPERTY, - PropertyArity.LIST, - clist)); - } - } - return new Property ("Codestream", - PropertyType.PROPERTY, - PropertyArity.LIST, - propList); - } + /* Property generated by the CRG marker segment */ + private Property _crgProperty; + + /* Property generated by the PaletteBox of a CodestreamHeader */ + private Property _paletteProperty; + /* Property generated by the ROIBox of a CodestreamHeader */ + private Property _roiProperty; - /** Set the initial Niso values from a default Niso object. - * This doesn't attempt to be complete, but sets - * the values which we know could have been set from the - * JP2 header. */ - public void setDefaultNiso (NisoImageMetadata dNiso) - { - _niso.setByteOrder(dNiso.getByteOrder ()); - _niso.setMimeType (dNiso.getMimeType ()); - _niso.setBitsPerSample (dNiso.getBitsPerSample ()); - _niso.setImageLength (dNiso.getImageLength ()); - _niso.setImageWidth (dNiso.getImageWidth ()); - _niso.setSamplesPerPixel (dNiso.getSamplesPerPixel ()); - _niso.setCompressionScheme (dNiso.getCompressionScheme ()); - _niso.setYSamplingFrequency (dNiso.getYSamplingFrequency ()); - _niso.setXSamplingFrequency (dNiso.getXSamplingFrequency ()); - _niso.setSamplingFrequencyUnit (dNiso.getSamplingFrequencyUnit ()); + /* List of tile length properties */ + private List tileLengthList; + + public Codestream() { + _precSize = null; + + _niso = new NisoImageMetadata(); + } + + /** Builds a Property out of everything we've collected. */ + public Property makeProperty() { + List propList = new ArrayList(10); + + if (_labelProperty != null) { + propList.add(_labelProperty); } - - /** Returns the images NisoImageMetadata. */ - public NisoImageMetadata getNiso () - { - return _niso; + if (_compMapProperty != null) { + propList.add(_compMapProperty); } - - /** Assign a List of Tile objects to the tiles field */ - public void setTiles (List tiles) - { - _tiles = tiles; + if (_paletteProperty != null) { + propList.add(_paletteProperty); } - - - /** Add a tile length property to the list of tile lengths. */ - public void addTileLength (Property p) - { - if (tileLengthList == null) { - tileLengthList = new LinkedList (); - } - tileLengthList.add (p); + if (_roiProperty != null) { + propList.add(_roiProperty); } - - /** Add a PPM tilepart header length to the list of lengths */ - public void addPPMLength (long len) - { - _ppmLengthList.add (new Long (len)); + if (_sizProperty != null) { + propList.add(_sizProperty); } - - - /** Sets the label property. */ - protected void setLabelProperty (Property p) - { - _labelProperty = p; + if (_codProperty != null) { + propList.add(_codProperty); } - - /** Sets the component mapping property. */ - protected void setCompMapProperty (Property p) - { - _compMapProperty = p; + if (_qcdProperty != null) { + propList.add(_qcdProperty); } - - /** Sets the palette property. */ - protected void setPaletteProperty (Property p) - { - _paletteProperty = p; + if (_pocProperty != null) { + propList.add(_pocProperty); } - - /** Sets the ROI property. */ - protected void setROIProperty (Property p) - { - _roiProperty = p; + if (tileLengthList != null && !tileLengthList.isEmpty()) { + propList.add( + new Property("TileLengths", PropertyType.PROPERTY, PropertyArity.LIST, tileLengthList)); } - - /** Sets the SIZ property. */ - protected void setSIZProperty (Property p) - { - _sizProperty = p; + if (_packetLengthList != null && !_packetLengthList.isEmpty()) { + propList.add( + new Property("PacketLengths", PropertyType.LONG, PropertyArity.LIST, _packetLengthList)); } - - /* Sets the CRG property. */ - protected void setCRGProperty (Property p) - { - _crgProperty = p; + if (_ppmLengthList != null && !_ppmLengthList.isEmpty()) { + propList.add( + new Property( + "PackedPacketHeaderLengths", PropertyType.LONG, PropertyArity.LIST, _ppmLengthList)); } - - - - /** Set a property indexed by component. - * If a property for that component doesn't already - * exist, it is created. prop is then - * added to the property list of that property. */ - @Override - public void setCompProperty (int idx, Property prop) - { - if (_components != null && _components.length > idx) { - if (_components[idx] == null) { - // Have to create the component property - _components[idx] = new Property ("Component", - PropertyType.PROPERTY, - PropertyArity.LIST, - new LinkedList ()); - } - List pList = (List) _components[idx].getValue (); - pList.add (prop); - } + if (_crgProperty != null) { + propList.add(_crgProperty); } - - + propList.add(new Property("NisoImageMetadata", PropertyType.NISOIMAGEMETADATA, _niso)); + if (_tiles != null && !_tiles.isEmpty()) { + List tpList = new ArrayList(_tiles.size()); + ListIterator iter = _tiles.listIterator(); + while (iter.hasNext()) { + Tile t = (Tile) iter.next(); + tpList.add(t.makeProperty()); + } + propList.add(new Property("Tiles", PropertyType.PROPERTY, PropertyArity.LIST, tpList)); + } + if (!_comments.isEmpty()) { + propList.add(new Property("Comments", PropertyType.PROPERTY, PropertyArity.LIST, _comments)); + } + if (_components != null) { + // The component array may be only partially populated, or + // not at all, so we reduce it to a List. + List clist = new ArrayList(_components.length); + for (int i = 0; i < _components.length; i++) { + Property c = _components[i]; + if (c != null) { + clist.add(c); + } + } + if (!clist.isEmpty()) { + propList.add(new Property("Components", PropertyType.PROPERTY, PropertyArity.LIST, clist)); + } + } + return new Property("Codestream", PropertyType.PROPERTY, PropertyArity.LIST, propList); + } + + /** + * Set the initial Niso values from a default Niso object. This doesn't attempt to be complete, + * but sets the values which we know could have been set from the JP2 header. + */ + public void setDefaultNiso(NisoImageMetadata dNiso) { + _niso.setByteOrder(dNiso.getByteOrder()); + _niso.setMimeType(dNiso.getMimeType()); + _niso.setBitsPerSample(dNiso.getBitsPerSample()); + _niso.setImageLength(dNiso.getImageLength()); + _niso.setImageWidth(dNiso.getImageWidth()); + _niso.setSamplesPerPixel(dNiso.getSamplesPerPixel()); + _niso.setCompressionScheme(dNiso.getCompressionScheme()); + _niso.setYSamplingFrequency(dNiso.getYSamplingFrequency()); + _niso.setXSamplingFrequency(dNiso.getXSamplingFrequency()); + _niso.setSamplingFrequencyUnit(dNiso.getSamplingFrequencyUnit()); + } + + /** Returns the images NisoImageMetadata. */ + public NisoImageMetadata getNiso() { + return _niso; + } + + /** Assign a List of Tile objects to the tiles field */ + public void setTiles(List tiles) { + _tiles = tiles; + } + + /** Add a tile length property to the list of tile lengths. */ + public void addTileLength(Property p) { + if (tileLengthList == null) { + tileLengthList = new LinkedList(); + } + tileLengthList.add(p); + } + + /** Add a PPM tilepart header length to the list of lengths */ + public void addPPMLength(long len) { + _ppmLengthList.add(new Long(len)); + } + + /** Sets the label property. */ + protected void setLabelProperty(Property p) { + _labelProperty = p; + } + + /** Sets the component mapping property. */ + protected void setCompMapProperty(Property p) { + _compMapProperty = p; + } + + /** Sets the palette property. */ + protected void setPaletteProperty(Property p) { + _paletteProperty = p; + } + + /** Sets the ROI property. */ + protected void setROIProperty(Property p) { + _roiProperty = p; + } + + /** Sets the SIZ property. */ + protected void setSIZProperty(Property p) { + _sizProperty = p; + } + + /* Sets the CRG property. */ + protected void setCRGProperty(Property p) { + _crgProperty = p; + } + + /** + * Set a property indexed by component. If a property for that component doesn't already exist, it + * is created. prop is then added to the property list of that property. + */ + @Override + public void setCompProperty(int idx, Property prop) { + if (_components != null && _components.length > idx) { + if (_components[idx] == null) { + // Have to create the component property + _components[idx] = + new Property("Component", PropertyType.PROPERTY, PropertyArity.LIST, new LinkedList()); + } + List pList = (List) _components[idx].getValue(); + pList.add(prop); + } + } } diff --git a/jhove-modules/jpeg2000-hul/src/main/java/edu/harvard/hul/ois/jhove/module/jpeg2000/CodestreamHeaderBox.java b/jhove-modules/jpeg2000-hul/src/main/java/edu/harvard/hul/ois/jhove/module/jpeg2000/CodestreamHeaderBox.java index c36d5da1a..b2b411015 100644 --- a/jhove-modules/jpeg2000-hul/src/main/java/edu/harvard/hul/ois/jhove/module/jpeg2000/CodestreamHeaderBox.java +++ b/jhove-modules/jpeg2000-hul/src/main/java/edu/harvard/hul/ois/jhove/module/jpeg2000/CodestreamHeaderBox.java @@ -1,124 +1,112 @@ -/********************************************************************** - * Jhove - JSTOR/Harvard Object Validation Environment - * Copyright 2004 by JSTOR and the President and Fellows of Harvard College +/** + * ******************************************************************** Jhove - JSTOR/Harvard Object + * Validation Environment Copyright 2004 by JSTOR and the President and Fellows of Harvard College * - **********************************************************************/ - + *

******************************************************************** + */ package edu.harvard.hul.ois.jhove.module.jpeg2000; -import java.io.*; import edu.harvard.hul.ois.jhove.*; +import java.io.*; /** - * Codestream Header box. - * This is similar to a JP2HeaderBox, and has many of the same subboxes, - * but applies to a single codestream. - * - * See ISO/IEC FCD15444-2: 2000, L.9.3 - * - * @author Gary McGath + * Codestream Header box. This is similar to a JP2HeaderBox, and has many of the same subboxes, but + * applies to a single codestream. + * + *

See ISO/IEC FCD15444-2: 2000, L.9.3 * + * @author Gary McGath */ public class CodestreamHeaderBox extends JP2Box { - private Codestream curCodestream; - + private Codestream curCodestream; - /** - * Constructor with superbox. - * - * @param parent Must be null or the TopLevelBoxHolder - */ - public CodestreamHeaderBox(RandomAccessFile raf, BoxHolder parent) { - super(raf, parent); + /** + * Constructor with superbox. + * + * @param parent Must be null or the TopLevelBoxHolder + */ + public CodestreamHeaderBox(RandomAccessFile raf, BoxHolder parent) { + super(raf, parent); + } + + /** + * Reads the box, putting appropriate information in the RepInfo object. setModule, setBoxHeader, + * setRepInfo and setDataInputStream must be called before readBox is called. + * readBox must completely consume the box, so that the next byte to be read by the + * DataInputStream is the FF byte of the next Box. + */ + @Override + public boolean readBox() throws IOException { + if (_parentBox != null) { + wrongBoxContext(); + return false; } + initBytesRead(); + hasBoxes = true; - /** Reads the box, putting appropriate information in - * the RepInfo object. setModule, setBoxHeader, - * setRepInfo and setDataInputStream must be called - * before readBox is called. - * readBox must completely consume the - * box, so that the next byte to be read by the - * DataInputStream is the FF byte of the next Box. - */ - @Override - public boolean readBox() throws IOException { - if (_parentBox != null) { - wrongBoxContext(); - return false; - } - initBytesRead (); - hasBoxes = true; + int nch = _module.getNCodestreamHeaders() + 1; + _module.setNCodestreams(nch); + curCodestream = _module.getCodestream(nch); - int nch = _module.getNCodestreamHeaders () + 1; - _module.setNCodestreams (nch); - curCodestream = _module.getCodestream (nch); + int state = 0; // state variable for checking progress of boxes + JP2Box box = null; + while (hasNext()) { + box = (JP2Box) next(); + if (state == 0 && box instanceof LabelBox) { + state = 1; + if (!box.readBox()) { + return false; + } + curCodestream.setLabelProperty( + new Property("Label", PropertyType.STRING, ((LabelBox) box).getLabel())); - int state = 0; // state variable for checking progress of boxes - JP2Box box = null; - while (hasNext ()) { - box = (JP2Box) next (); - if (state == 0 && box instanceof LabelBox) { - state = 1; - if (!box.readBox ()) { - return false; - } - curCodestream.setLabelProperty (new Property ("Label", - PropertyType.STRING, - ((LabelBox) box).getLabel ())); - - // Read the next box - box = (JP2Box) next (); - if (box == null) { - break; - } - } - // First box, except perhaps for the label box, - // is the image header. - else if (state <= 1) { - if (box instanceof ImageHeaderBox) { - state = 2; - if (!box.readBox ()) { - return false; - } - } - else { - _repInfo.setMessage (new ErrorMessage - (MessageConstants.JPEG2000_HUL_7, - _module.getFilePos ())); - _repInfo.setWellFormed (false); - return false; - } - } - else { - if (box instanceof BPCCBox || - box instanceof PaletteBox || - box instanceof ComponentMapBox || - box instanceof ROIBox) { - if (!box.readBox ()) { - return false; - } - } - else { - // Other boxes are legal; skip over them - box.skipBox (); - } - } + // Read the next box + box = (JP2Box) next(); + if (box == null) { + break; } - finalizeBytesRead (); - return true; - } - - /** Returns the associated Codestream object. */ - protected Codestream getCodestream () - { - return curCodestream; + } + // First box, except perhaps for the label box, + // is the image header. + else if (state <= 1) { + if (box instanceof ImageHeaderBox) { + state = 2; + if (!box.readBox()) { + return false; + } + } else { + _repInfo.setMessage( + new ErrorMessage(MessageConstants.JPEG2000_HUL_7, _module.getFilePos())); + _repInfo.setWellFormed(false); + return false; + } + } else { + if (box instanceof BPCCBox + || box instanceof PaletteBox + || box instanceof ComponentMapBox + || box instanceof ROIBox) { + if (!box.readBox()) { + return false; + } + } else { + // Other boxes are legal; skip over them + box.skipBox(); + } + } } + finalizeBytesRead(); + return true; + } - /** Returns the name of the Box. */ - @Override - protected String getSelfPropName () - { - return "Codestream Header Box"; - } + /** Returns the associated Codestream object. */ + protected Codestream getCodestream() { + return curCodestream; + } + + /** Returns the name of the Box. */ + @Override + protected String getSelfPropName() { + return "Codestream Header Box"; + } } diff --git a/jhove-modules/jpeg2000-hul/src/main/java/edu/harvard/hul/ois/jhove/module/jpeg2000/CodestreamRegBox.java b/jhove-modules/jpeg2000-hul/src/main/java/edu/harvard/hul/ois/jhove/module/jpeg2000/CodestreamRegBox.java index b3418c715..1e2b6f1f5 100644 --- a/jhove-modules/jpeg2000-hul/src/main/java/edu/harvard/hul/ois/jhove/module/jpeg2000/CodestreamRegBox.java +++ b/jhove-modules/jpeg2000-hul/src/main/java/edu/harvard/hul/ois/jhove/module/jpeg2000/CodestreamRegBox.java @@ -1,95 +1,100 @@ -/********************************************************************** - * Jhove - JSTOR/Harvard Object Validation Environment - * Copyright 2004 by JSTOR and the President and Fellows of Harvard College +/** + * ******************************************************************** Jhove - JSTOR/Harvard Object + * Validation Environment Copyright 2004 by JSTOR and the President and Fellows of Harvard College * - **********************************************************************/ - + *

******************************************************************** + */ package edu.harvard.hul.ois.jhove.module.jpeg2000; -import java.io.*; import edu.harvard.hul.ois.jhove.*; +import java.io.*; /** - * Codestream Registration Box. - * See ISO/IEC FCD15444-2: 2000, L.9.4.7 - * - * @author Gary McGath + * Codestream Registration Box. See ISO/IEC FCD15444-2: 2000, L.9.4.7 * + * @author Gary McGath */ public class CodestreamRegBox extends JP2Box { - /** - * Constructor with superbox. - * - * @param parent parent superbox of this box - * (must be a ComposLayerHdrBox) - */ - public CodestreamRegBox(RandomAccessFile raf, BoxHolder parent) { - super(raf, parent); + /** + * Constructor with superbox. + * + * @param parent parent superbox of this box (must be a ComposLayerHdrBox) + */ + public CodestreamRegBox(RandomAccessFile raf, BoxHolder parent) { + super(raf, parent); + } + + /** + * Reads the box, putting appropriate information in the RepInfo object. setModule, setBoxHeader, + * setRepInfo and setDataInputStream must be called before readBox is called. + * readBox must completely consume the box, so that the next byte to be read by the + * DataInputStream is the FF byte of the next Box. + */ + @Override + public boolean readBox() throws IOException { + if (!(_parentBox instanceof ComposLayerHdrBox)) { + wrongBoxContext(); + return false; } + Property[] propArray = new Property[3]; + propArray[0] = + new Property( + "HorizontalGridSize", + PropertyType.INTEGER, + new Integer(_module.readUnsignedShort(_dstrm))); + propArray[1] = + new Property( + "VerticalGridSize", + PropertyType.INTEGER, + new Integer(_module.readUnsignedShort(_dstrm))); + int bytesLeft = (int) _boxHeader.getDataLength() - 4; - /** Reads the box, putting appropriate information in - * the RepInfo object. setModule, setBoxHeader, - * setRepInfo and setDataInputStream must be called - * before readBox is called. - * readBox must completely consume the - * box, so that the next byte to be read by the - * DataInputStream is the FF byte of the next Box. - */ - @Override - public boolean readBox() throws IOException { - if (!(_parentBox instanceof ComposLayerHdrBox)) { - wrongBoxContext(); - return false; - } - Property[] propArray = new Property[3]; - propArray[0] = new Property ("HorizontalGridSize", - PropertyType.INTEGER, - new Integer (_module.readUnsignedShort (_dstrm))); - propArray[1] = new Property ("VerticalGridSize", - PropertyType.INTEGER, - new Integer (_module.readUnsignedShort (_dstrm))); - int bytesLeft = (int) _boxHeader.getDataLength() - 4; - - // Each codestream entry is 6 bytes long (a short and 4 bytes) - int nStreams = bytesLeft / 6; - Property[] streamsProp = new Property[nStreams]; - for (int i = 0; i < nStreams; i++) { - // Build a property for one codestream - Property[] csProp = new Property[5]; - csProp[0] = new Property ("CodestreamNumber", - PropertyType.INTEGER, - new Integer (_module.readUnsignedShort (_dstrm))); - csProp[1] = new Property ("HorizontalResolution", - PropertyType.INTEGER, - new Integer (ModuleBase.readUnsignedByte (_dstrm, _module))); - csProp[2] = new Property ("VerticalResolution", - PropertyType.INTEGER, - new Integer (ModuleBase.readUnsignedByte (_dstrm, _module))); - csProp[3] = new Property ("HorizontalOffset", - PropertyType.INTEGER, - new Integer (ModuleBase.readUnsignedByte (_dstrm, _module))); - csProp[4] = new Property ("VerticalOffset", - PropertyType.INTEGER, - new Integer (ModuleBase.readUnsignedByte (_dstrm, _module))); + // Each codestream entry is 6 bytes long (a short and 4 bytes) + int nStreams = bytesLeft / 6; + Property[] streamsProp = new Property[nStreams]; + for (int i = 0; i < nStreams; i++) { + // Build a property for one codestream + Property[] csProp = new Property[5]; + csProp[0] = + new Property( + "CodestreamNumber", + PropertyType.INTEGER, + new Integer(_module.readUnsignedShort(_dstrm))); + csProp[1] = + new Property( + "HorizontalResolution", + PropertyType.INTEGER, + new Integer(ModuleBase.readUnsignedByte(_dstrm, _module))); + csProp[2] = + new Property( + "VerticalResolution", + PropertyType.INTEGER, + new Integer(ModuleBase.readUnsignedByte(_dstrm, _module))); + csProp[3] = + new Property( + "HorizontalOffset", + PropertyType.INTEGER, + new Integer(ModuleBase.readUnsignedByte(_dstrm, _module))); + csProp[4] = + new Property( + "VerticalOffset", + PropertyType.INTEGER, + new Integer(ModuleBase.readUnsignedByte(_dstrm, _module))); - streamsProp[i] = new Property ("Codestreams", - PropertyType.PROPERTY, - PropertyArity.ARRAY, - csProp); - } - ((ComposLayerHdrBox) _parentBox).addCodestreamReg (new Property - ("CodestreamRegistration", - PropertyType.PROPERTY, - PropertyArity.ARRAY, - streamsProp)); - return true; + streamsProp[i] = + new Property("Codestreams", PropertyType.PROPERTY, PropertyArity.ARRAY, csProp); } + ((ComposLayerHdrBox) _parentBox) + .addCodestreamReg( + new Property( + "CodestreamRegistration", PropertyType.PROPERTY, PropertyArity.ARRAY, streamsProp)); + return true; + } - /** Returns the name of the Box. */ - @Override - protected String getSelfPropName () - { - return "Codestream Registration Box"; - } + /** Returns the name of the Box. */ + @Override + protected String getSelfPropName() { + return "Codestream Registration Box"; + } } diff --git a/jhove-modules/jpeg2000-hul/src/main/java/edu/harvard/hul/ois/jhove/module/jpeg2000/ColorGroupBox.java b/jhove-modules/jpeg2000-hul/src/main/java/edu/harvard/hul/ois/jhove/module/jpeg2000/ColorGroupBox.java index 9c5d0cba8..762b39bf4 100644 --- a/jhove-modules/jpeg2000-hul/src/main/java/edu/harvard/hul/ois/jhove/module/jpeg2000/ColorGroupBox.java +++ b/jhove-modules/jpeg2000-hul/src/main/java/edu/harvard/hul/ois/jhove/module/jpeg2000/ColorGroupBox.java @@ -1,82 +1,72 @@ -/********************************************************************** - * Jhove - JSTOR/Harvard Object Validation Environment - * Copyright 2004 by JSTOR and the President and Fellows of Harvard College +/** + * ******************************************************************** Jhove - JSTOR/Harvard Object + * Validation Environment Copyright 2004 by JSTOR and the President and Fellows of Harvard College * - **********************************************************************/ - + *

******************************************************************** + */ package edu.harvard.hul.ois.jhove.module.jpeg2000; import edu.harvard.hul.ois.jhove.*; import java.io.*; /** - * Color Group Box. - * See ISO/IEC FCD15444-2: 2000, L.9.4.1 - * - * - * @author Gary McGath + * Color Group Box. See ISO/IEC FCD15444-2: 2000, L.9.4.1 * + * @author Gary McGath */ public class ColorGroupBox extends JP2Box { - /** - * Constructor with superbox. - * - * @param parent parent superbox of this box - * (must be a ComposLayerHdrBox) - */ - public ColorGroupBox(RandomAccessFile raf, BoxHolder parent) { - super(raf, parent); - } + /** + * Constructor with superbox. + * + * @param parent parent superbox of this box (must be a ComposLayerHdrBox) + */ + public ColorGroupBox(RandomAccessFile raf, BoxHolder parent) { + super(raf, parent); + } - /** Reads the box, putting appropriate information in - * the RepInfo object. setModule, setBoxHeader, - * setRepInfo and setDataInputStream must be called - * before readBox is called. - * readBox must completely consume the - * box, so that the next byte to be read by the - * DataInputStream is the FF byte of the next Box. - */ - @Override - public boolean readBox() throws IOException { - // A Color Group box consists of 0 or more color specification - // boxes. It is allowed only in a Compositing Layer Header box. - if (!(_parentBox instanceof ComposLayerHdrBox)) { - wrongBoxContext(); - return false; - } - initBytesRead (); - JP2Box box = null; - while (hasNext ()) { - box = (JP2Box) next (); - if (box == null) { - break; - } - if (box instanceof ColorSpecBox) { - if (!box.readBox ()) { - return false; - } - } - else { - box.skipBox (); - } + /** + * Reads the box, putting appropriate information in the RepInfo object. setModule, setBoxHeader, + * setRepInfo and setDataInputStream must be called before readBox is called. + * readBox must completely consume the box, so that the next byte to be read by the + * DataInputStream is the FF byte of the next Box. + */ + @Override + public boolean readBox() throws IOException { + // A Color Group box consists of 0 or more color specification + // boxes. It is allowed only in a Compositing Layer Header box. + if (!(_parentBox instanceof ComposLayerHdrBox)) { + wrongBoxContext(); + return false; + } + initBytesRead(); + JP2Box box = null; + while (hasNext()) { + box = (JP2Box) next(); + if (box == null) { + break; + } + if (box instanceof ColorSpecBox) { + if (!box.readBox()) { + return false; } - - finalizeBytesRead (); - return false; + } else { + box.skipBox(); + } } - /** Adds a color spec property to the parent Compositing Layer - * Header Box. */ - protected void addColorSpec (Property p) - { - ((ComposLayerHdrBox) _parentBox).addColorSpec (p); - } + finalizeBytesRead(); + return false; + } - /** Returns the name of the Box. */ - @Override - protected String getSelfPropName () - { - return "Color Group Box"; - } + /** Adds a color spec property to the parent Compositing Layer Header Box. */ + protected void addColorSpec(Property p) { + ((ComposLayerHdrBox) _parentBox).addColorSpec(p); + } + + /** Returns the name of the Box. */ + @Override + protected String getSelfPropName() { + return "Color Group Box"; + } } diff --git a/jhove-modules/jpeg2000-hul/src/main/java/edu/harvard/hul/ois/jhove/module/jpeg2000/ColorSpecBox.java b/jhove-modules/jpeg2000-hul/src/main/java/edu/harvard/hul/ois/jhove/module/jpeg2000/ColorSpecBox.java index 68847bd18..06c9dddb1 100644 --- a/jhove-modules/jpeg2000-hul/src/main/java/edu/harvard/hul/ois/jhove/module/jpeg2000/ColorSpecBox.java +++ b/jhove-modules/jpeg2000-hul/src/main/java/edu/harvard/hul/ois/jhove/module/jpeg2000/ColorSpecBox.java @@ -1,8 +1,8 @@ -/********************************************************************** - * Jhove - JSTOR/Harvard Object Validation Environment - * Copyright 2004 by JSTOR and the President and Fellows of Harvard College - **********************************************************************/ - +/** + * ******************************************************************** Jhove - JSTOR/Harvard Object + * Validation Environment Copyright 2004 by JSTOR and the President and Fellows of Harvard College + * ******************************************************************** + */ package edu.harvard.hul.ois.jhove.module.jpeg2000; import edu.harvard.hul.ois.jhove.*; @@ -10,167 +10,143 @@ import java.util.*; /** - * Color specification box. - * See I.5.3.3 in ISO/IEC 15444-1:2000 - * and ISO/IEC FCD15444-2: 2000, L.9.4.2 + * Color specification box. See I.5.3.3 in ISO/IEC 15444-1:2000 and ISO/IEC FCD15444-2: 2000, + * L.9.4.2 * * @author Gary McGath - * */ public class ColorSpecBox extends JP2Box { - /** - * Constructor with superbox. - * - * @param parent parent superbox of this box - * (must be JP2HeaderBox or ColorGroupBox) - */ - public ColorSpecBox (RandomAccessFile raf, BoxHolder parent) - { - super (raf, parent); - } + /** + * Constructor with superbox. + * + * @param parent parent superbox of this box (must be JP2HeaderBox or ColorGroupBox) + */ + public ColorSpecBox(RandomAccessFile raf, BoxHolder parent) { + super(raf, parent); + } - /** Reads the box, putting appropriate information in - * the RepInfo object. setModule, setBoxHeader, - * setRepInfo and setDataInputStream must be called - * before readBox is called. - * readBox must completely consume the - * box, so that the next byte to be read by the - * DataInputStream is the FF byte of the next Box. - */ - @Override - public boolean readBox() throws IOException { - if (!(_parentBox instanceof JP2HeaderBox || - _parentBox instanceof ColorGroupBox)) { - wrongBoxContext(); - return false; - } - initBytesRead (); - int len = (int) _boxHeader.getDataLength (); + /** + * Reads the box, putting appropriate information in the RepInfo object. setModule, setBoxHeader, + * setRepInfo and setDataInputStream must be called before readBox is called. + * readBox must completely consume the box, so that the next byte to be read by the + * DataInputStream is the FF byte of the next Box. + */ + @Override + public boolean readBox() throws IOException { + if (!(_parentBox instanceof JP2HeaderBox || _parentBox instanceof ColorGroupBox)) { + wrongBoxContext(); + return false; + } + initBytesRead(); + int len = (int) _boxHeader.getDataLength(); - List subProps = new ArrayList (2); - Property prop = new Property ("ColorSpec", - PropertyType.PROPERTY, - PropertyArity.LIST, - subProps); - int meth = ModuleBase.readUnsignedByte (_dstrm, _module); - if (meth > 2) { - _module.setJP2Compliant (false); // only 1-2 recognized by JP2 - } - subProps.add (_module.addIntegerProperty("Method", meth, - JP2Strings.methodStr)); + List subProps = new ArrayList(2); + Property prop = new Property("ColorSpec", PropertyType.PROPERTY, PropertyArity.LIST, subProps); + int meth = ModuleBase.readUnsignedByte(_dstrm, _module); + if (meth > 2) { + _module.setJP2Compliant(false); // only 1-2 recognized by JP2 + } + subProps.add(_module.addIntegerProperty("Method", meth, JP2Strings.methodStr)); - // Precedence. Used in JPX only. - int prec = ModuleBase.readUnsignedByte (_dstrm, _module); - subProps.add (new Property ("Precedence", - PropertyType.INTEGER, - new Integer (prec))); - - // The approx field provides a litmus test for distinguishing - // a JP2 file from a JPX. JP2 may have only 0 for this value; - // JPX must have a non-zero value. - int approx = ModuleBase.readUnsignedByte (_dstrm, _module); - if (approx == 0) { - _module.setJPXCompliant (false); - subProps.add (new Property ("Approx", - PropertyType.INTEGER, - new Integer (0))); - } - else { - _module.setJP2Compliant (false); - subProps.add (_module.addIntegerProperty("Approx", approx, - JP2Strings.approxStr, - JP2Strings.approxIdx)); - } + // Precedence. Used in JPX only. + int prec = ModuleBase.readUnsignedByte(_dstrm, _module); + subProps.add(new Property("Precedence", PropertyType.INTEGER, new Integer(prec))); - if (meth == 1) { - // with meth = 1, we have an enumerated colorspace - long enumCS = _module.readUnsignedInt (_dstrm); - _module.skipBytes (_dstrm, len - 7, _module); - Property p; - p = _module.addIntegerProperty ("EnumCS", (int) enumCS, - JP2Strings.enumCSStr); - subProps.add (p); - } - else if (meth == 2 ) { - // Code by Justin Littman incorporated here. - // With meth = 2, the profile must be either a Monochrome - // Input or a Three-Component Matrix-Based Input profile, - // as defined in ICC.1:1998-09. - //Read the ICC profile - //Skip the header (128 bytes) - _module.skipBytes(_dstrm, 128, _module); - //Tag count - long tagCount = _module.readUnsignedInt(_dstrm); - HashSet tagSignatureSet = new HashSet(); - for (int i=0; i < tagCount; i++) { - //Read the tag - tagSignatureSet.add(_module.read4Chars(_dstrm)); - //Skip the rest of the tag table entry - _module.skipBytes(_dstrm, 8, _module); - } - - //Check if Monochrome Input Profile - if (tagSignatureSet.contains("desc") && - tagSignatureSet.contains("kTRC") && - tagSignatureSet.contains("wtpt") && - tagSignatureSet.contains("cprt")) { - subProps.add (new Property ("RestrictedICCProfile", - PropertyType.STRING, - "Monochrome Input Profile")); - } - //Check if Three-Component Matrix-Based Input Profile - else if (tagSignatureSet.contains("desc") && - tagSignatureSet.contains("rXYZ") && - tagSignatureSet.contains("gXYZ") && - tagSignatureSet.contains("bXYZ") && - tagSignatureSet.contains("rTRC") && - tagSignatureSet.contains("gTRC") && - tagSignatureSet.contains("bTRC") && - tagSignatureSet.contains("wtpt") && - tagSignatureSet.contains("cprt")) { - subProps.add (new Property ("RestrictedICCProfile", - PropertyType.STRING, - "Three-Component Matrix-Based Input Profile")); - } - //Not a valid method 2 box - else { - //_module.setJP2Compliant (false); - _repInfo.setMessage(new ErrorMessage - (MessageConstants.JPEG2000_HUL_10, - filePos)); - _repInfo.setValid(false); - } - } - else { - // We have an ICC profile, or else a method which isn't - // defined in the specification. This excludes it - // from the JP2 profile. - _module.setJP2Compliant (false); - } - // If it's in a JP2 Header, add to the default color specs. - // If it's in a Color Group Box, add to Compositing Layer - // properties. - if (_parentBox instanceof JP2HeaderBox) { - _module.addColorSpec (prop); - } - else if (_parentBox instanceof ColorGroupBox) { - ((ColorGroupBox) _parentBox).addColorSpec (prop); - } - // Skip any bytes we haven't read - if (_boxHeader.getLength () != 0) { - _module.skipBytes (_dstrm, - (int) (len - (_module.getFilePos () - startBytesRead)), _module); - } - finalizeBytesRead (); - _module.setColorSpecSeen (true); - return true; + // The approx field provides a litmus test for distinguishing + // a JP2 file from a JPX. JP2 may have only 0 for this value; + // JPX must have a non-zero value. + int approx = ModuleBase.readUnsignedByte(_dstrm, _module); + if (approx == 0) { + _module.setJPXCompliant(false); + subProps.add(new Property("Approx", PropertyType.INTEGER, new Integer(0))); + } else { + _module.setJP2Compliant(false); + subProps.add( + _module.addIntegerProperty("Approx", approx, JP2Strings.approxStr, JP2Strings.approxIdx)); } - /** Returns the name of the Box. */ - @Override - protected String getSelfPropName () - { - return "Color Specification Box"; + if (meth == 1) { + // with meth = 1, we have an enumerated colorspace + long enumCS = _module.readUnsignedInt(_dstrm); + _module.skipBytes(_dstrm, len - 7, _module); + Property p; + p = _module.addIntegerProperty("EnumCS", (int) enumCS, JP2Strings.enumCSStr); + subProps.add(p); + } else if (meth == 2) { + // Code by Justin Littman incorporated here. + // With meth = 2, the profile must be either a Monochrome + // Input or a Three-Component Matrix-Based Input profile, + // as defined in ICC.1:1998-09. + // Read the ICC profile + // Skip the header (128 bytes) + _module.skipBytes(_dstrm, 128, _module); + // Tag count + long tagCount = _module.readUnsignedInt(_dstrm); + HashSet tagSignatureSet = new HashSet(); + for (int i = 0; i < tagCount; i++) { + // Read the tag + tagSignatureSet.add(_module.read4Chars(_dstrm)); + // Skip the rest of the tag table entry + _module.skipBytes(_dstrm, 8, _module); + } + + // Check if Monochrome Input Profile + if (tagSignatureSet.contains("desc") + && tagSignatureSet.contains("kTRC") + && tagSignatureSet.contains("wtpt") + && tagSignatureSet.contains("cprt")) { + subProps.add( + new Property("RestrictedICCProfile", PropertyType.STRING, "Monochrome Input Profile")); + } + // Check if Three-Component Matrix-Based Input Profile + else if (tagSignatureSet.contains("desc") + && tagSignatureSet.contains("rXYZ") + && tagSignatureSet.contains("gXYZ") + && tagSignatureSet.contains("bXYZ") + && tagSignatureSet.contains("rTRC") + && tagSignatureSet.contains("gTRC") + && tagSignatureSet.contains("bTRC") + && tagSignatureSet.contains("wtpt") + && tagSignatureSet.contains("cprt")) { + subProps.add( + new Property( + "RestrictedICCProfile", + PropertyType.STRING, + "Three-Component Matrix-Based Input Profile")); + } + // Not a valid method 2 box + else { + // _module.setJP2Compliant (false); + _repInfo.setMessage(new ErrorMessage(MessageConstants.JPEG2000_HUL_10, filePos)); + _repInfo.setValid(false); + } + } else { + // We have an ICC profile, or else a method which isn't + // defined in the specification. This excludes it + // from the JP2 profile. + _module.setJP2Compliant(false); + } + // If it's in a JP2 Header, add to the default color specs. + // If it's in a Color Group Box, add to Compositing Layer + // properties. + if (_parentBox instanceof JP2HeaderBox) { + _module.addColorSpec(prop); + } else if (_parentBox instanceof ColorGroupBox) { + ((ColorGroupBox) _parentBox).addColorSpec(prop); } + // Skip any bytes we haven't read + if (_boxHeader.getLength() != 0) { + _module.skipBytes(_dstrm, (int) (len - (_module.getFilePos() - startBytesRead)), _module); + } + finalizeBytesRead(); + _module.setColorSpecSeen(true); + return true; + } + + /** Returns the name of the Box. */ + @Override + protected String getSelfPropName() { + return "Color Specification Box"; + } } diff --git a/jhove-modules/jpeg2000-hul/src/main/java/edu/harvard/hul/ois/jhove/module/jpeg2000/CommentMarkerSegment.java b/jhove-modules/jpeg2000-hul/src/main/java/edu/harvard/hul/ois/jhove/module/jpeg2000/CommentMarkerSegment.java index 6e9f25f50..5f22a633c 100644 --- a/jhove-modules/jpeg2000-hul/src/main/java/edu/harvard/hul/ois/jhove/module/jpeg2000/CommentMarkerSegment.java +++ b/jhove-modules/jpeg2000-hul/src/main/java/edu/harvard/hul/ois/jhove/module/jpeg2000/CommentMarkerSegment.java @@ -1,72 +1,57 @@ -/********************************************************************** - * Jhove - JSTOR/Harvard Object Validation Environment - * Copyright 2004 by JSTOR and the President and Fellows of Harvard College - **********************************************************************/ - +/** + * ******************************************************************** Jhove - JSTOR/Harvard Object + * Validation Environment Copyright 2004 by JSTOR and the President and Fellows of Harvard College + * ******************************************************************** + */ package edu.harvard.hul.ois.jhove.module.jpeg2000; -import java.io.IOException; import edu.harvard.hul.ois.jhove.*; +import java.io.IOException; /** - * Class for the COM (Comment) marker segment. - * This comes either in the main header or - * after an SOT. + * Class for the COM (Comment) marker segment. This comes either in the main header or after an SOT. * * @author Gary McGath - * */ public class CommentMarkerSegment extends MarkerSegment { - /** - * Constructor. - */ - public CommentMarkerSegment() { - super(); - } + /** Constructor. */ + public CommentMarkerSegment() { + super(); + } - /** Process the marker segment. The DataInputStream - * will be at the point of having read the marker code. The - * process method must consume exactly the number - * of bytes remaining in the marker segment. - * - * @param bytesToEat The number of bytes that must be consumed. - * If it is 0 for a MarkerSegment, the - * number of bytes to consume is unknown. - * - * @return true if segment is well-formed, - * false otherwise. - */ - @Override - protected boolean process(int bytesToEat) throws IOException { - MainOrTile cs = getMainOrTile (); - int rcom = _module.readUnsignedShort (_dstream); - Property prop; - byte[] byteBuf = new byte[bytesToEat - 2]; - ModuleBase.readByteBuf (_dstream, byteBuf, _module); - switch (rcom) { - case 0: - // Binary comment - prop = new Property ("Comment", - PropertyType.BYTE, - PropertyArity.ARRAY, - byteBuf); - break; - - case 1: - // ISO Latin comment - prop = new Property ("Comment", - PropertyType.STRING, - new String (byteBuf)); - break; - - default: - _repInfo.setMessage( (new ErrorMessage - (MessageConstants.JPEG2000_HUL_13))); - return false; // other values are reserved - } - cs.addComment (prop); - return true; - } + /** + * Process the marker segment. The DataInputStream will be at the point of having read the marker + * code. The process method must consume exactly the number of bytes remaining in the + * marker segment. + * + * @param bytesToEat The number of bytes that must be consumed. If it is 0 for a MarkerSegment, + * the number of bytes to consume is unknown. + * @return true if segment is well-formed, false otherwise. + */ + @Override + protected boolean process(int bytesToEat) throws IOException { + MainOrTile cs = getMainOrTile(); + int rcom = _module.readUnsignedShort(_dstream); + Property prop; + byte[] byteBuf = new byte[bytesToEat - 2]; + ModuleBase.readByteBuf(_dstream, byteBuf, _module); + switch (rcom) { + case 0: + // Binary comment + prop = new Property("Comment", PropertyType.BYTE, PropertyArity.ARRAY, byteBuf); + break; + case 1: + // ISO Latin comment + prop = new Property("Comment", PropertyType.STRING, new String(byteBuf)); + break; + + default: + _repInfo.setMessage((new ErrorMessage(MessageConstants.JPEG2000_HUL_13))); + return false; // other values are reserved + } + cs.addComment(prop); + return true; + } } diff --git a/jhove-modules/jpeg2000-hul/src/main/java/edu/harvard/hul/ois/jhove/module/jpeg2000/CompOptionsBox.java b/jhove-modules/jpeg2000-hul/src/main/java/edu/harvard/hul/ois/jhove/module/jpeg2000/CompOptionsBox.java index 457f393ca..de40e6b2f 100644 --- a/jhove-modules/jpeg2000-hul/src/main/java/edu/harvard/hul/ois/jhove/module/jpeg2000/CompOptionsBox.java +++ b/jhove-modules/jpeg2000-hul/src/main/java/edu/harvard/hul/ois/jhove/module/jpeg2000/CompOptionsBox.java @@ -1,58 +1,53 @@ -/********************************************************************** - * Jhove - JSTOR/Harvard Object Validation Environment - * Copyright 2004 by JSTOR and the President and Fellows of Harvard College - **********************************************************************/ - +/** + * ******************************************************************** Jhove - JSTOR/Harvard Object + * Validation Environment Copyright 2004 by JSTOR and the President and Fellows of Harvard College + * ******************************************************************** + */ package edu.harvard.hul.ois.jhove.module.jpeg2000; import java.io.*; /** * Composition options box (JPX). - * - * See ISO/IEC FCD15444-2: 2000, L.9.10.1 - * - * @author Gary McGath * + *

See ISO/IEC FCD15444-2: 2000, L.9.10.1 + * + * @author Gary McGath */ public class CompOptionsBox extends JP2Box { + /** + * Constructor with superbox. + * + * @param parent parent superbox of this box + */ + public CompOptionsBox(RandomAccessFile raf, BoxHolder parent) { + super(raf, parent); + } - /** - * Constructor with superbox. - * - * @param parent parent superbox of this box - */ - public CompOptionsBox(RandomAccessFile raf, BoxHolder parent) { - super(raf, parent); + /** + * Reads the box, putting appropriate information in the RepInfo object. setModule, setBoxHeader, + * setRepInfo and setDataInputStream must be called before readBox is called. + * readBox must completely consume the box, so that the next byte to be read by the + * DataInputStream is the FF byte of the next Box. + */ + @Override + public boolean readBox() throws IOException { + if (!(_parentBox instanceof CompositionBox)) { + wrongBoxContext(); + return false; } - - /** Reads the box, putting appropriate information in - * the RepInfo object. setModule, setBoxHeader, - * setRepInfo and setDataInputStream must be called - * before readBox is called. - * readBox must completely consume the - * box, so that the next byte to be read by the - * DataInputStream is the FF byte of the next Box. - */ - @Override - public boolean readBox() throws IOException { - if (!(_parentBox instanceof CompositionBox)) { - wrongBoxContext (); - return false; - } - initBytesRead (); - if (_boxHeader.getDataLength () != 10) { - wrongBoxSize (); - return false; - } - CompositionBox parent = (CompositionBox) _parentBox; - parent.setHeight (_module.readUnsignedInt (_dstrm)); - parent.setWidth (_module.readUnsignedInt (_dstrm)); - parent.setLoop (_module.readUnsignedShort (_dstrm)); - - finalizeBytesRead (); - return true; + initBytesRead(); + if (_boxHeader.getDataLength() != 10) { + wrongBoxSize(); + return false; } + CompositionBox parent = (CompositionBox) _parentBox; + parent.setHeight(_module.readUnsignedInt(_dstrm)); + parent.setWidth(_module.readUnsignedInt(_dstrm)); + parent.setLoop(_module.readUnsignedShort(_dstrm)); + finalizeBytesRead(); + return true; + } } diff --git a/jhove-modules/jpeg2000-hul/src/main/java/edu/harvard/hul/ois/jhove/module/jpeg2000/ComponentMapBox.java b/jhove-modules/jpeg2000-hul/src/main/java/edu/harvard/hul/ois/jhove/module/jpeg2000/ComponentMapBox.java index 65422a1b9..a93d27d1d 100644 --- a/jhove-modules/jpeg2000-hul/src/main/java/edu/harvard/hul/ois/jhove/module/jpeg2000/ComponentMapBox.java +++ b/jhove-modules/jpeg2000-hul/src/main/java/edu/harvard/hul/ois/jhove/module/jpeg2000/ComponentMapBox.java @@ -1,99 +1,80 @@ -/********************************************************************** - * Jhove - JSTOR/Harvard Object Validation Environment - * Copyright 2004 by JSTOR and the President and Fellows of Harvard College - **********************************************************************/ - +/** + * ******************************************************************** Jhove - JSTOR/Harvard Object + * Validation Environment Copyright 2004 by JSTOR and the President and Fellows of Harvard College + * ******************************************************************** + */ package edu.harvard.hul.ois.jhove.module.jpeg2000; -import java.io.*; import edu.harvard.hul.ois.jhove.*; +import java.io.*; /** - * Component Mapping Box. - * See I.5.3.5 in ISO/IEC 15444-1:2000 + * Component Mapping Box. See I.5.3.5 in ISO/IEC 15444-1:2000 * * @author Gary McGath - * */ public class ComponentMapBox extends JP2Box { + /** + * Constructor with superbox. + * + * @param parent parent superbox of this box + */ + public ComponentMapBox(RandomAccessFile raf, BoxHolder parent) { + super(raf, parent); + } - /** - * Constructor with superbox. - * - * @param parent parent superbox of this box - */ - public ComponentMapBox (RandomAccessFile raf, BoxHolder parent) - { - super (raf, parent); + /** + * Reads the box, putting appropriate information in the RepInfo object. setModule, setBoxHeader, + * setRepInfo and setDataInputStream must be called before readBox is called. + * readBox must completely consume the box, so that the next byte to be read by the + * DataInputStream is the FF byte of the next Box. + */ + @Override + public boolean readBox() throws IOException { + if (!(_parentBox instanceof JP2HeaderBox || _parentBox instanceof CodestreamHeaderBox)) { + wrongBoxContext(); + return false; } + initBytesRead(); - /** Reads the box, putting appropriate information in - * the RepInfo object. setModule, setBoxHeader, - * setRepInfo and setDataInputStream must be called - * before readBox is called. - * readBox must completely consume the - * box, so that the next byte to be read by the - * DataInputStream is the FF byte of the next Box. - */ - @Override - public boolean readBox() throws IOException { - if (!(_parentBox instanceof JP2HeaderBox || - _parentBox instanceof CodestreamHeaderBox)) { - wrongBoxContext(); - return false; - } - initBytesRead (); + int len = (int) _boxHeader.getDataLength(); + int ncomp = len / 4; + Property[] parray = new Property[ncomp]; - int len = (int) _boxHeader.getDataLength (); - int ncomp = len / 4; - Property[] parray = new Property[ncomp]; - - // Build the array of properties for each component. - // Components potentially have lots of stuff attached - // to them, so probably I should define a Component - // class. Need to determine how the Component Mapping - // relates to component information in a codestream. - // Or maybe this doesn't have anything to do with it. - for (int i = 0; i < ncomp; i++) { - Property[] cprop = new Property[3]; - int index = _module.readUnsignedShort (_dstrm); - cprop[0] = new Property ("ComponentIndex", - PropertyType.INTEGER, - new Integer (index)); - int mtyp = ModuleBase.readUnsignedByte (_dstrm, _module); - cprop[1] = _module.addIntegerProperty ("MTyp", mtyp, - JP2Strings.mtypStr); - int pcol = ModuleBase.readUnsignedByte (_dstrm, _module); - cprop[2] = new Property ("PaletteComponent", - PropertyType.INTEGER, - new Integer (pcol)); - parray[i] = new Property ("Component", - PropertyType.PROPERTY, - PropertyArity.ARRAY, - cprop); - } - - // put constructed property into the Module - Property cmProp = new Property ("ComponentMapping", - PropertyType.PROPERTY, - PropertyArity.ARRAY, - parray); - if (_parentBox instanceof CodestreamHeaderBox) { - Codestream cs = ((CodestreamHeaderBox) _parentBox).getCodestream (); - cs.setCompMapProperty (cmProp); - } - else { - _module.addProperty (cmProp); - } - finalizeBytesRead (); - return true; + // Build the array of properties for each component. + // Components potentially have lots of stuff attached + // to them, so probably I should define a Component + // class. Need to determine how the Component Mapping + // relates to component information in a codestream. + // Or maybe this doesn't have anything to do with it. + for (int i = 0; i < ncomp; i++) { + Property[] cprop = new Property[3]; + int index = _module.readUnsignedShort(_dstrm); + cprop[0] = new Property("ComponentIndex", PropertyType.INTEGER, new Integer(index)); + int mtyp = ModuleBase.readUnsignedByte(_dstrm, _module); + cprop[1] = _module.addIntegerProperty("MTyp", mtyp, JP2Strings.mtypStr); + int pcol = ModuleBase.readUnsignedByte(_dstrm, _module); + cprop[2] = new Property("PaletteComponent", PropertyType.INTEGER, new Integer(pcol)); + parray[i] = new Property("Component", PropertyType.PROPERTY, PropertyArity.ARRAY, cprop); } - /** Returns the name of the Box. */ - @Override - protected String getSelfPropName () - { - return "Component Mapping Box"; + // put constructed property into the Module + Property cmProp = + new Property("ComponentMapping", PropertyType.PROPERTY, PropertyArity.ARRAY, parray); + if (_parentBox instanceof CodestreamHeaderBox) { + Codestream cs = ((CodestreamHeaderBox) _parentBox).getCodestream(); + cs.setCompMapProperty(cmProp); + } else { + _module.addProperty(cmProp); } + finalizeBytesRead(); + return true; + } + + /** Returns the name of the Box. */ + @Override + protected String getSelfPropName() { + return "Component Mapping Box"; + } } diff --git a/jhove-modules/jpeg2000-hul/src/main/java/edu/harvard/hul/ois/jhove/module/jpeg2000/ComposLayerHdrBox.java b/jhove-modules/jpeg2000-hul/src/main/java/edu/harvard/hul/ois/jhove/module/jpeg2000/ComposLayerHdrBox.java index 6bb72a170..d654990c2 100644 --- a/jhove-modules/jpeg2000-hul/src/main/java/edu/harvard/hul/ois/jhove/module/jpeg2000/ComposLayerHdrBox.java +++ b/jhove-modules/jpeg2000-hul/src/main/java/edu/harvard/hul/ois/jhove/module/jpeg2000/ComposLayerHdrBox.java @@ -1,161 +1,138 @@ -/********************************************************************** - * Jhove - JSTOR/Harvard Object Validation Environment - * Copyright 2004 by JSTOR and the President and Fellows of Harvard College +/** + * ******************************************************************** Jhove - JSTOR/Harvard Object + * Validation Environment Copyright 2004 by JSTOR and the President and Fellows of Harvard College * - **********************************************************************/ - + *

******************************************************************** + */ package edu.harvard.hul.ois.jhove.module.jpeg2000; +import edu.harvard.hul.ois.jhove.*; import java.io.*; import java.util.*; -import edu.harvard.hul.ois.jhove.*; /** - * Compositing Layer Header Box (JPX superbox). - * See ISO/IEC FCD15444-2: 2000, L.9.4 - * - * @author Gary McGath + * Compositing Layer Header Box (JPX superbox). See ISO/IEC FCD15444-2: 2000, L.9.4 * + * @author Gary McGath */ public class ComposLayerHdrBox extends JP2Box { - private Property label; - private Property opacityProp; - private Property channelDefProp; - private Property codestreamRegProp; - private List colorSpecs; - + private Property label; + private Property opacityProp; + private Property channelDefProp; + private Property codestreamRegProp; + private List colorSpecs; + /** + * Constructor with superbox. + * + * @param parent parent superbox of this box + */ + public ComposLayerHdrBox(RandomAccessFile raf, BoxHolder parent) { + super(raf, parent); + } - /** - * Constructor with superbox. - * - * @param parent parent superbox of this box - */ - public ComposLayerHdrBox(RandomAccessFile raf, BoxHolder parent) { - super(raf, parent); + /** + * Reads the box, putting appropriate information in the RepInfo object. setModule, setBoxHeader, + * setRepInfo and setDataInputStream must be called before readBox is called. + * readBox must completely consume the box, so that the next byte to be read by the + * DataInputStream is the FF byte of the next Box. + */ + @Override + public boolean readBox() throws IOException { + if (_parentBox != null) { + // Box must be at top level. + wrongBoxContext(); + return false; } + initBytesRead(); + hasBoxes = true; + colorSpecs = new LinkedList<>(); - /** Reads the box, putting appropriate information in - * the RepInfo object. setModule, setBoxHeader, - * setRepInfo and setDataInputStream must be called - * before readBox is called. - * readBox must completely consume the - * box, so that the next byte to be read by the - * DataInputStream is the FF byte of the next Box. - */ - @Override - public boolean readBox() throws IOException { - if (_parentBox != null) { - // Box must be at top level. - wrongBoxContext(); - return false; - } - initBytesRead (); - hasBoxes = true; - colorSpecs = new LinkedList<> (); - - // Unlike some other boxes, compositing layer boxes - // are numbered by their order in the file, starting - // with 0. A definite case of design by committee. - JP2Box box = null; - boolean hasOpacity = false; - boolean hasChannelDef = false; - while (hasNext ()) { - box = (JP2Box) next (); - if (box == null) { - break; - } - if (box instanceof ColorGroupBox || - box instanceof OpacityBox || - box instanceof ChannelDefBox || - box instanceof CodestreamRegBox || - box instanceof IPRBox || - box instanceof ResolutionBox || - box instanceof LabelBox) { - if (!box.readBox ()) { - return false; - } - if (box instanceof OpacityBox) { - hasOpacity = true; - } - else if (box instanceof ChannelDefBox) { - hasChannelDef = true; - } - if (box instanceof LabelBox) { - label = new Property ("Label", - PropertyType.STRING, - ((LabelBox) box).getLabel ()); - } - } - else { - box.skipBox (); - } - } - if (hasOpacity && hasChannelDef) { - _repInfo.setMessage (new ErrorMessage - (MessageConstants.JPEG2000_HUL_12, - _module.getFilePos ())); - _repInfo.setValid (false); - } - finalizeBytesRead (); - - List propList = new ArrayList (4); - if (label != null) { - propList.add (label); + // Unlike some other boxes, compositing layer boxes + // are numbered by their order in the file, starting + // with 0. A definite case of design by committee. + JP2Box box = null; + boolean hasOpacity = false; + boolean hasChannelDef = false; + while (hasNext()) { + box = (JP2Box) next(); + if (box == null) { + break; + } + if (box instanceof ColorGroupBox + || box instanceof OpacityBox + || box instanceof ChannelDefBox + || box instanceof CodestreamRegBox + || box instanceof IPRBox + || box instanceof ResolutionBox + || box instanceof LabelBox) { + if (!box.readBox()) { + return false; } - if (!colorSpecs.isEmpty ()) { - propList.add (new Property ("ColorSpecs", - PropertyType.PROPERTY, - PropertyArity.LIST, - colorSpecs)); + if (box instanceof OpacityBox) { + hasOpacity = true; + } else if (box instanceof ChannelDefBox) { + hasChannelDef = true; } - if (opacityProp != null) { - propList.add (opacityProp); + if (box instanceof LabelBox) { + label = new Property("Label", PropertyType.STRING, ((LabelBox) box).getLabel()); } - if (channelDefProp != null) { - propList.add (channelDefProp); - } - if (codestreamRegProp != null) { - propList.add (codestreamRegProp); - } - _module.addComposLayer(new Property - ("CompositeLayerHeader", - PropertyType.PROPERTY, - PropertyArity.LIST, - propList)); - return true; + } else { + box.skipBox(); + } } + if (hasOpacity && hasChannelDef) { + _repInfo.setMessage(new ErrorMessage(MessageConstants.JPEG2000_HUL_12, _module.getFilePos())); + _repInfo.setValid(false); + } + finalizeBytesRead(); - - /** Add a color specification property. */ - protected void addColorSpec (Property p) - { - colorSpecs.add (p); + List propList = new ArrayList(4); + if (label != null) { + propList.add(label); } - - /** Add an opacity property. */ - protected void addOpacity (Property p) - { - opacityProp = p; + if (!colorSpecs.isEmpty()) { + propList.add( + new Property("ColorSpecs", PropertyType.PROPERTY, PropertyArity.LIST, colorSpecs)); } - - /** Add channel definition property. */ - protected void addChannelDef (Property p) - { - channelDefProp = p; + if (opacityProp != null) { + propList.add(opacityProp); } - - /** Add codestream registration property. */ - protected void addCodestreamReg (Property p) - { - codestreamRegProp = p; + if (channelDefProp != null) { + propList.add(channelDefProp); } - - /** Returns the name of the Box. */ - @Override - protected String getSelfPropName () - { - return "Compositing Layer Header Box"; + if (codestreamRegProp != null) { + propList.add(codestreamRegProp); } + _module.addComposLayer( + new Property("CompositeLayerHeader", PropertyType.PROPERTY, PropertyArity.LIST, propList)); + return true; + } + + /** Add a color specification property. */ + protected void addColorSpec(Property p) { + colorSpecs.add(p); + } + + /** Add an opacity property. */ + protected void addOpacity(Property p) { + opacityProp = p; + } + + /** Add channel definition property. */ + protected void addChannelDef(Property p) { + channelDefProp = p; + } + + /** Add codestream registration property. */ + protected void addCodestreamReg(Property p) { + codestreamRegProp = p; + } + + /** Returns the name of the Box. */ + @Override + protected String getSelfPropName() { + return "Compositing Layer Header Box"; + } } diff --git a/jhove-modules/jpeg2000-hul/src/main/java/edu/harvard/hul/ois/jhove/module/jpeg2000/CompositionBox.java b/jhove-modules/jpeg2000-hul/src/main/java/edu/harvard/hul/ois/jhove/module/jpeg2000/CompositionBox.java index 8efb5545e..82312b2b8 100644 --- a/jhove-modules/jpeg2000-hul/src/main/java/edu/harvard/hul/ois/jhove/module/jpeg2000/CompositionBox.java +++ b/jhove-modules/jpeg2000-hul/src/main/java/edu/harvard/hul/ois/jhove/module/jpeg2000/CompositionBox.java @@ -1,8 +1,8 @@ -/********************************************************************** - * Jhove - JSTOR/Harvard Object Validation Environment - * Copyright 2004 by JSTOR and the President and Fellows of Harvard College - **********************************************************************/ - +/** + * ******************************************************************** Jhove - JSTOR/Harvard Object + * Validation Environment Copyright 2004 by JSTOR and the President and Fellows of Harvard College + * ******************************************************************** + */ package edu.harvard.hul.ois.jhove.module.jpeg2000; import edu.harvard.hul.ois.jhove.*; @@ -11,155 +11,126 @@ /** * Composition Box (JPX). - * - * See ISO/IEC FCD15444-2: 2000, L.9.10 - * - * @author Gary McGath * + *

See ISO/IEC FCD15444-2: 2000, L.9.10 + * + * @author Gary McGath */ public class CompositionBox extends JP2Box { - private List instSets; - private long _height; - private long _width; - private int _loop; - + private List instSets; + private long _height; + private long _width; + private int _loop; - /** - * Constructor with superbox. - * - * @param parent parent superbox of this box - */ - public CompositionBox(RandomAccessFile raf, BoxHolder parent) { - super(raf, parent); + /** + * Constructor with superbox. + * + * @param parent parent superbox of this box + */ + public CompositionBox(RandomAccessFile raf, BoxHolder parent) { + super(raf, parent); + } + + /** + * Reads the box, putting appropriate information in the RepInfo object. setModule, setBoxHeader, + * setRepInfo and setDataInputStream must be called before readBox is called. + * readBox must completely consume the box, so that the next byte to be read by the + * DataInputStream is the FF byte of the next Box. + */ + @Override + public boolean readBox() throws IOException { + JP2Box box; + if (_parentBox != null) { + // May not occur in a superbox + wrongBoxContext(); + return false; } + initBytesRead(); + hasBoxes = true; + instSets = new LinkedList<>(); - /** Reads the box, putting appropriate information in - * the RepInfo object. setModule, setBoxHeader, - * setRepInfo and setDataInputStream must be called - * before readBox is called. - * readBox must completely consume the - * box, so that the next byte to be read by the - * DataInputStream is the FF byte of the next Box. - */ - @Override - public boolean readBox() throws IOException { - JP2Box box; - if (_parentBox != null) { - // May not occur in a superbox - wrongBoxContext(); - return false; - } - initBytesRead (); - hasBoxes = true; - instSets = new LinkedList<> (); + // A Composition box is a superbox which contains one + // Composition Options Box followed by 0 (?) or more + // Instruction Set Boxes. + // BoxHeader subhdr = new BoxHeader (_module, _dstrm); + // subhdr.readHeader (); + if (!hasNext()) { + emptyBox(); + return false; + } - // A Composition box is a superbox which contains one - // Composition Options Box followed by 0 (?) or more - // Instruction Set Boxes. - //BoxHeader subhdr = new BoxHeader (_module, _dstrm); - //subhdr.readHeader (); - if (!hasNext ()) { - emptyBox (); - return false; - } - - // Read the options box - box = (JP2Box) next (); - if (!(box instanceof CompOptionsBox)) { - _repInfo.setMessage (new ErrorMessage - (MessageConstants.JPEG2000_HUL_11, - _module.getFilePos())); - _repInfo.setWellFormed (false); - return false; - } - long sizeLeft = _boxHeader.getDataLength () - box.getLength (); -// box = new CompOptionsBox (this); -// box.setBoxHeader (subhdr); -// box.setDataInputStream (_dstrm); -// box.setRandomAccessFile (_raf); -// box.setModule (_module); -// box.setRepInfo (_repInfo); - if (!box.readBox ()) { - return false; - } + // Read the options box + box = (JP2Box) next(); + if (!(box instanceof CompOptionsBox)) { + _repInfo.setMessage(new ErrorMessage(MessageConstants.JPEG2000_HUL_11, _module.getFilePos())); + _repInfo.setWellFormed(false); + return false; + } + long sizeLeft = _boxHeader.getDataLength() - box.getLength(); + // box = new CompOptionsBox (this); + // box.setBoxHeader (subhdr); + // box.setDataInputStream (_dstrm); + // box.setRandomAccessFile (_raf); + // box.setModule (_module); + // box.setRepInfo (_repInfo); + if (!box.readBox()) { + return false; + } - // Read the instruction set boxes - while (hasNext ()) { - box = (JP2Box) next (); - if (box == null) { - break; - } - if (box instanceof InstructionSetBox) { - if (!box.readBox ()) { - return false; - } - } - else { - box.skipBox (); - } + // Read the instruction set boxes + while (hasNext()) { + box = (JP2Box) next(); + if (box == null) { + break; + } + if (box instanceof InstructionSetBox) { + if (!box.readBox()) { + return false; } - // A box has to be at least 8 bytes long, and there must - // not be any bytes left over. - if (sizeLeft != 0) { - // Underran the superbox -- get out quick - superboxUnderrun (); - return false; - - } - finalizeBytesRead (); - - List propList = new ArrayList<> (4); - propList.add (new Property ("Width", - PropertyType.LONG, - new Long (_width))); - propList.add (new Property ("Height", - PropertyType.LONG, - new Long (_height))); - propList.add (new Property ("Loop", - PropertyType.INTEGER, - new Integer (_loop))); - if (!instSets.isEmpty ()) { - propList.add (new Property ("InstructionSets", - PropertyType.PROPERTY, - PropertyArity.LIST, - instSets)); - } - _module.addProperty (new Property ("Composition", - PropertyType.PROPERTY, - PropertyArity.LIST, - propList)); - return true; + } else { + box.skipBox(); + } } - - /** Add an instruction set property to the list. - * This is called from InstructionSetBox. - */ - protected void addInstSet (Property p) - { - instSets.add (p); + // A box has to be at least 8 bytes long, and there must + // not be any bytes left over. + if (sizeLeft != 0) { + // Underran the superbox -- get out quick + superboxUnderrun(); + return false; } - - /** Set the height value. This is called from - * CompositionBox. */ - protected void setHeight (long h) - { - _height = h; + finalizeBytesRead(); + + List propList = new ArrayList<>(4); + propList.add(new Property("Width", PropertyType.LONG, new Long(_width))); + propList.add(new Property("Height", PropertyType.LONG, new Long(_height))); + propList.add(new Property("Loop", PropertyType.INTEGER, new Integer(_loop))); + if (!instSets.isEmpty()) { + propList.add( + new Property("InstructionSets", PropertyType.PROPERTY, PropertyArity.LIST, instSets)); } + _module.addProperty( + new Property("Composition", PropertyType.PROPERTY, PropertyArity.LIST, propList)); + return true; + } + /** Add an instruction set property to the list. This is called from InstructionSetBox. */ + protected void addInstSet(Property p) { + instSets.add(p); + } - /** Set the height value. This is called from - * CompositionBox. */ - protected void setWidth (long w) - { - _width = w; - } - - - /** Set the loop value. This is called from - * CompositionBox. */ - protected void setLoop (int l) - { - _loop = l; - } + /** Set the height value. This is called from CompositionBox. */ + protected void setHeight(long h) { + _height = h; + } + + /** Set the height value. This is called from CompositionBox. */ + protected void setWidth(long w) { + _width = w; + } + + /** Set the loop value. This is called from CompositionBox. */ + protected void setLoop(int l) { + _loop = l; + } } diff --git a/jhove-modules/jpeg2000-hul/src/main/java/edu/harvard/hul/ois/jhove/module/jpeg2000/ContCodestream.java b/jhove-modules/jpeg2000-hul/src/main/java/edu/harvard/hul/ois/jhove/module/jpeg2000/ContCodestream.java index 460c2cc4f..4ce1aa585 100644 --- a/jhove-modules/jpeg2000-hul/src/main/java/edu/harvard/hul/ois/jhove/module/jpeg2000/ContCodestream.java +++ b/jhove-modules/jpeg2000-hul/src/main/java/edu/harvard/hul/ois/jhove/module/jpeg2000/ContCodestream.java @@ -1,9 +1,9 @@ -/********************************************************************** - * Jhove - JSTOR/Harvard Object Validation Environment - * Copyright 2004 by JSTOR and the President and Fellows of Harvard College +/** + * ******************************************************************** Jhove - JSTOR/Harvard Object + * Validation Environment Copyright 2004 by JSTOR and the President and Fellows of Harvard College * - **********************************************************************/ - + *

******************************************************************** + */ package edu.harvard.hul.ois.jhove.module.jpeg2000; import edu.harvard.hul.ois.jhove.*; @@ -13,240 +13,211 @@ /** * Encapsulation of a JPEG 2000 codestream. - * - * This is based on the information in Appendix A of - * ISO/IEC 15444-1:2000(E). That standard "does not - * include a definition of compliance or conformance." - * - * @author Gary McGath * + *

This is based on the information in Appendix A of ISO/IEC 15444-1:2000(E). That standard "does + * not include a definition of compliance or conformance." + * + * @author Gary McGath */ public class ContCodestream { - - private Codestream _codestream; - private long _length; - private Jpeg2000Module _module; - private DataInputStream _dstream; -// private List _tileParts; - private List _tiles; - private long _tileLeft; - - /* Tile for which we have most recently seen an unclosed SOT */ - private Tile _curTile; - - /* Set to true when a PPM marker segment is found */ - private boolean ppmSeen; - - /* Constants defining codestream markers */ - private final static int - SOC = 0X4F, // start of codestream - COD = 0X52, // coding style default - COC = 0X53, // coding style component - TLM = 0X55, // tile-part lengths - PLM = 0X57, // packet length, main header - PLT = 0X58, // packet length, tile-part header - QCD = 0X5C, // quantization default - QCC = 0X5D, // quantization component - RGN = 0X5E, // region of interest - POC = 0X5F, // progression order change - PPM = 0X60, // Packed packet headers, main header - PPT = 0X61, // packed packet headers, tile-part header - CRG = 0X63, // component registration - COM = 0X64, // comment - SOT = 0X90, // start of tile part - SOP = 0X91, // start of packet - EPH = 0X92, // end of packet header - SOD = 0X93, // start of data - EOC = 0XD9, // end of codestream - SIZ = 0X51; // image and tile size - - /** - * Constructor. - * - * @param length Length of the codestream, exclusive of the - * box header. If the codestream box has a length - * field of 0, pass 0 for this parameter. - */ - public ContCodestream (Jpeg2000Module module, - DataInputStream dstream, - long length) - { - _module = module; - _dstream = dstream; - _length = length; - _tiles = new LinkedList<> (); - //_tileParts = new LinkedList (); // Do I want both lists? - ppmSeen = false; - } + private Codestream _codestream; + private long _length; + private Jpeg2000Module _module; + private DataInputStream _dstream; + // private List _tileParts; + private List _tiles; + private long _tileLeft; + /* Tile for which we have most recently seen an unclosed SOT */ + private Tile _curTile; - /** Reading a codestream generates various bits of information about - * the image. These are made available after reading through - * accessor functions. - * - * @param cs The image which this codestream defines. - * Must have a non-null codestream - * field. - * - * @param info The RepInfo object which accumulates information - * about the document. Used for reporting errors. - * - * @return True if no fatal errors detected, - * false if error prevents safe continuation - */ - public boolean readCodestream (Codestream cs, RepInfo info) - throws IOException - { - _codestream = cs; - long lengthLeft = _length; - _tileLeft = 0; - boolean socSeen = false; // flag to note an SOC marker has been seen - - // length may be 0, signifying that we go till EOF - if (lengthLeft == 0) { - lengthLeft = Long.MAX_VALUE; + /* Set to true when a PPM marker segment is found */ + private boolean ppmSeen; + + /* Constants defining codestream markers */ + private static final int SOC = 0X4F, // start of codestream + COD = 0X52, // coding style default + COC = 0X53, // coding style component + TLM = 0X55, // tile-part lengths + PLM = 0X57, // packet length, main header + PLT = 0X58, // packet length, tile-part header + QCD = 0X5C, // quantization default + QCC = 0X5D, // quantization component + RGN = 0X5E, // region of interest + POC = 0X5F, // progression order change + PPM = 0X60, // Packed packet headers, main header + PPT = 0X61, // packed packet headers, tile-part header + CRG = 0X63, // component registration + COM = 0X64, // comment + SOT = 0X90, // start of tile part + SOP = 0X91, // start of packet + EPH = 0X92, // end of packet header + SOD = 0X93, // start of data + EOC = 0XD9, // end of codestream + SIZ = 0X51; // image and tile size + + /** + * Constructor. + * + * @param length Length of the codestream, exclusive of the box header. If the codestream box has + * a length field of 0, pass 0 for this parameter. + */ + public ContCodestream(Jpeg2000Module module, DataInputStream dstream, long length) { + _module = module; + _dstream = dstream; + _length = length; + _tiles = new LinkedList<>(); + // _tileParts = new LinkedList (); // Do I want both lists? + ppmSeen = false; + } + + /** + * Reading a codestream generates various bits of information about the image. These are made + * available after reading through accessor functions. + * + * @param cs The image which this codestream defines. Must have a non-null codestream + * field. + * @param info The RepInfo object which accumulates information about the document. Used for + * reporting errors. + * @return True if no fatal errors detected, false if error prevents safe continuation + */ + public boolean readCodestream(Codestream cs, RepInfo info) throws IOException { + _codestream = cs; + long lengthLeft = _length; + _tileLeft = 0; + boolean socSeen = false; // flag to note an SOC marker has been seen + + // length may be 0, signifying that we go till EOF + if (lengthLeft == 0) { + lengthLeft = Long.MAX_VALUE; + } + try { + while (lengthLeft > 0) { + // "Marker segments" are followed by a length parameter, + // but "markers" aren't. + int ff = ModuleBase.readUnsignedByte(_dstream, _module); + if (ff != 0XFF) { + info.setMessage(new ErrorMessage(MessageConstants.JPEG2000_HUL_8)); + info.setWellFormed(false); + return false; } - try { - while (lengthLeft > 0) { - // "Marker segments" are followed by a length parameter, - // but "markers" aren't. - int ff = ModuleBase.readUnsignedByte (_dstream, _module); - if (ff != 0XFF) { - info.setMessage (new ErrorMessage(MessageConstants.JPEG2000_HUL_8)); - info.setWellFormed (false); - return false; - } - int marker = ModuleBase.readUnsignedByte (_dstream, _module); - if (marker == 0X4F) { - // we got the SOC marker, as expected - socSeen = true; - } - MarkerSegment ms = MarkerSegment.markerSegmentMaker (marker); - ms.setCodestream (cs); - ms.setContCodestream (this); - ms.setDataInputStream (_dstream); - ms.setRepInfo (info); - ms.setModule (_module); - int markLen = ms.readMarkLen (); - if (!ms.process (markLen == 0 ? 0 : markLen - 2)) { - info.setMessage (new ErrorMessage - (MessageConstants.JPEG2000_HUL_9)); - info.setWellFormed (false); - return false; - } - // markLen includes the marker length bytes, - // but not the marker bytes - - if (!(ms instanceof Marker)) { - lengthLeft -= markLen + 2; - // Count down on the bytes in a tile if we're in one - if (_tileLeft > 0) { - _tileLeft -= markLen + 2; - } - } - else { - // It's a plain marker -- no length data. - lengthLeft -= 2; - if (_tileLeft > 0) { - _tileLeft -= 2; - } - if (marker == SOD) { - // 0X93 is SOD, which is followed by a bitstream. - // We skip the number of bytes not yet deducted from _tileLeft - _module.skipBytes (_dstream, (int) _tileLeft, _module); - lengthLeft -= _tileLeft; - _tileLeft = 0; - } - else if (marker == EOC) { - break; // end of codestream - } - } - } + int marker = ModuleBase.readUnsignedByte(_dstream, _module); + if (marker == 0X4F) { + // we got the SOC marker, as expected + socSeen = true; } - catch (EOFException e) { - // we're done + MarkerSegment ms = MarkerSegment.markerSegmentMaker(marker); + ms.setCodestream(cs); + ms.setContCodestream(this); + ms.setDataInputStream(_dstream); + ms.setRepInfo(info); + ms.setModule(_module); + int markLen = ms.readMarkLen(); + if (!ms.process(markLen == 0 ? 0 : markLen - 2)) { + info.setMessage(new ErrorMessage(MessageConstants.JPEG2000_HUL_9)); + info.setWellFormed(false); + return false; } - if (!socSeen) { - info.setMessage (new ErrorMessage(MessageConstants.JPEG2000_HUL_8)); - info.setWellFormed (false); - return false; + // markLen includes the marker length bytes, + // but not the marker bytes + + if (!(ms instanceof Marker)) { + lengthLeft -= markLen + 2; + // Count down on the bytes in a tile if we're in one + if (_tileLeft > 0) { + _tileLeft -= markLen + 2; + } + } else { + // It's a plain marker -- no length data. + lengthLeft -= 2; + if (_tileLeft > 0) { + _tileLeft -= 2; + } + if (marker == SOD) { + // 0X93 is SOD, which is followed by a bitstream. + // We skip the number of bytes not yet deducted from _tileLeft + _module.skipBytes(_dstream, (int) _tileLeft, _module); + lengthLeft -= _tileLeft; + _tileLeft = 0; + } else if (marker == EOC) { + break; // end of codestream + } } - _codestream.setTiles (_tiles); - return true; + } + } catch (EOFException e) { + // we're done } - - - /** Returns the list of tiles. The elements are Tile objects. */ - public List getTiles () - { - return _tiles; - } - - /** Set the number of bytes remaining in the current tile. - * For use by MarkerSegment subclasses. - */ - protected void setTileLeft (long tileLeft) - { - _tileLeft = tileLeft; + if (!socSeen) { + info.setMessage(new ErrorMessage(MessageConstants.JPEG2000_HUL_8)); + info.setWellFormed(false); + return false; } + _codestream.setTiles(_tiles); + return true; + } + /** Returns the list of tiles. The elements are Tile objects. */ + public List getTiles() { + return _tiles; + } + /** Set the number of bytes remaining in the current tile. For use by MarkerSegment subclasses. */ + protected void setTileLeft(long tileLeft) { + _tileLeft = tileLeft; + } - - /** Gets the tile whose index is idx. */ - protected Tile getTile (int idx) - { - // If we haven't reached this index before, add a tile. - // Tiles are supposed to be added sequentially, but - // PIAV. - while (_tiles.size () <= idx) { - _tiles.add (new Tile ()); - } - return _tiles.get (idx); - } - - /** Sets the value of curTile. */ - protected void setCurTile (Tile tile) - { - _curTile = tile; - } - - /** Sets the value of the ppmSeen flag, signifying that - * a PPM marker segment has been encountered. */ - protected void setPPMSeen (boolean b) - { - ppmSeen = b; - } - - - /** Gets the value of curTile. May be null. */ - protected Tile getCurTile () - { - return _curTile; - } - - /** Returns the value of the ppmSeen flag, signifying that - * a PPM marker segment has been encountered. */ - protected boolean isPPMSeen () - { - return ppmSeen; + /** Gets the tile whose index is idx. */ + protected Tile getTile(int idx) { + // If we haven't reached this index before, add a tile. + // Tiles are supposed to be added sequentially, but + // PIAV. + while (_tiles.size() <= idx) { + _tiles.add(new Tile()); } + return _tiles.get(idx); + } - - /* Based on marker code, return true if this is a marker - * segment (i.e., it has parameters). The documentation - * isn't fully clear, but I think the only way to determine - * what is a marker is to enumerate all values that are - * markers. */ - private static boolean isSegment (int marker) - { - // end of codestream - - return !((marker >= 0X30 && marker <= 0X3F) || - marker == SOC || // start of codestream - marker == EPH || // end of packet header - marker == SOD || // start of data - marker == EOC); - } + /** Sets the value of curTile. */ + protected void setCurTile(Tile tile) { + _curTile = tile; + } + + /** + * Sets the value of the ppmSeen flag, signifying that a PPM marker segment has been encountered. + */ + protected void setPPMSeen(boolean b) { + ppmSeen = b; + } + + /** Gets the value of curTile. May be null. */ + protected Tile getCurTile() { + return _curTile; + } + + /** + * Returns the value of the ppmSeen flag, signifying that a PPM marker segment has been + * encountered. + */ + protected boolean isPPMSeen() { + return ppmSeen; + } + + /* Based on marker code, return true if this is a marker + * segment (i.e., it has parameters). The documentation + * isn't fully clear, but I think the only way to determine + * what is a marker is to enumerate all values that are + * markers. */ + private static boolean isSegment(int marker) { + // end of codestream + + return !((marker >= 0X30 && marker <= 0X3F) + || marker == SOC + || // start of codestream + marker == EPH + || // end of packet header + marker == SOD + || // start of data + marker == EOC); + } } diff --git a/jhove-modules/jpeg2000-hul/src/main/java/edu/harvard/hul/ois/jhove/module/jpeg2000/ContCodestreamBox.java b/jhove-modules/jpeg2000-hul/src/main/java/edu/harvard/hul/ois/jhove/module/jpeg2000/ContCodestreamBox.java index 92143ac6b..5020145e8 100644 --- a/jhove-modules/jpeg2000-hul/src/main/java/edu/harvard/hul/ois/jhove/module/jpeg2000/ContCodestreamBox.java +++ b/jhove-modules/jpeg2000-hul/src/main/java/edu/harvard/hul/ois/jhove/module/jpeg2000/ContCodestreamBox.java @@ -1,72 +1,60 @@ -/********************************************************************** - * Jhove - JSTOR/Harvard Object Validation Environment - * Copyright 2004 by JSTOR and the President and Fellows of Harvard College - **********************************************************************/ - +/** + * ******************************************************************** Jhove - JSTOR/Harvard Object + * Validation Environment Copyright 2004 by JSTOR and the President and Fellows of Harvard College + * ******************************************************************** + */ package edu.harvard.hul.ois.jhove.module.jpeg2000; -import java.io.*; import edu.harvard.hul.ois.jhove.*; +import java.io.*; /** - * Continuous codestream box. - * See I.5.4 in ISO/IEC 15444-1:2000 + * Continuous codestream box. See I.5.4 in ISO/IEC 15444-1:2000 * * @author Gary McGath - * */ public class ContCodestreamBox extends JP2Box { - - /** - * Constructor with superbox. - * - * @param parent parent superbox of this box - */ - public ContCodestreamBox (RandomAccessFile raf, BoxHolder parent) - { - super (raf, parent); - } - - - /** Reads the box, putting appropriate information in - * the RepInfo object. setModule, setBoxHeader, - * setRepInfo and setDataInputStream must be called - * before readBox is called. - * readBox must completely consume the - * box, so that the next byte to be read by the - * DataInputStream is the FF byte of the next Box. - * - * The reading and interpretation of the actual codestream - * occurs within the execution of readBox. - */ - @Override - public boolean readBox() throws IOException { - initBytesRead (); - - // Must come after the JP2 header - if (!_module.isJP2HdrSeen()) { - _repInfo.setMessage (new ErrorMessage - (MessageConstants.JPEG2000_HUL_34, _module.getFilePos ())); - return false; - } - int ncs = _module.getNCodestreams () + 1; - _module.setNCodestreams (ncs); - Codestream curCodestream = _module.getCodestream (ncs); - long len = - _boxHeader.getLength () == 0 ? - 0 : _boxHeader.getDataLength (); - ContCodestream ccs = - new ContCodestream (_module, _dstrm, len); - boolean retval = ccs.readCodestream (curCodestream, _repInfo); - finalizeBytesRead (); - return retval; - } - - /** Returns the name of the Box. */ - @Override - protected String getSelfPropName () - { - return "Contiguous Codestream Box"; + /** + * Constructor with superbox. + * + * @param parent parent superbox of this box + */ + public ContCodestreamBox(RandomAccessFile raf, BoxHolder parent) { + super(raf, parent); + } + + /** + * Reads the box, putting appropriate information in the RepInfo object. setModule, setBoxHeader, + * setRepInfo and setDataInputStream must be called before readBox is called. + * readBox must completely consume the box, so that the next byte to be read by the + * DataInputStream is the FF byte of the next Box. + * + *

The reading and interpretation of the actual codestream occurs within the execution of + * readBox. + */ + @Override + public boolean readBox() throws IOException { + initBytesRead(); + + // Must come after the JP2 header + if (!_module.isJP2HdrSeen()) { + _repInfo.setMessage(new ErrorMessage(MessageConstants.JPEG2000_HUL_34, _module.getFilePos())); + return false; } + int ncs = _module.getNCodestreams() + 1; + _module.setNCodestreams(ncs); + Codestream curCodestream = _module.getCodestream(ncs); + long len = _boxHeader.getLength() == 0 ? 0 : _boxHeader.getDataLength(); + ContCodestream ccs = new ContCodestream(_module, _dstrm, len); + boolean retval = ccs.readCodestream(curCodestream, _repInfo); + finalizeBytesRead(); + return retval; + } + + /** Returns the name of the Box. */ + @Override + protected String getSelfPropName() { + return "Contiguous Codestream Box"; + } } diff --git a/jhove-modules/jpeg2000-hul/src/main/java/edu/harvard/hul/ois/jhove/module/jpeg2000/CrossRefBox.java b/jhove-modules/jpeg2000-hul/src/main/java/edu/harvard/hul/ois/jhove/module/jpeg2000/CrossRefBox.java index 5087de9a4..bd16a8095 100644 --- a/jhove-modules/jpeg2000-hul/src/main/java/edu/harvard/hul/ois/jhove/module/jpeg2000/CrossRefBox.java +++ b/jhove-modules/jpeg2000-hul/src/main/java/edu/harvard/hul/ois/jhove/module/jpeg2000/CrossRefBox.java @@ -1,8 +1,8 @@ -/********************************************************************** - * Jhove - JSTOR/Harvard Object Validation Environment - * Copyright 2004 by JSTOR and the President and Fellows of Harvard College - **********************************************************************/ - +/** + * ******************************************************************** Jhove - JSTOR/Harvard Object + * Validation Environment Copyright 2004 by JSTOR and the President and Fellows of Harvard College + * ******************************************************************** + */ package edu.harvard.hul.ois.jhove.module.jpeg2000; import edu.harvard.hul.ois.jhove.*; @@ -10,93 +10,78 @@ import java.util.*; /** - * Cross Reference Box (JPX). - * A Cross Reference Box may be found in a Codestream - * Header, Compositing Layer Header, or Association box. - * When it is encountered, the box to which it refers - * should be substituted for the Cross Reference Box. - * Interesting features of the box are that it - * isn't all in one place, but is - * scattered through multiple locations by a fragment list, - * and it doesn't follow standard superbox rules. - * - * - * See ISO/IEC FCD15444-2: 2000, L.9.7 - * - * @author Gary McGath + * Cross Reference Box (JPX). A Cross Reference Box may be found in a Codestream Header, Compositing + * Layer Header, or Association box. When it is encountered, the box to which it refers should be + * substituted for the Cross Reference Box. Interesting features of the box are that it isn't all in + * one place, but is scattered through multiple locations by a fragment list, and it doesn't follow + * standard superbox rules. + * + *

See ISO/IEC FCD15444-2: 2000, L.9.7 * + * @author Gary McGath */ public class CrossRefBox extends JP2Box { - DataInputStream fragStream; + DataInputStream fragStream; + /** + * Constructor with superbox. + * + * @param parent parent superbox of this box or TopLevelBoxHolder + */ + public CrossRefBox(RandomAccessFile raf, BoxHolder parent) { + super(raf, parent); + } - /** - * Constructor with superbox. - * - * @param parent parent superbox of this box - * or TopLevelBoxHolder - */ - public CrossRefBox(RandomAccessFile raf, BoxHolder parent) { - super(raf, parent); + /** + * Reads the box, putting appropriate information in the RepInfo object. setModule, setBoxHeader, + * setRepInfo and setDataInputStream must be called before readBox is called. + * readBox must completely consume the box, so that the next byte to be read by the + * DataInputStream is the FF byte of the next Box. + */ + @Override + public boolean readBox() throws IOException { + if (!(_parentBox instanceof CodestreamHeaderBox + || _parentBox instanceof ComposLayerHdrBox + || _parentBox instanceof AssociationBox)) { + wrongBoxContext(); + return false; } - - /** Reads the box, putting appropriate information in - * the RepInfo object. setModule, setBoxHeader, - * setRepInfo and setDataInputStream must be called - * before readBox is called. - * readBox must completely consume the - * box, so that the next byte to be read by the - * DataInputStream is the FF byte of the next Box. - */ - @Override - public boolean readBox() throws IOException { - if (! (_parentBox instanceof CodestreamHeaderBox || - _parentBox instanceof ComposLayerHdrBox || - _parentBox instanceof AssociationBox)) { - wrongBoxContext (); - return false; - } - initBytesRead (); - hasBoxes = true; - // Skip the box type - _module.read4Chars (_dstrm); - bytesLeft -= 4; - JP2Box box = null; - if (hasNext ()) { - box = (JP2Box) next (); - } - if (!(box instanceof FragmentListBox)) { - _repInfo.setMessage (new ErrorMessage - (MessageConstants.JPEG2000_HUL_16, - _module.getFilePos ())); - _repInfo.setWellFormed (false); - return false; - } - box.readBox (); - List fragList = ((FragmentListBox) box).getFragmentList(); - //App app = _module.getApp(); - JhoveBase base = _module.getBase (); - int bufSize = base.getBufferSize (); - fragStream = new DataInputStream - (new FragmentInputStream (fragList, _raf, bufSize)); - finalizeBytesRead (); - return false; + initBytesRead(); + hasBoxes = true; + // Skip the box type + _module.read4Chars(_dstrm); + bytesLeft -= 4; + JP2Box box = null; + if (hasNext()) { + box = (JP2Box) next(); } - - /** Returns a DataInputStream based on a FragmentInputStream - * so that the fragments can be read as a single entity. - */ - public DataInputStream getCrossRefStream () - { - return fragStream; + if (!(box instanceof FragmentListBox)) { + _repInfo.setMessage(new ErrorMessage(MessageConstants.JPEG2000_HUL_16, _module.getFilePos())); + _repInfo.setWellFormed(false); + return false; } + box.readBox(); + List fragList = ((FragmentListBox) box).getFragmentList(); + // App app = _module.getApp(); + JhoveBase base = _module.getBase(); + int bufSize = base.getBufferSize(); + fragStream = new DataInputStream(new FragmentInputStream(fragList, _raf, bufSize)); + finalizeBytesRead(); + return false; + } + /** + * Returns a DataInputStream based on a FragmentInputStream so that the fragments can be read as a + * single entity. + */ + public DataInputStream getCrossRefStream() { + return fragStream; + } - /** Returns the name of the Box. */ - @Override - protected String getSelfPropName () - { - return "Cross Reference Box"; - } + /** Returns the name of the Box. */ + @Override + protected String getSelfPropName() { + return "Cross Reference Box"; + } } diff --git a/jhove-modules/jpeg2000-hul/src/main/java/edu/harvard/hul/ois/jhove/module/jpeg2000/DDResolutionBox.java b/jhove-modules/jpeg2000-hul/src/main/java/edu/harvard/hul/ois/jhove/module/jpeg2000/DDResolutionBox.java index 9f37551f7..c44f10aff 100644 --- a/jhove-modules/jpeg2000-hul/src/main/java/edu/harvard/hul/ois/jhove/module/jpeg2000/DDResolutionBox.java +++ b/jhove-modules/jpeg2000-hul/src/main/java/edu/harvard/hul/ois/jhove/module/jpeg2000/DDResolutionBox.java @@ -1,100 +1,89 @@ -/********************************************************************** - * Jhove - JSTOR/Harvard Object Validation Environment - * Copyright 2004 by JSTOR and the President and Fellows of Harvard College - **********************************************************************/ - +/** + * ******************************************************************** Jhove - JSTOR/Harvard Object + * Validation Environment Copyright 2004 by JSTOR and the President and Fellows of Harvard College + * ******************************************************************** + */ package edu.harvard.hul.ois.jhove.module.jpeg2000; -import java.io.*; - import edu.harvard.hul.ois.jhove.*; +import java.io.*; /** - * Default Display Resolution Box. - * See I.5.3.7.2 in ISO/IEC 15444-1:2000 - * - * @author Gary McGath + * Default Display Resolution Box. See I.5.3.7.2 in ISO/IEC 15444-1:2000 * + * @author Gary McGath */ public class DDResolutionBox extends JP2Box { + /** + * Constructor with superbox. + * + * @param parent parent superbox of this box + */ + public DDResolutionBox(RandomAccessFile raf, BoxHolder parent) { + super(raf, parent); + } - /** - * Constructor with superbox. - * - * @param parent parent superbox of this box - */ - public DDResolutionBox (RandomAccessFile raf, BoxHolder parent) - { - super (raf, parent); + /** + * Reads the box, putting appropriate information in the RepInfo object. setModule, setBoxHeader, + * setRepInfo and setDataInputStream must be called before readBox is called. + * readBox must completely consume the box, so that the next byte to be read by the + * DataInputStream is the FF byte of the next Box. + */ + @Override + public boolean readBox() throws IOException { + initBytesRead(); + if (!(_parentBox instanceof ResolutionBox)) { + wrongBoxContext(); + return false; } - /** Reads the box, putting appropriate information in - * the RepInfo object. setModule, setBoxHeader, - * setRepInfo and setDataInputStream must be called - * before readBox is called. - * readBox must completely consume the - * box, so that the next byte to be read by the - * DataInputStream is the FF byte of the next Box. - */ - @Override - public boolean readBox() throws IOException { - initBytesRead (); - if (!(_parentBox instanceof ResolutionBox)) { - wrongBoxContext(); - return false; - } - - // Vertical Capture grid resolution num & denom - int vrcNum = _module.readUnsignedShort (_dstrm); - int vrcDenom = _module.readUnsignedShort (_dstrm); - - // Horizontal Capture grid resolution num & denom - int hrcNum = _module.readUnsignedShort (_dstrm); - int hrcDenom = _module.readUnsignedShort (_dstrm); - - // Vertical and Horizontal capture grid exponents - int vrcExp = ModuleBase.readUnsignedByte (_dstrm, _module); - int hrcExp = ModuleBase.readUnsignedByte (_dstrm, _module); - - // And the two resolution properties are subsumed into - // one property for the Module. - Property[] topProps = new Property[2]; - topProps[0] = ResolutionBox.makeResolutionProperty("HorizResolution", - hrcNum, hrcDenom, hrcExp); - topProps[1] = ResolutionBox.makeResolutionProperty("VertResolution", - vrcNum, vrcDenom, vrcExp); - _module.addProperty(new Property ("DefaultDisplayResolution", - PropertyType.PROPERTY, - PropertyArity.ARRAY, - topProps)); - - // If the resolution is not set by a CaptureResolutionBox, we assign it - // We need to set resolution in NisoImageMetadata - // as a Rational. It seems unlikely that negative - // exponents will be used (signifying resolutions - // less than 1 dpi), so we figure the exponent into - // the numerator. Also, this resolution is in - // dots per meter, which isn't a NISO standard unit, - // so we multiply the denominator by 100 to give - // units per centimeter. - NisoImageMetadata niso = _module.getCurrentNiso (); - if (niso.getXSamplingFrequency() == null) { - Rational vrc = ResolutionBox.convertToRational(vrcNum, vrcDenom, vrcExp); - Rational hrc = ResolutionBox.convertToRational(hrcNum, hrcDenom, hrcExp); - niso.setYSamplingFrequency (vrc); - niso.setXSamplingFrequency (hrc); - final int RESOLUTION_UNIT_CM = 3; - niso.setSamplingFrequencyUnit (RESOLUTION_UNIT_CM); - } - finalizeBytesRead (); - return true; - } + // Vertical Capture grid resolution num & denom + int vrcNum = _module.readUnsignedShort(_dstrm); + int vrcDenom = _module.readUnsignedShort(_dstrm); + + // Horizontal Capture grid resolution num & denom + int hrcNum = _module.readUnsignedShort(_dstrm); + int hrcDenom = _module.readUnsignedShort(_dstrm); - /** Returns the name of the Box. */ - @Override - protected String getSelfPropName () - { - return "Default Display Resolution Box"; + // Vertical and Horizontal capture grid exponents + int vrcExp = ModuleBase.readUnsignedByte(_dstrm, _module); + int hrcExp = ModuleBase.readUnsignedByte(_dstrm, _module); + + // And the two resolution properties are subsumed into + // one property for the Module. + Property[] topProps = new Property[2]; + topProps[0] = ResolutionBox.makeResolutionProperty("HorizResolution", hrcNum, hrcDenom, hrcExp); + topProps[1] = ResolutionBox.makeResolutionProperty("VertResolution", vrcNum, vrcDenom, vrcExp); + _module.addProperty( + new Property( + "DefaultDisplayResolution", PropertyType.PROPERTY, PropertyArity.ARRAY, topProps)); + + // If the resolution is not set by a CaptureResolutionBox, we assign it + // We need to set resolution in NisoImageMetadata + // as a Rational. It seems unlikely that negative + // exponents will be used (signifying resolutions + // less than 1 dpi), so we figure the exponent into + // the numerator. Also, this resolution is in + // dots per meter, which isn't a NISO standard unit, + // so we multiply the denominator by 100 to give + // units per centimeter. + NisoImageMetadata niso = _module.getCurrentNiso(); + if (niso.getXSamplingFrequency() == null) { + Rational vrc = ResolutionBox.convertToRational(vrcNum, vrcDenom, vrcExp); + Rational hrc = ResolutionBox.convertToRational(hrcNum, hrcDenom, hrcExp); + niso.setYSamplingFrequency(vrc); + niso.setXSamplingFrequency(hrc); + final int RESOLUTION_UNIT_CM = 3; + niso.setSamplingFrequencyUnit(RESOLUTION_UNIT_CM); } + finalizeBytesRead(); + return true; + } + + /** Returns the name of the Box. */ + @Override + protected String getSelfPropName() { + return "Default Display Resolution Box"; + } } diff --git a/jhove-modules/jpeg2000-hul/src/main/java/edu/harvard/hul/ois/jhove/module/jpeg2000/DataEntryURLBox.java b/jhove-modules/jpeg2000-hul/src/main/java/edu/harvard/hul/ois/jhove/module/jpeg2000/DataEntryURLBox.java index d2595c42d..9c779d5e7 100644 --- a/jhove-modules/jpeg2000-hul/src/main/java/edu/harvard/hul/ois/jhove/module/jpeg2000/DataEntryURLBox.java +++ b/jhove-modules/jpeg2000-hul/src/main/java/edu/harvard/hul/ois/jhove/module/jpeg2000/DataEntryURLBox.java @@ -1,88 +1,82 @@ -/********************************************************************** - * Jhove - JSTOR/Harvard Object Validation Environment - * Copyright 2004 by JSTOR and the President and Fellows of Harvard College +/** + * ******************************************************************** Jhove - JSTOR/Harvard Object + * Validation Environment Copyright 2004 by JSTOR and the President and Fellows of Harvard College * - **********************************************************************/ - + *

******************************************************************** + */ package edu.harvard.hul.ois.jhove.module.jpeg2000; +import edu.harvard.hul.ois.jhove.*; import java.io.*; import java.util.*; -import edu.harvard.hul.ois.jhove.*; - /** * Data Entry URL Box. - * - * @author Gary McGath * + * @author Gary McGath */ public class DataEntryURLBox extends JP2Box { - /** - * Constructor with superbox. - * - * @param parent parent superbox of this box - */ - public DataEntryURLBox(RandomAccessFile raf, BoxHolder parent) { - super(raf, parent); - } - - /** Reads the box, putting appropriate information in - * the RepInfo object. setModule, setBoxHeader, - * setRepInfo and setDataInputStream must be called - * before readBox is called. - * readBox must completely consume the - * box, so that the next byte to be read by the - * DataInputStream is the FF byte of the next Box. - */ - @Override - public boolean readBox() throws IOException { - initBytesRead (); - for (int i = 0; i < 4; i++) { - // version and flags must be 0. - // If they aren't, keep going, since we can. - int v = ModuleBase.readUnsignedByte (_dstrm, _module); - if (v != 0) { - _repInfo.setMessage (new ErrorMessage - (MessageConstants.JPEG2000_HUL_17, - _module.getFilePos ())); - _repInfo.setValid (false); - break; - } - } - // The URL is encoded as a null-terminated string - // of UTF-8 characters. - List byteList = new ArrayList (512); - for (;;) { - int b = ModuleBase.readUnsignedByte(_dstrm, _module); - if (b == 0) { - break; - } - byteList.add (new Byte ((byte) b)); - } - // Turn the Byte List into a byte array. (Is there a better - // way to do this?) - ListIterator li = byteList.listIterator (); - byte byteArr[] = new byte[byteList.size ()]; - int j = 0; - while (li.hasNext ()) { - byteArr[j] = ((Byte)li.next ()).byteValue (); - } - String s = new String (byteArr, "UTF-8"); - if (_parentBox instanceof UUIDInfoBox) { - UUIDInfoBox uu = (UUIDInfoBox) _parentBox; - uu.setURL (s); - } + /** + * Constructor with superbox. + * + * @param parent parent superbox of this box + */ + public DataEntryURLBox(RandomAccessFile raf, BoxHolder parent) { + super(raf, parent); + } - finalizeBytesRead (); - return true; + /** + * Reads the box, putting appropriate information in the RepInfo object. setModule, setBoxHeader, + * setRepInfo and setDataInputStream must be called before readBox is called. + * readBox must completely consume the box, so that the next byte to be read by the + * DataInputStream is the FF byte of the next Box. + */ + @Override + public boolean readBox() throws IOException { + initBytesRead(); + for (int i = 0; i < 4; i++) { + // version and flags must be 0. + // If they aren't, keep going, since we can. + int v = ModuleBase.readUnsignedByte(_dstrm, _module); + if (v != 0) { + _repInfo.setMessage( + new ErrorMessage(MessageConstants.JPEG2000_HUL_17, _module.getFilePos())); + _repInfo.setValid(false); + break; + } } - - /** Returns the name of the Box. */ - @Override - protected String getSelfPropName () - { - return "Data Entry URL Box"; + // The URL is encoded as a null-terminated string + // of UTF-8 characters. + List byteList = new ArrayList(512); + for (; ; ) { + int b = ModuleBase.readUnsignedByte(_dstrm, _module); + if (b == 0) { + break; + } + byteList.add(new Byte((byte) b)); + } + // Turn the Byte List into a byte array. (Is there a better + // way to do this?) + ListIterator li = byteList.listIterator(); + byte byteArr[] = new byte[byteList.size()]; + int j = 0; + while (li.hasNext()) { + byteArr[j] = ((Byte) li.next()).byteValue(); } + String s = new String(byteArr, "UTF-8"); + if (_parentBox instanceof UUIDInfoBox) { + UUIDInfoBox uu = (UUIDInfoBox) _parentBox; + uu.setURL(s); + } + + finalizeBytesRead(); + return true; + } + + /** Returns the name of the Box. */ + @Override + protected String getSelfPropName() { + return "Data Entry URL Box"; + } } diff --git a/jhove-modules/jpeg2000-hul/src/main/java/edu/harvard/hul/ois/jhove/module/jpeg2000/DefaultBox.java b/jhove-modules/jpeg2000-hul/src/main/java/edu/harvard/hul/ois/jhove/module/jpeg2000/DefaultBox.java index e7622ef92..66661ed17 100644 --- a/jhove-modules/jpeg2000-hul/src/main/java/edu/harvard/hul/ois/jhove/module/jpeg2000/DefaultBox.java +++ b/jhove-modules/jpeg2000-hul/src/main/java/edu/harvard/hul/ois/jhove/module/jpeg2000/DefaultBox.java @@ -1,45 +1,36 @@ -/********************************************************************** - * Jhove - JSTOR/Harvard Object Validation Environment - * Copyright 2004 by JSTOR and the President and Fellows of Harvard College - **********************************************************************/ - +/** + * ******************************************************************** Jhove - JSTOR/Harvard Object + * Validation Environment Copyright 2004 by JSTOR and the President and Fellows of Harvard College + * ******************************************************************** + */ package edu.harvard.hul.ois.jhove.module.jpeg2000; import java.io.*; /** + * Default class for Boxes that have not yet been implemented. Also used for the "free" box, which + * by definition contains no information. * - * Default class for Boxes that have not yet been implemented. - * Also used for the "free" box, which by definition contains - * no information. - * * @author Gary McGath - * */ public class DefaultBox extends JP2Box { - /** - * Constructor. - */ - public DefaultBox(RandomAccessFile raf) { - super(raf); - } - - /** - * Constructor with superbox. - */ - public DefaultBox (RandomAccessFile raf, BoxHolder parent) - { - super (raf, parent); - } + /** Constructor. */ + public DefaultBox(RandomAccessFile raf) { + super(raf); + } - /* (non-Javadoc) - * @see edu.harvard.hul.ois.jhove.module.jpeg2000.JP2Box#readBox() - */ - @Override - public boolean readBox() throws IOException { - skipBox (); - return true; - } + /** Constructor with superbox. */ + public DefaultBox(RandomAccessFile raf, BoxHolder parent) { + super(raf, parent); + } + /* (non-Javadoc) + * @see edu.harvard.hul.ois.jhove.module.jpeg2000.JP2Box#readBox() + */ + @Override + public boolean readBox() throws IOException { + skipBox(); + return true; + } } diff --git a/jhove-modules/jpeg2000-hul/src/main/java/edu/harvard/hul/ois/jhove/module/jpeg2000/DefaultMarkerSegment.java b/jhove-modules/jpeg2000-hul/src/main/java/edu/harvard/hul/ois/jhove/module/jpeg2000/DefaultMarkerSegment.java index ae99dfe38..c683fe0f1 100644 --- a/jhove-modules/jpeg2000-hul/src/main/java/edu/harvard/hul/ois/jhove/module/jpeg2000/DefaultMarkerSegment.java +++ b/jhove-modules/jpeg2000-hul/src/main/java/edu/harvard/hul/ois/jhove/module/jpeg2000/DefaultMarkerSegment.java @@ -1,48 +1,38 @@ -/********************************************************************** - * Jhove - JSTOR/Harvard Object Validation Environment - * Copyright 2004 by JSTOR and the President and Fellows of Harvard College +/** + * ******************************************************************** Jhove - JSTOR/Harvard Object + * Validation Environment Copyright 2004 by JSTOR and the President and Fellows of Harvard College * - **********************************************************************/ - + *

******************************************************************** + */ package edu.harvard.hul.ois.jhove.module.jpeg2000; import java.io.*; /** - * This class is used to handle any unrecognized or unimplemented - * marker segment in a codestream. - * - * @author Gary McGath + * This class is used to handle any unrecognized or unimplemented marker segment in a codestream. * - * To change the template for this generated type comment go to - * Window>Preferences>Java>Code Generation>Code and Comments + * @author Gary McGath + *

To change the template for this generated type comment go to + * Window>Preferences>Java>Code Generation>Code and Comments */ public class DefaultMarkerSegment extends MarkerSegment { - /** - * - */ - public DefaultMarkerSegment() { - super(); - } - + /** */ + public DefaultMarkerSegment() { + super(); + } - /** - * Processes the marker segment. The DataInputStream - * will be at the point of having read the marker code. The - * process method must consume exactly the number - * of bytes remaining in the marker segment; for a marker, - * this number will always be 0. - * - * @param bytesToEat The number of bytes that must be consumed. - * For a Marker, this number will always be 0. - * If it is 0 for a MarkerSegment, the - * number of bytes to consume is unknown. - */ - @Override - protected boolean process (int bytesToEat) throws IOException - { - _module.skipBytes (_dstream, bytesToEat, _module); - return true; - } + /** + * Processes the marker segment. The DataInputStream will be at the point of having read the + * marker code. The process method must consume exactly the number of bytes remaining + * in the marker segment; for a marker, this number will always be 0. + * + * @param bytesToEat The number of bytes that must be consumed. For a Marker, this number will + * always be 0. If it is 0 for a MarkerSegment, the number of bytes to consume is unknown. + */ + @Override + protected boolean process(int bytesToEat) throws IOException { + _module.skipBytes(_dstream, bytesToEat, _module); + return true; + } } diff --git a/jhove-modules/jpeg2000-hul/src/main/java/edu/harvard/hul/ois/jhove/module/jpeg2000/DesiredReproBox.java b/jhove-modules/jpeg2000-hul/src/main/java/edu/harvard/hul/ois/jhove/module/jpeg2000/DesiredReproBox.java index 17f7c588f..ba1148f0a 100644 --- a/jhove-modules/jpeg2000-hul/src/main/java/edu/harvard/hul/ois/jhove/module/jpeg2000/DesiredReproBox.java +++ b/jhove-modules/jpeg2000-hul/src/main/java/edu/harvard/hul/ois/jhove/module/jpeg2000/DesiredReproBox.java @@ -1,80 +1,73 @@ -/********************************************************************** - * Jhove - JSTOR/Harvard Object Validation Environment - * Copyright 2004 by JSTOR and the President and Fellows of Harvard College - **********************************************************************/ - +/** + * ******************************************************************** Jhove - JSTOR/Harvard Object + * Validation Environment Copyright 2004 by JSTOR and the President and Fellows of Harvard College + * ******************************************************************** + */ package edu.harvard.hul.ois.jhove.module.jpeg2000; import java.io.*; /** - * * Desired Reproductions Box (JPX). - * - * See ISO/IEC FCD15444-2: 2000, L.9.15 - * - * @author Gary McGath * + *

See ISO/IEC FCD15444-2: 2000, L.9.15 + * + * @author Gary McGath */ public class DesiredReproBox extends JP2Box { + /** + * Constructor with superbox. + * + * @param parent parent superbox of this box + */ + public DesiredReproBox(RandomAccessFile raf, BoxHolder parent) { + super(raf, parent); + } - /** - * Constructor with superbox. - * - * @param parent parent superbox of this box - */ - public DesiredReproBox(RandomAccessFile raf, BoxHolder parent) { - super(raf, parent); - } + /** + * Reads the box, putting appropriate information in the RepInfo object. setModule, setBoxHeader, + * setRepInfo and setDataInputStream must be called before readBox is called. + * readBox must completely consume the box, so that the next byte to be read by the + * DataInputStream is the FF byte of the next Box. + */ + @Override + public boolean readBox() throws IOException { + // Oddly enough, this box is NOT required to be + // at the top level of the file. However, there + // can be only one in the file. + // It can have multiple subboxes, but the only + // significant one is the Graphics Technology + // Standard Output box, which simply holds an + // ISO profile. - /** Reads the box, putting appropriate information in - * the RepInfo object. setModule, setBoxHeader, - * setRepInfo and setDataInputStream must be called - * before readBox is called. - * readBox must completely consume the - * box, so that the next byte to be read by the - * DataInputStream is the FF byte of the next Box. - */ - @Override - public boolean readBox() throws IOException { - // Oddly enough, this box is NOT required to be - // at the top level of the file. However, there - // can be only one in the file. - // It can have multiple subboxes, but the only - // significant one is the Graphics Technology - // Standard Output box, which simply holds an - // ISO profile. - - initBytesRead (); - JP2Box box = null; - while (hasNext ()) { - box = (JP2Box) next (); - if (box == null) { - break; - } - if (box instanceof GTSOBox) { - if (!box.readBox ()) { - return false; - } - } - else { - box.skipBox (); - } + initBytesRead(); + JP2Box box = null; + while (hasNext()) { + box = (JP2Box) next(); + if (box == null) { + break; + } + if (box instanceof GTSOBox) { + if (!box.readBox()) { + return false; } - - // There is a NISO property for reporting the - // profile name. I should figure out how to - // extract the name of a profile. - - finalizeBytesRead (); - return false; + } else { + box.skipBox(); + } } - /** Returns the name of the Box. */ - @Override - protected String getSelfPropName () - { - return "Desired Reproductions Box"; - } + // There is a NISO property for reporting the + // profile name. I should figure out how to + // extract the name of a profile. + + finalizeBytesRead(); + return false; + } + + /** Returns the name of the Box. */ + @Override + protected String getSelfPropName() { + return "Desired Reproductions Box"; + } } diff --git a/jhove-modules/jpeg2000-hul/src/main/java/edu/harvard/hul/ois/jhove/module/jpeg2000/DigSignatureBox.java b/jhove-modules/jpeg2000-hul/src/main/java/edu/harvard/hul/ois/jhove/module/jpeg2000/DigSignatureBox.java index 051153686..204ef1b4a 100644 --- a/jhove-modules/jpeg2000-hul/src/main/java/edu/harvard/hul/ois/jhove/module/jpeg2000/DigSignatureBox.java +++ b/jhove-modules/jpeg2000-hul/src/main/java/edu/harvard/hul/ois/jhove/module/jpeg2000/DigSignatureBox.java @@ -1,8 +1,8 @@ -/********************************************************************** - * Jhove - JSTOR/Harvard Object Validation Environment - * Copyright 2004 by JSTOR and the President and Fellows of Harvard College - **********************************************************************/ - +/** + * ******************************************************************** Jhove - JSTOR/Harvard Object + * Validation Environment Copyright 2004 by JSTOR and the President and Fellows of Harvard College + * ******************************************************************** + */ package edu.harvard.hul.ois.jhove.module.jpeg2000; import edu.harvard.hul.ois.jhove.*; @@ -11,165 +11,136 @@ import java.util.*; /** - * Digital Signature Box (JPX). - * See ISO/IEC FCD15444-2: 2000, L.9.17 - * - * Only the MD5 and SHA-1 - * algorithms are supported. + * Digital Signature Box (JPX). See ISO/IEC FCD15444-2: 2000, L.9.17 * - * @author Gary McGath + *

Only the MD5 and SHA-1 algorithms are supported. * + * @author Gary McGath */ public class DigSignatureBox extends JP2Box { - /** - * Constructor with superbox. - * - * @param parent parent superbox of this box - */ - public DigSignatureBox(RandomAccessFile raf, BoxHolder parent) { - super(raf, parent); + /** + * Constructor with superbox. + * + * @param parent parent superbox of this box + */ + public DigSignatureBox(RandomAccessFile raf, BoxHolder parent) { + super(raf, parent); + } + + /** + * Reads the box, putting appropriate information in the RepInfo object. setModule, setBoxHeader, + * setRepInfo and setDataInputStream must be called before readBox is called. + * readBox must completely consume the box, so that the next byte to be read by the + * DataInputStream is the FF byte of the next Box. + */ + @Override + public boolean readBox() throws IOException { + initBytesRead(); + int sizeLeft = (int) _boxHeader.getDataLength(); + // This may occur "anywhere in the file." Does that + // mean that all superboxes should check it as a possible + // subbox? + + List propList = new ArrayList<>(10); + int styp = ModuleBase.readUnsignedByte(_dstrm, _module); + if (styp > 5) { + // Known signature types are 0-5 + _repInfo.setMessage(new ErrorMessage(MessageConstants.JPEG2000_HUL_19, _module.getFilePos())); + _repInfo.setValid(false); } + propList.add(_module.addIntegerProperty("Type", styp, JP2Strings.digitalSigTypeStr)); - /** Reads the box, putting appropriate information in - * the RepInfo object. setModule, setBoxHeader, - * setRepInfo and setDataInputStream must be called - * before readBox is called. - * readBox must completely consume the - * box, so that the next byte to be read by the - * DataInputStream is the FF byte of the next Box. - */ - @Override - public boolean readBox() throws IOException { - initBytesRead (); - int sizeLeft = (int) _boxHeader.getDataLength (); - // This may occur "anywhere in the file." Does that - // mean that all superboxes should check it as a possible - // subbox? - - List propList = new ArrayList<> (10); - int styp = ModuleBase.readUnsignedByte (_dstrm, _module); - if (styp > 5) { - // Known signature types are 0-5 - _repInfo.setMessage (new ErrorMessage - (MessageConstants.JPEG2000_HUL_19, - _module.getFilePos ())); - _repInfo.setValid (false); - } - propList.add (_module.addIntegerProperty ("Type", - styp, JP2Strings.digitalSigTypeStr)); - - int ptyp = ModuleBase.readUnsignedByte (_dstrm, _module); - if (ptyp > 1) { - _repInfo.setMessage (new ErrorMessage - (MessageConstants.JPEG2000_HUL_18, - _module.getFilePos ())); - _repInfo.setValid (false); - } - propList.add (_module.addIntegerProperty ("PointerType", - styp, JP2Strings.digitalSigPtrTypeStr)); - sizeLeft -= 2; - - long off = 0; - long len = 0; - if (ptyp == 1) { - off = _module.readSignedLong (_dstrm); - len = _module.readSignedLong (_dstrm); - propList.add (new Property ("Offset", - PropertyType.LONG, off)); - propList.add (new Property ("Length", - PropertyType.LONG, len)); - sizeLeft -= 8; - } - - byte[] data = new byte[sizeLeft]; - ModuleBase.readByteBuf(_dstrm, data, _module); - - if (styp == 0 || styp == 1) { - try { - // If the whole file is indicated, set the - // parameters accordingly - if (ptyp == 0) { - off = 0; - len = _raf.length (); - } - propList.add (new Property ("Valid", - PropertyType.BOOLEAN, isSigValid - (styp, off, len, data))); - } - catch (NoSuchAlgorithmException e) { - // In the unlikely event the algorithms aren't - // available, just don't report validity. - } - catch (IOException f) {} + int ptyp = ModuleBase.readUnsignedByte(_dstrm, _module); + if (ptyp > 1) { + _repInfo.setMessage(new ErrorMessage(MessageConstants.JPEG2000_HUL_18, _module.getFilePos())); + _repInfo.setValid(false); + } + propList.add(_module.addIntegerProperty("PointerType", styp, JP2Strings.digitalSigPtrTypeStr)); + sizeLeft -= 2; + + long off = 0; + long len = 0; + if (ptyp == 1) { + off = _module.readSignedLong(_dstrm); + len = _module.readSignedLong(_dstrm); + propList.add(new Property("Offset", PropertyType.LONG, off)); + propList.add(new Property("Length", PropertyType.LONG, len)); + sizeLeft -= 8; + } + + byte[] data = new byte[sizeLeft]; + ModuleBase.readByteBuf(_dstrm, data, _module); + + if (styp == 0 || styp == 1) { + try { + // If the whole file is indicated, set the + // parameters accordingly + if (ptyp == 0) { + off = 0; + len = _raf.length(); } - - _module.addDigitalSignatureProp (new Property - ("DigitalSignature", - PropertyType.PROPERTY, - PropertyArity.LIST, - propList)); - finalizeBytesRead (); - return true; + propList.add(new Property("Valid", PropertyType.BOOLEAN, isSigValid(styp, off, len, data))); + } catch (NoSuchAlgorithmException e) { + // In the unlikely event the algorithms aren't + // available, just don't report validity. + } catch (IOException f) { + } } - /** Returns the name of the Box. */ - @Override - protected String getSelfPropName () - { - return "Digital Signature Box"; + _module.addDigitalSignatureProp( + new Property("DigitalSignature", PropertyType.PROPERTY, PropertyArity.LIST, propList)); + finalizeBytesRead(); + return true; + } + + /** Returns the name of the Box. */ + @Override + protected String getSelfPropName() { + return "Digital Signature Box"; + } + + /* Check if the signature is valid. Only applicable + * for styp of 0 (MD5) or 1 (SHA-1). */ + private boolean isSigValid(int styp, long off, long len, byte[] data) + throws NoSuchAlgorithmException { + MessageDigest digest; + if (styp == 0) { + digest = MessageDigest.getInstance("MD5"); + } else { + digest = MessageDigest.getInstance("SHA-1"); } + // With the new robustness of RAFInputStream, it should + // no longer be necessary to save the file position. + // long savePos = _raf.getFilePointer (); + // If the whole file is indicated, set the parameters + // accordingly + try { + _raf.seek(off); + int buflen = (len < 65536 ? (int) len : 65536); + byte[] buf = new byte[buflen]; + while (len > 0) { + int btr = (len < buflen ? (int) len : buflen); + int bytesRead = _raf.read(buf, 0, btr); + digest.update(buf, 0, bytesRead); + } + byte[] digestVal = digest.digest(); - /* Check if the signature is valid. Only applicable - * for styp of 0 (MD5) or 1 (SHA-1). */ - private boolean isSigValid (int styp, - long off, long len, - byte[] data) - throws NoSuchAlgorithmException - { - MessageDigest digest; - if (styp == 0) { - digest = MessageDigest.getInstance ("MD5"); - } - else { - digest = MessageDigest.getInstance ("SHA-1"); - } - - // With the new robustness of RAFInputStream, it should - // no longer be necessary to save the file position. - //long savePos = _raf.getFilePointer (); - - // If the whole file is indicated, set the parameters - // accordingly - try { - _raf.seek (off); - int buflen = (len < 65536 ? (int) len : 65536); - byte[] buf = new byte[buflen]; - while (len > 0) { - int btr = (len < buflen ? (int) len : buflen); - int bytesRead = _raf.read (buf, 0, btr); - digest.update (buf, 0, bytesRead); - } - byte[] digestVal = digest.digest (); - - // Check if we suffer from indigestion - if (digestVal.length != data.length) { - return false; - } - for (int i = 0; i < data.length; i++) { - if (digestVal[i] != data[i]) { - return false; - } - } - // Our digestion is good - return true; + // Check if we suffer from indigestion + if (digestVal.length != data.length) { + return false; + } + for (int i = 0; i < data.length; i++) { + if (digestVal[i] != data[i]) { + return false; } - catch (IOException e) { - return false; // most likely invalid range - } - + } + // Our digestion is good + return true; + } catch (IOException e) { + return false; // most likely invalid range } - + } } diff --git a/jhove-modules/jpeg2000-hul/src/main/java/edu/harvard/hul/ois/jhove/module/jpeg2000/FileTypeBox.java b/jhove-modules/jpeg2000-hul/src/main/java/edu/harvard/hul/ois/jhove/module/jpeg2000/FileTypeBox.java index e857675ff..98ae1a84f 100644 --- a/jhove-modules/jpeg2000-hul/src/main/java/edu/harvard/hul/ois/jhove/module/jpeg2000/FileTypeBox.java +++ b/jhove-modules/jpeg2000-hul/src/main/java/edu/harvard/hul/ois/jhove/module/jpeg2000/FileTypeBox.java @@ -1,144 +1,122 @@ -/********************************************************************** - * Jhove - JSTOR/Harvard Object Validation Environment - * Copyright 2004 by JSTOR and the President and Fellows of Harvard College +/** + * ******************************************************************** Jhove - JSTOR/Harvard Object + * Validation Environment Copyright 2004 by JSTOR and the President and Fellows of Harvard College * - **********************************************************************/ - + *

******************************************************************** + */ package edu.harvard.hul.ois.jhove.module.jpeg2000; +import edu.harvard.hul.ois.jhove.*; import java.io.*; import java.util.*; -import edu.harvard.hul.ois.jhove.*; /** - * File Type Box. - * See I.5.2 in ISO/IEC 15444-1:2000 - * - * A File Type box can occur only as the first thing after the - * Signature Box, so this will be invoked only directly from - * the Module. - * - * @author Gary McGath + * File Type Box. See I.5.2 in ISO/IEC 15444-1:2000 + * + *

A File Type box can occur only as the first thing after the Signature Box, so this will be + * invoked only directly from the Module. * + * @author Gary McGath */ public class FileTypeBox extends JP2Box { - /** - * Constructor. - */ - public FileTypeBox(RandomAccessFile raf) { - super(raf); - } + /** Constructor. */ + public FileTypeBox(RandomAccessFile raf) { + super(raf); + } - /** - * The constructor with superbox is meaningless. - */ - public FileTypeBox(RandomAccessFile raf, JP2Box parent) { - super (raf); + /** The constructor with superbox is meaningless. */ + public FileTypeBox(RandomAccessFile raf, JP2Box parent) { + super(raf); + } + + /** + * Reads the box, putting appropriate information in the RepInfo object. setModule, setBoxHeader, + * setRepInfo and setDataInputStream must be called before readBox is called. + * readBox must completely consume the box, so that the next byte to be read by the + * DataInputStream is the FF byte of the next Box. + */ + @Override + public boolean readBox() throws IOException { + String brand = _module.read4Chars(_dstrm); + _module.addProperty(new Property("Brand", PropertyType.STRING, brand)); + // 12 bytes have been read + + // Brand indicates intended compliance + if (!"jp2 ".equals(brand)) { + _module.setJP2Compliant(false); + } + if (!"jpx ".equals(brand)) { + _module.setJPXCompliant(false); } - /** Reads the box, putting appropriate information in - * the RepInfo object. setModule, setBoxHeader, - * setRepInfo and setDataInputStream must be called - * before readBox is called. - * readBox must completely consume the - * box, so that the next byte to be read by the - * DataInputStream is the FF byte of the next Box. - */ - @Override - public boolean readBox() throws IOException { - String brand = _module.read4Chars(_dstrm); - _module.addProperty (new Property ("Brand", - PropertyType.STRING, - brand)); - // 12 bytes have been read - - // Brand indicates intended compliance - if (!"jp2 ".equals (brand)) { - _module.setJP2Compliant (false); - } - if (!"jpx ".equals (brand)) { - _module.setJPXCompliant (false); - } + long minv = _module.readUnsignedInt(_dstrm); + _module.addProperty(new Property("MinorVersion", PropertyType.LONG, new Long(minv))); + // 16 bytes have been read - long minv = _module.readUnsignedInt(_dstrm); - _module.addProperty (new Property ("MinorVersion", - PropertyType.LONG, - new Long (minv))); - // 16 bytes have been read - - // Read the compatibility list. It takes up the rest - // on the box length. - int ncomp = (((int) _boxHeader.getLength ()) - 16) / 4; - if (ncomp < 1) { - _repInfo.setMessage (new ErrorMessage - (MessageConstants.JPEG2000_HUL_21, - _module.getFilePos ())); - _repInfo.setWellFormed (false); - return false; - } - List compList = new ArrayList<> (ncomp); - boolean eflag = false; - StringBuffer hexcitem = new StringBuffer (8); - for (int i = 0; i < ncomp; i++) { - String citem = _module.read4Chars (_dstrm); - - // Some files have a count of entries, which isn't supposed - // to be there. If we see any nulls, report an ill-formed condition. - // For each entry, we build a hex string in hexcitem just in case - // it's necessary to report the string in hex. - char[] cbytes = citem.toCharArray(); - boolean binflag = false; - for (int j = 0; j < cbytes.length; j++) { - int ch = cbytes[j]; - hexcitem.append (Integer.toHexString(ch)); - if (ch == 0 || ch >= 0X7F) { - binflag = true; - if (!eflag) { - eflag = true; // Avoid multiple report of same error - _repInfo.setValid (false); - _repInfo.setMessage (new ErrorMessage - (MessageConstants.JPEG2000_HUL_20, - _module.getFilePos ())); - } - - } - } - if (!binflag) { - compList.add (citem); - } - else { - compList.add (hexifyString (citem)); - } - } - _module.addProperty (new Property ("Compatibility", - PropertyType.STRING, - PropertyArity.LIST, - compList)); - // All the bytes have been read - return true; + // Read the compatibility list. It takes up the rest + // on the box length. + int ncomp = (((int) _boxHeader.getLength()) - 16) / 4; + if (ncomp < 1) { + _repInfo.setMessage(new ErrorMessage(MessageConstants.JPEG2000_HUL_21, _module.getFilePos())); + _repInfo.setWellFormed(false); + return false; } - - private static String hexifyString (String s) - { - StringBuffer retval = new StringBuffer (2 * s.length () + 2); - retval.append ("0X"); - char[] chs = s.toCharArray(); - for (int i = 0; i < chs.length; i++) { - String hs = Integer.toHexString (chs[i]); - // Pad to 2 characters - if (hs.length () == 1) { - retval.append ('0'); - } - retval.append (hs); + List compList = new ArrayList<>(ncomp); + boolean eflag = false; + StringBuffer hexcitem = new StringBuffer(8); + for (int i = 0; i < ncomp; i++) { + String citem = _module.read4Chars(_dstrm); + + // Some files have a count of entries, which isn't supposed + // to be there. If we see any nulls, report an ill-formed condition. + // For each entry, we build a hex string in hexcitem just in case + // it's necessary to report the string in hex. + char[] cbytes = citem.toCharArray(); + boolean binflag = false; + for (int j = 0; j < cbytes.length; j++) { + int ch = cbytes[j]; + hexcitem.append(Integer.toHexString(ch)); + if (ch == 0 || ch >= 0X7F) { + binflag = true; + if (!eflag) { + eflag = true; // Avoid multiple report of same error + _repInfo.setValid(false); + _repInfo.setMessage( + new ErrorMessage(MessageConstants.JPEG2000_HUL_20, _module.getFilePos())); + } } - return retval.toString (); + } + if (!binflag) { + compList.add(citem); + } else { + compList.add(hexifyString(citem)); + } } + _module.addProperty( + new Property("Compatibility", PropertyType.STRING, PropertyArity.LIST, compList)); + // All the bytes have been read + return true; + } - /** Returns the name of the Box. */ - @Override - protected String getSelfPropName () - { - return "File Type Box"; + private static String hexifyString(String s) { + StringBuffer retval = new StringBuffer(2 * s.length() + 2); + retval.append("0X"); + char[] chs = s.toCharArray(); + for (int i = 0; i < chs.length; i++) { + String hs = Integer.toHexString(chs[i]); + // Pad to 2 characters + if (hs.length() == 1) { + retval.append('0'); + } + retval.append(hs); } + return retval.toString(); + } + + /** Returns the name of the Box. */ + @Override + protected String getSelfPropName() { + return "File Type Box"; + } } diff --git a/jhove-modules/jpeg2000-hul/src/main/java/edu/harvard/hul/ois/jhove/module/jpeg2000/FragmentInputStream.java b/jhove-modules/jpeg2000-hul/src/main/java/edu/harvard/hul/ois/jhove/module/jpeg2000/FragmentInputStream.java index ccd696b9c..507dc5b5e 100644 --- a/jhove-modules/jpeg2000-hul/src/main/java/edu/harvard/hul/ois/jhove/module/jpeg2000/FragmentInputStream.java +++ b/jhove-modules/jpeg2000-hul/src/main/java/edu/harvard/hul/ois/jhove/module/jpeg2000/FragmentInputStream.java @@ -1,110 +1,97 @@ -/********************************************************************** - * Jhove - JSTOR/Harvard Object Validation Environment - * Copyright 2004 by JSTOR and the President and Fellows of Harvard College - **********************************************************************/ - +/** + * ******************************************************************** Jhove - JSTOR/Harvard Object + * Validation Environment Copyright 2004 by JSTOR and the President and Fellows of Harvard College + * ******************************************************************** + */ package edu.harvard.hul.ois.jhove.module.jpeg2000; import java.io.*; import java.util.*; /** - * A FragmentInputStream provides an interface by which - * the scattered fragments of a Fragment List Box can - * be read as a single stream. Only fragments within - * the originating file are supported, not fragments - * in external files. + * A FragmentInputStream provides an interface by which the scattered fragments of a Fragment List + * Box can be read as a single stream. Only fragments within the originating file are supported, not + * fragments in external files. * * @author Gary McGath - * */ public class FragmentInputStream extends InputStream { - private List _fragments; - private RandomAccessFile _raf; - private ListIterator fragIterator; - private long curFragment[]; - byte[] fragBuffer; - - /* Offset within fragBuffer. -1 indicates fragBuffer - * does not contain valid data. */ - private int bufOffset; - - /* Offset in the file from the start of the current fragment. */ - private int fragOffset; - - /* Size allocated to the buffer. */ - private int _bufSize; + private List _fragments; + private RandomAccessFile _raf; + private ListIterator fragIterator; + private long curFragment[]; + byte[] fragBuffer; - /* Bytes of actual data in the buffer. */ - private int bufBytes; - - /** - * @param fragments List of fragment entries. - * Each fragment entry is an array of two longs, - * with fragment[0] being the length and - * fragment[1] the offset. - */ - public FragmentInputStream(List fragments, RandomAccessFile raf) - { - super(); - _fragments = fragments; - _raf = raf; - init (-1); - } - - public FragmentInputStream (List fragments, - RandomAccessFile raf, - int bufSize) - { - super (); - _fragments = fragments; - _raf = raf; - init (bufSize); - } - - private void init (int bufSize) - { - fragIterator = _fragments.listIterator (); - // If no buffer size was specified, assign a default - // size. - if (bufSize <= 0) { - bufSize = 8192; - } - _bufSize = bufSize; - fragBuffer = new byte[bufSize]; - bufOffset = 0; - bufBytes = 0; + /* Offset within fragBuffer. -1 indicates fragBuffer + * does not contain valid data. */ + private int bufOffset; + + /* Offset in the file from the start of the current fragment. */ + private int fragOffset; + + /* Size allocated to the buffer. */ + private int _bufSize; + + /* Bytes of actual data in the buffer. */ + private int bufBytes; + + /** + * @param fragments List of fragment entries. Each fragment entry is an array of two longs, with + * fragment[0] being the length and fragment[1] the offset. + */ + public FragmentInputStream(List fragments, RandomAccessFile raf) { + super(); + _fragments = fragments; + _raf = raf; + init(-1); + } + + public FragmentInputStream(List fragments, RandomAccessFile raf, int bufSize) { + super(); + _fragments = fragments; + _raf = raf; + init(bufSize); + } + + private void init(int bufSize) { + fragIterator = _fragments.listIterator(); + // If no buffer size was specified, assign a default + // size. + if (bufSize <= 0) { + bufSize = 8192; } + _bufSize = bufSize; + fragBuffer = new byte[bufSize]; + bufOffset = 0; + bufBytes = 0; + } - /** - * Returns the next byte from the stream, buffering each fragment - * in turn until the last fragment is exhausted. - * - * @return The next byte of the stream, or -1 to indicate no - * more bytes are available. - */ - @Override - public int read() throws IOException { - if (bufOffset >= bufBytes) { - // We need a fresh buffer read. - if (curFragment == null || fragOffset >= curFragment[1]) { - // We need a new fragment. - if (fragIterator.hasNext ()) { - curFragment = fragIterator.next (); - fragOffset = 0; - } - else { - // No more data available. - return -1; - } - } - _raf.seek(curFragment[0] + fragOffset); - bufBytes = _raf.read(fragBuffer); - fragOffset += bufBytes; - bufOffset = 0; + /** + * Returns the next byte from the stream, buffering each fragment in turn until the last fragment + * is exhausted. + * + * @return The next byte of the stream, or -1 to indicate no more bytes are available. + */ + @Override + public int read() throws IOException { + if (bufOffset >= bufBytes) { + // We need a fresh buffer read. + if (curFragment == null || fragOffset >= curFragment[1]) { + // We need a new fragment. + if (fragIterator.hasNext()) { + curFragment = fragIterator.next(); + fragOffset = 0; + } else { + // No more data available. + return -1; } - return fragBuffer[bufOffset++]; + } + _raf.seek(curFragment[0] + fragOffset); + bufBytes = _raf.read(fragBuffer); + fragOffset += bufBytes; + bufOffset = 0; } - + return fragBuffer[bufOffset++]; + } } diff --git a/jhove-modules/jpeg2000-hul/src/main/java/edu/harvard/hul/ois/jhove/module/jpeg2000/FragmentListBox.java b/jhove-modules/jpeg2000-hul/src/main/java/edu/harvard/hul/ois/jhove/module/jpeg2000/FragmentListBox.java index 9cdb87317..1788ceed2 100644 --- a/jhove-modules/jpeg2000-hul/src/main/java/edu/harvard/hul/ois/jhove/module/jpeg2000/FragmentListBox.java +++ b/jhove-modules/jpeg2000-hul/src/main/java/edu/harvard/hul/ois/jhove/module/jpeg2000/FragmentListBox.java @@ -1,8 +1,8 @@ -/********************************************************************** - * Jhove - JSTOR/Harvard Object Validation Environment - * Copyright 2004 by JSTOR and the President and Fellows of Harvard College - **********************************************************************/ - +/** + * ******************************************************************** Jhove - JSTOR/Harvard Object + * Validation Environment Copyright 2004 by JSTOR and the President and Fellows of Harvard College + * ******************************************************************** + */ package edu.harvard.hul.ois.jhove.module.jpeg2000; import edu.harvard.hul.ois.jhove.*; @@ -10,98 +10,85 @@ import java.util.*; /** - * Fragment List Box (JPX). - * Subbox of Fragment Table box or Cross-Reference box. - * See L.9.6.1 in ISO/IEC FCD15444-2:2000. + * Fragment List Box (JPX). Subbox of Fragment Table box or Cross-Reference box. See L.9.6.1 in + * ISO/IEC FCD15444-2:2000. * * @author Gary McGath - * */ public class FragmentListBox extends JP2Box { - private List _fragmentList; - - - /** - * Constructor with superbox. - * - * @param parent parent superbox of this box. - * The parent may be a FragmentTableBox - * or a CrossReferenceBox. - */ - public FragmentListBox(RandomAccessFile raf, BoxHolder parent) { - super (raf, parent); - } - - /** Reads the box, putting appropriate information in - * the RepInfo object. setModule, setBoxHeader, - * setRepInfo and setDataInputStream must be called - * before readBox is called. - * readBox must completely consume the - * box, so that the next byte to be read by the - * DataInputStream is the FF byte of the next Box. - */ - @Override - public boolean readBox() throws IOException { - if (!_module.isJP2HdrSeen()) { - _repInfo.setMessage (new ErrorMessage - (MessageConstants.JPEG2000_HUL_34, _module.getFilePos ())); - return false; - } - initBytesRead (); - int len = (int) _boxHeader.getDataLength (); + private List _fragmentList; - int nFrags = _module.readUnsignedShort (_dstrm); - if (_boxHeader.getLength () != 0 && len != 14 * nFrags + 2) { - _repInfo.setMessage - (new ErrorMessage - (MessageConstants.JPEG2000_HUL_23, - _module.getFilePos ())); - _repInfo.setWellFormed (false); - return false; - } - _fragmentList = new ArrayList<> (nFrags); - for (int i = 0; i < nFrags; i++) { - long offset = _module.readSignedLong (_dstrm); - long fragLen = _module.readUnsignedInt (_dstrm); - int dataRef = _module.readUnsignedShort (_dstrm); + /** + * Constructor with superbox. + * + * @param parent parent superbox of this box. The parent may be a FragmentTableBox or a + * CrossReferenceBox. + */ + public FragmentListBox(RandomAccessFile raf, BoxHolder parent) { + super(raf, parent); + } - // If dataRef is nonzero, the stream is outside the file, - // and all we can do is report the reference. In fact, - // if any of the fragments are outside the file, we - // have to punt. So we should collect all the - // fragments and then read the stream. + /** + * Reads the box, putting appropriate information in the RepInfo object. setModule, setBoxHeader, + * setRepInfo and setDataInputStream must be called before readBox is called. + * readBox must completely consume the box, so that the next byte to be read by the + * DataInputStream is the FF byte of the next Box. + */ + @Override + public boolean readBox() throws IOException { + if (!_module.isJP2HdrSeen()) { + _repInfo.setMessage(new ErrorMessage(MessageConstants.JPEG2000_HUL_34, _module.getFilePos())); + return false; + } + initBytesRead(); + int len = (int) _boxHeader.getDataLength(); - if (dataRef != 0) { - _fragmentList = null; // no can do fragments - _repInfo.setMessage (new InfoMessage - (MessageConstants.INF_FRAGMENT_LIST_BOX_EXT_FILE_REFERENCE, - _module.getFilePos())); - } - else if (_fragmentList != null) { - long[] frag = new long[2]; - frag[0] = offset; - frag[1] = fragLen; - _fragmentList.add (frag); - } - } - finalizeBytesRead (); - return true; + int nFrags = _module.readUnsignedShort(_dstrm); + if (_boxHeader.getLength() != 0 && len != 14 * nFrags + 2) { + _repInfo.setMessage(new ErrorMessage(MessageConstants.JPEG2000_HUL_23, _module.getFilePos())); + _repInfo.setWellFormed(false); + return false; } + _fragmentList = new ArrayList<>(nFrags); + for (int i = 0; i < nFrags; i++) { + long offset = _module.readSignedLong(_dstrm); + long fragLen = _module.readUnsignedInt(_dstrm); + int dataRef = _module.readUnsignedShort(_dstrm); + // If dataRef is nonzero, the stream is outside the file, + // and all we can do is report the reference. In fact, + // if any of the fragments are outside the file, we + // have to punt. So we should collect all the + // fragments and then read the stream. - /** Returns the fragment list. If there are external references - * to fragments, returns null; in this case, a warning message - * has been added to the RepInfo object. */ - protected List getFragmentList () - { - return _fragmentList; + if (dataRef != 0) { + _fragmentList = null; // no can do fragments + _repInfo.setMessage( + new InfoMessage( + MessageConstants.INF_FRAGMENT_LIST_BOX_EXT_FILE_REFERENCE, _module.getFilePos())); + } else if (_fragmentList != null) { + long[] frag = new long[2]; + frag[0] = offset; + frag[1] = fragLen; + _fragmentList.add(frag); + } } + finalizeBytesRead(); + return true; + } - /** Returns the name of the Box. */ - @Override - protected String getSelfPropName () - { - return "Fragment List Box"; - } + /** + * Returns the fragment list. If there are external references to fragments, returns null; in this + * case, a warning message has been added to the RepInfo object. + */ + protected List getFragmentList() { + return _fragmentList; + } + + /** Returns the name of the Box. */ + @Override + protected String getSelfPropName() { + return "Fragment List Box"; + } } diff --git a/jhove-modules/jpeg2000-hul/src/main/java/edu/harvard/hul/ois/jhove/module/jpeg2000/FragmentTableBox.java b/jhove-modules/jpeg2000-hul/src/main/java/edu/harvard/hul/ois/jhove/module/jpeg2000/FragmentTableBox.java index 19d748e17..b02c837ae 100644 --- a/jhove-modules/jpeg2000-hul/src/main/java/edu/harvard/hul/ois/jhove/module/jpeg2000/FragmentTableBox.java +++ b/jhove-modules/jpeg2000-hul/src/main/java/edu/harvard/hul/ois/jhove/module/jpeg2000/FragmentTableBox.java @@ -1,8 +1,8 @@ -/********************************************************************** - * Jhove - JSTOR/Harvard Object Validation Environment - * Copyright 2004 by JSTOR and the President and Fellows of Harvard College - **********************************************************************/ - +/** + * ******************************************************************** Jhove - JSTOR/Harvard Object + * Validation Environment Copyright 2004 by JSTOR and the President and Fellows of Harvard College + * ******************************************************************** + */ package edu.harvard.hul.ois.jhove.module.jpeg2000; import edu.harvard.hul.ois.jhove.*; @@ -10,96 +10,81 @@ import java.util.List; /** - * Fragment Table Box (JPX). - * See L.9.6 in ISO/IEC FCD15444-2:2000. - * @author Gary McGath + * Fragment Table Box (JPX). See L.9.6 in ISO/IEC FCD15444-2:2000. * + * @author Gary McGath */ public class FragmentTableBox extends JP2Box { - - - /** - * Constructor with superbox. - * - * @param parent parent superbox of this box - * or TopLevelBoxHolder - */ - public FragmentTableBox (RandomAccessFile raf, BoxHolder parent) - { - super (raf, parent); - } + /** + * Constructor with superbox. + * + * @param parent parent superbox of this box or TopLevelBoxHolder + */ + public FragmentTableBox(RandomAccessFile raf, BoxHolder parent) { + super(raf, parent); + } - /** Reads the box, putting appropriate information in - * the RepInfo object. setModule, setBoxHeader, - * setRepInfo and setDataInputStream must be called - * before readBox is called. - * readBox must completely consume the - * box, so that the next byte to be read by the - * DataInputStream is the FF byte of the next Box. - */ - @Override - public boolean readBox() throws IOException { - boolean retval = true; - initBytesRead (); - hasBoxes = true; - //int state = 0; // state variable for checking progress of boxes - JP2Box box = (JP2Box) next (); - if (box == null) { - return false; // empty box can't be right - } - // OK, how do I deal with subboxes? Should be able to handle - // them the same way as in the module, except that a different - // set of boxes is acceptable. The dispatcher (in BoxHeader?) - // may need to be given a list of boxes that are acceptable for - // any given context. For the top-level module, the list - // should probably (maybe) be in BoxHeader, but for boxes, the - // list of acceptable subboxes must be provided explicitly. - // Maybe an additional argument in the BoxHeader constructor. - if (box instanceof FragmentListBox) { - FragmentListBox fbox = (FragmentListBox) box; - if (!fbox.readBox ()) { - return false; - } - List fragList = fbox.getFragmentList(); - // fragList will be null if external files are referenced. - if (fragList != null) { - //App app = _module.getApp(); - JhoveBase base = _module.getBase (); - int bufSize = base.getBufferSize (); - FragmentInputStream fragStream = - new FragmentInputStream (fragList, _raf, bufSize); - DataInputStream dfstrm = new DataInputStream (fragStream); - int ncs = _module.getNCodestreams () + 1; - _module.setNCodestreams (ncs); - Codestream curCodestream = _module.getCodestream (ncs); - long len = - _boxHeader.getLength () == 0 ? - 0 : _boxHeader.getDataLength (); - ContCodestream ccs = - new ContCodestream (_module, dfstrm, len); - // Oh, FOOBAR. This creates another situation in which - // we can't count the bytes being read. Buf if we're - // going to a random access file, that may all be - // rendered moot anyway. - retval = ccs.readCodestream (curCodestream, _repInfo); - } - } - else { - _repInfo.setMessage (new ErrorMessage - (MessageConstants.JPEG2000_HUL_24, - _module.getFilePos ())); - _repInfo.setWellFormed (false); - return false; - } - finalizeBytesRead (); - return retval; + /** + * Reads the box, putting appropriate information in the RepInfo object. setModule, setBoxHeader, + * setRepInfo and setDataInputStream must be called before readBox is called. + * readBox must completely consume the box, so that the next byte to be read by the + * DataInputStream is the FF byte of the next Box. + */ + @Override + public boolean readBox() throws IOException { + boolean retval = true; + initBytesRead(); + hasBoxes = true; + // int state = 0; // state variable for checking progress of boxes + JP2Box box = (JP2Box) next(); + if (box == null) { + return false; // empty box can't be right } - - /** Returns the name of the Box. */ - @Override - protected String getSelfPropName () - { - return "Fragment Table Box"; + // OK, how do I deal with subboxes? Should be able to handle + // them the same way as in the module, except that a different + // set of boxes is acceptable. The dispatcher (in BoxHeader?) + // may need to be given a list of boxes that are acceptable for + // any given context. For the top-level module, the list + // should probably (maybe) be in BoxHeader, but for boxes, the + // list of acceptable subboxes must be provided explicitly. + // Maybe an additional argument in the BoxHeader constructor. + if (box instanceof FragmentListBox) { + FragmentListBox fbox = (FragmentListBox) box; + if (!fbox.readBox()) { + return false; + } + List fragList = fbox.getFragmentList(); + // fragList will be null if external files are referenced. + if (fragList != null) { + // App app = _module.getApp(); + JhoveBase base = _module.getBase(); + int bufSize = base.getBufferSize(); + FragmentInputStream fragStream = new FragmentInputStream(fragList, _raf, bufSize); + DataInputStream dfstrm = new DataInputStream(fragStream); + int ncs = _module.getNCodestreams() + 1; + _module.setNCodestreams(ncs); + Codestream curCodestream = _module.getCodestream(ncs); + long len = _boxHeader.getLength() == 0 ? 0 : _boxHeader.getDataLength(); + ContCodestream ccs = new ContCodestream(_module, dfstrm, len); + // Oh, FOOBAR. This creates another situation in which + // we can't count the bytes being read. Buf if we're + // going to a random access file, that may all be + // rendered moot anyway. + retval = ccs.readCodestream(curCodestream, _repInfo); + } + } else { + _repInfo.setMessage(new ErrorMessage(MessageConstants.JPEG2000_HUL_24, _module.getFilePos())); + _repInfo.setWellFormed(false); + return false; } + finalizeBytesRead(); + return retval; + } + + /** Returns the name of the Box. */ + @Override + protected String getSelfPropName() { + return "Fragment Table Box"; + } } diff --git a/jhove-modules/jpeg2000-hul/src/main/java/edu/harvard/hul/ois/jhove/module/jpeg2000/GTSOBox.java b/jhove-modules/jpeg2000-hul/src/main/java/edu/harvard/hul/ois/jhove/module/jpeg2000/GTSOBox.java index 07fb4d366..0409733f5 100644 --- a/jhove-modules/jpeg2000-hul/src/main/java/edu/harvard/hul/ois/jhove/module/jpeg2000/GTSOBox.java +++ b/jhove-modules/jpeg2000-hul/src/main/java/edu/harvard/hul/ois/jhove/module/jpeg2000/GTSOBox.java @@ -1,63 +1,53 @@ -/********************************************************************** - * Jhove - JSTOR/Harvard Object Validation Environment - * Copyright 2004 by JSTOR and the President and Fellows of Harvard College - **********************************************************************/ - +/** + * ******************************************************************** Jhove - JSTOR/Harvard Object + * Validation Environment Copyright 2004 by JSTOR and the President and Fellows of Harvard College + * ******************************************************************** + */ package edu.harvard.hul.ois.jhove.module.jpeg2000; import edu.harvard.hul.ois.jhove.*; import java.io.*; /** - * Graphics Technology Standard Output Box. - * This box holds an ICC color profile. - * - * See ISO/IEC FCD15444-2: 2000, L.9.15.1 - * - * @author Gary McGath + * Graphics Technology Standard Output Box. This box holds an ICC color profile. * + *

See ISO/IEC FCD15444-2: 2000, L.9.15.1 + * + * @author Gary McGath */ public class GTSOBox extends JP2Box { + /** + * Constructor with superbox. + * + * @param parent parent superbox of this box + */ + public GTSOBox(RandomAccessFile raf, BoxHolder parent) { + super(raf, parent); + } - /** - * Constructor with superbox. - * - * @param parent parent superbox of this box - */ - public GTSOBox(RandomAccessFile raf, BoxHolder parent) { - super(raf, parent); - } - - /** Reads the box, putting appropriate information in - * the RepInfo object. setModule, setBoxHeader, - * setRepInfo and setDataInputStream must be called - * before readBox is called. - * readBox must completely consume the - * box, so that the next byte to be read by the - * DataInputStream is the FF byte of the next Box. - */ - @Override - public boolean readBox() throws IOException { - initBytesRead (); - // Short of pulling out the bytes and somehow - // analyzing them, about all we can do is report - // the presence and length of the profile. - - // There can be only one GTSO box within the file, - // which seems oddly limiting compared to the rest - // of JPEG 2000. But it makes this simple. - long propSize = _boxHeader.getDataLength (); - Property sizeProp = new Property ("ProfileLength", - PropertyType.LONG, - new Long (propSize)); - _module.addProperty (new Property - ("GraphicsTechnologyStandardOutput", - PropertyType.PROPERTY, - sizeProp)); - _module.skipBytes (_dstrm, (int) propSize, _module); - finalizeBytesRead (); - return true; - } + /** + * Reads the box, putting appropriate information in the RepInfo object. setModule, setBoxHeader, + * setRepInfo and setDataInputStream must be called before readBox is called. + * readBox must completely consume the box, so that the next byte to be read by the + * DataInputStream is the FF byte of the next Box. + */ + @Override + public boolean readBox() throws IOException { + initBytesRead(); + // Short of pulling out the bytes and somehow + // analyzing them, about all we can do is report + // the presence and length of the profile. + // There can be only one GTSO box within the file, + // which seems oddly limiting compared to the rest + // of JPEG 2000. But it makes this simple. + long propSize = _boxHeader.getDataLength(); + Property sizeProp = new Property("ProfileLength", PropertyType.LONG, new Long(propSize)); + _module.addProperty( + new Property("GraphicsTechnologyStandardOutput", PropertyType.PROPERTY, sizeProp)); + _module.skipBytes(_dstrm, (int) propSize, _module); + finalizeBytesRead(); + return true; + } } diff --git a/jhove-modules/jpeg2000-hul/src/main/java/edu/harvard/hul/ois/jhove/module/jpeg2000/IPRBox.java b/jhove-modules/jpeg2000-hul/src/main/java/edu/harvard/hul/ois/jhove/module/jpeg2000/IPRBox.java index 92e26f125..28b5fadb5 100644 --- a/jhove-modules/jpeg2000-hul/src/main/java/edu/harvard/hul/ois/jhove/module/jpeg2000/IPRBox.java +++ b/jhove-modules/jpeg2000-hul/src/main/java/edu/harvard/hul/ois/jhove/module/jpeg2000/IPRBox.java @@ -1,66 +1,55 @@ -/********************************************************************** - * Jhove - JSTOR/Harvard Object Validation Environment - * Copyright 2004 by JSTOR and the President and Fellows of Harvard College - **********************************************************************/ - +/** + * ******************************************************************** Jhove - JSTOR/Harvard Object + * Validation Environment Copyright 2004 by JSTOR and the President and Fellows of Harvard College + * ******************************************************************** + */ package edu.harvard.hul.ois.jhove.module.jpeg2000; -import java.io.*; import edu.harvard.hul.ois.jhove.*; +import java.io.*; /** - * Intellectual Property Rights box. - * See I.6 in ISO/IEC 15444-1:2000 - * - * The spec says nothing about the content of the IPR box, - * so the generated Property reports it as a sequence of bytes. + * Intellectual Property Rights box. See I.6 in ISO/IEC 15444-1:2000 * - * @author Gary McGath + *

The spec says nothing about the content of the IPR box, so the generated Property reports it + * as a sequence of bytes. * + * @author Gary McGath */ public class IPRBox extends JP2Box { - - /** - * Constructor with superbox. - * - * @param parent parent superbox of this box - */ - public IPRBox (RandomAccessFile raf, BoxHolder parent) - { - super (raf); - } - - - - /** Reads the box, putting appropriate information in - * the RepInfo object. setModule, setBoxHeader, - * setRepInfo and setDataInputStream must be called - * before readBox is called. - * readBox must completely consume the - * box, so that the next byte to be read by the - * DataInputStream is the FF byte of the next Box. - */ - @Override - public boolean readBox() throws IOException { - initBytesRead (); - int len = (int) _boxHeader.getDataLength (); - byte[] bytes = new byte[len]; - for (int i = 0; i < len; i++) { - bytes[i] = (byte) ModuleBase.readUnsignedByte (_dstrm, _module); - } - _module.addProperty (new Property ("IntellectualPropertyRights", - PropertyType.BYTE, - PropertyArity.ARRAY, - bytes)); - finalizeBytesRead (); - return true; - } - - /** Returns the name of the Box. */ - @Override - protected String getSelfPropName () - { - return "Intellectual Property Rights Box"; + /** + * Constructor with superbox. + * + * @param parent parent superbox of this box + */ + public IPRBox(RandomAccessFile raf, BoxHolder parent) { + super(raf); + } + + /** + * Reads the box, putting appropriate information in the RepInfo object. setModule, setBoxHeader, + * setRepInfo and setDataInputStream must be called before readBox is called. + * readBox must completely consume the box, so that the next byte to be read by the + * DataInputStream is the FF byte of the next Box. + */ + @Override + public boolean readBox() throws IOException { + initBytesRead(); + int len = (int) _boxHeader.getDataLength(); + byte[] bytes = new byte[len]; + for (int i = 0; i < len; i++) { + bytes[i] = (byte) ModuleBase.readUnsignedByte(_dstrm, _module); } + _module.addProperty( + new Property("IntellectualPropertyRights", PropertyType.BYTE, PropertyArity.ARRAY, bytes)); + finalizeBytesRead(); + return true; + } + + /** Returns the name of the Box. */ + @Override + protected String getSelfPropName() { + return "Intellectual Property Rights Box"; + } } diff --git a/jhove-modules/jpeg2000-hul/src/main/java/edu/harvard/hul/ois/jhove/module/jpeg2000/ImageHeaderBox.java b/jhove-modules/jpeg2000-hul/src/main/java/edu/harvard/hul/ois/jhove/module/jpeg2000/ImageHeaderBox.java index 5308df87a..c54fdf5f2 100644 --- a/jhove-modules/jpeg2000-hul/src/main/java/edu/harvard/hul/ois/jhove/module/jpeg2000/ImageHeaderBox.java +++ b/jhove-modules/jpeg2000-hul/src/main/java/edu/harvard/hul/ois/jhove/module/jpeg2000/ImageHeaderBox.java @@ -1,113 +1,101 @@ -/********************************************************************** - * Jhove - JSTOR/Harvard Object Validation Environment - * Copyright 2004 by JSTOR and the President and Fellows of Harvard College - **********************************************************************/ - +/** + * ******************************************************************** Jhove - JSTOR/Harvard Object + * Validation Environment Copyright 2004 by JSTOR and the President and Fellows of Harvard College + * ******************************************************************** + */ package edu.harvard.hul.ois.jhove.module.jpeg2000; -import java.io.*; import edu.harvard.hul.ois.jhove.*; +import java.io.*; /** - * Image Header Box. - * See I.5.3.1 in ISO/IEC 15444-1:2000 + * Image Header Box. See I.5.3.1 in ISO/IEC 15444-1:2000 * * @author Gary McGath - * */ public class ImageHeaderBox extends JP2Box { - /** - * Constructor with superbox. - * - * @param parent parent superbox of this box - */ - public ImageHeaderBox(RandomAccessFile raf, BoxHolder parent) { - super(raf, parent); + /** + * Constructor with superbox. + * + * @param parent parent superbox of this box + */ + public ImageHeaderBox(RandomAccessFile raf, BoxHolder parent) { + super(raf, parent); + } + + /** + * Reads the box, putting appropriate information in the RepInfo object. setModule, setBoxHeader, + * setRepInfo and setDataInputStream must be called before readBox is called. + * readBox must completely consume the box, so that the next byte to be read by the + * DataInputStream is the FF byte of the next Box. + */ + @Override + public boolean readBox() throws IOException { + if (!(_parentBox instanceof JP2HeaderBox)) { + _repInfo.setMessage(new ErrorMessage(MessageConstants.JPEG2000_HUL_26, _module.getFilePos())); + return false; + } + initBytesRead(); + if (_boxHeader.getLength() != 22) { + _repInfo.setMessage(new ErrorMessage(MessageConstants.JPEG2000_HUL_27, _module.getFilePos())); + _repInfo.setWellFormed(false); + return false; } - /** Reads the box, putting appropriate information in - * the RepInfo object. setModule, setBoxHeader, - * setRepInfo and setDataInputStream must be called - * before readBox is called. - * readBox must completely consume the - * box, so that the next byte to be read by the - * DataInputStream is the FF byte of the next Box. - */ - @Override - public boolean readBox() throws IOException { - if (!(_parentBox instanceof JP2HeaderBox)) { - _repInfo.setMessage (new ErrorMessage - (MessageConstants.JPEG2000_HUL_26, - _module.getFilePos ())); - return false; - } - initBytesRead (); - if (_boxHeader.getLength() != 22) { - _repInfo.setMessage (new ErrorMessage - (MessageConstants.JPEG2000_HUL_27, - _module.getFilePos ())); - _repInfo.setWellFormed (false); - return false; - } - - // If this is called from a JP2 Header, we set values - // in _defaultNiso, otherwise we set them in the image's - // Niso metadata. Question: where do we get the codestream? - NisoImageMetadata niso; - if (_parentBox instanceof CodestreamHeaderBox) { - Codestream cs = ((CodestreamHeaderBox) _parentBox).getCodestream (); - niso = cs.getNiso (); - } - else { - niso = _module.getDefaultNiso (); - } - - long height = _module.readUnsignedInt (_dstrm); - niso.setImageLength (height); - long width = _module.readUnsignedInt (_dstrm); - niso.setImageWidth (width); - int nc = _module.readUnsignedShort (_dstrm); - if (nc == 0) { - _repInfo.setMessage (new ErrorMessage - (MessageConstants.JPEG2000_HUL_25, - _module.getFilePos ())); - return false; - } - niso.setSamplesPerPixel(nc); - int bpc = ModuleBase.readUnsignedByte(_dstrm, _module); - if (bpc != 255) { - // If the value is 255, use the BPC box. - int[] bits = new int[nc]; - int bps = (bpc & 0X7F) + 1; - for (int i = 0; i < nc; i++) { - bits[i] = bps; - } - // The high-order bit of bpc is 1 if the samples have - // signed values (!). What do we do with it? - niso.setBitsPerSample(bits); - } - int compression = ModuleBase.readUnsignedByte (_dstrm, _module); - if (compression == 7) { - niso.setCompressionScheme (34712); // JPEG 2000 - } - - int unk = ModuleBase.readUnsignedByte (_dstrm, _module); - _module.addProperty (new Property ("ColorspaceUnknown", - PropertyType.BOOLEAN, Boolean.valueOf(unk != 0))); - // Skip unsigned byte - ModuleBase.readUnsignedByte (_dstrm, _module); - // This just says whether there is an IPR box. - // Do we need to do anything with it? - finalizeBytesRead (); - _module.setImageHeaderSeen (true); - return true; + // If this is called from a JP2 Header, we set values + // in _defaultNiso, otherwise we set them in the image's + // Niso metadata. Question: where do we get the codestream? + NisoImageMetadata niso; + if (_parentBox instanceof CodestreamHeaderBox) { + Codestream cs = ((CodestreamHeaderBox) _parentBox).getCodestream(); + niso = cs.getNiso(); + } else { + niso = _module.getDefaultNiso(); } - /** Returns the name of the Box. */ - @Override - protected String getSelfPropName () - { - return "Image Header Box"; + long height = _module.readUnsignedInt(_dstrm); + niso.setImageLength(height); + long width = _module.readUnsignedInt(_dstrm); + niso.setImageWidth(width); + int nc = _module.readUnsignedShort(_dstrm); + if (nc == 0) { + _repInfo.setMessage(new ErrorMessage(MessageConstants.JPEG2000_HUL_25, _module.getFilePos())); + return false; + } + niso.setSamplesPerPixel(nc); + int bpc = ModuleBase.readUnsignedByte(_dstrm, _module); + if (bpc != 255) { + // If the value is 255, use the BPC box. + int[] bits = new int[nc]; + int bps = (bpc & 0X7F) + 1; + for (int i = 0; i < nc; i++) { + bits[i] = bps; + } + // The high-order bit of bpc is 1 if the samples have + // signed values (!). What do we do with it? + niso.setBitsPerSample(bits); } + int compression = ModuleBase.readUnsignedByte(_dstrm, _module); + if (compression == 7) { + niso.setCompressionScheme(34712); // JPEG 2000 + } + + int unk = ModuleBase.readUnsignedByte(_dstrm, _module); + _module.addProperty( + new Property("ColorspaceUnknown", PropertyType.BOOLEAN, Boolean.valueOf(unk != 0))); + // Skip unsigned byte + ModuleBase.readUnsignedByte(_dstrm, _module); + // This just says whether there is an IPR box. + // Do we need to do anything with it? + finalizeBytesRead(); + _module.setImageHeaderSeen(true); + return true; + } + + /** Returns the name of the Box. */ + @Override + protected String getSelfPropName() { + return "Image Header Box"; + } } diff --git a/jhove-modules/jpeg2000-hul/src/main/java/edu/harvard/hul/ois/jhove/module/jpeg2000/InstructionSetBox.java b/jhove-modules/jpeg2000-hul/src/main/java/edu/harvard/hul/ois/jhove/module/jpeg2000/InstructionSetBox.java index bfec72919..a4092e961 100644 --- a/jhove-modules/jpeg2000-hul/src/main/java/edu/harvard/hul/ois/jhove/module/jpeg2000/InstructionSetBox.java +++ b/jhove-modules/jpeg2000-hul/src/main/java/edu/harvard/hul/ois/jhove/module/jpeg2000/InstructionSetBox.java @@ -1,8 +1,8 @@ -/********************************************************************** - * Jhove - JSTOR/Harvard Object Validation Environment - * Copyright 2004 by JSTOR and the President and Fellows of Harvard College - **********************************************************************/ - +/** + * ******************************************************************** Jhove - JSTOR/Harvard Object + * Validation Environment Copyright 2004 by JSTOR and the President and Fellows of Harvard College + * ******************************************************************** + */ package edu.harvard.hul.ois.jhove.module.jpeg2000; import edu.harvard.hul.ois.jhove.*; @@ -10,137 +10,116 @@ import java.util.*; /** - * Instruction Set Box (JPX). - * See ISO/IEC FCD15444-2: 2000, L.9.10.2 - * + * Instruction Set Box (JPX). See ISO/IEC FCD15444-2: 2000, L.9.10.2 * * @author Gary McGath - * */ public class InstructionSetBox extends JP2Box { + /** + * Constructor with superbox. + * + * @param parent parent superbox of this box + */ + public InstructionSetBox(RandomAccessFile raf, BoxHolder parent) { + super(raf, parent); + } + + /** + * Reads the box, putting appropriate information in the RepInfo object. setModule, setBoxHeader, + * setRepInfo and setDataInputStream must be called before readBox is called. + * readBox must completely consume the box, so that the next byte to be read by the + * DataInputStream is the FF byte of the next Box. + */ + @Override + public boolean readBox() throws IOException { + + // Flags indicating which parameters are in instructions - /** - * Constructor with superbox. - * - * @param parent parent superbox of this box - */ - public InstructionSetBox(RandomAccessFile raf, BoxHolder parent) { - super(raf, parent); + // Can be found only in a Composition Box + if (!(_parentBox instanceof CompositionBox)) { + wrongBoxContext(); + return false; } - /** Reads the box, putting appropriate information in - * the RepInfo object. setModule, setBoxHeader, - * setRepInfo and setDataInputStream must be called - * before readBox is called. - * readBox must completely consume the - * box, so that the next byte to be read by the - * DataInputStream is the FF byte of the next Box. - */ - @Override - public boolean readBox() throws IOException { - - // Flags indicating which parameters are in instructions + initBytesRead(); - // Can be found only in a Composition Box - if (!(_parentBox instanceof CompositionBox)) { - wrongBoxContext (); - return false; - } + // ityp flags indicate which parameters are present + int ityp = _module.readUnsignedShort(_dstrm); + boolean hasXO_YO = ((ityp & 1) != 0); + boolean hasWid_Ht = ((ityp & 2) != 0); + boolean hasAnimation = ((ityp & 8) != 0); + boolean hasCrop = ((ityp & 0X20) != 0); + + // Get the repeat count + _module.readUnsignedShort(_dstrm); + + // Get the tick duration. Ignored (but still takes up + // space) if hasAnimation is false. + _module.readUnsignedInt(_dstrm); - initBytesRead (); - - // ityp flags indicate which parameters are present - int ityp = _module.readUnsignedShort (_dstrm); - boolean hasXO_YO = ((ityp & 1) != 0); - boolean hasWid_Ht = ((ityp & 2) != 0); - boolean hasAnimation = ((ityp & 8) != 0); - boolean hasCrop = ((ityp & 0X20) != 0); - - // Get the repeat count - _module.readUnsignedShort (_dstrm); - - // Get the tick duration. Ignored (but still takes up - // space) if hasAnimation is false. - _module.readUnsignedInt (_dstrm); - - int sizeLeft = (int) _boxHeader.getDataLength () - 8; - // If all significant bits of ityp are 0, there are no instructions - if ((ityp & 0X2B) == 0) { - if (sizeLeft != 0) { - _repInfo.setMessage (new ErrorMessage - (MessageConstants.JPEG2000_HUL_29, - _module.getFilePos ())); - _repInfo.setWellFormed (false); - return false; - } + int sizeLeft = (int) _boxHeader.getDataLength() - 8; + // If all significant bits of ityp are 0, there are no instructions + if ((ityp & 0X2B) == 0) { + if (sizeLeft != 0) { + _repInfo.setMessage( + new ErrorMessage(MessageConstants.JPEG2000_HUL_29, _module.getFilePos())); + _repInfo.setWellFormed(false); + return false; + } + } else { + List instProps = new ArrayList<>(11); + // Loop to read instructions + while (sizeLeft >= 0) { + if (hasXO_YO) { + long xo = _module.readUnsignedInt(_dstrm); + instProps.add(new Property("HorizontalOffset", PropertyType.LONG, xo)); + long yo = _module.readUnsignedInt(_dstrm); + instProps.add(new Property("VerticalOffset", PropertyType.LONG, yo)); + sizeLeft -= 8; } - else { - List instProps = new ArrayList<> (11); - // Loop to read instructions - while (sizeLeft >= 0) { - if (hasXO_YO) { - long xo = _module.readUnsignedInt (_dstrm); - instProps.add (new Property ("HorizontalOffset", - PropertyType.LONG, xo)); - long yo = _module.readUnsignedInt (_dstrm); - instProps.add (new Property ("VerticalOffset", - PropertyType.LONG, yo)); - sizeLeft -= 8; - } - if (hasWid_Ht) { - long width = _module.readUnsignedInt (_dstrm); - instProps.add (new Property ("Width", - PropertyType.LONG, width)); - long height = _module.readUnsignedInt (_dstrm); - instProps.add (new Property ("Height", - PropertyType.LONG, height)); - sizeLeft -= 8; - } - if (hasAnimation) { - long life = _module.readUnsignedInt (_dstrm); - // The high bit of life is the persistence flag - boolean persist = ((life & 0X80000000) != 0); - instProps.add (new Property ("Persist", - PropertyType.BOOLEAN, persist)); - life &= 0X7FFFFFFF; - instProps.add (new Property ("Life", - PropertyType.LONG, life)); + if (hasWid_Ht) { + long width = _module.readUnsignedInt(_dstrm); + instProps.add(new Property("Width", PropertyType.LONG, width)); + long height = _module.readUnsignedInt(_dstrm); + instProps.add(new Property("Height", PropertyType.LONG, height)); + sizeLeft -= 8; + } + if (hasAnimation) { + long life = _module.readUnsignedInt(_dstrm); + // The high bit of life is the persistence flag + boolean persist = ((life & 0X80000000) != 0); + instProps.add(new Property("Persist", PropertyType.BOOLEAN, persist)); + life &= 0X7FFFFFFF; + instProps.add(new Property("Life", PropertyType.LONG, life)); - // Sloppy documentation: I'm assuming that N - // and NEXT-USE are the same thing. - long nextuse = _module.readUnsignedInt (_dstrm); - instProps.add (new Property ("NextUse", - PropertyType.LONG, nextuse)); - sizeLeft -= 8; - } - if (hasCrop) { - long xc = _module.readUnsignedInt (_dstrm); - instProps.add (new Property ("HorizontalCropOffset", - PropertyType.LONG, xc)); - long yc = _module.readUnsignedInt (_dstrm); - instProps.add (new Property ("VerticalCropOffset", - PropertyType.LONG, yc)); - long wc = _module.readUnsignedInt (_dstrm); - instProps.add (new Property ("CroppedWidth", - PropertyType.LONG, wc)); - long hc = _module.readUnsignedInt (_dstrm); - instProps.add (new Property ("CroppedHeight", - PropertyType.LONG, hc)); - sizeLeft -= 16; - } - if (sizeLeft < 0) { - _repInfo.setMessage (new ErrorMessage - (MessageConstants.JPEG2000_HUL_28, - _module.getFilePos ())); - _repInfo.setWellFormed (false); - return false; - } - } + // Sloppy documentation: I'm assuming that N + // and NEXT-USE are the same thing. + long nextuse = _module.readUnsignedInt(_dstrm); + instProps.add(new Property("NextUse", PropertyType.LONG, nextuse)); + sizeLeft -= 8; + } + if (hasCrop) { + long xc = _module.readUnsignedInt(_dstrm); + instProps.add(new Property("HorizontalCropOffset", PropertyType.LONG, xc)); + long yc = _module.readUnsignedInt(_dstrm); + instProps.add(new Property("VerticalCropOffset", PropertyType.LONG, yc)); + long wc = _module.readUnsignedInt(_dstrm); + instProps.add(new Property("CroppedWidth", PropertyType.LONG, wc)); + long hc = _module.readUnsignedInt(_dstrm); + instProps.add(new Property("CroppedHeight", PropertyType.LONG, hc)); + sizeLeft -= 16; + } + if (sizeLeft < 0) { + _repInfo.setMessage( + new ErrorMessage(MessageConstants.JPEG2000_HUL_28, _module.getFilePos())); + _repInfo.setWellFormed(false); + return false; } - - finalizeBytesRead (); - return true; + } } + finalizeBytesRead(); + return true; + } } diff --git a/jhove-modules/jpeg2000-hul/src/main/java/edu/harvard/hul/ois/jhove/module/jpeg2000/JP2Box.java b/jhove-modules/jpeg2000-hul/src/main/java/edu/harvard/hul/ois/jhove/module/jpeg2000/JP2Box.java index 91e209047..b1918da34 100644 --- a/jhove-modules/jpeg2000-hul/src/main/java/edu/harvard/hul/ois/jhove/module/jpeg2000/JP2Box.java +++ b/jhove-modules/jpeg2000-hul/src/main/java/edu/harvard/hul/ois/jhove/module/jpeg2000/JP2Box.java @@ -1,15 +1,14 @@ -/********************************************************************** - * Jhove - JSTOR/Harvard Object Validation Environment - * Copyright 2004 by JSTOR and the President and Fellows of Harvard College - **********************************************************************/ - +/** + * ******************************************************************** Jhove - JSTOR/Harvard Object + * Validation Environment Copyright 2004 by JSTOR and the President and Fellows of Harvard College + * ******************************************************************** + */ package edu.harvard.hul.ois.jhove.module.jpeg2000; -import edu.harvard.hul.ois.jhove.module.Jpeg2000Module; import edu.harvard.hul.ois.jhove.*; import edu.harvard.hul.ois.jhove.messages.JhoveMessage; import edu.harvard.hul.ois.jhove.messages.JhoveMessages; - +import edu.harvard.hul.ois.jhove.module.Jpeg2000Module; import java.io.*; import java.util.*; @@ -17,380 +16,337 @@ * Superclass for JPEG 2000 boxes. * * @author Gary McGath - * */ public abstract class JP2Box extends BoxHolder { - protected long startBytesRead; - protected long _bytesRead; - protected List associations; + protected long startBytesRead; + protected long _bytesRead; + protected List associations; - /* Name to be used for self-description property. */ - protected final static String DESCRIPTION_NAME = "Description"; + /* Name to be used for self-description property. */ + protected static final String DESCRIPTION_NAME = "Description"; - /** - * Constructor. Has no arguments, so that - * invoking lots of different subclasses is - * relatively simple. setModule, setBoxHeader, - * setRepInfo, and setDataInputStream should - * be called immediately after the constructor. - */ - public JP2Box(RandomAccessFile raf) { - super(raf); - init(null); - } + /** + * Constructor. Has no arguments, so that invoking lots of different subclasses is relatively + * simple. setModule, setBoxHeader, setRepInfo, and setDataInputStream should be called + * immediately after the constructor. + */ + public JP2Box(RandomAccessFile raf) { + super(raf); + init(null); + } - /** - * Constructor for a box which is found within a - * superbox. - * - * @param parent - * parent superbox of this box - */ - public JP2Box(RandomAccessFile raf, BoxHolder parent) { - super(raf); - init(parent); - } + /** + * Constructor for a box which is found within a superbox. + * + * @param parent parent superbox of this box + */ + public JP2Box(RandomAccessFile raf, BoxHolder parent) { + super(raf); + init(parent); + } - protected void init(BoxHolder parent) { - // _boxHeader = hdr; - if (parent instanceof JP2Box) { - _parentBox = (JP2Box) parent; - } else { - _parentBox = null; - } - _bytesRead = 0; - associations = new LinkedList<>(); - } + protected void init(BoxHolder parent) { + // _boxHeader = hdr; + if (parent instanceof JP2Box) { + _parentBox = (JP2Box) parent; + } else { + _parentBox = null; + } + _bytesRead = 0; + associations = new LinkedList<>(); + } - /* - * This is the key to the reorganization of the code. - * A normal box generates an RAFInputStream based - * on the underlying RandomAccessFile. What do the - * weird subclasses do, and how do I handle box - * substitution? Rather than calling boxMaker statically, - * should I add a method BoxHeader.getBox? That doesn't - * quite cover the case where one header (for a - * Binary Filter Box) turns into multiple boxes. Could - * have an iterator in the BoxHeader class that is capable - * of returning no boxes (in the case of a Free box) or - * multiple boxes, but usually returns one box. - * - * But a subbox iterator, which returns all the top-level - * boxes (at the top level) or all the subboxes of a given - * box, is more natural. For this we need to add a BoxHolder - * class, of which JP2Box is a subclass. BoxHolder will - * have a method to generate an iterator; this method gets - * a box (which it may keep in its pocket), and knows about - * Binary Filter boxes and Cross Reference boxes. - */ + /* + * This is the key to the reorganization of the code. + * A normal box generates an RAFInputStream based + * on the underlying RandomAccessFile. What do the + * weird subclasses do, and how do I handle box + * substitution? Rather than calling boxMaker statically, + * should I add a method BoxHeader.getBox? That doesn't + * quite cover the case where one header (for a + * Binary Filter Box) turns into multiple boxes. Could + * have an iterator in the BoxHeader class that is capable + * of returning no boxes (in the case of a Free box) or + * multiple boxes, but usually returns one box. + * + * But a subbox iterator, which returns all the top-level + * boxes (at the top level) or all the subboxes of a given + * box, is more natural. For this we need to add a BoxHolder + * class, of which JP2Box is a subclass. BoxHolder will + * have a method to generate an iterator; this method gets + * a box (which it may keep in its pocket), and knows about + * Binary Filter boxes and Cross Reference boxes. + */ - /** Sets the module under which the Box is being read. */ - public void setModule(Jpeg2000Module module) { - _module = module; - } + /** Sets the module under which the Box is being read. */ + public void setModule(Jpeg2000Module module) { + _module = module; + } - /** Sets the BoxHeader from which this Box was obtained. */ - public void setBoxHeader(BoxHeader hdr) { - _boxHeader = hdr; - bytesLeft = _boxHeader.getDataLength(); - } + /** Sets the BoxHeader from which this Box was obtained. */ + public void setBoxHeader(BoxHeader hdr) { + _boxHeader = hdr; + bytesLeft = _boxHeader.getDataLength(); + } - /** - * Assigns a RepInfo object, so that subclasses of - * JP2Box can add Properties and Messages. - */ - public void setRepInfo(RepInfo info) { - _repInfo = info; - } + /** Assigns a RepInfo object, so that subclasses of JP2Box can add Properties and Messages. */ + public void setRepInfo(RepInfo info) { + _repInfo = info; + } - /** - * Assigns the DataInputStream from which the box is - * being read. - */ - public void setDataInputStream(DataInputStream dstrm) { - _dstrm = dstrm; - } + /** Assigns the DataInputStream from which the box is being read. */ + public void setDataInputStream(DataInputStream dstrm) { + _dstrm = dstrm; + } - /** - * Assigns the RandomAccessFile from which the box is - * being read. - */ - public void setRandomAccessFile(RandomAccessFile raf) { - _raf = raf; - } + /** Assigns the RandomAccessFile from which the box is being read. */ + public void setRandomAccessFile(RandomAccessFile raf) { + _raf = raf; + } - /** - * Static factory method for generating an object of the - * appropriate subclass of MarkerSegment, based on the - * box type. - * This is for use in top-level reading of boxes, not - * subboxes. Provision is made for calling this with a - * parent box, but the set of boxes dispatched on - * is the set used at top level. - * - * Certain box types have magical characteristics and have - * to be checked by the BoxHolder. These include - * BinaryFilterBox and CrossReferenceBox. - * - * @param hType - * 4-character string indicating the box type - * @param parent - * parent BoxHolder - */ - public static JP2Box boxMaker(String hType, BoxHolder parent) { - JP2Box box = null; - RandomAccessFile raf = null; - if (parent != null) { - raf = parent._raf; - } - if ("jp2h".equals(hType)) { - // The JP2 header superbox - box = new JP2HeaderBox(raf, parent); - } else if ("asoc".equals(hType)) { - // Association box (JPX) - box = new AssociationBox(raf, parent); - } else if ("bpcc".equals(hType)) { - box = new BPCCBox(raf, parent); - } else if ("chck".equals(hType)) { - box = new DigSignatureBox(raf, parent); - } else if ("cdef".equals(hType)) { - box = new ChannelDefBox(raf, parent); - } else if ("cgrp".equals(hType)) { - box = new ColorGroupBox(raf, parent); - } else if ("cmap".equals(hType)) { - box = new ComponentMapBox(raf, parent); - } else if ("colr".equals(hType)) { - box = new ColorSpecBox(raf, parent); - } else if ("comp".equals(hType)) { - // Composition box (JPX) - box = new CompositionBox(raf, parent); - } else if ("copt".equals(hType)) { - // Composition options box (JPX) - box = new CompOptionsBox(raf, parent); - } else if ("creg".equals(hType)) { - // codestream registration box (JPX) - box = new CodestreamRegBox(raf, parent); - } else if ("drep".equals(hType)) { - box = new DesiredReproBox(raf, parent); - } else if ("flst".equals(hType)) { - box = new FragmentListBox(raf, parent); - } else if ("ftbl".equals(hType)) { - // Fragment Table box (JPX) - box = new FragmentTableBox(raf, parent); - } else if ("gtso".equals(hType)) { - // Graphics Technology Standard Output Box (JPX) - box = new GTSOBox(raf, parent); - } else if ("inst".equals(hType)) { - // Instruction Set box (JPX) - box = new InstructionSetBox(raf, parent); - } else if ("ihdr".equals(hType)) { - box = new ImageHeaderBox(raf, parent); - } else if ("jp2c".equals(hType)) { - // The Continuous Codestream box. - box = new ContCodestreamBox(raf, parent); - } else if ("jpch".equals(hType)) { - // The Compositing Header box - box = new CodestreamHeaderBox(raf, parent); - } else if ("jplh".equals(hType)) { - // The Compositing Layer Header box - box = new ComposLayerHdrBox(raf, parent); - } else if ("jp2i".equals(hType)) { - // The Intellectual Property Rights box - box = new IPRBox(raf, parent); - } else if ("lbl ".equals(hType)) { - box = new LabelBox(raf, parent); - } else if ("nlst".equals(hType)) { - // Number list box (JPX) - box = new NumberListBox(raf, parent); - } else if ("opct".equals(hType)) { - box = new OpacityBox(raf, parent); - } else if ("pclr".equals(hType)) { - box = new PaletteBox(raf, parent); - } else if ("res ".equals(hType)) { - box = new ResolutionBox(raf, parent); - } else if ("roid".equals(hType)) { - box = new ROIBox(raf, parent); - } else if ("resc".equals(hType)) { - // Capture Resolution Box (JPX) - box = new CaptureResolutionBox(raf, parent); - } else if ("resd".equals(hType)) { - // Default Display Resolution Box (JPX) - box = new DDResolutionBox(raf, parent); - } else if ("rreq".equals(hType)) { - // Reader Requirements box (JPX) - box = new ReaderRequirementsBox(raf, parent); - } else if ("uinf".equals(hType)) { - box = new UUIDInfoBox(raf, parent); - } else if ("ulst".equals(hType)) { - box = new UUIDListBox(raf, parent); - } else if ("url ".equals(hType)) { - box = new DataEntryURLBox(raf, parent); - } else if ("uuid".equals(hType)) { - box = new UUIDBox(raf, parent); - } else if ("xml ".equals(hType)) { - box = new XMLBox(raf, parent); - } else { - // Not recognized; skip over it. - // The "free" box, which simply indicates - // unused space, goes through here. - // So does the Media Data ("mdat") box, - // whose content is defined only by references - // into it from a Fragment Table. - box = new DefaultBox(raf); - } - return box; - } + /** + * Static factory method for generating an object of the appropriate subclass of MarkerSegment, + * based on the box type. This is for use in top-level reading of boxes, not subboxes. Provision + * is made for calling this with a parent box, but the set of boxes dispatched on is the set used + * at top level. + * + *

Certain box types have magical characteristics and have to be checked by the BoxHolder. + * These include BinaryFilterBox and CrossReferenceBox. + * + * @param hType 4-character string indicating the box type + * @param parent parent BoxHolder + */ + public static JP2Box boxMaker(String hType, BoxHolder parent) { + JP2Box box = null; + RandomAccessFile raf = null; + if (parent != null) { + raf = parent._raf; + } + if ("jp2h".equals(hType)) { + // The JP2 header superbox + box = new JP2HeaderBox(raf, parent); + } else if ("asoc".equals(hType)) { + // Association box (JPX) + box = new AssociationBox(raf, parent); + } else if ("bpcc".equals(hType)) { + box = new BPCCBox(raf, parent); + } else if ("chck".equals(hType)) { + box = new DigSignatureBox(raf, parent); + } else if ("cdef".equals(hType)) { + box = new ChannelDefBox(raf, parent); + } else if ("cgrp".equals(hType)) { + box = new ColorGroupBox(raf, parent); + } else if ("cmap".equals(hType)) { + box = new ComponentMapBox(raf, parent); + } else if ("colr".equals(hType)) { + box = new ColorSpecBox(raf, parent); + } else if ("comp".equals(hType)) { + // Composition box (JPX) + box = new CompositionBox(raf, parent); + } else if ("copt".equals(hType)) { + // Composition options box (JPX) + box = new CompOptionsBox(raf, parent); + } else if ("creg".equals(hType)) { + // codestream registration box (JPX) + box = new CodestreamRegBox(raf, parent); + } else if ("drep".equals(hType)) { + box = new DesiredReproBox(raf, parent); + } else if ("flst".equals(hType)) { + box = new FragmentListBox(raf, parent); + } else if ("ftbl".equals(hType)) { + // Fragment Table box (JPX) + box = new FragmentTableBox(raf, parent); + } else if ("gtso".equals(hType)) { + // Graphics Technology Standard Output Box (JPX) + box = new GTSOBox(raf, parent); + } else if ("inst".equals(hType)) { + // Instruction Set box (JPX) + box = new InstructionSetBox(raf, parent); + } else if ("ihdr".equals(hType)) { + box = new ImageHeaderBox(raf, parent); + } else if ("jp2c".equals(hType)) { + // The Continuous Codestream box. + box = new ContCodestreamBox(raf, parent); + } else if ("jpch".equals(hType)) { + // The Compositing Header box + box = new CodestreamHeaderBox(raf, parent); + } else if ("jplh".equals(hType)) { + // The Compositing Layer Header box + box = new ComposLayerHdrBox(raf, parent); + } else if ("jp2i".equals(hType)) { + // The Intellectual Property Rights box + box = new IPRBox(raf, parent); + } else if ("lbl ".equals(hType)) { + box = new LabelBox(raf, parent); + } else if ("nlst".equals(hType)) { + // Number list box (JPX) + box = new NumberListBox(raf, parent); + } else if ("opct".equals(hType)) { + box = new OpacityBox(raf, parent); + } else if ("pclr".equals(hType)) { + box = new PaletteBox(raf, parent); + } else if ("res ".equals(hType)) { + box = new ResolutionBox(raf, parent); + } else if ("roid".equals(hType)) { + box = new ROIBox(raf, parent); + } else if ("resc".equals(hType)) { + // Capture Resolution Box (JPX) + box = new CaptureResolutionBox(raf, parent); + } else if ("resd".equals(hType)) { + // Default Display Resolution Box (JPX) + box = new DDResolutionBox(raf, parent); + } else if ("rreq".equals(hType)) { + // Reader Requirements box (JPX) + box = new ReaderRequirementsBox(raf, parent); + } else if ("uinf".equals(hType)) { + box = new UUIDInfoBox(raf, parent); + } else if ("ulst".equals(hType)) { + box = new UUIDListBox(raf, parent); + } else if ("url ".equals(hType)) { + box = new DataEntryURLBox(raf, parent); + } else if ("uuid".equals(hType)) { + box = new UUIDBox(raf, parent); + } else if ("xml ".equals(hType)) { + box = new XMLBox(raf, parent); + } else { + // Not recognized; skip over it. + // The "free" box, which simply indicates + // unused space, goes through here. + // So does the Media Data ("mdat") box, + // whose content is defined only by references + // into it from a Fragment Table. + box = new DefaultBox(raf); + } + return box; + } - /* - * Bracketing code for calculating bytes read. - * Every subclass's readBox() method should start - * by calling initBytesRead and finish by calling - * finalizeBytesRead. - */ - protected void initBytesRead() { - startBytesRead = _module.getFilePos(); - } + /* + * Bracketing code for calculating bytes read. + * Every subclass's readBox() method should start + * by calling initBytesRead and finish by calling + * finalizeBytesRead. + */ + protected void initBytesRead() { + startBytesRead = _module.getFilePos(); + } - protected void finalizeBytesRead() { - _bytesRead = _module.getFilePos() - startBytesRead; - } + protected void finalizeBytesRead() { + _bytesRead = _module.getFilePos() - startBytesRead; + } - /** - * Reads the box, putting appropriate information in - * the RepInfo object. setModule, setBoxHeader, - * setRepInfo and setDataInputStream must be called - * before readBox is called. - * Thus, the header of the box must already have been read. - * readBox must completely consume the - * box, so that the next byte to be read by the - * DataInputStream is the FF byte of the next Box. - * The number of bytes read must be placed in _bytesRead. - */ - public abstract boolean readBox() throws IOException; + /** + * Reads the box, putting appropriate information in the RepInfo object. setModule, setBoxHeader, + * setRepInfo and setDataInputStream must be called before readBox is called. Thus, + * the header of the box must already have been read. readBox must completely consume + * the box, so that the next byte to be read by the DataInputStream is the FF byte of + * the next Box. The number of bytes read must be placed in _bytesRead. + */ + public abstract boolean readBox() throws IOException; - public int getBytesRead() { - return (int) _bytesRead; - } + public int getBytesRead() { + return (int) _bytesRead; + } - /** - * Skips over the box. Can be called when the box is - * legal but meaningless in the current context. - */ - public void skipBox() throws IOException { - initBytesRead(); - if (_boxHeader.getLength() != 0) { - _module.skipBytes(_dstrm, (int) _boxHeader.getDataLength(), - _module); - } - finalizeBytesRead(); - } + /** + * Skips over the box. Can be called when the box is legal but meaningless in the current context. + */ + public void skipBox() throws IOException { + initBytesRead(); + if (_boxHeader.getLength() != 0) { + _module.skipBytes(_dstrm, (int) _boxHeader.getDataLength(), _module); + } + finalizeBytesRead(); + } - /* - * Adds an Association property. Most superboxes can - * contain Association boxes; these report themselves - * as Association properties. - */ - protected void addAssociation(Property p) { - associations.add(p); - } + /* + * Adds an Association property. Most superboxes can + * contain Association boxes; these report themselves + * as Association properties. + */ + protected void addAssociation(Property p) { + associations.add(p); + } - /** - * Utility error reporting function for incorrect box length. - * Sets the RepInfo's wellFormed flag to false. - */ - protected void wrongBoxSize() { - JhoveMessage message = JhoveMessages.getMessageInstance( - MessageConstants.JPEG2000_HUL_5.getId(), - String.format(MessageConstants.JPEG2000_HUL_5.getMessage(), - getSelfPropName())); - _repInfo.setMessage(new ErrorMessage(message, _module.getFilePos())); - _repInfo.setWellFormed(false); - } + /** + * Utility error reporting function for incorrect box length. Sets the RepInfo's wellFormed flag + * to false. + */ + protected void wrongBoxSize() { + JhoveMessage message = + JhoveMessages.getMessageInstance( + MessageConstants.JPEG2000_HUL_5.getId(), + String.format(MessageConstants.JPEG2000_HUL_5.getMessage(), getSelfPropName())); + _repInfo.setMessage(new ErrorMessage(message, _module.getFilePos())); + _repInfo.setWellFormed(false); + } - /** - * Utility error reporting function for box in a context - * (superbox or lack thereof) which is not permitted. - * Sets the RepInfo's wellFormed flag to false. - */ - protected void wrongBoxContext() { - _repInfo.setMessage(new ErrorMessage(MessageConstants.JPEG2000_HUL_3, - getSelfPropName(), _module.getFilePos())); - _repInfo.setWellFormed(false); - } + /** + * Utility error reporting function for box in a context (superbox or lack thereof) which is not + * permitted. Sets the RepInfo's wellFormed flag to false. + */ + protected void wrongBoxContext() { + _repInfo.setMessage( + new ErrorMessage(MessageConstants.JPEG2000_HUL_3, getSelfPropName(), _module.getFilePos())); + _repInfo.setWellFormed(false); + } - /** - * Utility error reporting function for a box which is - * expected to have subboxes, but doesn't. - */ - protected void emptyBox() { - _repInfo.setMessage(new ErrorMessage(MessageConstants.JPEG2000_HUL_4, - "Box type = " + getSelfPropName(), _module.getFilePos())); - _repInfo.setWellFormed(false); - } + /** Utility error reporting function for a box which is expected to have subboxes, but doesn't. */ + protected void emptyBox() { + _repInfo.setMessage( + new ErrorMessage( + MessageConstants.JPEG2000_HUL_4, + "Box type = " + getSelfPropName(), + _module.getFilePos())); + _repInfo.setWellFormed(false); + } - /** - * Make a Property from the association list. - * Returns null if the list is empty. - */ - protected Property makeAssocProperty() { - if (associations.isEmpty()) { - return null; - } - return new Property("Associations", PropertyType.PROPERTY, - PropertyArity.LIST, associations); - } + /** Make a Property from the association list. Returns null if the list is empty. */ + protected Property makeAssocProperty() { + if (associations.isEmpty()) { + return null; + } + return new Property("Associations", PropertyType.PROPERTY, PropertyArity.LIST, associations); + } - /** - * Returns a Property which describes the Box, for use - * by Association boxes and perhaps others. - * Most subclasses will only have to override - * getSelfPropName and - * getSelfPropDesc. A subclass - * that shouldn't be added to the Association box's - * property can override this to return null. - */ - protected Property selfDescProperty() { - List subprops = new ArrayList(2); - String name = getSelfPropName(); - if (name == null) { - return null; - } - subprops.add(new Property("Name", PropertyType.STRING, name)); - Property p2 = getSelfPropDesc(); - if (p2 != null) { - subprops.add(p2); - } - return new Property("Box", PropertyType.PROPERTY, PropertyArity.LIST, - subprops); - } + /** + * Returns a Property which describes the Box, for use by Association boxes and perhaps others. + * Most subclasses will only have to override getSelfPropName and + * getSelfPropDesc. A subclass that shouldn't be added to the Association box's property + * can override this to return null. + */ + protected Property selfDescProperty() { + List subprops = new ArrayList(2); + String name = getSelfPropName(); + if (name == null) { + return null; + } + subprops.add(new Property("Name", PropertyType.STRING, name)); + Property p2 = getSelfPropDesc(); + if (p2 != null) { + subprops.add(p2); + } + return new Property("Box", PropertyType.PROPERTY, PropertyArity.LIST, subprops); + } - /** - * Returns the name of the Box. All Boxes should - * override this. - */ - @Override - protected String getSelfPropName() { - return null; - } + /** Returns the name of the Box. All Boxes should override this. */ + @Override + protected String getSelfPropName() { + return null; + } - /** - * Returns a Property which describes the box. This is - * used as a subproperty of the Property returned by - * selfDescProperty. Properties that we don't care to - * describe don't have to override this. This class - * should return either null or a property - * with DESCRIPTION_NAME for its name. - */ - protected Property getSelfPropDesc() { - return null; - } + /** + * Returns a Property which describes the box. This is used as a subproperty of the Property + * returned by selfDescProperty. Properties that we don't care to describe don't have to override + * this. This class should return either null or a property with + * DESCRIPTION_NAME for its name. + */ + protected Property getSelfPropDesc() { + return null; + } - /** - * Returns the length of the box, including header, based - * on the information in the header. - */ - protected long getLength() { - return _boxHeader.getLength(); - } + /** Returns the length of the box, including header, based on the information in the header. */ + protected long getLength() { + return _boxHeader.getLength(); + } } diff --git a/jhove-modules/jpeg2000-hul/src/main/java/edu/harvard/hul/ois/jhove/module/jpeg2000/JP2HeaderBox.java b/jhove-modules/jpeg2000-hul/src/main/java/edu/harvard/hul/ois/jhove/module/jpeg2000/JP2HeaderBox.java index 9871e8d23..32a08d130 100644 --- a/jhove-modules/jpeg2000-hul/src/main/java/edu/harvard/hul/ois/jhove/module/jpeg2000/JP2HeaderBox.java +++ b/jhove-modules/jpeg2000-hul/src/main/java/edu/harvard/hul/ois/jhove/module/jpeg2000/JP2HeaderBox.java @@ -1,161 +1,135 @@ -/********************************************************************** - * Jhove - JSTOR/Harvard Object Validation Environment - * Copyright 2004 by JSTOR and the President and Fellows of Harvard College - **********************************************************************/ - +/** + * ******************************************************************** Jhove - JSTOR/Harvard Object + * Validation Environment Copyright 2004 by JSTOR and the President and Fellows of Harvard College + * ******************************************************************** + */ package edu.harvard.hul.ois.jhove.module.jpeg2000; -import java.io.*; import edu.harvard.hul.ois.jhove.*; +import java.io.*; /** - * JP2 Header Box. - * See I.5.3 in ISO/IEC 15444-1:2000 - * and ISO/IEC FCD15444-2: 2000, L.9.2 - * + * JP2 Header Box. See I.5.3 in ISO/IEC 15444-1:2000 and ISO/IEC FCD15444-2: 2000, L.9.2 * * @author Gary McGath - * */ public class JP2HeaderBox extends JP2Box { + /** Constructor with superbox. */ + public JP2HeaderBox(RandomAccessFile raf, BoxHolder parent) { + super(raf, parent); + } - /** - * Constructor with superbox. - */ - public JP2HeaderBox (RandomAccessFile raf, BoxHolder parent) - { - super (raf, parent); + /** + * Reads the box, putting appropriate information in the RepInfo object. setModule, setBoxHeader, + * setRepInfo and setDataInputStream must be called before readBox is called. + * readBox must completely consume the box, so that the next byte to be read by the + * DataInputStream is the FF byte of the next Box. + */ + @Override + public boolean readBox() throws IOException { + initBytesRead(); + hasBoxes = true; + // int sizeLeft = (int) _boxHeader.getDataLength (); + if (_module.isJP2HdrSeen()) { + _repInfo.setMessage(new ErrorMessage(MessageConstants.JPEG2000_HUL_32, _module.getFilePos())); + // Skip the redundant box and set invalid flag, + // but keep going. + _repInfo.setValid(false); + if (_boxHeader.getLength() != 0) { + _module.skipBytes(_dstrm, (int) _boxHeader.getDataLength(), _module); + } } + _module.setJP2HdrSeen(true); - /** Reads the box, putting appropriate information in - * the RepInfo object. setModule, setBoxHeader, - * setRepInfo and setDataInputStream must be called - * before readBox is called. - * readBox must completely consume the - * box, so that the next byte to be read by the - * DataInputStream is the FF byte of the next Box. - */ - @Override - public boolean readBox() throws IOException { - initBytesRead (); - hasBoxes = true; - //int sizeLeft = (int) _boxHeader.getDataLength (); - if (_module.isJP2HdrSeen ()) { - _repInfo.setMessage (new ErrorMessage - (MessageConstants.JPEG2000_HUL_32, - _module.getFilePos ())); - // Skip the redundant box and set invalid flag, - // but keep going. - _repInfo.setValid (false); - if (_boxHeader.getLength () != 0) { - _module.skipBytes (_dstrm, - (int) _boxHeader.getDataLength (), - _module); - } - } - _module.setJP2HdrSeen (true); - - // In JP2 format, this must come before the Contiguous - // Codestream - if (_module.getNCodestreams () > 0) { - _module.setJP2Compliant (false); - } - - // The JP2 header consists of a variety of boxes, - // so we keep reading boxes till we run out of bytes. - //BoxHeader subhdr = new BoxHeader (_module, _dstrm); - int state = 0; // state variable for checking progress of boxes - JP2Box box = null; - boolean hasCMap = false; - boolean hasPalette = false; - while (hasNext ()) { - box = (JP2Box) next (); - - // A JPX, but not a JP2, can have a Label Box - // before the Image header. - if (state == 0 && box instanceof LabelBox) { - state = 1; - _module.setJP2Compliant (false); - //box = new LabelBox (this); - if (!box.readBox ()) { - return false; - } - _module.addProperty (new Property ("JP2HeaderLabel", - PropertyType.STRING, - ((LabelBox) box).getLabel ())); - - // Read the next box - box = (JP2Box) next (); - } - - // First box, except perhaps for the label box, - // is the image header. - else if (state <= 1) { - if (box instanceof ImageHeaderBox) { - state = 2; - if (!box.readBox ()) { - return false; - } - } - else { - _repInfo.setMessage (new ErrorMessage - (MessageConstants.JPEG2000_HUL_33, - _module.getFilePos ())); - _repInfo.setWellFormed (false); - return false; - } - } - else { - // Only certain boxes are meaningful in a JP2 Header. - // However, others should be skipped over, not considered - // errors. - if (box instanceof AssociationBox || - box instanceof BPCCBox || - box instanceof ColorSpecBox || - box instanceof PaletteBox || - box instanceof ComponentMapBox || - box instanceof ChannelDefBox || - box instanceof ResolutionBox || - box instanceof ROIBox) { - if (!box.readBox ()) { - return false; - } - } - else { - box.skipBox (); - } + // In JP2 format, this must come before the Contiguous + // Codestream + if (_module.getNCodestreams() > 0) { + _module.setJP2Compliant(false); + } - } - } - - - // Consistency checks - if (hasCMap && !hasPalette) { - _repInfo.setMessage (new ErrorMessage - (MessageConstants.JPEG2000_HUL_31, - _module.getFilePos ())); - _repInfo.setValid (false); + // The JP2 header consists of a variety of boxes, + // so we keep reading boxes till we run out of bytes. + // BoxHeader subhdr = new BoxHeader (_module, _dstrm); + int state = 0; // state variable for checking progress of boxes + JP2Box box = null; + boolean hasCMap = false; + boolean hasPalette = false; + while (hasNext()) { + box = (JP2Box) next(); + + // A JPX, but not a JP2, can have a Label Box + // before the Image header. + if (state == 0 && box instanceof LabelBox) { + state = 1; + _module.setJP2Compliant(false); + // box = new LabelBox (this); + if (!box.readBox()) { + return false; } - if (!hasCMap && hasPalette) { - _repInfo.setMessage (new ErrorMessage - (MessageConstants.JPEG2000_HUL_30, - _module.getFilePos ())); - _repInfo.setValid (false); + _module.addProperty( + new Property("JP2HeaderLabel", PropertyType.STRING, ((LabelBox) box).getLabel())); + + // Read the next box + box = (JP2Box) next(); + } + + // First box, except perhaps for the label box, + // is the image header. + else if (state <= 1) { + if (box instanceof ImageHeaderBox) { + state = 2; + if (!box.readBox()) { + return false; + } + } else { + _repInfo.setMessage( + new ErrorMessage(MessageConstants.JPEG2000_HUL_33, _module.getFilePos())); + _repInfo.setWellFormed(false); + return false; } - // If there were any Associations, add a property for them. - Property a = makeAssocProperty (); - if (a != null) { - _module.addProperty(a); + } else { + // Only certain boxes are meaningful in a JP2 Header. + // However, others should be skipped over, not considered + // errors. + if (box instanceof AssociationBox + || box instanceof BPCCBox + || box instanceof ColorSpecBox + || box instanceof PaletteBox + || box instanceof ComponentMapBox + || box instanceof ChannelDefBox + || box instanceof ResolutionBox + || box instanceof ROIBox) { + if (!box.readBox()) { + return false; + } + } else { + box.skipBox(); } - finalizeBytesRead (); - return true; + } } - /** Returns the name of the Box. */ - @Override - protected String getSelfPropName () - { - return "JP2 Header Box"; + // Consistency checks + if (hasCMap && !hasPalette) { + _repInfo.setMessage(new ErrorMessage(MessageConstants.JPEG2000_HUL_31, _module.getFilePos())); + _repInfo.setValid(false); + } + if (!hasCMap && hasPalette) { + _repInfo.setMessage(new ErrorMessage(MessageConstants.JPEG2000_HUL_30, _module.getFilePos())); + _repInfo.setValid(false); } + // If there were any Associations, add a property for them. + Property a = makeAssocProperty(); + if (a != null) { + _module.addProperty(a); + } + finalizeBytesRead(); + return true; + } + + /** Returns the name of the Box. */ + @Override + protected String getSelfPropName() { + return "JP2 Header Box"; + } } diff --git a/jhove-modules/jpeg2000-hul/src/main/java/edu/harvard/hul/ois/jhove/module/jpeg2000/JP2Strings.java b/jhove-modules/jpeg2000-hul/src/main/java/edu/harvard/hul/ois/jhove/module/jpeg2000/JP2Strings.java index d8333172c..6cf0badef 100644 --- a/jhove-modules/jpeg2000-hul/src/main/java/edu/harvard/hul/ois/jhove/module/jpeg2000/JP2Strings.java +++ b/jhove-modules/jpeg2000-hul/src/main/java/edu/harvard/hul/ois/jhove/module/jpeg2000/JP2Strings.java @@ -1,146 +1,117 @@ -/********************************************************************** - * Jhove - JSTOR/Harvard Object Validation Environment - * Copyright 2004 by JSTOR and the President and Fellows of Harvard College +/** + * ******************************************************************** Jhove - JSTOR/Harvard Object + * Validation Environment Copyright 2004 by JSTOR and the President and Fellows of Harvard College * - **********************************************************************/ - + *

******************************************************************** + */ package edu.harvard.hul.ois.jhove.module.jpeg2000; /** - * String constants for JPEG 2000 module. - * This module contains no code, and all data are static. - * - * - * @author Gary McGath + * String constants for JPEG 2000 module. This module contains no code, and all data are static. * + * @author Gary McGath */ public final class JP2Strings { - - /** Strings for method values in the color specification box. */ - public final static String methodStr[] = { - "", - "Enumerated Colorspace", - "Restricted ICC Profile", - "Any ICC Method", - "Vendor Color Method" - }; - - - /** Strings for values of enumCS in the color specification box. - * Only values 16-17 are recognized by JP2. - */ - public final static String enumCSStr[] = { - "Bilevel (1 = black)", - "YCbCr (1)", - "", - "YCbCr (2)", - "YCbCr (3)", - "", "", "", "", // 5-8 - "PhotoYCC", - "", - "CMY", // 11 - "CMYK", // 12 - "YCCK", - "CIELab", - "", - - "sRGB", // 16 (JP2) - "Greyscale", // 17 (JP2) - "Bilevel (1 = white)", - "CIEJab", - "e-sRGB", // 20 - "ROMM-RGB", - "sRGB based YCbCr", - "YPbPr (1125/60)", - "YPbPr (1250/50)" // 24 - }; - - /** Strings for the MTYP field of the Component Mapping box. */ - public final static String mtypStr[] = { - "Direct Use", - "Palette Mapping" - }; - - /** Strings for the opacity type in the Opacity Box. */ - public final static String opacityTypeStr[] = { - "Last channel is opacity channel", - "Last channel is premultiplied opacity channel", - "Chroma key transparency" - }; - - /** Strings for the number type value in the Number - * List box. Types must be normalized by shifting - * the high byte right 24 bits before indexing. - */ - public final static String numberListTypeStr[] = { - "Rendered result", - "Codestream number", - "Compositing layer", - "Numbered entity" - }; - /** Strings for types in the Digital Signature Box. */ - public final static String digitalSigTypeStr[] = { - "MD5 checksum", - "SHA-1 checksum", - "DSA signature", - "RSA signature on MD5 digest", - "RSA signature on SHA-1 digest", - "Cryptographic Message Syntax" - }; + /** Strings for method values in the color specification box. */ + public static final String methodStr[] = { + "", "Enumerated Colorspace", "Restricted ICC Profile", "Any ICC Method", "Vendor Color Method" + }; + + /** + * Strings for values of enumCS in the color specification box. Only values 16-17 are recognized + * by JP2. + */ + public static final String enumCSStr[] = { + "Bilevel (1 = black)", + "YCbCr (1)", + "", + "YCbCr (2)", + "YCbCr (3)", + "", + "", + "", + "", // 5-8 + "PhotoYCC", + "", + "CMY", // 11 + "CMYK", // 12 + "YCCK", + "CIELab", + "", + "sRGB", // 16 (JP2) + "Greyscale", // 17 (JP2) + "Bilevel (1 = white)", + "CIEJab", + "e-sRGB", // 20 + "ROMM-RGB", + "sRGB based YCbCr", + "YPbPr (1125/60)", + "YPbPr (1250/50)" // 24 + }; + + /** Strings for the MTYP field of the Component Mapping box. */ + public static final String mtypStr[] = {"Direct Use", "Palette Mapping"}; + + /** Strings for the opacity type in the Opacity Box. */ + public static final String opacityTypeStr[] = { + "Last channel is opacity channel", + "Last channel is premultiplied opacity channel", + "Chroma key transparency" + }; + + /** + * Strings for the number type value in the Number List box. Types must be normalized by shifting + * the high byte right 24 bits before indexing. + */ + public static final String numberListTypeStr[] = { + "Rendered result", "Codestream number", "Compositing layer", "Numbered entity" + }; + + /** Strings for types in the Digital Signature Box. */ + public static final String digitalSigTypeStr[] = { + "MD5 checksum", + "SHA-1 checksum", + "DSA signature", + "RSA signature on MD5 digest", + "RSA signature on SHA-1 digest", + "Cryptographic Message Syntax" + }; + + /** Strings for pointer types in the Digital Signature Box. */ + public static final String digitalSigPtrTypeStr[] = {"Whole file", "Byte range"}; + + /** Strings for the "region of interest present in codestream" field of the ROI box. */ + public static final String inCodestreamStr[] = { + "Codestream does not contain static region of interest", + "Codestream contains static region of interest" + }; + + /** Strings for the region type field of the ROI box. */ + public static final String roiTypeStr[] = {"Rectangular", "Elliptical"}; + + /** Strings for the channel type field of the channel definition box, indexed by ctypIdx. */ + public static final String ctypStr[] = { + "Color image data", "Opacity", "Premultiplied opacity", "Not specified" // 2^16 - 1 + }; + + /** Indexes for ctypStr. */ + public static final int ctypIdx[] = {0, 1, 2, 65535}; + /** + * Strings for the approx field of the color specification box, indexed by approxIdx. A zero value + * will be reported as an Integer property of 0. + */ + public static final String approxStr[] = { + "Accurate representation", + "Approximation with exceptional quality", + "Approximation with reasonable quality", + "Approximation with poor quality" + }; - /** Strings for pointer types in the Digital Signature Box. */ - public final static String digitalSigPtrTypeStr[] = { - "Whole file", - "Byte range" - }; - - /** Strings for the "region of interest present in codestream" field - * of the ROI box. */ - public final static String inCodestreamStr[] = { - "Codestream does not contain static region of interest", - "Codestream contains static region of interest" - }; - - /** Strings for the region type field of the ROI box. */ - public final static String roiTypeStr[] = { - "Rectangular", - "Elliptical" - }; - - /** Strings for the channel type field of the channel definition box, - * indexed by ctypIdx. */ - public final static String ctypStr[] = { - "Color image data", - "Opacity", - "Premultiplied opacity", - "Not specified" // 2^16 - 1 - }; - - /** Indexes for ctypStr. */ - public final static int ctypIdx[] = {0, 1, 2, 65535}; - - /** Strings for the approx field of the color specification box, - * indexed by approxIdx. A zero value will be reported as - * an Integer property of 0. */ - public final static String approxStr[] = { - "Accurate representation", - "Approximation with exceptional quality", - "Approximation with reasonable quality", - "Approximation with poor quality" - }; - - - /** Indexes for approxStr. */ - public final static int approxIdx[] = {1, 2, 3, 4}; - - /** - * Private constructor, to make sure the class isn't inadvertently - * initiated. - */ - private JP2Strings () - { - } + /** Indexes for approxStr. */ + public static final int approxIdx[] = {1, 2, 3, 4}; + /** Private constructor, to make sure the class isn't inadvertently initiated. */ + private JP2Strings() {} } diff --git a/jhove-modules/jpeg2000-hul/src/main/java/edu/harvard/hul/ois/jhove/module/jpeg2000/LabelBox.java b/jhove-modules/jpeg2000-hul/src/main/java/edu/harvard/hul/ois/jhove/module/jpeg2000/LabelBox.java index 4731fd121..96d318ace 100644 --- a/jhove-modules/jpeg2000-hul/src/main/java/edu/harvard/hul/ois/jhove/module/jpeg2000/LabelBox.java +++ b/jhove-modules/jpeg2000-hul/src/main/java/edu/harvard/hul/ois/jhove/module/jpeg2000/LabelBox.java @@ -1,85 +1,70 @@ -/********************************************************************** - * Jhove - JSTOR/Harvard Object Validation Environment - * Copyright 2004 by JSTOR and the President and Fellows of Harvard College +/** + * ******************************************************************** Jhove - JSTOR/Harvard Object + * Validation Environment Copyright 2004 by JSTOR and the President and Fellows of Harvard College * - **********************************************************************/ - + *

******************************************************************** + */ package edu.harvard.hul.ois.jhove.module.jpeg2000; import edu.harvard.hul.ois.jhove.*; import java.io.*; /** - * Label box. - * A Label box does nothing in itself; it simply makes its - * label string available for its superbox. - * - * See ISO/IEC FCD15444-2: 2000, L.9.13 - * - * @author Gary McGath + * Label box. A Label box does nothing in itself; it simply makes its label string available for its + * superbox. * + *

See ISO/IEC FCD15444-2: 2000, L.9.13 + * + * @author Gary McGath */ public class LabelBox extends JP2Box { - /* The label text. */ - private String _label; - + /* The label text. */ + private String _label; - /** - * Constructor with superbox. - * - * @param parent parent superbox of this box - */ - public LabelBox(RandomAccessFile raf, BoxHolder parent) { - super(raf, parent); - } + /** + * Constructor with superbox. + * + * @param parent parent superbox of this box + */ + public LabelBox(RandomAccessFile raf, BoxHolder parent) { + super(raf, parent); + } - /** Reads the box, saving the label text. - * setModule, setBoxHeader, - * setRepInfo and setDataInputStream must be called - * before readBox is called. - * readBox must completely consume the - * box, so that the next byte to be read by the - * DataInputStream is the FF byte of the next Box. - */ - @Override - public boolean readBox() throws IOException { - if (_parentBox == null) { - wrongBoxContext(); - return false; - } - byte[] byteBuf = new byte [(int) _boxHeader.getDataLength()]; - ModuleBase.readByteBuf (_dstrm, byteBuf, _module); - _label = new String (byteBuf, "UTF-8"); - return true; + /** + * Reads the box, saving the label text. setModule, setBoxHeader, setRepInfo and + * setDataInputStream must be called before readBox is called. readBox + * must completely consume the box, so that the next byte to be read by the DataInputStream is the + * FF byte of the next Box. + */ + @Override + public boolean readBox() throws IOException { + if (_parentBox == null) { + wrongBoxContext(); + return false; } + byte[] byteBuf = new byte[(int) _boxHeader.getDataLength()]; + ModuleBase.readByteBuf(_dstrm, byteBuf, _module); + _label = new String(byteBuf, "UTF-8"); + return true; + } + /** Returns the label string. Valid only after readBox() has been called. */ + protected String getLabel() { + return _label; + } - /** Returns the label string. Valid only after - * readBox() has been called. - */ - protected String getLabel () - { - return _label; - } - - /** Returns a Property which describes the Box, for use - * by Association boxes and perhaps others. - */ - @Override - protected Property getSelfPropDesc () - { - return new Property (DESCRIPTION_NAME, - PropertyType.STRING, - _label); - } + /** + * Returns a Property which describes the Box, for use by Association boxes and perhaps others. + */ + @Override + protected Property getSelfPropDesc() { + return new Property(DESCRIPTION_NAME, PropertyType.STRING, _label); + } - - - /** Returns the name of the Box. */ - @Override - protected String getSelfPropName () - { - return "Label Box"; - } + /** Returns the name of the Box. */ + @Override + protected String getSelfPropName() { + return "Label Box"; + } } diff --git a/jhove-modules/jpeg2000-hul/src/main/java/edu/harvard/hul/ois/jhove/module/jpeg2000/MainOrTile.java b/jhove-modules/jpeg2000-hul/src/main/java/edu/harvard/hul/ois/jhove/module/jpeg2000/MainOrTile.java index 53b6a8117..18dd9433b 100644 --- a/jhove-modules/jpeg2000-hul/src/main/java/edu/harvard/hul/ois/jhove/module/jpeg2000/MainOrTile.java +++ b/jhove-modules/jpeg2000-hul/src/main/java/edu/harvard/hul/ois/jhove/module/jpeg2000/MainOrTile.java @@ -1,122 +1,104 @@ -/********************************************************************** - * Jhove - JSTOR/Harvard Object Validation Environment - * Copyright 2004 by JSTOR and the President and Fellows of Harvard College - **********************************************************************/ - +/** + * ******************************************************************** Jhove - JSTOR/Harvard Object + * Validation Environment Copyright 2004 by JSTOR and the President and Fellows of Harvard College + * ******************************************************************** + */ package edu.harvard.hul.ois.jhove.module.jpeg2000; import edu.harvard.hul.ois.jhove.*; import java.util.*; - /** + * Abstract superclass for Codestream and Tile classes. Many marker segments can apply either to the + * codestream as a whole or to specific tiles; this class merges those features in a single place. * - * Abstract superclass for Codestream and Tile classes. - * Many marker segments can apply either to the codestream - * as a whole or to specific tiles; this class merges those - * features in a single place. - * * @author Gary McGath - * */ public abstract class MainOrTile { - /* Default value */ - protected final static int NULL = -1; - - /* Precinct size array */ - protected int[] _precSize; - - /** Array of components. This is created when the SIZ - * marker segment reports the number of components. */ - protected Property[] _components; - - /** Coding style default property */ - protected Property _codProperty; - - /** Quantization default property */ - protected Property _qcdProperty; - - /** Progression order change property */ - protected Property _pocProperty; - - /** Comments -- list of properties */ - protected List _comments; - - /* List of packet lengths */ - protected List _packetLengthList; - - - - - public MainOrTile () - { - _components = null; - _qcdProperty = null; - _codProperty = null; - _comments = new LinkedList<> (); - } - - - - /** Sets the number of components. As a side effect, - * creates the compoments array. This should be called - * from the SIZMarkerSegment class, and in a valid - * file will precede the setting of any components. - */ - public void setNumComponents (int nComp) - { - _components = new Property[nComp]; + /* Default value */ + protected static final int NULL = -1; + + /* Precinct size array */ + protected int[] _precSize; + + /** + * Array of components. This is created when the SIZ marker segment reports the number of + * components. + */ + protected Property[] _components; + + /** Coding style default property */ + protected Property _codProperty; + + /** Quantization default property */ + protected Property _qcdProperty; + + /** Progression order change property */ + protected Property _pocProperty; + + /** Comments -- list of properties */ + protected List _comments; + + /* List of packet lengths */ + protected List _packetLengthList; + + public MainOrTile() { + _components = null; + _qcdProperty = null; + _codProperty = null; + _comments = new LinkedList<>(); + } + + /** + * Sets the number of components. As a side effect, creates the compoments array. This should be + * called from the SIZMarkerSegment class, and in a valid file will precede the setting of any + * components. + */ + public void setNumComponents(int nComp) { + _components = new Property[nComp]; + } + + /** Sets a property indexed by component. */ + public void setCompProperty(int idx, Property prop) { + if (_components != null && _components.length > idx) { + _components[idx] = prop; } + } - /** Sets a property indexed by component. */ - public void setCompProperty (int idx, Property prop) - { - if (_components != null && _components.length > idx) { - _components[idx] = prop; - } + /** Gets the number of components. */ + protected int getNumComponents() { + if (_components == null) { + return 0; } - - /** Gets the number of components. */ - protected int getNumComponents () - { - if (_components == null) { - return 0; - } - return _components.length; - } - - /** Sets the coding style default property. */ - public void setCODProperty (Property prop) - { - _codProperty = prop; - } - - /** Sets the quantization default property. */ - public void setQCDProperty (Property prop) - { - _qcdProperty = prop; - } - - /** Sets the progression order change property. */ - public void setPOCProperty (Property prop) - { - _pocProperty = prop; - } - - /** Adds a property to the comment list */ - public void addComment (Property comment) - { - _comments.add (comment); - } - - - /** Add a packet length to the list of packet lengths. */ - public void addPacketLength (long len) - { - if (_packetLengthList == null) { - _packetLengthList = new LinkedList<> (); - } - _packetLengthList.add (new Long (len)); + return _components.length; + } + + /** Sets the coding style default property. */ + public void setCODProperty(Property prop) { + _codProperty = prop; + } + + /** Sets the quantization default property. */ + public void setQCDProperty(Property prop) { + _qcdProperty = prop; + } + + /** Sets the progression order change property. */ + public void setPOCProperty(Property prop) { + _pocProperty = prop; + } + + /** Adds a property to the comment list */ + public void addComment(Property comment) { + _comments.add(comment); + } + + /** Add a packet length to the list of packet lengths. */ + public void addPacketLength(long len) { + if (_packetLengthList == null) { + _packetLengthList = new LinkedList<>(); } + _packetLengthList.add(new Long(len)); + } } diff --git a/jhove-modules/jpeg2000-hul/src/main/java/edu/harvard/hul/ois/jhove/module/jpeg2000/Marker.java b/jhove-modules/jpeg2000-hul/src/main/java/edu/harvard/hul/ois/jhove/module/jpeg2000/Marker.java index c8bfd5288..ad3ebcce4 100644 --- a/jhove-modules/jpeg2000-hul/src/main/java/edu/harvard/hul/ois/jhove/module/jpeg2000/Marker.java +++ b/jhove-modules/jpeg2000-hul/src/main/java/edu/harvard/hul/ois/jhove/module/jpeg2000/Marker.java @@ -1,40 +1,30 @@ -/********************************************************************** - * Jhove - JSTOR/Harvard Object Validation Environment - * Copyright 2004 by JSTOR and the President and Fellows of Harvard College - **********************************************************************/ - +/** + * ******************************************************************** Jhove - JSTOR/Harvard Object + * Validation Environment Copyright 2004 by JSTOR and the President and Fellows of Harvard College + * ******************************************************************** + */ package edu.harvard.hul.ois.jhove.module.jpeg2000; /** - * This is a subclass of MarkerSegment for Markers. - * Markers are those elements of a codestream which have - * no parameters. It can be subclassed for specific Markers, - * or used directly to provide default behavior. - * - * @author Gary McGath + * This is a subclass of MarkerSegment for Markers. Markers are those elements of a codestream which + * have no parameters. It can be subclassed for specific Markers, or used directly to provide + * default behavior. * + * @author Gary McGath */ public class Marker extends MarkerSegment { - public Marker () - { - } + public Marker() {} - /** Overrides the superclass to return 0 without consuming - * any bytes from the DataInputStream. - */ - @Override - protected int readMarkLen () - { - return 0; - } - - - /** Default processing. Does nothing, and always returns true. */ - @Override - protected boolean process (int bytesToEat) - { - return true; - } + /** Overrides the superclass to return 0 without consuming any bytes from the DataInputStream. */ + @Override + protected int readMarkLen() { + return 0; + } + /** Default processing. Does nothing, and always returns true. */ + @Override + protected boolean process(int bytesToEat) { + return true; + } } diff --git a/jhove-modules/jpeg2000-hul/src/main/java/edu/harvard/hul/ois/jhove/module/jpeg2000/MarkerSegment.java b/jhove-modules/jpeg2000-hul/src/main/java/edu/harvard/hul/ois/jhove/module/jpeg2000/MarkerSegment.java index 5657c1bcf..647a27c0a 100644 --- a/jhove-modules/jpeg2000-hul/src/main/java/edu/harvard/hul/ois/jhove/module/jpeg2000/MarkerSegment.java +++ b/jhove-modules/jpeg2000-hul/src/main/java/edu/harvard/hul/ois/jhove/module/jpeg2000/MarkerSegment.java @@ -1,228 +1,203 @@ -/********************************************************************** - * Jhove - JSTOR/Harvard Object Validation Environment - * Copyright 2004 by JSTOR and the President and Fellows of Harvard College - **********************************************************************/ - +/** + * ******************************************************************** Jhove - JSTOR/Harvard Object + * Validation Environment Copyright 2004 by JSTOR and the President and Fellows of Harvard College + * ******************************************************************** + */ package edu.harvard.hul.ois.jhove.module.jpeg2000; -import java.io.*; import edu.harvard.hul.ois.jhove.*; import edu.harvard.hul.ois.jhove.module.Jpeg2000Module; - +import java.io.*; /** - * Abstract superclass for marker segments. + * Abstract superclass for marker segments. * * @author Gary McGath - * */ public abstract class MarkerSegment { - protected final static int - SOC = 0X4F, // start of codestream - COD = 0X52, // coding style default - COC = 0X53, // coding style component - TLM = 0X55, // tile-part lengths - PLM = 0X57, // packet length, main header - PLT = 0X58, // packet length, tile-part header - QCD = 0X5C, // quantization default - QCC = 0X5D, // quantization component - RGN = 0X5E, // region of interest - POC = 0X5F, // progression order change - PPM = 0X60, // Packed packet headers, main header - PPT = 0X61, // packed packet headers, tile-part header - CRG = 0X63, // component registration - COM = 0X64, // comment - SOT = 0X90, // start of tile part - SOP = 0X91, // start of packet - EPH = 0X92, // end of packet header - SOD = 0X93, // start of data - EOC = 0XD9, // end of codestream - SIZ = 0X51; // image and tile size - - protected ContCodestream _ccs; - protected Codestream _cs; - protected Jpeg2000Module _module; - protected DataInputStream _dstream; - protected RepInfo _repInfo; - - /** - * Constructor. - * After an instance of a MarkerSegment is created, - * the setter methods setContCodestream, - * setCodestream, setModule, - * and setDataInputStream must all be called as - * part of the setup before process is called. - */ - public MarkerSegment () - { - } - - /** Sets the Continuous Codestream from which this marker was - * obtained. - */ - public void setContCodestream (ContCodestream ccs) - { - _ccs = ccs; - } - - /** Sets the Codestream object being built. */ - public void setCodestream (Codestream cs) - { - _cs = cs; - } - - /** Sets the Module under which all this is happening. */ - public void setModule (Jpeg2000Module module) - { - _module = module; - } - - /** Sets the DataInputStream over which this marker is being - * read. - */ - public void setDataInputStream (DataInputStream dstream) - { - _dstream = dstream; - } - - /** Sets the RepInfo into which messages may be placed. */ - public void setRepInfo (RepInfo repInfo) - { - _repInfo = repInfo; - } - - /** Returns true if this segment is a Marker. - * Will return false unless overridden. */ - public boolean isMarker () - { - return false; - } - - /** Static factory method for generating an object of the - * appropriate subclass of MarkerSegment, based on the - * marker code. - * - * @param markerCode The 8-bit marker code (ignoring the FF). */ - protected static MarkerSegment markerSegmentMaker (int markerCode) - { - switch (markerCode) { - case SOT: - return new SOTMarkerSegment (); - - case COC: - return new COCMarkerSegment (); - - case COD: - return new CODMarkerSegment (); - - case COM: - return new CommentMarkerSegment (); - - case CRG: - return new CRGMarkerSegment (); - - case PLM: - return new PLMMarkerSegment (); - - case PLT: - return new PLTMarkerSegment (); - - case POC: - return new POCMarkerSegment (); - - case PPM: - return new PPMMarkerSegment (); - - case PPT: - return new PPTMarkerSegment (); - - case QCC: - return new QCCMarkerSegment (); - - case QCD: - return new QCDMarkerSegment (); - - case RGN: - return new RGNMarkerSegment (); - - case SIZ: - return new SIZMarkerSegment (); - - case TLM: - return new TLMMarkerSegment (); - - case SOC: // start of codestream - case EPH: // end of packet header - case SOD: // start of data - case EOC: - return new Marker (); - - // SOP won't be implemented, at least for the time - // being, since it occurs within the bitstream data - // of a codestream, which we don't analyze. - case SOP: - default: - return new DefaultMarkerSegment (); - } - } - - /** Reads and returns the length field of the marker segment. - * The setter methods setModule - * and setDataInputStream must be called as - * part of the setup before readMarkLen is called. - */ - protected int readMarkLen () throws IOException - { - return _module.readUnsignedShort (_dstream); - } - - - /** Determines size of fields indexed by number of components. - * Some marker segments have fields which are 1 byte long if - * the number of components is 1-255, and 2 bytes long if - * the number of components is 256-65535. - * - * @return 0 if number of components not yet set, otherwise 1 or 2 - */ - protected int nCompBytes () - { - int nComp = _cs.getNumComponents (); - if (nComp == 0) { - return 0; // indicates an error condition - } - // size of Ccoc field depends on number of components - return (nComp < 257 ? 1 : 2); + protected static final int SOC = 0X4F, // start of codestream + COD = 0X52, // coding style default + COC = 0X53, // coding style component + TLM = 0X55, // tile-part lengths + PLM = 0X57, // packet length, main header + PLT = 0X58, // packet length, tile-part header + QCD = 0X5C, // quantization default + QCC = 0X5D, // quantization component + RGN = 0X5E, // region of interest + POC = 0X5F, // progression order change + PPM = 0X60, // Packed packet headers, main header + PPT = 0X61, // packed packet headers, tile-part header + CRG = 0X63, // component registration + COM = 0X64, // comment + SOT = 0X90, // start of tile part + SOP = 0X91, // start of packet + EPH = 0X92, // end of packet header + SOD = 0X93, // start of data + EOC = 0XD9, // end of codestream + SIZ = 0X51; // image and tile size + + protected ContCodestream _ccs; + protected Codestream _cs; + protected Jpeg2000Module _module; + protected DataInputStream _dstream; + protected RepInfo _repInfo; + + /** + * Constructor. After an instance of a MarkerSegment is created, the setter methods + * setContCodestream, setCodestream, setModule, and + * setDataInputStream must all be called as part of the setup before process + * is called. + */ + public MarkerSegment() {} + + /** Sets the Continuous Codestream from which this marker was obtained. */ + public void setContCodestream(ContCodestream ccs) { + _ccs = ccs; + } + + /** Sets the Codestream object being built. */ + public void setCodestream(Codestream cs) { + _cs = cs; + } + + /** Sets the Module under which all this is happening. */ + public void setModule(Jpeg2000Module module) { + _module = module; + } + + /** Sets the DataInputStream over which this marker is being read. */ + public void setDataInputStream(DataInputStream dstream) { + _dstream = dstream; + } + + /** Sets the RepInfo into which messages may be placed. */ + public void setRepInfo(RepInfo repInfo) { + _repInfo = repInfo; + } + + /** + * Returns true if this segment is a Marker. Will return false unless + * overridden. + */ + public boolean isMarker() { + return false; + } + + /** + * Static factory method for generating an object of the appropriate subclass of MarkerSegment, + * based on the marker code. + * + * @param markerCode The 8-bit marker code (ignoring the FF). + */ + protected static MarkerSegment markerSegmentMaker(int markerCode) { + switch (markerCode) { + case SOT: + return new SOTMarkerSegment(); + + case COC: + return new COCMarkerSegment(); + + case COD: + return new CODMarkerSegment(); + + case COM: + return new CommentMarkerSegment(); + + case CRG: + return new CRGMarkerSegment(); + + case PLM: + return new PLMMarkerSegment(); + + case PLT: + return new PLTMarkerSegment(); + + case POC: + return new POCMarkerSegment(); + + case PPM: + return new PPMMarkerSegment(); + + case PPT: + return new PPTMarkerSegment(); + + case QCC: + return new QCCMarkerSegment(); + + case QCD: + return new QCDMarkerSegment(); + + case RGN: + return new RGNMarkerSegment(); + + case SIZ: + return new SIZMarkerSegment(); + + case TLM: + return new TLMMarkerSegment(); + + case SOC: // start of codestream + case EPH: // end of packet header + case SOD: // start of data + case EOC: + return new Marker(); + + // SOP won't be implemented, at least for the time + // being, since it occurs within the bitstream data + // of a codestream, which we don't analyze. + case SOP: + default: + return new DefaultMarkerSegment(); } + } + /** + * Reads and returns the length field of the marker segment. The setter methods setModule + * and setDataInputStream must be called as part of the setup before + * readMarkLen is called. + */ + protected int readMarkLen() throws IOException { + return _module.readUnsignedShort(_dstream); + } + + /** + * Determines size of fields indexed by number of components. Some marker segments have fields + * which are 1 byte long if the number of components is 1-255, and 2 bytes long if the number of + * components is 256-65535. + * + * @return 0 if number of components not yet set, otherwise 1 or 2 + */ + protected int nCompBytes() { + int nComp = _cs.getNumComponents(); + if (nComp == 0) { + return 0; // indicates an error condition + } + // size of Ccoc field depends on number of components + return (nComp < 257 ? 1 : 2); + } - /** Returns the MainOrTile object which is currently - * applicable in the Contiguous Codestream. If the - * Contiguous Codestream has a current Tile, that is - * returned; otherwise the Codestream object established - * by setCodestream is returned. - */ - protected MainOrTile getMainOrTile () - { - Tile tile = _ccs.getCurTile (); - if (tile != null) { - return tile; - } - return _cs; + /** + * Returns the MainOrTile object which is currently applicable in the Contiguous Codestream. If + * the Contiguous Codestream has a current Tile, that is returned; otherwise the Codestream object + * established by setCodestream is returned. + */ + protected MainOrTile getMainOrTile() { + Tile tile = _ccs.getCurTile(); + if (tile != null) { + return tile; } + return _cs; + } - /** Process the marker or marker segment. The DataInputStream - * will be at the point of having read the marker code. The - * process method must consume exactly the number - * of bytes remaining in the marker segment; for a marker, - * this number will always be 0. - * - * @param bytesToEat The number of bytes that must be consumed. - * For a Marker, this number will always be 0. - * If it is 0 for a MarkerSegment, the - * number of bytes to consume is unknown. - * @return true if segment is well-formed, - * false otherwise. - */ - protected abstract boolean process (int bytesToEat) throws IOException; + /** + * Process the marker or marker segment. The DataInputStream will be at the point of having read + * the marker code. The process method must consume exactly the number of bytes + * remaining in the marker segment; for a marker, this number will always be 0. + * + * @param bytesToEat The number of bytes that must be consumed. For a Marker, this number will + * always be 0. If it is 0 for a MarkerSegment, the number of bytes to consume is unknown. + * @return true if segment is well-formed, false otherwise. + */ + protected abstract boolean process(int bytesToEat) throws IOException; } diff --git a/jhove-modules/jpeg2000-hul/src/main/java/edu/harvard/hul/ois/jhove/module/jpeg2000/MessageConstants.java b/jhove-modules/jpeg2000-hul/src/main/java/edu/harvard/hul/ois/jhove/module/jpeg2000/MessageConstants.java index 368c5c32e..3533ebf99 100644 --- a/jhove-modules/jpeg2000-hul/src/main/java/edu/harvard/hul/ois/jhove/module/jpeg2000/MessageConstants.java +++ b/jhove-modules/jpeg2000-hul/src/main/java/edu/harvard/hul/ois/jhove/module/jpeg2000/MessageConstants.java @@ -5,109 +5,106 @@ import edu.harvard.hul.ois.jhove.messages.JhoveMessages; /** - * Enum used to externalise the JPEG 2000 modules message Strings. Using an - * enum INSTANCE as a "trick" to ensure a single instance of the class. String - * constants should be prefixed according to their use in the module: + * Enum used to externalise the JPEG 2000 modules message Strings. Using an enum INSTANCE as a + * "trick" to ensure a single instance of the class. String constants should be prefixed according + * to their use in the module: + * *

    - *
  • WRN_ for warning strings, often logger messages.
  • - *
  • INF_ for informational messages.
  • - *
  • ERR_ for error messages that indicate a file is invalid or not well - * formed.
  • + *
  • WRN_ for warning strings, often logger messages. + *
  • INF_ for informational messages. + *
  • ERR_ for error messages that indicate a file is invalid or not well formed. *
- * When adding new messages try to adopt the following order for the naming - * elements: + * + * When adding new messages try to adopt the following order for the naming elements: + * *
    - *
  1. PREFIX: one of the three prefixes from the list above.
  2. - *
  3. ENTITY_NAME: the name of the JPEG 2000 entity causing the prohlem, e.g. - * BOX, FILE_TYPE_BOX, COC_MARKER, etc.
  4. - *
  5. PROBLEM_LOCATION: the location or component inside the entity, e.g. - * POSITION, SIZE, TYPE, NUMBER_OF_ENTRIES, etc.
  6. - *
  7. PROBLEM_TYPE: a short indicator of the problem type, e.g. MISSING, - * INVALID, EMPTY, OVERRUN, UNDERRUN, etc.
  8. + *
  9. PREFIX: one of the three prefixes from the list above. + *
  10. ENTITY_NAME: the name of the JPEG 2000 entity causing the prohlem, e.g. BOX, FILE_TYPE_BOX, + * COC_MARKER, etc. + *
  11. PROBLEM_LOCATION: the location or component inside the entity, e.g. POSITION, SIZE, TYPE, + * NUMBER_OF_ENTRIES, etc. + *
  12. PROBLEM_TYPE: a short indicator of the problem type, e.g. MISSING, INVALID, EMPTY, OVERRUN, + * UNDERRUN, etc. *
- * The elements should be separated by underscores. The messages currently - * don't follow a consistent vocabulary, that is terms such as invalid, - * illegal, or malformed are used without definition. - * - * @author Martin Hoppenheit - * marhop AT github + * + * The elements should be separated by underscores. The messages currently don't follow a consistent + * vocabulary, that is terms such as invalid, illegal, or malformed are used without definition. + * + * @author Martin Hoppenheit marhop AT github * @version 0.1 Created 27 Apr 2017:09:39:41 */ - public enum MessageConstants { - INSTANCE; + INSTANCE; + + public static final JhoveMessageFactory messageFactory = + JhoveMessages.getInstance("edu.harvard.hul.ois.jhove.module.jpeg2000.ErrorMessages"); + /** Warning messages */ + // None yet. + + /** Information messages */ + public static final String INF_BINARY_FILTER_BOX_NOT_GZIP = + "Binary Filter Box of type other than Gzip, contents not processed"; - public static final JhoveMessageFactory messageFactory = JhoveMessages - .getInstance( - "edu.harvard.hul.ois.jhove.module.jpeg2000.ErrorMessages"); - /** - * Warning messages - */ - // None yet. + public static final String INF_FRAGMENT_LIST_BOX_EXT_FILE_REFERENCE = + "Document references an external file"; - /** - * Information messages - */ - public static final String INF_BINARY_FILTER_BOX_NOT_GZIP = "Binary Filter Box of type other than Gzip, contents not processed"; - public static final String INF_FRAGMENT_LIST_BOX_EXT_FILE_REFERENCE = "Document references an external file"; + /** Error messages */ + // public static final JhoveMessage JPEG2000_HUL_1 = messageFactory.getMessage("JPEG2000-HUL-1"); + // public static final JhoveMessage JPEG2000_HUL_2 = messageFactory.getMessage("JPEG2000-HUL-2"); + public static final JhoveMessage JPEG2000_HUL_3 = messageFactory.getMessage("JPEG2000-HUL-3"); - /** - * Error messages - */ -// public static final JhoveMessage JPEG2000_HUL_1 = messageFactory.getMessage("JPEG2000-HUL-1"); -// public static final JhoveMessage JPEG2000_HUL_2 = messageFactory.getMessage("JPEG2000-HUL-2"); - public static final JhoveMessage JPEG2000_HUL_3 = messageFactory.getMessage("JPEG2000-HUL-3"); - public static final JhoveMessage JPEG2000_HUL_4 = messageFactory.getMessage("JPEG2000-HUL-4"); - public static final JhoveMessage JPEG2000_HUL_5 = messageFactory.getMessage("JPEG2000-HUL-5"); - public static final JhoveMessage JPEG2000_HUL_6 = messageFactory.getMessage("JPEG2000-HUL-6"); - public static final JhoveMessage JPEG2000_HUL_7 = messageFactory.getMessage("JPEG2000-HUL-7"); - public static final JhoveMessage JPEG2000_HUL_8 = messageFactory.getMessage("JPEG2000-HUL-8"); - public static final JhoveMessage JPEG2000_HUL_9 = messageFactory.getMessage("JPEG2000-HUL-9"); - public static final JhoveMessage JPEG2000_HUL_10 = messageFactory.getMessage("JPEG2000-HUL-10"); - public static final JhoveMessage JPEG2000_HUL_11 = messageFactory.getMessage("JPEG2000-HUL-11"); - public static final JhoveMessage JPEG2000_HUL_12 = messageFactory.getMessage("JPEG2000-HUL-12"); - public static final JhoveMessage JPEG2000_HUL_13 = messageFactory.getMessage("JPEG2000-HUL-13"); - public static final JhoveMessage JPEG2000_HUL_14 = messageFactory.getMessage("JPEG2000-HUL-14"); - public static final JhoveMessage JPEG2000_HUL_15 = messageFactory.getMessage("JPEG2000-HUL-15"); - public static final JhoveMessage JPEG2000_HUL_16 = messageFactory.getMessage("JPEG2000-HUL-16"); - public static final JhoveMessage JPEG2000_HUL_17 = messageFactory.getMessage("JPEG2000-HUL-17"); - public static final JhoveMessage JPEG2000_HUL_18 = messageFactory.getMessage("JPEG2000-HUL-18"); - public static final JhoveMessage JPEG2000_HUL_19 = messageFactory.getMessage("JPEG2000-HUL-19"); - public static final JhoveMessage JPEG2000_HUL_20 = messageFactory.getMessage("JPEG2000-HUL-20"); - public static final JhoveMessage JPEG2000_HUL_21 = messageFactory.getMessage("JPEG2000-HUL-21"); - public static final JhoveMessage JPEG2000_HUL_22 = messageFactory.getMessage("JPEG2000-HUL-22"); - public static final JhoveMessage JPEG2000_HUL_23 = messageFactory.getMessage("JPEG2000-HUL-23"); - public static final JhoveMessage JPEG2000_HUL_24 = messageFactory.getMessage("JPEG2000-HUL-24"); - public static final JhoveMessage JPEG2000_HUL_25 = messageFactory.getMessage("JPEG2000-HUL-25"); - public static final JhoveMessage JPEG2000_HUL_26 = messageFactory.getMessage("JPEG2000-HUL-26"); - public static final JhoveMessage JPEG2000_HUL_27 = messageFactory.getMessage("JPEG2000-HUL-27"); - public static final JhoveMessage JPEG2000_HUL_28 = messageFactory.getMessage("JPEG2000-HUL-28"); - public static final JhoveMessage JPEG2000_HUL_29 = messageFactory.getMessage("JPEG2000-HUL-29"); - public static final JhoveMessage JPEG2000_HUL_30 = messageFactory.getMessage("JPEG2000-HUL-30"); - public static final JhoveMessage JPEG2000_HUL_31 = messageFactory.getMessage("JPEG2000-HUL-31"); - public static final JhoveMessage JPEG2000_HUL_32 = messageFactory.getMessage("JPEG2000-HUL-32"); - public static final JhoveMessage JPEG2000_HUL_33 = messageFactory.getMessage("JPEG2000-HUL-33"); - public static final JhoveMessage JPEG2000_HUL_34 = messageFactory.getMessage("JPEG2000-HUL-34"); - public static final JhoveMessage JPEG2000_HUL_35 = messageFactory.getMessage("JPEG2000-HUL-35"); - public static final JhoveMessage JPEG2000_HUL_36 = messageFactory.getMessage("JPEG2000-HUL-36"); - public static final JhoveMessage JPEG2000_HUL_37 = messageFactory.getMessage("JPEG2000-HUL-37"); - public static final JhoveMessage JPEG2000_HUL_38 = messageFactory.getMessage("JPEG2000-HUL-38"); - public static final JhoveMessage JPEG2000_HUL_39 = messageFactory.getMessage("JPEG2000-HUL-39"); - public static final JhoveMessage JPEG2000_HUL_40 = messageFactory.getMessage("JPEG2000-HUL-40"); - public static final JhoveMessage JPEG2000_HUL_41 = messageFactory.getMessage("JPEG2000-HUL-41"); - public static final JhoveMessage JPEG2000_HUL_42 = messageFactory.getMessage("JPEG2000-HUL-42"); - public static final JhoveMessage JPEG2000_HUL_43 = messageFactory.getMessage("JPEG2000-HUL-43"); - public static final JhoveMessage JPEG2000_HUL_44 = messageFactory.getMessage("JPEG2000-HUL-44"); - public static final JhoveMessage JPEG2000_HUL_45 = messageFactory.getMessage("JPEG2000-HUL-45"); - public static final JhoveMessage JPEG2000_HUL_46 = messageFactory.getMessage("JPEG2000-HUL-46"); - public static final JhoveMessage JPEG2000_HUL_47 = messageFactory.getMessage("JPEG2000-HUL-47"); - public static final JhoveMessage JPEG2000_HUL_48 = messageFactory.getMessage("JPEG2000-HUL-48"); - public static final JhoveMessage JPEG2000_HUL_49 = messageFactory.getMessage("JPEG2000-HUL-49"); - public static final JhoveMessage JPEG2000_HUL_50 = messageFactory.getMessage("JPEG2000-HUL-50"); - public static final JhoveMessage JPEG2000_HUL_51 = messageFactory.getMessage("JPEG2000-HUL-51"); - public static final JhoveMessage JPEG2000_HUL_52 = messageFactory.getMessage("JPEG2000-HUL-52"); - public static final JhoveMessage JPEG2000_HUL_53 = messageFactory.getMessage("JPEG2000-HUL-53"); - public static final JhoveMessage JPEG2000_HUL_54 = messageFactory.getMessage("JPEG2000-HUL-54"); - public static final JhoveMessage JPEG2000_HUL_55 = messageFactory.getMessage("JPEG2000-HUL-55"); - public static final JhoveMessage JPEG2000_HUL_56 = messageFactory.getMessage("JPEG2000-HUL-56"); + public static final JhoveMessage JPEG2000_HUL_4 = messageFactory.getMessage("JPEG2000-HUL-4"); + public static final JhoveMessage JPEG2000_HUL_5 = messageFactory.getMessage("JPEG2000-HUL-5"); + public static final JhoveMessage JPEG2000_HUL_6 = messageFactory.getMessage("JPEG2000-HUL-6"); + public static final JhoveMessage JPEG2000_HUL_7 = messageFactory.getMessage("JPEG2000-HUL-7"); + public static final JhoveMessage JPEG2000_HUL_8 = messageFactory.getMessage("JPEG2000-HUL-8"); + public static final JhoveMessage JPEG2000_HUL_9 = messageFactory.getMessage("JPEG2000-HUL-9"); + public static final JhoveMessage JPEG2000_HUL_10 = messageFactory.getMessage("JPEG2000-HUL-10"); + public static final JhoveMessage JPEG2000_HUL_11 = messageFactory.getMessage("JPEG2000-HUL-11"); + public static final JhoveMessage JPEG2000_HUL_12 = messageFactory.getMessage("JPEG2000-HUL-12"); + public static final JhoveMessage JPEG2000_HUL_13 = messageFactory.getMessage("JPEG2000-HUL-13"); + public static final JhoveMessage JPEG2000_HUL_14 = messageFactory.getMessage("JPEG2000-HUL-14"); + public static final JhoveMessage JPEG2000_HUL_15 = messageFactory.getMessage("JPEG2000-HUL-15"); + public static final JhoveMessage JPEG2000_HUL_16 = messageFactory.getMessage("JPEG2000-HUL-16"); + public static final JhoveMessage JPEG2000_HUL_17 = messageFactory.getMessage("JPEG2000-HUL-17"); + public static final JhoveMessage JPEG2000_HUL_18 = messageFactory.getMessage("JPEG2000-HUL-18"); + public static final JhoveMessage JPEG2000_HUL_19 = messageFactory.getMessage("JPEG2000-HUL-19"); + public static final JhoveMessage JPEG2000_HUL_20 = messageFactory.getMessage("JPEG2000-HUL-20"); + public static final JhoveMessage JPEG2000_HUL_21 = messageFactory.getMessage("JPEG2000-HUL-21"); + public static final JhoveMessage JPEG2000_HUL_22 = messageFactory.getMessage("JPEG2000-HUL-22"); + public static final JhoveMessage JPEG2000_HUL_23 = messageFactory.getMessage("JPEG2000-HUL-23"); + public static final JhoveMessage JPEG2000_HUL_24 = messageFactory.getMessage("JPEG2000-HUL-24"); + public static final JhoveMessage JPEG2000_HUL_25 = messageFactory.getMessage("JPEG2000-HUL-25"); + public static final JhoveMessage JPEG2000_HUL_26 = messageFactory.getMessage("JPEG2000-HUL-26"); + public static final JhoveMessage JPEG2000_HUL_27 = messageFactory.getMessage("JPEG2000-HUL-27"); + public static final JhoveMessage JPEG2000_HUL_28 = messageFactory.getMessage("JPEG2000-HUL-28"); + public static final JhoveMessage JPEG2000_HUL_29 = messageFactory.getMessage("JPEG2000-HUL-29"); + public static final JhoveMessage JPEG2000_HUL_30 = messageFactory.getMessage("JPEG2000-HUL-30"); + public static final JhoveMessage JPEG2000_HUL_31 = messageFactory.getMessage("JPEG2000-HUL-31"); + public static final JhoveMessage JPEG2000_HUL_32 = messageFactory.getMessage("JPEG2000-HUL-32"); + public static final JhoveMessage JPEG2000_HUL_33 = messageFactory.getMessage("JPEG2000-HUL-33"); + public static final JhoveMessage JPEG2000_HUL_34 = messageFactory.getMessage("JPEG2000-HUL-34"); + public static final JhoveMessage JPEG2000_HUL_35 = messageFactory.getMessage("JPEG2000-HUL-35"); + public static final JhoveMessage JPEG2000_HUL_36 = messageFactory.getMessage("JPEG2000-HUL-36"); + public static final JhoveMessage JPEG2000_HUL_37 = messageFactory.getMessage("JPEG2000-HUL-37"); + public static final JhoveMessage JPEG2000_HUL_38 = messageFactory.getMessage("JPEG2000-HUL-38"); + public static final JhoveMessage JPEG2000_HUL_39 = messageFactory.getMessage("JPEG2000-HUL-39"); + public static final JhoveMessage JPEG2000_HUL_40 = messageFactory.getMessage("JPEG2000-HUL-40"); + public static final JhoveMessage JPEG2000_HUL_41 = messageFactory.getMessage("JPEG2000-HUL-41"); + public static final JhoveMessage JPEG2000_HUL_42 = messageFactory.getMessage("JPEG2000-HUL-42"); + public static final JhoveMessage JPEG2000_HUL_43 = messageFactory.getMessage("JPEG2000-HUL-43"); + public static final JhoveMessage JPEG2000_HUL_44 = messageFactory.getMessage("JPEG2000-HUL-44"); + public static final JhoveMessage JPEG2000_HUL_45 = messageFactory.getMessage("JPEG2000-HUL-45"); + public static final JhoveMessage JPEG2000_HUL_46 = messageFactory.getMessage("JPEG2000-HUL-46"); + public static final JhoveMessage JPEG2000_HUL_47 = messageFactory.getMessage("JPEG2000-HUL-47"); + public static final JhoveMessage JPEG2000_HUL_48 = messageFactory.getMessage("JPEG2000-HUL-48"); + public static final JhoveMessage JPEG2000_HUL_49 = messageFactory.getMessage("JPEG2000-HUL-49"); + public static final JhoveMessage JPEG2000_HUL_50 = messageFactory.getMessage("JPEG2000-HUL-50"); + public static final JhoveMessage JPEG2000_HUL_51 = messageFactory.getMessage("JPEG2000-HUL-51"); + public static final JhoveMessage JPEG2000_HUL_52 = messageFactory.getMessage("JPEG2000-HUL-52"); + public static final JhoveMessage JPEG2000_HUL_53 = messageFactory.getMessage("JPEG2000-HUL-53"); + public static final JhoveMessage JPEG2000_HUL_54 = messageFactory.getMessage("JPEG2000-HUL-54"); + public static final JhoveMessage JPEG2000_HUL_55 = messageFactory.getMessage("JPEG2000-HUL-55"); + public static final JhoveMessage JPEG2000_HUL_56 = messageFactory.getMessage("JPEG2000-HUL-56"); } diff --git a/jhove-modules/jpeg2000-hul/src/main/java/edu/harvard/hul/ois/jhove/module/jpeg2000/NumberListBox.java b/jhove-modules/jpeg2000-hul/src/main/java/edu/harvard/hul/ois/jhove/module/jpeg2000/NumberListBox.java index 48c2af74c..5109a6046 100644 --- a/jhove-modules/jpeg2000-hul/src/main/java/edu/harvard/hul/ois/jhove/module/jpeg2000/NumberListBox.java +++ b/jhove-modules/jpeg2000-hul/src/main/java/edu/harvard/hul/ois/jhove/module/jpeg2000/NumberListBox.java @@ -1,96 +1,78 @@ -/********************************************************************** - * Jhove - JSTOR/Harvard Object Validation Environment - * Copyright 2004 by JSTOR and the President and Fellows of Harvard College - **********************************************************************/ - +/** + * ******************************************************************** Jhove - JSTOR/Harvard Object + * Validation Environment Copyright 2004 by JSTOR and the President and Fellows of Harvard College + * ******************************************************************** + */ package edu.harvard.hul.ois.jhove.module.jpeg2000; import edu.harvard.hul.ois.jhove.*; import java.io.*; /** - * Number list box. - * Provides a list of numbers with types. - * It's apparently used only within an Association - * box, so it simply makes a property available. - * - * See ISO/IEC FCD15444-2: 2000, L.9.12 + * Number list box. Provides a list of numbers with types. It's apparently used only within an + * Association box, so it simply makes a property available. * - * @author Gary McGath + *

See ISO/IEC FCD15444-2: 2000, L.9.12 * + * @author Gary McGath */ public class NumberListBox extends JP2Box { - private Property[] propArray; + private Property[] propArray; - /** - * Constructor with superbox. - * - * @param parent parent superbox of this box - */ - public NumberListBox(RandomAccessFile raf, BoxHolder parent) { - super(raf, parent); - } + /** + * Constructor with superbox. + * + * @param parent parent superbox of this box + */ + public NumberListBox(RandomAccessFile raf, BoxHolder parent) { + super(raf, parent); + } - /** Reads the box, putting appropriate information in - * the RepInfo object. setModule, setBoxHeader, - * setRepInfo and setDataInputStream must be called - * before readBox is called. - * readBox must completely consume the - * box, so that the next byte to be read by the - * DataInputStream is the FF byte of the next Box. - */ - @Override - public boolean readBox() throws IOException { - initBytesRead (); + /** + * Reads the box, putting appropriate information in the RepInfo object. setModule, setBoxHeader, + * setRepInfo and setDataInputStream must be called before readBox is called. + * readBox must completely consume the box, so that the next byte to be read by the + * DataInputStream is the FF byte of the next Box. + */ + @Override + public boolean readBox() throws IOException { + initBytesRead(); - int numEntries = (int) _boxHeader.getDataLength () / 4; - if (numEntries > 0) { - propArray = new Property[numEntries]; - for (int i = 0; i < numEntries; i++) { - long num = _module.readUnsignedInt (_dstrm); - // High byte is type, low three bytes are the - // number. - int typeByte = (int) ((num & 0XFF000000L) >> 24); - int numValue = (int) (num & 0XFFFFFF); - Property[] p = new Property[2]; - p[0] = _module.addIntegerProperty("Type", - typeByte, - JP2Strings.numberListTypeStr); - p[1] = new Property ("Value", - PropertyType.INTEGER, - new Integer (numValue)); - propArray[i] = new Property ("Number", - PropertyType.PROPERTY, - PropertyArity.ARRAY, - p); - } - } - finalizeBytesRead (); - return true; + int numEntries = (int) _boxHeader.getDataLength() / 4; + if (numEntries > 0) { + propArray = new Property[numEntries]; + for (int i = 0; i < numEntries; i++) { + long num = _module.readUnsignedInt(_dstrm); + // High byte is type, low three bytes are the + // number. + int typeByte = (int) ((num & 0XFF000000L) >> 24); + int numValue = (int) (num & 0XFFFFFF); + Property[] p = new Property[2]; + p[0] = _module.addIntegerProperty("Type", typeByte, JP2Strings.numberListTypeStr); + p[1] = new Property("Value", PropertyType.INTEGER, new Integer(numValue)); + propArray[i] = new Property("Number", PropertyType.PROPERTY, PropertyArity.ARRAY, p); + } } + finalizeBytesRead(); + return true; + } - /** Returns a Property which describes the Box, for use - * by Association boxes and perhaps others. - */ - @Override - protected Property getSelfPropDesc () - { - if (propArray != null) { - return new Property (DESCRIPTION_NAME, - PropertyType.PROPERTY, - PropertyArity.ARRAY, - propArray); - } - // A number list with no numbers isn't explicitly illegal - return null; + /** + * Returns a Property which describes the Box, for use by Association boxes and perhaps others. + */ + @Override + protected Property getSelfPropDesc() { + if (propArray != null) { + return new Property(DESCRIPTION_NAME, PropertyType.PROPERTY, PropertyArity.ARRAY, propArray); } + // A number list with no numbers isn't explicitly illegal + return null; + } - - /** Returns the name of the Box. */ - @Override - protected String getSelfPropName () - { - return "Number List Box"; - } + /** Returns the name of the Box. */ + @Override + protected String getSelfPropName() { + return "Number List Box"; + } } diff --git a/jhove-modules/jpeg2000-hul/src/main/java/edu/harvard/hul/ois/jhove/module/jpeg2000/OpacityBox.java b/jhove-modules/jpeg2000-hul/src/main/java/edu/harvard/hul/ois/jhove/module/jpeg2000/OpacityBox.java index dc7dbff2e..5545c1b7c 100644 --- a/jhove-modules/jpeg2000-hul/src/main/java/edu/harvard/hul/ois/jhove/module/jpeg2000/OpacityBox.java +++ b/jhove-modules/jpeg2000-hul/src/main/java/edu/harvard/hul/ois/jhove/module/jpeg2000/OpacityBox.java @@ -1,9 +1,9 @@ -/********************************************************************** - * Jhove - JSTOR/Harvard Object Validation Environment - * Copyright 2004 by JSTOR and the President and Fellows of Harvard College +/** + * ******************************************************************** Jhove - JSTOR/Harvard Object + * Validation Environment Copyright 2004 by JSTOR and the President and Fellows of Harvard College * - **********************************************************************/ - + *

******************************************************************** + */ package edu.harvard.hul.ois.jhove.module.jpeg2000; import edu.harvard.hul.ois.jhove.*; @@ -11,96 +11,79 @@ import java.util.*; /** - * Opacity Box (JPX). - * See ISO/IEC FCD15444-2: 2000, L.9.4.6 - * - * - * @author Gary McGath + * Opacity Box (JPX). See ISO/IEC FCD15444-2: 2000, L.9.4.6 * + * @author Gary McGath */ public class OpacityBox extends JP2Box { + /** + * Constructor with superbox. + * + * @param parent parent superbox of this box + */ + public OpacityBox(RandomAccessFile raf, BoxHolder parent) { + super(raf, parent); + } - /** - * Constructor with superbox. - * - * @param parent parent superbox of this box - */ - public OpacityBox(RandomAccessFile raf, BoxHolder parent) { - super(raf, parent); + /** + * Reads the box, putting appropriate information in the RepInfo object. setModule, setBoxHeader, + * setRepInfo and setDataInputStream must be called before readBox is called. + * readBox must completely consume the box, so that the next byte to be read by the + * DataInputStream is the FF byte of the next Box. + */ + @Override + public boolean readBox() throws IOException { + if (!(_parentBox instanceof ComposLayerHdrBox)) { + wrongBoxContext(); + return false; } + initBytesRead(); - /** Reads the box, putting appropriate information in - * the RepInfo object. setModule, setBoxHeader, - * setRepInfo and setDataInputStream must be called - * before readBox is called. - * readBox must completely consume the - * box, so that the next byte to be read by the - * DataInputStream is the FF byte of the next Box. - */ - @Override - public boolean readBox() throws IOException { - if (!(_parentBox instanceof ComposLayerHdrBox)) { - wrongBoxContext(); - return false; - } - initBytesRead (); - - List propList = new ArrayList (4); - - int otyp = ModuleBase.readUnsignedByte (_dstrm, _module); - propList.add (_module.addIntegerProperty ("Type", - otyp, - JP2Strings.opacityTypeStr)); - if (otyp > 2) { - _repInfo.setMessage (new ErrorMessage - (MessageConstants.JPEG2000_HUL_36, - _module.getFilePos ())); - _repInfo.setValid (false); - return false; - } - - // The documentation of the Opacity Box is self-contradictory - // with regard to what OTyp values are - // followed by NCH and CV[n] fields. (There is also - // a reference to an unspecified "PR" field.) - // The only safe course is to see if there are any more bytes. - int bytesLeft = (int) _boxHeader.getDataLength () - 1; - if (bytesLeft > 0) { - int nch = ModuleBase.readUnsignedByte (_dstrm, _module); - // The size in bytes of the channel-key values - // depends on the bit depth of the corresponding - // channel, but it's simpler to calculate it based - // on the bytes remaining. - int[] keys = new int[nch]; - int keysize = (bytesLeft - 1) / nch; - for (int i = 0; i < nch; i++) { - int chkey = 0; - for (int j = 0; j < keysize; j++) { - chkey = (chkey << 8) + - ModuleBase.readUnsignedByte (_dstrm, _module); - } - keys[i] = chkey; - } - propList.add (new Property ("ChromaKeyValues", - PropertyType.INTEGER, - PropertyArity.ARRAY, - keys)); - } - ((ComposLayerHdrBox) _parentBox).addOpacity - (new Property ("Opacity", - PropertyType.PROPERTY, - PropertyArity.LIST, - propList)); - - finalizeBytesRead (); - return true; + List propList = new ArrayList(4); + + int otyp = ModuleBase.readUnsignedByte(_dstrm, _module); + propList.add(_module.addIntegerProperty("Type", otyp, JP2Strings.opacityTypeStr)); + if (otyp > 2) { + _repInfo.setMessage(new ErrorMessage(MessageConstants.JPEG2000_HUL_36, _module.getFilePos())); + _repInfo.setValid(false); + return false; } - /** Returns the name of the Box. */ - @Override - protected String getSelfPropName () - { - return "Opacity Box"; + // The documentation of the Opacity Box is self-contradictory + // with regard to what OTyp values are + // followed by NCH and CV[n] fields. (There is also + // a reference to an unspecified "PR" field.) + // The only safe course is to see if there are any more bytes. + int bytesLeft = (int) _boxHeader.getDataLength() - 1; + if (bytesLeft > 0) { + int nch = ModuleBase.readUnsignedByte(_dstrm, _module); + // The size in bytes of the channel-key values + // depends on the bit depth of the corresponding + // channel, but it's simpler to calculate it based + // on the bytes remaining. + int[] keys = new int[nch]; + int keysize = (bytesLeft - 1) / nch; + for (int i = 0; i < nch; i++) { + int chkey = 0; + for (int j = 0; j < keysize; j++) { + chkey = (chkey << 8) + ModuleBase.readUnsignedByte(_dstrm, _module); + } + keys[i] = chkey; + } + propList.add( + new Property("ChromaKeyValues", PropertyType.INTEGER, PropertyArity.ARRAY, keys)); } + ((ComposLayerHdrBox) _parentBox) + .addOpacity(new Property("Opacity", PropertyType.PROPERTY, PropertyArity.LIST, propList)); + + finalizeBytesRead(); + return true; + } + + /** Returns the name of the Box. */ + @Override + protected String getSelfPropName() { + return "Opacity Box"; + } } diff --git a/jhove-modules/jpeg2000-hul/src/main/java/edu/harvard/hul/ois/jhove/module/jpeg2000/PLMMarkerSegment.java b/jhove-modules/jpeg2000-hul/src/main/java/edu/harvard/hul/ois/jhove/module/jpeg2000/PLMMarkerSegment.java index 21d3f0cc0..030835f15 100644 --- a/jhove-modules/jpeg2000-hul/src/main/java/edu/harvard/hul/ois/jhove/module/jpeg2000/PLMMarkerSegment.java +++ b/jhove-modules/jpeg2000-hul/src/main/java/edu/harvard/hul/ois/jhove/module/jpeg2000/PLMMarkerSegment.java @@ -1,86 +1,77 @@ -/********************************************************************** - * Jhove - JSTOR/Harvard Object Validation Environment - * Copyright 2004 by JSTOR and the President and Fellows of Harvard College +/** + * ******************************************************************** Jhove - JSTOR/Harvard Object + * Validation Environment Copyright 2004 by JSTOR and the President and Fellows of Harvard College * - **********************************************************************/ - + *

******************************************************************** + */ package edu.harvard.hul.ois.jhove.module.jpeg2000; -import java.io.*; -//import java.util.*; import edu.harvard.hul.ois.jhove.*; +import java.io.*; /** - * Class for the PLM Marker segment. This gives packet lengths at - * the header level. - * - * @author Gary McGath + * Class for the PLM Marker segment. This gives packet lengths at the header level. * + * @author Gary McGath */ public class PLMMarkerSegment extends MarkerSegment { - // A tile part may extend across several marker segments. - // we store the remainder statically here. - static int nplmLeft; - - /** - * Constructor. - */ - public PLMMarkerSegment() { - super(); + // A tile part may extend across several marker segments. + // we store the remainder statically here. + static int nplmLeft; + + /** Constructor. */ + public PLMMarkerSegment() { + super(); + } + + /** + * Processes the marker segment. The DataInputStream will be at the point of having read the + * marker code. The process method must consume exactly the number of bytes remaining + * in the marker segment. + * + * @param bytesToEat The number of bytes that must be consumed. If it is 0 for a MarkerSegment, + * the number of bytes to consume is unknown. + */ + @Override + protected boolean process(int bytesToEat) throws IOException { + int zplm = ModuleBase.readUnsignedByte(_dstream, _module); + --bytesToEat; + if (zplm == 0) { + nplmLeft = 0; + } + // Whether there is an nplm, giving the number of bytes + // of iplm information for the tile part, depends on whether + // the previous nplm has been counted out. This is actually + // quite excessive, since nplm can't be any bigger than 255. + // Who DESIGNED this silly marker segment anyway? + if (nplmLeft == 0) { + nplmLeft = ModuleBase.readUnsignedByte(_dstream, _module); + --bytesToEat; } - /** - * Processes the marker segment. The DataInputStream - * will be at the point of having read the marker code. The - * process method must consume exactly the number - * of bytes remaining in the marker segment. - * - * @param bytesToEat The number of bytes that must be consumed. - * If it is 0 for a MarkerSegment, the - * number of bytes to consume is unknown. - */ - @Override - protected boolean process(int bytesToEat) throws IOException { - int zplm = ModuleBase.readUnsignedByte (_dstream, _module); - --bytesToEat; - if (zplm == 0) { - nplmLeft = 0; - } - // Whether there is an nplm, giving the number of bytes - // of iplm information for the tile part, depends on whether - // the previous nplm has been counted out. This is actually - // quite excessive, since nplm can't be any bigger than 255. - // Who DESIGNED this silly marker segment anyway? - if (nplmLeft == 0) { - nplmLeft = ModuleBase.readUnsignedByte (_dstream, _module); - --bytesToEat; + // To add to the complications, each iplm can have a different + // length. This allows unlimited packet lengths -- + // or to be exact, the maximum length is 2 ^ (7 * 255) if there's + // only one packet. For this implementation, we limit the maximum + // packet length to 2 ^ 63. + while (bytesToEat > 0) { + long pktLen = 0; + for (; ; ) { + int pkByte = ModuleBase.readUnsignedByte(_dstream, _module); + if (--bytesToEat < 0) { + // bytes of a number can't cross marker segment boundaries + _repInfo.setMessage(new ErrorMessage(MessageConstants.JPEG2000_HUL_38)); + return false; } - - // To add to the complications, each iplm can have a different - // length. This allows unlimited packet lengths -- - // or to be exact, the maximum length is 2 ^ (7 * 255) if there's - // only one packet. For this implementation, we limit the maximum - // packet length to 2 ^ 63. - while (bytesToEat > 0) { - long pktLen = 0; - for (;;) { - int pkByte = ModuleBase.readUnsignedByte (_dstream, _module); - if (--bytesToEat < 0) { - // bytes of a number can't cross marker segment boundaries - _repInfo.setMessage (new ErrorMessage - (MessageConstants.JPEG2000_HUL_38)); - return false; - } - pktLen = (pktLen << 7) | (pkByte | 0X7F); - if ((pkByte & 0X80) == 0) { - break; - } - _cs.addPacketLength (pktLen); - } + pktLen = (pktLen << 7) | (pkByte | 0X7F); + if ((pkByte & 0X80) == 0) { + break; } - - return true; + _cs.addPacketLength(pktLen); + } } + return true; + } } diff --git a/jhove-modules/jpeg2000-hul/src/main/java/edu/harvard/hul/ois/jhove/module/jpeg2000/PLTMarkerSegment.java b/jhove-modules/jpeg2000-hul/src/main/java/edu/harvard/hul/ois/jhove/module/jpeg2000/PLTMarkerSegment.java index c669a9afd..0bc90d17a 100644 --- a/jhove-modules/jpeg2000-hul/src/main/java/edu/harvard/hul/ois/jhove/module/jpeg2000/PLTMarkerSegment.java +++ b/jhove-modules/jpeg2000-hul/src/main/java/edu/harvard/hul/ois/jhove/module/jpeg2000/PLTMarkerSegment.java @@ -1,75 +1,65 @@ -/********************************************************************** - * Jhove - JSTOR/Harvard Object Validation Environment - * Copyright 2004 by JSTOR and the President and Fellows of Harvard College +/** + * ******************************************************************** Jhove - JSTOR/Harvard Object + * Validation Environment Copyright 2004 by JSTOR and the President and Fellows of Harvard College * - **********************************************************************/ - + *

******************************************************************** + */ package edu.harvard.hul.ois.jhove.module.jpeg2000; -import java.io.*; -//import java.util.*; import edu.harvard.hul.ois.jhove.*; +import java.io.*; /** - * Class for the PLT Marker segment. This gives packet lengths at - * the tile level. + * Class for the PLT Marker segment. This gives packet lengths at the tile level. * * @author Gary McGath - * */ public class PLTMarkerSegment extends MarkerSegment { - /** - * Constructor. - */ - public PLTMarkerSegment() { - super(); - } + /** Constructor. */ + public PLTMarkerSegment() { + super(); + } - /** - * Processes the marker segment. The DataInputStream - * will be at the point of having read the marker code. The - * process method must consume exactly the number - * of bytes remaining in the marker segment. - * - * @param bytesToEat The number of bytes that must be consumed. - * If it is 0 for a MarkerSegment, the - * number of bytes to consume is unknown. - */ - @Override - protected boolean process(int bytesToEat) throws IOException { - Tile tile = _ccs.getCurTile (); - if (tile == null) { - _repInfo.setMessage (new ErrorMessage - (MessageConstants.JPEG2000_HUL_40)); - return false; // a tile (SOT) is required + /** + * Processes the marker segment. The DataInputStream will be at the point of having read the + * marker code. The process method must consume exactly the number of bytes remaining + * in the marker segment. + * + * @param bytesToEat The number of bytes that must be consumed. If it is 0 for a MarkerSegment, + * the number of bytes to consume is unknown. + */ + @Override + protected boolean process(int bytesToEat) throws IOException { + Tile tile = _ccs.getCurTile(); + if (tile == null) { + _repInfo.setMessage(new ErrorMessage(MessageConstants.JPEG2000_HUL_40)); + return false; // a tile (SOT) is required + } + // Skip unsigned byte + ModuleBase.readUnsignedByte(_dstream, _module); + --bytesToEat; + // As with PLM, each iplt can have a different + // length. This allows unlimited packet lengths -- + // or to be exact, the maximum length is 2 ^ (7 * 255) if there's + // only one packet. For this implementation, we limit the maximum + // packet length to 2 ^ 63. + while (bytesToEat > 0) { + long pktLen = 0; + for (; ; ) { + int pkByte = ModuleBase.readUnsignedByte(_dstream, _module); + if (--bytesToEat < 0) { + // bytes of a number can't cross marker segment boundaries + _repInfo.setMessage(new ErrorMessage(MessageConstants.JPEG2000_HUL_39)); + return false; } - // Skip unsigned byte - ModuleBase.readUnsignedByte (_dstream, _module); - --bytesToEat; - // As with PLM, each iplt can have a different - // length. This allows unlimited packet lengths -- - // or to be exact, the maximum length is 2 ^ (7 * 255) if there's - // only one packet. For this implementation, we limit the maximum - // packet length to 2 ^ 63. - while (bytesToEat > 0) { - long pktLen = 0; - for (;;) { - int pkByte = ModuleBase.readUnsignedByte (_dstream, _module); - if (--bytesToEat < 0) { - // bytes of a number can't cross marker segment boundaries - _repInfo.setMessage (new ErrorMessage - (MessageConstants.JPEG2000_HUL_39)); - return false; - } - pktLen = (pktLen << 7) | (pkByte | 0X7F); - if ((pkByte & 0X80) == 0) { - break; - } - tile.addPacketLength (pktLen); - } + pktLen = (pktLen << 7) | (pkByte | 0X7F); + if ((pkByte & 0X80) == 0) { + break; } - return true; + tile.addPacketLength(pktLen); + } } - + return true; + } } diff --git a/jhove-modules/jpeg2000-hul/src/main/java/edu/harvard/hul/ois/jhove/module/jpeg2000/POCMarkerSegment.java b/jhove-modules/jpeg2000-hul/src/main/java/edu/harvard/hul/ois/jhove/module/jpeg2000/POCMarkerSegment.java index 9e1b05456..2820a4272 100644 --- a/jhove-modules/jpeg2000-hul/src/main/java/edu/harvard/hul/ois/jhove/module/jpeg2000/POCMarkerSegment.java +++ b/jhove-modules/jpeg2000-hul/src/main/java/edu/harvard/hul/ois/jhove/module/jpeg2000/POCMarkerSegment.java @@ -1,111 +1,88 @@ -/********************************************************************** - * Jhove - JSTOR/Harvard Object Validation Environment - * Copyright 2004 by JSTOR and the President and Fellows of Harvard College +/** + * ******************************************************************** Jhove - JSTOR/Harvard Object + * Validation Environment Copyright 2004 by JSTOR and the President and Fellows of Harvard College * - **********************************************************************/ - + *

******************************************************************** + */ package edu.harvard.hul.ois.jhove.module.jpeg2000; -import java.io.*; import edu.harvard.hul.ois.jhove.*; +import java.io.*; /** - * Class for the POC (Progression order change) marker segment. - * May occur in the main or the tile part header. + * Class for the POC (Progression order change) marker segment. May occur in the main or the tile + * part header. * * @author Gary McGath - * */ public class POCMarkerSegment extends MarkerSegment { - /** - * - */ - public POCMarkerSegment() { - super(); - } + /** */ + public POCMarkerSegment() { + super(); + } - /** Process the marker segment. The DataInputStream - * will be at the point of having read the marker code. The - * process method must consume exactly the number - * of bytes remaining in the marker segment. - * - * @param bytesToEat The number of bytes that must be consumed. - * If it is 0 for a MarkerSegment, the - * number of bytes to consume is unknown. - * - * @return true if segment is well-formed, - * false otherwise. - */ - @Override - protected boolean process(int bytesToEat) throws IOException { - int compIdxBytes = nCompBytes(); - if (compIdxBytes == 0) { - _repInfo.setMessage (new ErrorMessage - (MessageConstants.JPEG2000_HUL_41)); - // POC found before SIZ - return false; - } - // The number of bytes per change depends on whether component - // indices take one or two bytes. - int changeSize = compIdxBytes < 257 ? 7 : 9; // number of bytes per change - int nChanges = bytesToEat / changeSize; - // Make sure it's an even multiple - if (changeSize * nChanges != bytesToEat) { - _repInfo.setMessage (new ErrorMessage - (MessageConstants.JPEG2000_HUL_42)); - return false; - } - Property[] changes = new Property[nChanges]; - for (int i = 0; i < nChanges; i++) { - int rspoc = _module.readUnsignedShort (_dstream); // resolution level idx - int cspoc; - // size of Ccoc field depends on number of components - if (compIdxBytes < 257) { - cspoc = ModuleBase.readUnsignedByte (_dstream, _module); - } - else{ - cspoc = _module.readUnsignedShort (_dstream); - } - int lyepoc = _module.readUnsignedShort (_dstream); - // Skip repoc byte - ModuleBase.readUnsignedByte (_dstream, _module); - int cepoc; - if (compIdxBytes < 257) { - cepoc = ModuleBase.readUnsignedByte (_dstream, _module); - } - else { - cepoc = _module.readUnsignedShort (_dstream); - } - int ppoc = ModuleBase.readUnsignedByte (_dstream, _module); - Property[] propArr = new Property[5]; - propArr[0] = new Property ("StartResolutionLevelIndex", - PropertyType.INTEGER, - new Integer (rspoc)); - propArr[1] = new Property ("ComponentIndex", - PropertyType.INTEGER, - new Integer (cspoc)); - propArr[2] = new Property ("LayerIndex", - PropertyType.INTEGER, - new Integer (lyepoc)); - propArr[3] = new Property ("EndResolutionLevelIndex", - PropertyType.INTEGER, - new Integer (cepoc)); - propArr[4] = new Property ("ProgressionOrder", - PropertyType.INTEGER, - new Integer (ppoc)); - changes[i] = new Property ("Change", - PropertyType.PROPERTY, - PropertyArity.ARRAY, - propArr); - } - MainOrTile cs = getMainOrTile (); - cs.setPOCProperty (new Property ("ProgressionOrderChange", - PropertyType.PROPERTY, - PropertyArity.ARRAY, - changes)); - - return true; + /** + * Process the marker segment. The DataInputStream will be at the point of having read the marker + * code. The process method must consume exactly the number of bytes remaining in the + * marker segment. + * + * @param bytesToEat The number of bytes that must be consumed. If it is 0 for a MarkerSegment, + * the number of bytes to consume is unknown. + * @return true if segment is well-formed, false otherwise. + */ + @Override + protected boolean process(int bytesToEat) throws IOException { + int compIdxBytes = nCompBytes(); + if (compIdxBytes == 0) { + _repInfo.setMessage(new ErrorMessage(MessageConstants.JPEG2000_HUL_41)); + // POC found before SIZ + return false; + } + // The number of bytes per change depends on whether component + // indices take one or two bytes. + int changeSize = compIdxBytes < 257 ? 7 : 9; // number of bytes per change + int nChanges = bytesToEat / changeSize; + // Make sure it's an even multiple + if (changeSize * nChanges != bytesToEat) { + _repInfo.setMessage(new ErrorMessage(MessageConstants.JPEG2000_HUL_42)); + return false; + } + Property[] changes = new Property[nChanges]; + for (int i = 0; i < nChanges; i++) { + int rspoc = _module.readUnsignedShort(_dstream); // resolution level idx + int cspoc; + // size of Ccoc field depends on number of components + if (compIdxBytes < 257) { + cspoc = ModuleBase.readUnsignedByte(_dstream, _module); + } else { + cspoc = _module.readUnsignedShort(_dstream); + } + int lyepoc = _module.readUnsignedShort(_dstream); + // Skip repoc byte + ModuleBase.readUnsignedByte(_dstream, _module); + int cepoc; + if (compIdxBytes < 257) { + cepoc = ModuleBase.readUnsignedByte(_dstream, _module); + } else { + cepoc = _module.readUnsignedShort(_dstream); + } + int ppoc = ModuleBase.readUnsignedByte(_dstream, _module); + Property[] propArr = new Property[5]; + propArr[0] = + new Property("StartResolutionLevelIndex", PropertyType.INTEGER, new Integer(rspoc)); + propArr[1] = new Property("ComponentIndex", PropertyType.INTEGER, new Integer(cspoc)); + propArr[2] = new Property("LayerIndex", PropertyType.INTEGER, new Integer(lyepoc)); + propArr[3] = + new Property("EndResolutionLevelIndex", PropertyType.INTEGER, new Integer(cepoc)); + propArr[4] = new Property("ProgressionOrder", PropertyType.INTEGER, new Integer(ppoc)); + changes[i] = new Property("Change", PropertyType.PROPERTY, PropertyArity.ARRAY, propArr); } + MainOrTile cs = getMainOrTile(); + cs.setPOCProperty( + new Property( + "ProgressionOrderChange", PropertyType.PROPERTY, PropertyArity.ARRAY, changes)); + return true; + } } diff --git a/jhove-modules/jpeg2000-hul/src/main/java/edu/harvard/hul/ois/jhove/module/jpeg2000/PPMMarkerSegment.java b/jhove-modules/jpeg2000-hul/src/main/java/edu/harvard/hul/ois/jhove/module/jpeg2000/PPMMarkerSegment.java index 29e745d71..2ebb7e8f8 100644 --- a/jhove-modules/jpeg2000-hul/src/main/java/edu/harvard/hul/ois/jhove/module/jpeg2000/PPMMarkerSegment.java +++ b/jhove-modules/jpeg2000-hul/src/main/java/edu/harvard/hul/ois/jhove/module/jpeg2000/PPMMarkerSegment.java @@ -1,65 +1,55 @@ -/********************************************************************** - * Jhove - JSTOR/Harvard Object Validation Environment - * Copyright 2004 by JSTOR and the President and Fellows of Harvard College - **********************************************************************/ - +/** + * ******************************************************************** Jhove - JSTOR/Harvard Object + * Validation Environment Copyright 2004 by JSTOR and the President and Fellows of Harvard College + * ******************************************************************** + */ package edu.harvard.hul.ois.jhove.module.jpeg2000; -import java.io.*; import edu.harvard.hul.ois.jhove.*; +import java.io.*; /** - * Class for the PPM (Packed packet headers, main header) - * marker segment. I'm assuming for the present that the - * full details of packet headers is getting deeper into - * the bits than we want, so it just checks some basic - * information. There can be multiple PPM marker segments. + * Class for the PPM (Packed packet headers, main header) marker segment. I'm assuming for the + * present that the full details of packet headers is getting deeper into the bits than we want, so + * it just checks some basic information. There can be multiple PPM marker segments. * * @author Gary McGath - * */ public class PPMMarkerSegment extends MarkerSegment { - /** - * Constructor. - */ - public PPMMarkerSegment() { - super(); - _ccs.setPPMSeen (true); - } + /** Constructor. */ + public PPMMarkerSegment() { + super(); + _ccs.setPPMSeen(true); + } + /** + * Processes the marker segment. The DataInputStream will be at the point of having read the + * marker code. The process method must consume exactly the number of bytes remaining + * in the marker segment. + * + * @param bytesToEat The number of bytes that must be consumed. If it is 0 for a MarkerSegment, + * the number of bytes to consume is unknown. + */ + @Override + protected boolean process(int bytesToEat) throws IOException { + // Get index of this segment + // Skip Unsigned byte + ModuleBase.readUnsignedByte(_dstream, _module); + --bytesToEat; - /** - * Processes the marker segment. The DataInputStream - * will be at the point of having read the marker code. The - * process method must consume exactly the number - * of bytes remaining in the marker segment. - * - * @param bytesToEat The number of bytes that must be consumed. - * If it is 0 for a MarkerSegment, the - * number of bytes to consume is unknown. - */ - @Override - protected boolean process(int bytesToEat) throws IOException - { - // Get index of this segment - // Skip Unsigned byte - ModuleBase.readUnsignedByte (_dstream, _module); - --bytesToEat; - - while (bytesToEat > 0) { - // Number of bytes of Ippm info in the ith tile part - long nppm = _module.readUnsignedInt (_dstream); - bytesToEat -= 4; - if (nppm > bytesToEat) { - _repInfo.setMessage(new ErrorMessage - (MessageConstants.JPEG2000_HUL_43)); - return false; - } - _cs.addPPMLength (nppm); - _module.skipBytes (_dstream, (int) nppm, _module); - bytesToEat -= nppm; - } - return true; + while (bytesToEat > 0) { + // Number of bytes of Ippm info in the ith tile part + long nppm = _module.readUnsignedInt(_dstream); + bytesToEat -= 4; + if (nppm > bytesToEat) { + _repInfo.setMessage(new ErrorMessage(MessageConstants.JPEG2000_HUL_43)); + return false; + } + _cs.addPPMLength(nppm); + _module.skipBytes(_dstream, (int) nppm, _module); + bytesToEat -= nppm; } + return true; + } } diff --git a/jhove-modules/jpeg2000-hul/src/main/java/edu/harvard/hul/ois/jhove/module/jpeg2000/PPTMarkerSegment.java b/jhove-modules/jpeg2000-hul/src/main/java/edu/harvard/hul/ois/jhove/module/jpeg2000/PPTMarkerSegment.java index 536d68099..0342d904b 100644 --- a/jhove-modules/jpeg2000-hul/src/main/java/edu/harvard/hul/ois/jhove/module/jpeg2000/PPTMarkerSegment.java +++ b/jhove-modules/jpeg2000-hul/src/main/java/edu/harvard/hul/ois/jhove/module/jpeg2000/PPTMarkerSegment.java @@ -1,72 +1,62 @@ -/********************************************************************** - * Jhove - JSTOR/Harvard Object Validation Environment - * Copyright 2004 by JSTOR and the President and Fellows of Harvard College - **********************************************************************/ - +/** + * ******************************************************************** Jhove - JSTOR/Harvard Object + * Validation Environment Copyright 2004 by JSTOR and the President and Fellows of Harvard College + * ******************************************************************** + */ package edu.harvard.hul.ois.jhove.module.jpeg2000; -import java.io.*; import edu.harvard.hul.ois.jhove.*; +import java.io.*; /** - * Class for the PPT (Packed packet headers, tile-part header) - * marker segment. Similar to the PPM marker segment, but - * applicable to tile parts rather than the main header. + * Class for the PPT (Packed packet headers, tile-part header) marker segment. Similar to the PPM + * marker segment, but applicable to tile parts rather than the main header. * * @author Gary McGath - * */ public class PPTMarkerSegment extends MarkerSegment { - /** - * Constructor. - */ - public PPTMarkerSegment() { - super(); - } - - /** - * Processes the marker segment. The DataInputStream - * will be at the point of having read the marker code. The - * process method must consume exactly the number - * of bytes remaining in the marker segment. - * - * @param bytesToEat The number of bytes that must be consumed. - * If it is 0 for a MarkerSegment, the - * number of bytes to consume is unknown. - */ - @Override - protected boolean process(int bytesToEat) throws IOException - { - if (_ccs.isPPMSeen ()) { - _repInfo.setMessage (new ErrorMessage - (MessageConstants.JPEG2000_HUL_45)); - return false; - } - Tile tile = _ccs.getCurTile (); - if (tile == null ) { - _repInfo.setMessage (new ErrorMessage - (MessageConstants.JPEG2000_HUL_44)); - return false; - } + /** Constructor. */ + public PPTMarkerSegment() { + super(); + } - // Skip unsigned byte - ModuleBase.readUnsignedByte (_dstream, _module); - --bytesToEat; - while (bytesToEat > 0) { - // Number of bytes of Ippm info in the ith tile part - long nppt = _module.readUnsignedInt (_dstream); - bytesToEat -= 4; - if (nppt > bytesToEat) { - _repInfo.setMessage(new ErrorMessage - (MessageConstants.JPEG2000_HUL_43)); - return false; - } - tile.addPPTLength (nppt); - _module.skipBytes (_dstream, (int) nppt, _module); - bytesToEat -= nppt; - } + /** + * Processes the marker segment. The DataInputStream will be at the point of having read the + * marker code. The process method must consume exactly the number of bytes remaining + * in the marker segment. + * + * @param bytesToEat The number of bytes that must be consumed. If it is 0 for a MarkerSegment, + * the number of bytes to consume is unknown. + */ + @Override + protected boolean process(int bytesToEat) throws IOException { + if (_ccs.isPPMSeen()) { + _repInfo.setMessage(new ErrorMessage(MessageConstants.JPEG2000_HUL_45)); + return false; + } + Tile tile = _ccs.getCurTile(); + if (tile == null) { + _repInfo.setMessage(new ErrorMessage(MessageConstants.JPEG2000_HUL_44)); + return false; + } - return true; + // Skip unsigned byte + ModuleBase.readUnsignedByte(_dstream, _module); + --bytesToEat; + while (bytesToEat > 0) { + // Number of bytes of Ippm info in the ith tile part + long nppt = _module.readUnsignedInt(_dstream); + bytesToEat -= 4; + if (nppt > bytesToEat) { + _repInfo.setMessage(new ErrorMessage(MessageConstants.JPEG2000_HUL_43)); + return false; + } + tile.addPPTLength(nppt); + _module.skipBytes(_dstream, (int) nppt, _module); + bytesToEat -= nppt; } + + return true; + } } diff --git a/jhove-modules/jpeg2000-hul/src/main/java/edu/harvard/hul/ois/jhove/module/jpeg2000/PaletteBox.java b/jhove-modules/jpeg2000-hul/src/main/java/edu/harvard/hul/ois/jhove/module/jpeg2000/PaletteBox.java index 1eb5a3939..d0228ae40 100644 --- a/jhove-modules/jpeg2000-hul/src/main/java/edu/harvard/hul/ois/jhove/module/jpeg2000/PaletteBox.java +++ b/jhove-modules/jpeg2000-hul/src/main/java/edu/harvard/hul/ois/jhove/module/jpeg2000/PaletteBox.java @@ -1,130 +1,103 @@ -/********************************************************************** - * Jhove - JSTOR/Harvard Object Validation Environment - * Copyright 2004 by JSTOR and the President and Fellows of Harvard College - **********************************************************************/ - +/** + * ******************************************************************** Jhove - JSTOR/Harvard Object + * Validation Environment Copyright 2004 by JSTOR and the President and Fellows of Harvard College + * ******************************************************************** + */ package edu.harvard.hul.ois.jhove.module.jpeg2000; import edu.harvard.hul.ois.jhove.*; import java.io.*; /** - * Palette box. - * See I.5.3.4 in ISO/IEC 15444-1:2000 + * Palette box. See I.5.3.4 in ISO/IEC 15444-1:2000 * * @author Gary McGath - * */ public class PaletteBox extends JP2Box { + /** + * Constructor with superbox. + * + * @param parent parent superbox of this box + */ + public PaletteBox(RandomAccessFile raf, BoxHolder parent) { + super(raf, parent); + } - /** - * Constructor with superbox. - * - * @param parent parent superbox of this box - */ - public PaletteBox (RandomAccessFile raf, BoxHolder parent) - { - super (raf, parent); + /** + * Reads the box, putting appropriate information in the RepInfo object. setModule, setBoxHeader, + * setRepInfo and setDataInputStream must be called before readBox is called. + * readBox must completely consume the box, so that the next byte to be read by the + * DataInputStream is the FF byte of the next Box. + */ + @Override + public boolean readBox() throws IOException { + if (!(_parentBox instanceof JP2HeaderBox)) { + _repInfo.setMessage(new ErrorMessage(MessageConstants.JPEG2000_HUL_26, _module.getFilePos())); + return false; } + initBytesRead(); + // _module.setPaletteSeen (true); + int len = (int) _boxHeader.getDataLength(); + long startNByte = _module.getFilePos(); + // Track how many bytes to skip + int ne = _module.readUnsignedShort(_dstrm); + // 2 bytes have been read + if (ne < 1 || ne > 1024) { + _repInfo.setMessage(new ErrorMessage(MessageConstants.JPEG2000_HUL_37, _module.getFilePos())); + _repInfo.setValid(false); // But keep going anyway + } + Property[] subProp = new Property[4]; + subProp[0] = new Property("Entries", PropertyType.INTEGER, new Integer(ne)); - /** Reads the box, putting appropriate information in - * the RepInfo object. setModule, setBoxHeader, - * setRepInfo and setDataInputStream must be called - * before readBox is called. - * readBox must completely consume the - * box, so that the next byte to be read by the - * DataInputStream is the FF byte of the next Box. - */ - @Override - public boolean readBox() throws IOException { - if (!(_parentBox instanceof JP2HeaderBox)) { - _repInfo.setMessage (new ErrorMessage - (MessageConstants.JPEG2000_HUL_26, - _module.getFilePos ())); - return false; - } - initBytesRead (); - //_module.setPaletteSeen (true); - int len = (int) _boxHeader.getDataLength (); - long startNByte = _module.getFilePos(); - // Track how many bytes to skip + int nc = ModuleBase.readUnsignedByte(_dstrm, _module); + subProp[1] = new Property("Components", PropertyType.INTEGER, new Integer(nc)); - int ne = _module.readUnsignedShort (_dstrm); - // 2 bytes have been read - if (ne < 1 || ne > 1024) { - _repInfo.setMessage (new ErrorMessage - (MessageConstants.JPEG2000_HUL_37, - _module.getFilePos())); - _repInfo.setValid (false); // But keep going anyway - } - Property[] subProp = new Property[4]; - subProp[0] = new Property ("Entries", PropertyType.INTEGER, - new Integer (ne)); - - int nc = ModuleBase.readUnsignedByte (_dstrm, _module); - subProp[1] = new Property ("Components", PropertyType.INTEGER, - new Integer (nc)); + // Each component can, in principle, have a different bit depth, + // and each can separately be signed or unsigned. + int[] bpc = new int[nc]; + boolean[] cmpsigned = new boolean[nc]; + for (int i = 0; i < nc; i++) { + int b = ModuleBase.readUnsignedByte(_dstrm, _module); + cmpsigned[i] = ((b & 0X80) != 0); + bpc[i] = (b & 0X7F) + 1; + } - // Each component can, in principle, have a different bit depth, - // and each can separately be signed or unsigned. - int[] bpc = new int [nc]; - boolean[] cmpsigned = new boolean [nc]; - for (int i = 0; i < nc; i++) { - int b = ModuleBase.readUnsignedByte (_dstrm, _module); - cmpsigned[i] = ((b & 0X80) != 0); - bpc[i] = (b & 0X7F) + 1; - } - - subProp[2] = new Property ("BitDepth", PropertyType.INTEGER, - PropertyArity.ARRAY, bpc); - - // Now the actual component value arrays. Skip this if - // ne is out of bounds. - if (ne > 1024 || ne < 1) { - subProp[3] = new Property ("Values", PropertyType.STRING, - "Invalid"); - } - else { - Property[] cprop = new Property[nc]; - for (int i = 0; i < nc; i++) { - int[] c = new int[ne]; - for (int j = 0; j < ne; j++) { - c[j] = ModuleBase.readUnsignedByte (_dstrm, _module); - } - cprop[i] = new Property ("Component", - PropertyType.INTEGER, - PropertyArity.ARRAY, - c); - } - subProp[3] = new Property ("Values", PropertyType.PROPERTY, - PropertyArity.ARRAY, - cprop); - } - Property palProp = new Property ("Palette", - PropertyType.PROPERTY, - PropertyArity.ARRAY, - subProp); - if (_parentBox instanceof CodestreamHeaderBox) { - Codestream cs = ((CodestreamHeaderBox) _parentBox).getCodestream (); - cs.setPaletteProperty (palProp); - } - else { - _module.addProperty (palProp); - } - // Skip any bytes we haven't read - _module.skipBytes (_dstrm, - (int) (len - (_module.getFilePos() - startNByte)), _module); + subProp[2] = new Property("BitDepth", PropertyType.INTEGER, PropertyArity.ARRAY, bpc); - finalizeBytesRead (); - return true; + // Now the actual component value arrays. Skip this if + // ne is out of bounds. + if (ne > 1024 || ne < 1) { + subProp[3] = new Property("Values", PropertyType.STRING, "Invalid"); + } else { + Property[] cprop = new Property[nc]; + for (int i = 0; i < nc; i++) { + int[] c = new int[ne]; + for (int j = 0; j < ne; j++) { + c[j] = ModuleBase.readUnsignedByte(_dstrm, _module); + } + cprop[i] = new Property("Component", PropertyType.INTEGER, PropertyArity.ARRAY, c); + } + subProp[3] = new Property("Values", PropertyType.PROPERTY, PropertyArity.ARRAY, cprop); } - - /** Returns the name of the Box. */ - @Override - protected String getSelfPropName () - { - return "Palette Box"; + Property palProp = new Property("Palette", PropertyType.PROPERTY, PropertyArity.ARRAY, subProp); + if (_parentBox instanceof CodestreamHeaderBox) { + Codestream cs = ((CodestreamHeaderBox) _parentBox).getCodestream(); + cs.setPaletteProperty(palProp); + } else { + _module.addProperty(palProp); } + // Skip any bytes we haven't read + _module.skipBytes(_dstrm, (int) (len - (_module.getFilePos() - startNByte)), _module); + + finalizeBytesRead(); + return true; + } + + /** Returns the name of the Box. */ + @Override + protected String getSelfPropName() { + return "Palette Box"; + } } diff --git a/jhove-modules/jpeg2000-hul/src/main/java/edu/harvard/hul/ois/jhove/module/jpeg2000/QCCMarkerSegment.java b/jhove-modules/jpeg2000-hul/src/main/java/edu/harvard/hul/ois/jhove/module/jpeg2000/QCCMarkerSegment.java index cde6ea773..90e4d1911 100644 --- a/jhove-modules/jpeg2000-hul/src/main/java/edu/harvard/hul/ois/jhove/module/jpeg2000/QCCMarkerSegment.java +++ b/jhove-modules/jpeg2000-hul/src/main/java/edu/harvard/hul/ois/jhove/module/jpeg2000/QCCMarkerSegment.java @@ -1,114 +1,93 @@ -/********************************************************************** - * Jhove - JSTOR/Harvard Object Validation Environment - * Copyright 2004 by JSTOR and the President and Fellows of Harvard College - **********************************************************************/ - +/** + * ******************************************************************** Jhove - JSTOR/Harvard Object + * Validation Environment Copyright 2004 by JSTOR and the President and Fellows of Harvard College + * ******************************************************************** + */ package edu.harvard.hul.ois.jhove.module.jpeg2000; +import edu.harvard.hul.ois.jhove.*; import java.io.*; import java.util.*; -import edu.harvard.hul.ois.jhove.*; /** - * Class for the QCC (Quantization component) marker segment. - * May occur in the main or the tile part header. In the - * main header it overrides the QCD for the specified - * component. In the tile part header it overrides the - * QCD for the component in the tile part. + * Class for the QCC (Quantization component) marker segment. May occur in the main or the tile part + * header. In the main header it overrides the QCD for the specified component. In the tile part + * header it overrides the QCD for the component in the tile part. * * @author Gary McGath - * */ public class QCCMarkerSegment extends MarkerSegment { - /** - * - */ - public QCCMarkerSegment() { - super(); + /** */ + public QCCMarkerSegment() { + super(); + } + + /** + * Process the marker segment. The DataInputStream will be at the point of having read the marker + * code. The process method must consume exactly the number of bytes remaining in the + * marker segment. + * + * @param bytesToEat The number of bytes that must be consumed. If it is 0 for a MarkerSegment, + * the number of bytes to consume is unknown. + * @return true if segment is well-formed, false otherwise. + */ + @Override + protected boolean process(int bytesToEat) throws IOException { + int compIdxBytes = nCompBytes(); + if (compIdxBytes == 0) { + // QCC found before SIZ + _repInfo.setMessage(new ErrorMessage(MessageConstants.JPEG2000_HUL_46)); + return false; + } + int compIdx; + int bytesEaten; + // size of Ccoc field depends on number of components + if (compIdxBytes < 257) { + compIdx = ModuleBase.readUnsignedByte(_dstream, _module); + bytesEaten = 1; + } else { + compIdx = _module.readUnsignedShort(_dstream); + bytesEaten = 2; } + int sqcc = ModuleBase.readUnsignedByte(_dstream, _module); + bytesEaten++; - /** Process the marker segment. The DataInputStream - * will be at the point of having read the marker code. The - * process method must consume exactly the number - * of bytes remaining in the marker segment. - * - * @param bytesToEat The number of bytes that must be consumed. - * If it is 0 for a MarkerSegment, the - * number of bytes to consume is unknown. - * - * @return true if segment is well-formed, - * false otherwise. - */ - @Override - protected boolean process(int bytesToEat) - throws IOException - { - int compIdxBytes = nCompBytes(); - if (compIdxBytes == 0) { - // QCC found before SIZ - _repInfo.setMessage (new ErrorMessage - (MessageConstants.JPEG2000_HUL_46)); - return false; - } - int compIdx; - int bytesEaten; - // size of Ccoc field depends on number of components - if (compIdxBytes < 257) { - compIdx = ModuleBase.readUnsignedByte (_dstream, _module); - bytesEaten = 1; - } - else{ - compIdx = _module.readUnsignedShort (_dstream); - bytesEaten = 2; - } - int sqcc = ModuleBase.readUnsignedByte (_dstream, _module); - bytesEaten++; - - int sqccLow = sqcc & 0X1F; - int nspqcc; - int spqcc[]; - switch (sqccLow) { - case 0: - // no quantization -- byte entries in spqcd - nspqcc = bytesToEat - bytesEaten; - spqcc = new int[nspqcc]; - for (int i = 0; i < nspqcc; i++) { - spqcc[i] = ModuleBase.readUnsignedByte (_dstream, _module); - } - break; - - case 1: - // scalar derived (just 2 bytes of value) - case 2: - // scalar expounded - nspqcc = (bytesToEat - bytesEaten) / 2; - spqcc = new int[nspqcc]; - for (int i = 0; i < nspqcc; i++) { - spqcc[i] = _module.readUnsignedShort (_dstream); - } - break; - default: - _repInfo.setMessage (new ErrorMessage - (MessageConstants.JPEG2000_HUL_47)); - return false; // reserved value + int sqccLow = sqcc & 0X1F; + int nspqcc; + int spqcc[]; + switch (sqccLow) { + case 0: + // no quantization -- byte entries in spqcd + nspqcc = bytesToEat - bytesEaten; + spqcc = new int[nspqcc]; + for (int i = 0; i < nspqcc; i++) { + spqcc[i] = ModuleBase.readUnsignedByte(_dstream, _module); } + break; - MainOrTile cs = getMainOrTile (); - List propList = new ArrayList (2); - propList.add (new Property ("QuantizationStyle", - PropertyType.INTEGER, - new Integer (sqcc))); - propList.add (new Property ("StepValue", - PropertyType.INTEGER, - PropertyArity.ARRAY, - spqcc)); - cs.setCompProperty (compIdx, - new Property ("QuantizationComponent", - PropertyType.PROPERTY, - PropertyArity.LIST, - propList)); - return true; + case 1: + // scalar derived (just 2 bytes of value) + case 2: + // scalar expounded + nspqcc = (bytesToEat - bytesEaten) / 2; + spqcc = new int[nspqcc]; + for (int i = 0; i < nspqcc; i++) { + spqcc[i] = _module.readUnsignedShort(_dstream); + } + break; + default: + _repInfo.setMessage(new ErrorMessage(MessageConstants.JPEG2000_HUL_47)); + return false; // reserved value } + MainOrTile cs = getMainOrTile(); + List propList = new ArrayList(2); + propList.add(new Property("QuantizationStyle", PropertyType.INTEGER, new Integer(sqcc))); + propList.add(new Property("StepValue", PropertyType.INTEGER, PropertyArity.ARRAY, spqcc)); + cs.setCompProperty( + compIdx, + new Property("QuantizationComponent", PropertyType.PROPERTY, PropertyArity.LIST, propList)); + return true; + } } diff --git a/jhove-modules/jpeg2000-hul/src/main/java/edu/harvard/hul/ois/jhove/module/jpeg2000/QCDMarkerSegment.java b/jhove-modules/jpeg2000-hul/src/main/java/edu/harvard/hul/ois/jhove/module/jpeg2000/QCDMarkerSegment.java index 8c1229314..62237ec88 100644 --- a/jhove-modules/jpeg2000-hul/src/main/java/edu/harvard/hul/ois/jhove/module/jpeg2000/QCDMarkerSegment.java +++ b/jhove-modules/jpeg2000-hul/src/main/java/edu/harvard/hul/ois/jhove/module/jpeg2000/QCDMarkerSegment.java @@ -1,8 +1,8 @@ -/********************************************************************** - * Jhove - JSTOR/Harvard Object Validation Environment - * Copyright 2004 by JSTOR and the President and Fellows of Harvard College - **********************************************************************/ - +/** + * ******************************************************************** Jhove - JSTOR/Harvard Object + * Validation Environment Copyright 2004 by JSTOR and the President and Fellows of Harvard College + * ******************************************************************** + */ package edu.harvard.hul.ois.jhove.module.jpeg2000; import edu.harvard.hul.ois.jhove.*; @@ -10,81 +10,65 @@ import java.util.*; /** - * Class for the QCD (Quantization default) marker segment. - * This comes either in the main header or - * after an SOT. + * Class for the QCD (Quantization default) marker segment. This comes either in the main header or + * after an SOT. * * @author Gary McGath - * */ public class QCDMarkerSegment extends MarkerSegment { - /** - * Constructor. - */ - public QCDMarkerSegment() { - super(); - } + /** Constructor. */ + public QCDMarkerSegment() { + super(); + } - /** Process the marker segment. The DataInputStream - * will be at the point of having read the marker code. The - * process method must consume exactly the number - * of bytes remaining in the marker segment. - * - * @param bytesToEat The number of bytes that must be consumed. - * If it is 0 for a MarkerSegment, the - * number of bytes to consume is unknown. - * - * @return true if segment is well-formed, - * false otherwise. - */ - @Override - protected boolean process(int bytesToEat) throws IOException { - int sqcd = ModuleBase.readUnsignedByte (_dstream, _module); - // What follows depends on the value of sqcd in a messy way - int sqcdLow = sqcd & 0X1F; - int nspqcd; - int spqcd[]; - switch (sqcdLow) { - case 0: - // no quantization -- byte entries in spqcd - nspqcd = bytesToEat - 1; - spqcd = new int[nspqcd]; - for (int i = 0; i < nspqcd; i++) { - spqcd[i] = ModuleBase.readUnsignedByte (_dstream, _module); - } - break; - - case 1: - // scalar derived (just 2 bytes of value) - case 2: - // scalar expounded - nspqcd = (bytesToEat - 1) / 2; - spqcd = new int[nspqcd]; - for (int i = 0; i < nspqcd; i++) { - spqcd[i] = _module.readUnsignedShort (_dstream); - } - break; - - default: - _repInfo.setMessage (new ErrorMessage - (MessageConstants.JPEG2000_HUL_48)); - return false; // reserved value + /** + * Process the marker segment. The DataInputStream will be at the point of having read the marker + * code. The process method must consume exactly the number of bytes remaining in the + * marker segment. + * + * @param bytesToEat The number of bytes that must be consumed. If it is 0 for a MarkerSegment, + * the number of bytes to consume is unknown. + * @return true if segment is well-formed, false otherwise. + */ + @Override + protected boolean process(int bytesToEat) throws IOException { + int sqcd = ModuleBase.readUnsignedByte(_dstream, _module); + // What follows depends on the value of sqcd in a messy way + int sqcdLow = sqcd & 0X1F; + int nspqcd; + int spqcd[]; + switch (sqcdLow) { + case 0: + // no quantization -- byte entries in spqcd + nspqcd = bytesToEat - 1; + spqcd = new int[nspqcd]; + for (int i = 0; i < nspqcd; i++) { + spqcd[i] = ModuleBase.readUnsignedByte(_dstream, _module); } - List propList = new ArrayList (2); - propList.add (new Property ("QuantizationStyle", - PropertyType.INTEGER, - new Integer (sqcd))); - propList.add (new Property ("StepValue", - PropertyType.INTEGER, - PropertyArity.ARRAY, - spqcd)); - MainOrTile cs = getMainOrTile (); - cs.setQCDProperty (new Property ("QuantizationDefault", - PropertyType.PROPERTY, - PropertyArity.LIST, - propList)); - return true; - } + break; + + case 1: + // scalar derived (just 2 bytes of value) + case 2: + // scalar expounded + nspqcd = (bytesToEat - 1) / 2; + spqcd = new int[nspqcd]; + for (int i = 0; i < nspqcd; i++) { + spqcd[i] = _module.readUnsignedShort(_dstream); + } + break; + default: + _repInfo.setMessage(new ErrorMessage(MessageConstants.JPEG2000_HUL_48)); + return false; // reserved value + } + List propList = new ArrayList(2); + propList.add(new Property("QuantizationStyle", PropertyType.INTEGER, new Integer(sqcd))); + propList.add(new Property("StepValue", PropertyType.INTEGER, PropertyArity.ARRAY, spqcd)); + MainOrTile cs = getMainOrTile(); + cs.setQCDProperty( + new Property("QuantizationDefault", PropertyType.PROPERTY, PropertyArity.LIST, propList)); + return true; + } } diff --git a/jhove-modules/jpeg2000-hul/src/main/java/edu/harvard/hul/ois/jhove/module/jpeg2000/RGNMarkerSegment.java b/jhove-modules/jpeg2000-hul/src/main/java/edu/harvard/hul/ois/jhove/module/jpeg2000/RGNMarkerSegment.java index 30c794fcb..b91fa3207 100644 --- a/jhove-modules/jpeg2000-hul/src/main/java/edu/harvard/hul/ois/jhove/module/jpeg2000/RGNMarkerSegment.java +++ b/jhove-modules/jpeg2000-hul/src/main/java/edu/harvard/hul/ois/jhove/module/jpeg2000/RGNMarkerSegment.java @@ -1,8 +1,8 @@ -/********************************************************************** - * Jhove - JSTOR/Harvard Object Validation Environment - * Copyright 2004 by JSTOR and the President and Fellows of Harvard College - **********************************************************************/ - +/** + * ******************************************************************** Jhove - JSTOR/Harvard Object + * Validation Environment Copyright 2004 by JSTOR and the President and Fellows of Harvard College + * ******************************************************************** + */ package edu.harvard.hul.ois.jhove.module.jpeg2000; import edu.harvard.hul.ois.jhove.*; @@ -10,67 +10,52 @@ import java.util.*; /** - * Class for the RGN (region of interest) marker segment. - * This comes either in the main header or - * after an SOT. + * Class for the RGN (region of interest) marker segment. This comes either in the main header or + * after an SOT. * * @author Gary McGath - * */ public class RGNMarkerSegment extends MarkerSegment { - /** - * - */ - public RGNMarkerSegment() { - super(); + /** */ + public RGNMarkerSegment() { + super(); + } + + /** + * Processes the marker segment. The DataInputStream will be at the point of having read the + * marker code. The process method must consume exactly the number of bytes remaining + * in the marker segment. + * + * @param bytesToEat The number of bytes that must be consumed. If it is 0 for a MarkerSegment, + * the number of bytes to consume is unknown. + */ + @Override + protected boolean process(int bytesToEat) throws IOException { + int compIdxBytes = nCompBytes(); + if (compIdxBytes == 0) { + // RGN found before SIZ + _repInfo.setMessage(new ErrorMessage(MessageConstants.JPEG2000_HUL_50)); + return false; } - - /** - * Processes the marker segment. The DataInputStream - * will be at the point of having read the marker code. The - * process method must consume exactly the number - * of bytes remaining in the marker segment. - * - * @param bytesToEat The number of bytes that must be consumed. - * If it is 0 for a MarkerSegment, the - * number of bytes to consume is unknown. - */ - @Override - protected boolean process(int bytesToEat) throws IOException { - int compIdxBytes = nCompBytes(); - if (compIdxBytes == 0) { - // RGN found before SIZ - _repInfo.setMessage (new ErrorMessage - (MessageConstants.JPEG2000_HUL_50)); - return false; - } - int compIdx; - // size of Ccoc field depends on number of components - if (compIdxBytes < 257) { - compIdx = ModuleBase.readUnsignedByte (_dstream, _module); - } - else{ - compIdx = _module.readUnsignedShort (_dstream); - } - int srgn = ModuleBase.readUnsignedByte (_dstream, _module); - int sprgn = ModuleBase.readUnsignedByte (_dstream, _module); - MainOrTile cs = getMainOrTile (); - - List propList = new ArrayList (2); - propList.add (new Property ("ROIStyle", - PropertyType.INTEGER, - new Integer (srgn))); - propList.add (new Property ("ROIParameter", - PropertyType.INTEGER, - new Integer (sprgn))); - cs.setCompProperty (compIdx, - new Property ("RegionOfInterest", - PropertyType.PROPERTY, - PropertyArity.LIST, - propList)); - - return true; + int compIdx; + // size of Ccoc field depends on number of components + if (compIdxBytes < 257) { + compIdx = ModuleBase.readUnsignedByte(_dstream, _module); + } else { + compIdx = _module.readUnsignedShort(_dstream); } - + int srgn = ModuleBase.readUnsignedByte(_dstream, _module); + int sprgn = ModuleBase.readUnsignedByte(_dstream, _module); + MainOrTile cs = getMainOrTile(); + + List propList = new ArrayList(2); + propList.add(new Property("ROIStyle", PropertyType.INTEGER, new Integer(srgn))); + propList.add(new Property("ROIParameter", PropertyType.INTEGER, new Integer(sprgn))); + cs.setCompProperty( + compIdx, + new Property("RegionOfInterest", PropertyType.PROPERTY, PropertyArity.LIST, propList)); + + return true; + } } diff --git a/jhove-modules/jpeg2000-hul/src/main/java/edu/harvard/hul/ois/jhove/module/jpeg2000/ROIBox.java b/jhove-modules/jpeg2000-hul/src/main/java/edu/harvard/hul/ois/jhove/module/jpeg2000/ROIBox.java index ca5bd2cd8..ae1ad60c9 100644 --- a/jhove-modules/jpeg2000-hul/src/main/java/edu/harvard/hul/ois/jhove/module/jpeg2000/ROIBox.java +++ b/jhove-modules/jpeg2000-hul/src/main/java/edu/harvard/hul/ois/jhove/module/jpeg2000/ROIBox.java @@ -1,8 +1,8 @@ -/********************************************************************** - * Jhove - JSTOR/Harvard Object Validation Environment - * Copyright 2004 by JSTOR and the President and Fellows of Harvard College - **********************************************************************/ - +/** + * ******************************************************************** Jhove - JSTOR/Harvard Object + * Validation Environment Copyright 2004 by JSTOR and the President and Fellows of Harvard College + * ******************************************************************** + */ package edu.harvard.hul.ois.jhove.module.jpeg2000; import edu.harvard.hul.ois.jhove.*; @@ -10,127 +10,98 @@ import java.util.*; /** - * ROI Description box (JPX). - * See ISO/IEC FCD15444-2: 2000, L.9.16 + * ROI Description box (JPX). See ISO/IEC FCD15444-2: 2000, L.9.16 * * @author Gary McGath - * */ public class ROIBox extends JP2Box { - private Property roiProp; + private Property roiProp; - /** - * Constructor with superbox. - * - * @param parent parent superbox of this box - */ - public ROIBox (RandomAccessFile raf, BoxHolder parent) - { - super (raf, parent); - } + /** + * Constructor with superbox. + * + * @param parent parent superbox of this box + */ + public ROIBox(RandomAccessFile raf, BoxHolder parent) { + super(raf, parent); + } - /** Reads the box, putting appropriate information in - * the RepInfo object. setModule, setBoxHeader, - * setRepInfo and setDataInputStream must be called - * before readBox is called. - * readBox must completely consume the - * box, so that the next byte to be read by the - * DataInputStream is the FF byte of the next Box. - */ - @Override - public boolean readBox() throws IOException { - initBytesRead (); - int nroi = ModuleBase.readUnsignedByte (_dstrm, _module); - - List propList = new ArrayList (nroi); - for (int i = 0; i < nroi; i++) { - List roiPropList = new ArrayList (7); - int incs = ModuleBase.readUnsignedByte (_dstrm, _module); - if (incs > 1) { - _repInfo.setMessage (new ErrorMessage - (MessageConstants.JPEG2000_HUL_51, _module.getFilePos ())); - _repInfo.setValid (false); - } - roiPropList.add (_module.addIntegerProperty("InCodestream", - incs, JP2Strings.inCodestreamStr)); - - int rtyp = ModuleBase.readUnsignedByte (_dstrm, _module); - if (rtyp > 1) { - _repInfo.setMessage (new ErrorMessage - (MessageConstants.JPEG2000_HUL_52, - _module.getFilePos ())); - _repInfo.setValid (false); - } - roiPropList.add (_module.addIntegerProperty("RegionType", - rtyp, JP2Strings.roiTypeStr)); + /** + * Reads the box, putting appropriate information in the RepInfo object. setModule, setBoxHeader, + * setRepInfo and setDataInputStream must be called before readBox is called. + * readBox must completely consume the box, so that the next byte to be read by the + * DataInputStream is the FF byte of the next Box. + */ + @Override + public boolean readBox() throws IOException { + initBytesRead(); + int nroi = ModuleBase.readUnsignedByte(_dstrm, _module); - int rcp = ModuleBase.readUnsignedByte (_dstrm, _module); - roiPropList.add (new Property ("CodingPriority", - PropertyType.INTEGER, - new Integer (rcp))); - - long lcx = _module.readUnsignedInt (_dstrm); - roiPropList.add (new Property ("HorizontalLocation", - PropertyType.LONG, - new Long (lcx))); - long lcy = _module.readUnsignedInt (_dstrm); - roiPropList.add (new Property ("HorizontalLocation", - PropertyType.LONG, - new Long (lcy))); - long wdt = _module.readUnsignedInt (_dstrm); - roiPropList.add (new Property ("Width", - PropertyType.LONG, - new Long (wdt))); - long hth = _module.readUnsignedInt (_dstrm); - roiPropList.add (new Property ("Height", - PropertyType.LONG, - new Long (hth))); + List propList = new ArrayList(nroi); + for (int i = 0; i < nroi; i++) { + List roiPropList = new ArrayList(7); + int incs = ModuleBase.readUnsignedByte(_dstrm, _module); + if (incs > 1) { + _repInfo.setMessage( + new ErrorMessage(MessageConstants.JPEG2000_HUL_51, _module.getFilePos())); + _repInfo.setValid(false); + } + roiPropList.add(_module.addIntegerProperty("InCodestream", incs, JP2Strings.inCodestreamStr)); - propList.add (new Property ("ROI", - PropertyType.PROPERTY, - PropertyArity.LIST, - roiPropList)); - } - roiProp = new Property ("ROIs", - PropertyType.PROPERTY, - PropertyArity.LIST, - propList); - // If the parent box is an Association box, the property - // will be put into the Association property, so there's no - // need to put it in two places. - if (!(_parentBox instanceof AssociationBox)) { - if (_parentBox instanceof CodestreamHeaderBox) { - Codestream cs = ((CodestreamHeaderBox) _parentBox).getCodestream (); - cs.setROIProperty (roiProp); - } - else { - _module.addProperty (roiProp); - } - } - finalizeBytesRead (); - return true; - } + int rtyp = ModuleBase.readUnsignedByte(_dstrm, _module); + if (rtyp > 1) { + _repInfo.setMessage( + new ErrorMessage(MessageConstants.JPEG2000_HUL_52, _module.getFilePos())); + _repInfo.setValid(false); + } + roiPropList.add(_module.addIntegerProperty("RegionType", rtyp, JP2Strings.roiTypeStr)); + + int rcp = ModuleBase.readUnsignedByte(_dstrm, _module); + roiPropList.add(new Property("CodingPriority", PropertyType.INTEGER, new Integer(rcp))); + + long lcx = _module.readUnsignedInt(_dstrm); + roiPropList.add(new Property("HorizontalLocation", PropertyType.LONG, new Long(lcx))); + long lcy = _module.readUnsignedInt(_dstrm); + roiPropList.add(new Property("HorizontalLocation", PropertyType.LONG, new Long(lcy))); + long wdt = _module.readUnsignedInt(_dstrm); + roiPropList.add(new Property("Width", PropertyType.LONG, new Long(wdt))); + long hth = _module.readUnsignedInt(_dstrm); + roiPropList.add(new Property("Height", PropertyType.LONG, new Long(hth))); - /** Returns the name of the Box. */ - @Override - protected String getSelfPropName () - { - return "ROI Description Box"; + propList.add(new Property("ROI", PropertyType.PROPERTY, PropertyArity.LIST, roiPropList)); } - - /** Returns a Property which describes the box. This is - * used as a subproperty of the Property returned by - * selfDescProperty. - */ - @Override - protected Property getSelfPropDesc (){ - if (roiProp != null) { - return new Property (DESCRIPTION_NAME, - PropertyType.PROPERTY, - roiProp); - } - return null; + roiProp = new Property("ROIs", PropertyType.PROPERTY, PropertyArity.LIST, propList); + // If the parent box is an Association box, the property + // will be put into the Association property, so there's no + // need to put it in two places. + if (!(_parentBox instanceof AssociationBox)) { + if (_parentBox instanceof CodestreamHeaderBox) { + Codestream cs = ((CodestreamHeaderBox) _parentBox).getCodestream(); + cs.setROIProperty(roiProp); + } else { + _module.addProperty(roiProp); + } } + finalizeBytesRead(); + return true; + } + /** Returns the name of the Box. */ + @Override + protected String getSelfPropName() { + return "ROI Description Box"; + } + + /** + * Returns a Property which describes the box. This is used as a subproperty of the Property + * returned by selfDescProperty. + */ + @Override + protected Property getSelfPropDesc() { + if (roiProp != null) { + return new Property(DESCRIPTION_NAME, PropertyType.PROPERTY, roiProp); + } + return null; + } } diff --git a/jhove-modules/jpeg2000-hul/src/main/java/edu/harvard/hul/ois/jhove/module/jpeg2000/ReaderRequirementsBox.java b/jhove-modules/jpeg2000-hul/src/main/java/edu/harvard/hul/ois/jhove/module/jpeg2000/ReaderRequirementsBox.java index 906c2399a..36457a013 100644 --- a/jhove-modules/jpeg2000-hul/src/main/java/edu/harvard/hul/ois/jhove/module/jpeg2000/ReaderRequirementsBox.java +++ b/jhove-modules/jpeg2000-hul/src/main/java/edu/harvard/hul/ois/jhove/module/jpeg2000/ReaderRequirementsBox.java @@ -1,105 +1,99 @@ -/********************************************************************** - * Jhove - JSTOR/Harvard Object Validation Environment - * Copyright 2004 by JSTOR and the President and Fellows of Harvard College - **********************************************************************/ - +/** + * ******************************************************************** Jhove - JSTOR/Harvard Object + * Validation Environment Copyright 2004 by JSTOR and the President and Fellows of Harvard College + * ******************************************************************** + */ package edu.harvard.hul.ois.jhove.module.jpeg2000; import edu.harvard.hul.ois.jhove.*; import java.io.*; /** - * Reader requirements box (JPX). - * See L.9.1 in ISO/IEC FCD15444-2:2000. + * Reader requirements box (JPX). See L.9.1 in ISO/IEC FCD15444-2:2000. * * @author Gary McGath - * */ public class ReaderRequirementsBox extends JP2Box { + /** + * Constructor with superbox. + * + * @param parent parent superbox of this box + */ + public ReaderRequirementsBox(RandomAccessFile raf, BoxHolder parent) { + super(raf, parent); + } - /** - * Constructor with superbox. - * - * @param parent parent superbox of this box - */ - public ReaderRequirementsBox(RandomAccessFile raf, BoxHolder parent) { - super (raf, parent); + /** + * Reads the box, putting appropriate information in the RepInfo object. setModule, setBoxHeader, + * setRepInfo and setDataInputStream must be called before readBox is called. + * readBox must completely consume the box, so that the next byte to be read by the + * DataInputStream is the FF byte of the next Box. + */ + @Override + public boolean readBox() throws IOException { + if (_parentBox != null) { + wrongBoxContext(); + // System.out.println ("READBOX parentBox != null"); + // System.out.flush (); + return false; } + initBytesRead(); + int len = (int) _boxHeader.getDataLength(); + + int maskLength = ModuleBase.readUnsignedByte(_dstrm, _module); + // maskLength specifies the size of FUAM and DCM, and may be + // 1, 2, 4 or 8 + switch (maskLength) { + case 1: + ModuleBase.readUnsignedByte(_dstrm, _module); + ModuleBase.readUnsignedByte(_dstrm, _module); + break; - /** Reads the box, putting appropriate information in - * the RepInfo object. setModule, setBoxHeader, - * setRepInfo and setDataInputStream must be called - * before readBox is called. - * readBox must completely consume the - * box, so that the next byte to be read by the - * DataInputStream is the FF byte of the next Box. - */ - @Override - public boolean readBox() throws IOException { - if (_parentBox != null) { - wrongBoxContext(); - // System.out.println ("READBOX parentBox != null"); - // System.out.flush (); - return false; - } - initBytesRead (); - int len = (int) _boxHeader.getDataLength (); - - int maskLength = ModuleBase.readUnsignedByte (_dstrm, _module); - // maskLength specifies the size of FUAM and DCM, and may be - // 1, 2, 4 or 8 - switch (maskLength) { - case 1: - ModuleBase.readUnsignedByte (_dstrm, _module); - ModuleBase.readUnsignedByte (_dstrm, _module); - break; - - case 2: - _module.readUnsignedShort (_dstrm); - _module.readUnsignedShort (_dstrm); - break; - - case 4: - _module.readUnsignedInt (_dstrm); - _module.readUnsignedInt (_dstrm); - break; - - case 8: - _module.readSignedLong (_dstrm); - _module.readSignedLong (_dstrm); - break; - - default: - _repInfo.setMessage (new ErrorMessage (MessageConstants.JPEG2000_HUL_49, _module.getFilePos ())); - _repInfo.setWellFormed (false); - // System.out.println ("READBOX default"); - // System.out.flush (); - return false; - } - - // nsf (number of standard flags) - int nsf = _module.readUnsignedShort (_dstrm); - for (int i = 0; i < nsf; i++) { - _module.readUnsignedShort (_dstrm); - } - // Table L-13, which gives legal values of the - // SF field, has a completely blank "value" column! - // Presumably SF stands for "science fiction." - - _module.skipBytes (_dstrm, - (int) (len - (_module.getFilePos () - startBytesRead)), _module); - finalizeBytesRead (); - _module.setRReqSeen (true); - // System.out.println ("READBOX seen=true"); + case 2: + _module.readUnsignedShort(_dstrm); + _module.readUnsignedShort(_dstrm); + break; + + case 4: + _module.readUnsignedInt(_dstrm); + _module.readUnsignedInt(_dstrm); + break; + + case 8: + _module.readSignedLong(_dstrm); + _module.readSignedLong(_dstrm); + break; + + default: + _repInfo.setMessage( + new ErrorMessage(MessageConstants.JPEG2000_HUL_49, _module.getFilePos())); + _repInfo.setWellFormed(false); + // System.out.println ("READBOX default"); // System.out.flush (); - return true; + return false; } - /** Returns the name of the Box. */ - @Override - protected String getSelfPropName () - { - return "Reader Requirements Box"; + // nsf (number of standard flags) + int nsf = _module.readUnsignedShort(_dstrm); + for (int i = 0; i < nsf; i++) { + _module.readUnsignedShort(_dstrm); } + // Table L-13, which gives legal values of the + // SF field, has a completely blank "value" column! + // Presumably SF stands for "science fiction." + + _module.skipBytes(_dstrm, (int) (len - (_module.getFilePos() - startBytesRead)), _module); + finalizeBytesRead(); + _module.setRReqSeen(true); + // System.out.println ("READBOX seen=true"); + // System.out.flush (); + return true; + } + + /** Returns the name of the Box. */ + @Override + protected String getSelfPropName() { + return "Reader Requirements Box"; + } } diff --git a/jhove-modules/jpeg2000-hul/src/main/java/edu/harvard/hul/ois/jhove/module/jpeg2000/ResolutionBox.java b/jhove-modules/jpeg2000-hul/src/main/java/edu/harvard/hul/ois/jhove/module/jpeg2000/ResolutionBox.java index 2cea2cf5e..cfe773a68 100644 --- a/jhove-modules/jpeg2000-hul/src/main/java/edu/harvard/hul/ois/jhove/module/jpeg2000/ResolutionBox.java +++ b/jhove-modules/jpeg2000-hul/src/main/java/edu/harvard/hul/ois/jhove/module/jpeg2000/ResolutionBox.java @@ -1,122 +1,103 @@ -/********************************************************************** - * Jhove - JSTOR/Harvard Object Validation Environment - * Copyright 2004 by JSTOR and the President and Fellows of Harvard College - **********************************************************************/ - +/** + * ******************************************************************** Jhove - JSTOR/Harvard Object + * Validation Environment Copyright 2004 by JSTOR and the President and Fellows of Harvard College + * ******************************************************************** + */ package edu.harvard.hul.ois.jhove.module.jpeg2000; -import java.io.*; -import java.util.ArrayList; -import java.util.List; - import edu.harvard.hul.ois.jhove.Property; import edu.harvard.hul.ois.jhove.PropertyArity; import edu.harvard.hul.ois.jhove.PropertyType; import edu.harvard.hul.ois.jhove.Rational; +import java.io.*; +import java.util.ArrayList; +import java.util.List; /** - * Resolution box. - * See I.5.3.7 in ISO/IEC 15444-1:2000 + * Resolution box. See I.5.3.7 in ISO/IEC 15444-1:2000 * * @author Gary McGath - * * @see CaptureResolutionBox * @see DDResolutionBox */ public class ResolutionBox extends JP2Box { + /** + * Constructor with superbox. + * + * @param parent parent superbox of this box + */ + public ResolutionBox(RandomAccessFile raf, BoxHolder parent) { + super(raf, parent); + // _parentBox = parent; + } - /** - * Constructor with superbox. - * - * @param parent parent superbox of this box - */ - public ResolutionBox (RandomAccessFile raf, BoxHolder parent) - { - super (raf, parent); - //_parentBox = parent; - } + /* (non-Javadoc) + * @see edu.harvard.hul.ois.jhove.module.jpeg2000.JP2Box#readBox() + */ + @Override + public boolean readBox() throws IOException { + initBytesRead(); + this.hasBoxes = true; + // NisoImageMetadata niso = _module.getDefaultNiso (); + // Later have to implement support for compositing layers, + // assigning an appropriate value to niso + JP2Box box; + while (hasNext()) { - /* (non-Javadoc) - * @see edu.harvard.hul.ois.jhove.module.jpeg2000.JP2Box#readBox() - */ - @Override - public boolean readBox() throws IOException { - initBytesRead (); - this.hasBoxes = true; - - //NisoImageMetadata niso = _module.getDefaultNiso (); - // Later have to implement support for compositing layers, - // assigning an appropriate value to niso - JP2Box box; - while (hasNext ()) { - - box = (JP2Box) next(); - if (box == null) { - break; - } - if (box instanceof CaptureResolutionBox) { - // Capture resolution box - if (!box.readBox ()) { - return false; - } - } - else if (box instanceof DDResolutionBox) { - // Default Display Resolution box - if (!box.readBox ()) { - return false; - } - } - else { - // Skip over other boxes. - box.skipBox (); - } + box = (JP2Box) next(); + if (box == null) { + break; + } + if (box instanceof CaptureResolutionBox) { + // Capture resolution box + if (!box.readBox()) { + return false; } - finalizeBytesRead (); - return true; - } - - public static Property makeResolutionProperty(String name, int num, int denom, int exp) { - final int NUMBER_FOR_RATIONAL = 3; - List resList = new ArrayList(NUMBER_FOR_RATIONAL); - resList.add (new Property ("Numerator", - PropertyType.INTEGER, new Integer (num))); - resList.add (new Property ("Denominator", - PropertyType.INTEGER, new Integer (denom))); - resList.add (new Property ("Exponent", - PropertyType.INTEGER, new Integer (exp))); - // The three properties for each direction are subsumed into - // a property. - Property res = new Property (name, - PropertyType.PROPERTY, - PropertyArity.LIST, - resList); - return res; + } else if (box instanceof DDResolutionBox) { + // Default Display Resolution box + if (!box.readBox()) { + return false; + } + } else { + // Skip over other boxes. + box.skipBox(); + } } + finalizeBytesRead(); + return true; + } - public static Rational convertToRational(int num, int denom, int exp) { - // We need to set resolution in NisoImageMetadata - // as a Rational. It seems unlikely that negative - // exponents will be used (signifying resolutions - // less than 1 dpi), so we figure the exponent into - // the numerator. Also, this resolution is in - // dots per meter, which isn't a NISO standard unit, - // so we multiply the denominator by 100 to give - // units per centimeter. - final int POWER = 10; - final int FROM_M_TO_CM = 100; - return new Rational - ((int) (num * Math.pow (POWER, exp)), - denom * FROM_M_TO_CM); - } + public static Property makeResolutionProperty(String name, int num, int denom, int exp) { + final int NUMBER_FOR_RATIONAL = 3; + List resList = new ArrayList(NUMBER_FOR_RATIONAL); + resList.add(new Property("Numerator", PropertyType.INTEGER, new Integer(num))); + resList.add(new Property("Denominator", PropertyType.INTEGER, new Integer(denom))); + resList.add(new Property("Exponent", PropertyType.INTEGER, new Integer(exp))); + // The three properties for each direction are subsumed into + // a property. + Property res = new Property(name, PropertyType.PROPERTY, PropertyArity.LIST, resList); + return res; + } - + public static Rational convertToRational(int num, int denom, int exp) { + // We need to set resolution in NisoImageMetadata + // as a Rational. It seems unlikely that negative + // exponents will be used (signifying resolutions + // less than 1 dpi), so we figure the exponent into + // the numerator. Also, this resolution is in + // dots per meter, which isn't a NISO standard unit, + // so we multiply the denominator by 100 to give + // units per centimeter. + final int POWER = 10; + final int FROM_M_TO_CM = 100; + return new Rational((int) (num * Math.pow(POWER, exp)), denom * FROM_M_TO_CM); + } - /** Returns the name of the Box. */ - @Override - protected String getSelfPropName () - { - return "Resolution Box"; - } + /** Returns the name of the Box. */ + @Override + protected String getSelfPropName() { + return "Resolution Box"; + } } diff --git a/jhove-modules/jpeg2000-hul/src/main/java/edu/harvard/hul/ois/jhove/module/jpeg2000/SIZMarkerSegment.java b/jhove-modules/jpeg2000-hul/src/main/java/edu/harvard/hul/ois/jhove/module/jpeg2000/SIZMarkerSegment.java index 5f3b39694..c425588ac 100644 --- a/jhove-modules/jpeg2000-hul/src/main/java/edu/harvard/hul/ois/jhove/module/jpeg2000/SIZMarkerSegment.java +++ b/jhove-modules/jpeg2000-hul/src/main/java/edu/harvard/hul/ois/jhove/module/jpeg2000/SIZMarkerSegment.java @@ -1,140 +1,101 @@ -/********************************************************************** - * Jhove - JSTOR/Harvard Object Validation Environment - * Copyright 2004 by JSTOR and the President and Fellows of Harvard College +/** + * ******************************************************************** Jhove - JSTOR/Harvard Object + * Validation Environment Copyright 2004 by JSTOR and the President and Fellows of Harvard College * - **********************************************************************/ - + *

******************************************************************** + */ package edu.harvard.hul.ois.jhove.module.jpeg2000; +import edu.harvard.hul.ois.jhove.*; import java.io.*; import java.util.*; -import edu.harvard.hul.ois.jhove.*; /** - * Class for the SIZ marker segment. This is a mandatory marker - * in the main header, and provides information about the - * uncompressed image such as the width and height of the - * reference grid, the width and height of the tiles, the number - * of components, component bit depth, and the separation of - * component samples with respect to the reference grid. - * - * @author Gary McGath + * Class for the SIZ marker segment. This is a mandatory marker in the main header, and provides + * information about the uncompressed image such as the width and height of the reference grid, the + * width and height of the tiles, the number of components, component bit depth, and the separation + * of component samples with respect to the reference grid. * + * @author Gary McGath */ public class SIZMarkerSegment extends MarkerSegment { - /** - * - */ - public SIZMarkerSegment() { - super(); - } + /** */ + public SIZMarkerSegment() { + super(); + } - /** Process the marker segment. The DataInputStream - * will be at the point of having read the marker code. The - * process method must consume exactly the number - * of bytes remaining in the marker segment. - * - * @param bytesToEat The number of bytes that must be consumed. - * If it is 0 for a MarkerSegment, the - * number of bytes to consume is unknown. - */ - @Override - protected boolean process (int bytesToEat) throws IOException - { - int rsiz = _module.readUnsignedShort (_dstream); - // rsiz = capabilities needed to decode - int xsiz = (int) _module.readUnsignedInt (_dstream); - // width of reference grid - int ysiz = (int) _module.readUnsignedInt (_dstream); - // height of reference grid - int xosiz = (int) _module.readUnsignedInt (_dstream); - // horizontal offset to left side of image area - int yosiz = (int) _module.readUnsignedInt (_dstream); - // vertical offset to top of image area - int xtsiz = (int) _module.readUnsignedInt (_dstream); - // width of one reference tile - int ytsiz = (int) _module.readUnsignedInt (_dstream); - // height of one reference tile - int xtosiz = (int) _module.readUnsignedInt (_dstream); - // horizontal offset to left side of first tile - int ytosiz = (int) _module.readUnsignedInt (_dstream); - // vertical offset to top of first tile - int csiz = _module.readUnsignedShort (_dstream); - // number of components - _cs.setNumComponents (csiz); - int ssiz[] = new int [csiz]; - // precision and sign of samples - for (int i = 0; i < csiz; i++) { - ssiz[i] = ModuleBase.readUnsignedByte (_dstream, _module); - } - // number of bits per component - int xrsiz[] = new int [csiz]; - // precision and sign of samples - for (int i = 0; i < csiz; i++) { - xrsiz[i] = ModuleBase.readUnsignedByte (_dstream, _module); - } - // horizontal sample separation + /** + * Process the marker segment. The DataInputStream will be at the point of having read the marker + * code. The process method must consume exactly the number of bytes remaining in the + * marker segment. + * + * @param bytesToEat The number of bytes that must be consumed. If it is 0 for a MarkerSegment, + * the number of bytes to consume is unknown. + */ + @Override + protected boolean process(int bytesToEat) throws IOException { + int rsiz = _module.readUnsignedShort(_dstream); + // rsiz = capabilities needed to decode + int xsiz = (int) _module.readUnsignedInt(_dstream); + // width of reference grid + int ysiz = (int) _module.readUnsignedInt(_dstream); + // height of reference grid + int xosiz = (int) _module.readUnsignedInt(_dstream); + // horizontal offset to left side of image area + int yosiz = (int) _module.readUnsignedInt(_dstream); + // vertical offset to top of image area + int xtsiz = (int) _module.readUnsignedInt(_dstream); + // width of one reference tile + int ytsiz = (int) _module.readUnsignedInt(_dstream); + // height of one reference tile + int xtosiz = (int) _module.readUnsignedInt(_dstream); + // horizontal offset to left side of first tile + int ytosiz = (int) _module.readUnsignedInt(_dstream); + // vertical offset to top of first tile + int csiz = _module.readUnsignedShort(_dstream); + // number of components + _cs.setNumComponents(csiz); + int ssiz[] = new int[csiz]; + // precision and sign of samples + for (int i = 0; i < csiz; i++) { + ssiz[i] = ModuleBase.readUnsignedByte(_dstream, _module); + } + // number of bits per component + int xrsiz[] = new int[csiz]; + // precision and sign of samples + for (int i = 0; i < csiz; i++) { + xrsiz[i] = ModuleBase.readUnsignedByte(_dstream, _module); + } + // horizontal sample separation - int yrsiz[] = new int [csiz]; - for (int i = 0; i < csiz; i++) { - yrsiz[i] = ModuleBase.readUnsignedByte (_dstream, _module); - } - // vertical sample separation - - - // For now, just assemble the info into a SIZ property and - // hand it to the Codestream. - List plist = new ArrayList (13); - plist.add (new Property ("Capabilities", - PropertyType.INTEGER, - new Integer (rsiz))); - plist.add (new Property ("XSize", - PropertyType.INTEGER, - new Integer (xsiz))); - plist.add (new Property ("YSize", - PropertyType.INTEGER, - new Integer (ysiz))); - plist.add (new Property ("XOSize", - PropertyType.INTEGER, - new Integer (xosiz))); - plist.add (new Property ("YOSize", - PropertyType.INTEGER, - new Integer (yosiz))); - plist.add (new Property ("XTSize", - PropertyType.INTEGER, - new Integer (xtsiz))); - plist.add (new Property ("YTSize", - PropertyType.INTEGER, - new Integer (ytsiz))); - plist.add (new Property ("XTOSize", - PropertyType.INTEGER, - new Integer (xtosiz))); - plist.add (new Property ("YTOSize", - PropertyType.INTEGER, - new Integer (ytosiz))); - plist.add (new Property ("CSize", - PropertyType.INTEGER, - new Integer (csiz))); - plist.add (new Property ("SSize", - PropertyType.INTEGER, - PropertyArity.ARRAY, - ssiz)); - plist.add (new Property ("XRSize", - PropertyType.INTEGER, - PropertyArity.ARRAY, - xrsiz)); - plist.add (new Property ("YRSize", - PropertyType.INTEGER, - PropertyArity.ARRAY, - yrsiz)); - _cs.setSIZProperty(new Property ("ImageAndTileSize", - PropertyType.PROPERTY, - PropertyArity.LIST, - plist)); - - NisoImageMetadata niso = _module.getCurrentNiso (); - niso.setJp2Tiles("" + xtsiz + "x" + ytsiz); - return true; + int yrsiz[] = new int[csiz]; + for (int i = 0; i < csiz; i++) { + yrsiz[i] = ModuleBase.readUnsignedByte(_dstream, _module); } + // vertical sample separation + + // For now, just assemble the info into a SIZ property and + // hand it to the Codestream. + List plist = new ArrayList(13); + plist.add(new Property("Capabilities", PropertyType.INTEGER, new Integer(rsiz))); + plist.add(new Property("XSize", PropertyType.INTEGER, new Integer(xsiz))); + plist.add(new Property("YSize", PropertyType.INTEGER, new Integer(ysiz))); + plist.add(new Property("XOSize", PropertyType.INTEGER, new Integer(xosiz))); + plist.add(new Property("YOSize", PropertyType.INTEGER, new Integer(yosiz))); + plist.add(new Property("XTSize", PropertyType.INTEGER, new Integer(xtsiz))); + plist.add(new Property("YTSize", PropertyType.INTEGER, new Integer(ytsiz))); + plist.add(new Property("XTOSize", PropertyType.INTEGER, new Integer(xtosiz))); + plist.add(new Property("YTOSize", PropertyType.INTEGER, new Integer(ytosiz))); + plist.add(new Property("CSize", PropertyType.INTEGER, new Integer(csiz))); + plist.add(new Property("SSize", PropertyType.INTEGER, PropertyArity.ARRAY, ssiz)); + plist.add(new Property("XRSize", PropertyType.INTEGER, PropertyArity.ARRAY, xrsiz)); + plist.add(new Property("YRSize", PropertyType.INTEGER, PropertyArity.ARRAY, yrsiz)); + _cs.setSIZProperty( + new Property("ImageAndTileSize", PropertyType.PROPERTY, PropertyArity.LIST, plist)); + + NisoImageMetadata niso = _module.getCurrentNiso(); + niso.setJp2Tiles("" + xtsiz + "x" + ytsiz); + return true; + } } diff --git a/jhove-modules/jpeg2000-hul/src/main/java/edu/harvard/hul/ois/jhove/module/jpeg2000/SOTMarkerSegment.java b/jhove-modules/jpeg2000-hul/src/main/java/edu/harvard/hul/ois/jhove/module/jpeg2000/SOTMarkerSegment.java index 3e36dbdb9..5b7fffe6d 100644 --- a/jhove-modules/jpeg2000-hul/src/main/java/edu/harvard/hul/ois/jhove/module/jpeg2000/SOTMarkerSegment.java +++ b/jhove-modules/jpeg2000-hul/src/main/java/edu/harvard/hul/ois/jhove/module/jpeg2000/SOTMarkerSegment.java @@ -1,64 +1,52 @@ -/********************************************************************** - * Jhove - JSTOR/Harvard Object Validation Environment - * Copyright 2004 by JSTOR and the President and Fellows of Harvard College +/** + * ******************************************************************** Jhove - JSTOR/Harvard Object + * Validation Environment Copyright 2004 by JSTOR and the President and Fellows of Harvard College * - **********************************************************************/ - + *

******************************************************************** + */ package edu.harvard.hul.ois.jhove.module.jpeg2000; -import java.io.*; import edu.harvard.hul.ois.jhove.*; -//import edu.harvard.hul.ois.jhove.module.Jpeg2000Module; +import java.io.*; + +// import edu.harvard.hul.ois.jhove.module.Jpeg2000Module; /** * Class for the SOT (start of tile-part) marker segment. - * - * @author Gary McGath * + * @author Gary McGath */ public class SOTMarkerSegment extends MarkerSegment { - /** - * Constructor - * - */ - public SOTMarkerSegment() - { - } - - - /** Process the marker segment. The DataInputStream - * will be at the point of having read the marker code. The - * process method must consume exactly the number - * of bytes remaining in the marker segment. - * - * @param bytesToEat The number of bytes that must be consumed. - * If it is 0 for a MarkerSegment, the - * number of bytes to consume is unknown. - */ - @Override - protected boolean process (int bytesToEat) throws IOException - { - int tileIndex = _module.readUnsignedShort (_dstream); - long tileLeft = _module.readUnsignedInt (_dstream); - _ccs.setTileLeft (tileLeft); - int tilePartIndex = - ModuleBase.readUnsignedByte (_dstream, _module); - // Skip tile part byte - ModuleBase.readUnsignedByte (_dstream, _module); - - Tile tile = _ccs.getTile (tileIndex); - _ccs.setCurTile (tile); - TilePart tp = new TilePart (tile, tilePartIndex); - tile.addTilePart (tp); - tp.setLength (tileLeft); - // Shouldn't be anything left, but... - if (bytesToEat > 8) { - _module.skipBytes (_dstream, bytesToEat - 8, _module); - } - return true; + /** Constructor */ + public SOTMarkerSegment() {} + + /** + * Process the marker segment. The DataInputStream will be at the point of having read the marker + * code. The process method must consume exactly the number of bytes remaining in the + * marker segment. + * + * @param bytesToEat The number of bytes that must be consumed. If it is 0 for a MarkerSegment, + * the number of bytes to consume is unknown. + */ + @Override + protected boolean process(int bytesToEat) throws IOException { + int tileIndex = _module.readUnsignedShort(_dstream); + long tileLeft = _module.readUnsignedInt(_dstream); + _ccs.setTileLeft(tileLeft); + int tilePartIndex = ModuleBase.readUnsignedByte(_dstream, _module); + // Skip tile part byte + ModuleBase.readUnsignedByte(_dstream, _module); + + Tile tile = _ccs.getTile(tileIndex); + _ccs.setCurTile(tile); + TilePart tp = new TilePart(tile, tilePartIndex); + tile.addTilePart(tp); + tp.setLength(tileLeft); + // Shouldn't be anything left, but... + if (bytesToEat > 8) { + _module.skipBytes(_dstream, bytesToEat - 8, _module); } - - - + return true; + } } diff --git a/jhove-modules/jpeg2000-hul/src/main/java/edu/harvard/hul/ois/jhove/module/jpeg2000/TLMMarkerSegment.java b/jhove-modules/jpeg2000-hul/src/main/java/edu/harvard/hul/ois/jhove/module/jpeg2000/TLMMarkerSegment.java index 404c4b743..2d2217c4b 100644 --- a/jhove-modules/jpeg2000-hul/src/main/java/edu/harvard/hul/ois/jhove/module/jpeg2000/TLMMarkerSegment.java +++ b/jhove-modules/jpeg2000-hul/src/main/java/edu/harvard/hul/ois/jhove/module/jpeg2000/TLMMarkerSegment.java @@ -1,105 +1,91 @@ -/********************************************************************** - * Jhove - JSTOR/Harvard Object Validation Environment - * Copyright 2004 by JSTOR and the President and Fellows of Harvard College +/** + * ******************************************************************** Jhove - JSTOR/Harvard Object + * Validation Environment Copyright 2004 by JSTOR and the President and Fellows of Harvard College * - **********************************************************************/ - + *

******************************************************************** + */ package edu.harvard.hul.ois.jhove.module.jpeg2000; +import edu.harvard.hul.ois.jhove.*; import java.io.*; import java.util.*; -import edu.harvard.hul.ois.jhove.*; /** - * Class for the TLM (tile length) marker segment. - * This may occur only in the main header. + * Class for the TLM (tile length) marker segment. This may occur only in the main header. * * @author Gary McGath - * */ public class TLMMarkerSegment extends MarkerSegment { - /** - * - */ - public TLMMarkerSegment() { - super(); - } + /** */ + public TLMMarkerSegment() { + super(); + } - /** - * Processes the marker segment.The DataInputStream - will be at the point of having read the marker code.The - process method must consume exactly the number - of bytes remaining in the marker segment. - * - * @param bytesToEat The number of bytes that must be consumed. - * If it is 0 for a MarkerSegment, the - * number of bytes to consume is unknown. - * @return boolean process - * @throws IOException - */ - @Override - protected boolean process(int bytesToEat) throws IOException { - // Skip initial unsigned byte - ModuleBase.readUnsignedByte (_dstream, _module); - int stlm = ModuleBase.readUnsignedByte (_dstream, _module); - int st = (stlm & 0X30) >> 4; - int sp = (stlm & 0X40) >> 6; + /** + * Processes the marker segment.The DataInputStream will be at the point of having read the marker + * code.The process method must consume exactly the number of bytes remaining in the + * marker segment. + * + * @param bytesToEat The number of bytes that must be consumed. If it is 0 for a MarkerSegment, + * the number of bytes to consume is unknown. + * @return boolean process + * @throws IOException + */ + @Override + protected boolean process(int bytesToEat) throws IOException { + // Skip initial unsigned byte + ModuleBase.readUnsignedByte(_dstream, _module); + int stlm = ModuleBase.readUnsignedByte(_dstream, _module); + int st = (stlm & 0X30) >> 4; + int sp = (stlm & 0X40) >> 6; - int partLength = (sp == 1) ? 4 : 2; - switch (st) { - // case 0: add nothing - case 1: - partLength += 1; - break; - case 2: - partLength += 2; - break; - case 3: - _repInfo.setMessage (new ErrorMessage - (MessageConstants.JPEG2000_HUL_55)); - return false; // invalid st value - default: - break; - } + int partLength = (sp == 1) ? 4 : 2; + switch (st) { + // case 0: add nothing + case 1: + partLength += 1; + break; + case 2: + partLength += 2; + break; + case 3: + _repInfo.setMessage(new ErrorMessage(MessageConstants.JPEG2000_HUL_55)); + return false; // invalid st value + default: + break; + } - int nParts = (bytesToEat - 2) / partLength; - // Make sure it's an even multiple - if (nParts * partLength != bytesToEat - 2) { - return false; - } - if (_ccs.getCurTile () != null) { - return false; // not permitted in a tile - } - for (int i = 0; i < nParts; i++) { - List tpList = new ArrayList<> (2); - // The TileIndex property is given only if st != 0 - if (st != 0) { - int ttlm; - if (st == 1) { - ttlm = ModuleBase.readUnsignedByte (_dstream, _module); - } - else { - ttlm = _module.readUnsignedShort (_dstream); - } - tpList.add (new Property ("Index", - PropertyType.INTEGER, ttlm)); - } - int length; - if (sp == 1) { - length = (int) _module.readUnsignedInt (_dstream); - } - else { - length = _module.readUnsignedShort (_dstream); - } - tpList.add (new Property ("Length", - PropertyType.INTEGER, length)); - _cs.addTileLength (new Property ("TilePartLength", - PropertyType.PROPERTY, - PropertyArity.LIST, - tpList)); + int nParts = (bytesToEat - 2) / partLength; + // Make sure it's an even multiple + if (nParts * partLength != bytesToEat - 2) { + return false; + } + if (_ccs.getCurTile() != null) { + return false; // not permitted in a tile + } + for (int i = 0; i < nParts; i++) { + List tpList = new ArrayList<>(2); + // The TileIndex property is given only if st != 0 + if (st != 0) { + int ttlm; + if (st == 1) { + ttlm = ModuleBase.readUnsignedByte(_dstream, _module); + } else { + ttlm = _module.readUnsignedShort(_dstream); } - return true; + tpList.add(new Property("Index", PropertyType.INTEGER, ttlm)); + } + int length; + if (sp == 1) { + length = (int) _module.readUnsignedInt(_dstream); + } else { + length = _module.readUnsignedShort(_dstream); + } + tpList.add(new Property("Length", PropertyType.INTEGER, length)); + _cs.addTileLength( + new Property("TilePartLength", PropertyType.PROPERTY, PropertyArity.LIST, tpList)); } - + return true; + } } diff --git a/jhove-modules/jpeg2000-hul/src/main/java/edu/harvard/hul/ois/jhove/module/jpeg2000/Tile.java b/jhove-modules/jpeg2000-hul/src/main/java/edu/harvard/hul/ois/jhove/module/jpeg2000/Tile.java index 4673847c6..5cd973b41 100644 --- a/jhove-modules/jpeg2000-hul/src/main/java/edu/harvard/hul/ois/jhove/module/jpeg2000/Tile.java +++ b/jhove-modules/jpeg2000-hul/src/main/java/edu/harvard/hul/ois/jhove/module/jpeg2000/Tile.java @@ -1,8 +1,8 @@ -/********************************************************************** - * Jhove - JSTOR/Harvard Object Validation Environment - * Copyright 2004 by JSTOR and the President and Fellows of Harvard College - **********************************************************************/ - +/** + * ******************************************************************** Jhove - JSTOR/Harvard Object + * Validation Environment Copyright 2004 by JSTOR and the President and Fellows of Harvard College + * ******************************************************************** + */ package edu.harvard.hul.ois.jhove.module.jpeg2000; import edu.harvard.hul.ois.jhove.*; @@ -12,105 +12,80 @@ * Encapsulation of a JPEG 2000 tile in a codestream. * * @author Gary McGath - * */ public class Tile extends MainOrTile { + private List _tileParts; - private List _tileParts; + /* List of lengths (Long objects) found in PPT code segments */ + private List _pptLengthList; - /* List of lengths (Long objects) found in PPT code segments */ - private List _pptLengthList; + /** Constructor. */ + public Tile() { + _tileParts = new LinkedList(); + } - /** - * Constructor. - */ - public Tile () - { - - _tileParts = new LinkedList (); - } - - - /** Adds a TilePart to the List of TileParts. */ - public void addTilePart (TilePart tp) - { - _tileParts.add (tp); - } + /** Adds a TilePart to the List of TileParts. */ + public void addTilePart(TilePart tp) { + _tileParts.add(tp); + } - /** returns the List of TileParts.*/ - public List getTileParts () - { - return _tileParts; - } + /** returns the List of TileParts. */ + public List getTileParts() { + return _tileParts; + } - /** Adds a PPM tilepart header length to the list of lengths */ - public void addPPTLength (long len) - { - _pptLengthList.add (new Long (len)); - } + /** Adds a PPM tilepart header length to the list of lengths */ + public void addPPTLength(long len) { + _pptLengthList.add(new Long(len)); + } - /** Returns a Property describing the tile. - * The name of the Property is "Tile". */ - public Property makeProperty () - { - List propList = new LinkedList (); - if (!_tileParts.isEmpty ()) { - ListIterator tpiter = _tileParts.listIterator (); - while (tpiter.hasNext ()) { - TilePart tp = (TilePart) tpiter.next (); - propList.add (tp.makeProperty ()); - } - } - if (_components != null) { - // It's possible only some components have overriding - // properties. Go through the array and set a stub - // component for any that don't. - for (int i = 0; i < _components.length; i++) { - if (_components[i] == null) { - _components[i] = new Property ("Component", - PropertyType.PROPERTY, - PropertyArity.LIST, - new LinkedList ()); - } - } - propList.add (new Property ("Components", - PropertyType.PROPERTY, - PropertyArity.ARRAY, - _components)); - } - if (_codProperty != null) { - propList.add (_codProperty); - } - if (_qcdProperty != null) { - propList.add (_qcdProperty); - } - if (_pocProperty != null) { - propList.add (_pocProperty); - } - if (_packetLengthList != null && !_packetLengthList.isEmpty ()) { - propList.add (new Property ("PacketLengths", - PropertyType.LONG, - PropertyArity.LIST, - _packetLengthList)); - } - if (_pptLengthList != null && _pptLengthList.isEmpty ()) { - propList.add (new Property ("PackedPacketHeaderLengths", - PropertyType.LONG, - PropertyArity.LIST, - _pptLengthList)); - } - if (!_comments.isEmpty ()) { - propList.add (new Property ("Comments", - PropertyType.PROPERTY, - PropertyArity.LIST, - _comments)); + /** Returns a Property describing the tile. The name of the Property is "Tile". */ + public Property makeProperty() { + List propList = new LinkedList(); + if (!_tileParts.isEmpty()) { + ListIterator tpiter = _tileParts.listIterator(); + while (tpiter.hasNext()) { + TilePart tp = (TilePart) tpiter.next(); + propList.add(tp.makeProperty()); + } + } + if (_components != null) { + // It's possible only some components have overriding + // properties. Go through the array and set a stub + // component for any that don't. + for (int i = 0; i < _components.length; i++) { + if (_components[i] == null) { + _components[i] = + new Property( + "Component", PropertyType.PROPERTY, PropertyArity.LIST, new LinkedList()); } - return new Property ("Tile", - PropertyType.PROPERTY, - PropertyArity.LIST, - propList); + } + propList.add( + new Property("Components", PropertyType.PROPERTY, PropertyArity.ARRAY, _components)); } - + if (_codProperty != null) { + propList.add(_codProperty); + } + if (_qcdProperty != null) { + propList.add(_qcdProperty); + } + if (_pocProperty != null) { + propList.add(_pocProperty); + } + if (_packetLengthList != null && !_packetLengthList.isEmpty()) { + propList.add( + new Property("PacketLengths", PropertyType.LONG, PropertyArity.LIST, _packetLengthList)); + } + if (_pptLengthList != null && _pptLengthList.isEmpty()) { + propList.add( + new Property( + "PackedPacketHeaderLengths", PropertyType.LONG, PropertyArity.LIST, _pptLengthList)); + } + if (!_comments.isEmpty()) { + propList.add(new Property("Comments", PropertyType.PROPERTY, PropertyArity.LIST, _comments)); + } + return new Property("Tile", PropertyType.PROPERTY, PropertyArity.LIST, propList); + } } diff --git a/jhove-modules/jpeg2000-hul/src/main/java/edu/harvard/hul/ois/jhove/module/jpeg2000/TilePart.java b/jhove-modules/jpeg2000-hul/src/main/java/edu/harvard/hul/ois/jhove/module/jpeg2000/TilePart.java index 9b41c0011..191851c33 100644 --- a/jhove-modules/jpeg2000-hul/src/main/java/edu/harvard/hul/ois/jhove/module/jpeg2000/TilePart.java +++ b/jhove-modules/jpeg2000-hul/src/main/java/edu/harvard/hul/ois/jhove/module/jpeg2000/TilePart.java @@ -1,8 +1,8 @@ -/********************************************************************** - * Jhove - JSTOR/Harvard Object Validation Environment - * Copyright 2004 by JSTOR and the President and Fellows of Harvard College - **********************************************************************/ - +/** + * ******************************************************************** Jhove - JSTOR/Harvard Object + * Validation Environment Copyright 2004 by JSTOR and the President and Fellows of Harvard College + * ******************************************************************** + */ package edu.harvard.hul.ois.jhove.module.jpeg2000; import edu.harvard.hul.ois.jhove.*; @@ -12,51 +12,36 @@ * Encapsulation of a tile-part in a JPEG 2000 codestream. * * @author Gary McGath - * */ public class TilePart { - private Tile _tile; - private int _index; - private long _length; - - /** - * Constructor. - * - * @param tile The Tile of which this is a part - * @param index The index of this tile part - */ - public TilePart (Tile tile, int index) - { - _tile = tile; - _index = index; - } - - - /** Sets the length field. This must be called before - * calling makeProperty. */ - public void setLength (long len) - { - _length = len; - } - - - /** Returns a Property based on the TilePart. - * The Property is named "TilePart". */ - public Property makeProperty () - { - Property indexProp = new Property ("Index", - PropertyType.INTEGER, - new Integer (_index)); - Property lengthProp = new Property ("Length", - PropertyType.LONG, - new Long (_length)); - List propList = new ArrayList (2); - propList.add (indexProp); - propList.add (lengthProp); - return new Property ("TilePart", - PropertyType.PROPERTY, - PropertyArity.LIST, - propList); - } + private Tile _tile; + private int _index; + private long _length; + + /** + * Constructor. + * + * @param tile The Tile of which this is a part + * @param index The index of this tile part + */ + public TilePart(Tile tile, int index) { + _tile = tile; + _index = index; + } + + /** Sets the length field. This must be called before calling makeProperty. */ + public void setLength(long len) { + _length = len; + } + + /** Returns a Property based on the TilePart. The Property is named "TilePart". */ + public Property makeProperty() { + Property indexProp = new Property("Index", PropertyType.INTEGER, new Integer(_index)); + Property lengthProp = new Property("Length", PropertyType.LONG, new Long(_length)); + List propList = new ArrayList(2); + propList.add(indexProp); + propList.add(lengthProp); + return new Property("TilePart", PropertyType.PROPERTY, PropertyArity.LIST, propList); + } } diff --git a/jhove-modules/jpeg2000-hul/src/main/java/edu/harvard/hul/ois/jhove/module/jpeg2000/TopLevelBoxHolder.java b/jhove-modules/jpeg2000-hul/src/main/java/edu/harvard/hul/ois/jhove/module/jpeg2000/TopLevelBoxHolder.java index a6f706e05..b05d76e66 100644 --- a/jhove-modules/jpeg2000-hul/src/main/java/edu/harvard/hul/ois/jhove/module/jpeg2000/TopLevelBoxHolder.java +++ b/jhove-modules/jpeg2000-hul/src/main/java/edu/harvard/hul/ois/jhove/module/jpeg2000/TopLevelBoxHolder.java @@ -1,8 +1,8 @@ -/********************************************************************** - * Jhove - JSTOR/Harvard Object Validation Environment - * Copyright 2004 by JSTOR and the President and Fellows of Harvard College - **********************************************************************/ - +/** + * ******************************************************************** Jhove - JSTOR/Harvard Object + * Validation Environment Copyright 2004 by JSTOR and the President and Fellows of Harvard College + * ******************************************************************** + */ package edu.harvard.hul.ois.jhove.module.jpeg2000; import edu.harvard.hul.ois.jhove.RepInfo; @@ -10,45 +10,34 @@ import java.io.*; /** - * A subclass of BoxHolder specifically for getting top-level - * boxes in a JPEG 2000 file. - * - * - * @author Gary McGath + * A subclass of BoxHolder specifically for getting top-level boxes in a JPEG 2000 file. * + * @author Gary McGath */ public class TopLevelBoxHolder extends BoxHolder { - private boolean eof; - - /** - * @param raf - */ - public TopLevelBoxHolder(Jpeg2000Module module, - RandomAccessFile raf, - RepInfo info, - DataInputStream dstream) - { - super(raf); - _module = module; - _dstrm = dstream; - _repInfo = info; - eof = false; - hasBoxes = true; - bytesLeft = Long.MAX_VALUE; - } + private boolean eof; - /** Returns a name for use in messages. */ - @Override - protected String getSelfPropName () - { - return "Top Level"; - } + /** @param raf */ + public TopLevelBoxHolder( + Jpeg2000Module module, RandomAccessFile raf, RepInfo info, DataInputStream dstream) { + super(raf); + _module = module; + _dstrm = dstream; + _repInfo = info; + eof = false; + hasBoxes = true; + bytesLeft = Long.MAX_VALUE; + } - @Override - public boolean hasNext () - { - return (!eof); - } + /** Returns a name for use in messages. */ + @Override + protected String getSelfPropName() { + return "Top Level"; + } + @Override + public boolean hasNext() { + return (!eof); + } } diff --git a/jhove-modules/jpeg2000-hul/src/main/java/edu/harvard/hul/ois/jhove/module/jpeg2000/UUIDBox.java b/jhove-modules/jpeg2000-hul/src/main/java/edu/harvard/hul/ois/jhove/module/jpeg2000/UUIDBox.java index a2d7678bb..37292f0ea 100644 --- a/jhove-modules/jpeg2000-hul/src/main/java/edu/harvard/hul/ois/jhove/module/jpeg2000/UUIDBox.java +++ b/jhove-modules/jpeg2000-hul/src/main/java/edu/harvard/hul/ois/jhove/module/jpeg2000/UUIDBox.java @@ -1,93 +1,75 @@ -/********************************************************************** - * Jhove - JSTOR/Harvard Object Validation Environment - * Copyright 2004 by JSTOR and the President and Fellows of Harvard College - **********************************************************************/ - +/** + * ******************************************************************** Jhove - JSTOR/Harvard Object + * Validation Environment Copyright 2004 by JSTOR and the President and Fellows of Harvard College + * ******************************************************************** + */ package edu.harvard.hul.ois.jhove.module.jpeg2000; import edu.harvard.hul.ois.jhove.*; import java.io.*; /** - * UUID Box. - * See I.7.2 in ISO/IEC 15444-1:2000 + * UUID Box. See I.7.2 in ISO/IEC 15444-1:2000 * * @author Gary McGath - * * @see UUIDInfoBox * @see UUIDListBox */ public class UUIDBox extends JP2Box { - /** - * Constructor with superbox. - * - * @param parent parent superbox of this box - */ - public UUIDBox (RandomAccessFile raf, BoxHolder parent) - { - super (raf, parent); - } + /** + * Constructor with superbox. + * + * @param parent parent superbox of this box + */ + public UUIDBox(RandomAccessFile raf, BoxHolder parent) { + super(raf, parent); + } + + /** + * Reads the box, putting appropriate information in the RepInfo object. setModule, setBoxHeader, + * setRepInfo and setDataInputStream must be called before readBox is called. + * readBox must completely consume the box, so that the next byte to be read by the + * DataInputStream is the FF byte of the next Box. + */ + @Override + public boolean readBox() throws IOException { + // The UUID box consists of a 16-byte UUID field + // and a variable-size data field. Both are binary + // data, so we make them byte array properties. + Property parray[] = new Property[2]; - /** Reads the box, putting appropriate information in - * the RepInfo object. setModule, setBoxHeader, - * setRepInfo and setDataInputStream must be called - * before readBox is called. - * readBox must completely consume the - * box, so that the next byte to be read by the - * DataInputStream is the FF byte of the next Box. - */ - @Override - public boolean readBox() throws IOException { - // The UUID box consists of a 16-byte UUID field - // and a variable-size data field. Both are binary - // data, so we make them byte array properties. - Property parray[] = new Property[2]; - - initBytesRead (); - int len = (int) _boxHeader.getDataLength (); - if (_boxHeader.getLength() != 0 && len < 16) { - wrongBoxSize (); - return false; - } - byte[] uuid = new byte[16]; - ModuleBase.readByteBuf (_dstrm, uuid, _module); - parray[0] = new Property ("UUID", - PropertyType.BYTE, - PropertyArity.ARRAY, - uuid); - - // Whatever is left is the data field. - // This gets difficult if the length field is - // 0, implying that the rest of the file is used. - int dataLen = len - 16; - if (dataLen > 0) { - byte[] dataBytes = new byte[dataLen]; - ModuleBase.readByteBuf (_dstrm, dataBytes, _module); - parray[1] = new Property ("Data", - PropertyType.BYTE, - PropertyArity.ARRAY, - dataBytes); - } - else { - // No data -- put in a FALSE property just as placeholder - parray[1] = new Property ("Data", - PropertyType.BOOLEAN, - Boolean.FALSE); - } - _module.addUUID (new Property ("UUIDBox", - PropertyType.PROPERTY, - PropertyArity.ARRAY, - parray)); - - finalizeBytesRead (); - return true; + initBytesRead(); + int len = (int) _boxHeader.getDataLength(); + if (_boxHeader.getLength() != 0 && len < 16) { + wrongBoxSize(); + return false; } + byte[] uuid = new byte[16]; + ModuleBase.readByteBuf(_dstrm, uuid, _module); + parray[0] = new Property("UUID", PropertyType.BYTE, PropertyArity.ARRAY, uuid); - /** Returns the name of the Box. */ - @Override - protected String getSelfPropName () - { - return "UUID Box"; + // Whatever is left is the data field. + // This gets difficult if the length field is + // 0, implying that the rest of the file is used. + int dataLen = len - 16; + if (dataLen > 0) { + byte[] dataBytes = new byte[dataLen]; + ModuleBase.readByteBuf(_dstrm, dataBytes, _module); + parray[1] = new Property("Data", PropertyType.BYTE, PropertyArity.ARRAY, dataBytes); + } else { + // No data -- put in a FALSE property just as placeholder + parray[1] = new Property("Data", PropertyType.BOOLEAN, Boolean.FALSE); } + _module.addUUID(new Property("UUIDBox", PropertyType.PROPERTY, PropertyArity.ARRAY, parray)); + + finalizeBytesRead(); + return true; + } + + /** Returns the name of the Box. */ + @Override + protected String getSelfPropName() { + return "UUID Box"; + } } diff --git a/jhove-modules/jpeg2000-hul/src/main/java/edu/harvard/hul/ois/jhove/module/jpeg2000/UUIDInfoBox.java b/jhove-modules/jpeg2000-hul/src/main/java/edu/harvard/hul/ois/jhove/module/jpeg2000/UUIDInfoBox.java index bfc56ab72..d1ae56211 100644 --- a/jhove-modules/jpeg2000-hul/src/main/java/edu/harvard/hul/ois/jhove/module/jpeg2000/UUIDInfoBox.java +++ b/jhove-modules/jpeg2000-hul/src/main/java/edu/harvard/hul/ois/jhove/module/jpeg2000/UUIDInfoBox.java @@ -1,9 +1,9 @@ -/********************************************************************** - * Jhove - JSTOR/Harvard Object Validation Environment - * Copyright 2004 by JSTOR and the President and Fellows of Harvard College +/** + * ******************************************************************** Jhove - JSTOR/Harvard Object + * Validation Environment Copyright 2004 by JSTOR and the President and Fellows of Harvard College * - **********************************************************************/ - + *

******************************************************************** + */ package edu.harvard.hul.ois.jhove.module.jpeg2000; import edu.harvard.hul.ois.jhove.*; @@ -11,127 +11,102 @@ import java.util.*; /** - * UUID info box. - * See I.7.3 in ISO/IEC 15444-1:2000 - * - * @author Gary McGath + * UUID info box. See I.7.3 in ISO/IEC 15444-1:2000 * + * @author Gary McGath */ public class UUIDInfoBox extends JP2Box { - private Property _urlProp; - private Property _uuidListProp; + private Property _urlProp; + private Property _uuidListProp; + /** + * Constructor with superbox. + * + * @param parent parent superbox of this box + */ + public UUIDInfoBox(RandomAccessFile raf, BoxHolder parent) { + super(raf, parent); + } - /** - * Constructor with superbox. - * - * @param parent parent superbox of this box - */ - public UUIDInfoBox(RandomAccessFile raf, BoxHolder parent) { - super(raf, parent); + /** + * Reads the box, putting appropriate information in the RepInfo object. setModule, setBoxHeader, + * setRepInfo and setDataInputStream must be called before readBox is called. + * readBox must completely consume the box, so that the next byte to be read by the + * DataInputStream is the FF byte of the next Box. + */ + @Override + public boolean readBox() throws IOException { + if (_parentBox != null) { + wrongBoxContext(); + return false; } - - /** Reads the box, putting appropriate information in - * the RepInfo object. setModule, setBoxHeader, - * setRepInfo and setDataInputStream must be called - * before readBox is called. - * readBox must completely consume the - * box, so that the next byte to be read by the - * DataInputStream is the FF byte of the next Box. - */ - @Override - public boolean readBox() throws IOException { - if (_parentBox != null) { - wrongBoxContext (); - return false; - } - initBytesRead (); - hasBoxes = true; - int sizeLeft = (int) _boxHeader.getDataLength() ; - BoxHeader subhdr = new BoxHeader (_module, _dstrm); - JP2Box box = null; - while (hasNext ()) { - box = (JP2Box) next (); - if (box == null) { - break; - } - if (box instanceof UUIDListBox || - box instanceof UUIDListBox || - box instanceof DataEntryURLBox) { - box.setBoxHeader(subhdr); - box.setDataInputStream(_dstrm); - box.setRandomAccessFile (_raf); - box.setRepInfo(_repInfo); - box.setModule(_module); - if (!box.readBox ()) { - return false; - } - } - else { - box.skipBox (); - } - } - // A box has to be at least 8 bytes long, and there must - // not be any bytes left over. - if (sizeLeft != 0) { - // Underran the superbox -- get out quick - _repInfo.setMessage (new ErrorMessage - (MessageConstants.JPEG2000_HUL_56, - _module.getFilePos ())); - _repInfo.setWellFormed (false); - return false; - + initBytesRead(); + hasBoxes = true; + int sizeLeft = (int) _boxHeader.getDataLength(); + BoxHeader subhdr = new BoxHeader(_module, _dstrm); + JP2Box box = null; + while (hasNext()) { + box = (JP2Box) next(); + if (box == null) { + break; + } + if (box instanceof UUIDListBox + || box instanceof UUIDListBox + || box instanceof DataEntryURLBox) { + box.setBoxHeader(subhdr); + box.setDataInputStream(_dstrm); + box.setRandomAccessFile(_raf); + box.setRepInfo(_repInfo); + box.setModule(_module); + if (!box.readBox()) { + return false; } - List propList = new ArrayList<> (2); - if (_urlProp != null) { - propList.add (_urlProp); - } - if (_uuidListProp != null) { - propList.add (_uuidListProp); - } - _module.addUUIDInfo (new Property ("UUIDInfo", - PropertyType.PROPERTY, - PropertyArity.LIST, - propList)); - finalizeBytesRead (); - return true; + } else { + box.skipBox(); + } } - - - /** Sets the URL string. This will be called from - * DataEntryURLBox. - */ - protected void setURL(String url) - { - _urlProp = new Property ("URL", PropertyType.STRING, url); + // A box has to be at least 8 bytes long, and there must + // not be any bytes left over. + if (sizeLeft != 0) { + // Underran the superbox -- get out quick + _repInfo.setMessage(new ErrorMessage(MessageConstants.JPEG2000_HUL_56, _module.getFilePos())); + _repInfo.setWellFormed(false); + return false; } - - - /** Sets the UUID list. The argument is an array - * of byte arrays of length 16, or schematically: - * byte[][16]. - */ - protected void setUUIDList (byte[][] uuids) - { - List propList = new ArrayList<> (uuids.length); - for (int i = 0; i < uuids.length; i++) { - propList.add (new Property - ("UUIDList", - PropertyType.BYTE, - PropertyArity.ARRAY, - uuids[i])); - } - _uuidListProp = new Property ("UUIDInfo", - PropertyType.PROPERTY, - PropertyArity.LIST, - propList); + List propList = new ArrayList<>(2); + if (_urlProp != null) { + propList.add(_urlProp); } + if (_uuidListProp != null) { + propList.add(_uuidListProp); + } + _module.addUUIDInfo( + new Property("UUIDInfo", PropertyType.PROPERTY, PropertyArity.LIST, propList)); + finalizeBytesRead(); + return true; + } + + /** Sets the URL string. This will be called from DataEntryURLBox. */ + protected void setURL(String url) { + _urlProp = new Property("URL", PropertyType.STRING, url); + } - /** Returns the name of the Box. */ - @Override - protected String getSelfPropName () - { - return "UUID Info Box"; + /** + * Sets the UUID list. The argument is an array of byte arrays of length 16, or schematically: + * byte[][16]. + */ + protected void setUUIDList(byte[][] uuids) { + List propList = new ArrayList<>(uuids.length); + for (int i = 0; i < uuids.length; i++) { + propList.add(new Property("UUIDList", PropertyType.BYTE, PropertyArity.ARRAY, uuids[i])); } + _uuidListProp = new Property("UUIDInfo", PropertyType.PROPERTY, PropertyArity.LIST, propList); + } + + /** Returns the name of the Box. */ + @Override + protected String getSelfPropName() { + return "UUID Info Box"; + } } diff --git a/jhove-modules/jpeg2000-hul/src/main/java/edu/harvard/hul/ois/jhove/module/jpeg2000/UUIDListBox.java b/jhove-modules/jpeg2000-hul/src/main/java/edu/harvard/hul/ois/jhove/module/jpeg2000/UUIDListBox.java index 4f7daaed5..d2c7ce1a3 100644 --- a/jhove-modules/jpeg2000-hul/src/main/java/edu/harvard/hul/ois/jhove/module/jpeg2000/UUIDListBox.java +++ b/jhove-modules/jpeg2000-hul/src/main/java/edu/harvard/hul/ois/jhove/module/jpeg2000/UUIDListBox.java @@ -1,68 +1,63 @@ -/********************************************************************** - * Jhove - JSTOR/Harvard Object Validation Environment - * Copyright 2004 by JSTOR and the President and Fellows of Harvard College +/** + * ******************************************************************** Jhove - JSTOR/Harvard Object + * Validation Environment Copyright 2004 by JSTOR and the President and Fellows of Harvard College * - **********************************************************************/ - + *

******************************************************************** + */ package edu.harvard.hul.ois.jhove.module.jpeg2000; import edu.harvard.hul.ois.jhove.*; import java.io.*; /** - * UUID Box. - * See I.7.3.1 in ISO/IEC 15444-1:2000 - * - * @author Gary McGath + * UUID Box. See I.7.3.1 in ISO/IEC 15444-1:2000 * + * @author Gary McGath */ public class UUIDListBox extends JP2Box { - /** - * Constructor with superbox. - * - * @param parent parent superbox of this box - */ - public UUIDListBox(RandomAccessFile raf, BoxHolder parent) { - super(raf, parent); - } - - /** Reads the box, putting appropriate information in - * the RepInfo object. setModule, setBoxHeader, - * setRepInfo and setDataInputStream must be called - * before readBox is called. - * readBox must completely consume the - * box, so that the next byte to be read by the - * DataInputStream is the FF byte of the next Box. - */ - @Override - public boolean readBox() throws IOException { - if (!(_parentBox instanceof UUIDInfoBox)) { - wrongBoxContext(); - return false; - } - initBytesRead (); + /** + * Constructor with superbox. + * + * @param parent parent superbox of this box + */ + public UUIDListBox(RandomAccessFile raf, BoxHolder parent) { + super(raf, parent); + } - int nUUID = _module.readUnsignedShort (_dstrm); - if ((16 * nUUID + 2) != _boxHeader.getDataLength()) { - wrongBoxSize (); - return false; - } - byte[][] uuids = new byte[nUUID][]; - for (int i = 0; i < nUUID; i++) { - ModuleBase.readByteBuf (_dstrm, uuids[i], _module); - } - if (_parentBox instanceof UUIDInfoBox) { - ((UUIDInfoBox) _parentBox).setUUIDList (uuids); - } - finalizeBytesRead (); - return true; + /** + * Reads the box, putting appropriate information in the RepInfo object. setModule, setBoxHeader, + * setRepInfo and setDataInputStream must be called before readBox is called. + * readBox must completely consume the box, so that the next byte to be read by the + * DataInputStream is the FF byte of the next Box. + */ + @Override + public boolean readBox() throws IOException { + if (!(_parentBox instanceof UUIDInfoBox)) { + wrongBoxContext(); + return false; } + initBytesRead(); - /** Returns the name of the Box. */ - @Override - protected String getSelfPropName () - { - return "UUID List Box"; + int nUUID = _module.readUnsignedShort(_dstrm); + if ((16 * nUUID + 2) != _boxHeader.getDataLength()) { + wrongBoxSize(); + return false; + } + byte[][] uuids = new byte[nUUID][]; + for (int i = 0; i < nUUID; i++) { + ModuleBase.readByteBuf(_dstrm, uuids[i], _module); } + if (_parentBox instanceof UUIDInfoBox) { + ((UUIDInfoBox) _parentBox).setUUIDList(uuids); + } + finalizeBytesRead(); + return true; + } + + /** Returns the name of the Box. */ + @Override + protected String getSelfPropName() { + return "UUID List Box"; + } } diff --git a/jhove-modules/jpeg2000-hul/src/main/java/edu/harvard/hul/ois/jhove/module/jpeg2000/XMLBox.java b/jhove-modules/jpeg2000-hul/src/main/java/edu/harvard/hul/ois/jhove/module/jpeg2000/XMLBox.java index 2322b2281..24090ae22 100644 --- a/jhove-modules/jpeg2000-hul/src/main/java/edu/harvard/hul/ois/jhove/module/jpeg2000/XMLBox.java +++ b/jhove-modules/jpeg2000-hul/src/main/java/edu/harvard/hul/ois/jhove/module/jpeg2000/XMLBox.java @@ -1,77 +1,65 @@ -/********************************************************************** - * Jhove - JSTOR/Harvard Object Validation Environment - * Copyright 2004 by JSTOR and the President and Fellows of Harvard College - **********************************************************************/ - +/** + * ******************************************************************** Jhove - JSTOR/Harvard Object + * Validation Environment Copyright 2004 by JSTOR and the President and Fellows of Harvard College + * ******************************************************************** + */ package edu.harvard.hul.ois.jhove.module.jpeg2000; import edu.harvard.hul.ois.jhove.*; import java.io.*; /** - * XML Box. - * See I.7.1 in ISO/IEC 15444-1:2000 + * XML Box. See I.7.1 in ISO/IEC 15444-1:2000 * * @author Gary McGath - * */ public class XMLBox extends JP2Box { - private String xmlData; + private String xmlData; - + /** + * Constructor with superbox. + * + * @param parent parent superbox of this box + */ + public XMLBox(RandomAccessFile raf, BoxHolder parent) { + super(raf, parent); + } - /** - * Constructor with superbox. - * - * @param parent parent superbox of this box - */ - public XMLBox (RandomAccessFile raf, BoxHolder parent) - { - super (raf, parent); - } + /** + * Reads the box, putting appropriate information in the RepInfo object. setModule, setBoxHeader, + * setRepInfo and setDataInputStream must be called before readBox is called. + * readBox must completely consume the box, so that the next byte to be read by the + * DataInputStream is the FF byte of the next Box. + */ + @Override + public boolean readBox() throws IOException { + initBytesRead(); + int len = (int) _boxHeader.getDataLength(); - /** Reads the box, putting appropriate information in - * the RepInfo object. setModule, setBoxHeader, - * setRepInfo and setDataInputStream must be called - * before readBox is called. - * readBox must completely consume the - * box, so that the next byte to be read by the - * DataInputStream is the FF byte of the next Box. - */ - @Override - public boolean readBox() throws IOException { - initBytesRead (); - int len = (int) _boxHeader.getDataLength (); - - byte[] bbuf = new byte[len]; - - ModuleBase.readByteBuf (_dstrm, bbuf, _module); - xmlData = new String (bbuf); - if (_parentBox == null) { - _module.addXML (xmlData); - } - - finalizeBytesRead (); - return true; - } + byte[] bbuf = new byte[len]; - /** Returns a Property which describes the Box, for use - * by Association boxes and perhaps others. - */ - @Override - protected Property getSelfPropDesc () - { - return new Property (DESCRIPTION_NAME, - PropertyType.STRING, - xmlData); + ModuleBase.readByteBuf(_dstrm, bbuf, _module); + xmlData = new String(bbuf); + if (_parentBox == null) { + _module.addXML(xmlData); } + finalizeBytesRead(); + return true; + } - /** Returns the name of the Box. */ - @Override - protected String getSelfPropName () - { - return "XML Box"; - } + /** + * Returns a Property which describes the Box, for use by Association boxes and perhaps others. + */ + @Override + protected Property getSelfPropDesc() { + return new Property(DESCRIPTION_NAME, PropertyType.STRING, xmlData); + } + + /** Returns the name of the Box. */ + @Override + protected String getSelfPropName() { + return "XML Box"; + } } diff --git a/jhove-modules/jpeg2000-hul/src/main/java/edu/harvard/hul/ois/jhove/module/jpeg2000/package-info.java b/jhove-modules/jpeg2000-hul/src/main/java/edu/harvard/hul/ois/jhove/module/jpeg2000/package-info.java index 9ceb59ba7..21c4212b5 100644 --- a/jhove-modules/jpeg2000-hul/src/main/java/edu/harvard/hul/ois/jhove/module/jpeg2000/package-info.java +++ b/jhove-modules/jpeg2000-hul/src/main/java/edu/harvard/hul/ois/jhove/module/jpeg2000/package-info.java @@ -1,4 +1,2 @@ -/** - * Contains supporting classes for the JPEG2000-HUL module. - */ -package edu.harvard.hul.ois.jhove.module.jpeg2000; \ No newline at end of file +/** Contains supporting classes for the JPEG2000-HUL module. */ +package edu.harvard.hul.ois.jhove.module.jpeg2000; diff --git a/jhove-modules/pdf-hul/src/main/java/PDump.java b/jhove-modules/pdf-hul/src/main/java/PDump.java index 2a6437c3f..9d54969d1 100644 --- a/jhove-modules/pdf-hul/src/main/java/PDump.java +++ b/jhove-modules/pdf-hul/src/main/java/PDump.java @@ -1,115 +1,94 @@ -/********************************************************************** - * PDump - JSTOR/Harvard Object Validation Environment - * Copyright 2003 by JSTOR and the President and Fellows of Harvard College +/** + * ******************************************************************** PDump - JSTOR/Harvard Object + * Validation Environment Copyright 2003 by JSTOR and the President and Fellows of Harvard College * - * This program 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 2 of the License, or (at - * your option) any later version. - * - * This program 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. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 - * USA - **********************************************************************/ - + *

This program 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 2 + * of the License, or (at your option) any later version. + * + *

This program 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. + * + *

You should have received a copy of the GNU Lesser General Public License along with this + * program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA ******************************************************************** + */ import edu.harvard.hul.ois.jhove.*; import edu.harvard.hul.ois.jhove.module.pdf.*; import java.io.*; -/** - * Dump contents of PDF file in human-readable format. - */ -public class PDump - extends Dump -{ - /****************************************************************** - * MAIN ENTRY POINT. - ******************************************************************/ +/** Dump contents of PDF file in human-readable format. */ +public class PDump extends Dump { + /** + * **************************************************************** MAIN ENTRY POINT. + * **************************************************************** + */ - /** - * Main entry point. - * @param args Command line arguments - */ - public static void main (String [] args) - { - if (args.length < 1) { - System.err.println ("usage: java PDump file"); - System.exit (-1); - } + /** + * Main entry point. + * + * @param args Command line arguments + */ + public static void main(String[] args) { + if (args.length < 1) { + System.err.println("usage: java PDump file"); + System.exit(-1); + } - try { - RandomAccessFile file = new RandomAccessFile (args[0], "r"); - Tokenizer tokenizer = new FileTokenizer (file); - Token token = null; - long offset = 0; - while ((token = tokenizer.getNext ()) != null) { - System.out.print (leading (offset, 8) + offset + ": "); - if (token instanceof ArrayEnd) { - System.out.println ("ArrayEnd"); - } - else if (token instanceof ArrayStart) { - System.out.println ("ArrayStart"); - } - else if (token instanceof Comment) { - System.out.println ("Comment \"" + - ((Comment) token).getValue () + "\""); - } - else if (token instanceof DictionaryEnd) { - System.out.println ("DictionaryEnd"); - } - else if (token instanceof DictionaryStart) { - System.out.println ("DictionaryStart"); - } -// else if (token instanceof Hexadecimal) { -// System.out.println ("Hexadecimal[" + -// (((Hexadecimal) token).isPDFDocEncoding () ? -// "PDF" : "UTF-16") + "] \"" + -// ((Hexadecimal) token).getValue () + -// "\""); -// } - else if (token instanceof Keyword) { - System.out.println ("Keyword \"" + - ((Keyword) token).getValue () + "\""); - } - else if (token instanceof Literal) { - System.out.println ("Literal[" + - (((Literal)token).isPDFDocEncoding () ? - "PDF" : "UTF-16") + "] \"" + - ((Literal) token).getValue () + "\""); - } - else if (token instanceof Name) { - System.out.println ("Name \"" + - ((Name) token).getValue () + "\""); - } - else if (token instanceof Numeric) { - Numeric numeric = (Numeric) token; - if (numeric.isReal ()) { - System.out.println ("Numeric " + numeric.getValue ()); - } - else { - System.out.println ("Numeric " + - numeric.getIntegerValue ()); - } - } - else if (token instanceof Stream) { - System.out.println ("Stream " + - ((Stream) token).getLength ()); - } - else { - System.out.println (token); - } - offset = tokenizer.getOffset (); - } - } - catch (Exception e) { - e.printStackTrace (System.err); - System.exit (-2); - } + try { + RandomAccessFile file = new RandomAccessFile(args[0], "r"); + Tokenizer tokenizer = new FileTokenizer(file); + Token token = null; + long offset = 0; + while ((token = tokenizer.getNext()) != null) { + System.out.print(leading(offset, 8) + offset + ": "); + if (token instanceof ArrayEnd) { + System.out.println("ArrayEnd"); + } else if (token instanceof ArrayStart) { + System.out.println("ArrayStart"); + } else if (token instanceof Comment) { + System.out.println("Comment \"" + ((Comment) token).getValue() + "\""); + } else if (token instanceof DictionaryEnd) { + System.out.println("DictionaryEnd"); + } else if (token instanceof DictionaryStart) { + System.out.println("DictionaryStart"); + } + // else if (token instanceof Hexadecimal) { + // System.out.println ("Hexadecimal[" + + // (((Hexadecimal) token).isPDFDocEncoding () ? + // "PDF" : "UTF-16") + "] \"" + + // ((Hexadecimal) token).getValue () + + // "\""); + // } + else if (token instanceof Keyword) { + System.out.println("Keyword \"" + ((Keyword) token).getValue() + "\""); + } else if (token instanceof Literal) { + System.out.println( + "Literal[" + + (((Literal) token).isPDFDocEncoding() ? "PDF" : "UTF-16") + + "] \"" + + ((Literal) token).getValue() + + "\""); + } else if (token instanceof Name) { + System.out.println("Name \"" + ((Name) token).getValue() + "\""); + } else if (token instanceof Numeric) { + Numeric numeric = (Numeric) token; + if (numeric.isReal()) { + System.out.println("Numeric " + numeric.getValue()); + } else { + System.out.println("Numeric " + numeric.getIntegerValue()); + } + } else if (token instanceof Stream) { + System.out.println("Stream " + ((Stream) token).getLength()); + } else { + System.out.println(token); + } + offset = tokenizer.getOffset(); + } + } catch (Exception e) { + e.printStackTrace(System.err); + System.exit(-2); } + } } diff --git a/jhove-modules/pdf-hul/src/main/java/edu/harvard/hul/ois/jhove/module/PdfModule.java b/jhove-modules/pdf-hul/src/main/java/edu/harvard/hul/ois/jhove/module/PdfModule.java index c03e80280..9ed9a6591 100644 --- a/jhove-modules/pdf-hul/src/main/java/edu/harvard/hul/ois/jhove/module/PdfModule.java +++ b/jhove-modules/pdf-hul/src/main/java/edu/harvard/hul/ois/jhove/module/PdfModule.java @@ -1,49 +1,22 @@ -/********************************************************************** - * Jhove - JSTOR/Harvard Object Validation Environment - * Copyright 2003-2007 by JSTOR and the President and Fellows of Harvard College +/** + * ******************************************************************** Jhove - JSTOR/Harvard Object + * Validation Environment Copyright 2003-2007 by JSTOR and the President and Fellows of Harvard + * College * - * This program 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 2 of the License, or (at - * your option) any later version. + *

This program 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 2 + * of the License, or (at your option) any later version. * - * This program 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. + *

This program 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. * - * You should have received a copy of the GNU Lesser General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 - * USA - **********************************************************************/ - + *

You should have received a copy of the GNU Lesser General Public License along with this + * program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA ******************************************************************** + */ package edu.harvard.hul.ois.jhove.module; -import java.io.IOException; -import java.io.RandomAccessFile; -import java.io.UnsupportedEncodingException; -import java.text.MessageFormat; -import java.util.ArrayList; -import java.util.Date; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Iterator; -import java.util.LinkedList; -import java.util.List; -import java.util.ListIterator; -import java.util.Map; -import java.util.Set; -import java.util.Vector; -import java.util.logging.Level; -import java.util.logging.Logger; -import java.util.zip.ZipException; - -import javax.xml.parsers.SAXParserFactory; - -import org.xml.sax.SAXException; -import org.xml.sax.XMLReader; - import edu.harvard.hul.ois.jhove.Agent; import edu.harvard.hul.ois.jhove.Document; import edu.harvard.hul.ois.jhove.DocumentType; @@ -53,7 +26,6 @@ import edu.harvard.hul.ois.jhove.IdentifierType; import edu.harvard.hul.ois.jhove.InfoMessage; import edu.harvard.hul.ois.jhove.InternalSignature; -import edu.harvard.hul.ois.jhove.Message; import edu.harvard.hul.ois.jhove.Module; import edu.harvard.hul.ois.jhove.ModuleBase; import edu.harvard.hul.ois.jhove.NisoImageMetadata; @@ -107,4336 +79,4177 @@ import edu.harvard.hul.ois.jhove.module.pdf.X1aProfile; import edu.harvard.hul.ois.jhove.module.pdf.X2Profile; import edu.harvard.hul.ois.jhove.module.pdf.X3Profile; +import java.io.IOException; +import java.io.RandomAccessFile; +import java.io.UnsupportedEncodingException; +import java.text.MessageFormat; +import java.util.ArrayList; +import java.util.Date; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; +import java.util.ListIterator; +import java.util.Map; +import java.util.Set; +import java.util.Vector; +import java.util.logging.Level; +import java.util.logging.Logger; +import java.util.zip.ZipException; +import javax.xml.parsers.SAXParserFactory; +import org.xml.sax.SAXException; +import org.xml.sax.XMLReader; -/** - * Module for identification and validation of PDF files. - */ +/** Module for identification and validation of PDF files. */ public class PdfModule extends ModuleBase { - public static final String MIME_TYPE = "application/pdf"; - public static final String EXT = ".pdf"; - public static final int MAX_PAGE_TREE_DEPTH = 100; - public static final int MAX_OBJ_STREAM_DEPTH = 30; - - private static final String ENCODING_PREFIX = "ENC="; - - private static final String DEFAULT_PAGE_LAYOUT = "SinglePage"; - private static final String DEFAULT_MODE = "UseNone"; - - private static final String FILTER_NAME_CCITT = "CCITTFaxDecode"; - private static final String FILTER_NAME_CRYPT = "Crypt"; - private static final String FILTER_NAME_DCT = "DCTDecode"; - private static final String FILTER_NAME_FLATE = "FlateDecode"; - private static final String FILTER_NAME_JPX = "JPXDecode"; - private static final String FILTER_NAME_LZW = "LZWDecode"; - private static final String FILTER_NAME_RUN_LENGTH = "RunLengthDecode"; - - private static final String FILTER_VAL_STANDARD = "Standard"; - - private static final String RESOURCE_NAME_XOBJECT = "XObject"; - - private static final String FONT_TYPE0 = "Type0"; - private static final String FONT_TYPE1 = "Type1"; - private static final String FONT_TYPE3 = "Type3"; - private static final String FONT_MM_TYPE1 = "MMType1"; - private static final String FONT_TRUE_TYPE = "TrueType"; - private static final String FONT_CID_TYPE0 = "CIDFontType0"; - private static final String FONT_CID_TYPE2 = "CIDFontType2"; - - private static final String ACTION_VAL_GOTO = "GoTo"; - - private static final String DICT_KEY_DIRECTION = "Direction"; - - private static final String DICT_KEY_CENTER_WINDOW = "CenterWindow"; - private static final String DICT_KEY_DISP_DOC_TITLE = "DisplayDocTitle"; - private static final String DICT_KEY_FIT_WINDOW = "FitWindow"; - private static final String DICT_KEY_HIDE_MENUBAR = "HideMenubar"; - private static final String DICT_KEY_HIDE_TOOLBAR = "HideToolbar"; - private static final String DICT_KEY_HIDE_WINDOW_UI = "HideWindowUI"; - private static final String DICT_KEY_NO_FULL_PAGE = "NonFullScreenPageMode"; - private static final String DICT_KEY_PAGE_CLIP = "PageClip"; - private static final String DICT_KEY_PRINT_AREA = "PrintArea"; - private static final String DICT_KEY_VIEW_AREA = "ViewArea"; - private static final String DICT_KEY_VIEW_CLIP = "ViewClip"; - - private static final String PROP_NAME_CENTER_WINDOW = DICT_KEY_CENTER_WINDOW; - private static final String PROP_NAME_DISP_DOC_TITLE = DICT_KEY_DISP_DOC_TITLE; - private static final String PROP_NAME_FIT_WINDOW = DICT_KEY_FIT_WINDOW; - private static final String PROP_NAME_HIDE_MENUBAR = DICT_KEY_HIDE_MENUBAR; - private static final String PROP_NAME_HIDE_TOOLBAR = DICT_KEY_HIDE_TOOLBAR; - private static final String PROP_NAME_HIDE_WINDOW_UI = DICT_KEY_HIDE_WINDOW_UI; - private static final String PROP_NAME_NO_FULL_PAGE = DICT_KEY_NO_FULL_PAGE; - private static final String PROP_NAME_PAGE_CLIP = DICT_KEY_PAGE_CLIP; - private static final String PROP_NAME_PRINT_AREA = DICT_KEY_PRINT_AREA; - private static final String PROP_NAME_VIEW_AREA = DICT_KEY_VIEW_AREA; - private static final String PROP_NAME_VIEW_CLIP = DICT_KEY_VIEW_CLIP; - private static final String PROP_NAME_DIRECTION = DICT_KEY_DIRECTION; - - private static final String DICT_KEY_FONT_DESCRIPTOR = "FontDescriptor"; - private static final String DICT_KEY_STARTXREF = "startxref"; - private static final String DICT_KEY_BASE_FONT = "BaseFont"; - private static final String DICT_KEY_CONTENTS = "Contents"; - private static final String DICT_KEY_CID_INFO = "CIDSystemInfo"; - private static final String DICT_KEY_DIFFERENCES = "Differences"; - private static final String DICT_KEY_RESOURCES = "Resources"; - private static final String DICT_KEY_TO_UNICODE = "ToUnicode"; - private static final String DICT_KEY_ROOT = "Root"; - private static final String DICT_KEY_RECT = "Rect"; - private static final String DICT_KEY_DEST = "Dest"; - private static final String DICT_KEY_FIRST_CHAR = "FirstChar"; - private static final String DICT_KEY_LAST_CHAR = "LastChar"; - private static final String DICT_KEY_TRAILER = "trailer"; - private static final String DICT_KEY_SIZE = "Size"; - private static final String DICT_KEY_ENCRYPT = "Encrypt"; - private static final String DICT_KEY_INFO = "Info"; - private static final String DICT_KEY_ID = "ID"; - private static final String DICT_KEY_FONT_NAME = "FontName"; - private static final String DICT_KEY_FONT_FILE = "FontFile"; - private static final String DICT_KEY_FONT_FILE_2 = DICT_KEY_FONT_FILE + "2"; - private static final String DICT_KEY_FONT_FILE_3 = DICT_KEY_FONT_FILE + "3"; - private static final String DICT_KEY_BBOX = "BBox"; - private static final String DICT_KEY_FONT_BBOX = "Font" + DICT_KEY_BBOX; - private static final String DICT_KEY_XREF_STREAM = "XRefStm"; - private static final String DICT_KEY_VIEWER_PREFS = "ViewerPreferences"; - private static final String DICT_KEY_PAGE_LAYOUT = "PageLayout"; - private static final String DICT_KEY_PAGE_MODE = "PageMode"; - private static final String DICT_KEY_OUTLINES = "Outlines"; - private static final String DICT_KEY_ORDERING = "Ordering"; - private static final String DICT_KEY_REGISTRY = "Registry"; - private static final String DICT_KEY_SUPPLEMENT = "Supplement"; - private static final String DICT_KEY_LANG = "Lang"; - private static final String DICT_KEY_PAGES = "Pages"; - private static final String DICT_KEY_PAGE_LABELS = "PageLabels"; - private static final String DICT_KEY_TYPE = "Type"; - private static final String DICT_KEY_VERSION = "Version"; - private static final String DICT_KEY_NAME = "Name"; - private static final String DICT_KEY_NAMES = DICT_KEY_NAME + "s"; - private static final String DICT_KEY_EMBEDDED_FILES = "EmbeddedFiles"; - private static final String DICT_KEY_DESTS = "Dests"; - private static final String DICT_KEY_FILTER = "Filter"; - private static final String DICT_KEY_K = "K"; - private static final String DICT_KEY_P = "P"; - private static final String DICT_KEY_R = "R"; - private static final String DICT_KEY_V = "V"; - private static final String DICT_KEY_ENCODING = "Encoding"; - private static final String DICT_KEY_BASE_ENCODING = "Base" - + DICT_KEY_ENCODING; - private static final String DICT_KEY_LENGTH = "Length"; - private static final String DICT_KEY_WIDTH = "Width"; - private static final String DICT_KEY_HEIGHT = "Height"; - private static final String DICT_KEY_KEY_LENGTH = "KeyLength"; - private static final String DICT_KEY_TITLE = "Title"; - private static final String DICT_KEY_AUTHOR = "Author"; - private static final String DICT_KEY_SUBJECT = "Subject"; - private static final String DICT_KEY_KEYWORDS = "Keywords"; - private static final String DICT_KEY_CREATOR = "Creator"; - private static final String DICT_KEY_PRODUCER = "Producer"; - private static final String DICT_KEY_CREATION_DATE = "CreationDate"; - private static final String DICT_KEY_MODIFIED_DATE = "ModDate"; - private static final String DICT_KEY_TRAPPED = "Trapped"; - private static final String DICT_KEY_XOBJ_SUBTYPE = "Subtype"; - private static final String DICT_KEY_FONT_SUBTYPE = DICT_KEY_XOBJ_SUBTYPE; - private static final String DICT_KEY_DECODE_PARAMS = "DecodeParms"; - private static final String DICT_KEY_COLOR_SPACE = "ColorSpace"; - private static final String DICT_KEY_METADATA = "Metadata"; - private static final String DICT_KEY_BITS_PER_COMPONENT = "BitsPerComponent"; - private static final String DICT_KEY_INTENT = "Intent"; - private static final String DICT_KEY_IMAGE_MASK = "ImageMask"; - private static final String DICT_KEY_DECODE = "Decode"; - private static final String DICT_KEY_INTERPOLATE = "Interpolate"; - private static final String DICT_KEY_DESCENDANT_FONTS = "DescendantFonts"; - private static final String DICT_KEY_ROTATE = "Rotate"; - private static final String DICT_KEY_USER_UNIT = "UserUnit"; - private static final String DICT_KEY_VIEWPORT = "VP"; - private static final String DICT_KEY_THUMB = "Thumb"; - private static final String DICT_KEY_MEASURE = "Measure"; - private static final String DICT_KEY_COUNT = "Count"; - private static final String DICT_KEY_PARENT = "Parent"; - private static final String DICT_KEY_PREV = "Prev"; - private static final String DICT_KEY_NEXT = "Next"; - private static final String DICT_KEY_FIRST = "First"; - private static final String DICT_KEY_LAST = "Last"; - private static final String DICT_KEY_FLAGS = "Flags"; - - private static final String KEY_VAL_CATALOG = "Catalog"; - private static final String KEY_VAL_PAGES = "Pages"; - - private static final String PROP_NAME_BASE_FONT = DICT_KEY_BASE_FONT; - private static final String PROP_NAME_CALLOUT_LINE = "CalloutLine"; - private static final String PROP_NAME_CMAP_DICT = "CMapDictionary"; - private static final String PROP_NAME_CID_INFO = DICT_KEY_CID_INFO; - private static final String PROP_NAME_CID_INFOS = PROP_NAME_CID_INFO + "s"; - private static final String PROP_NAME_CONTENTS = DICT_KEY_CONTENTS; - private static final String PROP_NAME_DISTANCE = "Distance"; - private static final String PROP_NAME_DIFFERENCES = DICT_KEY_DIFFERENCES; - private static final String PROP_NAME_ENCODING = DICT_KEY_ENCODING; - private static final String PROP_NAME_ENCODING_DICTIONARY = PROP_NAME_ENCODING - + "Dictionary"; - private static final String PROP_NAME_BASE_ENCODING = DICT_KEY_BASE_ENCODING; - private static final String PROP_NAME_EXTERNAL_STREAMS = "ExternalStreams"; - private static final String PROP_NAME_FILTER = DICT_KEY_FILTER; - private static final String PROP_NAME_FILTERS = "Filters"; - private static final String PROP_NAME_FILE = "File"; - private static final String PROP_NAME_FIRST_CHAR = DICT_KEY_FIRST_CHAR; - private static final String PROP_NAME_FLAGS = DICT_KEY_FLAGS; - private static final String PROP_NAME_AREA = "Area"; - private static final String PROP_NAME_IMAGE = "Image"; - private static final String PROP_NAME_IMAGES = PROP_NAME_IMAGE + "s"; - private static final String PROP_NAME_OBJECTS = "Objects"; - private static final String PROP_NAME_RESOURCES = DICT_KEY_RESOURCES; - private static final String PROP_NAME_SUBTYPE = DICT_KEY_XOBJ_SUBTYPE; - private static final String PROP_NAME_FREE_OBJECTS = "FreeObjects"; - private static final String PROP_NAME_INC_UPDATES = "IncrementalUpdates"; - private static final String PROP_NAME_DOC_CATALOG = "DocumentCatalog"; - private static final String PROP_NAME_ENCRYPTION = "Encryption"; - private static final String PROP_NAME_KEY_LENGTH = DICT_KEY_KEY_LENGTH; - private static final String PROP_NAME_INFO = DICT_KEY_INFO; - private static final String PROP_NAME_DESTINATION = "Destination"; - private static final String PROP_NAME_CHILDREN = "Children"; - private static final String PROP_NAME_PAGE_LAYOUT = DICT_KEY_PAGE_LAYOUT; - private static final String PROP_NAME_LANG = "Language"; - private static final String PROP_NAME_LAST_CHAR = DICT_KEY_LAST_CHAR; - private static final String PROP_NAME_MEASURE = DICT_KEY_MEASURE; - private static final String PROP_NAME_SECURITY_HANDLER = "SecurityHandler"; - private static final String PROP_NAME_EFF = "EFF"; - private static final String PROP_NAME_ALGORITHM = "Algorithm"; - private static final String PROP_NAME_RECT = DICT_KEY_RECT; - private static final String PROP_NAME_REVISION = "Revision"; - private static final String PROP_NAME_OWNER_STRING = "OwnerString"; - private static final String PROP_NAME_USER_STRING = "UserString"; - private static final String PROP_NAME_OWNERKEY_STRING = "OwnerEncryptionKey"; - private static final String PROP_NAME_USERKEY_STRING = "UserEncryptionKey"; - private static final String PROP_NAME_USER_UNIT = DICT_KEY_USER_UNIT; - private static final String PROP_NAME_STANDARD_SECURITY_HANDLER = "StandardSecurityHandler"; - private static final String PROP_NAME_TITLE = DICT_KEY_TITLE; - private static final String PROP_NAME_AUTHOR = DICT_KEY_AUTHOR; - private static final String PROP_NAME_SUBJECT = DICT_KEY_SUBJECT; - private static final String PROP_NAME_KEYWORDS = DICT_KEY_KEYWORDS; - private static final String PROP_NAME_CREATOR = DICT_KEY_CREATOR; - private static final String PROP_NAME_PRODUCER = DICT_KEY_PRODUCER; - private static final String PROP_NAME_CREATION_DATE = DICT_KEY_CREATION_DATE; - private static final String PROP_NAME_MODIFIED_DATE = DICT_KEY_MODIFIED_DATE; - private static final String PROP_NAME_TRAPPED = DICT_KEY_TRAPPED; - private static final String PROP_NAME_FILTER_PIPELINE = "FilterPipeline"; - private static final String PROP_NAME_NISO_IMAGE_MD = "NisoImageMetadata"; - private static final String PROP_NAME_COLOR_SPACE = DICT_KEY_COLOR_SPACE; - private static final String PROP_NAME_ACTION_DEST = "ActionDest"; - private static final String PROP_NAME_ANNOTATION = "Annotation"; - private static final String PROP_NAME_APP_DICT = "AppearanceDictionary"; - private static final String PROP_NAME_INTENT = DICT_KEY_INTENT; - private static final String PROP_NAME_IMAGE_MASK = DICT_KEY_IMAGE_MASK; - private static final String PROP_NAME_DECODE = DICT_KEY_DECODE; - private static final String PROP_NAME_NAME = DICT_KEY_NAME; - private static final String PROP_NAME_ID = DICT_KEY_ID; - private static final String PROP_NAME_ITEM = "Item"; - private static final String PROP_NAME_INTERPOLATE = DICT_KEY_INTERPOLATE; - private static final String PROP_NAME_FONT_TYPE0 = FONT_TYPE0; - private static final String PROP_NAME_FONT_TYPE1 = FONT_TYPE1; - private static final String PROP_NAME_FONT_TYPE3 = FONT_TYPE3; - private static final String PROP_NAME_FONT_MM_TYPE1 = FONT_MM_TYPE1; - private static final String PROP_NAME_FONT_TRUE_TYPE = FONT_TRUE_TYPE; - private static final String PROP_NAME_FONT_CID_TYPE0 = FONT_CID_TYPE0; - private static final String PROP_NAME_FONT_CID_TYPE2 = FONT_CID_TYPE2; - private static final String PROP_NAME_FONT = "Font"; - private static final String PROP_NAME_FONTS = PROP_NAME_FONT + "s"; - private static final String PROP_NAME_FONT_SUBSET = PROP_NAME_FONT - + "Subset"; - private static final String PROP_NAME_FONT_BBOX = DICT_KEY_FONT_BBOX; - private static final String PROP_NAME_FONT_DESC = DICT_KEY_FONT_DESCRIPTOR; - private static final String PROP_NAME_FONT_FILE = DICT_KEY_FONT_FILE; - private static final String PROP_NAME_FONT_FILE_2 = DICT_KEY_FONT_FILE_2; - private static final String PROP_NAME_FONT_FILE_3 = DICT_KEY_FONT_FILE_3; - private static final String PROP_NAME_FONT_NAME = DICT_KEY_FONT_NAME; - private static final String PROP_NAME_PDF_METADATA = "PDFMetadata"; - private static final String PROP_NAME_LAST_MOD = "LastModified"; - private static final String PROP_NAME_OUTLINES = DICT_KEY_OUTLINES; - private static final String PROP_NAME_REGISTRY = DICT_KEY_REGISTRY; - private static final String PROP_NAME_SUPPLEMENT = DICT_KEY_SUPPLEMENT; - private static final String PROP_NAME_PAGES = DICT_KEY_PAGES; - private static final String PROP_NAME_SEQUENCE = "Sequence"; - private static final String PROP_NAME_ANNOTATIONS = "Annotations"; - private static final String PROP_NAME_ROTATE = DICT_KEY_ROTATE; - private static final String PROP_NAME_REPLY_TYPE = "ReplyType"; - private static final String PROP_NAME_VIEWPORT = "Viewport"; - private static final String PROP_NAME_VIEWPORTS = PROP_NAME_VIEWPORT + "s"; - private static final String PROP_NAME_THUMB = DICT_KEY_THUMB; - private static final String PROP_NAME_TO_UNICODE = DICT_KEY_TO_UNICODE; - private static final String PROP_NAME_PAGE = "Page"; - private static final String PROP_NAME_LABEL = "Label"; - private static final String PROP_NAME_RATIO = "Ratio"; - - private static final String PROP_VAL_CROP_BOX = "CropBox"; - private static final String PROP_VAL_FONT_BBOX = DICT_KEY_FONT_BBOX; - private static final String PROP_VAL_NULL = "null"; - private static final String PROP_VAL_EXTERNAL = "External"; - private static final String PROP_VAL_NO_FLAGS_SET = "No flags set"; - private static final String XOBJ_SUBTYPE_IMAGE = PROP_NAME_IMAGE; - private static final String EMPTY_LABEL_PROPERTY = "[empty]"; - - /****************************************************************** - * PRIVATE CLASS FIELDS. - ******************************************************************/ - - private static final String NAME = "PDF-hul"; - private static final String RELEASE = "1.12.2"; - private static final int [] DATE = { 2019, 12, 10 }; - private static final String[] FORMAT = { "PDF", - "Portable Document Format" }; - private static final String COVERAGE = "PDF 1.0-1.6; PDF/X-1 (ISO 15930-1:2001), X-1a (ISO 15930-4:2003), " - + "X-2 (ISO 15930-5:2003), and X-3 (ISO 15930-6:2003); Tagged PDF; " - + "Linearized PDF; PDF/A (ISO/CD 19005-1)"; - private static final String[] MIMETYPE = { MIME_TYPE }; - private static final String WELLFORMED = "A PDF file is " - + "well-formed if it meets the criteria defined in Chapter " - + "3 of the PDF Reference 1.6 (5th edition, 2004)"; - private static final String VALIDITY = null; - private static final String REPINFO = null; - private static final String NOTE = "This module does *not* validate " - + "data within content streams (including operators) or encrypted data"; - private static final String RIGHTS = "Copyright 2003-2007 by JSTOR and " - + "the President and Fellows of Harvard College. " - + "Released under the GNU Lesser General Public License."; - private static final String ENCRYPTED = ""; - - /** Logger for this class. */ - protected Logger _logger; - - /** Font type selectors. */ - public final static int F_TYPE0 = 1, F_TYPE1 = 2, F_TT = 3, F_TYPE3 = 4, - F_MM1 = 5, F_CID0 = 6, F_CID2 = 7; - - /****************************************************************** - * PRIVATE INSTANCE FIELDS. - ******************************************************************/ - - /* - * The maximum number of fonts that will be reported before we just - * give up and report a stub to avoid running out of memory. - */ - protected int DEFAULT_MAX_FONTS = 1000; - - /* Constants for trailer parsing */ - private static final int EOFSCANSIZE = 1024; - private static final int XREFSCANSIZE = 128; // generous... - - protected RandomAccessFile _raf; - protected Parser _parser; - protected String _version; - protected Property _metadata; - protected Property _xmpProp; - protected long _eof; - protected long _startxref; - protected long _prevxref; - protected int _numFreeObjects; - protected Property _idProperty; - protected int _objCount; // Count of objects in the cross-reference - // table - protected int _numObjects; // Value of the "Size" entry in the trailer - // dictionary - protected int _numTrailers; // Count of the number of trailers (updates) - protected Map _objects; // Map of the objects in the file - protected long[] _xref; // array of object offsets from xref table - protected int[][] _xref2; // array of int[2], giving object stream and - // offset when _xref[i] < 0 - protected boolean _xrefIsStream; // true if xref streams rather than tables - // are used - protected boolean _encrypted; // equivalent to _encryptDictRef != null - protected List _docCatalogList; // Info extracted from doc cat - // dict - protected List _encryptList; // Info from encryption dict - protected List _docInfoList; // info from doc info dict - protected List _extStreamsList; // List of external streams - protected List _imagesList; // List of image streams - protected List _filtersList; // List of filters - protected List _pagesList; // List of PageObjects - - protected Map _type0FontsMap; // Map of type 0 font - // dictionaries - protected Map _type1FontsMap; // Map of type 1 font - // dictionaries - protected Map _mmFontsMap; // Map of multi master - // font dictionaries - protected Map _type3FontsMap; // Map of type 3 font - // dictionaries - protected Map _trueTypeFontsMap; // Map of TrueType - // font dictionaries - protected Map _cid0FontsMap; // Map of CIDFont/Type1 - // dictionaries - protected Map _cid2FontsMap; // Map of - // CIDFont/TrueType - // dictionaries - - protected Map _pageSeqMap; // Map associating page - // object dicts with - // sequence numbers - - protected PdfIndirectObj _docCatDictRef; - protected PdfIndirectObj _encryptDictRef; - protected PdfIndirectObj _docInfoDictRef; - protected PdfIndirectObj _pagesDictRef; - - protected PdfDictionary _docCatDict; - protected PdfDictionary _docInfoDict; - protected PageTreeNode _docTreeRoot; - protected PdfDictionary _pageLabelDict; - protected PageLabelNode _pageLabelRoot; - protected NameTreeNode _embeddedFiles; - protected NameTreeNode _destNames; - protected PdfDictionary _encryptDict; - protected PdfDictionary _trailerDict; - protected PdfDictionary _viewPrefDict; - protected PdfDictionary _outlineDict; - protected PdfDictionary _destsDict; - - protected boolean _showFonts; - protected boolean _showOutlines; - protected boolean _showAnnotations; - protected boolean _showPages; - - protected boolean _actionsExist; - protected boolean _pdfACompliant; // flag checking PDF/A compliance - - protected boolean _recursionWarned; // Check if warning has been issued on - // recursive outlines. - - /* - * These three variables track whether a message has been posted - * notifying the user of omitted information. - */ - protected boolean _skippedFontsReported; - protected boolean _skippedOutlinesReported; - protected boolean _skippedAnnotationsReported; - protected boolean _skippedPagesReported; - - /** List of profile checkers */ - protected List _profile; - - /** Cached object stream. */ - protected ObjectStream _cachedObjectStream; - - /** Object number of cached object stream. */ - protected int _cachedStreamIndex; - - /** Map of visited nodes when walking through an outline. */ - protected Set _visitedOutlineNodes; - - /** maximum number of fonts to report full information on. */ - protected int maxFonts; - - /** Number of fonts reported so far. */ - protected int _nFonts; - - /* Name-to-value array pairs for NISO metadata */ - private final static String[] compressionStrings = { FILTER_NAME_LZW, - /* "FlateDecode", */ FILTER_NAME_RUN_LENGTH, FILTER_NAME_DCT, - FILTER_NAME_CCITT }; - private final static int[] compressionValues = { 5, /* 8, */ 32773, 6, 2 }; - /* - * The value of 2 (CCITTFaxDecode) is a placeholder; additional - * checking of the K parameter is needed to determine the real - * value if that's returned. - */ - - private final static String[] colorSpaceStrings = { "Lab", "DeviceRGB", - "DeviceCMYK", "DeviceGray", "Indexed" }; - private final static int[] colorSpaceValues = { 8, 2, 5, 1, 3 }; - - /****************************************************************** - * CLASS CONSTRUCTOR. - ******************************************************************/ - - /** - * Creates an instance of the module and initializes identifying - * information. - */ - public PdfModule() { - super(NAME, RELEASE, DATE, FORMAT, COVERAGE, MIMETYPE, WELLFORMED, - VALIDITY, REPINFO, NOTE, RIGHTS, true); - - _logger = Logger.getLogger("edu.harvard.hul.ois.jhove.module"); - - _vendor = Agent.harvardInstance(); - - Document doc = new Document("PDF Reference: Adobe Portable " - + "Document Format, Version 1.4", DocumentType.BOOK); - Agent agent = Agent.newAdobeInstance(); - doc.setPublisher(agent); - doc.setDate("2001-12"); - doc.setEdition("3rd edition"); - doc.setIdentifier(new Identifier("0-201-75839-3", IdentifierType.ISBN)); - doc.setIdentifier(new Identifier( - "http://partners.adobe.com/asn/" + "acrobat/docs/File_Format_" - + "Specifications/PDFReference.pdf", - IdentifierType.URL)); - _specification.add(doc); - - doc = new Document("PDF Reference: Adobe Portable " - + "Document Format, Version 1.5", DocumentType.BOOK); - doc.setPublisher(agent); - doc.setDate("2003"); - doc.setEdition("4th edition"); - doc.setIdentifier(new Identifier( - "http://partners.adobe.com/public/developer/en/pdf/PDFReference15_v6.pdf", - IdentifierType.URL)); - _specification.add(doc); - - doc = new Document("PDF Reference: Adobe Portable " - + "Document Format, Version 1.6", DocumentType.BOOK); - doc.setPublisher(agent); - doc.setDate("2004-11"); - doc.setEdition("5th edition"); - doc.setIdentifier(new Identifier( - "http://partners.adobe.com/public/developer/en/pdf/PDFReference16.pdf", - IdentifierType.URL)); - _specification.add(doc); - - doc = new Document("Graphic technology -- Prepress " - + "digital data exchange -- Use of PDF -- " - + "Part 1: Complete exchange using CMYK data " - + "(PDF/X-1 and PDF/X-1a)", DocumentType.STANDARD); - Agent isoAgent = Agent.newIsoInstance(); - doc.setPublisher(isoAgent); - doc.setDate("2001-12-06"); - doc.setIdentifier( - new Identifier("ISO 15930-1:2001", IdentifierType.ISO)); - _specification.add(doc); - - doc = new Document("Graphic technology -- Prepress " - + "digital data exchange -- Use of PDF -- " - + "Part 4: Complete exchange using CMYK and " - + "spot colour printing data using " + "PDF 1.4 (PDF/X-1a)", - DocumentType.STANDARD); - doc.setPublisher(isoAgent); - doc.setDate("2003-08-04"); - doc.setIdentifier( - new Identifier("ISO 15930-4:2003", IdentifierType.ISO)); - _specification.add(doc); - - doc = new Document("Graphic technology -- Prepress " - + "digital data exchange -- Use of PDF -- " - + "Part 5: Partial exchange of printing data " - + "using PDF 1.4 (PDF/X-2)", DocumentType.STANDARD); - doc.setPublisher(isoAgent); - doc.setDate("2003-08-05"); - doc.setIdentifier( - new Identifier("ISO 15930-5:2003", IdentifierType.ISO)); - _specification.add(doc); - - doc = new Document("Graphic technology -- Prepress " - + "digital data exchange -- Use of PDF -- " - + "Part 6: Complete exchange suitable for " - + "colour-managed workflows using " + "PDF 1.4 (PDF/X-3)", - DocumentType.STANDARD); - doc.setPublisher(isoAgent); - doc.setDate("2003-08-06"); - doc.setIdentifier( - new Identifier("ISO 15930-6:2003", IdentifierType.ISO)); - _specification.add(doc); - - _signature.add(new ExternalSignature(EXT, SignatureType.EXTENSION, - SignatureUseType.OPTIONAL)); - _signature.add(new InternalSignature(PdfHeader.PDF_SIG_HEADER, - SignatureType.MAGIC, SignatureUseType.MANDATORY, 0)); - - doc = new Document( - "Document management -- Electronic " - + "document file format for long-term " - + "preservation -- Part 1: Use of PDF (PDF/A)", - DocumentType.RFC); - doc.setPublisher(isoAgent); - doc.setDate("2003-11-30"); - doc.setIdentifier(new Identifier("ISO/CD 19005-1", IdentifierType.ISO)); - doc.setIdentifier(new Identifier( - "http://www.aiim.org/documents/standards/ISO_19005-1_(E).doc", - IdentifierType.URL)); - _specification.add(doc); - - _profile = new ArrayList(6); - _profile.add(new LinearizedProfile(this)); - TaggedProfile tpr = new TaggedProfile(this); - _profile.add(tpr); - AProfile apr = new AProfile(this); - _profile.add(apr); - // Link AProfile to TaggedProfile to save checking - // the former twice. - apr.setTaggedProfile(tpr); - - AProfileLevelA apra = new AProfileLevelA(this); - _profile.add(apra); - // AProfileLevelA depends on AProfile - apra.setAProfile(apr); - - X1Profile x1 = new X1Profile(this); - _profile.add(x1); - X1aProfile x1a = new X1aProfile(this); - _profile.add(x1a); - // Linking the X1 profile to the X1a profile saves checking the former - // twice. - x1a.setX1Profile(x1); - _profile.add(new X2Profile(this)); - _profile.add(new X3Profile(this)); - _showAnnotations = false; - _showFonts = false; - _showOutlines = false; - _showPages = false; - maxFonts = DEFAULT_MAX_FONTS; - } - - /****************************************************************** - * PUBLIC INSTANCE METHODS. - * - * Parsing methods. - ******************************************************************/ - - /** - * Reset parameter settings. - * Returns to a default state without any parameters. - */ - @Override - public void resetParams() { - _showAnnotations = true; - _showFonts = true; - _showOutlines = true; - _showPages = true; - maxFonts = DEFAULT_MAX_FONTS; - } - - /** - * Per-action initialization. May be called multiple times. - * - * @param param - * The module parameter; under command-line Jhove, the -p - * parameter. - * If the parameter contains the indicated characters, then the - * specified information is omitted; otherwise, it is included. - * (This is the reverse of the behavior prior to beta 3.) - * These characters may be provided as separate parameters, - * or all in a single parameter. - *

    - *
  • a: annotations
  • - *
  • f: fonts
  • - *
  • o: outlines
  • - *
  • p: pages
  • - *
- *
- * The parameter is case-independent. A null parameter is - * equivalent to the empty string. - */ - @Override - public void param(String param) { - if (param != null) { - param = param.toLowerCase(); - if (param.indexOf('a') >= 0) { - _showAnnotations = false; - } - if (param.indexOf('f') >= 0) { - _showFonts = false; - } - if (param.indexOf('o') >= 0) { - _showOutlines = false; - } - if (param.indexOf('p') >= 0) { - _showPages = false; - } - if (param.indexOf('n') >= 0) { - // Parse out the number after the n, and use that to set - // the maximum number of fonts reported. Default is - // DEFAULT_MAX_FONTS. - int n = param.indexOf('n'); - StringBuffer b = new StringBuffer(); - for (int i = n + 1; i < param.length(); i++) { - char ch = param.charAt(i); - if (Character.isDigit(ch)) { - b.append(ch); - } else { - break; - } - } - try { - int mx = Integer.parseInt(b.toString()); - if (mx > 0) { - maxFonts = mx; - } - } catch (Exception e) { - } - } - } - } - - /** - * Parses a file and stores descriptive information. A RandomAccessFile - * must be used to represent the object. - * - * @param raf - * A PDF file - * @param info - * A clean RepInfo object, which will be modified to hold - * the descriptive information - */ - @Override - public final void parse(RandomAccessFile raf, RepInfo info) - throws IOException { - initParse(); - initInfo(info); - _objects = new HashMap<>(); - _raf = raf; - - Tokenizer tok = new FileTokenizer(_raf); - _parser = new Parser(tok); - _parser.setObjectMap(_objects); - - List metadataList = new ArrayList(11); - /* - * We construct a big whopping property, - * which contains up to 11 subproperties - */ - _metadata = new Property(PROP_NAME_PDF_METADATA, PropertyType.PROPERTY, - PropertyArity.LIST, metadataList); - - if (_raf.length() > 10000000000L) { // that's 10^10 - _pdfACompliant = false; // doesn't meet size limit in Appendix C - // of PDF spec - } - if (!parseHeader(info)) { - return; - } - if (!findLastTrailer(info)) { - return; - } - - /* - * Walk through the linked trailer and cross reference - * sections. - */ - _prevxref = -1; - boolean lastTrailer = true; - while (_startxref > 0) { - // After the first (last) trailer, parse only for next "Prev" link - if (!parseTrailer(info, !lastTrailer)) { - return; - } - if (!readXRefInfo(info)) { - return; - } - ++_numTrailers; - if (_xrefIsStream) { - /* - * If we have an xref stream, readXRefInfo dealt with all - * the streams in a single call. - */ - break; - } - // Beware infinite loop on badly broken file - if (_startxref == _prevxref) { - info.setMessage(new ErrorMessage(MessageConstants.PDF_HUL_134, // PDF-HUL-134 - _parser.getOffset())); - info.setWellFormed(false); - return; - } - _startxref = _prevxref; - lastTrailer = false; - } - if (!readDocCatalogDict(info)) { - return; - } - if (!readEncryptDict(info)) { - return; - } - if (!readDocInfoDict(info)) { - return; - } - if (!readDocumentTree(info)) { - return; - } - if (!readPageLabelTree(info)) { - return; - } - if (!readXMPData(info)) { - return; - } - findExternalStreams(info); - if (!findFilters(info)) { - return; - } - findImages(info); - findFonts(info); - - /* Object is well-formed PDF. */ - - // Calculate checksums if not already present - checksumIfRafNotCopied(info, raf); - - info.setVersion(_version); - metadataList.add(new Property(PROP_NAME_OBJECTS, PropertyType.INTEGER, - new Integer(_numObjects))); - metadataList.add(new Property(PROP_NAME_FREE_OBJECTS, - PropertyType.INTEGER, new Integer(_numFreeObjects))); - metadataList.add(new Property(PROP_NAME_INC_UPDATES, - PropertyType.INTEGER, new Integer(_numTrailers))); - if (_docCatalogList != null) { - metadataList.add( - new Property(PROP_NAME_DOC_CATALOG, PropertyType.PROPERTY, - PropertyArity.LIST, _docCatalogList)); - } - if (_encryptList != null) { - metadataList.add(new Property(PROP_NAME_ENCRYPTION, - PropertyType.PROPERTY, PropertyArity.LIST, _encryptList)); - } - if (_docInfoList != null) { - metadataList.add(new Property(PROP_NAME_INFO, PropertyType.PROPERTY, - PropertyArity.LIST, _docInfoList)); - } - if (_idProperty != null) { - metadataList.add(_idProperty); - } - if (_extStreamsList != null && !_extStreamsList.isEmpty()) { - metadataList.add(new Property(PROP_NAME_EXTERNAL_STREAMS, - PropertyType.PROPERTY, PropertyArity.LIST, - _extStreamsList)); - } - if (_filtersList != null && !_filtersList.isEmpty()) { - metadataList.add(new Property(PROP_NAME_FILTERS, - PropertyType.PROPERTY, PropertyArity.LIST, _filtersList)); - } - if (_imagesList != null && !_imagesList.isEmpty()) { - metadataList.add(new Property(PROP_NAME_IMAGES, - PropertyType.PROPERTY, PropertyArity.LIST, _imagesList)); - } - if (_showFonts || _verbosity == Module.MAXIMUM_VERBOSITY) { - try { - addFontsProperty(metadataList); - } catch (NullPointerException e) { - info.setMessage(new ErrorMessage(MessageConstants.PDF_HUL_135, - e.toString())); // PDF-HUL-135 - } - } - if (_nFonts > maxFonts) { - info.setMessage(new InfoMessage(MessageConstants.PDF_HUL_136, // PDF-HUL-136 - MessageConstants.PDF_HUL_136_SUB.getMessage() + _nFonts)); - } - if (_xmpProp != null) { - metadataList.add(_xmpProp); - } - addPagesProperty(metadataList, info); - - if (!doOutlineStuff(info)) { - return; - } - - info.setProperty(_metadata); - - /* Check for profile conformance. */ - - if (!_parser.getPDFACompliant()) { - _pdfACompliant = false; - } - if (info.getWellFormed() == RepInfo.TRUE) { - // Well-formedness is necessary to satisfy any profile. - ListIterator pter = _profile.listIterator(); - while (pter.hasNext()) { - PdfProfile prof = pter.next(); - if (prof.satisfiesProfile(_raf, _parser)) { - info.setProfile(prof.getText()); - } - } - } - } - - /** - * Returns true if the module hasn't detected any violations - * of PDF/A compliance. This must return true, but is not - * sufficient by itself, to establish compliance. The - * AProfile profiler makes the final determination. - */ - public boolean mayBePDFACompliant() { - return _pdfACompliant; - } - - /** - * Returns the document tree root. - */ - public PageTreeNode getDocumentTree() { - return _docTreeRoot; - } - - /** - * Returns the document information dictionary. - */ - public PdfDictionary getDocInfo() { - return _docInfoDict; - } - - /** - * Returns the encryption dictionary. - */ - public PdfDictionary getEncryptionDict() { - return _encryptDict; - } - - /** - * Return true if Actions have been detected in the file. - */ - public boolean getActionsExist() { - return _actionsExist; - } - - /** - * Initialize the module. This is called at the start - * of parse restore the module to its initial state. - */ - @Override - protected final void initParse() { - super.initParse(); - _xref = null; - _xref2 = null; - _version = ""; - _objects = null; - _numFreeObjects = 0; - _objCount = 0; - _docInfoList = null; - _extStreamsList = null; - _docCatalogList = null; - _encryptList = null; - _imagesList = null; - _filtersList = null; - _pagesList = null; - _type0FontsMap = null; - _type1FontsMap = null; - _mmFontsMap = null; - _type3FontsMap = null; - _trueTypeFontsMap = null; - _cid0FontsMap = null; - _cid2FontsMap = null; - _docCatDictRef = null; - _encryptDictRef = null; - _docInfoDictRef = null; - _pagesDictRef = null; - _docCatDict = null; - _docInfoDict = null; - _docTreeRoot = null; - _pageLabelDict = null; - _encryptDict = null; - _trailerDict = null; - _viewPrefDict = null; - _outlineDict = null; - _destsDict = null; - _pageSeqMap = null; - _pageLabelRoot = null; - _embeddedFiles = null; - _destNames = null; - _skippedFontsReported = false; - _skippedOutlinesReported = false; - _skippedAnnotationsReported = false; - _skippedPagesReported = false; - _idProperty = null; - _actionsExist = false; - _numObjects = 0; - _numTrailers = -1; - _pdfACompliant = true; // assume compliance till disproven - _xmpProp = null; - _cachedStreamIndex = -1; - _nFonts = 0; - } - - protected boolean parseHeader(RepInfo info) throws IOException { - PdfHeader header = PdfHeader.parseHeader(_parser); - if (header == null) { - info.setWellFormed(false); - info.setMessage(new ErrorMessage(MessageConstants.PDF_HUL_137, 0L)); // PDF-HUL-137 - return false; - } - if (!header.isVersionValid()) { - info.setValid(false); - info.setMessage(new ErrorMessage(MessageConstants.PDF_HUL_148, 0L)); // PDF-HUL-148 - } - _version = header.getVersionString(); - _pdfACompliant = header.isPdfACompliant(); - info.setSigMatch(_name); - return true; - } - - private long lastEOFOffset(RandomAccessFile raf) throws IOException { - - long offset = 0; - long flen = 0; - byte[] buf = null; - - // overkill to restore fileposition, but make this - // as side-effect free as possible - long savepos = raf.getFilePointer(); - flen = raf.length(); - buf = new byte[(int) Math.min(EOFSCANSIZE, flen)]; - offset = flen - buf.length; - raf.seek(offset); - raf.read(buf); - raf.seek(savepos); - - // OK: - // flen is the total length of the file - // offset is 1024 bytes from the end of file or 0 if file is shorter - // than 1024 - // buf contains all bytes from offset to end of file - - long eofpos = -1; - // Note the limits, selected so the index never is out of bounds - for (int i = buf.length - 4; i >= 1; i--) { - if (buf[i] == '%') { - if ((buf[i - 1] == '%') && (buf[i + 1] == 'E') - && (buf[i + 2] == 'O') && (buf[i + 3] == 'F')) { - eofpos = offset + i - 1; - break; - } - } - } - - // if (Tracing.T_MODULE) System.out.println(flen - eofpos); - return eofpos; - - } - - private long lastStartXrefOffset(RandomAccessFile raf, long eofOffset) - throws IOException { - - long offset = 0; - long flen = 0; - byte[] buf = null; - - // overkill to restore fileposition, but make this - // as side-effect free as possible - long savepos = raf.getFilePointer(); - flen = raf.length(); - if (eofOffset <= 0) { - eofOffset = flen; - } - if (eofOffset >= flen) { - eofOffset = flen; - } - buf = new byte[(int) Math.min(XREFSCANSIZE, eofOffset)]; - offset = eofOffset - buf.length; - raf.seek(offset); - raf.read(buf); - raf.seek(savepos); - - // OK: - // flen is the total length of the file - // offset is 128 bytes from the end of file or 0 if file is shorter than - // 128 - // buf contains all bytes from offset to end of file - - long xrefpos = -1; - // Note the limits, selected so the index never is out of bounds - for (int i = buf.length - 9; i >= 0; i--) { - if (buf[i] == 's') { - if ((buf[i + 1] == 't') && (buf[i + 2] == 'a') - && (buf[i + 3] == 'r') && (buf[i + 4] == 't') - && (buf[i + 5] == 'x') && (buf[i + 6] == 'r') - && (buf[i + 7] == 'e') && (buf[i + 8] == 'f')) { - xrefpos = offset + i; - break; - } - } - } - - // if (Tracing.T_MODULE) System.out.println(flen - xrefpos); - return xrefpos; - - } - - /** Locate the last trailer of the file */ - protected boolean findLastTrailer(RepInfo info) throws IOException { - /* - * Parse file trailer. Technically, this should be the last thing in - * the file, but we follow the Acrobat convention of looking in the - * last 1024 bytes. Since incremental updates may add multiple - * EOF comments, make sure that we use the last one in the file. - */ - - Token token = null; - String value = null; - - _eof = lastEOFOffset(_raf); - - if (_eof < 0L) { - info.setWellFormed(false); - info.setMessage(new ErrorMessage(MessageConstants.PDF_HUL_138, - _raf.length())); // PDF-HUL-138 - return false; - } - - // For PDF-A compliance, this must be at the very end. - /* - * Fix contributed by FCLA, 2007-05-30, to test for trailing data - * properly. - * - * if (_raf.length () - _eof > 6) { - */ - if (_raf.length() - _eof > 7) { - _pdfACompliant = false; - } - - /* Retrieve the "startxref" keyword. */ - - long startxrefoffset = lastStartXrefOffset(_raf, _eof); - _startxref = -1L; - - if (startxrefoffset >= 0) { - try { - _parser.seek(startxrefoffset); // points to the 'startxref' kw - // _parser.seek(_eof - 23); // should we allow more slop? - } catch (PdfException e) { - } - while (true) { - try { - token = _parser.getNext(); - } catch (Exception e) { - // we're starting at an arbitrary point, so there - // can be parsing errors. Ignore them till we get - // back in sync. - continue; - } - if (token == null) { - break; - } - if (token instanceof Keyword) { - value = ((Keyword) token).getValue(); - if (DICT_KEY_STARTXREF.equals(value)) { - try { - token = _parser.getNext(); - } catch (Exception e) { - break; // no excuses here - } - if (token != null && token instanceof Numeric) { - _startxref = ((Numeric) token).getLongValue(); - } - } - } - } - } - if (_startxref < 0L) { - info.setWellFormed(false); - info.setMessage(new ErrorMessage(MessageConstants.PDF_HUL_139, // PDF-HUL-139 - _parser.getOffset())); - return false; - } - return true; - } - - /* - * Parse a "trailer" (which is not necessarily the last - * thing in the file, as trailers can be linked.) - */ - protected boolean parseTrailer(RepInfo info, boolean prevOnly) - throws IOException { - Token token = null; - String value = null; - /* Parse the trailer dictionary. */ - - try { - _parser.seek(_startxref); - /* - * The next object may be either the keyword "xref", signifying - * a classic cross-reference table, or a stream object, - * signifying the new-style cross-reference stream. - */ - Token xref = _parser.getNext(); - if (xref instanceof Keyword) { - _xrefIsStream = false; - _parser.getNext(Numeric.class, // PDF-HUL-68 - MessageConstants.PDF_HUL_68); // first obj number - - _objCount = ((Numeric) _parser.getNext(Numeric.class, // PDF-HUL-69 - MessageConstants.PDF_HUL_69)).getIntegerValue(); - _parser.seek(_parser.getOffset() + _objCount * 20); - } else if (xref instanceof Numeric) { - /* No cross-ref tables to backtrack. */ - _xrefIsStream = true; - _prevxref = -1; - /* - * But I do need to read the dictionary at this point, to get - * essential stuff out of it. - */ - PdfObject pdfStreamObj = _parser.readObjectDef((Numeric) xref); - // the retrieved object should be stream - if (!(pdfStreamObj instanceof PdfStream)) { - throw new PdfInvalidException(MessageConstants.PDF_HUL_150, - _parser.getOffset()); - } - PdfDictionary dict = ((PdfStream) pdfStreamObj).getDict(); - _docCatDictRef = (PdfIndirectObj) dict.get(DICT_KEY_ROOT); - if (_docCatDictRef == null) { - throw new PdfInvalidException(MessageConstants.PDF_HUL_70, // PDF-HUL-70 - _parser.getOffset()); - } - /* - * We don't need to see a trailer dictionary. - * Move along, move along. - */ - return true; - } - - /* Now find the "trailer" keyword. */ - long trailer = -1L; - while ((token = _parser.getNext()) != null) { - if (token instanceof Keyword) { - value = ((Keyword) token).getValue(); - if (DICT_KEY_TRAILER.equals(value)) { - token = _parser.getNext(); - if (token instanceof DictionaryStart) { - trailer = _parser.getOffset() - 7L; - break; - } - } - } - } - if (trailer < 0L) { - info.setWellFormed(false); - info.setMessage(new ErrorMessage(MessageConstants.PDF_HUL_71, // PDF-HUL-71 - _parser.getOffset())); - return false; - } - - _trailerDict = _parser.readDictionary(); - PdfObject obj; - - // Extract contents of the trailer dictionary - - _prevxref = -1; - obj = _trailerDict.get(DICT_KEY_PREV); - if (obj != null) { - if (obj instanceof PdfSimpleObject) { - token = ((PdfSimpleObject) obj).getToken(); - if (token instanceof Numeric) - _prevxref = ((Numeric) token).getLongValue(); - } - if (_prevxref < 0) { - throw new PdfInvalidException(MessageConstants.PDF_HUL_72, // PDF-HUL-72 - _parser.getOffset()); - } - } - // If this isn't the last (first read) trailer, then we - // ignore all the other dictionary entries. - if (prevOnly) { - return true; - } - - obj = _trailerDict.get(DICT_KEY_SIZE); - _docCatDictRef = (PdfIndirectObj) _trailerDict.get(DICT_KEY_ROOT); - if (obj != null) { - _numObjects = -1; - if (obj instanceof PdfSimpleObject) { - token = ((PdfSimpleObject) obj).getToken(); - if (token instanceof Numeric) - _numObjects = ((Numeric) token).getIntegerValue(); - _xref = new long[_numObjects]; - } - if (_numObjects < 0) { - throw new PdfInvalidException(MessageConstants.PDF_HUL_73, // PDF-HUL-73 - _parser.getOffset()); - } - if (_numObjects > 8388607) { - // Appendix C implementation limit is enforced by PDF/A - _pdfACompliant = false; - } - } else - throw new PdfInvalidException(MessageConstants.PDF_HUL_74, // PDF-HUL-74 - _parser.getOffset()); - - - if (_docCatDictRef == null) { - throw new PdfInvalidException(MessageConstants.PDF_HUL_75, // PDF-HUL-75 - _parser.getOffset()); - } - _encryptDictRef = (PdfIndirectObj) _trailerDict - .get(DICT_KEY_ENCRYPT); // This is at least v. 1.1 - _encrypted = (_encryptDictRef != null); - - PdfObject infoObj = _trailerDict.get(DICT_KEY_INFO); - if (infoObj != null && !(infoObj instanceof PdfIndirectObj)) { - throw new PdfInvalidException(MessageConstants.PDF_HUL_76, // PDF-HUL-76 - _parser.getOffset()); - } - _docInfoDictRef = (PdfIndirectObj) infoObj; - - obj = _trailerDict.get(DICT_KEY_ID); // This is at least v. 1.1 - if (obj != null) { - if (obj instanceof PdfArray) { - String[] id = new String[2]; - try { - PdfArray idArray = (PdfArray) obj; - Vector idVec = idArray.getContent(); - if (idVec.size() != 2) { - throw new PdfInvalidException( - MessageConstants.PDF_HUL_77); // PDF-HUL-77 - } - PdfSimpleObject idobj = (PdfSimpleObject) idVec.get(0); - id[0] = toHex(((StringValuedToken) idobj.getToken()) - .getRawBytes()); - idobj = (PdfSimpleObject) idVec.get(1); - id[1] = toHex(((StringValuedToken) idobj.getToken()) - .getRawBytes()); - _idProperty = new Property(DICT_KEY_ID, - PropertyType.STRING, PropertyArity.ARRAY, id); - } catch (Exception e) { - throw new PdfInvalidException( - MessageConstants.PDF_HUL_78); // PDF-HUL-78 - } - } else { - throw new PdfInvalidException(MessageConstants.PDF_HUL_79, - _parser.getOffset()); // PDF-HUL-79 - } - } - obj = _trailerDict.get(DICT_KEY_XREF_STREAM); - if (obj != null) { - /* - * We have a "hybrid" cross-reference scheme. This means we have - * to go through the cross-reference stream and have its entries - * supplement the cross-reference section. - */ - _logger.warning("Hybrid cross-reference not yet implemented"); - } - } catch (PdfException e) { - - e.disparage(info); - if (e.getJhoveMessage() != null) - info.setMessage(new ErrorMessage(e.getJhoveMessage(), _parser.getOffset())); - else - info.setMessage( - new ErrorMessage(e.getMessage(), _parser.getOffset())); - // If it's merely invalid rather than ill-formed, keep going - return (e instanceof PdfInvalidException); - } - return true; - } - - /* Parses the cross-reference table or stream. */ - protected boolean readXRefInfo(RepInfo info) throws IOException { - if (_xrefIsStream) { - return readXRefStreams(info); - } - return readXRefTables(info); - } - - /* - * Parses the cross-reference streams. This is called from - * readXRefInfo if there is no cross-reference table. - * I still need to deal with hybrid cases. All linked cross-reference - * streams are handled here. - */ - protected boolean readXRefStreams(RepInfo info) throws IOException { - _pdfACompliant = false; // current version of PDF/A doesn't recognize - // XREF streams - while (_startxref > 0) { - try { - _parser.seek(_startxref); - PdfObject pdfStreamObj = _parser.readObjectDef(); - // the retrieved object should be stream - if (!(pdfStreamObj instanceof PdfStream)) { - throw new PdfInvalidException(MessageConstants.PDF_HUL_150, - _parser.getOffset()); - } - PdfStream pstream = (PdfStream) pdfStreamObj; - int sObjNum = pstream.getObjNumber(); - CrossRefStream xstream = new CrossRefStream(pstream); - if (!xstream.isValid()) { - return false; - } - xstream.initRead(_raf); - int xrefSize = xstream.getCrossRefTableSize(); - if (_xref == null) { - _xref = new long[xrefSize]; - _xref2 = new int[xrefSize][]; - } - if (sObjNum < 0 || sObjNum >= xrefSize) { - throw new PdfMalformedException(MessageConstants.PDF_HUL_80, // PDF-HUL-80 - _parser.getOffset()); - } - _xref[sObjNum] = _startxref; // insert the index of the xref - // stream itself - _startxref = xstream.getPrevXref(); - try { - while (xstream.readNextObject()) { - int objNum = xstream.getObjNum(); - if (xstream.isObjCompressed()) { - // Hold off on this branch - _xref[objNum] = -1; // defers to _xref2 - _xref2[objNum] = new int[] { - xstream.getContentStreamObjNum(), - xstream.getContentStreamIndex() }; - } else { - if (_xref[objNum] == 0) { - _xref[objNum] = xstream.getOffset(); - } - } - } - _numFreeObjects += xstream.getFreeCount(); - } catch (IOException e) { - info.setWellFormed(false); - info.setMessage( - new ErrorMessage(MessageConstants.PDF_HUL_81, // PDF-HUL-81 - _parser.getOffset())); - return false; - } - } catch (PdfException e) { - - e.disparage(info); - if (e.getJhoveMessage() != null) - info.setMessage(new ErrorMessage(e.getJhoveMessage(), _parser.getOffset())); - else - info.setMessage( - new ErrorMessage(e.getMessage(), _parser.getOffset())); - // If it's merely invalid rather than ill-formed, keep going - return (e instanceof PdfInvalidException); - } - } - return true; // incomplete, but let it through - } - - /* - * Parses the cross-reference table. This is called from - * readXRefInfo if there is a cross-reference table. - */ - protected boolean readXRefTables(RepInfo info) throws IOException { - Token token = null; - try { - _parser.seek(_startxref); - token = _parser.getNext(); // "xref" keyword or numeric - if (token instanceof Keyword) { - while ((token = _parser.getNext()) != null) { - int firstObj = 0; - // Look for the start of a cross-ref subsection, which - // begins with a base object number and a count. - if (token instanceof Numeric) { - firstObj = ((Numeric) token).getIntegerValue(); - } else { - // On anything else, assume we're done with this - // section. - // (Most likely we've hit the keyword "trailer". - break; - } - _objCount = ((Numeric) _parser.getNext()).getIntegerValue(); - if (_xref == null) { - _xref = new long[_objCount]; - } - for (int i = 0; i < _objCount; i++) { - // In reading the cross-reference table, also check - // the extra syntactic requirements of PDF/A. - long offset = ((Numeric) _parser.getNext(Numeric.class, - MessageConstants.PDF_HUL_82)).getLongValue(); // PDF-HUL-82 - _parser.getNext(); // Generation number - if (_parser.getWSString().length() > 1) { - _pdfACompliant = false; - } - token = _parser.getNext(Keyword.class, - MessageConstants.PDF_HUL_83); // PDF-HUL-83 - if (_parser.getWSString().length() > 1) { - _pdfACompliant = false; - } - // A keyword of "n" signifies an object in use, - // "f" signifies a free object. If we already - // have an entry for this object, don't replace it. - String keyval = ((Keyword) token).getValue(); - if ("n".equals(keyval)) { - if (_xref[firstObj + i] == 0) { - _xref[firstObj + i] = offset; - } - } else if ("f".equals(keyval)) { - _numFreeObjects++; - } else { - throw new PdfMalformedException( - MessageConstants.PDF_HUL_84, // PDF-HUL-84 - _parser.getOffset()); - } - } - } - } - } catch (PdfException e) { - e.disparage(info); - if (e.getJhoveMessage() != null) - info.setMessage(new ErrorMessage(e.getJhoveMessage(), _parser.getOffset())); - else - info.setMessage( - new ErrorMessage(e.getMessage(), _parser.getOffset())); - return false; - } catch (Exception e) { - info.setValid(false); - info.setMessage( - new ErrorMessage(e.getMessage(), _parser.getOffset())); - } - return true; - } - - private boolean readDocCatalogDict(RepInfo info) throws IOException { - Property p = null; - _docCatDict = null; - _docCatalogList = new ArrayList(2); - // Get the Root reference which we had before, and - // resolve it to the dictionary object. - if (_docCatDictRef == null) { - info.setWellFormed(false); - info.setMessage(new ErrorMessage(MessageConstants.PDF_HUL_85, 0)); // PDF-HUL-85 - return false; - } - try { - _docCatDict = (PdfDictionary) resolveIndirectObject(_docCatDictRef); - } catch (Exception e) { - _logger.warning("Tried to cast non-dictionary to PdfDictionary"); - e.printStackTrace(); - } - if (_docCatDict == null) { - // If no object was returned, the PDF's not well-formed - info.setWellFormed(false); - info.setMessage(new ErrorMessage(MessageConstants.PDF_HUL_86, 0)); // PDF-HUL-86 - return false; - } else if (_docCatDict.getObjNumber() != _docCatDictRef - .getObjNumber()) { - // If the returned object nmumber is not the same as that requested - _logger.warning(String - .format("Inconsistent Document Catalog Object Number")); - _logger.warning(String.format( - " - /Root indirect reference number: %d, returned object ID: %d.", - _docCatDictRef.getObjNumber(), _docCatDict.getObjNumber())); - info.setWellFormed(false); - info.setMessage(new ErrorMessage(MessageConstants.PDF_HUL_140, 0)); // PDF-HUL-140 - return false; - } - try { - // Check that the catalog has a key type and the types value is - // "Catalog" - if (!checkTypeKey(_docCatDict, info, KEY_VAL_CATALOG, - MessageConstants.PDF_HUL_141, // PDF-HUL-141 - MessageConstants.PDF_HUL_142, // PDF-HUL-142 - MessageConstants.PDF_HUL_143)) { // PDF-HUL-143 - return false; - } - - PdfObject viewPref = _docCatDict.get(DICT_KEY_VIEWER_PREFS); - viewPref = resolveIndirectObject(viewPref); - if (viewPref instanceof PdfDictionary) { - _viewPrefDict = (PdfDictionary) viewPref; - p = buildViewPrefProperty(_viewPrefDict); - _docCatalogList.add(p); - } - String pLayoutText = DEFAULT_PAGE_LAYOUT; // default - PdfObject pLayout = resolveIndirectObject( - _docCatDict.get(DICT_KEY_PAGE_LAYOUT)); - if (pLayout instanceof PdfSimpleObject) { - pLayoutText = ((PdfSimpleObject) pLayout).getStringValue(); - } - p = new Property(PROP_NAME_PAGE_LAYOUT, PropertyType.STRING, - pLayoutText); - _docCatalogList.add(p); - - String pModeText = DEFAULT_MODE; // default - PdfObject pMode = resolveIndirectObject( - _docCatDict.get(DICT_KEY_PAGE_MODE)); - if (pMode instanceof PdfSimpleObject) { - pModeText = ((PdfSimpleObject) pMode).getStringValue(); - } - p = new Property(DICT_KEY_PAGE_MODE, PropertyType.STRING, - pModeText); - _docCatalogList.add(p); - - PdfObject outlines = resolveIndirectObject( - _docCatDict.get(DICT_KEY_OUTLINES)); - if (outlines instanceof PdfDictionary) { - _outlineDict = (PdfDictionary) outlines; - } - - PdfObject lang = resolveIndirectObject( - _docCatDict.get(DICT_KEY_LANG)); - if (lang != null && lang instanceof PdfSimpleObject) { - String langText = ((PdfSimpleObject) lang).getStringValue(); - p = new Property(PROP_NAME_LANG, PropertyType.STRING, - _encrypted ? ENCRYPTED : langText); - _docCatalogList.add(p); - } - - // The Pages dictionary doesn't go into the property, - // but this is a convenient time to grab it and the page label - // dictionary. - _pagesDictRef = (PdfIndirectObj) _docCatDict.get(DICT_KEY_PAGES); - _pageLabelDict = (PdfDictionary) resolveIndirectObject( - _docCatDict.get(DICT_KEY_PAGE_LABELS)); - - // Grab the Version entry, and use it to override the - // file header IF it's later. - PdfObject vers = resolveIndirectObject( - _docCatDict.get(DICT_KEY_VERSION)); - if (vers instanceof PdfSimpleObject) { - String versString = ((PdfSimpleObject) vers).getStringValue(); - String infoVersString = _version; - try { - double ver = Double.parseDouble(versString); - double infoVer = Double.parseDouble(infoVersString); - /* Set a message if this doesn't agree with RepInfo */ - if (ver != infoVer) { - String mess = MessageFormat.format( - MessageConstants.PDF_HUL_87.getMessage(), - infoVersString, versString); - JhoveMessage message = JhoveMessages.getMessageInstance( - MessageConstants.PDF_HUL_87.getId(), mess); - info.setMessage(new InfoMessage(message)); - } - /* Replace the version in RepInfo if this is larger */ - if (ver > infoVer) { - _version = versString; - } - } catch (NumberFormatException e) { - throw new PdfInvalidException(MessageConstants.PDF_HUL_88); // PDF-HUL-88 - } - } - - // Get the Names dictionary in order to grab the - // EmbeddedFiles and Dests entries. - try { - PdfDictionary namesDict = (PdfDictionary) resolveIndirectObject( - _docCatDict.get(DICT_KEY_NAMES)); - if (namesDict != null) { - PdfDictionary embeddedDict = (PdfDictionary) resolveIndirectObject( - namesDict.get(DICT_KEY_EMBEDDED_FILES)); - if (embeddedDict != null) { - _embeddedFiles = new NameTreeNode(this, null, - embeddedDict); - } - - PdfDictionary dDict = (PdfDictionary) resolveIndirectObject( - namesDict.get(DICT_KEY_DESTS)); - if (dDict != null) { - _destNames = new NameTreeNode(this, null, dDict); - } - } - } catch (ClassCastException ce) { - _logger.info("ClassCastException on names dictionary"); - throw new PdfInvalidException(MessageConstants.PDF_HUL_89); // PDF-HUL-89 - } catch (Exception e) { - _logger.info("Exception on names dictionary: " - + e.getClass().getName()); - throw new PdfMalformedException(MessageConstants.PDF_HUL_90); // PDF-HUL-90 - } - - // Get the optional Dests dictionary. Note that destinations - // may be specified in either of two completely different - // ways: a dictionary here, or a name tree from the Names - // dictionary. - - try { - _destsDict = (PdfDictionary) resolveIndirectObject( - _docCatDict.get(DICT_KEY_DESTS)); - } catch (ClassCastException ce) { - _logger.info("ClassCastException on dests dictionary"); - throw new PdfInvalidException(MessageConstants.PDF_HUL_91); // PDF-HUL-91 - } catch (Exception e) { - _logger.info("Exception on dests dictionary: " - + e.getClass().getName()); - throw new PdfMalformedException(MessageConstants.PDF_HUL_92); // PDF-HUL-92 - } - } - - catch (PdfException e) { - e.disparage(info); // clears Valid or WellFormed as appropriate - if (e.getJhoveMessage() != null) - info.setMessage(new ErrorMessage(e.getJhoveMessage(), _parser.getOffset())); - else - info.setMessage( - new ErrorMessage(e.getMessage(), _parser.getOffset())); - // Keep going if it's only invalid - return (e instanceof PdfInvalidException); - } catch (Exception e) { - // Unexpected exception -- declare not well-formed - info.setWellFormed(false); - info.setMessage( - new ErrorMessage(e.toString(), _parser.getOffset())); - return false; - } - return true; - } - - protected boolean readEncryptDict(RepInfo info) throws IOException { - String filterText = ""; - String effText = null; - // Get the reference which we had before, and - // resolve it to the dictionary object. - if (_encryptDictRef == null) { - return true; // encryption entry is optional - } - try { - _encryptList = new ArrayList(6); - PdfDictionary dict = (PdfDictionary) resolveIndirectObject( - _encryptDictRef); - _encryptDict = dict; - - PdfObject filter = dict.get(DICT_KEY_FILTER); - if (filter instanceof PdfSimpleObject) { - Token tok = ((PdfSimpleObject) filter).getToken(); - if (tok instanceof Name) { - filterText = ((Name) tok).getValue(); - } - } - Property p = new Property(PROP_NAME_SECURITY_HANDLER, - PropertyType.STRING, filterText); - _encryptList.add(p); - // PdfObject eff = dict.get("EFF"); - if (filter instanceof PdfSimpleObject) { - Token tok = ((PdfSimpleObject) filter).getToken(); - if (tok instanceof Name) { - effText = ((Name) tok).getValue(); - } - } - if (effText != null) { - p = new Property(PROP_NAME_EFF, PropertyType.STRING, effText); - _encryptList.add(p); - } - - int algValue = 0; - PdfObject algorithm = dict.get(DICT_KEY_V); - if (algorithm instanceof PdfSimpleObject) { - Token tok = ((PdfSimpleObject) algorithm).getToken(); - if (tok instanceof Numeric) { - algValue = ((Numeric) tok).getIntegerValue(); - if (_je != null && _je.getShowRawFlag()) { - p = new Property(PROP_NAME_ALGORITHM, - PropertyType.INTEGER, new Integer(algValue)); - } else { - try { - p = new Property(PROP_NAME_ALGORITHM, - PropertyType.STRING, - PdfStrings.ALGORITHM[algValue]); - } catch (ArrayIndexOutOfBoundsException aioobe) { - throw new PdfInvalidException // PDF-HUL-93 - (MessageConstants.PDF_HUL_93, _parser.getOffset()); - } - } - if (p != null) { - _encryptList.add(p); - } - } - } - - int keyLen = 40; - PdfObject length = dict.get(DICT_KEY_LENGTH); - if (length instanceof PdfSimpleObject) { - Token tok = ((PdfSimpleObject) length).getToken(); - if (tok instanceof Numeric) { - keyLen = ((Numeric) tok).getIntegerValue(); - } - if (_je != null) { - p = new Property(PROP_NAME_KEY_LENGTH, PropertyType.INTEGER, - new Integer(keyLen)); - _encryptList.add(p); - } - } - - if (FILTER_VAL_STANDARD.equals(filterText)) { - List stdList = new ArrayList(4); - // Flags have a known meaning only if Standard - // security handler was specified - PdfObject flagObj = dict.get(DICT_KEY_P); - PdfObject revObj = dict.get(DICT_KEY_R); - int rev = 2; // assume old rev if not present - if (revObj instanceof PdfSimpleObject) { - rev = ((PdfSimpleObject) revObj).getIntValue(); - } - if (flagObj instanceof PdfSimpleObject) { - int flags = ((PdfSimpleObject) flagObj).getIntValue(); - String[] flagStrs; - if (rev == 2) { - flagStrs = PdfStrings.USERPERMFLAGS2; - } else { - flagStrs = PdfStrings.USERPERMFLAGS3; - } - p = buildUserPermProperty(flags, flagStrs); - stdList.add(p); - - stdList.add(new Property(PROP_NAME_REVISION, - PropertyType.INTEGER, new Integer(rev))); - } - PdfObject oObj = dict.get("O"); - if (oObj != null) { - if (oObj instanceof PdfSimpleObject) { - stdList.add(new Property(PROP_NAME_OWNER_STRING, - PropertyType.STRING, - toHex(((PdfSimpleObject) oObj).getRawBytes()))); - } - } - PdfObject uObj = dict.get("U"); - if (uObj != null) { - if (uObj instanceof PdfSimpleObject) { - stdList.add(new Property(PROP_NAME_USER_STRING, - PropertyType.STRING, - toHex(((PdfSimpleObject) uObj).getRawBytes()))); - } - } - // Required if ExtensionLevel 3 and Encryption Algorithm (V) is 5 - // Defined in Adobe® Supplement to the ISO 32000 - if (algValue == 5) { - PdfObject oeObj = dict.get("OE"); - if (oeObj != null) { - if (oeObj instanceof PdfSimpleObject) { - stdList.add(new Property(PROP_NAME_OWNERKEY_STRING, - PropertyType.STRING, - toHex(((PdfSimpleObject) oeObj).getRawBytes()))); - } - } else { - // if algValue is 5; OE is mandatory - throw new PdfInvalidException - (MessageConstants.PDF_HUL_152, _parser.getOffset()); - } - PdfObject ueObj = dict.get("UE"); - if (ueObj != null) { - if (ueObj instanceof PdfSimpleObject) { - stdList.add(new Property(PROP_NAME_USERKEY_STRING, - PropertyType.STRING, - toHex(((PdfSimpleObject) ueObj).getRawBytes()))); - } - } else { - // if algValue is 5; UE is mandatory - throw new PdfInvalidException - (MessageConstants.PDF_HUL_153, _parser.getOffset()); - } - } - _encryptList.add(new Property( - PROP_NAME_STANDARD_SECURITY_HANDLER, - PropertyType.PROPERTY, PropertyArity.LIST, stdList)); - } - - } catch (PdfException e) { - e.disparage(info); - if (e.getJhoveMessage() != null) - info.setMessage(new ErrorMessage(e.getJhoveMessage(), _parser.getOffset())); - else - info.setMessage( - new ErrorMessage(e.getMessage(), _parser.getOffset())); - return (e instanceof PdfInvalidException); - } - return true; - } - - protected boolean readDocInfoDict(RepInfo info) { - // Get the Info reference which we had before, and - // resolve it to the dictionary object. - if (_docInfoDictRef == null) { - return true; // Info is optional - } - _docInfoList = new ArrayList(9); - try { - _docInfoDict = (PdfDictionary) resolveIndirectObject( - _docInfoDictRef); - - addStringProperty(_docInfoDict, _docInfoList, DICT_KEY_TITLE, - PROP_NAME_TITLE); - addStringProperty(_docInfoDict, _docInfoList, DICT_KEY_AUTHOR, - PROP_NAME_AUTHOR); - addStringProperty(_docInfoDict, _docInfoList, DICT_KEY_SUBJECT, - PROP_NAME_SUBJECT); - addStringProperty(_docInfoDict, _docInfoList, DICT_KEY_KEYWORDS, - PROP_NAME_KEYWORDS); - addStringProperty(_docInfoDict, _docInfoList, DICT_KEY_CREATOR, - PROP_NAME_CREATOR); - addStringProperty(_docInfoDict, _docInfoList, DICT_KEY_PRODUCER, - PROP_NAME_PRODUCER); - // CreationDate requires string-to-date conversion - // ModDate does too - addDateProperty(_docInfoDict, _docInfoList, DICT_KEY_CREATION_DATE, - PROP_NAME_CREATION_DATE); - addDateProperty(_docInfoDict, _docInfoList, DICT_KEY_MODIFIED_DATE, - PROP_NAME_MODIFIED_DATE); - addStringProperty(_docInfoDict, _docInfoList, DICT_KEY_TRAPPED, - PROP_NAME_TRAPPED); - } catch (PdfException e) { - e.disparage(info); - if (e.getJhoveMessage() != null) - info.setMessage(new ErrorMessage(e.getJhoveMessage(), _parser.getOffset())); - else - info.setMessage( - new ErrorMessage(e.getMessage(), _parser.getOffset())); - // Keep parsing if it's only invalid - return (e instanceof PdfInvalidException); - } catch (Exception e) { - info.setWellFormed(false); - String mess = MessageFormat.format( - MessageConstants.PDF_HUL_94.getMessage(), - e.getClass().getName()); - JhoveMessage message = JhoveMessages.getMessageInstance( - MessageConstants.PDF_HUL_94.getId(), mess); - info.setMessage(new ErrorMessage(message)); // PDF-HUL-94 - } - return true; - } - - protected boolean readDocumentTree(RepInfo info) { - try { - if (_pagesDictRef == null) { - throw new PdfInvalidException(MessageConstants.PDF_HUL_95); // PDF-HUL-95 - } - - PdfObject pagesObj = resolveIndirectObject(_pagesDictRef); - if (!(pagesObj instanceof PdfDictionary)) - throw new PdfMalformedException(MessageConstants.PDF_HUL_97); // PDF-HUL-97 - PdfDictionary pagesDict = (PdfDictionary) pagesObj; - - // Check that the pages dict has a key type and the types value is - // Pages - if (!checkTypeKey(pagesDict, info, KEY_VAL_PAGES, - MessageConstants.PDF_HUL_146, // PDF-HUL-146 - MessageConstants.PDF_HUL_144, // PDF-HUL-144 - MessageConstants.PDF_HUL_145)) { // PDF-HUL-145 - return false; - } - - _docTreeRoot = new PageTreeNode(this, null, pagesDict); - _docTreeRoot.buildSubtree(true, MAX_PAGE_TREE_DEPTH); - } catch (PdfException e) { - e.disparage(info); - if (e.getJhoveMessage() != null) - info.setMessage(new ErrorMessage(e.getJhoveMessage(), _parser.getOffset())); - else - info.setMessage( - new ErrorMessage(e.getMessage(), _parser.getOffset())); - // Continue parsing if it's only invalid - return (e instanceof PdfInvalidException); - } catch (ArrayIndexOutOfBoundsException excep) { - info.setMessage(new ErrorMessage(MessageConstants.PDF_HUL_96, - _parser.getOffset())); // PDF-HUL-96 - info.setWellFormed(false); - return false; - } catch (Exception e) { - // Catch any odd exceptions - String mess = MessageFormat.format( - MessageConstants.PDF_HUL_98.getMessage(), - e.getClass().getName()); - JhoveMessage message = JhoveMessages.getMessageInstance( - MessageConstants.PDF_HUL_98.getId(), mess); - info.setMessage(new ErrorMessage(message, _parser.getOffset())); // PDF-HUL-98 - info.setWellFormed(false); - return false; - } - return true; - } - - protected boolean readPageLabelTree(RepInfo info) { - // the page labels number tree is optional. - try { - if (_pageLabelDict != null) { - _pageLabelRoot = new PageLabelNode(this, null, _pageLabelDict); - _pageLabelRoot.buildSubtree(); - } - } catch (PdfException e) { - e.disparage(info); - if (e.getJhoveMessage() != null) - info.setMessage(new ErrorMessage(e.getJhoveMessage(), _parser.getOffset())); - else - info.setMessage( - new ErrorMessage(e.getMessage(), _parser.getOffset())); - // Continue parsing if it's only invalid - return (e instanceof PdfInvalidException); - } catch (Exception e) { - info.setWellFormed(false); - String mess = MessageFormat.format( - MessageConstants.PDF_HUL_99.getMessage(), - e.getClass().getName()); - JhoveMessage message = JhoveMessages.getMessageInstance( - MessageConstants.PDF_HUL_99.getId(), mess); - info.setMessage(new ErrorMessage(message)); // PDF-HUL-99 - return false; - } - return true; // always succeeds - } - - protected boolean readXMPData(RepInfo info) { - try { - PdfStream metadata = (PdfStream) resolveIndirectObject( - _docCatDict.get(DICT_KEY_METADATA)); - if (metadata == null) { - return true; // Not required - } - // PdfDictionary metaDict = metadata.getDict (); - - // Create an InputSource to feed the parser. - SAXParserFactory factory = SAXParserFactory.newInstance(); - factory.setNamespaceAware(true); - XMLReader parser = factory.newSAXParser().getXMLReader(); - PdfXMPSource src = new PdfXMPSource(metadata, getFile()); - XMPHandler handler = new XMPHandler(); - parser.setContentHandler(handler); - parser.setErrorHandler(handler); - - // We have to parse twice. The first time, we may get - // an encoding change as part of an exception thrown. If this - // happens, we create a new InputSource with the encoding, and - // continue. - try { - parser.parse(src); - _xmpProp = src.makeProperty(); - } catch (SAXException se) { - String msg = se.getMessage(); - if (msg != null && msg.startsWith(ENCODING_PREFIX)) { - String encoding = msg.substring(5); - try { - src = new PdfXMPSource(metadata, getFile(), encoding); - parser.parse(src); - _xmpProp = src.makeProperty(); - } catch (UnsupportedEncodingException uee) { - _logger.log(Level.INFO, - "Attempt to use explicit encoding to parse XMP metadata failed.", - uee); - throw new PdfInvalidException( - MessageConstants.PDF_HUL_100); // PDF-HUL-100 - } - } - } - - } catch (PdfException e) { - e.disparage(info); - if (e.getJhoveMessage() != null) - info.setMessage(new ErrorMessage(e.getJhoveMessage(), _parser.getOffset())); - else - info.setMessage( - new ErrorMessage(e.getMessage(), _parser.getOffset())); - // Continue parsing if it's only invalid - return (e instanceof PdfInvalidException); - } catch (Exception e) { - info.setMessage(new ErrorMessage(MessageConstants.PDF_HUL_101, // PDF-HUL-101 - _parser.getOffset())); - info.setValid(false); - return false; - } - return true; - } - - protected void findExternalStreams(RepInfo info) throws IOException { - _extStreamsList = new LinkedList(); - // stop processing if there is no root for the document tree - if (_docTreeRoot == null) - return; - _docTreeRoot.startWalk(); - try { - for (;;) { - // Get all the page objects in the document sequentially - PageObject page = _docTreeRoot.nextPageObject(); - if (page == null) { - break; - } - // Get the streams for the page and walk through them - List streams = page.getContentStreams(); - if (streams != null) { - ListIterator streamIter = streams.listIterator(); - while (streamIter.hasNext()) { - PdfStream stream = streamIter.next(); - String specStr = stream.getFileSpecification(); - if (specStr != null) { - Property prop = new Property(PROP_NAME_FILE, - PropertyType.STRING, specStr); - _extStreamsList.add(prop); - } - } - } - } - } catch (PdfException e) { - e.disparage(info); - if (e.getJhoveMessage() != null) { - info.setMessage(new ErrorMessage(e.getJhoveMessage())); - } else { - info.setMessage(new ErrorMessage(e.getMessage())); - } - } catch (Exception e) { - info.setWellFormed(false); - String mess = MessageFormat.format( - MessageConstants.PDF_HUL_102.getMessage(), - e.getClass().getName()); - JhoveMessage message = JhoveMessages.getMessageInstance( - MessageConstants.PDF_HUL_102.getId(), mess); - info.setMessage(new ErrorMessage(message)); // PDF-HUL-102 - } - } - - /** - * Locates the filters in the content stream dictionaries - * and generate a list of unique pipelines. - * - * @return false if the filter structure is - * defective. - */ - protected boolean findFilters(RepInfo info) throws IOException { - _filtersList = new LinkedList(); - // stop processing if there is no root for the document tree - if (_docTreeRoot == null) - return false; - _docTreeRoot.startWalk(); - try { - for (;;) { - // Get all the page objects in the document sequentially - PageObject page = _docTreeRoot.nextPageObject(); - if (page == null) { - break; - } - // Get the streams for the page and walk through them - List streams = page.getContentStreams(); - if (streams != null) { - ListIterator streamIter = streams.listIterator(); - while (streamIter.hasNext()) { - PdfStream stream = streamIter.next(); - Filter[] filters = stream.getFilters(); - extractFilters(filters, stream); - } - } - } - } catch (PdfException e) { - e.disparage(info); - if (e.getJhoveMessage() != null) - info.setMessage(new ErrorMessage(e.getJhoveMessage(), _parser.getOffset())); - else - info.setMessage( - new ErrorMessage(e.getMessage(), _parser.getOffset())); - // Continue parsing if it's only invalid - return (e instanceof PdfInvalidException); - } - return true; - } - - /** - * Finds the filters in a stream or array object which is the value - * of a stream's Filter key, and put them in _filtersList - * if a duplicate isn't there already. If the name is - * "Crypt", appends a colon and the name if available. - * Returns the filter string whether it's added or not, - * or null if there are no filters. - */ - protected String extractFilters(Filter[] filters, PdfStream stream) { - /* - * Concatenate the names into a string of names separated - * by spaces. - */ - int len = filters.length; - if (len == 0) { - return null; - } - StringBuffer buf = new StringBuffer(); - for (int i = 0; i < len; i++) { - Filter filt = filters[i]; - String fname = filt.getFilterName(); - buf.append(fname); - /* If it's a Crypt filter, add the crypt name. */ - if (FILTER_NAME_CRYPT.equals(fname)) { - String cname = filt.getNameParam(); - if (cname != null) { - buf.append(":" + cname); - } - } - if (i < len - 1) { - buf.append(' '); - } - } - String filterStr = buf.toString(); - boolean unique = true; - // Check for uniqueness. - Iterator iter = _filtersList.iterator(); - while (iter.hasNext()) { - Property p = iter.next(); - String s = (String) p.getValue(); - if (s.equals(filterStr)) { - unique = false; - break; - } - } - if (filterStr != null && unique) { - Property prop = new Property(PROP_NAME_FILTER_PIPELINE, - PropertyType.STRING, filterStr); - _filtersList.add(prop); - } - return filterStr; - } - - protected void findImages(RepInfo info) throws IOException { - _imagesList = new LinkedList(); - _docTreeRoot.startWalk(); - try { - for (;;) { - // Get all the page objects in the document sequentially - PageObject page = _docTreeRoot.nextPageObject(); - if (page == null) { - break; - } - // Get the resources for the page and look for image XObjects - PdfDictionary rsrc = page.getResources(); - if (rsrc != null) { - PdfDictionary xo = (PdfDictionary) resolveIndirectObject( - rsrc.get(RESOURCE_NAME_XOBJECT)); - if (xo != null) { - Iterator iter = xo.iterator(); - while (iter.hasNext()) { - // Get an XObject and check if it's an image. - _logger.info("Getting image"); - PdfDictionary xobdict = null; - PdfObject xob = resolveIndirectObject(iter.next()); - if (xob instanceof PdfStream) { - xobdict = ((PdfStream) xob).getDict(); - } - if (xobdict != null) { - PdfSimpleObject subtype = (PdfSimpleObject) xobdict - .get(DICT_KEY_XOBJ_SUBTYPE); - if (XOBJ_SUBTYPE_IMAGE - .equals(subtype.getStringValue())) { - // It's an image XObject. Report stuff. - _logger.info("Image XObject"); - List imgList = new ArrayList( - 10); - Property prop = new Property( - PROP_NAME_IMAGE, - PropertyType.PROPERTY, - PropertyArity.LIST, imgList); - NisoImageMetadata niso = new NisoImageMetadata(); - imgList.add(new Property( - PROP_NAME_NISO_IMAGE_MD, - PropertyType.NISOIMAGEMETADATA, - niso)); - PdfObject widthBase = xobdict - .get(DICT_KEY_WIDTH); - PdfSimpleObject widObj = (PdfSimpleObject) resolveIndirectObject( - widthBase); - niso.setImageWidth(widObj.getIntValue()); - PdfObject heightBase = xobdict - .get(DICT_KEY_HEIGHT); - PdfSimpleObject htObj = (PdfSimpleObject) resolveIndirectObject( - heightBase); - niso.setImageLength(htObj.getIntValue()); - - // Check for filters to add to the filter - // list - Filter[] filters = ((PdfStream) xob) - .getFilters(); - // Try to derive the image MIME type from - // filter names - String mimeType = imageMimeFromFilters( - filters); - niso.setMimeType(mimeType); - String filt = extractFilters(filters, - (PdfStream) xob); - if (filt != null) { - // If the filter is one which the NISO - // schema - // knows about, put it in the NISO - // metadata, - // otherwise put it in a Filter - // property. - int nisoFilt = nameToNiso(filt, - compressionStrings, - compressionValues); - if (nisoFilt >= 0) { - /* - * If it's 2, it's a CCITTFaxDecode - * filter. There may be an optional - * K entry that can change the - * value. - */ - PdfObject parms = xobdict.get( - DICT_KEY_DECODE_PARAMS); - if (parms != null) { - PdfSimpleObject kobj = null; - if (parms instanceof PdfDictionary) { - PdfDictionary pdict = (PdfDictionary) parms; - kobj = (PdfSimpleObject) resolveIndirectObject(pdict.get(DICT_KEY_K)); - } - /* - * Note that the DecodeParms - * value may also be an array - * of dictionaries. We are not - * handling that contingency. - */ - if (kobj != null) { - int k = kobj.getIntValue(); - if (k < 0) { - nisoFilt = 4; - } else if (k > 0) { - nisoFilt = 3; - } - } - } - niso.setCompressionScheme(nisoFilt); - } else { - imgList.add(new Property( - PROP_NAME_FILTER, - PropertyType.STRING, filt)); - } - } else { - niso.setCompressionScheme(1); // no - // filter - } - - // Check for color space info - PdfObject colorSpc = xobdict - .get(DICT_KEY_COLOR_SPACE); - if (colorSpc != null) { - String colorName = null; - if (colorSpc instanceof PdfSimpleObject) { - colorName = ((PdfSimpleObject) colorSpc) - .getStringValue(); - } else if (colorSpc instanceof PdfArray) { - Vector vec = ((PdfArray) colorSpc) - .getContent(); - // Use the first element, which is - // the color space family - PdfSimpleObject fam = (PdfSimpleObject) vec - .elementAt(0); - colorName = fam.getStringValue(); - } - if (colorName != null) { - int nisoSpace = nameToNiso( - colorName, - colorSpaceStrings, - colorSpaceValues); - if (nisoSpace >= 0) { - niso.setColorSpace(nisoSpace); - } else { - imgList.add(new Property( - PROP_NAME_COLOR_SPACE, - PropertyType.STRING, - colorName)); - } - } - } - - PdfSimpleObject bpc = (PdfSimpleObject) xobdict - .get(DICT_KEY_BITS_PER_COMPONENT); - if (bpc != null) { - // imgList.add(new - // Property(DICT_KEY_BITS_PER_COMPONENT, - // PropertyType.INTEGER, - // new Integer (bpc.getIntValue()))); - niso.setBitsPerSample(new int[] { - bpc.getIntValue() }); - } - - PdfSimpleObject intent = (PdfSimpleObject) xobdict - .get(DICT_KEY_INTENT); - if (intent != null) { - imgList.add(new Property( - PROP_NAME_INTENT, - PropertyType.STRING, - intent.getStringValue())); - } - - PdfSimpleObject imgmsk = (PdfSimpleObject) xobdict - .get(DICT_KEY_IMAGE_MASK); - if (imgmsk != null) { - boolean b = imgmsk.isTrue(); - imgList.add(new Property( - PROP_NAME_IMAGE_MASK, - PropertyType.BOOLEAN, - Boolean.valueOf(b))); - } - - PdfArray dcd = (PdfArray) xobdict - .get(DICT_KEY_DECODE); - if (dcd != null) { - Vector dcdvec = dcd - .getContent(); - List dcdlst = new ArrayList( - dcdvec.size()); - Iterator diter = dcdvec - .iterator(); - while (diter.hasNext()) { - PdfSimpleObject d = (PdfSimpleObject) diter - .next(); - dcdlst.add(new Integer( - d.getIntValue())); - } - imgList.add(new Property( - PROP_NAME_DECODE, - PropertyType.INTEGER, - PropertyArity.LIST, dcdlst)); - } - - PdfSimpleObject intrp = (PdfSimpleObject) xobdict - .get(DICT_KEY_INTERPOLATE); - if (intrp != null) { - boolean b = intrp.isTrue(); - imgList.add(new Property( - PROP_NAME_INTERPOLATE, - PropertyType.BOOLEAN, - Boolean.valueOf(b))); - } - - PdfSimpleObject nam = (PdfSimpleObject) xobdict - .get(DICT_KEY_NAME); - if (nam != null) { - imgList.add(new Property(PROP_NAME_NAME, - PropertyType.STRING, - nam.getStringValue())); - } - - PdfSimpleObject id = (PdfSimpleObject) resolveIndirectObject( - xobdict.get(DICT_KEY_ID)); - if (id != null) { - String idstr = toHex( - id.getStringValue()); - imgList.add(new Property(PROP_NAME_ID, - PropertyType.STRING, idstr)); - } - - _imagesList.add(prop); - } - - } - } - } - } - } - } catch (PdfException e) { - e.disparage(info); - if (e.getJhoveMessage() != null) - info.setMessage(new ErrorMessage(e.getJhoveMessage(), _parser.getOffset())); - else - info.setMessage( - new ErrorMessage(e.getMessage(), _parser.getOffset())); - } catch (Exception e) { - info.setWellFormed(false); - String mess = MessageFormat.format( - MessageConstants.PDF_HUL_103.getMessage(), - e.getClass().getName()); - JhoveMessage message = JhoveMessages.getMessageInstance( - MessageConstants.PDF_HUL_103.getId(), mess); - info.setMessage(new ErrorMessage(message)); // PDF-HUL-103 - } - } - - /* - * Convert a Filter name to a NISO compression scheme value. - * If the name is unknown to NISO, return -1. - */ - protected int nameToNiso(String name, String[] nameArray, int[] valArray) { - for (int i = 0; i < nameArray.length; i++) { - if (nameArray[i].equals(name)) { - return valArray[i]; - } - } - return -1; // no match - } - - protected void findFonts(RepInfo info) throws IOException { - _type0FontsMap = new HashMap(); - _type1FontsMap = new HashMap(); - _trueTypeFontsMap = new HashMap(); - _mmFontsMap = new HashMap(); - _type3FontsMap = new HashMap(); - _cid0FontsMap = new HashMap(); - _cid2FontsMap = new HashMap(); - try { - _docTreeRoot.startWalk(); - for (;;) { - // This time we need all the page objects and page tree - // nodes, because resources can be inherited from - // page tree nodes. - DocNode node = _docTreeRoot.nextDocNode(); - if (node == null) { - break; - } - // Get the fonts for the node - PdfDictionary fonts = null; - fonts = node.getFontResources(); - if (fonts != null) { - // In order to make sure we have a collection of - // unique fonts, we store them in a map keyed by - // object number. - Iterator fontIter = fonts.iterator(); - while (fontIter.hasNext()) { - PdfObject fontRef = fontIter.next(); - PdfObject font = resolveIndirectObject(fontRef); - if (font instanceof PdfDictionary) { - addFontToMap((PdfDictionary) font); - } else { - // Expected a dictionary - info.setWellFormed(false); - info.setMessage(new ErrorMessage( - MessageConstants.PDF_HUL_104, // PDF-HUL-104 - _parser.getOffset())); - return; - } - // If we've been directed appropriately, - // we accumulate the information, but don't - // report it. In that case, we post a message - // just once to that effect. - if (!_skippedFontsReported && !_showFonts - && _verbosity != Module.MAXIMUM_VERBOSITY) { - info.setMessage(new InfoMessage( - MessageConstants.PDF_HUL_105)); // PDF-HUL-105 - _skippedFontsReported = true; - } - } - } - } - } catch (PdfException e) { - e.disparage(info); - if (e.getJhoveMessage() != null) - info.setMessage(new ErrorMessage(e.getJhoveMessage(), _parser.getOffset())); - else - info.setMessage( - new ErrorMessage(e.getMessage(), _parser.getOffset())); - return; - } catch (Exception e) { - // Unexpected exception. - _logger.log(Level.WARNING, - MessageConstants.PDF_HUL_106.getMessage(), e); - info.setWellFormed(false); - info.setMessage(new ErrorMessage(MessageConstants.PDF_HUL_106, // PDF-HUL-106 - e.toString(), _parser.getOffset())); - return; - } - } - - /** - * Add the font to the appropriate map, and return its subtype. - * If we've exceeded the maximum number of fonts, then ignore it. - */ - protected String addFontToMap(PdfDictionary font) { - if (++_nFonts > maxFonts) { - return null; - } - String subtypeStr = null; - try { - PdfSimpleObject subtype = (PdfSimpleObject) font - .get(DICT_KEY_FONT_SUBTYPE); - subtypeStr = subtype.getStringValue(); - if (FONT_TYPE0.equals(subtypeStr)) { - _type0FontsMap.put(new Integer(font.getObjNumber()), font); - // If the font is Type 0, we must go - // through its descendant fonts - PdfObject desc0 = font.get(DICT_KEY_DESCENDANT_FONTS); - PdfArray descendants = (PdfArray) resolveIndirectObject(desc0); - Vector subfonts = descendants.getContent(); - Iterator subfontIter = subfonts.iterator(); - while (subfontIter.hasNext()) { - PdfObject subfont = subfontIter.next(); - subfont = resolveIndirectObject(subfont); - addFontToMap((PdfDictionary) subfont); - } - } else if (FONT_TYPE1.equals(subtypeStr)) { - _type1FontsMap.put(new Integer(font.getObjNumber()), font); - } else if (FONT_MM_TYPE1.equals(subtypeStr)) { - _mmFontsMap.put(new Integer(font.getObjNumber()), font); - } else if (FONT_TYPE3.equals(subtypeStr)) { - _type3FontsMap.put(new Integer(font.getObjNumber()), font); - } else if (FONT_TRUE_TYPE.equals(subtypeStr)) { - _trueTypeFontsMap.put(new Integer(font.getObjNumber()), font); - } else if (FONT_CID_TYPE0.equals(subtypeStr)) { - _cid0FontsMap.put(new Integer(font.getObjNumber()), font); - } else if (FONT_CID_TYPE2.equals(subtypeStr)) { - _cid2FontsMap.put(new Integer(font.getObjNumber()), font); - } - return subtypeStr; - } catch (Exception e) { - return null; - } - } - - /****************************************************************** - * PRIVATE CLASS METHODS. - ******************************************************************/ - - protected static String toHex(String s) { - StringBuffer buffer = new StringBuffer("0x"); - - int len = s.length(); - for (int i = 0; i < len; i++) { - String h = Integer.toHexString(s.charAt(i)); - if (h.length() < 2) { - buffer.append("0"); - } - buffer.append(h); - } - - return buffer.toString(); - } - - protected static String toHex(Vector v) { - StringBuffer buffer = new StringBuffer("0x"); - - int len = v.size(); - for (int i = 0; i < len; i++) { - int hdigit = v.elementAt(i).intValue(); - String h = Integer.toHexString(hdigit); - if (h.length() < 2) { - buffer.append("0"); - } - buffer.append(h); - } - - return buffer.toString(); - } - - /** - * If the argument is an indirect object reference, - * returns the object it resolves to, otherwise returns - * the object itself. In particular, calling with null will - * return null. - */ - public PdfObject resolveIndirectObject(PdfObject obj) - throws PdfException, IOException { - if (obj instanceof PdfIndirectObj) { - int objIndex = ((PdfIndirectObj) obj).getObjNumber(); - /* - * Here we need to allow for the possibility that the - * object is compressed in an object stream. That means - * creating a new structure (call it _xref2) that contains - * the stream object number and offset whenever _xref[objIndex] - * is negative. _xref2 will have to contain the content - * stream object number (which will itself have to be - * resolved) and the offset into the object stream. - */ - return getObject(objIndex, MAX_OBJ_STREAM_DEPTH); - } - return obj; - } - - /** - * Returns an object of a given number. This may involve - * recursion into object streams, in which case it calls itself. - * - * @param objIndex - * The object number to look up - * @param recGuard - * The maximum permitted number of recursion levels; - * no particular value is required, but 30 or more - * should avoid false exceptions. - */ - protected PdfObject getObject(int objIndex, int recGuard) - throws PdfException, IOException { - /* Guard against infinite recursion */ - if (recGuard <= 0) { - throw new PdfMalformedException(MessageConstants.PDF_HUL_107); - } - long offset = _xref[objIndex]; - if (offset == 0) { - return null; // This is considered legitimate by the spec - } - if (offset < 0) { - return getObjectFromStream(objIndex, recGuard); - } - _parser.seek(offset); - PdfObject obj = _parser.readObjectDef(); - // - // Experimental carl@openpreservation.org 2018-03-14 - // - // Previously all object numbers (ids) were overwritten even if they'd - // previously been assigned. - // - // This is caused by a little confusion where the object ID and the - // index of the _xref array are used interchangeably when they're not - // the same thing. There's an assumption when for the _xref array - // that the objects will have continuous numeric object numbers. This - // means that the object number and array position will always be the - // same. The setting of the object number meant that the wrong object - // could - // be returned with the id changed to match the id requested. - // - // My guess is that the assignment was put in to ensure that an - // object that escaped initialisation had an object number. If that's - // the case then the code below will still allow that to happen but - // will prevent assigned numbers from been overwritten by the xref array - // position. - if (obj.getObjNumber() == -1) { - obj.setObjNumber(objIndex); - } - return obj; - } - - /** - * Return the RandomAccessFile being read. - */ - public RandomAccessFile getFile() { - return _raf; - } - - /** - * Returns the catalog dictionary object. - */ - public PdfDictionary getCatalogDict() { - return _docCatDict; - } - - /** - * Returns the trailer dictionary object. - */ - public PdfDictionary getTrailerDict() { - return _trailerDict; - } - - /** - * Returns the viewer preferences dictionary object. - */ - public PdfDictionary getViewPrefDict() { - return _viewPrefDict; - } - - /** - * Returns the outlines dictionary object. - */ - public PdfDictionary getOutlineDict() { - return _outlineDict; - } - - /** - * Get a font map. The map returned is determined by the selector. - * Any other value returns null. - */ - public Map getFontMap(int selector) { - switch (selector) { - case F_TYPE0: - return _type0FontsMap; - case F_TYPE1: - return _type1FontsMap; - case F_TT: - return _mmFontsMap; - case F_TYPE3: - return _type3FontsMap; - case F_MM1: - return _mmFontsMap; - case F_CID0: - return _cid0FontsMap; - case F_CID2: - return _cid2FontsMap; - default: - return null; - } - } - - /** - * Return a List of all the font maps. Together, these contain - * all the fonts and subfonts in the document. Some of the maps - * may be null. - */ - public List> getFontMaps() { - List> lst = new ArrayList>( - 7); - lst.add(_type0FontsMap); - lst.add(_type1FontsMap); - lst.add(_mmFontsMap); - lst.add(_type3FontsMap); - lst.add(_trueTypeFontsMap); - lst.add(_cid0FontsMap); - lst.add(_cid2FontsMap); - return lst; - } - - /** - * Returns a NameTreeNode for the EmbeddedFiles entry of the - * Names dictionary. Returns null if there isn't one. - */ - public NameTreeNode getEmbeddedFiles() { - return _embeddedFiles; - } - - /** - * Add the various font lists as a fonts property. Note: only add - * the "Fonts" property if there are, in fact, fonts defined. - */ - protected void addFontsProperty(List metadataList) { - List fontTypesList = new LinkedList(); - Property fontp = null; - if (_type0FontsMap != null && !_type0FontsMap.isEmpty()) { - try { - fontp = buildFontProperty(PROP_NAME_FONT_TYPE0, _type0FontsMap, - F_TYPE0); - fontTypesList.add(fontp); - } catch (ClassCastException e) { - // Report an error here? - } - } - if (_type1FontsMap != null && !_type1FontsMap.isEmpty()) { - try { - fontp = buildFontProperty(PROP_NAME_FONT_TYPE1, _type1FontsMap, - F_TYPE1); - fontTypesList.add(fontp); - } catch (ClassCastException e) { - // Report an error here? - } - } - if (_trueTypeFontsMap != null && !_trueTypeFontsMap.isEmpty()) { - try { - fontp = buildFontProperty(PROP_NAME_FONT_TRUE_TYPE, - _trueTypeFontsMap, F_TT); - fontTypesList.add(fontp); - } catch (ClassCastException e) { - // Report an error here? - } - } - if (_type3FontsMap != null && !_type3FontsMap.isEmpty()) { - try { - fontp = buildFontProperty(PROP_NAME_FONT_TYPE3, _type3FontsMap, - F_TYPE3); - fontTypesList.add(fontp); - } catch (ClassCastException e) { - } - } - if (_mmFontsMap != null && !_mmFontsMap.isEmpty()) { - try { - fontp = buildFontProperty(PROP_NAME_FONT_MM_TYPE1, _mmFontsMap, - F_MM1); - fontTypesList.add(fontp); - } catch (ClassCastException e) { - } - } - if (_cid0FontsMap != null && !_cid0FontsMap.isEmpty()) { - try { - fontp = buildFontProperty(PROP_NAME_FONT_CID_TYPE0, - _cid0FontsMap, F_CID0); - fontTypesList.add(fontp); - } catch (ClassCastException e) { - } - } - if (_cid2FontsMap != null && !_cid2FontsMap.isEmpty()) { - try { - fontp = buildFontProperty(PROP_NAME_FONT_CID_TYPE2, - _cid2FontsMap, F_CID2); - fontTypesList.add(fontp); - } catch (ClassCastException e) { - } - } - if (fontTypesList.size() > 0) { - metadataList.add(new Property(PROP_NAME_FONTS, - PropertyType.PROPERTY, PropertyArity.LIST, fontTypesList)); - } - } - - /* Build Pages property, with associated subproperties. */ - protected void addPagesProperty(List metadataList, RepInfo info) { - _pagesList = new LinkedList(); - _pageSeqMap = new HashMap(500); - try { - _docTreeRoot.startWalk(); - int pageIndex = 0; - // Start the pipe with two entries. - // We always need to have the current and the next - // entry from the page label tree in order to determine - // the lower and upper bounds of the applicable range. - // If the first entry has a bound greater than zero, - // that appears to be an undefined situation, so we - // always treat the first entry as starting at zero. - if (_pageLabelRoot != null) { - if (!_pageLabelRoot.findNextKeyValue()) { - throw new PdfMalformedException( - MessageConstants.PDF_HUL_111); // PDF-HUL-111 - } - - _pageLabelRoot.findNextKeyValue(); - } - for (;;) { - // Get all the page objects in the document sequentially - // Have to do this in two passes so that link - // destinations can be properly reported. - PageObject page = _docTreeRoot.nextPageObject(); - if (page == null) { - break; - } - _pageSeqMap.put(new Integer(page.getDict().getObjNumber()), - new Integer(pageIndex + 1)); - } - _docTreeRoot.startWalk(); - for (;;) { - PageObject page = _docTreeRoot.nextPageObject(); - if (page == null) { - break; - } - Property p = buildPageProperty(page, pageIndex++, info); - _pagesList.add(p); - } - if (_showPages || _verbosity == Module.MAXIMUM_VERBOSITY) { - Property prop = new Property(PROP_NAME_PAGES, - PropertyType.PROPERTY, PropertyArity.LIST, _pagesList); - metadataList.add(prop); - } else { - if (!_skippedPagesReported) { - info.setMessage( - new InfoMessage(MessageConstants.PDF_HUL_112)); // PDF-HUL-112 - _skippedPagesReported = true; - } - } - } catch (PdfException e) { - - e.disparage(info); - if (e.getJhoveMessage() != null) - info.setMessage(new ErrorMessage(e.getJhoveMessage(), _parser.getOffset())); - else - info.setMessage( - new ErrorMessage(e.getMessage(), _parser.getOffset())); - return; - } - } - - /* Build a subproperty for one PageObject. */ - protected Property buildPageProperty(PageObject page, int idx, RepInfo info) - throws PdfException { - List pagePropList = new ArrayList(4); - try { - // Foo on Java's inability to return values through - // parameters. Passing an array is a crock to achieve - // that effect. - int[] nominalNum = new int[1]; - Property plProp = buildPageLabelProperty(page, idx, nominalNum); - if (plProp != null) { - pagePropList.add(plProp); - } - if (plProp == null || nominalNum[0] != idx + 1) { - // Page sequence is different from label, or - // there is no label. Make it 1-based. - pagePropList.add(new Property(PROP_NAME_SEQUENCE, - PropertyType.INTEGER, new Integer(idx + 1))); - - } - } catch (PdfException e) { - throw e; - } catch (Exception f) { - throw new PdfMalformedException(MessageConstants.PDF_HUL_113); // PDF-HUL-113 - } - - try { - List annotsList = new LinkedList(); - PdfArray annots = page.getAnnotations(); - if (annots != null) { - Vector contents = annots.getContent(); - for (int i = 0; i < contents.size(); i++) { - PdfObject annot = resolveIndirectObject( - contents.elementAt(i)); - if (annot instanceof PdfDictionary) { - annotsList.add(buildAnnotProperty((PdfDictionary) annot, - info)); - } else { - // There are annotations which aren't dictionaries. I've - // run into this, - // but it violates the spec as far as I can tell. - throw new PdfInvalidException( - MessageConstants.PDF_HUL_114); // PDF-HUL-114 - } - } - if (!annotsList.isEmpty()) { - if (_showAnnotations - || _verbosity == Module.MAXIMUM_VERBOSITY) { - Property annotProp = new Property(PROP_NAME_ANNOTATIONS, - PropertyType.PROPERTY, PropertyArity.LIST, - annotsList); - pagePropList.add(annotProp); - } else { - // We don't report annotations if we got here, - // but we do report that we don't report them. - if (!_skippedAnnotationsReported) { - info.setMessage(new InfoMessage( - MessageConstants.PDF_HUL_115)); // PDF-HUL-115 - _skippedAnnotationsReported = true; - } - } - } - } - } catch (PdfException e) { - throw e; - } catch (Exception f) { - throw new PdfMalformedException(MessageConstants.PDF_HUL_116); // PDF-HUL-116 - } - - try { - // Rotation property is inheritable - PdfObject tempObj = page.get(DICT_KEY_ROTATE, - true); - PdfSimpleObject rot = null; - if (tempObj != null && tempObj instanceof PdfSimpleObject) { - rot = (PdfSimpleObject) tempObj; - } else if (tempObj != null && tempObj instanceof PdfIndirectObj) { - rot = (PdfSimpleObject) ((PdfIndirectObj) tempObj) - .getObject(); - } - if (rot != null && rot.getIntValue() != 0) { - pagePropList.add(new Property(PROP_NAME_ROTATE, - PropertyType.INTEGER, new Integer(rot.getIntValue()))); - } - - // UserUnit property (1.6), not inheritable - PdfSimpleObject uu = (PdfSimpleObject) page.get(DICT_KEY_USER_UNIT, - false); - if (uu != null) { - pagePropList.add(new Property(PROP_NAME_USER_UNIT, - PropertyType.DOUBLE, new Double(rot.getDoubleValue()))); - } - // Viewport dictionaries (1.6), not inheritable - PdfArray vp = (PdfArray) page.get(DICT_KEY_VIEWPORT, false); - if (vp != null) { - Vector vpv = vp.getContent(); - Iterator iter = vpv.iterator(); - List vplist = new ArrayList(vpv.size()); - while (iter.hasNext()) { - PdfDictionary vpd = (PdfDictionary) resolveIndirectObject( - iter.next()); - PdfObject vpdbb = vpd.get(DICT_KEY_BBOX); - List vpPropList = new ArrayList(); - vpPropList.add(makeRectProperty( - (PdfArray) resolveIndirectObject(vpdbb), - DICT_KEY_BBOX)); - PdfObject meas = vpd.get(DICT_KEY_MEASURE); - if (meas instanceof PdfDictionary) { - vpPropList.add( - buildMeasureProperty((PdfDictionary) meas)); - // No, that's wrong -- the Viewport property itself - // needs to be a list with a bounding box. - } - vplist.add(new Property(PROP_NAME_VIEWPORT, - PropertyType.PROPERTY, PropertyArity.LIST, - vpPropList)); - } - pagePropList.add(new Property(PROP_NAME_VIEWPORTS, - PropertyType.PROPERTY, PropertyArity.LIST, vplist)); - } - // Thumbnail -- we just report if it's there. It's a - // non-inheritable property - PdfObject thumb = page.get(DICT_KEY_THUMB, false); - if (thumb != null) { - pagePropList.add(new Property(PROP_NAME_THUMB, - PropertyType.BOOLEAN, Boolean.TRUE)); - } - return new Property(PROP_NAME_PAGE, PropertyType.PROPERTY, - PropertyArity.LIST, pagePropList); - } catch (PdfException e) { - throw e; - } catch (Exception f) { - throw new PdfMalformedException(MessageConstants.PDF_HUL_117); // PDF-HUL-117 - } - } - - /* - * Build a subproperty of a subproperty for page labels. - * The nomNumRef argument is a crock for returning the - * nominal number; element 0 of the array is replaced - * by the nominal number of the page. - */ - protected Property buildPageLabelProperty(PageObject page, int pageIndex, - int[] nomNumRef) throws PdfException { - if (_pageLabelRoot == null) { - return null; // no page label info - } - - // Note that our "current" page is the page label tree's - // "previous" key. Sorry about that... - int curFirstPage = _pageLabelRoot.getPrevKey(); - int nextFirstPage = _pageLabelRoot.getCurrentKey(); - try { - // If we're onto the next page range, advance our pointers. - if (pageIndex >= nextFirstPage) { - _pageLabelRoot.findNextKeyValue(); - curFirstPage = nextFirstPage; - } - PdfDictionary pageLabelDict = (PdfDictionary) resolveIndirectObject( - _pageLabelRoot.getPrevValue()); - StringBuffer labelText = new StringBuffer(); - PdfSimpleObject prefixObj = (PdfSimpleObject) pageLabelDict - .get(DICT_KEY_P); - if (prefixObj != null) { - labelText.append(prefixObj.getStringValue()); - } - PdfSimpleObject firstPageObj = (PdfSimpleObject) pageLabelDict - .get("St"); - // Sequence start value defaults to 1 if there's no start value - int firstPageVal = ((firstPageObj != null) - ? firstPageObj.getIntValue() - : 1); - int nominalPage = pageIndex - curFirstPage + firstPageVal; - if (nominalPage <= 0) { - throw new PdfInvalidException(MessageConstants.PDF_HUL_118); // pDF-HUL-118 - } - nomNumRef[0] = nominalPage; - - // Get the numbering style. If there is no numbering - // style entry, the label consists only of the prefix. - PdfSimpleObject numStyleObj = (PdfSimpleObject) pageLabelDict - .get("S"); - String numStyle; - if (numStyleObj == null) { - numStyle = null; - } else { - numStyle = numStyleObj.getStringValue(); - } - if ("D".equals(numStyle)) { - // Nice, simple decimal numbers - labelText.append(nominalPage); - } else if ("R".equals(numStyle)) { - // Upper case roman numerals - labelText.append(PageLabelNode.intToRoman(nominalPage, true)); - } else if ("r".equals(numStyle)) { - // Lower case roman numerals - labelText.append(PageLabelNode.intToRoman(nominalPage, false)); - } else if ("A".equals(numStyle)) { - // Uppercase letters (A-Z, AA-ZZ, ...) - labelText.append(PageLabelNode.intToBase26(nominalPage, true)); - } else if ("a".equals(numStyle)) { - // Lowercase letters (a-z, aa-zz, ...) - labelText.append(PageLabelNode.intToBase26(nominalPage, false)); - } - // It screws up the PDF output if we have a blank Label property. - if (labelText.length() == 0) { - labelText.append(EMPTY_LABEL_PROPERTY); - } - return new Property(PROP_NAME_LABEL, PropertyType.STRING, - labelText.toString()); - } catch (Exception e) { - throw new PdfMalformedException(MessageConstants.PDF_HUL_119); // PDF-HUL-119 - } - } - - /* Build a subproperty for a measure dictionary. */ - protected Property buildMeasureProperty(PdfDictionary meas) { - List plist = new ArrayList(); - PdfObject itemObj = meas.get(DICT_KEY_XOBJ_SUBTYPE); - if (itemObj instanceof PdfSimpleObject) { - plist.add(new Property(PROP_NAME_SUBTYPE, PropertyType.STRING, - ((PdfSimpleObject) itemObj).getStringValue())); - } - itemObj = meas.get(DICT_KEY_R); - if (itemObj instanceof PdfSimpleObject) { - plist.add(new Property(PROP_NAME_RATIO, PropertyType.STRING, - ((PdfSimpleObject) itemObj).getStringValue())); - } - // All kinds of stuff I could add -- limit it to the required - // X, Y, D and A arrays. - itemObj = meas.get("X"); - if (itemObj instanceof PdfArray) { - Vector v = ((PdfArray) itemObj).getContent(); - double[] x = new double[v.size()]; - for (int i = 0; i < v.size(); i++) { - PdfSimpleObject xobj = (PdfSimpleObject) v.elementAt(i); - x[i] = xobj.getDoubleValue(); - } - plist.add(new Property("X", PropertyType.DOUBLE, - PropertyArity.ARRAY, x)); - } - itemObj = meas.get("Y"); - if (itemObj instanceof PdfArray) { - Vector v = ((PdfArray) itemObj).getContent(); - double[] x = new double[v.size()]; - for (int i = 0; i < v.size(); i++) { - PdfSimpleObject xobj = (PdfSimpleObject) v.elementAt(i); - x[i] = xobj.getDoubleValue(); - } - plist.add(new Property("Y", PropertyType.DOUBLE, - PropertyArity.ARRAY, x)); - } - itemObj = meas.get("D"); - if (itemObj instanceof PdfArray) { - Vector v = ((PdfArray) itemObj).getContent(); - double[] x = new double[v.size()]; - for (int i = 0; i < v.size(); i++) { - PdfSimpleObject xobj = (PdfSimpleObject) v.elementAt(i); - x[i] = xobj.getDoubleValue(); - } - plist.add(new Property(PROP_NAME_DISTANCE, PropertyType.DOUBLE, - PropertyArity.ARRAY, x)); - } - itemObj = meas.get("A"); - if (itemObj instanceof PdfArray) { - Vector v = ((PdfArray) itemObj).getContent(); - double[] x = new double[v.size()]; - for (int i = 0; i < v.size(); i++) { - PdfSimpleObject xobj = (PdfSimpleObject) v.elementAt(i); - x[i] = xobj.getDoubleValue(); - } - plist.add(new Property(PROP_NAME_AREA, PropertyType.DOUBLE, - PropertyArity.ARRAY, x)); - } - return new Property(PROP_NAME_MEASURE, PropertyType.PROPERTY, - PropertyArity.LIST, plist); - } - - /* Build a subproperty of a subproperty for an annotation. */ - protected Property buildAnnotProperty(PdfDictionary annot, RepInfo info) - throws PdfException { - List propList = new ArrayList(7); - PdfObject itemObj; - try { - // Subtype is required - itemObj = annot.get(DICT_KEY_XOBJ_SUBTYPE); - propList.add(new Property(PROP_NAME_SUBTYPE, PropertyType.STRING, - ((PdfSimpleObject) itemObj).getStringValue())); - - // Contents is optional for some subtypes, required for - // others. We consider it optional here. - itemObj = annot.get(DICT_KEY_CONTENTS); - if (itemObj != null) { - propList.add( - new Property(PROP_NAME_CONTENTS, PropertyType.STRING, - _encrypted ? ENCRYPTED - : ((PdfSimpleObject) itemObj) - .getStringValue())); - } - - // Rectangle is required, and must be in the rectangle format - itemObj = annot.get(DICT_KEY_RECT); - propList.add(makeRectProperty( - (PdfArray) resolveIndirectObject(itemObj), PROP_NAME_RECT)); - - // Name comes from the NM entry and is optional - itemObj = annot.get("NM"); - if (itemObj != null) { - propList.add(new Property(DICT_KEY_NAME, PropertyType.STRING, - ((PdfSimpleObject) itemObj).getStringValue())); - } - - // LastModified is optional. The documentation says that - // a PDF date is preferred but not guaranteed. We just - // put it out as a string. - itemObj = annot.get("M"); - if (itemObj != null) { - Literal lastModLit = (Literal) ((PdfSimpleObject) itemObj) - .getToken(); - Property dateProp; - dateProp = new Property(PROP_NAME_LAST_MOD, PropertyType.STRING, - lastModLit.getValue()); - - propList.add(dateProp); - } - - // Flags. - itemObj = annot.get("F"); - if (itemObj != null) { - int flagValue = ((PdfSimpleObject) itemObj).getIntValue(); - Property flagProp = (buildBitmaskProperty(flagValue, - PROP_NAME_FLAGS, PdfStrings.ANNOTATIONFLAGS, - PROP_VAL_NO_FLAGS_SET)); - if (flagProp != null) { - propList.add(flagProp); - } - } - - // Appearance dictionary -- just check if it's there. - itemObj = annot.get("AP"); - if (itemObj != null) { - propList.add(new Property(PROP_NAME_APP_DICT, - PropertyType.BOOLEAN, Boolean.TRUE)); - } - - // Action dictionary -- if it's there, set actionsExist - itemObj = annot.get("A"); - if (itemObj != null) { - _actionsExist = true; - itemObj = resolveIndirectObject(itemObj); - // Actions are as common as Destinations for - // connecting to destination pages. If the Action - // is of type GoTo, note its destination. - PdfSimpleObject actionSubtype = (PdfSimpleObject) ((PdfDictionary) itemObj) - .get("S"); - if (actionSubtype == null) { - throw new PdfMalformedException( - MessageConstants.PDF_HUL_120); // PDF-HUL-120 - } - if (ACTION_VAL_GOTO.equals(actionSubtype.getStringValue())) { - PdfObject destObj = ((PdfDictionary) itemObj).get("D"); - if (destObj != null) { - addDestination(destObj, PROP_NAME_ACTION_DEST, propList, - info); - } - } - } - - // Destination object. - itemObj = annot.get(DICT_KEY_DEST); - if (itemObj != null) { - addDestination(itemObj, PROP_NAME_DESTINATION, propList, info); - } - - // Reply Type (RT) (1.6) - itemObj = annot.get("RT"); - if (itemObj instanceof PdfSimpleObject) { - String type = ((PdfSimpleObject) itemObj).getStringValue(); - propList.add(new Property(PROP_NAME_REPLY_TYPE, - PropertyType.STRING, type)); - } - - // Intent (IT) (1.6) - itemObj = annot.get("IT"); - if (itemObj instanceof PdfSimpleObject) { - String type = ((PdfSimpleObject) itemObj).getStringValue(); - propList.add(new Property(PROP_NAME_INTENT, PropertyType.STRING, - type)); - } - - // Callout Line (CL) (1.6) - itemObj = annot.get("CL"); - if (itemObj instanceof PdfArray) { - Vector clData = ((PdfArray) itemObj).getContent(); - // This should be an array of numbers. - Iterator iter = clData.iterator(); - List clList = new ArrayList(6); - while (iter.hasNext()) { - PdfSimpleObject clItem = (PdfSimpleObject) iter.next(); - clList.add(new Double(clItem.getDoubleValue())); - } - propList.add(new Property(PROP_NAME_CALLOUT_LINE, - PropertyType.DOUBLE, PropertyArity.LIST, clList)); - } - - return new Property(PROP_NAME_ANNOTATION, PropertyType.PROPERTY, - PropertyArity.LIST, propList); - } catch (PdfException ee) { - // Just rethrow these - throw ee; - } catch (Exception e) { - throw new PdfMalformedException(MessageConstants.PDF_HUL_121); // PDF-HUL-121 - } - } - - /* - * Given a PdfObject that stands for a Destination, add - * a representative property to the property list. - */ - protected void addDestination(PdfObject itemObj, String propName, - List propList, RepInfo info) { - try { - Destination dest = new Destination(itemObj, this, false); - if (dest.isIndirect()) { - // Encryption messes up name trees - if (!_encrypted) { - int pageObjNum = resolveIndirectDest( - dest.getIndirectDest(), info); - if (pageObjNum == -1) { - // The scope of the reference is outside this - // file, so we just report it as such. - propList.add(new Property(propName, PropertyType.STRING, - PROP_VAL_EXTERNAL)); - } else { - propList.add(new Property(propName, - PropertyType.INTEGER, new Integer(pageObjNum))); - } - } - } else { - if (dest.getPageDest() == null) { - return; // can't get the page object number - } - int pageObjNum = dest.getPageDestObjNumber(); - Integer destPg = _pageSeqMap.get(new Integer(pageObjNum)); - if (destPg != null) { - propList.add(new Property(propName, PropertyType.INTEGER, - destPg)); - } - } - } catch (Exception e) { - - String msg = e.getClass().getName(); - String msg1 = e.getMessage(); - if (msg1 != null) { - msg = msg + ": " + msg1; - } - JhoveMessage message = JhoveMessages.getMessageInstance( - MessageConstants.PDF_HUL_122.getId(), msg); - propList.add( - new Property(propName, PropertyType.STRING, PROP_VAL_NULL)); - info.setMessage(new ErrorMessage(message, // PDF-HUL-122 - _parser.getOffset())); - info.setValid(false); - } - } - - /* - * Build up a property for one of the kinds of fonts - * in the file. - */ - protected Property buildFontProperty(String name, Map map, int fontType) { - List fontList = new LinkedList(); // list of fonts - Iterator fontIter = map.values().iterator(); - while (fontIter.hasNext()) { - // For each font in the map, build a property for it, - // which consists of a list of scalar properties. Each kind - // of font is spec'ed to have a slightly different set of - // properties, grumble... - PdfDictionary dict = (PdfDictionary) fontIter.next(); - List fontPropList = oneFontPropList(dict, fontType); - Property fProp = new Property(PROP_NAME_FONT, PropertyType.PROPERTY, - PropertyArity.LIST, fontPropList); - fontList.add(fProp); - } - return new Property(name, PropertyType.PROPERTY, PropertyArity.LIST, - fontList); - } - - /* Build the Property list for a given font */ - protected List oneFontPropList(PdfDictionary dict, int fontType) { - List fontPropList = new LinkedList(); - Property prop; - if (fontType == F_TYPE1 || fontType == F_TYPE3 || fontType == F_MM1 - || fontType == F_TT) { - PdfObject tempObj = dict.get(DICT_KEY_NAME); - PdfSimpleObject nameObj = null; - if (tempObj instanceof PdfSimpleObject) { - nameObj = (PdfSimpleObject) tempObj; - } else if (tempObj instanceof PdfIndirectObj) { - nameObj = (PdfSimpleObject) ((PdfIndirectObj) tempObj) - .getObject(); - } - - if (nameObj != null) { - String nameStr = nameObj.getStringValue(); - prop = new Property(DICT_KEY_NAME, PropertyType.STRING, - nameStr); - fontPropList.add(prop); - } - } - - String baseStr = null; - if (fontType != F_TYPE3) { - PdfObject tempObj = dict.get(DICT_KEY_BASE_FONT); - PdfSimpleObject baseFontObj = null; - if (tempObj instanceof PdfSimpleObject) { - baseFontObj = (PdfSimpleObject) tempObj; - } else if (tempObj instanceof PdfIndirectObj) { - baseFontObj = (PdfSimpleObject) ((PdfIndirectObj) tempObj) - .getObject(); - } - - if (baseFontObj != null) { - baseStr = baseFontObj.getStringValue(); - prop = new Property(PROP_NAME_BASE_FONT, PropertyType.STRING, - baseStr); - fontPropList.add(prop); - } - } - - if (fontType == F_CID0 || fontType == F_CID2) { - PdfObject elCid = dict.get(DICT_KEY_CID_INFO); - try { - elCid = resolveIndirectObject(elCid); - } catch (Exception e) { - } - if (elCid instanceof PdfDictionary) { - prop = buildCIDInfoProperty((PdfDictionary) elCid); - fontPropList.add(prop); - } - } - - if (fontType == F_TYPE1 || fontType == F_TT || fontType == F_MM1) { - if (isFontSubset(baseStr)) { - prop = new Property(PROP_NAME_FONT_SUBSET, PropertyType.BOOLEAN, - Boolean.TRUE); - fontPropList.add(prop); - } - } - - if (fontType == F_TYPE1 || fontType == F_TT || fontType == F_MM1 - || fontType == F_TYPE3) { - PdfObject firstCharObj = dict.get(DICT_KEY_FIRST_CHAR); - if (firstCharObj instanceof PdfIndirectObj) { - firstCharObj = ((PdfIndirectObj) firstCharObj).getObject(); - } - try { - int firstChar = ((PdfSimpleObject) firstCharObj).getIntValue(); - prop = new Property(PROP_NAME_FIRST_CHAR, PropertyType.INTEGER, - new Integer(firstChar)); - fontPropList.add(prop); - } catch (Exception e) { - } - - PdfObject lastCharObj = dict.get(DICT_KEY_LAST_CHAR); - if (lastCharObj instanceof PdfIndirectObj) { - lastCharObj = ((PdfIndirectObj) lastCharObj).getObject(); - } - try { - int lastChar = ((PdfSimpleObject) lastCharObj).getIntValue(); - prop = new Property(PROP_NAME_LAST_CHAR, PropertyType.INTEGER, - new Integer(lastChar)); - fontPropList.add(prop); - } catch (Exception e) { - } - } - - if (fontType == F_TYPE3) { - // Put FontBBox and CharProcs into properties - PdfObject bboxObj = dict.get(DICT_KEY_FONT_BBOX); - try { - if (bboxObj instanceof PdfArray) { - fontPropList.add(makeRectProperty((PdfArray) bboxObj, - PROP_VAL_FONT_BBOX)); - } - } catch (Exception e) { - } - - // For CharProcs, we're just checking if it's there. - // (It's required for a Type 3 font.) - // PdfObject charProcs = dict.get("CharProcs"); - // prop = new Property("CharProcs", - // PropertyType.BOOLEAN, - // Boolean.valueOf(charProcs != null)); - // fontPropList.add(prop); - } - - if (fontType == F_TYPE1 || fontType == F_TT || fontType == F_MM1 - || fontType == F_CID0 || fontType == F_CID2) { - PdfObject descriptorObj = dict.get(DICT_KEY_FONT_DESCRIPTOR); - try { - descriptorObj = resolveIndirectObject(descriptorObj); - } catch (Exception e) { - } - if (descriptorObj instanceof PdfDictionary) { - prop = buildFontDescriptorProperty( - (PdfDictionary) descriptorObj); - fontPropList.add(prop); - } - } - - PdfObject encodingObj = dict.get(DICT_KEY_ENCODING); - try { - encodingObj = resolveIndirectObject(encodingObj); - } catch (Exception e) { - } - - if (fontType == F_TYPE0 || fontType == F_TYPE1 || fontType == F_TT - || fontType == F_MM1 || fontType == F_TYPE3) { - // Encoding property -- but only if Encoding is a name - if (encodingObj instanceof PdfSimpleObject) { - prop = new Property(PROP_NAME_ENCODING, PropertyType.STRING, - ((PdfSimpleObject) encodingObj).getStringValue()); - fontPropList.add(prop); - } - } - - if (fontType == F_TYPE1 || fontType == F_TT || fontType == F_MM1 - || fontType == F_TYPE3) { - if (encodingObj != null && encodingObj instanceof PdfDictionary) { - prop = buildEncodingDictProperty((PdfDictionary) encodingObj); - fontPropList.add(prop); - } - } - - if (fontType == F_TYPE0) { - // Encoding is reported as a CMapDictionary property for type 0 - if (encodingObj != null && encodingObj instanceof PdfStream) { - prop = buildCMapDictProperty((PdfStream) encodingObj); - fontPropList.add(prop); - } - } - - if (fontType == F_TYPE3) { - // All we're interested in for Resources is whether - // the dictionary exists - PdfObject rsrc = dict.get(DICT_KEY_RESOURCES); - if (rsrc != null) { - prop = new Property(PROP_NAME_RESOURCES, PropertyType.BOOLEAN, - Boolean.TRUE); - fontPropList.add(prop); - } - } - - if (fontType == F_TYPE0 || fontType == F_TYPE1 || fontType == F_TT - || fontType == F_MM1 || fontType == F_TYPE3) { - PdfObject toUniObj = dict.get(DICT_KEY_TO_UNICODE); - if (toUniObj != null) { - prop = new Property(PROP_NAME_TO_UNICODE, PropertyType.BOOLEAN, - Boolean.TRUE); - fontPropList.add(prop); - } - } - - return fontPropList; - } - - /* - * Code for CMapProperty for Type 0 fonts, based on the Encoding - * entry, broken out of buildFontProperty. - */ - protected Property buildCMapDictProperty(PdfStream encoding) { - PdfDictionary dict = encoding.getDict(); - List propList = new ArrayList(4); - Property prop = new Property(PROP_NAME_CMAP_DICT, PropertyType.PROPERTY, - PropertyArity.LIST, propList); - Property subprop; - - // PdfObject mapName = dict.get ("CMapName"); - - PdfObject cidSysInfo = dict.get(DICT_KEY_CID_INFO); - // We can use buildCIDInfoProperty here to build the subproperty - PdfDictionary cidDict; - List cidList = new LinkedList(); - try { - if (cidSysInfo instanceof PdfDictionary) { - // One CIDInfo dictionary - cidDict = (PdfDictionary) cidSysInfo; - subprop = buildCIDInfoProperty(cidDict); - cidList.add(subprop); - } else if (cidSysInfo instanceof PdfArray) { - // Many CIDInfo dictionaries - Vector v = ((PdfArray) cidSysInfo).getContent(); - for (int i = 0; i < v.size(); i++) { - cidDict = (PdfDictionary) v.elementAt(i); - Property subsubprop = buildCIDInfoProperty(cidDict); - cidList.add(subsubprop); - } - } - } catch (Exception e) { - } - - if (!cidList.isEmpty()) { - subprop = new Property(PROP_NAME_CID_INFOS, PropertyType.PROPERTY, - PropertyArity.LIST, cidList); - propList.add(subprop); - } - - // PdfObject wMod = dict.get("WMode"); - // PdfObject useCMap = dict.get("UseCMap"); - - return prop; - } - - /* - * Code for CIDInfoProperty for CIDFontType0 and CIDFontType2 - * conts. - */ - protected Property buildCIDInfoProperty(PdfDictionary dict) { - List propList = new ArrayList(3); - Property prop = new Property(PROP_NAME_CID_INFO, PropertyType.PROPERTY, - PropertyArity.LIST, propList); - Property subprop; - - // Add the registry identifier - PdfObject reg = dict.get(DICT_KEY_REGISTRY); - if (reg instanceof PdfSimpleObject) { - try { - String regText = ((PdfSimpleObject) reg).getStringValue(); - subprop = new Property(PROP_NAME_REGISTRY, PropertyType.STRING, - _encrypted ? ENCRYPTED : regText); - propList.add(subprop); - } catch (Exception e) { - } - } - - // Add the name of the char collection within the registry - PdfObject order = dict.get(DICT_KEY_ORDERING); - if (reg instanceof PdfSimpleObject) { - try { - String ordText = ((PdfSimpleObject) order).getStringValue(); - subprop = new Property(PROP_NAME_REGISTRY, PropertyType.STRING, - ordText); - propList.add(subprop); - } catch (Exception e) { - } - } - - PdfObject supp = dict.get(DICT_KEY_SUPPLEMENT); - if (supp instanceof PdfSimpleObject) { - try { - int suppvalue = ((PdfSimpleObject) supp).getIntValue(); - subprop = new Property(PROP_NAME_SUPPLEMENT, - PropertyType.INTEGER, new Integer(suppvalue)); - propList.add(subprop); - } catch (Exception e) { - } - } - return prop; - } - - /* - * Code for EncodingDictionary Property for type 1, 3, TrueType, and - * MM fonts. This is based on a dictionary entry with the same name - * as the one for buildCMapDictProperty, but different information. - * Included properties are BaseEncoding and Differences. - */ - protected Property buildEncodingDictProperty(PdfDictionary encodingDict) { - List propList = new ArrayList(2); - Property prop = new Property(PROP_NAME_ENCODING_DICTIONARY, - PropertyType.PROPERTY, PropertyArity.LIST, propList); - PdfObject baseEnc = encodingDict.get(DICT_KEY_BASE_ENCODING); - if (baseEnc instanceof PdfSimpleObject) { - String baseEncString = ((PdfSimpleObject) baseEnc).getStringValue(); - if (baseEncString != null) { - Property baseEncProp = new Property(PROP_NAME_BASE_ENCODING, - PropertyType.STRING, baseEncString); - propList.add(baseEncProp); - } - } - - PdfObject diffs = encodingDict.get(DICT_KEY_DIFFERENCES); - Property diffsProp = new Property(PROP_NAME_DIFFERENCES, - PropertyType.BOOLEAN, Boolean.valueOf(diffs != null)); - propList.add(diffsProp); - - return prop; - } - - /* - * Separated-out code for FontDescriptor property. This - * is a list of six Properies: FontName, Flags, - * FontBBox, FontFile, FontFile2, and FontFile3. - */ - protected Property buildFontDescriptorProperty(PdfDictionary encodingDict) { - List propList = new ArrayList(6); - Property prop = new Property(PROP_NAME_FONT_DESC, PropertyType.PROPERTY, - PropertyArity.LIST, propList); - Property subprop; - try { - PdfSimpleObject fName = (PdfSimpleObject) encodingDict - .get(DICT_KEY_FONT_NAME); - String fNameStr = fName.getStringValue(); - subprop = new Property(PROP_NAME_FONT_NAME, PropertyType.STRING, - fNameStr); - propList.add(subprop); - } catch (Exception e) { - } - - try { - PdfSimpleObject flags = (PdfSimpleObject) encodingDict - .get(DICT_KEY_FLAGS); - int flagValue = flags.getIntValue(); - subprop = buildBitmaskProperty(flagValue, PROP_NAME_FLAGS, - PdfStrings.FONTDESCFLAGS, PROP_VAL_NO_FLAGS_SET); - if (subprop != null) { - propList.add(subprop); - } - } catch (Exception e) { - } - - try { - PdfArray bboxObj = (PdfArray) encodingDict.get(DICT_KEY_FONT_BBOX); - double[] bbox = bboxObj.toRectangle(); - // toRectangle is written to return an array of double, - // which is what the bounding box is in the most general - // case; but the spec requires an array of integer, so - // we convert is. This may seem like an excess of work, - // but I'd rather have toRectangle do the right thing - // rather than losing generality. - if (bbox != null) { - int[] ibbox = new int[4]; - for (int i = 0; i < 4; i++) { - ibbox[i] = (int) bbox[i]; - } - subprop = new Property(PROP_NAME_FONT_BBOX, - PropertyType.INTEGER, PropertyArity.ARRAY, ibbox); - propList.add(subprop); - } - } catch (Exception e) { - } - - PdfObject fontFile = encodingDict.get(DICT_KEY_FONT_FILE); - if (fontFile != null) { - // All we care about is whether it exists or not - subprop = new Property(PROP_NAME_FONT_FILE, PropertyType.BOOLEAN, - Boolean.TRUE); - propList.add(subprop); - } - fontFile = encodingDict.get(DICT_KEY_FONT_FILE_2); - if (fontFile != null) { - subprop = new Property(PROP_NAME_FONT_FILE_2, PropertyType.BOOLEAN, - Boolean.TRUE); - propList.add(subprop); - } - fontFile = encodingDict.get(DICT_KEY_FONT_FILE_3); - if (fontFile != null) { - subprop = new Property(PROP_NAME_FONT_FILE_3, PropertyType.BOOLEAN, - Boolean.TRUE); - propList.add(subprop); - } - return prop; - } - - protected Property buildViewPrefProperty(PdfDictionary prefDict) { - Property p; - PdfObject ob; - boolean b; - String s; - List propList = new ArrayList(12); - Property prop = new Property(DICT_KEY_VIEWER_PREFS, - PropertyType.PROPERTY, PropertyArity.LIST, propList); - - ob = prefDict.get(DICT_KEY_HIDE_TOOLBAR); - if (ob instanceof PdfSimpleObject) { - b = ((PdfSimpleObject) ob).isTrue(); - } else { - b = false; - } - p = new Property(PROP_NAME_HIDE_TOOLBAR, PropertyType.BOOLEAN, - Boolean.valueOf(b)); - propList.add(p); - - ob = prefDict.get(DICT_KEY_HIDE_MENUBAR); - if (ob instanceof PdfSimpleObject) { - b = ((PdfSimpleObject) ob).isTrue(); - } else { - b = false; - } - p = new Property(PROP_NAME_HIDE_MENUBAR, PropertyType.BOOLEAN, - Boolean.valueOf(b)); - propList.add(p); - - ob = prefDict.get(DICT_KEY_HIDE_WINDOW_UI); - if (ob instanceof PdfSimpleObject) { - b = ((PdfSimpleObject) ob).isTrue(); - } else { - b = false; - } - p = new Property(PROP_NAME_HIDE_WINDOW_UI, PropertyType.BOOLEAN, - Boolean.valueOf(b)); - propList.add(p); - - ob = prefDict.get(DICT_KEY_FIT_WINDOW); - if (ob instanceof PdfSimpleObject) { - b = ((PdfSimpleObject) ob).isTrue(); - } else { - b = false; - } - p = new Property(PROP_NAME_FIT_WINDOW, PropertyType.BOOLEAN, - Boolean.valueOf(b)); - propList.add(p); - - ob = prefDict.get(DICT_KEY_CENTER_WINDOW); - if (ob instanceof PdfSimpleObject) { - b = ((PdfSimpleObject) ob).isTrue(); - } else { - b = false; - } - p = new Property(PROP_NAME_CENTER_WINDOW, PropertyType.BOOLEAN, - Boolean.valueOf(b)); - propList.add(p); - - ob = prefDict.get(DICT_KEY_DISP_DOC_TITLE); - if (ob instanceof PdfSimpleObject) { - b = ((PdfSimpleObject) ob).isTrue(); - } else { - b = false; - } - p = new Property(PROP_NAME_DISP_DOC_TITLE, PropertyType.BOOLEAN, - Boolean.valueOf(b)); - propList.add(p); - - ob = prefDict.get(DICT_KEY_NO_FULL_PAGE); - if (ob instanceof PdfSimpleObject) { - s = ((PdfSimpleObject) ob).getStringValue(); - } else - s = DEFAULT_MODE; - p = new Property(PROP_NAME_NO_FULL_PAGE, PropertyType.STRING, s); - propList.add(p); - - ob = prefDict.get(DICT_KEY_DIRECTION); - if (ob instanceof PdfSimpleObject) { - s = ((PdfSimpleObject) ob).getStringValue(); - } else - s = "L2R"; - p = new Property(PROP_NAME_DIRECTION, PropertyType.STRING, s); - propList.add(p); - - ob = prefDict.get(DICT_KEY_VIEW_AREA); - if (ob instanceof PdfSimpleObject) { - s = ((PdfSimpleObject) ob).getStringValue(); - } else - s = PROP_VAL_CROP_BOX; - p = new Property(PROP_NAME_VIEW_AREA, PropertyType.STRING, s); - propList.add(p); - - ob = prefDict.get(DICT_KEY_VIEW_CLIP); - if (ob instanceof PdfSimpleObject) { - s = ((PdfSimpleObject) ob).getStringValue(); - } else - s = PROP_VAL_CROP_BOX; - p = new Property(PROP_NAME_VIEW_CLIP, PropertyType.STRING, s); - propList.add(p); - - ob = prefDict.get(DICT_KEY_PRINT_AREA); - if (ob instanceof PdfSimpleObject) { - s = ((PdfSimpleObject) ob).getStringValue(); - } else - s = PROP_VAL_CROP_BOX; - p = new Property(PROP_NAME_PRINT_AREA, PropertyType.STRING, s); - propList.add(p); - - ob = prefDict.get(DICT_KEY_PAGE_CLIP); - if (ob instanceof PdfSimpleObject) { - s = ((PdfSimpleObject) ob).getStringValue(); - } else - s = PROP_VAL_CROP_BOX; - p = new Property(PROP_NAME_PAGE_CLIP, PropertyType.STRING, s); - propList.add(p); - return prop; - } - - /* - * Return TRUE if the string is a font subset string, which begins - * with six uppercase letters and then a plus sign - */ - protected boolean isFontSubset(String baseStr) { - if (baseStr == null || baseStr.length() < 7) { - return false; - } - for (int i = 0; i < 6; i++) { - char ch = baseStr.charAt(i); - if (!Character.isUpperCase(ch)) { - return false; - } - } - return (baseStr.charAt(6) == '+'); - } - - /* - * Create the "Outlines" property from the Outlines item in the - * catalog dictionary. As a side effect, we set the actionsExist - * flag if any Actions are found. Because we check destinations, - * this can't be called till the page tree is built. - * - * Outlines can be recursive, according to Adobe people, so we have - * to track visited nodes. - */ - protected Property buildOutlinesProperty(PdfDictionary dict, RepInfo info) - throws PdfException { - _recursionWarned = false; - _visitedOutlineNodes = new HashSet(); - List itemList = new LinkedList(); - Property prop = new Property(PROP_NAME_OUTLINES, PropertyType.PROPERTY, - PropertyArity.LIST, itemList); - try { - PdfObject item = resolveIndirectObject(dict.get(DICT_KEY_FIRST)); - // In PDF 1.4, "First" and "Last" are unconditionally required. - // However, - // in 1.6, they can be omitted if there are no open or closed - // outline items. - // Strictly speaking, we should do several additional checks, but - // letting the - // outline go as empty seems sufficient. - // if (item == null || !(item instanceof PdfDictionary)) { - // throw new PdfInvalidException("Outline dictionary missing - // required entry"); - // } - int listCount = 0; // Guard against looping - while (item != null) { - Integer onum = new Integer(item.getObjNumber()); - Property p = buildOutlineItemProperty((PdfDictionary) item, - info); - itemList.add(p); - item = resolveIndirectObject( - ((PdfDictionary) item).get(DICT_KEY_NEXT)); - if (item == null) { - break; - } - // Check if this object is its own sibling. (It really does - // happen!) - if (item.getObjNumber() == onum.intValue()) { - if (!_recursionWarned) { - info.setMessage( - new InfoMessage(MessageConstants.PDF_HUL_123)); // PDF-HUL-123 - _recursionWarned = true; - } - break; - } - if (++listCount > 2000) { - break; - } - } - } catch (PdfException e1) { - throw e1; - } catch (Exception e) { - throw new PdfMalformedException(MessageConstants.PDF_HUL_124); // PDF-HUL-124 - } - if (itemList.isEmpty()) { - return null; - } - return prop; - } - - /* - * Create an item property within the outlines hierarchy. If an - * Outline item property has children, then there is a list - * property called "Children" with elements called "Item". - * It calls itself recursively to walk down the outline. - */ - protected Property buildOutlineItemProperty(PdfDictionary dict, - RepInfo info) throws PdfException { - List itemList = new ArrayList(3); - try { - Property prop = new Property(PROP_NAME_ITEM, PropertyType.PROPERTY, - PropertyArity.LIST, itemList); - PdfSimpleObject title = (PdfSimpleObject) resolveIndirectObject( - dict.get(DICT_KEY_TITLE)); - if (title == null) { - throw new PdfInvalidException(MessageConstants.PDF_HUL_125); // PDF-HUL-125 - } - itemList.add(new Property(PROP_NAME_TITLE, PropertyType.STRING, - _encrypted ? ENCRYPTED : title.getStringValue())); - - // Check other required stuff - if (dict.get(DICT_KEY_PARENT) == null) { - throw new PdfInvalidException(MessageConstants.PDF_HUL_126); // PDF-HUL-126 - } - PdfObject cnt = dict.get(DICT_KEY_COUNT); - if (cnt != null && (!(cnt instanceof PdfSimpleObject) - || !(((PdfSimpleObject) cnt) - .getToken() instanceof Numeric))) { - throw new PdfInvalidException(MessageConstants.PDF_HUL_127); // PDF-HUL-127 - } - // The entries for Prev, Next, First, and Last must - // all be indirect references or absent. Just cast them to - // throw an exception if they're something else - @SuppressWarnings("unused") - PdfIndirectObj ob = (PdfIndirectObj) dict.get(DICT_KEY_PREV); - ob = (PdfIndirectObj) dict.get(DICT_KEY_NEXT); - ob = (PdfIndirectObj) dict.get(DICT_KEY_FIRST); - ob = (PdfIndirectObj) dict.get(DICT_KEY_LAST); - - // Check if there are Actions in the outline. This saves going - // through the outlines all over again if a Profile checker - // needs to know this. We flag only the existence of one or more - // Actions - // in the document. - if (dict.get("A") != null) { - _actionsExist = true; - } - - PdfObject destObj = dict.get(DICT_KEY_DEST); - if (destObj != null) { - destObj = resolveIndirectObject(destObj); - Destination dest = new Destination(destObj, this, false); - if (dest.isIndirect()) { - itemList.add(new Property(PROP_NAME_DESTINATION, - PropertyType.STRING, dest.getIndirectDest().getStringValue())); - } else { - int pageObjNum = dest.getPageDestObjNumber(); - Integer destPg = _pageSeqMap.get(new Integer(pageObjNum)); - if (destPg != null) { - itemList.add(new Property(PROP_NAME_DESTINATION, - PropertyType.INTEGER, destPg)); - } - } - } - - PdfDictionary child = (PdfDictionary) resolveIndirectObject( - dict.get(DICT_KEY_FIRST)); - if (child != null) { - List childList = new LinkedList(); - Property childProp = new Property(PROP_NAME_CHILDREN, - PropertyType.PROPERTY, PropertyArity.LIST, childList); - // We aren't catching all possible combinations of looping. Put - // a maximum - // on the list just to be safe. - int listCount = 0; - while (child != null) { - Integer onum = new Integer(child.getObjNumber()); - if (_visitedOutlineNodes.contains(onum)) { - /* We have recursion! */ - if (!_recursionWarned) { - // Warn of recursion - info.setMessage(new InfoMessage( - MessageConstants.PDF_HUL_128)); // PDF-HUL-128 - _recursionWarned = true; - } - } else { - _visitedOutlineNodes.add(onum); - Property p = buildOutlineItemProperty(child, info); - childList.add(p); - } - child = (PdfDictionary) resolveIndirectObject( - child.get(DICT_KEY_NEXT)); - if (child == null) { - break; - } - // Check if this object is its own sibling. (It really does - // happen!) - if (child.getObjNumber() == onum.intValue()) { - if (!_recursionWarned) { - info.setMessage(new InfoMessage( - MessageConstants.PDF_HUL_129)); // PDF-HUL-129 - _recursionWarned = true; - } - break; - } - if (++listCount > 2000) - break; // safety check - } - itemList.add(childProp); - } - return prop; - } catch (PdfException pe) { - throw pe; - } catch (ClassCastException ce) { - throw new PdfInvalidException(MessageConstants.PDF_HUL_130); // PDF-HUL-130 - } catch (Exception e) { - throw new PdfInvalidException(MessageConstants.PDF_HUL_131); // PDF-HUL-131 - } - } - - /* - * This is separated out from readDocCatalogDict, where it - * would otherwise make sense, because we can't build - * the outlines property till we have a page tree to - * locate destinations. - */ - protected boolean doOutlineStuff(RepInfo info) { - if (_outlineDict != null) { - try { - Property oprop = buildOutlinesProperty(_outlineDict, info); - if (_showOutlines || _verbosity == Module.MAXIMUM_VERBOSITY) { - if (oprop != null) { - _docCatalogList.add(oprop); - } - } else if (!_skippedOutlinesReported) { - // We report that we aren't reporting skipped outlines - info.setMessage( - new InfoMessage(MessageConstants.PDF_HUL_132)); // PDF-HUL-132 - _skippedOutlinesReported = true; - } - } catch (PdfException e) { - if (e.getJhoveMessage() != null) - info.setMessage(new ErrorMessage(e.getJhoveMessage(), _parser.getOffset())); - else - info.setMessage( - new ErrorMessage(e.getMessage(), _parser.getOffset())); - e.disparage(info); - // If it's just invalid, we can keep going - return (e instanceof PdfInvalidException); - } - } - return true; - } - - /* - * Given a PdfSimpleObject representing a key, - * look up the Destination which it references. - * There are two completely different ways this can be done, - * though any given PDF file is supposed to implement only one. - * If _destsDict is non-null, we look the string up there, and - * may find either a dictionary or an array. Otherwise - * if _destNames is non-null, it's a NameTreeNode which contains - * the mapping. In either case, the destination could be - * external, in which case we just return a string saying so. - * (The implementation of Destinations in PDF is a prime example - * of design by stone soup.) - * We return the page sequence number for the referenced page. - * If we can't find a match for the reference, we return -1. - */ - protected int resolveIndirectDest(PdfSimpleObject key, RepInfo info) throws PdfException { - if (key == null) { - throw new IllegalArgumentException("Argument key can not be null"); - } - _logger.finest("Looking for indirectly referenced Dest: " - + key.getStringValue()); - if (_destNames == null) return -1; - PdfObject destObj = _destNames.get(key.getRawBytes()); - // Was the Dest this annotation refers to found in the document? - if (destObj == null) { - // Treat this condition as invalid: - String mess = MessageFormat.format( - MessageConstants.PDF_HUL_149.getMessage(), - key.getStringValue()); - JhoveMessage message = JhoveMessages.getMessageInstance( - MessageConstants.PDF_HUL_149.getId(), mess); - info.setMessage(new ErrorMessage(message)); - throw new PdfInvalidException(message); // PDF-HUL-149 - // OR if this is not considered invalid - // return -1; - } - Destination dest = new Destination(destObj, this, true); - return dest.getPageDestObjNumber(); - } - - /* Build the user permission property., */ - protected Property buildUserPermProperty(int flags, String[] flagStrs) { - return buildBitmaskProperty(flags, "UserAccess", flagStrs, - "No permissions"); - } - - /** - * Add a string property, based on a dictionary entry - * with a string value, to a specified List. - */ - protected void addStringProperty(PdfDictionary dict, - List propList, String key, String propName) { - String propText = null; - PdfObject propObject = dict.get(key); - if (propObject instanceof PdfSimpleObject) { - Token tok = ((PdfSimpleObject) propObject).getToken(); - if (tok instanceof Literal) { - if (_encrypted) { - propText = ENCRYPTED; - } else { - propText = ((Literal) tok).getValue(); - } - propList.add( - new Property(propName, PropertyType.STRING, propText)); - } - } - } - - /** - * Add a date property, based on a dictionary entry - * with a string value, to a specified List. - */ - protected void addDateProperty(PdfDictionary dict, List propList, - String key, String propName) throws PdfException { - if (_encrypted) { - return; // can't decipher an encrypted date - } - PdfObject propObject = dict.get(key); - if (propObject instanceof PdfSimpleObject) { - Token tok = ((PdfSimpleObject) propObject).getToken(); - if (tok instanceof Literal) { - Literal lit = (Literal) tok; - Date propDate = lit.parseDate(); - if (propDate != null) { - propList.add(new Property(propName, PropertyType.DATE, propDate)); - // Ignore empty literals as this isn't an error - } else if (!lit.getValue().isEmpty()) { - throw new PdfInvalidException(MessageConstants.PDF_HUL_133, 0); // PDF-HUL-133 - } - } - } - } - - /* - * General function for adding a property with a 32-bit - * value, with an array of Strings to interpret - * the value as a bitmask. - */ - protected Property buildBitmaskProperty(int val, String name, - String[] valueNames, String defaultStr) { - if (_je != null && _je.getShowRawFlag()) { - return new Property(name, PropertyType.INTEGER, new Integer(val)); - } - List slist = new LinkedList(); - try { - for (int i = 0; i < valueNames.length; i++) { - if ((val & (1 << i)) != 0 && valueNames[i].length() > 0) { - slist.add(valueNames[i]); - } - } - // Provision for a default string if the property - // would otherwise have an empty list - if (slist.isEmpty() && defaultStr != null) { - slist.add(defaultStr); - } - } catch (Exception e) { - return null; - } - return new Property(name, PropertyType.STRING, PropertyArity.LIST, - slist); - } - - /* - * Take a PdfArray which is supposed to conform to the rectangle - * description (i.e., it's an array of 4 numbers) and create - * a Property which is an array of 4 integers. - */ - protected Property makeRectProperty(PdfArray arrObj, String name) { - int[] iarr = new int[4]; - double[] arr = arrObj.toRectangle(); - // toRectangle is written to return an array of double, - // which is what the bounding box is in the most general - // case; but the spec requires an array of integer, so - // we convert it. This may seem like an excess of work, - // but I'd rather have toRectangle do the right thing - // rather than losing generality. - for (int i = 0; i < 4; i++) { - iarr[i] = (int) arr[i]; - } - return new Property(name, PropertyType.INTEGER, PropertyArity.ARRAY, - iarr); - } - - private static boolean checkTypeKey(final PdfDictionary dict, - final RepInfo info, final String expctVal, - final JhoveMessage typeInvalMess, - final JhoveMessage typeNotFoundMess, - final JhoveMessage typeNotSimpleMess) { - // Get the type key from the dictionary - PdfObject typeObj = dict.get(DICT_KEY_TYPE); - if (typeObj != null && typeObj instanceof PdfSimpleObject) { - // If the type key is not null and is a simple object - String typeValue = ((PdfSimpleObject) typeObj).getStringValue(); - if (!expctVal.equals(typeValue)) { - // If the type key value is not of the expected value - info.setWellFormed(false); - info.setMessage(new ErrorMessage(typeInvalMess, 0)); - return false; - } - } else { - // There's no type key or it's not a simple object - // Choose message depending on whether the value is null or of - // the wrong type - JhoveMessage message = (typeObj == null) ? typeNotFoundMess - : typeNotSimpleMess; - info.setMessage(new ErrorMessage(message, 0)); - info.setWellFormed(false); - return false; - } - return true; - } - - private static String imageMimeFromFilters(Filter[] filters) { - // If there's no filters it's a PNG - if (filters == null || filters.length == 0) { - return "image/png"; - } - // Iterate the filter list - for (Filter filt : filters) { - // Get the Filter name - String filterName = filt.getFilterName(); - // And the MIME type from htat - String mime = imageMimeFromFilterName(filterName); - if (mime != null) { - // If it's not null then return - return mime; - } - // Next filter - } - // No MIME type match made for filter list - return null; - } - - // Stolen from an Apache PDF Box method: - // https://github.com/apache/pdfbox/blob/2.0/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/graphics/image/PDImageXObject.java#L767 - private static String imageMimeFromFilterName(final String filterName) { - if (FILTER_NAME_DCT.equals(filterName)) { - // DCTDecode is JPEG - return "image/jpg"; - } else if (FILTER_NAME_JPX.equals(filterName)) { - // JPX Decode for JPX (JP2K) - return "image/jpx"; - } else if (FILTER_NAME_CCITT.equals(filterName)) { - // CCITT is a TIFF image - return "image/tiff"; - } else if (FILTER_NAME_FLATE.equals(filterName) - || FILTER_NAME_LZW.equals(filterName) - || FILTER_NAME_RUN_LENGTH.equals(filterName)) { - // There's a bunch of PNG possibilities - return "image/png"; - } - // No match made - return null; - } - - private PdfObject getObjectFromStream(final int objIndex, - final int recGuard) throws PdfMalformedException { - /* - * The object is located in an object stream. Need to get the - * object stream first. - * Be cautious dealing with _cachedStreamIndex and _cachedObjectStream; - * these can be modified by a recursive call to getObject. - */ - try { - int objStreamIndex = _xref2[objIndex][0]; - PdfObject streamObj; - ObjectStream ostrm = null; - if (objStreamIndex == _cachedStreamIndex) { - ostrm = _cachedObjectStream; - // Reset it - if (ostrm.isValid()) { - ostrm.readIndex(); - } - } else { - streamObj = resolveIndirectObject( - getObject(objStreamIndex, recGuard - 1)); - if (streamObj instanceof PdfStream) { - ostrm = new ObjectStream((PdfStream) streamObj, _raf); - if (ostrm.isValid()) { - ostrm.readIndex(); - _cachedObjectStream = ostrm; - _cachedStreamIndex = objStreamIndex; - } else { - throw new PdfMalformedException( - MessageConstants.PDF_HUL_108); // PDF-HUL-108 - } - } - } - /* And finally extract the object from the object stream. */ - return ostrm.getObject(objIndex); - } catch (ZipException excep) { - _logger.info(excep.getMessage()); - throw new PdfMalformedException(MessageConstants.PDF_HUL_109); // PDF-HUL-109 - } catch (Exception e) { - _logger.info(e.getMessage()); - /* Fall through with error */ - } - throw new PdfMalformedException(MessageConstants.PDF_HUL_110); // PDF-HUL-110 - } + public static final String MIME_TYPE = "application/pdf"; + public static final String EXT = ".pdf"; + public static final int MAX_PAGE_TREE_DEPTH = 100; + public static final int MAX_OBJ_STREAM_DEPTH = 30; + + private static final String ENCODING_PREFIX = "ENC="; + + private static final String DEFAULT_PAGE_LAYOUT = "SinglePage"; + private static final String DEFAULT_MODE = "UseNone"; + + private static final String FILTER_NAME_CCITT = "CCITTFaxDecode"; + private static final String FILTER_NAME_CRYPT = "Crypt"; + private static final String FILTER_NAME_DCT = "DCTDecode"; + private static final String FILTER_NAME_FLATE = "FlateDecode"; + private static final String FILTER_NAME_JPX = "JPXDecode"; + private static final String FILTER_NAME_LZW = "LZWDecode"; + private static final String FILTER_NAME_RUN_LENGTH = "RunLengthDecode"; + + private static final String FILTER_VAL_STANDARD = "Standard"; + + private static final String RESOURCE_NAME_XOBJECT = "XObject"; + + private static final String FONT_TYPE0 = "Type0"; + private static final String FONT_TYPE1 = "Type1"; + private static final String FONT_TYPE3 = "Type3"; + private static final String FONT_MM_TYPE1 = "MMType1"; + private static final String FONT_TRUE_TYPE = "TrueType"; + private static final String FONT_CID_TYPE0 = "CIDFontType0"; + private static final String FONT_CID_TYPE2 = "CIDFontType2"; + + private static final String ACTION_VAL_GOTO = "GoTo"; + + private static final String DICT_KEY_DIRECTION = "Direction"; + + private static final String DICT_KEY_CENTER_WINDOW = "CenterWindow"; + private static final String DICT_KEY_DISP_DOC_TITLE = "DisplayDocTitle"; + private static final String DICT_KEY_FIT_WINDOW = "FitWindow"; + private static final String DICT_KEY_HIDE_MENUBAR = "HideMenubar"; + private static final String DICT_KEY_HIDE_TOOLBAR = "HideToolbar"; + private static final String DICT_KEY_HIDE_WINDOW_UI = "HideWindowUI"; + private static final String DICT_KEY_NO_FULL_PAGE = "NonFullScreenPageMode"; + private static final String DICT_KEY_PAGE_CLIP = "PageClip"; + private static final String DICT_KEY_PRINT_AREA = "PrintArea"; + private static final String DICT_KEY_VIEW_AREA = "ViewArea"; + private static final String DICT_KEY_VIEW_CLIP = "ViewClip"; + + private static final String PROP_NAME_CENTER_WINDOW = DICT_KEY_CENTER_WINDOW; + private static final String PROP_NAME_DISP_DOC_TITLE = DICT_KEY_DISP_DOC_TITLE; + private static final String PROP_NAME_FIT_WINDOW = DICT_KEY_FIT_WINDOW; + private static final String PROP_NAME_HIDE_MENUBAR = DICT_KEY_HIDE_MENUBAR; + private static final String PROP_NAME_HIDE_TOOLBAR = DICT_KEY_HIDE_TOOLBAR; + private static final String PROP_NAME_HIDE_WINDOW_UI = DICT_KEY_HIDE_WINDOW_UI; + private static final String PROP_NAME_NO_FULL_PAGE = DICT_KEY_NO_FULL_PAGE; + private static final String PROP_NAME_PAGE_CLIP = DICT_KEY_PAGE_CLIP; + private static final String PROP_NAME_PRINT_AREA = DICT_KEY_PRINT_AREA; + private static final String PROP_NAME_VIEW_AREA = DICT_KEY_VIEW_AREA; + private static final String PROP_NAME_VIEW_CLIP = DICT_KEY_VIEW_CLIP; + private static final String PROP_NAME_DIRECTION = DICT_KEY_DIRECTION; + + private static final String DICT_KEY_FONT_DESCRIPTOR = "FontDescriptor"; + private static final String DICT_KEY_STARTXREF = "startxref"; + private static final String DICT_KEY_BASE_FONT = "BaseFont"; + private static final String DICT_KEY_CONTENTS = "Contents"; + private static final String DICT_KEY_CID_INFO = "CIDSystemInfo"; + private static final String DICT_KEY_DIFFERENCES = "Differences"; + private static final String DICT_KEY_RESOURCES = "Resources"; + private static final String DICT_KEY_TO_UNICODE = "ToUnicode"; + private static final String DICT_KEY_ROOT = "Root"; + private static final String DICT_KEY_RECT = "Rect"; + private static final String DICT_KEY_DEST = "Dest"; + private static final String DICT_KEY_FIRST_CHAR = "FirstChar"; + private static final String DICT_KEY_LAST_CHAR = "LastChar"; + private static final String DICT_KEY_TRAILER = "trailer"; + private static final String DICT_KEY_SIZE = "Size"; + private static final String DICT_KEY_ENCRYPT = "Encrypt"; + private static final String DICT_KEY_INFO = "Info"; + private static final String DICT_KEY_ID = "ID"; + private static final String DICT_KEY_FONT_NAME = "FontName"; + private static final String DICT_KEY_FONT_FILE = "FontFile"; + private static final String DICT_KEY_FONT_FILE_2 = DICT_KEY_FONT_FILE + "2"; + private static final String DICT_KEY_FONT_FILE_3 = DICT_KEY_FONT_FILE + "3"; + private static final String DICT_KEY_BBOX = "BBox"; + private static final String DICT_KEY_FONT_BBOX = "Font" + DICT_KEY_BBOX; + private static final String DICT_KEY_XREF_STREAM = "XRefStm"; + private static final String DICT_KEY_VIEWER_PREFS = "ViewerPreferences"; + private static final String DICT_KEY_PAGE_LAYOUT = "PageLayout"; + private static final String DICT_KEY_PAGE_MODE = "PageMode"; + private static final String DICT_KEY_OUTLINES = "Outlines"; + private static final String DICT_KEY_ORDERING = "Ordering"; + private static final String DICT_KEY_REGISTRY = "Registry"; + private static final String DICT_KEY_SUPPLEMENT = "Supplement"; + private static final String DICT_KEY_LANG = "Lang"; + private static final String DICT_KEY_PAGES = "Pages"; + private static final String DICT_KEY_PAGE_LABELS = "PageLabels"; + private static final String DICT_KEY_TYPE = "Type"; + private static final String DICT_KEY_VERSION = "Version"; + private static final String DICT_KEY_NAME = "Name"; + private static final String DICT_KEY_NAMES = DICT_KEY_NAME + "s"; + private static final String DICT_KEY_EMBEDDED_FILES = "EmbeddedFiles"; + private static final String DICT_KEY_DESTS = "Dests"; + private static final String DICT_KEY_FILTER = "Filter"; + private static final String DICT_KEY_K = "K"; + private static final String DICT_KEY_P = "P"; + private static final String DICT_KEY_R = "R"; + private static final String DICT_KEY_V = "V"; + private static final String DICT_KEY_ENCODING = "Encoding"; + private static final String DICT_KEY_BASE_ENCODING = "Base" + DICT_KEY_ENCODING; + private static final String DICT_KEY_LENGTH = "Length"; + private static final String DICT_KEY_WIDTH = "Width"; + private static final String DICT_KEY_HEIGHT = "Height"; + private static final String DICT_KEY_KEY_LENGTH = "KeyLength"; + private static final String DICT_KEY_TITLE = "Title"; + private static final String DICT_KEY_AUTHOR = "Author"; + private static final String DICT_KEY_SUBJECT = "Subject"; + private static final String DICT_KEY_KEYWORDS = "Keywords"; + private static final String DICT_KEY_CREATOR = "Creator"; + private static final String DICT_KEY_PRODUCER = "Producer"; + private static final String DICT_KEY_CREATION_DATE = "CreationDate"; + private static final String DICT_KEY_MODIFIED_DATE = "ModDate"; + private static final String DICT_KEY_TRAPPED = "Trapped"; + private static final String DICT_KEY_XOBJ_SUBTYPE = "Subtype"; + private static final String DICT_KEY_FONT_SUBTYPE = DICT_KEY_XOBJ_SUBTYPE; + private static final String DICT_KEY_DECODE_PARAMS = "DecodeParms"; + private static final String DICT_KEY_COLOR_SPACE = "ColorSpace"; + private static final String DICT_KEY_METADATA = "Metadata"; + private static final String DICT_KEY_BITS_PER_COMPONENT = "BitsPerComponent"; + private static final String DICT_KEY_INTENT = "Intent"; + private static final String DICT_KEY_IMAGE_MASK = "ImageMask"; + private static final String DICT_KEY_DECODE = "Decode"; + private static final String DICT_KEY_INTERPOLATE = "Interpolate"; + private static final String DICT_KEY_DESCENDANT_FONTS = "DescendantFonts"; + private static final String DICT_KEY_ROTATE = "Rotate"; + private static final String DICT_KEY_USER_UNIT = "UserUnit"; + private static final String DICT_KEY_VIEWPORT = "VP"; + private static final String DICT_KEY_THUMB = "Thumb"; + private static final String DICT_KEY_MEASURE = "Measure"; + private static final String DICT_KEY_COUNT = "Count"; + private static final String DICT_KEY_PARENT = "Parent"; + private static final String DICT_KEY_PREV = "Prev"; + private static final String DICT_KEY_NEXT = "Next"; + private static final String DICT_KEY_FIRST = "First"; + private static final String DICT_KEY_LAST = "Last"; + private static final String DICT_KEY_FLAGS = "Flags"; + + private static final String KEY_VAL_CATALOG = "Catalog"; + private static final String KEY_VAL_PAGES = "Pages"; + + private static final String PROP_NAME_BASE_FONT = DICT_KEY_BASE_FONT; + private static final String PROP_NAME_CALLOUT_LINE = "CalloutLine"; + private static final String PROP_NAME_CMAP_DICT = "CMapDictionary"; + private static final String PROP_NAME_CID_INFO = DICT_KEY_CID_INFO; + private static final String PROP_NAME_CID_INFOS = PROP_NAME_CID_INFO + "s"; + private static final String PROP_NAME_CONTENTS = DICT_KEY_CONTENTS; + private static final String PROP_NAME_DISTANCE = "Distance"; + private static final String PROP_NAME_DIFFERENCES = DICT_KEY_DIFFERENCES; + private static final String PROP_NAME_ENCODING = DICT_KEY_ENCODING; + private static final String PROP_NAME_ENCODING_DICTIONARY = PROP_NAME_ENCODING + "Dictionary"; + private static final String PROP_NAME_BASE_ENCODING = DICT_KEY_BASE_ENCODING; + private static final String PROP_NAME_EXTERNAL_STREAMS = "ExternalStreams"; + private static final String PROP_NAME_FILTER = DICT_KEY_FILTER; + private static final String PROP_NAME_FILTERS = "Filters"; + private static final String PROP_NAME_FILE = "File"; + private static final String PROP_NAME_FIRST_CHAR = DICT_KEY_FIRST_CHAR; + private static final String PROP_NAME_FLAGS = DICT_KEY_FLAGS; + private static final String PROP_NAME_AREA = "Area"; + private static final String PROP_NAME_IMAGE = "Image"; + private static final String PROP_NAME_IMAGES = PROP_NAME_IMAGE + "s"; + private static final String PROP_NAME_OBJECTS = "Objects"; + private static final String PROP_NAME_RESOURCES = DICT_KEY_RESOURCES; + private static final String PROP_NAME_SUBTYPE = DICT_KEY_XOBJ_SUBTYPE; + private static final String PROP_NAME_FREE_OBJECTS = "FreeObjects"; + private static final String PROP_NAME_INC_UPDATES = "IncrementalUpdates"; + private static final String PROP_NAME_DOC_CATALOG = "DocumentCatalog"; + private static final String PROP_NAME_ENCRYPTION = "Encryption"; + private static final String PROP_NAME_KEY_LENGTH = DICT_KEY_KEY_LENGTH; + private static final String PROP_NAME_INFO = DICT_KEY_INFO; + private static final String PROP_NAME_DESTINATION = "Destination"; + private static final String PROP_NAME_CHILDREN = "Children"; + private static final String PROP_NAME_PAGE_LAYOUT = DICT_KEY_PAGE_LAYOUT; + private static final String PROP_NAME_LANG = "Language"; + private static final String PROP_NAME_LAST_CHAR = DICT_KEY_LAST_CHAR; + private static final String PROP_NAME_MEASURE = DICT_KEY_MEASURE; + private static final String PROP_NAME_SECURITY_HANDLER = "SecurityHandler"; + private static final String PROP_NAME_EFF = "EFF"; + private static final String PROP_NAME_ALGORITHM = "Algorithm"; + private static final String PROP_NAME_RECT = DICT_KEY_RECT; + private static final String PROP_NAME_REVISION = "Revision"; + private static final String PROP_NAME_OWNER_STRING = "OwnerString"; + private static final String PROP_NAME_USER_STRING = "UserString"; + private static final String PROP_NAME_OWNERKEY_STRING = "OwnerEncryptionKey"; + private static final String PROP_NAME_USERKEY_STRING = "UserEncryptionKey"; + private static final String PROP_NAME_USER_UNIT = DICT_KEY_USER_UNIT; + private static final String PROP_NAME_STANDARD_SECURITY_HANDLER = "StandardSecurityHandler"; + private static final String PROP_NAME_TITLE = DICT_KEY_TITLE; + private static final String PROP_NAME_AUTHOR = DICT_KEY_AUTHOR; + private static final String PROP_NAME_SUBJECT = DICT_KEY_SUBJECT; + private static final String PROP_NAME_KEYWORDS = DICT_KEY_KEYWORDS; + private static final String PROP_NAME_CREATOR = DICT_KEY_CREATOR; + private static final String PROP_NAME_PRODUCER = DICT_KEY_PRODUCER; + private static final String PROP_NAME_CREATION_DATE = DICT_KEY_CREATION_DATE; + private static final String PROP_NAME_MODIFIED_DATE = DICT_KEY_MODIFIED_DATE; + private static final String PROP_NAME_TRAPPED = DICT_KEY_TRAPPED; + private static final String PROP_NAME_FILTER_PIPELINE = "FilterPipeline"; + private static final String PROP_NAME_NISO_IMAGE_MD = "NisoImageMetadata"; + private static final String PROP_NAME_COLOR_SPACE = DICT_KEY_COLOR_SPACE; + private static final String PROP_NAME_ACTION_DEST = "ActionDest"; + private static final String PROP_NAME_ANNOTATION = "Annotation"; + private static final String PROP_NAME_APP_DICT = "AppearanceDictionary"; + private static final String PROP_NAME_INTENT = DICT_KEY_INTENT; + private static final String PROP_NAME_IMAGE_MASK = DICT_KEY_IMAGE_MASK; + private static final String PROP_NAME_DECODE = DICT_KEY_DECODE; + private static final String PROP_NAME_NAME = DICT_KEY_NAME; + private static final String PROP_NAME_ID = DICT_KEY_ID; + private static final String PROP_NAME_ITEM = "Item"; + private static final String PROP_NAME_INTERPOLATE = DICT_KEY_INTERPOLATE; + private static final String PROP_NAME_FONT_TYPE0 = FONT_TYPE0; + private static final String PROP_NAME_FONT_TYPE1 = FONT_TYPE1; + private static final String PROP_NAME_FONT_TYPE3 = FONT_TYPE3; + private static final String PROP_NAME_FONT_MM_TYPE1 = FONT_MM_TYPE1; + private static final String PROP_NAME_FONT_TRUE_TYPE = FONT_TRUE_TYPE; + private static final String PROP_NAME_FONT_CID_TYPE0 = FONT_CID_TYPE0; + private static final String PROP_NAME_FONT_CID_TYPE2 = FONT_CID_TYPE2; + private static final String PROP_NAME_FONT = "Font"; + private static final String PROP_NAME_FONTS = PROP_NAME_FONT + "s"; + private static final String PROP_NAME_FONT_SUBSET = PROP_NAME_FONT + "Subset"; + private static final String PROP_NAME_FONT_BBOX = DICT_KEY_FONT_BBOX; + private static final String PROP_NAME_FONT_DESC = DICT_KEY_FONT_DESCRIPTOR; + private static final String PROP_NAME_FONT_FILE = DICT_KEY_FONT_FILE; + private static final String PROP_NAME_FONT_FILE_2 = DICT_KEY_FONT_FILE_2; + private static final String PROP_NAME_FONT_FILE_3 = DICT_KEY_FONT_FILE_3; + private static final String PROP_NAME_FONT_NAME = DICT_KEY_FONT_NAME; + private static final String PROP_NAME_PDF_METADATA = "PDFMetadata"; + private static final String PROP_NAME_LAST_MOD = "LastModified"; + private static final String PROP_NAME_OUTLINES = DICT_KEY_OUTLINES; + private static final String PROP_NAME_REGISTRY = DICT_KEY_REGISTRY; + private static final String PROP_NAME_SUPPLEMENT = DICT_KEY_SUPPLEMENT; + private static final String PROP_NAME_PAGES = DICT_KEY_PAGES; + private static final String PROP_NAME_SEQUENCE = "Sequence"; + private static final String PROP_NAME_ANNOTATIONS = "Annotations"; + private static final String PROP_NAME_ROTATE = DICT_KEY_ROTATE; + private static final String PROP_NAME_REPLY_TYPE = "ReplyType"; + private static final String PROP_NAME_VIEWPORT = "Viewport"; + private static final String PROP_NAME_VIEWPORTS = PROP_NAME_VIEWPORT + "s"; + private static final String PROP_NAME_THUMB = DICT_KEY_THUMB; + private static final String PROP_NAME_TO_UNICODE = DICT_KEY_TO_UNICODE; + private static final String PROP_NAME_PAGE = "Page"; + private static final String PROP_NAME_LABEL = "Label"; + private static final String PROP_NAME_RATIO = "Ratio"; + + private static final String PROP_VAL_CROP_BOX = "CropBox"; + private static final String PROP_VAL_FONT_BBOX = DICT_KEY_FONT_BBOX; + private static final String PROP_VAL_NULL = "null"; + private static final String PROP_VAL_EXTERNAL = "External"; + private static final String PROP_VAL_NO_FLAGS_SET = "No flags set"; + private static final String XOBJ_SUBTYPE_IMAGE = PROP_NAME_IMAGE; + private static final String EMPTY_LABEL_PROPERTY = "[empty]"; + + /** + * **************************************************************** PRIVATE CLASS FIELDS. + * **************************************************************** + */ + private static final String NAME = "PDF-hul"; + + private static final String RELEASE = "1.12.2"; + private static final int[] DATE = {2019, 12, 10}; + private static final String[] FORMAT = {"PDF", "Portable Document Format"}; + private static final String COVERAGE = + "PDF 1.0-1.6; PDF/X-1 (ISO 15930-1:2001), X-1a (ISO 15930-4:2003), " + + "X-2 (ISO 15930-5:2003), and X-3 (ISO 15930-6:2003); Tagged PDF; " + + "Linearized PDF; PDF/A (ISO/CD 19005-1)"; + private static final String[] MIMETYPE = {MIME_TYPE}; + private static final String WELLFORMED = + "A PDF file is " + + "well-formed if it meets the criteria defined in Chapter " + + "3 of the PDF Reference 1.6 (5th edition, 2004)"; + private static final String VALIDITY = null; + private static final String REPINFO = null; + private static final String NOTE = + "This module does *not* validate " + + "data within content streams (including operators) or encrypted data"; + private static final String RIGHTS = + "Copyright 2003-2007 by JSTOR and " + + "the President and Fellows of Harvard College. " + + "Released under the GNU Lesser General Public License."; + private static final String ENCRYPTED = ""; + + /** Logger for this class. */ + protected Logger _logger; + + /** Font type selectors. */ + public static final int F_TYPE0 = 1, + F_TYPE1 = 2, + F_TT = 3, + F_TYPE3 = 4, + F_MM1 = 5, + F_CID0 = 6, + F_CID2 = 7; + + /** + * **************************************************************** PRIVATE INSTANCE FIELDS. + * **************************************************************** + */ + + /* + * The maximum number of fonts that will be reported before we just + * give up and report a stub to avoid running out of memory. + */ + protected int DEFAULT_MAX_FONTS = 1000; + + /* Constants for trailer parsing */ + private static final int EOFSCANSIZE = 1024; + private static final int XREFSCANSIZE = 128; // generous... + + protected RandomAccessFile _raf; + protected Parser _parser; + protected String _version; + protected Property _metadata; + protected Property _xmpProp; + protected long _eof; + protected long _startxref; + protected long _prevxref; + protected int _numFreeObjects; + protected Property _idProperty; + protected int _objCount; // Count of objects in the cross-reference + // table + protected int _numObjects; // Value of the "Size" entry in the trailer + // dictionary + protected int _numTrailers; // Count of the number of trailers (updates) + protected Map _objects; // Map of the objects in the file + protected long[] _xref; // array of object offsets from xref table + protected int[][] _xref2; // array of int[2], giving object stream and + // offset when _xref[i] < 0 + protected boolean _xrefIsStream; // true if xref streams rather than tables + // are used + protected boolean _encrypted; // equivalent to _encryptDictRef != null + protected List _docCatalogList; // Info extracted from doc cat + // dict + protected List _encryptList; // Info from encryption dict + protected List _docInfoList; // info from doc info dict + protected List _extStreamsList; // List of external streams + protected List _imagesList; // List of image streams + protected List _filtersList; // List of filters + protected List _pagesList; // List of PageObjects + + protected Map _type0FontsMap; // Map of type 0 font + // dictionaries + protected Map _type1FontsMap; // Map of type 1 font + // dictionaries + protected Map _mmFontsMap; // Map of multi master + // font dictionaries + protected Map _type3FontsMap; // Map of type 3 font + // dictionaries + protected Map _trueTypeFontsMap; // Map of TrueType + // font dictionaries + protected Map _cid0FontsMap; // Map of CIDFont/Type1 + // dictionaries + protected Map _cid2FontsMap; // Map of + // CIDFont/TrueType + // dictionaries + + protected Map _pageSeqMap; // Map associating page + // object dicts with + // sequence numbers + + protected PdfIndirectObj _docCatDictRef; + protected PdfIndirectObj _encryptDictRef; + protected PdfIndirectObj _docInfoDictRef; + protected PdfIndirectObj _pagesDictRef; + + protected PdfDictionary _docCatDict; + protected PdfDictionary _docInfoDict; + protected PageTreeNode _docTreeRoot; + protected PdfDictionary _pageLabelDict; + protected PageLabelNode _pageLabelRoot; + protected NameTreeNode _embeddedFiles; + protected NameTreeNode _destNames; + protected PdfDictionary _encryptDict; + protected PdfDictionary _trailerDict; + protected PdfDictionary _viewPrefDict; + protected PdfDictionary _outlineDict; + protected PdfDictionary _destsDict; + + protected boolean _showFonts; + protected boolean _showOutlines; + protected boolean _showAnnotations; + protected boolean _showPages; + + protected boolean _actionsExist; + protected boolean _pdfACompliant; // flag checking PDF/A compliance + + protected boolean _recursionWarned; // Check if warning has been issued on + // recursive outlines. + + /* + * These three variables track whether a message has been posted + * notifying the user of omitted information. + */ + protected boolean _skippedFontsReported; + protected boolean _skippedOutlinesReported; + protected boolean _skippedAnnotationsReported; + protected boolean _skippedPagesReported; + + /** List of profile checkers */ + protected List _profile; + + /** Cached object stream. */ + protected ObjectStream _cachedObjectStream; + + /** Object number of cached object stream. */ + protected int _cachedStreamIndex; + + /** Map of visited nodes when walking through an outline. */ + protected Set _visitedOutlineNodes; + + /** maximum number of fonts to report full information on. */ + protected int maxFonts; + + /** Number of fonts reported so far. */ + protected int _nFonts; + + /* Name-to-value array pairs for NISO metadata */ + private static final String[] compressionStrings = { + FILTER_NAME_LZW, /* "FlateDecode", */ FILTER_NAME_RUN_LENGTH, FILTER_NAME_DCT, FILTER_NAME_CCITT + }; + private static final int[] compressionValues = {5, /* 8, */ 32773, 6, 2}; + /* + * The value of 2 (CCITTFaxDecode) is a placeholder; additional + * checking of the K parameter is needed to determine the real + * value if that's returned. + */ + + private static final String[] colorSpaceStrings = { + "Lab", "DeviceRGB", "DeviceCMYK", "DeviceGray", "Indexed" + }; + private static final int[] colorSpaceValues = {8, 2, 5, 1, 3}; + + /** + * **************************************************************** CLASS CONSTRUCTOR. + * **************************************************************** + */ + + /** Creates an instance of the module and initializes identifying information. */ + public PdfModule() { + super( + NAME, + RELEASE, + DATE, + FORMAT, + COVERAGE, + MIMETYPE, + WELLFORMED, + VALIDITY, + REPINFO, + NOTE, + RIGHTS, + true); + + _logger = Logger.getLogger("edu.harvard.hul.ois.jhove.module"); + + _vendor = Agent.harvardInstance(); + + Document doc = + new Document( + "PDF Reference: Adobe Portable " + "Document Format, Version 1.4", DocumentType.BOOK); + Agent agent = Agent.newAdobeInstance(); + doc.setPublisher(agent); + doc.setDate("2001-12"); + doc.setEdition("3rd edition"); + doc.setIdentifier(new Identifier("0-201-75839-3", IdentifierType.ISBN)); + doc.setIdentifier( + new Identifier( + "http://partners.adobe.com/asn/" + + "acrobat/docs/File_Format_" + + "Specifications/PDFReference.pdf", + IdentifierType.URL)); + _specification.add(doc); + + doc = + new Document( + "PDF Reference: Adobe Portable " + "Document Format, Version 1.5", DocumentType.BOOK); + doc.setPublisher(agent); + doc.setDate("2003"); + doc.setEdition("4th edition"); + doc.setIdentifier( + new Identifier( + "http://partners.adobe.com/public/developer/en/pdf/PDFReference15_v6.pdf", + IdentifierType.URL)); + _specification.add(doc); + + doc = + new Document( + "PDF Reference: Adobe Portable " + "Document Format, Version 1.6", DocumentType.BOOK); + doc.setPublisher(agent); + doc.setDate("2004-11"); + doc.setEdition("5th edition"); + doc.setIdentifier( + new Identifier( + "http://partners.adobe.com/public/developer/en/pdf/PDFReference16.pdf", + IdentifierType.URL)); + _specification.add(doc); + + doc = + new Document( + "Graphic technology -- Prepress " + + "digital data exchange -- Use of PDF -- " + + "Part 1: Complete exchange using CMYK data " + + "(PDF/X-1 and PDF/X-1a)", + DocumentType.STANDARD); + Agent isoAgent = Agent.newIsoInstance(); + doc.setPublisher(isoAgent); + doc.setDate("2001-12-06"); + doc.setIdentifier(new Identifier("ISO 15930-1:2001", IdentifierType.ISO)); + _specification.add(doc); + + doc = + new Document( + "Graphic technology -- Prepress " + + "digital data exchange -- Use of PDF -- " + + "Part 4: Complete exchange using CMYK and " + + "spot colour printing data using " + + "PDF 1.4 (PDF/X-1a)", + DocumentType.STANDARD); + doc.setPublisher(isoAgent); + doc.setDate("2003-08-04"); + doc.setIdentifier(new Identifier("ISO 15930-4:2003", IdentifierType.ISO)); + _specification.add(doc); + + doc = + new Document( + "Graphic technology -- Prepress " + + "digital data exchange -- Use of PDF -- " + + "Part 5: Partial exchange of printing data " + + "using PDF 1.4 (PDF/X-2)", + DocumentType.STANDARD); + doc.setPublisher(isoAgent); + doc.setDate("2003-08-05"); + doc.setIdentifier(new Identifier("ISO 15930-5:2003", IdentifierType.ISO)); + _specification.add(doc); + + doc = + new Document( + "Graphic technology -- Prepress " + + "digital data exchange -- Use of PDF -- " + + "Part 6: Complete exchange suitable for " + + "colour-managed workflows using " + + "PDF 1.4 (PDF/X-3)", + DocumentType.STANDARD); + doc.setPublisher(isoAgent); + doc.setDate("2003-08-06"); + doc.setIdentifier(new Identifier("ISO 15930-6:2003", IdentifierType.ISO)); + _specification.add(doc); + + _signature.add(new ExternalSignature(EXT, SignatureType.EXTENSION, SignatureUseType.OPTIONAL)); + _signature.add( + new InternalSignature( + PdfHeader.PDF_SIG_HEADER, SignatureType.MAGIC, SignatureUseType.MANDATORY, 0)); + + doc = + new Document( + "Document management -- Electronic " + + "document file format for long-term " + + "preservation -- Part 1: Use of PDF (PDF/A)", + DocumentType.RFC); + doc.setPublisher(isoAgent); + doc.setDate("2003-11-30"); + doc.setIdentifier(new Identifier("ISO/CD 19005-1", IdentifierType.ISO)); + doc.setIdentifier( + new Identifier( + "http://www.aiim.org/documents/standards/ISO_19005-1_(E).doc", IdentifierType.URL)); + _specification.add(doc); + + _profile = new ArrayList(6); + _profile.add(new LinearizedProfile(this)); + TaggedProfile tpr = new TaggedProfile(this); + _profile.add(tpr); + AProfile apr = new AProfile(this); + _profile.add(apr); + // Link AProfile to TaggedProfile to save checking + // the former twice. + apr.setTaggedProfile(tpr); + + AProfileLevelA apra = new AProfileLevelA(this); + _profile.add(apra); + // AProfileLevelA depends on AProfile + apra.setAProfile(apr); + + X1Profile x1 = new X1Profile(this); + _profile.add(x1); + X1aProfile x1a = new X1aProfile(this); + _profile.add(x1a); + // Linking the X1 profile to the X1a profile saves checking the former + // twice. + x1a.setX1Profile(x1); + _profile.add(new X2Profile(this)); + _profile.add(new X3Profile(this)); + _showAnnotations = false; + _showFonts = false; + _showOutlines = false; + _showPages = false; + maxFonts = DEFAULT_MAX_FONTS; + } + + /** + * **************************************************************** PUBLIC INSTANCE METHODS. + * + *

Parsing methods. **************************************************************** + */ + + /** Reset parameter settings. Returns to a default state without any parameters. */ + @Override + public void resetParams() { + _showAnnotations = true; + _showFonts = true; + _showOutlines = true; + _showPages = true; + maxFonts = DEFAULT_MAX_FONTS; + } + + /** + * Per-action initialization. May be called multiple times. + * + * @param param The module parameter; under command-line Jhove, the -p parameter. If the parameter + * contains the indicated characters, then the specified information is omitted; otherwise, it + * is included. (This is the reverse of the behavior prior to beta 3.) These characters may be + * provided as separate parameters, or all in a single parameter. + *

    + *
  • a: annotations + *
  • f: fonts + *
  • o: outlines + *
  • p: pages + *
+ *
+ * The parameter is case-independent. A null parameter is equivalent to the empty string. + */ + @Override + public void param(String param) { + if (param != null) { + param = param.toLowerCase(); + if (param.indexOf('a') >= 0) { + _showAnnotations = false; + } + if (param.indexOf('f') >= 0) { + _showFonts = false; + } + if (param.indexOf('o') >= 0) { + _showOutlines = false; + } + if (param.indexOf('p') >= 0) { + _showPages = false; + } + if (param.indexOf('n') >= 0) { + // Parse out the number after the n, and use that to set + // the maximum number of fonts reported. Default is + // DEFAULT_MAX_FONTS. + int n = param.indexOf('n'); + StringBuffer b = new StringBuffer(); + for (int i = n + 1; i < param.length(); i++) { + char ch = param.charAt(i); + if (Character.isDigit(ch)) { + b.append(ch); + } else { + break; + } + } + try { + int mx = Integer.parseInt(b.toString()); + if (mx > 0) { + maxFonts = mx; + } + } catch (Exception e) { + } + } + } + } + + /** + * Parses a file and stores descriptive information. A RandomAccessFile must be used to represent + * the object. + * + * @param raf A PDF file + * @param info A clean RepInfo object, which will be modified to hold the descriptive information + */ + @Override + public final void parse(RandomAccessFile raf, RepInfo info) throws IOException { + initParse(); + initInfo(info); + _objects = new HashMap<>(); + _raf = raf; + + Tokenizer tok = new FileTokenizer(_raf); + _parser = new Parser(tok); + _parser.setObjectMap(_objects); + + List metadataList = new ArrayList(11); + /* + * We construct a big whopping property, + * which contains up to 11 subproperties + */ + _metadata = + new Property( + PROP_NAME_PDF_METADATA, PropertyType.PROPERTY, PropertyArity.LIST, metadataList); + + if (_raf.length() > 10000000000L) { // that's 10^10 + _pdfACompliant = false; // doesn't meet size limit in Appendix C + // of PDF spec + } + if (!parseHeader(info)) { + return; + } + if (!findLastTrailer(info)) { + return; + } + + /* + * Walk through the linked trailer and cross reference + * sections. + */ + _prevxref = -1; + boolean lastTrailer = true; + while (_startxref > 0) { + // After the first (last) trailer, parse only for next "Prev" link + if (!parseTrailer(info, !lastTrailer)) { + return; + } + if (!readXRefInfo(info)) { + return; + } + ++_numTrailers; + if (_xrefIsStream) { + /* + * If we have an xref stream, readXRefInfo dealt with all + * the streams in a single call. + */ + break; + } + // Beware infinite loop on badly broken file + if (_startxref == _prevxref) { + info.setMessage( + new ErrorMessage( + MessageConstants.PDF_HUL_134, // PDF-HUL-134 + _parser.getOffset())); + info.setWellFormed(false); + return; + } + _startxref = _prevxref; + lastTrailer = false; + } + if (!readDocCatalogDict(info)) { + return; + } + if (!readEncryptDict(info)) { + return; + } + if (!readDocInfoDict(info)) { + return; + } + if (!readDocumentTree(info)) { + return; + } + if (!readPageLabelTree(info)) { + return; + } + if (!readXMPData(info)) { + return; + } + findExternalStreams(info); + if (!findFilters(info)) { + return; + } + findImages(info); + findFonts(info); + + /* Object is well-formed PDF. */ + + // Calculate checksums if not already present + checksumIfRafNotCopied(info, raf); + + info.setVersion(_version); + metadataList.add( + new Property(PROP_NAME_OBJECTS, PropertyType.INTEGER, new Integer(_numObjects))); + metadataList.add( + new Property(PROP_NAME_FREE_OBJECTS, PropertyType.INTEGER, new Integer(_numFreeObjects))); + metadataList.add( + new Property(PROP_NAME_INC_UPDATES, PropertyType.INTEGER, new Integer(_numTrailers))); + if (_docCatalogList != null) { + metadataList.add( + new Property( + PROP_NAME_DOC_CATALOG, PropertyType.PROPERTY, PropertyArity.LIST, _docCatalogList)); + } + if (_encryptList != null) { + metadataList.add( + new Property( + PROP_NAME_ENCRYPTION, PropertyType.PROPERTY, PropertyArity.LIST, _encryptList)); + } + if (_docInfoList != null) { + metadataList.add( + new Property(PROP_NAME_INFO, PropertyType.PROPERTY, PropertyArity.LIST, _docInfoList)); + } + if (_idProperty != null) { + metadataList.add(_idProperty); + } + if (_extStreamsList != null && !_extStreamsList.isEmpty()) { + metadataList.add( + new Property( + PROP_NAME_EXTERNAL_STREAMS, + PropertyType.PROPERTY, + PropertyArity.LIST, + _extStreamsList)); + } + if (_filtersList != null && !_filtersList.isEmpty()) { + metadataList.add( + new Property(PROP_NAME_FILTERS, PropertyType.PROPERTY, PropertyArity.LIST, _filtersList)); + } + if (_imagesList != null && !_imagesList.isEmpty()) { + metadataList.add( + new Property(PROP_NAME_IMAGES, PropertyType.PROPERTY, PropertyArity.LIST, _imagesList)); + } + if (_showFonts || _verbosity == Module.MAXIMUM_VERBOSITY) { + try { + addFontsProperty(metadataList); + } catch (NullPointerException e) { + info.setMessage( + new ErrorMessage(MessageConstants.PDF_HUL_135, e.toString())); // PDF-HUL-135 + } + } + if (_nFonts > maxFonts) { + info.setMessage( + new InfoMessage( + MessageConstants.PDF_HUL_136, // PDF-HUL-136 + MessageConstants.PDF_HUL_136_SUB.getMessage() + _nFonts)); + } + if (_xmpProp != null) { + metadataList.add(_xmpProp); + } + addPagesProperty(metadataList, info); + + if (!doOutlineStuff(info)) { + return; + } + + info.setProperty(_metadata); + + /* Check for profile conformance. */ + + if (!_parser.getPDFACompliant()) { + _pdfACompliant = false; + } + if (info.getWellFormed() == RepInfo.TRUE) { + // Well-formedness is necessary to satisfy any profile. + ListIterator pter = _profile.listIterator(); + while (pter.hasNext()) { + PdfProfile prof = pter.next(); + if (prof.satisfiesProfile(_raf, _parser)) { + info.setProfile(prof.getText()); + } + } + } + } + + /** + * Returns true if the module hasn't detected any violations of PDF/A compliance. This must return + * true, but is not sufficient by itself, to establish compliance. The AProfile + * profiler makes the final determination. + */ + public boolean mayBePDFACompliant() { + return _pdfACompliant; + } + + /** Returns the document tree root. */ + public PageTreeNode getDocumentTree() { + return _docTreeRoot; + } + + /** Returns the document information dictionary. */ + public PdfDictionary getDocInfo() { + return _docInfoDict; + } + + /** Returns the encryption dictionary. */ + public PdfDictionary getEncryptionDict() { + return _encryptDict; + } + + /** Return true if Actions have been detected in the file. */ + public boolean getActionsExist() { + return _actionsExist; + } + + /** + * Initialize the module. This is called at the start of parse restore the module to its initial + * state. + */ + @Override + protected final void initParse() { + super.initParse(); + _xref = null; + _xref2 = null; + _version = ""; + _objects = null; + _numFreeObjects = 0; + _objCount = 0; + _docInfoList = null; + _extStreamsList = null; + _docCatalogList = null; + _encryptList = null; + _imagesList = null; + _filtersList = null; + _pagesList = null; + _type0FontsMap = null; + _type1FontsMap = null; + _mmFontsMap = null; + _type3FontsMap = null; + _trueTypeFontsMap = null; + _cid0FontsMap = null; + _cid2FontsMap = null; + _docCatDictRef = null; + _encryptDictRef = null; + _docInfoDictRef = null; + _pagesDictRef = null; + _docCatDict = null; + _docInfoDict = null; + _docTreeRoot = null; + _pageLabelDict = null; + _encryptDict = null; + _trailerDict = null; + _viewPrefDict = null; + _outlineDict = null; + _destsDict = null; + _pageSeqMap = null; + _pageLabelRoot = null; + _embeddedFiles = null; + _destNames = null; + _skippedFontsReported = false; + _skippedOutlinesReported = false; + _skippedAnnotationsReported = false; + _skippedPagesReported = false; + _idProperty = null; + _actionsExist = false; + _numObjects = 0; + _numTrailers = -1; + _pdfACompliant = true; // assume compliance till disproven + _xmpProp = null; + _cachedStreamIndex = -1; + _nFonts = 0; + } + + protected boolean parseHeader(RepInfo info) throws IOException { + PdfHeader header = PdfHeader.parseHeader(_parser); + if (header == null) { + info.setWellFormed(false); + info.setMessage(new ErrorMessage(MessageConstants.PDF_HUL_137, 0L)); // PDF-HUL-137 + return false; + } + if (!header.isVersionValid()) { + info.setValid(false); + info.setMessage(new ErrorMessage(MessageConstants.PDF_HUL_148, 0L)); // PDF-HUL-148 + } + _version = header.getVersionString(); + _pdfACompliant = header.isPdfACompliant(); + info.setSigMatch(_name); + return true; + } + + private long lastEOFOffset(RandomAccessFile raf) throws IOException { + + long offset = 0; + long flen = 0; + byte[] buf = null; + + // overkill to restore fileposition, but make this + // as side-effect free as possible + long savepos = raf.getFilePointer(); + flen = raf.length(); + buf = new byte[(int) Math.min(EOFSCANSIZE, flen)]; + offset = flen - buf.length; + raf.seek(offset); + raf.read(buf); + raf.seek(savepos); + + // OK: + // flen is the total length of the file + // offset is 1024 bytes from the end of file or 0 if file is shorter + // than 1024 + // buf contains all bytes from offset to end of file + + long eofpos = -1; + // Note the limits, selected so the index never is out of bounds + for (int i = buf.length - 4; i >= 1; i--) { + if (buf[i] == '%') { + if ((buf[i - 1] == '%') + && (buf[i + 1] == 'E') + && (buf[i + 2] == 'O') + && (buf[i + 3] == 'F')) { + eofpos = offset + i - 1; + break; + } + } + } + + // if (Tracing.T_MODULE) System.out.println(flen - eofpos); + return eofpos; + } + + private long lastStartXrefOffset(RandomAccessFile raf, long eofOffset) throws IOException { + + long offset = 0; + long flen = 0; + byte[] buf = null; + + // overkill to restore fileposition, but make this + // as side-effect free as possible + long savepos = raf.getFilePointer(); + flen = raf.length(); + if (eofOffset <= 0) { + eofOffset = flen; + } + if (eofOffset >= flen) { + eofOffset = flen; + } + buf = new byte[(int) Math.min(XREFSCANSIZE, eofOffset)]; + offset = eofOffset - buf.length; + raf.seek(offset); + raf.read(buf); + raf.seek(savepos); + + // OK: + // flen is the total length of the file + // offset is 128 bytes from the end of file or 0 if file is shorter than + // 128 + // buf contains all bytes from offset to end of file + + long xrefpos = -1; + // Note the limits, selected so the index never is out of bounds + for (int i = buf.length - 9; i >= 0; i--) { + if (buf[i] == 's') { + if ((buf[i + 1] == 't') + && (buf[i + 2] == 'a') + && (buf[i + 3] == 'r') + && (buf[i + 4] == 't') + && (buf[i + 5] == 'x') + && (buf[i + 6] == 'r') + && (buf[i + 7] == 'e') + && (buf[i + 8] == 'f')) { + xrefpos = offset + i; + break; + } + } + } + + // if (Tracing.T_MODULE) System.out.println(flen - xrefpos); + return xrefpos; + } + + /** Locate the last trailer of the file */ + protected boolean findLastTrailer(RepInfo info) throws IOException { + /* + * Parse file trailer. Technically, this should be the last thing in + * the file, but we follow the Acrobat convention of looking in the + * last 1024 bytes. Since incremental updates may add multiple + * EOF comments, make sure that we use the last one in the file. + */ + + Token token = null; + String value = null; + + _eof = lastEOFOffset(_raf); + + if (_eof < 0L) { + info.setWellFormed(false); + info.setMessage(new ErrorMessage(MessageConstants.PDF_HUL_138, _raf.length())); // PDF-HUL-138 + return false; + } + + // For PDF-A compliance, this must be at the very end. + /* + * Fix contributed by FCLA, 2007-05-30, to test for trailing data + * properly. + * + * if (_raf.length () - _eof > 6) { + */ + if (_raf.length() - _eof > 7) { + _pdfACompliant = false; + } + + /* Retrieve the "startxref" keyword. */ + + long startxrefoffset = lastStartXrefOffset(_raf, _eof); + _startxref = -1L; + + if (startxrefoffset >= 0) { + try { + _parser.seek(startxrefoffset); // points to the 'startxref' kw + // _parser.seek(_eof - 23); // should we allow more slop? + } catch (PdfException e) { + } + while (true) { + try { + token = _parser.getNext(); + } catch (Exception e) { + // we're starting at an arbitrary point, so there + // can be parsing errors. Ignore them till we get + // back in sync. + continue; + } + if (token == null) { + break; + } + if (token instanceof Keyword) { + value = ((Keyword) token).getValue(); + if (DICT_KEY_STARTXREF.equals(value)) { + try { + token = _parser.getNext(); + } catch (Exception e) { + break; // no excuses here + } + if (token != null && token instanceof Numeric) { + _startxref = ((Numeric) token).getLongValue(); + } + } + } + } + } + if (_startxref < 0L) { + info.setWellFormed(false); + info.setMessage( + new ErrorMessage( + MessageConstants.PDF_HUL_139, // PDF-HUL-139 + _parser.getOffset())); + return false; + } + return true; + } + + /* + * Parse a "trailer" (which is not necessarily the last + * thing in the file, as trailers can be linked.) + */ + protected boolean parseTrailer(RepInfo info, boolean prevOnly) throws IOException { + Token token = null; + String value = null; + /* Parse the trailer dictionary. */ + + try { + _parser.seek(_startxref); + /* + * The next object may be either the keyword "xref", signifying + * a classic cross-reference table, or a stream object, + * signifying the new-style cross-reference stream. + */ + Token xref = _parser.getNext(); + if (xref instanceof Keyword) { + _xrefIsStream = false; + _parser.getNext( + Numeric.class, // PDF-HUL-68 + MessageConstants.PDF_HUL_68); // first obj number + + _objCount = + ((Numeric) + _parser.getNext( + Numeric.class, // PDF-HUL-69 + MessageConstants.PDF_HUL_69)) + .getIntegerValue(); + _parser.seek(_parser.getOffset() + _objCount * 20); + } else if (xref instanceof Numeric) { + /* No cross-ref tables to backtrack. */ + _xrefIsStream = true; + _prevxref = -1; + /* + * But I do need to read the dictionary at this point, to get + * essential stuff out of it. + */ + PdfObject pdfStreamObj = _parser.readObjectDef((Numeric) xref); + // the retrieved object should be stream + if (!(pdfStreamObj instanceof PdfStream)) { + throw new PdfInvalidException(MessageConstants.PDF_HUL_150, _parser.getOffset()); + } + PdfDictionary dict = ((PdfStream) pdfStreamObj).getDict(); + _docCatDictRef = (PdfIndirectObj) dict.get(DICT_KEY_ROOT); + if (_docCatDictRef == null) { + throw new PdfInvalidException( + MessageConstants.PDF_HUL_70, // PDF-HUL-70 + _parser.getOffset()); + } + /* + * We don't need to see a trailer dictionary. + * Move along, move along. + */ + return true; + } + + /* Now find the "trailer" keyword. */ + long trailer = -1L; + while ((token = _parser.getNext()) != null) { + if (token instanceof Keyword) { + value = ((Keyword) token).getValue(); + if (DICT_KEY_TRAILER.equals(value)) { + token = _parser.getNext(); + if (token instanceof DictionaryStart) { + trailer = _parser.getOffset() - 7L; + break; + } + } + } + } + if (trailer < 0L) { + info.setWellFormed(false); + info.setMessage( + new ErrorMessage( + MessageConstants.PDF_HUL_71, // PDF-HUL-71 + _parser.getOffset())); + return false; + } + + _trailerDict = _parser.readDictionary(); + PdfObject obj; + + // Extract contents of the trailer dictionary + + _prevxref = -1; + obj = _trailerDict.get(DICT_KEY_PREV); + if (obj != null) { + if (obj instanceof PdfSimpleObject) { + token = ((PdfSimpleObject) obj).getToken(); + if (token instanceof Numeric) _prevxref = ((Numeric) token).getLongValue(); + } + if (_prevxref < 0) { + throw new PdfInvalidException( + MessageConstants.PDF_HUL_72, // PDF-HUL-72 + _parser.getOffset()); + } + } + // If this isn't the last (first read) trailer, then we + // ignore all the other dictionary entries. + if (prevOnly) { + return true; + } + + obj = _trailerDict.get(DICT_KEY_SIZE); + _docCatDictRef = (PdfIndirectObj) _trailerDict.get(DICT_KEY_ROOT); + if (obj != null) { + _numObjects = -1; + if (obj instanceof PdfSimpleObject) { + token = ((PdfSimpleObject) obj).getToken(); + if (token instanceof Numeric) _numObjects = ((Numeric) token).getIntegerValue(); + _xref = new long[_numObjects]; + } + if (_numObjects < 0) { + throw new PdfInvalidException( + MessageConstants.PDF_HUL_73, // PDF-HUL-73 + _parser.getOffset()); + } + if (_numObjects > 8388607) { + // Appendix C implementation limit is enforced by PDF/A + _pdfACompliant = false; + } + } else + throw new PdfInvalidException( + MessageConstants.PDF_HUL_74, // PDF-HUL-74 + _parser.getOffset()); + + if (_docCatDictRef == null) { + throw new PdfInvalidException( + MessageConstants.PDF_HUL_75, // PDF-HUL-75 + _parser.getOffset()); + } + _encryptDictRef = + (PdfIndirectObj) _trailerDict.get(DICT_KEY_ENCRYPT); // This is at least v. 1.1 + _encrypted = (_encryptDictRef != null); + + PdfObject infoObj = _trailerDict.get(DICT_KEY_INFO); + if (infoObj != null && !(infoObj instanceof PdfIndirectObj)) { + throw new PdfInvalidException( + MessageConstants.PDF_HUL_76, // PDF-HUL-76 + _parser.getOffset()); + } + _docInfoDictRef = (PdfIndirectObj) infoObj; + + obj = _trailerDict.get(DICT_KEY_ID); // This is at least v. 1.1 + if (obj != null) { + if (obj instanceof PdfArray) { + String[] id = new String[2]; + try { + PdfArray idArray = (PdfArray) obj; + Vector idVec = idArray.getContent(); + if (idVec.size() != 2) { + throw new PdfInvalidException(MessageConstants.PDF_HUL_77); // PDF-HUL-77 + } + PdfSimpleObject idobj = (PdfSimpleObject) idVec.get(0); + id[0] = toHex(((StringValuedToken) idobj.getToken()).getRawBytes()); + idobj = (PdfSimpleObject) idVec.get(1); + id[1] = toHex(((StringValuedToken) idobj.getToken()).getRawBytes()); + _idProperty = new Property(DICT_KEY_ID, PropertyType.STRING, PropertyArity.ARRAY, id); + } catch (Exception e) { + throw new PdfInvalidException(MessageConstants.PDF_HUL_78); // PDF-HUL-78 + } + } else { + throw new PdfInvalidException( + MessageConstants.PDF_HUL_79, _parser.getOffset()); // PDF-HUL-79 + } + } + obj = _trailerDict.get(DICT_KEY_XREF_STREAM); + if (obj != null) { + /* + * We have a "hybrid" cross-reference scheme. This means we have + * to go through the cross-reference stream and have its entries + * supplement the cross-reference section. + */ + _logger.warning("Hybrid cross-reference not yet implemented"); + } + } catch (PdfException e) { + + e.disparage(info); + if (e.getJhoveMessage() != null) + info.setMessage(new ErrorMessage(e.getJhoveMessage(), _parser.getOffset())); + else info.setMessage(new ErrorMessage(e.getMessage(), _parser.getOffset())); + // If it's merely invalid rather than ill-formed, keep going + return (e instanceof PdfInvalidException); + } + return true; + } + + /* Parses the cross-reference table or stream. */ + protected boolean readXRefInfo(RepInfo info) throws IOException { + if (_xrefIsStream) { + return readXRefStreams(info); + } + return readXRefTables(info); + } + + /* + * Parses the cross-reference streams. This is called from + * readXRefInfo if there is no cross-reference table. + * I still need to deal with hybrid cases. All linked cross-reference + * streams are handled here. + */ + protected boolean readXRefStreams(RepInfo info) throws IOException { + _pdfACompliant = false; // current version of PDF/A doesn't recognize + // XREF streams + while (_startxref > 0) { + try { + _parser.seek(_startxref); + PdfObject pdfStreamObj = _parser.readObjectDef(); + // the retrieved object should be stream + if (!(pdfStreamObj instanceof PdfStream)) { + throw new PdfInvalidException(MessageConstants.PDF_HUL_150, _parser.getOffset()); + } + PdfStream pstream = (PdfStream) pdfStreamObj; + int sObjNum = pstream.getObjNumber(); + CrossRefStream xstream = new CrossRefStream(pstream); + if (!xstream.isValid()) { + return false; + } + xstream.initRead(_raf); + int xrefSize = xstream.getCrossRefTableSize(); + if (_xref == null) { + _xref = new long[xrefSize]; + _xref2 = new int[xrefSize][]; + } + if (sObjNum < 0 || sObjNum >= xrefSize) { + throw new PdfMalformedException( + MessageConstants.PDF_HUL_80, // PDF-HUL-80 + _parser.getOffset()); + } + _xref[sObjNum] = _startxref; // insert the index of the xref + // stream itself + _startxref = xstream.getPrevXref(); + try { + while (xstream.readNextObject()) { + int objNum = xstream.getObjNum(); + if (xstream.isObjCompressed()) { + // Hold off on this branch + _xref[objNum] = -1; // defers to _xref2 + _xref2[objNum] = + new int[] {xstream.getContentStreamObjNum(), xstream.getContentStreamIndex()}; + } else { + if (_xref[objNum] == 0) { + _xref[objNum] = xstream.getOffset(); + } + } + } + _numFreeObjects += xstream.getFreeCount(); + } catch (IOException e) { + info.setWellFormed(false); + info.setMessage( + new ErrorMessage( + MessageConstants.PDF_HUL_81, // PDF-HUL-81 + _parser.getOffset())); + return false; + } + } catch (PdfException e) { + + e.disparage(info); + if (e.getJhoveMessage() != null) + info.setMessage(new ErrorMessage(e.getJhoveMessage(), _parser.getOffset())); + else info.setMessage(new ErrorMessage(e.getMessage(), _parser.getOffset())); + // If it's merely invalid rather than ill-formed, keep going + return (e instanceof PdfInvalidException); + } + } + return true; // incomplete, but let it through + } + + /* + * Parses the cross-reference table. This is called from + * readXRefInfo if there is a cross-reference table. + */ + protected boolean readXRefTables(RepInfo info) throws IOException { + Token token = null; + try { + _parser.seek(_startxref); + token = _parser.getNext(); // "xref" keyword or numeric + if (token instanceof Keyword) { + while ((token = _parser.getNext()) != null) { + int firstObj = 0; + // Look for the start of a cross-ref subsection, which + // begins with a base object number and a count. + if (token instanceof Numeric) { + firstObj = ((Numeric) token).getIntegerValue(); + } else { + // On anything else, assume we're done with this + // section. + // (Most likely we've hit the keyword "trailer". + break; + } + _objCount = ((Numeric) _parser.getNext()).getIntegerValue(); + if (_xref == null) { + _xref = new long[_objCount]; + } + for (int i = 0; i < _objCount; i++) { + // In reading the cross-reference table, also check + // the extra syntactic requirements of PDF/A. + long offset = + ((Numeric) _parser.getNext(Numeric.class, MessageConstants.PDF_HUL_82)) + .getLongValue(); // PDF-HUL-82 + _parser.getNext(); // Generation number + if (_parser.getWSString().length() > 1) { + _pdfACompliant = false; + } + token = _parser.getNext(Keyword.class, MessageConstants.PDF_HUL_83); // PDF-HUL-83 + if (_parser.getWSString().length() > 1) { + _pdfACompliant = false; + } + // A keyword of "n" signifies an object in use, + // "f" signifies a free object. If we already + // have an entry for this object, don't replace it. + String keyval = ((Keyword) token).getValue(); + if ("n".equals(keyval)) { + if (_xref[firstObj + i] == 0) { + _xref[firstObj + i] = offset; + } + } else if ("f".equals(keyval)) { + _numFreeObjects++; + } else { + throw new PdfMalformedException( + MessageConstants.PDF_HUL_84, // PDF-HUL-84 + _parser.getOffset()); + } + } + } + } + } catch (PdfException e) { + e.disparage(info); + if (e.getJhoveMessage() != null) + info.setMessage(new ErrorMessage(e.getJhoveMessage(), _parser.getOffset())); + else info.setMessage(new ErrorMessage(e.getMessage(), _parser.getOffset())); + return false; + } catch (Exception e) { + info.setValid(false); + info.setMessage(new ErrorMessage(e.getMessage(), _parser.getOffset())); + } + return true; + } + + private boolean readDocCatalogDict(RepInfo info) throws IOException { + Property p = null; + _docCatDict = null; + _docCatalogList = new ArrayList(2); + // Get the Root reference which we had before, and + // resolve it to the dictionary object. + if (_docCatDictRef == null) { + info.setWellFormed(false); + info.setMessage(new ErrorMessage(MessageConstants.PDF_HUL_85, 0)); // PDF-HUL-85 + return false; + } + try { + _docCatDict = (PdfDictionary) resolveIndirectObject(_docCatDictRef); + } catch (Exception e) { + _logger.warning("Tried to cast non-dictionary to PdfDictionary"); + e.printStackTrace(); + } + if (_docCatDict == null) { + // If no object was returned, the PDF's not well-formed + info.setWellFormed(false); + info.setMessage(new ErrorMessage(MessageConstants.PDF_HUL_86, 0)); // PDF-HUL-86 + return false; + } else if (_docCatDict.getObjNumber() != _docCatDictRef.getObjNumber()) { + // If the returned object nmumber is not the same as that requested + _logger.warning(String.format("Inconsistent Document Catalog Object Number")); + _logger.warning( + String.format( + " - /Root indirect reference number: %d, returned object ID: %d.", + _docCatDictRef.getObjNumber(), _docCatDict.getObjNumber())); + info.setWellFormed(false); + info.setMessage(new ErrorMessage(MessageConstants.PDF_HUL_140, 0)); // PDF-HUL-140 + return false; + } + try { + // Check that the catalog has a key type and the types value is + // "Catalog" + if (!checkTypeKey( + _docCatDict, + info, + KEY_VAL_CATALOG, + MessageConstants.PDF_HUL_141, // PDF-HUL-141 + MessageConstants.PDF_HUL_142, // PDF-HUL-142 + MessageConstants.PDF_HUL_143)) { // PDF-HUL-143 + return false; + } + + PdfObject viewPref = _docCatDict.get(DICT_KEY_VIEWER_PREFS); + viewPref = resolveIndirectObject(viewPref); + if (viewPref instanceof PdfDictionary) { + _viewPrefDict = (PdfDictionary) viewPref; + p = buildViewPrefProperty(_viewPrefDict); + _docCatalogList.add(p); + } + String pLayoutText = DEFAULT_PAGE_LAYOUT; // default + PdfObject pLayout = resolveIndirectObject(_docCatDict.get(DICT_KEY_PAGE_LAYOUT)); + if (pLayout instanceof PdfSimpleObject) { + pLayoutText = ((PdfSimpleObject) pLayout).getStringValue(); + } + p = new Property(PROP_NAME_PAGE_LAYOUT, PropertyType.STRING, pLayoutText); + _docCatalogList.add(p); + + String pModeText = DEFAULT_MODE; // default + PdfObject pMode = resolveIndirectObject(_docCatDict.get(DICT_KEY_PAGE_MODE)); + if (pMode instanceof PdfSimpleObject) { + pModeText = ((PdfSimpleObject) pMode).getStringValue(); + } + p = new Property(DICT_KEY_PAGE_MODE, PropertyType.STRING, pModeText); + _docCatalogList.add(p); + + PdfObject outlines = resolveIndirectObject(_docCatDict.get(DICT_KEY_OUTLINES)); + if (outlines instanceof PdfDictionary) { + _outlineDict = (PdfDictionary) outlines; + } + + PdfObject lang = resolveIndirectObject(_docCatDict.get(DICT_KEY_LANG)); + if (lang != null && lang instanceof PdfSimpleObject) { + String langText = ((PdfSimpleObject) lang).getStringValue(); + p = new Property(PROP_NAME_LANG, PropertyType.STRING, _encrypted ? ENCRYPTED : langText); + _docCatalogList.add(p); + } + + // The Pages dictionary doesn't go into the property, + // but this is a convenient time to grab it and the page label + // dictionary. + _pagesDictRef = (PdfIndirectObj) _docCatDict.get(DICT_KEY_PAGES); + _pageLabelDict = (PdfDictionary) resolveIndirectObject(_docCatDict.get(DICT_KEY_PAGE_LABELS)); + + // Grab the Version entry, and use it to override the + // file header IF it's later. + PdfObject vers = resolveIndirectObject(_docCatDict.get(DICT_KEY_VERSION)); + if (vers instanceof PdfSimpleObject) { + String versString = ((PdfSimpleObject) vers).getStringValue(); + String infoVersString = _version; + try { + double ver = Double.parseDouble(versString); + double infoVer = Double.parseDouble(infoVersString); + /* Set a message if this doesn't agree with RepInfo */ + if (ver != infoVer) { + String mess = + MessageFormat.format( + MessageConstants.PDF_HUL_87.getMessage(), infoVersString, versString); + JhoveMessage message = + JhoveMessages.getMessageInstance(MessageConstants.PDF_HUL_87.getId(), mess); + info.setMessage(new InfoMessage(message)); + } + /* Replace the version in RepInfo if this is larger */ + if (ver > infoVer) { + _version = versString; + } + } catch (NumberFormatException e) { + throw new PdfInvalidException(MessageConstants.PDF_HUL_88); // PDF-HUL-88 + } + } + + // Get the Names dictionary in order to grab the + // EmbeddedFiles and Dests entries. + try { + PdfDictionary namesDict = + (PdfDictionary) resolveIndirectObject(_docCatDict.get(DICT_KEY_NAMES)); + if (namesDict != null) { + PdfDictionary embeddedDict = + (PdfDictionary) resolveIndirectObject(namesDict.get(DICT_KEY_EMBEDDED_FILES)); + if (embeddedDict != null) { + _embeddedFiles = new NameTreeNode(this, null, embeddedDict); + } + + PdfDictionary dDict = + (PdfDictionary) resolveIndirectObject(namesDict.get(DICT_KEY_DESTS)); + if (dDict != null) { + _destNames = new NameTreeNode(this, null, dDict); + } + } + } catch (ClassCastException ce) { + _logger.info("ClassCastException on names dictionary"); + throw new PdfInvalidException(MessageConstants.PDF_HUL_89); // PDF-HUL-89 + } catch (Exception e) { + _logger.info("Exception on names dictionary: " + e.getClass().getName()); + throw new PdfMalformedException(MessageConstants.PDF_HUL_90); // PDF-HUL-90 + } + + // Get the optional Dests dictionary. Note that destinations + // may be specified in either of two completely different + // ways: a dictionary here, or a name tree from the Names + // dictionary. + + try { + _destsDict = (PdfDictionary) resolveIndirectObject(_docCatDict.get(DICT_KEY_DESTS)); + } catch (ClassCastException ce) { + _logger.info("ClassCastException on dests dictionary"); + throw new PdfInvalidException(MessageConstants.PDF_HUL_91); // PDF-HUL-91 + } catch (Exception e) { + _logger.info("Exception on dests dictionary: " + e.getClass().getName()); + throw new PdfMalformedException(MessageConstants.PDF_HUL_92); // PDF-HUL-92 + } + } catch (PdfException e) { + e.disparage(info); // clears Valid or WellFormed as appropriate + if (e.getJhoveMessage() != null) + info.setMessage(new ErrorMessage(e.getJhoveMessage(), _parser.getOffset())); + else info.setMessage(new ErrorMessage(e.getMessage(), _parser.getOffset())); + // Keep going if it's only invalid + return (e instanceof PdfInvalidException); + } catch (Exception e) { + // Unexpected exception -- declare not well-formed + info.setWellFormed(false); + info.setMessage(new ErrorMessage(e.toString(), _parser.getOffset())); + return false; + } + return true; + } + + protected boolean readEncryptDict(RepInfo info) throws IOException { + String filterText = ""; + String effText = null; + // Get the reference which we had before, and + // resolve it to the dictionary object. + if (_encryptDictRef == null) { + return true; // encryption entry is optional + } + try { + _encryptList = new ArrayList(6); + PdfDictionary dict = (PdfDictionary) resolveIndirectObject(_encryptDictRef); + _encryptDict = dict; + + PdfObject filter = dict.get(DICT_KEY_FILTER); + if (filter instanceof PdfSimpleObject) { + Token tok = ((PdfSimpleObject) filter).getToken(); + if (tok instanceof Name) { + filterText = ((Name) tok).getValue(); + } + } + Property p = new Property(PROP_NAME_SECURITY_HANDLER, PropertyType.STRING, filterText); + _encryptList.add(p); + // PdfObject eff = dict.get("EFF"); + if (filter instanceof PdfSimpleObject) { + Token tok = ((PdfSimpleObject) filter).getToken(); + if (tok instanceof Name) { + effText = ((Name) tok).getValue(); + } + } + if (effText != null) { + p = new Property(PROP_NAME_EFF, PropertyType.STRING, effText); + _encryptList.add(p); + } + + int algValue = 0; + PdfObject algorithm = dict.get(DICT_KEY_V); + if (algorithm instanceof PdfSimpleObject) { + Token tok = ((PdfSimpleObject) algorithm).getToken(); + if (tok instanceof Numeric) { + algValue = ((Numeric) tok).getIntegerValue(); + if (_je != null && _je.getShowRawFlag()) { + p = new Property(PROP_NAME_ALGORITHM, PropertyType.INTEGER, new Integer(algValue)); + } else { + try { + p = + new Property( + PROP_NAME_ALGORITHM, PropertyType.STRING, PdfStrings.ALGORITHM[algValue]); + } catch (ArrayIndexOutOfBoundsException aioobe) { + throw new PdfInvalidException // PDF-HUL-93 + (MessageConstants.PDF_HUL_93, _parser.getOffset()); + } + } + if (p != null) { + _encryptList.add(p); + } + } + } + + int keyLen = 40; + PdfObject length = dict.get(DICT_KEY_LENGTH); + if (length instanceof PdfSimpleObject) { + Token tok = ((PdfSimpleObject) length).getToken(); + if (tok instanceof Numeric) { + keyLen = ((Numeric) tok).getIntegerValue(); + } + if (_je != null) { + p = new Property(PROP_NAME_KEY_LENGTH, PropertyType.INTEGER, new Integer(keyLen)); + _encryptList.add(p); + } + } + + if (FILTER_VAL_STANDARD.equals(filterText)) { + List stdList = new ArrayList(4); + // Flags have a known meaning only if Standard + // security handler was specified + PdfObject flagObj = dict.get(DICT_KEY_P); + PdfObject revObj = dict.get(DICT_KEY_R); + int rev = 2; // assume old rev if not present + if (revObj instanceof PdfSimpleObject) { + rev = ((PdfSimpleObject) revObj).getIntValue(); + } + if (flagObj instanceof PdfSimpleObject) { + int flags = ((PdfSimpleObject) flagObj).getIntValue(); + String[] flagStrs; + if (rev == 2) { + flagStrs = PdfStrings.USERPERMFLAGS2; + } else { + flagStrs = PdfStrings.USERPERMFLAGS3; + } + p = buildUserPermProperty(flags, flagStrs); + stdList.add(p); + + stdList.add(new Property(PROP_NAME_REVISION, PropertyType.INTEGER, new Integer(rev))); + } + PdfObject oObj = dict.get("O"); + if (oObj != null) { + if (oObj instanceof PdfSimpleObject) { + stdList.add( + new Property( + PROP_NAME_OWNER_STRING, + PropertyType.STRING, + toHex(((PdfSimpleObject) oObj).getRawBytes()))); + } + } + PdfObject uObj = dict.get("U"); + if (uObj != null) { + if (uObj instanceof PdfSimpleObject) { + stdList.add( + new Property( + PROP_NAME_USER_STRING, + PropertyType.STRING, + toHex(((PdfSimpleObject) uObj).getRawBytes()))); + } + } + // Required if ExtensionLevel 3 and Encryption Algorithm (V) is 5 + // Defined in Adobe® Supplement to the ISO 32000 + if (algValue == 5) { + PdfObject oeObj = dict.get("OE"); + if (oeObj != null) { + if (oeObj instanceof PdfSimpleObject) { + stdList.add( + new Property( + PROP_NAME_OWNERKEY_STRING, + PropertyType.STRING, + toHex(((PdfSimpleObject) oeObj).getRawBytes()))); + } + } else { + // if algValue is 5; OE is mandatory + throw new PdfInvalidException(MessageConstants.PDF_HUL_152, _parser.getOffset()); + } + PdfObject ueObj = dict.get("UE"); + if (ueObj != null) { + if (ueObj instanceof PdfSimpleObject) { + stdList.add( + new Property( + PROP_NAME_USERKEY_STRING, + PropertyType.STRING, + toHex(((PdfSimpleObject) ueObj).getRawBytes()))); + } + } else { + // if algValue is 5; UE is mandatory + throw new PdfInvalidException(MessageConstants.PDF_HUL_153, _parser.getOffset()); + } + } + _encryptList.add( + new Property( + PROP_NAME_STANDARD_SECURITY_HANDLER, + PropertyType.PROPERTY, + PropertyArity.LIST, + stdList)); + } + + } catch (PdfException e) { + e.disparage(info); + if (e.getJhoveMessage() != null) + info.setMessage(new ErrorMessage(e.getJhoveMessage(), _parser.getOffset())); + else info.setMessage(new ErrorMessage(e.getMessage(), _parser.getOffset())); + return (e instanceof PdfInvalidException); + } + return true; + } + + protected boolean readDocInfoDict(RepInfo info) { + // Get the Info reference which we had before, and + // resolve it to the dictionary object. + if (_docInfoDictRef == null) { + return true; // Info is optional + } + _docInfoList = new ArrayList(9); + try { + _docInfoDict = (PdfDictionary) resolveIndirectObject(_docInfoDictRef); + + addStringProperty(_docInfoDict, _docInfoList, DICT_KEY_TITLE, PROP_NAME_TITLE); + addStringProperty(_docInfoDict, _docInfoList, DICT_KEY_AUTHOR, PROP_NAME_AUTHOR); + addStringProperty(_docInfoDict, _docInfoList, DICT_KEY_SUBJECT, PROP_NAME_SUBJECT); + addStringProperty(_docInfoDict, _docInfoList, DICT_KEY_KEYWORDS, PROP_NAME_KEYWORDS); + addStringProperty(_docInfoDict, _docInfoList, DICT_KEY_CREATOR, PROP_NAME_CREATOR); + addStringProperty(_docInfoDict, _docInfoList, DICT_KEY_PRODUCER, PROP_NAME_PRODUCER); + // CreationDate requires string-to-date conversion + // ModDate does too + addDateProperty(_docInfoDict, _docInfoList, DICT_KEY_CREATION_DATE, PROP_NAME_CREATION_DATE); + addDateProperty(_docInfoDict, _docInfoList, DICT_KEY_MODIFIED_DATE, PROP_NAME_MODIFIED_DATE); + addStringProperty(_docInfoDict, _docInfoList, DICT_KEY_TRAPPED, PROP_NAME_TRAPPED); + } catch (PdfException e) { + e.disparage(info); + if (e.getJhoveMessage() != null) + info.setMessage(new ErrorMessage(e.getJhoveMessage(), _parser.getOffset())); + else info.setMessage(new ErrorMessage(e.getMessage(), _parser.getOffset())); + // Keep parsing if it's only invalid + return (e instanceof PdfInvalidException); + } catch (Exception e) { + info.setWellFormed(false); + String mess = + MessageFormat.format(MessageConstants.PDF_HUL_94.getMessage(), e.getClass().getName()); + JhoveMessage message = + JhoveMessages.getMessageInstance(MessageConstants.PDF_HUL_94.getId(), mess); + info.setMessage(new ErrorMessage(message)); // PDF-HUL-94 + } + return true; + } + + protected boolean readDocumentTree(RepInfo info) { + try { + if (_pagesDictRef == null) { + throw new PdfInvalidException(MessageConstants.PDF_HUL_95); // PDF-HUL-95 + } + + PdfObject pagesObj = resolveIndirectObject(_pagesDictRef); + if (!(pagesObj instanceof PdfDictionary)) + throw new PdfMalformedException(MessageConstants.PDF_HUL_97); // PDF-HUL-97 + PdfDictionary pagesDict = (PdfDictionary) pagesObj; + + // Check that the pages dict has a key type and the types value is + // Pages + if (!checkTypeKey( + pagesDict, + info, + KEY_VAL_PAGES, + MessageConstants.PDF_HUL_146, // PDF-HUL-146 + MessageConstants.PDF_HUL_144, // PDF-HUL-144 + MessageConstants.PDF_HUL_145)) { // PDF-HUL-145 + return false; + } + + _docTreeRoot = new PageTreeNode(this, null, pagesDict); + _docTreeRoot.buildSubtree(true, MAX_PAGE_TREE_DEPTH); + } catch (PdfException e) { + e.disparage(info); + if (e.getJhoveMessage() != null) + info.setMessage(new ErrorMessage(e.getJhoveMessage(), _parser.getOffset())); + else info.setMessage(new ErrorMessage(e.getMessage(), _parser.getOffset())); + // Continue parsing if it's only invalid + return (e instanceof PdfInvalidException); + } catch (ArrayIndexOutOfBoundsException excep) { + info.setMessage( + new ErrorMessage(MessageConstants.PDF_HUL_96, _parser.getOffset())); // PDF-HUL-96 + info.setWellFormed(false); + return false; + } catch (Exception e) { + // Catch any odd exceptions + String mess = + MessageFormat.format(MessageConstants.PDF_HUL_98.getMessage(), e.getClass().getName()); + JhoveMessage message = + JhoveMessages.getMessageInstance(MessageConstants.PDF_HUL_98.getId(), mess); + info.setMessage(new ErrorMessage(message, _parser.getOffset())); // PDF-HUL-98 + info.setWellFormed(false); + return false; + } + return true; + } + + protected boolean readPageLabelTree(RepInfo info) { + // the page labels number tree is optional. + try { + if (_pageLabelDict != null) { + _pageLabelRoot = new PageLabelNode(this, null, _pageLabelDict); + _pageLabelRoot.buildSubtree(); + } + } catch (PdfException e) { + e.disparage(info); + if (e.getJhoveMessage() != null) + info.setMessage(new ErrorMessage(e.getJhoveMessage(), _parser.getOffset())); + else info.setMessage(new ErrorMessage(e.getMessage(), _parser.getOffset())); + // Continue parsing if it's only invalid + return (e instanceof PdfInvalidException); + } catch (Exception e) { + info.setWellFormed(false); + String mess = + MessageFormat.format(MessageConstants.PDF_HUL_99.getMessage(), e.getClass().getName()); + JhoveMessage message = + JhoveMessages.getMessageInstance(MessageConstants.PDF_HUL_99.getId(), mess); + info.setMessage(new ErrorMessage(message)); // PDF-HUL-99 + return false; + } + return true; // always succeeds + } + + protected boolean readXMPData(RepInfo info) { + try { + PdfStream metadata = (PdfStream) resolveIndirectObject(_docCatDict.get(DICT_KEY_METADATA)); + if (metadata == null) { + return true; // Not required + } + // PdfDictionary metaDict = metadata.getDict (); + + // Create an InputSource to feed the parser. + SAXParserFactory factory = SAXParserFactory.newInstance(); + factory.setNamespaceAware(true); + XMLReader parser = factory.newSAXParser().getXMLReader(); + PdfXMPSource src = new PdfXMPSource(metadata, getFile()); + XMPHandler handler = new XMPHandler(); + parser.setContentHandler(handler); + parser.setErrorHandler(handler); + + // We have to parse twice. The first time, we may get + // an encoding change as part of an exception thrown. If this + // happens, we create a new InputSource with the encoding, and + // continue. + try { + parser.parse(src); + _xmpProp = src.makeProperty(); + } catch (SAXException se) { + String msg = se.getMessage(); + if (msg != null && msg.startsWith(ENCODING_PREFIX)) { + String encoding = msg.substring(5); + try { + src = new PdfXMPSource(metadata, getFile(), encoding); + parser.parse(src); + _xmpProp = src.makeProperty(); + } catch (UnsupportedEncodingException uee) { + _logger.log( + Level.INFO, "Attempt to use explicit encoding to parse XMP metadata failed.", uee); + throw new PdfInvalidException(MessageConstants.PDF_HUL_100); // PDF-HUL-100 + } + } + } + + } catch (PdfException e) { + e.disparage(info); + if (e.getJhoveMessage() != null) + info.setMessage(new ErrorMessage(e.getJhoveMessage(), _parser.getOffset())); + else info.setMessage(new ErrorMessage(e.getMessage(), _parser.getOffset())); + // Continue parsing if it's only invalid + return (e instanceof PdfInvalidException); + } catch (Exception e) { + info.setMessage( + new ErrorMessage( + MessageConstants.PDF_HUL_101, // PDF-HUL-101 + _parser.getOffset())); + info.setValid(false); + return false; + } + return true; + } + + protected void findExternalStreams(RepInfo info) throws IOException { + _extStreamsList = new LinkedList(); + // stop processing if there is no root for the document tree + if (_docTreeRoot == null) return; + _docTreeRoot.startWalk(); + try { + for (; ; ) { + // Get all the page objects in the document sequentially + PageObject page = _docTreeRoot.nextPageObject(); + if (page == null) { + break; + } + // Get the streams for the page and walk through them + List streams = page.getContentStreams(); + if (streams != null) { + ListIterator streamIter = streams.listIterator(); + while (streamIter.hasNext()) { + PdfStream stream = streamIter.next(); + String specStr = stream.getFileSpecification(); + if (specStr != null) { + Property prop = new Property(PROP_NAME_FILE, PropertyType.STRING, specStr); + _extStreamsList.add(prop); + } + } + } + } + } catch (PdfException e) { + e.disparage(info); + if (e.getJhoveMessage() != null) { + info.setMessage(new ErrorMessage(e.getJhoveMessage())); + } else { + info.setMessage(new ErrorMessage(e.getMessage())); + } + } catch (Exception e) { + info.setWellFormed(false); + String mess = + MessageFormat.format(MessageConstants.PDF_HUL_102.getMessage(), e.getClass().getName()); + JhoveMessage message = + JhoveMessages.getMessageInstance(MessageConstants.PDF_HUL_102.getId(), mess); + info.setMessage(new ErrorMessage(message)); // PDF-HUL-102 + } + } + + /** + * Locates the filters in the content stream dictionaries and generate a list of unique pipelines. + * + * @return false if the filter structure is defective. + */ + protected boolean findFilters(RepInfo info) throws IOException { + _filtersList = new LinkedList(); + // stop processing if there is no root for the document tree + if (_docTreeRoot == null) return false; + _docTreeRoot.startWalk(); + try { + for (; ; ) { + // Get all the page objects in the document sequentially + PageObject page = _docTreeRoot.nextPageObject(); + if (page == null) { + break; + } + // Get the streams for the page and walk through them + List streams = page.getContentStreams(); + if (streams != null) { + ListIterator streamIter = streams.listIterator(); + while (streamIter.hasNext()) { + PdfStream stream = streamIter.next(); + Filter[] filters = stream.getFilters(); + extractFilters(filters, stream); + } + } + } + } catch (PdfException e) { + e.disparage(info); + if (e.getJhoveMessage() != null) + info.setMessage(new ErrorMessage(e.getJhoveMessage(), _parser.getOffset())); + else info.setMessage(new ErrorMessage(e.getMessage(), _parser.getOffset())); + // Continue parsing if it's only invalid + return (e instanceof PdfInvalidException); + } + return true; + } + + /** + * Finds the filters in a stream or array object which is the value of a stream's Filter key, and + * put them in _filtersList if a duplicate isn't there already. If the name is "Crypt", appends a + * colon and the name if available. Returns the filter string whether it's added or not, or null + * if there are no filters. + */ + protected String extractFilters(Filter[] filters, PdfStream stream) { + /* + * Concatenate the names into a string of names separated + * by spaces. + */ + int len = filters.length; + if (len == 0) { + return null; + } + StringBuffer buf = new StringBuffer(); + for (int i = 0; i < len; i++) { + Filter filt = filters[i]; + String fname = filt.getFilterName(); + buf.append(fname); + /* If it's a Crypt filter, add the crypt name. */ + if (FILTER_NAME_CRYPT.equals(fname)) { + String cname = filt.getNameParam(); + if (cname != null) { + buf.append(":" + cname); + } + } + if (i < len - 1) { + buf.append(' '); + } + } + String filterStr = buf.toString(); + boolean unique = true; + // Check for uniqueness. + Iterator iter = _filtersList.iterator(); + while (iter.hasNext()) { + Property p = iter.next(); + String s = (String) p.getValue(); + if (s.equals(filterStr)) { + unique = false; + break; + } + } + if (filterStr != null && unique) { + Property prop = new Property(PROP_NAME_FILTER_PIPELINE, PropertyType.STRING, filterStr); + _filtersList.add(prop); + } + return filterStr; + } + + protected void findImages(RepInfo info) throws IOException { + _imagesList = new LinkedList(); + _docTreeRoot.startWalk(); + try { + for (; ; ) { + // Get all the page objects in the document sequentially + PageObject page = _docTreeRoot.nextPageObject(); + if (page == null) { + break; + } + // Get the resources for the page and look for image XObjects + PdfDictionary rsrc = page.getResources(); + if (rsrc != null) { + PdfDictionary xo = (PdfDictionary) resolveIndirectObject(rsrc.get(RESOURCE_NAME_XOBJECT)); + if (xo != null) { + Iterator iter = xo.iterator(); + while (iter.hasNext()) { + // Get an XObject and check if it's an image. + _logger.info("Getting image"); + PdfDictionary xobdict = null; + PdfObject xob = resolveIndirectObject(iter.next()); + if (xob instanceof PdfStream) { + xobdict = ((PdfStream) xob).getDict(); + } + if (xobdict != null) { + PdfSimpleObject subtype = (PdfSimpleObject) xobdict.get(DICT_KEY_XOBJ_SUBTYPE); + if (XOBJ_SUBTYPE_IMAGE.equals(subtype.getStringValue())) { + // It's an image XObject. Report stuff. + _logger.info("Image XObject"); + List imgList = new ArrayList(10); + Property prop = + new Property( + PROP_NAME_IMAGE, PropertyType.PROPERTY, PropertyArity.LIST, imgList); + NisoImageMetadata niso = new NisoImageMetadata(); + imgList.add( + new Property(PROP_NAME_NISO_IMAGE_MD, PropertyType.NISOIMAGEMETADATA, niso)); + PdfObject widthBase = xobdict.get(DICT_KEY_WIDTH); + PdfSimpleObject widObj = (PdfSimpleObject) resolveIndirectObject(widthBase); + niso.setImageWidth(widObj.getIntValue()); + PdfObject heightBase = xobdict.get(DICT_KEY_HEIGHT); + PdfSimpleObject htObj = (PdfSimpleObject) resolveIndirectObject(heightBase); + niso.setImageLength(htObj.getIntValue()); + + // Check for filters to add to the filter + // list + Filter[] filters = ((PdfStream) xob).getFilters(); + // Try to derive the image MIME type from + // filter names + String mimeType = imageMimeFromFilters(filters); + niso.setMimeType(mimeType); + String filt = extractFilters(filters, (PdfStream) xob); + if (filt != null) { + // If the filter is one which the NISO + // schema + // knows about, put it in the NISO + // metadata, + // otherwise put it in a Filter + // property. + int nisoFilt = nameToNiso(filt, compressionStrings, compressionValues); + if (nisoFilt >= 0) { + /* + * If it's 2, it's a CCITTFaxDecode + * filter. There may be an optional + * K entry that can change the + * value. + */ + PdfObject parms = xobdict.get(DICT_KEY_DECODE_PARAMS); + if (parms != null) { + PdfSimpleObject kobj = null; + if (parms instanceof PdfDictionary) { + PdfDictionary pdict = (PdfDictionary) parms; + kobj = (PdfSimpleObject) resolveIndirectObject(pdict.get(DICT_KEY_K)); + } + /* + * Note that the DecodeParms + * value may also be an array + * of dictionaries. We are not + * handling that contingency. + */ + if (kobj != null) { + int k = kobj.getIntValue(); + if (k < 0) { + nisoFilt = 4; + } else if (k > 0) { + nisoFilt = 3; + } + } + } + niso.setCompressionScheme(nisoFilt); + } else { + imgList.add(new Property(PROP_NAME_FILTER, PropertyType.STRING, filt)); + } + } else { + niso.setCompressionScheme(1); // no + // filter + } + + // Check for color space info + PdfObject colorSpc = xobdict.get(DICT_KEY_COLOR_SPACE); + if (colorSpc != null) { + String colorName = null; + if (colorSpc instanceof PdfSimpleObject) { + colorName = ((PdfSimpleObject) colorSpc).getStringValue(); + } else if (colorSpc instanceof PdfArray) { + Vector vec = ((PdfArray) colorSpc).getContent(); + // Use the first element, which is + // the color space family + PdfSimpleObject fam = (PdfSimpleObject) vec.elementAt(0); + colorName = fam.getStringValue(); + } + if (colorName != null) { + int nisoSpace = nameToNiso(colorName, colorSpaceStrings, colorSpaceValues); + if (nisoSpace >= 0) { + niso.setColorSpace(nisoSpace); + } else { + imgList.add( + new Property(PROP_NAME_COLOR_SPACE, PropertyType.STRING, colorName)); + } + } + } + + PdfSimpleObject bpc = (PdfSimpleObject) xobdict.get(DICT_KEY_BITS_PER_COMPONENT); + if (bpc != null) { + // imgList.add(new + // Property(DICT_KEY_BITS_PER_COMPONENT, + // PropertyType.INTEGER, + // new Integer (bpc.getIntValue()))); + niso.setBitsPerSample(new int[] {bpc.getIntValue()}); + } + + PdfSimpleObject intent = (PdfSimpleObject) xobdict.get(DICT_KEY_INTENT); + if (intent != null) { + imgList.add( + new Property( + PROP_NAME_INTENT, PropertyType.STRING, intent.getStringValue())); + } + + PdfSimpleObject imgmsk = (PdfSimpleObject) xobdict.get(DICT_KEY_IMAGE_MASK); + if (imgmsk != null) { + boolean b = imgmsk.isTrue(); + imgList.add( + new Property( + PROP_NAME_IMAGE_MASK, PropertyType.BOOLEAN, Boolean.valueOf(b))); + } + + PdfArray dcd = (PdfArray) xobdict.get(DICT_KEY_DECODE); + if (dcd != null) { + Vector dcdvec = dcd.getContent(); + List dcdlst = new ArrayList(dcdvec.size()); + Iterator diter = dcdvec.iterator(); + while (diter.hasNext()) { + PdfSimpleObject d = (PdfSimpleObject) diter.next(); + dcdlst.add(new Integer(d.getIntValue())); + } + imgList.add( + new Property( + PROP_NAME_DECODE, PropertyType.INTEGER, PropertyArity.LIST, dcdlst)); + } + + PdfSimpleObject intrp = (PdfSimpleObject) xobdict.get(DICT_KEY_INTERPOLATE); + if (intrp != null) { + boolean b = intrp.isTrue(); + imgList.add( + new Property( + PROP_NAME_INTERPOLATE, PropertyType.BOOLEAN, Boolean.valueOf(b))); + } + + PdfSimpleObject nam = (PdfSimpleObject) xobdict.get(DICT_KEY_NAME); + if (nam != null) { + imgList.add( + new Property(PROP_NAME_NAME, PropertyType.STRING, nam.getStringValue())); + } + + PdfSimpleObject id = + (PdfSimpleObject) resolveIndirectObject(xobdict.get(DICT_KEY_ID)); + if (id != null) { + String idstr = toHex(id.getStringValue()); + imgList.add(new Property(PROP_NAME_ID, PropertyType.STRING, idstr)); + } + + _imagesList.add(prop); + } + } + } + } + } + } + } catch (PdfException e) { + e.disparage(info); + if (e.getJhoveMessage() != null) + info.setMessage(new ErrorMessage(e.getJhoveMessage(), _parser.getOffset())); + else info.setMessage(new ErrorMessage(e.getMessage(), _parser.getOffset())); + } catch (Exception e) { + info.setWellFormed(false); + String mess = + MessageFormat.format(MessageConstants.PDF_HUL_103.getMessage(), e.getClass().getName()); + JhoveMessage message = + JhoveMessages.getMessageInstance(MessageConstants.PDF_HUL_103.getId(), mess); + info.setMessage(new ErrorMessage(message)); // PDF-HUL-103 + } + } + + /* + * Convert a Filter name to a NISO compression scheme value. + * If the name is unknown to NISO, return -1. + */ + protected int nameToNiso(String name, String[] nameArray, int[] valArray) { + for (int i = 0; i < nameArray.length; i++) { + if (nameArray[i].equals(name)) { + return valArray[i]; + } + } + return -1; // no match + } + + protected void findFonts(RepInfo info) throws IOException { + _type0FontsMap = new HashMap(); + _type1FontsMap = new HashMap(); + _trueTypeFontsMap = new HashMap(); + _mmFontsMap = new HashMap(); + _type3FontsMap = new HashMap(); + _cid0FontsMap = new HashMap(); + _cid2FontsMap = new HashMap(); + try { + _docTreeRoot.startWalk(); + for (; ; ) { + // This time we need all the page objects and page tree + // nodes, because resources can be inherited from + // page tree nodes. + DocNode node = _docTreeRoot.nextDocNode(); + if (node == null) { + break; + } + // Get the fonts for the node + PdfDictionary fonts = null; + fonts = node.getFontResources(); + if (fonts != null) { + // In order to make sure we have a collection of + // unique fonts, we store them in a map keyed by + // object number. + Iterator fontIter = fonts.iterator(); + while (fontIter.hasNext()) { + PdfObject fontRef = fontIter.next(); + PdfObject font = resolveIndirectObject(fontRef); + if (font instanceof PdfDictionary) { + addFontToMap((PdfDictionary) font); + } else { + // Expected a dictionary + info.setWellFormed(false); + info.setMessage( + new ErrorMessage( + MessageConstants.PDF_HUL_104, // PDF-HUL-104 + _parser.getOffset())); + return; + } + // If we've been directed appropriately, + // we accumulate the information, but don't + // report it. In that case, we post a message + // just once to that effect. + if (!_skippedFontsReported && !_showFonts && _verbosity != Module.MAXIMUM_VERBOSITY) { + info.setMessage(new InfoMessage(MessageConstants.PDF_HUL_105)); // PDF-HUL-105 + _skippedFontsReported = true; + } + } + } + } + } catch (PdfException e) { + e.disparage(info); + if (e.getJhoveMessage() != null) + info.setMessage(new ErrorMessage(e.getJhoveMessage(), _parser.getOffset())); + else info.setMessage(new ErrorMessage(e.getMessage(), _parser.getOffset())); + return; + } catch (Exception e) { + // Unexpected exception. + _logger.log(Level.WARNING, MessageConstants.PDF_HUL_106.getMessage(), e); + info.setWellFormed(false); + info.setMessage( + new ErrorMessage( + MessageConstants.PDF_HUL_106, // PDF-HUL-106 + e.toString(), + _parser.getOffset())); + return; + } + } + + /** + * Add the font to the appropriate map, and return its subtype. If we've exceeded the maximum + * number of fonts, then ignore it. + */ + protected String addFontToMap(PdfDictionary font) { + if (++_nFonts > maxFonts) { + return null; + } + String subtypeStr = null; + try { + PdfSimpleObject subtype = (PdfSimpleObject) font.get(DICT_KEY_FONT_SUBTYPE); + subtypeStr = subtype.getStringValue(); + if (FONT_TYPE0.equals(subtypeStr)) { + _type0FontsMap.put(new Integer(font.getObjNumber()), font); + // If the font is Type 0, we must go + // through its descendant fonts + PdfObject desc0 = font.get(DICT_KEY_DESCENDANT_FONTS); + PdfArray descendants = (PdfArray) resolveIndirectObject(desc0); + Vector subfonts = descendants.getContent(); + Iterator subfontIter = subfonts.iterator(); + while (subfontIter.hasNext()) { + PdfObject subfont = subfontIter.next(); + subfont = resolveIndirectObject(subfont); + addFontToMap((PdfDictionary) subfont); + } + } else if (FONT_TYPE1.equals(subtypeStr)) { + _type1FontsMap.put(new Integer(font.getObjNumber()), font); + } else if (FONT_MM_TYPE1.equals(subtypeStr)) { + _mmFontsMap.put(new Integer(font.getObjNumber()), font); + } else if (FONT_TYPE3.equals(subtypeStr)) { + _type3FontsMap.put(new Integer(font.getObjNumber()), font); + } else if (FONT_TRUE_TYPE.equals(subtypeStr)) { + _trueTypeFontsMap.put(new Integer(font.getObjNumber()), font); + } else if (FONT_CID_TYPE0.equals(subtypeStr)) { + _cid0FontsMap.put(new Integer(font.getObjNumber()), font); + } else if (FONT_CID_TYPE2.equals(subtypeStr)) { + _cid2FontsMap.put(new Integer(font.getObjNumber()), font); + } + return subtypeStr; + } catch (Exception e) { + return null; + } + } + + /** + * **************************************************************** PRIVATE CLASS METHODS. + * **************************************************************** + */ + protected static String toHex(String s) { + StringBuffer buffer = new StringBuffer("0x"); + + int len = s.length(); + for (int i = 0; i < len; i++) { + String h = Integer.toHexString(s.charAt(i)); + if (h.length() < 2) { + buffer.append("0"); + } + buffer.append(h); + } + + return buffer.toString(); + } + + protected static String toHex(Vector v) { + StringBuffer buffer = new StringBuffer("0x"); + + int len = v.size(); + for (int i = 0; i < len; i++) { + int hdigit = v.elementAt(i).intValue(); + String h = Integer.toHexString(hdigit); + if (h.length() < 2) { + buffer.append("0"); + } + buffer.append(h); + } + + return buffer.toString(); + } + + /** + * If the argument is an indirect object reference, returns the object it resolves to, otherwise + * returns the object itself. In particular, calling with null will return null. + */ + public PdfObject resolveIndirectObject(PdfObject obj) throws PdfException, IOException { + if (obj instanceof PdfIndirectObj) { + int objIndex = ((PdfIndirectObj) obj).getObjNumber(); + /* + * Here we need to allow for the possibility that the + * object is compressed in an object stream. That means + * creating a new structure (call it _xref2) that contains + * the stream object number and offset whenever _xref[objIndex] + * is negative. _xref2 will have to contain the content + * stream object number (which will itself have to be + * resolved) and the offset into the object stream. + */ + return getObject(objIndex, MAX_OBJ_STREAM_DEPTH); + } + return obj; + } + + /** + * Returns an object of a given number. This may involve recursion into object streams, in which + * case it calls itself. + * + * @param objIndex The object number to look up + * @param recGuard The maximum permitted number of recursion levels; no particular value is + * required, but 30 or more should avoid false exceptions. + */ + protected PdfObject getObject(int objIndex, int recGuard) throws PdfException, IOException { + /* Guard against infinite recursion */ + if (recGuard <= 0) { + throw new PdfMalformedException(MessageConstants.PDF_HUL_107); + } + long offset = _xref[objIndex]; + if (offset == 0) { + return null; // This is considered legitimate by the spec + } + if (offset < 0) { + return getObjectFromStream(objIndex, recGuard); + } + _parser.seek(offset); + PdfObject obj = _parser.readObjectDef(); + // + // Experimental carl@openpreservation.org 2018-03-14 + // + // Previously all object numbers (ids) were overwritten even if they'd + // previously been assigned. + // + // This is caused by a little confusion where the object ID and the + // index of the _xref array are used interchangeably when they're not + // the same thing. There's an assumption when for the _xref array + // that the objects will have continuous numeric object numbers. This + // means that the object number and array position will always be the + // same. The setting of the object number meant that the wrong object + // could + // be returned with the id changed to match the id requested. + // + // My guess is that the assignment was put in to ensure that an + // object that escaped initialisation had an object number. If that's + // the case then the code below will still allow that to happen but + // will prevent assigned numbers from been overwritten by the xref array + // position. + if (obj.getObjNumber() == -1) { + obj.setObjNumber(objIndex); + } + return obj; + } + + /** Return the RandomAccessFile being read. */ + public RandomAccessFile getFile() { + return _raf; + } + + /** Returns the catalog dictionary object. */ + public PdfDictionary getCatalogDict() { + return _docCatDict; + } + + /** Returns the trailer dictionary object. */ + public PdfDictionary getTrailerDict() { + return _trailerDict; + } + + /** Returns the viewer preferences dictionary object. */ + public PdfDictionary getViewPrefDict() { + return _viewPrefDict; + } + + /** Returns the outlines dictionary object. */ + public PdfDictionary getOutlineDict() { + return _outlineDict; + } + + /** + * Get a font map. The map returned is determined by the selector. Any other value returns null. + */ + public Map getFontMap(int selector) { + switch (selector) { + case F_TYPE0: + return _type0FontsMap; + case F_TYPE1: + return _type1FontsMap; + case F_TT: + return _mmFontsMap; + case F_TYPE3: + return _type3FontsMap; + case F_MM1: + return _mmFontsMap; + case F_CID0: + return _cid0FontsMap; + case F_CID2: + return _cid2FontsMap; + default: + return null; + } + } + + /** + * Return a List of all the font maps. Together, these contain all the fonts and subfonts in the + * document. Some of the maps may be null. + */ + public List> getFontMaps() { + List> lst = new ArrayList>(7); + lst.add(_type0FontsMap); + lst.add(_type1FontsMap); + lst.add(_mmFontsMap); + lst.add(_type3FontsMap); + lst.add(_trueTypeFontsMap); + lst.add(_cid0FontsMap); + lst.add(_cid2FontsMap); + return lst; + } + + /** + * Returns a NameTreeNode for the EmbeddedFiles entry of the Names dictionary. Returns null if + * there isn't one. + */ + public NameTreeNode getEmbeddedFiles() { + return _embeddedFiles; + } + + /** + * Add the various font lists as a fonts property. Note: only add the "Fonts" property if there + * are, in fact, fonts defined. + */ + protected void addFontsProperty(List metadataList) { + List fontTypesList = new LinkedList(); + Property fontp = null; + if (_type0FontsMap != null && !_type0FontsMap.isEmpty()) { + try { + fontp = buildFontProperty(PROP_NAME_FONT_TYPE0, _type0FontsMap, F_TYPE0); + fontTypesList.add(fontp); + } catch (ClassCastException e) { + // Report an error here? + } + } + if (_type1FontsMap != null && !_type1FontsMap.isEmpty()) { + try { + fontp = buildFontProperty(PROP_NAME_FONT_TYPE1, _type1FontsMap, F_TYPE1); + fontTypesList.add(fontp); + } catch (ClassCastException e) { + // Report an error here? + } + } + if (_trueTypeFontsMap != null && !_trueTypeFontsMap.isEmpty()) { + try { + fontp = buildFontProperty(PROP_NAME_FONT_TRUE_TYPE, _trueTypeFontsMap, F_TT); + fontTypesList.add(fontp); + } catch (ClassCastException e) { + // Report an error here? + } + } + if (_type3FontsMap != null && !_type3FontsMap.isEmpty()) { + try { + fontp = buildFontProperty(PROP_NAME_FONT_TYPE3, _type3FontsMap, F_TYPE3); + fontTypesList.add(fontp); + } catch (ClassCastException e) { + } + } + if (_mmFontsMap != null && !_mmFontsMap.isEmpty()) { + try { + fontp = buildFontProperty(PROP_NAME_FONT_MM_TYPE1, _mmFontsMap, F_MM1); + fontTypesList.add(fontp); + } catch (ClassCastException e) { + } + } + if (_cid0FontsMap != null && !_cid0FontsMap.isEmpty()) { + try { + fontp = buildFontProperty(PROP_NAME_FONT_CID_TYPE0, _cid0FontsMap, F_CID0); + fontTypesList.add(fontp); + } catch (ClassCastException e) { + } + } + if (_cid2FontsMap != null && !_cid2FontsMap.isEmpty()) { + try { + fontp = buildFontProperty(PROP_NAME_FONT_CID_TYPE2, _cid2FontsMap, F_CID2); + fontTypesList.add(fontp); + } catch (ClassCastException e) { + } + } + if (fontTypesList.size() > 0) { + metadataList.add( + new Property(PROP_NAME_FONTS, PropertyType.PROPERTY, PropertyArity.LIST, fontTypesList)); + } + } + + /* Build Pages property, with associated subproperties. */ + protected void addPagesProperty(List metadataList, RepInfo info) { + _pagesList = new LinkedList(); + _pageSeqMap = new HashMap(500); + try { + _docTreeRoot.startWalk(); + int pageIndex = 0; + // Start the pipe with two entries. + // We always need to have the current and the next + // entry from the page label tree in order to determine + // the lower and upper bounds of the applicable range. + // If the first entry has a bound greater than zero, + // that appears to be an undefined situation, so we + // always treat the first entry as starting at zero. + if (_pageLabelRoot != null) { + if (!_pageLabelRoot.findNextKeyValue()) { + throw new PdfMalformedException(MessageConstants.PDF_HUL_111); // PDF-HUL-111 + } + + _pageLabelRoot.findNextKeyValue(); + } + for (; ; ) { + // Get all the page objects in the document sequentially + // Have to do this in two passes so that link + // destinations can be properly reported. + PageObject page = _docTreeRoot.nextPageObject(); + if (page == null) { + break; + } + _pageSeqMap.put(new Integer(page.getDict().getObjNumber()), new Integer(pageIndex + 1)); + } + _docTreeRoot.startWalk(); + for (; ; ) { + PageObject page = _docTreeRoot.nextPageObject(); + if (page == null) { + break; + } + Property p = buildPageProperty(page, pageIndex++, info); + _pagesList.add(p); + } + if (_showPages || _verbosity == Module.MAXIMUM_VERBOSITY) { + Property prop = + new Property(PROP_NAME_PAGES, PropertyType.PROPERTY, PropertyArity.LIST, _pagesList); + metadataList.add(prop); + } else { + if (!_skippedPagesReported) { + info.setMessage(new InfoMessage(MessageConstants.PDF_HUL_112)); // PDF-HUL-112 + _skippedPagesReported = true; + } + } + } catch (PdfException e) { + + e.disparage(info); + if (e.getJhoveMessage() != null) + info.setMessage(new ErrorMessage(e.getJhoveMessage(), _parser.getOffset())); + else info.setMessage(new ErrorMessage(e.getMessage(), _parser.getOffset())); + return; + } + } + + /* Build a subproperty for one PageObject. */ + protected Property buildPageProperty(PageObject page, int idx, RepInfo info) throws PdfException { + List pagePropList = new ArrayList(4); + try { + // Foo on Java's inability to return values through + // parameters. Passing an array is a crock to achieve + // that effect. + int[] nominalNum = new int[1]; + Property plProp = buildPageLabelProperty(page, idx, nominalNum); + if (plProp != null) { + pagePropList.add(plProp); + } + if (plProp == null || nominalNum[0] != idx + 1) { + // Page sequence is different from label, or + // there is no label. Make it 1-based. + pagePropList.add( + new Property(PROP_NAME_SEQUENCE, PropertyType.INTEGER, new Integer(idx + 1))); + } + } catch (PdfException e) { + throw e; + } catch (Exception f) { + throw new PdfMalformedException(MessageConstants.PDF_HUL_113); // PDF-HUL-113 + } + + try { + List annotsList = new LinkedList(); + PdfArray annots = page.getAnnotations(); + if (annots != null) { + Vector contents = annots.getContent(); + for (int i = 0; i < contents.size(); i++) { + PdfObject annot = resolveIndirectObject(contents.elementAt(i)); + if (annot instanceof PdfDictionary) { + annotsList.add(buildAnnotProperty((PdfDictionary) annot, info)); + } else { + // There are annotations which aren't dictionaries. I've + // run into this, + // but it violates the spec as far as I can tell. + throw new PdfInvalidException(MessageConstants.PDF_HUL_114); // PDF-HUL-114 + } + } + if (!annotsList.isEmpty()) { + if (_showAnnotations || _verbosity == Module.MAXIMUM_VERBOSITY) { + Property annotProp = + new Property( + PROP_NAME_ANNOTATIONS, PropertyType.PROPERTY, PropertyArity.LIST, annotsList); + pagePropList.add(annotProp); + } else { + // We don't report annotations if we got here, + // but we do report that we don't report them. + if (!_skippedAnnotationsReported) { + info.setMessage(new InfoMessage(MessageConstants.PDF_HUL_115)); // PDF-HUL-115 + _skippedAnnotationsReported = true; + } + } + } + } + } catch (PdfException e) { + throw e; + } catch (Exception f) { + throw new PdfMalformedException(MessageConstants.PDF_HUL_116); // PDF-HUL-116 + } + + try { + // Rotation property is inheritable + PdfObject tempObj = page.get(DICT_KEY_ROTATE, true); + PdfSimpleObject rot = null; + if (tempObj != null && tempObj instanceof PdfSimpleObject) { + rot = (PdfSimpleObject) tempObj; + } else if (tempObj != null && tempObj instanceof PdfIndirectObj) { + rot = (PdfSimpleObject) ((PdfIndirectObj) tempObj).getObject(); + } + if (rot != null && rot.getIntValue() != 0) { + pagePropList.add( + new Property(PROP_NAME_ROTATE, PropertyType.INTEGER, new Integer(rot.getIntValue()))); + } + + // UserUnit property (1.6), not inheritable + PdfSimpleObject uu = (PdfSimpleObject) page.get(DICT_KEY_USER_UNIT, false); + if (uu != null) { + pagePropList.add( + new Property( + PROP_NAME_USER_UNIT, PropertyType.DOUBLE, new Double(rot.getDoubleValue()))); + } + // Viewport dictionaries (1.6), not inheritable + PdfArray vp = (PdfArray) page.get(DICT_KEY_VIEWPORT, false); + if (vp != null) { + Vector vpv = vp.getContent(); + Iterator iter = vpv.iterator(); + List vplist = new ArrayList(vpv.size()); + while (iter.hasNext()) { + PdfDictionary vpd = (PdfDictionary) resolveIndirectObject(iter.next()); + PdfObject vpdbb = vpd.get(DICT_KEY_BBOX); + List vpPropList = new ArrayList(); + vpPropList.add(makeRectProperty((PdfArray) resolveIndirectObject(vpdbb), DICT_KEY_BBOX)); + PdfObject meas = vpd.get(DICT_KEY_MEASURE); + if (meas instanceof PdfDictionary) { + vpPropList.add(buildMeasureProperty((PdfDictionary) meas)); + // No, that's wrong -- the Viewport property itself + // needs to be a list with a bounding box. + } + vplist.add( + new Property( + PROP_NAME_VIEWPORT, PropertyType.PROPERTY, PropertyArity.LIST, vpPropList)); + } + pagePropList.add( + new Property(PROP_NAME_VIEWPORTS, PropertyType.PROPERTY, PropertyArity.LIST, vplist)); + } + // Thumbnail -- we just report if it's there. It's a + // non-inheritable property + PdfObject thumb = page.get(DICT_KEY_THUMB, false); + if (thumb != null) { + pagePropList.add(new Property(PROP_NAME_THUMB, PropertyType.BOOLEAN, Boolean.TRUE)); + } + return new Property(PROP_NAME_PAGE, PropertyType.PROPERTY, PropertyArity.LIST, pagePropList); + } catch (PdfException e) { + throw e; + } catch (Exception f) { + throw new PdfMalformedException(MessageConstants.PDF_HUL_117); // PDF-HUL-117 + } + } + + /* + * Build a subproperty of a subproperty for page labels. + * The nomNumRef argument is a crock for returning the + * nominal number; element 0 of the array is replaced + * by the nominal number of the page. + */ + protected Property buildPageLabelProperty(PageObject page, int pageIndex, int[] nomNumRef) + throws PdfException { + if (_pageLabelRoot == null) { + return null; // no page label info + } + + // Note that our "current" page is the page label tree's + // "previous" key. Sorry about that... + int curFirstPage = _pageLabelRoot.getPrevKey(); + int nextFirstPage = _pageLabelRoot.getCurrentKey(); + try { + // If we're onto the next page range, advance our pointers. + if (pageIndex >= nextFirstPage) { + _pageLabelRoot.findNextKeyValue(); + curFirstPage = nextFirstPage; + } + PdfDictionary pageLabelDict = + (PdfDictionary) resolveIndirectObject(_pageLabelRoot.getPrevValue()); + StringBuffer labelText = new StringBuffer(); + PdfSimpleObject prefixObj = (PdfSimpleObject) pageLabelDict.get(DICT_KEY_P); + if (prefixObj != null) { + labelText.append(prefixObj.getStringValue()); + } + PdfSimpleObject firstPageObj = (PdfSimpleObject) pageLabelDict.get("St"); + // Sequence start value defaults to 1 if there's no start value + int firstPageVal = ((firstPageObj != null) ? firstPageObj.getIntValue() : 1); + int nominalPage = pageIndex - curFirstPage + firstPageVal; + if (nominalPage <= 0) { + throw new PdfInvalidException(MessageConstants.PDF_HUL_118); // pDF-HUL-118 + } + nomNumRef[0] = nominalPage; + + // Get the numbering style. If there is no numbering + // style entry, the label consists only of the prefix. + PdfSimpleObject numStyleObj = (PdfSimpleObject) pageLabelDict.get("S"); + String numStyle; + if (numStyleObj == null) { + numStyle = null; + } else { + numStyle = numStyleObj.getStringValue(); + } + if ("D".equals(numStyle)) { + // Nice, simple decimal numbers + labelText.append(nominalPage); + } else if ("R".equals(numStyle)) { + // Upper case roman numerals + labelText.append(PageLabelNode.intToRoman(nominalPage, true)); + } else if ("r".equals(numStyle)) { + // Lower case roman numerals + labelText.append(PageLabelNode.intToRoman(nominalPage, false)); + } else if ("A".equals(numStyle)) { + // Uppercase letters (A-Z, AA-ZZ, ...) + labelText.append(PageLabelNode.intToBase26(nominalPage, true)); + } else if ("a".equals(numStyle)) { + // Lowercase letters (a-z, aa-zz, ...) + labelText.append(PageLabelNode.intToBase26(nominalPage, false)); + } + // It screws up the PDF output if we have a blank Label property. + if (labelText.length() == 0) { + labelText.append(EMPTY_LABEL_PROPERTY); + } + return new Property(PROP_NAME_LABEL, PropertyType.STRING, labelText.toString()); + } catch (Exception e) { + throw new PdfMalformedException(MessageConstants.PDF_HUL_119); // PDF-HUL-119 + } + } + + /* Build a subproperty for a measure dictionary. */ + protected Property buildMeasureProperty(PdfDictionary meas) { + List plist = new ArrayList(); + PdfObject itemObj = meas.get(DICT_KEY_XOBJ_SUBTYPE); + if (itemObj instanceof PdfSimpleObject) { + plist.add( + new Property( + PROP_NAME_SUBTYPE, + PropertyType.STRING, + ((PdfSimpleObject) itemObj).getStringValue())); + } + itemObj = meas.get(DICT_KEY_R); + if (itemObj instanceof PdfSimpleObject) { + plist.add( + new Property( + PROP_NAME_RATIO, PropertyType.STRING, ((PdfSimpleObject) itemObj).getStringValue())); + } + // All kinds of stuff I could add -- limit it to the required + // X, Y, D and A arrays. + itemObj = meas.get("X"); + if (itemObj instanceof PdfArray) { + Vector v = ((PdfArray) itemObj).getContent(); + double[] x = new double[v.size()]; + for (int i = 0; i < v.size(); i++) { + PdfSimpleObject xobj = (PdfSimpleObject) v.elementAt(i); + x[i] = xobj.getDoubleValue(); + } + plist.add(new Property("X", PropertyType.DOUBLE, PropertyArity.ARRAY, x)); + } + itemObj = meas.get("Y"); + if (itemObj instanceof PdfArray) { + Vector v = ((PdfArray) itemObj).getContent(); + double[] x = new double[v.size()]; + for (int i = 0; i < v.size(); i++) { + PdfSimpleObject xobj = (PdfSimpleObject) v.elementAt(i); + x[i] = xobj.getDoubleValue(); + } + plist.add(new Property("Y", PropertyType.DOUBLE, PropertyArity.ARRAY, x)); + } + itemObj = meas.get("D"); + if (itemObj instanceof PdfArray) { + Vector v = ((PdfArray) itemObj).getContent(); + double[] x = new double[v.size()]; + for (int i = 0; i < v.size(); i++) { + PdfSimpleObject xobj = (PdfSimpleObject) v.elementAt(i); + x[i] = xobj.getDoubleValue(); + } + plist.add(new Property(PROP_NAME_DISTANCE, PropertyType.DOUBLE, PropertyArity.ARRAY, x)); + } + itemObj = meas.get("A"); + if (itemObj instanceof PdfArray) { + Vector v = ((PdfArray) itemObj).getContent(); + double[] x = new double[v.size()]; + for (int i = 0; i < v.size(); i++) { + PdfSimpleObject xobj = (PdfSimpleObject) v.elementAt(i); + x[i] = xobj.getDoubleValue(); + } + plist.add(new Property(PROP_NAME_AREA, PropertyType.DOUBLE, PropertyArity.ARRAY, x)); + } + return new Property(PROP_NAME_MEASURE, PropertyType.PROPERTY, PropertyArity.LIST, plist); + } + + /* Build a subproperty of a subproperty for an annotation. */ + protected Property buildAnnotProperty(PdfDictionary annot, RepInfo info) throws PdfException { + List propList = new ArrayList(7); + PdfObject itemObj; + try { + // Subtype is required + itemObj = annot.get(DICT_KEY_XOBJ_SUBTYPE); + propList.add( + new Property( + PROP_NAME_SUBTYPE, + PropertyType.STRING, + ((PdfSimpleObject) itemObj).getStringValue())); + + // Contents is optional for some subtypes, required for + // others. We consider it optional here. + itemObj = annot.get(DICT_KEY_CONTENTS); + if (itemObj != null) { + propList.add( + new Property( + PROP_NAME_CONTENTS, + PropertyType.STRING, + _encrypted ? ENCRYPTED : ((PdfSimpleObject) itemObj).getStringValue())); + } + + // Rectangle is required, and must be in the rectangle format + itemObj = annot.get(DICT_KEY_RECT); + propList.add(makeRectProperty((PdfArray) resolveIndirectObject(itemObj), PROP_NAME_RECT)); + + // Name comes from the NM entry and is optional + itemObj = annot.get("NM"); + if (itemObj != null) { + propList.add( + new Property( + DICT_KEY_NAME, PropertyType.STRING, ((PdfSimpleObject) itemObj).getStringValue())); + } + + // LastModified is optional. The documentation says that + // a PDF date is preferred but not guaranteed. We just + // put it out as a string. + itemObj = annot.get("M"); + if (itemObj != null) { + Literal lastModLit = (Literal) ((PdfSimpleObject) itemObj).getToken(); + Property dateProp; + dateProp = new Property(PROP_NAME_LAST_MOD, PropertyType.STRING, lastModLit.getValue()); + + propList.add(dateProp); + } + + // Flags. + itemObj = annot.get("F"); + if (itemObj != null) { + int flagValue = ((PdfSimpleObject) itemObj).getIntValue(); + Property flagProp = + (buildBitmaskProperty( + flagValue, PROP_NAME_FLAGS, PdfStrings.ANNOTATIONFLAGS, PROP_VAL_NO_FLAGS_SET)); + if (flagProp != null) { + propList.add(flagProp); + } + } + + // Appearance dictionary -- just check if it's there. + itemObj = annot.get("AP"); + if (itemObj != null) { + propList.add(new Property(PROP_NAME_APP_DICT, PropertyType.BOOLEAN, Boolean.TRUE)); + } + + // Action dictionary -- if it's there, set actionsExist + itemObj = annot.get("A"); + if (itemObj != null) { + _actionsExist = true; + itemObj = resolveIndirectObject(itemObj); + // Actions are as common as Destinations for + // connecting to destination pages. If the Action + // is of type GoTo, note its destination. + PdfSimpleObject actionSubtype = (PdfSimpleObject) ((PdfDictionary) itemObj).get("S"); + if (actionSubtype == null) { + throw new PdfMalformedException(MessageConstants.PDF_HUL_120); // PDF-HUL-120 + } + if (ACTION_VAL_GOTO.equals(actionSubtype.getStringValue())) { + PdfObject destObj = ((PdfDictionary) itemObj).get("D"); + if (destObj != null) { + addDestination(destObj, PROP_NAME_ACTION_DEST, propList, info); + } + } + } + + // Destination object. + itemObj = annot.get(DICT_KEY_DEST); + if (itemObj != null) { + addDestination(itemObj, PROP_NAME_DESTINATION, propList, info); + } + + // Reply Type (RT) (1.6) + itemObj = annot.get("RT"); + if (itemObj instanceof PdfSimpleObject) { + String type = ((PdfSimpleObject) itemObj).getStringValue(); + propList.add(new Property(PROP_NAME_REPLY_TYPE, PropertyType.STRING, type)); + } + + // Intent (IT) (1.6) + itemObj = annot.get("IT"); + if (itemObj instanceof PdfSimpleObject) { + String type = ((PdfSimpleObject) itemObj).getStringValue(); + propList.add(new Property(PROP_NAME_INTENT, PropertyType.STRING, type)); + } + + // Callout Line (CL) (1.6) + itemObj = annot.get("CL"); + if (itemObj instanceof PdfArray) { + Vector clData = ((PdfArray) itemObj).getContent(); + // This should be an array of numbers. + Iterator iter = clData.iterator(); + List clList = new ArrayList(6); + while (iter.hasNext()) { + PdfSimpleObject clItem = (PdfSimpleObject) iter.next(); + clList.add(new Double(clItem.getDoubleValue())); + } + propList.add( + new Property(PROP_NAME_CALLOUT_LINE, PropertyType.DOUBLE, PropertyArity.LIST, clList)); + } + + return new Property( + PROP_NAME_ANNOTATION, PropertyType.PROPERTY, PropertyArity.LIST, propList); + } catch (PdfException ee) { + // Just rethrow these + throw ee; + } catch (Exception e) { + throw new PdfMalformedException(MessageConstants.PDF_HUL_121); // PDF-HUL-121 + } + } + + /* + * Given a PdfObject that stands for a Destination, add + * a representative property to the property list. + */ + protected void addDestination( + PdfObject itemObj, String propName, List propList, RepInfo info) { + try { + Destination dest = new Destination(itemObj, this, false); + if (dest.isIndirect()) { + // Encryption messes up name trees + if (!_encrypted) { + int pageObjNum = resolveIndirectDest(dest.getIndirectDest(), info); + if (pageObjNum == -1) { + // The scope of the reference is outside this + // file, so we just report it as such. + propList.add(new Property(propName, PropertyType.STRING, PROP_VAL_EXTERNAL)); + } else { + propList.add(new Property(propName, PropertyType.INTEGER, new Integer(pageObjNum))); + } + } + } else { + if (dest.getPageDest() == null) { + return; // can't get the page object number + } + int pageObjNum = dest.getPageDestObjNumber(); + Integer destPg = _pageSeqMap.get(new Integer(pageObjNum)); + if (destPg != null) { + propList.add(new Property(propName, PropertyType.INTEGER, destPg)); + } + } + } catch (Exception e) { + + String msg = e.getClass().getName(); + String msg1 = e.getMessage(); + if (msg1 != null) { + msg = msg + ": " + msg1; + } + JhoveMessage message = + JhoveMessages.getMessageInstance(MessageConstants.PDF_HUL_122.getId(), msg); + propList.add(new Property(propName, PropertyType.STRING, PROP_VAL_NULL)); + info.setMessage( + new ErrorMessage( + message, // PDF-HUL-122 + _parser.getOffset())); + info.setValid(false); + } + } + + /* + * Build up a property for one of the kinds of fonts + * in the file. + */ + protected Property buildFontProperty(String name, Map map, int fontType) { + List fontList = new LinkedList(); // list of fonts + Iterator fontIter = map.values().iterator(); + while (fontIter.hasNext()) { + // For each font in the map, build a property for it, + // which consists of a list of scalar properties. Each kind + // of font is spec'ed to have a slightly different set of + // properties, grumble... + PdfDictionary dict = (PdfDictionary) fontIter.next(); + List fontPropList = oneFontPropList(dict, fontType); + Property fProp = + new Property(PROP_NAME_FONT, PropertyType.PROPERTY, PropertyArity.LIST, fontPropList); + fontList.add(fProp); + } + return new Property(name, PropertyType.PROPERTY, PropertyArity.LIST, fontList); + } + + /* Build the Property list for a given font */ + protected List oneFontPropList(PdfDictionary dict, int fontType) { + List fontPropList = new LinkedList(); + Property prop; + if (fontType == F_TYPE1 || fontType == F_TYPE3 || fontType == F_MM1 || fontType == F_TT) { + PdfObject tempObj = dict.get(DICT_KEY_NAME); + PdfSimpleObject nameObj = null; + if (tempObj instanceof PdfSimpleObject) { + nameObj = (PdfSimpleObject) tempObj; + } else if (tempObj instanceof PdfIndirectObj) { + nameObj = (PdfSimpleObject) ((PdfIndirectObj) tempObj).getObject(); + } + + if (nameObj != null) { + String nameStr = nameObj.getStringValue(); + prop = new Property(DICT_KEY_NAME, PropertyType.STRING, nameStr); + fontPropList.add(prop); + } + } + + String baseStr = null; + if (fontType != F_TYPE3) { + PdfObject tempObj = dict.get(DICT_KEY_BASE_FONT); + PdfSimpleObject baseFontObj = null; + if (tempObj instanceof PdfSimpleObject) { + baseFontObj = (PdfSimpleObject) tempObj; + } else if (tempObj instanceof PdfIndirectObj) { + baseFontObj = (PdfSimpleObject) ((PdfIndirectObj) tempObj).getObject(); + } + + if (baseFontObj != null) { + baseStr = baseFontObj.getStringValue(); + prop = new Property(PROP_NAME_BASE_FONT, PropertyType.STRING, baseStr); + fontPropList.add(prop); + } + } + + if (fontType == F_CID0 || fontType == F_CID2) { + PdfObject elCid = dict.get(DICT_KEY_CID_INFO); + try { + elCid = resolveIndirectObject(elCid); + } catch (Exception e) { + } + if (elCid instanceof PdfDictionary) { + prop = buildCIDInfoProperty((PdfDictionary) elCid); + fontPropList.add(prop); + } + } + + if (fontType == F_TYPE1 || fontType == F_TT || fontType == F_MM1) { + if (isFontSubset(baseStr)) { + prop = new Property(PROP_NAME_FONT_SUBSET, PropertyType.BOOLEAN, Boolean.TRUE); + fontPropList.add(prop); + } + } + + if (fontType == F_TYPE1 || fontType == F_TT || fontType == F_MM1 || fontType == F_TYPE3) { + PdfObject firstCharObj = dict.get(DICT_KEY_FIRST_CHAR); + if (firstCharObj instanceof PdfIndirectObj) { + firstCharObj = ((PdfIndirectObj) firstCharObj).getObject(); + } + try { + int firstChar = ((PdfSimpleObject) firstCharObj).getIntValue(); + prop = new Property(PROP_NAME_FIRST_CHAR, PropertyType.INTEGER, new Integer(firstChar)); + fontPropList.add(prop); + } catch (Exception e) { + } + + PdfObject lastCharObj = dict.get(DICT_KEY_LAST_CHAR); + if (lastCharObj instanceof PdfIndirectObj) { + lastCharObj = ((PdfIndirectObj) lastCharObj).getObject(); + } + try { + int lastChar = ((PdfSimpleObject) lastCharObj).getIntValue(); + prop = new Property(PROP_NAME_LAST_CHAR, PropertyType.INTEGER, new Integer(lastChar)); + fontPropList.add(prop); + } catch (Exception e) { + } + } + + if (fontType == F_TYPE3) { + // Put FontBBox and CharProcs into properties + PdfObject bboxObj = dict.get(DICT_KEY_FONT_BBOX); + try { + if (bboxObj instanceof PdfArray) { + fontPropList.add(makeRectProperty((PdfArray) bboxObj, PROP_VAL_FONT_BBOX)); + } + } catch (Exception e) { + } + + // For CharProcs, we're just checking if it's there. + // (It's required for a Type 3 font.) + // PdfObject charProcs = dict.get("CharProcs"); + // prop = new Property("CharProcs", + // PropertyType.BOOLEAN, + // Boolean.valueOf(charProcs != null)); + // fontPropList.add(prop); + } + + if (fontType == F_TYPE1 + || fontType == F_TT + || fontType == F_MM1 + || fontType == F_CID0 + || fontType == F_CID2) { + PdfObject descriptorObj = dict.get(DICT_KEY_FONT_DESCRIPTOR); + try { + descriptorObj = resolveIndirectObject(descriptorObj); + } catch (Exception e) { + } + if (descriptorObj instanceof PdfDictionary) { + prop = buildFontDescriptorProperty((PdfDictionary) descriptorObj); + fontPropList.add(prop); + } + } + + PdfObject encodingObj = dict.get(DICT_KEY_ENCODING); + try { + encodingObj = resolveIndirectObject(encodingObj); + } catch (Exception e) { + } + + if (fontType == F_TYPE0 + || fontType == F_TYPE1 + || fontType == F_TT + || fontType == F_MM1 + || fontType == F_TYPE3) { + // Encoding property -- but only if Encoding is a name + if (encodingObj instanceof PdfSimpleObject) { + prop = + new Property( + PROP_NAME_ENCODING, + PropertyType.STRING, + ((PdfSimpleObject) encodingObj).getStringValue()); + fontPropList.add(prop); + } + } + + if (fontType == F_TYPE1 || fontType == F_TT || fontType == F_MM1 || fontType == F_TYPE3) { + if (encodingObj != null && encodingObj instanceof PdfDictionary) { + prop = buildEncodingDictProperty((PdfDictionary) encodingObj); + fontPropList.add(prop); + } + } + + if (fontType == F_TYPE0) { + // Encoding is reported as a CMapDictionary property for type 0 + if (encodingObj != null && encodingObj instanceof PdfStream) { + prop = buildCMapDictProperty((PdfStream) encodingObj); + fontPropList.add(prop); + } + } + + if (fontType == F_TYPE3) { + // All we're interested in for Resources is whether + // the dictionary exists + PdfObject rsrc = dict.get(DICT_KEY_RESOURCES); + if (rsrc != null) { + prop = new Property(PROP_NAME_RESOURCES, PropertyType.BOOLEAN, Boolean.TRUE); + fontPropList.add(prop); + } + } + + if (fontType == F_TYPE0 + || fontType == F_TYPE1 + || fontType == F_TT + || fontType == F_MM1 + || fontType == F_TYPE3) { + PdfObject toUniObj = dict.get(DICT_KEY_TO_UNICODE); + if (toUniObj != null) { + prop = new Property(PROP_NAME_TO_UNICODE, PropertyType.BOOLEAN, Boolean.TRUE); + fontPropList.add(prop); + } + } + + return fontPropList; + } + + /* + * Code for CMapProperty for Type 0 fonts, based on the Encoding + * entry, broken out of buildFontProperty. + */ + protected Property buildCMapDictProperty(PdfStream encoding) { + PdfDictionary dict = encoding.getDict(); + List propList = new ArrayList(4); + Property prop = + new Property(PROP_NAME_CMAP_DICT, PropertyType.PROPERTY, PropertyArity.LIST, propList); + Property subprop; + + // PdfObject mapName = dict.get ("CMapName"); + + PdfObject cidSysInfo = dict.get(DICT_KEY_CID_INFO); + // We can use buildCIDInfoProperty here to build the subproperty + PdfDictionary cidDict; + List cidList = new LinkedList(); + try { + if (cidSysInfo instanceof PdfDictionary) { + // One CIDInfo dictionary + cidDict = (PdfDictionary) cidSysInfo; + subprop = buildCIDInfoProperty(cidDict); + cidList.add(subprop); + } else if (cidSysInfo instanceof PdfArray) { + // Many CIDInfo dictionaries + Vector v = ((PdfArray) cidSysInfo).getContent(); + for (int i = 0; i < v.size(); i++) { + cidDict = (PdfDictionary) v.elementAt(i); + Property subsubprop = buildCIDInfoProperty(cidDict); + cidList.add(subsubprop); + } + } + } catch (Exception e) { + } + + if (!cidList.isEmpty()) { + subprop = + new Property(PROP_NAME_CID_INFOS, PropertyType.PROPERTY, PropertyArity.LIST, cidList); + propList.add(subprop); + } + + // PdfObject wMod = dict.get("WMode"); + // PdfObject useCMap = dict.get("UseCMap"); + + return prop; + } + + /* + * Code for CIDInfoProperty for CIDFontType0 and CIDFontType2 + * conts. + */ + protected Property buildCIDInfoProperty(PdfDictionary dict) { + List propList = new ArrayList(3); + Property prop = + new Property(PROP_NAME_CID_INFO, PropertyType.PROPERTY, PropertyArity.LIST, propList); + Property subprop; + + // Add the registry identifier + PdfObject reg = dict.get(DICT_KEY_REGISTRY); + if (reg instanceof PdfSimpleObject) { + try { + String regText = ((PdfSimpleObject) reg).getStringValue(); + subprop = + new Property(PROP_NAME_REGISTRY, PropertyType.STRING, _encrypted ? ENCRYPTED : regText); + propList.add(subprop); + } catch (Exception e) { + } + } + + // Add the name of the char collection within the registry + PdfObject order = dict.get(DICT_KEY_ORDERING); + if (reg instanceof PdfSimpleObject) { + try { + String ordText = ((PdfSimpleObject) order).getStringValue(); + subprop = new Property(PROP_NAME_REGISTRY, PropertyType.STRING, ordText); + propList.add(subprop); + } catch (Exception e) { + } + } + + PdfObject supp = dict.get(DICT_KEY_SUPPLEMENT); + if (supp instanceof PdfSimpleObject) { + try { + int suppvalue = ((PdfSimpleObject) supp).getIntValue(); + subprop = new Property(PROP_NAME_SUPPLEMENT, PropertyType.INTEGER, new Integer(suppvalue)); + propList.add(subprop); + } catch (Exception e) { + } + } + return prop; + } + + /* + * Code for EncodingDictionary Property for type 1, 3, TrueType, and + * MM fonts. This is based on a dictionary entry with the same name + * as the one for buildCMapDictProperty, but different information. + * Included properties are BaseEncoding and Differences. + */ + protected Property buildEncodingDictProperty(PdfDictionary encodingDict) { + List propList = new ArrayList(2); + Property prop = + new Property( + PROP_NAME_ENCODING_DICTIONARY, PropertyType.PROPERTY, PropertyArity.LIST, propList); + PdfObject baseEnc = encodingDict.get(DICT_KEY_BASE_ENCODING); + if (baseEnc instanceof PdfSimpleObject) { + String baseEncString = ((PdfSimpleObject) baseEnc).getStringValue(); + if (baseEncString != null) { + Property baseEncProp = + new Property(PROP_NAME_BASE_ENCODING, PropertyType.STRING, baseEncString); + propList.add(baseEncProp); + } + } + + PdfObject diffs = encodingDict.get(DICT_KEY_DIFFERENCES); + Property diffsProp = + new Property(PROP_NAME_DIFFERENCES, PropertyType.BOOLEAN, Boolean.valueOf(diffs != null)); + propList.add(diffsProp); + + return prop; + } + + /* + * Separated-out code for FontDescriptor property. This + * is a list of six Properies: FontName, Flags, + * FontBBox, FontFile, FontFile2, and FontFile3. + */ + protected Property buildFontDescriptorProperty(PdfDictionary encodingDict) { + List propList = new ArrayList(6); + Property prop = + new Property(PROP_NAME_FONT_DESC, PropertyType.PROPERTY, PropertyArity.LIST, propList); + Property subprop; + try { + PdfSimpleObject fName = (PdfSimpleObject) encodingDict.get(DICT_KEY_FONT_NAME); + String fNameStr = fName.getStringValue(); + subprop = new Property(PROP_NAME_FONT_NAME, PropertyType.STRING, fNameStr); + propList.add(subprop); + } catch (Exception e) { + } + + try { + PdfSimpleObject flags = (PdfSimpleObject) encodingDict.get(DICT_KEY_FLAGS); + int flagValue = flags.getIntValue(); + subprop = + buildBitmaskProperty( + flagValue, PROP_NAME_FLAGS, PdfStrings.FONTDESCFLAGS, PROP_VAL_NO_FLAGS_SET); + if (subprop != null) { + propList.add(subprop); + } + } catch (Exception e) { + } + + try { + PdfArray bboxObj = (PdfArray) encodingDict.get(DICT_KEY_FONT_BBOX); + double[] bbox = bboxObj.toRectangle(); + // toRectangle is written to return an array of double, + // which is what the bounding box is in the most general + // case; but the spec requires an array of integer, so + // we convert is. This may seem like an excess of work, + // but I'd rather have toRectangle do the right thing + // rather than losing generality. + if (bbox != null) { + int[] ibbox = new int[4]; + for (int i = 0; i < 4; i++) { + ibbox[i] = (int) bbox[i]; + } + subprop = + new Property(PROP_NAME_FONT_BBOX, PropertyType.INTEGER, PropertyArity.ARRAY, ibbox); + propList.add(subprop); + } + } catch (Exception e) { + } + + PdfObject fontFile = encodingDict.get(DICT_KEY_FONT_FILE); + if (fontFile != null) { + // All we care about is whether it exists or not + subprop = new Property(PROP_NAME_FONT_FILE, PropertyType.BOOLEAN, Boolean.TRUE); + propList.add(subprop); + } + fontFile = encodingDict.get(DICT_KEY_FONT_FILE_2); + if (fontFile != null) { + subprop = new Property(PROP_NAME_FONT_FILE_2, PropertyType.BOOLEAN, Boolean.TRUE); + propList.add(subprop); + } + fontFile = encodingDict.get(DICT_KEY_FONT_FILE_3); + if (fontFile != null) { + subprop = new Property(PROP_NAME_FONT_FILE_3, PropertyType.BOOLEAN, Boolean.TRUE); + propList.add(subprop); + } + return prop; + } + + protected Property buildViewPrefProperty(PdfDictionary prefDict) { + Property p; + PdfObject ob; + boolean b; + String s; + List propList = new ArrayList(12); + Property prop = + new Property(DICT_KEY_VIEWER_PREFS, PropertyType.PROPERTY, PropertyArity.LIST, propList); + + ob = prefDict.get(DICT_KEY_HIDE_TOOLBAR); + if (ob instanceof PdfSimpleObject) { + b = ((PdfSimpleObject) ob).isTrue(); + } else { + b = false; + } + p = new Property(PROP_NAME_HIDE_TOOLBAR, PropertyType.BOOLEAN, Boolean.valueOf(b)); + propList.add(p); + + ob = prefDict.get(DICT_KEY_HIDE_MENUBAR); + if (ob instanceof PdfSimpleObject) { + b = ((PdfSimpleObject) ob).isTrue(); + } else { + b = false; + } + p = new Property(PROP_NAME_HIDE_MENUBAR, PropertyType.BOOLEAN, Boolean.valueOf(b)); + propList.add(p); + + ob = prefDict.get(DICT_KEY_HIDE_WINDOW_UI); + if (ob instanceof PdfSimpleObject) { + b = ((PdfSimpleObject) ob).isTrue(); + } else { + b = false; + } + p = new Property(PROP_NAME_HIDE_WINDOW_UI, PropertyType.BOOLEAN, Boolean.valueOf(b)); + propList.add(p); + + ob = prefDict.get(DICT_KEY_FIT_WINDOW); + if (ob instanceof PdfSimpleObject) { + b = ((PdfSimpleObject) ob).isTrue(); + } else { + b = false; + } + p = new Property(PROP_NAME_FIT_WINDOW, PropertyType.BOOLEAN, Boolean.valueOf(b)); + propList.add(p); + + ob = prefDict.get(DICT_KEY_CENTER_WINDOW); + if (ob instanceof PdfSimpleObject) { + b = ((PdfSimpleObject) ob).isTrue(); + } else { + b = false; + } + p = new Property(PROP_NAME_CENTER_WINDOW, PropertyType.BOOLEAN, Boolean.valueOf(b)); + propList.add(p); + + ob = prefDict.get(DICT_KEY_DISP_DOC_TITLE); + if (ob instanceof PdfSimpleObject) { + b = ((PdfSimpleObject) ob).isTrue(); + } else { + b = false; + } + p = new Property(PROP_NAME_DISP_DOC_TITLE, PropertyType.BOOLEAN, Boolean.valueOf(b)); + propList.add(p); + + ob = prefDict.get(DICT_KEY_NO_FULL_PAGE); + if (ob instanceof PdfSimpleObject) { + s = ((PdfSimpleObject) ob).getStringValue(); + } else s = DEFAULT_MODE; + p = new Property(PROP_NAME_NO_FULL_PAGE, PropertyType.STRING, s); + propList.add(p); + + ob = prefDict.get(DICT_KEY_DIRECTION); + if (ob instanceof PdfSimpleObject) { + s = ((PdfSimpleObject) ob).getStringValue(); + } else s = "L2R"; + p = new Property(PROP_NAME_DIRECTION, PropertyType.STRING, s); + propList.add(p); + + ob = prefDict.get(DICT_KEY_VIEW_AREA); + if (ob instanceof PdfSimpleObject) { + s = ((PdfSimpleObject) ob).getStringValue(); + } else s = PROP_VAL_CROP_BOX; + p = new Property(PROP_NAME_VIEW_AREA, PropertyType.STRING, s); + propList.add(p); + + ob = prefDict.get(DICT_KEY_VIEW_CLIP); + if (ob instanceof PdfSimpleObject) { + s = ((PdfSimpleObject) ob).getStringValue(); + } else s = PROP_VAL_CROP_BOX; + p = new Property(PROP_NAME_VIEW_CLIP, PropertyType.STRING, s); + propList.add(p); + + ob = prefDict.get(DICT_KEY_PRINT_AREA); + if (ob instanceof PdfSimpleObject) { + s = ((PdfSimpleObject) ob).getStringValue(); + } else s = PROP_VAL_CROP_BOX; + p = new Property(PROP_NAME_PRINT_AREA, PropertyType.STRING, s); + propList.add(p); + + ob = prefDict.get(DICT_KEY_PAGE_CLIP); + if (ob instanceof PdfSimpleObject) { + s = ((PdfSimpleObject) ob).getStringValue(); + } else s = PROP_VAL_CROP_BOX; + p = new Property(PROP_NAME_PAGE_CLIP, PropertyType.STRING, s); + propList.add(p); + return prop; + } + + /* + * Return TRUE if the string is a font subset string, which begins + * with six uppercase letters and then a plus sign + */ + protected boolean isFontSubset(String baseStr) { + if (baseStr == null || baseStr.length() < 7) { + return false; + } + for (int i = 0; i < 6; i++) { + char ch = baseStr.charAt(i); + if (!Character.isUpperCase(ch)) { + return false; + } + } + return (baseStr.charAt(6) == '+'); + } + + /* + * Create the "Outlines" property from the Outlines item in the + * catalog dictionary. As a side effect, we set the actionsExist + * flag if any Actions are found. Because we check destinations, + * this can't be called till the page tree is built. + * + * Outlines can be recursive, according to Adobe people, so we have + * to track visited nodes. + */ + protected Property buildOutlinesProperty(PdfDictionary dict, RepInfo info) throws PdfException { + _recursionWarned = false; + _visitedOutlineNodes = new HashSet(); + List itemList = new LinkedList(); + Property prop = + new Property(PROP_NAME_OUTLINES, PropertyType.PROPERTY, PropertyArity.LIST, itemList); + try { + PdfObject item = resolveIndirectObject(dict.get(DICT_KEY_FIRST)); + // In PDF 1.4, "First" and "Last" are unconditionally required. + // However, + // in 1.6, they can be omitted if there are no open or closed + // outline items. + // Strictly speaking, we should do several additional checks, but + // letting the + // outline go as empty seems sufficient. + // if (item == null || !(item instanceof PdfDictionary)) { + // throw new PdfInvalidException("Outline dictionary missing + // required entry"); + // } + int listCount = 0; // Guard against looping + while (item != null) { + Integer onum = new Integer(item.getObjNumber()); + Property p = buildOutlineItemProperty((PdfDictionary) item, info); + itemList.add(p); + item = resolveIndirectObject(((PdfDictionary) item).get(DICT_KEY_NEXT)); + if (item == null) { + break; + } + // Check if this object is its own sibling. (It really does + // happen!) + if (item.getObjNumber() == onum.intValue()) { + if (!_recursionWarned) { + info.setMessage(new InfoMessage(MessageConstants.PDF_HUL_123)); // PDF-HUL-123 + _recursionWarned = true; + } + break; + } + if (++listCount > 2000) { + break; + } + } + } catch (PdfException e1) { + throw e1; + } catch (Exception e) { + throw new PdfMalformedException(MessageConstants.PDF_HUL_124); // PDF-HUL-124 + } + if (itemList.isEmpty()) { + return null; + } + return prop; + } + + /* + * Create an item property within the outlines hierarchy. If an + * Outline item property has children, then there is a list + * property called "Children" with elements called "Item". + * It calls itself recursively to walk down the outline. + */ + protected Property buildOutlineItemProperty(PdfDictionary dict, RepInfo info) + throws PdfException { + List itemList = new ArrayList(3); + try { + Property prop = + new Property(PROP_NAME_ITEM, PropertyType.PROPERTY, PropertyArity.LIST, itemList); + PdfSimpleObject title = (PdfSimpleObject) resolveIndirectObject(dict.get(DICT_KEY_TITLE)); + if (title == null) { + throw new PdfInvalidException(MessageConstants.PDF_HUL_125); // PDF-HUL-125 + } + itemList.add( + new Property( + PROP_NAME_TITLE, + PropertyType.STRING, + _encrypted ? ENCRYPTED : title.getStringValue())); + + // Check other required stuff + if (dict.get(DICT_KEY_PARENT) == null) { + throw new PdfInvalidException(MessageConstants.PDF_HUL_126); // PDF-HUL-126 + } + PdfObject cnt = dict.get(DICT_KEY_COUNT); + if (cnt != null + && (!(cnt instanceof PdfSimpleObject) + || !(((PdfSimpleObject) cnt).getToken() instanceof Numeric))) { + throw new PdfInvalidException(MessageConstants.PDF_HUL_127); // PDF-HUL-127 + } + // The entries for Prev, Next, First, and Last must + // all be indirect references or absent. Just cast them to + // throw an exception if they're something else + @SuppressWarnings("unused") + PdfIndirectObj ob = (PdfIndirectObj) dict.get(DICT_KEY_PREV); + ob = (PdfIndirectObj) dict.get(DICT_KEY_NEXT); + ob = (PdfIndirectObj) dict.get(DICT_KEY_FIRST); + ob = (PdfIndirectObj) dict.get(DICT_KEY_LAST); + + // Check if there are Actions in the outline. This saves going + // through the outlines all over again if a Profile checker + // needs to know this. We flag only the existence of one or more + // Actions + // in the document. + if (dict.get("A") != null) { + _actionsExist = true; + } + + PdfObject destObj = dict.get(DICT_KEY_DEST); + if (destObj != null) { + destObj = resolveIndirectObject(destObj); + Destination dest = new Destination(destObj, this, false); + if (dest.isIndirect()) { + itemList.add( + new Property( + PROP_NAME_DESTINATION, + PropertyType.STRING, + dest.getIndirectDest().getStringValue())); + } else { + int pageObjNum = dest.getPageDestObjNumber(); + Integer destPg = _pageSeqMap.get(new Integer(pageObjNum)); + if (destPg != null) { + itemList.add(new Property(PROP_NAME_DESTINATION, PropertyType.INTEGER, destPg)); + } + } + } + + PdfDictionary child = (PdfDictionary) resolveIndirectObject(dict.get(DICT_KEY_FIRST)); + if (child != null) { + List childList = new LinkedList(); + Property childProp = + new Property(PROP_NAME_CHILDREN, PropertyType.PROPERTY, PropertyArity.LIST, childList); + // We aren't catching all possible combinations of looping. Put + // a maximum + // on the list just to be safe. + int listCount = 0; + while (child != null) { + Integer onum = new Integer(child.getObjNumber()); + if (_visitedOutlineNodes.contains(onum)) { + /* We have recursion! */ + if (!_recursionWarned) { + // Warn of recursion + info.setMessage(new InfoMessage(MessageConstants.PDF_HUL_128)); // PDF-HUL-128 + _recursionWarned = true; + } + } else { + _visitedOutlineNodes.add(onum); + Property p = buildOutlineItemProperty(child, info); + childList.add(p); + } + child = (PdfDictionary) resolveIndirectObject(child.get(DICT_KEY_NEXT)); + if (child == null) { + break; + } + // Check if this object is its own sibling. (It really does + // happen!) + if (child.getObjNumber() == onum.intValue()) { + if (!_recursionWarned) { + info.setMessage(new InfoMessage(MessageConstants.PDF_HUL_129)); // PDF-HUL-129 + _recursionWarned = true; + } + break; + } + if (++listCount > 2000) break; // safety check + } + itemList.add(childProp); + } + return prop; + } catch (PdfException pe) { + throw pe; + } catch (ClassCastException ce) { + throw new PdfInvalidException(MessageConstants.PDF_HUL_130); // PDF-HUL-130 + } catch (Exception e) { + throw new PdfInvalidException(MessageConstants.PDF_HUL_131); // PDF-HUL-131 + } + } + + /* + * This is separated out from readDocCatalogDict, where it + * would otherwise make sense, because we can't build + * the outlines property till we have a page tree to + * locate destinations. + */ + protected boolean doOutlineStuff(RepInfo info) { + if (_outlineDict != null) { + try { + Property oprop = buildOutlinesProperty(_outlineDict, info); + if (_showOutlines || _verbosity == Module.MAXIMUM_VERBOSITY) { + if (oprop != null) { + _docCatalogList.add(oprop); + } + } else if (!_skippedOutlinesReported) { + // We report that we aren't reporting skipped outlines + info.setMessage(new InfoMessage(MessageConstants.PDF_HUL_132)); // PDF-HUL-132 + _skippedOutlinesReported = true; + } + } catch (PdfException e) { + if (e.getJhoveMessage() != null) + info.setMessage(new ErrorMessage(e.getJhoveMessage(), _parser.getOffset())); + else info.setMessage(new ErrorMessage(e.getMessage(), _parser.getOffset())); + e.disparage(info); + // If it's just invalid, we can keep going + return (e instanceof PdfInvalidException); + } + } + return true; + } + + /* + * Given a PdfSimpleObject representing a key, + * look up the Destination which it references. + * There are two completely different ways this can be done, + * though any given PDF file is supposed to implement only one. + * If _destsDict is non-null, we look the string up there, and + * may find either a dictionary or an array. Otherwise + * if _destNames is non-null, it's a NameTreeNode which contains + * the mapping. In either case, the destination could be + * external, in which case we just return a string saying so. + * (The implementation of Destinations in PDF is a prime example + * of design by stone soup.) + * We return the page sequence number for the referenced page. + * If we can't find a match for the reference, we return -1. + */ + protected int resolveIndirectDest(PdfSimpleObject key, RepInfo info) throws PdfException { + if (key == null) { + throw new IllegalArgumentException("Argument key can not be null"); + } + _logger.finest("Looking for indirectly referenced Dest: " + key.getStringValue()); + if (_destNames == null) return -1; + PdfObject destObj = _destNames.get(key.getRawBytes()); + // Was the Dest this annotation refers to found in the document? + if (destObj == null) { + // Treat this condition as invalid: + String mess = + MessageFormat.format(MessageConstants.PDF_HUL_149.getMessage(), key.getStringValue()); + JhoveMessage message = + JhoveMessages.getMessageInstance(MessageConstants.PDF_HUL_149.getId(), mess); + info.setMessage(new ErrorMessage(message)); + throw new PdfInvalidException(message); // PDF-HUL-149 + // OR if this is not considered invalid + // return -1; + } + Destination dest = new Destination(destObj, this, true); + return dest.getPageDestObjNumber(); + } + + /* Build the user permission property., */ + protected Property buildUserPermProperty(int flags, String[] flagStrs) { + return buildBitmaskProperty(flags, "UserAccess", flagStrs, "No permissions"); + } + + /** + * Add a string property, based on a dictionary entry with a string value, to a specified List. + */ + protected void addStringProperty( + PdfDictionary dict, List propList, String key, String propName) { + String propText = null; + PdfObject propObject = dict.get(key); + if (propObject instanceof PdfSimpleObject) { + Token tok = ((PdfSimpleObject) propObject).getToken(); + if (tok instanceof Literal) { + if (_encrypted) { + propText = ENCRYPTED; + } else { + propText = ((Literal) tok).getValue(); + } + propList.add(new Property(propName, PropertyType.STRING, propText)); + } + } + } + + /** Add a date property, based on a dictionary entry with a string value, to a specified List. */ + protected void addDateProperty( + PdfDictionary dict, List propList, String key, String propName) + throws PdfException { + if (_encrypted) { + return; // can't decipher an encrypted date + } + PdfObject propObject = dict.get(key); + if (propObject instanceof PdfSimpleObject) { + Token tok = ((PdfSimpleObject) propObject).getToken(); + if (tok instanceof Literal) { + Literal lit = (Literal) tok; + Date propDate = lit.parseDate(); + if (propDate != null) { + propList.add(new Property(propName, PropertyType.DATE, propDate)); + // Ignore empty literals as this isn't an error + } else if (!lit.getValue().isEmpty()) { + throw new PdfInvalidException(MessageConstants.PDF_HUL_133, 0); // PDF-HUL-133 + } + } + } + } + + /* + * General function for adding a property with a 32-bit + * value, with an array of Strings to interpret + * the value as a bitmask. + */ + protected Property buildBitmaskProperty( + int val, String name, String[] valueNames, String defaultStr) { + if (_je != null && _je.getShowRawFlag()) { + return new Property(name, PropertyType.INTEGER, new Integer(val)); + } + List slist = new LinkedList(); + try { + for (int i = 0; i < valueNames.length; i++) { + if ((val & (1 << i)) != 0 && valueNames[i].length() > 0) { + slist.add(valueNames[i]); + } + } + // Provision for a default string if the property + // would otherwise have an empty list + if (slist.isEmpty() && defaultStr != null) { + slist.add(defaultStr); + } + } catch (Exception e) { + return null; + } + return new Property(name, PropertyType.STRING, PropertyArity.LIST, slist); + } + + /* + * Take a PdfArray which is supposed to conform to the rectangle + * description (i.e., it's an array of 4 numbers) and create + * a Property which is an array of 4 integers. + */ + protected Property makeRectProperty(PdfArray arrObj, String name) { + int[] iarr = new int[4]; + double[] arr = arrObj.toRectangle(); + // toRectangle is written to return an array of double, + // which is what the bounding box is in the most general + // case; but the spec requires an array of integer, so + // we convert it. This may seem like an excess of work, + // but I'd rather have toRectangle do the right thing + // rather than losing generality. + for (int i = 0; i < 4; i++) { + iarr[i] = (int) arr[i]; + } + return new Property(name, PropertyType.INTEGER, PropertyArity.ARRAY, iarr); + } + + private static boolean checkTypeKey( + final PdfDictionary dict, + final RepInfo info, + final String expctVal, + final JhoveMessage typeInvalMess, + final JhoveMessage typeNotFoundMess, + final JhoveMessage typeNotSimpleMess) { + // Get the type key from the dictionary + PdfObject typeObj = dict.get(DICT_KEY_TYPE); + if (typeObj != null && typeObj instanceof PdfSimpleObject) { + // If the type key is not null and is a simple object + String typeValue = ((PdfSimpleObject) typeObj).getStringValue(); + if (!expctVal.equals(typeValue)) { + // If the type key value is not of the expected value + info.setWellFormed(false); + info.setMessage(new ErrorMessage(typeInvalMess, 0)); + return false; + } + } else { + // There's no type key or it's not a simple object + // Choose message depending on whether the value is null or of + // the wrong type + JhoveMessage message = (typeObj == null) ? typeNotFoundMess : typeNotSimpleMess; + info.setMessage(new ErrorMessage(message, 0)); + info.setWellFormed(false); + return false; + } + return true; + } + + private static String imageMimeFromFilters(Filter[] filters) { + // If there's no filters it's a PNG + if (filters == null || filters.length == 0) { + return "image/png"; + } + // Iterate the filter list + for (Filter filt : filters) { + // Get the Filter name + String filterName = filt.getFilterName(); + // And the MIME type from htat + String mime = imageMimeFromFilterName(filterName); + if (mime != null) { + // If it's not null then return + return mime; + } + // Next filter + } + // No MIME type match made for filter list + return null; + } + + // Stolen from an Apache PDF Box method: + // https://github.com/apache/pdfbox/blob/2.0/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/graphics/image/PDImageXObject.java#L767 + private static String imageMimeFromFilterName(final String filterName) { + if (FILTER_NAME_DCT.equals(filterName)) { + // DCTDecode is JPEG + return "image/jpg"; + } else if (FILTER_NAME_JPX.equals(filterName)) { + // JPX Decode for JPX (JP2K) + return "image/jpx"; + } else if (FILTER_NAME_CCITT.equals(filterName)) { + // CCITT is a TIFF image + return "image/tiff"; + } else if (FILTER_NAME_FLATE.equals(filterName) + || FILTER_NAME_LZW.equals(filterName) + || FILTER_NAME_RUN_LENGTH.equals(filterName)) { + // There's a bunch of PNG possibilities + return "image/png"; + } + // No match made + return null; + } + + private PdfObject getObjectFromStream(final int objIndex, final int recGuard) + throws PdfMalformedException { + /* + * The object is located in an object stream. Need to get the + * object stream first. + * Be cautious dealing with _cachedStreamIndex and _cachedObjectStream; + * these can be modified by a recursive call to getObject. + */ + try { + int objStreamIndex = _xref2[objIndex][0]; + PdfObject streamObj; + ObjectStream ostrm = null; + if (objStreamIndex == _cachedStreamIndex) { + ostrm = _cachedObjectStream; + // Reset it + if (ostrm.isValid()) { + ostrm.readIndex(); + } + } else { + streamObj = resolveIndirectObject(getObject(objStreamIndex, recGuard - 1)); + if (streamObj instanceof PdfStream) { + ostrm = new ObjectStream((PdfStream) streamObj, _raf); + if (ostrm.isValid()) { + ostrm.readIndex(); + _cachedObjectStream = ostrm; + _cachedStreamIndex = objStreamIndex; + } else { + throw new PdfMalformedException(MessageConstants.PDF_HUL_108); // PDF-HUL-108 + } + } + } + /* And finally extract the object from the object stream. */ + return ostrm.getObject(objIndex); + } catch (ZipException excep) { + _logger.info(excep.getMessage()); + throw new PdfMalformedException(MessageConstants.PDF_HUL_109); // PDF-HUL-109 + } catch (Exception e) { + _logger.info(e.getMessage()); + /* Fall through with error */ + } + throw new PdfMalformedException(MessageConstants.PDF_HUL_110); // PDF-HUL-110 + } } diff --git a/jhove-modules/pdf-hul/src/main/java/edu/harvard/hul/ois/jhove/module/pdf/AProfile.java b/jhove-modules/pdf-hul/src/main/java/edu/harvard/hul/ois/jhove/module/pdf/AProfile.java index 472ff8984..b2f4e049c 100644 --- a/jhove-modules/pdf-hul/src/main/java/edu/harvard/hul/ois/jhove/module/pdf/AProfile.java +++ b/jhove-modules/pdf-hul/src/main/java/edu/harvard/hul/ois/jhove/module/pdf/AProfile.java @@ -1,1160 +1,1053 @@ -/********************************************************************** - * Jhove - JSTOR/Harvard Object Validation Environment - * Copyright 2003 by JSTOR and the President and Fellows of Harvard College - **********************************************************************/ - - +/** + * ******************************************************************** Jhove - JSTOR/Harvard Object + * Validation Environment Copyright 2003 by JSTOR and the President and Fellows of Harvard College + * ******************************************************************** + */ package edu.harvard.hul.ois.jhove.module.pdf; import edu.harvard.hul.ois.jhove.*; import edu.harvard.hul.ois.jhove.module.*; import java.util.*; -import org.xml.sax.*; import javax.xml.parsers.*; +import org.xml.sax.*; /** - * PDF profile checker for PDF/A-1 documents. - * See 19005-1:2005(E), "Document Imaging Applications - * Application Issues". + * PDF profile checker for PDF/A-1 documents. See 19005-1:2005(E), "Document Imaging Applications + * Application Issues". + * + *

Revised to reflect the final standard. With the new terminology, this profile is specific to + * PDF/A-1; there may be additional standards in the PDF/A family later on. "PDF/A" means "PDF/A-1" + * in the documentation of this code. * - * Revised to reflect the final standard. With the new - * terminology, this profile is specific to PDF/A-1; there may be - * additional standards in the PDF/A family later on. "PDF/A" - * means "PDF/A-1" in the documentation of this code. - * - * There are two levels of conformance, called Level A and Level B. - * We report these as two different profiles. To accomplish this, - * we use AProfileLevelA, linked to an instance of this, which - * simply checks if this profile established Level A compliance. + *

There are two levels of conformance, called Level A and Level B. We report these as two + * different profiles. To accomplish this, we use AProfileLevelA, linked to an instance of this, + * which simply checks if this profile established Level A compliance. */ -public final class AProfile extends PdfProfile -{ - /****************************************************************** - * PRIVATE CLASS FIELDS. - ******************************************************************/ +public final class AProfile extends PdfProfile { + /** + * **************************************************************** PRIVATE CLASS FIELDS. + * **************************************************************** + */ - /* TaggedProfile to which this profile is linked. */ - private TaggedProfile _taggedProfile; - private boolean _levelA; - private boolean hasDevRGB; - private boolean hasDevCMYK; - private boolean hasUncalCS; // flag for DeviceGray, DeviceCMYK or DeviceRGB - /* Allowable annotation types. Movie, Sound and FileAttachment - are allowed in PDF, but not in PDF/A. */ - private String[] annotTypes = { - "Text", "Link", "FreeText", "Line", "Square", "Circle", - "Polygon", "Polyline", "Highlight", "Underline", - "Squiggly", "StrikeOut", "Stamp", "Caret", - "Ink", "Popup", "Widget", "Screen", - "PrinterMark", "TrapNet" - }; - - /* The following are the annotation types which are considered - non-text annotations. */ -// private String[] nonTextAnnotTypes = { -// "Link", "Line", "Square", "Circle", -// "Polygon", "Polyline", "Stamp", "Caret", -// "Ink", "Popup", "Widget", "Screen", -// "PrinterMark", "TrapNet" -// }; - - private String[] excludedActions = { - "Launch", "Sound", "Movie", "ResetForm", - "ImportData", "JavaScript", "set-state", "no-op" - }; - - /* The following filters are not allowed */ - private String[] excludedFilters = { - /*"ASCIIHexDecode", "ASCII85Decode",*/ "LZWDecode" - }; + /* TaggedProfile to which this profile is linked. */ + private TaggedProfile _taggedProfile; + private boolean _levelA; + private boolean hasDevRGB; + private boolean hasDevCMYK; + private boolean hasUncalCS; // flag for DeviceGray, DeviceCMYK or DeviceRGB + /* Allowable annotation types. Movie, Sound and FileAttachment + are allowed in PDF, but not in PDF/A. */ + private String[] annotTypes = { + "Text", + "Link", + "FreeText", + "Line", + "Square", + "Circle", + "Polygon", + "Polyline", + "Highlight", + "Underline", + "Squiggly", + "StrikeOut", + "Stamp", + "Caret", + "Ink", + "Popup", + "Widget", + "Screen", + "PrinterMark", + "TrapNet" + }; + /* The following are the annotation types which are considered + non-text annotations. */ + // private String[] nonTextAnnotTypes = { + // "Link", "Line", "Square", "Circle", + // "Polygon", "Polyline", "Stamp", "Caret", + // "Ink", "Popup", "Widget", "Screen", + // "PrinterMark", "TrapNet" + // }; - /** - * Constructor. - * Creates an AProfile object for subsequent testing. - * - * @param module The module under which we are checking the profile. - * - */ - public AProfile (PdfModule module) - { - super (module); - _profileText = "ISO PDF/A-1, Level B"; + private String[] excludedActions = { + "Launch", "Sound", "Movie", "ResetForm", + "ImportData", "JavaScript", "set-state", "no-op" + }; + + /* The following filters are not allowed */ + private String[] excludedFilters = { + /*"ASCIIHexDecode", "ASCII85Decode",*/ + "LZWDecode" + }; + + /** + * Constructor. Creates an AProfile object for subsequent testing. + * + * @param module The module under which we are checking the profile. + */ + public AProfile(PdfModule module) { + super(module); + _profileText = "ISO PDF/A-1, Level B"; + } + + /** Calling setTaggedProfile links this AProfile to a TaggedProfile. */ + public void setTaggedProfile(TaggedProfile tpr) { + _taggedProfile = tpr; + } + + /** + * Returns true if the document satisfies the profile at Level B or better. Also sets + * the level A flag to the appropriate value, so that satisfiesLevelA can + * subsequently be called. + */ + @Override + public boolean satisfiesThisProfile() { + // Assume level A compliance. + _levelA = true; + // The module has already done some syntactic checks. + // If those failed, the file isn't compliant. + if (!_module.mayBePDFACompliant()) { + _levelA = false; + return false; } - /** - * Calling setTaggedProfile links this AProfile to a TaggedProfile. - * - */ - public void setTaggedProfile (TaggedProfile tpr) - { - _taggedProfile = tpr; + // Conforming to the TaggedProfile requirements is necessary + // for Level A + if (_taggedProfile != null && !_taggedProfile.isAlreadyOK()) { + _levelA = false; + // But it may still be Level B } - /** - * Returns true if the document satisfies the profile - * at Level B or better. Also sets the level A flag to the - * appropriate value, so that satisfiesLevelA can subsequently - * be called. - * - */ - @Override - public boolean satisfiesThisProfile () - { - // Assume level A compliance. - _levelA = true; - // The module has already done some syntactic checks. - // If those failed, the file isn't compliant. - if (!_module.mayBePDFACompliant ()) { - _levelA = false; - return false; - } + hasDevCMYK = false; + hasDevRGB = false; + hasUncalCS = false; - // Conforming to the TaggedProfile requirements is necessary - // for Level A - if (_taggedProfile != null && - !_taggedProfile.isAlreadyOK ()) { - _levelA = false; - // But it may still be Level B - } + try { + // Encryption dictionary is not allowed. + if (_module.getEncryptionDict() != null + || !trailerDictOK() + || !catalogOK() + || !resourcesOK() + || !fontsOK() + || !outlinesOK()) { + _levelA = false; + return false; + } + } catch (Exception e) { + _levelA = false; + return false; + } - hasDevCMYK = false; - hasDevRGB = false; - hasUncalCS = false; + return true; // Passed all tests + } - try { - // Encryption dictionary is not allowed. - if (_module.getEncryptionDict () != null || - !trailerDictOK () || - !catalogOK () || - !resourcesOK () || - !fontsOK () || - !outlinesOK()) { - _levelA = false; - return false; - } - } - catch (Exception e) { - _levelA = false; - return false; - } + /** + * Returns true if the document was found to be Level A conformant. This returns a meaningful + * result only after satisfiesThisProfile has been called, and is intended for use by + * the Level A profiler. + */ + protected boolean satisfiesLevelA() { + return _levelA; + } - return true; // Passed all tests - } - - /** Returns true if the document was found to be Level A - * conformant. This returns a meaningful result only after - * satisfiesThisProfile has been called, and - * is intended for use by the Level A profiler. */ - protected boolean satisfiesLevelA () - { - return _levelA; + /* The Encrypt and Info entries aren't allowed in the trailer + dictionary. The ID entry is required. */ + private boolean trailerDictOK() { + PdfDictionary trailerDict = _module.getTrailerDict(); + if (trailerDict == null) { + return false; // really shouldn't happen } - - /* The Encrypt and Info entries aren't allowed in the trailer - dictionary. The ID entry is required. */ - private boolean trailerDictOK () - { - PdfDictionary trailerDict = _module.getTrailerDict (); - if (trailerDict == null) { - return false; // really shouldn't happen - } - try { - if (trailerDict.get ("Encrypt") != null/* || + try { + if (trailerDict.get("Encrypt") != null /* || trailerDict.get ("Info") != null*/) { - return false; - } - if (trailerDict.get ("ID") == null) { - return false; - } - } - catch (Exception e) { - return false; - } - return true; + return false; + } + if (trailerDict.get("ID") == null) { + return false; + } + } catch (Exception e) { + return false; } + return true; + } - private boolean catalogOK () - { - PdfDictionary cat = _module.getCatalogDict (); - if (cat == null) { - return false; + private boolean catalogOK() { + PdfDictionary cat = _module.getCatalogDict(); + if (cat == null) { + return false; + } + try { + // The document catalog dictionary "should" be present. + // If it does, the value "shall" contain + // a valid RFC1766 language string. + PdfSimpleObject lang = (PdfSimpleObject) cat.get("Lang"); + if (lang != null) { + RFC1766Lang l = new RFC1766Lang(lang.getStringValue()); + if (!l.isSyntaxCorrect()) { + return false; } - try { - // The document catalog dictionary "should" be present. - // If it does, the value "shall" contain - // a valid RFC1766 language string. - PdfSimpleObject lang = (PdfSimpleObject) cat.get ("Lang"); - if (lang != null) { - RFC1766Lang l = new RFC1766Lang (lang.getStringValue ()); - if (!l.isSyntaxCorrect ()) { - return false; - } - } - - // It must have an unfiltered Metadata stream - PdfStream metadata = (PdfStream) - _module.resolveIndirectObject (cat.get ("Metadata")); - if (!metadataOK (metadata)) { - return false; - } + } - // If it has an interactive form, it must meet certain criteria - PdfDictionary form = (PdfDictionary) - _module.resolveIndirectObject (cat.get ("AcroForm")); - if (form != null && !formOK (form)) { - return false; - } + // It must have an unfiltered Metadata stream + PdfStream metadata = (PdfStream) _module.resolveIndirectObject(cat.get("Metadata")); + if (!metadataOK(metadata)) { + return false; + } - // It may not contain an AA entry or an OCProperties entry - if (cat.get ("AA") != null || - cat.get ("OCProperties") != null) { - return false; - } - } - catch (Exception e) { + // If it has an interactive form, it must meet certain criteria + PdfDictionary form = (PdfDictionary) _module.resolveIndirectObject(cat.get("AcroForm")); + if (form != null && !formOK(form)) { + return false; + } + + // It may not contain an AA entry or an OCProperties entry + if (cat.get("AA") != null || cat.get("OCProperties") != null) { + return false; + } + } catch (Exception e) { + return false; + } + return true; + } + + private boolean fontsOK() { + if (!type0FontsOK()) { + return false; + } + // For each type of font (just because that's the easiest way + // to get the fonts from the PdfModule), check that each font + // has a ToUnicode entry which is a CMap stream. + List> lst = _module.getFontMaps(); + Iterator> iter = lst.listIterator(); + try { + while (iter.hasNext()) { + Map fmap = iter.next(); + Iterator iter1 = fmap.values().iterator(); + while (iter1.hasNext()) { + PdfDictionary font = (PdfDictionary) iter1.next(); + if (!fontOK(font)) { return false; + } } - return true; + } + } catch (Exception e) { + return false; } + return true; + } - - private boolean fontsOK () - { - if (!type0FontsOK ()) { - return false; + /* Check a font for validity */ + private boolean fontOK(PdfDictionary font) { + try { + // The ToUnicode entry is required only for Level A, + // and there are an assortment of exceptions. + PdfSimpleObject fType = (PdfSimpleObject) font.get("Subtype"); + String fTypeStr = fType.getStringValue(); + PdfDictionary desc = + (PdfDictionary) _module.resolveIndirectObject(font.get("FontDescriptor")); + // MODIF THL 2010/10/11 test if desc is null + PdfSimpleObject flagsObj = null; + if (desc != null) { + flagsObj = (PdfSimpleObject) _module.resolveIndirectObject(desc.get("Flags")); + } + int flags = 0; + if (flagsObj != null) { + flags = flagsObj.getIntValue(); + } + if ("Type1".equals(fTypeStr)) { + // A Type 1 font must have a CharSet string in the + // font descriptor dictionary. + if (desc == null) { + return false; // The requirement mentioned above implies a FontDescriptor is needed. } - // For each type of font (just because that's the easiest way - // to get the fonts from the PdfModule), check that each font - // has a ToUnicode entry which is a CMap stream. - List> lst = _module.getFontMaps (); - Iterator> iter = lst.listIterator (); - try { - while (iter.hasNext ()) { - Map fmap = iter.next (); - Iterator iter1 = fmap.values ().iterator (); - while (iter1.hasNext ()) { - PdfDictionary font = (PdfDictionary) iter1.next (); - if (!fontOK (font)) { - return false; - } - } + return desc.get("CharSet") != null; + } + if ("Type0".equals(fTypeStr)) { + // Type 0 fonts are OK if the descendant CIDFont uses + // four specified character collections. + PdfObject order = font.get("Ordering"); + if (order instanceof PdfSimpleObject) { + try { + String ordText = ((PdfSimpleObject) order).getStringValue(); + if ("Adobe-GB1".equals(ordText) + || "Adobe-CNS1".equals(ordText) + || "Adobe-Japan1".equals(ordText) + || "Adobe-Korea1".equals(ordText)) { + return true; } + } catch (Exception e) { + } } - catch (Exception e) { - return false; + } + PdfObject enc = font.get("Encoding"); + if (enc instanceof PdfSimpleObject) { + if ((flags & 0X04) != 0) { // symbolic font? + return false; // symbolic font must not have encoding + } + String encName = ((PdfSimpleObject) enc).getStringValue(); + if ("WinAnsiEncoding".equals(encName) + || "MacRomanDecoding".equals(encName) + || "MacExpertDecoding".equals(encName)) { + return true; } - return true; + } + /* + * Fixed contributed by FCLA, 2007-05-30, to permit + * indirect as well as direct stream object. + * + * PdfStream toUni = (PdfStream) font.get ("ToUnicode"); + */ + PdfObject toUni = font.get("ToUnicode"); + if (toUni == null) { + _levelA = false; + } + } catch (Exception e) { + return false; } + return true; + } + /* Check the type 0 font map for compatibility with + CIDFont and CMap dictionaries */ + private boolean type0FontsOK() { + Map type0Map = _module.getFontMap(PdfModule.F_TYPE0); + if (type0Map == null) { + return true; + } + try { + PdfSimpleObject ob; + Iterator iter = type0Map.values().iterator(); + while (iter.hasNext()) { + String registry = null; + String ordering = null; + PdfDictionary font = (PdfDictionary) iter.next(); + // The Encoding entry can be a predefined name + // or a dictionary. If it's a dictionary, it + // must be compatible with the CIDSystemInfo + // dictionaries. + PdfObject enc = font.get("Encoding"); + if (enc instanceof PdfDictionary) { + // it's a CMap dictionary. + PdfDictionary info = + (PdfDictionary) + _module.resolveIndirectObject(((PdfDictionary) enc).get("CIDSystemInfo")); + ob = (PdfSimpleObject) info.get("Registry"); + registry = ob.getStringValue(); + ob = (PdfSimpleObject) info.get("Ordering"); + ordering = ob.getStringValue(); + } + PdfArray descendants = + (PdfArray) _module.resolveIndirectObject(font.get("DescendantFonts")); + // PDF 1.4 and previous allow only a single + // descendant font, and this must be a CIDFont. + // While Adobe warns that this may change in a + // previous version, we require here that the + // first descendant be a CIDFont, and ignore any others. + Vector subfonts = descendants.getContent(); - /* Check a font for validity */ - private boolean fontOK (PdfDictionary font) - { - try { - // The ToUnicode entry is required only for Level A, - // and there are an assortment of exceptions. - PdfSimpleObject fType = (PdfSimpleObject) font.get("Subtype"); - String fTypeStr = fType.getStringValue (); - PdfDictionary desc = (PdfDictionary) - _module.resolveIndirectObject (font.get ("FontDescriptor")); - // MODIF THL 2010/10/11 test if desc is null - PdfSimpleObject flagsObj = null; - if (desc != null) { - flagsObj = (PdfSimpleObject) _module.resolveIndirectObject( desc.get ("Flags")); - } - int flags = 0; - if (flagsObj != null) { - flags = flagsObj.getIntValue(); - } - if ("Type1".equals (fTypeStr)) { - // A Type 1 font must have a CharSet string in the - // font descriptor dictionary. - if (desc == null) { - return false; // The requirement mentioned above implies a FontDescriptor is needed. - } - return desc.get ("CharSet") != null; - } - if ("Type0".equals (fTypeStr)) { - // Type 0 fonts are OK if the descendant CIDFont uses - // four specified character collections. - PdfObject order = font.get ("Ordering"); - if (order instanceof PdfSimpleObject) { - try { - String ordText = - ((PdfSimpleObject) order).getStringValue (); - if ("Adobe-GB1".equals (ordText) || - "Adobe-CNS1".equals (ordText) || - "Adobe-Japan1".equals (ordText) || - "Adobe-Korea1".equals (ordText)) { - return true; - } - } - catch (Exception e) {} - } - } - PdfObject enc = font.get ("Encoding"); - if (enc instanceof PdfSimpleObject) { - if ((flags & 0X04) != 0) { // symbolic font? - return false; // symbolic font must not have encoding - } - String encName = ((PdfSimpleObject) enc).getStringValue (); - if ("WinAnsiEncoding".equals (encName) || - "MacRomanDecoding".equals (encName) || - "MacExpertDecoding".equals (encName)) { - return true; - } - } - /* - * Fixed contributed by FCLA, 2007-05-30, to permit - * indirect as well as direct stream object. - * - * PdfStream toUni = (PdfStream) font.get ("ToUnicode"); - */ - PdfObject toUni = font.get ("ToUnicode"); - if (toUni == null) { - _levelA = false; - } + /* + * Fix contributed by FCLA, 2007-05-30, to permit the + * subfonts array to store PdfObject as well as + * PdfDictionary. + * + * PdfDictionary subfont = + * (PdfDictionary) subfonts.elementAt (0); + * subfont = (PdfDictionary) + * _module.resolveIndirectObject (subfont); + */ + PdfObject objFont = subfonts.elementAt(0); + PdfDictionary subfont = (PdfDictionary) _module.resolveIndirectObject(objFont); + PdfSimpleObject subtype = (PdfSimpleObject) subfont.get("Subtype"); + /* + * Fix conributed by FCLA, 2007-05-30, to permit the + * comparison of a general PdfSimpleObject to a string. + * + * if (!"CIDFontType0".equals (subtype) && + * !"CIDFontType2".equals (subtype)) { + */ + if (!subtype.getStringValue().equals("CIDFontType0") + && !subtype.getStringValue().equals("CIDFontType2")) { + return false; } - catch (Exception e) { + // If there's no CMap dictionary and this is the + // first subfont, save the registration and + // ordering strings. Otherwise make sure they match. + PdfDictionary info = + (PdfDictionary) _module.resolveIndirectObject(subfont.get("CIDSystemInfo")); + ob = (PdfSimpleObject) info.get("Registry"); + String obstr = ob.getStringValue(); + if (registry == null) { + registry = obstr; + } else { + if (!registry.equals(obstr)) { return false; + } } - return true; - } - - - /* Check the type 0 font map for compatibility with - CIDFont and CMap dictionaries */ - private boolean type0FontsOK () - { - Map type0Map = _module.getFontMap (PdfModule.F_TYPE0); - if (type0Map == null) { - return true; + ob = (PdfSimpleObject) info.get("Ordering"); + obstr = ob.getStringValue(); + /* Fix contributed by FCLA, 2007-05-30, to fix an apparent + * typo. + * + * if (registry == null) { + */ + if (ordering == null) { + ordering = obstr; + } else { + if (!ordering.equals(obstr)) { + return false; + } } - try { - PdfSimpleObject ob; - Iterator iter = type0Map.values().iterator (); - while (iter.hasNext ()) { - String registry = null; - String ordering = null; - PdfDictionary font = (PdfDictionary) iter.next (); - // The Encoding entry can be a predefined name - // or a dictionary. If it's a dictionary, it - // must be compatible with the CIDSystemInfo - // dictionaries. - PdfObject enc = font.get ("Encoding"); - if (enc instanceof PdfDictionary) { - // it's a CMap dictionary. - PdfDictionary info = - (PdfDictionary) _module.resolveIndirectObject - (((PdfDictionary) enc).get ("CIDSystemInfo")); - ob = (PdfSimpleObject) info.get ("Registry"); - registry = ob.getStringValue (); - ob = (PdfSimpleObject) info.get ("Ordering"); - ordering = ob.getStringValue (); - } - PdfArray descendants = - (PdfArray) _module.resolveIndirectObject - (font.get ("DescendantFonts")); - // PDF 1.4 and previous allow only a single - // descendant font, and this must be a CIDFont. - // While Adobe warns that this may change in a - // previous version, we require here that the - // first descendant be a CIDFont, and ignore any others. - Vector subfonts = descendants.getContent (); - - /* - * Fix contributed by FCLA, 2007-05-30, to permit the - * subfonts array to store PdfObject as well as - * PdfDictionary. - * - * PdfDictionary subfont = - * (PdfDictionary) subfonts.elementAt (0); - * subfont = (PdfDictionary) - * _module.resolveIndirectObject (subfont); - */ - PdfObject objFont = subfonts.elementAt (0); - PdfDictionary subfont = (PdfDictionary) - _module.resolveIndirectObject (objFont); - PdfSimpleObject subtype = - (PdfSimpleObject) subfont.get ("Subtype"); - /* - * Fix conributed by FCLA, 2007-05-30, to permit the - * comparison of a general PdfSimpleObject to a string. - * - * if (!"CIDFontType0".equals (subtype) && - * !"CIDFontType2".equals (subtype)) { - */ - if (!subtype.getStringValue ().equals ("CIDFontType0") && - !subtype.getStringValue ().equals ("CIDFontType2")) { - return false; - } - // If there's no CMap dictionary and this is the - // first subfont, save the registration and - // ordering strings. Otherwise make sure they match. - PdfDictionary info = - (PdfDictionary) _module.resolveIndirectObject - (subfont.get ("CIDSystemInfo")); - ob = (PdfSimpleObject) info.get ("Registry"); - String obstr = ob.getStringValue (); - if (registry == null) { - registry = obstr; - } - else { - if (!registry.equals (obstr)) { - return false; - } - } - ob = (PdfSimpleObject) info.get ("Ordering"); - obstr = ob.getStringValue (); - /* Fix contributed by FCLA, 2007-05-30, to fix an apparent - * typo. - * - * if (registry == null) { - */ - if (ordering == null) { - ordering = obstr; - } - else { - if (!ordering.equals (obstr)) { - return false; - } - } - // A type 2 subfont must meet certain restrictions - if ("CIDFontType2".equals (subtype.getStringValue())) { - // MODIF THL 2010/10/11 Accept IndirectObject - PdfObject cgmap1 = subfont.get ("CIDToGIDMap"); - if (cgmap1 == null) { - return false; - } - PdfObject cgmap; - if (cgmap1 instanceof PdfIndirectObj) { - cgmap = - (PdfObject) _module.resolveIndirectObject (cgmap1); - } else { - cgmap = cgmap1; - } - if (cgmap instanceof PdfSimpleObject) { - if (!"Identity".equals (((PdfSimpleObject)cgmap).getStringValue ())) { - return false; - } - } - else if (!(cgmap instanceof PdfStream)) { - return false; - } - } + // A type 2 subfont must meet certain restrictions + if ("CIDFontType2".equals(subtype.getStringValue())) { + // MODIF THL 2010/10/11 Accept IndirectObject + PdfObject cgmap1 = subfont.get("CIDToGIDMap"); + if (cgmap1 == null) { + return false; + } + PdfObject cgmap; + if (cgmap1 instanceof PdfIndirectObj) { + cgmap = (PdfObject) _module.resolveIndirectObject(cgmap1); + } else { + cgmap = cgmap1; + } + if (cgmap instanceof PdfSimpleObject) { + if (!"Identity".equals(((PdfSimpleObject) cgmap).getStringValue())) { + return false; } - } - catch (Exception e) { + } else if (!(cgmap instanceof PdfStream)) { return false; + } } - return true; + } + } catch (Exception e) { + return false; } + return true; + } - /* Check if a font has an acceptable encoding. This applies - only to TrueType fonts. */ - private boolean ttFontEncodingOK (PdfDictionary font) - { - try { - PdfDictionary desc = (PdfDictionary) - _module.resolveIndirectObject (font.get ("FontDescriptor")); - // Not all fonts -- in particular, the standard 14 -- - // are required to have FontDescriptors. How do we - // handle encoding in those cases? - if (desc == null) { - return true; // for now, give benefit of doubt - } - PdfSimpleObject flagObj = (PdfSimpleObject) - desc.get ("Flags"); - int flags = flagObj.getIntValue (); - if ((flags & 4) == 0) { - // It's a nonsymbolic font, check the Encoding - PdfSimpleObject encoding = - (PdfSimpleObject) font.get ("Encoding"); - String encStr = encoding.getStringValue (); - if (!"MacRomanEncoding".equals (encStr) && - !"WinAnsiEncoding".equals (encStr)) { - return false; - } - } - } - catch (Exception e) { - return false; + /* Check if a font has an acceptable encoding. This applies + only to TrueType fonts. */ + private boolean ttFontEncodingOK(PdfDictionary font) { + try { + PdfDictionary desc = + (PdfDictionary) _module.resolveIndirectObject(font.get("FontDescriptor")); + // Not all fonts -- in particular, the standard 14 -- + // are required to have FontDescriptors. How do we + // handle encoding in those cases? + if (desc == null) { + return true; // for now, give benefit of doubt + } + PdfSimpleObject flagObj = (PdfSimpleObject) desc.get("Flags"); + int flags = flagObj.getIntValue(); + if ((flags & 4) == 0) { + // It's a nonsymbolic font, check the Encoding + PdfSimpleObject encoding = (PdfSimpleObject) font.get("Encoding"); + String encStr = encoding.getStringValue(); + if (!"MacRomanEncoding".equals(encStr) && !"WinAnsiEncoding".equals(encStr)) { + return false; } - return true; + } + } catch (Exception e) { + return false; } + return true; + } - - /* Check if the interactive form is OK */ - private boolean formOK (PdfDictionary form) - { - // Guess what? It's another hierarchy of dictionaries! - // So let's walk through the fields... - try { - PdfArray fields = (PdfArray)_module.resolveIndirectObject(form.get ("Fields")); - Vector fieldVec = fields.getContent (); - for (int i = 0; i < fieldVec.size (); i++) { - PdfDictionary field = (PdfDictionary) fieldVec.elementAt (i); - if (!fieldOK (field)) { - return false; - } - } - // The NeedAppearances flag either shall not be present - // or shall be false. - PdfSimpleObject needapp = (PdfSimpleObject) form.get ("NeedAppearances"); - if (needapp != null && !needapp.isFalse ()) { - return false; - } + /* Check if the interactive form is OK */ + private boolean formOK(PdfDictionary form) { + // Guess what? It's another hierarchy of dictionaries! + // So let's walk through the fields... + try { + PdfArray fields = (PdfArray) _module.resolveIndirectObject(form.get("Fields")); + Vector fieldVec = fields.getContent(); + for (int i = 0; i < fieldVec.size(); i++) { + PdfDictionary field = (PdfDictionary) fieldVec.elementAt(i); + if (!fieldOK(field)) { + return false; } - catch (Exception e) { - return false; - } - return true; + } + // The NeedAppearances flag either shall not be present + // or shall be false. + PdfSimpleObject needapp = (PdfSimpleObject) form.get("NeedAppearances"); + if (needapp != null && !needapp.isFalse()) { + return false; + } + } catch (Exception e) { + return false; } + return true; + } - - /* Check a form field for validity. We don't allow form fields - to have AA (Additional Actions) dictionaries */ - private boolean fieldOK (PdfDictionary field) - { - try { - // A Widget annotation dictionary or Field dictionary - // shall not contain the A or AA keys. - if (field.get ("AA") != null) { - return false; - } - if (field.get ("A") != null) { - return false; - } - // Every form field shall have an appearance dictionary - // associated with the field's data. - if (field.get ("DR") == null) { - return false; - } - PdfArray kids = (PdfArray) field.get ("Kids"); - // Now, just to complicate things, the contents of - // the array might be subfield dictionaries or might - // be widget annotations. Oh, and neither one has - // a required Type entry. - // We only case about subfields. - if (kids != null) { - Vector kidVec = kids.getContent (); - for (int i = 0; i < kidVec.size (); i++) { - PdfDictionary kid = (PdfDictionary) kidVec.elementAt (i); - // The safest way to check if this is a field seems - // to be to look for the required Parent entry. - if (kid.get ("Parent") != null && !fieldOK (kid)) { - return false; - } - } - } - } - catch (Exception e) { + /* Check a form field for validity. We don't allow form fields + to have AA (Additional Actions) dictionaries */ + private boolean fieldOK(PdfDictionary field) { + try { + // A Widget annotation dictionary or Field dictionary + // shall not contain the A or AA keys. + if (field.get("AA") != null) { + return false; + } + if (field.get("A") != null) { + return false; + } + // Every form field shall have an appearance dictionary + // associated with the field's data. + if (field.get("DR") == null) { + return false; + } + PdfArray kids = (PdfArray) field.get("Kids"); + // Now, just to complicate things, the contents of + // the array might be subfield dictionaries or might + // be widget annotations. Oh, and neither one has + // a required Type entry. + // We only case about subfields. + if (kids != null) { + Vector kidVec = kids.getContent(); + for (int i = 0; i < kidVec.size(); i++) { + PdfDictionary kid = (PdfDictionary) kidVec.elementAt(i); + // The safest way to check if this is a field seems + // to be to look for the required Parent entry. + if (kid.get("Parent") != null && !fieldOK(kid)) { return false; + } } - return true; + } + } catch (Exception e) { + return false; } + return true; + } - /* Walk through the page tree and check all Resources dictionaries - that we find. Along the way, we check several things: - - Color spaces. The document may not have both CMYK and - RGB color spaces. - - Extended graphic states. - - XObjects. - */ - private boolean resourcesOK () - { - PageTreeNode docTreeRoot = _module.getDocumentTree (); - try { - docTreeRoot.startWalk (); - DocNode docNode; - for (;;) { - docNode = docTreeRoot.nextDocNode (); - if (docNode == null) { - break; - } - // Check for node-level resources - PdfDictionary rsrc = docNode.getResources (); - if (rsrc != null) { - - // Check color spaces. - PdfDictionary cs = (PdfDictionary) - _module.resolveIndirectObject - (rsrc.get ("ColorSpace")); - if (!colorSpaceOK (cs)) { - return false; - } + /* Walk through the page tree and check all Resources dictionaries + that we find. Along the way, we check several things: - // Check extended graphics state. - PdfDictionary gs = (PdfDictionary) - _module.resolveIndirectObject - (rsrc.get ("ExtGState")); - if (!extGStateOK (gs)) { - return false; - } - - // Check XObjects. - PdfDictionary xo = (PdfDictionary) - _module.resolveIndirectObject - (rsrc.get ("XObject")); - if (!xObjectsOK (xo)) { - return false; - } - } - - // Check content streams for resources - if (docNode instanceof PageObject) { - List streams = - ((PageObject) docNode).getContentStreams (); - if (streams != null) { - Iterator iter = streams.listIterator (); - while (iter.hasNext ()) { - PdfStream stream = iter.next (); - PdfDictionary dict = stream.getDict (); - PdfDictionary rs = - (PdfDictionary) - _module.resolveIndirectObject(dict.get ("Resources")); - if (rs != null) { - PdfDictionary cs = (PdfDictionary) - _module.resolveIndirectObject - (rs.get ("ColorSpace")); - if (!colorSpaceOK (cs)) { - return false; - } + Color spaces. The document may not have both CMYK and + RGB color spaces. - PdfDictionary gs = (PdfDictionary) - _module.resolveIndirectObject - (rs.get ("ExtGState")); - if (!extGStateOK (gs)) { - return false; - } + Extended graphic states. - PdfDictionary xo = (PdfDictionary) - _module.resolveIndirectObject - (rs.get ("XObject")); - if (!xObjectsOK (xo)) { - return false; - } - } - // Also check for filters - PdfObject filters = - dict.get ("Filter"); - if (hasFilters (filters, excludedFilters)) { - return false; - } - } - } - - // Also check page objects for annotations. - // Must be one of the prescribed types, but not - // Movie, Sound, or FileAttachment. - PdfArray annots = ((PageObject) docNode).getAnnotations (); - if (annots != null) { - Vector annVec = annots.getContent (); - for (int i = 0; i < annVec.size (); i++) { - PdfDictionary annDict = (PdfDictionary) - _module.resolveIndirectObject - (annVec.elementAt (i)); - PdfSimpleObject subtypeObj = (PdfSimpleObject) annDict.get ("Subtype"); - String subtypeVal = subtypeObj.getStringValue (); - boolean stOK = false; - int j; - for (j = 0; j < annotTypes.length; j++) { - if (annotTypes[j].equals (subtypeVal)) { - - stOK = true; - break; - } - } - if (!stOK) { - return false; - } - - // If it's a Widget, it can't have an AA entry - if ("Widget".equals (subtypeVal) && annDict.get ("AA") != null) { - return false; - } - // For non-text annotation types, the - // Contents key is RECOMMENDED, not required. - // Therefore comment this test out GDM 4-Sep-2012 -// for (j = 0; i < nonTextAnnotTypes.length; j++) { -// if (nonTextAnnotTypes[i].equals (subtypeVal)) { -// if (annDict.get ("Contents") == null) { -// return false; -// } -// else { -// // Contents found, this dict OK -// break; -// } -// } -// } - - // if the CA key is present, it must have a - // value of 1.0. - PdfSimpleObject ca = (PdfSimpleObject) - annDict.get ("CA"); - if (ca != null) { - double caVal = ca.getDoubleValue (); - if (caVal != 1.0) { - return false; - } - } - } - } - } - } + XObjects. + */ + private boolean resourcesOK() { + PageTreeNode docTreeRoot = _module.getDocumentTree(); + try { + docTreeRoot.startWalk(); + DocNode docNode; + for (; ; ) { + docNode = docTreeRoot.nextDocNode(); + if (docNode == null) { + break; } - catch (Exception e) { - return false; - } - return true; // passed all tests - } + // Check for node-level resources + PdfDictionary rsrc = docNode.getResources(); + if (rsrc != null) { + // Check color spaces. + PdfDictionary cs = (PdfDictionary) _module.resolveIndirectObject(rsrc.get("ColorSpace")); + if (!colorSpaceOK(cs)) { + return false; + } + // Check extended graphics state. + PdfDictionary gs = (PdfDictionary) _module.resolveIndirectObject(rsrc.get("ExtGState")); + if (!extGStateOK(gs)) { + return false; + } - /* Check if a color space dictionary is conformant */ - private boolean colorSpaceOK (PdfDictionary cs) - { - // If it's null, that's fine. - if (cs == null) { - return true; + // Check XObjects. + PdfDictionary xo = (PdfDictionary) _module.resolveIndirectObject(rsrc.get("XObject")); + if (!xObjectsOK(xo)) { + return false; + } } - // Walk through the color space dictionary, - // checking device ("uncalibrated") color spaces - Iterator iter = cs.iterator (); - while (iter.hasNext ()) { - PdfObject res = (PdfObject) iter.next (); - if (res instanceof PdfArray) { - Vector resv = ((PdfArray) res).getContent (); - PdfSimpleObject snameobj = (PdfSimpleObject) resv.elementAt (0); - String sname = snameobj.getStringValue (); - boolean oldHasUncalCS = hasUncalCS; - if ("DeviceCMYK".equals (sname)) { - hasDevCMYK = true; - hasUncalCS = true; - } - else if ("DeviceRGB".equals (sname)) { - hasDevRGB = true; - hasUncalCS = true; - } - else if ("DeviceGray".equals (sname)) { - hasUncalCS = true; - } - // If this is the first time we've hit an uncalibrated - // color space, check for an appropriate OutputIntent dict. - if (hasUncalCS && !oldHasUncalCS && !checkUncalIntent ()) { - return false; + + // Check content streams for resources + if (docNode instanceof PageObject) { + List streams = ((PageObject) docNode).getContentStreams(); + if (streams != null) { + Iterator iter = streams.listIterator(); + while (iter.hasNext()) { + PdfStream stream = iter.next(); + PdfDictionary dict = stream.getDict(); + PdfDictionary rs = + (PdfDictionary) _module.resolveIndirectObject(dict.get("Resources")); + if (rs != null) { + PdfDictionary cs = + (PdfDictionary) _module.resolveIndirectObject(rs.get("ColorSpace")); + if (!colorSpaceOK(cs)) { + return false; } - if (hasDevRGB && hasDevCMYK) { - return false; // can't have both in same file + + PdfDictionary gs = + (PdfDictionary) _module.resolveIndirectObject(rs.get("ExtGState")); + if (!extGStateOK(gs)) { + return false; } - } - } - return true; // passed all tests - } - /* If there is an uncalibrated color space, then there must be a - * "PDF/A-1 OutputIntent." */ - private boolean checkUncalIntent () - { - try { - // First off, there must be an OutputIntents array - // in the document catalog dictionary. - PdfDictionary catDict = _module.getCatalogDict (); - PdfArray intentsArray = (PdfArray) _module.resolveIndirectObject - (catDict.get ("OutputIntents")); - if (intentsArray == null) { + PdfDictionary xo = (PdfDictionary) _module.resolveIndirectObject(rs.get("XObject")); + if (!xObjectsOK(xo)) { + return false; + } + } + // Also check for filters + PdfObject filters = dict.get("Filter"); + if (hasFilters(filters, excludedFilters)) { return false; + } } - Vector intVec = intentsArray.getContent (); - PdfStream theOutProfile = null; - boolean pdfaProfileSeen = false; - for (int i = 0; i < intVec.size (); i++) { - // Multiple intents arrays are allowed, but all must use - // the same DestOutputProfile object or none, and there - // must be at least one that as one and has GTS_PDFA1 as - // the value of its S key. - PdfDictionary intent = (PdfDictionary) intVec.elementAt (0); - PdfSimpleObject outCond = - (PdfSimpleObject) intent.get ("OutputCondition"); - if (outCond != null) { - PdfStream outProfile = (PdfStream) _module.resolveIndirectObject - (intent.get ("DestOutputProfile")); - if (outProfile != null) { - if (theOutProfile != null) { - // all output profiles must be the same. - if (outProfile != theOutProfile) { - return false; - } - } - else { - // All subsequent output profiles must matcht his. - theOutProfile = outProfile; - } - PdfSimpleObject subtype = (PdfSimpleObject) intent.get ("S"); - if (subtype != null && - "GTS_PDFA1".equals (subtype.getStringValue())) { - pdfaProfileSeen = true; - } - } + } + + // Also check page objects for annotations. + // Must be one of the prescribed types, but not + // Movie, Sound, or FileAttachment. + PdfArray annots = ((PageObject) docNode).getAnnotations(); + if (annots != null) { + Vector annVec = annots.getContent(); + for (int i = 0; i < annVec.size(); i++) { + PdfDictionary annDict = + (PdfDictionary) _module.resolveIndirectObject(annVec.elementAt(i)); + PdfSimpleObject subtypeObj = (PdfSimpleObject) annDict.get("Subtype"); + String subtypeVal = subtypeObj.getStringValue(); + boolean stOK = false; + int j; + for (j = 0; j < annotTypes.length; j++) { + if (annotTypes[j].equals(subtypeVal)) { + + stOK = true; + break; } - } - if (theOutProfile == null || !pdfaProfileSeen) { + } + if (!stOK) { + return false; + } + + // If it's a Widget, it can't have an AA entry + if ("Widget".equals(subtypeVal) && annDict.get("AA") != null) { return false; + } + // For non-text annotation types, the + // Contents key is RECOMMENDED, not required. + // Therefore comment this test out GDM 4-Sep-2012 + // for (j = 0; i < nonTextAnnotTypes.length; j++) { + // if (nonTextAnnotTypes[i].equals (subtypeVal)) { + // if (annDict.get ("Contents") == null) { + // return false; + // } + // else { + // // Contents found, this dict OK + // break; + // } + // } + // } + + // if the CA key is present, it must have a + // value of 1.0. + PdfSimpleObject ca = (PdfSimpleObject) annDict.get("CA"); + if (ca != null) { + double caVal = ca.getDoubleValue(); + if (caVal != 1.0) { + return false; + } + } } + } } - catch (Exception e) { - return false; - } - return true; + } + } catch (Exception e) { + return false; + } + return true; // passed all tests + } + + /* Check if a color space dictionary is conformant */ + private boolean colorSpaceOK(PdfDictionary cs) { + // If it's null, that's fine. + if (cs == null) { + return true; } - - - - - /* Check if the outlines (if any) are OK. This is a check - on Actions, and the module has already checked if there - are Actions in the outlines, so if there aren't any, - we save the time to do this test. */ - private boolean outlinesOK () - { - if (!_module.getActionsExist ()) { - return true; + // Walk through the color space dictionary, + // checking device ("uncalibrated") color spaces + Iterator iter = cs.iterator(); + while (iter.hasNext()) { + PdfObject res = (PdfObject) iter.next(); + if (res instanceof PdfArray) { + Vector resv = ((PdfArray) res).getContent(); + PdfSimpleObject snameobj = (PdfSimpleObject) resv.elementAt(0); + String sname = snameobj.getStringValue(); + boolean oldHasUncalCS = hasUncalCS; + if ("DeviceCMYK".equals(sname)) { + hasDevCMYK = true; + hasUncalCS = true; + } else if ("DeviceRGB".equals(sname)) { + hasDevRGB = true; + hasUncalCS = true; + } else if ("DeviceGray".equals(sname)) { + hasUncalCS = true; } - PdfDictionary outlineDict = _module.getOutlineDict (); - if (outlineDict == null) { - return true; + // If this is the first time we've hit an uncalibrated + // color space, check for an appropriate OutputIntent dict. + if (hasUncalCS && !oldHasUncalCS && !checkUncalIntent()) { + return false; } - try { - PdfDictionary item = (PdfDictionary) _module.resolveIndirectObject - (outlineDict.get ("First")); - while (item != null) { - if (!checkOutlineItem (item)) { - return false; - } - item = (PdfDictionary) _module.resolveIndirectObject - (((PdfDictionary) item).get ("Next")); - } + if (hasDevRGB && hasDevCMYK) { + return false; // can't have both in same file } - catch (Exception e) { - return false; - } - return true; + } } + return true; // passed all tests + } - - /* Check an outline item, going down recursively */ - private boolean checkOutlineItem (PdfDictionary item) - { - // Check if there are actions for this item - try { - PdfDictionary action = (PdfDictionary) - _module.resolveIndirectObject (item.get ("A")); - if (action != null && !actionOK (action)) { + /* If there is an uncalibrated color space, then there must be a + * "PDF/A-1 OutputIntent." */ + private boolean checkUncalIntent() { + try { + // First off, there must be an OutputIntents array + // in the document catalog dictionary. + PdfDictionary catDict = _module.getCatalogDict(); + PdfArray intentsArray = + (PdfArray) _module.resolveIndirectObject(catDict.get("OutputIntents")); + if (intentsArray == null) { + return false; + } + Vector intVec = intentsArray.getContent(); + PdfStream theOutProfile = null; + boolean pdfaProfileSeen = false; + for (int i = 0; i < intVec.size(); i++) { + // Multiple intents arrays are allowed, but all must use + // the same DestOutputProfile object or none, and there + // must be at least one that as one and has GTS_PDFA1 as + // the value of its S key. + PdfDictionary intent = (PdfDictionary) intVec.elementAt(0); + PdfSimpleObject outCond = (PdfSimpleObject) intent.get("OutputCondition"); + if (outCond != null) { + PdfStream outProfile = + (PdfStream) _module.resolveIndirectObject(intent.get("DestOutputProfile")); + if (outProfile != null) { + if (theOutProfile != null) { + // all output profiles must be the same. + if (outProfile != theOutProfile) { return false; + } + } else { + // All subsequent output profiles must matcht his. + theOutProfile = outProfile; } - PdfDictionary child = (PdfDictionary) - _module.resolveIndirectObject (item.get ("First")); - PdfDictionary next; - while (child != null) { - if (!checkOutlineItem (child)) { - return false; - } - next = (PdfDictionary) - _module.resolveIndirectObject (child.get ("Next")); - if (next.getObjNumber() != child.getObjNumber()) { - child = next; - } else { - child = null; - } + PdfSimpleObject subtype = (PdfSimpleObject) intent.get("S"); + if (subtype != null && "GTS_PDFA1".equals(subtype.getStringValue())) { + pdfaProfileSeen = true; } + } } - catch (Exception e) { - return false; + } + if (theOutProfile == null || !pdfaProfileSeen) { + return false; + } + } catch (Exception e) { + return false; + } + return true; + } + + /* Check if the outlines (if any) are OK. This is a check + on Actions, and the module has already checked if there + are Actions in the outlines, so if there aren't any, + we save the time to do this test. */ + private boolean outlinesOK() { + if (!_module.getActionsExist()) { + return true; + } + PdfDictionary outlineDict = _module.getOutlineDict(); + if (outlineDict == null) { + return true; + } + try { + PdfDictionary item = (PdfDictionary) _module.resolveIndirectObject(outlineDict.get("First")); + while (item != null) { + if (!checkOutlineItem(item)) { + return false; } - return true; + item = (PdfDictionary) _module.resolveIndirectObject(((PdfDictionary) item).get("Next")); + } + } catch (Exception e) { + return false; } + return true; + } + /* Check an outline item, going down recursively */ + private boolean checkOutlineItem(PdfDictionary item) { + // Check if there are actions for this item + try { + PdfDictionary action = (PdfDictionary) _module.resolveIndirectObject(item.get("A")); + if (action != null && !actionOK(action)) { + return false; + } + PdfDictionary child = (PdfDictionary) _module.resolveIndirectObject(item.get("First")); + PdfDictionary next; + while (child != null) { + if (!checkOutlineItem(child)) { + return false; + } + next = (PdfDictionary) _module.resolveIndirectObject(child.get("Next")); + if (next.getObjNumber() != child.getObjNumber()) { + child = next; + } else { + child = null; + } + } + } catch (Exception e) { + return false; + } + return true; + } - /* Validate an Action dictionary. Actions exclude certain types. */ - private boolean actionOK (PdfDictionary action) - { - int i; - // For some reason, an action's type is an "S" entry, not - // a "Subtype" entry. - try { - PdfSimpleObject actType = (PdfSimpleObject) action.get ("S"); - String actStr = actType.getStringValue (); - // Note: I should also be checking for the set-state - // and no-op actions, which are prohibited; but since - // the documentation I can find doesn't say what the - // actual names for these actions are, it's tough to - // exclude them, and I'd rather not guess the names. - for (i = 0; i < excludedActions.length; i++) { - if (excludedActions[i].equals (actStr)) { - return false; - } - } - // An action can have a "Next" entry which is either - // another action or an array of actions. Need to follow - // the whole tree to make sure all actions are legit. - PdfObject next = action.get ("Next"); - if (next instanceof PdfDictionary) { - if (!actionOK ((PdfDictionary) next)) { - return false; - } - } - else if (next instanceof PdfArray) { - Vector nextVec = ((PdfArray) next).getContent (); - for (i = 0; i < nextVec.size (); i++) { - PdfDictionary nact = (PdfDictionary) - nextVec.elementAt (i); - if (!actionOK (nact)) { - return false; - } - } - } - else if (next != null) { - return false; - } + /* Validate an Action dictionary. Actions exclude certain types. */ + private boolean actionOK(PdfDictionary action) { + int i; + // For some reason, an action's type is an "S" entry, not + // a "Subtype" entry. + try { + PdfSimpleObject actType = (PdfSimpleObject) action.get("S"); + String actStr = actType.getStringValue(); + // Note: I should also be checking for the set-state + // and no-op actions, which are prohibited; but since + // the documentation I can find doesn't say what the + // actual names for these actions are, it's tough to + // exclude them, and I'd rather not guess the names. + for (i = 0; i < excludedActions.length; i++) { + if (excludedActions[i].equals(actStr)) { + return false; } - catch (Exception e) { + } + // An action can have a "Next" entry which is either + // another action or an array of actions. Need to follow + // the whole tree to make sure all actions are legit. + PdfObject next = action.get("Next"); + if (next instanceof PdfDictionary) { + if (!actionOK((PdfDictionary) next)) { + return false; + } + } else if (next instanceof PdfArray) { + Vector nextVec = ((PdfArray) next).getContent(); + for (i = 0; i < nextVec.size(); i++) { + PdfDictionary nact = (PdfDictionary) nextVec.elementAt(i); + if (!actionOK(nact)) { return false; + } } - return true; + } else if (next != null) { + return false; + } + } catch (Exception e) { + return false; } + return true; + } + /* The ExtGState resource may not have a TR key, or a + TR2 with a value other than "Default". */ + private boolean extGStateOK(PdfDictionary gs) { + if (gs == null) { + // no object means no problem + return true; + } + try { + PdfObject tr = gs.get("TR"); + PdfObject tr2 = gs.get("TR2"); + if (tr != null) { + return false; + } + if (tr2 != null) { + String tr2Val = ((PdfSimpleObject) tr2).getStringValue(); + if (!"Default".equals(tr2Val)) { + return false; + } + } - /* The ExtGState resource may not have a TR key, or a - TR2 with a value other than "Default". */ - private boolean extGStateOK (PdfDictionary gs) - { - if (gs == null) { - // no object means no problem - return true; + // RI is restricted to the traditional 4 rendering intents + PdfSimpleObject ri = (PdfSimpleObject) gs.get("RI"); + if (ri != null) { + String riVal = ri.getStringValue(); + if (!validIntentString(riVal)) { + return false; } - try { - PdfObject tr = gs.get ("TR"); - PdfObject tr2 = gs.get ("TR2"); - - if (tr != null) { - return false; - } - if (tr2 != null) { - String tr2Val = ((PdfSimpleObject) tr2).getStringValue (); - if (!"Default".equals (tr2Val)) { - return false; - } - } + } - // RI is restricted to the traditional 4 rendering intents - PdfSimpleObject ri = (PdfSimpleObject) gs.get ("RI"); - if (ri != null) { - String riVal = ri.getStringValue (); - if (!validIntentString (riVal)) { - return false; - } - } - - // SMask is allowed only with a value of "None". - PdfSimpleObject smask = (PdfSimpleObject) gs.get ("SMask"); - if (smask != null) { - String smVal = smask.getStringValue (); - if (!"None".equals (smVal)) { - return false; - } - } - - // BM, if present, must be "Normal" or "Compatible" - PdfSimpleObject blendMode = - (PdfSimpleObject) gs.get ("BM"); - if (blendMode != null) { - String bmVal = blendMode.getStringValue (); - if (!"Normal".equals (bmVal) && - !"Compatible".equals (bmVal)) { - return false; - } - } - - // CA and ca must be 1.0, if present - PdfSimpleObject ca = (PdfSimpleObject) gs.get ("CA"); - double caVal; - if (ca != null) { - caVal = ca.getDoubleValue (); - if (caVal != 1.0) { - return false; - } - } - ca = (PdfSimpleObject) gs.get ("ca"); - if (ca != null) { - caVal = ca.getDoubleValue (); - if (caVal != 1.0) { - return false; - } - } + // SMask is allowed only with a value of "None". + PdfSimpleObject smask = (PdfSimpleObject) gs.get("SMask"); + if (smask != null) { + String smVal = smask.getStringValue(); + if (!"None".equals(smVal)) { + return false; } - catch (Exception e) { - return false; + } + + // BM, if present, must be "Normal" or "Compatible" + PdfSimpleObject blendMode = (PdfSimpleObject) gs.get("BM"); + if (blendMode != null) { + String bmVal = blendMode.getStringValue(); + if (!"Normal".equals(bmVal) && !"Compatible".equals(bmVal)) { + return false; } - return true; // passed all tests - } + } + // CA and ca must be 1.0, if present + PdfSimpleObject ca = (PdfSimpleObject) gs.get("CA"); + double caVal; + if (ca != null) { + caVal = ca.getDoubleValue(); + if (caVal != 1.0) { + return false; + } + } + ca = (PdfSimpleObject) gs.get("ca"); + if (ca != null) { + caVal = ca.getDoubleValue(); + if (caVal != 1.0) { + return false; + } + } + } catch (Exception e) { + return false; + } + return true; // passed all tests + } - /** - * Checks a single XObject for xObjectsOK. - */ - protected boolean xObjectOK (PdfDictionary xo) - { - if (xo == null) { - // no XObject means no problem - return true; + /** Checks a single XObject for xObjectsOK. */ + protected boolean xObjectOK(PdfDictionary xo) { + if (xo == null) { + // no XObject means no problem + return true; + } + try { + // PostScript XObjects aren't allowed. + // Image XObjects must meet certain tests. + PdfSimpleObject subtype = (PdfSimpleObject) xo.get("Subtype"); + if (subtype != null) { + String subtypeVal = subtype.getStringValue(); + if ("PS".equals(subtypeVal)) { + // PS XObjects aren't allowed. + return false; } - try { - // PostScript XObjects aren't allowed. - // Image XObjects must meet certain tests. - PdfSimpleObject subtype = (PdfSimpleObject) xo.get ("Subtype"); - if (subtype != null) { - String subtypeVal = subtype.getStringValue (); - if ("PS".equals (subtypeVal)) { - // PS XObjects aren't allowed. - return false; - } - if ("Image".equals (subtypeVal) && !imageObjectOK (xo)) { - return false; - } - if ("Form".equals (subtypeVal) && !formObjectOK (xo)) { - return false; - } - } + if ("Image".equals(subtypeVal) && !imageObjectOK(xo)) { + return false; } - catch (Exception e) { - return false; + if ("Form".equals(subtypeVal) && !formObjectOK(xo)) { + return false; } - return true; + } + } catch (Exception e) { + return false; } + return true; + } - /** Checks if a Form xobject is valid. This overrides the method in - XProfileBase. */ - protected boolean formObjectOK (PdfDictionary xo) - { - // PDF/A elements can't have an OPI or Ref key in Form xobjects. - - return !(xo.get ("OPI") != null || xo.get ("Ref") != null); - } + /** Checks if a Form xobject is valid. This overrides the method in XProfileBase. */ + protected boolean formObjectOK(PdfDictionary xo) { + // PDF/A elements can't have an OPI or Ref key in Form xobjects. + return !(xo.get("OPI") != null || xo.get("Ref") != null); + } - /** Checks if a single image XObject fits the profile */ - protected boolean imageObjectOK (PdfDictionary xo) - { - try { - // OPI and Alternates keys are disallowed - if (xo.get ("OPI") != null || - xo.get ("Alternates") != null) { - return false; - } - - // Check against LZW filter - PdfObject filters = - xo.get ("Filter"); - if (hasFilters (filters, excludedFilters)) { - return false; - } + /** Checks if a single image XObject fits the profile */ + protected boolean imageObjectOK(PdfDictionary xo) { + try { + // OPI and Alternates keys are disallowed + if (xo.get("OPI") != null || xo.get("Alternates") != null) { + return false; + } - - // Interpolate is allowed only if its value is false. - PdfSimpleObject interp = (PdfSimpleObject) xo.get ("Interpolate"); - if (interp != null && !interp.isFalse ()) { - return false; - } - - // Intent must be one of the four standard rendering intents, - // if present. - PdfSimpleObject intent = (PdfSimpleObject) xo.get ("Intent"); - if (intent != null) { - String intentStr = intent.getStringValue (); - if (! validIntentString (intentStr)) { - return false; - } - } - - } - catch (Exception e) { - return false; + // Check against LZW filter + PdfObject filters = xo.get("Filter"); + if (hasFilters(filters, excludedFilters)) { + return false; + } + + // Interpolate is allowed only if its value is false. + PdfSimpleObject interp = (PdfSimpleObject) xo.get("Interpolate"); + if (interp != null && !interp.isFalse()) { + return false; + } + + // Intent must be one of the four standard rendering intents, + // if present. + PdfSimpleObject intent = (PdfSimpleObject) xo.get("Intent"); + if (intent != null) { + String intentStr = intent.getStringValue(); + if (!validIntentString(intentStr)) { + return false; } - return true; + } + + } catch (Exception e) { + return false; } - - private boolean validIntentString (String str) - { - return ("RelativeColorimetric".equals (str) || - "AbsoluteColorimetric".equals (str) || - "Perceptual".equals (str) || - "Saturation".equals (str)); + return true; + } + + private boolean validIntentString(String str) { + return ("RelativeColorimetric".equals(str) + || "AbsoluteColorimetric".equals(str) + || "Perceptual".equals(str) + || "Saturation".equals(str)); + } + + // See if the metadata stream from the catalog dictionary is OK + private boolean metadataOK(PdfStream metadata) { + // Presence of metadata is required + if (metadata == null) { + return false; } - - // See if the metadata stream from the catalog dictionary is OK - private boolean metadataOK (PdfStream metadata) - { - // Presence of metadata is required - if (metadata == null) { - return false; - } - try { - PdfDictionary metaDict = metadata.getDict (); - if (metaDict.get ("Filter") != null) { - // We just metadata we didn't like. Filters aren't allowed. - return false; - } - - // Create an InputSource to feed the parser. - SAXParserFactory factory = - SAXParserFactory.newInstance(); - factory.setNamespaceAware (true); - XMLReader parser = factory.newSAXParser ().getXMLReader (); - //InputStream stream = new StreamInputStream (metadata, _module.getFile ()); - PdfXMPSource src = new PdfXMPSource (metadata, _module.getFile ()); - XMPHandler handler = new XMPHandler (); - parser.setContentHandler (handler); - parser.setErrorHandler (handler); - // We have to parse twice. The first time, we may get - // an encoding change as part of an exception thrown. If this - // happens, we create a new InputSource with the encoding, and - // continue. - try { - parser.parse (src); - if (!handler.isPdfaCompliant ()) { - return false; - } - } - catch (SAXException se) { - String msg = se.getMessage (); - if (msg != null && msg.startsWith ("ENC=")) { - // encoding change is not allowed with PDF/A, so there's no - // need to re-parse - return false; -// String encoding = msg.substring (5); -// try { -// //Reader rdr = new InputStreamReader (stream, encoding); -// src = new PdfXMPSource (metadata, _module.getFile (), encoding); -// parser.parse (src); -// if (!handler.isPdfaCompliant ()) { -// return false; -// } -// } -// catch (UnsupportedEncodingException uee) { -// return false; -// } - } - } + try { + PdfDictionary metaDict = metadata.getDict(); + if (metaDict.get("Filter") != null) { + // We just metadata we didn't like. Filters aren't allowed. + return false; + } + + // Create an InputSource to feed the parser. + SAXParserFactory factory = SAXParserFactory.newInstance(); + factory.setNamespaceAware(true); + XMLReader parser = factory.newSAXParser().getXMLReader(); + // InputStream stream = new StreamInputStream (metadata, _module.getFile ()); + PdfXMPSource src = new PdfXMPSource(metadata, _module.getFile()); + XMPHandler handler = new XMPHandler(); + parser.setContentHandler(handler); + parser.setErrorHandler(handler); + // We have to parse twice. The first time, we may get + // an encoding change as part of an exception thrown. If this + // happens, we create a new InputSource with the encoding, and + // continue. + try { + parser.parse(src); + if (!handler.isPdfaCompliant()) { + return false; } - catch (Exception e) { - return false; + } catch (SAXException se) { + String msg = se.getMessage(); + if (msg != null && msg.startsWith("ENC=")) { + // encoding change is not allowed with PDF/A, so there's no + // need to re-parse + return false; + // String encoding = msg.substring (5); + // try { + // //Reader rdr = new InputStreamReader (stream, encoding); + // src = new PdfXMPSource (metadata, _module.getFile (), encoding); + // parser.parse (src); + // if (!handler.isPdfaCompliant ()) { + // return false; + // } + // } + // catch (UnsupportedEncodingException uee) { + // return false; + // } } - return true; + } + } catch (Exception e) { + return false; } + return true; + } } diff --git a/jhove-modules/pdf-hul/src/main/java/edu/harvard/hul/ois/jhove/module/pdf/AProfileLevelA.java b/jhove-modules/pdf-hul/src/main/java/edu/harvard/hul/ois/jhove/module/pdf/AProfileLevelA.java index 966142077..1e0a1d73e 100644 --- a/jhove-modules/pdf-hul/src/main/java/edu/harvard/hul/ois/jhove/module/pdf/AProfileLevelA.java +++ b/jhove-modules/pdf-hul/src/main/java/edu/harvard/hul/ois/jhove/module/pdf/AProfileLevelA.java @@ -1,60 +1,51 @@ -/********************************************************************** - * Jhove - JSTOR/Harvard Object Validation Environment - * Copyright 2004-2005 by JSTOR and the President and Fellows of Harvard College - **********************************************************************/ - +/** + * ******************************************************************** Jhove - JSTOR/Harvard Object + * Validation Environment Copyright 2004-2005 by JSTOR and the President and Fellows of Harvard + * College ******************************************************************** + */ package edu.harvard.hul.ois.jhove.module.pdf; import edu.harvard.hul.ois.jhove.module.PdfModule; /** - * PDF profile checker for PDF/A-1 documents, Level A. - * See ISO 19005-1:2005(E), "Document Imaging Applications - * Application Issues". - * - * This profile checker is completely dependent on AProfile. - * It simply queries an instance of AProfile for Level A compliance. + * PDF profile checker for PDF/A-1 documents, Level A. See ISO 19005-1:2005(E), "Document Imaging + * Applications Application Issues". * - * @author Gary McGath + *

This profile checker is completely dependent on AProfile. It simply queries an instance of + * AProfile for Level A compliance. * + * @author Gary McGath */ public class AProfileLevelA extends PdfProfile { - /* AProfile to which this profile is linked. */ - private AProfile _aProfile; - - /** - * Constructor. - * Creates an AProfileLevelA object for subsequent testing. - * - * @param module The module under which we are checking the profile. - * - */ - public AProfileLevelA(PdfModule module) { - super (module); - _profileText = "ISO PDF/A-1, Level A"; - } - - /** - * Returns true if the document satisfies the profile - * at Level A. This returns a meaningful result only if - * satisfiesThisProfile() has previously - * been called on the profile assigned by setAProfile. - * - */ - @Override - public boolean satisfiesThisProfile() { - return _aProfile.satisfiesLevelA(); - } - - /** - * Calling setAProfile links this AProfile to a TaggedProfile. - * This class gets all its information from the linked AProfile, - * so calling this is mandatory. - */ - public void setAProfile (AProfile tpr) - { - _aProfile = tpr; - } - + /* AProfile to which this profile is linked. */ + private AProfile _aProfile; + + /** + * Constructor. Creates an AProfileLevelA object for subsequent testing. + * + * @param module The module under which we are checking the profile. + */ + public AProfileLevelA(PdfModule module) { + super(module); + _profileText = "ISO PDF/A-1, Level A"; + } + + /** + * Returns true if the document satisfies the profile at Level A. This returns a + * meaningful result only if satisfiesThisProfile() has previously been called on the + * profile assigned by setAProfile. + */ + @Override + public boolean satisfiesThisProfile() { + return _aProfile.satisfiesLevelA(); + } + + /** + * Calling setAProfile links this AProfile to a TaggedProfile. This class gets all its information + * from the linked AProfile, so calling this is mandatory. + */ + public void setAProfile(AProfile tpr) { + _aProfile = tpr; + } } diff --git a/jhove-modules/pdf-hul/src/main/java/edu/harvard/hul/ois/jhove/module/pdf/ArrayEnd.java b/jhove-modules/pdf-hul/src/main/java/edu/harvard/hul/ois/jhove/module/pdf/ArrayEnd.java index d7ef600ea..b5ef3664c 100644 --- a/jhove-modules/pdf-hul/src/main/java/edu/harvard/hul/ois/jhove/module/pdf/ArrayEnd.java +++ b/jhove-modules/pdf-hul/src/main/java/edu/harvard/hul/ois/jhove/module/pdf/ArrayEnd.java @@ -1,19 +1,14 @@ -/********************************************************************** - * Jhove - JSTOR/Harvard Object Validation Environment - * Copyright 2003 by JSTOR and the President and Fellows of Harvard College - **********************************************************************/ - -package edu.harvard.hul.ois.jhove.module.pdf; - /** - * Class for Tokens which represent the "]" that closes an array. + * ******************************************************************** Jhove - JSTOR/Harvard Object + * Validation Environment Copyright 2003 by JSTOR and the President and Fellows of Harvard College + * ******************************************************************** */ -public class ArrayEnd - extends Token -{ - /** Creates an instance of an ArrayEnd */ - public ArrayEnd () - { - super (); - } +package edu.harvard.hul.ois.jhove.module.pdf; + +/** Class for Tokens which represent the "]" that closes an array. */ +public class ArrayEnd extends Token { + /** Creates an instance of an ArrayEnd */ + public ArrayEnd() { + super(); + } } diff --git a/jhove-modules/pdf-hul/src/main/java/edu/harvard/hul/ois/jhove/module/pdf/ArrayStart.java b/jhove-modules/pdf-hul/src/main/java/edu/harvard/hul/ois/jhove/module/pdf/ArrayStart.java index bf1106909..be2762b71 100644 --- a/jhove-modules/pdf-hul/src/main/java/edu/harvard/hul/ois/jhove/module/pdf/ArrayStart.java +++ b/jhove-modules/pdf-hul/src/main/java/edu/harvard/hul/ois/jhove/module/pdf/ArrayStart.java @@ -1,19 +1,14 @@ -/********************************************************************** - * Jhove - JSTOR/Harvard Object Validation Environment - * Copyright 2003 by JSTOR and the President and Fellows of Harvard College - **********************************************************************/ - -package edu.harvard.hul.ois.jhove.module.pdf; - /** - * Class for Tokens which represent the "[" that opens an array. + * ******************************************************************** Jhove - JSTOR/Harvard Object + * Validation Environment Copyright 2003 by JSTOR and the President and Fellows of Harvard College + * ******************************************************************** */ -public class ArrayStart - extends Token -{ - /** Creates an instance of an ArrayStart */ - public ArrayStart () - { - super (); - } +package edu.harvard.hul.ois.jhove.module.pdf; + +/** Class for Tokens which represent the "[" that opens an array. */ +public class ArrayStart extends Token { + /** Creates an instance of an ArrayStart */ + public ArrayStart() { + super(); + } } diff --git a/jhove-modules/pdf-hul/src/main/java/edu/harvard/hul/ois/jhove/module/pdf/Ascii85FilterStream.java b/jhove-modules/pdf-hul/src/main/java/edu/harvard/hul/ois/jhove/module/pdf/Ascii85FilterStream.java index 6f8a1fbe4..06eb06763 100644 --- a/jhove-modules/pdf-hul/src/main/java/edu/harvard/hul/ois/jhove/module/pdf/Ascii85FilterStream.java +++ b/jhove-modules/pdf-hul/src/main/java/edu/harvard/hul/ois/jhove/module/pdf/Ascii85FilterStream.java @@ -1,29 +1,24 @@ -/********************************************************************** - * Jhove - JSTOR/Harvard Object Validation Environment - * Copyright 2005 by JSTOR and the President and Fellows of Harvard College - **********************************************************************/ - +/** + * ******************************************************************** Jhove - JSTOR/Harvard Object + * Validation Environment Copyright 2005 by JSTOR and the President and Fellows of Harvard College + * ******************************************************************** + */ package edu.harvard.hul.ois.jhove.module.pdf; import java.io.FilterInputStream; import java.io.InputStream; /** - * This is a stub which may be implemented in the future. - * It appears to be unnecessary for object streams and cross-reference - * streams created by any version of Acrobat through 7.0, and we - * don't look at other types of streams. + * This is a stub which may be implemented in the future. It appears to be unnecessary for object + * streams and cross-reference streams created by any version of Acrobat through 7.0, and we don't + * look at other types of streams. * * @author Gary McGath - * */ public class Ascii85FilterStream extends FilterInputStream { - /** - * @param in - */ - public Ascii85FilterStream(InputStream in) { - super(in); - } - + /** @param in */ + public Ascii85FilterStream(InputStream in) { + super(in); + } } diff --git a/jhove-modules/pdf-hul/src/main/java/edu/harvard/hul/ois/jhove/module/pdf/AsciiHexFilterStream.java b/jhove-modules/pdf-hul/src/main/java/edu/harvard/hul/ois/jhove/module/pdf/AsciiHexFilterStream.java index 363259e53..81767ffd3 100644 --- a/jhove-modules/pdf-hul/src/main/java/edu/harvard/hul/ois/jhove/module/pdf/AsciiHexFilterStream.java +++ b/jhove-modules/pdf-hul/src/main/java/edu/harvard/hul/ois/jhove/module/pdf/AsciiHexFilterStream.java @@ -1,29 +1,24 @@ -/********************************************************************** - * Jhove - JSTOR/Harvard Object Validation Environment - * Copyright 2005 by JSTOR and the President and Fellows of Harvard College - **********************************************************************/ - +/** + * ******************************************************************** Jhove - JSTOR/Harvard Object + * Validation Environment Copyright 2005 by JSTOR and the President and Fellows of Harvard College + * ******************************************************************** + */ package edu.harvard.hul.ois.jhove.module.pdf; import java.io.FilterInputStream; import java.io.InputStream; /** - * This is a stub which may be implemented in the future. - * It appears to be unnecessary for object streams and cross-reference - * streams created by any version of Acrobat through 7.0, and we - * don't look at other types of streams. + * This is a stub which may be implemented in the future. It appears to be unnecessary for object + * streams and cross-reference streams created by any version of Acrobat through 7.0, and we don't + * look at other types of streams. * * @author Gary McGath - * */ public class AsciiHexFilterStream extends FilterInputStream { - /** - * @param in - */ - public AsciiHexFilterStream(InputStream in) { - super(in); - } - + /** @param in */ + public AsciiHexFilterStream(InputStream in) { + super(in); + } } diff --git a/jhove-modules/pdf-hul/src/main/java/edu/harvard/hul/ois/jhove/module/pdf/Comment.java b/jhove-modules/pdf-hul/src/main/java/edu/harvard/hul/ois/jhove/module/pdf/Comment.java index c5a2b875d..cc457862e 100644 --- a/jhove-modules/pdf-hul/src/main/java/edu/harvard/hul/ois/jhove/module/pdf/Comment.java +++ b/jhove-modules/pdf-hul/src/main/java/edu/harvard/hul/ois/jhove/module/pdf/Comment.java @@ -1,19 +1,14 @@ -/********************************************************************** - * Jhove - JSTOR/Harvard Object Validation Environment - * Copyright 2003 by JSTOR and the President and Fellows of Harvard College - **********************************************************************/ - -package edu.harvard.hul.ois.jhove.module.pdf; - /** - * Class for Tokens which represent PDF comments. + * ******************************************************************** Jhove - JSTOR/Harvard Object + * Validation Environment Copyright 2003 by JSTOR and the President and Fellows of Harvard College + * ******************************************************************** */ -public class Comment - extends StringValuedToken -{ - /** Creates an instance of a Comment */ - public Comment () - { - super (); - } +package edu.harvard.hul.ois.jhove.module.pdf; + +/** Class for Tokens which represent PDF comments. */ +public class Comment extends StringValuedToken { + /** Creates an instance of a Comment */ + public Comment() { + super(); + } } diff --git a/jhove-modules/pdf-hul/src/main/java/edu/harvard/hul/ois/jhove/module/pdf/CrossRefStream.java b/jhove-modules/pdf-hul/src/main/java/edu/harvard/hul/ois/jhove/module/pdf/CrossRefStream.java index f53103d60..5222cbd92 100644 --- a/jhove-modules/pdf-hul/src/main/java/edu/harvard/hul/ois/jhove/module/pdf/CrossRefStream.java +++ b/jhove-modules/pdf-hul/src/main/java/edu/harvard/hul/ois/jhove/module/pdf/CrossRefStream.java @@ -1,332 +1,293 @@ -/********************************************************************** - * Jhove - JSTOR/Harvard Object Validation Environment - * Copyright 2005 by JSTOR and the President and Fellows of Harvard College - **********************************************************************/ - +/** + * ******************************************************************** Jhove - JSTOR/Harvard Object + * Validation Environment Copyright 2005 by JSTOR and the President and Fellows of Harvard College + * ******************************************************************** + */ package edu.harvard.hul.ois.jhove.module.pdf; import java.io.*; import java.util.*; /** - * This class implements the Cross-Reference Stream, an alternative - * to the Cross-Reference Table starting in PDF 1.4. - * - * A cross-reference stream is identified by a startxref keyword, - * as opposed to the xref keyword which identifies the old-style - * cross-reference table. - * - * JHOVE supports only FlateDecode as a filter for cross-reference - * streams. This is consistent with the implementation limitation - * described in Appendix H of the PDF manual for Acrobat 6 and earlier. - * + * This class implements the Cross-Reference Stream, an alternative to the Cross-Reference Table + * starting in PDF 1.4. * - * @author Gary McGath + *

A cross-reference stream is identified by a startxref keyword, as opposed to the xref keyword + * which identifies the old-style cross-reference table. + * + *

JHOVE supports only FlateDecode as a filter for cross-reference streams. This is consistent + * with the implementation limitation described in Appendix H of the PDF manual for Acrobat 6 and + * earlier. * + * @author Gary McGath */ public class CrossRefStream { - private PdfStream _xstrm; // The underlying Stream object. - private PdfDictionary _dict; - private int _size; - private IndexRange[] _index; - private int[] _fieldSizes; - private int _freeCount; - private Filter[] _filters; - private int _readRange; - private int _readIndex; + private PdfStream _xstrm; // The underlying Stream object. + private PdfDictionary _dict; + private int _size; + private IndexRange[] _index; + private int[] _fieldSizes; + private int _freeCount; + private Filter[] _filters; + private int _readRange; + private int _readIndex; + + private int _bytesPerEntry; + private long _prevXref; // byte offset to previous xref stream, if any + + /* Per-object variables. */ + private int _objType; + private int _objNum; + private int _objField1; + private int _objField2; - private int _bytesPerEntry; - private long _prevXref; // byte offset to previous xref stream, if any + /** Range elements of the _index array: Starting object and number of objects. */ + private class IndexRange { + public int start; + public int len; + } - /* Per-object variables. */ - private int _objType; - private int _objNum; - private int _objField1; - private int _objField2; + /** + * Constructor. + * + * @param xstrm PdfStream object which contains a presumed cross-reference stream. + */ + public CrossRefStream(PdfStream xstrm) { + _xstrm = xstrm; + _dict = xstrm.getDict(); + _freeCount = 0; + } - /** - * Range elements of the _index array: - * Starting object and number of objects. - */ - private class IndexRange { - public int start; - public int len; + /** + * Returns true if the PdfStream object meets the requirements of a cross-reference + * stream. Also extracts information from the dictionary for subsequent processing. + */ + public boolean isValid() { + PdfObject typeObj = _dict.get("Type"); + String typeStr = null; + if (typeObj instanceof PdfSimpleObject) { + typeStr = ((PdfSimpleObject) typeObj).getStringValue(); + if (!("XRef".equals(typeStr))) { + return false; + } + } + if (typeStr == null) { + return false; } - /** - * Constructor. - * - * @param xstrm PdfStream object which contains a presumed - * cross-reference stream. - */ - public CrossRefStream(PdfStream xstrm) { - _xstrm = xstrm; - _dict = xstrm.getDict (); - _freeCount = 0; + PdfObject sizeObj = _dict.get("Size"); + if (sizeObj instanceof PdfSimpleObject) { + _size = ((PdfSimpleObject) sizeObj).getIntValue(); + } else { + return false; } - /** - * Returns true if the PdfStream object meets - * the requirements of a cross-reference stream. Also extracts - * information from the dictionary for subsequent processing. - */ - public boolean isValid () { - PdfObject typeObj = _dict.get ("Type"); - String typeStr = null; - if (typeObj instanceof PdfSimpleObject) { - typeStr = ((PdfSimpleObject) typeObj).getStringValue (); - if (!("XRef".equals (typeStr))) { - return false; - } - } - if (typeStr == null) { - return false; - } + // The Index entry is optional, but must have the right + // format if present: [start1 length1 start2 length2 ...] + PdfObject indexObj = _dict.get("Index"); + if (indexObj instanceof PdfArray) { + Vector indexObjs = ((PdfArray) indexObj).getContent(); + int indexObjCount = indexObjs.size(); - PdfObject sizeObj = _dict.get ("Size"); - if (sizeObj instanceof PdfSimpleObject) { - _size = ((PdfSimpleObject) sizeObj).getIntValue(); - } - else { - return false; - } + // Must contain an even number of objects + if (indexObjCount % 2 != 0) return false; - // The Index entry is optional, but must have the right - // format if present: [start1 length1 start2 length2 ...] - PdfObject indexObj = _dict.get ("Index"); - if (indexObj instanceof PdfArray) { - Vector indexObjs = ((PdfArray) indexObj).getContent(); - int indexObjCount = indexObjs.size(); + _index = new IndexRange[indexObjCount / 2]; + for (int i = 0; i < _index.length; i++) { + _index[i] = new IndexRange(); + _index[i].start = ((PdfSimpleObject) indexObjs.get(i * 2)).getIntValue(); + _index[i].len = ((PdfSimpleObject) indexObjs.get(i * 2 + 1)).getIntValue(); + } + } else { + // Set up default index. + _index = new IndexRange[1]; + _index[0] = new IndexRange(); + _index[0].start = 0; + _index[0].len = _size; + } - // Must contain an even number of objects - if (indexObjCount % 2 != 0) return false; + // Get the field sizes. + PdfObject wObj = _dict.get("W"); + if (wObj instanceof PdfArray) { + Vector vec = ((PdfArray) wObj).getContent(); + int len = vec.size(); + _fieldSizes = new int[len]; + for (int i = 0; i < len; i++) { + PdfSimpleObject ob = (PdfSimpleObject) vec.get(i); + _fieldSizes[i] = ob.getIntValue(); + } + } - _index = new IndexRange[indexObjCount / 2]; - for (int i = 0; i < _index.length; i++) { - _index[i] = new IndexRange(); - _index[i].start = ((PdfSimpleObject)indexObjs.get(i * 2)).getIntValue(); - _index[i].len = ((PdfSimpleObject)indexObjs.get(i * 2 + 1)).getIntValue(); - } - } - else { - // Set up default index. - _index = new IndexRange[1]; - _index[0] = new IndexRange(); - _index[0].start = 0; - _index[0].len = _size; - } + // Get the offset to the previous xref stream, if any. + PdfObject prevObj = _dict.get("Prev"); + if (prevObj instanceof PdfSimpleObject) { + _prevXref = ((PdfSimpleObject) prevObj).getIntValue(); + } else { + _prevXref = -1; + } - // Get the field sizes. - PdfObject wObj = _dict.get ("W"); - if (wObj instanceof PdfArray) { - Vector vec = ((PdfArray) wObj).getContent (); - int len = vec.size (); - _fieldSizes = new int[len]; - for (int i = 0; i < len; i++) { - PdfSimpleObject ob = (PdfSimpleObject) vec.get (i); - _fieldSizes[i] = ob.getIntValue (); - } - } + // Get the filter, for subsequent decompression. + // We're guaranteed by the spec that this won't be a decryption + // filter. + _filters = _xstrm.getFilters(); + // Why isn't this being used? - // Get the offset to the previous xref stream, if any. - PdfObject prevObj = _dict.get ("Prev"); - if (prevObj instanceof PdfSimpleObject) { - _prevXref = ((PdfSimpleObject) prevObj).getIntValue(); - } - else { - _prevXref = -1; - } + // passed all tests + return true; + } - // Get the filter, for subsequent decompression. - // We're guaranteed by the spec that this won't be a decryption - // filter. - _filters = _xstrm.getFilters(); - // Why isn't this being used? + /** + * Prepares for reading the Stream. If the filter list includes one which we don't support, throws + * a PdfException. + */ + public void initRead(RandomAccessFile raf) throws IOException { + Stream strm = _xstrm.getStream(); + strm.setFilters(_xstrm.getFilters()); + strm.initRead(raf); + _readRange = 0; + _readIndex = 0; - // passed all tests - return true; + /* Calculate the total bytes per entry. This may have + * some utility. */ + _bytesPerEntry = 0; + for (int i = 0; i < _fieldSizes.length; i++) { + _bytesPerEntry += _fieldSizes[i]; } + } + + /** + * Reads the next object in the stream. + * + *

After calling readObject, it is possible to call accessors to get information + * about the object. For the moment, we punt on the question of how to deal with Object Streams. + * + *

Free objects are skipped over while being counted. After readNextObject returns + * false, the caller may call getFreeCount to determine the number of + * free objects. + * + * @return true if there is an object, false if no more objects are + * available. + */ + public boolean readNextObject() throws IOException { + /* Get the field type. */ + int wid; + Stream is = _xstrm.getStream(); + int i; + int b; - /** - * Prepares for reading the Stream. - * If the filter list includes one which we don't support, throws a - * PdfException. - */ - public void initRead (RandomAccessFile raf) - throws IOException - { - Stream strm = _xstrm.getStream (); - strm.setFilters (_xstrm.getFilters ()); - strm.initRead (raf); - _readRange = 0; - _readIndex = 0; - - /* Calculate the total bytes per entry. This may have - * some utility. */ - _bytesPerEntry = 0; - for (int i = 0; i < _fieldSizes.length; i++) { - _bytesPerEntry += _fieldSizes[i]; + for (; ; ) { + /* Loop till we find an actual object; we just count + * type 0's, which are free entries. */ + wid = _fieldSizes[0]; + if (_readIndex++ >= _index[_readRange].len) { + _readIndex = 1; + if (_readRange++ >= _index.length) { + return false; // Read full complement } - } + } + if (wid != 0) { + /* "Fields requiring more than one byte are stored + * with the high-order byte first." */ + _objType = 0; + for (i = 0; i < wid; i++) { + b = is.read(); + if (b < 0) { + return false; + } + _objType = _objType * 256 + b; + } + } else { + _objType = 1; // Default if field width is 0 + } - /** - * Reads the next object in the stream. - * - * After calling readObject, it is possible to - * call accessors to get information about the object. - * For the moment, we punt on the question of how to deal with - * Object Streams. - * - * Free objects are skipped over while being counted. After - * readNextObject returns false, the caller - * may call getFreeCount to determine the - * number of free objects. - * - * @return true if there is an object, false - * if no more objects are available. - */ - public boolean readNextObject () throws IOException - { - /* Get the field type. */ - int wid; - Stream is = _xstrm.getStream (); - int i; - int b; - - for (;;) { - /* Loop till we find an actual object; we just count - * type 0's, which are free entries. */ - wid = _fieldSizes[0]; - if (_readIndex++ >= _index[_readRange].len) { - _readIndex = 1; - if (_readRange++ >= _index.length) { - return false; // Read full complement - } - } - if (wid != 0) { - /* "Fields requiring more than one byte are stored - * with the high-order byte first." */ - _objType = 0; - for (i = 0; i < wid; i++) { - b = is.read (); - if (b < 0) { - return false; - } - _objType = _objType * 256 + b; - } - } - else { - _objType = 1; // Default if field width is 0 - } - - wid = _fieldSizes[1]; - _objField1 = 0; - for (i = 0; i < wid; i++) { - b = is.read (); - if (b < 0) { - return false; - } - _objField1 = _objField1 * 256 + b; - } - - wid = _fieldSizes[2]; - _objField2 = 0; - for (i = 0; i < wid; i++) { - b = is.read (); - if (b < 0) { - return false; - } - _objField2 = _objField2 * 256 + b; - } - - if (_objType != 0) { - _objNum = _index[_readRange].start + _readIndex - 1; - return true; - } - ++_freeCount; + wid = _fieldSizes[1]; + _objField1 = 0; + for (i = 0; i < wid; i++) { + b = is.read(); + if (b < 0) { + return false; } - } + _objField1 = _objField1 * 256 + b; + } - /** - * Returns number of the last object read by - * readNextObject (). - * Do not call if readNextObject () - * returns false. - */ - public int getObjNum () - { - return _objNum; - } + wid = _fieldSizes[2]; + _objField2 = 0; + for (i = 0; i < wid; i++) { + b = is.read(); + if (b < 0) { + return false; + } + _objField2 = _objField2 * 256 + b; + } - /** - * Returns true if the last object read by - * readNextObject () is a compressed object. - * Do not call if readNextObject () - * returns false. - */ - public boolean isObjCompressed () - { - return (_objType == 2); + if (_objType != 0) { + _objNum = _index[_readRange].start + _readIndex - 1; + return true; + } + ++_freeCount; } + } - /** - * Returns the number of free objects detected. This may - * be called after readNextObject returns - * false, signifying that all the objects - * have been read and all the free objects counted. - */ - public int getFreeCount () - { - return _freeCount; - } + /** + * Returns number of the last object read by readNextObject (). Do not call if + * readNextObject () returns false. + */ + public int getObjNum() { + return _objNum; + } - /** - * Returns the total number of objects in the document's - * cross-reference table at the time this stream was written. - */ - public int getCrossRefTableSize() - { - return _size; - } + /** + * Returns true if the last object read by readNextObject () is a + * compressed object. Do not call if readNextObject () returns false. + */ + public boolean isObjCompressed() { + return (_objType == 2); + } - /** - * Returns the offset of the last object read. - * This is meaningful only if the last object read - * was type 1 (uncompressed). - */ - public int getOffset () - { - return _objField1; - } + /** + * Returns the number of free objects detected. This may be called after readNextObject + * returns false, signifying that all the objects have been read and all the + * free objects counted. + */ + public int getFreeCount() { + return _freeCount; + } - /** - * Returns the object number of the content stream in - * which this object is stored. - * This is meaningful only if the last object read - * was type 2 (compressed in content stream). - */ - public int getContentStreamObjNum () - { - return _objField1; - } + /** + * Returns the total number of objects in the document's cross-reference table at the time this + * stream was written. + */ + public int getCrossRefTableSize() { + return _size; + } - /** - * Returns the offset of the previous cross-reference stream, - * or -1 if none is specified. - */ - public long getPrevXref () - { - return _prevXref; - } + /** + * Returns the offset of the last object read. This is meaningful only if the last object read was + * type 1 (uncompressed). + */ + public int getOffset() { + return _objField1; + } - /** - * Returns the content stream index of the last object read. - * This is meaningful only if the last object read - * was type 2 (compressed in content stream). - */ - public int getContentStreamIndex () - { - return _objField2; - } + /** + * Returns the object number of the content stream in which this object is stored. This is + * meaningful only if the last object read was type 2 (compressed in content stream). + */ + public int getContentStreamObjNum() { + return _objField1; + } + + /** Returns the offset of the previous cross-reference stream, or -1 if none is specified. */ + public long getPrevXref() { + return _prevXref; + } + + /** + * Returns the content stream index of the last object read. This is meaningful only if the last + * object read was type 2 (compressed in content stream). + */ + public int getContentStreamIndex() { + return _objField2; + } } diff --git a/jhove-modules/pdf-hul/src/main/java/edu/harvard/hul/ois/jhove/module/pdf/Destination.java b/jhove-modules/pdf-hul/src/main/java/edu/harvard/hul/ois/jhove/module/pdf/Destination.java index cf07796b0..6ba4e5753 100644 --- a/jhove-modules/pdf-hul/src/main/java/edu/harvard/hul/ois/jhove/module/pdf/Destination.java +++ b/jhove-modules/pdf-hul/src/main/java/edu/harvard/hul/ois/jhove/module/pdf/Destination.java @@ -1,141 +1,127 @@ -/********************************************************************** - * Jhove - JSTOR/Harvard Object Validation Environment - * Copyright 2003 by JSTOR and the President and Fellows of Harvard College - **********************************************************************/ - +/** + * ******************************************************************** Jhove - JSTOR/Harvard Object + * Validation Environment Copyright 2003 by JSTOR and the President and Fellows of Harvard College + * ******************************************************************** + */ package edu.harvard.hul.ois.jhove.module.pdf; -import java.io.IOException; - import edu.harvard.hul.ois.jhove.messages.JhoveMessage; import edu.harvard.hul.ois.jhove.messages.JhoveMessages; import edu.harvard.hul.ois.jhove.module.PdfModule; +import java.io.IOException; /** - * Class encapsulating PDF destination objects, which refer - * to a page in the document. + * Class encapsulating PDF destination objects, which refer to a page in the document. * - * We need to make two different kinds of distinctions: between - * destinations that make an indirect and a direct reference to - * a page; and between destinations that have been reached by - * a direct and an indirect reference. The PDF spec allows - * only one level of indirection, so each of these forms has - * options not available to the other. + *

We need to make two different kinds of distinctions: between destinations that make an + * indirect and a direct reference to a page; and between destinations that have been reached by a + * direct and an indirect reference. The PDF spec allows only one level of indirection, so each of + * these forms has options not available to the other. * - * We call a destination which has been reached directly an - * unnamed destination, and one which has been reached indirectly - * a named destination. We call a destination which has an - * indirect target an indirect destination, and one which has - * a page object as a target a direct destination. Applying - * the PDF documentation, we find that a destination can never - * be both named and indirect. In other words, there are really - * two cases, involving three kinds of destinations: + *

We call a destination which has been reached directly an unnamed destination, and one which + * has been reached indirectly a named destination. We call a destination which has an indirect + * target an indirect destination, and one which has a page object as a target a direct destination. + * Applying the PDF documentation, we find that a destination can never be both named and indirect. + * In other words, there are really two cases, involving three kinds of destinations: * *

    - *
  • An unnamed, direct destination, which refers to the page - * object. - *
  • An unnamed, indirect destination, which refers to a - * named, direct destination, which refers to the page object. + *
  • An unnamed, direct destination, which refers to the page object. + *
  • An unnamed, indirect destination, which refers to a named, direct destination, which refers + * to the page object. *
*/ public final class Destination { - /****************************************************************** - * PRIVATE CLASS FIELDS. - ******************************************************************/ + /** + * **************************************************************** PRIVATE CLASS FIELDS. + * **************************************************************** + */ - /* Flag indicating destination is indirect. */ - private boolean _indirect = false; + /* Flag indicating destination is indirect. */ + private boolean _indirect = false; - /* Name of indirect destination. */ - private PdfSimpleObject _indirectDest; + /* Name of indirect destination. */ + private PdfSimpleObject _indirectDest; - /* Page object for explicit destination. */ - private PdfDictionary _pageDest; + /* Page object for explicit destination. */ + private PdfDictionary _pageDest; - /** - * Constructor. If this is a named destination, the destObj - * may be a PdfArray or a PdfDictionary; if this is not a - * named destination, the destObj may be a PdfSimpleObject - * (encapsulating a Literal or Name) or a PdfDictionary. - * - * @param destObj - * The destination object - * @param module - * The invoking PdfModule - * @param named - * Flag indicating whether this object came - * from a named destination. - */ - public Destination(final PdfObject destObj, final PdfModule module, - final boolean named) throws PdfException { - if (destObj == null) { - throw new IllegalArgumentException("Parameter destObj cannot be null."); - } - if (!named && destObj instanceof PdfSimpleObject) { - _indirect = true; - _indirectDest = (PdfSimpleObject) destObj; - return; - } - PdfArray destArray = null; - try { - if (destObj instanceof PdfArray) { - destArray = (PdfArray) destObj; - // We extract only the page reference, not the view. - _pageDest = findDirectDest(module, destArray); - } else if (named && destObj instanceof PdfDictionary) { - destArray = (PdfArray) ((PdfDictionary) destObj).get("D"); - // The D entry is just like the array above. - _pageDest = findDirectDest(module, destArray); - } else { - throw new PdfInvalidException(MessageConstants.PDF_HUL_1); // PDF-HUL-1 - } - } catch (ClassCastException e) { - throw new PdfInvalidException(MessageConstants.PDF_HUL_2); // PDF-HUL-2 - } catch (IOException e) { - JhoveMessage message = JhoveMessages.getMessageInstance( - MessageConstants.PDF_HUL_3.getId(), - String.format(MessageConstants.PDF_HUL_3.getMessage(), // PDF-HUL-3 - e.getLocalizedMessage(), Integer.valueOf(destArray._objNumber))); - throw new PdfInvalidException(message); - } - } + /** + * Constructor. If this is a named destination, the destObj may be a PdfArray or a PdfDictionary; + * if this is not a named destination, the destObj may be a PdfSimpleObject (encapsulating a + * Literal or Name) or a PdfDictionary. + * + * @param destObj The destination object + * @param module The invoking PdfModule + * @param named Flag indicating whether this object came from a named destination. + */ + public Destination(final PdfObject destObj, final PdfModule module, final boolean named) + throws PdfException { + if (destObj == null) { + throw new IllegalArgumentException("Parameter destObj cannot be null."); + } + if (!named && destObj instanceof PdfSimpleObject) { + _indirect = true; + _indirectDest = (PdfSimpleObject) destObj; + return; + } + PdfArray destArray = null; + try { + if (destObj instanceof PdfArray) { + destArray = (PdfArray) destObj; + // We extract only the page reference, not the view. + _pageDest = findDirectDest(module, destArray); + } else if (named && destObj instanceof PdfDictionary) { + destArray = (PdfArray) ((PdfDictionary) destObj).get("D"); + // The D entry is just like the array above. + _pageDest = findDirectDest(module, destArray); + } else { + throw new PdfInvalidException(MessageConstants.PDF_HUL_1); // PDF-HUL-1 + } + } catch (ClassCastException e) { + throw new PdfInvalidException(MessageConstants.PDF_HUL_2); // PDF-HUL-2 + } catch (IOException e) { + JhoveMessage message = + JhoveMessages.getMessageInstance( + MessageConstants.PDF_HUL_3.getId(), + String.format( + MessageConstants.PDF_HUL_3.getMessage(), // PDF-HUL-3 + e.getLocalizedMessage(), + Integer.valueOf(destArray._objNumber))); + throw new PdfInvalidException(message); + } + } - /** - * Returns true if the destination is indirect. - */ - public boolean isIndirect() { - return _indirect; - } + /** Returns true if the destination is indirect. */ + public boolean isIndirect() { + return _indirect; + } - /** - * Returns the string naming the indirect destination. - * Returns null if the destination is not indirect. - */ - public PdfSimpleObject getIndirectDest() { - return _indirectDest; - } + /** + * Returns the string naming the indirect destination. Returns null if the destination is not + * indirect. + */ + public PdfSimpleObject getIndirectDest() { + return _indirectDest; + } - /** - * Returns the page object dictionary if the destination - * is direct. Returns null if the destination is not - * direct. - */ - public PdfDictionary getPageDest() { - return _pageDest; - } + /** + * Returns the page object dictionary if the destination is direct. Returns null if the + * destination is not direct. + */ + public PdfDictionary getPageDest() { + return _pageDest; + } - /** - * Returns the object number of the page object dictionary - * if the destination is direct. Throws a NullPointerException - * otherwise. - */ - public int getPageDestObjNumber() throws NullPointerException { - return _pageDest.getObjNumber(); - } + /** + * Returns the object number of the page object dictionary if the destination is direct. Throws a + * NullPointerException otherwise. + */ + public int getPageDestObjNumber() throws NullPointerException { + return _pageDest.getObjNumber(); + } - private static PdfDictionary findDirectDest(final PdfModule module, - final PdfArray destObj) throws PdfException, IOException { - return (PdfDictionary) module - .resolveIndirectObject(destObj.getContent().elementAt(0)); - } + private static PdfDictionary findDirectDest(final PdfModule module, final PdfArray destObj) + throws PdfException, IOException { + return (PdfDictionary) module.resolveIndirectObject(destObj.getContent().elementAt(0)); + } } diff --git a/jhove-modules/pdf-hul/src/main/java/edu/harvard/hul/ois/jhove/module/pdf/DictionaryEnd.java b/jhove-modules/pdf-hul/src/main/java/edu/harvard/hul/ois/jhove/module/pdf/DictionaryEnd.java index 88bc2d06b..ce32aee2b 100644 --- a/jhove-modules/pdf-hul/src/main/java/edu/harvard/hul/ois/jhove/module/pdf/DictionaryEnd.java +++ b/jhove-modules/pdf-hul/src/main/java/edu/harvard/hul/ois/jhove/module/pdf/DictionaryEnd.java @@ -1,19 +1,14 @@ -/********************************************************************** - * Jhove - JSTOR/Harvard Object Validation Environment - * Copyright 2003 by JSTOR and the President and Fellows of Harvard College - **********************************************************************/ - -package edu.harvard.hul.ois.jhove.module.pdf; - /** - * Class for Tokens which represent the {@code >>} sequence that ends a Dictionary. + * ******************************************************************** Jhove - JSTOR/Harvard Object + * Validation Environment Copyright 2003 by JSTOR and the President and Fellows of Harvard College + * ******************************************************************** */ -public class DictionaryEnd - extends Token -{ - /** Creates an instance of a DictionaryEnd */ - public DictionaryEnd () - { - super (); - } +package edu.harvard.hul.ois.jhove.module.pdf; + +/** Class for Tokens which represent the {@code >>} sequence that ends a Dictionary. */ +public class DictionaryEnd extends Token { + /** Creates an instance of a DictionaryEnd */ + public DictionaryEnd() { + super(); + } } diff --git a/jhove-modules/pdf-hul/src/main/java/edu/harvard/hul/ois/jhove/module/pdf/DictionaryStart.java b/jhove-modules/pdf-hul/src/main/java/edu/harvard/hul/ois/jhove/module/pdf/DictionaryStart.java index fb347b0af..c3e51437c 100644 --- a/jhove-modules/pdf-hul/src/main/java/edu/harvard/hul/ois/jhove/module/pdf/DictionaryStart.java +++ b/jhove-modules/pdf-hul/src/main/java/edu/harvard/hul/ois/jhove/module/pdf/DictionaryStart.java @@ -1,19 +1,14 @@ -/********************************************************************** - * Jhove - JSTOR/Harvard Object Validation Environment - * Copyright 2003 by JSTOR and the President and Fellows of Harvard College - **********************************************************************/ - -package edu.harvard.hul.ois.jhove.module.pdf; - /** - * Class for Tokens which represent the {@code <<} sequence that opens a Dictionary. + * ******************************************************************** Jhove - JSTOR/Harvard Object + * Validation Environment Copyright 2003 by JSTOR and the President and Fellows of Harvard College + * ******************************************************************** */ -public class DictionaryStart - extends Token -{ - /** Creates an instance of a DictionaryStart */ - public DictionaryStart () - { - super (); - } +package edu.harvard.hul.ois.jhove.module.pdf; + +/** Class for Tokens which represent the {@code <<} sequence that opens a Dictionary. */ +public class DictionaryStart extends Token { + /** Creates an instance of a DictionaryStart */ + public DictionaryStart() { + super(); + } } diff --git a/jhove-modules/pdf-hul/src/main/java/edu/harvard/hul/ois/jhove/module/pdf/DocNode.java b/jhove-modules/pdf-hul/src/main/java/edu/harvard/hul/ois/jhove/module/pdf/DocNode.java index d16ea7e07..4719f1dd4 100644 --- a/jhove-modules/pdf-hul/src/main/java/edu/harvard/hul/ois/jhove/module/pdf/DocNode.java +++ b/jhove-modules/pdf-hul/src/main/java/edu/harvard/hul/ois/jhove/module/pdf/DocNode.java @@ -1,178 +1,145 @@ -/********************************************************************** - * Jhove - JSTOR/Harvard Object Validation Environment - * Copyright 2003 by JSTOR and the President and Fellows of Harvard College - **********************************************************************/ - +/** + * ******************************************************************** Jhove - JSTOR/Harvard Object + * Validation Environment Copyright 2003 by JSTOR and the President and Fellows of Harvard College + * ******************************************************************** + */ package edu.harvard.hul.ois.jhove.module.pdf; import edu.harvard.hul.ois.jhove.module.PdfModule; import java.io.*; -/** - * Abstract class for nodes of a PDF document tree. - */ -public abstract class DocNode -{ - /** The PdfModule this node is associated with. */ - protected PdfModule _module; - - /** The parent node of this node. */ - protected PageTreeNode _parent; - - /** The dictionary which defines this node. */ - protected PdfDictionary _dict; - - /** True if this node is a PageObject. */ - protected boolean _pageObjectFlag; - - /** Set to true when all subnodes of this node - * have been iterated through following a StartWalk. */ - protected boolean _walkFinished; - - /** - * Superclass constructor. - * @param module The PdfModule under which we're operating - * @param parent The parent node in the document tree; - * may be null only for the root node - * @param dict The dictionary object on which this node - * is based - */ - public DocNode (PdfModule module, - PageTreeNode parent, - PdfDictionary dict) throws PdfMalformedException - { - if (dict == null) { - throw new PdfMalformedException (MessageConstants.PDF_HUL_4); // PDF-HUL-4 - } - _module = module; - _parent = parent; - _dict = dict; - } - - /** - * Returns true if this node is a PageObject. - */ - public boolean isPageObject () - { - return _pageObjectFlag; - } - - /** - * Initialize an iterator through the descendants of this node. - */ - public abstract void startWalk (); - - /** - * Get the next PageObject which is under this node. - */ - public abstract PageObject nextPageObject () throws PdfMalformedException; - - /** - * Get the next DocNode which is under this node. - * All PageTreeNodes and PageObjects are eventually returned - * by walking through a structure with nextNode. - */ - public abstract DocNode nextDocNode () throws PdfMalformedException; - - /** - * Returns the parent of this node. - */ - public DocNode getParent () - { - return _parent; - } +/** Abstract class for nodes of a PDF document tree. */ +public abstract class DocNode { + /** The PdfModule this node is associated with. */ + protected PdfModule _module; + + /** The parent node of this node. */ + protected PageTreeNode _parent; + + /** The dictionary which defines this node. */ + protected PdfDictionary _dict; + + /** True if this node is a PageObject. */ + protected boolean _pageObjectFlag; - /** - * Returns the page object or page tree node dictionary from - * which this object was constructed. - */ - public PdfDictionary getDict () - { - return _dict; + /** + * Set to true when all subnodes of this node have been iterated through following a StartWalk. + */ + protected boolean _walkFinished; + + /** + * Superclass constructor. + * + * @param module The PdfModule under which we're operating + * @param parent The parent node in the document tree; may be null only for the root node + * @param dict The dictionary object on which this node is based + */ + public DocNode(PdfModule module, PageTreeNode parent, PdfDictionary dict) + throws PdfMalformedException { + if (dict == null) { + throw new PdfMalformedException(MessageConstants.PDF_HUL_4); // PDF-HUL-4 } + _module = module; + _parent = parent; + _dict = dict; + } + + /** Returns true if this node is a PageObject. */ + public boolean isPageObject() { + return _pageObjectFlag; + } + + /** Initialize an iterator through the descendants of this node. */ + public abstract void startWalk(); + + /** Get the next PageObject which is under this node. */ + public abstract PageObject nextPageObject() throws PdfMalformedException; + + /** + * Get the next DocNode which is under this node. All PageTreeNodes and PageObjects are eventually + * returned by walking through a structure with nextNode. + */ + public abstract DocNode nextDocNode() throws PdfMalformedException; + + /** Returns the parent of this node. */ + public DocNode getParent() { + return _parent; + } + /** + * Returns the page object or page tree node dictionary from which this object was constructed. + */ + public PdfDictionary getDict() { + return _dict; + } - /** - * Get the Resources dictionary. Either a PageTreeNode or - * a PageObject can have a Resources dictionary. Returns - * null if there is no Resources dictionary. The object - * may be referenced indirectly. - */ - public PdfDictionary getResources () throws PdfException - { - PdfObject resdict = _dict.get ("Resources"); - try { - resdict = _module.resolveIndirectObject (resdict); - return (PdfDictionary) resdict; - } - catch (ClassCastException | IOException f) { - throw new PdfInvalidException(MessageConstants.PDF_HUL_5); // PDF-HUL-5 - } + /** + * Get the Resources dictionary. Either a PageTreeNode or a PageObject can have a Resources + * dictionary. Returns null if there is no Resources dictionary. The object may be referenced + * indirectly. + */ + public PdfDictionary getResources() throws PdfException { + PdfObject resdict = _dict.get("Resources"); + try { + resdict = _module.resolveIndirectObject(resdict); + return (PdfDictionary) resdict; + } catch (ClassCastException | IOException f) { + throw new PdfInvalidException(MessageConstants.PDF_HUL_5); // PDF-HUL-5 } + } - /** - * Returns the dictionary of fonts within the node's Resources - * dictionary, if both exist. Otherwise returns null. - * The dictionary will most often have indirect object - * references as values. What is returned is not a - * Font dictionary, but rather a dictionary of Font - * dictionaries. - */ - public PdfDictionary getFontResources () throws PdfException - { - PdfDictionary resdict = getResources (); - if (resdict == null) { - return null; - } - PdfObject fontdict = resdict.get("Font"); - try { - fontdict = _module.resolveIndirectObject (fontdict); - return (PdfDictionary) fontdict; - } - catch (ClassCastException | IOException e) { - throw new PdfMalformedException - (MessageConstants.PDF_HUL_6); // PDF-HUL-6 - } + /** + * Returns the dictionary of fonts within the node's Resources dictionary, if both exist. + * Otherwise returns null. The dictionary will most often have indirect object references as + * values. What is returned is not a Font dictionary, but rather a dictionary of Font + * dictionaries. + */ + public PdfDictionary getFontResources() throws PdfException { + PdfDictionary resdict = getResources(); + if (resdict == null) { + return null; } - - /** - * Get the MediaBox of this node. MediaBox is an inheritable - * property, so it walks up the chain of ancestors if it doesn't - * contain one. Returns null if none. Throws a - * PdfInvalidException if an invalid MediaBox is found. - */ - public PdfArray getMediaBox () throws PdfInvalidException - { - PdfArray mbox = null; - try { - mbox = (PdfArray) get ("MediaBox", true); - } - catch (ClassCastException e) { - throw new PdfInvalidException (MessageConstants.PDF_HUL_7); // PDF-HUL-7 - } - if (mbox != null && mbox.toRectangle () == null) { - // There's a MediaBox, but it's not a rectangle - throw new PdfInvalidException (MessageConstants.PDF_HUL_8); // PDF-HUL-8 - } - return mbox; + PdfObject fontdict = resdict.get("Font"); + try { + fontdict = _module.resolveIndirectObject(fontdict); + return (PdfDictionary) fontdict; + } catch (ClassCastException | IOException e) { + throw new PdfMalformedException(MessageConstants.PDF_HUL_6); // PDF-HUL-6 } + } - /** - * Get an named property. If this object doesn't - * have the specified property and inheritable - * is true, walks up the chain of ancestors - * to try to find one. If no ancestor has the property or - * inheritable is false, returns null. - */ - public PdfObject get (String key, boolean inheritable) - { - PdfObject val = _dict.get (key); - if (val == null) { - if (_parent == null || !inheritable) { - return null; - } - return _parent.get (key, inheritable); - } - return val; + /** + * Get the MediaBox of this node. MediaBox is an inheritable property, so it walks up the chain of + * ancestors if it doesn't contain one. Returns null if none. Throws a PdfInvalidException if an + * invalid MediaBox is found. + */ + public PdfArray getMediaBox() throws PdfInvalidException { + PdfArray mbox = null; + try { + mbox = (PdfArray) get("MediaBox", true); + } catch (ClassCastException e) { + throw new PdfInvalidException(MessageConstants.PDF_HUL_7); // PDF-HUL-7 + } + if (mbox != null && mbox.toRectangle() == null) { + // There's a MediaBox, but it's not a rectangle + throw new PdfInvalidException(MessageConstants.PDF_HUL_8); // PDF-HUL-8 } + return mbox; + } + + /** + * Get an named property. If this object doesn't have the specified property and inheritable + * is true, walks up the chain of ancestors to try to find one. If no ancestor has the + * property or inheritable is false, returns null. + */ + public PdfObject get(String key, boolean inheritable) { + PdfObject val = _dict.get(key); + if (val == null) { + if (_parent == null || !inheritable) { + return null; + } + return _parent.get(key, inheritable); + } + return val; + } } diff --git a/jhove-modules/pdf-hul/src/main/java/edu/harvard/hul/ois/jhove/module/pdf/FileSpecification.java b/jhove-modules/pdf-hul/src/main/java/edu/harvard/hul/ois/jhove/module/pdf/FileSpecification.java index 5d83a9342..b88f96c1c 100644 --- a/jhove-modules/pdf-hul/src/main/java/edu/harvard/hul/ois/jhove/module/pdf/FileSpecification.java +++ b/jhove-modules/pdf-hul/src/main/java/edu/harvard/hul/ois/jhove/module/pdf/FileSpecification.java @@ -1,60 +1,52 @@ -/********************************************************************** - * Jhove - JSTOR/Harvard Object Validation Environment - * Copyright 2003 by JSTOR and the President and Fellows of Harvard College - **********************************************************************/ - +/** + * ******************************************************************** Jhove - JSTOR/Harvard Object + * Validation Environment Copyright 2003 by JSTOR and the President and Fellows of Harvard College + * ******************************************************************** + */ package edu.harvard.hul.ois.jhove.module.pdf; /** - * A class which encapsulates a file specification in PDF. A file - * specification may be given as either a string or a dictionary. - * The specification is converted to a string according to the following - * rules: If a PDF string object is the file specifier, that string - * is used, without attempting to convert file separators to the local - * file system. If a PDF dictionary is used, one of the following is - * used, in decreasing order of preference: + * A class which encapsulates a file specification in PDF. A file specification may be given as + * either a string or a dictionary. The specification is converted to a string according to the + * following rules: If a PDF string object is the file specifier, that string is used, without + * attempting to convert file separators to the local file system. If a PDF dictionary is used, one + * of the following is used, in decreasing order of preference: * - *
    - *
  • The system-neutral file specification string - *
  • The Unix file specification string - *
  • The DOS file specification string - *
  • The Macintosh file specification string - *
+ *
    + *
  • The system-neutral file specification string + *
  • The Unix file specification string + *
  • The DOS file specification string + *
  • The Macintosh file specification string + *
*/ -public enum FileSpecification -{ - INSTANCE; - private static final String[] dictKeys = {"F", "Unix", "DOS", "Mac"}; - /** - * Constructor. - * - * @param obj A PdfDictionary with the file specification under the - * key "F", "Unix", "DOS", or "Mac"; or - * a PdfSimpleObject whose string value is the - * file specification. If obj is - * a dictionary and more than one key is specified, - * then the first of the keys F, Unix, DOS, and Mac - * to be found is used. - */ - public static String getFileSpecString (PdfObject obj) throws PdfInvalidException - { - if (obj instanceof PdfSimpleObject) { - return ((PdfSimpleObject) obj).getStringValue (); - } - try { - if (obj instanceof PdfDictionary) { - PdfDictionary dictObj = (PdfDictionary) obj; - for (final String dictKey : dictKeys) { - PdfSimpleObject pathObj = (PdfSimpleObject) dictObj.get(dictKey); - if (pathObj != null) { - return pathObj.getStringValue(); - } - } - } - } - catch (ClassCastException e) { - throw new PdfInvalidException(MessageConstants.PDF_HUL_9); // PDF-HUL-9 +public enum FileSpecification { + INSTANCE; + private static final String[] dictKeys = {"F", "Unix", "DOS", "Mac"}; + /** + * Constructor. + * + * @param obj A PdfDictionary with the file specification under the key "F", "Unix", "DOS", or + * "Mac"; or a PdfSimpleObject whose string value is the file specification. If obj + * is a dictionary and more than one key is specified, then the first of the keys F, + * Unix, DOS, and Mac to be found is used. + */ + public static String getFileSpecString(PdfObject obj) throws PdfInvalidException { + if (obj instanceof PdfSimpleObject) { + return ((PdfSimpleObject) obj).getStringValue(); + } + try { + if (obj instanceof PdfDictionary) { + PdfDictionary dictObj = (PdfDictionary) obj; + for (final String dictKey : dictKeys) { + PdfSimpleObject pathObj = (PdfSimpleObject) dictObj.get(dictKey); + if (pathObj != null) { + return pathObj.getStringValue(); + } } - return null; + } + } catch (ClassCastException e) { + throw new PdfInvalidException(MessageConstants.PDF_HUL_9); // PDF-HUL-9 } + return null; + } } diff --git a/jhove-modules/pdf-hul/src/main/java/edu/harvard/hul/ois/jhove/module/pdf/FileTokenizer.java b/jhove-modules/pdf-hul/src/main/java/edu/harvard/hul/ois/jhove/module/pdf/FileTokenizer.java index 8d7baf7fa..7873ad993 100644 --- a/jhove-modules/pdf-hul/src/main/java/edu/harvard/hul/ois/jhove/module/pdf/FileTokenizer.java +++ b/jhove-modules/pdf-hul/src/main/java/edu/harvard/hul/ois/jhove/module/pdf/FileTokenizer.java @@ -1,136 +1,115 @@ -/********************************************************************** - * Jhove - JSTOR/Harvard Object Validation Environment - * Copyright 2005 by JSTOR and the President and Fellows of Harvard College - **********************************************************************/ - +/** + * ******************************************************************** Jhove - JSTOR/Harvard Object + * Validation Environment Copyright 2005 by JSTOR and the President and Fellows of Harvard College + * ******************************************************************** + */ package edu.harvard.hul.ois.jhove.module.pdf; import java.io.*; /** - * * Tokenizer subclass which gets data from a RandomAccessFile. - * @author Gary McGath * + * @author Gary McGath */ public class FileTokenizer extends Tokenizer { - - /** Current offset to start of bytes stored in _fileBuffer */ - private long _fileBufferPositionOffset; - - /** File buffer */ - private byte[] _fileBuffer; - - /** Size for file buffer */ - private static final int FILEBUFSIZE = 65536; - - /** Number of valid bytes in fileBuffer */ - private int _fileBufferBytes; - - /** Offset to next valid byte in fileBuffer */ - private int _fileBufferOffset; - - - public FileTokenizer (RandomAccessFile file) - { - super (); - _file = file; - _fileBufferPositionOffset = -1; - _fileBuffer = new byte[FILEBUFSIZE]; - initFileBuffer (); + /** Current offset to start of bytes stored in _fileBuffer */ + private long _fileBufferPositionOffset; + + /** File buffer */ + private byte[] _fileBuffer; + + /** Size for file buffer */ + private static final int FILEBUFSIZE = 65536; + + /** Number of valid bytes in fileBuffer */ + private int _fileBufferBytes; + + /** Offset to next valid byte in fileBuffer */ + private int _fileBufferOffset; + + public FileTokenizer(RandomAccessFile file) { + super(); + _file = file; + _fileBufferPositionOffset = -1; + _fileBuffer = new byte[FILEBUFSIZE]; + initFileBuffer(); + } + + private void initFileBuffer() { + _fileBufferBytes = 0; + _fileBufferOffset = 0; + } + + /** Gets the current position in the file. This method is aware of buffering. */ + public long getFilePos() { + return _fileBufferPositionOffset + _fileBufferOffset; + } + + /** Gets a character from the file, using a buffer. */ + @Override + public int readChar() throws IOException { + if (_fileBufferOffset >= _fileBufferBytes) { + // If the byte size is 0, we can assume a seek was already + // done, but otherwise we must seek safety. + if (_fileBufferBytes > 0) { + long newOffset = _fileBufferPositionOffset + _fileBufferOffset; + _file.seek(newOffset); + _fileBufferPositionOffset = newOffset; + } + _fileBufferBytes = _file.read(_fileBuffer); + if (_fileBufferBytes <= 0) { + throw new EOFException(); + } + _fileBufferOffset = 0; } - - private void initFileBuffer () - { - _fileBufferBytes = 0; - _fileBufferOffset = 0; + return (_fileBuffer[_fileBufferOffset++] & 0XFF); + } + + /** + * Set the Tokenizer to a new position in the file. + * + * @param offset The offset in bytes from the start of the file. + */ + @Override + public void seek(long offset) throws IOException { + if (_fileBufferPositionOffset >= 0 + && offset >= _fileBufferPositionOffset + && offset < _fileBufferPositionOffset + _fileBufferBytes) { + // Reposition within the buffer + _fileBufferOffset = (int) (offset - _fileBufferPositionOffset); + } else { + _file.seek(offset); + initFileBuffer(); + _fileBufferPositionOffset = offset; } - - - /** Gets the current position in the file. This method is - * aware of buffering. */ - public long getFilePos () - { - return _fileBufferPositionOffset + _fileBufferOffset; + seekReset(offset); + } + + /** Back up a byte so it will be read again. */ + @Override + public void backupChar() { + _fileBufferOffset--; + } + + /** + * Streams can occur only in files, not in streams, so some of the initialization of a stream + * object goes here. + */ + @Override + protected void initStream(Stream token) { + token.setOffset(getFilePos()); + } + + /** + * Sets the offset of a Stream to the current file position. Only the file-based tokenizer can do + * this, which is why this overrides the Tokenizer method. + */ + @Override + protected void setStreamOffset(Stream token) { + if (token.getOffset() < 0) { + token.setOffset(getFilePos()); } - - - /** Gets a character from the file, using a buffer. */ - @Override - public int readChar () throws IOException - { - if (_fileBufferOffset >= _fileBufferBytes) { - // If the byte size is 0, we can assume a seek was already - // done, but otherwise we must seek safety. - if (_fileBufferBytes > 0) { - long newOffset = _fileBufferPositionOffset + _fileBufferOffset; - _file.seek (newOffset); - _fileBufferPositionOffset = newOffset; - } - _fileBufferBytes = _file.read(_fileBuffer); - if (_fileBufferBytes <= 0) { - throw new EOFException (); - } - _fileBufferOffset = 0; - } - return (_fileBuffer[_fileBufferOffset++] & 0XFF); - } - - /** - * Set the Tokenizer to a new position in the file. - * - * @param offset The offset in bytes from the start of the file. - */ - @Override - public void seek (long offset) - throws IOException - { - if (_fileBufferPositionOffset >= 0 && - offset >= _fileBufferPositionOffset && - offset < _fileBufferPositionOffset + _fileBufferBytes) { - // Reposition within the buffer - _fileBufferOffset = (int) (offset - _fileBufferPositionOffset); - } - else { - _file.seek (offset); - initFileBuffer (); - _fileBufferPositionOffset = offset; - } - seekReset (offset); - } - - - /** - * Back up a byte so it will be read again. - */ - @Override - public void backupChar () - { - _fileBufferOffset--; - } - - /** Streams can occur only in files, not in streams, - * so some of the initialization of a stream object - * goes here. - */ - @Override - protected void initStream (Stream token) - { - token.setOffset (getFilePos ()); - } - - - /** Sets the offset of a Stream to the current file position. - * Only the file-based tokenizer can do this, which is why this - * overrides the Tokenizer method. - */ - @Override - protected void setStreamOffset (Stream token) - { - if (token.getOffset() < 0) { - token.setOffset (getFilePos ()); - } - } - + } } diff --git a/jhove-modules/pdf-hul/src/main/java/edu/harvard/hul/ois/jhove/module/pdf/Filter.java b/jhove-modules/pdf-hul/src/main/java/edu/harvard/hul/ois/jhove/module/pdf/Filter.java index ed4346841..6164abddb 100644 --- a/jhove-modules/pdf-hul/src/main/java/edu/harvard/hul/ois/jhove/module/pdf/Filter.java +++ b/jhove-modules/pdf-hul/src/main/java/edu/harvard/hul/ois/jhove/module/pdf/Filter.java @@ -1,66 +1,57 @@ -/********************************************************************** - * Jhove - JSTOR/Harvard Object Validation Environment - * Copyright 2005 by JSTOR and the President and Fellows of Harvard College - **********************************************************************/ - +/** + * ******************************************************************** Jhove - JSTOR/Harvard Object + * Validation Environment Copyright 2005 by JSTOR and the President and Fellows of Harvard College + * ******************************************************************** + */ package edu.harvard.hul.ois.jhove.module.pdf; /** * Container for a PDF stream filter. * * @author Gary McGath - * */ public class Filter { - private String _filterName; - private PdfDictionary _decodeParms; - - /** - * Constructor. - * - * @param name The name of the filter. - */ - public Filter (String name) - { - _filterName = name; - } - - /** Returns the name of the filter. */ - public String getFilterName () - { - return _filterName; - } - - /** Returns the DecodeParms dictionary, or null if there is none. */ - public PdfDictionary getDecodeParms () - { - return _decodeParms; - } - - /** Returns the "Name" parameter of the filter, or null - * if there is no such parameter. - * This is normally associated with a Crypt filter, and - * shouldn't be confused with the name of the filter. - */ - public String getNameParam () - { - try { - if (_decodeParms != null) { - PdfSimpleObject obj = - (PdfSimpleObject) _decodeParms.get ("Name"); - return obj.getStringValue(); - } - } - catch (Exception e) { - } - return null; - } - - /** Stores the DecodeParms or FDecodeParms dictionary - * which is associated with this filter. */ - public void setDecodeParms (PdfDictionary parms) - { - _decodeParms = parms; + private String _filterName; + private PdfDictionary _decodeParms; + + /** + * Constructor. + * + * @param name The name of the filter. + */ + public Filter(String name) { + _filterName = name; + } + + /** Returns the name of the filter. */ + public String getFilterName() { + return _filterName; + } + + /** Returns the DecodeParms dictionary, or null if there is none. */ + public PdfDictionary getDecodeParms() { + return _decodeParms; + } + + /** + * Returns the "Name" parameter of the filter, or null if there is no such parameter. + * This is normally associated with a Crypt filter, and shouldn't be confused with the name of the + * filter. + */ + public String getNameParam() { + try { + if (_decodeParms != null) { + PdfSimpleObject obj = (PdfSimpleObject) _decodeParms.get("Name"); + return obj.getStringValue(); + } + } catch (Exception e) { } + return null; + } + + /** Stores the DecodeParms or FDecodeParms dictionary which is associated with this filter. */ + public void setDecodeParms(PdfDictionary parms) { + _decodeParms = parms; + } } diff --git a/jhove-modules/pdf-hul/src/main/java/edu/harvard/hul/ois/jhove/module/pdf/Hexadecimal.java b/jhove-modules/pdf-hul/src/main/java/edu/harvard/hul/ois/jhove/module/pdf/Hexadecimal.java index caddf455d..23af6108a 100644 --- a/jhove-modules/pdf-hul/src/main/java/edu/harvard/hul/ois/jhove/module/pdf/Hexadecimal.java +++ b/jhove-modules/pdf-hul/src/main/java/edu/harvard/hul/ois/jhove/module/pdf/Hexadecimal.java @@ -1,24 +1,20 @@ -/********************************************************************** - * Jhove - JSTOR/Harvard Object Validation Environment - * Copyright 2003 by JSTOR and the President and Fellows of Harvard College - **********************************************************************/ - +/** + * ******************************************************************** Jhove - JSTOR/Harvard Object + * Validation Environment Copyright 2003 by JSTOR and the President and Fellows of Harvard College + * ******************************************************************** + */ package edu.harvard.hul.ois.jhove.module.pdf; /** - * Class for Tokens which represent hexadecimally encoded PDF strings. - * This class really has no justification as a separate entity. Except - * for the way they're written, hexadecimal strings aren't different in - * any way from other strings. + * Class for Tokens which represent hexadecimally encoded PDF strings. This class really has no + * justification as a separate entity. Except for the way they're written, hexadecimal strings + * aren't different in any way from other strings. * - * @deprecated + * @deprecated */ - public class Hexadecimal - extends Literal -{ - /** Creates an instance of a hexadecimal string literal */ - public Hexadecimal () - { - super (); - } +public class Hexadecimal extends Literal { + /** Creates an instance of a hexadecimal string literal */ + public Hexadecimal() { + super(); + } } diff --git a/jhove-modules/pdf-hul/src/main/java/edu/harvard/hul/ois/jhove/module/pdf/Keyword.java b/jhove-modules/pdf-hul/src/main/java/edu/harvard/hul/ois/jhove/module/pdf/Keyword.java index 6df8f5063..8f84993a7 100644 --- a/jhove-modules/pdf-hul/src/main/java/edu/harvard/hul/ois/jhove/module/pdf/Keyword.java +++ b/jhove-modules/pdf-hul/src/main/java/edu/harvard/hul/ois/jhove/module/pdf/Keyword.java @@ -1,19 +1,14 @@ -/********************************************************************** - * Jhove - JSTOR/Harvard Object Validation Environment - * Copyright 2003 by JSTOR and the President and Fellows of Harvard College - **********************************************************************/ - -package edu.harvard.hul.ois.jhove.module.pdf; - /** - * Class for Tokens which represent PDF keywords. + * ******************************************************************** Jhove - JSTOR/Harvard Object + * Validation Environment Copyright 2003 by JSTOR and the President and Fellows of Harvard College + * ******************************************************************** */ -public class Keyword - extends StringValuedToken -{ - /** Creates an instance of a Keyword */ - public Keyword () - { - super (); - } +package edu.harvard.hul.ois.jhove.module.pdf; + +/** Class for Tokens which represent PDF keywords. */ +public class Keyword extends StringValuedToken { + /** Creates an instance of a Keyword */ + public Keyword() { + super(); + } } diff --git a/jhove-modules/pdf-hul/src/main/java/edu/harvard/hul/ois/jhove/module/pdf/LinearizedProfile.java b/jhove-modules/pdf-hul/src/main/java/edu/harvard/hul/ois/jhove/module/pdf/LinearizedProfile.java index 26e11927a..ddce69f33 100644 --- a/jhove-modules/pdf-hul/src/main/java/edu/harvard/hul/ois/jhove/module/pdf/LinearizedProfile.java +++ b/jhove-modules/pdf-hul/src/main/java/edu/harvard/hul/ois/jhove/module/pdf/LinearizedProfile.java @@ -1,4 +1,3 @@ - package edu.harvard.hul.ois.jhove.module.pdf; import edu.harvard.hul.ois.jhove.messages.JhoveMessages; @@ -6,302 +5,273 @@ import java.util.*; import java.util.logging.*; -/** - * PDF profile checker for Linearized documents. - */ -public final class LinearizedProfile extends PdfProfile -{ - /****************************************************************** - * PRIVATE CLASS FIELDS. - ******************************************************************/ - - private Logger _logger; +/** PDF profile checker for Linearized documents. */ +public final class LinearizedProfile extends PdfProfile { + /** + * **************************************************************** PRIVATE CLASS FIELDS. + * **************************************************************** + */ + private Logger _logger; - /** - * Creates a PdfProfile object for subsequent testing. - * - */ - public LinearizedProfile (PdfModule module) - { - super (module); - _profileText = "Linearized PDF"; - _logger = Logger.getLogger ("edu.harvard.hul.ois.jhove.module"); - } + /** Creates a PdfProfile object for subsequent testing. */ + public LinearizedProfile(PdfModule module) { + super(module); + _profileText = "Linearized PDF"; + _logger = Logger.getLogger("edu.harvard.hul.ois.jhove.module"); + } - /** - * Returns true if the document satisfies the profile. - * - */ - @Override - public boolean satisfiesThisProfile () - { - _logger.info(MessageConstants.LOG_LIN_PROF_CHK); - long fileLength; - try { - fileLength = _raf.length (); - // First we must find the first object from the beginning - // of the file. The documentation contradicts the examples - // on whether this is an indirect object or not. Based - // on the actual files I've seen so far, I think it isn't. - PdfObject firstObj = findFirstObject (); - if (!(firstObj instanceof PdfDictionary)) { - return false; - } - // Initial checks: that the first object is a linearization - // dictionary, and that it has a length element which matches - // the length of the file. - // All entries in a linearization dictionary must be direct. - PdfDictionary lindict = (PdfDictionary) firstObj; - if (lindict.get ("Linearized")!= null) { - PdfObject lengthObj = lindict.get ("L"); - if (lengthObj instanceof PdfSimpleObject) { - // The value of L must be the file length - Token lengthTok = - ((PdfSimpleObject) lengthObj).getToken (); - if (lengthTok instanceof Numeric) { - long length = ((Numeric) lengthTok).getLongValue (); - if (length != fileLength) { - return false; - } - } - else { - return false; - } - } - else { - return false; - } - } - else { - return false; + /** Returns true if the document satisfies the profile. */ + @Override + public boolean satisfiesThisProfile() { + _logger.info(MessageConstants.LOG_LIN_PROF_CHK); + long fileLength; + try { + fileLength = _raf.length(); + // First we must find the first object from the beginning + // of the file. The documentation contradicts the examples + // on whether this is an indirect object or not. Based + // on the actual files I've seen so far, I think it isn't. + PdfObject firstObj = findFirstObject(); + if (!(firstObj instanceof PdfDictionary)) { + return false; + } + // Initial checks: that the first object is a linearization + // dictionary, and that it has a length element which matches + // the length of the file. + // All entries in a linearization dictionary must be direct. + PdfDictionary lindict = (PdfDictionary) firstObj; + if (lindict.get("Linearized") != null) { + PdfObject lengthObj = lindict.get("L"); + if (lengthObj instanceof PdfSimpleObject) { + // The value of L must be the file length + Token lengthTok = ((PdfSimpleObject) lengthObj).getToken(); + if (lengthTok instanceof Numeric) { + long length = ((Numeric) lengthTok).getLongValue(); + if (length != fileLength) { + return false; } + } else { + return false; + } + } else { + return false; + } + } else { + return false; + } - // Next -- check the integrity of the hint tables. - // These are described in F.2.5 in the PDF 1.4 manual. - // The linearization dictionary must have an 'H' entry - // whose values is an array of 2 or 4 integers. - PdfArray hintArray = (PdfArray) lindict.get ("H"); - if (hintArray != null) { - _logger.info(MessageConstants.LOG_HINT_ARRY_CHK); - Vector hintVec = hintArray.getContent (); - int vecSize = hintVec.size (); - if (vecSize != 2 && vecSize != 4) { - return false; - } - // element 0 (and 2, if present) is the offset - // of a stream object. Make sure it really is. - // Also check elements 1 and 3, which are lengths, - // for not overflowing the file. - for (int i = 0; i < vecSize; i += 2) { - PdfSimpleObject hobj = - (PdfSimpleObject) _module.resolveIndirectObject - ((PdfObject) hintVec.elementAt (i)); - Numeric hnum = (Numeric) hobj.getToken (); - long hoffset = hnum.getLongValue (); - hobj = (PdfSimpleObject) hintVec.elementAt (i + 1); - hnum = (Numeric) hobj.getToken (); - long hlen = hnum.getLongValue (); - if (hoffset + hlen > fileLength) { - return false; // hint dict runs past EOF - } - _parser.seek (hoffset); - // The documentation appears to lie here. What - // we find isn't the stream, but an object - // definition for the stream (of the form - // m n obj). Allow for both possibilities. - PdfObject hintStream = _parser.readObject (false); - if (hintStream instanceof PdfSimpleObject) { - _parser.readObject (false); // discard version no. - _parser.readObject (false); // discard obj keyword - hintStream = _parser.readObject (false); // the real thing - } - // Parser will see a dictionary, not the stream - if (!(hintStream instanceof PdfDictionary)) { - return false; - } - if (!validateHintStream ((PdfDictionary) hintStream)) { - return false; - } - } - } - else { - return false; - } + // Next -- check the integrity of the hint tables. + // These are described in F.2.5 in the PDF 1.4 manual. + // The linearization dictionary must have an 'H' entry + // whose values is an array of 2 or 4 integers. + PdfArray hintArray = (PdfArray) lindict.get("H"); + if (hintArray != null) { + _logger.info(MessageConstants.LOG_HINT_ARRY_CHK); + Vector hintVec = hintArray.getContent(); + int vecSize = hintVec.size(); + if (vecSize != 2 && vecSize != 4) { + return false; + } + // element 0 (and 2, if present) is the offset + // of a stream object. Make sure it really is. + // Also check elements 1 and 3, which are lengths, + // for not overflowing the file. + for (int i = 0; i < vecSize; i += 2) { + PdfSimpleObject hobj = + (PdfSimpleObject) _module.resolveIndirectObject((PdfObject) hintVec.elementAt(i)); + Numeric hnum = (Numeric) hobj.getToken(); + long hoffset = hnum.getLongValue(); + hobj = (PdfSimpleObject) hintVec.elementAt(i + 1); + hnum = (Numeric) hobj.getToken(); + long hlen = hnum.getLongValue(); + if (hoffset + hlen > fileLength) { + return false; // hint dict runs past EOF + } + _parser.seek(hoffset); + // The documentation appears to lie here. What + // we find isn't the stream, but an object + // definition for the stream (of the form + // m n obj). Allow for both possibilities. + PdfObject hintStream = _parser.readObject(false); + if (hintStream instanceof PdfSimpleObject) { + _parser.readObject(false); // discard version no. + _parser.readObject(false); // discard obj keyword + hintStream = _parser.readObject(false); // the real thing + } + // Parser will see a dictionary, not the stream + if (!(hintStream instanceof PdfDictionary)) { + return false; + } + if (!validateHintStream((PdfDictionary) hintStream)) { + return false; + } + } + } else { + return false; + } - // Check for valid first object number - PdfSimpleObject firstObjNum = (PdfSimpleObject) lindict.get ("O"); - if (! (firstObjNum.getToken () instanceof Numeric)) { - return false; - } + // Check for valid first object number + PdfSimpleObject firstObjNum = (PdfSimpleObject) lindict.get("O"); + if (!(firstObjNum.getToken() instanceof Numeric)) { + return false; + } - // Check for valid offset to end of first page - PdfSimpleObject endpageObj = - (PdfSimpleObject) lindict.get ("E"); - Numeric endpageTok = (Numeric) endpageObj.getToken (); - long endpage = endpageTok.getLongValue (); - if (endpage > fileLength) { - return false; - } + // Check for valid offset to end of first page + PdfSimpleObject endpageObj = (PdfSimpleObject) lindict.get("E"); + Numeric endpageTok = (Numeric) endpageObj.getToken(); + long endpage = endpageTok.getLongValue(); + if (endpage > fileLength) { + return false; + } - // Check for valid number of pages entry - PdfSimpleObject numpagesObj = - (PdfSimpleObject) lindict.get ("N"); - if (!(numpagesObj.getToken () instanceof Numeric)) { - return false; - } + // Check for valid number of pages entry + PdfSimpleObject numpagesObj = (PdfSimpleObject) lindict.get("N"); + if (!(numpagesObj.getToken() instanceof Numeric)) { + return false; + } - // Check offset to main cross-reference table - PdfSimpleObject xrefObj = - (PdfSimpleObject) lindict.get ("T"); - Numeric xrefTok = (Numeric) xrefObj.getToken (); - long xrefOffset = xrefTok.getLongValue (); - if (!verifyXRef (xrefOffset)) { - return false; - } - } - catch (Exception e) { - // An exception thrown anywhere means some assumption - // has been violated, so it's not linearized. - return false; - } - return true; // passed all tests + // Check offset to main cross-reference table + PdfSimpleObject xrefObj = (PdfSimpleObject) lindict.get("T"); + Numeric xrefTok = (Numeric) xrefObj.getToken(); + long xrefOffset = xrefTok.getLongValue(); + if (!verifyXRef(xrefOffset)) { + return false; + } + } catch (Exception e) { + // An exception thrown anywhere means some assumption + // has been violated, so it's not linearized. + return false; } + return true; // passed all tests + } - /* Find the first object from the beginning of the file. - This is similar to, and perhaps a bit easier than, finding - the last dictionary. For the moment we don't worry about - what the object is. */ - private PdfObject findFirstObject () - { - try { - _parser.seek (8); - // To get in sync, read until we see the keyword - // "obj". - for (;;) { - Token tok = _parser.getNext (); - if (tok instanceof Keyword && "obj".equals(((Keyword) tok).getValue ())) { - PdfObject val = _parser.readObject (false); - // Object must be completely contained in - // the first 1024 bytes. - if (_parser.getOffset () <= 1024) { - return val; - } - return null; - } - if (_parser.getOffset () > 1024) { - return null; - } - } - + /* Find the first object from the beginning of the file. + This is similar to, and perhaps a bit easier than, finding + the last dictionary. For the moment we don't worry about + what the object is. */ + private PdfObject findFirstObject() { + try { + _parser.seek(8); + // To get in sync, read until we see the keyword + // "obj". + for (; ; ) { + Token tok = _parser.getNext(); + if (tok instanceof Keyword && "obj".equals(((Keyword) tok).getValue())) { + PdfObject val = _parser.readObject(false); + // Object must be completely contained in + // the first 1024 bytes. + if (_parser.getOffset() <= 1024) { + return val; + } + return null; } - catch (Exception e) { - return null; + if (_parser.getOffset() > 1024) { + return null; } - } + } + } catch (Exception e) { + return null; + } + } - /* Read a cross-reference table to make sure it looks OK. - What we're pointing at is the first _entry_ of the - table, not the start of the subsection. - This means we don't know the object count, which makes - it very tough to figure out whether we're hit the - end or we really have an invalid table. - Settle for reading one object to see if it looks good. */ - private boolean verifyXRef (long xrefOffset) - { - try { - _logger.info (MessageConstants.LOG_XREF_TABLE_VERIFYING); - _parser.seek (xrefOffset); - _parser.getNext (Numeric.class, JhoveMessages.DEFAULT_MESSAGE); // Object number - _parser.getNext (Numeric.class, JhoveMessages.DEFAULT_MESSAGE); // Generation number - _parser.getNext (Keyword.class, JhoveMessages.DEFAULT_MESSAGE); // n or f keyword - // If that didn't throw an exception, assume we're ok - return true; - } - catch (Exception e) { - return false; - } + /* Read a cross-reference table to make sure it looks OK. + What we're pointing at is the first _entry_ of the + table, not the start of the subsection. + This means we don't know the object count, which makes + it very tough to figure out whether we're hit the + end or we really have an invalid table. + Settle for reading one object to see if it looks good. */ + private boolean verifyXRef(long xrefOffset) { + try { + _logger.info(MessageConstants.LOG_XREF_TABLE_VERIFYING); + _parser.seek(xrefOffset); + _parser.getNext(Numeric.class, JhoveMessages.DEFAULT_MESSAGE); // Object number + _parser.getNext(Numeric.class, JhoveMessages.DEFAULT_MESSAGE); // Generation number + _parser.getNext(Keyword.class, JhoveMessages.DEFAULT_MESSAGE); // n or f keyword + // If that didn't throw an exception, assume we're ok + return true; + } catch (Exception e) { + return false; } + } - /* Check that a hint stream dictionary has some semblance - of validility. */ - private static boolean validateHintStream (PdfDictionary hDict) - { - try { - // An offset to the shared object hint table - // is the one thing that's required. - PdfSimpleObject obj = (PdfSimpleObject) - hDict.get ("S"); - if (obj == null) { - return false; - } - int offset = obj.getIntValue (); - if (offset < 0) { - return false; - } + /* Check that a hint stream dictionary has some semblance + of validility. */ + private static boolean validateHintStream(PdfDictionary hDict) { + try { + // An offset to the shared object hint table + // is the one thing that's required. + PdfSimpleObject obj = (PdfSimpleObject) hDict.get("S"); + if (obj == null) { + return false; + } + int offset = obj.getIntValue(); + if (offset < 0) { + return false; + } - // Other objects aren't required, but must be - // non-negative integers if they're there. - obj = (PdfSimpleObject) hDict.get ("T"); - if (obj != null) { - offset = obj.getIntValue (); - if (offset < 0) { - return false; - } - } - obj = (PdfSimpleObject) hDict.get ("O"); - if (obj != null) { - offset = obj.getIntValue (); - if (offset < 0) { - return false; - } - } - obj = (PdfSimpleObject) hDict.get ("A"); - if (obj != null) { - offset = obj.getIntValue (); - if (offset < 0) { - return false; - } - } - obj = (PdfSimpleObject) hDict.get ("E"); - if (obj != null) { - offset = obj.getIntValue (); - if (offset < 0) { - return false; - } - } - obj = (PdfSimpleObject) hDict.get ("V"); - if (obj != null) { - offset = obj.getIntValue (); - if (offset < 0) { - return false; - } - } - obj = (PdfSimpleObject) hDict.get ("I"); - if (obj != null) { - offset = obj.getIntValue (); - if (offset < 0) { - return false; - } - } - obj = (PdfSimpleObject) hDict.get ("L"); - if (obj != null) { - offset = obj.getIntValue (); - if (offset < 0) { - return false; - } - } - obj = (PdfSimpleObject) hDict.get ("C"); - if (obj != null) { - offset = obj.getIntValue (); - if (offset < 0) { - return false; - } - } - return true; // passed all tests + // Other objects aren't required, but must be + // non-negative integers if they're there. + obj = (PdfSimpleObject) hDict.get("T"); + if (obj != null) { + offset = obj.getIntValue(); + if (offset < 0) { + return false; } - catch (Exception e) { - return false; + } + obj = (PdfSimpleObject) hDict.get("O"); + if (obj != null) { + offset = obj.getIntValue(); + if (offset < 0) { + return false; + } + } + obj = (PdfSimpleObject) hDict.get("A"); + if (obj != null) { + offset = obj.getIntValue(); + if (offset < 0) { + return false; + } + } + obj = (PdfSimpleObject) hDict.get("E"); + if (obj != null) { + offset = obj.getIntValue(); + if (offset < 0) { + return false; + } + } + obj = (PdfSimpleObject) hDict.get("V"); + if (obj != null) { + offset = obj.getIntValue(); + if (offset < 0) { + return false; + } + } + obj = (PdfSimpleObject) hDict.get("I"); + if (obj != null) { + offset = obj.getIntValue(); + if (offset < 0) { + return false; + } + } + obj = (PdfSimpleObject) hDict.get("L"); + if (obj != null) { + offset = obj.getIntValue(); + if (offset < 0) { + return false; + } + } + obj = (PdfSimpleObject) hDict.get("C"); + if (obj != null) { + offset = obj.getIntValue(); + if (offset < 0) { + return false; } + } + return true; // passed all tests + } catch (Exception e) { + return false; } + } } diff --git a/jhove-modules/pdf-hul/src/main/java/edu/harvard/hul/ois/jhove/module/pdf/Literal.java b/jhove-modules/pdf-hul/src/main/java/edu/harvard/hul/ois/jhove/module/pdf/Literal.java index ad44303cc..5b8c88ace 100644 --- a/jhove-modules/pdf-hul/src/main/java/edu/harvard/hul/ois/jhove/module/pdf/Literal.java +++ b/jhove-modules/pdf-hul/src/main/java/edu/harvard/hul/ois/jhove/module/pdf/Literal.java @@ -1,751 +1,696 @@ -/********************************************************************** - * Jhove - JSTOR/Harvard Object Validation Environment - * Copyright 2003 by JSTOR and the President and Fellows of Harvard College - **********************************************************************/ - +/** + * ******************************************************************** Jhove - JSTOR/Harvard Object + * Validation Environment Copyright 2003 by JSTOR and the President and Fellows of Harvard College + * ******************************************************************** + */ package edu.harvard.hul.ois.jhove.module.pdf; -import java.util.*; -import java.text.*; import java.io.EOFException; import java.io.IOException; +import java.text.*; +import java.util.*; /** - * Class for Tokens which represent PDF strings. The class maintains - * a field for determining whether the string is encoded as PDF encoding - * or UTF-16. This is determined in the course of analyzing the - * characters for the token. + * Class for Tokens which represent PDF strings. The class maintains a field for determining whether + * the string is encoded as PDF encoding or UTF-16. This is determined in the course of analyzing + * the characters for the token. */ -public class Literal - extends StringValuedToken -{ - /** True if literal is in PDFDocEncoding; false if UTF-16. */ - private boolean _pdfDocEncoding; - - /** Used for accommodating the literal */ - private StringBuffer buffer; - - /** Indicates if a character for the first half of a hex byte - has already been buffered */ - private boolean haveHi; - - /** The high half-byte character */ - private int hi; - - /** First byte of a UTF-16 character. */ - int firstByte; - - /** First digit of a hexadecimal string value. */ - //int h1; - - /** The state of the tokenization. Only the subset of States which - pertain to Literals are used here. */ - private State _state; - - /** True if no discrepancies with PDF/A requirements have been found, - false if there is a discrepancy in this literal. */ - private boolean _pdfACompliant; - - /** Depth of parenthesis nesting. */ - private int _parenLevel; - - /** Mapping between PDFDocEncoding and Unicode code points. */ - public static char [] PDFDOCENCODING = { - '\u0000','\u0001','\u0002','\u0003','\u0004','\u0005','\u0006','\u0007', - '\b' ,'\t' ,'\n' ,'\u000b','\f' ,'\r' ,'\u000e','\u000f', - '\u0010','\u0011','\u0012','\u0013','\u0014','\u0015','\u0016','\u0017', - '\u02d8','\u02c7','\u02c6','\u02d9','\u02dd','\u02db','\u02da','\u02dc', - '\u0020','\u0021','\"' ,'\u0023','\u0024','\u0025','\u0026','\'', - '\u0028','\u0029','\u002a','\u002b','\u002c','\u002d','\u002e','\u002f', - '\u0030','\u0031','\u0032','\u0033','\u0034','\u0035','\u0036','\u0037', - '\u0038','\u0039','\u003a','\u003b','\u003c','\u003d','\u003e','\u003f', - '\u0040','\u0041','\u0042','\u0043','\u0044','\u0045','\u0046','\u0047', - '\u0048','\u0049','\u004a','\u004b','\u004c','\u004d','\u004e','\u004f', - '\u0050','\u0051','\u0052','\u0053','\u0054','\u0055','\u0056','\u0057', - '\u0058','\u0059','\u005a','\u005b','\\' ,'\u005d','\u005e','\u005f', - '\u0060','\u0061','\u0062','\u0063','\u0064','\u0065','\u0066','\u0067', - '\u0068','\u0069','\u006a','\u006b','\u006c','\u006d','\u006e','\u006f', - '\u0070','\u0071','\u0072','\u0073','\u0074','\u0075','\u0076','\u0077', - '\u0078','\u0079','\u007a','\u007b','\u007c','\u007d','\u007e','\u007f', - '\u2022','\u2020','\u2021','\u2026','\u2003','\u2002','\u0192','\u2044', - '\u2039','\u203a','\u2212','\u2030','\u201e','\u201c','\u201d','\u2018', - '\u2019','\u201a','\u2122','\ufb01','\ufb02','\u0141','\u0152','\u0160', - '\u0178','\u017d','\u0131','\u0142','\u0153','\u0161','\u017e','\u009f', - '\u20ac','\u00a1','\u00a2','\u00a3','\u00a4','\u00a5','\u00a6','\u00a7', - '\u00a8','\u00a9','\u00aa','\u00ab','\u00ac','\u00ad','\u00ae','\u00af', - '\u00b0','\u00b1','\u00b2','\u00b3','\u00b4','\u00b5','\u00b6','\u00b7', - '\u00b8','\u00b9','\u00ba','\u00bb','\u00bc','\u00bd','\u00be','\u00bf', - '\u00c0','\u00c1','\u00c2','\u00c3','\u00c4','\u00c5','\u00c6','\u00c7', - '\u00c8','\u00c9','\u00ca','\u00cb','\u00cc','\u00cd','\u00ce','\u00cf', - '\u00d0','\u00d1','\u00d2','\u00d3','\u00d4','\u00d5','\u00d6','\u00d7', - '\u00d8','\u00d9','\u00da','\u00db','\u00dc','\u00dd','\u00de','\u00df', - '\u00e0','\u00e1','\u00e2','\u00e3','\u00e4','\u00e5','\u00e6','\u00e7', - '\u00e8','\u00e9','\u00ea','\u00eb','\u00ec','\u00ed','\u00ef','\u00ef', - '\u00f0','\u00f1','\u00f2','\u00f3','\u00f4','\u00f5','\u00f6','\u00f7', - '\u00f8','\u00f9','\u00fa','\u00fb','\u00fc','\u00fd','\u00fe','\u00ff' - }; - - - private static final int CR = 0x0D; - private static final int LF = 0x0A; - private static final int BS = 0x08; - private static final int HT = 0x09; - private static final int FORMFEED = 0x0C; - private static final int ESC = 0X1B; - private static final int OPEN_PARENTHESIS = 0x28; - private static final int CLOSE_PARENTHESIS = 0x29; - private static final int BACKSLASH = 0x5C; - private static final int FE = 0xFE; - private static final int FF = 0xFF; - - /** Creates an instance of a string literal */ - public Literal () - { - super (); - _pdfDocEncoding = true; - buffer = new StringBuffer (); - haveHi = false; +public class Literal extends StringValuedToken { + /** True if literal is in PDFDocEncoding; false if UTF-16. */ + private boolean _pdfDocEncoding; + + /** Used for accommodating the literal */ + private StringBuffer buffer; + + /** Indicates if a character for the first half of a hex byte has already been buffered */ + private boolean haveHi; + + /** The high half-byte character */ + private int hi; + + /** First byte of a UTF-16 character. */ + int firstByte; + + /** First digit of a hexadecimal string value. */ + // int h1; + + /** + * The state of the tokenization. Only the subset of States which pertain to Literals are used + * here. + */ + private State _state; + + /** + * True if no discrepancies with PDF/A requirements have been found, false if there is a + * discrepancy in this literal. + */ + private boolean _pdfACompliant; + + /** Depth of parenthesis nesting. */ + private int _parenLevel; + + /** Mapping between PDFDocEncoding and Unicode code points. */ + public static char[] PDFDOCENCODING = { + '\u0000', '\u0001', '\u0002', '\u0003', '\u0004', '\u0005', '\u0006', '\u0007', + '\b', '\t', '\n', '\u000b', '\f', '\r', '\u000e', '\u000f', + '\u0010', '\u0011', '\u0012', '\u0013', '\u0014', '\u0015', '\u0016', '\u0017', + '\u02d8', '\u02c7', '\u02c6', '\u02d9', '\u02dd', '\u02db', '\u02da', '\u02dc', + '\u0020', '\u0021', '\"', '\u0023', '\u0024', '\u0025', '\u0026', '\'', + '\u0028', '\u0029', '\u002a', '\u002b', '\u002c', '\u002d', '\u002e', '\u002f', + '\u0030', '\u0031', '\u0032', '\u0033', '\u0034', '\u0035', '\u0036', '\u0037', + '\u0038', '\u0039', '\u003a', '\u003b', '\u003c', '\u003d', '\u003e', '\u003f', + '\u0040', '\u0041', '\u0042', '\u0043', '\u0044', '\u0045', '\u0046', '\u0047', + '\u0048', '\u0049', '\u004a', '\u004b', '\u004c', '\u004d', '\u004e', '\u004f', + '\u0050', '\u0051', '\u0052', '\u0053', '\u0054', '\u0055', '\u0056', '\u0057', + '\u0058', '\u0059', '\u005a', '\u005b', '\\', '\u005d', '\u005e', '\u005f', + '\u0060', '\u0061', '\u0062', '\u0063', '\u0064', '\u0065', '\u0066', '\u0067', + '\u0068', '\u0069', '\u006a', '\u006b', '\u006c', '\u006d', '\u006e', '\u006f', + '\u0070', '\u0071', '\u0072', '\u0073', '\u0074', '\u0075', '\u0076', '\u0077', + '\u0078', '\u0079', '\u007a', '\u007b', '\u007c', '\u007d', '\u007e', '\u007f', + '\u2022', '\u2020', '\u2021', '\u2026', '\u2003', '\u2002', '\u0192', '\u2044', + '\u2039', '\u203a', '\u2212', '\u2030', '\u201e', '\u201c', '\u201d', '\u2018', + '\u2019', '\u201a', '\u2122', '\ufb01', '\ufb02', '\u0141', '\u0152', '\u0160', + '\u0178', '\u017d', '\u0131', '\u0142', '\u0153', '\u0161', '\u017e', '\u009f', + '\u20ac', '\u00a1', '\u00a2', '\u00a3', '\u00a4', '\u00a5', '\u00a6', '\u00a7', + '\u00a8', '\u00a9', '\u00aa', '\u00ab', '\u00ac', '\u00ad', '\u00ae', '\u00af', + '\u00b0', '\u00b1', '\u00b2', '\u00b3', '\u00b4', '\u00b5', '\u00b6', '\u00b7', + '\u00b8', '\u00b9', '\u00ba', '\u00bb', '\u00bc', '\u00bd', '\u00be', '\u00bf', + '\u00c0', '\u00c1', '\u00c2', '\u00c3', '\u00c4', '\u00c5', '\u00c6', '\u00c7', + '\u00c8', '\u00c9', '\u00ca', '\u00cb', '\u00cc', '\u00cd', '\u00ce', '\u00cf', + '\u00d0', '\u00d1', '\u00d2', '\u00d3', '\u00d4', '\u00d5', '\u00d6', '\u00d7', + '\u00d8', '\u00d9', '\u00da', '\u00db', '\u00dc', '\u00dd', '\u00de', '\u00df', + '\u00e0', '\u00e1', '\u00e2', '\u00e3', '\u00e4', '\u00e5', '\u00e6', '\u00e7', + '\u00e8', '\u00e9', '\u00ea', '\u00eb', '\u00ec', '\u00ed', '\u00ef', '\u00ef', + '\u00f0', '\u00f1', '\u00f2', '\u00f3', '\u00f4', '\u00f5', '\u00f6', '\u00f7', + '\u00f8', '\u00f9', '\u00fa', '\u00fb', '\u00fc', '\u00fd', '\u00fe', '\u00ff' + }; + + private static final int CR = 0x0D; + private static final int LF = 0x0A; + private static final int BS = 0x08; + private static final int HT = 0x09; + private static final int FORMFEED = 0x0C; + private static final int ESC = 0X1B; + private static final int OPEN_PARENTHESIS = 0x28; + private static final int CLOSE_PARENTHESIS = 0x29; + private static final int BACKSLASH = 0x5C; + private static final int FE = 0xFE; + private static final int FF = 0xFF; + + /** Creates an instance of a string literal */ + public Literal() { + super(); + _pdfDocEncoding = true; + buffer = new StringBuffer(); + haveHi = false; + } + + /** + * Append a hex character.This is used only for hex literals (those that start with '<'). + * + * @param ch The integer 8-bit code for a hex character + * @throws edu.harvard.hul.ois.jhove.module.pdf.PdfException + */ + public void appendHex(int ch) throws PdfException { + if (_rawBytes == null) { + _rawBytes = new Vector<>(32); } - - /** - * Append a hex character.This is used only for hex literals - * (those that start with '<'). - * - * @param ch The integer 8-bit code for a hex character - * @throws edu.harvard.hul.ois.jhove.module.pdf.PdfException - */ - public void appendHex (int ch) throws PdfException - { - if (_rawBytes == null) { - _rawBytes = new Vector<> (32); - } - if (haveHi) { - _rawBytes.add(hexToInt (hi, ch)); - haveHi = false; - } - else { - hi = ch; - haveHi = true; - } + if (haveHi) { + _rawBytes.add(hexToInt(hi, ch)); + haveHi = false; + } else { + hi = ch; + haveHi = true; } - - /** - * Process the incoming characters into a string literal.This is used for literals delimited - by parentheses, as opposed to hex strings. - * - * @param tok The tokenizer, passed to give access to its getChar - * function. - * @return true if the character was processed - * normally, false if a terminating - * parenthesis was reached. - * @throws IOException - */ - public long processLiteral (Tokenizer tok) throws IOException - { - /** Variable for UTF-16 chars. */ - int utfch; - /** First byte of a UTF-16 character. */ - int b1 = 0x00; - /* Character read from tokenizer. */ - int ch; - _parenLevel = 0; - _rawBytes = new Vector<> (32); - _state = State.LITERAL; - - long offset = 0; - for (;;) { - ch = tok.readChar (); - // If we get -1, then we've hit an EOF without proper termination of - // the literal. Throw an exception. - if (ch < 0) { - throw new EOFException (MessageConstants.PDF_HUL_10.getMessage()); // PDF-HUL-10 - } - offset++; - _rawBytes.add (ch); - if (_state == State.LITERAL) { - // We are still in a state of flux, determining the encoding - if (ch == FE) { - _state = State.LITERAL_FE; - } - else if (ch == CLOSE_PARENTHESIS && --_parenLevel < 0) { - // We have an empty string - setPDFDocEncoding (true); - setValue(buffer.toString()); - return offset; - } - else if (ch == BACKSLASH) { - ch = readBackslashSequence (false, tok); - switch (ch) { - case 0: - continue; // invalid character, ignore - case FE: - _state = State.LITERAL_FE; - break; - default: - // any other char is treated nonspecially - setPDFDocEncoding (true); - buffer.append (PDFDOCENCODING[ch]); - break; - } - } - else { - // We now know we're in 8-bit PDF encoding. - // Append the character to the buffer. - if (ch == OPEN_PARENTHESIS) { - // Count open parens to be matched by close parens. - // Backslash-quoted parens won't get here. - ++_parenLevel; - } - _state = State.LITERAL_PDF; - setPDFDocEncoding (true); - buffer.append (PDFDOCENCODING[ch]); - } - } - else if (_state == (State.LITERAL_FE)) { - switch (ch) { - case FF: - _state = State.LITERAL_UTF16_1; - setPDFDocEncoding (false); - break; - case BACKSLASH: - ch = readBackslashSequence (false, tok); - if (ch == 0) { - continue; // invalid character, ignore - } if (ch == FF) { - _state = State.LITERAL_UTF16_1; - setPDFDocEncoding (false); - } - else { - // any other char is treated nonspecially - setPDFDocEncoding (true); - // The FE is just an FE, put it in the buffer - buffer.append (PDFDOCENCODING[FE]); - buffer.append (PDFDOCENCODING[ch]); - } break; - default: - _state = State.LITERAL_PDF; - setPDFDocEncoding (true); - // The FE is just an FE, put it in the buffer - buffer.append (PDFDOCENCODING[FE]); - buffer.append (PDFDOCENCODING[ch]); - break; - } - } - else if (_state == (State.LITERAL_PDF)) { - if (ch == OPEN_PARENTHESIS) { - // Count open parens to be matched by close parens. - // Backslash-quoted parens won't get here. - ++_parenLevel; - buffer.append (PDFDOCENCODING[ch]); - } - else if (ch == CLOSE_PARENTHESIS && --_parenLevel < 0) { - setValue(buffer.toString()); - return offset; - } - else if (ch == BACKSLASH) { - ch = readBackslashSequence (false, tok); - if (ch == 0) { - continue; // invalid character, ignore - } - // any other char is treated nonspecially - buffer.append (PDFDOCENCODING[ch]); - } - else { - buffer.append (PDFDOCENCODING[ch]); - } - } - else if (_state == (State.LITERAL_UTF16_1)) { - // First byte of a UTF16 character. But a close - // paren or backslash is a single-byte character. - // Parens within the string are double-byte characters, - // so we don't have to worry about them. - switch (ch) { - case CLOSE_PARENTHESIS: - setValue(buffer.toString()); - return offset; - case BACKSLASH: - utfch = readBackslashSequence (true, tok); - if (utfch == 0) { - continue; // invalid character, ignore - } break; - default: - _state = State.LITERAL_UTF16_2; - b1 = ch; - break; - } - } - else if (_state == (State.LITERAL_UTF16_2)) { - // Second byte of a UTF16 character. - utfch = 256 * b1 + ch; - _state = State.LITERAL_UTF16_1; - // an ESC may appear at any point to signify - // a language code. Remove the language code - // from the stream and save it in a list of codes. - if (utfch == ESC) { - readUTFLanguageCode (tok); - } - else { - /* It turns out that a backslash may be double-byte, - * rather than the assumed single.byte. The following - * allows for this. Suggested by Justin Litman, Library - * of Congress, 2006-03-17. - */ - if (utfch == BACKSLASH) { - utfch = readBackslashSequence (false, tok); - if (utfch == 0) { - _state = State.LITERAL_UTF16_2; // skip the wrong char and reset to previous state - continue; /* Invalid character, ignore. */ - } - } - buffer.append ((char) utfch); - } - } + } + + /** + * Process the incoming characters into a string literal.This is used for literals delimited by + * parentheses, as opposed to hex strings. + * + * @param tok The tokenizer, passed to give access to its getChar function. + * @return true if the character was processed normally, false if a + * terminating parenthesis was reached. + * @throws IOException + */ + public long processLiteral(Tokenizer tok) throws IOException { + /** Variable for UTF-16 chars. */ + int utfch; + /** First byte of a UTF-16 character. */ + int b1 = 0x00; + /* Character read from tokenizer. */ + int ch; + _parenLevel = 0; + _rawBytes = new Vector<>(32); + _state = State.LITERAL; + + long offset = 0; + for (; ; ) { + ch = tok.readChar(); + // If we get -1, then we've hit an EOF without proper termination of + // the literal. Throw an exception. + if (ch < 0) { + throw new EOFException(MessageConstants.PDF_HUL_10.getMessage()); // PDF-HUL-10 + } + offset++; + _rawBytes.add(ch); + if (_state == State.LITERAL) { + // We are still in a state of flux, determining the encoding + if (ch == FE) { + _state = State.LITERAL_FE; + } else if (ch == CLOSE_PARENTHESIS && --_parenLevel < 0) { + // We have an empty string + setPDFDocEncoding(true); + setValue(buffer.toString()); + return offset; + } else if (ch == BACKSLASH) { + ch = readBackslashSequence(false, tok); + switch (ch) { + case 0: + continue; // invalid character, ignore + case FE: + _state = State.LITERAL_FE; + break; + default: + // any other char is treated nonspecially + setPDFDocEncoding(true); + buffer.append(PDFDOCENCODING[ch]); + break; + } + } else { + // We now know we're in 8-bit PDF encoding. + // Append the character to the buffer. + if (ch == OPEN_PARENTHESIS) { + // Count open parens to be matched by close parens. + // Backslash-quoted parens won't get here. + ++_parenLevel; + } + _state = State.LITERAL_PDF; + setPDFDocEncoding(true); + buffer.append(PDFDOCENCODING[ch]); } - } - - - - /** - * Convert the raw hex data.Two buffers are saved: _rawBytes - * for the untranslated hex-encoded data, and _value for the - * PDF or UTF encoded string. - * @throws edu.harvard.hul.ois.jhove.module.pdf.PdfException - */ - public void convertHex () throws PdfException - { - if (_rawBytes != null) { - boolean utf = false; - StringBuilder localBuffer = new StringBuilder(); - // If a high byte is left hanging, complete it with a '0' - if (haveHi) { - _rawBytes.add(hexToInt(hi, '0')); - } - if (_rawBytes.size() >= 2 && rawByte(0) == 0XFE && - rawByte(1) == 0XFF) { - utf = true; + } else if (_state == (State.LITERAL_FE)) { + switch (ch) { + case FF: + _state = State.LITERAL_UTF16_1; + setPDFDocEncoding(false); + break; + case BACKSLASH: + ch = readBackslashSequence(false, tok); + if (ch == 0) { + continue; // invalid character, ignore } - if (utf) { - // Gather pairs of bytes into characters without conversion - for (int i = 2; i < _rawBytes.size(); i += 2) { - localBuffer.append - ((char) (rawByte(i) * 256 + rawByte(i + 1))); - } + if (ch == FF) { + _state = State.LITERAL_UTF16_1; + setPDFDocEncoding(false); } else { - // Convert single bytes to PDF encoded characters. - for (int i = 0; i < _rawBytes.size(); i++) { - localBuffer.append(Tokenizer.PDFDOCENCODING[rawByte(i)]); - } + // any other char is treated nonspecially + setPDFDocEncoding(true); + // The FE is just an FE, put it in the buffer + buffer.append(PDFDOCENCODING[FE]); + buffer.append(PDFDOCENCODING[ch]); } - _value = localBuffer.toString(); - } - } - - private static int hexToInt (int c1, int c2) throws PdfException - { - return 16 * hexValue (c1) + hexValue (c2); - } - - private static int hexValue (int h) throws PdfException - { - int d = 0; - if (0x30 <= h && h <= 0x39) { - // digit 0-9 - d = h - 0x30; + break; + default: + _state = State.LITERAL_PDF; + setPDFDocEncoding(true); + // The FE is just an FE, put it in the buffer + buffer.append(PDFDOCENCODING[FE]); + buffer.append(PDFDOCENCODING[ch]); + break; } - else if (0x41 <= h && h <= 0x46) { - // letter A-F - d = h - 0x37; + } else if (_state == (State.LITERAL_PDF)) { + if (ch == OPEN_PARENTHESIS) { + // Count open parens to be matched by close parens. + // Backslash-quoted parens won't get here. + ++_parenLevel; + buffer.append(PDFDOCENCODING[ch]); + } else if (ch == CLOSE_PARENTHESIS && --_parenLevel < 0) { + setValue(buffer.toString()); + return offset; + } else if (ch == BACKSLASH) { + ch = readBackslashSequence(false, tok); + if (ch == 0) { + continue; // invalid character, ignore + } + // any other char is treated nonspecially + buffer.append(PDFDOCENCODING[ch]); + } else { + buffer.append(PDFDOCENCODING[ch]); } - else if (0x61 <= h && h <= 0x66) { - // letter a-f - d = h - 0x57; + } else if (_state == (State.LITERAL_UTF16_1)) { + // First byte of a UTF16 character. But a close + // paren or backslash is a single-byte character. + // Parens within the string are double-byte characters, + // so we don't have to worry about them. + switch (ch) { + case CLOSE_PARENTHESIS: + setValue(buffer.toString()); + return offset; + case BACKSLASH: + utfch = readBackslashSequence(true, tok); + if (utfch == 0) { + continue; // invalid character, ignore + } + break; + default: + _state = State.LITERAL_UTF16_2; + b1 = ch; + break; } - else { - throw new PdfMalformedException (MessageConstants.PDF_HUL_11); // PDF-HUL-11 + } else if (_state == (State.LITERAL_UTF16_2)) { + // Second byte of a UTF16 character. + utfch = 256 * b1 + ch; + _state = State.LITERAL_UTF16_1; + // an ESC may appear at any point to signify + // a language code. Remove the language code + // from the stream and save it in a list of codes. + if (utfch == ESC) { + readUTFLanguageCode(tok); + } else { + /* It turns out that a backslash may be double-byte, + * rather than the assumed single.byte. The following + * allows for this. Suggested by Justin Litman, Library + * of Congress, 2006-03-17. + */ + if (utfch == BACKSLASH) { + utfch = readBackslashSequence(false, tok); + if (utfch == 0) { + _state = State.LITERAL_UTF16_2; // skip the wrong char and reset to previous state + continue; /* Invalid character, ignore. */ + } + } + buffer.append((char) utfch); } - return d; + } } - - - /* Extract a byte from _rawBytes. In order to allow for byte-short - situations, any byte off the end is returned as 0. */ - private int rawByte (int idx) - { - if (idx >= _rawBytes.size ()) { - return 0; + } + + /** + * Convert the raw hex data.Two buffers are saved: _rawBytes for the untranslated hex-encoded + * data, and _value for the PDF or UTF encoded string. + * + * @throws edu.harvard.hul.ois.jhove.module.pdf.PdfException + */ + public void convertHex() throws PdfException { + if (_rawBytes != null) { + boolean utf = false; + StringBuilder localBuffer = new StringBuilder(); + // If a high byte is left hanging, complete it with a '0' + if (haveHi) { + _rawBytes.add(hexToInt(hi, '0')); + } + if (_rawBytes.size() >= 2 && rawByte(0) == 0XFE && rawByte(1) == 0XFF) { + utf = true; + } + if (utf) { + // Gather pairs of bytes into characters without conversion + for (int i = 2; i < _rawBytes.size(); i += 2) { + localBuffer.append((char) (rawByte(i) * 256 + rawByte(i + 1))); } - return _rawBytes.elementAt(idx); + } else { + // Convert single bytes to PDF encoded characters. + for (int i = 0; i < _rawBytes.size(); i++) { + localBuffer.append(Tokenizer.PDFDOCENCODING[rawByte(i)]); + } + } + _value = localBuffer.toString(); } - - - /** - * Returns true if this string is in PDFDocEncoding, - * false if UTF-16. - * - * @return isPdfDocEncoding - */ - public boolean isPDFDocEncoding () - { - return _pdfDocEncoding; + } + + private static int hexToInt(int c1, int c2) throws PdfException { + return 16 * hexValue(c1) + hexValue(c2); + } + + private static int hexValue(int h) throws PdfException { + int d = 0; + if (0x30 <= h && h <= 0x39) { + // digit 0-9 + d = h - 0x30; + } else if (0x41 <= h && h <= 0x46) { + // letter A-F + d = h - 0x37; + } else if (0x61 <= h && h <= 0x66) { + // letter a-f + d = h - 0x57; + } else { + throw new PdfMalformedException(MessageConstants.PDF_HUL_11); // PDF-HUL-11 } - - /** - * Sets the value of pDFDocEncoding. - * @param pdfDocEncoding: boolean if the is in PDFDocEncoding - */ - public void setPDFDocEncoding (boolean pdfDocEncoding) - { - _pdfDocEncoding = pdfDocEncoding; + return d; + } + + /* Extract a byte from _rawBytes. In order to allow for byte-short + situations, any byte off the end is returned as 0. */ + private int rawByte(int idx) { + if (idx >= _rawBytes.size()) { + return 0; } - - /** - * Returns true if the string value is a parsable date. - * Conforms to the ASN.1 date format: D:YYYYMMDDHHmmSSOHH'mm' - * where everything before and after YYYY is optional. - * If we take this literally, the format is frighteningly ambiguous - * (imagine, for instance, leaving out hours but not minutes and - * seconds), so the checking is a bit loose. - * - * @return if it's a Date - */ - public boolean isDate () - { - return parseDate () != null; + return _rawBytes.elementAt(idx); + } + + /** + * Returns true if this string is in PDFDocEncoding, false if UTF-16. + * + * @return isPdfDocEncoding + */ + public boolean isPDFDocEncoding() { + return _pdfDocEncoding; + } + + /** + * Sets the value of pDFDocEncoding. + * + * @param pdfDocEncoding: boolean if the is in PDFDocEncoding + */ + public void setPDFDocEncoding(boolean pdfDocEncoding) { + _pdfDocEncoding = pdfDocEncoding; + } + + /** + * Returns true if the string value is a parsable date. Conforms to the ASN.1 date + * format: D:YYYYMMDDHHmmSSOHH'mm' where everything before and after YYYY is optional. If we take + * this literally, the format is frighteningly ambiguous (imagine, for instance, leaving out hours + * but not minutes and seconds), so the checking is a bit loose. + * + * @return if it's a Date + */ + public boolean isDate() { + return parseDate() != null; + } + + /** + * Parse the string value to a date. PDF dates conform to the ASN.1 date format. This consists of + * D:YYYYMMDDHHmmSSOHH'mm' where everything before and after YYYY is optional. Adobe doesn't + * actually say so, but I'm assuming that if a field is included, everything to its left must be + * included, e.g., you can't have seconds but leave out minutes. + * + * @return date of string value + */ + public Date parseDate() { + int year = 0; + int month = 0; + int day = 0; + int hour = 0; + int minute = 0; + int second = 0; + char timezonechar = '?'; // +, -, or Z + int timezonehour = 0; + int timezoneminute = 0; + Calendar cal; + + String str = getValue(); + if (str == null) { + return null; } - - /** - * Parse the string value to a date. PDF dates conform to - * the ASN.1 date format. This consists of - * D:YYYYMMDDHHmmSSOHH'mm' - * where everything before and after YYYY is optional. - * Adobe doesn't actually say so, but I'm assuming that if a - * field is included, everything to its left must be included, - * e.g., you can't have seconds but leave out minutes. - * - * @return date of string value - */ - public Date parseDate () - { - int year = 0; - int month = 0; - int day = 0; - int hour = 0; - int minute = 0; - int second = 0; - char timezonechar = '?'; // +, -, or Z - int timezonehour = 0; - int timezoneminute = 0; - Calendar cal; - - String str = getValue (); - if (str == null) { - return null; - } - str = str.trim (); - if (str.length() < 4) { - return null; - } - int datestate = 0; - int charidx = 0; - try { - wloop: - while (charidx < str.length ()) { - // We parse the date using a simple state machine, - // with a state for each date component. - switch (datestate) { - case 0: // starting state, may start with "D:" - if ("D:".equals (str.substring (charidx, charidx + 2))) { - charidx += 2; - } - datestate = 1; // advance regardless - break; - - case 1: // expecting year - year = Integer.parseInt (str.substring (charidx, charidx + 4)); - charidx += 4; - datestate = 2; - break; - - case 2: // expecting month - month = Integer.parseInt (str.substring (charidx, charidx+2)); - charidx += 2; - datestate = 3; - break; - - case 3: // expecting day of month - day = Integer.parseInt (str.substring (charidx, charidx + 2)); - if (day < 1 || day > 31) { - return null; - } - charidx += 2; - datestate = 4; - break; - - case 4: // expecting hour (00-23) - hour = Integer.parseInt (str.substring (charidx, charidx + 2)); - charidx += 2; - datestate = 5; - break; - - case 5: // expecting minute (00-59) - minute = Integer.parseInt (str.substring (charidx, charidx+2)); - charidx += 2; - datestate = 6; - break; - - case 6: // expecting second (00-59) - second = Integer.parseInt (str.substring (charidx, charidx+2)); - charidx += 2; - datestate = 7; - break; - - case 7: // expecting time zone ('+', '-', or 'Z') - timezonechar = str.charAt (charidx); - if (timezonechar != 'Z' && timezonechar != '+' && - timezonechar != '-') { - return null; - } - charidx++; - datestate = 8; - break; - - case 8: // expecting time zone hour. - // ignore if timezonechar is 'Z' - if (timezonechar == '+' || timezonechar == '-') { - timezonehour = Integer.parseInt (str.substring (charidx, - charidx + 2)); - if (timezonechar == '-') { - timezonehour = -timezonehour; - } - // Time zone hour must have trailing quote - if (!str.substring (charidx+2, charidx+3).equals ("'")) { - return null; - } - charidx += 3; - } - datestate = 9; - break; - - case 9: // expecting time zone minute -- in single quotes - // ignore if timezonechar is 'Z' - if (timezonechar == '+' || timezonechar == '-') { - if (str.charAt (charidx) == '\'') { - timezoneminute = - Integer.parseInt (str.substring (charidx, - charidx + 2)); - } - if (timezonechar == '-') { - timezoneminute = -timezoneminute; - } - // Time zone minute must have trailing quote - if (!str.substring (charidx+2, charidx+3).equals ("'")) { - return null; - } - } - break wloop; + str = str.trim(); + if (str.length() < 4) { + return null; + } + int datestate = 0; + int charidx = 0; + try { + wloop: + while (charidx < str.length()) { + // We parse the date using a simple state machine, + // with a state for each date component. + switch (datestate) { + case 0: // starting state, may start with "D:" + if ("D:".equals(str.substring(charidx, charidx + 2))) { + charidx += 2; } - } - } - // Previously, we assumed that a parsing exception meant the - // end of the date. This is too permissive; an exception means - // that the date is not well-formed. - catch (Exception e) { - return null; - } - if (datestate < 2) { - return null; // not enough fields - } - // First we must construct the time zone string, then use - // it to make a TimeZone object. - if (timezonechar != '?') { - String tzStr = "GMT"; - if (timezonechar == 'Z') { - tzStr += "+0000"; + datestate = 1; // advance regardless + break; + + case 1: // expecting year + year = Integer.parseInt(str.substring(charidx, charidx + 4)); + charidx += 4; + datestate = 2; + break; + + case 2: // expecting month + month = Integer.parseInt(str.substring(charidx, charidx + 2)); + charidx += 2; + datestate = 3; + break; + + case 3: // expecting day of month + day = Integer.parseInt(str.substring(charidx, charidx + 2)); + if (day < 1 || day > 31) { + return null; } - else { - tzStr += timezonechar; - NumberFormat nfmt = NumberFormat.getInstance (); - nfmt.setMinimumIntegerDigits (2); - nfmt.setMaximumIntegerDigits (2); - tzStr += nfmt.format (timezonehour); - tzStr += nfmt.format (timezoneminute); + charidx += 2; + datestate = 4; + break; + + case 4: // expecting hour (00-23) + hour = Integer.parseInt(str.substring(charidx, charidx + 2)); + charidx += 2; + datestate = 5; + break; + + case 5: // expecting minute (00-59) + minute = Integer.parseInt(str.substring(charidx, charidx + 2)); + charidx += 2; + datestate = 6; + break; + + case 6: // expecting second (00-59) + second = Integer.parseInt(str.substring(charidx, charidx + 2)); + charidx += 2; + datestate = 7; + break; + + case 7: // expecting time zone ('+', '-', or 'Z') + timezonechar = str.charAt(charidx); + if (timezonechar != 'Z' && timezonechar != '+' && timezonechar != '-') { + return null; } - TimeZone tz = TimeZone.getTimeZone (tzStr); - - // Use that TimeZone to create a Calendar with our date. - // Note that Java months are 0-based. - cal = Calendar.getInstance (tz); - } - else { - // time zone is unspecified - cal = Calendar.getInstance (); + charidx++; + datestate = 8; + break; + + case 8: // expecting time zone hour. + // ignore if timezonechar is 'Z' + if (timezonechar == '+' || timezonechar == '-') { + timezonehour = Integer.parseInt(str.substring(charidx, charidx + 2)); + if (timezonechar == '-') { + timezonehour = -timezonehour; + } + // Time zone hour must have trailing quote + if (!str.substring(charidx + 2, charidx + 3).equals("'")) { + return null; + } + charidx += 3; + } + datestate = 9; + break; + + case 9: // expecting time zone minute -- in single quotes + // ignore if timezonechar is 'Z' + if (timezonechar == '+' || timezonechar == '-') { + if (str.charAt(charidx) == '\'') { + timezoneminute = Integer.parseInt(str.substring(charidx, charidx + 2)); + } + if (timezonechar == '-') { + timezoneminute = -timezoneminute; + } + // Time zone minute must have trailing quote + if (!str.substring(charidx + 2, charidx + 3).equals("'")) { + return null; + } + } + break wloop; } - cal.set (year, month - 1, day, hour, minute, second); - return cal.getTime (); + } } - - - /** - * Returns true if this token doesn't violate any - * PDF/A rules, false if it does. - * @return if it's PDF/A compliant - */ - public boolean isPDFACompliant () - { - return _pdfACompliant; + // Previously, we assumed that a parsing exception meant the + // end of the date. This is too permissive; an exception means + // that the date is not well-formed. + catch (Exception e) { + return null; } - - -/* private void beginBackslashState () - { - octalBufLen = 0; - backslashFlag = true; + if (datestate < 2) { + return null; // not enough fields } -*/ - - - - /** After a backslash, read characters into an escape - sequence. If we don't find a valid escape sequence, - return 0. - */ - private int readBackslashSequence (boolean utf16, Tokenizer tok) - throws IOException - { - int ch = tok.readChar1 (utf16); - if (ch >= 0X30 && ch <= 0X37) { - int num = ch - 0X30; - // Read octal sequence. We may get 1, 2, or 3 characters. - // If we get a non-numeric character, we're done and we - // put it back. - for (int i = 0; i < 2; i++) { - int ch1 = tok.readChar1 (utf16); - if (ch1 >= 0X30 && ch1 <= 0X37) { - num = num * 8 + (ch1 - 0X30); - } - else { - //_fileBufferOffset--; // put it back - tok.backupChar (); // add this function to Tokenizer**** - _pdfACompliant = false; // octal sequences must be 3 chars in PDF/A - return num; - } - } - return num; - } - switch (ch) { - case 0X6E: // n - return LF; - case 0X72: // r - return CR; - case 0xd: // this is an error for CR - return 0; - case 0X74: // t - return HT; - case 0X68: // h - return BS; - case 0X66: // f - return FORMFEED; - case OPEN_PARENTHESIS: - return OPEN_PARENTHESIS; - case CLOSE_PARENTHESIS: - return CLOSE_PARENTHESIS; - case BACKSLASH: - return BACKSLASH; - default: - return 0; - } + // First we must construct the time zone string, then use + // it to make a TimeZone object. + if (timezonechar != '?') { + String tzStr = "GMT"; + if (timezonechar == 'Z') { + tzStr += "+0000"; + } else { + tzStr += timezonechar; + NumberFormat nfmt = NumberFormat.getInstance(); + nfmt.setMinimumIntegerDigits(2); + nfmt.setMaximumIntegerDigits(2); + tzStr += nfmt.format(timezonehour); + tzStr += nfmt.format(timezoneminute); + } + TimeZone tz = TimeZone.getTimeZone(tzStr); + + // Use that TimeZone to create a Calendar with our date. + // Note that Java months are 0-based. + cal = Calendar.getInstance(tz); + } else { + // time zone is unspecified + cal = Calendar.getInstance(); } - - - /** We have just read an ESC in a UTF string. - Save all character up to and exclusive of the next ESC - as a language code. - */ - private static void readUTFLanguageCode (Tokenizer tok) throws IOException - { - StringBuilder sb = new StringBuilder(); - for (;;) { - int ch = tok.readChar1(true); - if (ch == ESC) { - break; - } - sb.append ((char) ch); + cal.set(year, month - 1, day, hour, minute, second); + return cal.getTime(); + } + + /** + * Returns true if this token doesn't violate any PDF/A rules, false if + * it does. + * + * @return if it's PDF/A compliant + */ + public boolean isPDFACompliant() { + return _pdfACompliant; + } + + /* private void beginBackslashState () + { + octalBufLen = 0; + backslashFlag = true; + } + */ + + /** + * After a backslash, read characters into an escape sequence. If we don't find a valid escape + * sequence, return 0. + */ + private int readBackslashSequence(boolean utf16, Tokenizer tok) throws IOException { + int ch = tok.readChar1(utf16); + if (ch >= 0X30 && ch <= 0X37) { + int num = ch - 0X30; + // Read octal sequence. We may get 1, 2, or 3 characters. + // If we get a non-numeric character, we're done and we + // put it back. + for (int i = 0; i < 2; i++) { + int ch1 = tok.readChar1(utf16); + if (ch1 >= 0X30 && ch1 <= 0X37) { + num = num * 8 + (ch1 - 0X30); + } else { + // _fileBufferOffset--; // put it back + tok.backupChar(); // add this function to Tokenizer**** + _pdfACompliant = false; // octal sequences must be 3 chars in PDF/A + return num; } - tok.addLanguageCode (sb.toString ()); // ****add this to Tokenizer - //_languageCodes.add (sb.toString ()); + } + return num; } - - /** If we're in the backslash substate (backslashFlag = true), then call - this to process characters. It will accumulate octal digits into - octalBuf and process other escaped characters. If the accumulation - produces a character, it will return that character code, otherwise - it will return 0 to indicate no character is available yet. - - Althought the backslash itself is a byte, even in a 16-bit - string, the characters which follow it are 16-bit characters, - not bytes. So we call this only after applying UTF-16 encoding - where applicable. - */ - - /* DEPRECATED for the current millisecond */ -/* private int backslashProcess (int ch) - { - if (ch >= 0X30 && ch <= 0X37) { - int num = ch - 0X30; - // An octal sequence may have 1, 2, or 3 characters. - // If we get a non-numeric character, we're done and - // return the character, and put the character we - // just received into a holding buffer. - octalBuf[octalBufLen++] = num; - if (octalBufLen == 3) { - return octalBufValue (); - } - for (int i = 0; i < 2; i++) { - int ch1 = readChar1 (utf16); - if (ch1 >= 0X30 && ch1 <= 0X37) { - num = num * 8 + (ch1 - 0X30); - } - else { - holdChar = ch; - _pdfACompliant = false; // octal sequences must be 3 chars in PDF/A - return num; - } - } - return num; - } - - // If no octal characters have been seen yet, look for an - // escaped character. - if (octalBufLen == 0) { - switch (ch) { - case 0X6E: // n - return LF; - case 0X72: // r - return CR; - case 0X74: // t - return HT; - case 0X68: // h - return BS; - case 0X66: // f - return FORMFEED; - case OPEN_PARENTHESIS: - return OPEN_PARENTHESIS; - case CLOSE_PARENTHESIS: - return CLOSE_PARENTHESIS; - case BACKSLASH: - return BACKSLASH; - default: - // illegal escape -- dump the character - return 0; - } - else { - // We have one or two buffered octal characters, - // but this isn't one. Put the current character - // in a holding buffer, and return the octal value. - holdCh = ch; - return octalBufValue (); - } - } + switch (ch) { + case 0X6E: // n + return LF; + case 0X72: // r + return CR; + case 0xd: // this is an error for CR + return 0; + case 0X74: // t + return HT; + case 0X68: // h + return BS; + case 0X66: // f + return FORMFEED; + case OPEN_PARENTHESIS: + return OPEN_PARENTHESIS; + case CLOSE_PARENTHESIS: + return CLOSE_PARENTHESIS; + case BACKSLASH: + return BACKSLASH; + default: + return 0; + } + } + + /** + * We have just read an ESC in a UTF string. Save all character up to and exclusive of the next + * ESC as a language code. + */ + private static void readUTFLanguageCode(Tokenizer tok) throws IOException { + StringBuilder sb = new StringBuilder(); + for (; ; ) { + int ch = tok.readChar1(true); + if (ch == ESC) { + break; + } + sb.append((char) ch); } - */ + tok.addLanguageCode(sb.toString()); // ****add this to Tokenizer + // _languageCodes.add (sb.toString ()); + } + + /** + * If we're in the backslash substate (backslashFlag = true), then call this to process + * characters. It will accumulate octal digits into octalBuf and process other escaped characters. + * If the accumulation produces a character, it will return that character code, otherwise it will + * return 0 to indicate no character is available yet. + * + *

Althought the backslash itself is a byte, even in a 16-bit string, the characters which + * follow it are 16-bit characters, not bytes. So we call this only after applying UTF-16 encoding + * where applicable. + */ + + /* DEPRECATED for the current millisecond */ + /* private int backslashProcess (int ch) + { + if (ch >= 0X30 && ch <= 0X37) { + int num = ch - 0X30; + // An octal sequence may have 1, 2, or 3 characters. + // If we get a non-numeric character, we're done and + // return the character, and put the character we + // just received into a holding buffer. + octalBuf[octalBufLen++] = num; + if (octalBufLen == 3) { + return octalBufValue (); + } + for (int i = 0; i < 2; i++) { + int ch1 = readChar1 (utf16); + if (ch1 >= 0X30 && ch1 <= 0X37) { + num = num * 8 + (ch1 - 0X30); + } + else { + holdChar = ch; + _pdfACompliant = false; // octal sequences must be 3 chars in PDF/A + return num; + } + } + return num; + } + + // If no octal characters have been seen yet, look for an + // escaped character. + if (octalBufLen == 0) { + switch (ch) { + case 0X6E: // n + return LF; + case 0X72: // r + return CR; + case 0X74: // t + return HT; + case 0X68: // h + return BS; + case 0X66: // f + return FORMFEED; + case OPEN_PARENTHESIS: + return OPEN_PARENTHESIS; + case CLOSE_PARENTHESIS: + return CLOSE_PARENTHESIS; + case BACKSLASH: + return BACKSLASH; + default: + // illegal escape -- dump the character + return 0; + } + else { + // We have one or two buffered octal characters, + // but this isn't one. Put the current character + // in a holding buffer, and return the octal value. + holdCh = ch; + return octalBufValue (); + } + } + } + */ } diff --git a/jhove-modules/pdf-hul/src/main/java/edu/harvard/hul/ois/jhove/module/pdf/MessageConstants.java b/jhove-modules/pdf-hul/src/main/java/edu/harvard/hul/ois/jhove/module/pdf/MessageConstants.java index 2d7071a21..e3316c0ee 100644 --- a/jhove-modules/pdf-hul/src/main/java/edu/harvard/hul/ois/jhove/module/pdf/MessageConstants.java +++ b/jhove-modules/pdf-hul/src/main/java/edu/harvard/hul/ois/jhove/module/pdf/MessageConstants.java @@ -5,335 +5,427 @@ import edu.harvard.hul.ois.jhove.messages.JhoveMessages; /** - * Enum used to externalise the PDF modules message Strings. Using an enum - * INSTANCE as a "trick" to ensure a single instance of the class. String - * constants should be prefixed according to their use in the module: + * Enum used to externalise the PDF modules message Strings. Using an enum INSTANCE as a "trick" to + * ensure a single instance of the class. String constants should be prefixed according to their use + * in the module: + * *

    - *
  • LOG_ for messages used exclusively by the logger.
  • - *
  • INF_ for informational messages.
  • - *
  • ERR_ for error messages that indicate a file is invalid or not well - * formed.
  • + *
  • LOG_ for messages used exclusively by the logger. + *
  • INF_ for informational messages. + *
  • ERR_ for error messages that indicate a file is invalid or not well formed. *
- * When adding new messages try to adopt the following order for the naming - * elements: + * + * When adding new messages try to adopt the following order for the naming elements: + * *
    - *
  1. PREFIX: one of the three prefixes from the list above.
  2. - *
  3. ENTITY_NAME: the name of the PDF entity causing the prohlem, e.g. FONT, - * or DOC.
  4. - *
  5. Problem: a short indicator of the problem type, e.g. MISSING, ILLEGAL, - * etc.
  6. + *
  7. PREFIX: one of the three prefixes from the list above. + *
  8. ENTITY_NAME: the name of the PDF entity causing the prohlem, e.g. FONT, or DOC. + *
  9. Problem: a short indicator of the problem type, e.g. MISSING, ILLEGAL, etc. *
- * The elements should be separated by underscores. The messages currently don't - * follow a consistent vocabulary, that is terms such as invalid, illegal, or - * malformed are used without definition. * - * @author Carl Wilson - * carlwilson AT github + * The elements should be separated by underscores. The messages currently don't follow a consistent + * vocabulary, that is terms such as invalid, illegal, or malformed are used without definition. + * + * @author Carl Wilson carlwilson AT github * @version 0.1 Created 1 Oct 2016:11:38:18 */ - public enum MessageConstants { - INSTANCE; + INSTANCE; + + public static final JhoveMessageFactory messageFactory = + JhoveMessages.getInstance("edu.harvard.hul.ois.jhove.module.pdf.ErrorMessages"); - public static final JhoveMessageFactory messageFactory = JhoveMessages.getInstance("edu.harvard.hul.ois.jhove.module.pdf.ErrorMessages"); + public static final JhoveMessage PDF_HUL_1 = messageFactory.getMessage("PDF-HUL-1"); + public static final JhoveMessage PDF_HUL_2 = messageFactory.getMessage("PDF-HUL-2"); + public static final JhoveMessage PDF_HUL_3 = messageFactory.getMessage("PDF-HUL-3"); + public static final JhoveMessage PDF_HUL_4 = messageFactory.getMessage("PDF-HUL-4"); + public static final JhoveMessage PDF_HUL_5 = messageFactory.getMessage("PDF-HUL-5"); + public static final JhoveMessage PDF_HUL_6 = messageFactory.getMessage("PDF-HUL-6"); + public static final JhoveMessage PDF_HUL_7 = messageFactory.getMessage("PDF-HUL-7"); + public static final JhoveMessage PDF_HUL_8 = messageFactory.getMessage("PDF-HUL-8"); + public static final JhoveMessage PDF_HUL_9 = messageFactory.getMessage("PDF-HUL-9"); + public static final JhoveMessage PDF_HUL_10 = messageFactory.getMessage("PDF-HUL-10"); + public static final JhoveMessage PDF_HUL_11 = messageFactory.getMessage("PDF-HUL-11"); + public static final JhoveMessage PDF_HUL_12 = messageFactory.getMessage("PDF-HUL-12"); + public static final JhoveMessage PDF_HUL_13 = messageFactory.getMessage("PDF-HUL-13"); + public static final JhoveMessage PDF_HUL_14 = messageFactory.getMessage("PDF-HUL-14"); + public static final JhoveMessage PDF_HUL_15 = messageFactory.getMessage("PDF-HUL-15"); + public static final JhoveMessage PDF_HUL_16 = messageFactory.getMessage("PDF-HUL-16"); + public static final JhoveMessage PDF_HUL_17 = messageFactory.getMessage("PDF-HUL-17"); + public static final JhoveMessage PDF_HUL_18 = messageFactory.getMessage("PDF-HUL-18"); + public static final JhoveMessage PDF_HUL_19 = messageFactory.getMessage("PDF-HUL-19"); + public static final JhoveMessage PDF_HUL_20 = messageFactory.getMessage("PDF-HUL-20"); + public static final JhoveMessage PDF_HUL_21 = messageFactory.getMessage("PDF-HUL-21"); + public static final JhoveMessage PDF_HUL_22 = messageFactory.getMessage("PDF-HUL-22"); + public static final JhoveMessage PDF_HUL_23 = messageFactory.getMessage("PDF-HUL-23"); + public static final JhoveMessage PDF_HUL_24 = messageFactory.getMessage("PDF-HUL-24"); + public static final JhoveMessage PDF_HUL_25 = messageFactory.getMessage("PDF-HUL-25"); + public static final JhoveMessage PDF_HUL_26 = messageFactory.getMessage("PDF-HUL-26"); + public static final JhoveMessage PDF_HUL_27 = messageFactory.getMessage("PDF-HUL-27"); + public static final JhoveMessage PDF_HUL_28 = messageFactory.getMessage("PDF-HUL-28"); + public static final JhoveMessage PDF_HUL_29 = messageFactory.getMessage("PDF-HUL-29"); + public static final JhoveMessage PDF_HUL_30 = messageFactory.getMessage("PDF-HUL-30"); + public static final JhoveMessage PDF_HUL_31 = messageFactory.getMessage("PDF-HUL-31"); + public static final JhoveMessage PDF_HUL_32 = messageFactory.getMessage("PDF-HUL-32"); + public static final JhoveMessage PDF_HUL_33 = messageFactory.getMessage("PDF-HUL-33"); + public static final JhoveMessage PDF_HUL_34 = messageFactory.getMessage("PDF-HUL-34"); + public static final JhoveMessage PDF_HUL_35 = messageFactory.getMessage("PDF-HUL-35"); + public static final JhoveMessage PDF_HUL_36 = messageFactory.getMessage("PDF-HUL-36"); + public static final JhoveMessage PDF_HUL_37 = messageFactory.getMessage("PDF-HUL-37"); + public static final JhoveMessage PDF_HUL_38 = messageFactory.getMessage("PDF-HUL-38"); + public static final JhoveMessage PDF_HUL_39 = messageFactory.getMessage("PDF-HUL-39"); + public static final JhoveMessage PDF_HUL_40 = messageFactory.getMessage("PDF-HUL-40"); + public static final JhoveMessage PDF_HUL_41 = messageFactory.getMessage("PDF-HUL-41"); + public static final JhoveMessage PDF_HUL_42 = messageFactory.getMessage("PDF-HUL-42"); + public static final JhoveMessage PDF_HUL_43 = messageFactory.getMessage("PDF-HUL-43"); + public static final JhoveMessage PDF_HUL_44 = messageFactory.getMessage("PDF-HUL-44"); + public static final JhoveMessage PDF_HUL_45 = messageFactory.getMessage("PDF-HUL-45"); + public static final JhoveMessage PDF_HUL_46 = messageFactory.getMessage("PDF-HUL-46"); + public static final JhoveMessage PDF_HUL_47 = messageFactory.getMessage("PDF-HUL-47"); + public static final JhoveMessage PDF_HUL_48 = messageFactory.getMessage("PDF-HUL-48"); + public static final JhoveMessage PDF_HUL_49 = messageFactory.getMessage("PDF-HUL-49"); + public static final JhoveMessage PDF_HUL_50 = messageFactory.getMessage("PDF-HUL-50"); + public static final JhoveMessage PDF_HUL_51 = messageFactory.getMessage("PDF-HUL-51"); + public static final JhoveMessage PDF_HUL_52 = messageFactory.getMessage("PDF-HUL-52"); + public static final JhoveMessage PDF_HUL_53 = messageFactory.getMessage("PDF-HUL-53"); + public static final JhoveMessage PDF_HUL_54 = messageFactory.getMessage("PDF-HUL-54"); + public static final JhoveMessage PDF_HUL_55 = messageFactory.getMessage("PDF-HUL-55"); + public static final JhoveMessage PDF_HUL_56 = messageFactory.getMessage("PDF-HUL-56"); + public static final JhoveMessage PDF_HUL_57 = messageFactory.getMessage("PDF-HUL-57"); + public static final JhoveMessage PDF_HUL_58 = messageFactory.getMessage("PDF-HUL-58"); + public static final JhoveMessage PDF_HUL_59 = messageFactory.getMessage("PDF-HUL-59"); + public static final JhoveMessage PDF_HUL_60 = messageFactory.getMessage("PDF-HUL-60"); + public static final JhoveMessage PDF_HUL_61 = messageFactory.getMessage("PDF-HUL-61"); + public static final JhoveMessage PDF_HUL_62 = messageFactory.getMessage("PDF-HUL-62"); + public static final JhoveMessage PDF_HUL_63 = messageFactory.getMessage("PDF-HUL-63"); + public static final JhoveMessage PDF_HUL_64 = messageFactory.getMessage("PDF-HUL-64"); + public static final JhoveMessage PDF_HUL_65 = messageFactory.getMessage("PDF-HUL-65"); + public static final JhoveMessage PDF_HUL_66 = messageFactory.getMessage("PDF-HUL-66"); + public static final JhoveMessage PDF_HUL_67 = messageFactory.getMessage("PDF-HUL-67"); + public static final JhoveMessage PDF_HUL_68 = messageFactory.getMessage("PDF-HUL-68"); + public static final JhoveMessage PDF_HUL_69 = messageFactory.getMessage("PDF-HUL-69"); + public static final JhoveMessage PDF_HUL_70 = messageFactory.getMessage("PDF-HUL-70"); + public static final JhoveMessage PDF_HUL_71 = messageFactory.getMessage("PDF-HUL-71"); + public static final JhoveMessage PDF_HUL_72 = messageFactory.getMessage("PDF-HUL-72"); + public static final JhoveMessage PDF_HUL_73 = messageFactory.getMessage("PDF-HUL-73"); + public static final JhoveMessage PDF_HUL_74 = messageFactory.getMessage("PDF-HUL-74"); + public static final JhoveMessage PDF_HUL_75 = messageFactory.getMessage("PDF-HUL-75"); + public static final JhoveMessage PDF_HUL_76 = messageFactory.getMessage("PDF-HUL-76"); + public static final JhoveMessage PDF_HUL_77 = messageFactory.getMessage("PDF-HUL-77"); + public static final JhoveMessage PDF_HUL_78 = messageFactory.getMessage("PDF-HUL-78"); + public static final JhoveMessage PDF_HUL_79 = messageFactory.getMessage("PDF-HUL-79"); + public static final JhoveMessage PDF_HUL_80 = messageFactory.getMessage("PDF-HUL-80"); + public static final JhoveMessage PDF_HUL_81 = messageFactory.getMessage("PDF-HUL-81"); + public static final JhoveMessage PDF_HUL_82 = messageFactory.getMessage("PDF-HUL-82"); + public static final JhoveMessage PDF_HUL_83 = messageFactory.getMessage("PDF-HUL-83"); + public static final JhoveMessage PDF_HUL_84 = messageFactory.getMessage("PDF-HUL-84"); + public static final JhoveMessage PDF_HUL_85 = messageFactory.getMessage("PDF-HUL-85"); + public static final JhoveMessage PDF_HUL_86 = messageFactory.getMessage("PDF-HUL-86"); + public static final JhoveMessage PDF_HUL_87 = messageFactory.getMessage("PDF-HUL-87"); + public static final JhoveMessage PDF_HUL_88 = messageFactory.getMessage("PDF-HUL-88"); + public static final JhoveMessage PDF_HUL_89 = messageFactory.getMessage("PDF-HUL-89"); + public static final JhoveMessage PDF_HUL_90 = messageFactory.getMessage("PDF-HUL-90"); + public static final JhoveMessage PDF_HUL_91 = messageFactory.getMessage("PDF-HUL-91"); + public static final JhoveMessage PDF_HUL_92 = messageFactory.getMessage("PDF-HUL-92"); + public static final JhoveMessage PDF_HUL_93 = messageFactory.getMessage("PDF-HUL-93"); + public static final JhoveMessage PDF_HUL_94 = messageFactory.getMessage("PDF-HUL-94"); + public static final JhoveMessage PDF_HUL_95 = messageFactory.getMessage("PDF-HUL-95"); + public static final JhoveMessage PDF_HUL_96 = messageFactory.getMessage("PDF-HUL-96"); + public static final JhoveMessage PDF_HUL_97 = messageFactory.getMessage("PDF-HUL-97"); + public static final JhoveMessage PDF_HUL_98 = messageFactory.getMessage("PDF-HUL-98"); + public static final JhoveMessage PDF_HUL_99 = messageFactory.getMessage("PDF-HUL-99"); + public static final JhoveMessage PDF_HUL_100 = messageFactory.getMessage("PDF-HUL-100"); + public static final JhoveMessage PDF_HUL_101 = messageFactory.getMessage("PDF-HUL-101"); + public static final JhoveMessage PDF_HUL_102 = messageFactory.getMessage("PDF-HUL-102"); + public static final JhoveMessage PDF_HUL_103 = messageFactory.getMessage("PDF-HUL-103"); + public static final JhoveMessage PDF_HUL_104 = messageFactory.getMessage("PDF-HUL-104"); + public static final JhoveMessage PDF_HUL_105 = messageFactory.getMessage("PDF-HUL-105"); + public static final JhoveMessage PDF_HUL_106 = messageFactory.getMessage("PDF-HUL-106"); + public static final JhoveMessage PDF_HUL_107 = messageFactory.getMessage("PDF-HUL-107"); + public static final JhoveMessage PDF_HUL_108 = messageFactory.getMessage("PDF-HUL-108"); + public static final JhoveMessage PDF_HUL_109 = messageFactory.getMessage("PDF-HUL-109"); + public static final JhoveMessage PDF_HUL_110 = messageFactory.getMessage("PDF-HUL-110"); + public static final JhoveMessage PDF_HUL_111 = messageFactory.getMessage("PDF-HUL-111"); + public static final JhoveMessage PDF_HUL_112 = messageFactory.getMessage("PDF-HUL-112"); + public static final JhoveMessage PDF_HUL_113 = messageFactory.getMessage("PDF-HUL-113"); + public static final JhoveMessage PDF_HUL_114 = messageFactory.getMessage("PDF-HUL-114"); + public static final JhoveMessage PDF_HUL_115 = messageFactory.getMessage("PDF-HUL-115"); + public static final JhoveMessage PDF_HUL_116 = messageFactory.getMessage("PDF-HUL-116"); + public static final JhoveMessage PDF_HUL_117 = messageFactory.getMessage("PDF-HUL-117"); + public static final JhoveMessage PDF_HUL_118 = messageFactory.getMessage("PDF-HUL-118"); + public static final JhoveMessage PDF_HUL_119 = messageFactory.getMessage("PDF-HUL-119"); + public static final JhoveMessage PDF_HUL_120 = messageFactory.getMessage("PDF-HUL-120"); + public static final JhoveMessage PDF_HUL_121 = messageFactory.getMessage("PDF-HUL-121"); + public static final JhoveMessage PDF_HUL_122 = messageFactory.getMessage("PDF-HUL-122"); + public static final JhoveMessage PDF_HUL_123 = messageFactory.getMessage("PDF-HUL-123"); + public static final JhoveMessage PDF_HUL_124 = messageFactory.getMessage("PDF-HUL-124"); + public static final JhoveMessage PDF_HUL_125 = messageFactory.getMessage("PDF-HUL-125"); + public static final JhoveMessage PDF_HUL_126 = messageFactory.getMessage("PDF-HUL-126"); + public static final JhoveMessage PDF_HUL_127 = messageFactory.getMessage("PDF-HUL-127"); + public static final JhoveMessage PDF_HUL_128 = messageFactory.getMessage("PDF-HUL-128"); + public static final JhoveMessage PDF_HUL_129 = messageFactory.getMessage("PDF-HUL-129"); + public static final JhoveMessage PDF_HUL_130 = messageFactory.getMessage("PDF-HUL-130"); + public static final JhoveMessage PDF_HUL_131 = messageFactory.getMessage("PDF-HUL-131"); + public static final JhoveMessage PDF_HUL_132 = messageFactory.getMessage("PDF-HUL-132"); + public static final JhoveMessage PDF_HUL_133 = messageFactory.getMessage("PDF-HUL-133"); + public static final JhoveMessage PDF_HUL_134 = messageFactory.getMessage("PDF-HUL-134"); + public static final JhoveMessage PDF_HUL_135 = messageFactory.getMessage("PDF-HUL-135"); + public static final JhoveMessage PDF_HUL_136 = messageFactory.getMessage("PDF-HUL-136"); + public static final JhoveMessage PDF_HUL_136_SUB = messageFactory.getMessage("PDF-HUL-136-SUB"); + public static final JhoveMessage PDF_HUL_137 = messageFactory.getMessage("PDF-HUL-137"); + public static final JhoveMessage PDF_HUL_138 = messageFactory.getMessage("PDF-HUL-138"); + public static final JhoveMessage PDF_HUL_139 = messageFactory.getMessage("PDF-HUL-139"); + public static final JhoveMessage PDF_HUL_140 = messageFactory.getMessage("PDF-HUL-140"); + public static final JhoveMessage PDF_HUL_141 = messageFactory.getMessage("PDF-HUL-141"); + public static final JhoveMessage PDF_HUL_142 = messageFactory.getMessage("PDF-HUL-142"); + public static final JhoveMessage PDF_HUL_143 = messageFactory.getMessage("PDF-HUL-143"); + public static final JhoveMessage PDF_HUL_144 = messageFactory.getMessage("PDF-HUL-144"); + public static final JhoveMessage PDF_HUL_145 = messageFactory.getMessage("PDF-HUL-145"); + public static final JhoveMessage PDF_HUL_146 = messageFactory.getMessage("PDF-HUL-146"); + public static final JhoveMessage PDF_HUL_147 = messageFactory.getMessage("PDF-HUL-147"); + public static final JhoveMessage PDF_HUL_148 = messageFactory.getMessage("PDF-HUL-148"); + public static final JhoveMessage PDF_HUL_149 = messageFactory.getMessage("PDF-HUL-149"); + public static final JhoveMessage PDF_HUL_150 = messageFactory.getMessage("PDF-HUL-150"); + public static final JhoveMessage PDF_HUL_151 = messageFactory.getMessage("PDF-HUL-151"); + public static final JhoveMessage PDF_HUL_152 = messageFactory.getMessage("PDF-HUL-152"); + public static final JhoveMessage PDF_HUL_153 = messageFactory.getMessage("PDF-HUL-153"); - public static final JhoveMessage PDF_HUL_1 = messageFactory.getMessage("PDF-HUL-1"); - public static final JhoveMessage PDF_HUL_2 = messageFactory.getMessage("PDF-HUL-2"); - public static final JhoveMessage PDF_HUL_3 = messageFactory.getMessage("PDF-HUL-3"); - public static final JhoveMessage PDF_HUL_4 = messageFactory.getMessage("PDF-HUL-4"); - public static final JhoveMessage PDF_HUL_5 = messageFactory.getMessage("PDF-HUL-5"); - public static final JhoveMessage PDF_HUL_6 = messageFactory.getMessage("PDF-HUL-6"); - public static final JhoveMessage PDF_HUL_7 = messageFactory.getMessage("PDF-HUL-7"); - public static final JhoveMessage PDF_HUL_8 = messageFactory.getMessage("PDF-HUL-8"); - public static final JhoveMessage PDF_HUL_9 = messageFactory.getMessage("PDF-HUL-9"); - public static final JhoveMessage PDF_HUL_10 = messageFactory.getMessage("PDF-HUL-10"); - public static final JhoveMessage PDF_HUL_11 = messageFactory.getMessage("PDF-HUL-11"); - public static final JhoveMessage PDF_HUL_12 = messageFactory.getMessage("PDF-HUL-12"); - public static final JhoveMessage PDF_HUL_13 = messageFactory.getMessage("PDF-HUL-13"); - public static final JhoveMessage PDF_HUL_14 = messageFactory.getMessage("PDF-HUL-14"); - public static final JhoveMessage PDF_HUL_15 = messageFactory.getMessage("PDF-HUL-15"); - public static final JhoveMessage PDF_HUL_16 = messageFactory.getMessage("PDF-HUL-16"); - public static final JhoveMessage PDF_HUL_17 = messageFactory.getMessage("PDF-HUL-17"); - public static final JhoveMessage PDF_HUL_18 = messageFactory.getMessage("PDF-HUL-18"); - public static final JhoveMessage PDF_HUL_19 = messageFactory.getMessage("PDF-HUL-19"); - public static final JhoveMessage PDF_HUL_20 = messageFactory.getMessage("PDF-HUL-20"); - public static final JhoveMessage PDF_HUL_21 = messageFactory.getMessage("PDF-HUL-21"); - public static final JhoveMessage PDF_HUL_22 = messageFactory.getMessage("PDF-HUL-22"); - public static final JhoveMessage PDF_HUL_23 = messageFactory.getMessage("PDF-HUL-23"); - public static final JhoveMessage PDF_HUL_24 = messageFactory.getMessage("PDF-HUL-24"); - public static final JhoveMessage PDF_HUL_25 = messageFactory.getMessage("PDF-HUL-25"); - public static final JhoveMessage PDF_HUL_26 = messageFactory.getMessage("PDF-HUL-26"); - public static final JhoveMessage PDF_HUL_27 = messageFactory.getMessage("PDF-HUL-27"); - public static final JhoveMessage PDF_HUL_28 = messageFactory.getMessage("PDF-HUL-28"); - public static final JhoveMessage PDF_HUL_29 = messageFactory.getMessage("PDF-HUL-29"); - public static final JhoveMessage PDF_HUL_30 = messageFactory.getMessage("PDF-HUL-30"); - public static final JhoveMessage PDF_HUL_31 = messageFactory.getMessage("PDF-HUL-31"); - public static final JhoveMessage PDF_HUL_32 = messageFactory.getMessage("PDF-HUL-32"); - public static final JhoveMessage PDF_HUL_33 = messageFactory.getMessage("PDF-HUL-33"); - public static final JhoveMessage PDF_HUL_34 = messageFactory.getMessage("PDF-HUL-34"); - public static final JhoveMessage PDF_HUL_35 = messageFactory.getMessage("PDF-HUL-35"); - public static final JhoveMessage PDF_HUL_36 = messageFactory.getMessage("PDF-HUL-36"); - public static final JhoveMessage PDF_HUL_37 = messageFactory.getMessage("PDF-HUL-37"); - public static final JhoveMessage PDF_HUL_38 = messageFactory.getMessage("PDF-HUL-38"); - public static final JhoveMessage PDF_HUL_39 = messageFactory.getMessage("PDF-HUL-39"); - public static final JhoveMessage PDF_HUL_40 = messageFactory.getMessage("PDF-HUL-40"); - public static final JhoveMessage PDF_HUL_41 = messageFactory.getMessage("PDF-HUL-41"); - public static final JhoveMessage PDF_HUL_42 = messageFactory.getMessage("PDF-HUL-42"); - public static final JhoveMessage PDF_HUL_43 = messageFactory.getMessage("PDF-HUL-43"); - public static final JhoveMessage PDF_HUL_44 = messageFactory.getMessage("PDF-HUL-44"); - public static final JhoveMessage PDF_HUL_45 = messageFactory.getMessage("PDF-HUL-45"); - public static final JhoveMessage PDF_HUL_46 = messageFactory.getMessage("PDF-HUL-46"); - public static final JhoveMessage PDF_HUL_47 = messageFactory.getMessage("PDF-HUL-47"); - public static final JhoveMessage PDF_HUL_48 = messageFactory.getMessage("PDF-HUL-48"); - public static final JhoveMessage PDF_HUL_49 = messageFactory.getMessage("PDF-HUL-49"); - public static final JhoveMessage PDF_HUL_50 = messageFactory.getMessage("PDF-HUL-50"); - public static final JhoveMessage PDF_HUL_51 = messageFactory.getMessage("PDF-HUL-51"); - public static final JhoveMessage PDF_HUL_52 = messageFactory.getMessage("PDF-HUL-52"); - public static final JhoveMessage PDF_HUL_53 = messageFactory.getMessage("PDF-HUL-53"); - public static final JhoveMessage PDF_HUL_54 = messageFactory.getMessage("PDF-HUL-54"); - public static final JhoveMessage PDF_HUL_55 = messageFactory.getMessage("PDF-HUL-55"); - public static final JhoveMessage PDF_HUL_56 = messageFactory.getMessage("PDF-HUL-56"); - public static final JhoveMessage PDF_HUL_57 = messageFactory.getMessage("PDF-HUL-57"); - public static final JhoveMessage PDF_HUL_58 = messageFactory.getMessage("PDF-HUL-58"); - public static final JhoveMessage PDF_HUL_59 = messageFactory.getMessage("PDF-HUL-59"); - public static final JhoveMessage PDF_HUL_60 = messageFactory.getMessage("PDF-HUL-60"); - public static final JhoveMessage PDF_HUL_61 = messageFactory.getMessage("PDF-HUL-61"); - public static final JhoveMessage PDF_HUL_62 = messageFactory.getMessage("PDF-HUL-62"); - public static final JhoveMessage PDF_HUL_63 = messageFactory.getMessage("PDF-HUL-63"); - public static final JhoveMessage PDF_HUL_64 = messageFactory.getMessage("PDF-HUL-64"); - public static final JhoveMessage PDF_HUL_65 = messageFactory.getMessage("PDF-HUL-65"); - public static final JhoveMessage PDF_HUL_66 = messageFactory.getMessage("PDF-HUL-66"); - public static final JhoveMessage PDF_HUL_67 = messageFactory.getMessage("PDF-HUL-67"); - public static final JhoveMessage PDF_HUL_68 = messageFactory.getMessage("PDF-HUL-68"); - public static final JhoveMessage PDF_HUL_69 = messageFactory.getMessage("PDF-HUL-69"); - public static final JhoveMessage PDF_HUL_70 = messageFactory.getMessage("PDF-HUL-70"); - public static final JhoveMessage PDF_HUL_71 = messageFactory.getMessage("PDF-HUL-71"); - public static final JhoveMessage PDF_HUL_72 = messageFactory.getMessage("PDF-HUL-72"); - public static final JhoveMessage PDF_HUL_73 = messageFactory.getMessage("PDF-HUL-73"); - public static final JhoveMessage PDF_HUL_74 = messageFactory.getMessage("PDF-HUL-74"); - public static final JhoveMessage PDF_HUL_75 = messageFactory.getMessage("PDF-HUL-75"); - public static final JhoveMessage PDF_HUL_76 = messageFactory.getMessage("PDF-HUL-76"); - public static final JhoveMessage PDF_HUL_77 = messageFactory.getMessage("PDF-HUL-77"); - public static final JhoveMessage PDF_HUL_78 = messageFactory.getMessage("PDF-HUL-78"); - public static final JhoveMessage PDF_HUL_79 = messageFactory.getMessage("PDF-HUL-79"); - public static final JhoveMessage PDF_HUL_80 = messageFactory.getMessage("PDF-HUL-80"); - public static final JhoveMessage PDF_HUL_81 = messageFactory.getMessage("PDF-HUL-81"); - public static final JhoveMessage PDF_HUL_82 = messageFactory.getMessage("PDF-HUL-82"); - public static final JhoveMessage PDF_HUL_83 = messageFactory.getMessage("PDF-HUL-83"); - public static final JhoveMessage PDF_HUL_84 = messageFactory.getMessage("PDF-HUL-84"); - public static final JhoveMessage PDF_HUL_85 = messageFactory.getMessage("PDF-HUL-85"); - public static final JhoveMessage PDF_HUL_86 = messageFactory.getMessage("PDF-HUL-86"); - public static final JhoveMessage PDF_HUL_87 = messageFactory.getMessage("PDF-HUL-87"); - public static final JhoveMessage PDF_HUL_88 = messageFactory.getMessage("PDF-HUL-88"); - public static final JhoveMessage PDF_HUL_89 = messageFactory.getMessage("PDF-HUL-89"); - public static final JhoveMessage PDF_HUL_90 = messageFactory.getMessage("PDF-HUL-90"); - public static final JhoveMessage PDF_HUL_91 = messageFactory.getMessage("PDF-HUL-91"); - public static final JhoveMessage PDF_HUL_92 = messageFactory.getMessage("PDF-HUL-92"); - public static final JhoveMessage PDF_HUL_93 = messageFactory.getMessage("PDF-HUL-93"); - public static final JhoveMessage PDF_HUL_94 = messageFactory.getMessage("PDF-HUL-94"); - public static final JhoveMessage PDF_HUL_95 = messageFactory.getMessage("PDF-HUL-95"); - public static final JhoveMessage PDF_HUL_96 = messageFactory.getMessage("PDF-HUL-96"); - public static final JhoveMessage PDF_HUL_97 = messageFactory.getMessage("PDF-HUL-97"); - public static final JhoveMessage PDF_HUL_98 = messageFactory.getMessage("PDF-HUL-98"); - public static final JhoveMessage PDF_HUL_99 = messageFactory.getMessage("PDF-HUL-99"); - public static final JhoveMessage PDF_HUL_100 = messageFactory.getMessage("PDF-HUL-100"); - public static final JhoveMessage PDF_HUL_101 = messageFactory.getMessage("PDF-HUL-101"); - public static final JhoveMessage PDF_HUL_102 = messageFactory.getMessage("PDF-HUL-102"); - public static final JhoveMessage PDF_HUL_103 = messageFactory.getMessage("PDF-HUL-103"); - public static final JhoveMessage PDF_HUL_104 = messageFactory.getMessage("PDF-HUL-104"); - public static final JhoveMessage PDF_HUL_105 = messageFactory.getMessage("PDF-HUL-105"); - public static final JhoveMessage PDF_HUL_106 = messageFactory.getMessage("PDF-HUL-106"); - public static final JhoveMessage PDF_HUL_107 = messageFactory.getMessage("PDF-HUL-107"); - public static final JhoveMessage PDF_HUL_108 = messageFactory.getMessage("PDF-HUL-108"); - public static final JhoveMessage PDF_HUL_109 = messageFactory.getMessage("PDF-HUL-109"); - public static final JhoveMessage PDF_HUL_110 = messageFactory.getMessage("PDF-HUL-110"); - public static final JhoveMessage PDF_HUL_111 = messageFactory.getMessage("PDF-HUL-111"); - public static final JhoveMessage PDF_HUL_112 = messageFactory.getMessage("PDF-HUL-112"); - public static final JhoveMessage PDF_HUL_113 = messageFactory.getMessage("PDF-HUL-113"); - public static final JhoveMessage PDF_HUL_114 = messageFactory.getMessage("PDF-HUL-114"); - public static final JhoveMessage PDF_HUL_115 = messageFactory.getMessage("PDF-HUL-115"); - public static final JhoveMessage PDF_HUL_116 = messageFactory.getMessage("PDF-HUL-116"); - public static final JhoveMessage PDF_HUL_117 = messageFactory.getMessage("PDF-HUL-117"); - public static final JhoveMessage PDF_HUL_118 = messageFactory.getMessage("PDF-HUL-118"); - public static final JhoveMessage PDF_HUL_119 = messageFactory.getMessage("PDF-HUL-119"); - public static final JhoveMessage PDF_HUL_120 = messageFactory.getMessage("PDF-HUL-120"); - public static final JhoveMessage PDF_HUL_121 = messageFactory.getMessage("PDF-HUL-121"); - public static final JhoveMessage PDF_HUL_122 = messageFactory.getMessage("PDF-HUL-122"); - public static final JhoveMessage PDF_HUL_123 = messageFactory.getMessage("PDF-HUL-123"); - public static final JhoveMessage PDF_HUL_124 = messageFactory.getMessage("PDF-HUL-124"); - public static final JhoveMessage PDF_HUL_125 = messageFactory.getMessage("PDF-HUL-125"); - public static final JhoveMessage PDF_HUL_126 = messageFactory.getMessage("PDF-HUL-126"); - public static final JhoveMessage PDF_HUL_127 = messageFactory.getMessage("PDF-HUL-127"); - public static final JhoveMessage PDF_HUL_128 = messageFactory.getMessage("PDF-HUL-128"); - public static final JhoveMessage PDF_HUL_129 = messageFactory.getMessage("PDF-HUL-129"); - public static final JhoveMessage PDF_HUL_130 = messageFactory.getMessage("PDF-HUL-130"); - public static final JhoveMessage PDF_HUL_131 = messageFactory.getMessage("PDF-HUL-131"); - public static final JhoveMessage PDF_HUL_132 = messageFactory.getMessage("PDF-HUL-132"); - public static final JhoveMessage PDF_HUL_133 = messageFactory.getMessage("PDF-HUL-133"); - public static final JhoveMessage PDF_HUL_134 = messageFactory.getMessage("PDF-HUL-134"); - public static final JhoveMessage PDF_HUL_135 = messageFactory.getMessage("PDF-HUL-135"); - public static final JhoveMessage PDF_HUL_136 = messageFactory.getMessage("PDF-HUL-136"); - public static final JhoveMessage PDF_HUL_136_SUB = messageFactory.getMessage("PDF-HUL-136-SUB"); - public static final JhoveMessage PDF_HUL_137 = messageFactory.getMessage("PDF-HUL-137"); - public static final JhoveMessage PDF_HUL_138 = messageFactory.getMessage("PDF-HUL-138"); - public static final JhoveMessage PDF_HUL_139 = messageFactory.getMessage("PDF-HUL-139"); - public static final JhoveMessage PDF_HUL_140 = messageFactory.getMessage("PDF-HUL-140"); - public static final JhoveMessage PDF_HUL_141 = messageFactory.getMessage("PDF-HUL-141"); - public static final JhoveMessage PDF_HUL_142 = messageFactory.getMessage("PDF-HUL-142"); - public static final JhoveMessage PDF_HUL_143 = messageFactory.getMessage("PDF-HUL-143"); - public static final JhoveMessage PDF_HUL_144 = messageFactory.getMessage("PDF-HUL-144"); - public static final JhoveMessage PDF_HUL_145 = messageFactory.getMessage("PDF-HUL-145"); - public static final JhoveMessage PDF_HUL_146 = messageFactory.getMessage("PDF-HUL-146"); - public static final JhoveMessage PDF_HUL_147 = messageFactory.getMessage("PDF-HUL-147"); - public static final JhoveMessage PDF_HUL_148 = messageFactory.getMessage("PDF-HUL-148"); - public static final JhoveMessage PDF_HUL_149 = messageFactory.getMessage("PDF-HUL-149"); - public static final JhoveMessage PDF_HUL_150 = messageFactory.getMessage("PDF-HUL-150"); - public static final JhoveMessage PDF_HUL_151 = messageFactory.getMessage("PDF-HUL-151"); - public static final JhoveMessage PDF_HUL_152 = messageFactory.getMessage("PDF-HUL-152"); - public static final JhoveMessage PDF_HUL_153 = messageFactory.getMessage("PDF-HUL-153"); + /** Logger Messages */ + public static final String LOG_HINT_ARRY_CHK = "Checking hint array"; - /** - * Logger Messages - */ - public static final String LOG_HINT_ARRY_CHK = "Checking hint array"; - public static final String LOG_IMAGE_XOBJ = "Image XObject"; - public static final String LOG_IMAGE_GET = "Getting image"; - public static final String LOG_K_ELEM_IS_ARRY = "Type K element is an array"; - public static final String LOG_K_ELEM_IS_DICT = "Type K element is dictionary"; - public static final String LOG_LIN_PROF_CHK = "Checking Linearized Profile"; - public static final String LOG_NAMES_DICT_EXCEP = "Exception on names dictionary: "; - public static final String LOG_NO_CHILD_STRUCT_ELEM = "No children are structure elements"; - public static final String LOG_NO_CHILD_OBJS = "No child objects, exiting"; - public static final String LOG_REVISION_NUM_RETRIEVAL_EXCEP = "Exception getting revision number: "; - public static final String LOG_SUBTREE_BUILDING = "Building subtree"; - public static final String LOG_XREF_TABLE_VERIFYING = "Verifying cross-reference table"; -// /** -// * Prefixes -// */ -// private static final String preIoExcep = "An IOException was thrown"; -// -// /** -// * Information messages -// */ -// public static final String INF_FONT_REPORT_LIMIT = "Too many fonts to report; some fonts omitted."; // PDF-HUL-136 -// public static final String INF_FONT_REPORT_LIMIT_SUB = "Total fonts = "; // PDF-HUL-150 -// public static final String INF_FONTS_SKIPPED = "Fonts exist, but are not displayed; to display " + // PDF-HUL-105 -// "remove param value of f from the config file"; -// public static final String INF_OUTLINES_SKIPPED = "Outlines exist, but are not displayed; to display " + // PDF-HUL-132 -// "remove param value of o from the config file"; -// public static final String INF_ANNOTATIONS_SKIPPED = "Annotations exist, but are not displayed; to display " + // PDF-HUL-115 -// "remove param value of a from the config file"; -// public static final String INF_PAGES_SKIPPED = "Page information is not displayed; to display " + // PDF-HUL-112 -// "remove param value of p from the config file"; -// public static final String INF_HEADER_CAT_VER_MISMATCH_1 = "File header gives version as "; // PDF-HUL-87 -// public static final String INF_HEADER_CAT_VER_MISMATCH_2 = ", but catalog dictionary gives version as "; // PDF-HUL-87 -// public static final String INF_OUTLINES_RECURSIVE = "Outlines contain recursive references."; // PDF-HUL-123, PDF-HUL-128, PDF-HUL-129 -// -// /** -// * Error messages -// */ -// public static final String ERR_ANNOT_DICT_TYPES_MISSING = "Annotation dictionary missing required type (S) entry"; // PDF-HUL-120 -// public static final String ERR_ANNOT_INVALID = "Invalid Annotations"; // PDF-HUL-21, PDF-HUL-22 -// public static final String ERR_ANNOT_LIST_INVALID = "Invalid Annotation list"; // PDF-HUL-116 -// public static final String ERR_ANNOT_OBJ_NOT_DICT = "Annotation object is not a dictionary"; // PDF-HUL-114 -// public static final String ERR_ANNOT_PROP_INVALID = "Invalid Annotation property"; // PDF-HUL-121 -// public static final String ERR_ARRAY_CONTAINS_UNEXPECTED_TOKEN = "Unexpected token in array"; // PDF-HUL-40 -// public static final String ERR_ARRAY_IMPROPERLY_NESTED = "Improperly nested array delimiters"; // PDF-HUL-34 -// public static final String ERR_COMPRESSION_INVALID_OR_UNKNOWN = "Compression method is invalid or unknown to JHOVE"; // PDF-HUL-109 -// public static final String ERR_DATE_MALFORMED = "Improperly formed date"; // PDF-HUL-133 -// public static final String ERR_DEST_OBJ_INVALID = "Invalid destination object"; // PDF-HUL-1, PDF-HUL-2 -// public static final String ERR_DEST_IOEXCEP_READING = preIoExcep + " reading destination array id: %d"; // PDF-HUL-3 -// public static final String ERR_DESTS_DICT_INVALID = "Invalid destinations dictionary"; // PDF-HUL-91, PDF-HUL-92 -// public static final String ERR_DICT_CONTAINS_UNEXPECTED_TOKEN = "Unexpected token in dictionary"; // PDF-HUL-43 -// public static final String ERR_DICT_DELIMITERS_IMPROPERLY_NESTED = "Improperly nested dictionary delimiters"; // PDF-HUL-33 -// public static final String ERR_DICT_MALFORMED = "Malformed dictionary"; // PDF-HUL-42 -// public static final String ERR_DOC_CAT_DICT_MISSING = "No document catalog dictionary";// PDF-HUL-85, PDF-HUL-86 -// public static final String ERR_DOC_CAT_OBJ_NUM_INCNSTNT = "Document catalog dictionary object number and trailer root ref number are inconsistent."; // PDF-HUL-140 -// public static final String ERR_DOC_CAT_TYPE_INVALID = "Document catalog Type key must have value Catalog"; // PDF-HUL-141 -// public static final String ERR_DOC_CAT_NO_TYPE = "Document catalog has no Type key or it has a null value."; // PDF-HUL-142 -// public static final String ERR_DOC_CAT_NOT_SIMPLE = "Document catalog Type key does not have a simple String value."; // PDF-HUL-143 -// public static final String ERR_DOC_CAT_VERSION_INVALID = "Invalid Version in document catalog"; // PDF-HUL-88 -// public static final String ERR_DOC_NODE_DICT_MISSING = "Missing dictionary in document node"; // PDF-HUL-4 -// public static final String ERR_DOC_STRUCT_ATT_INVALID = "Invalid attribute in document structure"; // PDF-HUL-56 -// public static final String ERR_DOC_STRUCT_ROOT_CONTAINS_INVALID_DATA = "Invalid data in document structure root"; // PDF-HUL-61, PDF-HUL-62 -// public static final String ERR_DOC_STRUCT_ROOT_INVALID = "Invalid document structure root"; // PDF-HUL-59, PDF-HUL-60 -// public static final String ERR_DOC_STRUCT_TREE_DATA_INVALID = "Invalid data in document structure tree"; // PDF-HUL-58 -// public static final String ERR_ENCRYPT_DICT_ALG_INVALID = "Invalid algorithm value in encryption dictionary"; // PDF-HUL-93 -// public static final String ERR_EOF_UNEXPECTED = "Unexpected EOF"; // PDF-HUL-64 -// public static final String ERR_FILE_SPEC_INVALID = "Invalid file specification"; // PDF-HUL-9 -// public static final String ERR_FILE_TRAILER_MISSING = "No file trailer"; // PDF-HUL-71 -// public static final String ERR_FILTER_MALFORMED = "Malformed filter"; // PDF-HUL-45 -// public static final String ERR_FIND_FONTS_ERR = "Unexpected error in findFonts"; // PDF-HUL-106 -// public static final String ERR_FONT_PROP_PARSING = "Unexpected error in parsing font property"; // PDF-HUL-135 -// public static final String ERR_HEX_STRING_CHAR_INVALID = "Invalid character in hex string"; // PDF-HUL-11, PDF-HUL-65 -// public static final String ERR_INDIRECT_OBJ_REF_MALFORMED = "Malformed indirect object reference"; // PDF-HUL-44 -// public static final String ERR_INLINE_STRUCT_ELE_CONTAINS_BLOCK_ATTS = "Block-level attributes in inline structure element"; // PDF-HUL-55 -// public static final String ERR_LITERAL_UNTERMINATED = "Unterminated literal in PDF file"; // PDF-HUL-10 -// public static final String ERR_NAME_TREE_INVALID = "Invalid name tree"; // PDF-HUL-12, PDF-HUL-13, PDF-HUL-14, PDF-HUL-15, PDF-HUL-16 -// public static final String ERR_NAMES_DICT_INVALID = "Invalid Names dictionary"; // PDF-HUL-89, PDF-HUL-90 -// public static final String ERR_OBJ_DEF_INVALID = "Invalid object definition"; // PDF-HUL-35, PDF-HUL-36, PDF-HUL-37, PDF-HUL-38 -// public static final String ERR_OBJ_NOT_PARSABLE = "Cannot parse object"; // PDF-HUL-39 -// public static final String ERR_OBJ_STREAM_IMPROPER_NESTING = "Improper nesting of object streams"; // PDF-HUL-107 -// public static final String ERR_OBJ_STREAM_OFFSET_OUT_OF_BOUNDS = "Offset out of bounds in object stream"; // PDF-HUL-17 -// public static final String ERR_OBJ_STREAM_OR_NUMBER_INVALID = "Invalid object number or object stream"; // PDF-HUL-108, PDF-HUL-110 -// public static final String ERR_OUTLINE_DICT_ITEM_INVALID = "Invalid outline dictionary item"; // PDF-HUL-125, PDF-HUL-126, PDF-HUL-127, PDF-HUL-130, PDF-HUL-131 -// public static final String ERR_OUTLINE_DICT_MALFORMED = "Malformed outline dictionary"; // PDF-HUL-124 -// public static final String ERR_PAGE_DICT_DATA_INVALID = "Invalid dictionary data for page"; // PDF-HUL-26, PDF-HUL-27, PDF-HUL-28 -// public static final String ERR_PAGE_DICT_INVALID = "Invalid page dictionary"; // PDF-HUL-117 -// public static final String ERR_PAGE_DICT_NO_TYPE = "Pages dictionary has no Type key or it has a null value."; // PDF-HUL-144 -// public static final String ERR_PAGE_DICT_NOT_SIMPLE = "Pages dictionary Type key does not have a simple String value."; // PDF-HUL-145 -// public static final String ERR_PAGE_DICT_OBJ_INVALID = "Invalid page dictionary object"; // PDF-HUL-97 -// public static final String ERR_PAGE_DICT_TYPE_INVALID = "Pages dictionary Type key must have value /Pages."; // PDF-HUL-146 -// public static final String ERR_PAGE_FONT_DICT_MISSING = "Expected dictionary for font entry in page resource"; // PDF-HUL-104 -// public static final String ERR_PAGE_LABEL_INFO_INVALID = "Invalid page label info"; // PDF-HUL-113 -// public static final String ERR_PAGE_LABEL_NODE_INVALID = "Invalid page label node"; // PDF-HUL-20 -// public static final String ERR_PAGE_LABEL_SEQ_INVALID = "Invalid page label sequence"; // PDF-HUL-118 -// public static final String ERR_PAGE_LABEL_STRUCT_PROBLEM = "Problem with page label structure"; // PDF-HUL-119 -// public static final String ERR_PAGE_LABELS_BAD = "Bad page labels"; // PDF-HUL-111 -// public static final String ERR_PAGE_NUMBER_DICT_ELEMENT_MISSING = "Missing expected element in page number dictionary"; // PDF-HUL-18 -// public static final String ERR_PAGE_NUMBER_TREE_DATE_INVALID = "Invalid date in page number tree"; // PDF-HUL-19 -// public static final String ERR_PAGE_TREE_ARTBOX_MALFORMED = "Malformed ArtBox in page tree"; // PDF-HUL-23 -// public static final String ERR_PAGE_TREE_BLEEDBOX_MALFORMED = "Malformed BleedBox in page tree"; // PDF-HUL-25 -// public static final String ERR_PAGE_TREE_DEPTH_EXCEEDED = "Excessive depth or infinite recursion in page tree structure"; // PDF-HUL-32 -// public static final String ERR_PAGE_TREE_IMPROPERLY_CONSTRUCTED = "Improperly constructed page tree"; // PDF-HUL-30, PDF-HUL-31 -// public static final String ERR_PAGE_TREE_MEDIA_BOX_MALFORMED = "Malformed MediaBox in page tree"; // PDF-HUL-7, PDF-HUL-8 -// public static final String ERR_PAGE_TREE_MISSING = "Document page tree not found"; // PDF-HUL-95,PDF-HUL-96 -// public static final String ERR_PAGE_TREE_NODE_INVALID = "Invalid page tree node"; // PDF-HUL-29 -// public static final String ERR_PAGE_TREE_NODE_NOT_FOUND = "Page tree node not found."; // PDF-HUL-147 -// public static final String ERR_PAGE_TREE_TRIMBOX_MALFORMED = "Malformed TrimBox in page tree"; // PDF-HUL-24 -// public static final String ERR_PDF_HEADER_MISSING = "No PDF header"; // PDF-HUL-137 -// public static final String ERR_PDF_MINOR_INVALID = "PDF minor version number is greater than 7."; // PDF-HUL-148 -// public static final String ERR_PDF_TRAILER_MISSING = "No PDF trailer"; // PDF-HUL-138 -// public static final String ERR_PREV_OFFSET_TRAILER_DICT_INVALID = "Invalid Prev offset in trailer dictionary"; // PDF-HUL-72 -// public static final String ERR_RESOURCES_ENTRY_INVALID = "Invalid Resources Entry in document"; // PDF-HUL-5 -// public static final String ERR_RESOURCES_FONT_ENTRY_INVALID = "Invalid Font entry in Resources"; // PDF-HUL-6 -// public static final String ERR_ROLE_MAP_INVALID = "Invalid RoleMap"; // PDF-HUL-63 -// public static final String ERR_SIZE_ENTRY_TRAILER_DICT_INVALID = "Invalid Size entry in trailer dictionary"; // PDF-HUL-73 -// public static final String ERR_SIZE_ENTRY_TRAILER_DICT_MISSING = "Size entry missing in trailer dictionary"; // PDF-HUL-74 -// public static final String ERR_STARTXREF_MISSING = "Missing startxref keyword or value"; // PDF-HUL-139 -// public static final String ERR_STREAM_ASCII_INTEGER_INVALID = "Malformed ASCII number in stream"; // PDF-HUL-46 -// public static final String ERR_STREAM_EMBEDDED_IN_OBJ_STREAM = "Streams may not be embedded in object streams"; // PDF-HUL-47, PDF-HUL-48 -// public static final String ERR_STRUCT_ATT_INVALID = "Invalid structure attribute"; // PDF-HUL-51, PDF-HUL-52, PDF-HUL-53 -// public static final String ERR_STRUCT_ATT_REF_INVALID = "Invalid structure attribute reference"; // PDF-HUL-50 -// public static final String ERR_STRUCT_ATT_TYPE_ILLEGAL = "Structure attribute has illegal type"; // PDF-HUL-54 -// public static final String ERR_STRUCT_TREE_ELEMENT_UNKNOWN = "Unknown element in structure tree"; // PDF-HUL-49 -// public static final String DEP_STRUCT_TYPE_NAME_NON_STANDARD = "Non-standard structure type name"; // PDF-HUL-57 -// public static final String ERR_TOKEN_LEXICAL = "Lexical error"; // PDF-HUL-65, PDF-HUL-66 -// public static final String ERR_TRAILER_DICT_INFO_KEY_NOT_DIRECT = "Trailer dictionary Info key is not an indirect reference"; // PDF-HUL-76 -// public static final String ERR_TRAILER_DICT_ROOT_MISSING = "Root entry missing in trailer dictionary"; // PDF-HUL-75 -// public static final String ERR_TRAILER_ID_INVALID = "Invalid ID in trailer"; // PDF-HUL-77, PDF-HUL-78, PDF-HUL-79 -// public static final String ERR_UNEXPECTED_EXCEPTION = "Unexpected exception "; // PDF-HUL-94, PDF-HUL-99, PDF-HUL-102, PDF-HUL-103 -// public static final String ERR_VECTOR_OBJ_COUNT_NOT_EVEN = MessageConstants.ERR_DICT_MALFORMED // PDF-HUL-41 -// + ": Vector must contain an even number of objects, but has "; -// public static final String ERR_XMP_INVALID = "Invalid or ill-formed XMP metadata"; // PDF-HUL-100, PDF-HUL-101 -// public static final String ERR_XREF_STRM_DICT_ROOT_MISSING = "Root entry missing in cross-ref stream dictionary"; // PDF-HUL-70 -// public static final String ERR_XREF_STRM_OBJ_NUM_INVALID = "Invalid object number in cross-reference stream"; // PDF-HUL-80 -// public static final String ERR_XREF_STRM_MALFORMED = "Malformed cross-reference stream"; // PDF-HUL-81 -// public static final String ERR_XREF_STRM_TYPE_INVALID = "Cross-reference stream must be a stream"; -// public static final String ERR_XREF_TABLE_INVALID = "Invalid cross-reference table"; // PDF-HUL-68, PDF-HUL-69 -// public static final String ERR_XREF_TABLE_MALFORMED = "Malformed cross-reference table"; // PDF-HUL-82, PDF-HUL-83 -// public static final String ERR_XREF_TABLE_OPERATOR_ILLEGAL = "Illegal operator in cross-reference table"; // PDF-HUL-84 -// public static final String ERR_XREF_TABLES_BROKEN = "Cross-reference tables are broken"; // PDF-HUL-134 -// public static final String ERR_INDIRECT_DEST_INVALID_1 = "Invalid indirect destination - referenced object '"; // PDF-HUL-149 -// public static final String ERR_INDIRECT_DEST_INVALID_2 = "' cannot be found"; // PDF-HUL-149 + public static final String LOG_IMAGE_XOBJ = "Image XObject"; + public static final String LOG_IMAGE_GET = "Getting image"; + public static final String LOG_K_ELEM_IS_ARRY = "Type K element is an array"; + public static final String LOG_K_ELEM_IS_DICT = "Type K element is dictionary"; + public static final String LOG_LIN_PROF_CHK = "Checking Linearized Profile"; + public static final String LOG_NAMES_DICT_EXCEP = "Exception on names dictionary: "; + public static final String LOG_NO_CHILD_STRUCT_ELEM = "No children are structure elements"; + public static final String LOG_NO_CHILD_OBJS = "No child objects, exiting"; + public static final String LOG_REVISION_NUM_RETRIEVAL_EXCEP = + "Exception getting revision number: "; + public static final String LOG_SUBTREE_BUILDING = "Building subtree"; + public static final String LOG_XREF_TABLE_VERIFYING = "Verifying cross-reference table"; + // /** + // * Prefixes + // */ + // private static final String preIoExcep = "An IOException was thrown"; + // + // /** + // * Information messages + // */ + // public static final String INF_FONT_REPORT_LIMIT = "Too many fonts to report; some fonts + // omitted."; // PDF-HUL-136 + // public static final String INF_FONT_REPORT_LIMIT_SUB = "Total fonts = "; // PDF-HUL-150 + // public static final String INF_FONTS_SKIPPED = "Fonts exist, but are not displayed; to display + // " + // PDF-HUL-105 + // "remove param value of f from the config file"; + // public static final String INF_OUTLINES_SKIPPED = "Outlines exist, but are not displayed; to + // display " + // PDF-HUL-132 + // "remove param value of o from the config file"; + // public static final String INF_ANNOTATIONS_SKIPPED = "Annotations exist, but are not displayed; + // to display " + // PDF-HUL-115 + // "remove param value of a from the config file"; + // public static final String INF_PAGES_SKIPPED = "Page information is not displayed; to display " + // + // PDF-HUL-112 + // "remove param value of p from the config file"; + // public static final String INF_HEADER_CAT_VER_MISMATCH_1 = "File header gives version as "; // + // PDF-HUL-87 + // public static final String INF_HEADER_CAT_VER_MISMATCH_2 = ", but catalog dictionary gives + // version as "; // PDF-HUL-87 + // public static final String INF_OUTLINES_RECURSIVE = "Outlines contain recursive references."; + // // PDF-HUL-123, PDF-HUL-128, PDF-HUL-129 + // + // /** + // * Error messages + // */ + // public static final String ERR_ANNOT_DICT_TYPES_MISSING = "Annotation dictionary missing + // required type (S) entry"; // PDF-HUL-120 + // public static final String ERR_ANNOT_INVALID = "Invalid Annotations"; // PDF-HUL-21, PDF-HUL-22 + // public static final String ERR_ANNOT_LIST_INVALID = "Invalid Annotation list"; // PDF-HUL-116 + // public static final String ERR_ANNOT_OBJ_NOT_DICT = "Annotation object is not a dictionary"; // + // PDF-HUL-114 + // public static final String ERR_ANNOT_PROP_INVALID = "Invalid Annotation property"; // + // PDF-HUL-121 + // public static final String ERR_ARRAY_CONTAINS_UNEXPECTED_TOKEN = "Unexpected token in array"; + // // PDF-HUL-40 + // public static final String ERR_ARRAY_IMPROPERLY_NESTED = "Improperly nested array delimiters"; + // // PDF-HUL-34 + // public static final String ERR_COMPRESSION_INVALID_OR_UNKNOWN = "Compression method is invalid + // or unknown to JHOVE"; // PDF-HUL-109 + // public static final String ERR_DATE_MALFORMED = "Improperly formed date"; // PDF-HUL-133 + // public static final String ERR_DEST_OBJ_INVALID = "Invalid destination object"; // PDF-HUL-1, + // PDF-HUL-2 + // public static final String ERR_DEST_IOEXCEP_READING = preIoExcep + " reading destination array + // id: %d"; // PDF-HUL-3 + // public static final String ERR_DESTS_DICT_INVALID = "Invalid destinations dictionary"; // + // PDF-HUL-91, PDF-HUL-92 + // public static final String ERR_DICT_CONTAINS_UNEXPECTED_TOKEN = "Unexpected token in + // dictionary"; // PDF-HUL-43 + // public static final String ERR_DICT_DELIMITERS_IMPROPERLY_NESTED = "Improperly nested + // dictionary delimiters"; // PDF-HUL-33 + // public static final String ERR_DICT_MALFORMED = "Malformed dictionary"; // PDF-HUL-42 + // public static final String ERR_DOC_CAT_DICT_MISSING = "No document catalog dictionary";// + // PDF-HUL-85, PDF-HUL-86 + // public static final String ERR_DOC_CAT_OBJ_NUM_INCNSTNT = "Document catalog dictionary object + // number and trailer root ref number are inconsistent."; // PDF-HUL-140 + // public static final String ERR_DOC_CAT_TYPE_INVALID = "Document catalog Type key must have + // value Catalog"; // PDF-HUL-141 + // public static final String ERR_DOC_CAT_NO_TYPE = "Document catalog has no Type key or it has a + // null value."; // PDF-HUL-142 + // public static final String ERR_DOC_CAT_NOT_SIMPLE = "Document catalog Type key does not have a + // simple String value."; // PDF-HUL-143 + // public static final String ERR_DOC_CAT_VERSION_INVALID = "Invalid Version in document catalog"; + // // PDF-HUL-88 + // public static final String ERR_DOC_NODE_DICT_MISSING = "Missing dictionary in document node"; + // // PDF-HUL-4 + // public static final String ERR_DOC_STRUCT_ATT_INVALID = "Invalid attribute in document + // structure"; // PDF-HUL-56 + // public static final String ERR_DOC_STRUCT_ROOT_CONTAINS_INVALID_DATA = "Invalid data in + // document structure root"; // PDF-HUL-61, PDF-HUL-62 + // public static final String ERR_DOC_STRUCT_ROOT_INVALID = "Invalid document structure root"; // + // PDF-HUL-59, PDF-HUL-60 + // public static final String ERR_DOC_STRUCT_TREE_DATA_INVALID = "Invalid data in document + // structure tree"; // PDF-HUL-58 + // public static final String ERR_ENCRYPT_DICT_ALG_INVALID = "Invalid algorithm value in + // encryption dictionary"; // PDF-HUL-93 + // public static final String ERR_EOF_UNEXPECTED = "Unexpected EOF"; // PDF-HUL-64 + // public static final String ERR_FILE_SPEC_INVALID = "Invalid file specification"; // PDF-HUL-9 + // public static final String ERR_FILE_TRAILER_MISSING = "No file trailer"; // PDF-HUL-71 + // public static final String ERR_FILTER_MALFORMED = "Malformed filter"; // PDF-HUL-45 + // public static final String ERR_FIND_FONTS_ERR = "Unexpected error in findFonts"; // PDF-HUL-106 + // public static final String ERR_FONT_PROP_PARSING = "Unexpected error in parsing font property"; + // // PDF-HUL-135 + // public static final String ERR_HEX_STRING_CHAR_INVALID = "Invalid character in hex string"; // + // PDF-HUL-11, PDF-HUL-65 + // public static final String ERR_INDIRECT_OBJ_REF_MALFORMED = "Malformed indirect object + // reference"; // PDF-HUL-44 + // public static final String ERR_INLINE_STRUCT_ELE_CONTAINS_BLOCK_ATTS = "Block-level attributes + // in inline structure element"; // PDF-HUL-55 + // public static final String ERR_LITERAL_UNTERMINATED = "Unterminated literal in PDF file"; // + // PDF-HUL-10 + // public static final String ERR_NAME_TREE_INVALID = "Invalid name tree"; // PDF-HUL-12, + // PDF-HUL-13, PDF-HUL-14, PDF-HUL-15, PDF-HUL-16 + // public static final String ERR_NAMES_DICT_INVALID = "Invalid Names dictionary"; // PDF-HUL-89, + // PDF-HUL-90 + // public static final String ERR_OBJ_DEF_INVALID = "Invalid object definition"; // PDF-HUL-35, + // PDF-HUL-36, PDF-HUL-37, PDF-HUL-38 + // public static final String ERR_OBJ_NOT_PARSABLE = "Cannot parse object"; // PDF-HUL-39 + // public static final String ERR_OBJ_STREAM_IMPROPER_NESTING = "Improper nesting of object + // streams"; // PDF-HUL-107 + // public static final String ERR_OBJ_STREAM_OFFSET_OUT_OF_BOUNDS = "Offset out of bounds in + // object stream"; // PDF-HUL-17 + // public static final String ERR_OBJ_STREAM_OR_NUMBER_INVALID = "Invalid object number or object + // stream"; // PDF-HUL-108, PDF-HUL-110 + // public static final String ERR_OUTLINE_DICT_ITEM_INVALID = "Invalid outline dictionary item"; + // // PDF-HUL-125, PDF-HUL-126, PDF-HUL-127, PDF-HUL-130, PDF-HUL-131 + // public static final String ERR_OUTLINE_DICT_MALFORMED = "Malformed outline dictionary"; // + // PDF-HUL-124 + // public static final String ERR_PAGE_DICT_DATA_INVALID = "Invalid dictionary data for page"; // + // PDF-HUL-26, PDF-HUL-27, PDF-HUL-28 + // public static final String ERR_PAGE_DICT_INVALID = "Invalid page dictionary"; // PDF-HUL-117 + // public static final String ERR_PAGE_DICT_NO_TYPE = "Pages dictionary has no Type key or it has + // a null value."; // PDF-HUL-144 + // public static final String ERR_PAGE_DICT_NOT_SIMPLE = "Pages dictionary Type key does not have + // a simple String value."; // PDF-HUL-145 + // public static final String ERR_PAGE_DICT_OBJ_INVALID = "Invalid page dictionary object"; // + // PDF-HUL-97 + // public static final String ERR_PAGE_DICT_TYPE_INVALID = "Pages dictionary Type key must have + // value /Pages."; // PDF-HUL-146 + // public static final String ERR_PAGE_FONT_DICT_MISSING = "Expected dictionary for font entry in + // page resource"; // PDF-HUL-104 + // public static final String ERR_PAGE_LABEL_INFO_INVALID = "Invalid page label info"; // + // PDF-HUL-113 + // public static final String ERR_PAGE_LABEL_NODE_INVALID = "Invalid page label node"; // + // PDF-HUL-20 + // public static final String ERR_PAGE_LABEL_SEQ_INVALID = "Invalid page label sequence"; // + // PDF-HUL-118 + // public static final String ERR_PAGE_LABEL_STRUCT_PROBLEM = "Problem with page label structure"; + // // PDF-HUL-119 + // public static final String ERR_PAGE_LABELS_BAD = "Bad page labels"; // PDF-HUL-111 + // public static final String ERR_PAGE_NUMBER_DICT_ELEMENT_MISSING = "Missing expected element in + // page number dictionary"; // PDF-HUL-18 + // public static final String ERR_PAGE_NUMBER_TREE_DATE_INVALID = "Invalid date in page number + // tree"; // PDF-HUL-19 + // public static final String ERR_PAGE_TREE_ARTBOX_MALFORMED = "Malformed ArtBox in page tree"; // + // PDF-HUL-23 + // public static final String ERR_PAGE_TREE_BLEEDBOX_MALFORMED = "Malformed BleedBox in page + // tree"; // PDF-HUL-25 + // public static final String ERR_PAGE_TREE_DEPTH_EXCEEDED = "Excessive depth or infinite + // recursion in page tree structure"; // PDF-HUL-32 + // public static final String ERR_PAGE_TREE_IMPROPERLY_CONSTRUCTED = "Improperly constructed page + // tree"; // PDF-HUL-30, PDF-HUL-31 + // public static final String ERR_PAGE_TREE_MEDIA_BOX_MALFORMED = "Malformed MediaBox in page + // tree"; // PDF-HUL-7, PDF-HUL-8 + // public static final String ERR_PAGE_TREE_MISSING = "Document page tree not found"; // + // PDF-HUL-95,PDF-HUL-96 + // public static final String ERR_PAGE_TREE_NODE_INVALID = "Invalid page tree node"; // PDF-HUL-29 + // public static final String ERR_PAGE_TREE_NODE_NOT_FOUND = "Page tree node not found."; // + // PDF-HUL-147 + // public static final String ERR_PAGE_TREE_TRIMBOX_MALFORMED = "Malformed TrimBox in page tree"; + // // PDF-HUL-24 + // public static final String ERR_PDF_HEADER_MISSING = "No PDF header"; // PDF-HUL-137 + // public static final String ERR_PDF_MINOR_INVALID = "PDF minor version number is greater than + // 7."; // PDF-HUL-148 + // public static final String ERR_PDF_TRAILER_MISSING = "No PDF trailer"; // PDF-HUL-138 + // public static final String ERR_PREV_OFFSET_TRAILER_DICT_INVALID = "Invalid Prev offset in + // trailer dictionary"; // PDF-HUL-72 + // public static final String ERR_RESOURCES_ENTRY_INVALID = "Invalid Resources Entry in document"; + // // PDF-HUL-5 + // public static final String ERR_RESOURCES_FONT_ENTRY_INVALID = "Invalid Font entry in + // Resources"; // PDF-HUL-6 + // public static final String ERR_ROLE_MAP_INVALID = "Invalid RoleMap"; // PDF-HUL-63 + // public static final String ERR_SIZE_ENTRY_TRAILER_DICT_INVALID = "Invalid Size entry in trailer + // dictionary"; // PDF-HUL-73 + // public static final String ERR_SIZE_ENTRY_TRAILER_DICT_MISSING = "Size entry missing in trailer + // dictionary"; // PDF-HUL-74 + // public static final String ERR_STARTXREF_MISSING = "Missing startxref keyword or value"; // + // PDF-HUL-139 + // public static final String ERR_STREAM_ASCII_INTEGER_INVALID = "Malformed ASCII number in + // stream"; // PDF-HUL-46 + // public static final String ERR_STREAM_EMBEDDED_IN_OBJ_STREAM = "Streams may not be embedded in + // object streams"; // PDF-HUL-47, PDF-HUL-48 + // public static final String ERR_STRUCT_ATT_INVALID = "Invalid structure attribute"; // + // PDF-HUL-51, PDF-HUL-52, PDF-HUL-53 + // public static final String ERR_STRUCT_ATT_REF_INVALID = "Invalid structure attribute + // reference"; // PDF-HUL-50 + // public static final String ERR_STRUCT_ATT_TYPE_ILLEGAL = "Structure attribute has illegal + // type"; // PDF-HUL-54 + // public static final String ERR_STRUCT_TREE_ELEMENT_UNKNOWN = "Unknown element in structure + // tree"; // PDF-HUL-49 + // public static final String DEP_STRUCT_TYPE_NAME_NON_STANDARD = "Non-standard structure type + // name"; // PDF-HUL-57 + // public static final String ERR_TOKEN_LEXICAL = "Lexical error"; // PDF-HUL-65, PDF-HUL-66 + // public static final String ERR_TRAILER_DICT_INFO_KEY_NOT_DIRECT = "Trailer dictionary Info key + // is not an indirect reference"; // PDF-HUL-76 + // public static final String ERR_TRAILER_DICT_ROOT_MISSING = "Root entry missing in trailer + // dictionary"; // PDF-HUL-75 + // public static final String ERR_TRAILER_ID_INVALID = "Invalid ID in trailer"; // PDF-HUL-77, + // PDF-HUL-78, PDF-HUL-79 + // public static final String ERR_UNEXPECTED_EXCEPTION = "Unexpected exception "; // PDF-HUL-94, + // PDF-HUL-99, PDF-HUL-102, PDF-HUL-103 + // public static final String ERR_VECTOR_OBJ_COUNT_NOT_EVEN = MessageConstants.ERR_DICT_MALFORMED + // // PDF-HUL-41 + // + ": Vector must contain an even number of objects, but has "; + // public static final String ERR_XMP_INVALID = "Invalid or ill-formed XMP metadata"; // + // PDF-HUL-100, PDF-HUL-101 + // public static final String ERR_XREF_STRM_DICT_ROOT_MISSING = "Root entry missing in cross-ref + // stream dictionary"; // PDF-HUL-70 + // public static final String ERR_XREF_STRM_OBJ_NUM_INVALID = "Invalid object number in + // cross-reference stream"; // PDF-HUL-80 + // public static final String ERR_XREF_STRM_MALFORMED = "Malformed cross-reference stream"; // + // PDF-HUL-81 + // public static final String ERR_XREF_STRM_TYPE_INVALID = "Cross-reference stream must be a + // stream"; + // public static final String ERR_XREF_TABLE_INVALID = "Invalid cross-reference table"; // + // PDF-HUL-68, PDF-HUL-69 + // public static final String ERR_XREF_TABLE_MALFORMED = "Malformed cross-reference table"; // + // PDF-HUL-82, PDF-HUL-83 + // public static final String ERR_XREF_TABLE_OPERATOR_ILLEGAL = "Illegal operator in + // cross-reference table"; // PDF-HUL-84 + // public static final String ERR_XREF_TABLES_BROKEN = "Cross-reference tables are broken"; // + // PDF-HUL-134 + // public static final String ERR_INDIRECT_DEST_INVALID_1 = "Invalid indirect destination - + // referenced object '"; // PDF-HUL-149 + // public static final String ERR_INDIRECT_DEST_INVALID_2 = "' cannot be found"; // PDF-HUL-149 } diff --git a/jhove-modules/pdf-hul/src/main/java/edu/harvard/hul/ois/jhove/module/pdf/Name.java b/jhove-modules/pdf-hul/src/main/java/edu/harvard/hul/ois/jhove/module/pdf/Name.java index 774fcc96c..ef915cd4e 100644 --- a/jhove-modules/pdf-hul/src/main/java/edu/harvard/hul/ois/jhove/module/pdf/Name.java +++ b/jhove-modules/pdf-hul/src/main/java/edu/harvard/hul/ois/jhove/module/pdf/Name.java @@ -1,25 +1,20 @@ -/********************************************************************** - * Jhove - JSTOR/Harvard Object Validation Environment - * Copyright 2003 by JSTOR and the President and Fellows of Harvard College - **********************************************************************/ - -package edu.harvard.hul.ois.jhove.module.pdf; - /** - * Class for Tokens which represent PDF names. + * ******************************************************************** Jhove - JSTOR/Harvard Object + * Validation Environment Copyright 2003 by JSTOR and the President and Fellows of Harvard College + * ******************************************************************** */ -public class Name - extends StringValuedToken -{ - /** Creates an instance of a Name */ - public Name () - { - super (); - } - - /** Returns true if it's within the PDF/A implementation limit */ - @Override - public boolean isPdfACompliant () { - return _value.getBytes().length <= 127; - } +package edu.harvard.hul.ois.jhove.module.pdf; + +/** Class for Tokens which represent PDF names. */ +public class Name extends StringValuedToken { + /** Creates an instance of a Name */ + public Name() { + super(); + } + + /** Returns true if it's within the PDF/A implementation limit */ + @Override + public boolean isPdfACompliant() { + return _value.getBytes().length <= 127; + } } diff --git a/jhove-modules/pdf-hul/src/main/java/edu/harvard/hul/ois/jhove/module/pdf/NameTreeNode.java b/jhove-modules/pdf-hul/src/main/java/edu/harvard/hul/ois/jhove/module/pdf/NameTreeNode.java index 5abcc5001..31ea6dd32 100644 --- a/jhove-modules/pdf-hul/src/main/java/edu/harvard/hul/ois/jhove/module/pdf/NameTreeNode.java +++ b/jhove-modules/pdf-hul/src/main/java/edu/harvard/hul/ois/jhove/module/pdf/NameTreeNode.java @@ -1,191 +1,164 @@ -/********************************************************************** - * Jhove - JSTOR/Harvard Object Validation Environment - * Copyright 2003 by JSTOR and the President and Fellows of Harvard College - **********************************************************************/ - +/** + * ******************************************************************** Jhove - JSTOR/Harvard Object + * Validation Environment Copyright 2003 by JSTOR and the President and Fellows of Harvard College + * ******************************************************************** + */ package edu.harvard.hul.ois.jhove.module.pdf; import edu.harvard.hul.ois.jhove.module.PdfModule; - import java.io.IOException; import java.util.*; /** - * Class for nodes of a PDF name tree, e.g., ExternalFiles. Name trees - * are intended for large amounts of data that won't have to all be brought - * into memory at once; so this implementation is geared toward file-based - * lookup of a key rather than creating an in-memory structure. Descendant - * nodes become available for garbage collection if they are not on the - * search path for a match. + * Class for nodes of a PDF name tree, e.g., ExternalFiles. Name trees are intended for large + * amounts of data that won't have to all be brought into memory at once; so this implementation is + * geared toward file-based lookup of a key rather than creating an in-memory structure. Descendant + * nodes become available for garbage collection if they are not on the search path for a match. * - * Keys are collated according to raw bytes, not character encoding. + *

Keys are collated according to raw bytes, not character encoding. */ -public class NameTreeNode -{ - protected PdfModule _module; - protected NameTreeNode _parent; - protected PdfDictionary _dict; // dictionary which defines this node +public class NameTreeNode { + protected PdfModule _module; + protected NameTreeNode _parent; + protected PdfDictionary _dict; // dictionary which defines this node - private Vector _kids = null; - private Vector _names = null; - private Vector _lowerLimit = null; // Lower limit of keys for this node -- null for root - private Vector _upperLimit = null; // Upper limit of keys for this node -- null for root - + private Vector _kids = null; + private Vector _names = null; + private Vector _lowerLimit = null; // Lower limit of keys for this node -- null for root + private Vector _upperLimit = null; // Upper limit of keys for this node -- null for root - /** - * Constructor. - * @param module The PdfModule under which we're operating - * @param parent The parent node in the document tree; - * may be null only for the root node - * @param dict The dictionary object on which this node - * is based - */ - public NameTreeNode (PdfModule module, - NameTreeNode parent, - PdfDictionary dict) throws PdfException - { - _module = module; - _parent = parent; - _dict = dict; - - try { - // Get the limits of the key range. If there are no limits, this - // must be the root node. - PdfArray limitsDict = (PdfArray) module.resolveIndirectObject - (dict.get ("Limits")); - if (limitsDict != null) { - Vector vec = limitsDict.getContent (); - PdfSimpleObject limobj = (PdfSimpleObject) vec.elementAt (0); - _lowerLimit = limobj.getRawBytes (); - limobj = (PdfSimpleObject) vec.elementAt (1); - _upperLimit = limobj.getRawBytes (); - } - // Get the Kids and Names arrays. Normally only one will - // be present. - // carl@openpreservation.org : The PDF 1.6 spec is more specific: - // Root Node: Single entry, either Kids or Names, not both - // Intermediate Node: MUST have Kids and Limits - // Leaf Node: MUST have Names and Limits - PdfArray kidsVec = (PdfArray) module.resolveIndirectObject - (dict.get ("Kids")); - if (kidsVec != null) { - _kids = kidsVec.getContent (); - } - PdfArray namesVec = (PdfArray) module.resolveIndirectObject - (dict.get ("Names")); - if (namesVec != null) { - _names = namesVec.getContent (); - } - } - catch (ClassCastException ce) { - throw new PdfInvalidException (MessageConstants.PDF_HUL_12); // PDF-HUL-12 - } - catch (ArrayIndexOutOfBoundsException | NullPointerException ce) { - throw new PdfInvalidException (MessageConstants.PDF_HUL_13); // PDF-HUL-13 - } - catch (IOException e) { - throw new PdfMalformedException (MessageConstants.PDF_HUL_14); // PDF-HUL-14 - } + /** + * Constructor. + * + * @param module The PdfModule under which we're operating + * @param parent The parent node in the document tree; may be null only for the root node + * @param dict The dictionary object on which this node is based + */ + public NameTreeNode(PdfModule module, NameTreeNode parent, PdfDictionary dict) + throws PdfException { + _module = module; + _parent = parent; + _dict = dict; + + try { + // Get the limits of the key range. If there are no limits, this + // must be the root node. + PdfArray limitsDict = (PdfArray) module.resolveIndirectObject(dict.get("Limits")); + if (limitsDict != null) { + Vector vec = limitsDict.getContent(); + PdfSimpleObject limobj = (PdfSimpleObject) vec.elementAt(0); + _lowerLimit = limobj.getRawBytes(); + limobj = (PdfSimpleObject) vec.elementAt(1); + _upperLimit = limobj.getRawBytes(); + } + // Get the Kids and Names arrays. Normally only one will + // be present. + // carl@openpreservation.org : The PDF 1.6 spec is more specific: + // Root Node: Single entry, either Kids or Names, not both + // Intermediate Node: MUST have Kids and Limits + // Leaf Node: MUST have Names and Limits + PdfArray kidsVec = (PdfArray) module.resolveIndirectObject(dict.get("Kids")); + if (kidsVec != null) { + _kids = kidsVec.getContent(); + } + PdfArray namesVec = (PdfArray) module.resolveIndirectObject(dict.get("Names")); + if (namesVec != null) { + _names = namesVec.getContent(); + } + } catch (ClassCastException ce) { + throw new PdfInvalidException(MessageConstants.PDF_HUL_12); // PDF-HUL-12 + } catch (ArrayIndexOutOfBoundsException | NullPointerException ce) { + throw new PdfInvalidException(MessageConstants.PDF_HUL_13); // PDF-HUL-13 + } catch (IOException e) { + throw new PdfMalformedException(MessageConstants.PDF_HUL_14); // PDF-HUL-14 } + } - /** - * See if a key is within the bounds of this node. All keys - * are within the bounds of the root node. - */ - public boolean inBounds (Vector key) - { - if (_lowerLimit == null) { - return true; // root node - } - if (_upperLimit == null) { - return true; // no upper limit is specified - } - return !(compareKey (key, _lowerLimit) < 0 || - compareKey (key, _upperLimit) > 0); + /** + * See if a key is within the bounds of this node. All keys are within the bounds of the root + * node. + */ + public boolean inBounds(Vector key) { + if (_lowerLimit == null) { + return true; // root node } - - - /** - * Get the PdfObject which matches the key, or null if there is no match. - */ - public PdfObject get (Vector key) throws PdfException - { - try { - if (!inBounds (key)) { - return null; - } - // If this has a Names array, it's a leaf node or standalone root; - // search it for the key. - if (_names != null) { - for (int i = 0; i < _names.size (); i += 2) { - PdfSimpleObject k1 = (PdfSimpleObject) _names.elementAt (i); - int cmp = compareKey (key, k1.getRawBytes ()); - if (cmp == 0) { - /* Match! */ - return _module.resolveIndirectObject - ((PdfObject) _names.elementAt (i + 1)); - } - else if (cmp < 0) { - // Passed position where match should be - return null; - } - } - return null; // just not there - } - else if (_kids != null) { - // It's a non-standalone root or an intermediate note. - // Figure out which descendant we should search. - for (int i = 0; i < _kids.size (); i++) { - PdfDictionary kid = (PdfDictionary) - _module.resolveIndirectObject ( - (PdfObject) _kids.elementAt (i)); - NameTreeNode kidnode = new NameTreeNode (_module, this, kid); - if (kidnode.inBounds (key)) { - PdfObject res = kidnode.get (key); - if (res != null) { - return res; - } - } - } - return null; // Not in any subnode - } - else throw new PdfMalformedException (MessageConstants.PDF_HUL_15); // PDF-HUL-15 - } - catch (IOException | ArrayIndexOutOfBoundsException | NullPointerException | ClassCastException e) { - throw new PdfMalformedException (MessageConstants.PDF_HUL_16); // PDF-HUL-16 - } + if (_upperLimit == null) { + return true; // no upper limit is specified } + return !(compareKey(key, _lowerLimit) < 0 || compareKey(key, _upperLimit) > 0); + } - /* Compare two keys (Vectors of Integer). Returns -1 if the - first argument is less than the second, 1 if the first argument - is greater, and 0 if they are equal. Key A is less than key B - if A is a prefix of B. */ - private static int compareKey (Vector a, Vector b) { - int lena = a.size (); - int lenb = b.size (); - int len = (lena < lenb ? lena : lenb); - for (int i = 0; i < len; i++) { - int ai = ((Integer) a.elementAt (i)).intValue (); - int bi = ((Integer) b.elementAt (i)).intValue (); - if (ai < bi) { - return -1; - } - else if (ai > bi) { - return 1; - } + /** Get the PdfObject which matches the key, or null if there is no match. */ + public PdfObject get(Vector key) throws PdfException { + try { + if (!inBounds(key)) { + return null; + } + // If this has a Names array, it's a leaf node or standalone root; + // search it for the key. + if (_names != null) { + for (int i = 0; i < _names.size(); i += 2) { + PdfSimpleObject k1 = (PdfSimpleObject) _names.elementAt(i); + int cmp = compareKey(key, k1.getRawBytes()); + if (cmp == 0) { + /* Match! */ + return _module.resolveIndirectObject((PdfObject) _names.elementAt(i + 1)); + } else if (cmp < 0) { + // Passed position where match should be + return null; + } } - // Both are equal as far as the length of the shorter one goes. - // To be equal, they must have the same length; otherwise the - // shorter one is the lesser. - if (lena == lenb) { - return 0; - } - else if (lena < lenb) { - return -1; - } - else { - return 1; + return null; // just not there + } else if (_kids != null) { + // It's a non-standalone root or an intermediate note. + // Figure out which descendant we should search. + for (int i = 0; i < _kids.size(); i++) { + PdfDictionary kid = + (PdfDictionary) _module.resolveIndirectObject((PdfObject) _kids.elementAt(i)); + NameTreeNode kidnode = new NameTreeNode(_module, this, kid); + if (kidnode.inBounds(key)) { + PdfObject res = kidnode.get(key); + if (res != null) { + return res; + } + } } + return null; // Not in any subnode + } else throw new PdfMalformedException(MessageConstants.PDF_HUL_15); // PDF-HUL-15 + } catch (IOException + | ArrayIndexOutOfBoundsException + | NullPointerException + | ClassCastException e) { + throw new PdfMalformedException(MessageConstants.PDF_HUL_16); // PDF-HUL-16 } -} + } + /* Compare two keys (Vectors of Integer). Returns -1 if the + first argument is less than the second, 1 if the first argument + is greater, and 0 if they are equal. Key A is less than key B + if A is a prefix of B. */ + private static int compareKey(Vector a, Vector b) { + int lena = a.size(); + int lenb = b.size(); + int len = (lena < lenb ? lena : lenb); + for (int i = 0; i < len; i++) { + int ai = ((Integer) a.elementAt(i)).intValue(); + int bi = ((Integer) b.elementAt(i)).intValue(); + if (ai < bi) { + return -1; + } else if (ai > bi) { + return 1; + } + } + // Both are equal as far as the length of the shorter one goes. + // To be equal, they must have the same length; otherwise the + // shorter one is the lesser. + if (lena == lenb) { + return 0; + } else if (lena < lenb) { + return -1; + } else { + return 1; + } + } +} diff --git a/jhove-modules/pdf-hul/src/main/java/edu/harvard/hul/ois/jhove/module/pdf/Numeric.java b/jhove-modules/pdf-hul/src/main/java/edu/harvard/hul/ois/jhove/module/pdf/Numeric.java index 9d6586768..c737ede03 100644 --- a/jhove-modules/pdf-hul/src/main/java/edu/harvard/hul/ois/jhove/module/pdf/Numeric.java +++ b/jhove-modules/pdf-hul/src/main/java/edu/harvard/hul/ois/jhove/module/pdf/Numeric.java @@ -1,100 +1,79 @@ -/********************************************************************** - * Jhove - JSTOR/Harvard Object Validation Environment - * Copyright 2003 by JSTOR and the President and Fellows of Harvard College - **********************************************************************/ - +/** + * ******************************************************************** Jhove - JSTOR/Harvard Object + * Validation Environment Copyright 2003 by JSTOR and the President and Fellows of Harvard College + * ******************************************************************** + */ package edu.harvard.hul.ois.jhove.module.pdf; /** - * Class for Tokens which represent PDF numbers. Numeric values - * are stored as double if they have a real value, or - * as int if they have an integer value. The implementation notes - * state that the maximum value of an integer on a 32-bit machine - * is 2 ^ 31 - 1. However, they also say that byte offsets have - * a limit of 10 decimal digits, which is a larger value. So we - * store integer values as long. + * Class for Tokens which represent PDF numbers. Numeric values are stored as double if + * they have a real value, or as int if they have an integer value. The implementation notes state + * that the maximum value of an integer on a 32-bit machine is 2 ^ 31 - 1. However, they also say + * that byte offsets have a limit of 10 decimal digits, which is a larger value. So we store integer + * values as long. */ -public class Numeric - extends Token -{ - /** True if real value; false if integer. */ - private boolean _real; - private double _realValue; - private long _intValue; +public class Numeric extends Token { + /** True if real value; false if integer. */ + private boolean _real; - /** Creates an instance of a Numeric */ - public Numeric () - { - super (); - _real = false; - _intValue = 0; - } + private double _realValue; + private long _intValue; - /** Returns the value, converted to an integer */ - public int getIntegerValue () - { - if (_real) { - return (int) _realValue; - } - return (int) _intValue; - } + /** Creates an instance of a Numeric */ + public Numeric() { + super(); + _real = false; + _intValue = 0; + } - /** Returns the value, converted to a long */ - public long getLongValue () - { - if (_real) { - return (long) _realValue; - } - return _intValue; + /** Returns the value, converted to an integer */ + public int getIntegerValue() { + if (_real) { + return (int) _realValue; } + return (int) _intValue; + } - /** Returns the value of this Numeric as a double */ - public double getValue () - { - if (_real) { - return _realValue; - } - return _intValue; + /** Returns the value, converted to a long */ + public long getLongValue() { + if (_real) { + return (long) _realValue; } + return _intValue; + } - /** - * Returns true if the value is stored as a floating-point - * number. - */ - public boolean isReal () - { - return _real; + /** Returns the value of this Numeric as a double */ + public double getValue() { + if (_real) { + return _realValue; } + return _intValue; + } - /** - * Set this object's value to a double. - */ - public void setValue (double value) - { - _realValue = value; - _real = true; - } + /** Returns true if the value is stored as a floating-point number. */ + public boolean isReal() { + return _real; + } + /** Set this object's value to a double. */ + public void setValue(double value) { + _realValue = value; + _real = true; + } - /** - * Set this object's value to a long. - */ - public void setValue (long value) - { - _intValue = value; - _real = false; - } - - /** Returns true if this is within PDF/A implementation limits. */ - @Override - public boolean isPdfACompliant () { - if (_real) { - double absRealValue = (_realValue < 0 ? -_realValue : _realValue); - return (absRealValue <= 3.404E38); - } - return (_intValue <= 2147483647 && _intValue >= -2147483648); - - } + /** Set this object's value to a long. */ + public void setValue(long value) { + _intValue = value; + _real = false; + } + /** Returns true if this is within PDF/A implementation limits. */ + @Override + public boolean isPdfACompliant() { + if (_real) { + double absRealValue = (_realValue < 0 ? -_realValue : _realValue); + return (absRealValue <= 3.404E38); + } + return (_intValue <= 2147483647 && _intValue >= -2147483648); + } } - diff --git a/jhove-modules/pdf-hul/src/main/java/edu/harvard/hul/ois/jhove/module/pdf/ObjectStream.java b/jhove-modules/pdf-hul/src/main/java/edu/harvard/hul/ois/jhove/module/pdf/ObjectStream.java index ec1caa74c..fb82cf574 100644 --- a/jhove-modules/pdf-hul/src/main/java/edu/harvard/hul/ois/jhove/module/pdf/ObjectStream.java +++ b/jhove-modules/pdf-hul/src/main/java/edu/harvard/hul/ois/jhove/module/pdf/ObjectStream.java @@ -1,139 +1,121 @@ -/********************************************************************** - * Jhove - JSTOR/Harvard Object Validation Environment - * Copyright 2005 by JSTOR and the President and Fellows of Harvard College - **********************************************************************/ - +/** + * ******************************************************************** Jhove - JSTOR/Harvard Object + * Validation Environment Copyright 2005 by JSTOR and the President and Fellows of Harvard College + * ******************************************************************** + */ package edu.harvard.hul.ois.jhove.module.pdf; import java.io.*; import java.util.*; /** - * This class implements the Object Stream, a new way of storing - * objects starting in PDF 1.4. - * - * An object stream can contain one or more objects, as described in - * Section 3.4 of the PostScript manual. - * - * JHOVE supports only FlateDecode as a filter for cross-reference - * streams. This is consistent with the implementation limitation - * described in Appendix H of the PDF manual for Acrobat 6 and earlier. - * + * This class implements the Object Stream, a new way of storing objects starting in PDF 1.4. * - * @author Gary McGath + *

An object stream can contain one or more objects, as described in Section 3.4 of the + * PostScript manual. + * + *

JHOVE supports only FlateDecode as a filter for cross-reference streams. This is consistent + * with the implementation limitation described in Appendix H of the PDF manual for Acrobat 6 and + * earlier. * + * @author Gary McGath */ public class ObjectStream { - private PdfStream _ostrm; // The underlying Stream object. - private PdfDictionary _dict; - private int _numObjects; - private int _firstOffset; - private Parser _parser; - private RandomAccessFile _raf; - - /* Index of the object stream. Each element is an int[2], - * consisting of an object number and an offset. - */ - private Map _index; + private PdfStream _ostrm; // The underlying Stream object. + private PdfDictionary _dict; + private int _numObjects; + private int _firstOffset; + private Parser _parser; + private RandomAccessFile _raf; - /** - * Constructor. - */ - public ObjectStream(PdfStream ostrm, RandomAccessFile raf) { - _ostrm = ostrm; - _raf = raf; - _dict = ostrm.getDict (); - _parser = new Parser (new StreamTokenizer (raf, _ostrm.getStream())); - } - - /** Checks the validity of the stream dictionary, and extracts - * information necessary for subsequent reading. - */ - public boolean isValid () - { - try { - /* Type must be ObjStm */ - PdfObject obj = _dict.get ("Type"); - String typeStr = null; - if (obj instanceof PdfSimpleObject) { - typeStr = ((PdfSimpleObject) obj).getStringValue (); - } - if (!("ObjStm".equals (typeStr))) { - return false; - } - /* Number of objects */ - obj = _dict.get ("N"); - if (obj instanceof PdfSimpleObject) { - _numObjects = ((PdfSimpleObject) obj).getIntValue(); - } - else { - return false; - } - /* Offset of first object */ - obj = _dict.get ("First"); - if (obj instanceof PdfSimpleObject) { - _firstOffset = ((PdfSimpleObject) obj).getIntValue(); - } - else { - return false; - } - /* Optional refernce to object stream which this extends. */ - obj = _dict.get ("Extends"); - if (obj != null) { - /* What do we do with this? */ - } - return true; - } - catch (Exception e) { - return false; - } - - } + /* Index of the object stream. Each element is an int[2], + * consisting of an object number and an offset. + */ + private Map _index; + /** Constructor. */ + public ObjectStream(PdfStream ostrm, RandomAccessFile raf) { + _ostrm = ostrm; + _raf = raf; + _dict = ostrm.getDict(); + _parser = new Parser(new StreamTokenizer(raf, _ostrm.getStream())); + } - /** Reads the index of the object stream. - */ - public void readIndex () - throws PdfException, IOException - { - Stream strm = _ostrm.getStream (); - strm.setFilters (_ostrm.getFilters ()); - strm.initRead (_raf); - _index = new HashMap<> (_numObjects); - for (int i = 0; i < _numObjects; i++) { - /* If I'm reading it correctly, the numbers are - * encoded as ASCII strings separated by white space. - * I don't know what the restrictions, if any, are on - * the white space. - */ - Integer onum = new Integer (strm.readAsciiInt ()); - Integer offset = new Integer (strm.readAsciiInt ()); - _index.put (onum, offset); - } + /** + * Checks the validity of the stream dictionary, and extracts information necessary for subsequent + * reading. + */ + public boolean isValid() { + try { + /* Type must be ObjStm */ + PdfObject obj = _dict.get("Type"); + String typeStr = null; + if (obj instanceof PdfSimpleObject) { + typeStr = ((PdfSimpleObject) obj).getStringValue(); + } + if (!("ObjStm".equals(typeStr))) { + return false; + } + /* Number of objects */ + obj = _dict.get("N"); + if (obj instanceof PdfSimpleObject) { + _numObjects = ((PdfSimpleObject) obj).getIntValue(); + } else { + return false; + } + /* Offset of first object */ + obj = _dict.get("First"); + if (obj instanceof PdfSimpleObject) { + _firstOffset = ((PdfSimpleObject) obj).getIntValue(); + } else { + return false; + } + /* Optional refernce to object stream which this extends. */ + obj = _dict.get("Extends"); + if (obj != null) { + /* What do we do with this? */ + } + return true; + } catch (Exception e) { + return false; } - - /** Extracts an object from the stream. */ - public PdfObject getObject (int objnum) - throws PdfException - { - Integer onum = Integer.valueOf(objnum); - Integer off = _index.get (onum); - try { - if (off != null) { - int offset = off.intValue (); - _parser.seek (offset + _firstOffset); - PdfObject object = _parser.readObject (false); - /* Need to ensure the object number is set */ - object.setObjNumber(objnum); - return object; - } - return null; - } - catch (IOException e) { - throw new PdfMalformedException - (MessageConstants.PDF_HUL_16); // PDF-HUL-16 - } + } + + /** Reads the index of the object stream. */ + public void readIndex() throws PdfException, IOException { + Stream strm = _ostrm.getStream(); + strm.setFilters(_ostrm.getFilters()); + strm.initRead(_raf); + _index = new HashMap<>(_numObjects); + for (int i = 0; i < _numObjects; i++) { + /* If I'm reading it correctly, the numbers are + * encoded as ASCII strings separated by white space. + * I don't know what the restrictions, if any, are on + * the white space. + */ + Integer onum = new Integer(strm.readAsciiInt()); + Integer offset = new Integer(strm.readAsciiInt()); + _index.put(onum, offset); } + } + /** Extracts an object from the stream. */ + public PdfObject getObject(int objnum) throws PdfException { + Integer onum = Integer.valueOf(objnum); + Integer off = _index.get(onum); + try { + if (off != null) { + int offset = off.intValue(); + _parser.seek(offset + _firstOffset); + PdfObject object = _parser.readObject(false); + /* Need to ensure the object number is set */ + object.setObjNumber(objnum); + return object; + } + return null; + } catch (IOException e) { + throw new PdfMalformedException(MessageConstants.PDF_HUL_16); // PDF-HUL-16 + } + } } diff --git a/jhove-modules/pdf-hul/src/main/java/edu/harvard/hul/ois/jhove/module/pdf/PageLabelNode.java b/jhove-modules/pdf-hul/src/main/java/edu/harvard/hul/ois/jhove/module/pdf/PageLabelNode.java index 7fc326b9b..a6e444cfc 100644 --- a/jhove-modules/pdf-hul/src/main/java/edu/harvard/hul/ois/jhove/module/pdf/PageLabelNode.java +++ b/jhove-modules/pdf-hul/src/main/java/edu/harvard/hul/ois/jhove/module/pdf/PageLabelNode.java @@ -1,378 +1,344 @@ -/********************************************************************** - * Jhove - JSTOR/Harvard Object Validation Environment - * Copyright 2003 by JSTOR and the President and Fellows of Harvard College - **********************************************************************/ - +/** + * ******************************************************************** Jhove - JSTOR/Harvard Object + * Validation Environment Copyright 2003 by JSTOR and the President and Fellows of Harvard College + * ******************************************************************** + */ package edu.harvard.hul.ois.jhove.module.pdf; import edu.harvard.hul.ois.jhove.module.PdfModule; import java.util.*; -/** - * Class for nodes of a PDF number tree. - */ -public class PageLabelNode -{ - /** The PdfModule this node is associated with. */ - protected PdfModule _module; - - /** The parent node of this node. */ - protected PageLabelNode _parent; +/** Class for nodes of a PDF number tree. */ +public class PageLabelNode { + /** The PdfModule this node is associated with. */ + protected PdfModule _module; - /** The dictionary which defines this node. */ - protected PdfDictionary _dict; + /** The parent node of this node. */ + protected PageLabelNode _parent; - /** Set to true when all subnodes of this node - * have been iterated through following a StartWalk. */ - protected boolean _walkFinished; + /** The dictionary which defines this node. */ + protected PdfDictionary _dict; - private List _descendants; - private Iterator _descendantsIter; - private int _currentKey; // Key most recently obtained in traversing tree - private PdfObject _currentValue; // Value most recently obtained in traversing tree - private int _prevKey; // Key previously obtained in traversing tree - private PdfObject _prevValue; // Value previously obtained in traversing tree - private int _currentNumsIndex; // Current index into Nums entry - private int _currentNumsLength; // Length of current Nums entry - private Vector _currentNumsVec; // Vector from the Nums entry - private PageLabelNode _currentDescendant; - private PageLabelNode _currentLeaf; + /** + * Set to true when all subnodes of this node have been iterated through following a StartWalk. + */ + protected boolean _walkFinished; - /** - * Superclass constructor. - * @param module The PdfModule under which we're operating - * @param parent The parent node in the document tree; - * may be null only for the root node - * @param dict The dictionary object on which this node - * is based - */ - public PageLabelNode (PdfModule module, - PageLabelNode parent, - PdfDictionary dict) - { - _module = module; - _parent = parent; - _dict = dict; - } + private List _descendants; + private Iterator _descendantsIter; + private int _currentKey; // Key most recently obtained in traversing tree + private PdfObject _currentValue; // Value most recently obtained in traversing tree + private int _prevKey; // Key previously obtained in traversing tree + private PdfObject _prevValue; // Value previously obtained in traversing tree + private int _currentNumsIndex; // Current index into Nums entry + private int _currentNumsLength; // Length of current Nums entry + private Vector _currentNumsVec; // Vector from the Nums entry + private PageLabelNode _currentDescendant; + private PageLabelNode _currentLeaf; + /** + * Superclass constructor. + * + * @param module The PdfModule under which we're operating + * @param parent The parent node in the document tree; may be null only for the root node + * @param dict The dictionary object on which this node is based + */ + public PageLabelNode(PdfModule module, PageLabelNode parent, PdfDictionary dict) { + _module = module; + _parent = parent; + _dict = dict; + } - /** - * Build the subtree of descendants of this node, using - * the Kids entry in the dictionary.Leaf nodes are - * recognized by not having a Kids entry. - * @throws edu.harvard.hul.ois.jhove.module.pdf.PdfException - */ - public void buildSubtree () throws PdfException - { - PdfArray kids; - try { - kids = (PdfArray) _dict.get("Kids"); - if (kids != null) { - Vector kidsVec = kids.getContent (); - _descendants = new ArrayList<> (kidsVec.size ()); - for (int i = 0; i < kidsVec.size (); i++) { - PdfDictionary kid = (PdfDictionary) - _module.resolveIndirectObject - (kidsVec.elementAt (i)); - PageLabelNode nodeObj = - new PageLabelNode (_module, this, kid); - nodeObj.buildSubtree (); - _descendants.add(nodeObj); - } - } - else _descendants = null; - } - catch (PdfException pe) { - throw pe; - } - catch (Exception e) { - throw new PdfInvalidException (MessageConstants.PDF_HUL_20); // PDF-HUL-20 + /** + * Build the subtree of descendants of this node, using the Kids entry in the dictionary.Leaf + * nodes are recognized by not having a Kids entry. + * + * @throws edu.harvard.hul.ois.jhove.module.pdf.PdfException + */ + public void buildSubtree() throws PdfException { + PdfArray kids; + try { + kids = (PdfArray) _dict.get("Kids"); + if (kids != null) { + Vector kidsVec = kids.getContent(); + _descendants = new ArrayList<>(kidsVec.size()); + for (int i = 0; i < kidsVec.size(); i++) { + PdfDictionary kid = (PdfDictionary) _module.resolveIndirectObject(kidsVec.elementAt(i)); + PageLabelNode nodeObj = new PageLabelNode(_module, this, kid); + nodeObj.buildSubtree(); + _descendants.add(nodeObj); } + } else _descendants = null; + } catch (PdfException pe) { + throw pe; + } catch (Exception e) { + throw new PdfInvalidException(MessageConstants.PDF_HUL_20); // PDF-HUL-20 + } + } + /** Initialize an iterator through the descendants of this node. */ + public void startWalk() { + if (_descendants != null) { + _descendantsIter = _descendants.listIterator(); + _walkFinished = false; + } else { + _descendantsIter = null; // leaf node, or root in isolation + _walkFinished = true; } + _currentDescendant = null; + _currentLeaf = null; + _currentKey = -1; + _currentValue = null; + _prevKey = -1; + _prevValue = null; + } - /** - * Initialize an iterator through the descendants of this node. - */ - public void startWalk () - { - if (_descendants != null) { - _descendantsIter = _descendants.listIterator (); - _walkFinished = false; - } - else { - _descendantsIter = null; // leaf node, or root in isolation - _walkFinished = true; - } - _currentDescendant = null; - _currentLeaf = null; - _currentKey = -1; - _currentValue = null; - _prevKey = -1; - _prevValue = null; + /** + * Get the next leaf object which is under this node. This function is designed such that calling + * startWalk() and then repeatedly calling nextLeafObject() will return all the leaf objects in + * the tree under this node, and finally will return null when there are no more. A leaf object is + * one which has no Kids; it is required to have a Nums entry. + * + * @return nextLeafObject: the leaf object under this node + */ + public PageLabelNode nextLeafObject() { + if (_walkFinished) { + return null; + } + // _currentDescendant == null and _walkFinished == false indicates + // we're at the start. + if (_currentDescendant == null) { + if (_descendantsIter == null) { + // No descendants. This is a root node which functions as its + // only leaf. + _walkFinished = true; + return this; + } + // Get first descendant + _currentDescendant = _descendantsIter.next(); + _currentDescendant.startWalk(); } - /** - * Get the next leaf object which is under this node. This function - * is designed such that calling startWalk() and then repeatedly - * calling nextLeafObject() will return all the leaf objects in the tree - * under this node, and finally will return null when there are no more. - * A leaf object is one which has no Kids; it is required to have a - * Nums entry. - * @return nextLeafObject: the leaf object under this node - */ - public PageLabelNode nextLeafObject () - { - if (_walkFinished) { - return null; + PageLabelNode retval = _currentDescendant.nextLeafObject(); + if (retval == null) { + if (_descendantsIter.hasNext()) { + _currentDescendant = _descendantsIter.next(); + _currentDescendant.startWalk(); + return _currentDescendant.nextLeafObject(); + } + // We've gone through all our descendants. + _walkFinished = true; + return null; + } + return retval; + } + + /** + * Obtain the next key-value pair from the tree.This returns true if a pair is available, false if + * not. After this is called, getCurrentKey and getCurrentValue may be called to retrieve the key + * and value thus found. Each time this is called, currentKey and currentValue get copied into + * prevKey and prevValue. + * + * @return boolean: true if a next key-value pair is available, false if no + * @throws edu.harvard.hul.ois.jhove.module.pdf.PdfException + */ + public boolean findNextKeyValue() throws PdfException { + try { + if (_currentLeaf == null || _currentNumsIndex >= _currentNumsLength) { + _currentLeaf = nextLeafObject(); + if (_currentLeaf == null) { + _prevKey = _currentKey; + _prevValue = _currentValue; + _currentKey = Integer.MAX_VALUE; + return false; // all done } - // _currentDescendant == null and _walkFinished == false indicates - // we're at the start. - if (_currentDescendant == null) { - if (_descendantsIter == null) { - // No descendants. This is a root node which functions as its - // only leaf. - _walkFinished = true; - return this; - } - // Get first descendant - _currentDescendant = _descendantsIter.next (); - _currentDescendant.startWalk (); + _currentNumsIndex = 0; + PdfArray pairArray = + (PdfArray) _module.resolveIndirectObject(_currentLeaf._dict.get("Nums")); + if (pairArray == null) { + throw new PdfInvalidException(MessageConstants.PDF_HUL_18); // PDF-HUL-18 } + _currentNumsVec = pairArray.getContent(); + _currentNumsLength = _currentNumsVec.size(); + } - PageLabelNode retval = _currentDescendant.nextLeafObject (); - if (retval == null) { - if (_descendantsIter.hasNext ()) { - _currentDescendant = _descendantsIter.next (); - _currentDescendant.startWalk (); - return _currentDescendant.nextLeafObject (); - } - // We've gone through all our descendants. - _walkFinished = true; - return null; - } - return retval; + // The key and the value are in two successive positions in the + // array, which is of the form [key value key value ... ] + PdfSimpleObject keyObj = (PdfSimpleObject) _currentNumsVec.elementAt(_currentNumsIndex); + // Save the previous key-value pair + _prevKey = _currentKey; + _prevValue = _currentValue; + _currentKey = keyObj.getIntValue(); + + _currentValue = _currentNumsVec.elementAt(_currentNumsIndex + 1); + _currentNumsIndex += 2; + + return true; + } catch (PdfInvalidException e) { + throw e; + } catch (Exception e) { + throw new PdfInvalidException(MessageConstants.PDF_HUL_19); // PDF-HUL-19 } + } - /** - * Obtain the next key-value pair from the tree.This returns true - * if a pair is available, false if not. After this is called, - * getCurrentKey and getCurrentValue may be called to retrieve the - * key and value thus found. Each time this is called, - * currentKey and currentValue get copied into prevKey and - * prevValue. - * - * @return boolean: true if a next key-value pair is available, - * false if no - * @throws edu.harvard.hul.ois.jhove.module.pdf.PdfException - */ - public boolean findNextKeyValue () throws PdfException - { - try { - if (_currentLeaf == null || _currentNumsIndex >= _currentNumsLength) { - _currentLeaf = nextLeafObject (); - if (_currentLeaf == null) { - _prevKey = _currentKey; - _prevValue = _currentValue; - _currentKey = Integer.MAX_VALUE; - return false; // all done - } - _currentNumsIndex = 0; - PdfArray pairArray = (PdfArray) - _module.resolveIndirectObject (_currentLeaf._dict.get ("Nums")); - if (pairArray == null) { - throw new PdfInvalidException(MessageConstants.PDF_HUL_18); // PDF-HUL-18 - } - _currentNumsVec = pairArray.getContent (); - _currentNumsLength = _currentNumsVec.size (); - } + /** + * Returns key at current position in traversing tree + * + * @return int + */ + public int getCurrentKey() { + return _currentKey; + } - // The key and the value are in two successive positions in the - // array, which is of the form [key value key value ... ] - PdfSimpleObject keyObj = (PdfSimpleObject) - _currentNumsVec.elementAt (_currentNumsIndex); - // Save the previous key-value pair - _prevKey = _currentKey; - _prevValue = _currentValue; - _currentKey = keyObj.getIntValue (); + /** + * Returns value associated with current key + * + * @return PdfObject + */ + public PdfObject _getCurrentValue() { + return _currentValue; + } - _currentValue = _currentNumsVec.elementAt (_currentNumsIndex + 1); - _currentNumsIndex += 2; + /** + * Returns key previously obtained in traversing tree + * + * @return int + */ + public int getPrevKey() { + return _prevKey; + } - return true; - } - catch (PdfInvalidException e) { - throw e; - } - catch (Exception e) { - throw new PdfInvalidException (MessageConstants.PDF_HUL_19); // PDF-HUL-19 - } - } + /** + * Returns value associated with key previously obtained in traversing tree + * + * @return PdfObject + */ + public PdfObject getPrevValue() { + return _prevValue; + } - /** - * Returns key at current position in traversing tree - * @return int - */ - public int getCurrentKey () - { - return _currentKey; + /** + * A convenience method to turn integers into Roman numerals, for the generation of page labels. + * + * @param n: the integer + * @param upperCase: true if upperCase Roman numerals are wanted + * @return + */ + public static String intToRoman(int n, boolean upperCase) { + StringBuffer buf = new StringBuffer(); + // Numbers of a thousand or more start with an "M" for + // each full thousand. + while (n >= 1000) { + buf.append("M"); + n -= 1000; } - - /** - * Returns value associated with current key - * @return PdfObject - */ - public PdfObject _getCurrentValue () - { - return _currentValue; + // treat "CM" as a special case. + if (n >= 900) { + buf.append("CM"); + n -= 900; } - - /** - * Returns key previously obtained in traversing tree - * @return int - */ - public int getPrevKey () - { - return _prevKey; + // 500 through 899 uses D, DC, DCC, DCCC + if (n >= 500) { + buf.append("D"); + while (n >= 600) { + buf.append("C"); + n -= 100; + } + n -= 500; } - - /** - * Returns value associated with key previously obtained - * in traversing tree - * @return PdfObject - */ - public PdfObject getPrevValue () - { - return _prevValue; + // 400 through 499 is CD + if (n >= 400) { + buf.append("CD"); + n -= 400; } - - /** - * A convenience method to turn integers into Roman - * numerals, for the generation of page labels. - * @param n: the integer - * @param upperCase: true if upperCase Roman numerals are wanted - * @return - */ - public static String intToRoman (int n, boolean upperCase) - { - StringBuffer buf = new StringBuffer (); - // Numbers of a thousand or more start with an "M" for - // each full thousand. - while (n >= 1000) { - buf.append ("M"); - n -= 1000; - } - // treat "CM" as a special case. - if (n >= 900) { - buf.append ("CM"); - n -= 900; - } - // 500 through 899 uses D, DC, DCC, DCCC - if (n >= 500) { - buf.append ("D"); - while (n >= 600) { - buf.append ("C"); - n -= 100; - } - n -= 500; - } - // 400 through 499 is CD - if (n >= 400) { - buf.append ("CD"); - n -= 400; - } - // 100 through 399 is C, CC, CCC - while (n >= 100) { - buf.append ("C"); - n -= 100; - } - // 90 through 99 is XC - if (n >= 90) { - buf.append ("XC"); - n -= 90; - } - // 50 through 89 is L, LX, LXX, LXXX - if (n >= 50) { - buf.append ("L"); - while (n >= 60) { - buf.append ("X"); - n -= 10; - } - n -= 50; - } - // 40 through 49 is XL - if (n >= 40) { - buf.append ("XL"); - n -= 40; - } - // 10 through 39 is X, XX, XXX - while (n >= 10) { - buf.append ("X"); - n -= 10; - } - // From here on, nitpick it out with a switch statement. - switch (n) { - case 1: - buf.append ("I"); - break; - case 2: - buf.append ("II"); - break; - case 3: - buf.append ("III"); - break; - case 4: - buf.append ("IV"); - break; - case 5: - buf.append ("V"); - break; - case 6: - buf.append ("VI"); - break; - case 7: - buf.append ("VII"); - break; - case 8: - buf.append ("VIII"); - break; - case 9: - buf.append ("IX"); - break; - default : - break; - } - String val = buf.toString (); - if (upperCase) { - return val; - } - return val.toLowerCase (); + // 100 through 399 is C, CC, CCC + while (n >= 100) { + buf.append("C"); + n -= 100; + } + // 90 through 99 is XC + if (n >= 90) { + buf.append("XC"); + n -= 90; + } + // 50 through 89 is L, LX, LXX, LXXX + if (n >= 50) { + buf.append("L"); + while (n >= 60) { + buf.append("X"); + n -= 10; + } + n -= 50; + } + // 40 through 49 is XL + if (n >= 40) { + buf.append("XL"); + n -= 40; + } + // 10 through 39 is X, XX, XXX + while (n >= 10) { + buf.append("X"); + n -= 10; + } + // From here on, nitpick it out with a switch statement. + switch (n) { + case 1: + buf.append("I"); + break; + case 2: + buf.append("II"); + break; + case 3: + buf.append("III"); + break; + case 4: + buf.append("IV"); + break; + case 5: + buf.append("V"); + break; + case 6: + buf.append("VI"); + break; + case 7: + buf.append("VII"); + break; + case 8: + buf.append("VIII"); + break; + case 9: + buf.append("IX"); + break; + default: + break; + } + String val = buf.toString(); + if (upperCase) { + return val; + } + return val.toLowerCase(); + } + /** + * A convenience method to turn integers into "letter" page numbers as defined for PDF. The first + * 26 pages are A-Z, the next 26 AA-ZZ, etc. + * + * @param n: integers to be turned into letters + * @param upperCase: true if uppercase letters are wanted + * @return String + */ + public static String intToBase26(int n, boolean upperCase) { + int repeatCount = ((n - 1) / 26) + 1; + StringBuffer buf = new StringBuffer(); + int ch; + // Have ch be the appropriate character to repeat + if (upperCase) { + ch = 65 + ((n - 1) % 26); + } else { + ch = 97 + ((n - 1) % 26); } - /** - * A convenience method to turn integers into - * "letter" page numbers as defined for PDF. - * The first 26 pages are A-Z, the next 26 AA-ZZ, - * etc. - * @param n: integers to be turned into letters - * @param upperCase: true if uppercase letters are wanted - * - * @return String - */ - public static String intToBase26 (int n, boolean upperCase) - { - int repeatCount = ((n - 1) / 26) + 1; - StringBuffer buf = new StringBuffer (); - int ch; - // Have ch be the appropriate character to repeat - if (upperCase) { - ch = 65 + ((n - 1) % 26); - } - else { - ch = 97 + ((n - 1) % 26); - } - while (--repeatCount >= 0) { - buf.append ((char) ch); - } - return buf.toString (); + while (--repeatCount >= 0) { + buf.append((char) ch); } + return buf.toString(); + } } diff --git a/jhove-modules/pdf-hul/src/main/java/edu/harvard/hul/ois/jhove/module/pdf/PageObject.java b/jhove-modules/pdf-hul/src/main/java/edu/harvard/hul/ois/jhove/module/pdf/PageObject.java index 68ea261ab..4afc0b345 100644 --- a/jhove-modules/pdf-hul/src/main/java/edu/harvard/hul/ois/jhove/module/pdf/PageObject.java +++ b/jhove-modules/pdf-hul/src/main/java/edu/harvard/hul/ois/jhove/module/pdf/PageObject.java @@ -1,209 +1,177 @@ -/********************************************************************** - * Jhove - JSTOR/Harvard Object Validation Environment - * Copyright 2003 by JSTOR and the President and Fellows of Harvard College - **********************************************************************/ - +/** + * ******************************************************************** Jhove - JSTOR/Harvard Object + * Validation Environment Copyright 2003 by JSTOR and the President and Fellows of Harvard College + * ******************************************************************** + */ package edu.harvard.hul.ois.jhove.module.pdf; +import edu.harvard.hul.ois.jhove.messages.JhoveMessage; +import edu.harvard.hul.ois.jhove.module.PdfModule; import java.io.IOException; import java.util.ArrayList; import java.util.List; import java.util.Vector; -import edu.harvard.hul.ois.jhove.messages.JhoveMessage; -import edu.harvard.hul.ois.jhove.module.PdfModule; +/** Class encapsulating a PDF page object node. */ +public class PageObject extends DocNode { + private List _contentStreams = null; // contents of the page; may be null -/** - * Class encapsulating a PDF page object node. - */ -public class PageObject extends DocNode -{ - private List _contentStreams = null; // contents of the page; may be null + /** + * Superclass constructor. + * + * @param module The module under which we're operating + * @param parent The parent node in the document tree; may be null only for the root node + * @param dict The dictionary object on which this node is based + */ + public PageObject(PdfModule module, PageTreeNode parent, PdfDictionary dict) + throws PdfMalformedException { + super(module, parent, dict); + _pageObjectFlag = true; + } - /** - * Superclass constructor. - * @param module The module under which we're operating - * @param parent The parent node in the document tree; - * may be null only for the root node - * @param dict The dictionary object on which this node - * is based - */ - public PageObject (PdfModule module, - PageTreeNode parent, - PdfDictionary dict) throws PdfMalformedException - { - super (module, parent, dict); - _pageObjectFlag = true; + /** + * Find the content stream(s) for this page. This is called when the page tree content stream is + * built by PageTreeNode. getContentStreams may subsequently be called to get the + * content. + */ + public void loadContent(PdfModule module) throws PdfException { + PdfObject contents = _dict.get("Contents"); + // Contents object can be null, the page is empty. + if (contents == null) { + return; } - - /** - * Find the content stream(s) for this page. This is - * called when the page tree content stream is built - * by PageTreeNode. getContentStreams may - * subsequently be called to get the content. - */ - public void loadContent (PdfModule module) throws PdfException - { - PdfObject contents = _dict.get("Contents"); - // Contents object can be null, the page is empty. - if (contents == null) { - return; - } - try { - contents = module.resolveIndirectObject (contents); - processContents(module, contents); - } - catch (IOException e) { - throw new PdfMalformedException (MessageConstants.PDF_HUL_26, 0); // PDF-HUL-26 - } + try { + contents = module.resolveIndirectObject(contents); + processContents(module, contents); + } catch (IOException e) { + throw new PdfMalformedException(MessageConstants.PDF_HUL_26, 0); // PDF-HUL-26 } + } - /** - * Returns the List of content streams. The list elements are - * of type PdfStream. - */ - public List getContentStreams () - { - return _contentStreams; - } - - /** - * Return the page's Annots array of dictionaries, or null if none - */ - public PdfArray getAnnotations () throws PdfException - { - try { - return (PdfArray) _module.resolveIndirectObject (_dict.get ("Annots")); - } - catch (ClassCastException e) { - throw new PdfInvalidException (MessageConstants.PDF_HUL_21); // PDF-HUL-21 - } - catch (IOException e) { - throw new PdfMalformedException (MessageConstants.PDF_HUL_22); // PDF-HUL-22 - } + /** Returns the List of content streams. The list elements are of type PdfStream. */ + public List getContentStreams() { + return _contentStreams; + } + + /** Return the page's Annots array of dictionaries, or null if none */ + public PdfArray getAnnotations() throws PdfException { + try { + return (PdfArray) _module.resolveIndirectObject(_dict.get("Annots")); + } catch (ClassCastException e) { + throw new PdfInvalidException(MessageConstants.PDF_HUL_21); // PDF-HUL-21 + } catch (IOException e) { + throw new PdfMalformedException(MessageConstants.PDF_HUL_22); // PDF-HUL-22 } + } + /** + * Call this function when recursively walking through a document tree. This allows nextPageObject + * () to be return this object exactly once. + */ + @Override + public void startWalk() { + _walkFinished = false; + } - /** - * Call this function when recursively walking through a document - * tree. This allows nextPageObject () to be return this object - * exactly once. - */ - @Override - public void startWalk () - { - _walkFinished = false; - } - - /** - * Returns this object the first time it is called after startWalk - * is called, then null when called again. This allows a recursive - * walk through a document tree to work properly. - */ - @Override - public PageObject nextPageObject () - { - if (_walkFinished) - return null; - _walkFinished = true; - return this; - } + /** + * Returns this object the first time it is called after startWalk is called, then null when + * called again. This allows a recursive walk through a document tree to work properly. + */ + @Override + public PageObject nextPageObject() { + if (_walkFinished) return null; + _walkFinished = true; + return this; + } - /** - * Called to walk through all page tree nodes and page objects. - * Functionally identical with nextPageObject. - */ - @Override - public DocNode nextDocNode () - { - return nextPageObject (); - } - - /** - * Returns the ArtBox for the page, or null if none. Throws a - * PDFException if there is an ArtBox but it is not a rectangle. - */ - public PdfArray getArtBox () throws PdfException - { - return retrieveAndCheckRectangle(this._dict, "ArtBox", - MessageConstants.PDF_HUL_23); // PDF-HUL-23 - } + /** + * Called to walk through all page tree nodes and page objects. Functionally identical with + * nextPageObject. + */ + @Override + public DocNode nextDocNode() { + return nextPageObject(); + } - /** - * Returns the TrimBox for the page, or null if none. Throws a - * PDFException if there is an TrimBox but it is not a rectangle. - */ - public PdfArray getTrimBox () throws PdfException - { - return retrieveAndCheckRectangle(this._dict, "TrimBox", - MessageConstants.PDF_HUL_24); // PDF-HUL-24 - } + /** + * Returns the ArtBox for the page, or null if none. Throws a PDFException if there is an ArtBox + * but it is not a rectangle. + */ + public PdfArray getArtBox() throws PdfException { + return retrieveAndCheckRectangle( + this._dict, "ArtBox", MessageConstants.PDF_HUL_23); // PDF-HUL-23 + } - /** - * Returns the BleedBox for the page, or null if none. Throws a - * PDFException if there is an BleedBox but it is not a rectangle. - */ - public PdfArray getBleedBox () throws PdfException - { - return retrieveAndCheckRectangle(this._dict, "BleedBox", - MessageConstants.PDF_HUL_25); // PDF-HUL-25 - } + /** + * Returns the TrimBox for the page, or null if none. Throws a PDFException if there is an TrimBox + * but it is not a rectangle. + */ + public PdfArray getTrimBox() throws PdfException { + return retrieveAndCheckRectangle( + this._dict, "TrimBox", MessageConstants.PDF_HUL_24); // PDF-HUL-24 + } + + /** + * Returns the BleedBox for the page, or null if none. Throws a PDFException if there is an + * BleedBox but it is not a rectangle. + */ + public PdfArray getBleedBox() throws PdfException { + return retrieveAndCheckRectangle( + this._dict, "BleedBox", MessageConstants.PDF_HUL_25); // PDF-HUL-25 + } - private static PdfArray retrieveAndCheckRectangle(final PdfDictionary dict, - final String dictKey, final JhoveMessage invalidMessage) throws PdfInvalidException { - PdfArray mbox = null; - try { - // Retrieve the object from the passed dictionary - mbox = (PdfArray) dict.get (dictKey); - } catch (ClassCastException e) { - throw new PdfInvalidException(invalidMessage); - } - if (mbox == null) { - // If it's null it doesn't exist so return null - return null; - } - else if (mbox.toRectangle () != null) { - // If the returned array is a rectangle the return it - return mbox; - } - else { - // The retrieved object isn't a rectangle throw the exception - throw new PdfInvalidException (invalidMessage); - } + private static PdfArray retrieveAndCheckRectangle( + final PdfDictionary dict, final String dictKey, final JhoveMessage invalidMessage) + throws PdfInvalidException { + PdfArray mbox = null; + try { + // Retrieve the object from the passed dictionary + mbox = (PdfArray) dict.get(dictKey); + } catch (ClassCastException e) { + throw new PdfInvalidException(invalidMessage); } + if (mbox == null) { + // If it's null it doesn't exist so return null + return null; + } else if (mbox.toRectangle() != null) { + // If the returned array is a rectangle the return it + return mbox; + } else { + // The retrieved object isn't a rectangle throw the exception + throw new PdfInvalidException(invalidMessage); + } + } - private void processContents(PdfModule module, final PdfObject contents) throws PdfException, IOException { - // The Contents entry in the dictionary may be either - // a stream or an array of streams. - if (contents instanceof PdfStream) { - _contentStreams = new ArrayList<>(1); - _contentStreams.add((PdfStream) contents); - return; - } - else if (contents instanceof PdfArray) { - loadContentFromArray(module, (PdfArray) contents); - } - else { - throw new PdfInvalidException (MessageConstants.PDF_HUL_27, 0); // PDF-HUL-27 - } + private void processContents(PdfModule module, final PdfObject contents) + throws PdfException, IOException { + // The Contents entry in the dictionary may be either + // a stream or an array of streams. + if (contents instanceof PdfStream) { + _contentStreams = new ArrayList<>(1); + _contentStreams.add((PdfStream) contents); + return; + } else if (contents instanceof PdfArray) { + loadContentFromArray(module, (PdfArray) contents); + } else { + throw new PdfInvalidException(MessageConstants.PDF_HUL_27, 0); // PDF-HUL-27 } + } - private void loadContentFromArray(PdfModule module, final PdfArray contents) throws PdfException, IOException { - Vector contentVec = - contents.getContent (); - if (contentVec.size () == 0) { - return; - } - _contentStreams = new ArrayList<>(contentVec.size ()); - for (int i = 0; i < contentVec.size (); i++) { - PdfObject streamElement = contentVec.elementAt (i); - streamElement = module.resolveIndirectObject(streamElement); - if (streamElement instanceof PdfStream) { - _contentStreams.add ((PdfStream) streamElement); - } - else { - throw new PdfInvalidException (MessageConstants.PDF_HUL_28, 0); // PDF-HUL-28 - } - } + private void loadContentFromArray(PdfModule module, final PdfArray contents) + throws PdfException, IOException { + Vector contentVec = contents.getContent(); + if (contentVec.size() == 0) { + return; + } + _contentStreams = new ArrayList<>(contentVec.size()); + for (int i = 0; i < contentVec.size(); i++) { + PdfObject streamElement = contentVec.elementAt(i); + streamElement = module.resolveIndirectObject(streamElement); + if (streamElement instanceof PdfStream) { + _contentStreams.add((PdfStream) streamElement); + } else { + throw new PdfInvalidException(MessageConstants.PDF_HUL_28, 0); // PDF-HUL-28 + } } + } } diff --git a/jhove-modules/pdf-hul/src/main/java/edu/harvard/hul/ois/jhove/module/pdf/PageTreeNode.java b/jhove-modules/pdf-hul/src/main/java/edu/harvard/hul/ois/jhove/module/pdf/PageTreeNode.java index e251d7aae..6e5080d0d 100644 --- a/jhove-modules/pdf-hul/src/main/java/edu/harvard/hul/ois/jhove/module/pdf/PageTreeNode.java +++ b/jhove-modules/pdf-hul/src/main/java/edu/harvard/hul/ois/jhove/module/pdf/PageTreeNode.java @@ -1,255 +1,218 @@ -/********************************************************************** - * Jhove - JSTOR/Harvard Object Validation Environment - * Copyright 2003 by JSTOR and the President and Fellows of Harvard College - **********************************************************************/ - +/** + * ******************************************************************** Jhove - JSTOR/Harvard Object + * Validation Environment Copyright 2003 by JSTOR and the President and Fellows of Harvard College + * ******************************************************************** + */ package edu.harvard.hul.ois.jhove.module.pdf; import edu.harvard.hul.ois.jhove.module.*; import java.util.*; /** - * Class encapsulating a PDF page tree node. - * The page tree is built such that callers can walk through - * it by calling startWalk and then calling nextDocNode - * (for all nodes) or nextPageObject (for pages only) repeatedly. + * Class encapsulating a PDF page tree node. The page tree is built such that callers can walk + * through it by calling startWalk and then calling nextDocNode (for all nodes) or nextPageObject + * (for pages only) repeatedly. */ -public class PageTreeNode extends DocNode -{ - /* The descendant DocNodes. */ - private List _descendants; - private ListIterator _descendantsIter; - private DocNode _currentDescendant; - private boolean _walkFirst; - private Set _visitedNodes; +public class PageTreeNode extends DocNode { + /* The descendant DocNodes. */ + private List _descendants; + private ListIterator _descendantsIter; + private DocNode _currentDescendant; + private boolean _walkFirst; + private Set _visitedNodes; - /** - * Superclass constructor. - * @param module The PDFModule under which we're operating - * @param parent The parent node in the document tree; - * may be null only for the root node - * @param dict The dictionary object on which this node - * is based - */ - public PageTreeNode (PdfModule module, - PageTreeNode parent, - PdfDictionary dict) throws PdfMalformedException - { - super (module, parent, dict); - _pageObjectFlag = false; - _descendants = new ArrayList<> (1); // Empty list in case it doesn't get built - } + /** + * Superclass constructor. + * + * @param module The PDFModule under which we're operating + * @param parent The parent node in the document tree; may be null only for the root node + * @param dict The dictionary object on which this node is based + */ + public PageTreeNode(PdfModule module, PageTreeNode parent, PdfDictionary dict) + throws PdfMalformedException { + super(module, parent, dict); + _pageObjectFlag = false; + _descendants = new ArrayList<>(1); // Empty list in case it doesn't get built + } - /** - * Builds the subtree of descendants of this node, using - * the Kids entry in the dictionary. - */ - public void buildSubtree (boolean toplevel, int recGuard) throws PdfException - { - /* Guard against infinite recursion */ - if (recGuard <= 0) { - throw new PdfMalformedException (MessageConstants.PDF_HUL_32); // PDF-HUL-32 - } - PdfArray kids = null; - try { - /* Section 3.6.2 of the PDF 1.6 doc says: - * "Applications should be prepared - * to handle any form of tree structure built of such nodes - * [page tree nodes and page nodes]. The simplest structure - * would consist of a single page tree node that references - * all of the document's page objects directly." - * But actually, the simplest structure would be a single - * page node. And it appears that Acrobat 7 will indeed - * generate such. - */ - /* Note that the Kids dictionary can be an indirect object. */ - PdfObject obj = _dict.get("Kids"); - if (obj instanceof PdfIndirectObj) { - kids = (PdfArray) (((PdfIndirectObj) obj).getObject ()); - } - else { - kids = (PdfArray) obj; - } - if (toplevel && kids == null) { - // The single page node case, maybe. - PdfSimpleObject type = (PdfSimpleObject) _dict.get ("Type"); - if (type != null && - "Page".equals (type.getStringValue())) { - PageObject pageObj = new PageObject - (_module, this, _dict); - _descendants = new ArrayList<> (1); - _descendants.add (pageObj); - } - } - else { - Vector kidsVec = kids.getContent (); - _descendants = new ArrayList<> (kidsVec.size ()); - for (int i = 0; i < kidsVec.size (); i++) { - PdfIndirectObj kidRef = - (PdfIndirectObj) kidsVec.elementAt (i); - /************************************************** - * To avoid a simple case of infinite recursion, check - * that this kid is not the same page object as its - * parent. - **************************************************/ - /************************************************** - int kidObjNumber = kidRef.getObjNumber (); - int kidGenNumber = kidRef.getGenNumber (); - if (objNumber >= 0 && genNumber >= 0 && - objNumber == kidObjNumber && - genNumber == kidGenNumber) { - break; - } - **************************************************/ - PdfDictionary kid = (PdfDictionary) - _module.resolveIndirectObject (kidRef); - PdfSimpleObject kidtype = - (PdfSimpleObject) kid.get("Type"); - String kidtypeStr = kidtype.getStringValue (); - if ("Page".equals (kidtypeStr)) { - PageObject pageObj = new PageObject - (_module, this, kid); - pageObj.loadContent (_module); - _descendants.add(pageObj); - } - else if ("Pages".equals (kidtypeStr)) { - PageTreeNode nodeObj = - new PageTreeNode (_module, this, kid); - nodeObj.buildSubtree (false, recGuard - 1); - _descendants.add(nodeObj); - } - } - } - } - catch (PdfException ee) { - throw ee; - } - catch (ArrayIndexOutOfBoundsException excep) { - throw new PdfInvalidException(MessageConstants.PDF_HUL_147); // PDF-HUL-147 + /** Builds the subtree of descendants of this node, using the Kids entry in the dictionary. */ + public void buildSubtree(boolean toplevel, int recGuard) throws PdfException { + /* Guard against infinite recursion */ + if (recGuard <= 0) { + throw new PdfMalformedException(MessageConstants.PDF_HUL_32); // PDF-HUL-32 + } + PdfArray kids = null; + try { + /* Section 3.6.2 of the PDF 1.6 doc says: + * "Applications should be prepared + * to handle any form of tree structure built of such nodes + * [page tree nodes and page nodes]. The simplest structure + * would consist of a single page tree node that references + * all of the document's page objects directly." + * But actually, the simplest structure would be a single + * page node. And it appears that Acrobat 7 will indeed + * generate such. + */ + /* Note that the Kids dictionary can be an indirect object. */ + PdfObject obj = _dict.get("Kids"); + if (obj instanceof PdfIndirectObj) { + kids = (PdfArray) (((PdfIndirectObj) obj).getObject()); + } else { + kids = (PdfArray) obj; + } + if (toplevel && kids == null) { + // The single page node case, maybe. + PdfSimpleObject type = (PdfSimpleObject) _dict.get("Type"); + if (type != null && "Page".equals(type.getStringValue())) { + PageObject pageObj = new PageObject(_module, this, _dict); + _descendants = new ArrayList<>(1); + _descendants.add(pageObj); } - catch (Exception e) { - throw new PdfInvalidException(MessageConstants.PDF_HUL_29); // PDF-HUL-29 + } else { + Vector kidsVec = kids.getContent(); + _descendants = new ArrayList<>(kidsVec.size()); + for (int i = 0; i < kidsVec.size(); i++) { + PdfIndirectObj kidRef = (PdfIndirectObj) kidsVec.elementAt(i); + /** + * ************************************************ To avoid a simple case of infinite + * recursion, check that this kid is not the same page object as its parent. + * ************************************************ + */ + /** + * ************************************************ int kidObjNumber = kidRef.getObjNumber + * (); int kidGenNumber = kidRef.getGenNumber (); if (objNumber >= 0 && genNumber >= 0 && + * objNumber == kidObjNumber && genNumber == kidGenNumber) { break; } + * ************************************************ + */ + PdfDictionary kid = (PdfDictionary) _module.resolveIndirectObject(kidRef); + PdfSimpleObject kidtype = (PdfSimpleObject) kid.get("Type"); + String kidtypeStr = kidtype.getStringValue(); + if ("Page".equals(kidtypeStr)) { + PageObject pageObj = new PageObject(_module, this, kid); + pageObj.loadContent(_module); + _descendants.add(pageObj); + } else if ("Pages".equals(kidtypeStr)) { + PageTreeNode nodeObj = new PageTreeNode(_module, this, kid); + nodeObj.buildSubtree(false, recGuard - 1); + _descendants.add(nodeObj); + } } - + } + } catch (PdfException ee) { + throw ee; + } catch (ArrayIndexOutOfBoundsException excep) { + throw new PdfInvalidException(MessageConstants.PDF_HUL_147); // PDF-HUL-147 + } catch (Exception e) { + throw new PdfInvalidException(MessageConstants.PDF_HUL_29); // PDF-HUL-29 } + } - /** - * Initialize an iterator through the descendants of this node. - */ - @Override - public void startWalk () - { - _descendantsIter = _descendants.listIterator (); - _currentDescendant = null; - _walkFirst = true; - _walkFinished = false; - _visitedNodes = new HashSet<> (); // Track self-recursion + /** Initialize an iterator through the descendants of this node. */ + @Override + public void startWalk() { + _descendantsIter = _descendants.listIterator(); + _currentDescendant = null; + _walkFirst = true; + _walkFinished = false; + _visitedNodes = new HashSet<>(); // Track self-recursion + } + + /** + * Get the next PageObject which is under this node. This function is designed such that calling + * startWalk() and then repeatedly calling nextPageObject() will return all the PageObjects in the + * tree under this node, and finally will return null when there are no more. + */ + @Override + public PageObject nextPageObject() throws PdfMalformedException { + if (_walkFinished) { + return null; } - - /** - * Get the next PageObject which is under this node. This function - * is designed such that calling startWalk() and then repeatedly - * calling nextPageObject() will return all the PageObjects in the tree - * under this node, and finally will return null when there are no more. - */ - @Override - public PageObject nextPageObject () throws PdfMalformedException - { - if (_walkFinished) { - return null; - } - // _currentDescendant == null and _walkFinished == false indicates - // we're at the start. - if (_currentDescendant == null) { - if (!_descendantsIter.hasNext ()) { - _walkFinished = true; - return null; - } + // _currentDescendant == null and _walkFinished == false indicates + // we're at the start. + if (_currentDescendant == null) { + if (!_descendantsIter.hasNext()) { + _walkFinished = true; + return null; + } - // Get first descendant - _currentDescendant = _descendantsIter.next (); - _currentDescendant.startWalk (); - } - - PageObject retval = _currentDescendant.nextPageObject (); - if (retval == null) { - if (_descendantsIter.hasNext ()) { - // Every node is a page object or - // has at least one page object below it, right? - _currentDescendant = _descendantsIter.next (); - _currentDescendant.startWalk (); - retval = _currentDescendant.nextPageObject (); - } - else { - // We've gone through all our descendants. - _walkFinished = true; - } - } - if (retval != null) { - Integer objnum = Integer.valueOf(retval.getDict().getObjNumber()); - if (_visitedNodes.contains(objnum)) { - throw new PdfMalformedException(MessageConstants.PDF_HUL_30); // PDF-HUL-30 - } - _visitedNodes.add(objnum); - } - return retval; - } - - /** - * Get the next DocNode which is under this node. This function - * is designed such that calling startWalk() and then repeatedly - * calling nextPageObject() will return first this node, - * then all the DocNodes in the tree - * under this node. It finally will return null when there - * are no more. - */ - @Override - public DocNode nextDocNode () throws PdfMalformedException - { - if (_walkFinished) { - return null; - } - // _walkFinished == false and _walkFirst == true indicates - // we need to return "this". - if (_walkFirst) { - _walkFirst = false; - return this; - } - // _currentDescendant == null and _walkFinished == false indicates - // we're at the start. This is almost identical to the - // logic for nextPageObject. - if (_currentDescendant == null) { - if (!_descendantsIter.hasNext ()) { - _walkFinished = true; - return null; - } + // Get first descendant + _currentDescendant = _descendantsIter.next(); + _currentDescendant.startWalk(); + } - // Get first descendant - _currentDescendant = _descendantsIter.next (); - _currentDescendant.startWalk (); - } - - DocNode retval = _currentDescendant.nextDocNode (); - if (retval == null) { - if (_descendantsIter.hasNext ()) { - // Every node is a page object or - // has at least one page object below it, right? - _currentDescendant = _descendantsIter.next (); - _currentDescendant.startWalk (); - retval = _currentDescendant.nextDocNode (); - } - else { - // We've gone through all our descendants. - _walkFinished = true; - } - } - if (retval != null) { - Integer objnum = Integer.valueOf(retval.getDict().getObjNumber()); - if (_visitedNodes.contains(objnum)) { - throw new PdfMalformedException(MessageConstants.PDF_HUL_31); // PDF-HUL-31 - } - _visitedNodes.add(objnum); - } - return retval; - } + PageObject retval = _currentDescendant.nextPageObject(); + if (retval == null) { + if (_descendantsIter.hasNext()) { + // Every node is a page object or + // has at least one page object below it, right? + _currentDescendant = _descendantsIter.next(); + _currentDescendant.startWalk(); + retval = _currentDescendant.nextPageObject(); + } else { + // We've gone through all our descendants. + _walkFinished = true; + } + } + if (retval != null) { + Integer objnum = Integer.valueOf(retval.getDict().getObjNumber()); + if (_visitedNodes.contains(objnum)) { + throw new PdfMalformedException(MessageConstants.PDF_HUL_30); // PDF-HUL-30 + } + _visitedNodes.add(objnum); + } + return retval; + } + + /** + * Get the next DocNode which is under this node. This function is designed such that calling + * startWalk() and then repeatedly calling nextPageObject() will return first this node, then all + * the DocNodes in the tree under this node. It finally will return null when there are no more. + */ + @Override + public DocNode nextDocNode() throws PdfMalformedException { + if (_walkFinished) { + return null; + } + // _walkFinished == false and _walkFirst == true indicates + // we need to return "this". + if (_walkFirst) { + _walkFirst = false; + return this; + } + // _currentDescendant == null and _walkFinished == false indicates + // we're at the start. This is almost identical to the + // logic for nextPageObject. + if (_currentDescendant == null) { + if (!_descendantsIter.hasNext()) { + _walkFinished = true; + return null; + } + + // Get first descendant + _currentDescendant = _descendantsIter.next(); + _currentDescendant.startWalk(); + } + + DocNode retval = _currentDescendant.nextDocNode(); + if (retval == null) { + if (_descendantsIter.hasNext()) { + // Every node is a page object or + // has at least one page object below it, right? + _currentDescendant = _descendantsIter.next(); + _currentDescendant.startWalk(); + retval = _currentDescendant.nextDocNode(); + } else { + // We've gone through all our descendants. + _walkFinished = true; + } + } + if (retval != null) { + Integer objnum = Integer.valueOf(retval.getDict().getObjNumber()); + if (_visitedNodes.contains(objnum)) { + throw new PdfMalformedException(MessageConstants.PDF_HUL_31); // PDF-HUL-31 + } + _visitedNodes.add(objnum); + } + return retval; + } } diff --git a/jhove-modules/pdf-hul/src/main/java/edu/harvard/hul/ois/jhove/module/pdf/Parser.java b/jhove-modules/pdf-hul/src/main/java/edu/harvard/hul/ois/jhove/module/pdf/Parser.java index 3fe250a16..d14031ff2 100644 --- a/jhove-modules/pdf-hul/src/main/java/edu/harvard/hul/ois/jhove/module/pdf/Parser.java +++ b/jhove-modules/pdf-hul/src/main/java/edu/harvard/hul/ois/jhove/module/pdf/Parser.java @@ -1,463 +1,403 @@ -/********************************************************************** - * Jhove - JSTOR/Harvard Object Validation Environment - * Copyright 2003 by JSTOR and the President and Fellows of Harvard College - **********************************************************************/ - +/** + * ******************************************************************** Jhove - JSTOR/Harvard Object + * Validation Environment Copyright 2003 by JSTOR and the President and Fellows of Harvard College + * ******************************************************************** + */ package edu.harvard.hul.ois.jhove.module.pdf; +import edu.harvard.hul.ois.jhove.messages.JhoveMessage; +import edu.harvard.hul.ois.jhove.messages.JhoveMessages; import java.io.IOException; import java.text.MessageFormat; import java.util.Map; import java.util.Set; import java.util.Vector; -import edu.harvard.hul.ois.jhove.messages.JhoveMessage; -import edu.harvard.hul.ois.jhove.messages.JhoveMessages; - /** - * The Parser class implements some limited syntactic analysis for PDF. - * It isn't by any means intended to be a full parser. Its main job is - * to track nesting of syntactic elements such as dictionary and array - * beginnings and ends. + * The Parser class implements some limited syntactic analysis for PDF. It isn't by any means + * intended to be a full parser. Its main job is to track nesting of syntactic elements such as + * dictionary and array beginnings and ends. */ -public class Parser -{ - /** The Tokenizer which the parser will use. */ - private Tokenizer _tokenizer; - /** The number of dictionary starts on stack. */ - private int _dictDepth; - /** The number of array starts on stack. */ - private int _arrayDepth; - /** The file's object map. */ - private Map _objectMap; - /** PDF/A compliance flag. */ - private boolean _pdfACompliant; +public class Parser { + /** The Tokenizer which the parser will use. */ + private Tokenizer _tokenizer; + /** The number of dictionary starts on stack. */ + private int _dictDepth; + /** The number of array starts on stack. */ + private int _arrayDepth; + /** The file's object map. */ + private Map _objectMap; + /** PDF/A compliance flag. */ + private boolean _pdfACompliant; + /** + * Constructor. A Parser works with a Tokenizer that feeds it tokens. + * + * @param tokenizer The Tokenizer which the parser will use + */ + public Parser(Tokenizer tokenizer) { + _tokenizer = tokenizer; + _pdfACompliant = true; + reset(); + } - /** - * Constructor. A Parser works with a Tokenizer that feeds it tokens. - * - * @param tokenizer The Tokenizer which the parser will use - */ - public Parser (Tokenizer tokenizer) - { - _tokenizer = tokenizer; - _pdfACompliant = true; - reset (); - } + /** Sets the object map on which the parser will work. */ + public void setObjectMap(Map objectMap) { + _objectMap = objectMap; + } - /** Sets the object map on which the parser will work. */ - public void setObjectMap (Map objectMap) - { - _objectMap = objectMap; - } + /** + * Clears the state of the parser so that it can start reading at a different place in the file. + * Clears the stack and the dictionary and array depth counters. + */ + public void reset() { + _dictDepth = 0; + _arrayDepth = 0; + } - /** - * Clears the state of the parser so that it can start - * reading at a different place in the file. Clears the - * stack and the dictionary and array depth counters. - */ - public void reset () { - _dictDepth = 0; - _arrayDepth = 0; - } + /** + * Clears the state of the parser so that it can start reading at a different place in the file + * and ignore any nesting errors. Sets the stack and the dictionary and array depth counters to a + * large number so that nesting exceptions won't be thrown. + */ + public void resetLoose() { + _dictDepth = 1000000; + _arrayDepth = 1000000; + } - /** - * Clears the state of the parser so that it can start - * reading at a different place in the file and ignore - * any nesting errors. Sets the stack and the dictionary - * and array depth counters to a large number so that - * nesting exceptions won't be thrown. - */ - public void resetLoose () { - _dictDepth = 1000000; - _arrayDepth = 1000000; - } + /** + * Gets a token. Uses Tokenizer.getNext, and keeps track of the depth of dictionary and array + * nesting. + */ + public Token getNext() throws IOException, PdfException { + return getNext(0L); + } - /** - * Gets a token. Uses Tokenizer.getNext, and keeps track - * of the depth of dictionary and array nesting. - */ - public Token getNext () throws IOException, PdfException - { - return getNext (0L); + /** + * Gets a token. Uses Tokenizer.getNext, and keeps track of the depth of dictionary and array + * nesting. + * + * @param max Maximum allowable size of the token + */ + public Token getNext(long max) throws IOException, PdfException { + Token tok = _tokenizer.getNext(max); + if (tok instanceof DictionaryStart) { + ++_dictDepth; + } else if (tok instanceof DictionaryEnd) { + --_dictDepth; + if (_dictDepth < 0) { + throw new PdfMalformedException(MessageConstants.PDF_HUL_33); // PDF-HUL-33 + } } - - /** - * Gets a token. Uses Tokenizer.getNext, and keeps track - * of the depth of dictionary and array nesting. - * - * @param max Maximum allowable size of the token - */ - public Token getNext (long max) throws IOException, PdfException - { - Token tok = _tokenizer.getNext (max); - if (tok instanceof DictionaryStart) { - ++_dictDepth; - } - else if (tok instanceof DictionaryEnd) { - --_dictDepth; - if (_dictDepth < 0) { - throw new PdfMalformedException (MessageConstants.PDF_HUL_33); // PDF-HUL-33 - } - } - if (tok instanceof ArrayStart) { - ++_arrayDepth; - } - else if (tok instanceof ArrayEnd) { - --_arrayDepth; - if (_arrayDepth < 0) { - throw new PdfMalformedException (MessageConstants.PDF_HUL_34); // PDF-HUL-34 - } - } - return tok; + if (tok instanceof ArrayStart) { + ++_arrayDepth; + } else if (tok instanceof ArrayEnd) { + --_arrayDepth; + if (_arrayDepth < 0) { + throw new PdfMalformedException(MessageConstants.PDF_HUL_34); // PDF-HUL-34 + } } + return tok; + } - /** - * A class-sensitive version of getNext. The token which is - * obtained must be of the specified class (or a subclass thereof), - * or a PdfInvalidException with message errMsg - * will be thrown. - */ - public Token getNext (Class clas, JhoveMessage errMsg) - throws IOException, PdfException - { - Token tok = getNext (); - if (!clas.isInstance (tok)) { - throw new PdfInvalidException (errMsg); - } - if (!tok.isPdfACompliant()) { - _pdfACompliant = false; - } - return tok; + /** + * A class-sensitive version of getNext. The token which is obtained must be of the + * specified class (or a subclass thereof), or a PdfInvalidException with message + * errMsg will be thrown. + */ + public Token getNext(Class clas, JhoveMessage errMsg) throws IOException, PdfException { + Token tok = getNext(); + if (!clas.isInstance(tok)) { + throw new PdfInvalidException(errMsg); } - - /** - * Returns the number of dictionary starts not yet matched by - * dictionary ends. - */ - public int getDictDepth () - { - return _dictDepth; + if (!tok.isPdfACompliant()) { + _pdfACompliant = false; } + return tok; + } - /** - * Returns the number of array starts not yet matched by array ends. - */ - public int getArrayDepth () - { - return _arrayDepth; - } + /** Returns the number of dictionary starts not yet matched by dictionary ends. */ + public int getDictDepth() { + return _dictDepth; + } - /** Returns the Tokenizer's current whitespace string. */ - public String getWSString () { - return _tokenizer.getWSString (); - } + /** Returns the number of array starts not yet matched by array ends. */ + public int getArrayDepth() { + return _arrayDepth; + } - /** Returns the language code set from the Tokenizer. */ - public Set getLanguageCodes () - { - return _tokenizer.getLanguageCodes (); - } + /** Returns the Tokenizer's current whitespace string. */ + public String getWSString() { + return _tokenizer.getWSString(); + } - /** - * Returns false if either the parser or the tokenizer has detected - * non-compliance with PDF/A restrictions. A value of true - * is no guarantee that the file is compliant. - */ - public boolean getPDFACompliant () - { - if (!_tokenizer.getPDFACompliant ()) { - _pdfACompliant = false; - } - return _pdfACompliant; + /** Returns the language code set from the Tokenizer. */ + public Set getLanguageCodes() { + return _tokenizer.getLanguageCodes(); + } + + /** + * Returns false if either the parser or the tokenizer has detected non-compliance with PDF/A + * restrictions. A value of true is no guarantee that the file is compliant. + */ + public boolean getPDFACompliant() { + if (!_tokenizer.getPDFACompliant()) { + _pdfACompliant = false; } + return _pdfACompliant; + } - /** - * Sets the value of the pdfACompliant flag. This may be used to - * clear previous detection of noncompliance. If the parameter - * has a value of true, the tokenizer's pdfACompliant - * flag is also set to true. - */ - public void setPDFACompliant (boolean pdfACompliant) - { - _pdfACompliant = pdfACompliant; - if (pdfACompliant) { - _tokenizer.setPDFACompliant (true); - } + /** + * Sets the value of the pdfACompliant flag. This may be used to clear previous detection of + * noncompliance. If the parameter has a value of true, the tokenizer's pdfACompliant + * flag is also set to true. + */ + public void setPDFACompliant(boolean pdfACompliant) { + _pdfACompliant = pdfACompliant; + if (pdfACompliant) { + _tokenizer.setPDFACompliant(true); } + } - /** - * Reads an object definition, from wherever we are in the stream to - * the completion of one full object after the obj keyword. - */ - public PdfObject readObjectDef () throws IOException, PdfException - { - Numeric objNumTok = (Numeric) getNext - (Numeric.class, MessageConstants.PDF_HUL_35); // PDF-HUL-35 - return readObjectDef (objNumTok); + /** + * Reads an object definition, from wherever we are in the stream to the completion of one full + * object after the obj keyword. + */ + public PdfObject readObjectDef() throws IOException, PdfException { + Numeric objNumTok = (Numeric) getNext(Numeric.class, MessageConstants.PDF_HUL_35); // PDF-HUL-35 + return readObjectDef(objNumTok); + } + + /** + * Reads an object definition, given the first numeric object, which has already been read and is + * passed as an argument. This is called by the no-argument readObjectDef; the only other case in + * which it will be called is for a cross-reference stream, which can be distinguished from a + * cross-reference table only once the first token is read. + */ + public PdfObject readObjectDef(Numeric objNumTok) throws IOException, PdfException { + reset(); + // The start of an object must be obj + // Numeric objNumTok = (Numeric) getNext (Numeric.class, invDef); + Numeric genNumTok = (Numeric) getNext(Numeric.class, MessageConstants.PDF_HUL_36); // PDF-HUL-36 + Keyword objKey = (Keyword) getNext(Keyword.class, MessageConstants.PDF_HUL_37); // PDF-HUL-37 + if (!"obj".equals(objKey.getValue())) { + throw new PdfMalformedException(MessageConstants.PDF_HUL_38); // PDF-HUL-38 + } + if (_tokenizer.getWSString().length() > 1) { + _pdfACompliant = false; } + PdfObject obj = readObject(false); - /** - * Reads an object definition, given the first numeric object, which - * has already been read and is passed as an argument. This is called - * by the no-argument readObjectDef; the only other case in which it - * will be called is for a cross-reference stream, which can be distinguished - * from a cross-reference table only once the first token is read. - */ - public PdfObject readObjectDef (Numeric objNumTok) - throws IOException, PdfException - { - reset (); - // The start of an object must be obj - //Numeric objNumTok = (Numeric) getNext (Numeric.class, invDef); - Numeric genNumTok = (Numeric) getNext (Numeric.class, MessageConstants.PDF_HUL_36); // PDF-HUL-36 - Keyword objKey = (Keyword) getNext (Keyword.class, MessageConstants.PDF_HUL_37); // PDF-HUL-37 - if (!"obj".equals (objKey.getValue ())) { - throw new PdfMalformedException (MessageConstants.PDF_HUL_38); // PDF-HUL-38 - } - if (_tokenizer.getWSString ().length () > 1) { - _pdfACompliant = false; + // Now a special-case check to read a stream object, which + // consists of a dictionary followed by a stream token. + if (obj instanceof PdfDictionary) { + Stream strm = null; + try { + strm = (Stream) getNext(Stream.class, JhoveMessages.DEFAULT_MESSAGE); + } catch (Exception e) { + // If we get an exception, it just means it wasn't a stream + } + if (strm != null) { + // Assimilate the dictionary and the stream token into the + // object to be returned + PdfStream strmObj = new PdfStream((PdfDictionary) obj, strm); + if (!strmObj.isPdfaCompliant()) { + _pdfACompliant = false; } - PdfObject obj = readObject (false); + obj = strmObj; + } + } - // Now a special-case check to read a stream object, which - // consists of a dictionary followed by a stream token. - if (obj instanceof PdfDictionary) { - Stream strm = null; - try { - strm = (Stream) getNext (Stream.class, JhoveMessages.DEFAULT_MESSAGE); - } - catch (Exception e) { - // If we get an exception, it just means it wasn't a stream - } - if (strm != null) { - // Assimilate the dictionary and the stream token into the - // object to be returned - PdfStream strmObj = new PdfStream ((PdfDictionary) obj, strm); - if (!strmObj.isPdfaCompliant()) { - _pdfACompliant = false; - } - obj = strmObj; - } - } + obj.setObjNumber(objNumTok.getIntegerValue()); + obj.setGenNumber(genNumTok.getIntegerValue()); + return obj; + } - obj.setObjNumber (objNumTok.getIntegerValue ()); - obj.setGenNumber (genNumTok.getIntegerValue ()); - return obj; + /** + * Reads an object. By design, this reader has a number of limitations: + * + *

    + *
  • It doesn't retain the contents of streams + *
  • It doesn't recognize a stream when it's pointing at the stream's dictionary; it will just + * read the dictionary + *
+ * + * Functions which it uses may call it recursively to build up structures. If it encounters a + * token inappropriate for an object start, it throws a PdfException on which + * getToken may be called to retrieve that token. + */ + public PdfObject readObject(boolean allowPseudo) throws IOException, PdfException { + Token tok = getNext(); + if (tok instanceof ArrayStart) { + return readArray(); + } else if (tok instanceof DictionaryStart) { + return readDictionary(); } - - /** - * Reads an object. By design, this reader has a number - * of limitations: - *
    - *
  • It doesn't retain the contents of streams
  • - *
  • It doesn't recognize a stream when it's pointing at - * the stream's dictionary; it will just read the - * dictionary
  • - *
- * Functions which it uses may call it recursively to build up structures. - * If it encounters a token inappropriate for an object start, it - * throws a PdfException on which getToken - * may be called to retrieve that token. - */ - public PdfObject readObject (boolean allowPseudo) throws IOException, PdfException - { - Token tok = getNext (); - if (tok instanceof ArrayStart) { - return readArray (); - } - else if (tok instanceof DictionaryStart) { - return readDictionary (); - } - // For the end of a dictionary or array, retu - else if (allowPseudo && tok instanceof ArrayEnd) { - return new PdfArrayEnd(tok); - } - else if (allowPseudo && tok instanceof DictionaryEnd) { - return new PdfDictionaryEnd(tok); - } - else if (tok.isSimpleToken ()) { - return new PdfSimpleObject (tok); - } - else { - throw new PdfMalformedException - (MessageConstants.PDF_HUL_39, getOffset(), tok); // PDF-HUL-39 - } + // For the end of a dictionary or array, retu + else if (allowPseudo && tok instanceof ArrayEnd) { + return new PdfArrayEnd(tok); + } else if (allowPseudo && tok instanceof DictionaryEnd) { + return new PdfDictionaryEnd(tok); + } else if (tok.isSimpleToken()) { + return new PdfSimpleObject(tok); + } else { + throw new PdfMalformedException(MessageConstants.PDF_HUL_39, getOffset(), tok); // PDF-HUL-39 } + } - /** - * Reads a PDF array. When this is called we have already read the - * ArrayStart token and arrayDepth has been incremented to reflect this. - */ - public PdfArray readArray () throws IOException, PdfException - { - PdfArray arr = new PdfArray (); - for (;;) { - PdfObject obj = readObject (true); - if (!(obj instanceof PdfPseudoObject)) { - arr.add (obj); - } - else if (obj instanceof PdfArrayEnd) { - // We detect the end of an array by returning a PdfArrayEnd. - // When we get the end of the array, collapse the vector - // before returning the object. - PdfArrayEnd eobj = (PdfArrayEnd) obj; - Token tok = eobj.getToken(); - if (tok instanceof ArrayEnd) { - collapseObjectVector (arr.getContent ()); - if (!arr.isPdfACompliant()) { - _pdfACompliant = false; - } - return arr; - } - throw new PdfMalformedException - (MessageConstants.PDF_HUL_40, getOffset()); // PDF-HUL-40 - } + /** + * Reads a PDF array. When this is called we have already read the ArrayStart token and arrayDepth + * has been incremented to reflect this. + */ + public PdfArray readArray() throws IOException, PdfException { + PdfArray arr = new PdfArray(); + for (; ; ) { + PdfObject obj = readObject(true); + if (!(obj instanceof PdfPseudoObject)) { + arr.add(obj); + } else if (obj instanceof PdfArrayEnd) { + // We detect the end of an array by returning a PdfArrayEnd. + // When we get the end of the array, collapse the vector + // before returning the object. + PdfArrayEnd eobj = (PdfArrayEnd) obj; + Token tok = eobj.getToken(); + if (tok instanceof ArrayEnd) { + collapseObjectVector(arr.getContent()); + if (!arr.isPdfACompliant()) { + _pdfACompliant = false; + } + return arr; } + throw new PdfMalformedException(MessageConstants.PDF_HUL_40, getOffset()); // PDF-HUL-40 + } } + } - /** - * Reads a PDF dictionary. When this is called, we have already read the - * DictionaryStart token, and dictDepth has been incremented to reflect - * this. Only for use in this special case, where we're picking up - * a dictionary in midstream. - */ - public PdfDictionary readDictionary () throws IOException, PdfException - { - PdfDictionary dict = new PdfDictionary (); - // Create a vector as a temporary holding place for the objects - Vector vec = new Vector<> (); + /** + * Reads a PDF dictionary. When this is called, we have already read the DictionaryStart token, + * and dictDepth has been incremented to reflect this. Only for use in this special case, where + * we're picking up a dictionary in midstream. + */ + public PdfDictionary readDictionary() throws IOException, PdfException { + PdfDictionary dict = new PdfDictionary(); + // Create a vector as a temporary holding place for the objects + Vector vec = new Vector<>(); - for (;;) { - PdfObject obj = readObject (true); - // Comments within a dictionary need to be ignored. - if (obj instanceof PdfSimpleObject - && ((PdfSimpleObject) obj).getToken() instanceof Comment) { - continue; - } - if (!(obj instanceof PdfPseudoObject)) { - vec.add (obj); - } - else if (obj instanceof PdfDictionaryEnd) { - // When we get the end of the dictionary, - // collapse the vector before returning the object. - PdfDictionaryEnd eobj = (PdfDictionaryEnd) obj; - Token tok = eobj.getToken (); - if (tok instanceof DictionaryEnd) { - collapseObjectVector (vec); - // The collapsed vector must contain an even number of objects - int vecSize = vec.size (); - if ((vecSize % 2) != 0) { - String mess = MessageFormat.format(MessageConstants.PDF_HUL_41.getMessage(), Integer.valueOf(vecSize)); - JhoveMessage message = JhoveMessages.getMessageInstance(MessageConstants.PDF_HUL_41.getId(), mess); - throw new PdfMalformedException(message, getOffset ()); // PDF-HUL-41 - } - for (int i = 0; i < vecSize; i += 2) { - try { - Name key = (Name) ((PdfSimpleObject) - vec.elementAt (i)).getToken (); - PdfObject value = vec.elementAt (i + 1); - dict.add (key.getValue (), value); - } - catch (Exception f) { - throw new PdfMalformedException (MessageConstants.PDF_HUL_42, getOffset ()); // PDF-HUL-42 - } - } - if (!dict.isPdfACompliant()) { - _pdfACompliant = false; // Exceeds implementation limit for PDF/A - } - return dict; - } - throw new PdfMalformedException - (MessageConstants.PDF_HUL_43, getOffset()); // PDF-HUL-43 + for (; ; ) { + PdfObject obj = readObject(true); + // Comments within a dictionary need to be ignored. + if (obj instanceof PdfSimpleObject && ((PdfSimpleObject) obj).getToken() instanceof Comment) { + continue; + } + if (!(obj instanceof PdfPseudoObject)) { + vec.add(obj); + } else if (obj instanceof PdfDictionaryEnd) { + // When we get the end of the dictionary, + // collapse the vector before returning the object. + PdfDictionaryEnd eobj = (PdfDictionaryEnd) obj; + Token tok = eobj.getToken(); + if (tok instanceof DictionaryEnd) { + collapseObjectVector(vec); + // The collapsed vector must contain an even number of objects + int vecSize = vec.size(); + if ((vecSize % 2) != 0) { + String mess = + MessageFormat.format( + MessageConstants.PDF_HUL_41.getMessage(), Integer.valueOf(vecSize)); + JhoveMessage message = + JhoveMessages.getMessageInstance(MessageConstants.PDF_HUL_41.getId(), mess); + throw new PdfMalformedException(message, getOffset()); // PDF-HUL-41 + } + for (int i = 0; i < vecSize; i += 2) { + try { + Name key = (Name) ((PdfSimpleObject) vec.elementAt(i)).getToken(); + PdfObject value = vec.elementAt(i + 1); + dict.add(key.getValue(), value); + } catch (Exception f) { + throw new PdfMalformedException( + MessageConstants.PDF_HUL_42, getOffset()); // PDF-HUL-42 } + } + if (!dict.isPdfACompliant()) { + _pdfACompliant = false; // Exceeds implementation limit for PDF/A + } + return dict; } + throw new PdfMalformedException(MessageConstants.PDF_HUL_43, getOffset()); // PDF-HUL-43 + } } + } - /** Returns the current offset into the file. */ - public long getOffset () - { - return _tokenizer.getOffset (); - } + /** Returns the current offset into the file. */ + public long getOffset() { + return _tokenizer.getOffset(); + } - /** - * Positions the file to the specified offset, and - * resets the state for a new token stream. - */ - public void seek (long offset) throws IOException, PdfException - { - _tokenizer.seek (offset); - reset (); - } + /** Positions the file to the specified offset, and resets the state for a new token stream. */ + public void seek(long offset) throws IOException, PdfException { + _tokenizer.seek(offset); + reset(); + } - /** - * PDF has a wacky grammar which must be a legacy of - * PostScript's postfix syntax. A keyword of R means that - * the two previous objects are really part of an indirect object - * reference. This means that when a vector of objects is complete, - * it has to be read backwards so that indirect object references can - * be collapsed out. In the case of a dictionary, this has to be done - * before the content can be interpreted as key-value pairs. - */ - private void collapseObjectVector (Vector v) throws PdfException - { - int lowestChanged = -1; - for (int i = v.size() - 1; i >= 2; i--) { - PdfObject obj = v.elementAt (i); - if (obj instanceof PdfSimpleObject) { - Token tok = ((PdfSimpleObject) obj).getToken (); - if (tok instanceof Keyword && "R".equals (((Keyword)tok).getValue ())) { - // We're in the key of 'R'. The two previous tokens - // had better be Numerics. Three objects in the Vector - // are replaced by one. - try { - PdfSimpleObject nobj = - (PdfSimpleObject) v.elementAt (i - 2); - Numeric ntok = (Numeric) nobj.getToken (); - int objNum = ntok.getIntegerValue (); - nobj = (PdfSimpleObject) v.elementAt (i - 1); - ntok = (Numeric) nobj.getToken (); - int genNum = ntok.getIntegerValue (); - v.set (i - 2, new PdfIndirectObj - (objNum, genNum, _objectMap)); - //v.removeElementAt (i); - //v.removeElementAt (i - 1); - // Put in null as placeholder, to be removed below - v.set(i, null); - v.set(i - 1, null); - lowestChanged = i - 1; - i -= 2; - } - catch (Exception e) { - throw new PdfMalformedException - (MessageConstants.PDF_HUL_44); // PDF-HUL-44 - } - } - } + /** + * PDF has a wacky grammar which must be a legacy of PostScript's postfix syntax. A keyword of R + * means that the two previous objects are really part of an indirect object reference. This means + * that when a vector of objects is complete, it has to be read backwards so that indirect object + * references can be collapsed out. In the case of a dictionary, this has to be done before the + * content can be interpreted as key-value pairs. + */ + private void collapseObjectVector(Vector v) throws PdfException { + int lowestChanged = -1; + for (int i = v.size() - 1; i >= 2; i--) { + PdfObject obj = v.elementAt(i); + if (obj instanceof PdfSimpleObject) { + Token tok = ((PdfSimpleObject) obj).getToken(); + if (tok instanceof Keyword && "R".equals(((Keyword) tok).getValue())) { + // We're in the key of 'R'. The two previous tokens + // had better be Numerics. Three objects in the Vector + // are replaced by one. + try { + PdfSimpleObject nobj = (PdfSimpleObject) v.elementAt(i - 2); + Numeric ntok = (Numeric) nobj.getToken(); + int objNum = ntok.getIntegerValue(); + nobj = (PdfSimpleObject) v.elementAt(i - 1); + ntok = (Numeric) nobj.getToken(); + int genNum = ntok.getIntegerValue(); + v.set(i - 2, new PdfIndirectObj(objNum, genNum, _objectMap)); + // v.removeElementAt (i); + // v.removeElementAt (i - 1); + // Put in null as placeholder, to be removed below + v.set(i, null); + v.set(i - 1, null); + lowestChanged = i - 1; + i -= 2; + } catch (Exception e) { + throw new PdfMalformedException(MessageConstants.PDF_HUL_44); // PDF-HUL-44 + } } - // Now remove all the positioned that were nulled. - if (lowestChanged > 0) { - int i; - int j; - for (i = lowestChanged, j = lowestChanged; i < v.size(); i++) { - PdfObject elem = v.elementAt(i); - if (elem != null) { - v.set(j++, elem); - } - } - v.setSize(j); + } + } + // Now remove all the positioned that were nulled. + if (lowestChanged > 0) { + int i; + int j; + for (i = lowestChanged, j = lowestChanged; i < v.size(); i++) { + PdfObject elem = v.elementAt(i); + if (elem != null) { + v.set(j++, elem); } + } + v.setSize(j); } + } - /** - * If true, do not attempt to parse non-whitespace - * delimited tokens, e.g., literal and hexadecimal strings. - * - * @param flag Scan mode flag - */ - public void scanMode (boolean flag) - { - _tokenizer.scanMode (flag); - } + /** + * If true, do not attempt to parse non-whitespace delimited tokens, e.g., literal + * and hexadecimal strings. + * + * @param flag Scan mode flag + */ + public void scanMode(boolean flag) { + _tokenizer.scanMode(flag); + } } diff --git a/jhove-modules/pdf-hul/src/main/java/edu/harvard/hul/ois/jhove/module/pdf/PdfArray.java b/jhove-modules/pdf-hul/src/main/java/edu/harvard/hul/ois/jhove/module/pdf/PdfArray.java index 9ff291e91..871d57d9a 100644 --- a/jhove-modules/pdf-hul/src/main/java/edu/harvard/hul/ois/jhove/module/pdf/PdfArray.java +++ b/jhove-modules/pdf-hul/src/main/java/edu/harvard/hul/ois/jhove/module/pdf/PdfArray.java @@ -1,115 +1,94 @@ -/********************************************************************** - * Jhove - JSTOR/Harvard Object Validation Environment - * Copyright 2003 by JSTOR and the President and Fellows of Harvard College - **********************************************************************/ - +/** + * ******************************************************************** Jhove - JSTOR/Harvard Object + * Validation Environment Copyright 2003 by JSTOR and the President and Fellows of Harvard College + * ******************************************************************** + */ package edu.harvard.hul.ois.jhove.module.pdf; import java.util.*; -/** - * A representation of a PDF array object. - */ -public class PdfArray extends PdfObject -{ +/** A representation of a PDF array object. */ +public class PdfArray extends PdfObject { - private Vector _content; + private Vector _content; - /** - * Creates a PdfArray object. - * - * @param objNumber The PDF object number - * @param genNumber The PDF generation number - */ - public PdfArray (int objNumber, int genNumber) - { - super (objNumber, genNumber); - _content = new Vector<> (); - } + /** + * Creates a PdfArray object. + * + * @param objNumber The PDF object number + * @param genNumber The PDF generation number + */ + public PdfArray(int objNumber, int genNumber) { + super(objNumber, genNumber); + _content = new Vector<>(); + } - /** - * Creates a PdfArray object with empty contents. - * - */ - public PdfArray () - { - super (); - _content = new Vector<> (); - } + /** Creates a PdfArray object with empty contents. */ + public PdfArray() { + super(); + _content = new Vector<>(); + } - /** - * Adds an object to the array. - */ - public void add (PdfObject obj) - { - _content.add (obj); - } - - /** - * Return the contents of the array as a Vector. - */ - public Vector getContent () - { - return _content; - } - - /** Report if it's within implementation limits defined for PDF/A. */ - public boolean isPdfACompliant () - { - return _content.size() <= 8191; - } + /** Adds an object to the array. */ + public void add(PdfObject obj) { + _content.add(obj); + } + + /** Return the contents of the array as a Vector. */ + public Vector getContent() { + return _content; + } + + /** Report if it's within implementation limits defined for PDF/A. */ + public boolean isPdfACompliant() { + return _content.size() <= 8191; + } - /** - * Concatenate the elements, if they are PdfSimpleObjects, - * into a string separated by spaces. Return an empty string - * if there are no PdfSimpleObjects. - */ - public String toPipeline () - { - StringBuffer sb = new StringBuffer (); - for (int i = 0; i < _content.size (); i++) { - PdfObject elem = _content.elementAt (i); - if (elem instanceof PdfSimpleObject) { - String elemval = ((PdfSimpleObject) elem).getStringValue (); - // separate items with a space - if (sb.length () > 0) { - sb.append (' '); - } - sb.append (elemval); - } + /** + * Concatenate the elements, if they are PdfSimpleObjects, into a string separated by spaces. + * Return an empty string if there are no PdfSimpleObjects. + */ + public String toPipeline() { + StringBuffer sb = new StringBuffer(); + for (int i = 0; i < _content.size(); i++) { + PdfObject elem = _content.elementAt(i); + if (elem instanceof PdfSimpleObject) { + String elemval = ((PdfSimpleObject) elem).getStringValue(); + // separate items with a space + if (sb.length() > 0) { + sb.append(' '); } - return sb.toString (); + sb.append(elemval); + } } - - /** - * Attempts to convert this Array to a PDF rectangle. - * If the Array is a valid rectangle (i.e., an array of exactly - * four numbers), returns a Java array of four doubles reflecting - * the rectangle. Otherwise returns null. - */ - public double[] toRectangle () - { - if (_content.size () != 4) { - return null; - } - double[] retval = new double[4]; - try { - for (int i = 0; i < 4; i++) { - PdfObject elem = _content.elementAt (i); - if (elem instanceof PdfSimpleObject) { - double d = ((PdfSimpleObject) elem).getDoubleValue (); - retval[i] = d; - } - else { - return null; - } - } - return retval; - } - catch (Exception e) { - // Any failure (e.g., a ClassCastException) is assumed to mean - // it wasn't a proper Rectangle - return null; + return sb.toString(); + } + + /** + * Attempts to convert this Array to a PDF rectangle. If the Array is a valid rectangle (i.e., an + * array of exactly four numbers), returns a Java array of four doubles reflecting the rectangle. + * Otherwise returns null. + */ + public double[] toRectangle() { + if (_content.size() != 4) { + return null; + } + double[] retval = new double[4]; + try { + for (int i = 0; i < 4; i++) { + PdfObject elem = _content.elementAt(i); + if (elem instanceof PdfSimpleObject) { + double d = ((PdfSimpleObject) elem).getDoubleValue(); + retval[i] = d; + } else { + return null; } + } + return retval; + } catch (Exception e) { + // Any failure (e.g., a ClassCastException) is assumed to mean + // it wasn't a proper Rectangle + return null; } + } } diff --git a/jhove-modules/pdf-hul/src/main/java/edu/harvard/hul/ois/jhove/module/pdf/PdfArrayEnd.java b/jhove-modules/pdf-hul/src/main/java/edu/harvard/hul/ois/jhove/module/pdf/PdfArrayEnd.java index 9a97d6a27..70fb9bafd 100644 --- a/jhove-modules/pdf-hul/src/main/java/edu/harvard/hul/ois/jhove/module/pdf/PdfArrayEnd.java +++ b/jhove-modules/pdf-hul/src/main/java/edu/harvard/hul/ois/jhove/module/pdf/PdfArrayEnd.java @@ -1,17 +1,15 @@ package edu.harvard.hul.ois.jhove.module.pdf; -/** A marker for the parser to return when it encounters the end of - * a PDF array. - */ +/** A marker for the parser to return when it encounters the end of a PDF array. */ public class PdfArrayEnd extends PdfPseudoObject { - private Token token; - - public PdfArrayEnd (Token tok) { - token = tok; - } - - public Token getToken () { - return token; - } + private Token token; + + public PdfArrayEnd(Token tok) { + token = tok; + } + + public Token getToken() { + return token; + } } diff --git a/jhove-modules/pdf-hul/src/main/java/edu/harvard/hul/ois/jhove/module/pdf/PdfDictionary.java b/jhove-modules/pdf-hul/src/main/java/edu/harvard/hul/ois/jhove/module/pdf/PdfDictionary.java index 2f190226f..a2a786fe9 100644 --- a/jhove-modules/pdf-hul/src/main/java/edu/harvard/hul/ois/jhove/module/pdf/PdfDictionary.java +++ b/jhove-modules/pdf-hul/src/main/java/edu/harvard/hul/ois/jhove/module/pdf/PdfDictionary.java @@ -1,73 +1,59 @@ -/********************************************************************** - * Jhove - JSTOR/Harvard Object Validation Environment - * Copyright 2003 by JSTOR and the President and Fellows of Harvard College - **********************************************************************/ - -package edu.harvard.hul.ois.jhove.module.pdf; - -import java.util.*; - /** - * A representation of a PDF dictionary object. + * ******************************************************************** Jhove - JSTOR/Harvard Object + * Validation Environment Copyright 2003 by JSTOR and the President and Fellows of Harvard College + * ******************************************************************** */ -public class PdfDictionary extends PdfObject -{ - public static final int PDFA_IMPLEMENTATION_LIMIT = 4095; - private final Map _entries = new HashMap<> (); - - /** - * Creates a PdfDictionary object. - * - * @param objNumber The PDF object number - * @param genNumber The PDF generation number - */ - public PdfDictionary (int objNumber, int genNumber) - { - super (objNumber, genNumber); - } - - /** - * Creates a PdfDictionary object. - * - */ - public PdfDictionary () - { - super(); - } - - /** - * Accumulate an entry into the dictionary. - * - * @param key String value of the dictionary key - * @param value PdfObject encapsulation of the dictionary value - */ - public void add (String key, PdfObject value) - { - _entries.put (key, value); - } - - /** Get the PDFObject whose key has the specified string - * value. Returns null if there is no such key. - * - * @param key The string value of the key to look up. - */ - public PdfObject get (String key) - { - return _entries.get (key); - } +package edu.harvard.hul.ois.jhove.module.pdf; - /** Return true if it's within the PDF/A implementation limit. */ - public boolean isPdfACompliant () - { - return _entries.size() <= PDFA_IMPLEMENTATION_LIMIT; - } +import java.util.*; - /** - * Returns an iterator which will successively return - * all the values in the dictionary. - */ - public Iterator iterator () - { - return _entries.values ().iterator (); - } +/** A representation of a PDF dictionary object. */ +public class PdfDictionary extends PdfObject { + public static final int PDFA_IMPLEMENTATION_LIMIT = 4095; + private final Map _entries = new HashMap<>(); + + /** + * Creates a PdfDictionary object. + * + * @param objNumber The PDF object number + * @param genNumber The PDF generation number + */ + public PdfDictionary(int objNumber, int genNumber) { + super(objNumber, genNumber); + } + + /** Creates a PdfDictionary object. */ + public PdfDictionary() { + super(); + } + + /** + * Accumulate an entry into the dictionary. + * + * @param key String value of the dictionary key + * @param value PdfObject encapsulation of the dictionary value + */ + public void add(String key, PdfObject value) { + _entries.put(key, value); + } + + /** + * Get the PDFObject whose key has the specified string value. Returns null if there is no such + * key. + * + * @param key The string value of the key to look up. + */ + public PdfObject get(String key) { + return _entries.get(key); + } + + /** Return true if it's within the PDF/A implementation limit. */ + public boolean isPdfACompliant() { + return _entries.size() <= PDFA_IMPLEMENTATION_LIMIT; + } + + /** Returns an iterator which will successively return all the values in the dictionary. */ + public Iterator iterator() { + return _entries.values().iterator(); + } } diff --git a/jhove-modules/pdf-hul/src/main/java/edu/harvard/hul/ois/jhove/module/pdf/PdfDictionaryEnd.java b/jhove-modules/pdf-hul/src/main/java/edu/harvard/hul/ois/jhove/module/pdf/PdfDictionaryEnd.java index 67430e1a9..9407c6e5c 100644 --- a/jhove-modules/pdf-hul/src/main/java/edu/harvard/hul/ois/jhove/module/pdf/PdfDictionaryEnd.java +++ b/jhove-modules/pdf-hul/src/main/java/edu/harvard/hul/ois/jhove/module/pdf/PdfDictionaryEnd.java @@ -2,13 +2,13 @@ public class PdfDictionaryEnd extends PdfPseudoObject { - private Token token; - - public PdfDictionaryEnd (Token tok) { - token = tok; - } - - public Token getToken () { - return token; - } + private Token token; + + public PdfDictionaryEnd(Token tok) { + token = tok; + } + + public Token getToken() { + return token; + } } diff --git a/jhove-modules/pdf-hul/src/main/java/edu/harvard/hul/ois/jhove/module/pdf/PdfException.java b/jhove-modules/pdf-hul/src/main/java/edu/harvard/hul/ois/jhove/module/pdf/PdfException.java index 9611efa57..0407a5d75 100644 --- a/jhove-modules/pdf-hul/src/main/java/edu/harvard/hul/ois/jhove/module/pdf/PdfException.java +++ b/jhove-modules/pdf-hul/src/main/java/edu/harvard/hul/ois/jhove/module/pdf/PdfException.java @@ -1,89 +1,71 @@ -/********************************************************************** - * Jhove - JSTOR/Harvard Object Validation Environment - * Copyright 2003-2004 by JSTOR and the President and Fellows of Harvard College - **********************************************************************/ - +/** + * ******************************************************************** Jhove - JSTOR/Harvard Object + * Validation Environment Copyright 2003-2004 by JSTOR and the President and Fellows of Harvard + * College ******************************************************************** + */ package edu.harvard.hul.ois.jhove.module.pdf; import edu.harvard.hul.ois.jhove.RepInfo; import edu.harvard.hul.ois.jhove.messages.JhoveMessage; -import edu.harvard.hul.ois.jhove.messages.JhoveMessages; /** - * Abstract exception subclass used internally by the PDF module. - * Throwing a PDFException indicates that the document is - * ill-formed or invalid; use the appropriate subclass to - * indicate which. + * Abstract exception subclass used internally by the PDF module. Throwing a PDFException indicates + * that the document is ill-formed or invalid; use the appropriate subclass to indicate which. */ public abstract class PdfException extends Exception { - /** - * - */ - private static final long serialVersionUID = 972230109944524397L; - /* - * Note 25-Feb-2004: Previously PdfException indicated - * a not-well-formed condition, and PdfInvalidException - * was a subclass of PdfException that indicated an - * invalid condition. This is a bad class hierarchy, - * since the role of PdfException was ambiguous, - * so PdfMalformedException was added, and PdfException - * was made abstract. - */ - private final JhoveMessage message; - private final long _offset; // File offset at which the exception - // occurred - private final Token _token; // Token associated with the exception + /** */ + private static final long serialVersionUID = 972230109944524397L; + /* + * Note 25-Feb-2004: Previously PdfException indicated + * a not-well-formed condition, and PdfInvalidException + * was a subclass of PdfException that indicated an + * invalid condition. This is a bad class hierarchy, + * since the role of PdfException was ambiguous, + * so PdfMalformedException was added, and PdfException + * was made abstract. + */ + private final JhoveMessage message; + private final long _offset; // File offset at which the exception + // occurred + private final Token _token; // Token associated with the exception - /** - * Create a PdfException. - */ - public PdfException(final JhoveMessage message) { - this(message, -1); - } + /** Create a PdfException. */ + public PdfException(final JhoveMessage message) { + this(message, -1); + } - /** - * Create a PdfException with specified offset. - */ - public PdfException(final JhoveMessage message, final long offset) { - this(message, offset, null); - } + /** Create a PdfException with specified offset. */ + public PdfException(final JhoveMessage message, final long offset) { + this(message, offset, null); + } - /** - * Create a PdfException with specified offset and token. - */ - public PdfException(final JhoveMessage message, final long offset, final Token token) { - super(message.getMessage()); - this.message = message; - this._offset = offset; - this._token = token; - } + /** Create a PdfException with specified offset and token. */ + public PdfException(final JhoveMessage message, final long offset, final Token token) { + super(message.getMessage()); + this.message = message; + this._offset = offset; + this._token = token; + } - /** - * @return the JhoveMessage associated with this exception - */ - public JhoveMessage getJhoveMessage() { - return this.message; - } + /** @return the JhoveMessage associated with this exception */ + public JhoveMessage getJhoveMessage() { + return this.message; + } - /** - * Returns the offset at which the exception occurred. - */ - public long getOffset() { - return this._offset; - } + /** Returns the offset at which the exception occurred. */ + public long getOffset() { + return this._offset; + } - /** - * Return the token associated with the exception. - */ - public Token getToken() { - return this._token; - } + /** Return the token associated with the exception. */ + public Token getToken() { + return this._token; + } - /** - * Performs the appropriate disparagement act on a RepInfo - * object, such as setting the valid or well-formed - * flag to false. - */ - public abstract void disparage(final RepInfo info); + /** + * Performs the appropriate disparagement act on a RepInfo object, such as setting the valid or + * well-formed flag to false. + */ + public abstract void disparage(final RepInfo info); } diff --git a/jhove-modules/pdf-hul/src/main/java/edu/harvard/hul/ois/jhove/module/pdf/PdfFlateInputStream.java b/jhove-modules/pdf-hul/src/main/java/edu/harvard/hul/ois/jhove/module/pdf/PdfFlateInputStream.java index 642a69056..3a2437848 100644 --- a/jhove-modules/pdf-hul/src/main/java/edu/harvard/hul/ois/jhove/module/pdf/PdfFlateInputStream.java +++ b/jhove-modules/pdf-hul/src/main/java/edu/harvard/hul/ois/jhove/module/pdf/PdfFlateInputStream.java @@ -1,9 +1,9 @@ -/********************************************************************** - * Jhove - JSTOR/Harvard Object Validation Environment - * Copyright 2005 by JSTOR and the President and Fellows of Harvard College +/** + * ******************************************************************** Jhove - JSTOR/Harvard Object + * Validation Environment Copyright 2005 by JSTOR and the President and Fellows of Harvard College * - **********************************************************************/ - + *

******************************************************************** + */ package edu.harvard.hul.ois.jhove.module.pdf; import java.io.FilterInputStream; @@ -12,344 +12,323 @@ import java.util.zip.InflaterInputStream; /** - * An enhancement of InflaterInputStream to support Predictor and Columns. - * How complicated does this get? Do I need to read the whole thing before I can - * "predict" anything? + * An enhancement of InflaterInputStream to support Predictor and Columns. How complicated does this + * get? Do I need to read the whole thing before I can "predict" anything? * * @author Gary McGath - * */ public class PdfFlateInputStream extends FilterInputStream { - private InflaterInputStream iis; - private int predictor; - private int columns; - /* bits per component */ - private int bpc; - /* colors -- the term is being used in some idiosyncratic sense */ - private int colors; + private InflaterInputStream iis; + private int predictor; + private int columns; + /* bits per component */ + private int bpc; + /* colors -- the term is being used in some idiosyncratic sense */ + private int colors; - /* Number of bytes to hold last column of previous row, if needed */ - private int colBytes; - /* Length of the total row, including space for last column. - * (Actual length of a predictive row is rowLen - colBytes) */ - private int rowLen; - /* Two row buffers for double buffering */ - private byte[] rowBuf; - private byte[] rowBuf2; - private int rowBufOff; // aka linei_ - private boolean eof; + /* Number of bytes to hold last column of previous row, if needed */ + private int colBytes; + /* Length of the total row, including space for last column. + * (Actual length of a predictive row is rowLen - colBytes) */ + private int rowLen; + /* Two row buffers for double buffering */ + private byte[] rowBuf; + private byte[] rowBuf2; + private int rowBufOff; // aka linei_ + private boolean eof; - /* offset to next available byte */ - private int iisBufOff; - /* byte array read from IIS */ - private byte[] iisBuf; - /* Allocation size for iisBuf */ - private final static int IISBUF_SIZE = 4096; - /* iisBufLen -- number of bytes of valid data in iisBuf */ - private int iisBufLen; - /* End of file indicator for IIS stream */ - private boolean iisEof; + /* offset to next available byte */ + private int iisBufOff; + /* byte array read from IIS */ + private byte[] iisBuf; + /* Allocation size for iisBuf */ + private static final int IISBUF_SIZE = 4096; + /* iisBufLen -- number of bytes of valid data in iisBuf */ + private int iisBufLen; + /* End of file indicator for IIS stream */ + private boolean iisEof; - /** - * Constructor with null DecodeParms dictionary - * - * @param is InputStream to be inflated - */ - public PdfFlateInputStream(InputStream is) { - this (is, null); - } + /** + * Constructor with null DecodeParms dictionary + * + * @param is InputStream to be inflated + */ + public PdfFlateInputStream(InputStream is) { + this(is, null); + } - /** - * Constructor with specified DecodeParms dictionary - * - * @param is InputStream to be inflated - * @param parms DecodeParms dictionary. - * May be null, in which case this is equivalent - * to the one-parameter constructor. - * - */ - public PdfFlateInputStream (InputStream is, PdfDictionary parms) - { - super(is); - iis = new InflaterInputStream (is); - /* Set default values */ - predictor = 1; // no prediction - columns = 1; - bpc = 8; - colors = 1; + /** + * Constructor with specified DecodeParms dictionary + * + * @param is InputStream to be inflated + * @param parms DecodeParms dictionary. May be null, in which case this is equivalent to the + * one-parameter constructor. + */ + public PdfFlateInputStream(InputStream is, PdfDictionary parms) { + super(is); + iis = new InflaterInputStream(is); + /* Set default values */ + predictor = 1; // no prediction + columns = 1; + bpc = 8; + colors = 1; - iisBuf = new byte [IISBUF_SIZE]; - iisBufLen = 0; - iisBufOff = 0; - iisEof = false; - eof = false; - if (parms != null) { - /* Extract relevant dictionary defs */ - try { - PdfSimpleObject pred = (PdfSimpleObject) parms.get ("Predictor"); - if (pred != null) { - predictor = pred.getIntValue(); - } - } - catch (Exception e) {} - try { - PdfSimpleObject col = (PdfSimpleObject) parms.get ("Columns"); - if (col != null) { - columns = col.getIntValue(); - } - } - catch (Exception e) {} - try { - PdfSimpleObject bitsper = (PdfSimpleObject) parms.get ("BitsPerComponent"); - if (bitsper != null) { - bpc = bitsper.getIntValue(); - } - } - catch (Exception e) {} + iisBuf = new byte[IISBUF_SIZE]; + iisBufLen = 0; + iisBufOff = 0; + iisEof = false; + eof = false; + if (parms != null) { + /* Extract relevant dictionary defs */ + try { + PdfSimpleObject pred = (PdfSimpleObject) parms.get("Predictor"); + if (pred != null) { + predictor = pred.getIntValue(); } - /* Calculate byte counts */ - if (predictor != 1) { - colBytes = (colors * bpc + 7) / 8; - rowLen = (columns * colors * bpc + 7) / 8 + colBytes; - rowBuf = new byte[rowLen]; - rowBuf2 = new byte[rowLen]; - rowBufOff = rowLen; + } catch (Exception e) { + } + try { + PdfSimpleObject col = (PdfSimpleObject) parms.get("Columns"); + if (col != null) { + columns = col.getIntValue(); } - } - - /** Reads one byte from the stream. - * Returns -1 if end of file is reached. - * - * @return number of bytes - * @throws IOException - */ - @Override - public int read() throws IOException - { - if (eof) { - return -1; - } - if (predictor == 1) { - return readIISByte (); - } - if (rowBufOff == rowLen) { - // Starting out, or previous row exhausted. - readRow (); - if (eof) { - return -1; - } + } catch (Exception e) { + } + try { + PdfSimpleObject bitsper = (PdfSimpleObject) parms.get("BitsPerComponent"); + if (bitsper != null) { + bpc = bitsper.getIntValue(); } - return rowBuf[rowBufOff++] & 0xFF; + } catch (Exception e) { + } } - - /** * Reads the specified number of bytes into a buffer.Returns the number of bytes actually read, or -1 if - end of file has been reached. - * - * @param b: array of bytes in buffer - * @return int: the number of bytes actually read or -1 if - * end of file has been reached - * @throws IOException - */ - @Override - public int read (byte[] b) throws IOException - { - /* Need to read a byte at a time till we have something to expand */ - return read (b, 0, b.length); + /* Calculate byte counts */ + if (predictor != 1) { + colBytes = (colors * bpc + 7) / 8; + rowLen = (columns * colors * bpc + 7) / 8 + colBytes; + rowBuf = new byte[rowLen]; + rowBuf2 = new byte[rowLen]; + rowBufOff = rowLen; } + } - /** Reads the specified number of bytes into a buffer - * with offset and length specified. - * Returns -1 if end of file has been reached. - * No matter how much is requested, this will only return one - * row's worth of data at most. - * - * @param b: array of bytes of file - * @param off: the specified offset - * @param len: the specified length - * @return number of bytes or -1 if end of file is reached - * @throws IOException - */ - @Override - public int read (byte[] b, int off, int len) throws IOException - { - if (eof) { - return -1; - } - if (predictor == 1) { - // predictor of 1 means no predictor. - //return iis.read (b, off, len); - // That can't be right, can it? - return readIISBytes(b, off, len); - } - if (rowBufOff == rowLen) { - // Starting out, or previous row exhausted. - readRow (); - if (eof) { - return -1; - } - } - if (len > rowLen - rowBufOff) { - /* Return no more than a row's worth, regardless */ - len = rowLen - rowBufOff; - } - for (int i = 0; i < len; i++) { - b[off + i] = rowBuf[rowBufOff++]; - } - return len; + /** + * Reads one byte from the stream. Returns -1 if end of file is reached. + * + * @return number of bytes + * @throws IOException + */ + @Override + public int read() throws IOException { + if (eof) { + return -1; + } + if (predictor == 1) { + return readIISByte(); + } + if (rowBufOff == rowLen) { + // Starting out, or previous row exhausted. + readRow(); + if (eof) { + return -1; + } } + return rowBuf[rowBufOff++] & 0xFF; + } - @Override - public long skip (long n) throws IOException { - return skipIISBytes(n); + /** + * * Reads the specified number of bytes into a buffer.Returns the number of bytes actually read, + * or -1 if end of file has been reached. + * + * @param b: array of bytes in buffer + * @return int: the number of bytes actually read or -1 if end of file has been reached + * @throws IOException + */ + @Override + public int read(byte[] b) throws IOException { + /* Need to read a byte at a time till we have something to expand */ + return read(b, 0, b.length); + } + + /** + * Reads the specified number of bytes into a buffer with offset and length specified. Returns -1 + * if end of file has been reached. No matter how much is requested, this will only return one + * row's worth of data at most. + * + * @param b: array of bytes of file + * @param off: the specified offset + * @param len: the specified length + * @return number of bytes or -1 if end of file is reached + * @throws IOException + */ + @Override + public int read(byte[] b, int off, int len) throws IOException { + if (eof) { + return -1; + } + if (predictor == 1) { + // predictor of 1 means no predictor. + // return iis.read (b, off, len); + // That can't be right, can it? + return readIISBytes(b, off, len); + } + if (rowBufOff == rowLen) { + // Starting out, or previous row exhausted. + readRow(); + if (eof) { + return -1; + } + } + if (len > rowLen - rowBufOff) { + /* Return no more than a row's worth, regardless */ + len = rowLen - rowBufOff; + } + for (int i = 0; i < len; i++) { + b[off + i] = rowBuf[rowBufOff++]; } + return len; + } - /* Reads a row's worth of data and stores in rowBuf. */ - private void readRow () throws IOException - { - /* Swap rowBuf and rowBuf2 */ - byte[] r = rowBuf; - rowBuf = rowBuf2; - rowBuf2 = r; - rowBufOff = colBytes; + @Override + public long skip(long n) throws IOException { + return skipIISBytes(n); + } - // Ignore weird predictor of 15 for now - if (predictor >= 10) { - // throw one byte away - readIISByte (); + /* Reads a row's worth of data and stores in rowBuf. */ + private void readRow() throws IOException { + /* Swap rowBuf and rowBuf2 */ + byte[] r = rowBuf; + rowBuf = rowBuf2; + rowBuf2 = r; + rowBufOff = colBytes; + + // Ignore weird predictor of 15 for now + if (predictor >= 10) { + // throw one byte away + readIISByte(); + } + int off = colBytes; + while (off < rowLen) { + int n = readIISBytes(rowBuf, off, rowLen - off); + if (n > 0) { + off += n; + } else { + eof = true; + return; + } + } + switch (predictor) { + case 1: + case 10: + break; + case 2: + // TIFF predictor + case 11: + // Sub -- left + for (int i = colBytes; i < rowLen; i++) { + rowBuf[i] += rowBuf[i - colBytes]; } - int off = colBytes; - while (off < rowLen) { - int n = readIISBytes(rowBuf, off, rowLen - off); - if (n > 0) { - off += n; - } - else { - eof = true; - return; - } + break; + case 12: + // Up -- above + for (int i = colBytes; i < rowLen; i++) { + rowBuf[i] += rowBuf2[i]; } - switch (predictor) { - case 1: - case 10: - break; - case 2: - // TIFF predictor - case 11: - // Sub -- left - for (int i = colBytes; i < rowLen; i++) { - rowBuf[i] += rowBuf[i - colBytes]; - } - break; - case 12: - // Up -- above - for (int i = colBytes; i < rowLen; i++) { - rowBuf[i] += rowBuf2[i]; - } - break; - case 13: - // Average -- (left + above) / 2 - for (int i = colBytes; i < rowLen; i++) { - rowBuf[i] += ((rowBuf[i - colBytes] & 0xFF) + - (rowBuf2[i] & 0xFF)) / 2; - } - break; - case 14: // Paeth -- closest of left, above, upper-left - for (int i = colBytes; i < rowLen; i++) { - int a = rowBuf[i - colBytes] & 0xFF; - int b = rowBuf2[i] & 0xFF; - int c = rowBuf2[i - colBytes] & 0xFF; - int p = a + b - c; - int pa = Math.abs(p - a); - int pb = Math.abs(p - b); - int pc = Math.abs(p - c); + break; + case 13: + // Average -- (left + above) / 2 + for (int i = colBytes; i < rowLen; i++) { + rowBuf[i] += ((rowBuf[i - colBytes] & 0xFF) + (rowBuf2[i] & 0xFF)) / 2; + } + break; + case 14: // Paeth -- closest of left, above, upper-left + for (int i = colBytes; i < rowLen; i++) { + int a = rowBuf[i - colBytes] & 0xFF; + int b = rowBuf2[i] & 0xFF; + int c = rowBuf2[i - colBytes] & 0xFF; + int p = a + b - c; + int pa = Math.abs(p - a); + int pb = Math.abs(p - b); + int pc = Math.abs(p - c); - int val; - if (pa<=pb && pa<=pc) { - val = a; - } - else if (pb<=pc) { - val = b; - } - else { - val = c; - } + int val; + if (pa <= pb && pa <= pc) { + val = a; + } else if (pb <= pc) { + val = b; + } else { + val = c; + } - rowBuf[i] += (byte)val; - } - break; - case 15: // optimum -- per line determination - break; - default: - break; + rowBuf[i] += (byte) val; } + break; + case 15: // optimum -- per line determination + break; + default: + break; } + } - /** Get an "inflated" byte. We do buffering here - * for efficiency. */ - private int readIISByte () - throws IOException - { - // iisBufOff -- offset to next available byte - // iisBuf -- byte array read from IIS - // iisBufLen -- number of bytes of valid data in iisBuf - if (iisBufOff >= iisBufLen && !iisEof) { - readIIS (); - } - if (iisEof) { - return -1; - } - return iisBuf[iisBufOff++] & 0xFF; + /** Get an "inflated" byte. We do buffering here for efficiency. */ + private int readIISByte() throws IOException { + // iisBufOff -- offset to next available byte + // iisBuf -- byte array read from IIS + // iisBufLen -- number of bytes of valid data in iisBuf + if (iisBufOff >= iisBufLen && !iisEof) { + readIIS(); } + if (iisEof) { + return -1; + } + return iisBuf[iisBufOff++] & 0xFF; + } - /** Get a bufferload of bytes. */ - private int readIISBytes (byte[] buf, int off, int len) - throws IOException - { - if (iisBufOff >= iisBufLen && !iisEof) { - readIIS (); - } - if (iisEof) { - return -1; - } - /* We don't attempt to optimize across buffer boundaries */ - if (iisBufLen - iisBufOff < len) { - len = iisBufLen - iisBufOff; - } - for (int i = off; i < off + len; i++) { - buf[i] = iisBuf[iisBufOff++]; - } - return len; + /** Get a bufferload of bytes. */ + private int readIISBytes(byte[] buf, int off, int len) throws IOException { + if (iisBufOff >= iisBufLen && !iisEof) { + readIIS(); } + if (iisEof) { + return -1; + } + /* We don't attempt to optimize across buffer boundaries */ + if (iisBufLen - iisBufOff < len) { + len = iisBufLen - iisBufOff; + } + for (int i = off; i < off + len; i++) { + buf[i] = iisBuf[iisBufOff++]; + } + return len; + } - /** Skip a specified number of bytes. */ - private long skipIISBytes (long n) throws IOException { - if (iisBufOff >= iisBufLen && !iisEof) { - readIIS (); - } - if (iisEof) { - return -1; - } - if (iisBufLen - iisBufOff < n) { - n = iisBufLen - iisBufOff; - } - iisBufOff += n; - return n; + /** Skip a specified number of bytes. */ + private long skipIISBytes(long n) throws IOException { + if (iisBufOff >= iisBufLen && !iisEof) { + readIIS(); } + if (iisEof) { + return -1; + } + if (iisBufLen - iisBufOff < n) { + n = iisBufLen - iisBufOff; + } + iisBufOff += n; + return n; + } - /** Fill up the IIS buffer. Should be called only by - * other IIS buffer-specific routines. */ - private int readIIS () throws IOException - { - if (iisEof) { - return -1; - } - int n = iis.read (iisBuf); - iisBufOff = 0; - iisBufLen = n; - if (n <= 0) { - iisEof = true; - } - return n; + /** Fill up the IIS buffer. Should be called only by other IIS buffer-specific routines. */ + private int readIIS() throws IOException { + if (iisEof) { + return -1; + } + int n = iis.read(iisBuf); + iisBufOff = 0; + iisBufLen = n; + if (n <= 0) { + iisEof = true; } + return n; + } } diff --git a/jhove-modules/pdf-hul/src/main/java/edu/harvard/hul/ois/jhove/module/pdf/PdfHeader.java b/jhove-modules/pdf-hul/src/main/java/edu/harvard/hul/ois/jhove/module/pdf/PdfHeader.java index 80f57b345..a5354aca7 100644 --- a/jhove-modules/pdf-hul/src/main/java/edu/harvard/hul/ois/jhove/module/pdf/PdfHeader.java +++ b/jhove-modules/pdf-hul/src/main/java/edu/harvard/hul/ois/jhove/module/pdf/PdfHeader.java @@ -3,189 +3,170 @@ import java.io.IOException; /** - * Simple class that is the a prototype of a proper header parser class. The aim - * was to introduce a simple version check for the PDF/A minor version number, - * see {@link PdfHeader#isVersionValid()}, while not changing anything else - * through over ambition. + * Simple class that is the a prototype of a proper header parser class. The aim was to introduce a + * simple version check for the PDF/A minor version number, see {@link PdfHeader#isVersionValid()}, + * while not changing anything else through over ambition. * - * @author Carl Wilson - * carlwilson AT github + * @author Carl Wilson carlwilson AT github * @version 0.1 Created 8 Mar 2018:00:46:39 */ - public final class PdfHeader { - public static final String PDF_VER1_HEADER_PREFIX = "PDF-1."; //$NON-NLS-1$ - public static final String PDF_SIG_HEADER = "%" + PDF_VER1_HEADER_PREFIX; //$NON-NLS-1$ - public static final String POSTSCRIPT_HEADER_PREFIX = "!PS-Adobe-"; //$NON-NLS-1$ - public static final int MAX_VALID_MAJOR_VERSION = 7; - - private final String versionString; - private final boolean isPdfACompilant; - - /** - * - */ - private PdfHeader(final String versionString, - final boolean isPdfaCompliant) { - this.versionString = versionString; - this.isPdfACompilant = isPdfaCompliant; - } - - /** - * @return the version string parsed from the PDF Header - */ - public String getVersionString() { - return this.versionString; - } - - /** - * @return true if the header is considered PDF/A compliant, otherwise false - */ - public boolean isPdfACompliant() { - return this.isPdfACompilant; - } - - /** - * Performs a very simple version number validity check. Given version - * number is a String of form 1.x, x is the minor version number. This - * method parses the minor version number from the version String and tests - * whether it is less than or equal to - * {@link PdfHeader#MAX_VALID_MAJOR_VERSION}. - * - * @return true if an integer minor version number can be parsed from the - * version string AND it is less than or equal to - * {@link PdfHeader#MAX_VALID_MAJOR_VERSION}. Otherwise false. - */ - public boolean isVersionValid() { - // Set minor version to one larger than maximum so invalid if parse - // fails - int minorVersion = MAX_VALID_MAJOR_VERSION + 1; - try { - minorVersion = getMinorVersion(this.versionString); - } catch (NumberFormatException nfe) { - // TODO : This currently catches non-numbers and - // returns false. This marks the version number - // as invalid and ensured existing JHOVE behaviour - // changed as little as possible for v1.20 March 2018. - // Really this should be thrown as it's own validation - // exception and be assigned its own message - // Version numbers need better handling as PDF1. is - // baked into JHOVE's header signature rather than - // as part of version parsing and validation. - // The arrival of PDF 2.0 in summer 2017 leaves - // this looking very dubious behaviour. - } - return minorVersion <= MAX_VALID_MAJOR_VERSION; - } - - /** - * Creates a new {@link PdfHeader} instance using the passed parameters. - * - * @param versionString - * the version number from the PDF Header, should be of form - * 1.x where x should be of the range 0-7. - * @param isPdfaCompliant - * boolean flag indicating if the PDF/A is compliant or non - * compliant with JHOVE's PDF/A profile. - * @return a {@link PdfHeader} instance initialised using - * versionString and isPdfaCompliant. - * @throws NullPointerException - * when parameter versionString is null. - */ - static PdfHeader fromValues(final String versionString, - final boolean isPdfaCompliant) { - if (versionString == null) - throw new NullPointerException( - "Parameter versionString can not be null."); - return new PdfHeader(versionString, isPdfaCompliant); - } - - /** - * Factory method for {@link PdfHeader} that parses a new instance using the - * supplied {@link Parser} instance. - * - * @param parser - * the {@link Parser} instance that will be used to parse header - * details - * @return a new {@link PdfHeader} instance derived using the supplied - * {@link Parser} or null when no header could be found - * and parsed. - */ - public static PdfHeader parseHeader(final Parser parser) { - Token token = null; - String value = null; - boolean isPdfACompliant = false; - String version = null; - - /* Parse file header. */ - for (;;) { - if (parser.getOffset() > 1024) { - return null; - } - try { - token = null; - token = parser.getNext(1024L); - } catch (IOException ee) { - return null; - } catch (Exception e) { - // fall through - } - - if (token == null) { - return null; - } - if (token instanceof Comment) { - value = ((Comment) token).getValue(); - if (value.indexOf(PDF_VER1_HEADER_PREFIX) == 0) { - version = value.substring(4, 7); - isPdfACompliant = true; - break; - } - // The implementation notes (though not the spec) - // allow an alternative signature of %!PS-Adobe-N.n PDF-M.m - if (value.indexOf(POSTSCRIPT_HEADER_PREFIX) == 0) { - // But be careful: that much by itself is the standard - // PostScript signature. - int n = value.indexOf(PDF_VER1_HEADER_PREFIX); - if (n >= 11) { - version = value.substring(n + 4); - break; - } - } - } - } - - if (version == null) { - return null; - } - - try { - isPdfACompliant = isTokenPdfACompliant(parser.getNext()); - } catch (Exception excep) { - // Most likely a ClassCastException on a non-comment - isPdfACompliant = false; - } - // Check for PDF/A conformance. The next item must be - // a comment with four characters, each greater than 127 - return new PdfHeader(version, isPdfACompliant); - } - - private static int getMinorVersion(final String version) { - double doubleVer = Double.parseDouble(version); - double fractPart = doubleVer % 1; - int minor = (int) (10L * fractPart); - return minor; - } - - private static boolean isTokenPdfACompliant(final Token token) { - String cmt = ((Comment) token).getValue(); - char[] cmtArray = cmt.toCharArray(); - int ctlcnt = 0; - for (int i = 0; i < 4; i++) { - if (cmtArray[i] > 127) { - ctlcnt++; - } - } - return (ctlcnt > 3); - } + public static final String PDF_VER1_HEADER_PREFIX = "PDF-1."; // $NON-NLS-1$ + public static final String PDF_SIG_HEADER = "%" + PDF_VER1_HEADER_PREFIX; // $NON-NLS-1$ + public static final String POSTSCRIPT_HEADER_PREFIX = "!PS-Adobe-"; // $NON-NLS-1$ + public static final int MAX_VALID_MAJOR_VERSION = 7; + + private final String versionString; + private final boolean isPdfACompilant; + + /** */ + private PdfHeader(final String versionString, final boolean isPdfaCompliant) { + this.versionString = versionString; + this.isPdfACompilant = isPdfaCompliant; + } + + /** @return the version string parsed from the PDF Header */ + public String getVersionString() { + return this.versionString; + } + + /** @return true if the header is considered PDF/A compliant, otherwise false */ + public boolean isPdfACompliant() { + return this.isPdfACompilant; + } + + /** + * Performs a very simple version number validity check. Given version number is a String of form + * 1.x, x is the minor version number. This method parses the minor version number from the + * version String and tests whether it is less than or equal to {@link + * PdfHeader#MAX_VALID_MAJOR_VERSION}. + * + * @return true if an integer minor version number can be parsed from the version string AND it is + * less than or equal to {@link PdfHeader#MAX_VALID_MAJOR_VERSION}. Otherwise false. + */ + public boolean isVersionValid() { + // Set minor version to one larger than maximum so invalid if parse + // fails + int minorVersion = MAX_VALID_MAJOR_VERSION + 1; + try { + minorVersion = getMinorVersion(this.versionString); + } catch (NumberFormatException nfe) { + // TODO : This currently catches non-numbers and + // returns false. This marks the version number + // as invalid and ensured existing JHOVE behaviour + // changed as little as possible for v1.20 March 2018. + // Really this should be thrown as it's own validation + // exception and be assigned its own message + // Version numbers need better handling as PDF1. is + // baked into JHOVE's header signature rather than + // as part of version parsing and validation. + // The arrival of PDF 2.0 in summer 2017 leaves + // this looking very dubious behaviour. + } + return minorVersion <= MAX_VALID_MAJOR_VERSION; + } + + /** + * Creates a new {@link PdfHeader} instance using the passed parameters. + * + * @param versionString the version number from the PDF Header, should be of form 1.x + * where x should be of the range 0-7. + * @param isPdfaCompliant boolean flag indicating if the PDF/A is compliant or non compliant with + * JHOVE's PDF/A profile. + * @return a {@link PdfHeader} instance initialised using versionString and + * isPdfaCompliant. + * @throws NullPointerException when parameter versionString is null. + */ + static PdfHeader fromValues(final String versionString, final boolean isPdfaCompliant) { + if (versionString == null) + throw new NullPointerException("Parameter versionString can not be null."); + return new PdfHeader(versionString, isPdfaCompliant); + } + + /** + * Factory method for {@link PdfHeader} that parses a new instance using the supplied {@link + * Parser} instance. + * + * @param parser the {@link Parser} instance that will be used to parse header details + * @return a new {@link PdfHeader} instance derived using the supplied {@link Parser} or + * null when no header could be found and parsed. + */ + public static PdfHeader parseHeader(final Parser parser) { + Token token = null; + String value = null; + boolean isPdfACompliant = false; + String version = null; + + /* Parse file header. */ + for (; ; ) { + if (parser.getOffset() > 1024) { + return null; + } + try { + token = null; + token = parser.getNext(1024L); + } catch (IOException ee) { + return null; + } catch (Exception e) { + // fall through + } + + if (token == null) { + return null; + } + if (token instanceof Comment) { + value = ((Comment) token).getValue(); + if (value.indexOf(PDF_VER1_HEADER_PREFIX) == 0) { + version = value.substring(4, 7); + isPdfACompliant = true; + break; + } + // The implementation notes (though not the spec) + // allow an alternative signature of %!PS-Adobe-N.n PDF-M.m + if (value.indexOf(POSTSCRIPT_HEADER_PREFIX) == 0) { + // But be careful: that much by itself is the standard + // PostScript signature. + int n = value.indexOf(PDF_VER1_HEADER_PREFIX); + if (n >= 11) { + version = value.substring(n + 4); + break; + } + } + } + } + + if (version == null) { + return null; + } + + try { + isPdfACompliant = isTokenPdfACompliant(parser.getNext()); + } catch (Exception excep) { + // Most likely a ClassCastException on a non-comment + isPdfACompliant = false; + } + // Check for PDF/A conformance. The next item must be + // a comment with four characters, each greater than 127 + return new PdfHeader(version, isPdfACompliant); + } + + private static int getMinorVersion(final String version) { + double doubleVer = Double.parseDouble(version); + double fractPart = doubleVer % 1; + int minor = (int) (10L * fractPart); + return minor; + } + + private static boolean isTokenPdfACompliant(final Token token) { + String cmt = ((Comment) token).getValue(); + char[] cmtArray = cmt.toCharArray(); + int ctlcnt = 0; + for (int i = 0; i < 4; i++) { + if (cmtArray[i] > 127) { + ctlcnt++; + } + } + return (ctlcnt > 3); + } } diff --git a/jhove-modules/pdf-hul/src/main/java/edu/harvard/hul/ois/jhove/module/pdf/PdfIndirectObj.java b/jhove-modules/pdf-hul/src/main/java/edu/harvard/hul/ois/jhove/module/pdf/PdfIndirectObj.java index de2e8394c..27f6a159b 100644 --- a/jhove-modules/pdf-hul/src/main/java/edu/harvard/hul/ois/jhove/module/pdf/PdfIndirectObj.java +++ b/jhove-modules/pdf-hul/src/main/java/edu/harvard/hul/ois/jhove/module/pdf/PdfIndirectObj.java @@ -1,48 +1,41 @@ -/********************************************************************** - * Jhove - JSTOR/Harvard Object Validation Environment - * Copyright 2003 by JSTOR and the President and Fellows of Harvard College - **********************************************************************/ - +/** + * ******************************************************************** Jhove - JSTOR/Harvard Object + * Validation Environment Copyright 2003 by JSTOR and the President and Fellows of Harvard College + * ******************************************************************** + */ package edu.harvard.hul.ois.jhove.module.pdf; import java.util.*; -/** - * A representation of a PDF indirect object reference. - */ -public class PdfIndirectObj extends PdfObject -{ +/** A representation of a PDF indirect object reference. */ +public class PdfIndirectObj extends PdfObject { - private Map _objectMap; - private PdfObject _cachedObject; + private Map _objectMap; + private PdfObject _cachedObject; - /** - * Creates a PdfIndirectObj object. - * - * @param objNumber The PDF object number - * @param genNumber The PDF generation number - * @param objectMap The object map for the PDF file - */ - public PdfIndirectObj (int objNumber, int genNumber, Map objectMap) - { - super (objNumber, genNumber); - _objectMap = objectMap; - _cachedObject = null; - } + /** + * Creates a PdfIndirectObj object. + * + * @param objNumber The PDF object number + * @param genNumber The PDF generation number + * @param objectMap The object map for the PDF file + */ + public PdfIndirectObj(int objNumber, int genNumber, Map objectMap) { + super(objNumber, genNumber); + _objectMap = objectMap; + _cachedObject = null; + } - /** - * Retrieves the object which is referenced. Uses the - * cached reference if there is one; caches the reference - * if there wasn't one before. - */ - public PdfObject getObject () - { - if (_cachedObject != null) { - return _cachedObject; - } - long key = ((long) _objNumber << 32) + - (_genNumber & 0XFFFFFFFFL); - _cachedObject = _objectMap.get (new Long (key)); - return _cachedObject; + /** + * Retrieves the object which is referenced. Uses the cached reference if there is one; caches the + * reference if there wasn't one before. + */ + public PdfObject getObject() { + if (_cachedObject != null) { + return _cachedObject; } + long key = ((long) _objNumber << 32) + (_genNumber & 0XFFFFFFFFL); + _cachedObject = _objectMap.get(new Long(key)); + return _cachedObject; + } } diff --git a/jhove-modules/pdf-hul/src/main/java/edu/harvard/hul/ois/jhove/module/pdf/PdfInvalidException.java b/jhove-modules/pdf-hul/src/main/java/edu/harvard/hul/ois/jhove/module/pdf/PdfInvalidException.java index 1c292cb5e..be4b05db0 100644 --- a/jhove-modules/pdf-hul/src/main/java/edu/harvard/hul/ois/jhove/module/pdf/PdfInvalidException.java +++ b/jhove-modules/pdf-hul/src/main/java/edu/harvard/hul/ois/jhove/module/pdf/PdfInvalidException.java @@ -1,54 +1,42 @@ -/********************************************************************** - * Jhove - JSTOR/Harvard Object Validation Environment - * Copyright 2003 by JSTOR and the President and Fellows of Harvard College - **********************************************************************/ - +/** + * ******************************************************************** Jhove - JSTOR/Harvard Object + * Validation Environment Copyright 2003 by JSTOR and the President and Fellows of Harvard College + * ******************************************************************** + */ package edu.harvard.hul.ois.jhove.module.pdf; import edu.harvard.hul.ois.jhove.RepInfo; import edu.harvard.hul.ois.jhove.messages.JhoveMessage; /** - * Exception subclass used internally by the PDF module. - * A PdfInvalidException is thrown when a condition indicates - * that the document is invalid but not necessarily ill-formed. + * Exception subclass used internally by the PDF module. A PdfInvalidException is thrown when a + * condition indicates that the document is invalid but not necessarily ill-formed. */ public final class PdfInvalidException extends PdfException { - /** - * - */ - private static final long serialVersionUID = -3224356746816316033L; - - /** - * Creates a PdfInvalidException. - */ - public PdfInvalidException(final JhoveMessage message) { - super(message); - } - - /** - * Creates a PdfInvalidException with specified offset. - */ - public PdfInvalidException(final JhoveMessage message, final long offset) { - super(message, offset); - } - - - /** - * Creates a PdfInvalidException with specified offset and token. - */ - public PdfInvalidException(final JhoveMessage message, final long offset, - final Token token) { - super(message, offset, token); - } - - /** - * Performs the appropriate disparagement act on a RepInfo - * object. For a PdfInvalidException, this is to call - * setValid (false). - */ - @Override - public void disparage(final RepInfo info) { - info.setValid(false); - } + /** */ + private static final long serialVersionUID = -3224356746816316033L; + + /** Creates a PdfInvalidException. */ + public PdfInvalidException(final JhoveMessage message) { + super(message); + } + + /** Creates a PdfInvalidException with specified offset. */ + public PdfInvalidException(final JhoveMessage message, final long offset) { + super(message, offset); + } + + /** Creates a PdfInvalidException with specified offset and token. */ + public PdfInvalidException(final JhoveMessage message, final long offset, final Token token) { + super(message, offset, token); + } + + /** + * Performs the appropriate disparagement act on a RepInfo object. For a PdfInvalidException, this + * is to call setValid (false). + */ + @Override + public void disparage(final RepInfo info) { + info.setValid(false); + } } diff --git a/jhove-modules/pdf-hul/src/main/java/edu/harvard/hul/ois/jhove/module/pdf/PdfMalformedException.java b/jhove-modules/pdf-hul/src/main/java/edu/harvard/hul/ois/jhove/module/pdf/PdfMalformedException.java index a7ad23cfc..2db827019 100644 --- a/jhove-modules/pdf-hul/src/main/java/edu/harvard/hul/ois/jhove/module/pdf/PdfMalformedException.java +++ b/jhove-modules/pdf-hul/src/main/java/edu/harvard/hul/ois/jhove/module/pdf/PdfMalformedException.java @@ -1,58 +1,43 @@ -/********************************************************************** - * Jhove - JSTOR/Harvard Object Validation Environment - * Copyright 2004 by JSTOR and the President and Fellows of Harvard College - **********************************************************************/ - +/** + * ******************************************************************** Jhove - JSTOR/Harvard Object + * Validation Environment Copyright 2004 by JSTOR and the President and Fellows of Harvard College + * ******************************************************************** + */ package edu.harvard.hul.ois.jhove.module.pdf; import edu.harvard.hul.ois.jhove.RepInfo; import edu.harvard.hul.ois.jhove.messages.JhoveMessage; /** - * Exception subclass used internally by the PDF module. - * A PdfMalformedException is thrown when a condition indicates - * that the document is not well-formed. - * - * @see PdfInvalidException + * Exception subclass used internally by the PDF module. A PdfMalformedException is thrown when a + * condition indicates that the document is not well-formed. + * + * @see PdfInvalidException */ @SuppressWarnings("serial") public class PdfMalformedException extends PdfException { - /** - * Creates a PdfMalformedException. - */ - public PdfMalformedException (final JhoveMessage message) - { - super(message); - } - - - /** - * Creates a PdfMalformedException with specified offset. - */ - public PdfMalformedException (final JhoveMessage message, final long offset) - { - super(message, offset); - } - - - /** - * Creates a PdfMalformedException with specified offset and token. - */ - public PdfMalformedException (final JhoveMessage message, final long offset, final Token token) - { - super(message, offset, token); - } - - /** - * Performs the appropriate disparagement act on a RepInfo - * object. For a PdfInvalidException, this is to call - * setValid (false). - */ - @Override - public void disparage (final RepInfo info) - { - info.setWellFormed (false); - } - + /** Creates a PdfMalformedException. */ + public PdfMalformedException(final JhoveMessage message) { + super(message); + } + + /** Creates a PdfMalformedException with specified offset. */ + public PdfMalformedException(final JhoveMessage message, final long offset) { + super(message, offset); + } + + /** Creates a PdfMalformedException with specified offset and token. */ + public PdfMalformedException(final JhoveMessage message, final long offset, final Token token) { + super(message, offset, token); + } + + /** + * Performs the appropriate disparagement act on a RepInfo object. For a PdfInvalidException, this + * is to call setValid (false). + */ + @Override + public void disparage(final RepInfo info) { + info.setWellFormed(false); + } } diff --git a/jhove-modules/pdf-hul/src/main/java/edu/harvard/hul/ois/jhove/module/pdf/PdfObject.java b/jhove-modules/pdf-hul/src/main/java/edu/harvard/hul/ois/jhove/module/pdf/PdfObject.java index b21eca8ff..cabf3ae15 100644 --- a/jhove-modules/pdf-hul/src/main/java/edu/harvard/hul/ois/jhove/module/pdf/PdfObject.java +++ b/jhove-modules/pdf-hul/src/main/java/edu/harvard/hul/ois/jhove/module/pdf/PdfObject.java @@ -1,77 +1,64 @@ -/********************************************************************** - * Jhove - JSTOR/Harvard Object Validation Environment - * Copyright 2003 by JSTOR and the President and Fellows of Harvard College - **********************************************************************/ - +/** + * ******************************************************************** Jhove - JSTOR/Harvard Object + * Validation Environment Copyright 2003 by JSTOR and the President and Fellows of Harvard College + * ******************************************************************** + */ package edu.harvard.hul.ois.jhove.module.pdf; /** - * The abstract superclass for all representations of objects - * in PDF files. Objects may be created using the obj syntax, - * in which case they have an object and generation number, or - * they may be parts of other objects, in which case they don't. - * All subclasses should implement a constructor - * which has the object and generation - * numbers as its last two arguments, and one which has the - * same arguments except for omitting these two. + * The abstract superclass for all representations of objects in PDF files. Objects may be created + * using the obj syntax, in which case they have an object and generation number, or they may be + * parts of other objects, in which case they don't. All subclasses should implement a constructor + * which has the object and generation numbers as its last two arguments, and one which has the same + * arguments except for omitting these two. */ -public abstract class PdfObject -{ - /** PDF object number. */ - protected int _objNumber; - - /** PDF generation number. */ - protected int _genNumber; +public abstract class PdfObject { + /** PDF object number. */ + protected int _objNumber; + + /** PDF generation number. */ + protected int _genNumber; + + /** + * Superclass constructor which should be called for all PdfObject instances that include an + * object and generation number. + * + * @param objNumber The PDF object number + * @param genNumber The PDF generation number + */ + public PdfObject(int objNumber, int genNumber) { + _objNumber = objNumber; + _genNumber = genNumber; + } - /** - * Superclass constructor which should be called for all - * PdfObject instances that include an object and generation - * number. - * - * @param objNumber The PDF object number - * @param genNumber The PDF generation number - */ - public PdfObject (int objNumber, int genNumber) - { - _objNumber = objNumber; - _genNumber = genNumber; - } + /** + * Superclass constructor for which the object and generation number will be added separately or + * not at all. Initializes the object and generation numbers to -1 to signify their absence. + */ + public PdfObject() { + _objNumber = -1; + _genNumber = -1; + } - /** - * Superclass constructor for which the object and generation - * number will be added separately or not at all. Initializes - * the object and generation numbers to -1 to signify their - * absence. - */ - public PdfObject () - { - _objNumber = -1; - _genNumber = -1; - } + /** Returns the PDF object number. If the object wasn't given an object number, returns -1. */ + public int getObjNumber() { + return _objNumber; + } - /** Returns the PDF object number. If the object wasn't - given an object number, returns -1. */ - public int getObjNumber () - { - return _objNumber; - } + /** + * Returns the PDF generation number. If the object wasn't given a generation number, returns -1. + */ + public int getGenNumber() { + return _genNumber; + } - /** Returns the PDF generation number. If the object wasn't - given a generation number, returns -1. */ - public int getGenNumber () - { - return _genNumber; - } - - /** Sets the PDF object number. */ - public void setObjNumber (int num) - { - _objNumber = num; - } + /** Sets the PDF object number. */ + public void setObjNumber(int num) { + _objNumber = num; + } - /** Sets the PDF generation number. */ - public void setGenNumber (int num) - { - _genNumber = num; - } + /** Sets the PDF generation number. */ + public void setGenNumber(int num) { + _genNumber = num; + } } diff --git a/jhove-modules/pdf-hul/src/main/java/edu/harvard/hul/ois/jhove/module/pdf/PdfProfile.java b/jhove-modules/pdf-hul/src/main/java/edu/harvard/hul/ois/jhove/module/pdf/PdfProfile.java index 1526ebd5f..82549c588 100644 --- a/jhove-modules/pdf-hul/src/main/java/edu/harvard/hul/ois/jhove/module/pdf/PdfProfile.java +++ b/jhove-modules/pdf-hul/src/main/java/edu/harvard/hul/ois/jhove/module/pdf/PdfProfile.java @@ -1,196 +1,168 @@ -/********************************************************************** - * Jhove - JSTOR/Harvard Object Validation Environment - * Copyright 2003 by JSTOR and the President and Fellows of Harvard College - **********************************************************************/ - +/** + * ******************************************************************** Jhove - JSTOR/Harvard Object + * Validation Environment Copyright 2003 by JSTOR and the President and Fellows of Harvard College + * ******************************************************************** + */ package edu.harvard.hul.ois.jhove.module.pdf; import edu.harvard.hul.ois.jhove.module.*; import java.io.*; import java.util.*; -/** - * Abstract class for PDF profile checkers. - */ -public abstract class PdfProfile -{ - /****************************************************************** - * PRIVATE CLASS FIELDS. - ******************************************************************/ - - /** The module invoking this profile. */ - protected PdfModule _module; - - /** A brief human-readable description of the profile. */ - protected String _profileText; - - /** The Parser being used on the file. */ - protected Parser _parser; - - /** The file being analyzed. */ - protected RandomAccessFile _raf; - - /** Set to true if this file has previously - * been validated by an invocation of this PdfProfile. */ - private boolean _alreadyOK; +/** Abstract class for PDF profile checkers. */ +public abstract class PdfProfile { + /** + * **************************************************************** PRIVATE CLASS FIELDS. + * **************************************************************** + */ - /** - * Creates a PdfProfile. - * Subclass constructors should call the super constructor, - * then assign a value to _profileText. - * - * @param module The PDFModule we're working under - * - */ - public PdfProfile (PdfModule module) - { - _module = module; - } + /** The module invoking this profile. */ + protected PdfModule _module; - /** - * Returns the value of the alreadyOK flag. - * This flag when one profile depends on another, to save redundant - * checking. - * The alreadyOK flag is set whenever satisfiesProfile - * returns true. - */ - public boolean isAlreadyOK () - { - return _alreadyOK; - } + /** A brief human-readable description of the profile. */ + protected String _profileText; + /** The Parser being used on the file. */ + protected Parser _parser; - /** - * Returns true if the document satisfies the profile. - * This calls satisfiesThisProfile(), which does the actual work. - * - * @param raf The RandomAccessFile being parsed - * @param parser The Parser being used on the file - */ - public final boolean satisfiesProfile - (RandomAccessFile raf, Parser parser) - { - _raf = raf; - _parser = parser; - _alreadyOK = false; - boolean sp = satisfiesThisProfile (); - if (sp) { - _alreadyOK = true; - } - return sp; - } + /** The file being analyzed. */ + protected RandomAccessFile _raf; - /** - * Returns true if the document satisfies the - * profile. Subclasses should override satisfiesThisProfile(), - * not satisfiesProfile(), as - * satisfiesProfile() does some - * additional bookkeeping for all subclases. - */ - public abstract boolean satisfiesThisProfile (); + /** + * Set to true if this file has previously been validated by an invocation of this + * PdfProfile. + */ + private boolean _alreadyOK; + /** + * Creates a PdfProfile. Subclass constructors should call the super constructor, then assign a + * value to _profileText. + * + * @param module The PDFModule we're working under + */ + public PdfProfile(PdfModule module) { + _module = module; + } + /** + * Returns the value of the alreadyOK flag. This flag when one profile depends on another, to save + * redundant checking. The alreadyOK flag is set whenever satisfiesProfile returns true + * . + */ + public boolean isAlreadyOK() { + return _alreadyOK; + } - /** - * Returns the text which describes this profile. - */ - public String getText () - { - return _profileText; + /** + * Returns true if the document satisfies the profile. This calls + * satisfiesThisProfile(), which does the actual work. + * + * @param raf The RandomAccessFile being parsed + * @param parser The Parser being used on the file + */ + public final boolean satisfiesProfile(RandomAccessFile raf, Parser parser) { + _raf = raf; + _parser = parser; + _alreadyOK = false; + boolean sp = satisfiesThisProfile(); + if (sp) { + _alreadyOK = true; } + return sp; + } - /** Returns true if a Filter object contains a filter name which - * matches any of the Strings in the second argument. - * Will return false if a PdfException is thrown due - * to an unexpected data type. - * - * (Note 24-Feb-04: This was returning false if any filter matched, - * but that's contrary to both the sense conveyed by the name and - * the way it's being called. Was there a reason it was that way?) - * - * @param filter A PdfObject which may be either a PdfSimpleObject - * encapsulating a Name, or a PdfArray of such objects. - * If a null value is passed, it doesn't match any filter, - * so false is returned. - * @param names An array of Strings naming the filters which should - * precipitate a true result - */ - protected boolean hasFilters (PdfObject filter, String[] names) - { - String filterName; - try { - if (filter == null) { - return false; - } - if (filter instanceof PdfSimpleObject) { - // Name of just one filter - filterName = ((PdfSimpleObject) filter).getStringValue (); - for (int j = 0; j < names.length; j++) { - if (names[j].equals (filterName)) { - return true; - } - } - } - else { - // If it's not a name, it must be an array - Vector filterVec = ((PdfArray) filter).getContent (); - for (int i = 0; i < filterVec.size (); i++) { - PdfSimpleObject filt = - (PdfSimpleObject) filterVec.elementAt (i); - filterName = filt.getStringValue (); - for (int j = 0; j < names.length; j++) { - if (names[j].equals (filterName)) { - return true; - } - } - } - } - } - catch (Exception e) { - return false; - } - return false; // none of the filters were found + /** + * Returns true if the document satisfies the profile. Subclasses should override + * satisfiesThisProfile(), not satisfiesProfile(), as + * satisfiesProfile() does some additional bookkeeping for all subclases. + */ + public abstract boolean satisfiesThisProfile(); - } + /** Returns the text which describes this profile. */ + public String getText() { + return _profileText; + } - /** - * This checks the "XObjects" dictionary, which is a dictionary whose - * entries have values that are XObjects. Override xObjectOK to - * implement profile-specific behavior. - */ - protected boolean xObjectsOK (PdfDictionary xos) - { - if (xos == null) { - return true; // nothing to fail + /** + * Returns true if a Filter object contains a filter name which matches any of the + * Strings in the second argument. Will return false if a PdfException is thrown due to an + * unexpected data type. + * + *

(Note 24-Feb-04: This was returning false if any filter matched, but that's contrary to both + * the sense conveyed by the name and the way it's being called. Was there a reason it was that + * way?) + * + * @param filter A PdfObject which may be either a PdfSimpleObject encapsulating a Name, or a + * PdfArray of such objects. If a null value is passed, it doesn't match any filter, so + * false is returned. + * @param names An array of Strings naming the filters which should precipitate a true result + */ + protected boolean hasFilters(PdfObject filter, String[] names) { + String filterName; + try { + if (filter == null) { + return false; + } + if (filter instanceof PdfSimpleObject) { + // Name of just one filter + filterName = ((PdfSimpleObject) filter).getStringValue(); + for (int j = 0; j < names.length; j++) { + if (names[j].equals(filterName)) { + return true; + } } - try { - Iterator iter = xos.iterator (); - while (iter.hasNext ()) { - PdfObject obj = _module.resolveIndirectObject - (iter.next ()); - if (obj instanceof PdfStream) { - obj = ((PdfStream) obj).getDict (); - } - if (obj instanceof PdfDictionary) { - PdfDictionary xobj = (PdfDictionary) obj; - if (!xObjectOK (xobj)) { - return false; - } - } + } else { + // If it's not a name, it must be an array + Vector filterVec = ((PdfArray) filter).getContent(); + for (int i = 0; i < filterVec.size(); i++) { + PdfSimpleObject filt = (PdfSimpleObject) filterVec.elementAt(i); + filterName = filt.getStringValue(); + for (int j = 0; j < names.length; j++) { + if (names[j].equals(filterName)) { + return true; } + } + } + } + } catch (Exception e) { + return false; + } + return false; // none of the filters were found + } + + /** + * This checks the "XObjects" dictionary, which is a dictionary whose entries have values that are + * XObjects. Override xObjectOK to implement profile-specific behavior. + */ + protected boolean xObjectsOK(PdfDictionary xos) { + if (xos == null) { + return true; // nothing to fail + } + try { + Iterator iter = xos.iterator(); + while (iter.hasNext()) { + PdfObject obj = _module.resolveIndirectObject(iter.next()); + if (obj instanceof PdfStream) { + obj = ((PdfStream) obj).getDict(); } - catch (Exception e) { + if (obj instanceof PdfDictionary) { + PdfDictionary xobj = (PdfDictionary) obj; + if (!xObjectOK(xobj)) { return false; + } } - return true; + } + } catch (Exception e) { + return false; } + return true; + } - /** - * Checks a single XObject for xObjectsOK. Always returns true. - * Override to implement tests. - */ - protected boolean xObjectOK (PdfDictionary xo) - { - return true; - } + /** + * Checks a single XObject for xObjectsOK. Always returns true. Override to implement + * tests. + */ + protected boolean xObjectOK(PdfDictionary xo) { + return true; + } } diff --git a/jhove-modules/pdf-hul/src/main/java/edu/harvard/hul/ois/jhove/module/pdf/PdfPseudoObject.java b/jhove-modules/pdf-hul/src/main/java/edu/harvard/hul/ois/jhove/module/pdf/PdfPseudoObject.java index 4af64109f..2be460309 100644 --- a/jhove-modules/pdf-hul/src/main/java/edu/harvard/hul/ois/jhove/module/pdf/PdfPseudoObject.java +++ b/jhove-modules/pdf-hul/src/main/java/edu/harvard/hul/ois/jhove/module/pdf/PdfPseudoObject.java @@ -1,9 +1,7 @@ package edu.harvard.hul.ois.jhove.module.pdf; -/** Pseudo-objects are entities returned by the parser that don't - * corresond to objects, such as dictionary and array enders. */ -public abstract class PdfPseudoObject extends PdfObject { - - - -} +/** + * Pseudo-objects are entities returned by the parser that don't corresond to objects, such as + * dictionary and array enders. + */ +public abstract class PdfPseudoObject extends PdfObject {} diff --git a/jhove-modules/pdf-hul/src/main/java/edu/harvard/hul/ois/jhove/module/pdf/PdfSimpleObject.java b/jhove-modules/pdf-hul/src/main/java/edu/harvard/hul/ois/jhove/module/pdf/PdfSimpleObject.java index 850ce61f7..ed94a4cce 100644 --- a/jhove-modules/pdf-hul/src/main/java/edu/harvard/hul/ois/jhove/module/pdf/PdfSimpleObject.java +++ b/jhove-modules/pdf-hul/src/main/java/edu/harvard/hul/ois/jhove/module/pdf/PdfSimpleObject.java @@ -1,122 +1,100 @@ -/********************************************************************** - * Jhove - JSTOR/Harvard Object Validation Environment - * Copyright 2003 by JSTOR and the President and Fellows of Harvard College - **********************************************************************/ - +/** + * ******************************************************************** Jhove - JSTOR/Harvard Object + * Validation Environment Copyright 2003 by JSTOR and the President and Fellows of Harvard College + * ******************************************************************** + */ package edu.harvard.hul.ois.jhove.module.pdf; import java.util.*; /** - * A representation of a PDF object which can be represented - * by a Token. In some cases, this means that the full - * content of the object isn't stored, because we don't - * (think we) need it for our purposes. + * A representation of a PDF object which can be represented by a Token. In some cases, this means + * that the full content of the object isn't stored, because we don't (think we) need it for our + * purposes. */ -public class PdfSimpleObject extends PdfObject -{ - - private Token _token; - - /** - * Creates a PdfSimpleObject. - * - * @param objNumber The PDF object number - * @param genNumber The PDF generation number - * @param token The Token represented by this object - */ - public PdfSimpleObject (Token token, int objNumber, int genNumber) - { - super (objNumber, genNumber); - _token = token; +public class PdfSimpleObject extends PdfObject { + + private Token _token; + + /** + * Creates a PdfSimpleObject. + * + * @param objNumber The PDF object number + * @param genNumber The PDF generation number + * @param token The Token represented by this object + */ + public PdfSimpleObject(Token token, int objNumber, int genNumber) { + super(objNumber, genNumber); + _token = token; + } + + /** + * Creates a PdfSimpleObject. + * + * @param token The Token represented by this object + */ + public PdfSimpleObject(Token token) { + super(); + _token = token; + } + + /** Returns the token represented by this object. */ + public Token getToken() { + return _token; + } + + /** Return the string value of the token. Returns null if the token is not a StringValuedToken. */ + public String getStringValue() { + if (!(_token instanceof StringValuedToken)) { + return null; } - - - /** - * Creates a PdfSimpleObject. - * - * @param token The Token represented by this object - */ - public PdfSimpleObject (Token token) - { - super (); - _token = token; + return ((StringValuedToken) _token).getValue(); + } + + /** + * Return the raw bytes of the token, as a Vector of Integer objects. Returns null if the token is + * not a StringValuedToken. + */ + public Vector getRawBytes() { + if (!(_token instanceof StringValuedToken)) { + return null; } - - /** - * Returns the token represented by this object. - */ - public Token getToken () - { - return _token; + return ((StringValuedToken) _token).getRawBytes(); + } + + /** + * Return the integer value of the token. Throws a ClassCastException if the token is not a + * Numeric. + */ + public int getIntValue() { + return ((Numeric) _token).getIntegerValue(); + } + + /** + * Return the double value of the token. Throws a ClassCastException if the token is + * not a Numeric. + */ + public double getDoubleValue() { + return ((Numeric) _token).getValue(); + } + + /** + * Return true if the value of the token is the keyword "true", and false otherwise. + */ + public boolean isTrue() { + if (!(_token instanceof Keyword)) { + return false; } - - /** - * Return the string value of the token. Returns - * null if the token is not a StringValuedToken. - */ - public String getStringValue () - { - if (!(_token instanceof StringValuedToken)) { - return null; - } - return ((StringValuedToken) _token).getValue (); + return "true".equals(((Keyword) _token).getValue()); + } + + /** + * Return true if the value of the token is the keyword "false", and false otherwise. + */ + public boolean isFalse() { + if (!(_token instanceof Keyword)) { + return false; } - - - /** - * Return the raw bytes of the token, as a Vector of Integer objects. - * Returns null if the token is not a StringValuedToken. - */ - public Vector getRawBytes () - { - if (!(_token instanceof StringValuedToken)) { - return null; - } - return ((StringValuedToken) _token).getRawBytes (); - } - - - /** - * Return the integer value of the token. Throws a ClassCastException - * if the token is not a Numeric. - */ - public int getIntValue () - { - return ((Numeric) _token).getIntegerValue (); - } - - /** - * Return the double value of the token. Throws a - * ClassCastException if the token is not a Numeric. - */ - public double getDoubleValue () - { - return ((Numeric) _token).getValue (); - } - - /** - * Return true if the value of the token is the keyword - * "true", and false otherwise. - */ - public boolean isTrue () - { - if (!(_token instanceof Keyword)) { - return false; - } - return "true".equals (((Keyword) _token).getValue ()); - } - - /** - * Return true if the value of the token is the keyword - * "false", and false otherwise. - */ - public boolean isFalse () - { - if (!(_token instanceof Keyword)) { - return false; - } - return "false".equals (((Keyword) _token).getValue ()); - } - + return "false".equals(((Keyword) _token).getValue()); + } } diff --git a/jhove-modules/pdf-hul/src/main/java/edu/harvard/hul/ois/jhove/module/pdf/PdfStream.java b/jhove-modules/pdf-hul/src/main/java/edu/harvard/hul/ois/jhove/module/pdf/PdfStream.java index 64755c254..b5607654c 100644 --- a/jhove-modules/pdf-hul/src/main/java/edu/harvard/hul/ois/jhove/module/pdf/PdfStream.java +++ b/jhove-modules/pdf-hul/src/main/java/edu/harvard/hul/ois/jhove/module/pdf/PdfStream.java @@ -1,217 +1,181 @@ -/********************************************************************** - * Jhove - JSTOR/Harvard Object Validation Environment - * Copyright 2003 by JSTOR and the President and Fellows of Harvard College - **********************************************************************/ - +/** + * ******************************************************************** Jhove - JSTOR/Harvard Object + * Validation Environment Copyright 2003 by JSTOR and the President and Fellows of Harvard College + * ******************************************************************** + */ package edu.harvard.hul.ois.jhove.module.pdf; import java.util.*; /** - * A representation of a PDF stream object. - * A PdfStream consists of a dictionary and a stream token. - * By default the content of the stream isn't loaded, but - * it can be loaded when necessary. + * A representation of a PDF stream object. A PdfStream consists of a dictionary and a stream token. + * By default the content of the stream isn't loaded, but it can be loaded when necessary. */ -public class PdfStream extends PdfObject -{ - - private Stream _stream; - private PdfDictionary _dict; - private Filter[] _filters; - private boolean pdfaCompliant; - - /** - * Creates a PdfStream - * - * @param dict A dictionary describing the stream - * @param stream A Stream token - * @param objNumber The PDF object number - * @param genNumber The PDF generation number - */ - public PdfStream (PdfDictionary dict, Stream stream, - int objNumber, int genNumber) - throws PdfException - { - super (objNumber, genNumber); - _stream = stream; - _dict = dict; - pdfaCompliant = true; // assume compliance to start with - extractFilters (); - } - - - /** - * Creates a PdfStream. - * - * @param dict A dictionary describing the stream - * @param stream A Stream token - */ - public PdfStream (PdfDictionary dict, Stream stream) - throws PdfException - { - super (); - _stream = stream; - _dict = dict; - pdfaCompliant = true; // assume compliance to start with - extractFilters (); +public class PdfStream extends PdfObject { + + private Stream _stream; + private PdfDictionary _dict; + private Filter[] _filters; + private boolean pdfaCompliant; + + /** + * Creates a PdfStream + * + * @param dict A dictionary describing the stream + * @param stream A Stream token + * @param objNumber The PDF object number + * @param genNumber The PDF generation number + */ + public PdfStream(PdfDictionary dict, Stream stream, int objNumber, int genNumber) + throws PdfException { + super(objNumber, genNumber); + _stream = stream; + _dict = dict; + pdfaCompliant = true; // assume compliance to start with + extractFilters(); + } + + /** + * Creates a PdfStream. + * + * @param dict A dictionary describing the stream + * @param stream A Stream token + */ + public PdfStream(PdfDictionary dict, Stream stream) throws PdfException { + super(); + _stream = stream; + _dict = dict; + pdfaCompliant = true; // assume compliance to start with + extractFilters(); + } + + /** Returns the stream's dictionary */ + public PdfDictionary getDict() { + return _dict; + } + + /** Returns the stream's Stream portion */ + public Stream getStream() { + return _stream; + } + + /** If the stream is external, returns the file specification for it, otherwise returns null. */ + public String getFileSpecification() throws PdfInvalidException { + PdfObject spec = _dict.get("F"); + if (spec == null) { + return null; } - - - /** - * Returns the stream's dictionary - */ - public PdfDictionary getDict () - { - return _dict; - } - - - /** - * Returns the stream's Stream portion - */ - public Stream getStream () - { - return _stream; - } - - - /** - * If the stream is external, returns the file specification - * for it, otherwise returns null. - */ - public String getFileSpecification () throws PdfInvalidException - { - PdfObject spec = _dict.get ("F"); - if (spec == null) { - return null; - } - pdfaCompliant = false; // not allowed with PDF/A - return FileSpecification.getFileSpecString(spec); + pdfaCompliant = false; // not allowed with PDF/A + return FileSpecification.getFileSpecString(spec); + } + + /** + * Returns true if no PDF/A compliance problems have been found, false if problems have been found + */ + public boolean isPdfaCompliant() { + return pdfaCompliant; + } + + /** + * Returns an array (possibly empty but not null) of the filters for this Stream. The elements of + * the array are Filter objects. + */ + public Filter[] getFilters() { + return _filters; + } + + /** + * Return the name of the filter, if the DecodeParams dictionary is present and has a "Name" + * entry. + */ + public String getFilterName() { + PdfObject decparms = _dict.get("DecodeParams"); + if (decparms instanceof PdfDictionary) { + PdfObject name = ((PdfDictionary) decparms).get("Name"); + if (name instanceof PdfSimpleObject) { + return ((PdfSimpleObject) name).getStringValue(); + } } - - /** Returns true if no PDF/A compliance problems have been found, false if - * problems have been found */ - public boolean isPdfaCompliant () { - return pdfaCompliant; + return null; + } + + /* Constructs the _filters array. */ + private void extractFilters() throws PdfException { + boolean ff = false; + _filters = new Filter[] {}; // default value + PdfObject filter = _dict.get("Filter"); + if (filter == null) { + filter = _dict.get("FFilter"); + if (filter == null) { + return; + } + ff = true; + pdfaCompliant = false; } - - - /** - * Returns an array (possibly empty but not null) of the filters for - * this Stream. The elements of the array are Filter - * objects. - */ - public Filter[] getFilters () - { - return _filters; + PdfObject parms; + if (ff) { + parms = _dict.get("FDecodeParms"); + if (parms != null) { + pdfaCompliant = false; + } + } else { + parms = _dict.get("DecodeParms"); } - - /** - * Return the name of the filter, if the DecodeParams dictionary - * is present and has a "Name" entry. + /* There may be a single filter, which will be a string, + * in which case the parms will be a single dictionary. + * Or there may be an array, in which case the params will + * be an array of dictionaries. The parms are optional, so + * they may also be null. */ - public String getFilterName () - { - PdfObject decparms = _dict.get ("DecodeParams"); - if (decparms instanceof PdfDictionary) { - PdfObject name = ((PdfDictionary) decparms).get ("Name"); - if (name instanceof PdfSimpleObject) { - return ((PdfSimpleObject)name).getStringValue(); - } - } - return null; - } - - - - /* Constructs the _filters array. */ - private void extractFilters () throws PdfException - { - boolean ff = false; - _filters = new Filter[] {}; // default value - PdfObject filter = _dict.get ("Filter"); - if (filter == null) { - filter = _dict.get ("FFilter"); - if (filter == null) { - return; - } - ff = true; - pdfaCompliant = false; - } - PdfObject parms; - if (ff) { - parms = _dict.get ("FDecodeParms"); - if (parms != null) { - pdfaCompliant = false; - } + try { + if (filter instanceof PdfArray) { + Vector vec = ((PdfArray) filter).getContent(); + int size = vec.size(); + Filter[] val = new Filter[size]; + Vector parmVec = null; + if (parms != null) { + parmVec = ((PdfArray) parms).getContent(); } - else { - parms = _dict.get ("DecodeParms"); - } - - /* There may be a single filter, which will be a string, - * in which case the parms will be a single dictionary. - * Or there may be an array, in which case the params will - * be an array of dictionaries. The parms are optional, so - * they may also be null. - */ - try { - if (filter instanceof PdfArray) { - Vector vec = ((PdfArray) filter).getContent(); - int size = vec.size (); - Filter[] val = new Filter[size]; - Vector parmVec = null; - if (parms != null) { - parmVec = ((PdfArray) parms).getContent (); - } - for (int i = 0; i < size; i++) { - PdfSimpleObject f = (PdfSimpleObject) vec.get(i); - val[i] = new Filter (f.getStringValue()); - if (parmVec != null) { - PdfObject parm = parmVec.get(i); - // Parameter may be the null object. - if (parm instanceof PdfSimpleObject) { - PdfSimpleObject sParm = (PdfSimpleObject) parm; - if ("null".equals (sParm.getStringValue ())) { - continue; - } - } - val[i].setDecodeParms((PdfDictionary) parmVec.get(i)); - } - } - _filters = val; - } - else { - /* Only other allowed value is a string */ - Filter[] val = new Filter[1]; - val[0] = new Filter - (((PdfSimpleObject) filter).getStringValue()); - if (parms instanceof PdfDictionary) { - val[0].setDecodeParms((PdfDictionary) parms); - } - _filters = val; + for (int i = 0; i < size; i++) { + PdfSimpleObject f = (PdfSimpleObject) vec.get(i); + val[i] = new Filter(f.getStringValue()); + if (parmVec != null) { + PdfObject parm = parmVec.get(i); + // Parameter may be the null object. + if (parm instanceof PdfSimpleObject) { + PdfSimpleObject sParm = (PdfSimpleObject) parm; + if ("null".equals(sParm.getStringValue())) { + continue; + } } + val[i].setDecodeParms((PdfDictionary) parmVec.get(i)); + } } - catch (Exception e) { - throw new PdfMalformedException (MessageConstants.PDF_HUL_45); // PDF-HUL-45 + _filters = val; + } else { + /* Only other allowed value is a string */ + Filter[] val = new Filter[1]; + val[0] = new Filter(((PdfSimpleObject) filter).getStringValue()); + if (parms instanceof PdfDictionary) { + val[0].setDecodeParms((PdfDictionary) parms); } + _filters = val; + } + } catch (Exception e) { + throw new PdfMalformedException(MessageConstants.PDF_HUL_45); // PDF-HUL-45 } - - /** - * Returns true if this is an image stream. - */ - public boolean isImage () - { - // An image dictionary may not have a type, but must have a subtype - // of Image. - PdfObject subtype = _dict.get ("Subtype"); - if (subtype instanceof PdfSimpleObject) { - String subtypeStr = ((PdfSimpleObject) subtype).getStringValue (); - return ("Image".equals (subtypeStr)); - } - return false; + } + + /** Returns true if this is an image stream. */ + public boolean isImage() { + // An image dictionary may not have a type, but must have a subtype + // of Image. + PdfObject subtype = _dict.get("Subtype"); + if (subtype instanceof PdfSimpleObject) { + String subtypeStr = ((PdfSimpleObject) subtype).getStringValue(); + return ("Image".equals(subtypeStr)); } - + return false; + } } diff --git a/jhove-modules/pdf-hul/src/main/java/edu/harvard/hul/ois/jhove/module/pdf/PdfStrings.java b/jhove-modules/pdf-hul/src/main/java/edu/harvard/hul/ois/jhove/module/pdf/PdfStrings.java index cabd5ed1c..2584e6ab6 100644 --- a/jhove-modules/pdf-hul/src/main/java/edu/harvard/hul/ois/jhove/module/pdf/PdfStrings.java +++ b/jhove-modules/pdf-hul/src/main/java/edu/harvard/hul/ois/jhove/module/pdf/PdfStrings.java @@ -1,103 +1,90 @@ -/********************************************************************** - * Jhove - JSTOR/Harvard Object Validation Environment - * Copyright 2003 by JSTOR and the President and Fellows of Harvard College - **********************************************************************/ - +/** + * ******************************************************************** Jhove - JSTOR/Harvard Object + * Validation Environment Copyright 2003 by JSTOR and the President and Fellows of Harvard College + * ******************************************************************** + */ package edu.harvard.hul.ois.jhove.module.pdf; /** - * A class for holding arrays of informative strings that will go into - * properties of a PDF object. + * A class for holding arrays of informative strings that will go into properties of a PDF object. */ -public class PdfStrings -{ - /** Encryption algorithm strings. */ - public final static String[] ALGORITHM = - { - "Undocumented", - "40-bit RC4 or AES", - "40-bit or greater RC4 or AES", - "Unpublished", - "Document-defined", - "256-bit AES" - }; - - /** Flags for FontDescriptor. In PDF notation, bit 1 - * (not 0) is the low-order bit. - */ - public final static String[] FONTDESCFLAGS = - { - "FixedPitch", // 1 - "Serif", // 2 - "Symbolic", // 3 - "Script", // 4 - "", // 5 - "Nonsymbolic", // 6 - "Italic", // 7 - "", // 8 - "", // 9 - "", // 10 - "", // 11 - "", // 12 - "", // 13 - "", // 14 - "", // 15 - "", // 16 - "AllCap", // 17 - "SmallCap", // 18 - "ForceBold" // 19 - }; +public class PdfStrings { + /** Encryption algorithm strings. */ + public static final String[] ALGORITHM = { + "Undocumented", + "40-bit RC4 or AES", + "40-bit or greater RC4 or AES", + "Unpublished", + "Document-defined", + "256-bit AES" + }; - /** Flags for user access permissions when revision 3 is specified. */ - public final static String[] USERPERMFLAGS3 = - { - "", // 1, reserved - "", // 2, reserved - "Print", // 3 - "Modify", // 4 - "Extract", // 5 - "Add/modify annotations/forms", // 6 - "", // 7 - "", // 8 - "Fill interactive form fields", // 9 - "Extract for accessibility", // 10 - "Assemble", // 11 - "Print high quality" // 12 - }; + /** Flags for FontDescriptor. In PDF notation, bit 1 (not 0) is the low-order bit. */ + public static final String[] FONTDESCFLAGS = { + "FixedPitch", // 1 + "Serif", // 2 + "Symbolic", // 3 + "Script", // 4 + "", // 5 + "Nonsymbolic", // 6 + "Italic", // 7 + "", // 8 + "", // 9 + "", // 10 + "", // 11 + "", // 12 + "", // 13 + "", // 14 + "", // 15 + "", // 16 + "AllCap", // 17 + "SmallCap", // 18 + "ForceBold" // 19 + }; - /** Flags for user access permissions when revision 2 is specified. */ - public final static String[] USERPERMFLAGS2 = - { - "", // 1, reserved - "", // 2, reserved - "Print", // 3 - "Modify", // 4 - "Extract", // 5 - "Add/modify annotations/forms", // 6 - "", // 7 - "", // 8 - "", // 9 - "", // 10 - "", // 11 - "" // 12 - }; + /** Flags for user access permissions when revision 3 is specified. */ + public static final String[] USERPERMFLAGS3 = { + "", // 1, reserved + "", // 2, reserved + "Print", // 3 + "Modify", // 4 + "Extract", // 5 + "Add/modify annotations/forms", // 6 + "", // 7 + "", // 8 + "Fill interactive form fields", // 9 + "Extract for accessibility", // 10 + "Assemble", // 11 + "Print high quality" // 12 + }; - /** Flags for annotations */ - public final static String[] ANNOTATIONFLAGS = - { - "Invisible", // 1 - "Hidden", // 2 - "Print", // 3 - "NoZoom", // 4 - "NoRotate", // 5 - "NoView", // 6 - "ReadOnly" // 7 - }; + /** Flags for user access permissions when revision 2 is specified. */ + public static final String[] USERPERMFLAGS2 = { + "", // 1, reserved + "", // 2, reserved + "Print", // 3 + "Modify", // 4 + "Extract", // 5 + "Add/modify annotations/forms", // 6 + "", // 7 + "", // 8 + "", // 9 + "", // 10 + "", // 11 + "" // 12 + }; - /** A private constructor just to make sure nobody - instantiates the class by mistake. */ - private PdfStrings () - { - } + /** Flags for annotations */ + public static final String[] ANNOTATIONFLAGS = { + "Invisible", // 1 + "Hidden", // 2 + "Print", // 3 + "NoZoom", // 4 + "NoRotate", // 5 + "NoView", // 6 + "ReadOnly" // 7 + }; + /** A private constructor just to make sure nobody instantiates the class by mistake. */ + private PdfStrings() {} } diff --git a/jhove-modules/pdf-hul/src/main/java/edu/harvard/hul/ois/jhove/module/pdf/PdfXMPSource.java b/jhove-modules/pdf-hul/src/main/java/edu/harvard/hul/ois/jhove/module/pdf/PdfXMPSource.java index 0207517f9..cdc56e26f 100644 --- a/jhove-modules/pdf-hul/src/main/java/edu/harvard/hul/ois/jhove/module/pdf/PdfXMPSource.java +++ b/jhove-modules/pdf-hul/src/main/java/edu/harvard/hul/ois/jhove/module/pdf/PdfXMPSource.java @@ -1,95 +1,76 @@ -/********************************************************************** - * Jhove - JSTOR/Harvard Object Validation Environment - * Copyright 2003 by JSTOR and the President and Fellows of Harvard College - **********************************************************************/ - +/** + * ******************************************************************** Jhove - JSTOR/Harvard Object + * Validation Environment Copyright 2003 by JSTOR and the President and Fellows of Harvard College + * ******************************************************************** + */ package edu.harvard.hul.ois.jhove.module.pdf; -//import java.io.InputStream; -import java.io.*; +// import java.io.InputStream; import edu.harvard.hul.ois.jhove.XMLWrapperStream; +import java.io.*; -//import edu.harvard.hul.ois.jhove.XMPSource; +// import edu.harvard.hul.ois.jhove.XMPSource; /** - * Class for providing an InputSource to XMPHandler. - * Only an InputSource based on a Reader is supported. - * - * @author Gary McGath + * Class for providing an InputSource to XMPHandler. Only an InputSource based on a Reader is + * supported. * + * @author Gary McGath */ -public final class PdfXMPSource - extends edu.harvard.hul.ois.jhove.XMPSource { +public final class PdfXMPSource extends edu.harvard.hul.ois.jhove.XMPSource { - private PdfStream _stream; - private RandomAccessFile _raf; - protected String _encoding; - - /** - * Constructor based on Stream object. - * Since a double read may be necessary, we have - * one version without encoding (before it's known), - * and another with encoding. - * - * @param stream PDFStream containing the XMP - * @param raf The RandomAccessFile object underlying the PDF - */ - public PdfXMPSource(PdfStream stream, - RandomAccessFile raf) { - super (new InputStreamReader - (new XMLWrapperStream - (new StreamInputStream (stream, raf), - "XMP", "1.0", null, null))); - //super(rdr); - _stream = stream; - _raf = raf; - } + private PdfStream _stream; + private RandomAccessFile _raf; + protected String _encoding; + /** + * Constructor based on Stream object. Since a double read may be necessary, we have one version + * without encoding (before it's known), and another with encoding. + * + * @param stream PDFStream containing the XMP + * @param raf The RandomAccessFile object underlying the PDF + */ + public PdfXMPSource(PdfStream stream, RandomAccessFile raf) { + super( + new InputStreamReader( + new XMLWrapperStream(new StreamInputStream(stream, raf), "XMP", "1.0", null, null))); + // super(rdr); + _stream = stream; + _raf = raf; + } + /** + * Constructor based on Stream object with encoding specified. + * + * @param stream PDFStream containing the XMP + * @param raf The RandomAccessFile object underlying the PDF + * @param encoding The character encoding to use + */ + public PdfXMPSource(PdfStream stream, RandomAccessFile raf, String encoding) + throws UnsupportedEncodingException { + super(new InputStreamReader(new StreamInputStream(stream, raf), encoding)); + // super(rdr); + _stream = stream; + _raf = raf; + _encoding = encoding; + } - /** - * Constructor based on Stream object with - * encoding specified. - * - * @param stream PDFStream containing the XMP - * @param raf The RandomAccessFile object underlying the PDF - * @param encoding The character encoding to use - */ - public PdfXMPSource(PdfStream stream, - RandomAccessFile raf, - String encoding) - throws UnsupportedEncodingException { - super (new InputStreamReader - (new StreamInputStream (stream, raf), encoding)); - //super(rdr); - _stream = stream; - _raf = raf; - _encoding = encoding; + /* (non-Javadoc) + * + * Resets the reader by reinitializing it from the PdfStream. + * + * @see edu.harvard.hul.ois.jhove.XMPSource#resetReader() + */ + @Override + protected void resetReader() { + try { + if (_encoding == null) { + _reader = new InputStreamReader(new StreamInputStream(_stream, _raf)); + } else { + _reader = new InputStreamReader(new StreamInputStream(_stream, _raf), _encoding); + } + } catch (UnsupportedEncodingException e) { + // Has no business happening if it didn't the first time. } - - - /* (non-Javadoc) - * - * Resets the reader by reinitializing it from the PdfStream. - * - * @see edu.harvard.hul.ois.jhove.XMPSource#resetReader() - */ - @Override - protected void resetReader() { - try { - if (_encoding == null) { - _reader = new InputStreamReader - (new StreamInputStream (_stream, _raf)); - } - else { - _reader = new InputStreamReader - (new StreamInputStream - (_stream, _raf), _encoding); - } - } - catch (UnsupportedEncodingException e) { - // Has no business happening if it didn't the first time. - } - } - + } } diff --git a/jhove-modules/pdf-hul/src/main/java/edu/harvard/hul/ois/jhove/module/pdf/RunLengthFilterStream.java b/jhove-modules/pdf-hul/src/main/java/edu/harvard/hul/ois/jhove/module/pdf/RunLengthFilterStream.java index 53e152f3b..b81374c6b 100644 --- a/jhove-modules/pdf-hul/src/main/java/edu/harvard/hul/ois/jhove/module/pdf/RunLengthFilterStream.java +++ b/jhove-modules/pdf-hul/src/main/java/edu/harvard/hul/ois/jhove/module/pdf/RunLengthFilterStream.java @@ -1,29 +1,24 @@ -/********************************************************************** - * Jhove - JSTOR/Harvard Object Validation Environment - * Copyright 2004 by JSTOR and the President and Fellows of Harvard College - **********************************************************************/ - +/** + * ******************************************************************** Jhove - JSTOR/Harvard Object + * Validation Environment Copyright 2004 by JSTOR and the President and Fellows of Harvard College + * ******************************************************************** + */ package edu.harvard.hul.ois.jhove.module.pdf; import java.io.FilterInputStream; import java.io.InputStream; /** - * This is a stub which may be implemented in the future. - * It appears to be unnecessary for object streams and cross-reference - * streams created by any version of Acrobat through 7.0, and we - * don't look at other types of streams. + * This is a stub which may be implemented in the future. It appears to be unnecessary for object + * streams and cross-reference streams created by any version of Acrobat through 7.0, and we don't + * look at other types of streams. * * @author Gary McGath - * */ public class RunLengthFilterStream extends FilterInputStream { - /** - * @param in - */ - public RunLengthFilterStream(InputStream in) { - super(in); - } - + /** @param in */ + public RunLengthFilterStream(InputStream in) { + super(in); + } } diff --git a/jhove-modules/pdf-hul/src/main/java/edu/harvard/hul/ois/jhove/module/pdf/State.java b/jhove-modules/pdf-hul/src/main/java/edu/harvard/hul/ois/jhove/module/pdf/State.java index bb0bbde59..9d6086fb3 100644 --- a/jhove-modules/pdf-hul/src/main/java/edu/harvard/hul/ois/jhove/module/pdf/State.java +++ b/jhove-modules/pdf-hul/src/main/java/edu/harvard/hul/ois/jhove/module/pdf/State.java @@ -1,95 +1,80 @@ -/********************************************************************** - * Jhove - JSTOR/Harvard Object Validation Environment - * Copyright 2003 by JSTOR and the President and Fellows of Harvard College - **********************************************************************/ - +/** + * ******************************************************************** Jhove - JSTOR/Harvard Object + * Validation Environment Copyright 2003 by JSTOR and the President and Fellows of Harvard College + * ******************************************************************** + */ package edu.harvard.hul.ois.jhove.module.pdf; -/** - * An enumeration class for use with the Tokenizer. Only - * the static instances which are declared within the class - * should ever be created. +/** + * An enumeration class for use with the Tokenizer. Only the static instances which are declared + * within the class should ever be created. */ -public class State -{ - /* **************************************************************** - * PUBLIC CLASS FIELDS. - ******************************************************************/ +public class State { + /* **************************************************************** + * PUBLIC CLASS FIELDS. + ******************************************************************/ - public static final State COMMENT = new State ("COMMENT"); - public static final State E = new State ("E"); - public static final State EN = new State ("EN"); - public static final State END = new State ("END"); - public static final State ENDS = new State ("ENDS"); - public static final State ENDST = new State ("ENDST"); - public static final State ENDSTR = new State ("ENDSTR"); - public static final State ENDSTRE = new State ("ENDSTRE"); - public static final State ENDSTREA = new State ("ENDSTREA"); - public static final State ENDSTREAM = new State ("ENDSTREAM"); - public static final State FRACTIONAL = new State ("FRACTIONAL"); - public static final State GREATER_THAN = new State ("GREATER_THAN"); - public static final State HEXADECIMAL = new State ("HEXADECIMAL"); - public static final State HEX_FE_1 = new State ("HEX_FE_1"); - public static final State HEX_FE_2 = new State ("HEX_FE_2"); - public static final State HEX_PDF_1 = new State ("HEX_PDF_1"); - public static final State HEX_PDF_2 = new State ("HEX_PDF_2"); - public static final State HEX_UTF16_1 = new State ("HEX_UTF16_1"); - public static final State HEX_UTF16_2 = new State ("HEX_UTF16_2"); - public static final State HEX_UTF16_3 = new State ("HEX_UTF16_3"); - public static final State HEX_UTF16_4 = new State ("HEX_UTF16_4"); - public static final State HEX_RAW = new State ("HEX_RAW"); - public static final State KEYWORD = new State ("KEYWORD"); - public static final State LESS_THAN = new State ("LESS_THAN"); - public static final State LITERAL = new State ("LITERAL"); - public static final State LITERAL_FE = new State ("LITERAL_FE"); - public static final State LITERAL_PDF = new State ("LITERAL_PDF"); - public static final State LITERAL_UTF16_1 = new State ("LITERAL_UTF16_1"); - public static final State LITERAL_UTF16_2 = new State ("LITERAL_UTF16_2"); - public static final State NAME = new State ("NAME"); - public static final State NUMERIC = new State ("NUMERIC"); - public static final State STREAM = new State ("STREAM"); - public static final State WHITESPACE = new State ("WHITESPACE"); + public static final State COMMENT = new State("COMMENT"); + public static final State E = new State("E"); + public static final State EN = new State("EN"); + public static final State END = new State("END"); + public static final State ENDS = new State("ENDS"); + public static final State ENDST = new State("ENDST"); + public static final State ENDSTR = new State("ENDSTR"); + public static final State ENDSTRE = new State("ENDSTRE"); + public static final State ENDSTREA = new State("ENDSTREA"); + public static final State ENDSTREAM = new State("ENDSTREAM"); + public static final State FRACTIONAL = new State("FRACTIONAL"); + public static final State GREATER_THAN = new State("GREATER_THAN"); + public static final State HEXADECIMAL = new State("HEXADECIMAL"); + public static final State HEX_FE_1 = new State("HEX_FE_1"); + public static final State HEX_FE_2 = new State("HEX_FE_2"); + public static final State HEX_PDF_1 = new State("HEX_PDF_1"); + public static final State HEX_PDF_2 = new State("HEX_PDF_2"); + public static final State HEX_UTF16_1 = new State("HEX_UTF16_1"); + public static final State HEX_UTF16_2 = new State("HEX_UTF16_2"); + public static final State HEX_UTF16_3 = new State("HEX_UTF16_3"); + public static final State HEX_UTF16_4 = new State("HEX_UTF16_4"); + public static final State HEX_RAW = new State("HEX_RAW"); + public static final State KEYWORD = new State("KEYWORD"); + public static final State LESS_THAN = new State("LESS_THAN"); + public static final State LITERAL = new State("LITERAL"); + public static final State LITERAL_FE = new State("LITERAL_FE"); + public static final State LITERAL_PDF = new State("LITERAL_PDF"); + public static final State LITERAL_UTF16_1 = new State("LITERAL_UTF16_1"); + public static final State LITERAL_UTF16_2 = new State("LITERAL_UTF16_2"); + public static final State NAME = new State("NAME"); + public static final State NUMERIC = new State("NUMERIC"); + public static final State STREAM = new State("STREAM"); + public static final State WHITESPACE = new State("WHITESPACE"); - /* **************************************************************** - * PRIVATE INSTANCE FIELDS. - ******************************************************************/ + /* **************************************************************** + * PRIVATE INSTANCE FIELDS. + ******************************************************************/ - private String _name; + private String _name; - /* **************************************************************** - * CLASS CONSTRUCTOR. - ******************************************************************/ + /* **************************************************************** + * CLASS CONSTRUCTOR. + ******************************************************************/ - /** - * Constructor. It is private so that no other classes - * can create instances of State. - */ - private State (String name) - { - _name = name; - } + /** Constructor. It is private so that no other classes can create instances of State. */ + private State(String name) { + _name = name; + } - /* **************************************************************** - * PUBLIC INSTANCE METHODS. - ******************************************************************/ + /* **************************************************************** + * PUBLIC INSTANCE METHODS. + ******************************************************************/ - /** - * Equality test. - * Two State objects are considered equal only if they - * are the same object. - */ - public boolean equals (State state) - { - return this == state; - } + /** Equality test. Two State objects are considered equal only if they are the same object. */ + public boolean equals(State state) { + return this == state; + } - /** - * Convert to String representation. - * A State object's String representation is its name. - */ - @Override - public String toString () - { - return _name; - } + /** Convert to String representation. A State object's String representation is its name. */ + @Override + public String toString() { + return _name; + } } diff --git a/jhove-modules/pdf-hul/src/main/java/edu/harvard/hul/ois/jhove/module/pdf/StdStructTypes.java b/jhove-modules/pdf-hul/src/main/java/edu/harvard/hul/ois/jhove/module/pdf/StdStructTypes.java index 3eec9cc6e..b9fde180b 100644 --- a/jhove-modules/pdf-hul/src/main/java/edu/harvard/hul/ois/jhove/module/pdf/StdStructTypes.java +++ b/jhove-modules/pdf-hul/src/main/java/edu/harvard/hul/ois/jhove/module/pdf/StdStructTypes.java @@ -1,71 +1,82 @@ -/********************************************************************** - * Jhove - JSTOR/Harvard Object Validation Environment - * Copyright 2003 by JSTOR and the President and Fellows of Harvard College - **********************************************************************/ - +/** + * ******************************************************************** Jhove - JSTOR/Harvard Object + * Validation Environment Copyright 2003 by JSTOR and the President and Fellows of Harvard College + * ******************************************************************** + */ package edu.harvard.hul.ois.jhove.module.pdf; - /** - * This class holds the constants for standard structure type names, - * and a static method for determining if a string belongs - * to those names. + * This class holds the constants for standard structure type names, and a static method for + * determining if a string belongs to those names. */ -public class StdStructTypes -{ - /** - * Array of valid structure type names - */ - public final static String typeNames [] = { - "Document", "Part", "Art", "Sect", - "Div", "BlockQuote", "Caption", "TOC", - "TOCI", "Index", "NonStruct", "Private", - "P", "H", "H1", "H2", "H3", "H4", "H5", "H6", - "L", "LI", "Lbl", "LBody", - "Table", "TR", "TH", "TD", - "Span", "Quote", "Note", "Reference", - "BibEntry", "Code", "Link", - "Figure", "Formula", "Form" - }; +public class StdStructTypes { + /** Array of valid structure type names */ + public static final String typeNames[] = { + "Document", + "Part", + "Art", + "Sect", + "Div", + "BlockQuote", + "Caption", + "TOC", + "TOCI", + "Index", + "NonStruct", + "Private", + "P", + "H", + "H1", + "H2", + "H3", + "H4", + "H5", + "H6", + "L", + "LI", + "Lbl", + "LBody", + "Table", + "TR", + "TH", + "TD", + "Span", + "Quote", + "Note", + "Reference", + "BibEntry", + "Code", + "Link", + "Figure", + "Formula", + "Form" + }; - /** - * The subset of typeNames which denotes a block-level - * element - */ - public final static String blockLevelNames [] = { - "P", "H", "H1", "H2", "H3", "H4", "H5", "H6", - "L", "LI", "Lbl", "LBody", "Table" - }; + /** The subset of typeNames which denotes a block-level element */ + public static final String blockLevelNames[] = { + "P", "H", "H1", "H2", "H3", "H4", "H5", "H6", "L", "LI", "Lbl", "LBody", "Table" + }; + /* Private constructor, so no instances of this object + can be created */ + private StdStructTypes() {} - /* Private constructor, so no instances of this object - can be created */ - private StdStructTypes () - { + /** Returns true if s is equal (by an equals() test) to some string in typeNames. */ + public static boolean includes(String s) { + for (int i = 0; i < typeNames.length; i++) { + if (typeNames[i].equals(s)) { + return true; + } } + return false; + } - /** - * Returns true if s is equal (by an equals() test) - * to some string in typeNames. - */ - public static boolean includes (String s) - { - for (int i = 0; i < typeNames.length; i++) { - if (typeNames[i].equals (s)) { - return true; - } - } - return false; - } - - public static boolean isBlockLevel (String s) - { - for (int i = 0; i < blockLevelNames.length; i++) { - if (blockLevelNames[i].equals (s)) { - return true; - } - } - return false; + public static boolean isBlockLevel(String s) { + for (int i = 0; i < blockLevelNames.length; i++) { + if (blockLevelNames[i].equals(s)) { + return true; + } } + return false; + } } - diff --git a/jhove-modules/pdf-hul/src/main/java/edu/harvard/hul/ois/jhove/module/pdf/Stream.java b/jhove-modules/pdf-hul/src/main/java/edu/harvard/hul/ois/jhove/module/pdf/Stream.java index 08e419596..6378affd2 100644 --- a/jhove-modules/pdf-hul/src/main/java/edu/harvard/hul/ois/jhove/module/pdf/Stream.java +++ b/jhove-modules/pdf-hul/src/main/java/edu/harvard/hul/ois/jhove/module/pdf/Stream.java @@ -1,225 +1,197 @@ -/********************************************************************** - * Jhove - JSTOR/Harvard Object Validation Environment - * Copyright 2003-2005 by JSTOR and the President and Fellows of Harvard College - **********************************************************************/ - +/** + * ******************************************************************** Jhove - JSTOR/Harvard Object + * Validation Environment Copyright 2003-2005 by JSTOR and the President and Fellows of Harvard + * College ******************************************************************** + */ package edu.harvard.hul.ois.jhove.module.pdf; -//import edu.harvard.hul.ois.jhove.*; +// import edu.harvard.hul.ois.jhove.*; import java.io.*; -//import java.util.*; -//import java.util.zip.InflaterInputStream; + +// import java.util.*; +// import java.util.zip.InflaterInputStream; /** - * Class to encapsulate a stream token. The content of the - * stream is not saved, only its length and starting offset. + * Class to encapsulate a stream token. The content of the stream is not saved, only its length and + * starting offset. */ -public class Stream - extends Token -{ - /** Length of stream. */ - private long _length; - - /** Starting offset in file. */ - private long _offset; - - /** Number of bytes read so far. */ - private int _bytesRead; - - /** Filters which apply to this stream. */ - private Filter[] _filters; - - /** InputStream which incorporates all the filters. */ - private InputStream _inStream; - - /** Byte array which contains the raw file data for reading. */ - private byte[] _sdata; - - /** Constructor. */ - public Stream () - { - super (); - _length = 0; - _offset = -1; - _filters = new Filter[0]; - _bytesRead = 0; - _sdata = null; - } +public class Stream extends Token { + /** Length of stream. */ + private long _length; - /** Returns the length of the stream. This is 0, unless - * the Stream's setLength method has been called. - */ - public long getLength () - { - return _length; - } + /** Starting offset in file. */ + private long _offset; - /** - * Sets the length field. - * This should be the length of the stream proper - * (not counting its dictionary) before filtering, in other words, - * the number of bytes stored in the file. - */ - public void setLength (long length) - { - _length = length; - } + /** Number of bytes read so far. */ + private int _bytesRead; - /** Returns the current offset in the stream. This is -1, unless - * the Stream's setOffset method has been called. - */ - public long getOffset () - { - return _offset; - } + /** Filters which apply to this stream. */ + private Filter[] _filters; - /** - * Sets the offset field. - */ - public void setOffset (long offset) - { - _offset = offset; - } - - - /** Sets the array of filters used by the stream. - * This must be called before initRead. - */ - public void setFilters (Filter[] filters) - { - _filters = filters; - } + /** InputStream which incorporates all the filters. */ + private InputStream _inStream; + /** Byte array which contains the raw file data for reading. */ + private byte[] _sdata; - /** Prepares for reading the Stream. - * If the filter List includes one which we don't support, throws a - * PdfException. This supports the abbreviated filter names - * in Appendix H of the PDF spec. */ - public void initRead (RandomAccessFile raf) - throws IOException - { - _bytesRead = 0; - raf.seek(_offset); - //InputStream is = new RAFInputStream (raf); - /* We can't easily resume reading a filtered stream if we - * seek elsewhere in the file, so the only really - * safe bet is to read it all into memory first. - * Fortunately, _length tells us the number of raw - * bytes we need to read. This also saves rereading - * when we need to reset the stream. */ - if (_sdata == null) { - _sdata = new byte[(int) _length]; - int ln = 0; - while (ln < _length) { - int n = raf.read (_sdata, ln, (int) (_length - ln)); - if (n <= 0) { - break; - } - ln += n; - } - } - InputStream is = new ByteArrayInputStream (_sdata); - for (int i = 0; i < _filters.length; i++) { - Filter filt = _filters[i]; - String filtName = filt.getFilterName (); - - /* ASCIIHex-, ASCII85- and RunLengthDecode are currently - * just stubs. If we ever really need them, we should - * consider grabbing the implementations in PDFBox on - * SourceForge, which should (hint to third-party developers - * if you need them) just drop into place with - * the addition of an include. */ - if ("ASCIIHexDecode".equals (filtName) || "AHx".equals (filtName)) { - is = new AsciiHexFilterStream (is); - } - else if ("ASCII85Decode".equals (filtName) || "A85".equals (filtName)) { - is = new Ascii85FilterStream (is); - } - else if ("FlateDecode".equals (filtName) || "Fl".equals (filtName)) { - // InflaterInputStream does only part of the job. - // PdfFlateInputStream enhances it with Predictor support. - is = new PdfFlateInputStream (is, filt.getDecodeParms()); - } - else if ("RunLengthDecode".equals (filtName) || "RL".equals (filtName)) { - is = new RunLengthFilterStream (is); - } + /** Constructor. */ + public Stream() { + super(); + _length = 0; + _offset = -1; + _filters = new Filter[0]; + _bytesRead = 0; + _sdata = null; + } + + /** + * Returns the length of the stream. This is 0, unless the Stream's setLength method has been + * called. + */ + public long getLength() { + return _length; + } + + /** + * Sets the length field. This should be the length of the stream proper (not counting its + * dictionary) before filtering, in other words, the number of bytes stored in the file. + */ + public void setLength(long length) { + _length = length; + } + + /** + * Returns the current offset in the stream. This is -1, unless the Stream's setOffset method has + * been called. + */ + public long getOffset() { + return _offset; + } + + /** Sets the offset field. */ + public void setOffset(long offset) { + _offset = offset; + } + + /** Sets the array of filters used by the stream. This must be called before initRead. */ + public void setFilters(Filter[] filters) { + _filters = filters; + } + + /** + * Prepares for reading the Stream. If the filter List includes one which we don't support, throws + * a PdfException. This supports the abbreviated filter names in Appendix H of the PDF spec. + */ + public void initRead(RandomAccessFile raf) throws IOException { + _bytesRead = 0; + raf.seek(_offset); + // InputStream is = new RAFInputStream (raf); + /* We can't easily resume reading a filtered stream if we + * seek elsewhere in the file, so the only really + * safe bet is to read it all into memory first. + * Fortunately, _length tells us the number of raw + * bytes we need to read. This also saves rereading + * when we need to reset the stream. */ + if (_sdata == null) { + _sdata = new byte[(int) _length]; + int ln = 0; + while (ln < _length) { + int n = raf.read(_sdata, ln, (int) (_length - ln)); + if (n <= 0) { + break; } - _inStream = is; + ln += n; + } } - - - /** Reads a byte from the Stream, applying the Filters if any. - */ - public int read() throws IOException - { - - int val = _inStream.read(); - if (val >= 0) { - ++_bytesRead; - } - return val; + InputStream is = new ByteArrayInputStream(_sdata); + for (int i = 0; i < _filters.length; i++) { + Filter filt = _filters[i]; + String filtName = filt.getFilterName(); + + /* ASCIIHex-, ASCII85- and RunLengthDecode are currently + * just stubs. If we ever really need them, we should + * consider grabbing the implementations in PDFBox on + * SourceForge, which should (hint to third-party developers + * if you need them) just drop into place with + * the addition of an include. */ + if ("ASCIIHexDecode".equals(filtName) || "AHx".equals(filtName)) { + is = new AsciiHexFilterStream(is); + } else if ("ASCII85Decode".equals(filtName) || "A85".equals(filtName)) { + is = new Ascii85FilterStream(is); + } else if ("FlateDecode".equals(filtName) || "Fl".equals(filtName)) { + // InflaterInputStream does only part of the job. + // PdfFlateInputStream enhances it with Predictor support. + is = new PdfFlateInputStream(is, filt.getDecodeParms()); + } else if ("RunLengthDecode".equals(filtName) || "RL".equals(filtName)) { + is = new RunLengthFilterStream(is); + } } - - /** Reads a sequence of bytes from the Stream, applying the - * Filters if any. - */ - public int read (byte[] b) throws IOException - { - int n = _inStream.read (b); - if (n > 0) { - _bytesRead += n; - } - return n; + _inStream = is; + } + + /** Reads a byte from the Stream, applying the Filters if any. */ + public int read() throws IOException { + + int val = _inStream.read(); + if (val >= 0) { + ++_bytesRead; } - - /** Skips a specified number of bytes in the stream. */ - public long skipBytes (long n) throws IOException - { - long val = _inStream.skip(n); - _bytesRead += val; - return val; + return val; + } + + /** Reads a sequence of bytes from the Stream, applying the Filters if any. */ + public int read(byte[] b) throws IOException { + int n = _inStream.read(b); + if (n > 0) { + _bytesRead += n; } + return n; + } - /** Reads an ASCII string, which may be preceded by white space. - * Will eat the first white space character after the ASCII - * string. */ - public int readAsciiInt () throws IOException, PdfException - { - boolean digitSeen = false; - int val = 0; - for (;;) { - char c = (char) read (); - if (Character.isDigit(c)) { - digitSeen = true; - val = val * 10 + (c - '0'); - } - else if (digitSeen) { - /* Non-digit after a digit; we're done */ - break; - } - else if (!Character.isWhitespace(c)) { - throw new PdfMalformedException - (MessageConstants.PDF_HUL_46); // PDF-HUL-46 - } - } - return val; + /** Skips a specified number of bytes in the stream. */ + public long skipBytes(long n) throws IOException { + long val = _inStream.skip(n); + _bytesRead += val; + return val; + } + + /** + * Reads an ASCII string, which may be preceded by white space. Will eat the first white space + * character after the ASCII string. + */ + public int readAsciiInt() throws IOException, PdfException { + boolean digitSeen = false; + int val = 0; + for (; ; ) { + char c = (char) read(); + if (Character.isDigit(c)) { + digitSeen = true; + val = val * 10 + (c - '0'); + } else if (digitSeen) { + /* Non-digit after a digit; we're done */ + break; + } else if (!Character.isWhitespace(c)) { + throw new PdfMalformedException(MessageConstants.PDF_HUL_46); // PDF-HUL-46 + } } - - /** Advances to a specified offset in the stream. The offset - * is defined as the number of decompressed bytes which - * precede the position in the stream. Returns true - * if the advance is successful, false if the - * point has already been passed or some other failure occurs. - */ - public boolean advanceTo (int offset) throws IOException { - if (offset < _bytesRead) { - return false; // can't get there from here - } - while (_bytesRead < offset) { - if (skipBytes (offset - _bytesRead) <= 0) { - break; - } - } - return true; + return val; + } + + /** + * Advances to a specified offset in the stream. The offset is defined as the number of + * decompressed bytes which precede the position in the stream. Returns true if the + * advance is successful, false if the point has already been passed or some other + * failure occurs. + */ + public boolean advanceTo(int offset) throws IOException { + if (offset < _bytesRead) { + return false; // can't get there from here + } + while (_bytesRead < offset) { + if (skipBytes(offset - _bytesRead) <= 0) { + break; + } } + return true; + } } diff --git a/jhove-modules/pdf-hul/src/main/java/edu/harvard/hul/ois/jhove/module/pdf/StreamInputStream.java b/jhove-modules/pdf-hul/src/main/java/edu/harvard/hul/ois/jhove/module/pdf/StreamInputStream.java index 41655d759..5be2822e8 100644 --- a/jhove-modules/pdf-hul/src/main/java/edu/harvard/hul/ois/jhove/module/pdf/StreamInputStream.java +++ b/jhove-modules/pdf-hul/src/main/java/edu/harvard/hul/ois/jhove/module/pdf/StreamInputStream.java @@ -1,54 +1,44 @@ -/********************************************************************** - * Jhove - JSTOR/Harvard Object Validation Environment - * Copyright 2003 by JSTOR and the President and Fellows of Harvard College - **********************************************************************/ - - +/** + * ******************************************************************** Jhove - JSTOR/Harvard Object + * Validation Environment Copyright 2003 by JSTOR and the President and Fellows of Harvard College + * ******************************************************************** + */ package edu.harvard.hul.ois.jhove.module.pdf; import java.io.*; - -/** A StreamInputStream is an InputStream which provides the bytes - * from a PdfStream. It reads bytes from the PdfStream's underlying - * file starting at the beginning of the stream data and providing - * as many bytes as are indicated by its length. No filters are - * applied; just the raw data is read. - * +/** + * A StreamInputStream is an InputStream which provides the bytes from a PdfStream. It reads bytes + * from the PdfStream's underlying file starting at the beginning of the stream data and providing + * as many bytes as are indicated by its length. No filters are applied; just the raw data is read. */ public class StreamInputStream extends InputStream { - private RandomAccessFile _file; - private long _startPos; - private long _curPos; - private long _length; - - public StreamInputStream (PdfStream pdfStream, RandomAccessFile file) - { - _file = file; - Stream strm = pdfStream.getStream (); - _startPos = strm.getOffset (); - _curPos = _startPos; - _length = strm.getLength (); - try { - file.seek (_startPos); - } - catch (IOException e) {} + private RandomAccessFile _file; + private long _startPos; + private long _curPos; + private long _length; + + public StreamInputStream(PdfStream pdfStream, RandomAccessFile file) { + _file = file; + Stream strm = pdfStream.getStream(); + _startPos = strm.getOffset(); + _curPos = _startPos; + _length = strm.getLength(); + try { + file.seek(_startPos); + } catch (IOException e) { } - - - /** - * Return one byte from the stream. - * When the end of the stream is reached, returns -1. - */ - @Override - public int read () throws IOException - { - if (_curPos - _startPos >= _length) { - return -1; - } - _curPos++; - int ch = _file.read (); - return ch; + } + + /** Return one byte from the stream. When the end of the stream is reached, returns -1. */ + @Override + public int read() throws IOException { + if (_curPos - _startPos >= _length) { + return -1; } -} \ No newline at end of file + _curPos++; + int ch = _file.read(); + return ch; + } +} diff --git a/jhove-modules/pdf-hul/src/main/java/edu/harvard/hul/ois/jhove/module/pdf/StreamTokenizer.java b/jhove-modules/pdf-hul/src/main/java/edu/harvard/hul/ois/jhove/module/pdf/StreamTokenizer.java index de83b5b79..0195611c5 100644 --- a/jhove-modules/pdf-hul/src/main/java/edu/harvard/hul/ois/jhove/module/pdf/StreamTokenizer.java +++ b/jhove-modules/pdf-hul/src/main/java/edu/harvard/hul/ois/jhove/module/pdf/StreamTokenizer.java @@ -1,8 +1,8 @@ -/********************************************************************** - * Jhove - JSTOR/Harvard Object Validation Environment - * Copyright 2005 by JSTOR and the President and Fellows of Harvard College - **********************************************************************/ - +/** + * ******************************************************************** Jhove - JSTOR/Harvard Object + * Validation Environment Copyright 2005 by JSTOR and the President and Fellows of Harvard College + * ******************************************************************** + */ package edu.harvard.hul.ois.jhove.module.pdf; import java.io.*; @@ -11,88 +11,70 @@ * Tokenizer subclass which gets data from an object stream. * * @author Gary McGath - * */ public class StreamTokenizer extends Tokenizer { - /** Source from which to read bytes. */ - private Stream _stream; - - /** Backup flag. */ - private boolean _backupFlag; - - /** Last character read. Will be returned again if _backupFlag - * is true. */ - private int _lastChar; - + /** Source from which to read bytes. */ + private Stream _stream; - public StreamTokenizer (RandomAccessFile file, Stream stream) - { - super (); - _file = file; - _stream = stream; - _backupFlag = false; - } + /** Backup flag. */ + private boolean _backupFlag; - /** Streams can occur only in files, not in streams, - * so this should never be called. - */ - @Override - protected void initStream (Stream token) - throws PdfException - { - throw new PdfMalformedException (MessageConstants.PDF_HUL_47); // PDF-HUL-47 - } + /** Last character read. Will be returned again if _backupFlag is true. */ + private int _lastChar; - /** Gets a character from the file, using a buffer. */ - @Override - public int readChar () throws IOException - { - if (_backupFlag) { - _backupFlag = false; - return _lastChar; - } - _lastChar = _stream.read (); - return _lastChar; - } + public StreamTokenizer(RandomAccessFile file, Stream stream) { + super(); + _file = file; + _stream = stream; + _backupFlag = false; + } - /** - * Set the Tokenizer to a new position in the stream. - * - * @param offset The offset in bytes from the start of the stream. - */ - @Override - public void seek (long offset) - throws IOException - { - // Advancing in the stream is easy. Backing up requires starting - // the stream over. - if (!_stream.advanceTo ((int) offset)) { - _stream.initRead (_file); - _stream.advanceTo ((int) offset); - } - seekReset (_stream.getOffset()); - } + /** Streams can occur only in files, not in streams, so this should never be called. */ + @Override + protected void initStream(Stream token) throws PdfException { + throw new PdfMalformedException(MessageConstants.PDF_HUL_47); // PDF-HUL-47 + } - /** Sets the offset of a Stream to the current file position. - * Only the file-based tokenizer can do this, so this should never - * be called. - */ - @Override - protected void setStreamOffset (Stream token) - throws PdfException - { - throw new PdfMalformedException (MessageConstants.PDF_HUL_48); // PDF-HUL-48 + /** Gets a character from the file, using a buffer. */ + @Override + public int readChar() throws IOException { + if (_backupFlag) { + _backupFlag = false; + return _lastChar; } + _lastChar = _stream.read(); + return _lastChar; + } - - /** - * Back up a byte so it will be read again. - */ - @Override - public void backupChar () - { - _backupFlag = true; + /** + * Set the Tokenizer to a new position in the stream. + * + * @param offset The offset in bytes from the start of the stream. + */ + @Override + public void seek(long offset) throws IOException { + // Advancing in the stream is easy. Backing up requires starting + // the stream over. + if (!_stream.advanceTo((int) offset)) { + _stream.initRead(_file); + _stream.advanceTo((int) offset); } + seekReset(_stream.getOffset()); + } + + /** + * Sets the offset of a Stream to the current file position. Only the file-based tokenizer can do + * this, so this should never be called. + */ + @Override + protected void setStreamOffset(Stream token) throws PdfException { + throw new PdfMalformedException(MessageConstants.PDF_HUL_48); // PDF-HUL-48 + } + /** Back up a byte so it will be read again. */ + @Override + public void backupChar() { + _backupFlag = true; + } } diff --git a/jhove-modules/pdf-hul/src/main/java/edu/harvard/hul/ois/jhove/module/pdf/StringValuedToken.java b/jhove-modules/pdf-hul/src/main/java/edu/harvard/hul/ois/jhove/module/pdf/StringValuedToken.java index ca5982813..fc10b0424 100644 --- a/jhove-modules/pdf-hul/src/main/java/edu/harvard/hul/ois/jhove/module/pdf/StringValuedToken.java +++ b/jhove-modules/pdf-hul/src/main/java/edu/harvard/hul/ois/jhove/module/pdf/StringValuedToken.java @@ -1,49 +1,36 @@ -/********************************************************************** - * Jhove - JSTOR/Harvard Object Validation Environment - * Copyright 2003 by JSTOR and the President and Fellows of Harvard College - **********************************************************************/ - -package edu.harvard.hul.ois.jhove.module.pdf; - -import java.util.Vector; - /** - * Abstract class for all PDF tokens which consist of a character sequence. + * ******************************************************************** Jhove - JSTOR/Harvard Object + * Validation Environment Copyright 2003 by JSTOR and the President and Fellows of Harvard College + * ******************************************************************** */ -public abstract class StringValuedToken - extends Token -{ - protected String _value; - protected Vector _rawBytes; - - public StringValuedToken () - { - super (); - } - - /** - * Get the value of the token as a String. - */ - public String getValue () - { - return _value; - } - - /** - * Get the value of the token's untranslated bytes. This is unsupported - * and will always return null. - */ - public Vector getRawBytes () - { - return _rawBytes; - } +package edu.harvard.hul.ois.jhove.module.pdf; - /** - * Set the value of the token. - */ - public void setValue (String value) - { - _value = value; - } +import java.util.Vector; +/** Abstract class for all PDF tokens which consist of a character sequence. */ +public abstract class StringValuedToken extends Token { + protected String _value; + protected Vector _rawBytes; + + public StringValuedToken() { + super(); + } + + /** Get the value of the token as a String. */ + public String getValue() { + return _value; + } + + /** + * Get the value of the token's untranslated bytes. This is unsupported and will always return + * null. + */ + public Vector getRawBytes() { + return _rawBytes; + } + + /** Set the value of the token. */ + public void setValue(String value) { + _value = value; + } } diff --git a/jhove-modules/pdf-hul/src/main/java/edu/harvard/hul/ois/jhove/module/pdf/StructureElement.java b/jhove-modules/pdf-hul/src/main/java/edu/harvard/hul/ois/jhove/module/pdf/StructureElement.java index 36369ec5e..724585409 100644 --- a/jhove-modules/pdf-hul/src/main/java/edu/harvard/hul/ois/jhove/module/pdf/StructureElement.java +++ b/jhove-modules/pdf-hul/src/main/java/edu/harvard/hul/ois/jhove/module/pdf/StructureElement.java @@ -1,8 +1,8 @@ -/********************************************************************** - * Jhove - JSTOR/Harvard Object Validation Environment - * Copyright 2003 by JSTOR and the President and Fellows of Harvard College - **********************************************************************/ - +/** + * ******************************************************************** Jhove - JSTOR/Harvard Object + * Validation Environment Copyright 2003 by JSTOR and the President and Fellows of Harvard College + * ******************************************************************** + */ package edu.harvard.hul.ois.jhove.module.pdf; import edu.harvard.hul.ois.jhove.module.PdfModule; @@ -11,404 +11,362 @@ import java.util.logging.*; /** - * Class for element of PDF document structure tree. + * Class for element of PDF document structure tree. * - * @see StructureTree + * @see StructureTree */ -public class StructureElement -{ - private StructureTree _tree; - private PdfDictionary _dict; - private PdfModule _module; - private List children; - private String _structType; - private boolean _structIsInline; - private boolean _attrIsBlock; - - private Logger _logger; +public class StructureElement { + private StructureTree _tree; + private PdfDictionary _dict; + private PdfModule _module; + private List children; + private String _structType; + private boolean _structIsInline; + private boolean _attrIsBlock; - /* Attributes which should occur only in block level elements */ - private static final String blockLevelAttrs [] = { - "SpaceBefore", "SpaceAfter", "StartIndent", - "EndIndent", "TextIndent", "TextAlign", "BBox", - "Width", "Height", "BlockAlign", "InlineAlign" - }; + private Logger _logger; + /* Attributes which should occur only in block level elements */ + private static final String blockLevelAttrs[] = { + "SpaceBefore", + "SpaceAfter", + "StartIndent", + "EndIndent", + "TextIndent", + "TextAlign", + "BBox", + "Width", + "Height", + "BlockAlign", + "InlineAlign" + }; + /** + * Constructor. + * + * @param dict A PdfDictionary corresponding to a structure element + * @param tree The root StructureTree object + */ + public StructureElement(PdfDictionary dict, StructureTree tree) throws PdfException { + _logger = Logger.getLogger("edu.harvard.hul.ois.jhove.module"); + _tree = tree; + _dict = dict; + _module = tree.getModule(); + _structType = null; - /** - * Constructor. - * @param dict A PdfDictionary corresponding to a structure - * element - * @param tree The root StructureTree object - */ - public StructureElement (PdfDictionary dict, StructureTree tree) - throws PdfException - { - _logger = Logger.getLogger ("edu.harvard.hul.ois.jhove.module"); - _tree = tree; - _dict = dict; - _module = tree.getModule (); - _structType = null; - - // If this element has a standard structure type, find it. - try { - PdfObject s = _module.resolveIndirectObject (dict.get ("S")); - Token tok = ((PdfSimpleObject) s).getToken (); - String st = ((Name) tok).getValue (); - st = _tree.dereferenceStructType (st); - if (StdStructTypes.includes (st)) { - _structType = st; + // If this element has a standard structure type, find it. + try { + PdfObject s = _module.resolveIndirectObject(dict.get("S")); + Token tok = ((PdfSimpleObject) s).getToken(); + String st = ((Name) tok).getValue(); + st = _tree.dereferenceStructType(st); + if (StdStructTypes.includes(st)) { + _structType = st; + } + } catch (IOException e) { + _logger.warning(e.getClass().getName()); + } + } + + /** + * Build this element's subtree, if any This checks the "K" entry in the dictionary and locates + * all referened structure elements. These are put into StructureElement objects, which have their + * own subtrees built, and these StructureElements are accumulated into children. + */ + public void buildSubtree() throws PdfException { + _logger.info(MessageConstants.LOG_SUBTREE_BUILDING); + PdfObject k = null; + try { + k = _module.resolveIndirectObject(_dict.get("K")); + } catch (IOException e) { + throw new PdfInvalidException(MessageConstants.PDF_HUL_58); // PDF-HUL-58 + } + children = null; + + // The "K" element is complicated, having five variants. + if (k instanceof PdfSimpleObject) { + // A marked-content identifier. We don't explore further. + return; + } else if (k instanceof PdfDictionary) { + // Could be any of three kinds of dictionaries: + // - A marked-content reference dictionary + // - A PDF object reference dictionary + // - A structure element reference dictionary + // The only one we check seriously is a structure element. + _logger.info(MessageConstants.LOG_K_ELEM_IS_DICT); + PdfDictionary kdict = (PdfDictionary) k; + if (isStructElem(kdict)) { + PdfObject kidsObject = kdict.get("K"); + if (kidsObject instanceof PdfSimpleObject) { + PdfSimpleObject kids = (PdfSimpleObject) kidsObject; + Token tok = kids.getToken(); + if (tok instanceof Numeric) { + // if the kids value is zero then there are no child objects; exit method + if (((Numeric) tok).getValue() == 0) { + children = null; + return; } + } } - catch (IOException e) { - _logger.warning(e.getClass().getName()); - } - } - - /** - * Build this element's subtree, if any - * This checks the "K" entry in the dictionary and - * locates all referened structure elements. These - * are put into StructureElement objects, which have - * their own subtrees built, and these StructureElements - * are accumulated into children. - */ - public void buildSubtree () throws PdfException - { - _logger.info(MessageConstants.LOG_SUBTREE_BUILDING); - PdfObject k = null; + StructureElement se = new StructureElement(kdict, _tree); + se.buildSubtree(); + se.checkAttributes(); + children = new ArrayList<>(1); + children.add(se); + } else if (!isMarkedContent(kdict) && !isObjectRef(kdict)) { + throw new PdfInvalidException(MessageConstants.PDF_HUL_49); // PDF-HUL-49 + } + } else if (k instanceof PdfArray) { + _logger.info(MessageConstants.LOG_K_ELEM_IS_ARRY); + Vector kvec = ((PdfArray) k).getContent(); + children = new LinkedList<>(); + for (int i = 0; i < kvec.size(); i++) { + PdfObject kelem = kvec.elementAt(i); try { - k = _module.resolveIndirectObject (_dict.get ("K")); - } - catch (IOException e) { - throw new PdfInvalidException(MessageConstants.PDF_HUL_58); // PDF-HUL-58 - } - children = null; - - // The "K" element is complicated, having five variants. - if (k instanceof PdfSimpleObject) { - // A marked-content identifier. We don't explore further. - return; - } - else if (k instanceof PdfDictionary) { - // Could be any of three kinds of dictionaries: - // - A marked-content reference dictionary - // - A PDF object reference dictionary - // - A structure element reference dictionary - // The only one we check seriously is a structure element. - _logger.info (MessageConstants.LOG_K_ELEM_IS_DICT); - PdfDictionary kdict = (PdfDictionary) k; - if (isStructElem (kdict)) { - PdfObject kidsObject = kdict.get("K"); - if(kidsObject instanceof PdfSimpleObject) { - PdfSimpleObject kids = (PdfSimpleObject)kidsObject; - Token tok = kids.getToken(); - if(tok instanceof Numeric) { - //if the kids value is zero then there are no child objects; exit method - if(((Numeric)tok).getValue()==0) { - children = null; - return; - } - } - } - StructureElement se = - new StructureElement (kdict, _tree); - se.buildSubtree (); - se.checkAttributes (); - children = new ArrayList<> (1); - children.add (se); - } - else if (!isMarkedContent (kdict) && !isObjectRef (kdict)) { - throw new PdfInvalidException - (MessageConstants.PDF_HUL_49); // PDF-HUL-49 - } + kelem = _module.resolveIndirectObject(kelem); + } catch (IOException e) { } - else if (k instanceof PdfArray) { - _logger.info (MessageConstants.LOG_K_ELEM_IS_ARRY); - Vector kvec = ((PdfArray) k).getContent (); - children = new LinkedList<> (); - for (int i = 0; i < kvec.size (); i++) { - PdfObject kelem = kvec.elementAt (i); - try { - kelem = _module.resolveIndirectObject (kelem); - } - catch (IOException e) {} - if (kelem instanceof PdfDictionary) { - PdfDictionary kdict = (PdfDictionary) kelem; - if (isStructElem (kdict)) { - _logger.info (MessageConstants.LOG_SUBTREE_BUILDING); + if (kelem instanceof PdfDictionary) { + PdfDictionary kdict = (PdfDictionary) kelem; + if (isStructElem(kdict)) { + _logger.info(MessageConstants.LOG_SUBTREE_BUILDING); - //check for non-zero before creating a new StructureElement - //cf. Govdocs file http://digitalcorpora.org/corp/nps/files/govdocs1/000/000153.pdf - //object 717 that has "/K 0" enters infinite(?) loop without this check - PdfObject kidsObject = kdict.get("K"); - if(kidsObject instanceof PdfSimpleObject) { - PdfSimpleObject kids = (PdfSimpleObject)kidsObject; - Token tok = kids.getToken(); - if (tok instanceof Numeric && ((Numeric)tok).getValue() == 0) { - //if the kids value is zero then there are no child objects; exit method - _logger.info(MessageConstants.LOG_NO_CHILD_OBJS); - children = null; - return; - - } - } StructureElement se = - new StructureElement (kdict, _tree); - se.buildSubtree (); - se.checkAttributes (); - children.add (se); - } - } - } - // It's possible that none of the elements of the array - // were structure elements. In this case, we change - // children to null rather than have to check for an - // empty vector. - if (children.isEmpty ()) { - _logger.info (MessageConstants.LOG_NO_CHILD_STRUCT_ELEM); + // check for non-zero before creating a new StructureElement + // cf. Govdocs file http://digitalcorpora.org/corp/nps/files/govdocs1/000/000153.pdf + // object 717 that has "/K 0" enters infinite(?) loop without this check + PdfObject kidsObject = kdict.get("K"); + if (kidsObject instanceof PdfSimpleObject) { + PdfSimpleObject kids = (PdfSimpleObject) kidsObject; + Token tok = kids.getToken(); + if (tok instanceof Numeric && ((Numeric) tok).getValue() == 0) { + // if the kids value is zero then there are no child objects; exit method + _logger.info(MessageConstants.LOG_NO_CHILD_OBJS); children = null; + return; + } } + StructureElement se = new StructureElement(kdict, _tree); + se.buildSubtree(); + se.checkAttributes(); + children.add(se); + } } - - // If this is a transient tree, we don't need the children - // after we're done. Clear the tree to save memory. - if (_tree.isTransient()) { - children = null; - } + } + // It's possible that none of the elements of the array + // were structure elements. In this case, we change + // children to null rather than have to check for an + // empty vector. + if (children.isEmpty()) { + _logger.info(MessageConstants.LOG_NO_CHILD_STRUCT_ELEM); + children = null; + } } - /** - * Determine if the attributes of this element are - * valid. If errors are detected, throws a PdfInvalidException. - */ - public void checkAttributes () throws PdfException { - PdfObject attr; - - // Use the variables _structIsInline and _attrIsBlock to - // note when we've got a block-level-only attribute in - // an inline structure element. We initially set - // _structIsInline based on the structure type, but this - // may be overridden by the Placement attribute. - // Figure elements occupy an ambiguous position, so we - // don't mark them as ILSE's. Also, TR, TH and TD are - // defined to be neither BLSE's nor ILSE's. - _attrIsBlock = false; - _structIsInline = !"Figure".equals (_structType) && - !"TH".equals (_structType) && - !"TD".equals (_structType) && - !"TR".equals (_structType) && - !StdStructTypes.isBlockLevel (_structType); - - try { - attr = _module.resolveIndirectObject (_dict.get ("A")); - } - catch (Exception e) { - throw new PdfInvalidException (MessageConstants.PDF_HUL_50); // PDF-HUL-50 - } - if (attr == null) { - // no attributes is fine - return; - } - if (attr instanceof PdfArray) { - // If we have an array, it may contain elements and - // revision numbers. A revision number may follow - // an element, but there doesn't have to be one. - Vector attrVec = ((PdfArray) attr).getContent (); - for (int i = 0; i < attrVec.size (); i++) { - PdfObject attrElem; - try { - attrElem = _module.resolveIndirectObject - (attrVec.elementAt (i)); - } - catch (IOException e) { - _logger.log(Level.INFO, MessageConstants.PDF_HUL_51.getMessage(), e); - throw new PdfInvalidException (MessageConstants.PDF_HUL_51); // PDF-HUL-51 - } - if (attrElem instanceof PdfDictionary) { - checkAttribute ((PdfDictionary) attrElem); - } - else if (attrElem instanceof PdfSimpleObject) { - try { - Numeric revnum = (Numeric) - ((PdfSimpleObject)attrElem).getToken (); - } - catch (Exception e) { - _logger.log(Level.INFO, MessageConstants.LOG_REVISION_NUM_RETRIEVAL_EXCEP, e); - throw new PdfInvalidException (MessageConstants.PDF_HUL_52); // PDF-HUL-52 - } - } - else { - throw new PdfInvalidException (MessageConstants.PDF_HUL_53); // PDF-HUL-53 - } - } - } - else if (attr instanceof PdfDictionary) { - checkAttribute ((PdfDictionary) attr); - } - else { - throw new PdfInvalidException (MessageConstants.PDF_HUL_54); // PDF-HUL-54 - } - if (_structIsInline && _attrIsBlock) { - throw new PdfInvalidException (MessageConstants.PDF_HUL_55); // PDF-HUL-55 - } + // If this is a transient tree, we don't need the children + // after we're done. Clear the tree to save memory. + if (_tree.isTransient()) { + children = null; } + } + /** + * Determine if the attributes of this element are valid. If errors are detected, throws a + * PdfInvalidException. + */ + public void checkAttributes() throws PdfException { + PdfObject attr; - /* Check if an attribute dictionary is reasonable. */ - private void checkAttribute (PdfDictionary attr) - throws PdfException - { - try { - // Must have an entry named "O", whose value is a name. - //PdfSimpleObject plugin = (PdfSimpleObject) attr.get ("O"); - //Name tok = (Name) plugin.getToken (); + // Use the variables _structIsInline and _attrIsBlock to + // note when we've got a block-level-only attribute in + // an inline structure element. We initially set + // _structIsInline based on the structure type, but this + // may be overridden by the Placement attribute. + // Figure elements occupy an ambiguous position, so we + // don't mark them as ILSE's. Also, TR, TH and TD are + // defined to be neither BLSE's nor ILSE's. + _attrIsBlock = false; + _structIsInline = + !"Figure".equals(_structType) + && !"TH".equals(_structType) + && !"TD".equals(_structType) + && !"TR".equals(_structType) + && !StdStructTypes.isBlockLevel(_structType); - // If it has a Placement entry with a value other than - // "Inline", then we allow block level attributes. - PdfSimpleObject placement = - (PdfSimpleObject) attr.get ("Placement"); - if (placement != null && - !"Inline".equals (placement.getStringValue ())) { - _structIsInline = false; - } - // Though I don't think the Adobe PDF bible actually - // says so, it appears that the "attributes" are - // simply other keys in the attribute dictionary. - // Remember if we see attributes that can't go in BLSE's; - // we'll check later if we're actually in a BLSE. - if (attrIsBlockLevel (attr)) { - _attrIsBlock = true; - } + try { + attr = _module.resolveIndirectObject(_dict.get("A")); + } catch (Exception e) { + throw new PdfInvalidException(MessageConstants.PDF_HUL_50); // PDF-HUL-50 + } + if (attr == null) { + // no attributes is fine + return; + } + if (attr instanceof PdfArray) { + // If we have an array, it may contain elements and + // revision numbers. A revision number may follow + // an element, but there doesn't have to be one. + Vector attrVec = ((PdfArray) attr).getContent(); + for (int i = 0; i < attrVec.size(); i++) { + PdfObject attrElem; + try { + attrElem = _module.resolveIndirectObject(attrVec.elementAt(i)); + } catch (IOException e) { + _logger.log(Level.INFO, MessageConstants.PDF_HUL_51.getMessage(), e); + throw new PdfInvalidException(MessageConstants.PDF_HUL_51); // PDF-HUL-51 } - catch (Exception e) { - throw new PdfInvalidException (MessageConstants.PDF_HUL_56); // PDF-HUL-56 + if (attrElem instanceof PdfDictionary) { + checkAttribute((PdfDictionary) attrElem); + } else if (attrElem instanceof PdfSimpleObject) { + try { + Numeric revnum = (Numeric) ((PdfSimpleObject) attrElem).getToken(); + } catch (Exception e) { + _logger.log(Level.INFO, MessageConstants.LOG_REVISION_NUM_RETRIEVAL_EXCEP, e); + throw new PdfInvalidException(MessageConstants.PDF_HUL_52); // PDF-HUL-52 + } + } else { + throw new PdfInvalidException(MessageConstants.PDF_HUL_53); // PDF-HUL-53 } + } + } else if (attr instanceof PdfDictionary) { + checkAttribute((PdfDictionary) attr); + } else { + throw new PdfInvalidException(MessageConstants.PDF_HUL_54); // PDF-HUL-54 + } + if (_structIsInline && _attrIsBlock) { + throw new PdfInvalidException(MessageConstants.PDF_HUL_55); // PDF-HUL-55 } + } + /* Check if an attribute dictionary is reasonable. */ + private void checkAttribute(PdfDictionary attr) throws PdfException { + try { + // Must have an entry named "O", whose value is a name. + // PdfSimpleObject plugin = (PdfSimpleObject) attr.get ("O"); + // Name tok = (Name) plugin.getToken (); + // If it has a Placement entry with a value other than + // "Inline", then we allow block level attributes. + PdfSimpleObject placement = (PdfSimpleObject) attr.get("Placement"); + if (placement != null && !"Inline".equals(placement.getStringValue())) { + _structIsInline = false; + } + // Though I don't think the Adobe PDF bible actually + // says so, it appears that the "attributes" are + // simply other keys in the attribute dictionary. + // Remember if we see attributes that can't go in BLSE's; + // we'll check later if we're actually in a BLSE. + if (attrIsBlockLevel(attr)) { + _attrIsBlock = true; + } + } catch (Exception e) { + throw new PdfInvalidException(MessageConstants.PDF_HUL_56); // PDF-HUL-56 + } + } - /* See if a dictionary is a structure element. - We identify it by the S and P elements, which are - required, and by making sure that the Type element, - if present, has a value of "StructElem". - */ - private boolean isStructElem (PdfDictionary elem) - { - try { - PdfObject typ = elem.get ("Type"); - if (typ != null && !"StructElem".equals - (((PdfSimpleObject) typ).getStringValue ())) { - return false; - } + /* See if a dictionary is a structure element. + We identify it by the S and P elements, which are + required, and by making sure that the Type element, + if present, has a value of "StructElem". + */ + private boolean isStructElem(PdfDictionary elem) { + try { + PdfObject typ = elem.get("Type"); + if (typ != null && !"StructElem".equals(((PdfSimpleObject) typ).getStringValue())) { + return false; + } - PdfObject s = _module.resolveIndirectObject (elem.get ("S")); - // The structure type is supposed to be one of - // a list of known structure types, or else is - // mapped to one through the role map dictionary. - // For the moment, just make sure it's a name. - if (!(s instanceof PdfSimpleObject)) { - return false; - } - Token tok = ((PdfSimpleObject) s).getToken (); - if (!(tok instanceof Name)) { - return false; - } - // It appears that there really isn't a requirement - // to have structure types belong to the standard types. - // Conditionalize this code out, pending more info. - String st = ((Name) tok).getValue (); - st = _tree.dereferenceStructType (st); - // TODO: This check is indeed wrong and should be removed altogether - boolean checkStandardTypes = false; - if (checkStandardTypes) { - if (!StdStructTypes.includes (st)) { - throw new PdfInvalidException(MessageConstants.PDF_HUL_57); - } - } - else { - // The structure type is a standard one. - } - // The parent reference must be an indirect reference. - // The documentation says it must refer to another - // structure element dictionary, but it seems that it - // must also be able to refer to the structure tree root. - // I'll allow both. - PdfObject pref = elem.get ("P"); - if (!(pref instanceof PdfIndirectObj)) { - return false; - } - // Make sure it refers to a dictionary (at least). - PdfDictionary p = (PdfDictionary) - _module.resolveIndirectObject (pref); - PdfSimpleObject ptype = (PdfSimpleObject) p.get ("Type"); - if (ptype != null) { - String typename = ptype.getStringValue (); - if (!"StructTreeRoot".equals (typename) && - !"StructElem".equals (typename)) { - return false; - } - } - // Passed all tests. - return true; + PdfObject s = _module.resolveIndirectObject(elem.get("S")); + // The structure type is supposed to be one of + // a list of known structure types, or else is + // mapped to one through the role map dictionary. + // For the moment, just make sure it's a name. + if (!(s instanceof PdfSimpleObject)) { + return false; + } + Token tok = ((PdfSimpleObject) s).getToken(); + if (!(tok instanceof Name)) { + return false; + } + // It appears that there really isn't a requirement + // to have structure types belong to the standard types. + // Conditionalize this code out, pending more info. + String st = ((Name) tok).getValue(); + st = _tree.dereferenceStructType(st); + // TODO: This check is indeed wrong and should be removed altogether + boolean checkStandardTypes = false; + if (checkStandardTypes) { + if (!StdStructTypes.includes(st)) { + throw new PdfInvalidException(MessageConstants.PDF_HUL_57); } - catch (Exception e) { - // Some assumption was violated - return false; + } else { + // The structure type is a standard one. + } + // The parent reference must be an indirect reference. + // The documentation says it must refer to another + // structure element dictionary, but it seems that it + // must also be able to refer to the structure tree root. + // I'll allow both. + PdfObject pref = elem.get("P"); + if (!(pref instanceof PdfIndirectObj)) { + return false; + } + // Make sure it refers to a dictionary (at least). + PdfDictionary p = (PdfDictionary) _module.resolveIndirectObject(pref); + PdfSimpleObject ptype = (PdfSimpleObject) p.get("Type"); + if (ptype != null) { + String typename = ptype.getStringValue(); + if (!"StructTreeRoot".equals(typename) && !"StructElem".equals(typename)) { + return false; } + } + // Passed all tests. + return true; + } catch (Exception e) { + // Some assumption was violated + return false; } + } - /* See if an attribute dictionary has attributes which - are permitted only at block level. */ - private static boolean attrIsBlockLevel (PdfDictionary attrDict) - { - for (int i = 0; i < blockLevelAttrs.length; i++) { - if (attrDict.get (blockLevelAttrs[i]) != null) { - return true; - } - } - return false; + /* See if an attribute dictionary has attributes which + are permitted only at block level. */ + private static boolean attrIsBlockLevel(PdfDictionary attrDict) { + for (int i = 0; i < blockLevelAttrs.length; i++) { + if (attrDict.get(blockLevelAttrs[i]) != null) { + return true; + } } + return false; + } - /* Determine if a dictionary is a marked content dictionary. - See Table 9.11 in the PDF 1.4 book. */ - private boolean isMarkedContent (PdfDictionary dict) - { - try { - PdfSimpleObject typeObj = - (PdfSimpleObject) dict.get ("Type"); - if (!"MCR".equals (typeObj.getStringValue ())) { - return false; - } - // An MCID entry is required. - PdfSimpleObject mcidObj = - (PdfSimpleObject) _module.resolveIndirectObject - (dict.get ("MCID")); - return mcidObj != null; - } - catch (Exception e) { - return false; - } + /* Determine if a dictionary is a marked content dictionary. + See Table 9.11 in the PDF 1.4 book. */ + private boolean isMarkedContent(PdfDictionary dict) { + try { + PdfSimpleObject typeObj = (PdfSimpleObject) dict.get("Type"); + if (!"MCR".equals(typeObj.getStringValue())) { + return false; + } + // An MCID entry is required. + PdfSimpleObject mcidObj = (PdfSimpleObject) _module.resolveIndirectObject(dict.get("MCID")); + return mcidObj != null; + } catch (Exception e) { + return false; } + } - /* Determine if a dictionary is an object reference dictionary, - as in table 9.12. */ - private boolean isObjectRef (PdfDictionary dict) - { - try { - PdfSimpleObject typeObj = - (PdfSimpleObject) dict.get ("Type"); - if (!"OBJR".equals (typeObj.getStringValue ())) { - return false; - } - // An Obj entry is required. Must be an indirect object. - PdfObject obj = _module.resolveIndirectObject - (dict.get ("Obj")); - return obj != null; - } - catch (Exception e) { - return false; - } - + /* Determine if a dictionary is an object reference dictionary, + as in table 9.12. */ + private boolean isObjectRef(PdfDictionary dict) { + try { + PdfSimpleObject typeObj = (PdfSimpleObject) dict.get("Type"); + if (!"OBJR".equals(typeObj.getStringValue())) { + return false; + } + // An Obj entry is required. Must be an indirect object. + PdfObject obj = _module.resolveIndirectObject(dict.get("Obj")); + return obj != null; + } catch (Exception e) { + return false; } + } } diff --git a/jhove-modules/pdf-hul/src/main/java/edu/harvard/hul/ois/jhove/module/pdf/StructureTree.java b/jhove-modules/pdf-hul/src/main/java/edu/harvard/hul/ois/jhove/module/pdf/StructureTree.java index 073d8b47b..213558987 100644 --- a/jhove-modules/pdf-hul/src/main/java/edu/harvard/hul/ois/jhove/module/pdf/StructureTree.java +++ b/jhove-modules/pdf-hul/src/main/java/edu/harvard/hul/ois/jhove/module/pdf/StructureTree.java @@ -1,8 +1,8 @@ -/********************************************************************** - * Jhove - JSTOR/Harvard Object Validation Environment - * Copyright 2003 by JSTOR and the President and Fellows of Harvard College - **********************************************************************/ - +/** + * ******************************************************************** Jhove - JSTOR/Harvard Object + * Validation Environment Copyright 2003 by JSTOR and the President and Fellows of Harvard College + * ******************************************************************** + */ package edu.harvard.hul.ois.jhove.module.pdf; import edu.harvard.hul.ois.jhove.module.PdfModule; @@ -11,225 +11,183 @@ import java.util.logging.Logger; /** - * Class for PDF document structure tree. - * See section 9.6, "Logical Structure," of PDF Reference, - * Version 1.4, for an explanation of the document structure. + * Class for PDF document structure tree. See section 9.6, "Logical Structure," of PDF Reference, + * Version 1.4, for an explanation of the document structure. * - * @see StructureElement + * @see StructureElement */ -public class StructureTree -{ - /** Logger for this class. */ - protected Logger _logger; - - private PdfModule _module; - private PdfDictionary _rootDict; - private PdfDictionary _roleMap; - private boolean _present; - private boolean _valid; - private boolean _transient; - - /** - * Constructor. If there is a document structure tree, - * this fills in the appropriate information. If there isn't, - * it does nothing. Call {@code isPresent} to determine whether - * there is a document structure tree. A {@code PdfInvalidException} - * may be thrown if there is a structure tree but it is invalid. - * - * @param module The PdfModule under which we're operating - */ - public StructureTree (PdfModule module) - { - this (module, false); - } - - /** - * Constructor with transient flag. Calling this can save a lot of memory - * if the tree is being validated as it's built (which happens to be the - * only case, but I don't want to throw out the more general code). - */ - public StructureTree (PdfModule module, - boolean tranzhent) { - _logger = Logger.getLogger ("edu.harvard.hul.ois.jhove.module"); - - _module = module; - _transient = tranzhent; - //_raf = raf; - //_parser = parser; - try { - PdfDictionary docCatDict = module.getCatalogDict (); - // There must be an entry in the catalog dictionary - // named StructTreeRoot. If there isn't, set _present - // to false. - _rootDict = null; - try { - _rootDict = (PdfDictionary) _module.resolveIndirectObject - (docCatDict.get ("StructTreeRoot")); - } - catch (IOException e) {} - if (_rootDict == null) { - _present = false; - _valid = false; - return; - } - _present = true; - validateRoot (); - checkRoleMap (); - checkChildren (); - _valid = true; - } - catch (Exception e) { - _valid = false; - } +public class StructureTree { + /** Logger for this class. */ + protected Logger _logger; + + private PdfModule _module; + private PdfDictionary _rootDict; + private PdfDictionary _roleMap; + private boolean _present; + private boolean _valid; + private boolean _transient; + + /** + * Constructor. If there is a document structure tree, this fills in the appropriate information. + * If there isn't, it does nothing. Call {@code isPresent} to determine whether there is a + * document structure tree. A {@code PdfInvalidException} may be thrown if there is a structure + * tree but it is invalid. + * + * @param module The PdfModule under which we're operating + */ + public StructureTree(PdfModule module) { + this(module, false); + } + + /** + * Constructor with transient flag. Calling this can save a lot of memory if the tree is being + * validated as it's built (which happens to be the only case, but I don't want to throw out the + * more general code). + */ + public StructureTree(PdfModule module, boolean tranzhent) { + _logger = Logger.getLogger("edu.harvard.hul.ois.jhove.module"); + + _module = module; + _transient = tranzhent; + // _raf = raf; + // _parser = parser; + try { + PdfDictionary docCatDict = module.getCatalogDict(); + // There must be an entry in the catalog dictionary + // named StructTreeRoot. If there isn't, set _present + // to false. + _rootDict = null; + try { + _rootDict = (PdfDictionary) _module.resolveIndirectObject(docCatDict.get("StructTreeRoot")); + } catch (IOException e) { + } + if (_rootDict == null) { + _present = false; + _valid = false; + return; + } + _present = true; + validateRoot(); + checkRoleMap(); + checkChildren(); + _valid = true; + } catch (Exception e) { + _valid = false; } - - /** - * Returns true if and only if the document - * structure exists. - */ - public boolean isPresent () - { - return _present; + } + + /** Returns true if and only if the document structure exists. */ + public boolean isPresent() { + return _present; + } + + /** Returns true if and only if no errors were detected. */ + public boolean isValid() { + return _valid; + } + + /** Returns the module associated with this object. */ + public PdfModule getModule() { + return _module; + } + + protected boolean isTransient() { + return _transient; + } + + /** + * Dereference a name in the role map. If there is no role map, or if the parameter is not mapped + * by the role map, the original parameter will be returned. The string will be looked up through + * multiple levels in the role map. The maximum number of levels is limited to 50, in case of + * circular mappings. The value returned will be null if the role map contains invalid data or the + * limit of 50 lookups is reached. + */ + public String dereferenceStructType(String st) { + if (_roleMap == null) { + return st; } - - - /** - * Returns true if and only if no errors were - * detected. - */ - public boolean isValid () - { - return _valid; + // There could be a circular mapping, so we limit the + // number of concatenated lookups. + for (int i = 0; i < 50; i++) { + try { + PdfSimpleObject mapped = (PdfSimpleObject) _roleMap.get(st); + if (mapped == null) { + return st; + } + st = mapped.getStringValue(); + } catch (Exception e) { + return null; // BAD dictionary! No mapping! + } } - - - /** Returns the module associated with this object. */ - public PdfModule getModule () - { - return _module; + return null; // Looks like an infinite loop + } + + /* See if the root is valid. If not, throw a PDFException. */ + private void validateRoot() throws PdfException { + try { + PdfSimpleObject typ = (PdfSimpleObject) _rootDict.get("Type"); + if (!"StructTreeRoot".equals(typ.getStringValue())) { + throw new PdfInvalidException(MessageConstants.PDF_HUL_59); // PDF-HUL-59 + } + } catch (NullPointerException | ClassCastException e) { + throw new PdfInvalidException(MessageConstants.PDF_HUL_60); // PDF-HUL-60 } - - protected boolean isTransient () { - return _transient; + } + + /** + * Replaces a string with a string to which the role map maps it. This may involve multiple levels + * of lookup. + */ + + /* Build a list of the children of the root. The + elements of the list are StructureElements. + Returns null if there are none. */ + private void checkChildren() throws PdfException { + List kidsList = null; + PdfObject kids = null; + try { + kids = _module.resolveIndirectObject(_rootDict.get("K")); + } catch (IOException e) { } - - /** - * Dereference a name in the role map. - * If there is no role map, or if the parameter is not - * mapped by the role map, the original parameter will - * be returned. The string will be looked up through - * multiple levels in the role map. The maximum number - * of levels is limited to 50, in case of circular - * mappings. The value returned will be null if the - * role map contains invalid data or the limit of 50 - * lookups is reached. - */ - public String dereferenceStructType (String st) - { - if (_roleMap == null) { - return st; - } - // There could be a circular mapping, so we limit the - // number of concatenated lookups. - for (int i = 0; i < 50; i++) { - try { - PdfSimpleObject mapped = - (PdfSimpleObject) _roleMap.get (st); - if (mapped == null) { - return st; - } - st = mapped.getStringValue (); - } - catch (Exception e) { - return null; // BAD dictionary! No mapping! - } - } - return null; // Looks like an infinite loop + if (kids == null) { + return; } - - /* See if the root is valid. If not, throw a PDFException. */ - private void validateRoot () throws PdfException - { + if (kids instanceof PdfDictionary) { + // Only one child + StructureElement se = new StructureElement((PdfDictionary) kids, this); + se.buildSubtree(); + se.checkAttributes(); + return; + } else if (kids instanceof PdfArray) { + // Multiple children + Vector kidsVec = ((PdfArray) kids).getContent(); + kidsList = new ArrayList<>(kidsVec.size()); + for (int i = 0; i < kidsVec.size(); i++) { + PdfObject kid; try { - PdfSimpleObject typ = - (PdfSimpleObject)_rootDict.get ("Type"); - if (!"StructTreeRoot".equals (typ.getStringValue ())) { - throw new PdfInvalidException (MessageConstants.PDF_HUL_59); // PDF-HUL-59 - } - } - catch (NullPointerException | ClassCastException e) { - throw new PdfInvalidException (MessageConstants.PDF_HUL_60); // PDF-HUL-60 + kid = _module.resolveIndirectObject(kidsVec.elementAt(i)); + } catch (IOException e) { + throw new PdfMalformedException(MessageConstants.PDF_HUL_61); // PDF-HUL-61 } + StructureElement se = new StructureElement((PdfDictionary) kid, this); + se.buildSubtree(); + se.checkAttributes(); + kidsList.add(se); + } + } else { + throw new PdfInvalidException(MessageConstants.PDF_HUL_62); // PDF-HUL-62 } - - /** - * Replaces a string with a string to which the role map - * maps it. This may involve multiple levels of lookup. - */ - - /* Build a list of the children of the root. The - elements of the list are StructureElements. - Returns null if there are none. */ - private void checkChildren () throws PdfException - { - List kidsList = null; - PdfObject kids = null; - try { - kids = _module.resolveIndirectObject - (_rootDict.get ("K")); - } - catch (IOException e) {} - if (kids == null) { - return; - } - - if (kids instanceof PdfDictionary) { - // Only one child - StructureElement se = new StructureElement - ((PdfDictionary) kids, this); - se.buildSubtree (); - se.checkAttributes (); - return; - } - else if (kids instanceof PdfArray) { - // Multiple children - Vector kidsVec = ((PdfArray) kids).getContent (); - kidsList = new ArrayList<> (kidsVec.size ()); - for (int i = 0; i < kidsVec.size (); i++) { - PdfObject kid; - try { - kid = _module.resolveIndirectObject - (kidsVec.elementAt (i)); - } - catch (IOException e) { - throw new PdfMalformedException (MessageConstants.PDF_HUL_61); // PDF-HUL-61 - } - StructureElement se = new StructureElement - ((PdfDictionary) kid, this); - se.buildSubtree (); - se.checkAttributes (); - kidsList.add (se); - } - } - else { - throw new PdfInvalidException (MessageConstants.PDF_HUL_62); // PDF-HUL-62 - } + } + + /* Extract and save the role map, if any. Throw an + exception if RoleMap names something that isn't + a dictionary. It's legitimate for roleMap to be null. */ + private void checkRoleMap() throws PdfException { + try { + _roleMap = (PdfDictionary) _module.resolveIndirectObject(_rootDict.get("RoleMap")); + } catch (Exception e) { + throw new PdfInvalidException(MessageConstants.PDF_HUL_63); // PDF-HUL-63 } - - - /* Extract and save the role map, if any. Throw an - exception if RoleMap names something that isn't - a dictionary. It's legitimate for roleMap to be null. */ - private void checkRoleMap () throws PdfException - { - try { - _roleMap = (PdfDictionary) _module.resolveIndirectObject - (_rootDict.get ("RoleMap")); - } - catch (Exception e) { - throw new PdfInvalidException (MessageConstants.PDF_HUL_63); // PDF-HUL-63 - } - } - + } } diff --git a/jhove-modules/pdf-hul/src/main/java/edu/harvard/hul/ois/jhove/module/pdf/TaggedProfile.java b/jhove-modules/pdf-hul/src/main/java/edu/harvard/hul/ois/jhove/module/pdf/TaggedProfile.java index d099b5dc8..c8360983d 100644 --- a/jhove-modules/pdf-hul/src/main/java/edu/harvard/hul/ois/jhove/module/pdf/TaggedProfile.java +++ b/jhove-modules/pdf-hul/src/main/java/edu/harvard/hul/ois/jhove/module/pdf/TaggedProfile.java @@ -1,72 +1,60 @@ - package edu.harvard.hul.ois.jhove.module.pdf; import edu.harvard.hul.ois.jhove.module.*; /** - * PDF profile checker for Tagged PDF documents. - * See section 9.7, "Tagged PDF", of the PDF Reference, - * Version 1.4, for an explanation of tagged PDF. + * PDF profile checker for Tagged PDF documents. See section 9.7, "Tagged PDF", of the PDF + * Reference, Version 1.4, for an explanation of tagged PDF. */ -public final class TaggedProfile extends PdfProfile -{ - /****************************************************************** - * PRIVATE CLASS FIELDS. - ******************************************************************/ - - /** - * Constructor. - * Creates a TaggedProfile object for subsequent testing. - * - * @param module The module under which we are checking the profile. - * - */ - public TaggedProfile (PdfModule module) - { - super (module); - _profileText = "Tagged PDF"; +public final class TaggedProfile extends PdfProfile { + /** + * **************************************************************** PRIVATE CLASS FIELDS. + * **************************************************************** + */ + + /** + * Constructor. Creates a TaggedProfile object for subsequent testing. + * + * @param module The module under which we are checking the profile. + */ + public TaggedProfile(PdfModule module) { + super(module); + _profileText = "Tagged PDF"; + } + + /** + * Returns true if the document satisfies the profile. We check only the + * dictionaries, not the stream contents. + */ + @Override + public boolean satisfiesThisProfile() { + try { + PdfDictionary docCatDict = _module.getCatalogDict(); + // An entry named markInfo must be in the doc catalog, + // and must be a dictionary. The dictionary must + // contain an entry named Marked, which must have a value + // of true. + PdfDictionary markInfo = + (PdfDictionary) _module.resolveIndirectObject(docCatDict.get("MarkInfo")); + if (markInfo == null) { + return false; + } + PdfSimpleObject marked = (PdfSimpleObject) markInfo.get("Marked"); + if (!marked.isTrue()) { + return false; + } + + // So much for MarkInfo. Now see if there is a + // valid structure tree. + StructureTree stree = new StructureTree(_module, true); + if (!stree.isPresent() || !stree.isValid()) { + return false; + } + } catch (Exception e) { + // An exception thrown anywhere means some assumption + // has been violated, so it doesn't meet the profile. + return false; } - - /** - * Returns true if the document satisfies the profile. - * We check only the dictionaries, not the stream contents. - * - */ - @Override - public boolean satisfiesThisProfile () - { - try { - PdfDictionary docCatDict = _module.getCatalogDict (); - // An entry named markInfo must be in the doc catalog, - // and must be a dictionary. The dictionary must - // contain an entry named Marked, which must have a value - // of true. - PdfDictionary markInfo = (PdfDictionary) - _module.resolveIndirectObject - (docCatDict.get ("MarkInfo")); - if (markInfo == null) { - return false; - } - PdfSimpleObject marked = - (PdfSimpleObject) markInfo.get ("Marked"); - if (!marked.isTrue ()) { - return false; - } - - // So much for MarkInfo. Now see if there is a - // valid structure tree. - StructureTree stree = new StructureTree (_module, true); - if (!stree.isPresent () || !stree.isValid ()) { - return false; - } - } - catch (Exception e) { - // An exception thrown anywhere means some assumption - // has been violated, so it doesn't meet the profile. - return false; - } - return true; // passed all tests - } - - + return true; // passed all tests + } } diff --git a/jhove-modules/pdf-hul/src/main/java/edu/harvard/hul/ois/jhove/module/pdf/Token.java b/jhove-modules/pdf-hul/src/main/java/edu/harvard/hul/ois/jhove/module/pdf/Token.java index 81e400010..47ef26767 100644 --- a/jhove-modules/pdf-hul/src/main/java/edu/harvard/hul/ois/jhove/module/pdf/Token.java +++ b/jhove-modules/pdf-hul/src/main/java/edu/harvard/hul/ois/jhove/module/pdf/Token.java @@ -1,41 +1,36 @@ -/********************************************************************** - * Jhove - JSTOR/Harvard Object Validation Environment - * Copyright 2003 by JSTOR and the President and Fellows of Harvard College - **********************************************************************/ - +/** + * ******************************************************************** Jhove - JSTOR/Harvard Object + * Validation Environment Copyright 2003 by JSTOR and the President and Fellows of Harvard College + * ******************************************************************** + */ package edu.harvard.hul.ois.jhove.module.pdf; /** - * Abstract class to encapsulate lexical tokens from a PDF - * file. Tokens include numbers, strings, names, delimiters (the - * open and close markers for dictionaries and arrays), and streams. - * There are a variety of subclasses for specific kinds of tokens. + * Abstract class to encapsulate lexical tokens from a PDF file. Tokens include numbers, strings, + * names, delimiters (the open and close markers for dictionaries and arrays), and streams. There + * are a variety of subclasses for specific kinds of tokens. */ -public abstract class Token -{ +public abstract class Token { + + /** Superclass constructor */ + public Token() {} + + /** + * Returns true if the token is one which the Parser treats as a unitary object. + * Everything but arrays and dictionaries are considered "simple" tokens for our purposes. + */ + public boolean isSimpleToken() { + return (!(this instanceof ArrayStart) + && !(this instanceof ArrayEnd) + && !(this instanceof DictionaryStart) + && !(this instanceof DictionaryEnd)); + } - /** Superclass constructor */ - public Token () - { - } - - /** - * Returns true if the token is one which the Parser - * treats as a unitary object. Everything but arrays and dictionaries - * are considered "simple" tokens for our purposes. - */ - public boolean isSimpleToken () - { - return (! (this instanceof ArrayStart) && - ! (this instanceof ArrayEnd) && - ! (this instanceof DictionaryStart) && - ! (this instanceof DictionaryEnd)); - } - - /** Returns true if this token is within PDF/A implementation - * limits. Always returns true unless overridden. */ - public boolean isPdfACompliant () - { - return true; - } + /** + * Returns true if this token is within PDF/A implementation limits. Always returns + * true unless overridden. + */ + public boolean isPdfACompliant() { + return true; + } } diff --git a/jhove-modules/pdf-hul/src/main/java/edu/harvard/hul/ois/jhove/module/pdf/Tokenizer.java b/jhove-modules/pdf-hul/src/main/java/edu/harvard/hul/ois/jhove/module/pdf/Tokenizer.java index f574faf42..2895d9ded 100644 --- a/jhove-modules/pdf-hul/src/main/java/edu/harvard/hul/ois/jhove/module/pdf/Tokenizer.java +++ b/jhove-modules/pdf-hul/src/main/java/edu/harvard/hul/ois/jhove/module/pdf/Tokenizer.java @@ -1,8 +1,8 @@ -/********************************************************************** - * Jhove - JSTOR/Harvard Object Validation Environment - * Copyright 2003 by JSTOR and the President and Fellows of Harvard College - **********************************************************************/ - +/** + * ******************************************************************** Jhove - JSTOR/Harvard Object + * Validation Environment Copyright 2003 by JSTOR and the President and Fellows of Harvard College + * ******************************************************************** + */ package edu.harvard.hul.ois.jhove.module.pdf; import java.io.EOFException; @@ -14,786 +14,677 @@ /** * Tokenizer for PDF files. * - * This is used in conjunction with the Parser, - * which assembles Tokens into higher-level constructs. + *

This is used in conjunction with the Parser, which assembles Tokens into higher-level + * constructs. */ -public abstract class Tokenizer -{ - /** Mapping between PDFDocEncoding and Unicode code points. */ - public static char [] PDFDOCENCODING = { - '\u0000','\u0001','\u0002','\u0003','\u0004','\u0005','\u0006','\u0007', - '\b' ,'\t' ,'\n' ,'\u000b','\f' ,'\r' ,'\u000e','\u000f', - '\u0010','\u0011','\u0012','\u0013','\u0014','\u0015','\u0016','\u0017', - '\u02d8','\u02c7','\u02c6','\u02d9','\u02dd','\u02db','\u02da','\u02dc', - '\u0020','\u0021','\"' ,'\u0023','\u0024','\u0025','\u0026','\'', - '\u0028','\u0029','\u002a','\u002b','\u002c','\u002d','\u002e','\u002f', - '\u0030','\u0031','\u0032','\u0033','\u0034','\u0035','\u0036','\u0037', - '\u0038','\u0039','\u003a','\u003b','\u003c','\u003d','\u003e','\u003f', - '\u0040','\u0041','\u0042','\u0043','\u0044','\u0045','\u0046','\u0047', - '\u0048','\u0049','\u004a','\u004b','\u004c','\u004d','\u004e','\u004f', - '\u0050','\u0051','\u0052','\u0053','\u0054','\u0055','\u0056','\u0057', - '\u0058','\u0059','\u005a','\u005b','\\' ,'\u005d','\u005e','\u005f', - '\u0060','\u0061','\u0062','\u0063','\u0064','\u0065','\u0066','\u0067', - '\u0068','\u0069','\u006a','\u006b','\u006c','\u006d','\u006e','\u006f', - '\u0070','\u0071','\u0072','\u0073','\u0074','\u0075','\u0076','\u0077', - '\u0078','\u0079','\u007a','\u007b','\u007c','\u007d','\u007e','\u007f', - '\u2022','\u2020','\u2021','\u2026','\u2003','\u2002','\u0192','\u2044', - '\u2039','\u203a','\u2212','\u2030','\u201e','\u201c','\u201d','\u2018', - '\u2019','\u201a','\u2122','\ufb01','\ufb02','\u0141','\u0152','\u0160', - '\u0178','\u017d','\u0131','\u0142','\u0153','\u0161','\u017e','\u009f', - '\u20ac','\u00a1','\u00a2','\u00a3','\u00a4','\u00a5','\u00a6','\u00a7', - '\u00a8','\u00a9','\u00aa','\u00ab','\u00ac','\u00ad','\u00ae','\u00af', - '\u00b0','\u00b1','\u00b2','\u00b3','\u00b4','\u00b5','\u00b6','\u00b7', - '\u00b8','\u00b9','\u00ba','\u00bb','\u00bc','\u00bd','\u00be','\u00bf', - '\u00c0','\u00c1','\u00c2','\u00c3','\u00c4','\u00c5','\u00c6','\u00c7', - '\u00c8','\u00c9','\u00ca','\u00cb','\u00cc','\u00cd','\u00ce','\u00cf', - '\u00d0','\u00d1','\u00d2','\u00d3','\u00d4','\u00d5','\u00d6','\u00d7', - '\u00d8','\u00d9','\u00da','\u00db','\u00dc','\u00dd','\u00de','\u00df', - '\u00e0','\u00e1','\u00e2','\u00e3','\u00e4','\u00e5','\u00e6','\u00e7', - '\u00e8','\u00e9','\u00ea','\u00eb','\u00ec','\u00ed','\u00ef','\u00ef', - '\u00f0','\u00f1','\u00f2','\u00f3','\u00f4','\u00f5','\u00f6','\u00f7', - '\u00f8','\u00f9','\u00fa','\u00fb','\u00fc','\u00fd','\u00fe','\u00ff' - }; - - private static final String EMPTY = ""; - private static final String STREAM = "stream"; - - /* ASCII control character codes */ - private static final int NUL = 0x00; - private static final int HT = 0x09; - private static final int LF = 0x0A; - private static final int FF = 0x0C; - private static final int CR = 0x0D; - - /** Whitespace characters codes. */ - private static final int WHITESPACES [] = { - NUL, HT, LF, FF, CR, ' ' - }; - - /** Delimiting characters codes. */ - private static final int DELIMITERS [] = { - '%', '/', '(', ')', '<', '>', '[', ']', '{', '}' - }; - - /** Source from which to read bytes. */ - protected RandomAccessFile _file; - - /** Character code of current character. */ - protected int _ch; - - /** - * If true, use the look-ahead character, - * rather than reading from the file. - */ - private boolean _lookAhead; - - /** Current offset into file for reporting purposes. */ - private long _offset; - - /** Current parse state. */ - private State _state; - - /** White space string. */ - private String _wsString; - - /** PDF/A compliance flag. */ - private boolean _pdfACompliant; - - /** Set of language codes used in UTF strings. */ - private Set _languageCodes; - - /** - * If true, do not attempt to parse non-whitespace - * delimited tokens, e.g., literal and hexadecimal strings. - */ - private boolean _scanMode; - - public Tokenizer () - { - _state = State.WHITESPACE; - _wsString = EMPTY; - _lookAhead = false; - _ch = 0; - _offset = 0; - _languageCodes = new TreeSet<> (); - _pdfACompliant = true; - _scanMode = false; - } - - /** - * Parses out and returns a token from the input file. - * If it hits the end of the file, returns null. - * Other parsing problems cause an exception to be thrown. - * When an exception is thrown, the state is changed to - * WHITESPACE, so the parser can get back in sync more easily. - */ - public Token getNext () throws IOException, PdfException - { - return getNext (0L); - } - - /** - * Parses out and returns a token from the input file. - * If it hits the end of the file, returns null. - * Other parsing problems cause an exception to be thrown. - * When an exception is thrown, the state is changed to - * WHITESPACE, so the parser can get back in sync more easily. - * - * @param max Maximum allowable size of the token - */ - public Token getNext (long max) throws IOException, PdfException - { - Token token = null; - StringBuilder buffer = null; - _state = State.WHITESPACE; - _wsString = EMPTY; - // Numeric sign. - boolean negative = false; - // Floating value. - double realValue = 0.0; - // Integer value. - long intValue = 0; - // Numeric fractional positional unit. - double denom = 10.0; - // Stream length. - long length = 0L; - // Last character seen in stream but one. - int prelastch = 0; - // Last character seen in stream. - int lastch = 0; - // Line break flag for the beginning of a data stream. - boolean sawLineBreak = false; - // Carriage return flag for the beginning of a data stream. - boolean sawCR = false; - - long startOffset = _offset; - - try { - while (true) { - if (max > 0L && _offset - startOffset > max) { - // The token has exceeded the specified maximum size. - if (token != null - && token instanceof StringValuedToken - && buffer != null) { - ((StringValuedToken) token).setValue ( - buffer.toString ()); - } - else { - token = null; - } - return token; - } +public abstract class Tokenizer { + /** Mapping between PDFDocEncoding and Unicode code points. */ + public static char[] PDFDOCENCODING = { + '\u0000', '\u0001', '\u0002', '\u0003', '\u0004', '\u0005', '\u0006', '\u0007', + '\b', '\t', '\n', '\u000b', '\f', '\r', '\u000e', '\u000f', + '\u0010', '\u0011', '\u0012', '\u0013', '\u0014', '\u0015', '\u0016', '\u0017', + '\u02d8', '\u02c7', '\u02c6', '\u02d9', '\u02dd', '\u02db', '\u02da', '\u02dc', + '\u0020', '\u0021', '\"', '\u0023', '\u0024', '\u0025', '\u0026', '\'', + '\u0028', '\u0029', '\u002a', '\u002b', '\u002c', '\u002d', '\u002e', '\u002f', + '\u0030', '\u0031', '\u0032', '\u0033', '\u0034', '\u0035', '\u0036', '\u0037', + '\u0038', '\u0039', '\u003a', '\u003b', '\u003c', '\u003d', '\u003e', '\u003f', + '\u0040', '\u0041', '\u0042', '\u0043', '\u0044', '\u0045', '\u0046', '\u0047', + '\u0048', '\u0049', '\u004a', '\u004b', '\u004c', '\u004d', '\u004e', '\u004f', + '\u0050', '\u0051', '\u0052', '\u0053', '\u0054', '\u0055', '\u0056', '\u0057', + '\u0058', '\u0059', '\u005a', '\u005b', '\\', '\u005d', '\u005e', '\u005f', + '\u0060', '\u0061', '\u0062', '\u0063', '\u0064', '\u0065', '\u0066', '\u0067', + '\u0068', '\u0069', '\u006a', '\u006b', '\u006c', '\u006d', '\u006e', '\u006f', + '\u0070', '\u0071', '\u0072', '\u0073', '\u0074', '\u0075', '\u0076', '\u0077', + '\u0078', '\u0079', '\u007a', '\u007b', '\u007c', '\u007d', '\u007e', '\u007f', + '\u2022', '\u2020', '\u2021', '\u2026', '\u2003', '\u2002', '\u0192', '\u2044', + '\u2039', '\u203a', '\u2212', '\u2030', '\u201e', '\u201c', '\u201d', '\u2018', + '\u2019', '\u201a', '\u2122', '\ufb01', '\ufb02', '\u0141', '\u0152', '\u0160', + '\u0178', '\u017d', '\u0131', '\u0142', '\u0153', '\u0161', '\u017e', '\u009f', + '\u20ac', '\u00a1', '\u00a2', '\u00a3', '\u00a4', '\u00a5', '\u00a6', '\u00a7', + '\u00a8', '\u00a9', '\u00aa', '\u00ab', '\u00ac', '\u00ad', '\u00ae', '\u00af', + '\u00b0', '\u00b1', '\u00b2', '\u00b3', '\u00b4', '\u00b5', '\u00b6', '\u00b7', + '\u00b8', '\u00b9', '\u00ba', '\u00bb', '\u00bc', '\u00bd', '\u00be', '\u00bf', + '\u00c0', '\u00c1', '\u00c2', '\u00c3', '\u00c4', '\u00c5', '\u00c6', '\u00c7', + '\u00c8', '\u00c9', '\u00ca', '\u00cb', '\u00cc', '\u00cd', '\u00ce', '\u00cf', + '\u00d0', '\u00d1', '\u00d2', '\u00d3', '\u00d4', '\u00d5', '\u00d6', '\u00d7', + '\u00d8', '\u00d9', '\u00da', '\u00db', '\u00dc', '\u00dd', '\u00de', '\u00df', + '\u00e0', '\u00e1', '\u00e2', '\u00e3', '\u00e4', '\u00e5', '\u00e6', '\u00e7', + '\u00e8', '\u00e9', '\u00ea', '\u00eb', '\u00ec', '\u00ed', '\u00ef', '\u00ef', + '\u00f0', '\u00f1', '\u00f2', '\u00f3', '\u00f4', '\u00f5', '\u00f6', '\u00f7', + '\u00f8', '\u00f9', '\u00fa', '\u00fb', '\u00fc', '\u00fd', '\u00fe', '\u00ff' + }; + + private static final String EMPTY = ""; + private static final String STREAM = "stream"; + + /* ASCII control character codes */ + private static final int NUL = 0x00; + private static final int HT = 0x09; + private static final int LF = 0x0A; + private static final int FF = 0x0C; + private static final int CR = 0x0D; + + /** Whitespace characters codes. */ + private static final int WHITESPACES[] = {NUL, HT, LF, FF, CR, ' '}; + + /** Delimiting characters codes. */ + private static final int DELIMITERS[] = {'%', '/', '(', ')', '<', '>', '[', ']', '{', '}'}; + + /** Source from which to read bytes. */ + protected RandomAccessFile _file; + + /** Character code of current character. */ + protected int _ch; + + /** If true, use the look-ahead character, rather than reading from the file. */ + private boolean _lookAhead; + + /** Current offset into file for reporting purposes. */ + private long _offset; + + /** Current parse state. */ + private State _state; + + /** White space string. */ + private String _wsString; + + /** PDF/A compliance flag. */ + private boolean _pdfACompliant; + + /** Set of language codes used in UTF strings. */ + private Set _languageCodes; + + /** + * If true, do not attempt to parse non-whitespace delimited tokens, e.g., literal + * and hexadecimal strings. + */ + private boolean _scanMode; + + public Tokenizer() { + _state = State.WHITESPACE; + _wsString = EMPTY; + _lookAhead = false; + _ch = 0; + _offset = 0; + _languageCodes = new TreeSet<>(); + _pdfACompliant = true; + _scanMode = false; + } + + /** + * Parses out and returns a token from the input file. If it hits the end of the file, returns + * null. Other parsing problems cause an exception to be thrown. When an exception is thrown, the + * state is changed to WHITESPACE, so the parser can get back in sync more easily. + */ + public Token getNext() throws IOException, PdfException { + return getNext(0L); + } + + /** + * Parses out and returns a token from the input file. If it hits the end of the file, returns + * null. Other parsing problems cause an exception to be thrown. When an exception is thrown, the + * state is changed to WHITESPACE, so the parser can get back in sync more easily. + * + * @param max Maximum allowable size of the token + */ + public Token getNext(long max) throws IOException, PdfException { + Token token = null; + StringBuilder buffer = null; + _state = State.WHITESPACE; + _wsString = EMPTY; + // Numeric sign. + boolean negative = false; + // Floating value. + double realValue = 0.0; + // Integer value. + long intValue = 0; + // Numeric fractional positional unit. + double denom = 10.0; + // Stream length. + long length = 0L; + // Last character seen in stream but one. + int prelastch = 0; + // Last character seen in stream. + int lastch = 0; + // Line break flag for the beginning of a data stream. + boolean sawLineBreak = false; + // Carriage return flag for the beginning of a data stream. + boolean sawCR = false; + + long startOffset = _offset; + + try { + while (true) { + if (max > 0L && _offset - startOffset > max) { + // The token has exceeded the specified maximum size. + if (token != null && token instanceof StringValuedToken && buffer != null) { + ((StringValuedToken) token).setValue(buffer.toString()); + } else { + token = null; + } + return token; + } - if (!_lookAhead) { - _ch = readChar (); - if (_ch < 0) { - _state = State.WHITESPACE; - throw new PdfMalformedException(MessageConstants.PDF_HUL_64, // PDF-HUL-64 - _offset); - } - _offset++; - } - else { - _lookAhead = false; - } + if (!_lookAhead) { + _ch = readChar(); + if (_ch < 0) { + _state = State.WHITESPACE; + throw new PdfMalformedException( + MessageConstants.PDF_HUL_64, // PDF-HUL-64 + _offset); + } + _offset++; + } else { + _lookAhead = false; + } - if (_state == (State.WHITESPACE)) { - // We are not in the middle of a token. - // Anything we read here starts a token - // or continues whitespace. - - if (isWhitespace (_ch)) { - _wsString += (char) _ch; - } - else if (_ch == '[') { - _state = State.WHITESPACE; - _wsString = EMPTY; - return new ArrayStart (); - } - else if (_ch == ']') { - _state = State.WHITESPACE; - _wsString = EMPTY; - - return new ArrayEnd (); - } - else if (_ch == '%') { - _state = State.COMMENT; - buffer = new StringBuilder (); - token = new Comment (); - } - else if (_ch == '+' || _ch == '-') { - _state = State.NUMERIC; - intValue = 0; - negative = (_ch == '-'); - token = new Numeric (); - } - else if (_ch == '.') { - _state = State.FRACTIONAL; - realValue = 0.0; - negative = false; - denom = 10.0; - token = new Numeric (); - } - else if (isNumeral (_ch)) { - _state = State.NUMERIC; - intValue = _ch - 48; - denom = 10.0; - token = new Numeric (); - } - else if (_ch == '/') { - _state = State.NAME; - buffer = new StringBuilder (); - token = new Name (); - } - else if (_ch == '(') { - if (!_scanMode) { - _state = State.LITERAL; - token = new Literal (); - buffer = new StringBuilder (); - } - } - else if (_ch == '<') { - _state = State.LESS_THAN; - } - else if (_ch == '>') { - _state = State.GREATER_THAN; - } - else if (!isDelimiter (_ch)) { - _state = State.KEYWORD; - buffer = new StringBuilder (); - buffer.append ((char) _ch); - token = new Keyword (); - } - // end State.WHITESPACE - } - else if (_state == (State.COMMENT)) { - // We are in a comment. Only a line ender can get us out. - - if (_ch == CR || _ch == LF) { - _state = State.WHITESPACE; - _wsString += (char) _ch; - ((StringValuedToken) token).setValue(buffer.toString()); - if (!token.isPdfACompliant()) { - _pdfACompliant = false; - } - return token; - } - buffer.append ((char) _ch); - } - else if (_state == (State.FRACTIONAL)) { - // We are reading a number and have encountered - // a decimal point. - - if (isDelimiter (_ch) || isWhitespace (_ch)) { - _state = State.WHITESPACE; - _wsString = EMPTY + (char) _ch; - if (negative) { - realValue = -realValue; - } - ((Numeric) token).setValue (realValue); - - if (isDelimiter (_ch)) { - _lookAhead = true; - } - - if (!token.isPdfACompliant()) { - _pdfACompliant = false; - } - return token; - } - else if (isNumeral (_ch)) { - realValue = realValue + ((_ch - 48) / denom); - denom *= 10.0; - } - else { - // Invalid character in a number - _state = State.WHITESPACE; - _wsString = EMPTY; - throw new PdfMalformedException (MessageConstants.PDF_HUL_65, _offset); // PDF-HUL-65 - } - } - else if (_state == (State.GREATER_THAN)) { - // '>' must be followed by another '>' as a dict end - if (_ch == '>') { - _state = State.WHITESPACE; - _wsString = EMPTY; - return new DictionaryEnd (); - } - _state = State.WHITESPACE; - _wsString = EMPTY; - throw new PdfMalformedException (MessageConstants.PDF_HUL_66, _offset); // PDF-HUL-66 - } - else if (_state == (State.HEXADECIMAL)) { - // We're in a hexadecimal string. We will - // transition from this state to a state which - // indicates the encoding used. - - if (_ch == '>') { - // A '>' terminates the string. - _state = State.WHITESPACE; - _wsString = EMPTY; - ((Literal) token).convertHex (); - return token; - } - else if (!isWhitespace (_ch)) { - ((Literal) token).appendHex (_ch); - } - } - else if (_state == (State.KEYWORD)) { - if (isDelimiter (_ch) || isWhitespace (_ch)) { - if (isDelimiter (_ch)) { - _lookAhead = true; - } - if (STREAM.equals(buffer.toString())) { - // Streams can't be nested, so this is - // (or better be) a FileTokenizer. - - _state = State.STREAM; - sawLineBreak = (_ch == LF); - sawCR = (_ch == CR); - token = new Stream (); - length = 0L; - lastch = 0; - prelastch = 0; - initStream ((Stream) token); - } - else { - _state = State.WHITESPACE; - _wsString = EMPTY + (char) _ch; - ((StringValuedToken) token).setValue - (buffer.toString ()); - if (!token.isPdfACompliant()) { - _pdfACompliant = false; - } - return token; - } - } - else { - buffer.append ((char) _ch); - } - } - else if (_state == (State.LESS_THAN)) { - // The last character was '<'. If followed - // by another '<', it's the opening token - // for a dictionary. Otherwise it's the - // beginning of a hexadecimal character string. - - if (_ch == '<' || _scanMode) { - _state = State.WHITESPACE; - _wsString = EMPTY; - return new DictionaryStart (); - } - _state = State.HEXADECIMAL; - token = new Literal (); - buffer = new StringBuilder (); - if (_ch == '>') { - backupChar(); - } - else { - ((Literal) token).appendHex(_ch); - } - } - else if (_state == (State.LITERAL)) { - backupChar (); - _offset += ((Literal) token).processLiteral (this) - 1; - _state = State.WHITESPACE; - _wsString = EMPTY; - return token; - } - else if (_state == (State.NAME)) { - if (_ch == '#') { - // The pound sign can be used as an escape in a name; - // it's followed by two hexadecimal characters: - int ch1 = readChar (); - int ch2 = readChar (); - // Will throw a PDFException if not hexadecimal: - _ch = (hexValue(ch1) << 4) + hexValue(ch2); - } - else if (isDelimiter (_ch) || isWhitespace (_ch)) { - _state = State.WHITESPACE; - ((StringValuedToken) token).setValue(buffer.toString()); - - if (isDelimiter (_ch)) { - _lookAhead = true; - _wsString = EMPTY; - } - else { - _wsString = EMPTY + (char) _ch; - } - - if (!token.isPdfACompliant()) { - _pdfACompliant = false; - } - return token; - } - buffer.append ((char) _ch); - } - else if (_state == (State.NUMERIC)) { - if (_ch == '.') { - _state = State.FRACTIONAL; - realValue = intValue; - denom = 10; - } - else if (isDelimiter (_ch) || isWhitespace (_ch) - || !isNumeral (_ch)) { - if (negative) { - if (_state == State.FRACTIONAL) { - realValue = -realValue; - } - else { - intValue = -intValue; - } - } - if (_state == State.FRACTIONAL) { - ((Numeric) token).setValue (realValue); - } - else { - ((Numeric) token).setValue (intValue); - } - _state = State.WHITESPACE; - - if (isDelimiter (_ch)) { - _lookAhead = true; - _wsString = EMPTY; - } - else { - _wsString = EMPTY + (char) _ch; - } - - if (!token.isPdfACompliant()) { - _pdfACompliant = false; - } - return token; - } - else { - if (_state == State.FRACTIONAL) { - realValue = realValue * 10 + _ch - 48; - } - else { - intValue = intValue * 10 + _ch - 48; - } - } - } - else if (_state == (State.STREAM)) { - if (_ch == 'e') { - _state = State.E; - } - else { - prelastch = lastch; - lastch = _ch; - setStreamOffset ((Stream) token); - // Check for a CR/LF or just LF at the start of the stream. - // Since we don't know at this point (not having parsed - // the dictionary) whether the data is external, and since - // the PDF spec says that everything between stream and - // endstream is ignored, we don't know if this requirement - // is enforceable here. But PDF/A forbids external streams, - // so we can at least check compliance there. In any case, - // we subtrace the length of the CR/LF from the purported - // stream length. - if (length == 0 && !sawLineBreak) { - if (_ch == LF) { - sawLineBreak = true; - if (!sawCR) { - _pdfACompliant = false; - } - ((Stream) token).setOffset ( - ((Stream) token).getOffset () + 1); - } - else if (_ch == CR) { - sawCR = true; - ((Stream) token).setOffset ( - ((Stream) token).getOffset () + 1); - } - else { - // Coming here indicates an error if the stream - // isn't external; but we don't know whether - // it is. - _pdfACompliant = false; - } - } - else{ - length++; - } - } - } - else if (_state == (State.E)) { - if (_ch == 'n') { - _state = State.EN; - } - else { - _state = State.STREAM; - length += 2; - } - } - else if (_state == (State.EN)) { - if (_ch == 'd') { - _state = State.END; - } - else { - _state = State.STREAM; - length += 3; - } - } - else if (_state == (State.END)) { - if (_ch == 's') { - _state = State.ENDS; - } - else { - _state = State.STREAM; - length += 4; - } - } - else if (_state == (State.ENDS)) { - if (_ch == 't') { - _state = State.ENDST; - } - else { - _state = State.STREAM; - length += 5; - } - } - else if (_state == (State.ENDST)) { - if (_ch == 'r') { - _state = State.ENDSTR; - } - else { - _state = State.STREAM; - length += 6; - } - } - else if (_state == (State.ENDSTR)) { - if (_ch == 'e') { - _state = State.ENDSTRE; - } - else { - _state = State.STREAM; - length += 7; - } - } - else if (_state == (State.ENDSTRE)) { - if (_ch == 'a') { - _state = State.ENDSTREA; - } - else { - _state = State.STREAM; - length += 8; - } - } - else if (_state == (State.ENDSTREA)) { - if (_ch == 'm') { - _state = State.ENDSTREAM; - } - else { - _state = State.STREAM; - length += 9; - } - } - else if (_state == (State.ENDSTREAM)) { - if (isDelimiter (_ch) || isWhitespace (_ch)) { - _state = State.WHITESPACE; - - // The line break, if any, before endstream - // is not counted in the length. - if (prelastch == CR && lastch == LF) { - length -= 2; - } - else if (lastch == LF || lastch == CR) { - length -= 1; - } - ((Stream) token).setLength (length); - - if (isDelimiter (_ch)) { - _lookAhead = true; - _wsString = EMPTY; - } - else { - _wsString = EMPTY + (char) _ch; - } - - return token; - } - _state = State.STREAM; - } + if (_state == (State.WHITESPACE)) { + // We are not in the middle of a token. + // Anything we read here starts a token + // or continues whitespace. + + if (isWhitespace(_ch)) { + _wsString += (char) _ch; + } else if (_ch == '[') { + _state = State.WHITESPACE; + _wsString = EMPTY; + return new ArrayStart(); + } else if (_ch == ']') { + _state = State.WHITESPACE; + _wsString = EMPTY; + + return new ArrayEnd(); + } else if (_ch == '%') { + _state = State.COMMENT; + buffer = new StringBuilder(); + token = new Comment(); + } else if (_ch == '+' || _ch == '-') { + _state = State.NUMERIC; + intValue = 0; + negative = (_ch == '-'); + token = new Numeric(); + } else if (_ch == '.') { + _state = State.FRACTIONAL; + realValue = 0.0; + negative = false; + denom = 10.0; + token = new Numeric(); + } else if (isNumeral(_ch)) { + _state = State.NUMERIC; + intValue = _ch - 48; + denom = 10.0; + token = new Numeric(); + } else if (_ch == '/') { + _state = State.NAME; + buffer = new StringBuilder(); + token = new Name(); + } else if (_ch == '(') { + if (!_scanMode) { + _state = State.LITERAL; + token = new Literal(); + buffer = new StringBuilder(); } - } - catch (EOFException eofe) { - if (token != null - && token instanceof StringValuedToken - && buffer != null) { - ((StringValuedToken) token).setValue (buffer.toString ()); + } else if (_ch == '<') { + _state = State.LESS_THAN; + } else if (_ch == '>') { + _state = State.GREATER_THAN; + } else if (!isDelimiter(_ch)) { + _state = State.KEYWORD; + buffer = new StringBuilder(); + buffer.append((char) _ch); + token = new Keyword(); + } + // end State.WHITESPACE + } else if (_state == (State.COMMENT)) { + // We are in a comment. Only a line ender can get us out. + + if (_ch == CR || _ch == LF) { + _state = State.WHITESPACE; + _wsString += (char) _ch; + ((StringValuedToken) token).setValue(buffer.toString()); + if (!token.isPdfACompliant()) { + _pdfACompliant = false; } - else { - token = null; + return token; + } + buffer.append((char) _ch); + } else if (_state == (State.FRACTIONAL)) { + // We are reading a number and have encountered + // a decimal point. + + if (isDelimiter(_ch) || isWhitespace(_ch)) { + _state = State.WHITESPACE; + _wsString = EMPTY + (char) _ch; + if (negative) { + realValue = -realValue; } - } + ((Numeric) token).setValue(realValue); - return token; - } - - /** Returns the current offset into the file. */ - public long getOffset () - { - return _offset; - } - - /** Returns the set of language codes. Members of the set are Strings. */ - public Set getLanguageCodes () - { - return _languageCodes; - } + if (isDelimiter(_ch)) { + _lookAhead = true; + } - /** - * Returns the value of the pdfACompliant flag, which indicates that - * the tokenizer hasn't detected non-compliance. A value of - * true is no guarantee that the file is compliant. - */ - public boolean getPDFACompliant () - { - return _pdfACompliant; - } + if (!token.isPdfACompliant()) { + _pdfACompliant = false; + } + return token; + } else if (isNumeral(_ch)) { + realValue = realValue + ((_ch - 48) / denom); + denom *= 10.0; + } else { + // Invalid character in a number + _state = State.WHITESPACE; + _wsString = EMPTY; + throw new PdfMalformedException(MessageConstants.PDF_HUL_65, _offset); // PDF-HUL-65 + } + } else if (_state == (State.GREATER_THAN)) { + // '>' must be followed by another '>' as a dict end + if (_ch == '>') { + _state = State.WHITESPACE; + _wsString = EMPTY; + return new DictionaryEnd(); + } + _state = State.WHITESPACE; + _wsString = EMPTY; + throw new PdfMalformedException(MessageConstants.PDF_HUL_66, _offset); // PDF-HUL-66 + } else if (_state == (State.HEXADECIMAL)) { + // We're in a hexadecimal string. We will + // transition from this state to a state which + // indicates the encoding used. + + if (_ch == '>') { + // A '>' terminates the string. + _state = State.WHITESPACE; + _wsString = EMPTY; + ((Literal) token).convertHex(); + return token; + } else if (!isWhitespace(_ch)) { + ((Literal) token).appendHex(_ch); + } + } else if (_state == (State.KEYWORD)) { + if (isDelimiter(_ch) || isWhitespace(_ch)) { + if (isDelimiter(_ch)) { + _lookAhead = true; + } + if (STREAM.equals(buffer.toString())) { + // Streams can't be nested, so this is + // (or better be) a FileTokenizer. + + _state = State.STREAM; + sawLineBreak = (_ch == LF); + sawCR = (_ch == CR); + token = new Stream(); + length = 0L; + lastch = 0; + prelastch = 0; + initStream((Stream) token); + } else { + _state = State.WHITESPACE; + _wsString = EMPTY + (char) _ch; + ((StringValuedToken) token).setValue(buffer.toString()); + if (!token.isPdfACompliant()) { + _pdfACompliant = false; + } + return token; + } + } else { + buffer.append((char) _ch); + } + } else if (_state == (State.LESS_THAN)) { + // The last character was '<'. If followed + // by another '<', it's the opening token + // for a dictionary. Otherwise it's the + // beginning of a hexadecimal character string. + + if (_ch == '<' || _scanMode) { + _state = State.WHITESPACE; + _wsString = EMPTY; + return new DictionaryStart(); + } + _state = State.HEXADECIMAL; + token = new Literal(); + buffer = new StringBuilder(); + if (_ch == '>') { + backupChar(); + } else { + ((Literal) token).appendHex(_ch); + } + } else if (_state == (State.LITERAL)) { + backupChar(); + _offset += ((Literal) token).processLiteral(this) - 1; + _state = State.WHITESPACE; + _wsString = EMPTY; + return token; + } else if (_state == (State.NAME)) { + if (_ch == '#') { + // The pound sign can be used as an escape in a name; + // it's followed by two hexadecimal characters: + int ch1 = readChar(); + int ch2 = readChar(); + // Will throw a PDFException if not hexadecimal: + _ch = (hexValue(ch1) << 4) + hexValue(ch2); + } else if (isDelimiter(_ch) || isWhitespace(_ch)) { + _state = State.WHITESPACE; + ((StringValuedToken) token).setValue(buffer.toString()); + + if (isDelimiter(_ch)) { + _lookAhead = true; + _wsString = EMPTY; + } else { + _wsString = EMPTY + (char) _ch; + } - /** - * Sets the value of the pdfACompliant flag. This may be used to - * clear previous detection of noncompliance. - */ - public void setPDFACompliant (boolean pdfACompliant) - { - _pdfACompliant = pdfACompliant; - } + if (!token.isPdfACompliant()) { + _pdfACompliant = false; + } + return token; + } + buffer.append((char) _ch); + } else if (_state == (State.NUMERIC)) { + if (_ch == '.') { + _state = State.FRACTIONAL; + realValue = intValue; + denom = 10; + } else if (isDelimiter(_ch) || isWhitespace(_ch) || !isNumeral(_ch)) { + if (negative) { + if (_state == State.FRACTIONAL) { + realValue = -realValue; + } else { + intValue = -intValue; + } + } + if (_state == State.FRACTIONAL) { + ((Numeric) token).setValue(realValue); + } else { + ((Numeric) token).setValue(intValue); + } + _state = State.WHITESPACE; - /** - * Returns the value of the last white space string read by the - * tokenizer. Repositioning clears the white space string. - */ - public String getWSString () - { - return _wsString; - } + if (isDelimiter(_ch)) { + _lookAhead = true; + _wsString = EMPTY; + } else { + _wsString = EMPTY + (char) _ch; + } - /** - * Sets the Tokenizer to a new position in the file. - * - * @param offset The offset in bytes from the start of the file. - */ - public abstract void seek (long offset) throws IOException, PdfException; - - /** Reset after a seek. */ - protected void seekReset (long offset) - { - _state = State.WHITESPACE; - _wsString = EMPTY; - _lookAhead = false; - _ch = 0; - /* Don't panic, _offset is used only for reporting purposes */ - _offset = offset - 1; - } + if (!token.isPdfACompliant()) { + _pdfACompliant = false; + } + return token; + } else { + if (_state == State.FRACTIONAL) { + realValue = realValue * 10 + _ch - 48; + } else { + intValue = intValue * 10 + _ch - 48; + } + } + } else if (_state == (State.STREAM)) { + if (_ch == 'e') { + _state = State.E; + } else { + prelastch = lastch; + lastch = _ch; + setStreamOffset((Stream) token); + // Check for a CR/LF or just LF at the start of the stream. + // Since we don't know at this point (not having parsed + // the dictionary) whether the data is external, and since + // the PDF spec says that everything between stream and + // endstream is ignored, we don't know if this requirement + // is enforceable here. But PDF/A forbids external streams, + // so we can at least check compliance there. In any case, + // we subtrace the length of the CR/LF from the purported + // stream length. + if (length == 0 && !sawLineBreak) { + if (_ch == LF) { + sawLineBreak = true; + if (!sawCR) { + _pdfACompliant = false; + } + ((Stream) token).setOffset(((Stream) token).getOffset() + 1); + } else if (_ch == CR) { + sawCR = true; + ((Stream) token).setOffset(((Stream) token).getOffset() + 1); + } else { + // Coming here indicates an error if the stream + // isn't external; but we don't know whether + // it is. + _pdfACompliant = false; + } + } else { + length++; + } + } + } else if (_state == (State.E)) { + if (_ch == 'n') { + _state = State.EN; + } else { + _state = State.STREAM; + length += 2; + } + } else if (_state == (State.EN)) { + if (_ch == 'd') { + _state = State.END; + } else { + _state = State.STREAM; + length += 3; + } + } else if (_state == (State.END)) { + if (_ch == 's') { + _state = State.ENDS; + } else { + _state = State.STREAM; + length += 4; + } + } else if (_state == (State.ENDS)) { + if (_ch == 't') { + _state = State.ENDST; + } else { + _state = State.STREAM; + length += 5; + } + } else if (_state == (State.ENDST)) { + if (_ch == 'r') { + _state = State.ENDSTR; + } else { + _state = State.STREAM; + length += 6; + } + } else if (_state == (State.ENDSTR)) { + if (_ch == 'e') { + _state = State.ENDSTRE; + } else { + _state = State.STREAM; + length += 7; + } + } else if (_state == (State.ENDSTRE)) { + if (_ch == 'a') { + _state = State.ENDSTREA; + } else { + _state = State.STREAM; + length += 8; + } + } else if (_state == (State.ENDSTREA)) { + if (_ch == 'm') { + _state = State.ENDSTREAM; + } else { + _state = State.STREAM; + length += 9; + } + } else if (_state == (State.ENDSTREAM)) { + if (isDelimiter(_ch) || isWhitespace(_ch)) { + _state = State.WHITESPACE; + + // The line break, if any, before endstream + // is not counted in the length. + if (prelastch == CR && lastch == LF) { + length -= 2; + } else if (lastch == LF || lastch == CR) { + length -= 1; + } + ((Stream) token).setLength(length); - /** Gets a character from the file or stream, using a buffer. */ - public abstract int readChar () throws IOException; + if (isDelimiter(_ch)) { + _lookAhead = true; + _wsString = EMPTY; + } else { + _wsString = EMPTY + (char) _ch; + } - /** Reads a character in one-byte or two-byte format, as requested. */ - public int readChar1 (boolean utf16) throws IOException - { - if (utf16) { - int ch1 = readChar (); - int ch2 = readChar (); - return (ch1 << 8) | ch2; - } - else { - return readChar (); + return token; + } + _state = State.STREAM; } + } + } catch (EOFException eofe) { + if (token != null && token instanceof StringValuedToken && buffer != null) { + ((StringValuedToken) token).setValue(buffer.toString()); + } else { + token = null; + } } - /** Backs up a byte so it will be read again. */ - public abstract void backupChar (); - - /** Adds a string to the language codes. */ - public void addLanguageCode (String langCode) - { - _languageCodes.add (langCode); + return token; + } + + /** Returns the current offset into the file. */ + public long getOffset() { + return _offset; + } + + /** Returns the set of language codes. Members of the set are Strings. */ + public Set getLanguageCodes() { + return _languageCodes; + } + + /** + * Returns the value of the pdfACompliant flag, which indicates that the tokenizer hasn't detected + * non-compliance. A value of true is no guarantee that the file is compliant. + */ + public boolean getPDFACompliant() { + return _pdfACompliant; + } + + /** + * Sets the value of the pdfACompliant flag. This may be used to clear previous detection of + * noncompliance. + */ + public void setPDFACompliant(boolean pdfACompliant) { + _pdfACompliant = pdfACompliant; + } + + /** + * Returns the value of the last white space string read by the tokenizer. Repositioning clears + * the white space string. + */ + public String getWSString() { + return _wsString; + } + + /** + * Sets the Tokenizer to a new position in the file. + * + * @param offset The offset in bytes from the start of the file. + */ + public abstract void seek(long offset) throws IOException, PdfException; + + /** Reset after a seek. */ + protected void seekReset(long offset) { + _state = State.WHITESPACE; + _wsString = EMPTY; + _lookAhead = false; + _ch = 0; + /* Don't panic, _offset is used only for reporting purposes */ + _offset = offset - 1; + } + + /** Gets a character from the file or stream, using a buffer. */ + public abstract int readChar() throws IOException; + + /** Reads a character in one-byte or two-byte format, as requested. */ + public int readChar1(boolean utf16) throws IOException { + if (utf16) { + int ch1 = readChar(); + int ch2 = readChar(); + return (ch1 << 8) | ch2; + } else { + return readChar(); } - - - /****************************************************************** - * PRIVATE CLASS METHODS - ******************************************************************/ - - private static int hexValue (int h) throws PdfException - { - int d; - - if (isNumeral(h)) { - d = h - 0x30; - } - else if ('A' <= h && h <= 'F') { - d = h - 0x37; - } - else if ('a' <= h && h <= 'f') { - d = h - 0x57; - } - else { - throw new PdfMalformedException(MessageConstants.PDF_HUL_65); // PDF-HUL-65 - } - - return d; + } + + /** Backs up a byte so it will be read again. */ + public abstract void backupChar(); + + /** Adds a string to the language codes. */ + public void addLanguageCode(String langCode) { + _languageCodes.add(langCode); + } + + /** + * **************************************************************** PRIVATE CLASS METHODS + * **************************************************************** + */ + private static int hexValue(int h) throws PdfException { + int d; + + if (isNumeral(h)) { + d = h - 0x30; + } else if ('A' <= h && h <= 'F') { + d = h - 0x37; + } else if ('a' <= h && h <= 'f') { + d = h - 0x57; + } else { + throw new PdfMalformedException(MessageConstants.PDF_HUL_65); // PDF-HUL-65 } - /** - * Returns true if ch is - * a non-whitespace character which delimits a token. - */ - private static boolean isDelimiter (int ch) - { - for (int delimiter : DELIMITERS) { - if (ch == delimiter) { - return true; - } - } - - return false; + return d; + } + + /** + * Returns true if ch is a non-whitespace character which delimits a + * token. + */ + private static boolean isDelimiter(int ch) { + for (int delimiter : DELIMITERS) { + if (ch == delimiter) { + return true; + } } - private static boolean isNumeral (int ch) - { - return '0' <= ch && ch <= '9'; - } + return false; + } - private static boolean isWhitespace (int ch) - { - for (int whitespace : WHITESPACES) { - if (ch == whitespace) { - return true; - } - } - - return false; - } + private static boolean isNumeral(int ch) { + return '0' <= ch && ch <= '9'; + } - /** - * If true, do not attempt to parse non-whitespace - * delimited tokens, e.g., literal and hexadecimal strings. - * - * @param flag Scan mode flag - */ - public void scanMode (boolean flag) - { - _scanMode = flag; + private static boolean isWhitespace(int ch) { + for (int whitespace : WHITESPACES) { + if (ch == whitespace) { + return true; + } } - /** - * Initialization code for Stream object. This is meaningful - * only for the FileTokenizer subclass. - */ - protected abstract void initStream (Stream token) - throws IOException, PdfException; - - /** - * Sets the offset of a Stream to the current file position. - * Only the file-based tokenizer can do this, which is why this - * overrides the Tokenizer method. - */ - protected abstract void setStreamOffset (Stream token) - throws IOException, PdfException; - + return false; + } + + /** + * If true, do not attempt to parse non-whitespace delimited tokens, e.g., literal + * and hexadecimal strings. + * + * @param flag Scan mode flag + */ + public void scanMode(boolean flag) { + _scanMode = flag; + } + + /** + * Initialization code for Stream object. This is meaningful only for the FileTokenizer subclass. + */ + protected abstract void initStream(Stream token) throws IOException, PdfException; + + /** + * Sets the offset of a Stream to the current file position. Only the file-based tokenizer can do + * this, which is why this overrides the Tokenizer method. + */ + protected abstract void setStreamOffset(Stream token) throws IOException, PdfException; } diff --git a/jhove-modules/pdf-hul/src/main/java/edu/harvard/hul/ois/jhove/module/pdf/X1Profile.java b/jhove-modules/pdf-hul/src/main/java/edu/harvard/hul/ois/jhove/module/pdf/X1Profile.java index 6acafc13c..20d143680 100644 --- a/jhove-modules/pdf-hul/src/main/java/edu/harvard/hul/ois/jhove/module/pdf/X1Profile.java +++ b/jhove-modules/pdf-hul/src/main/java/edu/harvard/hul/ois/jhove/module/pdf/X1Profile.java @@ -1,452 +1,395 @@ -/********************************************************************** - * Jhove - JSTOR/Harvard Object Validation Environment - * Copyright 2003 by JSTOR and the President and Fellows of Harvard College - **********************************************************************/ - - +/** + * ******************************************************************** Jhove - JSTOR/Harvard Object + * Validation Environment Copyright 2003 by JSTOR and the President and Fellows of Harvard College + * ******************************************************************** + */ package edu.harvard.hul.ois.jhove.module.pdf; import edu.harvard.hul.ois.jhove.module.*; import java.util.*; /** - * PDF profile checker for PDF/X-1 documents. - * See ISO Standard 15930-1, "Complete exchange using - * CMYK data (PDF/X-1 and PDF/X-1a)" + * PDF profile checker for PDF/X-1 documents. See ISO Standard 15930-1, "Complete exchange using + * CMYK data (PDF/X-1 and PDF/X-1a)" */ -public final class X1Profile extends XProfileBase -{ - /****************************************************************** - * PRIVATE CLASS FIELDS. - ******************************************************************/ - - private boolean _x1aCompliant; - - /** - * Constructor. - * Creates an X1Profile object for subsequent testing. - * - * @param module The module under which we are checking the profile. - * - */ - public X1Profile (PdfModule module) - { - super (module, XProfileBase.PDFX1); - _profileText = "ISO PDF/X-1"; - } +public final class X1Profile extends XProfileBase { + /** + * **************************************************************** PRIVATE CLASS FIELDS. + * **************************************************************** + */ + private boolean _x1aCompliant; - /** - * Returns true if the document satisfies the profile. - * X-1a compliance is a superset of the requirements of X-1 compliance - * (i.e., X-1a compliant documents are a subset of X-1 compliant - * documents), so we test for X-1a compliance at the same time. - * The result can subsequently be obtained by calling - * isX1aCompliant. - * - */ - @Override - public boolean satisfiesThisProfile () - { - _x1aCompliant = false; // guilty till proven innocent - try { - // First off, there must be an OutputIntents array - // in the document catalog dictionary. - PdfDictionary catDict = _module.getCatalogDict (); - PdfArray intentsArray = (PdfArray) _module.resolveIndirectObject - (catDict.get ("OutputIntents")); - if (intentsArray == null) { - return false; - } + /** + * Constructor. Creates an X1Profile object for subsequent testing. + * + * @param module The module under which we are checking the profile. + */ + public X1Profile(PdfModule module) { + super(module, XProfileBase.PDFX1); + _profileText = "ISO PDF/X-1"; + } - // Check if PDF-X1/a conformance is asserted - PdfDictionary docInfo = _module.getDocInfo(); - try { - PdfSimpleObject conf = (PdfSimpleObject) docInfo.get ("GTS_PDFXConformance"); - String cn = conf.getStringValue (); - if (cn.startsWith ("PDF/X-1a:")) { - _x1aCompliant = true; - } - } - catch (Exception e) { } + /** + * Returns true if the document satisfies the profile. X-1a compliance is a superset + * of the requirements of X-1 compliance (i.e., X-1a compliant documents are a subset of X-1 + * compliant documents), so we test for X-1a compliance at the same time. The result can + * subsequently be obtained by calling isX1aCompliant. + */ + @Override + public boolean satisfiesThisProfile() { + _x1aCompliant = false; // guilty till proven innocent + try { + // First off, there must be an OutputIntents array + // in the document catalog dictionary. + PdfDictionary catDict = _module.getCatalogDict(); + PdfArray intentsArray = + (PdfArray) _module.resolveIndirectObject(catDict.get("OutputIntents")); + if (intentsArray == null) { + return false; + } - // Next check if the OutputIntents are valid. - if (!outputIntentsOK (intentsArray)) { - return false; - } + // Check if PDF-X1/a conformance is asserted + PdfDictionary docInfo = _module.getDocInfo(); + try { + PdfSimpleObject conf = (PdfSimpleObject) docInfo.get("GTS_PDFXConformance"); + String cn = conf.getStringValue(); + if (cn.startsWith("PDF/X-1a:")) { + _x1aCompliant = true; + } + } catch (Exception e) { + } - // Do several resource checks. - if (!resourcesOK ()) { - return false; - } - - // Check the trailer dictionary. - if (!trailerDictOK ()) { - return false; - } + // Next check if the OutputIntents are valid. + if (!outputIntentsOK(intentsArray)) { + return false; + } - // Check specific requirements on the doc info dictionary. - if (!infoDictOK ("PDF/X-1")) { - return false; - } - - // Check that an acceptable form of encryption (or none) is used. - if (!encryptionOK ()) { - return false; - } - - // Check that bounding boxes are present as required. - // MediaBox is required. - if (!bboxOK (true)) { - return false; - } - - // If the document contains Actions, it's non-conformant - if (_module.getActionsExist ()) { - return false; - } + // Do several resource checks. + if (!resourcesOK()) { + return false; + } - // Now for specific X1-a tests - // Encryption dictionary is not allowed. - if (_module.getEncryptionDict () != null) { - _x1aCompliant = false; - } + // Check the trailer dictionary. + if (!trailerDictOK()) { + return false; + } - // Check that ViewerPreferences meet certain restrictions - // if any BleedBoxes are present. - if (!checkPrefsAgainstBleedBox ()) { - _x1aCompliant = false; - } + // Check specific requirements on the doc info dictionary. + if (!infoDictOK("PDF/X-1")) { + return false; + } + + // Check that an acceptable form of encryption (or none) is used. + if (!encryptionOK()) { + return false; + } + // Check that bounding boxes are present as required. + // MediaBox is required. + if (!bboxOK(true)) { + return false; + } + + // If the document contains Actions, it's non-conformant + if (_module.getActionsExist()) { + return false; + } + + // Now for specific X1-a tests + // Encryption dictionary is not allowed. + if (_module.getEncryptionDict() != null) { + _x1aCompliant = false; + } + + // Check that ViewerPreferences meet certain restrictions + // if any BleedBoxes are present. + if (!checkPrefsAgainstBleedBox()) { + _x1aCompliant = false; + } + + } catch (Exception e) { + // Any otherwise uncaught exception means nonconformance + return false; + } + return true; + } + + /** + * Returns the result of X-1a compliance testing which was performed in the course of + * satisfiesThisProfile. If satisfiesThisProfile hasn't been called, returns + * false. + */ + public boolean isX1aCompliant() { + return _x1aCompliant; + } + + /* Walk through the page tree and check all Resources dictionaries + that we find. Along the way, we check several things: + + Color spaces. Any Separation and DeviceN resources we + find must have an AlternateSpace of DeviceGray or + DeviceCMYK. + + Extended graphic states. + + XObjects. + */ + private boolean resourcesOK() { + PageTreeNode docTreeRoot = _module.getDocumentTree(); + try { + docTreeRoot.startWalk(); + DocNode docNode; + for (; ; ) { + docNode = docTreeRoot.nextDocNode(); + if (docNode == null) { + break; } - catch (Exception e) { - // Any otherwise uncaught exception means nonconformance + // Check for node-level resources + PdfDictionary rsrc = docNode.getResources(); + if (rsrc != null) { + + // Check color spaces. + PdfDictionary cs = (PdfDictionary) _module.resolveIndirectObject(rsrc.get("ColorSpace")); + if (!colorSpaceOK(cs)) { return false; - } - return true; - } - - /** - * Returns the result of X-1a compliance testing which was performed in - * the course of satisfiesThisProfile. If - * satisfiesThisProfile hasn't been called, returns - * false. - */ - public boolean isX1aCompliant () - { - return _x1aCompliant; - } + } + + // Check extended graphics state. + PdfDictionary gs = (PdfDictionary) _module.resolveIndirectObject(rsrc.get("ExtGState")); + if (!extGStateOK(gs)) { + return false; + } + // Check XObjects. + PdfDictionary xo = (PdfDictionary) _module.resolveIndirectObject(rsrc.get("XObject")); + if (!xObjectsOK(xo)) { + return false; + } + } - /* Walk through the page tree and check all Resources dictionaries - that we find. Along the way, we check several things: - - Color spaces. Any Separation and DeviceN resources we - find must have an AlternateSpace of DeviceGray or - DeviceCMYK. - - Extended graphic states. - - XObjects. - */ - private boolean resourcesOK () - { - PageTreeNode docTreeRoot = _module.getDocumentTree (); - try { - docTreeRoot.startWalk (); - DocNode docNode; - for (;;) { - docNode = docTreeRoot.nextDocNode (); - if (docNode == null) { - break; + // Check content streams for resources + if (docNode instanceof PageObject) { + List streams = ((PageObject) docNode).getContentStreams(); + if (streams != null) { + Iterator iter = streams.listIterator(); + while (iter.hasNext()) { + PdfStream stream = iter.next(); + PdfDictionary dict = stream.getDict(); + PdfDictionary rs = (PdfDictionary) dict.get("Resources"); + if (rs != null) { + PdfDictionary cs = + (PdfDictionary) _module.resolveIndirectObject(rs.get("ColorSpace")); + if (!colorSpaceOK(cs)) { + return false; } - // Check for node-level resources - PdfDictionary rsrc = docNode.getResources (); - if (rsrc != null) { - - // Check color spaces. - PdfDictionary cs = (PdfDictionary) - _module.resolveIndirectObject - (rsrc.get ("ColorSpace")); - if (!colorSpaceOK (cs)) { - return false; - } - // Check extended graphics state. - PdfDictionary gs = (PdfDictionary) - _module.resolveIndirectObject - (rsrc.get ("ExtGState")); - if (!extGStateOK (gs)) { - return false; - } - - // Check XObjects. - PdfDictionary xo = (PdfDictionary) - _module.resolveIndirectObject - (rsrc.get ("XObject")); - if (!xObjectsOK (xo)) { - return false; - } + PdfDictionary gs = + (PdfDictionary) _module.resolveIndirectObject(rs.get("ExtGState")); + if (!extGStateOK(gs)) { + return false; } - - // Check content streams for resources - if (docNode instanceof PageObject) { - List streams = - ((PageObject) docNode).getContentStreams (); - if (streams != null) { - Iterator iter = streams.listIterator (); - while (iter.hasNext ()) { - PdfStream stream = iter.next (); - PdfDictionary dict = stream.getDict (); - PdfDictionary rs = - (PdfDictionary) dict.get ("Resources"); - if (rs != null) { - PdfDictionary cs = (PdfDictionary) - _module.resolveIndirectObject - (rs.get ("ColorSpace")); - if (!colorSpaceOK (cs)) { - return false; - } - - PdfDictionary gs = (PdfDictionary) - _module.resolveIndirectObject - (rs.get ("ExtGState")); - if (!extGStateOK (gs)) { - return false; - } - - PdfDictionary xo = (PdfDictionary) - _module.resolveIndirectObject - (rs.get ("XObject")); - if (!xObjectsOK (xo)) { - return false; - } - } - // Also check for filters, for X1-a restrictions. - PdfObject filters = - dict.get ("Filter"); - if (!filter1AOK (filters)) { - _x1aCompliant = false; - } - } - } - - // Also check page objects for annotations -- - // in particular, TrapNet annotations. - PdfArray annots = ((PageObject) docNode).getAnnotations (); - if (annots != null) { - Vector annVec = annots.getContent (); - for (int i = 0; i < annVec.size (); i++) { - PdfDictionary annDict = (PdfDictionary) - annVec.elementAt (i); - PdfSimpleObject subtypeObj = (PdfSimpleObject) annDict.get ("Subtype"); - if ("TrapNet".equals (subtypeObj.getStringValue ())) { - // FontFauxing must be absent or 0-length - PdfArray ff = (PdfArray) annDict.get ("FontFauxing"); - if (ff != null) { - Vector ffVec = ff.getContent (); - if (ffVec.size() > 0) { - return false; // a faux pas - } - } - - // Check Appearance dict for TrapNet annotation - PdfDictionary appDict = (PdfDictionary) - annDict.get ("AP"); - if (appDict != null) { - PdfDictionary normalDict = (PdfDictionary) appDict.get ("N"); - if (normalDict != null) { - PdfSimpleObject pcm = - (PdfSimpleObject) normalDict.get ("PCM"); - if (!"DeviceCMYK".equals (pcm.getStringValue ())) { - return false; - } - } - - } - } - } - } + + PdfDictionary xo = (PdfDictionary) _module.resolveIndirectObject(rs.get("XObject")); + if (!xObjectsOK(xo)) { + return false; } + } + // Also check for filters, for X1-a restrictions. + PdfObject filters = dict.get("Filter"); + if (!filter1AOK(filters)) { + _x1aCompliant = false; + } } - } - catch (Exception e) { - return false; - } - return true; // passed all tests - } - + } - /* Check if a color space dictionary is conformant */ - private static boolean colorSpaceOK (PdfDictionary cs) - { - // If it's null, that's fine. - if (cs == null) { - return true; - } - // Walk through the color space dictionary, - // checking Separation and DeviceN resources - Iterator iter = cs.iterator (); - while (iter.hasNext ()) { - PdfObject res = iter.next (); - if (res instanceof PdfArray) { - Vector resv = ((PdfArray) res).getContent (); - PdfSimpleObject snameobj = (PdfSimpleObject) resv.elementAt (0); - String sname = snameobj.getStringValue (); - if ("Separation".equals (sname) || "DeviceN".equals (sname)) { - PdfSimpleObject altSpaceObj = (PdfSimpleObject) resv.elementAt (2); - String altSpace = altSpaceObj.getStringValue (); - if (! ("DeviceGray".equals (altSpace) || - "DeviceCMYK".equals (altSpace))) { - return false; - } + // Also check page objects for annotations -- + // in particular, TrapNet annotations. + PdfArray annots = ((PageObject) docNode).getAnnotations(); + if (annots != null) { + Vector annVec = annots.getContent(); + for (int i = 0; i < annVec.size(); i++) { + PdfDictionary annDict = (PdfDictionary) annVec.elementAt(i); + PdfSimpleObject subtypeObj = (PdfSimpleObject) annDict.get("Subtype"); + if ("TrapNet".equals(subtypeObj.getStringValue())) { + // FontFauxing must be absent or 0-length + PdfArray ff = (PdfArray) annDict.get("FontFauxing"); + if (ff != null) { + Vector ffVec = ff.getContent(); + if (ffVec.size() > 0) { + return false; // a faux pas + } } - if ("Indexed".equals (sname) || - "Pattern".equals (sname)) { - // Indexed and pattern color spaces must have a - // base colorspace of DeviceCMYK, DeviceGray, - // DeviceN, or Separation. - PdfSimpleObject baseObj = (PdfSimpleObject) - resv.elementAt (1); - String base = baseObj.getStringValue (); - if (! ("DeviceCMYK".equals (base) || - "DeviceGray".equals (base) || - "DeviceN".equals (base) || - "Separation".equals (base))) { - return false; + + // Check Appearance dict for TrapNet annotation + PdfDictionary appDict = (PdfDictionary) annDict.get("AP"); + if (appDict != null) { + PdfDictionary normalDict = (PdfDictionary) appDict.get("N"); + if (normalDict != null) { + PdfSimpleObject pcm = (PdfSimpleObject) normalDict.get("PCM"); + if (!"DeviceCMYK".equals(pcm.getStringValue())) { + return false; } + } } + } } + } } - return true; // passed all tests + } + } catch (Exception e) { + return false; } + return true; // passed all tests + } - - - /* Checks a single XObject. */ - @Override - protected boolean xObjectOK (PdfDictionary xo) - { - if (xo == null) { - // no XObject means no problem - return true; - } - // Do common tests - if (!super.xObjectOK (xo)) { + /* Check if a color space dictionary is conformant */ + private static boolean colorSpaceOK(PdfDictionary cs) { + // If it's null, that's fine. + if (cs == null) { + return true; + } + // Walk through the color space dictionary, + // checking Separation and DeviceN resources + Iterator iter = cs.iterator(); + while (iter.hasNext()) { + PdfObject res = iter.next(); + if (res instanceof PdfArray) { + Vector resv = ((PdfArray) res).getContent(); + PdfSimpleObject snameobj = (PdfSimpleObject) resv.elementAt(0); + String sname = snameobj.getStringValue(); + if ("Separation".equals(sname) || "DeviceN".equals(sname)) { + PdfSimpleObject altSpaceObj = (PdfSimpleObject) resv.elementAt(2); + String altSpace = altSpaceObj.getStringValue(); + if (!("DeviceGray".equals(altSpace) || "DeviceCMYK".equals(altSpace))) { return false; + } } - // Tests specific to X/1 - try { - - PdfDictionary opi = (PdfDictionary) xo.get ("OPI"); - if (opi == null) { - // If it isn't an OPI object, we don't care - return true; - } - _x1aCompliant = false; // OPI objects aren't allowed in X-1a - // get the version 2.0 dictionary. If it has only - // a 1.3 dictionary, X1 apparently is indifferent. - PdfDictionary opi20 = (PdfDictionary) - _module.resolveIndirectObject (opi.get ("2.0")); - if (opi20 == null) { - return true; - } - // Now what we came for. The Inks entry is optional, - // but if present, must be full_color, registration, - // or an array containing monochrome as its first value. - // If monochrome, all ink names must be CMYK colorants. - // (Unfortunately, the spec doesn't tell us exactly - // what these names should be: C? Cyan? cyan?) - PdfObject inks = _module.resolveIndirectObject - (opi20.get ("Inks")); - if (inks == null) { - return true; - } - if (inks instanceof PdfSimpleObject) { - String inkname = ((PdfSimpleObject) inks).getStringValue (); - if (!("full_color".equals (inkname) || - "registration".equals (inkname))) { - return false; - } - } - else if (inks instanceof PdfArray) { - Vector inkvec = ((PdfArray) inks).getContent (); - PdfSimpleObject inkobj = (PdfSimpleObject) - inkvec.elementAt (0); - if (!("monochrome".equals (inkobj.getStringValue ()))) { - return false; - } - } - - // Next, the referenced file must be included as - // an EmbeddedFile. A file specification can be either - // a dictionary or a string. I don't understand what's - // being said on page 124. EmbeddedFiles maps name strings - // to embedded file streams; but exactly what are the name - // strings it uses? - - _module.resolveIndirectObject (opi20.get ("F")); - _module.getEmbeddedFiles (); - // Leave this for now, till I can make some sense of it. - } - catch (Exception e) { + if ("Indexed".equals(sname) || "Pattern".equals(sname)) { + // Indexed and pattern color spaces must have a + // base colorspace of DeviceCMYK, DeviceGray, + // DeviceN, or Separation. + PdfSimpleObject baseObj = (PdfSimpleObject) resv.elementAt(1); + String base = baseObj.getStringValue(); + if (!("DeviceCMYK".equals(base) + || "DeviceGray".equals(base) + || "DeviceN".equals(base) + || "Separation".equals(base))) { return false; + } } - return true; // passed all tests + } } - - + return true; // passed all tests + } + /* Checks a single XObject. */ + @Override + protected boolean xObjectOK(PdfDictionary xo) { + if (xo == null) { + // no XObject means no problem + return true; + } + // Do common tests + if (!super.xObjectOK(xo)) { + return false; + } + // Tests specific to X/1 + try { - private boolean encryptionOK () - { - PdfDictionary encryptDict = _module.getEncryptionDict (); - if (encryptDict == null) { - return true; //no encryption is good encryption - } - try { - PdfSimpleObject filter = (PdfSimpleObject) encryptDict.get ("Filter"); - if (!"Standard".equals (filter.getStringValue ())) { - return false; - } - - // the permissions must include bit 3 (printing). In PDF's - // notation, bit 1 is the low-order bit. - PdfSimpleObject perm = (PdfSimpleObject) encryptDict.get ("P"); - if (perm == null) { - // P is required with standard encryption - return false; - } - if ((perm.getIntValue () & 4) == 0) { - return false; - } + PdfDictionary opi = (PdfDictionary) xo.get("OPI"); + if (opi == null) { + // If it isn't an OPI object, we don't care + return true; + } + _x1aCompliant = false; // OPI objects aren't allowed in X-1a + // get the version 2.0 dictionary. If it has only + // a 1.3 dictionary, X1 apparently is indifferent. + PdfDictionary opi20 = (PdfDictionary) _module.resolveIndirectObject(opi.get("2.0")); + if (opi20 == null) { + return true; + } + // Now what we came for. The Inks entry is optional, + // but if present, must be full_color, registration, + // or an array containing monochrome as its first value. + // If monochrome, all ink names must be CMYK colorants. + // (Unfortunately, the spec doesn't tell us exactly + // what these names should be: C? Cyan? cyan?) + PdfObject inks = _module.resolveIndirectObject(opi20.get("Inks")); + if (inks == null) { + return true; + } + if (inks instanceof PdfSimpleObject) { + String inkname = ((PdfSimpleObject) inks).getStringValue(); + if (!("full_color".equals(inkname) || "registration".equals(inkname))) { + return false; } - catch (Exception e) { - return false; + } else if (inks instanceof PdfArray) { + Vector inkvec = ((PdfArray) inks).getContent(); + PdfSimpleObject inkobj = (PdfSimpleObject) inkvec.elementAt(0); + if (!("monochrome".equals(inkobj.getStringValue()))) { + return false; } - return true; + } + + // Next, the referenced file must be included as + // an EmbeddedFile. A file specification can be either + // a dictionary or a string. I don't understand what's + // being said on page 124. EmbeddedFiles maps name strings + // to embedded file streams; but exactly what are the name + // strings it uses? + + _module.resolveIndirectObject(opi20.get("F")); + _module.getEmbeddedFiles(); + // Leave this for now, till I can make some sense of it. + } catch (Exception e) { + return false; } + return true; // passed all tests + } + private boolean encryptionOK() { + PdfDictionary encryptDict = _module.getEncryptionDict(); + if (encryptDict == null) { + return true; // no encryption is good encryption + } + try { + PdfSimpleObject filter = (PdfSimpleObject) encryptDict.get("Filter"); + if (!"Standard".equals(filter.getStringValue())) { + return false; + } - /* Check for LZW and JBIG2 filters, which are forbidden in X/1a. - This does not affect X/1 compliance. */ - private boolean filter1AOK (PdfObject filters) - { - return !hasFilters (filters, - new String [] { "LZWDecode", "JBIG2Decode" } ); + // the permissions must include bit 3 (printing). In PDF's + // notation, bit 1 is the low-order bit. + PdfSimpleObject perm = (PdfSimpleObject) encryptDict.get("P"); + if (perm == null) { + // P is required with standard encryption + return false; + } + if ((perm.getIntValue() & 4) == 0) { + return false; + } + } catch (Exception e) { + return false; } + return true; + } - /** Checks if a Form xobject is valid. This overrides the method in - XProfileBase. */ - @Override - protected boolean formObjectOK (PdfDictionary xo) - { - // PDF-X/1-a elements can't have a Ref key in the - // Form dictionary. - if (xo.get ("Ref") != null) { - // This is an external reference XObject. - _x1aCompliant = false; - } - // Form objects aren't restricted in X/1 - return true; + /* Check for LZW and JBIG2 filters, which are forbidden in X/1a. + This does not affect X/1 compliance. */ + private boolean filter1AOK(PdfObject filters) { + return !hasFilters(filters, new String[] {"LZWDecode", "JBIG2Decode"}); + } + + /** Checks if a Form xobject is valid. This overrides the method in XProfileBase. */ + @Override + protected boolean formObjectOK(PdfDictionary xo) { + // PDF-X/1-a elements can't have a Ref key in the + // Form dictionary. + if (xo.get("Ref") != null) { + // This is an external reference XObject. + _x1aCompliant = false; } + // Form objects aren't restricted in X/1 + return true; + } } diff --git a/jhove-modules/pdf-hul/src/main/java/edu/harvard/hul/ois/jhove/module/pdf/X1aProfile.java b/jhove-modules/pdf-hul/src/main/java/edu/harvard/hul/ois/jhove/module/pdf/X1aProfile.java index 7f89f56a8..96aebc0b4 100644 --- a/jhove-modules/pdf-hul/src/main/java/edu/harvard/hul/ois/jhove/module/pdf/X1aProfile.java +++ b/jhove-modules/pdf-hul/src/main/java/edu/harvard/hul/ois/jhove/module/pdf/X1aProfile.java @@ -1,79 +1,64 @@ - package edu.harvard.hul.ois.jhove.module.pdf; import edu.harvard.hul.ois.jhove.module.*; import java.util.logging.*; /** - * PDF profile checker for PDF/X-1a documents. - * See ISO Standard 15930-1, "Complete exchange using - * CMYK data (PDF/X-1 and PDF/X-1a)" - * - * This module depends on the PDF/X-1 profiler, since the PDF/X-1 specification - * is PDF/X-1 plus a few additional restrictions. + * PDF profile checker for PDF/X-1a documents. See ISO Standard 15930-1, "Complete exchange using + * CMYK data (PDF/X-1 and PDF/X-1a)" + * + *

This module depends on the PDF/X-1 profiler, since the PDF/X-1 specification is PDF/X-1 plus a + * few additional restrictions. */ -public final class X1aProfile extends XProfileBase -{ - /****************************************************************** - * PRIVATE CLASS FIELDS. - ******************************************************************/ - - private Logger _logger; +public final class X1aProfile extends XProfileBase { + /** + * **************************************************************** PRIVATE CLASS FIELDS. + * **************************************************************** + */ + private Logger _logger; - private X1Profile _x1Profile; + private X1Profile _x1Profile; - /** - * Constructor. - * Creates an X1aProfile object for subsequent testing. - * - * @param module The module under which we are checking the profile. - * - */ - public X1aProfile (PdfModule module) - { - super (module, XProfileBase.PDFX1A); - _logger = Logger.getLogger ("edu.harvard.hul.ois.jhove.module"); - _profileText = "ISO PDF/X-1a"; - } - - /** - * Calling setX1Profile links this X1aProfiler to an X1Profiler. - * - */ - public void setX1Profile (X1Profile x1) - { - _x1Profile = x1; - } + /** + * Constructor. Creates an X1aProfile object for subsequent testing. + * + * @param module The module under which we are checking the profile. + */ + public X1aProfile(PdfModule module) { + super(module, XProfileBase.PDFX1A); + _logger = Logger.getLogger("edu.harvard.hul.ois.jhove.module"); + _profileText = "ISO PDF/X-1a"; + } - /** - * Returns true if the document satisfies the profile. - * If setX1Profile hasn't been called, - * creates a temporary X1Profile and tests against that profile first. - * Either way, X1Profile.isX1aCompliant is then called - * to determine the X-1/a compliance status. - * - */ - @Override - public boolean satisfiesThisProfile () - { - _logger.info("Checking X1A profile"); - if (_x1Profile != null) { - // If there is a linked X1Profile, we save time by checking if - // it passed or not. - if (!_x1Profile.isAlreadyOK ()) { - return false; - } - } - else { - // If there isn't a linked X1Profile, create one - // and check it. - _x1Profile = new X1Profile (_module); - if (!_x1Profile.satisfiesProfile (_raf, _parser)) { - return false; - } - } + /** Calling setX1Profile links this X1aProfiler to an X1Profiler. */ + public void setX1Profile(X1Profile x1) { + _x1Profile = x1; + } - return _x1Profile.isX1aCompliant (); + /** + * Returns true if the document satisfies the profile. If setX1Profile + * hasn't been called, creates a temporary X1Profile and tests against that profile first. Either + * way, X1Profile.isX1aCompliant is then called to determine the X-1/a compliance + * status. + */ + @Override + public boolean satisfiesThisProfile() { + _logger.info("Checking X1A profile"); + if (_x1Profile != null) { + // If there is a linked X1Profile, we save time by checking if + // it passed or not. + if (!_x1Profile.isAlreadyOK()) { + return false; + } + } else { + // If there isn't a linked X1Profile, create one + // and check it. + _x1Profile = new X1Profile(_module); + if (!_x1Profile.satisfiesProfile(_raf, _parser)) { + return false; + } } + return _x1Profile.isX1aCompliant(); + } } diff --git a/jhove-modules/pdf-hul/src/main/java/edu/harvard/hul/ois/jhove/module/pdf/X2Profile.java b/jhove-modules/pdf-hul/src/main/java/edu/harvard/hul/ois/jhove/module/pdf/X2Profile.java index c2014936e..009c05109 100644 --- a/jhove-modules/pdf-hul/src/main/java/edu/harvard/hul/ois/jhove/module/pdf/X2Profile.java +++ b/jhove-modules/pdf-hul/src/main/java/edu/harvard/hul/ois/jhove/module/pdf/X2Profile.java @@ -1,228 +1,199 @@ - package edu.harvard.hul.ois.jhove.module.pdf; import edu.harvard.hul.ois.jhove.module.*; import java.util.*; /** - * PDF profile checker for PDF/X-2 documents. - * See ISO Standard ISO 15930-2:2003(E), "Graphic technology - - * Prepress digital data exchange Use of PDF - Part 2: - * Partial exchange of printing data (PDF/X-2)" + * PDF profile checker for PDF/X-2 documents. See ISO Standard ISO 15930-2:2003(E), "Graphic + * technology - Prepress digital data exchange Use of PDF - Part 2: Partial exchange of printing + * data (PDF/X-2)" */ -public final class X2Profile extends XProfileBase -{ - /****************************************************************** - * PRIVATE CLASS FIELDS. - ******************************************************************/ - - /** - * Constructor. - * Creates an X2Profile object for subsequent testing. - * - * @param module The module under which we are checking the profile. - * - */ - public X2Profile (PdfModule module) - { - super (module, XProfileBase.PDFX2); - _profileText = "ISO PDF/X-2"; - } +public final class X2Profile extends XProfileBase { + /** + * **************************************************************** PRIVATE CLASS FIELDS. + * **************************************************************** + */ - /** - * Returns true if the document satisfies the profile. - * - */ - @Override - public boolean satisfiesThisProfile () - { - try { - // First off, there must be an OutputIntents array - // in the document catalog dictionary. - PdfDictionary catDict = _module.getCatalogDict (); - PdfArray intentsArray = (PdfArray) _module.resolveIndirectObject - (catDict.get ("OutputIntents")); - if (intentsArray == null) { - return false; - } - - // Next check if the OutputIntents are valid. - if (!outputIntentsOK (intentsArray)) { - return false; - } + /** + * Constructor. Creates an X2Profile object for subsequent testing. + * + * @param module The module under which we are checking the profile. + */ + public X2Profile(PdfModule module) { + super(module, XProfileBase.PDFX2); + _profileText = "ISO PDF/X-2"; + } - // Check that bounding boxes are present as required. - // MediaBox is required. - if (!bboxOK (true)) { - return false; - } + /** Returns true if the document satisfies the profile. */ + @Override + public boolean satisfiesThisProfile() { + try { + // First off, there must be an OutputIntents array + // in the document catalog dictionary. + PdfDictionary catDict = _module.getCatalogDict(); + PdfArray intentsArray = + (PdfArray) _module.resolveIndirectObject(catDict.get("OutputIntents")); + if (intentsArray == null) { + return false; + } - // Check that ViewerPreferences meet certain restrictions - // if any BleedBoxes are present. - if (!checkPrefsAgainstBleedBox ()) { - return false; - } + // Next check if the OutputIntents are valid. + if (!outputIntentsOK(intentsArray)) { + return false; + } - // Check resources and other stuff. - if (!resourcesOK ()) { - return false; - } + // Check that bounding boxes are present as required. + // MediaBox is required. + if (!bboxOK(true)) { + return false; + } - // Check the trailer dictionary. - if (!trailerDictOK ()) { - return false; - } + // Check that ViewerPreferences meet certain restrictions + // if any BleedBoxes are present. + if (!checkPrefsAgainstBleedBox()) { + return false; + } + // Check resources and other stuff. + if (!resourcesOK()) { + return false; + } - // Check specific requirements on the doc info dictionary. - if (!infoDictOK ("PDF/X-2:")) { - return false; - } + // Check the trailer dictionary. + if (!trailerDictOK()) { + return false; + } + // Check specific requirements on the doc info dictionary. + if (!infoDictOK("PDF/X-2:")) { + return false; + } - } - catch (Exception e) { - // Any otherwise uncaught exception means nonconformance - return false; - } - return true; // Placeholder + } catch (Exception e) { + // Any otherwise uncaught exception means nonconformance + return false; } - - /* Walk through the page tree and check all Resources dictionaries - that we find. Along the way, we check several things: - - Color spaces. Any Separation and DeviceN resources we - find must have an AlternateSpace of DeviceGray or - DeviceCMYK. - - Extended graphic states. - - XObjects. - */ - private boolean resourcesOK () - { - PageTreeNode docTreeRoot = _module.getDocumentTree (); - try { - docTreeRoot.startWalk (); - DocNode docNode; - for (;;) { - docNode = docTreeRoot.nextDocNode (); - if (docNode == null) { - break; - } - // Check for node-level resources - PdfDictionary rsrc = docNode.getResources (); - if (rsrc != null) { - - - // Check extended graphics state. - PdfDictionary gs = (PdfDictionary) - _module.resolveIndirectObject - (rsrc.get ("ExtGState")); - if (!extGStateOK (gs)) { - return false; - } - - // Check XObjects. - PdfDictionary xo = (PdfDictionary) - _module.resolveIndirectObject - (rsrc.get ("XObject")); - if (!xObjectsOK (xo)) { - return false; - } - } - - // Check content streams for resources - if (docNode instanceof PageObject) { - List streams = - ((PageObject) docNode).getContentStreams (); - if (streams != null) { - Iterator iter = streams.listIterator (); - while (iter.hasNext ()) { - PdfStream stream = iter.next (); - PdfDictionary dict = stream.getDict (); - PdfDictionary rs = - (PdfDictionary) dict.get ("Resources"); - if (rs != null) { - PdfDictionary gs = (PdfDictionary) - _module.resolveIndirectObject - (rs.get ("ExtGState")); - if (!extGStateOK (gs)) { - return false; - } - - PdfDictionary xo = (PdfDictionary) - _module.resolveIndirectObject - (rs.get ("XObject")); - if (!xObjectsOK (xo)) { - return false; - } - } - - // Also check for filters, to make sure - // there aren't any forbidden LZW filters. - PdfObject filters = - dict.get ("Filter"); - if (!filterOK (filters, true, true)) { - return false; - } - - } - } - - // Also check page objects for annotations -- - // in particular, TrapNet annotations. - PdfArray annots = ((PageObject) docNode).getAnnotations (); - if (annots != null) { - Vector annVec = annots.getContent (); - for (int i = 0; i < annVec.size (); i++) { - PdfDictionary annDict = (PdfDictionary) - _module.resolveIndirectObject - (annVec.elementAt (i)); - PdfSimpleObject subtypeObj = (PdfSimpleObject) annDict.get ("Subtype"); - if ("TrapNet".equals (subtypeObj.getStringValue ())) { - // FontFauxing must be absent or 0-length - PdfArray ff = (PdfArray) annDict.get ("FontFauxing"); - if (ff != null) { - Vector ffVec = ff.getContent (); - if (ffVec.size() > 0) { - return false; // a faux pas - } - } - } - } - } - } - } + return true; // Placeholder + } + + /* Walk through the page tree and check all Resources dictionaries + that we find. Along the way, we check several things: + + Color spaces. Any Separation and DeviceN resources we + find must have an AlternateSpace of DeviceGray or + DeviceCMYK. + + Extended graphic states. + + XObjects. + */ + private boolean resourcesOK() { + PageTreeNode docTreeRoot = _module.getDocumentTree(); + try { + docTreeRoot.startWalk(); + DocNode docNode; + for (; ; ) { + docNode = docTreeRoot.nextDocNode(); + if (docNode == null) { + break; } - catch (Exception e) { + // Check for node-level resources + PdfDictionary rsrc = docNode.getResources(); + if (rsrc != null) { + + // Check extended graphics state. + PdfDictionary gs = (PdfDictionary) _module.resolveIndirectObject(rsrc.get("ExtGState")); + if (!extGStateOK(gs)) { return false; - } - return true; // passed all tests - } + } - /** Checks if a Form xobject is valid. This overrides the method in - XProfileBase. */ - @Override - protected boolean formObjectOK (PdfDictionary xo) - { - // PDF-X/2 elements can't have an OPI key in Form - // or Image xobjects. - if (xo.get ("OPI") != null) { + // Check XObjects. + PdfDictionary xo = (PdfDictionary) _module.resolveIndirectObject(rsrc.get("XObject")); + if (!xObjectsOK(xo)) { return false; + } } - if (xo.get ("Ref") != null) { - // This is an external reference XObject. - // All PDF reference XOjbects must have a Page entry. - if (xo.get ("Page") == null) { + + // Check content streams for resources + if (docNode instanceof PageObject) { + List streams = ((PageObject) docNode).getContentStreams(); + if (streams != null) { + Iterator iter = streams.listIterator(); + while (iter.hasNext()) { + PdfStream stream = iter.next(); + PdfDictionary dict = stream.getDict(); + PdfDictionary rs = (PdfDictionary) dict.get("Resources"); + if (rs != null) { + PdfDictionary gs = + (PdfDictionary) _module.resolveIndirectObject(rs.get("ExtGState")); + if (!extGStateOK(gs)) { + return false; + } + + PdfDictionary xo = (PdfDictionary) _module.resolveIndirectObject(rs.get("XObject")); + if (!xObjectsOK(xo)) { + return false; + } + } + + // Also check for filters, to make sure + // there aren't any forbidden LZW filters. + PdfObject filters = dict.get("Filter"); + if (!filterOK(filters, true, true)) { return false; + } } - // An X/2 external reference XObject must also have a - // Metadata entry. - if (xo.get ("Metadata") == null) { - return false; + } + + // Also check page objects for annotations -- + // in particular, TrapNet annotations. + PdfArray annots = ((PageObject) docNode).getAnnotations(); + if (annots != null) { + Vector annVec = annots.getContent(); + for (int i = 0; i < annVec.size(); i++) { + PdfDictionary annDict = + (PdfDictionary) _module.resolveIndirectObject(annVec.elementAt(i)); + PdfSimpleObject subtypeObj = (PdfSimpleObject) annDict.get("Subtype"); + if ("TrapNet".equals(subtypeObj.getStringValue())) { + // FontFauxing must be absent or 0-length + PdfArray ff = (PdfArray) annDict.get("FontFauxing"); + if (ff != null) { + Vector ffVec = ff.getContent(); + if (ffVec.size() > 0) { + return false; // a faux pas + } + } + } } + } } - return true; + } + } catch (Exception e) { + return false; + } + return true; // passed all tests + } + + /** Checks if a Form xobject is valid. This overrides the method in XProfileBase. */ + @Override + protected boolean formObjectOK(PdfDictionary xo) { + // PDF-X/2 elements can't have an OPI key in Form + // or Image xobjects. + if (xo.get("OPI") != null) { + return false; + } + if (xo.get("Ref") != null) { + // This is an external reference XObject. + // All PDF reference XOjbects must have a Page entry. + if (xo.get("Page") == null) { + return false; + } + // An X/2 external reference XObject must also have a + // Metadata entry. + if (xo.get("Metadata") == null) { + return false; + } } + return true; + } } diff --git a/jhove-modules/pdf-hul/src/main/java/edu/harvard/hul/ois/jhove/module/pdf/X3Profile.java b/jhove-modules/pdf-hul/src/main/java/edu/harvard/hul/ois/jhove/module/pdf/X3Profile.java index 5af33695e..774b88eaf 100644 --- a/jhove-modules/pdf-hul/src/main/java/edu/harvard/hul/ois/jhove/module/pdf/X3Profile.java +++ b/jhove-modules/pdf-hul/src/main/java/edu/harvard/hul/ois/jhove/module/pdf/X3Profile.java @@ -1,226 +1,199 @@ - package edu.harvard.hul.ois.jhove.module.pdf; import edu.harvard.hul.ois.jhove.module.*; import java.util.*; /** - * PDF profile checker for PDF/X-3 documents. - * See ISO Standard 15930-3, "Complete exchange suitable - * for colour-managed workflows (PDF/X-3)" + * PDF profile checker for PDF/X-3 documents. See ISO Standard 15930-3, "Complete exchange suitable + * for colour-managed workflows (PDF/X-3)" */ -public final class X3Profile extends XProfileBase -{ - /****************************************************************** - * PRIVATE CLASS FIELDS. - ******************************************************************/ - - //private int _opiObjectCount; - - /** - * Constructor. - * Creates an X3Profile object for subsequent testing. - * - * @param module The module under which we are checking the profile. - * - */ - public X3Profile (PdfModule module) - { - super (module, XProfileBase.PDFX3); - _profileText = "ISO PDF/X-3"; +public final class X3Profile extends XProfileBase { + /** + * **************************************************************** PRIVATE CLASS FIELDS. + * **************************************************************** + */ + + // private int _opiObjectCount; + + /** + * Constructor. Creates an X3Profile object for subsequent testing. + * + * @param module The module under which we are checking the profile. + */ + public X3Profile(PdfModule module) { + super(module, XProfileBase.PDFX3); + _profileText = "ISO PDF/X-3"; + } + + /** Returns true if the document satisfies the profile. */ + @Override + public boolean satisfiesThisProfile() { + try { + // First off, there must be an OutputIntents array + // in the document catalog dictionary. + PdfDictionary catDict = _module.getCatalogDict(); + PdfArray intentsArray = + (PdfArray) _module.resolveIndirectObject(catDict.get("OutputIntents")); + if (intentsArray == null) { + return false; + } + + // Check specific requirements on the doc info dictionary. + if (!infoDictOK("PDF/X-3")) { + return false; + } + + // Next check if the OutputIntents are valid. + if (!outputIntentsOK(intentsArray)) { + return false; + } + + // Check resources and other stuff. + if (!resourcesOK()) { + return false; + } + + // Check the trailer dictionary. + if (!trailerDictOK()) { + return false; + } + + // Encryption dictionary is not allowed. + if (_module.getEncryptionDict() != null) { + return false; + } + + // Check that bounding boxes are present as required. + // MediaBox is not required. + if (!bboxOK(false)) { + return false; + } + + // If the document contains Actions, it's non-conformant + if (_module.getActionsExist()) { + return false; + } + } catch (Exception e) { + // Any otherwise uncaught exception means nonconformance + return false; } + return true; + } + + /* Walk through the page tree and check all Resources dictionaries + that we find. Along the way, we check several things: + + Color spaces. Any Separation and DeviceN resources we + find must have an AlternateSpace of DeviceGray or + DeviceCMYK. + + Extended graphic states. + + XObjects. + */ + private boolean resourcesOK() { + PageTreeNode docTreeRoot = _module.getDocumentTree(); + try { + docTreeRoot.startWalk(); + DocNode docNode; + for (; ; ) { + docNode = docTreeRoot.nextDocNode(); + if (docNode == null) { + break; + } + // Check for node-level resources + PdfDictionary rsrc = docNode.getResources(); + if (rsrc != null) { - /** - * Returns true if the document satisfies the profile. - * - */ - @Override - public boolean satisfiesThisProfile () - { - try { - // First off, there must be an OutputIntents array - // in the document catalog dictionary. - PdfDictionary catDict = _module.getCatalogDict (); - PdfArray intentsArray = (PdfArray) _module.resolveIndirectObject - (catDict.get ("OutputIntents")); - if (intentsArray == null) { - return false; - } - - // Check specific requirements on the doc info dictionary. - if (!infoDictOK ("PDF/X-3")) { - return false; - } - - // Next check if the OutputIntents are valid. - if (!outputIntentsOK (intentsArray)) { - return false; - } + // Check extended graphics state. + PdfDictionary gs = (PdfDictionary) _module.resolveIndirectObject(rsrc.get("ExtGState")); + if (!extGStateOK(gs)) { + return false; + } - // Check resources and other stuff. - if (!resourcesOK ()) { - return false; - } + // Check XObjects. + PdfDictionary xo = (PdfDictionary) _module.resolveIndirectObject(rsrc.get("XObject")); + if (!xObjectsOK(xo)) { + return false; + } + } - // Check the trailer dictionary. - if (!trailerDictOK ()) { - return false; - } + // Check content streams for resources + if (docNode instanceof PageObject) { + List streams = ((PageObject) docNode).getContentStreams(); + if (streams != null) { + Iterator iter = streams.listIterator(); + while (iter.hasNext()) { + PdfStream stream = iter.next(); + PdfDictionary dict = stream.getDict(); + PdfDictionary rs = (PdfDictionary) dict.get("Resources"); + if (rs != null) { + PdfDictionary gs = + (PdfDictionary) _module.resolveIndirectObject(rs.get("ExtGState")); + if (!extGStateOK(gs)) { + return false; + } - // Encryption dictionary is not allowed. - if (_module.getEncryptionDict () != null) { - return false; - } + PdfDictionary xo = (PdfDictionary) _module.resolveIndirectObject(rs.get("XObject")); + if (!xObjectsOK(xo)) { + return false; + } + } - // Check that bounding boxes are present as required. - // MediaBox is not required. - if (!bboxOK (false)) { + // Also check for filters, to make sure + // there aren't any forbidden LZW filters. + PdfObject filters = dict.get("Filter"); + if (!filterOK(filters, true, true)) { return false; - } + } - // If the document contains Actions, it's non-conformant - if (_module.getActionsExist ()) { + // External streams are also forbidden. + if (dict.get("F") != null) { return false; + } } - } - catch (Exception e) { - // Any otherwise uncaught exception means nonconformance - return false; - } - return true; - } - - - - /* Walk through the page tree and check all Resources dictionaries - that we find. Along the way, we check several things: - - Color spaces. Any Separation and DeviceN resources we - find must have an AlternateSpace of DeviceGray or - DeviceCMYK. - - Extended graphic states. - - XObjects. - */ - private boolean resourcesOK () - { - PageTreeNode docTreeRoot = _module.getDocumentTree (); - try { - docTreeRoot.startWalk (); - DocNode docNode; - for (;;) { - docNode = docTreeRoot.nextDocNode (); - if (docNode == null) { - break; - } - // Check for node-level resources - PdfDictionary rsrc = docNode.getResources (); - if (rsrc != null) { - - - // Check extended graphics state. - PdfDictionary gs = (PdfDictionary) - _module.resolveIndirectObject - (rsrc.get ("ExtGState")); - if (!extGStateOK (gs)) { - return false; - } - - // Check XObjects. - PdfDictionary xo = (PdfDictionary) - _module.resolveIndirectObject - (rsrc.get ("XObject")); - if (!xObjectsOK (xo)) { - return false; - } - } - - // Check content streams for resources - if (docNode instanceof PageObject) { - List streams = - ((PageObject) docNode).getContentStreams (); - if (streams != null) { - Iterator iter = streams.listIterator (); - while (iter.hasNext ()) { - PdfStream stream = iter.next (); - PdfDictionary dict = stream.getDict (); - PdfDictionary rs = - (PdfDictionary) dict.get ("Resources"); - if (rs != null) { - PdfDictionary gs = (PdfDictionary) - _module.resolveIndirectObject - (rs.get ("ExtGState")); - if (!extGStateOK (gs)) { - return false; - } - - PdfDictionary xo = (PdfDictionary) - _module.resolveIndirectObject - (rs.get ("XObject")); - if (!xObjectsOK (xo)) { - return false; - } - } - - // Also check for filters, to make sure - // there aren't any forbidden LZW filters. - PdfObject filters = - dict.get ("Filter"); - if (!filterOK (filters, true, true)) { - return false; - } - - // External streams are also forbidden. - if (dict.get ("F") != null) { - return false; - } - } - } - - // Also check page objects for annotations -- - // in particular, TrapNet annotations. - PdfArray annots = ((PageObject) docNode).getAnnotations (); - if (annots != null) { - Vector annVec = annots.getContent (); - for (int i = 0; i < annVec.size (); i++) { - PdfDictionary annDict = (PdfDictionary) - _module.resolveIndirectObject - (annVec.elementAt (i)); - PdfSimpleObject subtypeObj = (PdfSimpleObject) annDict.get ("Subtype"); - if ("TrapNet".equals (subtypeObj.getStringValue ())) { - // FontFauxing must be absent or 0-length - PdfArray ff = (PdfArray) annDict.get ("FontFauxing"); - if (ff != null) { - Vector ffVec = ff.getContent (); - if (ffVec.size() > 0) { - return false; // a faux pas - } - } - } - } - } + } + + // Also check page objects for annotations -- + // in particular, TrapNet annotations. + PdfArray annots = ((PageObject) docNode).getAnnotations(); + if (annots != null) { + Vector annVec = annots.getContent(); + for (int i = 0; i < annVec.size(); i++) { + PdfDictionary annDict = + (PdfDictionary) _module.resolveIndirectObject(annVec.elementAt(i)); + PdfSimpleObject subtypeObj = (PdfSimpleObject) annDict.get("Subtype"); + if ("TrapNet".equals(subtypeObj.getStringValue())) { + // FontFauxing must be absent or 0-length + PdfArray ff = (PdfArray) annDict.get("FontFauxing"); + if (ff != null) { + Vector ffVec = ff.getContent(); + if (ffVec.size() > 0) { + return false; // a faux pas + } } + } } + } } - catch (Exception e) { - return false; - } - return true; // passed all tests + } + } catch (Exception e) { + return false; } - - @Override - protected boolean xObjectOK (PdfDictionary xo) - { - if (xo == null) { - // no XObject means no problem - return true; - } - // Do common tests - if (!super.xObjectOK (xo)) { - return false; - } - // OPI objects aren't permitted - return xo.get ("OPI") == null; + return true; // passed all tests + } + + @Override + protected boolean xObjectOK(PdfDictionary xo) { + if (xo == null) { + // no XObject means no problem + return true; + } + // Do common tests + if (!super.xObjectOK(xo)) { + return false; } + // OPI objects aren't permitted + return xo.get("OPI") == null; + } } diff --git a/jhove-modules/pdf-hul/src/main/java/edu/harvard/hul/ois/jhove/module/pdf/XProfileBase.java b/jhove-modules/pdf-hul/src/main/java/edu/harvard/hul/ois/jhove/module/pdf/XProfileBase.java index 1fb308ab2..335a43190 100644 --- a/jhove-modules/pdf-hul/src/main/java/edu/harvard/hul/ois/jhove/module/pdf/XProfileBase.java +++ b/jhove-modules/pdf-hul/src/main/java/edu/harvard/hul/ois/jhove/module/pdf/XProfileBase.java @@ -1,466 +1,407 @@ - package edu.harvard.hul.ois.jhove.module.pdf; import edu.harvard.hul.ois.jhove.module.*; import java.util.*; /** - * Abstract base class for PDF profilers of the PDF/X family. - * See ISO Standard 15930-1, "Complete exchange using - * CMYK data (PDF/X-1 and PDF/X-1a)" + * Abstract base class for PDF profilers of the PDF/X family. See ISO Standard 15930-1, "Complete + * exchange using CMYK data (PDF/X-1 and PDF/X-1a)" */ -public abstract class XProfileBase extends PdfProfile -{ - /****************************************************************** - * PRIVATE CLASS FIELDS. - ******************************************************************/ - - /** Enumerated values for PDF-X type */ - public static int PDFX1 = 1, - PDFX1A = 2, - PDFX2 = 3, - PDFX3 = 4; - - /** PDF-X type used by the subclass. */ - protected int _xType; - - /** Set to true if a BleedBox is found. */ - protected boolean _bleedBoxPresent; +public abstract class XProfileBase extends PdfProfile { + /** + * **************************************************************** PRIVATE CLASS FIELDS. + * **************************************************************** + */ - /** - * Constructor. - * Creates an X1Profile object for subsequent testing. - * - * @param module The module under which we are checking the profile. - * @param xType The type of PDF/X profile being checked - * - */ - public XProfileBase (PdfModule module, int xType) - { - super (module); - _bleedBoxPresent = false; - _xType = xType; - } + /** Enumerated values for PDF-X type */ + public static int PDFX1 = 1, PDFX1A = 2, PDFX2 = 3, PDFX3 = 4; + /** PDF-X type used by the subclass. */ + protected int _xType; - /** Checks if the entries which are required in the document information - * dictionary by PDF-X/1 and X/3 are there. - * These entries are optional under the PDF specification, - * so they must be checked here. - */ - protected boolean infoDictOK (String xVersion) - { - PdfDictionary docInfo = _module.getDocInfo(); - try { - PdfSimpleObject trapped = - (PdfSimpleObject) docInfo.get ("Trapped"); - PdfSimpleObject xvers = (PdfSimpleObject) docInfo.get ("GTS_PDFXVersion"); - if (docInfo.get ("CreationDate") == null || - xvers == null || - docInfo.get ("ModDate") == null || - docInfo.get ("Title") == null || - trapped == null) { - return false; - } - - // The value of Trapped must be True or False. - // Unknown (and other random values) is prohibited. - String trappedVal = trapped.getStringValue (); - if (!("True".equals (trappedVal) || - "False".equals (trappedVal))) { - return false; - } - - // The value of GTS_PDFXVersion must begin with the value of xVersion. - String vers = xvers.getStringValue (); - if (!vers.startsWith (xVersion)) { - return false; - } - } - catch (Exception e) { - return false; - } - return true; - } + /** Set to true if a BleedBox is found. */ + protected boolean _bleedBoxPresent; + + /** + * Constructor. Creates an X1Profile object for subsequent testing. + * + * @param module The module under which we are checking the profile. + * @param xType The type of PDF/X profile being checked + */ + public XProfileBase(PdfModule module, int xType) { + super(module); + _bleedBoxPresent = false; + _xType = xType; + } + + /** + * Checks if the entries which are required in the document information dictionary by PDF-X/1 and + * X/3 are there. These entries are optional under the PDF specification, so they must be checked + * here. + */ + protected boolean infoDictOK(String xVersion) { + PdfDictionary docInfo = _module.getDocInfo(); + try { + PdfSimpleObject trapped = (PdfSimpleObject) docInfo.get("Trapped"); + PdfSimpleObject xvers = (PdfSimpleObject) docInfo.get("GTS_PDFXVersion"); + if (docInfo.get("CreationDate") == null + || xvers == null + || docInfo.get("ModDate") == null + || docInfo.get("Title") == null + || trapped == null) { + return false; + } + + // The value of Trapped must be True or False. + // Unknown (and other random values) is prohibited. + String trappedVal = trapped.getStringValue(); + if (!("True".equals(trappedVal) || "False".equals(trappedVal))) { + return false; + } - /** Returns true if a BleedBox has been detected. */ - protected boolean isBleedBoxPresent () - { - return _bleedBoxPresent; + // The value of GTS_PDFXVersion must begin with the value of xVersion. + String vers = xvers.getStringValue(); + if (!vers.startsWith(xVersion)) { + return false; + } + } catch (Exception e) { + return false; } + return true; + } + /** Returns true if a BleedBox has been detected. */ + protected boolean isBleedBoxPresent() { + return _bleedBoxPresent; + } - /** Checks if the OutputIntents of this document conform - * to profile requirements. - * There must be exactly one entry in the dictionary - * which has a subtype of GTS_PDFX. - */ - protected boolean outputIntentsOK (PdfArray intents) - { - Vector intVec = intents.getContent (); - int matchCount = 0; - try { - for (int i = 0; i < intVec.size (); i++) { - PdfDictionary intent = - (PdfDictionary) _module.resolveIndirectObject - (intVec.elementAt (i)); - PdfSimpleObject sval = (PdfSimpleObject) - _module.resolveIndirectObject (intent.get ("S")); - if (sval != null) { - String subtype = sval.getStringValue (); - if ("GTS_PDFX".equals (subtype)) { - ++matchCount; // there can be only one + /** + * Checks if the OutputIntents of this document conform to profile requirements. There must be + * exactly one entry in the dictionary which has a subtype of GTS_PDFX. + */ + protected boolean outputIntentsOK(PdfArray intents) { + Vector intVec = intents.getContent(); + int matchCount = 0; + try { + for (int i = 0; i < intVec.size(); i++) { + PdfDictionary intent = (PdfDictionary) _module.resolveIndirectObject(intVec.elementAt(i)); + PdfSimpleObject sval = (PdfSimpleObject) _module.resolveIndirectObject(intent.get("S")); + if (sval != null) { + String subtype = sval.getStringValue(); + if ("GTS_PDFX".equals(subtype)) { + ++matchCount; // there can be only one - // It must have an OutputConditionIdentifier - PdfSimpleObject outcond = (PdfSimpleObject) - _module.resolveIndirectObject - (intent.get("OutputConditionIdentifier")); - if (outcond == null) - return false; + // It must have an OutputConditionIdentifier + PdfSimpleObject outcond = + (PdfSimpleObject) + _module.resolveIndirectObject(intent.get("OutputConditionIdentifier")); + if (outcond == null) return false; - // X1 and 1a only: There must be either a RegistryName entry - // or a DestOutputProfile - // entry. Having them both is OK. - if (_xType == PDFX1) { - PdfSimpleObject regName = (PdfSimpleObject) - _module.resolveIndirectObject - (intent.get ("RegistryName")); - PdfStream dop = (PdfStream) - _module.resolveIndirectObject - (intent.get ("DestOutputProfile")); - if (regName == null && dop == null) { - return false; - } - /* This is WRONG for X-1 and X-1a. Was it - * supposed to go somewhere else?? */ -// if (dop != null) { -// // If present, the DestOutputProfile -// // must have an AtoB1Tag entry. -// PdfObject ab1 = dop.getDict().get ("AtoB1Tag"); -// if (ab1 == null) { -// return false; -// } -// } - } - } - } + // X1 and 1a only: There must be either a RegistryName entry + // or a DestOutputProfile + // entry. Having them both is OK. + if (_xType == PDFX1) { + PdfSimpleObject regName = + (PdfSimpleObject) _module.resolveIndirectObject(intent.get("RegistryName")); + PdfStream dop = + (PdfStream) _module.resolveIndirectObject(intent.get("DestOutputProfile")); + if (regName == null && dop == null) { + return false; + } + /* This is WRONG for X-1 and X-1a. Was it + * supposed to go somewhere else?? */ + // if (dop != null) { + // // If present, the DestOutputProfile + // // must have an AtoB1Tag entry. + // PdfObject ab1 = dop.getDict().get ("AtoB1Tag"); + // if (ab1 == null) { + // return false; + // } + // } } - return (matchCount == 1); - } - catch (Exception e) { - return false; + } } + } + return (matchCount == 1); + } catch (Exception e) { + return false; } + } - /** Checks profile requirements on the trailer dictionary. - */ - protected boolean trailerDictOK () - { - PdfDictionary trailerDict = _module.getTrailerDict (); - if (trailerDict == null) { - return false; // Something is SERIOUSLY wrong if this happens - } - // ID entry is required - return trailerDict.get ("ID") != null; + /** Checks profile requirements on the trailer dictionary. */ + protected boolean trailerDictOK() { + PdfDictionary trailerDict = _module.getTrailerDict(); + if (trailerDict == null) { + return false; // Something is SERIOUSLY wrong if this happens } + // ID entry is required + return trailerDict.get("ID") != null; + } - /** Checks if the ExtGState resource meets profile requirements. - * It may not have TR, TR2, or HTP entries. - */ - protected boolean extGStateOK (PdfDictionary gs) - { - if (gs == null) { - // no object means no problem - return true; + /** + * Checks if the ExtGState resource meets profile requirements. It may not have TR, TR2, or HTP + * entries. + */ + protected boolean extGStateOK(PdfDictionary gs) { + if (gs == null) { + // no object means no problem + return true; + } + try { + PdfObject tr = gs.get("TR"); + PdfObject tr2 = gs.get("TR2"); + PdfObject htp = gs.get("HTP"); + if (tr != null || tr2 != null || htp != null) { + return false; + } + + // If there is a halftone dictionary, it must meet + // certain requirements + PdfObject ht = gs.get("HT"); + if (ht instanceof PdfDictionary) { + // HalftoneType must be 1 or 5 + PdfDictionary htd = (PdfDictionary) ht; + PdfSimpleObject htType = (PdfSimpleObject) htd.get("HalftoneType"); + int htTypeVal = htType.getIntValue(); + if (htTypeVal != 1 && htTypeVal != 5) { + return false; } - try { - PdfObject tr = gs.get ("TR"); - PdfObject tr2 = gs.get ("TR2"); - PdfObject htp = gs.get ("HTP"); - if (tr != null || tr2 != null || htp != null) { - return false; - } - - // If there is a halftone dictionary, it must meet - // certain requirements - PdfObject ht = gs.get ("HT"); - if (ht instanceof PdfDictionary) { - // HalftoneType must be 1 or 5 - PdfDictionary htd = (PdfDictionary) ht; - PdfSimpleObject htType = (PdfSimpleObject) htd.get ("HalftoneType"); - int htTypeVal = htType.getIntValue (); - if (htTypeVal != 1 && htTypeVal != 5) { - return false; - } - } - - // The HalftoneName entry ist verboten - if (gs.get ("HalftoneName") != null) { - return false; - } + } - // The SMask entry is forbidden in X-1a and X-2 - // unless its value is "None" - if (_xType == PDFX1A || _xType == PDFX2) { - PdfSimpleObject smask = (PdfSimpleObject) gs.get ("SMask"); - if (smask != null) { - if (!"None".equals (smask.getStringValue ())) { - return false; - } - } + // The HalftoneName entry ist verboten + if (gs.get("HalftoneName") != null) { + return false; + } - // The values of BM, CA, and ca are restricted if - // these keys are present - PdfSimpleObject blendMode = - (PdfSimpleObject) gs.get ("BM"); - if (blendMode != null) { - String bmVal = blendMode.getStringValue (); - if (!"Normal".equals (bmVal) && - !"Compatible".equals (bmVal)) { - return false; - } - } - PdfSimpleObject ca = (PdfSimpleObject) gs.get ("CA"); - double caVal; - if (ca != null) { - caVal = ca.getDoubleValue (); - if (caVal != 1.0) { - return false; - } - } - ca = (PdfSimpleObject) gs.get ("ca"); - if (ca != null) { - caVal = ca.getDoubleValue (); - if (caVal != 1.0) { - return false; - } - } - } - } - catch (Exception e) { + // The SMask entry is forbidden in X-1a and X-2 + // unless its value is "None" + if (_xType == PDFX1A || _xType == PDFX2) { + PdfSimpleObject smask = (PdfSimpleObject) gs.get("SMask"); + if (smask != null) { + if (!"None".equals(smask.getStringValue())) { return false; + } } - return true; // passed all tests - } - - /** - * Checks a single XObject for xObjectsOK. Calls imageObjectOK - * and formObjectOK for profile-specific functionality. - */ - @Override - protected boolean xObjectOK (PdfDictionary xo) - { - if (xo == null) { - // no XObject means no problem - return true; + // The values of BM, CA, and ca are restricted if + // these keys are present + PdfSimpleObject blendMode = (PdfSimpleObject) gs.get("BM"); + if (blendMode != null) { + String bmVal = blendMode.getStringValue(); + if (!"Normal".equals(bmVal) && !"Compatible".equals(bmVal)) { + return false; + } } - try { - // PostScript XObjects aren't allowed. - // Image XObjects must meet certain tests. - PdfSimpleObject subtype = (PdfSimpleObject) xo.get ("Subtype"); - if (subtype != null) { - String subtypeVal = subtype.getStringValue (); - if ("PS".equals (subtypeVal)) { - // PS XObjects aren't allowed in any X format. - return false; - } - if ("Image".equals (subtypeVal) && !imageObjectOK (xo)) { - return false; - } - if ("Form".equals (subtypeVal) && !formObjectOK (xo)) { - return false; - } - } + PdfSimpleObject ca = (PdfSimpleObject) gs.get("CA"); + double caVal; + if (ca != null) { + caVal = ca.getDoubleValue(); + if (caVal != 1.0) { + return false; + } } - catch (Exception e) { - return false; + ca = (PdfSimpleObject) gs.get("ca"); + if (ca != null) { + caVal = ca.getDoubleValue(); + if (caVal != 1.0) { + return false; + } } - return true; + } + } catch (Exception e) { + return false; } + return true; // passed all tests + } - - /** Checks if a single image XObject fits the profile */ - protected boolean imageObjectOK (PdfDictionary xo) - { - try { - PdfArray alternates = (PdfArray) xo.get ("Alternates"); - if (alternates == null) { - // No alternates means we're fine - return true; - } - Vector altVec = alternates.getContent (); - for (int i = 0; i < altVec.size (); i++) { - PdfDictionary alt = (PdfDictionary) altVec.elementAt (i); - // No alternate may have DefaultForPrinting = true - PdfSimpleObject dfp = - (PdfSimpleObject) alt.get ("DefaultForPrinting"); - if (dfp.isTrue ()) { - return false; - } - } - if (_xType == PDFX2 && xo.get ("OPI") != null) { - // PDF-X/2 elements can't have an OPI key in Form - // or Image xobjects. - return false; - } - if (_xType == PDFX1A || _xType == PDFX2) { - // SMask is restricted in PDFX-1/a and X-2 - PdfSimpleObject smask = (PdfSimpleObject) xo.get ("SMask"); - if (smask != null && !"None".equals (smask.getStringValue ())) { - return false; - } - } + /** + * Checks a single XObject for xObjectsOK. Calls imageObjectOK and formObjectOK for + * profile-specific functionality. + */ + @Override + protected boolean xObjectOK(PdfDictionary xo) { + if (xo == null) { + // no XObject means no problem + return true; + } + try { + // PostScript XObjects aren't allowed. + // Image XObjects must meet certain tests. + PdfSimpleObject subtype = (PdfSimpleObject) xo.get("Subtype"); + if (subtype != null) { + String subtypeVal = subtype.getStringValue(); + if ("PS".equals(subtypeVal)) { + // PS XObjects aren't allowed in any X format. + return false; } - catch (Exception e) { - return false; + if ("Image".equals(subtypeVal) && !imageObjectOK(xo)) { + return false; + } + if ("Form".equals(subtypeVal) && !formObjectOK(xo)) { + return false; } - return true; // passed all tests + } + } catch (Exception e) { + return false; } + return true; + } - /** Checks the conformance of a form XObject. - * Does nothing; must be overridden if there are - * conditions on such forms. - */ - protected boolean formObjectOK (PdfDictionary fo) - { + /** Checks if a single image XObject fits the profile */ + protected boolean imageObjectOK(PdfDictionary xo) { + try { + PdfArray alternates = (PdfArray) xo.get("Alternates"); + if (alternates == null) { + // No alternates means we're fine return true; + } + Vector altVec = alternates.getContent(); + for (int i = 0; i < altVec.size(); i++) { + PdfDictionary alt = (PdfDictionary) altVec.elementAt(i); + // No alternate may have DefaultForPrinting = true + PdfSimpleObject dfp = (PdfSimpleObject) alt.get("DefaultForPrinting"); + if (dfp.isTrue()) { + return false; + } + } + if (_xType == PDFX2 && xo.get("OPI") != null) { + // PDF-X/2 elements can't have an OPI key in Form + // or Image xobjects. + return false; + } + if (_xType == PDFX1A || _xType == PDFX2) { + // SMask is restricted in PDFX-1/a and X-2 + PdfSimpleObject smask = (PdfSimpleObject) xo.get("SMask"); + if (smask != null && !"None".equals(smask.getStringValue())) { + return false; + } + } + } catch (Exception e) { + return false; } + return true; // passed all tests + } + /** + * Checks the conformance of a form XObject. Does nothing; must be overridden if there are + * conditions on such forms. + */ + protected boolean formObjectOK(PdfDictionary fo) { + return true; + } - - /** Checks all the page objects for bounding boxes. If requireMediaBox - * is true, each page has to include - * or inherit a MediaBox. - * Each page must include one but not both of a TrimBox and - * an ArtBox. - */ - protected boolean bboxOK (boolean requireMediaBox) - { - PageTreeNode pgtree = _module.getDocumentTree (); - try { - pgtree.startWalk (); - PageObject pageObject; - for (;;) { - pageObject = pgtree.nextPageObject (); - if (pageObject == null) { - break; - } - if (requireMediaBox) { - // Check for a MediaBox. If there isn't one here, one - // of its ancestors must have one. - PdfArray mbox = pageObject.getMediaBox (); - if (mbox == null) { - return false; - } - } - - // Check for TrimBox or ArtBox. Apply the Highlander rule. - PdfArray tbox = pageObject.getTrimBox (); - PdfArray abox = pageObject.getArtBox (); - if (tbox == null && abox == null) { - return false; - } - if (tbox != null && abox != null) { - return false; - } - - // BleedBox may be in conflict with other - // features. Record here whether any - // BleedBox is found. - if (pageObject.getBleedBox () != null) { - _bleedBoxPresent = true; - } - } + /** + * Checks all the page objects for bounding boxes. If requireMediaBox is true, each page has to + * include or inherit a MediaBox. Each page must include one but not both of a TrimBox and an + * ArtBox. + */ + protected boolean bboxOK(boolean requireMediaBox) { + PageTreeNode pgtree = _module.getDocumentTree(); + try { + pgtree.startWalk(); + PageObject pageObject; + for (; ; ) { + pageObject = pgtree.nextPageObject(); + if (pageObject == null) { + break; } - catch (Exception e) { + if (requireMediaBox) { + // Check for a MediaBox. If there isn't one here, one + // of its ancestors must have one. + PdfArray mbox = pageObject.getMediaBox(); + if (mbox == null) { return false; + } } - return true; // passed all tests - } + // Check for TrimBox or ArtBox. Apply the Highlander rule. + PdfArray tbox = pageObject.getTrimBox(); + PdfArray abox = pageObject.getArtBox(); + if (tbox == null && abox == null) { + return false; + } + if (tbox != null && abox != null) { + return false; + } - /** - * Checks ViewerPreferences dictionary against MediaBox - * and BleedBox. - * In PDF-X1/a and X2, if a BleedBox is present and - * if the ViewerPreferences dictionary contains the - * ViewClip, PrintArea or PrintClip keys, each of those - * keys present shall have the value MediaBox or BleedBox. - * This must be called after bboxOK has checked if any - * BleedBoxes are found. - */ - protected boolean checkPrefsAgainstBleedBox () - { - if (!_bleedBoxPresent) { - // No bleed box, the test isn't necessary. - return true; - } - PdfDictionary viewPrefDict = _module.getViewPrefDict (); - if (viewPrefDict == null) { - // No viewer prefs, passes the test trivially. - return true; - } - try { - PdfSimpleObject[] areas = new PdfSimpleObject[3]; - areas[0] = (PdfSimpleObject) viewPrefDict.get ("ViewArea"); - areas[1] = (PdfSimpleObject) viewPrefDict.get ("ViewClip"); - areas[2] = (PdfSimpleObject) viewPrefDict.get ("PrintArea"); + // BleedBox may be in conflict with other + // features. Record here whether any + // BleedBox is found. + if (pageObject.getBleedBox() != null) { + _bleedBoxPresent = true; + } + } + } catch (Exception e) { + return false; + } + return true; // passed all tests + } - for (int i = 0; i < 3; i++) { - if (areas[i] != null) { - String s = areas[i].getStringValue (); - if (!"MediaBox".equals (s) && - !"BleedBox".equals (s)) { - return false; - } - } - } - } - catch (Exception e) { - return false; - } - return true; + /** + * Checks ViewerPreferences dictionary against MediaBox and BleedBox. In PDF-X1/a and X2, if a + * BleedBox is present and if the ViewerPreferences dictionary contains the ViewClip, PrintArea or + * PrintClip keys, each of those keys present shall have the value MediaBox or BleedBox. This must + * be called after bboxOK has checked if any BleedBoxes are found. + */ + protected boolean checkPrefsAgainstBleedBox() { + if (!_bleedBoxPresent) { + // No bleed box, the test isn't necessary. + return true; } - /** - * Checks for forbidden filters in a Filters dictionary. - */ - protected boolean filterOK (PdfObject filters, - boolean forbidLZW, - boolean forbidJBIG2) - { - String filterName; - try { - if (filters == null) { - return true; - } - if (filters instanceof PdfSimpleObject) { - // Name of just one filter - filterName = ((PdfSimpleObject) filters).getStringValue (); - if ("LZWDecode".equals (filterName)) { - return false; - } - } - else { - // If it's not a name, it must be an array - Vector filterVec = ((PdfArray) filters).getContent (); - for (int i = 0; i < filterVec.size (); i++) { - PdfSimpleObject filter = - (PdfSimpleObject) filterVec.elementAt (i); - filterName = filter.getStringValue (); - if (forbidLZW && "LZWDecode".equals (filterName)) { - return false; - } - if (forbidJBIG2 && "JBIG2Decode".equals (filterName)) { - return false; - } - } - } + PdfDictionary viewPrefDict = _module.getViewPrefDict(); + if (viewPrefDict == null) { + // No viewer prefs, passes the test trivially. + return true; + } + try { + PdfSimpleObject[] areas = new PdfSimpleObject[3]; + areas[0] = (PdfSimpleObject) viewPrefDict.get("ViewArea"); + areas[1] = (PdfSimpleObject) viewPrefDict.get("ViewClip"); + areas[2] = (PdfSimpleObject) viewPrefDict.get("PrintArea"); + + for (int i = 0; i < 3; i++) { + if (areas[i] != null) { + String s = areas[i].getStringValue(); + if (!"MediaBox".equals(s) && !"BleedBox".equals(s)) { + return false; + } } - catch (Exception e) { - return false; + } + } catch (Exception e) { + return false; + } + return true; + } + /** Checks for forbidden filters in a Filters dictionary. */ + protected boolean filterOK(PdfObject filters, boolean forbidLZW, boolean forbidJBIG2) { + String filterName; + try { + if (filters == null) { + return true; + } + if (filters instanceof PdfSimpleObject) { + // Name of just one filter + filterName = ((PdfSimpleObject) filters).getStringValue(); + if ("LZWDecode".equals(filterName)) { + return false; } - return true; // passed all tests + } else { + // If it's not a name, it must be an array + Vector filterVec = ((PdfArray) filters).getContent(); + for (int i = 0; i < filterVec.size(); i++) { + PdfSimpleObject filter = (PdfSimpleObject) filterVec.elementAt(i); + filterName = filter.getStringValue(); + if (forbidLZW && "LZWDecode".equals(filterName)) { + return false; + } + if (forbidJBIG2 && "JBIG2Decode".equals(filterName)) { + return false; + } + } + } + } catch (Exception e) { + return false; } - + return true; // passed all tests + } } diff --git a/jhove-modules/pdf-hul/src/main/java/edu/harvard/hul/ois/jhove/module/pdf/package-info.java b/jhove-modules/pdf-hul/src/main/java/edu/harvard/hul/ois/jhove/module/pdf/package-info.java index 491cbc678..b357d6faf 100644 --- a/jhove-modules/pdf-hul/src/main/java/edu/harvard/hul/ois/jhove/module/pdf/package-info.java +++ b/jhove-modules/pdf-hul/src/main/java/edu/harvard/hul/ois/jhove/module/pdf/package-info.java @@ -1,4 +1,2 @@ -/** - * Contains supporting classes for the PDF-HUL module. - */ -package edu.harvard.hul.ois.jhove.module.pdf; \ No newline at end of file +/** Contains supporting classes for the PDF-HUL module. */ +package edu.harvard.hul.ois.jhove.module.pdf; diff --git a/jhove-modules/pdf-hul/src/test/java/edu/harvard/hul/ois/jhove/module/PdfModuleTest.java b/jhove-modules/pdf-hul/src/test/java/edu/harvard/hul/ois/jhove/module/PdfModuleTest.java index 2f341525e..a19806074 100644 --- a/jhove-modules/pdf-hul/src/test/java/edu/harvard/hul/ois/jhove/module/PdfModuleTest.java +++ b/jhove-modules/pdf-hul/src/test/java/edu/harvard/hul/ois/jhove/module/PdfModuleTest.java @@ -1,15 +1,14 @@ package edu.harvard.hul.ois.jhove.module; -import org.junit.runner.RunWith; -import org.junit.runners.Suite; -import org.junit.runners.Suite.SuiteClasses; - import edu.harvard.hul.ois.jhove.module.pdf.DocCatTests; import edu.harvard.hul.ois.jhove.module.pdf.HeaderTests; import edu.harvard.hul.ois.jhove.module.pdf.PageTreeTests; +import org.junit.runner.RunWith; +import org.junit.runners.Suite; +import org.junit.runners.Suite.SuiteClasses; @RunWith(Suite.class) -@SuiteClasses({ DocCatTests.class, HeaderTests.class, PageTreeTests.class }) +@SuiteClasses({DocCatTests.class, HeaderTests.class, PageTreeTests.class}) class PdfModuleTest { - // Empty test suite that runs the PDF Module tests. + // Empty test suite that runs the PDF Module tests. } diff --git a/jhove-modules/pdf-hul/src/test/java/edu/harvard/hul/ois/jhove/module/TestUtils.java b/jhove-modules/pdf-hul/src/test/java/edu/harvard/hul/ois/jhove/module/TestUtils.java index cacd6fabf..cd2effc03 100644 --- a/jhove-modules/pdf-hul/src/test/java/edu/harvard/hul/ois/jhove/module/TestUtils.java +++ b/jhove-modules/pdf-hul/src/test/java/edu/harvard/hul/ois/jhove/module/TestUtils.java @@ -4,6 +4,9 @@ import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; +import edu.harvard.hul.ois.jhove.Message; +import edu.harvard.hul.ois.jhove.Module; +import edu.harvard.hul.ois.jhove.RepInfo; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; @@ -12,238 +15,220 @@ import java.io.RandomAccessFile; import java.net.URISyntaxException; -import edu.harvard.hul.ois.jhove.Message; -import edu.harvard.hul.ois.jhove.Module; -import edu.harvard.hul.ois.jhove.RepInfo; - /** - * Convenience methods to test that the result of JHOVE validation are as - * expected. The tests are: + * Convenience methods to test that the result of JHOVE validation are as expected. The tests are: + * *

    - *
  • The well formed result is equal to a pre-defined value.
  • - *
  • The is valid result is equal to a pre-defined value.
  • - *
  • The message list contains an expected message (optionally).
  • + *
  • The well formed result is equal to a pre-defined value. + *
  • The is valid result is equal to a pre-defined value. + *
  • The message list contains an expected message (optionally). *
* - * @author Carl Wilson - * carlwilson AT github + * @author Carl Wilson carlwilson AT github * @version 0.1 Created 14 Mar 2018:20:14:19 */ - public final class TestUtils { - public static final String MODULE_RESOURCE_BASE = "/edu/harvard/hul/ois/jhove/module/"; - public static final String EMPTY_FILE_PATH = MODULE_RESOURCE_BASE + "empty"; - - private TestUtils() { - // Keep out - throw new AssertionError("Should never be in constructor."); - } - - /** - * Convenience method that takes the path to a test resource and tests that - * the results of JHOVE are as expected. - * - * @param module - * a {@link edu.harvard.hul.ois.jhove.Module} instance - * to use to validate the resource. - * @param resToTest - * the String path of the resource to validate and test. - * @param expctWllFrmd - * the expected well formed value - * @param expctVld - * the expected is valid value - * @throws URISyntaxException - * when there's an issue converting the resource name to a path - */ - public static RepInfo testValidateResource(final Module module, - final String resToTest, final int expctWllFrmd, final int expctVld) throws URISyntaxException { - return testValidateResource(module, resToTest, expctWllFrmd, expctVld, null); - } - - /** - * - * @param module - * a {@link edu.harvard.hul.ois.jhove.Module} instance - * to use to validate the resource. - * @param resToTest - * the String path of the resource to validate and test. - * @param expctWllFrmd - * the expected well formed value - * @param expctVld - * the expected is valid value - * @param message - * a JHOVE validation string message expected to be found in the - * list of validation messages. If this parameter is null the - * test isn't performed. - * @throws URISyntaxException - * when there's an issue converting the resource name to a path - */ - public static RepInfo testValidateResource(final Module module, - final String resToTest, final int expctWllFrmd, final int expctVld, - final String message) throws URISyntaxException { - return testValidateResource(module, resToTest, expctWllFrmd, expctVld, message, true); - } - - /** - * - * @param module - * a {@link edu.harvard.hul.ois.jhove.Module} instance - * to use to validate the resource. - * @param resToTest - * the String path of the resource to validate and test. - * @param expctWllFrmd - * the expected well formed value - * @param expctVld - * the expected is valid value - * @param message - * a JHOVE validation string message which MUST be found in the - * list of validation messages if messMustBePresent is true. - * When messMustBePresent is false the message MUST NOT be - * found in the list of validation messages. - * If this parameter is null the test isn't performed. - * @param messMustBePresent - * if message is not null this param dictates whether the - * test expects the validation message to be in the report or not. - * @throws URISyntaxException - * when there's an issue converting the resource name to a path - */ - public static RepInfo testValidateResource(final Module module, - final String resToTest, final int expctWllFrmd, final int expctVld, - final String expctMessage, boolean messMustBePresent ) throws URISyntaxException { - File toTest = new File(TestUtils.class.getResource(resToTest).toURI()); - - return testValidateFile(module, toTest, expctWllFrmd, expctVld, - expctMessage, messMustBePresent); - } - - /** - * Method that takes a file and tests that the results of JHOVE are as - * expected. - * - * @param pdfModule - * a {@link edu.harvard.hul.ois.jhove.module.PdfModule} instance - * to use to validate the resource. - * @param fileToTest - * a Java File instance to validate - * @param expctWllFrmd - * the expected well formed value - * @param expctVld - * the expected is valid value - * @param expctMessage - * a JHOVE validation string message expected to be found in the - * list of validation messages. If this parameter is null the - * test isn't performed. - */ - public static RepInfo testValidateFile(final Module module, - final File fileToTest, final int expctWllFrmd, final int expctVld) { - return testValidateFile(module, fileToTest, expctWllFrmd, expctVld, null); - } - - public static RepInfo testValidateFile(final Module module, - final File fileToTest, final int expctWllFrmd, final int expctVld, - final String message) { - return testValidateFile(module, fileToTest, expctWllFrmd, expctVld, message, true); - } - - public static RepInfo testValidateFile(final Module module, - final File fileToTest, final int expctWllFrmd, final int expctVld, - final String message, boolean messMustBePresent) { - RepInfo info = parseTestFile(module, fileToTest); - testResult(info, expctWllFrmd, expctVld, message, messMustBePresent); - return info; - } - - private static void testResult(final RepInfo info, final int expctWllFrmd, - final int expctVld, final String message, boolean messMustBePresent) { - testWellFormed(info, expctWllFrmd); - testIsValid(info, expctVld); - if (message == null) { - return; - } - Message jhoveMessage = getMessageIfPresent(info, message); - if (messMustBePresent) { - if (jhoveMessage == null) { - System.out.println(String.format( - "Expected message: %s, not found.", message)); - outputMessages(info); - } - assertTrue("Expected message: " + message, jhoveMessage != null); - } else { - if (jhoveMessage != null) { - System.out.println(String.format( - "Unexpected message: %s, found.", jhoveMessage.getMessage())); - outputMessages(info); - } - assertTrue("Unexpected message: " + message, jhoveMessage == null); - } - } - - private static void outputMessages(final RepInfo info) { - System.out.println("Messages in Report Info:"); - for (Message mess : info.getMessage()) { - System.out.println(String.format(" - %s", mess.getMessage())); - } - } - - private static RepInfo parseTestFile(final Module module, - final File toTest) { - if (module.isRandomAccess()) { - return rafModuleTest(module, toTest); - } - return streamModuleTest(module, toTest); - } - - - private static RepInfo streamModuleTest(final Module fisModule, final File toTest) { - RepInfo info = new RepInfo(toTest.getName()); - try (InputStream fis = new FileInputStream(toTest)) { - int index = fisModule.parse(fis, info, 0); - while (index > 0) { - index = fisModule.parse(fis, info, 0); - } - } catch (FileNotFoundException excep) { - excep.printStackTrace(); - fail("Couldn't find file to test: " + toTest.getName()); - } catch (IOException excep) { - excep.printStackTrace(); - fail("IOException Reading: " + toTest.getName()); - } - return info; - } - - private static RepInfo rafModuleTest(final Module rafModule, final File toTest) { - RepInfo info = new RepInfo(toTest.getName()); - try (RandomAccessFile raf = new RandomAccessFile(toTest, "r")) { - rafModule.parse(raf, info); - } catch (FileNotFoundException excep) { - excep.printStackTrace(); - fail("Couldn't find file to test: " + toTest.getName()); - } catch (IOException excep) { - excep.printStackTrace(); - fail("IOException Reading: " + toTest.getName()); - } - return info; - } - - private static void testWellFormed(final RepInfo info, final int expctWllFrmd) { - String message = (expctWllFrmd == RepInfo.TRUE) - ? "Should be well formed." - : "Should NOT be well formed."; - assertEquals(message, expctWllFrmd, info.getWellFormed()); - } - - private static void testIsValid(final RepInfo info, final int expctVld) { - String message = (expctVld == RepInfo.TRUE) ? "Should be valid." - : "Should NOT be valid."; - assertEquals(message, expctVld, info.getValid()); - } - - private static Message getMessageIfPresent(final RepInfo info, final String expctMessage) { - for (Message message : info.getMessage()) { - if (message.getMessage().equals(expctMessage)) { - return message; - } - } - return null; - } + public static final String MODULE_RESOURCE_BASE = "/edu/harvard/hul/ois/jhove/module/"; + public static final String EMPTY_FILE_PATH = MODULE_RESOURCE_BASE + "empty"; + + private TestUtils() { + // Keep out + throw new AssertionError("Should never be in constructor."); + } + + /** + * Convenience method that takes the path to a test resource and tests that the results of JHOVE + * are as expected. + * + * @param module a {@link edu.harvard.hul.ois.jhove.Module} instance to use to validate the + * resource. + * @param resToTest the String path of the resource to validate and test. + * @param expctWllFrmd the expected well formed value + * @param expctVld the expected is valid value + * @throws URISyntaxException when there's an issue converting the resource name to a path + */ + public static RepInfo testValidateResource( + final Module module, final String resToTest, final int expctWllFrmd, final int expctVld) + throws URISyntaxException { + return testValidateResource(module, resToTest, expctWllFrmd, expctVld, null); + } + + /** + * @param module a {@link edu.harvard.hul.ois.jhove.Module} instance to use to validate the + * resource. + * @param resToTest the String path of the resource to validate and test. + * @param expctWllFrmd the expected well formed value + * @param expctVld the expected is valid value + * @param message a JHOVE validation string message expected to be found in the list of validation + * messages. If this parameter is null the test isn't performed. + * @throws URISyntaxException when there's an issue converting the resource name to a path + */ + public static RepInfo testValidateResource( + final Module module, + final String resToTest, + final int expctWllFrmd, + final int expctVld, + final String message) + throws URISyntaxException { + return testValidateResource(module, resToTest, expctWllFrmd, expctVld, message, true); + } + + /** + * @param module a {@link edu.harvard.hul.ois.jhove.Module} instance to use to validate the + * resource. + * @param resToTest the String path of the resource to validate and test. + * @param expctWllFrmd the expected well formed value + * @param expctVld the expected is valid value + * @param message a JHOVE validation string message which MUST be found in the list of validation + * messages if messMustBePresent is true. When messMustBePresent is false the message MUST NOT + * be found in the list of validation messages. If this parameter is null the test isn't + * performed. + * @param messMustBePresent if message is not null this param dictates whether the test expects + * the validation message to be in the report or not. + * @throws URISyntaxException when there's an issue converting the resource name to a path + */ + public static RepInfo testValidateResource( + final Module module, + final String resToTest, + final int expctWllFrmd, + final int expctVld, + final String expctMessage, + boolean messMustBePresent) + throws URISyntaxException { + File toTest = new File(TestUtils.class.getResource(resToTest).toURI()); + + return testValidateFile( + module, toTest, expctWllFrmd, expctVld, expctMessage, messMustBePresent); + } + + /** + * Method that takes a file and tests that the results of JHOVE are as expected. + * + * @param pdfModule a {@link edu.harvard.hul.ois.jhove.module.PdfModule} instance to use to + * validate the resource. + * @param fileToTest a Java File instance to validate + * @param expctWllFrmd the expected well formed value + * @param expctVld the expected is valid value + * @param expctMessage a JHOVE validation string message expected to be found in the list of + * validation messages. If this parameter is null the test isn't performed. + */ + public static RepInfo testValidateFile( + final Module module, final File fileToTest, final int expctWllFrmd, final int expctVld) { + return testValidateFile(module, fileToTest, expctWllFrmd, expctVld, null); + } + + public static RepInfo testValidateFile( + final Module module, + final File fileToTest, + final int expctWllFrmd, + final int expctVld, + final String message) { + return testValidateFile(module, fileToTest, expctWllFrmd, expctVld, message, true); + } + + public static RepInfo testValidateFile( + final Module module, + final File fileToTest, + final int expctWllFrmd, + final int expctVld, + final String message, + boolean messMustBePresent) { + RepInfo info = parseTestFile(module, fileToTest); + testResult(info, expctWllFrmd, expctVld, message, messMustBePresent); + return info; + } + + private static void testResult( + final RepInfo info, + final int expctWllFrmd, + final int expctVld, + final String message, + boolean messMustBePresent) { + testWellFormed(info, expctWllFrmd); + testIsValid(info, expctVld); + if (message == null) { + return; + } + Message jhoveMessage = getMessageIfPresent(info, message); + if (messMustBePresent) { + if (jhoveMessage == null) { + System.out.println(String.format("Expected message: %s, not found.", message)); + outputMessages(info); + } + assertTrue("Expected message: " + message, jhoveMessage != null); + } else { + if (jhoveMessage != null) { + System.out.println( + String.format("Unexpected message: %s, found.", jhoveMessage.getMessage())); + outputMessages(info); + } + assertTrue("Unexpected message: " + message, jhoveMessage == null); + } + } + + private static void outputMessages(final RepInfo info) { + System.out.println("Messages in Report Info:"); + for (Message mess : info.getMessage()) { + System.out.println(String.format(" - %s", mess.getMessage())); + } + } + + private static RepInfo parseTestFile(final Module module, final File toTest) { + if (module.isRandomAccess()) { + return rafModuleTest(module, toTest); + } + return streamModuleTest(module, toTest); + } + + private static RepInfo streamModuleTest(final Module fisModule, final File toTest) { + RepInfo info = new RepInfo(toTest.getName()); + try (InputStream fis = new FileInputStream(toTest)) { + int index = fisModule.parse(fis, info, 0); + while (index > 0) { + index = fisModule.parse(fis, info, 0); + } + } catch (FileNotFoundException excep) { + excep.printStackTrace(); + fail("Couldn't find file to test: " + toTest.getName()); + } catch (IOException excep) { + excep.printStackTrace(); + fail("IOException Reading: " + toTest.getName()); + } + return info; + } + + private static RepInfo rafModuleTest(final Module rafModule, final File toTest) { + RepInfo info = new RepInfo(toTest.getName()); + try (RandomAccessFile raf = new RandomAccessFile(toTest, "r")) { + rafModule.parse(raf, info); + } catch (FileNotFoundException excep) { + excep.printStackTrace(); + fail("Couldn't find file to test: " + toTest.getName()); + } catch (IOException excep) { + excep.printStackTrace(); + fail("IOException Reading: " + toTest.getName()); + } + return info; + } + + private static void testWellFormed(final RepInfo info, final int expctWllFrmd) { + String message = + (expctWllFrmd == RepInfo.TRUE) ? "Should be well formed." : "Should NOT be well formed."; + assertEquals(message, expctWllFrmd, info.getWellFormed()); + } + + private static void testIsValid(final RepInfo info, final int expctVld) { + String message = (expctVld == RepInfo.TRUE) ? "Should be valid." : "Should NOT be valid."; + assertEquals(message, expctVld, info.getValid()); + } + + private static Message getMessageIfPresent(final RepInfo info, final String expctMessage) { + for (Message message : info.getMessage()) { + if (message.getMessage().equals(expctMessage)) { + return message; + } + } + return null; + } } diff --git a/jhove-modules/pdf-hul/src/test/java/edu/harvard/hul/ois/jhove/module/pdf/DocCatTests.java b/jhove-modules/pdf-hul/src/test/java/edu/harvard/hul/ois/jhove/module/pdf/DocCatTests.java index b3cd1ce83..596cf4d1f 100644 --- a/jhove-modules/pdf-hul/src/test/java/edu/harvard/hul/ois/jhove/module/pdf/DocCatTests.java +++ b/jhove-modules/pdf-hul/src/test/java/edu/harvard/hul/ois/jhove/module/pdf/DocCatTests.java @@ -1,139 +1,141 @@ package edu.harvard.hul.ois.jhove.module.pdf; -import java.net.URISyntaxException; - -import org.junit.Before; -import org.junit.Test; - import edu.harvard.hul.ois.jhove.JhoveBase; import edu.harvard.hul.ois.jhove.RepInfo; import edu.harvard.hul.ois.jhove.module.PdfModule; import edu.harvard.hul.ois.jhove.module.TestUtils; +import java.net.URISyntaxException; +import org.junit.Before; +import org.junit.Test; /** - * @author Carl Wilson - * carlwilson AT github + * @author Carl Wilson carlwilson AT github * @version 0.1 Created 13 Mar 2018:18:17:45 */ public class DocCatTests { - private static final String pdfResourcePath = "/edu/harvard/hul/ois/jhove/module/pdf/"; - private static final String docCatResourcePath = pdfResourcePath + "doc-cat/"; - - private static final String minimalPdfPath = pdfResourcePath - + "T00_000_minimal-valid.pdf"; - private static final String oneByteMissingPath = pdfResourcePath - + "corruptionOneByteMissing.pdf"; - - private static final String catNoCat = docCatResourcePath - + "T02-01_001_document-catalog-No-document-catalog.pdf"; - private static final String catWrongObjNumberPath = docCatResourcePath - + "T02-01_002_document-catalog-wrong-object-number.pdf"; - private static final String catPagRefMissPath = docCatResourcePath - + "T02-01_003_document-catalog-indirecte-pages-reference-missing.pdf"; - private static final String catPageRefIncorrectPath = docCatResourcePath - + "T02-01_004_document-catalog-incorrect-pages-reference.pdf"; - private static final String catTypeKyMissPath = docCatResourcePath - + "T02-01_005_document-catalog-type-key-missing.pdf"; - private static final String catTypeValNotCatalogPath = docCatResourcePath - + "T02-01_006_document-catalog-wrong-type-key.pdf"; - private static final String catTypeKyValPairMissPath = docCatResourcePath - + "T02-01_007_document-catalog-type-key-value-pair-missing.pdf"; - - private PdfModule module; - - @Before - public void setUp() throws Exception { - this.module = new PdfModule(); - JhoveBase je = new JhoveBase(); - this.module.setBase(je); - } - - /** - * Test method for {@link PdfModule}. - */ - @Test - public final void testValidCatType() throws URISyntaxException { - TestUtils.testValidateResource(this.module, minimalPdfPath, - RepInfo.TRUE, RepInfo.TRUE, null); - } - - /** - * Test method for {@link PdfModule}. - */ - @Test - public final void testNoCat() throws URISyntaxException { - TestUtils.testValidateResource(this.module, catNoCat, RepInfo.FALSE, - RepInfo.FALSE, MessageConstants.PDF_HUL_85.getMessage()); - } - - /** - * Test method for {@link PdfModule}. - */ - @Test - public final void testCatWrongObjNum() throws URISyntaxException { - TestUtils.testValidateResource(this.module, catWrongObjNumberPath, - RepInfo.FALSE, RepInfo.FALSE, - MessageConstants.PDF_HUL_140.getMessage()); - } - - /** - * Test method for {@link PdfModule}. - */ - @Test - public final void testPagRefMiss() throws URISyntaxException { - TestUtils.testValidateResource(this.module, catPagRefMissPath, - RepInfo.FALSE, RepInfo.FALSE, - MessageConstants.PDF_HUL_35.getMessage()); - } - - /** - * Test method for {@link PdfModule}. - */ - @Test - public final void testPageRefIncorrect() throws URISyntaxException { - TestUtils.testValidateResource(this.module, catPageRefIncorrectPath, - RepInfo.FALSE, RepInfo.FALSE, - MessageConstants.PDF_HUL_95.getMessage()); - } - - /** - * Test method for {@link PdfModule}. - */ - @Test - public final void testCatTypeKeyMiss() throws URISyntaxException { - TestUtils.testValidateResource(this.module, catTypeKyMissPath, - RepInfo.FALSE, RepInfo.FALSE, - MessageConstants.PDF_HUL_85.getMessage()); - } - - /** - * Test method for {@link PdfModule}. - */ - @Test - public final void testCatTypeVal() throws URISyntaxException { - TestUtils.testValidateResource(this.module, catTypeValNotCatalogPath, - RepInfo.FALSE, RepInfo.FALSE, - MessageConstants.PDF_HUL_141.getMessage()); - } - - /** - * Test method for {@link PdfModule}. - */ - @Test - public final void testCatTypeKeyValMiss() throws URISyntaxException { - TestUtils.testValidateResource(this.module, catTypeKyValPairMissPath, - RepInfo.FALSE, RepInfo.FALSE, - MessageConstants.PDF_HUL_36.getMessage()); - } - - /** - * Test method for {@link PdfModule}. - */ - @Test - public final void testOneByteMiss() throws URISyntaxException { - TestUtils.testValidateResource(this.module, oneByteMissingPath, - RepInfo.FALSE, RepInfo.FALSE, - MessageConstants.PDF_HUL_140.getMessage()); - } + private static final String pdfResourcePath = "/edu/harvard/hul/ois/jhove/module/pdf/"; + private static final String docCatResourcePath = pdfResourcePath + "doc-cat/"; + + private static final String minimalPdfPath = pdfResourcePath + "T00_000_minimal-valid.pdf"; + private static final String oneByteMissingPath = pdfResourcePath + "corruptionOneByteMissing.pdf"; + + private static final String catNoCat = + docCatResourcePath + "T02-01_001_document-catalog-No-document-catalog.pdf"; + private static final String catWrongObjNumberPath = + docCatResourcePath + "T02-01_002_document-catalog-wrong-object-number.pdf"; + private static final String catPagRefMissPath = + docCatResourcePath + "T02-01_003_document-catalog-indirecte-pages-reference-missing.pdf"; + private static final String catPageRefIncorrectPath = + docCatResourcePath + "T02-01_004_document-catalog-incorrect-pages-reference.pdf"; + private static final String catTypeKyMissPath = + docCatResourcePath + "T02-01_005_document-catalog-type-key-missing.pdf"; + private static final String catTypeValNotCatalogPath = + docCatResourcePath + "T02-01_006_document-catalog-wrong-type-key.pdf"; + private static final String catTypeKyValPairMissPath = + docCatResourcePath + "T02-01_007_document-catalog-type-key-value-pair-missing.pdf"; + + private PdfModule module; + + @Before + public void setUp() throws Exception { + this.module = new PdfModule(); + JhoveBase je = new JhoveBase(); + this.module.setBase(je); + } + + /** Test method for {@link PdfModule}. */ + @Test + public final void testValidCatType() throws URISyntaxException { + TestUtils.testValidateResource(this.module, minimalPdfPath, RepInfo.TRUE, RepInfo.TRUE, null); + } + + /** Test method for {@link PdfModule}. */ + @Test + public final void testNoCat() throws URISyntaxException { + TestUtils.testValidateResource( + this.module, + catNoCat, + RepInfo.FALSE, + RepInfo.FALSE, + MessageConstants.PDF_HUL_85.getMessage()); + } + + /** Test method for {@link PdfModule}. */ + @Test + public final void testCatWrongObjNum() throws URISyntaxException { + TestUtils.testValidateResource( + this.module, + catWrongObjNumberPath, + RepInfo.FALSE, + RepInfo.FALSE, + MessageConstants.PDF_HUL_140.getMessage()); + } + + /** Test method for {@link PdfModule}. */ + @Test + public final void testPagRefMiss() throws URISyntaxException { + TestUtils.testValidateResource( + this.module, + catPagRefMissPath, + RepInfo.FALSE, + RepInfo.FALSE, + MessageConstants.PDF_HUL_35.getMessage()); + } + + /** Test method for {@link PdfModule}. */ + @Test + public final void testPageRefIncorrect() throws URISyntaxException { + TestUtils.testValidateResource( + this.module, + catPageRefIncorrectPath, + RepInfo.FALSE, + RepInfo.FALSE, + MessageConstants.PDF_HUL_95.getMessage()); + } + + /** Test method for {@link PdfModule}. */ + @Test + public final void testCatTypeKeyMiss() throws URISyntaxException { + TestUtils.testValidateResource( + this.module, + catTypeKyMissPath, + RepInfo.FALSE, + RepInfo.FALSE, + MessageConstants.PDF_HUL_85.getMessage()); + } + + /** Test method for {@link PdfModule}. */ + @Test + public final void testCatTypeVal() throws URISyntaxException { + TestUtils.testValidateResource( + this.module, + catTypeValNotCatalogPath, + RepInfo.FALSE, + RepInfo.FALSE, + MessageConstants.PDF_HUL_141.getMessage()); + } + + /** Test method for {@link PdfModule}. */ + @Test + public final void testCatTypeKeyValMiss() throws URISyntaxException { + TestUtils.testValidateResource( + this.module, + catTypeKyValPairMissPath, + RepInfo.FALSE, + RepInfo.FALSE, + MessageConstants.PDF_HUL_36.getMessage()); + } + + /** Test method for {@link PdfModule}. */ + @Test + public final void testOneByteMiss() throws URISyntaxException { + TestUtils.testValidateResource( + this.module, + oneByteMissingPath, + RepInfo.FALSE, + RepInfo.FALSE, + MessageConstants.PDF_HUL_140.getMessage()); + } } diff --git a/jhove-modules/pdf-hul/src/test/java/edu/harvard/hul/ois/jhove/module/pdf/HeaderTests.java b/jhove-modules/pdf-hul/src/test/java/edu/harvard/hul/ois/jhove/module/pdf/HeaderTests.java index 8c33f6f1d..3d23e3a0f 100644 --- a/jhove-modules/pdf-hul/src/test/java/edu/harvard/hul/ois/jhove/module/pdf/HeaderTests.java +++ b/jhove-modules/pdf-hul/src/test/java/edu/harvard/hul/ois/jhove/module/pdf/HeaderTests.java @@ -1,140 +1,148 @@ package edu.harvard.hul.ois.jhove.module.pdf; -import java.net.URISyntaxException; - -import org.junit.Before; -import org.junit.Test; - import edu.harvard.hul.ois.jhove.JhoveBase; import edu.harvard.hul.ois.jhove.RepInfo; import edu.harvard.hul.ois.jhove.module.PdfModule; import edu.harvard.hul.ois.jhove.module.TestUtils; +import java.net.URISyntaxException; +import org.junit.Before; +import org.junit.Test; /** - * Tests for the {@link PdfHeader} class and for JHOVE's PDF Header validation - * capabilities. + * Tests for the {@link PdfHeader} class and for JHOVE's PDF Header validation capabilities. * - * @author Carl Wilson - * carlwilson AT github + * @author Carl Wilson carlwilson AT github * @version 0.1 Created 13 Mar 2018:11:28:10 */ public class HeaderTests { - private static final String pdfResourcePath = "/edu/harvard/hul/ois/jhove/module/pdf/"; - private static final String headerResourcePath = pdfResourcePath + "header/"; - private static final String minimalPdfPath = pdfResourcePath - + "T00_000_minimal-valid.pdf"; - - private static final String invalidMajorPath = headerResourcePath - + "T01_001_header-invalid-major-version.pdf"; - private static final String invalidMinorPath = headerResourcePath - + "T01_002_header-invalid-minor-version.pdf"; - private static final String noMinorPath = headerResourcePath - + "T01_003_header-no-minor-version.pdf"; - private static final String noHeaderDashPath = headerResourcePath - + "T01_004_header_invalid-syntax-no-dash.pdf"; - private static final String invalidSyntaxRepPath = headerResourcePath - + "T01_005_header-invalid-syntax-replace-char.pdf"; - private static final String invalidSyntaxNoPdfPath = headerResourcePath - + "T01_006_header-invalid-syntax-no-pdf.pdf"; - private static final String noVersionInfoPath = headerResourcePath - + "T01_007_header-no-version-info.pdf"; - - private PdfModule module; - - @Before - public void setUp() throws Exception { - this.module = new PdfModule(); - JhoveBase je = new JhoveBase(); - this.module.setBase(je); - } - - /** - * Test method for {@link PdfHeader}. - * Ensures that a valid PDF passes. - */ - @Test - public final void testMinorVersion() throws URISyntaxException { - TestUtils.testValidateResource(this.module, minimalPdfPath, - RepInfo.TRUE, RepInfo.TRUE); - } - - /** - * Test method for {@link PdfModule}. - * Ensures that a file with a major version > 1 (%PDF-2.4) is not well-formed. - * TODO: This currently makes all PDF 2.0 files invalid. - */ - @Test - public final void testInvalidMajorVersion() throws URISyntaxException { - TestUtils.testValidateResource(this.module, invalidMajorPath, - RepInfo.FALSE, RepInfo.FALSE, - MessageConstants.PDF_HUL_137.getMessage()); - } - - /** - * Test method for {@link PdfHeader}. - * Ensures that a file with a minor version > 7 (%PDF-1.9) is invalid. - */ - @Test - public final void testInvalidMinorVersion() throws URISyntaxException { - TestUtils.testValidateResource(this.module, invalidMinorPath, - RepInfo.TRUE, RepInfo.FALSE, - MessageConstants.PDF_HUL_148.getMessage()); - } - - /** - * Test method for {@link PdfHeader#getVersionString()}. - * Ensures that a file missing a minor version (%PDF-14) is not well-formed. - */ - @Test - public final void testNoMinor() throws URISyntaxException { - TestUtils.testValidateResource(this.module, noMinorPath, - RepInfo.FALSE, RepInfo.FALSE, - MessageConstants.PDF_HUL_137.getMessage()); - } - - /** - * Test method for {@link PdfModule}. - * Ensures that a file with no dash in header (%PDF1.4) is invalid. - */ - @Test - public final void testNoheaderDash() throws URISyntaxException { - TestUtils.testValidateResource(this.module, noHeaderDashPath, - RepInfo.FALSE, RepInfo.FALSE, - MessageConstants.PDF_HUL_137.getMessage()); - } - - /** - * Test method for {@link PdfModule}. - * Ensures that a file with no dash replaced by period in - * header (%PDF.1.4) is not well-formed. - */ - @Test - public final void testInvalidSyntaxRep() throws URISyntaxException { - TestUtils.testValidateResource(this.module, invalidSyntaxRepPath, - RepInfo.FALSE, RepInfo.FALSE, - MessageConstants.PDF_HUL_137.getMessage()); - } - - /** - * Test method for {@link PdfModule}. - * Ensures that a file with no PDF in header (%1.4) is not well-formed. - */ - @Test - public final void testInvalidSyntaxNoPdf() throws URISyntaxException { - TestUtils.testValidateResource(this.module, invalidSyntaxNoPdfPath, - RepInfo.FALSE, RepInfo.FALSE, - MessageConstants.PDF_HUL_137.getMessage()); - } - - /** - * Test method for {@link PdfModule}. - * Ensures that a file missing a header is not well-formed. - */ - @Test - public final void testNoVersionInfo() throws URISyntaxException { - TestUtils.testValidateResource(this.module, noVersionInfoPath, - RepInfo.FALSE, RepInfo.FALSE, - MessageConstants.PDF_HUL_137.getMessage()); - } + private static final String pdfResourcePath = "/edu/harvard/hul/ois/jhove/module/pdf/"; + private static final String headerResourcePath = pdfResourcePath + "header/"; + private static final String minimalPdfPath = pdfResourcePath + "T00_000_minimal-valid.pdf"; + + private static final String invalidMajorPath = + headerResourcePath + "T01_001_header-invalid-major-version.pdf"; + private static final String invalidMinorPath = + headerResourcePath + "T01_002_header-invalid-minor-version.pdf"; + private static final String noMinorPath = + headerResourcePath + "T01_003_header-no-minor-version.pdf"; + private static final String noHeaderDashPath = + headerResourcePath + "T01_004_header_invalid-syntax-no-dash.pdf"; + private static final String invalidSyntaxRepPath = + headerResourcePath + "T01_005_header-invalid-syntax-replace-char.pdf"; + private static final String invalidSyntaxNoPdfPath = + headerResourcePath + "T01_006_header-invalid-syntax-no-pdf.pdf"; + private static final String noVersionInfoPath = + headerResourcePath + "T01_007_header-no-version-info.pdf"; + + private PdfModule module; + + @Before + public void setUp() throws Exception { + this.module = new PdfModule(); + JhoveBase je = new JhoveBase(); + this.module.setBase(je); + } + + /** Test method for {@link PdfHeader}. Ensures that a valid PDF passes. */ + @Test + public final void testMinorVersion() throws URISyntaxException { + TestUtils.testValidateResource(this.module, minimalPdfPath, RepInfo.TRUE, RepInfo.TRUE); + } + + /** + * Test method for {@link PdfModule}. Ensures that a file with a major version > 1 (%PDF-2.4) + * is not well-formed. TODO: This currently makes all PDF 2.0 files invalid. + */ + @Test + public final void testInvalidMajorVersion() throws URISyntaxException { + TestUtils.testValidateResource( + this.module, + invalidMajorPath, + RepInfo.FALSE, + RepInfo.FALSE, + MessageConstants.PDF_HUL_137.getMessage()); + } + + /** + * Test method for {@link PdfHeader}. Ensures that a file with a minor version > 7 (%PDF-1.9) + * is invalid. + */ + @Test + public final void testInvalidMinorVersion() throws URISyntaxException { + TestUtils.testValidateResource( + this.module, + invalidMinorPath, + RepInfo.TRUE, + RepInfo.FALSE, + MessageConstants.PDF_HUL_148.getMessage()); + } + + /** + * Test method for {@link PdfHeader#getVersionString()}. Ensures that a file missing a minor + * version (%PDF-14) is not well-formed. + */ + @Test + public final void testNoMinor() throws URISyntaxException { + TestUtils.testValidateResource( + this.module, + noMinorPath, + RepInfo.FALSE, + RepInfo.FALSE, + MessageConstants.PDF_HUL_137.getMessage()); + } + + /** + * Test method for {@link PdfModule}. Ensures that a file with no dash in header (%PDF1.4) is + * invalid. + */ + @Test + public final void testNoheaderDash() throws URISyntaxException { + TestUtils.testValidateResource( + this.module, + noHeaderDashPath, + RepInfo.FALSE, + RepInfo.FALSE, + MessageConstants.PDF_HUL_137.getMessage()); + } + + /** + * Test method for {@link PdfModule}. Ensures that a file with no dash replaced by period in + * header (%PDF.1.4) is not well-formed. + */ + @Test + public final void testInvalidSyntaxRep() throws URISyntaxException { + TestUtils.testValidateResource( + this.module, + invalidSyntaxRepPath, + RepInfo.FALSE, + RepInfo.FALSE, + MessageConstants.PDF_HUL_137.getMessage()); + } + + /** + * Test method for {@link PdfModule}. Ensures that a file with no PDF in header (%1.4) is not + * well-formed. + */ + @Test + public final void testInvalidSyntaxNoPdf() throws URISyntaxException { + TestUtils.testValidateResource( + this.module, + invalidSyntaxNoPdfPath, + RepInfo.FALSE, + RepInfo.FALSE, + MessageConstants.PDF_HUL_137.getMessage()); + } + + /** Test method for {@link PdfModule}. Ensures that a file missing a header is not well-formed. */ + @Test + public final void testNoVersionInfo() throws URISyntaxException { + TestUtils.testValidateResource( + this.module, + noVersionInfoPath, + RepInfo.FALSE, + RepInfo.FALSE, + MessageConstants.PDF_HUL_137.getMessage()); + } } diff --git a/jhove-modules/pdf-hul/src/test/java/edu/harvard/hul/ois/jhove/module/pdf/PageTreeTests.java b/jhove-modules/pdf-hul/src/test/java/edu/harvard/hul/ois/jhove/module/pdf/PageTreeTests.java index 759245e4b..979150501 100644 --- a/jhove-modules/pdf-hul/src/test/java/edu/harvard/hul/ois/jhove/module/pdf/PageTreeTests.java +++ b/jhove-modules/pdf-hul/src/test/java/edu/harvard/hul/ois/jhove/module/pdf/PageTreeTests.java @@ -1,153 +1,151 @@ package edu.harvard.hul.ois.jhove.module.pdf; -import java.net.URISyntaxException; - -import org.junit.Before; -import org.junit.Test; - import edu.harvard.hul.ois.jhove.JhoveBase; import edu.harvard.hul.ois.jhove.RepInfo; import edu.harvard.hul.ois.jhove.module.PdfModule; import edu.harvard.hul.ois.jhove.module.TestUtils; +import java.net.URISyntaxException; +import org.junit.Before; +import org.junit.Test; /** - * @author Carl Wilson - * carlwilson AT github - * + * @author Carl Wilson carlwilson AT github * @version 0.1 - * - * Created 19 Mar 2018:02:03:50 + *

Created 19 Mar 2018:02:03:50 */ public class PageTreeTests { - private static final String pdfResourcePath = "/edu/harvard/hul/ois/jhove/module/pdf/"; - private static final String pageTreeResourcePath = pdfResourcePath + "page-tree/"; - - private static final String minimalPdfPath = pdfResourcePath - + "T00_000_minimal-valid.pdf"; - - private static final String noPageTreeNodePath = pageTreeResourcePath - + "T02-02_001_no-page-tree-node.pdf"; - private static final String rcrsPageTreeKidsPath = pageTreeResourcePath - + "T02-02_002_page-tree-kids-links-recursive.pdf"; - private static final String diffPageTreeKidsPath = pageTreeResourcePath - + "T02-02_003_page-tree-different-kids.pdf"; - private static final String ntExstPageTreeChldPath = pageTreeResourcePath - + "T02-02_004_page-tree-non-existing-object-as-kid.pdf"; - private static final String noPageTreeKidsPath = pageTreeResourcePath - + "T02-02_005_page-tree-no-kids.pdf"; - private static final String noTypePageTreePath = pageTreeResourcePath - + "T02-02_006_page-tree-no-type.pdf"; - private static final String wrngPageTreeCountPath = pageTreeResourcePath - + "T02-02_007_page-tree-wrong-count.pdf"; - private static final String noPageTreeCountPath = pageTreeResourcePath - + "T02-02_008_page-tree-node-no-count.pdf"; - private static final String wrngPageTreeTypePath = pageTreeResourcePath - + "T02-02_009_page-tree-wrong-type.pdf"; - private PdfModule module; - - @Before - public void setUp() throws Exception { - this.module = new PdfModule(); - JhoveBase je = new JhoveBase(); - this.module.setBase(je); - } - - /** - * Test method for {@link PdfModule#getCatalogDict()}. - */ - @Test - public final void testValidCatType() throws URISyntaxException { - TestUtils.testValidateResource(this.module, minimalPdfPath, - RepInfo.TRUE, RepInfo.TRUE, null); - } - - /** - * Test method for {@link PdfModule#getCatalogDict()}. - */ - @Test - public final void testNoPageTreeNode() throws URISyntaxException { - TestUtils.testValidateResource(this.module, noPageTreeNodePath, - RepInfo.FALSE, RepInfo.FALSE, - MessageConstants.PDF_HUL_37.getMessage()); - } - - /** - * Test method for {@link PdfModule#getCatalogDict()}. - */ - @Test - public final void testRcrsPageTreeKids() throws URISyntaxException { - TestUtils.testValidateResource(this.module, rcrsPageTreeKidsPath, - RepInfo.FALSE, RepInfo.FALSE, - MessageConstants.PDF_HUL_32.getMessage()); - } - - /** - * Test method for {@link PdfModule#getCatalogDict()}. - */ - @Test - public final void testDiffPageTreeKids() throws URISyntaxException { - TestUtils.testValidateResource(this.module, diffPageTreeKidsPath, - RepInfo.FALSE, RepInfo.FALSE, - MessageConstants.PDF_HUL_33.getMessage()); - } - - /** - * Test method for {@link PdfModule#getCatalogDict()}. - */ - @Test - public final void testNtExstPageTreeChldPath() throws URISyntaxException { - TestUtils.testValidateResource(this.module, ntExstPageTreeChldPath, - RepInfo.TRUE, RepInfo.FALSE, - MessageConstants.PDF_HUL_147.getMessage()); - } - - /** - * Test method for {@link PdfModule#getCatalogDict()}. - */ - @Test - public final void testNoPageTreeKids() throws URISyntaxException { - TestUtils.testValidateResource(this.module, noPageTreeKidsPath, - RepInfo.FALSE, RepInfo.FALSE, - MessageConstants.PDF_HUL_37.getMessage()); - } - - /** - * Test method for {@link PdfModule#getCatalogDict()}. - */ - @Test - public final void testNoTypePageTree() throws URISyntaxException { - TestUtils.testValidateResource(this.module, noTypePageTreePath, - RepInfo.FALSE, RepInfo.FALSE, - MessageConstants.PDF_HUL_38.getMessage()); - } - - /** - * Test method for {@link PdfModule#getCatalogDict()}. - */ - @Test - public final void testWrngPageTreeCount() throws URISyntaxException { - TestUtils.testValidateResource(this.module, wrngPageTreeCountPath, - RepInfo.TRUE, RepInfo.TRUE, null); - } - - /** - * Test method for {@link PdfModule#getCatalogDict()}. - */ - @Test - public final void testNoPageTreeCount() throws URISyntaxException { - TestUtils.testValidateResource(this.module, noPageTreeCountPath, - RepInfo.FALSE, RepInfo.FALSE, - MessageConstants.PDF_HUL_37.getMessage()); - } - - /** - * Test method for {@link PdfModule#getCatalogDict()}. - */ - @Test - public final void testWrngPageTreeType() throws URISyntaxException { - TestUtils.testValidateResource(this.module, wrngPageTreeTypePath, - RepInfo.FALSE, RepInfo.FALSE, - MessageConstants.PDF_HUL_146.getMessage()); - } + private static final String pdfResourcePath = "/edu/harvard/hul/ois/jhove/module/pdf/"; + private static final String pageTreeResourcePath = pdfResourcePath + "page-tree/"; + + private static final String minimalPdfPath = pdfResourcePath + "T00_000_minimal-valid.pdf"; + + private static final String noPageTreeNodePath = + pageTreeResourcePath + "T02-02_001_no-page-tree-node.pdf"; + private static final String rcrsPageTreeKidsPath = + pageTreeResourcePath + "T02-02_002_page-tree-kids-links-recursive.pdf"; + private static final String diffPageTreeKidsPath = + pageTreeResourcePath + "T02-02_003_page-tree-different-kids.pdf"; + private static final String ntExstPageTreeChldPath = + pageTreeResourcePath + "T02-02_004_page-tree-non-existing-object-as-kid.pdf"; + private static final String noPageTreeKidsPath = + pageTreeResourcePath + "T02-02_005_page-tree-no-kids.pdf"; + private static final String noTypePageTreePath = + pageTreeResourcePath + "T02-02_006_page-tree-no-type.pdf"; + private static final String wrngPageTreeCountPath = + pageTreeResourcePath + "T02-02_007_page-tree-wrong-count.pdf"; + private static final String noPageTreeCountPath = + pageTreeResourcePath + "T02-02_008_page-tree-node-no-count.pdf"; + private static final String wrngPageTreeTypePath = + pageTreeResourcePath + "T02-02_009_page-tree-wrong-type.pdf"; + private PdfModule module; + + @Before + public void setUp() throws Exception { + this.module = new PdfModule(); + JhoveBase je = new JhoveBase(); + this.module.setBase(je); + } + + /** Test method for {@link PdfModule#getCatalogDict()}. */ + @Test + public final void testValidCatType() throws URISyntaxException { + TestUtils.testValidateResource(this.module, minimalPdfPath, RepInfo.TRUE, RepInfo.TRUE, null); + } + + /** Test method for {@link PdfModule#getCatalogDict()}. */ + @Test + public final void testNoPageTreeNode() throws URISyntaxException { + TestUtils.testValidateResource( + this.module, + noPageTreeNodePath, + RepInfo.FALSE, + RepInfo.FALSE, + MessageConstants.PDF_HUL_37.getMessage()); + } + + /** Test method for {@link PdfModule#getCatalogDict()}. */ + @Test + public final void testRcrsPageTreeKids() throws URISyntaxException { + TestUtils.testValidateResource( + this.module, + rcrsPageTreeKidsPath, + RepInfo.FALSE, + RepInfo.FALSE, + MessageConstants.PDF_HUL_32.getMessage()); + } + + /** Test method for {@link PdfModule#getCatalogDict()}. */ + @Test + public final void testDiffPageTreeKids() throws URISyntaxException { + TestUtils.testValidateResource( + this.module, + diffPageTreeKidsPath, + RepInfo.FALSE, + RepInfo.FALSE, + MessageConstants.PDF_HUL_33.getMessage()); + } + + /** Test method for {@link PdfModule#getCatalogDict()}. */ + @Test + public final void testNtExstPageTreeChldPath() throws URISyntaxException { + TestUtils.testValidateResource( + this.module, + ntExstPageTreeChldPath, + RepInfo.TRUE, + RepInfo.FALSE, + MessageConstants.PDF_HUL_147.getMessage()); + } + + /** Test method for {@link PdfModule#getCatalogDict()}. */ + @Test + public final void testNoPageTreeKids() throws URISyntaxException { + TestUtils.testValidateResource( + this.module, + noPageTreeKidsPath, + RepInfo.FALSE, + RepInfo.FALSE, + MessageConstants.PDF_HUL_37.getMessage()); + } + + /** Test method for {@link PdfModule#getCatalogDict()}. */ + @Test + public final void testNoTypePageTree() throws URISyntaxException { + TestUtils.testValidateResource( + this.module, + noTypePageTreePath, + RepInfo.FALSE, + RepInfo.FALSE, + MessageConstants.PDF_HUL_38.getMessage()); + } + + /** Test method for {@link PdfModule#getCatalogDict()}. */ + @Test + public final void testWrngPageTreeCount() throws URISyntaxException { + TestUtils.testValidateResource( + this.module, wrngPageTreeCountPath, RepInfo.TRUE, RepInfo.TRUE, null); + } + + /** Test method for {@link PdfModule#getCatalogDict()}. */ + @Test + public final void testNoPageTreeCount() throws URISyntaxException { + TestUtils.testValidateResource( + this.module, + noPageTreeCountPath, + RepInfo.FALSE, + RepInfo.FALSE, + MessageConstants.PDF_HUL_37.getMessage()); + } + + /** Test method for {@link PdfModule#getCatalogDict()}. */ + @Test + public final void testWrngPageTreeType() throws URISyntaxException { + TestUtils.testValidateResource( + this.module, + wrngPageTreeTypePath, + RepInfo.FALSE, + RepInfo.FALSE, + MessageConstants.PDF_HUL_146.getMessage()); + } } diff --git a/jhove-modules/pdf-hul/src/test/java/edu/harvard/hul/ois/jhove/module/pdf/TestUtils.java b/jhove-modules/pdf-hul/src/test/java/edu/harvard/hul/ois/jhove/module/pdf/TestUtils.java index 622659415..19c3fb2d2 100644 --- a/jhove-modules/pdf-hul/src/test/java/edu/harvard/hul/ois/jhove/module/pdf/TestUtils.java +++ b/jhove-modules/pdf-hul/src/test/java/edu/harvard/hul/ois/jhove/module/pdf/TestUtils.java @@ -4,77 +4,75 @@ import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; +import edu.harvard.hul.ois.jhove.Message; +import edu.harvard.hul.ois.jhove.RepInfo; +import edu.harvard.hul.ois.jhove.module.PdfModule; import java.io.File; import java.io.FileNotFoundException; import java.io.IOException; import java.io.RandomAccessFile; import java.net.URISyntaxException; -import edu.harvard.hul.ois.jhove.Message; -import edu.harvard.hul.ois.jhove.RepInfo; -import edu.harvard.hul.ois.jhove.module.PdfModule; - /** - * @author Carl Wilson - * carlwilson AT github + * @author Carl Wilson carlwilson AT github * @version 0.1 Created 14 Mar 2018:20:14:19 */ - public final class TestUtils { - private TestUtils() { - // Keep out - throw new AssertionError("Should never be in constructor."); - } - - public static void testValidateFile(final PdfModule pdfModule, - final String pathToTest, final int expctWllFrmd, final int expctVld, - final String expctMessage) throws URISyntaxException { - File toTest = new File( - TestUtils.class.getResource(pathToTest).toURI()); + private TestUtils() { + // Keep out + throw new AssertionError("Should never be in constructor."); + } - RepInfo info = parseTestFile(pdfModule, toTest); - testResult(info, expctWllFrmd, expctVld, expctMessage); - } + public static void testValidateFile( + final PdfModule pdfModule, + final String pathToTest, + final int expctWllFrmd, + final int expctVld, + final String expctMessage) + throws URISyntaxException { + File toTest = new File(TestUtils.class.getResource(pathToTest).toURI()); - private static void testResult(final RepInfo info, final int expctWllFrmd, - final int expctVld, final String expctMessage) { - String message = (expctWllFrmd == RepInfo.TRUE) - ? "Should be well formed." - : "Should NOT be well formed."; - assertEquals(message, expctWllFrmd, info.getWellFormed()); - message = (expctVld == RepInfo.TRUE) ? "Should be valid." - : "Should NOT be valid."; - assertEquals(message, expctVld, info.getValid()); - boolean messagePresent = false; - if (expctMessage != null) { - for (Message mess : info.getMessage()) { - if (mess.getMessage().equals(expctMessage)) { - messagePresent = true; - } - } - if (!messagePresent) { - System.out.println("Expected message not found, messages in RepInfo:"); - for (Message mess: info.getMessage()) { - System.out.println(" - message:" + mess.getMessage()); - } - } - assertTrue("Expected message: " + expctMessage, messagePresent); - } - } + RepInfo info = parseTestFile(pdfModule, toTest); + testResult(info, expctWllFrmd, expctVld, expctMessage); + } - private static RepInfo parseTestFile(final PdfModule pdfModule, final File toTest) { - RepInfo info = new RepInfo(toTest.getName()); - try (RandomAccessFile raf = new RandomAccessFile(toTest, "r")){ - pdfModule.parse(raf, info); - } catch (FileNotFoundException excep) { - excep.printStackTrace(); - fail("Couldn't find file to test: " + toTest.getName()); - } catch (IOException excep) { - excep.printStackTrace(); - fail("IOException Reading: " + toTest.getName()); - } - return info; - } + private static void testResult( + final RepInfo info, final int expctWllFrmd, final int expctVld, final String expctMessage) { + String message = + (expctWllFrmd == RepInfo.TRUE) ? "Should be well formed." : "Should NOT be well formed."; + assertEquals(message, expctWllFrmd, info.getWellFormed()); + message = (expctVld == RepInfo.TRUE) ? "Should be valid." : "Should NOT be valid."; + assertEquals(message, expctVld, info.getValid()); + boolean messagePresent = false; + if (expctMessage != null) { + for (Message mess : info.getMessage()) { + if (mess.getMessage().equals(expctMessage)) { + messagePresent = true; + } + } + if (!messagePresent) { + System.out.println("Expected message not found, messages in RepInfo:"); + for (Message mess : info.getMessage()) { + System.out.println(" - message:" + mess.getMessage()); + } + } + assertTrue("Expected message: " + expctMessage, messagePresent); + } + } + private static RepInfo parseTestFile(final PdfModule pdfModule, final File toTest) { + RepInfo info = new RepInfo(toTest.getName()); + try (RandomAccessFile raf = new RandomAccessFile(toTest, "r")) { + pdfModule.parse(raf, info); + } catch (FileNotFoundException excep) { + excep.printStackTrace(); + fail("Couldn't find file to test: " + toTest.getName()); + } catch (IOException excep) { + excep.printStackTrace(); + fail("IOException Reading: " + toTest.getName()); + } + return info; + } } diff --git a/jhove-modules/tiff-hul/src/main/java/TDump.java b/jhove-modules/tiff-hul/src/main/java/TDump.java index 0848ceddc..9be4387c2 100644 --- a/jhove-modules/tiff-hul/src/main/java/TDump.java +++ b/jhove-modules/tiff-hul/src/main/java/TDump.java @@ -1,357 +1,344 @@ -/********************************************************************** - * TDump - JSTOR/Harvard Object Validation Environment - * Copyright 2003-2004 by JSTOR and the President and Fellows of Harvard College +/** + * ******************************************************************** TDump - JSTOR/Harvard Object + * Validation Environment Copyright 2003-2004 by JSTOR and the President and Fellows of Harvard + * College * - * This program 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 2 of the License, or (at - * your option) any later version. - * - * This program 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. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 - * USA - **********************************************************************/ - + *

This program 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 2 + * of the License, or (at your option) any later version. + * + *

This program 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. + * + *

You should have received a copy of the GNU Lesser General Public License along with this + * program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA ******************************************************************** + */ import edu.harvard.hul.ois.jhove.*; import edu.harvard.hul.ois.jhove.module.tiff.*; import java.io.*; import java.util.*; -/** - * Dump contents of TIFF file in human-readable format. - */ -public class TDump - extends Dump -{ - /****************************************************************** - * PRIVATE CLASS FIELDS. - ******************************************************************/ - - /** Count of IFDs. */ - private static int _nIFDs; - /** Sorted associative map of tags. */ - private static Map _tags; - - /****************************************************************** - * MAIN ENTRY POINT. - ******************************************************************/ - - /** - * Main entry point. - * @param args Command line arguments - */ - public static void main (String [] args) - { - if (args.length < 1) { - System.err.println ("usage: java TDump [-bs] file"); - System.exit (-1); - } - - String tiff = null; - boolean nobyte = false; - boolean nosub = false; - for (int i=0; i (); - - int err = 0; - try (/* Read TIFF header. */ - RandomAccessFile file = new RandomAccessFile (tiff, "r")) { - boolean bigEndian = true; - int b1 = ModuleBase.readUnsignedByte (file); - int b2 = ModuleBase.readUnsignedByte (file); - if (b1 == 0x49 && b2 == b1) { - bigEndian = false; - } - int magic = ModuleBase.readUnsignedShort (file, bigEndian); - long offset = ModuleBase.readUnsignedInt (file, bigEndian); - _tags.put ("00000000", "\"" + ((char) b1) + ((char) b2) + "\" (" + - (bigEndian ? "big" : "little") + " endian) " + magic + - " LONG @" + offset); - - /* Read IFDs. */ - - _nIFDs = 0; - while ((offset = readIFD (file, bigEndian, offset, nobyte, - nosub)) > 0) { - } - file.close (); - } - catch (Exception e) { - e.printStackTrace (System.out); - err = -2; - } - finally { - - /* Display all tags in offset-sorted order. */ - - Iterator iter = _tags.keySet ().iterator (); - while (iter.hasNext ()) { - String os = iter.next (); - System.out.println (os + ": " + _tags.get (os)); - } - if (err != 0) { - System.exit (err); - } - } +/** Dump contents of TIFF file in human-readable format. */ +public class TDump extends Dump { + /** + * **************************************************************** PRIVATE CLASS FIELDS. + * **************************************************************** + */ + + /** Count of IFDs. */ + private static int _nIFDs; + /** Sorted associative map of tags. */ + private static Map _tags; + + /** + * **************************************************************** MAIN ENTRY POINT. + * **************************************************************** + */ + + /** + * Main entry point. + * + * @param args Command line arguments + */ + public static void main(String[] args) { + if (args.length < 1) { + System.err.println("usage: java TDump [-bs] file"); + System.exit(-1); } - /** - * Read IFDs. - * @param file Open TIFF file - * @param bigEndian True if big-endian - * @param offset Byte offset of IFD - * @param nobyte If true, only display the first 8 bytes of data of - * type BYTE - * @param nosub If true, do not parse subIFDs - */ - private static long readIFD (RandomAccessFile file, boolean bigEndian, - long offset, boolean nobyte, boolean nosub) - throws Exception - { - int nIFD = ++_nIFDs; - List subIFDs = new ArrayList<> (); - List stripByteCounts = new ArrayList<> (); - List stripOffsets = new ArrayList<> (); - - file.seek (offset); - int nEntries = ModuleBase.readUnsignedShort (file, bigEndian); - _tags.put (leading (offset, 8) + offset, "IFD " + nIFD + " with " + - nEntries + " entries"); - - for (int i=0; i 4) { - long vo = ModuleBase.readUnsignedInt (file, bigEndian); - file.seek (vo); - - buffer.append (" @" + vo); - } - StringBuffer ascii = new StringBuffer (); - for (int j=0; j 4) { - long vo = ModuleBase.readUnsignedInt (file, bigEndian); - file.seek (vo); - - buffer.append (" @" + vo); - } - buffer.append (" ="); - long ct = count; - if (nobyte && count > 8) { - ct = 8; - } - for (int j=0; j 8) { - buffer.append (" ..."); - } - } - else if (type == IFD.DOUBLE) { - long vo = ModuleBase.readUnsignedInt (file, bigEndian); - file.seek (vo); - - buffer.append (" @" + vo + " ="); - for (int j=0; j 1) { - long vo = ModuleBase.readUnsignedInt (file, bigEndian); - file.seek (vo); - - buffer.append (" @" + vo); - } - buffer.append (" ="); - for (int j=0; j 1) { - long vo = ModuleBase.readUnsignedInt (file, bigEndian); - file.seek (vo); - - buffer.append (" @" + vo + " = "); - } - else if (tag == 330 || /* Sub IFD */ - tag == 34665 || /* EXIF IFD */ - tag == 34853 || /* EXIF GPS IFD */ - tag == 40965) { /* EXIF Interoperability IFD */ - buffer.append (" @"); - } - else { - buffer.append (" = "); - } - for (int j=0; j 0) { - buffer.append (" "); - } - buffer.append (in); - - if (tag == 330 || /* Sub IFD */ - tag == 34665 || /* EXIF IFD */ - tag == 34853 || /* EXIF GPS IFD */ - tag == 40965) { /* EXIF Interoperability IFD */ - subIFDs.add (new Long (in)); - } - else if (tag == 273) { - stripOffsets.add (new Long (in)); - } - else if (tag == 279) { - stripByteCounts.add (new Long (in)); - } - } - } - else if (type == IFD.RATIONAL) { - long vo = ModuleBase.readUnsignedInt (file, bigEndian); - file.seek (vo); - - buffer.append (" @" + vo + " ="); - for (int j=0; j 4) { - long vo = ModuleBase.readUnsignedInt (file, bigEndian); - file.seek (vo); - - buffer.append (" @" + vo); - } - buffer.append (" ="); - for (int j=0; j 2) { - long vo = ModuleBase.readUnsignedInt (file, bigEndian); - file.seek (vo); - - buffer.append (" @" + vo); - } - buffer.append (" ="); - for (int j=0; j 1) { - long vo = ModuleBase.readUnsignedInt (file, bigEndian); - file.seek (vo); - - buffer.append (" @" + vo); - } - buffer.append (" ="); - for (int j=0; j 2) { - long vo = ModuleBase.readUnsignedInt (file, bigEndian); - file.seek (vo); - - buffer.append (" @" + vo); - } - buffer.append (" ="); - for (int j=0; j 0) { - } - } - } - - long os = offset + 2 + nEntries*12; - file.seek (os); - long next = ModuleBase.readUnsignedInt (file, bigEndian); - _tags.put (leading (os, 8) + os, "NextIFDOffset LONG @" + next); - - int len = stripOffsets.size (); - if (len > 0) { - for (int j=0; j(); + + int err = 0; + try ( + /* Read TIFF header. */ + RandomAccessFile file = new RandomAccessFile(tiff, "r")) { + boolean bigEndian = true; + int b1 = ModuleBase.readUnsignedByte(file); + int b2 = ModuleBase.readUnsignedByte(file); + if (b1 == 0x49 && b2 == b1) { + bigEndian = false; + } + int magic = ModuleBase.readUnsignedShort(file, bigEndian); + long offset = ModuleBase.readUnsignedInt(file, bigEndian); + _tags.put( + "00000000", + "\"" + + ((char) b1) + + ((char) b2) + + "\" (" + + (bigEndian ? "big" : "little") + + " endian) " + + magic + + " LONG @" + + offset); + + /* Read IFDs. */ + + _nIFDs = 0; + while ((offset = readIFD(file, bigEndian, offset, nobyte, nosub)) > 0) {} + file.close(); + } catch (Exception e) { + e.printStackTrace(System.out); + err = -2; + } finally { + + /* Display all tags in offset-sorted order. */ + + Iterator iter = _tags.keySet().iterator(); + while (iter.hasNext()) { + String os = iter.next(); + System.out.println(os + ": " + _tags.get(os)); + } + if (err != 0) { + System.exit(err); + } + } + } + + /** + * Read IFDs. + * + * @param file Open TIFF file + * @param bigEndian True if big-endian + * @param offset Byte offset of IFD + * @param nobyte If true, only display the first 8 bytes of data of type BYTE + * @param nosub If true, do not parse subIFDs + */ + private static long readIFD( + RandomAccessFile file, boolean bigEndian, long offset, boolean nobyte, boolean nosub) + throws Exception { + int nIFD = ++_nIFDs; + List subIFDs = new ArrayList<>(); + List stripByteCounts = new ArrayList<>(); + List stripOffsets = new ArrayList<>(); + + file.seek(offset); + int nEntries = ModuleBase.readUnsignedShort(file, bigEndian); + _tags.put(leading(offset, 8) + offset, "IFD " + nIFD + " with " + nEntries + " entries"); + + for (int i = 0; i < nEntries; i++) { + long os = offset + 2 + i * 12; + file.seek(os); + int tag = ModuleBase.readUnsignedShort(file, bigEndian); + int type = ModuleBase.readUnsignedShort(file, bigEndian); + long count = ModuleBase.readUnsignedInt(file, bigEndian); + + StringBuffer buffer = + new StringBuffer( + tag + " (" + TiffTags.tagName(tag) + ") " + IFD.TYPE[type] + " " + count); + if (type == IFD.ASCII) { + if (count > 4) { + long vo = ModuleBase.readUnsignedInt(file, bigEndian); + file.seek(vo); + + buffer.append(" @" + vo); + } + StringBuffer ascii = new StringBuffer(); + for (int j = 0; j < count - 1; j++) { + ascii.append((char) ModuleBase.readUnsignedByte(file)); + } + buffer.append(" = \"" + ascii.toString() + "\""); + } else if (type == IFD.BYTE || type == IFD.UNDEFINED) { + if (count > 4) { + long vo = ModuleBase.readUnsignedInt(file, bigEndian); + file.seek(vo); + + buffer.append(" @" + vo); + } + buffer.append(" ="); + long ct = count; + if (nobyte && count > 8) { + ct = 8; + } + for (int j = 0; j < ct; j++) { + long by = ModuleBase.readUnsignedByte(file); + buffer.append(" " + by); + } + if (nobyte && count > 8) { + buffer.append(" ..."); + } + } else if (type == IFD.DOUBLE) { + long vo = ModuleBase.readUnsignedInt(file, bigEndian); + file.seek(vo); + + buffer.append(" @" + vo + " ="); + for (int j = 0; j < count; j++) { + double db = ModuleBase.readDouble(file, bigEndian); + buffer.append(" " + db); + } + } else if (type == IFD.FLOAT) { + if (count > 1) { + long vo = ModuleBase.readUnsignedInt(file, bigEndian); + file.seek(vo); + + buffer.append(" @" + vo); + } + buffer.append(" ="); + for (int j = 0; j < count; j++) { + float fl = ModuleBase.readFloat(file, bigEndian); + buffer.append(" " + fl); + } + } else if (type == IFD.LONG || type == IFD.IFD) { + if (count > 1) { + long vo = ModuleBase.readUnsignedInt(file, bigEndian); + file.seek(vo); + + buffer.append(" @" + vo + " = "); + } else if (tag == 330 + || /* Sub IFD */ tag == 34665 + || /* EXIF IFD */ tag == 34853 + || /* EXIF GPS IFD */ tag == 40965) { + /* EXIF Interoperability IFD */ + buffer.append(" @"); + } else { + buffer.append(" = "); + } + for (int j = 0; j < count; j++) { + long in = ModuleBase.readUnsignedInt(file, bigEndian); + if (j > 0) { + buffer.append(" "); + } + buffer.append(in); + + if (tag == 330 + || /* Sub IFD */ tag == 34665 + || /* EXIF IFD */ tag == 34853 + || /* EXIF GPS IFD */ tag == 40965) { + /* EXIF Interoperability IFD */ + subIFDs.add(new Long(in)); + } else if (tag == 273) { + stripOffsets.add(new Long(in)); + } else if (tag == 279) { + stripByteCounts.add(new Long(in)); + } + } + } else if (type == IFD.RATIONAL) { + long vo = ModuleBase.readUnsignedInt(file, bigEndian); + file.seek(vo); + + buffer.append(" @" + vo + " ="); + for (int j = 0; j < count; j++) { + long numer = ModuleBase.readUnsignedInt(file, bigEndian); + long denom = ModuleBase.readUnsignedInt(file, bigEndian); + buffer.append(" " + numer + "/" + denom); + } + } else if (type == IFD.SBYTE) { + if (count > 4) { + long vo = ModuleBase.readUnsignedInt(file, bigEndian); + file.seek(vo); + + buffer.append(" @" + vo); + } + buffer.append(" ="); + for (int j = 0; j < count; j++) { + long by = ModuleBase.readSignedByte(file); + buffer.append(" " + by); + } + } else if (type == IFD.SHORT) { + if (count > 2) { + long vo = ModuleBase.readUnsignedInt(file, bigEndian); + file.seek(vo); + + buffer.append(" @" + vo); + } + buffer.append(" ="); + for (int j = 0; j < count; j++) { + int sh = ModuleBase.readUnsignedShort(file, bigEndian); + buffer.append(" " + sh); + + if (tag == 273) { + stripOffsets.add(new Long(sh)); + } else if (tag == 279) { + stripByteCounts.add(new Long(sh)); + } + } + } else if (type == IFD.SLONG) { + if (count > 1) { + long vo = ModuleBase.readUnsignedInt(file, bigEndian); + file.seek(vo); + + buffer.append(" @" + vo); + } + buffer.append(" ="); + for (int j = 0; j < count; j++) { + int in = ModuleBase.readSignedInt(file, bigEndian); + buffer.append(" " + in); + } + } else if (type == IFD.SRATIONAL) { + long vo = ModuleBase.readUnsignedInt(file, bigEndian); + file.seek(vo); + + buffer.append(" @" + vo + " ="); + for (int j = 0; j < count; j++) { + long numer = ModuleBase.readSignedInt(file, bigEndian); + long denom = ModuleBase.readSignedInt(file, bigEndian); + buffer.append(" " + numer + "/" + denom); + } + } else if (type == IFD.SSHORT) { + if (count > 2) { + long vo = ModuleBase.readUnsignedInt(file, bigEndian); + file.seek(vo); + + buffer.append(" @" + vo); + } + buffer.append(" ="); + for (int j = 0; j < count; j++) { + int sh = ModuleBase.readSignedShort(file, bigEndian); + buffer.append(" " + sh); + } + } + _tags.put(leading(os, 8) + os, buffer.toString()); + } + + if (!nosub) { + for (int i = 0; i < subIFDs.size(); i++) { + long os = subIFDs.get(i).longValue(); + while ((os = readIFD(file, bigEndian, os, nobyte, nosub)) > 0) {} + } + } + + long os = offset + 2 + nEntries * 12; + file.seek(os); + long next = ModuleBase.readUnsignedInt(file, bigEndian); + _tags.put(leading(os, 8) + os, "NextIFDOffset LONG @" + next); + + int len = stripOffsets.size(); + if (len > 0) { + for (int j = 0; j < len; j++) { + long start = stripOffsets.get(j).longValue(); + long count = stripByteCounts.get(j).longValue(); + long finish = start + count - 1; + _tags.put( + leading(start, 8) + start, + "(Image " + + nIFD + + ",strip " + + (j + 1) + + ") IMAGEDATA " + + count + + ": ... " + + leading(finish, 8) + + finish); + } + } + + return next; + } } diff --git a/jhove-modules/tiff-hul/src/main/java/edu/harvard/hul/ois/jhove/module/TiffModule.java b/jhove-modules/tiff-hul/src/main/java/edu/harvard/hul/ois/jhove/module/TiffModule.java index 723c73b85..0081b39bf 100644 --- a/jhove-modules/tiff-hul/src/main/java/edu/harvard/hul/ois/jhove/module/TiffModule.java +++ b/jhove-modules/tiff-hul/src/main/java/edu/harvard/hul/ois/jhove/module/TiffModule.java @@ -1,34 +1,22 @@ -/********************************************************************** - * Jhove - JSTOR/Harvard Object Validation Environment Copyright 2003-2012 by - * JSTOR and the President and Fellows of Harvard College +/** + * ******************************************************************** Jhove - JSTOR/Harvard Object + * Validation Environment Copyright 2003-2012 by JSTOR and the President and Fellows of Harvard + * College * - * This program 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 2 of the License, or (at your option) any - * later version. + *

This program 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 2 + * of the License, or (at your option) any later version. * - * This program 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. + *

This program 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. * - * You should have received a copy of the GNU Lesser General Public License - * along with this program; if not, write to the Free Software Foundation, Inc., - * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - **********************************************************************/ - + *

You should have received a copy of the GNU Lesser General Public License along with this + * program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA ******************************************************************** + */ package edu.harvard.hul.ois.jhove.module; -import java.io.IOException; -import java.io.RandomAccessFile; -import java.text.MessageFormat; -import java.util.ArrayList; -import java.util.Iterator; -import java.util.LinkedList; -import java.util.List; -import java.util.ListIterator; -import java.util.logging.Logger; - import edu.harvard.hul.ois.jhove.Agent; import edu.harvard.hul.ois.jhove.AgentType; import edu.harvard.hul.ois.jhove.Document; @@ -102,1230 +90,1296 @@ import edu.harvard.hul.ois.jhove.module.tiff.TiffProfileFXS; import edu.harvard.hul.ois.jhove.module.tiff.TiffProfileGeoTIFF; import edu.harvard.hul.ois.jhove.module.tiff.TiffProfileRFC1314; +import java.io.IOException; +import java.io.RandomAccessFile; +import java.text.MessageFormat; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; +import java.util.ListIterator; +import java.util.logging.Logger; -/** - * Module for identification and validation of TIFF files. - */ +/** Module for identification and validation of TIFF files. */ public class TiffModule extends ModuleBase { - /** - * Value to write as module params to the default config file. - */ - public static final String[] defaultConfigParams = { "byteoffset=true" }; - - /****************************************************************** - * PRIVATE CLASS FIELDS. - ******************************************************************/ - - /** Logger for this class. */ - protected Logger _logger; - - private static final String NAME = "TIFF-hul"; - private static final String RELEASE = "1.9.2"; - private static final int [] DATE = { 2019, 12, 10 }; - private static final String[] FORMAT = { "TIFF", "Tagged Image File Format" }; - private static final String COVERAGE = "TIFF 4.0, 5.0, and 6.0; " - + "TIFF/IT (ISO/DIS 12639:2003), including file types CT, LW, HC, MP, " - + "BP, BL, and FP, and conformance levels P1 and P2; TIFF/EP " - + "(ISO 12234-2:2001); " - + "Exif 2.0, 2.1 (JEIDA-49-1998), 2.2 (JEITA CP-3451), 2.21 (JEITA CP-3451A), and 2.3 (JEITA CP-3451C); " - + "Baseline GeoTIFF " - + "1.0; Baseline 6.0 bilevel (known in TIFF 5.0 as Class B), " - + "grayscale (Class G), palette-color (Class P), and RGB (Class R); " - + "6.0 extension YCbCr (Class Y); DLF Benchmark for Faithful Digital " - + "Reproductions of Monographs and Serials; TIFF-FX (RFC 2301), " - + "Class F (RFC 2306); RFC 1314; " + "and DNG (Digital Negative)"; - - /*** - * These profiles are not reported anymore (SLA, 2004-01-06) - * "Adobe PageMaker 6.0; Adobe Photoshop 'Advanced TIFF'; " + - ***/ - - private static final String[] MIMETYPE = { "image/tiff", "image/tiff-fx", - "image/ief" }; - private static final String WELLFORMED = "A TIFF file is well-formed if " - + "it has a big-endian or little-endian header; at least one IFD; all " - + "IFDs are 16-bit word aligned; all IFDs have at least one entry; " - + "all IFD entries are sorted in ascending order by tag number; all " - + "IFD entries specify the correct type and count; all value offsets " - + "are 16-bit word aligned; all value offsets reference locations " - + "within the file; and the final IFD is followed by an offset of 0"; - private static final String VALIDITY = "A TIFF file is valid if " - + "well-formed; ImageLength, ImageWidth, and " - + "PhotometricInterpretation tags are defined; strip or tile tags " - + "are defined; tag values are self-consistent (see JHOVE " - + "documentation); TileWidth and TileLength values are integral " - + "multiples of 16; and DateTime tag is properly formatted"; - private static final String REPINFO = "Additional representation " - + "information includes: NISO Z39.87 Digital Still Image Technical " - + "Metadata and all other tag values"; - private static final String NOTE = null; - private static final String RIGHTS = "Copyright 2003-2007 by JSTOR and " - + "the President and Fellows of Harvard College. " - + "Released under the GNU Lesser General Public License."; - - /****************************************************************** - * PRIVATE INSTANCE FIELDS. - ******************************************************************/ - - /** List of profile checkers. */ - protected List _profile; - - /* Exif profile checker for the main IFD. */ - TiffProfileExif _exifMainProfile; - - /* Exif profile checker for the thumbnail IFD. */ - TiffProfileExifThumb _exifThumbnailProfile; - - /* DNG profile checker for Raw IFD. */ - TiffProfileDNG _dngMainProfile; - - /* DNG profile checker for IFD 0. */ - TiffProfileDNGThumb _dngThumbnailProfile; - - /** Special flag for Exif profiles: Is main IFD profile satisfied */ - protected boolean _exifFirstFlag; - /** Special flag for Exif profiles: Is thumbnail IFD profile satisfied */ - protected boolean _exifThumbnailFlag; - - /** Special flag for DNG profiles; is "thumbnail" (IFD 0) profile satisfied */ - protected boolean _dngThumbnailFlag; - /** Special flag for DNG profiles; is raw IFD profile satisfied */ - protected boolean _dngRawFlag; - - /** Open TIFF file. */ - protected RandomAccessFile _raf; - /** TIFF version. */ - protected int _version; - - protected boolean _byteOffsetIsValid; - - /****************************************************************** - * CLASS CONSTRUCTOR. - ******************************************************************/ - - /** - * Instantiate a TiffModule object. - */ - public TiffModule() { - super(NAME, RELEASE, DATE, FORMAT, COVERAGE, MIMETYPE, WELLFORMED, - VALIDITY, REPINFO, NOTE, RIGHTS, true); - - _logger = Logger.getLogger("edu.harvard.hul.ois.jhove"); - - // Define vendor agent (HUL) - _vendor = Agent.harvardInstance(); - - // Define TIFF 6.0 document with Adobe agent - Document doc = new Document("TIFF, Revision 6.0", DocumentType.REPORT); - Agent adobeAgent = Agent.newAdobeInstance(); - doc.setPublisher(adobeAgent); - doc.setDate("1992-06-03"); - doc.setEdition("Final"); - doc.setIdentifier(new Identifier("http://partners.adobe.com/asn/" - + "tech/tiff/specification.jsp", IdentifierType.URL)); - _specification.add(doc); - - // Define TIFF 5.0 document reusing Adobe agent - doc = new Document("TIFF, Revision 5.0", DocumentType.REPORT); - Agent agent = new Agent.Builder("Aldus Corporation", - AgentType.COMMERCIAL).build(); - doc.setPublisher(agent); - doc.setDate("1988-08-08"); - doc.setNote("Aldus was acquired by Adobe Systems, Inc., in 1993"); - _specification.add(doc); - - doc = new Document("Tagged Image File Format, Rev. 4.0", - DocumentType.REPORT); - agent = new Agent.Builder("Aldus Corporation", AgentType.COMMERCIAL) - .build(); - doc.setPublisher(agent); - doc.setDate("1987-04-30"); - doc.setNote("Aldus was acquired by Adobe Systems, Inc., in 1993"); - _specification.add(doc); - - // Define TIFF/EP document with ISO agent - doc = new Document("ISO 12234-2:2001, Electronic still-picture " + /** Value to write as module params to the default config file. */ + public static final String[] defaultConfigParams = {"byteoffset=true"}; + + /** + * **************************************************************** PRIVATE CLASS FIELDS. + * **************************************************************** + */ + + /** Logger for this class. */ + protected Logger _logger; + + private static final String NAME = "TIFF-hul"; + private static final String RELEASE = "1.9.2"; + private static final int[] DATE = {2019, 12, 10}; + private static final String[] FORMAT = {"TIFF", "Tagged Image File Format"}; + private static final String COVERAGE = + "TIFF 4.0, 5.0, and 6.0; " + + "TIFF/IT (ISO/DIS 12639:2003), including file types CT, LW, HC, MP, " + + "BP, BL, and FP, and conformance levels P1 and P2; TIFF/EP " + + "(ISO 12234-2:2001); " + + "Exif 2.0, 2.1 (JEIDA-49-1998), 2.2 (JEITA CP-3451), 2.21 (JEITA CP-3451A), and 2.3 (JEITA CP-3451C); " + + "Baseline GeoTIFF " + + "1.0; Baseline 6.0 bilevel (known in TIFF 5.0 as Class B), " + + "grayscale (Class G), palette-color (Class P), and RGB (Class R); " + + "6.0 extension YCbCr (Class Y); DLF Benchmark for Faithful Digital " + + "Reproductions of Monographs and Serials; TIFF-FX (RFC 2301), " + + "Class F (RFC 2306); RFC 1314; " + + "and DNG (Digital Negative)"; + + /** + * * These profiles are not reported anymore (SLA, 2004-01-06) "Adobe PageMaker 6.0; Adobe + * Photoshop 'Advanced TIFF'; " + * + */ + private static final String[] MIMETYPE = {"image/tiff", "image/tiff-fx", "image/ief"}; + + private static final String WELLFORMED = + "A TIFF file is well-formed if " + + "it has a big-endian or little-endian header; at least one IFD; all " + + "IFDs are 16-bit word aligned; all IFDs have at least one entry; " + + "all IFD entries are sorted in ascending order by tag number; all " + + "IFD entries specify the correct type and count; all value offsets " + + "are 16-bit word aligned; all value offsets reference locations " + + "within the file; and the final IFD is followed by an offset of 0"; + private static final String VALIDITY = + "A TIFF file is valid if " + + "well-formed; ImageLength, ImageWidth, and " + + "PhotometricInterpretation tags are defined; strip or tile tags " + + "are defined; tag values are self-consistent (see JHOVE " + + "documentation); TileWidth and TileLength values are integral " + + "multiples of 16; and DateTime tag is properly formatted"; + private static final String REPINFO = + "Additional representation " + + "information includes: NISO Z39.87 Digital Still Image Technical " + + "Metadata and all other tag values"; + private static final String NOTE = null; + private static final String RIGHTS = + "Copyright 2003-2007 by JSTOR and " + + "the President and Fellows of Harvard College. " + + "Released under the GNU Lesser General Public License."; + + /** + * **************************************************************** PRIVATE INSTANCE FIELDS. + * **************************************************************** + */ + + /** List of profile checkers. */ + protected List _profile; + + /* Exif profile checker for the main IFD. */ + TiffProfileExif _exifMainProfile; + + /* Exif profile checker for the thumbnail IFD. */ + TiffProfileExifThumb _exifThumbnailProfile; + + /* DNG profile checker for Raw IFD. */ + TiffProfileDNG _dngMainProfile; + + /* DNG profile checker for IFD 0. */ + TiffProfileDNGThumb _dngThumbnailProfile; + + /** Special flag for Exif profiles: Is main IFD profile satisfied */ + protected boolean _exifFirstFlag; + /** Special flag for Exif profiles: Is thumbnail IFD profile satisfied */ + protected boolean _exifThumbnailFlag; + + /** Special flag for DNG profiles; is "thumbnail" (IFD 0) profile satisfied */ + protected boolean _dngThumbnailFlag; + /** Special flag for DNG profiles; is raw IFD profile satisfied */ + protected boolean _dngRawFlag; + + /** Open TIFF file. */ + protected RandomAccessFile _raf; + /** TIFF version. */ + protected int _version; + + protected boolean _byteOffsetIsValid; + + /** + * **************************************************************** CLASS CONSTRUCTOR. + * **************************************************************** + */ + + /** Instantiate a TiffModule object. */ + public TiffModule() { + super( + NAME, + RELEASE, + DATE, + FORMAT, + COVERAGE, + MIMETYPE, + WELLFORMED, + VALIDITY, + REPINFO, + NOTE, + RIGHTS, + true); + + _logger = Logger.getLogger("edu.harvard.hul.ois.jhove"); + + // Define vendor agent (HUL) + _vendor = Agent.harvardInstance(); + + // Define TIFF 6.0 document with Adobe agent + Document doc = new Document("TIFF, Revision 6.0", DocumentType.REPORT); + Agent adobeAgent = Agent.newAdobeInstance(); + doc.setPublisher(adobeAgent); + doc.setDate("1992-06-03"); + doc.setEdition("Final"); + doc.setIdentifier( + new Identifier( + "http://partners.adobe.com/asn/" + "tech/tiff/specification.jsp", IdentifierType.URL)); + _specification.add(doc); + + // Define TIFF 5.0 document reusing Adobe agent + doc = new Document("TIFF, Revision 5.0", DocumentType.REPORT); + Agent agent = new Agent.Builder("Aldus Corporation", AgentType.COMMERCIAL).build(); + doc.setPublisher(agent); + doc.setDate("1988-08-08"); + doc.setNote("Aldus was acquired by Adobe Systems, Inc., in 1993"); + _specification.add(doc); + + doc = new Document("Tagged Image File Format, Rev. 4.0", DocumentType.REPORT); + agent = new Agent.Builder("Aldus Corporation", AgentType.COMMERCIAL).build(); + doc.setPublisher(agent); + doc.setDate("1987-04-30"); + doc.setNote("Aldus was acquired by Adobe Systems, Inc., in 1993"); + _specification.add(doc); + + // Define TIFF/EP document with ISO agent + doc = + new Document( + "ISO 12234-2:2001, Electronic still-picture " + "imaging -- Removable memory -- " - + "Part 2: TIFF/EP image data format", DocumentType.STANDARD); - Agent isoAgent = Agent.newIsoInstance(); - doc.setPublisher(isoAgent); - doc.setDate("2001-10-15"); - Identifier ident = new Identifier("ISO 12234-2:2001(E)", - IdentifierType.ISO); - doc.setIdentifier(ident); - _specification.add(doc); - - // Define TIFF/IT document, reusing ISO agent - doc = new Document("ISO/DIS 12639:2003, Graphic technology -- " + + "Part 2: TIFF/EP image data format", + DocumentType.STANDARD); + Agent isoAgent = Agent.newIsoInstance(); + doc.setPublisher(isoAgent); + doc.setDate("2001-10-15"); + Identifier ident = new Identifier("ISO 12234-2:2001(E)", IdentifierType.ISO); + doc.setIdentifier(ident); + _specification.add(doc); + + // Define TIFF/IT document, reusing ISO agent + doc = + new Document( + "ISO/DIS 12639:2003, Graphic technology -- " + "Prepress digital data exchange -- " - + "Tag image file format for image technology " + "(TIFF/IT)", - DocumentType.STANDARD); - /* This uses the same agent (ISO) as the prior doc */ - doc.setPublisher(isoAgent); - doc.setDate("2003-09-04"); - ident = new Identifier("ISO/DIS 12639:2003(E)", IdentifierType.ISO); - doc.setIdentifier(ident); - _specification.add(doc); - - // Define Digital Library Federation doc - doc = new Document("Benchmark for Faithful Digital Reproductions " - + "of Monographs and Serials", DocumentType.REPORT); - agent = new Agent.Builder("Digital Library Federation", - AgentType.NONPROFIT) - .address( - "1755 Massachusetts Ave., NW, Suite 500, " - + "Washington, DC 20036") - .telephone("+1 (202) 939-4761").fax("+1 (202) 939-4765") - .email("dlf@clir.org").web("http://www.diglib.org/").build(); - doc.setPublisher(agent); - doc.setEdition("Version 1"); - doc.setDate("2002-12"); - ident = new Identifier("http://www.diglib.org/standards/bmarkfin.htm", - IdentifierType.URL); - doc.setIdentifier(ident); - _specification.add(doc); - - // Define PageMaker TIFF doc, reusing Adobe agent - doc = new Document("Adobe PageMaker TIFF 6.0 Technical Notes", - DocumentType.REPORT); - doc.setPublisher(adobeAgent); - doc.setDate("1995-09-14"); - ident = new Identifier( - "http://partners.adobe.com/asn/developer/pdfs/tn/TIFFPM6.pdf", - IdentifierType.URL); - doc.setIdentifier(ident); - _specification.add(doc); - - // Define Photoshop TIFF doc, reusing Adobe agent - doc = new Document("Adobe Photoshop TIFF Technical Notes", - DocumentType.REPORT); - doc.setPublisher(adobeAgent); - doc.setDate("2002-03-22"); - ident = new Identifier("http://partners.adobe.com/asn/developer/" - + "pdfs/tn/TIFFphotoshop.pdf", IdentifierType.URL); - doc.setIdentifier(ident); - _specification.add(doc); - - // Define Photoshop file formats doc, reusing Adobe agent - doc = new Document("Adobe Photoshop 6.0 File Formats Specification", - DocumentType.REPORT); - doc.setPublisher(adobeAgent); - doc.setDate("2000-11"); - doc.setEdition("Version 6.0, Release 2"); - _specification.add(doc); - - // Define TIFF Class F doc - doc = new Document("TIFF-F Revised Specification: The " - + "Spirit of TIFF Class F", DocumentType.REPORT); - agent = new Agent.Builder("Cygnet Technologies", AgentType.COMMERCIAL) - .build(); - doc.setPublisher(agent); - doc.setDate("1990-04-28"); - doc.setNote("Cygnet is no longer in business"); - _specification.add(doc); - - // Define IETF Class F doc, with IETF agent Added 2/2/04 - doc = new Document("Tag Image File Format (TIFF) -- F Profile " - + "for Facsimile", DocumentType.RFC); - Agent ietfAgent = new Agent.Builder("IETF", AgentType.STANDARD).web( - "http://www.ietf.org").build(); - doc.setPublisher(ietfAgent); - doc.setDate("1998-03"); - ident = new Identifier("RFC 2306", IdentifierType.RFC); - doc.setIdentifier(ident); - ident = new Identifier( - "http://hul.harvard.edu/jhove/references.html#rfc2306", - IdentifierType.URL); - doc.setIdentifier(ident); - _specification.add(doc); - - // Define RFC 1314 doc, reusing IETF agent - doc = new Document("A File Format for the Exchange of " - + "Images in the Internet", DocumentType.RFC); - doc.setPublisher(ietfAgent); - doc.setDate("1992-04"); - ident = new Identifier("RFC 1314", IdentifierType.RFC); - doc.setIdentifier(ident); - ident = new Identifier("http://www.ietf.org/rfc/rfc1314.txt", - IdentifierType.URL); - doc.setIdentifier(ident); - _specification.add(doc); - - // Define JEITA Exif 2.3 doc - doc = new Document("Exchangeable image file format for digital " - + "still cameras: Exif Version 2.3", DocumentType.STANDARD); - Agent jeitaAgent = new Agent.Builder( - "Japan Electronics and Information Technology " - + "Industries Association", AgentType.STANDARD) - .web("http://www.jeita.or.jp/") - .address( - "Mitsui Sumitomo Kaijo Building Annex, " - + "11, Kanda Surugadai 3-chome, Chiyoda-ku, " - + "Tokyo 101-0062, Japan") - .telephone("+81(03) 3518-6421").fax("+81(03) 3295-8721") - .build(); - doc.setPublisher(jeitaAgent); - doc.setDate("2010-04"); - ident = new Identifier("JEITA CP-3451C", IdentifierType.JEITA); - doc.setIdentifier(ident); - ident = new Identifier("http://home.jeita.or.jp/tsc/std-pdf/CP3451C.pdf", - IdentifierType.URL); - doc.setIdentifier(ident); - _specification.add(doc); - - // Define JEITA Exif 2.2 doc - doc = new Document("Exchangeable image file format for digital " - + "still cameras: Exif Version 2.2", DocumentType.STANDARD); - doc.setPublisher(jeitaAgent); - doc.setDate("2002-04"); - ident = new Identifier("JEITA CP-3451", IdentifierType.JEITA); - doc.setIdentifier(ident); - ident = new Identifier("http://www.exif.org/Exif2-2.PDF", - IdentifierType.URL); - doc.setIdentifier(ident); - _specification.add(doc); - - // Define Exif 2.1 doc - doc = new Document( - "Digital Still Camera Image File Format Standard " - + "(Exchangeable image file format for Digital Still Camera:Exif)", - DocumentType.STANDARD); - doc.setPublisher(jeitaAgent); - doc.setDate("1998-12"); - ident = new Identifier("JEITA JEIDA-49-1998", IdentifierType.JEITA); - doc.setIdentifier(ident); - ident = new Identifier("http://www.exif.org/dcf-exif.PDF", - IdentifierType.URL); - doc.setIdentifier(ident); - _specification.add(doc); - - // Define GeoTIFF doc - doc = new Document("GeoTIFF Format Specification: " - + "GeoTIFF Revision 1.0", DocumentType.REPORT); - agent = new Agent.Builder("Niles Ritter", AgentType.OTHER).build(); - doc.setAuthor(agent); - agent = new Agent.Builder("Mike Ruth", AgentType.OTHER).build(); - doc.setAuthor(agent); - agent = new Agent.Builder("GeoTIFF Working Group", AgentType.OTHER).build(); - doc.setPublisher(agent); - doc.setEdition("Version 1.8.1"); - doc.setDate("1995-10-31"); - ident = new Identifier("http://remotesensing.org/geotiff/spec/" - + "geotiffhome.html", IdentifierType.URL); - doc.setIdentifier(ident); - _specification.add(doc); - - // Define RFC 2301 doc (Internet Fax) with IETF agent - doc = new Document("File Format for Internet Fax", DocumentType.RFC); - doc.setPublisher(ietfAgent); - doc.setDate("1998-03"); - ident = new Identifier("RFC 2301", IdentifierType.RFC); - doc.setIdentifier(ident); - ident = new Identifier("http://www.ietf.org/rfc/rfc2301.txt", - IdentifierType.URL); - doc.setIdentifier(ident); - _specification.add(doc); - - // Whew -- finally done with docs - - int[] sigbyteI = { 0x49, 0x49, 42, 0 }; - Signature sig = new InternalSignature(sigbyteI, SignatureType.MAGIC, - SignatureUseType.MANDATORY_IF_APPLICABLE, 0, - "Little-endian (least significant byte " + "first)"); - _signature.add(sig); - - int[] sigbyteM = { 0x4D, 0x4D, 0, 42 }; - sig = new InternalSignature(sigbyteM, SignatureType.MAGIC, - SignatureUseType.MANDATORY_IF_APPLICABLE, 0, - "Big-endian (most significant byte first)"); - _signature.add(sig); - - sig = new ExternalSignature("TIFF", SignatureType.FILETYPE, - SignatureUseType.OPTIONAL); - _signature.add(sig); - - sig = new ExternalSignature(".tif", SignatureType.EXTENSION, - SignatureUseType.OPTIONAL); - _signature.add(sig); - - sig = new ExternalSignature(".tfx", SignatureType.EXTENSION, - SignatureUseType.OPTIONAL, "For TIFF-FX"); - _signature.add(sig); - - sig = new ExternalSignature("TFX ", SignatureType.FILETYPE, - SignatureUseType.OPTIONAL, "For TIFF-FX"); - _signature.add(sig); - - buildProfileList(); - - _byteOffsetIsValid = false; - } - - /****************************************************************** - * PUBLIC INSTANCE METHODS. - * - * Parsing methods. - ******************************************************************/ - - /** - * Parse the TIFF for well-formedness and validity, accumulating - * representation information. - * - * @param raf - * Open TIFF file - * @param info - * Representation informatino - */ - @Override - public final void parse(RandomAccessFile raf, RepInfo info) - throws IOException { - if (_defaultParams != null) { - Iterator iter = _defaultParams.iterator(); - while (iter.hasNext()) { - String param = iter.next(); - if ("byteoffset=true".equalsIgnoreCase(param)) { - _byteOffsetIsValid = true; - } - } - } - - _raf = raf; - _logger.info("TiffModule parsing file"); - initParse(); - info.setModule(this); - info.setMimeType(_mimeType[0]); - info.setFormat(_format[0]); - - Property[] tiffMetadata = new Property[2]; - List ifds = null; - boolean inHeader = true; // Useful for catching empty files - try { - /* - * TIFF header is "II" (little-endian) or "MM" (big-endian), - * followed by the 16-bit integer value 42. - */ - raf.seek(0); - byte ch0 = _raf.readByte(); - byte ch1 = _raf.readByte(); - if (ch0 != ch1 || (ch0 != 0X49 && ch0 != 0X4D)) { - String mess = MessageFormat.format(MessageConstants.TIFF_HUL_20.getMessage(), (char) ch0, (char) ch1); - JhoveMessage message = JhoveMessages.getMessageInstance(MessageConstants.TIFF_HUL_20.getId(), mess); - throw new TiffException(message, 0); - } - inHeader = false; - - _bigEndian = (ch0 == 0X4D); - tiffMetadata[0] = new Property("ByteOrder", PropertyType.STRING, - _bigEndian ? "big-endian" : "little-endian"); - - int magic = readUnsignedShort(_raf, _bigEndian); - if (magic != 42) { - String mess = MessageFormat.format(MessageConstants.TIFF_HUL_21.getMessage(), magic); - JhoveMessage message = JhoveMessages.getMessageInstance(MessageConstants.TIFF_HUL_21.getId(), mess); - throw new TiffException(message, 2); - } - - /* If we got this far, take note that the signature is OK. */ - info.setSigMatch(_name); - - /* - * The offset of the first IFD is found at offset 4. /* The lowest - * recognized TIFF version is 4. Increment this as features specific - * to higher versions are recognized. - */ - _version = 4; - ifds = parseIFDs(4, info); - - info.setVersion(Integer.toString(_version) + ".0"); - - /* Construct IFDs property. */ - List ifdsList = new LinkedList(); - Property ifdsProp = new Property("IFDs", PropertyType.PROPERTY, - PropertyArity.LIST, ifdsList); - ifdsList.add(new Property("Number", PropertyType.INTEGER, - new Integer(ifds.size()))); - - /* Build the IFD property list, for each of the IFDs. */ - ListIterator iter = ifds.listIterator(); - while (iter.hasNext()) { - IFD ifd = iter.next(); - ifdsList.add(ifd.getProperty(_je != null ? _je.getShowRawFlag() - : false)); - - /* - * Check if any messages were generated in constructing the - * property. If so, the IFD is invalid. - */ - - List errors = ifd.getErrors(); - if (!errors.isEmpty()) { - info.setValid(false); - ListIterator eter = errors.listIterator(); - while (eter.hasNext()) { - info.setMessage(new ErrorMessage(eter.next())); - } - } - - /* Check each IFD for profile conformance. */ - - ListIterator pter = _profile.listIterator(); - while (pter.hasNext()) { - TiffProfile prof = pter.next(); - if (!prof.isAlreadyOK() && prof.satisfiesProfile(ifd)) { - info.setProfile(prof.getText()); - } - } - - /* - * Checking the Exif profile is more complicated, since several - * IFD's need to meet their respective requirements. Try - * something like this. - */ - if (ifd.isFirst()) { - _exifFirstFlag = _exifMainProfile.satisfiesProfile(ifd); - // We need to compare compression between the main and - // thumbnail IFD's. - _exifThumbnailProfile.setMainCompression(((TiffIFD) ifd) - .getNisoImageMetadata().getCompressionScheme()); - } else if (ifd.isThumbnail()) { - _exifThumbnailFlag = _exifThumbnailProfile - .satisfiesProfile(ifd); - } - /* - * The DNG profile is similarly complex, requiring IFD 0 to - * satisfy the thumbnail part and some other IFD to satisfy the - * main profile. The spec doesn't actually say they can't be the - * same profile, so we allow for that possibility. - */ - if (ifd.isFirst()) { - _dngThumbnailFlag = _dngThumbnailProfile - .satisfiesProfile(ifd); - } - if (!_dngRawFlag) { - _dngRawFlag = _dngMainProfile.satisfiesProfile(ifd); - } - } - tiffMetadata[1] = ifdsProp; - - /* - * The Exif profile requires coordinating several IFD checks, so we - * accumulate flags and then set the profile text if they're all - * set. - */ - if (_exifFirstFlag && _exifThumbnailFlag) { - info.setProfile(_exifMainProfile.getText()); - } - - /* Similarly for the DNG profile */ - if (_dngThumbnailFlag && _dngRawFlag) { - info.setProfile(_dngMainProfile.getText()); - } - - info.setProperty(new Property("TIFFMetadata", - PropertyType.PROPERTY, PropertyArity.ARRAY, tiffMetadata)); - } catch (TiffException e) { - if (e.getJhoveMessage() != null) { // try to keep the id - info.setMessage(new ErrorMessage(e.getJhoveMessage(), e.getOffset())); - } else { - info.setMessage(new ErrorMessage(e.getMessage(), e.getOffset())); - } - info.setWellFormed(false); - return; - } catch (IOException e) { - JhoveMessage msg; - if (inHeader) { - msg = MessageConstants.TIFF_HUL_67; - } else { - String mess = MessageFormat.format(MessageConstants.TIFF_HUL_68.getMessage(), e.getClass().getName()); - msg = JhoveMessages.getMessageInstance(MessageConstants.TIFF_HUL_68.getId(), mess); - } - info.setMessage(new ErrorMessage(msg)); - info.setWellFormed(false); - return; + + "Tag image file format for image technology " + + "(TIFF/IT)", + DocumentType.STANDARD); + /* This uses the same agent (ISO) as the prior doc */ + doc.setPublisher(isoAgent); + doc.setDate("2003-09-04"); + ident = new Identifier("ISO/DIS 12639:2003(E)", IdentifierType.ISO); + doc.setIdentifier(ident); + _specification.add(doc); + + // Define Digital Library Federation doc + doc = + new Document( + "Benchmark for Faithful Digital Reproductions " + "of Monographs and Serials", + DocumentType.REPORT); + agent = + new Agent.Builder("Digital Library Federation", AgentType.NONPROFIT) + .address("1755 Massachusetts Ave., NW, Suite 500, " + "Washington, DC 20036") + .telephone("+1 (202) 939-4761") + .fax("+1 (202) 939-4765") + .email("dlf@clir.org") + .web("http://www.diglib.org/") + .build(); + doc.setPublisher(agent); + doc.setEdition("Version 1"); + doc.setDate("2002-12"); + ident = new Identifier("http://www.diglib.org/standards/bmarkfin.htm", IdentifierType.URL); + doc.setIdentifier(ident); + _specification.add(doc); + + // Define PageMaker TIFF doc, reusing Adobe agent + doc = new Document("Adobe PageMaker TIFF 6.0 Technical Notes", DocumentType.REPORT); + doc.setPublisher(adobeAgent); + doc.setDate("1995-09-14"); + ident = + new Identifier( + "http://partners.adobe.com/asn/developer/pdfs/tn/TIFFPM6.pdf", IdentifierType.URL); + doc.setIdentifier(ident); + _specification.add(doc); + + // Define Photoshop TIFF doc, reusing Adobe agent + doc = new Document("Adobe Photoshop TIFF Technical Notes", DocumentType.REPORT); + doc.setPublisher(adobeAgent); + doc.setDate("2002-03-22"); + ident = + new Identifier( + "http://partners.adobe.com/asn/developer/" + "pdfs/tn/TIFFphotoshop.pdf", + IdentifierType.URL); + doc.setIdentifier(ident); + _specification.add(doc); + + // Define Photoshop file formats doc, reusing Adobe agent + doc = new Document("Adobe Photoshop 6.0 File Formats Specification", DocumentType.REPORT); + doc.setPublisher(adobeAgent); + doc.setDate("2000-11"); + doc.setEdition("Version 6.0, Release 2"); + _specification.add(doc); + + // Define TIFF Class F doc + doc = + new Document( + "TIFF-F Revised Specification: The " + "Spirit of TIFF Class F", DocumentType.REPORT); + agent = new Agent.Builder("Cygnet Technologies", AgentType.COMMERCIAL).build(); + doc.setPublisher(agent); + doc.setDate("1990-04-28"); + doc.setNote("Cygnet is no longer in business"); + _specification.add(doc); + + // Define IETF Class F doc, with IETF agent Added 2/2/04 + doc = + new Document( + "Tag Image File Format (TIFF) -- F Profile " + "for Facsimile", DocumentType.RFC); + Agent ietfAgent = + new Agent.Builder("IETF", AgentType.STANDARD).web("http://www.ietf.org").build(); + doc.setPublisher(ietfAgent); + doc.setDate("1998-03"); + ident = new Identifier("RFC 2306", IdentifierType.RFC); + doc.setIdentifier(ident); + ident = + new Identifier("http://hul.harvard.edu/jhove/references.html#rfc2306", IdentifierType.URL); + doc.setIdentifier(ident); + _specification.add(doc); + + // Define RFC 1314 doc, reusing IETF agent + doc = + new Document( + "A File Format for the Exchange of " + "Images in the Internet", DocumentType.RFC); + doc.setPublisher(ietfAgent); + doc.setDate("1992-04"); + ident = new Identifier("RFC 1314", IdentifierType.RFC); + doc.setIdentifier(ident); + ident = new Identifier("http://www.ietf.org/rfc/rfc1314.txt", IdentifierType.URL); + doc.setIdentifier(ident); + _specification.add(doc); + + // Define JEITA Exif 2.3 doc + doc = + new Document( + "Exchangeable image file format for digital " + "still cameras: Exif Version 2.3", + DocumentType.STANDARD); + Agent jeitaAgent = + new Agent.Builder( + "Japan Electronics and Information Technology " + "Industries Association", + AgentType.STANDARD) + .web("http://www.jeita.or.jp/") + .address( + "Mitsui Sumitomo Kaijo Building Annex, " + + "11, Kanda Surugadai 3-chome, Chiyoda-ku, " + + "Tokyo 101-0062, Japan") + .telephone("+81(03) 3518-6421") + .fax("+81(03) 3295-8721") + .build(); + doc.setPublisher(jeitaAgent); + doc.setDate("2010-04"); + ident = new Identifier("JEITA CP-3451C", IdentifierType.JEITA); + doc.setIdentifier(ident); + ident = new Identifier("http://home.jeita.or.jp/tsc/std-pdf/CP3451C.pdf", IdentifierType.URL); + doc.setIdentifier(ident); + _specification.add(doc); + + // Define JEITA Exif 2.2 doc + doc = + new Document( + "Exchangeable image file format for digital " + "still cameras: Exif Version 2.2", + DocumentType.STANDARD); + doc.setPublisher(jeitaAgent); + doc.setDate("2002-04"); + ident = new Identifier("JEITA CP-3451", IdentifierType.JEITA); + doc.setIdentifier(ident); + ident = new Identifier("http://www.exif.org/Exif2-2.PDF", IdentifierType.URL); + doc.setIdentifier(ident); + _specification.add(doc); + + // Define Exif 2.1 doc + doc = + new Document( + "Digital Still Camera Image File Format Standard " + + "(Exchangeable image file format for Digital Still Camera:Exif)", + DocumentType.STANDARD); + doc.setPublisher(jeitaAgent); + doc.setDate("1998-12"); + ident = new Identifier("JEITA JEIDA-49-1998", IdentifierType.JEITA); + doc.setIdentifier(ident); + ident = new Identifier("http://www.exif.org/dcf-exif.PDF", IdentifierType.URL); + doc.setIdentifier(ident); + _specification.add(doc); + + // Define GeoTIFF doc + doc = + new Document( + "GeoTIFF Format Specification: " + "GeoTIFF Revision 1.0", DocumentType.REPORT); + agent = new Agent.Builder("Niles Ritter", AgentType.OTHER).build(); + doc.setAuthor(agent); + agent = new Agent.Builder("Mike Ruth", AgentType.OTHER).build(); + doc.setAuthor(agent); + agent = new Agent.Builder("GeoTIFF Working Group", AgentType.OTHER).build(); + doc.setPublisher(agent); + doc.setEdition("Version 1.8.1"); + doc.setDate("1995-10-31"); + ident = + new Identifier( + "http://remotesensing.org/geotiff/spec/" + "geotiffhome.html", IdentifierType.URL); + doc.setIdentifier(ident); + _specification.add(doc); + + // Define RFC 2301 doc (Internet Fax) with IETF agent + doc = new Document("File Format for Internet Fax", DocumentType.RFC); + doc.setPublisher(ietfAgent); + doc.setDate("1998-03"); + ident = new Identifier("RFC 2301", IdentifierType.RFC); + doc.setIdentifier(ident); + ident = new Identifier("http://www.ietf.org/rfc/rfc2301.txt", IdentifierType.URL); + doc.setIdentifier(ident); + _specification.add(doc); + + // Whew -- finally done with docs + + int[] sigbyteI = {0x49, 0x49, 42, 0}; + Signature sig = + new InternalSignature( + sigbyteI, + SignatureType.MAGIC, + SignatureUseType.MANDATORY_IF_APPLICABLE, + 0, + "Little-endian (least significant byte " + "first)"); + _signature.add(sig); + + int[] sigbyteM = {0x4D, 0x4D, 0, 42}; + sig = + new InternalSignature( + sigbyteM, + SignatureType.MAGIC, + SignatureUseType.MANDATORY_IF_APPLICABLE, + 0, + "Big-endian (most significant byte first)"); + _signature.add(sig); + + sig = new ExternalSignature("TIFF", SignatureType.FILETYPE, SignatureUseType.OPTIONAL); + _signature.add(sig); + + sig = new ExternalSignature(".tif", SignatureType.EXTENSION, SignatureUseType.OPTIONAL); + _signature.add(sig); + + sig = + new ExternalSignature( + ".tfx", SignatureType.EXTENSION, SignatureUseType.OPTIONAL, "For TIFF-FX"); + _signature.add(sig); + + sig = + new ExternalSignature( + "TFX ", SignatureType.FILETYPE, SignatureUseType.OPTIONAL, "For TIFF-FX"); + _signature.add(sig); + + buildProfileList(); + + _byteOffsetIsValid = false; + } + + /** + * **************************************************************** PUBLIC INSTANCE METHODS. + * + *

Parsing methods. **************************************************************** + */ + + /** + * Parse the TIFF for well-formedness and validity, accumulating representation information. + * + * @param raf Open TIFF file + * @param info Representation informatino + */ + @Override + public final void parse(RandomAccessFile raf, RepInfo info) throws IOException { + if (_defaultParams != null) { + Iterator iter = _defaultParams.iterator(); + while (iter.hasNext()) { + String param = iter.next(); + if ("byteoffset=true".equalsIgnoreCase(param)) { + _byteOffsetIsValid = true; } - - /* Object is well-formed TIFF. */ - - /* Calculate checksums, if necessary. */ - checksumIfRafNotCopied(info, raf); - - info.setMimeType(_mimeType[selectMimeTypeIndex()]); - - /* The document is well-formed; check IFD's for validity. */ - checkValidity(ifds, info); - } - - /** Allow odd offsets in values */ - public void setByteOffsetValid(boolean v) { - _byteOffsetIsValid = v; + } } - /** - * Special-purpose, limited parser for embedded Exif files. - * - * @param raf - * Open TIFF file - * @param info - * Representation information - */ - public final List exifParse(RandomAccessFile raf, RepInfo info) - throws IOException { - _raf = raf; - initParse(); - - List ifds = null; - boolean inHeader = true; // flag to aid reporting empty file error - try { - /* - * TIFF header is "II" (little-endian) or "MM" (big-endian), - * followed by the 16-bit integer value 42. - */ - raf.seek(0); - byte ch0 = _raf.readByte(); - byte ch1 = _raf.readByte(); - if (ch0 != ch1 || (ch0 != 0X49 && ch0 != 0X4D)) { - String mess = MessageFormat.format(MessageConstants.TIFF_HUL_22.getMessage(), (char) ch0, (char) ch1); - JhoveMessage message = JhoveMessages.getMessageInstance(MessageConstants.TIFF_HUL_22.getId(), mess); - throw new TiffException(message, 0); - } - _bigEndian = (ch0 == 0X4D); - - int magic = readUnsignedShort(_raf, _bigEndian); - if (magic != 42) { - String mess = MessageFormat.format(MessageConstants.TIFF_HUL_23.getMessage(), magic); - JhoveMessage message = JhoveMessages.getMessageInstance(MessageConstants.TIFF_HUL_23.getId(), mess); - throw new TiffException(message, 2); - } - inHeader = false; // There's SOMETHING in the file - - /* - * The offset of the first IFD is found at offset 4. The lowest - * recognized TIFF version is 4. Increment this as features specific - * to higher versions are recognized. - * The first IFD is supposed to be normal TIFF IFD, see CP3451C 4.5.2. - */ - _version = 4; - ifds = parseIFDs(4, info, true, IFD.TIFF); - - // info.setVersion (Integer.toString (_version) + ".0"); - - /* Construct IFDs property. */ - // List ifdsList = new LinkedList (); - - ListIterator iter = ifds.listIterator(); - while (iter.hasNext()) { - IFD ifd = iter.next(); - - /* - * Check if any messages were generated in constructing the - * property. If so, the IFD is invalid. - */ - - List errors = ifd.getErrors(); - if (!errors.isEmpty()) { - info.setValid(false); - ListIterator eter = errors.listIterator(); - while (eter.hasNext()) { - info.setMessage(new ErrorMessage(eter.next())); - } - } - - } - - } catch (TiffException e) { - // For parsing EXIF, we don't want to make the enclosing - // document invalid, so we don't declare the EXIF non-well-formed - // even though it is. - if (e.getJhoveMessage() != null) - info.setMessage(new InfoMessage(e.getJhoveMessage(), e.getOffset())); - else - info.setMessage(new InfoMessage(e.getMessage(), e.getOffset())); - return ifds; - } catch (IOException e) { - JhoveMessage msg; - if (inHeader) { - msg = MessageConstants.TIFF_HUL_70; - } else { - String mess = MessageFormat.format(MessageConstants.TIFF_HUL_69.getMessage(), e.getClass().getName()); - msg = JhoveMessages.getMessageInstance(MessageConstants.TIFF_HUL_69.getId(), mess); - } - info.setMessage(new ErrorMessage(msg)); - info.setWellFormed(false); - return null; + _raf = raf; + _logger.info("TiffModule parsing file"); + initParse(); + info.setModule(this); + info.setMimeType(_mimeType[0]); + info.setFormat(_format[0]); + + Property[] tiffMetadata = new Property[2]; + List ifds = null; + boolean inHeader = true; // Useful for catching empty files + try { + /* + * TIFF header is "II" (little-endian) or "MM" (big-endian), + * followed by the 16-bit integer value 42. + */ + raf.seek(0); + byte ch0 = _raf.readByte(); + byte ch1 = _raf.readByte(); + if (ch0 != ch1 || (ch0 != 0X49 && ch0 != 0X4D)) { + String mess = + MessageFormat.format(MessageConstants.TIFF_HUL_20.getMessage(), (char) ch0, (char) ch1); + JhoveMessage message = + JhoveMessages.getMessageInstance(MessageConstants.TIFF_HUL_20.getId(), mess); + throw new TiffException(message, 0); + } + inHeader = false; + + _bigEndian = (ch0 == 0X4D); + tiffMetadata[0] = + new Property( + "ByteOrder", PropertyType.STRING, _bigEndian ? "big-endian" : "little-endian"); + + int magic = readUnsignedShort(_raf, _bigEndian); + if (magic != 42) { + String mess = MessageFormat.format(MessageConstants.TIFF_HUL_21.getMessage(), magic); + JhoveMessage message = + JhoveMessages.getMessageInstance(MessageConstants.TIFF_HUL_21.getId(), mess); + throw new TiffException(message, 2); + } + + /* If we got this far, take note that the signature is OK. */ + info.setSigMatch(_name); + + /* + * The offset of the first IFD is found at offset 4. /* The lowest + * recognized TIFF version is 4. Increment this as features specific + * to higher versions are recognized. + */ + _version = 4; + ifds = parseIFDs(4, info); + + info.setVersion(Integer.toString(_version) + ".0"); + + /* Construct IFDs property. */ + List ifdsList = new LinkedList(); + Property ifdsProp = new Property("IFDs", PropertyType.PROPERTY, PropertyArity.LIST, ifdsList); + ifdsList.add(new Property("Number", PropertyType.INTEGER, new Integer(ifds.size()))); + + /* Build the IFD property list, for each of the IFDs. */ + ListIterator iter = ifds.listIterator(); + while (iter.hasNext()) { + IFD ifd = iter.next(); + ifdsList.add(ifd.getProperty(_je != null ? _je.getShowRawFlag() : false)); + + /* + * Check if any messages were generated in constructing the + * property. If so, the IFD is invalid. + */ + + List errors = ifd.getErrors(); + if (!errors.isEmpty()) { + info.setValid(false); + ListIterator eter = errors.listIterator(); + while (eter.hasNext()) { + info.setMessage(new ErrorMessage(eter.next())); + } } - // Return the IFD list. - return ifds; - } - - /****************************************************************** - * PRIVATE INSTANCE METHODS. - ******************************************************************/ - - /** - * Build list of profiles to check. Profile checking is, for the most part, - * done per IFD rather than per file. Exif profile checking is an exception, - * since it requires the coordination of multiple IFD's. Hence, the Exif - * profiles aren't added to the list, but treated elsewhere. - */ - protected void buildProfileList() { - _profile = new ArrayList(30); - _profile.add(new TiffProfileClassB()); - _profile.add(new TiffProfileClassG()); - _profile.add(new TiffProfileClassP()); - _profile.add(new TiffProfileClassR()); - _profile.add(new TiffProfileClassY()); - - _profile.add(new TiffProfileClassITBL()); - _profile.add(new TiffProfileClassITBLP1()); - _profile.add(new TiffProfileClassITBP()); - _profile.add(new TiffProfileClassITBPP1()); - _profile.add(new TiffProfileClassITBPP2()); - _profile.add(new TiffProfileClassITCT()); - _profile.add(new TiffProfileClassITCTP1()); - _profile.add(new TiffProfileClassITCTP2()); - _profile.add(new TiffProfileClassITFP()); - _profile.add(new TiffProfileClassITFPP1()); - _profile.add(new TiffProfileClassITFPP2()); - _profile.add(new TiffProfileClassITHC()); - _profile.add(new TiffProfileClassITHCP1()); - _profile.add(new TiffProfileClassITHCP2()); - _profile.add(new TiffProfileClassITLW()); - _profile.add(new TiffProfileClassITLWP1()); - _profile.add(new TiffProfileClassITLWP2()); - _profile.add(new TiffProfileClassITMP()); - _profile.add(new TiffProfileClassITMPP1()); - _profile.add(new TiffProfileClassITMPP2()); - _profile.add(new TiffProfileClassITSD()); - _profile.add(new TiffProfileClassITSDP2()); - - _profile.add(new TiffProfileEP()); - - _profile.add(new TiffProfileGeoTIFF()); - - _profile.add(new TiffProfileDLFBW()); - _profile.add(new TiffProfileDLFGray()); - _profile.add(new TiffProfileDLFColor()); - - _profile.add(new TiffProfileRFC1314()); - - // TIFF/FX profiles. - _profile.add(new TiffProfileFXS()); - _profile.add(new TiffProfileFXF()); - _profile.add(new TiffProfileFXJ()); - _profile.add(new TiffProfileFXL()); - _profile.add(new TiffProfileFXC()); - _profile.add(new TiffProfileFXM()); - - _exifMainProfile = new TiffProfileExif(); - _exifThumbnailProfile = new TiffProfileExifThumb(); - _dngMainProfile = new TiffProfileDNG(); - _dngThumbnailProfile = new TiffProfileDNGThumb(); - } + /* Check each IFD for profile conformance. */ - /** - * Go through all the IFD's, calling checkIFDValidity on each one that is a - * standard IFD. (Private IFD's have different requirements, and for the - * moment aren't checked here.) If any of them are invalid, set info's valid - * field to false. Validity problems are non-fatal, and more information is - * better, so we keep going with all IFDs even if we find problems. - */ - protected void checkValidity(List ifds, RepInfo info) { - _logger.info("TiffModule checking validity of IFDs"); - ListIterator iter = ifds.listIterator(); - while (iter.hasNext()) { - try { - IFD ifd = iter.next(); - if (ifd instanceof TiffIFD) { - checkValidity((TiffIFD) ifd, info); - } - } catch (TiffException e) { - if (e.getJhoveMessage() != null) { // try to keep the id - info.setMessage(new ErrorMessage(e.getJhoveMessage(), e.getOffset())); - } else { - info.setMessage(new ErrorMessage(e.getMessage(), e.getOffset())); - } - info.setValid(false); - } + ListIterator pter = _profile.listIterator(); + while (pter.hasNext()) { + TiffProfile prof = pter.next(); + if (!prof.isAlreadyOK() && prof.satisfiesProfile(ifd)) { + info.setProfile(prof.getText()); + } } - } - /** - * Check the validity of the IFD. - * - * @param ifd - * IFD - */ - protected void checkValidity(TiffIFD ifd, RepInfo info) - throws TiffException { - /* Required fields. */ - - NisoImageMetadata niso = ifd.getNisoImageMetadata(); - int photometricInterpretation = niso.getColorSpace(); - if (photometricInterpretation == NisoImageMetadata.NULL) { - reportInvalid(MessageConstants.TIFF_HUL_63, info); + /* + * Checking the Exif profile is more complicated, since several + * IFD's need to meet their respective requirements. Try + * something like this. + */ + if (ifd.isFirst()) { + _exifFirstFlag = _exifMainProfile.satisfiesProfile(ifd); + // We need to compare compression between the main and + // thumbnail IFD's. + _exifThumbnailProfile.setMainCompression( + ((TiffIFD) ifd).getNisoImageMetadata().getCompressionScheme()); + } else if (ifd.isThumbnail()) { + _exifThumbnailFlag = _exifThumbnailProfile.satisfiesProfile(ifd); } - long imageWidth = niso.getImageWidth(); - if (imageWidth == NisoImageMetadata.NULL) { - reportInvalid(MessageConstants.TIFF_HUL_62, info); + /* + * The DNG profile is similarly complex, requiring IFD 0 to + * satisfy the thumbnail part and some other IFD to satisfy the + * main profile. The spec doesn't actually say they can't be the + * same profile, so we allow for that possibility. + */ + if (ifd.isFirst()) { + _dngThumbnailFlag = _dngThumbnailProfile.satisfiesProfile(ifd); } - long imageLength = niso.getImageLength(); - if (imageLength == NisoImageMetadata.NULL) { - reportInvalid(MessageConstants.TIFF_HUL_64, info); + if (!_dngRawFlag) { + _dngRawFlag = _dngMainProfile.satisfiesProfile(ifd); } + } + tiffMetadata[1] = ifdsProp; + + /* + * The Exif profile requires coordinating several IFD checks, so we + * accumulate flags and then set the profile text if they're all + * set. + */ + if (_exifFirstFlag && _exifThumbnailFlag) { + info.setProfile(_exifMainProfile.getText()); + } + + /* Similarly for the DNG profile */ + if (_dngThumbnailFlag && _dngRawFlag) { + info.setProfile(_dngMainProfile.getText()); + } + + info.setProperty( + new Property("TIFFMetadata", PropertyType.PROPERTY, PropertyArity.ARRAY, tiffMetadata)); + } catch (TiffException e) { + if (e.getJhoveMessage() != null) { // try to keep the id + info.setMessage(new ErrorMessage(e.getJhoveMessage(), e.getOffset())); + } else { + info.setMessage(new ErrorMessage(e.getMessage(), e.getOffset())); + } + info.setWellFormed(false); + return; + } catch (IOException e) { + JhoveMessage msg; + if (inHeader) { + msg = MessageConstants.TIFF_HUL_67; + } else { + String mess = + MessageFormat.format(MessageConstants.TIFF_HUL_68.getMessage(), e.getClass().getName()); + msg = JhoveMessages.getMessageInstance(MessageConstants.TIFF_HUL_68.getId(), mess); + } + info.setMessage(new ErrorMessage(msg)); + info.setWellFormed(false); + return; + } - /* Strips and tiles. */ - - long[] stripOffsets = niso.getStripOffsets(); - long[] stripByteCounts = niso.getStripByteCounts(); - boolean stripsDefined = (stripOffsets != null || stripByteCounts != null); - - long tileWidth = niso.getTileWidth(); - long tileLength = niso.getTileLength(); - long[] tileOffsets = niso.getTileOffsets(); - long[] tileByteCounts = niso.getTileByteCounts(); - boolean tilesDefined = (tileWidth != NisoImageMetadata.NULL - || tileLength != NisoImageMetadata.NULL || tileOffsets != null || tileByteCounts != null); - - if (stripsDefined && tilesDefined) { - reportInvalid(MessageConstants.TIFF_HUL_24, info); - throw new TiffException(MessageConstants.TIFF_HUL_24); - } - if (!stripsDefined && !tilesDefined) { - reportInvalid(MessageConstants.TIFF_HUL_25, info); - throw new TiffException(MessageConstants.TIFF_HUL_25); + /* Object is well-formed TIFF. */ + + /* Calculate checksums, if necessary. */ + checksumIfRafNotCopied(info, raf); + + info.setMimeType(_mimeType[selectMimeTypeIndex()]); + + /* The document is well-formed; check IFD's for validity. */ + checkValidity(ifds, info); + } + + /** Allow odd offsets in values */ + public void setByteOffsetValid(boolean v) { + _byteOffsetIsValid = v; + } + + /** + * Special-purpose, limited parser for embedded Exif files. + * + * @param raf Open TIFF file + * @param info Representation information + */ + public final List exifParse(RandomAccessFile raf, RepInfo info) throws IOException { + _raf = raf; + initParse(); + + List ifds = null; + boolean inHeader = true; // flag to aid reporting empty file error + try { + /* + * TIFF header is "II" (little-endian) or "MM" (big-endian), + * followed by the 16-bit integer value 42. + */ + raf.seek(0); + byte ch0 = _raf.readByte(); + byte ch1 = _raf.readByte(); + if (ch0 != ch1 || (ch0 != 0X49 && ch0 != 0X4D)) { + String mess = + MessageFormat.format(MessageConstants.TIFF_HUL_22.getMessage(), (char) ch0, (char) ch1); + JhoveMessage message = + JhoveMessages.getMessageInstance(MessageConstants.TIFF_HUL_22.getId(), mess); + throw new TiffException(message, 0); + } + _bigEndian = (ch0 == 0X4D); + + int magic = readUnsignedShort(_raf, _bigEndian); + if (magic != 42) { + String mess = MessageFormat.format(MessageConstants.TIFF_HUL_23.getMessage(), magic); + JhoveMessage message = + JhoveMessages.getMessageInstance(MessageConstants.TIFF_HUL_23.getId(), mess); + throw new TiffException(message, 2); + } + inHeader = false; // There's SOMETHING in the file + + /* + * The offset of the first IFD is found at offset 4. The lowest + * recognized TIFF version is 4. Increment this as features specific + * to higher versions are recognized. + * The first IFD is supposed to be normal TIFF IFD, see CP3451C 4.5.2. + */ + _version = 4; + ifds = parseIFDs(4, info, true, IFD.TIFF); + + // info.setVersion (Integer.toString (_version) + ".0"); + + /* Construct IFDs property. */ + // List ifdsList = new LinkedList (); + + ListIterator iter = ifds.listIterator(); + while (iter.hasNext()) { + IFD ifd = iter.next(); + + /* + * Check if any messages were generated in constructing the + * property. If so, the IFD is invalid. + */ + + List errors = ifd.getErrors(); + if (!errors.isEmpty()) { + info.setValid(false); + ListIterator eter = errors.listIterator(); + while (eter.hasNext()) { + info.setMessage(new ErrorMessage(eter.next())); + } } + } + + } catch (TiffException e) { + // For parsing EXIF, we don't want to make the enclosing + // document invalid, so we don't declare the EXIF non-well-formed + // even though it is. + if (e.getJhoveMessage() != null) + info.setMessage(new InfoMessage(e.getJhoveMessage(), e.getOffset())); + else info.setMessage(new InfoMessage(e.getMessage(), e.getOffset())); + return ifds; + } catch (IOException e) { + JhoveMessage msg; + if (inHeader) { + msg = MessageConstants.TIFF_HUL_70; + } else { + String mess = + MessageFormat.format(MessageConstants.TIFF_HUL_69.getMessage(), e.getClass().getName()); + msg = JhoveMessages.getMessageInstance(MessageConstants.TIFF_HUL_69.getId(), mess); + } + info.setMessage(new ErrorMessage(msg)); + info.setWellFormed(false); + return null; + } - int planarConfiguration = niso.getPlanarConfiguration(); - int samplesPerPixel = niso.getSamplesPerPixel(); - - if (stripsDefined) { - if (stripOffsets == null) { - reportInvalid(MessageConstants.TIFF_HUL_26, info); - throw new TiffException(MessageConstants.TIFF_HUL_26); - } - if (stripByteCounts == null) { - reportInvalid(MessageConstants.TIFF_HUL_27, info); - throw new TiffException(MessageConstants.TIFF_HUL_27); - } - - int len = stripOffsets.length; - if (len != stripByteCounts.length) { - String mess = MessageFormat.format(MessageConstants.TIFF_HUL_28.getMessage(), len, stripByteCounts.length); - JhoveMessage message = JhoveMessages.getMessageInstance(MessageConstants.TIFF_HUL_28.getId(), mess); - reportInvalid(message, info); - } - /* Check that all the strips are located within the file */ - try { - long fileLength = _raf.length(); - for (int i = 0; i < len; i++) { - long offset = stripOffsets[i]; - long count = stripByteCounts[i]; - if (offset + count > fileLength) { - reportInvalid(MessageConstants.TIFF_HUL_29, info); - } - } - } catch (IOException e) { - } + // Return the IFD list. + return ifds; + } + + /** + * **************************************************************** PRIVATE INSTANCE METHODS. + * **************************************************************** + */ + + /** + * Build list of profiles to check. Profile checking is, for the most part, done per IFD rather + * than per file. Exif profile checking is an exception, since it requires the coordination of + * multiple IFD's. Hence, the Exif profiles aren't added to the list, but treated elsewhere. + */ + protected void buildProfileList() { + _profile = new ArrayList(30); + _profile.add(new TiffProfileClassB()); + _profile.add(new TiffProfileClassG()); + _profile.add(new TiffProfileClassP()); + _profile.add(new TiffProfileClassR()); + _profile.add(new TiffProfileClassY()); + + _profile.add(new TiffProfileClassITBL()); + _profile.add(new TiffProfileClassITBLP1()); + _profile.add(new TiffProfileClassITBP()); + _profile.add(new TiffProfileClassITBPP1()); + _profile.add(new TiffProfileClassITBPP2()); + _profile.add(new TiffProfileClassITCT()); + _profile.add(new TiffProfileClassITCTP1()); + _profile.add(new TiffProfileClassITCTP2()); + _profile.add(new TiffProfileClassITFP()); + _profile.add(new TiffProfileClassITFPP1()); + _profile.add(new TiffProfileClassITFPP2()); + _profile.add(new TiffProfileClassITHC()); + _profile.add(new TiffProfileClassITHCP1()); + _profile.add(new TiffProfileClassITHCP2()); + _profile.add(new TiffProfileClassITLW()); + _profile.add(new TiffProfileClassITLWP1()); + _profile.add(new TiffProfileClassITLWP2()); + _profile.add(new TiffProfileClassITMP()); + _profile.add(new TiffProfileClassITMPP1()); + _profile.add(new TiffProfileClassITMPP2()); + _profile.add(new TiffProfileClassITSD()); + _profile.add(new TiffProfileClassITSDP2()); + + _profile.add(new TiffProfileEP()); + + _profile.add(new TiffProfileGeoTIFF()); + + _profile.add(new TiffProfileDLFBW()); + _profile.add(new TiffProfileDLFGray()); + _profile.add(new TiffProfileDLFColor()); + + _profile.add(new TiffProfileRFC1314()); + + // TIFF/FX profiles. + _profile.add(new TiffProfileFXS()); + _profile.add(new TiffProfileFXF()); + _profile.add(new TiffProfileFXJ()); + _profile.add(new TiffProfileFXL()); + _profile.add(new TiffProfileFXC()); + _profile.add(new TiffProfileFXM()); + + _exifMainProfile = new TiffProfileExif(); + _exifThumbnailProfile = new TiffProfileExifThumb(); + _dngMainProfile = new TiffProfileDNG(); + _dngThumbnailProfile = new TiffProfileDNGThumb(); + } + + /** + * Go through all the IFD's, calling checkIFDValidity on each one that is a standard IFD. (Private + * IFD's have different requirements, and for the moment aren't checked here.) If any of them are + * invalid, set info's valid field to false. Validity problems are non-fatal, and more information + * is better, so we keep going with all IFDs even if we find problems. + */ + protected void checkValidity(List ifds, RepInfo info) { + _logger.info("TiffModule checking validity of IFDs"); + ListIterator iter = ifds.listIterator(); + while (iter.hasNext()) { + try { + IFD ifd = iter.next(); + if (ifd instanceof TiffIFD) { + checkValidity((TiffIFD) ifd, info); } - - if (tilesDefined) { - if (tileWidth == NisoImageMetadata.NULL) { - reportInvalid(MessageConstants.TIFF_HUL_30, info); - } - if (tileLength == NisoImageMetadata.NULL) { - reportInvalid(MessageConstants.TIFF_HUL_31, info); - } - if (tileOffsets == null) { - reportInvalid(MessageConstants.TIFF_HUL_32, info); - } - if (tileByteCounts == null) { - reportInvalid(MessageConstants.TIFF_HUL_33, info); - } - - if (tileWidth % 16 > 0) { - String mess = MessageFormat.format(MessageConstants.TIFF_HUL_34.getMessage(), tileWidth); - JhoveMessage message = JhoveMessages.getMessageInstance(MessageConstants.TIFF_HUL_34.getId(), mess); - reportInvalid(message, info); - } - if (tileLength % 16 > 0) { - String mess = MessageFormat.format(MessageConstants.TIFF_HUL_35.getMessage(), tileLength); - JhoveMessage message = JhoveMessages.getMessageInstance(MessageConstants.TIFF_HUL_35.getId(), mess); - reportInvalid(message, info); - } - - long tilesPerImage = ((imageWidth + tileWidth - 1) / tileWidth) - * ((imageLength + tileLength - 1) / tileLength); - if (planarConfiguration == 2) { - long spp_tpi = samplesPerPixel * tilesPerImage; - if (tileOffsets != null && tileOffsets.length < spp_tpi) { - String mess = MessageFormat.format(MessageConstants.TIFF_HUL_36.getMessage(), tileOffsets.length, spp_tpi); - JhoveMessage message = JhoveMessages.getMessageInstance(MessageConstants.TIFF_HUL_36.getId(), mess); - reportInvalid(message, info); - } - if (tileByteCounts != null && tileByteCounts.length < spp_tpi) { - String mess = MessageFormat.format(MessageConstants.TIFF_HUL_37.getMessage(), tileByteCounts.length, spp_tpi); - JhoveMessage message = JhoveMessages.getMessageInstance(MessageConstants.TIFF_HUL_37.getId(), mess); - reportInvalid(message, info); - } - } else { - if (tileOffsets != null && tileOffsets.length < tilesPerImage) { - String mess = MessageFormat.format(MessageConstants.TIFF_HUL_38.getMessage(), tileOffsets.length, tilesPerImage); - JhoveMessage message = JhoveMessages.getMessageInstance(MessageConstants.TIFF_HUL_38.getId(), mess); - reportInvalid(message, info); - } - if (tileByteCounts != null - && tileByteCounts.length < tilesPerImage) { - String mess = MessageFormat.format(MessageConstants.TIFF_HUL_39.getMessage(), tileByteCounts.length, tilesPerImage); - JhoveMessage message = JhoveMessages.getMessageInstance(MessageConstants.TIFF_HUL_39.getId(), mess); - reportInvalid(message, info); - } - } + } catch (TiffException e) { + if (e.getJhoveMessage() != null) { // try to keep the id + info.setMessage(new ErrorMessage(e.getJhoveMessage(), e.getOffset())); + } else { + info.setMessage(new ErrorMessage(e.getMessage(), e.getOffset())); } + info.setValid(false); + } + } + } + + /** + * Check the validity of the IFD. + * + * @param ifd IFD + */ + protected void checkValidity(TiffIFD ifd, RepInfo info) throws TiffException { + /* Required fields. */ + + NisoImageMetadata niso = ifd.getNisoImageMetadata(); + int photometricInterpretation = niso.getColorSpace(); + if (photometricInterpretation == NisoImageMetadata.NULL) { + reportInvalid(MessageConstants.TIFF_HUL_63, info); + } + long imageWidth = niso.getImageWidth(); + if (imageWidth == NisoImageMetadata.NULL) { + reportInvalid(MessageConstants.TIFF_HUL_62, info); + } + long imageLength = niso.getImageLength(); + if (imageLength == NisoImageMetadata.NULL) { + reportInvalid(MessageConstants.TIFF_HUL_64, info); + } - /* Transparency mask. */ + /* Strips and tiles. */ + + long[] stripOffsets = niso.getStripOffsets(); + long[] stripByteCounts = niso.getStripByteCounts(); + boolean stripsDefined = (stripOffsets != null || stripByteCounts != null); + + long tileWidth = niso.getTileWidth(); + long tileLength = niso.getTileLength(); + long[] tileOffsets = niso.getTileOffsets(); + long[] tileByteCounts = niso.getTileByteCounts(); + boolean tilesDefined = + (tileWidth != NisoImageMetadata.NULL + || tileLength != NisoImageMetadata.NULL + || tileOffsets != null + || tileByteCounts != null); + + if (stripsDefined && tilesDefined) { + reportInvalid(MessageConstants.TIFF_HUL_24, info); + throw new TiffException(MessageConstants.TIFF_HUL_24); + } + if (!stripsDefined && !tilesDefined) { + reportInvalid(MessageConstants.TIFF_HUL_25, info); + throw new TiffException(MessageConstants.TIFF_HUL_25); + } - int newSubfileType = (int) ifd.getNewSubfileType(); - if ((photometricInterpretation == 4 && (newSubfileType & 4) == 0) - || (photometricInterpretation != 4 && (newSubfileType & 4) != 0)) { - reportInvalid(MessageConstants.TIFF_HUL_40, - info); - } - int[] bitsPerSample = niso.getBitsPerSample(); - if (photometricInterpretation == 4 && (samplesPerPixel < 1 || bitsPerSample[0] != 1)) { - reportInvalid(MessageConstants.TIFF_HUL_41, info); + int planarConfiguration = niso.getPlanarConfiguration(); + int samplesPerPixel = niso.getSamplesPerPixel(); + + if (stripsDefined) { + if (stripOffsets == null) { + reportInvalid(MessageConstants.TIFF_HUL_26, info); + throw new TiffException(MessageConstants.TIFF_HUL_26); + } + if (stripByteCounts == null) { + reportInvalid(MessageConstants.TIFF_HUL_27, info); + throw new TiffException(MessageConstants.TIFF_HUL_27); + } + + int len = stripOffsets.length; + if (len != stripByteCounts.length) { + String mess = + MessageFormat.format( + MessageConstants.TIFF_HUL_28.getMessage(), len, stripByteCounts.length); + JhoveMessage message = + JhoveMessages.getMessageInstance(MessageConstants.TIFF_HUL_28.getId(), mess); + reportInvalid(message, info); + } + /* Check that all the strips are located within the file */ + try { + long fileLength = _raf.length(); + for (int i = 0; i < len; i++) { + long offset = stripOffsets[i]; + long count = stripByteCounts[i]; + if (offset + count > fileLength) { + reportInvalid(MessageConstants.TIFF_HUL_29, info); + } } + } catch (IOException e) { + } + } - /* Samples per pixel. */ - - if ((photometricInterpretation == 0 || photometricInterpretation == 1 - || photometricInterpretation == 3 - || photometricInterpretation == 4) && samplesPerPixel < 1) { - String mess = MessageFormat.format(MessageConstants.TIFF_HUL_42.getMessage(), samplesPerPixel); - JhoveMessage message = JhoveMessages.getMessageInstance(MessageConstants.TIFF_HUL_42.getId(), mess); - reportInvalid(message, info); + if (tilesDefined) { + if (tileWidth == NisoImageMetadata.NULL) { + reportInvalid(MessageConstants.TIFF_HUL_30, info); + } + if (tileLength == NisoImageMetadata.NULL) { + reportInvalid(MessageConstants.TIFF_HUL_31, info); + } + if (tileOffsets == null) { + reportInvalid(MessageConstants.TIFF_HUL_32, info); + } + if (tileByteCounts == null) { + reportInvalid(MessageConstants.TIFF_HUL_33, info); + } + + if (tileWidth % 16 > 0) { + String mess = MessageFormat.format(MessageConstants.TIFF_HUL_34.getMessage(), tileWidth); + JhoveMessage message = + JhoveMessages.getMessageInstance(MessageConstants.TIFF_HUL_34.getId(), mess); + reportInvalid(message, info); + } + if (tileLength % 16 > 0) { + String mess = MessageFormat.format(MessageConstants.TIFF_HUL_35.getMessage(), tileLength); + JhoveMessage message = + JhoveMessages.getMessageInstance(MessageConstants.TIFF_HUL_35.getId(), mess); + reportInvalid(message, info); + } + + long tilesPerImage = + ((imageWidth + tileWidth - 1) / tileWidth) + * ((imageLength + tileLength - 1) / tileLength); + if (planarConfiguration == 2) { + long spp_tpi = samplesPerPixel * tilesPerImage; + if (tileOffsets != null && tileOffsets.length < spp_tpi) { + String mess = + MessageFormat.format( + MessageConstants.TIFF_HUL_36.getMessage(), tileOffsets.length, spp_tpi); + JhoveMessage message = + JhoveMessages.getMessageInstance(MessageConstants.TIFF_HUL_36.getId(), mess); + reportInvalid(message, info); } - if ((photometricInterpretation == 2 || photometricInterpretation == 6 - || photometricInterpretation == 8) && samplesPerPixel < 3) { - String mess = MessageFormat.format(MessageConstants.TIFF_HUL_43.getMessage(), samplesPerPixel); - JhoveMessage message = JhoveMessages.getMessageInstance(MessageConstants.TIFF_HUL_43.getId(), mess); - reportInvalid(message, info); + if (tileByteCounts != null && tileByteCounts.length < spp_tpi) { + String mess = + MessageFormat.format( + MessageConstants.TIFF_HUL_37.getMessage(), tileByteCounts.length, spp_tpi); + JhoveMessage message = + JhoveMessages.getMessageInstance(MessageConstants.TIFF_HUL_37.getId(), mess); + reportInvalid(message, info); } - - /* Palette color. */ - - if (photometricInterpretation == 3) { - int[] colormapBitCodeValue = niso.getColormapBitCodeValue(); - int[] colormapRedValue = niso.getColormapRedValue(); - int[] colormapGreenValue = niso.getColormapGreenValue(); - int[] colormapBlueValue = niso.getColormapBlueValue(); - if (colormapBitCodeValue == null || colormapRedValue == null - || colormapGreenValue == null || colormapBlueValue == null) { - reportInvalid(MessageConstants.TIFF_HUL_44, - info); - } - if (samplesPerPixel != 1) { - String mess = MessageFormat.format(MessageConstants.TIFF_HUL_45.getMessage(), samplesPerPixel); - JhoveMessage message = JhoveMessages.getMessageInstance(MessageConstants.TIFF_HUL_45.getId(), mess); - reportInvalid(message, info); - } - int len = (1 << bitsPerSample[0]); - if (colormapBitCodeValue.length < len) { - String mess = MessageFormat.format(MessageConstants.TIFF_HUL_46.getMessage(), colormapBitCodeValue.length, len); - JhoveMessage message = JhoveMessages.getMessageInstance(MessageConstants.TIFF_HUL_46.getId(), mess); - reportInvalid(message, info); - } + } else { + if (tileOffsets != null && tileOffsets.length < tilesPerImage) { + String mess = + MessageFormat.format( + MessageConstants.TIFF_HUL_38.getMessage(), tileOffsets.length, tilesPerImage); + JhoveMessage message = + JhoveMessages.getMessageInstance(MessageConstants.TIFF_HUL_38.getId(), mess); + reportInvalid(message, info); } - - /* Cells. */ - - if (ifd.getCellLength() != IFD.NULL && ifd.getThreshholding() != 2) { - reportInvalid(MessageConstants.TIFF_HUL_47, info); - } - - /* Dot range. */ - - int[] dotRange = ifd.getDotRange(); - if (dotRange != null && bitsPerSample != null) { - int sampleMax = 1 << bitsPerSample[0]; - if (dotRange.length < 2 || dotRange[0] >= sampleMax - || dotRange[1] >= sampleMax) { - reportInvalid(MessageConstants.TIFF_HUL_48, info); - } + if (tileByteCounts != null && tileByteCounts.length < tilesPerImage) { + String mess = + MessageFormat.format( + MessageConstants.TIFF_HUL_39.getMessage(), tileByteCounts.length, tilesPerImage); + JhoveMessage message = + JhoveMessages.getMessageInstance(MessageConstants.TIFF_HUL_39.getId(), mess); + reportInvalid(message, info); } + } + } - /* JPEG. */ + /* Transparency mask. */ - if (niso.getCompressionScheme() == 6 && ifd.getJPEGProc() == IFD.NULL) { - reportInvalid(MessageConstants.TIFF_HUL_49, - info); - } + int newSubfileType = (int) ifd.getNewSubfileType(); + if ((photometricInterpretation == 4 && (newSubfileType & 4) == 0) + || (photometricInterpretation != 4 && (newSubfileType & 4) != 0)) { + reportInvalid(MessageConstants.TIFF_HUL_40, info); + } + int[] bitsPerSample = niso.getBitsPerSample(); + if (photometricInterpretation == 4 && (samplesPerPixel < 1 || bitsPerSample[0] != 1)) { + reportInvalid(MessageConstants.TIFF_HUL_41, info); + } - /* CIE L*a*b*. */ - - if (photometricInterpretation == 8 || photometricInterpretation == 9) { - int len = 0; - int[] xs = niso.getExtraSamples(); - if (xs != null) { - len = niso.getExtraSamples().length; - } - int in = samplesPerPixel - len; - if (in != 1 && in != 3) { - String mess = MessageFormat.format(MessageConstants.TIFF_HUL_50.getMessage(), samplesPerPixel, len); - JhoveMessage message = JhoveMessages.getMessageInstance(MessageConstants.TIFF_HUL_50.getId(), mess); - reportInvalid(message, info); - } - for (int i = 0; i < bitsPerSample.length; i++) { - if (bitsPerSample[i] != 8 && bitsPerSample[i] != 16) { - reportInvalid(MessageConstants.TIFF_HUL_51, info); - } - } - } + /* Samples per pixel. */ + + if ((photometricInterpretation == 0 + || photometricInterpretation == 1 + || photometricInterpretation == 3 + || photometricInterpretation == 4) + && samplesPerPixel < 1) { + String mess = + MessageFormat.format(MessageConstants.TIFF_HUL_42.getMessage(), samplesPerPixel); + JhoveMessage message = + JhoveMessages.getMessageInstance(MessageConstants.TIFF_HUL_42.getId(), mess); + reportInvalid(message, info); + } + if ((photometricInterpretation == 2 + || photometricInterpretation == 6 + || photometricInterpretation == 8) + && samplesPerPixel < 3) { + String mess = + MessageFormat.format(MessageConstants.TIFF_HUL_43.getMessage(), samplesPerPixel); + JhoveMessage message = + JhoveMessages.getMessageInstance(MessageConstants.TIFF_HUL_43.getId(), mess); + reportInvalid(message, info); + } - /* Clipping path. */ + /* Palette color. */ + + if (photometricInterpretation == 3) { + int[] colormapBitCodeValue = niso.getColormapBitCodeValue(); + int[] colormapRedValue = niso.getColormapRedValue(); + int[] colormapGreenValue = niso.getColormapGreenValue(); + int[] colormapBlueValue = niso.getColormapBlueValue(); + if (colormapBitCodeValue == null + || colormapRedValue == null + || colormapGreenValue == null + || colormapBlueValue == null) { + reportInvalid(MessageConstants.TIFF_HUL_44, info); + } + if (samplesPerPixel != 1) { + String mess = + MessageFormat.format(MessageConstants.TIFF_HUL_45.getMessage(), samplesPerPixel); + JhoveMessage message = + JhoveMessages.getMessageInstance(MessageConstants.TIFF_HUL_45.getId(), mess); + reportInvalid(message, info); + } + int len = (1 << bitsPerSample[0]); + if (colormapBitCodeValue.length < len) { + String mess = + MessageFormat.format( + MessageConstants.TIFF_HUL_46.getMessage(), colormapBitCodeValue.length, len); + JhoveMessage message = + JhoveMessages.getMessageInstance(MessageConstants.TIFF_HUL_46.getId(), mess); + reportInvalid(message, info); + } + } - if (ifd.getClipPath() != null && ifd.getXClipPathUnits() == IFD.NULL) { - reportInvalid(MessageConstants.TIFF_HUL_52, - info); - } + /* Cells. */ - /* Date. */ - - String dateTime = ifd.getDateTime(); - if (dateTime != null) { - if (dateTime.length() != 19) { - String mess = MessageFormat.format(MessageConstants.TIFF_HUL_53.getMessage(), dateTime); - JhoveMessage message = JhoveMessages.getMessageInstance(MessageConstants.TIFF_HUL_53.getId(), mess); - reportInvalid(message, info); - return; - } - if (dateTime.charAt(4) != ':' || dateTime.charAt(7) != ':' - || dateTime.charAt(10) != ' ' || dateTime.charAt(13) != ':' - || dateTime.charAt(16) != ':') { - String mess = MessageFormat.format(MessageConstants.TIFF_HUL_54.getMessage(), dateTime); - JhoveMessage message = JhoveMessages.getMessageInstance(MessageConstants.TIFF_HUL_54.getId(), mess); - reportInvalid(message, info); - return; - } - try { - int yyyy = Integer.parseInt(dateTime.substring(0, 4)); - int mm = Integer.parseInt(dateTime.substring(5, 7)); - int dd = Integer.parseInt(dateTime.substring(8, 10)); - int hh = Integer.parseInt(dateTime.substring(11, 13)); - int mn = Integer.parseInt(dateTime.substring(14, 16)); - int ss = Integer.parseInt(dateTime.substring(17)); - if (yyyy < 0 || yyyy > 9999 || mm < 1 || mm > 12 || dd < 1 - || dd > 31 || hh < 0 || hh > 24 || mn < 0 || mn > 59 - || ss < 0 || mn > 59) { - String mess = MessageFormat.format(MessageConstants.TIFF_HUL_55.getMessage(), dateTime); - JhoveMessage message = JhoveMessages.getMessageInstance(MessageConstants.TIFF_HUL_55.getId(), mess); - reportInvalid(message, info); - } - } catch (Exception e) { - String mess = MessageFormat.format(MessageConstants.TIFF_HUL_56.getMessage(), dateTime); - JhoveMessage message = JhoveMessages.getMessageInstance(MessageConstants.TIFF_HUL_56.getId(), mess); - reportInvalid(message, info); - } - } + if (ifd.getCellLength() != IFD.NULL && ifd.getThreshholding() != 2) { + reportInvalid(MessageConstants.TIFF_HUL_47, info); } - /** Report an instance of invalidity. */ - protected void reportInvalid(final JhoveMessage message, final RepInfo info) { - info.setMessage(new ErrorMessage(message)); - info.setValid(false); + /* Dot range. */ + int[] dotRange = ifd.getDotRange(); + if (dotRange != null && bitsPerSample != null) { + int sampleMax = 1 << bitsPerSample[0]; + if (dotRange.length < 2 || dotRange[0] >= sampleMax || dotRange[1] >= sampleMax) { + reportInvalid(MessageConstants.TIFF_HUL_48, info); + } } - /** - * Parse all IFDs in the file, accumulating representation information. - * - * @param offset - * Starting byte offset - * @param info - * Representation information - */ - protected List parseIFDs(long offset, RepInfo info) - throws TiffException { - return parseIFDs(offset, info, false, IFD.TIFF); - } + /* JPEG. */ - /** - * Parse all IFDs in the file, accumulating representation information. - * - * @param offset - * Starting byte offset - * @param info - * Representation information - * @param suppressErrors - * If true, use IFD even if it has errors - */ - protected List parseIFDs(long offset, RepInfo info, - boolean suppressErrors, int ifdType) throws TiffException { - long next = 0L; - try { - _raf.seek(offset); - next = readUnsignedInt(_raf, _bigEndian); - } catch (IOException e) { - throw new TiffException(MessageConstants.TIFF_HUL_57, offset); - } + if (niso.getCompressionScheme() == 6 && ifd.getJPEGProc() == IFD.NULL) { + reportInvalid(MessageConstants.TIFF_HUL_49, info); + } - if (next == 0L) { - throw new TiffException(MessageConstants.TIFF_HUL_58, offset); + /* CIE L*a*b*. */ + + if (photometricInterpretation == 8 || photometricInterpretation == 9) { + int len = 0; + int[] xs = niso.getExtraSamples(); + if (xs != null) { + len = niso.getExtraSamples().length; + } + int in = samplesPerPixel - len; + if (in != 1 && in != 3) { + String mess = + MessageFormat.format(MessageConstants.TIFF_HUL_50.getMessage(), samplesPerPixel, len); + JhoveMessage message = + JhoveMessages.getMessageInstance(MessageConstants.TIFF_HUL_50.getId(), mess); + reportInvalid(message, info); + } + for (int i = 0; i < bitsPerSample.length; i++) { + if (bitsPerSample[i] != 8 && bitsPerSample[i] != 16) { + reportInvalid(MessageConstants.TIFF_HUL_51, info); } + } + } - List list = new LinkedList(); - while (next != 0L) { - if ((next & 1) != 0) { - String mess = MessageFormat.format(MessageConstants.TIFF_HUL_59.getMessage(), next); - JhoveMessage message = JhoveMessages.getMessageInstance(MessageConstants.TIFF_HUL_59.getId(), mess); - throw new TiffException(message); - } - if (list.size() > 50) { - throw new TiffException(MessageConstants.TIFF_HUL_60); - } - _logger.info("Parsing next IFD at offset " + next); - IFD ifd = parseIFDChain(next, info, ifdType, list, suppressErrors); - next = ifd.getNext(); - } + /* Clipping path. */ - return list; + if (ifd.getClipPath() != null && ifd.getXClipPathUnits() == IFD.NULL) { + reportInvalid(MessageConstants.TIFF_HUL_52, info); } - protected IFD parseIFDChain(long next, RepInfo info, int type, - List list, boolean suppressErrors) throws TiffException { - IFD ifd = null; - switch (type) { - case IFD.EXIF: - ifd = new ExifIFD(next, info, _raf, _bigEndian); - break; - case IFD.INTEROPERABILITY: - ifd = new InteroperabilityIFD(next, info, _raf, _bigEndian); - break; - case IFD.GPSINFO: - ifd = new GPSInfoIFD(next, info, _raf, _bigEndian); - break; - case IFD.GLOBALPARAMETERS: - ifd = new GlobalParametersIFD(next, info, _raf, _bigEndian); - break; - default: - ifd = new TiffIFD(next, info, _raf, _bigEndian); + /* Date. */ + + String dateTime = ifd.getDateTime(); + if (dateTime != null) { + if (dateTime.length() != 19) { + String mess = MessageFormat.format(MessageConstants.TIFF_HUL_53.getMessage(), dateTime); + JhoveMessage message = + JhoveMessages.getMessageInstance(MessageConstants.TIFF_HUL_53.getId(), mess); + reportInvalid(message, info); + return; + } + if (dateTime.charAt(4) != ':' + || dateTime.charAt(7) != ':' + || dateTime.charAt(10) != ' ' + || dateTime.charAt(13) != ':' + || dateTime.charAt(16) != ':') { + String mess = MessageFormat.format(MessageConstants.TIFF_HUL_54.getMessage(), dateTime); + JhoveMessage message = + JhoveMessages.getMessageInstance(MessageConstants.TIFF_HUL_54.getId(), mess); + reportInvalid(message, info); + return; + } + try { + int yyyy = Integer.parseInt(dateTime.substring(0, 4)); + int mm = Integer.parseInt(dateTime.substring(5, 7)); + int dd = Integer.parseInt(dateTime.substring(8, 10)); + int hh = Integer.parseInt(dateTime.substring(11, 13)); + int mn = Integer.parseInt(dateTime.substring(14, 16)); + int ss = Integer.parseInt(dateTime.substring(17)); + if (yyyy < 0 + || yyyy > 9999 + || mm < 1 + || mm > 12 + || dd < 1 + || dd > 31 + || hh < 0 + || hh > 24 + || mn < 0 + || mn > 59 + || ss < 0 + || mn > 59) { + String mess = MessageFormat.format(MessageConstants.TIFF_HUL_55.getMessage(), dateTime); + JhoveMessage message = + JhoveMessages.getMessageInstance(MessageConstants.TIFF_HUL_55.getId(), mess); + reportInvalid(message, info); } - ifd.parse(_byteOffsetIsValid, suppressErrors); + } catch (Exception e) { + String mess = MessageFormat.format(MessageConstants.TIFF_HUL_56.getMessage(), dateTime); + JhoveMessage message = + JhoveMessages.getMessageInstance(MessageConstants.TIFF_HUL_56.getId(), mess); + reportInvalid(message, info); + } + } + } + + /** Report an instance of invalidity. */ + protected void reportInvalid(final JhoveMessage message, final RepInfo info) { + info.setMessage(new ErrorMessage(message)); + info.setValid(false); + } + + /** + * Parse all IFDs in the file, accumulating representation information. + * + * @param offset Starting byte offset + * @param info Representation information + */ + protected List parseIFDs(long offset, RepInfo info) throws TiffException { + return parseIFDs(offset, info, false, IFD.TIFF); + } + + /** + * Parse all IFDs in the file, accumulating representation information. + * + * @param offset Starting byte offset + * @param info Representation information + * @param suppressErrors If true, use IFD even if it has errors + */ + protected List parseIFDs(long offset, RepInfo info, boolean suppressErrors, int ifdType) + throws TiffException { + long next = 0L; + try { + _raf.seek(offset); + next = readUnsignedInt(_raf, _bigEndian); + } catch (IOException e) { + throw new TiffException(MessageConstants.TIFF_HUL_57, offset); + } - /* Update the TIFF version number. */ - int version = ifd.getVersion(); - if (version > _version) { - _version = version; - } + if (next == 0L) { + throw new TiffException(MessageConstants.TIFF_HUL_58, offset); + } - if (list.isEmpty() && type == IFD.TIFF) { - ifd.setFirst(true); - } else if (list.size() == 1 && type == IFD.TIFF) { - // For some profiles, the second IFD is assumed to - // be the thumbnail. This may not be valid under - // all circumstances. - ifd.setThumbnail(true); - } - list.add(ifd); + List list = new LinkedList(); + while (next != 0L) { + if ((next & 1) != 0) { + String mess = MessageFormat.format(MessageConstants.TIFF_HUL_59.getMessage(), next); + JhoveMessage message = + JhoveMessages.getMessageInstance(MessageConstants.TIFF_HUL_59.getId(), mess); + throw new TiffException(message); + } + if (list.size() > 50) { + throw new TiffException(MessageConstants.TIFF_HUL_60); + } + _logger.info("Parsing next IFD at offset " + next); + IFD ifd = parseIFDChain(next, info, ifdType, list, suppressErrors); + next = ifd.getNext(); + } - if (ifd instanceof TiffIFD) { - TiffIFD tifd = (TiffIFD) ifd; - - long[] subIFDs = tifd.getSubIFDs(); - if (subIFDs != null) { - for (int i = 0; i < subIFDs.length; i++) { - next = subIFDs[i]; - while (next != 0) { - IFD sub = parseIFDChain(next, info, IFD.TIFF, list, - suppressErrors); - next = sub.getNext(); - } - } - } - - long offset = tifd.getExifIFD(); - if (offset != IFD.NULL) { - IFD ex = parseIFDChain(offset, info, IFD.EXIF, list, - suppressErrors); - tifd.setTheExifIFD((ExifIFD) ex); - } - if ((offset = tifd.getGPSInfoIFD()) != IFD.NULL) { - IFD gp = parseIFDChain(offset, info, IFD.GPSINFO, list, - suppressErrors); - tifd.setTheGPSInfoIFD((GPSInfoIFD) gp); - } - if ((offset = tifd.getInteroperabilityIFD()) != IFD.NULL) { - IFD io = parseIFDChain(offset, info, IFD.INTEROPERABILITY, - list, suppressErrors); - tifd.setTheInteroperabilityIFD((InteroperabilityIFD) io); - } - if ((offset = tifd.getGlobalParametersIFD()) != IFD.NULL) { - IFD io = parseIFDChain(offset, info, IFD.GLOBALPARAMETERS, - list, suppressErrors); - tifd.setTheGlobalParametersIFD((GlobalParametersIFD) io); - } - } + return list; + } + + protected IFD parseIFDChain( + long next, RepInfo info, int type, List list, boolean suppressErrors) + throws TiffException { + IFD ifd = null; + switch (type) { + case IFD.EXIF: + ifd = new ExifIFD(next, info, _raf, _bigEndian); + break; + case IFD.INTEROPERABILITY: + ifd = new InteroperabilityIFD(next, info, _raf, _bigEndian); + break; + case IFD.GPSINFO: + ifd = new GPSInfoIFD(next, info, _raf, _bigEndian); + break; + case IFD.GLOBALPARAMETERS: + ifd = new GlobalParametersIFD(next, info, _raf, _bigEndian); + break; + default: + ifd = new TiffIFD(next, info, _raf, _bigEndian); + } + ifd.parse(_byteOffsetIsValid, suppressErrors); - return ifd; + /* Update the TIFF version number. */ + int version = ifd.getVersion(); + if (version > _version) { + _version = version; } - /** - * Initializes the state of the module for parsing. This overrides the - * superclass method to reset all the profile flags. - */ - @Override - protected void initParse() { - super.initParse(); - ListIterator pter = _profile.listIterator(); - while (pter.hasNext()) { - TiffProfile prof = pter.next(); - prof.setAlreadyOK(false); + if (list.isEmpty() && type == IFD.TIFF) { + ifd.setFirst(true); + } else if (list.size() == 1 && type == IFD.TIFF) { + // For some profiles, the second IFD is assumed to + // be the thumbnail. This may not be valid under + // all circumstances. + ifd.setThumbnail(true); + } + list.add(ifd); + + if (ifd instanceof TiffIFD) { + TiffIFD tifd = (TiffIFD) ifd; + + long[] subIFDs = tifd.getSubIFDs(); + if (subIFDs != null) { + for (int i = 0; i < subIFDs.length; i++) { + next = subIFDs[i]; + while (next != 0) { + IFD sub = parseIFDChain(next, info, IFD.TIFF, list, suppressErrors); + next = sub.getNext(); + } } - // Initialize flags for the Exif profile. A thumbnail is not - // required, so by default we set the thumbnail flag to true. - // If there is a thumbnail, it must meet the profile. - _exifFirstFlag = false; - _exifThumbnailFlag = true; - - // Initialize flags for the DNG profile. - _dngThumbnailFlag = false; - _dngRawFlag = false; + } + + long offset = tifd.getExifIFD(); + if (offset != IFD.NULL) { + IFD ex = parseIFDChain(offset, info, IFD.EXIF, list, suppressErrors); + tifd.setTheExifIFD((ExifIFD) ex); + } + if ((offset = tifd.getGPSInfoIFD()) != IFD.NULL) { + IFD gp = parseIFDChain(offset, info, IFD.GPSINFO, list, suppressErrors); + tifd.setTheGPSInfoIFD((GPSInfoIFD) gp); + } + if ((offset = tifd.getInteroperabilityIFD()) != IFD.NULL) { + IFD io = parseIFDChain(offset, info, IFD.INTEROPERABILITY, list, suppressErrors); + tifd.setTheInteroperabilityIFD((InteroperabilityIFD) io); + } + if ((offset = tifd.getGlobalParametersIFD()) != IFD.NULL) { + IFD io = parseIFDChain(offset, info, IFD.GLOBALPARAMETERS, list, suppressErrors); + tifd.setTheGlobalParametersIFD((GlobalParametersIFD) io); + } } - /** - * Return the index into _mimeType which should be used for the MIME type - * property. This must be called after all the profiles have been checked. - * An index of 0 is dominant; if any profiles return 0 from their - * getMimeClass method, or if conflicting values are returned by different - * satisfied profiles, then we return 0. - */ - protected int selectMimeTypeIndex() { - int trial = -1; - ListIterator pter = _profile.listIterator(); - while (pter.hasNext()) { - TiffProfile prof = pter.next(); - if (prof.isAlreadyOK()) { - // Profile was satisfied - int idx = prof.getMimeClass(); - if (idx == 0) { - // 0 beats all others - return 0; - } else if (trial >= 0 && idx != trial) { - // any conflict implies 0 - return 0; - } else { - // Treat idx as the tentative return value - trial = idx; - } - } - } - if (trial == -1) { - // No profiles at all were satisfied - return 0; + return ifd; + } + + /** + * Initializes the state of the module for parsing. This overrides the superclass method to reset + * all the profile flags. + */ + @Override + protected void initParse() { + super.initParse(); + ListIterator pter = _profile.listIterator(); + while (pter.hasNext()) { + TiffProfile prof = pter.next(); + prof.setAlreadyOK(false); + } + // Initialize flags for the Exif profile. A thumbnail is not + // required, so by default we set the thumbnail flag to true. + // If there is a thumbnail, it must meet the profile. + _exifFirstFlag = false; + _exifThumbnailFlag = true; + + // Initialize flags for the DNG profile. + _dngThumbnailFlag = false; + _dngRawFlag = false; + } + + /** + * Return the index into _mimeType which should be used for the MIME type property. This must be + * called after all the profiles have been checked. An index of 0 is dominant; if any profiles + * return 0 from their getMimeClass method, or if conflicting values are returned by different + * satisfied profiles, then we return 0. + */ + protected int selectMimeTypeIndex() { + int trial = -1; + ListIterator pter = _profile.listIterator(); + while (pter.hasNext()) { + TiffProfile prof = pter.next(); + if (prof.isAlreadyOK()) { + // Profile was satisfied + int idx = prof.getMimeClass(); + if (idx == 0) { + // 0 beats all others + return 0; + } else if (trial >= 0 && idx != trial) { + // any conflict implies 0 + return 0; + } else { + // Treat idx as the tentative return value + trial = idx; } - // All satisfied profiles returned trial - return trial; + } } - + if (trial == -1) { + // No profiles at all were satisfied + return 0; + } + // All satisfied profiles returned trial + return trial; + } } diff --git a/jhove-modules/tiff-hul/src/main/java/edu/harvard/hul/ois/jhove/module/tiff/ExifIFD.java b/jhove-modules/tiff-hul/src/main/java/edu/harvard/hul/ois/jhove/module/tiff/ExifIFD.java index c711b9089..0b5a0eeff 100644 --- a/jhove-modules/tiff-hul/src/main/java/edu/harvard/hul/ois/jhove/module/tiff/ExifIFD.java +++ b/jhove-modules/tiff-hul/src/main/java/edu/harvard/hul/ois/jhove/module/tiff/ExifIFD.java @@ -1,1065 +1,910 @@ -/********************************************************************** - * Jhove - JSTOR/Harvard Object Validation Environment - * Copyright 2003-2007 by JSTOR and the President and Fellows of Harvard College - **********************************************************************/ - +/** + * ******************************************************************** Jhove - JSTOR/Harvard Object + * Validation Environment Copyright 2003-2007 by JSTOR and the President and Fellows of Harvard + * College ******************************************************************** + */ package edu.harvard.hul.ois.jhove.module.tiff; import edu.harvard.hul.ois.jhove.*; import edu.harvard.hul.ois.jhove.messages.JhoveMessage; import edu.harvard.hul.ois.jhove.messages.JhoveMessages; - import java.io.*; import java.text.MessageFormat; import java.util.*; -/** - * Encapsulation of a Exif IFD - */ -public class ExifIFD - extends IFD -{ - /****************************************************************** - * PRIVATE CLASS FIELDS. - ******************************************************************/ +/** Encapsulation of a Exif IFD */ +public class ExifIFD extends IFD { + /** + * **************************************************************** PRIVATE CLASS FIELDS. + * **************************************************************** + */ + /** ExifVersion tag. */ + protected static final int EXPOSURETIME = 33434, + FNUMBER = 33437, + EXPOSUREPROGRAM = 34850, + SPECTRALSENSITIVITY = 34852, + ISOSPEEDRATINGS = 34855, + OECF = 34856, + EXIFVERSION = 36864, + DATETIMEORIGINAL = 36867, + DATETIMEDIGITIZED = 36868, + COMPONENTSCONFIGURATION = 37121, + COMPRESSEDBITSPERPIXEL = 37122, + SHUTTERSPEEDVALUE = 37377, + APERTUREVALUE = 37378, + BRIGHTNESSVALUE = 37379, + EXPOSUREBIASVALUE = 37380, + MAXAPERTUREVALUE = 37381, + SUBJECTDISTANCE = 37382, + METERINGMODE = 37383, + LIGHTSOURCE = 37384, + FLASH = 37385, + FOCALLENGTH = 37386, + SUBJECTAREA = 37396, + MAKERNOTE = 37500, + USERCOMMENT = 37510, + SUBSECTIME = 37520, + SUBSECTIMEORIGINAL = 37521, + SUBSECTIMEDIGITIZED = 37522, + FLASHPIXVERSION = 40960, + COLORSPACE = 40961, + PIXELXDIMENSION = 40962, + PIXELYDIMENSION = 40963, + RELATEDSOUNDFILE = 40964, + FLASHENERGY = 41483, + SPATIALFREQUENCYRESPONSE = 41484, + FOCALPLANEXRESOLUTION = 41486, + FOCALPLANEYRESOLUTION = 41487, + FOCALPLANERESOLUTIONUNIT = 41488, + SUBJECTLOCATION = 41492, + EXPOSUREINDEX = 41493, + SENSINGMETHOD = 41495, + FILESOURCE = 41728, + SCENETYPE = 41729, + CFAPATTERN = 41730, + CUSTOMRENDERED = 41985, + EXPOSUREMODE = 41986, + WHITEBALANCE = 41987, + DIGITALZOOMRATIO = 41988, + FOCALLENGTHIN35MMFILM = 41989, + SCENECAPTURETYPE = 41990, + GAINCONTROL = 41991, + CONTRAST = 41992, + SATURATION = 41993, + SHARPNESS = 41994, + DEVICESETTINGDESCRIPTION = 41995, + SUBJECTDISTANCERANGE = 41996, + IMAGEUNIQUEID = 42016; - /** ExifVersion tag. */ - protected static final int - EXPOSURETIME = 33434, - FNUMBER = 33437, - EXPOSUREPROGRAM = 34850, - SPECTRALSENSITIVITY = 34852, - ISOSPEEDRATINGS = 34855, - OECF = 34856, - EXIFVERSION = 36864, - DATETIMEORIGINAL = 36867, - DATETIMEDIGITIZED = 36868, - COMPONENTSCONFIGURATION = 37121, - COMPRESSEDBITSPERPIXEL = 37122, - SHUTTERSPEEDVALUE = 37377, - APERTUREVALUE = 37378, - BRIGHTNESSVALUE = 37379, - EXPOSUREBIASVALUE = 37380, - MAXAPERTUREVALUE = 37381, - SUBJECTDISTANCE = 37382, - METERINGMODE = 37383, - LIGHTSOURCE = 37384, - FLASH = 37385, - FOCALLENGTH = 37386, - SUBJECTAREA = 37396, - MAKERNOTE = 37500, - USERCOMMENT = 37510, - SUBSECTIME = 37520, - SUBSECTIMEORIGINAL = 37521, - SUBSECTIMEDIGITIZED = 37522, - FLASHPIXVERSION = 40960, - COLORSPACE = 40961, - PIXELXDIMENSION = 40962, - PIXELYDIMENSION = 40963, - RELATEDSOUNDFILE = 40964, - FLASHENERGY = 41483, - SPATIALFREQUENCYRESPONSE = 41484, - FOCALPLANEXRESOLUTION = 41486, - FOCALPLANEYRESOLUTION = 41487, - FOCALPLANERESOLUTIONUNIT = 41488, - SUBJECTLOCATION = 41492, - EXPOSUREINDEX = 41493, - SENSINGMETHOD = 41495, - FILESOURCE = 41728, - SCENETYPE = 41729, - CFAPATTERN = 41730, - CUSTOMRENDERED = 41985, - EXPOSUREMODE = 41986, - WHITEBALANCE = 41987, - DIGITALZOOMRATIO = 41988, - FOCALLENGTHIN35MMFILM = 41989, - SCENECAPTURETYPE = 41990, - GAINCONTROL = 41991, - CONTRAST = 41992, - SATURATION = 41993, - SHARPNESS = 41994, - DEVICESETTINGDESCRIPTION = 41995, - SUBJECTDISTANCERANGE = 41996, - IMAGEUNIQUEID = 42016; + private static final String[] COLORSPACE_L = {"sRGB", "uncalibrated"}; + private static final int[] COLORSPACE_INDEX = {1, 65535}; + public static final String[] COMPONENTSCONFIGURATION_L = { + "Does not exist", "Y", "Cb", "Cr", "R", "G", "B" + }; + public static final String[] CONTRAST_L = {"normal", "soft", "hard"}; + public static final String[] CUSTOMRENDERED_L = {"normal", "custom"}; + public static final String[] EXPOSUREMODE_L = {"auto", "manual", "auto bracket"}; + public static final String[] EXPOSUREPROGRAM_L = { + "unidentified", + "manual", + "program normal", + "aperture priority", + "shutter priority", + "program creative", + "program action", + "portrait mode", + "landscape mode" + }; + public static final String[] FILESOURCE_L = {"", "", "", "DSC"}; + public static final String[] FLASH_L = { + "did not fire", + "fired", + "strobe return light not detected", + "strobe return light detected", + "fired, compulsory flash mode", + "fired, compulsory flash mode, return light not detected", + "fired, compulsory flash mode, return light detected", + "did not fire, compulsory flash mode", + "did not fire, auto mode", + "fired, auto mode", + "fired, auto mode, return light not detected", + "fired, auto mode, return light detected", + "no flash function", + "fired, red-eye reduction mode", + "fired, red-eye reduction mode, return light not detected", + "fired, red-eye reduction mode, return light detected", + "fired, compulsory mode", + "fired, compulsory mode, return light not detected", + "fired, compulsory flash mode, return light detected", + "fired, auto mode, red-eye reduction mode", + "fired, auto mode, red-eye reduction mode, return light not detected", + "fired, auto mode, red-eye reduction mode, return light detected", + }; + public static final int[] FLASH_INDEX = { + 0, 1, 5, 7, 9, 13, 15, 16, 24, 25, 29, 31, 32, 65, 69, 71, 73, 77, 79, 89, 93, 95 + }; + public static final String[] FOCALPLANERESOLUTIONUNIT_L = { + "", "", "inches", "centimeters", "millimeters", "micrometers" + }; + public static final String[] GAINCONTROL_L = { + "none", "low gain up", "high gain up", "low gain down", "high gain down" + }; + public static final String[] LIGHTSOURCE_L = { + "unknown", + "daylight", + "fluorescent", + "tungsten", + "flash", + "fine weather", + "cloudy weather", + "shade", + "daylight flourescent (D 5700 - 7100K)", + "day white flourescent (N 4600 - 5400K)", + "cool white flourescent (W 3900 - 4500K)", + "white flourescent (WW 3200 - 3700K)", + "standard light A", + "standard light B", + "standard light C", + "D55", + "D65", + "D75", + "D50", + "ISO studio tungsten", + "other" + }; + public static final int[] LIGHTSOURCE_INDEX = { + 0, 1, 2, 3, 4, 9, 10, 11, 12, 13, 14, 15, 17, 18, 19, 20, 21, 22, 23, 24, 255 + }; + public static final String[] METERINGMODE_L = { + "unidentified", "average", "centre weighted average", "spot", + "multispot", "pattern", "partial", "other" + }; + public static final int[] METERINGMODE_INDEX = {0, 1, 2, 3, 4, 5, 6, 255}; + public static final String[] SATURATION_L = {"normal", "soft", "hard"}; + public static final String[] SCENECAPTURETYPE_L = {"standard", "landscape", "portrait", "night"}; + public static final String[] SCENETYPE_L = {"", "directly photographed image"}; + public static final String[] SENSINGMETHOD_L = { + "", + "not defined", + "one-chip color area", + "two-chip color area", + "three-chip color area", + "color sequential area", + "", + "trilinear", + "colour sequential linear" + }; + public static final String[] SHARPNESS_L = {"normal", "soft", "hard"}; + public static final String[] SUBJECTDISTANCERANGE_L = {"unknown", "macro", "close", "distant"}; + public static final String[] WHITEBALANCE_L = {"auto", "manual"}; - private static final String [] COLORSPACE_L = { - "sRGB", "uncalibrated" - }; - private static final int [] COLORSPACE_INDEX = { - 1, 65535 - }; - public static final String [] COMPONENTSCONFIGURATION_L = { - "Does not exist", "Y", "Cb", "Cr", "R", "G", "B" - }; - public static final String [] CONTRAST_L = { - "normal", "soft", "hard" - }; - public static final String [] CUSTOMRENDERED_L = { - "normal", "custom" - }; - public static final String [] EXPOSUREMODE_L = { - "auto", "manual", "auto bracket" - }; - public static final String [] EXPOSUREPROGRAM_L = { - "unidentified", "manual", "program normal", "aperture priority", - "shutter priority", "program creative", "program action", - "portrait mode", "landscape mode" - }; - public static final String [] FILESOURCE_L = { - "", "", "", "DSC" - }; - public static final String [] FLASH_L = { - "did not fire", - "fired", - "strobe return light not detected", - "strobe return light detected", - "fired, compulsory flash mode", - "fired, compulsory flash mode, return light not detected", - "fired, compulsory flash mode, return light detected", - "did not fire, compulsory flash mode", - "did not fire, auto mode", - "fired, auto mode", - "fired, auto mode, return light not detected", - "fired, auto mode, return light detected", - "no flash function", - "fired, red-eye reduction mode", - "fired, red-eye reduction mode, return light not detected", - "fired, red-eye reduction mode, return light detected", - "fired, compulsory mode", - "fired, compulsory mode, return light not detected", - "fired, compulsory flash mode, return light detected", - "fired, auto mode, red-eye reduction mode", - "fired, auto mode, red-eye reduction mode, return light not detected", - "fired, auto mode, red-eye reduction mode, return light detected", - }; - public static final int[] FLASH_INDEX = { - 0, 1, 5, 7, 9, 13, 15, 16, 24, 25, 29, 31, 32, 65, 69, 71, 73, 77, - 79, 89, 93, 95 - }; - public static final String [] FOCALPLANERESOLUTIONUNIT_L = { - "", "", "inches", "centimeters", "millimeters", "micrometers" - }; - public static final String [] GAINCONTROL_L = { - "none", "low gain up", "high gain up", "low gain down", - "high gain down" - }; - public static final String [] LIGHTSOURCE_L = { - "unknown", "daylight", "fluorescent", "tungsten", - "flash", "fine weather", "cloudy weather", "shade", - "daylight flourescent (D 5700 - 7100K)", - "day white flourescent (N 4600 - 5400K)", - "cool white flourescent (W 3900 - 4500K)", - "white flourescent (WW 3200 - 3700K)", - "standard light A", "standard light B", "standard light C", - "D55", "D65", "D75", "D50", "ISO studio tungsten", "other" - }; - public static final int [] LIGHTSOURCE_INDEX = { - 0, 1, 2, 3, 4, 9, 10, 11, 12, 13, 14, 15, 17, 18, 19, 20, 21, 22, 23, - 24, 255 - }; - public static final String [] METERINGMODE_L = { - "unidentified", "average", "centre weighted average", "spot", - "multispot", "pattern", "partial", "other" - }; - public static final int [] METERINGMODE_INDEX = { - 0, 1, 2, 3, 4, 5, 6, 255 - }; - public static final String [] SATURATION_L = { - "normal", "soft", "hard" - }; - public static final String [] SCENECAPTURETYPE_L = { - "standard", "landscape", "portrait", "night" - }; - public static final String [] SCENETYPE_L = { - "", "directly photographed image" - }; - public static final String [] SENSINGMETHOD_L = { - "", "not defined", "one-chip color area", - "two-chip color area", "three-chip color area", - "color sequential area", "", "trilinear", "colour sequential linear" - }; - public static final String [] SHARPNESS_L = { - "normal", "soft", "hard" - }; - public static final String [] SUBJECTDISTANCERANGE_L = { - "unknown", "macro", "close", "distant" - }; - public static final String [] WHITEBALANCE_L = { - "auto", "manual" - }; + /** + * **************************************************************** PRIVATE INSTANCE FIELDS. + * **************************************************************** + */ - /****************************************************************** - * PRIVATE INSTANCE FIELDS. - ******************************************************************/ + /** Aperature value tag. */ + private Rational _apertureValue; - /** Aperature value tag. */ - private Rational _apertureValue; - private Rational _brightnessValue; - private int [] _cfaPattern; - protected int _colorSpace; - private int [] _componentsConfiguration; - private Rational _compressedBitsPerPixel; - private int _contrast; - private int _customRendered; - private String _dateTimeDigitized; - private String _dateTimeOriginal; - private int [] _deviceSettingDescription; - private Rational _digitalZoomRatio; - protected String _exifVersion; - private Rational _exposureBiasValue; - private Rational _exposureIndex; - private int _exposureMode; - private int _exposureProgram; - private Rational _exposureTime; - private int _fileSource; - private int _flash; - private Rational _flashEnergy; - protected String _flashpixVersion; - private Rational _fNumber; - private Rational _focalLength; - private int _focalLengthIn35mmFilm; - private Rational _focalPlaneXResolution; - private Rational _focalPlaneYResolution; - private int _focalPlaneResolutionUnit; - private int _gainControl; - private String _imageUniqueID; - private int [] _isoSpeedRatings; - private int _lightSource; - private int [] _makerNote; - private Rational _maxApertureValue; - private int _meteringMode; - private int [] _oecf; - private long _pixelXDimension; - private long _pixelYDimension; - private String _relatedSoundFile; - private int _saturation; - private int _sceneCaptureType; - private int _sceneType; - private int _sensingMethod; - private int _sharpness; - private Rational _shutterSpeedValue; - private int [] _spatialFrequencyResponse; - private String _spectralSensitivity; - private int [] _subjectArea; - private Rational _subjectDistance; - private int _subjectDistanceRange; - private int [] _subjectLocation; - private String _subSecTime; - private String _subSecTimeDigitized; - private String _subSecTimeOriginal; - private int [] _userComment; - private int _whiteBalance; - - /* data from standard TIFF tags */ - private String _manufacturer; - private String _model; - private String _software; - private String _artist; - private int _orientation; + private Rational _brightnessValue; + private int[] _cfaPattern; + protected int _colorSpace; + private int[] _componentsConfiguration; + private Rational _compressedBitsPerPixel; + private int _contrast; + private int _customRendered; + private String _dateTimeDigitized; + private String _dateTimeOriginal; + private int[] _deviceSettingDescription; + private Rational _digitalZoomRatio; + protected String _exifVersion; + private Rational _exposureBiasValue; + private Rational _exposureIndex; + private int _exposureMode; + private int _exposureProgram; + private Rational _exposureTime; + private int _fileSource; + private int _flash; + private Rational _flashEnergy; + protected String _flashpixVersion; + private Rational _fNumber; + private Rational _focalLength; + private int _focalLengthIn35mmFilm; + private Rational _focalPlaneXResolution; + private Rational _focalPlaneYResolution; + private int _focalPlaneResolutionUnit; + private int _gainControl; + private String _imageUniqueID; + private int[] _isoSpeedRatings; + private int _lightSource; + private int[] _makerNote; + private Rational _maxApertureValue; + private int _meteringMode; + private int[] _oecf; + private long _pixelXDimension; + private long _pixelYDimension; + private String _relatedSoundFile; + private int _saturation; + private int _sceneCaptureType; + private int _sceneType; + private int _sensingMethod; + private int _sharpness; + private Rational _shutterSpeedValue; + private int[] _spatialFrequencyResponse; + private String _spectralSensitivity; + private int[] _subjectArea; + private Rational _subjectDistance; + private int _subjectDistanceRange; + private int[] _subjectLocation; + private String _subSecTime; + private String _subSecTimeDigitized; + private String _subSecTimeOriginal; + private int[] _userComment; + private int _whiteBalance; - private NisoImageMetadata _niso; + /* data from standard TIFF tags */ + private String _manufacturer; + private String _model; + private String _software; + private String _artist; + private int _orientation; - /****************************************************************** - * CLASS CONSTRUCTOR. - ******************************************************************/ + private NisoImageMetadata _niso; - /** Instantiate an ExifIFD object. - * @param offset IFD offset - * @param info the RepInfo object - * @param raf TIFF file - * @param bigEndian True if big-endian file - */ - public ExifIFD (long offset, RepInfo info, RandomAccessFile raf, - boolean bigEndian) - { - super (offset, info, raf, bigEndian); + /** + * **************************************************************** CLASS CONSTRUCTOR. + * **************************************************************** + */ - _colorSpace = NULL; - _contrast = 0; - _customRendered = NULL; - _exifVersion = "0220"; - _exposureMode = NULL; - _exposureProgram = NULL; - _fileSource = NULL; - _flash = NULL; - _flashpixVersion = "0100"; - _focalLengthIn35mmFilm = NULL; - _focalPlaneResolutionUnit = 2; - _gainControl = NULL; - _lightSource = NULL; - _meteringMode = NULL; - _pixelXDimension = NULL; - _pixelYDimension = NULL; - _saturation = NULL; - _sceneCaptureType = NULL; - _sceneType = NULL; - _sensingMethod = NULL; - _sharpness = NULL; - _subjectDistanceRange = NULL; - _whiteBalance = NULL; - _niso = new NisoImageMetadata (); - } + /** + * Instantiate an ExifIFD object. + * + * @param offset IFD offset + * @param info the RepInfo object + * @param raf TIFF file + * @param bigEndian True if big-endian file + */ + public ExifIFD(long offset, RepInfo info, RandomAccessFile raf, boolean bigEndian) { + super(offset, info, raf, bigEndian); - /****************************************************************** - * PUBLIC INSTANCE METHODS. - ******************************************************************/ + _colorSpace = NULL; + _contrast = 0; + _customRendered = NULL; + _exifVersion = "0220"; + _exposureMode = NULL; + _exposureProgram = NULL; + _fileSource = NULL; + _flash = NULL; + _flashpixVersion = "0100"; + _focalLengthIn35mmFilm = NULL; + _focalPlaneResolutionUnit = 2; + _gainControl = NULL; + _lightSource = NULL; + _meteringMode = NULL; + _pixelXDimension = NULL; + _pixelYDimension = NULL; + _saturation = NULL; + _sceneCaptureType = NULL; + _sceneType = NULL; + _sensingMethod = NULL; + _sharpness = NULL; + _subjectDistanceRange = NULL; + _whiteBalance = NULL; + _niso = new NisoImageMetadata(); + } - /** Get the IFD properties. */ - public Property getProperty (boolean rawOutput) - { - List entries = new LinkedList (); + /** + * **************************************************************** PUBLIC INSTANCE METHODS. + * **************************************************************** + */ - entries.add (new Property ("ExifVersion", PropertyType.STRING, - _exifVersion)); - entries.add (new Property ("FlashpixVersion", PropertyType.STRING, - _flashpixVersion)); - if (_colorSpace != NULL) { - entries.add (addIntegerProperty ("ColorSpace", _colorSpace, - COLORSPACE_L, COLORSPACE_INDEX, - rawOutput)); - } - if (_componentsConfiguration != null) { - entries.add (new Property ("ComponentsConfiguration", - PropertyType.INTEGER, - PropertyArity.ARRAY, - _componentsConfiguration)); - } - if (_compressedBitsPerPixel != null) { - entries.add (addRationalProperty ("CompressedBitsPerPixel", - _compressedBitsPerPixel, - rawOutput)); - } - if (_pixelXDimension != NULL) { - entries.add (new Property ("PixelXDimension", PropertyType.LONG, - new Long (_pixelXDimension))); - } - if (_pixelYDimension != NULL) { - entries.add (new Property ("PixelYDimension", PropertyType.LONG, - new Long (_pixelYDimension))); - } - if (_makerNote != null) { - entries.add (new Property ("MakerNote", PropertyType.INTEGER, - PropertyArity.ARRAY, _makerNote)); - } - if (_userComment != null) { - Property ucp = makeUserCommentProperty (_userComment, rawOutput); - if (ucp != null) { - entries.add (ucp); - } - } - if (_relatedSoundFile != null) { - entries.add (new Property ("RelatedSoundFile", PropertyType.STRING, - _relatedSoundFile)); - } - if (_dateTimeOriginal != null) { - entries.add (new Property ("DateTimeOriginal", PropertyType.STRING, - _dateTimeOriginal)); - } - if (_dateTimeDigitized != null) { - entries.add (new Property ("DateTimeDigitized", - PropertyType.STRING, - _dateTimeDigitized)); - } - if (_subSecTime != null) { - entries.add (new Property ("SubSecTime", PropertyType.STRING, - _subSecTime)); - } - if (_subSecTimeOriginal != null) { - entries.add (new Property ("SubSecTimeOriginal", - PropertyType.STRING, - _subSecTimeOriginal)); - } - if (_subSecTimeDigitized != null) { - entries.add (new Property ("SubSecTimeDigitized", - PropertyType.STRING, - _subSecTimeDigitized)); - } - if (_imageUniqueID != null) { - entries.add (new Property ("ImageUniqueID",PropertyType.STRING, - _imageUniqueID)); - } - - if (_exposureTime != null) { - entries.add (addRationalProperty ("ExposureTime", _exposureTime, - rawOutput)); - } - if (_fNumber != null) { - entries.add (addRationalProperty ("FNumber", _fNumber, - rawOutput)); - } - if (_exposureProgram != NULL) { - entries.add (addIntegerProperty ("ExposureProgram", - _exposureProgram, - EXPOSUREPROGRAM_L, rawOutput)); - } - if (_spectralSensitivity != null) { - entries.add (new Property ("SpectralSensitivity", - PropertyType.STRING, - _spectralSensitivity)); - } - if (_isoSpeedRatings != null) { - entries.add (new Property ("ISOSpeedRatings", PropertyType.INTEGER, - PropertyArity.ARRAY, _isoSpeedRatings)); - } - if (_oecf != null) { - entries.add (new Property ("OECF", PropertyType.INTEGER, - PropertyArity.ARRAY, _oecf)); - } - if (_shutterSpeedValue != null) { - entries.add (addRationalProperty ("ShutterSpeedValue", - _shutterSpeedValue, rawOutput)); - } - if (_apertureValue != null) { - entries.add (addRationalProperty ("ApertureValue", _apertureValue, - rawOutput)); - } - if (_brightnessValue != null) { - entries.add (addRationalProperty ("BrightnessValue", - _brightnessValue, rawOutput)); - } - if (_exposureBiasValue != null) { - entries.add (addRationalProperty ("ExposureBiasValue", - _exposureBiasValue, - rawOutput)); - } - if (_maxApertureValue != null) { - entries.add (addRationalProperty ("MaxApertureValue", - _maxApertureValue, rawOutput)); - } - if (_subjectDistance != null) { - entries.add (addRationalProperty ("SubjectDistance", - _subjectDistance, rawOutput)); - } - if (_meteringMode != NULL) { - entries.add (addIntegerProperty ("MeteringMode", _meteringMode, - METERINGMODE_L, - METERINGMODE_INDEX, rawOutput)); - } - if (_lightSource != NULL) { - entries.add (addIntegerProperty ("LightSource", _lightSource, - LIGHTSOURCE_L, - LIGHTSOURCE_INDEX, rawOutput)); - } - if (_flash != NULL) { - entries.add (addIntegerProperty ("Flash", _flash, FLASH_L, - FLASH_INDEX, rawOutput)); - } - if (_focalLength != null) { - entries.add (addRationalProperty ("FocalLength", _focalLength, - rawOutput)); - } - if (_subjectArea != null) { - entries.add (new Property ("SubjectArea", PropertyType.INTEGER, - PropertyArity.ARRAY, _subjectArea)); - } - if (_flashEnergy != null) { - entries.add (addRationalProperty ("FlashEnergy", _flashEnergy, - rawOutput)); - } - if (_spatialFrequencyResponse != null) { - entries.add (new Property ("SubjectArea", PropertyType.INTEGER, - PropertyArity.ARRAY, _subjectArea)); - } - if (_focalPlaneXResolution != null) { - entries.add (addRationalProperty ("FocalPlaneXResolution", - _focalPlaneXResolution, - rawOutput)); - } - if (_focalPlaneYResolution != null) { - entries.add (addRationalProperty ("FocalPlaneYResolution", - _focalPlaneYResolution, - rawOutput)); - } - if (_focalPlaneResolutionUnit != NULL) { - entries.add (addIntegerProperty ("FocalPlaneResolutionUnit", - _focalPlaneResolutionUnit, - FOCALPLANERESOLUTIONUNIT_L, - rawOutput)); - } - if (_subjectLocation != null) { - entries.add (new Property ("SubjectLocation", PropertyType.INTEGER, - PropertyArity.ARRAY, _subjectLocation)); - } - if (_exposureIndex != null) { - entries.add (addRationalProperty ("ExposureIndex", _exposureIndex, - rawOutput)); - } - if (_sensingMethod != NULL) { - entries.add (addIntegerProperty ("SensingMethod", _sensingMethod, - SENSINGMETHOD_L, rawOutput)); - } - if (_fileSource != NULL) { - entries.add (addIntegerProperty ("FileSource", _fileSource, - FILESOURCE_L, rawOutput)); - } - if (_sceneType != NULL) { - entries.add (addIntegerProperty ("SceneType", _sceneType, - SCENETYPE_L, rawOutput)); - } - if (_cfaPattern != null) { - entries.add (new Property ("CFAPattern", PropertyType.INTEGER, - PropertyArity.ARRAY, _cfaPattern)); - } - if (_customRendered != NULL) { - entries.add (addIntegerProperty ("CustomRendered", _customRendered, - CUSTOMRENDERED_L, rawOutput)); - } - if (_exposureMode != NULL) { - entries.add (addIntegerProperty ("ExposureMode", _exposureMode, - EXPOSUREMODE_L, rawOutput)); - } - if (_whiteBalance != NULL) { - entries.add (addIntegerProperty ("WhiteBalance", _whiteBalance, - WHITEBALANCE_L, rawOutput)); - } - if (_digitalZoomRatio != null) { - entries.add (addRationalProperty ("DigitalZoomRatio", - _digitalZoomRatio, rawOutput)); - } - if (_focalLengthIn35mmFilm != NULL) { - entries.add (new Property ("FocalLengthIn35mmFilm", - PropertyType.INTEGER, - new Integer (_focalLengthIn35mmFilm))); - } - if (_sceneCaptureType != NULL) { - entries.add (addIntegerProperty ("SceneCaptureType", - _sceneCaptureType, - SCENECAPTURETYPE_L, rawOutput)); - } - if (_gainControl != NULL) { - entries.add (addIntegerProperty ("GainControl", _gainControl, - GAINCONTROL_L, rawOutput)); - } - if (_contrast != NULL) { - entries.add (addIntegerProperty ("Contrast", _contrast, - CONTRAST_L, rawOutput)); - } - if (_saturation != NULL) { - entries.add (addIntegerProperty ("Saturation", _saturation, - SATURATION_L, rawOutput)); - } - if (_sharpness != NULL) { - entries.add (addIntegerProperty ("Sharpness", _sharpness, - SHARPNESS_L, rawOutput)); - } - if (_deviceSettingDescription != null) { - entries.add (new Property ("DeviceSettingDescription", - PropertyType.INTEGER, - PropertyArity.ARRAY, - _deviceSettingDescription)); - } - if (_subjectDistanceRange != NULL) { - entries.add (addIntegerProperty ("SubjectDistanceRange", - _subjectDistanceRange, - SUBJECTDISTANCERANGE_L, - rawOutput)); - } + /** Get the IFD properties. */ + public Property getProperty(boolean rawOutput) { + List entries = new LinkedList(); - // properties from standard TIFF tags - if (_manufacturer != null) { - entries.add (new Property ("Make", - PropertyType.STRING, - _manufacturer)); - } - if (_model != null) { - entries.add (new Property ("Model", - PropertyType.STRING, - _model)); - } - if (_software != null) { - entries.add (new Property ("Software", - PropertyType.STRING, - _software)); - } - if (_artist != null) { - entries.add (new Property ("Artist", - PropertyType.STRING, - _artist)); - } - return propertyHeader ("Exif", entries); + entries.add(new Property("ExifVersion", PropertyType.STRING, _exifVersion)); + entries.add(new Property("FlashpixVersion", PropertyType.STRING, _flashpixVersion)); + if (_colorSpace != NULL) { + entries.add( + addIntegerProperty("ColorSpace", _colorSpace, COLORSPACE_L, COLORSPACE_INDEX, rawOutput)); + } + if (_componentsConfiguration != null) { + entries.add( + new Property( + "ComponentsConfiguration", + PropertyType.INTEGER, + PropertyArity.ARRAY, + _componentsConfiguration)); } - - - /** Returns the Exif version string (tag 36864). */ - public String getExifVersion () - { - return _exifVersion; + if (_compressedBitsPerPixel != null) { + entries.add( + addRationalProperty("CompressedBitsPerPixel", _compressedBitsPerPixel, rawOutput)); } - - /** Returns the constructed NisoImageMetadata. */ - public NisoImageMetadata getNisoImageMetadata () - { - return _niso; + if (_pixelXDimension != NULL) { + entries.add(new Property("PixelXDimension", PropertyType.LONG, new Long(_pixelXDimension))); + } + if (_pixelYDimension != NULL) { + entries.add(new Property("PixelYDimension", PropertyType.LONG, new Long(_pixelYDimension))); + } + if (_makerNote != null) { + entries.add(new Property("MakerNote", PropertyType.INTEGER, PropertyArity.ARRAY, _makerNote)); + } + if (_userComment != null) { + Property ucp = makeUserCommentProperty(_userComment, rawOutput); + if (ucp != null) { + entries.add(ucp); + } + } + if (_relatedSoundFile != null) { + entries.add(new Property("RelatedSoundFile", PropertyType.STRING, _relatedSoundFile)); + } + if (_dateTimeOriginal != null) { + entries.add(new Property("DateTimeOriginal", PropertyType.STRING, _dateTimeOriginal)); + } + if (_dateTimeDigitized != null) { + entries.add(new Property("DateTimeDigitized", PropertyType.STRING, _dateTimeDigitized)); + } + if (_subSecTime != null) { + entries.add(new Property("SubSecTime", PropertyType.STRING, _subSecTime)); + } + if (_subSecTimeOriginal != null) { + entries.add(new Property("SubSecTimeOriginal", PropertyType.STRING, _subSecTimeOriginal)); + } + if (_subSecTimeDigitized != null) { + entries.add(new Property("SubSecTimeDigitized", PropertyType.STRING, _subSecTimeDigitized)); + } + if (_imageUniqueID != null) { + entries.add(new Property("ImageUniqueID", PropertyType.STRING, _imageUniqueID)); } + if (_exposureTime != null) { + entries.add(addRationalProperty("ExposureTime", _exposureTime, rawOutput)); + } + if (_fNumber != null) { + entries.add(addRationalProperty("FNumber", _fNumber, rawOutput)); + } + if (_exposureProgram != NULL) { + entries.add( + addIntegerProperty("ExposureProgram", _exposureProgram, EXPOSUREPROGRAM_L, rawOutput)); + } + if (_spectralSensitivity != null) { + entries.add(new Property("SpectralSensitivity", PropertyType.STRING, _spectralSensitivity)); + } + if (_isoSpeedRatings != null) { + entries.add( + new Property( + "ISOSpeedRatings", PropertyType.INTEGER, PropertyArity.ARRAY, _isoSpeedRatings)); + } + if (_oecf != null) { + entries.add(new Property("OECF", PropertyType.INTEGER, PropertyArity.ARRAY, _oecf)); + } + if (_shutterSpeedValue != null) { + entries.add(addRationalProperty("ShutterSpeedValue", _shutterSpeedValue, rawOutput)); + } + if (_apertureValue != null) { + entries.add(addRationalProperty("ApertureValue", _apertureValue, rawOutput)); + } + if (_brightnessValue != null) { + entries.add(addRationalProperty("BrightnessValue", _brightnessValue, rawOutput)); + } + if (_exposureBiasValue != null) { + entries.add(addRationalProperty("ExposureBiasValue", _exposureBiasValue, rawOutput)); + } + if (_maxApertureValue != null) { + entries.add(addRationalProperty("MaxApertureValue", _maxApertureValue, rawOutput)); + } + if (_subjectDistance != null) { + entries.add(addRationalProperty("SubjectDistance", _subjectDistance, rawOutput)); + } + if (_meteringMode != NULL) { + entries.add( + addIntegerProperty( + "MeteringMode", _meteringMode, METERINGMODE_L, METERINGMODE_INDEX, rawOutput)); + } + if (_lightSource != NULL) { + entries.add( + addIntegerProperty( + "LightSource", _lightSource, LIGHTSOURCE_L, LIGHTSOURCE_INDEX, rawOutput)); + } + if (_flash != NULL) { + entries.add(addIntegerProperty("Flash", _flash, FLASH_L, FLASH_INDEX, rawOutput)); + } + if (_focalLength != null) { + entries.add(addRationalProperty("FocalLength", _focalLength, rawOutput)); + } + if (_subjectArea != null) { + entries.add( + new Property("SubjectArea", PropertyType.INTEGER, PropertyArity.ARRAY, _subjectArea)); + } + if (_flashEnergy != null) { + entries.add(addRationalProperty("FlashEnergy", _flashEnergy, rawOutput)); + } + if (_spatialFrequencyResponse != null) { + entries.add( + new Property("SubjectArea", PropertyType.INTEGER, PropertyArity.ARRAY, _subjectArea)); + } + if (_focalPlaneXResolution != null) { + entries.add(addRationalProperty("FocalPlaneXResolution", _focalPlaneXResolution, rawOutput)); + } + if (_focalPlaneYResolution != null) { + entries.add(addRationalProperty("FocalPlaneYResolution", _focalPlaneYResolution, rawOutput)); + } + if (_focalPlaneResolutionUnit != NULL) { + entries.add( + addIntegerProperty( + "FocalPlaneResolutionUnit", + _focalPlaneResolutionUnit, + FOCALPLANERESOLUTIONUNIT_L, + rawOutput)); + } + if (_subjectLocation != null) { + entries.add( + new Property( + "SubjectLocation", PropertyType.INTEGER, PropertyArity.ARRAY, _subjectLocation)); + } + if (_exposureIndex != null) { + entries.add(addRationalProperty("ExposureIndex", _exposureIndex, rawOutput)); + } + if (_sensingMethod != NULL) { + entries.add(addIntegerProperty("SensingMethod", _sensingMethod, SENSINGMETHOD_L, rawOutput)); + } + if (_fileSource != NULL) { + entries.add(addIntegerProperty("FileSource", _fileSource, FILESOURCE_L, rawOutput)); + } + if (_sceneType != NULL) { + entries.add(addIntegerProperty("SceneType", _sceneType, SCENETYPE_L, rawOutput)); + } + if (_cfaPattern != null) { + entries.add( + new Property("CFAPattern", PropertyType.INTEGER, PropertyArity.ARRAY, _cfaPattern)); + } + if (_customRendered != NULL) { + entries.add( + addIntegerProperty("CustomRendered", _customRendered, CUSTOMRENDERED_L, rawOutput)); + } + if (_exposureMode != NULL) { + entries.add(addIntegerProperty("ExposureMode", _exposureMode, EXPOSUREMODE_L, rawOutput)); + } + if (_whiteBalance != NULL) { + entries.add(addIntegerProperty("WhiteBalance", _whiteBalance, WHITEBALANCE_L, rawOutput)); + } + if (_digitalZoomRatio != null) { + entries.add(addRationalProperty("DigitalZoomRatio", _digitalZoomRatio, rawOutput)); + } + if (_focalLengthIn35mmFilm != NULL) { + entries.add( + new Property( + "FocalLengthIn35mmFilm", PropertyType.INTEGER, new Integer(_focalLengthIn35mmFilm))); + } + if (_sceneCaptureType != NULL) { + entries.add( + addIntegerProperty("SceneCaptureType", _sceneCaptureType, SCENECAPTURETYPE_L, rawOutput)); + } + if (_gainControl != NULL) { + entries.add(addIntegerProperty("GainControl", _gainControl, GAINCONTROL_L, rawOutput)); + } + if (_contrast != NULL) { + entries.add(addIntegerProperty("Contrast", _contrast, CONTRAST_L, rawOutput)); + } + if (_saturation != NULL) { + entries.add(addIntegerProperty("Saturation", _saturation, SATURATION_L, rawOutput)); + } + if (_sharpness != NULL) { + entries.add(addIntegerProperty("Sharpness", _sharpness, SHARPNESS_L, rawOutput)); + } + if (_deviceSettingDescription != null) { + entries.add( + new Property( + "DeviceSettingDescription", + PropertyType.INTEGER, + PropertyArity.ARRAY, + _deviceSettingDescription)); + } + if (_subjectDistanceRange != NULL) { + entries.add( + addIntegerProperty( + "SubjectDistanceRange", _subjectDistanceRange, SUBJECTDISTANCERANGE_L, rawOutput)); + } - /** Returns the Flashpix version string (tag 40960). */ - public String getFlashpixVersion () - { - return _flashpixVersion; + // properties from standard TIFF tags + if (_manufacturer != null) { + entries.add(new Property("Make", PropertyType.STRING, _manufacturer)); + } + if (_model != null) { + entries.add(new Property("Model", PropertyType.STRING, _model)); + } + if (_software != null) { + entries.add(new Property("Software", PropertyType.STRING, _software)); + } + if (_artist != null) { + entries.add(new Property("Artist", PropertyType.STRING, _artist)); } + return propertyHeader("Exif", entries); + } + + /** Returns the Exif version string (tag 36864). */ + public String getExifVersion() { + return _exifVersion; + } + + /** Returns the constructed NisoImageMetadata. */ + public NisoImageMetadata getNisoImageMetadata() { + return _niso; + } + + /** Returns the Flashpix version string (tag 40960). */ + public String getFlashpixVersion() { + return _flashpixVersion; + } + + /** returns the colorspace value (tag 40961). */ + public int getColorspace() { + return _colorSpace; + } - /** returns the colorspace value (tag 40961). */ - public int getColorspace () - { - return _colorSpace; + /** Extracts and returns the Exif property list from a standard IFD property header. */ + public List exifProps(Property pHeader) { + try { + Property[] pArr = (Property[]) pHeader.getValue(); + Property entries = pArr[2]; + return (List) entries.getValue(); + } catch (Exception e) { + // We could get caught here if we somehow tried to get + // the Exif properties from something that wasn't a + // standard property header. + return null; } + } + /** Lookup an IFD tag. */ + public void lookupTag(int tag, int type, long count, long value) throws TiffException { + try { + if (tag == APERTUREVALUE) { + checkType(tag, type, RATIONAL); + checkCount(tag, count, 1); + _apertureValue = readRational(count, value); + } else if (tag == BRIGHTNESSVALUE) { + checkType(tag, type, SRATIONAL); + checkCount(tag, count, 1); + _brightnessValue = readRational(count, value); + _niso.setBrightness(_brightnessValue); + } else if (tag == CFAPATTERN) { + checkType(tag, type, UNDEFINED); + _cfaPattern = readByteArray(type, count, value); + } else if (tag == COLORSPACE) { + checkType(tag, type, SHORT); + checkCount(tag, count, 1); + _colorSpace = readShort(type, count, value); + _niso.setColorSpace(_colorSpace); + } else if (tag == COMPONENTSCONFIGURATION) { + checkType(tag, type, UNDEFINED); + _componentsConfiguration = readByteArray(type, count, value); + } else if (tag == COMPRESSEDBITSPERPIXEL) { + checkType(tag, type, RATIONAL); + checkCount(tag, count, 1); + _compressedBitsPerPixel = readRational(count, value); + } else if (tag == CONTRAST) { + checkType(tag, type, SHORT); + checkCount(tag, count, 1); + _contrast = readShort(type, count, value); + } else if (tag == CUSTOMRENDERED) { + checkType(tag, type, SHORT); + checkCount(tag, count, 1); + _customRendered = readShort(type, count, value); + } else if (tag == DATETIMEDIGITIZED) { + checkType(tag, type, ASCII); + checkCount(tag, count, 20); + _dateTimeDigitized = readASCII(count, value); + } else if (tag == DATETIMEORIGINAL) { + checkType(tag, type, ASCII); + checkCount(tag, count, 20); + _dateTimeOriginal = readASCII(count, value); + } else if (tag == DEVICESETTINGDESCRIPTION) { + checkType(tag, type, UNDEFINED); + _deviceSettingDescription = readByteArray(type, count, value); + } else if (tag == DIGITALZOOMRATIO) { + checkType(tag, type, RATIONAL); + checkCount(tag, count, 1); + _digitalZoomRatio = readRational(count, value); + } else if (tag == EXIFVERSION) { + checkType(tag, type, UNDEFINED); + checkCount(tag, count, 4); + int[] iarray = readShortArray(type, count, value); + char[] carray = new char[iarray.length]; + for (int i = 0; i < iarray.length; i++) { + carray[i] = (char) iarray[i]; + } + _exifVersion = new String(carray); + _niso.setExifVersion(_exifVersion); + } else if (tag == EXPOSUREBIASVALUE) { + checkType(tag, type, SRATIONAL); + checkCount(tag, count, 1); + _exposureBiasValue = readRational(count, value); + _niso.setExposureBias(_exposureBiasValue); + } else if (tag == EXPOSUREINDEX) { + checkType(tag, type, RATIONAL); + checkCount(tag, count, 1); + _exposureIndex = readRational(count, value); + _niso.setExposureIndex(_exposureIndex.toDouble()); + } else if (tag == EXPOSUREMODE) { + checkType(tag, type, SHORT); + checkCount(tag, count, 1); + _exposureMode = readShort(type, count, value); + } else if (tag == EXPOSUREPROGRAM) { + checkType(tag, type, SHORT); + checkCount(tag, count, 1); + _exposureProgram = readShort(type, count, value); + _niso.setExposureProgram(_exposureProgram); + } else if (tag == EXPOSURETIME) { + checkType(tag, type, RATIONAL); + checkCount(tag, count, 1); + _exposureTime = readRational(count, value); + _niso.setExposureTime(_exposureTime.toDouble()); + } else if (tag == FILESOURCE) { + checkType(tag, type, UNDEFINED); + checkCount(tag, count, 1); + _fileSource = readByte(type, count, value); + } else if (tag == FLASH) { + checkType(tag, type, SHORT); + checkCount(tag, count, 1); + _flash = readShort(type, count, value); + _niso.setFlash(_flash); // TODO + } else if (tag == FLASHENERGY) { + checkType(tag, type, RATIONAL); + checkCount(tag, count, 1); + _flashEnergy = readRational(count, value); + _niso.setFlashEnergy(_flashEnergy); + } else if (tag == FLASHPIXVERSION) { + checkType(tag, type, UNDEFINED); + checkCount(tag, count, 4); + int[] iarray = readShortArray(type, count, value); + char[] carray = new char[iarray.length]; + for (int i = 0; i < iarray.length; i++) { + carray[i] = (char) iarray[i]; + } + _flashpixVersion = new String(carray); + } else if (tag == FNUMBER) { + checkType(tag, type, RATIONAL); + checkCount(tag, count, 1); + _fNumber = readRational(count, value); + _niso.setFNumber(_fNumber.toDouble()); + } else if (tag == FOCALLENGTH) { + checkType(tag, type, RATIONAL); + checkCount(tag, count, 1); + _focalLength = readRational(count, value); + _niso.setFocalLength(_focalLength.toDouble()); + } else if (tag == FOCALLENGTHIN35MMFILM) { + checkType(tag, type, SHORT); + checkCount(tag, count, 1); + _focalLengthIn35mmFilm = readShort(type, count, value); + } else if (tag == FOCALPLANEXRESOLUTION) { + checkType(tag, type, RATIONAL); + checkCount(tag, count, 1); + _focalPlaneXResolution = readRational(count, value); + } else if (tag == FOCALPLANEYRESOLUTION) { + checkType(tag, type, RATIONAL); + checkCount(tag, count, 1); + _focalPlaneYResolution = readRational(count, value); + } else if (tag == FOCALPLANERESOLUTIONUNIT) { + checkType(tag, type, SHORT); + checkCount(tag, count, 1); + _focalPlaneResolutionUnit = readShort(type, count, value); + } else if (tag == GAINCONTROL) { + checkType(tag, type, SHORT); + checkCount(tag, count, 1); + _gainControl = readShort(type, count, value); + /** + * **************************************************** The EXIF 2.2 specification (JEITA + * CP-3451, April 2002) incorrectly identifies the type of the GainControl tag (41911) as + * RATIONAL in Table 5 on page 25; it is correctly identified as SHORT in the detailed tag + * description on page 43. If we wanted to accept either SHORT or RATIONAL uncomment the + * following: ***************************************************** checkType (tag, type, + * SHORT, RATIONAL); checkCount (tag, count, 1); if (type == RATIONAL) { Rational rat = + * readRational (count, value); _gainControl = (int) rat.toLong (); _info.setMessage (new + * InfoMessage ("EXIF GainControl " + "tag (41991) is of type RATIONAL, not SHORT")); } else + * { _gainControl = readShort (type, count, value); } + * **************************************************** + */ + } else if (tag == IMAGEUNIQUEID) { + checkType(tag, type, ASCII); + checkCount(tag, count, 33); + _imageUniqueID = readASCII(count, value); + } else if (tag == ISOSPEEDRATINGS) { + checkType(tag, type, SHORT); + _isoSpeedRatings = readShortArray(type, count, value); + } else if (tag == LIGHTSOURCE) { + checkType(tag, type, SHORT); + checkCount(tag, count, 1); + _lightSource = readShort(type, count, value); + } else if (tag == MAKERNOTE) { + checkType(tag, type, UNDEFINED); + _makerNote = readShortArray(type, count, value); + } else if (tag == MAXAPERTUREVALUE) { + checkType(tag, type, RATIONAL); + checkCount(tag, count, 1); + _maxApertureValue = readRational(count, value); + _niso.setMaxApertureValue(_maxApertureValue); + } else if (tag == METERINGMODE) { + checkType(tag, type, SHORT); + checkCount(tag, count, 1); + _meteringMode = readShort(type, count, value); + _niso.setMeteringMode(_meteringMode); + } else if (tag == OECF) { + checkType(tag, type, SHORT); + _oecf = readShortArray(type, count, value); + } else if (tag == PIXELXDIMENSION) { + checkType(tag, type, LONG); + checkCount(tag, count, 1); + _pixelXDimension = readLong(type, count, value); + } else if (tag == PIXELYDIMENSION) { + checkType(tag, type, LONG); + checkCount(tag, count, 1); + _pixelYDimension = readLong(type, count, value); + } else if (tag == RELATEDSOUNDFILE) { + checkType(tag, type, ASCII); + checkCount(tag, count, 13); + _relatedSoundFile = readASCII(count, value); + } else if (tag == SATURATION) { + checkType(tag, type, SHORT); + checkCount(tag, count, 1); + _saturation = readShort(type, count, value); + } else if (tag == SCENECAPTURETYPE) { + checkType(tag, type, SHORT); + checkCount(tag, count, 1); + _sceneCaptureType = readShort(type, count, value); + } else if (tag == SCENETYPE) { + checkType(tag, type, UNDEFINED); + checkCount(tag, count, 1); + _sceneType = readShort(type, count, value); + } else if (tag == SENSINGMETHOD) { + checkType(tag, type, SHORT); + checkCount(tag, count, 1); + _sensingMethod = readShort(type, count, value); + } else if (tag == SHARPNESS) { + checkType(tag, type, SHORT); + checkCount(tag, count, 1); + _sharpness = readShort(type, count, value); + } else if (tag == SHUTTERSPEEDVALUE) { + checkType(tag, type, SRATIONAL); + checkCount(tag, count, 1); + _shutterSpeedValue = readRational(count, value); + } else if (tag == SPATIALFREQUENCYRESPONSE) { + checkType(tag, type, UNDEFINED); + _spatialFrequencyResponse = readShortArray(type, count, value); + } else if (tag == SPECTRALSENSITIVITY) { + checkType(tag, type, ASCII); + _spectralSensitivity = readASCII(count, value); + } else if (tag == SUBJECTAREA) { + checkType(tag, type, SHORT); + _subjectArea = readShortArray(type, count, value); + } else if (tag == SUBJECTDISTANCE) { + checkType(tag, type, RATIONAL); + checkCount(tag, count, 1); + _subjectDistance = readRational(count, value); + } else if (tag == SUBJECTDISTANCERANGE) { + checkType(tag, type, SHORT); + checkCount(tag, count, 1); + _subjectDistanceRange = readShort(type, count, value); + } else if (tag == SUBJECTLOCATION) { + checkType(tag, type, SHORT); + _subjectLocation = readShortArray(type, count, value); + } else if (tag == SUBSECTIME) { + checkType(tag, type, ASCII); + _subSecTime = readASCII(count, value); + } else if (tag == SUBSECTIMEDIGITIZED) { + checkType(tag, type, ASCII); + _subSecTimeDigitized = readASCII(count, value); + } else if (tag == SUBSECTIMEORIGINAL) { + checkType(tag, type, ASCII); + _subSecTimeOriginal = readASCII(count, value); + } else if (tag == USERCOMMENT) { + checkType(tag, type, UNDEFINED); + _userComment = readByteArray(type, count, value); + } else if (tag == WHITEBALANCE) { + checkType(tag, type, SHORT); + checkCount(tag, count, 1); + _whiteBalance = readShort(type, count, value); + } + // Here we check for non-EXIF tags, because some documents use + // standard TIFF tags in an EXIF IFD to provide metadata. + else if (tag == TiffIFD.MAKE) { + checkType(tag, type, ASCII); + String make = readASCII(count, value); + _niso.setScannerManufacturer(make); + _manufacturer = make; + } else if (tag == TiffIFD.MODEL) { + checkType(tag, type, ASCII); + String model = readASCII(count, value); + _niso.setScannerModelName(model); + _model = model; + } else if (tag == TiffIFD.ORIENTATION) { + checkType(tag, type, SHORT); + checkCount(tag, count, 1); + int orient = readShort(type, count, value); + _niso.setOrientation(orient); + _orientation = orient; + } else if (tag == TiffIFD.XRESOLUTION) { + checkType(tag, type, RATIONAL); + checkCount(tag, count, 1); + _niso.setXSamplingFrequency(readRational(count, value)); + } else if (tag == TiffIFD.YRESOLUTION) { + checkType(tag, type, RATIONAL); + checkCount(tag, count, 1); + _niso.setYSamplingFrequency(readRational(count, value)); + } - /** Extracts and returns the Exif property list from a standard - * IFD property header. - */ - public List exifProps (Property pHeader) - { - try { - Property[] pArr = (Property []) pHeader.getValue (); - Property entries = pArr[2]; - return (List) entries.getValue (); - } - catch (Exception e) { - // We could get caught here if we somehow tried to get - // the Exif properties from something that wasn't a - // standard property header. - return null; - } + } catch (IOException e) { + String mess = MessageFormat.format(MessageConstants.TIFF_HUL_13.getMessage(), tag); + JhoveMessage message = + JhoveMessages.getMessageInstance(MessageConstants.TIFF_HUL_13.getId(), mess); + throw new TiffException(message, value); } + } + /* Assemble the userComment, which consists of an array of up to three + * properties: IDCode, Value, and RawValue. + */ + private Property makeUserCommentProperty(int[] rawValue, boolean rawOutput) { + String idCode = "Undefined"; + String cmtValue = null; - /** Lookup an IFD tag. */ - public void lookupTag (int tag, int type, long count, long value) - throws TiffException - { - try { - if (tag == APERTUREVALUE) { - checkType (tag, type, RATIONAL); - checkCount (tag, count, 1); - _apertureValue = readRational (count, value); - } - else if (tag == BRIGHTNESSVALUE) { - checkType (tag, type, SRATIONAL); - checkCount (tag, count, 1); - _brightnessValue = readRational (count, value); - _niso.setBrightness(_brightnessValue); - } - else if (tag == CFAPATTERN) { - checkType (tag, type, UNDEFINED); - _cfaPattern = readByteArray (type, count, value); - } - else if (tag == COLORSPACE) { - checkType (tag, type, SHORT); - checkCount (tag, count, 1); - _colorSpace = readShort (type, count, value); - _niso.setColorSpace(_colorSpace); - } - else if (tag == COMPONENTSCONFIGURATION) { - checkType (tag, type, UNDEFINED); - _componentsConfiguration = readByteArray (type, count, value); - } - else if (tag == COMPRESSEDBITSPERPIXEL) { - checkType (tag, type, RATIONAL); - checkCount (tag, count, 1); - _compressedBitsPerPixel = readRational (count, value); - } - else if (tag == CONTRAST) { - checkType (tag, type, SHORT); - checkCount (tag, count, 1); - _contrast = readShort (type, count, value); - } - else if (tag == CUSTOMRENDERED) { - checkType (tag, type, SHORT); - checkCount (tag, count, 1); - _customRendered = readShort (type, count, value); - } - else if (tag == DATETIMEDIGITIZED) { - checkType (tag, type, ASCII); - checkCount (tag, count, 20); - _dateTimeDigitized = readASCII (count, value); - } - else if (tag == DATETIMEORIGINAL) { - checkType (tag, type, ASCII); - checkCount (tag, count, 20); - _dateTimeOriginal = readASCII (count, value); - } - else if (tag == DEVICESETTINGDESCRIPTION) { - checkType (tag, type, UNDEFINED); - _deviceSettingDescription = readByteArray (type, count, value); - } - else if (tag == DIGITALZOOMRATIO) { - checkType (tag, type, RATIONAL); - checkCount (tag, count, 1); - _digitalZoomRatio = readRational (count, value); - } - else if (tag == EXIFVERSION) { - checkType (tag, type, UNDEFINED); - checkCount (tag, count, 4); - int [] iarray = readShortArray (type, count, value); - char [] carray = new char [iarray.length]; - for (int i=0; iGPSInfoIFD object. - * - * @param offset - * IFD offset - * @param info - * The RepInfo object - * @param raf - * TIFF file - * @param bigEndian - * True if big-endian file - */ - public GPSInfoIFD(long offset, RepInfo info, RandomAccessFile raf, - boolean bigEndian) { - super(offset, info, raf, bigEndian); - - _gpsAltitudeRef = NULL; - _gpsDifferential = NULL; - - /* Set Exif defaults. */ - _gpsVersionID = new int[] { 2, 2, 0, 0 }; - _gpsAltitudeRef = 0; - _gpsSpeedRef = "K"; - _gpsTrackRef = "T"; - _gpsImgDirectionRef = "T"; - _gpsDestBearingRef = "T"; - _gpsDestDistanceRef = "K"; - } - - /****************************************************************** - * PUBLIC INSTANCE METHODS. - ******************************************************************/ - - /** Get the GPSAltitude (6). */ - public Rational getGPSAltitude() { - return _gpsAltitude; - } - - /** Get the GPSAltitudeRef (5). */ - public int getGPSAltitudeRef() { - return _gpsAltitudeRef; - } - - /** Get the GPSDateStamp (29). */ - public String getGPSDateStamp() { - return _gpsDateStamp; - } - - /** Get the GPSDestBearing (24). */ - public Rational getGPSDestBearing() { - return _gpsDestBearing; - } - - /** Get the GPSDestBearingRef (23). */ - public String getGPSDestBearingRef() { - return _gpsDestBearingRef; - } - - /** Get the GPSDestDistance (26). */ - public Rational getGPSDestDistance() { - return _gpsDestDistance; - } - - /** Get the GPSDestDistanceRef (25). */ - public String getGPSDestDistanceRef() { - return _gpsDestDistanceRef; - } - - /** Get the GPSDestLatitude (20). */ - public Rational[] getGPSDestLatitude() { - return _gpsDestLatitude; - } - - /** Get the GPSDestLatitudeRef (19). */ - public String getGPSDestLatitudeRef() { - return _gpsDestLatitudeRef; - } - - /** Get the GPSDestLongitude (22). */ - public Rational[] getGPSDestLongitude() { - return _gpsDestLongitude; - } - - /** Get the GPSDestLongitudeRef (21). */ - public String getGPSDestLongitudeRef() { - return _gpsDestLongitudeRef; - } - - /** Get the GPSDifferential (30). */ - public int getGPSDifferential() { - return _gpsDifferential; - } - - /** Get the GPSDOP (11). */ - public Rational getGPSDOP() { - return _gpsDOP; - } - - /** Get the GPSImgDirection (17). */ - public Rational getGPSImgDirection() { - return _gpsImgDirection; - } - - /** Get the GPSImgDirectionRef (16). */ - public String getGPSImgDirectionRef() { - return _gpsImgDirectionRef; - } - - /** Get the GPSLatitude (2). */ - public Rational[] getGPSLatitude() { - return _gpsLatitude; - } - - /** Get the GPSLatitudeRef (1). */ - public String getGPSLatitudeRef() { - return _gpsLatitudeRef; - } - - /** Get the GPSLongitude (4). */ - public Rational[] getGPSLongitude() { - return _gpsLongitude; - } - - /** Get the GPSLongitudeRef (3). */ - public String getGPSLongitudeRef() { - return _gpsLongitudeRef; - } - - /** Get the GPSMapDatum (18). */ - public String getGPSMapDatum() { - return _gpsMapDatum; - } - - /** Get the GPSMeasureMode (10). */ - public String getGPSMeasureMode() { - return _gpsMeasureMode; - } - - /** Get the GPSProcessingMethod (27). */ - public int[] getGPSProcessingMethod() { - return _gpsProcessingMethod; - } - - /** Get the GPSSatellites (8). */ - public String getGPSSatellites() { - return _gpsSatellites; - } - - /** Get the GPSSpeed (13). */ - public Rational getGPSSpeed() { - return _gpsSpeed; - } - - /** Get the GPSSpeedRef (12). */ - public String getGPSSpeedRef() { - return _gpsSpeedRef; - } - - /** Get the GPSStatus (9). */ - public String getGPStatus() { - return _gpsStatus; - } - - /** Get the GPSTimeStamp (7). */ - public Rational[] getGPTimeStamp() { - return _gpsTimeStamp; - } - - /** Get the GPSTrack (15). */ - public Rational getGPSTrack() { - return _gpsTrack; - } - - /** Get the GPSTrackRef (14). */ - public String getGPSTrackRef() { - return _gpsTrackRef; - } - - /** Get the GPSVersionID (1). */ - public int[] getGPSVersionID() { - return _gpsVersionID; - } - - /** Get the IFD properties. */ - @Override - public Property getProperty(boolean rawOutput) { - List entries = new LinkedList(); - entries.add(new Property("GPSVersionID", PropertyType.STRING, - Integer.toString(_gpsVersionID[0]) + "." - + Integer.toString(_gpsVersionID[1]) + "." - + Integer.toString(_gpsVersionID[2]) + "." - + Integer.toString(_gpsVersionID[3]))); - if (_gpsLatitudeRef != null) { - entries.add(new Property("GPSLatitudeRef", PropertyType.STRING, - _gpsLatitudeRef)); - } - if (_gpsLatitude != null) { - entries.add(new Property("GPSLatitude", PropertyType.RATIONAL, - PropertyArity.ARRAY, _gpsLatitude)); - } - if (_gpsLongitudeRef != null) { - entries.add(new Property("GPSLongitudeRef", PropertyType.STRING, - _gpsLongitudeRef)); - } - if (_gpsLongitude != null) { - entries.add(new Property("GPSLongitude", PropertyType.RATIONAL, - PropertyArity.ARRAY, _gpsLongitude)); - } - entries.add(new Property("GPSAltitudeRef", PropertyType.INTEGER, - new Integer(_gpsAltitudeRef))); - if (_gpsAltitude != null) { - entries.add(new Property("GPSAltitude", PropertyType.RATIONAL, - _gpsAltitude)); - } - if (_gpsTimeStamp != null) { - entries.add(new Property("GPSTimeStamp", PropertyType.RATIONAL, - PropertyArity.ARRAY, _gpsTimeStamp)); - } - if (_gpsSatellites != null) { - entries.add(new Property("GPSSatellites", PropertyType.STRING, - _gpsSatellites)); - } - if (_gpsStatus != null) { - entries.add( - new Property("GPSStatus", PropertyType.STRING, _gpsStatus)); - } - if (_gpsMeasureMode != null) { - entries.add(new Property("GPSMeasureMode", PropertyType.STRING, - _gpsMeasureMode)); - } - if (_gpsDOP != null) { - entries.add(new Property("GPSDOP", PropertyType.RATIONAL, _gpsDOP)); - } - entries.add( - new Property("GPSSpeedRef", PropertyType.STRING, _gpsSpeedRef)); - if (_gpsSpeed != null) { - entries.add( - new Property("GPSSpeed", PropertyType.RATIONAL, _gpsSpeed)); - } - entries.add( - new Property("GPSTrackRef", PropertyType.STRING, _gpsTrackRef)); - if (_gpsTrack != null) { - entries.add( - new Property("GPSTrack", PropertyType.RATIONAL, _gpsTrack)); - } - entries.add(new Property("GPSImgDirectionRef", PropertyType.STRING, - _gpsImgDirectionRef)); - if (_gpsImgDirection != null) { - entries.add(new Property("GPSImgDirection", PropertyType.RATIONAL, - _gpsImgDirection)); - } - if (_gpsMapDatum != null) { - entries.add(new Property("GPSMapDatum", PropertyType.STRING, - _gpsMapDatum)); - } - if (_gpsDestLatitudeRef != null) { - entries.add(new Property("GPSDestLatitudeRef", PropertyType.STRING, - _gpsDestLatitudeRef)); - } - if (_gpsDestLatitude != null) { - entries.add(new Property("GPSDestLatitude", PropertyType.RATIONAL, - PropertyArity.ARRAY, _gpsDestLatitude)); - } - if (_gpsDestLongitudeRef != null) { - entries.add(new Property("GPSDestLongitudeRef", PropertyType.STRING, - _gpsDestLongitudeRef)); - } - if (_gpsDestLongitude != null) { - entries.add(new Property("GPSDestLongitude", PropertyType.RATIONAL, - PropertyArity.ARRAY, _gpsDestLongitude)); - } - entries.add(new Property("GPSDestBearingRef", PropertyType.STRING, - _gpsDestBearingRef)); - if (_gpsDestBearing != null) { - entries.add(new Property("GPSDestBearing", PropertyType.RATIONAL, - _gpsDestBearing)); - } - entries.add(new Property("GPSDestDistanceRef", PropertyType.STRING, - _gpsDestDistanceRef)); - if (_gpsDestDistance != null) { - entries.add(new Property("GPSDestDistance", PropertyType.RATIONAL, - _gpsDestDistance)); - } - if (_gpsDestDistanceRef != null) { - entries.add(new Property("GPSDestDistanceRef", PropertyType.STRING, - _gpsDestDistanceRef)); - } - if (_gpsProcessingMethod != null) { - entries.add( - new Property("GPSProcessingMethod", PropertyType.INTEGER, - PropertyArity.ARRAY, _gpsProcessingMethod)); - } - if (_gpsAreaInformation != null) { - entries.add(new Property("GPSAreaInformation", PropertyType.INTEGER, - PropertyArity.ARRAY, _gpsAreaInformation)); - } - if (_gpsDateStamp != null) { - entries.add(new Property("GPSDateStamp", PropertyType.STRING, - _gpsDateStamp)); - } - entries.add(new Property("GPSDifferential", PropertyType.INTEGER, - new Integer(_gpsDifferential))); - - return propertyHeader("GPSInfo", entries); - } - - /** Lookup an IFD tag. */ - public void lookupTag(int tag, int type, long count, long value) - throws TiffException { - try { - if (tag == GPSALTITUDE) { - checkType(tag, type, RATIONAL); - checkCount(tag, count, 1); - _gpsAltitude = readRational(count, value); - } else if (tag == GPSALTITUDEREF) { - checkType(tag, type, BYTE); - checkCount(tag, count, 1); - _gpsAltitudeRef = readByte(type, count, value); - } else if (tag == GPSDATESTAMP) { - checkType(tag, type, ASCII); - checkCount(tag, count, 11); - _gpsDateStamp = readASCII(count, value); - } else if (tag == GPSDESTBEARING) { - checkType(tag, type, RATIONAL); - checkCount(tag, count, 1); - _gpsDestBearing = readRational(count, value); - } else if (tag == GPSDESTBEARINGREF) { - checkType(tag, type, ASCII); - checkCount(tag, count, 2); - _gpsDestBearingRef = readASCII(count, value); - } else if (tag == GPSDESTDISTANCE) { - checkType(tag, type, RATIONAL); - checkCount(tag, count, 1); - _gpsDestDistance = readRational(count, value); - } else if (tag == GPSDESTDISTANCEREF) { - checkType(tag, type, ASCII); - checkCount(tag, count, 2); - _gpsDestDistanceRef = readASCII(count, value); - } else if (tag == GPSDESTLATITUDE) { - checkType(tag, type, RATIONAL); - checkCount(tag, count, 3); - _gpsDestLatitude = readRationalArray(count, value); - } else if (tag == GPSDESTLATITUDEREF) { - checkType(tag, type, ASCII); - checkCount(tag, count, 2); - _gpsDestLatitudeRef = readASCII(count, value); - } else if (tag == GPSDESTLONGITUDE) { - checkType(tag, type, RATIONAL); - checkCount(tag, count, 3); - _gpsDestLongitude = readRationalArray(count, value); - } else if (tag == GPSDESTLONGITUDEREF) { - checkType(tag, type, ASCII); - checkCount(tag, count, 2); - _gpsDestLongitudeRef = readASCII(count, value); - } else if (tag == GPSDIFFERENTIAL) { - checkType(tag, type, SHORT); - checkCount(tag, count, 1); - _gpsDifferential = readShort(type, count, value); - } else if (tag == GPSDOP) { - checkType(tag, type, RATIONAL); - checkCount(tag, count, 1); - _gpsDOP = readRational(count, value); - } else if (tag == GPSIMGDIRECTION) { - checkType(tag, type, RATIONAL); - checkCount(tag, count, 1); - _gpsImgDirection = readRational(count, value); - } else if (tag == GPSIMGDIRECTIONREF) { - checkType(tag, type, ASCII); - checkCount(tag, count, 2); - _gpsImgDirectionRef = readASCII(count, value); - } else if (tag == GPSLATITUDE) { - checkType(tag, type, RATIONAL); - checkCount(tag, count, 3); - _gpsLatitude = readRationalArray(count, value); - } else if (tag == GPSLATITUDEREF) { - checkType(tag, type, ASCII); - checkCount(tag, count, 2); - _gpsLatitudeRef = readASCII(count, value); - } else if (tag == GPSLONGITUDE) { - checkType(tag, type, RATIONAL); - checkCount(tag, count, 3); - _gpsLongitude = readRationalArray(count, value); - } else if (tag == GPSLONGITUDEREF) { - checkType(tag, type, ASCII); - checkCount(tag, count, 2); - _gpsLongitudeRef = readASCII(count, value); - } else if (tag == GPSMAPDATUM) { - checkType(tag, type, ASCII); - _gpsMapDatum = readASCII(count, value); - } else if (tag == GPSMEASUREMODE) { - checkType(tag, type, ASCII); - checkCount(tag, count, 2); - _gpsMeasureMode = readASCII(count, value); - } else if (tag == GPSPROCESSINGMETHOD) { - checkType(tag, type, UNDEFINED); - _gpsProcessingMethod = readByteArray(type, count, value); - } else if (tag == GPSSATELLITES) { - checkType(tag, type, ASCII); - _gpsSatellites = readASCII(count, value); - } else if (tag == GPSSPEED) { - checkType(tag, type, RATIONAL); - checkCount(tag, count, 1); - _gpsSpeed = readRational(count, value); - } else if (tag == GPSSPEEDREF) { - checkType(tag, type, ASCII); - checkCount(tag, count, 2); - _gpsSpeedRef = readASCII(count, value); - } else if (tag == GPSSTATUS) { - checkType(tag, type, ASCII); - checkCount(tag, count, 2); - _gpsStatus = readASCII(count, value); - } else if (tag == GPSTIMESTAMP) { - checkType(tag, type, RATIONAL); - checkCount(tag, count, 3); - _gpsTimeStamp = readRationalArray(count, value); - } else if (tag == GPSTRACK) { - checkType(tag, type, RATIONAL); - checkCount(tag, count, 1); - _gpsTrack = readRational(count, value); - } else if (tag == GPSTRACKREF) { - checkType(tag, type, ASCII); - checkCount(tag, count, 2); - _gpsTrackRef = readASCII(count, value); - } else if (tag == GPSVERSIONID) { - checkType(tag, type, BYTE); - checkCount(tag, count, 4); - _gpsVersionID = readByteArray(type, count, value); - } else { - _info.setMessage(new ErrorMessage(MessageConstants.TIFF_HUL_17, - MessageFormat.format( - MessageConstants.TIFF_HUL_17_SUB.getMessage(), - tag), - value)); - } - } catch (IOException e) { - String mess = MessageFormat - .format(MessageConstants.TIFF_HUL_18.getMessage(), tag); - JhoveMessage message = JhoveMessages.getMessageInstance( - MessageConstants.TIFF_HUL_18.getId(), mess); - throw new TiffException(message, value); - } - } + /** + * **************************************************************** PRIVATE CLASS FIELDS. + * **************************************************************** + */ + + /** GPSVersionID tag. */ + private static final int GPSVERSIONID = 0; + /** GPSLatitudeRef tag. */ + private static final int GPSLATITUDEREF = 1; + /** GPSLatitude tag. */ + private static final int GPSLATITUDE = 2; + /** GPSLongitudeRef tag. */ + private static final int GPSLONGITUDEREF = 3; + /** GPSLongitude tag. */ + private static final int GPSLONGITUDE = 4; + /** GPSAltitudeRef tag. */ + private static final int GPSALTITUDEREF = 5; + /** GPSAltitude tag. */ + private static final int GPSALTITUDE = 6; + /** GPSTimeStamp tag. */ + private static final int GPSTIMESTAMP = 7; + /** GPSSatellites tag. */ + private static final int GPSSATELLITES = 8; + /** GPSStatus tag. */ + private static final int GPSSTATUS = 9; + /** GPSMeasureMode tag. */ + private static final int GPSMEASUREMODE = 10; + /** GPSDOP tag. */ + private static final int GPSDOP = 11; + /** GPSSpeedRef tag. */ + private static final int GPSSPEEDREF = 12; + /** GPSSpeed tag. */ + private static final int GPSSPEED = 13; + /** GPSTrackRef tag. */ + private static final int GPSTRACKREF = 14; + /** GPSTrack tag. */ + private static final int GPSTRACK = 15; + /** GPSImgDirectionRef tag. */ + private static final int GPSIMGDIRECTIONREF = 16; + /** GPSImgDirection tag. */ + private static final int GPSIMGDIRECTION = 17; + /** GPSMapDatum tag. */ + private static final int GPSMAPDATUM = 18; + /** GPSDestLatitudeRef tag. */ + private static final int GPSDESTLATITUDEREF = 19; + /** GPSDestLatitude tag. */ + private static final int GPSDESTLATITUDE = 20; + /** GPSDestLongitudeRef tag. */ + private static final int GPSDESTLONGITUDEREF = 21; + /** GPSDestLongitude tag. */ + private static final int GPSDESTLONGITUDE = 22; + /** GPSDestBearingRef tag. */ + private static final int GPSDESTBEARINGREF = 23; + /** GPSDestBearing tag. */ + private static final int GPSDESTBEARING = 24; + /** GPSDestDistanceRef tag. */ + private static final int GPSDESTDISTANCEREF = 25; + /** GPSDestDistance tag. */ + private static final int GPSDESTDISTANCE = 26; + /** GPSProcessingMethod tag. */ + private static final int GPSPROCESSINGMETHOD = 27; + /** GPSAreaInformation tag. */ + private static final int GPSAREAINFORMATION = 28; + /** GPSDateStamp tag. */ + private static final int GPSDATESTAMP = 29; + /** GPSDifferential tag. */ + private static final int GPSDIFFERENTIAL = 30; + + /** + * **************************************************************** PRIVATE INSTANCE FIELDS. + * **************************************************************** + */ + + /** GPSVersionID (Tag 0). */ + private int[] _gpsVersionID; + /** GPSLatitudeRef (1). */ + private String _gpsLatitudeRef; + /** GPSLatitude (2). */ + private Rational[] _gpsLatitude; + /** GPSLongitudeRef (3). */ + private String _gpsLongitudeRef; + /** GPSLongitude (4). */ + private Rational[] _gpsLongitude; + /** GPSAltitudeRef (5). */ + private int _gpsAltitudeRef; + /** GPSAltitude (6). */ + private Rational _gpsAltitude; + /** GPSTimeStamp (7). */ + private Rational[] _gpsTimeStamp; + /** GPSSatellites (8). */ + private String _gpsSatellites; + /** GPSStatus (9). */ + private String _gpsStatus; + /** GPSMeasureMode (10). */ + private String _gpsMeasureMode; + /** GPSDOP (11). */ + private Rational _gpsDOP; + /** GPSSpeedRef (12). */ + private String _gpsSpeedRef; + /** GPSSpeed (13). */ + private Rational _gpsSpeed; + /** GPSTrackRef (14). */ + private String _gpsTrackRef; + /** GPSTrack (15). */ + private Rational _gpsTrack; + /** GPSImgDirectionRef (16). */ + private String _gpsImgDirectionRef; + /** GPSImgDirection (17). */ + private Rational _gpsImgDirection; + /** GPSMapDatum (18). */ + private String _gpsMapDatum; + /** GPSDestLatitudeRef (19). */ + private String _gpsDestLatitudeRef; + /* GPSDestLatitude (20). */ + private Rational[] _gpsDestLatitude; + /** GPSDestLongitudeRef (21). */ + private String _gpsDestLongitudeRef; + /** GPSDestLongitude (22). */ + private Rational[] _gpsDestLongitude; + /** GPSDestBearingRef (23). */ + private String _gpsDestBearingRef; + /** GPSDestBearing (24). */ + private Rational _gpsDestBearing; + /** GPSDestDistanceRef (25). */ + private String _gpsDestDistanceRef; + /** GPSDestDistance (26). */ + private Rational _gpsDestDistance; + + /* The following four tags are for Exif only, not TIFF/EP. */ + + /** GPSProcessingMethod (27). */ + private int[] _gpsProcessingMethod; + /** GPSAreaInformation (28). */ + private int[] _gpsAreaInformation; + /** GPSDateStamp (29). */ + private String _gpsDateStamp; + /** GPSDifferential (30). */ + private int _gpsDifferential; + + /** + * **************************************************************** CLASS CONSTRUCTOR. + * **************************************************************** + */ + + /** + * Instantiate an GPSInfoIFD object. + * + * @param offset IFD offset + * @param info The RepInfo object + * @param raf TIFF file + * @param bigEndian True if big-endian file + */ + public GPSInfoIFD(long offset, RepInfo info, RandomAccessFile raf, boolean bigEndian) { + super(offset, info, raf, bigEndian); + + _gpsAltitudeRef = NULL; + _gpsDifferential = NULL; + + /* Set Exif defaults. */ + _gpsVersionID = new int[] {2, 2, 0, 0}; + _gpsAltitudeRef = 0; + _gpsSpeedRef = "K"; + _gpsTrackRef = "T"; + _gpsImgDirectionRef = "T"; + _gpsDestBearingRef = "T"; + _gpsDestDistanceRef = "K"; + } + + /** + * **************************************************************** PUBLIC INSTANCE METHODS. + * **************************************************************** + */ + + /** Get the GPSAltitude (6). */ + public Rational getGPSAltitude() { + return _gpsAltitude; + } + + /** Get the GPSAltitudeRef (5). */ + public int getGPSAltitudeRef() { + return _gpsAltitudeRef; + } + + /** Get the GPSDateStamp (29). */ + public String getGPSDateStamp() { + return _gpsDateStamp; + } + + /** Get the GPSDestBearing (24). */ + public Rational getGPSDestBearing() { + return _gpsDestBearing; + } + + /** Get the GPSDestBearingRef (23). */ + public String getGPSDestBearingRef() { + return _gpsDestBearingRef; + } + + /** Get the GPSDestDistance (26). */ + public Rational getGPSDestDistance() { + return _gpsDestDistance; + } + + /** Get the GPSDestDistanceRef (25). */ + public String getGPSDestDistanceRef() { + return _gpsDestDistanceRef; + } + + /** Get the GPSDestLatitude (20). */ + public Rational[] getGPSDestLatitude() { + return _gpsDestLatitude; + } + + /** Get the GPSDestLatitudeRef (19). */ + public String getGPSDestLatitudeRef() { + return _gpsDestLatitudeRef; + } + + /** Get the GPSDestLongitude (22). */ + public Rational[] getGPSDestLongitude() { + return _gpsDestLongitude; + } + + /** Get the GPSDestLongitudeRef (21). */ + public String getGPSDestLongitudeRef() { + return _gpsDestLongitudeRef; + } + + /** Get the GPSDifferential (30). */ + public int getGPSDifferential() { + return _gpsDifferential; + } + + /** Get the GPSDOP (11). */ + public Rational getGPSDOP() { + return _gpsDOP; + } + + /** Get the GPSImgDirection (17). */ + public Rational getGPSImgDirection() { + return _gpsImgDirection; + } + + /** Get the GPSImgDirectionRef (16). */ + public String getGPSImgDirectionRef() { + return _gpsImgDirectionRef; + } + + /** Get the GPSLatitude (2). */ + public Rational[] getGPSLatitude() { + return _gpsLatitude; + } + + /** Get the GPSLatitudeRef (1). */ + public String getGPSLatitudeRef() { + return _gpsLatitudeRef; + } + + /** Get the GPSLongitude (4). */ + public Rational[] getGPSLongitude() { + return _gpsLongitude; + } + + /** Get the GPSLongitudeRef (3). */ + public String getGPSLongitudeRef() { + return _gpsLongitudeRef; + } + + /** Get the GPSMapDatum (18). */ + public String getGPSMapDatum() { + return _gpsMapDatum; + } + + /** Get the GPSMeasureMode (10). */ + public String getGPSMeasureMode() { + return _gpsMeasureMode; + } + + /** Get the GPSProcessingMethod (27). */ + public int[] getGPSProcessingMethod() { + return _gpsProcessingMethod; + } + + /** Get the GPSSatellites (8). */ + public String getGPSSatellites() { + return _gpsSatellites; + } + + /** Get the GPSSpeed (13). */ + public Rational getGPSSpeed() { + return _gpsSpeed; + } + + /** Get the GPSSpeedRef (12). */ + public String getGPSSpeedRef() { + return _gpsSpeedRef; + } + + /** Get the GPSStatus (9). */ + public String getGPStatus() { + return _gpsStatus; + } + + /** Get the GPSTimeStamp (7). */ + public Rational[] getGPTimeStamp() { + return _gpsTimeStamp; + } + + /** Get the GPSTrack (15). */ + public Rational getGPSTrack() { + return _gpsTrack; + } + + /** Get the GPSTrackRef (14). */ + public String getGPSTrackRef() { + return _gpsTrackRef; + } + + /** Get the GPSVersionID (1). */ + public int[] getGPSVersionID() { + return _gpsVersionID; + } + + /** Get the IFD properties. */ + @Override + public Property getProperty(boolean rawOutput) { + List entries = new LinkedList(); + entries.add( + new Property( + "GPSVersionID", + PropertyType.STRING, + Integer.toString(_gpsVersionID[0]) + + "." + + Integer.toString(_gpsVersionID[1]) + + "." + + Integer.toString(_gpsVersionID[2]) + + "." + + Integer.toString(_gpsVersionID[3]))); + if (_gpsLatitudeRef != null) { + entries.add(new Property("GPSLatitudeRef", PropertyType.STRING, _gpsLatitudeRef)); + } + if (_gpsLatitude != null) { + entries.add( + new Property("GPSLatitude", PropertyType.RATIONAL, PropertyArity.ARRAY, _gpsLatitude)); + } + if (_gpsLongitudeRef != null) { + entries.add(new Property("GPSLongitudeRef", PropertyType.STRING, _gpsLongitudeRef)); + } + if (_gpsLongitude != null) { + entries.add( + new Property("GPSLongitude", PropertyType.RATIONAL, PropertyArity.ARRAY, _gpsLongitude)); + } + entries.add(new Property("GPSAltitudeRef", PropertyType.INTEGER, new Integer(_gpsAltitudeRef))); + if (_gpsAltitude != null) { + entries.add(new Property("GPSAltitude", PropertyType.RATIONAL, _gpsAltitude)); + } + if (_gpsTimeStamp != null) { + entries.add( + new Property("GPSTimeStamp", PropertyType.RATIONAL, PropertyArity.ARRAY, _gpsTimeStamp)); + } + if (_gpsSatellites != null) { + entries.add(new Property("GPSSatellites", PropertyType.STRING, _gpsSatellites)); + } + if (_gpsStatus != null) { + entries.add(new Property("GPSStatus", PropertyType.STRING, _gpsStatus)); + } + if (_gpsMeasureMode != null) { + entries.add(new Property("GPSMeasureMode", PropertyType.STRING, _gpsMeasureMode)); + } + if (_gpsDOP != null) { + entries.add(new Property("GPSDOP", PropertyType.RATIONAL, _gpsDOP)); + } + entries.add(new Property("GPSSpeedRef", PropertyType.STRING, _gpsSpeedRef)); + if (_gpsSpeed != null) { + entries.add(new Property("GPSSpeed", PropertyType.RATIONAL, _gpsSpeed)); + } + entries.add(new Property("GPSTrackRef", PropertyType.STRING, _gpsTrackRef)); + if (_gpsTrack != null) { + entries.add(new Property("GPSTrack", PropertyType.RATIONAL, _gpsTrack)); + } + entries.add(new Property("GPSImgDirectionRef", PropertyType.STRING, _gpsImgDirectionRef)); + if (_gpsImgDirection != null) { + entries.add(new Property("GPSImgDirection", PropertyType.RATIONAL, _gpsImgDirection)); + } + if (_gpsMapDatum != null) { + entries.add(new Property("GPSMapDatum", PropertyType.STRING, _gpsMapDatum)); + } + if (_gpsDestLatitudeRef != null) { + entries.add(new Property("GPSDestLatitudeRef", PropertyType.STRING, _gpsDestLatitudeRef)); + } + if (_gpsDestLatitude != null) { + entries.add( + new Property( + "GPSDestLatitude", PropertyType.RATIONAL, PropertyArity.ARRAY, _gpsDestLatitude)); + } + if (_gpsDestLongitudeRef != null) { + entries.add(new Property("GPSDestLongitudeRef", PropertyType.STRING, _gpsDestLongitudeRef)); + } + if (_gpsDestLongitude != null) { + entries.add( + new Property( + "GPSDestLongitude", PropertyType.RATIONAL, PropertyArity.ARRAY, _gpsDestLongitude)); + } + entries.add(new Property("GPSDestBearingRef", PropertyType.STRING, _gpsDestBearingRef)); + if (_gpsDestBearing != null) { + entries.add(new Property("GPSDestBearing", PropertyType.RATIONAL, _gpsDestBearing)); + } + entries.add(new Property("GPSDestDistanceRef", PropertyType.STRING, _gpsDestDistanceRef)); + if (_gpsDestDistance != null) { + entries.add(new Property("GPSDestDistance", PropertyType.RATIONAL, _gpsDestDistance)); + } + if (_gpsDestDistanceRef != null) { + entries.add(new Property("GPSDestDistanceRef", PropertyType.STRING, _gpsDestDistanceRef)); + } + if (_gpsProcessingMethod != null) { + entries.add( + new Property( + "GPSProcessingMethod", + PropertyType.INTEGER, + PropertyArity.ARRAY, + _gpsProcessingMethod)); + } + if (_gpsAreaInformation != null) { + entries.add( + new Property( + "GPSAreaInformation", + PropertyType.INTEGER, + PropertyArity.ARRAY, + _gpsAreaInformation)); + } + if (_gpsDateStamp != null) { + entries.add(new Property("GPSDateStamp", PropertyType.STRING, _gpsDateStamp)); + } + entries.add( + new Property("GPSDifferential", PropertyType.INTEGER, new Integer(_gpsDifferential))); + + return propertyHeader("GPSInfo", entries); + } + + /** Lookup an IFD tag. */ + public void lookupTag(int tag, int type, long count, long value) throws TiffException { + try { + if (tag == GPSALTITUDE) { + checkType(tag, type, RATIONAL); + checkCount(tag, count, 1); + _gpsAltitude = readRational(count, value); + } else if (tag == GPSALTITUDEREF) { + checkType(tag, type, BYTE); + checkCount(tag, count, 1); + _gpsAltitudeRef = readByte(type, count, value); + } else if (tag == GPSDATESTAMP) { + checkType(tag, type, ASCII); + checkCount(tag, count, 11); + _gpsDateStamp = readASCII(count, value); + } else if (tag == GPSDESTBEARING) { + checkType(tag, type, RATIONAL); + checkCount(tag, count, 1); + _gpsDestBearing = readRational(count, value); + } else if (tag == GPSDESTBEARINGREF) { + checkType(tag, type, ASCII); + checkCount(tag, count, 2); + _gpsDestBearingRef = readASCII(count, value); + } else if (tag == GPSDESTDISTANCE) { + checkType(tag, type, RATIONAL); + checkCount(tag, count, 1); + _gpsDestDistance = readRational(count, value); + } else if (tag == GPSDESTDISTANCEREF) { + checkType(tag, type, ASCII); + checkCount(tag, count, 2); + _gpsDestDistanceRef = readASCII(count, value); + } else if (tag == GPSDESTLATITUDE) { + checkType(tag, type, RATIONAL); + checkCount(tag, count, 3); + _gpsDestLatitude = readRationalArray(count, value); + } else if (tag == GPSDESTLATITUDEREF) { + checkType(tag, type, ASCII); + checkCount(tag, count, 2); + _gpsDestLatitudeRef = readASCII(count, value); + } else if (tag == GPSDESTLONGITUDE) { + checkType(tag, type, RATIONAL); + checkCount(tag, count, 3); + _gpsDestLongitude = readRationalArray(count, value); + } else if (tag == GPSDESTLONGITUDEREF) { + checkType(tag, type, ASCII); + checkCount(tag, count, 2); + _gpsDestLongitudeRef = readASCII(count, value); + } else if (tag == GPSDIFFERENTIAL) { + checkType(tag, type, SHORT); + checkCount(tag, count, 1); + _gpsDifferential = readShort(type, count, value); + } else if (tag == GPSDOP) { + checkType(tag, type, RATIONAL); + checkCount(tag, count, 1); + _gpsDOP = readRational(count, value); + } else if (tag == GPSIMGDIRECTION) { + checkType(tag, type, RATIONAL); + checkCount(tag, count, 1); + _gpsImgDirection = readRational(count, value); + } else if (tag == GPSIMGDIRECTIONREF) { + checkType(tag, type, ASCII); + checkCount(tag, count, 2); + _gpsImgDirectionRef = readASCII(count, value); + } else if (tag == GPSLATITUDE) { + checkType(tag, type, RATIONAL); + checkCount(tag, count, 3); + _gpsLatitude = readRationalArray(count, value); + } else if (tag == GPSLATITUDEREF) { + checkType(tag, type, ASCII); + checkCount(tag, count, 2); + _gpsLatitudeRef = readASCII(count, value); + } else if (tag == GPSLONGITUDE) { + checkType(tag, type, RATIONAL); + checkCount(tag, count, 3); + _gpsLongitude = readRationalArray(count, value); + } else if (tag == GPSLONGITUDEREF) { + checkType(tag, type, ASCII); + checkCount(tag, count, 2); + _gpsLongitudeRef = readASCII(count, value); + } else if (tag == GPSMAPDATUM) { + checkType(tag, type, ASCII); + _gpsMapDatum = readASCII(count, value); + } else if (tag == GPSMEASUREMODE) { + checkType(tag, type, ASCII); + checkCount(tag, count, 2); + _gpsMeasureMode = readASCII(count, value); + } else if (tag == GPSPROCESSINGMETHOD) { + checkType(tag, type, UNDEFINED); + _gpsProcessingMethod = readByteArray(type, count, value); + } else if (tag == GPSSATELLITES) { + checkType(tag, type, ASCII); + _gpsSatellites = readASCII(count, value); + } else if (tag == GPSSPEED) { + checkType(tag, type, RATIONAL); + checkCount(tag, count, 1); + _gpsSpeed = readRational(count, value); + } else if (tag == GPSSPEEDREF) { + checkType(tag, type, ASCII); + checkCount(tag, count, 2); + _gpsSpeedRef = readASCII(count, value); + } else if (tag == GPSSTATUS) { + checkType(tag, type, ASCII); + checkCount(tag, count, 2); + _gpsStatus = readASCII(count, value); + } else if (tag == GPSTIMESTAMP) { + checkType(tag, type, RATIONAL); + checkCount(tag, count, 3); + _gpsTimeStamp = readRationalArray(count, value); + } else if (tag == GPSTRACK) { + checkType(tag, type, RATIONAL); + checkCount(tag, count, 1); + _gpsTrack = readRational(count, value); + } else if (tag == GPSTRACKREF) { + checkType(tag, type, ASCII); + checkCount(tag, count, 2); + _gpsTrackRef = readASCII(count, value); + } else if (tag == GPSVERSIONID) { + checkType(tag, type, BYTE); + checkCount(tag, count, 4); + _gpsVersionID = readByteArray(type, count, value); + } else { + _info.setMessage( + new ErrorMessage( + MessageConstants.TIFF_HUL_17, + MessageFormat.format(MessageConstants.TIFF_HUL_17_SUB.getMessage(), tag), + value)); + } + } catch (IOException e) { + String mess = MessageFormat.format(MessageConstants.TIFF_HUL_18.getMessage(), tag); + JhoveMessage message = + JhoveMessages.getMessageInstance(MessageConstants.TIFF_HUL_18.getId(), mess); + throw new TiffException(message, value); + } + } } diff --git a/jhove-modules/tiff-hul/src/main/java/edu/harvard/hul/ois/jhove/module/tiff/GeoTiffStrings.java b/jhove-modules/tiff-hul/src/main/java/edu/harvard/hul/ois/jhove/module/tiff/GeoTiffStrings.java index 50820ec86..b8edf8916 100644 --- a/jhove-modules/tiff-hul/src/main/java/edu/harvard/hul/ois/jhove/module/tiff/GeoTiffStrings.java +++ b/jhove-modules/tiff-hul/src/main/java/edu/harvard/hul/ois/jhove/module/tiff/GeoTiffStrings.java @@ -1,2077 +1,2010 @@ -/********************************************************************** - * Jhove - JSTOR/Harvard Object Validation Environment - * Copyright 2003 by JSTOR and the President and Fellows of Harvard College - **********************************************************************/ - +/** + * ******************************************************************** Jhove - JSTOR/Harvard Object + * Validation Environment Copyright 2003 by JSTOR and the President and Fellows of Harvard College + * ******************************************************************** + */ package edu.harvard.hul.ois.jhove.module.tiff; /** - * This class contains indexed string arrays for the - * various keys that are used by GeoTIFF. It contains - * no code and never needs to be instantiated. + * This class contains indexed string arrays for the various keys that are used by GeoTIFF. It + * contains no code and never needs to be instantiated. */ -public class GeoTiffStrings -{ - /* GeoTIFF general codes - * Derived from European Petroleum Survey Group (ESPG) Geodesy parameters, - * v2.1, June 2, 1995 - * Copied 2003-08-18 from - */ - /* 6.3.1.1 Model type codes */ +public class GeoTiffStrings { + /* GeoTIFF general codes + * Derived from European Petroleum Survey Group (ESPG) Geodesy parameters, + * v2.1, June 2, 1995 + * Copied 2003-08-18 from + */ + /* 6.3.1.1 Model type codes */ + + public static final int[] MODELTYPE_INDEX = {0, 1, 2, 3, 32767}; + public static final String[] MODELTYPE = { + "Undefined", + "Projection coordinate system", + "Geographic latitude-longitude system (FGDC geographic)", + "Geocentric (X,Y,Z) coordinate system (FGDC planar-projected)", + "User-defined" + }; + + /* 6.3.1.2 Raster type codes */ + + public static final int[] RASTERTYPE_INDEX = {0, 1, 2, 32767}; + public static final String[] RASTERTYPE = { + "Undefined", "Pixel is area", "Pixel is point", "User-defined" + }; + + /* 6.3.1.3 Linear unit codes */ + + public static final int[] LINEARUNITS_INDEX = { + 0, 9001, 9002, 9003, 9004, 9005, 9006, 9007, 9008, 9009, 9010, 9011, 9012, 9013, 9014, 9015, + 32767 + }; + public static final String[] LINEARUNITS = { + "Undefined", + "Meter", + "Foot", + "Foot (US survey)", + "Foot (modified American)", + "Foot (Clarke)", + "Foot (Indian)", + "Link", + "Link (Benoit)", + "Link (Sears)", + "Chain (Benoit)", + "Chain (Sears)", + "Yard (Sears)", + "Yard (Indian)", + "Fathom", + "Mile (International nautical)", + "User-defined" + }; - public final static int[] MODELTYPE_INDEX ={ - 0, 1, 2, 3, 32767 - }; - public final static String[] MODELTYPE = { - "Undefined", - "Projection coordinate system", - "Geographic latitude-longitude system (FGDC geographic)", - "Geocentric (X,Y,Z) coordinate system (FGDC planar-projected)", - "User-defined" - }; + /* 6.3.1.4 Angular unit codes */ - /* 6.3.1.2 Raster type codes */ + public static final int[] ANGULARUNITS_INDEX = { + 0, 9101, 9102, 9103, 9104, 9105, 9106, 9107, 9108, 32767 + }; + public static final String[] ANGULARUNITS = { + "Undefined", + "Radian", + "Degree", + "Arc minute", + "Second", + "Grad", + "Gon", + "DMS", + "DMS hemisphere", + "User-defined" + }; - public final static int[] RASTERTYPE_INDEX = { - 0, 1, 2, 32767 - }; - public final static String[] RASTERTYPE = { - "Undefined", - "Pixel is area", - "Pixel is point", - "User-defined" - }; + /* 6.3.2.1 Geographic CS type codes */ - /* 6.3.1.3 Linear unit codes */ + public static final int[] GEOGRAPHICS_INDEX = { + 0, 4001, 4002, 4003, 4004, 4005, 4006, 4007, 4008, 4009, 4010, 4011, 4012, 4013, 4014, 4015, + 4016, 4017, 4018, 4019, 4020, 4021, 4022, 4023, 4024, 4025, 4026, 4027, 4028, 4029, 4030, 4031, + 4032, 4033, 4034, 4035, 4201, 4202, 4203, 4204, 4205, 4206, 4207, 4208, 4209, 4210, 4211, 4212, + 4213, 4214, 4215, 4216, 4217, 4218, 4219, 4220, 4221, 4222, 4223, 4224, 4225, 4226, 4227, 4228, + 4229, 4230, 4231, 4232, 4233, 4234, 4235, 4236, 4237, 4238, 4239, 4240, 4241, 4242, 4243, 4244, + 4245, 4246, 4247, 4248, 4249, 4250, 4251, 4252, 4253, 4254, 4255, 4256, 4257, 4258, 4259, 4260, + 4261, 4262, 4263, 4264, 4265, 4266, 4267, 4268, 4269, 4270, 4271, 4272, 4273, 4274, 4275, 4276, + 4277, 4278, 4279, 4280, 4281, 4282, 4283, 4284, 4285, 4286, 4287, 4288, 4289, 4290, 4291, 4292, + 4293, 4294, 4295, 4296, 4297, 4298, 4299, 4300, 4301, 4302, 4303, 4304, 4305, 4306, 4307, 4308, + 4309, 4310, 4311, 4312, 4313, 4314, 4315, 4801, 4802, 4803, 4804, 4805, 4806, 4807, 4808, 4809, + 4810, 4811, 4812, 4813, 4901, 4902, 32767 + }; + public static final String[] GEOGRAPHICS = { + "Undefined", + "Ellipsoid Airy 1830", + "Ellipsoid Airy Modified 1849", + "Ellipsoid Australian National Spheroid", + "Ellipsoid Bessel 1841", + "Ellipsoid Bessel modified", + "Ellipsoid Bessel Namibia", + "Ellipsoid Clarke 1858", + "Ellipsoid Clarke 1866", + "Ellipsoid Clarke 1866 Michigan", + "Ellipsoid Clarke 1880 Benoit", + "Ellipsoid Clarke 1880 IGN", + "Ellipsoid Clarke 1880 RGS", + "Ellipsoid Clarke 1880 arc", + "Ellipsoid Clarke 1880 SGA 1922", + "Ellipsoid Everest 1830-1937 adjustment", + "Ellipsoid Everest 1830-1967 definition", + "Ellipsoid Everest 1830-1975 definition", + "Ellipsoid Everest 1830 modified", + "Ellipsoid GRS 1980", + "Ellipsoid Helmert 1906", + "Ellipsoid Indonesian national spheroid", + "Ellipsoid international 1924", + "Ellipsoid international 1967", + "Ellipsoid Krassowsky 1940", + "Ellipsoid NWL9D", + "Ellipsoid NWL10D", + "Ellipsoid Plessis 1817", + "Ellipsoid Struve 1860", + "Ellipsoid war office", + "Ellipsoid WGS84", + "Ellipsoid GEM10C", + "Ellipsoid OSU86F", + "Ellipsoid OSU91A", + "Ellipsoid Clarke 1880", + "Ellipsoid sphere", + "Adindan", + "AGD66", + "AGD84", + "Ain el Abd", + "Afgooye", + "Agadez", + "Lisbon", + "Aratu", + "Arc 1950", + "Arc 1960", + "Batavia", + "Barbados", + "Beduaram", + "Beijing 1954", + "Belge 1950", + "Bermuda 1957", + "Bern 1898", + "Bogota", + "Bukit Rimpah", + "Camacupa", + "Campo Inchauspe", + "Cape", + "Carthage", + "Chua", + "Corrego Alegre", + "Cote d Ivoire", + "Deir ez Zor", + "Douala", + "Egypt 1907", + "ED50", + "ED87", + "Fahud", + "Gandajika 1970", + "Garoua", + "Guyane Francaise", + "Hu Tzu Shan", + "HD72", + "ID74", + "Indian 1954", + "Indian 1975", + "Jamaica 1875", + "JAD69", + "Kalianpur", + "Kandawala", + "Kertau", + "KOC", + "La Canoa", + "PSAD56", + "Lake", + "Leigon", + "Liberia 1964", + "Lome", + "Luzon 1911", + "Hito XVIII 1963", + "Herat north", + "Mahe 1971", + "Makassar", + "EUREF89", + "Malongo 1987", + "Manoca", + "Merchich", + "Massawa", + "Minna", + "Mhast", + "Monte Mario", + "M poraloko", + "NAD27", + "NAD Michigan", + "NAD83", + "Nahrwan 1967", + "Naparima 1972", + "GD49", + "NGO 1948", + "Datum 73", + "NTF", + "NSWC 9Z 2", + "OSGB 1936", + "OSGB70", + "OS SN80", + "Padang", + "Palestine 1923", + "Pointe Noire", + "GDA94", + "Pulkovo 1942", + "Qatar", + "Qatar 1948", + "Qornoq", + "Loma Quintana", + "Amersfoort", + "RT38", + "SAD69", + "Sapper Hill 1943", + "Schwarzeck", + "Segora", + "Serindung", + "Sudan", + "Tananarive", + "Timbalai 1948", + "TM65", + "TM75", + "Tokyo", + "Trinidad 1903", + "TC 1948", + "Voirol 1875", + "Voirol Unifie", + "Bern 1938", + "Nord Sahara 1959", + "Stockholm 1938", + "Yacare", + "Yoff", + "Zanderij", + "MGI", + "Belge 1972", + "DHDN", + "Conakry 1905", + "WGS 72", + "WGS 72BE", + "WGS 84", + "Bern 1898 Bern", + "Bogota Bogota", + "Lisbon Lisbon", + "Makassar Jakarta", + "MGI Ferro", + "Monte Mario Rome", + "NTF Paris", + "Padang Jakarta", + "Belge 1950 Brussels", + "Tananarive Paris", + "Voirol 1875 Paris", + "Voirol Unifie Paris", + "Batavia Jakarta", + "ATF Paris", + "NDG Paris", + "User-defined" + }; - public final static int[] LINEARUNITS_INDEX = { - 0, 9001, 9002, 9003, 9004, 9005, 9006, 9007, 9008, 9009, 9010, 9011, - 9012, 9013, 9014, 9015, 32767 - }; - public final static String[] LINEARUNITS = { - "Undefined", - "Meter", - "Foot", - "Foot (US survey)", - "Foot (modified American)", - "Foot (Clarke)", - "Foot (Indian)", - "Link", - "Link (Benoit)", - "Link (Sears)", - "Chain (Benoit)", - "Chain (Sears)", - "Yard (Sears)", - "Yard (Indian)", - "Fathom", - "Mile (International nautical)", - "User-defined" - }; + /* 6.3.2.2 Geodetic datum codes */ - /* 6.3.1.4 Angular unit codes */ + public static final int[] GEODETICDATUM_INDEX = { + 6001, 6002, 6003, 6004, 6005, 6006, 6007, 6008, 6009, 6010, 6011, + 6012, 6013, 6014, 6015, 6016, 6017, 6018, 6019, 6020, 6021, 6022, + 6023, 6024, 6025, 6026, 6027, 6028, 6029, 6030, 6031, 6032, 6033, + 6034, 6035, 6201, 6202, 6203, 6204, 6205, 6206, 6207, 6208, 6209, + 6210, 6211, 6212, 6213, 6214, 6215, 6216, 6217, 6218, 6219, 6220, + 6221, 6222, 6223, 6224, 6225, 6226, 6227, 6228, 6229, 6230, 6231, + 6232, 6233, 6234, 6235, 6236, 6237, 6238, 6239, 6240, 6241, 6242, + 6243, 6244, 6245, 6246, 6247, 6248, 6249, 6250, 6251, 6252, 6253, + 6254, 6255, 6256, 6257, 6258, 6259, 6260, 6261, 6262, 6263, 6264, + 6265, 6266, 6267, 6268, 6269, 6270, 6271, 6272, 6273, 6274, 6275, + 6276, 6277, 6278, 6279, 6280, 6281, 6282, 6283, 6284, 6285, 6286, + 6287, 6288, 6289, 6290, 6291, 6292, 6293, 6294, 6295, 6296, 6297, + 6298, 6299, 6300, 6301, 6302, 6303, 6304, 6305, 6306, 6307, 6308, + 6309, 6310, 6311, 6312, 6313, 6314, 6315, 6322, 6324, 6326, 6901, + 6902, 32767 + }; + public static final String[] GEODETICDATUM = { + "Undefined", + "Ellipsoid Airy 1830", + "Ellipsoid Airy modified 1849", + "Ellipsoid Australian national spheroid", + "Ellipsoid Bessel 1841", + "Ellipsoid Bessel modified", + "Ellipsoid Bessel Namibia", + "Ellipsoid Clarke 1858", + "Ellipsoid Clarke 1866", + "Ellipsoid Clarke 1866 Michigan", + "Ellipsoid Clarke 1880 Benoit", + "Ellipsoid Clarke 1880 IGN", + "Ellipsoid Clarke 1880 RGS", + "Ellipsoid Clarke 1880 arc", + "Ellipsoid Clarke 1880 SGA1922", + "Ellipsoid Everest 1830-1937 adjustment", + "Ellipsoid Everest 1830-1967 definition", + "Ellipsoid Everest 1830-1975 definition", + "Ellipsoid Everest 1830 modified", + "Ellipsoid GRS1980", + "Ellipsoid Helmert1906", + "Ellipsoid Indonesian national spheroid", + "Ellipsoid international 1924", + "Ellipsoid international 1967", + "Ellipsoid Krassowsky 1960", + "Ellipsoid NWL9D", + "Ellipsoid NWL10D", + "Ellipsoid Plessis 1817", + "Ellipsoid Struve 1860", + "Ellipsoid war office", + "Ellipsoid WGS84", + "Ellipsoid GEM10C", + "Ellipsoid OSU86F", + "Ellipsoid OSU91A", + "Ellipsoid Clarke 1880", + "Ellipsoid sphere", + "Adindan", + "Australian geodetic 1966", + "Australian geodetic 1984", + "Ain el Abd 1970", + "Afgooye", + "Agadez", + "Lisbon", + "Aratu", + "Arc 1950", + "Arc 1960", + "Batavia", + "Barbados", + "Beduaram", + "Beijing 1954", + "Reseau National Belge 1950", + "Bermuda 1957", + "Bern 1898", + "Bogota", + "Bukit Rimpah", + "Camacupa", + "Campo Inchauspe", + "Cape", + "Carthage", + "Chua", + "Corrego Alegre", + "Cote d'Ivoire", + "Deir ez Zor", + "Douala", + "Egypt 1907", + "European 1950", + "European 1987", + "Fahud", + "Gandajika 1970", + "Garoua", + "Guyane Francaise", + "Hu Tzu Shan", + "Hungarian 1972", + "Indonesian 1974", + "Indian 1954", + "Indian 1975", + "Jamaica 1875", + "Jamaica 1969", + "Kalianpur", + "Kandawala", + "Kertau", + "Kuwait Oil Company", + "La Canoa", + "Provisional S American 1956", + "Lake", + "Leigon", + "Liberia 1964", + "Lome", + "Luzon 1911", + "Hito XVIII 1963", + "Herat north", + "Mahe 1971", + "Makassar", + "European reference system 1989", + "Malongo 1987", + "Manoca", + "Merchich", + "Massawa", + "Minna", + "Mhast", + "Monte Mario", + "M poraloko", + "North American 1927", + "NAD Michigan", + "north American 1983", + "Nahrwan 1967", + "Naparima 1972", + "New Zealand geodetic 1949", + "NGO 1948", + "73", + "Nouvelle triangulation Francaise", + "NSWC 9Z 2", + "OSGB 1936", + "OSGB 1970 SN", + "OS SN 1980", + "Padang 1884", + "Palestine 1923", + "Pointe Noire", + "Geocentric of Australia 1994", + "Pulkovo 1942", + "Qatar", + "Qatar 1948", + "Qornoq", + "Loma Quintana", + "Amersfoort", + "RT38", + "south American 1969", + "Sapper Hill 1943", + "Schwarzeck", + "Segora", + "Serindung", + "Sudan", + "Tananarive 1925", + "Timbalai 1948", + "TM65", + "TM75", + "Tokyo", + "Trinidad 1903", + "Trucial Coast 1948", + "Voirol 1875", + "Voirol Unifie 1960", + "Bern 1938", + "Nord Sahara 1959", + "Stockholm 1938", + "Yacare", + "Yoff", + "Zanderij", + "Militar Geographische Institut", + "Reseau National Belge 1972", + "Deutsche Hauptdreiecksnetz", + "Conakry 1905", + "WGS72", + "WGS72 transit broadcast ephemeris", + "WGS84", + "Ancienne triangulation Francaise", + "Nord de Guerre", + "User-defined" + }; - public final static int[] ANGULARUNITS_INDEX = { - 0, 9101, 9102, 9103, 9104, 9105, 9106, 9107, 9108, 32767 - }; - public final static String[] ANGULARUNITS = { - "Undefined", - "Radian", - "Degree", - "Arc minute", - "Second", - "Grad", - "Gon", - "DMS", - "DMS hemisphere", - "User-defined" - }; + /* 6.3.2.3 Ellipsoid codes */ - /* 6.3.2.1 Geographic CS type codes */ - - public final static int[] GEOGRAPHICS_INDEX = { - 0, 4001, 4002, 4003, 4004, 4005, 4006, 4007, 4008, 4009, 4010, 4011, - 4012, 4013, 4014, 4015, 4016, 4017, 4018, 4019, 4020, 4021, 4022, - 4023, 4024, 4025, 4026, 4027, 4028, 4029, 4030, 4031, 4032, 4033, - 4034, 4035, 4201, 4202, 4203, 4204, 4205, 4206, 4207, 4208, 4209, - 4210, 4211, 4212, 4213, 4214, 4215, 4216, 4217, 4218, 4219, 4220, - 4221, 4222, 4223, 4224, 4225, 4226, 4227, 4228, 4229, 4230, 4231, - 4232, 4233, 4234, 4235, 4236, 4237, 4238, 4239, 4240, 4241, 4242, - 4243, 4244, 4245, 4246, 4247, 4248, 4249, 4250, 4251, 4252, 4253, - 4254, 4255, 4256, 4257, 4258, 4259, 4260, 4261, 4262, 4263, 4264, - 4265, 4266, 4267, 4268, 4269, 4270, 4271, 4272, 4273, 4274, 4275, - 4276, 4277, 4278, 4279, 4280, 4281, 4282, 4283, 4284, 4285, 4286, - 4287, 4288, 4289, 4290, 4291, 4292, 4293, 4294, 4295, 4296, 4297, - 4298, 4299, 4300, 4301, 4302, 4303, 4304, 4305, 4306, 4307, 4308, - 4309, 4310, 4311, 4312, 4313, 4314, 4315, 4801, 4802, 4803, 4804, - 4805, 4806, 4807, 4808, 4809, 4810, 4811, 4812, 4813, 4901, 4902, - 32767 - }; - public final static String[] GEOGRAPHICS = { - "Undefined", - "Ellipsoid Airy 1830", - "Ellipsoid Airy Modified 1849", - "Ellipsoid Australian National Spheroid", - "Ellipsoid Bessel 1841", - "Ellipsoid Bessel modified", - "Ellipsoid Bessel Namibia", - "Ellipsoid Clarke 1858", - "Ellipsoid Clarke 1866", - "Ellipsoid Clarke 1866 Michigan", - "Ellipsoid Clarke 1880 Benoit", - "Ellipsoid Clarke 1880 IGN", - "Ellipsoid Clarke 1880 RGS", - "Ellipsoid Clarke 1880 arc", - "Ellipsoid Clarke 1880 SGA 1922", - "Ellipsoid Everest 1830-1937 adjustment", - "Ellipsoid Everest 1830-1967 definition", - "Ellipsoid Everest 1830-1975 definition", - "Ellipsoid Everest 1830 modified", - "Ellipsoid GRS 1980", - "Ellipsoid Helmert 1906", - "Ellipsoid Indonesian national spheroid", - "Ellipsoid international 1924", - "Ellipsoid international 1967", - "Ellipsoid Krassowsky 1940", - "Ellipsoid NWL9D", - "Ellipsoid NWL10D", - "Ellipsoid Plessis 1817", - "Ellipsoid Struve 1860", - "Ellipsoid war office", - "Ellipsoid WGS84", - "Ellipsoid GEM10C", - "Ellipsoid OSU86F", - "Ellipsoid OSU91A", - "Ellipsoid Clarke 1880", - "Ellipsoid sphere", - "Adindan", - "AGD66", - "AGD84", - "Ain el Abd", - "Afgooye", - "Agadez", - "Lisbon", - "Aratu", - "Arc 1950", - "Arc 1960", - "Batavia", - "Barbados", - "Beduaram", - "Beijing 1954", - "Belge 1950", - "Bermuda 1957", - "Bern 1898", - "Bogota", - "Bukit Rimpah", - "Camacupa", - "Campo Inchauspe", - "Cape", - "Carthage", - "Chua", - "Corrego Alegre", - "Cote d Ivoire", - "Deir ez Zor", - "Douala", - "Egypt 1907", - "ED50", - "ED87", - "Fahud", - "Gandajika 1970", - "Garoua", - "Guyane Francaise", - "Hu Tzu Shan", - "HD72", - "ID74", - "Indian 1954", - "Indian 1975", - "Jamaica 1875", - "JAD69", - "Kalianpur", - "Kandawala", - "Kertau", - "KOC", - "La Canoa", - "PSAD56", - "Lake", - "Leigon", - "Liberia 1964", - "Lome", - "Luzon 1911", - "Hito XVIII 1963", - "Herat north", - "Mahe 1971", - "Makassar", - "EUREF89", - "Malongo 1987", - "Manoca", - "Merchich", - "Massawa", - "Minna", - "Mhast", - "Monte Mario", - "M poraloko", - "NAD27", - "NAD Michigan", - "NAD83", - "Nahrwan 1967", - "Naparima 1972", - "GD49", - "NGO 1948", - "Datum 73", - "NTF", - "NSWC 9Z 2", - "OSGB 1936", - "OSGB70", - "OS SN80", - "Padang", - "Palestine 1923", - "Pointe Noire", - "GDA94", - "Pulkovo 1942", - "Qatar", - "Qatar 1948", - "Qornoq", - "Loma Quintana", - "Amersfoort", - "RT38", - "SAD69", - "Sapper Hill 1943", - "Schwarzeck", - "Segora", - "Serindung", - "Sudan", - "Tananarive", - "Timbalai 1948", - "TM65", - "TM75", - "Tokyo", - "Trinidad 1903", - "TC 1948", - "Voirol 1875", - "Voirol Unifie", - "Bern 1938", - "Nord Sahara 1959", - "Stockholm 1938", - "Yacare", - "Yoff", - "Zanderij", - "MGI", - "Belge 1972", - "DHDN", - "Conakry 1905", - "WGS 72", - "WGS 72BE", - "WGS 84", - "Bern 1898 Bern", - "Bogota Bogota", - "Lisbon Lisbon", - "Makassar Jakarta", - "MGI Ferro", - "Monte Mario Rome", - "NTF Paris", - "Padang Jakarta", - "Belge 1950 Brussels", - "Tananarive Paris", - "Voirol 1875 Paris", - "Voirol Unifie Paris", - "Batavia Jakarta", - "ATF Paris", - "NDG Paris", - "User-defined" - }; + public static final int[] ELLIPSOID_INDEX = { + 0, 7001, 7002, 7003, 7004, 7005, 7006, 7007, 7008, 7009, 7010, 7011, 7012, 7013, 7014, 7015, + 7016, 7017, 7018, 7019, 7020, 7021, 7022, 7023, 7024, 7025, 7026, 7027, 7028, 7029, 7030, 7031, + 7032, 7033, 7034, 7035, 32767 + }; + public static final String[] ELLIPSOID = { + "Undefined", + "Airy 1830", + "Airy modified 1849", + "Australian national spheroid", + "Bessel 1841", + "Bessel modified", + "Bessel Namibia", + "Clarke 1858", + "Clarke 1866", + "Clarke 1866 Michigan", + "Clarke 1880 Benoit", + "Clarke 1880 IGN", + "Clarke 1880 RGS", + "Clarke 1880 arc", + "Clarke 1880 SGA 1922", + "Everest 1830-1937 adjustment", + "Everest 1830-1967 definition", + "Everest 1830-1975 definition", + "Everest 1830 modified", + "GRS 1980", + "Helmert 1906", + "Indonesian national spheroid", + "International 1924", + "International 1967", + "Krassowsky 1940", + "NWL 9D", + "NWL 10D", + "Plessis 1817", + "Struve 1860", + "War Office", + "WGS 84", + "GEM 10C", + "OSU86F", + "OSU91A", + "Clarke 1880", + "Sphere", + "User-defined" + }; - /* 6.3.2.2 Geodetic datum codes */ + /* 6.3.2.4 Prime meridian codes */ - public final static int[] GEODETICDATUM_INDEX = { - 6001, 6002, 6003, 6004, 6005, 6006, 6007, 6008, 6009, 6010, 6011, - 6012, 6013, 6014, 6015, 6016, 6017, 6018, 6019, 6020, 6021, 6022, - 6023, 6024, 6025, 6026, 6027, 6028, 6029, 6030, 6031, 6032, 6033, - 6034, 6035, 6201, 6202, 6203, 6204, 6205, 6206, 6207, 6208, 6209, - 6210, 6211, 6212, 6213, 6214, 6215, 6216, 6217, 6218, 6219, 6220, - 6221, 6222, 6223, 6224, 6225, 6226, 6227, 6228, 6229, 6230, 6231, - 6232, 6233, 6234, 6235, 6236, 6237, 6238, 6239, 6240, 6241, 6242, - 6243, 6244, 6245, 6246, 6247, 6248, 6249, 6250, 6251, 6252, 6253, - 6254, 6255, 6256, 6257, 6258, 6259, 6260, 6261, 6262, 6263, 6264, - 6265, 6266, 6267, 6268, 6269, 6270, 6271, 6272, 6273, 6274, 6275, - 6276, 6277, 6278, 6279, 6280, 6281, 6282, 6283, 6284, 6285, 6286, - 6287, 6288, 6289, 6290, 6291, 6292, 6293, 6294, 6295, 6296, 6297, - 6298, 6299, 6300, 6301, 6302, 6303, 6304, 6305, 6306, 6307, 6308, - 6309, 6310, 6311, 6312, 6313, 6314, 6315, 6322, 6324, 6326, 6901, - 6902, 32767 - }; - public final static String[] GEODETICDATUM = { - "Undefined", - "Ellipsoid Airy 1830", - "Ellipsoid Airy modified 1849", - "Ellipsoid Australian national spheroid", - "Ellipsoid Bessel 1841", - "Ellipsoid Bessel modified", - "Ellipsoid Bessel Namibia", - "Ellipsoid Clarke 1858", - "Ellipsoid Clarke 1866", - "Ellipsoid Clarke 1866 Michigan", - "Ellipsoid Clarke 1880 Benoit", - "Ellipsoid Clarke 1880 IGN", - "Ellipsoid Clarke 1880 RGS", - "Ellipsoid Clarke 1880 arc", - "Ellipsoid Clarke 1880 SGA1922", - "Ellipsoid Everest 1830-1937 adjustment", - "Ellipsoid Everest 1830-1967 definition", - "Ellipsoid Everest 1830-1975 definition", - "Ellipsoid Everest 1830 modified", - "Ellipsoid GRS1980", - "Ellipsoid Helmert1906", - "Ellipsoid Indonesian national spheroid", - "Ellipsoid international 1924", - "Ellipsoid international 1967", - "Ellipsoid Krassowsky 1960", - "Ellipsoid NWL9D", - "Ellipsoid NWL10D", - "Ellipsoid Plessis 1817", - "Ellipsoid Struve 1860", - "Ellipsoid war office", - "Ellipsoid WGS84", - "Ellipsoid GEM10C", - "Ellipsoid OSU86F", - "Ellipsoid OSU91A", - "Ellipsoid Clarke 1880", - "Ellipsoid sphere", - "Adindan", - "Australian geodetic 1966", - "Australian geodetic 1984", - "Ain el Abd 1970", - "Afgooye", - "Agadez", - "Lisbon", - "Aratu", - "Arc 1950", - "Arc 1960", - "Batavia", - "Barbados", - "Beduaram", - "Beijing 1954", - "Reseau National Belge 1950", - "Bermuda 1957", - "Bern 1898", - "Bogota", - "Bukit Rimpah", - "Camacupa", - "Campo Inchauspe", - "Cape", - "Carthage", - "Chua", - "Corrego Alegre", - "Cote d'Ivoire", - "Deir ez Zor", - "Douala", - "Egypt 1907", - "European 1950", - "European 1987", - "Fahud", - "Gandajika 1970", - "Garoua", - "Guyane Francaise", - "Hu Tzu Shan", - "Hungarian 1972", - "Indonesian 1974", - "Indian 1954", - "Indian 1975", - "Jamaica 1875", - "Jamaica 1969", - "Kalianpur", - "Kandawala", - "Kertau", - "Kuwait Oil Company", - "La Canoa", - "Provisional S American 1956", - "Lake", - "Leigon", - "Liberia 1964", - "Lome", - "Luzon 1911", - "Hito XVIII 1963", - "Herat north", - "Mahe 1971", - "Makassar", - "European reference system 1989", - "Malongo 1987", - "Manoca", - "Merchich", - "Massawa", - "Minna", - "Mhast", - "Monte Mario", - "M poraloko", - "North American 1927", - "NAD Michigan", - "north American 1983", - "Nahrwan 1967", - "Naparima 1972", - "New Zealand geodetic 1949", - "NGO 1948", - "73", - "Nouvelle triangulation Francaise", - "NSWC 9Z 2", - "OSGB 1936", - "OSGB 1970 SN", - "OS SN 1980", - "Padang 1884", - "Palestine 1923", - "Pointe Noire", - "Geocentric of Australia 1994", - "Pulkovo 1942", - "Qatar", - "Qatar 1948", - "Qornoq", - "Loma Quintana", - "Amersfoort", - "RT38", - "south American 1969", - "Sapper Hill 1943", - "Schwarzeck", - "Segora", - "Serindung", - "Sudan", - "Tananarive 1925", - "Timbalai 1948", - "TM65", - "TM75", - "Tokyo", - "Trinidad 1903", - "Trucial Coast 1948", - "Voirol 1875", - "Voirol Unifie 1960", - "Bern 1938", - "Nord Sahara 1959", - "Stockholm 1938", - "Yacare", - "Yoff", - "Zanderij", - "Militar Geographische Institut", - "Reseau National Belge 1972", - "Deutsche Hauptdreiecksnetz", - "Conakry 1905", - "WGS72", - "WGS72 transit broadcast ephemeris", - "WGS84", - "Ancienne triangulation Francaise", - "Nord de Guerre", - "User-defined" - }; + public static final int[] PRIMEMERIDIAN_INDEX = { + 0, 8901, 8902, 8903, 8904, 8905, 8906, 8907, 8908, 8909, 8910, 8911, 32767 + }; + public static final String[] PRIMEMERIDIAN = { + "Undefined", "Greenwich", "Lisbon", "Paris", "Bogota", "Madrid", + "Rome", "Bern", "Jakarta", "Ferro", "Brussels", "Stockholm", + "User-defined" + }; - /* 6.3.2.3 Ellipsoid codes */ + /* 6.3.3.1 Projected CS type codes */ - public final static int[] ELLIPSOID_INDEX = { - 0,7001, 7002, 7003, 7004, 7005, 7006, 7007, 7008, 7009, 7010, 7011, - 7012, 7013, 7014, 7015, 7016, 7017, 7018, 7019, 7020, 7021, 7022, - 7023, 7024, 7025, 7026, 7027, 7028, 7029, 7030, 7031, 7032, 7033, - 7034, 7035, 32767 - }; - public final static String[] ELLIPSOID = { - "Undefined", - "Airy 1830", - "Airy modified 1849", - "Australian national spheroid", - "Bessel 1841", - "Bessel modified", - "Bessel Namibia", - "Clarke 1858", - "Clarke 1866", - "Clarke 1866 Michigan", - "Clarke 1880 Benoit", - "Clarke 1880 IGN", - "Clarke 1880 RGS", - "Clarke 1880 arc", - "Clarke 1880 SGA 1922", - "Everest 1830-1937 adjustment", - "Everest 1830-1967 definition", - "Everest 1830-1975 definition", - "Everest 1830 modified", - "GRS 1980", - "Helmert 1906", - "Indonesian national spheroid", - "International 1924", - "International 1967", - "Krassowsky 1940", - "NWL 9D", - "NWL 10D", - "Plessis 1817", - "Struve 1860", - "War Office", - "WGS 84", - "GEM 10C", - "OSU86F", - "OSU91A", - "Clarke 1880", - "Sphere", - "User-defined" - }; + public static final int[] PROJECTEDCSTYPE_INDEX = { + 0, 20137, 20138, 20248, 20249, 20250, 20251, 20252, 20253, 20254, 20255, 20256, 20257, 20258, + 20348, 20349, 20350, 20351, 20352, 20353, 20354, 20355, 20356, 20357, 20358, 20437, 20438, + 20439, 20499, 20538, 20539, 20700, 20822, 20823, 20824, 20973, 20975, 20977, 20979, 20981, + 20983, 20985, 20987, 20989, 20991, 20993, 20995, 21100, 21148, 21149, 21150, 21413, 21414, + 21415, 21416, 21417, 21418, 21419, 21420, 21421, 21422, 21423, 21473, 21474, 21475, 21476, + 21477, 21478, 21479, 21480, 21481, 21482, 21483, 21500, 21790, 21817, 21818, 21891, 21892, + 21893, 21894, 22032, 22033, 22191, 22192, 22193, 22194, 22195, 22196, 22197, 22332, 22391, + 22392, 22523, 22524, 22832, 22992, 22993, 22994, 23028, 23029, 23030, 23031, 23032, 23033, + 23034, 23035, 23036, 23037, 23038, 23239, 23240, 23433, 23846, 23847, 23848, 23849, 23850, + 23851, 23852, 23853, 23886, 23887, 23888, 23889, 23890, 23891, 23892, 23893, 23894, 23947, + 23948, 24047, 24048, 24100, 24200, 24370, 24371, 24372, 24373, 24374, 24382, 24383, 24384, + 24500, 24547, 24548, 24720, 24721, 24818, 24819, 24820, 24821, 24877, 24878, 24879, 24880, + 24891, 24892, 24893, 25000, 25231, 25391, 25392, 25393, 25394, 25395, 25700, 25932, 26191, + 26192, 26193, 26237, 26331, 26332, 26391, 26392, 26393, 26432, 26591, 26592, 26632, 26692, + 26703, 26704, 26705, 26706, 26707, 26708, 26709, 26710, 26711, 26712, 26713, 26714, 26715, + 26716, 26717, 26718, 26719, 26720, 26721, 26722, 26729, 26730, 26731, 26732, 26733, 26734, + 26735, 26736, 26737, 26738, 26739, 26740, 26741, 26742, 26743, 26744, 26745, 26746, 26747, + 26748, 26749, 26750, 26751, 26752, 26753, 26754, 26755, 26756, 26757, 26758, 26759, 26760, + 26761, 26762, 26763, 26764, 26765, 26766, 26767, 26768, 26769, 26770, 26771, 26772, 26773, + 26774, 26774, 26775, 26775, 26776, 26776, 26777, 26777, 26778, 26779, 26780, 26781, 26782, + 26783, 26784, 26785, 26786, 26787, 26788, 26789, 26790, 26791, 26792, 26793, 26794, 26795, + 26796, 26797, 26798, 26801, 26802, 26803, 26903, 26904, 26905, 26906, 26907, 26908, 26909, + 26910, 26911, 26912, 26913, 26914, 26915, 26916, 26917, 26918, 26919, 26920, 26921, 26922, + 26923, 26929, 26930, 26931, 26932, 26933, 26934, 26935, 26936, 26937, 26938, 26939, 26940, + 26941, 26942, 26943, 26944, 26945, 26946, 26948, 26949, 26950, 26951, 26952, 26953, 26954, + 26955, 26956, 26957, 26958, 26959, 26960, 26961, 26962, 26963, 26964, 26965, 26966, 26967, + 26968, 26969, 26970, 26971, 26972, 26973, 26974, 26975, 26976, 26977, 26978, 26979, 26980, + 26981, 26982, 26983, 26984, 26985, 26986, 26987, 26988, 26989, 26990, 26991, 26992, 26993, + 26994, 26995, 26996, 26997, 26998, 27038, 27039, 27040, 27120, 27200, 27291, 27292, 27429, + 27500, 27581, 27582, 27583, 27591, 27592, 27593, 27700, 28232, 28348, 28349, 28350, 28351, + 28352, 28353, 28354, 28355, 28356, 28357, 28358, 28404, 28405, 28406, 28407, 28408, 28409, + 28410, 28411, 28412, 28413, 28414, 28415, 28416, 28417, 28418, 28419, 28420, 28421, 28422, + 28423, 28424, 28425, 28426, 28427, 28428, 28429, 28430, 28431, 28432, 28464, 28465, 28466, + 28467, 28468, 28469, 28470, 28471, 28472, 28473, 28474, 28475, 28476, 28477, 28478, 28479, + 28480, 28481, 28482, 28483, 28484, 28485, 28486, 28487, 28488, 28489, 28490, 28491, 28492, + 28600, 28991, 28992, 29118, 29119, 29120, 29121, 29122, 29177, 29178, 29179, 29180, 29181, + 29182, 29183, 29184, 29185, 29220, 29221, 29333, 29635, 29636, 29700, 29738, 29739, 29800, + 29849, 29850, 29900, 30200, 30339, 30340, 30491, 30492, 30591, 30592, 30600, 30729, 30730, + 30731, 30732, 31028, 31121, 31291, 31292, 31293, 31300, 31491, 31492, 31493, 31494, 31495, + 32001, 32002, 32003, 32005, 32006, 32007, 32008, 32009, 32010, 32011, 32012, 32013, 32014, + 32015, 32016, 32017, 32018, 32019, 32020, 32021, 32022, 32023, 32024, 32025, 32026, 32027, + 32028, 32029, 32030, 32031, 32033, 32034, 32035, 32036, 32037, 32038, 32039, 32040, 32041, + 32042, 32043, 32044, 32045, 32046, 32047, 32048, 32049, 32050, 32051, 32052, 32053, 32054, + 32055, 32056, 32057, 32058, 32059, 32060, 32100, 32104, 32107, 32108, 32109, 32110, 32111, + 32112, 32113, 32114, 32115, 32116, 32117, 32118, 32119, 32120, 32121, 32122, 32123, 32124, + 32125, 32126, 32127, 32128, 32129, 32130, 32133, 32134, 32135, 32136, 32137, 32138, 32139, + 32140, 32141, 32142, 32143, 32144, 32145, 32146, 32147, 32148, 32149, 32150, 32151, 32152, + 32153, 32154, 32155, 32156, 32157, 32158, 32161, 32201, 32202, 32203, 32204, 32205, 32206, + 32207, 32208, 32209, 32210, 32211, 32212, 32213, 32214, 32215, 32216, 32217, 32218, 32219, + 32220, 32221, 32222, 32223, 32224, 32225, 32226, 32227, 32228, 32229, 32230, 32231, 32232, + 32233, 32234, 32235, 32236, 32237, 32238, 32239, 32240, 32241, 32242, 32243, 32244, 32245, + 32246, 32247, 32248, 32249, 32250, 32251, 32252, 32253, 32254, 32255, 32256, 32257, 32258, + 32259, 32260, 32301, 32302, 32303, 32304, 32305, 32306, 32307, 32308, 32309, 32310, 32311, + 32312, 32313, 32314, 32315, 32316, 32317, 32318, 32319, 32320, 32321, 32322, 32323, 32324, + 32325, 32326, 32327, 32328, 32329, 32330, 32331, 32332, 32333, 32334, 32335, 32336, 32337, + 32338, 32339, 32340, 32341, 32342, 32343, 32344, 32345, 32346, 32347, 32348, 32349, 32350, + 32351, 32352, 32353, 32354, 32355, 32356, 32357, 32358, 32359, 32360, 32401, 32402, 32403, + 32404, 32405, 32406, 32407, 32408, 32409, 32410, 32411, 32412, 32413, 32414, 32415, 32416, + 32417, 32418, 32419, 32420, 32421, 32422, 32423, 32424, 32425, 32426, 32427, 32428, 32429, + 32430, 32431, 32432, 32433, 32434, 32435, 32436, 32437, 32438, 32439, 32440, 32441, 32442, + 32443, 32444, 32445, 32446, 32447, 32448, 32449, 32450, 32451, 32452, 32453, 32454, 32455, + 32456, 32457, 32458, 32459, 32460, 32501, 32502, 32503, 32504, 32505, 32506, 32507, 32508, + 32509, 32510, 32511, 32512, 32513, 32514, 32515, 32516, 32517, 32518, 32519, 32520, 32521, + 32522, 32523, 32524, 32525, 32526, 32527, 32528, 32529, 32530, 32531, 32532, 32533, 32534, + 32535, 32536, 32537, 32538, 32539, 32540, 32541, 32542, 32543, 32544, 32545, 32546, 32547, + 32548, 32549, 32550, 32551, 32552, 32553, 32554, 32555, 32556, 32557, 32558, 32559, 32560, + 32601, 32602, 32603, 32604, 32605, 32606, 32607, 32608, 32609, 32610, 32611, 32612, 32613, + 32614, 32615, 32616, 32617, 32618, 32619, 32620, 32621, 32622, 32623, 32624, 32625, 32626, + 32627, 32628, 32629, 32630, 32631, 32632, 32633, 32634, 32635, 32636, 32637, 32638, 32639, + 32640, 32641, 32642, 32643, 32644, 32645, 32646, 32647, 32648, 32649, 32650, 32651, 32652, + 32653, 32654, 32655, 32656, 32657, 32658, 32659, 32660, 32701, 32702, 32703, 32704, 32705, + 32706, 32707, 32708, 32709, 32710, 32711, 32712, 32713, 32714, 32715, 32716, 32717, 32718, + 32719, 32720, 32721, 32722, 32723, 32724, 32725, 32726, 32727, 32728, 32729, 32730, 32731, + 32732, 32733, 32734, 32735, 32736, 32737, 32738, 32739, 32740, 32741, 32742, 32743, 32744, + 32745, 32746, 32747, 32748, 32749, 32750, 32751, 32752, 32753, 32754, 32755, 32756, 32757, + 32758, 32759, 32760, 32767 + }; + public static final String[] PROJECTEDCSTYPE = { + "Undefined", + "Adindan UTM zone 37N", + "Adindan UTM zone 38N", + "AGD66 AMG zone 48", + "AGD66 AMG zone 49", + "AGD66 AMG zone 50", + "AGD66 AMG zone 51", + "AGD66 AMG zone 52", + "AGD66 AMG zone 53", + "AGD66 AMG zone 54", + "AGD66 AMG zone 55", + "AGD66 AMG zone 56", + "AGD66 AMG zone 57", + "AGD66 AMG zone 58", + "AGD84 AMG zone 48", + "AGD84 AMG zone 49", + "AGD84 AMG zone 50", + "AGD84 AMG zone 51", + "AGD84 AMG zone 52", + "AGD84 AMG zone 53", + "AGD84 AMG zone 54", + "AGD84 AMG zone 55", + "AGD84 AMG zone 56", + "AGD84 AMG zone 57", + "AGD84 AMG zone 58", + "Ain el Abd UTM zone 37N", + "Ain el Abd UTM zone 38N", + "Ain el Abd UTM zone 39N", + "Ain el Abd Bahrain Grid", + "Afgooye UTM zone 38N", + "Afgooye UTM zone 39N", + "Lisbon Portugese Grid", + "Aratu UTM zone 22S", + "Aratu UTM zone 23S", + "Aratu UTM zone 24S", + "Arc 1950 Lo13", + "Arc 1950 Lo15", + "Arc 1950 Lo17", + "Arc 1950 Lo19", + "Arc 1950 Lo21", + "Arc 1950 Lo23", + "Arc 1950 Lo25", + "Arc 1950 Lo27", + "Arc 1950 Lo29", + "Arc 1950 Lo31", + "Arc 1950 Lo33", + "Arc 1950 Lo35", + "Batavia NEIEZ", + "Batavia UTM zone 48S", + "Batavia UTM zone 49S", + "Batavia UTM zone 50S", + "Beijing Gauss zone 13", + "Beijing Gauss zone 14", + "Beijing Gauss zone 15", + "Beijing Gauss zone 16", + "Beijing Gauss zone 17", + "Beijing Gauss zone 18", + "Beijing Gauss zone 19", + "Beijing Gauss zone 20", + "Beijing Gauss zone 21", + "Beijing Gauss zone 22", + "Beijing Gauss zone 23", + "Beijing Gauss 13N", + "Beijing Gauss 14N", + "Beijing Gauss 15N", + "Beijing Gauss 16N", + "Beijing Gauss 17N", + "Beijing Gauss 18N", + "Beijing Gauss 19N", + "Beijing Gauss 20N", + "Beijing Gauss 21N", + "Beijing Gauss 22N", + "Beijing Gauss 23N", + "Belge Lambert 50", + "Bern 1898 Swiss Old", + "Bogota UTM zone 17N", + "Bogota UTM zone 18N", + "Bogota Colombia 3W", + "Bogota Colombia Bogota", + "Bogota Colombia 3E", + "Bogota Colombia 6E", + "Camacupa UTM 32S", + "Camacupa UTM 33S", + "C Inchauspe Argentina 1", + "C Inchauspe Argentina 2", + "C Inchauspe Argentina 3", + "C Inchauspe Argentina 4", + "C Inchauspe Argentina 5", + "C Inchauspe Argentina 6", + "C Inchauspe Argentina 7", + "Carthage UTM zone 32N", + "Carthage Nord Tunisie", + "Carthage Sud Tunisie", + "Corrego Alegre UTM 23S", + "Corrego Alegre UTM 24S", + "Douala UTM zone 32N", + "Egypt 1907 red belt", + "Egypt 1907 purple belt", + "Egypt 1907 ext purple", + "ED50 UTM zone 28N", + "ED50 UTM zone 29N", + "ED50 UTM zone 30N", + "ED50 UTM zone 31N", + "ED50 UTM zone 32N", + "ED50 UTM zone 33N", + "ED50 UTM zone 34N", + "ED50 UTM zone 35N", + "ED50 UTM zone 36N", + "ED50 UTM zone 37N", + "ED50 UTM zone 38N", + "Fahud UTM zone 39N", + "Fahud UTM zone 40N", + "Garoua UTM zone 33N", + "ID74 UTM zone 46N", + "ID74 UTM zone 47N", + "ID74 UTM zone 48N", + "ID74 UTM zone 49N", + "ID74 UTM zone 50N", + "ID74 UTM zone 51N", + "ID74 UTM zone 52N", + "ID74 UTM zone 53N", + "ID74 UTM zone 46S", + "ID74 UTM zone 47S", + "ID74 UTM zone 48S", + "ID74 UTM zone 49S", + "ID74 UTM zone 50S", + "ID74 UTM zone 51S", + "ID74 UTM zone 52S", + "ID74 UTM zone 53S", + "ID74 UTM zone 54S", + "Indian 1954 UTM 47N", + "Indian 1954 UTM 48N", + "Indian 1975 UTM 47N", + "Indian 1975 UTM 48N", + "Jamaica 1875 old grid", + "JAD69 Jamaica grid", + "Kalianpur India 0", + "Kalianpur India I", + "Kalianpur India IIa", + "Kalianpur India IIIa", + "Kalianpur India IVa", + "Kalianpur India IIb", + "Kalianpur India IIIb", + "Kalianpur India IVb", + "Kertau Singapore grid", + "Kertau UTM zone 47N", + "Kertau UTM zone 48N", + "La Canoa UTM zone 20N", + "La Canoa UTM zone 21N", + "PSAD56 UTM zone 18N", + "PSAD56 UTM zone 19N", + "PSAD56 UTM zone 20N", + "PSAD56 UTM zone 21N", + "PSAD56 UTM zone 17S", + "PSAD56 UTM zone 18S", + "PSAD56 UTM zone 19S", + "PSAD56 UTM zone 20S", + "PSAD56 Peru west zone", + "PSAD56 Peru central", + "PSAD56 Peru east zone", + "Leigon Ghana grid", + "Lome UTM zone 31N", + "Luzon Philippines I", + "Luzon Philippines II", + "Luzon Philippines III", + "Luzon Philippines IV", + "Luzon Philippines V", + "Makassar NEIEZ", + "Malongo 1987 UTM 32S", + "Merchich Nord Maroc", + "Merchich Sud Maroc", + "Merchich Sahara", + "Massawa UTM zone 37N", + "Minna UTM zone 31N", + "Minna UTM zone 32N", + "Minna Nigeria west", + "Minna Nigeria mid belt", + "Minna Nigeria east", + "Mhast UTM zone 32S", + "Monte Mario Italy 1", + "Monte Mario Italy 2", + "M poraloko UTM 32N", + "M poraloko UTM 32S", + "NAD27 UTM zone 3N", + "NAD27 UTM zone 4N", + "NAD27 UTM zone 5N", + "NAD27 UTM zone 6N", + "NAD27 UTM zone 7N", + "NAD27 UTM zone 8N", + "NAD27 UTM zone 9N", + "NAD27 UTM zone 10N", + "NAD27 UTM zone 11N", + "NAD27 UTM zone 12N", + "NAD27 UTM zone 13N", + "NAD27 UTM zone 14N", + "NAD27 UTM zone 15N", + "NAD27 UTM zone 16N", + "NAD27 UTM zone 17N", + "NAD27 UTM zone 18N", + "NAD27 UTM zone 19N", + "NAD27 UTM zone 20N", + "NAD27 UTM zone 21N", + "NAD27 UTM zone 22N", + "NAD27 Alabama east", + "NAD27 Alabama west", + "NAD27 Alaska zone 1", + "NAD27 Alaska zone 2", + "NAD27 Alaska zone 3", + "NAD27 Alaska zone 4", + "NAD27 Alaska zone 5", + "NAD27 Alaska zone 6", + "NAD27 Alaska zone 7", + "NAD27 Alaska zone 8", + "NAD27 Alaska zone 9", + "NAD27 Alaska zone 10", + "NAD27 California I", + "NAD27 California II", + "NAD27 California III", + "NAD27 California IV", + "NAD27 California V", + "NAD27 California VI", + "NAD27 California VII", + "NAD27 Arizona east", + "NAD27 Arizona central", + "NAD27 Arizona west", + "NAD27 Arkansas north", + "NAD27 Arkansas south", + "NAD27 Colorado north", + "NAD27 Colorado central", + "NAD27 Colorado south", + "NAD27 Connecticut", + "NAD27 Delaware", + "NAD27 Florida east", + "NAD27 Florida west", + "NAD27 Florida north", + "NAD27 Hawaii zone 1", + "NAD27 Hawaii zone 2", + "NAD27 Hawaii zone 3", + "NAD27 Hawaii zone 4", + "NAD27 Hawaii zone 5", + "NAD27 Georgia east", + "NAD27 Georgia west", + "NAD27 Idaho east", + "NAD27 Idaho central", + "NAD27 Idaho west", + "NAD27 Illinois east", + "NAD27 Illinois west", + "NAD27 Indiana east", + "NAD27 BLM 14N feet", + "NAD27 Indiana west", + "NAD27 BLM 15N feet", + "NAD27 Iowa north", + "NAD27 BLM 16N feet", + "NAD27 Iowa south", + "NAD27 BLM 17N feet", + "NAD27 Kansas north", + "NAD27 Kansas south", + "NAD27 Kentucky north", + "NAD27 Kentucky south", + "NAD27 Louisiana north", + "NAD27 Louisiana south", + "NAD27 Maine east", + "NAD27 Maine west", + "NAD27 Maryland", + "NAD27 Massachusetts", + "NAD27 Massachusetts Is", + "NAD27 Michigan north", + "NAD27 Michigan central", + "NAD27 Michigan south", + "NAD27 Minnesota north", + "NAD27 Minnesota Cent", + "NAD27 Minnesota south", + "NAD27 Mississippi east", + "NAD27 Mississippi west", + "NAD27 Missouri east", + "NAD27 Missouri central", + "NAD27 Missouri west", + "NAD Michigan Michigan east", + "NAD Michigan Michigan old central", + "NAD Michigan Michigan west", + "NAD83 UTM zone 3N", + "NAD83 UTM zone 4N", + "NAD83 UTM zone 5N", + "NAD83 UTM zone 6N", + "NAD83 UTM zone 7N", + "NAD83 UTM zone 8N", + "NAD83 UTM zone 9N", + "NAD83 UTM zone 10N", + "NAD83 UTM zone 11N", + "NAD83 UTM zone 12N", + "NAD83 UTM zone 13N", + "NAD83 UTM zone 14N", + "NAD83 UTM zone 15N", + "NAD83 UTM zone 16N", + "NAD83 UTM zone 17N", + "NAD83 UTM zone 18N", + "NAD83 UTM zone 19N", + "NAD83 UTM zone 20N", + "NAD83 UTM zone 21N", + "NAD83 UTM zone 22N", + "NAD83 UTM zone 23N", + "NAD83 Alabama east", + "NAD83 Alabama west", + "NAD83 Alaska zone 1", + "NAD83 Alaska zone 2", + "NAD83 Alaska zone 3", + "NAD83 Alaska zone 4", + "NAD83 Alaska zone 5", + "NAD83 Alaska zone 6", + "NAD83 Alaska zone 7", + "NAD83 Alaska zone 8", + "NAD83 Alaska zone 9", + "NAD83 Alaska zone 10", + "NAD83 California 1", + "NAD83 California 2", + "NAD83 California 3", + "NAD83 California 4", + "NAD83 California 5", + "NAD83 California 6", + "NAD83 Arizona east", + "NAD83 Arizona central", + "NAD83 Arizona west", + "NAD83 Arkansas north", + "NAD83 Arkansas south", + "NAD83 Colorado north", + "NAD83 Colorado central", + "NAD83 Colorado south", + "NAD83 Connecticut", + "NAD83 Delaware", + "NAD83 Florida east", + "NAD83 Florida west", + "NAD83 Florida north", + "NAD83 Hawaii zone 1", + "NAD83 Hawaii zone 2", + "NAD83 Hawaii zone 3", + "NAD83 Hawaii zone 4", + "NAD83 Hawaii zone 5", + "NAD83 Georgia east", + "NAD83 Georgia west", + "NAD83 Idaho east", + "NAD83 Idaho central", + "NAD83 Idaho west", + "NAD83 Illinois east", + "NAD83 Illinois west", + "NAD83 Indiana east", + "NAD83 Indiana west", + "NAD83 Iowa north", + "NAD83 Iowa south", + "NAD83 Kansas north", + "NAD83 Kansas south", + "NAD83 Kentucky north", + "NAD83 Kentucky south", + "NAD83 Louisiana north", + "NAD83 Louisiana south", + "NAD83 Maine east", + "NAD83 Maine west", + "NAD83 Maryland", + "NAD83 Massachusetts", + "NAD83 Massachusetts Is", + "NAD83 Michigan north", + "NAD83 Michigan central", + "NAD83 Michigan south", + "NAD83 Minnesota north", + "NAD83 Minnesota central", + "NAD83 Minnesota south", + "NAD83 Mississippi east", + "NAD83 Mississippi west", + "NAD83 Missouri east", + "NAD83 Missouri central", + "NAD83 Missouri west", + "Nahrwan 1967 UTM 38N", + "Nahrwan 1967 UTM 39N", + "Nahrwan 1967 UTM 40N", + "Naparima UTM 20N", + "GD49 NZ Map Grid", + "GD49 north Island Grid", + "GD49 south Island Grid", + "Datum 73 UTM zone 29N", + "ATF Nord de Guerre", + "NTF France I", + "NTF France II", + "NTF France III", + "NTF Nord France", + "NTF Centre France", + "NTF Sud France", + "British National Grid", + "Point Noire UTM 32S", + "GDA94 MGA zone 48", + "GDA94 MGA zone 49", + "GDA94 MGA zone 50", + "GDA94 MGA zone 51", + "GDA94 MGA zone 52", + "GDA94 MGA zone 53", + "GDA94 MGA zone 54", + "GDA94 MGA zone 55", + "GDA94 MGA zone 56", + "GDA94 MGA zone 57", + "GDA94 MGA zone 58", + "Pulkovo Gauss zone 4", + "Pulkovo Gauss zone 5", + "Pulkovo Gauss zone 6", + "Pulkovo Gauss zone 7", + "Pulkovo Gauss zone 8", + "Pulkovo Gauss zone 9", + "Pulkovo Gauss zone 10", + "Pulkovo Gauss zone 11", + "Pulkovo Gauss zone 12", + "Pulkovo Gauss zone 13", + "Pulkovo Gauss zone 14", + "Pulkovo Gauss zone 15", + "Pulkovo Gauss zone 16", + "Pulkovo Gauss zone 17", + "Pulkovo Gauss zone 18", + "Pulkovo Gauss zone 19", + "Pulkovo Gauss zone 20", + "Pulkovo Gauss zone 21", + "Pulkovo Gauss zone 22", + "Pulkovo Gauss zone 23", + "Pulkovo Gauss zone 24", + "Pulkovo Gauss zone 25", + "Pulkovo Gauss zone 26", + "Pulkovo Gauss zone 27", + "Pulkovo Gauss zone 28", + "Pulkovo Gauss zone 29", + "Pulkovo Gauss zone 30", + "Pulkovo Gauss zone 31", + "Pulkovo Gauss zone 32", + "Pulkovo Gauss 4N", + "Pulkovo Gauss 5N", + "Pulkovo Gauss 6N", + "Pulkovo Gauss 7N", + "Pulkovo Gauss 8N", + "Pulkovo Gauss 9N", + "Pulkovo Gauss 10N", + "Pulkovo Gauss 11N", + "Pulkovo Gauss 12N", + "Pulkovo Gauss 13N", + "Pulkovo Gauss 14N", + "Pulkovo Gauss 15N", + "Pulkovo Gauss 16N", + "Pulkovo Gauss 17N", + "Pulkovo Gauss 18N", + "Pulkovo Gauss 19N", + "Pulkovo Gauss 20N", + "Pulkovo Gauss 21N", + "Pulkovo Gauss 22N", + "Pulkovo Gauss 23N", + "Pulkovo Gauss 24N", + "Pulkovo Gauss 25N", + "Pulkovo Gauss 26N", + "Pulkovo Gauss 27N", + "Pulkovo Gauss 28N", + "Pulkovo Gauss 29N", + "Pulkovo Gauss 30N", + "Pulkovo Gauss 31N", + "Pulkovo Gauss 32N", + "Qatar National Grid", + "RD Netherlands Old", + "RD Netherlands New", + "SAD69 UTM zone 18N", + "SAD69 UTM zone 19N", + "SAD69 UTM zone 20N", + "SAD69 UTM zone 21N", + "SAD69 UTM zone 22N", + "SAD69 UTM zone 17S", + "SAD69 UTM zone 18S", + "SAD69 UTM zone 19S", + "SAD69 UTM zone 20S", + "SAD69 UTM zone 21S", + "SAD69 UTM zone 22S", + "SAD69 UTM zone 23S", + "SAD69 UTM zone 24S", + "SAD69 UTM zone 25S", + "Sapper Hill UTM 20S", + "Sapper Hill UTM 21S", + "Schwarzeck UTM 33S", + "Sudan UTM zone 35N", + "Sudan UTM zone 36N", + "Tananarive Laborde", + "Tananarive UTM 38S", + "Tananarive UTM 39S", + "Timbalai 1948 Borneo", + "Timbalai 1948 UTM 49N", + "Timbalai 1948 UTM 50N", + "TM65 Irish Nat Grid", + "Trinidad 1903 Trinidad", + "TC 1948 UTM zone 39N", + "TC 1948 UTM zone 40N", + "Voirol N Algerie ancien", + "Voirol S Algerie ancien", + "Voirol Unifie N Algerie", + "Voirol Unifie S Algerie", + "Bern 1938 Swiss New", + "Nord Sahara UTM 29N", + "Nord Sahara UTM 30N", + "Nord Sahara UTM 31N", + "Nord Sahara UTM 32N", + "Yoff UTM zone 28N", + "Zanderij UTM zone 21N", + "MGI Austria west", + "MGI Austria central", + "MGI Austria east", + "Belge Lambert 72", + "DHDN Germany zone 1", + "DHDN Germany zone 2", + "DHDN Germany zone 3", + "DHDN Germany zone 4", + "DHDN Germany zone 5", + "NAD27 Montana north", + "NAD27 Montana central", + "NAD27 Montana south", + "NAD27 Nebraska north", + "NAD27 Nebraska south", + "NAD27 Nevada east", + "NAD27 Nevada central", + "NAD27 Nevada west", + "NAD27 New Hampshire", + "NAD27 New Jersey", + "NAD27 New Mexico east", + "NAD27 New Mexico Cent", + "NAD27 New Mexico west", + "NAD27 New York east", + "NAD27 New York central", + "NAD27 New York west", + "NAD27 New York Long Is", + "NAD27 north Carolina", + "NAD27 north Dakota N", + "NAD27 north Dakota S", + "NAD27 Ohio north", + "NAD27 Ohio south", + "NAD27 Oklahoma north", + "NAD27 Oklahoma south", + "NAD27 Oregon north", + "NAD27 Oregon south", + "NAD27 Pennsylvania N", + "NAD27 Pennsylvania S", + "NAD27 Rhode Island", + "NAD27 south Carolina N", + "NAD27 south Carolina S", + "NAD27 south Dakota N", + "NAD27 south Dakota S", + "NAD27 Tennessee", + "NAD27 Texas north", + "NAD27 Texas north Cen", + "NAD27 Texas central", + "NAD27 Texas south Cen", + "NAD27 Texas south", + "NAD27 Utah north", + "NAD27 Utah central", + "NAD27 Utah south", + "NAD27 Vermont", + "NAD27 Virginia north", + "NAD27 Virginia south", + "NAD27 Washington north", + "NAD27 Washington south", + "NAD27 west Virginia N", + "NAD27 west Virginia S", + "NAD27 Wisconsin north", + "NAD27 Wisconsin central", + "NAD27 Wisconsin south", + "NAD27 Wyoming east", + "NAD27 Wyoming E central", + "NAD27 Wyoming W central", + "NAD27 Wyoming west", + "NAD27 Puerto Rico", + "NAD27 St Croix", + "NAD83 Montana", + "NAD83 Nebraska", + "NAD83 Nevada east", + "NAD83 Nevada central", + "NAD83 Nevada west", + "NAD83 New Hampshire", + "NAD83 New Jersey", + "NAD83 New Mexico east", + "NAD83 New Mexico central", + "NAD83 New Mexico west", + "NAD83 New York east", + "NAD83 New York central", + "NAD83 New York west", + "NAD83 New York Long Is", + "NAD83 north Carolina", + "NAD83 north Dakota N", + "NAD83 north Dakota S", + "NAD83 Ohio north", + "NAD83 Ohio south", + "NAD83 Oklahoma north", + "NAD83 Oklahoma south", + "NAD83 Oregon north", + "NAD83 Oregon south", + "NAD83 Pennsylvania N", + "NAD83 Pennsylvania S", + "NAD83 Rhode Island", + "NAD83 south Carolina", + "NAD83 south Dakota N", + "NAD83 south Dakota S", + "NAD83 Tennessee", + "NAD83 Texas north", + "NAD83 Texas north Cen", + "NAD83 Texas central", + "NAD83 Texas south Cen", + "NAD83 Texas south", + "NAD83 Utah north", + "NAD83 Utah central", + "NAD83 Utah south", + "NAD83 Vermont", + "NAD83 Virginia north", + "NAD83 Virginia south", + "NAD83 Washington north", + "NAD83 Washington south", + "NAD83 west Virginia N", + "NAD83 west Virginia S", + "NAD83 Wisconsin north", + "NAD83 Wisconsin Cen", + "NAD83 Wisconsin south", + "NAD83 Wyoming east", + "NAD83 Wyoming E Cen", + "NAD83 Wyoming W Cen", + "NAD83 Wyoming west", + "NAD83 Puerto Rico Virgin Is", + "WGS72 UTM zone 1N", + "WGS72 UTM zone 2N", + "WGS72 UTM zone 3N", + "WGS72 UTM zone 4N", + "WGS72 UTM zone 5N", + "WGS72 UTM zone 6N", + "WGS72 UTM zone 7N", + "WGS72 UTM zone 8N", + "WGS72 UTM zone 9N", + "WGS72 UTM zone 10N", + "WGS72 UTM zone 11N", + "WGS72 UTM zone 12N", + "WGS72 UTM zone 13N", + "WGS72 UTM zone 14N", + "WGS72 UTM zone 15N", + "WGS72 UTM zone 16N", + "WGS72 UTM zone 17N", + "WGS72 UTM zone 18N", + "WGS72 UTM zone 19N", + "WGS72 UTM zone 20N", + "WGS72 UTM zone 21N", + "WGS72 UTM zone 22N", + "WGS72 UTM zone 23N", + "WGS72 UTM zone 24N", + "WGS72 UTM zone 25N", + "WGS72 UTM zone 26N", + "WGS72 UTM zone 27N", + "WGS72 UTM zone 28N", + "WGS72 UTM zone 29N", + "WGS72 UTM zone 30N", + "WGS72 UTM zone 31N", + "WGS72 UTM zone 32N", + "WGS72 UTM zone 33N", + "WGS72 UTM zone 34N", + "WGS72 UTM zone 35N", + "WGS72 UTM zone 36N", + "WGS72 UTM zone 37N", + "WGS72 UTM zone 38N", + "WGS72 UTM zone 39N", + "WGS72 UTM zone 40N", + "WGS72 UTM zone 41N", + "WGS72 UTM zone 42N", + "WGS72 UTM zone 43N", + "WGS72 UTM zone 44N", + "WGS72 UTM zone 45N", + "WGS72 UTM zone 46N", + "WGS72 UTM zone 47N", + "WGS72 UTM zone 48N", + "WGS72 UTM zone 49N", + "WGS72 UTM zone 50N", + "WGS72 UTM zone 51N", + "WGS72 UTM zone 52N", + "WGS72 UTM zone 53N", + "WGS72 UTM zone 54N", + "WGS72 UTM zone 55N", + "WGS72 UTM zone 56N", + "WGS72 UTM zone 57N", + "WGS72 UTM zone 58N", + "WGS72 UTM zone 59N", + "WGS72 UTM zone 60N", + "WGS72 UTM zone 1S", + "WGS72 UTM zone 2S", + "WGS72 UTM zone 3S", + "WGS72 UTM zone 4S", + "WGS72 UTM zone 5S", + "WGS72 UTM zone 6S", + "WGS72 UTM zone 7S", + "WGS72 UTM zone 8S", + "WGS72 UTM zone 9S", + "WGS72 UTM zone 10S", + "WGS72 UTM zone 11S", + "WGS72 UTM zone 12S", + "WGS72 UTM zone 13S", + "WGS72 UTM zone 14S", + "WGS72 UTM zone 15S", + "WGS72 UTM zone 16S", + "WGS72 UTM zone 17S", + "WGS72 UTM zone 18S", + "WGS72 UTM zone 19S", + "WGS72 UTM zone 20S", + "WGS72 UTM zone 21S", + "WGS72 UTM zone 22S", + "WGS72 UTM zone 23S", + "WGS72 UTM zone 24S", + "WGS72 UTM zone 25S", + "WGS72 UTM zone 26S", + "WGS72 UTM zone 27S", + "WGS72 UTM zone 28S", + "WGS72 UTM zone 29S", + "WGS72 UTM zone 30S", + "WGS72 UTM zone 31S", + "WGS72 UTM zone 32S", + "WGS72 UTM zone 33S", + "WGS72 UTM zone 34S", + "WGS72 UTM zone 35S", + "WGS72 UTM zone 36S", + "WGS72 UTM zone 37S", + "WGS72 UTM zone 38S", + "WGS72 UTM zone 39S", + "WGS72 UTM zone 40S", + "WGS72 UTM zone 41S", + "WGS72 UTM zone 42S", + "WGS72 UTM zone 43S", + "WGS72 UTM zone 44S", + "WGS72 UTM zone 45S", + "WGS72 UTM zone 46S", + "WGS72 UTM zone 47S", + "WGS72 UTM zone 48S", + "WGS72 UTM zone 49S", + "WGS72 UTM zone 50S", + "WGS72 UTM zone 51S", + "WGS72 UTM zone 52S", + "WGS72 UTM zone 53S", + "WGS72 UTM zone 54S", + "WGS72 UTM zone 55S", + "WGS72 UTM zone 56S", + "WGS72 UTM zone 57S", + "WGS72 UTM zone 58S", + "WGS72 UTM zone 59S", + "WGS72 UTM zone 60S", + "WGS72BE UTM zone 1N", + "WGS72BE UTM zone 2N", + "WGS72BE UTM zone 3N", + "WGS72BE UTM zone 4N", + "WGS72BE UTM zone 5N", + "WGS72BE UTM zone 6N", + "WGS72BE UTM zone 7N", + "WGS72BE UTM zone 8N", + "WGS72BE UTM zone 9N", + "WGS72BE UTM zone 10N", + "WGS72BE UTM zone 11N", + "WGS72BE UTM zone 12N", + "WGS72BE UTM zone 13N", + "WGS72BE UTM zone 14N", + "WGS72BE UTM zone 15N", + "WGS72BE UTM zone 16N", + "WGS72BE UTM zone 17N", + "WGS72BE UTM zone 18N", + "WGS72BE UTM zone 19N", + "WGS72BE UTM zone 20N", + "WGS72BE UTM zone 21N", + "WGS72BE UTM zone 22N", + "WGS72BE UTM zone 23N", + "WGS72BE UTM zone 24N", + "WGS72BE UTM zone 25N", + "WGS72BE UTM zone 26N", + "WGS72BE UTM zone 27N", + "WGS72BE UTM zone 28N", + "WGS72BE UTM zone 29N", + "WGS72BE UTM zone 30N", + "WGS72BE UTM zone 31N", + "WGS72BE UTM zone 32N", + "WGS72BE UTM zone 33N", + "WGS72BE UTM zone 34N", + "WGS72BE UTM zone 35N", + "WGS72BE UTM zone 36N", + "WGS72BE UTM zone 37N", + "WGS72BE UTM zone 38N", + "WGS72BE UTM zone 39N", + "WGS72BE UTM zone 40N", + "WGS72BE UTM zone 41N", + "WGS72BE UTM zone 42N", + "WGS72BE UTM zone 43N", + "WGS72BE UTM zone 44N", + "WGS72BE UTM zone 45N", + "WGS72BE UTM zone 46N", + "WGS72BE UTM zone 47N", + "WGS72BE UTM zone 48N", + "WGS72BE UTM zone 49N", + "WGS72BE UTM zone 50N", + "WGS72BE UTM zone 51N", + "WGS72BE UTM zone 52N", + "WGS72BE UTM zone 53N", + "WGS72BE UTM zone 54N", + "WGS72BE UTM zone 55N", + "WGS72BE UTM zone 56N", + "WGS72BE UTM zone 57N", + "WGS72BE UTM zone 58N", + "WGS72BE UTM zone 59N", + "WGS72BE UTM zone 60N", + "WGS72BE UTM zone 1S", + "WGS72BE UTM zone 2S", + "WGS72BE UTM zone 3S", + "WGS72BE UTM zone 4S", + "WGS72BE UTM zone 5S", + "WGS72BE UTM zone 6S", + "WGS72BE UTM zone 7S", + "WGS72BE UTM zone 8S", + "WGS72BE UTM zone 9S", + "WGS72BE UTM zone 10S", + "WGS72BE UTM zone 11S", + "WGS72BE UTM zone 12S", + "WGS72BE UTM zone 13S", + "WGS72BE UTM zone 14S", + "WGS72BE UTM zone 15S", + "WGS72BE UTM zone 16S", + "WGS72BE UTM zone 17S", + "WGS72BE UTM zone 18S", + "WGS72BE UTM zone 19S", + "WGS72BE UTM zone 20S", + "WGS72BE UTM zone 21S", + "WGS72BE UTM zone 22S", + "WGS72BE UTM zone 23S", + "WGS72BE UTM zone 24S", + "WGS72BE UTM zone 25S", + "WGS72BE UTM zone 26S", + "WGS72BE UTM zone 27S", + "WGS72BE UTM zone 28S", + "WGS72BE UTM zone 29S", + "WGS72BE UTM zone 30S", + "WGS72BE UTM zone 31S", + "WGS72BE UTM zone 32S", + "WGS72BE UTM zone 33S", + "WGS72BE UTM zone 34S", + "WGS72BE UTM zone 35S", + "WGS72BE UTM zone 36S", + "WGS72BE UTM zone 37S", + "WGS72BE UTM zone 38S", + "WGS72BE UTM zone 39S", + "WGS72BE UTM zone 40S", + "WGS72BE UTM zone 41S", + "WGS72BE UTM zone 42S", + "WGS72BE UTM zone 43S", + "WGS72BE UTM zone 44S", + "WGS72BE UTM zone 45S", + "WGS72BE UTM zone 46S", + "WGS72BE UTM zone 47S", + "WGS72BE UTM zone 48S", + "WGS72BE UTM zone 49S", + "WGS72BE UTM zone 50S", + "WGS72BE UTM zone 51S", + "WGS72BE UTM zone 52S", + "WGS72BE UTM zone 53S", + "WGS72BE UTM zone 54S", + "WGS72BE UTM zone 55S", + "WGS72BE UTM zone 56S", + "WGS72BE UTM zone 57S", + "WGS72BE UTM zone 58S", + "WGS72BE UTM zone 59S", + "WGS72BE UTM zone 60S", + "WGS84 UTM zone 1N", + "WGS84 UTM zone 2N", + "WGS84 UTM zone 3N", + "WGS84 UTM zone 4N", + "WGS84 UTM zone 5N", + "WGS84 UTM zone 6N", + "WGS84 UTM zone 7N", + "WGS84 UTM zone 8N", + "WGS84 UTM zone 9N", + "WGS84 UTM zone 10N", + "WGS84 UTM zone 11N", + "WGS84 UTM zone 12N", + "WGS84 UTM zone 13N", + "WGS84 UTM zone 14N", + "WGS84 UTM zone 15N", + "WGS84 UTM zone 16N", + "WGS84 UTM zone 17N", + "WGS84 UTM zone 18N", + "WGS84 UTM zone 19N", + "WGS84 UTM zone 20N", + "WGS84 UTM zone 21N", + "WGS84 UTM zone 22N", + "WGS84 UTM zone 23N", + "WGS84 UTM zone 24N", + "WGS84 UTM zone 25N", + "WGS84 UTM zone 26N", + "WGS84 UTM zone 27N", + "WGS84 UTM zone 28N", + "WGS84 UTM zone 29N", + "WGS84 UTM zone 30N", + "WGS84 UTM zone 31N", + "WGS84 UTM zone 32N", + "WGS84 UTM zone 33N", + "WGS84 UTM zone 34N", + "WGS84 UTM zone 35N", + "WGS84 UTM zone 36N", + "WGS84 UTM zone 37N", + "WGS84 UTM zone 38N", + "WGS84 UTM zone 39N", + "WGS84 UTM zone 40N", + "WGS84 UTM zone 41N", + "WGS84 UTM zone 42N", + "WGS84 UTM zone 43N", + "WGS84 UTM zone 44N", + "WGS84 UTM zone 45N", + "WGS84 UTM zone 46N", + "WGS84 UTM zone 47N", + "WGS84 UTM zone 48N", + "WGS84 UTM zone 49N", + "WGS84 UTM zone 50N", + "WGS84 UTM zone 51N", + "WGS84 UTM zone 52N", + "WGS84 UTM zone 53N", + "WGS84 UTM zone 54N", + "WGS84 UTM zone 55N", + "WGS84 UTM zone 56N", + "WGS84 UTM zone 57N", + "WGS84 UTM zone 58N", + "WGS84 UTM zone 59N", + "WGS84 UTM zone 60N", + "WGS84 UTM zone 1S", + "WGS84 UTM zone 2S", + "WGS84 UTM zone 3S", + "WGS84 UTM zone 4S", + "WGS84 UTM zone 5S", + "WGS84 UTM zone 6S", + "WGS84 UTM zone 7S", + "WGS84 UTM zone 8S", + "WGS84 UTM zone 9S", + "WGS84 UTM zone 10S", + "WGS84 UTM zone 11S", + "WGS84 UTM zone 12S", + "WGS84 UTM zone 13S", + "WGS84 UTM zone 14S", + "WGS84 UTM zone 15S", + "WGS84 UTM zone 16S", + "WGS84 UTM zone 17S", + "WGS84 UTM zone 18S", + "WGS84 UTM zone 19S", + "WGS84 UTM zone 20S", + "WGS84 UTM zone 21S", + "WGS84 UTM zone 22S", + "WGS84 UTM zone 23S", + "WGS84 UTM zone 24S", + "WGS84 UTM zone 25S", + "WGS84 UTM zone 26S", + "WGS84 UTM zone 27S", + "WGS84 UTM zone 28S", + "WGS84 UTM zone 29S", + "WGS84 UTM zone 30S", + "WGS84 UTM zone 31S", + "WGS84 UTM zone 32S", + "WGS84 UTM zone 33S", + "WGS84 UTM zone 34S", + "WGS84 UTM zone 35S", + "WGS84 UTM zone 36S", + "WGS84 UTM zone 37S", + "WGS84 UTM zone 38S", + "WGS84 UTM zone 39S", + "WGS84 UTM zone 40S", + "WGS84 UTM zone 41S", + "WGS84 UTM zone 42S", + "WGS84 UTM zone 43S", + "WGS84 UTM zone 44S", + "WGS84 UTM zone 45S", + "WGS84 UTM zone 46S", + "WGS84 UTM zone 47S", + "WGS84 UTM zone 48S", + "WGS84 UTM zone 49S", + "WGS84 UTM zone 50S", + "WGS84 UTM zone 51S", + "WGS84 UTM zone 52S", + "WGS84 UTM zone 53S", + "WGS84 UTM zone 54S", + "WGS84 UTM zone 55S", + "WGS84 UTM zone 56S", + "WGS84 UTM zone 57S", + "WGS84 UTM zone 58S", + "WGS84 UTM zone 59S", + "WGS84 UTM zone 60S", + "User-defined" + }; - /* 6.3.2.4 Prime meridian codes */ + /* 6.3.3.2 Projection codes */ - public final static int[] PRIMEMERIDIAN_INDEX = { - 0, 8901, 8902, 8903, 8904, 8905, 8906, 8907, 8908, 8909, 8910, 8911, - 32767 - }; - public final static String[] PRIMEMERIDIAN = { - "Undefined", "Greenwich", "Lisbon", "Paris", "Bogota", "Madrid", - "Rome", "Bern", "Jakarta", "Ferro", "Brussels", "Stockholm", - "User-defined" - }; + public static final int[] PROJECTION_INDEX = { + 0, 10101, 10102, 10131, 10132, 10201, 10202, 10203, + 10231, 10232, 10233, 10301, 10302, 10331, 10332, 10401, + 10402, 10403, 10404, 10405, 10406, 10407, 10431, 10432, + 10433, 10434, 10435, 10436, 10501, 10502, 10503, 10531, + 10532, 10533, 10600, 10630, 10700, 10730, 10901, 10902, + 10903, 10931, 10932, 10933, 11001, 11002, 11031, 11032, + 11101, 11102, 11103, 11131, 11132, 11133, 11201, 11202, + 11231, 11232, 11301, 11302, 11331, 11332, 11401, 11402, + 11431, 11432, 11501, 11502, 11531, 11532, 11601, 11602, + 11631, 11632, 11701, 11702, 11731, 11732, 11801, 11802, + 11831, 11832, 11900, 11930, 12001, 12002, 12031, 12032, + 12101, 12102, 12103, 12111, 12112, 12113, 12141, 12142, + 12143, 12201, 12202, 12203, 12231, 12232, 12233, 12301, + 12302, 12331, 12332, 12401, 12402, 12403, 12431, 12432, + 12433, 12501, 12502, 12503, 12530, 12601, 12602, 12630, + 12701, 12702, 12703, 12731, 12732, 12733, 12800, 12830, + 12900, 12930, 13001, 13002, 13003, 13031, 13032, 13033, + 13101, 13102, 13103, 13104, 13131, 13132, 13133, 13134, + 13200, 13230, 13301, 13302, 13331, 13332, 13401, 13402, + 13431, 13432, 13501, 13502, 13531, 13532, 13601, 13602, + 13631, 13632, 13701, 13702, 13731, 13732, 13800, 13830, + 13901, 13902, 13930, 14001, 14002, 14031, 14032, 14100, + 14130, 14201, 14202, 14203, 14204, 14205, 14231, 14232, + 14233, 14234, 14235, 14301, 14302, 14303, 14331, 14332, + 14333, 14400, 14430, 14501, 14502, 14531, 14532, 14601, + 14602, 14631, 14632, 14701, 14702, 14731, 14732, 14801, + 14802, 14803, 14831, 14832, 14833, 14901, 14902, 14903, + 14904, 14931, 14932, 14933, 14934, 15001, 15002, 15003, + 15004, 15005, 15006, 15007, 15008, 15009, 15010, 15031, + 15032, 15033, 15034, 15035, 15036, 15037, 15038, 15039, + 15040, 15101, 15102, 15103, 15104, 15105, 15131, 15132, + 15133, 15134, 15135, 15201, 15202, 15230, 15914, 15915, + 15916, 15917, 17348, 17349, 17350, 17351, 17352, 17353, + 17354, 17355, 17356, 17357, 17358, 17448, 17449, 17450, + 17451, 17452, 17453, 17454, 17455, 17456, 17457, 17458, + 18031, 18032, 18033, 18034, 18035, 18036, 18037, 18051, + 18052, 18053, 18054, 18072, 18073, 18074, 18141, 18142, + 19900, 19905, 19912, 32767 + }; + public static final String[] PROJECTION = { + "Undefined", + "Alabama CS27 east", + "Alabama CS27 west", + "Alabama CS83 east", + "Alabama CS83 west", + "Arizona coordinate system east", + "Arizona coordinate system central", + "Arizona coordinate system west", + "Arizona CS83 east", + "Arizona CS83 central", + "Arizona CS83 west", + "Arkansas CS27 north", + "Arkansas CS27 south", + "Arkansas CS83 north", + "Arkansas CS83 south", + "California CS27 I", + "California CS27 II", + "California CS27 III", + "California CS27 IV", + "California CS27 V", + "California CS27 VI", + "California CS27 VII", + "California CS83 1", + "California CS83 2", + "California CS83 3", + "California CS83 4", + "California CS83 5", + "California CS83 6", + "Colorado CS27 north", + "Colorado CS27 central", + "Colorado CS27 south", + "Colorado CS83 north", + "Colorado CS83 central", + "Colorado CS83 south", + "Connecticut CS27", + "Connecticut CS83", + "Delaware CS27", + "Delaware CS83", + "Florida CS27 east", + "Florida CS27 west", + "Florida CS27 north", + "Florida CS83 east", + "Florida CS83 west", + "Florida CS83 north", + "Georgia CS27 east", + "Georgia CS27 west", + "Georgia CS83 east", + "Georgia CS83 west", + "Idaho CS27 east", + "Idaho CS27 central", + "Idaho CS27 west", + "Idaho CS83 east", + "Idaho CS83 central", + "Idaho CS83 west", + "Illinois CS27 east", + "Illinois CS27 west", + "Illinois CS83 east", + "Illinois CS83 west", + "Indiana CS27 east", + "Indiana CS27 west", + "Indiana CS83 east", + "Indiana CS83 west", + "Iowa CS27 north", + "Iowa CS27 south", + "Iowa CS83 north", + "Iowa CS83 south", + "Kansas CS27 north", + "Kansas CS27 south", + "Kansas CS83 north", + "Kansas CS83 south", + "Kentucky CS27 north", + "Kentucky CS27 south", + "Kentucky CS83 north", + "Kentucky CS83 south", + "Louisiana CS27 north", + "Louisiana CS27 south", + "Louisiana CS83 north", + "Louisiana CS83 south", + "Maine CS27 east", + "Maine CS27 west", + "Maine CS83 east", + "Maine CS83 west", + "Maryland CS27", + "Maryland CS83", + "Massachusetts CS27 mainland", + "Massachusetts CS27 island", + "Massachusetts CS83 mainland", + "Massachusetts CS83 island", + "Michigan state plane east", + "Michigan state plane old central", + "Michigan state plane west", + "Michigan CS27 north", + "Michigan CS27 central", + "Michigan CS27 south", + "Michigan CS83 north", + "Michigan CS83 central", + "Michigan CS83 south", + "Minnesota CS27 north", + "Minnesota CS27 central", + "Minnesota CS27 south", + "Minnesota CS83 north", + "Minnesota CS83 central", + "Minnesota CS83 south", + "Mississippi CS27 east", + "Mississippi CS27 west", + "Mississippi CS83 east", + "Mississippi CS83 west", + "Missouri CS27 east", + "Missouri CS27 central", + "Missouri CS27 west", + "Missouri CS83 east", + "Missouri CS83 central", + "Missouri CS83 west", + "Montana CS27 north", + "Montana CS27 central", + "Montana CS27 south", + "Montana CS83", + "Nebraska CS27 north", + "Nebraska CS27 south", + "Nebraska CS83", + "Nevada CS27 east", + "Nevada CS27 central", + "Nevada CS27 west", + "Nevada CS83 east", + "Nevada CS83 central", + "Nevada CS83 west", + "New Hampshire CS27", + "New Hampshire CS83", + "New Jersey CS27", + "New Jersey CS83", + "New Mexico CS27 east", + "New Mexico CS27 central", + "New Mexico CS27 west", + "New Mexico CS83 east", + "New Mexico CS83 central", + "New Mexico CS83 west", + "New York CS27 east", + "New York CS27 central", + "New York CS27 west", + "New York CS27 Long Island", + "New York CS83 east", + "New York CS83 central", + "New York CS83 west", + "New York CS83 Long Island", + "north Carolina CS27", + "north Carolina CS83", + "north Dakota CS27 north", + "north Dakota CS27 south", + "north Dakota CS83 north", + "north Dakota CS83 south", + "Ohio CS27 north", + "Ohio CS27 south", + "Ohio CS83 north", + "Ohio CS83 south", + "Oklahoma CS27 north", + "Oklahoma CS27 south", + "Oklahoma CS83 north", + "Oklahoma CS83 south", + "Oregon CS27 north", + "Oregon CS27 south", + "Oregon CS83 north", + "Oregon CS83 south", + "Pennsylvania CS27 north", + "Pennsylvania CS27 south", + "Pennsylvania CS83 north", + "Pennsylvania CS83 south", + "Rhode Island CS27", + "Rhode Island CS83", + "South Carolina CS27 north", + "South Carolina CS27 south", + "South Carolina CS83", + "South Dakota CS27 north", + "South Dakota CS27 south", + "South Dakota CS83 north", + "South Dakota CS83 south", + "Tennessee CS27", + "Tennessee CS83", + "Texas CS27 north", + "Texas CS27 north central", + "Texas CS27 central", + "Texas CS27 south central", + "Texas CS27 south", + "Texas CS83 north", + "Texas CS83 north central", + "Texas CS83 central", + "Texas CS83 south central", + "Texas CS83 south", + "Utah CS27 north", + "Utah CS27 central", + "Utah CS27 south", + "Utah CS83 north", + "Utah CS83 central", + "Utah CS83 south", + "Vermont CS27", + "Vermont CS83", + "Virginia CS27 north", + "Virginia CS27 south", + "Virginia CS83 north", + "Virginia CS83 south", + "Washington CS27 north", + "Washington CS27 south", + "Washington CS83 north", + "Washington CS83 south", + "west Virginia CS27 north", + "west Virginia CS27 south", + "west Virginia CS83 north", + "west Virginia CS83 south", + "Wisconsin CS27 north", + "Wisconsin CS27 central", + "Wisconsin CS27 south", + "Wisconsin CS83 north", + "Wisconsin CS83 central", + "Wisconsin CS83 south", + "Wyoming CS27 east", + "Wyoming CS27 east central", + "Wyoming CS27 west central", + "Wyoming CS27 west", + "Wyoming CS83 east", + "Wyoming CS83 east central", + "Wyoming CS83 west central", + "Wyoming CS83 west", + "Alaska CS27 1", + "Alaska CS27 2", + "Alaska CS27 3", + "Alaska CS27 4", + "Alaska CS27 5", + "Alaska CS27 6", + "Alaska CS27 7", + "Alaska CS27 8", + "Alaska CS27 9", + "Alaska CS27 10", + "Alaska CS83 1", + "Alaska CS83 2", + "Alaska CS83 3", + "Alaska CS83 4", + "Alaska CS83 5", + "Alaska CS83 6", + "Alaska CS83 7", + "Alaska CS83 8", + "Alaska CS83 9", + "Alaska CS83 10", + "Hawaii CS27 1", + "Hawaii CS27 2", + "Hawaii CS27 3", + "Hawaii CS27 4", + "Hawaii CS27 5", + "Hawaii CS83 1", + "Hawaii CS83 2", + "Hawaii CS83 3", + "Hawaii CS83 4", + "Hawaii CS83 5", + "Puerto Rico CS27", + "St Croix", + "Puerto Rico Virgin Is", + "BLM 14N feet", + "BLM 15N feet", + "BLM 16N feet", + "BLM 17N feet", + "Map grid of Australia 48", + "Map grid of Australia 49", + "Map grid of Australia 50", + "Map grid of Australia 51", + "Map grid of Australia 52", + "Map grid of Australia 53", + "Map grid of Australia 54", + "Map grid of Australia 55", + "Map grid of Australia 56", + "Map grid of Australia 57", + "Map grid of Australia 58", + "Australian map grid 48", + "Australian map grid 49", + "Australian map grid 50", + "Australian map grid 51", + "Australian map grid 52", + "Australian map grid 53", + "Australian map grid 54", + "Australian map grid 55", + "Australian map grid 56", + "Australian map grid 57", + "Australian map grid 58", + "Argentina 1", + "Argentina 2", + "Argentina 3", + "Argentina 4", + "Argentina 5", + "Argentina 6", + "Argentina 7", + "Colombia 3W", + "Colombia Bogota", + "Colombia 3E", + "Colombia 6E", + "Egypt red belt", + "Egypt purple belt", + "Extended Purple Belt", + "New Zealand north island nat grid", + "New Zealand south island nat grid", + "Bahrain grid", + "Netherlands E Indies Equatorial", + "RSO Borneo", + "User-defined" + }; - /* 6.3.3.1 Projected CS type codes */ + /* 6.3.3.3 Coordinate transformation codes */ - public final static int[] PROJECTEDCSTYPE_INDEX = { - 0, 20137, 20138, 20248, 20249, 20250, 20251, 20252, 20253, - 20254, 20255, 20256, 20257, 20258, 20348, 20349, 20350, 20351, - 20352, 20353, 20354, 20355, 20356, 20357, 20358, 20437, - 20438, 20439, 20499, 20538, 20539, 20700, 20822, 20823, - 20824, 20973, 20975, 20977, 20979, 20981, 20983, 20985, - 20987, 20989, 20991, 20993, 20995, 21100, 21148, 21149, - 21150, 21413, 21414, 21415, 21416, 21417, 21418, 21419, - 21420, 21421, 21422, 21423, 21473, 21474, 21475, 21476, - 21477, 21478, 21479, 21480, 21481, 21482, 21483, 21500, - 21790, 21817, 21818, 21891, 21892, 21893, 21894, 22032, - 22033, 22191, 22192, 22193, 22194, 22195, 22196, 22197, - 22332, 22391, 22392, 22523, 22524, 22832, 22992, 22993, - 22994, 23028, 23029, 23030, 23031, 23032, 23033, 23034, - 23035, 23036, 23037, 23038, 23239, 23240, 23433, 23846, - 23847, 23848, 23849, 23850, 23851, 23852, 23853, 23886, - 23887, 23888, 23889, 23890, 23891, 23892, 23893, 23894, - 23947, 23948, 24047, 24048, 24100, 24200, 24370, 24371, - 24372, 24373, 24374, 24382, 24383, 24384, 24500, 24547, - 24548, 24720, 24721, 24818, 24819, 24820, 24821, 24877, - 24878, 24879, 24880, 24891, 24892, 24893, 25000, 25231, - 25391, 25392, 25393, 25394, 25395, 25700, 25932, 26191, - 26192, 26193, 26237, 26331, 26332, 26391, 26392, 26393, - 26432, 26591, 26592, 26632, 26692, 26703, 26704, 26705, - 26706, 26707, 26708, 26709, 26710, 26711, 26712, 26713, - 26714, 26715, 26716, 26717, 26718, 26719, 26720, 26721, - 26722, 26729, 26730, 26731, 26732, 26733, 26734, 26735, - 26736, 26737, 26738, 26739, 26740, 26741, 26742, 26743, - 26744, 26745, 26746, 26747, 26748, 26749, 26750, 26751, - 26752, 26753, 26754, 26755, 26756, 26757, 26758, 26759, - 26760, 26761, 26762, 26763, 26764, 26765, 26766, 26767, - 26768, 26769, 26770, 26771, 26772, 26773, 26774, 26774, - 26775, 26775, 26776, 26776, 26777, 26777, 26778, 26779, - 26780, 26781, 26782, 26783, 26784, 26785, 26786, 26787, - 26788, 26789, 26790, 26791, 26792, 26793, 26794, 26795, - 26796, 26797, 26798, 26801, 26802, 26803, 26903, 26904, - 26905, 26906, 26907, 26908, 26909, 26910, 26911, 26912, - 26913, 26914, 26915, 26916, 26917, 26918, 26919, 26920, - 26921, 26922, 26923, 26929, 26930, 26931, 26932, 26933, - 26934, 26935, 26936, 26937, 26938, 26939, 26940, 26941, - 26942, 26943, 26944, 26945, 26946, 26948, 26949, 26950, - 26951, 26952, 26953, 26954, 26955, 26956, 26957, 26958, - 26959, 26960, 26961, 26962, 26963, 26964, 26965, 26966, - 26967, 26968, 26969, 26970, 26971, 26972, 26973, 26974, - 26975, 26976, 26977, 26978, 26979, 26980, 26981, 26982, - 26983, 26984, 26985, 26986, 26987, 26988, 26989, 26990, - 26991, 26992, 26993, 26994, 26995, 26996, 26997, 26998, - 27038, 27039, 27040, 27120, 27200, 27291, 27292, 27429, - 27500, 27581, 27582, 27583, 27591, 27592, 27593, 27700, - 28232, 28348, 28349, 28350, 28351, 28352, 28353, 28354, - 28355, 28356, 28357, 28358, 28404, 28405, 28406, 28407, - 28408, 28409, 28410, 28411, 28412, 28413, 28414, 28415, - 28416, 28417, 28418, 28419, 28420, 28421, 28422, 28423, - 28424, 28425, 28426, 28427, 28428, 28429, 28430, 28431, - 28432, 28464, 28465, 28466, 28467, 28468, 28469, 28470, - 28471, 28472, 28473, 28474, 28475, 28476, 28477, 28478, - 28479, 28480, 28481, 28482, 28483, 28484, 28485, 28486, - 28487, 28488, 28489, 28490, 28491, 28492, 28600, 28991, - 28992, 29118, 29119, 29120, 29121, 29122, 29177, 29178, - 29179, 29180, 29181, 29182, 29183, 29184, 29185, 29220, - 29221, 29333, 29635, 29636, 29700, 29738, 29739, 29800, - 29849, 29850, 29900, 30200, 30339, 30340, 30491, 30492, - 30591, 30592, 30600, 30729, 30730, 30731, 30732, 31028, - 31121, 31291, 31292, 31293, 31300, 31491, 31492, 31493, - 31494, 31495, 32001, 32002, 32003, 32005, 32006, 32007, - 32008, 32009, 32010, 32011, 32012, 32013, 32014, 32015, - 32016, 32017, 32018, 32019, 32020, 32021, 32022, 32023, - 32024, 32025, 32026, 32027, 32028, 32029, 32030, 32031, - 32033, 32034, 32035, 32036, 32037, 32038, 32039, 32040, - 32041, 32042, 32043, 32044, 32045, 32046, 32047, 32048, - 32049, 32050, 32051, 32052, 32053, 32054, 32055, 32056, - 32057, 32058, 32059, 32060, 32100, 32104, 32107, 32108, - 32109, 32110, 32111, 32112, 32113, 32114, 32115, 32116, - 32117, 32118, 32119, 32120, 32121, 32122, 32123, 32124, - 32125, 32126, 32127, 32128, 32129, 32130, 32133, 32134, - 32135, 32136, 32137, 32138, 32139, 32140, 32141, 32142, - 32143, 32144, 32145, 32146, 32147, 32148, 32149, 32150, - 32151, 32152, 32153, 32154, 32155, 32156, 32157, 32158, - 32161, 32201, 32202, 32203, 32204, 32205, 32206, 32207, - 32208, 32209, 32210, 32211, 32212, 32213, 32214, 32215, - 32216, 32217, 32218, 32219, 32220, 32221, 32222, 32223, - 32224, 32225, 32226, 32227, 32228, 32229, 32230, 32231, - 32232, 32233, 32234, 32235, 32236, 32237, 32238, 32239, - 32240, 32241, 32242, 32243, 32244, 32245, 32246, 32247, - 32248, 32249, 32250, 32251, 32252, 32253, 32254, 32255, - 32256, 32257, 32258, 32259, 32260, 32301, 32302, 32303, - 32304, 32305, 32306, 32307, 32308, 32309, 32310, 32311, - 32312, 32313, 32314, 32315, 32316, 32317, 32318, 32319, - 32320, 32321, 32322, 32323, 32324, 32325, 32326, 32327, - 32328, 32329, 32330, 32331, 32332, 32333, 32334, 32335, - 32336, 32337, 32338, 32339, 32340, 32341, 32342, 32343, - 32344, 32345, 32346, 32347, 32348, 32349, 32350, 32351, - 32352, 32353, 32354, 32355, 32356, 32357, 32358, 32359, - 32360, 32401, 32402, 32403, 32404, 32405, 32406, 32407, - 32408, 32409, 32410, 32411, 32412, 32413, 32414, 32415, - 32416, 32417, 32418, 32419, 32420, 32421, 32422, 32423, - 32424, 32425, 32426, 32427, 32428, 32429, 32430, 32431, - 32432, 32433, 32434, 32435, 32436, 32437, 32438, 32439, - 32440, 32441, 32442, 32443, 32444, 32445, 32446, 32447, - 32448, 32449, 32450, 32451, 32452, 32453, 32454, 32455, - 32456, 32457, 32458, 32459, 32460, 32501, 32502, 32503, - 32504, 32505, 32506, 32507, 32508, 32509, 32510, 32511, - 32512, 32513, 32514, 32515, 32516, 32517, 32518, 32519, - 32520, 32521, 32522, 32523, 32524, 32525, 32526, 32527, - 32528, 32529, 32530, 32531, 32532, 32533, 32534, 32535, - 32536, 32537, 32538, 32539, 32540, 32541, 32542, 32543, - 32544, 32545, 32546, 32547, 32548, 32549, 32550, 32551, - 32552, 32553, 32554, 32555, 32556, 32557, 32558, 32559, - 32560, 32601, 32602, 32603, 32604, 32605, 32606, 32607, - 32608, 32609, 32610, 32611, 32612, 32613, 32614, 32615, - 32616, 32617, 32618, 32619, 32620, 32621, 32622, 32623, - 32624, 32625, 32626, 32627, 32628, 32629, 32630, 32631, - 32632, 32633, 32634, 32635, 32636, 32637, 32638, 32639, - 32640, 32641, 32642, 32643, 32644, 32645, 32646, 32647, - 32648, 32649, 32650, 32651, 32652, 32653, 32654, 32655, - 32656, 32657, 32658, 32659, 32660, 32701, 32702, 32703, - 32704, 32705, 32706, 32707, 32708, 32709, 32710, 32711, - 32712, 32713, 32714, 32715, 32716, 32717, 32718, 32719, - 32720, 32721, 32722, 32723, 32724, 32725, 32726, 32727, - 32728, 32729, 32730, 32731, 32732, 32733, 32734, 32735, - 32736, 32737, 32738, 32739, 32740, 32741, 32742, 32743, - 32744, 32745, 32746, 32747, 32748, 32749, 32750, 32751, - 32752, 32753, 32754, 32755, 32756, 32757, 32758, 32759, - 32760, 32767 - }; - public final static String[] PROJECTEDCSTYPE = { - "Undefined", - "Adindan UTM zone 37N", - "Adindan UTM zone 38N", - "AGD66 AMG zone 48", - "AGD66 AMG zone 49", - "AGD66 AMG zone 50", - "AGD66 AMG zone 51", - "AGD66 AMG zone 52", - "AGD66 AMG zone 53", - "AGD66 AMG zone 54", - "AGD66 AMG zone 55", - "AGD66 AMG zone 56", - "AGD66 AMG zone 57", - "AGD66 AMG zone 58", - "AGD84 AMG zone 48", - "AGD84 AMG zone 49", - "AGD84 AMG zone 50", - "AGD84 AMG zone 51", - "AGD84 AMG zone 52", - "AGD84 AMG zone 53", - "AGD84 AMG zone 54", - "AGD84 AMG zone 55", - "AGD84 AMG zone 56", - "AGD84 AMG zone 57", - "AGD84 AMG zone 58", - "Ain el Abd UTM zone 37N", - "Ain el Abd UTM zone 38N", - "Ain el Abd UTM zone 39N", - "Ain el Abd Bahrain Grid", - "Afgooye UTM zone 38N", - "Afgooye UTM zone 39N", - "Lisbon Portugese Grid", - "Aratu UTM zone 22S", - "Aratu UTM zone 23S", - "Aratu UTM zone 24S", - "Arc 1950 Lo13", - "Arc 1950 Lo15", - "Arc 1950 Lo17", - "Arc 1950 Lo19", - "Arc 1950 Lo21", - "Arc 1950 Lo23", - "Arc 1950 Lo25", - "Arc 1950 Lo27", - "Arc 1950 Lo29", - "Arc 1950 Lo31", - "Arc 1950 Lo33", - "Arc 1950 Lo35", - "Batavia NEIEZ", - "Batavia UTM zone 48S", - "Batavia UTM zone 49S", - "Batavia UTM zone 50S", - "Beijing Gauss zone 13", - "Beijing Gauss zone 14", - "Beijing Gauss zone 15", - "Beijing Gauss zone 16", - "Beijing Gauss zone 17", - "Beijing Gauss zone 18", - "Beijing Gauss zone 19", - "Beijing Gauss zone 20", - "Beijing Gauss zone 21", - "Beijing Gauss zone 22", - "Beijing Gauss zone 23", - "Beijing Gauss 13N", - "Beijing Gauss 14N", - "Beijing Gauss 15N", - "Beijing Gauss 16N", - "Beijing Gauss 17N", - "Beijing Gauss 18N", - "Beijing Gauss 19N", - "Beijing Gauss 20N", - "Beijing Gauss 21N", - "Beijing Gauss 22N", - "Beijing Gauss 23N", - "Belge Lambert 50", - "Bern 1898 Swiss Old", - "Bogota UTM zone 17N", - "Bogota UTM zone 18N", - "Bogota Colombia 3W", - "Bogota Colombia Bogota", - "Bogota Colombia 3E", - "Bogota Colombia 6E", - "Camacupa UTM 32S", - "Camacupa UTM 33S", - "C Inchauspe Argentina 1", - "C Inchauspe Argentina 2", - "C Inchauspe Argentina 3", - "C Inchauspe Argentina 4", - "C Inchauspe Argentina 5", - "C Inchauspe Argentina 6", - "C Inchauspe Argentina 7", - "Carthage UTM zone 32N", - "Carthage Nord Tunisie", - "Carthage Sud Tunisie", - "Corrego Alegre UTM 23S", - "Corrego Alegre UTM 24S", - "Douala UTM zone 32N", - "Egypt 1907 red belt", - "Egypt 1907 purple belt", - "Egypt 1907 ext purple", - "ED50 UTM zone 28N", - "ED50 UTM zone 29N", - "ED50 UTM zone 30N", - "ED50 UTM zone 31N", - "ED50 UTM zone 32N", - "ED50 UTM zone 33N", - "ED50 UTM zone 34N", - "ED50 UTM zone 35N", - "ED50 UTM zone 36N", - "ED50 UTM zone 37N", - "ED50 UTM zone 38N", - "Fahud UTM zone 39N", - "Fahud UTM zone 40N", - "Garoua UTM zone 33N", - "ID74 UTM zone 46N", - "ID74 UTM zone 47N", - "ID74 UTM zone 48N", - "ID74 UTM zone 49N", - "ID74 UTM zone 50N", - "ID74 UTM zone 51N", - "ID74 UTM zone 52N", - "ID74 UTM zone 53N", - "ID74 UTM zone 46S", - "ID74 UTM zone 47S", - "ID74 UTM zone 48S", - "ID74 UTM zone 49S", - "ID74 UTM zone 50S", - "ID74 UTM zone 51S", - "ID74 UTM zone 52S", - "ID74 UTM zone 53S", - "ID74 UTM zone 54S", - "Indian 1954 UTM 47N", - "Indian 1954 UTM 48N", - "Indian 1975 UTM 47N", - "Indian 1975 UTM 48N", - "Jamaica 1875 old grid", - "JAD69 Jamaica grid", - "Kalianpur India 0", - "Kalianpur India I", - "Kalianpur India IIa", - "Kalianpur India IIIa", - "Kalianpur India IVa", - "Kalianpur India IIb", - "Kalianpur India IIIb", - "Kalianpur India IVb", - "Kertau Singapore grid", - "Kertau UTM zone 47N", - "Kertau UTM zone 48N", - "La Canoa UTM zone 20N", - "La Canoa UTM zone 21N", - "PSAD56 UTM zone 18N", - "PSAD56 UTM zone 19N", - "PSAD56 UTM zone 20N", - "PSAD56 UTM zone 21N", - "PSAD56 UTM zone 17S", - "PSAD56 UTM zone 18S", - "PSAD56 UTM zone 19S", - "PSAD56 UTM zone 20S", - "PSAD56 Peru west zone", - "PSAD56 Peru central", - "PSAD56 Peru east zone", - "Leigon Ghana grid", - "Lome UTM zone 31N", - "Luzon Philippines I", - "Luzon Philippines II", - "Luzon Philippines III", - "Luzon Philippines IV", - "Luzon Philippines V", - "Makassar NEIEZ", - "Malongo 1987 UTM 32S", - "Merchich Nord Maroc", - "Merchich Sud Maroc", - "Merchich Sahara", - "Massawa UTM zone 37N", - "Minna UTM zone 31N", - "Minna UTM zone 32N", - "Minna Nigeria west", - "Minna Nigeria mid belt", - "Minna Nigeria east", - "Mhast UTM zone 32S", - "Monte Mario Italy 1", - "Monte Mario Italy 2", - "M poraloko UTM 32N", - "M poraloko UTM 32S", - "NAD27 UTM zone 3N", - "NAD27 UTM zone 4N", - "NAD27 UTM zone 5N", - "NAD27 UTM zone 6N", - "NAD27 UTM zone 7N", - "NAD27 UTM zone 8N", - "NAD27 UTM zone 9N", - "NAD27 UTM zone 10N", - "NAD27 UTM zone 11N", - "NAD27 UTM zone 12N", - "NAD27 UTM zone 13N", - "NAD27 UTM zone 14N", - "NAD27 UTM zone 15N", - "NAD27 UTM zone 16N", - "NAD27 UTM zone 17N", - "NAD27 UTM zone 18N", - "NAD27 UTM zone 19N", - "NAD27 UTM zone 20N", - "NAD27 UTM zone 21N", - "NAD27 UTM zone 22N", - "NAD27 Alabama east", - "NAD27 Alabama west", - "NAD27 Alaska zone 1", - "NAD27 Alaska zone 2", - "NAD27 Alaska zone 3", - "NAD27 Alaska zone 4", - "NAD27 Alaska zone 5", - "NAD27 Alaska zone 6", - "NAD27 Alaska zone 7", - "NAD27 Alaska zone 8", - "NAD27 Alaska zone 9", - "NAD27 Alaska zone 10", - "NAD27 California I", - "NAD27 California II", - "NAD27 California III", - "NAD27 California IV", - "NAD27 California V", - "NAD27 California VI", - "NAD27 California VII", - "NAD27 Arizona east", - "NAD27 Arizona central", - "NAD27 Arizona west", - "NAD27 Arkansas north", - "NAD27 Arkansas south", - "NAD27 Colorado north", - "NAD27 Colorado central", - "NAD27 Colorado south", - "NAD27 Connecticut", - "NAD27 Delaware", - "NAD27 Florida east", - "NAD27 Florida west", - "NAD27 Florida north", - "NAD27 Hawaii zone 1", - "NAD27 Hawaii zone 2", - "NAD27 Hawaii zone 3", - "NAD27 Hawaii zone 4", - "NAD27 Hawaii zone 5", - "NAD27 Georgia east", - "NAD27 Georgia west", - "NAD27 Idaho east", - "NAD27 Idaho central", - "NAD27 Idaho west", - "NAD27 Illinois east", - "NAD27 Illinois west", - "NAD27 Indiana east", - "NAD27 BLM 14N feet", - "NAD27 Indiana west", - "NAD27 BLM 15N feet", - "NAD27 Iowa north", - "NAD27 BLM 16N feet", - "NAD27 Iowa south", - "NAD27 BLM 17N feet", - "NAD27 Kansas north", - "NAD27 Kansas south", - "NAD27 Kentucky north", - "NAD27 Kentucky south", - "NAD27 Louisiana north", - "NAD27 Louisiana south", - "NAD27 Maine east", - "NAD27 Maine west", - "NAD27 Maryland", - "NAD27 Massachusetts", - "NAD27 Massachusetts Is", - "NAD27 Michigan north", - "NAD27 Michigan central", - "NAD27 Michigan south", - "NAD27 Minnesota north", - "NAD27 Minnesota Cent", - "NAD27 Minnesota south", - "NAD27 Mississippi east", - "NAD27 Mississippi west", - "NAD27 Missouri east", - "NAD27 Missouri central", - "NAD27 Missouri west", - "NAD Michigan Michigan east", - "NAD Michigan Michigan old central", - "NAD Michigan Michigan west", - "NAD83 UTM zone 3N", - "NAD83 UTM zone 4N", - "NAD83 UTM zone 5N", - "NAD83 UTM zone 6N", - "NAD83 UTM zone 7N", - "NAD83 UTM zone 8N", - "NAD83 UTM zone 9N", - "NAD83 UTM zone 10N", - "NAD83 UTM zone 11N", - "NAD83 UTM zone 12N", - "NAD83 UTM zone 13N", - "NAD83 UTM zone 14N", - "NAD83 UTM zone 15N", - "NAD83 UTM zone 16N", - "NAD83 UTM zone 17N", - "NAD83 UTM zone 18N", - "NAD83 UTM zone 19N", - "NAD83 UTM zone 20N", - "NAD83 UTM zone 21N", - "NAD83 UTM zone 22N", - "NAD83 UTM zone 23N", - "NAD83 Alabama east", - "NAD83 Alabama west", - "NAD83 Alaska zone 1", - "NAD83 Alaska zone 2", - "NAD83 Alaska zone 3", - "NAD83 Alaska zone 4", - "NAD83 Alaska zone 5", - "NAD83 Alaska zone 6", - "NAD83 Alaska zone 7", - "NAD83 Alaska zone 8", - "NAD83 Alaska zone 9", - "NAD83 Alaska zone 10", - "NAD83 California 1", - "NAD83 California 2", - "NAD83 California 3", - "NAD83 California 4", - "NAD83 California 5", - "NAD83 California 6", - "NAD83 Arizona east", - "NAD83 Arizona central", - "NAD83 Arizona west", - "NAD83 Arkansas north", - "NAD83 Arkansas south", - "NAD83 Colorado north", - "NAD83 Colorado central", - "NAD83 Colorado south", - "NAD83 Connecticut", - "NAD83 Delaware", - "NAD83 Florida east", - "NAD83 Florida west", - "NAD83 Florida north", - "NAD83 Hawaii zone 1", - "NAD83 Hawaii zone 2", - "NAD83 Hawaii zone 3", - "NAD83 Hawaii zone 4", - "NAD83 Hawaii zone 5", - "NAD83 Georgia east", - "NAD83 Georgia west", - "NAD83 Idaho east", - "NAD83 Idaho central", - "NAD83 Idaho west", - "NAD83 Illinois east", - "NAD83 Illinois west", - "NAD83 Indiana east", - "NAD83 Indiana west", - "NAD83 Iowa north", - "NAD83 Iowa south", - "NAD83 Kansas north", - "NAD83 Kansas south", - "NAD83 Kentucky north", - "NAD83 Kentucky south", - "NAD83 Louisiana north", - "NAD83 Louisiana south", - "NAD83 Maine east", - "NAD83 Maine west", - "NAD83 Maryland", - "NAD83 Massachusetts", - "NAD83 Massachusetts Is", - "NAD83 Michigan north", - "NAD83 Michigan central", - "NAD83 Michigan south", - "NAD83 Minnesota north", - "NAD83 Minnesota central", - "NAD83 Minnesota south", - "NAD83 Mississippi east", - "NAD83 Mississippi west", - "NAD83 Missouri east", - "NAD83 Missouri central", - "NAD83 Missouri west", - "Nahrwan 1967 UTM 38N", - "Nahrwan 1967 UTM 39N", - "Nahrwan 1967 UTM 40N", - "Naparima UTM 20N", - "GD49 NZ Map Grid", - "GD49 north Island Grid", - "GD49 south Island Grid", - "Datum 73 UTM zone 29N", - "ATF Nord de Guerre", - "NTF France I", - "NTF France II", - "NTF France III", - "NTF Nord France", - "NTF Centre France", - "NTF Sud France", - "British National Grid", - "Point Noire UTM 32S", - "GDA94 MGA zone 48", - "GDA94 MGA zone 49", - "GDA94 MGA zone 50", - "GDA94 MGA zone 51", - "GDA94 MGA zone 52", - "GDA94 MGA zone 53", - "GDA94 MGA zone 54", - "GDA94 MGA zone 55", - "GDA94 MGA zone 56", - "GDA94 MGA zone 57", - "GDA94 MGA zone 58", - "Pulkovo Gauss zone 4", - "Pulkovo Gauss zone 5", - "Pulkovo Gauss zone 6", - "Pulkovo Gauss zone 7", - "Pulkovo Gauss zone 8", - "Pulkovo Gauss zone 9", - "Pulkovo Gauss zone 10", - "Pulkovo Gauss zone 11", - "Pulkovo Gauss zone 12", - "Pulkovo Gauss zone 13", - "Pulkovo Gauss zone 14", - "Pulkovo Gauss zone 15", - "Pulkovo Gauss zone 16", - "Pulkovo Gauss zone 17", - "Pulkovo Gauss zone 18", - "Pulkovo Gauss zone 19", - "Pulkovo Gauss zone 20", - "Pulkovo Gauss zone 21", - "Pulkovo Gauss zone 22", - "Pulkovo Gauss zone 23", - "Pulkovo Gauss zone 24", - "Pulkovo Gauss zone 25", - "Pulkovo Gauss zone 26", - "Pulkovo Gauss zone 27", - "Pulkovo Gauss zone 28", - "Pulkovo Gauss zone 29", - "Pulkovo Gauss zone 30", - "Pulkovo Gauss zone 31", - "Pulkovo Gauss zone 32", - "Pulkovo Gauss 4N", - "Pulkovo Gauss 5N", - "Pulkovo Gauss 6N", - "Pulkovo Gauss 7N", - "Pulkovo Gauss 8N", - "Pulkovo Gauss 9N", - "Pulkovo Gauss 10N", - "Pulkovo Gauss 11N", - "Pulkovo Gauss 12N", - "Pulkovo Gauss 13N", - "Pulkovo Gauss 14N", - "Pulkovo Gauss 15N", - "Pulkovo Gauss 16N", - "Pulkovo Gauss 17N", - "Pulkovo Gauss 18N", - "Pulkovo Gauss 19N", - "Pulkovo Gauss 20N", - "Pulkovo Gauss 21N", - "Pulkovo Gauss 22N", - "Pulkovo Gauss 23N", - "Pulkovo Gauss 24N", - "Pulkovo Gauss 25N", - "Pulkovo Gauss 26N", - "Pulkovo Gauss 27N", - "Pulkovo Gauss 28N", - "Pulkovo Gauss 29N", - "Pulkovo Gauss 30N", - "Pulkovo Gauss 31N", - "Pulkovo Gauss 32N", - "Qatar National Grid", - "RD Netherlands Old", - "RD Netherlands New", - "SAD69 UTM zone 18N", - "SAD69 UTM zone 19N", - "SAD69 UTM zone 20N", - "SAD69 UTM zone 21N", - "SAD69 UTM zone 22N", - "SAD69 UTM zone 17S", - "SAD69 UTM zone 18S", - "SAD69 UTM zone 19S", - "SAD69 UTM zone 20S", - "SAD69 UTM zone 21S", - "SAD69 UTM zone 22S", - "SAD69 UTM zone 23S", - "SAD69 UTM zone 24S", - "SAD69 UTM zone 25S", - "Sapper Hill UTM 20S", - "Sapper Hill UTM 21S", - "Schwarzeck UTM 33S", - "Sudan UTM zone 35N", - "Sudan UTM zone 36N", - "Tananarive Laborde", - "Tananarive UTM 38S", - "Tananarive UTM 39S", - "Timbalai 1948 Borneo", - "Timbalai 1948 UTM 49N", - "Timbalai 1948 UTM 50N", - "TM65 Irish Nat Grid", - "Trinidad 1903 Trinidad", - "TC 1948 UTM zone 39N", - "TC 1948 UTM zone 40N", - "Voirol N Algerie ancien", - "Voirol S Algerie ancien", - "Voirol Unifie N Algerie", - "Voirol Unifie S Algerie", - "Bern 1938 Swiss New", - "Nord Sahara UTM 29N", - "Nord Sahara UTM 30N", - "Nord Sahara UTM 31N", - "Nord Sahara UTM 32N", - "Yoff UTM zone 28N", - "Zanderij UTM zone 21N", - "MGI Austria west", - "MGI Austria central", - "MGI Austria east", - "Belge Lambert 72", - "DHDN Germany zone 1", - "DHDN Germany zone 2", - "DHDN Germany zone 3", - "DHDN Germany zone 4", - "DHDN Germany zone 5", - "NAD27 Montana north", - "NAD27 Montana central", - "NAD27 Montana south", - "NAD27 Nebraska north", - "NAD27 Nebraska south", - "NAD27 Nevada east", - "NAD27 Nevada central", - "NAD27 Nevada west", - "NAD27 New Hampshire", - "NAD27 New Jersey", - "NAD27 New Mexico east", - "NAD27 New Mexico Cent", - "NAD27 New Mexico west", - "NAD27 New York east", - "NAD27 New York central", - "NAD27 New York west", - "NAD27 New York Long Is", - "NAD27 north Carolina", - "NAD27 north Dakota N", - "NAD27 north Dakota S", - "NAD27 Ohio north", - "NAD27 Ohio south", - "NAD27 Oklahoma north", - "NAD27 Oklahoma south", - "NAD27 Oregon north", - "NAD27 Oregon south", - "NAD27 Pennsylvania N", - "NAD27 Pennsylvania S", - "NAD27 Rhode Island", - "NAD27 south Carolina N", - "NAD27 south Carolina S", - "NAD27 south Dakota N", - "NAD27 south Dakota S", - "NAD27 Tennessee", - "NAD27 Texas north", - "NAD27 Texas north Cen", - "NAD27 Texas central", - "NAD27 Texas south Cen", - "NAD27 Texas south", - "NAD27 Utah north", - "NAD27 Utah central", - "NAD27 Utah south", - "NAD27 Vermont", - "NAD27 Virginia north", - "NAD27 Virginia south", - "NAD27 Washington north", - "NAD27 Washington south", - "NAD27 west Virginia N", - "NAD27 west Virginia S", - "NAD27 Wisconsin north", - "NAD27 Wisconsin central", - "NAD27 Wisconsin south", - "NAD27 Wyoming east", - "NAD27 Wyoming E central", - "NAD27 Wyoming W central", - "NAD27 Wyoming west", - "NAD27 Puerto Rico", - "NAD27 St Croix", - "NAD83 Montana", - "NAD83 Nebraska", - "NAD83 Nevada east", - "NAD83 Nevada central", - "NAD83 Nevada west", - "NAD83 New Hampshire", - "NAD83 New Jersey", - "NAD83 New Mexico east", - "NAD83 New Mexico central", - "NAD83 New Mexico west", - "NAD83 New York east", - "NAD83 New York central", - "NAD83 New York west", - "NAD83 New York Long Is", - "NAD83 north Carolina", - "NAD83 north Dakota N", - "NAD83 north Dakota S", - "NAD83 Ohio north", - "NAD83 Ohio south", - "NAD83 Oklahoma north", - "NAD83 Oklahoma south", - "NAD83 Oregon north", - "NAD83 Oregon south", - "NAD83 Pennsylvania N", - "NAD83 Pennsylvania S", - "NAD83 Rhode Island", - "NAD83 south Carolina", - "NAD83 south Dakota N", - "NAD83 south Dakota S", - "NAD83 Tennessee", - "NAD83 Texas north", - "NAD83 Texas north Cen", - "NAD83 Texas central", - "NAD83 Texas south Cen", - "NAD83 Texas south", - "NAD83 Utah north", - "NAD83 Utah central", - "NAD83 Utah south", - "NAD83 Vermont", - "NAD83 Virginia north", - "NAD83 Virginia south", - "NAD83 Washington north", - "NAD83 Washington south", - "NAD83 west Virginia N", - "NAD83 west Virginia S", - "NAD83 Wisconsin north", - "NAD83 Wisconsin Cen", - "NAD83 Wisconsin south", - "NAD83 Wyoming east", - "NAD83 Wyoming E Cen", - "NAD83 Wyoming W Cen", - "NAD83 Wyoming west", - "NAD83 Puerto Rico Virgin Is", - "WGS72 UTM zone 1N", - "WGS72 UTM zone 2N", - "WGS72 UTM zone 3N", - "WGS72 UTM zone 4N", - "WGS72 UTM zone 5N", - "WGS72 UTM zone 6N", - "WGS72 UTM zone 7N", - "WGS72 UTM zone 8N", - "WGS72 UTM zone 9N", - "WGS72 UTM zone 10N", - "WGS72 UTM zone 11N", - "WGS72 UTM zone 12N", - "WGS72 UTM zone 13N", - "WGS72 UTM zone 14N", - "WGS72 UTM zone 15N", - "WGS72 UTM zone 16N", - "WGS72 UTM zone 17N", - "WGS72 UTM zone 18N", - "WGS72 UTM zone 19N", - "WGS72 UTM zone 20N", - "WGS72 UTM zone 21N", - "WGS72 UTM zone 22N", - "WGS72 UTM zone 23N", - "WGS72 UTM zone 24N", - "WGS72 UTM zone 25N", - "WGS72 UTM zone 26N", - "WGS72 UTM zone 27N", - "WGS72 UTM zone 28N", - "WGS72 UTM zone 29N", - "WGS72 UTM zone 30N", - "WGS72 UTM zone 31N", - "WGS72 UTM zone 32N", - "WGS72 UTM zone 33N", - "WGS72 UTM zone 34N", - "WGS72 UTM zone 35N", - "WGS72 UTM zone 36N", - "WGS72 UTM zone 37N", - "WGS72 UTM zone 38N", - "WGS72 UTM zone 39N", - "WGS72 UTM zone 40N", - "WGS72 UTM zone 41N", - "WGS72 UTM zone 42N", - "WGS72 UTM zone 43N", - "WGS72 UTM zone 44N", - "WGS72 UTM zone 45N", - "WGS72 UTM zone 46N", - "WGS72 UTM zone 47N", - "WGS72 UTM zone 48N", - "WGS72 UTM zone 49N", - "WGS72 UTM zone 50N", - "WGS72 UTM zone 51N", - "WGS72 UTM zone 52N", - "WGS72 UTM zone 53N", - "WGS72 UTM zone 54N", - "WGS72 UTM zone 55N", - "WGS72 UTM zone 56N", - "WGS72 UTM zone 57N", - "WGS72 UTM zone 58N", - "WGS72 UTM zone 59N", - "WGS72 UTM zone 60N", - "WGS72 UTM zone 1S", - "WGS72 UTM zone 2S", - "WGS72 UTM zone 3S", - "WGS72 UTM zone 4S", - "WGS72 UTM zone 5S", - "WGS72 UTM zone 6S", - "WGS72 UTM zone 7S", - "WGS72 UTM zone 8S", - "WGS72 UTM zone 9S", - "WGS72 UTM zone 10S", - "WGS72 UTM zone 11S", - "WGS72 UTM zone 12S", - "WGS72 UTM zone 13S", - "WGS72 UTM zone 14S", - "WGS72 UTM zone 15S", - "WGS72 UTM zone 16S", - "WGS72 UTM zone 17S", - "WGS72 UTM zone 18S", - "WGS72 UTM zone 19S", - "WGS72 UTM zone 20S", - "WGS72 UTM zone 21S", - "WGS72 UTM zone 22S", - "WGS72 UTM zone 23S", - "WGS72 UTM zone 24S", - "WGS72 UTM zone 25S", - "WGS72 UTM zone 26S", - "WGS72 UTM zone 27S", - "WGS72 UTM zone 28S", - "WGS72 UTM zone 29S", - "WGS72 UTM zone 30S", - "WGS72 UTM zone 31S", - "WGS72 UTM zone 32S", - "WGS72 UTM zone 33S", - "WGS72 UTM zone 34S", - "WGS72 UTM zone 35S", - "WGS72 UTM zone 36S", - "WGS72 UTM zone 37S", - "WGS72 UTM zone 38S", - "WGS72 UTM zone 39S", - "WGS72 UTM zone 40S", - "WGS72 UTM zone 41S", - "WGS72 UTM zone 42S", - "WGS72 UTM zone 43S", - "WGS72 UTM zone 44S", - "WGS72 UTM zone 45S", - "WGS72 UTM zone 46S", - "WGS72 UTM zone 47S", - "WGS72 UTM zone 48S", - "WGS72 UTM zone 49S", - "WGS72 UTM zone 50S", - "WGS72 UTM zone 51S", - "WGS72 UTM zone 52S", - "WGS72 UTM zone 53S", - "WGS72 UTM zone 54S", - "WGS72 UTM zone 55S", - "WGS72 UTM zone 56S", - "WGS72 UTM zone 57S", - "WGS72 UTM zone 58S", - "WGS72 UTM zone 59S", - "WGS72 UTM zone 60S", - "WGS72BE UTM zone 1N", - "WGS72BE UTM zone 2N", - "WGS72BE UTM zone 3N", - "WGS72BE UTM zone 4N", - "WGS72BE UTM zone 5N", - "WGS72BE UTM zone 6N", - "WGS72BE UTM zone 7N", - "WGS72BE UTM zone 8N", - "WGS72BE UTM zone 9N", - "WGS72BE UTM zone 10N", - "WGS72BE UTM zone 11N", - "WGS72BE UTM zone 12N", - "WGS72BE UTM zone 13N", - "WGS72BE UTM zone 14N", - "WGS72BE UTM zone 15N", - "WGS72BE UTM zone 16N", - "WGS72BE UTM zone 17N", - "WGS72BE UTM zone 18N", - "WGS72BE UTM zone 19N", - "WGS72BE UTM zone 20N", - "WGS72BE UTM zone 21N", - "WGS72BE UTM zone 22N", - "WGS72BE UTM zone 23N", - "WGS72BE UTM zone 24N", - "WGS72BE UTM zone 25N", - "WGS72BE UTM zone 26N", - "WGS72BE UTM zone 27N", - "WGS72BE UTM zone 28N", - "WGS72BE UTM zone 29N", - "WGS72BE UTM zone 30N", - "WGS72BE UTM zone 31N", - "WGS72BE UTM zone 32N", - "WGS72BE UTM zone 33N", - "WGS72BE UTM zone 34N", - "WGS72BE UTM zone 35N", - "WGS72BE UTM zone 36N", - "WGS72BE UTM zone 37N", - "WGS72BE UTM zone 38N", - "WGS72BE UTM zone 39N", - "WGS72BE UTM zone 40N", - "WGS72BE UTM zone 41N", - "WGS72BE UTM zone 42N", - "WGS72BE UTM zone 43N", - "WGS72BE UTM zone 44N", - "WGS72BE UTM zone 45N", - "WGS72BE UTM zone 46N", - "WGS72BE UTM zone 47N", - "WGS72BE UTM zone 48N", - "WGS72BE UTM zone 49N", - "WGS72BE UTM zone 50N", - "WGS72BE UTM zone 51N", - "WGS72BE UTM zone 52N", - "WGS72BE UTM zone 53N", - "WGS72BE UTM zone 54N", - "WGS72BE UTM zone 55N", - "WGS72BE UTM zone 56N", - "WGS72BE UTM zone 57N", - "WGS72BE UTM zone 58N", - "WGS72BE UTM zone 59N", - "WGS72BE UTM zone 60N", - "WGS72BE UTM zone 1S", - "WGS72BE UTM zone 2S", - "WGS72BE UTM zone 3S", - "WGS72BE UTM zone 4S", - "WGS72BE UTM zone 5S", - "WGS72BE UTM zone 6S", - "WGS72BE UTM zone 7S", - "WGS72BE UTM zone 8S", - "WGS72BE UTM zone 9S", - "WGS72BE UTM zone 10S", - "WGS72BE UTM zone 11S", - "WGS72BE UTM zone 12S", - "WGS72BE UTM zone 13S", - "WGS72BE UTM zone 14S", - "WGS72BE UTM zone 15S", - "WGS72BE UTM zone 16S", - "WGS72BE UTM zone 17S", - "WGS72BE UTM zone 18S", - "WGS72BE UTM zone 19S", - "WGS72BE UTM zone 20S", - "WGS72BE UTM zone 21S", - "WGS72BE UTM zone 22S", - "WGS72BE UTM zone 23S", - "WGS72BE UTM zone 24S", - "WGS72BE UTM zone 25S", - "WGS72BE UTM zone 26S", - "WGS72BE UTM zone 27S", - "WGS72BE UTM zone 28S", - "WGS72BE UTM zone 29S", - "WGS72BE UTM zone 30S", - "WGS72BE UTM zone 31S", - "WGS72BE UTM zone 32S", - "WGS72BE UTM zone 33S", - "WGS72BE UTM zone 34S", - "WGS72BE UTM zone 35S", - "WGS72BE UTM zone 36S", - "WGS72BE UTM zone 37S", - "WGS72BE UTM zone 38S", - "WGS72BE UTM zone 39S", - "WGS72BE UTM zone 40S", - "WGS72BE UTM zone 41S", - "WGS72BE UTM zone 42S", - "WGS72BE UTM zone 43S", - "WGS72BE UTM zone 44S", - "WGS72BE UTM zone 45S", - "WGS72BE UTM zone 46S", - "WGS72BE UTM zone 47S", - "WGS72BE UTM zone 48S", - "WGS72BE UTM zone 49S", - "WGS72BE UTM zone 50S", - "WGS72BE UTM zone 51S", - "WGS72BE UTM zone 52S", - "WGS72BE UTM zone 53S", - "WGS72BE UTM zone 54S", - "WGS72BE UTM zone 55S", - "WGS72BE UTM zone 56S", - "WGS72BE UTM zone 57S", - "WGS72BE UTM zone 58S", - "WGS72BE UTM zone 59S", - "WGS72BE UTM zone 60S", - "WGS84 UTM zone 1N", - "WGS84 UTM zone 2N", - "WGS84 UTM zone 3N", - "WGS84 UTM zone 4N", - "WGS84 UTM zone 5N", - "WGS84 UTM zone 6N", - "WGS84 UTM zone 7N", - "WGS84 UTM zone 8N", - "WGS84 UTM zone 9N", - "WGS84 UTM zone 10N", - "WGS84 UTM zone 11N", - "WGS84 UTM zone 12N", - "WGS84 UTM zone 13N", - "WGS84 UTM zone 14N", - "WGS84 UTM zone 15N", - "WGS84 UTM zone 16N", - "WGS84 UTM zone 17N", - "WGS84 UTM zone 18N", - "WGS84 UTM zone 19N", - "WGS84 UTM zone 20N", - "WGS84 UTM zone 21N", - "WGS84 UTM zone 22N", - "WGS84 UTM zone 23N", - "WGS84 UTM zone 24N", - "WGS84 UTM zone 25N", - "WGS84 UTM zone 26N", - "WGS84 UTM zone 27N", - "WGS84 UTM zone 28N", - "WGS84 UTM zone 29N", - "WGS84 UTM zone 30N", - "WGS84 UTM zone 31N", - "WGS84 UTM zone 32N", - "WGS84 UTM zone 33N", - "WGS84 UTM zone 34N", - "WGS84 UTM zone 35N", - "WGS84 UTM zone 36N", - "WGS84 UTM zone 37N", - "WGS84 UTM zone 38N", - "WGS84 UTM zone 39N", - "WGS84 UTM zone 40N", - "WGS84 UTM zone 41N", - "WGS84 UTM zone 42N", - "WGS84 UTM zone 43N", - "WGS84 UTM zone 44N", - "WGS84 UTM zone 45N", - "WGS84 UTM zone 46N", - "WGS84 UTM zone 47N", - "WGS84 UTM zone 48N", - "WGS84 UTM zone 49N", - "WGS84 UTM zone 50N", - "WGS84 UTM zone 51N", - "WGS84 UTM zone 52N", - "WGS84 UTM zone 53N", - "WGS84 UTM zone 54N", - "WGS84 UTM zone 55N", - "WGS84 UTM zone 56N", - "WGS84 UTM zone 57N", - "WGS84 UTM zone 58N", - "WGS84 UTM zone 59N", - "WGS84 UTM zone 60N", - "WGS84 UTM zone 1S", - "WGS84 UTM zone 2S", - "WGS84 UTM zone 3S", - "WGS84 UTM zone 4S", - "WGS84 UTM zone 5S", - "WGS84 UTM zone 6S", - "WGS84 UTM zone 7S", - "WGS84 UTM zone 8S", - "WGS84 UTM zone 9S", - "WGS84 UTM zone 10S", - "WGS84 UTM zone 11S", - "WGS84 UTM zone 12S", - "WGS84 UTM zone 13S", - "WGS84 UTM zone 14S", - "WGS84 UTM zone 15S", - "WGS84 UTM zone 16S", - "WGS84 UTM zone 17S", - "WGS84 UTM zone 18S", - "WGS84 UTM zone 19S", - "WGS84 UTM zone 20S", - "WGS84 UTM zone 21S", - "WGS84 UTM zone 22S", - "WGS84 UTM zone 23S", - "WGS84 UTM zone 24S", - "WGS84 UTM zone 25S", - "WGS84 UTM zone 26S", - "WGS84 UTM zone 27S", - "WGS84 UTM zone 28S", - "WGS84 UTM zone 29S", - "WGS84 UTM zone 30S", - "WGS84 UTM zone 31S", - "WGS84 UTM zone 32S", - "WGS84 UTM zone 33S", - "WGS84 UTM zone 34S", - "WGS84 UTM zone 35S", - "WGS84 UTM zone 36S", - "WGS84 UTM zone 37S", - "WGS84 UTM zone 38S", - "WGS84 UTM zone 39S", - "WGS84 UTM zone 40S", - "WGS84 UTM zone 41S", - "WGS84 UTM zone 42S", - "WGS84 UTM zone 43S", - "WGS84 UTM zone 44S", - "WGS84 UTM zone 45S", - "WGS84 UTM zone 46S", - "WGS84 UTM zone 47S", - "WGS84 UTM zone 48S", - "WGS84 UTM zone 49S", - "WGS84 UTM zone 50S", - "WGS84 UTM zone 51S", - "WGS84 UTM zone 52S", - "WGS84 UTM zone 53S", - "WGS84 UTM zone 54S", - "WGS84 UTM zone 55S", - "WGS84 UTM zone 56S", - "WGS84 UTM zone 57S", - "WGS84 UTM zone 58S", - "WGS84 UTM zone 59S", - "WGS84 UTM zone 60S", - "User-defined" }; - - /* 6.3.3.2 Projection codes */ - - public final static int[] PROJECTION_INDEX = { - 0, 10101, 10102, 10131, 10132, 10201, 10202, 10203, - 10231, 10232, 10233, 10301, 10302, 10331, 10332, 10401, - 10402, 10403, 10404, 10405, 10406, 10407, 10431, 10432, - 10433, 10434, 10435, 10436, 10501, 10502, 10503, 10531, - 10532, 10533, 10600, 10630, 10700, 10730, 10901, 10902, - 10903, 10931, 10932, 10933, 11001, 11002, 11031, 11032, - 11101, 11102, 11103, 11131, 11132, 11133, 11201, 11202, - 11231, 11232, 11301, 11302, 11331, 11332, 11401, 11402, - 11431, 11432, 11501, 11502, 11531, 11532, 11601, 11602, - 11631, 11632, 11701, 11702, 11731, 11732, 11801, 11802, - 11831, 11832, 11900, 11930, 12001, 12002, 12031, 12032, - 12101, 12102, 12103, 12111, 12112, 12113, 12141, 12142, - 12143, 12201, 12202, 12203, 12231, 12232, 12233, 12301, - 12302, 12331, 12332, 12401, 12402, 12403, 12431, 12432, - 12433, 12501, 12502, 12503, 12530, 12601, 12602, 12630, - 12701, 12702, 12703, 12731, 12732, 12733, 12800, 12830, - 12900, 12930, 13001, 13002, 13003, 13031, 13032, 13033, - 13101, 13102, 13103, 13104, 13131, 13132, 13133, 13134, - 13200, 13230, 13301, 13302, 13331, 13332, 13401, 13402, - 13431, 13432, 13501, 13502, 13531, 13532, 13601, 13602, - 13631, 13632, 13701, 13702, 13731, 13732, 13800, 13830, - 13901, 13902, 13930, 14001, 14002, 14031, 14032, 14100, - 14130, 14201, 14202, 14203, 14204, 14205, 14231, 14232, - 14233, 14234, 14235, 14301, 14302, 14303, 14331, 14332, - 14333, 14400, 14430, 14501, 14502, 14531, 14532, 14601, - 14602, 14631, 14632, 14701, 14702, 14731, 14732, 14801, - 14802, 14803, 14831, 14832, 14833, 14901, 14902, 14903, - 14904, 14931, 14932, 14933, 14934, 15001, 15002, 15003, - 15004, 15005, 15006, 15007, 15008, 15009, 15010, 15031, - 15032, 15033, 15034, 15035, 15036, 15037, 15038, 15039, - 15040, 15101, 15102, 15103, 15104, 15105, 15131, 15132, - 15133, 15134, 15135, 15201, 15202, 15230, 15914, 15915, - 15916, 15917, 17348, 17349, 17350, 17351, 17352, 17353, - 17354, 17355, 17356, 17357, 17358, 17448, 17449, 17450, - 17451, 17452, 17453, 17454, 17455, 17456, 17457, 17458, - 18031, 18032, 18033, 18034, 18035, 18036, 18037, 18051, - 18052, 18053, 18054, 18072, 18073, 18074, 18141, 18142, - 19900, 19905, 19912, 32767 - }; - public final static String[] PROJECTION = { - "Undefined", - "Alabama CS27 east", - "Alabama CS27 west", - "Alabama CS83 east", - "Alabama CS83 west", - "Arizona coordinate system east", - "Arizona coordinate system central", - "Arizona coordinate system west", - "Arizona CS83 east", - "Arizona CS83 central", - "Arizona CS83 west", - "Arkansas CS27 north", - "Arkansas CS27 south", - "Arkansas CS83 north", - "Arkansas CS83 south", - "California CS27 I", - "California CS27 II", - "California CS27 III", - "California CS27 IV", - "California CS27 V", - "California CS27 VI", - "California CS27 VII", - "California CS83 1", - "California CS83 2", - "California CS83 3", - "California CS83 4", - "California CS83 5", - "California CS83 6", - "Colorado CS27 north", - "Colorado CS27 central", - "Colorado CS27 south", - "Colorado CS83 north", - "Colorado CS83 central", - "Colorado CS83 south", - "Connecticut CS27", - "Connecticut CS83", - "Delaware CS27", - "Delaware CS83", - "Florida CS27 east", - "Florida CS27 west", - "Florida CS27 north", - "Florida CS83 east", - "Florida CS83 west", - "Florida CS83 north", - "Georgia CS27 east", - "Georgia CS27 west", - "Georgia CS83 east", - "Georgia CS83 west", - "Idaho CS27 east", - "Idaho CS27 central", - "Idaho CS27 west", - "Idaho CS83 east", - "Idaho CS83 central", - "Idaho CS83 west", - "Illinois CS27 east", - "Illinois CS27 west", - "Illinois CS83 east", - "Illinois CS83 west", - "Indiana CS27 east", - "Indiana CS27 west", - "Indiana CS83 east", - "Indiana CS83 west", - "Iowa CS27 north", - "Iowa CS27 south", - "Iowa CS83 north", - "Iowa CS83 south", - "Kansas CS27 north", - "Kansas CS27 south", - "Kansas CS83 north", - "Kansas CS83 south", - "Kentucky CS27 north", - "Kentucky CS27 south", - "Kentucky CS83 north", - "Kentucky CS83 south", - "Louisiana CS27 north", - "Louisiana CS27 south", - "Louisiana CS83 north", - "Louisiana CS83 south", - "Maine CS27 east", - "Maine CS27 west", - "Maine CS83 east", - "Maine CS83 west", - "Maryland CS27", - "Maryland CS83", - "Massachusetts CS27 mainland", - "Massachusetts CS27 island", - "Massachusetts CS83 mainland", - "Massachusetts CS83 island", - "Michigan state plane east", - "Michigan state plane old central", - "Michigan state plane west", - "Michigan CS27 north", - "Michigan CS27 central", - "Michigan CS27 south", - "Michigan CS83 north", - "Michigan CS83 central", - "Michigan CS83 south", - "Minnesota CS27 north", - "Minnesota CS27 central", - "Minnesota CS27 south", - "Minnesota CS83 north", - "Minnesota CS83 central", - "Minnesota CS83 south", - "Mississippi CS27 east", - "Mississippi CS27 west", - "Mississippi CS83 east", - "Mississippi CS83 west", - "Missouri CS27 east", - "Missouri CS27 central", - "Missouri CS27 west", - "Missouri CS83 east", - "Missouri CS83 central", - "Missouri CS83 west", - "Montana CS27 north", - "Montana CS27 central", - "Montana CS27 south", - "Montana CS83", - "Nebraska CS27 north", - "Nebraska CS27 south", - "Nebraska CS83", - "Nevada CS27 east", - "Nevada CS27 central", - "Nevada CS27 west", - "Nevada CS83 east", - "Nevada CS83 central", - "Nevada CS83 west", - "New Hampshire CS27", - "New Hampshire CS83", - "New Jersey CS27", - "New Jersey CS83", - "New Mexico CS27 east", - "New Mexico CS27 central", - "New Mexico CS27 west", - "New Mexico CS83 east", - "New Mexico CS83 central", - "New Mexico CS83 west", - "New York CS27 east", - "New York CS27 central", - "New York CS27 west", - "New York CS27 Long Island", - "New York CS83 east", - "New York CS83 central", - "New York CS83 west", - "New York CS83 Long Island", - "north Carolina CS27", - "north Carolina CS83", - "north Dakota CS27 north", - "north Dakota CS27 south", - "north Dakota CS83 north", - "north Dakota CS83 south", - "Ohio CS27 north", - "Ohio CS27 south", - "Ohio CS83 north", - "Ohio CS83 south", - "Oklahoma CS27 north", - "Oklahoma CS27 south", - "Oklahoma CS83 north", - "Oklahoma CS83 south", - "Oregon CS27 north", - "Oregon CS27 south", - "Oregon CS83 north", - "Oregon CS83 south", - "Pennsylvania CS27 north", - "Pennsylvania CS27 south", - "Pennsylvania CS83 north", - "Pennsylvania CS83 south", - "Rhode Island CS27", - "Rhode Island CS83", - "South Carolina CS27 north", - "South Carolina CS27 south", - "South Carolina CS83", - "South Dakota CS27 north", - "South Dakota CS27 south", - "South Dakota CS83 north", - "South Dakota CS83 south", - "Tennessee CS27", - "Tennessee CS83", - "Texas CS27 north", - "Texas CS27 north central", - "Texas CS27 central", - "Texas CS27 south central", - "Texas CS27 south", - "Texas CS83 north", - "Texas CS83 north central", - "Texas CS83 central", - "Texas CS83 south central", - "Texas CS83 south", - "Utah CS27 north", - "Utah CS27 central", - "Utah CS27 south", - "Utah CS83 north", - "Utah CS83 central", - "Utah CS83 south", - "Vermont CS27", - "Vermont CS83", - "Virginia CS27 north", - "Virginia CS27 south", - "Virginia CS83 north", - "Virginia CS83 south", - "Washington CS27 north", - "Washington CS27 south", - "Washington CS83 north", - "Washington CS83 south", - "west Virginia CS27 north", - "west Virginia CS27 south", - "west Virginia CS83 north", - "west Virginia CS83 south", - "Wisconsin CS27 north", - "Wisconsin CS27 central", - "Wisconsin CS27 south", - "Wisconsin CS83 north", - "Wisconsin CS83 central", - "Wisconsin CS83 south", - "Wyoming CS27 east", - "Wyoming CS27 east central", - "Wyoming CS27 west central", - "Wyoming CS27 west", - "Wyoming CS83 east", - "Wyoming CS83 east central", - "Wyoming CS83 west central", - "Wyoming CS83 west", - "Alaska CS27 1", - "Alaska CS27 2", - "Alaska CS27 3", - "Alaska CS27 4", - "Alaska CS27 5", - "Alaska CS27 6", - "Alaska CS27 7", - "Alaska CS27 8", - "Alaska CS27 9", - "Alaska CS27 10", - "Alaska CS83 1", - "Alaska CS83 2", - "Alaska CS83 3", - "Alaska CS83 4", - "Alaska CS83 5", - "Alaska CS83 6", - "Alaska CS83 7", - "Alaska CS83 8", - "Alaska CS83 9", - "Alaska CS83 10", - "Hawaii CS27 1", - "Hawaii CS27 2", - "Hawaii CS27 3", - "Hawaii CS27 4", - "Hawaii CS27 5", - "Hawaii CS83 1", - "Hawaii CS83 2", - "Hawaii CS83 3", - "Hawaii CS83 4", - "Hawaii CS83 5", - "Puerto Rico CS27", - "St Croix", - "Puerto Rico Virgin Is", - "BLM 14N feet", - "BLM 15N feet", - "BLM 16N feet", - "BLM 17N feet", - "Map grid of Australia 48", - "Map grid of Australia 49", - "Map grid of Australia 50", - "Map grid of Australia 51", - "Map grid of Australia 52", - "Map grid of Australia 53", - "Map grid of Australia 54", - "Map grid of Australia 55", - "Map grid of Australia 56", - "Map grid of Australia 57", - "Map grid of Australia 58", - "Australian map grid 48", - "Australian map grid 49", - "Australian map grid 50", - "Australian map grid 51", - "Australian map grid 52", - "Australian map grid 53", - "Australian map grid 54", - "Australian map grid 55", - "Australian map grid 56", - "Australian map grid 57", - "Australian map grid 58", - "Argentina 1", - "Argentina 2", - "Argentina 3", - "Argentina 4", - "Argentina 5", - "Argentina 6", - "Argentina 7", - "Colombia 3W", - "Colombia Bogota", - "Colombia 3E", - "Colombia 6E", - "Egypt red belt", - "Egypt purple belt", - "Extended Purple Belt", - "New Zealand north island nat grid", - "New Zealand south island nat grid", - "Bahrain grid", - "Netherlands E Indies Equatorial", - "RSO Borneo", - "User-defined" - }; - - /* 6.3.3.3 Coordinate transformation codes */ - - public final static int[] COORDINATETRANSFORMATION_INDEX = { - 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, - 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 32767 - }; - public final static String[] COORDINATETRANSFORMATION = { - "Undefined", - "Transverse Mercator", - "Transverse Mercator modified Alaska", - "Oblique Mercator", - "Oblique Mercator Laborde", - "Oblique Mercator Rosenmund", - "Oblique Mercator Spherical", - "Mercator", - "Lambert Conformal Conic 2SP", - "Lambert Conformal conic Helmert", - "Lambert Azimuthal equal area", - "Albers equal area", - "Azimuthal equidistant", - "Equidistant conic", - "Stereographic", - "Polar stereographic", - "Oblique stereographic", - "Equirectangular", - "Cassini Soldner", - "Gnomonic", - "Miller cylindrical", - "Orthographic", - "Polyconic", - "Robinson", - "Sinusoidal", - "Van Der Grinten", - "New Zealand map grid", - "Transverse Mercator south oriented", - "User-defined" }; + public static final int[] COORDINATETRANSFORMATION_INDEX = { + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, + 26, 27, 32767 + }; + public static final String[] COORDINATETRANSFORMATION = { + "Undefined", + "Transverse Mercator", + "Transverse Mercator modified Alaska", + "Oblique Mercator", + "Oblique Mercator Laborde", + "Oblique Mercator Rosenmund", + "Oblique Mercator Spherical", + "Mercator", + "Lambert Conformal Conic 2SP", + "Lambert Conformal conic Helmert", + "Lambert Azimuthal equal area", + "Albers equal area", + "Azimuthal equidistant", + "Equidistant conic", + "Stereographic", + "Polar stereographic", + "Oblique stereographic", + "Equirectangular", + "Cassini Soldner", + "Gnomonic", + "Miller cylindrical", + "Orthographic", + "Polyconic", + "Robinson", + "Sinusoidal", + "Van Der Grinten", + "New Zealand map grid", + "Transverse Mercator south oriented", + "User-defined" + }; - /* 6.3.4.1 Vertical CS type codes */ + /* 6.3.4.1 Vertical CS type codes */ - public final static int[] VERTICALCSTYPE_INDEX = { - 0, 5001, 5002, 5003, 5004, 5005, 5006, 5007, - 5008, 5010, 5011, 5012, 5013, 5014, 5015, 5016, - 5017, 5018, 5019, 5020, 5021, 5022, 5023, 5024, - 5025, 5026, 5027, 5028, 5029, 5030, 5031, 5032, - 5033, 5101, 5102, 5103, 5104, 5105, 5106, 32767 - }; - public final static String[] VERTICALCSTYPE = { - "Undefined", - "Airy 1830 ellipsoid", - "Airy modified 1849 ellipsoid", - "ANS ellipsoid", - "Bessel 1841 ellipsoid", - "Bessel Modified ellipsoid", - "Bessel Namibia ellipsoid", - "Clarke 1858 ellipsoid", - "Clarke 1866 ellipsoid", - "Clarke 1880 Benoit ellipsoid", - "Clarke 1880 IGN ellipsoid", - "Clarke 1880 RGS ellipsoid", - "Clarke 1880 arc ellipsoid", - "Clarke 1880 SGA 1922 ellipsoid", - "Everest 1830 1937 adjustment ellipsoid", - "Everest 1830 1967 definition ellipsoid", - "Everest 1830 1975 definition ellipsoid", - "Everest 1830 modified ellipsoid", - "GRS 1980 ellipsoid", - "Helmert 1906 ellipsoid", - "INS ellipsoid", - "International 1924 ellipsoid", - "International 1967 ellipsoid", - "Krassowsky 1940 ellipsoid", - "NWL 9D ellipsoid", - "NWL 10D ellipsoid", - "Plessis 1817 ellipsoid", - "Struve 1860 ellipsoid", - "War Office ellipsoid", - "WGS 84 ellipsoid", - "GEM 10C ellipsoid", - "OSU86F ellipsoid", - "OSU91A ellipsoid", - "Newlyn", - "North American vertical datum 1929", - "North American vertical datum 1988", - "Yellow Sea 1956", - "Baltic Sea", - "Caspian Sea", - "User-defined" - }; + public static final int[] VERTICALCSTYPE_INDEX = { + 0, 5001, 5002, 5003, 5004, 5005, 5006, 5007, + 5008, 5010, 5011, 5012, 5013, 5014, 5015, 5016, + 5017, 5018, 5019, 5020, 5021, 5022, 5023, 5024, + 5025, 5026, 5027, 5028, 5029, 5030, 5031, 5032, + 5033, 5101, 5102, 5103, 5104, 5105, 5106, 32767 + }; + public static final String[] VERTICALCSTYPE = { + "Undefined", + "Airy 1830 ellipsoid", + "Airy modified 1849 ellipsoid", + "ANS ellipsoid", + "Bessel 1841 ellipsoid", + "Bessel Modified ellipsoid", + "Bessel Namibia ellipsoid", + "Clarke 1858 ellipsoid", + "Clarke 1866 ellipsoid", + "Clarke 1880 Benoit ellipsoid", + "Clarke 1880 IGN ellipsoid", + "Clarke 1880 RGS ellipsoid", + "Clarke 1880 arc ellipsoid", + "Clarke 1880 SGA 1922 ellipsoid", + "Everest 1830 1937 adjustment ellipsoid", + "Everest 1830 1967 definition ellipsoid", + "Everest 1830 1975 definition ellipsoid", + "Everest 1830 modified ellipsoid", + "GRS 1980 ellipsoid", + "Helmert 1906 ellipsoid", + "INS ellipsoid", + "International 1924 ellipsoid", + "International 1967 ellipsoid", + "Krassowsky 1940 ellipsoid", + "NWL 9D ellipsoid", + "NWL 10D ellipsoid", + "Plessis 1817 ellipsoid", + "Struve 1860 ellipsoid", + "War Office ellipsoid", + "WGS 84 ellipsoid", + "GEM 10C ellipsoid", + "OSU86F ellipsoid", + "OSU91A ellipsoid", + "Newlyn", + "North American vertical datum 1929", + "North American vertical datum 1988", + "Yellow Sea 1956", + "Baltic Sea", + "Caspian Sea", + "User-defined" + }; - /* 6.3.4.2 Vertical CS datum codes */ + /* 6.3.4.2 Vertical CS datum codes */ - public final static int[] VERTICALCSDATUM_INDEX = { - 0, 32767 - }; - public final static String[] VERTICALCSDATUM = { - "Undefined", "User-defined" - }; + public static final int[] VERTICALCSDATUM_INDEX = {0, 32767}; + public static final String[] VERTICALCSDATUM = {"Undefined", "User-defined"}; - /* Private constructor to prevent instantiation */ - private GeoTiffStrings () - { - } + /* Private constructor to prevent instantiation */ + private GeoTiffStrings() {} } diff --git a/jhove-modules/tiff-hul/src/main/java/edu/harvard/hul/ois/jhove/module/tiff/GlobalParametersIFD.java b/jhove-modules/tiff-hul/src/main/java/edu/harvard/hul/ois/jhove/module/tiff/GlobalParametersIFD.java index 572f04250..d60ab2b79 100644 --- a/jhove-modules/tiff-hul/src/main/java/edu/harvard/hul/ois/jhove/module/tiff/GlobalParametersIFD.java +++ b/jhove-modules/tiff-hul/src/main/java/edu/harvard/hul/ois/jhove/module/tiff/GlobalParametersIFD.java @@ -1,167 +1,144 @@ -/********************************************************************** - * Jhove - JSTOR/Harvard Object Validation Environment - * Copyright 2004 by JSTOR and the President and Fellows of Harvard College - **********************************************************************/ - +/** + * ******************************************************************** Jhove - JSTOR/Harvard Object + * Validation Environment Copyright 2004 by JSTOR and the President and Fellows of Harvard College + * ******************************************************************** + */ package edu.harvard.hul.ois.jhove.module.tiff; import edu.harvard.hul.ois.jhove.*; import edu.harvard.hul.ois.jhove.messages.JhoveMessage; import edu.harvard.hul.ois.jhove.messages.JhoveMessages; - import java.io.*; import java.text.MessageFormat; import java.util.*; /** - * Encapsulation of a GlobalParameters IFD, as defined by - * TIFF/FX, RFC 2301. + * Encapsulation of a GlobalParameters IFD, as defined by TIFF/FX, RFC 2301. * * @author Gary McGath - * */ public class GlobalParametersIFD extends IFD { - /****************************************************************** - * PRIVATE INSTANCE FIELDS. - ******************************************************************/ - private int _profileType; - private int _faxProfile; - private int _codingMethods; - private String _versionYear; - private int _modeNumber; - - /** Tiff/FX-specific tags. */ - public static final int - PROFILETYPE = 401, - FAXPROFILE = 402, - CODINGMETHODS = 403, - VERSIONYEAR = 404, - MODENUMBER = 405; - - private static final String [] PROFILETYPE_L = { - "Unspecified", "Group 3 Fax" - }; - - private static final String [] FAXPROFILE_L = { - "does not conform to a profile defined for TIFF for facsimile", - "Minimal black & white lossless, Profile S", - "Extended black & white lossless, Profile F", - "Lossless JBIG black & white, Profile J", - "Lossy color and grayscale, Profile C", - "Lossless color and grayscale, Profile L", - "Mixed Raster Content, Profile M" - }; - - private static final String [] CODINGMETHODS_L = { - "unspecified compression", - "1-dimensional coding, ITU-T Rec. T.4 (MH - Modified Huffman)", - "2-dimensional coding, ITU-T Rec. T.4 (MR - Modified Read)", - "2-dimensional coding, ITU-T Rec. T.6 (MMR - Modified MR)", - "ITU-T Rec. T.82 coding, using ITU-T Rec. T.85 (JBIG)", - "ITU-T Rec. T.81 (Baseline JPEG)", - "ITU-T Rec. T.82 coding, using ITU-T Rec. T.43 (JBIG color)" - - }; - - /****************************************************************** - * CLASS CONSTRUCTOR. - ******************************************************************/ - - /** Instantiate a GlobalParametersIFD object. - * @param offset IFD offset - * @param raf TIFF file - * @param bigEndian True if big-endian file - */ - public GlobalParametersIFD (long offset, RepInfo info, - RandomAccessFile raf, - boolean bigEndian) - { - super (offset, info, raf, bigEndian); - - _profileType = NULL; - _faxProfile = NULL; - _codingMethods = NULL; - _versionYear = null; - _modeNumber = NULL; + /** + * **************************************************************** PRIVATE INSTANCE FIELDS. + * **************************************************************** + */ + private int _profileType; + + private int _faxProfile; + private int _codingMethods; + private String _versionYear; + private int _modeNumber; + + /** Tiff/FX-specific tags. */ + public static final int PROFILETYPE = 401, + FAXPROFILE = 402, + CODINGMETHODS = 403, + VERSIONYEAR = 404, + MODENUMBER = 405; + + private static final String[] PROFILETYPE_L = {"Unspecified", "Group 3 Fax"}; + + private static final String[] FAXPROFILE_L = { + "does not conform to a profile defined for TIFF for facsimile", + "Minimal black & white lossless, Profile S", + "Extended black & white lossless, Profile F", + "Lossless JBIG black & white, Profile J", + "Lossy color and grayscale, Profile C", + "Lossless color and grayscale, Profile L", + "Mixed Raster Content, Profile M" + }; + + private static final String[] CODINGMETHODS_L = { + "unspecified compression", + "1-dimensional coding, ITU-T Rec. T.4 (MH - Modified Huffman)", + "2-dimensional coding, ITU-T Rec. T.4 (MR - Modified Read)", + "2-dimensional coding, ITU-T Rec. T.6 (MMR - Modified MR)", + "ITU-T Rec. T.82 coding, using ITU-T Rec. T.85 (JBIG)", + "ITU-T Rec. T.81 (Baseline JPEG)", + "ITU-T Rec. T.82 coding, using ITU-T Rec. T.43 (JBIG color)" + }; + + /** + * **************************************************************** CLASS CONSTRUCTOR. + * **************************************************************** + */ + + /** + * Instantiate a GlobalParametersIFD object. + * + * @param offset IFD offset + * @param raf TIFF file + * @param bigEndian True if big-endian file + */ + public GlobalParametersIFD(long offset, RepInfo info, RandomAccessFile raf, boolean bigEndian) { + super(offset, info, raf, bigEndian); + + _profileType = NULL; + _faxProfile = NULL; + _codingMethods = NULL; + _versionYear = null; + _modeNumber = NULL; + } + + /** + * **************************************************************** PUBLIC INSTANCE METHODS. + * **************************************************************** + */ + + /** Get the IFD properties. */ + @Override + public Property getProperty(boolean rawOutput) { + List entries = new LinkedList(); + if (_profileType != NULL) { + entries.add(addIntegerProperty("ProfileType", _profileType, PROFILETYPE_L, rawOutput)); } - - - /****************************************************************** - * PUBLIC INSTANCE METHODS. - ******************************************************************/ - - /** Get the IFD properties. */ - @Override - public Property getProperty(boolean rawOutput) { - List entries = new LinkedList (); - if (_profileType != NULL) { - entries.add (addIntegerProperty ("ProfileType", _profileType, - PROFILETYPE_L, - rawOutput)); - } - if (_faxProfile != NULL) { - entries.add (addIntegerProperty ("FaxProfile", _faxProfile, - FAXPROFILE_L, - rawOutput)); - } - if (_codingMethods != NULL) { - entries.add (addBitmaskProperty ("CodingMethods", _codingMethods, - CODINGMETHODS_L, - rawOutput)); - } - if (_versionYear != null) { - entries.add (new Property ("VersionYear", - PropertyType.STRING, - _versionYear)); - } - if (_modeNumber != NULL) { - entries.add (new Property ("ModeNumber", - PropertyType.INTEGER, - new Integer (_modeNumber))); - } - return propertyHeader ("GlobalParameterIFD", entries); + if (_faxProfile != NULL) { + entries.add(addIntegerProperty("FaxProfile", _faxProfile, FAXPROFILE_L, rawOutput)); } - - /** Lookup an IFD tag. */ - @Override - public void lookupTag (int tag, int type, long count, long value) - throws TiffException - { - try { - if (tag == PROFILETYPE) { - checkType (tag, type, LONG); - checkCount (tag, count, 1); - _profileType = (int) readLong(type, count, value); - } - else if (tag == FAXPROFILE) { - checkType (tag, type, BYTE); - checkCount (tag, count, 1); - _faxProfile = readByte(type, count, value); - } - else if (tag == CODINGMETHODS) { - checkType (tag, type, LONG); - checkCount (tag, count, 1); - _codingMethods = (int) readLong(type, count, value); - } - else if (tag == VERSIONYEAR) { - checkType (tag, type, BYTE); - checkCount (tag, count, 4); - _versionYear = readASCII(count, value); - } - else if (tag == MODENUMBER) { - checkType (tag, type, BYTE); - checkCount (tag, count, 1); - _modeNumber = readByte(type, count, value); - } - } - catch (IOException e) { - String mess = MessageFormat - .format(MessageConstants.TIFF_HUL_19.getMessage(), Integer.valueOf(tag)); - JhoveMessage message = JhoveMessages.getMessageInstance( - MessageConstants.TIFF_HUL_19.getId(), mess); - throw new TiffException(message, value); - } + if (_codingMethods != NULL) { + entries.add(addBitmaskProperty("CodingMethods", _codingMethods, CODINGMETHODS_L, rawOutput)); } - + if (_versionYear != null) { + entries.add(new Property("VersionYear", PropertyType.STRING, _versionYear)); + } + if (_modeNumber != NULL) { + entries.add(new Property("ModeNumber", PropertyType.INTEGER, new Integer(_modeNumber))); + } + return propertyHeader("GlobalParameterIFD", entries); + } + + /** Lookup an IFD tag. */ + @Override + public void lookupTag(int tag, int type, long count, long value) throws TiffException { + try { + if (tag == PROFILETYPE) { + checkType(tag, type, LONG); + checkCount(tag, count, 1); + _profileType = (int) readLong(type, count, value); + } else if (tag == FAXPROFILE) { + checkType(tag, type, BYTE); + checkCount(tag, count, 1); + _faxProfile = readByte(type, count, value); + } else if (tag == CODINGMETHODS) { + checkType(tag, type, LONG); + checkCount(tag, count, 1); + _codingMethods = (int) readLong(type, count, value); + } else if (tag == VERSIONYEAR) { + checkType(tag, type, BYTE); + checkCount(tag, count, 4); + _versionYear = readASCII(count, value); + } else if (tag == MODENUMBER) { + checkType(tag, type, BYTE); + checkCount(tag, count, 1); + _modeNumber = readByte(type, count, value); + } + } catch (IOException e) { + String mess = + MessageFormat.format(MessageConstants.TIFF_HUL_19.getMessage(), Integer.valueOf(tag)); + JhoveMessage message = + JhoveMessages.getMessageInstance(MessageConstants.TIFF_HUL_19.getId(), mess); + throw new TiffException(message, value); + } + } } diff --git a/jhove-modules/tiff-hul/src/main/java/edu/harvard/hul/ois/jhove/module/tiff/IFD.java b/jhove-modules/tiff-hul/src/main/java/edu/harvard/hul/ois/jhove/module/tiff/IFD.java index 1b37b212e..d9ee80190 100644 --- a/jhove-modules/tiff-hul/src/main/java/edu/harvard/hul/ois/jhove/module/tiff/IFD.java +++ b/jhove-modules/tiff-hul/src/main/java/edu/harvard/hul/ois/jhove/module/tiff/IFD.java @@ -1,20 +1,10 @@ -/********************************************************************** - * Jhove - JSTOR/Harvard Object Validation Environment - * Copyright 2004 by JSTOR and the President and Fellows of Harvard College - **********************************************************************/ - +/** + * ******************************************************************** Jhove - JSTOR/Harvard Object + * Validation Environment Copyright 2004 by JSTOR and the President and Fellows of Harvard College + * ******************************************************************** + */ package edu.harvard.hul.ois.jhove.module.tiff; -import java.io.ByteArrayInputStream; -import java.io.DataInputStream; -import java.io.IOException; -import java.io.RandomAccessFile; -import java.text.MessageFormat; -import java.text.NumberFormat; -import java.util.LinkedList; -import java.util.List; -import java.util.ListIterator; - import edu.harvard.hul.ois.jhove.ErrorMessage; import edu.harvard.hul.ois.jhove.InfoMessage; import edu.harvard.hul.ois.jhove.ModuleBase; @@ -25,1169 +15,1101 @@ import edu.harvard.hul.ois.jhove.RepInfo; import edu.harvard.hul.ois.jhove.messages.JhoveMessage; import edu.harvard.hul.ois.jhove.messages.JhoveMessages; +import java.io.ByteArrayInputStream; +import java.io.DataInputStream; +import java.io.IOException; +import java.io.RandomAccessFile; +import java.text.MessageFormat; +import java.text.NumberFormat; +import java.util.LinkedList; +import java.util.List; +import java.util.ListIterator; -/** - * Encapsulation of a TIFF image file directory (IFD). - */ -public abstract class IFD -{ - - /****************************************************************** - * DEBUGGING FIELDS. - * All debugging fields should be set to false for release code. - ******************************************************************/ - - /* Set to true to allow out-of-sequence tags. */ - private static final boolean DEBUG_ALLOW_OUT_OF_SEQUENCE = false; - - /****************************************************************** - * PUBLIC CLASS FIELDS. - ******************************************************************/ - - /** Standard TIFF IFD. */ - public static final int TIFF = 0; - /** Exif IFD. */ - public static final int EXIF = 1; - /** Exif Interoperability IFD. */ - public static final int INTEROPERABILITY = 2; - /** GPSInfo IFD. */ - public static final int GPSINFO = 3; - /** Global parameters IFD. */ - public static final int GLOBALPARAMETERS = 4; - - - /** Undefined value for integer tags. */ - public static final int NULL = -1; - - /****************************************************************** - * PRIVATE CLASS FIELDS. - ******************************************************************/ - - /* TIFF data types. */ - - /** TIFF BYTE (unsigned 8-bit) type. */ - public static final int BYTE = 1; - /** TIFF ASCII type. */ - public static final int ASCII = 2; - /** TIFF SHORT (unsigned 16-bit) type. */ - public static final int SHORT = 3; - /** TIFF LONG (unsigned 32-bit) type. */ - public static final int LONG = 4; - /** TIFF RATIONAL (two LONGs) type. */ - public static final int RATIONAL = 5; - /** TIFF SBYTE (signed 8-bit) type. */ - public static final int SBYTE = 6; - /** TIFF UNDEFINED (unsigned 8-bit) type. */ - public static final int UNDEFINED = 7; - /** TIFF SSHORT (signed 16-bit) type. */ - public static final int SSHORT = 8; - /** TIFF SLONG (signed 32-bit) type. */ - public static final int SLONG = 9; - /** TIFF SRATIONAL (two SLONGs) type. */ - public static final int SRATIONAL = 10; - /** TIFF FLOAT (32-bit IEEE floating point) type. */ - public static final int FLOAT = 11; - /** TIFF DOUBLE (64-bit IEEE floating point) type. */ - public static final int DOUBLE = 12; - /** TIFF IFD (LONG) type. */ - public static final int IFD = 13; - - /** TIFF type labels. */ - public static final String TYPE [] = { - "", "BYTE", "ASCII", "SHORT", "LONG", "RATIONAL", "SBYTE", "UNDEFINED", - "SSHORT", "SLONG", "SRATIONAL", "FLOAT", "DOUBLE", "IFD" - }; - - /****************************************************************** - * PRIVATE INSTANCE FIELDS. - ******************************************************************/ - - /** True if big-endian file. */ - protected boolean _bigEndian; - - /** List of errors. */ - private List _errors; - - /** True if this is the first IFD. */ - private boolean _first; - - /** True if the is the "thumbnail" IFD. */ - private boolean _thumbnail; - - /** Format for converting float to string. */ - private NumberFormat _format; - - /** Representation information. */ - protected RepInfo _info; - - /** Offset of next IFD. */ - protected long _next; - - /** IFD offset. */ - protected long _offset; - - /** Open random access TIFF file. */ - private RandomAccessFile _raf; - - /** TIFF version. */ - protected int _version; - - /****************************************************************** - * CLASS CONSTRUCTOR. - ******************************************************************/ - - /** Instantiate an IFD object. - * @param offset IFD offset - * @param info Representation information - * @param raf TIFF file - * @param bigEndian True if big-endian file - */ - public IFD(long offset, RepInfo info, RandomAccessFile raf, - boolean bigEndian) - { - _offset = offset; - _info = info; - _raf = raf; - _bigEndian = bigEndian; - - _first = false; - _thumbnail = false; - _next = 0L; - _version = 4; - - _errors = new LinkedList<>(); - - _format = NumberFormat.getInstance(); - _format.setGroupingUsed(false); - _format.setMinimumFractionDigits(0); - } - - /****************************************************************** - * PUBLIC INSTANCE METHODS. - ******************************************************************/ - - /** Get any errors discovered during parsing. - * @return list of strings with errors - */ - public List getErrors() - { - return _errors; - } - - /** Get the offset of the next IFD. - * @return next - */ - public long getNext() - { - return _next; - } - - /** Get the IFD offset. - * @return IFD offset - */ - public long getOffset() - { - return _offset; - } - - /** Get the IFD properties. - * @param rawOutput: boolean - * @return Property - * @throws edu.harvard.hul.ois.jhove.module.tiff.TiffException - */ - public abstract Property getProperty(boolean rawOutput) - throws TiffException; - - /** Get the TIFF version. - * @return TIFF version - */ - public int getVersion() - { - return _version; - } - - /** Return true if this is the first IFD. - * @return if it's first? - */ - public boolean isFirst() - { - return _first; - } - - /** Return true if this is the thumbnail IFD. - * @return if it's a thumbnail - */ - public boolean isThumbnail() - { - return _thumbnail; +/** Encapsulation of a TIFF image file directory (IFD). */ +public abstract class IFD { + + /** + * **************************************************************** DEBUGGING FIELDS. All + * debugging fields should be set to false for release code. + * **************************************************************** + */ + + /* Set to true to allow out-of-sequence tags. */ + private static final boolean DEBUG_ALLOW_OUT_OF_SEQUENCE = false; + + /** + * **************************************************************** PUBLIC CLASS FIELDS. + * **************************************************************** + */ + + /** Standard TIFF IFD. */ + public static final int TIFF = 0; + /** Exif IFD. */ + public static final int EXIF = 1; + /** Exif Interoperability IFD. */ + public static final int INTEROPERABILITY = 2; + /** GPSInfo IFD. */ + public static final int GPSINFO = 3; + /** Global parameters IFD. */ + public static final int GLOBALPARAMETERS = 4; + + /** Undefined value for integer tags. */ + public static final int NULL = -1; + + /** + * **************************************************************** PRIVATE CLASS FIELDS. + * **************************************************************** + */ + + /* TIFF data types. */ + + /** TIFF BYTE (unsigned 8-bit) type. */ + public static final int BYTE = 1; + /** TIFF ASCII type. */ + public static final int ASCII = 2; + /** TIFF SHORT (unsigned 16-bit) type. */ + public static final int SHORT = 3; + /** TIFF LONG (unsigned 32-bit) type. */ + public static final int LONG = 4; + /** TIFF RATIONAL (two LONGs) type. */ + public static final int RATIONAL = 5; + /** TIFF SBYTE (signed 8-bit) type. */ + public static final int SBYTE = 6; + /** TIFF UNDEFINED (unsigned 8-bit) type. */ + public static final int UNDEFINED = 7; + /** TIFF SSHORT (signed 16-bit) type. */ + public static final int SSHORT = 8; + /** TIFF SLONG (signed 32-bit) type. */ + public static final int SLONG = 9; + /** TIFF SRATIONAL (two SLONGs) type. */ + public static final int SRATIONAL = 10; + /** TIFF FLOAT (32-bit IEEE floating point) type. */ + public static final int FLOAT = 11; + /** TIFF DOUBLE (64-bit IEEE floating point) type. */ + public static final int DOUBLE = 12; + /** TIFF IFD (LONG) type. */ + public static final int IFD = 13; + + /** TIFF type labels. */ + public static final String TYPE[] = { + "", + "BYTE", + "ASCII", + "SHORT", + "LONG", + "RATIONAL", + "SBYTE", + "UNDEFINED", + "SSHORT", + "SLONG", + "SRATIONAL", + "FLOAT", + "DOUBLE", + "IFD" + }; + + /** + * **************************************************************** PRIVATE INSTANCE FIELDS. + * **************************************************************** + */ + + /** True if big-endian file. */ + protected boolean _bigEndian; + + /** List of errors. */ + private List _errors; + + /** True if this is the first IFD. */ + private boolean _first; + + /** True if the is the "thumbnail" IFD. */ + private boolean _thumbnail; + + /** Format for converting float to string. */ + private NumberFormat _format; + + /** Representation information. */ + protected RepInfo _info; + + /** Offset of next IFD. */ + protected long _next; + + /** IFD offset. */ + protected long _offset; + + /** Open random access TIFF file. */ + private RandomAccessFile _raf; + + /** TIFF version. */ + protected int _version; + + /** + * **************************************************************** CLASS CONSTRUCTOR. + * **************************************************************** + */ + + /** + * Instantiate an IFD object. + * + * @param offset IFD offset + * @param info Representation information + * @param raf TIFF file + * @param bigEndian True if big-endian file + */ + public IFD(long offset, RepInfo info, RandomAccessFile raf, boolean bigEndian) { + _offset = offset; + _info = info; + _raf = raf; + _bigEndian = bigEndian; + + _first = false; + _thumbnail = false; + _next = 0L; + _version = 4; + + _errors = new LinkedList<>(); + + _format = NumberFormat.getInstance(); + _format.setGroupingUsed(false); + _format.setMinimumFractionDigits(0); + } + + /** + * **************************************************************** PUBLIC INSTANCE METHODS. + * **************************************************************** + */ + + /** + * Get any errors discovered during parsing. + * + * @return list of strings with errors + */ + public List getErrors() { + return _errors; + } + + /** + * Get the offset of the next IFD. + * + * @return next + */ + public long getNext() { + return _next; + } + + /** + * Get the IFD offset. + * + * @return IFD offset + */ + public long getOffset() { + return _offset; + } + + /** + * Get the IFD properties. + * + * @param rawOutput: boolean + * @return Property + * @throws edu.harvard.hul.ois.jhove.module.tiff.TiffException + */ + public abstract Property getProperty(boolean rawOutput) throws TiffException; + + /** + * Get the TIFF version. + * + * @return TIFF version + */ + public int getVersion() { + return _version; + } + + /** + * Return true if this is the first IFD. + * + * @return if it's first? + */ + public boolean isFirst() { + return _first; + } + + /** + * Return true if this is the thumbnail IFD. + * + * @return if it's a thumbnail + */ + public boolean isThumbnail() { + return _thumbnail; + } + + /** + * Lookup IFD tag. + * + * @param tag + * @param type + * @param count + * @param value + * @throws edu.harvard.hul.ois.jhove.module.tiff.TiffException + */ + public abstract void lookupTag(int tag, int type, long count, long value) throws TiffException; + + /** + * Parse the IFD.Errors are not suppressed, and odd byte offsets for tags not allowed. + * + * @return The offset of the next IFD + * @throws edu.harvard.hul.ois.jhove.module.tiff.TiffException + */ + public long parse() throws TiffException { + return parse(false, false); + } + + /** + * Parse the IFD. + * + * @param byteOffsetIsValid If true, allow offsets on odd byte boundaries + * @param suppressErrors If true, return IFD even with errors + * @return The offset of the next IFD + * @throws edu.harvard.hul.ois.jhove.module.tiff.TiffException + */ + public long parse(boolean byteOffsetIsValid, boolean suppressErrors) throws TiffException { + try { + return parse(byteOffsetIsValid); + } catch (TiffException e) { + // If we got a TiffException and we're suppressing errors, + // cover over the exception and issue an info message; + // but we can't follow the IFD chain further. + if (suppressErrors) { + _info.setMessage(new InfoMessage(e.getMessage(), e.getOffset())); + return 0; + } + throw e; } - - - /** Lookup IFD tag. - * @param tag - * @param type - * @param count - * @param value - * @throws edu.harvard.hul.ois.jhove.module.tiff.TiffException + } + + /** + * Parse the IFD.Errors are not suppressed. + * + * @param byteOffsetIsValid If true, allow offsets on odd byte boundaries + * @return The offset of the next IFD + * @throws edu.harvard.hul.ois.jhove.module.tiff.TiffException + */ + public long parse(boolean byteOffsetIsValid) throws TiffException { + /* Start at the IFD offset, read the number of entries, then + * read the entire IFD. */ - public abstract void lookupTag(int tag, int type, long count, long value) - throws TiffException; - - /** Parse the IFD.Errors are not suppressed, and odd byte offsets for - * tags not allowed. - * - * @return The offset of the next IFD - * @throws edu.harvard.hul.ois.jhove.module.tiff.TiffException - */ - public long parse() - throws TiffException - { - return parse(false, false); + long offset = _offset; + _next = 0L; + byte[] buffer; + int nFields = 0; + try { + _raf.seek(offset); + nFields = ModuleBase.readUnsignedShort(_raf, _bigEndian); + offset += 2; + + int len = 12 * nFields; + buffer = new byte[len]; + _raf.read(buffer, 0, len); + + /* Read the offset of the next IFD (or 0 if none). */ + offset += len; + _next = ModuleBase.readUnsignedInt(_raf, _bigEndian); + } catch (Exception e) { + throw new TiffException(MessageConstants.TIFF_HUL_1, offset); } - /** Parse the IFD. - * @param byteOffsetIsValid If true, allow offsets on odd byte boundaries - * @param suppressErrors If true, return IFD even with errors - * @return The offset of the next IFD - * @throws edu.harvard.hul.ois.jhove.module.tiff.TiffException - */ - public long parse(boolean byteOffsetIsValid, boolean suppressErrors) - throws TiffException - { - try { - return parse(byteOffsetIsValid); + DataInputStream ifdStream = new DataInputStream(new ByteArrayInputStream(buffer)); + + try { + int prevTag = 0; + for (int i = 0; i < nFields; i++) { + int tag = ModuleBase.readUnsignedShort(ifdStream, _bigEndian, null); + /* Tags must be in ascending numerical order. */ + if (!DEBUG_ALLOW_OUT_OF_SEQUENCE && tag < prevTag) { + String mess = MessageFormat.format(MessageConstants.TIFF_HUL_2.getMessage(), tag); + JhoveMessage message = + JhoveMessages.getMessageInstance(MessageConstants.TIFF_HUL_2.getId(), mess); + _info.setMessage(new ErrorMessage(message, _offset + 2 + 12 * i)); + _info.setWellFormed(false); } - catch (TiffException e) { - // If we got a TiffException and we're suppressing errors, - // cover over the exception and issue an info message; - // but we can't follow the IFD chain further. - if (suppressErrors) { - _info.setMessage - (new InfoMessage(e.getMessage(), e.getOffset())); - return 0; + prevTag = tag; + + int type = ModuleBase.readUnsignedShort(ifdStream, _bigEndian, null); + /* Skip over tags with unknown type. */ + if (type < BYTE || type > IFD) { + String subMess = + MessageFormat.format( + MessageConstants.TIFF_HUL_3_SUB.getMessage(), type, Integer.valueOf(tag)); + _info.setMessage( + new ErrorMessage(MessageConstants.TIFF_HUL_3, subMess, _offset + 4 + 12 * i)); + } else { + /* Type gives indication of the TIFF version. */ + if (SBYTE <= type && type <= IFD) { + _version = 6; + } + + long count = ModuleBase.readUnsignedInt(ifdStream, _bigEndian, null); + long value = ModuleBase.readUnsignedInt(ifdStream, _bigEndian, null); + if (calcValueSize(type, count) > 4) { + /* Value is the word-aligned offset of the actual + * value. */ + if ((value & 1) != 0) { + final String mess = + MessageFormat.format(MessageConstants.TIFF_HUL_4.getMessage(), value); + JhoveMessage message = + JhoveMessages.getMessageInstance(MessageConstants.TIFF_HUL_4.getId(), mess); + if (byteOffsetIsValid) { + _info.setMessage(new InfoMessage(message, _offset + 10 + 12 * i)); + } else { + throw new TiffException(message, _offset + 10 + 12 * i); + } } - throw e; + } else { + /* Value is the actual value; pass the offset of + * the value. */ + value = _offset + 10 + 12 * i; + } + lookupTag(tag, type, count, value); } + } + } catch (IOException e) { + throw new TiffException(MessageConstants.TIFF_HUL_5, _offset + 2); } - - - /** Parse the IFD.Errors are not suppressed. - * - * @param byteOffsetIsValid If true, allow offsets on odd byte boundaries - * @return The offset of the next IFD - * @throws edu.harvard.hul.ois.jhove.module.tiff.TiffException - */ - public long parse(boolean byteOffsetIsValid) - throws TiffException - { - /* Start at the IFD offset, read the number of entries, then - * read the entire IFD. - */ - long offset = _offset; - _next = 0L; - byte [] buffer; - int nFields = 0; - try { - _raf.seek(offset); - nFields = ModuleBase.readUnsignedShort(_raf, _bigEndian); - offset += 2; - - int len = 12*nFields; - buffer = new byte[len]; - _raf.read(buffer, 0, len); - - /* Read the offset of the next IFD (or 0 if none). */ - offset += len; - _next = ModuleBase.readUnsignedInt(_raf, _bigEndian); - } - catch (Exception e) { - throw new TiffException(MessageConstants.TIFF_HUL_1, offset); - } - - DataInputStream ifdStream = - new DataInputStream(new ByteArrayInputStream(buffer)); - - try { - int prevTag = 0; - for (int i=0; i IFD) { - String subMess = MessageFormat.format(MessageConstants.TIFF_HUL_3_SUB.getMessage(), type, Integer.valueOf(tag)); - _info.setMessage(new ErrorMessage(MessageConstants.TIFF_HUL_3, subMess, - _offset + 4 + 12*i)); - } - else { - /* Type gives indication of the TIFF version. */ - if (SBYTE <= type && type <= IFD) { - _version = 6; - } - - long count = ModuleBase.readUnsignedInt(ifdStream, - _bigEndian, null); - long value = ModuleBase.readUnsignedInt(ifdStream, - _bigEndian, null); - if (calcValueSize(type, count) > 4) { - /* Value is the word-aligned offset of the actual - * value. */ - if ((value & 1) != 0) { - final String mess = MessageFormat.format(MessageConstants.TIFF_HUL_4.getMessage(), value); - JhoveMessage message = JhoveMessages.getMessageInstance(MessageConstants.TIFF_HUL_4.getId(), mess); - if (byteOffsetIsValid) { - _info.setMessage(new InfoMessage(message, _offset + 10 + 12*i)); - } - else { - throw new TiffException(message,_offset + 10 + 12*i); - } - } - } - else { - /* Value is the actual value; pass the offset of - * the value. */ - value = _offset + 10 + 12*i; - } - lookupTag(tag, type, count, value); - } - } + postParseInitialization(); + + return _next; + } + + /** + * Sets flag indicating whether this is the first IFD. + * + * @param first: true if it's the first IFD + */ + public void setFirst(boolean first) { + _first = first; + } + + /** + * Sets flag indicating whether this is the "thumbnail" IFD. The second IFD in the top-level chain + * is assumed to be the Thumbnail IFD. + * + * @param thumbnail: flag true if this is the thumbnail IFD + */ + public void setThumbnail(boolean thumbnail) { + _thumbnail = thumbnail; + } + + /** + * Returns a Property representing a bitmask. If rawOutput is true, returns a LIST + * property whose elements are STRING properties. The string values of these STRING properties are + * the elements of labels whose indices correspond to 1 bits in the bitmask, counting + * the low-order bit as bit 0. if rawOutput is false, returns a LONG property whose + * numeric value is value. + * + * @param name + * @param value + * @param labels + * @param rawOutput: true if string output is wanted, false returns a long property + * @return property representing a bitmask + */ + protected Property addBitmaskProperty( + String name, long value, String[] labels, boolean rawOutput) { + Property prop = null; + if (!rawOutput) { + List list = new LinkedList<>(); + try { + for (int i = 0; i < labels.length; i++) { + if ((value & (1 << i)) != 0) { + list.add(labels[i]); + } } - catch (IOException e) { - throw new TiffException(MessageConstants.TIFF_HUL_5, _offset + 2); - } - postParseInitialization(); - - return _next; - } - - /** Sets flag indicating whether this is the first IFD. - * @param first: true if it's the first IFD - */ - public void setFirst(boolean first) - { - _first = first; + } catch (Exception e) { + _errors.add( + MessageFormat.format( + MessageConstants.TIFF_HUL_66.getMessage(), name, Long.valueOf(value))); + } + prop = new Property(name, PropertyType.STRING, PropertyArity.LIST, list); } - - /** Sets flag indicating whether this is the "thumbnail" IFD. - * The second IFD in the top-level chain is assumed to be - * the Thumbnail IFD. - * @param thumbnail: flag true if this is the thumbnail IFD - */ - public void setThumbnail(boolean thumbnail) - { - _thumbnail = thumbnail; + if (prop == null) { + prop = new Property(name, PropertyType.LONG, value); } - /** - * Returns a Property representing a bitmask. - * If rawOutput is true, returns a LIST - * property whose elements are STRING properties. The - * string values of these STRING properties are the - * elements of labels whose indices - * correspond to 1 bits in the bitmask, counting - * the low-order bit as bit 0. - * if rawOutput is false, returns a LONG - * property whose numeric value is value. - * - * @param name - * @param value - * @param labels - * @param rawOutput: true if string output is wanted, false returns - * a long property - * @return property representing a bitmask - * - */ - protected Property addBitmaskProperty(String name, long value, - String [] labels, boolean rawOutput) - { - Property prop = null; - if (!rawOutput) { - List list = new LinkedList<>(); - try { - for (int i=0; irawOutput is true, returns - * an INTEGER property, and labels and - * index are unused. Otherwise, - * returns a STRING property, with the - * string being the element of labels - * whose index is value. - * - * @param name - * @param value - * @param labels - * @param rawOutput: true if integer output is wanted, false returns - * a string property - * @return property representing an integer value - */ - protected Property addIntegerProperty(String name, int value, - String [] labels, boolean rawOutput) - { - Property prop = null; - if (!rawOutput) { - try { - prop = new Property(name, PropertyType.STRING, labels[value]); - } - catch (ArrayIndexOutOfBoundsException aioobe) { - _errors.add(MessageFormat.format(MessageConstants.TIFF_HUL_66.getMessage(), name, Long.valueOf(value))); - } - } - if (prop == null) { - prop = new Property(name, PropertyType.INTEGER, value); - } - - return prop; - } - - /** - * Returns an Property representing an integer value. - * If rawOutput is true, returns - * an INTEGER property, and labels and - * index are unused. Otherwise, - * returns a STRING property, with the - * string being the element of labels - * whose index is the index of - * value in index. - * - * @param name - * @param value - * @param labels - * @param index - * @param rawOutput: true if int output is wanted, false returns - * a string property - * @return property representing an integer value - */ - protected Property addIntegerProperty(String name, int value, - String [] labels, int [] index, - boolean rawOutput) - { - Property prop = null; - if (!rawOutput) { - int n = -1; - for (int i = 0; i < index.length; i++) { - if (value == index[i]) { - n = i; - break; - } - } - if (n > -1) { - prop = new Property(name, PropertyType.STRING, labels[n]); - } - else { - _errors.add(MessageFormat.format(MessageConstants.TIFF_HUL_66.getMessage(), name, Long.valueOf(value))); - } - } - if (prop == null) { - prop = new Property(name, PropertyType.INTEGER, - value); - } - - return prop; - } - - /** - * Returns an ARRAY Property representing an integer array. - * If rawOutput is true, the elements of the property array - * are INTEGER properties, and labels is unused. Otherwise, - * the elements of the array are STRING properties, with the - * elements of value used as indices into - * labels. - * - * @param name - * @param value - * @param labels - * @param rawOutput: true if int output is wanted, false returns - * a string property - * @return property representing an integer array - */ - protected Property addIntegerArrayProperty(String name, int [] value, - String [] labels, - boolean rawOutput) - { - Property prop = null; - if (!rawOutput) { - String [] s = new String[value.length]; - for (int i=0; irawOutput is true, returns + * an INTEGER property, and labels and index are unused. Otherwise, + * returns a STRING property, with the string being the element of labels whose index + * is value. + * + * @param name + * @param value + * @param labels + * @param rawOutput: true if integer output is wanted, false returns a string property + * @return property representing an integer value + */ + protected Property addIntegerProperty( + String name, int value, String[] labels, boolean rawOutput) { + Property prop = null; + if (!rawOutput) { + try { + prop = new Property(name, PropertyType.STRING, labels[value]); + } catch (ArrayIndexOutOfBoundsException aioobe) { + _errors.add( + MessageFormat.format( + MessageConstants.TIFF_HUL_66.getMessage(), name, Long.valueOf(value))); + } } - - /** - * Returns a property for a tag with a RATIONAL value. - * If rawOutput is true, returns a property with type - * RATIONAL. Otherwise, returns a property with type - * STRING, and the text representation of the Rational - * value as a floating-point ratio. - * - * @param name - * @param r: a rational value - * @param rawOutput: true if int output is wanted, false returns - * a string property - * @return property for a tag with a RATIONAL value - */ - protected Property addRationalProperty(String name, Rational r, - boolean rawOutput) - { - Property prop = null; - if (!rawOutput) { - prop = new Property(name, PropertyType.STRING, - _format.format(r.toDouble())); - } - if (prop == null) { - prop = new Property(name, PropertyType.RATIONAL, r); - } - - return prop; + if (prop == null) { + prop = new Property(name, PropertyType.INTEGER, value); } - protected Property addRationalArrayProperty(String name, Rational [] r, - boolean rawOutput) - { - Property prop = null; - if (!rawOutput) { - String [] s = new String[r.length]; - for (int i=0; irawOutput is true, returns + * an INTEGER property, and labels and index are unused. Otherwise, + * returns a STRING property, with the string being the element of labels whose index + * is the index of value in index. + * + * @param name + * @param value + * @param labels + * @param index + * @param rawOutput: true if int output is wanted, false returns a string property + * @return property representing an integer value + */ + protected Property addIntegerProperty( + String name, int value, String[] labels, int[] index, boolean rawOutput) { + Property prop = null; + if (!rawOutput) { + int n = -1; + for (int i = 0; i < index.length; i++) { + if (value == index[i]) { + n = i; + break; } - - return prop; - } - - /** Perform initializations that have to wait until after the - * IFD has been parsed. - */ - protected void postParseInitialization() - { + } + if (n > -1) { + prop = new Property(name, PropertyType.STRING, labels[n]); + } else { + _errors.add( + MessageFormat.format( + MessageConstants.TIFF_HUL_66.getMessage(), name, Long.valueOf(value))); + } } - - /** Standard IFD property header. - * - * @param type - * @param entries - * @return IDF property header - */ - protected Property propertyHeader(String type, List entries) - { - Property [] array = new Property [3]; - array[0] = new Property("Offset", PropertyType.LONG,_offset); - array[1] = new Property("Type", PropertyType.STRING, type); - array[2] = new Property("Entries", PropertyType.PROPERTY, - PropertyArity.LIST, entries); - - return new Property("IFD", PropertyType.PROPERTY, PropertyArity.ARRAY, - array); + if (prop == null) { + prop = new Property(name, PropertyType.INTEGER, value); } - /** - * Reads a string value from the TIFF file. - * If there are non-ASCII characters, they're escaped as %XX - * @param count Length of string - * @param value Offset of string - * @return ASCII string - * @throws IOException - */ - protected String readASCII(long count, long value) - throws IOException - { - _raf.seek(value); - - byte [] buffer = new byte [(int) count]; - _raf.read(buffer); - - StringBuffer sb = new StringBuffer(); - for (int i=0; i 127) { - sb.append(byteToHex(c)); - } - else { - sb.append((char) c); - } + return prop; + } + + /** + * Returns an ARRAY Property representing an integer array. If rawOutput is true, the + * elements of the property array are INTEGER properties, and labels is unused. + * Otherwise, the elements of the array are STRING properties, with the elements of value + * used as indices into labels. + * + * @param name + * @param value + * @param labels + * @param rawOutput: true if int output is wanted, false returns a string property + * @return property representing an integer array + */ + protected Property addIntegerArrayProperty( + String name, int[] value, String[] labels, boolean rawOutput) { + Property prop = null; + if (!rawOutput) { + String[] s = new String[value.length]; + for (int i = 0; i < value.length; i++) { + try { + s[i] = labels[value[i]]; + } catch (ArrayIndexOutOfBoundsException aioobe) { + _errors.add( + MessageFormat.format( + MessageConstants.TIFF_HUL_66.getMessage(), name, Long.valueOf(value[i]))); } - return sb.toString(); + } + prop = new Property(name, PropertyType.STRING, PropertyArity.ARRAY, s); } - - /** Reads an array of strings from the TIFF file. - * - * @param count Number of strings to read - * @param value Offset from which to read - * - * @return ASCII string array - * @throws IOException - */ - protected String [] readASCIIArray(long count, long value) - throws IOException - { - _raf.seek(value); - - int nstrs = 0; - List list = new LinkedList<>(); - byte[] buf = new byte[(int) count]; - _raf.read(buf); - StringBuffer strbuf = new StringBuffer(); - for (int i=0; i 127) { - strbuf.append(byteToHex((byte) b)); - } - else { - strbuf.append((char) b); - } - } - } - /* We can't use ArrayList.toArray because that returns an - Object[], not a String[] ... sigh. */ - String [] strs = new String[nstrs]; - ListIterator iter = list.listIterator(); - for (int i=0; i 127) { + sb.append(byteToHex(c)); + } else { + sb.append((char) c); + } } - - - - /** - * Reads a TIFF array of signed 32-bit integer values and returns - * it as a long array. - * - * @param type TIFF type to read; must be a 32-bit type - * @param count Number of values to read - * @param value Offset from which to read - * - * @return long array - * @throws IOException - */ - protected long [] readLongArray(int type, long count, long value) - throws IOException - { - _raf.seek(value); - - long [] array = new long [(int) count]; - for (int i=0; i list = new LinkedList<>(); + byte[] buf = new byte[(int) count]; + _raf.read(buf); + StringBuffer strbuf = new StringBuffer(); + for (int i = 0; i < count; i++) { + int b = buf[i]; + if (b == 0) { + list.add(strbuf.toString()); + strbuf.setLength(0); + } else { + // Escape characters that aren't ASCII. There really shouldn't + // be any, and if there are, we don't know how they're encoded. + if (b < 32 || b > 127) { + strbuf.append(byteToHex((byte) b)); + } else { + strbuf.append((char) b); } - return array; + } } - - /** Reads an unsigned number of any type. - * @param type TIFF type to read - * - * @return unsigned number - * @throws IOException - */ - public long readUnsigned(int type) - throws IOException - { - long u = 0L; - - switch (type) { - case BYTE: - case UNDEFINED: - u = ModuleBase.readUnsignedByte(_raf); - break; - case SHORT: - u = ModuleBase.readUnsignedShort(_raf, _bigEndian); - break; - case LONG: - case IFD: - u = ModuleBase.readUnsignedInt(_raf, _bigEndian); - break; - default: - break; - } - - return u; + /* We can't use ArrayList.toArray because that returns an + Object[], not a String[] ... sigh. */ + String[] strs = new String[nstrs]; + ListIterator iter = list.listIterator(); + for (int i = 0; i < nstrs; i++) { + strs[i] = iter.next(); } - - - /** Reads a RATIONAL value and returns it as a Rational. - * - * @param count - * @param value - * - * @return rational - * @throws IOException - */ - protected Rational readRational(long count, long value) - throws IOException - { - _raf.seek(value); - - long numer = ModuleBase.readUnsignedInt(_raf, _bigEndian); - long denom = ModuleBase.readUnsignedInt(_raf, _bigEndian); - return new Rational(numer, denom); + return strs; + } + + /** + * Reads and returns a single unsigned 8-bit integer value. + * + * @param type TIFF type to read; must be an 8-bit type + * @param count Unused + * @param value Offset from which to read + * @return unsigned 8-bit integer value + * @throws java.io.IOException + */ + protected int readByte(int type, long count, long value) throws IOException { + _raf.seek(value); + + return (int) readUnsigned(type); + } + + /** + * Reads an array of bytes and returns it as an int array. + * + * @param type TIFF type to read; must be an 8-bit type + * @param count Number of bytes to read + * @param value Offset from which to read + * @return int array of bytes + * @throws IOException + */ + protected int[] readByteArray(int type, long count, long value) throws IOException { + _raf.seek(value); + + int[] array = new int[(int) count]; + for (int i = 0; i < count; i++) { + array[i] = (int) readUnsigned(type); } - /** Reads an array of RATIONAL values and returns it as an - * array of Rational. - * - * @param count - * @param value - * - * @return array of rational values - * @throws IOException - */ - protected Rational [] readRationalArray(long count, long value) - throws IOException - { - _raf.seek(value); - - byte [] buffer = new byte [(int) calcValueSize(RATIONAL, count)]; - _raf.read(buffer); - DataInputStream stream = - new DataInputStream(new ByteArrayInputStream(buffer)); - - Rational [] rarray = new Rational [(int) count]; - for (int i=0; itrue if file is big-endian, - * false if little-endian. - */ - public boolean isBigEndian() - { - return _bigEndian; + return iarray; + } + + /** + * Calculate how many bytes a given number of fields of a given type will require. + * + * @param type Field type + * @param count Field count + * @return number of bytes + */ + public static long calcValueSize(int type, long count) { + int fieldSize = 0; + switch (type) { + case BYTE: + case ASCII: + case SBYTE: + case UNDEFINED: + fieldSize = 1; + break; + case SHORT: + case SSHORT: + case FLOAT: + fieldSize = 2; + break; + case LONG: + case SLONG: + case IFD: + fieldSize = 4; + break; + case RATIONAL: + case SRATIONAL: + case DOUBLE: + fieldSize = 8; + break; + default: + break; } - - - - /****************************************************************** - * PRIVATE CLASS METHODS. - ******************************************************************/ - - /** - * Check the tag entry count. - * @param tag Tag entry value - * @param count Tag entry count - * @param minCount Tag count - * @throws edu.harvard.hul.ois.jhove.module.tiff.TiffException - */ - protected static void checkCount(int tag, long count, int minCount) - throws TiffException - { - if (count < minCount) { - String mess = MessageFormat.format(MessageConstants.TIFF_HUL_6.getMessage(), - tag, Integer.valueOf(minCount), Long.valueOf(count)); - JhoveMessage message = JhoveMessages.getMessageInstance(MessageConstants.TIFF_HUL_6.getId(), mess); - throw new TiffException(message); - } + return count * fieldSize; + } + + /** Returns true if file is big-endian, false if little-endian. */ + public boolean isBigEndian() { + return _bigEndian; + } + + /** + * **************************************************************** PRIVATE CLASS METHODS. + * **************************************************************** + */ + + /** + * Check the tag entry count. + * + * @param tag Tag entry value + * @param count Tag entry count + * @param minCount Tag count + * @throws edu.harvard.hul.ois.jhove.module.tiff.TiffException + */ + protected static void checkCount(int tag, long count, int minCount) throws TiffException { + if (count < minCount) { + String mess = + MessageFormat.format( + MessageConstants.TIFF_HUL_6.getMessage(), + tag, + Integer.valueOf(minCount), + Long.valueOf(count)); + JhoveMessage message = + JhoveMessages.getMessageInstance(MessageConstants.TIFF_HUL_6.getId(), mess); + throw new TiffException(message); } - - /** - * Check that the count is compatible with array instanciation. - * @param tag Tag entry value - * @param count Tag entry count - */ - protected static void checkCountArray(int tag, long count) - throws TiffException - { - if (count > Integer.MAX_VALUE) { - String mess = MessageFormat.format(MessageConstants.TIFF_HUL_6.getMessage(), Integer.valueOf(tag), Integer.valueOf(Integer.MAX_VALUE), Long.valueOf(count)); - JhoveMessage message = JhoveMessages.getMessageInstance(MessageConstants.TIFF_HUL_6.getId(), mess); - throw new TiffException(message); - } + } + + /** + * Check that the count is compatible with array instanciation. + * + * @param tag Tag entry value + * @param count Tag entry count + */ + protected static void checkCountArray(int tag, long count) throws TiffException { + if (count > Integer.MAX_VALUE) { + String mess = + MessageFormat.format( + MessageConstants.TIFF_HUL_6.getMessage(), + Integer.valueOf(tag), + Integer.valueOf(Integer.MAX_VALUE), + Long.valueOf(count)); + JhoveMessage message = + JhoveMessages.getMessageInstance(MessageConstants.TIFF_HUL_6.getId(), mess); + throw new TiffException(message); } - - /** - * Check the tag entry type. - * @param tag Tag entry value - * @param type Tag entry type - * @param expected Tag - * @throws edu.harvard.hul.ois.jhove.module.tiff.TiffException - */ - protected static void checkType(int tag, int type, int expected) - throws TiffException - { - /* Readers are supposed to accept BYTE, SHORT or LONG for any - * unsigned integer field. */ - if ((type == BYTE || type == SHORT || type == LONG || type == IFD) && - (expected == BYTE || expected == SHORT || expected == LONG || - expected == IFD)) { - return; // it's OK - } - if (type != expected) { - String mess = MessageFormat.format(MessageConstants.TIFF_HUL_7.getMessage(), tag, Integer.valueOf(expected), Integer.valueOf(type)); - JhoveMessage message = JhoveMessages.getMessageInstance(MessageConstants.TIFF_HUL_7.getId(), mess); - throw new TiffException(message); - } + } + + /** + * Check the tag entry type. + * + * @param tag Tag entry value + * @param type Tag entry type + * @param expected Tag + * @throws edu.harvard.hul.ois.jhove.module.tiff.TiffException + */ + protected static void checkType(int tag, int type, int expected) throws TiffException { + /* Readers are supposed to accept BYTE, SHORT or LONG for any + * unsigned integer field. */ + if ((type == BYTE || type == SHORT || type == LONG || type == IFD) + && (expected == BYTE || expected == SHORT || expected == LONG || expected == IFD)) { + return; // it's OK } - - /** - * Check the tag entry type. - * @param tag Tag entry value - * @param type Tag entry type - * @param type1 Tag type - * @param type2 Alternate tag type - * @throws edu.harvard.hul.ois.jhove.module.tiff.TiffException - */ - protected static void checkType(int tag, int type, int type1, int type2) - throws TiffException - { - if (type != type1 && type != type2) { - String mess = MessageFormat.format(MessageConstants.TIFF_HUL_8.getMessage(), - tag, Integer.valueOf(type1), Integer.valueOf(type2), Integer.valueOf(type)); - JhoveMessage message = JhoveMessages.getMessageInstance(MessageConstants.TIFF_HUL_8.getId(), mess); - throw new TiffException(message); - } + if (type != expected) { + String mess = + MessageFormat.format( + MessageConstants.TIFF_HUL_7.getMessage(), + tag, + Integer.valueOf(expected), + Integer.valueOf(type)); + JhoveMessage message = + JhoveMessages.getMessageInstance(MessageConstants.TIFF_HUL_7.getId(), mess); + throw new TiffException(message); } - - protected static Rational average(Rational r1, Rational r2) - { - long d1 = r1.getDenominator(); - long d2 = r2.getDenominator(); - - Rational f1 = new Rational(r1.getNumerator()*d2, - r1.getDenominator()*d2); - Rational f2 = new Rational(r2.getNumerator()*d1, - r2.getDenominator()*d1); - - return new Rational((f1.getNumerator() + f2.getNumerator())/2, - f1.getDenominator()); + } + + /** + * Check the tag entry type. + * + * @param tag Tag entry value + * @param type Tag entry type + * @param type1 Tag type + * @param type2 Alternate tag type + * @throws edu.harvard.hul.ois.jhove.module.tiff.TiffException + */ + protected static void checkType(int tag, int type, int type1, int type2) throws TiffException { + if (type != type1 && type != type2) { + String mess = + MessageFormat.format( + MessageConstants.TIFF_HUL_8.getMessage(), + tag, + Integer.valueOf(type1), + Integer.valueOf(type2), + Integer.valueOf(type)); + JhoveMessage message = + JhoveMessages.getMessageInstance(MessageConstants.TIFF_HUL_8.getId(), mess); + throw new TiffException(message); } - - - /****************************************************************** - * PRIVATE INSTANCE METHODS. - ******************************************************************/ - - /** Represent a byte value as %XX */ - private String byteToHex(byte c) { - int[] nibbles = new int[2]; - nibbles[0] = ((int) c & 0XF0) >> 4; - nibbles[1] = (int) c & 0X0F; - StringBuffer retval = new StringBuffer("%"); - for (int i = 0; i <= 1; i++) { - int b = nibbles[i]; - if (b >= 10) { - b += (int) 'A' - 10; - } - else { - b += (int) '0'; - } - retval.append((char) b); - } - return retval.toString(); + } + + protected static Rational average(Rational r1, Rational r2) { + long d1 = r1.getDenominator(); + long d2 = r2.getDenominator(); + + Rational f1 = new Rational(r1.getNumerator() * d2, r1.getDenominator() * d2); + Rational f2 = new Rational(r2.getNumerator() * d1, r2.getDenominator() * d1); + + return new Rational((f1.getNumerator() + f2.getNumerator()) / 2, f1.getDenominator()); + } + + /** + * **************************************************************** PRIVATE INSTANCE METHODS. + * **************************************************************** + */ + + /** Represent a byte value as %XX */ + private String byteToHex(byte c) { + int[] nibbles = new int[2]; + nibbles[0] = ((int) c & 0XF0) >> 4; + nibbles[1] = (int) c & 0X0F; + StringBuffer retval = new StringBuffer("%"); + for (int i = 0; i <= 1; i++) { + int b = nibbles[i]; + if (b >= 10) { + b += (int) 'A' - 10; + } else { + b += (int) '0'; + } + retval.append((char) b); } + return retval.toString(); + } } diff --git a/jhove-modules/tiff-hul/src/main/java/edu/harvard/hul/ois/jhove/module/tiff/InteroperabilityIFD.java b/jhove-modules/tiff-hul/src/main/java/edu/harvard/hul/ois/jhove/module/tiff/InteroperabilityIFD.java index 10aacdadd..1ab5af400 100644 --- a/jhove-modules/tiff-hul/src/main/java/edu/harvard/hul/ois/jhove/module/tiff/InteroperabilityIFD.java +++ b/jhove-modules/tiff-hul/src/main/java/edu/harvard/hul/ois/jhove/module/tiff/InteroperabilityIFD.java @@ -1,98 +1,90 @@ -/********************************************************************** - * Jhove - JSTOR/Harvard Object Validation Environment - * Copyright 2003 by JSTOR and the President and Fellows of Harvard College - **********************************************************************/ - +/** + * ******************************************************************** Jhove - JSTOR/Harvard Object + * Validation Environment Copyright 2003 by JSTOR and the President and Fellows of Harvard College + * ******************************************************************** + */ package edu.harvard.hul.ois.jhove.module.tiff; import edu.harvard.hul.ois.jhove.*; import edu.harvard.hul.ois.jhove.messages.JhoveMessage; import edu.harvard.hul.ois.jhove.messages.JhoveMessages; - import java.io.*; import java.text.MessageFormat; import java.util.*; -/** - * Encapsulation of an Exif Interoperability IFD (for Exif). - */ +/** Encapsulation of an Exif Interoperability IFD (for Exif). */ public class InteroperabilityIFD extends IFD { - /****************************************************************** - * PRIVATE CLASS FIELDS. - ******************************************************************/ + /** + * **************************************************************** PRIVATE CLASS FIELDS. + * **************************************************************** + */ - /** InteroperabilityIndex tag. */ - private static final int INTEROPERABILITYINDEX = 1; + /** InteroperabilityIndex tag. */ + private static final int INTEROPERABILITYINDEX = 1; - /****************************************************************** - * PRIVATE INSTANCE FIELDS. - ******************************************************************/ + /** + * **************************************************************** PRIVATE INSTANCE FIELDS. + * **************************************************************** + */ - /** Interoperability identification tag (1). */ - private String _interoperabilityIndex; + /** Interoperability identification tag (1). */ + private String _interoperabilityIndex; - /****************************************************************** - * CLASS CONSTRUCTOR. - ******************************************************************/ + /** + * **************************************************************** CLASS CONSTRUCTOR. + * **************************************************************** + */ - /** - * Instantiate an InteroperabilityIFD object. - * - * @param offset - * IFD offset - * @param info - * the RepInfo object - * @param raf - * TIFF file - * @param bigEndian - * True if big-endian file - */ - public InteroperabilityIFD(long offset, RepInfo info, RandomAccessFile raf, - boolean bigEndian) { - super(offset, info, raf, bigEndian); - } + /** + * Instantiate an InteroperabilityIFD object. + * + * @param offset IFD offset + * @param info the RepInfo object + * @param raf TIFF file + * @param bigEndian True if big-endian file + */ + public InteroperabilityIFD(long offset, RepInfo info, RandomAccessFile raf, boolean bigEndian) { + super(offset, info, raf, bigEndian); + } - /****************************************************************** - * PUBLIC INSTANCE METHODS. - ******************************************************************/ + /** + * **************************************************************** PUBLIC INSTANCE METHODS. + * **************************************************************** + */ - /** Get the InteroperabilityIndex tag (1). */ - public String getInteroperabilityIndex() { - return _interoperabilityIndex; - } + /** Get the InteroperabilityIndex tag (1). */ + public String getInteroperabilityIndex() { + return _interoperabilityIndex; + } - /** Get the IFD properties. */ - @Override - public Property getProperty(boolean rawOutput) { - List entries = new LinkedList(); - entries.add(new Property("Index", PropertyType.STRING, - _interoperabilityIndex)); + /** Get the IFD properties. */ + @Override + public Property getProperty(boolean rawOutput) { + List entries = new LinkedList(); + entries.add(new Property("Index", PropertyType.STRING, _interoperabilityIndex)); - return propertyHeader("Exif Interoperability", entries); - } + return propertyHeader("Exif Interoperability", entries); + } - /** Lookup an IFD tag. */ - @Override - public void lookupTag(int tag, int type, long count, long value) - throws TiffException { - try { - if (tag == INTEROPERABILITYINDEX) { - checkType(tag, type, ASCII); - _interoperabilityIndex = readASCII(count, value); - } else { - String subMessage = MessageFormat.format( - MessageConstants.TIFF_HUL_15_SUB.getMessage(), - Integer.valueOf(tag)); - _info.setMessage(new ErrorMessage(MessageConstants.TIFF_HUL_15, - subMessage, value)); - } - } catch (IOException e) { - String mess = MessageFormat.format( - MessageConstants.TIFF_HUL_16.getMessage(), - Integer.valueOf(tag)); - JhoveMessage message = JhoveMessages.getMessageInstance( - MessageConstants.TIFF_HUL_16.getId(), mess); - throw new TiffException(message, value); - } - } + /** Lookup an IFD tag. */ + @Override + public void lookupTag(int tag, int type, long count, long value) throws TiffException { + try { + if (tag == INTEROPERABILITYINDEX) { + checkType(tag, type, ASCII); + _interoperabilityIndex = readASCII(count, value); + } else { + String subMessage = + MessageFormat.format( + MessageConstants.TIFF_HUL_15_SUB.getMessage(), Integer.valueOf(tag)); + _info.setMessage(new ErrorMessage(MessageConstants.TIFF_HUL_15, subMessage, value)); + } + } catch (IOException e) { + String mess = + MessageFormat.format(MessageConstants.TIFF_HUL_16.getMessage(), Integer.valueOf(tag)); + JhoveMessage message = + JhoveMessages.getMessageInstance(MessageConstants.TIFF_HUL_16.getId(), mess); + throw new TiffException(message, value); + } + } } diff --git a/jhove-modules/tiff-hul/src/main/java/edu/harvard/hul/ois/jhove/module/tiff/MessageConstants.java b/jhove-modules/tiff-hul/src/main/java/edu/harvard/hul/ois/jhove/module/tiff/MessageConstants.java index f26d27e1d..a98554dcf 100644 --- a/jhove-modules/tiff-hul/src/main/java/edu/harvard/hul/ois/jhove/module/tiff/MessageConstants.java +++ b/jhove-modules/tiff-hul/src/main/java/edu/harvard/hul/ois/jhove/module/tiff/MessageConstants.java @@ -5,194 +5,206 @@ import edu.harvard.hul.ois.jhove.messages.JhoveMessages; /** - * Enum used to externalise the PDF modules message Strings. Using an enum - * INSTANCE as a "trick" to ensure a single instance of the class. String - * constants should be prefixed according to their use in the module: + * Enum used to externalise the PDF modules message Strings. Using an enum INSTANCE as a "trick" to + * ensure a single instance of the class. String constants should be prefixed according to their use + * in the module: + * *

    - *
  • WRN_ for warning strings, often logger messages.
  • - *
  • INF_ for informational messages.
  • - *
  • ERR_ for error messages that indicate a file is invalid or not well - * formed.
  • + *
  • WRN_ for warning strings, often logger messages. + *
  • INF_ for informational messages. + *
  • ERR_ for error messages that indicate a file is invalid or not well formed. *
- * When adding new messages try to adopt the following order for the naming - * elements: + * + * When adding new messages try to adopt the following order for the naming elements: + * *
    - *
  1. PREFIX: one of the three prefixes from the list above.
  2. - *
  3. ENTITY_NAME: the name of the PDF entity causing the prohlem, e.g. FONT, - * or DOC.
  4. - *
  5. Problem: a short indicator of the problem type, e.g. MISSING, ILLEGAL, - * etc.
  6. + *
  7. PREFIX: one of the three prefixes from the list above. + *
  8. ENTITY_NAME: the name of the PDF entity causing the prohlem, e.g. FONT, or DOC. + *
  9. Problem: a short indicator of the problem type, e.g. MISSING, ILLEGAL, etc. *
- * The elements should be separated by underscores. The messages currently don't - * follow a consistent vocabulary, that is terms such as invalid, illegal, or - * malformed are used without definition. - * - * @author Carl Wilson - * carlwilson AT github + * + * The elements should be separated by underscores. The messages currently don't follow a consistent + * vocabulary, that is terms such as invalid, illegal, or malformed are used without definition. + * + * @author Carl Wilson carlwilson AT github * @version 0.1 Created 1 Oct 2016:11:38:18 */ - public enum MessageConstants { - INSTANCE; + INSTANCE; + + public static final JhoveMessageFactory messageFactory = + JhoveMessages.getInstance("edu.harvard.hul.ois.jhove.module.tiff.ErrorMessages"); - public static final JhoveMessageFactory messageFactory = JhoveMessages.getInstance("edu.harvard.hul.ois.jhove.module.tiff.ErrorMessages"); - - public static final JhoveMessage TIFF_HUL_1 = messageFactory.getMessage("TIFF-HUL-1"); - public static final JhoveMessage TIFF_HUL_2 = messageFactory.getMessage("TIFF-HUL-2"); - public static final JhoveMessage TIFF_HUL_3 = messageFactory.getMessage("TIFF-HUL-3"); - public static final JhoveMessage TIFF_HUL_3_SUB = messageFactory.getMessage("TIFF-HUL-3-SUB"); - public static final JhoveMessage TIFF_HUL_4 = messageFactory.getMessage("TIFF-HUL-4"); - public static final JhoveMessage TIFF_HUL_5 = messageFactory.getMessage("TIFF-HUL-5"); - public static final JhoveMessage TIFF_HUL_6 = messageFactory.getMessage("TIFF-HUL-6"); - public static final JhoveMessage TIFF_HUL_7 = messageFactory.getMessage("TIFF-HUL-7"); - public static final JhoveMessage TIFF_HUL_8 = messageFactory.getMessage("TIFF-HUL-8"); - public static final JhoveMessage TIFF_HUL_9 = messageFactory.getMessage("TIFF-HUL-9"); - public static final JhoveMessage TIFF_HUL_10 = messageFactory.getMessage("TIFF-HUL-10"); - public static final JhoveMessage TIFF_HUL_11 = messageFactory.getMessage("TIFF-HUL-11"); - public static final JhoveMessage TIFF_HUL_11_SUB = messageFactory.getMessage("TIFF-HUL-11-SUB"); - public static final JhoveMessage TIFF_HUL_12 = messageFactory.getMessage("TIFF-HUL-12"); - public static final JhoveMessage TIFF_HUL_13 = messageFactory.getMessage("TIFF-HUL-13"); - public static final JhoveMessage TIFF_HUL_14 = messageFactory.getMessage("TIFF-HUL-14"); - public static final JhoveMessage TIFF_HUL_15 = messageFactory.getMessage("TIFF-HUL-15"); - public static final JhoveMessage TIFF_HUL_15_SUB = messageFactory.getMessage("TIFF-HUL-15-SUB"); - public static final JhoveMessage TIFF_HUL_16 = messageFactory.getMessage("TIFF-HUL-16"); - public static final JhoveMessage TIFF_HUL_17 = messageFactory.getMessage("TIFF-HUL-17"); - public static final JhoveMessage TIFF_HUL_17_SUB = messageFactory.getMessage("TIFF-HUL-17-SUB"); - public static final JhoveMessage TIFF_HUL_18 = messageFactory.getMessage("TIFF-HUL-18"); - public static final JhoveMessage TIFF_HUL_19 = messageFactory.getMessage("TIFF-HUL-19"); - public static final JhoveMessage TIFF_HUL_20 = messageFactory.getMessage("TIFF-HUL-20"); - public static final JhoveMessage TIFF_HUL_21 = messageFactory.getMessage("TIFF-HUL-21"); - public static final JhoveMessage TIFF_HUL_22 = messageFactory.getMessage("TIFF-HUL-22"); - public static final JhoveMessage TIFF_HUL_23 = messageFactory.getMessage("TIFF-HUL-23"); - public static final JhoveMessage TIFF_HUL_24 = messageFactory.getMessage("TIFF-HUL-24"); - public static final JhoveMessage TIFF_HUL_25 = messageFactory.getMessage("TIFF-HUL-25"); - public static final JhoveMessage TIFF_HUL_26 = messageFactory.getMessage("TIFF-HUL-26"); - public static final JhoveMessage TIFF_HUL_27 = messageFactory.getMessage("TIFF-HUL-27"); - public static final JhoveMessage TIFF_HUL_28 = messageFactory.getMessage("TIFF-HUL-28"); - public static final JhoveMessage TIFF_HUL_29 = messageFactory.getMessage("TIFF-HUL-29"); - public static final JhoveMessage TIFF_HUL_30 = messageFactory.getMessage("TIFF-HUL-30"); - public static final JhoveMessage TIFF_HUL_31 = messageFactory.getMessage("TIFF-HUL-31"); - public static final JhoveMessage TIFF_HUL_32 = messageFactory.getMessage("TIFF-HUL-32"); - public static final JhoveMessage TIFF_HUL_33 = messageFactory.getMessage("TIFF-HUL-33"); - public static final JhoveMessage TIFF_HUL_34 = messageFactory.getMessage("TIFF-HUL-34"); - public static final JhoveMessage TIFF_HUL_35 = messageFactory.getMessage("TIFF-HUL-35"); - public static final JhoveMessage TIFF_HUL_36 = messageFactory.getMessage("TIFF-HUL-36"); - public static final JhoveMessage TIFF_HUL_37 = messageFactory.getMessage("TIFF-HUL-37"); - public static final JhoveMessage TIFF_HUL_38 = messageFactory.getMessage("TIFF-HUL-38"); - public static final JhoveMessage TIFF_HUL_39 = messageFactory.getMessage("TIFF-HUL-39"); - public static final JhoveMessage TIFF_HUL_40 = messageFactory.getMessage("TIFF-HUL-40"); - public static final JhoveMessage TIFF_HUL_41 = messageFactory.getMessage("TIFF-HUL-41"); - public static final JhoveMessage TIFF_HUL_42 = messageFactory.getMessage("TIFF-HUL-42"); - public static final JhoveMessage TIFF_HUL_43 = messageFactory.getMessage("TIFF-HUL-43"); - public static final JhoveMessage TIFF_HUL_44 = messageFactory.getMessage("TIFF-HUL-44"); - public static final JhoveMessage TIFF_HUL_45 = messageFactory.getMessage("TIFF-HUL-45"); - public static final JhoveMessage TIFF_HUL_46 = messageFactory.getMessage("TIFF-HUL-46"); - public static final JhoveMessage TIFF_HUL_47 = messageFactory.getMessage("TIFF-HUL-47"); - public static final JhoveMessage TIFF_HUL_48 = messageFactory.getMessage("TIFF-HUL-48"); - public static final JhoveMessage TIFF_HUL_49 = messageFactory.getMessage("TIFF-HUL-49"); - public static final JhoveMessage TIFF_HUL_50 = messageFactory.getMessage("TIFF-HUL-50"); - public static final JhoveMessage TIFF_HUL_51 = messageFactory.getMessage("TIFF-HUL-51"); - public static final JhoveMessage TIFF_HUL_52 = messageFactory.getMessage("TIFF-HUL-52"); - public static final JhoveMessage TIFF_HUL_53 = messageFactory.getMessage("TIFF-HUL-53"); - public static final JhoveMessage TIFF_HUL_54 = messageFactory.getMessage("TIFF-HUL-54"); - public static final JhoveMessage TIFF_HUL_55 = messageFactory.getMessage("TIFF-HUL-55"); - public static final JhoveMessage TIFF_HUL_56 = messageFactory.getMessage("TIFF-HUL-56"); - public static final JhoveMessage TIFF_HUL_57 = messageFactory.getMessage("TIFF-HUL-57"); - public static final JhoveMessage TIFF_HUL_58 = messageFactory.getMessage("TIFF-HUL-58"); - public static final JhoveMessage TIFF_HUL_59 = messageFactory.getMessage("TIFF-HUL-59"); - public static final JhoveMessage TIFF_HUL_60 = messageFactory.getMessage("TIFF-HUL-60"); - public static final JhoveMessage TIFF_HUL_61 = messageFactory.getMessage("TIFF-HUL-61"); - public static final JhoveMessage TIFF_HUL_62 = messageFactory.getMessage("TIFF-HUL-62"); - public static final JhoveMessage TIFF_HUL_63 = messageFactory.getMessage("TIFF-HUL-63"); - public static final JhoveMessage TIFF_HUL_64 = messageFactory.getMessage("TIFF-HUL-64"); - public static final JhoveMessage TIFF_HUL_65 = messageFactory.getMessage("TIFF-HUL-65"); - public static final JhoveMessage TIFF_HUL_66 = messageFactory.getMessage("TIFF-HUL-66"); - public static final JhoveMessage TIFF_HUL_67 = messageFactory.getMessage("TIFF-HUL-67"); - public static final JhoveMessage TIFF_HUL_68 = messageFactory.getMessage("TIFF-HUL-68"); - public static final JhoveMessage TIFF_HUL_69 = messageFactory.getMessage("TIFF-HUL-69"); - public static final JhoveMessage TIFF_HUL_70 = messageFactory.getMessage("TIFF-HUL-70"); - public static final JhoveMessage TIFF_HUL_71 = messageFactory.getMessage("TIFF-HUL-71"); + public static final JhoveMessage TIFF_HUL_1 = messageFactory.getMessage("TIFF-HUL-1"); + public static final JhoveMessage TIFF_HUL_2 = messageFactory.getMessage("TIFF-HUL-2"); + public static final JhoveMessage TIFF_HUL_3 = messageFactory.getMessage("TIFF-HUL-3"); + public static final JhoveMessage TIFF_HUL_3_SUB = messageFactory.getMessage("TIFF-HUL-3-SUB"); + public static final JhoveMessage TIFF_HUL_4 = messageFactory.getMessage("TIFF-HUL-4"); + public static final JhoveMessage TIFF_HUL_5 = messageFactory.getMessage("TIFF-HUL-5"); + public static final JhoveMessage TIFF_HUL_6 = messageFactory.getMessage("TIFF-HUL-6"); + public static final JhoveMessage TIFF_HUL_7 = messageFactory.getMessage("TIFF-HUL-7"); + public static final JhoveMessage TIFF_HUL_8 = messageFactory.getMessage("TIFF-HUL-8"); + public static final JhoveMessage TIFF_HUL_9 = messageFactory.getMessage("TIFF-HUL-9"); + public static final JhoveMessage TIFF_HUL_10 = messageFactory.getMessage("TIFF-HUL-10"); + public static final JhoveMessage TIFF_HUL_11 = messageFactory.getMessage("TIFF-HUL-11"); + public static final JhoveMessage TIFF_HUL_11_SUB = messageFactory.getMessage("TIFF-HUL-11-SUB"); + public static final JhoveMessage TIFF_HUL_12 = messageFactory.getMessage("TIFF-HUL-12"); + public static final JhoveMessage TIFF_HUL_13 = messageFactory.getMessage("TIFF-HUL-13"); + public static final JhoveMessage TIFF_HUL_14 = messageFactory.getMessage("TIFF-HUL-14"); + public static final JhoveMessage TIFF_HUL_15 = messageFactory.getMessage("TIFF-HUL-15"); + public static final JhoveMessage TIFF_HUL_15_SUB = messageFactory.getMessage("TIFF-HUL-15-SUB"); + public static final JhoveMessage TIFF_HUL_16 = messageFactory.getMessage("TIFF-HUL-16"); + public static final JhoveMessage TIFF_HUL_17 = messageFactory.getMessage("TIFF-HUL-17"); + public static final JhoveMessage TIFF_HUL_17_SUB = messageFactory.getMessage("TIFF-HUL-17-SUB"); + public static final JhoveMessage TIFF_HUL_18 = messageFactory.getMessage("TIFF-HUL-18"); + public static final JhoveMessage TIFF_HUL_19 = messageFactory.getMessage("TIFF-HUL-19"); + public static final JhoveMessage TIFF_HUL_20 = messageFactory.getMessage("TIFF-HUL-20"); + public static final JhoveMessage TIFF_HUL_21 = messageFactory.getMessage("TIFF-HUL-21"); + public static final JhoveMessage TIFF_HUL_22 = messageFactory.getMessage("TIFF-HUL-22"); + public static final JhoveMessage TIFF_HUL_23 = messageFactory.getMessage("TIFF-HUL-23"); + public static final JhoveMessage TIFF_HUL_24 = messageFactory.getMessage("TIFF-HUL-24"); + public static final JhoveMessage TIFF_HUL_25 = messageFactory.getMessage("TIFF-HUL-25"); + public static final JhoveMessage TIFF_HUL_26 = messageFactory.getMessage("TIFF-HUL-26"); + public static final JhoveMessage TIFF_HUL_27 = messageFactory.getMessage("TIFF-HUL-27"); + public static final JhoveMessage TIFF_HUL_28 = messageFactory.getMessage("TIFF-HUL-28"); + public static final JhoveMessage TIFF_HUL_29 = messageFactory.getMessage("TIFF-HUL-29"); + public static final JhoveMessage TIFF_HUL_30 = messageFactory.getMessage("TIFF-HUL-30"); + public static final JhoveMessage TIFF_HUL_31 = messageFactory.getMessage("TIFF-HUL-31"); + public static final JhoveMessage TIFF_HUL_32 = messageFactory.getMessage("TIFF-HUL-32"); + public static final JhoveMessage TIFF_HUL_33 = messageFactory.getMessage("TIFF-HUL-33"); + public static final JhoveMessage TIFF_HUL_34 = messageFactory.getMessage("TIFF-HUL-34"); + public static final JhoveMessage TIFF_HUL_35 = messageFactory.getMessage("TIFF-HUL-35"); + public static final JhoveMessage TIFF_HUL_36 = messageFactory.getMessage("TIFF-HUL-36"); + public static final JhoveMessage TIFF_HUL_37 = messageFactory.getMessage("TIFF-HUL-37"); + public static final JhoveMessage TIFF_HUL_38 = messageFactory.getMessage("TIFF-HUL-38"); + public static final JhoveMessage TIFF_HUL_39 = messageFactory.getMessage("TIFF-HUL-39"); + public static final JhoveMessage TIFF_HUL_40 = messageFactory.getMessage("TIFF-HUL-40"); + public static final JhoveMessage TIFF_HUL_41 = messageFactory.getMessage("TIFF-HUL-41"); + public static final JhoveMessage TIFF_HUL_42 = messageFactory.getMessage("TIFF-HUL-42"); + public static final JhoveMessage TIFF_HUL_43 = messageFactory.getMessage("TIFF-HUL-43"); + public static final JhoveMessage TIFF_HUL_44 = messageFactory.getMessage("TIFF-HUL-44"); + public static final JhoveMessage TIFF_HUL_45 = messageFactory.getMessage("TIFF-HUL-45"); + public static final JhoveMessage TIFF_HUL_46 = messageFactory.getMessage("TIFF-HUL-46"); + public static final JhoveMessage TIFF_HUL_47 = messageFactory.getMessage("TIFF-HUL-47"); + public static final JhoveMessage TIFF_HUL_48 = messageFactory.getMessage("TIFF-HUL-48"); + public static final JhoveMessage TIFF_HUL_49 = messageFactory.getMessage("TIFF-HUL-49"); + public static final JhoveMessage TIFF_HUL_50 = messageFactory.getMessage("TIFF-HUL-50"); + public static final JhoveMessage TIFF_HUL_51 = messageFactory.getMessage("TIFF-HUL-51"); + public static final JhoveMessage TIFF_HUL_52 = messageFactory.getMessage("TIFF-HUL-52"); + public static final JhoveMessage TIFF_HUL_53 = messageFactory.getMessage("TIFF-HUL-53"); + public static final JhoveMessage TIFF_HUL_54 = messageFactory.getMessage("TIFF-HUL-54"); + public static final JhoveMessage TIFF_HUL_55 = messageFactory.getMessage("TIFF-HUL-55"); + public static final JhoveMessage TIFF_HUL_56 = messageFactory.getMessage("TIFF-HUL-56"); + public static final JhoveMessage TIFF_HUL_57 = messageFactory.getMessage("TIFF-HUL-57"); + public static final JhoveMessage TIFF_HUL_58 = messageFactory.getMessage("TIFF-HUL-58"); + public static final JhoveMessage TIFF_HUL_59 = messageFactory.getMessage("TIFF-HUL-59"); + public static final JhoveMessage TIFF_HUL_60 = messageFactory.getMessage("TIFF-HUL-60"); + public static final JhoveMessage TIFF_HUL_61 = messageFactory.getMessage("TIFF-HUL-61"); + public static final JhoveMessage TIFF_HUL_62 = messageFactory.getMessage("TIFF-HUL-62"); + public static final JhoveMessage TIFF_HUL_63 = messageFactory.getMessage("TIFF-HUL-63"); + public static final JhoveMessage TIFF_HUL_64 = messageFactory.getMessage("TIFF-HUL-64"); + public static final JhoveMessage TIFF_HUL_65 = messageFactory.getMessage("TIFF-HUL-65"); + public static final JhoveMessage TIFF_HUL_66 = messageFactory.getMessage("TIFF-HUL-66"); + public static final JhoveMessage TIFF_HUL_67 = messageFactory.getMessage("TIFF-HUL-67"); + public static final JhoveMessage TIFF_HUL_68 = messageFactory.getMessage("TIFF-HUL-68"); + public static final JhoveMessage TIFF_HUL_69 = messageFactory.getMessage("TIFF-HUL-69"); + public static final JhoveMessage TIFF_HUL_70 = messageFactory.getMessage("TIFF-HUL-70"); + public static final JhoveMessage TIFF_HUL_71 = messageFactory.getMessage("TIFF-HUL-71"); -// private static final String tagMismatch = " mismatch for tag "; -// -// /** -// * Error "fill in" strings -// */ -// public static final String MISMATCH_SUB_1 = "; expecting "; -// public static final String MISMATCH_SUB_2 = ", saw "; -// public static final String MISMATCH_SUB_3 = " or "; -// public static final String TAG_SUB_MESS = "Tag = "; -// public static final String MESSAGE_SUB_MESS = "; message "; -// -// /** -// * Information messages -// */ -// public static final String INF_COMP_SCH_6_DEPR = "TIFF compression scheme 6 is deprecated"; -// public static final String INF_IFD_TAG_UNK = "Unknown TIFF IFD tag: "; -// public static final String INF_IMAGE_LEN_NO_DEF = "ImageLength not defined"; -// public static final String INF_IMAGE_WID_NO_DEF = "ImageWidth not defined"; -// public static final String INF_PHO_NO_DEF = "PhotometricInterpretation not defined"; -// public static final String INF_SHADOW_SCALE = "ShadowScale (50739)"; -// public static final String INF_STR_AND_TILE_TOGETHER = "Strips and tiles defined together"; -// public static final String INF_STR_AND_TILE_NO_DEF = "Neither strips nor tiles defined"; -// public static final String INF_STR_BYTE_COUNT_NO_DEF = "StripByteCounts not defined"; -// public static final String INF_STR_OFF_BYTE_COUNT_INCONS = "StripOffsets inconsistent with StripByteCounts: "; -// public static final String INF_STR_OFF_INVALID = "Invalid strip offset"; -// public static final String INF_STR_OFF_NO_DEF = "StripOffsets not defined"; -// public static final String INF_TIFF_TAG_UNDOC = "Undocumented TIFF tag"; -// public static final String INF_VALOFF_NOT_WORD_ALIGN = "Value offset not word-aligned: "; -// -// /** -// * Error messages -// */ -// public static final String ERR_CELL_LEN_NOT_ALLWD = "CellLength tag not permitted when Threshholding not 2"; -// public static final String ERR_CIELAB_BPS_NOT_8_OR_16 = "BitsPerSample not 8 or 16 for CIE L*a*b*"; -// public static final String ERR_COL_MAP_NOT_DEF = "ColorMap not defined for palette-color"; -// public static final String ERR_COL_MAP_MISS_VALS = "Insufficient ColorMap values for palette-color: "; -// public static final String ERR_DATE_TIME_LEN_INV = "Invalid DateTime length: "; -// public static final String ERR_DATE_TIME_SEP_INV = "Invalid DateTime separator: "; -// public static final String ERR_DATE_TIME_DIG_INV = "Invalid DateTime digit: "; -// public static final String ERR_DOT_RANGE_BPS = "DotRange out of range specified by BitsPerSample"; -// public static final String ERR_EXIF_BLOCK_TOO_SHORT = "Embedded Exif block is too short"; -// public static final String ERR_FILE_TOO_SHORT = "File is too short"; -// public static final String ERR_GEO_KEY_DIRECT_INVALID = "Invalid GeoKeyDirectory tag"; -// public static final String ERR_GEO_KEY_OUT_SEQ = "GeoKey "; -// public static final String ERR_GEO_KEY_OUT_SEQ_2 = " out of sequence"; -// public static final String ERR_GPS_IFD_TAG_UNK = "Unknown GPSInfo IFD tag"; -// public static final String ERR_IFD_MISSING = "No IFD in file "; -// public static final String ERR_IFD_OFF_MISALIGN = "IFD offset not word-aligned: "; -// public static final String ERR_IFD_MAX_EXCEEDED = "More than 50 IFDs in chain, probably an infinite loop"; -// public static final String ERR_IO_READ = "Read error"; -// public static final String ERR_JPEGPROC_NO_DEF = "JPEGProc not defined for JPEG compression"; -// public static final String ERR_PAL_COL_SPP_NE_1 = "For palette-color SamplesPerPixel must be 1: "; -// public static final String ERR_PHO_AND_NEW_SUBFILE_INCONSISTENT = "PhotometricInterpretation and NewSubfileType must agree on transparency mask"; -// public static final String ERR_PHO_INT_SPP_GT_1 = "For PhotometricInterpretation, SamplesPerPixel must be >= 1, equals: "; -// public static final String ERR_PHO_INT_SPP_GT_3 = "For PhotometricInterpretation, SamplesPerPixel must be >= 3, equals: "; -// public static final String ERR_SPP_EXTRA_NT_1_OR_3 = "SamplesPerPixel-ExtraSamples not 1 or 3:"; -// public static final String ERR_TAG_COUNT_MISMATCH = "Count" + tagMismatch; -// public static final String ERR_TAG_IO_READ = ERR_IO_READ + " for tag "; -// public static final String ERR_TAG_ICCPROFILE_BAD = "Bad ICCProfile in tag "; -// public static final String ERR_TAG_OUT_OF_SEQ_1 = "Tag "; -// public static final String ERR_TAG_OUT_OF_SEQ_2 = ERR_GEO_KEY_OUT_SEQ_2; -// public static final String ERR_TAG_TYPE_MISMATCH = "Type" + tagMismatch; -// public static final String ERR_TIFF_HEADER_MISSING = "No TIFF header: "; -// public static final String ERR_TIFF_MAGIC_NUM_MISSING = "No TIFF magic number: "; -// public static final String ERR_TIFF_PREM_EOF = "Premature EOF"; -// public static final String ERR_TRANS_MASK_BPS = "For transparency mask BitsPerSample must be 1"; -// public static final String ERR_UNK_DATA_TYPE = "Unknown data type"; -// public static final String ERR_UNK_DATA_TYPE_SUB_1 = "Type = "; -// public static final String ERR_UNK_DATA_TYPE_SUB_2 = ", Tag = "; -// public static final String ERR_EXIF_INTER_IFD_UNK = "Unknown Exif Interoperability IFD tag"; -// public static final String ERR_VAL_OUT_OF_RANGE = " value out of range: "; -// public static final String ERR_XMP_INVALID = "Invalid or ill-formed XMP metadata"; -// public static final String ERR_TILE_WID_NO_DEF = "TileWidth not defined"; -// public static final String ERR_TILE_LEN_NO_DEF = "TileLength not defined"; -// public static final String ERR_TILE_OFF_NO_DEF = "TileOffsets not defined"; -// public static final String ERR_TILE_COUNT_NO_DEF = "TileByteCounts not defined"; -// public static final String ERR_TILE_WID_NOT_DIV_16 = "TileWidth not a multiple of 16: "; -// public static final String ERR_TILE_LEN_NOT_DIV_16 = "TileLength not a multiple of 16: "; -// public static final String ERR_TILE_OFF_MISS_VALS = "Insufficient values for TileOffsets: "; -// public static final String ERR_TILE_COUNT_MISS_VALS = "Insufficient values for TileByteCounts: "; -// public static final String ERR_XCLIP_PATH_NO_DEF = "XClipPathUnits not defined for ClipPath"; + // private static final String tagMismatch = " mismatch for tag "; + // + // /** + // * Error "fill in" strings + // */ + // public static final String MISMATCH_SUB_1 = "; expecting "; + // public static final String MISMATCH_SUB_2 = ", saw "; + // public static final String MISMATCH_SUB_3 = " or "; + // public static final String TAG_SUB_MESS = "Tag = "; + // public static final String MESSAGE_SUB_MESS = "; message "; + // + // /** + // * Information messages + // */ + // public static final String INF_COMP_SCH_6_DEPR = "TIFF compression scheme 6 is deprecated"; + // public static final String INF_IFD_TAG_UNK = "Unknown TIFF IFD tag: "; + // public static final String INF_IMAGE_LEN_NO_DEF = "ImageLength not defined"; + // public static final String INF_IMAGE_WID_NO_DEF = "ImageWidth not defined"; + // public static final String INF_PHO_NO_DEF = "PhotometricInterpretation not defined"; + // public static final String INF_SHADOW_SCALE = "ShadowScale (50739)"; + // public static final String INF_STR_AND_TILE_TOGETHER = "Strips and tiles defined together"; + // public static final String INF_STR_AND_TILE_NO_DEF = "Neither strips nor tiles defined"; + // public static final String INF_STR_BYTE_COUNT_NO_DEF = "StripByteCounts not defined"; + // public static final String INF_STR_OFF_BYTE_COUNT_INCONS = "StripOffsets inconsistent with + // StripByteCounts: "; + // public static final String INF_STR_OFF_INVALID = "Invalid strip offset"; + // public static final String INF_STR_OFF_NO_DEF = "StripOffsets not defined"; + // public static final String INF_TIFF_TAG_UNDOC = "Undocumented TIFF tag"; + // public static final String INF_VALOFF_NOT_WORD_ALIGN = "Value offset not word-aligned: "; + // + // /** + // * Error messages + // */ + // public static final String ERR_CELL_LEN_NOT_ALLWD = "CellLength tag not permitted when + // Threshholding not 2"; + // public static final String ERR_CIELAB_BPS_NOT_8_OR_16 = "BitsPerSample not 8 or 16 for CIE + // L*a*b*"; + // public static final String ERR_COL_MAP_NOT_DEF = "ColorMap not defined for palette-color"; + // public static final String ERR_COL_MAP_MISS_VALS = "Insufficient ColorMap values for + // palette-color: "; + // public static final String ERR_DATE_TIME_LEN_INV = "Invalid DateTime length: "; + // public static final String ERR_DATE_TIME_SEP_INV = "Invalid DateTime separator: "; + // public static final String ERR_DATE_TIME_DIG_INV = "Invalid DateTime digit: "; + // public static final String ERR_DOT_RANGE_BPS = "DotRange out of range specified by + // BitsPerSample"; + // public static final String ERR_EXIF_BLOCK_TOO_SHORT = "Embedded Exif block is too short"; + // public static final String ERR_FILE_TOO_SHORT = "File is too short"; + // public static final String ERR_GEO_KEY_DIRECT_INVALID = "Invalid GeoKeyDirectory tag"; + // public static final String ERR_GEO_KEY_OUT_SEQ = "GeoKey "; + // public static final String ERR_GEO_KEY_OUT_SEQ_2 = " out of sequence"; + // public static final String ERR_GPS_IFD_TAG_UNK = "Unknown GPSInfo IFD tag"; + // public static final String ERR_IFD_MISSING = "No IFD in file "; + // public static final String ERR_IFD_OFF_MISALIGN = "IFD offset not word-aligned: "; + // public static final String ERR_IFD_MAX_EXCEEDED = "More than 50 IFDs in chain, probably an + // infinite loop"; + // public static final String ERR_IO_READ = "Read error"; + // public static final String ERR_JPEGPROC_NO_DEF = "JPEGProc not defined for JPEG compression"; + // public static final String ERR_PAL_COL_SPP_NE_1 = "For palette-color SamplesPerPixel must be 1: + // "; + // public static final String ERR_PHO_AND_NEW_SUBFILE_INCONSISTENT = "PhotometricInterpretation + // and NewSubfileType must agree on transparency mask"; + // public static final String ERR_PHO_INT_SPP_GT_1 = "For PhotometricInterpretation, + // SamplesPerPixel must be >= 1, equals: "; + // public static final String ERR_PHO_INT_SPP_GT_3 = "For PhotometricInterpretation, + // SamplesPerPixel must be >= 3, equals: "; + // public static final String ERR_SPP_EXTRA_NT_1_OR_3 = "SamplesPerPixel-ExtraSamples not 1 or + // 3:"; + // public static final String ERR_TAG_COUNT_MISMATCH = "Count" + tagMismatch; + // public static final String ERR_TAG_IO_READ = ERR_IO_READ + " for tag "; + // public static final String ERR_TAG_ICCPROFILE_BAD = "Bad ICCProfile in tag "; + // public static final String ERR_TAG_OUT_OF_SEQ_1 = "Tag "; + // public static final String ERR_TAG_OUT_OF_SEQ_2 = ERR_GEO_KEY_OUT_SEQ_2; + // public static final String ERR_TAG_TYPE_MISMATCH = "Type" + tagMismatch; + // public static final String ERR_TIFF_HEADER_MISSING = "No TIFF header: "; + // public static final String ERR_TIFF_MAGIC_NUM_MISSING = "No TIFF magic number: "; + // public static final String ERR_TIFF_PREM_EOF = "Premature EOF"; + // public static final String ERR_TRANS_MASK_BPS = "For transparency mask BitsPerSample must be + // 1"; + // public static final String ERR_UNK_DATA_TYPE = "Unknown data type"; + // public static final String ERR_UNK_DATA_TYPE_SUB_1 = "Type = "; + // public static final String ERR_UNK_DATA_TYPE_SUB_2 = ", Tag = "; + // public static final String ERR_EXIF_INTER_IFD_UNK = "Unknown Exif Interoperability IFD tag"; + // public static final String ERR_VAL_OUT_OF_RANGE = " value out of range: "; + // public static final String ERR_XMP_INVALID = "Invalid or ill-formed XMP metadata"; + // public static final String ERR_TILE_WID_NO_DEF = "TileWidth not defined"; + // public static final String ERR_TILE_LEN_NO_DEF = "TileLength not defined"; + // public static final String ERR_TILE_OFF_NO_DEF = "TileOffsets not defined"; + // public static final String ERR_TILE_COUNT_NO_DEF = "TileByteCounts not defined"; + // public static final String ERR_TILE_WID_NOT_DIV_16 = "TileWidth not a multiple of 16: "; + // public static final String ERR_TILE_LEN_NOT_DIV_16 = "TileLength not a multiple of 16: "; + // public static final String ERR_TILE_OFF_MISS_VALS = "Insufficient values for TileOffsets: "; + // public static final String ERR_TILE_COUNT_MISS_VALS = "Insufficient values for TileByteCounts: + // "; + // public static final String ERR_XCLIP_PATH_NO_DEF = "XClipPathUnits not defined for ClipPath"; -} \ No newline at end of file +} diff --git a/jhove-modules/tiff-hul/src/main/java/edu/harvard/hul/ois/jhove/module/tiff/TiffException.java b/jhove-modules/tiff-hul/src/main/java/edu/harvard/hul/ois/jhove/module/tiff/TiffException.java index a2bea87d8..dd544f217 100644 --- a/jhove-modules/tiff-hul/src/main/java/edu/harvard/hul/ois/jhove/module/tiff/TiffException.java +++ b/jhove-modules/tiff-hul/src/main/java/edu/harvard/hul/ois/jhove/module/tiff/TiffException.java @@ -1,62 +1,48 @@ -/********************************************************************** - * Jhove - JSTOR/Harvard Object Validation Environment - * Copyright 2003 by JSTOR and the President and Fellows of Harvard College - **********************************************************************/ - +/** + * ******************************************************************** Jhove - JSTOR/Harvard Object + * Validation Environment Copyright 2003 by JSTOR and the President and Fellows of Harvard College + * ******************************************************************** + */ package edu.harvard.hul.ois.jhove.module.tiff; import edu.harvard.hul.ois.jhove.messages.JhoveMessage; import edu.harvard.hul.ois.jhove.messages.JhoveMessages; -/** - * Exception subclass used internally by the TIFF module. - */ +/** Exception subclass used internally by the TIFF module. */ public final class TiffException extends Exception { - private final JhoveMessage message; - private final long _offset; // File offset at which the exception - // occurred - - /** - * Create a TiffException. - */ - public TiffException(final String m) { - this(JhoveMessages.getMessageInstance(m)); - } - - /** - * Create a TiffException. - */ - public TiffException(final JhoveMessage message) { - this(message, -1); - } - - /** - * Create a TiffException with specified offset. - */ - public TiffException(String m, long offset) { - this(JhoveMessages.getMessageInstance(m), -1); - } - - /** - * Create a TiffException with specified offset. - */ - public TiffException(final JhoveMessage message, long offset) { - super(message.getMessage()); - this.message = message; - this._offset = offset; - } - - /** - * @return the JhoveMessage associated with this exception - */ - public JhoveMessage getJhoveMessage() { - return this.message; - } - - /** - * Returns the offset at which the exception occurred. - */ - public long getOffset() { - return this._offset; - } + private final JhoveMessage message; + private final long _offset; // File offset at which the exception + // occurred + + /** Create a TiffException. */ + public TiffException(final String m) { + this(JhoveMessages.getMessageInstance(m)); + } + + /** Create a TiffException. */ + public TiffException(final JhoveMessage message) { + this(message, -1); + } + + /** Create a TiffException with specified offset. */ + public TiffException(String m, long offset) { + this(JhoveMessages.getMessageInstance(m), -1); + } + + /** Create a TiffException with specified offset. */ + public TiffException(final JhoveMessage message, long offset) { + super(message.getMessage()); + this.message = message; + this._offset = offset; + } + + /** @return the JhoveMessage associated with this exception */ + public JhoveMessage getJhoveMessage() { + return this.message; + } + + /** Returns the offset at which the exception occurred. */ + public long getOffset() { + return this._offset; + } } diff --git a/jhove-modules/tiff-hul/src/main/java/edu/harvard/hul/ois/jhove/module/tiff/TiffFXBase.java b/jhove-modules/tiff-hul/src/main/java/edu/harvard/hul/ois/jhove/module/tiff/TiffFXBase.java index c16c5d151..3fc76b6a6 100644 --- a/jhove-modules/tiff-hul/src/main/java/edu/harvard/hul/ois/jhove/module/tiff/TiffFXBase.java +++ b/jhove-modules/tiff-hul/src/main/java/edu/harvard/hul/ois/jhove/module/tiff/TiffFXBase.java @@ -1,91 +1,82 @@ -/********************************************************************** - * Jhove - JSTOR/Harvard Object Validation Environment - * Copyright 2004 by JSTOR and the President and Fellows of Harvard College - **********************************************************************/ - +/** + * ******************************************************************** Jhove - JSTOR/Harvard Object + * Validation Environment Copyright 2004 by JSTOR and the President and Fellows of Harvard College + * ******************************************************************** + */ package edu.harvard.hul.ois.jhove.module.tiff; import edu.harvard.hul.ois.jhove.*; /** + * Base class for all profiles under TIFF/FX. All TIFF/FX profiles should call + * TiffFXBase.satisfiesClass to establish that common requirements are met. * - * Base class for all profiles under TIFF/FX. - * All TIFF/FX profiles should call TiffFXBase.satisfiesClass - * to establish that common requirements are met. - * * @author Gary McGath - * */ public abstract class TiffFXBase extends TiffProfile { - /** Tiff/FX-specific tags. */ - public static final int - GLOBALPARAMETERSIFD = 400, - STRIPROWCOUNTS = 559; + /** Tiff/FX-specific tags. */ + public static final int GLOBALPARAMETERSIFD = 400, STRIPROWCOUNTS = 559; - /****************************************************************** - * PRIVATE CLASS FIELDS. - ******************************************************************/ - - /* Conversion table from units/inch to units/cm */ - private int[] cmInchTab[] = { - {80, 204}, - {160, 408}, - {38, 98}, // really 38.5 - {77, 196}, - {154, 391} - }; + /** + * **************************************************************** PRIVATE CLASS FIELDS. + * **************************************************************** + */ - /** - * Test for common requirements of all Tiff/FX profiles. - * Subclasses should call satisfiesClass() from their - * satisfiesThisProfile() method to avoid redundant code. - * If this method returns false, the IFD does not - * meet the requirements of any TIFF/FX profile. Calling this - * also guarantees that the image length, image width, - * bits per sample, X resolution (sampling frequency), - * Y resolution and page number values are non-null objects. - */ - protected boolean satisfiesClass (TiffIFD ifd) - { - /* Check required tags. */ - NisoImageMetadata niso = ifd.getNisoImageMetadata (); - if (niso.getImageLength () == NisoImageMetadata.NULL || - niso.getStripOffsets () == null || - niso.getImageWidth () == NisoImageMetadata.NULL || - niso.getBitsPerSample () == null || - niso.getColorSpace () == NisoImageMetadata.NULL || - niso.getCompressionScheme () == NisoImageMetadata.NULL || - // colorSpace == photometricInterpretation - niso.getXSamplingFrequency () == null || - niso.getYSamplingFrequency () == null || - ifd.getNewSubfileType () == IFD.NULL || - ifd.getPageNumber () == null) { - return false; - } - - // If compression method is 3, T4 options must be specified - if (niso.getCompressionScheme () == 3 && ifd.getT4Options () == IFD.NULL) { - return false; - } - return true; // placeholder + /* Conversion table from units/inch to units/cm */ + private int[] cmInchTab[] = { + {80, 204}, + {160, 408}, + {38, 98}, // really 38.5 + {77, 196}, + {154, 391} + }; + + /** + * Test for common requirements of all Tiff/FX profiles. Subclasses should call + * satisfiesClass() from their satisfiesThisProfile() method to avoid + * redundant code. If this method returns false, the IFD does not meet the + * requirements of any TIFF/FX profile. Calling this also guarantees that the image length, image + * width, bits per sample, X resolution (sampling frequency), Y resolution and page number values + * are non-null objects. + */ + protected boolean satisfiesClass(TiffIFD ifd) { + /* Check required tags. */ + NisoImageMetadata niso = ifd.getNisoImageMetadata(); + if (niso.getImageLength() == NisoImageMetadata.NULL + || niso.getStripOffsets() == null + || niso.getImageWidth() == NisoImageMetadata.NULL + || niso.getBitsPerSample() == null + || niso.getColorSpace() == NisoImageMetadata.NULL + || niso.getCompressionScheme() == NisoImageMetadata.NULL + || + // colorSpace == photometricInterpretation + niso.getXSamplingFrequency() == null + || niso.getYSamplingFrequency() == null + || ifd.getNewSubfileType() == IFD.NULL + || ifd.getPageNumber() == null) { + return false; + } + + // If compression method is 3, T4 options must be specified + if (niso.getCompressionScheme() == 3 && ifd.getT4Options() == IFD.NULL) { + return false; } - - /** - * Convert a units/cm value to a units/inch value. - * For expected values, we use a table lookup to avoid - * rounding problems. If a value isn't in the table, - * we do a rounded conversion. - */ - protected int perCMtoPerInch (int res) - { - for (int i = 0; i < cmInchTab.length; i++) { - int[] pair = cmInchTab[i]; - if (pair[0] == res) { - return pair[1]; - } - } - // No table match; use rounding. - return (int) ((res * 2.54) + 0.5); + return true; // placeholder + } + + /** + * Convert a units/cm value to a units/inch value. For expected values, we use a table lookup to + * avoid rounding problems. If a value isn't in the table, we do a rounded conversion. + */ + protected int perCMtoPerInch(int res) { + for (int i = 0; i < cmInchTab.length; i++) { + int[] pair = cmInchTab[i]; + if (pair[0] == res) { + return pair[1]; + } } + // No table match; use rounding. + return (int) ((res * 2.54) + 0.5); + } } diff --git a/jhove-modules/tiff-hul/src/main/java/edu/harvard/hul/ois/jhove/module/tiff/TiffIFD.java b/jhove-modules/tiff-hul/src/main/java/edu/harvard/hul/ois/jhove/module/tiff/TiffIFD.java index 908364613..048ee3390 100644 --- a/jhove-modules/tiff-hul/src/main/java/edu/harvard/hul/ois/jhove/module/tiff/TiffIFD.java +++ b/jhove-modules/tiff-hul/src/main/java/edu/harvard/hul/ois/jhove/module/tiff/TiffIFD.java @@ -1,3609 +1,3727 @@ -/********************************************************************** - * Jhove - JSTOR/Harvard Object Validation Environment - * Copyright 2004 by JSTOR and the President and Fellows of Harvard College - **********************************************************************/ - +/** + * ******************************************************************** Jhove - JSTOR/Harvard Object + * Validation Environment Copyright 2004 by JSTOR and the President and Fellows of Harvard College + * ******************************************************************** + */ package edu.harvard.hul.ois.jhove.module.tiff; import edu.harvard.hul.ois.jhove.*; import edu.harvard.hul.ois.jhove.messages.JhoveMessage; import edu.harvard.hul.ois.jhove.messages.JhoveMessages; - import java.io.*; import java.text.MessageFormat; import java.util.*; -import org.xml.sax.XMLReader; -import org.xml.sax.SAXException; import javax.xml.parsers.SAXParserFactory; +import org.xml.sax.SAXException; +import org.xml.sax.XMLReader; -/** - * Encapsulation of standard TIFF IFD. - */ +/** Encapsulation of standard TIFF IFD. */ public class TiffIFD extends IFD { - /****************************************************************** - * PRIVATE CLASS FIELDS. - ******************************************************************/ - - /** Standard TIFF 6.0 tags. */ - public static final int NEWSUBFILETYPE = 254, SUBFILETYPE = 255, - IMAGEWIDTH = 256, IMAGELENGTH = 257, BITSPERSAMPLE = 258, - COMPRESSION = 259, PHOTOMETRICINTERPRETATION = 262, - THRESHHOLDING = 263, CELLWIDTH = 264, CELLLENGTH = 265, - FILLORDER = 266, DOCUMENTNAME = 269, IMAGEDESCRIPTION = 270, - MAKE = 271, MODEL = 272, STRIPOFFSETS = 273, ORIENTATION = 274, - SAMPLESPERPIXEL = 277, ROWSPERSTRIP = 278, STRIPBYTECOUNTS = 279, - MINSAMPLEVALUE = 280, MAXSAMPLEVALUE = 281, XRESOLUTION = 282, - YRESOLUTION = 283, PLANARCONFIGURATION = 284, PAGENAME = 285, - XPOSITION = 286, YPOSITION = 287, FREEOFFSETS = 288, - FREEBYTECOUNTS = 289, GRAYRESPONSEUNIT = 290, - GRAYRESPONSECURVE = 291, T4OPTIONS = 292, T6OPTIONS = 293, - RESOLUTIONUNIT = 296, PAGENUMBER = 297, TRANSFERFUNCTION = 301, - SOFTWARE = 305, DATETIME = 306, ARTIST = 315, HOSTCOMPUTER = 316, - PREDICTOR = 317, WHITEPOINT = 318, PRIMARYCHROMATICITIES = 319, - COLORMAP = 320, HALFTONEHINTS = 321, TILEWIDTH = 322, - TILELENGTH = 323, TILEOFFSETS = 324, TILEBYTECOUNTS = 325, - INKSET = 332, INKNAMES = 333, NUMBEROFINKS = 334, DOTRANGE = 336, - TARGETPRINTER = 337, EXTRASAMPLES = 338, SAMPLEFORMAT = 339, - SMINSAMPLEVALUE = 340, SMAXSAMPLEVALUE = 341, TRANSFERRANGE = 342, - JPEGPROC = 512, JPEGINTERCHANGEFORMAT = 513, - JPEGINTERCHANGEFORMATLENGTH = 514, JPEGRESTARTINTERVAL = 515, - JPEGLOSSLESSPREDICTORS = 517, JPEGPOINTTRANSFORMS = 518, - JPEGQTABLES = 519, JPEGDCTABLES = 520, JPEGACTABLES = 521, - YCBCRCOEFFICIENTS = 529, YCBCRSUBSAMPLING = 530, - YCBCRPOSITIONING = 531, REFERENCEBLACKWHITE = 532, - COPYRIGHT = 33432; - - /** Fill order tag (266) labels. */ - private static final String[] FILLORDER_L = { "", "high-order", - "low-order" }; - /** Indexed tag (346) labels. */ - private static final String[] INDEXED_L = { "not indexed", "indexed" }; - /** InkSet tag (332) labels. */ - private static final String[] INKSET_L = { "", "CMYK", "not CMYK" }; - /** JPEGLosslessPredictors tag (517) labels. */ - private static final String[] JPEGLOSSLESSPREDICTORS_L = { "", "A", "B", - "C", "A+B+C", "A+((B-C)/2)", "B+((A-C)/2)", "(A+B)/2" }; - /** JPEGProc tag (512) labels. */ - private static final String[] JPEGPROC_L = { "baseline sequential process", - "lossless process with Huffman coding" }; - private static final int[] JPEGPROC_INDEX = { 1, 14 }; - /** NewSubfileType tag (254) bit labels. */ - private static final String[] NEWSUBFILETYPE_L = { - "reduced-resolution image of another image in this file", - "single page of multi-page image", - "transparency mask for another image in this file" }; - /** OPIProxy tag (351) labels. */ - private static final String[] OPIPROXY_L = { - "no higher-resolution version exists", - "higher-resolution version exists" }; - /** Predictor tag (317) labels. */ - private static final String[] PREDICTOR_L = { "", "no prediction scheme", - "horizontal differencing" }; - /** SampleFormat tag (339) labels. */ - private static final String[] SAMPLEFORMAT_L = { "", "unsigned integer", - "signed integer", "IEEE floating point", "undefined" }; - /** SubfileType tag (255) labels. */ - private static final String[] SUBFILETYPE_L = { "", "full-resolution image", - "reduced-resolution image", "single page of multi-page image" }; - /** Threshholding tag (263) labels. */ - private static final String[] THRESHHOLDING_L = { "", - "no dithering or halftoning", "ordered dithering or halftoning", - "randomized process" }; - /** YCbCrPositioning tag (531) labels. */ - private static final String[] YCBCRPOSITIONING_L = { "", "centered", - "cosited" }; - /** YCbCrSubSampling tag (530) labels. */ - private static final String[] YCBCRSUBSAMPLING_HORZ = { "", - "width of chroma image is equal to width of associated luma image", - "width of chroma image is 1/2 the width of associated luma image", - "", - "width of chroma image is 1/4 the width of associated luma image" }; - private static final String[] YCBCRSUBSAMPLING_VERT = { "", - "length of chroma image is equal to length of associated luma image", - "length of chroma image is 1/2 the length of associated luma image", - "", - "length of chroma image is 1/4 the length of associated luma image" }; - - /** TIFF/IT tags. */ - private static final int SITE = 34016, COLORSEQUENCE = 34017, - IT8HEADER = 34018, RASTERPADDING = 34019, BITSPERRUNLENGTH = 34020, - BITSPEREXTENDEDRUNLENGTH = 34021, COLORTABLE = 34022, - IMAGECOLORINDICATOR = 34023, BACKGROUNDCOLORINDICATOR = 34024, - IMAGECOLORVALUE = 34025, BACKGROUNDCOLORVALUE = 34026, - PIXELINTENSITYRANGE = 34027, TRANSPARENCYINDICATOR = 34028, - COLORCHARACTERIZATION = 34029, HCUSAGE = 34030; - - public static final String[] BACKGROUNDCOLORINDICATOR_L = { - "background not defined", "Background color defined", - "full transparency, background color not defined" }; - public static final String[] HCUSAGE_L = { - "high resolution CT contone information", - "line art (line work) information", "trapping information" }; - public static final String[] IMAGECOLORINDICATOR_L = { "image not defined", - "image color defined", - "full transparency, image color not defined" }; - /* RasterPadding tag (34019) labels */ - private static final String[] RASTERPADDING_L = { "1 byte", "2 bytes", - "4 bytes", "512 bytes", "1024 bytes" }; - private static final int[] RASTERPADDING_INDEX = { 0, 1, 2, 9, 10 }; - public static final String[] TRANSPARENCYINDICATOR_L = { "no transparency", - "transparency used" }; - - /** TIFF/EP tags. */ - private static final int CFAREPEATPATTERNDIM = 33421, CFAPATTERN = 33422, - BATTERYLEVEL = 33423, EXPOSURETIME = 33434, FNUMBER = 33437, - IPTCNAA = 33723, ICC_PROFILE = 34675, EXPOSUREPROGRAM = 34850, - SPECTRALSENSITIVITY = 34852, ISOSPEEDRATINGS = 34855, OECF = 34856, - INTERLACE = 34857, TIMEZONEOFFSET = 34858, SELFTIMERMODE = 34859, - DATETIMEORIGINAL = 36867, COMPRESSEDBITSPERPIXEL = 37122, - SHUTTERSPEEDVALUE = 37377, APERTUREVALUE = 37378, - BRIGHTNESSVALUE = 37379, EXPOSUREBIASVALUE = 37380, - MAXAPERTUREVALUE = 37381, SUBJECTDISTANCE = 37382, - METERINGMODE = 37383, LIGHTSOURCE = 37384, FLASH = 37385, - FOCALLENGTH = 37386, FLASHENERGY = 37387, - SPATIALFREQUENCYRESPONSE = 37388, NOISE = 37389, - FOCALPLANEXRESOLUTION = 37390, FOCALPLANEYRESOLUTION = 37391, - FOCALPLANERESOLUTIONUNIT = 37392, IMAGENUMBER = 37393, - SECURITYCLASSIFICATION = 37394, IMAGEHISTORY = 37395, - SUBJECTLOCATION = 37396, EXPOSUREINDEX = 37397, - TIFFEPSTANDARDID = 37398, SENSINGMETHOD = 37399; - /** TIFF/EP tag labels. */ - private static final String[] EXPOSUREPROGRAM_L = { "unidentified", - "manual", "program normal", "aperature priority", - "shutter priority", "program creative", "program action", - "portrait mode", "landscape mode" }; - private static final String[] FLASH_L = { "did not fire", "fired", - "fired, return not sensed", "fired, return sensed", - "fired, fill flash mode, camera has no flash return sensing capability", - "fired, fill flash mode, return not sensed", - "fired, fill flash mode, return sensed", - "did not fire, flash 'off' mode", "did not fire, 'auto' mode", - "fired, 'auto' mode, camera has no flash return sensing capability", - "fired, 'auto' mode, return not sensed", - "fired, 'auto' mode, return sensed", - "camera does not have a flash unit" }; - private static final int[] FLASH_INDEX = { 0, 1, 5, 7, 9, 13, 15, 16, 24, - 25, 29, 31, 32 }; - // Official values in TIFF/EP (not compatible with EXIF or MIX) - // "", "inches", "meters", "centimeters", "millimeters", "micrometers" - protected static final String[] FOCALPLANERESOLUTIONUNIT_L = { "", "", - "inches", "centimeters", "millimeters", "micrometers" - }; - - /** Exif tags. */ - private static final int EXIFIFD = 34665, GPSINFOIFD = 34853, - INTEROPERABILITYIFD = 40965; - - /** GeoTIFF tags. */ - private static final int GEOKEYDIRECTORYTAG = 34735, - GEODOUBLEPARAMSTAG = 34736, GEOASCIIPARAMSTAG = 34737, - MODELTIEPOINTTAG = 33922, MODELPIXELSCALETAG = 33550, - MODELTRANSFORMATIONTAG = 34264; - /** GeoTIFF key values. */ - public static final int GTMODELTYPEGEOKEY = 1024, GTRASTERTYPEGEOKEY = 1025, - GTCITATIONGEOKEY = 1026, GEOGRAPHICTYPEGEOKEY = 2048, - GEOGCITATIONGEOKEY = 2049, GEOGGEODETICDATUMGEOKEY = 2050, - GEOGPRIMEMERIDIANGEOKEY = 2051, GEOGLINEARUNITSGEOKEY = 2052, - GEOGLINEARUNITSIZEGEOKEY = 2053, GEOGANGULARUNITSGEOKEY = 2054, - GEOGANGULARUNITSIZEGEOKEY = 2055, GEOGELLIPSOIDGEOKEY = 2056, - GEOGSEMIMAJORAXISGEOKEY = 2057, GEOGSEMIMINORAXISGEOKEY = 2058, - GEOGINVFLATTENINGGEOKEY = 2059, GEOGAZIMUTHUNITSGEOKEY = 2060, - GEOGPRIMEMERIDIANLONGGEOKEY = 2061, PROJECTEDCSTYPEGEOKEY = 3072, - PCSCITATIONGEOKEY = 3073, PROJECTIONGEOKEY = 3074, - PROJCOORDTRANSGEOKEY = 3075, PROJLINEARUNITSGEOKEY = 3076, - PROJLINEARUNITSIZEGEOKEY = 3077, PROJSTDPARALLEL1GEOKEY = 3078, - PROJSTDPARALLEL2GEOKEY = 3079, PROJNATORIGINLONGGEOKEY = 3080, - PROJNATORIGINLATGEOKEY = 3081, PROJFALSEEASTINGGEOKEY = 3082, - PROJFALSENORTHINGGEOKEY = 3083, PROJFALSEORIGINLONGGEOKEY = 3084, - PROJFALSEORIGINLATGEOKEY = 3085, - PROJFALSEORIGINEASTINGGEOKEY = 3086, - PROJFALSEORIGINNORTHINGGEOKEY = 3087, PROJCENTERLONGGEOKEY = 3088, - PROJCENTERLATGEOKEY = 3089, PROJCENTEREASTINGGEOKEY = 3090, - PROJFALSEORIGINNORTHINGGEOKEY_2 = 3091, - PROJSCALEATNATORIGINGEOKEY = 3092, PROJSCALEATCENTERGEOKEY = 3093, - PROJAZIMUTHANGLEGEOKEY = 3094, PROJSTRAIGHTVERTPOLELONGEOKEY = 3095, - VERTICALCSTYPEGEOKEY = 4096, VERTICALCITATIONGEOKEY = 4097, - VERTICALDATUMGEOKEY = 4098, VERTICALUNITSGEOKEY = 4099; - - /** PageMaker 6.0 tags. */ - private static final int SUBIFDS = 330, CLIPPATH = 343, - XCLIPPATHUNITS = 344, YCLIPPATHUNITS = 345, INDEXED = 346, - OPIPROXY = 351, IMAGEID = 32781; - - /** Photoshop 'Advanced Tiff' tags. */ - private static final int JPEGTABLES = 347, IMAGESOURCEDATA = 37724; - - /** - * More Photoshop TIFF tags. - */ - private static final int PHOTOSHOPPROPS = 34377, ANNOTATIONS = 50255; - - /** Class F tags. */ - private static final int BADFAXLINES = 326, CLEANFAXDATA = 327, - CONSECUTIVEBADFAXLINES = 328; - - /** XMP tag. */ - private static final int XMP = 700; - - /** TIFF/FX tags. */ - private static final int GLOBALPARAMETERSIFD = 400, STRIPROWCOUNTS = 559, - IMAGELAYER = 34732; - - public static final String[] IMAGELAYER_L = { "", "Background", "Mask", - "Foreground" }; - - /** DNG tags. */ - private static final int DNGVERSION = 50706, DNGBACKWARDVERSION = 50707, - UNIQUECAMERAMODEL = 50708, LOCALIZEDCAMERAMODEL = 50709, - CFAPLANECOLOR = 50710, CFALAYOUT = 50711, - LINEARIZATIONTABLE = 50712, BLACKLEVELREPEATDIM = 50713, - BLACKLEVEL = 50714, BLACKLEVELDELTAH = 50715, - BLACKLEVELDELTAV = 50716, WHITELEVEL = 50717, DEFAULTSCALE = 50718, - DEFAULTCROPORIGIN = 50719, DEFAULTCROPSIZE = 50720, - COLORMATRIX1 = 50721, COLORMATRIX2 = 50722, - CAMERACALIBRATION1 = 50723, CAMERACALIBRATION2 = 50724, - REDUCTIONMATRIX1 = 50725, REDUCTIONMATRIX2 = 50726, - ANALOGBALANCE = 50727, ASSHOTNEUTRAL = 50728, ASSHOTWHITEXY = 50729, - BASELINEEXPOSURE = 50730, BASELINENOISE = 50731, - BASELINESHARPNESS = 50732, BAYERGREENSPLIT = 50733, - LINEARRESPONSELIMIT = 50734, CAMERASERIALNUMBER = 50735, - LENSINFO = 50736, CHROMABLURRADIUS = 50737, - ANTIALIASSTRENGTH = 50738, SHADOWSCALE = 50739, // Undocumented - // tag - DNGPRIVATEDATA = 50740, MAKERNOTESAFETY = 50741, - CALIBRATIONILLUMINANT1 = 50778, CALIBRATIONILLUMINANT2 = 50779, - BESTQUALITYSCALE = 50780; - - public static final String[] CFALAYOUT_L = { "", "Rectangular", - "Staggered Layout A", "Staggered Layout B", "Staggered Layout C", - "Staggered Layout D" }; - public static final String[] MAKERNOTESAFETY_L = { "Unsafe", "Safe" }; - - /****************************************************************** - * PRIVATE INSTANCE FIELDS. - ******************************************************************/ - - /** NISO Z39.87/AIIM 20-2002 image metadata. */ - private NisoImageMetadata _niso; - - /** NewSubfileType tag (254). */ - private long _newSubfileType; - /** SubfileType tag (255). */ - private int _subfileType; - /** PhotometricInterpretation tag (262). */ - private int _photometricInterpretation; - /** Threshholding tag (263). */ - private int _threshholding; - /** Cell width tag (264). */ - private int _cellWidth; - /** Cell length tag (265). */ - private int _cellLength; - /** Fill order tag (266). */ - private int _fillOrder; - /** Document name tag (269). */ - private String _documentName; - /** Image description tag (270). */ - private String _imageDescription; - /** Minimum sample value tag (280). */ - private int[] _minSampleValue; - /** Maximum sample value tag (281). */ - private int[] _maxSampleValue; - /** Page name tag (285). */ - private String _pageName; - /** X position tag (286). */ - private Rational _xPosition; - /** Y position tag (287). */ - private Rational _yPosition; - /** Free offsets tag (288). */ - private long[] _freeOffsets; - /** Free byte counts tag (289). */ - private long[] _freeByteCounts; - /** CCITT Group 3 compression options tag (292). */ - private long _t4Options; - /** CCITT Group 4 compression options tag (293). */ - private long _t6Options; - /** Page number tag (297). */ - private int[] _pageNumber; - /** Transfer function tag (301). */ - private boolean _transferFunction; - /** Date/time tag (306). */ - private String _dateTime; - /** Compression differencing predictor tag (317). */ - private int _predictor; - /** Halftone hints tag (321). */ - private int[] _halftoneHints; - /** Bad fax lines tag (326). */ - private long _badFaxLines; - /** Clean fax data tag (327). */ - private short _cleanFaxData; - /** Consecutive bad fax lines tag (328). */ - private long _consecutiveBadFaxLines; - /** Ink set tag (332). */ - private int _inkSet; - /** InkNames tag (322). */ - private String[] _inkNames; - /** Sub IFDs tag (330). */ - private long[] _subIFDs; - /** Number of inks tag (334). */ - private int _numberOfInks; - /** Dot range tag (336). */ - private int[] _dotRange; - /** Target printer tag (337). */ - private String _targetPrinter; - /** Sample format tag (339). */ - private int[] _sampleFormat; - /** Transfer range tag (342). */ - private int[] _transferRange; - /** Clip path tag (343). */ - private int[] _clipPath; - /** X clip path units tag (344). */ - private long _xClipPathUnits; - /** Y clip path units tag (345). */ - private long _yClipPathUnits; - /** Indexed tag (346). */ - private int _indexed; - /** JPEG tables tag (347). */ - private int[] _jpegTables; - /** OPI proxy tag (351). */ - private int _opiProxy; - /** JPEG Proc tag (512). */ - private int _jpegProc; - /** JPEG interchange format tag (513). */ - private long _jpegInterchangeFormat; - /** JPEG interchange format length tag (514). */ - private long _jpegInterchangeFormatLength; - /** JPEG restart interval tag (515). */ - private int _jpegRestartInterval; - /** JPEG lossless predictors tag (517). */ - private int[] _jpegLosslessPredictors; - /** JPEG point transforms tag (518). */ - private int[] _jpegPointTransforms; - /** JPEG Q tables tag (519). */ - private long[] _jpegQTables; - /** JPEG DC tables tag (520). */ - private long[] _jpegDCTables; - /** JPEG AC tables tag (521). */ - private long[] _jpegACTables; - /** Copyright tag (33432). */ - private String _copyright; - /** Exif IFD tag (34665). */ - private long _exifIFD; - /** GPSInfo IFD tag (34853). */ - private long _gpsInfoIFD; - /** GlobalParametersIFD tag (400). */ - private long _globalParametersIFD; - /** Photoshop Properties tag (34377). */ - private int[] _photoshopProperties; - /** ImageSourceData tag (37724). */ - private int[] _imageSourceData; - /** Exif Interoperability IFD tag (40965). */ - private long _interoperabilityIFD; - /** Annotations tag (50255). */ - private int[] _annotations; - - /* TIFF/IT tags. */ - private int _backgroundColorIndicator; - private int _backgroundColorValue; - private int _bitsPerExtendedRunLength; - private int _bitsPerRunLength; - private String _colorCharacterization; - private String _colorSequence; - private int[] _colorTable; - private long _hcUsage; - private int _imageColorIndicator; - private int _imageColorValue; - private String _it8Header; - private int[] _pixelIntensityRange; - private int _rasterPadding; - private String _site; - private int _transparencyIndicator; - - /* TIFF/EP tags. */ - private Rational _aperatureValue; - private String _batteryLevel; - private int[] _cfaRepeatPatternDim; - private int[] _cfaPattern; - private Rational _compressedBitsPerPixel; - private int _exposureProgram; - private int _flash; - private int _focalPlaneResolutionUnit; - private Rational _focalPlaneXResolution; - private Rational _focalPlaneYResolution; - private byte[] _iccProfile; - private String _imageHistory; - private long _imageNumber; - private int _interlace; - private long[] _iptc; - private int[] _isoSpeedRatings; - private Rational _maxAperatureValue; - private int[] _noise; - private int[] _oecf; - private String _securityClassification; - private int _selfTimerMode; - private Rational _shutterSpeedValue; - private int[] _spatialFrequencyResponse; - private String _spectralSensitivity; - private int[] _subjectLocation; - private String _tiffEPStandardID; - private int[] _timeZoneOffset; - - /* GeoTIFF tags. */ - private String _geoAsciiParamsTag; - private double[] _geoDoubleParamsTag; - private int[] _geoKeyDirectoryTag; - private double[] _modelPixelScaleTag; - private double[] _modelTiepointTag; - private double[] _modelTransformationTag; - - /* XMP property. */ - private Property _xmpProp; - - /* Tiff/FX tag values. */ - private long[] _stripRowCounts; - private int[] _imageLayer; - - /** Exif IFD object. */ - private ExifIFD _theExifIFD; - /** GPSInfo IFD object. */ - private GPSInfoIFD _theGPSInfoIFD; - /** Exif Interoperability IFD. */ - private InteroperabilityIFD _theInteroperabilityIFD; - /** GlobalParameters IFD. */ - private GlobalParametersIFD _theGlobalParametersIFD; - - /* - * DNG tag values. The spec says that some of these tags go into - * a "raw IFD," which isn't defined. Until this is explained, - * throw it all in here. - */ - private int[] _dngVersion; - private int[] _dngBackwardVersion; - private String _uniqueCameraModel; - private String _localizedCameraModel; // Note: This is specified as Unicode - private int[] _cfaPlaneColor; - private int _cfaLayout; - private int[] _linearizationTable; - private int[] _blackLevelRepeatDim; - // BlackLevel can be SHORT or LONG or RATIONAL. - // To avoid having to store multiple versions, we will convert - // SHORT or LONG values to RATIONAL. - private Rational[] _blackLevel; - private Rational[] _blackLevelDeltaH; - private Rational[] _blackLevelDeltaV; - // Though BlackLevel can be RATIONAL, WhiteLevel can't. - // There must be a rational explanation. - private long[] _whiteLevel; - private Rational[] _defaultScale; - private Rational _bestQualityScale; - private Rational[] _defaultCropOrigin; - private Rational[] _defaultCropSize; - private int _calibrationIlluminant1; - private int _calibrationIlluminant2; - private Rational[] _colorMatrix1; - private Rational[] _colorMatrix2; // for calculating revolutions, no doubt - private Rational[] _cameraCalibration1; - private Rational[] _cameraCalibration2; - private Rational[] _reductionMatrix1; - private Rational[] _reductionMatrix2; - private Rational[] _analogBalance; - private Rational[] _asShotNeutral; - private Rational[] _asShotWhiteXY; - private Rational _baselineExposure; - private Rational _baselineNoise; - private Rational _baselineSharpness; - private int _bayerGreenSplit; - private Rational _linearResponseLimit; - private String _cameraSerialNumber; - private Rational[] _lensInfo; - private Rational _chromaBlurRadius; - private Rational _antiAliasStrength; - private int[] _dngPrivateData; - private int _makerNoteSafety; - - /****************************************************************** - * CLASS CONSTRUCTOR. - ******************************************************************/ - - /** - * Instantiate an TiffIFD object. - * - * @param offset - * IFD offset - * @param info - * The RepInfo object - * @param raf - * TIFF file - * @param bigEndian - * True if big-endian file - */ - public TiffIFD(long offset, RepInfo info, RandomAccessFile raf, - boolean bigEndian) { - super(offset, info, raf, bigEndian); - - /* Define a NISO metadata object and set defaults. */ - _niso = new NisoImageMetadata(); - _niso.setMimeType("image/tiff"); - _niso.setCompressionScheme(1); - _niso.setOrientation(1); - _niso.setPlanarConfiguration(1); - _niso.setRowsPerStrip(4294967295L); - _niso.setSamplesPerPixel(1); - _niso.setByteOrder(bigEndian ? "big-endian" : "little-endian"); - - /* Set non-NISO defaults. */ - _photometricInterpretation = NULL; - _cellLength = NULL; - _cellWidth = NULL; - _fillOrder = NULL; - _indexed = 0; - _inkSet = NULL; - _jpegInterchangeFormat = NULL; - _jpegInterchangeFormatLength = NULL; - _jpegProc = NULL; - _jpegRestartInterval = NULL; - _newSubfileType = 0L; - _numberOfInks = NULL; - _opiProxy = NULL; - _predictor = NULL; - _subfileType = NULL; - _t4Options = NULL; - _t6Options = NULL; - _threshholding = 1; - _xClipPathUnits = NULL; - _yClipPathUnits = NULL; - - /* TIFF/IT defaults. */ - _backgroundColorIndicator = 0; - _backgroundColorValue = NULL; - _bitsPerExtendedRunLength = 16; - _bitsPerRunLength = 8; - _hcUsage = NULL; - _imageColorIndicator = 0; - _imageColorValue = NULL; - _rasterPadding = 0; - _transparencyIndicator = 0; - - /* TIFF/EP defaults. */ - _exposureProgram = NULL; - _flash = NULL; - _focalPlaneResolutionUnit = NULL; - _gpsInfoIFD = NULL; - _imageNumber = NULL; - _selfTimerMode = NULL; - - /* Exif defaults. */ - _exifIFD = NULL; - _focalPlaneResolutionUnit = NULL; - _imageNumber = NULL; - _interlace = NULL; - _interoperabilityIFD = NULL; - _globalParametersIFD = NULL; - - /* Class F/RFC 1324 defaults. */ - _badFaxLines = NULL; - _cleanFaxData = NULL; - _consecutiveBadFaxLines = NULL; - - /* XMP default. */ - _xmpProp = null; - - /* Tiff/FX defaults. */ - _stripRowCounts = null; - _imageLayer = null; - - /* DNG defaults. */ - _dngVersion = null; - _dngBackwardVersion = null; - _uniqueCameraModel = null; - _localizedCameraModel = null; - _cfaPlaneColor = null; - _cfaLayout = NULL; - _linearizationTable = null; - _blackLevelRepeatDim = null; - _blackLevel = null; - _blackLevelDeltaH = null; - _blackLevelDeltaV = null; - _whiteLevel = null; - _defaultScale = null; - _bestQualityScale = null; - _defaultCropOrigin = null; - _defaultCropSize = null; - _calibrationIlluminant1 = NULL; - _calibrationIlluminant2 = NULL; - _colorMatrix1 = null; - _colorMatrix2 = null; - _cameraCalibration1 = null; - _cameraCalibration2 = null; - _reductionMatrix1 = null; - _reductionMatrix2 = null; - _analogBalance = null; - _asShotNeutral = null; - _asShotWhiteXY = null; - _baselineExposure = null; - _baselineNoise = null; - _baselineSharpness = null; - _bayerGreenSplit = NULL; - _linearResponseLimit = null; - _cameraSerialNumber = null; - _lensInfo = null; - _chromaBlurRadius = null; - _antiAliasStrength = null; - _dngPrivateData = null; - _makerNoteSafety = NULL; - } - - /****************************************************************** - * PUBLIC INSTANCE METHODS. - ******************************************************************/ - - /** - * Returns the value of the APERTUREVALUE (37378) tag. Note typo in - * function name. - */ - public Rational getAperatureValue() { - return _aperatureValue; - } - - /** - * Returns the value of the TIFF/IT BACKGROUNDCOLORINDICATOR - * (34024) tag. - */ - public int getBackgroundColorIndicator() { - return _backgroundColorIndicator; - } - - /** - * Returns the value of the BACKGROUNDCOLORVALUE - * (34026) tag. - */ - public int getBackgroundColorValue() { - return _backgroundColorValue; - } - - /** Returns the value of the BATTERYLEVEL (33423) tag. */ - public String getBatteryLevel() { - return _batteryLevel; - } - - /** - * Returns the value of the BITSPEREXTENDEDRUNLENGTH - * (34021) tag. - */ - public int getBitsPerExtendedRunLength() { - return _bitsPerExtendedRunLength; - } - - /** Returns the value of the BITSPERRUNLENGTH (34020) tag. */ - public int getBitsPerRunLength() { - return _bitsPerRunLength; - } - - /** Returns the value of the CELLLENGTH (265) tag. */ - public int getCellLength() { - return _cellLength; - } - - /** Returns the value of the CELLWIDTH (264) tag. */ - public int getCellWidth() { - return _cellWidth; - } - - /** Returns the value of the CFAPATTERN (33422) tag. */ - public int[] getCFAPattern() { - return _cfaPattern; - } - - /** - * Returns the value of the CFAREPEATPATTERNDIM - * (33421) tag. - */ - public int[] getCFARepeatPatternDim() { - return _cfaRepeatPatternDim; - } - - /** Returns the value of the CLIPPATH (343) tag. */ - public int[] getClipPath() { - return _clipPath; - } - - /** - * Returns the value of the COLORSEQUENCE - * (34017) tag. - */ - public String getColorSequence() { - return _colorSequence; - } - - /** Returns the value of the COLORTABLE (34022) tag. */ - public int[] getColorTable() { - return _colorTable; - } - - /** - * Returns the value of the COMPRESSEDBITSPERPIXEL - * (37122) tag. - */ - public Rational getCompressedBitsPerPixel() { - return _compressedBitsPerPixel; - } - - /** Returns the value of the COPYRIGHT (33432) tag. */ - public String getCopyright() { - return _copyright; - } - - /** Returns the value of the DATETIME (306) tag. */ - public String getDateTime() { - return _dateTime; - } - - /** Returns the value of the DOCUMENTNAME (269) tag. */ - public String getDocumentName() { - return _documentName; - } - - /** Returns the value of the DOTRANGE (336) tag. */ - public int[] getDotRange() { - return _dotRange; - } - - /** Return the offset of the Exif IFD. */ - public long getExifIFD() { - return _exifIFD; - } - - /** Return the offset of the GlobalParameters IFD. */ - public long getGlobalParametersIFD() { - return _globalParametersIFD; - } - - /** Returns the value of the EXPOSUREPROGRAM (34850) tag. */ - public int getExposureProgram() { - return _exposureProgram; - } - - /** Returns the value of the FILLORDER (266) tag. */ - public int getFillOrder() { - return _fillOrder; - } - - /** - * Returns the value of the FOCALPLANERESOLUTIONUNIT - * (37392) tag. - */ - public int getFocalPlaneResolutionUnit() { - return _focalPlaneResolutionUnit; - } - - /** - * Returns the value of the FOCALPLANEXRESOLUTION - * (37390) tag. - */ - public Rational getFocalPlaneXResolution() { - return _focalPlaneXResolution; - } - - /** - * Returns the value of the FOCALPLANEYRESOLUTION - * (37390) tag. - */ - public Rational getFocalPlaneYResolution() { - return _focalPlaneYResolution; - } - - /** - * Returns the value of the GEOKEYDIRECTORYTAG - * (34735) tag. - */ - public int[] getGeoKeyDirectoryTag() { - return _geoKeyDirectoryTag; - } - - /** Return the offset of the GPSInfo IFD. */ - public long getGPSInfoIFD() { - return _gpsInfoIFD; - } - - /** - * Returns the value of the IMAGECOLORINDICATOR - * (34023) tag. - */ - public int getImageColorIndicator() { - return _imageColorIndicator; - } - - /** Returns the value of the IMAGECOLORVALUE (34025) tag. */ - public int getImageColorValue() { - return _imageColorValue; - } - - /** Returns the value of the IMAGEDESCRIPTION (270) tag. */ - public String getImageDescription() { - return _imageDescription; - } - - /** Returns the value of the IMAGEHISTORY (37395) tag. */ - public String getImageHistory() { - return _imageHistory; - } - - /** Returns the value of the IMAGELAYER (34732) tag. */ - public int[] getImageLayer() { - return _imageLayer; - } - - /** Returns the value of the IMAGENUMBER (37393) tag. */ - public long getImageNumber() { - return _imageNumber; - } - - /** - * Returns the value of the IMAGESOURCEDATA - * (37724) tag. - */ - public int[] getImageSourceData() { - return _imageSourceData; - } - - /** - * Returns the value of the PHOTOSHOPPROPS - * (34377) tag. - */ - public int[] getPhotoshopProperties() { - return _photoshopProperties; - } - - /** - * Returns the value of the ANNOTATIONS - * (50255) tag. - */ - public int[] getAnnotations() { - return _annotations; - } - - /** Returns the value of the INKNAMES (333) tag. */ - public String[] getInkNames() { - return _inkNames; - } - - /** Returns the value of the INKSET (332) tag. */ - public int getInkSet() { - return _inkSet; - } - - /** Returns the value of the INTERLACE (34857) tag. */ - public int getInterlace() { - return _interlace; - } - - /** Returns the offset of the Exif Interoperability IFD. */ - public long getInteroperabilityIFD() { - return _interoperabilityIFD; - } - - /** Returns the value of the ICC_PROFILE tag. */ - public byte[] getICCProfile() { - return _iccProfile; - } - - /** Returns the value of the INDEXED (364) tag. */ - public int getIndexed() { - return _indexed; - } - - public long getJpegInterchangeFormat() { - return _jpegInterchangeFormat; - } - - /** Returns the value of the IPTCNAA (33723) tag. */ - public long[] getIPTCNAA() { - return _iptc; - } - - /** - * Returns the value of the ISOSPEEDRATINGS - * (34855) tag. - */ - public int[] getISOSpeedRatings() { - return _isoSpeedRatings; - } - - /** Returns the value of the IT8HEADER (34018) tag. */ - public String getIT8Header() { - return _it8Header; - } - - /** Returns the value of the JPEGPROC (512) tag. */ - public int getJPEGProc() { - return _jpegProc; - } - - /** - * Returns the value of the MAXAPERTUREVALUE (37381) - * tag. Note typo in function name. - */ - public Rational getMaxAperatureValue() { - return _maxAperatureValue; - } - - /** - * Returns the value of the MODELTIEPOINTTAG (33922) - * tag. - */ - public double[] getModelTiepointTag() { - return _modelTiepointTag; - } - - /** - * Returns the value of the MODELTRANSFORMATIONTAG - * (34264) tag. - */ - public double[] getModelTransformationTag() { - return _modelTransformationTag; - } - - /** Returns the value of the NEWSUBFILETYPE (254) tag. */ - public long getNewSubfileType() { - return _newSubfileType; - } - - /** Returns the constructed NisoImageMetadata. */ - public NisoImageMetadata getNisoImageMetadata() { - return _niso; - } - - /** Returns the value of the NOISE (37389) tag. */ - public int[] getNoise() { - return _noise; - } - - /** Returns the value of the NUMBEROFINKS (334) tag. */ - public int getNumberOfInks() { - return _numberOfInks; - } - - /** Returns the value of the OECF (34856) tag. */ - public int[] getOECF() { - return _oecf; - } - - /** Returns the value of the PAGENAME (285) tag. */ - public String getPageName() { - return _pageName; - } - - /** Returns the value of the PAGENUMBER (297) tag. */ - public int[] getPageNumber() { - return _pageNumber; - } - - /** Returns the value of the PIXELINTENSITYRANGE (34027) tag. */ - public int[] getPixelIntensityRange() { - return _pixelIntensityRange; - } - - /** Returns the value of the RASTERPADDING (34019) tag. */ - public int getRasterPadding() { - return _rasterPadding; - } - - /** Returns the value of the SECURITYCLASSIFICATION (37394) tag. */ - public String getSecurityClasssification() { - return _securityClassification; - } - - /** Returns the value of the SELFTIMERMODE (34859) tag. */ - public int getSelfTimerMode() { - return _selfTimerMode; - } - - /** Returns the value of the SHUTTERSPEEDVALUE (37377) tag. */ - public Rational getShutterSpeedValue() { - return _shutterSpeedValue; - } - - /** Returns the value of the SITE (34016) tag. */ - public String getSite() { - return _site; - } - - /** Returns the value of the SPATIALFREQUENCYRESPONSE (37388) tag. */ - public int[] getSpatialFrequencyResponse() { - return _spatialFrequencyResponse; - } - - /** Returns the value of the SPECTRALSENSITIVITY (34852) tag. */ - public String getSpectralSensitivity() { - return _spectralSensitivity; - } - - /** Returns the value of the STRIPROWCOUNTS (559) tag. */ - public long[] getStripRowCounts() { - return _stripRowCounts; - } - - /** Returns the value of the SUBIFDS (330) tag. */ - public long[] getSubIFDs() { - return _subIFDs; - } - - /** Returns the value of the SUBJECTLOCATION (37396) tag. */ - public int[] getSubjectLocation() { - return _subjectLocation; - } - - /** Returns the value of the T4OPTIONS (292) tag. */ - public long getT4Options() { - return _t4Options; - } - - /** Returns the value of the T6OPTIONS (293) tag. */ - public long getT6Options() { - return _t6Options; - } - - /** Returns the Exif IFD object, or null if none. */ - public ExifIFD getTheExifIFD() { - return _theExifIFD; - } - - /** Returns the GPS info IFD object, or null if none. */ - public GPSInfoIFD getTheGPSInfoIFD() { - return _theGPSInfoIFD; - } - - /** - * Returns the Interoperability IFD object, - * or null if none. - */ - public InteroperabilityIFD getTheInteroperabilityIFD() { - return _theInteroperabilityIFD; - } - - /** Returns the GlobalParameters IFD object, or null if none. */ - public GlobalParametersIFD getTheGlobalParametersIFD() { - return _theGlobalParametersIFD; - } - - /** Returns the value of the THRESHHOLDING (263) tag. */ - public int getThreshholding() { - return _threshholding; - } - - /** Returns the value of the TIFFEPSTANDARDID (37398) tag. */ - public String getTIFFEPStandardID() { - return _tiffEPStandardID; - } - - /** Returns the value of the TIMEZONEOFFSET (34858) tag. */ - public int[] getTimeZoneOffset() { - return _timeZoneOffset; - } - - /** Returns the value of the TRANSPARENCYINDICATOR (34028) tag. */ - public int getTransparencyIndicator() { - return _transparencyIndicator; - } - - /** Returns the value of the XCLIPPATHUNITS (344) tag. */ - public long getXClipPathUnits() { - return _xClipPathUnits; - } - - /** Returns the value of the XPOSITION (286) tag. */ - public Rational getXPosition() { - return _xPosition; - } - - /** Returns the value of the XPOSITION (287) tag. */ - public Rational getYPosition() { - return _yPosition; - } - - /** Returns the value of the DNGVERSION (50706) tag. */ - public int[] getDNGVersion() { - return _dngVersion; - } - - /** Returns the value of the DNG UNIQUECAMERAMODEL (50708) tag. */ - public String getUniqueCameraModel() { - return _uniqueCameraModel; - } - - /** Returns the value of the CFAPlaneColor (50710) tag. */ - public int[] getCFAPlaneColor() { - return _cfaPlaneColor; - } - - /** Returns the value of the AsShotNeutral (50728) tag. */ - public Rational[] getAsShotNeutral() { - return _asShotNeutral; - } - - /** Returns the value of the AsShotWhiteXY (50729) tag. */ - public Rational[] getAsShotWhiteXY() { - return _asShotWhiteXY; - } - - /** Get the IFD properties. */ - public Property getProperty(boolean rawOutput) throws TiffException { - List entries = new LinkedList(); - // This function has gotten obscenely large. Split it up. - addNisoProperties(entries, rawOutput); - addMiscProperties(entries, rawOutput); - addTiffITProperties(entries, rawOutput); - addTiffEPProperties(entries, rawOutput); - addGeoTiffProperties(entries, rawOutput); - addTiffFXProperties(entries, rawOutput); - addDNGProperties(entries, rawOutput); - return propertyHeader("TIFF", entries); - } - - private void addNisoProperties(List entries, boolean rawOutput) { - entries.add(new Property("NisoImageMetadata", - PropertyType.NISOIMAGEMETADATA, _niso)); - } - - /* Add non-NISO properties. */ - private void addMiscProperties(List entries, boolean rawOutput) { - if (_imageDescription != null) { - entries.add(new Property("ImageDescription", PropertyType.STRING, - _imageDescription)); - } - if (_dateTime != null) { - entries.add( - new Property("DateTime", PropertyType.STRING, _dateTime)); - } - if (_newSubfileType != 0L || rawOutput) { - entries.add(addBitmaskProperty("NewSubfileType", _newSubfileType, - NEWSUBFILETYPE_L, rawOutput)); - } else { - // if 0, always report as a raw number - entries.add(new Property("NewSubfileType", PropertyType.LONG, _newSubfileType)); - } - if (_subfileType != NULL && (_subfileType != 0 || rawOutput)) { - entries.add(addIntegerProperty("SubfileType", _subfileType - 1, - SUBFILETYPE_L, rawOutput)); - } else if (_subfileType != NULL) { - // if 0, always report as a raw number - entries.add(new Property("SubfileType", PropertyType.LONG, - new Long(_subfileType))); - } - if (_documentName != null) { - entries.add(new Property("DocumentName", PropertyType.STRING, - _documentName)); - } - if (_pageName != null) { - entries.add( - new Property("PageName", PropertyType.STRING, _pageName)); - } - if (_pageNumber != null) { - entries.add(new Property("PageNumber", PropertyType.INTEGER, - PropertyArity.ARRAY, _pageNumber)); - } - if (_xPosition != null) { - entries.add( - addRationalProperty("XPosition", _xPosition, rawOutput)); - } - if (_yPosition != null) { - entries.add( - addRationalProperty("YPosition", _yPosition, rawOutput)); - } - if (_copyright != null) { - entries.add( - new Property("Copyright", PropertyType.STRING, _copyright)); - } - if (_fillOrder != NULL) { - entries.add(addIntegerProperty("FillOrder", _fillOrder, FILLORDER_L, - rawOutput)); - } - entries.add(new Property("SampleFormat", PropertyType.INTEGER, - PropertyArity.ARRAY, _sampleFormat)); - if (_minSampleValue != null) { - entries.add(new Property("MinSampleValue", PropertyType.INTEGER, - PropertyArity.ARRAY, _minSampleValue)); - } - if (_maxSampleValue != null) { - entries.add(new Property("MaxSampleValue", PropertyType.INTEGER, - PropertyArity.ARRAY, _maxSampleValue)); - } - if (_inkSet != NULL) { - entries.add( - addIntegerProperty("InkSet", _inkSet, INKSET_L, rawOutput)); - } - if (_numberOfInks != NULL) { - entries.add(new Property("NumberOfInks", PropertyType.INTEGER, _numberOfInks)); - } - if (_inkNames != null) { - entries.add(new Property("InkNames", PropertyType.STRING, - PropertyArity.ARRAY, _inkNames)); - } - if (_dotRange != null) { - entries.add(new Property("DotRange", PropertyType.INTEGER, - PropertyArity.ARRAY, _dotRange)); - } - if (_targetPrinter != null) { - entries.add(new Property("TargetPrinter", PropertyType.STRING, - _targetPrinter)); - } - if (_halftoneHints != null) { - entries.add(new Property("HalftoneHints", PropertyType.INTEGER, - PropertyArity.ARRAY, _halftoneHints)); - } - if (_cellLength != NULL) { - entries.add(new Property("CellLength", PropertyType.INTEGER, _cellLength)); - } - if (_cellWidth != NULL) { - entries.add(new Property("CellWidth", PropertyType.INTEGER, _cellWidth)); - } - if (_transferFunction) { - entries.add(new Property("TransferFunction", PropertyType.BOOLEAN, - Boolean.TRUE)); - } - if (_transferRange != null) { - entries.add(new Property("TransferRange", PropertyType.INTEGER, - PropertyArity.ARRAY, _transferRange)); - } - entries.add(new Property("Threshholding", PropertyType.INTEGER, _threshholding)); - if (_predictor != NULL) { - entries.add(addIntegerProperty("Predictor", _predictor, PREDICTOR_L, - rawOutput)); - } - if (_t4Options != NULL) { - entries.add(new Property("T4Options", PropertyType.LONG, _t4Options)); - } - if (_t6Options != NULL) { - entries.add(new Property("T6Options", PropertyType.LONG, _t6Options)); - } - if (_jpegProc != NULL) { - entries.add(addIntegerProperty("JPEGProc", _jpegProc, JPEGPROC_L, - JPEGPROC_INDEX, rawOutput)); - } - if (_jpegInterchangeFormat != NULL) { - entries.add(new Property("JPEGInterchangeFormat", PropertyType.LONG, _jpegInterchangeFormat)); - } - if (_jpegInterchangeFormatLength != NULL) { - entries.add(new Property("JPEGInterchangeFormatLength", - PropertyType.LONG, _jpegInterchangeFormatLength)); - } - if (_jpegRestartInterval != NULL) { - entries.add(new Property("JPEGRestartInterval", - PropertyType.INTEGER, _jpegRestartInterval)); - } - if (_jpegLosslessPredictors != null) { - entries.add(addIntegerArrayProperty("JPEGLosslessPredictors", - _jpegLosslessPredictors, JPEGLOSSLESSPREDICTORS_L, - rawOutput)); - } - if (_jpegPointTransforms != null) { - entries.add( - new Property("JPEGPointTransforms", PropertyType.INTEGER, - PropertyArity.ARRAY, _jpegPointTransforms)); - } - if (_jpegQTables != null) { - entries.add(new Property("JPEGQTables", PropertyType.LONG, - PropertyArity.ARRAY, _jpegQTables)); - } - if (_jpegDCTables != null) { - entries.add(new Property("JPEGDCTables", PropertyType.LONG, - PropertyArity.ARRAY, _jpegDCTables)); - } - if (_jpegACTables != null) { - entries.add(new Property("JPEGACTables", PropertyType.LONG, - PropertyArity.ARRAY, _jpegACTables)); - } - if (_jpegTables != null) { - entries.add(new Property("JPEGTables", PropertyType.INTEGER, - PropertyArity.ARRAY, _jpegTables)); - } - if (_imageSourceData != null) { - entries.add(new Property("ImageSourceData", PropertyType.INTEGER, - PropertyArity.ARRAY, _imageSourceData)); - } - if (_photoshopProperties != null) { - entries.add( - new Property("PhotoshopProperties", PropertyType.INTEGER, - PropertyArity.ARRAY, _photoshopProperties)); - } - if (_annotations != null) { - entries.add(new Property("Annotations", PropertyType.INTEGER, - PropertyArity.ARRAY, _annotations)); - } - if (_clipPath != null) { - entries.add(new Property("ClipPath", PropertyType.INTEGER, - PropertyArity.ARRAY, _clipPath)); - } - if (_xClipPathUnits != NULL) { - entries.add(new Property("XClipPathUnits", PropertyType.LONG, _xClipPathUnits)); - } - if (_yClipPathUnits != NULL) { - entries.add(new Property("YClipPathUnits", PropertyType.LONG, _yClipPathUnits)); - } - if (_cleanFaxData != NULL) { - entries.add(new Property("CleanFaxData", PropertyType.LONG, - new Long(_cleanFaxData))); - } - if (_badFaxLines != NULL) { - entries.add(new Property("BadFaxLines", PropertyType.LONG, _badFaxLines)); - } - if (_consecutiveBadFaxLines != NULL) { - entries.add(new Property("ConsecutiveBadFaxLines", - PropertyType.LONG, _consecutiveBadFaxLines)); - } - if (_freeByteCounts != null) { - entries.add(new Property("FreeByteCounts", PropertyType.LONG, - PropertyArity.ARRAY, _freeByteCounts)); - } - if (_freeOffsets != null) { - entries.add(new Property("FreeOffsets", PropertyType.LONG, - PropertyArity.ARRAY, _freeOffsets)); - } - } - - private void addTiffITProperties(List entries, - boolean rawOutput) { - /* Add TIFF/IT properties. */ - - List itList = new LinkedList(); - if (_site != null) { - itList.add(new Property("Site", PropertyType.STRING, _site)); - } - if (_colorTable != null) { - itList.add(new Property("ColorTable", PropertyType.INTEGER, - PropertyArity.ARRAY, _colorTable)); - } - itList.add(addIntegerProperty("BackgroundColorIndicator", - _backgroundColorIndicator, BACKGROUNDCOLORINDICATOR_L, - rawOutput)); - if (_backgroundColorValue != NULL) { - itList.add(new Property("BackgroundColorValue", - PropertyType.INTEGER, _backgroundColorValue)); - } - itList.add(addIntegerProperty("ImageColorIndicator", - _imageColorIndicator, IMAGECOLORINDICATOR_L, rawOutput)); - itList.add(addIntegerProperty("TransparencyIndicator", - _transparencyIndicator, TRANSPARENCYINDICATOR_L, rawOutput)); - if (_imageColorValue != NULL) { - itList.add(new Property("ImageColorValue", PropertyType.INTEGER, _imageColorValue)); - } - if (_colorCharacterization != null) { - itList.add(new Property("ColorCharacterization", - PropertyType.STRING, _colorCharacterization)); - } - if (_colorSequence != null) { - itList.add(new Property("ColorSequence", PropertyType.STRING, - _colorSequence)); - } - if (_hcUsage != NULL) { - itList.add(addBitmaskProperty("HCUsage", _hcUsage, HCUSAGE_L, - rawOutput)); - } - if (_it8Header != null) { - itList.add( - new Property("IT8Header", PropertyType.STRING, _it8Header)); - } - if (_pixelIntensityRange != null) { - itList.add(new Property("PixelIntensityRange", PropertyType.INTEGER, - PropertyArity.ARRAY, _pixelIntensityRange)); - } - itList.add(addIntegerProperty("RasterPadding", _rasterPadding, - RASTERPADDING_L, rawOutput)); - itList.add(new Property("BitsPerRunLength", PropertyType.INTEGER, _bitsPerRunLength)); - itList.add(new Property("BitsPerExtendedRunLength", - PropertyType.INTEGER, _bitsPerExtendedRunLength)); - entries.add(new Property("TIFFITProperties", PropertyType.PROPERTY, - PropertyArity.LIST, itList)); - } - - /* Add TIFF/EP properties. */ - private void addTiffEPProperties(List entries, - boolean rawOutput) { - List epList = new LinkedList(); - if (_cfaRepeatPatternDim != null) { - epList.add(new Property("CFARepeatPatternDim", PropertyType.INTEGER, - PropertyArity.ARRAY, _cfaRepeatPatternDim)); - } - if (_cfaPattern != null) { - epList.add(new Property("CFAPattern", PropertyType.INTEGER, - PropertyArity.ARRAY, _cfaPattern)); - } - if (_batteryLevel != null) { - epList.add(new Property("BatteryLevel", PropertyType.STRING, - _batteryLevel)); - } - if (_iptc != null) { - epList.add(new Property("IPTCNAA", PropertyType.LONG, - PropertyArity.ARRAY, _iptc)); - } - if (_iccProfile != null) { - epList.add(new Property("ICCProfile", PropertyType.BOOLEAN, - Boolean.TRUE)); - } - if (_exposureProgram != NULL) { - epList.add(addIntegerProperty("ExposureProgram", _exposureProgram, - EXPOSUREPROGRAM_L, rawOutput)); - } - if (_spectralSensitivity != null) { - epList.add(new Property("SpectralSensitivity", PropertyType.STRING, - _spectralSensitivity)); - } - if (_isoSpeedRatings != null) { - epList.add(new Property("ISOSpeedRatings", PropertyType.INTEGER, - PropertyArity.ARRAY, _isoSpeedRatings)); - } - if (_oecf != null) { - epList.add(new Property("OECF", PropertyType.INTEGER, - PropertyArity.ARRAY, _oecf)); - } - if (_interlace != NULL) { - epList.add(new Property("Interlace", PropertyType.INTEGER, _interlace)); - } - if (_timeZoneOffset != null) { - epList.add(new Property("TimeZoneOffset", PropertyType.INTEGER, - PropertyArity.ARRAY, _timeZoneOffset)); - } - if (_selfTimerMode != NULL) { - epList.add(new Property("SelfTimerMode", PropertyType.INTEGER, _selfTimerMode)); - } - if (_compressedBitsPerPixel != null) { - epList.add(addRationalProperty("CompressedBitsPerPixel", - _compressedBitsPerPixel, rawOutput)); - } - if (_shutterSpeedValue != null) { - epList.add(addRationalProperty("ShutterSpeedValue", - _shutterSpeedValue, rawOutput)); - } - if (_aperatureValue != null) { - epList.add(addRationalProperty("AperatureValue", _aperatureValue, - rawOutput)); - } - if (_maxAperatureValue != null) { - epList.add(addRationalProperty("MaxAperatureValue", - _maxAperatureValue, rawOutput)); - } - if (_flash != NULL) { - epList.add(addIntegerProperty("FLASH", _flash, FLASH_L, FLASH_INDEX, - rawOutput)); - } - if (_spatialFrequencyResponse != null) { - epList.add(new Property("SpatialFrequencyResponse", - PropertyType.INTEGER, PropertyArity.ARRAY, - _spatialFrequencyResponse)); - } - if (_noise != null) { - epList.add(new Property("Noise", PropertyType.INTEGER, - PropertyArity.ARRAY, _noise)); - } - if (_focalPlaneXResolution != null) { - epList.add(addRationalProperty("FocalPlaneXResolution", - _focalPlaneXResolution, rawOutput)); - } - if (_focalPlaneYResolution != null) { - epList.add(addRationalProperty("FocalPlaneYResolution", - _focalPlaneYResolution, rawOutput)); - } - if (_focalPlaneResolutionUnit != NULL) { - epList.add(addIntegerProperty("FocalPlaneResolutionUnit", - _focalPlaneResolutionUnit, FOCALPLANERESOLUTIONUNIT_L, - rawOutput)); - } - if (_imageNumber != NULL) { - epList.add(new Property("ImageNumber", PropertyType.LONG, _imageNumber)); - } - if (_securityClassification != null) { - epList.add(new Property("SecurityClassification", - PropertyType.STRING, _securityClassification)); - } - if (_imageHistory != null) { - epList.add(new Property("ImageHistory", PropertyType.STRING, - _imageHistory)); - } - if (_subjectLocation != null) { - epList.add(new Property("SubjectLocation", PropertyType.INTEGER, - PropertyArity.ARRAY, _subjectLocation)); - } - if (_tiffEPStandardID != null) { - epList.add(new Property("TIFFEPSStandardID", PropertyType.STRING, - _tiffEPStandardID)); - } - if (epList.size() > 0) { - entries.add(new Property("TIFFEPProperties", PropertyType.PROPERTY, - PropertyArity.LIST, epList)); - } - - if (_xmpProp != null) { - entries.add(_xmpProp); - } - } - - private void addGeoTiffProperties(List entries, boolean rawOutput) - throws TiffException { - /* Add GeoTIFF properties. */ - - List dirList = new LinkedList(); - if (_geoKeyDirectoryTag != null) { - dirList.add(new Property("Version", PropertyType.INTEGER, _geoKeyDirectoryTag[0])); - dirList.add(new Property("Revision", PropertyType.STRING, - Integer.toString(_geoKeyDirectoryTag[1]) + "." - + Integer.toString(_geoKeyDirectoryTag[2]))); - dirList.add(new Property("NumberOfKeys", PropertyType.INTEGER, _geoKeyDirectoryTag[3])); - for (int i = 0; i < _geoKeyDirectoryTag[3]; i++) { - int j = i * 4 + 4; - int key = _geoKeyDirectoryTag[j]; - int location = _geoKeyDirectoryTag[j + 1]; - int count = _geoKeyDirectoryTag[j + 2]; - int offset = _geoKeyDirectoryTag[j + 3]; - - int ival = 0; - double dval = 0.0; - String sval = "NULL"; - switch (location) { - case 0: - ival = offset; - break; - case 34736: - dval = _geoDoubleParamsTag[offset]; - break; - case 34737: - try { - sval = _geoAsciiParamsTag.substring(offset, - offset + count - 1); - } catch (Exception e) { - throw new TiffException(MessageConstants.TIFF_HUL_9); - } - break; - default: - break; - } - - switch (key) { - case GTMODELTYPEGEOKEY: - dirList.add(addIntegerProperty("GTModelType", ival, - GeoTiffStrings.MODELTYPE, - GeoTiffStrings.MODELTYPE_INDEX, rawOutput)); - break; - case GTRASTERTYPEGEOKEY: - dirList.add(addIntegerProperty("GTRasterType", ival, - GeoTiffStrings.RASTERTYPE, - GeoTiffStrings.RASTERTYPE_INDEX, rawOutput)); - break; - case GTCITATIONGEOKEY: - dirList.add(new Property("GTCitation", PropertyType.STRING, - sval)); - break; - case GEOGRAPHICTYPEGEOKEY: - dirList.add(addIntegerProperty("GeographicType", ival, - GeoTiffStrings.GEOGRAPHICS, - GeoTiffStrings.GEOGRAPHICS_INDEX, rawOutput)); - break; - case GEOGCITATIONGEOKEY: - dirList.add(new Property("GeogCitation", - PropertyType.STRING, sval)); - break; - case GEOGGEODETICDATUMGEOKEY: - dirList.add(addIntegerProperty("GeogGeodeticDatum", ival, - GeoTiffStrings.GEODETICDATUM, - GeoTiffStrings.GEODETICDATUM_INDEX, rawOutput)); - break; - case GEOGPRIMEMERIDIANGEOKEY: - dirList.add(addIntegerProperty("GeogPrimeMeridian", ival, - GeoTiffStrings.PRIMEMERIDIAN, - GeoTiffStrings.PRIMEMERIDIAN_INDEX, rawOutput)); - break; - case GEOGPRIMEMERIDIANLONGGEOKEY: - dirList.add(new Property("GeogPrimeMeridianLong", - PropertyType.DOUBLE, dval)); - break; - case GEOGLINEARUNITSGEOKEY: - dirList.add(addIntegerProperty("GeogLinearUnits", ival, - GeoTiffStrings.LINEARUNITS, - GeoTiffStrings.LINEARUNITS_INDEX, rawOutput)); - break; - case GEOGLINEARUNITSIZEGEOKEY: - dirList.add(new Property("GeogLinearUnitSize", - PropertyType.DOUBLE, dval)); - break; - case GEOGANGULARUNITSGEOKEY: - dirList.add(addIntegerProperty("GeogAngularUnits", ival, - GeoTiffStrings.ANGULARUNITS, - GeoTiffStrings.ANGULARUNITS_INDEX, rawOutput)); - break; - case GEOGANGULARUNITSIZEGEOKEY: - dirList.add(new Property("GeogAngularUnitSize", - PropertyType.DOUBLE, dval)); - break; - case GEOGELLIPSOIDGEOKEY: - dirList.add(addIntegerProperty("GeogEllipsoid", ival, - GeoTiffStrings.ELLIPSOID, - GeoTiffStrings.ELLIPSOID_INDEX, rawOutput)); - break; - case GEOGSEMIMAJORAXISGEOKEY: - dirList.add(new Property("GeogSemiMajorAxis", - PropertyType.DOUBLE, dval)); - break; - case GEOGSEMIMINORAXISGEOKEY: - dirList.add(new Property("GeogSemiMinorAxis", - PropertyType.DOUBLE, dval)); - break; - case GEOGINVFLATTENINGGEOKEY: - dirList.add(new Property("GeogInvFlattening", - PropertyType.DOUBLE, dval)); - break; - case GEOGAZIMUTHUNITSGEOKEY: - dirList.add(addIntegerProperty("GeogAzimuthUnits", ival, - GeoTiffStrings.ANGULARUNITS, - GeoTiffStrings.ANGULARUNITS_INDEX, rawOutput)); - break; - case PROJECTEDCSTYPEGEOKEY: - dirList.add(addIntegerProperty("ProjectedCSType", ival, - GeoTiffStrings.PROJECTEDCSTYPE, - GeoTiffStrings.PROJECTEDCSTYPE_INDEX, rawOutput)); - break; - case PCSCITATIONGEOKEY: - dirList.add(new Property("PCSCitation", PropertyType.STRING, - sval)); - break; - case PROJECTIONGEOKEY: - dirList.add(addIntegerProperty("Projection", ival, - GeoTiffStrings.PROJECTION, - GeoTiffStrings.PROJECTION_INDEX, rawOutput)); - break; - case PROJCOORDTRANSGEOKEY: - dirList.add(addIntegerProperty("ProjCoordTrans", ival, - GeoTiffStrings.COORDINATETRANSFORMATION, - GeoTiffStrings.COORDINATETRANSFORMATION_INDEX, - rawOutput)); - break; - case PROJLINEARUNITSGEOKEY: - dirList.add(addIntegerProperty("ProjLinearUnits", ival, - GeoTiffStrings.LINEARUNITS, - GeoTiffStrings.LINEARUNITS_INDEX, rawOutput)); - break; - case PROJLINEARUNITSIZEGEOKEY: - dirList.add(new Property("ProjLinearUnitSize", - PropertyType.DOUBLE, dval)); - break; - case PROJSTDPARALLEL1GEOKEY: - dirList.add(new Property("ProjStdParallel1", - PropertyType.DOUBLE, dval)); - break; - case PROJSTDPARALLEL2GEOKEY: - dirList.add(new Property("ProjStdParallel2", - PropertyType.DOUBLE, dval)); - break; - case PROJNATORIGINLONGGEOKEY: - dirList.add(new Property("ProjNatOriginLong", - PropertyType.DOUBLE, dval)); - break; - case PROJNATORIGINLATGEOKEY: - dirList.add(new Property("ProjNatOriginLat", - PropertyType.DOUBLE, dval)); - break; - case PROJFALSEEASTINGGEOKEY: - dirList.add(new Property("ProjFalseEasting", - PropertyType.DOUBLE, dval)); - break; - case PROJFALSENORTHINGGEOKEY: - dirList.add(new Property("ProjFalseNorthing", - PropertyType.DOUBLE, dval)); - break; - case PROJFALSEORIGINLONGGEOKEY: - dirList.add(new Property("ProjFalseOriginLong", - PropertyType.DOUBLE, dval)); - break; - case PROJFALSEORIGINLATGEOKEY: - dirList.add(new Property("ProjFalseOriginLat", - PropertyType.DOUBLE, dval)); - break; - case PROJFALSEORIGINEASTINGGEOKEY: - dirList.add(new Property("ProjFalseOriginEasting", - PropertyType.DOUBLE, dval)); - break; - case PROJFALSEORIGINNORTHINGGEOKEY: - case PROJFALSEORIGINNORTHINGGEOKEY_2: - dirList.add(new Property("ProjFalseOriginNorthing", - PropertyType.DOUBLE, dval)); - break; - case PROJCENTERLONGGEOKEY: - dirList.add(new Property("ProjCenterLong", - PropertyType.DOUBLE, dval)); - break; - case PROJCENTERLATGEOKEY: - dirList.add(new Property("ProjCenterLat", - PropertyType.DOUBLE, dval)); - break; - case PROJCENTEREASTINGGEOKEY: - dirList.add(new Property("ProjCenterEasting", - PropertyType.DOUBLE, dval)); - break; - case PROJSCALEATNATORIGINGEOKEY: - dirList.add(new Property("ProjScaleAtNatOrigin", - PropertyType.DOUBLE, dval)); - break; - case PROJSCALEATCENTERGEOKEY: - dirList.add(new Property("ProjScaleAtCenter", - PropertyType.DOUBLE, dval)); - break; - case PROJAZIMUTHANGLEGEOKEY: - dirList.add(new Property("ProjAzimuthAngle", - PropertyType.DOUBLE, dval)); - break; - case PROJSTRAIGHTVERTPOLELONGEOKEY: - dirList.add(new Property("ProjStraightVertPoleLong", - PropertyType.DOUBLE, dval)); - break; - case VERTICALCSTYPEGEOKEY: - dirList.add(addIntegerProperty("VerticalCSType", ival, - GeoTiffStrings.VERTICALCSTYPE, - GeoTiffStrings.VERTICALCSTYPE_INDEX, rawOutput)); - break; - case VERTICALCITATIONGEOKEY: - dirList.add(new Property("VerticalCitation", - PropertyType.STRING, sval)); - break; - case VERTICALDATUMGEOKEY: - dirList.add(addIntegerProperty("VerticalDatum", ival, - GeoTiffStrings.VERTICALCSDATUM, - GeoTiffStrings.VERTICALCSDATUM_INDEX, rawOutput)); - break; - case VERTICALUNITSGEOKEY: - dirList.add(addIntegerProperty("VerticalUnits", ival, - GeoTiffStrings.LINEARUNITS, - GeoTiffStrings.LINEARUNITS_INDEX, rawOutput)); - break; - default: - break; - } - } - } - List geoList = new LinkedList(); - if (dirList.size() > 0) { - geoList.add(new Property("GeoKeyDirectory", PropertyType.PROPERTY, - PropertyArity.LIST, dirList)); - } - - if (_modelTiepointTag != null) { - geoList.add(new Property("ModelTiepointTag", PropertyType.DOUBLE, - PropertyArity.ARRAY, _modelTiepointTag)); - } - if (_modelPixelScaleTag != null) { - geoList.add(new Property("ModelPixelScaleTag", PropertyType.DOUBLE, - PropertyArity.ARRAY, _modelPixelScaleTag)); - } - if (_modelTransformationTag != null) { - geoList.add( - new Property("ModelTransformationTag", PropertyType.DOUBLE, - PropertyArity.ARRAY, _modelTransformationTag)); - } - - if (geoList.size() > 0) { - entries.add(new Property("GeoTIFFProperties", PropertyType.PROPERTY, - PropertyArity.LIST, geoList)); - } - } - - /* Add Tiff/FX properties */ - private void addTiffFXProperties(List entries, - boolean rawOutput) { - if (_stripRowCounts != null) { - entries.add(new Property("StripRowCounts", PropertyType.LONG, - PropertyArity.ARRAY, _stripRowCounts)); - } - if (_imageLayer != null) { - // Do up ImageLayer as a property with two subproperties. - Property[] layerProps = new Property[2]; - try { - layerProps[0] = addIntegerProperty("LayerType", _imageLayer[0], - IMAGELAYER_L, rawOutput); - layerProps[1] = new Property("OrdinalNumber", - PropertyType.INTEGER, _imageLayer[1]); - - entries.add(new Property("ImageLayer", PropertyType.PROPERTY, - PropertyArity.ARRAY, layerProps)); - } - // Don't blow up on incorrect array size - catch (Exception e) { - } - } - } - - /* Adds DNG properties. */ - private void addDNGProperties(List entries, boolean rawOutput) { - setDNGDefaults(); - List dngList = new LinkedList(); - if (_dngVersion != null) { - dngList.add(new Property("DNGVersion", PropertyType.INTEGER, - PropertyArity.ARRAY, _dngVersion)); - } - if (_dngBackwardVersion != null) { - dngList.add(new Property("DNGBackwardVersion", PropertyType.INTEGER, - PropertyArity.ARRAY, _dngBackwardVersion)); - } - if (_uniqueCameraModel != null) { - dngList.add(new Property("UniqueCameraModel", PropertyType.STRING, - _uniqueCameraModel)); - } - if (_localizedCameraModel != null) { - dngList.add(new Property("LocalizedCameraModel", - PropertyType.STRING, _localizedCameraModel)); - } - if (_cfaPlaneColor != null) { - dngList.add(new Property("CFAPlaneColor", PropertyType.INTEGER, - PropertyArity.ARRAY, _cfaPlaneColor)); - } - if (_cfaLayout != NULL) { - dngList.add(addIntegerProperty("CFALayout", _cfaLayout, CFALAYOUT_L, - rawOutput)); - } - if (_linearizationTable != null) { - dngList.add(new Property("LinearizationTable", PropertyType.INTEGER, - PropertyArity.ARRAY, _linearizationTable)); - } - if (_blackLevelRepeatDim != null) { - dngList.add( - new Property("BlackLevelRepeatDim", PropertyType.INTEGER, - PropertyArity.ARRAY, _blackLevelRepeatDim)); - } - if (_blackLevel != null) { - dngList.add(new Property("BlackLevel", PropertyType.RATIONAL, - PropertyArity.ARRAY, _blackLevel)); - } - if (_blackLevelDeltaH != null) { - dngList.add(new Property("BlackLevelDeltaH", PropertyType.RATIONAL, - PropertyArity.ARRAY, _blackLevelDeltaH)); - } - if (_blackLevelDeltaV != null) { - dngList.add(new Property("BlackLevelDeltaV", PropertyType.RATIONAL, - PropertyArity.ARRAY, _blackLevelDeltaV)); - } - if (_whiteLevel != null) { - dngList.add(new Property("WhiteLevel", PropertyType.LONG, - PropertyArity.ARRAY, _whiteLevel)); - } - if (_defaultScale != null) { - dngList.add(new Property("DefaultScale", PropertyType.RATIONAL, - PropertyArity.ARRAY, _defaultScale)); - } - if (_bestQualityScale != null) { - dngList.add(new Property("BestQualityScale", PropertyType.RATIONAL, - _bestQualityScale)); - } - if (_defaultCropOrigin != null) { - dngList.add(new Property("DefaultCropOrigin", PropertyType.RATIONAL, - PropertyArity.ARRAY, _defaultCropOrigin)); - } - if (_defaultCropSize != null) { - dngList.add(new Property("DefaultCropSize", PropertyType.RATIONAL, - PropertyArity.ARRAY, _defaultCropSize)); - } - if (_calibrationIlluminant1 != NULL) { - dngList.add( - new Property("CalibrationIlluminant1", PropertyType.INTEGER, _calibrationIlluminant1)); - } - if (_calibrationIlluminant2 != NULL) { - dngList.add( - new Property("CalibrationIlluminant2", PropertyType.INTEGER, _calibrationIlluminant2)); - } - if (_colorMatrix1 != null) { - dngList.add(new Property("ColorMatrix1", PropertyType.RATIONAL, - PropertyArity.ARRAY, _colorMatrix1)); - } - if (_colorMatrix2 != null) { - dngList.add(new Property("ColorMatrix2", PropertyType.RATIONAL, - PropertyArity.ARRAY, _colorMatrix2)); - } - if (_cameraCalibration1 != null) { - dngList.add( - new Property("CameraCalibration1", PropertyType.RATIONAL, - PropertyArity.ARRAY, _cameraCalibration1)); - } - if (_cameraCalibration2 != null) { - dngList.add( - new Property("CameraCalibration2", PropertyType.RATIONAL, - PropertyArity.ARRAY, _cameraCalibration2)); - } - if (_reductionMatrix1 != null) { - dngList.add(new Property("ReductionMatrix1", PropertyType.RATIONAL, - PropertyArity.ARRAY, _reductionMatrix1)); - } - if (_reductionMatrix2 != null) { - dngList.add(new Property("ReductionMatrix2", PropertyType.RATIONAL, - PropertyArity.ARRAY, _reductionMatrix2)); - } - if (_analogBalance != null) { - dngList.add(new Property("AnalogBalance", PropertyType.RATIONAL, - PropertyArity.ARRAY, _analogBalance)); - } - if (_asShotNeutral != null) { - dngList.add(new Property("AsShotNeutral", PropertyType.RATIONAL, - PropertyArity.ARRAY, _asShotNeutral)); - } - if (_asShotWhiteXY != null) { - dngList.add(new Property("AsShotWhiteXY", PropertyType.RATIONAL, - PropertyArity.ARRAY, _asShotWhiteXY)); - } - if (_baselineExposure != null) { - dngList.add(new Property("BaselineExposure", PropertyType.RATIONAL, - _baselineExposure)); - } - if (_baselineNoise != null) { - dngList.add(new Property("BaselineNoise", PropertyType.RATIONAL, - _baselineNoise)); - } - if (_baselineNoise != null) { - dngList.add(new Property("BaselineSharpness", PropertyType.RATIONAL, - _baselineSharpness)); - } - if (_bayerGreenSplit != NULL) { - dngList.add(new Property("BayerGreenSplit", PropertyType.INTEGER, _bayerGreenSplit)); - } - if (_linearResponseLimit != null) { - dngList.add(new Property("LinearResponseLimit", - PropertyType.RATIONAL, _linearResponseLimit)); - } - if (_cameraSerialNumber != null) { - dngList.add(new Property("CameraSerialNumber", PropertyType.STRING, - _cameraSerialNumber)); - } - if (_lensInfo != null) { - dngList.add(new Property("LensInfo", PropertyType.RATIONAL, - PropertyArity.ARRAY, _lensInfo)); - } - if (_chromaBlurRadius != null) { - dngList.add(new Property("ChromaBlurRadius", PropertyType.RATIONAL, - _chromaBlurRadius)); - } - if (_antiAliasStrength != null) { - dngList.add(new Property("AntiAliasStrength", PropertyType.RATIONAL, - _antiAliasStrength)); - } - if (_dngPrivateData != null) { - dngList.add(new Property("DNGPrivateData", PropertyType.INTEGER, - _dngPrivateData)); - } - if (_makerNoteSafety != NULL) { - dngList.add(addIntegerProperty("MakerNoteSafety", _makerNoteSafety, - MAKERNOTESAFETY_L, rawOutput)); - } - if (dngList.size() > 0) { - entries.add(new Property("DNGProperties", PropertyType.PROPERTY, - PropertyArity.LIST, dngList)); - } - - } - - /** - * ColorPlanes is an undefined primary in the TIFF spec. It's - * actually a non-obvious definition which is something of a - * pain to calculate. - */ - private int calcColorPlanes() { - if (_photometricInterpretation == TiffProfileDNG.CFA) { - // In this case, it's the number of unique colors - // in the CFA pattern. (You mean you didn't already - // know that?) - int nUnique = 0; - if (_cfaPattern == null) { - // It's broken; return 1 so we don't try to - // allocate zero-length objects. - return 1; - } - int[] uniqueColors = new int[_cfaPattern.length]; - for (int i = 0; i < _cfaPattern.length; i++) { - boolean unique = true; - int color = _cfaPattern[i]; - for (int j = 0; j < nUnique; j++) { - if (color == uniqueColors[j]) { - unique = false; - break; - } - } - if (unique) { - uniqueColors[nUnique++] = color; - } - - } - return nUnique; - } - return _niso.getSamplesPerPixel(); - } - - /** - * Set the default values for any DNG tags that haven't been - * encountered yet. If _dngVersion is zero, apply the IFD 0 - * defaults; if PhotometricInterpretation is CFA or RawLinear, - * apply the Raw IFD defaults. - */ - private void setDNGDefaults() { - if (_dngVersion != null) { - // Apply "IFD 0" defaults - if (_dngBackwardVersion == null) { - _dngBackwardVersion = new int[4]; - // The default value is _dngVersion with the last two - // bytes set to zero. - _dngBackwardVersion[0] = _dngVersion[0]; - _dngBackwardVersion[1] = _dngVersion[1]; - _dngBackwardVersion[2] = 0; - _dngBackwardVersion[3] = 0; - } - - if (_uniqueCameraModel != null && _localizedCameraModel == null) { - _localizedCameraModel = _uniqueCameraModel; - } - - if (_calibrationIlluminant1 == NULL) { - _calibrationIlluminant1 = 0; - } - if (_baselineExposure == null) { - _baselineExposure = new Rational(0, 1); - } - if (_baselineNoise == null) { - _baselineNoise = new Rational(1, 1); - } - if (_baselineSharpness == null) { - _baselineSharpness = new Rational(1, 1); - } - if (_linearResponseLimit == null) { - _linearResponseLimit = new Rational(1, 1); - } - if (_makerNoteSafety == NULL) { - _makerNoteSafety = 0; - } - // There are some IFD 0 defaults which depend on the value of - // "ColorPlanes," which is derived from information in the Raw IFD. - // This would require processing the IFD's out of order, so those - // defaults (AnalogBalance) go unreported. - } - - if (_photometricInterpretation == TiffProfileDNG.CFA - || _photometricInterpretation == TiffProfileDNG.LINEAR_RAW) { - // Apply "Raw IFD" defaults. This really isn't sufficient - // information to establish a file as DNG, but the properties - // are in their own category, so it's fairly harmless to leave - // them in even if it's actually, for example, a TIFF-EP file. - - if (_cfaPlaneColor == null) { - _cfaPlaneColor = new int[] { 0, 1, 2 }; - } - - if (_cfaLayout == NULL) { - _cfaLayout = 1; - } - - // The size of the LinearizationTable is -- I quote -- N. - // This is NOT useful. Skip that default. - - if (_blackLevelRepeatDim == null) { - _blackLevelRepeatDim = new int[] { 1, 1 }; - } - - Rational zero = new Rational(0, 1); - if (_blackLevel == null) { - _blackLevel = new Rational[_blackLevelRepeatDim[0] - * _blackLevelRepeatDim[1] * _niso.getSamplesPerPixel()]; - for (int i = 0; i < _blackLevel.length; i++) { - _blackLevel[i] = zero; - } - } - - if (_blackLevelDeltaH == null) { - _blackLevelDeltaH = new Rational[(int) _niso.getImageWidth()]; - for (int i = 0; i < _blackLevelDeltaH.length; i++) { - _blackLevelDeltaH[i] = zero; - } - } - - if (_blackLevelDeltaV == null) { - _blackLevelDeltaV = new Rational[(int) _niso.getImageLength()]; - for (int i = 0; i < _blackLevelDeltaV.length; i++) { - _blackLevelDeltaV[i] = zero; - } - } - - if (_whiteLevel == null) { - _whiteLevel = new long[_niso.getSamplesPerPixel()]; - long defWhite = (1L << _niso.getBitsPerSample()[0] - 1); - for (int i = 0; i < _whiteLevel.length; i++) { - _whiteLevel[i] = defWhite; - } - } - - Rational one = new Rational(1, 1); - if (_defaultScale == null) { - _defaultScale = new Rational[] { one, one }; - } - - if (_bestQualityScale == null) { - _bestQualityScale = one; - } - - if (_defaultCropOrigin == null) { - _defaultCropOrigin = new Rational[] { zero, zero }; - } - - if (_defaultCropSize == null) { - _defaultCropSize = new Rational[2]; - _defaultCropSize[0] = new Rational(_niso.getImageWidth(), 1); - _defaultCropSize[1] = new Rational(_niso.getImageLength(), 1); - } - int colorPlanes = calcColorPlanes(); - if (_cameraCalibration1 == null) { - // Identity matrix with dimension of ColorPlanes*ColorPlanes - _cameraCalibration1 = identityMatrix(colorPlanes); - } - if (_cameraCalibration2 == null) { - // Identity matrix with dimension of ColorPlanes*ColorPlanes - _cameraCalibration2 = identityMatrix(colorPlanes); - } - if (_bayerGreenSplit == NULL - && _photometricInterpretation == TiffProfileDNG.CFA) { - _bayerGreenSplit = 0; - } - if (_antiAliasStrength == null) { - _antiAliasStrength = new Rational(1, 1); - } - } - } - - /** Create a Rational identity matrix of the specified size. */ - private Rational[] identityMatrix(int dim) { - Rational[] val = new Rational[dim * dim]; - // Set them all to zero, then overwrite the diagonal values - // to one. - int i; - for (i = 0; i < dim * dim; i++) { - val[i] = new Rational(0, 1); - } - for (i = 0; i < dim; i++) { - val[dim * i + i] = new Rational(1, 1); - } - return val; - } - - /** Looks up an IFD tag. */ - public void lookupTag(int tag, int type, long count, long value) - throws TiffException { - try { - switch (tag) { - case APERTUREVALUE: - checkType(tag, type, RATIONAL); - checkCount(tag, count, 1); - _aperatureValue = readRational(count, value); - break; - case ARTIST: - checkType(tag, type, ASCII); - _niso.setImageProducer(readASCII(count, value)); - if (_version < 5) { - _version = 5; - } - break; - case BACKGROUNDCOLORINDICATOR: - checkType(tag, type, BYTE); - checkCount(tag, count, 1); - _backgroundColorIndicator = readByte(type, count, value); - break; - case BACKGROUNDCOLORVALUE: - checkType(tag, type, BYTE); - checkCount(tag, count, 1); - _backgroundColorValue = readByte(type, count, value); - break; - case BADFAXLINES: - checkType(tag, type, SHORT, LONG); - checkCount(tag, count, 1); - _badFaxLines = readLong(type, count, value); - break; - case BATTERYLEVEL: - checkType(tag, type, RATIONAL, ASCII); - if (type == RATIONAL) { - Rational r = readRational(count, value); - _batteryLevel = Double.toString(r.toDouble()); - } else { - _batteryLevel = readASCII(count, value); - } - break; - case BITSPEREXTENDEDRUNLENGTH: - checkType(tag, type, SHORT); - checkCount(tag, count, 1); - _bitsPerExtendedRunLength = readShort(type, count, value); - break; - case BITSPERRUNLENGTH: - checkType(tag, type, SHORT); - checkCount(tag, count, 1); - _bitsPerRunLength = readShort(type, count, value); - break; - case BITSPERSAMPLE: - checkType(tag, type, SHORT); - checkCountArray(tag, count); - _niso.setBitsPerSample(readShortArray(type, count, value)); - break; - case BRIGHTNESSVALUE: - checkType(tag, type, SRATIONAL); - if (count == 1) { - _niso.setBrightness( - readSignedRational(count, value)); - } else { - checkCountArray(tag, count); - Rational[] r = readSignedRationalArray(count, value); - _niso.setBrightness(average(r[0], r[1])); - } - break; - case CELLLENGTH: - checkType(tag, type, SHORT); - _cellLength = readShort(type, count, value); - break; - case CELLWIDTH: - checkType(tag, type, SHORT); - _cellWidth = readShort(type, count, value); - break; - case CFAPATTERN: - checkType(tag, type, BYTE); - checkCountArray(tag, count); - _cfaPattern = readByteArray(type, count, value); - break; - case CFAREPEATPATTERNDIM: - checkType(tag, type, SHORT); - checkCount(tag, count, 2); - _cfaRepeatPatternDim = readShortArray(type, count, value); - break; - case CLEANFAXDATA: - checkType(tag, type, SHORT); - checkCount(tag, count, 1); - _badFaxLines = readShort(type, count, value); - break; - case CLIPPATH: - checkType(tag, type, BYTE); - checkCountArray(tag, count); - _clipPath = readByteArray(type, count, value); - break; - case COLORCHARACTERIZATION: - checkType(tag, type, ASCII); - _colorCharacterization = readASCII(count, value); - break; - case COLORSEQUENCE: - checkType(tag, type, ASCII); - _colorSequence = readASCII(count, value); - break; - case COLORMAP: - { - checkType(tag, type, SHORT); - checkCountArray(tag, count); - int[] colorMap = readShortArray(type, count, value); - int[] bitCode = new int[colorMap.length]; - int[] red = new int[colorMap.length]; - int[] green = new int[colorMap.length]; - int[] blue = new int[colorMap.length]; - int len = colorMap.length / 3; - int len2 = 2 * len; - for (int i = 0; i < len; i++) { - bitCode[i] = i; - red[i] = colorMap[i]; - green[i] = colorMap[i + len]; - blue[i] = colorMap[i + len2]; - } - _niso.setColormapBitCodeValue(bitCode); - _niso.setColormapRedValue(red); - _niso.setColormapGreenValue(green); - _niso.setColormapBlueValue(blue); - if (_version < 5) { - _version = 5; - } - break; - } - case COLORTABLE: - checkType(tag, type, BYTE); - checkCountArray(tag, count); - _colorTable = readByteArray(type, count, value); - break; - case COMPRESSEDBITSPERPIXEL: - checkType(tag, type, RATIONAL); - checkCount(tag, count, 1); - _compressedBitsPerPixel = readRational(count, value); - break; - case COMPRESSION: - checkType(tag, type, SHORT); - checkCount(tag, count, 1); - int scheme = readShort(type, count, value); - _niso.setCompressionScheme(scheme); - if (scheme == 5) { - // Set default predictor if none has been set - if (_predictor == NULL) { - _predictor = 1; - } - if (_version < 5) { - _version = 5; - } - } - if (scheme == 6 && _version < 6) { - _version = 6; - } - if (scheme == 3 && _t4Options == NULL) { - // Set default t4Options only if compression is 3 - _t4Options = 0; - } - if (scheme == 4 && _t6Options == NULL) { - // Set default t6Options only if compression is 4 - _t6Options = 0; - } - if (scheme == 6) { - _info.setMessage(new InfoMessage( - MessageConstants.TIFF_HUL_61)); - } - break; - case CONSECUTIVEBADFAXLINES: - checkType(tag, type, SHORT, LONG); - checkCount(tag, count, 1); - _consecutiveBadFaxLines = readLong(type, count, value); - break; - case COPYRIGHT: - checkType(tag, type, ASCII); - _copyright = readASCII(count, value); - if (_version < 6) { - _version = 6; - } - break; - case DATETIME: - checkType(tag, type, ASCII); - checkCount(tag, count, 20); - _dateTime = readASCII(count, value); - _niso.setDateTimeCreated(_dateTime); - if (_version < 5) { - _version = 5; - } - break; - case DATETIMEORIGINAL: - checkType(tag, type, ASCII); - checkCount(tag, count, 20); - _niso.setDateTimeCreated(readASCII(count, value)); - break; - case DOCUMENTNAME: - checkType(tag, type, ASCII); - _documentName = readASCII(count, value); - break; - case DOTRANGE: - checkType(tag, type, BYTE, SHORT); - checkCount(tag, count, 2); - _dotRange = readShortArray(type, count, value); - if (_version < 6) { - _version = 6; - } - break; - case EXIFIFD: - checkType(tag, type, LONG); - checkCount(tag, count, 1); - _exifIFD = readLong(type, count, value); - break; - case EXPOSUREBIASVALUE: - checkType(tag, type, SRATIONAL); - if (count == 1) { - _niso.setExposureBias( - readSignedRational(count, value)); - } else { - checkCountArray(tag, count); - Rational[] r = readSignedRationalArray(count, value); - _niso.setExposureBias(average(r[0], r[1])); - } - break; - case EXPOSUREPROGRAM: - checkType(tag, type, SHORT); - checkCount(tag, count, 1); - _exposureProgram = readShort(type, count, value); - break; - case EXPOSURETIME: - checkType(tag, type, RATIONAL); - if (count == 1) { - _niso.setExposureTime( - readRational(count, value).toDouble()); - } else { - checkCountArray(tag, count); - Rational[] r = readRationalArray(count, value); - _niso.setExposureTime(average(r[0], r[1]).toDouble()); - } - break; - case EXTRASAMPLES: - checkType(tag, type, SHORT); - checkCountArray(tag, count); - _niso.setExtraSamples(readShortArray(type, count, value)); - if (_version < 6) { - _version = 6; - } - break; - case FILLORDER: - checkType(tag, type, SHORT); - checkCount(tag, count, 1); - _fillOrder = readShort(type, count, value); - break; - case FLASH: - checkType(tag, type, SHORT); - checkCount(tag, count, 1); - _flash = readShort(type, count, value); - _niso.setFlash(_flash); - _niso.setFlashReturn(((_flash & 0X6) != 0) ? 1 : 0); - break; - case FLASHENERGY: - checkType(tag, type, RATIONAL); - if (count == 1) { - _niso.setFlashEnergy(readRational(count, value)); - } else { - checkCountArray(tag, count); - Rational[] r = readRationalArray(count, value); - _niso.setFlashEnergy(average(r[0], r[1])); - } - break; - case FNUMBER: - checkType(tag, type, RATIONAL); - if (count == 1) { - _niso.setFNumber(readRational(count, value).toDouble()); - } else { - checkCountArray(tag, count); - Rational[] r = readRationalArray(count, value); - _niso.setFNumber(average(r[0], r[1]).toDouble()); - } - break; - case FOCALLENGTH: - checkType(tag, type, RATIONAL); - if (count == 1) { - _niso.setFocalLength(readRational(count, value).toDouble()); - } else { - checkCountArray(tag, count); - Rational[] r = readRationalArray(count, value); - _niso.setFocalLength(average(r[0], r[1]).toDouble()); - } - break; - case FOCALPLANERESOLUTIONUNIT: - checkType(tag, type, SHORT); - checkCount(tag, count, 1); - _focalPlaneResolutionUnit = readShort(type, count, value); - break; - case FOCALPLANEXRESOLUTION: - checkType(tag, type, RATIONAL); - checkCount(tag, count, 1); - _focalPlaneXResolution = readRational(count, value); - break; - case FOCALPLANEYRESOLUTION: - checkType(tag, type, RATIONAL); - checkCount(tag, count, 1); - _focalPlaneYResolution = readRational(count, value); - break; - case FREEBYTECOUNTS: - checkType(tag, type, LONG); - checkCountArray(tag, count); - _freeByteCounts = readLongArray(type, count, value); - break; - case FREEOFFSETS: - checkType(tag, type, LONG); - checkCountArray(tag, count); - _freeOffsets = readLongArray(type, count, value); - break; - case GEOASCIIPARAMSTAG: - checkType(tag, type, ASCII); - _geoAsciiParamsTag = readASCII(count, value); - break; - case GEODOUBLEPARAMSTAG: - checkType(tag, type, DOUBLE); - checkCountArray(tag, count); - _geoDoubleParamsTag = readDoubleArray(count, value); - break; - case GEOKEYDIRECTORYTAG: - checkType(tag, type, SHORT); - checkCountArray(tag, count); - _geoKeyDirectoryTag = readShortArray(type, count, value); - int num = _geoKeyDirectoryTag[3]; - int prevKey = -1; - for (int i = 0; i < num; i++) { - int j = i * 4 + 4; - int key = _geoKeyDirectoryTag[j]; - if (prevKey > key) { - String mess = MessageFormat.format(MessageConstants.TIFF_HUL_10.getMessage(), key); - JhoveMessage message = JhoveMessages.getMessageInstance( - MessageConstants.TIFF_HUL_10.getId(), mess); - throw new TiffException(message); - } - prevKey = key; - } - break; - case GLOBALPARAMETERSIFD: - checkType(tag, type, LONG, IFD); - // RFC 2301 allows only IFD, but the latest working - // draft allows LONG. Even though allowing LONG - // technically isn't allowed yet, letting it by seems - // reasonable, since other IFD tags can be LONG. - checkCount(tag, count, 1); - _globalParametersIFD = readLong(type, count, value); - break; - case GPSINFOIFD: - checkType(tag, type, LONG); - checkCount(tag, count, 1); - _gpsInfoIFD = readLong(type, count, value); - break; - case GRAYRESPONSECURVE: - checkType(tag, type, SHORT); - checkCountArray(tag, count); - _niso.setGrayResponseCurve(readShortArray(type, count, value)); - break; - case GRAYRESPONSEUNIT: - checkType(tag, type, SHORT); - checkCount(tag, count, 1); - _niso.setGrayResponseUnit(readShort(type, count, value)); - break; - case HALFTONEHINTS: - checkType(tag, type, SHORT); - checkCount(tag, count, 2); - _halftoneHints = readShortArray(type, count, value); - if (_version < 6) { - _version = 6; - } - break; - case HCUSAGE: - checkType(tag, type, LONG); - checkCount(tag, count, 1); - _hcUsage = readLong(type, count, value); - break; - case HOSTCOMPUTER: - checkType(tag, type, ASCII); - _niso.setHostComputer(readASCII(count, value)); - if (_version < 5) { - _version = 5; - } - break; - case IMAGEDESCRIPTION: - checkType(tag, type, ASCII); - _imageDescription = readASCII(count, value); - break; - case IMAGEID: - checkType(tag, type, ASCII); - _niso.setImageIdentifier(readASCII(count, value)); - break; - case IMAGECOLORINDICATOR: - checkType(tag, type, BYTE); - checkCount(tag, count, 1); - _imageColorIndicator = readByte(type, count, value); - break; - case IMAGECOLORVALUE: - checkType(tag, type, BYTE); - checkCount(tag, count, 1); - _imageColorValue = readByte(type, count, value); - break; - case IMAGEHISTORY: - checkType(tag, type, ASCII); - _imageHistory = readASCII(count, value); - break; - case IMAGELAYER: - checkType(tag, type, SHORT, LONG); - checkCount(tag, count, 2); - _imageLayer = readShortArray(type, count, value); - break; - case IMAGELENGTH: - checkType(tag, type, SHORT, LONG); - checkCount(tag, count, 1); - _niso.setImageLength(readLong(type, count, value)); - break; - case IMAGENUMBER: - checkType(tag, type, LONG); - checkCount(tag, count, 1); - _imageNumber = readLong(type, count, value); - break; - case IMAGESOURCEDATA: - checkType(tag, type, UNDEFINED); - // _imageSourceData = readByteArray (type, count, value); - // GDM 16-Sep-2005: - // The ImageSourceData tag sometimes has a gigantic - // amount of data, and we don't actually do anything with - // it in the current version of JHOVE except determine if - // it's there. - _imageSourceData = new int[] { 1 }; - break; - case PHOTOSHOPPROPS: - // Can't find any info on what type is expected. - checkCountArray(tag, count); - _photoshopProperties = readByteArray(type, count, value); - break; - case ANNOTATIONS: - // Can't find any info on what type is expected. - checkCountArray(tag, count); - _annotations = readByteArray(type, count, value); - break; - case IMAGEWIDTH: - checkType(tag, type, SHORT, LONG); - checkCount(tag, count, 1); - _niso.setImageWidth(readLong(type, count, value)); - break; - case INDEXED: - checkType(tag, type, SHORT); - checkCount(tag, count, 1); - _indexed = readShort(type, count, value); - break; - case INKNAMES: - checkType(tag, type, ASCII); - _inkNames = readASCIIArray(count, value); - if (_version < 6) { - _version = 6; - } - break; - case INKSET: - checkType(tag, type, SHORT); - checkCount(tag, count, 1); - _inkSet = readShort(type, count, value); - if (_version < 6) { - _version = 6; - } - break; - case ICC_PROFILE: - checkType(tag, type, UNDEFINED); - // Read the iccProdfile data as an array of bytes - checkCountArray(tag, count); - _iccProfile = readTrueByteArray(type, count, value); - try { - String desc = NisoImageMetadata - .extractIccProfileDescription(_iccProfile); - if (desc != null) { - _niso.setProfileName(desc); - } - } catch (IllegalArgumentException ie) { - String mess = MessageFormat.format(MessageConstants.TIFF_HUL_71.getMessage(), tag, ie.getMessage()); - JhoveMessage message = JhoveMessages.getMessageInstance(MessageConstants.TIFF_HUL_71.getId(), mess); - throw new TiffException(message); - } - break; - case INTERLACE: - checkType(tag, type, SHORT); - checkCount(tag, count, 1); - _interlace = readShort(type, count, value); - break; - case INTEROPERABILITYIFD: - checkType(tag, type, LONG); - checkCount(tag, count, 1); - _interoperabilityIFD = readLong(type, count, value); - break; - case IPTCNAA: - if (type == ASCII) { - String s = readASCII(count, value); - long[] larray = new long[s.length()]; - for (int i = 0; i < s.length(); i++) { - larray[i] = (int) s.charAt(i); - } - _iptc = larray; - } else if (type == LONG) { - checkCountArray(tag, count); - _iptc = readLongArray(type, count, value); - } else { - checkType(tag, type, BYTE, UNDEFINED); - checkCountArray(tag, count); - int[] b = readByteArray(type, count, value); - long[] larray = new long[b.length]; - for (int i = 0; i < b.length; i++) { - larray[i] = b[i]; - _iptc = larray; - } - } - break; - case ISOSPEEDRATINGS: - checkType(tag, type, SHORT); - checkCount(tag, count, 1); - _isoSpeedRatings = readShortArray(tag, count, value); - break; - case IT8HEADER: - checkType(tag, type, ASCII); - _it8Header = readASCII(count, value); - break; - case JPEGACTABLES: - checkType(tag, type, LONG); - checkCountArray(tag, count); - _jpegACTables = readLongArray(type, count, value); - if (_version < 6) { - _version = 6; - } - break; - case JPEGDCTABLES: - checkType(tag, type, LONG); - checkCountArray(tag, count); - _jpegDCTables = readLongArray(type, count, value); - if (_version < 6) { - _version = 6; - } - break; - case JPEGINTERCHANGEFORMAT: - checkType(tag, type, LONG); - checkCount(tag, count, 1); - _jpegInterchangeFormat = readLong(type, count, value); - if (_version < 6) { - _version = 6; - } - break; - case JPEGINTERCHANGEFORMATLENGTH: - checkType(tag, type, LONG); - checkCount(tag, count, 1); - _jpegInterchangeFormatLength = readLong(type, count, value); - if (_version < 6) { - _version = 6; - } - break; - case JPEGLOSSLESSPREDICTORS: - checkType(tag, type, SHORT); - checkCountArray(tag, count); - _jpegLosslessPredictors = readShortArray(type, count, value); - if (_version < 6) { - _version = 6; - } - break; - case JPEGPOINTTRANSFORMS: - checkType(tag, type, SHORT); - checkCountArray(tag, count); - _jpegPointTransforms = readShortArray(type, count, value); - if (_version < 6) { - _version = 6; - } - break; - case JPEGPROC: - checkType(tag, type, SHORT); - checkCount(tag, count, 1); - _jpegProc = readShort(type, count, value); - if (_version < 6) { - _version = 6; - } - break; - case JPEGQTABLES: - checkType(tag, type, LONG); - checkCountArray(tag, count); - _jpegQTables = readLongArray(type, count, value); - if (_version < 6) { - _version = 6; - } - break; - case JPEGRESTARTINTERVAL: - checkType(tag, type, SHORT); - checkCount(tag, count, 1); - _jpegRestartInterval = readShort(type, count, value); - if (_version < 6) { - _version = 6; - } - break; - case JPEGTABLES: - checkType(tag, type, UNDEFINED); - checkCountArray(tag, count); - _jpegTables = readByteArray(type, count, value); - break; - case LIGHTSOURCE: - checkType(tag, type, SHORT); - checkCount(tag, count, 1); - _niso.setSceneIlluminant(readShort(type, count, value)); - break; - case MAKE: - checkType(tag, type, ASCII); - _niso.setScannerManufacturer(readASCII(count, value)); - break; - case MAXAPERTUREVALUE: - checkType(tag, type, RATIONAL); - checkCount(tag, count, 1); - _maxAperatureValue = readRational(count, value); - break; - case MAXSAMPLEVALUE: - checkType(tag, type, SHORT); - checkCountArray(tag, count); - _maxSampleValue = readShortArray(type, count, value); - break; - case METERINGMODE: - checkType(tag, type, SHORT); - checkCount(tag, count, 1); - _niso.setMeteringMode(readShort(type, count, value)); - break; - case MINSAMPLEVALUE: - checkType(tag, type, SHORT); - checkCountArray(tag, count); - _minSampleValue = readShortArray(type, count, value); - break; - case MODEL: - checkType(tag, type, ASCII); - _niso.setScannerModelName(readASCII(count, value)); - break; - case MODELPIXELSCALETAG: - checkType(tag, type, DOUBLE); - checkCount(tag, count, 3); - _modelPixelScaleTag = readDoubleArray(count, value); - break; - case MODELTIEPOINTTAG: - checkType(tag, type, DOUBLE); - checkCountArray(tag, count); - _modelTiepointTag = readDoubleArray(count, value); - break; - case MODELTRANSFORMATIONTAG: - checkType(tag, type, DOUBLE); - checkCount(tag, count, 16); - _modelTransformationTag = readDoubleArray(count, value); - break; - case NEWSUBFILETYPE: - checkType(tag, type, LONG); - checkCount(tag, count, 1); - _newSubfileType = readLong(type, count, value); - if (_version < 5) { - _version = 5; - } - break; - case NOISE: - checkType(tag, type, UNDEFINED); - checkCountArray(tag, count); - _noise = readByteArray(type, count, value); - break; - case NUMBEROFINKS: - checkType(tag, type, SHORT); - checkCount(tag, count, 1); - _numberOfInks = readShort(type, count, value); - if (_version < 6) { - _version = 6; - } - break; - case OECF: - checkType(tag, type, UNDEFINED); - checkCountArray(tag, count); - _oecf = readByteArray(tag, count, value); - break; - case OPIPROXY: - checkType(tag, type, SHORT); - checkCount(tag, count, 1); - _opiProxy = readShort(type, count, value); - break; - case ORIENTATION: - checkType(tag, type, SHORT); - checkCount(tag, count, 1); - _niso.setOrientation(readShort(type, count, value)); - break; - case PAGENAME: - checkType(tag, type, ASCII); - _pageName = readASCII(count, value); - break; - case PAGENUMBER: - checkType(tag, type, SHORT); - checkCount(tag, count, 2); - _pageNumber = readShortArray(type, count, value); - break; - case PHOTOMETRICINTERPRETATION: - checkType(tag, type, SHORT); - checkCount(tag, count, 1); - _photometricInterpretation = readShort(type, count, value); - _niso.setColorSpace(_photometricInterpretation); - // Set default values appropriate to interpretation, - // only if no value has been set. - if (_photometricInterpretation == 5) { - if (_inkSet == NULL) { - _inkSet = 1; - } - if (_numberOfInks == NULL) { - _numberOfInks = 4; - } - } else if (_photometricInterpretation == 6) { - if (_niso.getYCbCrCoefficients() == null) { - _niso.setYCbCrCoefficients( - new Rational[] { new Rational(299, 1000), - new Rational(587, 1000), - new Rational(114, 1000) }); - } - if (_niso.getYCbCrPositioning() == NULL) { - _niso.setYCbCrPositioning(1); - } - if (_niso.getYCbCrSubSampling() == null) { - _niso.setYCbCrSubSampling(new int[] { 2, 2 }); - } - } - int colorSpace = _niso.getColorSpace(); - if (colorSpace == 3 || colorSpace == 4) { - if (_version < 5) { - _version = 5; - } - } else if (colorSpace == 5 || colorSpace == 6 - || colorSpace == 8) { - if (_version < 6) { - _version = 6; - } - } - break; - case PIXELINTENSITYRANGE: - checkType(tag, type, BYTE); - checkCountArray(tag, count); - _pixelIntensityRange = readShortArray(type, count, value); - break; - case PLANARCONFIGURATION: - checkType(tag, type, SHORT); - checkCount(tag, count, 1); - _niso.setPlanarConfiguration(readShort(type, count, value)); - break; - case PREDICTOR: - checkType(tag, type, SHORT); - checkCount(tag, count, 1); - _predictor = readShort(type, count, value); - if (_version < 5) { - _version = 5; - } - break; - case PRIMARYCHROMATICITIES: - { - checkType(tag, type, RATIONAL); - checkCount(tag, count, 6); - Rational[] rarray = readRationalArray(count, value); - _niso.setPrimaryChromaticitiesRedX(rarray[0]); - _niso.setPrimaryChromaticitiesRedY(rarray[1]); - _niso.setPrimaryChromaticitiesGreenX(rarray[2]); - _niso.setPrimaryChromaticitiesGreenY(rarray[3]); - _niso.setPrimaryChromaticitiesBlueX(rarray[4]); - _niso.setPrimaryChromaticitiesBlueY(rarray[5]); - if (_version < 5) { - _version = 5; - } - break; - } - case RASTERPADDING: - checkType(tag, type, SHORT); - checkCount(tag, count, 1); - _rasterPadding = readShort(type, count, value); - break; - case REFERENCEBLACKWHITE: - checkType(tag, type, RATIONAL); - checkCount(tag, count, 6); - _niso.setReferenceBlackWhite(readRationalArray(count, value)); - if (_version < 6) { - _version = 6; - } - break; - case RESOLUTIONUNIT: - checkType(tag, type, SHORT); - checkCount(tag, count, 1); - _niso.setSamplingFrequencyUnit(readShort(type, count, value)); - break; - case ROWSPERSTRIP: - checkType(tag, type, SHORT, LONG); - checkCount(tag, count, 1); - _niso.setRowsPerStrip(readLong(type, count, value)); - break; - case SAMPLEFORMAT: - checkType(tag, type, SHORT); - checkCountArray(tag, count); - _sampleFormat = readShortArray(type, count, value); - if (_version < 6) { - _version = 6; - } - break; - case SAMPLESPERPIXEL: - checkType(tag, type, SHORT); - checkCount(tag, count, 1); - _niso.setSamplesPerPixel(readShort(type, count, value)); - break; - case SECURITYCLASSIFICATION: - checkType(tag, type, ASCII); - _securityClassification = readASCII(count, value); - break; - case SELFTIMERMODE: - checkType(tag, type, SHORT); - checkCount(tag, count, 1); - _selfTimerMode = readShort(type, count, value); - break; - case SENSINGMETHOD: - checkType(tag, type, SHORT); - checkCount(tag, count, 1); - _niso.setSensor(readShort(type, count, value)); - break; - case SITE: - checkType(tag, type, ASCII); - _site = readASCII(count, value); - break; - case STRIPROWCOUNTS: - checkType(tag, type, LONG); - checkCountArray(tag, count); - _stripRowCounts = readLongArray(type, count, value); - break; - case SUBJECTDISTANCE: - checkType(tag, type, RATIONAL, SRATIONAL); - double[] darray = new double[2]; - if (count == 1) { - darray[0] = readRational(count, value).toDouble(); - darray[1] = darray[0]; - } else { - checkCountArray(tag, count); - Rational[] r; - if (type == RATIONAL) { - r = readRationalArray(count, value); - } else { - r = readSignedRationalArray(count, value); - } - darray[0] = r[0].toDouble(); - if (r.length > 1) { - darray[1] = r[1].toDouble(); - } else { - darray[1] = darray[0]; - } - } - _niso.setSubjectDistance(darray); - break; - case SOFTWARE: - checkType(tag, type, ASCII); - _niso.setScanningSoftware(readASCII(count, value)); - if (_version < 5) { - _version = 5; - } - break; - case SPATIALFREQUENCYRESPONSE: - checkType(tag, type, UNDEFINED); - checkCountArray(tag, count); - _spatialFrequencyResponse = readByteArray(type, count, value); - break; - case SPECTRALSENSITIVITY: - checkType(tag, type, ASCII); - _spectralSensitivity = readASCII(count, value); - break; - case STRIPBYTECOUNTS: - checkType(tag, type, SHORT, LONG); - checkCountArray(tag, count); - _niso.setStripByteCounts(readLongArray(type, count, value)); - break; - case STRIPOFFSETS: - checkType(tag, type, SHORT, LONG); - checkCountArray(tag, count); - _niso.setStripOffsets(readLongArray(type, count, value)); - break; - case SUBFILETYPE: - checkType(tag, type, LONG); - checkCount(tag, count, 1); - _subfileType = readShort(type, count, value); - break; - case SUBIFDS: - checkType(tag, type, LONG, IFD); - checkCountArray(tag, count); - _subIFDs = readLongArray(type, count, value); - break; - case SUBJECTLOCATION: - checkType(tag, type, SHORT); - checkCountArray(tag, count); - _subjectLocation = readShortArray(type, count, value); - break; - case T4OPTIONS: - checkType(tag, type, LONG); - _t4Options = readShort(type, count, value); - break; - case T6OPTIONS: - checkType(tag, type, LONG); - _t6Options = readShort(type, count, value); - break; - case TARGETPRINTER: - checkType(tag, type, ASCII); - _targetPrinter = readASCII(count, value); - if (_version < 6) { - _version = 6; - } - break; - case THRESHHOLDING: - checkType(tag, type, SHORT); - checkCount(tag, count, 1); - _threshholding = readShort(type, count, value); - break; - case TIFFEPSTANDARDID: - checkType(tag, type, SHORT); - checkCount(tag, count, 4); - int[] iarray = readShortArray(type, count, value); - _tiffEPStandardID = Integer.toString(iarray[0]) + "." - + Integer.toString(iarray[1]) + "." - + Integer.toString(iarray[2]) + "." - + Integer.toString(iarray[3]); - break; - case TILEBYTECOUNTS: - checkType(tag, type, SHORT, LONG); - checkCountArray(tag, count); - _niso.setTileByteCounts(readLongArray(type, count, value)); - if (_version < 6) { - _version = 6; - } - break; - case TILELENGTH: - checkType(tag, type, SHORT, LONG); - checkCount(tag, count, 1); - _niso.setTileLength(readLong(type, count, value)); - if (_version < 6) { - _version = 6; - } - break; - case TILEOFFSETS: - checkType(tag, type, SHORT, LONG); - checkCountArray(tag, count); - _niso.setTileOffsets(readLongArray(type, count, value)); - if (_version < 6) { - _version = 6; - } - break; - case TILEWIDTH: - checkType(tag, type, SHORT, LONG); - checkCount(tag, count, 1); - _niso.setTileWidth(readLong(type, count, value)); - if (_version < 6) { - _version = 6; - } - break; - case TIMEZONEOFFSET: - checkType(tag, type, SSHORT); - checkCountArray(tag, count); - _timeZoneOffset = readSShortArray(type, count, value); - break; - case TRANSFERFUNCTION: - /* - * Transfer function arrays potentially can have millions - * of elements, so we just report presence - */ - checkType(tag, type, SHORT); - _transferFunction = true; - break; - case TRANSFERRANGE: - checkType(tag, type, SHORT); - checkCount(tag, count, 6); - _transferRange = readShortArray(type, count, value); - if (_version < 6) { - _version = 6; - } - break; - case TRANSPARENCYINDICATOR: - checkType(tag, type, BYTE); - checkCount(tag, count, 1); - _transparencyIndicator = readByte(type, count, value); - break; - case WHITEPOINT: - { - checkType(tag, type, RATIONAL); - checkCount(tag, count, 2); - Rational[] rarray = readRationalArray(count, value); - _niso.setWhitePointXValue(rarray[0]); - _niso.setWhitePointYValue(rarray[0]); - if (_version < 5) { - _version = 5; - } - break; - } - case XCLIPPATHUNITS: - checkType(tag, type, LONG); - checkCount(tag, count, 1); - _xClipPathUnits = readLong(type, count, value); - break; - case XPOSITION: - checkType(tag, type, RATIONAL); - checkCount(tag, count, 1); - _xPosition = readRational(count, value); - break; - case XRESOLUTION: - checkType(tag, type, RATIONAL); - checkCount(tag, count, 1); - _niso.setXSamplingFrequency(readRational(count, value)); - break; - case YCBCRCOEFFICIENTS: - checkType(tag, type, RATIONAL); - checkCount(tag, count, 3); - _niso.setYCbCrCoefficients(readRationalArray(count, value)); - if (_version < 6) { - _version = 6; - } - break; - case YCBCRPOSITIONING: - checkType(tag, type, SHORT); - checkCount(tag, count, 1); - _niso.setYCbCrPositioning(readShort(type, count, value)); - if (_version < 6) { - _version = 6; - } - break; - case YCBCRSUBSAMPLING: - checkType(tag, type, SHORT); - checkCount(tag, count, 2); - _niso.setYCbCrSubSampling(readShortArray(type, count, value)); - if (_version < 6) { - _version = 6; - } - break; - case YCLIPPATHUNITS: - checkType(tag, type, LONG); - checkCount(tag, count, 1); - _yClipPathUnits = readLong(type, count, value); - break; - case YPOSITION: - checkType(tag, type, RATIONAL); - checkCount(tag, count, 1); - _yPosition = readRational(count, value); - break; - case YRESOLUTION: - checkType(tag, type, RATIONAL); - checkCount(tag, count, 1); - _niso.setYSamplingFrequency(readRational(count, value)); - break; - case XMP: - checkType(tag, type, UNDEFINED, BYTE); - checkCountArray(tag, count); - _xmpProp = readXMP(count, value); - break; - case DNGVERSION: - checkType(tag, type, BYTE); - checkCount(tag, count, 4); - _dngVersion = readByteArray(type, count, value); - break; - case DNGBACKWARDVERSION: - checkType(tag, type, BYTE); - checkCount(tag, count, 4); - _dngBackwardVersion = readByteArray(type, count, value); - break; - case UNIQUECAMERAMODEL: - checkType(tag, type, ASCII); - _uniqueCameraModel = readASCII(count, value); - break; - case LOCALIZEDCAMERAMODEL: - { - checkType(tag, type, ASCII, BYTE); - // This tag is specified as UTF-8 - byte[] lcm = readTrueByteArray(type, count, value); - // Trim off trailing null (s) - int len = lcm.length; - while (len > 0 && lcm[len - 1] == 0) { - len--; - } - _localizedCameraModel = new String(lcm, 0, len); - break; - } - case CFAPLANECOLOR: - checkType(tag, type, BYTE); - checkCountArray(tag, count); - _cfaPlaneColor = readByteArray(type, count, value); - break; - case CFALAYOUT: - checkType(tag, type, SHORT); - _cfaLayout = readShort(type, count, value); - break; - case LINEARIZATIONTABLE: - checkType(tag, type, SHORT); - checkCountArray(tag, count); - _linearizationTable = readShortArray(type, count, value); - break; - case BLACKLEVELREPEATDIM: - checkType(tag, type, SHORT); - checkCountArray(tag, count); - _blackLevelRepeatDim = readShortArray(type, count, value); - break; - case BLACKLEVEL: - // Just to make things complicated, this can be SHORT, LONG - // or RATIONAL. To give these a least common (pardon the - // expression) denominator, we convert all to rational. - checkCountArray(tag, count); - if (type == RATIONAL) { - _blackLevel = readRationalArray(count, value); - } else { - checkType(tag, type, SHORT, LONG); - long[] ibl = readLongArray(type, count, value); - _blackLevel = new Rational[(int) count]; - for (int i = 0; i < count; i++) { - _blackLevel[i] = new Rational(ibl[i], 1); - } - } - break; - case BLACKLEVELDELTAH: - checkType(tag, type, SRATIONAL); - checkCountArray(tag, count); - _blackLevelDeltaH = readSignedRationalArray(count, value); - break; - case BLACKLEVELDELTAV: - checkType(tag, type, SRATIONAL); - checkCountArray(tag, count); - _blackLevelDeltaV = readSignedRationalArray(count, value); - break; - case WHITELEVEL: - checkType(tag, type, SHORT, LONG); - checkCountArray(tag, count); - _whiteLevel = readLongArray(type, count, value); - break; - case DEFAULTSCALE: - checkType(tag, type, RATIONAL); - checkCount(tag, count, 2); - _defaultScale = readRationalArray(count, value); - break; - case BESTQUALITYSCALE: - checkType(tag, type, RATIONAL); - checkCount(tag, count, 1); - _bestQualityScale = readRational(count, value); - break; - case DEFAULTCROPORIGIN: - checkCount(tag, count, 2); - // Just to make things complicated, this can be SHORT, LONG - // or RATIONAL. To give these a least common (pardon the - // expression) denominator, we convert all to rational. - if (type == RATIONAL) { - _defaultCropOrigin = readRationalArray(count, value); - } else { - checkType(tag, type, SHORT, LONG); - long[] lco = readLongArray(type, count, value); - _defaultCropOrigin = new Rational[(int) count]; - for (int i = 0; i < count; i++) { - _defaultCropOrigin[i] = new Rational(lco[i], 1); - } - } - break; - case DEFAULTCROPSIZE: - checkCount(tag, count, 2); - if (type == RATIONAL) { - _defaultCropSize = readRationalArray(count, value); - } else { - checkType(tag, type, SHORT, LONG); - long[] lcs = readLongArray(type, count, value); - _defaultCropSize = new Rational[(int) count]; - for (int i = 0; i < count; i++) { - _defaultCropSize[i] = new Rational(lcs[i], 1); - } - } - break; - case CALIBRATIONILLUMINANT1: - checkCount(tag, count, 1); - checkType(tag, type, SHORT); - _calibrationIlluminant1 = readShort(type, count, value); - break; - case CALIBRATIONILLUMINANT2: - checkCount(tag, count, 1); - checkType(tag, type, SHORT); - _calibrationIlluminant2 = readShort(type, count, value); - break; - case COLORMATRIX1: - checkType(tag, type, SRATIONAL); - checkCountArray(tag, count); - _colorMatrix1 = readSignedRationalArray(count, value); - break; - case COLORMATRIX2: - checkType(tag, type, SRATIONAL); - checkCountArray(tag, count); - _colorMatrix2 = readSignedRationalArray(count, value); - break; - case CAMERACALIBRATION1: - checkType(tag, type, SRATIONAL); - checkCountArray(tag, count); - _colorMatrix1 = readSignedRationalArray(count, value); - break; - case CAMERACALIBRATION2: - checkType(tag, type, SRATIONAL); - checkCountArray(tag, count); - _colorMatrix2 = readSignedRationalArray(count, value); - break; - case REDUCTIONMATRIX1: - checkType(tag, type, SRATIONAL); - checkCountArray(tag, count); - _reductionMatrix1 = readSignedRationalArray(count, value); - break; - case REDUCTIONMATRIX2: - checkType(tag, type, SRATIONAL); - checkCountArray(tag, count); - _reductionMatrix2 = readSignedRationalArray(count, value); - break; - case ANALOGBALANCE: - checkType(tag, type, RATIONAL); - checkCountArray(tag, count); - _analogBalance = readRationalArray(count, value); - break; - case ASSHOTNEUTRAL: - // this can be either SHORT or RATIONAL - checkType(tag, type, SHORT, RATIONAL); - checkCountArray(tag, count); - if (type == SHORT) { - int[] asn = readShortArray(type, count, value); - _asShotNeutral = new Rational[(int) count]; - for (int i = 0; i < count; i++) { - _asShotNeutral[i] = new Rational(asn[i], 1); - } - } else { - _asShotNeutral = readRationalArray(count, value); - } - break; - case ASSHOTWHITEXY: - checkType(tag, type, RATIONAL); - checkCount(tag, count, 2); - _asShotWhiteXY = readRationalArray(count, value); - break; - case BASELINEEXPOSURE: - checkType(tag, type, SRATIONAL); - _baselineExposure = readSignedRational(count, value); - break; - case BASELINENOISE: - checkType(tag, type, RATIONAL); - _baselineNoise = readRational(count, value); - break; - case BASELINESHARPNESS: - checkType(tag, type, RATIONAL); - _baselineSharpness = readRational(count, value); - break; - case BAYERGREENSPLIT: - checkType(tag, type, LONG); - _bayerGreenSplit = (int) readLong(type, count, value); - break; - case LINEARRESPONSELIMIT: - checkType(tag, type, RATIONAL); - _linearResponseLimit = readRational(count, value); - break; - case CAMERASERIALNUMBER: - checkType(tag, type, ASCII); - _cameraSerialNumber = readASCII(count, value); - break; - case LENSINFO: - checkType(tag, type, RATIONAL); - checkCount(tag, count, 4); - _lensInfo = readRationalArray(count, value); - break; - case CHROMABLURRADIUS: - checkType(tag, type, RATIONAL); - _chromaBlurRadius = readRational(count, value); - break; - case ANTIALIASSTRENGTH: - checkType(tag, type, RATIONAL); - _antiAliasStrength = readRational(count, value); - break; - case SHADOWSCALE: - _info.setMessage(new InfoMessage(MessageConstants.TIFF_HUL_11, - MessageConstants.TIFF_HUL_11_SUB.getMessage())); - break; - case DNGPRIVATEDATA: - checkType(tag, type, BYTE); - checkCountArray(tag, count); - _dngPrivateData = readByteArray(type, count, value); - break; - case MAKERNOTESAFETY: - checkType(tag, type, SHORT); - _makerNoteSafety = readShort(type, count, value); - break; - default: - String mess = MessageFormat - .format(MessageConstants.TIFF_HUL_12.getMessage(), tag); - JhoveMessage message = JhoveMessages.getMessageInstance( - MessageConstants.TIFF_HUL_12.getId(), mess); - _info.setMessage(new InfoMessage(message, value)); - break; - } - } catch (IOException e) { - String mess = MessageFormat - .format(MessageConstants.TIFF_HUL_13.getMessage(), tag); - JhoveMessage message = JhoveMessages.getMessageInstance( - MessageConstants.TIFF_HUL_13.getId(), mess); - throw new TiffException(message, value); - } - } - - /** - * Perform initializations that have to wait until after the - * IFD has been parsed. - */ - protected void postParseInitialization() { - int samplesPerPixel = _niso.getSamplesPerPixel(); - int[] bitsPerSample = _niso.getBitsPerSample(); - if (bitsPerSample == null) { - bitsPerSample = new int[samplesPerPixel]; - for (int i = 0; i < samplesPerPixel; i++) { - bitsPerSample[i] = 1; - } - _niso.setBitsPerSample(bitsPerSample); - } - int bps1 = (1 << bitsPerSample[0]) - 1; - if (_maxSampleValue == null) { - _maxSampleValue = new int[samplesPerPixel]; - for (int i = 0; i < samplesPerPixel; i++) { - _maxSampleValue[i] = bps1; - } - - } - if (_minSampleValue == null) { - _minSampleValue = new int[samplesPerPixel]; - for (int i = 0; i < samplesPerPixel; i++) { - _minSampleValue[i] = 0; - } - } - if (_sampleFormat == null) { - _sampleFormat = new int[samplesPerPixel]; - for (int i = 0; i < samplesPerPixel; i++) { - _sampleFormat[i] = 1; - } - } - /* - * The default transfer function is expressed as an array - * of 2^BitsPerSample elements. If BitsPerSample is 24, - * this would paraylze processing for many minutes, if it - * didn't just run out of memory. So it was a nice idea... - */ - // if (_photometricInterpretation == 0 || - // _photometricInterpretation == 1 || - // _photometricInterpretation == 2 || - // _photometricInterpretation == 3 || - // _photometricInterpretation == 6) { - // // Set default transfer function only for indicated - // // photometricInterpretations. - // if (_transferFunction == null) { - // int n = 1<TiffIFD object. + * + * @param offset IFD offset + * @param info The RepInfo object + * @param raf TIFF file + * @param bigEndian True if big-endian file + */ + public TiffIFD(long offset, RepInfo info, RandomAccessFile raf, boolean bigEndian) { + super(offset, info, raf, bigEndian); + + /* Define a NISO metadata object and set defaults. */ + _niso = new NisoImageMetadata(); + _niso.setMimeType("image/tiff"); + _niso.setCompressionScheme(1); + _niso.setOrientation(1); + _niso.setPlanarConfiguration(1); + _niso.setRowsPerStrip(4294967295L); + _niso.setSamplesPerPixel(1); + _niso.setByteOrder(bigEndian ? "big-endian" : "little-endian"); + + /* Set non-NISO defaults. */ + _photometricInterpretation = NULL; + _cellLength = NULL; + _cellWidth = NULL; + _fillOrder = NULL; + _indexed = 0; + _inkSet = NULL; + _jpegInterchangeFormat = NULL; + _jpegInterchangeFormatLength = NULL; + _jpegProc = NULL; + _jpegRestartInterval = NULL; + _newSubfileType = 0L; + _numberOfInks = NULL; + _opiProxy = NULL; + _predictor = NULL; + _subfileType = NULL; + _t4Options = NULL; + _t6Options = NULL; + _threshholding = 1; + _xClipPathUnits = NULL; + _yClipPathUnits = NULL; + + /* TIFF/IT defaults. */ + _backgroundColorIndicator = 0; + _backgroundColorValue = NULL; + _bitsPerExtendedRunLength = 16; + _bitsPerRunLength = 8; + _hcUsage = NULL; + _imageColorIndicator = 0; + _imageColorValue = NULL; + _rasterPadding = 0; + _transparencyIndicator = 0; + + /* TIFF/EP defaults. */ + _exposureProgram = NULL; + _flash = NULL; + _focalPlaneResolutionUnit = NULL; + _gpsInfoIFD = NULL; + _imageNumber = NULL; + _selfTimerMode = NULL; + + /* Exif defaults. */ + _exifIFD = NULL; + _focalPlaneResolutionUnit = NULL; + _imageNumber = NULL; + _interlace = NULL; + _interoperabilityIFD = NULL; + _globalParametersIFD = NULL; + + /* Class F/RFC 1324 defaults. */ + _badFaxLines = NULL; + _cleanFaxData = NULL; + _consecutiveBadFaxLines = NULL; + + /* XMP default. */ + _xmpProp = null; + + /* Tiff/FX defaults. */ + _stripRowCounts = null; + _imageLayer = null; + + /* DNG defaults. */ + _dngVersion = null; + _dngBackwardVersion = null; + _uniqueCameraModel = null; + _localizedCameraModel = null; + _cfaPlaneColor = null; + _cfaLayout = NULL; + _linearizationTable = null; + _blackLevelRepeatDim = null; + _blackLevel = null; + _blackLevelDeltaH = null; + _blackLevelDeltaV = null; + _whiteLevel = null; + _defaultScale = null; + _bestQualityScale = null; + _defaultCropOrigin = null; + _defaultCropSize = null; + _calibrationIlluminant1 = NULL; + _calibrationIlluminant2 = NULL; + _colorMatrix1 = null; + _colorMatrix2 = null; + _cameraCalibration1 = null; + _cameraCalibration2 = null; + _reductionMatrix1 = null; + _reductionMatrix2 = null; + _analogBalance = null; + _asShotNeutral = null; + _asShotWhiteXY = null; + _baselineExposure = null; + _baselineNoise = null; + _baselineSharpness = null; + _bayerGreenSplit = NULL; + _linearResponseLimit = null; + _cameraSerialNumber = null; + _lensInfo = null; + _chromaBlurRadius = null; + _antiAliasStrength = null; + _dngPrivateData = null; + _makerNoteSafety = NULL; + } + + /** + * **************************************************************** PUBLIC INSTANCE METHODS. + * **************************************************************** + */ + + /** Returns the value of the APERTUREVALUE (37378) tag. Note typo in function name. */ + public Rational getAperatureValue() { + return _aperatureValue; + } + + /** Returns the value of the TIFF/IT BACKGROUNDCOLORINDICATOR (34024) tag. */ + public int getBackgroundColorIndicator() { + return _backgroundColorIndicator; + } + + /** Returns the value of the BACKGROUNDCOLORVALUE (34026) tag. */ + public int getBackgroundColorValue() { + return _backgroundColorValue; + } + + /** Returns the value of the BATTERYLEVEL (33423) tag. */ + public String getBatteryLevel() { + return _batteryLevel; + } + + /** Returns the value of the BITSPEREXTENDEDRUNLENGTH (34021) tag. */ + public int getBitsPerExtendedRunLength() { + return _bitsPerExtendedRunLength; + } + + /** Returns the value of the BITSPERRUNLENGTH (34020) tag. */ + public int getBitsPerRunLength() { + return _bitsPerRunLength; + } + + /** Returns the value of the CELLLENGTH (265) tag. */ + public int getCellLength() { + return _cellLength; + } + + /** Returns the value of the CELLWIDTH (264) tag. */ + public int getCellWidth() { + return _cellWidth; + } + + /** Returns the value of the CFAPATTERN (33422) tag. */ + public int[] getCFAPattern() { + return _cfaPattern; + } + + /** Returns the value of the CFAREPEATPATTERNDIM (33421) tag. */ + public int[] getCFARepeatPatternDim() { + return _cfaRepeatPatternDim; + } + + /** Returns the value of the CLIPPATH (343) tag. */ + public int[] getClipPath() { + return _clipPath; + } + + /** Returns the value of the COLORSEQUENCE (34017) tag. */ + public String getColorSequence() { + return _colorSequence; + } + + /** Returns the value of the COLORTABLE (34022) tag. */ + public int[] getColorTable() { + return _colorTable; + } + + /** Returns the value of the COMPRESSEDBITSPERPIXEL (37122) tag. */ + public Rational getCompressedBitsPerPixel() { + return _compressedBitsPerPixel; + } + + /** Returns the value of the COPYRIGHT (33432) tag. */ + public String getCopyright() { + return _copyright; + } + + /** Returns the value of the DATETIME (306) tag. */ + public String getDateTime() { + return _dateTime; + } + + /** Returns the value of the DOCUMENTNAME (269) tag. */ + public String getDocumentName() { + return _documentName; + } + + /** Returns the value of the DOTRANGE (336) tag. */ + public int[] getDotRange() { + return _dotRange; + } + + /** Return the offset of the Exif IFD. */ + public long getExifIFD() { + return _exifIFD; + } + + /** Return the offset of the GlobalParameters IFD. */ + public long getGlobalParametersIFD() { + return _globalParametersIFD; + } + + /** Returns the value of the EXPOSUREPROGRAM (34850) tag. */ + public int getExposureProgram() { + return _exposureProgram; + } + + /** Returns the value of the FILLORDER (266) tag. */ + public int getFillOrder() { + return _fillOrder; + } + + /** Returns the value of the FOCALPLANERESOLUTIONUNIT (37392) tag. */ + public int getFocalPlaneResolutionUnit() { + return _focalPlaneResolutionUnit; + } + + /** Returns the value of the FOCALPLANEXRESOLUTION (37390) tag. */ + public Rational getFocalPlaneXResolution() { + return _focalPlaneXResolution; + } + + /** Returns the value of the FOCALPLANEYRESOLUTION (37390) tag. */ + public Rational getFocalPlaneYResolution() { + return _focalPlaneYResolution; + } + + /** Returns the value of the GEOKEYDIRECTORYTAG (34735) tag. */ + public int[] getGeoKeyDirectoryTag() { + return _geoKeyDirectoryTag; + } + + /** Return the offset of the GPSInfo IFD. */ + public long getGPSInfoIFD() { + return _gpsInfoIFD; + } + + /** Returns the value of the IMAGECOLORINDICATOR (34023) tag. */ + public int getImageColorIndicator() { + return _imageColorIndicator; + } + + /** Returns the value of the IMAGECOLORVALUE (34025) tag. */ + public int getImageColorValue() { + return _imageColorValue; + } + + /** Returns the value of the IMAGEDESCRIPTION (270) tag. */ + public String getImageDescription() { + return _imageDescription; + } + + /** Returns the value of the IMAGEHISTORY (37395) tag. */ + public String getImageHistory() { + return _imageHistory; + } + + /** Returns the value of the IMAGELAYER (34732) tag. */ + public int[] getImageLayer() { + return _imageLayer; + } + + /** Returns the value of the IMAGENUMBER (37393) tag. */ + public long getImageNumber() { + return _imageNumber; + } + + /** Returns the value of the IMAGESOURCEDATA (37724) tag. */ + public int[] getImageSourceData() { + return _imageSourceData; + } + + /** Returns the value of the PHOTOSHOPPROPS (34377) tag. */ + public int[] getPhotoshopProperties() { + return _photoshopProperties; + } + + /** Returns the value of the ANNOTATIONS (50255) tag. */ + public int[] getAnnotations() { + return _annotations; + } + + /** Returns the value of the INKNAMES (333) tag. */ + public String[] getInkNames() { + return _inkNames; + } + + /** Returns the value of the INKSET (332) tag. */ + public int getInkSet() { + return _inkSet; + } + + /** Returns the value of the INTERLACE (34857) tag. */ + public int getInterlace() { + return _interlace; + } + + /** Returns the offset of the Exif Interoperability IFD. */ + public long getInteroperabilityIFD() { + return _interoperabilityIFD; + } + + /** Returns the value of the ICC_PROFILE tag. */ + public byte[] getICCProfile() { + return _iccProfile; + } + + /** Returns the value of the INDEXED (364) tag. */ + public int getIndexed() { + return _indexed; + } + + public long getJpegInterchangeFormat() { + return _jpegInterchangeFormat; + } + + /** Returns the value of the IPTCNAA (33723) tag. */ + public long[] getIPTCNAA() { + return _iptc; + } + + /** Returns the value of the ISOSPEEDRATINGS (34855) tag. */ + public int[] getISOSpeedRatings() { + return _isoSpeedRatings; + } + + /** Returns the value of the IT8HEADER (34018) tag. */ + public String getIT8Header() { + return _it8Header; + } + + /** Returns the value of the JPEGPROC (512) tag. */ + public int getJPEGProc() { + return _jpegProc; + } + + /** Returns the value of the MAXAPERTUREVALUE (37381) tag. Note typo in function name. */ + public Rational getMaxAperatureValue() { + return _maxAperatureValue; + } + + /** Returns the value of the MODELTIEPOINTTAG (33922) tag. */ + public double[] getModelTiepointTag() { + return _modelTiepointTag; + } + + /** Returns the value of the MODELTRANSFORMATIONTAG (34264) tag. */ + public double[] getModelTransformationTag() { + return _modelTransformationTag; + } + + /** Returns the value of the NEWSUBFILETYPE (254) tag. */ + public long getNewSubfileType() { + return _newSubfileType; + } + + /** Returns the constructed NisoImageMetadata. */ + public NisoImageMetadata getNisoImageMetadata() { + return _niso; + } + + /** Returns the value of the NOISE (37389) tag. */ + public int[] getNoise() { + return _noise; + } + + /** Returns the value of the NUMBEROFINKS (334) tag. */ + public int getNumberOfInks() { + return _numberOfInks; + } + + /** Returns the value of the OECF (34856) tag. */ + public int[] getOECF() { + return _oecf; + } + + /** Returns the value of the PAGENAME (285) tag. */ + public String getPageName() { + return _pageName; + } + + /** Returns the value of the PAGENUMBER (297) tag. */ + public int[] getPageNumber() { + return _pageNumber; + } + + /** Returns the value of the PIXELINTENSITYRANGE (34027) tag. */ + public int[] getPixelIntensityRange() { + return _pixelIntensityRange; + } + + /** Returns the value of the RASTERPADDING (34019) tag. */ + public int getRasterPadding() { + return _rasterPadding; + } + + /** Returns the value of the SECURITYCLASSIFICATION (37394) tag. */ + public String getSecurityClasssification() { + return _securityClassification; + } + + /** Returns the value of the SELFTIMERMODE (34859) tag. */ + public int getSelfTimerMode() { + return _selfTimerMode; + } + + /** Returns the value of the SHUTTERSPEEDVALUE (37377) tag. */ + public Rational getShutterSpeedValue() { + return _shutterSpeedValue; + } + + /** Returns the value of the SITE (34016) tag. */ + public String getSite() { + return _site; + } + + /** Returns the value of the SPATIALFREQUENCYRESPONSE (37388) tag. */ + public int[] getSpatialFrequencyResponse() { + return _spatialFrequencyResponse; + } + + /** Returns the value of the SPECTRALSENSITIVITY (34852) tag. */ + public String getSpectralSensitivity() { + return _spectralSensitivity; + } + + /** Returns the value of the STRIPROWCOUNTS (559) tag. */ + public long[] getStripRowCounts() { + return _stripRowCounts; + } + + /** Returns the value of the SUBIFDS (330) tag. */ + public long[] getSubIFDs() { + return _subIFDs; + } + + /** Returns the value of the SUBJECTLOCATION (37396) tag. */ + public int[] getSubjectLocation() { + return _subjectLocation; + } + + /** Returns the value of the T4OPTIONS (292) tag. */ + public long getT4Options() { + return _t4Options; + } + + /** Returns the value of the T6OPTIONS (293) tag. */ + public long getT6Options() { + return _t6Options; + } + + /** Returns the Exif IFD object, or null if none. */ + public ExifIFD getTheExifIFD() { + return _theExifIFD; + } + + /** Returns the GPS info IFD object, or null if none. */ + public GPSInfoIFD getTheGPSInfoIFD() { + return _theGPSInfoIFD; + } + + /** Returns the Interoperability IFD object, or null if none. */ + public InteroperabilityIFD getTheInteroperabilityIFD() { + return _theInteroperabilityIFD; + } + + /** Returns the GlobalParameters IFD object, or null if none. */ + public GlobalParametersIFD getTheGlobalParametersIFD() { + return _theGlobalParametersIFD; + } + + /** Returns the value of the THRESHHOLDING (263) tag. */ + public int getThreshholding() { + return _threshholding; + } + + /** Returns the value of the TIFFEPSTANDARDID (37398) tag. */ + public String getTIFFEPStandardID() { + return _tiffEPStandardID; + } + + /** Returns the value of the TIMEZONEOFFSET (34858) tag. */ + public int[] getTimeZoneOffset() { + return _timeZoneOffset; + } + + /** Returns the value of the TRANSPARENCYINDICATOR (34028) tag. */ + public int getTransparencyIndicator() { + return _transparencyIndicator; + } + + /** Returns the value of the XCLIPPATHUNITS (344) tag. */ + public long getXClipPathUnits() { + return _xClipPathUnits; + } + + /** Returns the value of the XPOSITION (286) tag. */ + public Rational getXPosition() { + return _xPosition; + } + + /** Returns the value of the XPOSITION (287) tag. */ + public Rational getYPosition() { + return _yPosition; + } + + /** Returns the value of the DNGVERSION (50706) tag. */ + public int[] getDNGVersion() { + return _dngVersion; + } + + /** Returns the value of the DNG UNIQUECAMERAMODEL (50708) tag. */ + public String getUniqueCameraModel() { + return _uniqueCameraModel; + } + + /** Returns the value of the CFAPlaneColor (50710) tag. */ + public int[] getCFAPlaneColor() { + return _cfaPlaneColor; + } + + /** Returns the value of the AsShotNeutral (50728) tag. */ + public Rational[] getAsShotNeutral() { + return _asShotNeutral; + } + + /** Returns the value of the AsShotWhiteXY (50729) tag. */ + public Rational[] getAsShotWhiteXY() { + return _asShotWhiteXY; + } + + /** Get the IFD properties. */ + public Property getProperty(boolean rawOutput) throws TiffException { + List entries = new LinkedList(); + // This function has gotten obscenely large. Split it up. + addNisoProperties(entries, rawOutput); + addMiscProperties(entries, rawOutput); + addTiffITProperties(entries, rawOutput); + addTiffEPProperties(entries, rawOutput); + addGeoTiffProperties(entries, rawOutput); + addTiffFXProperties(entries, rawOutput); + addDNGProperties(entries, rawOutput); + return propertyHeader("TIFF", entries); + } + + private void addNisoProperties(List entries, boolean rawOutput) { + entries.add(new Property("NisoImageMetadata", PropertyType.NISOIMAGEMETADATA, _niso)); + } + + /* Add non-NISO properties. */ + private void addMiscProperties(List entries, boolean rawOutput) { + if (_imageDescription != null) { + entries.add(new Property("ImageDescription", PropertyType.STRING, _imageDescription)); + } + if (_dateTime != null) { + entries.add(new Property("DateTime", PropertyType.STRING, _dateTime)); + } + if (_newSubfileType != 0L || rawOutput) { + entries.add( + addBitmaskProperty("NewSubfileType", _newSubfileType, NEWSUBFILETYPE_L, rawOutput)); + } else { + // if 0, always report as a raw number + entries.add(new Property("NewSubfileType", PropertyType.LONG, _newSubfileType)); + } + if (_subfileType != NULL && (_subfileType != 0 || rawOutput)) { + entries.add(addIntegerProperty("SubfileType", _subfileType - 1, SUBFILETYPE_L, rawOutput)); + } else if (_subfileType != NULL) { + // if 0, always report as a raw number + entries.add(new Property("SubfileType", PropertyType.LONG, new Long(_subfileType))); + } + if (_documentName != null) { + entries.add(new Property("DocumentName", PropertyType.STRING, _documentName)); + } + if (_pageName != null) { + entries.add(new Property("PageName", PropertyType.STRING, _pageName)); + } + if (_pageNumber != null) { + entries.add( + new Property("PageNumber", PropertyType.INTEGER, PropertyArity.ARRAY, _pageNumber)); + } + if (_xPosition != null) { + entries.add(addRationalProperty("XPosition", _xPosition, rawOutput)); + } + if (_yPosition != null) { + entries.add(addRationalProperty("YPosition", _yPosition, rawOutput)); + } + if (_copyright != null) { + entries.add(new Property("Copyright", PropertyType.STRING, _copyright)); + } + if (_fillOrder != NULL) { + entries.add(addIntegerProperty("FillOrder", _fillOrder, FILLORDER_L, rawOutput)); + } + entries.add( + new Property("SampleFormat", PropertyType.INTEGER, PropertyArity.ARRAY, _sampleFormat)); + if (_minSampleValue != null) { + entries.add( + new Property( + "MinSampleValue", PropertyType.INTEGER, PropertyArity.ARRAY, _minSampleValue)); + } + if (_maxSampleValue != null) { + entries.add( + new Property( + "MaxSampleValue", PropertyType.INTEGER, PropertyArity.ARRAY, _maxSampleValue)); + } + if (_inkSet != NULL) { + entries.add(addIntegerProperty("InkSet", _inkSet, INKSET_L, rawOutput)); + } + if (_numberOfInks != NULL) { + entries.add(new Property("NumberOfInks", PropertyType.INTEGER, _numberOfInks)); + } + if (_inkNames != null) { + entries.add(new Property("InkNames", PropertyType.STRING, PropertyArity.ARRAY, _inkNames)); + } + if (_dotRange != null) { + entries.add(new Property("DotRange", PropertyType.INTEGER, PropertyArity.ARRAY, _dotRange)); + } + if (_targetPrinter != null) { + entries.add(new Property("TargetPrinter", PropertyType.STRING, _targetPrinter)); + } + if (_halftoneHints != null) { + entries.add( + new Property("HalftoneHints", PropertyType.INTEGER, PropertyArity.ARRAY, _halftoneHints)); + } + if (_cellLength != NULL) { + entries.add(new Property("CellLength", PropertyType.INTEGER, _cellLength)); + } + if (_cellWidth != NULL) { + entries.add(new Property("CellWidth", PropertyType.INTEGER, _cellWidth)); + } + if (_transferFunction) { + entries.add(new Property("TransferFunction", PropertyType.BOOLEAN, Boolean.TRUE)); + } + if (_transferRange != null) { + entries.add( + new Property("TransferRange", PropertyType.INTEGER, PropertyArity.ARRAY, _transferRange)); + } + entries.add(new Property("Threshholding", PropertyType.INTEGER, _threshholding)); + if (_predictor != NULL) { + entries.add(addIntegerProperty("Predictor", _predictor, PREDICTOR_L, rawOutput)); + } + if (_t4Options != NULL) { + entries.add(new Property("T4Options", PropertyType.LONG, _t4Options)); + } + if (_t6Options != NULL) { + entries.add(new Property("T6Options", PropertyType.LONG, _t6Options)); + } + if (_jpegProc != NULL) { + entries.add(addIntegerProperty("JPEGProc", _jpegProc, JPEGPROC_L, JPEGPROC_INDEX, rawOutput)); + } + if (_jpegInterchangeFormat != NULL) { + entries.add(new Property("JPEGInterchangeFormat", PropertyType.LONG, _jpegInterchangeFormat)); + } + if (_jpegInterchangeFormatLength != NULL) { + entries.add( + new Property( + "JPEGInterchangeFormatLength", PropertyType.LONG, _jpegInterchangeFormatLength)); + } + if (_jpegRestartInterval != NULL) { + entries.add(new Property("JPEGRestartInterval", PropertyType.INTEGER, _jpegRestartInterval)); + } + if (_jpegLosslessPredictors != null) { + entries.add( + addIntegerArrayProperty( + "JPEGLosslessPredictors", + _jpegLosslessPredictors, + JPEGLOSSLESSPREDICTORS_L, + rawOutput)); + } + if (_jpegPointTransforms != null) { + entries.add( + new Property( + "JPEGPointTransforms", + PropertyType.INTEGER, + PropertyArity.ARRAY, + _jpegPointTransforms)); + } + if (_jpegQTables != null) { + entries.add( + new Property("JPEGQTables", PropertyType.LONG, PropertyArity.ARRAY, _jpegQTables)); + } + if (_jpegDCTables != null) { + entries.add( + new Property("JPEGDCTables", PropertyType.LONG, PropertyArity.ARRAY, _jpegDCTables)); + } + if (_jpegACTables != null) { + entries.add( + new Property("JPEGACTables", PropertyType.LONG, PropertyArity.ARRAY, _jpegACTables)); + } + if (_jpegTables != null) { + entries.add( + new Property("JPEGTables", PropertyType.INTEGER, PropertyArity.ARRAY, _jpegTables)); + } + if (_imageSourceData != null) { + entries.add( + new Property( + "ImageSourceData", PropertyType.INTEGER, PropertyArity.ARRAY, _imageSourceData)); + } + if (_photoshopProperties != null) { + entries.add( + new Property( + "PhotoshopProperties", + PropertyType.INTEGER, + PropertyArity.ARRAY, + _photoshopProperties)); + } + if (_annotations != null) { + entries.add( + new Property("Annotations", PropertyType.INTEGER, PropertyArity.ARRAY, _annotations)); + } + if (_clipPath != null) { + entries.add(new Property("ClipPath", PropertyType.INTEGER, PropertyArity.ARRAY, _clipPath)); + } + if (_xClipPathUnits != NULL) { + entries.add(new Property("XClipPathUnits", PropertyType.LONG, _xClipPathUnits)); + } + if (_yClipPathUnits != NULL) { + entries.add(new Property("YClipPathUnits", PropertyType.LONG, _yClipPathUnits)); + } + if (_cleanFaxData != NULL) { + entries.add(new Property("CleanFaxData", PropertyType.LONG, new Long(_cleanFaxData))); + } + if (_badFaxLines != NULL) { + entries.add(new Property("BadFaxLines", PropertyType.LONG, _badFaxLines)); + } + if (_consecutiveBadFaxLines != NULL) { + entries.add( + new Property("ConsecutiveBadFaxLines", PropertyType.LONG, _consecutiveBadFaxLines)); + } + if (_freeByteCounts != null) { + entries.add( + new Property("FreeByteCounts", PropertyType.LONG, PropertyArity.ARRAY, _freeByteCounts)); + } + if (_freeOffsets != null) { + entries.add( + new Property("FreeOffsets", PropertyType.LONG, PropertyArity.ARRAY, _freeOffsets)); + } + } + + private void addTiffITProperties(List entries, boolean rawOutput) { + /* Add TIFF/IT properties. */ + + List itList = new LinkedList(); + if (_site != null) { + itList.add(new Property("Site", PropertyType.STRING, _site)); + } + if (_colorTable != null) { + itList.add( + new Property("ColorTable", PropertyType.INTEGER, PropertyArity.ARRAY, _colorTable)); + } + itList.add( + addIntegerProperty( + "BackgroundColorIndicator", + _backgroundColorIndicator, + BACKGROUNDCOLORINDICATOR_L, + rawOutput)); + if (_backgroundColorValue != NULL) { + itList.add(new Property("BackgroundColorValue", PropertyType.INTEGER, _backgroundColorValue)); + } + itList.add( + addIntegerProperty( + "ImageColorIndicator", _imageColorIndicator, IMAGECOLORINDICATOR_L, rawOutput)); + itList.add( + addIntegerProperty( + "TransparencyIndicator", _transparencyIndicator, TRANSPARENCYINDICATOR_L, rawOutput)); + if (_imageColorValue != NULL) { + itList.add(new Property("ImageColorValue", PropertyType.INTEGER, _imageColorValue)); + } + if (_colorCharacterization != null) { + itList.add( + new Property("ColorCharacterization", PropertyType.STRING, _colorCharacterization)); + } + if (_colorSequence != null) { + itList.add(new Property("ColorSequence", PropertyType.STRING, _colorSequence)); + } + if (_hcUsage != NULL) { + itList.add(addBitmaskProperty("HCUsage", _hcUsage, HCUSAGE_L, rawOutput)); + } + if (_it8Header != null) { + itList.add(new Property("IT8Header", PropertyType.STRING, _it8Header)); + } + if (_pixelIntensityRange != null) { + itList.add( + new Property( + "PixelIntensityRange", + PropertyType.INTEGER, + PropertyArity.ARRAY, + _pixelIntensityRange)); + } + itList.add(addIntegerProperty("RasterPadding", _rasterPadding, RASTERPADDING_L, rawOutput)); + itList.add(new Property("BitsPerRunLength", PropertyType.INTEGER, _bitsPerRunLength)); + itList.add( + new Property("BitsPerExtendedRunLength", PropertyType.INTEGER, _bitsPerExtendedRunLength)); + entries.add( + new Property("TIFFITProperties", PropertyType.PROPERTY, PropertyArity.LIST, itList)); + } + + /* Add TIFF/EP properties. */ + private void addTiffEPProperties(List entries, boolean rawOutput) { + List epList = new LinkedList(); + if (_cfaRepeatPatternDim != null) { + epList.add( + new Property( + "CFARepeatPatternDim", + PropertyType.INTEGER, + PropertyArity.ARRAY, + _cfaRepeatPatternDim)); + } + if (_cfaPattern != null) { + epList.add( + new Property("CFAPattern", PropertyType.INTEGER, PropertyArity.ARRAY, _cfaPattern)); + } + if (_batteryLevel != null) { + epList.add(new Property("BatteryLevel", PropertyType.STRING, _batteryLevel)); + } + if (_iptc != null) { + epList.add(new Property("IPTCNAA", PropertyType.LONG, PropertyArity.ARRAY, _iptc)); + } + if (_iccProfile != null) { + epList.add(new Property("ICCProfile", PropertyType.BOOLEAN, Boolean.TRUE)); + } + if (_exposureProgram != NULL) { + epList.add( + addIntegerProperty("ExposureProgram", _exposureProgram, EXPOSUREPROGRAM_L, rawOutput)); + } + if (_spectralSensitivity != null) { + epList.add(new Property("SpectralSensitivity", PropertyType.STRING, _spectralSensitivity)); + } + if (_isoSpeedRatings != null) { + epList.add( + new Property( + "ISOSpeedRatings", PropertyType.INTEGER, PropertyArity.ARRAY, _isoSpeedRatings)); + } + if (_oecf != null) { + epList.add(new Property("OECF", PropertyType.INTEGER, PropertyArity.ARRAY, _oecf)); + } + if (_interlace != NULL) { + epList.add(new Property("Interlace", PropertyType.INTEGER, _interlace)); + } + if (_timeZoneOffset != null) { + epList.add( + new Property( + "TimeZoneOffset", PropertyType.INTEGER, PropertyArity.ARRAY, _timeZoneOffset)); + } + if (_selfTimerMode != NULL) { + epList.add(new Property("SelfTimerMode", PropertyType.INTEGER, _selfTimerMode)); + } + if (_compressedBitsPerPixel != null) { + epList.add(addRationalProperty("CompressedBitsPerPixel", _compressedBitsPerPixel, rawOutput)); + } + if (_shutterSpeedValue != null) { + epList.add(addRationalProperty("ShutterSpeedValue", _shutterSpeedValue, rawOutput)); + } + if (_aperatureValue != null) { + epList.add(addRationalProperty("AperatureValue", _aperatureValue, rawOutput)); + } + if (_maxAperatureValue != null) { + epList.add(addRationalProperty("MaxAperatureValue", _maxAperatureValue, rawOutput)); + } + if (_flash != NULL) { + epList.add(addIntegerProperty("FLASH", _flash, FLASH_L, FLASH_INDEX, rawOutput)); + } + if (_spatialFrequencyResponse != null) { + epList.add( + new Property( + "SpatialFrequencyResponse", + PropertyType.INTEGER, + PropertyArity.ARRAY, + _spatialFrequencyResponse)); + } + if (_noise != null) { + epList.add(new Property("Noise", PropertyType.INTEGER, PropertyArity.ARRAY, _noise)); + } + if (_focalPlaneXResolution != null) { + epList.add(addRationalProperty("FocalPlaneXResolution", _focalPlaneXResolution, rawOutput)); + } + if (_focalPlaneYResolution != null) { + epList.add(addRationalProperty("FocalPlaneYResolution", _focalPlaneYResolution, rawOutput)); + } + if (_focalPlaneResolutionUnit != NULL) { + epList.add( + addIntegerProperty( + "FocalPlaneResolutionUnit", + _focalPlaneResolutionUnit, + FOCALPLANERESOLUTIONUNIT_L, + rawOutput)); + } + if (_imageNumber != NULL) { + epList.add(new Property("ImageNumber", PropertyType.LONG, _imageNumber)); + } + if (_securityClassification != null) { + epList.add( + new Property("SecurityClassification", PropertyType.STRING, _securityClassification)); + } + if (_imageHistory != null) { + epList.add(new Property("ImageHistory", PropertyType.STRING, _imageHistory)); + } + if (_subjectLocation != null) { + epList.add( + new Property( + "SubjectLocation", PropertyType.INTEGER, PropertyArity.ARRAY, _subjectLocation)); + } + if (_tiffEPStandardID != null) { + epList.add(new Property("TIFFEPSStandardID", PropertyType.STRING, _tiffEPStandardID)); + } + if (epList.size() > 0) { + entries.add( + new Property("TIFFEPProperties", PropertyType.PROPERTY, PropertyArity.LIST, epList)); + } + + if (_xmpProp != null) { + entries.add(_xmpProp); + } + } + + private void addGeoTiffProperties(List entries, boolean rawOutput) + throws TiffException { + /* Add GeoTIFF properties. */ + + List dirList = new LinkedList(); + if (_geoKeyDirectoryTag != null) { + dirList.add(new Property("Version", PropertyType.INTEGER, _geoKeyDirectoryTag[0])); + dirList.add( + new Property( + "Revision", + PropertyType.STRING, + Integer.toString(_geoKeyDirectoryTag[1]) + + "." + + Integer.toString(_geoKeyDirectoryTag[2]))); + dirList.add(new Property("NumberOfKeys", PropertyType.INTEGER, _geoKeyDirectoryTag[3])); + for (int i = 0; i < _geoKeyDirectoryTag[3]; i++) { + int j = i * 4 + 4; + int key = _geoKeyDirectoryTag[j]; + int location = _geoKeyDirectoryTag[j + 1]; + int count = _geoKeyDirectoryTag[j + 2]; + int offset = _geoKeyDirectoryTag[j + 3]; + + int ival = 0; + double dval = 0.0; + String sval = "NULL"; + switch (location) { + case 0: + ival = offset; + break; + case 34736: + dval = _geoDoubleParamsTag[offset]; + break; + case 34737: + try { + sval = _geoAsciiParamsTag.substring(offset, offset + count - 1); + } catch (Exception e) { + throw new TiffException(MessageConstants.TIFF_HUL_9); + } + break; + default: + break; + } + + switch (key) { + case GTMODELTYPEGEOKEY: + dirList.add( + addIntegerProperty( + "GTModelType", + ival, + GeoTiffStrings.MODELTYPE, + GeoTiffStrings.MODELTYPE_INDEX, + rawOutput)); + break; + case GTRASTERTYPEGEOKEY: + dirList.add( + addIntegerProperty( + "GTRasterType", + ival, + GeoTiffStrings.RASTERTYPE, + GeoTiffStrings.RASTERTYPE_INDEX, + rawOutput)); + break; + case GTCITATIONGEOKEY: + dirList.add(new Property("GTCitation", PropertyType.STRING, sval)); + break; + case GEOGRAPHICTYPEGEOKEY: + dirList.add( + addIntegerProperty( + "GeographicType", + ival, + GeoTiffStrings.GEOGRAPHICS, + GeoTiffStrings.GEOGRAPHICS_INDEX, + rawOutput)); + break; + case GEOGCITATIONGEOKEY: + dirList.add(new Property("GeogCitation", PropertyType.STRING, sval)); + break; + case GEOGGEODETICDATUMGEOKEY: + dirList.add( + addIntegerProperty( + "GeogGeodeticDatum", + ival, + GeoTiffStrings.GEODETICDATUM, + GeoTiffStrings.GEODETICDATUM_INDEX, + rawOutput)); + break; + case GEOGPRIMEMERIDIANGEOKEY: + dirList.add( + addIntegerProperty( + "GeogPrimeMeridian", + ival, + GeoTiffStrings.PRIMEMERIDIAN, + GeoTiffStrings.PRIMEMERIDIAN_INDEX, + rawOutput)); + break; + case GEOGPRIMEMERIDIANLONGGEOKEY: + dirList.add(new Property("GeogPrimeMeridianLong", PropertyType.DOUBLE, dval)); + break; + case GEOGLINEARUNITSGEOKEY: + dirList.add( + addIntegerProperty( + "GeogLinearUnits", + ival, + GeoTiffStrings.LINEARUNITS, + GeoTiffStrings.LINEARUNITS_INDEX, + rawOutput)); + break; + case GEOGLINEARUNITSIZEGEOKEY: + dirList.add(new Property("GeogLinearUnitSize", PropertyType.DOUBLE, dval)); + break; + case GEOGANGULARUNITSGEOKEY: + dirList.add( + addIntegerProperty( + "GeogAngularUnits", + ival, + GeoTiffStrings.ANGULARUNITS, + GeoTiffStrings.ANGULARUNITS_INDEX, + rawOutput)); + break; + case GEOGANGULARUNITSIZEGEOKEY: + dirList.add(new Property("GeogAngularUnitSize", PropertyType.DOUBLE, dval)); + break; + case GEOGELLIPSOIDGEOKEY: + dirList.add( + addIntegerProperty( + "GeogEllipsoid", + ival, + GeoTiffStrings.ELLIPSOID, + GeoTiffStrings.ELLIPSOID_INDEX, + rawOutput)); + break; + case GEOGSEMIMAJORAXISGEOKEY: + dirList.add(new Property("GeogSemiMajorAxis", PropertyType.DOUBLE, dval)); + break; + case GEOGSEMIMINORAXISGEOKEY: + dirList.add(new Property("GeogSemiMinorAxis", PropertyType.DOUBLE, dval)); + break; + case GEOGINVFLATTENINGGEOKEY: + dirList.add(new Property("GeogInvFlattening", PropertyType.DOUBLE, dval)); + break; + case GEOGAZIMUTHUNITSGEOKEY: + dirList.add( + addIntegerProperty( + "GeogAzimuthUnits", + ival, + GeoTiffStrings.ANGULARUNITS, + GeoTiffStrings.ANGULARUNITS_INDEX, + rawOutput)); + break; + case PROJECTEDCSTYPEGEOKEY: + dirList.add( + addIntegerProperty( + "ProjectedCSType", + ival, + GeoTiffStrings.PROJECTEDCSTYPE, + GeoTiffStrings.PROJECTEDCSTYPE_INDEX, + rawOutput)); + break; + case PCSCITATIONGEOKEY: + dirList.add(new Property("PCSCitation", PropertyType.STRING, sval)); + break; + case PROJECTIONGEOKEY: + dirList.add( + addIntegerProperty( + "Projection", + ival, + GeoTiffStrings.PROJECTION, + GeoTiffStrings.PROJECTION_INDEX, + rawOutput)); + break; + case PROJCOORDTRANSGEOKEY: + dirList.add( + addIntegerProperty( + "ProjCoordTrans", + ival, + GeoTiffStrings.COORDINATETRANSFORMATION, + GeoTiffStrings.COORDINATETRANSFORMATION_INDEX, + rawOutput)); + break; + case PROJLINEARUNITSGEOKEY: + dirList.add( + addIntegerProperty( + "ProjLinearUnits", + ival, + GeoTiffStrings.LINEARUNITS, + GeoTiffStrings.LINEARUNITS_INDEX, + rawOutput)); + break; + case PROJLINEARUNITSIZEGEOKEY: + dirList.add(new Property("ProjLinearUnitSize", PropertyType.DOUBLE, dval)); + break; + case PROJSTDPARALLEL1GEOKEY: + dirList.add(new Property("ProjStdParallel1", PropertyType.DOUBLE, dval)); + break; + case PROJSTDPARALLEL2GEOKEY: + dirList.add(new Property("ProjStdParallel2", PropertyType.DOUBLE, dval)); + break; + case PROJNATORIGINLONGGEOKEY: + dirList.add(new Property("ProjNatOriginLong", PropertyType.DOUBLE, dval)); + break; + case PROJNATORIGINLATGEOKEY: + dirList.add(new Property("ProjNatOriginLat", PropertyType.DOUBLE, dval)); + break; + case PROJFALSEEASTINGGEOKEY: + dirList.add(new Property("ProjFalseEasting", PropertyType.DOUBLE, dval)); + break; + case PROJFALSENORTHINGGEOKEY: + dirList.add(new Property("ProjFalseNorthing", PropertyType.DOUBLE, dval)); + break; + case PROJFALSEORIGINLONGGEOKEY: + dirList.add(new Property("ProjFalseOriginLong", PropertyType.DOUBLE, dval)); + break; + case PROJFALSEORIGINLATGEOKEY: + dirList.add(new Property("ProjFalseOriginLat", PropertyType.DOUBLE, dval)); + break; + case PROJFALSEORIGINEASTINGGEOKEY: + dirList.add(new Property("ProjFalseOriginEasting", PropertyType.DOUBLE, dval)); + break; + case PROJFALSEORIGINNORTHINGGEOKEY: + case PROJFALSEORIGINNORTHINGGEOKEY_2: + dirList.add(new Property("ProjFalseOriginNorthing", PropertyType.DOUBLE, dval)); + break; + case PROJCENTERLONGGEOKEY: + dirList.add(new Property("ProjCenterLong", PropertyType.DOUBLE, dval)); + break; + case PROJCENTERLATGEOKEY: + dirList.add(new Property("ProjCenterLat", PropertyType.DOUBLE, dval)); + break; + case PROJCENTEREASTINGGEOKEY: + dirList.add(new Property("ProjCenterEasting", PropertyType.DOUBLE, dval)); + break; + case PROJSCALEATNATORIGINGEOKEY: + dirList.add(new Property("ProjScaleAtNatOrigin", PropertyType.DOUBLE, dval)); + break; + case PROJSCALEATCENTERGEOKEY: + dirList.add(new Property("ProjScaleAtCenter", PropertyType.DOUBLE, dval)); + break; + case PROJAZIMUTHANGLEGEOKEY: + dirList.add(new Property("ProjAzimuthAngle", PropertyType.DOUBLE, dval)); + break; + case PROJSTRAIGHTVERTPOLELONGEOKEY: + dirList.add(new Property("ProjStraightVertPoleLong", PropertyType.DOUBLE, dval)); + break; + case VERTICALCSTYPEGEOKEY: + dirList.add( + addIntegerProperty( + "VerticalCSType", + ival, + GeoTiffStrings.VERTICALCSTYPE, + GeoTiffStrings.VERTICALCSTYPE_INDEX, + rawOutput)); + break; + case VERTICALCITATIONGEOKEY: + dirList.add(new Property("VerticalCitation", PropertyType.STRING, sval)); + break; + case VERTICALDATUMGEOKEY: + dirList.add( + addIntegerProperty( + "VerticalDatum", + ival, + GeoTiffStrings.VERTICALCSDATUM, + GeoTiffStrings.VERTICALCSDATUM_INDEX, + rawOutput)); + break; + case VERTICALUNITSGEOKEY: + dirList.add( + addIntegerProperty( + "VerticalUnits", + ival, + GeoTiffStrings.LINEARUNITS, + GeoTiffStrings.LINEARUNITS_INDEX, + rawOutput)); + break; + default: + break; + } + } + } + List geoList = new LinkedList(); + if (dirList.size() > 0) { + geoList.add( + new Property("GeoKeyDirectory", PropertyType.PROPERTY, PropertyArity.LIST, dirList)); + } + + if (_modelTiepointTag != null) { + geoList.add( + new Property( + "ModelTiepointTag", PropertyType.DOUBLE, PropertyArity.ARRAY, _modelTiepointTag)); + } + if (_modelPixelScaleTag != null) { + geoList.add( + new Property( + "ModelPixelScaleTag", PropertyType.DOUBLE, PropertyArity.ARRAY, _modelPixelScaleTag)); + } + if (_modelTransformationTag != null) { + geoList.add( + new Property( + "ModelTransformationTag", + PropertyType.DOUBLE, + PropertyArity.ARRAY, + _modelTransformationTag)); + } + + if (geoList.size() > 0) { + entries.add( + new Property("GeoTIFFProperties", PropertyType.PROPERTY, PropertyArity.LIST, geoList)); + } + } + + /* Add Tiff/FX properties */ + private void addTiffFXProperties(List entries, boolean rawOutput) { + if (_stripRowCounts != null) { + entries.add( + new Property("StripRowCounts", PropertyType.LONG, PropertyArity.ARRAY, _stripRowCounts)); + } + if (_imageLayer != null) { + // Do up ImageLayer as a property with two subproperties. + Property[] layerProps = new Property[2]; + try { + layerProps[0] = addIntegerProperty("LayerType", _imageLayer[0], IMAGELAYER_L, rawOutput); + layerProps[1] = new Property("OrdinalNumber", PropertyType.INTEGER, _imageLayer[1]); + + entries.add( + new Property("ImageLayer", PropertyType.PROPERTY, PropertyArity.ARRAY, layerProps)); + } + // Don't blow up on incorrect array size + catch (Exception e) { + } + } + } + + /* Adds DNG properties. */ + private void addDNGProperties(List entries, boolean rawOutput) { + setDNGDefaults(); + List dngList = new LinkedList(); + if (_dngVersion != null) { + dngList.add( + new Property("DNGVersion", PropertyType.INTEGER, PropertyArity.ARRAY, _dngVersion)); + } + if (_dngBackwardVersion != null) { + dngList.add( + new Property( + "DNGBackwardVersion", + PropertyType.INTEGER, + PropertyArity.ARRAY, + _dngBackwardVersion)); + } + if (_uniqueCameraModel != null) { + dngList.add(new Property("UniqueCameraModel", PropertyType.STRING, _uniqueCameraModel)); + } + if (_localizedCameraModel != null) { + dngList.add(new Property("LocalizedCameraModel", PropertyType.STRING, _localizedCameraModel)); + } + if (_cfaPlaneColor != null) { + dngList.add( + new Property("CFAPlaneColor", PropertyType.INTEGER, PropertyArity.ARRAY, _cfaPlaneColor)); + } + if (_cfaLayout != NULL) { + dngList.add(addIntegerProperty("CFALayout", _cfaLayout, CFALAYOUT_L, rawOutput)); + } + if (_linearizationTable != null) { + dngList.add( + new Property( + "LinearizationTable", + PropertyType.INTEGER, + PropertyArity.ARRAY, + _linearizationTable)); + } + if (_blackLevelRepeatDim != null) { + dngList.add( + new Property( + "BlackLevelRepeatDim", + PropertyType.INTEGER, + PropertyArity.ARRAY, + _blackLevelRepeatDim)); + } + if (_blackLevel != null) { + dngList.add( + new Property("BlackLevel", PropertyType.RATIONAL, PropertyArity.ARRAY, _blackLevel)); + } + if (_blackLevelDeltaH != null) { + dngList.add( + new Property( + "BlackLevelDeltaH", PropertyType.RATIONAL, PropertyArity.ARRAY, _blackLevelDeltaH)); + } + if (_blackLevelDeltaV != null) { + dngList.add( + new Property( + "BlackLevelDeltaV", PropertyType.RATIONAL, PropertyArity.ARRAY, _blackLevelDeltaV)); + } + if (_whiteLevel != null) { + dngList.add(new Property("WhiteLevel", PropertyType.LONG, PropertyArity.ARRAY, _whiteLevel)); + } + if (_defaultScale != null) { + dngList.add( + new Property("DefaultScale", PropertyType.RATIONAL, PropertyArity.ARRAY, _defaultScale)); + } + if (_bestQualityScale != null) { + dngList.add(new Property("BestQualityScale", PropertyType.RATIONAL, _bestQualityScale)); + } + if (_defaultCropOrigin != null) { + dngList.add( + new Property( + "DefaultCropOrigin", PropertyType.RATIONAL, PropertyArity.ARRAY, _defaultCropOrigin)); + } + if (_defaultCropSize != null) { + dngList.add( + new Property( + "DefaultCropSize", PropertyType.RATIONAL, PropertyArity.ARRAY, _defaultCropSize)); + } + if (_calibrationIlluminant1 != NULL) { + dngList.add( + new Property("CalibrationIlluminant1", PropertyType.INTEGER, _calibrationIlluminant1)); + } + if (_calibrationIlluminant2 != NULL) { + dngList.add( + new Property("CalibrationIlluminant2", PropertyType.INTEGER, _calibrationIlluminant2)); + } + if (_colorMatrix1 != null) { + dngList.add( + new Property("ColorMatrix1", PropertyType.RATIONAL, PropertyArity.ARRAY, _colorMatrix1)); + } + if (_colorMatrix2 != null) { + dngList.add( + new Property("ColorMatrix2", PropertyType.RATIONAL, PropertyArity.ARRAY, _colorMatrix2)); + } + if (_cameraCalibration1 != null) { + dngList.add( + new Property( + "CameraCalibration1", + PropertyType.RATIONAL, + PropertyArity.ARRAY, + _cameraCalibration1)); + } + if (_cameraCalibration2 != null) { + dngList.add( + new Property( + "CameraCalibration2", + PropertyType.RATIONAL, + PropertyArity.ARRAY, + _cameraCalibration2)); + } + if (_reductionMatrix1 != null) { + dngList.add( + new Property( + "ReductionMatrix1", PropertyType.RATIONAL, PropertyArity.ARRAY, _reductionMatrix1)); + } + if (_reductionMatrix2 != null) { + dngList.add( + new Property( + "ReductionMatrix2", PropertyType.RATIONAL, PropertyArity.ARRAY, _reductionMatrix2)); + } + if (_analogBalance != null) { + dngList.add( + new Property( + "AnalogBalance", PropertyType.RATIONAL, PropertyArity.ARRAY, _analogBalance)); + } + if (_asShotNeutral != null) { + dngList.add( + new Property( + "AsShotNeutral", PropertyType.RATIONAL, PropertyArity.ARRAY, _asShotNeutral)); + } + if (_asShotWhiteXY != null) { + dngList.add( + new Property( + "AsShotWhiteXY", PropertyType.RATIONAL, PropertyArity.ARRAY, _asShotWhiteXY)); + } + if (_baselineExposure != null) { + dngList.add(new Property("BaselineExposure", PropertyType.RATIONAL, _baselineExposure)); + } + if (_baselineNoise != null) { + dngList.add(new Property("BaselineNoise", PropertyType.RATIONAL, _baselineNoise)); + } + if (_baselineNoise != null) { + dngList.add(new Property("BaselineSharpness", PropertyType.RATIONAL, _baselineSharpness)); + } + if (_bayerGreenSplit != NULL) { + dngList.add(new Property("BayerGreenSplit", PropertyType.INTEGER, _bayerGreenSplit)); + } + if (_linearResponseLimit != null) { + dngList.add(new Property("LinearResponseLimit", PropertyType.RATIONAL, _linearResponseLimit)); + } + if (_cameraSerialNumber != null) { + dngList.add(new Property("CameraSerialNumber", PropertyType.STRING, _cameraSerialNumber)); + } + if (_lensInfo != null) { + dngList.add(new Property("LensInfo", PropertyType.RATIONAL, PropertyArity.ARRAY, _lensInfo)); + } + if (_chromaBlurRadius != null) { + dngList.add(new Property("ChromaBlurRadius", PropertyType.RATIONAL, _chromaBlurRadius)); + } + if (_antiAliasStrength != null) { + dngList.add(new Property("AntiAliasStrength", PropertyType.RATIONAL, _antiAliasStrength)); + } + if (_dngPrivateData != null) { + dngList.add(new Property("DNGPrivateData", PropertyType.INTEGER, _dngPrivateData)); + } + if (_makerNoteSafety != NULL) { + dngList.add( + addIntegerProperty("MakerNoteSafety", _makerNoteSafety, MAKERNOTESAFETY_L, rawOutput)); + } + if (dngList.size() > 0) { + entries.add( + new Property("DNGProperties", PropertyType.PROPERTY, PropertyArity.LIST, dngList)); + } + } + + /** + * ColorPlanes is an undefined primary in the TIFF spec. It's actually a non-obvious definition + * which is something of a pain to calculate. + */ + private int calcColorPlanes() { + if (_photometricInterpretation == TiffProfileDNG.CFA) { + // In this case, it's the number of unique colors + // in the CFA pattern. (You mean you didn't already + // know that?) + int nUnique = 0; + if (_cfaPattern == null) { + // It's broken; return 1 so we don't try to + // allocate zero-length objects. + return 1; + } + int[] uniqueColors = new int[_cfaPattern.length]; + for (int i = 0; i < _cfaPattern.length; i++) { + boolean unique = true; + int color = _cfaPattern[i]; + for (int j = 0; j < nUnique; j++) { + if (color == uniqueColors[j]) { + unique = false; + break; + } + } + if (unique) { + uniqueColors[nUnique++] = color; + } + } + return nUnique; + } + return _niso.getSamplesPerPixel(); + } + + /** + * Set the default values for any DNG tags that haven't been encountered yet. If _dngVersion is + * zero, apply the IFD 0 defaults; if PhotometricInterpretation is CFA or RawLinear, apply the Raw + * IFD defaults. + */ + private void setDNGDefaults() { + if (_dngVersion != null) { + // Apply "IFD 0" defaults + if (_dngBackwardVersion == null) { + _dngBackwardVersion = new int[4]; + // The default value is _dngVersion with the last two + // bytes set to zero. + _dngBackwardVersion[0] = _dngVersion[0]; + _dngBackwardVersion[1] = _dngVersion[1]; + _dngBackwardVersion[2] = 0; + _dngBackwardVersion[3] = 0; + } + + if (_uniqueCameraModel != null && _localizedCameraModel == null) { + _localizedCameraModel = _uniqueCameraModel; + } + + if (_calibrationIlluminant1 == NULL) { + _calibrationIlluminant1 = 0; + } + if (_baselineExposure == null) { + _baselineExposure = new Rational(0, 1); + } + if (_baselineNoise == null) { + _baselineNoise = new Rational(1, 1); + } + if (_baselineSharpness == null) { + _baselineSharpness = new Rational(1, 1); + } + if (_linearResponseLimit == null) { + _linearResponseLimit = new Rational(1, 1); + } + if (_makerNoteSafety == NULL) { + _makerNoteSafety = 0; + } + // There are some IFD 0 defaults which depend on the value of + // "ColorPlanes," which is derived from information in the Raw IFD. + // This would require processing the IFD's out of order, so those + // defaults (AnalogBalance) go unreported. + } + + if (_photometricInterpretation == TiffProfileDNG.CFA + || _photometricInterpretation == TiffProfileDNG.LINEAR_RAW) { + // Apply "Raw IFD" defaults. This really isn't sufficient + // information to establish a file as DNG, but the properties + // are in their own category, so it's fairly harmless to leave + // them in even if it's actually, for example, a TIFF-EP file. + + if (_cfaPlaneColor == null) { + _cfaPlaneColor = new int[] {0, 1, 2}; + } + + if (_cfaLayout == NULL) { + _cfaLayout = 1; + } + + // The size of the LinearizationTable is -- I quote -- N. + // This is NOT useful. Skip that default. + + if (_blackLevelRepeatDim == null) { + _blackLevelRepeatDim = new int[] {1, 1}; + } + + Rational zero = new Rational(0, 1); + if (_blackLevel == null) { + _blackLevel = + new Rational + [_blackLevelRepeatDim[0] * _blackLevelRepeatDim[1] * _niso.getSamplesPerPixel()]; + for (int i = 0; i < _blackLevel.length; i++) { + _blackLevel[i] = zero; + } + } + + if (_blackLevelDeltaH == null) { + _blackLevelDeltaH = new Rational[(int) _niso.getImageWidth()]; + for (int i = 0; i < _blackLevelDeltaH.length; i++) { + _blackLevelDeltaH[i] = zero; + } + } + + if (_blackLevelDeltaV == null) { + _blackLevelDeltaV = new Rational[(int) _niso.getImageLength()]; + for (int i = 0; i < _blackLevelDeltaV.length; i++) { + _blackLevelDeltaV[i] = zero; + } + } + + if (_whiteLevel == null) { + _whiteLevel = new long[_niso.getSamplesPerPixel()]; + long defWhite = (1L << _niso.getBitsPerSample()[0] - 1); + for (int i = 0; i < _whiteLevel.length; i++) { + _whiteLevel[i] = defWhite; + } + } + + Rational one = new Rational(1, 1); + if (_defaultScale == null) { + _defaultScale = new Rational[] {one, one}; + } + + if (_bestQualityScale == null) { + _bestQualityScale = one; + } + + if (_defaultCropOrigin == null) { + _defaultCropOrigin = new Rational[] {zero, zero}; + } + + if (_defaultCropSize == null) { + _defaultCropSize = new Rational[2]; + _defaultCropSize[0] = new Rational(_niso.getImageWidth(), 1); + _defaultCropSize[1] = new Rational(_niso.getImageLength(), 1); + } + int colorPlanes = calcColorPlanes(); + if (_cameraCalibration1 == null) { + // Identity matrix with dimension of ColorPlanes*ColorPlanes + _cameraCalibration1 = identityMatrix(colorPlanes); + } + if (_cameraCalibration2 == null) { + // Identity matrix with dimension of ColorPlanes*ColorPlanes + _cameraCalibration2 = identityMatrix(colorPlanes); + } + if (_bayerGreenSplit == NULL && _photometricInterpretation == TiffProfileDNG.CFA) { + _bayerGreenSplit = 0; + } + if (_antiAliasStrength == null) { + _antiAliasStrength = new Rational(1, 1); + } + } + } + + /** Create a Rational identity matrix of the specified size. */ + private Rational[] identityMatrix(int dim) { + Rational[] val = new Rational[dim * dim]; + // Set them all to zero, then overwrite the diagonal values + // to one. + int i; + for (i = 0; i < dim * dim; i++) { + val[i] = new Rational(0, 1); + } + for (i = 0; i < dim; i++) { + val[dim * i + i] = new Rational(1, 1); + } + return val; + } + + /** Looks up an IFD tag. */ + public void lookupTag(int tag, int type, long count, long value) throws TiffException { + try { + switch (tag) { + case APERTUREVALUE: + checkType(tag, type, RATIONAL); + checkCount(tag, count, 1); + _aperatureValue = readRational(count, value); + break; + case ARTIST: + checkType(tag, type, ASCII); + _niso.setImageProducer(readASCII(count, value)); + if (_version < 5) { + _version = 5; + } + break; + case BACKGROUNDCOLORINDICATOR: + checkType(tag, type, BYTE); + checkCount(tag, count, 1); + _backgroundColorIndicator = readByte(type, count, value); + break; + case BACKGROUNDCOLORVALUE: + checkType(tag, type, BYTE); + checkCount(tag, count, 1); + _backgroundColorValue = readByte(type, count, value); + break; + case BADFAXLINES: + checkType(tag, type, SHORT, LONG); + checkCount(tag, count, 1); + _badFaxLines = readLong(type, count, value); + break; + case BATTERYLEVEL: + checkType(tag, type, RATIONAL, ASCII); + if (type == RATIONAL) { + Rational r = readRational(count, value); + _batteryLevel = Double.toString(r.toDouble()); + } else { + _batteryLevel = readASCII(count, value); + } + break; + case BITSPEREXTENDEDRUNLENGTH: + checkType(tag, type, SHORT); + checkCount(tag, count, 1); + _bitsPerExtendedRunLength = readShort(type, count, value); + break; + case BITSPERRUNLENGTH: + checkType(tag, type, SHORT); + checkCount(tag, count, 1); + _bitsPerRunLength = readShort(type, count, value); + break; + case BITSPERSAMPLE: + checkType(tag, type, SHORT); + checkCountArray(tag, count); + _niso.setBitsPerSample(readShortArray(type, count, value)); + break; + case BRIGHTNESSVALUE: + checkType(tag, type, SRATIONAL); + if (count == 1) { + _niso.setBrightness(readSignedRational(count, value)); + } else { + checkCountArray(tag, count); + Rational[] r = readSignedRationalArray(count, value); + _niso.setBrightness(average(r[0], r[1])); + } + break; + case CELLLENGTH: + checkType(tag, type, SHORT); + _cellLength = readShort(type, count, value); + break; + case CELLWIDTH: + checkType(tag, type, SHORT); + _cellWidth = readShort(type, count, value); + break; + case CFAPATTERN: + checkType(tag, type, BYTE); + checkCountArray(tag, count); + _cfaPattern = readByteArray(type, count, value); + break; + case CFAREPEATPATTERNDIM: + checkType(tag, type, SHORT); + checkCount(tag, count, 2); + _cfaRepeatPatternDim = readShortArray(type, count, value); + break; + case CLEANFAXDATA: + checkType(tag, type, SHORT); + checkCount(tag, count, 1); + _badFaxLines = readShort(type, count, value); + break; + case CLIPPATH: + checkType(tag, type, BYTE); + checkCountArray(tag, count); + _clipPath = readByteArray(type, count, value); + break; + case COLORCHARACTERIZATION: + checkType(tag, type, ASCII); + _colorCharacterization = readASCII(count, value); + break; + case COLORSEQUENCE: + checkType(tag, type, ASCII); + _colorSequence = readASCII(count, value); + break; + case COLORMAP: + { + checkType(tag, type, SHORT); + checkCountArray(tag, count); + int[] colorMap = readShortArray(type, count, value); + int[] bitCode = new int[colorMap.length]; + int[] red = new int[colorMap.length]; + int[] green = new int[colorMap.length]; + int[] blue = new int[colorMap.length]; + int len = colorMap.length / 3; + int len2 = 2 * len; + for (int i = 0; i < len; i++) { + bitCode[i] = i; + red[i] = colorMap[i]; + green[i] = colorMap[i + len]; + blue[i] = colorMap[i + len2]; + } + _niso.setColormapBitCodeValue(bitCode); + _niso.setColormapRedValue(red); + _niso.setColormapGreenValue(green); + _niso.setColormapBlueValue(blue); + if (_version < 5) { + _version = 5; + } + break; + } + case COLORTABLE: + checkType(tag, type, BYTE); + checkCountArray(tag, count); + _colorTable = readByteArray(type, count, value); + break; + case COMPRESSEDBITSPERPIXEL: + checkType(tag, type, RATIONAL); + checkCount(tag, count, 1); + _compressedBitsPerPixel = readRational(count, value); + break; + case COMPRESSION: + checkType(tag, type, SHORT); + checkCount(tag, count, 1); + int scheme = readShort(type, count, value); + _niso.setCompressionScheme(scheme); + if (scheme == 5) { + // Set default predictor if none has been set + if (_predictor == NULL) { + _predictor = 1; + } + if (_version < 5) { + _version = 5; + } + } + if (scheme == 6 && _version < 6) { + _version = 6; + } + if (scheme == 3 && _t4Options == NULL) { + // Set default t4Options only if compression is 3 + _t4Options = 0; + } + if (scheme == 4 && _t6Options == NULL) { + // Set default t6Options only if compression is 4 + _t6Options = 0; + } + if (scheme == 6) { + _info.setMessage(new InfoMessage(MessageConstants.TIFF_HUL_61)); + } + break; + case CONSECUTIVEBADFAXLINES: + checkType(tag, type, SHORT, LONG); + checkCount(tag, count, 1); + _consecutiveBadFaxLines = readLong(type, count, value); + break; + case COPYRIGHT: + checkType(tag, type, ASCII); + _copyright = readASCII(count, value); + if (_version < 6) { + _version = 6; + } + break; + case DATETIME: + checkType(tag, type, ASCII); + checkCount(tag, count, 20); + _dateTime = readASCII(count, value); + _niso.setDateTimeCreated(_dateTime); + if (_version < 5) { + _version = 5; + } + break; + case DATETIMEORIGINAL: + checkType(tag, type, ASCII); + checkCount(tag, count, 20); + _niso.setDateTimeCreated(readASCII(count, value)); + break; + case DOCUMENTNAME: + checkType(tag, type, ASCII); + _documentName = readASCII(count, value); + break; + case DOTRANGE: + checkType(tag, type, BYTE, SHORT); + checkCount(tag, count, 2); + _dotRange = readShortArray(type, count, value); + if (_version < 6) { + _version = 6; + } + break; + case EXIFIFD: + checkType(tag, type, LONG); + checkCount(tag, count, 1); + _exifIFD = readLong(type, count, value); + break; + case EXPOSUREBIASVALUE: + checkType(tag, type, SRATIONAL); + if (count == 1) { + _niso.setExposureBias(readSignedRational(count, value)); + } else { + checkCountArray(tag, count); + Rational[] r = readSignedRationalArray(count, value); + _niso.setExposureBias(average(r[0], r[1])); + } + break; + case EXPOSUREPROGRAM: + checkType(tag, type, SHORT); + checkCount(tag, count, 1); + _exposureProgram = readShort(type, count, value); + break; + case EXPOSURETIME: + checkType(tag, type, RATIONAL); + if (count == 1) { + _niso.setExposureTime(readRational(count, value).toDouble()); + } else { + checkCountArray(tag, count); + Rational[] r = readRationalArray(count, value); + _niso.setExposureTime(average(r[0], r[1]).toDouble()); + } + break; + case EXTRASAMPLES: + checkType(tag, type, SHORT); + checkCountArray(tag, count); + _niso.setExtraSamples(readShortArray(type, count, value)); + if (_version < 6) { + _version = 6; + } + break; + case FILLORDER: + checkType(tag, type, SHORT); + checkCount(tag, count, 1); + _fillOrder = readShort(type, count, value); + break; + case FLASH: + checkType(tag, type, SHORT); + checkCount(tag, count, 1); + _flash = readShort(type, count, value); + _niso.setFlash(_flash); + _niso.setFlashReturn(((_flash & 0X6) != 0) ? 1 : 0); + break; + case FLASHENERGY: + checkType(tag, type, RATIONAL); + if (count == 1) { + _niso.setFlashEnergy(readRational(count, value)); + } else { + checkCountArray(tag, count); + Rational[] r = readRationalArray(count, value); + _niso.setFlashEnergy(average(r[0], r[1])); + } + break; + case FNUMBER: + checkType(tag, type, RATIONAL); + if (count == 1) { + _niso.setFNumber(readRational(count, value).toDouble()); + } else { + checkCountArray(tag, count); + Rational[] r = readRationalArray(count, value); + _niso.setFNumber(average(r[0], r[1]).toDouble()); + } + break; + case FOCALLENGTH: + checkType(tag, type, RATIONAL); + if (count == 1) { + _niso.setFocalLength(readRational(count, value).toDouble()); + } else { + checkCountArray(tag, count); + Rational[] r = readRationalArray(count, value); + _niso.setFocalLength(average(r[0], r[1]).toDouble()); + } + break; + case FOCALPLANERESOLUTIONUNIT: + checkType(tag, type, SHORT); + checkCount(tag, count, 1); + _focalPlaneResolutionUnit = readShort(type, count, value); + break; + case FOCALPLANEXRESOLUTION: + checkType(tag, type, RATIONAL); + checkCount(tag, count, 1); + _focalPlaneXResolution = readRational(count, value); + break; + case FOCALPLANEYRESOLUTION: + checkType(tag, type, RATIONAL); + checkCount(tag, count, 1); + _focalPlaneYResolution = readRational(count, value); + break; + case FREEBYTECOUNTS: + checkType(tag, type, LONG); + checkCountArray(tag, count); + _freeByteCounts = readLongArray(type, count, value); + break; + case FREEOFFSETS: + checkType(tag, type, LONG); + checkCountArray(tag, count); + _freeOffsets = readLongArray(type, count, value); + break; + case GEOASCIIPARAMSTAG: + checkType(tag, type, ASCII); + _geoAsciiParamsTag = readASCII(count, value); + break; + case GEODOUBLEPARAMSTAG: + checkType(tag, type, DOUBLE); + checkCountArray(tag, count); + _geoDoubleParamsTag = readDoubleArray(count, value); + break; + case GEOKEYDIRECTORYTAG: + checkType(tag, type, SHORT); + checkCountArray(tag, count); + _geoKeyDirectoryTag = readShortArray(type, count, value); + int num = _geoKeyDirectoryTag[3]; + int prevKey = -1; + for (int i = 0; i < num; i++) { + int j = i * 4 + 4; + int key = _geoKeyDirectoryTag[j]; + if (prevKey > key) { + String mess = MessageFormat.format(MessageConstants.TIFF_HUL_10.getMessage(), key); + JhoveMessage message = + JhoveMessages.getMessageInstance(MessageConstants.TIFF_HUL_10.getId(), mess); + throw new TiffException(message); + } + prevKey = key; + } + break; + case GLOBALPARAMETERSIFD: + checkType(tag, type, LONG, IFD); + // RFC 2301 allows only IFD, but the latest working + // draft allows LONG. Even though allowing LONG + // technically isn't allowed yet, letting it by seems + // reasonable, since other IFD tags can be LONG. + checkCount(tag, count, 1); + _globalParametersIFD = readLong(type, count, value); + break; + case GPSINFOIFD: + checkType(tag, type, LONG); + checkCount(tag, count, 1); + _gpsInfoIFD = readLong(type, count, value); + break; + case GRAYRESPONSECURVE: + checkType(tag, type, SHORT); + checkCountArray(tag, count); + _niso.setGrayResponseCurve(readShortArray(type, count, value)); + break; + case GRAYRESPONSEUNIT: + checkType(tag, type, SHORT); + checkCount(tag, count, 1); + _niso.setGrayResponseUnit(readShort(type, count, value)); + break; + case HALFTONEHINTS: + checkType(tag, type, SHORT); + checkCount(tag, count, 2); + _halftoneHints = readShortArray(type, count, value); + if (_version < 6) { + _version = 6; + } + break; + case HCUSAGE: + checkType(tag, type, LONG); + checkCount(tag, count, 1); + _hcUsage = readLong(type, count, value); + break; + case HOSTCOMPUTER: + checkType(tag, type, ASCII); + _niso.setHostComputer(readASCII(count, value)); + if (_version < 5) { + _version = 5; + } + break; + case IMAGEDESCRIPTION: + checkType(tag, type, ASCII); + _imageDescription = readASCII(count, value); + break; + case IMAGEID: + checkType(tag, type, ASCII); + _niso.setImageIdentifier(readASCII(count, value)); + break; + case IMAGECOLORINDICATOR: + checkType(tag, type, BYTE); + checkCount(tag, count, 1); + _imageColorIndicator = readByte(type, count, value); + break; + case IMAGECOLORVALUE: + checkType(tag, type, BYTE); + checkCount(tag, count, 1); + _imageColorValue = readByte(type, count, value); + break; + case IMAGEHISTORY: + checkType(tag, type, ASCII); + _imageHistory = readASCII(count, value); + break; + case IMAGELAYER: + checkType(tag, type, SHORT, LONG); + checkCount(tag, count, 2); + _imageLayer = readShortArray(type, count, value); + break; + case IMAGELENGTH: + checkType(tag, type, SHORT, LONG); + checkCount(tag, count, 1); + _niso.setImageLength(readLong(type, count, value)); + break; + case IMAGENUMBER: + checkType(tag, type, LONG); + checkCount(tag, count, 1); + _imageNumber = readLong(type, count, value); + break; + case IMAGESOURCEDATA: + checkType(tag, type, UNDEFINED); + // _imageSourceData = readByteArray (type, count, value); + // GDM 16-Sep-2005: + // The ImageSourceData tag sometimes has a gigantic + // amount of data, and we don't actually do anything with + // it in the current version of JHOVE except determine if + // it's there. + _imageSourceData = new int[] {1}; + break; + case PHOTOSHOPPROPS: + // Can't find any info on what type is expected. + checkCountArray(tag, count); + _photoshopProperties = readByteArray(type, count, value); + break; + case ANNOTATIONS: + // Can't find any info on what type is expected. + checkCountArray(tag, count); + _annotations = readByteArray(type, count, value); + break; + case IMAGEWIDTH: + checkType(tag, type, SHORT, LONG); + checkCount(tag, count, 1); + _niso.setImageWidth(readLong(type, count, value)); + break; + case INDEXED: + checkType(tag, type, SHORT); + checkCount(tag, count, 1); + _indexed = readShort(type, count, value); + break; + case INKNAMES: + checkType(tag, type, ASCII); + _inkNames = readASCIIArray(count, value); + if (_version < 6) { + _version = 6; + } + break; + case INKSET: + checkType(tag, type, SHORT); + checkCount(tag, count, 1); + _inkSet = readShort(type, count, value); + if (_version < 6) { + _version = 6; + } + break; + case ICC_PROFILE: + checkType(tag, type, UNDEFINED); + // Read the iccProdfile data as an array of bytes + checkCountArray(tag, count); + _iccProfile = readTrueByteArray(type, count, value); + try { + String desc = NisoImageMetadata.extractIccProfileDescription(_iccProfile); + if (desc != null) { + _niso.setProfileName(desc); + } + } catch (IllegalArgumentException ie) { + String mess = + MessageFormat.format( + MessageConstants.TIFF_HUL_71.getMessage(), tag, ie.getMessage()); + JhoveMessage message = + JhoveMessages.getMessageInstance(MessageConstants.TIFF_HUL_71.getId(), mess); + throw new TiffException(message); + } + break; + case INTERLACE: + checkType(tag, type, SHORT); + checkCount(tag, count, 1); + _interlace = readShort(type, count, value); + break; + case INTEROPERABILITYIFD: + checkType(tag, type, LONG); + checkCount(tag, count, 1); + _interoperabilityIFD = readLong(type, count, value); + break; + case IPTCNAA: + if (type == ASCII) { + String s = readASCII(count, value); + long[] larray = new long[s.length()]; + for (int i = 0; i < s.length(); i++) { + larray[i] = (int) s.charAt(i); + } + _iptc = larray; + } else if (type == LONG) { + checkCountArray(tag, count); + _iptc = readLongArray(type, count, value); + } else { + checkType(tag, type, BYTE, UNDEFINED); + checkCountArray(tag, count); + int[] b = readByteArray(type, count, value); + long[] larray = new long[b.length]; + for (int i = 0; i < b.length; i++) { + larray[i] = b[i]; + _iptc = larray; + } + } + break; + case ISOSPEEDRATINGS: + checkType(tag, type, SHORT); + checkCount(tag, count, 1); + _isoSpeedRatings = readShortArray(tag, count, value); + break; + case IT8HEADER: + checkType(tag, type, ASCII); + _it8Header = readASCII(count, value); + break; + case JPEGACTABLES: + checkType(tag, type, LONG); + checkCountArray(tag, count); + _jpegACTables = readLongArray(type, count, value); + if (_version < 6) { + _version = 6; + } + break; + case JPEGDCTABLES: + checkType(tag, type, LONG); + checkCountArray(tag, count); + _jpegDCTables = readLongArray(type, count, value); + if (_version < 6) { + _version = 6; + } + break; + case JPEGINTERCHANGEFORMAT: + checkType(tag, type, LONG); + checkCount(tag, count, 1); + _jpegInterchangeFormat = readLong(type, count, value); + if (_version < 6) { + _version = 6; + } + break; + case JPEGINTERCHANGEFORMATLENGTH: + checkType(tag, type, LONG); + checkCount(tag, count, 1); + _jpegInterchangeFormatLength = readLong(type, count, value); + if (_version < 6) { + _version = 6; + } + break; + case JPEGLOSSLESSPREDICTORS: + checkType(tag, type, SHORT); + checkCountArray(tag, count); + _jpegLosslessPredictors = readShortArray(type, count, value); + if (_version < 6) { + _version = 6; + } + break; + case JPEGPOINTTRANSFORMS: + checkType(tag, type, SHORT); + checkCountArray(tag, count); + _jpegPointTransforms = readShortArray(type, count, value); + if (_version < 6) { + _version = 6; + } + break; + case JPEGPROC: + checkType(tag, type, SHORT); + checkCount(tag, count, 1); + _jpegProc = readShort(type, count, value); + if (_version < 6) { + _version = 6; + } + break; + case JPEGQTABLES: + checkType(tag, type, LONG); + checkCountArray(tag, count); + _jpegQTables = readLongArray(type, count, value); + if (_version < 6) { + _version = 6; + } + break; + case JPEGRESTARTINTERVAL: + checkType(tag, type, SHORT); + checkCount(tag, count, 1); + _jpegRestartInterval = readShort(type, count, value); + if (_version < 6) { + _version = 6; + } + break; + case JPEGTABLES: + checkType(tag, type, UNDEFINED); + checkCountArray(tag, count); + _jpegTables = readByteArray(type, count, value); + break; + case LIGHTSOURCE: + checkType(tag, type, SHORT); + checkCount(tag, count, 1); + _niso.setSceneIlluminant(readShort(type, count, value)); + break; + case MAKE: + checkType(tag, type, ASCII); + _niso.setScannerManufacturer(readASCII(count, value)); + break; + case MAXAPERTUREVALUE: + checkType(tag, type, RATIONAL); + checkCount(tag, count, 1); + _maxAperatureValue = readRational(count, value); + break; + case MAXSAMPLEVALUE: + checkType(tag, type, SHORT); + checkCountArray(tag, count); + _maxSampleValue = readShortArray(type, count, value); + break; + case METERINGMODE: + checkType(tag, type, SHORT); + checkCount(tag, count, 1); + _niso.setMeteringMode(readShort(type, count, value)); + break; + case MINSAMPLEVALUE: + checkType(tag, type, SHORT); + checkCountArray(tag, count); + _minSampleValue = readShortArray(type, count, value); + break; + case MODEL: + checkType(tag, type, ASCII); + _niso.setScannerModelName(readASCII(count, value)); + break; + case MODELPIXELSCALETAG: + checkType(tag, type, DOUBLE); + checkCount(tag, count, 3); + _modelPixelScaleTag = readDoubleArray(count, value); + break; + case MODELTIEPOINTTAG: + checkType(tag, type, DOUBLE); + checkCountArray(tag, count); + _modelTiepointTag = readDoubleArray(count, value); + break; + case MODELTRANSFORMATIONTAG: + checkType(tag, type, DOUBLE); + checkCount(tag, count, 16); + _modelTransformationTag = readDoubleArray(count, value); + break; + case NEWSUBFILETYPE: + checkType(tag, type, LONG); + checkCount(tag, count, 1); + _newSubfileType = readLong(type, count, value); + if (_version < 5) { + _version = 5; + } + break; + case NOISE: + checkType(tag, type, UNDEFINED); + checkCountArray(tag, count); + _noise = readByteArray(type, count, value); + break; + case NUMBEROFINKS: + checkType(tag, type, SHORT); + checkCount(tag, count, 1); + _numberOfInks = readShort(type, count, value); + if (_version < 6) { + _version = 6; + } + break; + case OECF: + checkType(tag, type, UNDEFINED); + checkCountArray(tag, count); + _oecf = readByteArray(tag, count, value); + break; + case OPIPROXY: + checkType(tag, type, SHORT); + checkCount(tag, count, 1); + _opiProxy = readShort(type, count, value); + break; + case ORIENTATION: + checkType(tag, type, SHORT); + checkCount(tag, count, 1); + _niso.setOrientation(readShort(type, count, value)); + break; + case PAGENAME: + checkType(tag, type, ASCII); + _pageName = readASCII(count, value); + break; + case PAGENUMBER: + checkType(tag, type, SHORT); + checkCount(tag, count, 2); + _pageNumber = readShortArray(type, count, value); + break; + case PHOTOMETRICINTERPRETATION: + checkType(tag, type, SHORT); + checkCount(tag, count, 1); + _photometricInterpretation = readShort(type, count, value); + _niso.setColorSpace(_photometricInterpretation); + // Set default values appropriate to interpretation, + // only if no value has been set. + if (_photometricInterpretation == 5) { + if (_inkSet == NULL) { + _inkSet = 1; + } + if (_numberOfInks == NULL) { + _numberOfInks = 4; + } + } else if (_photometricInterpretation == 6) { + if (_niso.getYCbCrCoefficients() == null) { + _niso.setYCbCrCoefficients( + new Rational[] { + new Rational(299, 1000), new Rational(587, 1000), new Rational(114, 1000) + }); + } + if (_niso.getYCbCrPositioning() == NULL) { + _niso.setYCbCrPositioning(1); + } + if (_niso.getYCbCrSubSampling() == null) { + _niso.setYCbCrSubSampling(new int[] {2, 2}); + } + } + int colorSpace = _niso.getColorSpace(); + if (colorSpace == 3 || colorSpace == 4) { + if (_version < 5) { + _version = 5; + } + } else if (colorSpace == 5 || colorSpace == 6 || colorSpace == 8) { + if (_version < 6) { + _version = 6; + } + } + break; + case PIXELINTENSITYRANGE: + checkType(tag, type, BYTE); + checkCountArray(tag, count); + _pixelIntensityRange = readShortArray(type, count, value); + break; + case PLANARCONFIGURATION: + checkType(tag, type, SHORT); + checkCount(tag, count, 1); + _niso.setPlanarConfiguration(readShort(type, count, value)); + break; + case PREDICTOR: + checkType(tag, type, SHORT); + checkCount(tag, count, 1); + _predictor = readShort(type, count, value); + if (_version < 5) { + _version = 5; + } + break; + case PRIMARYCHROMATICITIES: + { + checkType(tag, type, RATIONAL); + checkCount(tag, count, 6); + Rational[] rarray = readRationalArray(count, value); + _niso.setPrimaryChromaticitiesRedX(rarray[0]); + _niso.setPrimaryChromaticitiesRedY(rarray[1]); + _niso.setPrimaryChromaticitiesGreenX(rarray[2]); + _niso.setPrimaryChromaticitiesGreenY(rarray[3]); + _niso.setPrimaryChromaticitiesBlueX(rarray[4]); + _niso.setPrimaryChromaticitiesBlueY(rarray[5]); + if (_version < 5) { + _version = 5; + } + break; + } + case RASTERPADDING: + checkType(tag, type, SHORT); + checkCount(tag, count, 1); + _rasterPadding = readShort(type, count, value); + break; + case REFERENCEBLACKWHITE: + checkType(tag, type, RATIONAL); + checkCount(tag, count, 6); + _niso.setReferenceBlackWhite(readRationalArray(count, value)); + if (_version < 6) { + _version = 6; + } + break; + case RESOLUTIONUNIT: + checkType(tag, type, SHORT); + checkCount(tag, count, 1); + _niso.setSamplingFrequencyUnit(readShort(type, count, value)); + break; + case ROWSPERSTRIP: + checkType(tag, type, SHORT, LONG); + checkCount(tag, count, 1); + _niso.setRowsPerStrip(readLong(type, count, value)); + break; + case SAMPLEFORMAT: + checkType(tag, type, SHORT); + checkCountArray(tag, count); + _sampleFormat = readShortArray(type, count, value); + if (_version < 6) { + _version = 6; + } + break; + case SAMPLESPERPIXEL: + checkType(tag, type, SHORT); + checkCount(tag, count, 1); + _niso.setSamplesPerPixel(readShort(type, count, value)); + break; + case SECURITYCLASSIFICATION: + checkType(tag, type, ASCII); + _securityClassification = readASCII(count, value); + break; + case SELFTIMERMODE: + checkType(tag, type, SHORT); + checkCount(tag, count, 1); + _selfTimerMode = readShort(type, count, value); + break; + case SENSINGMETHOD: + checkType(tag, type, SHORT); + checkCount(tag, count, 1); + _niso.setSensor(readShort(type, count, value)); + break; + case SITE: + checkType(tag, type, ASCII); + _site = readASCII(count, value); + break; + case STRIPROWCOUNTS: + checkType(tag, type, LONG); + checkCountArray(tag, count); + _stripRowCounts = readLongArray(type, count, value); + break; + case SUBJECTDISTANCE: + checkType(tag, type, RATIONAL, SRATIONAL); + double[] darray = new double[2]; + if (count == 1) { + darray[0] = readRational(count, value).toDouble(); + darray[1] = darray[0]; + } else { + checkCountArray(tag, count); + Rational[] r; + if (type == RATIONAL) { + r = readRationalArray(count, value); + } else { + r = readSignedRationalArray(count, value); + } + darray[0] = r[0].toDouble(); + if (r.length > 1) { + darray[1] = r[1].toDouble(); + } else { + darray[1] = darray[0]; + } + } + _niso.setSubjectDistance(darray); + break; + case SOFTWARE: + checkType(tag, type, ASCII); + _niso.setScanningSoftware(readASCII(count, value)); + if (_version < 5) { + _version = 5; + } + break; + case SPATIALFREQUENCYRESPONSE: + checkType(tag, type, UNDEFINED); + checkCountArray(tag, count); + _spatialFrequencyResponse = readByteArray(type, count, value); + break; + case SPECTRALSENSITIVITY: + checkType(tag, type, ASCII); + _spectralSensitivity = readASCII(count, value); + break; + case STRIPBYTECOUNTS: + checkType(tag, type, SHORT, LONG); + checkCountArray(tag, count); + _niso.setStripByteCounts(readLongArray(type, count, value)); + break; + case STRIPOFFSETS: + checkType(tag, type, SHORT, LONG); + checkCountArray(tag, count); + _niso.setStripOffsets(readLongArray(type, count, value)); + break; + case SUBFILETYPE: + checkType(tag, type, LONG); + checkCount(tag, count, 1); + _subfileType = readShort(type, count, value); + break; + case SUBIFDS: + checkType(tag, type, LONG, IFD); + checkCountArray(tag, count); + _subIFDs = readLongArray(type, count, value); + break; + case SUBJECTLOCATION: + checkType(tag, type, SHORT); + checkCountArray(tag, count); + _subjectLocation = readShortArray(type, count, value); + break; + case T4OPTIONS: + checkType(tag, type, LONG); + _t4Options = readShort(type, count, value); + break; + case T6OPTIONS: + checkType(tag, type, LONG); + _t6Options = readShort(type, count, value); + break; + case TARGETPRINTER: + checkType(tag, type, ASCII); + _targetPrinter = readASCII(count, value); + if (_version < 6) { + _version = 6; + } + break; + case THRESHHOLDING: + checkType(tag, type, SHORT); + checkCount(tag, count, 1); + _threshholding = readShort(type, count, value); + break; + case TIFFEPSTANDARDID: + checkType(tag, type, SHORT); + checkCount(tag, count, 4); + int[] iarray = readShortArray(type, count, value); + _tiffEPStandardID = + Integer.toString(iarray[0]) + + "." + + Integer.toString(iarray[1]) + + "." + + Integer.toString(iarray[2]) + + "." + + Integer.toString(iarray[3]); + break; + case TILEBYTECOUNTS: + checkType(tag, type, SHORT, LONG); + checkCountArray(tag, count); + _niso.setTileByteCounts(readLongArray(type, count, value)); + if (_version < 6) { + _version = 6; + } + break; + case TILELENGTH: + checkType(tag, type, SHORT, LONG); + checkCount(tag, count, 1); + _niso.setTileLength(readLong(type, count, value)); + if (_version < 6) { + _version = 6; + } + break; + case TILEOFFSETS: + checkType(tag, type, SHORT, LONG); + checkCountArray(tag, count); + _niso.setTileOffsets(readLongArray(type, count, value)); + if (_version < 6) { + _version = 6; + } + break; + case TILEWIDTH: + checkType(tag, type, SHORT, LONG); + checkCount(tag, count, 1); + _niso.setTileWidth(readLong(type, count, value)); + if (_version < 6) { + _version = 6; + } + break; + case TIMEZONEOFFSET: + checkType(tag, type, SSHORT); + checkCountArray(tag, count); + _timeZoneOffset = readSShortArray(type, count, value); + break; + case TRANSFERFUNCTION: + /* + * Transfer function arrays potentially can have millions + * of elements, so we just report presence + */ + checkType(tag, type, SHORT); + _transferFunction = true; + break; + case TRANSFERRANGE: + checkType(tag, type, SHORT); + checkCount(tag, count, 6); + _transferRange = readShortArray(type, count, value); + if (_version < 6) { + _version = 6; + } + break; + case TRANSPARENCYINDICATOR: + checkType(tag, type, BYTE); + checkCount(tag, count, 1); + _transparencyIndicator = readByte(type, count, value); + break; + case WHITEPOINT: + { + checkType(tag, type, RATIONAL); + checkCount(tag, count, 2); + Rational[] rarray = readRationalArray(count, value); + _niso.setWhitePointXValue(rarray[0]); + _niso.setWhitePointYValue(rarray[0]); + if (_version < 5) { + _version = 5; + } + break; + } + case XCLIPPATHUNITS: + checkType(tag, type, LONG); + checkCount(tag, count, 1); + _xClipPathUnits = readLong(type, count, value); + break; + case XPOSITION: + checkType(tag, type, RATIONAL); + checkCount(tag, count, 1); + _xPosition = readRational(count, value); + break; + case XRESOLUTION: + checkType(tag, type, RATIONAL); + checkCount(tag, count, 1); + _niso.setXSamplingFrequency(readRational(count, value)); + break; + case YCBCRCOEFFICIENTS: + checkType(tag, type, RATIONAL); + checkCount(tag, count, 3); + _niso.setYCbCrCoefficients(readRationalArray(count, value)); + if (_version < 6) { + _version = 6; + } + break; + case YCBCRPOSITIONING: + checkType(tag, type, SHORT); + checkCount(tag, count, 1); + _niso.setYCbCrPositioning(readShort(type, count, value)); + if (_version < 6) { + _version = 6; + } + break; + case YCBCRSUBSAMPLING: + checkType(tag, type, SHORT); + checkCount(tag, count, 2); + _niso.setYCbCrSubSampling(readShortArray(type, count, value)); + if (_version < 6) { + _version = 6; + } + break; + case YCLIPPATHUNITS: + checkType(tag, type, LONG); + checkCount(tag, count, 1); + _yClipPathUnits = readLong(type, count, value); + break; + case YPOSITION: + checkType(tag, type, RATIONAL); + checkCount(tag, count, 1); + _yPosition = readRational(count, value); + break; + case YRESOLUTION: + checkType(tag, type, RATIONAL); + checkCount(tag, count, 1); + _niso.setYSamplingFrequency(readRational(count, value)); + break; + case XMP: + checkType(tag, type, UNDEFINED, BYTE); + checkCountArray(tag, count); + _xmpProp = readXMP(count, value); + break; + case DNGVERSION: + checkType(tag, type, BYTE); + checkCount(tag, count, 4); + _dngVersion = readByteArray(type, count, value); + break; + case DNGBACKWARDVERSION: + checkType(tag, type, BYTE); + checkCount(tag, count, 4); + _dngBackwardVersion = readByteArray(type, count, value); + break; + case UNIQUECAMERAMODEL: + checkType(tag, type, ASCII); + _uniqueCameraModel = readASCII(count, value); + break; + case LOCALIZEDCAMERAMODEL: + { + checkType(tag, type, ASCII, BYTE); + // This tag is specified as UTF-8 + byte[] lcm = readTrueByteArray(type, count, value); + // Trim off trailing null (s) + int len = lcm.length; + while (len > 0 && lcm[len - 1] == 0) { + len--; + } + _localizedCameraModel = new String(lcm, 0, len); + break; + } + case CFAPLANECOLOR: + checkType(tag, type, BYTE); + checkCountArray(tag, count); + _cfaPlaneColor = readByteArray(type, count, value); + break; + case CFALAYOUT: + checkType(tag, type, SHORT); + _cfaLayout = readShort(type, count, value); + break; + case LINEARIZATIONTABLE: + checkType(tag, type, SHORT); + checkCountArray(tag, count); + _linearizationTable = readShortArray(type, count, value); + break; + case BLACKLEVELREPEATDIM: + checkType(tag, type, SHORT); + checkCountArray(tag, count); + _blackLevelRepeatDim = readShortArray(type, count, value); + break; + case BLACKLEVEL: + // Just to make things complicated, this can be SHORT, LONG + // or RATIONAL. To give these a least common (pardon the + // expression) denominator, we convert all to rational. + checkCountArray(tag, count); + if (type == RATIONAL) { + _blackLevel = readRationalArray(count, value); + } else { + checkType(tag, type, SHORT, LONG); + long[] ibl = readLongArray(type, count, value); + _blackLevel = new Rational[(int) count]; + for (int i = 0; i < count; i++) { + _blackLevel[i] = new Rational(ibl[i], 1); + } + } + break; + case BLACKLEVELDELTAH: + checkType(tag, type, SRATIONAL); + checkCountArray(tag, count); + _blackLevelDeltaH = readSignedRationalArray(count, value); + break; + case BLACKLEVELDELTAV: + checkType(tag, type, SRATIONAL); + checkCountArray(tag, count); + _blackLevelDeltaV = readSignedRationalArray(count, value); + break; + case WHITELEVEL: + checkType(tag, type, SHORT, LONG); + checkCountArray(tag, count); + _whiteLevel = readLongArray(type, count, value); + break; + case DEFAULTSCALE: + checkType(tag, type, RATIONAL); + checkCount(tag, count, 2); + _defaultScale = readRationalArray(count, value); + break; + case BESTQUALITYSCALE: + checkType(tag, type, RATIONAL); + checkCount(tag, count, 1); + _bestQualityScale = readRational(count, value); + break; + case DEFAULTCROPORIGIN: + checkCount(tag, count, 2); + // Just to make things complicated, this can be SHORT, LONG + // or RATIONAL. To give these a least common (pardon the + // expression) denominator, we convert all to rational. + if (type == RATIONAL) { + _defaultCropOrigin = readRationalArray(count, value); + } else { + checkType(tag, type, SHORT, LONG); + long[] lco = readLongArray(type, count, value); + _defaultCropOrigin = new Rational[(int) count]; + for (int i = 0; i < count; i++) { + _defaultCropOrigin[i] = new Rational(lco[i], 1); + } + } + break; + case DEFAULTCROPSIZE: + checkCount(tag, count, 2); + if (type == RATIONAL) { + _defaultCropSize = readRationalArray(count, value); + } else { + checkType(tag, type, SHORT, LONG); + long[] lcs = readLongArray(type, count, value); + _defaultCropSize = new Rational[(int) count]; + for (int i = 0; i < count; i++) { + _defaultCropSize[i] = new Rational(lcs[i], 1); + } + } + break; + case CALIBRATIONILLUMINANT1: + checkCount(tag, count, 1); + checkType(tag, type, SHORT); + _calibrationIlluminant1 = readShort(type, count, value); + break; + case CALIBRATIONILLUMINANT2: + checkCount(tag, count, 1); + checkType(tag, type, SHORT); + _calibrationIlluminant2 = readShort(type, count, value); + break; + case COLORMATRIX1: + checkType(tag, type, SRATIONAL); + checkCountArray(tag, count); + _colorMatrix1 = readSignedRationalArray(count, value); + break; + case COLORMATRIX2: + checkType(tag, type, SRATIONAL); + checkCountArray(tag, count); + _colorMatrix2 = readSignedRationalArray(count, value); + break; + case CAMERACALIBRATION1: + checkType(tag, type, SRATIONAL); + checkCountArray(tag, count); + _colorMatrix1 = readSignedRationalArray(count, value); + break; + case CAMERACALIBRATION2: + checkType(tag, type, SRATIONAL); + checkCountArray(tag, count); + _colorMatrix2 = readSignedRationalArray(count, value); + break; + case REDUCTIONMATRIX1: + checkType(tag, type, SRATIONAL); + checkCountArray(tag, count); + _reductionMatrix1 = readSignedRationalArray(count, value); + break; + case REDUCTIONMATRIX2: + checkType(tag, type, SRATIONAL); + checkCountArray(tag, count); + _reductionMatrix2 = readSignedRationalArray(count, value); + break; + case ANALOGBALANCE: + checkType(tag, type, RATIONAL); + checkCountArray(tag, count); + _analogBalance = readRationalArray(count, value); + break; + case ASSHOTNEUTRAL: + // this can be either SHORT or RATIONAL + checkType(tag, type, SHORT, RATIONAL); + checkCountArray(tag, count); + if (type == SHORT) { + int[] asn = readShortArray(type, count, value); + _asShotNeutral = new Rational[(int) count]; + for (int i = 0; i < count; i++) { + _asShotNeutral[i] = new Rational(asn[i], 1); + } + } else { + _asShotNeutral = readRationalArray(count, value); + } + break; + case ASSHOTWHITEXY: + checkType(tag, type, RATIONAL); + checkCount(tag, count, 2); + _asShotWhiteXY = readRationalArray(count, value); + break; + case BASELINEEXPOSURE: + checkType(tag, type, SRATIONAL); + _baselineExposure = readSignedRational(count, value); + break; + case BASELINENOISE: + checkType(tag, type, RATIONAL); + _baselineNoise = readRational(count, value); + break; + case BASELINESHARPNESS: + checkType(tag, type, RATIONAL); + _baselineSharpness = readRational(count, value); + break; + case BAYERGREENSPLIT: + checkType(tag, type, LONG); + _bayerGreenSplit = (int) readLong(type, count, value); + break; + case LINEARRESPONSELIMIT: + checkType(tag, type, RATIONAL); + _linearResponseLimit = readRational(count, value); + break; + case CAMERASERIALNUMBER: + checkType(tag, type, ASCII); + _cameraSerialNumber = readASCII(count, value); + break; + case LENSINFO: + checkType(tag, type, RATIONAL); + checkCount(tag, count, 4); + _lensInfo = readRationalArray(count, value); + break; + case CHROMABLURRADIUS: + checkType(tag, type, RATIONAL); + _chromaBlurRadius = readRational(count, value); + break; + case ANTIALIASSTRENGTH: + checkType(tag, type, RATIONAL); + _antiAliasStrength = readRational(count, value); + break; + case SHADOWSCALE: + _info.setMessage( + new InfoMessage( + MessageConstants.TIFF_HUL_11, MessageConstants.TIFF_HUL_11_SUB.getMessage())); + break; + case DNGPRIVATEDATA: + checkType(tag, type, BYTE); + checkCountArray(tag, count); + _dngPrivateData = readByteArray(type, count, value); + break; + case MAKERNOTESAFETY: + checkType(tag, type, SHORT); + _makerNoteSafety = readShort(type, count, value); + break; + default: + String mess = MessageFormat.format(MessageConstants.TIFF_HUL_12.getMessage(), tag); + JhoveMessage message = + JhoveMessages.getMessageInstance(MessageConstants.TIFF_HUL_12.getId(), mess); + _info.setMessage(new InfoMessage(message, value)); + break; + } + } catch (IOException e) { + String mess = MessageFormat.format(MessageConstants.TIFF_HUL_13.getMessage(), tag); + JhoveMessage message = + JhoveMessages.getMessageInstance(MessageConstants.TIFF_HUL_13.getId(), mess); + throw new TiffException(message, value); + } + } + + /** Perform initializations that have to wait until after the IFD has been parsed. */ + protected void postParseInitialization() { + int samplesPerPixel = _niso.getSamplesPerPixel(); + int[] bitsPerSample = _niso.getBitsPerSample(); + if (bitsPerSample == null) { + bitsPerSample = new int[samplesPerPixel]; + for (int i = 0; i < samplesPerPixel; i++) { + bitsPerSample[i] = 1; + } + _niso.setBitsPerSample(bitsPerSample); + } + int bps1 = (1 << bitsPerSample[0]) - 1; + if (_maxSampleValue == null) { + _maxSampleValue = new int[samplesPerPixel]; + for (int i = 0; i < samplesPerPixel; i++) { + _maxSampleValue[i] = bps1; + } + } + if (_minSampleValue == null) { + _minSampleValue = new int[samplesPerPixel]; + for (int i = 0; i < samplesPerPixel; i++) { + _minSampleValue[i] = 0; + } + } + if (_sampleFormat == null) { + _sampleFormat = new int[samplesPerPixel]; + for (int i = 0; i < samplesPerPixel; i++) { + _sampleFormat[i] = 1; + } + } + /* + * The default transfer function is expressed as an array + * of 2^BitsPerSample elements. If BitsPerSample is 24, + * this would paraylze processing for many minutes, if it + * didn't just run out of memory. So it was a nice idea... + */ + // if (_photometricInterpretation == 0 || + // _photometricInterpretation == 1 || + // _photometricInterpretation == 2 || + // _photometricInterpretation == 3 || + // _photometricInterpretation == 6) { + // // Set default transfer function only for indicated + // // photometricInterpretations. + // if (_transferFunction == null) { + // int n = 1< 1) { - return false; - } - int [] bps = niso.getBitsPerSample (); - if (bps == null || (bps[0] > 1)) { - return false; - } - - if (!satisfiesCompression (tifd, new int [] {1, 2, 32773} )) { - return false; - } + /* Check required values. */ + if (niso.getSamplesPerPixel() > 1) { + return false; + } + int[] bps = niso.getBitsPerSample(); + if (bps == null || (bps[0] > 1)) { + return false; + } - if (!satisfiesPhotometricInterpretation (tifd, new int [] {0, 1} )) { - return false; - } + if (!satisfiesCompression(tifd, new int[] {1, 2, 32773})) { + return false; + } - return satisfiesResolutionUnit (tifd, new int [] {1, 2, 3} ); + if (!satisfiesPhotometricInterpretation(tifd, new int[] {0, 1})) { + return false; } + + return satisfiesResolutionUnit(tifd, new int[] {1, 2, 3}); + } } diff --git a/jhove-modules/tiff-hul/src/main/java/edu/harvard/hul/ois/jhove/module/tiff/TiffProfileClassF.java b/jhove-modules/tiff-hul/src/main/java/edu/harvard/hul/ois/jhove/module/tiff/TiffProfileClassF.java index 4a1d93988..b0f8c3a29 100644 --- a/jhove-modules/tiff-hul/src/main/java/edu/harvard/hul/ois/jhove/module/tiff/TiffProfileClassF.java +++ b/jhove-modules/tiff-hul/src/main/java/edu/harvard/hul/ois/jhove/module/tiff/TiffProfileClassF.java @@ -1,157 +1,145 @@ -/********************************************************************** - * Jhove - JSTOR/Harvard Object Validation Environment - * Copyright 2003 by JSTOR and the President and Fellows of Harvard College - **********************************************************************/ - +/** + * ******************************************************************** Jhove - JSTOR/Harvard Object + * Validation Environment Copyright 2003 by JSTOR and the President and Fellows of Harvard College + * ******************************************************************** + */ package edu.harvard.hul.ois.jhove.module.tiff; import edu.harvard.hul.ois.jhove.*; /** - * Profile checker for TIFF Class F. + * Profile checker for TIFF Class F. * - * @author Gary McGath + * @author Gary McGath */ -public final class TiffProfileClassF extends TiffProfile -{ - - /* Significant changes February 2, 2004 */ - - /** - * Constructor. - */ - public TiffProfileClassF () - { - super (); - _profileText = "Class F"; +public final class TiffProfileClassF extends TiffProfile { + + /* Significant changes February 2, 2004 */ + + /** Constructor. */ + public TiffProfileClassF() { + super(); + _profileText = "Class F"; + } + + /** + * Returns true if the IFD satisfies the requirements of a Class F profile. See the Class F + * specification for details. + */ + @Override + public boolean satisfiesThisProfile(IFD ifd) { + if (!(ifd instanceof TiffIFD)) { + return false; + } + TiffIFD tifd = (TiffIFD) ifd; + + /* Check required tags. */ + NisoImageMetadata niso = tifd.getNisoImageMetadata(); + if (niso.getImageLength() == NisoImageMetadata.NULL + || niso.getStripOffsets() == null + || niso.getRowsPerStrip() == NisoImageMetadata.NULL + || niso.getStripByteCounts() == null + || tifd.getPageNumber() == null + || niso.getScanningSoftware() == null) { + return false; } - /** - * Returns true if the IFD satisfies the requirements of a - * Class F profile. See the Class F specification for - * details. - */ - @Override - public boolean satisfiesThisProfile (IFD ifd) - { - if (!(ifd instanceof TiffIFD)) { - return false; - } - TiffIFD tifd = (TiffIFD) ifd; - - /* Check required tags. */ - NisoImageMetadata niso = tifd.getNisoImageMetadata (); - if (niso.getImageLength () == NisoImageMetadata.NULL || - niso.getStripOffsets () == null || - niso.getRowsPerStrip () == NisoImageMetadata.NULL || - niso.getStripByteCounts () == null || - tifd.getPageNumber () == null || - niso.getScanningSoftware () == null) { - return false; - } + /* Check required values. */ + if (!satisfiesCompression(tifd, new int[] {3, 4})) { + return false; + } - /* Check required values. */ - if (!satisfiesCompression (tifd, new int[] {3, 4} )) { - return false; - } + int fo = tifd.getFillOrder(); + if (fo != 1 && fo != 2) { + return false; + } - int fo = tifd.getFillOrder(); - if (fo != 1 && fo != 2) { - return false; - } + int cmpr = niso.getCompressionScheme(); + if (cmpr == 3) { + // T4 options are also known as Group 3 options + long t4opt = tifd.getT4Options(); + if (t4opt != 0 && t4opt != 1 && t4opt != 4 && t4opt != 5) { + return false; + } + } else if (cmpr == 4) { + // T6 options are also known as Group 4 options + long t6opt = tifd.getT6Options(); + if (t6opt != 2) { + return false; + } + } - int cmpr = niso.getCompressionScheme (); - if (cmpr == 3) { - // T4 options are also known as Group 3 options - long t4opt = tifd.getT4Options (); - if (t4opt != 0 && t4opt != 1 && - t4opt != 4 && t4opt != 5) { - return false; - } - } - else if (cmpr == 4) { - // T6 options are also known as Group 4 options - long t6opt = tifd.getT6Options (); - if (t6opt != 2) { - return false; - } - } - - //long wid = niso.getImageWidth (); - if (!satisfiesImageWidth (tifd, - new int [] {1728, 2048, - 2432, 2592, 3072, 3648, 3456, 4096, 4864} )) { - return false; - } + // long wid = niso.getImageWidth (); + if (!satisfiesImageWidth( + tifd, new int[] {1728, 2048, 2432, 2592, 3072, 3648, 3456, 4096, 4864})) { + return false; + } - if (!satisfiesNewSubfileType (tifd, 2)) { - return false; - } + if (!satisfiesNewSubfileType(tifd, 2)) { + return false; + } - if (!satisfiesResolutionUnit (tifd, new int [] {2, 3} )) { - return false; - } + if (!satisfiesResolutionUnit(tifd, new int[] {2, 3})) { + return false; + } - if (!satisfiesXResolution (tifd, - new int[] {204, 200, 300, 400, 408} )) { - return false; - } + if (!satisfiesXResolution(tifd, new int[] {204, 200, 300, 400, 408})) { + return false; + } - if (!satisfiesYResolution (tifd, - new int[] {98, 196, 100, 200, 300, 391, 400} )) { - return false; - } + if (!satisfiesYResolution(tifd, new int[] {98, 196, 100, 200, 300, 391, 400})) { + return false; + } - int[] bps = niso.getBitsPerSample (); - if (bps == null || bps[0] != 1 ) { - return false; - } + int[] bps = niso.getBitsPerSample(); + if (bps == null || bps[0] != 1) { + return false; + } - if (!satisfiesPhotometricInterpretation (tifd, new int [] {0, 1} )) { - return false; + if (!satisfiesPhotometricInterpretation(tifd, new int[] {0, 1})) { + return false; + } + + if (!satisfiesSamplesPerPixel(tifd, 1)) { + return false; + } + + // Only certain combinations of ImageWidth and resolution are + // permitted. + int wid = (int) niso.getImageWidth(); + int xres = (int) niso.getXSamplingFrequency().toLong(); + int yres = (int) niso.getYSamplingFrequency().toLong(); + switch (wid) { + case 1728: + case 2048: + case 2432: + if (!(xres == 204 && yres == 391) + || (xres == 200 && yres == 100) + || (xres == 200 && yres == 200)) { + return false; } + break; - if (!satisfiesSamplesPerPixel (tifd, 1)) { - return false; + case 2592: + case 3072: + case 3648: + if (!(xres == 300 && yres == 300)) { + return false; } - - // Only certain combinations of ImageWidth and resolution are - // permitted. - int wid = (int) niso.getImageWidth (); - int xres = (int) niso.getXSamplingFrequency ().toLong (); - int yres = (int) niso.getYSamplingFrequency ().toLong (); - switch (wid) { - case 1728: - case 2048: - case 2432: - if (!(xres == 204 && yres == 391) || - (xres == 200 && yres == 100) || - (xres == 200 && yres == 200)) { - return false; - } - break; - - case 2592: - case 3072: - case 3648: - if (!(xres == 300 && yres == 300)) { - return false; - } - break; - - case 3456: - case 4096: - case 4864: - if (!(xres == 408 && yres == 391) || - (xres == 400 && yres == 400)) { - return false; - } - break; - - default: - break; + break; + + case 3456: + case 4096: + case 4864: + if (!(xres == 408 && yres == 391) || (xres == 400 && yres == 400)) { + return false; } - - return true; + break; + + default: + break; } + + return true; + } } diff --git a/jhove-modules/tiff-hul/src/main/java/edu/harvard/hul/ois/jhove/module/tiff/TiffProfileClassG.java b/jhove-modules/tiff-hul/src/main/java/edu/harvard/hul/ois/jhove/module/tiff/TiffProfileClassG.java index 03eb3189b..667047f4d 100644 --- a/jhove-modules/tiff-hul/src/main/java/edu/harvard/hul/ois/jhove/module/tiff/TiffProfileClassG.java +++ b/jhove-modules/tiff-hul/src/main/java/edu/harvard/hul/ois/jhove/module/tiff/TiffProfileClassG.java @@ -1,67 +1,63 @@ -/********************************************************************** - * Jhove - JSTOR/Harvard Object Validation Environment - * Copyright 2003 by JSTOR and the President and Fellows of Harvard College - **********************************************************************/ - +/** + * ******************************************************************** Jhove - JSTOR/Harvard Object + * Validation Environment Copyright 2003 by JSTOR and the President and Fellows of Harvard College + * ******************************************************************** + */ package edu.harvard.hul.ois.jhove.module.tiff; import edu.harvard.hul.ois.jhove.*; /** - * Profile checker for TIFF Class G (Baseline Grayscale). + * Profile checker for TIFF Class G (Baseline Grayscale). * - * @author Gary McGath + * @author Gary McGath */ -public final class TiffProfileClassG extends TiffProfile -{ - public TiffProfileClassG () - { - super (); - _profileText = "Baseline grayscale (Class G)"; +public final class TiffProfileClassG extends TiffProfile { + public TiffProfileClassG() { + super(); + _profileText = "Baseline grayscale (Class G)"; + } + + /** + * Returns true if the IFD satisfies the requirements of a Class G profile. See the TIFF 6.0 + * specification for details. + */ + @Override + public boolean satisfiesThisProfile(IFD ifd) { + if (!(ifd instanceof TiffIFD)) { + return false; + } + TiffIFD tifd = (TiffIFD) ifd; + + /* Check required tags. */ + NisoImageMetadata niso = tifd.getNisoImageMetadata(); + if (niso.getImageWidth() == NisoImageMetadata.NULL + || niso.getImageLength() == NisoImageMetadata.NULL + || niso.getStripOffsets() == null + || niso.getRowsPerStrip() == NisoImageMetadata.NULL + || niso.getStripByteCounts() == null + || niso.getXSamplingFrequency() == null + || niso.getYSamplingFrequency() == null) { + return false; } - /** - * Returns true if the IFD satisfies the requirements of a - * Class G profile. See the TIFF 6.0 specification for - * details. - */ - @Override - public boolean satisfiesThisProfile (IFD ifd) - { - if (!(ifd instanceof TiffIFD)) { - return false; - } - TiffIFD tifd = (TiffIFD) ifd; - - /* Check required tags. */ - NisoImageMetadata niso = tifd.getNisoImageMetadata (); - if (niso.getImageWidth () == NisoImageMetadata.NULL || - niso.getImageLength () == NisoImageMetadata.NULL || - niso.getStripOffsets () == null || - niso.getRowsPerStrip () == NisoImageMetadata.NULL || - niso.getStripByteCounts () == null || - niso.getXSamplingFrequency () == null || - niso.getYSamplingFrequency () == null) { - return false; - } - - /* Check required values. */ - if (niso.getSamplesPerPixel () > 1) { - return false; - } - int [] bps = niso.getBitsPerSample (); - if (bps == null || (bps[0] != 4 && bps[0] != 8)) { - return false; - } - - if (!satisfiesCompression (tifd, new int [] {1, 32773} )) { - return false; - } + /* Check required values. */ + if (niso.getSamplesPerPixel() > 1) { + return false; + } + int[] bps = niso.getBitsPerSample(); + if (bps == null || (bps[0] != 4 && bps[0] != 8)) { + return false; + } - if (!satisfiesPhotometricInterpretation (tifd, new int [] {0, 1} )) { - return false; - } + if (!satisfiesCompression(tifd, new int[] {1, 32773})) { + return false; + } - return satisfiesResolutionUnit (tifd, new int [] {1, 2, 3} ); + if (!satisfiesPhotometricInterpretation(tifd, new int[] {0, 1})) { + return false; } + + return satisfiesResolutionUnit(tifd, new int[] {1, 2, 3}); + } } diff --git a/jhove-modules/tiff-hul/src/main/java/edu/harvard/hul/ois/jhove/module/tiff/TiffProfileClassIT.java b/jhove-modules/tiff-hul/src/main/java/edu/harvard/hul/ois/jhove/module/tiff/TiffProfileClassIT.java index 381b76d58..10bee35a9 100644 --- a/jhove-modules/tiff-hul/src/main/java/edu/harvard/hul/ois/jhove/module/tiff/TiffProfileClassIT.java +++ b/jhove-modules/tiff-hul/src/main/java/edu/harvard/hul/ois/jhove/module/tiff/TiffProfileClassIT.java @@ -1,46 +1,40 @@ -/********************************************************************** - * Jhove - JSTOR/Harvard Object Validation Environment - * Copyright 2003 by JSTOR and the President and Fellows of Harvard College - **********************************************************************/ - +/** + * ******************************************************************** Jhove - JSTOR/Harvard Object + * Validation Environment Copyright 2003 by JSTOR and the President and Fellows of Harvard College + * ******************************************************************** + */ package edu.harvard.hul.ois.jhove.module.tiff; import edu.harvard.hul.ois.jhove.*; /** - * Abstract superclass for Tiff Profile Checkers Tiff IP/whatever. + * Abstract superclass for Tiff Profile Checkers Tiff IP/whatever. * - * @author Gary McGath + * @author Gary McGath */ -public abstract class TiffProfileClassIT - extends TiffProfile -{ - public TiffProfileClassIT () - { - super (); - } - - /** - * Returns true if the IFD satisfies the requirements - * which are common to all Tiff IT profiles. - * Subclasses will call this, then apply additional - * tests if it returns true. - */ - @Override - public boolean satisfiesThisProfile (IFD ifd) - { - if (!(ifd instanceof TiffIFD)) { - return false; - } - TiffIFD tifd = (TiffIFD) ifd; +public abstract class TiffProfileClassIT extends TiffProfile { + public TiffProfileClassIT() { + super(); + } - /* Check required tags. */ - NisoImageMetadata niso = tifd.getNisoImageMetadata (); - return !(niso.getImageWidth () == NisoImageMetadata.NULL || - niso.getImageLength () == NisoImageMetadata.NULL || - niso.getStripOffsets () == null || - niso.getStripByteCounts () == null || - niso.getXSamplingFrequency () == null || - niso.getYSamplingFrequency () == null); + /** + * Returns true if the IFD satisfies the requirements which are common to all Tiff IT profiles. + * Subclasses will call this, then apply additional tests if it returns true. + */ + @Override + public boolean satisfiesThisProfile(IFD ifd) { + if (!(ifd instanceof TiffIFD)) { + return false; } + TiffIFD tifd = (TiffIFD) ifd; + + /* Check required tags. */ + NisoImageMetadata niso = tifd.getNisoImageMetadata(); + return !(niso.getImageWidth() == NisoImageMetadata.NULL + || niso.getImageLength() == NisoImageMetadata.NULL + || niso.getStripOffsets() == null + || niso.getStripByteCounts() == null + || niso.getXSamplingFrequency() == null + || niso.getYSamplingFrequency() == null); + } } diff --git a/jhove-modules/tiff-hul/src/main/java/edu/harvard/hul/ois/jhove/module/tiff/TiffProfileClassITBL.java b/jhove-modules/tiff-hul/src/main/java/edu/harvard/hul/ois/jhove/module/tiff/TiffProfileClassITBL.java index 7ca32e831..83863d957 100644 --- a/jhove-modules/tiff-hul/src/main/java/edu/harvard/hul/ois/jhove/module/tiff/TiffProfileClassITBL.java +++ b/jhove-modules/tiff-hul/src/main/java/edu/harvard/hul/ois/jhove/module/tiff/TiffProfileClassITBL.java @@ -1,82 +1,75 @@ -/********************************************************************** - * Jhove - JSTOR/Harvard Object Validation Environment - * Copyright 2003 by JSTOR and the President and Fellows of Harvard College - **********************************************************************/ - +/** + * ******************************************************************** Jhove - JSTOR/Harvard Object + * Validation Environment Copyright 2003 by JSTOR and the President and Fellows of Harvard College + * ******************************************************************** + */ package edu.harvard.hul.ois.jhove.module.tiff; import edu.harvard.hul.ois.jhove.*; /** - * Profile checker for TIFF Class IT-BL. + * Profile checker for TIFF Class IT-BL. * - * The TIFF/IT spec states that "TIFF/IT-BP makes use of all - * the features and functionality supported by the TIFF and - * TIFF/IT fields appropriate to binary line art images." + *

The TIFF/IT spec states that "TIFF/IT-BP makes use of all the features and functionality + * supported by the TIFF and TIFF/IT fields appropriate to binary line art images." * - * @author Gary McGath + * @author Gary McGath */ -public final class TiffProfileClassITBL extends TiffProfileClassIT -{ - public TiffProfileClassITBL () - { - super (); - _profileText = "TIFF/IT-BL (ISO 12639:1998)"; +public final class TiffProfileClassITBL extends TiffProfileClassIT { + public TiffProfileClassITBL() { + super(); + _profileText = "TIFF/IT-BL (ISO 12639:1998)"; + } + + /** + * Returns true if the IFD satisfies the requirements of the profile. See the documentation for + * details. + */ + @Override + public boolean satisfiesThisProfile(IFD ifd) { + if (!super.satisfiesThisProfile(ifd)) { + return false; } - /** - * Returns true if the IFD satisfies the requirements - * of the profile. See the documentation for - * details. - */ - @Override - public boolean satisfiesThisProfile (IFD ifd) - { - if (!super.satisfiesThisProfile (ifd)) { - return false; - } + TiffIFD tifd = (TiffIFD) ifd; + NisoImageMetadata niso = tifd.getNisoImageMetadata(); + int[] bps = niso.getBitsPerSample(); + if (bps == null || bps[0] != 1) { + return false; + } + + if (!satisfiesCompression(tifd, 32898)) { + return false; + } - TiffIFD tifd = (TiffIFD) ifd; - NisoImageMetadata niso = tifd.getNisoImageMetadata (); - int [] bps = niso.getBitsPerSample (); - if (bps == null || bps[0] != 1) { - return false; - } - - if (!satisfiesCompression (tifd, 32898)) { - return false; - } - - if (!satisfiesPhotometricInterpretation (tifd, new int [] {0, 1} )) { - return false; - } - - if (!satisfiesSamplesPerPixel (tifd, 1)) { - return false; - } + if (!satisfiesPhotometricInterpretation(tifd, new int[] {0, 1})) { + return false; + } + + if (!satisfiesSamplesPerPixel(tifd, 1)) { + return false; + } + + /* ImageColorIndicator=0,1, or 2; BackgroundColorIndicator=0,1, or 2; + * ImageColorIndicator=1, but only if ImageColorValue is defined; + * BackgroundColorIndicator=1, + * but only if BackgroundColorValue is defined. + */ + int[] valueVec; + if (tifd.getImageColorValue() != IFD.NULL) { + valueVec = new int[] {1}; + } else { + valueVec = new int[] {0, 1, 2}; + } + if (!satisfiesImageColorIndicator(tifd, valueVec)) { + return false; + } - /* ImageColorIndicator=0,1, or 2; BackgroundColorIndicator=0,1, or 2; - * ImageColorIndicator=1, but only if ImageColorValue is defined; - * BackgroundColorIndicator=1, - * but only if BackgroundColorValue is defined. - */ - int [] valueVec; - if (tifd.getImageColorValue () != IFD.NULL) { - valueVec = new int [] {1}; - } - else { - valueVec = new int [] {0, 1, 2}; - } - if (!satisfiesImageColorIndicator (tifd, valueVec)) { - return false; - } - - if (tifd.getBackgroundColorValue () != IFD.NULL) { - valueVec = new int [] {1}; - } - else { - valueVec = new int [] {0, 1, 2}; - } - return satisfiesBackgroundColorIndicator (tifd, valueVec); + if (tifd.getBackgroundColorValue() != IFD.NULL) { + valueVec = new int[] {1}; + } else { + valueVec = new int[] {0, 1, 2}; } + return satisfiesBackgroundColorIndicator(tifd, valueVec); + } } diff --git a/jhove-modules/tiff-hul/src/main/java/edu/harvard/hul/ois/jhove/module/tiff/TiffProfileClassITBLP1.java b/jhove-modules/tiff-hul/src/main/java/edu/harvard/hul/ois/jhove/module/tiff/TiffProfileClassITBLP1.java index 017e85b78..c32cc2499 100644 --- a/jhove-modules/tiff-hul/src/main/java/edu/harvard/hul/ois/jhove/module/tiff/TiffProfileClassITBLP1.java +++ b/jhove-modules/tiff-hul/src/main/java/edu/harvard/hul/ois/jhove/module/tiff/TiffProfileClassITBLP1.java @@ -1,107 +1,99 @@ -/********************************************************************** - * Jhove - JSTOR/Harvard Object Validation Environment - * Copyright 2003 by JSTOR and the President and Fellows of Harvard College - **********************************************************************/ - +/** + * ******************************************************************** Jhove - JSTOR/Harvard Object + * Validation Environment Copyright 2003 by JSTOR and the President and Fellows of Harvard College + * ******************************************************************** + */ package edu.harvard.hul.ois.jhove.module.tiff; import edu.harvard.hul.ois.jhove.*; /** - * Profile checker for TIFF Class IT-BL/P1. + * Profile checker for TIFF Class IT-BL/P1. * - * The TIFF/IT spec states that "TIFF/IT-BL/P1 is a simplified - * image file format profile for binary line art (BL) image - * data and can be considered a constrained - * subset of TIFF/IT-BL specifically intended for - * simpler implementation." + *

The TIFF/IT spec states that "TIFF/IT-BL/P1 is a simplified image file format profile for + * binary line art (BL) image data and can be considered a constrained subset of TIFF/IT-BL + * specifically intended for simpler implementation." * - * @author Gary McGath + * @author Gary McGath */ -public final class TiffProfileClassITBLP1 extends TiffProfileClassIT -{ - public TiffProfileClassITBLP1 () - { - super (); - _profileText = "TIFF/IT-BL/P1 (ISO 12639:1998)"; +public final class TiffProfileClassITBLP1 extends TiffProfileClassIT { + public TiffProfileClassITBLP1() { + super(); + _profileText = "TIFF/IT-BL/P1 (ISO 12639:1998)"; + } + + /** + * Returns true if the IFD satisfies the requirements of the profile. See the documentation for + * details. + */ + @Override + public boolean satisfiesThisProfile(IFD ifd) { + if (!super.satisfiesThisProfile(ifd)) { + return false; } - /** - * Returns true if the IFD satisfies the requirements - * of the profile. See the documentation for - * details. - */ - @Override - public boolean satisfiesThisProfile (IFD ifd) - { - if (!super.satisfiesThisProfile (ifd)) { - return false; - } + TiffIFD tifd = (TiffIFD) ifd; + + if (!satisfiesNewSubfileType(tifd, 0)) { + return false; + } + + NisoImageMetadata niso = tifd.getNisoImageMetadata(); + int[] bps = niso.getBitsPerSample(); + if (bps == null || bps[0] != 1) { + return false; + } - TiffIFD tifd = (TiffIFD) ifd; - - if (!satisfiesNewSubfileType (tifd, 0)) { - return false; - } - - NisoImageMetadata niso = tifd.getNisoImageMetadata (); - int [] bps = niso.getBitsPerSample (); - if (bps == null || bps[0] != 1) { - return false; - } - - if (!satisfiesCompression (tifd, 32898)) { - return false; - } - - if (!satisfiesPhotometricInterpretation (tifd, 0)) { - return false; - } - - if (!satisfiesOrientation (tifd, 1)) { - return false; - } - - if (!satisfiesSamplesPerPixel (tifd, 1)) { - return false; - } + if (!satisfiesCompression(tifd, 32898)) { + return false; + } + + if (!satisfiesPhotometricInterpretation(tifd, 0)) { + return false; + } - if (!satisfiesResolutionUnit (tifd, new int [] {2, 3} )) { - return false; - } - - /* ImageColorIndicator=0,1, or 2; BackgroundColorIndicator=0,1, or 2; - * ImageColorIndicator=1, but only if ImageColorValue is defined; - * BackgroundColorIndicator=1, - *but only if BackgroundColorValue is defined. - */ - int [] valueVec; - if (tifd.getImageColorValue () != IFD.NULL) { - valueVec = new int [] {1}; - } - else { - valueVec = new int [] {0, 1, 2}; - } - if (!satisfiesImageColorIndicator (tifd, valueVec)) { - return false; - } - - if (tifd.getBackgroundColorValue () != IFD.NULL) { - valueVec = new int [] {1}; - } - else { - valueVec = new int [] {0, 1, 2}; - } - if (!satisfiesBackgroundColorIndicator (tifd, valueVec)) { - return false; - } - /* Tags which must NOT be defined */ - return !(tifd.getDocumentName () != null || - niso.getScannerModelName () != null || - tifd.getPageName () != null || - niso.getHostComputer () != null || - tifd.getSite () != null || - tifd.getColorSequence () != null || - tifd.getIT8Header() != null); + if (!satisfiesOrientation(tifd, 1)) { + return false; + } + + if (!satisfiesSamplesPerPixel(tifd, 1)) { + return false; + } + + if (!satisfiesResolutionUnit(tifd, new int[] {2, 3})) { + return false; + } + + /* ImageColorIndicator=0,1, or 2; BackgroundColorIndicator=0,1, or 2; + * ImageColorIndicator=1, but only if ImageColorValue is defined; + * BackgroundColorIndicator=1, + *but only if BackgroundColorValue is defined. + */ + int[] valueVec; + if (tifd.getImageColorValue() != IFD.NULL) { + valueVec = new int[] {1}; + } else { + valueVec = new int[] {0, 1, 2}; + } + if (!satisfiesImageColorIndicator(tifd, valueVec)) { + return false; + } + + if (tifd.getBackgroundColorValue() != IFD.NULL) { + valueVec = new int[] {1}; + } else { + valueVec = new int[] {0, 1, 2}; + } + if (!satisfiesBackgroundColorIndicator(tifd, valueVec)) { + return false; } + /* Tags which must NOT be defined */ + return !(tifd.getDocumentName() != null + || niso.getScannerModelName() != null + || tifd.getPageName() != null + || niso.getHostComputer() != null + || tifd.getSite() != null + || tifd.getColorSequence() != null + || tifd.getIT8Header() != null); + } } diff --git a/jhove-modules/tiff-hul/src/main/java/edu/harvard/hul/ois/jhove/module/tiff/TiffProfileClassITBP.java b/jhove-modules/tiff-hul/src/main/java/edu/harvard/hul/ois/jhove/module/tiff/TiffProfileClassITBP.java index 858d3011c..f98307fc2 100644 --- a/jhove-modules/tiff-hul/src/main/java/edu/harvard/hul/ois/jhove/module/tiff/TiffProfileClassITBP.java +++ b/jhove-modules/tiff-hul/src/main/java/edu/harvard/hul/ois/jhove/module/tiff/TiffProfileClassITBP.java @@ -1,83 +1,76 @@ -/********************************************************************** - * Jhove - JSTOR/Harvard Object Validation Environment - * Copyright 2003 by JSTOR and the President and Fellows of Harvard College - **********************************************************************/ - +/** + * ******************************************************************** Jhove - JSTOR/Harvard Object + * Validation Environment Copyright 2003 by JSTOR and the President and Fellows of Harvard College + * ******************************************************************** + */ package edu.harvard.hul.ois.jhove.module.tiff; import edu.harvard.hul.ois.jhove.*; /** - * Profile checker for TIFF Class IT-BP. + * Profile checker for TIFF Class IT-BP. * - * The TIFF/IT spec states that "TIFF/IT-BP makes use of all - * the features and functionality supported by the TIFF and - * TIFF/IT fields appropriate to binary picture images." + *

The TIFF/IT spec states that "TIFF/IT-BP makes use of all the features and functionality + * supported by the TIFF and TIFF/IT fields appropriate to binary picture images." * - * @author Gary McGath + * @author Gary McGath */ -public final class TiffProfileClassITBP extends TiffProfileClassIT -{ - public TiffProfileClassITBP () - { - super (); - _profileText = "TIFF/IT-BP (ISO 12639:1998)"; +public final class TiffProfileClassITBP extends TiffProfileClassIT { + public TiffProfileClassITBP() { + super(); + _profileText = "TIFF/IT-BP (ISO 12639:1998)"; + } + + /** + * Returns true if the IFD satisfies the requirements of the profile. See the documentation for + * details. + */ + @Override + public boolean satisfiesThisProfile(IFD ifd) { + if (!super.satisfiesThisProfile(ifd)) { + return false; + } + + // We now know it's a TiffIFD + TiffIFD tifd = (TiffIFD) ifd; + NisoImageMetadata niso = tifd.getNisoImageMetadata(); + int[] bps = niso.getBitsPerSample(); + if (bps == null || bps[0] != 1) { + return false; + } + + if (!satisfiesCompression(tifd, 1)) { + return false; + } + + if (!satisfiesPhotometricInterpretation(tifd, new int[] {0, 1})) { + return false; } - /** - * Returns true if the IFD satisfies the requirements - * of the profile. See the documentation for - * details. + if (!satisfiesSamplesPerPixel(tifd, 1)) { + return false; + } + + /* ImageColorIndicator=0,1, or 2; BackgroundColorIndicator=0,1, or 2; + * ImageColorIndicator=1, but only if ImageColorValue is defined; + * BackgroundColorIndicator=1, + * but only if BackgroundColorValue is defined. */ - @Override - public boolean satisfiesThisProfile (IFD ifd) - { - if (!super.satisfiesThisProfile (ifd)) { - return false; - } - - // We now know it's a TiffIFD - TiffIFD tifd = (TiffIFD) ifd; - NisoImageMetadata niso = tifd.getNisoImageMetadata (); - int [] bps = niso.getBitsPerSample (); - if (bps == null || bps[0] != 1) { - return false; - } - - if (!satisfiesCompression (tifd, 1)) { - return false; - } - - if (!satisfiesPhotometricInterpretation (tifd, new int[] {0, 1} )) { - return false; - } - - if (!satisfiesSamplesPerPixel (tifd, 1)) { - return false; - } + int[] valueVec; + if (tifd.getImageColorValue() != IFD.NULL) { + valueVec = new int[] {1}; + } else { + valueVec = new int[] {0, 1, 2}; + } + if (!satisfiesImageColorIndicator(tifd, valueVec)) { + return false; + } - /* ImageColorIndicator=0,1, or 2; BackgroundColorIndicator=0,1, or 2; - * ImageColorIndicator=1, but only if ImageColorValue is defined; - * BackgroundColorIndicator=1, - * but only if BackgroundColorValue is defined. - */ - int [] valueVec; - if (tifd.getImageColorValue () != IFD.NULL) { - valueVec = new int [] {1}; - } - else { - valueVec = new int [] {0, 1, 2}; - } - if (!satisfiesImageColorIndicator (tifd, valueVec)) { - return false; - } - - if (tifd.getBackgroundColorValue () != IFD.NULL) { - valueVec = new int [] {1}; - } - else { - valueVec = new int [] {0, 1, 2}; - } - return satisfiesBackgroundColorIndicator (tifd, valueVec); + if (tifd.getBackgroundColorValue() != IFD.NULL) { + valueVec = new int[] {1}; + } else { + valueVec = new int[] {0, 1, 2}; } + return satisfiesBackgroundColorIndicator(tifd, valueVec); + } } diff --git a/jhove-modules/tiff-hul/src/main/java/edu/harvard/hul/ois/jhove/module/tiff/TiffProfileClassITBPP1.java b/jhove-modules/tiff-hul/src/main/java/edu/harvard/hul/ois/jhove/module/tiff/TiffProfileClassITBPP1.java index 5ae77b011..0c0e2828e 100644 --- a/jhove-modules/tiff-hul/src/main/java/edu/harvard/hul/ois/jhove/module/tiff/TiffProfileClassITBPP1.java +++ b/jhove-modules/tiff-hul/src/main/java/edu/harvard/hul/ois/jhove/module/tiff/TiffProfileClassITBPP1.java @@ -1,100 +1,92 @@ -/********************************************************************** - * Jhove - JSTOR/Harvard Object Validation Environment - * Copyright 2003 by JSTOR and the President and Fellows of Harvard College - **********************************************************************/ - +/** + * ******************************************************************** Jhove - JSTOR/Harvard Object + * Validation Environment Copyright 2003 by JSTOR and the President and Fellows of Harvard College + * ******************************************************************** + */ package edu.harvard.hul.ois.jhove.module.tiff; import edu.harvard.hul.ois.jhove.*; /** - * Profile checker for TIFF Class IT-BP/P1. + * Profile checker for TIFF Class IT-BP/P1. * - * The TIFF/IT spec states that "TIFF/IT-BP/P1 is a simplified - * image file format profile for binary picture (BP) image - * data and can be considred a constrained - * subset of TIFF/IT-BP specifically intended for - * simpler implementation." + *

The TIFF/IT spec states that "TIFF/IT-BP/P1 is a simplified image file format profile for + * binary picture (BP) image data and can be considred a constrained subset of TIFF/IT-BP + * specifically intended for simpler implementation." * - * @author Gary McGath + * @author Gary McGath */ -public final class TiffProfileClassITBPP1 extends TiffProfileClassIT -{ - public TiffProfileClassITBPP1 () - { - super (); - _profileText = "TIFF/IT-BP/P1 (ISO 12639:1998)"; +public final class TiffProfileClassITBPP1 extends TiffProfileClassIT { + public TiffProfileClassITBPP1() { + super(); + _profileText = "TIFF/IT-BP/P1 (ISO 12639:1998)"; + } + + /** + * Returns true if the IFD satisfies the requirements of the profile. See the documentation for + * details. + */ + @Override + public boolean satisfiesThisProfile(IFD ifd) { + if (!super.satisfiesThisProfile(ifd)) { + return false; + } + + if (!(ifd instanceof TiffIFD)) { + return false; + } + TiffIFD tifd = (TiffIFD) ifd; + NisoImageMetadata niso = tifd.getNisoImageMetadata(); + if (!satisfiesNewSubfileType(tifd, 0)) { + return false; + } + + if (!satisfiesCompression(tifd, 1)) { + return false; } - /** - * Returns true if the IFD satisfies the requirements - * of the profile. See the documentation for - * details. + if (!satisfiesPhotometricInterpretation(tifd, 0)) { + return false; + } + + if (!satisfiesSamplesPerPixel(tifd, 1)) { + return false; + } + + if (!satisfiesResolutionUnit(tifd, new int[] {2, 3})) { + return false; + } + + /* ImageColorIndicator=0,1, or 2; BackgroundColorIndicator=0,1, or 2; + * ImageColorIndicator=1, but only if ImageColorValue is defined; + * BackgroundColorIndicator=1, + * but only if BackgroundColorValue is defined. */ - @Override - public boolean satisfiesThisProfile (IFD ifd) - { - if (!super.satisfiesThisProfile (ifd)) { - return false; - } - - if (!(ifd instanceof TiffIFD)) { - return false; - } - TiffIFD tifd = (TiffIFD) ifd; - NisoImageMetadata niso = tifd.getNisoImageMetadata (); - if (!satisfiesNewSubfileType (tifd, 0)) { - return false; - } + int[] valueVec; + if (tifd.getImageColorValue() != IFD.NULL) { + valueVec = new int[] {1}; + } else { + valueVec = new int[] {0, 1, 2}; + } + if (!satisfiesImageColorIndicator(tifd, valueVec)) { + return false; + } - if (!satisfiesCompression (tifd, 1)) { - return false; - } - - if (!satisfiesPhotometricInterpretation (tifd, 0)) { - return false; - } - - if (!satisfiesSamplesPerPixel (tifd, 1)) { - return false; - } - - if (!satisfiesResolutionUnit (tifd, new int[] {2, 3} )) { - return false; - } - - /* ImageColorIndicator=0,1, or 2; BackgroundColorIndicator=0,1, or 2; - * ImageColorIndicator=1, but only if ImageColorValue is defined; - * BackgroundColorIndicator=1, - * but only if BackgroundColorValue is defined. - */ - int [] valueVec; - if (tifd.getImageColorValue () != IFD.NULL) { - valueVec = new int [] {1}; - } - else { - valueVec = new int [] {0, 1, 2}; - } - if (!satisfiesImageColorIndicator (tifd, valueVec)) { - return false; - } - - if (tifd.getBackgroundColorValue () != IFD.NULL) { - valueVec = new int [] {1}; - } - else { - valueVec = new int [] {0, 1, 2}; - } - if (!satisfiesBackgroundColorIndicator (tifd, valueVec)) { - return false; - } - /* Tags which must NOT be defined */ - return !(tifd.getDocumentName () != null || - niso.getScannerModelName () != null || - tifd.getPageName () != null || - niso.getHostComputer () != null || - tifd.getSite () != null || - tifd.getColorSequence () != null || - tifd.getIT8Header() != null); + if (tifd.getBackgroundColorValue() != IFD.NULL) { + valueVec = new int[] {1}; + } else { + valueVec = new int[] {0, 1, 2}; + } + if (!satisfiesBackgroundColorIndicator(tifd, valueVec)) { + return false; } + /* Tags which must NOT be defined */ + return !(tifd.getDocumentName() != null + || niso.getScannerModelName() != null + || tifd.getPageName() != null + || niso.getHostComputer() != null + || tifd.getSite() != null + || tifd.getColorSequence() != null + || tifd.getIT8Header() != null); + } } diff --git a/jhove-modules/tiff-hul/src/main/java/edu/harvard/hul/ois/jhove/module/tiff/TiffProfileClassITBPP2.java b/jhove-modules/tiff-hul/src/main/java/edu/harvard/hul/ois/jhove/module/tiff/TiffProfileClassITBPP2.java index 35398fd69..f1b3ddb24 100644 --- a/jhove-modules/tiff-hul/src/main/java/edu/harvard/hul/ois/jhove/module/tiff/TiffProfileClassITBPP2.java +++ b/jhove-modules/tiff-hul/src/main/java/edu/harvard/hul/ois/jhove/module/tiff/TiffProfileClassITBPP2.java @@ -1,95 +1,88 @@ -/********************************************************************** - * Jhove - JSTOR/Harvard Object Validation Environment - * Copyright 2003 by JSTOR and the President and Fellows of Harvard College - **********************************************************************/ - +/** + * ******************************************************************** Jhove - JSTOR/Harvard Object + * Validation Environment Copyright 2003 by JSTOR and the President and Fellows of Harvard College + * ******************************************************************** + */ package edu.harvard.hul.ois.jhove.module.tiff; import edu.harvard.hul.ois.jhove.*; /** - * Profile checker for TIFF Class IT-BP/P2. + * Profile checker for TIFF Class IT-BP/P2. * - * The TIFF/IT spec states that "TIFF/IT-BP/P2 is an extension of - * TIFF/IT-BP/P1." + *

The TIFF/IT spec states that "TIFF/IT-BP/P2 is an extension of TIFF/IT-BP/P1." * - * @author Gary McGath + * @author Gary McGath */ -public final class TiffProfileClassITBPP2 extends TiffProfileClassIT -{ - public TiffProfileClassITBPP2 () - { - super (); - _profileText = "TIFF/IT-BP/P2 (ISO 12639:1998)"; +public final class TiffProfileClassITBPP2 extends TiffProfileClassIT { + public TiffProfileClassITBPP2() { + super(); + _profileText = "TIFF/IT-BP/P2 (ISO 12639:1998)"; + } + + /** + * Returns true if the IFD satisfies the requirements of the profile. See the documentation for + * details. + */ + @Override + public boolean satisfiesThisProfile(IFD ifd) { + if (!super.satisfiesThisProfile(ifd)) { + return false; } - /** - * Returns true if the IFD satisfies the requirements - * of the profile. See the documentation for - * details. + // We now know it's a TiffIFD + TiffIFD tifd = (TiffIFD) ifd; + NisoImageMetadata niso = tifd.getNisoImageMetadata(); + if (!satisfiesNewSubfileType(tifd, 0)) { + return false; + } + + if (!satisfiesCompression(tifd, new int[] {1, 4, 8})) { + return false; + } + + if (!satisfiesPhotometricInterpretation(tifd, 0)) { + return false; + } + + if (!satisfiesSamplesPerPixel(tifd, 1)) { + return false; + } + + if (!satisfiesResolutionUnit(tifd, new int[] {2, 3})) { + return false; + } + + /* ImageColorIndicator=0,1, or 2; BackgroundColorIndicator=0,1, or 2; + * ImageColorIndicator=1, but only if ImageColorValue is defined; + * BackgroundColorIndicator=1, + * but only if BackgroundColorValue is defined. */ - @Override - public boolean satisfiesThisProfile (IFD ifd) - { - if (!super.satisfiesThisProfile (ifd)) { - return false; - } - - // We now know it's a TiffIFD - TiffIFD tifd = (TiffIFD) ifd; - NisoImageMetadata niso = tifd.getNisoImageMetadata (); - if (!satisfiesNewSubfileType (tifd, 0)) { - return false; - } + int[] valueVec; + if (tifd.getImageColorValue() != IFD.NULL) { + valueVec = new int[] {1}; + } else { + valueVec = new int[] {0, 1, 2}; + } + if (!satisfiesImageColorIndicator(tifd, valueVec)) { + return false; + } - if (!satisfiesCompression (tifd, new int[] {1, 4, 8} )) { - return false; - } - - if (!satisfiesPhotometricInterpretation (tifd, 0)) { - return false; - } - - if (!satisfiesSamplesPerPixel (tifd, 1)) { - return false; - } - - if (!satisfiesResolutionUnit (tifd, new int[] {2, 3} )) { - return false; - } - - /* ImageColorIndicator=0,1, or 2; BackgroundColorIndicator=0,1, or 2; - * ImageColorIndicator=1, but only if ImageColorValue is defined; - * BackgroundColorIndicator=1, - * but only if BackgroundColorValue is defined. - */ - int [] valueVec; - if (tifd.getImageColorValue () != IFD.NULL) { - valueVec = new int [] {1}; - } - else { - valueVec = new int [] {0, 1, 2}; - } - if (!satisfiesImageColorIndicator (tifd, valueVec)) { - return false; - } - - if (tifd.getBackgroundColorValue () != IFD.NULL) { - valueVec = new int [] {1}; - } - else { - valueVec = new int [] {0, 1, 2}; - } - if (!satisfiesBackgroundColorIndicator (tifd, valueVec)) { - return false; - } - /* Tags which must NOT be defined */ - return !(tifd.getDocumentName () != null || - niso.getScannerModelName () != null || - tifd.getPageName () != null || - niso.getHostComputer () != null || - tifd.getSite () != null || - tifd.getColorSequence () != null || - tifd.getIT8Header() != null); + if (tifd.getBackgroundColorValue() != IFD.NULL) { + valueVec = new int[] {1}; + } else { + valueVec = new int[] {0, 1, 2}; + } + if (!satisfiesBackgroundColorIndicator(tifd, valueVec)) { + return false; } + /* Tags which must NOT be defined */ + return !(tifd.getDocumentName() != null + || niso.getScannerModelName() != null + || tifd.getPageName() != null + || niso.getHostComputer() != null + || tifd.getSite() != null + || tifd.getColorSequence() != null + || tifd.getIT8Header() != null); + } } diff --git a/jhove-modules/tiff-hul/src/main/java/edu/harvard/hul/ois/jhove/module/tiff/TiffProfileClassITCT.java b/jhove-modules/tiff-hul/src/main/java/edu/harvard/hul/ois/jhove/module/tiff/TiffProfileClassITCT.java index 2b39fdb13..abbb86c2c 100644 --- a/jhove-modules/tiff-hul/src/main/java/edu/harvard/hul/ois/jhove/module/tiff/TiffProfileClassITCT.java +++ b/jhove-modules/tiff-hul/src/main/java/edu/harvard/hul/ois/jhove/module/tiff/TiffProfileClassITCT.java @@ -1,72 +1,68 @@ -/********************************************************************** - * Jhove - JSTOR/Harvard Object Validation Environment - * Copyright 2003 by JSTOR and the President and Fellows of Harvard College - **********************************************************************/ - +/** + * ******************************************************************** Jhove - JSTOR/Harvard Object + * Validation Environment Copyright 2003 by JSTOR and the President and Fellows of Harvard College + * ******************************************************************** + */ package edu.harvard.hul.ois.jhove.module.tiff; import edu.harvard.hul.ois.jhove.*; /** - * Profile checker for TIFF Class IT-CT. + * Profile checker for TIFF Class IT-CT. * - * @author Gary McGath + * @author Gary McGath */ -public final class TiffProfileClassITCT extends TiffProfileClassIT -{ - public TiffProfileClassITCT () - { - super (); - _profileText = "TIFF/IT-CT (ISO 12639:1998)"; - } - - /** - * Returns true if the IFD satisfies the requirements - * of the profile. See the documentation for - * details. - */ - @Override - public boolean satisfiesThisProfile (IFD ifd) - { - if (!super.satisfiesThisProfile (ifd)) { - return false; - } +public final class TiffProfileClassITCT extends TiffProfileClassIT { + public TiffProfileClassITCT() { + super(); + _profileText = "TIFF/IT-CT (ISO 12639:1998)"; + } - // We now know it's a TiffIFD - TiffIFD tifd = (TiffIFD) ifd; + /** + * Returns true if the IFD satisfies the requirements of the profile. See the documentation for + * details. + */ + @Override + public boolean satisfiesThisProfile(IFD ifd) { + if (!super.satisfiesThisProfile(ifd)) { + return false; + } - /* Check required tags.*/ - NisoImageMetadata niso = tifd.getNisoImageMetadata (); - if (niso.getBitsPerSample () == null || - niso.getSamplesPerPixel () == NisoImageMetadata.NULL || - niso.getSamplingFrequencyUnit () == NisoImageMetadata.NULL) { - return false; - } + // We now know it's a TiffIFD + TiffIFD tifd = (TiffIFD) ifd; - /* Check required values. */ + /* Check required tags.*/ + NisoImageMetadata niso = tifd.getNisoImageMetadata(); + if (niso.getBitsPerSample() == null + || niso.getSamplesPerPixel() == NisoImageMetadata.NULL + || niso.getSamplingFrequencyUnit() == NisoImageMetadata.NULL) { + return false; + } - if (!satisfiesPhotometricInterpretation (tifd, 5)) { - return false; - } + /* Check required values. */ - if (!satisfiesCompression (tifd, new int [] {1, 32895} )) { - return false; - /* NOTE: If compression is 32895, RasterPadding must be - 0, 1, 2, 9, or 10. Fix this when RasterPadding support is - implemented. */ - } + if (!satisfiesPhotometricInterpretation(tifd, 5)) { + return false; + } - int inkSet = tifd.getInkSet (); - if (inkSet != 1 && inkSet != 2) { - return false; - } - String seq = tifd.getColorSequence (); - if ((seq == null || "CMYK".equals (seq)) && inkSet != 1) { - return false; - } + if (!satisfiesCompression(tifd, new int[] {1, 32895})) { + return false; + /* NOTE: If compression is 32895, RasterPadding must be + 0, 1, 2, 9, or 10. Fix this when RasterPadding support is + implemented. */ + } - int spp = niso.getSamplesPerPixel (); - int numInks = tifd.getNumberOfInks (); - return !(numInks != NisoImageMetadata.NULL && numInks != spp); + int inkSet = tifd.getInkSet(); + if (inkSet != 1 && inkSet != 2) { + return false; } + String seq = tifd.getColorSequence(); + if ((seq == null || "CMYK".equals(seq)) && inkSet != 1) { + return false; + } + + int spp = niso.getSamplesPerPixel(); + int numInks = tifd.getNumberOfInks(); + return !(numInks != NisoImageMetadata.NULL && numInks != spp); + } } diff --git a/jhove-modules/tiff-hul/src/main/java/edu/harvard/hul/ois/jhove/module/tiff/TiffProfileClassITCTP1.java b/jhove-modules/tiff-hul/src/main/java/edu/harvard/hul/ois/jhove/module/tiff/TiffProfileClassITCTP1.java index 0a606e3fa..ff5893f52 100644 --- a/jhove-modules/tiff-hul/src/main/java/edu/harvard/hul/ois/jhove/module/tiff/TiffProfileClassITCTP1.java +++ b/jhove-modules/tiff-hul/src/main/java/edu/harvard/hul/ois/jhove/module/tiff/TiffProfileClassITCTP1.java @@ -1,101 +1,97 @@ -/********************************************************************** - * Jhove - JSTOR/Harvard Object Validation Environment - * Copyright 2003 by JSTOR and the President and Fellows of Harvard College - **********************************************************************/ - +/** + * ******************************************************************** Jhove - JSTOR/Harvard Object + * Validation Environment Copyright 2003 by JSTOR and the President and Fellows of Harvard College + * ******************************************************************** + */ package edu.harvard.hul.ois.jhove.module.tiff; import edu.harvard.hul.ois.jhove.*; /** - * Profile checker for TIFF Class IT-CT/P1. + * Profile checker for TIFF Class IT-CT/P1. * - * @author Gary McGath + * @author Gary McGath */ -public final class TiffProfileClassITCTP1 extends TiffProfileClassIT -{ - public TiffProfileClassITCTP1 () - { - super (); - _profileText = "TIFF/IT-CT/P1 (ISO 12639:1998)"; +public final class TiffProfileClassITCTP1 extends TiffProfileClassIT { + public TiffProfileClassITCTP1() { + super(); + _profileText = "TIFF/IT-CT/P1 (ISO 12639:1998)"; + } + + /** + * Returns true if the IFD satisfies the requirements of the profile. See the documentation for + * details. + */ + @Override + public boolean satisfiesThisProfile(IFD ifd) { + if (!super.satisfiesThisProfile(ifd)) { + return false; } - /** - * Returns true if the IFD satisfies the requirements - * of the profile. See the documentation for - * details. - */ - @Override - public boolean satisfiesThisProfile (IFD ifd) - { - if (!super.satisfiesThisProfile (ifd)) { - return false; - } - - // We now know that this is a TiffIFD - TiffIFD tifd = (TiffIFD) ifd; + // We now know that this is a TiffIFD + TiffIFD tifd = (TiffIFD) ifd; - /* Check required tags.*/ - if (!satisfiesNewSubfileType (tifd, 0)) { - return false; - } - NisoImageMetadata niso = tifd.getNisoImageMetadata (); + /* Check required tags.*/ + if (!satisfiesNewSubfileType(tifd, 0)) { + return false; + } + NisoImageMetadata niso = tifd.getNisoImageMetadata(); - // bps must be { 8, 8, 8, 8 } - int [] bps = niso.getBitsPerSample (); - if (bps == null || bps.length != 4) { - return false; - } - for (int i = 0; i < 4; i++) { - if (bps[i] != 8) { - return false; - } - } + // bps must be { 8, 8, 8, 8 } + int[] bps = niso.getBitsPerSample(); + if (bps == null || bps.length != 4) { + return false; + } + for (int i = 0; i < 4; i++) { + if (bps[i] != 8) { + return false; + } + } - if (!satisfiesCompression (tifd, 1 )) { - return false; - } + if (!satisfiesCompression(tifd, 1)) { + return false; + } - if (!satisfiesPhotometricInterpretation (tifd, 5)) { - return false; - } + if (!satisfiesPhotometricInterpretation(tifd, 5)) { + return false; + } - if (!satisfiesOrientation (tifd, 1)) { - return false; - } + if (!satisfiesOrientation(tifd, 1)) { + return false; + } - if (!satisfiesSamplesPerPixel (tifd, 4)) { - return false; - } + if (!satisfiesSamplesPerPixel(tifd, 4)) { + return false; + } - if (!satisfiesPlanarConfiguration (tifd, 1)) { - return false; - } + if (!satisfiesPlanarConfiguration(tifd, 1)) { + return false; + } - if (!satisfiesResolutionUnit (tifd, new int [] {2, 3} )) { - return false; - } + if (!satisfiesResolutionUnit(tifd, new int[] {2, 3})) { + return false; + } - int inkSet = tifd.getInkSet (); - if (inkSet != 1) { - return false; - } + int inkSet = tifd.getInkSet(); + if (inkSet != 1) { + return false; + } - int numInks = tifd.getNumberOfInks (); - if (numInks != 4) { - return false; - } + int numInks = tifd.getNumberOfInks(); + if (numInks != 4) { + return false; + } - if (!satisfiesDotRange (tifd, 0, 255)) { - return false; - } - /* Tags which must NOT be defined */ - return !(tifd.getDocumentName () != null || - niso.getScannerModelName () != null || - tifd.getPageName () != null || - niso.getHostComputer () != null || - tifd.getSite () != null || - tifd.getColorSequence () != null || - tifd.getIT8Header() != null); + if (!satisfiesDotRange(tifd, 0, 255)) { + return false; } + /* Tags which must NOT be defined */ + return !(tifd.getDocumentName() != null + || niso.getScannerModelName() != null + || tifd.getPageName() != null + || niso.getHostComputer() != null + || tifd.getSite() != null + || tifd.getColorSequence() != null + || tifd.getIT8Header() != null); + } } diff --git a/jhove-modules/tiff-hul/src/main/java/edu/harvard/hul/ois/jhove/module/tiff/TiffProfileClassITCTP2.java b/jhove-modules/tiff-hul/src/main/java/edu/harvard/hul/ois/jhove/module/tiff/TiffProfileClassITCTP2.java index f8dfd5fff..216437fe7 100644 --- a/jhove-modules/tiff-hul/src/main/java/edu/harvard/hul/ois/jhove/module/tiff/TiffProfileClassITCTP2.java +++ b/jhove-modules/tiff-hul/src/main/java/edu/harvard/hul/ois/jhove/module/tiff/TiffProfileClassITCTP2.java @@ -1,99 +1,95 @@ -/********************************************************************** - * Jhove - JSTOR/Harvard Object Validation Environment - * Copyright 2003 by JSTOR and the President and Fellows of Harvard College - **********************************************************************/ - +/** + * ******************************************************************** Jhove - JSTOR/Harvard Object + * Validation Environment Copyright 2003 by JSTOR and the President and Fellows of Harvard College + * ******************************************************************** + */ package edu.harvard.hul.ois.jhove.module.tiff; import edu.harvard.hul.ois.jhove.*; /** - * Profile checker for TIFF Class IT-CT/P2. + * Profile checker for TIFF Class IT-CT/P2. * - * @author Gary McGath + * @author Gary McGath */ -public final class TiffProfileClassITCTP2 extends TiffProfileClassIT -{ - public TiffProfileClassITCTP2 () - { - super (); - _profileText = "TIFF/IT-CT/P2 (ISO 12639:2003)"; +public final class TiffProfileClassITCTP2 extends TiffProfileClassIT { + public TiffProfileClassITCTP2() { + super(); + _profileText = "TIFF/IT-CT/P2 (ISO 12639:2003)"; + } + + /** + * Returns true if the IFD satisfies the requirements of the profile. See the documentation for + * details. + */ + @Override + public boolean satisfiesThisProfile(IFD ifd) { + if (!super.satisfiesThisProfile(ifd)) { + return false; } - /** - * Returns true if the IFD satisfies the requirements - * of the profile. See the documentation for - * details. - */ - @Override - public boolean satisfiesThisProfile (IFD ifd) - { - if (!super.satisfiesThisProfile (ifd)) { - return false; - } - - // We now know this is a TiffIFD - TiffIFD tifd = (TiffIFD) ifd; + // We now know this is a TiffIFD + TiffIFD tifd = (TiffIFD) ifd; - /* Check required tags.*/ - if (!satisfiesNewSubfileType (tifd, 0)) { - return false; - } - NisoImageMetadata niso = tifd.getNisoImageMetadata (); + /* Check required tags.*/ + if (!satisfiesNewSubfileType(tifd, 0)) { + return false; + } + NisoImageMetadata niso = tifd.getNisoImageMetadata(); - // bps must be { 8, ... } - int [] bps = niso.getBitsPerSample (); - if (bps == null) { - return false; - } - if (bps[0] != 8) { - return false; - } + // bps must be { 8, ... } + int[] bps = niso.getBitsPerSample(); + if (bps == null) { + return false; + } + if (bps[0] != 8) { + return false; + } - if (!satisfiesCompression (tifd, new int[] {1, 7, 8} )) { - return false; - } + if (!satisfiesCompression(tifd, new int[] {1, 7, 8})) { + return false; + } - if (!satisfiesPhotometricInterpretation (tifd, 5)) { - return false; - } + if (!satisfiesPhotometricInterpretation(tifd, 5)) { + return false; + } - if (!satisfiesOrientation (tifd, 1)) { - return false; - } + if (!satisfiesOrientation(tifd, 1)) { + return false; + } - if (!satisfiesSamplesPerPixel (tifd, 4)) { - return false; - } + if (!satisfiesSamplesPerPixel(tifd, 4)) { + return false; + } - if (!satisfiesPlanarConfiguration (tifd, 1)) { - return false; - } + if (!satisfiesPlanarConfiguration(tifd, 1)) { + return false; + } - if (!satisfiesResolutionUnit (tifd, new int [] {2, 3} )) { - return false; - } + if (!satisfiesResolutionUnit(tifd, new int[] {2, 3})) { + return false; + } - int inkSet = tifd.getInkSet (); - if (inkSet != 1) { - return false; - } + int inkSet = tifd.getInkSet(); + if (inkSet != 1) { + return false; + } - int numInks = tifd.getNumberOfInks (); - if (numInks != 4) { - return false; - } + int numInks = tifd.getNumberOfInks(); + if (numInks != 4) { + return false; + } - if (!satisfiesDotRange (tifd, 0, 255)) { - return false; - } - /* Tags which must NOT be defined */ - return !(tifd.getDocumentName () != null || - niso.getScannerModelName () != null || - tifd.getPageName () != null || - niso.getHostComputer () != null || - tifd.getSite () != null || - tifd.getColorSequence () != null || - tifd.getIT8Header() != null); + if (!satisfiesDotRange(tifd, 0, 255)) { + return false; } + /* Tags which must NOT be defined */ + return !(tifd.getDocumentName() != null + || niso.getScannerModelName() != null + || tifd.getPageName() != null + || niso.getHostComputer() != null + || tifd.getSite() != null + || tifd.getColorSequence() != null + || tifd.getIT8Header() != null); + } } diff --git a/jhove-modules/tiff-hul/src/main/java/edu/harvard/hul/ois/jhove/module/tiff/TiffProfileClassITFP.java b/jhove-modules/tiff-hul/src/main/java/edu/harvard/hul/ois/jhove/module/tiff/TiffProfileClassITFP.java index 205aca6f8..a191e552a 100644 --- a/jhove-modules/tiff-hul/src/main/java/edu/harvard/hul/ois/jhove/module/tiff/TiffProfileClassITFP.java +++ b/jhove-modules/tiff-hul/src/main/java/edu/harvard/hul/ois/jhove/module/tiff/TiffProfileClassITFP.java @@ -1,96 +1,86 @@ -/********************************************************************** - * Jhove - JSTOR/Harvard Object Validation Environment - * Copyright 2003 by JSTOR and the President and Fellows of Harvard College - **********************************************************************/ - +/** + * ******************************************************************** Jhove - JSTOR/Harvard Object + * Validation Environment Copyright 2003 by JSTOR and the President and Fellows of Harvard College + * ******************************************************************** + */ package edu.harvard.hul.ois.jhove.module.tiff; import edu.harvard.hul.ois.jhove.*; /** - * Profile checker for TIFF Class IT-FP. + * Profile checker for TIFF Class IT-FP. * - * The TIFF/IT spec states that "TIFF/IT-FP provides a mechanism for - * associating image files of the different types that make up a - * final page." Note that Jhove profiles are applied to individual - * IFD levels, so this profile does not check the relationships among - * IFDs which are part of the FP specification. + *

The TIFF/IT spec states that "TIFF/IT-FP provides a mechanism for associating image files of + * the different types that make up a final page." Note that Jhove profiles are applied to + * individual IFD levels, so this profile does not check the relationships among IFDs which are part + * of the FP specification. * - * @author Gary McGath + * @author Gary McGath */ -public final class TiffProfileClassITFP extends TiffProfileClassIT -{ - public TiffProfileClassITFP () - { - super (); - _profileText = "TIFF/IT-FP"; +public final class TiffProfileClassITFP extends TiffProfileClassIT { + public TiffProfileClassITFP() { + super(); + _profileText = "TIFF/IT-FP"; + } + + /** + * Returns true if the IFD satisfies the requirements of the profile. See the documentation for + * details. + */ + @Override + public boolean satisfiesThisProfile(IFD ifd) { + if (!super.satisfiesThisProfile(ifd)) { + return false; + } + + // We now know this is a TiffIFD + TiffIFD tifd = (TiffIFD) ifd; + + if (tifd.getImageDescription() == null) { + return false; } - /** - * Returns true if the IFD satisfies the requirements - * of the profile. See the documentation for - * details. - */ - @Override - public boolean satisfiesThisProfile (IFD ifd) - { - if (!super.satisfiesThisProfile (ifd)) { - return false; - } + // BitsPerSample=4 or 8 or {8,8,8} or {8,8,8,8} or undefined + // (consistent with PhotometricInterpretation) + NisoImageMetadata niso = tifd.getNisoImageMetadata(); + int[] bps = niso.getBitsPerSample(); + if (!(bps == null + || (bps.length == 1 && (bps[0] == 4 || bps[0] == 8)) + || (bps.length == 3 && bps[0] == 8 && bps[1] == 8 && bps[2] == 8) + || (bps.length == 4 && bps[0] == 8 && bps[1] == 8 && bps[2] == 8 && bps[3] == 8))) { + return false; + } - // We now know this is a TiffIFD - TiffIFD tifd = (TiffIFD) ifd; - - if (tifd.getImageDescription () == null) { - return false; - } + // NewSubfileType bit 3=1 + long nsft = tifd.getNewSubfileType(); + if ((nsft & 8) == 0) { + return false; + } - // BitsPerSample=4 or 8 or {8,8,8} or {8,8,8,8} or undefined - // (consistent with PhotometricInterpretation) - NisoImageMetadata niso = tifd.getNisoImageMetadata (); - int [] bps = niso.getBitsPerSample (); - if (!(bps == null || - (bps.length == 1 && (bps[0] == 4 || bps[0] == 8)) || - (bps.length == 3 && bps[0] == 8 && bps[1] == 8 && bps[2] == 8) || - (bps.length == 4 && bps[0] == 8 && - bps[1] == 8 && bps[2] == 8 && bps[3] == 8))) { - return false; - } - - - // NewSubfileType bit 3=1 - long nsft = tifd.getNewSubfileType (); - if ((nsft & 8) == 0) { - return false; - } + if (!satisfiesPhotometricInterpretation(tifd, new int[] {0, 1, 2, 5})) { + return false; + } - if (!satisfiesPhotometricInterpretation (tifd, new int [] {0, 1, 2, - 5} )) { - return false; - } - - // SamplesPerPixel=3 or 4 or undefined (consistent with - // PhotometricInterpretation) - if (!satisfiesSamplesPerPixel (tifd, new int [] {3, 4, - NisoImageMetadata.NULL} )) { - return false; - } - - if (!satisfiesCompression (tifd, 1)) { - return false; - } + // SamplesPerPixel=3 or 4 or undefined (consistent with + // PhotometricInterpretation) + if (!satisfiesSamplesPerPixel(tifd, new int[] {3, 4, NisoImageMetadata.NULL})) { + return false; + } - if (!satisfiesPlanarConfiguration (tifd, 1)) { - return false; - } - - // InkSet=1, but only if PhotometricInterpretation=5 - // NumberOfInks=4, but only if PhotometricInterpretation=5 - // DotRange={0,255}, but only if PhotometricInterpretation=5 - int pint = niso.getColorSpace (); - int inkSet = tifd.getInkSet (); - int nInks = tifd.getNumberOfInks (); - return !(pint == 5 && (inkSet != 1 || nInks != 4 || - !satisfiesDotRange (tifd, 0, 255))); + if (!satisfiesCompression(tifd, 1)) { + return false; } + + if (!satisfiesPlanarConfiguration(tifd, 1)) { + return false; + } + + // InkSet=1, but only if PhotometricInterpretation=5 + // NumberOfInks=4, but only if PhotometricInterpretation=5 + // DotRange={0,255}, but only if PhotometricInterpretation=5 + int pint = niso.getColorSpace(); + int inkSet = tifd.getInkSet(); + int nInks = tifd.getNumberOfInks(); + return !(pint == 5 && (inkSet != 1 || nInks != 4 || !satisfiesDotRange(tifd, 0, 255))); + } } diff --git a/jhove-modules/tiff-hul/src/main/java/edu/harvard/hul/ois/jhove/module/tiff/TiffProfileClassITFPP1.java b/jhove-modules/tiff-hul/src/main/java/edu/harvard/hul/ois/jhove/module/tiff/TiffProfileClassITFPP1.java index c6b7ec6c2..e6d11bf4a 100644 --- a/jhove-modules/tiff-hul/src/main/java/edu/harvard/hul/ois/jhove/module/tiff/TiffProfileClassITFPP1.java +++ b/jhove-modules/tiff-hul/src/main/java/edu/harvard/hul/ois/jhove/module/tiff/TiffProfileClassITFPP1.java @@ -1,109 +1,101 @@ -/********************************************************************** - * Jhove - JSTOR/Harvard Object Validation Environment - * Copyright 2003 by JSTOR and the President and Fellows of Harvard College - **********************************************************************/ - +/** + * ******************************************************************** Jhove - JSTOR/Harvard Object + * Validation Environment Copyright 2003 by JSTOR and the President and Fellows of Harvard College + * ******************************************************************** + */ package edu.harvard.hul.ois.jhove.module.tiff; import edu.harvard.hul.ois.jhove.*; /** - * Profile checker for TIFF Class IT-FP/P1. + * Profile checker for TIFF Class IT-FP/P1. * - * TIFF/IT-FP/P1 is a simplified form of TIFF/IT-FP. + *

TIFF/IT-FP/P1 is a simplified form of TIFF/IT-FP. * - * @author Gary McGath + * @author Gary McGath */ -public final class TiffProfileClassITFPP1 extends TiffProfileClassIT -{ - public TiffProfileClassITFPP1 () - { - super (); - _profileText = "TIFF/IT-FP/P1 (ISO 12639:1998)"; - } +public final class TiffProfileClassITFPP1 extends TiffProfileClassIT { + public TiffProfileClassITFPP1() { + super(); + _profileText = "TIFF/IT-FP/P1 (ISO 12639:1998)"; + } - /** - * Returns true if the IFD satisfies the requirements - * of the profile. See the documentation for - * details. - */ - @Override - public boolean satisfiesThisProfile (IFD ifd) - { - if (!super.satisfiesThisProfile (ifd)) { - return false; - } + /** + * Returns true if the IFD satisfies the requirements of the profile. See the documentation for + * details. + */ + @Override + public boolean satisfiesThisProfile(IFD ifd) { + if (!super.satisfiesThisProfile(ifd)) { + return false; + } - // We now know this is a TiffIFD - TiffIFD tifd = (TiffIFD) ifd; + // We now know this is a TiffIFD + TiffIFD tifd = (TiffIFD) ifd; - // Tags which must be present - if (tifd.getImageDescription () == null || - tifd.getXPosition () == null || - tifd.getYPosition () == null) { - return false; - } + // Tags which must be present + if (tifd.getImageDescription() == null + || tifd.getXPosition() == null + || tifd.getYPosition() == null) { + return false; + } - // BitsPerSample=4 or 8 or {8,8,8} or {8,8,8,8} or undefined - // (consistent with PhotometricInterpretation) - NisoImageMetadata niso = tifd.getNisoImageMetadata (); - int [] bps = niso.getBitsPerSample (); - if (!(bps == null || - (bps.length == 1 && (bps[0] == 4 || bps[0] == 8)) || - (bps.length == 3 && bps[0] == 8 && bps[1] == 8 && bps[2] == 8) || - (bps.length == 4 && bps[0] == 8 && - bps[1] == 8 && bps[2] == 8 && bps[3] == 8))) { - return false; - } + // BitsPerSample=4 or 8 or {8,8,8} or {8,8,8,8} or undefined + // (consistent with PhotometricInterpretation) + NisoImageMetadata niso = tifd.getNisoImageMetadata(); + int[] bps = niso.getBitsPerSample(); + if (!(bps == null + || (bps.length == 1 && (bps[0] == 4 || bps[0] == 8)) + || (bps.length == 3 && bps[0] == 8 && bps[1] == 8 && bps[2] == 8) + || (bps.length == 4 && bps[0] == 8 && bps[1] == 8 && bps[2] == 8 && bps[3] == 8))) { + return false; + } - // NewSubfileType bit 3=1 - long nsft = tifd.getNewSubfileType (); - if ((nsft & 8) == 0) { - return false; - } + // NewSubfileType bit 3=1 + long nsft = tifd.getNewSubfileType(); + if ((nsft & 8) == 0) { + return false; + } - if (!satisfiesPhotometricInterpretation (tifd, new int [] {0, 1, 2, - 5} )) { - return false; - } + if (!satisfiesPhotometricInterpretation(tifd, new int[] {0, 1, 2, 5})) { + return false; + } - - // SamplesPerPixel=3 or 4 or undefined (consistent with - // PhotometricInterpretation) - if (!satisfiesSamplesPerPixel (tifd, new int [] {3, 4, - NisoImageMetadata.NULL} )) { - return false; - } + // SamplesPerPixel=3 or 4 or undefined (consistent with + // PhotometricInterpretation) + if (!satisfiesSamplesPerPixel(tifd, new int[] {3, 4, NisoImageMetadata.NULL})) { + return false; + } - if (!satisfiesOrientation (tifd, 1)) { - return false; - } + if (!satisfiesOrientation(tifd, 1)) { + return false; + } - if (!satisfiesResolutionUnit (tifd, new int [] {2, 3} )) { - return false; - } + if (!satisfiesResolutionUnit(tifd, new int[] {2, 3})) { + return false; + } - if (!satisfiesPlanarConfiguration (tifd, 1)) { - return false; - } - - if (!satisfiesCompression (tifd, 1)) { - return false; - } + if (!satisfiesPlanarConfiguration(tifd, 1)) { + return false; + } - // NumberOfInks=4, but only if PhotometricInterpretation=5 - // DotRange={0,255}, but only if PhotometricInterpretation=5 - int pint = niso.getColorSpace (); - int nInks = tifd.getNumberOfInks (); - if (pint == 5 && (nInks != 4 || !satisfiesDotRange (tifd, 0, 255))) { - return false; - } - /* Tags which must NOT be defined */ + if (!satisfiesCompression(tifd, 1)) { + return false; + } - return !(tifd.getDocumentName () != null || - niso.getScannerModelName () != null || - tifd.getPageName () != null || - niso.getHostComputer () != null || - tifd.getSite () != null); + // NumberOfInks=4, but only if PhotometricInterpretation=5 + // DotRange={0,255}, but only if PhotometricInterpretation=5 + int pint = niso.getColorSpace(); + int nInks = tifd.getNumberOfInks(); + if (pint == 5 && (nInks != 4 || !satisfiesDotRange(tifd, 0, 255))) { + return false; } + /* Tags which must NOT be defined */ + + return !(tifd.getDocumentName() != null + || niso.getScannerModelName() != null + || tifd.getPageName() != null + || niso.getHostComputer() != null + || tifd.getSite() != null); + } } diff --git a/jhove-modules/tiff-hul/src/main/java/edu/harvard/hul/ois/jhove/module/tiff/TiffProfileClassITFPP2.java b/jhove-modules/tiff-hul/src/main/java/edu/harvard/hul/ois/jhove/module/tiff/TiffProfileClassITFPP2.java index 18acda7dd..b7e5e7776 100644 --- a/jhove-modules/tiff-hul/src/main/java/edu/harvard/hul/ois/jhove/module/tiff/TiffProfileClassITFPP2.java +++ b/jhove-modules/tiff-hul/src/main/java/edu/harvard/hul/ois/jhove/module/tiff/TiffProfileClassITFPP2.java @@ -1,110 +1,101 @@ -/********************************************************************** - * Jhove - JSTOR/Harvard Object Validation Environment - * Copyright 2003 by JSTOR and the President and Fellows of Harvard College - **********************************************************************/ - +/** + * ******************************************************************** Jhove - JSTOR/Harvard Object + * Validation Environment Copyright 2003 by JSTOR and the President and Fellows of Harvard College + * ******************************************************************** + */ package edu.harvard.hul.ois.jhove.module.tiff; import edu.harvard.hul.ois.jhove.*; /** - * Profile checker for TIFF Class IT-FP/P2. + * Profile checker for TIFF Class IT-FP/P2. * - * TIFF/IT-FP/P2 is a simplified form of TIFF/IT-FP. - * The differences between FP/P1 and FP/P2 are very minor. + *

TIFF/IT-FP/P2 is a simplified form of TIFF/IT-FP. The differences between FP/P1 and FP/P2 are + * very minor. * - * @author Gary McGath + * @author Gary McGath */ -public final class TiffProfileClassITFPP2 extends TiffProfileClassIT -{ - public TiffProfileClassITFPP2 () - { - super (); - _profileText = "TIFF/IT-FP/P2 (ISO 12639:2003)"; - } +public final class TiffProfileClassITFPP2 extends TiffProfileClassIT { + public TiffProfileClassITFPP2() { + super(); + _profileText = "TIFF/IT-FP/P2 (ISO 12639:2003)"; + } - /** - * Returns true if the IFD satisfies the requirements - * of the profile. See the documentation for - * details. - * - */ - @Override - public boolean satisfiesThisProfile (IFD ifd) - { - if (!super.satisfiesThisProfile (ifd)) { - return false; - } + /** + * Returns true if the IFD satisfies the requirements of the profile. See the documentation for + * details. + */ + @Override + public boolean satisfiesThisProfile(IFD ifd) { + if (!super.satisfiesThisProfile(ifd)) { + return false; + } - if (!(ifd instanceof TiffIFD)) { - return false; - } - TiffIFD tifd = (TiffIFD) ifd; + if (!(ifd instanceof TiffIFD)) { + return false; + } + TiffIFD tifd = (TiffIFD) ifd; - if (tifd.getImageDescription () == null) { - return false; - } + if (tifd.getImageDescription() == null) { + return false; + } - // BitsPerSample=4 or 8 or {8,8,8} or {8,8,8,8} or undefined - // (consistent with PhotometricInterpretation) - NisoImageMetadata niso = tifd.getNisoImageMetadata (); - int [] bps = niso.getBitsPerSample (); - if (!(bps == null || - (bps.length == 1 && (bps[0] == 4 || bps[0] == 8)) || - (bps.length == 3 && bps[0] == 8 && bps[1] == 8 && bps[2] == 8) || - (bps.length == 4 && bps[0] == 8 && - bps[1] == 8 && bps[2] == 8 && bps[3] == 8))) { - return false; - } + // BitsPerSample=4 or 8 or {8,8,8} or {8,8,8,8} or undefined + // (consistent with PhotometricInterpretation) + NisoImageMetadata niso = tifd.getNisoImageMetadata(); + int[] bps = niso.getBitsPerSample(); + if (!(bps == null + || (bps.length == 1 && (bps[0] == 4 || bps[0] == 8)) + || (bps.length == 3 && bps[0] == 8 && bps[1] == 8 && bps[2] == 8) + || (bps.length == 4 && bps[0] == 8 && bps[1] == 8 && bps[2] == 8 && bps[3] == 8))) { + return false; + } - // NewSubfileType bit 3=1 - long nsft = tifd.getNewSubfileType (); - if ((nsft & 8) == 0) { - return false; - } + // NewSubfileType bit 3=1 + long nsft = tifd.getNewSubfileType(); + if ((nsft & 8) == 0) { + return false; + } - if (!satisfiesPhotometricInterpretation (tifd, new int [] {0, 1, 2, - 5} )) { - return false; - } + if (!satisfiesPhotometricInterpretation(tifd, new int[] {0, 1, 2, 5})) { + return false; + } - - // SamplesPerPixel=3 or 4 or undefined (consistent with - // PhotometricInterpretation) - if (!satisfiesSamplesPerPixel (tifd, new int [] {3, 4, - NisoImageMetadata.NULL} )) { - return false; - } + // SamplesPerPixel=3 or 4 or undefined (consistent with + // PhotometricInterpretation) + if (!satisfiesSamplesPerPixel(tifd, new int[] {3, 4, NisoImageMetadata.NULL})) { + return false; + } - if (!satisfiesOrientation (tifd, 1)) { - return false; - } + if (!satisfiesOrientation(tifd, 1)) { + return false; + } - if (!satisfiesResolutionUnit (tifd, new int [] {2, 3} )) { - return false; - } + if (!satisfiesResolutionUnit(tifd, new int[] {2, 3})) { + return false; + } - if (!satisfiesPlanarConfiguration (tifd, 1)) { - return false; - } - - if (!satisfiesCompression (tifd, 1)) { - return false; - } + if (!satisfiesPlanarConfiguration(tifd, 1)) { + return false; + } - // NumberOfInks=4, but only if PhotometricInterpretation=5 - // DotRange={0,255}, but only if PhotometricInterpretation=5 - int pint = niso.getColorSpace (); - int nInks = tifd.getNumberOfInks (); - if (pint == 5 && (nInks != 4 || !satisfiesDotRange (tifd, 0, 255))) { - return false; - } - /* Tags which must NOT be defined */ + if (!satisfiesCompression(tifd, 1)) { + return false; + } - return !(tifd.getDocumentName () != null || - niso.getScannerModelName () != null || - tifd.getPageName () != null || - niso.getHostComputer () != null || - tifd.getSite () != null); + // NumberOfInks=4, but only if PhotometricInterpretation=5 + // DotRange={0,255}, but only if PhotometricInterpretation=5 + int pint = niso.getColorSpace(); + int nInks = tifd.getNumberOfInks(); + if (pint == 5 && (nInks != 4 || !satisfiesDotRange(tifd, 0, 255))) { + return false; } + /* Tags which must NOT be defined */ + + return !(tifd.getDocumentName() != null + || niso.getScannerModelName() != null + || tifd.getPageName() != null + || niso.getHostComputer() != null + || tifd.getSite() != null); + } } diff --git a/jhove-modules/tiff-hul/src/main/java/edu/harvard/hul/ois/jhove/module/tiff/TiffProfileClassITHC.java b/jhove-modules/tiff-hul/src/main/java/edu/harvard/hul/ois/jhove/module/tiff/TiffProfileClassITHC.java index cdeac9392..9c5c7c3fb 100644 --- a/jhove-modules/tiff-hul/src/main/java/edu/harvard/hul/ois/jhove/module/tiff/TiffProfileClassITHC.java +++ b/jhove-modules/tiff-hul/src/main/java/edu/harvard/hul/ois/jhove/module/tiff/TiffProfileClassITHC.java @@ -1,85 +1,78 @@ -/********************************************************************** - * Jhove - JSTOR/Harvard Object Validation Environment - * Copyright 2003 by JSTOR and the President and Fellows of Harvard College - **********************************************************************/ - +/** + * ******************************************************************** Jhove - JSTOR/Harvard Object + * Validation Environment Copyright 2003 by JSTOR and the President and Fellows of Harvard College + * ******************************************************************** + */ package edu.harvard.hul.ois.jhove.module.tiff; import edu.harvard.hul.ois.jhove.*; /** - * Profile checker for TIFF Class IT-HC. + * Profile checker for TIFF Class IT-HC. * - * The TIFF/IT spec states that "TIFF/IT-HC makes use of all - * the features and functionality supported by the TIFF and - * TIFF/IT fields appropriate to high resolution continuous - * tone images." + *

The TIFF/IT spec states that "TIFF/IT-HC makes use of all the features and functionality + * supported by the TIFF and TIFF/IT fields appropriate to high resolution continuous tone images." * - * @author Gary McGath + * @author Gary McGath */ -public final class TiffProfileClassITHC extends TiffProfileClassIT -{ - public TiffProfileClassITHC () - { - super (); - _profileText = "TIFF/IT-HC (ISO 12639:1998)"; +public final class TiffProfileClassITHC extends TiffProfileClassIT { + public TiffProfileClassITHC() { + super(); + _profileText = "TIFF/IT-HC (ISO 12639:1998)"; + } + + /** + * Returns true if the IFD satisfies the requirements of the profile. See the documentation for + * details. + */ + @Override + public boolean satisfiesThisProfile(IFD ifd) { + if (!super.satisfiesThisProfile(ifd)) { + return false; } - /** - * Returns true if the IFD satisfies the requirements - * of the profile. See the documentation for - * details. - */ - @Override - public boolean satisfiesThisProfile (IFD ifd) - { - if (!super.satisfiesThisProfile (ifd)) { - return false; - } + if (!(ifd instanceof TiffIFD)) { + return false; + } + TiffIFD tifd = (TiffIFD) ifd; - if (!(ifd instanceof TiffIFD)) { - return false; - } - TiffIFD tifd = (TiffIFD) ifd; + /* Check required tags. */ + NisoImageMetadata niso = tifd.getNisoImageMetadata(); + if (niso.getBitsPerSample() == null || niso.getSamplesPerPixel() == NisoImageMetadata.NULL) { + return false; + } - /* Check required tags. */ - NisoImageMetadata niso = tifd.getNisoImageMetadata (); - if (niso.getBitsPerSample () == null || - niso.getSamplesPerPixel () == NisoImageMetadata.NULL) { - return false; - } - - if (!satisfiesPhotometricInterpretation (tifd, 5)) { - return false; - } + if (!satisfiesPhotometricInterpretation(tifd, 5)) { + return false; + } - if (!satisfiesCompression (tifd, 32895)) { - return false; - } + if (!satisfiesCompression(tifd, 32895)) { + return false; + } - if (!satisfiesPlanarConfiguration (tifd, 1)) { - return false; - } - - int inkSet = tifd.getInkSet (); - if (inkSet != 1 && inkSet != 2) { - return false; - } - String seq = tifd.getColorSequence (); - if ((seq == null || "CMYK".equals (seq)) && inkSet != 1) { - return false; - } + if (!satisfiesPlanarConfiguration(tifd, 1)) { + return false; + } - // Per footnote: If NumberOfInks tag is used, it must have the - // same value as SamplesPerPixel. This requirement doesn't - // apply to P1 or P2. - int spp = niso.getSamplesPerPixel (); - int numInks = tifd.getNumberOfInks (); - if (numInks != IFD.NULL && numInks != spp) { - return false; - } + int inkSet = tifd.getInkSet(); + if (inkSet != 1 && inkSet != 2) { + return false; + } + String seq = tifd.getColorSequence(); + if ((seq == null || "CMYK".equals(seq)) && inkSet != 1) { + return false; + } - int trans = tifd.getTransparencyIndicator (); - return !(trans != 0 && trans != 1); + // Per footnote: If NumberOfInks tag is used, it must have the + // same value as SamplesPerPixel. This requirement doesn't + // apply to P1 or P2. + int spp = niso.getSamplesPerPixel(); + int numInks = tifd.getNumberOfInks(); + if (numInks != IFD.NULL && numInks != spp) { + return false; } + + int trans = tifd.getTransparencyIndicator(); + return !(trans != 0 && trans != 1); + } } diff --git a/jhove-modules/tiff-hul/src/main/java/edu/harvard/hul/ois/jhove/module/tiff/TiffProfileClassITHCP1.java b/jhove-modules/tiff-hul/src/main/java/edu/harvard/hul/ois/jhove/module/tiff/TiffProfileClassITHCP1.java index ac384ca61..dedc40215 100644 --- a/jhove-modules/tiff-hul/src/main/java/edu/harvard/hul/ois/jhove/module/tiff/TiffProfileClassITHCP1.java +++ b/jhove-modules/tiff-hul/src/main/java/edu/harvard/hul/ois/jhove/module/tiff/TiffProfileClassITHCP1.java @@ -1,106 +1,101 @@ -/********************************************************************** - * Jhove - JSTOR/Harvard Object Validation Environment - * Copyright 2003 by JSTOR and the President and Fellows of Harvard College - **********************************************************************/ - +/** + * ******************************************************************** Jhove - JSTOR/Harvard Object + * Validation Environment Copyright 2003 by JSTOR and the President and Fellows of Harvard College + * ******************************************************************** + */ package edu.harvard.hul.ois.jhove.module.tiff; import edu.harvard.hul.ois.jhove.*; /** - * Profile checker for TIFF Class IT-HC/P1. + * Profile checker for TIFF Class IT-HC/P1. * - * The TIFF/IT spec states that "TIFF/IT-HC/P1 is a simplified - * image file format profile for high resolution continuous tone - * (HC) image data and can be considered a constrained subset - * of TIFF/IT-HC specifically intended for simpler implementation." + *

The TIFF/IT spec states that "TIFF/IT-HC/P1 is a simplified image file format profile for high + * resolution continuous tone (HC) image data and can be considered a constrained subset of + * TIFF/IT-HC specifically intended for simpler implementation." * - * @author Gary McGath + * @author Gary McGath */ -public final class TiffProfileClassITHCP1 extends TiffProfileClassIT -{ - public TiffProfileClassITHCP1 () - { - super (); - _profileText = "TIFF/IT-HC/P1 (ISO 12639:1998)"; +public final class TiffProfileClassITHCP1 extends TiffProfileClassIT { + public TiffProfileClassITHCP1() { + super(); + _profileText = "TIFF/IT-HC/P1 (ISO 12639:1998)"; + } + + /** + * Returns true if the IFD satisfies the requirements of the profile. See the documentation for + * details. + */ + @Override + public boolean satisfiesThisProfile(IFD ifd) { + if (!super.satisfiesThisProfile(ifd)) { + return false; } - /** - * Returns true if the IFD satisfies the requirements - * of the profile. See the documentation for - * details. - */ - @Override - public boolean satisfiesThisProfile (IFD ifd) - { - if (!super.satisfiesThisProfile (ifd)) { - return false; - } + if (!(ifd instanceof TiffIFD)) { + return false; + } + TiffIFD tifd = (TiffIFD) ifd; - if (!(ifd instanceof TiffIFD)) { - return false; - } - TiffIFD tifd = (TiffIFD) ifd; + /* Check required tags. */ + if (!satisfiesSamplesPerPixel(tifd, 4)) { + return false; + } - /* Check required tags. */ - if (!satisfiesSamplesPerPixel (tifd, 4)) { - return false; - } - - // BitsPerSample must be {8, 8, 8, 8} - NisoImageMetadata niso = tifd.getNisoImageMetadata (); - int[] bps = niso.getBitsPerSample (); - if (bps == null || bps.length < 4) { - return false; - } - for (int i=0; i<4; i++) { - if (bps[i] != 8) { - return false; - } - } + // BitsPerSample must be {8, 8, 8, 8} + NisoImageMetadata niso = tifd.getNisoImageMetadata(); + int[] bps = niso.getBitsPerSample(); + if (bps == null || bps.length < 4) { + return false; + } + for (int i = 0; i < 4; i++) { + if (bps[i] != 8) { + return false; + } + } - if (!satisfiesPhotometricInterpretation (tifd, 5)) { - return false; - } + if (!satisfiesPhotometricInterpretation(tifd, 5)) { + return false; + } - if (!satisfiesCompression (tifd, 32895)) { - return false; - } + if (!satisfiesCompression(tifd, 32895)) { + return false; + } + + if (!satisfiesPlanarConfiguration(tifd, 1)) { + return false; + } - if (!satisfiesPlanarConfiguration (tifd, 1)) { - return false; - } - - if (!satisfiesResolutionUnit (tifd, new int [] {2, 3} )) { - return false; - } + if (!satisfiesResolutionUnit(tifd, new int[] {2, 3})) { + return false; + } - int inkSet = tifd.getInkSet (); - if (inkSet != 1) { - return false; - } + int inkSet = tifd.getInkSet(); + if (inkSet != 1) { + return false; + } - if (tifd.getNumberOfInks () != 4) { - return false; - } - - // DotRange={0,255} - if (!satisfiesDotRange (tifd, 0, 255)) { - return false; - } + if (tifd.getNumberOfInks() != 4) { + return false; + } - int trans = tifd.getTransparencyIndicator (); - if (trans != 0 && trans != 1) { - return false; - } - // The tags DocumentName, Model, PageName, HostComputer, - // Site, and ColorSequence must NOT be defined + // DotRange={0,255} + if (!satisfiesDotRange(tifd, 0, 255)) { + return false; + } - return !(tifd.getDocumentName () != null || - niso.getScannerModelName () != null || - tifd.getPageName () != null || - niso.getHostComputer () != null || - tifd.getSite () != null || - tifd.getColorSequence () != null); + int trans = tifd.getTransparencyIndicator(); + if (trans != 0 && trans != 1) { + return false; } + // The tags DocumentName, Model, PageName, HostComputer, + // Site, and ColorSequence must NOT be defined + + return !(tifd.getDocumentName() != null + || niso.getScannerModelName() != null + || tifd.getPageName() != null + || niso.getHostComputer() != null + || tifd.getSite() != null + || tifd.getColorSequence() != null); + } } diff --git a/jhove-modules/tiff-hul/src/main/java/edu/harvard/hul/ois/jhove/module/tiff/TiffProfileClassITHCP2.java b/jhove-modules/tiff-hul/src/main/java/edu/harvard/hul/ois/jhove/module/tiff/TiffProfileClassITHCP2.java index d5dfc4c54..9cd55e960 100644 --- a/jhove-modules/tiff-hul/src/main/java/edu/harvard/hul/ois/jhove/module/tiff/TiffProfileClassITHCP2.java +++ b/jhove-modules/tiff-hul/src/main/java/edu/harvard/hul/ois/jhove/module/tiff/TiffProfileClassITHCP2.java @@ -1,95 +1,90 @@ -/********************************************************************** - * Jhove - JSTOR/Harvard Object Validation Environment - * Copyright 2003 by JSTOR and the President and Fellows of Harvard College - **********************************************************************/ - +/** + * ******************************************************************** Jhove - JSTOR/Harvard Object + * Validation Environment Copyright 2003 by JSTOR and the President and Fellows of Harvard College + * ******************************************************************** + */ package edu.harvard.hul.ois.jhove.module.tiff; import edu.harvard.hul.ois.jhove.*; /** - * Profile checker for TIFF Class IT-HC/P2. + * Profile checker for TIFF Class IT-HC/P2. * - * The TIFF/IT spec states that "TIFF/IT-HC/P2 is an extension of - * TIFF/IT-HC/P1." + *

The TIFF/IT spec states that "TIFF/IT-HC/P2 is an extension of TIFF/IT-HC/P1." * - * @author Gary McGath + * @author Gary McGath */ -public final class TiffProfileClassITHCP2 extends TiffProfileClassIT -{ - public TiffProfileClassITHCP2 () - { - super (); - _profileText = "TIFF/IT-HC/P2 (ISO 12639:2003)"; +public final class TiffProfileClassITHCP2 extends TiffProfileClassIT { + public TiffProfileClassITHCP2() { + super(); + _profileText = "TIFF/IT-HC/P2 (ISO 12639:2003)"; + } + + /** + * Returns true if the IFD satisfies the requirements of the profile. See the documentation for + * details. + */ + @Override + public boolean satisfiesThisProfile(IFD ifd) { + if (!super.satisfiesThisProfile(ifd)) { + return false; } - /** - * Returns true if the IFD satisfies the requirements - * of the profile. See the documentation for - * details. - */ - @Override - public boolean satisfiesThisProfile (IFD ifd) - { - if (!super.satisfiesThisProfile (ifd)) { - return false; - } + if (!(ifd instanceof TiffIFD)) { + return false; + } + TiffIFD tifd = (TiffIFD) ifd; - if (!(ifd instanceof TiffIFD)) { - return false; - } - TiffIFD tifd = (TiffIFD) ifd; + /* Check required tags. */ - /* Check required tags. */ + // BitsPerSample must be {8, ...} + NisoImageMetadata niso = tifd.getNisoImageMetadata(); + int[] bps = niso.getBitsPerSample(); + if (bps == null || bps[0] != 8) { + return false; + } - // BitsPerSample must be {8, ...} - NisoImageMetadata niso = tifd.getNisoImageMetadata (); - int[] bps = niso.getBitsPerSample (); - if (bps == null || bps[0] != 8) { - return false; - } + if (!satisfiesPhotometricInterpretation(tifd, 5)) { + return false; + } - if (!satisfiesPhotometricInterpretation (tifd, 5)) { - return false; - } + if (!satisfiesCompression(tifd, 32895)) { + return false; + } - if (!satisfiesCompression (tifd, 32895)) { - return false; - } + if (!satisfiesPlanarConfiguration(tifd, 1)) { + return false; + } - if (!satisfiesPlanarConfiguration (tifd, 1)) { - return false; - } - - if (!satisfiesResolutionUnit (tifd, new int [] {2, 3} )) { - return false; - } + if (!satisfiesResolutionUnit(tifd, new int[] {2, 3})) { + return false; + } - int inkSet = tifd.getInkSet (); - if (inkSet != 1) { - return false; - } + int inkSet = tifd.getInkSet(); + if (inkSet != 1) { + return false; + } - if (tifd.getNumberOfInks () != 4) { - return false; - } - - // DotRange={0,255} - if (!satisfiesDotRange (tifd, 0, 255)) { - return false; - } + if (tifd.getNumberOfInks() != 4) { + return false; + } - int trans = tifd.getTransparencyIndicator (); - if (trans != 0 && trans != 1) { - return false; - } - // The tags DocumentName, Model, PageName, HostComputer, - // Site, and ColorSequence must NOT be defined + // DotRange={0,255} + if (!satisfiesDotRange(tifd, 0, 255)) { + return false; + } - return !(tifd.getDocumentName () != null || - niso.getScannerModelName () != null || - tifd.getPageName () != null || - niso.getHostComputer () != null || - tifd.getSite () != null); + int trans = tifd.getTransparencyIndicator(); + if (trans != 0 && trans != 1) { + return false; } + // The tags DocumentName, Model, PageName, HostComputer, + // Site, and ColorSequence must NOT be defined + + return !(tifd.getDocumentName() != null + || niso.getScannerModelName() != null + || tifd.getPageName() != null + || niso.getHostComputer() != null + || tifd.getSite() != null); + } } diff --git a/jhove-modules/tiff-hul/src/main/java/edu/harvard/hul/ois/jhove/module/tiff/TiffProfileClassITLW.java b/jhove-modules/tiff-hul/src/main/java/edu/harvard/hul/ois/jhove/module/tiff/TiffProfileClassITLW.java index 99d5f7a70..8c62b073a 100644 --- a/jhove-modules/tiff-hul/src/main/java/edu/harvard/hul/ois/jhove/module/tiff/TiffProfileClassITLW.java +++ b/jhove-modules/tiff-hul/src/main/java/edu/harvard/hul/ois/jhove/module/tiff/TiffProfileClassITLW.java @@ -1,83 +1,78 @@ -/********************************************************************** - * Jhove - JSTOR/Harvard Object Validation Environment - * Copyright 2003 by JSTOR and the President and Fellows of Harvard College - **********************************************************************/ - +/** + * ******************************************************************** Jhove - JSTOR/Harvard Object + * Validation Environment Copyright 2003 by JSTOR and the President and Fellows of Harvard College + * ******************************************************************** + */ package edu.harvard.hul.ois.jhove.module.tiff; import edu.harvard.hul.ois.jhove.*; /** - * Profile checker for TIFF Class IT-LW. + * Profile checker for TIFF Class IT-LW. * - * The TIFF/IT spec states that "TIFF/IT-LW makes use of all - * the features and functionality supported by the TIFF and - * TIFF/IT fields appropriate to line art images." + *

The TIFF/IT spec states that "TIFF/IT-LW makes use of all the features and functionality + * supported by the TIFF and TIFF/IT fields appropriate to line art images." * - * @author Gary McGath + * @author Gary McGath */ -public final class TiffProfileClassITLW extends TiffProfileClassIT -{ - public TiffProfileClassITLW () - { - super (); - _profileText = "TIFF/IT-LW (ISO 12639:1998)"; - } +public final class TiffProfileClassITLW extends TiffProfileClassIT { + public TiffProfileClassITLW() { + super(); + _profileText = "TIFF/IT-LW (ISO 12639:1998)"; + } - /** - * Returns true if the IFD satisfies the requirements - * of the profile. See the documentation for - * details. - */ - @Override - public boolean satisfiesThisProfile (IFD ifd) - { - if (!super.satisfiesThisProfile (ifd)) { - return false; - } - - if (!(ifd instanceof TiffIFD)) { - return false; - } - TiffIFD tifd = (TiffIFD) ifd; + /** + * Returns true if the IFD satisfies the requirements of the profile. See the documentation for + * details. + */ + @Override + public boolean satisfiesThisProfile(IFD ifd) { + if (!super.satisfiesThisProfile(ifd)) { + return false; + } - /* Check required tags. */ - if (tifd.getColorTable () == null) { - return false; - } + if (!(ifd instanceof TiffIFD)) { + return false; + } + TiffIFD tifd = (TiffIFD) ifd; - NisoImageMetadata niso = tifd.getNisoImageMetadata (); - int[] bps = niso.getBitsPerSample (); - if (bps[0] != 8) { - return false; - } + /* Check required tags. */ + if (tifd.getColorTable() == null) { + return false; + } - /* Check required values. */ - if (!satisfiesSamplesPerPixel (tifd, 1)) { - return false; - } - if (!satisfiesPhotometricInterpretation (tifd, 5)) { - return false; - } - /* NOTE: If compression is 32895, RasterPadding must be - * 0, 1, 2, 9, or 10. Fix this when RasterPadding support is - * implemented. */ - if (!satisfiesCompression (tifd, 32896)) { - return false; - } + NisoImageMetadata niso = tifd.getNisoImageMetadata(); + int[] bps = niso.getBitsPerSample(); + if (bps[0] != 8) { + return false; + } - int inkSet = tifd.getInkSet (); - if (inkSet != 1 && inkSet != 2) { - return false; - } - String seq = tifd.getColorSequence (); - if ((seq == null || "CMYK".equals (seq)) && inkSet != 1) { - return false; - } + /* Check required values. */ + if (!satisfiesSamplesPerPixel(tifd, 1)) { + return false; + } + if (!satisfiesPhotometricInterpretation(tifd, 5)) { + return false; + } + /* NOTE: If compression is 32895, RasterPadding must be + * 0, 1, 2, 9, or 10. Fix this when RasterPadding support is + * implemented. */ + if (!satisfiesCompression(tifd, 32896)) { + return false; + } - // Per footnote h, this applies to LW, LW/P1 and LW/P2 - int spp = niso.getSamplesPerPixel (); - int numInks = tifd.getNumberOfInks (); - return !(numInks != IFD.NULL && numInks != spp); + int inkSet = tifd.getInkSet(); + if (inkSet != 1 && inkSet != 2) { + return false; + } + String seq = tifd.getColorSequence(); + if ((seq == null || "CMYK".equals(seq)) && inkSet != 1) { + return false; } + + // Per footnote h, this applies to LW, LW/P1 and LW/P2 + int spp = niso.getSamplesPerPixel(); + int numInks = tifd.getNumberOfInks(); + return !(numInks != IFD.NULL && numInks != spp); + } } diff --git a/jhove-modules/tiff-hul/src/main/java/edu/harvard/hul/ois/jhove/module/tiff/TiffProfileClassITLWP1.java b/jhove-modules/tiff-hul/src/main/java/edu/harvard/hul/ois/jhove/module/tiff/TiffProfileClassITLWP1.java index 2d052f283..969c307df 100644 --- a/jhove-modules/tiff-hul/src/main/java/edu/harvard/hul/ois/jhove/module/tiff/TiffProfileClassITLWP1.java +++ b/jhove-modules/tiff-hul/src/main/java/edu/harvard/hul/ois/jhove/module/tiff/TiffProfileClassITLWP1.java @@ -1,95 +1,90 @@ -/********************************************************************** - * Jhove - JSTOR/Harvard Object Validation Environment - * Copyright 2003 by JSTOR and the President and Fellows of Harvard College - **********************************************************************/ - +/** + * ******************************************************************** Jhove - JSTOR/Harvard Object + * Validation Environment Copyright 2003 by JSTOR and the President and Fellows of Harvard College + * ******************************************************************** + */ package edu.harvard.hul.ois.jhove.module.tiff; import edu.harvard.hul.ois.jhove.*; /** - * Profile checker for TIFF Class IT-LW/P1. + * Profile checker for TIFF Class IT-LW/P1. * - * The TIFF/IT spec states that "TIFF/IT-LW/P1 is a simplified - * image file format profile for line art (LW) image data and - * can be considered as a constrained subset of TIFF/IT-LW specified - * for simpler implementation." + *

The TIFF/IT spec states that "TIFF/IT-LW/P1 is a simplified image file format profile for line + * art (LW) image data and can be considered as a constrained subset of TIFF/IT-LW specified for + * simpler implementation." * - * @author Gary McGath + * @author Gary McGath */ -public final class TiffProfileClassITLWP1 extends TiffProfileClassIT -{ - public TiffProfileClassITLWP1 () - { - super (); - _profileText = "TIFF/IT-LW/P1 (ISO 12639:1998)"; +public final class TiffProfileClassITLWP1 extends TiffProfileClassIT { + public TiffProfileClassITLWP1() { + super(); + _profileText = "TIFF/IT-LW/P1 (ISO 12639:1998)"; + } + + /** + * Returns true if the IFD satisfies the requirements of the profile. See the documentation for + * details. + */ + @Override + public boolean satisfiesThisProfile(IFD ifd) { + if (!super.satisfiesThisProfile(ifd)) { + return false; } - /** - * Returns true if the IFD satisfies the requirements - * of the profile. See the documentation for - * details. - */ - @Override - public boolean satisfiesThisProfile (IFD ifd) - { - if (!super.satisfiesThisProfile (ifd)) { - return false; - } - - if (!(ifd instanceof TiffIFD)) { - return false; - } - TiffIFD tifd = (TiffIFD) ifd; - - /* Check required tags. */ - if (tifd.getColorTable () == null) { - return false; - } + if (!(ifd instanceof TiffIFD)) { + return false; + } + TiffIFD tifd = (TiffIFD) ifd; - NisoImageMetadata niso = tifd.getNisoImageMetadata (); - int [] bps = niso.getBitsPerSample (); - if (bps[0] != 8) { - return false; - } + /* Check required tags. */ + if (tifd.getColorTable() == null) { + return false; + } - /* Check required values. */ - if (!satisfiesSamplesPerPixel (tifd, 1)) { - return false; - } + NisoImageMetadata niso = tifd.getNisoImageMetadata(); + int[] bps = niso.getBitsPerSample(); + if (bps[0] != 8) { + return false; + } - if (!satisfiesResolutionUnit (tifd, new int [] {2, 3} )) { - return false; - } + /* Check required values. */ + if (!satisfiesSamplesPerPixel(tifd, 1)) { + return false; + } - if (!satisfiesNewSubfileType (tifd, 0)) { - return false; - } + if (!satisfiesResolutionUnit(tifd, new int[] {2, 3})) { + return false; + } - if (!satisfiesPhotometricInterpretation (tifd, 5)) { - return false; - } + if (!satisfiesNewSubfileType(tifd, 0)) { + return false; + } - if (!satisfiesCompression (tifd, 32896)) { - return false; - } + if (!satisfiesPhotometricInterpretation(tifd, 5)) { + return false; + } - int inkSet = tifd.getInkSet (); - if (inkSet != 1) { - return false; - } + if (!satisfiesCompression(tifd, 32896)) { + return false; + } - int bprl = tifd.getBitsPerRunLength (); - if (bprl != 8) { - return false; - } + int inkSet = tifd.getInkSet(); + if (inkSet != 1) { + return false; + } - int bperl = tifd.getBitsPerExtendedRunLength (); - if (bperl != 16) { - return false; - } + int bprl = tifd.getBitsPerRunLength(); + if (bprl != 8) { + return false; + } - int numInks = tifd.getNumberOfInks (); - return !(numInks != 4 || numInks != niso.getSamplesPerPixel ()); + int bperl = tifd.getBitsPerExtendedRunLength(); + if (bperl != 16) { + return false; } + + int numInks = tifd.getNumberOfInks(); + return !(numInks != 4 || numInks != niso.getSamplesPerPixel()); + } } diff --git a/jhove-modules/tiff-hul/src/main/java/edu/harvard/hul/ois/jhove/module/tiff/TiffProfileClassITLWP2.java b/jhove-modules/tiff-hul/src/main/java/edu/harvard/hul/ois/jhove/module/tiff/TiffProfileClassITLWP2.java index e1f633470..4f84d0be8 100644 --- a/jhove-modules/tiff-hul/src/main/java/edu/harvard/hul/ois/jhove/module/tiff/TiffProfileClassITLWP2.java +++ b/jhove-modules/tiff-hul/src/main/java/edu/harvard/hul/ois/jhove/module/tiff/TiffProfileClassITLWP2.java @@ -1,86 +1,81 @@ -/********************************************************************** - * Jhove - JSTOR/Harvard Object Validation Environment - * Copyright 2003 by JSTOR and the President and Fellows of Harvard College - **********************************************************************/ - +/** + * ******************************************************************** Jhove - JSTOR/Harvard Object + * Validation Environment Copyright 2003 by JSTOR and the President and Fellows of Harvard College + * ******************************************************************** + */ package edu.harvard.hul.ois.jhove.module.tiff; import edu.harvard.hul.ois.jhove.*; /** - * Profile checker for TIFF Class IT-LW/P2. + * Profile checker for TIFF Class IT-LW/P2. + * + *

The TIFF/IT spec states that "TIFF/IT-LW/P1 is a simplified image file format profile for line + * art (LW) image data and can be considered as a constrained subset of TIFF/IT-LW specified for + * simpler implementation. TIFF/IT-LW/P2 can be considered as an extension of TIFF/IT-LW/P1." * - * The TIFF/IT spec states that "TIFF/IT-LW/P1 is a simplified - * image file format profile for line art (LW) image data and - * can be considered as a constrained subset of TIFF/IT-LW specified - * for simpler implementation. TIFF/IT-LW/P2 can be considered - * as an extension of TIFF/IT-LW/P1." - * @author Gary McGath + * @author Gary McGath */ -public final class TiffProfileClassITLWP2 extends TiffProfileClassIT -{ - public TiffProfileClassITLWP2 () - { - super (); - _profileText = "TIFF/IT-LW/P2 (ISO 12639:2003)"; - } - - /** - * Returns true if the IFD satisfies the requirements - * of the profile. See the documentation for - * details. - */ - @Override - public boolean satisfiesThisProfile (IFD ifd) - { - if (!super.satisfiesThisProfile (ifd)) { - return false; - } +public final class TiffProfileClassITLWP2 extends TiffProfileClassIT { + public TiffProfileClassITLWP2() { + super(); + _profileText = "TIFF/IT-LW/P2 (ISO 12639:2003)"; + } - if (!(ifd instanceof TiffIFD)) { - return false; - } - TiffIFD tifd = (TiffIFD) ifd; + /** + * Returns true if the IFD satisfies the requirements of the profile. See the documentation for + * details. + */ + @Override + public boolean satisfiesThisProfile(IFD ifd) { + if (!super.satisfiesThisProfile(ifd)) { + return false; + } - /* Check required tags. */ - if (tifd.getColorTable () == null) { - return false; - } + if (!(ifd instanceof TiffIFD)) { + return false; + } + TiffIFD tifd = (TiffIFD) ifd; - NisoImageMetadata niso = tifd.getNisoImageMetadata (); - int [] bps = niso.getBitsPerSample (); - if (bps[0] != 8) { - return false; - } + /* Check required tags. */ + if (tifd.getColorTable() == null) { + return false; + } - /* Check required values. */ - if (!satisfiesSamplesPerPixel (tifd, 1)) { - return false; - } + NisoImageMetadata niso = tifd.getNisoImageMetadata(); + int[] bps = niso.getBitsPerSample(); + if (bps[0] != 8) { + return false; + } - if (!satisfiesResolutionUnit (tifd, new int [] {2, 3} )) { - return false; - } + /* Check required values. */ + if (!satisfiesSamplesPerPixel(tifd, 1)) { + return false; + } - if (!satisfiesPhotometricInterpretation (tifd, 5)) { - return false; - } + if (!satisfiesResolutionUnit(tifd, new int[] {2, 3})) { + return false; + } - if (!satisfiesCompression (tifd, 32896)) { - return false; - } + if (!satisfiesPhotometricInterpretation(tifd, 5)) { + return false; + } - int inkSet = tifd.getInkSet (); - if (inkSet != 1) { - return false; - } + if (!satisfiesCompression(tifd, 32896)) { + return false; + } - int bperl = tifd.getBitsPerExtendedRunLength (); - if (bperl != 16) { - return false; - } + int inkSet = tifd.getInkSet(); + if (inkSet != 1) { + return false; + } - int numInks = tifd.getNumberOfInks (); - return !(numInks != 4 || numInks != niso.getSamplesPerPixel ()); + int bperl = tifd.getBitsPerExtendedRunLength(); + if (bperl != 16) { + return false; } + + int numInks = tifd.getNumberOfInks(); + return !(numInks != 4 || numInks != niso.getSamplesPerPixel()); + } } diff --git a/jhove-modules/tiff-hul/src/main/java/edu/harvard/hul/ois/jhove/module/tiff/TiffProfileClassITMP.java b/jhove-modules/tiff-hul/src/main/java/edu/harvard/hul/ois/jhove/module/tiff/TiffProfileClassITMP.java index 2fa38aae6..92ee5e88e 100644 --- a/jhove-modules/tiff-hul/src/main/java/edu/harvard/hul/ois/jhove/module/tiff/TiffProfileClassITMP.java +++ b/jhove-modules/tiff-hul/src/main/java/edu/harvard/hul/ois/jhove/module/tiff/TiffProfileClassITMP.java @@ -1,78 +1,73 @@ -/********************************************************************** - * Jhove - JSTOR/Harvard Object Validation Environment - * Copyright 2003 by JSTOR and the President and Fellows of Harvard College - **********************************************************************/ - +/** + * ******************************************************************** Jhove - JSTOR/Harvard Object + * Validation Environment Copyright 2003 by JSTOR and the President and Fellows of Harvard College + * ******************************************************************** + */ package edu.harvard.hul.ois.jhove.module.tiff; import edu.harvard.hul.ois.jhove.*; /** - * Profile checker for TIFF Class IT-MP. + * Profile checker for TIFF Class IT-MP. * - * The TIFF/IT spec states that "TIFF/IT-MP makes use of all - * the features and functionality supported by the TIFF and - * TIFF/IT fields appropriate to monochrome continuous - * tone picture images." + *

The TIFF/IT spec states that "TIFF/IT-MP makes use of all the features and functionality + * supported by the TIFF and TIFF/IT fields appropriate to monochrome continuous tone picture + * images." * - * @author Gary McGath + * @author Gary McGath */ -public final class TiffProfileClassITMP extends TiffProfileClassIT -{ - public TiffProfileClassITMP () - { - super (); - _profileText = "TIFF/IT-MP (ISO 12639:1998)"; +public final class TiffProfileClassITMP extends TiffProfileClassIT { + public TiffProfileClassITMP() { + super(); + _profileText = "TIFF/IT-MP (ISO 12639:1998)"; + } + + /** + * Returns true if the IFD satisfies the requirements of the profile. See the documentation for + * details. + */ + @Override + public boolean satisfiesThisProfile(IFD ifd) { + if (!super.satisfiesThisProfile(ifd)) { + return false; + } + + if (!(ifd instanceof TiffIFD)) { + return false; + } + TiffIFD tifd = (TiffIFD) ifd; + + /* Check required tags. */ + NisoImageMetadata niso = tifd.getNisoImageMetadata(); + if (niso.getBitsPerSample() == null) { + return false; + } + + if (!satisfiesCompression(tifd, new int[] {1, 32895})) { + return false; } - /** - * Returns true if the IFD satisfies the requirements - * of the profile. See the documentation for - * details. - */ - @Override - public boolean satisfiesThisProfile (IFD ifd) - { - if (!super.satisfiesThisProfile (ifd)) { - return false; - } + if (!satisfiesPhotometricInterpretation(tifd, new int[] {0, 1})) { + return false; + } - if (!(ifd instanceof TiffIFD)) { - return false; - } - TiffIFD tifd = (TiffIFD) ifd; + // RasterPadding=0,1,2,9, or 10, but only if Compression=32895 + if (niso.getCompressionScheme() == 32895) { + int pad = tifd.getRasterPadding(); + if (pad != 0 && pad != 1 && pad != 2 && pad != 9 && pad != 10) { + return false; + } + } + + if (!satisfiesImageColorIndicator(tifd, new int[] {0, 1})) { + return false; + } - /* Check required tags. */ - NisoImageMetadata niso = tifd.getNisoImageMetadata (); - if (niso.getBitsPerSample () == null) { - return false; - } - - if (!satisfiesCompression (tifd, new int [] {1, 32895} )) { - return false; - } - - if (!satisfiesPhotometricInterpretation (tifd, new int [] {0, 1} )) { - return false; - } - - // RasterPadding=0,1,2,9, or 10, but only if Compression=32895 - if (niso.getCompressionScheme () == 32895) { - int pad = tifd.getRasterPadding (); - if (pad != 0 && pad != 1 && pad != 2 && pad != 9 && pad != 10) { - return false; - } - } - - if (!satisfiesImageColorIndicator (tifd, new int [] {0, 1} )) { - return false; - } - - // ImageColorValue is defined if ImageColorIndicator=1 - int ind = tifd.getImageColorIndicator (); - if (ind == 1 && tifd.getImageColorValue () == IFD.NULL) { - return false; - } - return true; + // ImageColorValue is defined if ImageColorIndicator=1 + int ind = tifd.getImageColorIndicator(); + if (ind == 1 && tifd.getImageColorValue() == IFD.NULL) { + return false; } + return true; + } } diff --git a/jhove-modules/tiff-hul/src/main/java/edu/harvard/hul/ois/jhove/module/tiff/TiffProfileClassITMPP1.java b/jhove-modules/tiff-hul/src/main/java/edu/harvard/hul/ois/jhove/module/tiff/TiffProfileClassITMPP1.java index d51856408..33473644b 100644 --- a/jhove-modules/tiff-hul/src/main/java/edu/harvard/hul/ois/jhove/module/tiff/TiffProfileClassITMPP1.java +++ b/jhove-modules/tiff-hul/src/main/java/edu/harvard/hul/ois/jhove/module/tiff/TiffProfileClassITMPP1.java @@ -1,108 +1,102 @@ -/********************************************************************** - * Jhove - JSTOR/Harvard Object Validation Environment - * Copyright 2003 by JSTOR and the President and Fellows of Harvard College - **********************************************************************/ - +/** + * ******************************************************************** Jhove - JSTOR/Harvard Object + * Validation Environment Copyright 2003 by JSTOR and the President and Fellows of Harvard College + * ******************************************************************** + */ package edu.harvard.hul.ois.jhove.module.tiff; import edu.harvard.hul.ois.jhove.*; /** - * Profile checker for TIFF Class IT-MP/P1. + * Profile checker for TIFF Class IT-MP/P1. * - * The TIFF/IT spec states that "TIFF/IT-MP/P1 is a simplified - * image file format profile for monochrome continuous tone - * picture image (MP) data and can be considred a constrained - * subset of TIFF/IT-MP specifically intended for - * simpler implementation." + *

The TIFF/IT spec states that "TIFF/IT-MP/P1 is a simplified image file format profile for + * monochrome continuous tone picture image (MP) data and can be considred a constrained subset of + * TIFF/IT-MP specifically intended for simpler implementation." * - * @author Gary McGath + * @author Gary McGath */ -public final class TiffProfileClassITMPP1 extends TiffProfileClassIT -{ - public TiffProfileClassITMPP1 () - { - super (); - _profileText = "TIFF/IT-MP/P1 (ISO 12639:1998)"; +public final class TiffProfileClassITMPP1 extends TiffProfileClassIT { + public TiffProfileClassITMPP1() { + super(); + _profileText = "TIFF/IT-MP/P1 (ISO 12639:1998)"; + } + + /** + * Returns true if the IFD satisfies the requirements of the profile. See the documentation for + * details. + */ + @Override + public boolean satisfiesThisProfile(IFD ifd) { + if (!super.satisfiesThisProfile(ifd)) { + return false; + } + + if (!(ifd instanceof TiffIFD)) { + return false; + } + TiffIFD tifd = (TiffIFD) ifd; + + if (!satisfiesNewSubfileType(tifd, 0)) { + return false; + } + + NisoImageMetadata niso = tifd.getNisoImageMetadata(); + int[] bps = niso.getBitsPerSample(); + if (bps == null || bps[0] != 8) { + return false; + } + + if (!satisfiesCompression(tifd, 1)) { + return false; + } + + if (!satisfiesPhotometricInterpretation(tifd, 0)) { + return false; } - /** - * Returns true if the IFD satisfies the requirements - * of the profile. See the documentation for - * details. - */ - @Override - public boolean satisfiesThisProfile (IFD ifd) - { - if (!super.satisfiesThisProfile (ifd)) { - return false; - } - - if (!(ifd instanceof TiffIFD)) { - return false; - } - TiffIFD tifd = (TiffIFD) ifd; - - if (!satisfiesNewSubfileType (tifd, 0)) { - return false; - } - - NisoImageMetadata niso = tifd.getNisoImageMetadata (); - int[] bps = niso.getBitsPerSample (); - if (bps == null || bps[0] != 8) { - return false; - } - - if (!satisfiesCompression (tifd, 1 )) { - return false; - } - - if (!satisfiesPhotometricInterpretation (tifd, 0)) { - return false; - } - - if (!satisfiesOrientation (tifd, 1)) { - return false; - } - - if (!satisfiesSamplesPerPixel (tifd, 1)) { - return false; - } - - if (!satisfiesResolutionUnit (tifd, new int [] { 2, 3} )) { - return false; - } - - if (!satisfiesDotRange (tifd, 0, 255)) { - return false; - } - - int ind = tifd.getImageColorIndicator (); - if (ind != 0 && ind != 1) { - return false; - } - - // ImageColorValue is defined if ImageColorIndicator=1 - if (ind == 1 && tifd.getImageColorValue () == IFD.NULL) { - return false; - } - - // PixelIntesityRange={0,255} - int [] pir = tifd.getPixelIntensityRange (); - if (pir == null || pir.length < 2) { - return false; - } - if (pir[0] != 0 || pir[1] != 255) { - return false; - } - // Tags which must NOT be defined - - return !(tifd.getDocumentName () != null || - niso.getScannerModelName () != null || - tifd.getPageName () != null || - niso.getHostComputer () != null || - tifd.getSite () != null || - tifd.getColorSequence () != null || - tifd.getIT8Header() != null); + if (!satisfiesOrientation(tifd, 1)) { + return false; } + + if (!satisfiesSamplesPerPixel(tifd, 1)) { + return false; + } + + if (!satisfiesResolutionUnit(tifd, new int[] {2, 3})) { + return false; + } + + if (!satisfiesDotRange(tifd, 0, 255)) { + return false; + } + + int ind = tifd.getImageColorIndicator(); + if (ind != 0 && ind != 1) { + return false; + } + + // ImageColorValue is defined if ImageColorIndicator=1 + if (ind == 1 && tifd.getImageColorValue() == IFD.NULL) { + return false; + } + + // PixelIntesityRange={0,255} + int[] pir = tifd.getPixelIntensityRange(); + if (pir == null || pir.length < 2) { + return false; + } + if (pir[0] != 0 || pir[1] != 255) { + return false; + } + // Tags which must NOT be defined + + return !(tifd.getDocumentName() != null + || niso.getScannerModelName() != null + || tifd.getPageName() != null + || niso.getHostComputer() != null + || tifd.getSite() != null + || tifd.getColorSequence() != null + || tifd.getIT8Header() != null); + } } diff --git a/jhove-modules/tiff-hul/src/main/java/edu/harvard/hul/ois/jhove/module/tiff/TiffProfileClassITMPP2.java b/jhove-modules/tiff-hul/src/main/java/edu/harvard/hul/ois/jhove/module/tiff/TiffProfileClassITMPP2.java index 73028e2aa..b5332cdbb 100644 --- a/jhove-modules/tiff-hul/src/main/java/edu/harvard/hul/ois/jhove/module/tiff/TiffProfileClassITMPP2.java +++ b/jhove-modules/tiff-hul/src/main/java/edu/harvard/hul/ois/jhove/module/tiff/TiffProfileClassITMPP2.java @@ -1,105 +1,100 @@ -/********************************************************************** - * Jhove - JSTOR/Harvard Object Validation Environment - * Copyright 2003 by JSTOR and the President and Fellows of Harvard College - **********************************************************************/ - +/** + * ******************************************************************** Jhove - JSTOR/Harvard Object + * Validation Environment Copyright 2003 by JSTOR and the President and Fellows of Harvard College + * ******************************************************************** + */ package edu.harvard.hul.ois.jhove.module.tiff; import edu.harvard.hul.ois.jhove.*; /** - * Profile checker for TIFF Class IT-MP/P2. + * Profile checker for TIFF Class IT-MP/P2. * - * The TIFF/IT spec states that "TIFF/IT-MP/P2 is an extension of - * TIFF/IT-MP/P1." + *

The TIFF/IT spec states that "TIFF/IT-MP/P2 is an extension of TIFF/IT-MP/P1." * - * @author Gary McGath + * @author Gary McGath */ -public final class TiffProfileClassITMPP2 extends TiffProfileClassIT -{ - public TiffProfileClassITMPP2 () - { - super (); - _profileText = "TIFF/IT-MP/P2 (ISO 12639:2003)"; +public final class TiffProfileClassITMPP2 extends TiffProfileClassIT { + public TiffProfileClassITMPP2() { + super(); + _profileText = "TIFF/IT-MP/P2 (ISO 12639:2003)"; + } + + /** + * Returns true if the IFD satisfies the requirements of the profile. See the documentation for + * details. + */ + @Override + public boolean satisfiesThisProfile(IFD ifd) { + if (!super.satisfiesThisProfile(ifd)) { + return false; + } + + if (!(ifd instanceof TiffIFD)) { + return false; + } + TiffIFD tifd = (TiffIFD) ifd; + + if (!satisfiesNewSubfileType(tifd, 0)) { + return false; + } + + NisoImageMetadata niso = tifd.getNisoImageMetadata(); + int[] bps = niso.getBitsPerSample(); + if (bps == null || bps[0] != 8) { + return false; + } + + if (!satisfiesCompression(tifd, new int[] {1, 7, 8})) { + return false; + } + + if (!satisfiesPhotometricInterpretation(tifd, 0)) { + return false; } - /** - * Returns true if the IFD satisfies the requirements - * of the profile. See the documentation for - * details. - */ - @Override - public boolean satisfiesThisProfile (IFD ifd) - { - if (!super.satisfiesThisProfile (ifd)) { - return false; - } - - if (!(ifd instanceof TiffIFD)) { - return false; - } - TiffIFD tifd = (TiffIFD) ifd; - - if (!satisfiesNewSubfileType (tifd, 0)) { - return false; - } - - NisoImageMetadata niso = tifd.getNisoImageMetadata (); - int[] bps = niso.getBitsPerSample (); - if (bps == null || bps[0] != 8) { - return false; - } - - if (!satisfiesCompression (tifd, new int[] {1, 7, 8} )) { - return false; - } - - if (!satisfiesPhotometricInterpretation (tifd, 0)) { - return false; - } - - if (!satisfiesOrientation (tifd, 1)) { - return false; - } - - if (!satisfiesSamplesPerPixel (tifd, 1)) { - return false; - } - - if (!satisfiesResolutionUnit (tifd, new int [] { 2, 3} )) { - return false; - } - - if (!satisfiesDotRange (tifd, 0, 255)) { - return false; - } - - int ind = tifd.getImageColorIndicator (); - if (ind != 0 && ind != 1) { - return false; - } - - // ImageColorValue is defined if ImageColorIndicator=1 - if (ind == 1 && tifd.getImageColorValue () == IFD.NULL) { - return false; - } - - // PixelIntesityRange={0,255} - int [] pir = tifd.getPixelIntensityRange (); - if (pir == null || pir.length < 2) { - return false; - } - if (pir[0] != 0 || pir[1] != 255) { - return false; - } - // Tags which must NOT be defined - - return !(tifd.getDocumentName () != null || - niso.getScannerModelName () != null || - tifd.getPageName () != null || - niso.getHostComputer () != null || - tifd.getSite () != null || - tifd.getColorSequence () != null || - tifd.getIT8Header() != null); + if (!satisfiesOrientation(tifd, 1)) { + return false; } + + if (!satisfiesSamplesPerPixel(tifd, 1)) { + return false; + } + + if (!satisfiesResolutionUnit(tifd, new int[] {2, 3})) { + return false; + } + + if (!satisfiesDotRange(tifd, 0, 255)) { + return false; + } + + int ind = tifd.getImageColorIndicator(); + if (ind != 0 && ind != 1) { + return false; + } + + // ImageColorValue is defined if ImageColorIndicator=1 + if (ind == 1 && tifd.getImageColorValue() == IFD.NULL) { + return false; + } + + // PixelIntesityRange={0,255} + int[] pir = tifd.getPixelIntensityRange(); + if (pir == null || pir.length < 2) { + return false; + } + if (pir[0] != 0 || pir[1] != 255) { + return false; + } + // Tags which must NOT be defined + + return !(tifd.getDocumentName() != null + || niso.getScannerModelName() != null + || tifd.getPageName() != null + || niso.getHostComputer() != null + || tifd.getSite() != null + || tifd.getColorSequence() != null + || tifd.getIT8Header() != null); + } } diff --git a/jhove-modules/tiff-hul/src/main/java/edu/harvard/hul/ois/jhove/module/tiff/TiffProfileClassITSD.java b/jhove-modules/tiff-hul/src/main/java/edu/harvard/hul/ois/jhove/module/tiff/TiffProfileClassITSD.java index 68da94077..bf70bc9c7 100644 --- a/jhove-modules/tiff-hul/src/main/java/edu/harvard/hul/ois/jhove/module/tiff/TiffProfileClassITSD.java +++ b/jhove-modules/tiff-hul/src/main/java/edu/harvard/hul/ois/jhove/module/tiff/TiffProfileClassITSD.java @@ -1,82 +1,77 @@ -/********************************************************************** - * Jhove - JSTOR/Harvard Object Validation Environment - * Copyright 2003 by JSTOR and the President and Fellows of Harvard College - **********************************************************************/ - +/** + * ******************************************************************** Jhove - JSTOR/Harvard Object + * Validation Environment Copyright 2003 by JSTOR and the President and Fellows of Harvard College + * ******************************************************************** + */ package edu.harvard.hul.ois.jhove.module.tiff; import edu.harvard.hul.ois.jhove.*; /** - * Profile checker for TIFF Class IT-SD. + * Profile checker for TIFF Class IT-SD. * - * The TIFF/IT spec states that "TIFF/IT-SD makes use of all - * the features and functionality supported by the TIFF and - * TIFF/IT fields appropriate to prescreened (copydot) - * colour separation images." + *

The TIFF/IT spec states that "TIFF/IT-SD makes use of all the features and functionality + * supported by the TIFF and TIFF/IT fields appropriate to prescreened (copydot) colour separation + * images." * - * @author Gary McGath + * @author Gary McGath */ -public final class TiffProfileClassITSD extends TiffProfileClassIT -{ - public TiffProfileClassITSD () - { - super (); - _profileText = "TIFF/IT-SD (ISO 12639:2003)"; - } +public final class TiffProfileClassITSD extends TiffProfileClassIT { + public TiffProfileClassITSD() { + super(); + _profileText = "TIFF/IT-SD (ISO 12639:2003)"; + } - /** - * Returns true if the IFD satisfies the requirements - * of the profile. See the documentation for - * details. - */ - @Override - public boolean satisfiesThisProfile (IFD ifd) - { - if (!super.satisfiesThisProfile (ifd)) { - return false; - } - - if (!(ifd instanceof TiffIFD)) { - return false; - } - TiffIFD tifd = (TiffIFD) ifd; + /** + * Returns true if the IFD satisfies the requirements of the profile. See the documentation for + * details. + */ + @Override + public boolean satisfiesThisProfile(IFD ifd) { + if (!super.satisfiesThisProfile(ifd)) { + return false; + } - NisoImageMetadata niso = tifd.getNisoImageMetadata (); + if (!(ifd instanceof TiffIFD)) { + return false; + } + TiffIFD tifd = (TiffIFD) ifd; - /* Check required values. */ + NisoImageMetadata niso = tifd.getNisoImageMetadata(); - int[] bps = niso.getBitsPerSample (); - if (bps[0] != 1) { - return false; - } + /* Check required values. */ - if (!satisfiesResolutionUnit (tifd, new int[] {2, 3})) { - return false; - } + int[] bps = niso.getBitsPerSample(); + if (bps[0] != 1) { + return false; + } - if (!satisfiesSamplesPerPixel (tifd, new int[] {1, 4})) { - return false; - } + if (!satisfiesResolutionUnit(tifd, new int[] {2, 3})) { + return false; + } - if (!satisfiesPhotometricInterpretation (tifd, 5)) { - return false; - } + if (!satisfiesSamplesPerPixel(tifd, new int[] {1, 4})) { + return false; + } - if (!satisfiesCompression (tifd, new int [] {1, 4, 8} )) { - return false; - } + if (!satisfiesPhotometricInterpretation(tifd, 5)) { + return false; + } - if (!satisfiesPlanarConfiguration (tifd, 2)) { - return false; - } + if (!satisfiesCompression(tifd, new int[] {1, 4, 8})) { + return false; + } - int inkSet = tifd.getInkSet (); - if (inkSet != 1 ) { - return false; - } + if (!satisfiesPlanarConfiguration(tifd, 2)) { + return false; + } - String seq = tifd.getColorSequence (); - return !(seq != null && !"CMYK".equals (seq) && !"YMCK".equals (seq)); + int inkSet = tifd.getInkSet(); + if (inkSet != 1) { + return false; } + + String seq = tifd.getColorSequence(); + return !(seq != null && !"CMYK".equals(seq) && !"YMCK".equals(seq)); + } } diff --git a/jhove-modules/tiff-hul/src/main/java/edu/harvard/hul/ois/jhove/module/tiff/TiffProfileClassITSDP2.java b/jhove-modules/tiff-hul/src/main/java/edu/harvard/hul/ois/jhove/module/tiff/TiffProfileClassITSDP2.java index b70b357c4..0d4fc10a9 100644 --- a/jhove-modules/tiff-hul/src/main/java/edu/harvard/hul/ois/jhove/module/tiff/TiffProfileClassITSDP2.java +++ b/jhove-modules/tiff-hul/src/main/java/edu/harvard/hul/ois/jhove/module/tiff/TiffProfileClassITSDP2.java @@ -1,98 +1,93 @@ -/********************************************************************** - * Jhove - JSTOR/Harvard Object Validation Environment - * Copyright 2003 by JSTOR and the President and Fellows of Harvard College - **********************************************************************/ - +/** + * ******************************************************************** Jhove - JSTOR/Harvard Object + * Validation Environment Copyright 2003 by JSTOR and the President and Fellows of Harvard College + * ******************************************************************** + */ package edu.harvard.hul.ois.jhove.module.tiff; import edu.harvard.hul.ois.jhove.*; /** - * Profile checker for TIFF Class IT-SD/P2. + * Profile checker for TIFF Class IT-SD/P2. * - * The TIFF/IT spec states that "TIFF/IT-SD/P2 is a simplified file - * format profile for screened data image (SD) data and can be - * considered a constrained subset of TIFF/IT-SD specifically - * intended for simpler implementation." + *

The TIFF/IT spec states that "TIFF/IT-SD/P2 is a simplified file format profile for screened + * data image (SD) data and can be considered a constrained subset of TIFF/IT-SD specifically + * intended for simpler implementation." * - * There is no TIFF/IT-SD/P1. + *

There is no TIFF/IT-SD/P1. * - * @author Gary McGath + * @author Gary McGath */ -public final class TiffProfileClassITSDP2 extends TiffProfileClassIT -{ - public TiffProfileClassITSDP2 () - { - super (); - _profileText = "TIFF/IT-SD/P2 (ISO 12639:2003)"; +public final class TiffProfileClassITSDP2 extends TiffProfileClassIT { + public TiffProfileClassITSDP2() { + super(); + _profileText = "TIFF/IT-SD/P2 (ISO 12639:2003)"; + } + + /** + * Returns true if the IFD satisfies the requirements of the profile. See the documentation for + * details. + */ + @Override + public boolean satisfiesThisProfile(IFD ifd) { + if (!super.satisfiesThisProfile(ifd)) { + return false; } - /** - * Returns true if the IFD satisfies the requirements - * of the profile. See the documentation for - * details. - */ - @Override - public boolean satisfiesThisProfile (IFD ifd) - { - if (!super.satisfiesThisProfile (ifd)) { - return false; - } - - if (!(ifd instanceof TiffIFD)) { - return false; - } - TiffIFD tifd = (TiffIFD) ifd; + if (!(ifd instanceof TiffIFD)) { + return false; + } + TiffIFD tifd = (TiffIFD) ifd; - NisoImageMetadata niso = tifd.getNisoImageMetadata (); + NisoImageMetadata niso = tifd.getNisoImageMetadata(); - /* Check required values. */ + /* Check required values. */ - int[] bps = niso.getBitsPerSample (); - if (bps[0] != 1) { - return false; - } + int[] bps = niso.getBitsPerSample(); + if (bps[0] != 1) { + return false; + } - if (!satisfiesResolutionUnit (tifd, new int[] {2, 3})) { - return false; - } + if (!satisfiesResolutionUnit(tifd, new int[] {2, 3})) { + return false; + } - if (!satisfiesSamplesPerPixel (tifd, new int[] {1, 4})) { - return false; - } + if (!satisfiesSamplesPerPixel(tifd, new int[] {1, 4})) { + return false; + } - if (!satisfiesPhotometricInterpretation (tifd, 5)) { - return false; - } + if (!satisfiesPhotometricInterpretation(tifd, 5)) { + return false; + } - if (!satisfiesCompression (tifd, new int [] {1, 4, 8} )) { - return false; - } + if (!satisfiesCompression(tifd, new int[] {1, 4, 8})) { + return false; + } - if (!satisfiesPlanarConfiguration (tifd, 2)) { - return false; - } + if (!satisfiesPlanarConfiguration(tifd, 2)) { + return false; + } - int inkSet = tifd.getInkSet (); - if (inkSet != 1 ) { - return false; - } + int inkSet = tifd.getInkSet(); + if (inkSet != 1) { + return false; + } - int numInks = tifd.getNumberOfInks (); - if (numInks != 4) { - return false; - } + int numInks = tifd.getNumberOfInks(); + if (numInks != 4) { + return false; + } - if (!satisfiesOrientation (tifd, 1)) { - return false; - } - /* Tags which must NOT be defined */ - return !(tifd.getDocumentName () != null || - niso.getScannerModelName () != null || - tifd.getPageName () != null || - niso.getHostComputer () != null || - tifd.getSite () != null || - tifd.getColorSequence () != null || - tifd.getIT8Header () != null); + if (!satisfiesOrientation(tifd, 1)) { + return false; } + /* Tags which must NOT be defined */ + return !(tifd.getDocumentName() != null + || niso.getScannerModelName() != null + || tifd.getPageName() != null + || niso.getHostComputer() != null + || tifd.getSite() != null + || tifd.getColorSequence() != null + || tifd.getIT8Header() != null); + } } diff --git a/jhove-modules/tiff-hul/src/main/java/edu/harvard/hul/ois/jhove/module/tiff/TiffProfileClassP.java b/jhove-modules/tiff-hul/src/main/java/edu/harvard/hul/ois/jhove/module/tiff/TiffProfileClassP.java index 9a3c1bc40..3e7c708f2 100644 --- a/jhove-modules/tiff-hul/src/main/java/edu/harvard/hul/ois/jhove/module/tiff/TiffProfileClassP.java +++ b/jhove-modules/tiff-hul/src/main/java/edu/harvard/hul/ois/jhove/module/tiff/TiffProfileClassP.java @@ -1,68 +1,64 @@ -/********************************************************************** - * Jhove - JSTOR/Harvard Object Validation Environment - * Copyright 2003 by JSTOR and the President and Fellows of Harvard College - **********************************************************************/ - +/** + * ******************************************************************** Jhove - JSTOR/Harvard Object + * Validation Environment Copyright 2003 by JSTOR and the President and Fellows of Harvard College + * ******************************************************************** + */ package edu.harvard.hul.ois.jhove.module.tiff; import edu.harvard.hul.ois.jhove.*; /** - * Profile checker for TIFF Class P (Baseline Palette color). + * Profile checker for TIFF Class P (Baseline Palette color). * - * @author Gary McGath + * @author Gary McGath */ -public final class TiffProfileClassP extends TiffProfile -{ - public TiffProfileClassP () - { - super (); - _profileText = "Baseline palette-color (Class P)"; - } - - /** - * Returns true if the IFD satisfies the requirements of a - * Class P profile. See the TIFF 6.0 specification for - * details. - */ - @Override - public boolean satisfiesThisProfile (IFD ifd) - { - if (!(ifd instanceof TiffIFD)) { - return false; - } - TiffIFD tifd = (TiffIFD) ifd; +public final class TiffProfileClassP extends TiffProfile { + public TiffProfileClassP() { + super(); + _profileText = "Baseline palette-color (Class P)"; + } - /* Check required tags. */ - NisoImageMetadata niso = tifd.getNisoImageMetadata (); - if (niso.getImageWidth () == NisoImageMetadata.NULL || - niso.getImageLength () == NisoImageMetadata.NULL || - niso.getStripOffsets () == null || - niso.getRowsPerStrip () == NisoImageMetadata.NULL || - niso.getStripByteCounts () == null || - niso.getXSamplingFrequency () == null || - niso.getYSamplingFrequency () == null || - niso.getColormapBitCodeValue () == null || - niso.getColormapRedValue () == null || - niso.getColormapGreenValue () == null || - niso.getColormapBlueValue () == null) { - return false; - } + /** + * Returns true if the IFD satisfies the requirements of a Class P profile. See the TIFF 6.0 + * specification for details. + */ + @Override + public boolean satisfiesThisProfile(IFD ifd) { + if (!(ifd instanceof TiffIFD)) { + return false; + } + TiffIFD tifd = (TiffIFD) ifd; - /* Check required values. */ - int[] bps = niso.getBitsPerSample (); - if (bps == null || (bps[0] != 4 && bps[0] != 8)) { - return false; - } + /* Check required tags. */ + NisoImageMetadata niso = tifd.getNisoImageMetadata(); + if (niso.getImageWidth() == NisoImageMetadata.NULL + || niso.getImageLength() == NisoImageMetadata.NULL + || niso.getStripOffsets() == null + || niso.getRowsPerStrip() == NisoImageMetadata.NULL + || niso.getStripByteCounts() == null + || niso.getXSamplingFrequency() == null + || niso.getYSamplingFrequency() == null + || niso.getColormapBitCodeValue() == null + || niso.getColormapRedValue() == null + || niso.getColormapGreenValue() == null + || niso.getColormapBlueValue() == null) { + return false; + } - if (!satisfiesCompression (tifd, new int [] {1, 32773} )) { - return false; - } + /* Check required values. */ + int[] bps = niso.getBitsPerSample(); + if (bps == null || (bps[0] != 4 && bps[0] != 8)) { + return false; + } - if (!satisfiesPhotometricInterpretation (tifd, 3)) { - return false; - } + if (!satisfiesCompression(tifd, new int[] {1, 32773})) { + return false; + } - return satisfiesResolutionUnit (tifd, new int [] {1, 2, 3} ); + if (!satisfiesPhotometricInterpretation(tifd, 3)) { + return false; } + + return satisfiesResolutionUnit(tifd, new int[] {1, 2, 3}); + } } diff --git a/jhove-modules/tiff-hul/src/main/java/edu/harvard/hul/ois/jhove/module/tiff/TiffProfileClassR.java b/jhove-modules/tiff-hul/src/main/java/edu/harvard/hul/ois/jhove/module/tiff/TiffProfileClassR.java index b60d757fb..e592263cf 100644 --- a/jhove-modules/tiff-hul/src/main/java/edu/harvard/hul/ois/jhove/module/tiff/TiffProfileClassR.java +++ b/jhove-modules/tiff-hul/src/main/java/edu/harvard/hul/ois/jhove/module/tiff/TiffProfileClassR.java @@ -1,69 +1,64 @@ -/********************************************************************** - * Jhove - JSTOR/Harvard Object Validation Environment - * Copyright 2003 by JSTOR and the President and Fellows of Harvard College - **********************************************************************/ - +/** + * ******************************************************************** Jhove - JSTOR/Harvard Object + * Validation Environment Copyright 2003 by JSTOR and the President and Fellows of Harvard College + * ******************************************************************** + */ package edu.harvard.hul.ois.jhove.module.tiff; import edu.harvard.hul.ois.jhove.*; /** - * Profile checker for TIFF Class R (Baseline Palette color). + * Profile checker for TIFF Class R (Baseline Palette color). * - * @author Gary McGath + * @author Gary McGath */ -public final class TiffProfileClassR extends TiffProfile -{ - public TiffProfileClassR () - { - super (); - _profileText = "Baseline RGB (Class R)"; - } - - /** - * Returns true if the IFD satisfies the requirements of a - * Class R profile. See the TIFF 6.0 specification for - * details. - */ - @Override - public boolean satisfiesThisProfile (IFD ifd) - { - if (!(ifd instanceof TiffIFD)) { - return false; - } - TiffIFD tifd = (TiffIFD) ifd; +public final class TiffProfileClassR extends TiffProfile { + public TiffProfileClassR() { + super(); + _profileText = "Baseline RGB (Class R)"; + } - /* Check required tags. */ - NisoImageMetadata niso = tifd.getNisoImageMetadata (); - if (niso.getImageWidth () == NisoImageMetadata.NULL || - niso.getImageLength () == NisoImageMetadata.NULL || - niso.getStripOffsets () == null || - niso.getRowsPerStrip () == NisoImageMetadata.NULL || - niso.getStripByteCounts () == null || - niso.getXSamplingFrequency () == null || - niso.getYSamplingFrequency () == null) { - return false; - } + /** + * Returns true if the IFD satisfies the requirements of a Class R profile. See the TIFF 6.0 + * specification for details. + */ + @Override + public boolean satisfiesThisProfile(IFD ifd) { + if (!(ifd instanceof TiffIFD)) { + return false; + } + TiffIFD tifd = (TiffIFD) ifd; - /* Check required values. */ - int [] bps = niso.getBitsPerSample (); - if (bps == null || bps.length < 3 || - bps[0] != 8 || bps[1] != 8 || bps[2] != 8) { - return false; - } + /* Check required tags. */ + NisoImageMetadata niso = tifd.getNisoImageMetadata(); + if (niso.getImageWidth() == NisoImageMetadata.NULL + || niso.getImageLength() == NisoImageMetadata.NULL + || niso.getStripOffsets() == null + || niso.getRowsPerStrip() == NisoImageMetadata.NULL + || niso.getStripByteCounts() == null + || niso.getXSamplingFrequency() == null + || niso.getYSamplingFrequency() == null) { + return false; + } - if (!satisfiesCompression (tifd, new int [] {1, 32773} )) { - return false; - } + /* Check required values. */ + int[] bps = niso.getBitsPerSample(); + if (bps == null || bps.length < 3 || bps[0] != 8 || bps[1] != 8 || bps[2] != 8) { + return false; + } - if (!satisfiesPhotometricInterpretation (tifd, 2)) { - return false; - } + if (!satisfiesCompression(tifd, new int[] {1, 32773})) { + return false; + } - if (niso.getSamplesPerPixel () < 3) { - return false; - } + if (!satisfiesPhotometricInterpretation(tifd, 2)) { + return false; + } - return satisfiesResolutionUnit (tifd, new int [] {1, 2, 3} ); + if (niso.getSamplesPerPixel() < 3) { + return false; } + + return satisfiesResolutionUnit(tifd, new int[] {1, 2, 3}); + } } diff --git a/jhove-modules/tiff-hul/src/main/java/edu/harvard/hul/ois/jhove/module/tiff/TiffProfileClassY.java b/jhove-modules/tiff-hul/src/main/java/edu/harvard/hul/ois/jhove/module/tiff/TiffProfileClassY.java index b87fe8212..80c1ea6a2 100644 --- a/jhove-modules/tiff-hul/src/main/java/edu/harvard/hul/ois/jhove/module/tiff/TiffProfileClassY.java +++ b/jhove-modules/tiff-hul/src/main/java/edu/harvard/hul/ois/jhove/module/tiff/TiffProfileClassY.java @@ -1,70 +1,65 @@ -/********************************************************************** - * Jhove - JSTOR/Harvard Object Validation Environment - * Copyright 2004 by JSTOR and the President and Fellows of Harvard College - **********************************************************************/ - +/** + * ******************************************************************** Jhove - JSTOR/Harvard Object + * Validation Environment Copyright 2004 by JSTOR and the President and Fellows of Harvard College + * ******************************************************************** + */ package edu.harvard.hul.ois.jhove.module.tiff; import edu.harvard.hul.ois.jhove.*; /** - * Profile checker for TIFF Class Y (Baseline YCbCr). + * Profile checker for TIFF Class Y (Baseline YCbCr). * - * @author Gary McGath + * @author Gary McGath */ -public final class TiffProfileClassY extends TiffProfile -{ - public TiffProfileClassY () - { - super (); - _profileText = "Extension YCbCr (Class Y)"; - } - - /** - * Returns true if the IFD satisfies the requirements of a - * Class Y profile. See the TIFF 6.0 specification for - * details. - */ - @Override - public boolean satisfiesThisProfile (IFD ifd) - { - if (!(ifd instanceof TiffIFD)) { - return false; - } - TiffIFD tifd = (TiffIFD) ifd; +public final class TiffProfileClassY extends TiffProfile { + public TiffProfileClassY() { + super(); + _profileText = "Extension YCbCr (Class Y)"; + } - /* Check required tags. */ - NisoImageMetadata niso = tifd.getNisoImageMetadata (); - if (niso.getImageWidth () == NisoImageMetadata.NULL || - niso.getImageLength () == NisoImageMetadata.NULL || - niso.getStripOffsets () == null || - niso.getRowsPerStrip () == NisoImageMetadata.NULL || - niso.getStripByteCounts () == null || - niso.getXSamplingFrequency () == null || - niso.getYSamplingFrequency () == null || - niso.getReferenceBlackWhite () == null) { - return false; - } + /** + * Returns true if the IFD satisfies the requirements of a Class Y profile. See the TIFF 6.0 + * specification for details. + */ + @Override + public boolean satisfiesThisProfile(IFD ifd) { + if (!(ifd instanceof TiffIFD)) { + return false; + } + TiffIFD tifd = (TiffIFD) ifd; - /* Check required values. */ - if (!satisfiesSamplesPerPixel (tifd, 3)) { - return false; - } + /* Check required tags. */ + NisoImageMetadata niso = tifd.getNisoImageMetadata(); + if (niso.getImageWidth() == NisoImageMetadata.NULL + || niso.getImageLength() == NisoImageMetadata.NULL + || niso.getStripOffsets() == null + || niso.getRowsPerStrip() == NisoImageMetadata.NULL + || niso.getStripByteCounts() == null + || niso.getXSamplingFrequency() == null + || niso.getYSamplingFrequency() == null + || niso.getReferenceBlackWhite() == null) { + return false; + } - int[] bps = niso.getBitsPerSample (); - if (bps == null || bps.length < 3 || - bps[0] != 8 || bps[1] != 8 || bps[2] != 8) { - return false; - } + /* Check required values. */ + if (!satisfiesSamplesPerPixel(tifd, 3)) { + return false; + } - if (!satisfiesCompression (tifd, new int [] {1, 5, 6} )) { - return false; - } + int[] bps = niso.getBitsPerSample(); + if (bps == null || bps.length < 3 || bps[0] != 8 || bps[1] != 8 || bps[2] != 8) { + return false; + } - if (!satisfiesPhotometricInterpretation (tifd, 6)) { - return false; - } + if (!satisfiesCompression(tifd, new int[] {1, 5, 6})) { + return false; + } - return satisfiesResolutionUnit (tifd, new int [] {1, 2, 3} ); + if (!satisfiesPhotometricInterpretation(tifd, 6)) { + return false; } + + return satisfiesResolutionUnit(tifd, new int[] {1, 2, 3}); + } } diff --git a/jhove-modules/tiff-hul/src/main/java/edu/harvard/hul/ois/jhove/module/tiff/TiffProfileDLF.java b/jhove-modules/tiff-hul/src/main/java/edu/harvard/hul/ois/jhove/module/tiff/TiffProfileDLF.java index d6978226c..724c139b3 100644 --- a/jhove-modules/tiff-hul/src/main/java/edu/harvard/hul/ois/jhove/module/tiff/TiffProfileDLF.java +++ b/jhove-modules/tiff-hul/src/main/java/edu/harvard/hul/ois/jhove/module/tiff/TiffProfileDLF.java @@ -1,78 +1,67 @@ -/********************************************************************** - * Jhove - JSTOR/Harvard Object Validation Environment - * Copyright 2003 by JSTOR and the President and Fellows of Harvard College - **********************************************************************/ - +/** + * ******************************************************************** Jhove - JSTOR/Harvard Object + * Validation Environment Copyright 2003 by JSTOR and the President and Fellows of Harvard College + * ******************************************************************** + */ package edu.harvard.hul.ois.jhove.module.tiff; import edu.harvard.hul.ois.jhove.*; -/** - * Abstract superclass for the profile checkers for TIFF/DLF - */ -public abstract class TiffProfileDLF extends TiffProfile -{ - public TiffProfileDLF () - { - super (); +/** Abstract superclass for the profile checkers for TIFF/DLF */ +public abstract class TiffProfileDLF extends TiffProfile { + public TiffProfileDLF() { + super(); + } + + /** + * Returns true if the IFD satisfies the requirements that are common to the bilevel, grayscale, + * and color DLF profiles. The subclasses should call super(ifd) first, then do additional + * checking if it returns true. details. + */ + @Override + public boolean satisfiesThisProfile(IFD ifd) { + if (!(ifd instanceof TiffIFD)) { + return false; } + TiffIFD tifd = (TiffIFD) ifd; + // passed all tests - /** - * Returns true if the IFD satisfies the requirements - * that are common to the bilevel, grayscale, and color - * DLF profiles. The subclasses should call super(ifd) - * first, then do additional checking if it returns true. - * details. - */ - @Override - public boolean satisfiesThisProfile (IFD ifd) - { - if (!(ifd instanceof TiffIFD)) { - return false; - } - TiffIFD tifd = (TiffIFD) ifd; - // passed all tests + return satisfiesPhotometricInterpretation(tifd, new int[] {0, 1}); + } - return satisfiesPhotometricInterpretation (tifd, new int[] {0, 1} ); + /** + * Checks for minimum X and Y resolution. All of the DLF profiles have similar tests for + * XResolution and YResolution. In all cases the values depend on the ResolutionUnit, which must + * be either 2 or 3. + * + * @param tifd The TiffIFD from which to extract the tags. + * @param minUnit2Res The minimum XResolution and YResolution when ResolutionUnit is 2 + * @param minUnit3Res The minimum XResolution and YResolution when ResolutionUnit is 3 + */ + protected boolean hasMinimumResolution(TiffIFD tifd, double minUnit2Res, double minUnit3Res) { + NisoImageMetadata niso = tifd.getNisoImageMetadata(); + Rational xrat = niso.getXSamplingFrequency(); + Rational yrat = niso.getYSamplingFrequency(); + if (xrat == null || yrat == null) { + return false; } - /** Checks for minimum X and Y resolution. - * All of the DLF profiles have similar tests for - * XResolution and YResolution. In all cases the - * values depend on the ResolutionUnit, which must be - * either 2 or 3. - * - * @param tifd The TiffIFD from which to extract the tags. - * @param minUnit2Res The minimum XResolution and YResolution - * when ResolutionUnit is 2 - * @param minUnit3Res The minimum XResolution and YResolution - * when ResolutionUnit is 3 - */ - - protected boolean hasMinimumResolution (TiffIFD tifd, double minUnit2Res, - double minUnit3Res) - { - NisoImageMetadata niso = tifd.getNisoImageMetadata (); - Rational xrat = niso.getXSamplingFrequency (); - Rational yrat = niso.getYSamplingFrequency (); - if (xrat == null || yrat == null) { - return false; + int resUnit = niso.getSamplingFrequencyUnit(); + switch (resUnit) { + case 2: + if (xrat.toDouble() < minUnit2Res || yrat.toDouble() < minUnit2Res) { + return false; } - - int resUnit = niso.getSamplingFrequencyUnit (); - switch (resUnit) { - case 2: - if (xrat.toDouble() < minUnit2Res || yrat.toDouble() < minUnit2Res) { - return false; - } break; - case 3: - if (xrat.toDouble() < minUnit3Res || yrat.toDouble() < minUnit3Res) { - return false; - } break; - default: - return false; // resUnit must be 2 or 3 + break; + case 3: + if (xrat.toDouble() < minUnit3Res || yrat.toDouble() < minUnit3Res) { + return false; } - - return true; // passed all tests + break; + default: + return false; // resUnit must be 2 or 3 } + + return true; // passed all tests + } } diff --git a/jhove-modules/tiff-hul/src/main/java/edu/harvard/hul/ois/jhove/module/tiff/TiffProfileDLFBW.java b/jhove-modules/tiff-hul/src/main/java/edu/harvard/hul/ois/jhove/module/tiff/TiffProfileDLFBW.java index 6d7d94c16..9af2eb106 100644 --- a/jhove-modules/tiff-hul/src/main/java/edu/harvard/hul/ois/jhove/module/tiff/TiffProfileDLFBW.java +++ b/jhove-modules/tiff-hul/src/main/java/edu/harvard/hul/ois/jhove/module/tiff/TiffProfileDLFBW.java @@ -1,47 +1,42 @@ -/********************************************************************** - * Jhove - JSTOR/Harvard Object Validation Environment - * Copyright 2003 by JSTOR and the President and Fellows of Harvard College - **********************************************************************/ - +/** + * ******************************************************************** Jhove - JSTOR/Harvard Object + * Validation Environment Copyright 2003 by JSTOR and the President and Fellows of Harvard College + * ******************************************************************** + */ package edu.harvard.hul.ois.jhove.module.tiff; - - /** - * Profile checker for TIFF DLF Benchmark for Faithful Digital - * Reproductions of Monographs and Serials: black and white. + * Profile checker for TIFF DLF Benchmark for Faithful Digital Reproductions of Monographs and + * Serials: black and white. */ -public final class TiffProfileDLFBW extends TiffProfileDLF -{ - public TiffProfileDLFBW () - { - super (); - _profileText = "DLF Benchmark for Faithful Digital " + - "Reproductions of Monographs and Serials: " + - "black and white"; - } +public final class TiffProfileDLFBW extends TiffProfileDLF { + public TiffProfileDLFBW() { + super(); + _profileText = + "DLF Benchmark for Faithful Digital " + + "Reproductions of Monographs and Serials: " + + "black and white"; + } - /** - * Returns true if the IFD satisfies the requirements - * of the profile. See the documentation for - * details. - */ - @Override - public boolean satisfiesThisProfile (IFD ifd) - { - if (!(ifd instanceof TiffIFD)) { - return false; - } - TiffIFD tifd = (TiffIFD) ifd; + /** + * Returns true if the IFD satisfies the requirements of the profile. See the documentation for + * details. + */ + @Override + public boolean satisfiesThisProfile(IFD ifd) { + if (!(ifd instanceof TiffIFD)) { + return false; + } + TiffIFD tifd = (TiffIFD) ifd; - if (!satisfiesCompression (tifd, new int [] {1, 6} )) { - return false; - } + if (!satisfiesCompression(tifd, new int[] {1, 6})) { + return false; + } - if (!satisfiesPhotometricInterpretation (tifd, new int [] {0, 1} )) { - return false; - } - /* XResolution and YResolution >= 600 (inches) or 1520 (cm) */ - return hasMinimumResolution (tifd, 600.0, 1520.0); + if (!satisfiesPhotometricInterpretation(tifd, new int[] {0, 1})) { + return false; } + /* XResolution and YResolution >= 600 (inches) or 1520 (cm) */ + return hasMinimumResolution(tifd, 600.0, 1520.0); + } } diff --git a/jhove-modules/tiff-hul/src/main/java/edu/harvard/hul/ois/jhove/module/tiff/TiffProfileDLFColor.java b/jhove-modules/tiff-hul/src/main/java/edu/harvard/hul/ois/jhove/module/tiff/TiffProfileDLFColor.java index e7566969c..e5df435a8 100644 --- a/jhove-modules/tiff-hul/src/main/java/edu/harvard/hul/ois/jhove/module/tiff/TiffProfileDLFColor.java +++ b/jhove-modules/tiff-hul/src/main/java/edu/harvard/hul/ois/jhove/module/tiff/TiffProfileDLFColor.java @@ -1,56 +1,51 @@ -/********************************************************************** - * Jhove - JSTOR/Harvard Object Validation Environment - * Copyright 2003 by JSTOR and the President and Fellows of Harvard College - **********************************************************************/ - +/** + * ******************************************************************** Jhove - JSTOR/Harvard Object + * Validation Environment Copyright 2003 by JSTOR and the President and Fellows of Harvard College + * ******************************************************************** + */ package edu.harvard.hul.ois.jhove.module.tiff; - /** - * Profile checker for TIFF DLF Benchmark for Faithful Digital - * Reproductions of Monographs and Serials: color. + * Profile checker for TIFF DLF Benchmark for Faithful Digital Reproductions of Monographs and + * Serials: color. */ -public final class TiffProfileDLFColor extends TiffProfileDLF -{ - public TiffProfileDLFColor () - { - super (); - _profileText = "DLF Benchmark for Faithful Digital " + - "Reproductions of Monographs and Serials: color"; +public final class TiffProfileDLFColor extends TiffProfileDLF { + public TiffProfileDLFColor() { + super(); + _profileText = + "DLF Benchmark for Faithful Digital " + "Reproductions of Monographs and Serials: color"; + } + + /** + * Returns true if the IFD satisfies the requirements of the profile. See the documentation for + * details. + */ + @Override + public boolean satisfiesThisProfile(IFD ifd) { + if (!(ifd instanceof TiffIFD)) { + return false; } + TiffIFD tifd = (TiffIFD) ifd; - /** - * Returns true if the IFD satisfies the requirements - * of the profile. See the documentation for - * details. - */ - @Override - public boolean satisfiesThisProfile (IFD ifd) - { - if (!(ifd instanceof TiffIFD)) { - return false; - } - TiffIFD tifd = (TiffIFD) ifd; - - if (!satisfiesCompression (tifd, new int [] {1, 5, 32773} )) { - return false; - } + if (!satisfiesCompression(tifd, new int[] {1, 5, 32773})) { + return false; + } - if (!satisfiesPhotometricInterpretation (tifd, new int [] {2, 6} )) { - return false; - } + if (!satisfiesPhotometricInterpretation(tifd, new int[] {2, 6})) { + return false; + } - if (!satisfiesSamplesPerPixel (tifd, new int [] {3} )) { - return false; - } + if (!satisfiesSamplesPerPixel(tifd, new int[] {3})) { + return false; + } - int[] bps = tifd.getNisoImageMetadata ().getBitsPerSample (); - for (int i=0; i= 300 (in) or 760 (cm) */ - return hasMinimumResolution (tifd, 300.0, 760.0); + int[] bps = tifd.getNisoImageMetadata().getBitsPerSample(); + for (int i = 0; i < bps.length; i++) { + if (bps[i] != 8) { + return false; + } } + /* XResolution and YResolution >= 300 (in) or 760 (cm) */ + return hasMinimumResolution(tifd, 300.0, 760.0); + } } diff --git a/jhove-modules/tiff-hul/src/main/java/edu/harvard/hul/ois/jhove/module/tiff/TiffProfileDLFGray.java b/jhove-modules/tiff-hul/src/main/java/edu/harvard/hul/ois/jhove/module/tiff/TiffProfileDLFGray.java index 50463b946..3b1b491fb 100644 --- a/jhove-modules/tiff-hul/src/main/java/edu/harvard/hul/ois/jhove/module/tiff/TiffProfileDLFGray.java +++ b/jhove-modules/tiff-hul/src/main/java/edu/harvard/hul/ois/jhove/module/tiff/TiffProfileDLFGray.java @@ -1,56 +1,51 @@ -/********************************************************************** - * Jhove - JSTOR/Harvard Object Validation Environment - * Copyright 2003 by JSTOR and the President and Fellows of Harvard College - **********************************************************************/ - +/** + * ******************************************************************** Jhove - JSTOR/Harvard Object + * Validation Environment Copyright 2003 by JSTOR and the President and Fellows of Harvard College + * ******************************************************************** + */ package edu.harvard.hul.ois.jhove.module.tiff; - - /** - * Profile checker for TIFF DLF Benchmark for Faithful Digital - * Reproductions of Monographs and Serials: grayscale. + * Profile checker for TIFF DLF Benchmark for Faithful Digital Reproductions of Monographs and + * Serials: grayscale. */ -public final class TiffProfileDLFGray extends TiffProfileDLF -{ - public TiffProfileDLFGray () - { - super (); - _profileText = "DLF Benchmark for Faithful Digital " + - "Reproductions of Monographs and Serials: " + - "grayscale and white"; +public final class TiffProfileDLFGray extends TiffProfileDLF { + public TiffProfileDLFGray() { + super(); + _profileText = + "DLF Benchmark for Faithful Digital " + + "Reproductions of Monographs and Serials: " + + "grayscale and white"; + } + + /** + * Returns true if the IFD satisfies the requirements of the profile. See the documentation for + * details. + */ + @Override + public boolean satisfiesThisProfile(IFD ifd) { + if (!(ifd instanceof TiffIFD)) { + return false; } + TiffIFD tifd = (TiffIFD) ifd; - /** - * Returns true if the IFD satisfies the requirements - * of the profile. See the documentation for - * details. - */ - @Override - public boolean satisfiesThisProfile (IFD ifd) - { - if (!(ifd instanceof TiffIFD)) { - return false; - } - TiffIFD tifd = (TiffIFD) ifd; - - if (!satisfiesCompression (tifd, new int [] {1, 5, 32773} )) { - return false; - } + if (!satisfiesCompression(tifd, new int[] {1, 5, 32773})) { + return false; + } - if (!satisfiesPhotometricInterpretation (tifd, new int [] {0, 1} )) { - return false; - } + if (!satisfiesPhotometricInterpretation(tifd, new int[] {0, 1})) { + return false; + } - if (!satisfiesSamplesPerPixel (tifd, new int [] {1} )) { - return false; - } + if (!satisfiesSamplesPerPixel(tifd, new int[] {1})) { + return false; + } - int[] bps = tifd.getNisoImageMetadata ().getBitsPerSample (); - if (bps == null || bps[0] != 8) { - return false; - } - /* XResolution and YResolution >= 300 (in) or 760 (cm) */ - return hasMinimumResolution (tifd, 300.0, 760.0); + int[] bps = tifd.getNisoImageMetadata().getBitsPerSample(); + if (bps == null || bps[0] != 8) { + return false; } + /* XResolution and YResolution >= 300 (in) or 760 (cm) */ + return hasMinimumResolution(tifd, 300.0, 760.0); + } } diff --git a/jhove-modules/tiff-hul/src/main/java/edu/harvard/hul/ois/jhove/module/tiff/TiffProfileDNG.java b/jhove-modules/tiff-hul/src/main/java/edu/harvard/hul/ois/jhove/module/tiff/TiffProfileDNG.java index d29312e6d..7efd6afd2 100644 --- a/jhove-modules/tiff-hul/src/main/java/edu/harvard/hul/ois/jhove/module/tiff/TiffProfileDNG.java +++ b/jhove-modules/tiff-hul/src/main/java/edu/harvard/hul/ois/jhove/module/tiff/TiffProfileDNG.java @@ -1,123 +1,116 @@ -/********************************************************************** - * Jhove - JSTOR/Harvard Object Validation Environment - * Copyright 2004 by JSTOR and the President and Fellows of Harvard College +/** + * ******************************************************************** Jhove - JSTOR/Harvard Object + * Validation Environment Copyright 2004 by JSTOR and the President and Fellows of Harvard College * - **********************************************************************/ - + *

******************************************************************** + */ package edu.harvard.hul.ois.jhove.module.tiff; import edu.harvard.hul.ois.jhove.*; /** - * Profile checker for the DNG raw IFD. - * - * + * Profile checker for the DNG raw IFD. + * * @author Gary McGath - * * @see TiffProfileDNG - * */ public class TiffProfileDNG extends TiffProfile { - /** PhotometricInterpretation for CFA space */ - public final static int CFA = 32803; - - /* PhotometricInterpretation for LinearRaw space */ - public final static int LINEAR_RAW = 34892; - - /* Set to true if anything directly contravenes DNG, - * or a previous profile has reported as DNG */ -// private boolean notDNG; - - /* An IFD has been seen with a photometricInterpretation - * specific to DNG. */ -// private boolean photoInterpOK; - - /* Orientation has been specified. */ -// private boolean orientationSeen; - - /* DNGVersion tag has been seen */ -// private boolean dngVersionSeen; - - /* UniqueCameraModel tag has been seen */ -// private boolean uniqueCameraModelSeen; - - /* AsShotNeutral tag has been seen. This isn't required, - * but is mutually exclusive with AsShotWhiteXY. - private boolean asShotNeutralSeen; - */ - - /* AsShotWhiteXY tag has been seen. This isn't required, - * but is mutually exclusive with AsShotNeutral. - private boolean asShotWhiteXYSeen; - */ - - /** - * - */ - public TiffProfileDNG() { - super(); - _profileText = "DNG 1.0.0.0 (September 2004)"; - //notDNG = false; - //photoInterpOK = false; - //orientationSeen = false; - //dngVersionSeen = false; - //uniqueCameraModelSeen = false; + /** PhotometricInterpretation for CFA space */ + public static final int CFA = 32803; + + /* PhotometricInterpretation for LinearRaw space */ + public static final int LINEAR_RAW = 34892; + + /* Set to true if anything directly contravenes DNG, + * or a previous profile has reported as DNG */ + // private boolean notDNG; + + /* An IFD has been seen with a photometricInterpretation + * specific to DNG. */ + // private boolean photoInterpOK; + + /* Orientation has been specified. */ + // private boolean orientationSeen; + + /* DNGVersion tag has been seen */ + // private boolean dngVersionSeen; + + /* UniqueCameraModel tag has been seen */ + // private boolean uniqueCameraModelSeen; + + /* AsShotNeutral tag has been seen. This isn't required, + * but is mutually exclusive with AsShotWhiteXY. + private boolean asShotNeutralSeen; + */ + + /* AsShotWhiteXY tag has been seen. This isn't required, + * but is mutually exclusive with AsShotNeutral. + private boolean asShotWhiteXYSeen; + */ + + /** */ + public TiffProfileDNG() { + super(); + _profileText = "DNG 1.0.0.0 (September 2004)"; + // notDNG = false; + // photoInterpOK = false; + // orientationSeen = false; + // dngVersionSeen = false; + // uniqueCameraModelSeen = false; + } + + /** + * Returns true if the IFD satisfies the requirements of the profile. See the documentation for + * details. + */ + @Override + public boolean satisfiesThisProfile(IFD ifd) { + if (!(ifd instanceof TiffIFD)) { + return false; } + TiffIFD tifd = (TiffIFD) ifd; - /** - * Returns true if the IFD satisfies the requirements - * of the profile. See the documentation for - * details. - */ - @Override - public boolean satisfiesThisProfile(IFD ifd) { - if (!(ifd instanceof TiffIFD)) { - return false; + /* Check if this is the "raw" profile. */ + NisoImageMetadata niso = tifd.getNisoImageMetadata(); + int pInterpretation = niso.getColorSpace(); + if (!(pInterpretation == CFA || pInterpretation == LINEAR_RAW)) { + return false; + } + /* BitsPerSample must be 8 to 32, and same for all samples */ + int[] bps = niso.getBitsPerSample(); + if (bps != null) { + int bpsval = bps[0]; + if (bpsval < 8 || bpsval > 32) { + return false; + } + for (int i = 0; i < bps.length; i++) { + if (bpsval != bps[i]) { + return false; } - TiffIFD tifd = (TiffIFD) ifd; + } + } - /* Check if this is the "raw" profile. */ - NisoImageMetadata niso = tifd.getNisoImageMetadata (); - int pInterpretation = niso.getColorSpace (); - if (!(pInterpretation == CFA || pInterpretation == LINEAR_RAW)) { - return false; - } - /* BitsPerSample must be 8 to 32, and same for all samples */ - int[] bps = niso.getBitsPerSample(); - if (bps != null) { - int bpsval = bps[0]; - if (bpsval < 8 || bpsval > 32) { - return false; - } - for (int i = 0; i < bps.length; i++) { - if (bpsval != bps[i]) { - return false; - } - } - } - - /* If the photometric interpretation is CFA, there must be - * certain other tags. */ - if (pInterpretation == CFA && (tifd.getCFAPlaneColor() == null || - tifd.getCFARepeatPatternDim() == null || - tifd.getCFAPattern() == null)) { - return false; - } + /* If the photometric interpretation is CFA, there must be + * certain other tags. */ + if (pInterpretation == CFA + && (tifd.getCFAPlaneColor() == null + || tifd.getCFARepeatPatternDim() == null + || tifd.getCFAPattern() == null)) { + return false; + } - /* Orientation is required. */ - if (niso.getOrientation() == NisoImageMetadata.NULL) { - return false; - } - - /* Compression must be 1 or 7 */ - int compression = niso.getCompressionScheme (); - if (compression != NisoImageMetadata.NULL && - !(compression == 1 || compression == 7)) { - return false; - } - - return true; + /* Orientation is required. */ + if (niso.getOrientation() == NisoImageMetadata.NULL) { + return false; + } + + /* Compression must be 1 or 7 */ + int compression = niso.getCompressionScheme(); + if (compression != NisoImageMetadata.NULL && !(compression == 1 || compression == 7)) { + return false; } + return true; + } } diff --git a/jhove-modules/tiff-hul/src/main/java/edu/harvard/hul/ois/jhove/module/tiff/TiffProfileDNGThumb.java b/jhove-modules/tiff-hul/src/main/java/edu/harvard/hul/ois/jhove/module/tiff/TiffProfileDNGThumb.java index 7e90148c8..57ceb74dd 100644 --- a/jhove-modules/tiff-hul/src/main/java/edu/harvard/hul/ois/jhove/module/tiff/TiffProfileDNGThumb.java +++ b/jhove-modules/tiff-hul/src/main/java/edu/harvard/hul/ois/jhove/module/tiff/TiffProfileDNGThumb.java @@ -1,61 +1,51 @@ -/********************************************************************** - * Jhove - JSTOR/Harvard Object Validation Environment - * Copyright 2004 by JSTOR and the President and Fellows of Harvard College +/** + * ******************************************************************** Jhove - JSTOR/Harvard Object + * Validation Environment Copyright 2004 by JSTOR and the President and Fellows of Harvard College * - **********************************************************************/ - + *

******************************************************************** + */ package edu.harvard.hul.ois.jhove.module.tiff; /** - * IFD 0 of a DNG document must satisfy this profile. It doesn't - * actually have to be a "thumbnail" in the sense of containing - * a low-resolution image, but it has to contain the "IFD 0" - * tags specified by DNG. In addition, - * some other document must satisfy TiffProfileDNG. - * + * IFD 0 of a DNG document must satisfy this profile. It doesn't actually have to be a "thumbnail" + * in the sense of containing a low-resolution image, but it has to contain the "IFD 0" tags + * specified by DNG. In addition, some other document must satisfy TiffProfileDNG. + * * @author Gary McGath * @see TiffProfileDNG */ public class TiffProfileDNGThumb extends TiffProfile { - /** - * - */ - public TiffProfileDNGThumb() { - super(); - } + /** */ + public TiffProfileDNGThumb() { + super(); + } - /** - * Returns true if the IFD satisfies the requirements of a - * DNG thumbnail profile. - */ - @Override - public boolean satisfiesThisProfile(IFD ifd) - { - if (!(ifd instanceof TiffIFD)) { - return false; - } - TiffIFD tifd = (TiffIFD) ifd; - /* Check required tags. */ - if (tifd.getDNGVersion() == null) { - return false; - } + /** Returns true if the IFD satisfies the requirements of a DNG thumbnail profile. */ + @Override + public boolean satisfiesThisProfile(IFD ifd) { + if (!(ifd instanceof TiffIFD)) { + return false; + } + TiffIFD tifd = (TiffIFD) ifd; + /* Check required tags. */ + if (tifd.getDNGVersion() == null) { + return false; + } - if (tifd.getNewSubfileType() != 1) { - return false; - } - - if (tifd.getAsShotNeutral () != null && - tifd.getAsShotWhiteXY () != null) { - // There can be only one - return false; - } - /* The specification says that PhotometricInterpretation - * must be 1 or 2 for a thumbnail -- but there's no requirement - * that this BE a thumbnail. So that requirement appears - * to be moot. */ + if (tifd.getNewSubfileType() != 1) { + return false; + } - return tifd.getUniqueCameraModel () != null; + if (tifd.getAsShotNeutral() != null && tifd.getAsShotWhiteXY() != null) { + // There can be only one + return false; } + /* The specification says that PhotometricInterpretation + * must be 1 or 2 for a thumbnail -- but there's no requirement + * that this BE a thumbnail. So that requirement appears + * to be moot. */ + return tifd.getUniqueCameraModel() != null; + } } diff --git a/jhove-modules/tiff-hul/src/main/java/edu/harvard/hul/ois/jhove/module/tiff/TiffProfileEP.java b/jhove-modules/tiff-hul/src/main/java/edu/harvard/hul/ois/jhove/module/tiff/TiffProfileEP.java index 5ce147eee..5fc15a85e 100644 --- a/jhove-modules/tiff-hul/src/main/java/edu/harvard/hul/ois/jhove/module/tiff/TiffProfileEP.java +++ b/jhove-modules/tiff-hul/src/main/java/edu/harvard/hul/ois/jhove/module/tiff/TiffProfileEP.java @@ -1,142 +1,130 @@ -/********************************************************************** - * Jhove - JSTOR/Harvard Object Validation Environment - * Copyright 2003 by JSTOR and the President and Fellows of Harvard College - **********************************************************************/ - +/** + * ******************************************************************** Jhove - JSTOR/Harvard Object + * Validation Environment Copyright 2003 by JSTOR and the President and Fellows of Harvard College + * ******************************************************************** + */ package edu.harvard.hul.ois.jhove.module.tiff; import edu.harvard.hul.ois.jhove.*; - /** - * Profile checker for TIFF/EP. - * For TIFF/EP, no default values may be assumed. - * At the moment, we have no way to determine which values - * were defaulted, so defaults are shown even if the file - * satisfies the EP profile. - * - * This class also serves as the base class for DNG, - * which is defined as a restricted subset of TIFF/EP. + * Profile checker for TIFF/EP. For TIFF/EP, no default values may be assumed. At the moment, we + * have no way to determine which values were defaulted, so defaults are shown even if the file + * satisfies the EP profile. + * + *

This class also serves as the base class for DNG, which is defined as a restricted subset of + * TIFF/EP. */ -public class TiffProfileEP extends TiffProfile -{ - public TiffProfileEP () - { - super (); - _profileText = "TIFF/EP (ISO 12234-2:2001)"; +public class TiffProfileEP extends TiffProfile { + public TiffProfileEP() { + super(); + _profileText = "TIFF/EP (ISO 12234-2:2001)"; + } + + /** + * Returns true if the IFD satisfies the requirements of a TIFF/EP profile. See the TIFF/EP + * specification for details. + */ + @Override + public boolean satisfiesThisProfile(IFD ifd) { + if (!(ifd instanceof TiffIFD)) { + return false; } + TiffIFD tifd = (TiffIFD) ifd; - /** - * Returns true if the IFD satisfies the requirements of a - * TIFF/EP profile. See the TIFF/EP specification for details. + /* Check required tags. */ + NisoImageMetadata niso = tifd.getNisoImageMetadata(); + if (niso.getImageWidth() == NisoImageMetadata.NULL + || niso.getImageLength() == NisoImageMetadata.NULL + || niso.getBitsPerSample() == null + || tifd.getImageDescription() == null + || niso.getXSamplingFrequency() == null + || niso.getYSamplingFrequency() == null + || niso.getScannerManufacturer() == null + || (niso.getScannerModelName() == null && niso.getScannerModelNumber() == null) + || niso.getScanningSoftware() == null + || tifd.getImageDescription() == null + || tifd.getCopyright() == null + || niso.getDateTimeCreated() == null + || tifd.getDateTime() == null + || tifd.getTIFFEPStandardID() == null) { + return false; + } + /* Must have either a full complement of strip tags or + * a full complement of tile tags. */ - @Override - public boolean satisfiesThisProfile (IFD ifd) - { - if (!(ifd instanceof TiffIFD)) { - return false; - } - TiffIFD tifd = (TiffIFD) ifd; + if (!(niso.getStripOffsets() != null + && niso.getRowsPerStrip() != NisoImageMetadata.NULL + && niso.getStripByteCounts() != null) + && !(niso.getTileWidth() != NisoImageMetadata.NULL + && niso.getTileLength() != NisoImageMetadata.NULL + && niso.getTileOffsets() != null + && niso.getTileByteCounts() != null)) { + return false; + } - /* Check required tags. */ - NisoImageMetadata niso = tifd.getNisoImageMetadata (); - if (niso.getImageWidth () == NisoImageMetadata.NULL || - niso.getImageLength () == NisoImageMetadata.NULL || - niso.getBitsPerSample () == null || - tifd.getImageDescription () == null || - niso.getXSamplingFrequency () == null || - niso.getYSamplingFrequency () == null || - niso.getScannerManufacturer () == null || - (niso.getScannerModelName () == null && - niso.getScannerModelNumber () == null) || - niso.getScanningSoftware() == null || - tifd.getImageDescription () == null || - tifd.getCopyright () == null || - niso.getDateTimeCreated () == null || - tifd.getDateTime () == null || - tifd.getTIFFEPStandardID () == null) { - return false; - } - /* Must have either a full complement of strip tags or - * a full complement of tile tags. - */ - if (!(niso.getStripOffsets () != null && - niso.getRowsPerStrip () != NisoImageMetadata.NULL && - niso.getStripByteCounts () != null) && - !(niso.getTileWidth () != NisoImageMetadata.NULL && - niso.getTileLength () != NisoImageMetadata.NULL && - niso.getTileOffsets () != null && - niso.getTileByteCounts () != null)) { - return false; - } + long subfile = tifd.getNewSubfileType(); + if (subfile != 0 && subfile != 1) { + return false; + } - long subfile = tifd.getNewSubfileType (); - if (subfile != 0 && subfile != 1) { - return false; - } + if (!satisfiesResolutionUnit(tifd, new int[] {1, 2, 3})) { + return false; + } - if (!satisfiesResolutionUnit (tifd, new int [] {1, 2, 3} )) { - return false; - } + if (!satisfiesOrientation(tifd, new int[] {NisoImageMetadata.NULL, 1, 3, 6, 8, 9})) { + return false; + } - if (!satisfiesOrientation (tifd, new int [] {NisoImageMetadata.NULL, - 1, 3, 6, 8, 9} )) { - return false; - } + int pInterpretation = niso.getColorSpace(); + if (!(pInterpretation == 1 + || pInterpretation == 2 + || pInterpretation == 6 + || pInterpretation == 32803 + || pInterpretation > 32767)) { + return false; + } - int pInterpretation = niso.getColorSpace (); - if (!(pInterpretation == 1 || - pInterpretation == 2 || - pInterpretation == 6 || - pInterpretation == 32803 || - pInterpretation > 32767)) { - return false; - } + int config = niso.getPlanarConfiguration(); + if (config != 1 && config != 2) { + return false; + } - int config = niso.getPlanarConfiguration (); - if (config != 1 && config != 2) { - return false; - } + int method = niso.getSensor(); + if (method == NisoImageMetadata.NULL || method < 0 || method > 8) { + return false; + } - int method = niso.getSensor (); - if (method == NisoImageMetadata.NULL || method < 0 || method > 8) { - return false; - } + if (pInterpretation == 32803) { + if (tifd.getCFARepeatPatternDim() == null) { + return false; + } + if (tifd.getCFAPattern() == null) { + return false; + } + } - if (pInterpretation == 32803) { - if (tifd.getCFARepeatPatternDim () == null) { - return false; - } - if (tifd.getCFAPattern () == null) { - return false; - } - } + /* Make sure PhotometricInterpretation and SamplesPerPixel + * are compatible. + */ + int samplesPerPixel = niso.getSamplesPerPixel(); + if ((pInterpretation == 1 || pInterpretation == 32803) && samplesPerPixel != 1) {} + if ((pInterpretation == 2 || pInterpretation == 6) && samplesPerPixel != 3) {} + if (pInterpretation == 6 + && (niso.getYCbCrCoefficients() == null + || niso.getYCbCrSubSampling() == null + || niso.getYCbCrPositioning() == NisoImageMetadata.NULL + || niso.getReferenceBlackWhite() == null)) { + return false; + } + // meteringMode and exposureProgram checks deleted, per Bugzilla #33 - /* Make sure PhotometricInterpretation and SamplesPerPixel - * are compatible. - */ - int samplesPerPixel = niso.getSamplesPerPixel (); - if ((pInterpretation == 1 || pInterpretation == 32803) && - samplesPerPixel != 1) { - } - if ((pInterpretation == 2 || pInterpretation == 6) && - samplesPerPixel != 3) { - } - if (pInterpretation == 6 && (niso.getYCbCrCoefficients() == null || - niso.getYCbCrSubSampling () == null || - niso.getYCbCrPositioning () == NisoImageMetadata.NULL || - niso.getReferenceBlackWhite () == null)) { - return false; - } - // meteringMode and exposureProgram checks deleted, per Bugzilla #33 - - int compression = niso.getCompressionScheme (); - // Corrected 6-Jan-04 per Bugzilla #33 - if (compression != NisoImageMetadata.NULL && - !(compression == 1 || compression == 7 || - compression > 32767)) { - return false; - } - return true; + int compression = niso.getCompressionScheme(); + // Corrected 6-Jan-04 per Bugzilla #33 + if (compression != NisoImageMetadata.NULL + && !(compression == 1 || compression == 7 || compression > 32767)) { + return false; } + return true; + } } diff --git a/jhove-modules/tiff-hul/src/main/java/edu/harvard/hul/ois/jhove/module/tiff/TiffProfileExif.java b/jhove-modules/tiff-hul/src/main/java/edu/harvard/hul/ois/jhove/module/tiff/TiffProfileExif.java index af05de945..a6f63bfbc 100644 --- a/jhove-modules/tiff-hul/src/main/java/edu/harvard/hul/ois/jhove/module/tiff/TiffProfileExif.java +++ b/jhove-modules/tiff-hul/src/main/java/edu/harvard/hul/ois/jhove/module/tiff/TiffProfileExif.java @@ -1,124 +1,114 @@ -/********************************************************************** - * Jhove - JSTOR/Harvard Object Validation Environment - * Copyright 2003 by JSTOR and the President and Fellows of Harvard College - **********************************************************************/ - +/** + * ******************************************************************** Jhove - JSTOR/Harvard Object + * Validation Environment Copyright 2003 by JSTOR and the President and Fellows of Harvard College + * ******************************************************************** + */ package edu.harvard.hul.ois.jhove.module.tiff; import edu.harvard.hul.ois.jhove.*; - /** - * Profile checker for Exif. This applies to the main IFD - * of the file. To satisfy the Exif profile, the thumbnail - * IFD must also satisfy TiffProfileExifThumb. - * - * @see TiffProfileExifThumb - * @see TiffProfileExifIFD + * Profile checker for Exif. This applies to the main IFD of the file. To satisfy the Exif profile, + * the thumbnail IFD must also satisfy TiffProfileExifThumb. + * + * @see TiffProfileExifThumb + * @see TiffProfileExifIFD */ -public final class TiffProfileExif extends TiffProfile -{ - /* The profile text depends on the version. */ - private static final String[] EXIF_VERSIONS = { "0200", "0210", "0220", "0221", "0230" }; +public final class TiffProfileExif extends TiffProfile { + /* The profile text depends on the version. */ + private static final String[] EXIF_VERSIONS = {"0200", "0210", "0220", "0221", "0230"}; + + private static final String[] PROFILE_TEXTS = { + "Exif 2.0", + "Exif 2.1 (JEIDA-49-1998)", + "Exif 2.2 (JEITA CP-3451)", + "Exif 2.21 (JEITA CP-3451A)", + "Exif 2.3 (JEITA CP-3451C)" + }; - private static final String[] PROFILE_TEXTS = { "Exif 2.0", - "Exif 2.1 (JEIDA-49-1998)", - "Exif 2.2 (JEITA CP-3451)", - "Exif 2.21 (JEITA CP-3451A)", - "Exif 2.3 (JEITA CP-3451C)" - }; + private TiffProfileExifIFD _exifIFDProfile; - private TiffProfileExifIFD _exifIFDProfile; - - public TiffProfileExif () - { - super (); - _exifIFDProfile = new TiffProfileExifIFD (); + public TiffProfileExif() { + super(); + _exifIFDProfile = new TiffProfileExifIFD(); + } + + /** + * Returns true if the IFD satisfies the requirements of an Exif profile. See the Exif + * specification for details. + */ + @Override + public boolean satisfiesThisProfile(IFD ifd) { + if (!(ifd instanceof TiffIFD)) { + return false; } + TiffIFD tifd = (TiffIFD) ifd; - /** - * Returns true if the IFD satisfies the requirements of an - * Exif profile. See the Exif specification for details. - */ - @Override - public boolean satisfiesThisProfile (IFD ifd) - { - if (!(ifd instanceof TiffIFD)) { - return false; - } - TiffIFD tifd = (TiffIFD) ifd; + /* Check required tags. */ + NisoImageMetadata niso = tifd.getNisoImageMetadata(); + if (niso.getXSamplingFrequency() == null || niso.getYSamplingFrequency() == null) { + return false; + } - /* Check required tags. */ - NisoImageMetadata niso = tifd.getNisoImageMetadata (); - if (niso.getXSamplingFrequency () == null || - niso.getYSamplingFrequency () == null) { - return false; + if (!tifd.isFirst() || tifd.getTheExifIFD() == null) { + // The first image is not a Exif JPEG => the compression should be verify + if (satisfiesCompression(tifd, 1)) { + if (niso.getImageWidth() == NisoImageMetadata.NULL + || niso.getImageLength() == NisoImageMetadata.NULL + || niso.getStripOffsets() == null + || niso.getRowsPerStrip() == NisoImageMetadata.NULL + || niso.getStripByteCounts() == null) { + return false; } - - if (!tifd.isFirst () || tifd.getTheExifIFD () == null) { - // The first image is not a Exif JPEG => the compression should be verify - if (satisfiesCompression (tifd, 1)) { - if (niso.getImageWidth () == NisoImageMetadata.NULL || - niso.getImageLength () == NisoImageMetadata.NULL || - niso.getStripOffsets () == null || - niso.getRowsPerStrip () == NisoImageMetadata.NULL || - niso.getStripByteCounts () == null) { - return false; - } - if (niso.getSamplesPerPixel () != 3) { - return false; - } - /* BitsPerSample must be [8, 8, 8] */ - int[] bps = niso.getBitsPerSample (); - if (bps == null || bps.length < 3 || bps[0] != 8 || bps[1] != 8 || - bps[2] != 8) { - return false; - } - int pInterpretation = niso.getColorSpace (); - if (!(pInterpretation == 2 || pInterpretation == 6)) { - return false; - } - if (pInterpretation == 6 && ( - niso.getYCbCrSubSampling () == null || - niso.getYCbCrPositioning () == NisoImageMetadata.NULL)) { - return false; - } - } - else { - // If the compression isn't 1, then the JPEGInterchangeFormat - // tag must be present, but other requirements are lifted. - if (tifd.getJpegInterchangeFormat() == NisoImageMetadata.NULL) { - return false; - } - } + if (niso.getSamplesPerPixel() != 3) { + return false; } + /* BitsPerSample must be [8, 8, 8] */ + int[] bps = niso.getBitsPerSample(); + if (bps == null || bps.length < 3 || bps[0] != 8 || bps[1] != 8 || bps[2] != 8) { + return false; + } + int pInterpretation = niso.getColorSpace(); + if (!(pInterpretation == 2 || pInterpretation == 6)) { + return false; + } + if (pInterpretation == 6 + && (niso.getYCbCrSubSampling() == null + || niso.getYCbCrPositioning() == NisoImageMetadata.NULL)) { + return false; + } + } else { + // If the compression isn't 1, then the JPEGInterchangeFormat + // tag must be present, but other requirements are lifted. + if (tifd.getJpegInterchangeFormat() == NisoImageMetadata.NULL) { + return false; + } + } + } + if (!satisfiesResolutionUnit(tifd, new int[] {2, 3})) { + return false; + } - - - if (!satisfiesResolutionUnit (tifd, new int [] {2, 3} )) { - return false; - } - - /* for the first IFD only, there must be an Exif subifd */ - if (tifd.isFirst ()) { - ExifIFD eifd = tifd.getTheExifIFD (); - if (eifd == null) { - return false; - } - // The Exif IFD must satisfy the profile requirements - if (!_exifIFDProfile.satisfiesThisProfile(eifd)) { - return false; - } - String version = eifd.getExifVersion (); - for (int idx = 0; idx < EXIF_VERSIONS.length; idx++) { - if (EXIF_VERSIONS[idx].equals (version)) { - _profileText = PROFILE_TEXTS[idx]; - break; - } - } + /* for the first IFD only, there must be an Exif subifd */ + if (tifd.isFirst()) { + ExifIFD eifd = tifd.getTheExifIFD(); + if (eifd == null) { + return false; + } + // The Exif IFD must satisfy the profile requirements + if (!_exifIFDProfile.satisfiesThisProfile(eifd)) { + return false; + } + String version = eifd.getExifVersion(); + for (int idx = 0; idx < EXIF_VERSIONS.length; idx++) { + if (EXIF_VERSIONS[idx].equals(version)) { + _profileText = PROFILE_TEXTS[idx]; + break; } - - return true; + } } + + return true; + } } diff --git a/jhove-modules/tiff-hul/src/main/java/edu/harvard/hul/ois/jhove/module/tiff/TiffProfileExifIFD.java b/jhove-modules/tiff-hul/src/main/java/edu/harvard/hul/ois/jhove/module/tiff/TiffProfileExifIFD.java index 245204346..a391d12bd 100644 --- a/jhove-modules/tiff-hul/src/main/java/edu/harvard/hul/ois/jhove/module/tiff/TiffProfileExifIFD.java +++ b/jhove-modules/tiff-hul/src/main/java/edu/harvard/hul/ois/jhove/module/tiff/TiffProfileExifIFD.java @@ -1,71 +1,66 @@ -/********************************************************************** - * Jhove - JSTOR/Harvard Object Validation Environment - * Copyright 2004 by JSTOR and the President and Fellows of Harvard College - **********************************************************************/ - +/** + * ******************************************************************** Jhove - JSTOR/Harvard Object + * Validation Environment Copyright 2004 by JSTOR and the President and Fellows of Harvard College + * ******************************************************************** + */ package edu.harvard.hul.ois.jhove.module.tiff; /** - * Profile checker for the Exif IFD of a TIFF file which potentially - * matches the TIFF profile. This is called from TiffProfileExif - * to check the Exif IFD. + * Profile checker for the Exif IFD of a TIFF file which potentially matches the TIFF profile. This + * is called from TiffProfileExif to check the Exif IFD. * * @author Gary McGath - * */ public class TiffProfileExifIFD extends TiffProfile { - private static final String[] ACCEPTED_EXIF_VERSIONS = { "0200", "0210", - "0220", "0221", "0230" }; + private static final String[] ACCEPTED_EXIF_VERSIONS = {"0200", "0210", "0220", "0221", "0230"}; - private int _majVersion; - private int _minVersion; + private int _majVersion; + private int _minVersion; - public TiffProfileExifIFD () - { - super (); - // This isn't used directly to report a profile, so the - // profile text is irrelevant. - _profileText = null; - _majVersion = -1; - _minVersion = -1; + public TiffProfileExifIFD() { + super(); + // This isn't used directly to report a profile, so the + // profile text is irrelevant. + _profileText = null; + _majVersion = -1; + _minVersion = -1; + } + + /** + * Returns true if the IFD satisfies the requirements of an Exif profile. See the Exif + * specification for details. + */ + @Override + public boolean satisfiesThisProfile(IFD ifd) { + if (!(ifd instanceof ExifIFD)) { + return false; + } + ExifIFD eifd = (ExifIFD) ifd; + String version = eifd.getExifVersion(); + for (String acceptedVersion : ACCEPTED_EXIF_VERSIONS) { + if (acceptedVersion.equals(version)) { + _majVersion = Integer.parseInt(version.substring(0, 2)); + _minVersion = Integer.parseInt(version.substring(2, 4)); + break; + } + } + if (_majVersion == -1) { + // Other versions aren't accepted + return false; } - /** - * Returns true if the IFD satisfies the requirements of an - * Exif profile. See the Exif specification for details. - */ - @Override - public boolean satisfiesThisProfile (IFD ifd) - { - if (!(ifd instanceof ExifIFD)) { - return false; - } - ExifIFD eifd = (ExifIFD) ifd; - String version = eifd.getExifVersion (); - for (String acceptedVersion : ACCEPTED_EXIF_VERSIONS) { - if (acceptedVersion.equals (version)) { - _majVersion = Integer.parseInt(version.substring(0, 2)); - _minVersion = Integer.parseInt(version.substring(2, 4)); - break; - } - } - if (_majVersion == -1) { - // Other versions aren't accepted - return false; - } - - if (!("0100".equals(eifd.getFlashpixVersion ()))) { - return false; - } - int colspc = eifd.getColorspace (); - return !(colspc != 1 && colspc != 65535); + if (!("0100".equals(eifd.getFlashpixVersion()))) { + return false; } + int colspc = eifd.getColorspace(); + return !(colspc != 1 && colspc != 65535); + } - public int getMajorVersion() { - return _majVersion; - } + public int getMajorVersion() { + return _majVersion; + } - public int getMinorVersion() { - return _minVersion; - } + public int getMinorVersion() { + return _minVersion; + } } diff --git a/jhove-modules/tiff-hul/src/main/java/edu/harvard/hul/ois/jhove/module/tiff/TiffProfileExifThumb.java b/jhove-modules/tiff-hul/src/main/java/edu/harvard/hul/ois/jhove/module/tiff/TiffProfileExifThumb.java index 71dd39bec..3ecc47c07 100644 --- a/jhove-modules/tiff-hul/src/main/java/edu/harvard/hul/ois/jhove/module/tiff/TiffProfileExifThumb.java +++ b/jhove-modules/tiff-hul/src/main/java/edu/harvard/hul/ois/jhove/module/tiff/TiffProfileExifThumb.java @@ -1,69 +1,55 @@ -/********************************************************************** - * Jhove - JSTOR/Harvard Object Validation Environment - * Copyright 2004 by JSTOR and the President and Fellows of Harvard College - **********************************************************************/ - +/** + * ******************************************************************** Jhove - JSTOR/Harvard Object + * Validation Environment Copyright 2004 by JSTOR and the President and Fellows of Harvard College + * ******************************************************************** + */ package edu.harvard.hul.ois.jhove.module.tiff; /** - * Profile checker for the thumbnail IFD of a - * TIFF file potentially meeting the TIFF profile. - * - * This doesn't go into the _profiles list of TiffIFD, - * but rather is one of two (or more?) profiles that must - * be checked to determine if the file meets the Exif - * profile. It should be called only for the "thumbnail" - * IFD, which is the second top-level IFD. - * + * Profile checker for the thumbnail IFD of a TIFF file potentially meeting the TIFF profile. * - * @author Gary McGath + *

This doesn't go into the _profiles list of TiffIFD, but rather is one of two (or more?) + * profiles that must be checked to determine if the file meets the Exif profile. It should be + * called only for the "thumbnail" IFD, which is the second top-level IFD. * - * @see TiffProfileExif + * @author Gary McGath + * @see TiffProfileExif */ public class TiffProfileExifThumb extends TiffProfile { - - /** Compression scheme of the main IFD. We need to check - * our compression against the main IFD's compression. */ - int mainCompression; - - public TiffProfileExifThumb () - { - super (); - // This isn't used directly to report a profile, so the - // profile text is irrelevant. - _profileText = null; - } - - /** - * Record the compression scheme of the main IFD; required - * for comparison. - */ - public void setMainCompression (int comp) - { - mainCompression = comp; - } + /** + * Compression scheme of the main IFD. We need to check our compression against the main IFD's + * compression. + */ + int mainCompression; + public TiffProfileExifThumb() { + super(); + // This isn't used directly to report a profile, so the + // profile text is irrelevant. + _profileText = null; + } + /** Record the compression scheme of the main IFD; required for comparison. */ + public void setMainCompression(int comp) { + mainCompression = comp; + } - /** - * Returns true if the IFD satisfies the requirements of a - * thumbnail IFD for an - * Exif profile. See the Exif specification for details. - */ - @Override - public boolean satisfiesThisProfile (IFD ifd) - { - if (!(ifd instanceof TiffIFD)) { - return false; - } - TiffIFD tifd = (TiffIFD) ifd; - if (!satisfiesCompression (tifd, new int [] {1, 6} )) { - return false; - } - // If the main IFD is uncompressed, the thumbnail must be too - - return !(mainCompression == 1 && - tifd.getNisoImageMetadata().getCompressionScheme() != 1); + /** + * Returns true if the IFD satisfies the requirements of a thumbnail IFD for an Exif profile. See + * the Exif specification for details. + */ + @Override + public boolean satisfiesThisProfile(IFD ifd) { + if (!(ifd instanceof TiffIFD)) { + return false; } + TiffIFD tifd = (TiffIFD) ifd; + if (!satisfiesCompression(tifd, new int[] {1, 6})) { + return false; + } + // If the main IFD is uncompressed, the thumbnail must be too + + return !(mainCompression == 1 && tifd.getNisoImageMetadata().getCompressionScheme() != 1); + } } diff --git a/jhove-modules/tiff-hul/src/main/java/edu/harvard/hul/ois/jhove/module/tiff/TiffProfileFXC.java b/jhove-modules/tiff-hul/src/main/java/edu/harvard/hul/ois/jhove/module/tiff/TiffProfileFXC.java index f1a90dc94..833c9ac86 100644 --- a/jhove-modules/tiff-hul/src/main/java/edu/harvard/hul/ois/jhove/module/tiff/TiffProfileFXC.java +++ b/jhove-modules/tiff-hul/src/main/java/edu/harvard/hul/ois/jhove/module/tiff/TiffProfileFXC.java @@ -1,140 +1,128 @@ -/********************************************************************** - * Jhove - JSTOR/Harvard Object Validation Environment - * Copyright 2004 by JSTOR and the President and Fellows of Harvard College - **********************************************************************/ - +/** + * ******************************************************************** Jhove - JSTOR/Harvard Object + * Validation Environment Copyright 2004 by JSTOR and the President and Fellows of Harvard College + * ******************************************************************** + */ package edu.harvard.hul.ois.jhove.module.tiff; import edu.harvard.hul.ois.jhove.*; /** + * Profile checker for TIFF FX, Profile C (Baseline Color). * - * Profile checker for TIFF FX, Profile C (Baseline Color). - * - * Image data content is not checked for profile conformance. - * Only tags are checked. - * - * @author Gary McGath + *

Image data content is not checked for profile conformance. Only tags are checked. * + * @author Gary McGath */ public class TiffProfileFXC extends TiffFXBase { - /** - * Constructor. - */ - public TiffProfileFXC () - { - super (); - _profileText = "TIFF-FX (Profile C)"; - _mimeClass = MIME_FX; - } + /** Constructor. */ + public TiffProfileFXC() { + super(); + _profileText = "TIFF-FX (Profile C)"; + _mimeClass = MIME_FX; + } + /** + * Returns true if the IFD satisfies the requirements of a TIFF/FX C profile. See the TIFF/FX + * specification for details. + * + * @param ifd: IDF object + * @return true if it satifies the profile + */ + @Override + public boolean satisfiesThisProfile(IFD ifd) { + if (!(ifd instanceof TiffIFD)) { + return false; + } + TiffIFD tifd = (TiffIFD) ifd; + if (!satisfiesClass(tifd)) { + return false; + } + if (!satisfiesImageWidth( + tifd, + new int[] { + 864, 1024, 1216, 1728, 2048, 2432, + 2592, 3072, 3456, 3648, 4096, 4864 + })) { + return false; + } - /** - * Returns true if the IFD satisfies the requirements of a - * TIFF/FX C profile. See the TIFF/FX specification for - * details. - * - * @param ifd: IDF object - * - * @return true if it satifies the profile - */ - @Override - public boolean satisfiesThisProfile(IFD ifd) - { - if (!(ifd instanceof TiffIFD)) { - return false; - } - TiffIFD tifd = (TiffIFD) ifd; - if (!satisfiesClass (tifd)) { - return false; - } - if (!satisfiesImageWidth (tifd, new int[] - {864, 1024, 1216, 1728, 2048, 2432, - 2592, 3072, 3456, 3648, 4096, 4864} )) { - return false; - } + if (!satisfiesSamplesPerPixel(tifd, new int[] {1, 3})) { + return false; + } - if (!satisfiesSamplesPerPixel(tifd, new int[] {1, 3})) { - return false; - } + if (!satisfiesCompression(tifd, 7)) { + return false; + } - if (!satisfiesCompression (tifd, 7)) { - return false; - } + if (!satisfiesPhotometricInterpretation(tifd, 10)) { + return false; + } + if (!satisfiesResolutionUnit(tifd, new int[] {2, 3, NisoImageMetadata.NULL})) { + return false; + // NOTE: RFC 2301 (1998) allows 2 or 3, but + // the 2003 working draft allows only 2 (inch). + // Watch for change. + } + if (!satisfiesSamplesPerPixel(tifd, new int[] {1, 3})) { + return false; + } + // XResolution must be one of the specified values + // and equal YResolution + if (!satisfiesXResolution(tifd, new int[] {100, 200, 300, 400})) { + return false; + } + if (!satisfiesFillOrder(tifd, new int[] {1, 2})) { + return false; + } + NisoImageMetadata niso = tifd.getNisoImageMetadata(); + long xRes = niso.getXSamplingFrequency().toLong(); + if (xRes != niso.getYSamplingFrequency().toLong()) { + return false; + } + if (niso.getSamplingFrequencyUnit() == 3) { + // Convert from units/cm to units/inch, with rounding + xRes = perCMtoPerInch((int) xRes); + } + int bps = niso.getBitsPerSample()[0]; + if (bps != 8 && bps != 12) { + // NOTE: RFC 2301 (1998) allows 8 or 12 bits per + // sample, but the 2003 working draft allows only 8. + // Watch for changes. + return false; + } - if (!satisfiesPhotometricInterpretation(tifd, 10)) { - return false; - } - if (!satisfiesResolutionUnit (tifd, - new int[] {2, 3, NisoImageMetadata.NULL} )) { - return false; - // NOTE: RFC 2301 (1998) allows 2 or 3, but - // the 2003 working draft allows only 2 (inch). - // Watch for change. + // Check if image width is suitable to resolution + int wid = (int) niso.getImageWidth(); + switch ((int) xRes) { + case 100: + if (wid != 864 && wid != 1024 & wid != 1216) { + return false; } - if (!satisfiesSamplesPerPixel(tifd, - new int[] {1, 3} )) { - return false; + break; + case 200: + if (wid != 1728 && wid != 2048 & wid != 2432) { + return false; } - // XResolution must be one of the specified values - // and equal YResolution - if (!satisfiesXResolution(tifd, - new int[] {100, 200, 300, 400} )) { - return false; - } - if (!satisfiesFillOrder (tifd, - new int[] {1, 2} )) { - return false; - } - NisoImageMetadata niso = tifd.getNisoImageMetadata (); - long xRes = niso.getXSamplingFrequency ().toLong (); - if (xRes != niso.getYSamplingFrequency ().toLong ()) { - return false; - } - if (niso.getSamplingFrequencyUnit() == 3) { - // Convert from units/cm to units/inch, with rounding - xRes = perCMtoPerInch ((int) xRes); - } - int bps = niso.getBitsPerSample ()[0]; - if (bps != 8 && bps != 12) { - // NOTE: RFC 2301 (1998) allows 8 or 12 bits per - // sample, but the 2003 working draft allows only 8. - // Watch for changes. - return false; - } - - // Check if image width is suitable to resolution - int wid = (int) niso.getImageWidth (); - switch ((int) xRes) { - case 100: - if (wid != 864 && wid != 1024 & wid != 1216) { - return false; - } - break; - case 200: - if (wid != 1728 && wid != 2048 & wid != 2432) { - return false; - } - break; + break; - case 300: - if (wid != 2592 && wid != 3072 & wid != 3648) { - return false; - } - break; + case 300: + if (wid != 2592 && wid != 3072 & wid != 3648) { + return false; + } + break; - case 400: - if (wid != 3456 && wid != 4096 & wid != 4864) { - return false; - } - break; - default: - break; + case 400: + if (wid != 3456 && wid != 4096 & wid != 4864) { + return false; } - // By my best reading, the colormap is needed only - // if the Indexed value is 1. - return !(tifd.getIndexed() == 1 && niso.getColormapRedValue () == null); // passed all tests + break; + default: + break; } - + // By my best reading, the colormap is needed only + // if the Indexed value is 1. + return !(tifd.getIndexed() == 1 && niso.getColormapRedValue() == null); // passed all tests + } } diff --git a/jhove-modules/tiff-hul/src/main/java/edu/harvard/hul/ois/jhove/module/tiff/TiffProfileFXF.java b/jhove-modules/tiff-hul/src/main/java/edu/harvard/hul/ois/jhove/module/tiff/TiffProfileFXF.java index dfed33062..d5231c3a8 100644 --- a/jhove-modules/tiff-hul/src/main/java/edu/harvard/hul/ois/jhove/module/tiff/TiffProfileFXF.java +++ b/jhove-modules/tiff-hul/src/main/java/edu/harvard/hul/ois/jhove/module/tiff/TiffProfileFXF.java @@ -1,99 +1,86 @@ -/********************************************************************** - * Jhove - JSTOR/Harvard Object Validation Environment - * Copyright 2004 by JSTOR and the President and Fellows of Harvard College - **********************************************************************/ - +/** + * ******************************************************************** Jhove - JSTOR/Harvard Object + * Validation Environment Copyright 2004 by JSTOR and the President and Fellows of Harvard College + * ******************************************************************** + */ package edu.harvard.hul.ois.jhove.module.tiff; import edu.harvard.hul.ois.jhove.*; /** + * Profile checker for TIFF FX, Profile F, aka TIFF-F. This supersedes the Class F profile + * (TiffProfileClassF), which will be deprecated and removed from the Jhove application. * - * Profile checker for TIFF FX, Profile F, aka TIFF-F. - * This supersedes the Class F profile (TiffProfileClassF), - * which will be deprecated and removed from the Jhove - * application. - * - * Image data content is not checked for profile conformance. - * Only tags are checked. - * - * @author Gary McGath + *

Image data content is not checked for profile conformance. Only tags are checked. * + * @author Gary McGath */ public class TiffProfileFXF extends TiffFXBase { - /** - * Constructor. - */ - public TiffProfileFXF () - { - super (); - _profileText = "TIFF-FX (Profile F)"; - _mimeClass = MIME_FX; - } - - /** - * Returns true if the IFD satisfies the requirements of a - * TIFF/FX S profile. See the TIFF/FX specification for - * details. - */ - @Override - public boolean satisfiesThisProfile(IFD ifd) { - if (!(ifd instanceof TiffIFD)) { - return false; - } - TiffIFD tifd = (TiffIFD) ifd; - if (!satisfiesClass (tifd)) { - return false; - } - NisoImageMetadata niso = tifd.getNisoImageMetadata (); - int[] bps = niso.getBitsPerSample (); - if (bps[0] != 1) { - return false; - } - if (!satisfiesCompression (tifd, new int[] {3, 4} )) { - return false; - } - if (!satisfiesFillOrder (tifd, - new int[] {1, 2} )) { - return false; - } - // We've already established that if the compression - // scheme is 3, T4Options exists. But we must establish - // that if it's 4, T6Options exists and has a value of 0. - if (niso.getCompressionScheme() == 4 && tifd.getT6Options() != 0) { - return false; - } + /** Constructor. */ + public TiffProfileFXF() { + super(); + _profileText = "TIFF-FX (Profile F)"; + _mimeClass = MIME_FX; + } - // XResolution, YResolution, and ImageWidth have codependencies. - boolean xywOK = false; // guilty till proven innocent - long xRes = niso.getXSamplingFrequency ().toLong(); - long yRes = niso.getYSamplingFrequency ().toLong(); - if (niso.getSamplingFrequencyUnit() == 3) { - // Convert from units/cm to units/inch, with rounding - xRes = perCMtoPerInch ((int) xRes); - yRes = perCMtoPerInch ((int) yRes); - } - long wid = niso.getImageWidth(); - if (((xRes == 200 && yRes == 100) || - (xRes == 204 && yRes == 98) || - (xRes == 200 && yRes == 200) || - (xRes == 204 && yRes == 196) || - (xRes == 204 && yRes == 391)) && - (wid == 1728 || wid == 2048 || wid == 2432)) { - xywOK = true; - } - if (xRes == 300 && yRes == 300 && - (wid == 2592 || wid == 3072 || wid == 3648)) { - xywOK = true; - } - if (((xRes == 408 && yRes == 391) || - (xRes == 400 && yRes == 400)) - && (wid == 3456 || wid == 4096 || wid == 4864)) { - xywOK = true; - } - // passed all tests - return xywOK; + /** + * Returns true if the IFD satisfies the requirements of a TIFF/FX S profile. See the TIFF/FX + * specification for details. + */ + @Override + public boolean satisfiesThisProfile(IFD ifd) { + if (!(ifd instanceof TiffIFD)) { + return false; + } + TiffIFD tifd = (TiffIFD) ifd; + if (!satisfiesClass(tifd)) { + return false; + } + NisoImageMetadata niso = tifd.getNisoImageMetadata(); + int[] bps = niso.getBitsPerSample(); + if (bps[0] != 1) { + return false; + } + if (!satisfiesCompression(tifd, new int[] {3, 4})) { + return false; + } + if (!satisfiesFillOrder(tifd, new int[] {1, 2})) { + return false; + } + // We've already established that if the compression + // scheme is 3, T4Options exists. But we must establish + // that if it's 4, T6Options exists and has a value of 0. + if (niso.getCompressionScheme() == 4 && tifd.getT6Options() != 0) { + return false; } + // XResolution, YResolution, and ImageWidth have codependencies. + boolean xywOK = false; // guilty till proven innocent + long xRes = niso.getXSamplingFrequency().toLong(); + long yRes = niso.getYSamplingFrequency().toLong(); + if (niso.getSamplingFrequencyUnit() == 3) { + // Convert from units/cm to units/inch, with rounding + xRes = perCMtoPerInch((int) xRes); + yRes = perCMtoPerInch((int) yRes); + } + long wid = niso.getImageWidth(); + if (((xRes == 200 && yRes == 100) + || (xRes == 204 && yRes == 98) + || (xRes == 200 && yRes == 200) + || (xRes == 204 && yRes == 196) + || (xRes == 204 && yRes == 391)) + && (wid == 1728 || wid == 2048 || wid == 2432)) { + xywOK = true; + } + if (xRes == 300 && yRes == 300 && (wid == 2592 || wid == 3072 || wid == 3648)) { + xywOK = true; + } + if (((xRes == 408 && yRes == 391) || (xRes == 400 && yRes == 400)) + && (wid == 3456 || wid == 4096 || wid == 4864)) { + xywOK = true; + } + // passed all tests + return xywOK; + } } diff --git a/jhove-modules/tiff-hul/src/main/java/edu/harvard/hul/ois/jhove/module/tiff/TiffProfileFXJ.java b/jhove-modules/tiff-hul/src/main/java/edu/harvard/hul/ois/jhove/module/tiff/TiffProfileFXJ.java index cbd825a1f..ac006ed30 100644 --- a/jhove-modules/tiff-hul/src/main/java/edu/harvard/hul/ois/jhove/module/tiff/TiffProfileFXJ.java +++ b/jhove-modules/tiff-hul/src/main/java/edu/harvard/hul/ois/jhove/module/tiff/TiffProfileFXJ.java @@ -1,101 +1,90 @@ -/********************************************************************** - * Jhove - JSTOR/Harvard Object Validation Environment - * Copyright 2004 by JSTOR and the President and Fellows of Harvard College - **********************************************************************/ - +/** + * ******************************************************************** Jhove - JSTOR/Harvard Object + * Validation Environment Copyright 2004 by JSTOR and the President and Fellows of Harvard College + * ******************************************************************** + */ package edu.harvard.hul.ois.jhove.module.tiff; import edu.harvard.hul.ois.jhove.*; /** - * Profile checker for TIFF FX, Profile J (lossless JBIG). + * Profile checker for TIFF FX, Profile J (lossless JBIG). * - * Image data content is not checked for profile conformance. - * Only tags are checked. + *

Image data content is not checked for profile conformance. Only tags are checked. * * @author Gary McGath - * */ public class TiffProfileFXJ extends TiffFXBase { - /** - * Constructor. - */ - public TiffProfileFXJ () - { - super (); - _profileText = "TIFF-FX (Profile J)"; - _mimeClass = MIME_FX; - } - + /** Constructor. */ + public TiffProfileFXJ() { + super(); + _profileText = "TIFF-FX (Profile J)"; + _mimeClass = MIME_FX; + } - /** - * Returns true if the IFD satisfies the requirements of a - * TIFF/FX J profile. See the TIFF/FX specification for - * details. - */ - @Override - public boolean satisfiesThisProfile(IFD ifd) { - if (!(ifd instanceof TiffIFD)) { - return false; - } - TiffIFD tifd = (TiffIFD) ifd; - if (!satisfiesClass (tifd)) { - return false; - } - NisoImageMetadata niso = tifd.getNisoImageMetadata (); - int[] bps = niso.getBitsPerSample (); - if (bps[0] != 1) { - return false; - } - int cmp = niso.getCompressionScheme(); - if (cmp != 9) { - return false; - } - if (!satisfiesFillOrder (tifd, - new int[] {1, 2} )) { - return false; - // RFC 2301 (1998) is internally inconsistent about - // whether a FillOrder of 1 is permitted. The latest - // working draft allows a FillOrder of 1, so I've - // resolved the conflict in favor of that interpretation. - } - // We've already established that if the compression - // scheme is 3, T4Options exists. But we must establish - // that if it's 4, T6Options exists and has a value of 0. - if (cmp == 4 && tifd.getT6Options () != 0) { - return false; - } - - // XResolution, YResolution, and ImageWidth have codependencies. - boolean xywOK = false; // guilty till proven innocent - long xRes = niso.getXSamplingFrequency ().toLong(); - long yRes = niso.getYSamplingFrequency ().toLong(); - if (niso.getSamplingFrequencyUnit() == 3) { - // Convert from units/cm to units/inch, with rounding - xRes = perCMtoPerInch ((int) xRes); - yRes = perCMtoPerInch ((int) yRes); - } - long wid = niso.getImageWidth(); - if (((xRes == 200 && yRes == 100) || - (xRes == 204 && yRes == 98) || - (xRes == 200 && yRes == 200) || - (xRes == 204 && yRes == 196) || - (xRes == 204 && yRes == 391)) && - (wid == 1728 || wid == 2048 || wid == 2432)) { - xywOK = true; - } - if (xRes == 300 && yRes == 300 && - (wid == 2592 || wid == 3072 || wid == 3648)) { - xywOK = true; - } - if (((xRes == 408 && yRes == 391) || - (xRes == 400 && yRes == 400)) && - (wid == 3456 || wid == 4096 || wid == 4864)) { - xywOK = true; - } - // passed all tests - return xywOK; + /** + * Returns true if the IFD satisfies the requirements of a TIFF/FX J profile. See the TIFF/FX + * specification for details. + */ + @Override + public boolean satisfiesThisProfile(IFD ifd) { + if (!(ifd instanceof TiffIFD)) { + return false; + } + TiffIFD tifd = (TiffIFD) ifd; + if (!satisfiesClass(tifd)) { + return false; + } + NisoImageMetadata niso = tifd.getNisoImageMetadata(); + int[] bps = niso.getBitsPerSample(); + if (bps[0] != 1) { + return false; + } + int cmp = niso.getCompressionScheme(); + if (cmp != 9) { + return false; + } + if (!satisfiesFillOrder(tifd, new int[] {1, 2})) { + return false; + // RFC 2301 (1998) is internally inconsistent about + // whether a FillOrder of 1 is permitted. The latest + // working draft allows a FillOrder of 1, so I've + // resolved the conflict in favor of that interpretation. + } + // We've already established that if the compression + // scheme is 3, T4Options exists. But we must establish + // that if it's 4, T6Options exists and has a value of 0. + if (cmp == 4 && tifd.getT6Options() != 0) { + return false; } + // XResolution, YResolution, and ImageWidth have codependencies. + boolean xywOK = false; // guilty till proven innocent + long xRes = niso.getXSamplingFrequency().toLong(); + long yRes = niso.getYSamplingFrequency().toLong(); + if (niso.getSamplingFrequencyUnit() == 3) { + // Convert from units/cm to units/inch, with rounding + xRes = perCMtoPerInch((int) xRes); + yRes = perCMtoPerInch((int) yRes); + } + long wid = niso.getImageWidth(); + if (((xRes == 200 && yRes == 100) + || (xRes == 204 && yRes == 98) + || (xRes == 200 && yRes == 200) + || (xRes == 204 && yRes == 196) + || (xRes == 204 && yRes == 391)) + && (wid == 1728 || wid == 2048 || wid == 2432)) { + xywOK = true; + } + if (xRes == 300 && yRes == 300 && (wid == 2592 || wid == 3072 || wid == 3648)) { + xywOK = true; + } + if (((xRes == 408 && yRes == 391) || (xRes == 400 && yRes == 400)) + && (wid == 3456 || wid == 4096 || wid == 4864)) { + xywOK = true; + } + // passed all tests + return xywOK; + } } diff --git a/jhove-modules/tiff-hul/src/main/java/edu/harvard/hul/ois/jhove/module/tiff/TiffProfileFXL.java b/jhove-modules/tiff-hul/src/main/java/edu/harvard/hul/ois/jhove/module/tiff/TiffProfileFXL.java index 2fe3d0b68..3ef2d1a36 100644 --- a/jhove-modules/tiff-hul/src/main/java/edu/harvard/hul/ois/jhove/module/tiff/TiffProfileFXL.java +++ b/jhove-modules/tiff-hul/src/main/java/edu/harvard/hul/ois/jhove/module/tiff/TiffProfileFXL.java @@ -1,113 +1,98 @@ -/********************************************************************** - * Jhove - JSTOR/Harvard Object Validation Environment - * Copyright 2004 by JSTOR and the President and Fellows of Harvard College - **********************************************************************/ - +/** + * ******************************************************************** Jhove - JSTOR/Harvard Object + * Validation Environment Copyright 2004 by JSTOR and the President and Fellows of Harvard College + * ******************************************************************** + */ package edu.harvard.hul.ois.jhove.module.tiff; import edu.harvard.hul.ois.jhove.*; /** + * Profile checker for TIFF FX, Profile L (Lossless Color). * - * Profile checker for TIFF FX, Profile L (Lossless Color). - * - * Image data content is not checked for profile conformance. - * Only tags are checked. - * - * @author Gary McGath + *

Image data content is not checked for profile conformance. Only tags are checked. * + * @author Gary McGath */ public class TiffProfileFXL extends TiffFXBase { + /** Constructor. */ + public TiffProfileFXL() { + super(); + _profileText = "TIFF-FX (Profile L)"; + _mimeClass = MIME_FX; + } - /** - * Constructor. - */ - public TiffProfileFXL () - { - super (); - _profileText = "TIFF-FX (Profile L)"; - _mimeClass = MIME_FX; + /** + * Returns true if the IFD satisfies the requirements of a TIFF/FX L profile. See the TIFF/FX + * specification for details. + */ + @Override + public boolean satisfiesThisProfile(IFD ifd) { + if (!(ifd instanceof TiffIFD)) { + return false; + } + TiffIFD tifd = (TiffIFD) ifd; + if (!satisfiesClass(tifd)) { + return false; + } + if (!satisfiesImageWidth( + tifd, + new int[] { + 864, 1024, 1216, 1728, 2048, 2432, + 2592, 3072, 3456, 3648, 4096, 4864 + })) { + return false; } + // I can't make sense of whether compression mode 7 is + // allowed (since Profile L implementors are required to + // implement profile C) or not (since only 10 is mentioned + // under Profile L). Since the compression scheme is the + // defining characteristic of JBIG, I assume it must be 10. + if (!satisfiesCompression(tifd, 10)) { + return false; + } + if (!satisfiesPhotometricInterpretation(tifd, new int[] {2, 5, 10})) { + return false; + } + if (!satisfiesResolutionUnit(tifd, new int[] {2, 3, NisoImageMetadata.NULL})) { + // NOTE: RFC 2301 (1998) allows 2 or 3, but + // the 2003 working draft allows only 2 (inch). + // Watch for change. + return false; + } + if (!satisfiesSamplesPerPixel(tifd, new int[] {1, 3, 4})) { + return false; + } + // XResolution must be one of the specified values + // and equal YResolution + if (!satisfiesXResolution(tifd, new int[] {100, 200, 300, 400})) { + return false; + } + NisoImageMetadata niso = tifd.getNisoImageMetadata(); + if (niso.getXSamplingFrequency().toLong() != niso.getYSamplingFrequency().toLong()) { + return false; + } + if (!satisfiesIndexed(tifd, new int[] {0, 1})) { + return false; + } + if (!satisfiesFillOrder(tifd, new int[] {1, 2})) { + return false; + } + int bps = niso.getBitsPerSample()[0]; + if (bps > 16) { + // NOTE: RFC 2301 (1998) allows 1-16 bits per + // sample, but the 2003 working draft allows only 1-12. + // Watch for changes. + return false; + } - /** - * Returns true if the IFD satisfies the requirements of a - * TIFF/FX L profile. See the TIFF/FX specification for - * details. - */ - @Override - public boolean satisfiesThisProfile(IFD ifd) - { - if (!(ifd instanceof TiffIFD)) { - return false; - } - TiffIFD tifd = (TiffIFD) ifd; - if (!satisfiesClass (tifd)) { - return false; - } - if (!satisfiesImageWidth (tifd, new int[] - {864, 1024, 1216, 1728, 2048, 2432, - 2592, 3072, 3456, 3648, 4096, 4864} )) { - return false; - } - - // I can't make sense of whether compression mode 7 is - // allowed (since Profile L implementors are required to - // implement profile C) or not (since only 10 is mentioned - // under Profile L). Since the compression scheme is the - // defining characteristic of JBIG, I assume it must be 10. - if (!satisfiesCompression (tifd, 10 )) { - return false; - } - - if (!satisfiesPhotometricInterpretation(tifd, - new int[] {2, 5, 10 } )) { - return false; - } - if (!satisfiesResolutionUnit (tifd, - new int[] {2, 3, NisoImageMetadata.NULL} )) { - // NOTE: RFC 2301 (1998) allows 2 or 3, but - // the 2003 working draft allows only 2 (inch). - // Watch for change. - return false; - } - if (!satisfiesSamplesPerPixel(tifd, - new int[] {1, 3, 4} )) { - return false; - } - // XResolution must be one of the specified values - // and equal YResolution - if (!satisfiesXResolution(tifd, - new int[] {100, 200, 300, 400} )) { - return false; - } - NisoImageMetadata niso = tifd.getNisoImageMetadata (); - if (niso.getXSamplingFrequency ().toLong () != - niso.getYSamplingFrequency ().toLong ()) { - return false; - } - if (!satisfiesIndexed (tifd, new int[] {0, 1} )) { - return false; - } - if (!satisfiesFillOrder (tifd, - new int[] {1, 2} )) { - return false; - } - int bps = niso.getBitsPerSample ()[0]; - if (bps > 16) { - // NOTE: RFC 2301 (1998) allows 1-16 bits per - // sample, but the 2003 working draft allows only 1-12. - // Watch for changes. - return false; - } - - if (tifd.getIndexed() == 1 && niso.getColormapRedValue () == null) { - return false; - } - - return true; // passed all tests + if (tifd.getIndexed() == 1 && niso.getColormapRedValue() == null) { + return false; } + return true; // passed all tests + } } diff --git a/jhove-modules/tiff-hul/src/main/java/edu/harvard/hul/ois/jhove/module/tiff/TiffProfileFXM.java b/jhove-modules/tiff-hul/src/main/java/edu/harvard/hul/ois/jhove/module/tiff/TiffProfileFXM.java index fc28ddd8b..a8ebf2142 100644 --- a/jhove-modules/tiff-hul/src/main/java/edu/harvard/hul/ois/jhove/module/tiff/TiffProfileFXM.java +++ b/jhove-modules/tiff-hul/src/main/java/edu/harvard/hul/ois/jhove/module/tiff/TiffProfileFXM.java @@ -1,114 +1,100 @@ -/********************************************************************** - * Jhove - JSTOR/Harvard Object Validation Environment - * Copyright 2004 by JSTOR and the President and Fellows of Harvard College - **********************************************************************/ - +/** + * ******************************************************************** Jhove - JSTOR/Harvard Object + * Validation Environment Copyright 2004 by JSTOR and the President and Fellows of Harvard College + * ******************************************************************** + */ package edu.harvard.hul.ois.jhove.module.tiff; import edu.harvard.hul.ois.jhove.*; /** + * Profile checker for TIFF FX, Profile M (Mixed Raster Content). * - * Profile checker for TIFF FX, Profile M (Mixed Raster Content). - * - * Image data content is not checked for profile conformance. - * Only tags are checked. - * - * @author Gary McGath + *

Image data content is not checked for profile conformance. Only tags are checked. * + * @author Gary McGath */ public class TiffProfileFXM extends TiffFXBase { - /** - * Constructor. - */ - public TiffProfileFXM () - { - super (); - _profileText = "TIFF-FX (Profile M)"; - _mimeClass = MIME_FX; - } + /** Constructor. */ + public TiffProfileFXM() { + super(); + _profileText = "TIFF-FX (Profile M)"; + _mimeClass = MIME_FX; + } - /** - * Returns true if the IFD satisfies the requirements of a - * TIFF/FX M profile. See the TIFF/FX specification for - * details. - * - * Proper validation should check if the subIFDs are appropriate - * to the M profile layer scheme. However, the existing design - * of the TIFF module has almost no understanding of IFD - * hierarchies. This could be an enhancement for a future - * release. - */ - @Override - public boolean satisfiesThisProfile(IFD ifd) - { - if (!(ifd instanceof TiffIFD)) { - return false; - } - TiffIFD tifd = (TiffIFD) ifd; - if (!satisfiesClass (tifd)) { - return false; - } - NisoImageMetadata niso = tifd.getNisoImageMetadata (); - if (!satisfiesImageWidth (tifd, new int[] - {864, 1024, 1216, 1728, 2048, 2432, - 2592, 3072, 3456, 3648, 4096, 4864} )) { - return false; - } - if (!satisfiesNewSubfileType(tifd, new long[] {16, 18})) { - return false; - } - if (!satisfiesCompression (tifd, - new int[] {3, 4, 7, 9, 10})) { - return false; - // NOTE: The March 2003 draft allows a compression - // value of 1 if StripByteCounts contains a 0 - // value, i.e., there is no image data. Watch - // for changes. - } - if (!satisfiesSamplesPerPixel (tifd, - new int[] {1, 3, 4} )) { - return false; - } - if (!satisfiesResolutionUnit (tifd, - new int[] {2, 3, NisoImageMetadata.NULL} )) { - return false; - } - if (!satisfiesPhotometricInterpretation (tifd, - new int[] {0, 1, 2, 5, 10} )) { - return false; - // NOTE: The March 2003 draft allows only 0, 2 and - // 10. Watch for change. - } - if (!satisfiesFillOrder (tifd, - new int[] {1, 2} )) { - return false; - } - int bps = niso.getBitsPerSample ()[0]; - if (bps > 16) { - // NOTE: RFC 2301 (1998) allows 1-16 bits per - // sample, but the 2003 working draft allows only 1-12. - // Watch for changes. - return false; - } - int[] imgl = tifd.getImageLayer(); - if (imgl == null || imgl[0] < 1 || imgl[0] > 3) { - return false; - } - - // Can't have both StripRowCounts and RowsPerStrip - if (tifd.getStripRowCounts () != null && - niso.getRowsPerStrip () != NisoImageMetadata.NULL) { - return false; - } - // By my best reading, the colormap is needed only - // if the Indexed value is 1. - if (tifd.getIndexed() == 1 && niso.getColormapRedValue () == null) { - return false; - } + /** + * Returns true if the IFD satisfies the requirements of a TIFF/FX M profile. See the TIFF/FX + * specification for details. + * + *

Proper validation should check if the subIFDs are appropriate to the M profile layer scheme. + * However, the existing design of the TIFF module has almost no understanding of IFD hierarchies. + * This could be an enhancement for a future release. + */ + @Override + public boolean satisfiesThisProfile(IFD ifd) { + if (!(ifd instanceof TiffIFD)) { + return false; + } + TiffIFD tifd = (TiffIFD) ifd; + if (!satisfiesClass(tifd)) { + return false; + } + NisoImageMetadata niso = tifd.getNisoImageMetadata(); + if (!satisfiesImageWidth( + tifd, + new int[] { + 864, 1024, 1216, 1728, 2048, 2432, + 2592, 3072, 3456, 3648, 4096, 4864 + })) { + return false; + } + if (!satisfiesNewSubfileType(tifd, new long[] {16, 18})) { + return false; + } + if (!satisfiesCompression(tifd, new int[] {3, 4, 7, 9, 10})) { + return false; + // NOTE: The March 2003 draft allows a compression + // value of 1 if StripByteCounts contains a 0 + // value, i.e., there is no image data. Watch + // for changes. + } + if (!satisfiesSamplesPerPixel(tifd, new int[] {1, 3, 4})) { + return false; + } + if (!satisfiesResolutionUnit(tifd, new int[] {2, 3, NisoImageMetadata.NULL})) { + return false; + } + if (!satisfiesPhotometricInterpretation(tifd, new int[] {0, 1, 2, 5, 10})) { + return false; + // NOTE: The March 2003 draft allows only 0, 2 and + // 10. Watch for change. + } + if (!satisfiesFillOrder(tifd, new int[] {1, 2})) { + return false; + } + int bps = niso.getBitsPerSample()[0]; + if (bps > 16) { + // NOTE: RFC 2301 (1998) allows 1-16 bits per + // sample, but the 2003 working draft allows only 1-12. + // Watch for changes. + return false; + } + int[] imgl = tifd.getImageLayer(); + if (imgl == null || imgl[0] < 1 || imgl[0] > 3) { + return false; + } - return true; // passed all tests + // Can't have both StripRowCounts and RowsPerStrip + if (tifd.getStripRowCounts() != null && niso.getRowsPerStrip() != NisoImageMetadata.NULL) { + return false; + } + // By my best reading, the colormap is needed only + // if the Indexed value is 1. + if (tifd.getIndexed() == 1 && niso.getColormapRedValue() == null) { + return false; } + return true; // passed all tests + } } diff --git a/jhove-modules/tiff-hul/src/main/java/edu/harvard/hul/ois/jhove/module/tiff/TiffProfileFXS.java b/jhove-modules/tiff-hul/src/main/java/edu/harvard/hul/ois/jhove/module/tiff/TiffProfileFXS.java index f3e81c423..952d23e05 100644 --- a/jhove-modules/tiff-hul/src/main/java/edu/harvard/hul/ois/jhove/module/tiff/TiffProfileFXS.java +++ b/jhove-modules/tiff-hul/src/main/java/edu/harvard/hul/ois/jhove/module/tiff/TiffProfileFXS.java @@ -1,96 +1,87 @@ -/********************************************************************** - * Jhove - JSTOR/Harvard Object Validation Environment - * Copyright 2004 by JSTOR and the President and Fellows of Harvard College - **********************************************************************/ - +/** + * ******************************************************************** Jhove - JSTOR/Harvard Object + * Validation Environment Copyright 2004 by JSTOR and the President and Fellows of Harvard College + * ******************************************************************** + */ package edu.harvard.hul.ois.jhove.module.tiff; import edu.harvard.hul.ois.jhove.*; /** - * Profile checker for TIFF FX, Profile S. + * Profile checker for TIFF FX, Profile S. * - * Image data content is not checked for profile conformance. - * Only tags are checked. + *

Image data content is not checked for profile conformance. Only tags are checked. * * @author Gary McGath - * */ public class TiffProfileFXS extends TiffFXBase { - /** - * Constructor. - */ - public TiffProfileFXS () - { - super (); - _profileText = "TIFF-FX (Profile S)"; - _mimeClass = MIME_FX; + /** Constructor. */ + public TiffProfileFXS() { + super(); + _profileText = "TIFF-FX (Profile S)"; + _mimeClass = MIME_FX; + } + + /** + * Returns true if the IFD satisfies the requirements of a TIFF/FX S profile. See the TIFF/FX + * specification for details. + */ + @Override + public boolean satisfiesThisProfile(IFD ifd) { + if (!(ifd instanceof TiffIFD)) { + return false; + } + TiffIFD tifd = (TiffIFD) ifd; + if (!satisfiesClass(tifd)) { + return false; + } + + // Profile S (but not any other fax profile) requires + // "II", little-endian data. + if (ifd.isBigEndian()) { + return false; } + NisoImageMetadata niso = tifd.getNisoImageMetadata(); + int[] bps = niso.getBitsPerSample(); + if (bps[0] != 1) { + return false; + } + if (niso.getStripOffsets().length > 1) { + // Image data must be a single strip + return false; + } - /** - * Returns true if the IFD satisfies the requirements of a - * TIFF/FX S profile. See the TIFF/FX specification for - * details. - */ - @Override - public boolean satisfiesThisProfile(IFD ifd) { - if (!(ifd instanceof TiffIFD)) { - return false; - } - TiffIFD tifd = (TiffIFD) ifd; - if (!satisfiesClass (tifd)) { - return false; - } - - // Profile S (but not any other fax profile) requires - // "II", little-endian data. - if (ifd.isBigEndian()) { - return false; - } - - NisoImageMetadata niso = tifd.getNisoImageMetadata (); - int[] bps = niso.getBitsPerSample (); - if (bps[0] != 1) { - return false; - } - if (niso.getStripOffsets().length > 1) { - // Image data must be a single strip - return false; - } - - int resUnit = niso.getSamplingFrequencyUnit(); - if (resUnit != 2 && resUnit != NisoImageMetadata.NULL) { - return false; - } - - if (niso.getCompressionScheme() != 3) { - return false; - } - if (tifd.getFillOrder () != 2) { - return false; - } - if (niso.getImageWidth () != 1728) { - return false; - } - if (niso.getSamplesPerPixel () != 1) { - return false; - } - long xRes = niso.getXSamplingFrequency ().toLong(); - long yRes = niso.getYSamplingFrequency ().toLong(); - // resolution unit must be inches, so no need to - // do metric conversion - if (xRes != 200 && xRes != 204) { - return false; - } - if (yRes != 98 && yRes != 100 && - yRes != 196 && yRes != 200) { - return false; - } - long t4Opt = tifd.getT4Options (); - // passed all tests - return (t4Opt & 0X3) == 0; + int resUnit = niso.getSamplingFrequencyUnit(); + if (resUnit != 2 && resUnit != NisoImageMetadata.NULL) { + return false; } + if (niso.getCompressionScheme() != 3) { + return false; + } + if (tifd.getFillOrder() != 2) { + return false; + } + if (niso.getImageWidth() != 1728) { + return false; + } + if (niso.getSamplesPerPixel() != 1) { + return false; + } + long xRes = niso.getXSamplingFrequency().toLong(); + long yRes = niso.getYSamplingFrequency().toLong(); + // resolution unit must be inches, so no need to + // do metric conversion + if (xRes != 200 && xRes != 204) { + return false; + } + if (yRes != 98 && yRes != 100 && yRes != 196 && yRes != 200) { + return false; + } + long t4Opt = tifd.getT4Options(); + // passed all tests + return (t4Opt & 0X3) == 0; + } } diff --git a/jhove-modules/tiff-hul/src/main/java/edu/harvard/hul/ois/jhove/module/tiff/TiffProfileGeoTIFF.java b/jhove-modules/tiff-hul/src/main/java/edu/harvard/hul/ois/jhove/module/tiff/TiffProfileGeoTIFF.java index db7ffe5d2..400ae8be4 100644 --- a/jhove-modules/tiff-hul/src/main/java/edu/harvard/hul/ois/jhove/module/tiff/TiffProfileGeoTIFF.java +++ b/jhove-modules/tiff-hul/src/main/java/edu/harvard/hul/ois/jhove/module/tiff/TiffProfileGeoTIFF.java @@ -1,46 +1,37 @@ -/********************************************************************** - * Jhove - JSTOR/Harvard Object Validation Environment - * Copyright 2003 by JSTOR and the President and Fellows of Harvard College - **********************************************************************/ - +/** + * ******************************************************************** Jhove - JSTOR/Harvard Object + * Validation Environment Copyright 2003 by JSTOR and the President and Fellows of Harvard College + * ******************************************************************** + */ package edu.harvard.hul.ois.jhove.module.tiff; +/** Profile checker for GeoTIFF. */ +public final class TiffProfileGeoTIFF extends TiffProfile { + public TiffProfileGeoTIFF() { + super(); + _profileText = "Baseline GeoTIFF 1.0"; + } - -/** - * Profile checker for GeoTIFF. - */ -public final class TiffProfileGeoTIFF extends TiffProfile -{ - public TiffProfileGeoTIFF () - { - super (); - _profileText = "Baseline GeoTIFF 1.0"; + /** + * Returns true if the IFD satisfies the requirements of the the profile. See the GeoTIFF + * specification for details. + */ + @Override + public boolean satisfiesThisProfile(IFD ifd) { + if (!(ifd instanceof TiffIFD)) { + return false; } + TiffIFD tifd = (TiffIFD) ifd; - /** - * Returns true if the IFD satisfies the requirements of the - * the profile. See the GeoTIFF specification for details. - */ - @Override - public boolean satisfiesThisProfile (IFD ifd) - { - if (!(ifd instanceof TiffIFD)) { - return false; - } - TiffIFD tifd = (TiffIFD) ifd; - - if (tifd.getGeoKeyDirectoryTag () == null) { - return false; - } - - /* Exactly one of modelTiepointTag and modelTransformationTag - * must be present. */ - boolean hasModelTiepoint = - (tifd.getModelTiepointTag() != null); - boolean hasModelTransformation = - (tifd.getModelTransformationTag() != null); - return !((hasModelTiepoint && hasModelTransformation) || - (!hasModelTiepoint && !hasModelTransformation)); + if (tifd.getGeoKeyDirectoryTag() == null) { + return false; } + + /* Exactly one of modelTiepointTag and modelTransformationTag + * must be present. */ + boolean hasModelTiepoint = (tifd.getModelTiepointTag() != null); + boolean hasModelTransformation = (tifd.getModelTransformationTag() != null); + return !((hasModelTiepoint && hasModelTransformation) + || (!hasModelTiepoint && !hasModelTransformation)); + } } diff --git a/jhove-modules/tiff-hul/src/main/java/edu/harvard/hul/ois/jhove/module/tiff/TiffProfilePagemaker6.java b/jhove-modules/tiff-hul/src/main/java/edu/harvard/hul/ois/jhove/module/tiff/TiffProfilePagemaker6.java index ab1145cab..580fea833 100644 --- a/jhove-modules/tiff-hul/src/main/java/edu/harvard/hul/ois/jhove/module/tiff/TiffProfilePagemaker6.java +++ b/jhove-modules/tiff-hul/src/main/java/edu/harvard/hul/ois/jhove/module/tiff/TiffProfilePagemaker6.java @@ -1,123 +1,111 @@ -/********************************************************************** - * Jhove - JSTOR/Harvard Object Validation Environment - * Copyright 2003 by JSTOR and the President and Fellows of Harvard College - **********************************************************************/ - +/** + * ******************************************************************** Jhove - JSTOR/Harvard Object + * Validation Environment Copyright 2003 by JSTOR and the President and Fellows of Harvard College + * ******************************************************************** + */ package edu.harvard.hul.ois.jhove.module.tiff; import edu.harvard.hul.ois.jhove.*; +/** Profile checker for TIFF Pagemaker 6.0. */ +public final class TiffProfilePagemaker6 extends TiffProfile { + public TiffProfilePagemaker6() { + super(); + _profileText = "Adobe PageMaker 6.0"; + } -/** - * Profile checker for TIFF Pagemaker 6.0. - */ -public final class TiffProfilePagemaker6 extends TiffProfile -{ - public TiffProfilePagemaker6 () - { - super (); - _profileText = "Adobe PageMaker 6.0"; + /** + * Returns true if the IFD satisfies the requirements of the profile. See the PageMaker + * specification for details. + */ + @Override + public boolean satisfiesThisProfile(IFD ifd) { + if (!(ifd instanceof TiffIFD)) { + return false; } + TiffIFD tifd = (TiffIFD) ifd; - /** - * Returns true if the IFD satisfies the requirements of the - * profile. See the PageMaker specification for details. - */ - @Override - public boolean satisfiesThisProfile (IFD ifd) - { - if (!(ifd instanceof TiffIFD)) { - return false; - } - TiffIFD tifd = (TiffIFD) ifd; - - /* Check required tags. */ - NisoImageMetadata niso = tifd.getNisoImageMetadata (); - long imageLength = niso.getImageLength (); - if (imageLength == NisoImageMetadata.NULL || - niso.getImageWidth () == NisoImageMetadata.NULL) { - return false; - } - - boolean so = (niso.getStripOffsets () != null); - boolean to = (niso.getTileOffsets () != null); - if ((so && to) || (!so && !to)) { - return false; - } + /* Check required tags. */ + NisoImageMetadata niso = tifd.getNisoImageMetadata(); + long imageLength = niso.getImageLength(); + if (imageLength == NisoImageMetadata.NULL || niso.getImageWidth() == NisoImageMetadata.NULL) { + return false; + } - if (so) { - if (niso.getStripByteCounts () == null) { - return false; - } - long rowsPerStrip = niso.getRowsPerStrip (); - if (rowsPerStrip == NisoImageMetadata.NULL || - rowsPerStrip < 1L || rowsPerStrip > imageLength) { - return false; - } - } + boolean so = (niso.getStripOffsets() != null); + boolean to = (niso.getTileOffsets() != null); + if ((so && to) || (!so && !to)) { + return false; + } - if (to && (niso.getTileWidth () == NisoImageMetadata.NULL || - niso.getTileLength () == NisoImageMetadata.NULL || - niso.getTileOffsets () == null || - niso.getTileByteCounts () == null)) { - return false; - } + if (so) { + if (niso.getStripByteCounts() == null) { + return false; + } + long rowsPerStrip = niso.getRowsPerStrip(); + if (rowsPerStrip == NisoImageMetadata.NULL + || rowsPerStrip < 1L + || rowsPerStrip > imageLength) { + return false; + } + } - /* Check required values. */ - if (!satisfiesCompression (tifd, new int [] {1, 2, 5, 32773, 32895, - 32896} )) { - return false; - } + if (to + && (niso.getTileWidth() == NisoImageMetadata.NULL + || niso.getTileLength() == NisoImageMetadata.NULL + || niso.getTileOffsets() == null + || niso.getTileByteCounts() == null)) { + return false; + } - int pi = niso.getColorSpace (); - if (pi != 0 && pi != 1 && pi != 2 && pi != 3 && pi != 5 && pi != 8 && - pi != 9) { - return false; - } + /* Check required values. */ + if (!satisfiesCompression(tifd, new int[] {1, 2, 5, 32773, 32895, 32896})) { + return false; + } - int inkSet = tifd.getInkSet (); - int spp = niso.getSamplesPerPixel (); - if (pi == 0 || pi == 1 || pi == 3) { - if (spp != 1) { - return false; - } - } - else if (pi == 2 || pi == 8 || pi == 9) { - if (spp != 3) { - return false; - } - } - else if (inkSet == 1 && spp != 4) { /* Only check for RGB, not hi-fi/multi-ink. */ - return false; - } + int pi = niso.getColorSpace(); + if (pi != 0 && pi != 1 && pi != 2 && pi != 3 && pi != 5 && pi != 8 && pi != 9) { + return false; + } - int [] bps = niso.getBitsPerSample (); - if (bps != null) { - if (pi == 0 || pi == 1 || pi == 3) { - for (int i=0; i -1) { - name = TAG_LABELS[n]; - } - else { - name = Integer.toString (tag); - } - return name; + String name = null; + int n = -1; + for (int i = 0; i < TAG_INDEX.length; i++) { + if (tag == TAG_INDEX[i]) { + n = i; + break; + } + } + if (n > -1) { + name = TAG_LABELS[n]; + } else { + name = Integer.toString(tag); } -} + return name; + } +} diff --git a/jhove-modules/tiff-hul/src/main/java/edu/harvard/hul/ois/jhove/module/tiff/package-info.java b/jhove-modules/tiff-hul/src/main/java/edu/harvard/hul/ois/jhove/module/tiff/package-info.java index 8a53a6069..e81db0c01 100644 --- a/jhove-modules/tiff-hul/src/main/java/edu/harvard/hul/ois/jhove/module/tiff/package-info.java +++ b/jhove-modules/tiff-hul/src/main/java/edu/harvard/hul/ois/jhove/module/tiff/package-info.java @@ -1,4 +1,2 @@ -/** - * Contains supporting classes for the TIFF-HUL module. - */ -package edu.harvard.hul.ois.jhove.module.tiff; \ No newline at end of file +/** Contains supporting classes for the TIFF-HUL module. */ +package edu.harvard.hul.ois.jhove.module.tiff; diff --git a/jhove-modules/tiff-hul/src/test/java/edu/harvard/hul/ois/jhove/module/tiff/TiffIFDTest.java b/jhove-modules/tiff-hul/src/test/java/edu/harvard/hul/ois/jhove/module/tiff/TiffIFDTest.java index b06bfb7bd..ef2fb9a3e 100644 --- a/jhove-modules/tiff-hul/src/test/java/edu/harvard/hul/ois/jhove/module/tiff/TiffIFDTest.java +++ b/jhove-modules/tiff-hul/src/test/java/edu/harvard/hul/ois/jhove/module/tiff/TiffIFDTest.java @@ -2,42 +2,45 @@ import static org.junit.Assert.*; +import edu.harvard.hul.ois.jhove.Property; import org.junit.Before; import org.junit.Test; -import edu.harvard.hul.ois.jhove.Property; - public class TiffIFDTest { - private TiffIFD ifd; - - @Before - public void setUp() { - ifd = new TiffIFD(0L, null, null, true); - } - - @Test - public void testAddFocalPlaneValue5() { - final String testedPropertyName = "FocalPlaneResolutionUnit"; - final int testedFocalPlaneResolutionUnit = 5; - - // Test for raw value - Property p1 = ifd.addIntegerProperty(testedPropertyName, - testedFocalPlaneResolutionUnit, - TiffIFD.FOCALPLANERESOLUTIONUNIT_L, true); - assertNotNull(p1); - assertEquals(new Integer(testedFocalPlaneResolutionUnit), p1.getValue()); - - // Test for decoded value - Property p2 = ifd.addIntegerProperty(testedPropertyName, - testedFocalPlaneResolutionUnit, - TiffIFD.FOCALPLANERESOLUTIONUNIT_L, false); - assertNotNull(p2); - assertEquals( - "Bad value " + p2.getValue(), - true, - TiffIFD.FOCALPLANERESOLUTIONUNIT_L[testedFocalPlaneResolutionUnit] - .equals(p2.getValue())); - } - + private TiffIFD ifd; + + @Before + public void setUp() { + ifd = new TiffIFD(0L, null, null, true); + } + + @Test + public void testAddFocalPlaneValue5() { + final String testedPropertyName = "FocalPlaneResolutionUnit"; + final int testedFocalPlaneResolutionUnit = 5; + + // Test for raw value + Property p1 = + ifd.addIntegerProperty( + testedPropertyName, + testedFocalPlaneResolutionUnit, + TiffIFD.FOCALPLANERESOLUTIONUNIT_L, + true); + assertNotNull(p1); + assertEquals(new Integer(testedFocalPlaneResolutionUnit), p1.getValue()); + + // Test for decoded value + Property p2 = + ifd.addIntegerProperty( + testedPropertyName, + testedFocalPlaneResolutionUnit, + TiffIFD.FOCALPLANERESOLUTIONUNIT_L, + false); + assertNotNull(p2); + assertEquals( + "Bad value " + p2.getValue(), + true, + TiffIFD.FOCALPLANERESOLUTIONUNIT_L[testedFocalPlaneResolutionUnit].equals(p2.getValue())); + } } diff --git a/jhove-modules/tiff-hul/src/test/java/edu/harvard/hul/ois/jhove/module/tiff/TiffProfileExifIFDTest.java b/jhove-modules/tiff-hul/src/test/java/edu/harvard/hul/ois/jhove/module/tiff/TiffProfileExifIFDTest.java index b6a16604c..caac7afcc 100644 --- a/jhove-modules/tiff-hul/src/test/java/edu/harvard/hul/ois/jhove/module/tiff/TiffProfileExifIFDTest.java +++ b/jhove-modules/tiff-hul/src/test/java/edu/harvard/hul/ois/jhove/module/tiff/TiffProfileExifIFDTest.java @@ -7,56 +7,55 @@ public class TiffProfileExifIFDTest { - private static final String GOOD_FLASHPIX_VERSION = "0100"; - private static final String BAD_FLASHPIX_VERSION = "0010"; - private static final String GOOD_EXIF_VERSION = "0200"; - private static final String GOOD_EXIF_VERSION2 = "0221"; - private static final String BAD_EXIF_VERSION = "0101"; - private static final int GOOD_COLORSPACE = 65535; - private static final int BAD_COLORSPACE = 3; - - private TiffProfileExifIFD profile; - - @Before - public void setUp() { - profile = new TiffProfileExifIFD(); - } - - @Test - public void testSatisfiesThisProfileOK() { - ExifIFD ifd = new ExifIFD(0, null, null, true); - ifd._colorSpace = GOOD_COLORSPACE; - ifd._exifVersion = GOOD_EXIF_VERSION; - ifd._flashpixVersion = GOOD_FLASHPIX_VERSION; - - assertTrue(profile.satisfiesThisProfile(ifd)); - assertEquals(2, profile.getMajorVersion()); - assertEquals(0, profile.getMinorVersion()); - - ifd._exifVersion = GOOD_EXIF_VERSION2; - assertTrue(profile.satisfiesThisProfile(ifd)); - assertEquals(2, profile.getMajorVersion()); - assertEquals(21, profile.getMinorVersion()); - } - - @Test - public void testSatisfiesThisProfileKO() { - ExifIFD ifd = new ExifIFD(0, null, null, true); - - ifd._colorSpace = GOOD_COLORSPACE; - ifd._exifVersion = BAD_EXIF_VERSION; - ifd._flashpixVersion = GOOD_FLASHPIX_VERSION; - assertFalse(profile.satisfiesThisProfile(ifd)); - - ifd._colorSpace = GOOD_COLORSPACE; - ifd._exifVersion = GOOD_EXIF_VERSION; - ifd._flashpixVersion = BAD_FLASHPIX_VERSION; - assertFalse(profile.satisfiesThisProfile(ifd)); - - ifd._colorSpace = BAD_COLORSPACE; - ifd._exifVersion = GOOD_EXIF_VERSION; - ifd._flashpixVersion = GOOD_FLASHPIX_VERSION; - assertFalse(profile.satisfiesThisProfile(ifd)); - } - + private static final String GOOD_FLASHPIX_VERSION = "0100"; + private static final String BAD_FLASHPIX_VERSION = "0010"; + private static final String GOOD_EXIF_VERSION = "0200"; + private static final String GOOD_EXIF_VERSION2 = "0221"; + private static final String BAD_EXIF_VERSION = "0101"; + private static final int GOOD_COLORSPACE = 65535; + private static final int BAD_COLORSPACE = 3; + + private TiffProfileExifIFD profile; + + @Before + public void setUp() { + profile = new TiffProfileExifIFD(); + } + + @Test + public void testSatisfiesThisProfileOK() { + ExifIFD ifd = new ExifIFD(0, null, null, true); + ifd._colorSpace = GOOD_COLORSPACE; + ifd._exifVersion = GOOD_EXIF_VERSION; + ifd._flashpixVersion = GOOD_FLASHPIX_VERSION; + + assertTrue(profile.satisfiesThisProfile(ifd)); + assertEquals(2, profile.getMajorVersion()); + assertEquals(0, profile.getMinorVersion()); + + ifd._exifVersion = GOOD_EXIF_VERSION2; + assertTrue(profile.satisfiesThisProfile(ifd)); + assertEquals(2, profile.getMajorVersion()); + assertEquals(21, profile.getMinorVersion()); + } + + @Test + public void testSatisfiesThisProfileKO() { + ExifIFD ifd = new ExifIFD(0, null, null, true); + + ifd._colorSpace = GOOD_COLORSPACE; + ifd._exifVersion = BAD_EXIF_VERSION; + ifd._flashpixVersion = GOOD_FLASHPIX_VERSION; + assertFalse(profile.satisfiesThisProfile(ifd)); + + ifd._colorSpace = GOOD_COLORSPACE; + ifd._exifVersion = GOOD_EXIF_VERSION; + ifd._flashpixVersion = BAD_FLASHPIX_VERSION; + assertFalse(profile.satisfiesThisProfile(ifd)); + + ifd._colorSpace = BAD_COLORSPACE; + ifd._exifVersion = GOOD_EXIF_VERSION; + ifd._flashpixVersion = GOOD_FLASHPIX_VERSION; + assertFalse(profile.satisfiesThisProfile(ifd)); + } } diff --git a/jhove-modules/utf8-hul/src/main/java/edu/harvard/hul/ois/jhove/module/Utf8Module.java b/jhove-modules/utf8-hul/src/main/java/edu/harvard/hul/ois/jhove/module/Utf8Module.java index 296f06eb3..abd6ce142 100644 --- a/jhove-modules/utf8-hul/src/main/java/edu/harvard/hul/ois/jhove/module/Utf8Module.java +++ b/jhove-modules/utf8-hul/src/main/java/edu/harvard/hul/ois/jhove/module/Utf8Module.java @@ -1,36 +1,22 @@ -/********************************************************************** - * Jhove - JSTOR/Harvard Object Validation Environment Copyright 2003-2007 by - * JSTOR and the President and Fellows of Harvard College +/** + * ******************************************************************** Jhove - JSTOR/Harvard Object + * Validation Environment Copyright 2003-2007 by JSTOR and the President and Fellows of Harvard + * College * - * This program 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 2 of the License, or (at your option) any - * later version. + *

This program 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 2 + * of the License, or (at your option) any later version. * - * This program 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. + *

This program 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. * - * You should have received a copy of the GNU Lesser General Public License - * along with this program; if not, write to the Free Software Foundation, Inc., - * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - **********************************************************************/ - + *

You should have received a copy of the GNU Lesser General Public License along with this + * program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA ******************************************************************** + */ package edu.harvard.hul.ois.jhove.module; -import java.io.DataInputStream; -import java.io.EOFException; -import java.io.File; -import java.io.IOException; -import java.io.InputStream; -import java.util.ArrayList; -import java.util.EnumSet; -import java.util.HashSet; -import java.util.LinkedList; -import java.util.List; -import java.util.Set; - import edu.harvard.hul.ois.jhove.Agent; import edu.harvard.hul.ois.jhove.AgentType; import edu.harvard.hul.ois.jhove.Document; @@ -51,469 +37,485 @@ import edu.harvard.hul.ois.jhove.module.ascii.LineEnding; import edu.harvard.hul.ois.jhove.module.utf8.MessageConstants; import edu.harvard.hul.ois.jhove.module.utf8.Utf8BlockMarker; +import java.io.DataInputStream; +import java.io.EOFException; +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.util.ArrayList; +import java.util.EnumSet; +import java.util.HashSet; +import java.util.LinkedList; +import java.util.List; +import java.util.Set; -/** - * Module for analysis of content as a UTF-8 stream. - */ +/** Module for analysis of content as a UTF-8 stream. */ public class Utf8Module extends ModuleBase { - public final static String INF_PRINT_CHAR_MISS = "No printable characters"; - - /****************************************************************** - * PRIVATE CLASS FIELDS. - ******************************************************************/ - private static final String NAME = "UTF8-hul"; - private static final String RELEASE = "1.7.1"; - private static final int [] DATE = { 2019, 04, 17 }; - private static final String[] FORMAT = { "UTF-8" }; - private static final String COVERAGE = "Unicode 7.0.0"; - private static final String[] MIMETYPE = { "text/plain; charset=UTF-8" }; - private static final String WELLFORMED = "An UTF-8 object is well-formed " - + "if each character is correctly encoded as a one-to-four byte " - + "sequence, as defined in the specifications"; - private static final String VALIDITY = null; - private static final String REPINFO = "Additional representation " - + "information includes: number of characters and Unicode 7.0.0 code " - + "blocks"; - private static final String NOTE = null; - private static final String RIGHTS = "Copyright 2003-2011 by JSTOR and " - + "the President and Fellows of Harvard College. " - + "Released under the GNU Lesser General Public License."; - - /****************************************************************** - * PRIVATE INSTANCE FIELDS. - ******************************************************************/ - - protected Set usedCtrlChars; - protected Set usedLineEndings; - protected int initialBytes[]; - protected Utf8BlockMarker blockMarker; - - /* Flag to know if the property TextMDMetadata is to be added */ - protected boolean _withTextMD = false; - /* Hold the information needed to generate a textMD metadata fragment */ - protected TextMDMetadata _textMD; - - /****************************************************************** - * CLASS CONSTRUCTOR. - ******************************************************************/ - - /** - * Creates a Utf8Module. - */ - public Utf8Module() { - super(NAME, RELEASE, DATE, FORMAT, COVERAGE, MIMETYPE, WELLFORMED, - VALIDITY, REPINFO, NOTE, RIGHTS, false); - - this._vendor = Agent.harvardInstance(); - - Document doc = new Document("The Unicode Standard, Version 6.0", - DocumentType.BOOK); - Agent agent = new Agent.Builder("The Unicode Consortium", - AgentType.NONPROFIT) - .web("http://www.unicode.org/versions/Unicode7.0.0/") - .address("Mountain View, California").build(); - doc.setAuthor(agent); - agent = new Agent.Builder("Addison-Wesley", AgentType.COMMERCIAL).address("Boston, Massachusetts").build(); - doc.setPublisher(agent); - doc.setDate("2011"); - doc.setIdentifier(new Identifier("978-1-936213-01-6", - IdentifierType.ISBN)); - this._specification.add(doc); - - doc = new Document("Information technology -- Universal " + public static final String INF_PRINT_CHAR_MISS = "No printable characters"; + + /** + * **************************************************************** PRIVATE CLASS FIELDS. + * **************************************************************** + */ + private static final String NAME = "UTF8-hul"; + + private static final String RELEASE = "1.7.1"; + private static final int[] DATE = {2019, 04, 17}; + private static final String[] FORMAT = {"UTF-8"}; + private static final String COVERAGE = "Unicode 7.0.0"; + private static final String[] MIMETYPE = {"text/plain; charset=UTF-8"}; + private static final String WELLFORMED = + "An UTF-8 object is well-formed " + + "if each character is correctly encoded as a one-to-four byte " + + "sequence, as defined in the specifications"; + private static final String VALIDITY = null; + private static final String REPINFO = + "Additional representation " + + "information includes: number of characters and Unicode 7.0.0 code " + + "blocks"; + private static final String NOTE = null; + private static final String RIGHTS = + "Copyright 2003-2011 by JSTOR and " + + "the President and Fellows of Harvard College. " + + "Released under the GNU Lesser General Public License."; + + /** + * **************************************************************** PRIVATE INSTANCE FIELDS. + * **************************************************************** + */ + protected Set usedCtrlChars; + + protected Set usedLineEndings; + protected int initialBytes[]; + protected Utf8BlockMarker blockMarker; + + /* Flag to know if the property TextMDMetadata is to be added */ + protected boolean _withTextMD = false; + /* Hold the information needed to generate a textMD metadata fragment */ + protected TextMDMetadata _textMD; + + /** + * **************************************************************** CLASS CONSTRUCTOR. + * **************************************************************** + */ + + /** Creates a Utf8Module. */ + public Utf8Module() { + super( + NAME, + RELEASE, + DATE, + FORMAT, + COVERAGE, + MIMETYPE, + WELLFORMED, + VALIDITY, + REPINFO, + NOTE, + RIGHTS, + false); + + this._vendor = Agent.harvardInstance(); + + Document doc = new Document("The Unicode Standard, Version 6.0", DocumentType.BOOK); + Agent agent = + new Agent.Builder("The Unicode Consortium", AgentType.NONPROFIT) + .web("http://www.unicode.org/versions/Unicode7.0.0/") + .address("Mountain View, California") + .build(); + doc.setAuthor(agent); + agent = + new Agent.Builder("Addison-Wesley", AgentType.COMMERCIAL) + .address("Boston, Massachusetts") + .build(); + doc.setPublisher(agent); + doc.setDate("2011"); + doc.setIdentifier(new Identifier("978-1-936213-01-6", IdentifierType.ISBN)); + this._specification.add(doc); + + doc = + new Document( + "Information technology -- Universal " + "Multiple-Octet Coded Character Set (UCS) -- " + "Part 1: Architecture and Basic Multilingual " - + "Plane. Appendix R, Amendment 2", DocumentType.STANDARD); - doc.setPublisher(Agent.newIsoInstance()); - doc.setDate("1991"); - doc.setIdentifier(new Identifier("ISO/IEC 10646-1 Amendment 2", - IdentifierType.ISO)); - this._specification.add(doc); - - doc = new Document("UTF-8, a transformation format of ISO 10646", - DocumentType.RFC); - agent = new Agent.Builder("F. Yergeau", AgentType.OTHER).build(); - doc.setAuthor(agent); - agent = new Agent.Builder("IETF", AgentType.NONPROFIT).web("http://www.ietf.org/").build(); - doc.setPublisher(agent); - doc.setDate("1998-01"); - doc.setIdentifier(new Identifier("RFC 2279", IdentifierType.RFC)); - doc.setIdentifier(new Identifier("http://www.ietf.org/rfc/rfc2279.txt", - IdentifierType.URL)); - this._specification.add(doc); + + "Plane. Appendix R, Amendment 2", + DocumentType.STANDARD); + doc.setPublisher(Agent.newIsoInstance()); + doc.setDate("1991"); + doc.setIdentifier(new Identifier("ISO/IEC 10646-1 Amendment 2", IdentifierType.ISO)); + this._specification.add(doc); + + doc = new Document("UTF-8, a transformation format of ISO 10646", DocumentType.RFC); + agent = new Agent.Builder("F. Yergeau", AgentType.OTHER).build(); + doc.setAuthor(agent); + agent = new Agent.Builder("IETF", AgentType.NONPROFIT).web("http://www.ietf.org/").build(); + doc.setPublisher(agent); + doc.setDate("1998-01"); + doc.setIdentifier(new Identifier("RFC 2279", IdentifierType.RFC)); + doc.setIdentifier(new Identifier("http://www.ietf.org/rfc/rfc2279.txt", IdentifierType.URL)); + this._specification.add(doc); + } + + /** + * **************************************************************** PUBLIC INSTANCE METHODS. + * + *

Parsing methods. **************************************************************** + */ + + /** + * Parse the content of a stream digital object and store the results in RepInfo. + * + * @param stream An InputStream, positioned at its beginning, which is generated from the object + * to be parsed. If multiple calls to parse are made on the basis of a nonzero + * value being returned, a new InputStream must be provided each time. + * @param info A fresh (on the first call) RepInfo object which will be modified to reflect the + * results of the parsing If multiple calls to parse are made on the basis of a + * nonzero value being returned, the same RepInfo object should be passed with each call. + * @param parseIndex Must be 0 in first call to parse. If parse returns + * a nonzero value, it must be called again with parseIndex equal to that return + * value. + */ + @Override + public final int parse(InputStream stream, RepInfo info, int parseIndex) throws IOException { + // Test if textMD is to be generated + _withTextMD = isParamInDefaults("withtextmd=true"); + + initParse(); + initInfo(info); + this.initialBytes = new int[4]; + + // No line end types have been discovered. + ControlChar prevChar = null; + this.usedCtrlChars = new HashSet<>(); + this.usedLineEndings = new HashSet<>(); + this._textMD = new TextMDMetadata(); + + boolean printableChars = false; + + // TODO: Why here and not ASCII + info.setNote( + "Additional representation information includes " + "the line endings: CR, LF, or CRLF"); + this._nByte = 0; + long nChar = 0; + + // Setup the data stream, will determine if we use checksum stream + setupDataStream(stream, info); + this.blockMarker = new Utf8BlockMarker(); + + boolean eof = false; + while (!eof) { + try { + boolean isMark = false; + int[] b = new int[4]; + int ch = -1; + + /* Byte values must be valid UTF-8 encodings: */ + /* Unicode value Byte 1 Byte 2 Byte 3 Byte 4 */ + /* 000000000xxxxxxx 0xxxxxxx */ + /* 00000yyyyyxxxxxx 110yyyyy 10xxxxxx */ + /* zzzzyyyyyyxxxxxx 1110zzzz 10yyyyyy 10yyyyyy */ + /* uuuuuzzzzyyyyyyxxxxxx 11110uuu 10uuzzzz 10yyyyyy 10xxxxxx */ + + b[0] = readUnsignedByte(this._dstream, this); + if (this._nByte < 4) { + isMark = checkMark(b[0], info); + if (info.getWellFormed() == RepInfo.FALSE) { + return 0; + } + if (isMark) { + nChar = 0; + } + } - } + int nBytes = 1; + if (0xc0 <= b[0] && b[0] <= 0xdf) { + nBytes = 2; + } else if (0xe0 <= b[0] && b[0] <= 0xef) { + nBytes = 3; + } else if (0xf0 <= b[0] && b[0] <= 0xf7) { + nBytes = 4; + } else if ((0x80 <= b[0] && b[0] <= 0xbf) || (0xf8 <= b[0] && b[0] <= 0xff)) { + ErrorMessage error = + new ErrorMessage( + MessageConstants.UTF8_HUL_2, + "Value = " + ((char) b[0]) + " (0x" + Integer.toHexString(b[0]) + ")", + this._nByte); + info.setMessage(error); + info.setWellFormed(false); + return 0; + } - /****************************************************************** - * PUBLIC INSTANCE METHODS. - * - * Parsing methods. - ******************************************************************/ - - /** - * Parse the content of a stream digital object and store the results in - * RepInfo. - * - * @param stream - * An InputStream, positioned at its beginning, which is - * generated from the object to be parsed. If multiple calls to - * parse are made on the basis of a nonzero value - * being returned, a new InputStream must be provided each time. - * - * @param info - * A fresh (on the first call) RepInfo object which will be - * modified to reflect the results of the parsing If multiple - * calls to parse are made on the basis of a nonzero - * value being returned, the same RepInfo object should be passed - * with each call. - * - * @param parseIndex - * Must be 0 in first call to parse. If - * parse returns a nonzero value, it must be called - * again with parseIndex equal to that return value. - * - */ - @Override - public final int parse(InputStream stream, RepInfo info, int parseIndex) - throws IOException { - // Test if textMD is to be generated - _withTextMD = isParamInDefaults("withtextmd=true"); - - initParse(); - initInfo(info); - this.initialBytes = new int[4]; - - // No line end types have been discovered. - ControlChar prevChar = null; - this.usedCtrlChars = new HashSet<>(); - this.usedLineEndings = new HashSet<>(); - this._textMD = new TextMDMetadata(); - - boolean printableChars = false; - - // TODO: Why here and not ASCII - info.setNote("Additional representation information includes " - + "the line endings: CR, LF, or CRLF"); - this._nByte = 0; - long nChar = 0; - - // Setup the data stream, will determine if we use checksum stream - setupDataStream(stream, info); - this.blockMarker = new Utf8BlockMarker(); - - boolean eof = false; - while (!eof) { - try { - boolean isMark = false; - int[] b = new int[4]; - int ch = -1; - - /* Byte values must be valid UTF-8 encodings: */ - /* Unicode value Byte 1 Byte 2 Byte 3 Byte 4 */ - /* 000000000xxxxxxx 0xxxxxxx */ - /* 00000yyyyyxxxxxx 110yyyyy 10xxxxxx */ - /* zzzzyyyyyyxxxxxx 1110zzzz 10yyyyyy 10yyyyyy */ - /* uuuuuzzzzyyyyyyxxxxxx 11110uuu 10uuzzzz 10yyyyyy 10xxxxxx */ - - b[0] = readUnsignedByte(this._dstream, this); - if (this._nByte < 4) { - isMark = checkMark(b[0], info); - if (info.getWellFormed() == RepInfo.FALSE) { - return 0; - } - if (isMark) { - nChar = 0; - } - } - - int nBytes = 1; - if (0xc0 <= b[0] && b[0] <= 0xdf) { - nBytes = 2; - } else if (0xe0 <= b[0] && b[0] <= 0xef) { - nBytes = 3; - } else if (0xf0 <= b[0] && b[0] <= 0xf7) { - nBytes = 4; - } else if ((0x80 <= b[0] && b[0] <= 0xbf) - || (0xf8 <= b[0] && b[0] <= 0xff)) { - ErrorMessage error = new ErrorMessage(MessageConstants.UTF8_HUL_2, - "Value = " + ((char) b[0]) + " (0x" - + Integer.toHexString(b[0]) + ")", this._nByte); - info.setMessage(error); - info.setWellFormed(false); - return 0; - } - - for (int i = 1; i < nBytes; i++) { - b[i] = readUnsignedByte(this._dstream, this); - if (this._nByte < 4) { - isMark = checkMark(b[i], info); - } - if (info.getWellFormed() == RepInfo.FALSE) { - return 0; - } - - if (0x80 > b[i] || b[i] > 0xbf) { - String subMessage = "Value = " + ((char) b[i]) + " (0x" + Integer.toHexString(b[i]) + ")"; - JhoveMessage errMessage = null; - switch (i) { // max(nBytes) is 4 - case 1: errMessage = MessageConstants.UTF8_HUL_3; break; - case 2: errMessage = MessageConstants.UTF8_HUL_4; break; - case 3: errMessage = MessageConstants.UTF8_HUL_5; break; - default: break; - } - ErrorMessage error = new ErrorMessage(errMessage, subMessage , this._nByte); - info.setMessage(error); - info.setWellFormed(false); - return 0; - } - } - - if (nBytes == 1) { - ch = b[0]; - } else if (nBytes == 2) { - ch = ((b[0] & 0x1f) << 6) + (b[1] & 0x3f); - } else if (nBytes == 3) { - ch = ((b[0] & 0x0f) << 12) + ((b[1] & 0x3f) << 6) - + (b[2] & 0x3f); - } else if (nBytes == 4) { - ch = ((b[0] & 0x07) << 18) + ((b[1] & 0x3f) << 12) - + ((b[2] & 0x3f) << 6) + (b[3] & 0x3f); - } - - if (!isMark) { - this.blockMarker.markBlock(ch); - } - - /* Character values U+000..U+001f,U+007f aren't printable. */ - /* Only byte values 0x20 through 0x7e are printable. */ - if (!printableChars) { - printableChars = (ch > 0x001f && ch != 0x7f); - } - - /* Determine the line ending type(s). */ - ControlChar ctrlChar = ControlChar.asciiFromInt(ch); - if (ControlChar.isLineEndChar(ctrlChar)) { - // Carry out the line endings test - LineEnding le = LineEnding.fromControlChars(ctrlChar, prevChar); - if (le != null) this.usedLineEndings.add(le); - } else if (ctrlChar != null) { - // The passed char is a control char and not a line ending - this.usedCtrlChars.add(ctrlChar); - } else if (!printableChars) { - // Only byte values 0x20 through 0x7e are printable. - printableChars = (0x001f < ch); - } - if (prevChar == ControlChar.CR && ctrlChar != ControlChar.LF) { - // Carry out the line endings test - LineEnding le = LineEnding.fromControlChars(ctrlChar, prevChar); - if (le != null) this.usedLineEndings.add(le); - } - prevChar = ctrlChar; - - nChar++; - } catch (EOFException e) { - eof = true; - /* Catch line endings at very end. */ - LineEnding le = LineEnding.fromControlChars(ControlChar.NUL, prevChar); - if (le != null) this.usedLineEndings.add(le); + for (int i = 1; i < nBytes; i++) { + b[i] = readUnsignedByte(this._dstream, this); + if (this._nByte < 4) { + isMark = checkMark(b[i], info); + } + if (info.getWellFormed() == RepInfo.FALSE) { + return 0; + } + + if (0x80 > b[i] || b[i] > 0xbf) { + String subMessage = + "Value = " + ((char) b[i]) + " (0x" + Integer.toHexString(b[i]) + ")"; + JhoveMessage errMessage = null; + switch (i) { // max(nBytes) is 4 + case 1: + errMessage = MessageConstants.UTF8_HUL_3; + break; + case 2: + errMessage = MessageConstants.UTF8_HUL_4; + break; + case 3: + errMessage = MessageConstants.UTF8_HUL_5; + break; + default: + break; } + ErrorMessage error = new ErrorMessage(errMessage, subMessage, this._nByte); + info.setMessage(error); + info.setWellFormed(false); + return 0; + } } - /* Object is well-formed UTF-8. */ - - // Set the checksums in the report if they're calculated - setChecksums(this._ckSummer, info); + if (nBytes == 1) { + ch = b[0]; + } else if (nBytes == 2) { + ch = ((b[0] & 0x1f) << 6) + (b[1] & 0x3f); + } else if (nBytes == 3) { + ch = ((b[0] & 0x0f) << 12) + ((b[1] & 0x3f) << 6) + (b[2] & 0x3f); + } else if (nBytes == 4) { + ch = ((b[0] & 0x07) << 18) + ((b[1] & 0x3f) << 12) + ((b[2] & 0x3f) << 6) + (b[3] & 0x3f); + } - /* - * Only non-zero-length files are well-formed UTF-8. - */ - if (this._nByte == 0) { - info.setMessage(new ErrorMessage(MessageConstants.UTF8_HUL_6)); - info.setWellFormed(RepInfo.FALSE); - return 0; + if (!isMark) { + this.blockMarker.markBlock(ch); } - /* Add the textMD information */ - this._textMD.setCharset(TextMDMetadata.CHARSET_UTF8); - this._textMD.setByte_order(this._bigEndian ? TextMDMetadata.BYTE_ORDER_BIG - : TextMDMetadata.BYTE_ORDER_LITTLE); - this._textMD.setByte_size("8"); - this._textMD.setCharacter_size("variable"); - - /* - * Create a metadata property for the module-specific info. (4-Feb-04) - */ - List metadataList = new ArrayList<>(4); - info.setProperty(new Property("UTF8Metadata", PropertyType.PROPERTY, - PropertyArity.LIST, metadataList)); - - Property property = new Property("Characters", PropertyType.LONG, - new Long(nChar)); - metadataList.add(property); - - property = this.blockMarker.getBlocksUsedProperty("UnicodeBlocks"); - if (property != null) { - metadataList.add(property); + /* Character values U+000..U+001f,U+007f aren't printable. */ + /* Only byte values 0x20 through 0x7e are printable. */ + if (!printableChars) { + printableChars = (ch > 0x001f && ch != 0x7f); } - /* Set property reporting line ending type */ - List propArray = reportLineEndings(); - if (!propArray.isEmpty()) { - property = new Property(LineEnding.PROP_NAME, - PropertyType.STRING, PropertyArity.LIST, propArray); - metadataList.add(property); + /* Determine the line ending type(s). */ + ControlChar ctrlChar = ControlChar.asciiFromInt(ch); + if (ControlChar.isLineEndChar(ctrlChar)) { + // Carry out the line endings test + LineEnding le = LineEnding.fromControlChars(ctrlChar, prevChar); + if (le != null) this.usedLineEndings.add(le); + } else if (ctrlChar != null) { + // The passed char is a control char and not a line ending + this.usedCtrlChars.add(ctrlChar); + } else if (!printableChars) { + // Only byte values 0x20 through 0x7e are printable. + printableChars = (0x001f < ch); } - /* Set property reporting control characters used */ - if (!this.usedCtrlChars.isEmpty()) { - LinkedList propList = new LinkedList<>(); - for (ControlChar ctrlChar : EnumSet.copyOf(this.usedCtrlChars)) { - propList.add(ctrlChar.mnemonic); - } - property = new Property(ControlChar.PROP_NAME, - PropertyType.STRING, PropertyArity.LIST, propList); - metadataList.add(property); + if (prevChar == ControlChar.CR && ctrlChar != ControlChar.LF) { + // Carry out the line endings test + LineEnding le = LineEnding.fromControlChars(ctrlChar, prevChar); + if (le != null) this.usedLineEndings.add(le); } + prevChar = ctrlChar; + + nChar++; + } catch (EOFException e) { + eof = true; + /* Catch line endings at very end. */ + LineEnding le = LineEnding.fromControlChars(ControlChar.NUL, prevChar); + if (le != null) this.usedLineEndings.add(le); + } + } - if (this._withTextMD) { - property = new Property("TextMDMetadata", - PropertyType.TEXTMDMETADATA, PropertyArity.SCALAR, this._textMD); - metadataList.add(property); - } + /* Object is well-formed UTF-8. */ - if (!printableChars) { - info.setMessage(new InfoMessage(INF_PRINT_CHAR_MISS)); - } + // Set the checksums in the report if they're calculated + setChecksums(this._ckSummer, info); - return 0; + /* + * Only non-zero-length files are well-formed UTF-8. + */ + if (this._nByte == 0) { + info.setMessage(new ErrorMessage(MessageConstants.UTF8_HUL_6)); + info.setWellFormed(RepInfo.FALSE); + return 0; } - /** - * Check if the digital object conforms to this Module's internal signature - * information. Try to read the BOM if it's present, and check the beginning - * of the file. - * - * @param file - * A File object for the object being parsed - * @param stream - * An InputStream, positioned at its beginning, which is - * generated from the object to be parsed - * @param info - * A fresh RepInfo object which will be modified to reflect the - * results of the test + /* Add the textMD information */ + this._textMD.setCharset(TextMDMetadata.CHARSET_UTF8); + this._textMD.setByte_order( + this._bigEndian ? TextMDMetadata.BYTE_ORDER_BIG : TextMDMetadata.BYTE_ORDER_LITTLE); + this._textMD.setByte_size("8"); + this._textMD.setCharacter_size("variable"); + + /* + * Create a metadata property for the module-specific info. (4-Feb-04) */ - @Override - public void checkSignatures(File file, InputStream stream, RepInfo info) - throws IOException { - info.setFormat(this._format[0]); - info.setMimeType(this._mimeType[0]); - info.setModule(this); - this.initialBytes = new int[4]; - JhoveBase jb = getBase(); - int sigBytes = jb.getSigBytes(); - int bytesRead = 0; - this.blockMarker = new Utf8BlockMarker(); - boolean eof = false; - this._nByte = 0; - DataInputStream dstream = new DataInputStream(stream); - while (!eof && bytesRead < sigBytes) { - int[] b = new int[4]; - try { - b[0] = readUnsignedByte(dstream, this); - ++bytesRead; - if (this._nByte < 4) { - checkMark(b[0], info); - if (info.getWellFormed() == RepInfo.FALSE) { - return; - } - } - int nBytes = 1; - if (0xc0 <= b[0] && b[0] <= 0xdf) { - nBytes = 2; - } else if (0xe0 <= b[0] && b[0] <= 0xef) { - nBytes = 3; - } else if (0xf0 <= b[0] && b[0] <= 0xf7) { - nBytes = 4; - } else if ((0x80 <= b[0] && b[0] <= 0xbf) - || (0xf8 <= b[0] && b[0] <= 0xff)) { - info.setWellFormed(false); - return; - } - for (int i = 1; i < nBytes; i++) { - b[i] = readUnsignedByte(dstream, this); - if (this._nByte < 4) { - checkMark(b[i], info); - } - if (info.getWellFormed() == RepInfo.FALSE) { - return; - } - - if (0x80 > b[i] || b[i] > 0xbf) { - // Not a valid UTF-8 character - info.setWellFormed(false); - return; - } - } - - } catch (EOFException e) { - eof = true; - } + List metadataList = new ArrayList<>(4); + info.setProperty( + new Property("UTF8Metadata", PropertyType.PROPERTY, PropertyArity.LIST, metadataList)); + + Property property = new Property("Characters", PropertyType.LONG, new Long(nChar)); + metadataList.add(property); + + property = this.blockMarker.getBlocksUsedProperty("UnicodeBlocks"); + if (property != null) { + metadataList.add(property); + } + + /* Set property reporting line ending type */ + List propArray = reportLineEndings(); + if (!propArray.isEmpty()) { + property = + new Property(LineEnding.PROP_NAME, PropertyType.STRING, PropertyArity.LIST, propArray); + metadataList.add(property); + } + /* Set property reporting control characters used */ + if (!this.usedCtrlChars.isEmpty()) { + LinkedList propList = new LinkedList<>(); + for (ControlChar ctrlChar : EnumSet.copyOf(this.usedCtrlChars)) { + propList.add(ctrlChar.mnemonic); + } + property = + new Property(ControlChar.PROP_NAME, PropertyType.STRING, PropertyArity.LIST, propList); + metadataList.add(property); + } + + if (this._withTextMD) { + property = + new Property( + "TextMDMetadata", PropertyType.TEXTMDMETADATA, PropertyArity.SCALAR, this._textMD); + metadataList.add(property); + } + + if (!printableChars) { + info.setMessage(new InfoMessage(INF_PRINT_CHAR_MISS)); + } + + return 0; + } + + /** + * Check if the digital object conforms to this Module's internal signature information. Try to + * read the BOM if it's present, and check the beginning of the file. + * + * @param file A File object for the object being parsed + * @param stream An InputStream, positioned at its beginning, which is generated from the object + * to be parsed + * @param info A fresh RepInfo object which will be modified to reflect the results of the test + */ + @Override + public void checkSignatures(File file, InputStream stream, RepInfo info) throws IOException { + info.setFormat(this._format[0]); + info.setMimeType(this._mimeType[0]); + info.setModule(this); + this.initialBytes = new int[4]; + JhoveBase jb = getBase(); + int sigBytes = jb.getSigBytes(); + int bytesRead = 0; + this.blockMarker = new Utf8BlockMarker(); + boolean eof = false; + this._nByte = 0; + DataInputStream dstream = new DataInputStream(stream); + while (!eof && bytesRead < sigBytes) { + int[] b = new int[4]; + try { + b[0] = readUnsignedByte(dstream, this); + ++bytesRead; + if (this._nByte < 4) { + checkMark(b[0], info); + if (info.getWellFormed() == RepInfo.FALSE) { + return; + } } - if (bytesRead > 0) { - info.setSigMatch(this._name); - } else { - // Don't match an empty file + int nBytes = 1; + if (0xc0 <= b[0] && b[0] <= 0xdf) { + nBytes = 2; + } else if (0xe0 <= b[0] && b[0] <= 0xef) { + nBytes = 3; + } else if (0xf0 <= b[0] && b[0] <= 0xf7) { + nBytes = 4; + } else if ((0x80 <= b[0] && b[0] <= 0xbf) || (0xf8 <= b[0] && b[0] <= 0xff)) { + info.setWellFormed(false); + return; + } + for (int i = 1; i < nBytes; i++) { + b[i] = readUnsignedByte(dstream, this); + if (this._nByte < 4) { + checkMark(b[i], info); + } + if (info.getWellFormed() == RepInfo.FALSE) { + return; + } + + if (0x80 > b[i] || b[i] > 0xbf) { + // Not a valid UTF-8 character info.setWellFormed(false); + return; + } } - } - - /****************************************************************** - * PRIVATE INSTANCE METHODS. - ******************************************************************/ - - protected boolean checkMark(int byt, RepInfo info) { - ErrorMessage msg; - this.initialBytes[(int) this._nByte - 1] = byt; - if (this._nByte == 3) { - // Check for UTF-8 byte order mark in 1st 3 bytes - if (this.initialBytes[0] == 0xEF && this.initialBytes[1] == 0xBB - && this.initialBytes[2] == 0xBF) { - InfoMessage im = new InfoMessage(MessageConstants.UTF8_HUL_1, 0); // UTF8-HUL-1 - info.setMessage(im); - // If we've found a non-character header, clear - // all usage blocks - this.blockMarker.reset(); - return true; - } - if (this.initialBytes[0] == 0xFF && this.initialBytes[1] == 0xFE) { - if (this.initialBytes[2] == 0 && this.initialBytes[3] == 0) { - msg = new ErrorMessage(MessageConstants.UTF8_HUL_7); - } else { - msg = new ErrorMessage(MessageConstants.UTF8_HUL_8); - } - info.setMessage(msg); - info.setWellFormed(false); - return false; - } else if (this.initialBytes[0] == 0xFE && this.initialBytes[1] == 0xFF) { - msg = new ErrorMessage(MessageConstants.UTF8_HUL_9); - info.setMessage(msg); - info.setWellFormed(false); - return false; - } + } catch (EOFException e) { + eof = true; + } + } + if (bytesRead > 0) { + info.setSigMatch(this._name); + } else { + // Don't match an empty file + info.setWellFormed(false); + } + } + + /** + * **************************************************************** PRIVATE INSTANCE METHODS. + * **************************************************************** + */ + protected boolean checkMark(int byt, RepInfo info) { + ErrorMessage msg; + this.initialBytes[(int) this._nByte - 1] = byt; + if (this._nByte == 3) { + // Check for UTF-8 byte order mark in 1st 3 bytes + if (this.initialBytes[0] == 0xEF + && this.initialBytes[1] == 0xBB + && this.initialBytes[2] == 0xBF) { + InfoMessage im = new InfoMessage(MessageConstants.UTF8_HUL_1, 0); // UTF8-HUL-1 + info.setMessage(im); + // If we've found a non-character header, clear + // all usage blocks + this.blockMarker.reset(); + return true; + } + + if (this.initialBytes[0] == 0xFF && this.initialBytes[1] == 0xFE) { + if (this.initialBytes[2] == 0 && this.initialBytes[3] == 0) { + msg = new ErrorMessage(MessageConstants.UTF8_HUL_7); + } else { + msg = new ErrorMessage(MessageConstants.UTF8_HUL_8); } + info.setMessage(msg); + info.setWellFormed(false); + return false; + } else if (this.initialBytes[0] == 0xFE && this.initialBytes[1] == 0xFF) { + msg = new ErrorMessage(MessageConstants.UTF8_HUL_9); + info.setMessage(msg); + info.setWellFormed(false); return false; + } } - - /* Set property reporting line ending type */ - private List reportLineEndings() { - List retVal = new ArrayList<>(); - if (!this.usedLineEndings.isEmpty()) { - for (LineEnding le : EnumSet.copyOf(this.usedLineEndings)) { - retVal.add(le.toString()); - this._textMD.setLinebreak(le.textMdVal); - } - } - return retVal; + return false; + } + + /* Set property reporting line ending type */ + private List reportLineEndings() { + List retVal = new ArrayList<>(); + if (!this.usedLineEndings.isEmpty()) { + for (LineEnding le : EnumSet.copyOf(this.usedLineEndings)) { + retVal.add(le.toString()); + this._textMD.setLinebreak(le.textMdVal); + } } + return retVal; + } } diff --git a/jhove-modules/utf8-hul/src/main/java/edu/harvard/hul/ois/jhove/module/utf8/MessageConstants.java b/jhove-modules/utf8-hul/src/main/java/edu/harvard/hul/ois/jhove/module/utf8/MessageConstants.java index aa03c595b..fe98117c9 100644 --- a/jhove-modules/utf8-hul/src/main/java/edu/harvard/hul/ois/jhove/module/utf8/MessageConstants.java +++ b/jhove-modules/utf8-hul/src/main/java/edu/harvard/hul/ois/jhove/module/utf8/MessageConstants.java @@ -1,6 +1,4 @@ -/** - * - */ +/** */ package edu.harvard.hul.ois.jhove.module.utf8; import edu.harvard.hul.ois.jhove.messages.JhoveMessage; @@ -8,44 +6,42 @@ import edu.harvard.hul.ois.jhove.messages.JhoveMessages; /** - * Enum used to externalise the UTF8 module message Strings. Using an enum - * INSTANCE as a "trick" to ensure a single instance of the class. String - * constants should be prefixed according to their use in the module: + * Enum used to externalise the UTF8 module message Strings. Using an enum INSTANCE as a "trick" to + * ensure a single instance of the class. String constants should be prefixed according to their use + * in the module: + * *

    - *
  • WRN_ for warning strings, often logger messages.
  • - *
  • INF_ for informational messages.
  • - *
  • ERR_ for error messages that indicate a file is invalid or not well - * formed.
  • + *
  • WRN_ for warning strings, often logger messages. + *
  • INF_ for informational messages. + *
  • ERR_ for error messages that indicate a file is invalid or not well formed. *
- * When adding new messages try to adopt the following order for the naming - * elements: + * + * When adding new messages try to adopt the following order for the naming elements: + * *
    - *
  1. PREFIX: one of the three prefixes from the list above.
  2. - *
  3. ENTITY_NAME: the name of the entity causing the problem.
  4. - *
  5. Problem: a short indicator of the problem type, e.g. MISSING, ILLEGAL, - * etc.
  6. + *
  7. PREFIX: one of the three prefixes from the list above. + *
  8. ENTITY_NAME: the name of the entity causing the problem. + *
  9. Problem: a short indicator of the problem type, e.g. MISSING, ILLEGAL, etc. *
- * The elements should be separated by underscores. The messages currently don't - * follow a consistent vocabulary, that is terms such as invalid, illegal, or - * malformed are used without definition. + * + * The elements should be separated by underscores. The messages currently don't follow a consistent + * vocabulary, that is terms such as invalid, illegal, or malformed are used without definition. * * @author Thomas Ledoux - * */ - public enum MessageConstants { - INSTANCE; + INSTANCE; - public static final JhoveMessageFactory messageFactory = JhoveMessages - .getInstance("edu.harvard.hul.ois.jhove.module.utf8.ErrorMessages"); + public static final JhoveMessageFactory messageFactory = + JhoveMessages.getInstance("edu.harvard.hul.ois.jhove.module.utf8.ErrorMessages"); - public static final JhoveMessage UTF8_HUL_1 = messageFactory.getMessage("UTF8-HUL-1"); - public static final JhoveMessage UTF8_HUL_2 = messageFactory.getMessage("UTF8-HUL-2"); - public static final JhoveMessage UTF8_HUL_3 = messageFactory.getMessage("UTF8-HUL-3"); - public static final JhoveMessage UTF8_HUL_4 = messageFactory.getMessage("UTF8-HUL-4"); - public static final JhoveMessage UTF8_HUL_5 = messageFactory.getMessage("UTF8-HUL-5"); - public static final JhoveMessage UTF8_HUL_6 = messageFactory.getMessage("UTF8-HUL-6"); - public static final JhoveMessage UTF8_HUL_7 = messageFactory.getMessage("UTF8-HUL-7"); - public static final JhoveMessage UTF8_HUL_8 = messageFactory.getMessage("UTF8-HUL-8"); - public static final JhoveMessage UTF8_HUL_9 = messageFactory.getMessage("UTF8-HUL-9"); + public static final JhoveMessage UTF8_HUL_1 = messageFactory.getMessage("UTF8-HUL-1"); + public static final JhoveMessage UTF8_HUL_2 = messageFactory.getMessage("UTF8-HUL-2"); + public static final JhoveMessage UTF8_HUL_3 = messageFactory.getMessage("UTF8-HUL-3"); + public static final JhoveMessage UTF8_HUL_4 = messageFactory.getMessage("UTF8-HUL-4"); + public static final JhoveMessage UTF8_HUL_5 = messageFactory.getMessage("UTF8-HUL-5"); + public static final JhoveMessage UTF8_HUL_6 = messageFactory.getMessage("UTF8-HUL-6"); + public static final JhoveMessage UTF8_HUL_7 = messageFactory.getMessage("UTF8-HUL-7"); + public static final JhoveMessage UTF8_HUL_8 = messageFactory.getMessage("UTF8-HUL-8"); + public static final JhoveMessage UTF8_HUL_9 = messageFactory.getMessage("UTF8-HUL-9"); } diff --git a/jhove-modules/utf8-hul/src/main/java/edu/harvard/hul/ois/jhove/module/utf8/Utf8Block.java b/jhove-modules/utf8-hul/src/main/java/edu/harvard/hul/ois/jhove/module/utf8/Utf8Block.java index 210af86d0..a38e83b4e 100644 --- a/jhove-modules/utf8-hul/src/main/java/edu/harvard/hul/ois/jhove/module/utf8/Utf8Block.java +++ b/jhove-modules/utf8-hul/src/main/java/edu/harvard/hul/ois/jhove/module/utf8/Utf8Block.java @@ -1,234 +1,233 @@ -/********************************************************************** - * Jhove - JSTOR/Harvard Object Validation Environment - * Copyright 2003 by JSTOR and the President and Fellows of Harvard College - **********************************************************************/ - +/** + * ******************************************************************** Jhove - JSTOR/Harvard Object + * Validation Environment Copyright 2003 by JSTOR and the President and Fellows of Harvard College + * ******************************************************************** + */ package edu.harvard.hul.ois.jhove.module.utf8; - /** - * This class encapsulates a Unicode code block. - * - * Updated to Unicode 7.0.0. + * This class encapsulates a Unicode code block. + * + *

Updated to Unicode 7.0.0. * - * @see edu.harvard.hul.ois.jhove.module.Utf8Module + * @see edu.harvard.hul.ois.jhove.module.Utf8Module */ public enum Utf8Block { - /* Unicode 6.0.0 blocks, derived from - * <http://www.unicode.org/Public/3.2-Update/Blocks-3.2.0.txt> - * and updated to Unicode 6.0.0 */ - LAT(0x0000, 0x007F, "Basic Latin"), - LAT_1_SUPP(0x0080, 0x00FF, "Latin-1 Supplement"), - LAT_1_EXT_A(0x0100, 0x017F, "Latin Extended-A"), - LAT_1_EXT_B(0x0180, 0x024F, "Latin Extended-B"), - IPA_EXT(0x0250, 0x02AF, "IPA Extensions"), - SPACE_MOD(0x02B0, 0x02FF, "Spacing Modifier Letters"), - COMB_DIACRITICAL(0x0300, 0x036F, "Combining Diacritical Marks"), - GREEK_COPTIC(0x0370, 0x03FF, "Greek and Coptic"), - CYRILLIC(0x0400, 0x04FF, "Cyrillic"), - CYRILLIC_SUPP(0x0500, 0x052F, "Cyrillic Supplementary"), - ARMENIAN(0x0530, 0x058F, "Armenian"), - HEBREW(0x0590, 0x05FF, "Hebrew"), - ARABIC(0x0600, 0x06FF, "Arabic"), - SYRIAC(0x0700, 0x074F, "Syriac"), - THAANA(0x0780, 0x07BF, "Thaana"), - NKO(0x07C0, 0x07FF, "NKo"), - MANDIAC(0x0840, 0x085F, "Mandaic"), - DEVANAGARI(0x0900, 0x097F, "Devanagari"), - BENGALI(0x0980, 0x09FF, "Bengali"), - GURMUKHI(0x0A00, 0x0A7F, "Gurmukhi"), - GUJARATI(0x0A80, 0x0AFF, "Gujarati"), - ORIYA(0x0B00, 0x0B7F, "Oriya"), - TAMIL(0x0B80, 0x0BFF, "Tamil"), - TELUGU(0x0C00, 0x0C7F, "Telugu"), - KANNADA(0x0C80, 0x0CFF, "Kannada"), - MALAYALAM(0x0D00, 0x0D7F, "Malayalam"), - SINHALA(0x0D80, 0x0DFF, "Sinhala"), - THAI(0x0E00, 0x0E7F, "Thai"), - LAO(0x0E80, 0x0EFF, "Lao"), - TIBETAN(0x0F00, 0x0FFF, "Tibetan"), - MYANMAR(0x1000, 0x109F, "Myanmar"), - GEORGIAN(0x10A0, 0x10FF, "Georgian"), - HANGUL_JAMO(0x1100, 0x11FF, "Hangul Jamo"), - ETHIOPOC(0x1200, 0x137F, "Ethiopic"), - CHEROKEE(0x13A0, 0x13FF, "Cherokee"), - UNFD_CNDN_ABRGNL_SYLL(0x1400, 0x167F, "Unified Canadian Aboriginal Syllabics"), - OGHAM(0x1680, 0x169F, "Ogham"), - RUNIC(0x16A0, 0x16FF, "Runic"), - TAGALOG(0x1700, 0x171F, "Tagalog"), - HANUNOO(0x1720, 0x173F, "Hanunoo"), - BUHID(0x1740, 0x175F, "Buhid"), - TADBANWA(0x1760, 0x177F, "Tagbanwa"), - KHMER(0x1780, 0x17FF, "Khmer"), - MONGOLIAN(0x1800, 0x18AF, "Mongolian"), - - /* 1900-1D7F new for 4.0 */ - LIMBU(0x1900, 0x194F, "Limbu"), - TAI_LE(0x1950, 0x197F, "Tai Le"), - KHMER_SYM(0x19E0, 0x19FF, "Khmer Symbols"), - - COMB_DIACRITICAL_EXT(0x1AB0, 0x1AFF, "Combining Diacritical Marks Extended"), // 7.0.0 - - BALINESE(0x1B00, 0x1B7F, "Balinese"), - BATAK(0x1BC0, 0x1BFF, "Batak"), - PHONETIC_EXT(0x1D00, 0x1D7F, "Phonetic Extensions"), - - LATIN_EXT_ADD(0x1E00, 0x1EFF, "Latin Extended Additional"), - GREEK_EXT(0x1F00, 0x1FFF, "Greek Extended"), - GENERAL_PUNCT(0x2000, 0x206F, "General Punctuation"), - SUPER_AND_SUB(0x2070, 0x209F, "Superscripts and Subscripts"), - CURRENCY_SYM(0x20A0, 0x20CF, "Currency Symbols"), - COMB_DIACRITICAL_SYM(0x20D0, 0x20FF, "Combining Diacritical Marks for Symbols"), - LETTERLIKE_SYM(0x2100, 0x214F, "Letterlike Symbols"), - NUMBER_FORMS(0x2150, 0x218F, "Number Forms"), - ARROWS(0x2190, 0x21FF, "Arrows"), - MATHS_OPS(0x2200, 0x22FF, "Mathematical Operators"), - MISC_TECH(0x2300, 0x23FF, "Miscellaneous Technical"), - CONTROL_PICS(0x2400, 0x243F, "Control Pictures"), - OCR(0x2440, 0x245F, "Optical Character Recognition"), - ENCL_ALPHANUMS(0x2460, 0x24FF, "Enclosed Alphanumerics"), - BOX_DRAWING(0x2500, 0x257F, "Box Drawing"), - BLOCK_ELEMS(0x2580, 0x259F, "Block Elements"), - GEOM_SHAPES(0x25A0, 0x25FF, "Geometric Shapes"), - MISC_SHAPES(0x2600, 0x26FF, "Miscellaneous Symbols"), - DINGBATS(0x2700, 0x27BF, "Dingbats"), - MISC_MATH_SYMS_a(0x27C0, 0x27EF, "Miscellaneous Mathematical Symbols-A"), - SUPP_ARROWS_A(0x27F0, 0x27FF, "Supplemental Arrows-A"), - BRAILLE_PATTS(0x2800, 0x28FF, "Braille Patterns"), - SUPP_ARROWS_B(0x2900, 0x297F, "Supplemental Arrows-B"), - MISC_MATH_SYMS_B(0x2980, 0x29FF, "Miscellaneous Mathematical Symbols-B"), - SUPP_MATHS_OPS(0x2A00, 0x2AFF, "Supplemental Mathematical Operators"), - LATIN_EXT_C(0x2C60, 0x2C7F, "Latin Extended-C"), - CJK_RADICALS_SUPP(0x2E80, 0x2EFF, "CJK Radicals Supplement"), - KANGXI_RADICALS(0x2F00, 0x2FDF, "Kangxi Radicals"), - IDEOGRAPHIC_DESC_CHARS(0x2FF0, 0x2FFF, "Ideographic Description Characters"), - CJK_SYMS_PUNCT(0x3000, 0x303F, "CJK Symbols and Punctuation"), - HIRAGANA(0x3040, 0x309F, "Hiragana"), - KATAKANA(0x30A0, 0x30FF, "Katakana"), - BOPOMOFO(0x3100, 0x312F, "Bopomofo"), - HANGUL_COMPAT_JAMO(0x3130, 0x318F, "Hangul Compatibility Jamo"), - KANBUN(0x3190, 0x319F, "Kanbun"), - BOPOMOFO_EXT(0x31A0, 0x31BF, "Bopomofo Extended"), - KATAKANA_PHONETIC_EXT(0x31F0, 0x31FF, "Katakana Phonetic Extensions"), - ENCL_CJK_LETT_MONTHS(0x3200, 0x32FF, "Enclosed CJK Letters and Months"), - CJK_COMPAT(0x3300, 0x33FF, "CJK Compatibility"), - CJK_UNIFIED_IDEOGRAPHS_EXT_A(0x3400, 0x4DBF, "CJK Unified Ideographs Extension A"), - - /* 4DC0-4DFF new for 4.0 */ - YIJING_HEX_SYMS(0x4DC0, 0x4DFF, "Yijing Hexagram Symbols"), - - CJK_UNIFIED_IDEOGRAPHS(0x4E00, 0x9FFF, "CJK Unified Ideographs"), - YI_SYLLABLES(0xA000, 0xA48F, "Yi Syllables"), - YI_RADICALS(0xA490, 0xA4CF, "Yi Radicals"), - LATIN_EXT_D(0xA720, 0xA7FF, "Latin Extended-D"), - PHAGS_PA(0xA840, 0xA87F, "Phags-pa"), - MYNAMAR_EXT_B(0xA9E0, 0xA9FF, "Myanmar Extended-B"), // 7.0.0 - ETHIOPIC_EXT_A(0xAB00, 0xAB2F, "Ethiopic Extended-A"), - LATIN_EXT_E(0xAB30, 0xAB6F, "Latin Extended-E"), // 7.0.0 - HANGUL_SYM(0xAC00, 0xD7AF, "Hangul Syllables"), - HIGH_SURROG(0xD800, 0xDB7F, "High Surrogates"), - HIGH_PRIVATE_SURROG(0xDB80, 0xDBFF, "High Private Use Surrogates"), - LOW_SURROG(0xDC00, 0xDFFF, "Low Surrogates"), - PRIVATE_USE(0xE000, 0xF8FF, "Private Use Area"), - CJK_COMPAT_IDEOGRAPH(0xF900, 0xFAFF, "CJK Compatibility Ideographs"), - ALPHA_PRES_FORMS_A(0xFB00, 0xFB4F, "Alphabetic Presentation Forms"), - ARABIC_PRES_FORMS_A(0xFB50, 0xFDFF, "Arabic Presentation Forms-A"), - VAR_SELS(0xFE00, 0xFE0F, "Variation Selectors"), - COMB_HALF_MARKS(0xFE20, 0xFE2F, "Combining Half Marks"), - CJK_COMPAT_FORMS(0xFE30, 0xFE4F, "CJK Compatibility Forms"), - SMALL_FORMS_VAR(0xFE50, 0xFE6F, "Small Form Variants"), - ARABIC_PRES_FORMS_B(0xFE70, 0xFEFF, "Arabic Presentation Forms-B"), - HALFWDTH_FULLWDTH_FORMS(0xFF00, 0xFFEF, "Halfwidth and Fullwidth Forms"), - SPECIALS(0xFFF0, 0xFFFF, "Specials"), - - LINEAR_B_SYLLABARY(0x10000, 0x1007F, "Linear B Syllabary"), - LINEAR_B_IDEOGRAMS(0x10080, 0x100FF, "Linear B Ideograms"), - AGEAN_NUMS(0x10100, 0x1013F, "Aegean Numbers"), - COPTIC_EPACT_NUMS(0x102E0, 0x102FF, "Coptic Epact Numbers"), // 7.0.0 - - OLD_ITALIC(0x10300, 0x1032F, "Old Italic"), - GOTHIC(0x10330, 0x1034F, "Gothic"), - OLD_PERMIC(0x10350, 0x1037F, "Old Permic"), // 7.0.0 - - UGARITIC(0x10380, 0x1039F, "Ugaritic"), - - DESERET(0x10400, 0x1044F, "Deseret"), - - SHAVIAN(0x10450, 0x1047F, "Shavian"), - OSMANYA(0x10480, 0x104AF, "Osmanya"), - ELBASAN(0x10500, 0x1052F, "Elbasan"), // 7.0.0 - CAUCASIAN_ALBANIAN(0x10530, 0x1056F, "Caucasian Albanian"), // 7.0.0 - LINEAR_A(0x10600, 0x1077F, "Linear A"), // 7.0.0 - CYPRIOT_SYLLAB(0x10800, 0x1083F, "Cypriot Syllabary"), - PALMYRENE(0x10860, 0x1087F, "Palmyrene"), // 7.0.0 - NABATAEAN(0x10880, 0x108AF, "Nabataean"), // 7.0.0 - PHOENICIAN(0x10900, 0x1091F, "Phoenician"), - OLD_NORTH_ARABIAN(0x10A80, 0x10A9F, "Old North Arabian"), // 7.0.0 - MANICHAEAN(0x10AC0, 0x10AFF, "Manichaean"), // 7.0.0 - PSALTER_PAHLAVI(0x10B80, 0x10BAF, "Psalter Pahlavi"), // 7.0.0 - BRHAMI(0x11000, 0x1107F, "Brahmi"), - MAHAJANI(0x11150, 0x1117F, "Mahajani"), // 7.0.0 - SINHALA_ARCHAIC_NUMS(0x111E0, 0x111FF, "Sinhala Archaic Numbers"), // 7.0.0 - KHOJKI(0x11200, 0x1124F, "Khojki"), // 7.0.0 - KHUDAWADI(0x112B0, 0x112FF, "Khudawadi"), // 7.0.0 - GRANTHA(0x11300, 0x1137F, "Grantha"), // 7.0.0 - TIRHUTA(0x11480, 0x114DF, "Tirhuta"), // 7.0.0 - SIDDHAM(0x11580, 0x115FF, "Siddham"), // 7.0.0 - MODI(0x11600, 0x1165F, "Modi"), // 7.0.0 - WARANG_CITI(0x118A0, 0x118FF, "Warang Citi"), // 7.0.0 - PAU_CIN_HAU(0x11AC0, 0x11AFF, "Pau Cin Hau"), // 7.0.0 - CUNEIFORM(0x12000, 0x120FF, "Cuneiform"), - BAMUM_SUPP(0x16800, 0x168BF, "Bamum Supplement"), - MRO(0x16A40, 0x16A6F, "Mro"), // 7.0.0 - BASSA_VAH(0x16AD0, 0x16AFF, "Bassa Vah"), // 7.0.0 - PAHAWH_HMONG(0x16B00, 0x16B8F, "Pahawh Hmong"), // 7.0.0 - KANA_SUPP(0x1B000, 0x1B0FF, "Kana Supplement"), - DUPLOYAN(0x1BC00, 0x1BC9F, "Duployan"), // 7.0.0 - SHORTHAND_FORMAT_CTRLS(0x1BCA0, 0x1BCAF, "Shorthand Format Controls"), // 7.0.0 - - BYZANT_MUSIC_SYMS(0x1D000, 0x1D0FF, "Byzantine Musical Symbols"), - MUSIC_SYMS(0x1D100, 0x1D1FF, "Musical Symbols"), - COUNT_ROD_NUMS(0x1D360, 0x1D37F, "Counting Rod Numerals"), - MATHS_ALPAHNUM_SYMS(0x1D400, 0x1D7FF, "Mathematical Alphanumeric Symbols"), - MENDE_KIKAKUI(0x1E800, 0x1E8DF, "Mende Kikakui"), // 7.0.0 - PLAYING_CARDS(0x1F0A0, 0x1F0FF, "Playing Cards"), - MISC_SUMS_PICTOGRPHS(0x1F300, 0x1F3FF, "Miscellaneous Symbols and Pictographs"), - EMOTICONS(0x1F600, 0x1F64F, "Emoticons"), - ORNAMENTAL_DINGBATS(0x1F650, 0x1F67F, "Ornamental Dingbats"), // 7.0.0 - TRANSPORT_MAPS_SYMS(0x1F680, 0x1F6FF, "Transport and Map Symbols"), - ALCHEMICAL_SYMS(0x1F700, 0x1F77F, "Alchemical Symbols"), - GEOM_SHAPES_EXT(0x1F780, 0x1F7FF, "Geometric Shapes Extended"), // 7.0.0 - SUPP_ARROWS_C(0x1F800, 0x1F8FF, "Supplemental Arrows-C"), // 7.0.0 - CJK_UNIFIED_IDEOGRAPHS_EXT_B(0x20000, 0x2A6DF, "CJK Unified Ideographs Extension B"), - CJK_UNIFIED_IDEOGRAPHS_EXT_D(0x2B740, 0x2B78F, "CJK Unified Ideographs Extension D"), - CJK_COMPAT_IDEOGRAPH_EXT(0x2F800, 0x2FA1F, "CJK Compatibility Ideographs Supplement"), - TAGS(0xE0000, 0xE007F, "Tags"), - - /* E0100-E01EF new for 4.0 */ - VAR_SELS_SUPP(0xE0100, 0xE01EF, "Variation Selectors Supplement"), - - SUPP_PRIV_USE_A(0xF0000, 0xFFFFF, "Supplementary Private Use Area-A"), - SUPP_PRIV_USE_b(0x100000, 0x10FFFF, "Supplementary Private Use Area-B"); - - /** End code. */ - public final int end; - /** Block name. */ - public final String name; - /** Start code. */ - public final int start; - - Utf8Block(final int start, final int end, final String name) { - this.start = start; - this.end = end; - this.name = name; - } - - public static Utf8Block blockFromInt(final int code) { - for (Utf8Block block : Utf8Block.values()) { - if (block.start <= code && block.end >= code) { - return block; - } - } - return null; - } + /* Unicode 6.0.0 blocks, derived from + * <http://www.unicode.org/Public/3.2-Update/Blocks-3.2.0.txt> + * and updated to Unicode 6.0.0 */ + LAT(0x0000, 0x007F, "Basic Latin"), + LAT_1_SUPP(0x0080, 0x00FF, "Latin-1 Supplement"), + LAT_1_EXT_A(0x0100, 0x017F, "Latin Extended-A"), + LAT_1_EXT_B(0x0180, 0x024F, "Latin Extended-B"), + IPA_EXT(0x0250, 0x02AF, "IPA Extensions"), + SPACE_MOD(0x02B0, 0x02FF, "Spacing Modifier Letters"), + COMB_DIACRITICAL(0x0300, 0x036F, "Combining Diacritical Marks"), + GREEK_COPTIC(0x0370, 0x03FF, "Greek and Coptic"), + CYRILLIC(0x0400, 0x04FF, "Cyrillic"), + CYRILLIC_SUPP(0x0500, 0x052F, "Cyrillic Supplementary"), + ARMENIAN(0x0530, 0x058F, "Armenian"), + HEBREW(0x0590, 0x05FF, "Hebrew"), + ARABIC(0x0600, 0x06FF, "Arabic"), + SYRIAC(0x0700, 0x074F, "Syriac"), + THAANA(0x0780, 0x07BF, "Thaana"), + NKO(0x07C0, 0x07FF, "NKo"), + MANDIAC(0x0840, 0x085F, "Mandaic"), + DEVANAGARI(0x0900, 0x097F, "Devanagari"), + BENGALI(0x0980, 0x09FF, "Bengali"), + GURMUKHI(0x0A00, 0x0A7F, "Gurmukhi"), + GUJARATI(0x0A80, 0x0AFF, "Gujarati"), + ORIYA(0x0B00, 0x0B7F, "Oriya"), + TAMIL(0x0B80, 0x0BFF, "Tamil"), + TELUGU(0x0C00, 0x0C7F, "Telugu"), + KANNADA(0x0C80, 0x0CFF, "Kannada"), + MALAYALAM(0x0D00, 0x0D7F, "Malayalam"), + SINHALA(0x0D80, 0x0DFF, "Sinhala"), + THAI(0x0E00, 0x0E7F, "Thai"), + LAO(0x0E80, 0x0EFF, "Lao"), + TIBETAN(0x0F00, 0x0FFF, "Tibetan"), + MYANMAR(0x1000, 0x109F, "Myanmar"), + GEORGIAN(0x10A0, 0x10FF, "Georgian"), + HANGUL_JAMO(0x1100, 0x11FF, "Hangul Jamo"), + ETHIOPOC(0x1200, 0x137F, "Ethiopic"), + CHEROKEE(0x13A0, 0x13FF, "Cherokee"), + UNFD_CNDN_ABRGNL_SYLL(0x1400, 0x167F, "Unified Canadian Aboriginal Syllabics"), + OGHAM(0x1680, 0x169F, "Ogham"), + RUNIC(0x16A0, 0x16FF, "Runic"), + TAGALOG(0x1700, 0x171F, "Tagalog"), + HANUNOO(0x1720, 0x173F, "Hanunoo"), + BUHID(0x1740, 0x175F, "Buhid"), + TADBANWA(0x1760, 0x177F, "Tagbanwa"), + KHMER(0x1780, 0x17FF, "Khmer"), + MONGOLIAN(0x1800, 0x18AF, "Mongolian"), + + /* 1900-1D7F new for 4.0 */ + LIMBU(0x1900, 0x194F, "Limbu"), + TAI_LE(0x1950, 0x197F, "Tai Le"), + KHMER_SYM(0x19E0, 0x19FF, "Khmer Symbols"), + + COMB_DIACRITICAL_EXT(0x1AB0, 0x1AFF, "Combining Diacritical Marks Extended"), // 7.0.0 + + BALINESE(0x1B00, 0x1B7F, "Balinese"), + BATAK(0x1BC0, 0x1BFF, "Batak"), + PHONETIC_EXT(0x1D00, 0x1D7F, "Phonetic Extensions"), + + LATIN_EXT_ADD(0x1E00, 0x1EFF, "Latin Extended Additional"), + GREEK_EXT(0x1F00, 0x1FFF, "Greek Extended"), + GENERAL_PUNCT(0x2000, 0x206F, "General Punctuation"), + SUPER_AND_SUB(0x2070, 0x209F, "Superscripts and Subscripts"), + CURRENCY_SYM(0x20A0, 0x20CF, "Currency Symbols"), + COMB_DIACRITICAL_SYM(0x20D0, 0x20FF, "Combining Diacritical Marks for Symbols"), + LETTERLIKE_SYM(0x2100, 0x214F, "Letterlike Symbols"), + NUMBER_FORMS(0x2150, 0x218F, "Number Forms"), + ARROWS(0x2190, 0x21FF, "Arrows"), + MATHS_OPS(0x2200, 0x22FF, "Mathematical Operators"), + MISC_TECH(0x2300, 0x23FF, "Miscellaneous Technical"), + CONTROL_PICS(0x2400, 0x243F, "Control Pictures"), + OCR(0x2440, 0x245F, "Optical Character Recognition"), + ENCL_ALPHANUMS(0x2460, 0x24FF, "Enclosed Alphanumerics"), + BOX_DRAWING(0x2500, 0x257F, "Box Drawing"), + BLOCK_ELEMS(0x2580, 0x259F, "Block Elements"), + GEOM_SHAPES(0x25A0, 0x25FF, "Geometric Shapes"), + MISC_SHAPES(0x2600, 0x26FF, "Miscellaneous Symbols"), + DINGBATS(0x2700, 0x27BF, "Dingbats"), + MISC_MATH_SYMS_a(0x27C0, 0x27EF, "Miscellaneous Mathematical Symbols-A"), + SUPP_ARROWS_A(0x27F0, 0x27FF, "Supplemental Arrows-A"), + BRAILLE_PATTS(0x2800, 0x28FF, "Braille Patterns"), + SUPP_ARROWS_B(0x2900, 0x297F, "Supplemental Arrows-B"), + MISC_MATH_SYMS_B(0x2980, 0x29FF, "Miscellaneous Mathematical Symbols-B"), + SUPP_MATHS_OPS(0x2A00, 0x2AFF, "Supplemental Mathematical Operators"), + LATIN_EXT_C(0x2C60, 0x2C7F, "Latin Extended-C"), + CJK_RADICALS_SUPP(0x2E80, 0x2EFF, "CJK Radicals Supplement"), + KANGXI_RADICALS(0x2F00, 0x2FDF, "Kangxi Radicals"), + IDEOGRAPHIC_DESC_CHARS(0x2FF0, 0x2FFF, "Ideographic Description Characters"), + CJK_SYMS_PUNCT(0x3000, 0x303F, "CJK Symbols and Punctuation"), + HIRAGANA(0x3040, 0x309F, "Hiragana"), + KATAKANA(0x30A0, 0x30FF, "Katakana"), + BOPOMOFO(0x3100, 0x312F, "Bopomofo"), + HANGUL_COMPAT_JAMO(0x3130, 0x318F, "Hangul Compatibility Jamo"), + KANBUN(0x3190, 0x319F, "Kanbun"), + BOPOMOFO_EXT(0x31A0, 0x31BF, "Bopomofo Extended"), + KATAKANA_PHONETIC_EXT(0x31F0, 0x31FF, "Katakana Phonetic Extensions"), + ENCL_CJK_LETT_MONTHS(0x3200, 0x32FF, "Enclosed CJK Letters and Months"), + CJK_COMPAT(0x3300, 0x33FF, "CJK Compatibility"), + CJK_UNIFIED_IDEOGRAPHS_EXT_A(0x3400, 0x4DBF, "CJK Unified Ideographs Extension A"), + + /* 4DC0-4DFF new for 4.0 */ + YIJING_HEX_SYMS(0x4DC0, 0x4DFF, "Yijing Hexagram Symbols"), + + CJK_UNIFIED_IDEOGRAPHS(0x4E00, 0x9FFF, "CJK Unified Ideographs"), + YI_SYLLABLES(0xA000, 0xA48F, "Yi Syllables"), + YI_RADICALS(0xA490, 0xA4CF, "Yi Radicals"), + LATIN_EXT_D(0xA720, 0xA7FF, "Latin Extended-D"), + PHAGS_PA(0xA840, 0xA87F, "Phags-pa"), + MYNAMAR_EXT_B(0xA9E0, 0xA9FF, "Myanmar Extended-B"), // 7.0.0 + ETHIOPIC_EXT_A(0xAB00, 0xAB2F, "Ethiopic Extended-A"), + LATIN_EXT_E(0xAB30, 0xAB6F, "Latin Extended-E"), // 7.0.0 + HANGUL_SYM(0xAC00, 0xD7AF, "Hangul Syllables"), + HIGH_SURROG(0xD800, 0xDB7F, "High Surrogates"), + HIGH_PRIVATE_SURROG(0xDB80, 0xDBFF, "High Private Use Surrogates"), + LOW_SURROG(0xDC00, 0xDFFF, "Low Surrogates"), + PRIVATE_USE(0xE000, 0xF8FF, "Private Use Area"), + CJK_COMPAT_IDEOGRAPH(0xF900, 0xFAFF, "CJK Compatibility Ideographs"), + ALPHA_PRES_FORMS_A(0xFB00, 0xFB4F, "Alphabetic Presentation Forms"), + ARABIC_PRES_FORMS_A(0xFB50, 0xFDFF, "Arabic Presentation Forms-A"), + VAR_SELS(0xFE00, 0xFE0F, "Variation Selectors"), + COMB_HALF_MARKS(0xFE20, 0xFE2F, "Combining Half Marks"), + CJK_COMPAT_FORMS(0xFE30, 0xFE4F, "CJK Compatibility Forms"), + SMALL_FORMS_VAR(0xFE50, 0xFE6F, "Small Form Variants"), + ARABIC_PRES_FORMS_B(0xFE70, 0xFEFF, "Arabic Presentation Forms-B"), + HALFWDTH_FULLWDTH_FORMS(0xFF00, 0xFFEF, "Halfwidth and Fullwidth Forms"), + SPECIALS(0xFFF0, 0xFFFF, "Specials"), + + LINEAR_B_SYLLABARY(0x10000, 0x1007F, "Linear B Syllabary"), + LINEAR_B_IDEOGRAMS(0x10080, 0x100FF, "Linear B Ideograms"), + AGEAN_NUMS(0x10100, 0x1013F, "Aegean Numbers"), + COPTIC_EPACT_NUMS(0x102E0, 0x102FF, "Coptic Epact Numbers"), // 7.0.0 + + OLD_ITALIC(0x10300, 0x1032F, "Old Italic"), + GOTHIC(0x10330, 0x1034F, "Gothic"), + OLD_PERMIC(0x10350, 0x1037F, "Old Permic"), // 7.0.0 + + UGARITIC(0x10380, 0x1039F, "Ugaritic"), + + DESERET(0x10400, 0x1044F, "Deseret"), + + SHAVIAN(0x10450, 0x1047F, "Shavian"), + OSMANYA(0x10480, 0x104AF, "Osmanya"), + ELBASAN(0x10500, 0x1052F, "Elbasan"), // 7.0.0 + CAUCASIAN_ALBANIAN(0x10530, 0x1056F, "Caucasian Albanian"), // 7.0.0 + LINEAR_A(0x10600, 0x1077F, "Linear A"), // 7.0.0 + CYPRIOT_SYLLAB(0x10800, 0x1083F, "Cypriot Syllabary"), + PALMYRENE(0x10860, 0x1087F, "Palmyrene"), // 7.0.0 + NABATAEAN(0x10880, 0x108AF, "Nabataean"), // 7.0.0 + PHOENICIAN(0x10900, 0x1091F, "Phoenician"), + OLD_NORTH_ARABIAN(0x10A80, 0x10A9F, "Old North Arabian"), // 7.0.0 + MANICHAEAN(0x10AC0, 0x10AFF, "Manichaean"), // 7.0.0 + PSALTER_PAHLAVI(0x10B80, 0x10BAF, "Psalter Pahlavi"), // 7.0.0 + BRHAMI(0x11000, 0x1107F, "Brahmi"), + MAHAJANI(0x11150, 0x1117F, "Mahajani"), // 7.0.0 + SINHALA_ARCHAIC_NUMS(0x111E0, 0x111FF, "Sinhala Archaic Numbers"), // 7.0.0 + KHOJKI(0x11200, 0x1124F, "Khojki"), // 7.0.0 + KHUDAWADI(0x112B0, 0x112FF, "Khudawadi"), // 7.0.0 + GRANTHA(0x11300, 0x1137F, "Grantha"), // 7.0.0 + TIRHUTA(0x11480, 0x114DF, "Tirhuta"), // 7.0.0 + SIDDHAM(0x11580, 0x115FF, "Siddham"), // 7.0.0 + MODI(0x11600, 0x1165F, "Modi"), // 7.0.0 + WARANG_CITI(0x118A0, 0x118FF, "Warang Citi"), // 7.0.0 + PAU_CIN_HAU(0x11AC0, 0x11AFF, "Pau Cin Hau"), // 7.0.0 + CUNEIFORM(0x12000, 0x120FF, "Cuneiform"), + BAMUM_SUPP(0x16800, 0x168BF, "Bamum Supplement"), + MRO(0x16A40, 0x16A6F, "Mro"), // 7.0.0 + BASSA_VAH(0x16AD0, 0x16AFF, "Bassa Vah"), // 7.0.0 + PAHAWH_HMONG(0x16B00, 0x16B8F, "Pahawh Hmong"), // 7.0.0 + KANA_SUPP(0x1B000, 0x1B0FF, "Kana Supplement"), + DUPLOYAN(0x1BC00, 0x1BC9F, "Duployan"), // 7.0.0 + SHORTHAND_FORMAT_CTRLS(0x1BCA0, 0x1BCAF, "Shorthand Format Controls"), // 7.0.0 + + BYZANT_MUSIC_SYMS(0x1D000, 0x1D0FF, "Byzantine Musical Symbols"), + MUSIC_SYMS(0x1D100, 0x1D1FF, "Musical Symbols"), + COUNT_ROD_NUMS(0x1D360, 0x1D37F, "Counting Rod Numerals"), + MATHS_ALPAHNUM_SYMS(0x1D400, 0x1D7FF, "Mathematical Alphanumeric Symbols"), + MENDE_KIKAKUI(0x1E800, 0x1E8DF, "Mende Kikakui"), // 7.0.0 + PLAYING_CARDS(0x1F0A0, 0x1F0FF, "Playing Cards"), + MISC_SUMS_PICTOGRPHS(0x1F300, 0x1F3FF, "Miscellaneous Symbols and Pictographs"), + EMOTICONS(0x1F600, 0x1F64F, "Emoticons"), + ORNAMENTAL_DINGBATS(0x1F650, 0x1F67F, "Ornamental Dingbats"), // 7.0.0 + TRANSPORT_MAPS_SYMS(0x1F680, 0x1F6FF, "Transport and Map Symbols"), + ALCHEMICAL_SYMS(0x1F700, 0x1F77F, "Alchemical Symbols"), + GEOM_SHAPES_EXT(0x1F780, 0x1F7FF, "Geometric Shapes Extended"), // 7.0.0 + SUPP_ARROWS_C(0x1F800, 0x1F8FF, "Supplemental Arrows-C"), // 7.0.0 + CJK_UNIFIED_IDEOGRAPHS_EXT_B(0x20000, 0x2A6DF, "CJK Unified Ideographs Extension B"), + CJK_UNIFIED_IDEOGRAPHS_EXT_D(0x2B740, 0x2B78F, "CJK Unified Ideographs Extension D"), + CJK_COMPAT_IDEOGRAPH_EXT(0x2F800, 0x2FA1F, "CJK Compatibility Ideographs Supplement"), + TAGS(0xE0000, 0xE007F, "Tags"), + + /* E0100-E01EF new for 4.0 */ + VAR_SELS_SUPP(0xE0100, 0xE01EF, "Variation Selectors Supplement"), + + SUPP_PRIV_USE_A(0xF0000, 0xFFFFF, "Supplementary Private Use Area-A"), + SUPP_PRIV_USE_b(0x100000, 0x10FFFF, "Supplementary Private Use Area-B"); + + /** End code. */ + public final int end; + /** Block name. */ + public final String name; + /** Start code. */ + public final int start; + + Utf8Block(final int start, final int end, final String name) { + this.start = start; + this.end = end; + this.name = name; + } + + public static Utf8Block blockFromInt(final int code) { + for (Utf8Block block : Utf8Block.values()) { + if (block.start <= code && block.end >= code) { + return block; + } + } + return null; + } } diff --git a/jhove-modules/utf8-hul/src/main/java/edu/harvard/hul/ois/jhove/module/utf8/Utf8BlockMarker.java b/jhove-modules/utf8-hul/src/main/java/edu/harvard/hul/ois/jhove/module/utf8/Utf8BlockMarker.java index 4cfd96015..95a6ba0d1 100644 --- a/jhove-modules/utf8-hul/src/main/java/edu/harvard/hul/ois/jhove/module/utf8/Utf8BlockMarker.java +++ b/jhove-modules/utf8-hul/src/main/java/edu/harvard/hul/ois/jhove/module/utf8/Utf8BlockMarker.java @@ -1,61 +1,52 @@ -/********************************************************************** - * Jhove - JSTOR/Harvard Object Validation Environment - * Copyright 2004 by JSTOR and the President and Fellows of Harvard College - **********************************************************************/ - +/** + * ******************************************************************** Jhove - JSTOR/Harvard Object + * Validation Environment Copyright 2004 by JSTOR and the President and Fellows of Harvard College + * ******************************************************************** + */ package edu.harvard.hul.ois.jhove.module.utf8; +import edu.harvard.hul.ois.jhove.Property; +import edu.harvard.hul.ois.jhove.PropertyArity; +import edu.harvard.hul.ois.jhove.PropertyType; import java.util.ArrayList; import java.util.EnumSet; import java.util.HashSet; import java.util.List; import java.util.Set; -import edu.harvard.hul.ois.jhove.Property; -import edu.harvard.hul.ois.jhove.PropertyArity; -import edu.harvard.hul.ois.jhove.PropertyType; - -/** - * - * @author Gary McGath - * - */ +/** @author Gary McGath */ public class Utf8BlockMarker { - private Set blocksUsed; + private Set blocksUsed; - public Utf8BlockMarker () { - this.blocksUsed = new HashSet<>(); - } + public Utf8BlockMarker() { + this.blocksUsed = new HashSet<>(); + } - public void markBlock (int code) { - Utf8Block block = Utf8Block.blockFromInt(code); - if (block == null) { - return; - } - this.blocksUsed.add(block); + public void markBlock(int code) { + Utf8Block block = Utf8Block.blockFromInt(code); + if (block == null) { + return; } - - /** Returns a Property listing the blocks that have been - * marked as used. If no blocks have been marked, - * returns null. */ - public Property getBlocksUsedProperty (String name) - { - if (this.blocksUsed.isEmpty()) { - return null; - } - List blocks = new ArrayList<>(); - for (Utf8Block block : EnumSet.copyOf(this.blocksUsed)) { - blocks.add(block.name); - } - return new Property(name, - PropertyType.STRING, - PropertyArity.LIST, - blocks); + this.blocksUsed.add(block); + } + + /** + * Returns a Property listing the blocks that have been marked as used. If no blocks have been + * marked, returns null. + */ + public Property getBlocksUsedProperty(String name) { + if (this.blocksUsed.isEmpty()) { + return null; } - - /** Clears all marked blocks. */ - public void reset () - { - this.blocksUsed.clear(); + List blocks = new ArrayList<>(); + for (Utf8Block block : EnumSet.copyOf(this.blocksUsed)) { + blocks.add(block.name); } + return new Property(name, PropertyType.STRING, PropertyArity.LIST, blocks); + } + + /** Clears all marked blocks. */ + public void reset() { + this.blocksUsed.clear(); + } } diff --git a/jhove-modules/utf8-hul/src/test/java/edu/harvard/hul/ois/jhove/module/TestUtils.java b/jhove-modules/utf8-hul/src/test/java/edu/harvard/hul/ois/jhove/module/TestUtils.java index cacd6fabf..cd2effc03 100644 --- a/jhove-modules/utf8-hul/src/test/java/edu/harvard/hul/ois/jhove/module/TestUtils.java +++ b/jhove-modules/utf8-hul/src/test/java/edu/harvard/hul/ois/jhove/module/TestUtils.java @@ -4,6 +4,9 @@ import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; +import edu.harvard.hul.ois.jhove.Message; +import edu.harvard.hul.ois.jhove.Module; +import edu.harvard.hul.ois.jhove.RepInfo; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; @@ -12,238 +15,220 @@ import java.io.RandomAccessFile; import java.net.URISyntaxException; -import edu.harvard.hul.ois.jhove.Message; -import edu.harvard.hul.ois.jhove.Module; -import edu.harvard.hul.ois.jhove.RepInfo; - /** - * Convenience methods to test that the result of JHOVE validation are as - * expected. The tests are: + * Convenience methods to test that the result of JHOVE validation are as expected. The tests are: + * *

    - *
  • The well formed result is equal to a pre-defined value.
  • - *
  • The is valid result is equal to a pre-defined value.
  • - *
  • The message list contains an expected message (optionally).
  • + *
  • The well formed result is equal to a pre-defined value. + *
  • The is valid result is equal to a pre-defined value. + *
  • The message list contains an expected message (optionally). *
* - * @author Carl Wilson - * carlwilson AT github + * @author Carl Wilson carlwilson AT github * @version 0.1 Created 14 Mar 2018:20:14:19 */ - public final class TestUtils { - public static final String MODULE_RESOURCE_BASE = "/edu/harvard/hul/ois/jhove/module/"; - public static final String EMPTY_FILE_PATH = MODULE_RESOURCE_BASE + "empty"; - - private TestUtils() { - // Keep out - throw new AssertionError("Should never be in constructor."); - } - - /** - * Convenience method that takes the path to a test resource and tests that - * the results of JHOVE are as expected. - * - * @param module - * a {@link edu.harvard.hul.ois.jhove.Module} instance - * to use to validate the resource. - * @param resToTest - * the String path of the resource to validate and test. - * @param expctWllFrmd - * the expected well formed value - * @param expctVld - * the expected is valid value - * @throws URISyntaxException - * when there's an issue converting the resource name to a path - */ - public static RepInfo testValidateResource(final Module module, - final String resToTest, final int expctWllFrmd, final int expctVld) throws URISyntaxException { - return testValidateResource(module, resToTest, expctWllFrmd, expctVld, null); - } - - /** - * - * @param module - * a {@link edu.harvard.hul.ois.jhove.Module} instance - * to use to validate the resource. - * @param resToTest - * the String path of the resource to validate and test. - * @param expctWllFrmd - * the expected well formed value - * @param expctVld - * the expected is valid value - * @param message - * a JHOVE validation string message expected to be found in the - * list of validation messages. If this parameter is null the - * test isn't performed. - * @throws URISyntaxException - * when there's an issue converting the resource name to a path - */ - public static RepInfo testValidateResource(final Module module, - final String resToTest, final int expctWllFrmd, final int expctVld, - final String message) throws URISyntaxException { - return testValidateResource(module, resToTest, expctWllFrmd, expctVld, message, true); - } - - /** - * - * @param module - * a {@link edu.harvard.hul.ois.jhove.Module} instance - * to use to validate the resource. - * @param resToTest - * the String path of the resource to validate and test. - * @param expctWllFrmd - * the expected well formed value - * @param expctVld - * the expected is valid value - * @param message - * a JHOVE validation string message which MUST be found in the - * list of validation messages if messMustBePresent is true. - * When messMustBePresent is false the message MUST NOT be - * found in the list of validation messages. - * If this parameter is null the test isn't performed. - * @param messMustBePresent - * if message is not null this param dictates whether the - * test expects the validation message to be in the report or not. - * @throws URISyntaxException - * when there's an issue converting the resource name to a path - */ - public static RepInfo testValidateResource(final Module module, - final String resToTest, final int expctWllFrmd, final int expctVld, - final String expctMessage, boolean messMustBePresent ) throws URISyntaxException { - File toTest = new File(TestUtils.class.getResource(resToTest).toURI()); - - return testValidateFile(module, toTest, expctWllFrmd, expctVld, - expctMessage, messMustBePresent); - } - - /** - * Method that takes a file and tests that the results of JHOVE are as - * expected. - * - * @param pdfModule - * a {@link edu.harvard.hul.ois.jhove.module.PdfModule} instance - * to use to validate the resource. - * @param fileToTest - * a Java File instance to validate - * @param expctWllFrmd - * the expected well formed value - * @param expctVld - * the expected is valid value - * @param expctMessage - * a JHOVE validation string message expected to be found in the - * list of validation messages. If this parameter is null the - * test isn't performed. - */ - public static RepInfo testValidateFile(final Module module, - final File fileToTest, final int expctWllFrmd, final int expctVld) { - return testValidateFile(module, fileToTest, expctWllFrmd, expctVld, null); - } - - public static RepInfo testValidateFile(final Module module, - final File fileToTest, final int expctWllFrmd, final int expctVld, - final String message) { - return testValidateFile(module, fileToTest, expctWllFrmd, expctVld, message, true); - } - - public static RepInfo testValidateFile(final Module module, - final File fileToTest, final int expctWllFrmd, final int expctVld, - final String message, boolean messMustBePresent) { - RepInfo info = parseTestFile(module, fileToTest); - testResult(info, expctWllFrmd, expctVld, message, messMustBePresent); - return info; - } - - private static void testResult(final RepInfo info, final int expctWllFrmd, - final int expctVld, final String message, boolean messMustBePresent) { - testWellFormed(info, expctWllFrmd); - testIsValid(info, expctVld); - if (message == null) { - return; - } - Message jhoveMessage = getMessageIfPresent(info, message); - if (messMustBePresent) { - if (jhoveMessage == null) { - System.out.println(String.format( - "Expected message: %s, not found.", message)); - outputMessages(info); - } - assertTrue("Expected message: " + message, jhoveMessage != null); - } else { - if (jhoveMessage != null) { - System.out.println(String.format( - "Unexpected message: %s, found.", jhoveMessage.getMessage())); - outputMessages(info); - } - assertTrue("Unexpected message: " + message, jhoveMessage == null); - } - } - - private static void outputMessages(final RepInfo info) { - System.out.println("Messages in Report Info:"); - for (Message mess : info.getMessage()) { - System.out.println(String.format(" - %s", mess.getMessage())); - } - } - - private static RepInfo parseTestFile(final Module module, - final File toTest) { - if (module.isRandomAccess()) { - return rafModuleTest(module, toTest); - } - return streamModuleTest(module, toTest); - } - - - private static RepInfo streamModuleTest(final Module fisModule, final File toTest) { - RepInfo info = new RepInfo(toTest.getName()); - try (InputStream fis = new FileInputStream(toTest)) { - int index = fisModule.parse(fis, info, 0); - while (index > 0) { - index = fisModule.parse(fis, info, 0); - } - } catch (FileNotFoundException excep) { - excep.printStackTrace(); - fail("Couldn't find file to test: " + toTest.getName()); - } catch (IOException excep) { - excep.printStackTrace(); - fail("IOException Reading: " + toTest.getName()); - } - return info; - } - - private static RepInfo rafModuleTest(final Module rafModule, final File toTest) { - RepInfo info = new RepInfo(toTest.getName()); - try (RandomAccessFile raf = new RandomAccessFile(toTest, "r")) { - rafModule.parse(raf, info); - } catch (FileNotFoundException excep) { - excep.printStackTrace(); - fail("Couldn't find file to test: " + toTest.getName()); - } catch (IOException excep) { - excep.printStackTrace(); - fail("IOException Reading: " + toTest.getName()); - } - return info; - } - - private static void testWellFormed(final RepInfo info, final int expctWllFrmd) { - String message = (expctWllFrmd == RepInfo.TRUE) - ? "Should be well formed." - : "Should NOT be well formed."; - assertEquals(message, expctWllFrmd, info.getWellFormed()); - } - - private static void testIsValid(final RepInfo info, final int expctVld) { - String message = (expctVld == RepInfo.TRUE) ? "Should be valid." - : "Should NOT be valid."; - assertEquals(message, expctVld, info.getValid()); - } - - private static Message getMessageIfPresent(final RepInfo info, final String expctMessage) { - for (Message message : info.getMessage()) { - if (message.getMessage().equals(expctMessage)) { - return message; - } - } - return null; - } + public static final String MODULE_RESOURCE_BASE = "/edu/harvard/hul/ois/jhove/module/"; + public static final String EMPTY_FILE_PATH = MODULE_RESOURCE_BASE + "empty"; + + private TestUtils() { + // Keep out + throw new AssertionError("Should never be in constructor."); + } + + /** + * Convenience method that takes the path to a test resource and tests that the results of JHOVE + * are as expected. + * + * @param module a {@link edu.harvard.hul.ois.jhove.Module} instance to use to validate the + * resource. + * @param resToTest the String path of the resource to validate and test. + * @param expctWllFrmd the expected well formed value + * @param expctVld the expected is valid value + * @throws URISyntaxException when there's an issue converting the resource name to a path + */ + public static RepInfo testValidateResource( + final Module module, final String resToTest, final int expctWllFrmd, final int expctVld) + throws URISyntaxException { + return testValidateResource(module, resToTest, expctWllFrmd, expctVld, null); + } + + /** + * @param module a {@link edu.harvard.hul.ois.jhove.Module} instance to use to validate the + * resource. + * @param resToTest the String path of the resource to validate and test. + * @param expctWllFrmd the expected well formed value + * @param expctVld the expected is valid value + * @param message a JHOVE validation string message expected to be found in the list of validation + * messages. If this parameter is null the test isn't performed. + * @throws URISyntaxException when there's an issue converting the resource name to a path + */ + public static RepInfo testValidateResource( + final Module module, + final String resToTest, + final int expctWllFrmd, + final int expctVld, + final String message) + throws URISyntaxException { + return testValidateResource(module, resToTest, expctWllFrmd, expctVld, message, true); + } + + /** + * @param module a {@link edu.harvard.hul.ois.jhove.Module} instance to use to validate the + * resource. + * @param resToTest the String path of the resource to validate and test. + * @param expctWllFrmd the expected well formed value + * @param expctVld the expected is valid value + * @param message a JHOVE validation string message which MUST be found in the list of validation + * messages if messMustBePresent is true. When messMustBePresent is false the message MUST NOT + * be found in the list of validation messages. If this parameter is null the test isn't + * performed. + * @param messMustBePresent if message is not null this param dictates whether the test expects + * the validation message to be in the report or not. + * @throws URISyntaxException when there's an issue converting the resource name to a path + */ + public static RepInfo testValidateResource( + final Module module, + final String resToTest, + final int expctWllFrmd, + final int expctVld, + final String expctMessage, + boolean messMustBePresent) + throws URISyntaxException { + File toTest = new File(TestUtils.class.getResource(resToTest).toURI()); + + return testValidateFile( + module, toTest, expctWllFrmd, expctVld, expctMessage, messMustBePresent); + } + + /** + * Method that takes a file and tests that the results of JHOVE are as expected. + * + * @param pdfModule a {@link edu.harvard.hul.ois.jhove.module.PdfModule} instance to use to + * validate the resource. + * @param fileToTest a Java File instance to validate + * @param expctWllFrmd the expected well formed value + * @param expctVld the expected is valid value + * @param expctMessage a JHOVE validation string message expected to be found in the list of + * validation messages. If this parameter is null the test isn't performed. + */ + public static RepInfo testValidateFile( + final Module module, final File fileToTest, final int expctWllFrmd, final int expctVld) { + return testValidateFile(module, fileToTest, expctWllFrmd, expctVld, null); + } + + public static RepInfo testValidateFile( + final Module module, + final File fileToTest, + final int expctWllFrmd, + final int expctVld, + final String message) { + return testValidateFile(module, fileToTest, expctWllFrmd, expctVld, message, true); + } + + public static RepInfo testValidateFile( + final Module module, + final File fileToTest, + final int expctWllFrmd, + final int expctVld, + final String message, + boolean messMustBePresent) { + RepInfo info = parseTestFile(module, fileToTest); + testResult(info, expctWllFrmd, expctVld, message, messMustBePresent); + return info; + } + + private static void testResult( + final RepInfo info, + final int expctWllFrmd, + final int expctVld, + final String message, + boolean messMustBePresent) { + testWellFormed(info, expctWllFrmd); + testIsValid(info, expctVld); + if (message == null) { + return; + } + Message jhoveMessage = getMessageIfPresent(info, message); + if (messMustBePresent) { + if (jhoveMessage == null) { + System.out.println(String.format("Expected message: %s, not found.", message)); + outputMessages(info); + } + assertTrue("Expected message: " + message, jhoveMessage != null); + } else { + if (jhoveMessage != null) { + System.out.println( + String.format("Unexpected message: %s, found.", jhoveMessage.getMessage())); + outputMessages(info); + } + assertTrue("Unexpected message: " + message, jhoveMessage == null); + } + } + + private static void outputMessages(final RepInfo info) { + System.out.println("Messages in Report Info:"); + for (Message mess : info.getMessage()) { + System.out.println(String.format(" - %s", mess.getMessage())); + } + } + + private static RepInfo parseTestFile(final Module module, final File toTest) { + if (module.isRandomAccess()) { + return rafModuleTest(module, toTest); + } + return streamModuleTest(module, toTest); + } + + private static RepInfo streamModuleTest(final Module fisModule, final File toTest) { + RepInfo info = new RepInfo(toTest.getName()); + try (InputStream fis = new FileInputStream(toTest)) { + int index = fisModule.parse(fis, info, 0); + while (index > 0) { + index = fisModule.parse(fis, info, 0); + } + } catch (FileNotFoundException excep) { + excep.printStackTrace(); + fail("Couldn't find file to test: " + toTest.getName()); + } catch (IOException excep) { + excep.printStackTrace(); + fail("IOException Reading: " + toTest.getName()); + } + return info; + } + + private static RepInfo rafModuleTest(final Module rafModule, final File toTest) { + RepInfo info = new RepInfo(toTest.getName()); + try (RandomAccessFile raf = new RandomAccessFile(toTest, "r")) { + rafModule.parse(raf, info); + } catch (FileNotFoundException excep) { + excep.printStackTrace(); + fail("Couldn't find file to test: " + toTest.getName()); + } catch (IOException excep) { + excep.printStackTrace(); + fail("IOException Reading: " + toTest.getName()); + } + return info; + } + + private static void testWellFormed(final RepInfo info, final int expctWllFrmd) { + String message = + (expctWllFrmd == RepInfo.TRUE) ? "Should be well formed." : "Should NOT be well formed."; + assertEquals(message, expctWllFrmd, info.getWellFormed()); + } + + private static void testIsValid(final RepInfo info, final int expctVld) { + String message = (expctVld == RepInfo.TRUE) ? "Should be valid." : "Should NOT be valid."; + assertEquals(message, expctVld, info.getValid()); + } + + private static Message getMessageIfPresent(final RepInfo info, final String expctMessage) { + for (Message message : info.getMessage()) { + if (message.getMessage().equals(expctMessage)) { + return message; + } + } + return null; + } } diff --git a/jhove-modules/utf8-hul/src/test/java/edu/harvard/hul/ois/jhove/module/Utf8Test.java b/jhove-modules/utf8-hul/src/test/java/edu/harvard/hul/ois/jhove/module/Utf8Test.java index 05f2a82fe..09bd6b565 100644 --- a/jhove-modules/utf8-hul/src/test/java/edu/harvard/hul/ois/jhove/module/Utf8Test.java +++ b/jhove-modules/utf8-hul/src/test/java/edu/harvard/hul/ois/jhove/module/Utf8Test.java @@ -1,14 +1,13 @@ package edu.harvard.hul.ois.jhove.module; +import edu.harvard.hul.ois.jhove.module.utf8.Utf8BlockTests; +import edu.harvard.hul.ois.jhove.module.utf8.Utf8ByteOrderTests; import org.junit.runner.RunWith; import org.junit.runners.Suite; import org.junit.runners.Suite.SuiteClasses; -import edu.harvard.hul.ois.jhove.module.utf8.Utf8BlockTests; -import edu.harvard.hul.ois.jhove.module.utf8.Utf8ByteOrderTests; - @RunWith(Suite.class) -@SuiteClasses({ Utf8BlockTests.class, Utf8ByteOrderTests.class }) +@SuiteClasses({Utf8BlockTests.class, Utf8ByteOrderTests.class}) public class Utf8Test { - // Empty test suite that runs the UTF-8 Module tests. + // Empty test suite that runs the UTF-8 Module tests. } diff --git a/jhove-modules/utf8-hul/src/test/java/edu/harvard/hul/ois/jhove/module/utf8/Utf8BlockTests.java b/jhove-modules/utf8-hul/src/test/java/edu/harvard/hul/ois/jhove/module/utf8/Utf8BlockTests.java index 4c7959544..8a024e692 100644 --- a/jhove-modules/utf8-hul/src/test/java/edu/harvard/hul/ois/jhove/module/utf8/Utf8BlockTests.java +++ b/jhove-modules/utf8-hul/src/test/java/edu/harvard/hul/ois/jhove/module/utf8/Utf8BlockTests.java @@ -4,32 +4,34 @@ import java.util.HashSet; import java.util.Set; - import org.junit.Test; public class Utf8BlockTests { - /** This test makes sure that the code blocks are declared in monotonically - * increasing order without overlap, each one has a positive range, - * and names aren't duplicated. - */ - @Test - public void testBlocks() { - int lastEnd = -1; - Set names = new HashSet<>(); - for (Utf8Block blk : Utf8Block.values()) { - int start = blk.start; - int end = blk.end; - String name = blk.name; - String note = "Bad block: " + name + ", " + - String.format ("%05X", Integer.valueOf(start)) + " = " + - String.format ("%05X", Integer.valueOf(end)); - assertTrue (note, end > start); - assertTrue (note, start > lastEnd); - assertFalse ("Duplicate name " + name, names.contains(name)); - lastEnd = end; - names.add (name); - } - } - + /** + * This test makes sure that the code blocks are declared in monotonically increasing order + * without overlap, each one has a positive range, and names aren't duplicated. + */ + @Test + public void testBlocks() { + int lastEnd = -1; + Set names = new HashSet<>(); + for (Utf8Block blk : Utf8Block.values()) { + int start = blk.start; + int end = blk.end; + String name = blk.name; + String note = + "Bad block: " + + name + + ", " + + String.format("%05X", Integer.valueOf(start)) + + " = " + + String.format("%05X", Integer.valueOf(end)); + assertTrue(note, end > start); + assertTrue(note, start > lastEnd); + assertFalse("Duplicate name " + name, names.contains(name)); + lastEnd = end; + names.add(name); + } + } } diff --git a/jhove-modules/utf8-hul/src/test/java/edu/harvard/hul/ois/jhove/module/utf8/Utf8ByteOrderTests.java b/jhove-modules/utf8-hul/src/test/java/edu/harvard/hul/ois/jhove/module/utf8/Utf8ByteOrderTests.java index 6f8c80c62..135dc52f4 100644 --- a/jhove-modules/utf8-hul/src/test/java/edu/harvard/hul/ois/jhove/module/utf8/Utf8ByteOrderTests.java +++ b/jhove-modules/utf8-hul/src/test/java/edu/harvard/hul/ois/jhove/module/utf8/Utf8ByteOrderTests.java @@ -1,49 +1,51 @@ package edu.harvard.hul.ois.jhove.module.utf8; -import java.net.URISyntaxException; - -import org.junit.Before; -import org.junit.Test; - import edu.harvard.hul.ois.jhove.JhoveBase; import edu.harvard.hul.ois.jhove.RepInfo; import edu.harvard.hul.ois.jhove.module.TestUtils; import edu.harvard.hul.ois.jhove.module.Utf8Module; +import java.net.URISyntaxException; +import org.junit.Before; +import org.junit.Test; /** - * @author Carl Wilson - * carlwilson AT github + * @author Carl Wilson carlwilson AT github */ - public class Utf8ByteOrderTests { - private static final String utf8ResourcePath = "/edu/harvard/hul/ois/jhove/module/utf8/"; - - private static final String utf8BomTest = utf8ResourcePath - + "bom-test.txt"; - private static final String utf8NoBomTest = utf8ResourcePath - + "no-bom-test.txt"; - private Utf8Module module; - - /** - * @throws java.lang.Exception - */ - @Before - public void setUp() throws Exception { - this.module = new Utf8Module(); - JhoveBase je = new JhoveBase(); - this.module.setBase(je); - } - - @Test - public final void testBom() throws URISyntaxException { - TestUtils.testValidateResource(this.module, utf8BomTest, RepInfo.TRUE, - RepInfo.TRUE, MessageConstants.UTF8_HUL_1.getMessage(), true); - } - - @Test - public final void testNoBom() throws URISyntaxException { - TestUtils.testValidateResource(this.module, utf8NoBomTest, RepInfo.TRUE, - RepInfo.TRUE, MessageConstants.UTF8_HUL_1.getMessage(), false); - } - + private static final String utf8ResourcePath = "/edu/harvard/hul/ois/jhove/module/utf8/"; + + private static final String utf8BomTest = utf8ResourcePath + "bom-test.txt"; + private static final String utf8NoBomTest = utf8ResourcePath + "no-bom-test.txt"; + private Utf8Module module; + + /** @throws java.lang.Exception */ + @Before + public void setUp() throws Exception { + this.module = new Utf8Module(); + JhoveBase je = new JhoveBase(); + this.module.setBase(je); + } + + @Test + public final void testBom() throws URISyntaxException { + TestUtils.testValidateResource( + this.module, + utf8BomTest, + RepInfo.TRUE, + RepInfo.TRUE, + MessageConstants.UTF8_HUL_1.getMessage(), + true); + } + + @Test + public final void testNoBom() throws URISyntaxException { + TestUtils.testValidateResource( + this.module, + utf8NoBomTest, + RepInfo.TRUE, + RepInfo.TRUE, + MessageConstants.UTF8_HUL_1.getMessage(), + false); + } } diff --git a/jhove-modules/utf8-hul/src/test/java/edu/harvard/hul/ois/jhove/module/utf8/Utf8EmptyFileTests.java b/jhove-modules/utf8-hul/src/test/java/edu/harvard/hul/ois/jhove/module/utf8/Utf8EmptyFileTests.java index fd13d0178..8297ac944 100644 --- a/jhove-modules/utf8-hul/src/test/java/edu/harvard/hul/ois/jhove/module/utf8/Utf8EmptyFileTests.java +++ b/jhove-modules/utf8-hul/src/test/java/edu/harvard/hul/ois/jhove/module/utf8/Utf8EmptyFileTests.java @@ -1,48 +1,49 @@ package edu.harvard.hul.ois.jhove.module.utf8; -import java.net.URISyntaxException; - -import org.junit.Before; -import org.junit.Test; - import edu.harvard.hul.ois.jhove.JhoveBase; import edu.harvard.hul.ois.jhove.RepInfo; import edu.harvard.hul.ois.jhove.module.TestUtils; import edu.harvard.hul.ois.jhove.module.Utf8Module; +import java.net.URISyntaxException; +import org.junit.Before; +import org.junit.Test; /** - * @author Carl Wilson - * carlwilson AT github + * @author Carl Wilson carlwilson AT github */ - public class Utf8EmptyFileTests { - private static final String utf8ResourcePath = "/edu/harvard/hul/ois/jhove/module/utf8/"; - - private static final String utf8NotEmpty = utf8ResourcePath - + "no-bom-test.txt"; - private Utf8Module module; - - /** - * @throws java.lang.Exception - */ - @Before - public void setUp() throws Exception { - this.module = new Utf8Module(); - JhoveBase je = new JhoveBase(); - this.module.setBase(je); - } - - @Test - public final void testNotEmpty() throws URISyntaxException { - TestUtils.testValidateResource(this.module, utf8NotEmpty, RepInfo.TRUE, - RepInfo.TRUE, MessageConstants.UTF8_HUL_6.getMessage(), false); - } - - @Test - public final void testEmpty() throws URISyntaxException { - TestUtils.testValidateResource(this.module, TestUtils.EMPTY_FILE_PATH, - RepInfo.FALSE, RepInfo.FALSE, - MessageConstants.UTF8_HUL_6.getMessage()); - } - + private static final String utf8ResourcePath = "/edu/harvard/hul/ois/jhove/module/utf8/"; + + private static final String utf8NotEmpty = utf8ResourcePath + "no-bom-test.txt"; + private Utf8Module module; + + /** @throws java.lang.Exception */ + @Before + public void setUp() throws Exception { + this.module = new Utf8Module(); + JhoveBase je = new JhoveBase(); + this.module.setBase(je); + } + + @Test + public final void testNotEmpty() throws URISyntaxException { + TestUtils.testValidateResource( + this.module, + utf8NotEmpty, + RepInfo.TRUE, + RepInfo.TRUE, + MessageConstants.UTF8_HUL_6.getMessage(), + false); + } + + @Test + public final void testEmpty() throws URISyntaxException { + TestUtils.testValidateResource( + this.module, + TestUtils.EMPTY_FILE_PATH, + RepInfo.FALSE, + RepInfo.FALSE, + MessageConstants.UTF8_HUL_6.getMessage()); + } } diff --git a/jhove-modules/wave-hul/src/main/java/WDump.java b/jhove-modules/wave-hul/src/main/java/WDump.java index cadf50d86..577dee960 100644 --- a/jhove-modules/wave-hul/src/main/java/WDump.java +++ b/jhove-modules/wave-hul/src/main/java/WDump.java @@ -1,23 +1,19 @@ -/********************************************************************** - * JDump - JSTOR/Harvard Object Validation Environment - * Copyright 2004-2005 by the President and Fellows of Harvard College +/** + * ******************************************************************** JDump - JSTOR/Harvard Object + * Validation Environment Copyright 2004-2005 by the President and Fellows of Harvard College * - * This program 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 2 of the License, or (at - * your option) any later version. - * - * This program 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. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 - * USA - **********************************************************************/ - + *

This program 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 2 + * of the License, or (at your option) any later version. + * + *

This program 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. + * + *

You should have received a copy of the GNU Lesser General Public License along with this + * program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA ******************************************************************** + */ import edu.harvard.hul.ois.jhove.*; import java.io.*; @@ -25,171 +21,148 @@ * Dump contents of WAVE file in human-readable format. * * @author Gary McGath - * */ public class WDump extends Dump { - /* Fixed value for first 4 bytes */ - private static final int[] sigByte = - { 0X52, 0X49, 0X46, 0X46 }; + /* Fixed value for first 4 bytes */ + private static final int[] sigByte = {0X52, 0X49, 0X46, 0X46}; - private static final boolean ENDIAN = false; /* little endian */ + private static final boolean ENDIAN = false; /* little endian */ - /****************************************************************** - * MAIN ENTRY POINT. - ******************************************************************/ + /** + * **************************************************************** MAIN ENTRY POINT. + * **************************************************************** + */ - /** - * Main entry point. - * @param args Command line arguments - */ - public static void main (String [] args) - { - if (args.length < 1) { - System.err.println ("usage: java WDump file"); - System.exit (-1); + /** + * Main entry point. + * + * @param args Command line arguments + */ + public static void main(String[] args) { + if (args.length < 1) { + System.err.println("usage: java WDump file"); + System.exit(-1); + } + try { + FileInputStream file = new FileInputStream(args[0]); + BufferedInputStream buffer = new BufferedInputStream(file); + DataInputStream stream = new DataInputStream(buffer); + long os = 0; + for (int i = 0; i < 4; i++) { + int ch; + ch = stream.readUnsignedByte(); + if (ch != sigByte[i]) { + System.out.println("No AIFF FORM header"); + System.exit(-2); } - try { - FileInputStream file = new FileInputStream (args[0]); - BufferedInputStream buffer = new BufferedInputStream (file); - DataInputStream stream = new DataInputStream (buffer); - long os = 0; - for (int i=0; i<4; i++) { - int ch; - ch = stream.readUnsignedByte(); - if (ch != sigByte[i]) { - System.out.println ("No AIFF FORM header"); - System.exit (-2); - } - os++; - } - long ckSize = ModuleBase.readUnsignedInt (stream, ENDIAN); - os += 4; - - // Read the file type - String formType = read4Chars (stream); - os += 4; + os++; + } + long ckSize = ModuleBase.readUnsignedInt(stream, ENDIAN); + os += 4; - System.out.println ("00000000: RIFF " + ckSize + ": " + formType); + // Read the file type + String formType = read4Chars(stream); + os += 4; - boolean endOfFile = false; - while (!endOfFile) { - // Read chunks - try { - // Read chunk name. - String ckID = read4Chars (stream); - // Read size (excluding chunk name and size fields) - ckSize = ModuleBase.readUnsignedInt (stream, ENDIAN); - System.out.print (leading (os, 8) + os + ": " + ckID + - " " + ckSize); - long alreadyRead = 0; - if ("fact".equals (ckID)) { - long sampleLength = - ModuleBase.readUnsignedInt (stream, ENDIAN); - System.out.print (": " + sampleLength); - alreadyRead = 4; - } - else if ("fmt ".equals (ckID)) { - int formatTag = ModuleBase.readUnsignedShort (stream, - ENDIAN); - int channels = ModuleBase.readUnsignedShort (stream, - ENDIAN); - long samplesPerSec = - ModuleBase.readUnsignedInt (stream, ENDIAN); - long avgBytesPerSec = - ModuleBase.readUnsignedInt (stream, ENDIAN); - int blockAlign = ModuleBase.readUnsignedShort (stream, - ENDIAN); - String hex = Integer.toHexString (formatTag); - System.out.print (": 0x" + leading (hex, 4) + hex + - " " + channels + " " + - samplesPerSec + " " + - avgBytesPerSec + " " + blockAlign); - alreadyRead = 14; - if (ckSize > 14) { - int bitsPerSample = - ModuleBase.readUnsignedShort (stream, ENDIAN); - System.out.print (" " + bitsPerSample); - alreadyRead = 16; - if (ckSize > 16) { - int size = - ModuleBase.readUnsignedShort (stream, - ENDIAN); - System.out.print (" " + size); - alreadyRead = 18; - if (size == 22) { - int validBitsPerSample = - ModuleBase.readUnsignedShort (stream, - ENDIAN); - long channelMask = - ModuleBase.readUnsignedInt (stream, - ENDIAN); - hex = Long.toHexString (channelMask); - System.out.print (" " + - validBitsPerSample + - " 0x" + - leading (hex, 8) + hex + - " 0x"); - for (int i=0; i<4; i++) { - long guid = - ModuleBase.readUnsignedInt ( - stream, - ENDIAN); - hex = Long.toHexString (guid); - System.out.print (leading (hex, 8) + - hex); - } - alreadyRead = 40; - } - } - } - } - System.out.println (); + System.out.println("00000000: RIFF " + ckSize + ": " + formType); - if ("list".equals (ckID) || "LIST".equals (ckID)) { - readNestedChunks (ckID, stream, ckSize, os + 8); - } - else { - stream.skipBytes ((int) (ckSize - alreadyRead)); - } - os += ckSize + 8; - } - catch (EOFException e) { - endOfFile = true; + boolean endOfFile = false; + while (!endOfFile) { + // Read chunks + try { + // Read chunk name. + String ckID = read4Chars(stream); + // Read size (excluding chunk name and size fields) + ckSize = ModuleBase.readUnsignedInt(stream, ENDIAN); + System.out.print(leading(os, 8) + os + ": " + ckID + " " + ckSize); + long alreadyRead = 0; + if ("fact".equals(ckID)) { + long sampleLength = ModuleBase.readUnsignedInt(stream, ENDIAN); + System.out.print(": " + sampleLength); + alreadyRead = 4; + } else if ("fmt ".equals(ckID)) { + int formatTag = ModuleBase.readUnsignedShort(stream, ENDIAN); + int channels = ModuleBase.readUnsignedShort(stream, ENDIAN); + long samplesPerSec = ModuleBase.readUnsignedInt(stream, ENDIAN); + long avgBytesPerSec = ModuleBase.readUnsignedInt(stream, ENDIAN); + int blockAlign = ModuleBase.readUnsignedShort(stream, ENDIAN); + String hex = Integer.toHexString(formatTag); + System.out.print( + ": 0x" + + leading(hex, 4) + + hex + + " " + + channels + + " " + + samplesPerSec + + " " + + avgBytesPerSec + + " " + + blockAlign); + alreadyRead = 14; + if (ckSize > 14) { + int bitsPerSample = ModuleBase.readUnsignedShort(stream, ENDIAN); + System.out.print(" " + bitsPerSample); + alreadyRead = 16; + if (ckSize > 16) { + int size = ModuleBase.readUnsignedShort(stream, ENDIAN); + System.out.print(" " + size); + alreadyRead = 18; + if (size == 22) { + int validBitsPerSample = ModuleBase.readUnsignedShort(stream, ENDIAN); + long channelMask = ModuleBase.readUnsignedInt(stream, ENDIAN); + hex = Long.toHexString(channelMask); + System.out.print( + " " + validBitsPerSample + " 0x" + leading(hex, 8) + hex + " 0x"); + for (int i = 0; i < 4; i++) { + long guid = ModuleBase.readUnsignedInt(stream, ENDIAN); + hex = Long.toHexString(guid); + System.out.print(leading(hex, 8) + hex); + } + alreadyRead = 40; } + } } + } + System.out.println(); + + if ("list".equals(ckID) || "LIST".equals(ckID)) { + readNestedChunks(ckID, stream, ckSize, os + 8); + } else { + stream.skipBytes((int) (ckSize - alreadyRead)); + } + os += ckSize + 8; + } catch (EOFException e) { + endOfFile = true; } - catch (Exception e) { - } + } + } catch (Exception e) { } - - /* The "list" and "LIST" chunks (which are two distinct chunk types) - hold nested chunks. */ - private static void readNestedChunks - (String ckID, DataInputStream stream, long ckSize, long os) - throws IOException - { - String listType = read4Chars (stream); - System.out.println ("List type = " + listType); - while (ckSize > 0) { - String subCkID = read4Chars (stream); - long subCkSize = ModuleBase.readUnsignedInt (stream, ENDIAN, null); - System.out.println (leading (os, 8) + os + ": " + - ckID + "/" + subCkID + " " + subCkSize); - stream.skipBytes ((int) subCkSize); - os += subCkSize + 8; - ckSize -= subCkSize + 8; - } + } + + /* The "list" and "LIST" chunks (which are two distinct chunk types) + hold nested chunks. */ + private static void readNestedChunks(String ckID, DataInputStream stream, long ckSize, long os) + throws IOException { + String listType = read4Chars(stream); + System.out.println("List type = " + listType); + while (ckSize > 0) { + String subCkID = read4Chars(stream); + long subCkSize = ModuleBase.readUnsignedInt(stream, ENDIAN, null); + System.out.println(leading(os, 8) + os + ": " + ckID + "/" + subCkID + " " + subCkSize); + stream.skipBytes((int) subCkSize); + os += subCkSize + 8; + ckSize -= subCkSize + 8; } - - private static String read4Chars (DataInputStream stream) - throws IOException - { - StringBuffer sbuf = new StringBuffer(4); - for (int i = 0; i < 4; i++) { - int ch = ModuleBase.readUnsignedByte(stream, null); - sbuf.append((char) ch); - } - return sbuf.toString (); + } + + private static String read4Chars(DataInputStream stream) throws IOException { + StringBuffer sbuf = new StringBuffer(4); + for (int i = 0; i < 4; i++) { + int ch = ModuleBase.readUnsignedByte(stream, null); + sbuf.append((char) ch); } + return sbuf.toString(); + } } diff --git a/jhove-modules/wave-hul/src/main/java/edu/harvard/hul/ois/jhove/module/WaveModule.java b/jhove-modules/wave-hul/src/main/java/edu/harvard/hul/ois/jhove/module/WaveModule.java index fe7b83546..5badbac8d 100644 --- a/jhove-modules/wave-hul/src/main/java/edu/harvard/hul/ois/jhove/module/WaveModule.java +++ b/jhove-modules/wave-hul/src/main/java/edu/harvard/hul/ois/jhove/module/WaveModule.java @@ -1,19 +1,10 @@ -/********************************************************************** - * JHOVE - JSTOR/Harvard Object Validation Environment - * Copyright 2004-2007 by JSTOR and the President and Fellows of Harvard College - **********************************************************************/ - +/** + * ******************************************************************** JHOVE - JSTOR/Harvard Object + * Validation Environment Copyright 2004-2007 by JSTOR and the President and Fellows of Harvard + * College ******************************************************************** + */ package edu.harvard.hul.ois.jhove.module; -import java.io.DataInputStream; -import java.io.EOFException; -import java.io.IOException; -import java.io.InputStream; -import java.util.HashMap; -import java.util.LinkedList; -import java.util.List; -import java.util.Map; - import edu.harvard.hul.ois.jhove.AESAudioMetadata; import edu.harvard.hul.ois.jhove.Agent; import edu.harvard.hul.ois.jhove.AgentType; @@ -55,935 +46,939 @@ import edu.harvard.hul.ois.jhove.module.wave.NoteChunk; import edu.harvard.hul.ois.jhove.module.wave.PeakEnvelopeChunk; import edu.harvard.hul.ois.jhove.module.wave.SampleChunk; +import java.io.DataInputStream; +import java.io.EOFException; +import java.io.IOException; +import java.io.InputStream; +import java.util.HashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; /** * Module for identification and validation of WAVE sound files. * - * There is no published specification for WAVE files; this module is based on - * several Internet sources. + *

There is no published specification for WAVE files; this module is based on several Internet + * sources. * - * WAVE format is a type of RIFF format. RIFF, in turn, is a variant on EA IFF - * 85. + *

WAVE format is a type of RIFF format. RIFF, in turn, is a variant on EA IFF 85. * * @author Gary McGath */ public class WaveModule extends ModuleBase { - /* Module metadata */ - private static final String NAME = "WAVE-hul"; - private static final String RELEASE = "1.8.1"; - private static final int [] DATE = { 2019, 12, 10 }; - private static final String[] FORMATS = { "WAVE", "Audio for Windows", - "EBU Technical Specification 3285", "Broadcast Wave Format", "BWF", - "EBU Technical Specification 3306", "RF64" }; - private static final String COVERAGE = "WAVE (PCMWAVEFORMAT, WAVEFORMATEX, WAVEFORMATEXTENSIBLE); " - + "Broadcast Wave Format (BWF) version 0, 1 and 2; RF64"; - private static final String[] MIMETYPES = { "audio/vnd.wave", "audio/wav", - "audio/wave", "audio/x-wav", "audio/x-wave" }; - private static final String WELLFORMED = null; - private static final String VALIDITY = null; - private static final String REPINFO = null; - private static final String NOTE = "There is no published standard for WAVE files. This module regards " - + "a file as valid if it conforms to common usage practices."; - private static final String RIGHTS = "Copyright 2004-2007 by JSTOR and the " - + "President and Fellows of Harvard College. " - + "Released under the GNU Lesser General Public License."; - - /** Fixed value for first four bytes of WAVE files */ - private static final String RIFF_SIGNATURE = "RIFF"; - - /** Fixed value for first four bytes of RF64 files */ - private static final String RF64_SIGNATURE = "RF64"; - - /** Length of the RIFF form type field in bytes */ - private static final int RIFF_FORM_TYPE_LENGTH = 4; - - /** Value indicating a required 64-bit data size lookup */ - public static final long LOOKUP_EXTENDED_DATA_SIZE = 0xFFFFFFFFL; - - /** - * Map of 64-bit chunk sizes found in the Data Size 64 chunk. - * Long size values should be treated as unsigned. - */ - protected Map extendedChunkSizes; - - /** Top-level metadata property */ - protected Property _metadata; - - /** Top-level property list */ - protected List _propList; - - /** List of Note properties */ - protected List _notes; + /* Module metadata */ + private static final String NAME = "WAVE-hul"; + private static final String RELEASE = "1.8.1"; + private static final int[] DATE = {2019, 12, 10}; + private static final String[] FORMATS = { + "WAVE", + "Audio for Windows", + "EBU Technical Specification 3285", + "Broadcast Wave Format", + "BWF", + "EBU Technical Specification 3306", + "RF64" + }; + private static final String COVERAGE = + "WAVE (PCMWAVEFORMAT, WAVEFORMATEX, WAVEFORMATEXTENSIBLE); " + + "Broadcast Wave Format (BWF) version 0, 1 and 2; RF64"; + private static final String[] MIMETYPES = { + "audio/vnd.wave", "audio/wav", "audio/wave", "audio/x-wav", "audio/x-wave" + }; + private static final String WELLFORMED = null; + private static final String VALIDITY = null; + private static final String REPINFO = null; + private static final String NOTE = + "There is no published standard for WAVE files. This module regards " + + "a file as valid if it conforms to common usage practices."; + private static final String RIGHTS = + "Copyright 2004-2007 by JSTOR and the " + + "President and Fellows of Harvard College. " + + "Released under the GNU Lesser General Public License."; - /** List of Label properties */ - protected List _labels; + /** Fixed value for first four bytes of WAVE files */ + private static final String RIFF_SIGNATURE = "RIFF"; - /** List of Labeled Text properties */ - protected List _labeledText; + /** Fixed value for first four bytes of RF64 files */ + private static final String RF64_SIGNATURE = "RF64"; - /** List of Sample properties */ - protected List _samples; + /** Length of the RIFF form type field in bytes */ + private static final int RIFF_FORM_TYPE_LENGTH = 4; - /** AES audio metadata to go into WAVE metadata */ - protected AESAudioMetadata _aesMetadata; + /** Value indicating a required 64-bit data size lookup */ + public static final long LOOKUP_EXTENDED_DATA_SIZE = 0xFFFFFFFFL; - /** RIFF size as found in the RIFF chunk header. */ - protected long riffSize; + /** + * Map of 64-bit chunk sizes found in the Data Size 64 chunk. Long size values should + * be treated as unsigned. + */ + protected Map extendedChunkSizes; - /** Bytes needed to store a file */ - protected int _blockAlign; + /** Top-level metadata property */ + protected Property _metadata; - /** Exif data from file */ - protected ExifInfo _exifInfo; + /** Top-level property list */ + protected List _propList; - /** WAVE codec, used for profile verification */ - protected int waveCodec; - - /** Extended (and unsigned) RIFF size as found in Data Size 64 chunk */ - protected long extendedRiffSize; - - /** Extended (and unsigned) sample length as found in Data Size 64 chunk */ - protected long extendedSampleLength; - - /** - * Number of samples in the file. Obtained from the Data chunk for - * uncompressed files, and the Fact chunk for compressed ones. Value - * should be treated as unsigned. - */ - protected long sampleCount; - - /** Sample rate from file */ - protected long sampleRate; - - /** Flag to check for exactly one Format chunk */ - protected boolean formatChunkSeen; - - /** Flag to check for presence of a Fact chunk */ - protected boolean factChunkSeen; - - /** Flag to check for not more than one Data chunk */ - protected boolean dataChunkSeen; - - /** Flag to check for a Data Size 64 chunk */ - protected boolean dataSize64ChunkSeen; - - /** Flag to check for not more than one Instrument chunk */ - protected boolean instrumentChunkSeen; - - /** Flag to check for not more than one MPEG chunk */ - protected boolean mpegChunkSeen; - - /** Flag to check for not more than one Cart chunk */ - protected boolean cartChunkSeen; - - /** Flag to check for not more than one Broadcast Audio Extension chunk */ - protected boolean broadcastExtChunkSeen; - - /** Flag to check for not more than one Peak Envelope chunk */ - protected boolean peakChunkSeen; - - /** Flag to check for not more than one Link chunk */ - protected boolean linkChunkSeen; - - /** Flag to check for not more than one Cue chunk */ - protected boolean cueChunkSeen; - - /** Profile flag for PCMWAVEFORMAT */ - protected boolean flagPCMWaveFormat; - - /** Profile flag for WAVEFORMATEX */ - protected boolean flagWaveFormatEx; - - /** Profile flag for WAVEFORMATEXTENSIBLE */ - protected boolean flagWaveFormatExtensible; - - /** Profile flag for RF64 */ - protected boolean flagRF64; - - /** Flag to note that first sample offset has been recorded */ - protected boolean firstSampleOffsetMarked; - - /** - * Class constructor. - * - * Instantiates a WaveModule object. - */ - public WaveModule() { - super(NAME, RELEASE, DATE, FORMATS, COVERAGE, MIMETYPES, WELLFORMED, - VALIDITY, REPINFO, NOTE, RIGHTS, false); - _vendor = Agent.harvardInstance(); - - Agent msAgent = new Agent.Builder("Microsoft Corporation", - AgentType.COMMERCIAL) - .address("One Microsoft Way, Redmond, WA 98052-6399") - .telephone("+1 (800) 426-9400") - .web("http://www.microsoft.com").build(); - - Document doc = new Document("PCMWAVEFORMAT", DocumentType.WEB); - doc.setIdentifier(new Identifier( - "http://msdn.microsoft.com/library/default.asp?url=/library/en-us/" - + "multimed/htm/_win32_pcmwaveformat_str.asp", - IdentifierType.URL)); - doc.setPublisher(msAgent); - _specification.add(doc); - - doc = new Document("WAVEFORMATEX", DocumentType.WEB); - doc.setIdentifier(new Identifier( - "http://msdn.microsoft.com/library/default.asp?url=/library/en-us/" - + "multimed/htm/_win32_waveformatex_str.asp", - IdentifierType.URL)); - doc.setPublisher(msAgent); - _specification.add(doc); - - doc = new Document("WAVEFORMATEXTENSIBLE", DocumentType.WEB); - doc.setIdentifier(new Identifier( - "http://msdn.microsoft.com/library/default.asp?url=/library/en-us/" - + "multimed/htm/_win32_waveformatextensible_str.asp", - IdentifierType.URL)); - doc.setPublisher(msAgent); - _specification.add(doc); - - Agent ebuAgent = new Agent.Builder("European Broadcasting Union", - AgentType.COMMERCIAL) - .address("Casa postale 45, Ancienne Route 17A, " - + "CH-1218 Grand-Saconex, Geneva, Switzerland") - .telephone("+41 (0)22 717 2111") - .fax("+41 (0)22 747 4000").email("techreview@ebu.ch") - .web("http://www.ebu.ch").build(); - - doc = new Document("Specification of the Broadcast Wave Format (BWF)", - DocumentType.REPORT); - doc.setIdentifier(new Identifier("EBU Technical Specification 3285", - IdentifierType.OTHER)); - doc.setIdentifier( - new Identifier("https://tech.ebu.ch/docs/tech/tech3285.pdf", - IdentifierType.URL)); - doc.setPublisher(ebuAgent); - doc.setDate("2011-05"); - _specification.add(doc); - - doc = new Document("MBWF / RF64: An Extended File Format for Audio", - DocumentType.REPORT); - doc.setIdentifier(new Identifier("EBU Technical Specification 3306", - IdentifierType.OTHER)); - doc.setIdentifier(new Identifier( - "https://tech.ebu.ch/docs/tech/tech3306-2009.pdf", - IdentifierType.URL)); - doc.setPublisher(ebuAgent); - doc.setDate("2009-07"); - _specification.add(doc); - - Agent ietfAgent = new Agent.Builder("IETF", AgentType.STANDARD) - .web("https://www.ietf.org").build(); - - doc = new Document("WAVE and AVI Codec Registries", DocumentType.RFC); - doc.setPublisher(ietfAgent); - doc.setDate("1998-06"); - doc.setIdentifier(new Identifier("RFC 2361", IdentifierType.RFC)); - doc.setIdentifier(new Identifier("https://www.ietf.org/rfc/rfc2361.txt", - IdentifierType.URL)); - _specification.add(doc); - - Signature sig = new ExternalSignature(".wav", SignatureType.EXTENSION, - SignatureUseType.OPTIONAL); - _signature.add(sig); - - sig = new ExternalSignature(".bwf", SignatureType.EXTENSION, - SignatureUseType.OPTIONAL, "For BWF profile"); - _signature.add(sig); - - sig = new ExternalSignature(".rf64", SignatureType.EXTENSION, - SignatureUseType.OPTIONAL, "For RF64 profile"); - _signature.add(sig); - - sig = new InternalSignature("RIFF", SignatureType.MAGIC, - SignatureUseType.MANDATORY_IF_APPLICABLE, 0); - _signature.add(sig); - - sig = new InternalSignature("RF64", SignatureType.MAGIC, - SignatureUseType.MANDATORY_IF_APPLICABLE, 0); - _signature.add(sig); - - sig = new InternalSignature("WAVE", SignatureType.MAGIC, - SignatureUseType.MANDATORY, 8); - _signature.add(sig); - - _bigEndian = false; - } - - /** - * Parses the content of a purported WAVE digital object and stores the - * results in RepInfo. - * - * @param stream - * An InputStream, positioned at its beginning, which is - * generated from the object to be parsed - * @param info - * A fresh RepInfo object which will be modified to reflect the - * results of the parsing - * @param parseIndex - * Must be 0 in first call to parse. If - * parse returns a nonzero value, it must be called - * again with parseIndex equal to that return value. - */ - @Override - public int parse(InputStream stream, RepInfo info, int parseIndex) { - initParse(); - info.setModule(this); - - _aesMetadata.setPrimaryIdentifier(info.getUri()); - if (info.getURLFlag()) { - _aesMetadata.setOtherPrimaryIdentifierType("URI"); - } else { - _aesMetadata.setPrimaryIdentifierType(AESAudioMetadata.FILE_NAME); - } - - // We may have already done the checksums - // while converting a temporary file. - setupDataStream(stream, info); - - try { - // Check the start of the file for the right opening bytes - String firstFourChars = read4Chars(_dstream); - if (firstFourChars.equals(RF64_SIGNATURE)) { - info.setProfile("RF64"); - flagRF64 = true; - } else if (!firstFourChars.equals(RIFF_SIGNATURE)) { - info.setMessage( - new ErrorMessage(MessageConstants.WAVE_HUL_1, 0)); - info.setWellFormed(false); - return 0; - } - - // Get the length of the Form chunk. This includes all - // subsequent form fields and form subchunks, but excludes - // the form chunk's header (its ID and the its length). - riffSize = readUnsignedInt(_dstream); - - // Read the RIFF form type - String formType = read4Chars(_dstream); - if (!"WAVE".equals(formType)) { - info.setMessage(new ErrorMessage( - MessageConstants.WAVE_HUL_2, - _nByte - RIFF_FORM_TYPE_LENGTH)); - info.setWellFormed(false); - return 0; - } - - // If we get this far, the signature is OK. - info.setSigMatch(_name); - info.setFormat(_format[0]); - info.setMimeType(_mimeType[0]); - - if (flagRF64) { - // For RF64 files the first chunk should be a Data Size 64 chunk - // containing the extended data sizes for a number of elements. - if (readChunk(info) && dataSize64ChunkSeen) { - if (riffSize == LOOKUP_EXTENDED_DATA_SIZE) { - // Even though RF64 can support files larger than - // Long.MAX_VALUE, this module currently does not. - if (Long.compareUnsigned(extendedRiffSize, - Long.MAX_VALUE) > 0) { - info.setMessage(new InfoMessage( - MessageConstants.WAVE_HUL_22)); - info.setWellFormed(RepInfo.UNDETERMINED); - return 0; - } - } - } else { - info.setMessage(new ErrorMessage( - MessageConstants.WAVE_HUL_23, - Chunk.HEADER_LENGTH + RIFF_FORM_TYPE_LENGTH)); - info.setWellFormed(false); - return 0; - } - } - - while (getBytesRemaining() > 0) { - if (!readChunk(info)) { - break; - } - } - - if (getBytesRemaining() > 0) { - // The file has been truncated or there - // remains unexpected chunk data to skip - remainingDataInfo(_dstream, info, getBytesRemaining(), - firstFourChars); - } - - } catch (EOFException eofe) { - info.setWellFormed(false); - String subMessage = String.format( - MessageConstants.WAVE_HUL_3_SUB.getMessage(), - getBytesRemaining()); - if (eofe.getMessage() != null) { - subMessage += "; " + eofe.getMessage(); - } - info.setMessage(new ErrorMessage(MessageConstants.WAVE_HUL_3, - subMessage, _nByte)); - } catch (Exception e) { // TODO make this more specific - e.printStackTrace(); - info.setWellFormed(false); - JhoveMessage message = JhoveMessages.getMessageInstance( - MessageConstants.WAVE_HUL_4.getId(), - String.format(MessageConstants.WAVE_HUL_4.getMessage(), - e.getClass().getName() + ", " + e.getMessage())); - info.setMessage(new ErrorMessage(message, _nByte)); - return 0; - } - - // Set duration from number of samples and rate. - if (sampleCount > 0) { - _aesMetadata.setDuration(sampleCount); - } - - // Add note and label properties, if there's anything to report. - if (!_labels.isEmpty()) { - _propList.add(new Property("Labels", PropertyType.PROPERTY, - PropertyArity.LIST, _labels)); - } - if (!_labeledText.isEmpty()) { - _propList.add(new Property("LabeledText", PropertyType.PROPERTY, - PropertyArity.LIST, _labeledText)); - } - if (!_notes.isEmpty()) { - _propList.add(new Property("Notes", PropertyType.PROPERTY, - PropertyArity.LIST, _notes)); - } - if (!_samples.isEmpty()) { - _propList.add(new Property("Samples", PropertyType.PROPERTY, - PropertyArity.LIST, _samples)); - } - if (_exifInfo != null) { - _propList.add(_exifInfo.buildProperty()); - } - - if (!formatChunkSeen) { - info.setMessage(new ErrorMessage(MessageConstants.WAVE_HUL_5)); - info.setWellFormed(false); - return 0; - } - if (!dataChunkSeen) { - info.setMessage(new ErrorMessage(MessageConstants.WAVE_HUL_24)); - info.setWellFormed(false); - return 0; - } - - // This file looks OK. - if (_ckSummer != null) { - skipDstreamToEnd(info); - // Set the checksums in the report if they're calculated - setChecksums(this._ckSummer, info); - } - - info.setProperty(_metadata); - - // Indicate satisfied profiles. - if (flagPCMWaveFormat) { - info.setProfile("PCMWAVEFORMAT"); - } - if (flagWaveFormatEx) { - info.setProfile("WAVEFORMATEX"); - } - if (flagWaveFormatExtensible) { - info.setProfile("WAVEFORMATEXTENSIBLE"); - } - if (broadcastExtChunkSeen && ( - (waveCodec == FormatChunk.WAVE_FORMAT_MPEG && factChunkSeen) - || waveCodec == FormatChunk.WAVE_FORMAT_PCM)) { - info.setProfile("BWF"); - } - return 0; - } - - /** - * Marks the first sample offset as the current byte position, if it hasn't - * already been marked. - */ - public void markFirstSampleOffset() { - if (!firstSampleOffsetMarked) { - firstSampleOffsetMarked = true; - _aesMetadata.setFirstSampleOffset(_nByte); - } - } - - /** Sets an ExifInfo object for the module. */ - public void setExifInfo(ExifInfo exifInfo) { - _exifInfo = exifInfo; - } - - /** Sets the number of bytes that holds an aligned sample. */ - public void setBlockAlign(int align) { - _blockAlign = align; - } - - /** - * Returns the ExifInfo object. - * - * If no ExifInfo object has been set, returns null. - */ - public ExifInfo getExifInfo() { - return _exifInfo; - } - - /** Returns the WAVE codec value. */ - public int getWaveCodec() { - return waveCodec; - } - - /** Returns the number of bytes needed per aligned sample. */ - public int getBlockAlign() { - return _blockAlign; - } - - /** Adds a Property to the WAVE metadata. */ - public void addWaveProperty(Property prop) { - _propList.add(prop); - } - - /** Adds a Label property */ - public void addLabel(Property p) { - _labels.add(p); - } - - /** Adds a LabeledText property */ - public void addLabeledText(Property p) { - _labeledText.add(p); - } - - /** Adds a Sample property */ - public void addSample(Property p) { - _samples.add(p); - } - - /** Adds a Note string */ - public void addNote(Property p) { - _notes.add(p); - } - - /** Adds the ListInfo property, which is a List of String Properties. */ - public void addListInfo(List l) { - _propList.add(new Property("ListInfo", PropertyType.PROPERTY, - PropertyArity.LIST, l)); - } - - /** - * One-argument version of readSignedLong. WAVE is always - * little-endian, so we can unambiguously drop its endian argument. - */ - public long readSignedLong(DataInputStream stream) throws IOException { - return readSignedLong(stream, false, this); - } - - /** - * One-argument version of readUnsignedInt. WAVE is always - * little-endian, so we can unambiguously drop its endian argument. - */ - public long readUnsignedInt(DataInputStream stream) throws IOException { - return readUnsignedInt(stream, false, this); - } - - /** - * One-argument version of readSignedInt. WAVE is always - * little-endian, so we can unambiguously drop its endian argument. - */ - public int readSignedInt(DataInputStream stream) throws IOException { - return readSignedInt(stream, false, this); - } - - /** - * One-argument version of readUnsignedShort. WAVE is always - * little-endian, so we can unambiguously drop its endian argument. - */ - public int readUnsignedShort(DataInputStream stream) throws IOException { - return readUnsignedShort(stream, false, this); - } - - /** - * One-argument version of readSignedShort. WAVE is always - * little-endian, so we can unambiguously drop its endian argument. - */ - public int readSignedShort(DataInputStream stream) throws IOException { - return readSignedShort(stream, false, this); - } - - /** - * Reads 4 bytes and concatenates them into a String. This pattern is used - * for ID's of various kinds. - */ - public String read4Chars(DataInputStream stream) throws IOException { - StringBuilder sb = new StringBuilder(4); - for (int i = 0; i < 4; i++) { - int ch = readUnsignedByte(stream, this); - // Omit nulls - if (ch != 0) { - sb.append((char) ch); - } - } - return sb.toString(); - } - - /** Sets the WAVE codec. */ - public void setWaveCodec(int value) { - waveCodec = value; - } - - /** - * Adds to the number of data bytes. This may be called multiple times to - * give a cumulative total. - */ - public void addSamples(long samples) { - sampleCount += samples; - } - - /** Sets the sample rate. */ - public void setSampleRate(long rate) { - sampleRate = rate; - } - - /** Sets the profile flag for PCMWAVEFORMAT. */ - public void setPCMWaveFormat(boolean b) { - flagPCMWaveFormat = b; - } - - /** Sets the profile flag for WAVEFORMATEX. */ - public void setWaveFormatEx(boolean b) { - flagWaveFormatEx = b; - } - - /** Sets the profile flag for WAVEFORMATEXTENSIBLE. */ - public void setWaveFormatExtensible(boolean b) { - flagWaveFormatExtensible = b; - } - - /** Initializes the state of the module for parsing. */ - @Override - protected void initParse() { - super.initParse(); - _propList = new LinkedList<>(); - _notes = new LinkedList<>(); - _labels = new LinkedList<>(); - _labeledText = new LinkedList<>(); - _samples = new LinkedList<>(); - firstSampleOffsetMarked = false; - waveCodec = -1; - sampleCount = 0; - riffSize = 0; - extendedRiffSize = 0; - extendedSampleLength = 0; - extendedChunkSizes = new HashMap<>(); - - _metadata = new Property("WAVEMetadata", PropertyType.PROPERTY, - PropertyArity.LIST, _propList); - _aesMetadata = new AESAudioMetadata(); - _aesMetadata.setByteOrder(AESAudioMetadata.LITTLE_ENDIAN); - _aesMetadata.setAnalogDigitalFlag("FILE_DIGITAL"); - _aesMetadata.setFormat("WAVE"); - _aesMetadata.setUse("OTHER", "JHOVE_validation"); - _aesMetadata.setDirection("NONE"); - - _propList.add(new Property("AESAudioMetadata", - PropertyType.AESAUDIOMETADATA, _aesMetadata)); - - // Most chunk types are allowed to occur only once, - // and a few must occur exactly once. - // Clear flags for whether they have been seen. - formatChunkSeen = false; - dataChunkSeen = false; - dataSize64ChunkSeen = false; - instrumentChunkSeen = false; - cartChunkSeen = false; - mpegChunkSeen = false; - broadcastExtChunkSeen = false; - peakChunkSeen = false; - linkChunkSeen = false; - cueChunkSeen = false; - - // Initialize profile flags - flagPCMWaveFormat = false; - flagWaveFormatEx = false; - flagWaveFormatExtensible = false; - flagRF64 = false; - } - - /** Reads a WAVE chunk. */ - protected boolean readChunk(RepInfo info) throws IOException { - - Chunk chunk = null; - ChunkHeader chunkh = new ChunkHeader(this, info); - if (!chunkh.readHeader(_dstream)) { - return false; - } - - String chunkId = chunkh.getID(); - long chunkSize = chunkh.getSize(); - if (hasExtendedDataSizes() && chunkSize == LOOKUP_EXTENDED_DATA_SIZE) { - Long extendedSize = extendedChunkSizes.get(chunkId); - if (extendedSize != null) { - chunkh.setSize(extendedSize); - chunkSize = extendedSize; - } - } - - // Check if the chunk size is greater than the RIFF's remaining length - if (Long.compareUnsigned(getBytesRemaining(), chunkSize) < 0) { - info.setMessage(new ErrorMessage( - MessageConstants.WAVE_HUL_6, _nByte - Chunk.SIZE_LENGTH)); - info.setWellFormed(false); - return false; - } - - if ("fmt ".equals(chunkId)) { - if (formatChunkSeen) { - dupChunkError(info, "Format"); - } - chunk = new FormatChunk(this, chunkh, _dstream); - formatChunkSeen = true; - } else if ("data".equals(chunkId)) { - if (!formatChunkSeen) { - info.setMessage(new ErrorMessage( - MessageConstants.WAVE_HUL_25, chunkh.getOffset())); - info.setValid(false); - } - if (dataChunkSeen) { - dupChunkError(info, "Data"); - } - chunk = new DataChunk(this, chunkh, _dstream); - dataChunkSeen = true; - } else if ("ds64".equals(chunkId)) { - chunk = new DataSize64Chunk(this, chunkh, _dstream); - dataSize64ChunkSeen = true; - } else if ("fact".equals(chunkId)) { - chunk = new FactChunk(this, chunkh, _dstream); - factChunkSeen = true; - // Are multiple 'fact' chunks allowed? - } else if ("note".equals(chunkId)) { - chunk = new NoteChunk(this, chunkh, _dstream); - // Multiple note chunks are allowed - } else if ("labl".equals(chunkId)) { - chunk = new LabelChunk(this, chunkh, _dstream); - // Multiple label chunks are allowed - } else if ("list".equals(chunkId)) { - chunk = new AssocDataListChunk(this, chunkh, _dstream, info); - // Are multiple chunks allowed? Who knows? - } else if ("LIST".equals(chunkId)) { - chunk = new ListInfoChunk(this, chunkh, _dstream, info); - // Multiple list chunks must be OK, since there can - // be different types, e.g., an INFO list and an exif list. - } else if ("smpl".equals(chunkId)) { - chunk = new SampleChunk(this, chunkh, _dstream); - // Multiple sample chunks are allowed -- I think - } else if ("inst".equals(chunkId)) { - if (instrumentChunkSeen) { - dupChunkError(info, "Instrument"); - } - chunk = new InstrumentChunk(this, chunkh, _dstream); - // Only one instrument chunk is allowed - instrumentChunkSeen = true; - } else if ("mext".equals(chunkId)) { - if (mpegChunkSeen) { - dupChunkError(info, "MPEG Audio Extension"); - } - chunk = new MpegChunk(this, chunkh, _dstream); - // I think only one MPEG chunk is allowed - mpegChunkSeen = true; - } else if ("cart".equals(chunkId)) { - if (cartChunkSeen) { - dupChunkError(info, "Cart"); - } - chunk = new CartChunk(this, chunkh, _dstream); - cartChunkSeen = true; - } else if ("bext".equals(chunkId)) { - if (broadcastExtChunkSeen) { - dupChunkError(info, "Broadcast Audio Extension"); - } - chunk = new BroadcastExtChunk(this, chunkh, _dstream); - broadcastExtChunkSeen = true; - } else if ("levl".equals(chunkId)) { - if (peakChunkSeen) { - dupChunkError(info, "Peak Envelope"); - } - chunk = new PeakEnvelopeChunk(this, chunkh, _dstream); - peakChunkSeen = true; - } else if ("link".equals(chunkId)) { - if (linkChunkSeen) { - dupChunkError(info, "Link"); - } - chunk = new LinkChunk(this, chunkh, _dstream); - linkChunkSeen = true; - } else if ("cue ".equals(chunkId)) { - if (cueChunkSeen) { - dupChunkError(info, "Cue Points"); - } - chunk = new CueChunk(this, chunkh, _dstream); - cueChunkSeen = true; - } else { - JhoveMessage message = JhoveMessages.getMessageInstance( - MessageConstants.WAVE_HUL_7.getId(), - String.format(MessageConstants.WAVE_HUL_7.getMessage(), - chunkId)); - info.setMessage(new InfoMessage(message, chunkh.getOffset())); - } - - long dataRead = _nByte; - if (chunk != null) { - if (!chunk.readChunk(info)) { - return false; - } - } else { - // Other chunk types are legal, just skip over them - skipBytes(_dstream, chunkSize, this); - } - dataRead = _nByte - dataRead; - - if (dataRead < chunkSize) { - // The file has been truncated or there - // remains unexpected chunk data to skip - remainingDataInfo(_dstream, info, chunkSize - dataRead, chunkId); - } - - if ((chunkSize & 1) != 0) { - // Must come out to an even byte boundary - skipBytes(_dstream, 1, this); - } - - return true; - } - - /** - * Reports and passes over any data still remaining following an attempt - * at reading a chunk. This ensures we align with the start of any - * subsequent chunk. - */ - private void remainingDataInfo(DataInputStream stream, RepInfo info, - long bytesToProcess, String chunkId) - throws IOException { - - if (stream.available() > 0) { - - boolean nullData = true; - long bytesProcessed = 0; - - // Check for non-null data - while (nullData && bytesProcessed < bytesToProcess) { - int b = readUnsignedByte(stream, this); - if (b != 0) nullData = false; - bytesProcessed++; - } - - // Skip any remaining data - bytesProcessed += skipBytes(stream, - bytesToProcess - bytesProcessed, this); - - info.setMessage(new InfoMessage( - MessageConstants.WAVE_HUL_26, - String.format(MessageConstants.WAVE_HUL_26_SUB.getMessage(), - chunkId, bytesProcessed, nullData), - _nByte - bytesProcessed)); - - } else { - throw new EOFException(String.format( - MessageConstants.WAVE_HUL_3_SUB_2.getMessage(), chunkId)); - } - } - - /** Returns the number of RIFF bytes remaining to be read. */ - private long getBytesRemaining() { - - long totalBytes = Chunk.HEADER_LENGTH; - - if (hasExtendedDataSizes()) { - totalBytes += extendedRiffSize; - } else { - totalBytes += riffSize; - } - - return totalBytes - _nByte; - } - - /** Returns the module's AES metadata. */ - public AESAudioMetadata getAESMetadata() { - return _aesMetadata; - } - - /** Reports a duplicate chunk. */ - protected void dupChunkError(RepInfo info, String chunkName) { - JhoveMessage message = JhoveMessages.getMessageInstance( - MessageConstants.WAVE_HUL_8.getId(), String.format( - MessageConstants.WAVE_HUL_8.getMessage(), chunkName)); - info.setMessage(new ErrorMessage( - message, _nByte - Chunk.HEADER_LENGTH)); - info.setValid(false); - } - - /** - * General function for adding a property with a 32-bit value, with two - * arrays of Strings to interpret 0 and 1 values as a bitmask. - * - * @param val - * The bitmask - * @param name - * The name for the Property - * @param oneValueNames - * Array of names to use for '1' values - * @param zeroValueNames - * Array of names to use for '0' values - */ - public Property buildBitmaskProperty(int val, String name, - String[] oneValueNames, String[] zeroValueNames) { - if (_je != null && _je.getShowRawFlag()) { - return new Property(name, PropertyType.INTEGER, val); - } - List slist = new LinkedList<>(); - try { - for (int i = 0; i < oneValueNames.length; i++) { - String s; - if ((val & (1 << i)) != 0) { - s = oneValueNames[i]; - } else { - s = zeroValueNames[i]; - } - if (s != null && s.length() > 0) { - slist.add(s); - } - } - } catch (Exception e) { - return null; - } - return new Property(name, PropertyType.STRING, PropertyArity.LIST, - slist); - } - - /** - * Returns whether or not the module has parsed the chunks required to - * provide extended data sizes, namely RF64's Data Size 64 chunk. - */ - public boolean hasExtendedDataSizes() { - return flagRF64 && dataSize64ChunkSeen; - } - - /** Sets the extended RIFF size. */ - public void setExtendedRiffSize(long size) { - extendedRiffSize = size; - } - - /** Sets the extended sample length. */ - public void setExtendedSampleLength(long length) { - extendedSampleLength = length; - } - - /** Returns the extended sample length. */ - public long getExtendedSampleLength() { - return extendedSampleLength; - } - - /** - * Adds a chunk's extended chunk size to the map of extended sizes. - * If a chunk has previously been mapped, its chunk size will be replaced. - */ - public void addExtendedChunkSize(String chunkId, Long chunkSize) { - extendedChunkSizes.put(chunkId, chunkSize); - } + /** List of Note properties */ + protected List _notes; + + /** List of Label properties */ + protected List _labels; + + /** List of Labeled Text properties */ + protected List _labeledText; + + /** List of Sample properties */ + protected List _samples; + + /** AES audio metadata to go into WAVE metadata */ + protected AESAudioMetadata _aesMetadata; + + /** RIFF size as found in the RIFF chunk header. */ + protected long riffSize; + + /** Bytes needed to store a file */ + protected int _blockAlign; + + /** Exif data from file */ + protected ExifInfo _exifInfo; + + /** WAVE codec, used for profile verification */ + protected int waveCodec; + + /** Extended (and unsigned) RIFF size as found in Data Size 64 chunk */ + protected long extendedRiffSize; + + /** Extended (and unsigned) sample length as found in Data Size 64 chunk */ + protected long extendedSampleLength; + + /** + * Number of samples in the file. Obtained from the Data chunk for uncompressed files, and the + * Fact chunk for compressed ones. Value should be treated as unsigned. + */ + protected long sampleCount; + + /** Sample rate from file */ + protected long sampleRate; + + /** Flag to check for exactly one Format chunk */ + protected boolean formatChunkSeen; + + /** Flag to check for presence of a Fact chunk */ + protected boolean factChunkSeen; + + /** Flag to check for not more than one Data chunk */ + protected boolean dataChunkSeen; + + /** Flag to check for a Data Size 64 chunk */ + protected boolean dataSize64ChunkSeen; + + /** Flag to check for not more than one Instrument chunk */ + protected boolean instrumentChunkSeen; + + /** Flag to check for not more than one MPEG chunk */ + protected boolean mpegChunkSeen; + + /** Flag to check for not more than one Cart chunk */ + protected boolean cartChunkSeen; + + /** Flag to check for not more than one Broadcast Audio Extension chunk */ + protected boolean broadcastExtChunkSeen; + + /** Flag to check for not more than one Peak Envelope chunk */ + protected boolean peakChunkSeen; + + /** Flag to check for not more than one Link chunk */ + protected boolean linkChunkSeen; + + /** Flag to check for not more than one Cue chunk */ + protected boolean cueChunkSeen; + + /** Profile flag for PCMWAVEFORMAT */ + protected boolean flagPCMWaveFormat; + + /** Profile flag for WAVEFORMATEX */ + protected boolean flagWaveFormatEx; + + /** Profile flag for WAVEFORMATEXTENSIBLE */ + protected boolean flagWaveFormatExtensible; + + /** Profile flag for RF64 */ + protected boolean flagRF64; + + /** Flag to note that first sample offset has been recorded */ + protected boolean firstSampleOffsetMarked; + + /** + * Class constructor. + * + *

Instantiates a WaveModule object. + */ + public WaveModule() { + super( + NAME, + RELEASE, + DATE, + FORMATS, + COVERAGE, + MIMETYPES, + WELLFORMED, + VALIDITY, + REPINFO, + NOTE, + RIGHTS, + false); + _vendor = Agent.harvardInstance(); + + Agent msAgent = + new Agent.Builder("Microsoft Corporation", AgentType.COMMERCIAL) + .address("One Microsoft Way, Redmond, WA 98052-6399") + .telephone("+1 (800) 426-9400") + .web("http://www.microsoft.com") + .build(); + + Document doc = new Document("PCMWAVEFORMAT", DocumentType.WEB); + doc.setIdentifier( + new Identifier( + "http://msdn.microsoft.com/library/default.asp?url=/library/en-us/" + + "multimed/htm/_win32_pcmwaveformat_str.asp", + IdentifierType.URL)); + doc.setPublisher(msAgent); + _specification.add(doc); + + doc = new Document("WAVEFORMATEX", DocumentType.WEB); + doc.setIdentifier( + new Identifier( + "http://msdn.microsoft.com/library/default.asp?url=/library/en-us/" + + "multimed/htm/_win32_waveformatex_str.asp", + IdentifierType.URL)); + doc.setPublisher(msAgent); + _specification.add(doc); + + doc = new Document("WAVEFORMATEXTENSIBLE", DocumentType.WEB); + doc.setIdentifier( + new Identifier( + "http://msdn.microsoft.com/library/default.asp?url=/library/en-us/" + + "multimed/htm/_win32_waveformatextensible_str.asp", + IdentifierType.URL)); + doc.setPublisher(msAgent); + _specification.add(doc); + + Agent ebuAgent = + new Agent.Builder("European Broadcasting Union", AgentType.COMMERCIAL) + .address( + "Casa postale 45, Ancienne Route 17A, " + + "CH-1218 Grand-Saconex, Geneva, Switzerland") + .telephone("+41 (0)22 717 2111") + .fax("+41 (0)22 747 4000") + .email("techreview@ebu.ch") + .web("http://www.ebu.ch") + .build(); + + doc = new Document("Specification of the Broadcast Wave Format (BWF)", DocumentType.REPORT); + doc.setIdentifier(new Identifier("EBU Technical Specification 3285", IdentifierType.OTHER)); + doc.setIdentifier( + new Identifier("https://tech.ebu.ch/docs/tech/tech3285.pdf", IdentifierType.URL)); + doc.setPublisher(ebuAgent); + doc.setDate("2011-05"); + _specification.add(doc); + + doc = new Document("MBWF / RF64: An Extended File Format for Audio", DocumentType.REPORT); + doc.setIdentifier(new Identifier("EBU Technical Specification 3306", IdentifierType.OTHER)); + doc.setIdentifier( + new Identifier("https://tech.ebu.ch/docs/tech/tech3306-2009.pdf", IdentifierType.URL)); + doc.setPublisher(ebuAgent); + doc.setDate("2009-07"); + _specification.add(doc); + + Agent ietfAgent = + new Agent.Builder("IETF", AgentType.STANDARD).web("https://www.ietf.org").build(); + + doc = new Document("WAVE and AVI Codec Registries", DocumentType.RFC); + doc.setPublisher(ietfAgent); + doc.setDate("1998-06"); + doc.setIdentifier(new Identifier("RFC 2361", IdentifierType.RFC)); + doc.setIdentifier(new Identifier("https://www.ietf.org/rfc/rfc2361.txt", IdentifierType.URL)); + _specification.add(doc); + + Signature sig = + new ExternalSignature(".wav", SignatureType.EXTENSION, SignatureUseType.OPTIONAL); + _signature.add(sig); + + sig = + new ExternalSignature( + ".bwf", SignatureType.EXTENSION, SignatureUseType.OPTIONAL, "For BWF profile"); + _signature.add(sig); + + sig = + new ExternalSignature( + ".rf64", SignatureType.EXTENSION, SignatureUseType.OPTIONAL, "For RF64 profile"); + _signature.add(sig); + + sig = + new InternalSignature( + "RIFF", SignatureType.MAGIC, SignatureUseType.MANDATORY_IF_APPLICABLE, 0); + _signature.add(sig); + + sig = + new InternalSignature( + "RF64", SignatureType.MAGIC, SignatureUseType.MANDATORY_IF_APPLICABLE, 0); + _signature.add(sig); + + sig = new InternalSignature("WAVE", SignatureType.MAGIC, SignatureUseType.MANDATORY, 8); + _signature.add(sig); + + _bigEndian = false; + } + + /** + * Parses the content of a purported WAVE digital object and stores the results in RepInfo. + * + * @param stream An InputStream, positioned at its beginning, which is generated from the object + * to be parsed + * @param info A fresh RepInfo object which will be modified to reflect the results of the parsing + * @param parseIndex Must be 0 in first call to parse. If parse returns + * a nonzero value, it must be called again with parseIndex equal to that return + * value. + */ + @Override + public int parse(InputStream stream, RepInfo info, int parseIndex) { + initParse(); + info.setModule(this); + + _aesMetadata.setPrimaryIdentifier(info.getUri()); + if (info.getURLFlag()) { + _aesMetadata.setOtherPrimaryIdentifierType("URI"); + } else { + _aesMetadata.setPrimaryIdentifierType(AESAudioMetadata.FILE_NAME); + } + + // We may have already done the checksums + // while converting a temporary file. + setupDataStream(stream, info); + + try { + // Check the start of the file for the right opening bytes + String firstFourChars = read4Chars(_dstream); + if (firstFourChars.equals(RF64_SIGNATURE)) { + info.setProfile("RF64"); + flagRF64 = true; + } else if (!firstFourChars.equals(RIFF_SIGNATURE)) { + info.setMessage(new ErrorMessage(MessageConstants.WAVE_HUL_1, 0)); + info.setWellFormed(false); + return 0; + } + + // Get the length of the Form chunk. This includes all + // subsequent form fields and form subchunks, but excludes + // the form chunk's header (its ID and the its length). + riffSize = readUnsignedInt(_dstream); + + // Read the RIFF form type + String formType = read4Chars(_dstream); + if (!"WAVE".equals(formType)) { + info.setMessage( + new ErrorMessage(MessageConstants.WAVE_HUL_2, _nByte - RIFF_FORM_TYPE_LENGTH)); + info.setWellFormed(false); + return 0; + } + + // If we get this far, the signature is OK. + info.setSigMatch(_name); + info.setFormat(_format[0]); + info.setMimeType(_mimeType[0]); + + if (flagRF64) { + // For RF64 files the first chunk should be a Data Size 64 chunk + // containing the extended data sizes for a number of elements. + if (readChunk(info) && dataSize64ChunkSeen) { + if (riffSize == LOOKUP_EXTENDED_DATA_SIZE) { + // Even though RF64 can support files larger than + // Long.MAX_VALUE, this module currently does not. + if (Long.compareUnsigned(extendedRiffSize, Long.MAX_VALUE) > 0) { + info.setMessage(new InfoMessage(MessageConstants.WAVE_HUL_22)); + info.setWellFormed(RepInfo.UNDETERMINED); + return 0; + } + } + } else { + info.setMessage( + new ErrorMessage( + MessageConstants.WAVE_HUL_23, Chunk.HEADER_LENGTH + RIFF_FORM_TYPE_LENGTH)); + info.setWellFormed(false); + return 0; + } + } + + while (getBytesRemaining() > 0) { + if (!readChunk(info)) { + break; + } + } + + if (getBytesRemaining() > 0) { + // The file has been truncated or there + // remains unexpected chunk data to skip + remainingDataInfo(_dstream, info, getBytesRemaining(), firstFourChars); + } + + } catch (EOFException eofe) { + info.setWellFormed(false); + String subMessage = + String.format(MessageConstants.WAVE_HUL_3_SUB.getMessage(), getBytesRemaining()); + if (eofe.getMessage() != null) { + subMessage += "; " + eofe.getMessage(); + } + info.setMessage(new ErrorMessage(MessageConstants.WAVE_HUL_3, subMessage, _nByte)); + } catch (Exception e) { // TODO make this more specific + e.printStackTrace(); + info.setWellFormed(false); + JhoveMessage message = + JhoveMessages.getMessageInstance( + MessageConstants.WAVE_HUL_4.getId(), + String.format( + MessageConstants.WAVE_HUL_4.getMessage(), + e.getClass().getName() + ", " + e.getMessage())); + info.setMessage(new ErrorMessage(message, _nByte)); + return 0; + } + + // Set duration from number of samples and rate. + if (sampleCount > 0) { + _aesMetadata.setDuration(sampleCount); + } + + // Add note and label properties, if there's anything to report. + if (!_labels.isEmpty()) { + _propList.add(new Property("Labels", PropertyType.PROPERTY, PropertyArity.LIST, _labels)); + } + if (!_labeledText.isEmpty()) { + _propList.add( + new Property("LabeledText", PropertyType.PROPERTY, PropertyArity.LIST, _labeledText)); + } + if (!_notes.isEmpty()) { + _propList.add(new Property("Notes", PropertyType.PROPERTY, PropertyArity.LIST, _notes)); + } + if (!_samples.isEmpty()) { + _propList.add(new Property("Samples", PropertyType.PROPERTY, PropertyArity.LIST, _samples)); + } + if (_exifInfo != null) { + _propList.add(_exifInfo.buildProperty()); + } + + if (!formatChunkSeen) { + info.setMessage(new ErrorMessage(MessageConstants.WAVE_HUL_5)); + info.setWellFormed(false); + return 0; + } + if (!dataChunkSeen) { + info.setMessage(new ErrorMessage(MessageConstants.WAVE_HUL_24)); + info.setWellFormed(false); + return 0; + } + + // This file looks OK. + if (_ckSummer != null) { + skipDstreamToEnd(info); + // Set the checksums in the report if they're calculated + setChecksums(this._ckSummer, info); + } + + info.setProperty(_metadata); + + // Indicate satisfied profiles. + if (flagPCMWaveFormat) { + info.setProfile("PCMWAVEFORMAT"); + } + if (flagWaveFormatEx) { + info.setProfile("WAVEFORMATEX"); + } + if (flagWaveFormatExtensible) { + info.setProfile("WAVEFORMATEXTENSIBLE"); + } + if (broadcastExtChunkSeen + && ((waveCodec == FormatChunk.WAVE_FORMAT_MPEG && factChunkSeen) + || waveCodec == FormatChunk.WAVE_FORMAT_PCM)) { + info.setProfile("BWF"); + } + return 0; + } + + /** + * Marks the first sample offset as the current byte position, if it hasn't already been marked. + */ + public void markFirstSampleOffset() { + if (!firstSampleOffsetMarked) { + firstSampleOffsetMarked = true; + _aesMetadata.setFirstSampleOffset(_nByte); + } + } + + /** Sets an ExifInfo object for the module. */ + public void setExifInfo(ExifInfo exifInfo) { + _exifInfo = exifInfo; + } + + /** Sets the number of bytes that holds an aligned sample. */ + public void setBlockAlign(int align) { + _blockAlign = align; + } + + /** + * Returns the ExifInfo object. + * + *

If no ExifInfo object has been set, returns null. + */ + public ExifInfo getExifInfo() { + return _exifInfo; + } + + /** Returns the WAVE codec value. */ + public int getWaveCodec() { + return waveCodec; + } + + /** Returns the number of bytes needed per aligned sample. */ + public int getBlockAlign() { + return _blockAlign; + } + + /** Adds a Property to the WAVE metadata. */ + public void addWaveProperty(Property prop) { + _propList.add(prop); + } + + /** Adds a Label property */ + public void addLabel(Property p) { + _labels.add(p); + } + + /** Adds a LabeledText property */ + public void addLabeledText(Property p) { + _labeledText.add(p); + } + + /** Adds a Sample property */ + public void addSample(Property p) { + _samples.add(p); + } + + /** Adds a Note string */ + public void addNote(Property p) { + _notes.add(p); + } + + /** Adds the ListInfo property, which is a List of String Properties. */ + public void addListInfo(List l) { + _propList.add(new Property("ListInfo", PropertyType.PROPERTY, PropertyArity.LIST, l)); + } + + /** + * One-argument version of readSignedLong. WAVE is always little-endian, so we can + * unambiguously drop its endian argument. + */ + public long readSignedLong(DataInputStream stream) throws IOException { + return readSignedLong(stream, false, this); + } + + /** + * One-argument version of readUnsignedInt. WAVE is always little-endian, so we can + * unambiguously drop its endian argument. + */ + public long readUnsignedInt(DataInputStream stream) throws IOException { + return readUnsignedInt(stream, false, this); + } + + /** + * One-argument version of readSignedInt. WAVE is always little-endian, so we can + * unambiguously drop its endian argument. + */ + public int readSignedInt(DataInputStream stream) throws IOException { + return readSignedInt(stream, false, this); + } + + /** + * One-argument version of readUnsignedShort. WAVE is always little-endian, so we can + * unambiguously drop its endian argument. + */ + public int readUnsignedShort(DataInputStream stream) throws IOException { + return readUnsignedShort(stream, false, this); + } + + /** + * One-argument version of readSignedShort. WAVE is always little-endian, so we can + * unambiguously drop its endian argument. + */ + public int readSignedShort(DataInputStream stream) throws IOException { + return readSignedShort(stream, false, this); + } + + /** + * Reads 4 bytes and concatenates them into a String. This pattern is used for ID's of various + * kinds. + */ + public String read4Chars(DataInputStream stream) throws IOException { + StringBuilder sb = new StringBuilder(4); + for (int i = 0; i < 4; i++) { + int ch = readUnsignedByte(stream, this); + // Omit nulls + if (ch != 0) { + sb.append((char) ch); + } + } + return sb.toString(); + } + + /** Sets the WAVE codec. */ + public void setWaveCodec(int value) { + waveCodec = value; + } + + /** + * Adds to the number of data bytes. This may be called multiple times to give a cumulative total. + */ + public void addSamples(long samples) { + sampleCount += samples; + } + + /** Sets the sample rate. */ + public void setSampleRate(long rate) { + sampleRate = rate; + } + + /** Sets the profile flag for PCMWAVEFORMAT. */ + public void setPCMWaveFormat(boolean b) { + flagPCMWaveFormat = b; + } + + /** Sets the profile flag for WAVEFORMATEX. */ + public void setWaveFormatEx(boolean b) { + flagWaveFormatEx = b; + } + + /** Sets the profile flag for WAVEFORMATEXTENSIBLE. */ + public void setWaveFormatExtensible(boolean b) { + flagWaveFormatExtensible = b; + } + + /** Initializes the state of the module for parsing. */ + @Override + protected void initParse() { + super.initParse(); + _propList = new LinkedList<>(); + _notes = new LinkedList<>(); + _labels = new LinkedList<>(); + _labeledText = new LinkedList<>(); + _samples = new LinkedList<>(); + firstSampleOffsetMarked = false; + waveCodec = -1; + sampleCount = 0; + riffSize = 0; + extendedRiffSize = 0; + extendedSampleLength = 0; + extendedChunkSizes = new HashMap<>(); + + _metadata = new Property("WAVEMetadata", PropertyType.PROPERTY, PropertyArity.LIST, _propList); + _aesMetadata = new AESAudioMetadata(); + _aesMetadata.setByteOrder(AESAudioMetadata.LITTLE_ENDIAN); + _aesMetadata.setAnalogDigitalFlag("FILE_DIGITAL"); + _aesMetadata.setFormat("WAVE"); + _aesMetadata.setUse("OTHER", "JHOVE_validation"); + _aesMetadata.setDirection("NONE"); + + _propList.add(new Property("AESAudioMetadata", PropertyType.AESAUDIOMETADATA, _aesMetadata)); + + // Most chunk types are allowed to occur only once, + // and a few must occur exactly once. + // Clear flags for whether they have been seen. + formatChunkSeen = false; + dataChunkSeen = false; + dataSize64ChunkSeen = false; + instrumentChunkSeen = false; + cartChunkSeen = false; + mpegChunkSeen = false; + broadcastExtChunkSeen = false; + peakChunkSeen = false; + linkChunkSeen = false; + cueChunkSeen = false; + + // Initialize profile flags + flagPCMWaveFormat = false; + flagWaveFormatEx = false; + flagWaveFormatExtensible = false; + flagRF64 = false; + } + + /** Reads a WAVE chunk. */ + protected boolean readChunk(RepInfo info) throws IOException { + + Chunk chunk = null; + ChunkHeader chunkh = new ChunkHeader(this, info); + if (!chunkh.readHeader(_dstream)) { + return false; + } + + String chunkId = chunkh.getID(); + long chunkSize = chunkh.getSize(); + if (hasExtendedDataSizes() && chunkSize == LOOKUP_EXTENDED_DATA_SIZE) { + Long extendedSize = extendedChunkSizes.get(chunkId); + if (extendedSize != null) { + chunkh.setSize(extendedSize); + chunkSize = extendedSize; + } + } + + // Check if the chunk size is greater than the RIFF's remaining length + if (Long.compareUnsigned(getBytesRemaining(), chunkSize) < 0) { + info.setMessage(new ErrorMessage(MessageConstants.WAVE_HUL_6, _nByte - Chunk.SIZE_LENGTH)); + info.setWellFormed(false); + return false; + } + + if ("fmt ".equals(chunkId)) { + if (formatChunkSeen) { + dupChunkError(info, "Format"); + } + chunk = new FormatChunk(this, chunkh, _dstream); + formatChunkSeen = true; + } else if ("data".equals(chunkId)) { + if (!formatChunkSeen) { + info.setMessage(new ErrorMessage(MessageConstants.WAVE_HUL_25, chunkh.getOffset())); + info.setValid(false); + } + if (dataChunkSeen) { + dupChunkError(info, "Data"); + } + chunk = new DataChunk(this, chunkh, _dstream); + dataChunkSeen = true; + } else if ("ds64".equals(chunkId)) { + chunk = new DataSize64Chunk(this, chunkh, _dstream); + dataSize64ChunkSeen = true; + } else if ("fact".equals(chunkId)) { + chunk = new FactChunk(this, chunkh, _dstream); + factChunkSeen = true; + // Are multiple 'fact' chunks allowed? + } else if ("note".equals(chunkId)) { + chunk = new NoteChunk(this, chunkh, _dstream); + // Multiple note chunks are allowed + } else if ("labl".equals(chunkId)) { + chunk = new LabelChunk(this, chunkh, _dstream); + // Multiple label chunks are allowed + } else if ("list".equals(chunkId)) { + chunk = new AssocDataListChunk(this, chunkh, _dstream, info); + // Are multiple chunks allowed? Who knows? + } else if ("LIST".equals(chunkId)) { + chunk = new ListInfoChunk(this, chunkh, _dstream, info); + // Multiple list chunks must be OK, since there can + // be different types, e.g., an INFO list and an exif list. + } else if ("smpl".equals(chunkId)) { + chunk = new SampleChunk(this, chunkh, _dstream); + // Multiple sample chunks are allowed -- I think + } else if ("inst".equals(chunkId)) { + if (instrumentChunkSeen) { + dupChunkError(info, "Instrument"); + } + chunk = new InstrumentChunk(this, chunkh, _dstream); + // Only one instrument chunk is allowed + instrumentChunkSeen = true; + } else if ("mext".equals(chunkId)) { + if (mpegChunkSeen) { + dupChunkError(info, "MPEG Audio Extension"); + } + chunk = new MpegChunk(this, chunkh, _dstream); + // I think only one MPEG chunk is allowed + mpegChunkSeen = true; + } else if ("cart".equals(chunkId)) { + if (cartChunkSeen) { + dupChunkError(info, "Cart"); + } + chunk = new CartChunk(this, chunkh, _dstream); + cartChunkSeen = true; + } else if ("bext".equals(chunkId)) { + if (broadcastExtChunkSeen) { + dupChunkError(info, "Broadcast Audio Extension"); + } + chunk = new BroadcastExtChunk(this, chunkh, _dstream); + broadcastExtChunkSeen = true; + } else if ("levl".equals(chunkId)) { + if (peakChunkSeen) { + dupChunkError(info, "Peak Envelope"); + } + chunk = new PeakEnvelopeChunk(this, chunkh, _dstream); + peakChunkSeen = true; + } else if ("link".equals(chunkId)) { + if (linkChunkSeen) { + dupChunkError(info, "Link"); + } + chunk = new LinkChunk(this, chunkh, _dstream); + linkChunkSeen = true; + } else if ("cue ".equals(chunkId)) { + if (cueChunkSeen) { + dupChunkError(info, "Cue Points"); + } + chunk = new CueChunk(this, chunkh, _dstream); + cueChunkSeen = true; + } else { + JhoveMessage message = + JhoveMessages.getMessageInstance( + MessageConstants.WAVE_HUL_7.getId(), + String.format(MessageConstants.WAVE_HUL_7.getMessage(), chunkId)); + info.setMessage(new InfoMessage(message, chunkh.getOffset())); + } + + long dataRead = _nByte; + if (chunk != null) { + if (!chunk.readChunk(info)) { + return false; + } + } else { + // Other chunk types are legal, just skip over them + skipBytes(_dstream, chunkSize, this); + } + dataRead = _nByte - dataRead; + + if (dataRead < chunkSize) { + // The file has been truncated or there + // remains unexpected chunk data to skip + remainingDataInfo(_dstream, info, chunkSize - dataRead, chunkId); + } + + if ((chunkSize & 1) != 0) { + // Must come out to an even byte boundary + skipBytes(_dstream, 1, this); + } + + return true; + } + + /** + * Reports and passes over any data still remaining following an attempt at reading a chunk. This + * ensures we align with the start of any subsequent chunk. + */ + private void remainingDataInfo( + DataInputStream stream, RepInfo info, long bytesToProcess, String chunkId) + throws IOException { + + if (stream.available() > 0) { + + boolean nullData = true; + long bytesProcessed = 0; + + // Check for non-null data + while (nullData && bytesProcessed < bytesToProcess) { + int b = readUnsignedByte(stream, this); + if (b != 0) nullData = false; + bytesProcessed++; + } + + // Skip any remaining data + bytesProcessed += skipBytes(stream, bytesToProcess - bytesProcessed, this); + + info.setMessage( + new InfoMessage( + MessageConstants.WAVE_HUL_26, + String.format( + MessageConstants.WAVE_HUL_26_SUB.getMessage(), chunkId, bytesProcessed, nullData), + _nByte - bytesProcessed)); + + } else { + throw new EOFException( + String.format(MessageConstants.WAVE_HUL_3_SUB_2.getMessage(), chunkId)); + } + } + + /** Returns the number of RIFF bytes remaining to be read. */ + private long getBytesRemaining() { + + long totalBytes = Chunk.HEADER_LENGTH; + + if (hasExtendedDataSizes()) { + totalBytes += extendedRiffSize; + } else { + totalBytes += riffSize; + } + + return totalBytes - _nByte; + } + + /** Returns the module's AES metadata. */ + public AESAudioMetadata getAESMetadata() { + return _aesMetadata; + } + + /** Reports a duplicate chunk. */ + protected void dupChunkError(RepInfo info, String chunkName) { + JhoveMessage message = + JhoveMessages.getMessageInstance( + MessageConstants.WAVE_HUL_8.getId(), + String.format(MessageConstants.WAVE_HUL_8.getMessage(), chunkName)); + info.setMessage(new ErrorMessage(message, _nByte - Chunk.HEADER_LENGTH)); + info.setValid(false); + } + + /** + * General function for adding a property with a 32-bit value, with two arrays of Strings to + * interpret 0 and 1 values as a bitmask. + * + * @param val The bitmask + * @param name The name for the Property + * @param oneValueNames Array of names to use for '1' values + * @param zeroValueNames Array of names to use for '0' values + */ + public Property buildBitmaskProperty( + int val, String name, String[] oneValueNames, String[] zeroValueNames) { + if (_je != null && _je.getShowRawFlag()) { + return new Property(name, PropertyType.INTEGER, val); + } + List slist = new LinkedList<>(); + try { + for (int i = 0; i < oneValueNames.length; i++) { + String s; + if ((val & (1 << i)) != 0) { + s = oneValueNames[i]; + } else { + s = zeroValueNames[i]; + } + if (s != null && s.length() > 0) { + slist.add(s); + } + } + } catch (Exception e) { + return null; + } + return new Property(name, PropertyType.STRING, PropertyArity.LIST, slist); + } + + /** + * Returns whether or not the module has parsed the chunks required to provide extended data + * sizes, namely RF64's Data Size 64 chunk. + */ + public boolean hasExtendedDataSizes() { + return flagRF64 && dataSize64ChunkSeen; + } + + /** Sets the extended RIFF size. */ + public void setExtendedRiffSize(long size) { + extendedRiffSize = size; + } + + /** Sets the extended sample length. */ + public void setExtendedSampleLength(long length) { + extendedSampleLength = length; + } + + /** Returns the extended sample length. */ + public long getExtendedSampleLength() { + return extendedSampleLength; + } + + /** + * Adds a chunk's extended chunk size to the map of extended sizes. If a chunk has previously been + * mapped, its chunk size will be replaced. + */ + public void addExtendedChunkSize(String chunkId, Long chunkSize) { + extendedChunkSizes.put(chunkId, chunkSize); + } } diff --git a/jhove-modules/wave-hul/src/main/java/edu/harvard/hul/ois/jhove/module/wave/AXMLChunk.java b/jhove-modules/wave-hul/src/main/java/edu/harvard/hul/ois/jhove/module/wave/AXMLChunk.java index 91b1f275e..97210dffe 100644 --- a/jhove-modules/wave-hul/src/main/java/edu/harvard/hul/ois/jhove/module/wave/AXMLChunk.java +++ b/jhove-modules/wave-hul/src/main/java/edu/harvard/hul/ois/jhove/module/wave/AXMLChunk.java @@ -1,60 +1,50 @@ -/********************************************************************** - * Jhove - JSTOR/Harvard Object Validation Environment - * Copyright 2004 by JSTOR and the President and Fellows of Harvard College - **********************************************************************/ - +/** + * ******************************************************************** Jhove - JSTOR/Harvard Object + * Validation Environment Copyright 2004 by JSTOR and the President and Fellows of Harvard College + * ******************************************************************** + */ package edu.harvard.hul.ois.jhove.module.wave; -import edu.harvard.hul.ois.jhove.module.iff.*; import edu.harvard.hul.ois.jhove.*; import edu.harvard.hul.ois.jhove.module.WaveModule; +import edu.harvard.hul.ois.jhove.module.iff.*; import java.io.*; -//import java.util.*; + +// import java.util.*; /** - * Implementation of the WAVE AXML Chunk, which - * contains arbitrary XML metadata, as specified in - * Specification of the Broadcast Wave Format: - * A format for audio data files in broadcasting; - * Supplement 5: <axml> Chunk - * (European Broadcasting Union) + * Implementation of the WAVE AXML Chunk, which contains arbitrary XML metadata, as specified in + * Specification of the Broadcast Wave Format: A format for audio data files in broadcasting; + * Supplement 5: <axml> Chunk (European Broadcasting Union) * * @author Gary McGath - * */ public class AXMLChunk extends Chunk { - /** - * Constructor. - * - * @param module The WaveModule under which this was called - * @param hdr The header for this chunk - * @param dstrm The stream from which the WAVE data are being read - */ - public AXMLChunk ( - ModuleBase module, - ChunkHeader hdr, - DataInputStream dstrm) { - super(module, hdr, dstrm); - } + /** + * Constructor. + * + * @param module The WaveModule under which this was called + * @param hdr The header for this chunk + * @param dstrm The stream from which the WAVE data are being read + */ + public AXMLChunk(ModuleBase module, ChunkHeader hdr, DataInputStream dstrm) { + super(module, hdr, dstrm); + } + /** + * Reads a chunk and puts a BroadcastAudioExtension Property into the RepInfo object. + * + * @return false if the chunk is structurally invalid, otherwise true + */ + @Override + public boolean readChunk(RepInfo info) throws IOException { + WaveModule module = (WaveModule) _module; + byte[] bbuf = new byte[(int) bytesLeft]; - /** Reads a chunk and puts a BroadcastAudioExtension Property into - * the RepInfo object. - * - * @return false if the chunk is structurally - * invalid, otherwise true - */ - @Override - public boolean readChunk(RepInfo info) throws IOException { - WaveModule module = (WaveModule) _module; - byte[] bbuf = new byte[(int) bytesLeft]; - - ModuleBase.readByteBuf (_dstream, bbuf, _module); - String xmlData = new String (bbuf); - module.addWaveProperty (new Property ("XML", - PropertyType.STRING, - xmlData)); - return true; - } + ModuleBase.readByteBuf(_dstream, bbuf, _module); + String xmlData = new String(bbuf); + module.addWaveProperty(new Property("XML", PropertyType.STRING, xmlData)); + return true; + } } diff --git a/jhove-modules/wave-hul/src/main/java/edu/harvard/hul/ois/jhove/module/wave/AssocDataListChunk.java b/jhove-modules/wave-hul/src/main/java/edu/harvard/hul/ois/jhove/module/wave/AssocDataListChunk.java index e64d8b590..85e32466c 100644 --- a/jhove-modules/wave-hul/src/main/java/edu/harvard/hul/ois/jhove/module/wave/AssocDataListChunk.java +++ b/jhove-modules/wave-hul/src/main/java/edu/harvard/hul/ois/jhove/module/wave/AssocDataListChunk.java @@ -1,102 +1,96 @@ -/********************************************************************** - * Jhove - JSTOR/Harvard Object Validation Environment - * Copyright 2004 by JSTOR and the President and Fellows of Harvard College - **********************************************************************/ - +/** + * ******************************************************************** Jhove - JSTOR/Harvard Object + * Validation Environment Copyright 2004 by JSTOR and the President and Fellows of Harvard College + * ******************************************************************** + */ package edu.harvard.hul.ois.jhove.module.wave; -import java.io.DataInputStream; -import java.io.IOException; - import edu.harvard.hul.ois.jhove.*; import edu.harvard.hul.ois.jhove.messages.JhoveMessage; import edu.harvard.hul.ois.jhove.messages.JhoveMessages; import edu.harvard.hul.ois.jhove.module.WaveModule; import edu.harvard.hul.ois.jhove.module.iff.*; +import java.io.DataInputStream; +import java.io.IOException; /** - * The associated data list ('list') chunk, which is different from - * the RIFF 'LIST' chunk, ListInfoTextChunk. It can contain - * several different types of informational chunks. + * The associated data list ('list') chunk, which is different from the RIFF 'LIST' chunk, + * ListInfoTextChunk. It can contain several different types of informational chunks. * * @author Gary McGath */ public class AssocDataListChunk extends Superchunk { - private static final int TYPE_LENGTH = 4; + private static final int TYPE_LENGTH = 4; - /** - * Constructor. - * - * @param module - * The WaveModule under which this was called - * @param hdr - * The header for this chunk - * @param dstrm - * The stream from which the WAVE data are being read - * @param info - * RepInfo object for error reporting - */ - public AssocDataListChunk(ModuleBase module, ChunkHeader hdr, - DataInputStream dstrm, RepInfo info) { - super(module, hdr, dstrm, info); - } + /** + * Constructor. + * + * @param module The WaveModule under which this was called + * @param hdr The header for this chunk + * @param dstrm The stream from which the WAVE data are being read + * @param info RepInfo object for error reporting + */ + public AssocDataListChunk( + ModuleBase module, ChunkHeader hdr, DataInputStream dstrm, RepInfo info) { + super(module, hdr, dstrm, info); + } - /** - * Reads the chunk and its nested chunks, and puts appropriate - * properties into the RepInfo object. - * - * @return false if the chunk or a nested chunk - * is structurally - * invalid, otherwise true - */ - @Override - public boolean readChunk(RepInfo info) throws IOException { - WaveModule module = (WaveModule) _module; + /** + * Reads the chunk and its nested chunks, and puts appropriate properties into the RepInfo object. + * + * @return false if the chunk or a nested chunk is structurally invalid, otherwise + * true + */ + @Override + public boolean readChunk(RepInfo info) throws IOException { + WaveModule module = (WaveModule) _module; - // The chunk has a type ID, which is always "adtl". Presumably - // this was intended to allow other list structures (don't ask - // why), but any others will be considered non-conforming. - String typeID = module.read4Chars(_dstream); - bytesLeft -= TYPE_LENGTH; - if (!"adtl".equals(typeID)) { - info.setMessage(new ErrorMessage(MessageConstants.WAVE_HUL_9, - String.format(MessageConstants.WAVE_HUL_9_SUB.getMessage(), - typeID), - _module.getNByte() - TYPE_LENGTH)); - info.setWellFormed(false); - return false; - } - for (;;) { - ChunkHeader chunkh = getNextChunkHeader(); - if (chunkh == null) { - break; - } - Chunk chunk = null; - // The chunk list can include Labels, Notes, and - // Labelled Text. - String id = chunkh.getID(); - int chunkSize = (int) chunkh.getSize(); - if ("labl".equals(id)) { - chunk = new LabelChunk(_module, chunkh, _dstream); - } else if ("note".equals(id)) { - chunk = new NoteChunk(_module, chunkh, _dstream); - } else if ("ltxt".equals(id)) { - chunk = new LabeledTextChunk(_module, chunkh, _dstream); - } + // The chunk has a type ID, which is always "adtl". Presumably + // this was intended to allow other list structures (don't ask + // why), but any others will be considered non-conforming. + String typeID = module.read4Chars(_dstream); + bytesLeft -= TYPE_LENGTH; + if (!"adtl".equals(typeID)) { + info.setMessage( + new ErrorMessage( + MessageConstants.WAVE_HUL_9, + String.format(MessageConstants.WAVE_HUL_9_SUB.getMessage(), typeID), + _module.getNByte() - TYPE_LENGTH)); + info.setWellFormed(false); + return false; + } + for (; ; ) { + ChunkHeader chunkh = getNextChunkHeader(); + if (chunkh == null) { + break; + } + Chunk chunk = null; + // The chunk list can include Labels, Notes, and + // Labelled Text. + String id = chunkh.getID(); + int chunkSize = (int) chunkh.getSize(); + if ("labl".equals(id)) { + chunk = new LabelChunk(_module, chunkh, _dstream); + } else if ("note".equals(id)) { + chunk = new NoteChunk(_module, chunkh, _dstream); + } else if ("ltxt".equals(id)) { + chunk = new LabeledTextChunk(_module, chunkh, _dstream); + } - if (chunk == null) { - _module.skipBytes(_dstream, chunkSize, _module); - JhoveMessage message = JhoveMessages.getMessageInstance( - MessageConstants.WAVE_HUL_10.getId(), String.format( - MessageConstants.WAVE_HUL_10.getMessage(), id)); - info.setMessage(new InfoMessage(message)); - } else { - if (!chunk.readChunk(info)) { - return false; - } - } - } - return true; - } + if (chunk == null) { + _module.skipBytes(_dstream, chunkSize, _module); + JhoveMessage message = + JhoveMessages.getMessageInstance( + MessageConstants.WAVE_HUL_10.getId(), + String.format(MessageConstants.WAVE_HUL_10.getMessage(), id)); + info.setMessage(new InfoMessage(message)); + } else { + if (!chunk.readChunk(info)) { + return false; + } + } + } + return true; + } } diff --git a/jhove-modules/wave-hul/src/main/java/edu/harvard/hul/ois/jhove/module/wave/BroadcastExtChunk.java b/jhove-modules/wave-hul/src/main/java/edu/harvard/hul/ois/jhove/module/wave/BroadcastExtChunk.java index 0d1dc269a..ed4b60cf3 100644 --- a/jhove-modules/wave-hul/src/main/java/edu/harvard/hul/ois/jhove/module/wave/BroadcastExtChunk.java +++ b/jhove-modules/wave-hul/src/main/java/edu/harvard/hul/ois/jhove/module/wave/BroadcastExtChunk.java @@ -1,18 +1,10 @@ -/********************************************************************** - * JHOVE - JSTOR/Harvard Object Validation Environment - * Copyright 2004 by JSTOR and the President and Fellows of Harvard College - **********************************************************************/ - +/** + * ******************************************************************** JHOVE - JSTOR/Harvard Object + * Validation Environment Copyright 2004 by JSTOR and the President and Fellows of Harvard College + * ******************************************************************** + */ package edu.harvard.hul.ois.jhove.module.wave; -import java.io.DataInputStream; -import java.io.IOException; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; - -import javax.xml.bind.DatatypeConverter; - import edu.harvard.hul.ois.jhove.AESAudioMetadata; import edu.harvard.hul.ois.jhove.InfoMessage; import edu.harvard.hul.ois.jhove.ModuleBase; @@ -25,6 +17,12 @@ import edu.harvard.hul.ois.jhove.module.WaveModule; import edu.harvard.hul.ois.jhove.module.iff.Chunk; import edu.harvard.hul.ois.jhove.module.iff.ChunkHeader; +import java.io.DataInputStream; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import javax.xml.bind.DatatypeConverter; /** * Implementation of the WAVE Broadcast Audio Extension Chunk. @@ -33,234 +31,213 @@ */ public class BroadcastExtChunk extends Chunk { - private final static int BASE_CHUNK_SIZE = 602; - private final static int VER_0_RESERVED_LENGTH = 254; - private final static int VER_1_RESERVED_LENGTH = 190; - private final static int VER_2_RESERVED_LENGTH = 180; - private final static int UNUSED_LOUDNESS_FIELD = 0x7FFF; - - /** - * Constructor. - * - * @param module - * The WaveModule under which this was called - * @param hdr - * The header for this chunk - * @param dstrm - * The stream from which the WAVE data are being read - */ - public BroadcastExtChunk(ModuleBase module, ChunkHeader hdr, - DataInputStream dstrm) { - super(module, hdr, dstrm); - } - - /** - * Reads a chunk and puts a BroadcastAudioExtension Property into - * the RepInfo object. - * - * @return false if the chunk is structurally - * invalid, otherwise true - */ - @Override - public boolean readChunk(RepInfo info) throws IOException { - - WaveModule module = (WaveModule) _module; - - byte[] buf256 = new byte[256]; - ModuleBase.readByteBuf(_dstream, buf256, module); - String description = byteBufString(buf256); - - byte[] buf32 = new byte[32]; - ModuleBase.readByteBuf(_dstream, buf32, module); - String originator = byteBufString(buf32); - - ModuleBase.readByteBuf(_dstream, buf32, module); - String originatorRef = byteBufString(buf32); - - byte[] buf10 = new byte[10]; - ModuleBase.readByteBuf(_dstream, buf10, module); - String originationDate = byteBufString(buf10); - - byte[] buf8 = new byte[8]; - ModuleBase.readByteBuf(_dstream, buf8, module); - String originationTime = byteBufString(buf8); - - long timeReference = module.readSignedLong(_dstream); - int version = module.readUnsignedShort(_dstream); - - String umid = ""; - if (version >= 1) { - byte[] buf64 = new byte[64]; - ModuleBase.readByteBuf(_dstream, buf64, module); - umid = formatUmid(buf64); - } - - String loudnessValue = ""; - String loudnessRange = ""; - String maxTruePeakLevel = ""; - String maxMomentaryLoudness = ""; - String maxShortTermLoudness = ""; - if (version >= 2) { - loudnessValue = formatLoudness(module.readSignedShort(_dstream)); - loudnessRange = formatLoudness(module.readSignedShort(_dstream)); - maxTruePeakLevel = formatLoudness(module.readSignedShort(_dstream)); - maxMomentaryLoudness = formatLoudness( - module.readSignedShort(_dstream)); - maxShortTermLoudness = formatLoudness( - module.readSignedShort(_dstream)); - } - - if (version == 0) { - module.skipBytes(_dstream, VER_0_RESERVED_LENGTH, module); - } else if (version == 1) { - module.skipBytes(_dstream, VER_1_RESERVED_LENGTH, module); - } else if (version == 2) { - module.skipBytes(_dstream, VER_2_RESERVED_LENGTH, module); - } else { - // If it's a higher version, we can't read its fields, - // so skip any remaining reserved bytes anyway. - module.skipBytes(_dstream, VER_2_RESERVED_LENGTH, module); - JhoveMessage message = JhoveMessages.getMessageInstance( - MessageConstants.WAVE_HUL_27.getId(), - String.format(MessageConstants.WAVE_HUL_27.getMessage(), - Integer.valueOf(version))); - info.setMessage(new InfoMessage(message)); - } - - String codingHistory = ""; - int codingHistorySize = (int) chunkSize - BASE_CHUNK_SIZE; - if (codingHistorySize > 0) { - byte[] bufCodingHistory = new byte[codingHistorySize]; - ModuleBase.readByteBuf(_dstream, bufCodingHistory, module); - codingHistory = byteBufString(bufCodingHistory); - } - - // Whew -- we've read the whole thing. Now make that into a - // list of Properties. - List plist = new ArrayList<>(14); - - if (!description.isEmpty()) { - plist.add(new Property("Description", PropertyType.STRING, - description)); - } - if (!originator.isEmpty()) { - plist.add(new Property("Originator", PropertyType.STRING, - originator)); - } - if (!originatorRef.isEmpty()) { - plist.add(new Property("OriginatorReference", PropertyType.STRING, - originatorRef)); - } - if (!originationDate.isEmpty()) { - plist.add(new Property("OriginationDate", PropertyType.STRING, - originationDate)); - } - if (!originationTime.isEmpty()) { - plist.add(new Property("OriginationTime", PropertyType.STRING, - originationTime)); - } - - plist.add(new Property("TimeReference", PropertyType.LONG, - Long.valueOf(timeReference))); - plist.add(new Property("Version", PropertyType.INTEGER, - Integer.valueOf(version))); - - if (!umid.isEmpty()) { - plist.add(new Property("UMID", PropertyType.STRING, umid)); - } - - if (!loudnessValue.isEmpty()) { - plist.add(new Property("LoudnessValue", PropertyType.STRING, - loudnessValue)); - } - if (!loudnessRange.isEmpty()) { - plist.add(new Property("LoudnessRange", PropertyType.STRING, - loudnessRange)); - } - if (!maxTruePeakLevel.isEmpty()) { - plist.add(new Property("MaxTruePeakLevel", PropertyType.STRING, - maxTruePeakLevel)); - } - if (!maxMomentaryLoudness.isEmpty()) { - plist.add(new Property("MaxMomentaryLoudness", PropertyType.STRING, - maxMomentaryLoudness)); - } - if (!maxShortTermLoudness.isEmpty()) { - plist.add(new Property("MaxShortTermLoudness", PropertyType.STRING, - maxShortTermLoudness)); - } - - if (!codingHistory.isEmpty()) { - plist.add(new Property("CodingHistory", PropertyType.STRING, - codingHistory)); - } - - module.addWaveProperty(new Property("BroadcastAudioExtension", - PropertyType.PROPERTY, PropertyArity.LIST, plist)); - - AESAudioMetadata aes = module.getAESMetadata(); - aes.setStartTime(timeReference); - - return true; - } - - /** - * Returns a UMID formatted as a hexadecimal string. - * - * @return an empty String if no UMID is found; a basic UMID if the - * extension space is empty; and an extended UMID if it isn't - */ - private static String formatUmid(byte[] umid) { - - String formattedUmid = ""; - - byte[] basicUmid = Arrays.copyOfRange(umid, 0, 32); - byte[] umidExtension = Arrays.copyOfRange(umid, 32, 64); - - boolean basicUmidExists = hasValue(basicUmid); - boolean extendedUmidExists = false; - - if (basicUmidExists) { - extendedUmidExists = hasValue(umidExtension); - } - - if (extendedUmidExists) { - formattedUmid = DatatypeConverter.printHexBinary(umid); - } else if (basicUmidExists) { - formattedUmid = DatatypeConverter.printHexBinary(basicUmid); - } - - return formattedUmid; - } - - /** Checks for a non-zero value in an array of bytes. */ - private static boolean hasValue(byte[] byteArray) { - - for (byte b : byteArray) { - if (b != 0) { - return true; - } - } - return false; - } - - /** - * Returns a properly formatted loudness value. - * - * Loudness fields store integer values accurate to two decimal places by - * multiplying the original value by 100, and rounding away any remainder. - * To recover the original value, we do the reverse. - * - * Unused fields should contain the value 0x7FFF. - */ - private static String formatLoudness(int value) { - - String formattedValue = ""; - - if (value != UNUSED_LOUDNESS_FIELD) { - formattedValue = String.format("%.2f", Float.valueOf(value / 100f)); - } - - return formattedValue; - } + private static final int BASE_CHUNK_SIZE = 602; + private static final int VER_0_RESERVED_LENGTH = 254; + private static final int VER_1_RESERVED_LENGTH = 190; + private static final int VER_2_RESERVED_LENGTH = 180; + private static final int UNUSED_LOUDNESS_FIELD = 0x7FFF; + + /** + * Constructor. + * + * @param module The WaveModule under which this was called + * @param hdr The header for this chunk + * @param dstrm The stream from which the WAVE data are being read + */ + public BroadcastExtChunk(ModuleBase module, ChunkHeader hdr, DataInputStream dstrm) { + super(module, hdr, dstrm); + } + + /** + * Reads a chunk and puts a BroadcastAudioExtension Property into the RepInfo object. + * + * @return false if the chunk is structurally invalid, otherwise true + */ + @Override + public boolean readChunk(RepInfo info) throws IOException { + + WaveModule module = (WaveModule) _module; + + byte[] buf256 = new byte[256]; + ModuleBase.readByteBuf(_dstream, buf256, module); + String description = byteBufString(buf256); + + byte[] buf32 = new byte[32]; + ModuleBase.readByteBuf(_dstream, buf32, module); + String originator = byteBufString(buf32); + + ModuleBase.readByteBuf(_dstream, buf32, module); + String originatorRef = byteBufString(buf32); + + byte[] buf10 = new byte[10]; + ModuleBase.readByteBuf(_dstream, buf10, module); + String originationDate = byteBufString(buf10); + + byte[] buf8 = new byte[8]; + ModuleBase.readByteBuf(_dstream, buf8, module); + String originationTime = byteBufString(buf8); + + long timeReference = module.readSignedLong(_dstream); + int version = module.readUnsignedShort(_dstream); + + String umid = ""; + if (version >= 1) { + byte[] buf64 = new byte[64]; + ModuleBase.readByteBuf(_dstream, buf64, module); + umid = formatUmid(buf64); + } + + String loudnessValue = ""; + String loudnessRange = ""; + String maxTruePeakLevel = ""; + String maxMomentaryLoudness = ""; + String maxShortTermLoudness = ""; + if (version >= 2) { + loudnessValue = formatLoudness(module.readSignedShort(_dstream)); + loudnessRange = formatLoudness(module.readSignedShort(_dstream)); + maxTruePeakLevel = formatLoudness(module.readSignedShort(_dstream)); + maxMomentaryLoudness = formatLoudness(module.readSignedShort(_dstream)); + maxShortTermLoudness = formatLoudness(module.readSignedShort(_dstream)); + } + + if (version == 0) { + module.skipBytes(_dstream, VER_0_RESERVED_LENGTH, module); + } else if (version == 1) { + module.skipBytes(_dstream, VER_1_RESERVED_LENGTH, module); + } else if (version == 2) { + module.skipBytes(_dstream, VER_2_RESERVED_LENGTH, module); + } else { + // If it's a higher version, we can't read its fields, + // so skip any remaining reserved bytes anyway. + module.skipBytes(_dstream, VER_2_RESERVED_LENGTH, module); + JhoveMessage message = + JhoveMessages.getMessageInstance( + MessageConstants.WAVE_HUL_27.getId(), + String.format(MessageConstants.WAVE_HUL_27.getMessage(), Integer.valueOf(version))); + info.setMessage(new InfoMessage(message)); + } + + String codingHistory = ""; + int codingHistorySize = (int) chunkSize - BASE_CHUNK_SIZE; + if (codingHistorySize > 0) { + byte[] bufCodingHistory = new byte[codingHistorySize]; + ModuleBase.readByteBuf(_dstream, bufCodingHistory, module); + codingHistory = byteBufString(bufCodingHistory); + } + + // Whew -- we've read the whole thing. Now make that into a + // list of Properties. + List plist = new ArrayList<>(14); + + if (!description.isEmpty()) { + plist.add(new Property("Description", PropertyType.STRING, description)); + } + if (!originator.isEmpty()) { + plist.add(new Property("Originator", PropertyType.STRING, originator)); + } + if (!originatorRef.isEmpty()) { + plist.add(new Property("OriginatorReference", PropertyType.STRING, originatorRef)); + } + if (!originationDate.isEmpty()) { + plist.add(new Property("OriginationDate", PropertyType.STRING, originationDate)); + } + if (!originationTime.isEmpty()) { + plist.add(new Property("OriginationTime", PropertyType.STRING, originationTime)); + } + + plist.add(new Property("TimeReference", PropertyType.LONG, Long.valueOf(timeReference))); + plist.add(new Property("Version", PropertyType.INTEGER, Integer.valueOf(version))); + + if (!umid.isEmpty()) { + plist.add(new Property("UMID", PropertyType.STRING, umid)); + } + + if (!loudnessValue.isEmpty()) { + plist.add(new Property("LoudnessValue", PropertyType.STRING, loudnessValue)); + } + if (!loudnessRange.isEmpty()) { + plist.add(new Property("LoudnessRange", PropertyType.STRING, loudnessRange)); + } + if (!maxTruePeakLevel.isEmpty()) { + plist.add(new Property("MaxTruePeakLevel", PropertyType.STRING, maxTruePeakLevel)); + } + if (!maxMomentaryLoudness.isEmpty()) { + plist.add(new Property("MaxMomentaryLoudness", PropertyType.STRING, maxMomentaryLoudness)); + } + if (!maxShortTermLoudness.isEmpty()) { + plist.add(new Property("MaxShortTermLoudness", PropertyType.STRING, maxShortTermLoudness)); + } + + if (!codingHistory.isEmpty()) { + plist.add(new Property("CodingHistory", PropertyType.STRING, codingHistory)); + } + + module.addWaveProperty( + new Property("BroadcastAudioExtension", PropertyType.PROPERTY, PropertyArity.LIST, plist)); + + AESAudioMetadata aes = module.getAESMetadata(); + aes.setStartTime(timeReference); + + return true; + } + + /** + * Returns a UMID formatted as a hexadecimal string. + * + * @return an empty String if no UMID is found; a basic UMID if the extension space is empty; and + * an extended UMID if it isn't + */ + private static String formatUmid(byte[] umid) { + + String formattedUmid = ""; + + byte[] basicUmid = Arrays.copyOfRange(umid, 0, 32); + byte[] umidExtension = Arrays.copyOfRange(umid, 32, 64); + + boolean basicUmidExists = hasValue(basicUmid); + boolean extendedUmidExists = false; + + if (basicUmidExists) { + extendedUmidExists = hasValue(umidExtension); + } + + if (extendedUmidExists) { + formattedUmid = DatatypeConverter.printHexBinary(umid); + } else if (basicUmidExists) { + formattedUmid = DatatypeConverter.printHexBinary(basicUmid); + } + + return formattedUmid; + } + + /** Checks for a non-zero value in an array of bytes. */ + private static boolean hasValue(byte[] byteArray) { + + for (byte b : byteArray) { + if (b != 0) { + return true; + } + } + return false; + } + + /** + * Returns a properly formatted loudness value. + * + *

Loudness fields store integer values accurate to two decimal places by multiplying the + * original value by 100, and rounding away any remainder. To recover the original value, we do + * the reverse. + * + *

Unused fields should contain the value 0x7FFF. + */ + private static String formatLoudness(int value) { + + String formattedValue = ""; + + if (value != UNUSED_LOUDNESS_FIELD) { + formattedValue = String.format("%.2f", Float.valueOf(value / 100f)); + } + + return formattedValue; + } } diff --git a/jhove-modules/wave-hul/src/main/java/edu/harvard/hul/ois/jhove/module/wave/CartChunk.java b/jhove-modules/wave-hul/src/main/java/edu/harvard/hul/ois/jhove/module/wave/CartChunk.java index f5d09a5f1..a7b931965 100644 --- a/jhove-modules/wave-hul/src/main/java/edu/harvard/hul/ois/jhove/module/wave/CartChunk.java +++ b/jhove-modules/wave-hul/src/main/java/edu/harvard/hul/ois/jhove/module/wave/CartChunk.java @@ -1,15 +1,14 @@ -/********************************************************************** - * Jhove - JSTOR/Harvard Object Validation Environment - * Copyright 2004 by JSTOR and the President and Fellows of Harvard College - **********************************************************************/ - +/** + * ******************************************************************** Jhove - JSTOR/Harvard Object + * Validation Environment Copyright 2004 by JSTOR and the President and Fellows of Harvard College + * ******************************************************************** + */ package edu.harvard.hul.ois.jhove.module.wave; - -import edu.harvard.hul.ois.jhove.RepInfo; -import edu.harvard.hul.ois.jhove.module.iff.*; import edu.harvard.hul.ois.jhove.*; +import edu.harvard.hul.ois.jhove.RepInfo; import edu.harvard.hul.ois.jhove.module.WaveModule; +import edu.harvard.hul.ois.jhove.module.iff.*; import java.io.*; import java.util.*; @@ -17,181 +16,159 @@ * Implementation of the WAVE Cart Chunk. * * @author Gary McGath - * */ public class CartChunk extends Chunk { - /** Number of timer tags. This is a fixed value specified by the - * chunk definition. */ - private static final int N_TIMER_TAGS = 8; - - /** - * Constructor. - * - * @param module The WaveModule under which this was called - * @param hdr The header for this chunk - * @param dstrm The stream from which the WAVE data are being read - */ - public CartChunk ( - ModuleBase module, - ChunkHeader hdr, - DataInputStream dstrm) { - super(module, hdr, dstrm); - } + /** Number of timer tags. This is a fixed value specified by the chunk definition. */ + private static final int N_TIMER_TAGS = 8; + + /** + * Constructor. + * + * @param module The WaveModule under which this was called + * @param hdr The header for this chunk + * @param dstrm The stream from which the WAVE data are being read + */ + public CartChunk(ModuleBase module, ChunkHeader hdr, DataInputStream dstrm) { + super(module, hdr, dstrm); + } + + /** + * Reads a chunk and puts a Cart Property into the RepInfo object. + * + * @return false if the chunk is structurally invalid, otherwise true + */ + @Override + public boolean readChunk(RepInfo info) throws IOException { + WaveModule module = (WaveModule) _module; + byte[] buf4 = new byte[4]; + ModuleBase.readByteBuf(_dstream, buf4, module); + String version = byteBufString(buf4); + // Title and most other fields within this chunk are ASCII strings + // with a fixed allocation; calling trim() is necessary to get rid + // of nulls. + byte[] buf64 = new byte[64]; + ModuleBase.readByteBuf(_dstream, buf64, module); + String title = byteBufString(buf64); + ModuleBase.readByteBuf(_dstream, buf64, module); + String artist = byteBufString(buf64); + ModuleBase.readByteBuf(_dstream, buf64, module); + String cutID = byteBufString(buf64); + ModuleBase.readByteBuf(_dstream, buf64, module); + String clientID = byteBufString(buf64); + ModuleBase.readByteBuf(_dstream, buf64, module); + String category = byteBufString(buf64); + ModuleBase.readByteBuf(_dstream, buf64, module); + String classification = byteBufString(buf64); + ModuleBase.readByteBuf(_dstream, buf64, module); + String outCue = byteBufString(buf64); + byte[] buf10 = new byte[10]; + ModuleBase.readByteBuf(_dstream, buf10, module); + String startDate = byteBufString(buf10); + byte[] buf8 = new byte[8]; + ModuleBase.readByteBuf(_dstream, buf8, module); + String startTime = byteBufString(buf8); + + ModuleBase.readByteBuf(_dstream, buf10, module); + String endDate = byteBufString(buf10); + ModuleBase.readByteBuf(_dstream, buf8, module); + String endTime = byteBufString(buf8); + + ModuleBase.readByteBuf(_dstream, buf64, module); + String producerAppID = byteBufString(buf64); + ModuleBase.readByteBuf(_dstream, buf64, module); + String producerAppVersion = byteBufString(buf64); + ModuleBase.readByteBuf(_dstream, buf64, module); + String userDef = byteBufString(buf64); - /** Reads a chunk and puts a Cart Property into - * the RepInfo object. - * - * @return false if the chunk is structurally - * invalid, otherwise true - */ - @Override - public boolean readChunk(RepInfo info) throws IOException { - WaveModule module = (WaveModule) _module; - byte[] buf4 = new byte[4]; - ModuleBase.readByteBuf (_dstream, buf4, module); - String version = byteBufString(buf4); - // Title and most other fields within this chunk are ASCII strings - // with a fixed allocation; calling trim() is necessary to get rid - // of nulls. - byte[] buf64 = new byte[64]; - ModuleBase.readByteBuf (_dstream, buf64, module); - String title = byteBufString (buf64); - ModuleBase.readByteBuf (_dstream, buf64, module); - String artist = byteBufString (buf64); - ModuleBase.readByteBuf (_dstream, buf64, module); - String cutID = byteBufString (buf64); - ModuleBase.readByteBuf (_dstream, buf64, module); - String clientID = byteBufString (buf64); - ModuleBase.readByteBuf (_dstream, buf64, module); - String category = byteBufString (buf64); - ModuleBase.readByteBuf (_dstream, buf64, module); - String classification = byteBufString (buf64); - ModuleBase.readByteBuf (_dstream, buf64, module); - String outCue = byteBufString (buf64); - byte[] buf10 = new byte[10]; - ModuleBase.readByteBuf (_dstream, buf10, module); - String startDate = byteBufString (buf10); - byte[] buf8 = new byte[8]; - ModuleBase.readByteBuf (_dstream, buf8, module); - String startTime = byteBufString (buf8); + int levelReference = module.readSignedInt(_dstream); - ModuleBase.readByteBuf (_dstream, buf10, module); - String endDate = byteBufString (buf10); - ModuleBase.readByteBuf (_dstream, buf8, module); - String endTime = byteBufString (buf8); + List timerTags = new ArrayList(N_TIMER_TAGS); + for (int i = 0; i < N_TIMER_TAGS; i++) { + String timerTagUsage = module.read4Chars(_dstream).trim(); + long timerTagValue = module.readUnsignedInt(_dstream); + if (timerTagUsage.length() > 0) { + Property[] ttprop = new Property[2]; + ttprop[0] = new Property("Usage", PropertyType.STRING, timerTagUsage); + ttprop[1] = new Property("Value", PropertyType.LONG, new Long(timerTagValue)); - ModuleBase.readByteBuf (_dstream, buf64, module); - String producerAppID = byteBufString (buf64); - ModuleBase.readByteBuf (_dstream, buf64, module); - String producerAppVersion = byteBufString (buf64); - ModuleBase.readByteBuf (_dstream, buf64, module); - String userDef = byteBufString (buf64); + timerTags.add( + new Property("PostTimer", PropertyType.PROPERTY, PropertyArity.ARRAY, ttprop)); + } + } + module.skipBytes(_dstream, 276, module); + + byte[] buf1k = new byte[1024]; + ModuleBase.readByteBuf(_dstream, buf1k, module); + String url = byteBufString(buf1k); + + String tagText = ""; + if (bytesLeft > 2048) { + byte[] bufTagText = new byte[(int) bytesLeft - 2048]; + ModuleBase.readByteBuf(_dstream, bufTagText, module); + tagText = byteBufString(bufTagText); + } - int levelReference = module.readSignedInt(_dstream); - - List timerTags = new ArrayList (N_TIMER_TAGS); - for (int i = 0; i < N_TIMER_TAGS; i++) { - String timerTagUsage = module.read4Chars(_dstream).trim (); - long timerTagValue = module.readUnsignedInt (_dstream); - if (timerTagUsage.length () > 0) { - Property[] ttprop = new Property[2]; - ttprop[0] = new Property ("Usage", - PropertyType.STRING, - timerTagUsage); - ttprop[1] = new Property ("Value", - PropertyType.LONG, - new Long (timerTagValue)); - - timerTags.add (new Property ("PostTimer", - PropertyType.PROPERTY, - PropertyArity.ARRAY, - ttprop)); - } - } - module.skipBytes (_dstream, 276, module); - - byte[] buf1k = new byte[1024]; - ModuleBase.readByteBuf (_dstream, buf1k, module); - String url = byteBufString (buf1k); - - String tagText = ""; - if (bytesLeft > 2048) { - byte[] bufTagText = new byte[(int) bytesLeft - 2048]; - ModuleBase.readByteBuf (_dstream, bufTagText, module); - tagText = byteBufString (bufTagText); - } - - // Whew -- we've read the whole thing. Now make that into a - // list of Properties. - List plist = new ArrayList (20); - if (version. length () > 0) { - plist.add (new Property ("Version", PropertyType.STRING, version)); - } - if (title.length() > 0) { - plist.add (new Property ("Title", PropertyType.STRING, title)); - } - if (artist.length () > 0) { - plist.add (new Property ("Artist", PropertyType.STRING, artist)); - } - if (cutID.length () > 0) { - plist.add (new Property ("CutID", PropertyType.STRING, cutID)); - } - if (clientID.length () > 0) { - plist.add (new Property ("ClientID", PropertyType.STRING, clientID)); - } - if (category.length() > 0) { - plist.add (new Property ("Category", PropertyType.STRING, category)); - } - if (classification.length () > 0) { - plist.add (new Property - ("Classification", PropertyType.STRING, classification)); - } - if (outCue.length () > 0) { - plist.add (new Property ("OutCue", PropertyType.STRING, outCue)); - } - if (startDate.length () > 0) { - plist.add (new Property ("StartDate", PropertyType.STRING, startDate)); - } - if (startTime.length () > 0) { - plist.add (new Property ("StartTime", PropertyType.STRING, startTime)); - } - if (endDate.length () > 0) { - plist.add (new Property ("EndDate", PropertyType.STRING, endDate)); - } - if (endTime.length () > 0) { - plist.add (new Property ("EndTime", PropertyType.STRING, startTime)); - } - if (producerAppID.length () > 0) { - plist.add (new Property - ("ProducerAppID", PropertyType.STRING, producerAppID)); - } - if (producerAppVersion.length () > 0) { - plist.add (new Property - ("ProducerAppVersion", PropertyType.STRING, producerAppVersion)); - } - if (userDef.length () > 0) { - plist.add (new Property ("UserDef", PropertyType.STRING, userDef)); - } - plist.add (new Property ("LevelReference", PropertyType.INTEGER, - new Integer (levelReference))); - if (timerTags.size() > 0) { - plist.add (new Property ("PostTimers", PropertyType.PROPERTY, - PropertyArity.LIST, - timerTags)); - } - if (url.length () > 0) { - plist.add (new Property ("URL", PropertyType.STRING, url)); - } - if (tagText.length () > 0) { - plist.add (new Property ("TagText", PropertyType.STRING, tagText)); - } - - module.addWaveProperty (new Property ("Cart", - PropertyType.PROPERTY, - PropertyArity.LIST, - plist)); - return true; + // Whew -- we've read the whole thing. Now make that into a + // list of Properties. + List plist = new ArrayList(20); + if (version.length() > 0) { + plist.add(new Property("Version", PropertyType.STRING, version)); + } + if (title.length() > 0) { + plist.add(new Property("Title", PropertyType.STRING, title)); + } + if (artist.length() > 0) { + plist.add(new Property("Artist", PropertyType.STRING, artist)); + } + if (cutID.length() > 0) { + plist.add(new Property("CutID", PropertyType.STRING, cutID)); + } + if (clientID.length() > 0) { + plist.add(new Property("ClientID", PropertyType.STRING, clientID)); + } + if (category.length() > 0) { + plist.add(new Property("Category", PropertyType.STRING, category)); + } + if (classification.length() > 0) { + plist.add(new Property("Classification", PropertyType.STRING, classification)); + } + if (outCue.length() > 0) { + plist.add(new Property("OutCue", PropertyType.STRING, outCue)); + } + if (startDate.length() > 0) { + plist.add(new Property("StartDate", PropertyType.STRING, startDate)); + } + if (startTime.length() > 0) { + plist.add(new Property("StartTime", PropertyType.STRING, startTime)); + } + if (endDate.length() > 0) { + plist.add(new Property("EndDate", PropertyType.STRING, endDate)); + } + if (endTime.length() > 0) { + plist.add(new Property("EndTime", PropertyType.STRING, startTime)); + } + if (producerAppID.length() > 0) { + plist.add(new Property("ProducerAppID", PropertyType.STRING, producerAppID)); + } + if (producerAppVersion.length() > 0) { + plist.add(new Property("ProducerAppVersion", PropertyType.STRING, producerAppVersion)); + } + if (userDef.length() > 0) { + plist.add(new Property("UserDef", PropertyType.STRING, userDef)); + } + plist.add(new Property("LevelReference", PropertyType.INTEGER, new Integer(levelReference))); + if (timerTags.size() > 0) { + plist.add(new Property("PostTimers", PropertyType.PROPERTY, PropertyArity.LIST, timerTags)); + } + if (url.length() > 0) { + plist.add(new Property("URL", PropertyType.STRING, url)); + } + if (tagText.length() > 0) { + plist.add(new Property("TagText", PropertyType.STRING, tagText)); } + module.addWaveProperty(new Property("Cart", PropertyType.PROPERTY, PropertyArity.LIST, plist)); + return true; + } } diff --git a/jhove-modules/wave-hul/src/main/java/edu/harvard/hul/ois/jhove/module/wave/CueChunk.java b/jhove-modules/wave-hul/src/main/java/edu/harvard/hul/ois/jhove/module/wave/CueChunk.java index 5f9ffdff9..228449fbb 100644 --- a/jhove-modules/wave-hul/src/main/java/edu/harvard/hul/ois/jhove/module/wave/CueChunk.java +++ b/jhove-modules/wave-hul/src/main/java/edu/harvard/hul/ois/jhove/module/wave/CueChunk.java @@ -1,105 +1,82 @@ -/********************************************************************** - * JHOVE - JSTOR/Harvard Object Validation Environment - * Copyright 2004 by JSTOR and the President and Fellows of Harvard College - **********************************************************************/ - +/** + * ******************************************************************** JHOVE - JSTOR/Harvard Object + * Validation Environment Copyright 2004 by JSTOR and the President and Fellows of Harvard College + * ******************************************************************** + */ package edu.harvard.hul.ois.jhove.module.wave; import edu.harvard.hul.ois.jhove.*; import edu.harvard.hul.ois.jhove.module.WaveModule; import edu.harvard.hul.ois.jhove.module.iff.Chunk; import edu.harvard.hul.ois.jhove.module.iff.ChunkHeader; - import java.io.DataInputStream; import java.io.IOException; import java.util.ArrayList; import java.util.List; /** - * Implementation of the WAVE Cue Points chunk, which defines cue - * points in an audio stream. + * Implementation of the WAVE Cue Points chunk, which defines cue points in an audio stream. * * @author Gary McGath */ public class CueChunk extends Chunk { - /** - * Constructor. - * - * @param module The WaveModule under which this was called - * @param hdr The header for this chunk - * @param dstrm The stream from which the WAVE data are being read - */ - public CueChunk( - ModuleBase module, - ChunkHeader hdr, - DataInputStream dstrm) { - super(module, hdr, dstrm); - } + /** + * Constructor. + * + * @param module The WaveModule under which this was called + * @param hdr The header for this chunk + * @param dstrm The stream from which the WAVE data are being read + */ + public CueChunk(ModuleBase module, ChunkHeader hdr, DataInputStream dstrm) { + super(module, hdr, dstrm); + } - /** - * Reads a chunk and puts a CuePoints property into the RepInfo object. - * - * @return false if the chunk is structurally invalid, - * otherwise true - */ - @Override - public boolean readChunk(RepInfo info) throws IOException { - WaveModule module = (WaveModule) _module; - int nPoints = (int) module.readUnsignedInt(_dstream); - List points = new ArrayList<>(nPoints); - for (int i = 0; i < nPoints; i++) { - // Get unique ID for cue point - long dwIdent = module.readUnsignedInt(_dstream); - // Get position in play order - long dwPos = module.readUnsignedInt(_dstream); - // Get chunk ID of referenced chunk ('data' or 'slnt') - String fccID = module.read4Chars(_dstream); - // Get offset to start of chunk -- zero if a single - // Data chunk is used - long dwChunkStart = module.readUnsignedInt(_dstream); - // Get offset to start of block containing position - long dwBlockStart = module.readUnsignedInt(_dstream); - // Get offset from start of block to cue point. - // Note from the web page I'm using as a source: - // Unfortunately, the WAVE documentation is much too ambiguous, - // and doesn't define what it means by the term "sample offset". - // This could mean a byte offset, or it could mean counting - // the sample points (for example, in a 16-bit wave, every - // 2 bytes would be 1 sample point), or it could even mean - // sample frames (as the loop offsets in AIFF are specified). - // Who knows? The guy who conjured up the Cue chunk certainly - // isn't saying. I'm assuming that it's a byte offset, - // like the above 2 fields. - long dwSampleOffset = module.readUnsignedInt(_dstream); - Property[] cueProps = new Property[6]; - cueProps[0] = new Property("ID", - PropertyType.LONG, - Long.valueOf(dwIdent)); - cueProps[1] = new Property("Position", - PropertyType.LONG, - Long.valueOf(dwPos)); - cueProps[2] = new Property("DataChunkID", - PropertyType.STRING, - fccID); - cueProps[3] = new Property("ChunkStart", - PropertyType.LONG, - Long.valueOf(dwChunkStart)); - cueProps[4] = new Property("BlockStart", - PropertyType.LONG, - Long.valueOf(dwBlockStart)); - cueProps[5] = new Property("SampleOffset", - PropertyType.LONG, - Long.valueOf(dwSampleOffset)); - points.add(new Property("CuePoint", - PropertyType.PROPERTY, - PropertyArity.ARRAY, - cueProps)); - } - module.addWaveProperty(new Property("CuePoints", - PropertyType.PROPERTY, - PropertyArity.LIST, - points)); - return true; + /** + * Reads a chunk and puts a CuePoints property into the RepInfo object. + * + * @return false if the chunk is structurally invalid, otherwise true + */ + @Override + public boolean readChunk(RepInfo info) throws IOException { + WaveModule module = (WaveModule) _module; + int nPoints = (int) module.readUnsignedInt(_dstream); + List points = new ArrayList<>(nPoints); + for (int i = 0; i < nPoints; i++) { + // Get unique ID for cue point + long dwIdent = module.readUnsignedInt(_dstream); + // Get position in play order + long dwPos = module.readUnsignedInt(_dstream); + // Get chunk ID of referenced chunk ('data' or 'slnt') + String fccID = module.read4Chars(_dstream); + // Get offset to start of chunk -- zero if a single + // Data chunk is used + long dwChunkStart = module.readUnsignedInt(_dstream); + // Get offset to start of block containing position + long dwBlockStart = module.readUnsignedInt(_dstream); + // Get offset from start of block to cue point. + // Note from the web page I'm using as a source: + // Unfortunately, the WAVE documentation is much too ambiguous, + // and doesn't define what it means by the term "sample offset". + // This could mean a byte offset, or it could mean counting + // the sample points (for example, in a 16-bit wave, every + // 2 bytes would be 1 sample point), or it could even mean + // sample frames (as the loop offsets in AIFF are specified). + // Who knows? The guy who conjured up the Cue chunk certainly + // isn't saying. I'm assuming that it's a byte offset, + // like the above 2 fields. + long dwSampleOffset = module.readUnsignedInt(_dstream); + Property[] cueProps = new Property[6]; + cueProps[0] = new Property("ID", PropertyType.LONG, Long.valueOf(dwIdent)); + cueProps[1] = new Property("Position", PropertyType.LONG, Long.valueOf(dwPos)); + cueProps[2] = new Property("DataChunkID", PropertyType.STRING, fccID); + cueProps[3] = new Property("ChunkStart", PropertyType.LONG, Long.valueOf(dwChunkStart)); + cueProps[4] = new Property("BlockStart", PropertyType.LONG, Long.valueOf(dwBlockStart)); + cueProps[5] = new Property("SampleOffset", PropertyType.LONG, Long.valueOf(dwSampleOffset)); + points.add(new Property("CuePoint", PropertyType.PROPERTY, PropertyArity.ARRAY, cueProps)); } + module.addWaveProperty( + new Property("CuePoints", PropertyType.PROPERTY, PropertyArity.LIST, points)); + return true; + } } diff --git a/jhove-modules/wave-hul/src/main/java/edu/harvard/hul/ois/jhove/module/wave/DataChunk.java b/jhove-modules/wave-hul/src/main/java/edu/harvard/hul/ois/jhove/module/wave/DataChunk.java index e8d5a3c0c..49e310062 100644 --- a/jhove-modules/wave-hul/src/main/java/edu/harvard/hul/ois/jhove/module/wave/DataChunk.java +++ b/jhove-modules/wave-hul/src/main/java/edu/harvard/hul/ois/jhove/module/wave/DataChunk.java @@ -1,76 +1,64 @@ -/********************************************************************** - * Jhove - JSTOR/Harvard Object Validation Environment - * Copyright 2004 by JSTOR and the President and Fellows of Harvard College +/** + * ******************************************************************** Jhove - JSTOR/Harvard Object + * Validation Environment Copyright 2004 by JSTOR and the President and Fellows of Harvard College * - **********************************************************************/ - + *

******************************************************************** + */ package edu.harvard.hul.ois.jhove.module.wave; -import java.io.DataInputStream; -import java.io.IOException; - import edu.harvard.hul.ois.jhove.*; import edu.harvard.hul.ois.jhove.module.WaveModule; import edu.harvard.hul.ois.jhove.module.iff.Chunk; import edu.harvard.hul.ois.jhove.module.iff.ChunkHeader; +import java.io.DataInputStream; +import java.io.IOException; /** * Implementation of the WAVE Data Chunk. - * - * Data Chunks may occur either at the top level (i.e., under the RIFF - * chunk) or under a data list chunk. There can be only one top-level - * Data Chunk. * - * @author Gary McGath + *

Data Chunks may occur either at the top level (i.e., under the RIFF chunk) or under a data + * list chunk. There can be only one top-level Data Chunk. * + * @author Gary McGath */ public class DataChunk extends Chunk { - /** - * Constructor. - * - * @param module The WaveModule under which this was called - * @param hdr The header for this chunk - * @param dstrm The stream from which the WAVE data are being read - */ - public DataChunk( - ModuleBase module, - ChunkHeader hdr, - DataInputStream dstrm) { - super(module, hdr, dstrm); - } - - /* We may want to have another constructor which sets a parent chunk. */ + /** + * Constructor. + * + * @param module The WaveModule under which this was called + * @param hdr The header for this chunk + * @param dstrm The stream from which the WAVE data are being read + */ + public DataChunk(ModuleBase module, ChunkHeader hdr, DataInputStream dstrm) { + super(module, hdr, dstrm); + } - /** Reads a chunk and puts a Data property into - * the RepInfo object. - * - * @return false if the chunk is structurally - * invalid, otherwise true - */ - @Override - public boolean readChunk(RepInfo info) throws IOException { - WaveModule module = (WaveModule) _module; - Property lenProp = new Property ("DataLength", - PropertyType.LONG, - new Long (bytesLeft)); - // The behavior will be different if we are reading this under - // a 'wavl' chunk. - - // If we have PCM compression, the number of samples is given - // by the number of bytes divided by the sample blocking; otherwise - // we use the Fact chunk to count samples. - if (module.getWaveCodec() == FormatChunk.WAVE_FORMAT_PCM) { - module.addSamples (bytesLeft / module.getBlockAlign()); - } - module.addWaveProperty(new Property ("Data", - PropertyType.PROPERTY, - lenProp)); - // This must be called precisely at this point in reading the - // data stream to produce an accurate result. - module.markFirstSampleOffset (); - module.skipBytes(_dstream, bytesLeft, module); - return true; - } + /* We may want to have another constructor which sets a parent chunk. */ + + /** + * Reads a chunk and puts a Data property into the RepInfo object. + * + * @return false if the chunk is structurally invalid, otherwise true + */ + @Override + public boolean readChunk(RepInfo info) throws IOException { + WaveModule module = (WaveModule) _module; + Property lenProp = new Property("DataLength", PropertyType.LONG, new Long(bytesLeft)); + // The behavior will be different if we are reading this under + // a 'wavl' chunk. + // If we have PCM compression, the number of samples is given + // by the number of bytes divided by the sample blocking; otherwise + // we use the Fact chunk to count samples. + if (module.getWaveCodec() == FormatChunk.WAVE_FORMAT_PCM) { + module.addSamples(bytesLeft / module.getBlockAlign()); + } + module.addWaveProperty(new Property("Data", PropertyType.PROPERTY, lenProp)); + // This must be called precisely at this point in reading the + // data stream to produce an accurate result. + module.markFirstSampleOffset(); + module.skipBytes(_dstream, bytesLeft, module); + return true; + } } diff --git a/jhove-modules/wave-hul/src/main/java/edu/harvard/hul/ois/jhove/module/wave/DataSize64Chunk.java b/jhove-modules/wave-hul/src/main/java/edu/harvard/hul/ois/jhove/module/wave/DataSize64Chunk.java index 73b62c4cd..603fdbef6 100644 --- a/jhove-modules/wave-hul/src/main/java/edu/harvard/hul/ois/jhove/module/wave/DataSize64Chunk.java +++ b/jhove-modules/wave-hul/src/main/java/edu/harvard/hul/ois/jhove/module/wave/DataSize64Chunk.java @@ -1,70 +1,65 @@ -/********************************************************************** - * JHOVE - JSTOR/Harvard Object Validation Environment - **********************************************************************/ - +/** + * ******************************************************************** JHOVE - JSTOR/Harvard Object + * Validation Environment ******************************************************************** + */ package edu.harvard.hul.ois.jhove.module.wave; import edu.harvard.hul.ois.jhove.*; import edu.harvard.hul.ois.jhove.module.WaveModule; import edu.harvard.hul.ois.jhove.module.iff.Chunk; import edu.harvard.hul.ois.jhove.module.iff.ChunkHeader; - import java.io.DataInputStream; import java.io.IOException; /** * Implementation of the WAVE Data Size 64 chunk. * - * Stores 64-bit data sizes for fields previously limited to 32-bit lengths. + *

Stores 64-bit data sizes for fields previously limited to 32-bit lengths. */ public class DataSize64Chunk extends Chunk { - /** The combined length of all mandatory fields. */ - private final static int MINIMUM_CHUNK_LENGTH = 24; + /** The combined length of all mandatory fields. */ + private static final int MINIMUM_CHUNK_LENGTH = 24; - /** - * Constructor. - * - * @param module The WaveModule under which this was called - * @param hdr The header for this chunk - * @param dstrm The stream from which the WAVE data are being read - */ - public DataSize64Chunk( - ModuleBase module, - ChunkHeader hdr, - DataInputStream dstrm) { - super(module, hdr, dstrm); - } - - /** - * Reads and extracts the file's 64-bit data sizes. - * - * @return false if the chunk is structurally - * invalid, otherwise true - */ - @Override - public boolean readChunk(RepInfo info) throws IOException { + /** + * Constructor. + * + * @param module The WaveModule under which this was called + * @param hdr The header for this chunk + * @param dstrm The stream from which the WAVE data are being read + */ + public DataSize64Chunk(ModuleBase module, ChunkHeader hdr, DataInputStream dstrm) { + super(module, hdr, dstrm); + } - WaveModule module = (WaveModule) _module; + /** + * Reads and extracts the file's 64-bit data sizes. + * + * @return false if the chunk is structurally invalid, otherwise true + */ + @Override + public boolean readChunk(RepInfo info) throws IOException { - long riffSize = module.readSignedLong(_dstream); - module.setExtendedRiffSize(riffSize); + WaveModule module = (WaveModule) _module; - long dataSize = module.readSignedLong(_dstream); - module.addExtendedChunkSize("data", dataSize); + long riffSize = module.readSignedLong(_dstream); + module.setExtendedRiffSize(riffSize); - long sampleCount = module.readSignedLong(_dstream); - module.setExtendedSampleLength(sampleCount); + long dataSize = module.readSignedLong(_dstream); + module.addExtendedChunkSize("data", dataSize); - if (chunkSize > MINIMUM_CHUNK_LENGTH) { - long tableSize = module.readUnsignedInt(_dstream); - for (int i = 0; i < tableSize; i++) { - String chunkId = module.read4Chars(_dstream); - long chunkSize = module.readSignedLong(_dstream); - module.addExtendedChunkSize(chunkId, chunkSize); - } - } + long sampleCount = module.readSignedLong(_dstream); + module.setExtendedSampleLength(sampleCount); - return true; + if (chunkSize > MINIMUM_CHUNK_LENGTH) { + long tableSize = module.readUnsignedInt(_dstream); + for (int i = 0; i < tableSize; i++) { + String chunkId = module.read4Chars(_dstream); + long chunkSize = module.readSignedLong(_dstream); + module.addExtendedChunkSize(chunkId, chunkSize); + } } + + return true; + } } diff --git a/jhove-modules/wave-hul/src/main/java/edu/harvard/hul/ois/jhove/module/wave/ExifInfo.java b/jhove-modules/wave-hul/src/main/java/edu/harvard/hul/ois/jhove/module/wave/ExifInfo.java index 113e44457..88961c361 100644 --- a/jhove-modules/wave-hul/src/main/java/edu/harvard/hul/ois/jhove/module/wave/ExifInfo.java +++ b/jhove-modules/wave-hul/src/main/java/edu/harvard/hul/ois/jhove/module/wave/ExifInfo.java @@ -1,123 +1,95 @@ -/********************************************************************** - * Jhove - JSTOR/Harvard Object Validation Environment - * Copyright 2004 by JSTOR and the President and Fellows of Harvard College - **********************************************************************/ - +/** + * ******************************************************************** Jhove - JSTOR/Harvard Object + * Validation Environment Copyright 2004 by JSTOR and the President and Fellows of Harvard College + * ******************************************************************** + */ package edu.harvard.hul.ois.jhove.module.wave; import edu.harvard.hul.ois.jhove.*; import java.util.*; /** - * Encapsulation of Exif information for a Wave file. Exif sound - * information doesn't closely follow Exif image information, so we - * don't particularly try to force property names to match. - * - * @see edu.harvard.hul.ois.jhove.module.tiff.ExifIFD + * Encapsulation of Exif information for a Wave file. Exif sound information doesn't closely follow + * Exif image information, so we don't particularly try to force property names to match. * + * @see edu.harvard.hul.ois.jhove.module.tiff.ExifIFD * @author Gary McGath - * */ public class ExifInfo { - private String _exifVersion; - private String _relatedImageFile; - private String _timeCreated; - private String _manufacturer; - private String _model; - private byte[] _makerNote; - private String _userComment; - - public ExifInfo () - { - - } - - /** Constructs a property and returns it. */ - public Property buildProperty () - { - List entries = new LinkedList (); + private String _exifVersion; + private String _relatedImageFile; + private String _timeCreated; + private String _manufacturer; + private String _model; + private byte[] _makerNote; + private String _userComment; - if (_exifVersion != null) { - entries.add (new Property ("ExifVersion", PropertyType.STRING, - _exifVersion)); - } - else { - return null; // Version must be specified - } - if (_relatedImageFile != null) { - entries.add (new Property ("RelatedImageFile", PropertyType.STRING, - _relatedImageFile)); - } - if (_timeCreated != null) { - entries.add (new Property ("TimeCreated", PropertyType.STRING, - _timeCreated)); - } - if (_manufacturer != null) { - entries.add (new Property ("Manufacturer", PropertyType.STRING, - _manufacturer)); - } - if (_model != null) { - entries.add (new Property ("Model", PropertyType.STRING, - _model)); - } - if (_makerNote != null) { - entries.add (new Property ("MakerNote", PropertyType.BYTE, - PropertyArity.ARRAY, - _makerNote)); - } - if (_userComment != null) { - entries.add (new Property ("UserComment", PropertyType.STRING, - _userComment)); - } - - - return new Property ("Exif", PropertyType.PROPERTY, - PropertyArity.LIST, - entries); - } - - - /** Converts the raw 4-byte array into a version string and - * stores it. */ - protected void setExifVersion(String version) { - _exifVersion = version; + public ExifInfo() {} + + /** Constructs a property and returns it. */ + public Property buildProperty() { + List entries = new LinkedList(); + + if (_exifVersion != null) { + entries.add(new Property("ExifVersion", PropertyType.STRING, _exifVersion)); + } else { + return null; // Version must be specified } - - /** Sets the related image file name. */ - protected void setRelatedImageFile (String file) - { - _relatedImageFile = file; + if (_relatedImageFile != null) { + entries.add(new Property("RelatedImageFile", PropertyType.STRING, _relatedImageFile)); } - - /** Sets the creation time as an ASCII string. */ - protected void setTimeCreated (String time) - { - _timeCreated = time; + if (_timeCreated != null) { + entries.add(new Property("TimeCreated", PropertyType.STRING, _timeCreated)); } - - - /** Sets the manufacturer of the equipment that produced the file. */ - protected void setManufacturer (String file) - { - _manufacturer = file; + if (_manufacturer != null) { + entries.add(new Property("Manufacturer", PropertyType.STRING, _manufacturer)); } - - /** Sets the model of the equipment that produced the file. */ - protected void setModel (String file) - { - _model = file; + if (_model != null) { + entries.add(new Property("Model", PropertyType.STRING, _model)); } - - /** Sets the maker note. */ - protected void setMakerNote (byte[] note) - { - _makerNote = note; + if (_makerNote != null) { + entries.add(new Property("MakerNote", PropertyType.BYTE, PropertyArity.ARRAY, _makerNote)); } - - /** Sets the user comment. */ - protected void setUserComment (String comment) - { - _userComment = comment; + if (_userComment != null) { + entries.add(new Property("UserComment", PropertyType.STRING, _userComment)); } + + return new Property("Exif", PropertyType.PROPERTY, PropertyArity.LIST, entries); + } + + /** Converts the raw 4-byte array into a version string and stores it. */ + protected void setExifVersion(String version) { + _exifVersion = version; + } + + /** Sets the related image file name. */ + protected void setRelatedImageFile(String file) { + _relatedImageFile = file; + } + + /** Sets the creation time as an ASCII string. */ + protected void setTimeCreated(String time) { + _timeCreated = time; + } + + /** Sets the manufacturer of the equipment that produced the file. */ + protected void setManufacturer(String file) { + _manufacturer = file; + } + + /** Sets the model of the equipment that produced the file. */ + protected void setModel(String file) { + _model = file; + } + + /** Sets the maker note. */ + protected void setMakerNote(byte[] note) { + _makerNote = note; + } + + /** Sets the user comment. */ + protected void setUserComment(String comment) { + _userComment = comment; + } } diff --git a/jhove-modules/wave-hul/src/main/java/edu/harvard/hul/ois/jhove/module/wave/ExifMakerNoteChunk.java b/jhove-modules/wave-hul/src/main/java/edu/harvard/hul/ois/jhove/module/wave/ExifMakerNoteChunk.java index 4bd765717..ca4c479c9 100644 --- a/jhove-modules/wave-hul/src/main/java/edu/harvard/hul/ois/jhove/module/wave/ExifMakerNoteChunk.java +++ b/jhove-modules/wave-hul/src/main/java/edu/harvard/hul/ois/jhove/module/wave/ExifMakerNoteChunk.java @@ -1,8 +1,8 @@ -/********************************************************************** - * Jhove - JSTOR/Harvard Object Validation Environment - * Copyright 2004 by JSTOR and the President and Fellows of Harvard College - **********************************************************************/ - +/** + * ******************************************************************** Jhove - JSTOR/Harvard Object + * Validation Environment Copyright 2004 by JSTOR and the President and Fellows of Harvard College + * ******************************************************************** + */ package edu.harvard.hul.ois.jhove.module.wave; import edu.harvard.hul.ois.jhove.*; @@ -12,42 +12,34 @@ import java.io.*; /** - * This class encapsulates the Exif Maker Note chunk. The format - * of this is manufacturer-depedent, hence is regarded simply as an - * array of integers. + * This class encapsulates the Exif Maker Note chunk. The format of this is manufacturer-depedent, + * hence is regarded simply as an array of integers. * * @author Gary McGath - * */ public class ExifMakerNoteChunk extends Chunk { - /** - * Constructor. - * - * @param module The WaveModule under which this was called - * @param hdr The header for this chunk - * @param dstrm The stream from which the WAVE data are being read - */ - public ExifMakerNoteChunk( - ModuleBase module, - ChunkHeader hdr, - DataInputStream dstrm) { - super(module, hdr, dstrm); - } - - - /** Reads a chunk and puts information into the superchunk's - * Exif property. - * - * @return false if the chunk is structurally - * invalid, otherwise true - */ - @Override - public boolean readChunk(RepInfo info) throws IOException { - WaveModule module = (WaveModule) _module; - byte[] buf = new byte[(int) bytesLeft]; - ModuleBase.readByteBuf (_dstream, buf, module); - module.getExifInfo ().setMakerNote (buf); - return true; - } + /** + * Constructor. + * + * @param module The WaveModule under which this was called + * @param hdr The header for this chunk + * @param dstrm The stream from which the WAVE data are being read + */ + public ExifMakerNoteChunk(ModuleBase module, ChunkHeader hdr, DataInputStream dstrm) { + super(module, hdr, dstrm); + } + /** + * Reads a chunk and puts information into the superchunk's Exif property. + * + * @return false if the chunk is structurally invalid, otherwise true + */ + @Override + public boolean readChunk(RepInfo info) throws IOException { + WaveModule module = (WaveModule) _module; + byte[] buf = new byte[(int) bytesLeft]; + ModuleBase.readByteBuf(_dstream, buf, module); + module.getExifInfo().setMakerNote(buf); + return true; + } } diff --git a/jhove-modules/wave-hul/src/main/java/edu/harvard/hul/ois/jhove/module/wave/ExifStringChunk.java b/jhove-modules/wave-hul/src/main/java/edu/harvard/hul/ois/jhove/module/wave/ExifStringChunk.java index 4dc90b3ae..d9887467f 100644 --- a/jhove-modules/wave-hul/src/main/java/edu/harvard/hul/ois/jhove/module/wave/ExifStringChunk.java +++ b/jhove-modules/wave-hul/src/main/java/edu/harvard/hul/ois/jhove/module/wave/ExifStringChunk.java @@ -1,8 +1,8 @@ -/********************************************************************** - * Jhove - JSTOR/Harvard Object Validation Environment - * Copyright 2004 by JSTOR and the President and Fellows of Harvard College - **********************************************************************/ - +/** + * ******************************************************************** Jhove - JSTOR/Harvard Object + * Validation Environment Copyright 2004 by JSTOR and the President and Fellows of Harvard College + * ******************************************************************** + */ package edu.harvard.hul.ois.jhove.module.wave; import edu.harvard.hul.ois.jhove.*; @@ -12,59 +12,48 @@ import java.io.*; /** - * Class for encapsulating Exif chunks whose content consists of - * a null-terminated ASCII string. + * Class for encapsulating Exif chunks whose content consists of a null-terminated ASCII string. * * @author Gary McGath - * */ public class ExifStringChunk extends Chunk { - private String id; - - - /** - * Constructor. - * - * @param module The WaveModule under which this was called - * @param hdr The header for this chunk - * @param dstrm The stream from which the WAVE data are being read - */ - public ExifStringChunk( - ModuleBase module, - ChunkHeader hdr, - DataInputStream dstrm) { - super(module, hdr, dstrm); - id = hdr.getID(); - } - - /** Reads a chunk and puts information into the superchunk's - * Exif property. - * - * @return false if the chunk is structurally - * invalid, otherwise true - */ - @Override - public boolean readChunk(RepInfo info) throws IOException { - WaveModule module = (WaveModule) _module; - byte[] buf = new byte[(int) bytesLeft]; - ModuleBase.readByteBuf (_dstream, buf, module); - String txt = new String (buf).trim (); - ExifInfo exif = module.getExifInfo (); - if ("erel".equals (id)) { - exif.setRelatedImageFile(txt); - } - else if ("etim".equals (id)) { - exif.setTimeCreated (txt); - } - else if ("ecor".equals (id)) { - exif.setManufacturer(txt); - } - else if ("emdl".equals (id)) { - exif.setModel (txt); - } - module.getExifInfo ().setRelatedImageFile(txt); - return true; + private String id; + + /** + * Constructor. + * + * @param module The WaveModule under which this was called + * @param hdr The header for this chunk + * @param dstrm The stream from which the WAVE data are being read + */ + public ExifStringChunk(ModuleBase module, ChunkHeader hdr, DataInputStream dstrm) { + super(module, hdr, dstrm); + id = hdr.getID(); + } + + /** + * Reads a chunk and puts information into the superchunk's Exif property. + * + * @return false if the chunk is structurally invalid, otherwise true + */ + @Override + public boolean readChunk(RepInfo info) throws IOException { + WaveModule module = (WaveModule) _module; + byte[] buf = new byte[(int) bytesLeft]; + ModuleBase.readByteBuf(_dstream, buf, module); + String txt = new String(buf).trim(); + ExifInfo exif = module.getExifInfo(); + if ("erel".equals(id)) { + exif.setRelatedImageFile(txt); + } else if ("etim".equals(id)) { + exif.setTimeCreated(txt); + } else if ("ecor".equals(id)) { + exif.setManufacturer(txt); + } else if ("emdl".equals(id)) { + exif.setModel(txt); } - + module.getExifInfo().setRelatedImageFile(txt); + return true; + } } diff --git a/jhove-modules/wave-hul/src/main/java/edu/harvard/hul/ois/jhove/module/wave/ExifUserCommentChunk.java b/jhove-modules/wave-hul/src/main/java/edu/harvard/hul/ois/jhove/module/wave/ExifUserCommentChunk.java index f725c0196..f27c272bf 100644 --- a/jhove-modules/wave-hul/src/main/java/edu/harvard/hul/ois/jhove/module/wave/ExifUserCommentChunk.java +++ b/jhove-modules/wave-hul/src/main/java/edu/harvard/hul/ois/jhove/module/wave/ExifUserCommentChunk.java @@ -1,8 +1,8 @@ -/********************************************************************** - * Jhove - JSTOR/Harvard Object Validation Environment - * Copyright 2004 by JSTOR and the President and Fellows of Harvard College - **********************************************************************/ - +/** + * ******************************************************************** Jhove - JSTOR/Harvard Object + * Validation Environment Copyright 2004 by JSTOR and the President and Fellows of Harvard College + * ******************************************************************** + */ package edu.harvard.hul.ois.jhove.module.wave; import edu.harvard.hul.ois.jhove.*; @@ -15,86 +15,74 @@ * This class encapsulates the Exif User Comment chunk. * * @author Gary McGath - * */ public class ExifUserCommentChunk extends Chunk { - - // 8-byte codes for encodings; all the existing recognized - // encodings are representable as ASCII strings with null - // padding. "UNICODE" doesn't specify whether it's UTF-8, - // UTF-16, or UTF-32. - private final static String asciiDes = "ASCII"; - private final static String jisDes = "JIS"; - private final static String unicodeDes = "UNICODE"; + // 8-byte codes for encodings; all the existing recognized + // encodings are representable as ASCII strings with null + // padding. "UNICODE" doesn't specify whether it's UTF-8, + // UTF-16, or UTF-32. + private static final String asciiDes = "ASCII"; + private static final String jisDes = "JIS"; + private static final String unicodeDes = "UNICODE"; - /** - * Constructor. - * - * @param module The WaveModule under which this was called - * @param hdr The header for this chunk - * @param dstrm The stream from which the WAVE data are being read - */ - public ExifUserCommentChunk( - ModuleBase module, - ChunkHeader hdr, - DataInputStream dstrm) { - super(module, hdr, dstrm); + /** + * Constructor. + * + * @param module The WaveModule under which this was called + * @param hdr The header for this chunk + * @param dstrm The stream from which the WAVE data are being read + */ + public ExifUserCommentChunk(ModuleBase module, ChunkHeader hdr, DataInputStream dstrm) { + super(module, hdr, dstrm); + } + + /** + * Reads a chunk and puts information into the superchunk's Exif property. + * + * @return false if the chunk is structurally invalid, otherwise true + */ + @Override + public boolean readChunk(RepInfo info) throws IOException { + WaveModule module = (WaveModule) _module; + if (bytesLeft < 8) { + info.setMessage(new ErrorMessage(MessageConstants.WAVE_HUL_11)); + info.setWellFormed(false); + return false; } + // Read the 8-byte encoding designation. + byte[] buf = new byte[8]; + ModuleBase.readByteBuf(_dstream, buf, module); + String encoding = new String(buf).trim(); + bytesLeft -= 8; + String charset = null; - /** Reads a chunk and puts information into the superchunk's - * Exif property. - * - * @return false if the chunk is structurally - * invalid, otherwise true - */ - @Override - public boolean readChunk(RepInfo info) throws IOException - { - WaveModule module = (WaveModule) _module; - if (bytesLeft < 8) { - info.setMessage (new ErrorMessage - (MessageConstants.WAVE_HUL_11)); - info.setWellFormed (false); - return false; - } - // Read the 8-byte encoding designation. - byte[] buf = new byte[8]; - ModuleBase.readByteBuf (_dstream, buf, module); - String encoding = new String (buf).trim (); - bytesLeft -= 8; - - String charset = null; - - // Here we have to do some guessing if the character set isn't - // ASCII. There are three different Unicode encodings and - // even more JIS variants. - if (asciiDes.equals (encoding)) { - charset = "US-ASCII"; - } - else if (jisDes.equals (encoding)) { - charset = "EUC_JP"; - } - else if (unicodeDes.equals (encoding)) { - charset = "UTF-16"; - } - // Read the comment itself. - buf = new byte[(int) bytesLeft]; - ModuleBase.readByteBuf (_dstream, buf, module); - String text = null; - try { - if (charset != null) { - text = new String (buf, charset); - } - } - catch (Exception e) { - // If we can't decode the charset, punt to default. - } - if (text == null) { - text = new String (buf); - } - module.getExifInfo ().setUserComment (text); - return true; + // Here we have to do some guessing if the character set isn't + // ASCII. There are three different Unicode encodings and + // even more JIS variants. + if (asciiDes.equals(encoding)) { + charset = "US-ASCII"; + } else if (jisDes.equals(encoding)) { + charset = "EUC_JP"; + } else if (unicodeDes.equals(encoding)) { + charset = "UTF-16"; + } + // Read the comment itself. + buf = new byte[(int) bytesLeft]; + ModuleBase.readByteBuf(_dstream, buf, module); + String text = null; + try { + if (charset != null) { + text = new String(buf, charset); + } + } catch (Exception e) { + // If we can't decode the charset, punt to default. + } + if (text == null) { + text = new String(buf); } + module.getExifInfo().setUserComment(text); + return true; + } } diff --git a/jhove-modules/wave-hul/src/main/java/edu/harvard/hul/ois/jhove/module/wave/ExifVersionChunk.java b/jhove-modules/wave-hul/src/main/java/edu/harvard/hul/ois/jhove/module/wave/ExifVersionChunk.java index ec6fb0796..d9d24afb3 100644 --- a/jhove-modules/wave-hul/src/main/java/edu/harvard/hul/ois/jhove/module/wave/ExifVersionChunk.java +++ b/jhove-modules/wave-hul/src/main/java/edu/harvard/hul/ois/jhove/module/wave/ExifVersionChunk.java @@ -1,8 +1,8 @@ -/********************************************************************** - * Jhove - JSTOR/Harvard Object Validation Environment - * Copyright 2004 by JSTOR and the President and Fellows of Harvard College - **********************************************************************/ - +/** + * ******************************************************************** Jhove - JSTOR/Harvard Object + * Validation Environment Copyright 2004 by JSTOR and the President and Fellows of Harvard College + * ******************************************************************** + */ package edu.harvard.hul.ois.jhove.module.wave; import edu.harvard.hul.ois.jhove.*; @@ -12,49 +12,40 @@ import java.io.*; /** - * Chunk for Exif version information. - * This chunk may occur only within a LIST chunk of type - * "exif". + * Chunk for Exif version information. This chunk may occur only within a LIST chunk of type "exif". * * @author Gary McGath - * */ public class ExifVersionChunk extends Chunk { - /** - * Constructor. - * - * @param module The WaveModule under which this was called - * @param hdr The header for this chunk - * @param dstrm The stream from which the WAVE data are being read - */ - public ExifVersionChunk( - ModuleBase module, - ChunkHeader hdr, - DataInputStream dstrm) { - super(module, hdr, dstrm); - } + /** + * Constructor. + * + * @param module The WaveModule under which this was called + * @param hdr The header for this chunk + * @param dstrm The stream from which the WAVE data are being read + */ + public ExifVersionChunk(ModuleBase module, ChunkHeader hdr, DataInputStream dstrm) { + super(module, hdr, dstrm); + } - /** Reads a chunk and puts information into the superchunk's - * Exif property. - * - * @return false if the chunk is structurally - * invalid, otherwise true - */ - @Override - public boolean readChunk(RepInfo info) throws IOException { - WaveModule module = (WaveModule) _module; - if (bytesLeft != 4) { - info.setMessage (new ErrorMessage - (MessageConstants.WAVE_HUL_12)); - info.setWellFormed (false); - return false; - } - byte[] buf = new byte[4]; - ModuleBase.readByteBuf (_dstream, buf, module); - String txt = new String (buf); - module.getExifInfo ().setExifVersion(txt); - return true; + /** + * Reads a chunk and puts information into the superchunk's Exif property. + * + * @return false if the chunk is structurally invalid, otherwise true + */ + @Override + public boolean readChunk(RepInfo info) throws IOException { + WaveModule module = (WaveModule) _module; + if (bytesLeft != 4) { + info.setMessage(new ErrorMessage(MessageConstants.WAVE_HUL_12)); + info.setWellFormed(false); + return false; } - + byte[] buf = new byte[4]; + ModuleBase.readByteBuf(_dstream, buf, module); + String txt = new String(buf); + module.getExifInfo().setExifVersion(txt); + return true; + } } diff --git a/jhove-modules/wave-hul/src/main/java/edu/harvard/hul/ois/jhove/module/wave/FactChunk.java b/jhove-modules/wave-hul/src/main/java/edu/harvard/hul/ois/jhove/module/wave/FactChunk.java index 89b777b40..de51847d4 100644 --- a/jhove-modules/wave-hul/src/main/java/edu/harvard/hul/ois/jhove/module/wave/FactChunk.java +++ b/jhove-modules/wave-hul/src/main/java/edu/harvard/hul/ois/jhove/module/wave/FactChunk.java @@ -1,8 +1,8 @@ -/********************************************************************** - * JHOVE - JSTOR/Harvard Object Validation Environment - * Copyright 2004 by JSTOR and the President and Fellows of Harvard College - **********************************************************************/ - +/** + * ******************************************************************** JHOVE - JSTOR/Harvard Object + * Validation Environment Copyright 2004 by JSTOR and the President and Fellows of Harvard College + * ******************************************************************** + */ package edu.harvard.hul.ois.jhove.module.wave; import edu.harvard.hul.ois.jhove.ModuleBase; @@ -12,62 +12,54 @@ import edu.harvard.hul.ois.jhove.module.WaveModule; import edu.harvard.hul.ois.jhove.module.iff.Chunk; import edu.harvard.hul.ois.jhove.module.iff.ChunkHeader; - import java.io.DataInputStream; import java.io.IOException; /** * Implementation of the WAVE Fact Chunk. * - * The Fact chunk contains information specific to the compression scheme. + *

The Fact chunk contains information specific to the compression scheme. * * @author Gary McGath */ public class FactChunk extends Chunk { - private final static int BASE_CHUNK_SIZE = 4; + private static final int BASE_CHUNK_SIZE = 4; - /** - * Constructor. - * - * @param module The WaveModule under which this was called - * @param hdr The header for this chunk - * @param dstrm The stream from which the WAVE data are being read - */ - public FactChunk( - ModuleBase module, - ChunkHeader hdr, - DataInputStream dstrm) { - super(module, hdr, dstrm); - } + /** + * Constructor. + * + * @param module The WaveModule under which this was called + * @param hdr The header for this chunk + * @param dstrm The stream from which the WAVE data are being read + */ + public FactChunk(ModuleBase module, ChunkHeader hdr, DataInputStream dstrm) { + super(module, hdr, dstrm); + } - /** - * Reads a chunk and puts a Fact Property into the RepInfo object. - * - * @return false if the chunk is structurally invalid, - * otherwise true - */ - @Override - public boolean readChunk(RepInfo info) throws IOException { + /** + * Reads a chunk and puts a Fact Property into the RepInfo object. + * + * @return false if the chunk is structurally invalid, otherwise true + */ + @Override + public boolean readChunk(RepInfo info) throws IOException { - WaveModule module = (WaveModule) _module; + WaveModule module = (WaveModule) _module; - long sampleLength = module.readUnsignedInt(_dstream); - if (module.hasExtendedDataSizes() - && sampleLength == WaveModule.LOOKUP_EXTENDED_DATA_SIZE) { - sampleLength = module.getExtendedSampleLength(); - } + long sampleLength = module.readUnsignedInt(_dstream); + if (module.hasExtendedDataSizes() && sampleLength == WaveModule.LOOKUP_EXTENDED_DATA_SIZE) { + sampleLength = module.getExtendedSampleLength(); + } - module.addSamples(sampleLength); - bytesLeft -= BASE_CHUNK_SIZE; + module.addSamples(sampleLength); + bytesLeft -= BASE_CHUNK_SIZE; - module.skipBytes(_dstream, bytesLeft, module); + module.skipBytes(_dstream, bytesLeft, module); - Property sizeProp = new Property("Size", - PropertyType.LONG, Long.valueOf(chunkSize)); - module.addWaveProperty(new Property("Fact", - PropertyType.PROPERTY, sizeProp)); + Property sizeProp = new Property("Size", PropertyType.LONG, Long.valueOf(chunkSize)); + module.addWaveProperty(new Property("Fact", PropertyType.PROPERTY, sizeProp)); - return true; - } + return true; + } } diff --git a/jhove-modules/wave-hul/src/main/java/edu/harvard/hul/ois/jhove/module/wave/FormatChunk.java b/jhove-modules/wave-hul/src/main/java/edu/harvard/hul/ois/jhove/module/wave/FormatChunk.java index 0b92922dd..3c4b5fd4c 100644 --- a/jhove-modules/wave-hul/src/main/java/edu/harvard/hul/ois/jhove/module/wave/FormatChunk.java +++ b/jhove-modules/wave-hul/src/main/java/edu/harvard/hul/ois/jhove/module/wave/FormatChunk.java @@ -1,19 +1,18 @@ -/********************************************************************** - * JHOVE - JSTOR/Harvard Object Validation Environment - * Copyright 2004 by JSTOR and the President and Fellows of Harvard College - **********************************************************************/ - +/** + * ******************************************************************** JHOVE - JSTOR/Harvard Object + * Validation Environment Copyright 2004 by JSTOR and the President and Fellows of Harvard College + * ******************************************************************** + */ package edu.harvard.hul.ois.jhove.module.wave; import edu.harvard.hul.ois.jhove.*; import edu.harvard.hul.ois.jhove.module.WaveModule; import edu.harvard.hul.ois.jhove.module.iff.Chunk; import edu.harvard.hul.ois.jhove.module.iff.ChunkHeader; - -import javax.xml.bind.DatatypeConverter; import java.io.DataInputStream; import java.io.IOException; import java.util.Arrays; +import javax.xml.bind.DatatypeConverter; /** * Implementation of the WAVE Format Chunk. @@ -22,271 +21,256 @@ */ public class FormatChunk extends Chunk { - /** Compression code for original Microsoft PCM */ - public final static int WAVE_FORMAT_PCM = 0x0001; - - /** Compression code for MPEG */ - public final static int WAVE_FORMAT_MPEG = 0x0050; - - /** Compression code for Microsoft Extensible Wave Format */ - public final static int WAVE_FORMAT_EXTENSIBLE = 0xFFFE; - - /** Chunk size for PCMWAVEFORMAT files */ - private final static int PCMWAVEFORMAT_LENGTH = 16; - - /** Minimum extra chunk bytes for WAVEFORMATEXTENSIBLE files */ - private final static int EXTRA_WAVEFORMATEXTENSIBLE_LENGTH = 22; - - /** Suffix given to GUIDs migrated from Format Tags */ - private final static String FORMAT_TAG_GUID_SUFFIX = - "-0000-0010-8000-00AA00389B71"; - - /** Table of losslessly compressed codecs */ - private final static int[] losslessCodecs = { - 0x0163, // WMA lossless - 0x1971, // Sonic foundry lossless - 0xF1AC // FLAC - }; - - /** - * Constructor. - * - * @param module The WaveModule under which this was called - * @param hdr The header for this chunk - * @param dstrm The stream from which the WAVE data are being read - */ - public FormatChunk( - WaveModule module, - ChunkHeader hdr, - DataInputStream dstrm) { - super(module, hdr, dstrm); - } - - /** - * Reads a chunk and puts appropriate Properties into the RepInfo object. - * - * @return false if the chunk is structurally invalid, - * otherwise true - */ - @Override - public boolean readChunk(RepInfo info) throws IOException { - - WaveModule module = (WaveModule) _module; - - int validBitsPerSample = -1; - byte[] extraBytes = null; - String subformat = null; - long channelMask = -1; - - int waveCodec = module.readUnsignedShort(_dstream); - module.setWaveCodec(waveCodec); - int numChannels = module.readUnsignedShort(_dstream); - long sampleRate = module.readUnsignedInt(_dstream); - module.setSampleRate(sampleRate); - long bytesPerSecond = module.readUnsignedInt(_dstream); - int blockAlign = module.readUnsignedShort(_dstream); - module.setBlockAlign(blockAlign); - int bitsPerSample = module.readUnsignedShort(_dstream); - - bytesLeft -= PCMWAVEFORMAT_LENGTH; - - if (bytesLeft > 0) { - int extraFormatBytes = module.readUnsignedShort(_dstream); - extraBytes = new byte[extraFormatBytes]; - if (waveCodec == WAVE_FORMAT_EXTENSIBLE - && extraFormatBytes >= EXTRA_WAVEFORMATEXTENSIBLE_LENGTH) { - // This is -- or should be -- WAVEFORMATEXTENSIBLE. - // Need to do some additional checks on profile satisfaction. - boolean wfe = true; // Accept tentatively - // The next word may be valid bits per sample, samples - // per block, or merely "reserved". Which one it is - // apparently depends on the compression format. I really - // can't figure out how to tell which it is without - // exhaustively researching all compression formats. - validBitsPerSample = module.readUnsignedShort(_dstream); - channelMask = module.readUnsignedInt(_dstream); - - // The Subformat field is a Microsoft GUID - byte[] guidBytes = new byte[16]; - ModuleBase.readByteBuf(_dstream, guidBytes, module); - subformat = formatGUID(guidBytes); - - // If the Subformat GUID was generated from a Format Tag value, - // extract the old Format Tag value from its GUID so we can - // properly identify the codec and treat it the same way. - if (subformat.endsWith(FORMAT_TAG_GUID_SUFFIX)) { - waveCodec = Integer.parseInt(subformat.substring(4, 8), 16); - module.setWaveCodec(waveCodec); - } - - // Nitpicking profile requirements - if ((((bitsPerSample + 7) / 8) * numChannels) != blockAlign) { - wfe = false; - } - if ((bitsPerSample % 8) != 0) { - // So why was that fancy ceiling arithmetic needed? - // So it can be the same calculation as with WaveFormatEx. - wfe = false; - } - if (validBitsPerSample > bitsPerSample) { - wfe = false; - } - if (wfe) { - module.setWaveFormatExtensible(true); - } - } - else { - if (waveCodec != WAVE_FORMAT_PCM || - (((bitsPerSample + 7) / 8) * numChannels) == blockAlign) { - module.setWaveFormatEx(true); - } - ModuleBase.readByteBuf(_dstream, extraBytes, module); - } - - // Possible pad to maintain even alignment - if ((extraFormatBytes & 1) != 0) { - _module.skipBytes(_dstream, 1, module); - } - } - else { - // No extra bytes signifies the PCM profile. In this case, - // the wave codec also needs to be Microsoft PCM. - if (waveCodec == WAVE_FORMAT_PCM && - (((bitsPerSample + 7) / 8) * numChannels) == blockAlign) { - module.setPCMWaveFormat(true); - } - } - - // Add extended MIME type information when available - if (waveCodec != WAVE_FORMAT_EXTENSIBLE) { - String codecID = Integer.toHexString(waveCodec).toUpperCase(); - String extendedMIMEType = _module.getMimeType()[0] - + "; codec=" + codecID; - info.setMimeType(extendedMIMEType); + /** Compression code for original Microsoft PCM */ + public static final int WAVE_FORMAT_PCM = 0x0001; + + /** Compression code for MPEG */ + public static final int WAVE_FORMAT_MPEG = 0x0050; + + /** Compression code for Microsoft Extensible Wave Format */ + public static final int WAVE_FORMAT_EXTENSIBLE = 0xFFFE; + + /** Chunk size for PCMWAVEFORMAT files */ + private static final int PCMWAVEFORMAT_LENGTH = 16; + + /** Minimum extra chunk bytes for WAVEFORMATEXTENSIBLE files */ + private static final int EXTRA_WAVEFORMATEXTENSIBLE_LENGTH = 22; + + /** Suffix given to GUIDs migrated from Format Tags */ + private static final String FORMAT_TAG_GUID_SUFFIX = "-0000-0010-8000-00AA00389B71"; + + /** Table of losslessly compressed codecs */ + private static final int[] losslessCodecs = { + 0x0163, // WMA lossless + 0x1971, // Sonic foundry lossless + 0xF1AC // FLAC + }; + + /** + * Constructor. + * + * @param module The WaveModule under which this was called + * @param hdr The header for this chunk + * @param dstrm The stream from which the WAVE data are being read + */ + public FormatChunk(WaveModule module, ChunkHeader hdr, DataInputStream dstrm) { + super(module, hdr, dstrm); + } + + /** + * Reads a chunk and puts appropriate Properties into the RepInfo object. + * + * @return false if the chunk is structurally invalid, otherwise true + */ + @Override + public boolean readChunk(RepInfo info) throws IOException { + + WaveModule module = (WaveModule) _module; + + int validBitsPerSample = -1; + byte[] extraBytes = null; + String subformat = null; + long channelMask = -1; + + int waveCodec = module.readUnsignedShort(_dstream); + module.setWaveCodec(waveCodec); + int numChannels = module.readUnsignedShort(_dstream); + long sampleRate = module.readUnsignedInt(_dstream); + module.setSampleRate(sampleRate); + long bytesPerSecond = module.readUnsignedInt(_dstream); + int blockAlign = module.readUnsignedShort(_dstream); + module.setBlockAlign(blockAlign); + int bitsPerSample = module.readUnsignedShort(_dstream); + + bytesLeft -= PCMWAVEFORMAT_LENGTH; + + if (bytesLeft > 0) { + int extraFormatBytes = module.readUnsignedShort(_dstream); + extraBytes = new byte[extraFormatBytes]; + if (waveCodec == WAVE_FORMAT_EXTENSIBLE + && extraFormatBytes >= EXTRA_WAVEFORMATEXTENSIBLE_LENGTH) { + // This is -- or should be -- WAVEFORMATEXTENSIBLE. + // Need to do some additional checks on profile satisfaction. + boolean wfe = true; // Accept tentatively + // The next word may be valid bits per sample, samples + // per block, or merely "reserved". Which one it is + // apparently depends on the compression format. I really + // can't figure out how to tell which it is without + // exhaustively researching all compression formats. + validBitsPerSample = module.readUnsignedShort(_dstream); + channelMask = module.readUnsignedInt(_dstream); + + // The Subformat field is a Microsoft GUID + byte[] guidBytes = new byte[16]; + ModuleBase.readByteBuf(_dstream, guidBytes, module); + subformat = formatGUID(guidBytes); + + // If the Subformat GUID was generated from a Format Tag value, + // extract the old Format Tag value from its GUID so we can + // properly identify the codec and treat it the same way. + if (subformat.endsWith(FORMAT_TAG_GUID_SUFFIX)) { + waveCodec = Integer.parseInt(subformat.substring(4, 8), 16); + module.setWaveCodec(waveCodec); } - Property prop = module.addIntegerProperty("CompressionCode", waveCodec, - WaveStrings.COMPRESSION_FORMAT, WaveStrings.COMPRESSION_INDEX); - module.addWaveProperty(prop); - String compName = (String)prop.getValue(); - - AESAudioMetadata aes = module.getAESMetadata(); - aes.setAudioDataEncoding(compName); - aes.setNumChannels(numChannels); - setChannelLocations(aes, numChannels); - aes.setSampleRate(sampleRate); - aes.setBitDepth(bitsPerSample); - - // Check which codecs are losslessly compressed - String qual = "LOSSY"; - for (int losslessCodec : losslessCodecs) { - if (waveCodec == losslessCodec) { - qual = "CODE_REGENERATING"; - } + // Nitpicking profile requirements + if ((((bitsPerSample + 7) / 8) * numChannels) != blockAlign) { + wfe = false; } - if (waveCodec == WAVE_FORMAT_PCM) { - aes.clearBitrateReduction(); - } - else { - aes.setBitrateReduction(compName, "", "", "", - qual, Long.toString(bytesPerSecond), "FIXED"); - } - - module.addWaveProperty(new Property("AverageBytesPerSecond", - PropertyType.LONG, - Long.valueOf(bytesPerSecond))); - module.addWaveProperty(new Property("BlockAlign", - PropertyType.INTEGER, - Integer.valueOf(blockAlign))); - if (extraBytes != null) { - module.addWaveProperty(new Property("ExtraFormatBytes", - PropertyType.BYTE, - PropertyArity.ARRAY, - extraBytes)); + if ((bitsPerSample % 8) != 0) { + // So why was that fancy ceiling arithmetic needed? + // So it can be the same calculation as with WaveFormatEx. + wfe = false; } - if (validBitsPerSample != -1) { - // Should this property be called something like - // ValidBitsPersampleOrSamplesPerBlock? - module.addWaveProperty(new Property("ValidBitsPerSample", - PropertyType.INTEGER, - Integer.valueOf(validBitsPerSample))); + if (validBitsPerSample > bitsPerSample) { + wfe = false; } - if (channelMask != -1) { - module.addWaveProperty(new Property("ChannelMask", - PropertyType.LONG, - Long.valueOf(channelMask))); + if (wfe) { + module.setWaveFormatExtensible(true); } - if (subformat != null) { - module.addWaveProperty(new Property("Subformat", - PropertyType.STRING, - subformat)); + } else { + if (waveCodec != WAVE_FORMAT_PCM + || (((bitsPerSample + 7) / 8) * numChannels) == blockAlign) { + module.setWaveFormatEx(true); } - - return true; + ModuleBase.readByteBuf(_dstream, extraBytes, module); + } + + // Possible pad to maintain even alignment + if ((extraFormatBytes & 1) != 0) { + _module.skipBytes(_dstream, 1, module); + } + } else { + // No extra bytes signifies the PCM profile. In this case, + // the wave codec also needs to be Microsoft PCM. + if (waveCodec == WAVE_FORMAT_PCM && (((bitsPerSample + 7) / 8) * numChannels) == blockAlign) { + module.setPCMWaveFormat(true); + } } - /** - * Set default channel assignments. This is fairly simple, - * but it's helpful to keep the same structure as the equivalent - * CommonChunk.setChannelLocations function. - */ - private static void setChannelLocations(AESAudioMetadata aes, int numChannels) { - - String[] mapLoc = new String[numChannels]; - switch (numChannels) { - case 2: - mapLoc[0] = "LEFT"; - mapLoc[1] = "RIGHT"; - break; - - // If we get some other number of channels, punt. - default: - for (int i = 0; i < numChannels; i++) { - mapLoc[i] = "UNKNOWN"; - } - } - aes.setMapLocations(mapLoc); + // Add extended MIME type information when available + if (waveCodec != WAVE_FORMAT_EXTENSIBLE) { + String codecID = Integer.toHexString(waveCodec).toUpperCase(); + String extendedMIMEType = _module.getMimeType()[0] + "; codec=" + codecID; + info.setMimeType(extendedMIMEType); } - /** - * Returns the string representation of a Microsoft GUID. - * - * @param guidBytes a 16-byte array with GUID values in little-endian order - * @return a String of the form 00000001-0000-0010-8000-00AA00389B71 - */ - private static String formatGUID(byte[] guidBytes) { - - StringBuilder guid = new StringBuilder(36); - - byte[] doubleWord = reverseBytes(Arrays.copyOf(guidBytes, 4)); - guid.append(DatatypeConverter.printHexBinary(doubleWord)); - guid.append("-"); - - byte[] word = reverseBytes(Arrays.copyOfRange(guidBytes, 4, 6)); - guid.append(DatatypeConverter.printHexBinary(word)); - guid.append("-"); - - word = reverseBytes(Arrays.copyOfRange(guidBytes, 6, 8)); - guid.append(DatatypeConverter.printHexBinary(word)); - guid.append("-"); - - byte[] bytes = Arrays.copyOfRange(guidBytes, 8, 10); - guid.append(DatatypeConverter.printHexBinary(bytes)); - guid.append("-"); - - bytes = Arrays.copyOfRange(guidBytes, 10, 16); - guid.append(DatatypeConverter.printHexBinary(bytes)); + Property prop = + module.addIntegerProperty( + "CompressionCode", + waveCodec, + WaveStrings.COMPRESSION_FORMAT, + WaveStrings.COMPRESSION_INDEX); + module.addWaveProperty(prop); + String compName = (String) prop.getValue(); + + AESAudioMetadata aes = module.getAESMetadata(); + aes.setAudioDataEncoding(compName); + aes.setNumChannels(numChannels); + setChannelLocations(aes, numChannels); + aes.setSampleRate(sampleRate); + aes.setBitDepth(bitsPerSample); + + // Check which codecs are losslessly compressed + String qual = "LOSSY"; + for (int losslessCodec : losslessCodecs) { + if (waveCodec == losslessCodec) { + qual = "CODE_REGENERATING"; + } + } + if (waveCodec == WAVE_FORMAT_PCM) { + aes.clearBitrateReduction(); + } else { + aes.setBitrateReduction(compName, "", "", "", qual, Long.toString(bytesPerSecond), "FIXED"); + } - return guid.toString(); + module.addWaveProperty( + new Property("AverageBytesPerSecond", PropertyType.LONG, Long.valueOf(bytesPerSecond))); + module.addWaveProperty( + new Property("BlockAlign", PropertyType.INTEGER, Integer.valueOf(blockAlign))); + if (extraBytes != null) { + module.addWaveProperty( + new Property("ExtraFormatBytes", PropertyType.BYTE, PropertyArity.ARRAY, extraBytes)); + } + if (validBitsPerSample != -1) { + // Should this property be called something like + // ValidBitsPersampleOrSamplesPerBlock? + module.addWaveProperty( + new Property( + "ValidBitsPerSample", PropertyType.INTEGER, Integer.valueOf(validBitsPerSample))); + } + if (channelMask != -1) { + module.addWaveProperty( + new Property("ChannelMask", PropertyType.LONG, Long.valueOf(channelMask))); + } + if (subformat != null) { + module.addWaveProperty(new Property("Subformat", PropertyType.STRING, subformat)); } - /** Reverses a byte array in place */ - private static byte[] reverseBytes(byte[] bytes) { - for (int i = 0; i < bytes.length / 2; i++) { - byte valueToSwap = bytes[i]; - bytes[i] = bytes[bytes.length - 1 - i]; - bytes[bytes.length - 1 - i] = valueToSwap; + return true; + } + + /** + * Set default channel assignments. This is fairly simple, but it's helpful to keep the same + * structure as the equivalent CommonChunk.setChannelLocations function. + */ + private static void setChannelLocations(AESAudioMetadata aes, int numChannels) { + + String[] mapLoc = new String[numChannels]; + switch (numChannels) { + case 2: + mapLoc[0] = "LEFT"; + mapLoc[1] = "RIGHT"; + break; + + // If we get some other number of channels, punt. + default: + for (int i = 0; i < numChannels; i++) { + mapLoc[i] = "UNKNOWN"; } - return bytes; } + aes.setMapLocations(mapLoc); + } + + /** + * Returns the string representation of a Microsoft GUID. + * + * @param guidBytes a 16-byte array with GUID values in little-endian order + * @return a String of the form 00000001-0000-0010-8000-00AA00389B71 + */ + private static String formatGUID(byte[] guidBytes) { + + StringBuilder guid = new StringBuilder(36); + + byte[] doubleWord = reverseBytes(Arrays.copyOf(guidBytes, 4)); + guid.append(DatatypeConverter.printHexBinary(doubleWord)); + guid.append("-"); + + byte[] word = reverseBytes(Arrays.copyOfRange(guidBytes, 4, 6)); + guid.append(DatatypeConverter.printHexBinary(word)); + guid.append("-"); + + word = reverseBytes(Arrays.copyOfRange(guidBytes, 6, 8)); + guid.append(DatatypeConverter.printHexBinary(word)); + guid.append("-"); + + byte[] bytes = Arrays.copyOfRange(guidBytes, 8, 10); + guid.append(DatatypeConverter.printHexBinary(bytes)); + guid.append("-"); + + bytes = Arrays.copyOfRange(guidBytes, 10, 16); + guid.append(DatatypeConverter.printHexBinary(bytes)); + + return guid.toString(); + } + + /** Reverses a byte array in place */ + private static byte[] reverseBytes(byte[] bytes) { + for (int i = 0; i < bytes.length / 2; i++) { + byte valueToSwap = bytes[i]; + bytes[i] = bytes[bytes.length - 1 - i]; + bytes[bytes.length - 1 - i] = valueToSwap; + } + return bytes; + } } diff --git a/jhove-modules/wave-hul/src/main/java/edu/harvard/hul/ois/jhove/module/wave/InstrumentChunk.java b/jhove-modules/wave-hul/src/main/java/edu/harvard/hul/ois/jhove/module/wave/InstrumentChunk.java index 2fb9008f9..0de16b223 100644 --- a/jhove-modules/wave-hul/src/main/java/edu/harvard/hul/ois/jhove/module/wave/InstrumentChunk.java +++ b/jhove-modules/wave-hul/src/main/java/edu/harvard/hul/ois/jhove/module/wave/InstrumentChunk.java @@ -1,80 +1,62 @@ -/********************************************************************** - * Jhove - JSTOR/Harvard Object Validation Environment - * Copyright 2004 by JSTOR and the President and Fellows of Harvard College - **********************************************************************/ - +/** + * ******************************************************************** Jhove - JSTOR/Harvard Object + * Validation Environment Copyright 2004 by JSTOR and the President and Fellows of Harvard College + * ******************************************************************** + */ package edu.harvard.hul.ois.jhove.module.wave; -import java.io.DataInputStream; -import java.io.IOException; import edu.harvard.hul.ois.jhove.*; import edu.harvard.hul.ois.jhove.module.WaveModule; import edu.harvard.hul.ois.jhove.module.iff.Chunk; import edu.harvard.hul.ois.jhove.module.iff.ChunkHeader; +import java.io.DataInputStream; +import java.io.IOException; /** - * Implementation of the WAVE Instrument Chunk, which - * gives information about a MIDI instrument. Similar to - * the Sample chunk or the AIFF Instrument chunk, but simpler - * than either. + * Implementation of the WAVE Instrument Chunk, which gives information about a MIDI instrument. + * Similar to the Sample chunk or the AIFF Instrument chunk, but simpler than either. * * @author Gary McGath - * */ public class InstrumentChunk extends Chunk { - /** - * Constructor. - * - * @param module The WaveModule under which this was called - * @param hdr The header for this chunk - * @param dstrm The stream from which the WAVE data are being read - */ - public InstrumentChunk( - ModuleBase module, - ChunkHeader hdr, - DataInputStream dstrm) { - super(module, hdr, dstrm); - } + /** + * Constructor. + * + * @param module The WaveModule under which this was called + * @param hdr The header for this chunk + * @param dstrm The stream from which the WAVE data are being read + */ + public InstrumentChunk(ModuleBase module, ChunkHeader hdr, DataInputStream dstrm) { + super(module, hdr, dstrm); + } + /** + * Reads a chunk and puts an Instrument property into the RepInfo object. + * + * @return false if the chunk is structurally invalid, otherwise true + */ + @Override + public boolean readChunk(RepInfo info) throws IOException { + WaveModule module = (WaveModule) _module; + int unshiftedNote = ModuleBase.readUnsignedByte(_dstream, _module); + int fineTune = ModuleBase.readSignedByte(_dstream, _module); + int gain = ModuleBase.readSignedByte(_dstream, _module); + int lowNote = ModuleBase.readUnsignedByte(_dstream, _module); + int highNote = ModuleBase.readUnsignedByte(_dstream, _module); + int lowVelocity = ModuleBase.readUnsignedByte(_dstream, _module); + int highVelocity = ModuleBase.readUnsignedByte(_dstream, _module); - /** Reads a chunk and puts an Instrument property into - * the RepInfo object. - * - * - * @return false if the chunk is structurally - * invalid, otherwise true - */ - @Override - public boolean readChunk(RepInfo info) throws IOException { - WaveModule module = (WaveModule) _module; - int unshiftedNote = ModuleBase.readUnsignedByte (_dstream, _module); - int fineTune = ModuleBase.readSignedByte (_dstream, _module); - int gain = ModuleBase.readSignedByte (_dstream, _module); - int lowNote = ModuleBase.readUnsignedByte (_dstream, _module); - int highNote = ModuleBase.readUnsignedByte (_dstream, _module); - int lowVelocity = ModuleBase.readUnsignedByte (_dstream, _module); - int highVelocity = ModuleBase.readUnsignedByte (_dstream, _module); - - Property[] propArr = new Property[7]; - propArr[0] = new Property ("UnshiftedNote", PropertyType.INTEGER, - new Integer (unshiftedNote)); - propArr[1] = new Property ("FineTune", PropertyType.INTEGER, - new Integer (fineTune)); - propArr[2] = new Property ("Gain", PropertyType.INTEGER, - new Integer (gain)); - propArr[3] = new Property ("LowNote", PropertyType.INTEGER, - new Integer (lowNote)); - propArr[4] = new Property ("HighNote", PropertyType.INTEGER, - new Integer (highNote)); - propArr[5] = new Property ("LowVelocity", PropertyType.INTEGER, - new Integer (lowVelocity)); - propArr[6] = new Property ("HighVelocity", PropertyType.INTEGER, - new Integer (highVelocity)); - module.addWaveProperty (new Property ("Instrument", - PropertyType.PROPERTY, - PropertyArity.ARRAY, - propArr)); - return true; - } + Property[] propArr = new Property[7]; + propArr[0] = new Property("UnshiftedNote", PropertyType.INTEGER, new Integer(unshiftedNote)); + propArr[1] = new Property("FineTune", PropertyType.INTEGER, new Integer(fineTune)); + propArr[2] = new Property("Gain", PropertyType.INTEGER, new Integer(gain)); + propArr[3] = new Property("LowNote", PropertyType.INTEGER, new Integer(lowNote)); + propArr[4] = new Property("HighNote", PropertyType.INTEGER, new Integer(highNote)); + propArr[5] = new Property("LowVelocity", PropertyType.INTEGER, new Integer(lowVelocity)); + propArr[6] = new Property("HighVelocity", PropertyType.INTEGER, new Integer(highVelocity)); + module.addWaveProperty( + new Property("Instrument", PropertyType.PROPERTY, PropertyArity.ARRAY, propArr)); + return true; + } } diff --git a/jhove-modules/wave-hul/src/main/java/edu/harvard/hul/ois/jhove/module/wave/LabelChunk.java b/jhove-modules/wave-hul/src/main/java/edu/harvard/hul/ois/jhove/module/wave/LabelChunk.java index 64bfe895c..7d8182ae2 100644 --- a/jhove-modules/wave-hul/src/main/java/edu/harvard/hul/ois/jhove/module/wave/LabelChunk.java +++ b/jhove-modules/wave-hul/src/main/java/edu/harvard/hul/ois/jhove/module/wave/LabelChunk.java @@ -1,49 +1,44 @@ -/********************************************************************** - * Jhove - JSTOR/Harvard Object Validation Environment - * Copyright 2004 by JSTOR and the President and Fellows of Harvard College - **********************************************************************/ - +/** + * ******************************************************************** Jhove - JSTOR/Harvard Object + * Validation Environment Copyright 2004 by JSTOR and the President and Fellows of Harvard College + * ******************************************************************** + */ package edu.harvard.hul.ois.jhove.module.wave; -import java.io.DataInputStream; -import java.io.IOException; import edu.harvard.hul.ois.jhove.*; import edu.harvard.hul.ois.jhove.module.WaveModule; import edu.harvard.hul.ois.jhove.module.iff.ChunkHeader; +import java.io.DataInputStream; +import java.io.IOException; /** * Implementation of the WAVE Label Chunk. * * @author Gary McGath - * */ public class LabelChunk extends SimpleTextChunk { - /** - * Constructor. - * - * @param module The WaveModule under which this was called - * @param hdr The header for this chunk - * @param dstrm The stream from which the WAVE data are being read - */ - public LabelChunk( - ModuleBase module, - ChunkHeader hdr, - DataInputStream dstrm) { - super(module, hdr, dstrm); - } - - /** Reads a chunk and adds to the module's list of labels. - * - * @return false if the chunk is structurally - * invalid, otherwise true - */ - @Override - public boolean readChunk(RepInfo info) throws IOException { - WaveModule module = (WaveModule) _module; - Property p = readTextProp (module, "Label"); - module.addLabel (p); - return true; - } + /** + * Constructor. + * + * @param module The WaveModule under which this was called + * @param hdr The header for this chunk + * @param dstrm The stream from which the WAVE data are being read + */ + public LabelChunk(ModuleBase module, ChunkHeader hdr, DataInputStream dstrm) { + super(module, hdr, dstrm); + } + /** + * Reads a chunk and adds to the module's list of labels. + * + * @return false if the chunk is structurally invalid, otherwise true + */ + @Override + public boolean readChunk(RepInfo info) throws IOException { + WaveModule module = (WaveModule) _module; + Property p = readTextProp(module, "Label"); + module.addLabel(p); + return true; + } } diff --git a/jhove-modules/wave-hul/src/main/java/edu/harvard/hul/ois/jhove/module/wave/LabeledTextChunk.java b/jhove-modules/wave-hul/src/main/java/edu/harvard/hul/ois/jhove/module/wave/LabeledTextChunk.java index cae4e4e16..abbdbed7f 100644 --- a/jhove-modules/wave-hul/src/main/java/edu/harvard/hul/ois/jhove/module/wave/LabeledTextChunk.java +++ b/jhove-modules/wave-hul/src/main/java/edu/harvard/hul/ois/jhove/module/wave/LabeledTextChunk.java @@ -1,87 +1,67 @@ -/********************************************************************** - * Jhove - JSTOR/Harvard Object Validation Environment - * Copyright 2004 by JSTOR and the President and Fellows of Harvard College +/** + * ******************************************************************** Jhove - JSTOR/Harvard Object + * Validation Environment Copyright 2004 by JSTOR and the President and Fellows of Harvard College * - **********************************************************************/ - + *

******************************************************************** + */ package edu.harvard.hul.ois.jhove.module.wave; -import java.io.*; -import java.util.*; import edu.harvard.hul.ois.jhove.*; import edu.harvard.hul.ois.jhove.module.WaveModule; import edu.harvard.hul.ois.jhove.module.iff.Chunk; import edu.harvard.hul.ois.jhove.module.iff.ChunkHeader; +import java.io.*; +import java.util.*; /** - * - * The Labelled Text Chunk, which can occur only in an Associated Data - * List. - * - * @author Gary McGath + * The Labelled Text Chunk, which can occur only in an Associated Data List. * + * @author Gary McGath */ public class LabeledTextChunk extends Chunk { - /** - * Constructor. - * - * @param module The WaveModule under which this was called - * @param hdr The header for this chunk - * @param dstrm The stream from which the WAVE data are being read - */ - public LabeledTextChunk( - ModuleBase module, - ChunkHeader hdr, - DataInputStream dstrm) - { - super(module, hdr, dstrm); - } + /** + * Constructor. + * + * @param module The WaveModule under which this was called + * @param hdr The header for this chunk + * @param dstrm The stream from which the WAVE data are being read + */ + public LabeledTextChunk(ModuleBase module, ChunkHeader hdr, DataInputStream dstrm) { + super(module, hdr, dstrm); + } + /** + * Reads a chunk and puts an MPEG Property into the RepInfo object. + * + * @return false if the chunk is structurally invalid, otherwise true + */ + @Override + public boolean readChunk(RepInfo info) throws IOException { + WaveModule module = (WaveModule) _module; + long cuePointID = module.readUnsignedInt(_dstream); + long sampleLength = module.readUnsignedInt(_dstream); + long purposeID = module.readUnsignedInt(_dstream); + int country = module.readUnsignedShort(_dstream); + int language = module.readUnsignedShort(_dstream); + int dialect = module.readUnsignedShort(_dstream); + int codePage = module.readUnsignedShort(_dstream); + byte[] buf = new byte[(int) (bytesLeft - 20)]; + ModuleBase.readByteBuf(_dstream, buf, module); + String text = new String(buf).trim(); - /** Reads a chunk and puts an MPEG Property into - * the RepInfo object. - * - * @return false if the chunk is structurally - * invalid, otherwise true - */ - @Override - public boolean readChunk(RepInfo info) throws IOException - { - WaveModule module = (WaveModule) _module; - long cuePointID = module.readUnsignedInt (_dstream); - long sampleLength = module.readUnsignedInt (_dstream); - long purposeID = module.readUnsignedInt (_dstream); - int country = module.readUnsignedShort (_dstream); - int language = module.readUnsignedShort (_dstream); - int dialect = module.readUnsignedShort (_dstream); - int codePage = module.readUnsignedShort (_dstream); - byte[] buf = new byte[(int) (bytesLeft - 20)]; - ModuleBase.readByteBuf(_dstream, buf, module); - String text = new String (buf).trim (); - - // Make the information into a Property. - List plist = new ArrayList (10); - plist.add (new Property ("CuePointID", PropertyType.LONG, - new Long (cuePointID))); - plist.add (new Property ("SampleLength", PropertyType.LONG, - new Long (sampleLength))); - plist.add (new Property ("PurposeID", PropertyType.LONG, - new Long (purposeID))); - plist.add (new Property ("Country", PropertyType.INTEGER, - new Integer (country))); - plist.add (new Property ("Language", PropertyType.INTEGER, - new Integer (language))); - plist.add (new Property ("Dialect", PropertyType.INTEGER, - new Integer (dialect))); - plist.add (new Property ("CodePage", PropertyType.INTEGER, - new Integer (codePage))); - plist.add (new Property ("Text", PropertyType.STRING, - text)); - module.addLabeledText(new Property ("LabeledTextItem", - PropertyType.PROPERTY, - PropertyArity.LIST, - plist)); - return true; - } + // Make the information into a Property. + List plist = new ArrayList(10); + plist.add(new Property("CuePointID", PropertyType.LONG, new Long(cuePointID))); + plist.add(new Property("SampleLength", PropertyType.LONG, new Long(sampleLength))); + plist.add(new Property("PurposeID", PropertyType.LONG, new Long(purposeID))); + plist.add(new Property("Country", PropertyType.INTEGER, new Integer(country))); + plist.add(new Property("Language", PropertyType.INTEGER, new Integer(language))); + plist.add(new Property("Dialect", PropertyType.INTEGER, new Integer(dialect))); + plist.add(new Property("CodePage", PropertyType.INTEGER, new Integer(codePage))); + plist.add(new Property("Text", PropertyType.STRING, text)); + module.addLabeledText( + new Property("LabeledTextItem", PropertyType.PROPERTY, PropertyArity.LIST, plist)); + return true; + } } diff --git a/jhove-modules/wave-hul/src/main/java/edu/harvard/hul/ois/jhove/module/wave/LinkChunk.java b/jhove-modules/wave-hul/src/main/java/edu/harvard/hul/ois/jhove/module/wave/LinkChunk.java index 4f4c4270d..c88233860 100644 --- a/jhove-modules/wave-hul/src/main/java/edu/harvard/hul/ois/jhove/module/wave/LinkChunk.java +++ b/jhove-modules/wave-hul/src/main/java/edu/harvard/hul/ois/jhove/module/wave/LinkChunk.java @@ -1,110 +1,88 @@ -/********************************************************************** - * Jhove - JSTOR/Harvard Object Validation Environment - * Copyright 2004 by JSTOR and the President and Fellows of Harvard College +/** + * ******************************************************************** Jhove - JSTOR/Harvard Object + * Validation Environment Copyright 2004 by JSTOR and the President and Fellows of Harvard College * - **********************************************************************/ - + *

******************************************************************** + */ package edu.harvard.hul.ois.jhove.module.wave; -import edu.harvard.hul.ois.jhove.module.iff.*; import edu.harvard.hul.ois.jhove.*; import edu.harvard.hul.ois.jhove.module.WaveModule; +import edu.harvard.hul.ois.jhove.module.iff.*; import java.io.*; import java.util.*; - import javax.xml.parsers.ParserConfigurationException; import javax.xml.parsers.SAXParserFactory; - import org.xml.sax.SAXException; import org.xml.sax.XMLReader; /** - * Implementation of the WAVE Link Chunk, as specified in - * Specification of the Broadcast Wave Format: - * A format for audio data files in broadcasting; - * Supplement 4: <link> Chunk + * Implementation of the WAVE Link Chunk, as specified in Specification of the Broadcast Wave + * Format: A format for audio data files in broadcasting; Supplement 4: <link> Chunk * (European Broadcasting Union) - * - * @author Gary McGath * + * @author Gary McGath */ public class LinkChunk extends Chunk { - /** - * Constructor. - * - * @param module The WaveModule under which this was called - * @param hdr The header for this chunk - * @param dstrm The stream from which the WAVE data are being read - */ - public LinkChunk ( - ModuleBase module, - ChunkHeader hdr, - DataInputStream dstrm) { - super(module, hdr, dstrm); - } + /** + * Constructor. + * + * @param module The WaveModule under which this was called + * @param hdr The header for this chunk + * @param dstrm The stream from which the WAVE data are being read + */ + public LinkChunk(ModuleBase module, ChunkHeader hdr, DataInputStream dstrm) { + super(module, hdr, dstrm); + } + /** + * Reads a chunk and puts a BroadcastAudioExtension Property into the RepInfo object. + * + * @return false if the chunk is structurally invalid, otherwise true + */ + @Override + public boolean readChunk(RepInfo info) throws IOException { + WaveModule module = (WaveModule) _module; - /** Reads a chunk and puts a BroadcastAudioExtension Property into - * the RepInfo object. - * - * @return false if the chunk is structurally - * invalid, otherwise true - */ - @Override - public boolean readChunk(RepInfo info) throws IOException { - WaveModule module = (WaveModule) _module; - - // We read the XML into a byte array, then use a ByteArrayXMPSource - // to parse it. This isn't XMP, but that code provides a ready-made - // way to generate an XML InputSource. - byte[] buf = new byte[(int) bytesLeft]; - ModuleBase.readByteBuf (_dstream, buf, module); - ByteArrayInputStream bais = new ByteArrayInputStream (buf); - ByteArrayXMPSource xs = new ByteArrayXMPSource (bais); - - try { - // Create an InputSource to feed the parser. - SAXParserFactory factory = - SAXParserFactory.newInstance(); - factory.setNamespaceAware (true); - XMLReader parser = factory.newSAXParser ().getXMLReader (); - LinkChunkHandler handler = new LinkChunkHandler (); - parser.setContentHandler (handler); - parser.parse (xs); - List fileNames = handler.getFileNames (); - String id = handler.getID(); - if (!fileNames.isEmpty ()) { - List plist = new ArrayList (2); - plist.add (new Property ("FileNames", - PropertyType.STRING, - PropertyArity.LIST, - fileNames)); - if (id != null) { - plist.add (new Property ("ID", - PropertyType.STRING, - id)); - } - - module.addWaveProperty (new Property ("Link", - PropertyType.PROPERTY, - PropertyArity.LIST, - plist)); - } - - } - catch (SAXException se) { - info.setMessage (new ErrorMessage - (MessageConstants.WAVE_HUL_13)); - info.setValid (false); - return true; - } - catch (ParserConfigurationException pe) { - info.setMessage (new ErrorMessage - (MessageConstants.WAVE_HUL_14)); - info.setValid (false); - return true; + // We read the XML into a byte array, then use a ByteArrayXMPSource + // to parse it. This isn't XMP, but that code provides a ready-made + // way to generate an XML InputSource. + byte[] buf = new byte[(int) bytesLeft]; + ModuleBase.readByteBuf(_dstream, buf, module); + ByteArrayInputStream bais = new ByteArrayInputStream(buf); + ByteArrayXMPSource xs = new ByteArrayXMPSource(bais); + + try { + // Create an InputSource to feed the parser. + SAXParserFactory factory = SAXParserFactory.newInstance(); + factory.setNamespaceAware(true); + XMLReader parser = factory.newSAXParser().getXMLReader(); + LinkChunkHandler handler = new LinkChunkHandler(); + parser.setContentHandler(handler); + parser.parse(xs); + List fileNames = handler.getFileNames(); + String id = handler.getID(); + if (!fileNames.isEmpty()) { + List plist = new ArrayList(2); + plist.add(new Property("FileNames", PropertyType.STRING, PropertyArity.LIST, fileNames)); + if (id != null) { + plist.add(new Property("ID", PropertyType.STRING, id)); } - return true; + + module.addWaveProperty( + new Property("Link", PropertyType.PROPERTY, PropertyArity.LIST, plist)); + } + + } catch (SAXException se) { + info.setMessage(new ErrorMessage(MessageConstants.WAVE_HUL_13)); + info.setValid(false); + return true; + } catch (ParserConfigurationException pe) { + info.setMessage(new ErrorMessage(MessageConstants.WAVE_HUL_14)); + info.setValid(false); + return true; } + return true; + } } diff --git a/jhove-modules/wave-hul/src/main/java/edu/harvard/hul/ois/jhove/module/wave/LinkChunkHandler.java b/jhove-modules/wave-hul/src/main/java/edu/harvard/hul/ois/jhove/module/wave/LinkChunkHandler.java index 337b081ed..6b336f582 100644 --- a/jhove-modules/wave-hul/src/main/java/edu/harvard/hul/ois/jhove/module/wave/LinkChunkHandler.java +++ b/jhove-modules/wave-hul/src/main/java/edu/harvard/hul/ois/jhove/module/wave/LinkChunkHandler.java @@ -1,9 +1,9 @@ -/********************************************************************** - * Jhove - JSTOR/Harvard Object Validation Environment - * Copyright 2004 by JSTOR and the President and Fellows of Harvard College +/** + * ******************************************************************** Jhove - JSTOR/Harvard Object + * Validation Environment Copyright 2004 by JSTOR and the President and Fellows of Harvard College * - **********************************************************************/ - + *

******************************************************************** + */ package edu.harvard.hul.ois.jhove.module.wave; import java.util.*; @@ -11,101 +11,77 @@ import org.xml.sax.helpers.DefaultHandler; /** - * * This handler parses the data of a WAVE List chunk. - * - * @author Gary McGath * + * @author Gary McGath */ public class LinkChunkHandler extends DefaultHandler { - private StringBuffer _content; - private int state; - private List fileNames; - private String id; - - private final static int STATE_DEFAULT = 0, - STATE_FILE = 1, - STATE_ID = 2; - - public LinkChunkHandler () - { - state = STATE_DEFAULT; - _content = new StringBuffer (); - fileNames = new LinkedList (); - id = null; - } - - - /** Accessor for getting file name list. The value returned - * is meaningful only after parsing. The value returned is - * guaranteed not to be null, but may be empty. */ - public List getFileNames () - { - return fileNames; - } - - - /** Accessor for getting the ID element. The value returned - * may be null, as the ID element is optional. */ - public String getID () - { - return id; - } + private StringBuffer _content; + private int state; + private List fileNames; + private String id; + private static final int STATE_DEFAULT = 0, STATE_FILE = 1, STATE_ID = 2; + public LinkChunkHandler() { + state = STATE_DEFAULT; + _content = new StringBuffer(); + fileNames = new LinkedList(); + id = null; + } - /** - * Looks for the first element encountered. Stores - * its name as the value to be returned by getRoot, - * qualified name by preference, local name if the - * qualified name isn't available. - */ - @Override - public void startElement (String namespaceURI, - String localName, - String qualifiedName, - Attributes atts) - { - if ("FILE".equals (qualifiedName)) { - state = STATE_FILE; - } - else if ("ID".equals (qualifiedName)) { - state = STATE_ID; - } - } + /** + * Accessor for getting file name list. The value returned is meaningful only after parsing. The + * value returned is guaranteed not to be null, but may be empty. + */ + public List getFileNames() { + return fileNames; + } + /** + * Accessor for getting the ID element. The value returned may be null, as the ID element is + * optional. + */ + public String getID() { + return id; + } - /** - * SAX parser callback method for PC text. - */ - @Override - public void characters (char [] ch, int start, int length) - { - _content.append (ch, start, length); + /** + * Looks for the first element encountered. Stores its name as the value to be returned by + * getRoot, qualified name by preference, local name if the qualified name isn't available. + */ + @Override + public void startElement( + String namespaceURI, String localName, String qualifiedName, Attributes atts) { + if ("FILE".equals(qualifiedName)) { + state = STATE_FILE; + } else if ("ID".equals(qualifiedName)) { + state = STATE_ID; } + } + /** SAX parser callback method for PC text. */ + @Override + public void characters(char[] ch, int start, int length) { + _content.append(ch, start, length); + } - /** - * SAX parser callback method. - */ - @Override - public void endElement (String namespaceURI, String localName, - String rawName) - { - switch (state) { - case STATE_FILE: - fileNames.add (_content.toString ()); - break; - case STATE_ID: - id = _content.toString (); - break; - case STATE_DEFAULT: - default: - break; - } - state = STATE_DEFAULT; - _content.setLength (0); + /** SAX parser callback method. */ + @Override + public void endElement(String namespaceURI, String localName, String rawName) { + switch (state) { + case STATE_FILE: + fileNames.add(_content.toString()); + break; + case STATE_ID: + id = _content.toString(); + break; + case STATE_DEFAULT: + default: + break; } - + state = STATE_DEFAULT; + _content.setLength(0); + } } diff --git a/jhove-modules/wave-hul/src/main/java/edu/harvard/hul/ois/jhove/module/wave/ListInfoChunk.java b/jhove-modules/wave-hul/src/main/java/edu/harvard/hul/ois/jhove/module/wave/ListInfoChunk.java index 63f13f601..d038ff282 100644 --- a/jhove-modules/wave-hul/src/main/java/edu/harvard/hul/ois/jhove/module/wave/ListInfoChunk.java +++ b/jhove-modules/wave-hul/src/main/java/edu/harvard/hul/ois/jhove/module/wave/ListInfoChunk.java @@ -1,195 +1,182 @@ -/********************************************************************** - * Jhove - JSTOR/Harvard Object Validation Environment - * Copyright 2004 by JSTOR and the President and Fellows of Harvard College - **********************************************************************/ - +/** + * ******************************************************************** Jhove - JSTOR/Harvard Object + * Validation Environment Copyright 2004 by JSTOR and the President and Fellows of Harvard College + * ******************************************************************** + */ package edu.harvard.hul.ois.jhove.module.wave; -import java.io.DataInputStream; -import java.io.IOException; -import java.util.*; - import edu.harvard.hul.ois.jhove.*; import edu.harvard.hul.ois.jhove.messages.JhoveMessage; import edu.harvard.hul.ois.jhove.messages.JhoveMessages; import edu.harvard.hul.ois.jhove.module.WaveModule; import edu.harvard.hul.ois.jhove.module.iff.*; +import java.io.DataInputStream; +import java.io.IOException; +import java.util.*; /** * Implementation of the WAVE LIST chunk. - * - * Two chunk types, 'exif' and 'INFO', are supported; - * other list types will be reported as unknown - * and treated as an error. + * + *

Two chunk types, 'exif' and 'INFO', are supported; other list types will be reported as + * unknown and treated as an error. * * @author Gary McGath */ public class ListInfoChunk extends Superchunk { - private static final int TYPE_LENGTH = 4; + private static final int TYPE_LENGTH = 4; - /** - * Constructor. - * - * @param module - * The WaveModule under which this was called - * @param hdr - * The header for this chunk - * @param dstrm - * The stream from which the WAVE data are being read - * @param info - * RepInfo object for error reporting - */ - public ListInfoChunk(ModuleBase module, ChunkHeader hdr, - DataInputStream dstrm, RepInfo info) { - super(module, hdr, dstrm, info); - } + /** + * Constructor. + * + * @param module The WaveModule under which this was called + * @param hdr The header for this chunk + * @param dstrm The stream from which the WAVE data are being read + * @param info RepInfo object for error reporting + */ + public ListInfoChunk(ModuleBase module, ChunkHeader hdr, DataInputStream dstrm, RepInfo info) { + super(module, hdr, dstrm, info); + } - /** - * Reads a chunk and puts appropriate information into - * the RepInfo object. - * - * @return false if the chunk is structurally - * invalid, otherwise true - */ - @Override - public boolean readChunk(RepInfo info) throws IOException { - String typeID = ((WaveModule) _module).read4Chars(_dstream); - bytesLeft -= TYPE_LENGTH; - if ("INFO".equals(typeID)) { - return readInfoChunk(info); - } else if ("exif".equals(typeID)) { - return readExifChunk(info); - } else if ("adtl".equals(typeID)) { - return readAdtlChunk(info); - } else { - // Skip unrecognized list types - JhoveMessage message = JhoveMessages.getMessageInstance( - MessageConstants.WAVE_HUL_15.getId(), String.format( - MessageConstants.WAVE_HUL_15.getMessage(), typeID)); - info.setMessage(new InfoMessage(message, chunkOffset)); - _module.skipBytes(_dstream, bytesLeft, _module); - return true; - } - } + /** + * Reads a chunk and puts appropriate information into the RepInfo object. + * + * @return false if the chunk is structurally invalid, otherwise true + */ + @Override + public boolean readChunk(RepInfo info) throws IOException { + String typeID = ((WaveModule) _module).read4Chars(_dstream); + bytesLeft -= TYPE_LENGTH; + if ("INFO".equals(typeID)) { + return readInfoChunk(info); + } else if ("exif".equals(typeID)) { + return readExifChunk(info); + } else if ("adtl".equals(typeID)) { + return readAdtlChunk(info); + } else { + // Skip unrecognized list types + JhoveMessage message = + JhoveMessages.getMessageInstance( + MessageConstants.WAVE_HUL_15.getId(), + String.format(MessageConstants.WAVE_HUL_15.getMessage(), typeID)); + info.setMessage(new InfoMessage(message, chunkOffset)); + _module.skipBytes(_dstream, bytesLeft, _module); + return true; + } + } - private boolean readInfoChunk(RepInfo info) throws IOException { - List listInfoProps = new LinkedList(); - WaveModule module = (WaveModule) _module; - // The set of subchunks is somewhat - // open-ended, but apparently all are identical in format, consisting - // of a null-terminated string. These are subsumed under - // ListInfoTextChunk. We accumulate them into a List of Properties. - for (;;) { - ChunkHeader chunkh = getNextChunkHeader(); - if (chunkh == null) { - break; - } - Chunk chunk = null; - int chunkSize = (int) chunkh.getSize(); - chunk = new ListInfoTextChunk(_module, chunkh, _dstream, - listInfoProps, this); + private boolean readInfoChunk(RepInfo info) throws IOException { + List listInfoProps = new LinkedList(); + WaveModule module = (WaveModule) _module; + // The set of subchunks is somewhat + // open-ended, but apparently all are identical in format, consisting + // of a null-terminated string. These are subsumed under + // ListInfoTextChunk. We accumulate them into a List of Properties. + for (; ; ) { + ChunkHeader chunkh = getNextChunkHeader(); + if (chunkh == null) { + break; + } + Chunk chunk = null; + int chunkSize = (int) chunkh.getSize(); + chunk = new ListInfoTextChunk(_module, chunkh, _dstream, listInfoProps, this); - if (!chunk.readChunk(info)) { - return false; - } - if ((chunkSize & 1) != 0) { - // Must come out to an even byte boundary - _module.skipBytes(_dstream, 1, _module); - --bytesLeft; - } - } - if (!listInfoProps.isEmpty()) { - module.addListInfo(listInfoProps); - } - return true; - } + if (!chunk.readChunk(info)) { + return false; + } + if ((chunkSize & 1) != 0) { + // Must come out to an even byte boundary + _module.skipBytes(_dstream, 1, _module); + --bytesLeft; + } + } + if (!listInfoProps.isEmpty()) { + module.addListInfo(listInfoProps); + } + return true; + } - /** - * The Exif chunk, unlike the Info chunk, has subchunks which aren't - * homogeneous. - */ - private boolean readExifChunk(RepInfo info) throws IOException { - WaveModule module = (WaveModule) _module; - module.setExifInfo(new ExifInfo()); - for (;;) { - ChunkHeader chunkh = getNextChunkHeader(); - if (chunkh == null) { - break; - } - Chunk chunk = null; - String id = chunkh.getID(); - int chunkSize = (int) chunkh.getSize(); + /** The Exif chunk, unlike the Info chunk, has subchunks which aren't homogeneous. */ + private boolean readExifChunk(RepInfo info) throws IOException { + WaveModule module = (WaveModule) _module; + module.setExifInfo(new ExifInfo()); + for (; ; ) { + ChunkHeader chunkh = getNextChunkHeader(); + if (chunkh == null) { + break; + } + Chunk chunk = null; + String id = chunkh.getID(); + int chunkSize = (int) chunkh.getSize(); - if ("ever".equals(id)) { - chunk = new ExifVersionChunk(_module, chunkh, _dstream); - } else if ("erel".equals(id) || "etim".equals(id) - || "ecor".equals(id) || "emdl".equals(id)) { - chunk = new ExifStringChunk(_module, chunkh, _dstream); - } else if ("emnt".equals(id)) { + if ("ever".equals(id)) { + chunk = new ExifVersionChunk(_module, chunkh, _dstream); + } else if ("erel".equals(id) || "etim".equals(id) || "ecor".equals(id) || "emdl".equals(id)) { + chunk = new ExifStringChunk(_module, chunkh, _dstream); + } else if ("emnt".equals(id)) { - } else if ("eucm".equals(id)) { + } else if ("eucm".equals(id)) { - } - if (chunk == null) { - _module.skipBytes(_dstream, chunkSize, _module); - JhoveMessage message = JhoveMessages.getMessageInstance( - MessageConstants.WAVE_HUL_17.getId(), String.format( - MessageConstants.WAVE_HUL_17.getMessage(), id)); - info.setMessage(new InfoMessage(message)); - } else { - if (!chunk.readChunk(info)) { - return false; - } - } - } - return false; - } + } + if (chunk == null) { + _module.skipBytes(_dstream, chunkSize, _module); + JhoveMessage message = + JhoveMessages.getMessageInstance( + MessageConstants.WAVE_HUL_17.getId(), + String.format(MessageConstants.WAVE_HUL_17.getMessage(), id)); + info.setMessage(new InfoMessage(message)); + } else { + if (!chunk.readChunk(info)) { + return false; + } + } + } + return false; + } - /** - * Reads the chunk and its nested chunks, and puts appropriate - * properties into the RepInfo object. - * - * @return false if the chunk or a nested chunk - * is structurally - * invalid, otherwise true - */ - public boolean readAdtlChunk(RepInfo info) throws IOException { - for (;;) { - ChunkHeader chunkh = getNextChunkHeader(); - if (chunkh == null) { - break; - } - Chunk chunk = null; - // The chunk list can include Labels, Notes, and - // Labelled Text. - String id = chunkh.getID(); - int chunkSize = (int) chunkh.getSize(); - if ("labl".equals(id)) { - chunk = new LabelChunk(_module, chunkh, _dstream); - } else if ("note".equals(id)) { - chunk = new NoteChunk(_module, chunkh, _dstream); - } else if ("ltxt".equals(id)) { - chunk = new LabeledTextChunk(_module, chunkh, _dstream); - } + /** + * Reads the chunk and its nested chunks, and puts appropriate properties into the RepInfo object. + * + * @return false if the chunk or a nested chunk is structurally invalid, otherwise + * true + */ + public boolean readAdtlChunk(RepInfo info) throws IOException { + for (; ; ) { + ChunkHeader chunkh = getNextChunkHeader(); + if (chunkh == null) { + break; + } + Chunk chunk = null; + // The chunk list can include Labels, Notes, and + // Labelled Text. + String id = chunkh.getID(); + int chunkSize = (int) chunkh.getSize(); + if ("labl".equals(id)) { + chunk = new LabelChunk(_module, chunkh, _dstream); + } else if ("note".equals(id)) { + chunk = new NoteChunk(_module, chunkh, _dstream); + } else if ("ltxt".equals(id)) { + chunk = new LabeledTextChunk(_module, chunkh, _dstream); + } - if (chunk == null) { - _module.skipBytes(_dstream, chunkSize, _module); - JhoveMessage message = JhoveMessages.getMessageInstance( - MessageConstants.WAVE_HUL_18.getId(), String.format( - MessageConstants.WAVE_HUL_18.getMessage(), id)); - info.setMessage(new InfoMessage(message)); - } else { - if (!chunk.readChunk(info)) { - return false; - } - } - if ((chunkSize & 1) != 0) { - // Must come out to an even byte boundary - _module.skipBytes(_dstream, 1, _module); - --bytesLeft; - } - } - return true; - } + if (chunk == null) { + _module.skipBytes(_dstream, chunkSize, _module); + JhoveMessage message = + JhoveMessages.getMessageInstance( + MessageConstants.WAVE_HUL_18.getId(), + String.format(MessageConstants.WAVE_HUL_18.getMessage(), id)); + info.setMessage(new InfoMessage(message)); + } else { + if (!chunk.readChunk(info)) { + return false; + } + } + if ((chunkSize & 1) != 0) { + // Must come out to an even byte boundary + _module.skipBytes(_dstream, 1, _module); + --bytesLeft; + } + } + return true; + } } diff --git a/jhove-modules/wave-hul/src/main/java/edu/harvard/hul/ois/jhove/module/wave/ListInfoTextChunk.java b/jhove-modules/wave-hul/src/main/java/edu/harvard/hul/ois/jhove/module/wave/ListInfoTextChunk.java index eb2bfd72e..ab30ca904 100644 --- a/jhove-modules/wave-hul/src/main/java/edu/harvard/hul/ois/jhove/module/wave/ListInfoTextChunk.java +++ b/jhove-modules/wave-hul/src/main/java/edu/harvard/hul/ois/jhove/module/wave/ListInfoTextChunk.java @@ -1,135 +1,125 @@ -/********************************************************************** - * Jhove - JSTOR/Harvard Object Validation Environment - * Copyright 2004 by JSTOR and the President and Fellows of Harvard College +/** + * ******************************************************************** Jhove - JSTOR/Harvard Object + * Validation Environment Copyright 2004 by JSTOR and the President and Fellows of Harvard College * - **********************************************************************/ - + *

******************************************************************** + */ package edu.harvard.hul.ois.jhove.module.wave; -import java.io.DataInputStream; -import java.io.IOException; -import java.util.*; - import edu.harvard.hul.ois.jhove.*; import edu.harvard.hul.ois.jhove.messages.JhoveMessage; import edu.harvard.hul.ois.jhove.messages.JhoveMessages; import edu.harvard.hul.ois.jhove.module.WaveModule; import edu.harvard.hul.ois.jhove.module.iff.*; +import java.io.DataInputStream; +import java.io.IOException; +import java.util.*; /** - * - * This implements any of the subchunks of the ListInfoChunk - * (a LIST chunk with a list type of INFO). - * All such chunks are identical in format, consisting of a - * null-terminated string. About 17 chunk ID's are recognized; - * others will be ignored. - * - * @author Gary McGath + * This implements any of the subchunks of the ListInfoChunk (a LIST chunk with a list type of + * INFO). All such chunks are identical in format, consisting of a null-terminated string. About 17 + * chunk ID's are recognized; others will be ignored. * + * @author Gary McGath */ public class ListInfoTextChunk extends Chunk { - private ListInfoChunk _parent; - private List _listInfoProps; - String _chunkID; - - /** - * Constructor. - * - * @param module - * The WaveModule under which this was called - * @param hdr - * The header for this chunk - * @param dstrm - * The stream from which the WAVE data are being read - * @param listInfoProps - * A List of the Properties associated with the - * ListInfoChunk - * @param parent - * The ListInfoChunk within which this Chunk occurs - */ - public ListInfoTextChunk(ModuleBase module, ChunkHeader hdr, - DataInputStream dstrm, List listInfoProps, ListInfoChunk parent) { - super(module, hdr, dstrm); - _parent = parent; - _chunkID = hdr.getID(); - _listInfoProps = listInfoProps; - } + private ListInfoChunk _parent; + private List _listInfoProps; + String _chunkID; - /** - * Reads a chunk and..... - * - * @return false if the chunk is structurally - * invalid, otherwise true - */ - @Override - public boolean readChunk(RepInfo info) throws IOException { - WaveModule module = (WaveModule) _module; - byte[] buf = new byte[(int) bytesLeft]; - ModuleBase.readByteBuf(_dstream, buf, module); - String txt = new String(buf); - txt = txt.trim(); // remove trailing null - String propName = null; + /** + * Constructor. + * + * @param module The WaveModule under which this was called + * @param hdr The header for this chunk + * @param dstrm The stream from which the WAVE data are being read + * @param listInfoProps A List of the Properties associated with the ListInfoChunk + * @param parent The ListInfoChunk within which this Chunk occurs + */ + public ListInfoTextChunk( + ModuleBase module, + ChunkHeader hdr, + DataInputStream dstrm, + List listInfoProps, + ListInfoChunk parent) { + super(module, hdr, dstrm); + _parent = parent; + _chunkID = hdr.getID(); + _listInfoProps = listInfoProps; + } - // Add the string to the property list if we can identify it. - if ("IARL".equals(_chunkID)) { - propName = "ArchivalLocation"; - } else if ("IART".equals(_chunkID)) { - propName = "Artist"; - } else if ("ICMS".equals(_chunkID)) { - propName = "Commissioned"; - } else if ("ICMT".equals(_chunkID)) { - propName = "Comments"; - } else if ("ICOP".equals(_chunkID)) { - propName = "Copyright"; - } else if ("ICRD".equals(_chunkID)) { - propName = "CreationDate"; - } else if ("ICRP".equals(_chunkID)) { - propName = "Cropped"; - } else if ("IDIM".equals(_chunkID)) { - propName = "Dimensions"; - } else if ("IDPI".equals(_chunkID)) { - propName = "DotsPerInch"; - } else if ("IENG".equals(_chunkID)) { - propName = "Engineer"; - } else if ("IGNR".equals(_chunkID)) { - propName = "Genre"; - } else if ("IKEY".equals(_chunkID)) { - propName = "Keywords"; - } else if ("ILGT".equals(_chunkID)) { - propName = "Lightness"; - } else if ("IMED".equals(_chunkID)) { - propName = "Medium"; - } else if ("INAM".equals(_chunkID)) { - propName = "Name"; - } else if ("IPLT".equals(_chunkID)) { - propName = "PaletteSetting"; - } else if ("IPRD".equals(_chunkID)) { - propName = "Product"; - } else if ("ISBJ".equals(_chunkID)) { - propName = "Subject"; - } else if ("ISFT".equals(_chunkID)) { - propName = "Software"; - } else if ("ISHP".equals(_chunkID)) { - propName = "Sharpness"; - } else if ("ISRC".equals(_chunkID)) { - propName = "Source"; - } else if ("ISRF".equals(_chunkID)) { - propName = "SourceForm"; - } else if ("ITCH".equals(_chunkID)) { - propName = "Technician"; // making this a scratch file? - } - if (propName != null) { - _listInfoProps - .add(new Property(propName, PropertyType.STRING, txt)); - } else { - JhoveMessage message = JhoveMessages.getMessageInstance( - MessageConstants.WAVE_HUL_19.getId(), - String.format(MessageConstants.WAVE_HUL_19.getMessage(), - _chunkID)); - info.setMessage(new InfoMessage(message)); - } - return true; - } + /** + * Reads a chunk and..... + * + * @return false if the chunk is structurally invalid, otherwise true + */ + @Override + public boolean readChunk(RepInfo info) throws IOException { + WaveModule module = (WaveModule) _module; + byte[] buf = new byte[(int) bytesLeft]; + ModuleBase.readByteBuf(_dstream, buf, module); + String txt = new String(buf); + txt = txt.trim(); // remove trailing null + String propName = null; + // Add the string to the property list if we can identify it. + if ("IARL".equals(_chunkID)) { + propName = "ArchivalLocation"; + } else if ("IART".equals(_chunkID)) { + propName = "Artist"; + } else if ("ICMS".equals(_chunkID)) { + propName = "Commissioned"; + } else if ("ICMT".equals(_chunkID)) { + propName = "Comments"; + } else if ("ICOP".equals(_chunkID)) { + propName = "Copyright"; + } else if ("ICRD".equals(_chunkID)) { + propName = "CreationDate"; + } else if ("ICRP".equals(_chunkID)) { + propName = "Cropped"; + } else if ("IDIM".equals(_chunkID)) { + propName = "Dimensions"; + } else if ("IDPI".equals(_chunkID)) { + propName = "DotsPerInch"; + } else if ("IENG".equals(_chunkID)) { + propName = "Engineer"; + } else if ("IGNR".equals(_chunkID)) { + propName = "Genre"; + } else if ("IKEY".equals(_chunkID)) { + propName = "Keywords"; + } else if ("ILGT".equals(_chunkID)) { + propName = "Lightness"; + } else if ("IMED".equals(_chunkID)) { + propName = "Medium"; + } else if ("INAM".equals(_chunkID)) { + propName = "Name"; + } else if ("IPLT".equals(_chunkID)) { + propName = "PaletteSetting"; + } else if ("IPRD".equals(_chunkID)) { + propName = "Product"; + } else if ("ISBJ".equals(_chunkID)) { + propName = "Subject"; + } else if ("ISFT".equals(_chunkID)) { + propName = "Software"; + } else if ("ISHP".equals(_chunkID)) { + propName = "Sharpness"; + } else if ("ISRC".equals(_chunkID)) { + propName = "Source"; + } else if ("ISRF".equals(_chunkID)) { + propName = "SourceForm"; + } else if ("ITCH".equals(_chunkID)) { + propName = "Technician"; // making this a scratch file? + } + if (propName != null) { + _listInfoProps.add(new Property(propName, PropertyType.STRING, txt)); + } else { + JhoveMessage message = + JhoveMessages.getMessageInstance( + MessageConstants.WAVE_HUL_19.getId(), + String.format(MessageConstants.WAVE_HUL_19.getMessage(), _chunkID)); + info.setMessage(new InfoMessage(message)); + } + return true; + } } diff --git a/jhove-modules/wave-hul/src/main/java/edu/harvard/hul/ois/jhove/module/wave/MessageConstants.java b/jhove-modules/wave-hul/src/main/java/edu/harvard/hul/ois/jhove/module/wave/MessageConstants.java index e1520bd2f..ac673ae80 100644 --- a/jhove-modules/wave-hul/src/main/java/edu/harvard/hul/ois/jhove/module/wave/MessageConstants.java +++ b/jhove-modules/wave-hul/src/main/java/edu/harvard/hul/ois/jhove/module/wave/MessageConstants.java @@ -5,68 +5,66 @@ import edu.harvard.hul.ois.jhove.messages.JhoveMessages; /** - * Enum used to externalise the PDF modules message Strings. Using an enum - * INSTANCE as a "trick" to ensure a single instance of the class. String - * constants should be prefixed according to their use in the module: + * Enum used to externalise the PDF modules message Strings. Using an enum INSTANCE as a "trick" to + * ensure a single instance of the class. String constants should be prefixed according to their use + * in the module: + * *

    - *
  • WRN_ for warning strings, often logger messages.
  • - *
  • INF_ for informational messages.
  • - *
  • ERR_ for error messages that indicate a file is invalid or not well - * formed.
  • + *
  • WRN_ for warning strings, often logger messages. + *
  • INF_ for informational messages. + *
  • ERR_ for error messages that indicate a file is invalid or not well formed. *
- * When adding new messages try to adopt the following order for the naming - * elements: + * + * When adding new messages try to adopt the following order for the naming elements: + * *
    - *
  1. PREFIX: one of the three prefixes from the list above.
  2. - *
  3. ENTITY_NAME: the name of the PDF entity causing the prohlem, e.g. FONT, - * or DOC.
  4. - *
  5. Problem: a short indicator of the problem type, e.g. MISSING, ILLEGAL, - * etc.
  6. + *
  7. PREFIX: one of the three prefixes from the list above. + *
  8. ENTITY_NAME: the name of the PDF entity causing the prohlem, e.g. FONT, or DOC. + *
  9. Problem: a short indicator of the problem type, e.g. MISSING, ILLEGAL, etc. *
- * The elements should be separated by underscores. The messages currently don't - * follow a consistent vocabulary, that is terms such as invalid, illegal, or - * malformed are used without definition. - * - * @author Carl Wilson - * carlwilson AT github + * + * The elements should be separated by underscores. The messages currently don't follow a consistent + * vocabulary, that is terms such as invalid, illegal, or malformed are used without definition. + * + * @author Carl Wilson carlwilson AT github * @version 0.1 Created 1 Oct 2016:11:38:18 */ - public enum MessageConstants { - INSTANCE; + INSTANCE; - public static final JhoveMessageFactory messageFactory = JhoveMessages - .getInstance("edu.harvard.hul.ois.jhove.module.wave.ErrorMessages"); + public static final JhoveMessageFactory messageFactory = + JhoveMessages.getInstance("edu.harvard.hul.ois.jhove.module.wave.ErrorMessages"); - public static final JhoveMessage WAVE_HUL_1 = messageFactory.getMessage("WAVE-HUL-1"); - public static final JhoveMessage WAVE_HUL_2 = messageFactory.getMessage("WAVE-HUL-2"); - public static final JhoveMessage WAVE_HUL_3 = messageFactory.getMessage("WAVE-HUL-3"); - public static final JhoveMessage WAVE_HUL_3_SUB = messageFactory.getMessage("WAVE-HUL-3-SUB"); - public static final JhoveMessage WAVE_HUL_3_SUB_2 = messageFactory.getMessage("WAVE-HUL-3-SUB-2"); - public static final JhoveMessage WAVE_HUL_4 = messageFactory.getMessage("WAVE-HUL-4"); - public static final JhoveMessage WAVE_HUL_5 = messageFactory.getMessage("WAVE-HUL-5"); - public static final JhoveMessage WAVE_HUL_6 = messageFactory.getMessage("WAVE-HUL-6"); - public static final JhoveMessage WAVE_HUL_7 = messageFactory.getMessage("WAVE-HUL-7"); - public static final JhoveMessage WAVE_HUL_8 = messageFactory.getMessage("WAVE-HUL-8"); - public static final JhoveMessage WAVE_HUL_9 = messageFactory.getMessage("WAVE-HUL-9"); - public static final JhoveMessage WAVE_HUL_9_SUB = messageFactory.getMessage("WAVE-HUL-9-SUB"); - public static final JhoveMessage WAVE_HUL_10 = messageFactory.getMessage("WAVE-HUL-10"); - public static final JhoveMessage WAVE_HUL_11 = messageFactory.getMessage("WAVE-HUL-11"); - public static final JhoveMessage WAVE_HUL_12 = messageFactory.getMessage("WAVE-HUL-12"); - public static final JhoveMessage WAVE_HUL_13 = messageFactory.getMessage("WAVE-HUL-13"); - public static final JhoveMessage WAVE_HUL_14 = messageFactory.getMessage("WAVE-HUL-14"); - public static final JhoveMessage WAVE_HUL_15 = messageFactory.getMessage("WAVE-HUL-15"); -// public static final JhoveMessage WAVE_HUL_16 = RETIRED - public static final JhoveMessage WAVE_HUL_17 = messageFactory.getMessage("WAVE-HUL-17"); - public static final JhoveMessage WAVE_HUL_18 = messageFactory.getMessage("WAVE-HUL-18"); - public static final JhoveMessage WAVE_HUL_19 = messageFactory.getMessage("WAVE-HUL-19"); - public static final JhoveMessage WAVE_HUL_20 = messageFactory.getMessage("WAVE-HUL-20"); - public static final JhoveMessage WAVE_HUL_21 = messageFactory.getMessage("WAVE-HUL-21"); - public static final JhoveMessage WAVE_HUL_22 = messageFactory.getMessage("WAVE-HUL-22"); - public static final JhoveMessage WAVE_HUL_23 = messageFactory.getMessage("WAVE-HUL-23"); - public static final JhoveMessage WAVE_HUL_24 = messageFactory.getMessage("WAVE-HUL-24"); - public static final JhoveMessage WAVE_HUL_25 = messageFactory.getMessage("WAVE-HUL-25"); - public static final JhoveMessage WAVE_HUL_26 = messageFactory.getMessage("WAVE-HUL-26"); - public static final JhoveMessage WAVE_HUL_26_SUB = messageFactory.getMessage("WAVE-HUL-26-SUB"); - public static final JhoveMessage WAVE_HUL_27 = messageFactory.getMessage("WAVE-HUL-27"); + public static final JhoveMessage WAVE_HUL_1 = messageFactory.getMessage("WAVE-HUL-1"); + public static final JhoveMessage WAVE_HUL_2 = messageFactory.getMessage("WAVE-HUL-2"); + public static final JhoveMessage WAVE_HUL_3 = messageFactory.getMessage("WAVE-HUL-3"); + public static final JhoveMessage WAVE_HUL_3_SUB = messageFactory.getMessage("WAVE-HUL-3-SUB"); + public static final JhoveMessage WAVE_HUL_3_SUB_2 = messageFactory.getMessage("WAVE-HUL-3-SUB-2"); + public static final JhoveMessage WAVE_HUL_4 = messageFactory.getMessage("WAVE-HUL-4"); + public static final JhoveMessage WAVE_HUL_5 = messageFactory.getMessage("WAVE-HUL-5"); + public static final JhoveMessage WAVE_HUL_6 = messageFactory.getMessage("WAVE-HUL-6"); + public static final JhoveMessage WAVE_HUL_7 = messageFactory.getMessage("WAVE-HUL-7"); + public static final JhoveMessage WAVE_HUL_8 = messageFactory.getMessage("WAVE-HUL-8"); + public static final JhoveMessage WAVE_HUL_9 = messageFactory.getMessage("WAVE-HUL-9"); + public static final JhoveMessage WAVE_HUL_9_SUB = messageFactory.getMessage("WAVE-HUL-9-SUB"); + public static final JhoveMessage WAVE_HUL_10 = messageFactory.getMessage("WAVE-HUL-10"); + public static final JhoveMessage WAVE_HUL_11 = messageFactory.getMessage("WAVE-HUL-11"); + public static final JhoveMessage WAVE_HUL_12 = messageFactory.getMessage("WAVE-HUL-12"); + public static final JhoveMessage WAVE_HUL_13 = messageFactory.getMessage("WAVE-HUL-13"); + public static final JhoveMessage WAVE_HUL_14 = messageFactory.getMessage("WAVE-HUL-14"); + public static final JhoveMessage WAVE_HUL_15 = messageFactory.getMessage("WAVE-HUL-15"); + // public static final JhoveMessage WAVE_HUL_16 = RETIRED + public static final JhoveMessage WAVE_HUL_17 = messageFactory.getMessage("WAVE-HUL-17"); + public static final JhoveMessage WAVE_HUL_18 = messageFactory.getMessage("WAVE-HUL-18"); + public static final JhoveMessage WAVE_HUL_19 = messageFactory.getMessage("WAVE-HUL-19"); + public static final JhoveMessage WAVE_HUL_20 = messageFactory.getMessage("WAVE-HUL-20"); + public static final JhoveMessage WAVE_HUL_21 = messageFactory.getMessage("WAVE-HUL-21"); + public static final JhoveMessage WAVE_HUL_22 = messageFactory.getMessage("WAVE-HUL-22"); + public static final JhoveMessage WAVE_HUL_23 = messageFactory.getMessage("WAVE-HUL-23"); + public static final JhoveMessage WAVE_HUL_24 = messageFactory.getMessage("WAVE-HUL-24"); + public static final JhoveMessage WAVE_HUL_25 = messageFactory.getMessage("WAVE-HUL-25"); + public static final JhoveMessage WAVE_HUL_26 = messageFactory.getMessage("WAVE-HUL-26"); + public static final JhoveMessage WAVE_HUL_26_SUB = messageFactory.getMessage("WAVE-HUL-26-SUB"); + public static final JhoveMessage WAVE_HUL_27 = messageFactory.getMessage("WAVE-HUL-27"); } diff --git a/jhove-modules/wave-hul/src/main/java/edu/harvard/hul/ois/jhove/module/wave/MpegChunk.java b/jhove-modules/wave-hul/src/main/java/edu/harvard/hul/ois/jhove/module/wave/MpegChunk.java index f317bcf38..d38218da7 100644 --- a/jhove-modules/wave-hul/src/main/java/edu/harvard/hul/ois/jhove/module/wave/MpegChunk.java +++ b/jhove-modules/wave-hul/src/main/java/edu/harvard/hul/ois/jhove/module/wave/MpegChunk.java @@ -1,77 +1,69 @@ -/********************************************************************** - * Jhove - JSTOR/Harvard Object Validation Environment - * Copyright 2004 by JSTOR and the President and Fellows of Harvard College - **********************************************************************/ - +/** + * ******************************************************************** Jhove - JSTOR/Harvard Object + * Validation Environment Copyright 2004 by JSTOR and the President and Fellows of Harvard College + * ******************************************************************** + */ package edu.harvard.hul.ois.jhove.module.wave; -import java.io.IOException; - -import edu.harvard.hul.ois.jhove.RepInfo; -import edu.harvard.hul.ois.jhove.module.iff.*; import edu.harvard.hul.ois.jhove.*; +import edu.harvard.hul.ois.jhove.RepInfo; import edu.harvard.hul.ois.jhove.module.WaveModule; +import edu.harvard.hul.ois.jhove.module.iff.*; import java.io.*; +import java.io.IOException; import java.util.*; /** * Implementation of the WAVE MPEG Audio Extension Chunk. * * @author Gary McGath - * */ public class MpegChunk extends Chunk { - /** - * Constructor. - * - * @param module The WaveModule under which this was called - * @param hdr The header for this chunk - * @param dstrm The stream from which the WAVE data are being read - */ - public MpegChunk ( - ModuleBase module, - ChunkHeader hdr, - DataInputStream dstrm) { - super(module, hdr, dstrm); - } - + /** + * Constructor. + * + * @param module The WaveModule under which this was called + * @param hdr The header for this chunk + * @param dstrm The stream from which the WAVE data are being read + */ + public MpegChunk(ModuleBase module, ChunkHeader hdr, DataInputStream dstrm) { + super(module, hdr, dstrm); + } - /** Reads a chunk and puts an MPEG Property into - * the RepInfo object. - * - * @return false if the chunk is structurally - * invalid, otherwise true - */ - @Override - public boolean readChunk(RepInfo info) throws IOException { - WaveModule module = (WaveModule) _module; - int soundInformation = module.readUnsignedShort(_dstream); - int frameSize = module.readUnsignedShort (_dstream); - int ancillaryDataLength = module.readUnsignedShort (_dstream); - int ancillaryDataDef = module.readUnsignedShort (_dstream); - module.skipBytes (_dstream, 4, module); // reserved - - List propList = new ArrayList (); - propList.add (module.buildBitmaskProperty(soundInformation, - "SoundInformation", - WaveStrings.SOUND_INFORMATION_1, - WaveStrings.SOUND_INFORMATION_0)); - propList.add (new Property ("FrameSize", - PropertyType.INTEGER, - new Integer (frameSize))); - propList.add (new Property ("AncillaryDataLength", - PropertyType.INTEGER, - new Integer (ancillaryDataLength))); - propList.add (module.buildBitmaskProperty(ancillaryDataDef, - "AncillaryDataDef", - WaveStrings.ANCILLARY_DEF_1, - WaveStrings.ANCILLARY_DEF_0)); - module.addWaveProperty (new Property ("MPEG", - PropertyType.PROPERTY, - PropertyArity.LIST, - propList)); - return true; - } + /** + * Reads a chunk and puts an MPEG Property into the RepInfo object. + * + * @return false if the chunk is structurally invalid, otherwise true + */ + @Override + public boolean readChunk(RepInfo info) throws IOException { + WaveModule module = (WaveModule) _module; + int soundInformation = module.readUnsignedShort(_dstream); + int frameSize = module.readUnsignedShort(_dstream); + int ancillaryDataLength = module.readUnsignedShort(_dstream); + int ancillaryDataDef = module.readUnsignedShort(_dstream); + module.skipBytes(_dstream, 4, module); // reserved + List propList = new ArrayList(); + propList.add( + module.buildBitmaskProperty( + soundInformation, + "SoundInformation", + WaveStrings.SOUND_INFORMATION_1, + WaveStrings.SOUND_INFORMATION_0)); + propList.add(new Property("FrameSize", PropertyType.INTEGER, new Integer(frameSize))); + propList.add( + new Property( + "AncillaryDataLength", PropertyType.INTEGER, new Integer(ancillaryDataLength))); + propList.add( + module.buildBitmaskProperty( + ancillaryDataDef, + "AncillaryDataDef", + WaveStrings.ANCILLARY_DEF_1, + WaveStrings.ANCILLARY_DEF_0)); + module.addWaveProperty( + new Property("MPEG", PropertyType.PROPERTY, PropertyArity.LIST, propList)); + return true; + } } diff --git a/jhove-modules/wave-hul/src/main/java/edu/harvard/hul/ois/jhove/module/wave/NoteChunk.java b/jhove-modules/wave-hul/src/main/java/edu/harvard/hul/ois/jhove/module/wave/NoteChunk.java index 12d0c2978..709b00562 100644 --- a/jhove-modules/wave-hul/src/main/java/edu/harvard/hul/ois/jhove/module/wave/NoteChunk.java +++ b/jhove-modules/wave-hul/src/main/java/edu/harvard/hul/ois/jhove/module/wave/NoteChunk.java @@ -1,49 +1,44 @@ -/********************************************************************** - * Jhove - JSTOR/Harvard Object Validation Environment - * Copyright 2004 by JSTOR and the President and Fellows of Harvard College - **********************************************************************/ - +/** + * ******************************************************************** Jhove - JSTOR/Harvard Object + * Validation Environment Copyright 2004 by JSTOR and the President and Fellows of Harvard College + * ******************************************************************** + */ package edu.harvard.hul.ois.jhove.module.wave; -import java.io.DataInputStream; -import java.io.IOException; import edu.harvard.hul.ois.jhove.*; import edu.harvard.hul.ois.jhove.module.WaveModule; import edu.harvard.hul.ois.jhove.module.iff.ChunkHeader; +import java.io.DataInputStream; +import java.io.IOException; /** * Implementation of the WAVE Note Chunk. * * @author Gary McGath - * */ public class NoteChunk extends SimpleTextChunk { - /** - * Constructor. - * - * @param module The WaveModule under which this was called - * @param hdr The header for this chunk - * @param dstrm The stream from which the WAVE data are being read - */ - public NoteChunk( - ModuleBase module, - ChunkHeader hdr, - DataInputStream dstrm) { - super(module, hdr, dstrm); - } - - /** Reads a chunk and adds to the module's list of Notes. - * - * @return false if the chunk is structurally - * invalid, otherwise true - */ - @Override - public boolean readChunk(RepInfo info) throws IOException { - WaveModule module = (WaveModule) _module; - Property p = readTextProp (module, "Note"); - module.addNote (p); - return true; - } + /** + * Constructor. + * + * @param module The WaveModule under which this was called + * @param hdr The header for this chunk + * @param dstrm The stream from which the WAVE data are being read + */ + public NoteChunk(ModuleBase module, ChunkHeader hdr, DataInputStream dstrm) { + super(module, hdr, dstrm); + } + /** + * Reads a chunk and adds to the module's list of Notes. + * + * @return false if the chunk is structurally invalid, otherwise true + */ + @Override + public boolean readChunk(RepInfo info) throws IOException { + WaveModule module = (WaveModule) _module; + Property p = readTextProp(module, "Note"); + module.addNote(p); + return true; + } } diff --git a/jhove-modules/wave-hul/src/main/java/edu/harvard/hul/ois/jhove/module/wave/PeakEnvelopeChunk.java b/jhove-modules/wave-hul/src/main/java/edu/harvard/hul/ois/jhove/module/wave/PeakEnvelopeChunk.java index 072c49813..fc302c1b3 100644 --- a/jhove-modules/wave-hul/src/main/java/edu/harvard/hul/ois/jhove/module/wave/PeakEnvelopeChunk.java +++ b/jhove-modules/wave-hul/src/main/java/edu/harvard/hul/ois/jhove/module/wave/PeakEnvelopeChunk.java @@ -1,13 +1,13 @@ -/********************************************************************** - * Jhove - JSTOR/Harvard Object Validation Environment - * Copyright 2004 by JSTOR and the President and Fellows of Harvard College - **********************************************************************/ - +/** + * ******************************************************************** Jhove - JSTOR/Harvard Object + * Validation Environment Copyright 2004 by JSTOR and the President and Fellows of Harvard College + * ******************************************************************** + */ package edu.harvard.hul.ois.jhove.module.wave; -import edu.harvard.hul.ois.jhove.module.iff.*; import edu.harvard.hul.ois.jhove.*; import edu.harvard.hul.ois.jhove.module.WaveModule; +import edu.harvard.hul.ois.jhove.module.iff.*; import java.io.*; import java.util.*; @@ -15,173 +15,125 @@ * Implementation of the WAVE Peak Envelope ('levl') Chunk. * * @author Gary McGath - * */ public class PeakEnvelopeChunk extends Chunk { + /** + * Constructor. + * + * @param module The WaveModule under which this was called + * @param hdr The header for this chunk + * @param dstrm The stream from which the WAVE data are being read + */ + public PeakEnvelopeChunk(ModuleBase module, ChunkHeader hdr, DataInputStream dstrm) { + super(module, hdr, dstrm); + } - /** - * Constructor. - * - * @param module The WaveModule under which this was called - * @param hdr The header for this chunk - * @param dstrm The stream from which the WAVE data are being read - */ - public PeakEnvelopeChunk ( - ModuleBase module, - ChunkHeader hdr, - DataInputStream dstrm) { - super(module, hdr, dstrm); - } + /** + * Reads a chunk and puts a BroadcastAudioExtension Property into the RepInfo object. + * + * @return false if the chunk is structurally invalid, otherwise true + */ + @Override + public boolean readChunk(RepInfo info) throws IOException { + WaveModule module = (WaveModule) _module; + long version = module.readUnsignedInt(_dstream); + long format = module.readUnsignedInt(_dstream); + long pointsPerValue = module.readUnsignedInt(_dstream); + long blockSize = module.readUnsignedInt(_dstream); + long peakChannels = module.readUnsignedInt(_dstream); + long numPeakFrames = module.readUnsignedInt(_dstream); + long posPeakOfPeaks = module.readUnsignedInt(_dstream); + long offsetToPeaks = module.readUnsignedInt(_dstream); + byte[] buf28 = new byte[28]; + ModuleBase.readByteBuf(_dstream, buf28, module); + String timestamp = byteBufString(buf28); + module.skipBytes(_dstream, 60, module); + // The format of the peak data depends on the value of + // format and pointsPerValue. If format = 1, points are + // unsigned byte. If format = 2, points are unsigned short. + // The number of points per peak value is equal to the + // value of pointsPerValue, which must be 1 or 2. - /** Reads a chunk and puts a BroadcastAudioExtension Property into - * the RepInfo object. - * - * @return false if the chunk is structurally - * invalid, otherwise true - */ - @Override - public boolean readChunk(RepInfo info) throws IOException { - WaveModule module = (WaveModule) _module; - long version = module.readUnsignedInt (_dstream); - long format = module.readUnsignedInt (_dstream); - long pointsPerValue = module.readUnsignedInt (_dstream); - long blockSize = module.readUnsignedInt (_dstream); - long peakChannels = module.readUnsignedInt (_dstream); - long numPeakFrames = module.readUnsignedInt (_dstream); - long posPeakOfPeaks = module.readUnsignedInt (_dstream); - long offsetToPeaks = module.readUnsignedInt (_dstream); - byte[] buf28 = new byte[28]; - ModuleBase.readByteBuf (_dstream, buf28, module); - String timestamp = byteBufString (buf28); - module.skipBytes (_dstream, 60, module); + Property peaksProp = null; + if (bytesLeft > 120) { + int pointBytes = (int) (bytesLeft - 120); + int nPoints = 0; + int nValues = 0; + if (format == 1) { + nPoints = pointBytes; + } else if (format == 2) { + nPoints = pointBytes / 2; + } else { + info.setValid(false); + info.setMessage(new ErrorMessage(MessageConstants.WAVE_HUL_20)); + } + if (pointsPerValue == 1) { + nValues = nPoints; + } else if (pointsPerValue == 2) { + nValues = nPoints / 2; + } else { + info.setValid(false); + info.setMessage(new ErrorMessage(MessageConstants.WAVE_HUL_21)); + } + if (info.getValid() == RepInfo.FALSE) { + module.skipBytes(_dstream, (int) bytesLeft - 120, module); + return true; + } - // The format of the peak data depends on the value of - // format and pointsPerValue. If format = 1, points are - // unsigned byte. If format = 2, points are unsigned short. - // The number of points per peak value is equal to the - // value of pointsPerValue, which must be 1 or 2. - - Property peaksProp = null; - if (bytesLeft > 120) { - int pointBytes = (int) (bytesLeft - 120); - int nPoints = 0; - int nValues = 0; - if (format == 1) { - nPoints = pointBytes; - } - else if (format == 2) { - nPoints = pointBytes / 2; - } - else { - info.setValid (false); - info.setMessage (new ErrorMessage - (MessageConstants.WAVE_HUL_20)); - } - if (pointsPerValue == 1) { - nValues = nPoints; - } - else if (pointsPerValue == 2) { - nValues = nPoints / 2; - } - else { - info.setValid (false); - info.setMessage (new ErrorMessage - (MessageConstants.WAVE_HUL_21)); - } - if (info.getValid() == RepInfo.FALSE) { - module.skipBytes (_dstream, (int) bytesLeft - 120, module); - return true; - } - - // We have two different kinds of property depending on - // pointsPerValue. - if (pointsPerValue == 2) { - Property[] pointArray = new Property[nValues]; - for (int i = 0; i < nValues; i++) { - int[] point = new int[2]; - pointArray[i] = new Property ("Point", - PropertyType.INTEGER, - PropertyArity.ARRAY, - point); - if (format == 1) { - point[0] = - ModuleBase.readUnsignedByte (_dstream, module); - point[1] = - ModuleBase.readUnsignedByte (_dstream, module); - } - else { - point[0] = - module.readUnsignedShort (_dstream); - point[1] = - module.readUnsignedShort (_dstream); - } - } - peaksProp = new Property ("PeakEnvelopeData", - PropertyType.PROPERTY, - PropertyArity.ARRAY, - pointArray); - } - else { - // 1 point per value - int[] pointArray = new int[nValues]; - for (int i = 0; i < nValues; i++) { - if (format == 1) { - pointArray[i] = - ModuleBase.readUnsignedByte (_dstream, module); - } - else { - pointArray[i] = - module.readUnsignedShort (_dstream); - } - } - peaksProp = new Property ("PeakEnvelopeData", - PropertyType.INTEGER, - PropertyArity.ARRAY, - pointArray); - } - } - - // Now put the whole mess together as a List of Properties. - List plist = new ArrayList (20); - plist.add (new Property ("Version", - PropertyType.LONG, - new Long (version))); - plist.add (new Property ("Format", - PropertyType.LONG, - new Long (format))); - plist.add (new Property ("PointsPerValue", - PropertyType.LONG, - new Long (pointsPerValue))); - plist.add (new Property ("BlockSize", - PropertyType.LONG, - new Long (blockSize))); - plist.add (new Property ("PeakChannels", - PropertyType.LONG, - new Long (peakChannels))); - plist.add (new Property ("NumPeakFrames", - PropertyType.LONG, - new Long (numPeakFrames))); - plist.add (new Property ("PosPeakOfPeaks", - PropertyType.LONG, - new Long (posPeakOfPeaks))); - plist.add (new Property ("OffsetToPeaks", - PropertyType.LONG, - new Long (offsetToPeaks))); - if (timestamp.length () > 0) { - plist.add (new Property ("Timestamp", - PropertyType.STRING, - timestamp)); + // We have two different kinds of property depending on + // pointsPerValue. + if (pointsPerValue == 2) { + Property[] pointArray = new Property[nValues]; + for (int i = 0; i < nValues; i++) { + int[] point = new int[2]; + pointArray[i] = new Property("Point", PropertyType.INTEGER, PropertyArity.ARRAY, point); + if (format == 1) { + point[0] = ModuleBase.readUnsignedByte(_dstream, module); + point[1] = ModuleBase.readUnsignedByte(_dstream, module); + } else { + point[0] = module.readUnsignedShort(_dstream); + point[1] = module.readUnsignedShort(_dstream); + } } - if (peaksProp != null) { - plist.add (peaksProp); + peaksProp = + new Property( + "PeakEnvelopeData", PropertyType.PROPERTY, PropertyArity.ARRAY, pointArray); + } else { + // 1 point per value + int[] pointArray = new int[nValues]; + for (int i = 0; i < nValues; i++) { + if (format == 1) { + pointArray[i] = ModuleBase.readUnsignedByte(_dstream, module); + } else { + pointArray[i] = module.readUnsignedShort(_dstream); + } } - module.addWaveProperty (new Property ("PeakEnvelope", - PropertyType.PROPERTY, - PropertyArity.LIST, - plist)); - - return true; + peaksProp = + new Property("PeakEnvelopeData", PropertyType.INTEGER, PropertyArity.ARRAY, pointArray); + } } + + // Now put the whole mess together as a List of Properties. + List plist = new ArrayList(20); + plist.add(new Property("Version", PropertyType.LONG, new Long(version))); + plist.add(new Property("Format", PropertyType.LONG, new Long(format))); + plist.add(new Property("PointsPerValue", PropertyType.LONG, new Long(pointsPerValue))); + plist.add(new Property("BlockSize", PropertyType.LONG, new Long(blockSize))); + plist.add(new Property("PeakChannels", PropertyType.LONG, new Long(peakChannels))); + plist.add(new Property("NumPeakFrames", PropertyType.LONG, new Long(numPeakFrames))); + plist.add(new Property("PosPeakOfPeaks", PropertyType.LONG, new Long(posPeakOfPeaks))); + plist.add(new Property("OffsetToPeaks", PropertyType.LONG, new Long(offsetToPeaks))); + if (timestamp.length() > 0) { + plist.add(new Property("Timestamp", PropertyType.STRING, timestamp)); + } + if (peaksProp != null) { + plist.add(peaksProp); + } + module.addWaveProperty( + new Property("PeakEnvelope", PropertyType.PROPERTY, PropertyArity.LIST, plist)); + + return true; + } } diff --git a/jhove-modules/wave-hul/src/main/java/edu/harvard/hul/ois/jhove/module/wave/SampleChunk.java b/jhove-modules/wave-hul/src/main/java/edu/harvard/hul/ois/jhove/module/wave/SampleChunk.java index 3512cabb8..30ca3fcbf 100644 --- a/jhove-modules/wave-hul/src/main/java/edu/harvard/hul/ois/jhove/module/wave/SampleChunk.java +++ b/jhove-modules/wave-hul/src/main/java/edu/harvard/hul/ois/jhove/module/wave/SampleChunk.java @@ -1,149 +1,114 @@ -/********************************************************************** - * Jhove - JSTOR/Harvard Object Validation Environment - * Copyright 2004 by JSTOR and the President and Fellows of Harvard College - **********************************************************************/ - +/** + * ******************************************************************** Jhove - JSTOR/Harvard Object + * Validation Environment Copyright 2004 by JSTOR and the President and Fellows of Harvard College + * ******************************************************************** + */ package edu.harvard.hul.ois.jhove.module.wave; -import java.io.DataInputStream; -import java.io.IOException; import edu.harvard.hul.ois.jhove.*; import edu.harvard.hul.ois.jhove.module.WaveModule; import edu.harvard.hul.ois.jhove.module.iff.Chunk; import edu.harvard.hul.ois.jhove.module.iff.ChunkHeader; +import java.io.DataInputStream; +import java.io.IOException; /** - * Implementation of the WAVE Sample (or Sampler) Chunk, which - * gives information about a MIDI sample. + * Implementation of the WAVE Sample (or Sampler) Chunk, which gives information about a MIDI + * sample. * * @author Gary McGath - * */ public class SampleChunk extends Chunk { - /** - * Constructor. - * - * @param module The WaveModule under which this was called - * @param hdr The header for this chunk - * @param dstrm The stream from which the WAVE data are being read - */ - public SampleChunk( - ModuleBase module, - ChunkHeader hdr, - DataInputStream dstrm) { - super(module, hdr, dstrm); - } + /** + * Constructor. + * + * @param module The WaveModule under which this was called + * @param hdr The header for this chunk + * @param dstrm The stream from which the WAVE data are being read + */ + public SampleChunk(ModuleBase module, ChunkHeader hdr, DataInputStream dstrm) { + super(module, hdr, dstrm); + } + + /** + * Reads a chunk and puts a Sample property into the RepInfo object. + * + *

It isn't clear whether multiple Sample chunks are allowed (representing different sound + * samples for different notes or note ranges). This module assumes they are, so it constructs a + * Samples property, consisting of a list of Sample properties. + * + * @return false if the chunk is structurally invalid, otherwise true + */ + @Override + public boolean readChunk(RepInfo info) throws IOException { + WaveModule module = (WaveModule) _module; + // read MMA manufacturer and product codes (which we probably won't + // try to resolve) + long manufacturer = module.readUnsignedInt(_dstream); + long product = module.readUnsignedInt(_dstream); + // sample time in nanoseconds + long samplePeriod = module.readUnsignedInt(_dstream); + // read midi unity note (1-127, so why does it get 4 bytes?) + long unityNote = module.readUnsignedInt(_dstream); + // MIDI pitch fraction. This is apparently a fixed-point + // number representing a value between 0 and 1. + long pitchFraction = module.readUnsignedInt(_dstream); + // Get SMPTE format. Maximum value is 30, but it also gets 4 bytes. + long smpteFormat = module.readUnsignedInt(_dstream); + // SMPTE offset consists of four values in a confusing mix + // of signed and unsigned data. The web page I'm working from + // says the frame offset is an unsigned value from 0 to -1. + int sampleOffsetHour = ModuleBase.readSignedByte(_dstream, module); + int sampleOffsetMinute = ModuleBase.readUnsignedByte(_dstream, module); + int sampleOffsetSecond = ModuleBase.readUnsignedByte(_dstream, module); + int sampleOffsetFrames = ModuleBase.readSignedByte(_dstream, module); + // Or should it be unsigned?? + Property[] smpteArr = new Property[4]; + smpteArr[0] = new Property("Hour", PropertyType.INTEGER, new Integer(sampleOffsetHour)); + smpteArr[1] = new Property("Minute", PropertyType.INTEGER, new Integer(sampleOffsetMinute)); + smpteArr[2] = new Property("Second", PropertyType.INTEGER, new Integer(sampleOffsetSecond)); + smpteArr[3] = new Property("Frames", PropertyType.INTEGER, new Integer(sampleOffsetFrames)); - /** Reads a chunk and puts a Sample property into - * the RepInfo object. - * - * It isn't clear whether multiple - * Sample chunks are allowed (representing different sound samples - * for different notes or note ranges). This module assumes - * they are, so it constructs a Samples property, consisting - * of a list of Sample properties. - * - * @return false if the chunk is structurally - * invalid, otherwise true - */ - @Override - public boolean readChunk(RepInfo info) throws IOException { - WaveModule module = (WaveModule) _module; - // read MMA manufacturer and product codes (which we probably won't - // try to resolve) - long manufacturer = module.readUnsignedInt (_dstream); - long product = module.readUnsignedInt (_dstream); - // sample time in nanoseconds - long samplePeriod = module.readUnsignedInt (_dstream); - // read midi unity note (1-127, so why does it get 4 bytes?) - long unityNote = module.readUnsignedInt (_dstream); - // MIDI pitch fraction. This is apparently a fixed-point - // number representing a value between 0 and 1. - long pitchFraction = module.readUnsignedInt (_dstream); - // Get SMPTE format. Maximum value is 30, but it also gets 4 bytes. - long smpteFormat = module.readUnsignedInt (_dstream); - // SMPTE offset consists of four values in a confusing mix - // of signed and unsigned data. The web page I'm working from - // says the frame offset is an unsigned value from 0 to -1. - int sampleOffsetHour = ModuleBase.readSignedByte (_dstream, module); - int sampleOffsetMinute = ModuleBase.readUnsignedByte (_dstream, module); - int sampleOffsetSecond = ModuleBase.readUnsignedByte (_dstream, module); - int sampleOffsetFrames = ModuleBase.readSignedByte (_dstream, module); - // Or should it be unsigned?? - Property[] smpteArr = new Property[4]; - smpteArr[0] = new Property ("Hour", PropertyType.INTEGER, - new Integer (sampleOffsetHour)); - smpteArr[1] = new Property ("Minute", PropertyType.INTEGER, - new Integer (sampleOffsetMinute)); - smpteArr[2] = new Property ("Second", PropertyType.INTEGER, - new Integer (sampleOffsetSecond)); - smpteArr[3] = new Property ("Frames", PropertyType.INTEGER, - new Integer (sampleOffsetFrames)); + int nLoops = (int) module.readUnsignedInt(_dstream); + long extraBytes = module.readUnsignedInt(_dstream); // no. of extra bytes after loops - int nLoops = (int) module.readUnsignedInt (_dstream); - long extraBytes = module.readUnsignedInt (_dstream); // no. of extra bytes after loops - - // Build an array of loop properties - Property[] loopProps = new Property[nLoops]; - for (int i = 0; i < nLoops; i++) { - long cuePoint = module.readUnsignedInt (_dstream); - int type = (int) module.readUnsignedInt (_dstream); - int start = (int) module.readUnsignedInt (_dstream); - int end = (int) module.readUnsignedInt (_dstream); - long fraction = module.readUnsignedInt (_dstream); - long playCount = module.readUnsignedInt (_dstream); - - // Build the loop property. - Property[] lp = new Property[6]; - lp[0] = new Property ("CuePointID", PropertyType.LONG, - new Long (cuePoint)); - lp[1] = new Property ("Type", PropertyType.INTEGER, - new Integer (type)); - lp[2] = new Property ("Start", PropertyType.INTEGER, - new Integer (start)); - lp[3] = new Property ("End", PropertyType.INTEGER, - new Integer (end)); - lp[4] = new Property ("Fraction", PropertyType.LONG, - new Long (fraction)); - lp[5] = new Property ("PlayCount", PropertyType.LONG, - new Long (playCount)); - loopProps[i] = new Property ("Loop", - PropertyType.PROPERTY, - PropertyArity.ARRAY, - lp); - } - - Property[] propArr = new Property[9]; - propArr[0] = new Property ("Manufacturer", PropertyType.LONG, - new Long (manufacturer)); - propArr[1] = new Property ("Product", PropertyType.LONG, - new Long (product)); - propArr[2] = new Property ("SamplePeriod", PropertyType.LONG, - new Long (samplePeriod)); - propArr[3] = new Property ("UnityNote", PropertyType.LONG, - new Long (unityNote)); - propArr[4] = new Property ("PitchFraction", PropertyType.LONG, - new Long (pitchFraction)); - propArr[5] = new Property ("SMPTEFormat", PropertyType.LONG, - new Long (smpteFormat)); - propArr[6] = new Property ("SMPTEOffset", PropertyType.PROPERTY, - PropertyArity.ARRAY, - smpteArr); - propArr[7] = new Property ("Loops", PropertyType.PROPERTY, - PropertyArity.ARRAY, - loopProps); - propArr[8] = new Property ("ExtraDataBytes", PropertyType.LONG, - new Long (extraBytes)); - module.addSample (new Property ("Sample", - PropertyType.PROPERTY, - PropertyArity.ARRAY, - propArr)); - // Skip the extra data bytes that follow - // the loop data. - module.skipBytes (_dstream, (int) extraBytes, _module); - - - return true; + // Build an array of loop properties + Property[] loopProps = new Property[nLoops]; + for (int i = 0; i < nLoops; i++) { + long cuePoint = module.readUnsignedInt(_dstream); + int type = (int) module.readUnsignedInt(_dstream); + int start = (int) module.readUnsignedInt(_dstream); + int end = (int) module.readUnsignedInt(_dstream); + long fraction = module.readUnsignedInt(_dstream); + long playCount = module.readUnsignedInt(_dstream); + + // Build the loop property. + Property[] lp = new Property[6]; + lp[0] = new Property("CuePointID", PropertyType.LONG, new Long(cuePoint)); + lp[1] = new Property("Type", PropertyType.INTEGER, new Integer(type)); + lp[2] = new Property("Start", PropertyType.INTEGER, new Integer(start)); + lp[3] = new Property("End", PropertyType.INTEGER, new Integer(end)); + lp[4] = new Property("Fraction", PropertyType.LONG, new Long(fraction)); + lp[5] = new Property("PlayCount", PropertyType.LONG, new Long(playCount)); + loopProps[i] = new Property("Loop", PropertyType.PROPERTY, PropertyArity.ARRAY, lp); } + + Property[] propArr = new Property[9]; + propArr[0] = new Property("Manufacturer", PropertyType.LONG, new Long(manufacturer)); + propArr[1] = new Property("Product", PropertyType.LONG, new Long(product)); + propArr[2] = new Property("SamplePeriod", PropertyType.LONG, new Long(samplePeriod)); + propArr[3] = new Property("UnityNote", PropertyType.LONG, new Long(unityNote)); + propArr[4] = new Property("PitchFraction", PropertyType.LONG, new Long(pitchFraction)); + propArr[5] = new Property("SMPTEFormat", PropertyType.LONG, new Long(smpteFormat)); + propArr[6] = new Property("SMPTEOffset", PropertyType.PROPERTY, PropertyArity.ARRAY, smpteArr); + propArr[7] = new Property("Loops", PropertyType.PROPERTY, PropertyArity.ARRAY, loopProps); + propArr[8] = new Property("ExtraDataBytes", PropertyType.LONG, new Long(extraBytes)); + module.addSample(new Property("Sample", PropertyType.PROPERTY, PropertyArity.ARRAY, propArr)); + // Skip the extra data bytes that follow + // the loop data. + module.skipBytes(_dstream, (int) extraBytes, _module); + + return true; + } } diff --git a/jhove-modules/wave-hul/src/main/java/edu/harvard/hul/ois/jhove/module/wave/SimpleTextChunk.java b/jhove-modules/wave-hul/src/main/java/edu/harvard/hul/ois/jhove/module/wave/SimpleTextChunk.java index 6f3571e39..3a8810628 100644 --- a/jhove-modules/wave-hul/src/main/java/edu/harvard/hul/ois/jhove/module/wave/SimpleTextChunk.java +++ b/jhove-modules/wave-hul/src/main/java/edu/harvard/hul/ois/jhove/module/wave/SimpleTextChunk.java @@ -1,62 +1,45 @@ -/********************************************************************** - * Jhove - JSTOR/Harvard Object Validation Environment - * Copyright 2004 by JSTOR and the President and Fellows of Harvard College - **********************************************************************/ - +/** + * ******************************************************************** Jhove - JSTOR/Harvard Object + * Validation Environment Copyright 2004 by JSTOR and the President and Fellows of Harvard College + * ******************************************************************** + */ package edu.harvard.hul.ois.jhove.module.wave; -import java.io.DataInputStream; -import java.io.IOException; - import edu.harvard.hul.ois.jhove.*; -//import edu.harvard.hul.ois.jhove.RepInfo; import edu.harvard.hul.ois.jhove.module.WaveModule; import edu.harvard.hul.ois.jhove.module.iff.Chunk; import edu.harvard.hul.ois.jhove.module.iff.ChunkHeader; +import java.io.DataInputStream; +import java.io.IOException; /** * Superclass for the very similar Note and Label chunks. * * @author Gary McGath - * */ public abstract class SimpleTextChunk extends Chunk { - /** - * Constructor. - * - * @param module The WaveModule under which this was called - * @param hdr The header for this chunk - * @param dstrm The stream from which the WAVE data are being read - */ - public SimpleTextChunk( - ModuleBase module, - ChunkHeader hdr, - DataInputStream dstrm) { - super(module, hdr, dstrm); - } - - /** Reads the text item, and returns a Property containing the - * cue point ID and the text. */ - protected Property readTextProp (WaveModule module, String propName) - throws IOException - { - long cueID = module.readUnsignedInt (_dstream); - bytesLeft -= 4; - byte[] buf = new byte[(int) bytesLeft]; - ModuleBase.readByteBuf (_dstream, buf, module); - String txt = new String (buf).trim (); - Property[] propArr = new Property[2]; - propArr[0] = new Property ("CuePointID", - PropertyType.LONG, - new Long (cueID)); - propArr[1] = new Property ("Text", - PropertyType.STRING, - txt); - return new Property (propName, - PropertyType.PROPERTY, - PropertyArity.ARRAY, - propArr); - } + /** + * Constructor. + * + * @param module The WaveModule under which this was called + * @param hdr The header for this chunk + * @param dstrm The stream from which the WAVE data are being read + */ + public SimpleTextChunk(ModuleBase module, ChunkHeader hdr, DataInputStream dstrm) { + super(module, hdr, dstrm); + } + /** Reads the text item, and returns a Property containing the cue point ID and the text. */ + protected Property readTextProp(WaveModule module, String propName) throws IOException { + long cueID = module.readUnsignedInt(_dstream); + bytesLeft -= 4; + byte[] buf = new byte[(int) bytesLeft]; + ModuleBase.readByteBuf(_dstream, buf, module); + String txt = new String(buf).trim(); + Property[] propArr = new Property[2]; + propArr[0] = new Property("CuePointID", PropertyType.LONG, new Long(cueID)); + propArr[1] = new Property("Text", PropertyType.STRING, txt); + return new Property(propName, PropertyType.PROPERTY, PropertyArity.ARRAY, propArr); + } } diff --git a/jhove-modules/wave-hul/src/main/java/edu/harvard/hul/ois/jhove/module/wave/WaveStrings.java b/jhove-modules/wave-hul/src/main/java/edu/harvard/hul/ois/jhove/module/wave/WaveStrings.java index d5bff7bae..28d4b827e 100644 --- a/jhove-modules/wave-hul/src/main/java/edu/harvard/hul/ois/jhove/module/wave/WaveStrings.java +++ b/jhove-modules/wave-hul/src/main/java/edu/harvard/hul/ois/jhove/module/wave/WaveStrings.java @@ -1,493 +1,276 @@ -/********************************************************************** - * Jhove - JSTOR/Harvard Object Validation Environment - * Copyright 2004 by JSTOR and the President and Fellows of Harvard College - **********************************************************************/ - +/** + * ******************************************************************** Jhove - JSTOR/Harvard Object + * Validation Environment Copyright 2004 by JSTOR and the President and Fellows of Harvard College + * ******************************************************************** + */ package edu.harvard.hul.ois.jhove.module.wave; /** - * A class for holding arrays of informative strings that will go into - * properties of a WAVE object. + * A class for holding arrays of informative strings that will go into properties of a WAVE object. * * @author Gary McGath - * */ public class WaveStrings { - - /** Strings for data compression formats; indexed by - * COMPRESSION_INDEX */ - public final static String[] COMPRESSION_FORMAT = - { "Unknown or unspecified format", // 0 - "PCM audio in integer format", // 1 - "Microsoft adaptive PCM", // 2 - "PCM audio in IEEE floating-point format", // 3 - "VSELP codec for Windows CE 2.0 device", // 4 - "IBM CVSD", // 5 - "Microsoft ALAW", // 6 - "Microsoft MULAW", // 7 - "Microsoft DTS", // 8 - "Microsoft Digital Rights Managed encrypted audio", // 9 - "Microsoft Speech audio codec", // 0XA - "Windows Media RT Voice", // 0xB - "OKI ADPCM", // 0X10 - "Intel ADPCM", // 0X11 - "Videologic Systems ADPCM", // 0X12 - "Sierra ADPCM", // 0X13 - "Antex ADPCM", // 0X14 - "DSP Solutions DIGISTD", // 0X15 - "DSP Solutions DIGIFIX", // 0X16 - "OKI ADPCM chips or firmware", // 0X17 - "ADPCM for Jazz 16 chip set", // 0X18 - "HP CU Codec", // 0X19 - "HP Dynamic Voice", // 0X1A - "Yamaha ADPCM", // 0X20 - "Speech Compression SONARC", // 0X21 - "DSP Group True Speech", // 0X22 - "Echo Speech SC1", // 0X23 - "Ahead Audio File AF36", // 0X24 - "Audio Processing Technology APTX", // 0X25 - "Ahead Audio File AF10", // 0X26 - "Prosody CTI speech card", // 0X27 - "Merging Technologies LRC", // 0X28 - "Dolby AC2", // 0X30 - "Microsoft GSM610", // 0X31 - "Microsoft MSN audio codec", // 0X32 - "Antex ADPCME", // 0X33 - "Control Resources VQLPC", // 0X34 - "DSP Solutions Digireal", // 0X35 - "DSP Solutions DIGIADPCM", // 0X36 - "Control Resources CR10", // 0X37 - "Natural Microsystems VBXADPCM", // 0X38 - "Roland RDAC", // 0X39 - "Echo Speech SC3", // 0X3A - "Rockwell ADPCM", // 0X3B - "Rockwell DIGITALK", // 0X3C - "Xebec Multimedia Solutions", // 0X3D - "Antex G721 ADPCM", // 0X40 - "Antex G728 CELP", // 0X41 - "Microsoft MSG723", // 0X42 - "Microsoft MSG723.1", // 0X43 - "Microsoft MSG729", // 0X44 - "Microsoft MSG726", // 0X45 - "Microsoft MPEG", // 0X50 - "InSoft RT24", // 0X52 - "InSoft PAC", // 0X53 - "ISO/MPEG Layer 3 format tag", // 0X55 - "Lucent G723", // 0X59 - "Cirrus Logic", // 0X60 - "ESS PCM", // 0X61 - "Voxware", // 0X62 - "Canopus ATRACWAVEFORMAT", // 0X63 - "APICOM G726 ADPCM", // 0X64 - "APICOM G722 ADPCM", // 0X65 - "Microsoft DSAT Display", // 0X67 - "Voxware Byte Aligned", // 0X69 - "Voxware AC8", // 0X70 - "Voxware AC10", // 0X71 - "Voxware AC16", // 0X72 - "Voxware AC20", // 0X73 - "Voxware RT24", // 0X74 - "Voxware RT29", // 0X75 - "Voxware RT29HW", // 0X76 - "Voxware VR12", // 0X77 - "Voxware VR18", // 0X78 - "Voxware TQ40", // 0X79 - "Voxware SC3 (7A)", // 0X7A - "Voxware SC3 (7B)", // 0X7B - "SoftSound", // 0X80 - "Voxware TQ60", // 0X81 - "Microsoft MSRT24", // 0X82 - "AT&T G729A", // 0X83 - "Motion Pixels MVI2", // 0X84 - "Datafusion Systems G726", // 0X85 - "Datafusion Systems GSM610", // 0X86 - "Iterated Systems ISI Audio", // 0X88 - "OnLive", // 0X89 - "Multitude FT SX20", // 0X8A - "G.721 ADPCM", // 0X8B - "Convedia G729", // 0X8C - "Congruency Audio Codec", // 0X8D - "Siemens SBC24", // 0X91 - "Sonic Foundry Dolby AC3 SPDIF", // 0X92 - "MediaSonic G723", // 0x93, - "Prosody CTI speech card", // 0X94, - "ZyXEL ADPCM", // 0X97, - "Philips LPCBB", // 0X98, - "Studer Professional Audio Packed", // 0X99, - "Phony Talk", // 0XA0, - "Racal Recorder GSM", // 0XA1, - "Racal Recorder G720.a", // 0XA2, - "Racal G723.1", // 0XA3, - "Racal Tetra ACELP", // 0XA4, - "NEC AAC", // 0XB0, - "Rhetorex ADPCM wave format type", // 0X100, - "BeCubed IRAT", // 0x101, - "Vivo G723", // 0X111, - "Vivo Siren", // 0X112, - "Philips CELP", // 0X120, - "Philips Grundig", // 0X121, - "DEC G723", // 0X123, - "SANYO LD-ADPCM wave type", // 0X125, - "Sipro Lab ACELPNET", // 0X130, - "Sipro Lab ACELP4800", // 0X131, - "Sipro Lab ACELP8V3", // 0X132, - "Sipro Lab ACELPG729", // 0X133, - "Sipro Lab ACELPG729A", // 0X134, - "Sipro Lab Kelvin", // 0X135, - "VoiceAge AMR", // 0X136, - "Dictaphone G726 ADPCM", // 0X140, - "Dictaphone CELP68", // 0X141, - "Dictaphone CELP54", // 0X142, - "Qualcomm Pure Voice", // 0X150, - "Qualcomm Half Rate", // 0X151, - "Related to GSM 6.10", // 0x155, - "Microsoft Audio 1", // 0X160, - "Microsoft Audio 2", // 0X161, - "Microsoft Multichannel WMA", // 0X162, - "WMA lossless", // 0x163 - "WMA Pro over S/PDIF", // 0x164 - "Unisys ADPCM", // 0X170, - "Unisys ULAW", // 0X171, - "Unisys ALAW", // 0X172, - "Unisys NAP 16K", // 0X173, - "SyCom ACM SYC008", // 0X174, - "SyCom ACM SYC701 G726L", // 0X175, - "SyCom ACM SYC701 CELP54", // 0X176, - "SyCom ACM SYC701 CELP68", // 0X177 - "Knowledge Adventure ADPCM", // 0X178 - "Fraunhofer IIS MPEC 2AAC", // 0X180 - "Digital Theater Systems DS", // 0X190, - "Creative Labs ADPCM", // 0X200 - "Fast Speech 8", // 0X202 - "Fast Speech 10", // 0X203 - "UHER ADPCM", // 0X210 - "Quarterdeck", // 0X220 - "I-Link VC", // 0X230 - "Aureal Raw Sport", // 0x240 - "Interactive Products HSX", // 0x250 - "Interactive Products RPELP", // 0x251 - "Cs2", // 0X260 - "Sony SCX", // 0X270 - "Sony SCY", // 0X271 - "Sony ATRAC3", // 0X272 - "Sony SPC", // 0X273 - "Telum", // 0X280 - "Telum IA", // 0X281 - "Norcom Voice Systems ADPCM", // 0x285 - "Fujitsu FM Towns SND", // 0X300 - "Fujitsu (301)", // 0x301 - "Fujitsu (302)", // 0x302 - "Fujitsu (303)", // 0x303 - "Fujitsu (304)", // 0x304 - "Fujitsu (305)", // 0x305 - "Fujitsu (306)", // 0x306 - "Fujitsu (307)", // 0x307 - "Fujitsu (308)", // 0x308 - "Micronas Development", // 0x350 - "Micronas CELP833", // 0x351 - "Brooktree digital audio format", // 0x400 - "QDesign Music", // 0x450 - "AT&T VMPCM", // 0x680 - "AT&T TPC", // 0x681 - "Olivetti SM", // 0x1000 - "Olivetti PCM", // 0x1001 - "Olivetti CELP", // 0x1002 - "Olivetti SBC", // 0x1003 - "Olivetti OPR", // 0x1004 - "Lernout & Hauspie Codec", // 0x1100 - "Lernout & Hauspie CELP", // 0x1101 - "Lernout & Hauspie SB8", // 0x1102 - "Lernout & Hauspie SB12", // 0x1103 - "Lernout & Hauspie SB16", // 0x1104 - "Norris", // 0x1400 - "AT&T Soundspace Musicompress", // 0x1500 - "Sonic Foundry Lossless", // 0x1971 - "Innings ADPCM", // 0X1979 - "FAST Multimedia DVM", // 0x2000 - "Reserved rangle to 0x2600", // 0x2500 - "Divio's AAC", // 0x4143 - "Nokia adaptive multirate", // 0x4201 - "Divio's G726", // 0x4243 - "3Com NBX", // 0x7000 - "Adaptive multirate", // 0x7a21 - "AMR with silence detection", // 0x7a22 - "Comverse G723.1", // 0xa100 - "Comverse AVQSBC", // 0xa101 - "Comverse old SBC", // 0xa102 - "Symbol Technology's G729A", // 0xa103 - "Voice Age AMR WB", // 0xa104 - "Ingenient's G726", // 0xa105 - "ISO/MPEG-4 advanced audio Coding", // 0xa106 - "Encore Software Ltd's G726", // 0xa107 - "Extensible Wave format" // 0xfffe - }; - - public final static int[] COMPRESSION_INDEX = - { - 0, - 1, - 2, - 3, - 4, - 5, - 6, - 7, - 8, - 9, - 0XA, - 0xB, - 0X10, - 0X11, - 0X12, - 0X13, - 0X14, - 0X15, - 0X16, - 0X17, - 0X18, - 0X19, - 0X1A, - 0X20, - 0X21, - 0X22, - 0X23, - 0X24, - 0X25, - 0X26, - 0X27, - 0X28, - 0X30, - 0X31, - 0X32, - 0X33, - 0X34, - 0X35, - 0X36, - 0X37, - 0X38, - 0X39, - 0X3A, - 0X3B, - 0X3C, - 0X3D, - 0X40, - 0X41, - 0X42, - 0X43, - 0X44, - 0X45, - 0X50, - 0X52, - 0X53, - 0X55, - 0X59, - 0X60, - 0X61, - 0X62, - 0X63, - 0X64, - 0X65, - 0X67, - 0X69, - 0X70, - 0X71, - 0X72, - 0X73, - 0X74, - 0X75, - 0X76, - 0X77, - 0X78, - 0X79, - 0X7A, - 0X7B, - 0X80, - 0X81, - 0X82, - 0X83, - 0X84, - 0X85, - 0X86, - 0X88, - 0X89, - 0X8A, - 0X8B, - 0X8C, - 0X8D, - 0X91, - 0X92, - 0x93, - 0X94, - 0X97, - 0X98, - 0X99, - 0XA0, - 0XA1, - 0XA2, - 0XA3, - 0XA4, - 0XB0, - 0X100, - 0x101, - 0X111, - 0X112, - 0X120, - 0X121, - 0X123, - 0X125, - 0X130, - 0X131, - 0X132, - 0X133, - 0X134, - 0X135, - 0X136, - 0X140, - 0X141, - 0X142, - 0X150, - 0X151, - 0x155, - 0X160, - 0X161, - 0X162, - 0x163, - 0x164, - 0X170, - 0X171, - 0X172, - 0X173, - 0X174, - 0X175, - 0X176, - 0X177, - 0X178, - 0X180, - 0X190, - 0X200, - 0X202, - 0X203, - 0X210, - 0X220, - 0X230, - 0x240, - 0x250, - 0x251, - 0X260, - 0X270, - 0X271, - 0X272, - 0X273, - 0X280, - 0X281, - 0x285, - 0X300, - 0x301, - 0x302, - 0x303, - 0x304, - 0x305, - 0x306, - 0x307, - 0x308, - 0x350, - 0x351, - 0x400, - 0x450, - 0x680, - 0x681, - 0x1000, - 0x1001, - 0x1002, - 0x1003, - 0x1004, - 0x1100, - 0x1101, - 0x1102, - 0x1103, - 0x1104, - 0x1400, - 0x1500, - 0x1971, - 0X1979, - 0x2000, - 0x2500, - 0x4143, - 0x4201, - 0x4243, - 0x7000, - 0x7a21, - 0x7a22, - 0xa100, - 0xa101, - 0xa102, - 0xa103, - 0xa104, - 0xa105, - 0xa106, - 0xa107, - 0xfffe - }; + /** Strings for data compression formats; indexed by COMPRESSION_INDEX */ + public static final String[] COMPRESSION_FORMAT = { + "Unknown or unspecified format", // 0 + "PCM audio in integer format", // 1 + "Microsoft adaptive PCM", // 2 + "PCM audio in IEEE floating-point format", // 3 + "VSELP codec for Windows CE 2.0 device", // 4 + "IBM CVSD", // 5 + "Microsoft ALAW", // 6 + "Microsoft MULAW", // 7 + "Microsoft DTS", // 8 + "Microsoft Digital Rights Managed encrypted audio", // 9 + "Microsoft Speech audio codec", // 0XA + "Windows Media RT Voice", // 0xB + "OKI ADPCM", // 0X10 + "Intel ADPCM", // 0X11 + "Videologic Systems ADPCM", // 0X12 + "Sierra ADPCM", // 0X13 + "Antex ADPCM", // 0X14 + "DSP Solutions DIGISTD", // 0X15 + "DSP Solutions DIGIFIX", // 0X16 + "OKI ADPCM chips or firmware", // 0X17 + "ADPCM for Jazz 16 chip set", // 0X18 + "HP CU Codec", // 0X19 + "HP Dynamic Voice", // 0X1A + "Yamaha ADPCM", // 0X20 + "Speech Compression SONARC", // 0X21 + "DSP Group True Speech", // 0X22 + "Echo Speech SC1", // 0X23 + "Ahead Audio File AF36", // 0X24 + "Audio Processing Technology APTX", // 0X25 + "Ahead Audio File AF10", // 0X26 + "Prosody CTI speech card", // 0X27 + "Merging Technologies LRC", // 0X28 + "Dolby AC2", // 0X30 + "Microsoft GSM610", // 0X31 + "Microsoft MSN audio codec", // 0X32 + "Antex ADPCME", // 0X33 + "Control Resources VQLPC", // 0X34 + "DSP Solutions Digireal", // 0X35 + "DSP Solutions DIGIADPCM", // 0X36 + "Control Resources CR10", // 0X37 + "Natural Microsystems VBXADPCM", // 0X38 + "Roland RDAC", // 0X39 + "Echo Speech SC3", // 0X3A + "Rockwell ADPCM", // 0X3B + "Rockwell DIGITALK", // 0X3C + "Xebec Multimedia Solutions", // 0X3D + "Antex G721 ADPCM", // 0X40 + "Antex G728 CELP", // 0X41 + "Microsoft MSG723", // 0X42 + "Microsoft MSG723.1", // 0X43 + "Microsoft MSG729", // 0X44 + "Microsoft MSG726", // 0X45 + "Microsoft MPEG", // 0X50 + "InSoft RT24", // 0X52 + "InSoft PAC", // 0X53 + "ISO/MPEG Layer 3 format tag", // 0X55 + "Lucent G723", // 0X59 + "Cirrus Logic", // 0X60 + "ESS PCM", // 0X61 + "Voxware", // 0X62 + "Canopus ATRACWAVEFORMAT", // 0X63 + "APICOM G726 ADPCM", // 0X64 + "APICOM G722 ADPCM", // 0X65 + "Microsoft DSAT Display", // 0X67 + "Voxware Byte Aligned", // 0X69 + "Voxware AC8", // 0X70 + "Voxware AC10", // 0X71 + "Voxware AC16", // 0X72 + "Voxware AC20", // 0X73 + "Voxware RT24", // 0X74 + "Voxware RT29", // 0X75 + "Voxware RT29HW", // 0X76 + "Voxware VR12", // 0X77 + "Voxware VR18", // 0X78 + "Voxware TQ40", // 0X79 + "Voxware SC3 (7A)", // 0X7A + "Voxware SC3 (7B)", // 0X7B + "SoftSound", // 0X80 + "Voxware TQ60", // 0X81 + "Microsoft MSRT24", // 0X82 + "AT&T G729A", // 0X83 + "Motion Pixels MVI2", // 0X84 + "Datafusion Systems G726", // 0X85 + "Datafusion Systems GSM610", // 0X86 + "Iterated Systems ISI Audio", // 0X88 + "OnLive", // 0X89 + "Multitude FT SX20", // 0X8A + "G.721 ADPCM", // 0X8B + "Convedia G729", // 0X8C + "Congruency Audio Codec", // 0X8D + "Siemens SBC24", // 0X91 + "Sonic Foundry Dolby AC3 SPDIF", // 0X92 + "MediaSonic G723", // 0x93, + "Prosody CTI speech card", // 0X94, + "ZyXEL ADPCM", // 0X97, + "Philips LPCBB", // 0X98, + "Studer Professional Audio Packed", // 0X99, + "Phony Talk", // 0XA0, + "Racal Recorder GSM", // 0XA1, + "Racal Recorder G720.a", // 0XA2, + "Racal G723.1", // 0XA3, + "Racal Tetra ACELP", // 0XA4, + "NEC AAC", // 0XB0, + "Rhetorex ADPCM wave format type", // 0X100, + "BeCubed IRAT", // 0x101, + "Vivo G723", // 0X111, + "Vivo Siren", // 0X112, + "Philips CELP", // 0X120, + "Philips Grundig", // 0X121, + "DEC G723", // 0X123, + "SANYO LD-ADPCM wave type", // 0X125, + "Sipro Lab ACELPNET", // 0X130, + "Sipro Lab ACELP4800", // 0X131, + "Sipro Lab ACELP8V3", // 0X132, + "Sipro Lab ACELPG729", // 0X133, + "Sipro Lab ACELPG729A", // 0X134, + "Sipro Lab Kelvin", // 0X135, + "VoiceAge AMR", // 0X136, + "Dictaphone G726 ADPCM", // 0X140, + "Dictaphone CELP68", // 0X141, + "Dictaphone CELP54", // 0X142, + "Qualcomm Pure Voice", // 0X150, + "Qualcomm Half Rate", // 0X151, + "Related to GSM 6.10", // 0x155, + "Microsoft Audio 1", // 0X160, + "Microsoft Audio 2", // 0X161, + "Microsoft Multichannel WMA", // 0X162, + "WMA lossless", // 0x163 + "WMA Pro over S/PDIF", // 0x164 + "Unisys ADPCM", // 0X170, + "Unisys ULAW", // 0X171, + "Unisys ALAW", // 0X172, + "Unisys NAP 16K", // 0X173, + "SyCom ACM SYC008", // 0X174, + "SyCom ACM SYC701 G726L", // 0X175, + "SyCom ACM SYC701 CELP54", // 0X176, + "SyCom ACM SYC701 CELP68", // 0X177 + "Knowledge Adventure ADPCM", // 0X178 + "Fraunhofer IIS MPEC 2AAC", // 0X180 + "Digital Theater Systems DS", // 0X190, + "Creative Labs ADPCM", // 0X200 + "Fast Speech 8", // 0X202 + "Fast Speech 10", // 0X203 + "UHER ADPCM", // 0X210 + "Quarterdeck", // 0X220 + "I-Link VC", // 0X230 + "Aureal Raw Sport", // 0x240 + "Interactive Products HSX", // 0x250 + "Interactive Products RPELP", // 0x251 + "Cs2", // 0X260 + "Sony SCX", // 0X270 + "Sony SCY", // 0X271 + "Sony ATRAC3", // 0X272 + "Sony SPC", // 0X273 + "Telum", // 0X280 + "Telum IA", // 0X281 + "Norcom Voice Systems ADPCM", // 0x285 + "Fujitsu FM Towns SND", // 0X300 + "Fujitsu (301)", // 0x301 + "Fujitsu (302)", // 0x302 + "Fujitsu (303)", // 0x303 + "Fujitsu (304)", // 0x304 + "Fujitsu (305)", // 0x305 + "Fujitsu (306)", // 0x306 + "Fujitsu (307)", // 0x307 + "Fujitsu (308)", // 0x308 + "Micronas Development", // 0x350 + "Micronas CELP833", // 0x351 + "Brooktree digital audio format", // 0x400 + "QDesign Music", // 0x450 + "AT&T VMPCM", // 0x680 + "AT&T TPC", // 0x681 + "Olivetti SM", // 0x1000 + "Olivetti PCM", // 0x1001 + "Olivetti CELP", // 0x1002 + "Olivetti SBC", // 0x1003 + "Olivetti OPR", // 0x1004 + "Lernout & Hauspie Codec", // 0x1100 + "Lernout & Hauspie CELP", // 0x1101 + "Lernout & Hauspie SB8", // 0x1102 + "Lernout & Hauspie SB12", // 0x1103 + "Lernout & Hauspie SB16", // 0x1104 + "Norris", // 0x1400 + "AT&T Soundspace Musicompress", // 0x1500 + "Sonic Foundry Lossless", // 0x1971 + "Innings ADPCM", // 0X1979 + "FAST Multimedia DVM", // 0x2000 + "Reserved rangle to 0x2600", // 0x2500 + "Divio's AAC", // 0x4143 + "Nokia adaptive multirate", // 0x4201 + "Divio's G726", // 0x4243 + "3Com NBX", // 0x7000 + "Adaptive multirate", // 0x7a21 + "AMR with silence detection", // 0x7a22 + "Comverse G723.1", // 0xa100 + "Comverse AVQSBC", // 0xa101 + "Comverse old SBC", // 0xa102 + "Symbol Technology's G729A", // 0xa103 + "Voice Age AMR WB", // 0xa104 + "Ingenient's G726", // 0xa105 + "ISO/MPEG-4 advanced audio Coding", // 0xa106 + "Encore Software Ltd's G726", // 0xa107 + "Extensible Wave format" // 0xfffe + }; - /** Strings for SMPTE formats in the Sample Chunk */ - public final static String[] SMPTE_FORMAT = - { - "No SMPTE offset", - "24 frames per second", - "25 frames per second", - "30 frames per second with frame dropping", - "30 frames per second" - }; - - /** Indices for SMPTE formats in the Sample Chunk */ - public final static int[] SMPTE_FORMAT_INDEX = - { 0, 24, 25, 29, 30 }; + public static final int[] COMPRESSION_INDEX = { + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0XA, 0xB, 0X10, 0X11, 0X12, 0X13, 0X14, 0X15, 0X16, 0X17, 0X18, + 0X19, 0X1A, 0X20, 0X21, 0X22, 0X23, 0X24, 0X25, 0X26, 0X27, 0X28, 0X30, 0X31, 0X32, 0X33, 0X34, + 0X35, 0X36, 0X37, 0X38, 0X39, 0X3A, 0X3B, 0X3C, 0X3D, 0X40, 0X41, 0X42, 0X43, 0X44, 0X45, 0X50, + 0X52, 0X53, 0X55, 0X59, 0X60, 0X61, 0X62, 0X63, 0X64, 0X65, 0X67, 0X69, 0X70, 0X71, 0X72, 0X73, + 0X74, 0X75, 0X76, 0X77, 0X78, 0X79, 0X7A, 0X7B, 0X80, 0X81, 0X82, 0X83, 0X84, 0X85, 0X86, 0X88, + 0X89, 0X8A, 0X8B, 0X8C, 0X8D, 0X91, 0X92, 0x93, 0X94, 0X97, 0X98, 0X99, 0XA0, 0XA1, 0XA2, 0XA3, + 0XA4, 0XB0, 0X100, 0x101, 0X111, 0X112, 0X120, 0X121, 0X123, 0X125, 0X130, 0X131, 0X132, 0X133, + 0X134, 0X135, 0X136, 0X140, 0X141, 0X142, 0X150, 0X151, 0x155, 0X160, 0X161, 0X162, 0x163, + 0x164, 0X170, 0X171, 0X172, 0X173, 0X174, 0X175, 0X176, 0X177, 0X178, 0X180, 0X190, 0X200, + 0X202, 0X203, 0X210, 0X220, 0X230, 0x240, 0x250, 0x251, 0X260, 0X270, 0X271, 0X272, 0X273, + 0X280, 0X281, 0x285, 0X300, 0x301, 0x302, 0x303, 0x304, 0x305, 0x306, 0x307, 0x308, 0x350, + 0x351, 0x400, 0x450, 0x680, 0x681, 0x1000, 0x1001, 0x1002, 0x1003, 0x1004, 0x1100, 0x1101, + 0x1102, 0x1103, 0x1104, 0x1400, 0x1500, 0x1971, 0X1979, 0x2000, 0x2500, 0x4143, 0x4201, 0x4243, + 0x7000, 0x7a21, 0x7a22, 0xa100, 0xa101, 0xa102, 0xa103, 0xa104, 0xa105, 0xa106, 0xa107, 0xfffe + }; - /** Flags for SoundInformation bits in the MPEG chunk, - * "1" values */ - public final static String[] SOUND_INFORMATION_1 = - { - "Non-homogeneous sound data", - "Padding bit always 0", - "Sample frequency 22.05 or 44.1 KHz", - "Free format is used" - }; + /** Strings for SMPTE formats in the Sample Chunk */ + public static final String[] SMPTE_FORMAT = { + "No SMPTE offset", + "24 frames per second", + "25 frames per second", + "30 frames per second with frame dropping", + "30 frames per second" + }; + /** Indices for SMPTE formats in the Sample Chunk */ + public static final int[] SMPTE_FORMAT_INDEX = {0, 24, 25, 29, 30}; - /** Flags for SoundInformation bits in the MPEG chunk, - * "0" values */ - public final static String[] SOUND_INFORMATION_0 = - { - "Homogeneous sound data", - "Padding bit may alternate", - "", - "No free format audio frame" - }; - - /** Flags for ancillary data definition in the MPEG chunk, - * "1" values - */ - public final static String[] ANCILLARY_DEF_1 = - { - "Energy of left channel present", - "Private byte is free for internal use" - }; + /** Flags for SoundInformation bits in the MPEG chunk, "1" values */ + public static final String[] SOUND_INFORMATION_1 = { + "Non-homogeneous sound data", + "Padding bit always 0", + "Sample frequency 22.05 or 44.1 KHz", + "Free format is used" + }; + /** Flags for SoundInformation bits in the MPEG chunk, "0" values */ + public static final String[] SOUND_INFORMATION_0 = { + "Homogeneous sound data", "Padding bit may alternate", "", "No free format audio frame" + }; - /** Flags for ancillary data definition in the MPEG chunk, - * "0" values - */ - public final static String[] ANCILLARY_DEF_0 = - { - "Energy of left channel absent", - "No private byte free for internal use" - }; + /** Flags for ancillary data definition in the MPEG chunk, "1" values */ + public static final String[] ANCILLARY_DEF_1 = { + "Energy of left channel present", "Private byte is free for internal use" + }; - /** A private constructor just to make sure nobody - instantiates the class by mistake. */ - private WaveStrings() - { - } + /** Flags for ancillary data definition in the MPEG chunk, "0" values */ + public static final String[] ANCILLARY_DEF_0 = { + "Energy of left channel absent", "No private byte free for internal use" + }; + /** A private constructor just to make sure nobody instantiates the class by mistake. */ + private WaveStrings() {} } diff --git a/jhove-modules/wave-hul/src/main/java/edu/harvard/hul/ois/jhove/module/wave/package-info.java b/jhove-modules/wave-hul/src/main/java/edu/harvard/hul/ois/jhove/module/wave/package-info.java index 78a9e652f..a4e581fc3 100644 --- a/jhove-modules/wave-hul/src/main/java/edu/harvard/hul/ois/jhove/module/wave/package-info.java +++ b/jhove-modules/wave-hul/src/main/java/edu/harvard/hul/ois/jhove/module/wave/package-info.java @@ -1,4 +1,2 @@ -/** - * Contains supporting classes for the WAVE-HUL module. - */ -package edu.harvard.hul.ois.jhove.module.wave; \ No newline at end of file +/** Contains supporting classes for the WAVE-HUL module. */ +package edu.harvard.hul.ois.jhove.module.wave; diff --git a/jhove-modules/wave-hul/src/test/java/edu/harvard/hul/ois/jhove/module/WaveModuleTest.java b/jhove-modules/wave-hul/src/test/java/edu/harvard/hul/ois/jhove/module/WaveModuleTest.java index bfe4179f6..81b1920cf 100644 --- a/jhove-modules/wave-hul/src/test/java/edu/harvard/hul/ois/jhove/module/WaveModuleTest.java +++ b/jhove-modules/wave-hul/src/test/java/edu/harvard/hul/ois/jhove/module/WaveModuleTest.java @@ -3,59 +3,60 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; +import edu.harvard.hul.ois.jhove.JhoveBase; +import edu.harvard.hul.ois.jhove.Message; +import edu.harvard.hul.ois.jhove.RepInfo; import java.io.File; import java.io.FileInputStream; import java.io.IOException; - import org.junit.Before; import org.junit.Test; -import edu.harvard.hul.ois.jhove.JhoveBase; -import edu.harvard.hul.ois.jhove.Message; -import edu.harvard.hul.ois.jhove.RepInfo; - public class WaveModuleTest { - private WaveModule module; - - @Before - public void setUp() throws Exception { - module = new WaveModule(); - JhoveBase je = new JhoveBase(); - module.setBase(je); - } - - @Test - public void testSample3() throws IOException { - File f = new File("src/test/resources/wave/sample3.wav"); - RepInfo info = new RepInfo(f.getName()); - - // Parse - module.parse(new FileInputStream(f), info, 0); - - // Check that JHOVE found this was NOT well formed: - assertEquals("Should not be well formed. ", info.getWellFormed(), - RepInfo.FALSE); - - // Check that there is a message: - assertTrue("There should be at least one message. ", info.getMessage() - .size() > 0); - - // Go though messages looking for the expected error: - boolean foundEofMessage = false; - for( Message m : info.getMessage()) { - // Use ID to be language independent - if ("WAVE-HUL-3".equals(m.getId()) - && m.getOffset() == 96) { - foundEofMessage = true; - } - System.out.println("MESSAGE: (" + m.getId() + ") " + m.getMessage() + " " - + m.getSubMessage() - + " @" + m.getOffset()); - } - // Fail if the error was not found. - assertTrue("The message of id=WAVE-HUL-3 ('Unexpected end of file')@96 was not found. ", - foundEofMessage); - } - + private WaveModule module; + + @Before + public void setUp() throws Exception { + module = new WaveModule(); + JhoveBase je = new JhoveBase(); + module.setBase(je); + } + + @Test + public void testSample3() throws IOException { + File f = new File("src/test/resources/wave/sample3.wav"); + RepInfo info = new RepInfo(f.getName()); + + // Parse + module.parse(new FileInputStream(f), info, 0); + + // Check that JHOVE found this was NOT well formed: + assertEquals("Should not be well formed. ", info.getWellFormed(), RepInfo.FALSE); + + // Check that there is a message: + assertTrue("There should be at least one message. ", info.getMessage().size() > 0); + + // Go though messages looking for the expected error: + boolean foundEofMessage = false; + for (Message m : info.getMessage()) { + // Use ID to be language independent + if ("WAVE-HUL-3".equals(m.getId()) && m.getOffset() == 96) { + foundEofMessage = true; + } + System.out.println( + "MESSAGE: (" + + m.getId() + + ") " + + m.getMessage() + + " " + + m.getSubMessage() + + " @" + + m.getOffset()); + } + // Fail if the error was not found. + assertTrue( + "The message of id=WAVE-HUL-3 ('Unexpected end of file')@96 was not found. ", + foundEofMessage); + } } diff --git a/jhove-modules/xml-hul/src/main/java/edu/harvard/hul/ois/jhove/module/XmlModule.java b/jhove-modules/xml-hul/src/main/java/edu/harvard/hul/ois/jhove/module/XmlModule.java index 55b899455..6712a7c91 100644 --- a/jhove-modules/xml-hul/src/main/java/edu/harvard/hul/ois/jhove/module/XmlModule.java +++ b/jhove-modules/xml-hul/src/main/java/edu/harvard/hul/ois/jhove/module/XmlModule.java @@ -1,40 +1,35 @@ -/********************************************************************** - * Jhove - JSTOR/Harvard Object Validation Environment - * Copyright 2004-2007 by JSTOR and the President and Fellows of Harvard College - * +/** + * ******************************************************************** Jhove - JSTOR/Harvard Object + * Validation Environment Copyright 2004-2007 by JSTOR and the President and Fellows of Harvard + * College * - * This program 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 2 of the License, or (at - * your option) any later version. + *

This program 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 2 + * of the License, or (at your option) any later version. * - * This program 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. + *

This program 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. * - * You should have received a copy of the GNU Lesser General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 - * USA - **********************************************************************/ - + *

You should have received a copy of the GNU Lesser General Public License along with this + * program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA ******************************************************************** + */ package edu.harvard.hul.ois.jhove.module; -import java.io.*; -import java.text.MessageFormat; -import java.util.*; import edu.harvard.hul.ois.jhove.*; import edu.harvard.hul.ois.jhove.messages.JhoveMessage; import edu.harvard.hul.ois.jhove.messages.JhoveMessages; import edu.harvard.hul.ois.jhove.module.utf8.Utf8BlockMarker; import edu.harvard.hul.ois.jhove.module.xml.*; - -import org.xml.sax.XMLReader; +import java.io.*; +import java.text.MessageFormat; +import java.util.*; import javax.xml.parsers.SAXParserFactory; import org.xml.sax.InputSource; import org.xml.sax.SAXException; import org.xml.sax.SAXParseException; +import org.xml.sax.XMLReader; import org.xml.sax.helpers.*; /** @@ -43,994 +38,934 @@ * @author Gary McGath */ public class XmlModule extends ModuleBase { - /****************************************************************** - * PRIVATE CLASS FIELDS. - ******************************************************************/ - - private static final String NAME = "XML-hul"; - private static final String RELEASE = "1.5.1"; - private static final int [] DATE = { 2019, 04, 17 }; - private static final String[] FORMAT = { "XML", "XHTML" }; - private static final String COVERAGE = "XML 1.0"; - /* - * According to RFC 3023, text/xml should be used for human-readable - * XML documents, and application/xml should be used for documents - * that aren't easily read by humans. Since that determination - * is beyond the scope of this project, we err on the side of - * pessimism and use application/xml as the primary MIME type. - * MIMETYPE[2] is only for XHTML. - */ - private static final String[] MIMETYPE = { "text/xml", "application/xml", - "text/html" }; - private static final String WELLFORMED = "An XML file is well-formed if " - + "it meets the criteria defined in Section 2.1 of the XML " - + "specification (W3C Recommendation, 3rd edition, 2004-02-04)"; - private static final String VALIDITY = "An XML file is valid if " - + "well-formed, and the file has an associated DTD or XML Schema and " - + "the file meets the constraints defined by that DTD or Schema"; - private static final String REPINFO = "Additional representation " - + "information includes: version, endcoding, standalone flag, DTD or " - + "schema, namespaces, notations, character references, entities, " - + "processing instructions, and comments"; - private static final String NOTE = "This module determines " - + "well-formedness and validity using the SAX2-conforming parser " - + "specified by the invoking application"; - private static final String RIGHTS = "Copyright 2004-2007 by JSTOR and " - + "the President and Fellows of Harvard College. " - + "Released under the GNU Lesser General Public License."; - - /****************************************************************** - * PRIVATE INSTANCE FIELDS. - ******************************************************************/ - - /* Top-level property list. */ - protected List _propList; - - /* Top-level property. */ - protected Property _metadata; - - /* Doctype for XHTML documents only, otherwise null. */ - protected String _xhtmlDoctype; - - /* Base URL for DTD's. If null, all DTD URL's are absolute. */ - protected String _baseURL; - - /* - * Flag to control signature checking behavior. If true, - * checkSignatures insists on an XML document declaration; if - * false, it will parse the file if there is no document - * declaration. - */ - protected boolean _sigWantsDecl; - - /* - * Flag to indicate we're invoking the parser from checkSignatures. - * When true, it's up to checkSignatures to mark a signature as present. - */ - protected boolean _parseFromSig; - - /* Flag to know if the property TextMDMetadata is to be added */ - protected boolean _withTextMD = false; - /* Hold the information needed to generate a textMD metadata fragment */ - protected TextMDMetadata _textMD; - - /* Map from URIs to locally stored schemas */ - protected Map _localSchemas; - - /****************************************************************** - * CLASS CONSTRUCTOR. - ******************************************************************/ - /** - * Instantiate an XmlModule object. - */ - public XmlModule() { - super(NAME, RELEASE, DATE, FORMAT, COVERAGE, MIMETYPE, WELLFORMED, - VALIDITY, REPINFO, NOTE, RIGHTS, false); - - _vendor = Agent.harvardInstance(); - - Document doc = new Document( - "Extensible Markup Language (XML) 1.0 " + "(Third Edition)", - DocumentType.REPORT); - doc.setPublisher(Agent.newW3CInstance()); - doc.setDate("2004-02-04"); - doc.setIdentifier(new Identifier("http://www.w3.org/TR/REC-xml", - IdentifierType.URL)); - _specification.add(doc); - - doc = new Document("SAX", DocumentType.WEB); - doc.setIdentifier(new Identifier("http://sax.sourceforge.net/", - IdentifierType.URL)); - _specification.add(doc); - - Signature sig = new ExternalSignature(".xml", SignatureType.EXTENSION, - SignatureUseType.OPTIONAL); - _signature.add(sig); - _localSchemas = new HashMap<>(); - } - - /** - * Sets the value of the doctype string, assumed to have been forced - * to upper case. This is set only when the HTML module invokes the - * XML module for an XHTML document. - */ - public void setXhtmlDoctype(String doctype) { - _xhtmlDoctype = doctype; - if (_textMD != null) { - _textMD.setMarkup_language(_xhtmlDoctype); - } - } - - /** - * Reset parameter settings. - * Returns to a default state without any parameters. - */ - @Override - public void resetParams() { - _baseURL = null; - _sigWantsDecl = false; - _parseFromSig = false; - } - - /** - * Per-action initialization. - * - * @param param - * The module parameter; under command-line Jhove, the -p - * parameter. - * If the parameter starts with "schema", then the part to the - * right of the equal sign identifies a URI with a local path - * (URI, then semicolon, then path). - * If the first character is 's' and the parameter isn't - * "schema", - * then signature checking requires - * a document declaration, and the rest of the URL is considered - * as follows. - * If the parameter begins with 'b' or 'B', then the remainder of - * the parameter is used as a base URL. Otherwise it is ignored, - * and there is no base URL. - */ - @Override - public void param(String param) { - if (param != null) { - param = param.toLowerCase(); - if (param.startsWith("schema=")) { - addLocalSchema(param); - } else if (param.indexOf('s') == 0) { - _sigWantsDecl = true; - param = param.substring(1); - } else if (param.indexOf('b') == 0) { - _baseURL = param.substring(1); - } - } - } - - /** - * Parse the content of a purported XML digital object and store the - * results in RepInfo. - * - * This is designed to be called in two passes. On the first pass, - * a nonvalidating parse is done. If this succeeds, and the presence - * of DTD's or schemas is detected, then parse returns 1 so that it - * will be called again to do a validating parse. If there is nothing - * to validate, we consider it "valid." - * - * @param stream - * An InputStream, positioned at its beginning, - * which is generated from the object to be parsed. - * If multiple calls to parse are made - * on the basis of a nonzero value being returned, - * a new InputStream must be provided each time. - * - * @param info - * A fresh (on the first call) RepInfo object - * which will be modified - * to reflect the results of the parsing - * If multiple calls to parse are made - * on the basis of a nonzero value being returned, - * the same RepInfo object should be passed with each - * call. - * - * @param parseIndex - * Must be 0 in first call to parse. If - * parse returns a nonzero value, it must be - * called again with parseIndex - * equal to that return value. - */ - @Override - public int parse(InputStream stream, RepInfo info, int parseIndex) { - // Test if textMD is to be generated - - if (_defaultParams != null) { - _withTextMD = false; - Iterator iter = _defaultParams.iterator(); - while (iter.hasNext()) { - String param = iter.next(); - if ("withtextmd=true".equalsIgnoreCase(param)) { - _withTextMD = true; - } - } - } - - boolean canValidate = true; - initParse(); - info.setFormat(_format[0]); - info.setMimeType(_mimeType[0]); - info.setModule(this); - if (_textMD == null || parseIndex == 0) { - _textMD = new TextMDMetadata(); - _xhtmlDoctype = null; - } - - // Setup the data stream, will determine if we use checksum stream - setupDataStream(stream, info); - - _propList = new LinkedList<>(); - _metadata = new Property("XMLMetadata", PropertyType.PROPERTY, - PropertyArity.LIST, _propList); - - XMLReader parser = null; - InputSource src = null; - XmlModuleHandler handler = null; - XmlLexicalHandler lexHandler = new XmlLexicalHandler(); - XmlDeclHandler declHandler = new XmlDeclHandler(); - - // The XmlDeclStream filters the characters, looking for an - // XML declaration, since there's no way to get that info - // out of SAX. - XmlDeclStream xds = new XmlDeclStream(_dstream); - try { - // Create an InputSource to feed the parser. - // If a SAX class was specified, use it, otherwise use - // the default parser. - src = new InputSource(xds); - // setSystemId may be helpful in resolving relative URI's, - // though its use is unclear. Its actual content is merely - // informative, not a part of any actual link - // src.setSystemId ("http://hul.harvard.edu/hul"); - if (_baseURL != null) { - src.setSystemId(new File(_baseURL).toURI().toURL().toString()); - } - String saxClass = _je.getSaxClass(); - if (saxClass == null) { - SAXParserFactory factory = SAXParserFactory.newInstance(); - factory.setNamespaceAware(true); - parser = factory.newSAXParser().getXMLReader(); - } else { - parser = XMLReaderFactory.createXMLReader(saxClass); - } - handler = new XmlModuleHandler(); - handler.setXhtmlFlag(_xhtmlDoctype != null); - handler.setLocalSchemas(_localSchemas); - parser.setContentHandler(handler); - parser.setErrorHandler(handler); - parser.setEntityResolver(handler); - parser.setDTDHandler(handler); - try { - parser.setProperty( - "http://xml.org/sax/properties/lexical-handler", - lexHandler); - } catch (SAXException e) { - info.setMessage(new InfoMessage(MessageConstants.XML_HUL_5)); - } - try { - parser.setProperty( - "http://xml.org/sax/properties/declaration-handler", - declHandler); - } catch (SAXException e) { - info.setMessage(new InfoMessage(MessageConstants.XML_HUL_6)); - } - - } catch (Exception f) { - info.setMessage(new ErrorMessage(f.getMessage())); - info.setWellFormed(false); // actually not the file's fault - return 0; - } - try { - // On the first pass, we parse without validation. - parser.setFeature("http://xml.org/sax/features/validation", - parseIndex != 0); - } catch (SAXException se) { - if (parseIndex != 0) { - info.setMessage(new InfoMessage(MessageConstants.XML_HUL_8)); - } - canValidate = false; - } - try { - parser.setFeature("http://xml.org/sax/features/namespaces", true); - } catch (SAXException se) { - info.setMessage(new InfoMessage(MessageConstants.XML_HUL_7)); - } - // This property for supporting schemas is a JAXP 1.2 - // recommendation, not likely to be supported widely as - // of this (February 2004) writing, and not supported in - // standard Crimson. But it looks like the way to prepare - // for schema validation in the future, and at least the - // info message will tell users why they're getting bogus - // invalid status. - - // Try 2 different ways of setting schema validation; - // it appears that no one way works for all parsers. - if (parseIndex > 0) { - try { - parser.setFeature( - "http://apache.org/xml/features/validation/schema", - true); - } catch (SAXException ee) { - try { - parser.setProperty( - "http://java.sun.com/xml/jaxp/properties/schemaLanguage", - "http://www.w3.org/2001/XMLSchema"); - } catch (SAXException e) { - info.setMessage( - new InfoMessage(MessageConstants.XML_HUL_9)); - } - } - } - try { - parser.parse(src); - } catch (FileNotFoundException ef) { - // Make this particular exception a little more user-friendly - info.setMessage(new ErrorMessage(MessageConstants.XML_HUL_10, - ef.getMessage().toString())); - info.setWellFormed(false); - return 0; - } catch (UTFDataFormatException u) { - if (handler.getSigFlag() && !_parseFromSig) { - info.setSigMatch(_name); - } - info.setMessage(new ErrorMessage(MessageConstants.XML_HUL_11)); - info.setWellFormed(false); - return 0; - } catch (IOException e) { - // We may get an IOException from trying to resolve an - // external entity. - if (handler.getSigFlag() && !_parseFromSig) { - info.setSigMatch(_name); - } - String mess = e.getClass().getName() + ": " + e.getMessage().toString(); - info.setMessage(new ErrorMessage(CoreMessageConstants.ERR_FILE_READ, mess)); - info.setWellFormed(false); - return 0; - } catch (SAXParseException e) { - // Document failed to parse. - if (handler.getSigFlag() && !_parseFromSig) { - info.setSigMatch(_name); - } - int line = e.getLineNumber(); - int col = e.getColumnNumber(); - String mess = e.getMessage().toString() + - "Line = " + line + ", Column = " + col; - info.setMessage(new ErrorMessage(MessageConstants.XML_HUL_1, mess)); - info.setWellFormed(false); - return 0; - } catch (SAXException e) { - // Other SAX error. - if (handler.getSigFlag()) { - info.setSigMatch(_name); - } - // Sometimes the message will be null and another message - // wrapped inside it. Try to report that. - JhoveMessage message = JhoveMessages.getMessageInstance( - MessageConstants.XML_HUL_3.getId(), - MessageFormat.format( - MessageConstants.XML_HUL_3.getMessage(), - e.getMessage() != null ? e.getMessage() : "")); - Throwable ee = e.getCause(); - String subMess = (ee != null) - ? MessageFormat.format( - MessageConstants.XML_HUL_12.getMessage(), - ee.getClass().getName()) - : MessageConstants.XML_HUL_13.getMessage(); - info.setMessage(new ErrorMessage(message, subMess)); - info.setWellFormed(false); - return 0; - } - - // Check if user has aborted - if (_je.getAbort()) { - return 0; - } - - if (handler.getSigFlag() && parseIndex == 0) { - info.setSigMatch(_name); - } - // If it's the first pass, check if we found a DTD - // or schema. - // If so, reparse with validation enabled. - // (Validation with schemas may prove futile, as the - // Crimson parser understands only DTD and DOCTYPE - // declarations as contributing to validity.) - String dtdURI = handler.getDTDURI(); - List schemaList = handler.getSchemas(); - - // In order to find the "primary" markup language, we try 3 things : - // 1/ first, the first NamespaceURI - // 3/ then, the first SchemaLocation - // 1/ finally, the dtd URI - // It should be noted that latter on when we look at the namespace in - // relation with the Root element - // if a URI is defined with it, it will get the preference ... - if (!schemaList.isEmpty()) { - SchemaInfo schItems = schemaList.get(0); - // First NamespaceURI - if (isNotEmpty(schItems.namespaceURI)) { - _textMD.setMarkup_language(schItems.namespaceURI); - // Then SchemaLocation - } else if (isNotEmpty(schItems.location)) { - _textMD.setMarkup_language(schItems.location); - } - } else if (isNotEmpty(dtdURI)) { - _textMD.setMarkup_language(dtdURI); - } - - if (parseIndex == 0) { - if ((handler.getDTDURI() != null || !schemaList.isEmpty()) - && canValidate) { - return 1; - } - info.setValid(RepInfo.UNDETERMINED); - // This may get downgraded to false, but won't - // be upgraded to true. - } - - // Take a deep breath. We parsed it. Now assemble the - // properties. - info.setProperty(_metadata); - - // If it's XHTML, add the HTML property. - HtmlMetadata hMetadata = handler.getHtmlMetadata(); - if (hMetadata != null) { - info.setProperty( - hMetadata.toProperty(_withTextMD ? _textMD : null)); - } - - // Report the parser in a property. - _propList.add(new Property("Parser", PropertyType.STRING, - parser.getClass().getName())); - - // Add the version property. Give precedence to XHTML doctype. - String vers = null; - if (_xhtmlDoctype != null) { - vers = DTDMapper.getXHTMLVersion(_xhtmlDoctype); - _textMD.setMarkup_language_version(vers); - } - if (vers != null) { - info.setVersion(vers); - } else { - vers = xds.getVersion(); - if (vers != null) { - info.setVersion(vers); - } - } - _textMD.setMarkup_basis_version(vers); - - // Add the encoding property. - String encoding = xds.getEncoding(); - if (encoding == null) { - // If no explicit encoding, use default (Bugzilla 136) - encoding = "UTF-8"; - } - _propList.add(new Property("Encoding", PropertyType.STRING, encoding)); - - _textMD.setCharset(encoding); - String textMDEncoding = _textMD.getCharset(); - if (textMDEncoding.indexOf("UTF") != -1) { - _textMD.setByte_order(_bigEndian ? TextMDMetadata.BYTE_ORDER_BIG - : TextMDMetadata.BYTE_ORDER_LITTLE); - _textMD.setByte_size("8"); - _textMD.setCharacter_size("variable"); - } else { - _textMD.setByte_order(_bigEndian ? TextMDMetadata.BYTE_ORDER_BIG - : TextMDMetadata.BYTE_ORDER_LITTLE); - _textMD.setByte_size("8"); - _textMD.setCharacter_size("1"); - } - // CRLF from XmlDeclStream ... - String lineEnd = xds.getKindOfLineEnd(); - if (lineEnd == null) { - info.setMessage(new InfoMessage(MessageConstants.XML_HUL_4)); - _textMD.setLinebreak(TextMDMetadata.NILL); - } else if ("CR".equalsIgnoreCase(lineEnd)) { - _textMD.setLinebreak(TextMDMetadata.LINEBREAK_CR); - } else if ("LF".equalsIgnoreCase(lineEnd)) { - _textMD.setLinebreak(TextMDMetadata.LINEBREAK_LF); - } else if ("CRLF".equalsIgnoreCase(lineEnd)) { - _textMD.setLinebreak(TextMDMetadata.LINEBREAK_CRLF); - } - - // Add the standalone property. - String sa = xds.getStandalone(); - if (sa != null) { - _propList.add(new Property("Standalone", PropertyType.STRING, sa)); - } - - // Add the DTD property. - if (dtdURI != null) { - _propList.add(new Property("DTD_URI", PropertyType.STRING, dtdURI)); - } - - if (!schemaList.isEmpty()) { - // Build a List of Properties, which will be the value - // of the Schemas Property. - List schemaPropList = new ArrayList<>(schemaList.size()); - ListIterator iter = schemaList.listIterator(); - // Iterate through all the schemas. - while (iter.hasNext()) { - SchemaInfo schItems = iter.next(); - // Build a Property (Schema) whose value is an array - // of two Properties (NamespaceURI and SchemaLocation). - Property[] schItemProps = new Property[2]; - schItemProps[0] = new Property("NamespaceURI", - PropertyType.STRING, schItems.namespaceURI); - schItemProps[1] = new Property("SchemaLocation", - PropertyType.STRING, schItems.location); - schemaPropList.add(new Property("Schema", PropertyType.PROPERTY, - PropertyArity.ARRAY, schItemProps)); - } - // Now put the list into a Property, which goes into - // the metadata. - Property prop = new Property("Schemas", PropertyType.PROPERTY, - PropertyArity.LIST, schemaPropList); - _propList.add(prop); - } - - // Add the root element. - String root = handler.getRoot(); - String rootPrefix = null; - if (root != null) { - _propList.add(new Property("Root", PropertyType.STRING, root)); - if ("html".equals(root)) { - // Specify format as XHTML - info.setFormat(_format[1]); - // Set the version according to the doctype... how? - - } - // Get the prefix of root - int indexOfColon = root.indexOf(':'); - if (indexOfColon != -1) { - rootPrefix = root.substring(0, indexOfColon); - } - } - if (rootPrefix == null) { - rootPrefix = ""; - } - - // Declare properties we're going to add. They have - // some odd interdependencies, so we create them all - // and them add them in the right (specified) order. - Property namespaceProp = null; - Property notationsProp = null; - Property charRefsProp = null; - Property entitiesProp = null; - Property procInstProp = null; - Property commentProp = null; - Property unicodeBlocksProp = null; - - Map ns = handler.getNamespaces(); - if (!ns.isEmpty()) { - Set keys = ns.keySet(); - List nsList = new ArrayList<>(keys.size()); - Iterator iter = keys.iterator(); - while (iter.hasNext()) { - String key = iter.next(); - String val = ns.get(key); - Property[] supPropArr = new Property[2]; - supPropArr[0] = new Property("Prefix", PropertyType.STRING, - key); - supPropArr[1] = new Property("URI", PropertyType.STRING, val); - Property onens = new Property("Namespace", - PropertyType.PROPERTY, PropertyArity.ARRAY, supPropArr); - nsList.add(onens); - - // Try to find the namespace URI of root - if (rootPrefix.equalsIgnoreCase(key) && isNotEmpty(val)) { - _textMD.setMarkup_language(val); - } - } - namespaceProp = new Property("Namespaces", PropertyType.PROPERTY, - PropertyArity.LIST, nsList); - } - - // CharacterReferences property goes here. - // Report as a list of 4-digit hexadecimal strings, - // e.g., 003C, 04AA, etc. - // Also build the Unicode blocks here. - List refs = xds.getCharacterReferences(); - if (!refs.isEmpty()) { - Utf8BlockMarker utf8BM = new Utf8BlockMarker(); - List refList = new ArrayList<>(refs.size()); - ListIterator iter = refs.listIterator(); - while (iter.hasNext()) { - Integer refi = iter.next(); - int refint = refi.intValue(); - refList.add(intTo4DigitHex(refint)); - utf8BM.markBlock(refint); - } - charRefsProp = new Property("CharacterReferences", - PropertyType.STRING, PropertyArity.LIST, refList); - unicodeBlocksProp = utf8BM - .getBlocksUsedProperty("UnicodeCharRefBlocks"); - } - - // Entities property - // External unparsed entities - Set entNames = lexHandler.getEntityNames(); - Set attributeVals = handler.getAttributeValues(); - List entProps = new LinkedList<>(); - List uent = handler.getUnparsedEntities(); - List unparsedNotationNames = new LinkedList<>(); - if (!uent.isEmpty()) { - ListIterator iter = uent.listIterator(); - while (iter.hasNext()) { - // We check external parsed entities against - // the list of attribute values which we've - // accumulated. If a parsed entity name matches an - // attribute value, we assume it's used. - String[] entarr = iter.next(); - String name = entarr[0]; - if (nameInCollection(name, attributeVals)) { - // Add the notation name to the list - // unparsedNotationNames, so we can use it - // in determining which notations are used. - unparsedNotationNames.add(entarr[3]); - List subPropList = new ArrayList<>(6); - subPropList.add( - new Property("Name", PropertyType.STRING, name)); - subPropList.add(new Property("Type", PropertyType.STRING, - "External unparsed")); - subPropList.add(new Property("PublicID", - PropertyType.STRING, entarr[1])); - subPropList.add(new Property("SystemID", - PropertyType.STRING, entarr[2])); - subPropList.add(new Property("NotationName", - PropertyType.STRING, entarr[3])); - - entProps.add(new Property("Entity", PropertyType.PROPERTY, - PropertyArity.LIST, subPropList)); - } - } - } - - // Internal entities - List declEnts = declHandler.getInternalEntityDeclarations(); - if (!declEnts.isEmpty()) { - ListIterator iter = declEnts.listIterator(); - while (iter.hasNext()) { - String[] entarr = iter.next(); - String name = entarr[0]; - // include only if the entity was actually used - if (nameInCollection(name, entNames)) { - List subPropList = new ArrayList<>(4); - subPropList.add( - new Property("Name", PropertyType.STRING, name)); - subPropList.add(new Property("Type", PropertyType.STRING, - "Internal")); - subPropList.add(new Property("Value", PropertyType.STRING, - entarr[1])); - entProps.add(new Property("Entity", PropertyType.PROPERTY, - PropertyArity.LIST, subPropList)); - } - } - } - - // External parsed entities - declEnts = declHandler.getExternalEntityDeclarations(); - if (!declEnts.isEmpty()) { - ListIterator iter = declEnts.listIterator(); - while (iter.hasNext()) { - String[] entarr = iter.next(); - String name = entarr[0]; - // include only if the entity was actually used - if (nameInCollection(name, entNames)) { - List subPropList = new ArrayList<>(4); - subPropList.add( - new Property("Name", PropertyType.STRING, name)); - subPropList.add(new Property("Type", PropertyType.STRING, - "External parsed")); - if (entarr[1] != null) { - subPropList.add(new Property("PublicID", - PropertyType.STRING, entarr[1])); - } - if (entarr[2] != null) { - subPropList.add(new Property("SystemID", - PropertyType.STRING, entarr[2])); - } - - entProps.add(new Property("Entity", PropertyType.PROPERTY, - PropertyArity.LIST, subPropList)); - } - } - } - - if (!entProps.isEmpty()) { - entitiesProp = new Property("Entities", PropertyType.PROPERTY, - PropertyArity.LIST, entProps); - } - - List pi = handler - .getProcessingInstructions(); - List piTargets = new LinkedList<>(); - if (!pi.isEmpty()) { - // Build a property, which consists of a list - // of properties, each of which is an array of - // two String properties, named Target and - // Data respectively. - List piPropList = new ArrayList<>(pi.size()); - ListIterator pii = pi.listIterator(); - while (pii.hasNext()) { - ProcessingInstructionInfo pistr = pii.next(); - Property[] subPropArr = new Property[2]; - // Accumulate targets in a list, so we can tell - // which Notations use them. - // Wait a minute -- what we're doing here can't work!! TODO - // what's supposed to be happening? - // piTargets.add (subPropArr[0]); - subPropArr[0] = new Property("Target", PropertyType.STRING, - pistr.target); - subPropArr[1] = new Property("Data", PropertyType.STRING, - pistr.data); - piPropList.add(new Property("ProcessingInstruction", - PropertyType.PROPERTY, PropertyArity.ARRAY, - subPropArr)); - } - procInstProp = new Property("ProcessingInstructions", - PropertyType.PROPERTY, PropertyArity.LIST, piPropList); - } - - // Notations property. We list notations only if they're - // "actually used," meaning that they designate either - // the target of a processing instruction or the ndata - // of an unparsed entry which is itself "actually used." - List notations = handler.getNotations(); - if (!notations.isEmpty()) { - List notProps = new ArrayList<>(notations.size()); - ListIterator iter = notations.listIterator(); - List subPropList = new ArrayList<>(3); - while (iter.hasNext()) { - String[] notArray = iter.next(); - String notName = notArray[0]; - // Check for use of Notation before including - // TODO this is implemented wrong! Need to reinvestigate - if (nameInCollection(notName, piTargets) - || nameInCollection(notName, unparsedNotationNames)) { - // notArray has name, public ID, system ID - subPropList.add( - new Property("Name", PropertyType.STRING, notName)); - if (notArray[1] != null) { - subPropList.add(new Property("PublicID", - PropertyType.STRING, notArray[1])); - } - if (notArray[2] != null) { - subPropList.add(new Property("SystemID", - PropertyType.STRING, notArray[2])); - } - notProps.add(new Property("Notation", PropertyType.PROPERTY, - PropertyArity.LIST, subPropList)); - } - } - // Recheck emptiness in case only unprocessed notations were found - if (!notProps.isEmpty()) { - notationsProp = new Property("Notations", PropertyType.PROPERTY, - PropertyArity.LIST, notProps); - } - } - - // Now add all the properties we created. - if (namespaceProp != null) { - _propList.add(namespaceProp); - } - if (notationsProp != null) { - _propList.add(notationsProp); - } - if (charRefsProp != null) { - _propList.add(charRefsProp); - } - if (unicodeBlocksProp != null) { - _propList.add(unicodeBlocksProp); - } - if (entitiesProp != null) { - _propList.add(entitiesProp); - } - if (procInstProp != null) { - _propList.add(procInstProp); - } - - List comm = lexHandler.getComments(); - if (!comm.isEmpty()) { - commentProp = new Property("Comments", PropertyType.STRING, - PropertyArity.LIST, comm); - } - if (commentProp != null) { - _propList.add(commentProp); - } - - // Check if parse detected invalid XML - if (!handler.isValid()) { - info.setValid(false); - } - - if (info.getWellFormed() == RepInfo.TRUE) { - if (_xhtmlDoctype != null) { - info.setMimeType(_mimeType[2]); - } else { - info.setMimeType(_mimeType[0]); - } - } - - // Add any messages from the parse. - List msgs = handler.getMessages(); - ListIterator msgi = msgs.listIterator(); - while (msgi.hasNext()) { - info.setMessage((Message) msgi.next()); - } - - if (_withTextMD) { - _textMD.setMarkup_basis(info.getFormat()); - _textMD.setMarkup_basis_version(info.getVersion()); - Property property = new Property("TextMDMetadata", - PropertyType.TEXTMDMETADATA, PropertyArity.SCALAR, _textMD); - _propList.add(property); - } - - // Set the checksums in the report if they're calculated - setChecksums(this._ckSummer, info); - - if (info.getVersion() == null) { - info.setVersion("1.0"); - _textMD.setMarkup_basis_version("1.0"); - } - return 0; - } - - /** - * Check if the digital object conforms to this Module's - * internal signature information. - * - * XML is a particularly messy case; in general, there's no - * even moderately good way to check "signatures" without parsing - * the whole file, since the document declaration is optional. - * We provide the user two choices, based on the "s" parameter. - * If 's' is the first character of the module parameter, then - * we look for an XML document declaration, and say there's no - * signature if it's missing. (This can reject well-formed - * XML files, though not valid ones.) Otherwise, if there's no - * document declaration, we parse the whole file. - * - * @param file - * A File object for the object being parsed - * @param stream - * An InputStream, positioned at its beginning, - * which is generated from the object to be parsed - * @param info - * A fresh RepInfo object which will be modified - * to reflect the results of the test - */ - @Override - public void checkSignatures(File file, InputStream stream, RepInfo info) - throws IOException { - _parseFromSig = false; - info.setFormat(_format[0]); - info.setMimeType(_mimeType[0]); - info.setModule(this); - String sigStr = "= sigStr.length()) { - info.setSigMatch(_name); - return; // sig matches - } - } else - break; - } - } catch (IOException e) { - info.setWellFormed(false); - return; - } - if (_sigWantsDecl) { - - // No XML declaration, and it's manadatory according to the param. - info.setWellFormed(false); - return; - } - - // No XML signature, but we're allowed to parse the file now. - // This means rewinding back to the start of the file. - int parseIndex = 1; - _parseFromSig = true; // we set the sig match ourselves - while (parseIndex != 0) { - stream.close(); - stream = new FileInputStream(file); - parseIndex = parse(stream, info, parseIndex); - } - if (info.getWellFormed() == RepInfo.TRUE) { - info.setSigMatch(_name); - } - } - - @Override - protected void initParse() { - super.initParse(); - // if (_defaultParams != null) { - // Iterator iter = _defaultParams.iterator (); - // while (iter.hasNext ()) { - // String param = iter.next (); - // if (param.toLowerCase ().startsWith("localschema=")) { - // addLocalSchema(param); - // } - // } - // } - } - - /* Checks if a String is .equals to any member of a Set of strings. */ - protected static boolean nameInCollection(String name, - Collection coll) { - Iterator iter = coll.iterator(); - while (iter.hasNext()) { - String s = iter.next(); - if (name.equals(s)) { - return true; - } - } - return false; - } - - /* - * Converts an int to a 4-digit hex value, e.g., - * 003F or F10A. This is used for Character References. - */ - protected static String intTo4DigitHex(int n) { - StringBuffer buf = new StringBuffer(4); - for (int i = 3; i >= 0; i--) { - int d = (n >> (4 * i)) & 0XF; // extract a nybble - if (d < 10) { - buf.append((char) ('0' + d)); - } else { - buf.append((char) ('A' + (d - 10))); - } - } - return buf.toString(); - } - - /** - * Verification that the string contains something usefull. - * - * @param value - * string to test - * @return boolean - */ - protected static boolean isNotEmpty(String value) { - return ((value != null) && (value.length() != 0) - && !("[None]".equals(value))); - } - - /** - * Add a mapping from a schema URI to a local file. - * The parameter is of the form schema=[URI];[path] - */ - private void addLocalSchema(String param) { - int eq = param.indexOf('='); - int semi = param.indexOf(';'); - try { - String uri = param.substring(eq + 1, semi).trim(); - String path = param.substring(semi + 1).trim(); - File f = new File(path); - if (f.exists()) { - _localSchemas.put(uri, f); - } - } catch (Exception e) { - } - } + /** + * **************************************************************** PRIVATE CLASS FIELDS. + * **************************************************************** + */ + private static final String NAME = "XML-hul"; + + private static final String RELEASE = "1.5.1"; + private static final int[] DATE = {2019, 04, 17}; + private static final String[] FORMAT = {"XML", "XHTML"}; + private static final String COVERAGE = "XML 1.0"; + /* + * According to RFC 3023, text/xml should be used for human-readable + * XML documents, and application/xml should be used for documents + * that aren't easily read by humans. Since that determination + * is beyond the scope of this project, we err on the side of + * pessimism and use application/xml as the primary MIME type. + * MIMETYPE[2] is only for XHTML. + */ + private static final String[] MIMETYPE = {"text/xml", "application/xml", "text/html"}; + private static final String WELLFORMED = + "An XML file is well-formed if " + + "it meets the criteria defined in Section 2.1 of the XML " + + "specification (W3C Recommendation, 3rd edition, 2004-02-04)"; + private static final String VALIDITY = + "An XML file is valid if " + + "well-formed, and the file has an associated DTD or XML Schema and " + + "the file meets the constraints defined by that DTD or Schema"; + private static final String REPINFO = + "Additional representation " + + "information includes: version, endcoding, standalone flag, DTD or " + + "schema, namespaces, notations, character references, entities, " + + "processing instructions, and comments"; + private static final String NOTE = + "This module determines " + + "well-formedness and validity using the SAX2-conforming parser " + + "specified by the invoking application"; + private static final String RIGHTS = + "Copyright 2004-2007 by JSTOR and " + + "the President and Fellows of Harvard College. " + + "Released under the GNU Lesser General Public License."; + + /** + * **************************************************************** PRIVATE INSTANCE FIELDS. + * **************************************************************** + */ + + /* Top-level property list. */ + protected List _propList; + + /* Top-level property. */ + protected Property _metadata; + + /* Doctype for XHTML documents only, otherwise null. */ + protected String _xhtmlDoctype; + + /* Base URL for DTD's. If null, all DTD URL's are absolute. */ + protected String _baseURL; + + /* + * Flag to control signature checking behavior. If true, + * checkSignatures insists on an XML document declaration; if + * false, it will parse the file if there is no document + * declaration. + */ + protected boolean _sigWantsDecl; + + /* + * Flag to indicate we're invoking the parser from checkSignatures. + * When true, it's up to checkSignatures to mark a signature as present. + */ + protected boolean _parseFromSig; + + /* Flag to know if the property TextMDMetadata is to be added */ + protected boolean _withTextMD = false; + /* Hold the information needed to generate a textMD metadata fragment */ + protected TextMDMetadata _textMD; + + /* Map from URIs to locally stored schemas */ + protected Map _localSchemas; + + /** + * **************************************************************** CLASS CONSTRUCTOR. + * **************************************************************** + */ + /** Instantiate an XmlModule object. */ + public XmlModule() { + super( + NAME, + RELEASE, + DATE, + FORMAT, + COVERAGE, + MIMETYPE, + WELLFORMED, + VALIDITY, + REPINFO, + NOTE, + RIGHTS, + false); + + _vendor = Agent.harvardInstance(); + + Document doc = + new Document( + "Extensible Markup Language (XML) 1.0 " + "(Third Edition)", DocumentType.REPORT); + doc.setPublisher(Agent.newW3CInstance()); + doc.setDate("2004-02-04"); + doc.setIdentifier(new Identifier("http://www.w3.org/TR/REC-xml", IdentifierType.URL)); + _specification.add(doc); + + doc = new Document("SAX", DocumentType.WEB); + doc.setIdentifier(new Identifier("http://sax.sourceforge.net/", IdentifierType.URL)); + _specification.add(doc); + + Signature sig = + new ExternalSignature(".xml", SignatureType.EXTENSION, SignatureUseType.OPTIONAL); + _signature.add(sig); + _localSchemas = new HashMap<>(); + } + + /** + * Sets the value of the doctype string, assumed to have been forced to upper case. This is set + * only when the HTML module invokes the XML module for an XHTML document. + */ + public void setXhtmlDoctype(String doctype) { + _xhtmlDoctype = doctype; + if (_textMD != null) { + _textMD.setMarkup_language(_xhtmlDoctype); + } + } + + /** Reset parameter settings. Returns to a default state without any parameters. */ + @Override + public void resetParams() { + _baseURL = null; + _sigWantsDecl = false; + _parseFromSig = false; + } + + /** + * Per-action initialization. + * + * @param param The module parameter; under command-line Jhove, the -p parameter. If the parameter + * starts with "schema", then the part to the right of the equal sign identifies a URI with a + * local path (URI, then semicolon, then path). If the first character is 's' and the + * parameter isn't "schema", then signature checking requires a document declaration, and the + * rest of the URL is considered as follows. If the parameter begins with 'b' or 'B', then the + * remainder of the parameter is used as a base URL. Otherwise it is ignored, and there is no + * base URL. + */ + @Override + public void param(String param) { + if (param != null) { + param = param.toLowerCase(); + if (param.startsWith("schema=")) { + addLocalSchema(param); + } else if (param.indexOf('s') == 0) { + _sigWantsDecl = true; + param = param.substring(1); + } else if (param.indexOf('b') == 0) { + _baseURL = param.substring(1); + } + } + } + + /** + * Parse the content of a purported XML digital object and store the results in RepInfo. + * + *

This is designed to be called in two passes. On the first pass, a nonvalidating parse is + * done. If this succeeds, and the presence of DTD's or schemas is detected, then parse returns 1 + * so that it will be called again to do a validating parse. If there is nothing to validate, we + * consider it "valid." + * + * @param stream An InputStream, positioned at its beginning, which is generated from the object + * to be parsed. If multiple calls to parse are made on the basis of a nonzero + * value being returned, a new InputStream must be provided each time. + * @param info A fresh (on the first call) RepInfo object which will be modified to reflect the + * results of the parsing If multiple calls to parse are made on the basis of a + * nonzero value being returned, the same RepInfo object should be passed with each call. + * @param parseIndex Must be 0 in first call to parse. If parse returns + * a nonzero value, it must be called again with parseIndex equal to that return + * value. + */ + @Override + public int parse(InputStream stream, RepInfo info, int parseIndex) { + // Test if textMD is to be generated + + if (_defaultParams != null) { + _withTextMD = false; + Iterator iter = _defaultParams.iterator(); + while (iter.hasNext()) { + String param = iter.next(); + if ("withtextmd=true".equalsIgnoreCase(param)) { + _withTextMD = true; + } + } + } + + boolean canValidate = true; + initParse(); + info.setFormat(_format[0]); + info.setMimeType(_mimeType[0]); + info.setModule(this); + if (_textMD == null || parseIndex == 0) { + _textMD = new TextMDMetadata(); + _xhtmlDoctype = null; + } + + // Setup the data stream, will determine if we use checksum stream + setupDataStream(stream, info); + + _propList = new LinkedList<>(); + _metadata = new Property("XMLMetadata", PropertyType.PROPERTY, PropertyArity.LIST, _propList); + + XMLReader parser = null; + InputSource src = null; + XmlModuleHandler handler = null; + XmlLexicalHandler lexHandler = new XmlLexicalHandler(); + XmlDeclHandler declHandler = new XmlDeclHandler(); + + // The XmlDeclStream filters the characters, looking for an + // XML declaration, since there's no way to get that info + // out of SAX. + XmlDeclStream xds = new XmlDeclStream(_dstream); + try { + // Create an InputSource to feed the parser. + // If a SAX class was specified, use it, otherwise use + // the default parser. + src = new InputSource(xds); + // setSystemId may be helpful in resolving relative URI's, + // though its use is unclear. Its actual content is merely + // informative, not a part of any actual link + // src.setSystemId ("http://hul.harvard.edu/hul"); + if (_baseURL != null) { + src.setSystemId(new File(_baseURL).toURI().toURL().toString()); + } + String saxClass = _je.getSaxClass(); + if (saxClass == null) { + SAXParserFactory factory = SAXParserFactory.newInstance(); + factory.setNamespaceAware(true); + parser = factory.newSAXParser().getXMLReader(); + } else { + parser = XMLReaderFactory.createXMLReader(saxClass); + } + handler = new XmlModuleHandler(); + handler.setXhtmlFlag(_xhtmlDoctype != null); + handler.setLocalSchemas(_localSchemas); + parser.setContentHandler(handler); + parser.setErrorHandler(handler); + parser.setEntityResolver(handler); + parser.setDTDHandler(handler); + try { + parser.setProperty("http://xml.org/sax/properties/lexical-handler", lexHandler); + } catch (SAXException e) { + info.setMessage(new InfoMessage(MessageConstants.XML_HUL_5)); + } + try { + parser.setProperty("http://xml.org/sax/properties/declaration-handler", declHandler); + } catch (SAXException e) { + info.setMessage(new InfoMessage(MessageConstants.XML_HUL_6)); + } + + } catch (Exception f) { + info.setMessage(new ErrorMessage(f.getMessage())); + info.setWellFormed(false); // actually not the file's fault + return 0; + } + try { + // On the first pass, we parse without validation. + parser.setFeature("http://xml.org/sax/features/validation", parseIndex != 0); + } catch (SAXException se) { + if (parseIndex != 0) { + info.setMessage(new InfoMessage(MessageConstants.XML_HUL_8)); + } + canValidate = false; + } + try { + parser.setFeature("http://xml.org/sax/features/namespaces", true); + } catch (SAXException se) { + info.setMessage(new InfoMessage(MessageConstants.XML_HUL_7)); + } + // This property for supporting schemas is a JAXP 1.2 + // recommendation, not likely to be supported widely as + // of this (February 2004) writing, and not supported in + // standard Crimson. But it looks like the way to prepare + // for schema validation in the future, and at least the + // info message will tell users why they're getting bogus + // invalid status. + + // Try 2 different ways of setting schema validation; + // it appears that no one way works for all parsers. + if (parseIndex > 0) { + try { + parser.setFeature("http://apache.org/xml/features/validation/schema", true); + } catch (SAXException ee) { + try { + parser.setProperty( + "http://java.sun.com/xml/jaxp/properties/schemaLanguage", + "http://www.w3.org/2001/XMLSchema"); + } catch (SAXException e) { + info.setMessage(new InfoMessage(MessageConstants.XML_HUL_9)); + } + } + } + try { + parser.parse(src); + } catch (FileNotFoundException ef) { + // Make this particular exception a little more user-friendly + info.setMessage(new ErrorMessage(MessageConstants.XML_HUL_10, ef.getMessage().toString())); + info.setWellFormed(false); + return 0; + } catch (UTFDataFormatException u) { + if (handler.getSigFlag() && !_parseFromSig) { + info.setSigMatch(_name); + } + info.setMessage(new ErrorMessage(MessageConstants.XML_HUL_11)); + info.setWellFormed(false); + return 0; + } catch (IOException e) { + // We may get an IOException from trying to resolve an + // external entity. + if (handler.getSigFlag() && !_parseFromSig) { + info.setSigMatch(_name); + } + String mess = e.getClass().getName() + ": " + e.getMessage().toString(); + info.setMessage(new ErrorMessage(CoreMessageConstants.ERR_FILE_READ, mess)); + info.setWellFormed(false); + return 0; + } catch (SAXParseException e) { + // Document failed to parse. + if (handler.getSigFlag() && !_parseFromSig) { + info.setSigMatch(_name); + } + int line = e.getLineNumber(); + int col = e.getColumnNumber(); + String mess = e.getMessage().toString() + "Line = " + line + ", Column = " + col; + info.setMessage(new ErrorMessage(MessageConstants.XML_HUL_1, mess)); + info.setWellFormed(false); + return 0; + } catch (SAXException e) { + // Other SAX error. + if (handler.getSigFlag()) { + info.setSigMatch(_name); + } + // Sometimes the message will be null and another message + // wrapped inside it. Try to report that. + JhoveMessage message = + JhoveMessages.getMessageInstance( + MessageConstants.XML_HUL_3.getId(), + MessageFormat.format( + MessageConstants.XML_HUL_3.getMessage(), + e.getMessage() != null ? e.getMessage() : "")); + Throwable ee = e.getCause(); + String subMess = + (ee != null) + ? MessageFormat.format( + MessageConstants.XML_HUL_12.getMessage(), ee.getClass().getName()) + : MessageConstants.XML_HUL_13.getMessage(); + info.setMessage(new ErrorMessage(message, subMess)); + info.setWellFormed(false); + return 0; + } + + // Check if user has aborted + if (_je.getAbort()) { + return 0; + } + + if (handler.getSigFlag() && parseIndex == 0) { + info.setSigMatch(_name); + } + // If it's the first pass, check if we found a DTD + // or schema. + // If so, reparse with validation enabled. + // (Validation with schemas may prove futile, as the + // Crimson parser understands only DTD and DOCTYPE + // declarations as contributing to validity.) + String dtdURI = handler.getDTDURI(); + List schemaList = handler.getSchemas(); + + // In order to find the "primary" markup language, we try 3 things : + // 1/ first, the first NamespaceURI + // 3/ then, the first SchemaLocation + // 1/ finally, the dtd URI + // It should be noted that latter on when we look at the namespace in + // relation with the Root element + // if a URI is defined with it, it will get the preference ... + if (!schemaList.isEmpty()) { + SchemaInfo schItems = schemaList.get(0); + // First NamespaceURI + if (isNotEmpty(schItems.namespaceURI)) { + _textMD.setMarkup_language(schItems.namespaceURI); + // Then SchemaLocation + } else if (isNotEmpty(schItems.location)) { + _textMD.setMarkup_language(schItems.location); + } + } else if (isNotEmpty(dtdURI)) { + _textMD.setMarkup_language(dtdURI); + } + + if (parseIndex == 0) { + if ((handler.getDTDURI() != null || !schemaList.isEmpty()) && canValidate) { + return 1; + } + info.setValid(RepInfo.UNDETERMINED); + // This may get downgraded to false, but won't + // be upgraded to true. + } + + // Take a deep breath. We parsed it. Now assemble the + // properties. + info.setProperty(_metadata); + + // If it's XHTML, add the HTML property. + HtmlMetadata hMetadata = handler.getHtmlMetadata(); + if (hMetadata != null) { + info.setProperty(hMetadata.toProperty(_withTextMD ? _textMD : null)); + } + + // Report the parser in a property. + _propList.add(new Property("Parser", PropertyType.STRING, parser.getClass().getName())); + + // Add the version property. Give precedence to XHTML doctype. + String vers = null; + if (_xhtmlDoctype != null) { + vers = DTDMapper.getXHTMLVersion(_xhtmlDoctype); + _textMD.setMarkup_language_version(vers); + } + if (vers != null) { + info.setVersion(vers); + } else { + vers = xds.getVersion(); + if (vers != null) { + info.setVersion(vers); + } + } + _textMD.setMarkup_basis_version(vers); + + // Add the encoding property. + String encoding = xds.getEncoding(); + if (encoding == null) { + // If no explicit encoding, use default (Bugzilla 136) + encoding = "UTF-8"; + } + _propList.add(new Property("Encoding", PropertyType.STRING, encoding)); + + _textMD.setCharset(encoding); + String textMDEncoding = _textMD.getCharset(); + if (textMDEncoding.indexOf("UTF") != -1) { + _textMD.setByte_order( + _bigEndian ? TextMDMetadata.BYTE_ORDER_BIG : TextMDMetadata.BYTE_ORDER_LITTLE); + _textMD.setByte_size("8"); + _textMD.setCharacter_size("variable"); + } else { + _textMD.setByte_order( + _bigEndian ? TextMDMetadata.BYTE_ORDER_BIG : TextMDMetadata.BYTE_ORDER_LITTLE); + _textMD.setByte_size("8"); + _textMD.setCharacter_size("1"); + } + // CRLF from XmlDeclStream ... + String lineEnd = xds.getKindOfLineEnd(); + if (lineEnd == null) { + info.setMessage(new InfoMessage(MessageConstants.XML_HUL_4)); + _textMD.setLinebreak(TextMDMetadata.NILL); + } else if ("CR".equalsIgnoreCase(lineEnd)) { + _textMD.setLinebreak(TextMDMetadata.LINEBREAK_CR); + } else if ("LF".equalsIgnoreCase(lineEnd)) { + _textMD.setLinebreak(TextMDMetadata.LINEBREAK_LF); + } else if ("CRLF".equalsIgnoreCase(lineEnd)) { + _textMD.setLinebreak(TextMDMetadata.LINEBREAK_CRLF); + } + + // Add the standalone property. + String sa = xds.getStandalone(); + if (sa != null) { + _propList.add(new Property("Standalone", PropertyType.STRING, sa)); + } + + // Add the DTD property. + if (dtdURI != null) { + _propList.add(new Property("DTD_URI", PropertyType.STRING, dtdURI)); + } + + if (!schemaList.isEmpty()) { + // Build a List of Properties, which will be the value + // of the Schemas Property. + List schemaPropList = new ArrayList<>(schemaList.size()); + ListIterator iter = schemaList.listIterator(); + // Iterate through all the schemas. + while (iter.hasNext()) { + SchemaInfo schItems = iter.next(); + // Build a Property (Schema) whose value is an array + // of two Properties (NamespaceURI and SchemaLocation). + Property[] schItemProps = new Property[2]; + schItemProps[0] = new Property("NamespaceURI", PropertyType.STRING, schItems.namespaceURI); + schItemProps[1] = new Property("SchemaLocation", PropertyType.STRING, schItems.location); + schemaPropList.add( + new Property("Schema", PropertyType.PROPERTY, PropertyArity.ARRAY, schItemProps)); + } + // Now put the list into a Property, which goes into + // the metadata. + Property prop = + new Property("Schemas", PropertyType.PROPERTY, PropertyArity.LIST, schemaPropList); + _propList.add(prop); + } + + // Add the root element. + String root = handler.getRoot(); + String rootPrefix = null; + if (root != null) { + _propList.add(new Property("Root", PropertyType.STRING, root)); + if ("html".equals(root)) { + // Specify format as XHTML + info.setFormat(_format[1]); + // Set the version according to the doctype... how? + + } + // Get the prefix of root + int indexOfColon = root.indexOf(':'); + if (indexOfColon != -1) { + rootPrefix = root.substring(0, indexOfColon); + } + } + if (rootPrefix == null) { + rootPrefix = ""; + } + + // Declare properties we're going to add. They have + // some odd interdependencies, so we create them all + // and them add them in the right (specified) order. + Property namespaceProp = null; + Property notationsProp = null; + Property charRefsProp = null; + Property entitiesProp = null; + Property procInstProp = null; + Property commentProp = null; + Property unicodeBlocksProp = null; + + Map ns = handler.getNamespaces(); + if (!ns.isEmpty()) { + Set keys = ns.keySet(); + List nsList = new ArrayList<>(keys.size()); + Iterator iter = keys.iterator(); + while (iter.hasNext()) { + String key = iter.next(); + String val = ns.get(key); + Property[] supPropArr = new Property[2]; + supPropArr[0] = new Property("Prefix", PropertyType.STRING, key); + supPropArr[1] = new Property("URI", PropertyType.STRING, val); + Property onens = + new Property("Namespace", PropertyType.PROPERTY, PropertyArity.ARRAY, supPropArr); + nsList.add(onens); + + // Try to find the namespace URI of root + if (rootPrefix.equalsIgnoreCase(key) && isNotEmpty(val)) { + _textMD.setMarkup_language(val); + } + } + namespaceProp = new Property("Namespaces", PropertyType.PROPERTY, PropertyArity.LIST, nsList); + } + + // CharacterReferences property goes here. + // Report as a list of 4-digit hexadecimal strings, + // e.g., 003C, 04AA, etc. + // Also build the Unicode blocks here. + List refs = xds.getCharacterReferences(); + if (!refs.isEmpty()) { + Utf8BlockMarker utf8BM = new Utf8BlockMarker(); + List refList = new ArrayList<>(refs.size()); + ListIterator iter = refs.listIterator(); + while (iter.hasNext()) { + Integer refi = iter.next(); + int refint = refi.intValue(); + refList.add(intTo4DigitHex(refint)); + utf8BM.markBlock(refint); + } + charRefsProp = + new Property("CharacterReferences", PropertyType.STRING, PropertyArity.LIST, refList); + unicodeBlocksProp = utf8BM.getBlocksUsedProperty("UnicodeCharRefBlocks"); + } + + // Entities property + // External unparsed entities + Set entNames = lexHandler.getEntityNames(); + Set attributeVals = handler.getAttributeValues(); + List entProps = new LinkedList<>(); + List uent = handler.getUnparsedEntities(); + List unparsedNotationNames = new LinkedList<>(); + if (!uent.isEmpty()) { + ListIterator iter = uent.listIterator(); + while (iter.hasNext()) { + // We check external parsed entities against + // the list of attribute values which we've + // accumulated. If a parsed entity name matches an + // attribute value, we assume it's used. + String[] entarr = iter.next(); + String name = entarr[0]; + if (nameInCollection(name, attributeVals)) { + // Add the notation name to the list + // unparsedNotationNames, so we can use it + // in determining which notations are used. + unparsedNotationNames.add(entarr[3]); + List subPropList = new ArrayList<>(6); + subPropList.add(new Property("Name", PropertyType.STRING, name)); + subPropList.add(new Property("Type", PropertyType.STRING, "External unparsed")); + subPropList.add(new Property("PublicID", PropertyType.STRING, entarr[1])); + subPropList.add(new Property("SystemID", PropertyType.STRING, entarr[2])); + subPropList.add(new Property("NotationName", PropertyType.STRING, entarr[3])); + + entProps.add( + new Property("Entity", PropertyType.PROPERTY, PropertyArity.LIST, subPropList)); + } + } + } + + // Internal entities + List declEnts = declHandler.getInternalEntityDeclarations(); + if (!declEnts.isEmpty()) { + ListIterator iter = declEnts.listIterator(); + while (iter.hasNext()) { + String[] entarr = iter.next(); + String name = entarr[0]; + // include only if the entity was actually used + if (nameInCollection(name, entNames)) { + List subPropList = new ArrayList<>(4); + subPropList.add(new Property("Name", PropertyType.STRING, name)); + subPropList.add(new Property("Type", PropertyType.STRING, "Internal")); + subPropList.add(new Property("Value", PropertyType.STRING, entarr[1])); + entProps.add( + new Property("Entity", PropertyType.PROPERTY, PropertyArity.LIST, subPropList)); + } + } + } + + // External parsed entities + declEnts = declHandler.getExternalEntityDeclarations(); + if (!declEnts.isEmpty()) { + ListIterator iter = declEnts.listIterator(); + while (iter.hasNext()) { + String[] entarr = iter.next(); + String name = entarr[0]; + // include only if the entity was actually used + if (nameInCollection(name, entNames)) { + List subPropList = new ArrayList<>(4); + subPropList.add(new Property("Name", PropertyType.STRING, name)); + subPropList.add(new Property("Type", PropertyType.STRING, "External parsed")); + if (entarr[1] != null) { + subPropList.add(new Property("PublicID", PropertyType.STRING, entarr[1])); + } + if (entarr[2] != null) { + subPropList.add(new Property("SystemID", PropertyType.STRING, entarr[2])); + } + + entProps.add( + new Property("Entity", PropertyType.PROPERTY, PropertyArity.LIST, subPropList)); + } + } + } + + if (!entProps.isEmpty()) { + entitiesProp = new Property("Entities", PropertyType.PROPERTY, PropertyArity.LIST, entProps); + } + + List pi = handler.getProcessingInstructions(); + List piTargets = new LinkedList<>(); + if (!pi.isEmpty()) { + // Build a property, which consists of a list + // of properties, each of which is an array of + // two String properties, named Target and + // Data respectively. + List piPropList = new ArrayList<>(pi.size()); + ListIterator pii = pi.listIterator(); + while (pii.hasNext()) { + ProcessingInstructionInfo pistr = pii.next(); + Property[] subPropArr = new Property[2]; + // Accumulate targets in a list, so we can tell + // which Notations use them. + // Wait a minute -- what we're doing here can't work!! TODO + // what's supposed to be happening? + // piTargets.add (subPropArr[0]); + subPropArr[0] = new Property("Target", PropertyType.STRING, pistr.target); + subPropArr[1] = new Property("Data", PropertyType.STRING, pistr.data); + piPropList.add( + new Property( + "ProcessingInstruction", PropertyType.PROPERTY, PropertyArity.ARRAY, subPropArr)); + } + procInstProp = + new Property( + "ProcessingInstructions", PropertyType.PROPERTY, PropertyArity.LIST, piPropList); + } + + // Notations property. We list notations only if they're + // "actually used," meaning that they designate either + // the target of a processing instruction or the ndata + // of an unparsed entry which is itself "actually used." + List notations = handler.getNotations(); + if (!notations.isEmpty()) { + List notProps = new ArrayList<>(notations.size()); + ListIterator iter = notations.listIterator(); + List subPropList = new ArrayList<>(3); + while (iter.hasNext()) { + String[] notArray = iter.next(); + String notName = notArray[0]; + // Check for use of Notation before including + // TODO this is implemented wrong! Need to reinvestigate + if (nameInCollection(notName, piTargets) + || nameInCollection(notName, unparsedNotationNames)) { + // notArray has name, public ID, system ID + subPropList.add(new Property("Name", PropertyType.STRING, notName)); + if (notArray[1] != null) { + subPropList.add(new Property("PublicID", PropertyType.STRING, notArray[1])); + } + if (notArray[2] != null) { + subPropList.add(new Property("SystemID", PropertyType.STRING, notArray[2])); + } + notProps.add( + new Property("Notation", PropertyType.PROPERTY, PropertyArity.LIST, subPropList)); + } + } + // Recheck emptiness in case only unprocessed notations were found + if (!notProps.isEmpty()) { + notationsProp = + new Property("Notations", PropertyType.PROPERTY, PropertyArity.LIST, notProps); + } + } + + // Now add all the properties we created. + if (namespaceProp != null) { + _propList.add(namespaceProp); + } + if (notationsProp != null) { + _propList.add(notationsProp); + } + if (charRefsProp != null) { + _propList.add(charRefsProp); + } + if (unicodeBlocksProp != null) { + _propList.add(unicodeBlocksProp); + } + if (entitiesProp != null) { + _propList.add(entitiesProp); + } + if (procInstProp != null) { + _propList.add(procInstProp); + } + + List comm = lexHandler.getComments(); + if (!comm.isEmpty()) { + commentProp = new Property("Comments", PropertyType.STRING, PropertyArity.LIST, comm); + } + if (commentProp != null) { + _propList.add(commentProp); + } + + // Check if parse detected invalid XML + if (!handler.isValid()) { + info.setValid(false); + } + + if (info.getWellFormed() == RepInfo.TRUE) { + if (_xhtmlDoctype != null) { + info.setMimeType(_mimeType[2]); + } else { + info.setMimeType(_mimeType[0]); + } + } + + // Add any messages from the parse. + List msgs = handler.getMessages(); + ListIterator msgi = msgs.listIterator(); + while (msgi.hasNext()) { + info.setMessage((Message) msgi.next()); + } + + if (_withTextMD) { + _textMD.setMarkup_basis(info.getFormat()); + _textMD.setMarkup_basis_version(info.getVersion()); + Property property = + new Property( + "TextMDMetadata", PropertyType.TEXTMDMETADATA, PropertyArity.SCALAR, _textMD); + _propList.add(property); + } + + // Set the checksums in the report if they're calculated + setChecksums(this._ckSummer, info); + + if (info.getVersion() == null) { + info.setVersion("1.0"); + _textMD.setMarkup_basis_version("1.0"); + } + return 0; + } + + /** + * Check if the digital object conforms to this Module's internal signature information. + * + *

XML is a particularly messy case; in general, there's no even moderately good way to check + * "signatures" without parsing the whole file, since the document declaration is optional. We + * provide the user two choices, based on the "s" parameter. If 's' is the first character of the + * module parameter, then we look for an XML document declaration, and say there's no signature if + * it's missing. (This can reject well-formed XML files, though not valid ones.) Otherwise, if + * there's no document declaration, we parse the whole file. + * + * @param file A File object for the object being parsed + * @param stream An InputStream, positioned at its beginning, which is generated from the object + * to be parsed + * @param info A fresh RepInfo object which will be modified to reflect the results of the test + */ + @Override + public void checkSignatures(File file, InputStream stream, RepInfo info) throws IOException { + _parseFromSig = false; + info.setFormat(_format[0]); + info.setMimeType(_mimeType[0]); + info.setModule(this); + String sigStr = "= sigStr.length()) { + info.setSigMatch(_name); + return; // sig matches + } + } else break; + } + } catch (IOException e) { + info.setWellFormed(false); + return; + } + if (_sigWantsDecl) { + + // No XML declaration, and it's manadatory according to the param. + info.setWellFormed(false); + return; + } + + // No XML signature, but we're allowed to parse the file now. + // This means rewinding back to the start of the file. + int parseIndex = 1; + _parseFromSig = true; // we set the sig match ourselves + while (parseIndex != 0) { + stream.close(); + stream = new FileInputStream(file); + parseIndex = parse(stream, info, parseIndex); + } + if (info.getWellFormed() == RepInfo.TRUE) { + info.setSigMatch(_name); + } + } + + @Override + protected void initParse() { + super.initParse(); + // if (_defaultParams != null) { + // Iterator iter = _defaultParams.iterator (); + // while (iter.hasNext ()) { + // String param = iter.next (); + // if (param.toLowerCase ().startsWith("localschema=")) { + // addLocalSchema(param); + // } + // } + // } + } + + /* Checks if a String is .equals to any member of a Set of strings. */ + protected static boolean nameInCollection(String name, Collection coll) { + Iterator iter = coll.iterator(); + while (iter.hasNext()) { + String s = iter.next(); + if (name.equals(s)) { + return true; + } + } + return false; + } + + /* + * Converts an int to a 4-digit hex value, e.g., + * 003F or F10A. This is used for Character References. + */ + protected static String intTo4DigitHex(int n) { + StringBuffer buf = new StringBuffer(4); + for (int i = 3; i >= 0; i--) { + int d = (n >> (4 * i)) & 0XF; // extract a nybble + if (d < 10) { + buf.append((char) ('0' + d)); + } else { + buf.append((char) ('A' + (d - 10))); + } + } + return buf.toString(); + } + + /** + * Verification that the string contains something usefull. + * + * @param value string to test + * @return boolean + */ + protected static boolean isNotEmpty(String value) { + return ((value != null) && (value.length() != 0) && !("[None]".equals(value))); + } + + /** + * Add a mapping from a schema URI to a local file. The parameter is of the form + * schema=[URI];[path] + */ + private void addLocalSchema(String param) { + int eq = param.indexOf('='); + int semi = param.indexOf(';'); + try { + String uri = param.substring(eq + 1, semi).trim(); + String path = param.substring(semi + 1).trim(); + File f = new File(path); + if (f.exists()) { + _localSchemas.put(uri, f); + } + } catch (Exception e) { + } + } } diff --git a/jhove-modules/xml-hul/src/main/java/edu/harvard/hul/ois/jhove/module/xml/DTDMapper.java b/jhove-modules/xml-hul/src/main/java/edu/harvard/hul/ois/jhove/module/xml/DTDMapper.java index b6de41099..37dd5b287 100644 --- a/jhove-modules/xml-hul/src/main/java/edu/harvard/hul/ois/jhove/module/xml/DTDMapper.java +++ b/jhove-modules/xml-hul/src/main/java/edu/harvard/hul/ois/jhove/module/xml/DTDMapper.java @@ -1,113 +1,98 @@ -/********************************************************************** - * Jhove - JSTOR/Harvard Object Validation Environment - * Copyright 2004 by JSTOR and the President and Fellows of Harvard College +/** + * ******************************************************************** Jhove - JSTOR/Harvard Object + * Validation Environment Copyright 2004 by JSTOR and the President and Fellows of Harvard College * - **********************************************************************/ - + *

******************************************************************** + */ package edu.harvard.hul.ois.jhove.module.xml; import java.io.InputStream; import java.net.URL; import org.xml.sax.InputSource; - /** - * Class to map public DTD ID's to files which are included with this - * HTML module. This class is used by the XML module, due to the - * intermodulary nature of XHTML. - * - * @author Gary McGath + * Class to map public DTD ID's to files which are included with this HTML module. This class is + * used by the XML module, due to the intermodulary nature of XHTML. * + * @author Gary McGath */ public class DTDMapper { - private final static String xhtml1Frameset = "-//W3C//DTD XHTML 1.0 FRAMESET//EN"; - private final static String xhtml1Strict = "-//W3C//DTD XHTML 1.0 STRICT//EN"; - private final static String xhtml1Transitional = "-//W3C//DTD XHTML 1.0 TRANSITIONAL//EN"; - private final static String xhtml11 = "-//W3C//DTD XHTML 1.1//EN"; - private final static String latin1Ent = "-//W3C//ENTITIES LATIN 1 FOR XHTML//EN"; - private final static String specialEnt = "-//W3C//ENTITIES SPECIAL FOR XHTML//EN"; - private final static String symbolEnt = "-//W3C//ENTITIES SYMBOLS FOR XHTML//EN"; - - /** Attempts to convert a public ID to a matching DTD or Entity resource. - * Returns an InputStream for that resource if there is a match. - * Otherwise returns null. - * - * @param publicID The PUBLIC ID associated with a DTD or entity document - */ - public static InputSource publicIDToFile(String publicID) - { - String filename = null; - if (publicID == null) { - return null; - } - // Make comparisons case-insensitive -- just in case - publicID = publicID.toUpperCase (); - if (xhtml1Frameset.equals (publicID)) { - filename = "xhtml1-frameset.dtd"; - } - else if (xhtml1Strict.equals (publicID)) { - filename = "xhtml1-strict.dtd"; - } - else if (xhtml1Transitional.equals (publicID)) { - filename = "xhtml1-transitional.dtd"; - } - else if (xhtml11.equals (publicID)) { - filename = "xhtml11-flat.dtd"; - } - else if (latin1Ent.equals (publicID)) { - filename = "xhtml-lat1.ent"; - } - else if (specialEnt.equals (publicID)) { - filename = "xhtml-special.ent"; - } - else if (symbolEnt.equals (publicID)) { - filename = "xhtml-symbol.ent"; - } - if (filename != null) { - ClassLoader classLoader = ClassLoader.getSystemClassLoader(); - URL dtdURL = classLoader.getResource(filename); - if (dtdURL != null) { - try { - InputStream strm = dtdURL.openStream(); - return new InputSource (strm); - } - catch (Exception e) { - return null; - } - } - } - return null; + private static final String xhtml1Frameset = "-//W3C//DTD XHTML 1.0 FRAMESET//EN"; + private static final String xhtml1Strict = "-//W3C//DTD XHTML 1.0 STRICT//EN"; + private static final String xhtml1Transitional = "-//W3C//DTD XHTML 1.0 TRANSITIONAL//EN"; + private static final String xhtml11 = "-//W3C//DTD XHTML 1.1//EN"; + private static final String latin1Ent = "-//W3C//ENTITIES LATIN 1 FOR XHTML//EN"; + private static final String specialEnt = "-//W3C//ENTITIES SPECIAL FOR XHTML//EN"; + private static final String symbolEnt = "-//W3C//ENTITIES SYMBOLS FOR XHTML//EN"; + + /** + * Attempts to convert a public ID to a matching DTD or Entity resource. Returns an InputStream + * for that resource if there is a match. Otherwise returns null. + * + * @param publicID The PUBLIC ID associated with a DTD or entity document + */ + public static InputSource publicIDToFile(String publicID) { + String filename = null; + if (publicID == null) { + return null; } - - - /** Returns TRUE if the parameter is the public ID of a - * known XHTML DTD. */ - public static boolean isXHTMLDTD (String publicID) - { - if (publicID == null) { - return false; - } - publicID = publicID.toUpperCase (); - return (xhtml1Frameset.equals (publicID) || - xhtml1Strict.equals (publicID) || - xhtml1Transitional.equals (publicID) || - xhtml11.equals (publicID)); + // Make comparisons case-insensitive -- just in case + publicID = publicID.toUpperCase(); + if (xhtml1Frameset.equals(publicID)) { + filename = "xhtml1-frameset.dtd"; + } else if (xhtml1Strict.equals(publicID)) { + filename = "xhtml1-strict.dtd"; + } else if (xhtml1Transitional.equals(publicID)) { + filename = "xhtml1-transitional.dtd"; + } else if (xhtml11.equals(publicID)) { + filename = "xhtml11-flat.dtd"; + } else if (latin1Ent.equals(publicID)) { + filename = "xhtml-lat1.ent"; + } else if (specialEnt.equals(publicID)) { + filename = "xhtml-special.ent"; + } else if (symbolEnt.equals(publicID)) { + filename = "xhtml-symbol.ent"; } - - /** Returns the XHTML version associated with the DTD's - * public ID. Returns null if it isn't - * a known XHTML public ID. */ - public static String getXHTMLVersion (String publicID) { - publicID = publicID.toUpperCase (); - if (!isXHTMLDTD (publicID)) { - return null; - } - else if (xhtml11.equals (publicID)) { - return "1.1"; - } - else { - return "1.0"; + if (filename != null) { + ClassLoader classLoader = ClassLoader.getSystemClassLoader(); + URL dtdURL = classLoader.getResource(filename); + if (dtdURL != null) { + try { + InputStream strm = dtdURL.openStream(); + return new InputSource(strm); + } catch (Exception e) { + return null; } + } + } + return null; + } + + /** Returns TRUE if the parameter is the public ID of a known XHTML DTD. */ + public static boolean isXHTMLDTD(String publicID) { + if (publicID == null) { + return false; + } + publicID = publicID.toUpperCase(); + return (xhtml1Frameset.equals(publicID) + || xhtml1Strict.equals(publicID) + || xhtml1Transitional.equals(publicID) + || xhtml11.equals(publicID)); + } + + /** + * Returns the XHTML version associated with the DTD's public ID. Returns null if it + * isn't a known XHTML public ID. + */ + public static String getXHTMLVersion(String publicID) { + publicID = publicID.toUpperCase(); + if (!isXHTMLDTD(publicID)) { + return null; + } else if (xhtml11.equals(publicID)) { + return "1.1"; + } else { + return "1.0"; } + } } diff --git a/jhove-modules/xml-hul/src/main/java/edu/harvard/hul/ois/jhove/module/xml/EntityInfo.java b/jhove-modules/xml-hul/src/main/java/edu/harvard/hul/ois/jhove/module/xml/EntityInfo.java index 88dd4edf5..bba087215 100644 --- a/jhove-modules/xml-hul/src/main/java/edu/harvard/hul/ois/jhove/module/xml/EntityInfo.java +++ b/jhove-modules/xml-hul/src/main/java/edu/harvard/hul/ois/jhove/module/xml/EntityInfo.java @@ -1,12 +1,12 @@ -/********************************************************************** - * Jhove - JSTOR/Harvard Object Validation Environment - * Copyright 2004-2012 by JSTOR and the President and Fellows of Harvard College - **********************************************************************/ - +/** + * ******************************************************************** Jhove - JSTOR/Harvard Object + * Validation Environment Copyright 2004-2012 by JSTOR and the President and Fellows of Harvard + * College ******************************************************************** + */ package edu.harvard.hul.ois.jhove.module.xml; /** A little class so we can treat entity information as a structure */ public class EntityInfo { - public String publicID; - public String systemID; + public String publicID; + public String systemID; } diff --git a/jhove-modules/xml-hul/src/main/java/edu/harvard/hul/ois/jhove/module/xml/HtmlMetadata.java b/jhove-modules/xml-hul/src/main/java/edu/harvard/hul/ois/jhove/module/xml/HtmlMetadata.java index 6772a017c..04bbc00bc 100644 --- a/jhove-modules/xml-hul/src/main/java/edu/harvard/hul/ois/jhove/module/xml/HtmlMetadata.java +++ b/jhove-modules/xml-hul/src/main/java/edu/harvard/hul/ois/jhove/module/xml/HtmlMetadata.java @@ -1,414 +1,348 @@ -/********************************************************************** - * Jhove - JSTOR/Harvard Object Validation Environment - * Copyright 2004-2009 by JSTOR and the President and Fellows of Harvard College - **********************************************************************/ - +/** + * ******************************************************************** Jhove - JSTOR/Harvard Object + * Validation Environment Copyright 2004-2009 by JSTOR and the President and Fellows of Harvard + * College ******************************************************************** + */ package edu.harvard.hul.ois.jhove.module.xml; -import java.util.LinkedList; -import java.util.List; -import java.util.TreeSet; - import edu.harvard.hul.ois.jhove.Property; import edu.harvard.hul.ois.jhove.PropertyArity; import edu.harvard.hul.ois.jhove.PropertyType; import edu.harvard.hul.ois.jhove.TextMDMetadata; import edu.harvard.hul.ois.jhove.module.utf8.Utf8BlockMarker; +import java.util.LinkedList; +import java.util.List; +import java.util.TreeSet; /** - * Repository for an HTML document's metadata. - * Also hold some state information, so that properties involving - * tags, attributes and pcdata can be constructed. + * Repository for an HTML document's metadata. Also hold some state information, so that properties + * involving tags, attributes and pcdata can be constructed. * * @author Gary McGath - * */ public class HtmlMetadata { - private String _title; - private String _lang; - private List _meta; - private String _charset; - private TreeSet _languages; - private List _links; - private List _images; - private List _citations; - private List _defs; - private List _frames; - private List _scripts; - private List _abbrs; - private TreeSet _entities; - private Property _propUnderConstruction; + private String _title; + private String _lang; + private List _meta; + private String _charset; + private TreeSet _languages; + private List _links; + private List _images; + private List _citations; + private List _defs; + private List _frames; + private List _scripts; + private List _abbrs; + private TreeSet _entities; + private Property _propUnderConstruction; + + /** Object for tracking UTF8 blocks. */ + private Utf8BlockMarker utf8BM; + + /** Constructor. Initializes to the empty state. */ + public HtmlMetadata() { + // Mostly sets variables to their defaults; it's good + // documentation practice. Lists are set to null until + // there's actually something to add to them; this guarantees + // that toProperty() doesn't have to deal with empty lists. + _title = null; + _lang = null; + _meta = null; + _charset = null; + _links = null; + _images = null; + _citations = null; + _defs = null; + _frames = null; + _scripts = null; + _entities = null; + _languages = null; + _propUnderConstruction = null; + utf8BM = new Utf8BlockMarker(); + } + + /** Stores the contents of the TITLE element. */ + public void setTitle(String title) { + _title = title; + } - /** Object for tracking UTF8 blocks. */ - private Utf8BlockMarker utf8BM; + /** Stores the language defined in the HTML element. */ + public void setLanguage(String lang) { + _lang = lang; + } - /** Constructor. Initializes to the empty state. */ - public HtmlMetadata () - { - // Mostly sets variables to their defaults; it's good - // documentation practice. Lists are set to null until - // there's actually something to add to them; this guarantees - // that toProperty() doesn't have to deal with empty lists. - _title = null; - _lang = null; - _meta = null; - _charset = null; - _links = null; - _images = null; - _citations = null; - _defs = null; - _frames = null; - _scripts = null; - _entities = null; - _languages = null; - _propUnderConstruction = null; - utf8BM = new Utf8BlockMarker (); + /** Add a language defined in an attribute of any element except the HTML element. */ + public void addLanguage(String lang) { + if (!lang.equals(_lang)) { + if (_languages == null) { + _languages = new TreeSet(); + } + _languages.add(lang); } - - /** Stores the contents of the TITLE element. */ - public void setTitle (String title) - { - _title = title; + } + + /** Adds a CITE element's pcdata to the Citations property. */ + public void addCitation(String text) { + if (_citations == null) { + _citations = new LinkedList(); } - - /** Stores the language defined in the HTML element. */ - public void setLanguage (String lang) - { - _lang = lang; + _citations.add(text); + } + + /** Adds a META tag's contents to the Meta property. */ + public void addMeta(Property prop) { + // We don't set _meta until there's a property; + // thus, we guarantee it will never be an empty list. + if (_meta == null) { + _meta = new LinkedList(); } - - /** Add a language defined in an attribute of any element - * except the HTML element. */ - public void addLanguage (String lang) - { - if (!lang.equals(_lang)) { - if (_languages == null) { - _languages = new TreeSet (); - } - _languages.add (lang); - } + _meta.add(prop); + + // Is it a httpequiv=Content-Type ? + String valContentType = extractHttpEquivValue(prop, "Content-Type"); + if (valContentType != null) { + final String toSearch = "charset="; + int indexOfCharset = valContentType.indexOf(toSearch); + if (indexOfCharset != -1) { + setCharset(valContentType.substring(indexOfCharset + toSearch.length())); + } } - - /** Adds a CITE element's pcdata to the Citations property. */ - public void addCitation (String text) - { - if (_citations == null) { - _citations = new LinkedList (); - } - _citations.add (text); + // Is it a httpequiv=Content-Language ? + String valContentLanguage = extractHttpEquivValue(prop, "Content-Language"); + if (valContentLanguage != null) { + setLanguage(valContentLanguage); } - - /** Adds a META tag's contents to the Meta property. */ - public void addMeta (Property prop) - { - // We don't set _meta until there's a property; - // thus, we guarantee it will never be an empty list. - if (_meta == null) { - _meta = new LinkedList (); - } - _meta.add (prop); - - // Is it a httpequiv=Content-Type ? - String valContentType = extractHttpEquivValue(prop, "Content-Type"); - if (valContentType != null) { - final String toSearch = "charset="; - int indexOfCharset = valContentType.indexOf(toSearch); - if (indexOfCharset != -1) { - setCharset(valContentType.substring(indexOfCharset + toSearch.length())); - } - } - // Is it a httpequiv=Content-Language ? - String valContentLanguage = extractHttpEquivValue(prop, "Content-Language"); - if (valContentLanguage != null) { - setLanguage(valContentLanguage); + } + + /** + * Extract the content value associated with a given httpEquiv. + * + * @param prop List containing the description of the meta tag + * @param httpEquivValue the httpEquiv to consider + * @return the content value + */ + public String extractHttpEquivValue(Property prop, String httpEquivValue) { + if (httpEquivValue == null) return null; + String value = null; + Property httpEquiv = prop.getByName("Httpequiv"); + if (httpEquiv != null + && PropertyArity.SCALAR.equals(httpEquiv.getArity()) + && PropertyType.STRING.equals(httpEquiv.getType())) { + String val = (String) httpEquiv.getValue(); + if (httpEquivValue.equalsIgnoreCase(val)) { + // Look for charset in the Content property + Property content = prop.getByName("Content"); + if (content != null + && PropertyArity.SCALAR.equals(content.getArity()) + && PropertyType.STRING.equals(content.getType())) { + value = (String) content.getValue(); } + } } + return value; + } - /** - * Extract the content value associated with a given httpEquiv. - * @param prop List containing the description of the meta tag - * @param httpEquivValue the httpEquiv to consider - * @return the content value - */ - public String extractHttpEquivValue(Property prop, String httpEquivValue) { - if (httpEquivValue == null) return null; - String value = null; - Property httpEquiv = prop.getByName("Httpequiv"); - if (httpEquiv != null && - PropertyArity.SCALAR.equals(httpEquiv.getArity()) && - PropertyType.STRING.equals(httpEquiv.getType()) - ) { - String val = (String)httpEquiv.getValue(); - if (httpEquivValue.equalsIgnoreCase(val)) { - // Look for charset in the Content property - Property content = prop.getByName("Content"); - if (content != null && - PropertyArity.SCALAR.equals(content.getArity()) && - PropertyType.STRING.equals(content.getType()) - ) { - value = (String)content.getValue(); - } - } - } - return value; + /** Stores the charset defined in the HTML element. */ + public void setCharset(String charset) { + _charset = charset; + } + + /** Adds a FRAME tag's contents to the Meta property. */ + public void addFrame(Property prop) { + // We don't set _frames until there's a property; + // thus, we guarantee it will never be an empty list. + if (_frames == null) { + _frames = new LinkedList(); } - - /** Stores the charset defined in the HTML element. */ - public void setCharset (String charset) - { - _charset = charset; + _frames.add(prop); + } + + /** Adds an ABBR tag's contents to the Meta property. */ + public void addAbbr(Property prop) { + if (_abbrs == null) { + _abbrs = new LinkedList(); } - - /** Adds a FRAME tag's contents to the Meta property. */ - public void addFrame (Property prop) - { - // We don't set _frames until there's a property; - // thus, we guarantee it will never be an empty list. - if (_frames == null) { - _frames = new LinkedList (); - } - _frames.add (prop); + _abbrs.add(prop); + } + + /** Adds a link to the Links property. */ + public void addLink(String link) { + if (_links == null) { + _links = new LinkedList(); } - - /** Adds an ABBR tag's contents to the Meta property. */ - public void addAbbr (Property prop) - { - if (_abbrs == null) { - _abbrs = new LinkedList (); - } - _abbrs.add (prop); + _links.add(link); + } + + /** Adds an item to the Images property. */ + public void addImage(Property prop) { + if (_images == null) { + _images = new LinkedList(); } - - /** Adds a link to the Links property. */ - public void addLink (String link) - { - if (_links == null) { - _links = new LinkedList (); - } - _links.add (link); + _images.add(prop); + } + + /** Adds a defined term to the Defined Terms property. */ + public void addDef(String text) { + if (_defs == null) { + _defs = new LinkedList(); } - - /** Adds an item to the Images property. */ - public void addImage (Property prop) - { - if (_images == null) { - _images = new LinkedList (); - } - _images.add (prop); + _defs.add(text); + } + + /** Adds the language of a SCRIPT element to the Scripts property. */ + public void addScript(String stype) { + if (_scripts == null) { + _scripts = new LinkedList(); } - - /** Adds a defined term to the Defined Terms property. */ - public void addDef (String text) - { - if (_defs == null) { - _defs = new LinkedList (); - } - _defs.add (text); + _scripts.add(stype); + } + + /** + * Adds a String to the Entities property. This property is a SortedSet, so duplicates are not + * added, and the resulting set can be iterated in alphabetical order. + */ + public void addEntity(String entity) { + if (_entities == null) { + _entities = new TreeSet(); } - - /** Adds the language of a SCRIPT element to the Scripts property. */ - public void addScript (String stype) - { - if (_scripts == null) { - _scripts = new LinkedList (); - } - _scripts.add (stype); + _entities.add(entity); + } + + /** Returns the UTF8BlockMarker for the metadata. */ + public Utf8BlockMarker getUtf8BlockMarker() { + return utf8BM; + } + + /** Returns the contents of the TITLE element. */ + public String getTitle() { + return _title; + } + + public String getCharset() { + return _charset; + } + + /** Converts the metadata to a Property. */ + public Property toProperty(TextMDMetadata _textMD) { + List propList = new LinkedList(); + Property val = + new Property("HTMLMetadata", PropertyType.PROPERTY, PropertyArity.LIST, propList); + if (_lang != null) { + propList.add(new Property("PrimaryLanguage", PropertyType.STRING, _lang)); + if (_textMD != null) { + _textMD.setLanguage(_lang); + } } - - /** Adds a String to the Entities property. This property is a - * SortedSet, so duplicates are not added, and the resulting set - * can be iterated in alphabetical order. */ - public void addEntity (String entity) - { - if (_entities == null) { - _entities = new TreeSet (); - } - _entities.add (entity); + if (_languages != null) { + propList.add( + new Property("OtherLanguages", PropertyType.STRING, PropertyArity.SET, _languages)); } - - /** Returns the UTF8BlockMarker for the metadata. */ - public Utf8BlockMarker getUtf8BlockMarker () - { - return utf8BM; + if (_title != null) { + propList.add(new Property("Title", PropertyType.STRING, _title)); } - - /** Returns the contents of the TITLE element. */ - public String getTitle () - { - return _title; + if (_meta != null) { + // We're guaranteed that if _meta isn't null, it's non-empty. + propList.add(new Property("MetaTags", PropertyType.PROPERTY, PropertyArity.LIST, _meta)); } - - public String getCharset() { - return _charset; + if (_frames != null) { + propList.add(new Property("Frames", PropertyType.PROPERTY, PropertyArity.LIST, _frames)); } - - /** Converts the metadata to a Property. */ - public Property toProperty (TextMDMetadata _textMD) - { - List propList = new LinkedList (); - Property val = new Property ("HTMLMetadata", - PropertyType.PROPERTY, - PropertyArity.LIST, - propList); - if (_lang != null) { - propList.add (new Property ("PrimaryLanguage", - PropertyType.STRING, - _lang)); - if (_textMD != null) { - _textMD.setLanguage(_lang); - } - } - if (_languages != null) { - propList.add (new Property ("OtherLanguages", - PropertyType.STRING, - PropertyArity.SET, - _languages)); - } - if (_title != null) { - propList.add (new Property ("Title", - PropertyType.STRING, - _title)); - } - if (_meta != null) { - // We're guaranteed that if _meta isn't null, it's non-empty. - propList.add (new Property ("MetaTags", - PropertyType.PROPERTY, - PropertyArity.LIST, - _meta)); - } - if (_frames != null) { - propList.add (new Property ("Frames", - PropertyType.PROPERTY, - PropertyArity.LIST, - _frames)); - } - if (_links != null) { - propList.add (new Property ("Links", - PropertyType.STRING, - PropertyArity.LIST, - _links)); - } - if (_scripts != null) { - propList.add (new Property ("Scripts", - PropertyType.STRING, - PropertyArity.LIST, - _scripts)); - } - if (_images != null) { - propList.add (new Property("Images", - PropertyType.PROPERTY, - PropertyArity.LIST, - _images)); - } - if (_citations != null) { - propList.add (new Property("Citations", - PropertyType.STRING, - PropertyArity.LIST, - _citations)); - } - if (_defs != null) { - propList.add (new Property ("DefinedTerms", - PropertyType.STRING, - PropertyArity.LIST, - _defs)); - } - if (_abbrs != null) { - propList.add (new Property ("Abbreviations", - PropertyType.PROPERTY, - PropertyArity.LIST, - _abbrs)); - } - if (_entities != null) { - propList.add (new Property ("Entities", - PropertyType.STRING, - PropertyArity.SET, - _entities)); - } - if (utf8BM != null) { - Property p = utf8BM.getBlocksUsedProperty("UnicodeEntityBlocks"); - if (p != null) { - propList.add (p); - } - } - if (_textMD != null) { - propList.add (new Property ("TextMDMetadata", - PropertyType.TEXTMDMETADATA, - PropertyArity.SCALAR, - _textMD)); - } - - if (propList.isEmpty ()) { - return null; - } - - return val; + if (_links != null) { + propList.add(new Property("Links", PropertyType.STRING, PropertyArity.LIST, _links)); } - - /** Sets a "property under construction". This is generally - * called when an XML element is found, and the PCDATA must - * be incorporated into the property. - */ - public void setPropUnderConstruction (Property p) - { - _propUnderConstruction = p; + if (_scripts != null) { + propList.add(new Property("Scripts", PropertyType.STRING, PropertyArity.LIST, _scripts)); } - - /** Returns the "property under construction." */ - public Property getPropUnderConstruction () - { - return _propUnderConstruction; + if (_images != null) { + propList.add(new Property("Images", PropertyType.PROPERTY, PropertyArity.LIST, _images)); } - - /** Adds PCDATA text to the property under construction. - * This may not all be provided in one lump, so it - * has to allow for multiple chunks. */ - public void addToPropUnderConstruction - (char[] ch, int start, int length) - { - if (_propUnderConstruction != null) { - String argStr = new String (ch, start, length); - String name = _propUnderConstruction.getName (); - Object val = _propUnderConstruction.getValue (); - if ("abbr".equals (name)) { - // Theoretically, this can come in more than one - // chunk, but a long abbreviation is moronic if - // not oxymoronic. - List propList = (List) _propUnderConstruction.getValue(); - Property abProp = new Property ("abbr", - PropertyType.STRING, - argStr); - propList.add(0, abProp); - } - else if ("title".equals (name) || - "dfn".equals (name)) { - // For these properties, we just need to maintain - // the String. But to keep the design consistent and - // simple, we maintain the Property and then just pull - // out the String at the end. - // A Property is immutable. Rather than risk obscure - // consequences from changing this assumption, we append - // the text to a new Property. - _propUnderConstruction = new Property (name, - PropertyType.STRING, - (String) val + argStr); - } - } + if (_citations != null) { + propList.add(new Property("Citations", PropertyType.STRING, PropertyArity.LIST, _citations)); } - - /** Finishes any property under construction. This is called - * when an end element is encountered. */ - public void finishPropUnderConstruction () - { - if (_propUnderConstruction != null) { - String name = _propUnderConstruction.getName (); - if ("abbr".equals(name)) { - addAbbr (_propUnderConstruction); - } - else if ("title".equals (name)) { - _title = (String) _propUnderConstruction.getValue (); - } - _propUnderConstruction = null; - } + if (_defs != null) { + propList.add(new Property("DefinedTerms", PropertyType.STRING, PropertyArity.LIST, _defs)); + } + if (_abbrs != null) { + propList.add( + new Property("Abbreviations", PropertyType.PROPERTY, PropertyArity.LIST, _abbrs)); + } + if (_entities != null) { + propList.add(new Property("Entities", PropertyType.STRING, PropertyArity.SET, _entities)); + } + if (utf8BM != null) { + Property p = utf8BM.getBlocksUsedProperty("UnicodeEntityBlocks"); + if (p != null) { + propList.add(p); + } + } + if (_textMD != null) { + propList.add( + new Property( + "TextMDMetadata", PropertyType.TEXTMDMETADATA, PropertyArity.SCALAR, _textMD)); + } + + if (propList.isEmpty()) { + return null; + } + + return val; + } + + /** + * Sets a "property under construction". This is generally called when an XML element is found, + * and the PCDATA must be incorporated into the property. + */ + public void setPropUnderConstruction(Property p) { + _propUnderConstruction = p; + } + + /** Returns the "property under construction." */ + public Property getPropUnderConstruction() { + return _propUnderConstruction; + } + + /** + * Adds PCDATA text to the property under construction. This may not all be provided in one lump, + * so it has to allow for multiple chunks. + */ + public void addToPropUnderConstruction(char[] ch, int start, int length) { + if (_propUnderConstruction != null) { + String argStr = new String(ch, start, length); + String name = _propUnderConstruction.getName(); + Object val = _propUnderConstruction.getValue(); + if ("abbr".equals(name)) { + // Theoretically, this can come in more than one + // chunk, but a long abbreviation is moronic if + // not oxymoronic. + List propList = (List) _propUnderConstruction.getValue(); + Property abProp = new Property("abbr", PropertyType.STRING, argStr); + propList.add(0, abProp); + } else if ("title".equals(name) || "dfn".equals(name)) { + // For these properties, we just need to maintain + // the String. But to keep the design consistent and + // simple, we maintain the Property and then just pull + // out the String at the end. + // A Property is immutable. Rather than risk obscure + // consequences from changing this assumption, we append + // the text to a new Property. + _propUnderConstruction = new Property(name, PropertyType.STRING, (String) val + argStr); + } + } + } + + /** + * Finishes any property under construction. This is called when an end element is encountered. + */ + public void finishPropUnderConstruction() { + if (_propUnderConstruction != null) { + String name = _propUnderConstruction.getName(); + if ("abbr".equals(name)) { + addAbbr(_propUnderConstruction); + } else if ("title".equals(name)) { + _title = (String) _propUnderConstruction.getValue(); + } + _propUnderConstruction = null; } + } } diff --git a/jhove-modules/xml-hul/src/main/java/edu/harvard/hul/ois/jhove/module/xml/MessageConstants.java b/jhove-modules/xml-hul/src/main/java/edu/harvard/hul/ois/jhove/module/xml/MessageConstants.java index 7e19e62c6..75e73416a 100644 --- a/jhove-modules/xml-hul/src/main/java/edu/harvard/hul/ois/jhove/module/xml/MessageConstants.java +++ b/jhove-modules/xml-hul/src/main/java/edu/harvard/hul/ois/jhove/module/xml/MessageConstants.java @@ -5,60 +5,46 @@ import edu.harvard.hul.ois.jhove.messages.JhoveMessages; /** - * Enum used to externalise the XML module message Strings. Using an enum - * INSTANCE as a "trick" to ensure a single instance of the class. String - * constants should be prefixed according to their use in the module: + * Enum used to externalise the XML module message Strings. Using an enum INSTANCE as a "trick" to + * ensure a single instance of the class. String constants should be prefixed according to their use + * in the module: + * *

    - *
  • WRN_ for warning strings, often logger messages.
  • - *
  • INF_ for informational messages.
  • - *
  • ERR_ for error messages that indicate a file is invalid or not well - * formed.
  • + *
  • WRN_ for warning strings, often logger messages. + *
  • INF_ for informational messages. + *
  • ERR_ for error messages that indicate a file is invalid or not well formed. *
- * When adding new messages try to adopt the following order for the naming - * elements: + * + * When adding new messages try to adopt the following order for the naming elements: + * *
    - *
  1. PREFIX: one of the three prefixes from the list above.
  2. - *
  3. ENTITY_NAME: the name of the entity causing the problem.
  4. - *
  5. Problem: a short indicator of the problem type, e.g. MISSING, ILLEGAL, - * etc.
  6. + *
  7. PREFIX: one of the three prefixes from the list above. + *
  8. ENTITY_NAME: the name of the entity causing the problem. + *
  9. Problem: a short indicator of the problem type, e.g. MISSING, ILLEGAL, etc. *
- * The elements should be separated by underscores. The messages currently don't - * follow a consistent vocabulary, that is terms such as invalid, illegal, or - * malformed are used without definition. + * + * The elements should be separated by underscores. The messages currently don't follow a consistent + * vocabulary, that is terms such as invalid, illegal, or malformed are used without definition. * * @author Thomas Ledoux */ - public enum MessageConstants { - INSTANCE; + INSTANCE; - public static final JhoveMessageFactory messageFactory = JhoveMessages - .getInstance("edu.harvard.hul.ois.jhove.module.xml.ErrorMessages"); + public static final JhoveMessageFactory messageFactory = + JhoveMessages.getInstance("edu.harvard.hul.ois.jhove.module.xml.ErrorMessages"); - public static final JhoveMessage XML_HUL_1 = messageFactory - .getMessage("XML-HUL-1"); - public static final JhoveMessage XML_HUL_2 = messageFactory - .getMessage("XML-HUL-2"); - public static final JhoveMessage XML_HUL_3 = messageFactory - .getMessage("XML-HUL-3"); - public static final JhoveMessage XML_HUL_4 = messageFactory - .getMessage("XML-HUL-4"); - public static final JhoveMessage XML_HUL_5 = messageFactory - .getMessage("XML-HUL-5"); - public static final JhoveMessage XML_HUL_6 = messageFactory - .getMessage("XML-HUL-6"); - public static final JhoveMessage XML_HUL_7 = messageFactory - .getMessage("XML-HUL-7"); - public static final JhoveMessage XML_HUL_8 = messageFactory - .getMessage("XML-HUL-8"); - public static final JhoveMessage XML_HUL_9 = messageFactory - .getMessage("XML-HUL-9"); - public static final JhoveMessage XML_HUL_10 = messageFactory - .getMessage("XML-HUL-10"); - public static final JhoveMessage XML_HUL_11 = messageFactory - .getMessage("XML-HUL-11"); - public static final JhoveMessage XML_HUL_12 = messageFactory - .getMessage("XML-HUL-12"); - public static final JhoveMessage XML_HUL_13 = messageFactory - .getMessage("XML-HUL-13"); + public static final JhoveMessage XML_HUL_1 = messageFactory.getMessage("XML-HUL-1"); + public static final JhoveMessage XML_HUL_2 = messageFactory.getMessage("XML-HUL-2"); + public static final JhoveMessage XML_HUL_3 = messageFactory.getMessage("XML-HUL-3"); + public static final JhoveMessage XML_HUL_4 = messageFactory.getMessage("XML-HUL-4"); + public static final JhoveMessage XML_HUL_5 = messageFactory.getMessage("XML-HUL-5"); + public static final JhoveMessage XML_HUL_6 = messageFactory.getMessage("XML-HUL-6"); + public static final JhoveMessage XML_HUL_7 = messageFactory.getMessage("XML-HUL-7"); + public static final JhoveMessage XML_HUL_8 = messageFactory.getMessage("XML-HUL-8"); + public static final JhoveMessage XML_HUL_9 = messageFactory.getMessage("XML-HUL-9"); + public static final JhoveMessage XML_HUL_10 = messageFactory.getMessage("XML-HUL-10"); + public static final JhoveMessage XML_HUL_11 = messageFactory.getMessage("XML-HUL-11"); + public static final JhoveMessage XML_HUL_12 = messageFactory.getMessage("XML-HUL-12"); + public static final JhoveMessage XML_HUL_13 = messageFactory.getMessage("XML-HUL-13"); } diff --git a/jhove-modules/xml-hul/src/main/java/edu/harvard/hul/ois/jhove/module/xml/ProcessingInstructionInfo.java b/jhove-modules/xml-hul/src/main/java/edu/harvard/hul/ois/jhove/module/xml/ProcessingInstructionInfo.java index 199e3cac6..2acd7439e 100644 --- a/jhove-modules/xml-hul/src/main/java/edu/harvard/hul/ois/jhove/module/xml/ProcessingInstructionInfo.java +++ b/jhove-modules/xml-hul/src/main/java/edu/harvard/hul/ois/jhove/module/xml/ProcessingInstructionInfo.java @@ -1,12 +1,12 @@ -/********************************************************************** - * Jhove - JSTOR/Harvard Object Validation Environment - * Copyright 2004-2012 by JSTOR and the President and Fellows of Harvard College - **********************************************************************/ - +/** + * ******************************************************************** Jhove - JSTOR/Harvard Object + * Validation Environment Copyright 2004-2012 by JSTOR and the President and Fellows of Harvard + * College ******************************************************************** + */ package edu.harvard.hul.ois.jhove.module.xml; /** A little class so we can treat processing instruction information as a structure */ public class ProcessingInstructionInfo { - public String target; - public String data; + public String target; + public String data; } diff --git a/jhove-modules/xml-hul/src/main/java/edu/harvard/hul/ois/jhove/module/xml/SchemaInfo.java b/jhove-modules/xml-hul/src/main/java/edu/harvard/hul/ois/jhove/module/xml/SchemaInfo.java index 890a5e7dd..8bc5bdbe4 100644 --- a/jhove-modules/xml-hul/src/main/java/edu/harvard/hul/ois/jhove/module/xml/SchemaInfo.java +++ b/jhove-modules/xml-hul/src/main/java/edu/harvard/hul/ois/jhove/module/xml/SchemaInfo.java @@ -1,12 +1,12 @@ -/********************************************************************** - * Jhove - JSTOR/Harvard Object Validation Environment - * Copyright 2004-2012 by JSTOR and the President and Fellows of Harvard College - **********************************************************************/ - +/** + * ******************************************************************** Jhove - JSTOR/Harvard Object + * Validation Environment Copyright 2004-2012 by JSTOR and the President and Fellows of Harvard + * College ******************************************************************** + */ package edu.harvard.hul.ois.jhove.module.xml; /** A little class so we can treat schema information as a structure */ public class SchemaInfo { - public String namespaceURI; - public String location; + public String namespaceURI; + public String location; } diff --git a/jhove-modules/xml-hul/src/main/java/edu/harvard/hul/ois/jhove/module/xml/XhtmlProcessing.java b/jhove-modules/xml-hul/src/main/java/edu/harvard/hul/ois/jhove/module/xml/XhtmlProcessing.java index 246b28f94..ea931d8ea 100644 --- a/jhove-modules/xml-hul/src/main/java/edu/harvard/hul/ois/jhove/module/xml/XhtmlProcessing.java +++ b/jhove-modules/xml-hul/src/main/java/edu/harvard/hul/ois/jhove/module/xml/XhtmlProcessing.java @@ -1,8 +1,8 @@ -/********************************************************************** - * Jhove - JSTOR/Harvard Object Validation Environment - * Copyright 2004 by JSTOR and the President and Fellows of Harvard College - **********************************************************************/ - +/** + * ******************************************************************** Jhove - JSTOR/Harvard Object + * Validation Environment Copyright 2004 by JSTOR and the President and Fellows of Harvard College + * ******************************************************************** + */ package edu.harvard.hul.ois.jhove.module.xml; import edu.harvard.hul.ois.jhove.*; @@ -10,320 +10,251 @@ import org.xml.sax.Attributes; /** - * Methods for processing XHTML elements in an XML document. - * These are closely related in functionality to corresponding - * methods in the XML module. + * Methods for processing XHTML elements in an XML document. These are closely related in + * functionality to corresponding methods in the XML module. * * @author Gary McGath - * @see edu.harvard.hul.ois.jhove.module.html.HtmlDocDesc + * @see edu.harvard.hul.ois.jhove.module.html.HtmlDocDesc */ public final class XhtmlProcessing { - /** Process an element and extract metadata */ - /** Process the element to extract any available metadata. */ - protected static void processElement (String localName, - String qualifiedName, - Attributes atts, - HtmlMetadata mdata) - { - if ("html".equals (localName)) { - processHtml (mdata, atts); - } - else if ("meta".equals (localName)) { - processMeta (mdata, atts); - } - else if ("a".equals (localName)) { - processA (mdata, atts); - } - else if ("img".equals (localName)) { - processImg (mdata, atts); - } - else if ("frame".equals (localName)) { - processFrame (mdata, atts); - } - else if ("script".equals (localName)) { - processScript (mdata, atts); - } - else if ("abbr".equals (localName)) { - processAbbr (mdata, atts); - } - else if ("title".equals (localName)) { - processTitle (mdata, atts); - } - else if ("cite".equals (localName)) { - processCite (mdata, atts); - } - - /* Look for certain attributes in any tag. */ - for (int i = 0; i < atts.getLength (); i++) { - String attname = atts.getLocalName (i); - String attval = atts.getValue (i); - if ("lang".equals (attname) && attval != null) { - mdata.addLanguage (attval); - } - } + /** Process an element and extract metadata */ + /** Process the element to extract any available metadata. */ + protected static void processElement( + String localName, String qualifiedName, Attributes atts, HtmlMetadata mdata) { + if ("html".equals(localName)) { + processHtml(mdata, atts); + } else if ("meta".equals(localName)) { + processMeta(mdata, atts); + } else if ("a".equals(localName)) { + processA(mdata, atts); + } else if ("img".equals(localName)) { + processImg(mdata, atts); + } else if ("frame".equals(localName)) { + processFrame(mdata, atts); + } else if ("script".equals(localName)) { + processScript(mdata, atts); + } else if ("abbr".equals(localName)) { + processAbbr(mdata, atts); + } else if ("title".equals(localName)) { + processTitle(mdata, atts); + } else if ("cite".equals(localName)) { + processCite(mdata, atts); } - - /** Process metadata from an HTML tag */ - private static void processHtml (HtmlMetadata mdata, Attributes atts) - { - String lang = null; - for (int i = 0; i < atts.getLength (); i++) { - String attname = atts.getLocalName (i); - String attval = atts.getValue (i); - if ("lang".equals (attname)) { - lang = attval; - } - } - if (lang != null) { - mdata.setLanguage(lang); - } + /* Look for certain attributes in any tag. */ + for (int i = 0; i < atts.getLength(); i++) { + String attname = atts.getLocalName(i); + String attval = atts.getValue(i); + if ("lang".equals(attname) && attval != null) { + mdata.addLanguage(attval); + } } + } - - /** Process metadata from a META tag */ - private static void processMeta (HtmlMetadata mdata, Attributes atts) - { - String name = null; - String httpeq = null; - String content = null; - for (int i = 0; i < atts.getLength (); i++) { - String attname = atts.getLocalName (i); - String attval = atts.getValue (i); - if ("name".equals (attname)) { - name = attval; - } - if ("http-equiv".equals (attname)) { - httpeq = attval; - } - if ("content".equals (attname)) { - content = attval; - } - } - if (name != null || httpeq != null || content != null) { - List plist = new ArrayList (3); - if (name != null) { - plist.add (new Property ("Name", - PropertyType.STRING, - name)); - } - if (httpeq != null) { - plist.add (new Property ("Httpequiv", - PropertyType.STRING, - httpeq)); - } - if (content != null) { - plist.add (new Property ("Content", - PropertyType.STRING, - content)); - } - mdata.addMeta (new Property ("Meta", - PropertyType.PROPERTY, - PropertyArity.LIST, - plist)); - } + /** Process metadata from an HTML tag */ + private static void processHtml(HtmlMetadata mdata, Attributes atts) { + String lang = null; + for (int i = 0; i < atts.getLength(); i++) { + String attname = atts.getLocalName(i); + String attval = atts.getValue(i); + if ("lang".equals(attname)) { + lang = attval; + } + } + if (lang != null) { + mdata.setLanguage(lang); } + } - /** Process metadata from an A element. Only elements with an - * HREF attribute are of interest here. We ignore links - * to anchors. */ - private static void processA (HtmlMetadata mdata, Attributes atts) - { - for (int i = 0; i < atts.getLength (); i++) { - String attname = atts.getLocalName (i); - String attval = atts.getValue (i); - if ("href".equals (attname)) { - String link = attval; - if (link.length() > 0 && link.charAt (0) != '#') { - mdata.addLink (link); - } - break; - } - } + /** Process metadata from a META tag */ + private static void processMeta(HtmlMetadata mdata, Attributes atts) { + String name = null; + String httpeq = null; + String content = null; + for (int i = 0; i < atts.getLength(); i++) { + String attname = atts.getLocalName(i); + String attval = atts.getValue(i); + if ("name".equals(attname)) { + name = attval; + } + if ("http-equiv".equals(attname)) { + httpeq = attval; + } + if ("content".equals(attname)) { + content = attval; + } } - - /** Process metadata from the IMG element. */ - private static void processImg (HtmlMetadata mdata, Attributes atts) - { - String alt = null; - String longdesc = null; - String src = null; - int height = -1; - int width = -1; - for (int i = 0; i < atts.getLength (); i++) { - String attname = atts.getLocalName (i); - String attval = atts.getValue (i); - if ("alt".equals (attname)) { - alt = attval; - } - else if ("src".equals (attname)) { - src = attval; - } - else if ("longdesc".equals (attname)) { - longdesc = attval; - } - else if ("height".equals (attname)) { - try { - height = Integer.parseInt(attval); - } - catch (Exception e) {} - } - else if ("width".equals (attname)) { - try { - width = Integer.parseInt(attval); - } - catch (Exception e) {} - } - } - List plist = new ArrayList (5); - if (alt != null) { - plist.add (new Property ("Alt", - PropertyType.STRING, - alt)); - } - if (longdesc != null) { - plist.add (new Property ("Longdesc", - PropertyType.STRING, - longdesc)); - } - if (src != null) { - plist.add (new Property ("Src", - PropertyType.STRING, - src)); - } - if (height >= 0) { - plist.add (new Property ("Height", - PropertyType.INTEGER, - new Integer (height))); - } - if (width >= 0) { - plist.add (new Property ("Width", - PropertyType.INTEGER, - new Integer (width))); - } - if (!plist.isEmpty ()) { - mdata.addImage(new Property ("Image", - PropertyType.PROPERTY, - PropertyArity.LIST, - plist)); - } + if (name != null || httpeq != null || content != null) { + List plist = new ArrayList(3); + if (name != null) { + plist.add(new Property("Name", PropertyType.STRING, name)); + } + if (httpeq != null) { + plist.add(new Property("Httpequiv", PropertyType.STRING, httpeq)); + } + if (content != null) { + plist.add(new Property("Content", PropertyType.STRING, content)); + } + mdata.addMeta(new Property("Meta", PropertyType.PROPERTY, PropertyArity.LIST, plist)); } - + } - /** Process metadata from the FRAME element. */ - private static void processFrame (HtmlMetadata mdata, Attributes atts) - { - String name = null; - String title = null; - String src = null; - String longdesc = null; - for (int i = 0; i < atts.getLength (); i++) { - String attname = atts.getLocalName (i); - String attval = atts.getValue (i); - if ("name".equals (attname)) { - name = attval; - } - else if ("title".equals (attname)) { - title = attval; - } - else if ("src".equals (attname)) { - src = attval; - } - else if ("longdesc".equals (attname)) { - longdesc = attval; - } - } - List plist = new ArrayList (4); - if (name != null) { - plist.add (new Property ("Name", - PropertyType.STRING, - name)); - } - if (title != null) { - plist.add (new Property ("Title", - PropertyType.STRING, - title)); + /** + * Process metadata from an A element. Only elements with an HREF attribute are of interest here. + * We ignore links to anchors. + */ + private static void processA(HtmlMetadata mdata, Attributes atts) { + for (int i = 0; i < atts.getLength(); i++) { + String attname = atts.getLocalName(i); + String attval = atts.getValue(i); + if ("href".equals(attname)) { + String link = attval; + if (link.length() > 0 && link.charAt(0) != '#') { + mdata.addLink(link); } - if (longdesc != null) { - plist.add (new Property ("Longdesc", - PropertyType.STRING, - longdesc)); - } - if (src != null) { - plist.add (new Property ("Src", - PropertyType.STRING, - src)); + break; + } + } + } + + /** Process metadata from the IMG element. */ + private static void processImg(HtmlMetadata mdata, Attributes atts) { + String alt = null; + String longdesc = null; + String src = null; + int height = -1; + int width = -1; + for (int i = 0; i < atts.getLength(); i++) { + String attname = atts.getLocalName(i); + String attval = atts.getValue(i); + if ("alt".equals(attname)) { + alt = attval; + } else if ("src".equals(attname)) { + src = attval; + } else if ("longdesc".equals(attname)) { + longdesc = attval; + } else if ("height".equals(attname)) { + try { + height = Integer.parseInt(attval); + } catch (Exception e) { } - if (!plist.isEmpty ()) { - mdata.addFrame(new Property ("Frame", - PropertyType.PROPERTY, - PropertyArity.LIST, - plist)); + } else if ("width".equals(attname)) { + try { + width = Integer.parseInt(attval); + } catch (Exception e) { } + } + } + List plist = new ArrayList(5); + if (alt != null) { + plist.add(new Property("Alt", PropertyType.STRING, alt)); + } + if (longdesc != null) { + plist.add(new Property("Longdesc", PropertyType.STRING, longdesc)); + } + if (src != null) { + plist.add(new Property("Src", PropertyType.STRING, src)); } + if (height >= 0) { + plist.add(new Property("Height", PropertyType.INTEGER, new Integer(height))); + } + if (width >= 0) { + plist.add(new Property("Width", PropertyType.INTEGER, new Integer(width))); + } + if (!plist.isEmpty()) { + mdata.addImage(new Property("Image", PropertyType.PROPERTY, PropertyArity.LIST, plist)); + } + } - /** Process metadata from the SCRIPT element. */ - private static void processScript (HtmlMetadata mdata, Attributes atts) - { - for (int i = 0; i < atts.getLength (); i++) { - String attname = atts.getLocalName (i); - String attval = atts.getValue (i); - if ("type".equals (attname)) { - String stype = attval; - if (stype.length() > 0 ) { - mdata.addScript (stype); - } - } - } + /** Process metadata from the FRAME element. */ + private static void processFrame(HtmlMetadata mdata, Attributes atts) { + String name = null; + String title = null; + String src = null; + String longdesc = null; + for (int i = 0; i < atts.getLength(); i++) { + String attname = atts.getLocalName(i); + String attval = atts.getValue(i); + if ("name".equals(attname)) { + name = attval; + } else if ("title".equals(attname)) { + title = attval; + } else if ("src".equals(attname)) { + src = attval; + } else if ("longdesc".equals(attname)) { + longdesc = attval; + } + } + List plist = new ArrayList(4); + if (name != null) { + plist.add(new Property("Name", PropertyType.STRING, name)); + } + if (title != null) { + plist.add(new Property("Title", PropertyType.STRING, title)); } + if (longdesc != null) { + plist.add(new Property("Longdesc", PropertyType.STRING, longdesc)); + } + if (src != null) { + plist.add(new Property("Src", PropertyType.STRING, src)); + } + if (!plist.isEmpty()) { + mdata.addFrame(new Property("Frame", PropertyType.PROPERTY, PropertyArity.LIST, plist)); + } + } - /** Processes metadata from the ABBR element. - * This will require the PCdata as well as the attributes, - * so we deposit the Property temporarily in the Metadata. - */ - private static void processAbbr (HtmlMetadata mdata, Attributes atts) - { - List lst = new ArrayList (2); - Property p = new Property ("abbr", - PropertyType.PROPERTY, - PropertyArity.LIST, - lst); - for (int i = 0; i < atts.getLength (); i++) { - String attname = atts.getLocalName (i); - String attval = atts.getValue (i); - if ("title".equals (attname) && attval.length() > 0) { - lst.add (new Property ("title", PropertyType.STRING, attval)); - } - // Note: The PCData should be stuck at the - // front of the list when we get it, as - // the "abbr" property. + /** Process metadata from the SCRIPT element. */ + private static void processScript(HtmlMetadata mdata, Attributes atts) { + for (int i = 0; i < atts.getLength(); i++) { + String attname = atts.getLocalName(i); + String attval = atts.getValue(i); + if ("type".equals(attname)) { + String stype = attval; + if (stype.length() > 0) { + mdata.addScript(stype); } - mdata.setPropUnderConstruction (p); + } } - - /** Processes metadata from the TITLE element. - * This will require PCData, so we deposit the Property - * temporarily in the Metadata. - */ - private static void processTitle (HtmlMetadata mdata, Attributes atts) - { - Property p = new Property ("title", - PropertyType.STRING, - ""); // store property with placeholder value - mdata.setPropUnderConstruction (p); - } - - /** Processes metadata from the CITE element. - * This will require PCData, so we deposit the Property - * temporarily in the Metadata. - */ - private static void processCite (HtmlMetadata mdata, Attributes atts) - { - Property p = new Property ("cite", - PropertyType.STRING, - ""); // store property with placeholder value - mdata.setPropUnderConstruction (p); + } + + /** + * Processes metadata from the ABBR element. This will require the PCdata as well as the + * attributes, so we deposit the Property temporarily in the Metadata. + */ + private static void processAbbr(HtmlMetadata mdata, Attributes atts) { + List lst = new ArrayList(2); + Property p = new Property("abbr", PropertyType.PROPERTY, PropertyArity.LIST, lst); + for (int i = 0; i < atts.getLength(); i++) { + String attname = atts.getLocalName(i); + String attval = atts.getValue(i); + if ("title".equals(attname) && attval.length() > 0) { + lst.add(new Property("title", PropertyType.STRING, attval)); + } + // Note: The PCData should be stuck at the + // front of the list when we get it, as + // the "abbr" property. } + mdata.setPropUnderConstruction(p); + } + + /** + * Processes metadata from the TITLE element. This will require PCData, so we deposit the Property + * temporarily in the Metadata. + */ + private static void processTitle(HtmlMetadata mdata, Attributes atts) { + Property p = + new Property("title", PropertyType.STRING, ""); // store property with placeholder value + mdata.setPropUnderConstruction(p); + } + /** + * Processes metadata from the CITE element. This will require PCData, so we deposit the Property + * temporarily in the Metadata. + */ + private static void processCite(HtmlMetadata mdata, Attributes atts) { + Property p = + new Property("cite", PropertyType.STRING, ""); // store property with placeholder value + mdata.setPropUnderConstruction(p); + } } diff --git a/jhove-modules/xml-hul/src/main/java/edu/harvard/hul/ois/jhove/module/xml/XmlDeclHandler.java b/jhove-modules/xml-hul/src/main/java/edu/harvard/hul/ois/jhove/module/xml/XmlDeclHandler.java index e9e042988..335162874 100644 --- a/jhove-modules/xml-hul/src/main/java/edu/harvard/hul/ois/jhove/module/xml/XmlDeclHandler.java +++ b/jhove-modules/xml-hul/src/main/java/edu/harvard/hul/ois/jhove/module/xml/XmlDeclHandler.java @@ -1,107 +1,84 @@ -/********************************************************************** - * Jhove - JSTOR/Harvard Object Validation Environment - * Copyright 2004 by JSTOR and the President and Fellows of Harvard College - **********************************************************************/ - +/** + * ******************************************************************** Jhove - JSTOR/Harvard Object + * Validation Environment Copyright 2004 by JSTOR and the President and Fellows of Harvard College + * ******************************************************************** + */ package edu.harvard.hul.ois.jhove.module.xml; import java.util.LinkedList; import java.util.List; - import org.xml.sax.ext.DeclHandler; /** - * - * This implementation of DeclHandler takes care of - * collecting entity declarations. - * - * @author Gary McGath + * This implementation of DeclHandler takes care of collecting entity declarations. * + * @author Gary McGath */ public class XmlDeclHandler implements DeclHandler { - private List _intEntityDeclarations; - private List _extEntityDeclarations; - - public XmlDeclHandler () - { - _intEntityDeclarations = new LinkedList<> (); - _extEntityDeclarations = new LinkedList<> (); - } - - - - /** - * Report an element type declaration. - * Does nothing. - * @see org.xml.sax.ext.DeclHandler#elementDecl(java.lang.String, java.lang.String) - */ - @Override - public void elementDecl(String arg0, String arg1) - { - } + private List _intEntityDeclarations; + private List _extEntityDeclarations; - /** - * Adds internal entity declarations to the entity declarations - * list in the form of a String[2], with element 0 being the - * name and element 1 being the value. - */ - @Override - public void internalEntityDecl(String name, String value) { - String[] decl = new String[2]; - decl[0] = name; - decl[1] = value; - _intEntityDeclarations.add (decl); - } + public XmlDeclHandler() { + _intEntityDeclarations = new LinkedList<>(); + _extEntityDeclarations = new LinkedList<>(); + } - /** - * Adds external entity declarations to the entity declarations - * list in the form of a String[3], with element 0 being the - * name, element 1 the public ID, and 2 the system ID. - */ - @Override - public void externalEntityDecl(String name, String publicID, String systemID) { - String[] decl = new String[3]; - decl[0] = name; - decl[1] = publicID; - decl[2] = systemID; - _extEntityDeclarations.add (decl); - } + /** + * Report an element type declaration. Does nothing. + * + * @see org.xml.sax.ext.DeclHandler#elementDecl(java.lang.String, java.lang.String) + */ + @Override + public void elementDecl(String arg0, String arg1) {} - /** Report an attribute type declaration. - * Does nothing. - * @see org.xml.sax.ext.DeclHandler#attributeDecl(java.lang.String, java.lang.String, java.lang.String, java.lang.String, java.lang.String) - */ - @Override - public void attributeDecl( - String arg0, - String arg1, - String arg2, - String arg3, - String arg4) - { - - } + /** + * Adds internal entity declarations to the entity declarations list in the form of a String[2], + * with element 0 being the name and element 1 being the value. + */ + @Override + public void internalEntityDecl(String name, String value) { + String[] decl = new String[2]; + decl[0] = name; + decl[1] = value; + _intEntityDeclarations.add(decl); + } + /** + * Adds external entity declarations to the entity declarations list in the form of a String[3], + * with element 0 being the name, element 1 the public ID, and 2 the system ID. + */ + @Override + public void externalEntityDecl(String name, String publicID, String systemID) { + String[] decl = new String[3]; + decl[0] = name; + decl[1] = publicID; + decl[2] = systemID; + _extEntityDeclarations.add(decl); + } - /** - * Returns list of entity declarations. Each list - * is an array String[2], giving the name and - * value respectively. - */ - public List getInternalEntityDeclarations () - { - return _intEntityDeclarations; - } + /** + * Report an attribute type declaration. Does nothing. + * + * @see org.xml.sax.ext.DeclHandler#attributeDecl(java.lang.String, java.lang.String, + * java.lang.String, java.lang.String, java.lang.String) + */ + @Override + public void attributeDecl(String arg0, String arg1, String arg2, String arg3, String arg4) {} + /** + * Returns list of entity declarations. Each list is an array String[2], giving the name and value + * respectively. + */ + public List getInternalEntityDeclarations() { + return _intEntityDeclarations; + } - /** - * Returns list of entity declarations. Each list - * is an array String[3], giving the name, - * public ID, and system ID respectively. - */ - public List getExternalEntityDeclarations () - { - return _extEntityDeclarations; - } + /** + * Returns list of entity declarations. Each list is an array String[3], giving the name, public + * ID, and system ID respectively. + */ + public List getExternalEntityDeclarations() { + return _extEntityDeclarations; + } } diff --git a/jhove-modules/xml-hul/src/main/java/edu/harvard/hul/ois/jhove/module/xml/XmlDeclStream.java b/jhove-modules/xml-hul/src/main/java/edu/harvard/hul/ois/jhove/module/xml/XmlDeclStream.java index 857ff52a4..2d3081a59 100644 --- a/jhove-modules/xml-hul/src/main/java/edu/harvard/hul/ois/jhove/module/xml/XmlDeclStream.java +++ b/jhove-modules/xml-hul/src/main/java/edu/harvard/hul/ois/jhove/module/xml/XmlDeclStream.java @@ -1,354 +1,301 @@ -/********************************************************************** - * Jhove - JSTOR/Harvard Object Validation Environment - * Copyright 2003-4 by JSTOR and the President and Fellows of Harvard College +/** + * ******************************************************************** Jhove - JSTOR/Harvard Object + * Validation Environment Copyright 2003-4 by JSTOR and the President and Fellows of Harvard College * - **********************************************************************/ - + *

******************************************************************** + */ package edu.harvard.hul.ois.jhove.module.xml; import java.io.*; import java.util.*; /** - * This class is layered under the InputSource for the XmlModule - * so that it can detect an XML declaration and character references, - * which aren't reported by other API's. - * - * This is called XmlDeclStream for historical reasons, though it's - * no longer limited to that function. - * + * This class is layered under the InputSource for the XmlModule so that it can detect an XML + * declaration and character references, which aren't reported by other API's. + * + *

This is called XmlDeclStream for historical reasons, though it's no longer limited to that + * function. + * * @author Gary McGath */ public class XmlDeclStream extends FilterInputStream { - private static final int CR = 0x0d; // '\r' - private static final int LF = 0x0a; // '\n' + private static final int CR = 0x0d; // '\r' + private static final int LF = 0x0a; // '\n' + + private StringBuffer declBuf; + private StringBuffer refBuf; + private boolean seenChars; + + private String _version; + private String _encoding; + private String _standalone; + + /* List of Integers giving character references */ + private List _charRefs; + + /* To try to determine line ending */ + protected boolean _lineEndCR; + protected boolean _lineEndLF; + protected boolean _lineEndCRLF; + protected int _prevChar; - - private StringBuffer declBuf; - private StringBuffer refBuf; - private boolean seenChars; + public XmlDeclStream(InputStream strm) { + super(strm); + declBuf = null; + seenChars = false; + _charRefs = new LinkedList<>(); - private String _version; - private String _encoding; - private String _standalone; - - /* List of Integers giving character references */ - private List _charRefs; + // No line end types have been discovered. + _lineEndCR = false; + _lineEndLF = false; + _lineEndCRLF = false; + _prevChar = 0; + } - /* To try to determine line ending */ - protected boolean _lineEndCR; - protected boolean _lineEndLF; - protected boolean _lineEndCRLF; - protected int _prevChar; - - public XmlDeclStream (InputStream strm) { - super (strm); - declBuf = null; - seenChars = false; - _charRefs = new LinkedList<> (); - - // No line end types have been discovered. - _lineEndCR = false; - _lineEndLF = false; - _lineEndCRLF = false; - _prevChar = 0; + /** Reads the next byte of data from this input stream. Processes bytes as it reads them. */ + @Override + public int read() throws IOException { + int retval = super.read(); + if (retval > 0) { + process(retval); } - - /** - * Reads the next byte of data from this input stream. - * Processes bytes as it reads them. - */ - @Override - public int read () throws IOException - { - int retval = super.read (); - if (retval > 0) { - process (retval); - } - return retval; + return retval; + } + + /** + * Reads up to byte.length bytes of data from this input stream into an array of + * bytes. Processes bytes as it reads them. + */ + @Override + public int read(byte[] b) throws IOException { + int nbytes = super.read(b); + for (int i = 0; i < nbytes; i++) { + process(b[i]); } - - - /** - * Reads up to byte.length bytes of data from this - * input stream into an array of bytes. - * Processes bytes as it reads them. - */ - @Override - public int read (byte[] b) throws IOException - { - int nbytes = super.read (b); - for (int i = 0; i < nbytes; i++) { - process (b[i]); - } - return nbytes; + return nbytes; + } + + /** + * Reads up to len bytes of data from this input stream into an array of bytes. + * Processes bytes as it reads them. + */ + @Override + public int read(byte[] b, int off, int len) throws IOException { + int nbytes = super.read(b, off, len); + for (int i = off; i < off + nbytes; i++) { + process(b[i]); } - - /** - * Reads up to len bytes of data from this - * input stream into an array of bytes. - * Processes bytes as it reads them. - */ - @Override - public int read (byte[] b, int off, int len) throws IOException - { - int nbytes = super.read (b, off, len); - for (int i = off; i < off + nbytes; i++) { - process (b[i]); + return nbytes; + } + + /** + * Returns the character references as a List of Integers. No sorting or elimination of duplicates + * is done; this is just all the character references in the order they occurred. + */ + public List getCharacterReferences() { + return _charRefs; + } + + /** Accessor functions. */ + + /** Returns the version string. May be null (though it shouldn't be in well-formed XML). */ + public String getVersion() { + return _version; + } + + /** Returns the encoding string. May be null. */ + public String getEncoding() { + return _encoding; + } + + /** Returns the standalone string. May be null. */ + public String getStandalone() { + return _standalone; + } + + /* Processes each byte which comes through, looking for an XML + * declaration. When it has a complete one, parses out the + * parameters and makes them available. + * + * The XML declaration must be the first thing in the file. + */ + private void process(int b) { + /* Determine the line ending type(s). */ + checkLineEnd(b); + _prevChar = b; + + if (!seenChars || declBuf != null) { + if (declBuf == null && b == '<') { + declBuf = new StringBuffer("<"); + } else if (declBuf != null) { + declBuf.append((char) b); + if ((char) b == '>') { + processDecl(); + declBuf = null; } - - return nbytes; - } - - - /** - * Returns the character references as a List - * of Integers. No sorting or elimination of - * duplicates is done; this is just all the - * character references in the order they occurred. - */ - public List getCharacterReferences () - { - return _charRefs; - } - - - - /** Accessor functions. */ - - /** Returns the version string. May be null (though it shouldn't - * be in well-formed XML). */ - public String getVersion () - { - return _version; + } } - - - /** Returns the encoding string. May be null. */ - public String getEncoding () - { - return _encoding; + if (refBuf == null && b == '&') { + refBuf = new StringBuffer("&"); + } else if (refBuf != null) { + if (refBuf.length() == 1 && b != '#') { + // If & isn't followed by #, it's not a character + // reference. + refBuf = null; + } else if (b == ';') { + processRef(); + refBuf = null; + } else { + refBuf.append((char) b); + } } - + seenChars = true; + } - /** Returns the standalone string. May be null. */ - public String getStandalone () - { - return _standalone; + /* We have the first thing to be enclosed in angle + * brackets in declBuf. See if it's an XML declaration, + * and if so, extract the interesting information. + */ + private void processDecl() { + String decl = declBuf.toString(); + if (!decl.startsWith("")) { + declBuf = null; + } else { + // get version, encoding, standalone + int off; + int off1 = 0; + off = decl.indexOf("version"); + if (off > 0) { + _version = extractParam(decl, off); + off1 = off; + } + + // Use of off1 enforces order of attributes + off = decl.indexOf("encoding", off1); + if (off > 0) { + _encoding = extractParam(decl, off); + off1 = off; + } + + off = decl.indexOf("standalone", off1); + if (off > 0) { + _standalone = extractParam(decl, off); + } } + } - - /* Processes each byte which comes through, looking for an XML - * declaration. When it has a complete one, parses out the - * parameters and makes them available. - * - * The XML declaration must be the first thing in the file. - */ - private void process (int b) - { - /* Determine the line ending type(s). */ - checkLineEnd(b); - _prevChar = b; - - if (!seenChars || declBuf != null) { - if (declBuf == null && b == '<') { - declBuf = new StringBuffer ("<"); - } - else if (declBuf != null) { - declBuf.append ((char) b); - if ((char) b == '>') { - processDecl (); - declBuf = null; - } - } + /* We have a character reference -- or at least something + * that looks vaguely like one -- in refBuf. This includes + * the initial &# but not the final semicolon. + * + * According to the w3c documentation, the 'x' which indicates + * a hexadecimal value must be lower case, but the + * hexadecimal digits may be upper or lower case. + */ + private void processRef() { + boolean isHex = (refBuf.charAt(2) == 'x'); + int val = 0; + // Copy refBuf to a local variable so we can make sure + // it gets nulled however we return. + StringBuffer refBuf1 = refBuf; + refBuf = null; + if (isHex) { + for (int i = 3; i < refBuf1.length(); i++) { + char ch = Character.toUpperCase(refBuf1.charAt(i)); + if (ch >= 'A' && ch <= 'F') { + val = 16 * val + (ch - 'A' + 10); + } else if (ch >= '0' && ch <= '9') { + val = 16 * val + (ch - '0'); + } else { + return; // invalid character in hex ref } - if (refBuf == null && b == '&') { - refBuf = new StringBuffer ("&"); + } + } else { + // better be decimal + for (int i = 2; i < refBuf1.length(); i++) { + char ch = refBuf1.charAt(i); + if (ch >= '0' && ch <= '9') { + val = 10 * val + (ch - '0'); + } else { + return; // invalid character in hex ref } - else if (refBuf != null) { - if (refBuf.length() == 1 && b != '#') { - // If & isn't followed by #, it's not a character - // reference. - refBuf = null; - } - else if (b == ';') { - processRef (); - refBuf = null; - } - else { - refBuf.append ((char) b); - } - } - seenChars = true; + } } - - /* We have the first thing to be enclosed in angle - * brackets in declBuf. See if it's an XML declaration, - * and if so, extract the interesting information. - */ - private void processDecl () - { - String decl = declBuf.toString (); - if (!decl.startsWith ("")) { - declBuf = null; - } - else { - // get version, encoding, standalone - int off; - int off1 = 0; - off = decl.indexOf ("version"); - if (off > 0) { - _version = extractParam (decl, off); - off1 = off; - } - - // Use of off1 enforces order of attributes - off = decl.indexOf ("encoding", off1); - if (off > 0) { - _encoding = extractParam (decl, off); - off1 = off; - } - - off = decl.indexOf ("standalone", off1); - if (off > 0) { - _standalone = extractParam (decl, off); - } - } + _charRefs.add(new Integer(val)); + } + + /* extract a parameter (after an equal sign) + * from a string, after the offset off. */ + private static String extractParam(String str, int off) { + int equIdx = str.indexOf('=', off); + if (equIdx == -1) { + return null; } - - - /* We have a character reference -- or at least something - * that looks vaguely like one -- in refBuf. This includes - * the initial &# but not the final semicolon. - * - * According to the w3c documentation, the 'x' which indicates - * a hexadecimal value must be lower case, but the - * hexadecimal digits may be upper or lower case. - */ - private void processRef () - { - boolean isHex = (refBuf.charAt (2) == 'x'); - int val = 0; - // Copy refBuf to a local variable so we can make sure - // it gets nulled however we return. - StringBuffer refBuf1 = refBuf; - refBuf = null; - if (isHex) { - for (int i = 3; i < refBuf1.length (); i++) { - char ch = Character.toUpperCase (refBuf1.charAt (i)); - if (ch >= 'A' && ch <= 'F') { - val = 16 * val + (ch - 'A' + 10); - } - else if (ch >= '0' && ch <= '9') { - val = 16 * val + (ch - '0'); - } - else { - return; // invalid character in hex ref - } - } - } - else { - // better be decimal - for (int i = 2; i < refBuf1.length (); i++) { - char ch = refBuf1.charAt (i); - if (ch >= '0' && ch <= '9') { - val = 10 * val + (ch - '0'); - } - else { - return; // invalid character in hex ref - } - } + // The parameter may be in single or double quotes, + boolean singleQuote = false; + boolean doubleQuote = false; + int startOff = -1; + for (int i = equIdx + 1; i < str.length(); i++) { + char ch = str.charAt(i); + if (Character.isWhitespace(ch)) { + if (startOff < 0) { + continue; + } else if (!singleQuote && !doubleQuote) { + // white space, and not in quotes. + return str.substring(startOff, i + 1); } - _charRefs.add (new Integer (val)); - } - - - - /* extract a parameter (after an equal sign) - * from a string, after the offset off. */ - private static String extractParam (String str, int off) - { - int equIdx = str.indexOf ('=', off); - if (equIdx == -1) { - return null; + } else if (ch == '\'' && !doubleQuote) { + if (!singleQuote) { + // Start of single-quoted string + singleQuote = true; + startOff = i + 1; + } else { + // End of single-quoted string + return str.substring(startOff, i); } - // The parameter may be in single or double quotes, - boolean singleQuote = false; - boolean doubleQuote = false; - int startOff = -1; - for (int i = equIdx + 1; i < str.length(); i++) { - char ch = str.charAt (i); - if (Character.isWhitespace(ch)) { - if (startOff < 0) { - continue; - } - else if (!singleQuote && !doubleQuote) { - // white space, and not in quotes. - return str.substring(startOff, i + 1); - } - } - else if (ch == '\'' && !doubleQuote) { - if (!singleQuote) { - // Start of single-quoted string - singleQuote = true; - startOff = i + 1; - } - else { - // End of single-quoted string - return str.substring (startOff, i); - } - } - else if (ch == '"' && !singleQuote) { - if (!doubleQuote) { - // Start of double-quoted string - doubleQuote = true; - startOff = i + 1; - } - else { - // End of double-quoted string - return str.substring (startOff, i); - } - } - else if (startOff < 0) { - // Non-whitespace character, start of unquoted string - startOff = i; - } + } else if (ch == '"' && !singleQuote) { + if (!doubleQuote) { + // Start of double-quoted string + doubleQuote = true; + startOff = i + 1; + } else { + // End of double-quoted string + return str.substring(startOff, i); } - return null; // fell off end without finding a valid string + } else if (startOff < 0) { + // Non-whitespace character, start of unquoted string + startOff = i; + } } - /* Accumulate information about line endings. ch is the - current character, and _prevChar the one before it. */ - protected void checkLineEnd (int ch) - { - if (ch == LF) { - if (_prevChar == CR) { - _lineEndCRLF = true; - } - else { - _lineEndLF = true; - } - } - else if (_prevChar == CR) { - _lineEndCR = true; - } - } + return null; // fell off end without finding a valid string + } + /* Accumulate information about line endings. ch is the + current character, and _prevChar the one before it. */ + protected void checkLineEnd(int ch) { + if (ch == LF) { + if (_prevChar == CR) { + _lineEndCRLF = true; + } else { + _lineEndLF = true; + } + } else if (_prevChar == CR) { + _lineEndCR = true; + } + } - public String getKindOfLineEnd() { - if (_lineEndCR || _lineEndLF || _lineEndCRLF) { - if (_lineEndCRLF) { - return "CRLF"; - } - if (_lineEndCR) { - return "CR"; - } - if (_lineEndLF) { - return "LF"; - } - } - return null; - } - - + public String getKindOfLineEnd() { + if (_lineEndCR || _lineEndLF || _lineEndCRLF) { + if (_lineEndCRLF) { + return "CRLF"; + } + if (_lineEndCR) { + return "CR"; + } + if (_lineEndLF) { + return "LF"; + } + } + return null; + } } diff --git a/jhove-modules/xml-hul/src/main/java/edu/harvard/hul/ois/jhove/module/xml/XmlLexicalHandler.java b/jhove-modules/xml-hul/src/main/java/edu/harvard/hul/ois/jhove/module/xml/XmlLexicalHandler.java index d67bdeee1..057b8efaf 100644 --- a/jhove-modules/xml-hul/src/main/java/edu/harvard/hul/ois/jhove/module/xml/XmlLexicalHandler.java +++ b/jhove-modules/xml-hul/src/main/java/edu/harvard/hul/ois/jhove/module/xml/XmlLexicalHandler.java @@ -1,132 +1,108 @@ -/********************************************************************** - * Jhove - JSTOR/Harvard Object Validation Environment - * Copyright 2004 by JSTOR and the President and Fellows of Harvard College - **********************************************************************/ - +/** + * ******************************************************************** Jhove - JSTOR/Harvard Object + * Validation Environment Copyright 2004 by JSTOR and the President and Fellows of Harvard College + * ******************************************************************** + */ package edu.harvard.hul.ois.jhove.module.xml; import java.util.HashSet; import java.util.LinkedList; import java.util.List; import java.util.Set; - import org.xml.sax.ext.LexicalHandler; /** - * - * This implementation of LexicalHandler takes care of - * comments, DTD's, entities and other stuff for XmlModule. - * The caller has to make sure the LexicalHandler property - * is supported by the SAX implementation, and set that - * property to this class. - * - * @author Gary McGath + * This implementation of LexicalHandler takes care of comments, DTD's, entities and other stuff for + * XmlModule. The caller has to make sure the LexicalHandler property is supported by the SAX + * implementation, and set that property to this class. * + * @author Gary McGath */ public class XmlLexicalHandler implements LexicalHandler { - private List _comments; - private Set _entityNames; - public XmlLexicalHandler () - { - _comments = new LinkedList<> (); - _entityNames = new HashSet<> (); - } - - - - /** - * Report the end of a CDATA section. - * Does nothing. - * @see org.xml.sax.ext.LexicalHandler#endCDATA() - */ - @Override - public void endCDATA() { - // no action necessary - } - - /** - * Report the end of DTD declarations. - * Does nothing. - * @see org.xml.sax.ext.LexicalHandler#endDTD() - */ - @Override - public void endDTD() { - - } - - /** - * Report the start of a CDATA section. - * Does nothing. - * @see org.xml.sax.ext.LexicalHandler#startCDATA() - */ - @Override - public void startCDATA() { - // no action necessary - } - - /** - * Gathers comments into the comments list. - * - * @see org.xml.sax.ext.LexicalHandler#comment(char[], int, int) - */ - @Override - public void comment(char[] text, int start, int length) { - _comments.add (String.copyValueOf (text, start, length)); - } - - /** - * Accumulates entity names into the entity set. This will be - * used for determining which entities are actually used. - * - * @see org.xml.sax.ext.LexicalHandler#startEntity(java.lang.String) - */ - @Override - public void startEntity(String name) - { - _entityNames.add (name); - } - - /** - * Report the end of an entity. - * Does nothing. - * - * @see org.xml.sax.ext.LexicalHandler#endEntity(java.lang.String) - */ - @Override - public void endEntity(String name) - { - // No action necessary - } - - - /** - * Report the start of DTD declarations, if any. - * Does nothing. - * @see org.xml.sax.ext.LexicalHandler#startDTD(java.lang.String, java.lang.String, java.lang.String) - */ - @Override - public void startDTD(String arg0, String arg1, String arg2) - { - - } - - - /** - * Returns the value of the comments list, which is - * a List of Strings. - */ - public List getComments () - { - return _comments; - } - - - /** - * Returns the Set of entity names. - */ - public Set getEntityNames () - { - return _entityNames; - } + private List _comments; + private Set _entityNames; + + public XmlLexicalHandler() { + _comments = new LinkedList<>(); + _entityNames = new HashSet<>(); + } + + /** + * Report the end of a CDATA section. Does nothing. + * + * @see org.xml.sax.ext.LexicalHandler#endCDATA() + */ + @Override + public void endCDATA() { + // no action necessary + } + + /** + * Report the end of DTD declarations. Does nothing. + * + * @see org.xml.sax.ext.LexicalHandler#endDTD() + */ + @Override + public void endDTD() {} + + /** + * Report the start of a CDATA section. Does nothing. + * + * @see org.xml.sax.ext.LexicalHandler#startCDATA() + */ + @Override + public void startCDATA() { + // no action necessary + } + + /** + * Gathers comments into the comments list. + * + * @see org.xml.sax.ext.LexicalHandler#comment(char[], int, int) + */ + @Override + public void comment(char[] text, int start, int length) { + _comments.add(String.copyValueOf(text, start, length)); + } + + /** + * Accumulates entity names into the entity set. This will be used for determining which entities + * are actually used. + * + * @see org.xml.sax.ext.LexicalHandler#startEntity(java.lang.String) + */ + @Override + public void startEntity(String name) { + _entityNames.add(name); + } + + /** + * Report the end of an entity. Does nothing. + * + * @see org.xml.sax.ext.LexicalHandler#endEntity(java.lang.String) + */ + @Override + public void endEntity(String name) { + // No action necessary + } + + /** + * Report the start of DTD declarations, if any. Does nothing. + * + * @see org.xml.sax.ext.LexicalHandler#startDTD(java.lang.String, java.lang.String, + * java.lang.String) + */ + @Override + public void startDTD(String arg0, String arg1, String arg2) {} + + /** Returns the value of the comments list, which is a List of Strings. */ + public List getComments() { + return _comments; + } + + /** Returns the Set of entity names. */ + public Set getEntityNames() { + return _entityNames; + } } diff --git a/jhove-modules/xml-hul/src/main/java/edu/harvard/hul/ois/jhove/module/xml/XmlModuleHandler.java b/jhove-modules/xml-hul/src/main/java/edu/harvard/hul/ois/jhove/module/xml/XmlModuleHandler.java index 02788a70f..7390c5b03 100644 --- a/jhove-modules/xml-hul/src/main/java/edu/harvard/hul/ois/jhove/module/xml/XmlModuleHandler.java +++ b/jhove-modules/xml-hul/src/main/java/edu/harvard/hul/ois/jhove/module/xml/XmlModuleHandler.java @@ -1,10 +1,15 @@ -/********************************************************************** - * Jhove - JSTOR/Harvard Object Validation Environment - * Copyright 2004-2007 by JSTOR and the President and Fellows of Harvard College - **********************************************************************/ - +/** + * ******************************************************************** Jhove - JSTOR/Harvard Object + * Validation Environment Copyright 2004-2007 by JSTOR and the President and Fellows of Harvard + * College ******************************************************************** + */ package edu.harvard.hul.ois.jhove.module.xml; +import edu.harvard.hul.ois.jhove.ErrorMessage; +import edu.harvard.hul.ois.jhove.InfoMessage; +import edu.harvard.hul.ois.jhove.Message; +import edu.harvard.hul.ois.jhove.messages.JhoveMessage; +import edu.harvard.hul.ois.jhove.messages.JhoveMessages; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; @@ -18,539 +23,485 @@ import java.util.List; import java.util.Map; import java.util.Set; - import org.xml.sax.Attributes; import org.xml.sax.InputSource; import org.xml.sax.SAXException; import org.xml.sax.SAXParseException; import org.xml.sax.helpers.DefaultHandler; -// import java.io.*; -import edu.harvard.hul.ois.jhove.ErrorMessage; -import edu.harvard.hul.ois.jhove.InfoMessage; -import edu.harvard.hul.ois.jhove.Message; -import edu.harvard.hul.ois.jhove.messages.JhoveMessage; -import edu.harvard.hul.ois.jhove.messages.JhoveMessages; - /** - * * This handler does the parsing work of the XML module. * * @author Gary McGath - * */ public class XmlModuleHandler extends DefaultHandler { - /* List of entities String[2], { public ID, system ID} */ - private List _entities; - - /* Map of namespaces, prefix (String) to URI (String) */ - private Map _namespaces; - - /* - * List of processing instructions. Each element - * is an array of two strings, giving the target - * and data respectively. - */ - private List _processingInsts; - - /* List of generated Messages. */ - private List _messages; - - /* Validity flag. */ - private boolean _valid; - - /* Qualified name of the root element. */ - private String _root; - - /* URI for DTD specification */ - private String _dtdURI; - - /* - * List of schema URI's. Each element is a String[2], - * consisting of the namespace URI and the schema location. - */ - private List _schemas; - - /* - * List of unparsed entities. Each is an array String[4]; - * name, public ID, system ID and notation name - * respectively. - */ - private List _unparsedEntities; - - /* Error counter. */ - private int _nErrors; - - /* - * Notations list. Each is an array String[3]: - * name, public ID, and system ID. - */ - private List _notations; - - /* - * List of all the attributes. This is used to - * check on the use of unparsed entities. - */ - private Set _attributeVals; - - /* Limit on number of errors to report. */ - private static final int MAXERRORS = 2000; - - /* - * XHTML flag, only for XHTML documents referred - * by the HTML module. - */ - private boolean _xhtmlFlag; - - /* HTMLMetadata object; used only with XHTML documents. */ - private HtmlMetadata _htmlMetadata; - - /* - * Flag set if we've seen any components. This is an indirect - * way of checking if the "signature" (the XML declaration) - * has been seen. - */ - private boolean _sigFlag; - - /* Map from URIs to local schema files */ - private Map _localSchemas; - - /** - * Constructor. - */ - public XmlModuleHandler() { - _xhtmlFlag = false; - _htmlMetadata = null; - _entities = new LinkedList<>(); - _namespaces = new HashMap<>(); - _processingInsts = new LinkedList<>(); - _messages = new LinkedList<>(); - _attributeVals = new HashSet<>(); - _dtdURI = null; - _root = null; - _valid = true; - _nErrors = 0; - _schemas = new LinkedList<>(); - _unparsedEntities = new LinkedList<>(); - _notations = new LinkedList<>(); - _sigFlag = false; - } - - /** - * Sets the value of the XHTML flag. Special properties - * are extracted if this is an XHTML document. - */ - public void setXhtmlFlag(boolean flag) { - _xhtmlFlag = flag; - } - - /** - * Sets a map of schema URIs to local files. This information - * comes from jhove.conf parameters. - */ - public void setLocalSchemas(Map schemas) { - _localSchemas = schemas; - } - - /** - * Returns the HTML metadata object. Will be non-null only - * for a document recognized as XHTML. - */ - public HtmlMetadata getHtmlMetadata() { - return _htmlMetadata; - } - - /** - * Looks for the first element encountered. Stores - * its name as the value to be returned by getRoot, - * qualified name by preference, local name if the - * qualified name isn't available. - */ - @Override - public void startElement(String namespaceURI, String localName, - String qualifiedName, Attributes atts) { - // The first element we encounter is the root. - // Save it. - if (_root == null) { - _sigFlag = true; - if (!"".equals(qualifiedName)) { - _root = qualifiedName; - } else { - _root = localName; - } - } - if ((namespaceURI != null) && (namespaceURI.length() != 0)) { - SchemaInfo schi = new SchemaInfo(); - schi.namespaceURI = namespaceURI; - schi.location = ""; - if (!hasSchemaURI(schi)) { - _schemas.add(schi); - } - } - if (atts != null) { - int natts = atts.getLength(); - for (int i = 0; i < natts; i++) { - String name = atts.getLocalName(i); - String namespace = atts.getURI(i); // namespace URI - String val = atts.getValue(i); - if ("http://www.w3.org/2001/XMLSchema-instance" - .equals(namespace)) { - SchemaInfo schInfo = new SchemaInfo(); - if ("schemaLocation".equals(name)) { - /* - * val should consist of two tokens, giving the - * URI and the location respectively. - */ - String[] toks = val.split("\\s", 2); - /* - * Could be a length 0 or 1 array in pathological - * cases, so convert it to a length-2 array. - * Note that while the schemaLocation attribute - * SHOULD have two white-space separated elements, - * this may not be the case, so always check the - * array length before referencing its elements. - */ - if (toks.length > 0 && toks[0] != null) { - schInfo.namespaceURI = toks[0].trim(); - } else { - schInfo.namespaceURI = ""; - } - if (toks.length > 1 && toks[1] != null) { - schInfo.location = toks[1].trim(); - } else { - schInfo.location = ""; - } - if (!hasSchemaURI(schInfo)) { - _schemas.add(schInfo); - } - } - if ("noNamespaceSchemaLocation".equals(name)) { - schInfo.location = "[None]"; - schInfo.namespaceURI = val; - if (!hasSchemaURI(schInfo)) { - _schemas.add(schInfo); - } - } - } - // Collect all attribute values. - _attributeVals.add(val); - } - } - if (_xhtmlFlag) { - if (_htmlMetadata == null) { - _htmlMetadata = new HtmlMetadata(); - } - XhtmlProcessing.processElement(localName, qualifiedName, atts, - _htmlMetadata); - } - } - - /** - * The only action taken here is some bookkeeping in connection - * with the HTML metadata. - */ - @Override - public void endElement(String namespaceURI, String localName, - String qName) { - if (_htmlMetadata != null) { - _htmlMetadata.finishPropUnderConstruction(); - } - } - - /** - * Processes PCData characters. This does things only - * in connection with properties under construction in - * HTML metadata. - */ - @Override - public void characters(char[] ch, int start, int length) { - if (_htmlMetadata != null - && _htmlMetadata.getPropUnderConstruction() != null) { - _htmlMetadata.addToPropUnderConstruction(ch, start, length); - } - } - - /** - * Begin the scope of a prefix-URI Namespace mapping. - * Prefixes mappings are stored in _namespaces. - */ - @Override - public void startPrefixMapping(String prefix, String uri) { - // THL we want the root namespace even if it declares no prefix !!! - // if (!"".equals (prefix)) { - _namespaces.put(prefix, uri); - // } - } - - /** - * Handles a processing instruction. Adds it to - * the list that will be returned by getProcessingInstructions. - * Each element of the list is an array of two Strings. Element 0 of - * the array is the target, and element 1 is the data. - */ - @Override - public void processingInstruction(String target, String data) { - _sigFlag = true; - if (data == null) { - data = ""; - } - ProcessingInstructionInfo pi = new ProcessingInstructionInfo(); - pi.target = target; - pi.data = data; - _processingInsts.add(pi); - } - - /** - * Puts all notations into the notation list. A list entry - * is a String[3], consisting of name, public ID, and system - * ID. - */ - @Override - public void notationDecl(String name, String publicID, String systemID) { - String[] notArr = new String[3]; - notArr[0] = name; - notArr[1] = publicID; - notArr[2] = systemID; - _notations.add(notArr); - } - - /** - * Overrides standard resolveEntity. First looks for DTD and - * entity files that are stored as resources, and uses those - * if available. (Faster and more reliable than grabbing them - * over the Net.) If that fails, calls the superclass's resolveEntity. - * Regardless, it then looks for anything - * that appears to be a DTD and puts it in the DTD URI field. - * If the superclass's attempt to resolve the entity results in - * an IOException, we just ignore it. - * - */ - @Override - public InputSource resolveEntity(String publicId, String systemId) - throws SAXException - - { - // Check any custom mapping from the config - File fil = _localSchemas.get(systemId.toLowerCase()); - if (fil != null) { - try { - FileInputStream inStrm = new FileInputStream(fil); - return new InputSource(inStrm); - } catch (FileNotFoundException e) { - } - } - - // Do special-case checking for the XHTML DTD's - if (!_xhtmlFlag && DTDMapper.isXHTMLDTD(publicId)) { - _xhtmlFlag = true; - } - InputSource ent = DTDMapper.publicIDToFile(publicId); - if (ent == null) { - try { - URL obj = new URL(systemId); - HttpURLConnection conn = (HttpURLConnection) obj - .openConnection(); - - int status = conn.getResponseCode(); - if (status == HttpURLConnection.HTTP_MOVED_TEMP - || status == HttpURLConnection.HTTP_MOVED_PERM - || status == HttpURLConnection.HTTP_SEE_OTHER) { - - String newUrl = conn.getHeaderField("Location"); - conn = (HttpURLConnection) new URL(newUrl).openConnection(); - ent = new InputSource(conn.getInputStream()); - } - - } catch (Exception e) { - // Depending on the JDK version, super.resolveEntity - // may or may not be formally capable of throwing an - // IOException. - // This hack allows compatibility in either case. - throw new SAXException(e); - } - } else { - // A little magic so SAX won't give up in advance on - // relative URI's. - ent.setSystemId("http://hul.harvard.edu/hul"); - } - - // Report in entity properties - EntityInfo entArr = new EntityInfo(); - entArr.publicID = publicId; - entArr.systemID = systemId; - _entities.add(entArr); - /* - * Assume that the first system ID in the file with a .dtd - * extension is the actual DTD - */ - if (systemId.endsWith(".dtd") && _dtdURI == null) { - _dtdURI = systemId; - } - return ent; - } - - /** - * Picks up unparsed entity declarations, after calling the - * superclass's unparsedEntityDecl, and puts their information - * into the unparsed entity declaration list as an array of - * four strings: [ name, publicId, systemId, notationName]. - * Null values are converted into empty strings. - */ - @Override - public void unparsedEntityDecl(String name, String publicId, - String systemId, String notationName) throws SAXException { - super.unparsedEntityDecl(name, publicId, systemId, notationName); - String[] info = new String[4]; - info[0] = name == null ? "" : name; - info[1] = publicId == null ? "" : publicId; - info[2] = systemId == null ? "" : systemId; - info[3] = notationName == null ? "" : notationName; - _unparsedEntities.add(info); - } - - /** - * Processes a warning. We just add an InfoMessage. - */ - @Override - public void warning(SAXParseException e) { - JhoveMessage message = JhoveMessages.getMessageInstance( - MessageConstants.XML_HUL_1.getId(), - MessageFormat.format(MessageConstants.XML_HUL_1.getMessage(), - e.getMessage().toString())); - _messages.add(new InfoMessage(message)); - } - - /** - * Processes a parsing exception. An ill-formed piece - * of XML will get a fatalError (I think), so we can assume - * that any error here indicates only invalidity. - */ - @Override - public void error(SAXParseException e) { - _valid = false; - if (_nErrors == MAXERRORS) { - JhoveMessage message = JhoveMessages.getMessageInstance( - MessageConstants.XML_HUL_2.getId(), - MessageFormat.format( - MessageConstants.XML_HUL_2.getMessage(), - Integer.valueOf(MAXERRORS))); - _messages.add(new InfoMessage(message)); - } else if (_nErrors < MAXERRORS) { - int line = e.getLineNumber(); - int col = e.getColumnNumber(); - JhoveMessage message = JhoveMessages.getMessageInstance( - MessageConstants.XML_HUL_1.getId(), - MessageFormat.format( - MessageConstants.XML_HUL_1.getMessage(), - e.getMessage().toString())); - _messages.add(new ErrorMessage(message, - "Line = " + line + ", Column = " + col)); - } - ++_nErrors; - } - - /** - * Returns the set of attribute values. - */ - public Set getAttributeValues() { - return _attributeVals; - } - - /** - * Returns the list of schemas. The elements of the list - * are Strings, giving the URI's for the schemas. - */ - public List getSchemas() { - return _schemas; - } - - /** - * Returns the list of unparsed entities. The elements of the - * list are arrays of four Strings, giving the name, public - * ID, system ID and notation name respectively. - */ - public List getUnparsedEntities() { - return _unparsedEntities; - } - - /** - * Returns the map of prefixes to namespaces. The keys - * and values are Strings. - */ - public Map getNamespaces() { - return _namespaces; - } - - /** - * Returns the DTD URI. May be null. - */ - public String getDTDURI() { - return _dtdURI; - } - - /** - * Returns the List of processing instructions. Each element - * is an array of two strings, giving the target - * and data respectively. - */ - public List getProcessingInstructions() { - return _processingInsts; - } - - /** - * Returns the list of notations. Each is an array String[3]: - * name, public ID, and system ID. - */ - public List getNotations() { - return _notations; - } - - /** Returns the qualified name of the root element. */ - public String getRoot() { - return _root; - } - - /** Returns the List of messages generated during the parse. */ - public List getMessages() { - return _messages; - } - - /** - * Returns the validity state. If error - * has been called, the return value will be false. - */ - public boolean isValid() { - return _valid; - } - - /** - * Returns true if we have seen an element or a - * processing instruction, which implies that we've seen an - * XML declaration. - */ - public boolean getSigFlag() { - return _sigFlag; - } - - /* - * Check if we already know about this schema URI. If we do but the new info - * provides - * a location, quietly stuff the old one into a sewer and pretend it was - * never there. - */ - public boolean hasSchemaURI(SchemaInfo newinfo) { - Iterator schmiter = _schemas.iterator(); - while (schmiter.hasNext()) { - SchemaInfo schmi = schmiter.next(); - if (newinfo.namespaceURI.equals(schmi.namespaceURI)) { - if (schmi.location.isEmpty() && !newinfo.location.isEmpty()) { - _schemas.remove(schmi); - return false; // we like the new info better - } - return true; - } - } - return false; - } + /* List of entities String[2], { public ID, system ID} */ + private List _entities; + + /* Map of namespaces, prefix (String) to URI (String) */ + private Map _namespaces; + + /* + * List of processing instructions. Each element + * is an array of two strings, giving the target + * and data respectively. + */ + private List _processingInsts; + + /* List of generated Messages. */ + private List _messages; + + /* Validity flag. */ + private boolean _valid; + + /* Qualified name of the root element. */ + private String _root; + + /* URI for DTD specification */ + private String _dtdURI; + + /* + * List of schema URI's. Each element is a String[2], + * consisting of the namespace URI and the schema location. + */ + private List _schemas; + + /* + * List of unparsed entities. Each is an array String[4]; + * name, public ID, system ID and notation name + * respectively. + */ + private List _unparsedEntities; + + /* Error counter. */ + private int _nErrors; + + /* + * Notations list. Each is an array String[3]: + * name, public ID, and system ID. + */ + private List _notations; + + /* + * List of all the attributes. This is used to + * check on the use of unparsed entities. + */ + private Set _attributeVals; + + /* Limit on number of errors to report. */ + private static final int MAXERRORS = 2000; + + /* + * XHTML flag, only for XHTML documents referred + * by the HTML module. + */ + private boolean _xhtmlFlag; + + /* HTMLMetadata object; used only with XHTML documents. */ + private HtmlMetadata _htmlMetadata; + + /* + * Flag set if we've seen any components. This is an indirect + * way of checking if the "signature" (the XML declaration) + * has been seen. + */ + private boolean _sigFlag; + + /* Map from URIs to local schema files */ + private Map _localSchemas; + + /** Constructor. */ + public XmlModuleHandler() { + _xhtmlFlag = false; + _htmlMetadata = null; + _entities = new LinkedList<>(); + _namespaces = new HashMap<>(); + _processingInsts = new LinkedList<>(); + _messages = new LinkedList<>(); + _attributeVals = new HashSet<>(); + _dtdURI = null; + _root = null; + _valid = true; + _nErrors = 0; + _schemas = new LinkedList<>(); + _unparsedEntities = new LinkedList<>(); + _notations = new LinkedList<>(); + _sigFlag = false; + } + + /** + * Sets the value of the XHTML flag. Special properties are extracted if this is an XHTML + * document. + */ + public void setXhtmlFlag(boolean flag) { + _xhtmlFlag = flag; + } + + /** + * Sets a map of schema URIs to local files. This information comes from jhove.conf parameters. + */ + public void setLocalSchemas(Map schemas) { + _localSchemas = schemas; + } + + /** Returns the HTML metadata object. Will be non-null only for a document recognized as XHTML. */ + public HtmlMetadata getHtmlMetadata() { + return _htmlMetadata; + } + + /** + * Looks for the first element encountered. Stores its name as the value to be returned by + * getRoot, qualified name by preference, local name if the qualified name isn't available. + */ + @Override + public void startElement( + String namespaceURI, String localName, String qualifiedName, Attributes atts) { + // The first element we encounter is the root. + // Save it. + if (_root == null) { + _sigFlag = true; + if (!"".equals(qualifiedName)) { + _root = qualifiedName; + } else { + _root = localName; + } + } + if ((namespaceURI != null) && (namespaceURI.length() != 0)) { + SchemaInfo schi = new SchemaInfo(); + schi.namespaceURI = namespaceURI; + schi.location = ""; + if (!hasSchemaURI(schi)) { + _schemas.add(schi); + } + } + if (atts != null) { + int natts = atts.getLength(); + for (int i = 0; i < natts; i++) { + String name = atts.getLocalName(i); + String namespace = atts.getURI(i); // namespace URI + String val = atts.getValue(i); + if ("http://www.w3.org/2001/XMLSchema-instance".equals(namespace)) { + SchemaInfo schInfo = new SchemaInfo(); + if ("schemaLocation".equals(name)) { + /* + * val should consist of two tokens, giving the + * URI and the location respectively. + */ + String[] toks = val.split("\\s", 2); + /* + * Could be a length 0 or 1 array in pathological + * cases, so convert it to a length-2 array. + * Note that while the schemaLocation attribute + * SHOULD have two white-space separated elements, + * this may not be the case, so always check the + * array length before referencing its elements. + */ + if (toks.length > 0 && toks[0] != null) { + schInfo.namespaceURI = toks[0].trim(); + } else { + schInfo.namespaceURI = ""; + } + if (toks.length > 1 && toks[1] != null) { + schInfo.location = toks[1].trim(); + } else { + schInfo.location = ""; + } + if (!hasSchemaURI(schInfo)) { + _schemas.add(schInfo); + } + } + if ("noNamespaceSchemaLocation".equals(name)) { + schInfo.location = "[None]"; + schInfo.namespaceURI = val; + if (!hasSchemaURI(schInfo)) { + _schemas.add(schInfo); + } + } + } + // Collect all attribute values. + _attributeVals.add(val); + } + } + if (_xhtmlFlag) { + if (_htmlMetadata == null) { + _htmlMetadata = new HtmlMetadata(); + } + XhtmlProcessing.processElement(localName, qualifiedName, atts, _htmlMetadata); + } + } + + /** The only action taken here is some bookkeeping in connection with the HTML metadata. */ + @Override + public void endElement(String namespaceURI, String localName, String qName) { + if (_htmlMetadata != null) { + _htmlMetadata.finishPropUnderConstruction(); + } + } + + /** + * Processes PCData characters. This does things only in connection with properties under + * construction in HTML metadata. + */ + @Override + public void characters(char[] ch, int start, int length) { + if (_htmlMetadata != null && _htmlMetadata.getPropUnderConstruction() != null) { + _htmlMetadata.addToPropUnderConstruction(ch, start, length); + } + } + + /** + * Begin the scope of a prefix-URI Namespace mapping. Prefixes mappings are stored in _namespaces. + */ + @Override + public void startPrefixMapping(String prefix, String uri) { + // THL we want the root namespace even if it declares no prefix !!! + // if (!"".equals (prefix)) { + _namespaces.put(prefix, uri); + // } + } + + /** + * Handles a processing instruction. Adds it to the list that will be returned by + * getProcessingInstructions. Each element of the list is an array of two Strings. Element + * 0 of the array is the target, and element 1 is the data. + */ + @Override + public void processingInstruction(String target, String data) { + _sigFlag = true; + if (data == null) { + data = ""; + } + ProcessingInstructionInfo pi = new ProcessingInstructionInfo(); + pi.target = target; + pi.data = data; + _processingInsts.add(pi); + } + + /** + * Puts all notations into the notation list. A list entry is a String[3], consisting of name, + * public ID, and system ID. + */ + @Override + public void notationDecl(String name, String publicID, String systemID) { + String[] notArr = new String[3]; + notArr[0] = name; + notArr[1] = publicID; + notArr[2] = systemID; + _notations.add(notArr); + } + + /** + * Overrides standard resolveEntity. First looks for DTD and entity files that are stored as + * resources, and uses those if available. (Faster and more reliable than grabbing them over the + * Net.) If that fails, calls the superclass's resolveEntity. Regardless, it then looks for + * anything that appears to be a DTD and puts it in the DTD URI field. If the superclass's attempt + * to resolve the entity results in an IOException, we just ignore it. + */ + @Override + public InputSource resolveEntity(String publicId, String systemId) throws SAXException { + + // Check any custom mapping from the config + File fil = _localSchemas.get(systemId.toLowerCase()); + if (fil != null) { + try { + FileInputStream inStrm = new FileInputStream(fil); + return new InputSource(inStrm); + } catch (FileNotFoundException e) { + } + } + + // Do special-case checking for the XHTML DTD's + if (!_xhtmlFlag && DTDMapper.isXHTMLDTD(publicId)) { + _xhtmlFlag = true; + } + InputSource ent = DTDMapper.publicIDToFile(publicId); + if (ent == null) { + try { + URL obj = new URL(systemId); + HttpURLConnection conn = (HttpURLConnection) obj.openConnection(); + + int status = conn.getResponseCode(); + if (status == HttpURLConnection.HTTP_MOVED_TEMP + || status == HttpURLConnection.HTTP_MOVED_PERM + || status == HttpURLConnection.HTTP_SEE_OTHER) { + + String newUrl = conn.getHeaderField("Location"); + conn = (HttpURLConnection) new URL(newUrl).openConnection(); + ent = new InputSource(conn.getInputStream()); + } + + } catch (Exception e) { + // Depending on the JDK version, super.resolveEntity + // may or may not be formally capable of throwing an + // IOException. + // This hack allows compatibility in either case. + throw new SAXException(e); + } + } else { + // A little magic so SAX won't give up in advance on + // relative URI's. + ent.setSystemId("http://hul.harvard.edu/hul"); + } + + // Report in entity properties + EntityInfo entArr = new EntityInfo(); + entArr.publicID = publicId; + entArr.systemID = systemId; + _entities.add(entArr); + /* + * Assume that the first system ID in the file with a .dtd + * extension is the actual DTD + */ + if (systemId.endsWith(".dtd") && _dtdURI == null) { + _dtdURI = systemId; + } + return ent; + } + + /** + * Picks up unparsed entity declarations, after calling the superclass's unparsedEntityDecl, and + * puts their information into the unparsed entity declaration list as an array of four strings: [ + * name, publicId, systemId, notationName]. Null values are converted into empty strings. + */ + @Override + public void unparsedEntityDecl(String name, String publicId, String systemId, String notationName) + throws SAXException { + super.unparsedEntityDecl(name, publicId, systemId, notationName); + String[] info = new String[4]; + info[0] = name == null ? "" : name; + info[1] = publicId == null ? "" : publicId; + info[2] = systemId == null ? "" : systemId; + info[3] = notationName == null ? "" : notationName; + _unparsedEntities.add(info); + } + + /** Processes a warning. We just add an InfoMessage. */ + @Override + public void warning(SAXParseException e) { + JhoveMessage message = + JhoveMessages.getMessageInstance( + MessageConstants.XML_HUL_1.getId(), + MessageFormat.format( + MessageConstants.XML_HUL_1.getMessage(), e.getMessage().toString())); + _messages.add(new InfoMessage(message)); + } + + /** + * Processes a parsing exception. An ill-formed piece of XML will get a fatalError (I think), so + * we can assume that any error here indicates only invalidity. + */ + @Override + public void error(SAXParseException e) { + _valid = false; + if (_nErrors == MAXERRORS) { + JhoveMessage message = + JhoveMessages.getMessageInstance( + MessageConstants.XML_HUL_2.getId(), + MessageFormat.format( + MessageConstants.XML_HUL_2.getMessage(), Integer.valueOf(MAXERRORS))); + _messages.add(new InfoMessage(message)); + } else if (_nErrors < MAXERRORS) { + int line = e.getLineNumber(); + int col = e.getColumnNumber(); + JhoveMessage message = + JhoveMessages.getMessageInstance( + MessageConstants.XML_HUL_1.getId(), + MessageFormat.format( + MessageConstants.XML_HUL_1.getMessage(), e.getMessage().toString())); + _messages.add(new ErrorMessage(message, "Line = " + line + ", Column = " + col)); + } + ++_nErrors; + } + + /** Returns the set of attribute values. */ + public Set getAttributeValues() { + return _attributeVals; + } + + /** + * Returns the list of schemas. The elements of the list are Strings, giving the URI's for the + * schemas. + */ + public List getSchemas() { + return _schemas; + } + + /** + * Returns the list of unparsed entities. The elements of the list are arrays of four Strings, + * giving the name, public ID, system ID and notation name respectively. + */ + public List getUnparsedEntities() { + return _unparsedEntities; + } + + /** Returns the map of prefixes to namespaces. The keys and values are Strings. */ + public Map getNamespaces() { + return _namespaces; + } + + /** Returns the DTD URI. May be null. */ + public String getDTDURI() { + return _dtdURI; + } + + /** + * Returns the List of processing instructions. Each element is an array of two strings, giving + * the target and data respectively. + */ + public List getProcessingInstructions() { + return _processingInsts; + } + + /** Returns the list of notations. Each is an array String[3]: name, public ID, and system ID. */ + public List getNotations() { + return _notations; + } + + /** Returns the qualified name of the root element. */ + public String getRoot() { + return _root; + } + + /** Returns the List of messages generated during the parse. */ + public List getMessages() { + return _messages; + } + + /** + * Returns the validity state. If error has been called, the return value will be + * false. + */ + public boolean isValid() { + return _valid; + } + + /** + * Returns true if we have seen an element or a processing instruction, which implies + * that we've seen an XML declaration. + */ + public boolean getSigFlag() { + return _sigFlag; + } + + /* + * Check if we already know about this schema URI. If we do but the new info + * provides + * a location, quietly stuff the old one into a sewer and pretend it was + * never there. + */ + public boolean hasSchemaURI(SchemaInfo newinfo) { + Iterator schmiter = _schemas.iterator(); + while (schmiter.hasNext()) { + SchemaInfo schmi = schmiter.next(); + if (newinfo.namespaceURI.equals(schmi.namespaceURI)) { + if (schmi.location.isEmpty() && !newinfo.location.isEmpty()) { + _schemas.remove(schmi); + return false; // we like the new info better + } + return true; + } + } + return false; + } } diff --git a/jhove-modules/xml-hul/src/main/java/edu/harvard/hul/ois/jhove/module/xml/package-info.java b/jhove-modules/xml-hul/src/main/java/edu/harvard/hul/ois/jhove/module/xml/package-info.java index 54dbedd46..77e8e1cf4 100644 --- a/jhove-modules/xml-hul/src/main/java/edu/harvard/hul/ois/jhove/module/xml/package-info.java +++ b/jhove-modules/xml-hul/src/main/java/edu/harvard/hul/ois/jhove/module/xml/package-info.java @@ -1,4 +1,2 @@ -/** - * Contains supporting classes for the XML-HUL module. - */ -package edu.harvard.hul.ois.jhove.module.xml; \ No newline at end of file +/** Contains supporting classes for the XML-HUL module. */ +package edu.harvard.hul.ois.jhove.module.xml; diff --git a/pom.xml b/pom.xml index 5cd8b192a..a724264bc 100644 --- a/pom.xml +++ b/pom.xml @@ -93,6 +93,28 @@ + + com.cosium.code + git-code-format-maven-plugin + 2.5 + + + + install-formatter-hook + + install-hooks + + + + + validate-code-format + + validate-code-format + + + + org.apache.maven.plugins maven-antrun-plugin @@ -353,7 +375,7 @@ - + org.apache.maven.plugins maven-source-plugin