Author: phancock
Date: Tue May 17 11:07:06 2011
New Revision: 1104135

URL: http://svn.apache.org/viewvc?rev=1104135&view=rev
Log:
Fixed io exception in MODCAParser caused by the improper use of mark() and 
reset()
on the MODCA data input stream.  See bugzilla 50909.

Added:
    xmlgraphics/fop/trunk/test/java/org/apache/fop/afp/
    xmlgraphics/fop/trunk/test/java/org/apache/fop/afp/parser/
    
xmlgraphics/fop/trunk/test/java/org/apache/fop/afp/parser/MODCAParserTestCase.java
Modified:
    
xmlgraphics/fop/trunk/src/java/org/apache/fop/afp/apps/FontPatternExtractor.java
    xmlgraphics/fop/trunk/src/java/org/apache/fop/afp/parser/MODCAParser.java
    
xmlgraphics/fop/trunk/src/java/org/apache/fop/afp/parser/UnparsedStructuredField.java
    xmlgraphics/fop/trunk/src/java/org/apache/fop/afp/util/AFPResourceUtil.java
    xmlgraphics/fop/trunk/status.xml
    xmlgraphics/fop/trunk/test/java/org/apache/fop/StandardTestSuite.java

Modified: 
xmlgraphics/fop/trunk/src/java/org/apache/fop/afp/apps/FontPatternExtractor.java
URL: 
http://svn.apache.org/viewvc/xmlgraphics/fop/trunk/src/java/org/apache/fop/afp/apps/FontPatternExtractor.java?rev=1104135&r1=1104134&r2=1104135&view=diff
==============================================================================
--- 
xmlgraphics/fop/trunk/src/java/org/apache/fop/afp/apps/FontPatternExtractor.java
 (original)
+++ 
xmlgraphics/fop/trunk/src/java/org/apache/fop/afp/apps/FontPatternExtractor.java
 Tue May 17 11:07:06 2011
@@ -55,9 +55,10 @@ public class FontPatternExtractor {
             UnparsedStructuredField strucField;
             while ((strucField = parser.readNextStructuredField()) != null) {
                 if (strucField.getSfTypeID() == 0xD3EE89) {
+                    byte[] sfData = strucField.getData();
                     println(strucField.toString());
-                    HexDump.dump(strucField.getData(), 0, printStream, 0);
-                    baout.write(strucField.getData());
+                    HexDump.dump(sfData, 0, printStream, 0);
+                    baout.write(sfData);
                 }
             }
 

Modified: 
xmlgraphics/fop/trunk/src/java/org/apache/fop/afp/parser/MODCAParser.java
URL: 
http://svn.apache.org/viewvc/xmlgraphics/fop/trunk/src/java/org/apache/fop/afp/parser/MODCAParser.java?rev=1104135&r1=1104134&r2=1104135&view=diff
==============================================================================
--- xmlgraphics/fop/trunk/src/java/org/apache/fop/afp/parser/MODCAParser.java 
(original)
+++ xmlgraphics/fop/trunk/src/java/org/apache/fop/afp/parser/MODCAParser.java 
Tue May 17 11:07:06 2011
@@ -20,15 +20,23 @@
 package org.apache.fop.afp.parser;
 
 import java.io.DataInputStream;
-import java.io.EOFException;
 import java.io.IOException;
 import java.io.InputStream;
 
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import org.apache.fop.afp.parser.UnparsedStructuredField.Introducer;
+
 /**
  * An simple MO:DCA/AFP parser.
  */
 public class MODCAParser {
 
+    private static final Log LOG = LogFactory.getLog(MODCAParser.class);
+
+    private static final int INTRODUCER_LENGTH = 8;
+
     /** The carriage control character (0x5A) used to indicate the start of a 
structured field. */
     public static final byte CARRIAGE_CONTROL_CHAR = (byte)(0x5A & 0xFF);
 
@@ -39,19 +47,9 @@ public class MODCAParser {
      * @param in the {@link InputStream} to read the AFP file from.
      */
     public MODCAParser(InputStream in) {
-        if (!in.markSupported()) {
-            in = new java.io.BufferedInputStream(in);
-        }
         this.din = new DataInputStream(in);
     }
 
-    /**
-     * Returns the {@link DataInputStream} used for parsing structured fields.
-     * @return the data input stream
-     */
-    public DataInputStream getDataInputStream() {
-        return this.din;
-    }
 
     /**
      * Reads the next structured field from the input stream.
@@ -61,20 +59,46 @@ public class MODCAParser {
      * @throws IOException if an I/O error occurs
      */
     public UnparsedStructuredField readNextStructuredField() throws 
IOException {
-        try {
-            while (true) {
-                byte b = din.readByte(); //Skip 0x5A character if necessary 
(ex. AFP)
-                if (b == 0x0D || b == 0x0A) {
-                    //CR and LF may be used as field delimiters
-                    continue;
-                } else if (b == CARRIAGE_CONTROL_CHAR) {
-                    break; //Signals the start of a new structured field
-                }
+
+        //Find the SF delimiter
+        do {
+            //Exhausted streams and so no next SF
+            //  - null return represents this case
+            //  TODO should this happen?
+            if (din.available() == 0) {
+                return null;
+            }
+        } while (din.readByte() != CARRIAGE_CONTROL_CHAR);
+
+        //Read introducer as byte array to preserve any data not parsed below
+        byte[] introducerData = new byte[INTRODUCER_LENGTH]; //Length of 
introducer
+        din.readFully(introducerData);
+
+        Introducer introducer = new Introducer(introducerData);
+
+        int dataLength = introducer.getLength() - INTRODUCER_LENGTH;
+
+        //Handle optional extension
+        byte[] extData = null;
+        if (introducer.isExtensionPresent()) {
+            short extLength = 0;
+            extLength = (short)((din.readByte()) & 0xFF);
+            if (extLength > 0) {
+                extData = new byte[extLength - 1];
+                din.readFully(extData);
+                dataLength -= extLength;
             }
-        } catch (EOFException eof) {
-            return null;
         }
-        return 
UnparsedStructuredField.readStructuredField(getDataInputStream());
-    }
+        //Read payload
+        byte[] data = new byte[dataLength];
+        din.readFully(data);
 
+        UnparsedStructuredField sf = new UnparsedStructuredField(introducer, 
data, extData);
+
+        if (LOG.isTraceEnabled()) {
+            LOG.trace(sf);
+        }
+
+        return sf;
+    }
 }

Modified: 
xmlgraphics/fop/trunk/src/java/org/apache/fop/afp/parser/UnparsedStructuredField.java
URL: 
http://svn.apache.org/viewvc/xmlgraphics/fop/trunk/src/java/org/apache/fop/afp/parser/UnparsedStructuredField.java?rev=1104135&r1=1104134&r2=1104135&view=diff
==============================================================================
--- 
xmlgraphics/fop/trunk/src/java/org/apache/fop/afp/parser/UnparsedStructuredField.java
 (original)
+++ 
xmlgraphics/fop/trunk/src/java/org/apache/fop/afp/parser/UnparsedStructuredField.java
 Tue May 17 11:07:06 2011
@@ -19,337 +19,331 @@
 
 package org.apache.fop.afp.parser;
 
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
 import java.io.DataInputStream;
-import java.io.EOFException;
 import java.io.IOException;
 import java.io.OutputStream;
-import java.io.PrintStream;
 import java.text.DecimalFormat;
 
-import org.apache.commons.io.HexDump;
-import org.apache.commons.logging.Log;
-import org.apache.commons.logging.LogFactory;
-
 /**
- * Represents an unparsed (generic) AFP structured field.
- */
-public class UnparsedStructuredField {
-
-    private static final Log LOG = 
LogFactory.getLog(UnparsedStructuredField.class);
-
-    private static final int INTRODUCER_LENGTH = 8;
-
-    private short sfLength;
-    private byte sfClassCode;
-    private byte sfTypeCode;
-    private byte sfCategoryCode;
-    private boolean sfiExtensionPresent;
-    private boolean sfiSegmentedData;
-    private boolean sfiPaddingPresent;
-    private short extLength;
-    private byte[] introducerData;
-    private byte[] extData;
-    private byte[] data;
-
-    /**
-     * Default constructor.
-     */
-    public UnparsedStructuredField() {
-        //nop
-    }
-
-    /**
-     * Reads a structured field from a {@link DataInputStream}. The resulting 
object can be
-     * further interpreted be follow-up code.
-     * @param din the stream to read from
-     * @return the generic structured field
-     * @throws IOException if an I/O error occurs
-     */
-    public static UnparsedStructuredField readStructuredField(DataInputStream 
din)
-            throws IOException {
-        UnparsedStructuredField sf = new UnparsedStructuredField();
-
-        //Read introducer as byte array to preserve any data not parsed below
-        din.mark(INTRODUCER_LENGTH);
-        sf.introducerData = new byte[INTRODUCER_LENGTH]; //Length of introducer
-        din.readFully(sf.introducerData);
-        din.reset();
-
-        //Parse the introducer
-        short len;
-        try {
-            len = din.readShort();
-        } catch (EOFException eof) {
-            return null;
-        }
-        sf.sfLength = len;
-        sf.sfClassCode = din.readByte();
-        sf.sfTypeCode = din.readByte();
-        sf.sfCategoryCode = din.readByte();
-
-        //Flags
-        byte f = din.readByte();
-        sf.sfiExtensionPresent = (f & 0x01) != 0;
-        sf.sfiSegmentedData = (f & 0x04) != 0;
-        sf.sfiPaddingPresent = (f & 0x10) != 0;
-        din.skip(2); //Reserved
-
-        int dataLength = sf.sfLength - INTRODUCER_LENGTH;
-
-        //Handle optional extension
-        if (sf.sfiExtensionPresent) {
-            sf.extLength = (short)(((short)din.readByte()) & 0xFF);
-            if (sf.extLength > 0) {
-                sf.extData = new byte[sf.extLength - 1];
-                din.readFully(sf.extData);
-                dataLength -= sf.extLength;
-            }
-        }
-
-        //Read payload
-        sf.data = new byte[dataLength];
-        din.readFully(sf.data);
-
-        if (LOG.isTraceEnabled()) {
-            LOG.trace(sf);
-        }
-
-        return sf;
-    }
-
-    /** {@inheritDoc} */
-    public String toString() {
-        StringBuffer sb = new StringBuffer("Structured Field: ");
-        sb.append(Integer.toHexString(getSfTypeID()).toUpperCase());
-        sb.append(", len=");
-        sb.append(new DecimalFormat("00000").format(getSfLength()));
-        sb.append(" ").append(getTypeCodeAsString());
-        sb.append(" ").append(getCategoryCodeAsString());
-        if (isSfiExtensionPresent()) {
-            sb.append(", SFI extension present");
-        }
-        if (isSfiSegmentedData()) {
-            sb.append(", segmented data");
-        }
-        if (isSfiPaddingPresent()) {
-            sb.append(", with padding");
-        }
-        return sb.toString();
-    }
-
-    /**
-     * Dump the structured field as hex data to the given {@link PrintStream}.
-     * @param out the {@link PrintStream} to dump to
-     * @throws IOException if an I/O error occurs
-     */
-    public void dump(PrintStream out) throws IOException {
-        out.println(toString());
-        HexDump.dump(getData(), 0, out, 0);
-    }
-
-    /**
-     * Dump the structured field as hex data to <code>System.out</code>.
-     * @throws IOException if an I/O error occurs
-     */
-    public void dump() throws IOException {
-        dump(System.out);
-    }
-
-    /**
-     * Returns type code function name for this field.
-     * @return the type code function name
-     */
-    public String getTypeCodeAsString() {
-        switch ((int)getSfTypeCode() & 0xFF) {
-        case 0xA0: return "Attribute";
-        case 0xA2: return "CopyCount";
-        case 0xA6: return "Descriptor";
-        case 0xA7: return "Control";
-        case 0xA8: return "Begin";
-        case 0xA9: return "End";
-        case 0xAB: return "Map";
-        case 0xAC: return "Position";
-        case 0xAD: return "Process";
-        case 0xAF: return "Include";
-        case 0xB0: return "Table";
-        case 0xB1: return "Migration";
-        case 0xB2: return "Variable";
-        case 0xB4: return "Link";
-        case 0xEE: return "Data";
-        default: return "Unknown:" + 
Integer.toHexString((int)getSfTypeCode()).toUpperCase();
-        }
-    }
-
-    /**
-     * Returns category code function name for this field.
-     * @return the category code function name
-     */
-    public String getCategoryCodeAsString() {
-        switch ((int)getSfCategoryCode() & 0xFF) {
-        case 0x5F: return "Page Segment";
-        case 0x6B: return "Object Area";
-        case 0x77: return "Color Attribute Table";
-        case 0x7B: return "IM Image";
-        case 0x88: return "Medium";
-        case 0x89: return "Font";
-        case 0x8A: return "Coded Font";
-        case 0x90: return "Process Element";
-        case 0x92: return "Object Container";
-        case 0x9B: return "Presentation Text";
-        case 0xA7: return "Index";
-        case 0xA8: return "Document";
-        case 0xAD: return "Page Group";
-        case 0xAF: return "Page";
-        case 0xBB: return "Graphics";
-        case 0xC3: return "Data Resource";
-        case 0xC4: return "Document Environment Group (DEG)";
-        case 0xC6: return "Resource Group";
-        case 0xC7: return "Object Environment Group (OEG)";
-        case 0xC9: return "Active Environment Group (AEG)";
-        case 0xCC: return "Medium Map";
-        case 0xCD: return "Form Map";
-        case 0xCE: return "Name Resource";
-        case 0xD8: return "Page Overlay";
-        case 0xD9: return "Resource Environment Group (REG)";
-        case 0xDF: return "Overlay";
-        case 0xEA: return "Data Supression";
-        case 0xEB: return "Bar Code";
-        case 0xEE: return "No Operation";
-        case 0xFB: return "Image";
-        default: return "Unknown:" + 
Integer.toHexString((int)getSfTypeCode()).toUpperCase();
-        }
-    }
-
-    /**
-     * Returns the structured field's length.
-     * @return the field length
-     */
-    public short getSfLength() {
-        return this.sfLength;
-    }
-
-    /**
-     * Returns the structured field's identifier.
-     * @return the field identifier
-     */
-    public int getSfTypeID() {
-        return ((getSfClassCode() & 0xFF) << 16)
-                | ((getSfTypeCode() & 0xFF) << 8)
-                | (getSfCategoryCode() & 0xFF);
-    }
-
-    /**
-     * Returns the structured field's class code.
-     * @return the field class code
-     */
-    public byte getSfClassCode() {
-        return this.sfClassCode;
-    }
-
-    /**
-     * Returns the structured field's type code.
-     * @return the type code
-     */
-    public byte getSfTypeCode() {
-        return this.sfTypeCode;
-    }
-
-    /**
-     * Returns the structured field's category code.
-     * @return the sfCategoryCode
-     */
-    public byte getSfCategoryCode() {
-        return this.sfCategoryCode;
-    }
-
-    /**
-     * Indicates whether an field introducer extension is present.
-     * @return true if an field introducer extension is present
-     */
-    public boolean isSfiExtensionPresent() {
-        return this.sfiExtensionPresent && (this.extData != null);
-    }
-
-    /**
-     * Indicates whether segmented data is present.
-     * @return true if the data is segmented
-     */
-    public boolean isSfiSegmentedData() {
-        return this.sfiSegmentedData;
-    }
-
-    /**
-     * Indicates whether the data is padded.
-     * @return true if the data is padded
-     */
-    public boolean isSfiPaddingPresent() {
-        return this.sfiPaddingPresent;
-    }
-
-    /**
-     * Returns the length of the extension if present.
-     * @return the length of the extension (or 0 if no extension is present)
-     */
-    public short getExtLength() {
-        return this.extLength;
-    }
-
-    /**
-     * Returns the extension data if present.
-     * @return the extension data (or null if no extension is present)
-     */
-    public byte[] getExtData() {
-        return this.extData;
-    }
-
-    /**
-     * Returns the structured field's payload.
-     * @return the field's data
-     */
-    public byte[] getData() {
-        return this.data;
-    }
-
-    /**
-     * Returns the structured field's introducer data.
-     * @return the introducer data
-     */
-    public byte[] getIntroducerData() {
-        return this.introducerData;
-    }
-
-    /**
-     * Returns the complete structured field as a byte array.
-     * @return the complete field data
-     */
-    public byte[] getCompleteFieldAsBytes() {
-        int len = INTRODUCER_LENGTH;
-        if (isSfiExtensionPresent()) {
-            len += getExtLength();
-        }
-        len += getData().length;
-        byte[] bytes = new byte[len];
-        int pos = 0;
-        System.arraycopy(getIntroducerData(), 0, bytes, pos, 
INTRODUCER_LENGTH);
-        pos += INTRODUCER_LENGTH;
-        if (isSfiExtensionPresent()) {
-            System.arraycopy(getExtData(), 0, bytes, pos, getExtLength());
-            pos += getExtLength();
-        }
-        System.arraycopy(getData(), 0, bytes, pos, getData().length);
-        return bytes;
-    }
-
-    /**
-     * Writes this structured field to the given {@link OutputStream}.
-     * @param out the output stream
-     * @throws IOException if an I/O error occurs
-     */
-    public void writeTo(OutputStream out) throws IOException {
-        out.write(this.introducerData);
-        if (isSfiExtensionPresent()) {
-            out.write(this.extData);
-        }
-        out.write(this.data);
-    }
+* Represents an unparsed (generic) AFP structured field.
+*/
+public final class UnparsedStructuredField {
+
+   private final Introducer introducer;
+   private final byte[] extData;
+   private final byte[] data;
+
+   /**
+    *
+    * @param Structured field introducer
+    * @param data Structured field data
+    * @param extData Structured field extension data
+    */
+   UnparsedStructuredField(Introducer introducer,
+           byte[] data, byte[] extData) {
+       this.introducer = introducer;
+       this.data = data;
+       if (extData != null) {
+           this.extData = extData;
+       } else {
+           this.extData = null;
+       }
+   }
+
+   @Override
+   public String toString() {
+       StringBuffer sb = new StringBuffer("Structured Field: ");
+       sb.append(Integer.toHexString(getSfTypeID()).toUpperCase());
+       sb.append(", len=");
+       sb.append(new DecimalFormat("00000").format(getSfLength()));
+       sb.append(" ").append(getTypeCodeAsString());
+       sb.append(" ").append(getCategoryCodeAsString());
+       if (isSfiExtensionPresent()) {
+           sb.append(", SFI extension present");
+       }
+       if (isSfiSegmentedData()) {
+           sb.append(", segmented data");
+       }
+       if (isSfiPaddingPresent()) {
+           sb.append(", with padding");
+       }
+       return sb.toString();
+   }
+
+
+   /**
+    * Returns type code function name for this field.
+    * @return the type code function name
+    */
+   private String getTypeCodeAsString() {
+       switch (getSfTypeCode() & 0xFF) {
+       case 0xA0: return "Attribute";
+       case 0xA2: return "CopyCount";
+       case 0xA6: return "Descriptor";
+       case 0xA7: return "Control";
+       case 0xA8: return "Begin";
+       case 0xA9: return "End";
+       case 0xAB: return "Map";
+       case 0xAC: return "Position";
+       case 0xAD: return "Process";
+       case 0xAF: return "Include";
+       case 0xB0: return "Table";
+       case 0xB1: return "Migration";
+       case 0xB2: return "Variable";
+       case 0xB4: return "Link";
+       case 0xEE: return "Data";
+       default: return "Unknown:" + 
Integer.toHexString(getSfTypeCode()).toUpperCase();
+       }
+   }
+
+   /**
+    * Returns category code function name for this field.
+    * @return the category code function name
+    */
+   private String getCategoryCodeAsString() {
+       switch (getSfCategoryCode() & 0xFF) {
+       case 0x5F: return "Page Segment";
+       case 0x6B: return "Object Area";
+       case 0x77: return "Color Attribute Table";
+       case 0x7B: return "IM Image";
+       case 0x88: return "Medium";
+       case 0x89: return "Font";
+       case 0x8A: return "Coded Font";
+       case 0x90: return "Process Element";
+       case 0x92: return "Object Container";
+       case 0x9B: return "Presentation Text";
+       case 0xA7: return "Index";
+       case 0xA8: return "Document";
+       case 0xAD: return "Page Group";
+       case 0xAF: return "Page";
+       case 0xBB: return "Graphics";
+       case 0xC3: return "Data Resource";
+       case 0xC4: return "Document Environment Group (DEG)";
+       case 0xC6: return "Resource Group";
+       case 0xC7: return "Object Environment Group (OEG)";
+       case 0xC9: return "Active Environment Group (AEG)";
+       case 0xCC: return "Medium Map";
+       case 0xCD: return "Form Map";
+       case 0xCE: return "Name Resource";
+       case 0xD8: return "Page Overlay";
+       case 0xD9: return "Resource Environment Group (REG)";
+       case 0xDF: return "Overlay";
+       case 0xEA: return "Data Supression";
+       case 0xEB: return "Bar Code";
+       case 0xEE: return "No Operation";
+       case 0xFB: return "Image";
+       default: return "Unknown:" + 
Integer.toHexString(getSfTypeCode()).toUpperCase();
+       }
+   }
+
+   /**
+    * Returns the structured field's length.
+    * @return the field length
+    */
+   public short getSfLength() {
+       return introducer.length;
+   }
+
+   /**
+    * Returns the structured field's identifier.
+    * @return the field identifier
+    */
+   public int getSfTypeID() {
+       return ((getSfClassCode() & 0xFF) << 16)
+       | ((getSfTypeCode() & 0xFF) << 8)
+       | (getSfCategoryCode() & 0xFF);
+   }
+
+   /**
+    * Returns the structured field's class code.
+    * @return the field class code
+    */
+   public byte getSfClassCode() {
+       return introducer.classCode;
+   }
+
+   /**
+    * Returns the structured field's type code.
+    * @return the type code
+    */
+   public byte getSfTypeCode() {
+       return introducer.typeCode;
+   }
+
+   /**
+    * Returns the structured field's category code.
+    * @return the sfCategoryCode
+    */
+   public byte getSfCategoryCode() {
+       return introducer.categoryCode;
+   }
+
+   /**
+    * Indicates whether an field introducer extension is present.
+    * @return true if an field introducer extension is present
+    */
+   public boolean isSfiExtensionPresent() {
+       return introducer.extensionPresent && (this.extData != null);
+   }
+
+   /**
+    * Indicates whether segmented data is present.
+    * @return true if the data is segmented
+    */
+   public boolean isSfiSegmentedData() {
+       return introducer.segmentedData;
+   }
+
+   /**
+    * Indicates whether the data is padded.
+    * @return true if the data is padded
+    */
+   public boolean isSfiPaddingPresent() {
+       return introducer.paddingPresent;
+   }
+
+   /**
+    * Returns the length of the extension if present.
+    * @return the length of the extension (or 0 if no extension is present)
+    */
+   public short getExtLength() {
+       return (extData != null) ? (short)(extData.length + 1) : 0;
+
+   }
+
+   /**
+    * Returns the extension data if present.
+    * @return the extension data (or null if no extension is present)
+    */
+   byte[] getExtData() {
+       if (this.extData == null) {
+           return null;
+       }
+       byte[] rtn = new byte[this.extData.length];
+       System.arraycopy(this.extData, 0, rtn, 0, rtn.length);
+       return rtn;
+   }
+
+   /**
+    * Returns the structured field's payload.
+    * @return the field's data
+    */
+   public byte[] getData() {
+       if (this.data == null) {
+           return null;
+       }
+       byte[] rtn = new byte[this.data.length];
+       System.arraycopy(this.data, 0, rtn, 0, rtn.length);
+       return rtn;
+   }
+
+   //For unit testing
+   byte[] getIntroducerData() {
+       return introducer.getIntroducerData();
+   }
+
+   /**
+    * Returns the complete structured field as a byte array.
+    * @return the complete field data
+    */
+   public byte[] getCompleteFieldAsBytes() {
+
+       ByteArrayOutputStream baos = new ByteArrayOutputStream(getSfLength());
+       try {
+           writeTo(baos);
+       } catch (IOException ioe) {
+           //nop
+       }
+       return baos.toByteArray();
+
+   }
+
+   /**
+    * Writes this structured field to the given {@link OutputStream}.
+    * @param out the output stream
+    * @throws IOException if an I/O error occurs
+    */
+   public void writeTo(OutputStream out) throws IOException {
+       out.write(introducer.introducerData);
+       if (isSfiExtensionPresent()) {
+           out.write(this.extData.length + 1);
+           out.write(this.extData);
+       }
+       out.write(this.data);
+   }
+
+   static final class Introducer {
+
+       private final short length;
+       private final byte classCode;
+       private final byte typeCode;
+       private final byte categoryCode;
+       private final boolean extensionPresent;
+       private final boolean segmentedData;
+       private final boolean paddingPresent;
+       private final byte[] introducerData;
+
+       Introducer(byte[] introducerData) throws IOException {
+
+           this.introducerData = introducerData;
+
+           // Parse the introducer; the 8 bytes have already been read from 
the stream just
+           // before, so we parse the introducer from the byte array
+           DataInputStream iis = new DataInputStream(
+                   new ByteArrayInputStream(introducerData));
+
+           length = iis.readShort();
+
+           classCode = iis.readByte();
+           typeCode = iis.readByte();
+           categoryCode = iis.readByte();
+
+           //Flags
+           byte f = iis.readByte();
+
+           extensionPresent = (f & 0x01) != 0;
+           segmentedData = (f & 0x04) != 0;
+           paddingPresent = (f & 0x10) != 0;
+
+       }
+
+
+       public short getLength() {
+           return length;
+       }
+
+       public byte getClassCode() {
+           return classCode;
+       }
+
+       public byte getTypeCode() {
+           return typeCode;
+       }
+
+       public byte getCategoryCode() {
+           return categoryCode;
+       }
+
+       public boolean isExtensionPresent() {
+           return extensionPresent;
+       }
+
+
+       public boolean isSegmentedData() {
+           return segmentedData;
+       }
+
+       public boolean isPaddingPresent() {
+           return paddingPresent;
+       }
+
+       public byte[] getIntroducerData() {
+           byte[] rtn = new byte[introducerData.length];
+           System.arraycopy(introducerData, 0, rtn, 0, rtn.length);
+           return rtn;
+       }
+   }
+
 }

Modified: 
xmlgraphics/fop/trunk/src/java/org/apache/fop/afp/util/AFPResourceUtil.java
URL: 
http://svn.apache.org/viewvc/xmlgraphics/fop/trunk/src/java/org/apache/fop/afp/util/AFPResourceUtil.java?rev=1104135&r1=1104134&r2=1104135&view=diff
==============================================================================
--- xmlgraphics/fop/trunk/src/java/org/apache/fop/afp/util/AFPResourceUtil.java 
(original)
+++ xmlgraphics/fop/trunk/src/java/org/apache/fop/afp/util/AFPResourceUtil.java 
Tue May 17 11:07:06 2011
@@ -29,8 +29,8 @@ import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
 
 import org.apache.fop.afp.AFPConstants;
-import org.apache.fop.afp.modca.ResourceObject;
 import org.apache.fop.afp.modca.AbstractAFPObject.Category;
+import org.apache.fop.afp.modca.ResourceObject;
 import org.apache.fop.afp.parser.MODCAParser;
 import org.apache.fop.afp.parser.UnparsedStructuredField;
 

Modified: xmlgraphics/fop/trunk/status.xml
URL: 
http://svn.apache.org/viewvc/xmlgraphics/fop/trunk/status.xml?rev=1104135&r1=1104134&r2=1104135&view=diff
==============================================================================
--- xmlgraphics/fop/trunk/status.xml (original)
+++ xmlgraphics/fop/trunk/status.xml Tue May 17 11:07:06 2011
@@ -59,6 +59,9 @@
       documents. Example: the fix of marks layering will be such a case when 
it's done.
     -->
     <release version="FOP Trunk" date="TBD">
+      <action context="Renderers" dev="PH" type="fix" fixes-bug="50909">
+        Fixed io exception in MODCAParser caused by the improper use of mark() 
and reset() on the
+        MODCA data input stream.  Added unit test. </action>
       <action context="Fonts" dev="JM" type="fix" fixes-bug="51144" 
due-to="Mehdi Houshmand">
         Removed invalid entries in ToUnicode table of CID subset fonts. 
       </action>

Modified: xmlgraphics/fop/trunk/test/java/org/apache/fop/StandardTestSuite.java
URL: 
http://svn.apache.org/viewvc/xmlgraphics/fop/trunk/test/java/org/apache/fop/StandardTestSuite.java?rev=1104135&r1=1104134&r2=1104135&view=diff
==============================================================================
--- xmlgraphics/fop/trunk/test/java/org/apache/fop/StandardTestSuite.java 
(original)
+++ xmlgraphics/fop/trunk/test/java/org/apache/fop/StandardTestSuite.java Tue 
May 17 11:07:06 2011
@@ -23,6 +23,7 @@ import junit.framework.Test;
 import junit.framework.TestSuite;
 
 import org.apache.fop.area.ViewportTestSuite;
+import org.apache.fop.afp.parser.MODCAParserTestCase;
 import org.apache.fop.fonts.DejaVuLGCSerifTest;
 import org.apache.fop.image.loader.batik.ImageLoaderTestCase;
 import org.apache.fop.image.loader.batik.ImagePreloaderTestCase;
@@ -57,6 +58,7 @@ public class StandardTestSuite {
         suite.addTest(new TestSuite(PDFCMapTestCase.class));
         suite.addTest(new TestSuite(PDFsRGBSettingsTestCase.class));
         suite.addTest(new TestSuite(DejaVuLGCSerifTest.class));
+        suite.addTest(new TestSuite(MODCAParserTestCase.class));
         suite.addTest(AFPTestSuite.suite());
         suite.addTest(PSTestSuite.suite());
         suite.addTest(RichTextFormatTestSuite.suite());

Added: 
xmlgraphics/fop/trunk/test/java/org/apache/fop/afp/parser/MODCAParserTestCase.java
URL: 
http://svn.apache.org/viewvc/xmlgraphics/fop/trunk/test/java/org/apache/fop/afp/parser/MODCAParserTestCase.java?rev=1104135&view=auto
==============================================================================
--- 
xmlgraphics/fop/trunk/test/java/org/apache/fop/afp/parser/MODCAParserTestCase.java
 (added)
+++ 
xmlgraphics/fop/trunk/test/java/org/apache/fop/afp/parser/MODCAParserTestCase.java
 Tue May 17 11:07:06 2011
@@ -0,0 +1,238 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* $Id$ */
+
+package org.apache.fop.afp.parser;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.EOFException;
+import java.io.InputStream;
+import java.util.Arrays;
+
+import junit.framework.TestCase;
+
+/**
+ * MODCAParser and MODCAParser.UnparsedStructuredField Unit tests
+ */
+public class MODCAParserTestCase extends TestCase {
+
+    /** The carriage control character (0x5A) used to indicate the start of a 
structured field. */
+    public static final byte CARRIAGE_CONTROL_CHAR = (byte)0x5A;
+    /**ASCII carriage return control character*/
+    public static final byte CARRIAGE_RETURN = (byte)0x0A;
+    /**ASCII line feed control character */
+    public static final byte LINE_FEED = (byte)0x0D;
+    /** 8 byte introducer describe the SF */
+    private static final int INTRODUCER_LENGTH = 8;
+
+    /**
+     * Test that the MODCA parser recognises carriage control (0x5A) as the 
Structured Field
+     * delimeter
+     *
+     * @throws Exception *
+     */
+    public void testReadNextStructuredField1() throws Exception {
+
+        // carriage control (0x5A) delimits structured fields,
+        // and control is handed to readStructuredField(DataInputStream)
+        byte[][] goodInputStream = new byte[][]{
+                new byte[]{CARRIAGE_CONTROL_CHAR}
+        };
+
+        for (byte[] b : goodInputStream) {
+            try {
+                new MODCAParser(new ByteArrayInputStream(b))
+                        .readNextStructuredField();
+                fail("BAD SF should throw EOF: " + byteArrayToString(b));
+            } catch (EOFException eof) {
+                //passed
+            }
+        }
+
+        // EOFException thrown when reading the input stream are caught and
+        // a null value is returned
+        byte[][] badInputStream = new byte[][]{
+                new byte[]{},
+                new byte[]{CARRIAGE_RETURN},
+                new byte[]{LINE_FEED}
+        };
+
+        for (byte[] b : badInputStream) {
+            UnparsedStructuredField usf = new MODCAParser(new 
ByteArrayInputStream(b))
+            .readNextStructuredField();
+            assertNull(usf);
+        }
+    }
+
+
+    /**
+     * Test that the MODCA parser correctly constructs an 
UnparsedStructuredField
+     * from a well formed structured field
+     *
+     * @throws Exception *
+     */
+    public void testReadNextStructuredField2() throws Exception {
+
+        // no extension data
+        testSF((byte)0xd3, (byte)0xa8, (byte)0x89, //SFTypeID
+                (byte)0, //flags excluding the bits for
+                //extension present, segmented data and padding present
+                false, false,
+                new byte[]{0, 0},
+                new byte[]{1}, null);
+
+        // with extension data
+        testSF((byte)0xd3, (byte)0xa8, (byte)0x89, //SFTypeID
+                (byte)0, //flags excluding the bits for
+                //extension present, segmented data and padding present
+                false, false,
+                new byte[]{0, 0},
+                new byte[]{1}, new byte[]{10});
+
+        // with ignored reserved bits
+        testSF((byte)0xd3, (byte)0xa8, (byte)0x89, //SFTypeID
+                (byte)0, //flags excluding the bits for
+                //extension present, segmented data and padding present
+                false, false,
+                new byte[]{1, 2},
+                new byte[]{1}, null);
+
+        // with padding present and segmented data
+        testSF((byte)0xd3, (byte)0xa8, (byte)0x89, //SFTypeID
+                (byte)(1 << 3), //flags excluding the bits for
+                //extension present, segmented data and padding present
+                true, true,
+                new byte[]{0, 0},
+                new byte[]{1}, null);
+
+     // with flags non zero
+        testSF((byte)0xd3, (byte)0xa8, (byte)0x89, //SFTypeID
+                (byte)(1 << 3), //flags excluding the bits for
+                //extension present, segmented data and padding present
+                false, false,
+                new byte[]{0, 0},
+                new byte[]{1}, null);
+    }
+
+
+    private void testSF(byte classCode, byte typeCode, byte categoryCode,
+            byte flags, boolean segmentedData, boolean paddingPresent, byte[] 
reserved,
+            byte[] data, byte[] extData) throws Exception {
+
+        byte extDataLength = 0;
+        boolean extensionPresent = (extData != null);
+
+        if (extensionPresent) {
+            flags = (byte)(flags | 0x01);
+            extDataLength = (byte)(extData.length + 1); //length includes 
length byte
+        }
+
+        if (segmentedData) {
+            flags = (byte)(flags | 0x04);
+        }
+
+        if (paddingPresent) {
+            flags = (byte)(flags | 0x10);
+        }
+
+        short length = (short)(INTRODUCER_LENGTH + data.length + 
extDataLength);
+        byte[] lengthBytes = new byte[]{(byte)(length >> 8), (byte)(length & 
0xFF)};
+
+        byte[] sfBytes = new byte[length];
+
+        //introducer bytes
+        sfBytes[0] = lengthBytes[0];
+        sfBytes[1] = lengthBytes[1];
+        sfBytes[2] = classCode;
+        sfBytes[3] = typeCode;
+        sfBytes[4] = categoryCode;
+        sfBytes[5] = flags;
+        sfBytes[6] = reserved[0];
+        sfBytes[7] = reserved[1];
+
+        if (extDataLength > 0) {
+            sfBytes[8] = (byte)(extData.length + 1);
+            System.arraycopy(extData, 0, sfBytes, 9, extData.length);
+        }
+
+        System.arraycopy(data, 0, sfBytes, length - data.length, data.length);
+
+
+        byte[] delimiteredSF = new byte[length + 1];
+
+        delimiteredSF[0] = (byte)0x5A;
+
+        System.arraycopy(sfBytes, 0, delimiteredSF, 1, length);
+
+        InputStream bis = new ByteArrayInputStream(delimiteredSF);
+
+        UnparsedStructuredField actual =  new MODCAParser(bis)
+        .readNextStructuredField();
+
+        //check introducer
+        assertEquals(length, actual.getSfLength());
+        assertEquals(classCode, actual.getSfClassCode());
+        assertEquals(typeCode, actual.getSfTypeCode());
+        assertEquals(categoryCode, actual.getSfCategoryCode());
+        assertEquals(extensionPresent, actual.isSfiExtensionPresent());
+        assertEquals(segmentedData, actual.isSfiSegmentedData());
+        assertEquals(paddingPresent, actual.isSfiPaddingPresent());
+
+        byte[] introducerData = new byte[]{(byte)(length >> 8), (byte)(length 
& 0xFF),
+                classCode,  typeCode, categoryCode, flags, reserved[0], 
reserved[1]};
+
+        assertTrue(Arrays.equals(introducerData, actual.getIntroducerData()));
+
+        //check data
+        assertTrue(Arrays.equals(data, actual.getData()));
+
+        //check extension data
+        if (extData != null) {
+            assertTrue(Arrays.equals(extData, actual.getExtData()));
+        }
+        assertEquals(
+                (extData == null) ? 0 : extData.length + 1, // 1 byte for 
length byte
+                        actual.getExtLength());
+
+        assertTrue(Arrays.equals(data, actual.getData()));
+
+        int expectedSfTypeID = ((classCode & 0xFF) << 16)
+                | ((typeCode & 0xFF) << 8)
+                | (categoryCode & 0xFF);
+
+        assertEquals(expectedSfTypeID, actual.getSfTypeID());
+
+        assertTrue(Arrays.equals(sfBytes, actual.getCompleteFieldAsBytes()));
+
+        ByteArrayOutputStream baos = new ByteArrayOutputStream();
+        actual.writeTo(baos);
+        assertTrue(Arrays.equals(sfBytes, baos.toByteArray()));
+
+    }
+
+
+    private static String byteArrayToString(byte[] byteArray) {
+        StringBuilder sb = new StringBuilder();
+        for (byte b : byteArray) {
+            sb.append(Integer.toHexString(b & 0xFF)).append(" ");
+        }
+        return sb.toString();
+    }
+
+}



---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to