From: Mehdi Houshmand Date: Wed, 4 Jul 2012 07:04:03 +0000 (+0000) Subject: Bugzilla#53502: MODCA end structured field now more conformant with the spec by allow... X-Git-Tag: fop-2_0~342 X-Git-Url: https://source.dussan.org/?a=commitdiff_plain;h=8f37765b5c9b6dbe80d860207802aa4f7f8b1a94;p=xmlgraphics-fop.git Bugzilla#53502: MODCA end structured field now more conformant with the spec by allowing 0xFFFF match (match any). Submitted by Robert Meyer git-svn-id: https://svn.apache.org/repos/asf/xmlgraphics/fop/trunk@1357110 13f79535-47bb-0310-9956-ffa450edef68 --- diff --git a/src/java/org/apache/fop/afp/util/AFPResourceUtil.java b/src/java/org/apache/fop/afp/util/AFPResourceUtil.java index 979376b3e..98d2a8f8a 100644 --- a/src/java/org/apache/fop/afp/util/AFPResourceUtil.java +++ b/src/java/org/apache/fop/afp/util/AFPResourceUtil.java @@ -56,8 +56,11 @@ import org.apache.fop.afp.parser.UnparsedStructuredField; */ public final class AFPResourceUtil { - private static final byte TYPE_CODE_BEGIN = (byte)(0xA8 & 0xFF); - private static final byte TYPE_CODE_END = (byte)(0xA9 & 0xFF); + private static final byte TYPE_CODE_BEGIN = (byte) (0xA8 & 0xFF); + + private static final byte TYPE_CODE_END = (byte) (0xA9 & 0xFF); + + private static final byte END_FIELD_ANY_NAME = (byte) (0xFF & 0xFF); private static final Log LOG = LogFactory.getLog(AFPResourceUtil.class); @@ -92,10 +95,13 @@ public final class AFPResourceUtil { throws UnsupportedEncodingException { //The first 8 bytes of the field data represent the resource name byte[] nameBytes = new byte[8]; - System.arraycopy(field.getData(), 0, nameBytes, 0, 8); - String asciiName; - asciiName = new String(nameBytes, AFPConstants.EBCIDIC_ENCODING); - return asciiName; + + byte[] fieldData = field.getData(); + if (fieldData.length < 8) { + throw new IllegalArgumentException("Field data does not contain a resource name"); + } + System.arraycopy(fieldData, 0, nameBytes, 0, 8); + return new String(nameBytes, AFPConstants.EBCIDIC_ENCODING); } /** @@ -128,12 +134,13 @@ public final class AFPResourceUtil { public static void copyNamedResource(String name, final InputStream in, final OutputStream out) throws IOException { final MODCAParser parser = new MODCAParser(in); - Collection resourceNames = new java.util.HashSet(); + Collection resourceNames = new java.util.HashSet(); //Find matching "Begin" field final UnparsedStructuredField fieldBegin; while (true) { - UnparsedStructuredField field = parser.readNextStructuredField(); + final UnparsedStructuredField field = parser.readNextStructuredField(); + if (field == null) { throw new IOException("Requested resource '" + name + "' not found. Encountered resource names: " + resourceNames); @@ -142,8 +149,10 @@ public final class AFPResourceUtil { if (field.getSfTypeCode() != TYPE_CODE_BEGIN) { //0xA8=Begin continue; //Not a "Begin" field } - String resourceName = getResourceName(field); + final String resourceName = getResourceName(field); + resourceNames.add(resourceName); + if (resourceName.equals(name)) { if (LOG.isDebugEnabled()) { LOG.debug("Start of requested structured field found:\n" @@ -170,45 +179,65 @@ public final class AFPResourceUtil { if (wrapInResource) { ResourceObject resourceObject = new ResourceObject(name) { protected void writeContent(OutputStream os) throws IOException { - copyStructuredFields(name, fieldBegin, parser, out); + copyNamedStructuredFields(name, fieldBegin, parser, out); } }; resourceObject.setType(ResourceObject.TYPE_PAGE_SEGMENT); resourceObject.writeToStream(out); } else { - copyStructuredFields(name, fieldBegin, parser, out); + copyNamedStructuredFields(name, fieldBegin, parser, out); } } - private static void copyStructuredFields(String name, UnparsedStructuredField fieldBegin, + private static void copyNamedStructuredFields(final String name, UnparsedStructuredField fieldBegin, MODCAParser parser, OutputStream out) throws IOException { - boolean inRequestedResource; - - //The "Begin" field first - out.write(MODCAParser.CARRIAGE_CONTROL_CHAR); - fieldBegin.writeTo(out); - UnparsedStructuredField field; - - //Then the rest of the fields until the corresponding "End" field - inRequestedResource = true; - do { - field = parser.readNextStructuredField(); + UnparsedStructuredField field = fieldBegin; + while (true) { if (field == null) { - break; //Unexpected EOF - } - - if (field.getSfTypeCode() == TYPE_CODE_END) { - String resourceName = getResourceName(field); - if (resourceName.equals(name)) { - inRequestedResource = false; //Signal end of loop - } + throw new IOException("Ending structured field not found for resource " + name); } out.write(MODCAParser.CARRIAGE_CONTROL_CHAR); field.writeTo(out); - } while (inRequestedResource); - if (inRequestedResource) { - throw new IOException("Ending structured field not found for resource " + name); + + if (isEndOfStructuredField(field, fieldBegin, name)) { + break; + } + field = parser.readNextStructuredField(); } } + private static boolean isEndOfStructuredField(UnparsedStructuredField field, + UnparsedStructuredField fieldBegin, String name) throws UnsupportedEncodingException { + return fieldMatchesEndTagType(field) + && fieldMatchesBeginCategoryCode(field, fieldBegin) + && fieldHasValidName(field, name); + } + + private static boolean fieldMatchesEndTagType(UnparsedStructuredField field) { + return field.getSfTypeCode() == TYPE_CODE_END; + } + + private static boolean fieldMatchesBeginCategoryCode(UnparsedStructuredField field, + UnparsedStructuredField fieldBegin) { + return fieldBegin.getSfCategoryCode() == field.getSfCategoryCode(); + } + + /** + * The AFP specification states that it is valid for the end structured field to have: + * - No tag name specified, which will cause it to match any existing tag type match. + * - The name has FFFF as its first two bytes + * - The given name matches the previous structured field name + */ + private static boolean fieldHasValidName(UnparsedStructuredField field, String name) + throws UnsupportedEncodingException { + if (field.getData().length > 0) { + if (field.getData()[0] == field.getData()[1] + && field.getData()[0] == END_FIELD_ANY_NAME) { + return true; + } else { + return name.equals(getResourceName(field)); + } + } + return true; + } } diff --git a/test/java/org/apache/fop/afp/AFPResourceUtilTestCase.java b/test/java/org/apache/fop/afp/AFPResourceUtilTestCase.java index 6ab7f475d..a7cf57ebe 100644 --- a/test/java/org/apache/fop/afp/AFPResourceUtilTestCase.java +++ b/test/java/org/apache/fop/afp/AFPResourceUtilTestCase.java @@ -22,23 +22,34 @@ package org.apache.fop.afp; import static org.junit.Assert.assertTrue; import java.io.ByteArrayOutputStream; +import java.io.IOException; import java.io.InputStream; +import java.io.OutputStream; import java.util.Arrays; +import org.junit.Test; + import org.apache.commons.io.IOUtils; import org.apache.fop.afp.util.AFPResourceUtil; import org.junit.Test; +import static org.junit.Assert.assertTrue; + /** * Tests the {@link AFPResourceUtil} class. */ public class AFPResourceUtilTestCase { private static final String RESOURCE_FILENAME = "expected_resource.afp"; - private static final String NAMED_RESOURCE_FILENAME = "expected_named_resource.afp"; - private static final String PSEG = "XFEATHER"; + private static final String RESOURCE_ANY_NAME = "resource_any_name.afp"; + private static final String RESOURCE_NAME_MATCH = "resource_name_match.afp"; + private static final String RESOURCE_NAME_MISMATCH = "resource_name_mismatch.afp"; + private static final String RESOURCE_NO_END_NAME = "resource_no_end_name.afp"; + + private static final String PSEG_A = "XFEATHER"; + private static final String PSEG_B = "S1CODEQR"; /** * Tests copyResourceFile() @@ -46,57 +57,105 @@ public class AFPResourceUtilTestCase { */ @Test public void testCopyResourceFile() throws Exception { + compareResources(new ResourceCopier() { + public void copy(InputStream in, OutputStream out) throws IOException { + AFPResourceUtil.copyResourceFile(in, out); + } + }, RESOURCE_FILENAME, RESOURCE_FILENAME); + } - final ByteArrayOutputStream baos = new ByteArrayOutputStream(); + /** + * Tests copyNamedResource() + * @throws Exception - + */ + @Test + public void testCopyNamedResource() throws Exception { + compareResources(new ResourceCopier() { + public void copy(InputStream in, OutputStream out) throws IOException { + AFPResourceUtil.copyNamedResource(PSEG_A, in, out); + } + }, RESOURCE_FILENAME, NAMED_RESOURCE_FILENAME); + } - InputStream in = null; + private void compareResources(ResourceCopier copyResource, String resourceA, String resourceB) + throws IOException { + ByteArrayOutputStream baos = copyResource(resourceA, copyResource); + byte[] expectedBytes = resourceAsByteArray(resourceB); + assertTrue(Arrays.equals(expectedBytes, baos.toByteArray())); + } + private ByteArrayOutputStream copyResource(String resource, ResourceCopier resourceCopier) + throws IOException { + final ByteArrayOutputStream baos = new ByteArrayOutputStream(); + InputStream in = null; try { - in = getClass().getResourceAsStream(RESOURCE_FILENAME); - AFPResourceUtil.copyResourceFile(in, baos); + in = getClass().getResourceAsStream(resource); + resourceCopier.copy(in, baos); } finally { in.close(); } + return baos; + } + private byte[] resourceAsByteArray(String resource) throws IOException { + InputStream in = null; byte[] expectedBytes = null; - try { - in = getClass().getResourceAsStream(RESOURCE_FILENAME); + in = getClass().getResourceAsStream(resource); expectedBytes = IOUtils.toByteArray(in); } finally { in.close(); } - - assertTrue(Arrays.equals(expectedBytes, baos.toByteArray())); - + return expectedBytes; } /** - * Tests copyNamedResource() + * Tests the validity of a closing structured field having an FF FF name which + * allows it to match any existing matching starting field * @throws Exception - */ @Test - public void testCopyNamedResource() throws Exception { - final ByteArrayOutputStream baos = new ByteArrayOutputStream(); + public void testResourceAnyName() throws Exception { + testResource(RESOURCE_ANY_NAME, PSEG_B); + } - InputStream in = null; + /** + * Tests a matching end structured field name + * @throws Exception - + */ + @Test + public void testResourceNameMatch() throws Exception { + testResource(RESOURCE_NAME_MATCH, PSEG_B); + } - try { - in = getClass().getResourceAsStream(RESOURCE_FILENAME); - AFPResourceUtil.copyNamedResource(PSEG, in, baos); - } finally { - in.close(); - } + /** + * Tests to see whether a matching structured field pair with mismatching + * names fails. + * @throws Exception - + */ + @Test(expected=Exception.class) + public void testResourceNameMismatch() throws Exception { + testResource(RESOURCE_NAME_MISMATCH, PSEG_B); + } - byte[] expectedBytes = null; + /** + * Tests a matching structured end field with no name + * @throws Exception - + */ + @Test + public void testResourceNoEndName() throws Exception { + testResource(RESOURCE_NO_END_NAME, PSEG_B); + } - try { - in = getClass().getResourceAsStream(NAMED_RESOURCE_FILENAME); - expectedBytes = IOUtils.toByteArray(in); - } finally { - in.close(); - } + private void testResource(String resource, final String pseg) throws Exception { + copyResource(resource, new ResourceCopier() { + public void copy(InputStream in, OutputStream out) throws IOException { + AFPResourceUtil.copyNamedResource(pseg, in, out); + } + }); + } - assertTrue(Arrays.equals(expectedBytes, baos.toByteArray())); + private interface ResourceCopier { + public void copy(InputStream in, OutputStream out) throws IOException; } } diff --git a/test/java/org/apache/fop/afp/resource_any_name.afp b/test/java/org/apache/fop/afp/resource_any_name.afp new file mode 100644 index 000000000..a91cabfae Binary files /dev/null and b/test/java/org/apache/fop/afp/resource_any_name.afp differ diff --git a/test/java/org/apache/fop/afp/resource_name_match.afp b/test/java/org/apache/fop/afp/resource_name_match.afp new file mode 100644 index 000000000..53999574e Binary files /dev/null and b/test/java/org/apache/fop/afp/resource_name_match.afp differ diff --git a/test/java/org/apache/fop/afp/resource_name_mismatch.afp b/test/java/org/apache/fop/afp/resource_name_mismatch.afp new file mode 100644 index 000000000..d4f2c7f81 Binary files /dev/null and b/test/java/org/apache/fop/afp/resource_name_mismatch.afp differ diff --git a/test/java/org/apache/fop/afp/resource_no_end_name.afp b/test/java/org/apache/fop/afp/resource_no_end_name.afp new file mode 100644 index 000000000..517e4cb15 Binary files /dev/null and b/test/java/org/apache/fop/afp/resource_no_end_name.afp differ