diff --git a/Dockerfile b/Dockerfile index 9f8e10ca1..fcdd093c3 100644 --- a/Dockerfile +++ b/Dockerfile @@ -2,7 +2,7 @@ # First build the app on a maven open jdk 11 container FROM maven:3-eclipse-temurin-11-focal as dev-builder ARG JHOVE_VERSION -ENV JHOVE_VERSION=${JHOVE_VERSION:-1.27.0-SNAPSHOT} +ENV JHOVE_VERSION=${JHOVE_VERSION:-1.32.0-RC1} # Copy the current dev source branch to a local build dir COPY . /build/jhove/ @@ -31,7 +31,7 @@ ARG JAVA_OPTS ENV JAVA_OPTS=$JAVA_OPTS # Specify the veraPDF REST version if you want to (to be used in build automation) ARG JHOVE_VERSION -ENV JHOVE_VERSION=${JHOVE_VERSION:-1.27.0-SNAPSHOT} +ENV JHOVE_VERSION=${JHOVE_VERSION:-1.32.0-RC1} # Copy the JRE from the previous stage ENV JAVA_HOME=/opt/java/openjdk diff --git a/docs/index.html b/docs/index.html index c0222260f..becc09713 100644 --- a/docs/index.html +++ b/docs/index.html @@ -11,17 +11,17 @@
JHOVE logo

Open source file format identification, validation & characterisation

- Download JHOVE + Download JHOVE

Software

-

Currently v1.28 19-05-2023

+

Currently v1.30.1 31-07-2024

Details of the latest release, including release notes can be found on GitHub.

JHOVE is a file format identification, validation and characterisation tool. It is implemented as a - Java + Java application and is usable on any Unix, Windows, or OS X platform with appropriate Java installation.

Supported Formats

@@ -156,21 +156,21 @@

License

JHOVE is made available by the - Open + Open Preservation Foundation under the GNU - Lesser General Public + Lesser General Public License (LGPL).

Note that some previous versions of JHOVE were released under the - GNU - General Public + GNU + General Public License (GPL).

Mailing list

- Subscribe to the JHOVE mailing list. + Subscribe to the JHOVE mailing list.

diff --git a/jhove-apps/pom.xml b/jhove-apps/pom.xml index 96610605e..bd1098bb7 100644 --- a/jhove-apps/pom.xml +++ b/jhove-apps/pom.xml @@ -5,12 +5,12 @@ org.openpreservation.jhove jhove - 1.30.0 + 1.32.0-RC1 jhove-apps jar - 1.30.1 + 1.32.0-RC1 JHOVE Applications @@ -60,7 +60,7 @@ org.openpreservation.jhove jhove-core - 1.30.0 + 1.32.0-RC1 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 5610772a4..c3bbc9900 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 @@ -37,1493 +37,1428 @@ */ 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: { - if (pVal instanceof double[]) { - Double[] vals = Arrays.stream((double[])pVal) // IntStream - .boxed() // Stream - .toArray(Double[]::new); - addToNode(node, vals); - } else { - 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("FormatName: " + 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)); - //BitsPerSampleUnit Integer is assumed, because of BitsPerSample - // According to the specification, it can also be float. - // This is currently not supported by jHove - nod.add(new DefaultMutableTreeNode("Integer")); - } - } - 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: "; + + /** + * 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. + return new DefaultMutableTreeNode( + pProp.getName() + ": " + pValue.toString()); + } 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 + 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.isEmpty()) { + 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.isEmpty()) { + 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.isEmpty()) { + 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); + 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: { + if (pVal instanceof double[]) { + Double[] vals = Arrays.stream((double[]) pVal) // IntStream + .boxed() // Stream + .toArray(Double[]::new); + addToNode(node, vals); + } else { + 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.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) { + region.add(new DefaultMutableTreeNode("NumChannels: " + + Integer.toString(nchan), false)); + for (int ch = 0; ch < nchan; ch++) { + // write a stream element for each channel + DefaultMutableTreeNode stream = new DefaultMutableTreeNode( + "Stream", true); + region.add(stream); + stream.add(new DefaultMutableTreeNode("ChannelNum: " + Integer.toString(ch), 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) { + writeAESTimeRangePart(parent, "StartTime", start); + // Duration is optional. + if (duration != null) { + writeAESTimeRangePart(parent, "Duration", duration); + } + } + + private void writeAESTimeRangePart(DefaultMutableTreeNode parent, + String name, AESAudioMetadata.TimeDesc timeDesc) { + double sampleRate = timeDesc.getSampleRate(); + if (sampleRate == 1.0) { + sampleRate = _sampleRate; + } + + DefaultMutableTreeNode node = new DefaultMutableTreeNode(name, true); + node.add(new DefaultMutableTreeNode( + "Value: " + String.valueOf(timeDesc.getSamples()), false)); + node.add(new DefaultMutableTreeNode( + "EditRate: " + sampleRate, false)); + node.add(new DefaultMutableTreeNode( + "FactorNumerator: 1", false)); + node.add(new DefaultMutableTreeNode( + "FactorDenominator: 1", false)); + + 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("FormatName: " + 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)); + // BitsPerSampleUnit Integer is assumed, because of BitsPerSample + // According to the specification, it can also be float. + // This is currently not supported by jHove + nod.add(new DefaultMutableTreeNode("Integer")); + } + } + 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 + 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-bbt/scripts/create-1.28-target.sh b/jhove-bbt/scripts/create-1.28-target.sh index 86789e286..becf2c8c8 100755 --- a/jhove-bbt/scripts/create-1.28-target.sh +++ b/jhove-bbt/scripts/create-1.28-target.sh @@ -243,3 +243,9 @@ fi # Patch release details of the reporting module. find "${targetRoot}" -type f -name "*.jhove.xml" -exec sed -i 's/jhove\/1.8\/jhove.xsd/jhove\/1.9\/jhove.xsd/' {} \; find "${targetRoot}" -type f -name "audit.jhove.xml" -exec sed -i 's/outputHandler release="1.9"/outputHandler release="1.10"/' {} \; + +# Patch XML reporting tweak differences +find "${targetRoot}" -type f -name "valid-external.dtd.jhove.xml" -exec sed -i 's/The markup in the document preceding the root element must be well-formed. Line = 1/Line = 1/' {} \; +find "${targetRoot}" -type f -name "valid-external.dtd.jhove.xml" -exec sed -i 's/SAXParseException/SAXParseException: The markup in the document preceding the root element must be well-formed./' {} \; +find "${targetRoot}" -type f -name "*parsed-entity.ent.jhove.xml" -exec sed -i 's/Content is not allowed in prolog. Line = 1/Line = 1/' {} \; +find "${targetRoot}" -type f -name "*parsed-entity.ent.jhove.xml" -exec sed -i 's/SAXParseException/SAXParseException: Content is not allowed in prolog./' {} \; diff --git a/jhove-bbt/scripts/create-1.30-target.sh b/jhove-bbt/scripts/create-1.30-target.sh index 0f7b94d28..46ed6dd84 100755 --- a/jhove-bbt/scripts/create-1.30-target.sh +++ b/jhove-bbt/scripts/create-1.30-target.sh @@ -114,6 +114,74 @@ if [[ -f "${candidateRoot}/regression/modules/PDF-hul/issue_306.pdf.jhove.xml" ] cp "${candidateRoot}/regression/modules/PDF-hul/issue_306.pdf.jhove.xml" "${targetRoot}/regression/modules/PDF-hul/issue_306.pdf.jhove.xml" fi +# Copy the TIFF Module results changed by https://github.com/openpreserve/jhove/pull/915 +if [[ -f "${candidateRoot}/examples/modules/TIFF-hul/AA_Banner.tif.jhove.xml" ]]; then + cp "${candidateRoot}/examples/modules/TIFF-hul/AA_Banner.tif.jhove.xml" "${targetRoot}/examples/modules/TIFF-hul/AA_Banner.tif.jhove.xml" +fi +if [[ -f "${candidateRoot}/examples/modules/TIFF-hul/strike.tif.jhove.xml" ]]; then + cp "${candidateRoot}/examples/modules/TIFF-hul/strike.tif.jhove.xml" "${targetRoot}/examples/modules/TIFF-hul/strike.tif.jhove.xml" +fi +if [[ -f "${candidateRoot}/examples/modules/TIFF-hul/testpage-large.tif.jhove.xml" ]]; then + cp "${candidateRoot}/examples/modules/TIFF-hul/testpage-large.tif.jhove.xml" "${targetRoot}/examples/modules/TIFF-hul/testpage-large.tif.jhove.xml" +fi +if [[ -f "${candidateRoot}/examples/modules/TIFF-hul/testpage-medium.tif.jhove.xml" ]]; then + cp "${candidateRoot}/examples/modules/TIFF-hul/testpage-medium.tif.jhove.xml" "${targetRoot}/examples/modules/TIFF-hul/testpage-medium.tif.jhove.xml" +fi +if [[ -f "${candidateRoot}/examples/modules/TIFF-hul/oxford.tif.jhove.xml" ]]; then + cp "${candidateRoot}/examples/modules/TIFF-hul/oxford.tif.jhove.xml" "${targetRoot}/examples/modules/TIFF-hul/oxford.tif.jhove.xml" +fi +if [[ -f "${candidateRoot}/examples/modules/TIFF-hul/jim___gg.tif.jhove.xml" ]]; then + cp "${candidateRoot}/examples/modules/TIFF-hul/jim___gg.tif.jhove.xml" "${targetRoot}/examples/modules/TIFF-hul/jim___gg.tif.jhove.xml" +fi +if [[ -f "${candidateRoot}/examples/modules/TIFF-hul/bathy1.tif.jhove.xml" ]]; then + cp "${candidateRoot}/examples/modules/TIFF-hul/bathy1.tif.jhove.xml" "${targetRoot}/examples/modules/TIFF-hul/bathy1.tif.jhove.xml" +fi +if [[ -f "${candidateRoot}/examples/modules/TIFF-hul/jim___cg.tif.jhove.xml" ]]; then + cp "${candidateRoot}/examples/modules/TIFF-hul/jim___cg.tif.jhove.xml" "${targetRoot}/examples/modules/TIFF-hul/jim___cg.tif.jhove.xml" +fi +if [[ -f "${candidateRoot}/examples/modules/TIFF-hul/quad-tile.tif.jhove.xml" ]]; then + cp "${candidateRoot}/examples/modules/TIFF-hul/quad-tile.tif.jhove.xml" "${targetRoot}/examples/modules/TIFF-hul/quad-tile.tif.jhove.xml" +fi +if [[ -f "${candidateRoot}/examples/modules/TIFF-hul/compos.tif.jhove.xml" ]]; then + cp "${candidateRoot}/examples/modules/TIFF-hul/compos.tif.jhove.xml" "${targetRoot}/examples/modules/TIFF-hul/compos.tif.jhove.xml" +fi +if [[ -f "${candidateRoot}/examples/modules/TIFF-hul/pagemaker.tif.jhove.xml" ]]; then + cp "${candidateRoot}/examples/modules/TIFF-hul/pagemaker.tif.jhove.xml" "${targetRoot}/examples/modules/TIFF-hul/pagemaker.tif.jhove.xml" +fi +if [[ -f "${candidateRoot}/examples/modules/TIFF-hul/jello.tif.jhove.xml" ]]; then + cp "${candidateRoot}/examples/modules/TIFF-hul/jello.tif.jhove.xml" "${targetRoot}/examples/modules/TIFF-hul/jello.tif.jhove.xml" +fi +if [[ -f "${candidateRoot}/examples/modules/TIFF-hul/little-endian.tif.jhove.xml" ]]; then + cp "${candidateRoot}/examples/modules/TIFF-hul/little-endian.tif.jhove.xml" "${targetRoot}/examples/modules/TIFF-hul/little-endian.tif.jhove.xml" +fi +if [[ -f "${candidateRoot}/examples/modules/TIFF-hul/cramps-tile.tif.jhove.xml" ]]; then + cp "${candidateRoot}/examples/modules/TIFF-hul/cramps-tile.tif.jhove.xml" "${targetRoot}/examples/modules/TIFF-hul/cramps-tile.tif.jhove.xml" +fi +if [[ -f "${candidateRoot}/examples/modules/TIFF-hul/jim___ah.tif.jhove.xml" ]]; then + cp "${candidateRoot}/examples/modules/TIFF-hul/jim___ah.tif.jhove.xml" "${targetRoot}/examples/modules/TIFF-hul/jim___ah.tif.jhove.xml" +fi +if [[ -f "${candidateRoot}/examples/modules/TIFF-hul/g3test.tif.jhove.xml" ]]; then + cp "${candidateRoot}/examples/modules/TIFF-hul/g3test.tif.jhove.xml" "${targetRoot}/examples/modules/TIFF-hul/g3test.tif.jhove.xml" +fi +if [[ -f "${candidateRoot}/examples/modules/TIFF-hul/6mp_soft.tif.jhove.xml" ]]; then + cp "${candidateRoot}/examples/modules/TIFF-hul/6mp_soft.tif.jhove.xml" "${targetRoot}/examples/modules/TIFF-hul/6mp_soft.tif.jhove.xml" +fi +if [[ -f "${candidateRoot}/examples/modules/TIFF-hul/ycbcr-cat.tif.jhove.xml" ]]; then + cp "${candidateRoot}/examples/modules/TIFF-hul/ycbcr-cat.tif.jhove.xml" "${targetRoot}/examples/modules/TIFF-hul/ycbcr-cat.tif.jhove.xml" +fi +if [[ -f "${candidateRoot}/examples/modules/TIFF-hul/quad-lzw.tif.jhove.xml" ]]; then + cp "${candidateRoot}/examples/modules/TIFF-hul/quad-lzw.tif.jhove.xml" "${targetRoot}/examples/modules/TIFF-hul/quad-lzw.tif.jhove.xml" +fi +if [[ -f "${candidateRoot}/examples/modules/TIFF-hul/jim___dg.tif.jhove.xml" ]]; then + cp "${candidateRoot}/examples/modules/TIFF-hul/jim___dg.tif.jhove.xml" "${targetRoot}/examples/modules/TIFF-hul/jim___dg.tif.jhove.xml" +fi +if [[ -f "${candidateRoot}/examples/modules/TIFF-hul/fax2d.tif.jhove.xml" ]]; then + cp "${candidateRoot}/examples/modules/TIFF-hul/fax2d.tif.jhove.xml" "${targetRoot}/examples/modules/TIFF-hul/fax2d.tif.jhove.xml" +fi +if [[ -f "${candidateRoot}/examples/modules/TIFF-hul/peppers.tif.jhove.xml" ]]; then + cp "${candidateRoot}/examples/modules/TIFF-hul/peppers.tif.jhove.xml" "${targetRoot}/examples/modules/TIFF-hul/peppers.tif.jhove.xml" +fi + # Copy the PNG Module results changed by https://github.com/openpreserve/jhove/pull/843 if [[ -f "${candidateRoot}/regression/modules/PNG-gdm/issue_148.png.jhove.xml" ]]; then cp "${candidateRoot}/regression/modules/PNG-gdm/issue_148.png.jhove.xml" "${targetRoot}/regression/modules/PNG-gdm/issue_148.png.jhove.xml" diff --git a/jhove-bbt/scripts/create-1.31-target.sh b/jhove-bbt/scripts/create-1.31-target.sh new file mode 100755 index 000000000..48f7049f2 --- /dev/null +++ b/jhove-bbt/scripts/create-1.31-target.sh @@ -0,0 +1,182 @@ +#!/usr/bin/env bash + +testRoot="test-root" +paramCandidateVersion="" +paramBaselineVersion="" +baselineRoot="${testRoot}/baselines" +candidateRoot="${testRoot}/candidates" +targetRoot="${testRoot}/targets" +# Check the passed params to avoid disapointment +checkParams () { + OPTIND=1 # Reset in case getopts previously used + + while getopts "h?b:c:" opt; do # Grab the options + case "$opt" in + h|\?) + showHelp + exit 0 + ;; + b) paramBaselineVersion=$OPTARG + ;; + c) paramCandidateVersion=$OPTARG + ;; + esac + done + + if [ -z "$paramBaselineVersion" ] || [ -z "$paramCandidateVersion" ] + then + showHelp + exit 0 + fi + + baselineRoot="${baselineRoot}/${paramBaselineVersion}" + candidateRoot="${candidateRoot}/${paramCandidateVersion}" + targetRoot="${targetRoot}/${paramCandidateVersion}" +} + +# Show usage message +showHelp() { + echo "usage: create-target [-b ] [-c ] [-h|?]" + echo "" + echo " baselineVersion : The version number id for the baseline data." + echo " candidateVersion : The version number id for the candidate data." + echo "" + echo " -h|? : This message." +} + +# Execution starts here +checkParams "$@"; +if [[ -d "${targetRoot}" ]]; then + echo " - removing existing baseline at ${targetRoot}." + rm -rf "${targetRoot}" +fi + +echo "TEST BASELINE: Creating baseline" +# Simply copy baseline for now we're not making any changes +echo " - copying ${baselineRoot} baseline to ${targetRoot}" +cp -R "${baselineRoot}" "${targetRoot}" + +# Copy the TIFF Module results changed by https://github.com/openpreserve/jhove/pull/915 +if [[ -f "${candidateRoot}/examples/modules/TIFF-hul/AA_Banner.tif.jhove.xml" ]]; then + cp "${candidateRoot}/examples/modules/TIFF-hul/AA_Banner.tif.jhove.xml" "${targetRoot}/examples/modules/TIFF-hul/AA_Banner.tif.jhove.xml" +fi +if [[ -f "${candidateRoot}/examples/modules/TIFF-hul/strike.tif.jhove.xml" ]]; then + cp "${candidateRoot}/examples/modules/TIFF-hul/strike.tif.jhove.xml" "${targetRoot}/examples/modules/TIFF-hul/strike.tif.jhove.xml" +fi +if [[ -f "${candidateRoot}/examples/modules/TIFF-hul/testpage-large.tif.jhove.xml" ]]; then + cp "${candidateRoot}/examples/modules/TIFF-hul/testpage-large.tif.jhove.xml" "${targetRoot}/examples/modules/TIFF-hul/testpage-large.tif.jhove.xml" +fi +if [[ -f "${candidateRoot}/examples/modules/TIFF-hul/testpage-medium.tif.jhove.xml" ]]; then + cp "${candidateRoot}/examples/modules/TIFF-hul/testpage-medium.tif.jhove.xml" "${targetRoot}/examples/modules/TIFF-hul/testpage-medium.tif.jhove.xml" +fi +if [[ -f "${candidateRoot}/examples/modules/TIFF-hul/oxford.tif.jhove.xml" ]]; then + cp "${candidateRoot}/examples/modules/TIFF-hul/oxford.tif.jhove.xml" "${targetRoot}/examples/modules/TIFF-hul/oxford.tif.jhove.xml" +fi +if [[ -f "${candidateRoot}/examples/modules/TIFF-hul/jim___gg.tif.jhove.xml" ]]; then + cp "${candidateRoot}/examples/modules/TIFF-hul/jim___gg.tif.jhove.xml" "${targetRoot}/examples/modules/TIFF-hul/jim___gg.tif.jhove.xml" +fi +if [[ -f "${candidateRoot}/examples/modules/TIFF-hul/bathy1.tif.jhove.xml" ]]; then + cp "${candidateRoot}/examples/modules/TIFF-hul/bathy1.tif.jhove.xml" "${targetRoot}/examples/modules/TIFF-hul/bathy1.tif.jhove.xml" +fi +if [[ -f "${candidateRoot}/examples/modules/TIFF-hul/jim___cg.tif.jhove.xml" ]]; then + cp "${candidateRoot}/examples/modules/TIFF-hul/jim___cg.tif.jhove.xml" "${targetRoot}/examples/modules/TIFF-hul/jim___cg.tif.jhove.xml" +fi +if [[ -f "${candidateRoot}/examples/modules/TIFF-hul/quad-tile.tif.jhove.xml" ]]; then + cp "${candidateRoot}/examples/modules/TIFF-hul/quad-tile.tif.jhove.xml" "${targetRoot}/examples/modules/TIFF-hul/quad-tile.tif.jhove.xml" +fi +if [[ -f "${candidateRoot}/examples/modules/TIFF-hul/compos.tif.jhove.xml" ]]; then + cp "${candidateRoot}/examples/modules/TIFF-hul/compos.tif.jhove.xml" "${targetRoot}/examples/modules/TIFF-hul/compos.tif.jhove.xml" +fi +if [[ -f "${candidateRoot}/examples/modules/TIFF-hul/pagemaker.tif.jhove.xml" ]]; then + cp "${candidateRoot}/examples/modules/TIFF-hul/pagemaker.tif.jhove.xml" "${targetRoot}/examples/modules/TIFF-hul/pagemaker.tif.jhove.xml" +fi +if [[ -f "${candidateRoot}/examples/modules/TIFF-hul/jello.tif.jhove.xml" ]]; then + cp "${candidateRoot}/examples/modules/TIFF-hul/jello.tif.jhove.xml" "${targetRoot}/examples/modules/TIFF-hul/jello.tif.jhove.xml" +fi +if [[ -f "${candidateRoot}/examples/modules/TIFF-hul/little-endian.tif.jhove.xml" ]]; then + cp "${candidateRoot}/examples/modules/TIFF-hul/little-endian.tif.jhove.xml" "${targetRoot}/examples/modules/TIFF-hul/little-endian.tif.jhove.xml" +fi +if [[ -f "${candidateRoot}/examples/modules/TIFF-hul/cramps-tile.tif.jhove.xml" ]]; then + cp "${candidateRoot}/examples/modules/TIFF-hul/cramps-tile.tif.jhove.xml" "${targetRoot}/examples/modules/TIFF-hul/cramps-tile.tif.jhove.xml" +fi +if [[ -f "${candidateRoot}/examples/modules/TIFF-hul/jim___ah.tif.jhove.xml" ]]; then + cp "${candidateRoot}/examples/modules/TIFF-hul/jim___ah.tif.jhove.xml" "${targetRoot}/examples/modules/TIFF-hul/jim___ah.tif.jhove.xml" +fi +if [[ -f "${candidateRoot}/examples/modules/TIFF-hul/g3test.tif.jhove.xml" ]]; then + cp "${candidateRoot}/examples/modules/TIFF-hul/g3test.tif.jhove.xml" "${targetRoot}/examples/modules/TIFF-hul/g3test.tif.jhove.xml" +fi +if [[ -f "${candidateRoot}/examples/modules/TIFF-hul/6mp_soft.tif.jhove.xml" ]]; then + cp "${candidateRoot}/examples/modules/TIFF-hul/6mp_soft.tif.jhove.xml" "${targetRoot}/examples/modules/TIFF-hul/6mp_soft.tif.jhove.xml" +fi +if [[ -f "${candidateRoot}/examples/modules/TIFF-hul/ycbcr-cat.tif.jhove.xml" ]]; then + cp "${candidateRoot}/examples/modules/TIFF-hul/ycbcr-cat.tif.jhove.xml" "${targetRoot}/examples/modules/TIFF-hul/ycbcr-cat.tif.jhove.xml" +fi +if [[ -f "${candidateRoot}/examples/modules/TIFF-hul/quad-lzw.tif.jhove.xml" ]]; then + cp "${candidateRoot}/examples/modules/TIFF-hul/quad-lzw.tif.jhove.xml" "${targetRoot}/examples/modules/TIFF-hul/quad-lzw.tif.jhove.xml" +fi +if [[ -f "${candidateRoot}/examples/modules/TIFF-hul/jim___dg.tif.jhove.xml" ]]; then + cp "${candidateRoot}/examples/modules/TIFF-hul/jim___dg.tif.jhove.xml" "${targetRoot}/examples/modules/TIFF-hul/jim___dg.tif.jhove.xml" +fi +if [[ -f "${candidateRoot}/examples/modules/TIFF-hul/fax2d.tif.jhove.xml" ]]; then + cp "${candidateRoot}/examples/modules/TIFF-hul/fax2d.tif.jhove.xml" "${targetRoot}/examples/modules/TIFF-hul/fax2d.tif.jhove.xml" +fi +if [[ -f "${candidateRoot}/examples/modules/TIFF-hul/peppers.tif.jhove.xml" ]]; then + cp "${candidateRoot}/examples/modules/TIFF-hul/peppers.tif.jhove.xml" "${targetRoot}/examples/modules/TIFF-hul/peppers.tif.jhove.xml" +fi + +# Copy the TIFF fix affected files from the candidate to the target +declare -a tiff_affected=("examples/modules/TIFF-hul/cramps.tif.jhove.xml" + "examples/modules/TIFF-hul/text.tif.jhove.xml" + "examples/modules/TIFF-hul/testpage-small.tif.jhove.xml") +for filename in "${tiff_affected[@]}" +do + if [[ -f "${candidateRoot}/${filename}" ]]; then + cp "${candidateRoot}/${filename}" "${targetRoot}/${filename}" + fi +done + +# Copy the XHTML fix affected files from the candidate to the target +declare -a xhtml_affected=("errors/modules/HTML-hul/xhtml-trans-no-xml-dec.html.jhove.xml" + "errors/modules/HTML-hul/xhtml-strict-no-xml-dec.html.jhove.xml" + "errors/modules/HTML-hul/xhtml-frames-no-xml-dec.html.jhove.xml" + "errors/modules/HTML-hul/xhtml-1-1-no-xml-dec.html.jhove.xml") +for filename in "${xhtml_affected[@]}" +do + if [[ -f "${candidateRoot}/${filename}" ]]; then + cp "${candidateRoot}/${filename}" "${targetRoot}/${filename}" + fi +done + +# Copy the XML fix affected files from the candidate to the target +declare -a xhtml_affected=("errors/modules/HTML-hul/xhtml-trans-xml-dec.html.jhove.xml" + "errors/modules/HTML-hul/xhtml-strict-xml-dec.html.jhove.xml" + "errors/modules/HTML-hul/xhtml-frames-xml-dec.html.jhove.xml" + "errors/modules/HTML-hul/xhtml-1-1-xml-dec.html.jhove.xml" + "examples/modules/XML-hul/valid-external.dtd.jhove.xml" + "examples/modules/XML-hul/external-unparsed-entity.ent.jhove.xml" + "examples/modules/XML-hul/external-parsed-entity.ent.jhove.xml") +for filename in "${xhtml_affected[@]}" +do + if [[ -f "${candidateRoot}/${filename}" ]]; then + cp "${candidateRoot}/${filename}" "${targetRoot}/${filename}" + fi +done + +# Copy all of the AIF and WAV results as these are changed by the AES schema changes +cp -rf "${candidateRoot}/examples/modules/AIFF-hul" "${targetRoot}/examples/modules/" +cp -rf "${candidateRoot}/examples/modules/WAVE-hul" "${targetRoot}/examples/modules/" +cp -rf "${candidateRoot}/errors/modules/WAVE-hul" "${targetRoot}/errors/modules/" + +# Copy the results of the new XML fixes for multiple redirect lookups and to ensure no regression for repeat XML warnings +cp -rf "${candidateRoot}/errors/modules/XML-hul" "${targetRoot}/errors/modules/" + +# Copy the results of the PDF offset message fix +declare -a pdf_offset_affected=("errors/modules/PDF-hul/pdf-hul-5-govdocs-659152.pdf.jhove.xml" + "errors/modules/PDF-hul/pdf-hul-10-govdocs-803945.pdf.jhove.xml" + "regression/modules/PDF-hul/issue_306.pdf.jhove.xml") +for filename in "${pdf_offset_affected[@]}" +do + if [[ -f "${candidateRoot}/${filename}" ]]; then + cp "${candidateRoot}/${filename}" "${targetRoot}/${filename}" + fi +done diff --git a/jhove-bbt/scripts/create-1.32-target.sh b/jhove-bbt/scripts/create-1.32-target.sh new file mode 100755 index 000000000..6857fa33d --- /dev/null +++ b/jhove-bbt/scripts/create-1.32-target.sh @@ -0,0 +1,213 @@ +#!/usr/bin/env bash + +testRoot="test-root" +paramCandidateVersion="" +paramBaselineVersion="" +baselineRoot="${testRoot}/baselines" +candidateRoot="${testRoot}/candidates" +targetRoot="${testRoot}/targets" +# Check the passed params to avoid disapointment +checkParams () { + OPTIND=1 # Reset in case getopts previously used + + while getopts "h?b:c:" opt; do # Grab the options + case "$opt" in + h|\?) + showHelp + exit 0 + ;; + b) paramBaselineVersion=$OPTARG + ;; + c) paramCandidateVersion=$OPTARG + ;; + esac + done + + if [ -z "$paramBaselineVersion" ] || [ -z "$paramCandidateVersion" ] + then + showHelp + exit 0 + fi + + baselineRoot="${baselineRoot}/${paramBaselineVersion}" + candidateRoot="${candidateRoot}/${paramCandidateVersion}" + targetRoot="${targetRoot}/${paramCandidateVersion}" +} + +# Show usage message +showHelp() { + echo "usage: create-target [-b ] [-c ] [-h|?]" + echo "" + echo " baselineVersion : The version number id for the baseline data." + echo " candidateVersion : The version number id for the candidate data." + echo "" + echo " -h|? : This message." +} + +# Execution starts here +checkParams "$@"; +if [[ -d "${targetRoot}" ]]; then + echo " - removing existing baseline at ${targetRoot}." + rm -rf "${targetRoot}" +fi + +echo "TEST BASELINE: Creating baseline" +# Simply copy baseline for now we're not making any changes +echo " - copying ${baselineRoot} baseline to ${targetRoot}" +cp -R "${baselineRoot}" "${targetRoot}" + +# Patch release details of the reporting module in the audit file +find "${targetRoot}" -type f -name "audit.jhove.xml" -exec sed -i 's/outputHandler release="1.11">XML/outputHandler release="1.12">XML/' {} \; +find "${targetRoot}" -type f -name "audit.jhove.xml" -exec sed -i 's/outputHandler release="1.2">JSON/outputHandler release="1.3">JSON/' {} \; +find "${targetRoot}" -type f -name "audit.jhove.xml" -exec sed -i 's/outputHandler release="1.6">TEXT/outputHandler release="1.7">TEXT/' {} \; + +# Update release details for HTML module +find "${targetRoot}" -type f -name "*.html.jhove.xml" -exec sed -i 's/HTML-hul<\/reportingModule>/HTML-hul<\/reportingModule>/' {} \; +find "${targetRoot}" -type f -name "audit.jhove.xml" -exec sed -i 's/HTML-hul<\/module>/HTML-hul<\/module>/' {} \; +find "${targetRoot}" -type f -name "audit-HTML-hul.jhove.xml" -exec sed -i 's/1.4.3<\/release>/1.4.4<\/release>/' {} \; +find "${targetRoot}" -type f -name "audit-HTML-hul.jhove.xml" -exec sed -i 's/2023-03-16/2024-08-22/' {} \; + +# Update release details for PDF module +find "${targetRoot}" -type f -name "*.pdf.jhove.xml" -exec sed -i 's/PDF-hul<\/reportingModule>/PDF-hul<\/reportingModule>/' {} \; +find "${targetRoot}" -type f -name "audit.jhove.xml" -exec sed -i 's/PDF-hul<\/module>/PDF-hul<\/module>/' {} \; +find "${targetRoot}" -type f -name "audit-PDF-hul.jhove.xml" -exec sed -i 's/1.12.6<\/release>/1.12.7<\/release>/' {} \; +find "${targetRoot}" -type f -name "audit-PDF-hul.jhove.xml" -exec sed -i 's/2024-07-31/2024-08-22/' {} \; + +# Update release details for TIFF module +find "${targetRoot}" -type f -name "*.tiff.jhove.xml" -exec sed -i 's/TIFF-hul<\/reportingModule>/TIFF-hul<\/reportingModule>/' {} \; +find "${targetRoot}" -type f -name "*.tif.jhove.xml" -exec sed -i 's/TIFF-hul<\/reportingModule>/TIFF-hul<\/reportingModule>/' {} \; +find "${targetRoot}" -type f -name "*.g3.jhove.xml" -exec sed -i 's/TIFF-hul<\/reportingModule>/TIFF-hul<\/reportingModule>/' {} \; +find "${targetRoot}" -type f -name "audit.jhove.xml" -exec sed -i 's/TIFF-hul<\/module>/TIFF-hul<\/module>/' {} \; +find "${targetRoot}" -type f -name "audit-TIFF-hul.jhove.xml" -exec sed -i 's/1.9.4<\/release>/1.9.5<\/release>/' {} \; +find "${targetRoot}" -type f -name "audit-TIFF-hul.jhove.xml" -exec sed -i 's/2023-03-16/2024-08-22/' {} \; + +# Update release details for XML module +find "${targetRoot}" -type f -name "*.xml.jhove.xml" -exec sed -i 's/XML-hul<\/reportingModule>/XML-hul<\/reportingModule>/' {} \; +find "${targetRoot}" -type f -name "audit.jhove.xml" -exec sed -i 's/XML-hul<\/module>/XML-hul<\/module>/' {} \; +find "${targetRoot}" -type f -name "audit-XML-hul.jhove.xml" -exec sed -i 's/1.5.4<\/release>/1.5.5<\/release>/' {} \; +find "${targetRoot}" -type f -name "audit-XML-hul.jhove.xml" -exec sed -i 's/2024-03-05/2024-08-22/' {} \; + +# Copy the TIFF Module results changed by https://github.com/openpreserve/jhove/pull/915 +if [[ -f "${candidateRoot}/examples/modules/TIFF-hul/AA_Banner.tif.jhove.xml" ]]; then + cp "${candidateRoot}/examples/modules/TIFF-hul/AA_Banner.tif.jhove.xml" "${targetRoot}/examples/modules/TIFF-hul/AA_Banner.tif.jhove.xml" +fi +if [[ -f "${candidateRoot}/examples/modules/TIFF-hul/strike.tif.jhove.xml" ]]; then + cp "${candidateRoot}/examples/modules/TIFF-hul/strike.tif.jhove.xml" "${targetRoot}/examples/modules/TIFF-hul/strike.tif.jhove.xml" +fi +if [[ -f "${candidateRoot}/examples/modules/TIFF-hul/testpage-large.tif.jhove.xml" ]]; then + cp "${candidateRoot}/examples/modules/TIFF-hul/testpage-large.tif.jhove.xml" "${targetRoot}/examples/modules/TIFF-hul/testpage-large.tif.jhove.xml" +fi +if [[ -f "${candidateRoot}/examples/modules/TIFF-hul/testpage-medium.tif.jhove.xml" ]]; then + cp "${candidateRoot}/examples/modules/TIFF-hul/testpage-medium.tif.jhove.xml" "${targetRoot}/examples/modules/TIFF-hul/testpage-medium.tif.jhove.xml" +fi +if [[ -f "${candidateRoot}/examples/modules/TIFF-hul/oxford.tif.jhove.xml" ]]; then + cp "${candidateRoot}/examples/modules/TIFF-hul/oxford.tif.jhove.xml" "${targetRoot}/examples/modules/TIFF-hul/oxford.tif.jhove.xml" +fi +if [[ -f "${candidateRoot}/examples/modules/TIFF-hul/jim___gg.tif.jhove.xml" ]]; then + cp "${candidateRoot}/examples/modules/TIFF-hul/jim___gg.tif.jhove.xml" "${targetRoot}/examples/modules/TIFF-hul/jim___gg.tif.jhove.xml" +fi +if [[ -f "${candidateRoot}/examples/modules/TIFF-hul/bathy1.tif.jhove.xml" ]]; then + cp "${candidateRoot}/examples/modules/TIFF-hul/bathy1.tif.jhove.xml" "${targetRoot}/examples/modules/TIFF-hul/bathy1.tif.jhove.xml" +fi +if [[ -f "${candidateRoot}/examples/modules/TIFF-hul/jim___cg.tif.jhove.xml" ]]; then + cp "${candidateRoot}/examples/modules/TIFF-hul/jim___cg.tif.jhove.xml" "${targetRoot}/examples/modules/TIFF-hul/jim___cg.tif.jhove.xml" +fi +if [[ -f "${candidateRoot}/examples/modules/TIFF-hul/quad-tile.tif.jhove.xml" ]]; then + cp "${candidateRoot}/examples/modules/TIFF-hul/quad-tile.tif.jhove.xml" "${targetRoot}/examples/modules/TIFF-hul/quad-tile.tif.jhove.xml" +fi +if [[ -f "${candidateRoot}/examples/modules/TIFF-hul/compos.tif.jhove.xml" ]]; then + cp "${candidateRoot}/examples/modules/TIFF-hul/compos.tif.jhove.xml" "${targetRoot}/examples/modules/TIFF-hul/compos.tif.jhove.xml" +fi +if [[ -f "${candidateRoot}/examples/modules/TIFF-hul/pagemaker.tif.jhove.xml" ]]; then + cp "${candidateRoot}/examples/modules/TIFF-hul/pagemaker.tif.jhove.xml" "${targetRoot}/examples/modules/TIFF-hul/pagemaker.tif.jhove.xml" +fi +if [[ -f "${candidateRoot}/examples/modules/TIFF-hul/jello.tif.jhove.xml" ]]; then + cp "${candidateRoot}/examples/modules/TIFF-hul/jello.tif.jhove.xml" "${targetRoot}/examples/modules/TIFF-hul/jello.tif.jhove.xml" +fi +if [[ -f "${candidateRoot}/examples/modules/TIFF-hul/little-endian.tif.jhove.xml" ]]; then + cp "${candidateRoot}/examples/modules/TIFF-hul/little-endian.tif.jhove.xml" "${targetRoot}/examples/modules/TIFF-hul/little-endian.tif.jhove.xml" +fi +if [[ -f "${candidateRoot}/examples/modules/TIFF-hul/cramps-tile.tif.jhove.xml" ]]; then + cp "${candidateRoot}/examples/modules/TIFF-hul/cramps-tile.tif.jhove.xml" "${targetRoot}/examples/modules/TIFF-hul/cramps-tile.tif.jhove.xml" +fi +if [[ -f "${candidateRoot}/examples/modules/TIFF-hul/jim___ah.tif.jhove.xml" ]]; then + cp "${candidateRoot}/examples/modules/TIFF-hul/jim___ah.tif.jhove.xml" "${targetRoot}/examples/modules/TIFF-hul/jim___ah.tif.jhove.xml" +fi +if [[ -f "${candidateRoot}/examples/modules/TIFF-hul/g3test.tif.jhove.xml" ]]; then + cp "${candidateRoot}/examples/modules/TIFF-hul/g3test.tif.jhove.xml" "${targetRoot}/examples/modules/TIFF-hul/g3test.tif.jhove.xml" +fi +if [[ -f "${candidateRoot}/examples/modules/TIFF-hul/6mp_soft.tif.jhove.xml" ]]; then + cp "${candidateRoot}/examples/modules/TIFF-hul/6mp_soft.tif.jhove.xml" "${targetRoot}/examples/modules/TIFF-hul/6mp_soft.tif.jhove.xml" +fi +if [[ -f "${candidateRoot}/examples/modules/TIFF-hul/ycbcr-cat.tif.jhove.xml" ]]; then + cp "${candidateRoot}/examples/modules/TIFF-hul/ycbcr-cat.tif.jhove.xml" "${targetRoot}/examples/modules/TIFF-hul/ycbcr-cat.tif.jhove.xml" +fi +if [[ -f "${candidateRoot}/examples/modules/TIFF-hul/quad-lzw.tif.jhove.xml" ]]; then + cp "${candidateRoot}/examples/modules/TIFF-hul/quad-lzw.tif.jhove.xml" "${targetRoot}/examples/modules/TIFF-hul/quad-lzw.tif.jhove.xml" +fi +if [[ -f "${candidateRoot}/examples/modules/TIFF-hul/jim___dg.tif.jhove.xml" ]]; then + cp "${candidateRoot}/examples/modules/TIFF-hul/jim___dg.tif.jhove.xml" "${targetRoot}/examples/modules/TIFF-hul/jim___dg.tif.jhove.xml" +fi +if [[ -f "${candidateRoot}/examples/modules/TIFF-hul/fax2d.tif.jhove.xml" ]]; then + cp "${candidateRoot}/examples/modules/TIFF-hul/fax2d.tif.jhove.xml" "${targetRoot}/examples/modules/TIFF-hul/fax2d.tif.jhove.xml" +fi +if [[ -f "${candidateRoot}/examples/modules/TIFF-hul/peppers.tif.jhove.xml" ]]; then + cp "${candidateRoot}/examples/modules/TIFF-hul/peppers.tif.jhove.xml" "${targetRoot}/examples/modules/TIFF-hul/peppers.tif.jhove.xml" +fi + +# Copy the TIFF fix affected files from the candidate to the target +declare -a tiff_affected=("examples/modules/TIFF-hul/cramps.tif.jhove.xml" + "examples/modules/TIFF-hul/text.tif.jhove.xml" + "examples/modules/TIFF-hul/testpage-small.tif.jhove.xml") +for filename in "${tiff_affected[@]}" +do + if [[ -f "${candidateRoot}/${filename}" ]]; then + cp "${candidateRoot}/${filename}" "${targetRoot}/${filename}" + fi +done + +# Copy the XHTML fix affected files from the candidate to the target +declare -a xhtml_affected=("errors/modules/HTML-hul/xhtml-trans-no-xml-dec.html.jhove.xml" + "errors/modules/HTML-hul/xhtml-strict-no-xml-dec.html.jhove.xml" + "errors/modules/HTML-hul/xhtml-frames-no-xml-dec.html.jhove.xml" + "errors/modules/HTML-hul/xhtml-1-1-no-xml-dec.html.jhove.xml") +for filename in "${xhtml_affected[@]}" +do + if [[ -f "${candidateRoot}/${filename}" ]]; then + cp "${candidateRoot}/${filename}" "${targetRoot}/${filename}" + fi +done + +# Copy the XML fix affected files from the candidate to the target +declare -a xhtml_affected=("errors/modules/HTML-hul/xhtml-trans-xml-dec.html.jhove.xml" + "errors/modules/HTML-hul/xhtml-strict-xml-dec.html.jhove.xml" + "errors/modules/HTML-hul/xhtml-frames-xml-dec.html.jhove.xml" + "errors/modules/HTML-hul/xhtml-1-1-xml-dec.html.jhove.xml" + "examples/modules/XML-hul/valid-external.dtd.jhove.xml" + "examples/modules/XML-hul/external-unparsed-entity.ent.jhove.xml" + "examples/modules/XML-hul/external-parsed-entity.ent.jhove.xml") +for filename in "${xhtml_affected[@]}" +do + if [[ -f "${candidateRoot}/${filename}" ]]; then + cp "${candidateRoot}/${filename}" "${targetRoot}/${filename}" + fi +done + +# Copy all of the AIF and WAV results as these are changed by the AES schema changes +cp -rf "${candidateRoot}/examples/modules/AIFF-hul" "${targetRoot}/examples/modules/" +cp -rf "${candidateRoot}/examples/modules/WAVE-hul" "${targetRoot}/examples/modules/" +cp -rf "${candidateRoot}/errors/modules/WAVE-hul" "${targetRoot}/errors/modules/" + +# Copy the results of the new XML fixes for multiple redirect lookups and to ensure no regression for repeat XML warnings +cp -rf "${candidateRoot}/errors/modules/XML-hul" "${targetRoot}/errors/modules/" + +# Copy the results of the PDF offset message fix +declare -a pdf_offset_affected=("errors/modules/PDF-hul/pdf-hul-5-govdocs-659152.pdf.jhove.xml" + "errors/modules/PDF-hul/pdf-hul-10-govdocs-803945.pdf.jhove.xml" + "regression/modules/PDF-hul/issue_306.pdf.jhove.xml") +for filename in "${pdf_offset_affected[@]}" +do + if [[ -f "${candidateRoot}/${filename}" ]]; then + cp "${candidateRoot}/${filename}" "${targetRoot}/${filename}" + fi +done diff --git a/jhove-bbt/scripts/travis-test.sh b/jhove-bbt/scripts/travis-test.sh index 94ace84e3..c2ee948d6 100755 --- a/jhove-bbt/scripts/travis-test.sh +++ b/jhove-bbt/scripts/travis-test.sh @@ -13,7 +13,7 @@ TEST_BASELINES_ROOT="${TEST_ROOT}/baselines" TEST_INSTALL_ROOT="${TEST_ROOT}/jhove" CANDIDATE_ROOT="${TEST_ROOT}/candidates" TARGET_ROOT="${TEST_ROOT}/targets" -BASELINE_VERSION=1.28 +BASELINE_VERSION=1.30 # Create the JHOVE test root if it doesn't exist [[ -d "${TEST_ROOT}" ]] || mkdir -p "${TEST_ROOT}" diff --git a/jhove-core/pom.xml b/jhove-core/pom.xml index c7e17204c..4ac656bdb 100644 --- a/jhove-core/pom.xml +++ b/jhove-core/pom.xml @@ -5,7 +5,7 @@ org.openpreservation.jhove jhove - 1.30.0 + 1.32.0-RC1 jhove-core 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..633a9d64d 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 @@ -14,8 +14,7 @@ * @author Gary McGath * */ -public class AESAudioMetadata -{ +public class AESAudioMetadata { /****************************************************************** * PUBLIC CLASS FIELDS. ******************************************************************/ @@ -27,14 +26,13 @@ public class AESAudioMetadata public static final int LITTLE_ENDIAN = 1; /** Analog / digital labels. */ - public static final String [] A_D = { - "ANALOG", "PHYS_DIGITAL", "FILE_DIGITAL" + 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"; + public static final String FILE_NAME = "FILE_NAME", + OTHER = "OTHER"; /** Constant for an undefined integer value. */ public static final int NULL = -1; @@ -47,8 +45,8 @@ public class AESAudioMetadata ******************************************************************/ /** Constant value for the SchemaVersion field */ - public static final String SCHEMA_VERSION = "1.02b"; - + public static final String SCHEMA_VERSION = "1.0.0"; + /** Constant value for the disposition field */ private static final String DEFAULT_DISPOSITION = "validation"; @@ -78,10 +76,10 @@ public class AESAudioMetadata * CLASS CONSTRUCTOR. ******************************************************************/ - /** Instantiate a NisoImageMetadata object. + /** + * Instantiate a NisoImageMetadata object. */ - public AESAudioMetadata () - { + public AESAudioMetadata() { _schemaVersion = SCHEMA_VERSION; _disposition = DEFAULT_DISPOSITION; _analogDigitalFlag = null; @@ -92,151 +90,170 @@ public AESAudioMetadata () _primaryIdentifierType = null; _use = null; - // We add one format region to get started. In practice, + // 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 (); + _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 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: - *
    - *
  • 0: codecName - *
  • 1: codecNameVersion - *
  • 2: codecCreatorApplication - *
  • 3: codecCreatorApplicationVersion - *
  • 4: codecQuality - *
  • 5: dataRate - *
  • 6: dataRateMode - *
+ 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: + *
    + *
  • 0: codecName + *
  • 1: codecNameVersion + *
  • 2: codecCreatorApplication + *
  • 3: codecCreatorApplicationVersion + *
  • 4: codecQuality + *
  • 5: dataRate + *
  • 6: dataRateMode + *
*/ - public String[] getBitrateReduction (); + public String[] getBitrateReduction(); + /** Returns the sample rate. */ - public double getSampleRate (); + public double getSampleRate(); + /** Returns the word size. */ - int getWordSize (); + int getWordSize(); + /** Returns true if the region is empty. */ - public boolean isEmpty (); + public boolean isEmpty(); + /** Sets the bit depth value. */ - public void setBitDepth (int bitDepth); + public void setBitDepth(int bitDepth); + /** Sets the bitrate reduction information to null (no compression). */ - public void clearBitrateReduction (); + public void clearBitrateReduction(); + /** Sets the bitrate reduction (aka compression type). */ - public void setBitrateReduction (String codecName, + 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); + public void setSampleRate(double sampleRate); + /** Sets the word size. */ - public void setWordSize (int wordSize); + 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 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. */ + /** + * Returns the number of samples. + */ + public long getSamples(); + + /** + * Returns the sample rate. + */ + 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 (); - + 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 (); - + public TimeDesc getStartTime(); + /** Returns the duration. */ - public TimeDesc getDuration (); - + 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 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); + 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 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 (); - + public TimeDesc getStartTime(); + /** Returns the duration. */ - public TimeDesc getDuration (); + public TimeDesc getDuration(); - /** Returns the channel map locations. The array length must - * equal the number of channels. */ - public String[] getMapLocations (); + /** + * 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); - + 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); + 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 */ } @@ -245,101 +262,97 @@ public static 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_. + /** + * 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 () { + public FormatRegionImpl() { _bitDepth = NULL; _sampleRate = NILL; _wordSize = NULL; _bitrateReduction = null; } - + /** Returns bit depth. */ @Override - public int getBitDepth () - { + 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: - *
    - *
  • 0: codecName - *
  • 1: codecNameVersion - *
  • 2: codecCreatorApplication - *
  • 3: codecCreatorApplicationVersion - *
  • 4: codecQuality - *
  • 5: dataRate - *
  • 6: dataRateMode - *
+ + /** + * 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 () - { + public String[] getBitrateReduction() { return _bitrateReduction; } - + /** Returns sample rate. */ @Override - public double getSampleRate () - { + public double getSampleRate() { return _sampleRate; } - + /** Returns word size. */ @Override - public int getWordSize () - { + public int getWordSize() { return _wordSize; } - - /** Returns true if the FormatRegion contains only - * default values. */ + + /** + * Returns true if the FormatRegion contains only + * default values. + */ @Override - public boolean isEmpty () - { + public boolean isEmpty() { return _bitDepth == NULL && - _sampleRate == NILL && - _wordSize == NULL; + _sampleRate == NILL && + _wordSize == NULL; } /** Sets bit depth. */ @Override - public void setBitDepth (int bitDepth) - { + public void setBitDepth(int bitDepth) { _bitDepth = bitDepth; } /** Sets the bitrate reduction information to null (no compression). */ @Override - public void clearBitrateReduction () - { + public void clearBitrateReduction() { _bitrateReduction = null; } /** Sets the bitrate reduction (compression type). */ @Override - public void setBitrateReduction (String codecName, + public void setBitrateReduction(String codecName, String codecNameVersion, String codecCreatorApplication, String codecCreatorApplicationVersion, String codecQuality, String dataRate, - String dataRateMode) - { + String dataRateMode) { _bitrateReduction = new String[7]; _bitrateReduction[0] = codecName; - _bitrateReduction[1] = codecNameVersion; + _bitrateReduction[1] = codecNameVersion; _bitrateReduction[2] = codecCreatorApplication; _bitrateReduction[3] = codecCreatorApplicationVersion; _bitrateReduction[4] = codecQuality; @@ -349,275 +362,225 @@ public void setBitrateReduction (String codecName, /** Sets sample rate. */ @Override - public void setSampleRate (double sampleRate) - { + public void setSampleRate(double sampleRate) { _sampleRate = sampleRate; } - - + /** Sets word size. */ @Override - public void setWordSize (int wordSize) - { + 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_. + /** + * 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; + class TimeDescImpl implements TimeDesc { 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; - } + /* + * 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) { + _samples = samples; + _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 + } - /** 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. */ + /** + * Returns the number of samples. + */ @Override - public long getSamples () { + public long getSamples() { return _samples; } - - /** Returns the sample rate on which the samples remainder - * is based. */ + + /** + * Returns the sample rate. + */ @Override - public double getSampleRate () { + 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_. + /** + * 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 () - { + + /** + * Constructor. Initially the duration is set + * to null, indicating unknown value. + */ + public FaceImpl() { _regionList = new ArrayList<>(); - _startTime = new TimeDescImpl (0); + _startTime = new TimeDescImpl(0); _duration = null; } - /** Returns an indexed FaceRegion. */ @Override - public FaceRegion getFaceRegion (int i) { - return _regionList.get (i); + public FaceRegion getFaceRegion(int i) { + return _regionList.get(i); } - - /** Adds a FaceRegion. This may be called repeatedly to - * add multiple FaceRegions. */ + + /** + * Adds a FaceRegion. This may be called repeatedly to + * add multiple FaceRegions. + */ @Override - public void addFaceRegion () { - _regionList.add (new FaceRegionImpl ()); + public void addFaceRegion() { + _regionList.add(new FaceRegionImpl()); } - - /** Returns the starting time. Will be zero if not - * explicitly specified. */ + + /** + * Returns the starting time. Will be zero if not + * explicitly specified. + */ @Override - public TimeDesc getStartTime () { + public TimeDesc getStartTime() { return _startTime; } - - /** Returns the duration. May be null if the duration - * is unspecified. */ + + /** + * Returns the duration. May be null if the duration + * is unspecified. + */ @Override - public TimeDesc getDuration () { + public TimeDesc getDuration() { return _duration; } /** Returns the direction. */ @Override - public String getDirection () - { + public String getDirection() { return _direction; } - /** Sets the starting time. This will be converted - * into a TimeDesc. */ + /** + * Sets the starting time. This will be converted + * into a TimeDesc. + */ @Override - public void setStartTime (long samples) - { - _startTime = new TimeDescImpl (samples); + public void setStartTime(long samples) { + _startTime = new TimeDescImpl(samples); } - - /** Sets the duration. This will be converted - * into a TimeDesc. */ + + /** + * Sets the duration. This will be converted + * into a TimeDesc. + */ @Override - public void setDuration (long samples) - { - _duration = new TimeDescImpl (samples); + 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. + /** + * Sets the direction. This must be one of the + * directionTypes. FORWARD is recommended for most + * or all cases. */ @Override - public void setDirection (String direction) - { + 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_. + /** + * 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); + + public FaceRegionImpl() { + _startTime = new TimeDescImpl(0); _duration = null; } - + /** Returns the starting time. */ @Override - public TimeDesc getStartTime () { + public TimeDesc getStartTime() { return _startTime; } - + /** Returns the duration. */ @Override - public TimeDesc getDuration () { + public TimeDesc getDuration() { return _duration; } - - /** Returns the channel map locations. The array length - * will equal the number of channels. */ + + /** + * Returns the channel map locations. The array length + * will equal the number of channels. + */ @Override - public String[] getMapLocations () - { + public String[] getMapLocations() { return _mapLocations; } - /** Sets the duration. This will be converted - * into a TimeDesc. */ + /** + * Sets the duration. This will be converted + * into a TimeDesc. + */ @Override - public void setStartTime (long samples) { - _startTime = new TimeDescImpl (samples); + public void setStartTime(long samples) { + _startTime = new TimeDescImpl(samples); } - - /** Sets the duration. This will be converted - * into a TimeDesc. */ + + /** + * Sets the duration. This will be converted + * into a TimeDesc. + */ @Override - public void setDuration (long samples) - { - _duration = new TimeDescImpl (samples); + public void setDuration(long samples) { + _duration = new TimeDescImpl(samples); } - /** Sets the channel map locations. The array length must - * equal the number of channels. */ + /** + * Sets the channel map locations. The array length must + * equal the number of channels. + */ @Override - public void setMapLocations (String[] locations) - { + public void setMapLocations(String[] locations) { _mapLocations = locations; - } + } /* End of FaceRegionImpl */ } - + /* End of inner classes */ /****************************************************************** @@ -626,345 +589,327 @@ public void setMapLocations (String[] locations) * Accessor methods. ******************************************************************/ - /** Returns analog/digital flag. Value will always be - * "FILE_DIGITAL" in practice. */ - public String getAnalogDigitalFlag () - { + /** + * 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. + + /** + * Returns application-specific data. We assume this is + * representable in String format. */ - public String getAppSpecificData () - { + public String getAppSpecificData() { return _appSpecificData; } - + /** Returns audio data encoding. */ - public String getAudioDataEncoding () - { + 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 - *
+ /** + * 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 () - { + public String[] getBitrateReduction() { return _curFormatRegion.getBitrateReduction(); } /* Returns the sample rate. */ - public double getSampleRate () - { - return _curFormatRegion.getSampleRate (); + public double getSampleRate() { + return _curFormatRegion.getSampleRate(); } /** Return the byte order: 0 = big-endian; 1 = little-endian. */ - public int getByteOrder () - { + public int getByteOrder() { return _byteOrder; } - + /** Returns disposition. */ - public String getDisposition () - { + public String getDisposition() { return _disposition; } - - /** Gets the list of Faces. Normally there will be only one face - * in a digital file. */ - public List getFaceList () - { + + /** + * 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 () - { + public long getFirstSampleOffset() { return _firstSampleOffset; } /** Returns format name. */ - public String getFormat () - { + 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 (). + + /** + * 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 () - { + 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. + + /** + * 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 () - { + public int getNumChannels() { return _numChannels; } /** Returns primary identifier. */ - public String getPrimaryIdentifier () - { + public String getPrimaryIdentifier() { return _primaryIdentifier; } /** Returns primary identifier type. */ - public String getPrimaryIdentifierType () - { + public String getPrimaryIdentifierType() { return _primaryIdentifierType; } /** Returns schema version. */ - public String getSchemaVersion () - { + public String getSchemaVersion() { return _schemaVersion; } - + /** Returns specification version of the document format. */ - public String getSpecificationVersion () - { + 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 () - { + + /** + * 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) - { + + /** + * 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, + public void setBitrateReduction(String codecName, String codecNameVersion, String codecCreatorApplication, String codecCreatorApplicationVersion, String codecQuality, String dataRate, - String dataRateMode) - { - _curFormatRegion.setBitrateReduction (codecName, + 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 (); + public void clearBitrateReduction() { + _curFormatRegion.clearBitrateReduction(); } - /** Sets the byte order. + /** + * Sets the byte order. + * * @param order Byte order: 0 = big-endian, 1 = little-endian */ - public void setByteOrder (int order) - { - _byteOrder = order; + public void setByteOrder(int order) { + _byteOrder = order; } - /** Sets the byte 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; - } + public void setByteOrder(String order) { + if (order.substring(0, 3).equalsIgnoreCase("big")) { + _byteOrder = BIG_ENDIAN; + } else if (order.substring(0, 6).equalsIgnoreCase("little")) { + _byteOrder = LITTLE_ENDIAN; + } } /** Sets the audio data encoding. */ - public void setAudioDataEncoding (String audioDataEncoding) - { + 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) - { + + /** + * 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); + public void setBitDepth(int bitDepth) { + _curFormatRegion.setBitDepth(bitDepth); } - + /** Sets the disposition. */ - public void setDisposition (String 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. + /** + * 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); + public void setDirection(String direction) { + _curFace.setDirection(direction); } - - /** Sets the duration in samples. + + /** + * 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); + 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) - { + public void setFirstSampleOffset(long offset) { _firstSampleOffset = offset; } /** Sets the format name. */ - public void setFormat (String format) - { + 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 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) - { + public void setNumChannels(int numChannels) { _numChannels = numChannels; } /** Sets the primary identifier. */ - public void setPrimaryIdentifier (String primaryIdentifier) - { + public void setPrimaryIdentifier(String primaryIdentifier) { _primaryIdentifier = primaryIdentifier; } - - /** Sets the primary identifier type. If the primary identifier - * type is OTHER, use setOtherPrimaryIdentifierType instead. + + /** + * Sets the primary identifier type. If the primary identifier + * type is OTHER, use setOtherPrimaryIdentifierType instead. */ - public void setPrimaryIdentifierType (String primaryIdentifierType) - { + public void setPrimaryIdentifierType(String primaryIdentifierType) { _primaryIdentifierType = primaryIdentifierType; } - /** Sets the primary identifier type as "OTHER", and - * set the otherType. + /** + * Sets the primary identifier type as "OTHER", and + * set the otherType. */ - public void setOtherPrimaryIdentifierType (String otherType) - { - _primaryIdentifierType = "OTHER"; + 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) - { + 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. + /** + * 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}; - } - + 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 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); + public void addFormatRegion() { + _curFormatRegion = new FormatRegionImpl(); + _formatList.add(_curFormatRegion); } - - /** Adds a Face. + + /** + * Adds a Face. */ - public void addFace () - { - _curFace = new FaceImpl (); - _faceList.add (_curFace); + public void addFace() { + _curFace = new FaceImpl(); + _faceList.add(_curFace); _curFace.addFaceRegion(); } - + } 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 3ee2fedce..5265c04b7 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 @@ -12,96 +12,96 @@ */ public abstract class Message { - /****************************************************************** - * PUBLIC CLASS FIELDS. - ******************************************************************/ + /****************************************************************** + * PUBLIC CLASS FIELDS. + ******************************************************************/ - /** Value indicating a null offset. */ - public static final long NULL = -1; + /** Value indicating a null offset. */ + public static final long NULL = -1; - /****************************************************************** - * PRIVATE INSTANCE FIELDS. - ******************************************************************/ + /****************************************************************** + * PRIVATE INSTANCE FIELDS. + ******************************************************************/ protected final JhoveMessage jhoveMessage; - /** Additional information. */ + /** Additional information. */ protected final String subMessage; - /** Byte offset to which message applies. */ + /** Byte offset to which message applies. */ protected final long offset; protected final String prefix; - /****************************************************************** - * CLASS CONSTRUCTOR. - ******************************************************************/ - - /** - * 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. - */ + /****************************************************************** + * CLASS CONSTRUCTOR. + ******************************************************************/ + + /** + * 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, final String subMessage, final long offset, final String prefix) { - super(); + super(); this.jhoveMessage = message; this.subMessage = (subMessage.isEmpty()) ? null : subMessage; this.offset = offset; this.prefix = prefix; - } - - /****************************************************************** - * PUBLIC INSTANCE METHODS. - * - * Accessor methods. - ******************************************************************/ - - /** - * Returns the message text. - */ - public String getMessage() { + } + + /****************************************************************** + * PUBLIC INSTANCE METHODS. + * + * Accessor methods. + ******************************************************************/ + + /** + * Returns the message text. + */ + public String getMessage() { return this.jhoveMessage.getMessage(); - } + } - /** - * Returns the submessage text. - */ - public String getSubMessage() { + /** + * Returns the submessage text. + */ + public String getSubMessage() { return this.subMessage; - } + } - /** - * Returns the offset to which this message is related. - */ - public long getOffset() { + /** + * Returns the offset to which this message is related. + */ + public long getOffset() { return this.offset; - } + } - /** - * Returns the message's identifier. - */ - public String getId() { + /** + * Returns the message's identifier. + */ + public String getId() { return this.jhoveMessage.getId(); - } + } - public JhoveMessage getJhoveMessage() { + public JhoveMessage getJhoveMessage() { return this.jhoveMessage; - } + } - public String getPrefix() { + public String getPrefix() { return this.prefix; - } + } @Override public String toString() { 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 bc2eb388b..113ac5b79 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 @@ -185,8 +185,8 @@ public class NisoImageMetadata /** 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" + "left top", "rotated cw 90 deg", "right bottom", "rotated ccw 90 deg", + "unknown" }; /** 6.1.6 planar configuration value labels. */ 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 eebd23059..e6cfa2cc6 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 @@ -68,13 +68,13 @@ public class JsonHandler extends HandlerBase { private static final String NAME = "JSON"; /** Handler release identifier. */ - private static final String RELEASE = "1.2"; + private static final String RELEASE = "1.3"; /** String release. */ private static final String RELEASE_CONSTANT = "release"; /** Handler release date. */ - private static final int[] DATE = { 2024, 03, 05 }; + private static final int[] DATE = { 2024, 8, 22 }; private static final String DATE_CONSTANT = "date"; @@ -1648,19 +1648,19 @@ protected JsonObjectBuilder showNisoImageCaptureMetadata( n = niso.getOrientation(); if (n != NisoImageMetadata.NULL) { + if (n > 9 || n < 1) { + n = 9; // force "unknown" for reserved value + } if (bMix10) { mixBuilder.add("mix:orientation", n); } else { - final String[] orient = { "unknown", "normal*", + final String[] orient = { "", "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 - } + "normal, rotated cw 90\u00B0", "unknown" }; mixBuilder.add("mix:orientation", orient[n]); } hasBuilder = true; @@ -1944,13 +1944,11 @@ protected JsonObjectBuilder showAESAudioMetadata(AESAudioMetadata aes) { 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])); + .add("aes:channelNum", ch)); } faceBuilder.add("aes:streams", streamsBuilder); aesBuilder.add("aes:face", faceBuilder); @@ -2016,72 +2014,26 @@ private void showAesFormatList(List flist, */ 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"))); + this.writeAESTimeRangePart(timerangeBuilder, "aes:start", start); + 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"))); + this.writeAESTimeRangePart(timerangeBuilder, "aes:duration", duration); } return timerangeBuilder; } + private void writeAESTimeRangePart(final JsonObjectBuilder timerangeBuilder, final String name, + AESAudioMetadata.TimeDesc part) { + double sr = (part.getSampleRate() == 1.0) ? _sampleRate : part.getSampleRate(); + timerangeBuilder + .add(name, + Json.createObjectBuilder() + .add("aes:value", part.getSamples()) + .add("aes:editRate", (int) sr) + .add("aes:factorNumerator", "1") + .add("aes:factorDenominator", "1")); + } protected JsonArrayBuilder showArray(int[] iarray) { JsonArrayBuilder aBuilder = Json.createArrayBuilder(); for (int i : iarray) { 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..7e5c6030e 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 @@ -59,8 +59,8 @@ public class TextHandler extends HandlerBase { ******************************************************************/ 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 RELEASE = "1.7"; + private static final int[] DATE = { 2022, 8, 22 }; private static final String NOTE = "This is the default JHOVE output " + "handler"; private static final String RIGHTS = "Derived from software Copyright 2004-2011 " @@ -933,17 +933,23 @@ private void showAESAudioMetadata(AESAudioMetadata aes, String margin, if (startTime != null) { writeAESTimeRange(margn3, startTime, f.getDuration()); } + + // For the present, assume just one face region + AESAudioMetadata.FaceRegion facergn = f.getFaceRegion(0); + _writer.println(margn3 + "Region: "); + _writer.println (margn4 + "TimeRange: "); + writeAESTimeRange(margn4, facergn.getStartTime(), facergn.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++) { + + 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]); } } @@ -997,58 +1003,28 @@ private void showAESAudioMetadata(AESAudioMetadata aes, String margin, /* 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"); + writeAESTimeRangePart(baseIndent, "StartTime", start); 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"); + writeAESTimeRangePart(baseIndent, "Duration", duration); } } + private void writeAESTimeRangePart(String baseIndent, String name, AESAudioMetadata.TimeDesc timeDesc) { + String margn1 = baseIndent + " "; + String margn2 = margn1 + " "; + + _writer.println (margn1 + name + ": "); + + double sampleRate = timeDesc.getSampleRate(); + if (sampleRate == 1.0) { + sampleRate = _sampleRate; + } + + _writer.println (margn2 + "Value: " + String.valueOf(timeDesc.getSamples())); + _writer.println (margn2 + "EditRate: " + Integer.toString ((int) sampleRate)); + _writer.println (margn2 + "FactorNumerator: 1"); + _writer.println (margn2 + "FactorDenominator: 1"); + } /** * Display the NISO image metadata formatted according to the MIX schema. 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 cd98de9fb..e84b4fee3 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 @@ -83,10 +83,10 @@ protected NumberFormat initialValue() { private static final String NAME = "XML"; /** Handler release identifier. */ - private static final String RELEASE = "1.11"; + private static final String RELEASE = "1.12"; /** Handler release date. */ - private static final int[] DATE = { 2024, 03, 05 }; + private static final int[] DATE = { 2024, 8, 22 }; /** Handler informative note. */ private static final String NOTE = "This output handler is defined by the XML Schema " @@ -1311,6 +1311,9 @@ protected void showNisoBasicImageParameters02(NisoImageMetadata niso, } n = niso.getOrientation(); if (n != NisoImageMetadata.NULL) { + if (n > 9 || n < 1) { + n = 9; // force "unknown" for reserved value + } fileBuf.append(margn4 + element("mix:Orientation", Integer.toString(n)) + EOL); useFileBuf = true; @@ -2717,6 +2720,9 @@ protected void showNisoImageCaptureMetadata10(NisoImageMetadata niso, n = niso.getOrientation(); if (n != NisoImageMetadata.NULL) { + if (n > 9 || n < 1) { + n = 9; // force "unknown" for reserved value + } captureBuffer.append(margn3 + element("mix:orientation", Integer.toString(n)) + EOL); useCaptureBuffer = true; @@ -3780,15 +3786,16 @@ protected void showNisoImageCaptureMetadata20(NisoImageMetadata niso, n = niso.getOrientation(); if (n != NisoImageMetadata.NULL) { - final String[] orient = { "unknown", "normal*", + // Values defined in the MIX 2.0 schema + final String[] orient = { "", "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 + "normal, rotated cw 90\u00B0", "unknown" }; + if (n > 9 || n < 1) { + n = 9; // force "unknown" for reserved value } captureBuffer.append(margn3 + element("mix:orientation", orient[n]) + EOL); @@ -4240,7 +4247,6 @@ protected void showAESAudioMetadata(AESAudioMetadata aes) { _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 }, @@ -4248,7 +4254,7 @@ protected void showAESAudioMetadata(AESAudioMetadata aes) { aes.getAnalogDigitalFlag() }, { "disposition", "Validated by JHOVE" }, - { "schemaVersion", "1.02b" } }; + { "schemaVersion", aes.getSchemaVersion() } }; _writer.println(margin + elementStart("aes:audioObject", attrs)); String s = aes.getFormat(); if (s != null) { @@ -4333,7 +4339,7 @@ protected void showAESAudioMetadata(AESAudioMetadata aes) { AESAudioMetadata.FaceRegion facergn = f.getFaceRegion(0); _writer.println(margn3 + elementStart("aes:region", faceRegionAttrs)); _writer.println(margn4 + elementStart("aes:timeRange")); - writeAESTimeRange(margn3, + writeAESTimeRange(margn4, facergn.getStartTime(), facergn.getDuration()); _writer.println(margn4 + elementEnd("aes:timeRange")); int nchan = aes.getNumChannels(); @@ -4341,7 +4347,6 @@ protected void showAESAudioMetadata(AESAudioMetadata aes) { _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 = { @@ -4352,8 +4357,7 @@ protected void showAESAudioMetadata(AESAudioMetadata aes) { }; _writer.println(margn4 + elementStart("aes:stream", streamAttrs)); String[][] chanAttrs = { - { "channelNum", Integer.toString(ch) }, - { "mapLocation", locs[ch] } + { "channelNum", Integer.toString(ch) } }; _writer.println(margn5 + element("aes:channelAssignment", chanAttrs)); _writer.println(margn4 + elementEnd("aes:stream")); @@ -4379,7 +4383,10 @@ protected void showAESAudioMetadata(AESAudioMetadata aes) { sampleRate != AESAudioMetadata.NILL || wordSize != AESAudioMetadata.NULL) { _writer.println(margn2 + elementStart("aes:formatList")); - String[][] frAttr = { { "ID", formatRegionID } }; + String[][] frAttr = { { "ID", formatRegionID }, + { "xsi:type", "aes:formatRegionType" }, + { "ownerRef", faceRegionID }, + { "label", "JHOVE" } }; _writer.println(margn3 + elementStart("aes:formatRegion", frAttr)); if (bitDepth != AESAudioMetadata.NULL) { _writer.println(margn4 + element("aes:bitDepth", @@ -4420,36 +4427,32 @@ protected void showAESAudioMetadata(AESAudioMetadata aes) { _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; + + writeAESTimeRangePart(margn1, "aes:startTime", start); + + if (duration != null) { + writeAESTimeRangePart(margn1, "aes:duration", duration); + } + } + + private void writeAESTimeRangePart(String indent, String elementName, AESAudioMetadata.TimeDesc timeDesc) { + double sampleRate = timeDesc.getSampleRate(); + if (sampleRate == 1.0) { + sampleRate = _sampleRate; } + + String[][] attributes = { + { "editRate", formatters.get().format(sampleRate) }, + { "factorNumerator", "1" }, + { "factorDenominator", "1" } + }; + + _writer.println(indent + + element(elementName, attributes, String.valueOf(timeDesc.getSamples()))); } /* 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 7c4fc2606..5dc1a0d7a 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 @@ -45,449 +45,442 @@ @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 BYTESTREAM = "BYTESTREAM"; - 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 = - "{\"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); - - 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) { + 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 BYTESTREAM = "BYTESTREAM"; + 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 = "{\"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); + + 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); + 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() { + @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() { + + 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.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"); - + + 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.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); - + + 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.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); - + + 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.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 + ",\"repInfo\":[" + INFO_JSON + "]}}"; - - assertEquals(expected, result); - } - - @Test - public void testShowRepInfos() { - 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); - + + 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 + ",\"repInfo\":[" + INFO_JSON + "]}}"; + + assertEquals(expected, result); + } + + @Test + public void testShowRepInfos() { + 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.show(info); + handler.show(info); + 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 + ",\"repInfo\":[" + INFO_JSON + "," + INFO_JSON + "]}}"; - - assertEquals(expected, result); - } - - @Test - public void testShowVendor() { - OutputHandler jsonHandler = je.getHandler("JSON"); - Agent v = jsonHandler.getVendor(); - + + 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 + ",\"repInfo\":[" + INFO_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); + + 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); + + 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); + + 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); + + 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); + + 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); + + 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); + + 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); + + String result = outString.toString(); + LOGGER.info(FIND + result); + final String expected = "{\"aes:schemaVersion\":\"1.0.0\",\"aes:format\":\"audio/wav\"," + + "\"aes:face\":{\"aes:timeline\":{\"aes:start\":{\"aes:value\":0,\"aes:editRate\":44100," + + "\"aes:factorNumerator\":\"1\",\"aes:factorDenominator\":\"1\"}}," + + "\"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); + + 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); + + 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); + + 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); + + 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); - } + + String result = outString.toString(); + LOGGER.info(FIND + result); + final String expected = "{\"ARRAY\":[123456,43211]}"; + + assertEquals(expected, result); + } } diff --git a/jhove-ext-modules/pom.xml b/jhove-ext-modules/pom.xml index 97ed7377b..662f7edbd 100644 --- a/jhove-ext-modules/pom.xml +++ b/jhove-ext-modules/pom.xml @@ -3,7 +3,7 @@ org.openpreservation.jhove jhove - 1.30.0 + 1.32.0-RC1 jhove-ext-modules 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 5fa0e80de..e3d5eab2e 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 @@ -421,6 +421,7 @@ private static Date toDate(String isoDate) { if (isoDate == null || isoDate.length() == 0) { return null; } + isoDate = isoDate.trim(); SimpleDateFormat simpleDateFormat = new SimpleDateFormat(ISO_DATE_PATTERN); Date date; diff --git a/jhove-ext-modules/src/main/resources/edu/harvard/hul/ois/jhove/module/epub/ErrorMessages.properties b/jhove-ext-modules/src/main/resources/org/ithaka/portico/jhove/module/epub/ErrorMessages.properties similarity index 99% rename from jhove-ext-modules/src/main/resources/edu/harvard/hul/ois/jhove/module/epub/ErrorMessages.properties rename to jhove-ext-modules/src/main/resources/org/ithaka/portico/jhove/module/epub/ErrorMessages.properties index 7551a314e..4c05dfd38 100644 --- a/jhove-ext-modules/src/main/resources/edu/harvard/hul/ois/jhove/module/epub/ErrorMessages.properties +++ b/jhove-ext-modules/src/main/resources/org/ithaka/portico/jhove/module/epub/ErrorMessages.properties @@ -1,3 +1,3 @@ -EPUB-PTC-1 = FATAL, [An error occurred.] -EPUB-PTC-1-SUB = Exception: %s +EPUB-PTC-1 = FATAL, [An error occurred.] +EPUB-PTC-1-SUB = Exception: %s EPUB-PTC-2 = FATAL, [An undefined error occurred. This may be caused by a system error or indicative of a problem with the file provided.] \ No newline at end of file diff --git a/jhove-installer/pom.xml b/jhove-installer/pom.xml index df9b0e891..875c5bace 100644 --- a/jhove-installer/pom.xml +++ b/jhove-installer/pom.xml @@ -5,11 +5,11 @@ org.openpreservation.jhove jhove - 1.30.0 + 1.32.0-RC1 jhove-installer - 1.30.1 + 1.32.0-RC1 JHOVE Installer Maven-built IzPack installer for JHOVE. @@ -22,14 +22,14 @@ 1.6.2 1.4.2 1.4.3 - 1.4.3 + 1.4.4 1.4.4 1.5.4 - 1.12.6 - 1.9.4 + 1.12.7 + 1.9.5 1.7.3 1.8.3 - 1.5.4 + 1.5.5 @@ -175,7 +175,7 @@ org.openpreservation.jhove jhove-ext-modules - 1.30.0 + ${project.version} org.openpreservation.jhove.modules diff --git a/jhove-installer/src/main/izpack/install.xml b/jhove-installer/src/main/izpack/install.xml index 41ea703d1..f8ec2b446 100644 --- a/jhove-installer/src/main/izpack/install.xml +++ b/jhove-installer/src/main/izpack/install.xml @@ -107,8 +107,8 @@ Third-party validation modules developed for JHOVE, currently GZIP, PNG and WARC. - - + + diff --git a/jhove-modules/aiff-hul/pom.xml b/jhove-modules/aiff-hul/pom.xml index 1395924e3..62c8b5191 100644 --- a/jhove-modules/aiff-hul/pom.xml +++ b/jhove-modules/aiff-hul/pom.xml @@ -3,7 +3,7 @@ org.openpreservation.jhove.modules jhove-modules - 1.30.1 + 1.32.0-RC1 aiff-hul 1.6.2 diff --git a/jhove-modules/ascii-hul/pom.xml b/jhove-modules/ascii-hul/pom.xml index fbdaf3f6e..45e59316b 100644 --- a/jhove-modules/ascii-hul/pom.xml +++ b/jhove-modules/ascii-hul/pom.xml @@ -3,7 +3,7 @@ org.openpreservation.jhove.modules jhove-modules - 1.30.1 + 1.32.0-RC1 ascii-hul 1.4.2 diff --git a/jhove-modules/gif-hul/pom.xml b/jhove-modules/gif-hul/pom.xml index 2abd4cec3..519953a30 100644 --- a/jhove-modules/gif-hul/pom.xml +++ b/jhove-modules/gif-hul/pom.xml @@ -3,7 +3,7 @@ org.openpreservation.jhove.modules jhove-modules - 1.30.1 + 1.32.0-RC1 gif-hul 1.4.3 diff --git a/jhove-modules/html-hul/pom.xml b/jhove-modules/html-hul/pom.xml index 03ae60382..31e43b42a 100644 --- a/jhove-modules/html-hul/pom.xml +++ b/jhove-modules/html-hul/pom.xml @@ -3,10 +3,10 @@ org.openpreservation.jhove.modules jhove-modules - 1.30.1 + 1.32.0-RC1 html-hul - 1.4.3 + 1.4.4 JHOVE HTML Module HUL HTML module developed by Harvard University Library @@ -14,7 +14,7 @@ org.openpreservation.jhove.modules xml-hul - 1.5.4 + 1.5.5 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 6c667367c..9d3f8d727 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 @@ -93,651 +93,660 @@ */ 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.3"; - private static final int[] DATE = { 2023, 03, 16 }; - 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.HTML_HUL_23)); - _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.HTML_HUL_22)); - 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 XHTML_1_1_STR = "XHTML 1.1"; + + private static final String NAME = "HTML-hul"; + private static final String RELEASE = "1.4.4"; + private static final int[] DATE = { 2024, 8, 22 }; + 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_STR }; + + /* 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.HTML_HUL_23)); + _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.HTML_HUL_22)); + 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; + case "-//W3C//DTD XHTML 1.0 STRICT//EN": + return XHTML_1_0_STRICT; + case "-//W3C//DTD XHTML 1.0 TRANSITIONAL//EN": + return XHTML_1_0_TRANSITIONAL; + case "-//W3C//DTD XHTML 1.0 FRAMESET//EN": + return XHTML_1_0_FRAMESET; + case "-//W3C//DTD XHTML 1.1//EN": + return XHTML_1_1; + 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/jpeg-hul/pom.xml b/jhove-modules/jpeg-hul/pom.xml index 1b1d663bc..3dd165646 100644 --- a/jhove-modules/jpeg-hul/pom.xml +++ b/jhove-modules/jpeg-hul/pom.xml @@ -3,7 +3,7 @@ org.openpreservation.jhove.modules jhove-modules - 1.30.1 + 1.32.0-RC1 jpeg-hul 1.5.4 @@ -14,7 +14,7 @@ org.openpreservation.jhove.modules tiff-hul - 1.9.4 + 1.9.5 diff --git a/jhove-modules/xml-hul/src/main/resources/edu/harvard/hul/ois/jhove/module/xml/ErrorMessages_nl.properties b/jhove-modules/jpeg-hul/src/main/resources/edu/harvard/hul/ois/jhove/module/jpeg/ErrorMessages_nl.properties similarity index 100% rename from jhove-modules/xml-hul/src/main/resources/edu/harvard/hul/ois/jhove/module/xml/ErrorMessages_nl.properties rename to jhove-modules/jpeg-hul/src/main/resources/edu/harvard/hul/ois/jhove/module/jpeg/ErrorMessages_nl.properties diff --git a/jhove-modules/xml-hul/src/main/resources/edu/harvard/hul/ois/jhove/module/xml/ErrorMessages_sv.properties b/jhove-modules/jpeg-hul/src/main/resources/edu/harvard/hul/ois/jhove/module/jpeg/ErrorMessages_sv.properties similarity index 100% rename from jhove-modules/xml-hul/src/main/resources/edu/harvard/hul/ois/jhove/module/xml/ErrorMessages_sv.properties rename to jhove-modules/jpeg-hul/src/main/resources/edu/harvard/hul/ois/jhove/module/jpeg/ErrorMessages_sv.properties diff --git a/jhove-modules/jpeg2000-hul/pom.xml b/jhove-modules/jpeg2000-hul/pom.xml index 1d4557036..5bac0118c 100644 --- a/jhove-modules/jpeg2000-hul/pom.xml +++ b/jhove-modules/jpeg2000-hul/pom.xml @@ -3,7 +3,7 @@ org.openpreservation.jhove.modules jhove-modules - 1.30.1 + 1.32.0-RC1 jpeg2000-hul 1.4.4 diff --git a/jhove-modules/pdf-hul/pom.xml b/jhove-modules/pdf-hul/pom.xml index b7102d68d..9b9893727 100644 --- a/jhove-modules/pdf-hul/pom.xml +++ b/jhove-modules/pdf-hul/pom.xml @@ -3,10 +3,10 @@ org.openpreservation.jhove.modules jhove-modules - 1.30.1 + 1.32.0-RC1 pdf-hul - 1.12.6 + 1.12.7 JHOVE PDF Module HUL PDF module developed by Harvard University Library 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 2c25fd0c3..c3014d307 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 @@ -380,8 +380,8 @@ public class PdfModule extends ModuleBase { ******************************************************************/ private static final String NAME = "PDF-hul"; - private static final String RELEASE = "1.12.6"; - private static final int[] DATE = { 2024, 07, 31 }; + private static final String RELEASE = "1.12.7"; + private static final int[] DATE = { 2024, 8, 22 }; private static final String[] FORMAT = { "PDF", "Portable Document Format" }; private static final String COVERAGE = "PDF 1.0-1.6; " @@ -3492,18 +3492,10 @@ protected void addDestination(PdfObject itemObj, String propName, destPg)); } } - } catch (PdfMalformedException e) { + } catch (PdfException e) { propList.add(new Property(propName, PropertyType.STRING, PROP_VAL_NULL)); info.setMessage(new ErrorMessage(e.getJhoveMessage(), _parser.getOffset())); info.setValid(false); - } catch (PdfInvalidException e) { - if (e.getJhoveMessage() != null) { - info.setMessage(new ErrorMessage( - JhoveMessages.getMessageInstance( - e.getJhoveMessage().getId(), e.getJhoveMessage().getMessage(), - e.getJhoveMessage().getSubMessage()))); - info.setValid(false); - } } catch (Exception e) { String msg = e.getClass().getName(); @@ -4285,7 +4277,7 @@ protected boolean doOutlineStuff(RepInfo info) { * 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 { + protected int resolveIndirectDest(PdfSimpleObject key, RepInfo info) throws PdfException, IOException { if (key == null) { throw new IllegalArgumentException("Argument key can not be null"); } @@ -4302,10 +4294,7 @@ protected int resolveIndirectDest(PdfSimpleObject key, RepInfo info) throws PdfE 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(); @@ -4344,7 +4333,7 @@ protected void addStringProperty(PdfDictionary dict, * with a string value, to a specified List. */ protected void addDateProperty(PdfDictionary dict, List propList, - String key, String propName) throws PdfException { + String key, String propName) throws PdfInvalidException { if (_encrypted) { String propText = ENCRYPTED; propList.add(new Property(propName, PropertyType.STRING, propText)); @@ -4354,12 +4343,11 @@ protected void addDateProperty(PdfDictionary dict, List propList, 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 + if (!lit.getValue().isEmpty()) { + Date propDate = lit.parseDate(); + if (propDate != null) { + propList.add(new Property(propName, PropertyType.DATE, propDate)); + } } } } 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..405fd71e6 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 @@ -67,7 +67,7 @@ public final class Destination { * from a named destination. */ public Destination(final PdfObject destObj, final PdfModule module, - final boolean named) throws PdfException { + final boolean named) throws PdfException, IOException { if (destObj == null) { throw new IllegalArgumentException("Parameter destObj cannot be null."); } @@ -75,6 +75,9 @@ public Destination(final PdfObject destObj, final PdfModule module, _indirect = true; _indirectDest = (PdfSimpleObject) destObj; return; + } else if (!named && destObj instanceof PdfIndirectObj) { + _pageDest = findDirectDest(module, (PdfArray) module.resolveIndirectObject(destObj)); + return; } PdfArray destArray = null; try { 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 6bc540fd5..3a6f8fc5d 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 @@ -11,81 +11,85 @@ import java.io.IOException; /** - * 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 -{ + 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 */ + /** + * 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; + // int h1; - /** The state of the tokenization. Only the subset of States which - pertain to Literals are used here. */ + /** + * 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. */ + + /** + * 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' + 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; @@ -99,95 +103,89 @@ public class Literal private static final int FF = 0xFF; /** Creates an instance of a string literal */ - public Literal () - { - super (); + public Literal() { + super(); _pdfDocEncoding = true; - buffer = new StringBuffer (); + buffer = new StringBuffer(); haveHi = false; } /** - * Append a hex character.This is used only for hex literals - * (those that start with '<'). + * 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 + * @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 - { + public void appendHex(int ch) throws PdfException { if (_rawBytes == null) { - _rawBytes = new Vector<> (32); + _rawBytes = new Vector<>(32); } if (haveHi) { - _rawBytes.add(hexToInt (hi, ch)); + _rawBytes.add(hexToInt(hi, ch)); haveHi = false; - } - else { + } 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. + * 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 + * @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 - { + 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. */ + /* Character read from tokenizer. */ int ch; _parenLevel = 0; - _rawBytes = new Vector<> (32); + _rawBytes = new Vector<>(32); _state = State.LITERAL; - long offset = 0; + long offset = 0; for (;;) { - ch = tok.readChar (); + 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 + throw new EOFException(MessageConstants.PDF_HUL_10.getMessage()); // PDF-HUL-10 } offset++; 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) { + } else if (ch == CLOSE_PARENTHESIS && --_parenLevel < 0) { // We have an empty string - setPDFDocEncoding (true); + setPDFDocEncoding(true); setValue(buffer.toString()); return offset; - } - else if (ch == BACKSLASH) { - ch = readBackslashSequence (false, tok); + } else if (ch == BACKSLASH) { + ch = readBackslashSequence(false, tok); switch (ch) { case 0: - continue; // invalid character, ignore + continue; // invalid character, ignore case FE: _state = State.LITERAL_FE; break; default: // any other char is treated nonspecially - setPDFDocEncoding (true); - buffer.append (PDFDOCENCODING[ch]); + setPDFDocEncoding(true); + buffer.append(PDFDOCENCODING[ch]); break; } - } - else { + } else { // We now know we're in 8-bit PDF encoding. // Append the character to the buffer. if (ch == OPEN_PARENTHESIS) { @@ -196,65 +194,60 @@ else if (ch == BACKSLASH) { ++_parenLevel; } _state = State.LITERAL_PDF; - setPDFDocEncoding (true); - buffer.append (PDFDOCENCODING[ch]); + setPDFDocEncoding(true); + buffer.append(PDFDOCENCODING[ch]); } - } - else if (_state == (State.LITERAL_FE)) { + } else if (_state == (State.LITERAL_FE)) { switch (ch) { case FF: _state = State.LITERAL_UTF16_1; - setPDFDocEncoding (false); + setPDFDocEncoding(false); break; case BACKSLASH: - ch = readBackslashSequence (false, tok); + ch = readBackslashSequence(false, tok); if (ch == 0) { - continue; // invalid character, ignore - } if (ch == FF) { - _state = State.LITERAL_UTF16_1; - setPDFDocEncoding (false); + continue; // invalid character, ignore } - else { + if (ch == FF) { + _state = State.LITERAL_UTF16_1; + setPDFDocEncoding(false); + } else { // any other char is treated nonspecially - setPDFDocEncoding (true); + setPDFDocEncoding(true); // The FE is just an FE, put it in the buffer - buffer.append (PDFDOCENCODING[FE]); - buffer.append (PDFDOCENCODING[ch]); - } break; + buffer.append(PDFDOCENCODING[FE]); + buffer.append(PDFDOCENCODING[ch]); + } + break; default: _state = State.LITERAL_PDF; - setPDFDocEncoding (true); + setPDFDocEncoding(true); // The FE is just an FE, put it in the buffer - buffer.append (PDFDOCENCODING[FE]); - buffer.append (PDFDOCENCODING[ch]); + buffer.append(PDFDOCENCODING[FE]); + buffer.append(PDFDOCENCODING[ch]); break; } - } - else if (_state == (State.LITERAL_PDF)) { + } 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) { + buffer.append(PDFDOCENCODING[ch]); + } else if (ch == CLOSE_PARENTHESIS && --_parenLevel < 0) { setValue(buffer.toString()); return offset; - } - else if (ch == BACKSLASH) { - ch = readBackslashSequence (false, tok); + } else if (ch == BACKSLASH) { + ch = readBackslashSequence(false, tok); if (ch == 0) { - continue; // invalid character, ignore + continue; // invalid character, ignore } // any other char is treated nonspecially - buffer.append (PDFDOCENCODING[ch]); - } - else { - buffer.append (PDFDOCENCODING[ch]); + 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 + } 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. @@ -263,56 +256,54 @@ else if (_state == (State.LITERAL_UTF16_1)) { setValue(buffer.toString()); return offset; case BACKSLASH: - utfch = readBackslashSequence (true, tok); + utfch = readBackslashSequence(true, tok); if (utfch == 0) { - continue; // invalid character, ignore - } break; + continue; // invalid character, ignore + } + break; default: _state = State.LITERAL_UTF16_2; b1 = ch; break; } - } - else if (_state == (State.LITERAL_UTF16_2)) { + } else if (_state == (State.LITERAL_UTF16_2)) { // Second byte of a UTF16 character. - /* 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. - */ + /* + * 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 (ch == BACKSLASH) { - ch = readBackslashSequence (false, tok); + ch = readBackslashSequence(false, tok); if (ch == 0) { _state = State.LITERAL_UTF16_2; // skip the wrong char and reset to previous state - continue; /* Invalid character, ignore. */ + continue; /* Invalid character, ignore. */ } } 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 + // a language code. Remove the language code // from the stream and save it in a list of codes. if (utfch == ESC) { - readUTFLanguageCode (tok); - } - else { - buffer.append ((char) utfch); + readUTFLanguageCode(tok); + } else { + buffer.append((char) utfch); } } - _rawBytes.add (ch); + _rawBytes.add(ch); } } - - /** - * Convert the raw hex data.Two buffers are saved: _rawBytes + * 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 - { + public void convertHex() throws PdfException { if (_rawBytes != null) { boolean utf = false; StringBuilder localBuffer = new StringBuilder(); @@ -327,8 +318,7 @@ public void convertHex () throws PdfException 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))); + localBuffer.append((char) (rawByte(i) * 256 + rawByte(i + 1))); } } else { // Convert single bytes to PDF encoded characters. @@ -339,311 +329,306 @@ public void convertHex () throws PdfException _value = localBuffer.toString(); } } - - private static int hexToInt (int c1, int c2) throws PdfException - { - return 16 * hexValue (c1) + hexValue (c2); + + private static int hexToInt(int c1, int c2) throws PdfException { + return 16 * hexValue(c1) + hexValue(c2); } - private static int hexValue (int h) throws PdfException - { + 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) { + } else if (0x41 <= h && h <= 0x46) { // letter A-F d = h - 0x37; - } - else if (0x61 <= h && h <= 0x66) { + } else if (0x61 <= h && h <= 0x66) { // letter a-f d = h - 0x57; - } - else { - throw new PdfMalformedException (MessageConstants.PDF_HUL_11); // PDF-HUL-11 + } else { + throw new PdfMalformedException(MessageConstants.PDF_HUL_11); // PDF-HUL-11 } 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 ()) { + /* + * 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; } return _rawBytes.elementAt(idx); } - /** - * Returns true if this string is in PDFDocEncoding, - * false if UTF-16. + * Returns true if this string is in PDFDocEncoding, + * false if UTF-16. * - * @return isPdfDocEncoding + * @return isPdfDocEncoding */ - public boolean isPDFDocEncoding () - { + public boolean isPDFDocEncoding() { return _pdfDocEncoding; } /** - * Sets the value of pDFDocEncoding. - * @param pdfDocEncoding: boolean if the is in PDFDocEncoding + * Sets the value of pDFDocEncoding. + * + * @param pdfDocEncoding: boolean if the is in PDFDocEncoding */ - public void setPDFDocEncoding (boolean 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. + * 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; + public boolean isDate() { + try { + return parseDate() != null; + } catch (PdfInvalidException e) { + return false; + } } /** - * 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. + * 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 + * @return date of string value */ - public Date parseDate () - { + public Date parseDate() throws PdfInvalidException { + String str = this.getValue(); + if (str == null) { + return null; + } + return parseDate(str); + } + + static final Date parseDate(String dateString) throws PdfInvalidException { + String str = dateString.trim(); + if (str.length() < 4) { + throw new PdfInvalidException(MessageConstants.PDF_HUL_133, 0); // PDF-HUL-133 + } int year = 0; int month = 0; int day = 0; int hour = 0; int minute = 0; int second = 0; - char timezonechar = '?'; // +, -, or Z + 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; + 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; - } + 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) { + throw new PdfInvalidException(MessageConstants.PDF_HUL_133, 0); // PDF-HUL-133 + } + 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 != '-') { + throw new PdfInvalidException(MessageConstants.PDF_HUL_133, 0); // PDF-HUL-133 + } + 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("'")) { + throw new PdfInvalidException(MessageConstants.PDF_HUL_133, 0); // PDF-HUL-133 + } + 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.length() > 22) && (!str.substring(charidx + 2, charidx + 3).equals("'"))) { + throw new PdfInvalidException(MessageConstants.PDF_HUL_133, 0); // PDF-HUL-133 + } + } + break wloop; } - break wloop; } - } } // 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. + // end of the date. This is too permissive; an exception means + // that the date is not well-formed. catch (Exception e) { - return null; + throw new PdfInvalidException(MessageConstants.PDF_HUL_133, 0); // PDF-HUL-133 } if (datestate < 2) { - return null; // not enough fields + throw new PdfInvalidException(MessageConstants.PDF_HUL_133, 0); // PDF-HUL-133 } + return createTzDate(year, month, day, hour, minute, second, timezonehour, timezoneminute, timezonechar); + } + + static Date createTzDate(final int year, final int month, final int day, final int hour, final int minute, + final int second, final int timezonehour, final int timezoneminute, final char timezonechar) { + Calendar cal; // 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 { + } else { tzStr += timezonechar; - NumberFormat nfmt = NumberFormat.getInstance (); - nfmt.setMinimumIntegerDigits (2); - nfmt.setMaximumIntegerDigits (2); - tzStr += nfmt.format (timezonehour); - tzStr += nfmt.format (timezoneminute); + NumberFormat nfmt = NumberFormat.getInstance(); + nfmt.setMinimumIntegerDigits(2); + nfmt.setMaximumIntegerDigits(2); + tzStr += nfmt.format(timezonehour); + tzStr += nfmt.format(timezoneminute); } - TimeZone tz = TimeZone.getTimeZone (tzStr); - + 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 { + cal = Calendar.getInstance(tz); + } else { // time zone is unspecified - cal = Calendar.getInstance (); + cal = Calendar.getInstance(); } - cal.set (year, month - 1, day, hour, minute, second); - return cal.getTime (); + 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. + /** + * 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 () - { + public boolean isPDFACompliant() { return _pdfACompliant; } - - -/* private void beginBackslashState () - { - octalBufLen = 0; - backslashFlag = true; - } -*/ - + /* + * 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. + /** + * 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); + 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. + // 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); + 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 + } 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 + case 0X6E: // n return LF; - case 0X72: // r + case 0X72: // r return CR; case 0xd: // this is an error for CR - return 0; - case 0X74: // t + return 0; + case 0X74: // t return HT; - case 0X62: // b + case 0X62: // b return BS; - case 0X66: // f + case 0X66: // f return FORMFEED; case OPEN_PARENTHESIS: return OPEN_PARENTHESIS; @@ -656,102 +641,103 @@ private int readBackslashSequence (boolean utf16, Tokenizer tok) } } - - /** 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. + /** + * 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 - { + private static void readUTFLanguageCode(Tokenizer tok) throws IOException { StringBuilder sb = new StringBuilder(); for (;;) { int ch = tok.readChar1(true); - + // 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 + throw new EOFException(MessageConstants.PDF_HUL_10.getMessage()); // PDF-HUL-10 } if (ch == ESC) { break; } - sb.append ((char) ch); + sb.append((char) ch); } - tok.addLanguageCode (sb.toString ()); // ****add this to Tokenizer - //_languageCodes.add (sb.toString ()); + 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. + /** + * 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 (); - } - } - } - */ + /* + * 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/test/java/edu/harvard/hul/ois/jhove/module/pdf/LiteralTests.java b/jhove-modules/pdf-hul/src/test/java/edu/harvard/hul/ois/jhove/module/pdf/LiteralTests.java new file mode 100644 index 000000000..930665db2 --- /dev/null +++ b/jhove-modules/pdf-hul/src/test/java/edu/harvard/hul/ois/jhove/module/pdf/LiteralTests.java @@ -0,0 +1,34 @@ +package edu.harvard.hul.ois.jhove.module.pdf; + +import static org.junit.Assert.assertNotNull; + +import org.junit.Test; + +/** + * Tests for the {@link Literal} class. + * + * @author Carl Wilson + * carlwilson AT github + * @version 0.1 Created 13 Mar 2018:11:28:10 + */ + +public class LiteralTests { + /** + * Test method for {@link Literal}. + * Ensures that a valid Date passes. + * + * @throws PdfInvalidException + */ + @Test + public final void testValidDates() throws PdfInvalidException { + assertNotNull(Literal.parseDate("D:20180313112810Z")); + assertNotNull(Literal.parseDate("D:20180313112810+01'00'")); + assertNotNull(Literal.parseDate("D:20180313112810+01'00")); + } + + @Test(expected = PdfInvalidException.class) + public final void testInvalidDate() throws PdfInvalidException { + Literal.parseDate("D:20180313112810+01'00`"); + } + +} diff --git a/jhove-modules/pom.xml b/jhove-modules/pom.xml index be67b6629..af3133987 100644 --- a/jhove-modules/pom.xml +++ b/jhove-modules/pom.xml @@ -5,13 +5,13 @@ org.openpreservation.jhove jhove - 1.30.0 + 1.32.0-RC1 org.openpreservation.jhove.modules jhove-modules pom - 1.30.1 + 1.32.0-RC1 JHOVE Validation Modules The JHOVE HUL validation modules. @@ -19,7 +19,7 @@ org.openpreservation.jhove jhove-core - 1.30.0 + 1.32.0-RC1 org.junit.vintage diff --git a/jhove-modules/tiff-hul/pom.xml b/jhove-modules/tiff-hul/pom.xml index 1bae3e28d..dc4661644 100644 --- a/jhove-modules/tiff-hul/pom.xml +++ b/jhove-modules/tiff-hul/pom.xml @@ -3,10 +3,10 @@ org.openpreservation.jhove.modules jhove-modules - 1.30.1 + 1.32.0-RC1 tiff-hul - 1.9.4 + 1.9.5 JHOVE TIFF Module HUL TIFF module developed by Harvard University Library 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 7d0d6318f..26c61c894 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 @@ -121,8 +121,8 @@ public class TiffModule extends ModuleBase { protected Logger _logger; private static final String NAME = "TIFF-hul"; - private static final String RELEASE = "1.9.4"; - private static final int[] DATE = { 2023, 03, 16 }; + private static final String RELEASE = "1.9.5"; + private static final int[] DATE = { 2024, 8, 22 }; 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, " @@ -1228,7 +1228,7 @@ protected IFD parseIFDChain(long next, RepInfo info, int type, ifd.setThumbnail(true); } list.add(ifd); - + if (list.size() > 50) { throw new TiffException(MessageConstants.TIFF_HUL_60); } 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..5bc48e037 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 @@ -1131,6 +1131,8 @@ public Property getProperty(boolean rawOutput) throws TiffException { // This function has gotten obscenely large. Split it up. addNisoProperties(entries, rawOutput); addMiscProperties(entries, rawOutput); + // Add Strip and Tile information from NISO since not displayed in MIX v1.0+ + addNonDisplayedNisoProperties(entries, rawOutput); addTiffITProperties(entries, rawOutput); addTiffEPProperties(entries, rawOutput); addGeoTiffProperties(entries, rawOutput); @@ -1340,6 +1342,58 @@ private void addMiscProperties(List entries, boolean rawOutput) { } } + // Add Strip and Tile information from NISO since not displayed in v1.0 and subsequent + private void addNonDisplayedNisoProperties(List entries, boolean rawOutput) { + if (_niso == null) return; + + int segmentType = _niso.getSegmentType(); + if (segmentType != NULL) { + entries.add(new Property("SegmentType", PropertyType.INTEGER, + segmentType)); + } + long[] stripOffsets = _niso.getStripOffsets(); + if (stripOffsets != null) { + entries.add(new Property("StripOffsets", PropertyType.LONG, + PropertyArity.ARRAY, stripOffsets)); + } + long rowsPerStrip = _niso.getRowsPerStrip(); + if (rowsPerStrip != NULL) { + entries.add(new Property("RowsPerStrip", PropertyType.LONG, + rowsPerStrip)); + } + long[] stripByteCounts = _niso.getStripByteCounts(); + if (stripByteCounts != null) { + entries.add(new Property("StripByteCounts", PropertyType.LONG, + PropertyArity.ARRAY, stripByteCounts)); + } + + long tileWidth = _niso.getTileWidth(); + if (tileWidth != NULL) { + entries.add(new Property("TileWidth", PropertyType.LONG, + tileWidth)); + } + long tileLength = _niso.getTileLength(); + if (tileLength != NULL) { + entries.add(new Property("TileLength", PropertyType.LONG, + tileLength)); + } + long[] tileOffsets = _niso.getTileOffsets(); + if (tileOffsets != null) { + entries.add(new Property("TileOffsets", PropertyType.LONG, + PropertyArity.ARRAY, tileOffsets)); + } + long[] tileByteCounts = _niso.getTileByteCounts(); + if (tileByteCounts != null) { + entries.add(new Property("TileByteCounts", PropertyType.LONG, + PropertyArity.ARRAY, tileByteCounts)); + } + int planarConfiguration = _niso.getPlanarConfiguration(); + if (planarConfiguration != NULL) { + entries.add(new Property("PlanarConfiguration", PropertyType.INTEGER, + planarConfiguration)); + } + } + private void addTiffITProperties(List entries, boolean rawOutput) { /* Add TIFF/IT properties. */ diff --git a/jhove-modules/utf8-hul/pom.xml b/jhove-modules/utf8-hul/pom.xml index b8b5c2d19..568545ac3 100644 --- a/jhove-modules/utf8-hul/pom.xml +++ b/jhove-modules/utf8-hul/pom.xml @@ -3,7 +3,7 @@ org.openpreservation.jhove.modules jhove-modules - 1.30.1 + 1.32.0-RC1 utf8-hul 1.7.3 @@ -19,7 +19,7 @@ org.openpreservation.jhove.modules pdf-hul - 1.12.1 + 1.12.7 test diff --git a/jhove-modules/wave-hul/pom.xml b/jhove-modules/wave-hul/pom.xml index 33a5a90c6..7dae61bda 100644 --- a/jhove-modules/wave-hul/pom.xml +++ b/jhove-modules/wave-hul/pom.xml @@ -3,7 +3,7 @@ org.openpreservation.jhove.modules jhove-modules - 1.30.1 + 1.32.0-RC1 wave-hul 1.8.3 diff --git a/jhove-modules/xml-hul/pom.xml b/jhove-modules/xml-hul/pom.xml index 41cbd791c..c074ddec0 100644 --- a/jhove-modules/xml-hul/pom.xml +++ b/jhove-modules/xml-hul/pom.xml @@ -3,10 +3,10 @@ org.openpreservation.jhove.modules jhove-modules - 1.30.1 + 1.32.0-RC1 xml-hul - 1.5.4 + 1.5.5 JHOVE XML Module HUL XML module developed by Harvard University Library 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 2e6669e97..1abf44cff 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 @@ -49,8 +49,8 @@ public class XmlModule extends ModuleBase { private static final String NAME = "XML-hul"; - private static final String RELEASE = "1.5.4"; - private static final int[] DATE = { 2024, 03, 05 }; + private static final String RELEASE = "1.5.5"; + private static final int[] DATE = { 2024, 8, 22 }; private static final String[] FORMAT = { "XML", "XHTML" }; private static final String COVERAGE = "XML 1.0"; private static final String[] MIMETYPE = { "text/xml", "application/xml", @@ -396,13 +396,7 @@ public int parse(InputStream stream, RepInfo info, int parseIndex) { if (handler.getSigFlag() && !_parseFromSig) { info.setSigMatch(_name); } - info.setMessage(new ErrorMessage( - MessageConstants.XML_HUL_1, - MessageFormat.format( - MessageConstants.XML_HUL_1_SUB.getMessage(), - spe.getMessage(), - spe.getLineNumber(), - spe.getColumnNumber()))); + info.setMessage(new ErrorMessage(MessageConstants.INSTANCE.makeSaxParseMessage(spe))); info.setWellFormed(false); return 0; } catch (SAXException se) { 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 07fc5baec..ee14bb2e5 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 @@ -1,5 +1,9 @@ package edu.harvard.hul.ois.jhove.module.xml; +import java.text.MessageFormat; + +import org.xml.sax.SAXParseException; + import edu.harvard.hul.ois.jhove.messages.JhoveMessage; import edu.harvard.hul.ois.jhove.messages.JhoveMessageFactory; import edu.harvard.hul.ois.jhove.messages.JhoveMessages; @@ -63,4 +67,15 @@ public enum MessageConstants { .getMessage("XML-HUL-12"); public static final JhoveMessage XML_HUL_13 = messageFactory .getMessage("XML-HUL-13"); + + public final JhoveMessage makeSaxParseMessage(final SAXParseException spe) { + return JhoveMessages.getMessageInstance( + MessageConstants.XML_HUL_1.getId(), + MessageFormat.format(MessageConstants.XML_HUL_1.getMessage(), + spe.getMessage()), + MessageFormat.format( + MessageConstants.XML_HUL_1_SUB.getMessage(), + spe.getLineNumber(), + spe.getColumnNumber())); + } } 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 28119dada..90f90304a 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 @@ -13,7 +13,12 @@ import java.net.URL; import java.net.URLConnection; import java.text.MessageFormat; -import java.util.*; +import java.util.HashMap; +import java.util.HashSet; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.Set; import org.xml.sax.Attributes; import org.xml.sax.InputSource; @@ -34,495 +39,497 @@ */ public class XmlModuleHandler extends DefaultHandler { - /** Map of namespace prefixes to URIs. */ - 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; - _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) { - for (int i = 0; i < atts.getLength(); i++) { - String name = atts.getLocalName(i); - String namespace = atts.getURI(i); - String val = atts.getValue(i); - if ("http://www.w3.org/2001/XMLSchema-instance" - .equals(namespace)) { - if ("schemaLocation".equals(name)) { - // schemaLocation should contain pairs of namespace - // and location URIs separated by white space. Any - // number of such pairs may be declared in a single - // schemaLocation attribute. - String[] uris = val.trim().split("\\s+"); - for (int j = 0; j < uris.length; j += 2) { - SchemaInfo schema = new SchemaInfo(); - schema.namespaceURI = uris[j]; - if (uris.length > j + 1) { - schema.location = uris[j + 1]; - } else { - schema.location = ""; - } - if (!hasSchemaURI(schema)) { - _schemas.add(schema); - } - } - } - if ("noNamespaceSchemaLocation".equals(name)) { - SchemaInfo schema = new SchemaInfo(); - schema.location = val; - schema.namespaceURI = ""; - if (!hasSchemaURI(schema)) { - _schemas.add(schema); - } - } - } - // 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 method to - * provide special-case handling for external entity resolution. - * - * First looks for external entities that are stored as - * local resources and uses those if available (faster and - * more reliable than using the internet), preferring - * user-supplied resources over packaged resources. Then - * attempts to resolve any URLs that result in redirection - * requests. - * - * Finally, returns null if the entity doesn't - * require special handling, allowing the SAX implementation - * to attempt its own resolution. - */ - @Override - public InputSource resolveEntity(String publicId, String systemId) - throws SAXException - { - // Check for XHTML DTDs - if (!_xhtmlFlag && DTDMapper.isXHTMLDTD(publicId)) { - _xhtmlFlag = true; - } - // 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; - } - - // Attempt to resolve system identifiers to schemas from config. - // This takes precedence, allowing users to override JHOVE resources. - File fil = _localSchemas.get(systemId.toLowerCase()); - if (fil != null) { - try { - FileInputStream inStrm = new FileInputStream(fil); - return new InputSource(inStrm); - } catch (FileNotFoundException fnfe) { - // File locations should be verified before - // being added to the list of local schemas. - } - } - - // Attempt to resolve public identifiers to local JHOVE resources. - InputSource ent = DTDMapper.publicIDToFile(publicId); - - // Attempt to resolve redirected URLs. - if (ent == null) { - try { - URLConnection conn = new URL(systemId).openConnection(); - if (conn instanceof HttpURLConnection) { - int status = ((HttpURLConnection) conn).getResponseCode(); - if (status == HttpURLConnection.HTTP_MOVED_TEMP - || status == HttpURLConnection.HTTP_MOVED_PERM - || status == HttpURLConnection.HTTP_SEE_OTHER) { - - String newUrl = conn.getHeaderField("Location"); - conn = new URL(newUrl).openConnection(); - ent = new InputSource(conn.getInputStream()); - } - } - } - catch (IOException ioe) { - // Malformed URL or bad connection. - throw new SAXException(ioe); - } - } else { - // A little magic so SAX won't give up in advance on - // relative URI's. - ent.setSystemId("http://hul.harvard.edu/hul"); - } - - 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 spe) { - _messages.add(new InfoMessage( - MessageConstants.XML_HUL_1, - spe.getMessage())); - } - - /** - * 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 spe) { - _valid = false; - if (_nErrors == MAXERRORS) { - JhoveMessage message = JhoveMessages.getMessageInstance( - MessageConstants.XML_HUL_2.getId(), - MessageFormat.format( - MessageConstants.XML_HUL_2.getMessage(), - MAXERRORS)); - _messages.add(new InfoMessage(message)); - } else if (_nErrors < MAXERRORS) { - _messages.add(new ErrorMessage( - MessageConstants.XML_HUL_1, - MessageFormat.format( - MessageConstants.XML_HUL_1_SUB.getMessage(), - spe.getMessage(), - spe.getLineNumber(), - spe.getColumnNumber() - ))); - } - ++_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) { - for (SchemaInfo schema : _schemas) { - if (newinfo.namespaceURI.equals(schema.namespaceURI)) { - if (schema.location.isEmpty() && !newinfo.location.isEmpty()) { - _schemas.remove(schema); - return false; // we like the new info better - } - return true; - } - } - return false; - } + /** Map of namespace prefixes to URIs. */ + 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; + _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) { + for (int i = 0; i < atts.getLength(); i++) { + String name = atts.getLocalName(i); + String namespace = atts.getURI(i); + String val = atts.getValue(i); + if ("http://www.w3.org/2001/XMLSchema-instance" + .equals(namespace)) { + if ("schemaLocation".equals(name)) { + // schemaLocation should contain pairs of namespace + // and location URIs separated by white space. Any + // number of such pairs may be declared in a single + // schemaLocation attribute. + String[] uris = val.trim().split("\\s+"); + for (int j = 0; j < uris.length; j += 2) { + SchemaInfo schema = new SchemaInfo(); + schema.namespaceURI = uris[j]; + if (uris.length > j + 1) { + schema.location = uris[j + 1]; + } else { + schema.location = ""; + } + if (!hasSchemaURI(schema)) { + _schemas.add(schema); + } + } + } + if ("noNamespaceSchemaLocation".equals(name)) { + SchemaInfo schema = new SchemaInfo(); + schema.location = val; + schema.namespaceURI = ""; + if (!hasSchemaURI(schema)) { + _schemas.add(schema); + } + } + } + // 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 method to + * provide special-case handling for external entity resolution. + * + * First looks for external entities that are stored as + * local resources and uses those if available (faster and + * more reliable than using the internet), preferring + * user-supplied resources over packaged resources. Then + * attempts to resolve any URLs that result in redirection + * requests. + * + * Finally, returns null if the entity doesn't + * require special handling, allowing the SAX implementation + * to attempt its own resolution. + */ + @Override + public InputSource resolveEntity(String publicId, String systemId) + throws SAXException { + // Check for XHTML DTDs + if (!_xhtmlFlag && DTDMapper.isXHTMLDTD(publicId)) { + _xhtmlFlag = true; + } + // 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; + } + + // Attempt to resolve system identifiers to schemas from config. + // This takes precedence, allowing users to override JHOVE resources. + File fil = _localSchemas.get(systemId.toLowerCase()); + if (fil != null) { + try { + FileInputStream inStrm = new FileInputStream(fil); + return new InputSource(inStrm); + } catch (FileNotFoundException fnfe) { + // File locations should be verified before + // being added to the list of local schemas. + } + } + + // Attempt to resolve public identifiers to local JHOVE resources. + InputSource ent = DTDMapper.publicIDToFile(publicId); + + // Attempt to resolve redirected URLs. + if (ent == null) { + return this.resolveEntity(systemId); + } else { + // A little magic so SAX won't give up in advance on + // relative URI's. + ent.setSystemId("http://hul.harvard.edu/hul"); + } + + return ent; + } + + private final InputSource resolveEntity(String entityUrl) throws SAXException { + try { + URLConnection conn = new URL(entityUrl).openConnection(); + if (conn instanceof HttpURLConnection) { + int status = ((HttpURLConnection) conn).getResponseCode(); + if (status == HttpURLConnection.HTTP_OK) { + return new InputSource(conn.getInputStream()); + } + if (status == HttpURLConnection.HTTP_MOVED_TEMP + || status == HttpURLConnection.HTTP_MOVED_PERM + || status == HttpURLConnection.HTTP_SEE_OTHER + || status == 307 + || status == 308) { + + String newUrl = conn.getHeaderField("Location"); + return this.resolveEntity(newUrl); + } + } + } catch (IOException ioe) { + // Malformed URL or bad connection. + System.err.println("Error resolving entity: " + ioe.getMessage()); + throw new SAXException(ioe); + } + return null; + } + + /** + * 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 spe) { + JhoveMessage message = JhoveMessages.getMessageInstance( + MessageConstants.XML_HUL_1.getId(), MessageFormat.format( + MessageConstants.XML_HUL_1.getMessage(), spe.getMessage())); + _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 spe) { + _valid = false; + if (_nErrors == MAXERRORS) { + JhoveMessage message = JhoveMessages.getMessageInstance( + MessageConstants.XML_HUL_2.getId(), + MessageFormat.format( + MessageConstants.XML_HUL_2.getMessage(), + MAXERRORS)); + _messages.add(new InfoMessage(message)); + } else if (_nErrors < MAXERRORS) { + _messages.add(new ErrorMessage(MessageConstants.INSTANCE.makeSaxParseMessage(spe))); + } + ++_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) { + for (SchemaInfo schema : _schemas) { + if (newinfo.namespaceURI.equals(schema.namespaceURI)) { + if (schema.location.isEmpty() && !newinfo.location.isEmpty()) { + _schemas.remove(schema); + return false; // we like the new info better + } + return true; + } + } + return false; + } } diff --git a/jhove-modules/xml-hul/src/main/resources/edu/harvard/hul/ois/jhove/module/xml/ErrorMessages.properties b/jhove-modules/xml-hul/src/main/resources/edu/harvard/hul/ois/jhove/module/xml/ErrorMessages.properties index 66279c8d7..761943ccd 100644 --- a/jhove-modules/xml-hul/src/main/resources/edu/harvard/hul/ois/jhove/module/xml/ErrorMessages.properties +++ b/jhove-modules/xml-hul/src/main/resources/edu/harvard/hul/ois/jhove/module/xml/ErrorMessages.properties @@ -1,5 +1,5 @@ -XML-HUL-1 = SAXParseException -XML-HUL-1-SUB = {0} Line = {1,number,integer}, Column = {2,number,integer}. +XML-HUL-1 = SAXParseException: {0} +XML-HUL-1-SUB = Line = {0,number,integer}, Column = {1,number,integer}. XML-HUL-2 = Error messages in excess of {0,number,integer} not reported. XML-HUL-3 = SAXException: {0} XML-HUL-4 = Not able to determine end-of-line type. diff --git a/jhove-modules/xml-hul/src/main/resources/edu/harvard/hul/ois/jhove/module/xml/ErrorMessages_da.properties b/jhove-modules/xml-hul/src/main/resources/edu/harvard/hul/ois/jhove/module/xml/ErrorMessages_da.properties index 8b26bd61e..3c907a6f2 100644 --- a/jhove-modules/xml-hul/src/main/resources/edu/harvard/hul/ois/jhove/module/xml/ErrorMessages_da.properties +++ b/jhove-modules/xml-hul/src/main/resources/edu/harvard/hul/ois/jhove/module/xml/ErrorMessages_da.properties @@ -1,4 +1,5 @@ XML-HUL-1 = SaxParseException: {0} +XML-HUL-1-SUB = Line = {0,number,integer}, Column = {1,number,integer}. XML-HUL-2 = Fejlmeddelelser flere end {0} ikke rapporteret XML-HUL-3 = SaxParseException: {0} XML-HUL-4 = Ikke i stand til at bestemme typen af linjeslutning diff --git a/jhove-modules/xml-hul/src/main/resources/edu/harvard/hul/ois/jhove/module/xml/ErrorMessages_en.properties b/jhove-modules/xml-hul/src/main/resources/edu/harvard/hul/ois/jhove/module/xml/ErrorMessages_en.properties index 66279c8d7..761943ccd 100644 --- a/jhove-modules/xml-hul/src/main/resources/edu/harvard/hul/ois/jhove/module/xml/ErrorMessages_en.properties +++ b/jhove-modules/xml-hul/src/main/resources/edu/harvard/hul/ois/jhove/module/xml/ErrorMessages_en.properties @@ -1,5 +1,5 @@ -XML-HUL-1 = SAXParseException -XML-HUL-1-SUB = {0} Line = {1,number,integer}, Column = {2,number,integer}. +XML-HUL-1 = SAXParseException: {0} +XML-HUL-1-SUB = Line = {0,number,integer}, Column = {1,number,integer}. XML-HUL-2 = Error messages in excess of {0,number,integer} not reported. XML-HUL-3 = SAXException: {0} XML-HUL-4 = Not able to determine end-of-line type. diff --git a/jhove-modules/xml-hul/src/main/resources/edu/harvard/hul/ois/jhove/module/xml/ErrorMessages_fr.properties b/jhove-modules/xml-hul/src/main/resources/edu/harvard/hul/ois/jhove/module/xml/ErrorMessages_fr.properties index 9ef528bdc..70aafaf56 100644 --- a/jhove-modules/xml-hul/src/main/resources/edu/harvard/hul/ois/jhove/module/xml/ErrorMessages_fr.properties +++ b/jhove-modules/xml-hul/src/main/resources/edu/harvard/hul/ois/jhove/module/xml/ErrorMessages_fr.properties @@ -1,4 +1,5 @@ XML-HUL-1 = SaxParseException : {0} +XML-HUL-1-SUB = Line = {0,number,integer}, Column = {1,number,integer}. XML-HUL-2 = {0} messages d'erreur supplémentaires non signalés XML-HUL-3 = SaxParseException : {0} XML-HUL-4 = Impossible de déterminer le type de fin de ligne diff --git a/jhove-modules/xml-hul/src/main/resources/edu/harvard/hul/ois/jhove/module/xml/ErrorMessages_pt_BR.properties b/jhove-modules/xml-hul/src/main/resources/edu/harvard/hul/ois/jhove/module/xml/ErrorMessages_pt_BR.properties index 3844b4799..b25b8ecc8 100644 --- a/jhove-modules/xml-hul/src/main/resources/edu/harvard/hul/ois/jhove/module/xml/ErrorMessages_pt_BR.properties +++ b/jhove-modules/xml-hul/src/main/resources/edu/harvard/hul/ois/jhove/module/xml/ErrorMessages_pt_BR.properties @@ -1,4 +1,5 @@ XML-HUL-1 = SaxParseException: {0} +XML-HUL-1-SUB = Line = {0,number,integer}, Column = {1,number,integer}. XML-HUL-2 = As mensagens de erro acima de {0} não reportadas XML-HUL-3 = SaxParseException: {0} XML-HUL-4 = Não foi possível determinar o tipo de fim de linha diff --git a/pom.xml b/pom.xml index 053b0f762..02ce407c8 100644 --- a/pom.xml +++ b/pom.xml @@ -10,7 +10,7 @@ org.openpreservation.jhove jhove - 1.30.0 + 1.32.0-RC1 pom JHOVE - JSTOR/Harvard Object Validation Environment diff --git a/test-root/corpora/errors/modules/HTML-hul/xhtml-1-1-no-xml-dec.html b/test-root/corpora/errors/modules/HTML-hul/xhtml-1-1-no-xml-dec.html new file mode 100644 index 000000000..073974613 --- /dev/null +++ b/test-root/corpora/errors/modules/HTML-hul/xhtml-1-1-no-xml-dec.html @@ -0,0 +1,17 @@ + + + + TITLE + + + + +

Moved to example.org.

+ + diff --git a/test-root/corpora/errors/modules/HTML-hul/xhtml-1-1-xml-dec.html b/test-root/corpora/errors/modules/HTML-hul/xhtml-1-1-xml-dec.html new file mode 100644 index 000000000..d9d134902 --- /dev/null +++ b/test-root/corpora/errors/modules/HTML-hul/xhtml-1-1-xml-dec.html @@ -0,0 +1,18 @@ + + + + + TITLE + + + + +

Moved to example.org.

+ + diff --git a/test-root/corpora/errors/modules/HTML-hul/xhtml-frames-no-xml-dec.html b/test-root/corpora/errors/modules/HTML-hul/xhtml-frames-no-xml-dec.html new file mode 100644 index 000000000..834ce09a0 --- /dev/null +++ b/test-root/corpora/errors/modules/HTML-hul/xhtml-frames-no-xml-dec.html @@ -0,0 +1,14 @@ + + + + + TITLE + + + + +
+ TEXT +
+ + \ No newline at end of file diff --git a/test-root/corpora/errors/modules/HTML-hul/xhtml-frames-xml-dec.html b/test-root/corpora/errors/modules/HTML-hul/xhtml-frames-xml-dec.html new file mode 100644 index 000000000..a70e95b7c --- /dev/null +++ b/test-root/corpora/errors/modules/HTML-hul/xhtml-frames-xml-dec.html @@ -0,0 +1,15 @@ + + + + + + TITLE + + + + +
+ TEXT +
+ + \ No newline at end of file diff --git a/test-root/corpora/errors/modules/HTML-hul/xhtml-strict-no-xml-dec.html b/test-root/corpora/errors/modules/HTML-hul/xhtml-strict-no-xml-dec.html new file mode 100644 index 000000000..eab2c34ff --- /dev/null +++ b/test-root/corpora/errors/modules/HTML-hul/xhtml-strict-no-xml-dec.html @@ -0,0 +1,14 @@ + + + + + TITLE + + + + +
+ TEXT +
+ + \ No newline at end of file diff --git a/test-root/corpora/errors/modules/HTML-hul/xhtml-strict-xml-dec.html b/test-root/corpora/errors/modules/HTML-hul/xhtml-strict-xml-dec.html new file mode 100644 index 000000000..746c647ca --- /dev/null +++ b/test-root/corpora/errors/modules/HTML-hul/xhtml-strict-xml-dec.html @@ -0,0 +1,15 @@ + + + + + + TITLE + + + + +
+ TEXT +
+ + \ No newline at end of file diff --git a/test-root/corpora/errors/modules/HTML-hul/xhtml-trans-no-xml-dec.html b/test-root/corpora/errors/modules/HTML-hul/xhtml-trans-no-xml-dec.html new file mode 100644 index 000000000..793523694 --- /dev/null +++ b/test-root/corpora/errors/modules/HTML-hul/xhtml-trans-no-xml-dec.html @@ -0,0 +1,14 @@ + + + + + TITLE + + + + +
+ TEXT +
+ + \ No newline at end of file diff --git a/test-root/corpora/errors/modules/HTML-hul/xhtml-trans-xml-dec.html b/test-root/corpora/errors/modules/HTML-hul/xhtml-trans-xml-dec.html new file mode 100644 index 000000000..ddac982d5 --- /dev/null +++ b/test-root/corpora/errors/modules/HTML-hul/xhtml-trans-xml-dec.html @@ -0,0 +1,15 @@ + + + + + + TITLE + + + + +
+ TEXT +
+ + \ No newline at end of file diff --git a/test-root/corpora/errors/modules/XML-hul/0003.xml b/test-root/corpora/errors/modules/XML-hul/0003.xml new file mode 100644 index 000000000..7317dfea6 --- /dev/null +++ b/test-root/corpora/errors/modules/XML-hul/0003.xml @@ -0,0 +1,12248 @@ + + + + mm10 + + //docstorage2/impdata1/IN/NZ_17/WT/1920/WT_19200303/PM_01/0003.tif + + + + + CCS Content Conversion Specialists GmbH, Hamburg, Germany + CCS docWorks + 7.2-0.59 + + + + + ABBYY (BIT Software), Russia + FineReader + 12.0 + + + + + CCS Content Conversion Specialists GmbH, Hamburg, Germany + CCS docWorks + 7.2-0.59 + docWorks: Text modified manually, or automatically replaced + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/test-root/corpora/errors/modules/XML-hul/12745764.xml b/test-root/corpora/errors/modules/XML-hul/12745764.xml new file mode 100644 index 000000000..6a5eb0e29 --- /dev/null +++ b/test-root/corpora/errors/modules/XML-hul/12745764.xml @@ -0,0 +1,4273 @@ + + + + + + +F + +O + +U + + +o +n +t + +a +v +e +c + +c +e +u +x + +d +e +s + +o +r +g +u +e +s + +; + +t +e +l +s + +f +o +n +t + +c +e +u +x + +d +e + +l +i + +f +o +r +g +e + +& + +d +u + +f +o +u +r +n +e +a +u + +d +e + +f +u +s +i +o +n + +f +i +g +. + +8 +. + +A +u + +r +e +s +t +e + +c + +e +s +t + +l +e +u +r + +u +s +a +g +e +, + +& + +n +o +n + +! +a + +f +i +g +u +r +e +, + +q +u +i + +d +é +c +i +d +e +. + +O +n + +a +p +p +e +l +l +e + +e +n +c +o +r +e + +r +e +g +i +s +t +r +e +s + +l +e +s + +i +n +f +t +r +u +m +e +n +s + +o +u + +c +e +s + +p +e +t +i +t +s + +p +a +r +a +l +l +é +l +i +p +i +p +e +d +e +s + +d +e + +t +e +r +r +e + +c +u +i +t +e +, + +q +u + +o +n + +m +e +t + +d +e +v +a +n +t + +l +e +s + +s +o +u +¬ + +p +i +r +a +u +x + +d +e + +l +a + +m +o +u +f +l +e +. + + +U +n +e + +o +u +v +e +r +t +u +r +e + +f +e +u +l +e + +a +u + +m +i +l +i +e +u + +d +u + +d +ô +m +e +, + +f +a +i +t + +q +u +e + +l +a + +c +h +a +l +e +u +r + +e +s +t + +p +a +r +- +t +o +u +t + +é +g +a +l +e + +d +a +n +s + +1 + +q + +f +o +u +r +n +e +a +u +, + +S +t + +p +l +u +s + +c +o +n +c +e +n +t +r +é +e + +; + +d + +a +i +l +l +e +u +r +s + +i +l + +e +s +t + +p +l +u +s + +a +i +s +é + +d +e + +l +a + +f +e +r +m +e +r +. + +Q +u +a +n +d + +i +l + +y + +e +n + +a + +t +r +o +i +s + +o +u + +q +u +a +t +r +e +, + +i +l + +f +a +u +t + +l +e +s + +t +e +n +i +r + +t +o +u +j +o +u +r +s + +o +u +v +e +r +t +s +, + +o +u + +s +i + +o +n + +l +e +s + +f +e +r +m +e + +d +a +n +s + +l +a + +f +u +i +t +e +, + +n +e + +l +e +s + +p +a +s + +r +o +u +v +r +i +r + +; + +c +a +r + +i +l + +a +r +r +i +v +e + +q +u +e + +f +a + +p +a +r +t +i +e + +d +e + +l +a + +r +e +t +o +r +t +e + +q +u +i + +e +s +t + +v +i +s +- +à +- +v +i +s +, + +& + +q +u +i + +s + +e +s +t + +r +e +f +r +o +i +d +i +e + +p +e +n +d +a +n +t + +q +u + +i +l +s + +o +n +t + +é +t +é + +f +e +r +m +é +s +, + +p +a +r +c +e + +q +u +e + +l +a + +c +h +a +l +e +u +r + +n + +a + +p +l +u +s + +é +t +é + +d +é +t +e +r +m +i +n +é +e + +d +e + +c +e + +c +ó +t +é +- +l +à + +, + +f +e + +f +e +n +d + +p +a +r +c +e + +q +u + +e +l +l +e + +e +s +t + +f +r +a +p +p +é +e + +d + +u +n +e + +c +h +a +l +e +u +r + +s +u +b +i +t +e + +: + +c +e +t + +i +n +c +o +n +v +é +n +i +e +n +t + +a +r +r +i +v +e + +d + +a +u +t +a +n +t + +m +i +e +u +x + +q +u + +e +l +l +e + +e +s +t + +p +l +u +s + +é +p +a +i +í +í +è +, + +p +a +r + +l +a + +r +a +i +s +o +n + +q +u +e + +l +a + +t +a +b +l +e + +i +n +t +e +r +n +e + +n +e + +p +e +u +t + +p +a +s + +ê +t +r +e + +d +i +l +a +t +é +e + +e +n + +m +ê +m +e + +t +e +m +s + +q +u +e + +l + +e +x +t +e +r +n +e +. + +C +e +t + +u +s +a +g +e + +d + +u +n + +s +e +u +l + +r +e +g +i +s +t +r +e + +a +u + +m +i +l +i +e +u + +d +u +d +ô +m +e + +e +s +t + +f +o +r +t + +a +n +c +i +e +n +, + +c +o +m +m +e + +n +o +u +s + +s +a +v +o +n +s + +r +e +m +a +r +q +u +é + +à + +l +a + +s +e +c +t +i +o +n + +d +e +s + +f +o +u +r +n +e +a +u +x + +p +h +i +l +o +s +o +p +h +i +q +u +e +s +. + +P +e +u + +d +' +a +u +¬ + +t +e +u +r +s + +e +n + +o +n +t + +m +i +s + +q +u +a +t +r +e +. + +I +I + +n + +y + +a + +e +u + +q +u +e + +q +u +e +l +q +u +e +s + +m +a +u +v +a +i +s + +a +r +t +i +s +t +e +s + +o +u + +f +o +u +r +n +a +l +i +s +t +e +s +, + +q +u +i + +e +n + +o +n +t + +i +n +t +r +o +d +u +i +t + +c +e + +n +o +m +b +r +e + +d +e + +t +e +m +s + +e +n + +t +e +m +s +. + + +S +i + +l +e +s + +r +e +g +i +s +t +r +e +s + +f +o +n +t + +a +u + +n +o +m +b +r +e + +d +e + +q +u +a +t +r +e +, + +S +t + +t +o +u +t + +a +u +t +o +u +r + +d +u + +d +ô +m +e + +d +u + +f +o +u +r +n +e +a +u + +s +e +r +¬ + +v +a +n +t + +à + +l +a + +d +i +s +t +i +l +l +a +t +i +o +n + +d +u + +v +i +n +a +i +g +r +e +, + +d +e + +l +a + +m +a +n +n +e +, + +d +u + +m +i +e +l +, + +& +c +. + +f +i +g +. + +7 +4 +. +, + +c + +e +s +t + +q +u + +o +n + +n +e + +p +e +u +t + +p +a +s + +l +e +s + +p +l +a +c +e +r + +a +i +l +l +e +u +r +s +, + +q +u + +o +n + +l +e +s + +l +a +i +s +i +e + +o +u +v +e +r +t +s + +c +o +n +t +i +n +u +e +l +l +e +m +e +n +t +, + +& + +q +u + +i +l +n +e + +f +a +u +t + +q +u + +u +n +e + +c +h +a +l +e +u +r + +d +o +u +c +e + +p +o +u +r + +c +e +s + +f +o +r +t +e +s + +d + +o +p +é +r +a +t +i +o +n +s +. + + +Q +u +o +i +q +u + +i +l + +s +o +i +t + +v +r +a +i + +q +u + +o +n + +a +u +g +m +e +n +t +e + +l +e + +f +e +u + +e +n + +o +u +v +r +a +n +t + +l +e +s + +r +e +g +i +s +t +r +e +s +, + +c +e +l +a + +n + +a + +p +o +u +r +¬ + +t +a +n +t + +l +i +e +u + +q +u + +à + +l + +é +g +a +r +d + +d +e + +c +e +u +x + +q +u +i + +n +e + +f +o +n +t + +p +a +s + +t +r +o +p + +g +r +a +n +d +s + +; + +c +a +r + +p +l +u +s + +o +n + +e +n + +o +u +v +r +i +r +o +i +t +, + +& + +p +l +u +s + +o +n + +d +e +v +r +o +i +t + +a +u +g +m +e +n +t +e +r + +l +e + +f +e +u +, + +a +u + +l +i +e +u + +q +u + +o +n + +l +e + +d +i +m +i +n +u +e + +r +é +e +l +l +e +m +e +n +t + +s +i + +o +n + +e +n + +o +u +v +r +e + +t +r +o +p + +o +u + +s + +i +l +s + +f +o +n +t + +t +r +o +p + +g +r +a +n +d +s + +: + +a +i +n +s +i + +i +l + +n + +e +s +t + +q +u +e +s +t +i +o +n + +d +a +n +s + +c +e +t + +a +x +i +o +m +e + +, + +q +u +e + +d +e +s + +r +e +g +i +s +t +r +e +s + +q +u +i + +f +o +n +t + +e +n + +p +r +o +p +o +r +t +i +o +n + +a +v +e +c + +l +e + +r +e +s +t +e +. + + +L +e +s + +r +e +g +i +s +t +r +e +s + +d +o +i +v +e +n +t + +ê +t +r +e + +a +u + +p +l +u +s + +u +n + +t +i +e +r +s + +o +u + +u +n + +q +u +a +r +t + +d +u + +d +i +a +m +è +t +r +e + +d +u + +c +e +n +d +r +i +e +r +, + +d +o +n +t + +j +e + +c +r +o +i +s + +q +u + +o +n + +p +e +u +t + +r +é +g +l +e +r + +l +a + +p +o +r +t +e + +f +u +r + +l +e + +d +i +a +m +è +t +r +e + +d +u + +f +o +u +r +n +e +a +u +. + +C +e +l +u +i + +d +e + +G +l +a +u +b +e +r +, + + + + + + +F +O +U + +2 +j +i + + +p +a +r + +e +x +. + +a + +u +n + +p +i +e +d + +d +e + +d +i +a +m +è +t +r +e + +: + +a +i +n +s +i + +é +g +a +l +e + +d +i +m +e +n +s +i +o +n + +s +u +f +f +i +r +a + +p +o +u +r +T +o +n + +s +o +u +p +i +r +a +i +l + +; + +& + +l +e + +t +i +e +r +s + +o +u + +l +e + +q +u +a +r +t +, + +c +o +m +m +e + +o +n + +a + +d +i +t +, + +p +o +u +r + +l +e + +t +u +y +a +u +. + +Q +u +a +n +t + +a +u + +s +o +u +p +i +r +a +i +l +, + +j +e + +p +e +n +s +e + +q +u + +i +l + +s +u +f +f +i +t + +q +u + +i +l + +f +o +u +r +n +i +s +s +e + +a +u + +f +o +y +e +r +; + +m +a +i +s + +l +e + +f +o +y +e +r + +n + +a + +q +u +e + +c +e +t +t +e + +l +a +r +g +e +u +r +, + +& + +e +l +l +e + +e +s +t + +m +ê +m +e + +d +i +m +i +n +u +é +e + +p +a +r + +l +a + +g +r +i +l +l +e + +S +t + +l +e +s + +c +h +a +r +b +o +n +s +: + +c +e + +f +e +r +a + +d +o +n +c + +a +s +s +e +z + +p +o +u +r + +l +e + +s +o +u +p +i +r +a +i +l +, + +c +e + +s +e +r +a + +m +ê +m +e + +t +r +o +p + +; + +m +a +i +s + +d +a +n +s + +l +e + +c +a +s + +o +ù + +l + +o +n + +n +e + +p +e +u +t + +a +p +p +r +é +c +i +e +r + +a +u + +j +u +s +t +e + +l +a + +q +u +a +n +t +i +t +é + +c +o +n +v +e +¬ + +n +a +b +l +e + +, + +i +l + +v +a +u +t + +m +i +e +u +x + +p +é +c +h +e +r + +p +a +r + +c +e +t + +e +x +c +è +s + +q +u +e + +p +a +r + +l +e + +c +o +n +t +r +a +i +r +e + +; + +S +t + +j +e + +c +r +o +i +s + +q +u + +o +n + +d +o +i +t + +s + +e +n + +t +e +n +i +r + +à + +c +e +t +t +e + +d +i +m +e +n +s +i +o +n + +: + +u +n +e + +p +l +u +s + +g +r +a +n +d +e + +n +e + +f +e +r +o +i +t + +p +a +s + +f +o +n +d +é +e + +e +n + +r +a +i +s +o +n +, + +c +o +m +m +e + +o +n + +v +o +i +t + +a +u + +f +o +u +r +n +e +a +u + +d +e + +B +o +e +r +h +a +a +v +e + +; + +e +l +l +e + +e +s +t + +m +ê +m +e + +n +u +i +s +i +b +l +e +, + +c +o +m +m +e + +i +l + +e +s +t + +a +i +s +é + +d +e + +l +e + +p +e +n +s +e +r + +, + +& + +c +o +m +m +e + +n +o +u +s + +l +e + +d +i +r +o +n +s + +e +n + +p +a +r +l +a +n +t + +d +e +s + +a +t +h +a +n +o +r +s +. + +M +a +i +s + +i +l + +n + +e +n + +e +s +t + +p +a +s + +d +e + +m +ê +m +e + +d +u + +t +u +y +a +u + +o +u + +c +h +e +m +i +n +é +e + +; + +i +l + +n +e + +d +o +i +t + +p +a +s + +a +v +o +i +r + +l +e + +m +ê +m +e + +d +i +a +m +è +t +r +e + +q +u +e + +l +e + +f +o +u +r +¬ + +n +e +a +u +. + +C +e +c +i + +a +u + +r +e +s +t +e + +e +s +t + +u +n +e + +a +f +f +a +i +r +e + +d + +e +x +p +é +- + +r +i +e +n +c +e +, + +f +u +r + +l +a +q +u +e +l +l +e + +o +n + +n + +a + +p +a +s + +e +n +c +o +r +e + +f +a +i +t + +b +e +a +u +c +o +u +p + +d + +o +b +f +e +r +v +a +t +i +o +n +s +. + +O +n + +p +e +u +t + +n +é +a +n +¬ + +m +o +i +n +s + +a +s +s +u +r +e +r + +q +u + +e +n + +f +a +i +s +a +n +t + +u +n + +f +o +u +r +n +e +a +u + +d +e + +m +a +n +i +é +r +é + +q +u + +i +l + +a +i +l +l +e + +t +o +u +j +o +u +r +s + +e +n + +r +é +t +r +é +¬ + +c +i +s +s +a +n +t + +, + +i +l + +a +d +m +e +t +t +r +a + +p +l +u +s + +d + +a +i +r + +q +u + +i +l + +n +e + +l +u +i + +e +n + +f +a +u +t +. + + +A +u + +r +e +s +t +e +, + +s +i + +l + +o +n + +p +e +n +s +e + +q +u + +u +n + +s +o +u +p +i +r +a +i +l + +d +e + +m +ê +m +e + +d +i +a +m +è +t +r +e + +q +u +e + +l +e + +f +o +u +r +n +e +a +u + +n +e + +s +u +f +f +i +s +e + +p +a +s +, + +i +l +f +a +u +d +r +o +i +t + +> + +n +o +n + +l +' +é +l +e +v +e +r + +n +i + +f +a +i +r +e + +p +l +u +s +i +e +u +r +s + +p +o +r +t +e +s + +t +o +u +t + +a +u +t +o +u +r + +d +u + +f +o +l + +d +u + +c +e +n +¬ + +d +r +i +e +r + +, + +c +e +l +a + +f +e +r +o +i +t + +i +n +u +t +i +l +e +, + +m +a +i +s + +a +g +r +a +n +d +i +r + +l +e + +d +i +a +m +è +t +r +e + +d +u + +c +e +n +d +r +i +e +r + +l +u +i +- +m +é +m +e + +: + +& + +p +a +r + +c +e + +m +o +y +e +n + +o +n + +a +u +r +o +i +t + +u +n +e + +p +o +r +t +e + +p +l +u +s + +l +a +r +g +e + +; + +c +a +r + +i +l + +e +s +t + +a +u +s +s +i + +i +n +u +t +i +l +e + +d +e + +l +a + +f +a +i +r +e + +p +l +u +s + +h +a +u +t +e + +q +u +e + +l +a +r +g +e + +q +u +a +n +d + +e +l +l +e + +e +s +t + +d +e + +l +a + +l +a +r +g +e +u +r + +d +u + +c +e +n +¬ + +d +r +i +e +r +, + +q +u +e + +d + +e +n + +m +e +t +t +r +e + +p +l +u +s +i +e +u +r +s + +t +o +u +t + +a +u +¬ + +t +o +u +r + +, + +d +e + +c +e +t +t +e + +m +ê +m +e + +l +a +r +g +e +u +r +. + +C +e +l +a + +n +e + +p +e +u +t + +a +v +o +i +r + +l +i +e +u + +q +u +e + +q +u +a +n +d + +c +h +a +c +u +n +e + +d + +e +l +l +e +s + +n + +a + +q +u + +u +n +e + +p +a +r +t +i +e + +d +u + +d +i +a +m +è +t +r +e + +d +u + +c +e +n +d +r +i +e +r +, + +S +c + +e +n + +c +e + +c +a +s + +e +l +l +e +s + +n +e + +d +o +i +v +e +n +t + +t +a +i +r +e + +e +n +t +r + +e +l +l +e +s + +q +u +e + +l +a + +s +o +m +m +e + +d +e + +f +a + +l +a +r +g +e +u +r +. + + +D +e +s + +d +e +g +r +é +s + +d +u + +f +e +u +. + +C + +e +s +t + +p +a +r + +l +e + +m +o +y +e +n + +d +e +s + +r +e +g +i +s +t +r +e +s + +S +t + +d +u + +s +o +u +p +i +r +a +i +l +, + +c +o +m +m +e + +n +o +i +i +s + +l + +a +v +o +n +s +d +é +j +à + +d +i +t + +e +n + +p +l +u +s + +d + +u +n + +e +n +d +r +o +i +t +, + +q +u + +o +n + +r +é +g +l +é + +l +e +s + +d +i +f +f +é +r +e +n +s + +d +e +g +r +é +s + +d +u + +f +e +u +. + +V +o +y +e +% + +c +ç + +q +u + +o +n + +e +n + +a + +d +i +t + +à + +Y + +a +r +t +i +c +l +e + +F +e +u +. + + +L +e +s + +c +h +y +m +i +s +t +e +s + +f +e + +f +o +n +t + +u +n + +p +e +u + +p +l +u +s + +d +o +n +n +é + +d +e + +p +e +i +n +e + +p +o +u +r + +r +é +g +l +e +r + +l +e +s + +d +e +g +r +é +s + +d +u + +f +e +u +, + +q +u +e + +p +o +u +r + +l +a + +c +o +n +s +t +r +u +c +t +i +o +n + +d +e +s + +f +o +u +r +n +e +a +u +x + +; + +& + +c +e +p +e +n +d +a +n +t + +l + +u +n + +S +t + +l + +a +u +t +r +e + +d +é +v +o +i +e +n +t + +a +i +l +e +s + +e +n +s +e +m +b +l +e +. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/test-root/corpora/regression/modules/JPEG-hul/19_e190014.jpg b/test-root/corpora/regression/modules/JPEG-hul/19_e190014.jpg new file mode 100644 index 000000000..29a2903eb Binary files /dev/null and b/test-root/corpora/regression/modules/JPEG-hul/19_e190014.jpg differ