]> source.dussan.org Git - poi.git/commitdiff
Merged revisions 638786-638802,638805-638811,638813-638814,638816-639230,639233-63924...
authorNick Burch <nick@apache.org>
Tue, 20 May 2008 16:49:41 +0000 (16:49 +0000)
committerNick Burch <nick@apache.org>
Tue, 20 May 2008 16:49:41 +0000 (16:49 +0000)
https://svn.apache.org/repos/asf/poi/trunk

........
  r657702 | josh | 2008-05-19 02:06:54 +0100 (Mon, 19 May 2008) | 1 line

  Bug 44306 - fixed reading/writing of AttrPtg and rendering of CHOOSE formulas
........
  r657875 | yegor | 2008-05-19 18:29:55 +0100 (Mon, 19 May 2008) | 1 line

  updated release date and started a new section for 3.1-final
........
  r658033 | josh | 2008-05-20 00:07:27 +0100 (Tue, 20 May 2008) | 1 line

  Minor patch to improve FormulaParser error messages like those from bug 45041
........
  r658285 | nick | 2008-05-20 16:42:16 +0100 (Tue, 20 May 2008) | 1 line

  Fix bug 44898 - Correctly handle short last blocks in POIFS
........
  r658287 | nick | 2008-05-20 16:46:54 +0100 (Tue, 20 May 2008) | 1 line

  Fix #44824 - Avoid an infinite loop when reading some HWPF pictures
........
  r658302 | nick | 2008-05-20 17:01:53 +0100 (Tue, 20 May 2008) | 1 line

  Patch from bug #44937 from Squeeself- Partial support for extracting Escher images from HWPF files
........
  r658308 | nick | 2008-05-20 17:30:19 +0100 (Tue, 20 May 2008) | 1 line

  Support for specifying a policy to HSSF on missing / blank cells when fetching
........

git-svn-id: https://svn.apache.org/repos/asf/poi/branches/ooxml@658333 13f79535-47bb-0310-9956-ffa450edef68

25 files changed:
src/documentation/content/xdocs/changes.xml
src/documentation/content/xdocs/status.xml
src/java/org/apache/poi/ddf/EscherClientAnchorRecord.java
src/java/org/apache/poi/hssf/model/FormulaParser.java
src/java/org/apache/poi/hssf/record/formula/AttrPtg.java
src/java/org/apache/poi/hssf/usermodel/HSSFRow.java
src/java/org/apache/poi/poifs/storage/RawDataBlock.java
src/java/org/apache/poi/poifs/storage/RawDataBlockList.java
src/ooxml/interfaces-jdk14/org/apache/poi/ss/usermodel/Row.java
src/ooxml/interfaces-jdk15/org/apache/poi/ss/usermodel/Row.java
src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFRow.java
src/ooxml/testcases/org/apache/poi/xssf/usermodel/TestXSSFRow.java
src/scratchpad/src/org/apache/poi/hwpf/HWPFDocument.java
src/scratchpad/src/org/apache/poi/hwpf/model/EscherRecordHolder.java [new file with mode: 0644]
src/scratchpad/src/org/apache/poi/hwpf/model/FSPA.java [new file with mode: 0644]
src/scratchpad/src/org/apache/poi/hwpf/model/FSPATable.java [new file with mode: 0644]
src/scratchpad/src/org/apache/poi/hwpf/model/FileInformationBlock.java
src/scratchpad/src/org/apache/poi/hwpf/model/PicturesTable.java
src/scratchpad/src/org/apache/poi/hwpf/usermodel/Picture.java
src/scratchpad/testcases/org/apache/poi/hwpf/TestHWPFPictures.java
src/testcases/org/apache/poi/hssf/record/TestExternalNameRecord.java
src/testcases/org/apache/poi/hssf/record/TestFormulaRecord.java
src/testcases/org/apache/poi/hssf/usermodel/TestBugs.java
src/testcases/org/apache/poi/hssf/usermodel/TestHSSFRow.java
src/testcases/org/apache/poi/poifs/filesystem/TestPOIFSFileSystem.java

index 380ecb923c0c6d92611e9b85cae8a9fdd6a758b8..b84dec3344dd10361b54ede2a166a38fa1894dd6 100644 (file)
            <action dev="POI-DEVELOPERS" type="add">Created a common interface for handling PowerPoint files, irrespective of if they are .ppt or .pptx</action>
            <action dev="POI-DEVELOPERS" type="add">Created a common interface for handling Excel files, irrespective of if they are .xls or .xlsx</action>
         </release>
-        <release version="3.1-beta2" date="2008-05-??">
+        <release version="3.1-final" date="2008-06-??">
+           <action dev="POI-DEVELOPERS" type="add">Support for specifying a policy to HSSF on missing / blank cells when fetching</action>
+           <action dev="POI-DEVELOPERS" type="add">44937 - Partial support for extracting Escher images from HWPF files</action>
+           <action dev="POI-DEVELOPERS" type="fix">44824 - Avoid an infinite loop when reading some HWPF pictures</action>
+           <action dev="POI-DEVELOPERS" type="fix">44898 - Correctly handle short last blocks in POIFS</action>
+        </release>  
+        <release version="3.1-beta2" date="2008-05-26">
+           <action dev="POI-DEVELOPERS" type="fix">44306 - fixed reading/writing of AttrPtg(type=choose) and method toFormulaString() for CHOOSE formulas</action>
            <action dev="POI-DEVELOPERS" type="fix">24207 - added HSSFName.isDeleted() to check if the name points to cell that no longer exists</action>
            <action dev="POI-DEVELOPERS" type="fix">40414 - fixed selected/active sheet after removing sheet from workbook</action>
            <action dev="POI-DEVELOPERS" type="fix">44523 - fixed workbook sheet selection and focus</action>
index ebf869ce9593ad4b03b0562dd9d7238bf0fb2874..0b7f0140d4e4622982ab416c96bb7b19577de2d4 100644 (file)
            <action dev="POI-DEVELOPERS" type="add">Created a common interface for handling PowerPoint files, irrespective of if they are .ppt or .pptx</action>
            <action dev="POI-DEVELOPERS" type="add">Created a common interface for handling Excel files, irrespective of if they are .xls or .xlsx</action>
         </release>
-        <release version="3.1-beta2" date="2008-05-??">
+        <release version="3.1-final" date="2008-06-??">
+           <action dev="POI-DEVELOPERS" type="add">Support for specifying a policy to HSSF on missing / blank cells when fetching</action>
+           <action dev="POI-DEVELOPERS" type="add">44937 - Partial support for extracting Escher images from HWPF files</action>
+           <action dev="POI-DEVELOPERS" type="fix">44824 - Avoid an infinite loop when reading some HWPF pictures</action>
+           <action dev="POI-DEVELOPERS" type="fix">44898 - Correctly handle short last blocks in POIFS</action>
+        </release>  
+        <release version="3.1-beta2" date="2008-05-26">
+           <action dev="POI-DEVELOPERS" type="fix">44306 - fixed reading/writing of AttrPtg(type=choose) and method toFormulaString() for CHOOSE formulas</action>
            <action dev="POI-DEVELOPERS" type="fix">24207 - added HSSFName.isDeleted() to check if the name points to cell that no longer exists</action>
            <action dev="POI-DEVELOPERS" type="fix">40414 - fixed selected/active sheet after removing sheet from workbook</action>
            <action dev="POI-DEVELOPERS" type="fix">44523 - fixed workbook sheet selection and focus</action>
index f3d90c715d450a49453189c9921c834336073d53..ec3354c59952bac998f340f1d9fdaddd1afad284 100644 (file)
@@ -65,20 +65,27 @@ public class EscherClientAnchorRecord
         int size           = 0;
 
         // Always find 4 two byte entries. Sometimes find 9
-        field_1_flag   =  LittleEndian.getShort( data, pos + size );     size += 2;
-        field_2_col1   =  LittleEndian.getShort( data, pos + size );     size += 2;
-        field_3_dx1    =  LittleEndian.getShort( data, pos + size );     size += 2;
-        field_4_row1   =  LittleEndian.getShort( data, pos + size );     size += 2;
-        if(bytesRemaining >= 18) {
-                   field_5_dy1    =  LittleEndian.getShort( data, pos + size );     size += 2;
-                   field_6_col2   =  LittleEndian.getShort( data, pos + size );     size += 2;
-                   field_7_dx2    =  LittleEndian.getShort( data, pos + size );     size += 2;
-                   field_8_row2   =  LittleEndian.getShort( data, pos + size );     size += 2;
-                   field_9_dy2    =  LittleEndian.getShort( data, pos + size );     size += 2;
-                       shortRecord = false;
-        } else {
-                       shortRecord = true;
-               }
+        if (bytesRemaining == 4) // Word format only 4 bytes
+        {
+            // Not sure exactly what the format is quite yet, likely a reference to a PLC
+        }
+        else
+        {
+            field_1_flag   =  LittleEndian.getShort( data, pos + size );     size += 2;
+            field_2_col1   =  LittleEndian.getShort( data, pos + size );     size += 2;
+            field_3_dx1    =  LittleEndian.getShort( data, pos + size );     size += 2;
+            field_4_row1   =  LittleEndian.getShort( data, pos + size );     size += 2;
+            if(bytesRemaining >= 18) {
+                field_5_dy1    =  LittleEndian.getShort( data, pos + size );     size += 2;
+                field_6_col2   =  LittleEndian.getShort( data, pos + size );     size += 2;
+                field_7_dx2    =  LittleEndian.getShort( data, pos + size );     size += 2;
+                field_8_row2   =  LittleEndian.getShort( data, pos + size );     size += 2;
+                field_9_dy2    =  LittleEndian.getShort( data, pos + size );     size += 2;
+                shortRecord = false;
+            } else {
+                shortRecord = true;
+            }
+        }
         bytesRemaining -= size;
         remainingData  =  new byte[bytesRemaining];
         System.arraycopy( data, pos + size, remainingData, 0, bytesRemaining );
index 88a39ffe6fc5e45fd93aa64417240f57d697fcf4..39a79bb76d1551e9f09d504905cf31ab0eed5dd2 100644 (file)
@@ -134,7 +134,10 @@ public final class FormulaParser {
 
     /** Report What Was Expected */
     private RuntimeException expected(String s) {
-        return new FormulaParseException(s + " Expected");
+        String msg = "Parse error near char " + (pointer-1) + "'" + look + "'" 
+            + " in specified formula '" + formulaString + "'. Expected "
+            + s;
+        return new FormulaParseException(msg);
     }
 
 
@@ -1000,7 +1003,7 @@ end;
 
             if (ptg instanceof AttrPtg) {
                 AttrPtg attrPtg = ((AttrPtg) ptg);
-                if (attrPtg.isOptimizedIf()) {
+                if (attrPtg.isOptimizedIf() || attrPtg.isOptimizedChoose() || attrPtg.isGoto()) {
                     continue;
                 }
                 if (attrPtg.isSpace()) {
@@ -1014,6 +1017,9 @@ end;
                     // similar to tAttrSpace - RPN is violated
                     continue;
                 }
+                if (!attrPtg.isSum()) {
+                    throw new RuntimeException("Unexpected tAttr: " + attrPtg.toString());
+                }
             }
 
             final OperationPtg o = (OperationPtg) ptg;
index c2ff29116f268db943de6e2e53181f0f0255c8c6..263c1728ba2beb8dca523502858b16674278b73c 100644 (file)
@@ -39,6 +39,11 @@ public final class AttrPtg extends OperationPtg {
     private byte              field_1_options;
     private short             field_2_data;
     
+    /** only used for tAttrChoose: table of offsets to starts of args */
+    private final int[] _jumpTable;
+    /** only used for tAttrChoose: offset to the tFuncVar for CHOOSE() */
+    private final int   _chooseFuncOffset;
+    
     // flags 'volatile' and 'space', can be combined.  
     // OOO spec says other combinations are theoretically possible but not likely to occur.
     private static final BitField semiVolatile = BitFieldFactory.getInstance(0x01);
@@ -63,7 +68,7 @@ public final class AttrPtg extends OperationPtg {
         /** 03H = Carriage returns before opening parenthesis (only allowed before tParen token) */
         public static final int CR_BEFORE_OPEN_PAREN = 0x03;
         /** 04H = Spaces before closing parenthesis (only allowed before tParen, tFunc, and tFuncVar tokens) */
-        public static final int SPACE_BEFORE_CLOSE_PAERN = 0x04;
+        public static final int SPACE_BEFORE_CLOSE_PAREN = 0x04;
         /** 05H = Carriage returns before closing parenthesis (only allowed before tParen, tFunc, and tFuncVar tokens) */
         public static final int CR_BEFORE_CLOSE_PAREN = 0x05;
         /** 06H = Spaces following the equality sign (only in macro sheets) */
@@ -71,16 +76,33 @@ public final class AttrPtg extends OperationPtg {
     }
 
     public AttrPtg() {
+        _jumpTable = null;
+        _chooseFuncOffset = -1;
     }
     
     public AttrPtg(RecordInputStream in)
     {
         field_1_options = in.readByte();
         field_2_data    = in.readShort();
+        if (isOptimizedChoose()) {
+            int nCases = field_2_data;
+            int[] jumpTable = new int[nCases];
+            for (int i = 0; i < jumpTable.length; i++) {
+                jumpTable[i] = in.readUShort();
+            }
+            _jumpTable = jumpTable;
+            _chooseFuncOffset = in.readUShort();
+        } else {
+            _jumpTable = null;
+            _chooseFuncOffset = -1;
+        }
+        
     }
-    private AttrPtg(int options, int data) {
+    private AttrPtg(int options, int data, int[] jt, int chooseFuncOffset) {
         field_1_options = (byte) options;
         field_2_data = (short) data;
+        _jumpTable = jt;
+        _chooseFuncOffset = chooseFuncOffset;
     }
     
     /**
@@ -89,7 +111,7 @@ public final class AttrPtg extends OperationPtg {
      */
     public static AttrPtg createSpace(int type, int count) {
         int data = type & 0x00FF | (count << 8) & 0x00FFFF;
-        return new AttrPtg(space.set(0), data);
+        return new AttrPtg(space.set(0), data, null, -1);
     }
 
     public void setOptions(byte options)
@@ -136,14 +158,14 @@ public final class AttrPtg extends OperationPtg {
         field_1_options=optiIf.setByteBoolean(field_1_options,bif);
     }
 
-       /**
-        * Flags this ptg as a goto/jump 
-        * @param isGoto
-        */
-       public void setGoto(boolean isGoto) {
-               field_1_options=optGoto.setByteBoolean(field_1_options, isGoto);
-       }
-       
+    /**
+     * Flags this ptg as a goto/jump 
+     * @param isGoto
+     */
+    public void setGoto(boolean isGoto) {
+        field_1_options=optGoto.setByteBoolean(field_1_options, isGoto);
+    }
+    
     // lets hope no one uses this anymore
     public boolean isBaxcel()
     {
@@ -181,7 +203,7 @@ public final class AttrPtg extends OperationPtg {
         if(isOptimizedIf()) {
             sb.append("if dist=").append(getData());
         } else if(isOptimizedChoose()) {
-            sb.append("choose dist=").append(getData());
+            sb.append("choose nCases=").append(getData());
         } else if(isGoto()) {
             sb.append("skip dist=").append(getData()); 
         } else if(isSum()) {
@@ -195,13 +217,28 @@ public final class AttrPtg extends OperationPtg {
 
     public void writeBytes(byte [] array, int offset)
     {
-        array[offset]=sid;
-        array[offset+1]=field_1_options;
-        LittleEndian.putShort(array,offset+2,field_2_data);                
+        LittleEndian.putByte(array, offset+0, sid);
+        LittleEndian.putByte(array, offset+1, field_1_options);
+        LittleEndian.putShort(array,offset+2, field_2_data);
+        int[] jt = _jumpTable;
+        if (jt != null) {
+            int joff = offset+4; 
+            LittleEndian.putUShort(array, joff, _chooseFuncOffset);
+            joff+=2;
+            for (int i = 0; i < jt.length; i++) {
+                LittleEndian.putUShort(array, joff, jt[i]);
+                joff+=2;
+            }
+            LittleEndian.putUShort(array, joff, _chooseFuncOffset);
+        }
+        
     }
 
     public int getSize()
     {
+        if (_jumpTable != null) {
+            return SIZE + (_jumpTable.length + 1) * LittleEndian.SHORT_SIZE;
+        }
         return SIZE;
     }
 
@@ -255,12 +292,17 @@ public final class AttrPtg extends OperationPtg {
     
     
  
-    public byte getDefaultOperandClass() {return Ptg.CLASS_VALUE;}
+    public byte getDefaultOperandClass() {
+        return Ptg.CLASS_VALUE;
+    }
 
     public Object clone() {
-      AttrPtg ptg = new AttrPtg();
-      ptg.field_1_options = field_1_options;
-      ptg.field_2_data = field_2_data;
-      return ptg;
+        int[] jt;
+        if (_jumpTable == null) {
+            jt = null;
+        } else {
+            jt = (int[]) _jumpTable.clone();
+        }
+        return new AttrPtg(field_1_options, field_2_data, jt, _chooseFuncOffset);
     }
 }
index d62a7c23a046fe67d943334be6fce6fb3a7544a2..056c872e7c2ae842f428267580e5b4ecd8f12de8 100644 (file)
@@ -319,6 +319,36 @@ public final class HSSFRow implements Comparable, Row {
         if(cellnum<0||cellnum>=cells.length) return null;
         return cells[cellnum];
     }
+    
+    /**
+     * Get the hssfcell representing a given column (logical cell)
+     *  0-based.  If you ask for a cell that is not defined, then
+     *  your supplied policy says what to do
+     *
+     * @param cellnum  0 based column number
+     * @param policy Policy on blank / missing cells
+     * @return representing that column or null if undefined + policy allows.
+     */
+    public HSSFCell getCell(int cellnum, MissingCellPolicy policy) {
+       HSSFCell cell = getCell(cellnum);
+       if(policy == RETURN_NULL_AND_BLANK) {
+               return cell;
+       }
+       if(policy == RETURN_BLANK_AS_NULL) {
+               if(cell == null) return cell;
+               if(cell.getCellType() == HSSFCell.CELL_TYPE_BLANK) {
+                       return null;
+               }
+               return cell;
+       }
+       if(policy == CREATE_NULL_AS_BLANK) {
+               if(cell == null) {
+                       return createCell((short)cellnum, HSSFCell.CELL_TYPE_BLANK);
+               }
+               return cell;
+       }
+       throw new IllegalArgumentException("Illegal policy " + policy + " (" + policy.id + ")");
+    }
 
     /**
      * get the number of the first cell contained in this row.
@@ -485,6 +515,7 @@ public final class HSSFRow implements Comparable, Row {
         return cellnum;
     }
 
+
     /**
      * @return cell iterator of the physically defined cells. 
      * Note that the 4th element might well not be cell 4, as the iterator
@@ -503,6 +534,9 @@ public final class HSSFRow implements Comparable, Row {
        return cellIterator();
     }
     
+    /**
+     * An iterator over the (physical) cells in the row.
+     */
     private class CellIterator implements Iterator
     {
       int thisId=-1;
index b4630a78b400ef9e46f5a57533102c1713cd0468..5ca1781d072c12a5fc22b615e1ef379f8466959f 100644 (file)
@@ -37,6 +37,7 @@ public class RawDataBlock
 {
     private byte[]  _data;
     private boolean _eof;
+    private boolean _hasData;
     private static POILogger log = POILogFactory.getLogger(RawDataBlock.class);
 
     /**
@@ -66,6 +67,7 @@ public class RawDataBlock
                throws IOException {
         _data = new byte[ blockSize ];
         int count = IOUtils.readFully(stream, _data);
+        _hasData = (count > 0);
 
         if (count == -1) {
             _eof = true;
@@ -94,16 +96,21 @@ public class RawDataBlock
     /**
      * When we read the data, did we hit end of file?
      *
-     * @return true if no data was read because we were at the end of
-     *         the file, else false
-     *
-     * @exception IOException
+     * @return true if the EoF was hit during this block, or
+     *  false if not. If you have a dodgy short last block, then
+     *  it's possible to both have data, and also hit EoF...
      */
-    public boolean eof()
-        throws IOException
-    {
+    public boolean eof() {
         return _eof;
     }
+    /**
+     * Did we actually find any data to read? It's possible,
+     *  in the event of a short last block, to both have hit
+     *  the EoF, but also to have data
+     */
+    public boolean hasData() {
+       return _hasData;
+    }
 
     /* ********** START implementation of ListManagedBlock ********** */
 
@@ -117,7 +124,7 @@ public class RawDataBlock
     public byte [] getData()
         throws IOException
     {
-        if (eof())
+        if (! hasData())
         {
             throw new IOException("Cannot return empty data");
         }
index 76ab219562007e692fee01b6b39f5553b6c11ee7..66eb237a8e92916509c2db61fe4f10243c73ba11 100644 (file)
@@ -51,12 +51,16 @@ public class RawDataBlockList
         while (true)
         {
             RawDataBlock block = new RawDataBlock(stream, bigBlockSize);
+            
+            // If there was data, add the block to the list
+            if(block.hasData()) {
+               blocks.add(block);
+            }
 
-            if (block.eof())
-            {
+            // If the stream is now at the End Of File, we're done
+            if (block.eof()) {
                 break;
             }
-            blocks.add(block);
         }
         setBlocks(( RawDataBlock [] ) blocks.toArray(new RawDataBlock[ 0 ]));
     }
index 80a191d7c67fb47906518c3a4acafb9b07987f18..8a0810dd315148cd1645a0e91b1d6afdd095a4d8 100644 (file)
@@ -33,4 +33,22 @@ public interface Row {
     HSSFCell getCell(int cellnum);
 
     Iterator cellIterator();
+
+    /**
+     * Used to specify the different possible policies
+     *  if for the case of null and blank cells
+     */
+    public static class MissingCellPolicy {
+       private static int NEXT_ID = 1;
+       public final int id;
+       private MissingCellPolicy() {
+               this.id = NEXT_ID++;
+       }
+    }
+    /** Missing cells are returned as null, Blank cells are returned as normal */
+    public static final MissingCellPolicy RETURN_NULL_AND_BLANK = new MissingCellPolicy();
+    /** Missing cells are returned as null, as are blank cells */
+    public static final MissingCellPolicy RETURN_BLANK_AS_NULL = new MissingCellPolicy();
+    /** A new, blank cell is created for missing cells. Blank cells are returned as normal */
+    public static final MissingCellPolicy CREATE_NULL_AS_BLANK = new MissingCellPolicy();
 }
index 28257bae423aa546ebc076a30b24cd57a1761a29..0c46c5863d56dc7ff613c22af6dd71a4b829052c 100644 (file)
@@ -88,9 +88,20 @@ public interface Row extends Iterable<Cell> {
      * ask for a cell that is not defined....you get a null.
      *
      * @param cellnum  0 based column number
-     * @return HSSFCell representing that column or null if undefined.
+     * @return Cell representing that column or null if undefined.
      */
     Cell getCell(int cellnum);
+    
+    /**
+     * Get the hssfcell representing a given column (logical cell)
+     *  0-based.  If you ask for a cell that is not defined, then
+     *  your supplied policy says what to do
+     *
+     * @param cellnum  0 based column number
+     * @param policy Policy on blank / missing cells
+     * @return representing that column or null if undefined + policy allows.
+     */
+    public Cell getCell(int cellnum, MissingCellPolicy policy);
 
     /**
      * get the number of the first cell contained in this row.
@@ -170,4 +181,22 @@ public interface Row extends Iterable<Cell> {
 
     boolean equals(Object obj);
 
+    
+    /**
+     * Used to specify the different possible policies
+     *  if for the case of null and blank cells
+     */
+    public static class MissingCellPolicy {
+       private static int NEXT_ID = 1;
+       public final int id;
+       private MissingCellPolicy() {
+               this.id = NEXT_ID++;
+       }
+    }
+    /** Missing cells are returned as null, Blank cells are returned as normal */
+    public static final MissingCellPolicy RETURN_NULL_AND_BLANK = new MissingCellPolicy();
+    /** Missing cells are returned as null, as are blank cells */
+    public static final MissingCellPolicy RETURN_BLANK_AS_NULL = new MissingCellPolicy();
+    /** A new, blank cell is created for missing cells. Blank cells are returned as normal */
+    public static final MissingCellPolicy CREATE_NULL_AS_BLANK = new MissingCellPolicy();
 }
index 1a1e831a2ac12f8bd6a513718f9a2adf6676d19d..3047368e22a022d6a31dbf5419fcd637ac00d6ef 100644 (file)
@@ -21,6 +21,7 @@ import java.util.Iterator;
 import java.util.LinkedList;
 import java.util.List;
 
+import org.apache.poi.hssf.usermodel.HSSFCell;
 import org.apache.poi.ss.usermodel.Cell;
 import org.apache.poi.ss.usermodel.Row;
 import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTCell;
@@ -138,6 +139,27 @@ public class XSSFRow implements Row {
         }
         return null;
     }
+    
+    public Cell getCell(int cellnum, MissingCellPolicy policy) {
+       Cell cell = getCell(cellnum);
+       if(policy == RETURN_NULL_AND_BLANK) {
+               return cell;
+       }
+       if(policy == RETURN_BLANK_AS_NULL) {
+               if(cell == null) return cell;
+               if(cell.getCellType() == HSSFCell.CELL_TYPE_BLANK) {
+                       return null;
+               }
+               return cell;
+       }
+       if(policy == CREATE_NULL_AS_BLANK) {
+               if(cell == null) {
+                       return createCell((short)cellnum, HSSFCell.CELL_TYPE_BLANK);
+               }
+               return cell;
+       }
+       throw new IllegalArgumentException("Illegal policy " + policy + " (" + policy.id + ")");
+    }
 
     public short getFirstCellNum() {
        for (Iterator<Cell> it = cellIterator() ; it.hasNext() ; ) {
index 54ded70ca0ff8b08755d8d17e62f05c206e8059d..a682bab1c64e44bd032aa82f22a17e37ba4897c6 100644 (file)
@@ -22,6 +22,7 @@ import java.util.Iterator;
 import junit.framework.TestCase;
 
 import org.apache.poi.ss.usermodel.Cell;
+import org.apache.poi.ss.usermodel.Row;
 import org.apache.poi.xssf.usermodel.TestXSSFCell.DummySharedStringSource;
 
 /**
@@ -166,6 +167,64 @@ public final class TestXSSFRow extends TestCase {
         row.setZeroHeight(true);
         assertTrue(row.getZeroHeight());
     }
+        
+    /**
+     * Tests for the missing/blank cell policy stuff
+     */
+    public void testGetCellPolicy() throws Exception {
+        XSSFRow row = new XSSFRow(createParentObjects());
+
+        // 0 -> string
+        // 1 -> num
+        // 2 missing
+        // 3 missing
+        // 4 -> blank
+        // 5 -> num
+        row.createCell((short)0).setCellValue(new XSSFRichTextString("test"));
+        row.createCell((short)1).setCellValue(3.2);
+        row.createCell((short)4, Cell.CELL_TYPE_BLANK);
+        row.createCell((short)5).setCellValue(4);
+        
+        // First up, no policy
+        assertEquals(Cell.CELL_TYPE_STRING,  row.getCell(0).getCellType());
+        assertEquals(Cell.CELL_TYPE_NUMERIC, row.getCell(1).getCellType());
+        assertEquals(null, row.getCell(2));
+        assertEquals(null, row.getCell(3));
+        assertEquals(Cell.CELL_TYPE_BLANK,   row.getCell(4).getCellType());
+        assertEquals(Cell.CELL_TYPE_NUMERIC, row.getCell(5).getCellType());
+        
+        // RETURN_NULL_AND_BLANK - same as default
+        assertEquals(Cell.CELL_TYPE_STRING,  row.getCell(0, Row.RETURN_NULL_AND_BLANK).getCellType());
+        assertEquals(Cell.CELL_TYPE_NUMERIC, row.getCell(1, Row.RETURN_NULL_AND_BLANK).getCellType());
+        assertEquals(null, row.getCell(2, Row.RETURN_NULL_AND_BLANK));
+        assertEquals(null, row.getCell(3, Row.RETURN_NULL_AND_BLANK));
+        assertEquals(Cell.CELL_TYPE_BLANK,   row.getCell(4, Row.RETURN_NULL_AND_BLANK).getCellType());
+        assertEquals(Cell.CELL_TYPE_NUMERIC, row.getCell(5, Row.RETURN_NULL_AND_BLANK).getCellType());
+        
+        // RETURN_BLANK_AS_NULL - nearly the same
+        assertEquals(Cell.CELL_TYPE_STRING,  row.getCell(0, XSSFRow.RETURN_BLANK_AS_NULL).getCellType());
+        assertEquals(Cell.CELL_TYPE_NUMERIC, row.getCell(1, XSSFRow.RETURN_BLANK_AS_NULL).getCellType());
+        assertEquals(null, row.getCell(2, XSSFRow.RETURN_BLANK_AS_NULL));
+        assertEquals(null, row.getCell(3, XSSFRow.RETURN_BLANK_AS_NULL));
+        assertEquals(null, row.getCell(4, XSSFRow.RETURN_BLANK_AS_NULL));
+        assertEquals(Cell.CELL_TYPE_NUMERIC, row.getCell(5, XSSFRow.RETURN_BLANK_AS_NULL).getCellType());
+        
+        // CREATE_NULL_AS_BLANK - creates as needed
+        assertEquals(Cell.CELL_TYPE_STRING,  row.getCell(0, XSSFRow.CREATE_NULL_AS_BLANK).getCellType());
+        assertEquals(Cell.CELL_TYPE_NUMERIC, row.getCell(1, XSSFRow.CREATE_NULL_AS_BLANK).getCellType());
+        assertEquals(Cell.CELL_TYPE_BLANK,   row.getCell(2, XSSFRow.CREATE_NULL_AS_BLANK).getCellType());
+        assertEquals(Cell.CELL_TYPE_BLANK,   row.getCell(3, XSSFRow.CREATE_NULL_AS_BLANK).getCellType());
+        assertEquals(Cell.CELL_TYPE_BLANK,   row.getCell(4, XSSFRow.CREATE_NULL_AS_BLANK).getCellType());
+        assertEquals(Cell.CELL_TYPE_NUMERIC, row.getCell(5, XSSFRow.CREATE_NULL_AS_BLANK).getCellType());
+        
+        // Check created ones get the right column
+        assertEquals((short)0, row.getCell(0, XSSFRow.CREATE_NULL_AS_BLANK).getCellNum());
+        assertEquals((short)1, row.getCell(1, XSSFRow.CREATE_NULL_AS_BLANK).getCellNum());
+        assertEquals((short)2, row.getCell(2, XSSFRow.CREATE_NULL_AS_BLANK).getCellNum());
+        assertEquals((short)3, row.getCell(3, XSSFRow.CREATE_NULL_AS_BLANK).getCellNum());
+        assertEquals((short)4, row.getCell(4, XSSFRow.CREATE_NULL_AS_BLANK).getCellNum());
+        assertEquals((short)5, row.getCell(5, XSSFRow.CREATE_NULL_AS_BLANK).getCellNum());
+    }
 
     /**
      * Method that returns a row with some sample cells
index a54e50de43c618c5a7d5f82507a0c88488c03d07..a43357f02146ea1950eb4b9914e04f2b6bcaf3d4 100644 (file)
@@ -53,10 +53,10 @@ public class HWPFDocument extends POIDocument
   protected FileInformationBlock _fib;
 
   /** main document stream buffer*/
-  private byte[] _mainStream;
+  protected byte[] _mainStream;
 
   /** table stream buffer*/
-  private byte[] _tableStream;
+  protected byte[] _tableStream;
 
   /** data stream buffer*/
   protected byte[] _dataStream;
@@ -93,6 +93,12 @@ public class HWPFDocument extends POIDocument
   
   /** Holds pictures table */
   protected PicturesTable _pictures;
+  
+  /** Holds FSBA (shape) information */
+  protected FSPATable _fspa;
+  
+  /** Escher Drawing Group information */
+  protected EscherRecordHolder _dgg;
 
   protected HWPFDocument()
   {
@@ -204,9 +210,6 @@ public class HWPFDocument extends POIDocument
     {
         _dataStream = new byte[0];
     }
-    
-    // read in the pictures stream
-    _pictures = new PicturesTable(this, _dataStream);
 
     // get the start of text in the main stream
     int fcMin = _fib.getFcMin();
@@ -226,6 +229,20 @@ public class HWPFDocument extends POIDocument
       _cbt.adjustForDelete(0, 0, cpMin);
       _pbt.adjustForDelete(0, 0, cpMin);
     }
+    
+    // Read FSPA and Escher information
+    _fspa = new FSPATable(_tableStream, _fib.getFcPlcspaMom(), _fib.getLcbPlcspaMom(), getTextTable().getTextPieces());
+    
+    if (_fib.getFcDggInfo() != 0)
+    {
+        _dgg = new EscherRecordHolder(_tableStream, _fib.getFcDggInfo(), _fib.getLcbDggInfo());
+    } else
+    {
+        _dgg = new EscherRecordHolder();
+    }
+    
+    // read in the pictures stream
+    _pictures = new PicturesTable(this, _dataStream, _mainStream, _fspa, _dgg);
 
     _st = new SectionTable(_mainStream, _tableStream, _fib.getFcPlcfsed(), _fib.getLcbPlcfsed(), fcMin, getTextTable().getTextPieces());
     _ss = new StyleSheet(_tableStream, _fib.getFcStshf());
diff --git a/src/scratchpad/src/org/apache/poi/hwpf/model/EscherRecordHolder.java b/src/scratchpad/src/org/apache/poi/hwpf/model/EscherRecordHolder.java
new file mode 100644 (file)
index 0000000..4242dd4
--- /dev/null
@@ -0,0 +1,116 @@
+/*
+ * To change this template, choose Tools | Templates
+ * and open the template in the editor.
+ */
+
+package org.apache.poi.hwpf.model;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+import org.apache.poi.ddf.DefaultEscherRecordFactory;
+import org.apache.poi.ddf.EscherContainerRecord;
+import org.apache.poi.ddf.EscherRecord;
+import org.apache.poi.ddf.EscherRecordFactory;
+
+/**
+ * Based on AbstractEscherRecordHolder fomr HSSF.
+ * 
+ * @author Squeeself
+ */
+public class EscherRecordHolder 
+{
+    protected ArrayList escherRecords = new ArrayList();
+    
+    public EscherRecordHolder()
+    {
+        
+    }
+    
+    public EscherRecordHolder(byte[] data, int offset, int size)
+    {
+        fillEscherRecords(data, offset, size);
+    }
+    
+    private void fillEscherRecords(byte[] data, int offset, int size)
+    {
+        EscherRecordFactory recordFactory = new DefaultEscherRecordFactory();
+        int pos = offset;
+        while ( pos < offset + size)
+        {
+            EscherRecord r = recordFactory.createRecord(data, pos);
+            escherRecords.add(r);
+            int bytesRead = r.fillFields(data, pos, recordFactory);
+            pos += bytesRead + 1; // There is an empty byte between each top-level record in a Word doc
+        }
+    }
+    
+    public List getEscherRecords()
+    {
+        return escherRecords;
+    }
+    
+    public String toString()
+    {
+        StringBuffer buffer = new StringBuffer();
+
+        final String nl = System.getProperty("line.separator");
+        if (escherRecords.size() == 0)
+            buffer.append("No Escher Records Decoded" + nl);
+        for ( Iterator iterator = escherRecords.iterator(); iterator.hasNext(); )
+        {
+            EscherRecord r = (EscherRecord) iterator.next();
+            buffer.append(r.toString());
+        }
+
+        return buffer.toString();
+    }
+    
+    /**
+     * If we have a EscherContainerRecord as one of our
+     *  children (and most top level escher holders do),
+     *  then return that.
+     */
+    public EscherContainerRecord getEscherContainer() {
+       for(Iterator it = escherRecords.iterator(); it.hasNext();) {
+               Object er = it.next();
+               if(er instanceof EscherContainerRecord) {
+                       return (EscherContainerRecord)er;
+               }
+       }
+       return null;
+    }
+
+    /**
+     * Descends into all our children, returning the
+     *  first EscherRecord with the given id, or null
+     *  if none found
+     */
+    public EscherRecord findFirstWithId(short id) {
+       return findFirstWithId(id, getEscherRecords());
+    }
+    private EscherRecord findFirstWithId(short id, List records) {
+       // Check at our level
+       for(Iterator it = records.iterator(); it.hasNext();) {
+               EscherRecord r = (EscherRecord)it.next();
+               if(r.getRecordId() == id) {
+                       return r;
+               }
+       }
+       
+       // Then check our children in turn
+       for(Iterator it = records.iterator(); it.hasNext();) {
+               EscherRecord r = (EscherRecord)it.next();
+               if(r.isContainerRecord()) {
+                       EscherRecord found =
+                               findFirstWithId(id, r.getChildRecords());
+                       if(found != null) {
+                               return found;
+                       }
+               }
+       }
+       
+       // Not found in this lot
+       return null;
+    }
+}
diff --git a/src/scratchpad/src/org/apache/poi/hwpf/model/FSPA.java b/src/scratchpad/src/org/apache/poi/hwpf/model/FSPA.java
new file mode 100644 (file)
index 0000000..bb3d5de
--- /dev/null
@@ -0,0 +1,182 @@
+/* ====================================================================
+   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.
+==================================================================== */
+
+package org.apache.poi.hwpf.model;
+
+import org.apache.poi.util.BitField;
+import org.apache.poi.util.BitFieldFactory;
+import org.apache.poi.util.LittleEndian;
+
+/**
+ * File Shape Address structure
+ * 
+ * @author Squeeself
+ */
+public class FSPA 
+{
+    public static final int FSPA_SIZE = 26;
+    private int spid; // Shape identifier. Used to get data position
+    private int xaLeft; // Enclosing rectangle
+    private int yaTop; // Enclosing rectangle
+    private int xaRight; // Enclosing rectangle
+    private int yaBottom; // Enclosing rectangle
+    private short options;
+        private static BitField fHdr = BitFieldFactory.getInstance(0x0001); // 1 in undo when in header
+        private static BitField bx = BitFieldFactory.getInstance(0x0006); // x pos relative to anchor CP: 0 - page margin, 1 - top of page, 2 - text, 3 - reserved
+        private static BitField by = BitFieldFactory.getInstance(0x0018); // y pos relative to anchor CP: ditto
+        private static BitField wr = BitFieldFactory.getInstance(0x01E0); // Text wrapping mode: 0 - like 2 w/o absolute, 1 - no text next to shape, 2 - wrap around absolute object, 3 - wrap as if no object, 4 - wrap tightly around object, 5 - wrap tightly, allow holes, 6-15 - reserved
+        private static BitField wrk = BitFieldFactory.getInstance(0x1E00); // Text wrapping mode type (for modes 2&4): 0 - wrap both sides, 1 - wrap only left, 2 - wrap only right, 3 - wrap largest side
+        private static BitField fRcaSimple = BitFieldFactory.getInstance(0x2000); // Overwrites bx if set, forcing rectangle to be page relative
+        private static BitField fBelowText = BitFieldFactory.getInstance(0x4000); // if true, shape is below text, otherwise above
+        private static BitField fAnchorLock = BitFieldFactory.getInstance(0x8000); // if true, anchor is locked
+    private int cTxbx; // Count of textboxes in shape (undo doc only)
+    
+    public FSPA()
+    {
+    }
+    
+    public FSPA(byte[] bytes, int offset)
+    {
+        spid = LittleEndian.getInt(bytes, offset);
+        offset += LittleEndian.INT_SIZE;
+        xaLeft = LittleEndian.getInt(bytes, offset);
+        offset += LittleEndian.INT_SIZE;
+        yaTop = LittleEndian.getInt(bytes, offset);
+        offset += LittleEndian.INT_SIZE;
+        xaRight = LittleEndian.getInt(bytes, offset);
+        offset += LittleEndian.INT_SIZE;
+        yaBottom = LittleEndian.getInt(bytes, offset);
+        offset += LittleEndian.INT_SIZE;
+        options = LittleEndian.getShort(bytes, offset);
+        offset += LittleEndian.SHORT_SIZE;
+        cTxbx = LittleEndian.getInt(bytes, offset);
+    }
+    
+    public int getSpid()
+    {
+        return spid;
+    }
+    
+    public int getXaLeft()
+    {
+        return xaLeft;
+    }
+    
+    public int getYaTop()
+    {
+        return yaTop;
+    }
+    
+    public int getXaRight()
+    {
+        return xaRight;
+    }
+    
+    public int getYaBottom()
+    {
+        return yaBottom;
+    }
+    
+    public boolean isFHdr()
+    {
+        return fHdr.isSet(options);
+    }
+    
+    public short getBx()
+    {
+        return bx.getShortValue(options);
+    }
+    
+    public short getBy()
+    {
+        return by.getShortValue(options);
+    }
+    
+    public short getWr()
+    {
+        return wr.getShortValue(options);
+    }
+    
+    public short getWrk()
+    {
+        return wrk.getShortValue(options);
+    }
+    
+    public boolean isFRcaSimple()
+    {
+        return fRcaSimple.isSet(options);
+    }
+    
+    public boolean isFBelowText()
+    {
+        return fBelowText.isSet(options);
+    }
+    
+    public boolean isFAnchorLock()
+    {
+        return fAnchorLock.isSet(options);
+    }
+    
+    public int getCTxbx()
+    {
+        return cTxbx;
+    }
+    
+    public byte[] toByteArray()
+    {
+        int offset = 0;
+        byte[] buf = new byte[FSPA_SIZE];
+
+        LittleEndian.putInt(buf, offset, spid);
+        offset += LittleEndian.INT_SIZE;
+        LittleEndian.putInt(buf, offset, xaLeft);
+        offset += LittleEndian.INT_SIZE;
+        LittleEndian.putInt(buf, offset, yaTop);
+        offset += LittleEndian.INT_SIZE;
+        LittleEndian.putInt(buf, offset, xaRight);
+        offset += LittleEndian.INT_SIZE;
+        LittleEndian.putInt(buf, offset, yaBottom);
+        offset += LittleEndian.INT_SIZE;
+        LittleEndian.putShort(buf, offset, options);
+        offset += LittleEndian.SHORT_SIZE;
+        LittleEndian.putInt(buf, offset, cTxbx);
+        offset += LittleEndian.INT_SIZE;
+
+        return buf;
+    }
+    
+    public String toString()
+    {
+        StringBuffer buf = new StringBuffer();
+        buf.append("spid: ").append(spid);
+        buf.append(", xaLeft: ").append(xaLeft);
+        buf.append(", yaTop: ").append(yaTop);
+        buf.append(", xaRight: ").append(xaRight);
+        buf.append(", yaBottom: ").append(yaBottom);
+        buf.append(", options: ").append(options);
+            buf.append(" (fHdr: ").append(isFHdr());
+            buf.append(", bx: ").append(getBx());
+            buf.append(", by: ").append(getBy());
+            buf.append(", wr: ").append(getWr());
+            buf.append(", wrk: ").append(getWrk());
+            buf.append(", fRcaSimple: ").append(isFRcaSimple());
+            buf.append(", fBelowText: ").append(isFBelowText());
+            buf.append(", fAnchorLock: ").append(isFAnchorLock());
+        buf.append("), cTxbx: ").append(cTxbx);
+        return buf.toString();
+    }
+}
diff --git a/src/scratchpad/src/org/apache/poi/hwpf/model/FSPATable.java b/src/scratchpad/src/org/apache/poi/hwpf/model/FSPATable.java
new file mode 100644 (file)
index 0000000..58c69ff
--- /dev/null
@@ -0,0 +1,82 @@
+/* ====================================================================
+   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.
+==================================================================== */
+
+package org.apache.poi.hwpf.model;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+
+/**
+ * This class holds all the FSPA (File Shape Address) structures.
+ * 
+ * @author Squeeself
+ */
+public class FSPATable 
+{
+    protected ArrayList shapes = new ArrayList();
+    protected HashMap cps = new HashMap();
+    protected List _text;
+    
+    public FSPATable(byte[] tableStream, int fcPlcspa, int lcbPlcspa, List tpt)
+    {
+        _text = tpt;
+        // Will be 0 if no drawing objects in document
+        if (fcPlcspa == 0)
+            return;
+        
+        PlexOfCps plex = new PlexOfCps(tableStream, fcPlcspa, lcbPlcspa, FSPA.FSPA_SIZE);
+        for (int i=0; i < plex.length(); i++)
+        {
+            GenericPropertyNode property = plex.getProperty(i);
+            FSPA fspa = new FSPA(property.getBytes(), 0);
+            
+            shapes.add(fspa);
+            cps.put(Integer.valueOf(property.getStart()), Integer.valueOf(i));
+        }
+    }
+    
+    public FSPA getFspaFromCp(int cp)
+    {
+        Integer idx = (Integer)cps.get(Integer.valueOf(cp));
+        if (idx == null)
+            return null;
+        return (FSPA)shapes.get(idx.intValue());
+    }
+    
+    public List getShapes()
+    {
+        return shapes;
+    }
+    
+    public String toString()
+    {
+        StringBuffer buf = new StringBuffer();
+        buf.append("[FPSA PLC size=").append(shapes.size()).append("]\n");
+        for (Iterator it = cps.keySet().iterator(); it.hasNext(); )
+        {
+            Integer i = (Integer) it.next();
+            FSPA fspa = (FSPA) shapes.get(((Integer)cps.get(i)).intValue());
+            buf.append("  [FC: ").append(i.toString()).append("] ");
+            buf.append(fspa.toString());
+            buf.append("\n");
+        }
+        buf.append("[/FSPA PLC]");
+        return buf.toString();
+    }
+}
index ee8f9724e9fcd4977b3ba0adbd3d714faf7a5dda..48e6d78b3a9852b7a654ccc8432daa6f8d44eb00 100644 (file)
@@ -308,6 +308,26 @@ public class FileInformationBlock extends FIBAbstractType
     {
       return _fieldHandler.getFieldSize(FIBFieldHandler.PLCFFLDMOM);
     }
+    
+    public int getFcPlcspaMom()
+    {
+        return _fieldHandler.getFieldOffset(FIBFieldHandler.PLCSPAMOM);
+    }
+    
+    public int getLcbPlcspaMom()
+    {
+        return _fieldHandler.getFieldSize(FIBFieldHandler.PLCSPAMOM);
+    }
+    
+    public int getFcDggInfo()
+    {
+        return _fieldHandler.getFieldOffset(FIBFieldHandler.DGGINFO);
+    }
+    
+    public int getLcbDggInfo()
+    {
+        return _fieldHandler.getFieldSize(FIBFieldHandler.DGGINFO);
+    }
 
     public void writeTo (byte[] mainStream, HWPFOutputStream tableStream)
       throws IOException
index d9598b1061cefad7b934deaf5fe0cff0c1b8a0b3..9681741d94256ad2ffd9bec1344a6a89c83e6fcc 100644 (file)
@@ -26,7 +26,12 @@ import org.apache.poi.hwpf.usermodel.Range;
 
 import java.util.List;
 import java.util.ArrayList;
-
+import java.util.Iterator;
+import org.apache.poi.ddf.DefaultEscherRecordFactory;
+import org.apache.poi.ddf.EscherBSERecord;
+import org.apache.poi.ddf.EscherBlipRecord;
+import org.apache.poi.ddf.EscherRecord;
+import org.apache.poi.ddf.EscherRecordFactory;
 
 /**
  * Holds information about all pictures embedded in Word Document either via "Insert -> Picture -> From File" or via
@@ -57,6 +62,9 @@ public class PicturesTable
 
   private HWPFDocument _document;
   private byte[] _dataStream;
+  private byte[] _mainStream;
+  private FSPATable _fspa;
+  private EscherRecordHolder _dgg;
 
   /** @link dependency
    * @stereotype instantiate*/
@@ -67,10 +75,13 @@ public class PicturesTable
    * @param document 
    * @param _dataStream
    */
-  public PicturesTable(HWPFDocument _document, byte[] _dataStream)
+  public PicturesTable(HWPFDocument _document, byte[] _dataStream, byte[] _mainStream, FSPATable fspa, EscherRecordHolder dgg)
   {
-       this._document = _document;
+    this._document = _document;
     this._dataStream = _dataStream;
+    this._mainStream = _mainStream;
+    this._fspa = fspa;
+    this._dgg = dgg;
   }
 
   /**
@@ -83,6 +94,13 @@ public class PicturesTable
     }
     return false;
   }
+  
+  public boolean hasEscherPicture(CharacterRun run) {
+    if (run.isSpecialCharacter() && !run.isObj() && !run.isOle2() && !run.isData() && run.text().startsWith("\u0008")) {
+      return true;
+    }
+    return false;
+  }
 
   /**
    * determines whether specified CharacterRun contains reference to a picture
@@ -122,6 +140,46 @@ public class PicturesTable
     }
     return null;
   }
+  
+  /**
+     * Performs a recursive search for pictures in the given list of escher records.
+     *
+     * @param escherRecords the escher records.
+     * @param pictures the list to populate with the pictures.
+     */
+    private void searchForPictures(List escherRecords, List pictures)
+    {
+        Iterator recordIter = escherRecords.iterator();
+        while (recordIter.hasNext())
+        {
+            Object obj = recordIter.next();
+            if (obj instanceof EscherRecord)
+            {
+                EscherRecord escherRecord = (EscherRecord) obj;
+
+                if (escherRecord instanceof EscherBSERecord)
+                {
+                    EscherBSERecord bse = (EscherBSERecord) escherRecord;
+                    EscherBlipRecord blip = bse.getBlipRecord();
+                    if (blip != null)
+                    {
+                        pictures.add(new Picture(blip.getPicturedata()));
+                    }
+                    else if (bse.getOffset() > 0)
+                    {
+                        // Blip stored in delay stream, which in a word doc, is the main stream
+                        EscherRecordFactory recordFactory = new DefaultEscherRecordFactory();
+                        blip = (EscherBlipRecord) recordFactory.createRecord(_mainStream, bse.getOffset());
+                        blip.fillFields(_mainStream, bse.getOffset(), recordFactory);
+                        pictures.add(new Picture(blip.getPicturedata()));
+                    }
+                }
+
+                // Recursive call.
+                searchForPictures(escherRecord.getChildRecords(), pictures);
+            }
+        }
+    }
 
   /**
    * Not all documents have all the images concatenated in the data stream
@@ -136,12 +194,13 @@ public class PicturesTable
     for (int i = 0; i < range.numCharacterRuns(); i++) {
        CharacterRun run = range.getCharacterRun(i);
        String text = run.text();
-       int j = text.charAt(0);
        Picture picture = extractPicture(run, false);
        if (picture != null) {
                pictures.add(picture);
        }
        }
+    
+    searchForPictures(_dgg.getEscherRecords(), pictures);
 
     return pictures;
   }
index e9ee228c12693e1b6934e41dbee937d6f5c38250..8a3737865bbf408e7f8f2ff0b1a96df36be0c3cf 100644 (file)
@@ -98,6 +98,15 @@ public class Picture
       fillImageContent();
     }
   }
+  
+  public Picture(byte[] _dataStream)
+  {
+      this._dataStream = _dataStream;
+      this.dataBlockStartOfsset = 0;
+      this.dataBlockSize = _dataStream.length;
+      this.pictureBytesStartOffset = 0;
+      this.size = _dataStream.length;
+  }
 
   private void fillWidthHeight()
   {
@@ -363,6 +372,7 @@ public class Picture
       do {
         firstByte = _dataStream[pointer];
         secondByte = _dataStream[pointer+1];
+        pointer += 2;
       } while (!(firstByte==(byte)0xFF) && pointer<endOfPicture-1);
 
       if (firstByte==((byte)0xFF) && pointer<endOfPicture-1) {
index bf867c40d410cb389fa607120da028e1aebd3650..bcf02a8a1da3c6b3f74ac686fc53f4e932bef3de 100644 (file)
@@ -35,10 +35,12 @@ public class TestHWPFPictures extends TestCase {
        private String docAFile;
        private String docBFile;
        private String docCFile;
+    private String docDFile;
        
        private String imgAFile;
        private String imgBFile;
        private String imgCFile;
+    private String imgDFile;
        
        protected void setUp() throws Exception {
                String dirname = System.getProperty("HWPF.testdata.path");
@@ -46,10 +48,12 @@ public class TestHWPFPictures extends TestCase {
                docAFile = dirname + "/testPictures.doc";
                docBFile = dirname + "/two_images.doc";
                docCFile = dirname + "/vector_image.doc";
+        docDFile = dirname + "/GaiaTest.doc";
                
                imgAFile = dirname + "/simple_image.jpg";
                imgBFile = dirname + "/simple_image.png";
                imgCFile = dirname + "/vector_image.emf";
+        imgDFile = dirname + "/GaiaTestImg.png";
        }
        
        /**
@@ -126,7 +130,26 @@ public class TestHWPFPictures extends TestCase {
                assertEquals(picBytes.length, pic.getContent().length);
                assertBytesSame(picBytes, pic.getContent());
        }
-       
+    
+       /**
+        * Pending the missing files being uploaded to
+        *  bug #44937
+        */
+    public void BROKENtestEscherDrawing() throws Exception
+    {
+        HWPFDocument docD = new HWPFDocument(new FileInputStream(docDFile));
+        List allPictures = docD.getPicturesTable().getAllPictures();
+        
+        assertEquals(1, allPictures.size());
+        
+        Picture pic = (Picture) allPictures.get(0);
+        assertNotNull(pic);
+        byte[] picD = readFile(imgDFile);
+        
+        assertEquals(picD.length, pic.getContent().length);
+        
+        assertBytesSame(picD, pic.getContent());
+    }
        
        private void assertBytesSame(byte[] a, byte[] b) {
                assertEquals(a.length, b.length);
index 714c0358ff7d9665bfbe437cc2cd9b12a64a34f4..7a9acb087b2f231ac8c625904cbdc4d55eec31db 100644 (file)
@@ -38,6 +38,8 @@ public final class TestExternalNameRecord extends TestCase {
        // data taken from bugzilla 44774 att 21790\r
        private static final byte[] dataPlainName = {\r
                0, 0, 0, 0, 0, 0, 9, 0, 82, 97, 116, 101, 95, 68, 97, 116, 101, 9, 0, 58, 0, 0, 0, 0, 4, 0, 8, 0\r
+               // TODO - the last 2 bytes of formula data (8,0) seem weird.  They encode to ConcatPtg, UnknownPtg\r
+               // UnknownPtg is otherwise not created by any other test cases\r
        };\r
        \r
        private static ExternalNameRecord createSimpleENR(byte[] data) {\r
index abe6943c799927b2396fab1e4a4a6ce7338a0d82..4f5e390873082dec5276edb73414153127434bf3 100644 (file)
@@ -19,16 +19,14 @@ package org.apache.poi.hssf.record;
 
 
 import java.io.ByteArrayInputStream;
+import java.util.List;
+
+import junit.framework.TestCase;
 
 import org.apache.poi.hssf.record.formula.AttrPtg;
-import org.apache.poi.hssf.record.formula.ConcatPtg;
 import org.apache.poi.hssf.record.formula.FuncVarPtg;
 import org.apache.poi.hssf.record.formula.IntPtg;
-import org.apache.poi.hssf.record.formula.RangePtg;
 import org.apache.poi.hssf.record.formula.ReferencePtg;
-import org.apache.poi.hssf.record.formula.UnknownPtg;
-
-import junit.framework.TestCase;
 
 /**
  * Tests the serialization and deserialization of the FormulaRecord
@@ -57,7 +55,7 @@ public final class TestFormulaRecord extends TestCase {
         */
        public void testCheckNanPreserve() {
                byte[] formulaByte = new byte[29];
-               for (int i = 0; i < formulaByte.length; i++) formulaByte[i] = (byte)0;
+
                formulaByte[4] = (byte)0x0F;
                formulaByte[6] = (byte)0x02;
                formulaByte[8] = (byte)0x07;
@@ -91,8 +89,6 @@ public final class TestFormulaRecord extends TestCase {
        public void testExpFormula() {
                byte[] formulaByte = new byte[27];
                
-               for (int i = 0; i < formulaByte.length; i++) formulaByte[i] = (byte)0;
-               
                formulaByte[4] =(byte)0x0F;
                formulaByte[14]=(byte)0x08;
                formulaByte[18]=(byte)0xE0;
@@ -109,15 +105,14 @@ public final class TestFormulaRecord extends TestCase {
        
        public void testWithConcat()  throws Exception {
                // =CHOOSE(2,A2,A3,A4)
-               byte[] data = new byte[] {
+               byte[] data = {
                                6, 0, 68, 0,
                                1, 0, 1, 0, 15, 0, 0, 0, 0, 0, 0, 0, 57,
                                64, 0, 0, 12, 0, 12, -4, 46, 0, 
                                30, 2, 0,       // Int - 2
                                25, 4, 3, 0, // Attr
-                               8, 0,           // Concat 
-                               17, 0,     // Range 
-                               26, 0, 35, 0, // Bit like an attr
+                                       8, 0, 17, 0, 26, 0, // jumpTable
+                                       35, 0, // chooseOffset
                                36, 1, 0, 0, -64, // Ref - A2
                                25, 8, 21, 0, // Attr
                                36, 2, 0, 0, -64, // Ref - A3
@@ -126,30 +121,24 @@ public final class TestFormulaRecord extends TestCase {
                                25, 8, 3, 0,  // Attr 
                                66, 4, 100, 0 // CHOOSE
                };
-               RecordInputStream inp = new RecordInputStream(
-                               new ByteArrayInputStream(data)
-               );
+               RecordInputStream inp = new RecordInputStream( new ByteArrayInputStream(data));
                inp.nextRecord();
                
                FormulaRecord fr = new FormulaRecord(inp);
                
-               assertEquals(14, fr.getNumberOfExpressionTokens());
-               assertEquals(IntPtg.class,         fr.getParsedExpression().get(0).getClass());
-               assertEquals(AttrPtg.class,       fr.getParsedExpression().get(1).getClass());
-               assertEquals(ConcatPtg.class,   fr.getParsedExpression().get(2).getClass());
-               assertEquals(UnknownPtg.class,   fr.getParsedExpression().get(3).getClass());
-               assertEquals(RangePtg.class,     fr.getParsedExpression().get(4).getClass());
-               assertEquals(UnknownPtg.class,   fr.getParsedExpression().get(5).getClass());
-               assertEquals(AttrPtg.class,       fr.getParsedExpression().get(6).getClass());
-               assertEquals(ReferencePtg.class, fr.getParsedExpression().get(7).getClass());
-               assertEquals(AttrPtg.class,       fr.getParsedExpression().get(8).getClass());
-               assertEquals(ReferencePtg.class, fr.getParsedExpression().get(9).getClass());
-               assertEquals(AttrPtg.class,       fr.getParsedExpression().get(10).getClass());
-               assertEquals(ReferencePtg.class, fr.getParsedExpression().get(11).getClass());
-               assertEquals(AttrPtg.class,       fr.getParsedExpression().get(12).getClass());
-               assertEquals(FuncVarPtg.class,   fr.getParsedExpression().get(13).getClass());
+               List ptgs = fr.getParsedExpression();
+               assertEquals(9, ptgs.size());
+               assertEquals(IntPtg.class,         ptgs.get(0).getClass());
+               assertEquals(AttrPtg.class,       ptgs.get(1).getClass());
+               assertEquals(ReferencePtg.class, ptgs.get(2).getClass());
+               assertEquals(AttrPtg.class,       ptgs.get(3).getClass());
+               assertEquals(ReferencePtg.class, ptgs.get(4).getClass());
+               assertEquals(AttrPtg.class,       ptgs.get(5).getClass());
+               assertEquals(ReferencePtg.class, ptgs.get(6).getClass());
+               assertEquals(AttrPtg.class,       ptgs.get(7).getClass());
+               assertEquals(FuncVarPtg.class,   ptgs.get(8).getClass());
                
-               FuncVarPtg choose = (FuncVarPtg)fr.getParsedExpression().get(13);
+               FuncVarPtg choose = (FuncVarPtg)ptgs.get(8);
                assertEquals("CHOOSE", choose.getName());
        }
        
index 0a55d6508768c4d79ee8b1c2c7ae25d4d0a38e01..0e0a656341640409cae9f68963fd087a90fe5337 100644 (file)
 
 package org.apache.poi.hssf.usermodel;
 
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.util.Iterator;
+
 import junit.framework.AssertionFailedError;
 import junit.framework.TestCase;
 
 import org.apache.poi.ss.util.Region;
 
 import org.apache.poi.hssf.HSSFTestDataSamples;
-import org.apache.poi.hssf.record.RecordFormatException;
 import org.apache.poi.util.TempFile;
 
-import java.io.*;
-import java.util.Iterator;
-
 /**
  * Testcases for bugs entered in bugzilla
  * the Test name contains the bugzilla bug id
@@ -81,13 +82,7 @@ public final class TestBugs extends TestCase {
         HSSFRow r = s.createRow(0);
         HSSFCell c = r.createCell((short)0);
         c.setCellValue(10);
-        try {
-            writeOutAndReadBack(wb);
-        } catch (RecordFormatException e) {
-            if (false) { // TODO (Apr-2008) this file does not read back ok.  create bugzilla bug & fix.
-                throw new AssertionFailedError("Identified bug XXXX");
-            }
-        }
+        writeOutAndReadBack(wb);
     }
     /**Test writing a hyperlink
      * Open resulting sheet in Excel and check that A1 contains a hyperlink*/
@@ -758,9 +753,13 @@ public final class TestBugs extends TestCase {
         HSSFCell c2 = r2.getCell((short)1);
         assertEquals(25, (int)c2.getNumericCellValue());
 
-        if (false) { // TODO (Apr-2008) This will blow up with IllegalStateException (stack underflow)
-            // excel function "CHOOSE" probably needs some special handling in FormulaParser.toFormulaString()
-            assertEquals("=CHOOSE(2,A2,A3,A4)", c2.getCellFormula());
+        try {
+            assertEquals("CHOOSE(2,A2,A3,A4)", c2.getCellFormula());
+        } catch (IllegalStateException e) {
+            if (e.getMessage().startsWith("Too few arguments")
+                    && e.getMessage().indexOf("ConcatPtg") > 0) {
+                throw new AssertionFailedError("identified bug 44306");
+            }
         }
     }
 
@@ -888,13 +887,13 @@ public final class TestBugs extends TestCase {
         writeOutAndReadBack(wb);
         assertTrue("no errors writing sample xls", true);
     }
-    
+
     /**
      * Had a problem apparently, not sure what as it
      *  works just fine...
      */
     public void test44891() throws Exception {
-       HSSFWorkbook wb = openSample("44891.xls");
+        HSSFWorkbook wb = openSample("44891.xls");
         assertTrue("no errors reading sample xls", true);
         writeOutAndReadBack(wb);
         assertTrue("no errors writing sample xls", true);
@@ -906,7 +905,7 @@ public final class TestBugs extends TestCase {
      * Works fine with poi-3.1-beta1.
      */
     public void test44235() throws Exception {
-       HSSFWorkbook wb = openSample("44235.xls");
+        HSSFWorkbook wb = openSample("44235.xls");
         assertTrue("no errors reading sample xls", true);
         writeOutAndReadBack(wb);
         assertTrue("no errors writing sample xls", true);
@@ -930,7 +929,7 @@ public final class TestBugs extends TestCase {
     }
 
     public void test36947() throws Exception {
-       HSSFWorkbook wb = openSample("36947.xls");
+        HSSFWorkbook wb = openSample("36947.xls");
         assertTrue("no errors reading sample xls", true);
         writeOutAndReadBack(wb);
         assertTrue("no errors writing sample xls", true);
@@ -947,7 +946,7 @@ public final class TestBugs extends TestCase {
     }
 
     public void test39634() throws Exception {
-       HSSFWorkbook wb = openSample("39634.xls");
+        HSSFWorkbook wb = openSample("39634.xls");
         assertTrue("no errors reading sample xls", true);
         writeOutAndReadBack(wb);
         assertTrue("no errors writing sample xls", true);
index 7611abb51ce8bc3e8ec0f93449af87e0fb715a17..d307a0e2514e4c352bc9b4bf3b845fa04e1ba741 100644 (file)
@@ -204,4 +204,64 @@ public final class TestHSSFRow extends TestCase {
         row.createCell((short) 255);
         assertEquals(256, row.getLastCellNum());
     }
+    
+    /**
+     * Tests for the missing/blank cell policy stuff
+     */
+    public void testGetCellPolicy() throws Exception {
+        HSSFWorkbook book = new HSSFWorkbook();
+        HSSFSheet sheet = book.createSheet("test");
+        HSSFRow row = sheet.createRow(0);
+
+        // 0 -> string
+        // 1 -> num
+        // 2 missing
+        // 3 missing
+        // 4 -> blank
+        // 5 -> num
+        row.createCell((short)0).setCellValue(new HSSFRichTextString("test"));
+        row.createCell((short)1).setCellValue(3.2);
+        row.createCell((short)4, HSSFCell.CELL_TYPE_BLANK);
+        row.createCell((short)5).setCellValue(4);
+        
+        // First up, no policy
+        assertEquals(HSSFCell.CELL_TYPE_STRING,  row.getCell(0).getCellType());
+        assertEquals(HSSFCell.CELL_TYPE_NUMERIC, row.getCell(1).getCellType());
+        assertEquals(null, row.getCell(2));
+        assertEquals(null, row.getCell(3));
+        assertEquals(HSSFCell.CELL_TYPE_BLANK,   row.getCell(4).getCellType());
+        assertEquals(HSSFCell.CELL_TYPE_NUMERIC, row.getCell(5).getCellType());
+        
+        // RETURN_NULL_AND_BLANK - same as default
+        assertEquals(HSSFCell.CELL_TYPE_STRING,  row.getCell(0, HSSFRow.RETURN_NULL_AND_BLANK).getCellType());
+        assertEquals(HSSFCell.CELL_TYPE_NUMERIC, row.getCell(1, HSSFRow.RETURN_NULL_AND_BLANK).getCellType());
+        assertEquals(null, row.getCell(2, HSSFRow.RETURN_NULL_AND_BLANK));
+        assertEquals(null, row.getCell(3, HSSFRow.RETURN_NULL_AND_BLANK));
+        assertEquals(HSSFCell.CELL_TYPE_BLANK,   row.getCell(4, HSSFRow.RETURN_NULL_AND_BLANK).getCellType());
+        assertEquals(HSSFCell.CELL_TYPE_NUMERIC, row.getCell(5, HSSFRow.RETURN_NULL_AND_BLANK).getCellType());
+        
+        // RETURN_BLANK_AS_NULL - nearly the same
+        assertEquals(HSSFCell.CELL_TYPE_STRING,  row.getCell(0, HSSFRow.RETURN_BLANK_AS_NULL).getCellType());
+        assertEquals(HSSFCell.CELL_TYPE_NUMERIC, row.getCell(1, HSSFRow.RETURN_BLANK_AS_NULL).getCellType());
+        assertEquals(null, row.getCell(2, HSSFRow.RETURN_BLANK_AS_NULL));
+        assertEquals(null, row.getCell(3, HSSFRow.RETURN_BLANK_AS_NULL));
+        assertEquals(null, row.getCell(4, HSSFRow.RETURN_BLANK_AS_NULL));
+        assertEquals(HSSFCell.CELL_TYPE_NUMERIC, row.getCell(5, HSSFRow.RETURN_BLANK_AS_NULL).getCellType());
+        
+        // CREATE_NULL_AS_BLANK - creates as needed
+        assertEquals(HSSFCell.CELL_TYPE_STRING,  row.getCell(0, HSSFRow.CREATE_NULL_AS_BLANK).getCellType());
+        assertEquals(HSSFCell.CELL_TYPE_NUMERIC, row.getCell(1, HSSFRow.CREATE_NULL_AS_BLANK).getCellType());
+        assertEquals(HSSFCell.CELL_TYPE_BLANK,   row.getCell(2, HSSFRow.CREATE_NULL_AS_BLANK).getCellType());
+        assertEquals(HSSFCell.CELL_TYPE_BLANK,   row.getCell(3, HSSFRow.CREATE_NULL_AS_BLANK).getCellType());
+        assertEquals(HSSFCell.CELL_TYPE_BLANK,   row.getCell(4, HSSFRow.CREATE_NULL_AS_BLANK).getCellType());
+        assertEquals(HSSFCell.CELL_TYPE_NUMERIC, row.getCell(5, HSSFRow.CREATE_NULL_AS_BLANK).getCellType());
+        
+        // Check created ones get the right column
+        assertEquals((short)0, row.getCell(0, HSSFRow.CREATE_NULL_AS_BLANK).getCellNum());
+        assertEquals((short)1, row.getCell(1, HSSFRow.CREATE_NULL_AS_BLANK).getCellNum());
+        assertEquals((short)2, row.getCell(2, HSSFRow.CREATE_NULL_AS_BLANK).getCellNum());
+        assertEquals((short)3, row.getCell(3, HSSFRow.CREATE_NULL_AS_BLANK).getCellNum());
+        assertEquals((short)4, row.getCell(4, HSSFRow.CREATE_NULL_AS_BLANK).getCellNum());
+        assertEquals((short)5, row.getCell(5, HSSFRow.CREATE_NULL_AS_BLANK).getCellNum());
+    }
 }
index 1cde86918b9623a6105ff51ff1f187d69b96bc6a..4a948ba8c60d9e15d380fefd3eed14ddec5727ad 100755 (executable)
@@ -130,7 +130,7 @@ public final class TestPOIFSFileSystem extends TestCase {
         * The other is to fix the handling of the last block in
         *  POIFS, since it seems to be slight wrong
         */
-       public void DISABLEDtestShortLastBlock() throws Exception {
+       public void testShortLastBlock() throws Exception {
                String[] files = new String[] {
                        "ShortLastBlock.qwp", "ShortLastBlock.wps"      
                };