From: Nick Burch Date: Sat, 5 Apr 2008 13:38:52 +0000 (+0000) Subject: Merged revisions 638786-638802,638805-638811,638813-638814,638816-639230,639233-63924... X-Git-Tag: REL_3_5_BETA2~136 X-Git-Url: https://source.dussan.org/?a=commitdiff_plain;h=2486c5524cca2afd7c8b31b6f468945cdd18de1b;p=poi.git Merged revisions 638786-638802,638805-638811,638813-638814,638816-639230,639233-639241,639243-639253,639255-639486,639488-639601,639603-639835,639837-639917,639919-640056,640058-640710,640712-641156,641158-641184,641186-641795,641797-641798,641800-641933,641935-641963,641965-641966,641968-641995,641997-642230,642232-642562,642564-642565,642568-642570,642572-642573,642576-642736,642739-642877,642879,642881-642890,642892-642903,642905-642945,642947-645088 via svnmerge from https://svn.apache.org/repos/asf/poi/trunk ........ r643625 | josh | 2008-04-01 23:18:25 +0100 (Tue, 01 Apr 2008) | 1 line Bug #44733 - DPRODUCT function should be index 189, not 191 ........ r643654 | josh | 2008-04-02 00:53:51 +0100 (Wed, 02 Apr 2008) | 1 line fix for bug 44710 - Incorrect skip() of second formula in DATAVALIDATION record ........ r643670 | klute | 2008-04-02 01:50:45 +0100 (Wed, 02 Apr 2008) | 1 line 44694 - HPSF: Support for property sets without sections ........ r643672 | klute | 2008-04-02 01:55:52 +0100 (Wed, 02 Apr 2008) | 1 line Restored an entry that had been deleted by chance. ........ r643831 | yegor | 2008-04-02 11:20:31 +0100 (Wed, 02 Apr 2008) | 1 line added set accessor to indentation level ........ r643834 | yegor | 2008-04-02 11:25:18 +0100 (Wed, 02 Apr 2008) | 1 line Fixed inconsistency between HSSFSHeet.getColumnWidth and HSSFSheet.getDefaultColumnWidth: getColumnWidth should always return width measured in 1/256th units. ........ r644343 | nick | 2008-04-03 16:04:52 +0100 (Thu, 03 Apr 2008) | 1 line Make a bit of a start on being able to edit chart titles, based on the email to user@poi from Russ on the 2nd of April. Not quite there though ........ r644473 | josh | 2008-04-03 21:25:53 +0100 (Thu, 03 Apr 2008) | 1 line Fix for bug 44739 - Conditional formatting (regions with max row/col index) ........ r644509 | josh | 2008-04-03 22:17:26 +0100 (Thu, 03 Apr 2008) | 1 line Follow up fix after r644473 (bug 44739) TestHSSFConditionalFormatting had a bug and wasn't yet included in the test suite hierarchy ........ git-svn-id: https://svn.apache.org/repos/asf/poi/branches/ooxml@645096 13f79535-47bb-0310-9956-ffa450edef68 --- diff --git a/src/documentation/content/xdocs/changes.xml b/src/documentation/content/xdocs/changes.xml index 5722a7be45..e2e6faef7e 100644 --- a/src/documentation/content/xdocs/changes.xml +++ b/src/documentation/content/xdocs/changes.xml @@ -37,7 +37,9 @@ + 44739 - Small fixes for conditional formatting (regions with max row/col index) Implement Sheet.removeShape(Shape shape) in HSLF + 44694 - HPSF: Support for property sets without sections Various fixes: Recognising var-arg built-in functions #44675, ExternalNameRecord serialisation bug #44695, PMT() bug #44691 30311 - More work on Conditional Formatting Move the Formula Evaluator code out of scratchpad diff --git a/src/documentation/content/xdocs/status.xml b/src/documentation/content/xdocs/status.xml index 9d35cb7fd1..7a1d2e9717 100644 --- a/src/documentation/content/xdocs/status.xml +++ b/src/documentation/content/xdocs/status.xml @@ -34,6 +34,8 @@ + 44739 - Small fixes for conditional formatting (regions with max row/col index) + 44694 - HPSF: Support for property sets without sections Implement Sheet.removeShape(Shape shape) in HSLF Various fixes: Recognising var-arg built-in functions #44675, ExternalNameRecord serialisation bug #44695, PMT() bug #44691 30311 - More work on Conditional Formatting diff --git a/src/java/org/apache/poi/hpsf/MutablePropertySet.java b/src/java/org/apache/poi/hpsf/MutablePropertySet.java index 34281b90c1..5ecbfeee3d 100644 --- a/src/java/org/apache/poi/hpsf/MutablePropertySet.java +++ b/src/java/org/apache/poi/hpsf/MutablePropertySet.java @@ -92,6 +92,8 @@ public class MutablePropertySet extends PropertySet osVersion = ps.getOSVersion(); setClassID(ps.getClassID()); clearSections(); + if (sections == null) + sections = new LinkedList(); for (final Iterator i = ps.getSections().iterator(); i.hasNext();) { final MutableSection s = new MutableSection((Section) (i.next())); diff --git a/src/java/org/apache/poi/hpsf/PropertySet.java b/src/java/org/apache/poi/hpsf/PropertySet.java index 0bfbb8f32f..b39ba08bfa 100644 --- a/src/java/org/apache/poi/hpsf/PropertySet.java +++ b/src/java/org/apache/poi/hpsf/PropertySet.java @@ -387,7 +387,7 @@ public class PropertySet o += ClassID.LENGTH; final long sectionCount = LittleEndian.getUInt(src, o); o += LittleEndian.INT_SIZE; - if (sectionCount < 1) + if (sectionCount < 0) return false; return true; } @@ -426,9 +426,9 @@ public class PropertySet o += ClassID.LENGTH; final int sectionCount = LittleEndian.getInt(src, o); o += LittleEndian.INT_SIZE; - if (sectionCount <= 0) + if (sectionCount < 0) throw new HPSFRuntimeException("Section count " + sectionCount + - " must be greater than 0."); + " is negative."); /* * Read the sections, which are following the header. They @@ -468,6 +468,8 @@ public class PropertySet */ public boolean isSummaryInformation() { + if (sections.size() <= 0) + return false; return Util.equal(((Section) sections.get(0)).getFormatID().getBytes(), SectionIDMap.SUMMARY_INFORMATION_ID); } @@ -483,6 +485,8 @@ public class PropertySet */ public boolean isDocumentSummaryInformation() { + if (sections.size() <= 0) + return false; return Util.equal(((Section) sections.get(0)).getFormatID().getBytes(), SectionIDMap.DOCUMENT_SUMMARY_INFORMATION_ID[0]); } diff --git a/src/java/org/apache/poi/hssf/model/Sheet.java b/src/java/org/apache/poi/hssf/model/Sheet.java index 451cde7579..2af25e7481 100644 --- a/src/java/org/apache/poi/hssf/model/Sheet.java +++ b/src/java/org/apache/poi/hssf/model/Sheet.java @@ -1879,12 +1879,12 @@ public class Sheet implements Model } /** - * get the width of a given column in units of 1/20th of a point width (twips?) + * get the width of a given column in units of 1/256th of a character width * @param column index * @see org.apache.poi.hssf.record.DefaultColWidthRecord * @see org.apache.poi.hssf.record.ColumnInfoRecord * @see #setColumnWidth(short,short) - * @return column width in units of 1/20th of a point (twips?) + * @return column width in units of 1/256th of a character width */ public short getColumnWidth(short column) @@ -1912,7 +1912,9 @@ public class Sheet implements Model } else { - retval = defaultcolwidth.getColWidth(); + //default column width is measured in characters + //multiply + retval = (short)(256*defaultcolwidth.getColWidth()); } return retval; } @@ -1951,9 +1953,9 @@ public class Sheet implements Model } /** - * set the width for a given column in 1/20th of a character width units + * set the width for a given column in 1/256th of a character width units * @param column - the column number - * @param width (in units of 1/20th of a character width) + * @param width (in units of 1/256th of a character width) */ public void setColumnWidth(short column, short width) { diff --git a/src/java/org/apache/poi/hssf/record/CFHeaderRecord.java b/src/java/org/apache/poi/hssf/record/CFHeaderRecord.java index 7f2d5c3197..d74d54ab17 100644 --- a/src/java/org/apache/poi/hssf/record/CFHeaderRecord.java +++ b/src/java/org/apache/poi/hssf/record/CFHeaderRecord.java @@ -58,12 +58,12 @@ public final class CFHeaderRecord extends Record { field_1_numcf = in.readShort(); field_2_need_recalculation = in.readShort(); - field_3_enclosing_cell_range = new CellRange(in.readShort(),in.readShort(),in.readShort(),in.readShort()); + field_3_enclosing_cell_range = new CellRange(in.readUShort(), in.readUShort(), in.readUShort(), in.readUShort()); int numCellRanges = in.readShort(); CellRange[] crs = new CellRange[numCellRanges]; for( int i=0; i + * Title: DATAVALIDATION Record (0x01BE)

* Description: This record stores data validation settings and a list of cell ranges * which contain these settings. The data validation settings of a sheet * are stored in a sequential list of DV records. This list is followed by @@ -36,7 +36,7 @@ import org.apache.poi.util.StringUtil; * @author Dragos Buleandra (dragos.buleandra@trade2b.ro) * @version 2.0-pre */ -public class DVRecord extends Record +public final class DVRecord extends Record { public final static short sid = 0x01BE; @@ -170,11 +170,6 @@ public class DVRecord extends Record this.field_not_used_1 = in.readShort(); //read first formula data condition - // Not sure if this was needed or not... -// try { -// in.skip(this.field_size_first_formula); -// } catch(IOException e) { throw new IllegalStateException(e); } - int token_pos = 0; while (token_pos < this.field_size_first_formula) { @@ -187,14 +182,14 @@ public class DVRecord extends Record this.field_not_used_2 = in.readShort(); //read sec formula data condition - //Not sure if this was needed or not... - try { - in.skip(this.field_size_sec_formula); - } catch(IOException e) { - e.printStackTrace(); - throw new IllegalStateException(e.getMessage()); + if (false) { // TODO - prior to bug 44710 this 'skip' was being executed. write a junit to confirm this fix + try { + in.skip(this.field_size_sec_formula); + } catch(IOException e) { + e.printStackTrace(); + throw new IllegalStateException(e.getMessage()); + } } - token_pos = 0; while (token_pos < this.field_size_sec_formula) { @@ -516,7 +511,7 @@ public class DVRecord extends Record /**@todo DVRecord = Serializare */ - private class StringHandler + private static final class StringHandler { private int _string_length = 0x0001; private byte _string_unicode_flag = 0x00; diff --git a/src/java/org/apache/poi/hssf/record/cf/CellRange.java b/src/java/org/apache/poi/hssf/record/cf/CellRange.java index 16093ee582..f45b79b003 100644 --- a/src/java/org/apache/poi/hssf/record/cf/CellRange.java +++ b/src/java/org/apache/poi/hssf/record/cf/CellRange.java @@ -29,12 +29,10 @@ import org.apache.poi.hssf.util.Region; */ public final class CellRange { - /** - * max index for both row and column

- * - * Note - this value converts to -1 when cast to a short - */ - private static final int MAX_INDEX = Integer.MAX_VALUE; + /** max 65536 rows in BIFF8 */ + private static final int LAST_ROW_INDEX = 0x00FFFF; + /** max 256 columns in BIFF8 */ + private static final int LAST_COLUMN_INDEX = 0x00FF; private static final Region[] EMPTY_REGION_ARRAY = { }; @@ -57,54 +55,46 @@ public final class CellRange + ", " + firstColumn + ", " + lastColumn + ")"); } _firstRow = firstRow; - _lastRow = convertM1ToMax(lastRow); + _lastRow = convertM1ToMax(lastRow, LAST_ROW_INDEX); _firstColumn = firstColumn; - _lastColumn = convertM1ToMax(lastColumn); + _lastColumn = convertM1ToMax(lastColumn, LAST_COLUMN_INDEX); } - private static int convertM1ToMax(int lastIx) { + /** + * Range arithmetic is easier when using a large positive number for 'max row or column' + * instead of -1. + */ + private static int convertM1ToMax(int lastIx, int maxIndex) { if(lastIx < 0) { - return MAX_INDEX; - } - return lastIx; - } - private static int convertMaxToM1(int lastIx) { - if(lastIx == MAX_INDEX) { - return -1; + return maxIndex; } return lastIx; } public boolean isFullColumnRange() { - return _firstColumn == 0 && _lastColumn == MAX_INDEX; + return _firstRow == 0 && _lastRow == LAST_ROW_INDEX; } public boolean isFullRowRange() { - return _firstRow == 0 && _lastRow == MAX_INDEX; + return _firstColumn == 0 && _lastColumn == LAST_COLUMN_INDEX; } - public CellRange(Region r) { - this(r.getRowFrom(), r.getRowTo(), r.getColumnFrom(), r.getColumnTo()); + private static CellRange createFromRegion(Region r) { + return new CellRange(r.getRowFrom(), r.getRowTo(), r.getColumnFrom(), r.getColumnTo()); } - - private static boolean isValid(int firstRow, int lastRow, int firstColumn, int lastColumn) { - if(lastRow == -1) { - if(firstRow !=0) { - return false; - } + if(lastRow < 0 || lastRow > LAST_ROW_INDEX) { + return false; } - if(firstRow < 0 || lastRow < -1) { + if(firstRow < 0 || firstRow > LAST_ROW_INDEX) { return false; } - if(lastColumn == -1) { - if(firstColumn !=0) { - return false; - } + if(lastColumn < 0 || lastColumn > LAST_COLUMN_INDEX) { + return false; } - if(firstColumn < 0 || lastColumn < -1) { + if(firstColumn < 0 || firstColumn > LAST_COLUMN_INDEX) { return false; } return true; @@ -114,23 +104,17 @@ public final class CellRange { return _firstRow; } - /** - * @return -1 for whole column ranges - */ public int getLastRow() { - return convertMaxToM1(_lastRow); + return _lastRow; } public int getFirstColumn() { return _firstColumn; } - /** - * @return -1 for whole row ranges - */ public int getLastColumn() { - return convertMaxToM1(_lastColumn); + return _lastColumn; } public static final int NO_INTERSECTION = 1; @@ -374,7 +358,7 @@ public final class CellRange CellRange[] result = new CellRange[regions.length]; for( int i=0; iorg.apache.poi.hssf.record. * @@ -104,6 +104,7 @@ public final class AllRecordTests { result.addTestSuite(TestUnicodeString.class); result.addTestSuite(TestUnitsRecord.class); result.addTestSuite(TestValueRangeRecord.class); + result.addTestSuite(TestCellRange.class); return result; } } diff --git a/src/testcases/org/apache/poi/hssf/record/TestCFHeaderRecord.java b/src/testcases/org/apache/poi/hssf/record/TestCFHeaderRecord.java index 2a6faaccdd..e883503674 100644 --- a/src/testcases/org/apache/poi/hssf/record/TestCFHeaderRecord.java +++ b/src/testcases/org/apache/poi/hssf/record/TestCFHeaderRecord.java @@ -17,6 +17,7 @@ package org.apache.poi.hssf.record; +import junit.framework.AssertionFailedError; import junit.framework.TestCase; import org.apache.poi.hssf.record.cf.CellRange; @@ -34,8 +35,8 @@ public final class TestCFHeaderRecord extends TestCase { CFHeaderRecord record = new CFHeaderRecord(); CellRange[] ranges = { - new CellRange(0,-1,5,5), - new CellRange(0,-1,6,6), + new CellRange(0,0xFFFF,5,5), + new CellRange(0,0xFFFF,6,6), new CellRange(0,1,0,1), new CellRange(0,1,2,3), new CellRange(2,3,0,1), @@ -46,7 +47,7 @@ public final class TestCFHeaderRecord extends TestCase assertEquals(6,ranges.length); CellRange enclosingCellRange = record.getEnclosingCellRange(); assertEquals(0, enclosingCellRange.getFirstRow()); - assertEquals(-1, enclosingCellRange.getLastRow()); + assertEquals(65535, enclosingCellRange.getLastRow()); assertEquals(0, enclosingCellRange.getFirstColumn()); assertEquals(6, enclosingCellRange.getLastColumn()); record.setNeedRecalculation(true); @@ -56,7 +57,7 @@ public final class TestCFHeaderRecord extends TestCase } public void testSerialization() { - byte[] recordData = new byte[] + byte[] recordData = { (byte)0x03, (byte)0x00, (byte)0x01, (byte)0x00, @@ -66,7 +67,7 @@ public final class TestCFHeaderRecord extends TestCase (byte)0x00, (byte)0x00, (byte)0x03, (byte)0x00, - (byte)0x04, (byte)0x00, + (byte)0x04, (byte)0x00, // nRegions (byte)0x00, (byte)0x00, (byte)0x01, (byte)0x00, @@ -93,44 +94,88 @@ public final class TestCFHeaderRecord extends TestCase assertEquals("#CFRULES", 3, record.getNumberOfConditionalFormats()); assertTrue(record.getNeedRecalculation()); - CellRange enclosingCellRange = record.getEnclosingCellRange(); - assertEquals(0, enclosingCellRange.getFirstRow()); - assertEquals(3, enclosingCellRange.getLastRow()); - assertEquals(0, enclosingCellRange.getFirstColumn()); - assertEquals(3, enclosingCellRange.getLastColumn()); + confirm(record.getEnclosingCellRange(), 0, 3, 0, 3); CellRange[] ranges = record.getCellRanges(); - CellRange range0 = ranges[0]; - assertEquals(0, range0.getFirstRow()); - assertEquals(1, range0.getLastRow()); - assertEquals(0, range0.getFirstColumn()); - assertEquals(1, range0.getLastColumn()); - CellRange range1 = ranges[1]; - assertEquals(0, range1.getFirstRow()); - assertEquals(1, range1.getLastRow()); - assertEquals(2, range1.getFirstColumn()); - assertEquals(3, range1.getLastColumn()); - CellRange range2 = ranges[2]; - assertEquals(2, range2.getFirstRow()); - assertEquals(3, range2.getLastRow()); - assertEquals(0, range2.getFirstColumn()); - assertEquals(1, range2.getLastColumn()); - CellRange range3 = ranges[3]; - assertEquals(2, range3.getFirstRow()); - assertEquals(3, range3.getLastRow()); - assertEquals(2, range3.getFirstColumn()); - assertEquals(3, range3.getLastColumn()); + assertEquals(4, ranges.length); + confirm(ranges[0], 0, 1, 0, 1); + confirm(ranges[1], 0, 1, 2, 3); + confirm(ranges[2], 2, 3, 0, 1); + confirm(ranges[3], 2, 3, 2, 3); assertEquals(recordData.length+4, record.getRecordSize()); byte[] output = record.serialize(); assertEquals("Output size", recordData.length+4, output.length); //includes sid+recordlength - for (int i = 0; i < recordData.length;i++) + for (int i = 0; i < recordData.length; i++) { assertEquals("CFHeaderRecord doesn't match", recordData[i], output[i+4]); } } + public void testExtremeRows() { + byte[] recordData = { + (byte)0x13, (byte)0x00, // nFormats + (byte)0x00, (byte)0x00, + + (byte)0x00, (byte)0x00, + (byte)0xFF, (byte)0xFF, + (byte)0x00, (byte)0x00, + (byte)0xFF, (byte)0x00, + + (byte)0x03, (byte)0x00, // nRegions + + (byte)0x40, (byte)0x9C, + (byte)0x50, (byte)0xC3, + (byte)0x02, (byte)0x00, + (byte)0x02, (byte)0x00, + + (byte)0x00, (byte)0x00, + (byte)0xFF, (byte)0xFF, + (byte)0x05, (byte)0x00, + (byte)0x05, (byte)0x00, + + (byte)0x07, (byte)0x00, + (byte)0x07, (byte)0x00, + (byte)0x00, (byte)0x00, + (byte)0xFF, (byte)0x00, + }; + + CFHeaderRecord record; + try { + record = new CFHeaderRecord(new TestcaseRecordInputStream(CFHeaderRecord.sid, (short)recordData.length, recordData)); + } catch (IllegalArgumentException e) { + if(e.getMessage().equals("invalid cell range (-25536, 2, -15536, 2)")) { + throw new AssertionFailedError("Identified bug 44739b"); + } + throw e; + } + + assertEquals("#CFRULES", 19, record.getNumberOfConditionalFormats()); + assertFalse(record.getNeedRecalculation()); + confirm(record.getEnclosingCellRange(), 0, 65535, 0, 255); + CellRange[] ranges = record.getCellRanges(); + assertEquals(3, ranges.length); + confirm(ranges[0], 40000, 50000, 2, 2); + confirm(ranges[1], 0, 65535, 5, 5); + confirm(ranges[2], 7, 7, 0, 255); + + byte[] output = record.serialize(); + + assertEquals("Output size", recordData.length+4, output.length); //includes sid+recordlength + + for (int i = 0; i < recordData.length;i++) { + assertEquals("CFHeaderRecord doesn't match", recordData[i], output[i+4]); + } + } + + + private static void confirm(CellRange cr, int expFirstRow, int expLastRow, int expFirstCol, int expLastColumn) { + assertEquals("first row", expFirstRow, cr.getFirstRow()); + assertEquals("last row", expLastRow, cr.getLastRow()); + assertEquals("first column", expFirstCol, cr.getFirstColumn()); + assertEquals("last column", expLastColumn, cr.getLastColumn()); + } public static void main(String[] ignored_args) { diff --git a/src/testcases/org/apache/poi/hssf/record/cf/TestCellRange.java b/src/testcases/org/apache/poi/hssf/record/cf/TestCellRange.java index ade097e697..2ba196d55a 100644 --- a/src/testcases/org/apache/poi/hssf/record/cf/TestCellRange.java +++ b/src/testcases/org/apache/poi/hssf/record/cf/TestCellRange.java @@ -17,62 +17,76 @@ limitations under the License. package org.apache.poi.hssf.record.cf; +import junit.framework.AssertionFailedError; import junit.framework.TestCase; /** * Tests CellRange operations. */ -public class TestCellRange extends TestCase +public final class TestCellRange extends TestCase { - private static final CellRange biggest = new CellRange( 0, -1, 0,-1); - private static final CellRange tenthColumn = new CellRange( 0, -1,10,10); - private static final CellRange tenthRow = new CellRange(10, 10, 0,-1); - private static final CellRange box10x10 = new CellRange( 0, 10, 0,10); - private static final CellRange box9x9 = new CellRange( 0, 9, 0, 9); - private static final CellRange box10to20c = new CellRange( 0, 10,10,20); - private static final CellRange oneCell = new CellRange(10, 10,10,10); + private static final CellRange biggest = createCR( 0, -1, 0,-1); + private static final CellRange tenthColumn = createCR( 0, -1,10,10); + private static final CellRange tenthRow = createCR(10, 10, 0,-1); + private static final CellRange box10x10 = createCR( 0, 10, 0,10); + private static final CellRange box9x9 = createCR( 0, 9, 0, 9); + private static final CellRange box10to20c = createCR( 0, 10,10,20); + private static final CellRange oneCell = createCR(10, 10,10,10); - boolean [][] contanis = new boolean[][] + private static final CellRange[] sampleRanges = { + biggest, tenthColumn, tenthRow, box10x10, box9x9, box10to20c, oneCell, + }; + + /** cross-reference of contains() operations for sampleRanges against itself */ + private static final boolean [][] containsExpectedResults = { - // biggest, tenthColumn, tenthRow, box10x10, box9x9, box10to20c, oneCell - /*biggest */ new boolean[]{true, true , true , true , true , true , true}, - /*tenthColumn*/ new boolean[]{false, true , false, false, false, false, true}, - /*tenthRow */ new boolean[]{false, false, true , false, false, false, true}, - /*box10x10 */ new boolean[]{false, false, false, true , true , false, true}, - /*box9x9 */ new boolean[]{false, false, false, false, true , false, false}, - /*box10to20c */ new boolean[]{false, false, false, false, false, true , true}, - /*oneCell */ new boolean[]{false, false, false, false, false, false, true}, + // biggest, tenthColumn, tenthRow, box10x10, box9x9, box10to20c, oneCell + /*biggest */ {true, true , true , true , true , true , true}, + /*tenthColumn*/ {false, true , false, false, false, false, true}, + /*tenthRow */ {false, false, true , false, false, false, true}, + /*box10x10 */ {false, false, false, true , true , false, true}, + /*box9x9 */ {false, false, false, false, true , false, false}, + /*box10to20c */ {false, false, false, false, false, true , true}, + /*oneCell */ {false, false, false, false, false, false, true}, } ; - - - public void testContainsMethod() - { - CellRange [] ranges = new CellRange[]{biggest,tenthColumn,tenthRow,box10x10,box9x9,box10to20c,oneCell}; - testContainsMethod(contanis,ranges); + + /** + * @param lastRow pass -1 for max row index + * @param lastCol pass -1 for max col index + */ + private static CellRange createCR(int firstRow, int lastRow, int firstCol, int lastCol) { + // max row & max col limit as per BIFF8 + return new CellRange( + firstRow, + lastRow == -1 ? 0xFFFF : lastRow, + firstCol, + lastCol == -1 ? 0x00FF : lastCol); } - private void testContainsMethod(boolean[][]contains,CellRange[] ranges) + public void testContainsMethod() { + CellRange [] ranges = sampleRanges; for(int i=0; i!=ranges.length;i++) { for(int j=0; j!=ranges.length;j++) { - assertEquals("("+i+","+j+"): ",contains[i][j],ranges[i].contains(ranges[j])); + boolean expectedResult = containsExpectedResults[i][j]; + assertEquals("("+i+","+j+"): ", expectedResult, ranges[i].contains(ranges[j])); } } } - private static final CellRange col1 = new CellRange( 0, -1, 1,1); - private static final CellRange col2 = new CellRange( 0, -1, 2,2); - private static final CellRange row1 = new CellRange( 1, 1, 0,-1); - private static final CellRange row2 = new CellRange( 2, 2, 0,-1); + private static final CellRange col1 = createCR( 0, -1, 1,1); + private static final CellRange col2 = createCR( 0, -1, 2,2); + private static final CellRange row1 = createCR( 1, 1, 0,-1); + private static final CellRange row2 = createCR( 2, 2, 0,-1); - private static final CellRange box0 = new CellRange( 0, 2, 0,2); - private static final CellRange box1 = new CellRange( 0, 1, 0,1); - private static final CellRange box2 = new CellRange( 0, 1, 2,3); - private static final CellRange box3 = new CellRange( 2, 3, 0,1); - private static final CellRange box4 = new CellRange( 2, 3, 2,3); - private static final CellRange box5 = new CellRange( 1, 3, 1,3); + private static final CellRange box0 = createCR( 0, 2, 0,2); + private static final CellRange box1 = createCR( 0, 1, 0,1); + private static final CellRange box2 = createCR( 0, 1, 2,3); + private static final CellRange box3 = createCR( 2, 3, 0,1); + private static final CellRange box4 = createCR( 2, 3, 2,3); + private static final CellRange box5 = createCR( 1, 3, 1,3); public void testHasSharedBorderMethod() { @@ -119,8 +133,8 @@ public class TestCellRange extends TestCase public void testIntersectMethod() { - assertEquals( CellRange.OVERLAP,box0.intersect(box5)); - assertEquals( CellRange.OVERLAP,box5.intersect(box0)); + assertEquals(CellRange.OVERLAP,box0.intersect(box5)); + assertEquals(CellRange.OVERLAP,box5.intersect(box0)); assertEquals(CellRange.NO_INTERSECTION,box1.intersect(box4)); assertEquals(CellRange.NO_INTERSECTION,box4.intersect(box1)); assertEquals(CellRange.NO_INTERSECTION,box2.intersect(box3)); @@ -135,4 +149,31 @@ public class TestCellRange extends TestCase assertEquals(CellRange.INSIDE,tenthColumn.intersect(tenthColumn)); assertEquals(CellRange.INSIDE,tenthRow.intersect(tenthRow)); } + + /** + * Cell ranges like the following are valid + * =$C:$IV,$B$1:$B$8,$B$10:$B$65536,$A:$A + */ + public void testCreate() { + CellRange cr; + + cr = createCR(0, -1, 2, 255); // $C:$IV + confirmRange(cr, false, true); + cr = createCR(0, 7, 1, 1); // $B$1:$B$8 + + try { + cr = createCR(9, -1, 1, 1); // $B$65536 + } catch (IllegalArgumentException e) { + if(e.getMessage().startsWith("invalid cell range")) { + throw new AssertionFailedError("Identified bug 44739"); + } + throw e; + } + cr = createCR(0, -1, 0, 0); // $A:$A + } + + private static void confirmRange(CellRange cr, boolean isFullRow, boolean isFullColumn) { + assertEquals("isFullRowRange", isFullRow, cr.isFullRowRange()); + assertEquals("isFullColumnRange", isFullColumn, cr.isFullColumnRange()); + } } diff --git a/src/testcases/org/apache/poi/hssf/record/formula/AllFormulaTests.java b/src/testcases/org/apache/poi/hssf/record/formula/AllFormulaTests.java index 645709bb6e..3a70450223 100644 --- a/src/testcases/org/apache/poi/hssf/record/formula/AllFormulaTests.java +++ b/src/testcases/org/apache/poi/hssf/record/formula/AllFormulaTests.java @@ -29,7 +29,7 @@ import junit.framework.TestSuite; * * @author Josh Micich */ -public class AllFormulaTests { +public final class AllFormulaTests { public static Test suite() { TestSuite result = new TestSuite(AllFormulaTests.class.getName()); @@ -50,7 +50,6 @@ public class AllFormulaTests { result.addTestSuite(TestReferencePtg.class); result.addTestSuite(TestSheetNameFormatter.class); result.addTestSuite(TestUnionPtg.class); - result.addTest(AllFormulaFunctionTests.suite()); return result; } } diff --git a/src/testcases/org/apache/poi/hssf/record/formula/function/TestParseMissingBuiltInFuncs.java b/src/testcases/org/apache/poi/hssf/record/formula/function/TestParseMissingBuiltInFuncs.java index 3671d37c1a..fdad5f44d8 100644 --- a/src/testcases/org/apache/poi/hssf/record/formula/function/TestParseMissingBuiltInFuncs.java +++ b/src/testcases/org/apache/poi/hssf/record/formula/function/TestParseMissingBuiltInFuncs.java @@ -27,6 +27,9 @@ import org.apache.poi.hssf.record.formula.FuncPtg; import org.apache.poi.hssf.record.formula.FuncVarPtg; import org.apache.poi.hssf.record.formula.Ptg; /** + * Tests parsing of some built-in functions that were not properly + * registered in POI as bug #44675, #44733 (March/April 2008). + * * @author Josh Micich */ public final class TestParseMissingBuiltInFuncs extends TestCase { @@ -39,6 +42,7 @@ public final class TestParseMissingBuiltInFuncs extends TestCase { Ptg[] ptgs = parse(formula); Ptg ptgF = ptgs[ptgs.length-1]; // func is last RPN token in all these formulas + // Check critical things in the Ptg array encoding. if(!(ptgF instanceof AbstractFunctionPtg)) { throw new RuntimeException("function token missing"); } @@ -47,11 +51,15 @@ public final class TestParseMissingBuiltInFuncs extends TestCase { throw new AssertionFailedError("Failed to recognise built-in function in formula '" + formula + "'"); } - assertEquals(expPtgArraySize, ptgs.length); assertEquals(funcIx, func.getFunctionIndex()); Class expCls = isVarArgFunc ? FuncVarPtg.class : FuncPtg.class; assertEquals(expCls, ptgF.getClass()); + + // check that parsed Ptg array converts back to formula text OK + Workbook book = Workbook.createWorkbook(); + String reRenderedFormula = FormulaParser.toFormulaString(book, ptgs); + assertEquals(formula, reRenderedFormula); } public void testDatedif() { @@ -76,4 +84,7 @@ public final class TestParseMissingBuiltInFuncs extends TestCase { public void testIsnontext() { confirmFunc("ISNONTEXT(\"abc\")", 2, false, 190); } + public void testDproduct() { + confirmFunc("DPRODUCT(C1:E5,\"HarvestYield\",G1:H2)", 4, false, 189); + } } diff --git a/src/testcases/org/apache/poi/hssf/record/formula/function/TestReadMissingBuiltInFuncs.java b/src/testcases/org/apache/poi/hssf/record/formula/function/TestReadMissingBuiltInFuncs.java index f1e6bcfacf..14799bd0a7 100644 --- a/src/testcases/org/apache/poi/hssf/record/formula/function/TestReadMissingBuiltInFuncs.java +++ b/src/testcases/org/apache/poi/hssf/record/formula/function/TestReadMissingBuiltInFuncs.java @@ -21,7 +21,9 @@ import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; +import java.lang.reflect.InvocationTargetException; +import org.apache.poi.hssf.record.RecordFormatException; import org.apache.poi.hssf.usermodel.HSSFSheet; import org.apache.poi.hssf.usermodel.HSSFWorkbook; @@ -29,24 +31,31 @@ import junit.framework.AssertionFailedError; import junit.framework.TestCase; /** * Tests reading from a sample spreadsheet some built-in functions that were not properly - * registered in POI as bug #44675 (March 2008). + * registered in POI as bug #44675, #44733 (March/April 2008). * * @author Josh Micich */ public final class TestReadMissingBuiltInFuncs extends TestCase { - private HSSFSheet sht; + /** + * This spreadsheet has examples of calls to the interesting built-in functions in cells A1:A7 + */ + private static final String SAMPLE_SPREADSHEET_FILE_NAME = "missingFuncs44675.xls"; + private static HSSFSheet _sheet; - protected void setUp() { - String cwd = System.getProperty("HSSF.testdata.path"); - HSSFWorkbook wb; - try { - InputStream is = new FileInputStream(new File(cwd, "missingFuncs44675.xls")); - wb = new HSSFWorkbook(is); - } catch (IOException e) { - throw new RuntimeException(e); + private static HSSFSheet getSheet() { + if (_sheet == null) { + String cwd = System.getProperty("HSSF.testdata.path"); + HSSFWorkbook wb; + try { + InputStream is = new FileInputStream(new File(cwd, SAMPLE_SPREADSHEET_FILE_NAME)); + wb = new HSSFWorkbook(is); + } catch (IOException e) { + throw new RuntimeException(e); + } + _sheet = wb.getSheetAt(0); } - sht = wb.getSheetAt(0); + return _sheet; } public void testDatedif() { @@ -128,9 +137,30 @@ public final class TestReadMissingBuiltInFuncs extends TestCase { } assertEquals("ISNONTEXT(\"abc\")", formula); } + public void testDproduct() { + + String formula = getCellFormula(6); + assertEquals("DPRODUCT(C1:E5,\"HarvestYield\",G1:H2)", formula); + } private String getCellFormula(int rowIx) { - String result = sht.getRow(rowIx).getCell((short)0).getCellFormula(); + HSSFSheet sheet; + try { + sheet = getSheet(); + } catch (RecordFormatException e) { + if(e.getCause() instanceof InvocationTargetException) { + InvocationTargetException ite = (InvocationTargetException) e.getCause(); + if(ite.getTargetException() instanceof RuntimeException) { + RuntimeException re = (RuntimeException) ite.getTargetException(); + if(re.getMessage().equals("Invalid built-in function index (189)")) { + throw afe("DPRODUCT() registered with wrong index"); + } + } + } + // some other unexpected error + throw e; + } + String result = sheet.getRow(rowIx).getCell((short)0).getCellFormula(); if (false) { System.err.println(result); } diff --git a/src/testcases/org/apache/poi/hssf/usermodel/AllUserModelTests.java b/src/testcases/org/apache/poi/hssf/usermodel/AllUserModelTests.java index cbc555da36..6d6b053a63 100755 --- a/src/testcases/org/apache/poi/hssf/usermodel/AllUserModelTests.java +++ b/src/testcases/org/apache/poi/hssf/usermodel/AllUserModelTests.java @@ -40,6 +40,7 @@ public class AllUserModelTests { result.addTestSuite(TestFormulas.class); result.addTestSuite(TestHSSFCell.class); result.addTestSuite(TestHSSFClientAnchor.class); + result.addTestSuite(TestHSSFConditionalFormatting.class); result.addTestSuite(TestHSSFComment.class); result.addTestSuite(TestHSSFDateUtil.class); result.addTestSuite(TestHSSFHeaderFooter.class); diff --git a/src/testcases/org/apache/poi/hssf/usermodel/TestHSSFConditionalFormatting.java b/src/testcases/org/apache/poi/hssf/usermodel/TestHSSFConditionalFormatting.java new file mode 100644 index 0000000000..5dcc61aa41 --- /dev/null +++ b/src/testcases/org/apache/poi/hssf/usermodel/TestHSSFConditionalFormatting.java @@ -0,0 +1,90 @@ +/* ==================================================================== + 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.hssf.usermodel; + +import junit.framework.TestCase; + +import org.apache.poi.hssf.record.CFRuleRecord.ComparisonOperator; +import org.apache.poi.hssf.util.HSSFColor; +import org.apache.poi.hssf.util.Region; +/** + * + * @author Dmitriy Kumshayev + */ +public final class TestHSSFConditionalFormatting extends TestCase +{ + public void testLastAndFirstColumns() + { + HSSFWorkbook workbook = new HSSFWorkbook(); + HSSFSheet sheet = workbook.createSheet(); + String formula = "7"; + + HSSFFontFormatting fontFmt = new HSSFFontFormatting(); + fontFmt.setFontStyle(true, false); + + HSSFBorderFormatting bordFmt = new HSSFBorderFormatting(); + bordFmt.setBorderBottom(HSSFBorderFormatting.BORDER_THIN); + bordFmt.setBorderTop(HSSFBorderFormatting.BORDER_THICK); + bordFmt.setBorderLeft(HSSFBorderFormatting.BORDER_DASHED); + bordFmt.setBorderRight(HSSFBorderFormatting.BORDER_DOTTED); + + HSSFPatternFormatting patternFmt = new HSSFPatternFormatting(); + patternFmt.setFillBackgroundColor(HSSFColor.RED.index); + + HSSFConditionalFormattingRule [] cfRules = + { + sheet.createConditionalFormattingRule(formula, fontFmt, bordFmt, patternFmt), + sheet.createConditionalFormattingRule(ComparisonOperator.BETWEEN, "1", "2", fontFmt, bordFmt, patternFmt) + }; + + short col = 1; + Region [] regions = + { + new Region(0,col,65535,col) + }; + + sheet.addConditionalFormatting(regions, cfRules); + sheet.addConditionalFormatting(regions, cfRules); + + // Verification + assertEquals(2, sheet.getNumConditionalFormattings()); + sheet.removeConditionalFormatting(1); + assertEquals(1, sheet.getNumConditionalFormattings()); + HSSFConditionalFormatting cf = sheet.getConditionalFormattingAt(0); + assertNotNull(cf); + + regions = cf.getFormattingRegions(); + assertNotNull(regions); + assertEquals(1, regions.length); + Region r = regions[0]; + assertEquals(1, r.getColumnFrom()); + assertEquals(1, r.getColumnTo()); + assertEquals(0, r.getRowFrom()); + assertEquals(65535, r.getRowTo()); + + assertEquals(2, cf.getNumberOfRules()); + + HSSFConditionalFormattingRule rule1 = cf.getRule(0); + assertEquals("7",rule1.getFormula1()); + assertNull(rule1.getFormula2()); + + HSSFConditionalFormattingRule rule2 = cf.getRule(1); + assertEquals("2",rule2.getFormula2()); + assertEquals("1",rule2.getFormula1()); + } +} diff --git a/src/testcases/org/apache/poi/hssf/usermodel/TestHSSFConfditionalFormatting.java b/src/testcases/org/apache/poi/hssf/usermodel/TestHSSFConfditionalFormatting.java deleted file mode 100644 index 6186d227ef..0000000000 --- a/src/testcases/org/apache/poi/hssf/usermodel/TestHSSFConfditionalFormatting.java +++ /dev/null @@ -1,90 +0,0 @@ -/* ==================================================================== - 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.hssf.usermodel; - -import junit.framework.TestCase; - -import org.apache.poi.hssf.record.CFRuleRecord.ComparisonOperator; -import org.apache.poi.hssf.util.HSSFColor; -import org.apache.poi.hssf.util.Region; -/** - * - * @author Dmitriy Kumshayev - */ -public final class TestHSSFConfditionalFormatting extends TestCase -{ - public void testLastAndFirstColumns() - { - HSSFWorkbook workbook = new HSSFWorkbook(); - HSSFSheet sheet = workbook.createSheet(); - String formula = "7"; - - HSSFFontFormatting fontFmt = new HSSFFontFormatting(); - fontFmt.setFontStyle(true, false); - - HSSFBorderFormatting bordFmt = new HSSFBorderFormatting(); - bordFmt.setBorderBottom(HSSFBorderFormatting.BORDER_THIN); - bordFmt.setBorderTop(HSSFBorderFormatting.BORDER_THICK); - bordFmt.setBorderLeft(HSSFBorderFormatting.BORDER_DASHED); - bordFmt.setBorderRight(HSSFBorderFormatting.BORDER_DOTTED); - - HSSFPatternFormatting patternFmt = new HSSFPatternFormatting(); - patternFmt.setFillBackgroundColor(HSSFColor.RED.index); - - HSSFConditionalFormattingRule [] cfRules = - { - sheet.createConditionalFormattingRule(formula, fontFmt, bordFmt, patternFmt), - sheet.createConditionalFormattingRule(ComparisonOperator.BETWEEN, "1", "2", fontFmt, bordFmt, patternFmt) - }; - - short col = 1; - Region [] regions = - { - new Region(0,col,-1,col) - }; - - sheet.addConditionalFormatting(regions, cfRules); - sheet.addConditionalFormatting(regions, cfRules); - - // Verification - assertEquals(2, sheet.getNumConditionalFormattings()); - sheet.removeConditionalFormatting(1); - assertEquals(1, sheet.getNumConditionalFormattings()); - HSSFConditionalFormatting cf = sheet.getConditionalFormattingAt(0); - assertNotNull(cf); - - regions = cf.getFormattingRegions(); - assertNotNull(regions); - assertEquals(1, regions.length); - Region r = regions[0]; - assertEquals(1, r.getColumnFrom()); - assertEquals(1, r.getColumnTo()); - assertEquals(0, r.getRowFrom()); - assertEquals(-1, r.getRowTo()); - - assertEquals(2, cf.getNumberOfRules()); - - HSSFConditionalFormattingRule rule1 = cf.getRule(0); - assertEquals("7",rule1.getFormula1()); - assertNull(rule1.getFormula2()); - - HSSFConditionalFormattingRule rule2 = cf.getRule(1); - assertEquals("2",rule2.getFormula2()); - assertEquals("1",rule2.getFormula1()); - } -} diff --git a/src/testcases/org/apache/poi/hssf/usermodel/TestHSSFSheet.java b/src/testcases/org/apache/poi/hssf/usermodel/TestHSSFSheet.java index a4a31feabf..13eafa4fd8 100644 --- a/src/testcases/org/apache/poi/hssf/usermodel/TestHSSFSheet.java +++ b/src/testcases/org/apache/poi/hssf/usermodel/TestHSSFSheet.java @@ -870,5 +870,65 @@ public class TestHSSFSheet public static void main(java.lang.String[] args) { junit.textui.TestRunner.run(TestHSSFSheet.class); - } + } + + public void testColumnWidth() throws Exception { + //check we can correctly read column widths from a reference workbook + String filename = System.getProperty("HSSF.testdata.path") + "/colwidth.xls"; + HSSFWorkbook wb = new HSSFWorkbook(new FileInputStream(filename)); + + //reference values + int[] ref = {365, 548, 731, 914, 1097, 1280, 1462, 1645, 1828, 2011, 2194, 2377, 2560, 2742, 2925, 3108, 3291, 3474, 3657}; + + HSSFSheet sh = wb.getSheetAt(0); + for (char i = 'A'; i <= 'S'; i++) { + int idx = i - 'A'; + int w = sh.getColumnWidth((short)idx); + assertEquals(ref[idx], w); + } + + //the second sheet doesn't have overridden column widths + sh = wb.getSheetAt(1); + int def_width = sh.getDefaultColumnWidth(); + for (char i = 'A'; i <= 'S'; i++) { + int idx = i - 'A'; + int w = sh.getColumnWidth((short)idx); + //getDefaultColumnWidth returns width measued in characters + //getColumnWidth returns width measued in 1/256th units + assertEquals(def_width*256, w); + } + + //test new workbook + wb = new HSSFWorkbook(); + sh = wb.createSheet(); + sh.setDefaultColumnWidth((short)10); + assertEquals(10, sh.getDefaultColumnWidth()); + assertEquals(256*10, sh.getColumnWidth((short)0)); + assertEquals(256*10, sh.getColumnWidth((short)1)); + assertEquals(256*10, sh.getColumnWidth((short)2)); + for (char i = 'D'; i <= 'F'; i++) { + short w = (short)(256*12); + sh.setColumnWidth((short)i, w); + assertEquals(w, sh.getColumnWidth((short)i)); + } + + //serialize and read again + ByteArrayOutputStream out = new ByteArrayOutputStream(); + wb.write(out); + out.close(); + + wb = new HSSFWorkbook(new ByteArrayInputStream(out.toByteArray())); + sh = wb.getSheetAt(0); + assertEquals(10, sh.getDefaultColumnWidth()); + //columns A-C have default width + assertEquals(256*10, sh.getColumnWidth((short)0)); + assertEquals(256*10, sh.getColumnWidth((short)1)); + assertEquals(256*10, sh.getColumnWidth((short)2)); + //columns D-F have custom wodth + for (char i = 'D'; i <= 'F'; i++) { + short w = (short)(256*12); + assertEquals(w, sh.getColumnWidth((short)i)); + } + + } }