]> source.dussan.org Git - xmlgraphics-fop.git/commitdiff
Bugzilla#53502: MODCA end structured field now more conformant with the spec by allow...
authorMehdi Houshmand <mehdi@apache.org>
Wed, 4 Jul 2012 07:04:03 +0000 (07:04 +0000)
committerMehdi Houshmand <mehdi@apache.org>
Wed, 4 Jul 2012 07:04:03 +0000 (07:04 +0000)
git-svn-id: https://svn.apache.org/repos/asf/xmlgraphics/fop/trunk@1357110 13f79535-47bb-0310-9956-ffa450edef68

src/java/org/apache/fop/afp/util/AFPResourceUtil.java
test/java/org/apache/fop/afp/AFPResourceUtilTestCase.java
test/java/org/apache/fop/afp/resource_any_name.afp [new file with mode: 0644]
test/java/org/apache/fop/afp/resource_name_match.afp [new file with mode: 0644]
test/java/org/apache/fop/afp/resource_name_mismatch.afp [new file with mode: 0644]
test/java/org/apache/fop/afp/resource_no_end_name.afp [new file with mode: 0644]

index 979376b3e4c0fa6e12affabe34911cc1d630ebeb..98d2a8f8a0ad1c3eb80e6ac53ee0ce6c33a66ff3 100644 (file)
@@ -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<String> resourceNames = new java.util.HashSet<String>();
 
         //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;
+    }
 }
index 6ab7f475dd1faf8907dec24926b4376a1d446a2b..a7cf57ebe0f7c43cfd6cb3cf7b235c086e3d09ec 100644 (file)
@@ -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 (file)
index 0000000..a91cabf
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 (file)
index 0000000..5399957
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 (file)
index 0000000..d4f2c7f
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 (file)
index 0000000..517e4cb
Binary files /dev/null and b/test/java/org/apache/fop/afp/resource_no_end_name.afp differ