diff --git a/src/documentation/content/xdocs/changes.xml b/src/documentation/content/xdocs/changes.xml
index 380ecb923c..b84dec3344 100644
--- a/src/documentation/content/xdocs/changes.xml
+++ b/src/documentation/content/xdocs/changes.xml
@@ -43,7 +43,14 @@
Created a common interface for handling PowerPoint files, irrespective of if they are .ppt or .pptx
Created a common interface for handling Excel files, irrespective of if they are .xls or .xlsx
-
+
+ Support for specifying a policy to HSSF on missing / blank cells when fetching
+ 44937 - Partial support for extracting Escher images from HWPF files
+ 44824 - Avoid an infinite loop when reading some HWPF pictures
+ 44898 - Correctly handle short last blocks in POIFS
+
+
+ 44306 - fixed reading/writing of AttrPtg(type=choose) and method toFormulaString() for CHOOSE formulas
24207 - added HSSFName.isDeleted() to check if the name points to cell that no longer exists
40414 - fixed selected/active sheet after removing sheet from workbook
44523 - fixed workbook sheet selection and focus
diff --git a/src/documentation/content/xdocs/status.xml b/src/documentation/content/xdocs/status.xml
index ebf869ce95..0b7f0140d4 100644
--- a/src/documentation/content/xdocs/status.xml
+++ b/src/documentation/content/xdocs/status.xml
@@ -40,7 +40,14 @@
Created a common interface for handling PowerPoint files, irrespective of if they are .ppt or .pptx
Created a common interface for handling Excel files, irrespective of if they are .xls or .xlsx
-
+
+ Support for specifying a policy to HSSF on missing / blank cells when fetching
+ 44937 - Partial support for extracting Escher images from HWPF files
+ 44824 - Avoid an infinite loop when reading some HWPF pictures
+ 44898 - Correctly handle short last blocks in POIFS
+
+
+ 44306 - fixed reading/writing of AttrPtg(type=choose) and method toFormulaString() for CHOOSE formulas
24207 - added HSSFName.isDeleted() to check if the name points to cell that no longer exists
40414 - fixed selected/active sheet after removing sheet from workbook
44523 - fixed workbook sheet selection and focus
diff --git a/src/java/org/apache/poi/ddf/EscherClientAnchorRecord.java b/src/java/org/apache/poi/ddf/EscherClientAnchorRecord.java
index f3d90c715d..ec3354c599 100644
--- a/src/java/org/apache/poi/ddf/EscherClientAnchorRecord.java
+++ b/src/java/org/apache/poi/ddf/EscherClientAnchorRecord.java
@@ -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 );
diff --git a/src/java/org/apache/poi/hssf/model/FormulaParser.java b/src/java/org/apache/poi/hssf/model/FormulaParser.java
index 88a39ffe6f..39a79bb76d 100644
--- a/src/java/org/apache/poi/hssf/model/FormulaParser.java
+++ b/src/java/org/apache/poi/hssf/model/FormulaParser.java
@@ -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;
diff --git a/src/java/org/apache/poi/hssf/record/formula/AttrPtg.java b/src/java/org/apache/poi/hssf/record/formula/AttrPtg.java
index c2ff29116f..263c1728ba 100644
--- a/src/java/org/apache/poi/hssf/record/formula/AttrPtg.java
+++ b/src/java/org/apache/poi/hssf/record/formula/AttrPtg.java
@@ -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);
}
}
diff --git a/src/java/org/apache/poi/hssf/usermodel/HSSFRow.java b/src/java/org/apache/poi/hssf/usermodel/HSSFRow.java
index d62a7c23a0..056c872e7c 100644
--- a/src/java/org/apache/poi/hssf/usermodel/HSSFRow.java
+++ b/src/java/org/apache/poi/hssf/usermodel/HSSFRow.java
@@ -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;
diff --git a/src/java/org/apache/poi/poifs/storage/RawDataBlock.java b/src/java/org/apache/poi/poifs/storage/RawDataBlock.java
index b4630a78b4..5ca1781d07 100644
--- a/src/java/org/apache/poi/poifs/storage/RawDataBlock.java
+++ b/src/java/org/apache/poi/poifs/storage/RawDataBlock.java
@@ -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");
}
diff --git a/src/java/org/apache/poi/poifs/storage/RawDataBlockList.java b/src/java/org/apache/poi/poifs/storage/RawDataBlockList.java
index 76ab219562..66eb237a8e 100644
--- a/src/java/org/apache/poi/poifs/storage/RawDataBlockList.java
+++ b/src/java/org/apache/poi/poifs/storage/RawDataBlockList.java
@@ -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 ]));
}
diff --git a/src/ooxml/interfaces-jdk14/org/apache/poi/ss/usermodel/Row.java b/src/ooxml/interfaces-jdk14/org/apache/poi/ss/usermodel/Row.java
index 80a191d7c6..8a0810dd31 100644
--- a/src/ooxml/interfaces-jdk14/org/apache/poi/ss/usermodel/Row.java
+++ b/src/ooxml/interfaces-jdk14/org/apache/poi/ss/usermodel/Row.java
@@ -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();
}
diff --git a/src/ooxml/interfaces-jdk15/org/apache/poi/ss/usermodel/Row.java b/src/ooxml/interfaces-jdk15/org/apache/poi/ss/usermodel/Row.java
index 28257bae42..0c46c5863d 100644
--- a/src/ooxml/interfaces-jdk15/org/apache/poi/ss/usermodel/Row.java
+++ b/src/ooxml/interfaces-jdk15/org/apache/poi/ss/usermodel/Row.java
@@ -88,9 +88,20 @@ public interface Row extends Iterable {
* 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 {
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();
}
diff --git a/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFRow.java b/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFRow.java
index 1a1e831a2a..3047368e22 100644
--- a/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFRow.java
+++ b/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFRow.java
@@ -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 it = cellIterator() ; it.hasNext() ; ) {
diff --git a/src/ooxml/testcases/org/apache/poi/xssf/usermodel/TestXSSFRow.java b/src/ooxml/testcases/org/apache/poi/xssf/usermodel/TestXSSFRow.java
index 54ded70ca0..a682bab1c6 100644
--- a/src/ooxml/testcases/org/apache/poi/xssf/usermodel/TestXSSFRow.java
+++ b/src/ooxml/testcases/org/apache/poi/xssf/usermodel/TestXSSFRow.java
@@ -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
diff --git a/src/scratchpad/src/org/apache/poi/hwpf/HWPFDocument.java b/src/scratchpad/src/org/apache/poi/hwpf/HWPFDocument.java
index a54e50de43..a43357f021 100644
--- a/src/scratchpad/src/org/apache/poi/hwpf/HWPFDocument.java
+++ b/src/scratchpad/src/org/apache/poi/hwpf/HWPFDocument.java
@@ -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
index 0000000000..4242dd4dfb
--- /dev/null
+++ b/src/scratchpad/src/org/apache/poi/hwpf/model/EscherRecordHolder.java
@@ -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
index 0000000000..bb3d5def1f
--- /dev/null
+++ b/src/scratchpad/src/org/apache/poi/hwpf/model/FSPA.java
@@ -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
index 0000000000..58c69ff4b9
--- /dev/null
+++ b/src/scratchpad/src/org/apache/poi/hwpf/model/FSPATable.java
@@ -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();
+ }
+}
diff --git a/src/scratchpad/src/org/apache/poi/hwpf/model/FileInformationBlock.java b/src/scratchpad/src/org/apache/poi/hwpf/model/FileInformationBlock.java
index ee8f9724e9..48e6d78b3a 100644
--- a/src/scratchpad/src/org/apache/poi/hwpf/model/FileInformationBlock.java
+++ b/src/scratchpad/src/org/apache/poi/hwpf/model/FileInformationBlock.java
@@ -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
diff --git a/src/scratchpad/src/org/apache/poi/hwpf/model/PicturesTable.java b/src/scratchpad/src/org/apache/poi/hwpf/model/PicturesTable.java
index d9598b1061..9681741d94 100644
--- a/src/scratchpad/src/org/apache/poi/hwpf/model/PicturesTable.java
+++ b/src/scratchpad/src/org/apache/poi/hwpf/model/PicturesTable.java
@@ -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;
}
diff --git a/src/scratchpad/src/org/apache/poi/hwpf/usermodel/Picture.java b/src/scratchpad/src/org/apache/poi/hwpf/usermodel/Picture.java
index e9ee228c12..8a3737865b 100644
--- a/src/scratchpad/src/org/apache/poi/hwpf/usermodel/Picture.java
+++ b/src/scratchpad/src/org/apache/poi/hwpf/usermodel/Picture.java
@@ -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 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);
diff --git a/src/testcases/org/apache/poi/hssf/usermodel/TestHSSFRow.java b/src/testcases/org/apache/poi/hssf/usermodel/TestHSSFRow.java
index 7611abb51c..d307a0e251 100644
--- a/src/testcases/org/apache/poi/hssf/usermodel/TestHSSFRow.java
+++ b/src/testcases/org/apache/poi/hssf/usermodel/TestHSSFRow.java
@@ -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());
+ }
}
diff --git a/src/testcases/org/apache/poi/poifs/filesystem/TestPOIFSFileSystem.java b/src/testcases/org/apache/poi/poifs/filesystem/TestPOIFSFileSystem.java
index 1cde86918b..4a948ba8c6 100755
--- a/src/testcases/org/apache/poi/poifs/filesystem/TestPOIFSFileSystem.java
+++ b/src/testcases/org/apache/poi/poifs/filesystem/TestPOIFSFileSystem.java
@@ -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"
};
| | |