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

........
  r680530 | nick | 2008-07-28 23:10:07 +0100 (Mon, 28 Jul 2008) | 1 line

  Some work on bug #45466 - Partial support for removing excel comments (won't work for all excel versions yet)
........
  r680853 | nick | 2008-07-29 22:40:47 +0100 (Tue, 29 Jul 2008) | 1 line

  Support for creating new HSLF CurrentUserAtoms
........
  r681530 | josh | 2008-07-31 23:44:48 +0100 (Thu, 31 Jul 2008) | 1 line

  Fix for bug 45519 - keep data validation records together
........
  r681572 | josh | 2008-08-01 02:04:28 +0100 (Fri, 01 Aug 2008) | 1 line

  Small update for c681530 bug 45519
........
  r682225 | josh | 2008-08-03 23:11:26 +0100 (Sun, 03 Aug 2008) | 1 line

  Extensive fixes for data validation (bug 44953)
........
  r682227 | josh | 2008-08-03 23:15:46 +0100 (Sun, 03 Aug 2008) | 1 line

  should have been submitted with c682225 - Extensive fixes for data validation (bug 44953)
........
  r682229 | josh | 2008-08-03 23:49:58 +0100 (Sun, 03 Aug 2008) | 1 line

  fixed BiffViewer to add some missing record types. Formatted switch/case for readability
........
  r682230 | josh | 2008-08-04 00:13:17 +0100 (Mon, 04 Aug 2008) | 1 line

  Small tweaks for data validation (bug 44953)
........
  r682282 | josh | 2008-08-04 09:00:11 +0100 (Mon, 04 Aug 2008) | 1 line

  Consolidating various duplicates of CellRangeAddress
........
  r682336 | yegor | 2008-08-04 12:40:25 +0100 (Mon, 04 Aug 2008) | 1 line

  support for headers / footers in HSLF
........

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

71 files changed:
src/contrib/src/org/apache/poi/hssf/usermodel/contrib/HSSFRegionUtil.java
src/documentation/content/xdocs/changes.xml
src/documentation/content/xdocs/status.xml
src/java/org/apache/poi/hssf/dev/BiffViewer.java
src/java/org/apache/poi/hssf/dev/HSSF.java
src/java/org/apache/poi/hssf/model/FormulaParser.java
src/java/org/apache/poi/hssf/model/OperandClassTransformer.java
src/java/org/apache/poi/hssf/model/RecordStream.java
src/java/org/apache/poi/hssf/model/Sheet.java
src/java/org/apache/poi/hssf/record/CFHeaderRecord.java
src/java/org/apache/poi/hssf/record/DVRecord.java
src/java/org/apache/poi/hssf/record/MergeCellsRecord.java
src/java/org/apache/poi/hssf/record/SelectionRecord.java
src/java/org/apache/poi/hssf/record/aggregates/CFRecordsAggregate.java
src/java/org/apache/poi/hssf/record/aggregates/DataValidityTable.java [new file with mode: 0644]
src/java/org/apache/poi/hssf/record/aggregates/RecordAggregate.java [new file with mode: 0644]
src/java/org/apache/poi/hssf/record/cf/CellRange.java [deleted file]
src/java/org/apache/poi/hssf/record/cf/CellRangeUtil.java [new file with mode: 0644]
src/java/org/apache/poi/hssf/record/formula/NumberPtg.java
src/java/org/apache/poi/hssf/record/formula/Ptg.java
src/java/org/apache/poi/hssf/usermodel/DVConstraint.java [new file with mode: 0644]
src/java/org/apache/poi/hssf/usermodel/HSSFCell.java
src/java/org/apache/poi/hssf/usermodel/HSSFComment.java
src/java/org/apache/poi/hssf/usermodel/HSSFConditionalFormatting.java
src/java/org/apache/poi/hssf/usermodel/HSSFDataValidation.java [new file with mode: 0644]
src/java/org/apache/poi/hssf/usermodel/HSSFSheet.java
src/java/org/apache/poi/hssf/usermodel/HSSFSheetConditionalFormatting.java
src/java/org/apache/poi/hssf/util/CellRangeAddress.java [new file with mode: 0644]
src/java/org/apache/poi/hssf/util/CellRangeAddressList.java [new file with mode: 0644]
src/java/org/apache/poi/hssf/util/HSSFCellRangeAddress.java [deleted file]
src/java/org/apache/poi/hssf/util/HSSFDataValidation.java [deleted file]
src/java/org/apache/poi/hssf/util/Region.java
src/java/org/apache/poi/ss/usermodel/DateUtil.java
src/java/org/apache/poi/ss/util/CellRangeAddress.java [new file with mode: 0644]
src/java/org/apache/poi/ss/util/CellRangeAddressList.java [new file with mode: 0644]
src/java/org/apache/poi/ss/util/Region.java
src/scratchpad/examples/src/org/apache/poi/hslf/examples/HeadersFootersDemo.java [new file with mode: 0644]
src/scratchpad/src/org/apache/poi/hslf/model/HeadersFooters.java [new file with mode: 0644]
src/scratchpad/src/org/apache/poi/hslf/model/Slide.java
src/scratchpad/src/org/apache/poi/hslf/record/CString.java
src/scratchpad/src/org/apache/poi/hslf/record/Comment2000.java
src/scratchpad/src/org/apache/poi/hslf/record/CurrentUserAtom.java
src/scratchpad/src/org/apache/poi/hslf/record/ExEmbed.java
src/scratchpad/src/org/apache/poi/hslf/record/ExHyperlink.java
src/scratchpad/src/org/apache/poi/hslf/record/HeadersFootersAtom.java [new file with mode: 0644]
src/scratchpad/src/org/apache/poi/hslf/record/HeadersFootersContainer.java [new file with mode: 0644]
src/scratchpad/src/org/apache/poi/hslf/record/RecordTypes.java
src/scratchpad/src/org/apache/poi/hslf/usermodel/SlideShow.java
src/scratchpad/testcases/org/apache/poi/hslf/data/headers_footers.ppt [new file with mode: 0644]
src/scratchpad/testcases/org/apache/poi/hslf/model/TestHeadersFooters.java [new file with mode: 0644]
src/scratchpad/testcases/org/apache/poi/hslf/record/TestCString.java
src/scratchpad/testcases/org/apache/poi/hslf/record/TestCurrentUserAtom.java [new file with mode: 0644]
src/scratchpad/testcases/org/apache/poi/hslf/record/TestHeadersFootersAtom.java [new file with mode: 0644]
src/scratchpad/testcases/org/apache/poi/hslf/record/TestHeadersFootersContainer.java [new file with mode: 0644]
src/testcases/org/apache/poi/hssf/data/dvEmpty.xls [new file with mode: 0644]
src/testcases/org/apache/poi/hssf/model/TestSheet.java
src/testcases/org/apache/poi/hssf/model/TestSheetAdditional.java
src/testcases/org/apache/poi/hssf/record/TestCFHeaderRecord.java
src/testcases/org/apache/poi/hssf/record/TestMergeCellsRecord.java
src/testcases/org/apache/poi/hssf/record/aggregates/TestCFRecordsAggregate.java
src/testcases/org/apache/poi/hssf/record/cf/TestCellRange.java
src/testcases/org/apache/poi/hssf/usermodel/StreamUtility.java [new file with mode: 0644]
src/testcases/org/apache/poi/hssf/usermodel/TestBugs.java
src/testcases/org/apache/poi/hssf/usermodel/TestCloneSheet.java
src/testcases/org/apache/poi/hssf/usermodel/TestDataValidation.java
src/testcases/org/apache/poi/hssf/usermodel/TestHSSFComment.java
src/testcases/org/apache/poi/hssf/usermodel/TestHSSFConditionalFormatting.java
src/testcases/org/apache/poi/hssf/usermodel/TestHSSFDataFormatter.java
src/testcases/org/apache/poi/hssf/usermodel/TestHSSFDateUtil.java
src/testcases/org/apache/poi/hssf/usermodel/TestHSSFSheet.java
src/testcases/org/apache/poi/hssf/usermodel/TestWorkbook.java

index 53e176c076443e182405d22483b3f3b0e3d9e4d5..be35b0425505397f8fec7a1512e15cc0865a5973 100644 (file)
@@ -21,197 +21,257 @@ import org.apache.poi.hssf.usermodel.HSSFCell;
 import org.apache.poi.hssf.usermodel.HSSFRow;
 import org.apache.poi.hssf.usermodel.HSSFSheet;
 import org.apache.poi.hssf.usermodel.HSSFWorkbook;
-
+import org.apache.poi.ss.util.CellRangeAddress;
 import org.apache.poi.ss.util.Region;
 
 /**
- *  Various utility functions that make working with a region of cells easier.
- *
- *@author     Eric Pugh epugh@upstate.com
- *@since      July 29, 2002
+ * Various utility functions that make working with a region of cells easier.
+ * 
+ * @author Eric Pugh epugh@upstate.com
  */
-public final class HSSFRegionUtil
-{
-
-  /**  Constructor for the HSSFRegionUtil object */
-  private HSSFRegionUtil() {
-      // no instances of this class
-  }
-
-  /**
-   *  Sets the left border for a region of cells by manipulating the cell style
-   *  of the individual cells on the left
-   *
-   *@param  border                 The new border
-   *@param  region                 The region that should have the border
-   *@param  workbook               The workbook that the region is on.
-   *@param  sheet                  The sheet that the region is on.
-   */
-  public static void setBorderLeft( short border, Region region, HSSFSheet sheet, HSSFWorkbook workbook )
-  {
-    int rowStart = region.getRowFrom();
-    int rowEnd = region.getRowTo();
-    int column = region.getColumnFrom();
-
-    for ( int i = rowStart; i <= rowEnd; i++ ) {
-      HSSFRow row = HSSFCellUtil.getRow( i, sheet );
-      HSSFCell cell = HSSFCellUtil.getCell( row, column );
-      HSSFCellUtil.setCellStyleProperty(
-          cell, workbook, HSSFCellUtil.BORDER_LEFT, new Short( border ) );
-    }
-  }
-
-  /**
-   *  Sets the leftBorderColor attribute of the HSSFRegionUtil object
-   *
-   *@param  color                  The color of the border
-   *@param  region                 The region that should have the border
-   *@param  workbook               The workbook that the region is on.
-   *@param  sheet                  The sheet that the region is on.
-   */
-  public static void setLeftBorderColor( short color, Region region, HSSFSheet sheet, HSSFWorkbook workbook )
-  {
-    int rowStart = region.getRowFrom();
-    int rowEnd = region.getRowTo();
-    int column = region.getColumnFrom();
-
-    for ( int i = rowStart; i <= rowEnd; i++ ) {
-      HSSFRow row = HSSFCellUtil.getRow( i, sheet );
-      HSSFCell cell = HSSFCellUtil.getCell( row, column );
-      HSSFCellUtil.setCellStyleProperty(
-          cell, workbook, HSSFCellUtil.LEFT_BORDER_COLOR, new Short( color ) );
-    }
-  }
-
-  /**
-   *  Sets the borderRight attribute of the HSSFRegionUtil object
-   *
-   *@param  border                 The new border
-   *@param  region                 The region that should have the border
-   *@param  workbook               The workbook that the region is on.
-   *@param  sheet                  The sheet that the region is on.
-   */
-  public static void setBorderRight( short border, Region region, HSSFSheet sheet, HSSFWorkbook workbook )
-  {
-    int rowStart = region.getRowFrom();
-    int rowEnd = region.getRowTo();
-    int column = region.getColumnTo();
-
-    for ( int i = rowStart; i <= rowEnd; i++ ) {
-      HSSFRow row = HSSFCellUtil.getRow( i, sheet );
-      HSSFCell cell = HSSFCellUtil.getCell( row, column );
-
-      HSSFCellUtil.setCellStyleProperty(
-          cell, workbook, HSSFCellUtil.BORDER_RIGHT, new Short( border ) );
-    }
-  }
-
-  /**
-   *  Sets the rightBorderColor attribute of the HSSFRegionUtil object
-   *
-   *@param  color                  The color of the border
-   *@param  region                 The region that should have the border
-   *@param  workbook               The workbook that the region is on.
-   *@param  sheet                  The sheet that the region is on.
-   */
-  public static void setRightBorderColor( short color, Region region, HSSFSheet sheet, HSSFWorkbook workbook )
-  {
-    int rowStart = region.getRowFrom();
-    int rowEnd = region.getRowTo();
-    int column = region.getColumnTo();
-
-    for ( int i = rowStart; i <= rowEnd; i++ ) {
-      HSSFRow row = HSSFCellUtil.getRow( i, sheet );
-      HSSFCell cell = HSSFCellUtil.getCell( row, column );
-      HSSFCellUtil.setCellStyleProperty(
-          cell, workbook, HSSFCellUtil.RIGHT_BORDER_COLOR, new Short( color ) );
-    }
-  }
-
-  /**
-   *  Sets the borderBottom attribute of the HSSFRegionUtil object
-   *
-   *@param  border                 The new border
-   *@param  region                 The region that should have the border
-   *@param  workbook               The workbook that the region is on.
-   *@param  sheet                  The sheet that the region is on.
-   */
-  public static void setBorderBottom( short border, Region region, HSSFSheet sheet, HSSFWorkbook workbook )
-  {
-    int colStart = region.getColumnFrom();
-    int colEnd = region.getColumnTo();
-    int rowIndex = region.getRowTo();
-    HSSFRow row = HSSFCellUtil.getRow( rowIndex, sheet );
-    for ( int i = colStart; i <= colEnd; i++ ) {
-
-      HSSFCell cell = HSSFCellUtil.getCell( row, i );
-      HSSFCellUtil.setCellStyleProperty(
-          cell, workbook, HSSFCellUtil.BORDER_BOTTOM, new Short( border ) );
-    }
-  }
-
-  /**
-   *  Sets the bottomBorderColor attribute of the HSSFRegionUtil object
-   *
-   *@param  color                  The color of the border
-   *@param  region                 The region that should have the border
-   *@param  workbook               The workbook that the region is on.
-   *@param  sheet                  The sheet that the region is on.
-   */
-  public static void setBottomBorderColor( short color, Region region, HSSFSheet sheet, HSSFWorkbook workbook )
-  {
-    int colStart = region.getColumnFrom();
-    int colEnd = region.getColumnTo();
-    int rowIndex = region.getRowTo();
-    HSSFRow row = HSSFCellUtil.getRow( rowIndex, sheet );
-    for ( int i = colStart; i <= colEnd; i++ ) {
-      HSSFCell cell = HSSFCellUtil.getCell( row, i );
-      HSSFCellUtil.setCellStyleProperty(
-          cell, workbook, HSSFCellUtil.BOTTOM_BORDER_COLOR, new Short( color ) );
-    }
-  }
-
-  /**
-   *  Sets the borderBottom attribute of the HSSFRegionUtil object
-   *
-   *@param  border                 The new border
-   *@param  region                 The region that should have the border
-   *@param  workbook               The workbook that the region is on.
-   *@param  sheet                  The sheet that the region is on.
-   */
-  public static void setBorderTop( short border, Region region, HSSFSheet sheet, HSSFWorkbook workbook )
-  {
-    int colStart = region.getColumnFrom();
-    int colEnd = region.getColumnTo();
-    int rowIndex = region.getRowFrom();
-    HSSFRow row = HSSFCellUtil.getRow( rowIndex, sheet );
-    for ( int i = colStart; i <= colEnd; i++ ) {
-
-      HSSFCell cell = HSSFCellUtil.getCell( row, i );
-      HSSFCellUtil.setCellStyleProperty(
-          cell, workbook, HSSFCellUtil.BORDER_TOP, new Short( border ) );
-    }
-  }
-
-  /**
-   *  Sets the topBorderColor attribute of the HSSFRegionUtil object
-   *
-   *@param  color                  The color of the border
-   *@param  region                 The region that should have the border
-   *@param  workbook               The workbook that the region is on.
-   *@param  sheet                  The sheet that the region is on.
-   */
-  public static void setTopBorderColor( short color, Region region, HSSFSheet sheet, HSSFWorkbook workbook )
-  {
-    int colStart = region.getColumnFrom();
-    int colEnd = region.getColumnTo();
-    int rowIndex = region.getRowFrom();
-    HSSFRow row = HSSFCellUtil.getRow( rowIndex, sheet );
-    for ( int i = colStart; i <= colEnd; i++ ) {
-      HSSFCell cell = HSSFCellUtil.getCell( row, i );
-      HSSFCellUtil.setCellStyleProperty(
-          cell, workbook, HSSFCellUtil.TOP_BORDER_COLOR, new Short( color ) );
-    }
-  }
-}
+public final class HSSFRegionUtil {
+
+       private HSSFRegionUtil() {
+               // no instances of this class
+       }
+       /**
+        * For setting the same property on many cells to the same value
+        */
+       private static final class CellPropertySetter {
+
+               private final HSSFWorkbook _workbook;
+               private final String _propertyName;
+               private final Short _propertyValue;
+
+               public CellPropertySetter(HSSFWorkbook workbook, String propertyName, int value) {
+                       _workbook = workbook;
+                       _propertyName = propertyName;
+                       _propertyValue = new Short((short)value);
+               }
+               public void setProperty(HSSFRow row, int column) {
+                       HSSFCell cell = HSSFCellUtil.getCell(row, column);
+                       HSSFCellUtil.setCellStyleProperty(cell, _workbook, _propertyName, _propertyValue);
+               }
+       }
+
+       private static CellRangeAddress toCRA(Region region) {
+               return Region.convertToCellRangeAddress(region);
+       }
+
+       /**
+        * @deprecated (Aug 2008) use {@link CellRangeAddress} instead of {@link Region}
+        */
+       public static void setBorderLeft(short border, Region region, HSSFSheet sheet,
+                       HSSFWorkbook workbook) {
+               setBorderLeft(border, toCRA(region), sheet, workbook);
+       }
+       /**
+        * Sets the left border for a region of cells by manipulating the cell style
+        * of the individual cells on the left
+        * 
+        * @param border The new border
+        * @param region The region that should have the border
+        * @param workbook The workbook that the region is on.
+        * @param sheet The sheet that the region is on.
+        */
+       public static void setBorderLeft(int border, CellRangeAddress region, HSSFSheet sheet,
+                       HSSFWorkbook workbook) {
+               int rowStart = region.getFirstRow();
+               int rowEnd = region.getLastRow();
+               int column = region.getFirstColumn();
+
+               CellPropertySetter cps = new CellPropertySetter(workbook, HSSFCellUtil.BORDER_LEFT, border);
+               for (int i = rowStart; i <= rowEnd; i++) {
+                       cps.setProperty(HSSFCellUtil.getRow(i, sheet), column);
+               }
+       }
+
+       /**
+        * @deprecated (Aug 2008) use {@link CellRangeAddress} instead of {@link Region}
+        */
+       public static void setLeftBorderColor(short color, Region region, HSSFSheet sheet,
+                       HSSFWorkbook workbook) {
+               setLeftBorderColor(color, toCRA(region), sheet, workbook);
+       }
+       /**
+        * Sets the leftBorderColor attribute of the HSSFRegionUtil object
+        * 
+        * @param color The color of the border
+        * @param region The region that should have the border
+        * @param workbook The workbook that the region is on.
+        * @param sheet The sheet that the region is on.
+        */
+       public static void setLeftBorderColor(int color, CellRangeAddress region, HSSFSheet sheet,
+                       HSSFWorkbook workbook) {
+               int rowStart = region.getFirstRow();
+               int rowEnd = region.getLastRow();
+               int column = region.getFirstColumn();
 
+               CellPropertySetter cps = new CellPropertySetter(workbook, HSSFCellUtil.LEFT_BORDER_COLOR, color);
+               for (int i = rowStart; i <= rowEnd; i++) {
+                       cps.setProperty(HSSFCellUtil.getRow(i, sheet), column);
+               }
+       }
+
+       /**
+        * @deprecated (Aug 2008) use {@link CellRangeAddress} instead of {@link Region}
+        */
+       public static void setBorderRight(short border, Region region, HSSFSheet sheet,
+                       HSSFWorkbook workbook) {
+               setBorderRight(border, toCRA(region), sheet, workbook);
+       }
+       /**
+        * Sets the borderRight attribute of the HSSFRegionUtil object
+        * 
+        * @param border The new border
+        * @param region The region that should have the border
+        * @param workbook The workbook that the region is on.
+        * @param sheet The sheet that the region is on.
+        */
+       public static void setBorderRight(int border, CellRangeAddress region, HSSFSheet sheet,
+                       HSSFWorkbook workbook) {
+               int rowStart = region.getFirstRow();
+               int rowEnd = region.getLastRow();
+               int column = region.getLastColumn();
+
+               CellPropertySetter cps = new CellPropertySetter(workbook, HSSFCellUtil.BORDER_RIGHT, border);
+               for (int i = rowStart; i <= rowEnd; i++) {
+                       cps.setProperty(HSSFCellUtil.getRow(i, sheet), column);
+               }
+       }
+
+       /**
+        * @deprecated (Aug 2008) use {@link CellRangeAddress} instead of {@link Region}
+        */
+       public static void setRightBorderColor(short color, Region region, HSSFSheet sheet,
+                       HSSFWorkbook workbook) {
+               setRightBorderColor(color, toCRA(region), sheet, workbook);
+       }
+       /**
+        * Sets the rightBorderColor attribute of the HSSFRegionUtil object
+        * 
+        * @param color The color of the border
+        * @param region The region that should have the border
+        * @param workbook The workbook that the region is on.
+        * @param sheet The sheet that the region is on.
+        */
+       public static void setRightBorderColor(int color, CellRangeAddress region, HSSFSheet sheet,
+                       HSSFWorkbook workbook) {
+               int rowStart = region.getFirstRow();
+               int rowEnd = region.getLastRow();
+               int column = region.getLastColumn();
+
+               CellPropertySetter cps = new CellPropertySetter(workbook, HSSFCellUtil.RIGHT_BORDER_COLOR, color);
+               for (int i = rowStart; i <= rowEnd; i++) {
+                       cps.setProperty(HSSFCellUtil.getRow(i, sheet), column);
+               }
+       }
+
+       /**
+        * @deprecated (Aug 2008) use {@link CellRangeAddress} instead of {@link Region}
+        */
+       public static void setBorderBottom(short border, Region region, HSSFSheet sheet,
+                       HSSFWorkbook workbook) {
+               setBorderBottom(border, toCRA(region), sheet, workbook);
+       }
+       /**
+        * Sets the borderBottom attribute of the HSSFRegionUtil object
+        * 
+        * @param border The new border
+        * @param region The region that should have the border
+        * @param workbook The workbook that the region is on.
+        * @param sheet The sheet that the region is on.
+        */
+       public static void setBorderBottom(int border, CellRangeAddress region, HSSFSheet sheet,
+                       HSSFWorkbook workbook) {
+               int colStart = region.getFirstColumn();
+               int colEnd = region.getLastColumn();
+               int rowIndex = region.getLastRow();
+               CellPropertySetter cps = new CellPropertySetter(workbook, HSSFCellUtil.BORDER_BOTTOM, border);
+               HSSFRow row = HSSFCellUtil.getRow(rowIndex, sheet);
+               for (int i = colStart; i <= colEnd; i++) {
+                       cps.setProperty(row, i);
+               }
+       }
+
+       /**
+        * @deprecated (Aug 2008) use {@link CellRangeAddress} instead of {@link Region}
+        */
+       public static void setBottomBorderColor(short color, Region region, HSSFSheet sheet,
+                       HSSFWorkbook workbook) {
+               setBottomBorderColor(color, toCRA(region), sheet, workbook);
+       }
+       /**
+        * Sets the bottomBorderColor attribute of the HSSFRegionUtil object
+        * 
+        * @param color The color of the border
+        * @param region The region that should have the border
+        * @param workbook The workbook that the region is on.
+        * @param sheet The sheet that the region is on.
+        */
+       public static void setBottomBorderColor(int color, CellRangeAddress region, HSSFSheet sheet,
+                       HSSFWorkbook workbook) {
+               int colStart = region.getFirstColumn();
+               int colEnd = region.getLastColumn();
+               int rowIndex = region.getLastRow();
+               CellPropertySetter cps = new CellPropertySetter(workbook, HSSFCellUtil.BOTTOM_BORDER_COLOR, color);
+               HSSFRow row = HSSFCellUtil.getRow(rowIndex, sheet);
+               for (int i = colStart; i <= colEnd; i++) {
+                       cps.setProperty(row, i);
+               }
+       }
+
+       /**
+        * @deprecated (Aug 2008) use {@link CellRangeAddress} instead of {@link Region}
+        */
+       public static void setBorderTop(short border, Region region, HSSFSheet sheet,
+                       HSSFWorkbook workbook) {
+               setBorderTop(border, toCRA(region), sheet, workbook);
+       }
+       /**
+        * Sets the borderBottom attribute of the HSSFRegionUtil object
+        * 
+        * @param border The new border
+        * @param region The region that should have the border
+        * @param workbook The workbook that the region is on.
+        * @param sheet The sheet that the region is on.
+        */
+       public static void setBorderTop(int border, CellRangeAddress region, HSSFSheet sheet,
+                       HSSFWorkbook workbook) {
+               int colStart = region.getFirstColumn();
+               int colEnd = region.getLastColumn();
+               int rowIndex = region.getFirstRow();
+               CellPropertySetter cps = new CellPropertySetter(workbook, HSSFCellUtil.BORDER_TOP, border);
+               HSSFRow row = HSSFCellUtil.getRow(rowIndex, sheet);
+               for (int i = colStart; i <= colEnd; i++) {
+                       cps.setProperty(row, i);
+               }
+       }
+
+       /**
+        * @deprecated (Aug 2008) use {@link CellRangeAddress} instead of {@link Region}
+        */
+       public static void setTopBorderColor(short color, Region region, HSSFSheet sheet,
+                       HSSFWorkbook workbook) {
+               setTopBorderColor(color, toCRA(region), sheet, workbook);
+       }
+       /**
+        * Sets the topBorderColor attribute of the HSSFRegionUtil object
+        * 
+        * @param color The color of the border
+        * @param region  The region that should have the border
+        * @param workbook The workbook that the region is on.
+        * @param sheet The sheet that the region is on.
+        */
+       public static void setTopBorderColor(int color, CellRangeAddress region, HSSFSheet sheet,
+                       HSSFWorkbook workbook) {
+               int colStart = region.getFirstColumn();
+               int colEnd = region.getLastColumn();
+               int rowIndex = region.getFirstRow();
+               CellPropertySetter cps = new CellPropertySetter(workbook, HSSFCellUtil.TOP_BORDER_COLOR, color);
+               HSSFRow row = HSSFCellUtil.getRow(rowIndex, sheet);
+               for (int i = colStart; i <= colEnd; i++) {
+                       cps.setProperty(row, i);
+               }
+       }
+}
index d7f6d17324b256735d9f651eda1c57d66f1a8d36..8e167940bc9350c2d40afdb72d41631552536beb 100644 (file)
            <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.1-alpha1" date="2008-??-??">
+           <action dev="POI-DEVELOPERS" type="add">Support for Headers / Footers in HSLF</action>
+           <action dev="POI-DEVELOPERS" type="fix">44953 - Extensive fixes for data validation</action>
+           <action dev="POI-DEVELOPERS" type="fix">45519 - Fixed to keep datavalidation records together</action>
+           <action dev="POI-DEVELOPERS" type="add">Support for creating new HSLF CurrentUserAtoms</action>
+           <action dev="POI-DEVELOPERS" type="add">45466 - Partial support for removing excel comments (won't work for all excel versions yet)</action>
            <action dev="POI-DEVELOPERS" type="fix">45437 - Detect encrypted word documents, and throw an EncryptedDocumentException instead of a OOM</action>
            <action dev="POI-DEVELOPERS" type="add">45404 - New class, hssf.usermodel.HSSFDataFormatter, for formatting numbers and dates in the same way that Excel does</action>
            <action dev="POI-DEVELOPERS" type="fix">45414 - Don't add too many UncalcedRecords to sheets with charts in them</action>
index 208799a0048ed360ed809defc9f541dccc14e3e6..790390b0289274c83d83d856d63e29ca7e18d47d 100644 (file)
            <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.1-alpha1" date="2008-??-??">
+           <action dev="POI-DEVELOPERS" type="add">Support for Headers / Footers in HSLF</action>
+           <action dev="POI-DEVELOPERS" type="fix">44953 - Extensive fixes for data validation</action>
+           <action dev="POI-DEVELOPERS" type="fix">45519 - Fixed to keep datavalidation records together</action>
+           <action dev="POI-DEVELOPERS" type="add">Support for creating new HSLF CurrentUserAtoms</action>
+           <action dev="POI-DEVELOPERS" type="add">45466 - Partial support for removing excel comments (won't work for all excel versions yet)</action>
            <action dev="POI-DEVELOPERS" type="fix">45437 - Detect encrypted word documents, and throw an EncryptedDocumentException instead of a OOM</action>
            <action dev="POI-DEVELOPERS" type="add">45404 - New class, hssf.usermodel.HSSFDataFormatter, for formatting numbers and dates in the same way that Excel does</action>
            <action dev="POI-DEVELOPERS" type="fix">45414 - Don't add too many UncalcedRecords to sheets with charts in them</action>
index 85b57f089f17a55341fca0b0caa26075d5f66c84..175b920881c5eb8774e7b3bf6a7c8bb5796ae629 100644 (file)
@@ -113,262 +113,138 @@ public final class BiffViewer {
     {
         switch ( in.getSid() )
         {
-            case ChartRecord.sid:
-                return new ChartRecord( in );
-            case ChartFormatRecord.sid:
-                return new ChartFormatRecord( in );
-            case SeriesRecord.sid:
-                return new SeriesRecord( in );
-            case BeginRecord.sid:
-                return new BeginRecord( in );
-            case EndRecord.sid:
-                return new EndRecord( in );
-            case BOFRecord.sid:
-                return new BOFRecord( in );
-            case InterfaceHdrRecord.sid:
-                return new InterfaceHdrRecord( in );
-            case MMSRecord.sid:
-                return new MMSRecord( in );
-            case InterfaceEndRecord.sid:
-                return new InterfaceEndRecord( in );
-            case WriteAccessRecord.sid:
-                return new WriteAccessRecord( in );
-            case CodepageRecord.sid:
-                return new CodepageRecord( in );
-            case DSFRecord.sid:
-                return new DSFRecord( in );
-            case TabIdRecord.sid:
-                return new TabIdRecord( in );
-            case FnGroupCountRecord.sid:
-                return new FnGroupCountRecord( in );
-            case WindowProtectRecord.sid:
-                return new WindowProtectRecord( in );
-            case ProtectRecord.sid:
-                return new ProtectRecord( in );
-            case PasswordRecord.sid:
-                return new PasswordRecord( in );
-            case ProtectionRev4Record.sid:
-                return new ProtectionRev4Record( in );
-            case PasswordRev4Record.sid:
-                return new PasswordRev4Record( in );
-            case WindowOneRecord.sid:
-                return new WindowOneRecord( in );
-            case BackupRecord.sid:
-                return new BackupRecord( in );
-            case HideObjRecord.sid:
-                return new HideObjRecord( in );
-            case DateWindow1904Record.sid:
-                return new DateWindow1904Record( in );
-            case PrecisionRecord.sid:
-                return new PrecisionRecord( in );
-            case RefreshAllRecord.sid:
-                return new RefreshAllRecord( in );
-            case BookBoolRecord.sid:
-                return new BookBoolRecord( in );
-            case FontRecord.sid:
-                return new FontRecord( in );
-            case FormatRecord.sid:
-                return new FormatRecord( in );
-            case ExtendedFormatRecord.sid:
-                return new ExtendedFormatRecord( in );
-            case StyleRecord.sid:
-                return new StyleRecord( in );
-            case UseSelFSRecord.sid:
-                return new UseSelFSRecord( in );
-            case BoundSheetRecord.sid:
-                return new BoundSheetRecord( in );
-            case CountryRecord.sid:
-                return new CountryRecord( in );
-            case SSTRecord.sid:
-                return new SSTRecord( in );
-            case ExtSSTRecord.sid:
-                return new ExtSSTRecord( in );
-            case EOFRecord.sid:
-                return new EOFRecord( in );
-            case IndexRecord.sid:
-                return new IndexRecord( in );
-            case CalcModeRecord.sid:
-                return new CalcModeRecord( in );
-            case CalcCountRecord.sid:
-                return new CalcCountRecord( in );
-            case RefModeRecord.sid:
-                return new RefModeRecord( in );
-            case IterationRecord.sid:
-                return new IterationRecord( in );
-            case DeltaRecord.sid:
-                return new DeltaRecord( in );
-            case SaveRecalcRecord.sid:
-                return new SaveRecalcRecord( in );
-            case PrintHeadersRecord.sid:
-                return new PrintHeadersRecord( in );
-            case PrintGridlinesRecord.sid:
-                return new PrintGridlinesRecord( in );
-            case GridsetRecord.sid:
-                return new GridsetRecord( in );
-            case DrawingGroupRecord.sid:
-                return new DrawingGroupRecord( in );
-            case DrawingRecordForBiffViewer.sid:
-                return new DrawingRecordForBiffViewer( in );
-            case DrawingSelectionRecord.sid:
-                return new DrawingSelectionRecord( in );
-            case GutsRecord.sid:
-                return new GutsRecord( in );
-            case DefaultRowHeightRecord.sid:
-                return new DefaultRowHeightRecord( in );
-            case WSBoolRecord.sid:
-                return new WSBoolRecord( in );
-            case HeaderRecord.sid:
-                return new HeaderRecord( in );
-            case FooterRecord.sid:
-                return new FooterRecord( in );
-            case HCenterRecord.sid:
-                return new HCenterRecord( in );
-            case VCenterRecord.sid:
-                return new VCenterRecord( in );
-            case PrintSetupRecord.sid:
-                return new PrintSetupRecord( in );
-            case DefaultColWidthRecord.sid:
-                return new DefaultColWidthRecord( in );
-            case DimensionsRecord.sid:
-                return new DimensionsRecord( in );
-            case RowRecord.sid:
-                return new RowRecord( in );
-            case LabelSSTRecord.sid:
-                return new LabelSSTRecord( in );
-            case RKRecord.sid:
-                return new RKRecord( in );
-            case NumberRecord.sid:
-                return new NumberRecord( in );
-            case DBCellRecord.sid:
-                return new DBCellRecord( in );
-            case WindowTwoRecord.sid:
-                return new WindowTwoRecord( in );
-            case SelectionRecord.sid:
-                return new SelectionRecord( in );
-            case ContinueRecord.sid:
-                return new ContinueRecord( in );
-            case LabelRecord.sid:
-                return new LabelRecord( in );
-            case MulRKRecord.sid:
-                return new MulRKRecord( in );
-            case MulBlankRecord.sid:
-                return new MulBlankRecord( in );
-            case BlankRecord.sid:
-                return new BlankRecord( in );
-            case BoolErrRecord.sid:
-                return new BoolErrRecord( in );
-            case ColumnInfoRecord.sid:
-                return new ColumnInfoRecord( in );
-            case MergeCellsRecord.sid:
-                return new MergeCellsRecord( in );
-            case AreaRecord.sid:
-                return new AreaRecord( in );
-            case DataFormatRecord.sid:
-                return new DataFormatRecord( in );
-            case BarRecord.sid:
-                return new BarRecord( in );
-            case DatRecord.sid:
-                return new DatRecord( in );
-            case PlotGrowthRecord.sid:
-                return new PlotGrowthRecord( in );
-            case UnitsRecord.sid:
-                return new UnitsRecord( in );
-            case FrameRecord.sid:
-                return new FrameRecord( in );
-            case ValueRangeRecord.sid:
-                return new ValueRangeRecord( in );
-            case SeriesListRecord.sid:
-                return new SeriesListRecord( in );
-            case FontBasisRecord.sid:
-                return new FontBasisRecord( in );
-            case FontIndexRecord.sid:
-                return new FontIndexRecord( in );
-            case LineFormatRecord.sid:
-                return new LineFormatRecord( in );
-            case AreaFormatRecord.sid:
-                return new AreaFormatRecord( in );
-            case LinkedDataRecord.sid:
-                return new LinkedDataRecord( in );
-            case FormulaRecord.sid:
-                return new FormulaRecord( in );
-            case SheetPropertiesRecord.sid:
-                return new SheetPropertiesRecord( in );
-            case DefaultDataLabelTextPropertiesRecord.sid:
-                return new DefaultDataLabelTextPropertiesRecord( in );
-            case TextRecord.sid:
-                return new TextRecord( in );
-            case AxisParentRecord.sid:
-                return new AxisParentRecord( in );
-            case AxisLineFormatRecord.sid:
-                return new AxisLineFormatRecord( in );
-            case SupBookRecord.sid:
-                return new SupBookRecord( in );
-            case ExternSheetRecord.sid:
-                return new ExternSheetRecord( in );
-            case SCLRecord.sid:
-                return new SCLRecord( in );
-            case SeriesToChartGroupRecord.sid:
-                return new SeriesToChartGroupRecord( in );
-            case AxisUsedRecord.sid:
-                return new AxisUsedRecord( in );
-            case AxisRecord.sid:
-                return new AxisRecord( in );
-            case CategorySeriesAxisRecord.sid:
-                return new CategorySeriesAxisRecord( in );
-            case AxisOptionsRecord.sid:
-                return new AxisOptionsRecord( in );
-            case TickRecord.sid:
-                return new TickRecord( in );
-            case SeriesTextRecord.sid:
-                return new SeriesTextRecord( in );
-            case ObjectLinkRecord.sid:
-                return new ObjectLinkRecord( in );
-            case PlotAreaRecord.sid:
-                return new PlotAreaRecord( in );
-            case SeriesIndexRecord.sid:
-                return new SeriesIndexRecord( in );
-            case LegendRecord.sid:
-                return new LegendRecord( in );
-            case LeftMarginRecord.sid:
-                return new LeftMarginRecord( in );
-            case RightMarginRecord.sid:
-                return new RightMarginRecord( in );
-            case TopMarginRecord.sid:
-                return new TopMarginRecord( in );
-            case BottomMarginRecord.sid:
-                return new BottomMarginRecord( in );
-            case PaletteRecord.sid:
-                return new PaletteRecord( in );
-            case StringRecord.sid:
-                return new StringRecord( in );
-            case NameRecord.sid:
-                return new NameRecord( in );
-            case PaneRecord.sid:
-                return new PaneRecord( in );
-            case SharedFormulaRecord.sid:
-                return new SharedFormulaRecord( in);
-            case ObjRecord.sid:
-                return new ObjRecord( in);
-            case TextObjectRecord.sid:
-                return new TextObjectRecord( in);
-            case HorizontalPageBreakRecord.sid:
-                return new HorizontalPageBreakRecord( in);
-            case VerticalPageBreakRecord.sid:
-                return new VerticalPageBreakRecord( in);
-            case WriteProtectRecord.sid:
-               return new WriteProtectRecord( in);
-            case FilePassRecord.sid:
-               return new FilePassRecord(in);
-            case NoteRecord.sid:
-                return new NoteRecord( in );
-            case FileSharingRecord.sid:
-                return new FileSharingRecord( in );
-            case HyperlinkRecord.sid:
-                return new HyperlinkRecord( in );
-            case TableRecord.sid:
-               return new TableRecord( in );
+            case AreaFormatRecord.sid:     return new AreaFormatRecord(in);
+            case AreaRecord.sid:           return new AreaRecord(in);
+            case AxisLineFormatRecord.sid: return new AxisLineFormatRecord(in);
+            case AxisOptionsRecord.sid:    return new AxisOptionsRecord(in);
+            case AxisParentRecord.sid:     return new AxisParentRecord(in);
+            case AxisRecord.sid:           return new AxisRecord(in);
+            case AxisUsedRecord.sid:       return new AxisUsedRecord(in);
+            case BOFRecord.sid:            return new BOFRecord(in);
+            case BackupRecord.sid:         return new BackupRecord(in);
+            case BarRecord.sid:            return new BarRecord(in);
+            case BeginRecord.sid:          return new BeginRecord(in);
+            case BlankRecord.sid:          return new BlankRecord(in);
+            case BookBoolRecord.sid:       return new BookBoolRecord(in);
+            case BoolErrRecord.sid:        return new BoolErrRecord(in);
+            case BottomMarginRecord.sid:   return new BottomMarginRecord(in);
+            case BoundSheetRecord.sid:     return new BoundSheetRecord(in);
+            case CalcCountRecord.sid:      return new CalcCountRecord(in);
+            case CalcModeRecord.sid:       return new CalcModeRecord(in);
+            case CategorySeriesAxisRecord.sid: return new CategorySeriesAxisRecord(in);
+            case ChartFormatRecord.sid:    return new ChartFormatRecord(in);
+            case ChartRecord.sid:          return new ChartRecord(in);
+            case CodepageRecord.sid:       return new CodepageRecord(in);
+            case ColumnInfoRecord.sid:     return new ColumnInfoRecord(in);
+            case ContinueRecord.sid:       return new ContinueRecord(in);
+            case CountryRecord.sid:        return new CountryRecord(in);
+            case DBCellRecord.sid:         return new DBCellRecord(in);
+            case DSFRecord.sid:            return new DSFRecord(in);
+            case DatRecord.sid:            return new DatRecord(in);
+            case DataFormatRecord.sid:     return new DataFormatRecord(in);
+            case DateWindow1904Record.sid: return new DateWindow1904Record(in);
+            case DefaultColWidthRecord.sid:return new DefaultColWidthRecord(in);
+            case DefaultDataLabelTextPropertiesRecord.sid: return new DefaultDataLabelTextPropertiesRecord(in);
+            case DefaultRowHeightRecord.sid: return new DefaultRowHeightRecord(in);
+            case DeltaRecord.sid:          return new DeltaRecord(in);
+            case DimensionsRecord.sid:     return new DimensionsRecord(in);
+            case DrawingGroupRecord.sid:   return new DrawingGroupRecord(in);
+            case DrawingRecordForBiffViewer.sid: return new DrawingRecordForBiffViewer(in);
+            case DrawingSelectionRecord.sid: return new DrawingSelectionRecord(in);
+            case DVRecord.sid:             return new DVRecord(in);
+            case DVALRecord.sid:           return new DVALRecord(in);
+            case EOFRecord.sid:            return new EOFRecord(in);
+            case EndRecord.sid:            return new EndRecord(in);
+            case ExtSSTRecord.sid:         return new ExtSSTRecord(in);
+            case ExtendedFormatRecord.sid: return new ExtendedFormatRecord(in);
+            case ExternSheetRecord.sid:    return new ExternSheetRecord(in);
+            case FilePassRecord.sid:       return new FilePassRecord(in);
+            case FileSharingRecord.sid:    return new FileSharingRecord(in);
+            case FnGroupCountRecord.sid:   return new FnGroupCountRecord(in);
+            case FontBasisRecord.sid:      return new FontBasisRecord(in);
+            case FontIndexRecord.sid:      return new FontIndexRecord(in);
+            case FontRecord.sid:           return new FontRecord(in);
+            case FooterRecord.sid:         return new FooterRecord(in);
+            case FormatRecord.sid:         return new FormatRecord(in);
+            case FormulaRecord.sid:        return new FormulaRecord(in);
+            case FrameRecord.sid:          return new FrameRecord(in);
+            case GridsetRecord.sid:        return new GridsetRecord(in);
+            case GutsRecord.sid:           return new GutsRecord(in);
+            case HCenterRecord.sid:        return new HCenterRecord(in);
+            case HeaderRecord.sid:         return new HeaderRecord(in);
+            case HideObjRecord.sid:        return new HideObjRecord(in);
+            case HorizontalPageBreakRecord.sid: return new HorizontalPageBreakRecord(in);
+            case HyperlinkRecord.sid:      return new HyperlinkRecord(in);
+            case IndexRecord.sid:          return new IndexRecord(in);
+            case InterfaceEndRecord.sid:   return new InterfaceEndRecord(in);
+            case InterfaceHdrRecord.sid:   return new InterfaceHdrRecord(in);
+            case IterationRecord.sid:      return new IterationRecord(in);
+            case LabelRecord.sid:          return new LabelRecord(in);
+            case LabelSSTRecord.sid:       return new LabelSSTRecord(in);
+            case LeftMarginRecord.sid:     return new LeftMarginRecord(in);
+            case LegendRecord.sid:         return new LegendRecord(in);
+            case LineFormatRecord.sid:     return new LineFormatRecord(in);
+            case LinkedDataRecord.sid:     return new LinkedDataRecord(in);
+            case MMSRecord.sid:            return new MMSRecord(in);
+            case MergeCellsRecord.sid:     return new MergeCellsRecord(in);
+            case MulBlankRecord.sid:       return new MulBlankRecord(in);
+            case MulRKRecord.sid:          return new MulRKRecord(in);
+            case NameRecord.sid:           return new NameRecord(in);
+            case NoteRecord.sid:           return new NoteRecord(in);
+            case NumberRecord.sid:         return new NumberRecord(in);
+            case ObjRecord.sid:            return new ObjRecord(in);
+            case ObjectLinkRecord.sid:     return new ObjectLinkRecord(in);
+            case PaletteRecord.sid:        return new PaletteRecord(in);
+            case PaneRecord.sid:           return new PaneRecord(in);
+            case PasswordRecord.sid:       return new PasswordRecord(in);
+            case PasswordRev4Record.sid:   return new PasswordRev4Record(in);
+            case PlotAreaRecord.sid:       return new PlotAreaRecord(in);
+            case PlotGrowthRecord.sid:     return new PlotGrowthRecord(in);
+            case PrecisionRecord.sid:      return new PrecisionRecord(in);
+            case PrintGridlinesRecord.sid: return new PrintGridlinesRecord(in);
+            case PrintHeadersRecord.sid:   return new PrintHeadersRecord(in);
+            case PrintSetupRecord.sid:     return new PrintSetupRecord(in);
+            case ProtectRecord.sid:        return new ProtectRecord(in);
+            case ProtectionRev4Record.sid: return new ProtectionRev4Record(in);
+            case RKRecord.sid:             return new RKRecord(in);
+            case RefModeRecord.sid:        return new RefModeRecord(in);
+            case RefreshAllRecord.sid:     return new RefreshAllRecord(in);
+            case RightMarginRecord.sid:    return new RightMarginRecord(in);
+            case RowRecord.sid:            return new RowRecord(in);
+            case SCLRecord.sid:            return new SCLRecord(in);
+            case SSTRecord.sid:            return new SSTRecord(in);
+            case SaveRecalcRecord.sid:     return new SaveRecalcRecord(in);
+            case SelectionRecord.sid:      return new SelectionRecord(in);
+            case SeriesIndexRecord.sid:    return new SeriesIndexRecord(in);
+            case SeriesListRecord.sid:     return new SeriesListRecord(in);
+            case SeriesRecord.sid:         return new SeriesRecord(in);
+            case SeriesTextRecord.sid:     return new SeriesTextRecord(in);
+            case SeriesToChartGroupRecord.sid: return new SeriesToChartGroupRecord(in);
+            case SharedFormulaRecord.sid:  return new SharedFormulaRecord(in);
+            case SheetPropertiesRecord.sid:return new SheetPropertiesRecord(in);
+            case StringRecord.sid:         return new StringRecord(in);
+            case StyleRecord.sid:          return new StyleRecord(in);
+            case SupBookRecord.sid:        return new SupBookRecord(in);
+            case TabIdRecord.sid:          return new TabIdRecord(in);
+            case TableRecord.sid:          return new TableRecord(in);
+            case TextObjectRecord.sid:     return new TextObjectRecord(in);
+            case TextRecord.sid:           return new TextRecord(in);
+            case TickRecord.sid:           return new TickRecord(in);
+            case TopMarginRecord.sid:      return new TopMarginRecord(in);
+            case UnitsRecord.sid:          return new UnitsRecord(in);
+            case UseSelFSRecord.sid:       return new UseSelFSRecord(in);
+            case VCenterRecord.sid:        return new VCenterRecord(in);
+            case ValueRangeRecord.sid:     return new ValueRangeRecord(in);
+            case VerticalPageBreakRecord.sid: return new VerticalPageBreakRecord(in);
+            case WSBoolRecord.sid:         return new WSBoolRecord(in);
+            case WindowOneRecord.sid:      return new WindowOneRecord(in);
+            case WindowProtectRecord.sid:  return new WindowProtectRecord(in);
+            case WindowTwoRecord.sid:      return new WindowTwoRecord(in);
+            case WriteAccessRecord.sid:    return new WriteAccessRecord(in);
+            case WriteProtectRecord.sid:   return new WriteProtectRecord(in);        
+        
         }
-        return new UnknownRecord( in );
+        return new UnknownRecord(in);
     }
 
 
index 4302357c47cf095c7b460460fe442deac2425af0..5297f0781f2374fe469e123dbeabfe9831804a66 100644 (file)
 
 package org.apache.poi.hssf.dev;
 
-import java.io.IOException;
 import java.io.FileInputStream;
 import java.io.FileOutputStream;
+import java.io.IOException;
 
+import org.apache.poi.hssf.usermodel.HSSFCell;
+import org.apache.poi.hssf.usermodel.HSSFCellStyle;
+import org.apache.poi.hssf.usermodel.HSSFDataFormat;
+import org.apache.poi.hssf.usermodel.HSSFFont;
+import org.apache.poi.hssf.usermodel.HSSFRichTextString;
+import org.apache.poi.hssf.usermodel.HSSFRow;
+import org.apache.poi.hssf.usermodel.HSSFSheet;
+import org.apache.poi.hssf.usermodel.HSSFWorkbook;
+import org.apache.poi.hssf.util.CellRangeAddress;
 import org.apache.poi.poifs.filesystem.POIFSFileSystem;
 import org.apache.poi.ss.util.Region;
-import org.apache.poi.hssf.usermodel.*;
-import org.apache.poi.hssf.util.*;
 
 /**
  * File for HSSF testing/examples
@@ -127,7 +134,7 @@ public class HSSF
                 }
                 c = r.createCell(( short ) (cellnum + 1),
                                  HSSFCell.CELL_TYPE_STRING);
-                c.setCellValue("TEST");
+                c.setCellValue(new HSSFRichTextString("TEST"));
                 s.setColumnWidth(( short ) (cellnum + 1),
                                  ( short ) ((50 * 8) / (( double ) 1 / 20)));
                 if ((rownum % 2) == 0)
@@ -149,10 +156,8 @@ public class HSSF
             // c.setCellValue(0);
             c.setCellStyle(cs3);
         }
-        s.addMergedRegion(new Region(( short ) 0, ( short ) 0, ( short ) 3,
-                                     ( short ) 3));
-        s.addMergedRegion(new Region(( short ) 100, ( short ) 100,
-                                     ( short ) 110, ( short ) 110));
+        s.addMergedRegion(new CellRangeAddress(0, 3, 0, 3));
+        s.addMergedRegion(new CellRangeAddress(100, 110, 100, 110));
 
         // end draw thick black border
         // create a sheet, set its title then delete it
index afe7de239e02974d6d9cf7a1f13775629f0dc6cf..eb3f2ad6caa5fbf591c78867b1d5341e389169cb 100644 (file)
@@ -27,6 +27,7 @@ import org.apache.poi.hssf.record.formula.*;
 import org.apache.poi.hssf.record.formula.function.FunctionMetadata;
 import org.apache.poi.hssf.record.formula.function.FunctionMetadataRegistry;
 import org.apache.poi.ss.usermodel.Workbook;
+import org.apache.poi.hssf.usermodel.HSSFWorkbook;
 import org.apache.poi.hssf.util.AreaReference;
 import org.apache.poi.hssf.util.CellReference;
 import org.apache.poi.hssf.util.CellReference.NameType;
@@ -66,8 +67,11 @@ public final class FormulaParser {
     public static final int FORMULA_TYPE_CELL = 0;
     public static final int FORMULA_TYPE_SHARED = 1;
     public static final int FORMULA_TYPE_ARRAY =2;
-    public static final int FORMULA_TYPE_CONDFOMRAT = 3;
+    public static final int FORMULA_TYPE_CONDFORMAT = 3;
     public static final int FORMULA_TYPE_NAMEDRANGE = 4;
+    // this constant is currently very specific.  The exact differences from general data 
+    // validation formulas or conditional format formulas is not known yet
+    public static final int FORMULA_TYPE_DATAVALIDATION_LIST = 5;    
 
     private final String formulaString;
     private final int formulaLength;
@@ -75,12 +79,6 @@ public final class FormulaParser {
 
     private ParseNode _rootNode;
 
-    /**
-     * Used for spotting if we have a cell reference,
-     *  or a named range
-     */
-    private final static Pattern CELL_REFERENCE_PATTERN = Pattern.compile("(?:('?)[^:\\\\/\\?\\*\\[\\]]+\\1!)?\\$?[A-Za-z]+\\$?[\\d]+");
-
     private static char TAB = '\t';
 
     /**
@@ -112,9 +110,13 @@ public final class FormulaParser {
     }
 
     public static Ptg[] parse(String formula, Workbook book) {
-        FormulaParser fp = new FormulaParser(formula, book);
+        return parse(formula, book, FORMULA_TYPE_CELL);
+    }
+
+    public static Ptg[] parse(String formula, Workbook workbook, int formulaType) {
+        FormulaParser fp = new FormulaParser(formula, workbook);
         fp.parse();
-        return fp.getRPNPtg();
+        return fp.getRPNPtg(formulaType);
     }
 
     /** Read New Character From Input Stream */
index 07d2bd2fd623c343034a29462b5634a09fd870e6..9b5804f0c4a4ab2e8b845cd681424758640e6635 100644 (file)
@@ -66,6 +66,9 @@ final class OperandClassTransformer {
                        case FormulaParser.FORMULA_TYPE_CELL:
                                rootNodeOperandClass = Ptg.CLASS_VALUE;
                                break;
+                       case FormulaParser.FORMULA_TYPE_DATAVALIDATION_LIST:
+                               rootNodeOperandClass = Ptg.CLASS_REF;
+                               break;
                        default:
                                throw new RuntimeException("Incomplete code - formula type (" 
                                                + _formulaType + ") not supported yet");
index 03177c7c22c15b942ec208109bcc9307e545a768..bec1c40e6a5daaccbd482de6d969109abcef706f 100755 (executable)
@@ -25,7 +25,7 @@ import org.apache.poi.hssf.record.Record;
  *
  * @author Josh Micich
  */
-final class RecordStream {
+public final class RecordStream {
 
        private final List _list;
        private int _nextIndex;
index cbbe663b1dfa386c3917392a7f581367df485a68..1f9cfdff407fd24332b99afcb39db153bc1ee2d1 100644 (file)
 
 package org.apache.poi.hssf.model;
 
-import org.apache.poi.hssf.record.*;
+import org.apache.poi.hssf.record.*; // normally I don't do this, buy we literally mean ALL
 import org.apache.poi.hssf.record.aggregates.ColumnInfoRecordsAggregate;
+import org.apache.poi.hssf.record.aggregates.DataValidityTable;
 import org.apache.poi.hssf.record.aggregates.FormulaRecordAggregate;
 import org.apache.poi.hssf.record.aggregates.RowRecordsAggregate;
 import org.apache.poi.hssf.record.aggregates.ValueRecordsAggregate;
 import org.apache.poi.hssf.record.aggregates.CFRecordsAggregate;
-import org.apache.poi.hssf.record.formula.Ptg;
+import org.apache.poi.ss.util.CellRangeAddress;
 import org.apache.poi.hssf.util.PaneInformation;
 
 import org.apache.poi.util.POILogFactory;
@@ -31,7 +32,7 @@ import org.apache.poi.util.POILogger;
 
 import java.util.ArrayList;
 import java.util.Iterator;
-import java.util.List;   // normally I don't do this, buy we literally mean ALL
+import java.util.List;   
 
 /**
  * Low level model implementation of a Sheet (one workbook contains many sheets)
@@ -90,6 +91,7 @@ public final class Sheet implements Model {
     protected ProtectRecord              protect           =     null;
     protected PageBreakRecord            rowBreaks         =     null;
     protected PageBreakRecord            colBreaks         =     null;
+    private   DataValidityTable          _dataValidityTable=     null;
     protected ObjectProtectRecord        objprotect        =     null;
     protected ScenarioProtectRecord      scenprotect       =     null;
     protected PasswordRecord             password          =     null;
@@ -299,7 +301,12 @@ public final class Sheet implements Model {
                 // and POI always re-calculates its contents 
                 rec = null;
             }
-
+            else if ( rec.getSid() == DVALRecord.sid) {
+                RecordStream rs = new RecordStream(recs, k);
+                retval._dataValidityTable = new DataValidityTable(rs);
+                k += rs.getCountRead() - 1; // TODO - convert this method result to be zero based
+                rec = retval._dataValidityTable;
+            }
             else if ( rec.getSid() == ProtectRecord.sid )
             {
                 retval.protect = (ProtectRecord) rec;
@@ -425,56 +432,56 @@ public final class Sheet implements Model {
         Sheet     retval  = new Sheet();
         ArrayList records = new ArrayList(30);
 
-        records.add(retval.createBOF());
+        records.add(createBOF());
 
         // records.add(retval.createIndex());
-        records.add(retval.createCalcMode());
-        records.add(retval.createCalcCount() );
-        records.add( retval.createRefMode() );
-        records.add( retval.createIteration() );
-        records.add( retval.createDelta() );
-        records.add( retval.createSaveRecalc() );
-        records.add( retval.createPrintHeaders() );
-        retval.printGridlines = (PrintGridlinesRecord) retval.createPrintGridlines();
+        records.add(createCalcMode());
+        records.add(createCalcCount() );
+        records.add(createRefMode() );
+        records.add(createIteration() );
+        records.add(createDelta() );
+        records.add(createSaveRecalc() );
+        records.add(createPrintHeaders() );
+        retval.printGridlines = createPrintGridlines();
         records.add( retval.printGridlines );
-        retval.gridset = (GridsetRecord) retval.createGridset();
+        retval.gridset = createGridset();
         records.add( retval.gridset );
         records.add( retval.createGuts() );
-        retval.defaultrowheight =
-                (DefaultRowHeightRecord) retval.createDefaultRowHeight();
+        retval.defaultrowheight = createDefaultRowHeight();
         records.add( retval.defaultrowheight );
         records.add( retval.createWSBool() );
-
+        
+        // 'Page Settings Block'
         retval.rowBreaks = new PageBreakRecord(PageBreakRecord.HORIZONTAL_SID);
         records.add(retval.rowBreaks);
         retval.colBreaks = new PageBreakRecord(PageBreakRecord.VERTICAL_SID);
         records.add(retval.colBreaks);
 
-        retval.header = (HeaderRecord) retval.createHeader();
+        retval.header = createHeader();
         records.add( retval.header );
-        retval.footer = (FooterRecord) retval.createFooter();
+        retval.footer = createFooter();
         records.add( retval.footer );
-        records.add( retval.createHCenter() );
-        records.add( retval.createVCenter() );
-        retval.printSetup = (PrintSetupRecord) retval.createPrintSetup();
+        records.add(createHCenter() );
+        records.add(createVCenter() );
+        retval.printSetup = createPrintSetup();
         records.add( retval.printSetup );
-        retval.defaultcolwidth =
-                (DefaultColWidthRecord) retval.createDefaultColWidth();
+
+        // 'Worksheet Protection Block' (after 'Page Settings Block' and before DEFCOLWIDTH)
+        // PROTECT record normally goes here, don't add yet since the flag is initially false
+        
+        retval.defaultcolwidth = createDefaultColWidth();
         records.add( retval.defaultcolwidth);
         ColumnInfoRecordsAggregate columns = new ColumnInfoRecordsAggregate();
         records.add( columns );
         retval.columns = columns;
-        retval.dims    = ( DimensionsRecord ) retval.createDimensions();
+        retval.dims    =  createDimensions();
         records.add(retval.dims);
         retval.dimsloc = records.size()-1;
         records.add(retval.windowTwo = retval.createWindowTwo());
         retval.setLoc(records.size() - 1);
-        retval.selection =
-                (SelectionRecord) retval.createSelection();
+        retval.selection = createSelection();
         records.add(retval.selection);
-        retval.protect = (ProtectRecord) retval.createProtect();
-        records.add(retval.protect);
-        records.add(retval.createEOF());
+        records.add(new EOFRecord());
 
 
         retval.records = records;
@@ -509,7 +516,7 @@ public final class Sheet implements Model {
         }
     }
 
-    public int addMergedRegion(int rowFrom, short colFrom, int rowTo, short colTo) {
+    public int addMergedRegion(int rowFrom, int colFrom, int rowTo, int colTo) {
         // Validate input
         if (rowTo < rowFrom) {
             throw new IllegalArgumentException("The 'to' row (" + rowTo
@@ -522,7 +529,7 @@ public final class Sheet implements Model {
 
         if (merged == null || merged.getNumAreas() == 1027)
         {
-            merged = ( MergeCellsRecord ) createMergedCells();
+            merged = createMergedCells();
             mergedRecords.add(merged);
             records.add(records.size() - 1, merged);
         }
@@ -579,7 +586,7 @@ public final class Sheet implements Model {
         }
     }
 
-    public MergeCellsRecord.MergedRegion getMergedRegionAt(int index)
+    public CellRangeAddress getMergedRegionAt(int index)
     {
         //safety checks
         if (index >= numMergedRegions || mergedRecords.size() == 0)
@@ -911,124 +918,11 @@ public final class Sheet implements Model {
 
     /**
      * Create a row record.  (does not add it to the records contained in this sheet)
-     *
-     * @param row number
-     * @return RowRecord created for the passed in row number
-     * @see org.apache.poi.hssf.record.RowRecord
      */
-
-    public RowRecord createRow(int row)
-    {
+    private static RowRecord createRow(int row) {
         return RowRecordsAggregate.createRow( row );
     }
 
-    /**
-     * Create a LABELSST Record (does not add it to the records contained in this sheet)
-     *
-     * @param row the row the LabelSST is a member of
-     * @param col the column the LabelSST defines
-     * @param index the index of the string within the SST (use workbook addSSTString method)
-     * @return LabelSSTRecord newly created containing your SST Index, row,col.
-     * @see org.apache.poi.hssf.record.SSTRecord
-     */
-    public LabelSSTRecord createLabelSST(int row, short col, int index)
-    {
-        log.logFormatted(POILogger.DEBUG, "create labelsst row,col,index %,%,%",
-                         new int[]
-        {
-            row, col, index
-        });
-        LabelSSTRecord rec = new LabelSSTRecord();
-
-        rec.setRow(row);
-        rec.setColumn(col);
-        rec.setSSTIndex(index);
-        rec.setXFIndex(( short ) 0x0f);
-        return rec;
-    }
-
-    /**
-     * Create a NUMBER Record (does not add it to the records contained in this sheet)
-     *
-     * @param row the row the NumberRecord is a member of
-     * @param col the column the NumberRecord defines
-     * @param value for the number record
-     *
-     * @return NumberRecord for that row, col containing that value as added to the sheet
-     */
-    public NumberRecord createNumber(int row, short col, double value)
-    {
-        log.logFormatted(POILogger.DEBUG, "create number row,col,value %,%,%",
-                         new double[]
-        {
-            row, col, value
-        });
-        NumberRecord rec = new NumberRecord();
-
-        rec.setRow(row);
-        rec.setColumn(col);
-        rec.setValue(value);
-        rec.setXFIndex(( short ) 0x0f);
-        return rec;
-    }
-
-    /**
-     * create a BLANK record (does not add it to the records contained in this sheet)
-     *
-     * @param row - the row the BlankRecord is a member of
-     * @param col - the column the BlankRecord is a member of
-     */
-    public BlankRecord createBlank(int row, short col)
-    {
-        log.logFormatted(POILogger.DEBUG, "create blank row,col %,%", new int[]
-        {
-            row, col
-        });
-        BlankRecord rec = new BlankRecord();
-
-        rec.setRow(row);
-        rec.setColumn(col);
-        rec.setXFIndex(( short ) 0x0f);
-        return rec;
-    }
-
-    /**
-     * Attempts to parse the formula into PTGs and create a formula record
-     * DOES NOT WORK YET
-     *
-     * @param row - the row for the formula record
-     * @param col - the column of the formula record
-     * @param formula - a String representing the formula.  To be parsed to PTGs
-     * @return bogus/useless formula record
-     */
-    public FormulaRecord createFormula(int row, short col, String formula)
-    {
-        log.logFormatted(POILogger.DEBUG, "create formula row,col,formula %,%,%",
-                         new int[]
-        {
-            row, col
-        }, formula);
-        FormulaRecord rec = new FormulaRecord();
-
-        rec.setRow(row);
-        rec.setColumn(col);
-        rec.setOptions(( short ) 2);
-        rec.setValue(0);
-        rec.setXFIndex(( short ) 0x0f);
-        FormulaParser fp = new FormulaParser(formula,null); //fix - do we need this method?
-        fp.parse();
-        Ptg[] ptg  = fp.getRPNPtg();
-        int   size = 0;
-
-        for (int k = 0; k < ptg.length; k++)
-        {
-            size += ptg[ k ].getSize();
-            rec.pushExpressionToken(ptg[ k ]);
-        }
-        rec.setExpressionLength(( short ) size);
-        return rec;
-    }
-
     /**
      * Adds a value record to the sheet's contained binary records
      * (i.e. LabelSSTRecord or NumberRecord).
@@ -1247,13 +1141,8 @@ public final class Sheet implements Model {
 
     /**
      * creates the BOF record
-     * @see org.apache.poi.hssf.record.BOFRecord
-     * @see org.apache.poi.hssf.record.Record
-     * @return record containing a BOFRecord
      */
-
-    protected Record createBOF()
-    {
+    private static BOFRecord createBOF() {
         BOFRecord retval = new BOFRecord();
 
         retval.setVersion(( short ) 0x600);
@@ -1266,31 +1155,10 @@ public final class Sheet implements Model {
         return retval;
     }
 
-    /**
-     * creates the Index record  - not currently used
-     * @see org.apache.poi.hssf.record.IndexRecord
-     * @see org.apache.poi.hssf.record.Record
-     * @return record containing a IndexRecord
-     */
-
-    protected Record createIndex()
-    {
-        IndexRecord retval = new IndexRecord();
-
-        retval.setFirstRow(0);   // must be set explicitly
-        retval.setLastRowAdd1(0);
-        return retval;
-    }
-
     /**
      * creates the CalcMode record and sets it to 1 (automatic formula caculation)
-     * @see org.apache.poi.hssf.record.CalcModeRecord
-     * @see org.apache.poi.hssf.record.Record
-     * @return record containing a CalcModeRecord
      */
-
-    protected Record createCalcMode()
-    {
+    private static CalcModeRecord createCalcMode() {
         CalcModeRecord retval = new CalcModeRecord();
 
         retval.setCalcMode(( short ) 1);
@@ -1298,29 +1166,19 @@ public final class Sheet implements Model {
     }
 
     /**
-     * creates the CalcCount record and sets it to 0x64 (default number of iterations)
-     * @see org.apache.poi.hssf.record.CalcCountRecord
-     * @see org.apache.poi.hssf.record.Record
-     * @return record containing a CalcCountRecord
+     * creates the CalcCount record and sets it to 100 (default number of iterations)
      */
-
-    protected Record createCalcCount()
-    {
+    private static CalcCountRecord createCalcCount() {
         CalcCountRecord retval = new CalcCountRecord();
 
-        retval.setIterations(( short ) 0x64);   // default 64 iterations
+        retval.setIterations(( short ) 100);   // default 100 iterations
         return retval;
     }
 
     /**
      * creates the RefMode record and sets it to A1 Mode (default reference mode)
-     * @see org.apache.poi.hssf.record.RefModeRecord
-     * @see org.apache.poi.hssf.record.Record
-     * @return record containing a RefModeRecord
      */
-
-    protected Record createRefMode()
-    {
+    private static RefModeRecord createRefMode() {
         RefModeRecord retval = new RefModeRecord();
 
         retval.setMode(RefModeRecord.USE_A1_MODE);
@@ -1329,13 +1187,8 @@ public final class Sheet implements Model {
 
     /**
      * creates the Iteration record and sets it to false (don't iteratively calculate formulas)
-     * @see org.apache.poi.hssf.record.IterationRecord
-     * @see org.apache.poi.hssf.record.Record
-     * @return record containing a IterationRecord
      */
-
-    protected Record createIteration()
-    {
+    private static IterationRecord createIteration() {
         IterationRecord retval = new IterationRecord();
 
         retval.setIteration(false);
@@ -1344,13 +1197,8 @@ public final class Sheet implements Model {
 
     /**
      * creates the Delta record and sets it to 0.0010 (default accuracy)
-     * @see org.apache.poi.hssf.record.DeltaRecord
-     * @see org.apache.poi.hssf.record.Record
-     * @return record containing a DeltaRecord
      */
-
-    protected Record createDelta()
-    {
+    private static DeltaRecord createDelta() {
         DeltaRecord retval = new DeltaRecord();
 
         retval.setMaxChange(0.0010);
@@ -1359,13 +1207,8 @@ public final class Sheet implements Model {
 
     /**
      * creates the SaveRecalc record and sets it to true (recalculate before saving)
-     * @see org.apache.poi.hssf.record.SaveRecalcRecord
-     * @see org.apache.poi.hssf.record.Record
-     * @return record containing a SaveRecalcRecord
      */
-
-    protected Record createSaveRecalc()
-    {
+    private static SaveRecalcRecord createSaveRecalc() {
         SaveRecalcRecord retval = new SaveRecalcRecord();
 
         retval.setRecalc(true);
@@ -1374,13 +1217,8 @@ public final class Sheet implements Model {
 
     /**
      * creates the PrintHeaders record and sets it to false (we don't create headers yet so why print them)
-     * @see org.apache.poi.hssf.record.PrintHeadersRecord
-     * @see org.apache.poi.hssf.record.Record
-     * @return record containing a PrintHeadersRecord
      */
-
-    protected Record createPrintHeaders()
-    {
+    private static PrintHeadersRecord createPrintHeaders() {
         PrintHeadersRecord retval = new PrintHeadersRecord();
 
         retval.setPrintHeaders(false);
@@ -1390,14 +1228,8 @@ public final class Sheet implements Model {
     /**
      * creates the PrintGridlines record and sets it to false (that makes for ugly sheets).  As far as I can
      * tell this does the same thing as the GridsetRecord
-     *
-     * @see org.apache.poi.hssf.record.PrintGridlinesRecord
-     * @see org.apache.poi.hssf.record.Record
-     * @return record containing a PrintGridlinesRecord
      */
-
-    protected Record createPrintGridlines()
-    {
+    private static PrintGridlinesRecord createPrintGridlines() {
         PrintGridlinesRecord retval = new PrintGridlinesRecord();
 
         retval.setPrintGridlines(false);
@@ -1406,13 +1238,8 @@ public final class Sheet implements Model {
 
     /**
      * creates the Gridset record and sets it to true (user has mucked with the gridlines)
-     * @see org.apache.poi.hssf.record.GridsetRecord
-     * @see org.apache.poi.hssf.record.Record
-     * @return record containing a GridsetRecord
      */
-
-    protected Record createGridset()
-    {
+    private static GridsetRecord createGridset() {
         GridsetRecord retval = new GridsetRecord();
 
         retval.setGridset(true);
@@ -1421,13 +1248,8 @@ public final class Sheet implements Model {
 
     /**
      * creates the Guts record and sets leftrow/topcol guttter and rowlevelmax/collevelmax to 0
-     * @see org.apache.poi.hssf.record.GutsRecord
-     * @see org.apache.poi.hssf.record.Record
-     * @return record containing a GutsRecordRecord
-     */
-
-    protected Record createGuts()
-    {
+      */
+    private static GutsRecord createGuts() {
         GutsRecord retval = new GutsRecord();
 
         retval.setLeftRowGutter(( short ) 0);
@@ -1439,13 +1261,8 @@ public final class Sheet implements Model {
 
     /**
      * creates the DefaultRowHeight Record and sets its options to 0 and rowheight to 0xff
-     * @see org.apache.poi.hssf.record.DefaultRowHeightRecord
-     * @see org.apache.poi.hssf.record.Record
-     * @return record containing a DefaultRowHeightRecord
      */
-
-    protected Record createDefaultRowHeight()
-    {
+    private static DefaultRowHeightRecord createDefaultRowHeight() {
         DefaultRowHeightRecord retval = new DefaultRowHeightRecord();
 
         retval.setOptionFlags(( short ) 0);
@@ -1455,13 +1272,8 @@ public final class Sheet implements Model {
 
     /**
      * creates the WSBoolRecord and sets its values to defaults
-     * @see org.apache.poi.hssf.record.WSBoolRecord
-     * @see org.apache.poi.hssf.record.Record
-     * @return record containing a WSBoolRecord
      */
-
-    protected Record createWSBool()
-    {
+    private static WSBoolRecord createWSBool() {
         WSBoolRecord retval = new WSBoolRecord();
 
         retval.setWSBool1(( byte ) 0x4);
@@ -1471,13 +1283,8 @@ public final class Sheet implements Model {
 
     /**
      * creates the Header Record and sets it to nothing/0 length
-     * @see org.apache.poi.hssf.record.HeaderRecord
-     * @see org.apache.poi.hssf.record.Record
-     * @return record containing a HeaderRecord
      */
-
-    protected Record createHeader()
-    {
+    private static HeaderRecord createHeader() {
         HeaderRecord retval = new HeaderRecord();
 
         retval.setHeaderLength(( byte ) 0);
@@ -1487,13 +1294,8 @@ public final class Sheet implements Model {
 
     /**
      * creates the Footer Record and sets it to nothing/0 length
-     * @see org.apache.poi.hssf.record.FooterRecord
-     * @see org.apache.poi.hssf.record.Record
-     * @return record containing a FooterRecord
      */
-
-    protected Record createFooter()
-    {
+    private static FooterRecord createFooter() {
         FooterRecord retval = new FooterRecord();
 
         retval.setFooterLength(( byte ) 0);
@@ -1503,13 +1305,8 @@ public final class Sheet implements Model {
 
     /**
      * creates the HCenter Record and sets it to false (don't horizontally center)
-     * @see org.apache.poi.hssf.record.HCenterRecord
-     * @see org.apache.poi.hssf.record.Record
-     * @return record containing a HCenterRecord
      */
-
-    protected Record createHCenter()
-    {
+    private static HCenterRecord createHCenter() {
         HCenterRecord retval = new HCenterRecord();
 
         retval.setHCenter(false);
@@ -1518,13 +1315,8 @@ public final class Sheet implements Model {
 
     /**
      * creates the VCenter Record and sets it to false (don't horizontally center)
-     * @see org.apache.poi.hssf.record.VCenterRecord
-     * @see org.apache.poi.hssf.record.Record
-     * @return record containing a VCenterRecord
-     */
-
-    protected Record createVCenter()
-    {
+    */
+    private static VCenterRecord createVCenter() {
         VCenterRecord retval = new VCenterRecord();
 
         retval.setVCenter(false);
@@ -1537,9 +1329,7 @@ public final class Sheet implements Model {
      * @see org.apache.poi.hssf.record.Record
      * @return record containing a PrintSetupRecord
      */
-
-    protected Record createPrintSetup()
-    {
+    private static PrintSetupRecord createPrintSetup() {
         PrintSetupRecord retval = new PrintSetupRecord();
 
         retval.setPaperSize(( short ) 1);
@@ -1558,30 +1348,13 @@ public final class Sheet implements Model {
 
     /**
      * creates the DefaultColWidth Record and sets it to 8
-     * @see org.apache.poi.hssf.record.DefaultColWidthRecord
-     * @see org.apache.poi.hssf.record.Record
-     * @return record containing a DefaultColWidthRecord
-     */
-
-    protected Record createDefaultColWidth()
-    {
+      */
+    private static DefaultColWidthRecord createDefaultColWidth() {
         DefaultColWidthRecord retval = new DefaultColWidthRecord();
-
         retval.setColWidth(( short ) 8);
         return retval;
     }
 
-    /**
-     * creates the ColumnInfo Record and sets it to a default column/width
-     * @see org.apache.poi.hssf.record.ColumnInfoRecord
-     * @return record containing a ColumnInfoRecord
-     */
-    // TODO change return type to ColumnInfoRecord 
-    protected Record createColInfo()
-    {
-        return ColumnInfoRecordsAggregate.createColInfo();
-    }
-
     /**
      * get the default column width for the sheet (if the columns do not define their own width)
      * @return default column width
@@ -1600,7 +1373,7 @@ public final class Sheet implements Model {
     public boolean isGridsPrinted()
     {
         if (gridset == null) {
-            gridset = (GridsetRecord)createGridset();
+            gridset = createGridset();
             //Insert the newlycreated Gridset record at the end of the record (just before the EOF)
             int loc = findFirstRecordLocBySid(EOFRecord.sid);
             records.add(loc, gridset);
@@ -1816,22 +1589,18 @@ public final class Sheet implements Model {
 
         GutsRecord guts = (GutsRecord) findFirstRecordBySid( GutsRecord.sid );
         guts.setColLevelMax( (short) ( maxLevel+1 ) );
-        if (maxLevel == 0)
+        if (maxLevel == 0) {
             guts.setTopColGutter( (short)0 );
-        else
+        } else {
             guts.setTopColGutter( (short) ( 29 + (12 * (maxLevel-1)) ) );
+        }
     }
 
     /**
      * creates the Dimensions Record and sets it to bogus values (you should set this yourself
      * or let the high level API do it for you)
-     * @see org.apache.poi.hssf.record.DimensionsRecord
-     * @see org.apache.poi.hssf.record.Record
-     * @return record containing a DimensionsRecord
      */
-
-    protected Record createDimensions()
-    {
+    private static DimensionsRecord createDimensions() {
         DimensionsRecord retval = new DimensionsRecord();
 
         retval.setFirstCol(( short ) 0);
@@ -1849,13 +1618,8 @@ public final class Sheet implements Model {
      * headercolor    = 0x40 <P>
      * pagebreakzoom  = 0x0 <P>
      * normalzoom     = 0x0 <p>
-     * @see org.apache.poi.hssf.record.WindowTwoRecord
-     * @see org.apache.poi.hssf.record.Record
-     * @return record containing a WindowTwoRecord
      */
-
-    protected WindowTwoRecord createWindowTwo()
-    {
+    private static WindowTwoRecord createWindowTwo() {
         WindowTwoRecord retval = new WindowTwoRecord();
 
         retval.setOptions(( short ) 0x6b6);
@@ -1869,21 +1633,9 @@ public final class Sheet implements Model {
 
     /**
      * Creates the Selection record and sets it to nothing selected
-     *
-     * @see org.apache.poi.hssf.record.SelectionRecord
-     * @see org.apache.poi.hssf.record.Record
-     * @return record containing a SelectionRecord
-     */
-
-    protected Record createSelection()
-    {
-        SelectionRecord retval = new SelectionRecord();
-
-        retval.setPane(( byte ) 0x3);
-        retval.setActiveCellCol(( short ) 0x0);
-        retval.setActiveCellRow(( short ) 0x0);
-        retval.setNumRefs(( short ) 0x0);
-        return retval;
+    */
+    private static SelectionRecord createSelection() {
+        return new SelectionRecord(0, 0);
     }
 
     public short getTopRow()
@@ -1903,19 +1655,15 @@ public final class Sheet implements Model {
      * Sets the left column to show in desktop window pane.
      * @param leftCol the left column to show in desktop window pane
      */
-        public void setLeftCol(short leftCol){
-            if (windowTwo!=null)
-            {
+    public void setLeftCol(short leftCol){
+        if (windowTwo!=null) {
             windowTwo.setLeftCol(leftCol);
-            }
         }
+    }
 
-        public short getLeftCol()
-        {
-            return (windowTwo==null) ? (short) 0 : windowTwo.getLeftCol();
-        }
-
-
+    public short getLeftCol() {
+        return (windowTwo==null) ? (short) 0 : windowTwo.getLeftCol();
+    }
 
     /**
      * Returns the active row
@@ -1948,18 +1696,14 @@ public final class Sheet implements Model {
     }
 
     /**
-     * Returns the active column
-     *
      * @see org.apache.poi.hssf.record.SelectionRecord
-     * @return row the active column index
+     * @return column of the active cell
      */
-    public short getActiveCellCol()
-    {
-        if (selection == null)
-        {
-            return (short) 0;
+    public short getActiveCellCol() {
+        if (selection == null) {
+            return 0;
         }
-        return selection.getActiveCellCol();
+        return (short)selection.getActiveCellCol();
     }
 
     /**
@@ -1977,23 +1721,8 @@ public final class Sheet implements Model {
         }
     }
 
-    protected Record createMergedCells()
-    {
-        MergeCellsRecord retval = new MergeCellsRecord();
-        retval.setNumAreas(( short ) 0);
-        return retval;
-    }
-
-    /**
-     * creates the EOF record
-     * @see org.apache.poi.hssf.record.EOFRecord
-     * @see org.apache.poi.hssf.record.Record
-     * @return record containing a EOFRecord
-     */
-
-    protected Record createEOF()
-    {
-        return new EOFRecord();
+    private static  MergeCellsRecord createMergedCells() {
+        return new MergeCellsRecord();
     }
 
     /**
@@ -2383,28 +2112,20 @@ public final class Sheet implements Model {
 
     /**
      * creates a Protect record with protect set to false.
-     * @see org.apache.poi.hssf.record.ProtectRecord
-     * @see org.apache.poi.hssf.record.Record
-     * @return a ProtectRecord
      */
-    protected Record createProtect()
-    {
-        if (log.check( POILogger.DEBUG ))
+    private static ProtectRecord createProtect() {
+        if (log.check( POILogger.DEBUG )) {
             log.log(POILogger.DEBUG, "create protect record with protection disabled");
-        ProtectRecord retval = new ProtectRecord();
-
-        retval.setProtect(false);
+        }
+        ProtectRecord retval = new ProtectRecord(); 
+        retval.setProtect(false); // TODO - supply param to constructor
         return retval;
     }
 
     /**
      * creates an ObjectProtect record with protect set to false.
-     * @see org.apache.poi.hssf.record.ObjectProtectRecord
-     * @see org.apache.poi.hssf.record.Record
-     * @return an ObjectProtectRecord
      */
-    protected ObjectProtectRecord createObjectProtect()
-    {
+    private static ObjectProtectRecord createObjectProtect() {
         if (log.check( POILogger.DEBUG ))
             log.log(POILogger.DEBUG, "create protect record with protection disabled");
         ObjectProtectRecord retval = new ObjectProtectRecord();
@@ -2415,12 +2136,8 @@ public final class Sheet implements Model {
 
     /**
      * creates a ScenarioProtect record with protect set to false.
-     * @see org.apache.poi.hssf.record.ScenarioProtectRecord
-     * @see org.apache.poi.hssf.record.Record
-     * @return a ScenarioProtectRecord
      */
-    protected ScenarioProtectRecord createScenarioProtect()
-    {
+    private static ScenarioProtectRecord createScenarioProtect() {
         if (log.check( POILogger.DEBUG ))
             log.log(POILogger.DEBUG, "create protect record with protection disabled");
         ScenarioProtectRecord retval = new ScenarioProtectRecord();
@@ -2435,9 +2152,9 @@ public final class Sheet implements Model {
     public ProtectRecord getProtect()
     {
         if (protect == null) {
-            protect = (ProtectRecord)createProtect();
-            //Insert the newlycreated protect record at the end of the record (just before the EOF)
-            int loc = findFirstRecordLocBySid(EOFRecord.sid);
+            protect = createProtect();
+            // Insert the newly created protect record just before DefaultColWidthRecord
+            int loc = findFirstRecordLocBySid(DefaultColWidthRecord.sid);
             records.add(loc, protect);
         }
         return protect;
@@ -2459,14 +2176,11 @@ public final class Sheet implements Model {
 
     /**
      * creates a Password record with password set to 00.
-     * @see org.apache.poi.hssf.record.PasswordRecord
-     * @see org.apache.poi.hssf.record.Record
-     * @return a PasswordRecord
      */
-    protected PasswordRecord createPassword()
-    {
-        if (log.check( POILogger.DEBUG ))
-            log.log(POILogger.DEBUG, "create password record with 00 password");
+    private static PasswordRecord createPassword() {
+        if (log.check( POILogger.DEBUG )) {
+                       log.log(POILogger.DEBUG, "create password record with 00 password");
+               }
         PasswordRecord retval = new PasswordRecord();
 
         retval.setPassword((short)00);
@@ -2892,4 +2606,10 @@ public final class Sheet implements Model {
             rows.expandRow( row );
         }
     }
+    public DataValidityTable getOrCreateDataValidityTable() {
+        if (_dataValidityTable == null) {
+            _dataValidityTable = DataValidityTable.createForSheet(records);
+        }
+        return _dataValidityTable;
+    }
 }
index 6447546ec67f71863518a0db111d8515fe7071ca..862adc9c5936df7200e22d6d35b31813347a1c89 100644 (file)
 
 package org.apache.poi.hssf.record;
 
-import org.apache.poi.hssf.record.cf.CellRange;
-import org.apache.poi.hssf.util.Region;
+import org.apache.poi.hssf.record.cf.CellRangeUtil;
+import org.apache.poi.ss.util.CellRangeAddress;
+import org.apache.poi.ss.util.CellRangeAddressList;
 import org.apache.poi.util.LittleEndian;
 
 /**
- * Conditional Formatting Header record (CFHEADER)
+ * Conditional Formatting Header record CFHEADER (0x1B0)
  * 
  * @author Dmitriy Kumshayev
  */
-public final class CFHeaderRecord extends Record
-{
+public final class CFHeaderRecord extends Record {
        public static final short sid = 0x1B0;
 
-       private static final CellRange[] EMPTY_CELL_RANGE_ARRAY = { };
-
        private int field_1_numcf;
        private int field_2_need_recalculation;
-       private CellRange field_3_enclosing_cell_range;
-       private CellRange[] field_4_cell_ranges;
+       private CellRangeAddress field_3_enclosing_cell_range;
+       private CellRangeAddressList field_4_cell_ranges;
 
        /** Creates new CFHeaderRecord */
        public CFHeaderRecord()
        {
-               field_4_cell_ranges = EMPTY_CELL_RANGE_ARRAY;
+               field_4_cell_ranges = new CellRangeAddressList();
        }
-       public CFHeaderRecord(org.apache.poi.ss.util.Region[] regions)
+       public CFHeaderRecord(CellRangeAddress[] regions)
        {
-               CellRange[] unmergedRanges = CellRange.convertRegionsToCellRanges(regions);
-               CellRange[] mergeCellRanges = CellRange.mergeCellRanges(unmergedRanges);
+               CellRangeAddress[] unmergedRanges = regions;
+               CellRangeAddress[] mergeCellRanges = CellRangeUtil.mergeCellRanges(unmergedRanges);
                setCellRanges(mergeCellRanges);
        }
 
@@ -58,14 +56,8 @@ 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.readUShort(), in.readUShort(), in.readUShort(), in.readUShort());
-               int numCellRanges = in.readShort();
-               CellRange[] crs = new CellRange[numCellRanges];
-               for( int i=0; i<numCellRanges; i++)
-               {
-                       crs[i] = new CellRange(in.readUShort(),in.readUShort(),in.readUShort(),in.readUShort());
-               }
-               field_4_cell_ranges = crs;
+               field_3_enclosing_cell_range = new org.apache.poi.hssf.util.CellRangeAddress(in);
+               field_4_cell_ranges = new org.apache.poi.hssf.util.CellRangeAddressList(in);
        }
        
        public int getNumberOfConditionalFormats()
@@ -87,14 +79,14 @@ public final class CFHeaderRecord extends Record
                field_2_need_recalculation=b?1:0;
        }
        
-       public CellRange getEnclosingCellRange()
+       public CellRangeAddress getEnclosingCellRange()
        {
                return field_3_enclosing_cell_range;
        }
 
-       public void setEnclosingCellRange( CellRange cr)
+       public void setEnclosingCellRange(CellRangeAddress cr)
        {
-               field_3_enclosing_cell_range = cr.cloneCellRange();
+               field_3_enclosing_cell_range = cr;
        }
 
        /**
@@ -102,24 +94,26 @@ public final class CFHeaderRecord extends Record
         * modify the enclosing cell range accordingly.
         * @param List cellRanges - list of CellRange objects
         */
-       public void setCellRanges(CellRange[] cellRanges)
+       public void setCellRanges(CellRangeAddress[] cellRanges)
        {
                if(cellRanges == null)
                {
                        throw new IllegalArgumentException("cellRanges must not be null");
                }
-               field_4_cell_ranges = (CellRange[]) cellRanges.clone();
-               CellRange enclosingRange = null;
+               CellRangeAddressList cral = new CellRangeAddressList();
+               CellRangeAddress enclosingRange = null;
                for (int i = 0; i < cellRanges.length; i++)
                {
-                       enclosingRange = cellRanges[i].createEnclosingCellRange(enclosingRange);
+                       CellRangeAddress cr = cellRanges[i];
+                       enclosingRange = CellRangeUtil.createEnclosingCellRange(cr, enclosingRange);
+                       cral.addCellRangeAddress(cr);
                }
-               field_3_enclosing_cell_range=enclosingRange;
+               field_3_enclosing_cell_range = enclosingRange;
+               field_4_cell_ranges = cral;
        }
        
-       public CellRange[] getCellRanges()
-       {
-               return (CellRange[]) field_4_cell_ranges.clone();
+       public CellRangeAddress[] getCellRanges() {
+               return field_4_cell_ranges.getCellRangeAddresses();
        }
 
        public String toString()
@@ -131,50 +125,39 @@ public final class CFHeaderRecord extends Record
                buffer.append(" .numCF                  = ").append(getNumberOfConditionalFormats()).append("\n");
                buffer.append(" .needRecalc        = ").append(getNeedRecalculation()).append("\n");
                buffer.append(" .enclosingCellRange= ").append(getEnclosingCellRange()).append("\n");
-               if( field_4_cell_ranges.length>0)
+               buffer.append(" .cfranges=[");
+               for( int i=0; i<field_4_cell_ranges.countRanges(); i++)
                {
-                       buffer.append(" .cfranges=[");
-                       for( int i=0; i<field_4_cell_ranges.length; i++)
-                       {
-                               buffer.append(i==0?"":",").append(field_4_cell_ranges[i].toString());
-                       }
-                       buffer.append("]\n");
+                       buffer.append(i==0?"":",").append(field_4_cell_ranges.getCellRangeAddress(i).toString());
                }
+               buffer.append("]\n");
                buffer.append("[/CFHEADER]\n");
                return buffer.toString();
        }
 
+       private int getDataSize() {
+               return 4 // 2 short fields
+                       + CellRangeAddress.ENCODED_SIZE
+                       + field_4_cell_ranges.getSize();
+       }
+       
        /**
         * @return byte array containing instance data
         */
-
-       public int serialize(int offset, byte[] data)
-       {
-               int recordsize = getRecordSize();
+       public int serialize(int offset, byte[] data) {
+               int dataSize = getDataSize();
                
-               LittleEndian.putShort(data, 0 + offset, sid);
-               LittleEndian.putShort(data, 2 + offset, (short) (recordsize-4));
-               LittleEndian.putShort(data, 4 + offset, (short) field_1_numcf);
-               LittleEndian.putShort(data, 6 + offset, (short) field_2_need_recalculation);
-               LittleEndian.putShort(data, 8 + offset, (short) field_3_enclosing_cell_range.getFirstRow());
-               LittleEndian.putShort(data, 10 + offset, (short) field_3_enclosing_cell_range.getLastRow());
-               LittleEndian.putShort(data, 12 + offset, (short) field_3_enclosing_cell_range.getFirstColumn());
-               LittleEndian.putShort(data, 14 + offset, (short) field_3_enclosing_cell_range.getLastColumn());
-               LittleEndian.putShort(data, 16 + offset, (short) field_4_cell_ranges.length);
-               for( int i=0 ; i!=field_4_cell_ranges.length; i++)
-               {
-                       CellRange cr = field_4_cell_ranges[i];
-                       LittleEndian.putShort(data, 18 + 0 + 8 * i + offset, (short) cr.getFirstRow());
-                       LittleEndian.putShort(data, 18 + 2 + 8 * i + offset, (short) cr.getLastRow());
-                       LittleEndian.putShort(data, 18 + 4 + 8 * i + offset, (short) cr.getFirstColumn());
-                       LittleEndian.putShort(data, 18 + 6 + 8 * i + offset, (short) cr.getLastColumn());
-               }
-               return getRecordSize();
+               LittleEndian.putUShort(data, 0 + offset, sid);
+               LittleEndian.putUShort(data, 2 + offset, dataSize);
+               LittleEndian.putUShort(data, 4 + offset, field_1_numcf);
+               LittleEndian.putUShort(data, 6 + offset, field_2_need_recalculation);
+               field_3_enclosing_cell_range.serialize(8 + offset, data);
+               field_4_cell_ranges.serialize(16 + offset, data);
+               return 4 + dataSize;
        }
 
-       public int getRecordSize()
-       {
-               return 18+8*field_4_cell_ranges.length;
+       public int getRecordSize() {
+               return 4 + getDataSize();
        }
 
        /**
@@ -204,11 +187,7 @@ public final class CFHeaderRecord extends Record
                result.field_1_numcf = field_1_numcf;
                result.field_2_need_recalculation = field_2_need_recalculation;
                result.field_3_enclosing_cell_range = field_3_enclosing_cell_range;
-               CellRange[] crs = new CellRange[field_4_cell_ranges.length];
-               for (int i = 0; i < crs.length; i++) {
-                       crs[i] = field_4_cell_ranges[i].cloneCellRange();
-               }
-               result.field_4_cell_ranges = crs;
+               result.field_4_cell_ranges = field_4_cell_ranges.copy();
                return result;
        }
 }
index c4e37114d958c209741624cb376cc7bbb2911e5a..852e4dfae7cc08160b2f687f7b0367fdfc699ff8 100644 (file)
 
 package org.apache.poi.hssf.record;
 
-import java.io.IOException;
-import java.util.Enumeration;
-import java.util.Hashtable;
-import java.util.Stack;
-
+import org.apache.poi.hssf.record.UnicodeString.UnicodeRecordStats;
 import org.apache.poi.hssf.record.formula.Ptg;
-import org.apache.poi.hssf.util.HSSFCellRangeAddress;
+import org.apache.poi.hssf.usermodel.DVConstraint;
+import org.apache.poi.hssf.usermodel.HSSFDataValidation;
+import org.apache.poi.ss.util.CellRangeAddress;
+import org.apache.poi.ss.util.CellRangeAddressList;
 import org.apache.poi.util.BitField;
 import org.apache.poi.util.LittleEndian;
-import org.apache.poi.util.StringUtil;
 
 /**
  * Title:        DATAVALIDATION Record (0x01BE)<p/>
@@ -34,563 +32,312 @@ import org.apache.poi.util.StringUtil;
  *               are stored in a sequential list of DV records. This list is followed by
  *               DVAL record(s)
  * @author Dragos Buleandra (dragos.buleandra@trade2b.ro)
- * @version 2.0-pre
+ * @author Josh Micich
  */
-public final class DVRecord extends Record
-{
-    public final static short sid = 0x01BE;
-
-    /**
-     * Option flags
-     */
-    private int                field_option_flags;
-
-    /**
-     * Title of the prompt box
-     */
-    private String             field_title_prompt;
-
-    /**
-     * Title of the error box
-     */
-    private String             field_title_error;
-
-    /**
-     * Text of the prompt box
-     */
-    private String             field_text_prompt;
-
-    /**
-     * Text of the error box
-     */
-    private String             field_text_error;
-
-    /**
-     * Size of the formula data for first condition
-     */
-    private short             field_size_first_formula;
-
-    /**
-     * Not used
-     */
-    private short             field_not_used_1 = 0x3FE0;
-
-    /**
-     * Formula data for first condition (RPN token array without size field)
-     */
-    private Stack             field_rpn_token_1 ;
-
-    /**
-     * Size of the formula data for second condition
-     */
-    private short             field_size_sec_formula;
-
-    /**
-     * Not used
-     */
-    private short             field_not_used_2 = 0x0000;
-
-    /**
-     * Formula data for second condition (RPN token array without size field)
-     */
-    private Stack             field_rpn_token_2 ;
-
-    /**
-     * Cell range address list with all affected ranges
-     */
-    private HSSFCellRangeAddress         field_regions;
-
-    public static final Integer STRING_PROMPT_TITLE = new Integer(0);
-    public static final Integer STRING_ERROR_TITLE  = new Integer(1);
-    public static final Integer STRING_PROMPT_TEXT  = new Integer(2);
-    public static final Integer STRING_ERROR_TEXT   = new Integer(3);
-    private Hashtable _hash_strings ;
-
-    /**
-     * Option flags field
-     * @see org.apache.poi.hssf.util.HSSFDataValidation utility class
-     */
-    private BitField          opt_data_type                    = new BitField(0x0000000F);
-    private BitField          opt_error_style                  = new BitField(0x00000070);
-    private BitField          opt_string_list_formula          = new BitField(0x00000080);
-    private BitField          opt_empty_cell_allowed           = new BitField(0x00000100);
-    private BitField          opt_surppres_dropdown_arrow      = new BitField(0x00000200);
-    private BitField          opt_show_prompt_on_cell_selected = new BitField(0x00040000);
-    private BitField          opt_show_error_on_invalid_value  = new BitField(0x00080000);
-    private BitField          opt_condition_operator           = new BitField(0x00F00000);
-
-    public DVRecord()
-    {
-    }
-
-    /**
-     * Constructs a DV record and sets its fields appropriately.
-     *
-     * @param in the RecordInputstream to read the record from
-     */
-
-    public DVRecord(RecordInputStream in)
-    {
-        super(in);
-    }
-
-    protected void validateSid(short id)
-    {
-        if (id != sid)
-        {
-            throw new RecordFormatException("NOT a valid DV RECORD");
-        }
-    }
-
-    protected void fillFields(RecordInputStream in)
-    {
-       field_rpn_token_1 = new Stack();
-       field_rpn_token_2 = new Stack();
-        
-       this.field_option_flags = in.readInt();
-       this._hash_strings = new Hashtable(4);
-       
-       StringHandler strHandler_prompt_title = new StringHandler( in );
-       this.field_title_prompt = strHandler_prompt_title.getStringData();
-       this._hash_strings.put(DVRecord.STRING_PROMPT_TITLE, strHandler_prompt_title);
-
-       StringHandler strHandler_error_title = new StringHandler( in );
-       this.field_title_error = strHandler_error_title.getStringData();
-       this._hash_strings.put(DVRecord.STRING_ERROR_TITLE, strHandler_error_title);
-
-       StringHandler strHandler_prompt_text = new StringHandler( in );
-       this.field_text_prompt = strHandler_prompt_text.getStringData();
-       this._hash_strings.put(DVRecord.STRING_PROMPT_TEXT, strHandler_prompt_text);
-
-       StringHandler strHandler_error_text = new StringHandler( in );
-       this.field_text_error = strHandler_error_text.getStringData();
-       this._hash_strings.put(DVRecord.STRING_ERROR_TEXT, strHandler_error_text);
-
-       this.field_size_first_formula = in.readShort(); 
-       this.field_not_used_1 = in.readShort();
-
-       //read first formula data condition
-       int token_pos = 0;
-       while (token_pos < this.field_size_first_formula)
-       {
-           Ptg ptg = Ptg.createPtg(in);
-           token_pos += ptg.getSize();
-           field_rpn_token_1.push(ptg);
-       }
-
-       this.field_size_sec_formula = in.readShort(); 
-       this.field_not_used_2 = in.readShort();
-
-       //read sec formula data condition
-       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)
-       {
-           Ptg ptg = Ptg.createPtg(in);
-           token_pos += ptg.getSize();
-           field_rpn_token_2.push(ptg);
-       }
-
-       //read cell range address list with all affected ranges
-       this.field_regions = new HSSFCellRangeAddress(in);
-    }
-
-
-    // --> start option flags
-    /**
-     * set the condition data type
-     * @param type - condition data type
-     * @see org.apache.poi.hssf.util.HSSFDataValidation utility class
-     */
-    public void setDataType(int type)
-    {
-        this.field_option_flags =  this.opt_data_type.setValue(this.field_option_flags, type);
-    }
-
-    /**
-     * get the condition data type
-     * @return the condition data type
-     * @see org.apache.poi.hssf.util.HSSFDataValidation utility class
-     */
-    public int getDataType()
-    {
-       return this.opt_data_type.getValue(this.field_option_flags);
-    }
-
-    /**
-     * set the condition error style
-     * @param type - condition error style
-     * @see org.apache.poi.hssf.util.HSSFDataValidation utility class
-     */
-    public void setErrorStyle(int style)
-    {
-        this.field_option_flags =  this.opt_error_style.setValue(this.field_option_flags, style);
-    }
-
-    /**
-     * get the condition error style
-     * @return the condition error style
-     * @see org.apache.poi.hssf.util.HSSFDataValidation utility class
-     */
-    public int getErrorStyle()
-    {
-       return this.opt_error_style.getValue(this.field_option_flags);
-    }
-
-    /**
-     * set if in list validations the string list is explicitly given in the formula
-     * @param type - true if in list validations the string list is explicitly given in the formula; false otherwise
-     * @see org.apache.poi.hssf.util.HSSFDataValidation utility class
-     */
-    public void setListExplicitFormula(boolean explicit)
-    {
-        this.field_option_flags = this.opt_string_list_formula.setBoolean(this.field_option_flags, explicit);
-    }
-
-    /**
-     * return true if in list validations the string list is explicitly given in the formula, false otherwise
-     * @return true if in list validations the string list is explicitly given in the formula, false otherwise
-     * @see org.apache.poi.hssf.util.HSSFDataValidation utility class
-     */
-    public boolean getListExplicitFormula()
-    {
-       return (this.opt_string_list_formula.isSet(this.field_option_flags));
-    }
-
-    /**
-     * set if empty values are allowed in cells
-     * @param type - true if empty values are allowed in cells, false otherwise
-     * @see org.apache.poi.hssf.util.HSSFDataValidation utility class
-     */
-    public void setEmptyCellAllowed(boolean allowed)
-    {
-        this.field_option_flags =  this.opt_empty_cell_allowed.setBoolean(this.field_option_flags, allowed);
-    }
-
-    /**
-     * return true if empty values are allowed in cells, false otherwise
-     * @return if empty values are allowed in cells, false otherwise
-     * @see org.apache.poi.hssf.util.HSSFDataValidation utility class
-     */
-    public boolean getEmptyCellAllowed()
-    {
-       return (this.opt_empty_cell_allowed.isSet(this.field_option_flags));
-    }
-
-    /**
-     * set if drop down arrow should be surppressed when list validation is used
-     * @param type - true if drop down arrow should be surppressed when list validation is used, false otherwise
-     * @see org.apache.poi.hssf.util.HSSFDataValidation utility class
-     */
-    public void setSurppresDropdownArrow(boolean surppress)
-    {
-        this.field_option_flags =  this.opt_surppres_dropdown_arrow.setBoolean(this.field_option_flags, surppress);
-    }
-
-    /**
-     * return true if drop down arrow should be surppressed when list validation is used, false otherwise
-     * @return if drop down arrow should be surppressed when list validation is used, false otherwise
-     * @see org.apache.poi.hssf.util.HSSFDataValidation utility class
-     */
-    public boolean getSurppresDropdownArrow()
-    {
-       return (this.opt_surppres_dropdown_arrow.isSet(this.field_option_flags));
-    }
-
-    /**
-     * set if a prompt window should appear when cell is selected
-     * @param type - true if a prompt window should appear when cell is selected, false otherwise
-     * @see org.apache.poi.hssf.util.HSSFDataValidation utility class
-     */
-    public void setShowPromptOnCellSelected(boolean show)
-    {
-        this.field_option_flags =  this.opt_show_prompt_on_cell_selected.setBoolean(this.field_option_flags, show);
-    }
-
-    /**
-     * return true if a prompt window should appear when cell is selected, false otherwise
-     * @return if a prompt window should appear when cell is selected, false otherwise
-     * @see org.apache.poi.hssf.util.HSSFDataValidation utility class
-     */
-    public boolean getShowPromptOnCellSelected()
-    {
-       return (this.opt_show_prompt_on_cell_selected.isSet(this.field_option_flags));
-    }
-
-    /**
-     * set if an error window should appear when an invalid value is entered in the cell
-     * @param type - true if an error window should appear when an invalid value is entered in the cell, false otherwise
-     * @see org.apache.poi.hssf.util.HSSFDataValidation utility class
-     */
-    public void setShowErrorOnInvalidValue(boolean show)
-    {
-        this.field_option_flags =  this.opt_show_error_on_invalid_value.setBoolean(this.field_option_flags, show);
-    }
-
-    /**
-     * return true if an error window should appear when an invalid value is entered in the cell, false otherwise
-     * @return if an error window should appear when an invalid value is entered in the cell, false otherwise
-     * @see org.apache.poi.hssf.util.HSSFDataValidation utility class
-     */
-    public boolean getShowErrorOnInvalidValue()
-    {
-       return (this.opt_show_error_on_invalid_value.isSet(this.field_option_flags));
-    }
-
-    /**
-     * set the condition operator
-     * @param type - condition operator
-     * @see org.apache.poi.hssf.util.HSSFDataValidation utility class
-     */
-    public void setConditionOperator(int operator)
-    {
-        this.field_option_flags =  this.opt_condition_operator.setValue(this.field_option_flags, operator);
-    }
-
-    /**
-     * get the condition operator
-     * @return the condition operator
-     * @see org.apache.poi.hssf.util.HSSFDataValidation utility class
-     */
-    public int getConditionOperator()
-    {
-       return this.opt_condition_operator.getValue(this.field_option_flags);
-    }
-    // <-- end option flags
-
-    public void setFirstFormulaRPN( Stack rpn )
-    {
-        this.field_rpn_token_1 = rpn;
-    }
-
-    public void setFirstFormulaSize( short size )
-    {
-        this.field_size_first_formula = size;
-    }
-
-    public void setSecFormulaRPN( Stack rpn )
-    {
-        this.field_rpn_token_2 = rpn;
-    }
-
-    public void setSecFormulaSize( short size )
-    {
-        this.field_size_sec_formula = size;
-    }
-
-    public void setStringField( Integer type, String str_data )
-    {
-       if ( this._hash_strings == null )
-       {
-          this._hash_strings = new Hashtable();
-       }
-       StringHandler strHandler = new StringHandler();
-       if ( str_data == null )
-       {
-          str_data = "";
-       }
-       else
-       {
-          strHandler.setStringLength(str_data.length());
-       }
-       strHandler.setStringData(str_data);
-
-       strHandler.setUnicodeFlag((byte)0x00);
-       this._hash_strings.put( type, strHandler);
-    }
-
-    public String getStringField( Integer type )
-    {
-        return ((StringHandler)this._hash_strings.get(type)).getStringData();
-    }
-
-    public void setCellRangeAddress( HSSFCellRangeAddress range )
-    {
-        this.field_regions = range;
-    }
-
-    public HSSFCellRangeAddress getCellRangeAddress( )
-    {
-        return this.field_regions;
-    }
-
-    /**
-     * gets the option flags field.
-     * @return options - the option flags field
-     */
-    public int getOptionFlags()
-    {
-       return this.field_option_flags;
-    }
-
-    public String toString()
-    {
-      /** @todo DVRecord string representation */
-        StringBuffer buffer = new StringBuffer();
-
-        return buffer.toString();
-    }
-
-    public int serialize(int offset, byte [] data)
-    {
-        int size = this.getRecordSize();
-        LittleEndian.putShort(data, 0 + offset, sid);
-        LittleEndian.putShort(data, 2 + offset, ( short ) (size-4));
-
-        int pos = 4;
-        LittleEndian.putInt(data, pos + offset, this.getOptionFlags());
-        pos += 4;
-        pos += ((StringHandler)this._hash_strings.get( DVRecord.STRING_PROMPT_TITLE )).serialize(pos+offset, data);
-        pos += ((StringHandler)this._hash_strings.get( DVRecord.STRING_ERROR_TITLE )).serialize(pos+offset, data);
-        pos += ((StringHandler)this._hash_strings.get( DVRecord.STRING_PROMPT_TEXT )).serialize(pos+offset, data);
-        pos += ((StringHandler)this._hash_strings.get( DVRecord.STRING_ERROR_TEXT )).serialize(pos+offset, data);
-        LittleEndian.putShort(data, offset+pos, this.field_size_first_formula);
-        pos += 2;
-        LittleEndian.putShort(data, offset+pos, this.field_not_used_1);
-        pos += 2;
-
-        for (int k = 0; k < this.field_rpn_token_1.size(); k++)
-        {
-            Ptg ptg = ( Ptg ) this.field_rpn_token_1.get(k);
-            ptg.writeBytes(data, pos+offset);
-            pos += ptg.getSize();
-        }
-
-        LittleEndian.putShort(data, offset+pos, this.field_size_sec_formula);
-        pos += 2;
-        LittleEndian.putShort(data, offset+pos, this.field_not_used_2);
-        pos += 2;
-        if ( this.field_size_sec_formula > 0 )
-        {
-          for (int k = 0; k < this.field_rpn_token_2.size(); k++)
-          {
-              Ptg ptg = ( Ptg ) this.field_rpn_token_2.get(k);
-              ptg.writeBytes(data, pos+offset);
-              pos += ptg.getSize();
-          }
-        }
-        this.field_regions.serialize(pos+offset, data);
-        return size;
-    }
-
-    public int getRecordSize()
-    {
-        int size = 4+4+2+2+2+2;//header+options_field+first_formula_size+first_unused+sec_formula_size+sec+unused;
-        if ( this._hash_strings != null )
-        {
-            Enumeration enum_keys = this._hash_strings.keys();
-            while ( enum_keys.hasMoreElements() )
-            {
-                size += ((StringHandler)this._hash_strings.get( (Integer)enum_keys.nextElement() )).getSize();
-            }
-        }
-        size += this.field_size_first_formula+ this.field_size_sec_formula;
-        size += this.field_regions.getSize();
-        return size;
-    }
-
-    public short getSid()
-    {
-        return this.sid;
-    }
-    
-    /**
-     * Clones the object. Uses serialisation, as the
-     *  contents are somewhat complex
-     */
-    public Object clone() {
-       return cloneViaReserialise();
-    }
-
-    /**@todo DVRecord = Serializare */
-
-    private static final class StringHandler
-    {
-        private int     _string_length       = 0x0001;
-        private byte    _string_unicode_flag = 0x00;
-        private String  _string_data         = "0x00";
-        private int     _start_offset;
-        private int     _end_offset;
-
-        StringHandler()
-        {
-
-        }
-
-        StringHandler(RecordInputStream in)
-        {
-            this.fillFields(in);
-        }
-
-        protected void fillFields(RecordInputStream in) 
-        {
-            this._string_length       = in.readUShort(); 
-            this._string_unicode_flag = in.readByte(); 
-            if (this._string_unicode_flag == 1)
-            {
-               this._string_data = in.readUnicodeLEString(this._string_length);
-            }
-            else
-            {
-                this._string_data = in.readCompressedUnicode(this._string_length);
-            }
-        }
-
-        private void setStringData( String string_data )
-        {
-          this._string_data = string_data;
-        }
-
-        private String getStringData()
-        {
-            return this._string_data;
-        }
-
-        private int getEndOffset()
-        {
-            return this._end_offset;
-        }
-
-        public int serialize( int offset, byte[] data )
-        {
-            LittleEndian.putUShort(data, offset, this._string_length );
-            data[2 + offset] = this._string_unicode_flag;
-            if (this._string_unicode_flag == 1)
-            {
-                StringUtil.putUnicodeLE(this._string_data, data, 3 + offset);
-            }
-            else
-            {
-                StringUtil.putCompressedUnicode(this._string_data, data, 3 + offset);
-            }
-            return getSize();
-        }
-
-        private void setUnicodeFlag( byte flag )
-        {
-            this._string_unicode_flag = flag;
-        }
-
-        private void setStringLength( int len )
-        {
-           this._string_length = len;
-        }
-
-        private int getStringByteLength()
-        {
-            return (this._string_unicode_flag == 1) ? this._string_length * 2 : this._string_length;
-        }
-
-        public int getSize()
-        {
-            return 2 + 1 + getStringByteLength();
-        }
-    }
+public final class DVRecord extends Record {
+       public final static short sid = 0x01BE;
+       
+       /** the unicode string used for error/prompt title/text when not present */
+       private static final UnicodeString NULL_TEXT_STRING = new UnicodeString("\0");
+
+       /** Option flags */
+       private int _option_flags;
+       /** Title of the prompt box */
+       private UnicodeString _promptTitle;
+       /** Title of the error box */
+       private UnicodeString _errorTitle;
+       /** Text of the prompt box */
+       private UnicodeString _promptText;
+       /** Text of the error box */
+       private UnicodeString _errorText;
+       /** Not used - Excel seems to always write 0x3FE0 */
+       private short _not_used_1 = 0x3FE0;
+       /** Formula data for first condition (RPN token array without size field) */
+       private Ptg[] _formula1;
+       /** Not used - Excel seems to always write 0x0000 */
+       private short _not_used_2 = 0x0000;
+       /** Formula data for second condition (RPN token array without size field) */
+       private Ptg[] _formula2;
+       /** Cell range address list with all affected ranges */
+       private CellRangeAddressList _regions;
+
+       /**
+        * Option flags field
+        * 
+        * @see org.apache.poi.hssf.util.HSSFDataValidation utility class
+        */
+       private static final BitField opt_data_type                    = new BitField(0x0000000F);
+       private static final BitField opt_error_style                  = new BitField(0x00000070);
+       private static final BitField opt_string_list_formula          = new BitField(0x00000080);
+       private static final BitField opt_empty_cell_allowed           = new BitField(0x00000100);
+       private static final BitField opt_suppress_dropdown_arrow      = new BitField(0x00000200);
+       private static final BitField opt_show_prompt_on_cell_selected = new BitField(0x00040000);
+       private static final BitField opt_show_error_on_invalid_value  = new BitField(0x00080000);
+       private static final BitField opt_condition_operator           = new BitField(0x00700000);
+
+       /**
+        * Constructs a DV record and sets its fields appropriately.
+        *
+        * @param in the RecordInputstream to read the record from
+        */
+       public DVRecord(RecordInputStream in) {
+               super(in);
+       }
+
+       public DVRecord(int validationType, int operator, int errorStyle, boolean emptyCellAllowed,
+                       boolean suppressDropDownArrow, boolean isExplicitList,
+                       boolean showPromptBox, String promptTitle, String promptText, 
+                       boolean showErrorBox, String errorTitle, String errorText,
+                       Ptg[] formula1, Ptg[] formula2,
+                       CellRangeAddressList regions) {
+               
+               int flags = 0;
+               flags = opt_data_type.setValue(flags, validationType);
+               flags = opt_condition_operator.setValue(flags, operator);
+               flags = opt_error_style.setValue(flags, errorStyle);
+               flags = opt_empty_cell_allowed.setBoolean(flags, emptyCellAllowed);
+               flags = opt_suppress_dropdown_arrow.setBoolean(flags, suppressDropDownArrow);
+               flags = opt_string_list_formula.setBoolean(flags, isExplicitList);
+               flags = opt_show_prompt_on_cell_selected.setBoolean(flags, showPromptBox);
+               flags = opt_show_error_on_invalid_value.setBoolean(flags, showErrorBox);
+               _option_flags = flags;
+               _promptTitle = resolveTitleText(promptTitle);
+               _promptText = resolveTitleText(promptText);
+               _errorTitle = resolveTitleText(errorTitle);
+               _errorText = resolveTitleText(errorText);
+               _formula1 = formula1;
+               _formula2 = formula2;
+               _regions = regions;
+       }
+
+       protected void validateSid(short id) {
+               if (id != sid) {
+                       throw new RecordFormatException("NOT a valid DV RECORD");
+               }
+       }
+
+       protected void fillFields(RecordInputStream in) {
+               
+          _option_flags = in.readInt();
+          
+          _promptTitle = readUnicodeString(in);
+          _errorTitle = readUnicodeString(in);
+          _promptText = readUnicodeString(in);
+          _errorText = readUnicodeString(in);
+
+          int field_size_first_formula = in.readUShort(); 
+          _not_used_1 = in.readShort();
+
+          //read first formula data condition
+          _formula1 = Ptg.readTokens(field_size_first_formula, in);
+
+          int field_size_sec_formula = in.readUShort(); 
+          _not_used_2 = in.readShort();
+
+          //read sec formula data condition
+          _formula2 = Ptg.readTokens(field_size_sec_formula, in);
+
+          //read cell range address list with all affected ranges
+          _regions = new org.apache.poi.hssf.util.CellRangeAddressList(in);
+       }
+
+       // --> start option flags
+       /**
+        * @return the condition data type
+        * @see DVConstraint.ValidationType
+        */
+       public int getDataType() {
+          return opt_data_type.getValue(_option_flags);
+       }
+
+       /**
+        * @return the condition error style
+        * @see HSSFDataValidation.ErrorStyle
+        */
+       public int getErrorStyle() {
+          return opt_error_style.getValue(_option_flags);
+       }
+
+       /**
+        * @return <code>true</code> if in list validations the string list is explicitly given in the
+        *  formula, <code>false</code> otherwise
+         */
+       public boolean getListExplicitFormula() {
+          return (opt_string_list_formula.isSet(_option_flags));
+       }
+
+       /**
+        * @return <code>true</code> if empty values are allowed in cells, <code>false</code> otherwise
+        */
+       public boolean getEmptyCellAllowed() {
+          return (opt_empty_cell_allowed.isSet(_option_flags));
+       }
+
+
+       /**
+         * @return <code>true</code> if drop down arrow should be suppressed when list validation is
+         * used, <code>false</code> otherwise
+        */
+       public boolean getSuppressDropdownArrow() {
+          return (opt_suppress_dropdown_arrow.isSet(_option_flags));
+       }
+
+       /**
+        * @return <code>true</code> if a prompt window should appear when cell is selected, <code>false</code> otherwise
+        */
+       public boolean getShowPromptOnCellSelected() {
+          return (opt_show_prompt_on_cell_selected.isSet(_option_flags));
+       }
+
+       /**
+        * @return <code>true</code> if an error window should appear when an invalid value is entered
+        *  in the cell, <code>false</code> otherwise
+        */
+       public boolean getShowErrorOnInvalidValue() {
+          return (opt_show_error_on_invalid_value.isSet(_option_flags));
+       }
+
+       /**
+        * get the condition operator
+        * @return the condition operator
+        * @see org.apache.poi.hssf.util.HSSFDataValidation utility class
+        */
+       public int getConditionOperator() {
+          return opt_condition_operator.getValue(_option_flags);
+       }
+       // <-- end option flags
+
+
+
+
+       public CellRangeAddressList getCellRangeAddress() {
+               return this._regions;
+       }
+
+
+       public String toString() {
+               StringBuffer sb = new StringBuffer();
+               sb.append("[DV]\n");
+               sb.append(" options=").append(Integer.toHexString(_option_flags));
+               sb.append(" title-prompt=").append(formatTextTitle(_promptTitle));
+               sb.append(" title-error=").append(formatTextTitle(_errorTitle));
+               sb.append(" text-prompt=").append(formatTextTitle(_promptText));
+               sb.append(" text-error=").append(formatTextTitle(_errorText));
+               sb.append("\n");
+               appendFormula(sb, "Formula 1:",  _formula1);
+               appendFormula(sb, "Formula 2:",  _formula2);
+               sb.append("Regions: ");
+               int nRegions = _regions.countRanges();
+               for(int i=0; i<nRegions; i++) {
+                       if (i>0) {
+                               sb.append(", ");
+                       }
+                       CellRangeAddress addr = _regions.getCellRangeAddress(i);
+                       sb.append('(').append(addr.getFirstRow()).append(',').append(addr.getLastRow());
+                       sb.append(',').append(addr.getFirstColumn()).append(',').append(addr.getLastColumn()).append(')');
+               }
+               sb.append("\n");
+               sb.append("[/DV]");
+
+               return sb.toString();
+       }
+
+       private static String formatTextTitle(UnicodeString us) {
+               String str = us.getString();
+               if (str.length() == 1 && str.charAt(0) == '\0') {
+                       return "'\\0'";
+               }
+               return str;
+       }
+
+       private void appendFormula(StringBuffer sb, String label, Ptg[] ptgs) {
+               sb.append(label);
+               if (ptgs.length < 1) {
+                       sb.append("<empty>\n");
+                       return;
+               }
+               sb.append("\n");
+               for (int i = 0; i < ptgs.length; i++) {
+                       sb.append('\t').append(ptgs[i].toString()).append('\n');
+               }
+       }
+
+       public int serialize(int offset, byte [] data) {
+               int size = this.getRecordSize();
+               LittleEndian.putShort(data, 0 + offset, sid);
+               LittleEndian.putShort(data, 2 + offset, ( short ) (size-4));
+
+               int pos = 4;
+               LittleEndian.putInt(data, pos + offset, _option_flags);
+               pos += 4;
+               
+               pos += serializeUnicodeString(_promptTitle, pos+offset, data);
+               pos += serializeUnicodeString(_errorTitle, pos+offset, data);
+               pos += serializeUnicodeString(_promptText, pos+offset, data);
+               pos += serializeUnicodeString(_errorText, pos+offset, data);
+               LittleEndian.putUShort(data, offset+pos, Ptg.getEncodedSize(_formula1));
+               pos += 2;
+               LittleEndian.putUShort(data, offset+pos, _not_used_1);
+               pos += 2;
+
+               pos += Ptg.serializePtgs(_formula1, data, pos+offset);
+
+               LittleEndian.putUShort(data, offset+pos, Ptg.getEncodedSize(_formula2));
+               pos += 2;
+               LittleEndian.putShort(data, offset+pos, _not_used_2);
+               pos += 2;
+               pos += Ptg.serializePtgs(_formula2, data, pos+offset);
+               _regions.serialize(pos+offset, data);
+               return size;
+       }
+
+       /**
+        * When entered via the UI, Excel translates empty string into "\0"
+        * While it is possible to encode the title/text as empty string (Excel doesn't exactly crash),
+        * the resulting tool-tip text / message box looks wrong.  It is best to do the same as the 
+        * Excel UI and encode 'not present' as "\0". 
+        */
+       private static UnicodeString resolveTitleText(String str) {
+               if (str == null || str.length() < 1) {
+                       return NULL_TEXT_STRING;
+               }
+               return new UnicodeString(str);
+       }
+       
+       private static UnicodeString readUnicodeString(RecordInputStream in) {
+               return new UnicodeString(in);
+       }
+
+       private static int serializeUnicodeString(UnicodeString us, int offset, byte[] data) {
+               UnicodeRecordStats urs = new UnicodeRecordStats();
+               us.serialize(urs, offset, data);
+               return urs.recordSize;
+       }
+       private static int getUnicodeStringSize(UnicodeString str) {
+               return 3 + str.getString().length();
+       }
+
+       public int getRecordSize()  {
+               int size = 4+4+2+2+2+2;//header+options_field+first_formula_size+first_unused+sec_formula_size+sec+unused;
+               size += getUnicodeStringSize(_promptTitle);
+               size += getUnicodeStringSize(_errorTitle);
+               size += getUnicodeStringSize(_promptText);
+               size += getUnicodeStringSize(_errorText);
+               size += Ptg.getEncodedSize(_formula1);
+               size += Ptg.getEncodedSize(_formula2);
+               size += _regions.getSize();
+               return size;
+       }
+
+       public short getSid() {
+               return sid;
+       }
+       
+       /**
+        * Clones the object. Uses serialisation, as the
+        *  contents are somewhat complex
+        */
+       public Object clone() {
+               return cloneViaReserialise();
+       }
 }
index 41d8170dff26e6f058396dd886137bf3df8489ea..2e0a417e02213dca41fb40e6d041895dc079f1a9 100644 (file)
@@ -1,4 +1,3 @@
-
 /* ====================================================================
    Licensed to the Apache Software Foundation (ASF) under one or more
    contributor license agreements.  See the NOTICE file distributed with
    See the License for the specific language governing permissions and
    limitations under the License.
 ==================================================================== */
-        
 
 package org.apache.poi.hssf.record;
 
-import java.util.ArrayList;
-import java.util.Iterator;
-
+import org.apache.poi.ss.util.CellRangeAddress;
+import org.apache.poi.ss.util.CellRangeAddressList;
 import org.apache.poi.util.LittleEndian;
 
 /**
- * Title: Merged Cells Record
- * <br>
+ * Title: Merged Cells Record (0x00E5)
+ * <br/>
  * Description:  Optional record defining a square area of cells to "merged" into
  *               one cell. <br>
  * REFERENCE:  NONE (UNDOCUMENTED PRESENTLY) <br>
  * @author Andrew C. Oliver (acoliver at apache dot org)
  * @version 2.0-pre
  */
-public class MergeCellsRecord
-    extends Record
-{
-    public final static short sid = 0xe5;
-    private ArrayList         field_2_regions;
+public final class MergeCellsRecord extends Record {
+    public final static short sid = 0x00E5;
+    private CellRangeAddressList _regions;
 
-    public MergeCellsRecord()
-    {
+    /** 
+     * Creates an empty <tt>MergedCellsRecord</tt>
+     */
+    public MergeCellsRecord() {
+       _regions = new CellRangeAddressList();
     }
 
     /**
      * Constructs a MergedCellsRecord and sets its fields appropriately
      * @param in the RecordInputstream to read the record from
      */
-
-    public MergeCellsRecord(RecordInputStream in)
-    {
+    public MergeCellsRecord(RecordInputStream in) {
         super(in);
     }
 
-    protected void fillFields(RecordInputStream in)
-    {
-        short numAreas    = in.readShort();
-        field_2_regions   = new ArrayList(numAreas + 10);
-
-        for (int k = 0; k < numAreas; k++)
-        {
-            MergedRegion region =
-                new MergedRegion(in.readShort(), in.readShort(),
-                                 in.readShort(), in.readShort());
-
-            field_2_regions.add(region);
-        }
+    protected void fillFields(RecordInputStream in) {
+       _regions = new org.apache.poi.hssf.util.CellRangeAddressList(in);
     }
 
     /**
@@ -73,27 +58,8 @@ public class MergeCellsRecord
      * ahead and delete the record.
      * @return number of areas
      */
-
-    public short getNumAreas()
-    {
-        //if the array size is larger than a short (65536), the record can't hold that many merges anyway
-        if (field_2_regions == null) return 0;
-        return (short)field_2_regions.size();
-    }
-
-    /**
-     * set the number of merged areas.  You do not need to call this if you use addArea,
-     * it will be incremented automatically or decremented when an area is removed.  If
-     * you are setting this to 0 then you are a terrible person.  Just remove the record.
-     * (just kidding about you being a terrible person..hehe)
-     * @deprecated We now link the size to the actual array of merged regions
-     * @see #getNumAreas()
-     * @param numareas  number of areas
-     */
-
-    public void setNumAreas(short numareas)
-    {
-        
+    public short getNumAreas() {
+        return (short)_regions.countRanges();
     }
 
     /**
@@ -101,178 +67,84 @@ public class MergeCellsRecord
      * be correct provided you do not add ahead of or remove ahead of it  (in which case
      * you should increment or decrement appropriately....in other words its an arrayList)
      *
-     * @param rowfrom - the upper left hand corner's row
-     * @param colfrom - the upper left hand corner's col
-     * @param rowto - the lower right hand corner's row
-     * @param colto - the lower right hand corner's col
+     * @param firstRow - the upper left hand corner's row
+     * @param firstCol - the upper left hand corner's col
+     * @param lastRow - the lower right hand corner's row
+     * @param lastCol - the lower right hand corner's col
      * @return new index of said area (don't depend on it if you add/remove)
      */
-
-    //public int addArea(short rowfrom, short colfrom, short rowto, short colto)
-    public int addArea(int rowfrom, short colfrom, int rowto, short colto)
-    {
-        if (field_2_regions == null)
-        {
-            field_2_regions = new ArrayList(10);
-        }
-        MergedRegion region = new MergedRegion(rowfrom, rowto, colfrom,
-                                               colto);
-
-        field_2_regions.add(region);
-        return field_2_regions.size() - 1;
+    public void addArea(int firstRow, int firstCol, int lastRow, int lastCol) {
+       _regions.addCellRangeAddress(firstRow, firstCol, lastRow, lastCol);
     }
 
     /**
      * essentially unmerge the cells in the "area" stored at the passed in index
-     * @param area index
+     * @param areaIndex
      */
-
-    public void removeAreaAt(int area)
-    {
-        field_2_regions.remove(area);
+    public void removeAreaAt(int areaIndex) {
+        _regions.remove(areaIndex);
     }
 
     /**
-     * return the MergedRegion at the given index.
-     *
-     * @return MergedRegion representing the area that is Merged (r1,c1 - r2,c2)
+     * @return MergedRegion at the given index representing the area that is Merged (r1,c1 - r2,c2)
      */
-
-    public MergedRegion getAreaAt(int index)
-    {
-        return ( MergedRegion ) field_2_regions.get(index);
+    public CellRangeAddress getAreaAt(int index) {
+        return _regions.getCellRangeAddress(index);
     }
 
-    public int getRecordSize()
-    {
-        int retValue;
-
-        retValue = 6 + (8 * field_2_regions.size());
-        return retValue;
+    public int getRecordSize() {
+       return 4 + _regions.getSize();
     }
 
-    public short getSid()
-    {
+    public short getSid() {
         return sid;
     }
 
-    public int serialize(int offset, byte [] data)
-    {
-        int recordsize = getRecordSize();
-        int pos        = 6;
+    public int serialize(int offset, byte [] data) {
+        int dataSize = _regions.getSize();
 
         LittleEndian.putShort(data, offset + 0, sid);
-        LittleEndian.putShort(data, offset + 2, ( short ) (recordsize - 4));
-        LittleEndian.putShort(data, offset + 4, getNumAreas());
-        for (int k = 0; k < getNumAreas(); k++)
-        {
-            MergedRegion region = getAreaAt(k);
-
-            //LittleEndian.putShort(data, offset + pos, region.row_from);
-            LittleEndian.putShort(data, offset + pos, ( short ) region.row_from);
-            pos += 2;
-            //LittleEndian.putShort(data, offset + pos, region.row_to);
-            LittleEndian.putShort(data, offset + pos, ( short ) region.row_to);
-            pos += 2;
-            LittleEndian.putShort(data, offset + pos, region.col_from);
-            pos += 2;
-            LittleEndian.putShort(data, offset + pos, region.col_to);
-            pos += 2;
-        }
-        return recordsize;
+        LittleEndian.putUShort(data, offset + 2, dataSize);
+        _regions.serialize(offset + 4, data);
+        return 4 + dataSize;
     }
 
-    public String toString()
-    {
+    public String toString() {
         StringBuffer retval = new StringBuffer();
 
         retval.append("[MERGEDCELLS]").append("\n");
         retval.append("     .sid        =").append(sid).append("\n");
         retval.append("     .numregions =").append(getNumAreas())
             .append("\n");
-        for (int k = 0; k < getNumAreas(); k++)
-        {
-            MergedRegion region = ( MergedRegion ) field_2_regions.get(k);
+        for (int k = 0; k < _regions.countRanges(); k++) {
+            CellRangeAddress region = _regions.getCellRangeAddress(k);
 
-            retval.append("     .rowfrom    =").append(region.row_from)
+            retval.append("     .rowfrom    =").append(region.getFirstRow())
                 .append("\n");
-            retval.append("     .colfrom    =").append(region.col_from)
+            retval.append("     .colfrom    =").append(region.getFirstColumn())
                 .append("\n");
-            retval.append("     .rowto      =").append(region.row_to)
+            retval.append("     .rowto      =").append(region.getLastRow())
                 .append("\n");
-            retval.append("     .colto      =").append(region.col_to)
+            retval.append("     .colto      =").append(region.getLastColumn())
                 .append("\n");
         }
         retval.append("[MERGEDCELLS]").append("\n");
         return retval.toString();
     }
 
-    protected void validateSid(short id)
-    {
-        if (id != sid)
-        {
+    protected void validateSid(short id) {
+        if (id != sid) {
             throw new RecordFormatException("NOT A MERGEDCELLS RECORD!! "
                                             + id);
         }
     }
 
-    /**
-     * this is a low level representation of a MergedRegion of cells.  It is an
-     * inner class because we do not want it used without reference to this class.
-     *
-     */
-
-    public class MergedRegion
-    {
-
-        /**
-         * create a merged region all in one stroke.
-         */
-
-        //public MergedRegion(short row_from, short row_to, short col_from,
-        public MergedRegion(int row_from, int row_to, short col_from,
-                            short col_to)
-        {
-            this.row_from = row_from;
-            this.row_to   = row_to;
-            this.col_from = col_from;
-            this.col_to   = col_to;
-        }
-
-        /**
-         * upper lefthand corner row
-         */
-
-        //public short row_from;
-        public int row_from;
-
-        /**
-         * lower right hand corner row
-         */
-
-        //public short row_to;
-        public int row_to;
-
-        /**
-         * upper right hand corner col
-         */
-
-        public short col_from;
-
-        /**
-         * lower right hand corner col
-         */
-
-        public short col_to;
-    }
-
     public Object clone() {
         MergeCellsRecord rec = new MergeCellsRecord();        
-        rec.field_2_regions = new ArrayList();
-        Iterator iterator = field_2_regions.iterator();
-        while (iterator.hasNext()) {
-           MergedRegion oldRegion = (MergedRegion)iterator.next();
-           rec.addArea(oldRegion.row_from, oldRegion.col_from, oldRegion.row_to, oldRegion.col_to);
+        for (int k = 0; k < _regions.countRanges(); k++) {
+            CellRangeAddress oldRegion = _regions.getCellRangeAddress(k);
+           rec.addArea(oldRegion.getFirstRow(), oldRegion.getFirstColumn(), 
+                          oldRegion.getLastRow(), oldRegion.getLastColumn());
         }
         
         return rec;
index 8ad4d0cefa6a11443262a4d86b2754f968aec003..2f36ea644ff862539715e413faf5df551cba8a08 100644 (file)
@@ -1,4 +1,3 @@
-
 /* ====================================================================
    Licensed to the Apache Software Foundation (ASF) under one or more
    contributor license agreements.  See the NOTICE file distributed with
    See the License for the specific language governing permissions and
    limitations under the License.
 ==================================================================== */
-        
 
 package org.apache.poi.hssf.record;
 
-import java.util.*;
-
 import org.apache.poi.util.LittleEndian;
 
 /**
- * Title:        Selection Record<P>
+ * Title:        Selection Record (0x001D)<P>
  * Description:  shows the user's selection on the sheet
  *               for write set num refs to 0<P>
  *
- * TODO :  Fully implement reference subrecords.
  * REFERENCE:  PG 291 Microsoft Excel 97 Developer's Kit (ISBN: 1-57231-498-2)<P>
  * @author Andrew C. Oliver (acoliver at apache dot org)
  * @author Jason Height (jheight at chariot dot net dot au)
  * @author Glen Stampoultzis (glens at apache.org)
  */
+public final class SelectionRecord extends Record {
+    public final static short sid = 0x001D;
+    private byte        field_1_pane;
+    private int         field_2_row_active_cell;
+    private int         field_3_col_active_cell;
+    private int         field_4_active_cell_ref_index;
+    private Reference[] field_6_refs;
 
-public class SelectionRecord
-    extends Record
-{
-    public final static short sid = 0x1d;
-    private byte              field_1_pane;
-    //private short             field_2_row_active_cell;
-    private int             field_2_row_active_cell;
-    private short             field_3_col_active_cell;
-    private short             field_4_ref_active_cell;
-    private short             field_5_num_refs;
-    private ArrayList         field_6_refs;     // not used yet
-
+    /**
+     *  Note - column values are 8-bit so cannot use <tt>CellRangeAddressList</tt>
+     */
     public class Reference {
-      private short field_1_first_row;
-      private short field_2_last_row;
-      private byte field_3_first_column;
-      private byte field_4_last_column;
-      
-      Reference(RecordInputStream in) {
-        field_1_first_row = in.readShort();
-        field_2_last_row = in.readShort();
-        field_3_first_column = in.readByte();
-        field_4_last_column = in.readByte();
-      }
+        /* package */ static final int ENCODED_SIZE = 6;
+        private int _firstRow;
+               private int _lastRow;
+               private int _firstCol;
+               private int _lastCol;
       
-      public short getFirstRow() {
-         return field_1_first_row;
-      }
-      
-      public short getLastRow() {
-         return field_2_last_row;
-      }
-      
-      public byte getFirstColumn() {
-         return field_3_first_column;
-      }
-      
-      public byte getLastColumn() {
-         return field_4_last_column;
-      }
+               /* package */ Reference(int firstRow, int lastRow, int firstColumn, int lastColumn) {
+                       _firstRow = firstRow;
+                       _lastRow = lastRow;
+                       _firstCol = firstColumn;
+                       _lastCol = lastColumn;
+               }
+               /* package */ Reference(RecordInputStream in) {
+                   this(in.readUShort(), in.readUShort(), in.readUByte(), in.readUByte());
+               }
+               public void serialize(int offset, byte[] data) {
+                       LittleEndian.putUShort(data, offset + 0, _firstRow);
+                       LittleEndian.putUShort(data, offset + 2, _lastRow);
+                       LittleEndian.putByte(data, offset + 4, _firstCol);
+                       LittleEndian.putByte(data, offset + 6, _lastCol);
+               }
+
+               public int getFirstRow() {
+                   return _firstRow;
+               }
+               public int getLastRow() {
+                   return _lastRow;
+               }
+               public int getFirstColumn() {
+                   return _firstCol;
+               }
+               public int getLastColumn() {
+                   return _lastCol;
+               }
     }
 
-    public SelectionRecord()
-    {
+    /**
+     * Creates a default selection record (cell A1, in pane ID 3)
+     */
+    public SelectionRecord(int activeCellRow, int activeCellCol) {
+       field_1_pane = 3; // pane id 3 is always present.  see OOO sec 5.75 'PANE'
+       field_2_row_active_cell = activeCellRow;
+       field_3_col_active_cell = activeCellCol;
+       field_4_active_cell_ref_index = 0;
+       field_6_refs = new Reference[] {
+               new Reference(activeCellRow, activeCellRow, activeCellCol, activeCellCol),
+       };
     }
 
     /**
@@ -86,41 +95,33 @@ public class SelectionRecord
      * @param in the RecordInputstream to read the record from
      */
 
-    public SelectionRecord(RecordInputStream in)
-    {
+    public SelectionRecord(RecordInputStream in) {
         super(in);
     }
 
-    protected void validateSid(short id)
-    {
-        if (id != sid)
-        {
+    protected void validateSid(short id) {
+        if (id != sid) {
             throw new RecordFormatException("NOT A valid Selection RECORD");
         }
     }
 
-    protected void fillFields(RecordInputStream in)
-    {
+    protected void fillFields(RecordInputStream in) {
         field_1_pane            = in.readByte();
-        //field_2_row_active_cell = LittleEndian.getShort(data, 1 + offset);
         field_2_row_active_cell = in.readUShort();
         field_3_col_active_cell = in.readShort();
-        field_4_ref_active_cell = in.readShort();
-        field_5_num_refs        = in.readShort();
+        field_4_active_cell_ref_index = in.readShort();
+        int field_5_num_refs    = in.readUShort();
         
-        field_6_refs = new ArrayList(field_5_num_refs);
-        for (int i=0; i<field_5_num_refs; i++) {
-          field_6_refs.add(new Reference(in));
-        }
+        field_6_refs = new Reference[field_5_num_refs];
+        for (int i = 0; i < field_6_refs.length; i++) {
+               field_6_refs[i] = new Reference(in);
+               }
     }
 
     /**
      * set which window pane this is for
-     * @param pane
      */
-
-    public void setPane(byte pane)
-    {
+    public void setPane(byte pane) {
         field_1_pane = pane;
     }
 
@@ -128,10 +129,7 @@ public class SelectionRecord
      * set the active cell's row
      * @param row number of active cell
      */
-
-    //public void setActiveCellRow(short row)
-    public void setActiveCellRow(int row)
-    {
+    public void setActiveCellRow(int row) {
         field_2_row_active_cell = row;
     }
 
@@ -139,9 +137,7 @@ public class SelectionRecord
      * set the active cell's col
      * @param col number of active cell
      */
-
-    public void setActiveCellCol(short col)
-    {
+    public void setActiveCellCol(short col) {
         field_3_col_active_cell = col;
     }
 
@@ -149,29 +145,14 @@ public class SelectionRecord
      * set the active cell's reference number
      * @param ref number of active cell
      */
-
-    public void setActiveCellRef(short ref)
-    {
-        field_4_ref_active_cell = ref;
-    }
-
-    /**
-     * set the number of cell refs (we don't support selection so set to 0
-     * @param refs - number of references
-     */
-
-    public void setNumRefs(short refs)
-    {
-        field_5_num_refs = refs;
+    public void setActiveCellRef(short ref) {
+        field_4_active_cell_ref_index = ref;
     }
 
     /**
-     * get which window pane this is for
-     * @return pane
+     * @return the pane ID which window pane this is for
      */
-
-    public byte getPane()
-    {
+    public byte getPane() {
         return field_1_pane;
     }
 
@@ -179,10 +160,7 @@ public class SelectionRecord
      * get the active cell's row
      * @return row number of active cell
      */
-
-    //public short getActiveCellRow()
-    public int getActiveCellRow()
-    {
+    public int getActiveCellRow() {
         return field_2_row_active_cell;
     }
 
@@ -190,9 +168,7 @@ public class SelectionRecord
      * get the active cell's col
      * @return col number of active cell
      */
-
-    public short getActiveCellCol()
-    {
+    public int getActiveCellCol() {
         return field_3_col_active_cell;
     }
 
@@ -200,20 +176,8 @@ public class SelectionRecord
      * get the active cell's reference number
      * @return ref number of active cell
      */
-
-    public short getActiveCellRef()
-    {
-        return field_4_ref_active_cell;
-    }
-
-    /**
-     * get the number of cell refs (we don't support selection so set to 0
-     * @return refs - number of references
-     */
-
-    public short getNumRefs()
-    {
-        return field_5_num_refs;
+    public int getActiveCellRef() {
+        return (short)field_4_active_cell_ref_index;
     }
 
     public String toString()
@@ -230,47 +194,44 @@ public class SelectionRecord
         buffer.append("    .activecellref   = ")
             .append(Integer.toHexString(getActiveCellRef())).append("\n");
         buffer.append("    .numrefs         = ")
-            .append(Integer.toHexString(getNumRefs())).append("\n");
+            .append(Integer.toHexString(field_6_refs.length)).append("\n");
         buffer.append("[/SELECTION]\n");
         return buffer.toString();
     }
-
-//hacked to provide one cell reference to 0,0 - 0,0
-    public int serialize(int offset, byte [] data)
-    {
-        LittleEndian.putShort(data, 0 + offset, sid);
-        LittleEndian.putShort(data, 2 + offset, ( short ) 15);
-        data[ 4 + offset ] = getPane();
-        //LittleEndian.putShort(data, 5 + offset, getActiveCellRow());
-        LittleEndian.putShort(data, 5 + offset, ( short ) getActiveCellRow());
-        LittleEndian.putShort(data, 7 + offset, getActiveCellCol());
-        LittleEndian.putShort(data, 9 + offset, getActiveCellRef());
-        LittleEndian.putShort(data, 11 + offset, ( short ) 1);
-        LittleEndian.putShort(data, 13 + offset, ( short ) getActiveCellRow());
-        LittleEndian.putShort(data, 15 + offset, ( short ) getActiveCellRow());
-        data[ 17 + offset ] = (byte)getActiveCellCol();
-        data[ 18 + offset ] = (byte)getActiveCellCol();
-        return getRecordSize();
+    private int getDataSize() {
+       return 9 // 1 byte + 4 shorts 
+               + field_6_refs.length * Reference.ENCODED_SIZE;
+    }
+    public int serialize(int offset, byte [] data) {
+       int dataSize = getDataSize();
+        LittleEndian.putUShort(data, 0 + offset, sid);
+        LittleEndian.putUShort(data, 2 + offset, dataSize);
+        LittleEndian.putByte(data, 4 + offset,  getPane());
+        LittleEndian.putUShort(data, 5 + offset, getActiveCellRow());
+        LittleEndian.putUShort(data, 7 + offset, getActiveCellCol());
+        LittleEndian.putUShort(data, 9 + offset, getActiveCellRef());
+        int nRefs = field_6_refs.length;
+        LittleEndian.putUShort(data, 11 + offset, nRefs);
+        for (int i = 0; i < field_6_refs.length; i++) {
+                       Reference r = field_6_refs[i];
+                       r.serialize(offset + 13 + i * Reference.ENCODED_SIZE, data);
+               }
+        return 4 + dataSize;
     }
 
-    public int getRecordSize()
-    {
-        return 19;
+    public int getRecordSize() {
+        return 4 + getDataSize();
     }
 
-    public short getSid()
-    {
+    public short getSid() {
         return sid;
     }
 
     public Object clone() {
-      SelectionRecord rec = new SelectionRecord();
-      rec.field_1_pane = field_1_pane;
-      rec.field_2_row_active_cell = field_2_row_active_cell;
-      rec.field_3_col_active_cell = field_3_col_active_cell;
-      rec.field_4_ref_active_cell = field_4_ref_active_cell;
-      rec.field_5_num_refs = field_5_num_refs;
-      rec.field_6_refs = field_6_refs;
-      return rec;
+        SelectionRecord rec = new SelectionRecord(field_2_row_active_cell, field_3_col_active_cell);
+        rec.field_1_pane = field_1_pane;
+        rec.field_4_active_cell_ref_index = field_4_active_cell_ref_index;
+        rec.field_6_refs = field_6_refs;
+        return rec;
     }
 }
index d91269ed342eee9832ef3466f0f63c2c55ac550b..cc74b98f699a3101ed3ffdef3049fb762d08d4ec 100644 (file)
@@ -25,6 +25,7 @@ import org.apache.poi.hssf.record.CFRuleRecord;
 import org.apache.poi.hssf.record.Record;
 import org.apache.poi.hssf.record.RecordInputStream;
 import org.apache.poi.ss.util.Region;
+import org.apache.poi.ss.util.CellRangeAddress;
 import org.apache.poi.util.POILogFactory;
 import org.apache.poi.util.POILogger;
 
@@ -68,7 +69,7 @@ public final class CFRecordsAggregate extends Record
                }
        }
 
-       public CFRecordsAggregate(Region[] regions, CFRuleRecord[] rules) {
+       public CFRecordsAggregate(CellRangeAddress[] regions, CFRuleRecord[] rules) {
                this(new CFHeaderRecord(regions), rules);
        }
 
diff --git a/src/java/org/apache/poi/hssf/record/aggregates/DataValidityTable.java b/src/java/org/apache/poi/hssf/record/aggregates/DataValidityTable.java
new file mode 100644 (file)
index 0000000..133386d
--- /dev/null
@@ -0,0 +1,178 @@
+/* ====================================================================\r
+   Licensed to the Apache Software Foundation (ASF) under one or more\r
+   contributor license agreements.  See the NOTICE file distributed with\r
+   this work for additional information regarding copyright ownership.\r
+   The ASF licenses this file to You under the Apache License, Version 2.0\r
+   (the "License"); you may not use this file except in compliance with\r
+   the License.  You may obtain a copy of the License at\r
+\r
+       http://www.apache.org/licenses/LICENSE-2.0\r
+\r
+   Unless required by applicable law or agreed to in writing, software\r
+   distributed under the License is distributed on an "AS IS" BASIS,\r
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+   See the License for the specific language governing permissions and\r
+   limitations under the License.\r
+==================================================================== */\r
+\r
+package org.apache.poi.hssf.record.aggregates;\r
+\r
+import java.util.ArrayList;\r
+import java.util.List;\r
+\r
+import org.apache.poi.hssf.model.RecordStream;\r
+import org.apache.poi.hssf.record.CFHeaderRecord;\r
+import org.apache.poi.hssf.record.CFRuleRecord;\r
+import org.apache.poi.hssf.record.DVALRecord;\r
+import org.apache.poi.hssf.record.DVRecord;\r
+import org.apache.poi.hssf.record.EOFRecord;\r
+import org.apache.poi.hssf.record.HyperlinkRecord;\r
+import org.apache.poi.hssf.record.MergeCellsRecord;\r
+import org.apache.poi.hssf.record.PaneRecord;\r
+import org.apache.poi.hssf.record.Record;\r
+import org.apache.poi.hssf.record.SelectionRecord;\r
+import org.apache.poi.hssf.record.WindowTwoRecord;\r
+\r
+/**\r
+ * Manages the DVALRecord and DVRecords for a single sheet<br/>\r
+ * See OOO excelfileformat.pdf section 4.14\r
+ * @author Josh Micich\r
+ */\r
+public final class DataValidityTable extends RecordAggregate {\r
+\r
+       private static final short sid = -0x01B2; // not a real record\r
+       private final DVALRecord _headerRec;\r
+       /**\r
+        * The list of data validations for the current sheet.\r
+        * Note - this may be empty (contrary to OOO documentation)\r
+        */\r
+       private final List _validationList;\r
+\r
+       public DataValidityTable(RecordStream rs) {\r
+               _headerRec = (DVALRecord) rs.getNext();\r
+               List temp = new ArrayList();\r
+               while (rs.peekNextClass() == DVRecord.class) {\r
+                       temp.add(rs.getNext());\r
+               }\r
+               _validationList = temp;\r
+       }\r
+\r
+       private DataValidityTable() {\r
+               _headerRec = new DVALRecord();\r
+               _validationList = new ArrayList();\r
+       }\r
+\r
+       public short getSid() {\r
+               return sid;\r
+       }\r
+\r
+       public int serialize(int offset, byte[] data) {\r
+               int result = _headerRec.serialize(offset, data);\r
+               for (int i = 0; i < _validationList.size(); i++) {\r
+                       result += ((Record) _validationList.get(i)).serialize(offset + result, data);\r
+               }\r
+               return result;\r
+       }\r
+\r
+       public int getRecordSize() {\r
+               int result = _headerRec.getRecordSize();\r
+               for (int i = _validationList.size() - 1; i >= 0; i--) {\r
+                       result += ((Record) _validationList.get(i)).getRecordSize();\r
+               }\r
+               return result;\r
+       }\r
+\r
+       /**\r
+        * Creates a new <tt>DataValidityTable</tt> and inserts it in the right\r
+        * place in the sheetRecords list.\r
+        */\r
+       public static DataValidityTable createForSheet(List sheetRecords) {\r
+               int index = findDVTableInsertPos(sheetRecords);\r
+\r
+               DataValidityTable result = new DataValidityTable();\r
+               sheetRecords.add(index, result);\r
+               return result;\r
+       }\r
+       \r
+    /**\r
+     * Finds the index where the sheet validations header record should be inserted\r
+     * @param records the records for this sheet\r
+     * \r
+     * + WINDOW2\r
+     * o SCL\r
+     * o PANE\r
+     * oo SELECTION\r
+     * o STANDARDWIDTH\r
+     * oo MERGEDCELLS\r
+     * o LABELRANGES\r
+     * o PHONETICPR\r
+     * o Conditional Formatting Table\r
+     * o Hyperlink Table\r
+     * o Data Validity Table\r
+     * o SHEETLAYOUT\r
+     * o SHEETPROTECTION\r
+     * o RANGEPROTECTION\r
+     * + EOF\r
+     */\r
+    private static int findDVTableInsertPos(List records) {\r
+               int i = records.size() - 1;\r
+               if (!(records.get(i) instanceof EOFRecord)) {\r
+                       throw new IllegalStateException("Last sheet record should be EOFRecord");\r
+               }\r
+               while (i > 0) {\r
+                       i--;\r
+                       Record rec = (Record) records.get(i);\r
+                       if (isPriorRecord(rec.getSid())) {\r
+                               Record nextRec = (Record) records.get(i + 1);\r
+                               if (!isSubsequentRecord(nextRec.getSid())) {\r
+                                       throw new IllegalStateException("Unexpected (" + nextRec.getClass().getName()\r
+                                                       + ") found after (" + rec.getClass().getName() + ")");\r
+                               }\r
+                               return i;\r
+                       }\r
+                       if (!isSubsequentRecord(rec.getSid())) {\r
+                               throw new IllegalStateException("Unexpected (" + rec.getClass().getName()\r
+                                               + ") while looking for DV Table insert pos");\r
+                       }\r
+               }\r
+               return 0;\r
+       }\r
+\r
+       // TODO - add UninterpretedRecord as base class for many of these\r
+       // unimplemented sids\r
+\r
+       private static boolean isPriorRecord(short sid) {\r
+               switch(sid) {\r
+                       case WindowTwoRecord.sid:\r
+                       case 0x00A0: // SCL\r
+                       case PaneRecord.sid:\r
+                       case SelectionRecord.sid:\r
+                       case 0x0099: // STANDARDWIDTH\r
+                       case MergeCellsRecord.sid:\r
+                       case 0x015F: // LABELRANGES\r
+                       case 0x00EF: // PHONETICPR\r
+                       case CFHeaderRecord.sid:\r
+                       case CFRuleRecord.sid:\r
+                       case HyperlinkRecord.sid:\r
+                       case 0x0800: // QUICKTIP\r
+                               return true;\r
+               }\r
+               return false;\r
+       }\r
+\r
+       private static boolean isSubsequentRecord(short sid) {\r
+               switch(sid) {\r
+                       case 0x0862: // SHEETLAYOUT\r
+                       case 0x0867: // SHEETPROTECTION\r
+                       case 0x0868: // RANGEPROTECTION\r
+                       case EOFRecord.sid:\r
+                               return true;\r
+               }\r
+               return false;\r
+       }\r
+\r
+       public void addDataValidation(DVRecord dvRecord) {\r
+               _validationList.add(dvRecord);\r
+               _headerRec.setDVRecNo(_validationList.size());\r
+       }\r
+}\r
diff --git a/src/java/org/apache/poi/hssf/record/aggregates/RecordAggregate.java b/src/java/org/apache/poi/hssf/record/aggregates/RecordAggregate.java
new file mode 100644 (file)
index 0000000..3a86871
--- /dev/null
@@ -0,0 +1,41 @@
+/* ====================================================================\r
+   Licensed to the Apache Software Foundation (ASF) under one or more\r
+   contributor license agreements.  See the NOTICE file distributed with\r
+   this work for additional information regarding copyright ownership.\r
+   The ASF licenses this file to You under the Apache License, Version 2.0\r
+   (the "License"); you may not use this file except in compliance with\r
+   the License.  You may obtain a copy of the License at\r
+\r
+       http://www.apache.org/licenses/LICENSE-2.0\r
+\r
+   Unless required by applicable law or agreed to in writing, software\r
+   distributed under the License is distributed on an "AS IS" BASIS,\r
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+   See the License for the specific language governing permissions and\r
+   limitations under the License.\r
+==================================================================== */\r
+\r
+package org.apache.poi.hssf.record.aggregates;\r
+\r
+import org.apache.poi.hssf.record.Record;\r
+import org.apache.poi.hssf.record.RecordInputStream;\r
+\r
+/**\r
+ * <tt>RecordAggregate</tt>s are groups of of BIFF <tt>Record</tt>s that are typically stored \r
+ * together and/or updated together.  Workbook / Sheet records are typically stored in a sequential\r
+ * list, which does not provide much structure to coordinate updates.\r
+ * \r
+ * @author Josh Micich\r
+ */\r
+public abstract class RecordAggregate extends Record {\r
+       // TODO - convert existing aggregate classes to proper subclasses of this one\r
+       protected final void validateSid(short id) {\r
+               // TODO - break class hierarchy and make separate from Record\r
+               throw new RuntimeException("Should not be called");\r
+       }\r
+       protected final void fillFields(RecordInputStream in) {\r
+               throw new RuntimeException("Should not be called");\r
+       }\r
+       // force subclassses to provide better implementation than default\r
+       public abstract int getRecordSize();\r
+}\r
diff --git a/src/java/org/apache/poi/hssf/record/cf/CellRange.java b/src/java/org/apache/poi/hssf/record/cf/CellRange.java
deleted file mode 100644 (file)
index 8331fd9..0000000
+++ /dev/null
@@ -1,513 +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.record.cf;
-
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.List;
-
-import org.apache.poi.hssf.util.Region;
-
-/**
- * 
- * @author Dmitriy Kumshayev
- */
-public final class CellRange
-{
-       /** 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 = { };
-       
-       private int _firstRow;
-       private int _lastRow;
-       private int _firstColumn;
-       private int _lastColumn;
-       
-       /**
-        * 
-        * @param firstRow
-        * @param lastRow pass <tt>-1</tt> for full column ranges
-        * @param firstColumn
-        * @param lastColumn  pass <tt>-1</tt> for full row ranges
-        */
-       public CellRange(int firstRow, int lastRow, int firstColumn, int lastColumn)
-       {
-               if(!isValid(firstRow, lastRow, firstColumn, lastColumn)) {
-                       throw new IllegalArgumentException("invalid cell range (" + firstRow + ", " + lastRow 
-                                       + ", " + firstColumn + ", " + lastColumn + ")");
-               }
-               _firstRow = firstRow;
-               _lastRow = convertM1ToMax(lastRow, LAST_ROW_INDEX);
-               _firstColumn = firstColumn;
-               _lastColumn = convertM1ToMax(lastColumn, LAST_COLUMN_INDEX);
-       }
-       
-       /** 
-        * Range arithmetic is easier when using a large positive number for 'max row or column' 
-        * instead of <tt>-1</tt>. 
-        */
-       private static int convertM1ToMax(int lastIx, int maxIndex) {
-               if(lastIx < 0) {
-                       return maxIndex;
-               }
-               return lastIx;
-       }
-
-       public boolean isFullColumnRange() {
-               return _firstRow == 0 && _lastRow == LAST_ROW_INDEX;
-       }
-       public boolean isFullRowRange() {
-               return _firstColumn == 0 && _lastColumn == LAST_COLUMN_INDEX;
-       }
-       
-       private static CellRange createFromRegion(org.apache.poi.ss.util.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 < 0 || lastRow > LAST_ROW_INDEX) {
-                       return false;
-               }
-               if(firstRow < 0 || firstRow > LAST_ROW_INDEX) {
-                       return false;
-               }
-               
-               if(lastColumn < 0 || lastColumn > LAST_COLUMN_INDEX) {
-                       return false;
-               }
-               if(firstColumn < 0 || firstColumn > LAST_COLUMN_INDEX) {
-                       return false;
-               }
-               return true;
-       }
-       
-       public int getFirstRow()
-       {
-               return _firstRow;
-       }
-       public int getLastRow()
-       {
-               return _lastRow;
-       }
-       public int getFirstColumn()
-       {
-               return _firstColumn;
-       }
-       public int getLastColumn()
-       {
-               return _lastColumn;
-       }
-       
-       public static final int NO_INTERSECTION = 1;
-       public static final int OVERLAP = 2;
-       /** first range is within the second range */
-       public static final int INSIDE = 3;
-       /** first range encloses or is equal to the second */
-       public static final int ENCLOSES = 4;
-       
-       /**
-        * Intersect this range with the specified range.
-        * 
-        * @param another - the specified range
-        * @return code which reflects how the specified range is related to this range.<br/>
-        * Possible return codes are:   
-        *              NO_INTERSECTION - the specified range is outside of this range;<br/> 
-        *              OVERLAP - both ranges partially overlap;<br/>
-        *              INSIDE - the specified range is inside of this one<br/>
-        *              ENCLOSES - the specified range encloses (possibly exactly the same as) this range<br/>
-        */
-       public int intersect(CellRange another )
-       {
-               
-               int firstRow = another.getFirstRow();
-               int lastRow  = another.getLastRow();
-               int firstCol = another.getFirstColumn();
-               int lastCol  = another.getLastColumn();
-               
-               if
-               ( 
-                               gt(getFirstRow(),lastRow) || 
-                               lt(getLastRow(),firstRow) ||
-                               gt(getFirstColumn(),lastCol) || 
-                               lt(getLastColumn(),firstCol) 
-               )
-               {
-                       return NO_INTERSECTION;
-               }
-               else if( contains(another) )
-               {
-                       return INSIDE;
-               }
-               else if( another.contains(this))
-               {
-                       return ENCLOSES;
-               }
-               else
-               {
-                       return OVERLAP;
-               }
-                       
-       }
-       
-       /**
-        * Do all possible cell merges between cells of the list so that:<br>
-        *      <li>if a cell range is completely inside of another cell range, it gets removed from the list 
-        *      <li>if two cells have a shared border, merge them into one bigger cell range
-        * @param cellRangeList
-        * @return updated List of cell ranges
-        */
-       public static CellRange[] mergeCellRanges(CellRange[] cellRanges) {
-               if(cellRanges.length < 1) {
-                       return cellRanges;
-               }
-               List temp = mergeCellRanges(Arrays.asList(cellRanges));
-               return toArray(temp);
-       }
-       private static List mergeCellRanges(List cellRangeList)
-       {
-
-               while(cellRangeList.size() > 1)
-               {
-                       boolean somethingGotMerged = false;
-                       
-                       for( int i=0; i<cellRangeList.size(); i++)
-                       {
-                               CellRange range1 = (CellRange)cellRangeList.get(i);
-                               for( int j=i+1; j<cellRangeList.size(); j++)
-                               {
-                                       CellRange range2 = (CellRange)cellRangeList.get(j);
-                                       
-                                       CellRange[] mergeResult = mergeRanges(range1, range2);
-                                       if(mergeResult == null) {
-                                               continue;
-                                       }
-                                       somethingGotMerged = true;
-                                       // overwrite range1 with first result 
-                                       cellRangeList.set(i, mergeResult[0]);
-                                       // remove range2
-                                       cellRangeList.remove(j--);
-                                       // add any extra results beyond the first
-                                       for(int k=1; k<mergeResult.length; k++) {
-                                               j++;
-                                               cellRangeList.add(j, mergeResult[k]);
-                                       }
-                               }
-                       }
-                       if(!somethingGotMerged) {
-                               break;
-                       }
-               }
-               
-
-               return cellRangeList;
-       }
-       
-       /**
-        * @return the new range(s) to replace the supplied ones.  <code>null</code> if no merge is possible
-        */
-       private static CellRange[] mergeRanges(CellRange range1, CellRange range2) {
-               
-               int x = range1.intersect(range2);
-               switch(x)
-               {
-                       case CellRange.NO_INTERSECTION: 
-                               if( range1.hasExactSharedBorder(range2))
-                               {
-                                       return new CellRange[] { range1.createEnclosingCellRange(range2), };
-                               }
-                               // else - No intersection and no shared border: do nothing 
-                               return null;
-                       case CellRange.OVERLAP:
-                               return resolveRangeOverlap(range1, range2);
-                       case CellRange.INSIDE:
-                               // Remove range2, since it is completely inside of range1
-                               return new CellRange[] { range1, };
-                       case CellRange.ENCLOSES:
-                               // range2 encloses range1, so replace it with the enclosing one
-                               return new CellRange[] { range2, };
-               }
-               throw new RuntimeException("unexpected intersection result (" + x + ")");
-       }
-       
-       // TODO - write junit test for this
-       static CellRange[] resolveRangeOverlap(CellRange rangeA, CellRange rangeB) {
-               
-               if(rangeA.isFullColumnRange()) {
-                       if(rangeB.isFullRowRange()) {
-                               // Excel seems to leave these unresolved
-                               return null;
-                       }
-                       return rangeA.sliceUp(rangeB);
-               }
-               if(rangeA.isFullRowRange()) {
-                       if(rangeB.isFullColumnRange()) {
-                               // Excel seems to leave these unresolved
-                               return null;
-                       }
-                       return rangeA.sliceUp(rangeB);
-               }
-               if(rangeB.isFullColumnRange()) {
-                       return rangeB.sliceUp(rangeA);
-               }
-               if(rangeB.isFullRowRange()) {
-                       return rangeB.sliceUp(rangeA);
-               }
-               return rangeA.sliceUp(rangeB);
-       }
-
-       /**
-        * @param range never a full row or full column range
-        * @return an array including <b>this</b> <tt>CellRange</tt> and all parts of <tt>range</tt> 
-        * outside of this range  
-        */
-       private CellRange[] sliceUp(CellRange range) {
-               
-               List temp = new ArrayList();
-               
-               // Chop up range horizontally and vertically
-               temp.add(range);
-               if(!isFullColumnRange()) {
-                       temp = cutHorizontally(_firstRow, temp);
-                       temp = cutHorizontally(_lastRow+1, temp);
-               }
-               if(!isFullRowRange()) {
-                       temp = cutVertically(_firstColumn, temp);
-                       temp = cutVertically(_lastColumn+1, temp);
-               }
-               CellRange[] crParts = toArray(temp);
-
-               // form result array
-               temp.clear();
-               temp.add(this);
-               
-               for (int i = 0; i < crParts.length; i++) {
-                       CellRange crPart = crParts[i];
-                       // only include parts that are not enclosed by this
-                       if(intersect(crPart) != ENCLOSES) {
-                               temp.add(crPart);
-                       }
-               }
-               return toArray(temp);
-       }
-
-       private static List cutHorizontally(int cutRow, List input) {
-               
-               List result = new ArrayList();
-               CellRange[] crs = toArray(input);
-               for (int i = 0; i < crs.length; i++) {
-                       CellRange cr = crs[i];
-                       if(cr._firstRow < cutRow && cutRow < cr._lastRow) {
-                               result.add(new CellRange(cr._firstRow, cutRow, cr._firstColumn, cr._lastColumn));
-                               result.add(new CellRange(cutRow+1, cr._lastRow, cr._firstColumn, cr._lastColumn));
-                       } else {
-                               result.add(cr);
-                       }
-               }
-               return result;
-       }
-       private static List cutVertically(int cutColumn, List input) {
-               
-               List result = new ArrayList();
-               CellRange[] crs = toArray(input);
-               for (int i = 0; i < crs.length; i++) {
-                       CellRange cr = crs[i];
-                       if(cr._firstColumn < cutColumn && cutColumn < cr._lastColumn) {
-                               result.add(new CellRange(cr._firstRow, cr._lastRow, cr._firstColumn, cutColumn));
-                               result.add(new CellRange(cr._firstRow, cr._lastRow, cutColumn+1, cr._lastColumn));
-                       } else {
-                               result.add(cr);
-                       }
-               }
-               return result;
-       }
-
-
-       private static CellRange[] toArray(List temp) {
-               CellRange[] result = new CellRange[temp.size()];
-               temp.toArray(result);
-               return result;
-       }
-
-       /**
-        * Convert array of regions to a List of CellRange objects
-        *  
-        * @param regions
-        * @return List of CellRange objects
-        */
-       public static CellRange[] convertRegionsToCellRanges(org.apache.poi.ss.util.Region[] regions)
-       {
-               CellRange[] result = new CellRange[regions.length];
-               for( int i=0; i<regions.length; i++)
-               {
-                       result[i] = createFromRegion(regions[i]);
-               }
-               return result;
-       }
-       
-       /**
-        * Convert a List of CellRange objects to an array of regions 
-        *  
-        * @param List of CellRange objects
-        * @return regions
-        */
-       public static Region[] convertCellRangesToRegions(CellRange[] cellRanges)
-       {
-               int size = cellRanges.length;
-               if(size < 1) {
-                       return EMPTY_REGION_ARRAY;
-               }
-               
-               Region[] result = new Region[size];
-
-               for (int i = 0; i != size; i++)
-               {
-                       result[i] = cellRanges[i].convertToRegion();
-               }
-               return result;
-       }
-
-
-               
-       private Region convertToRegion() {
-               
-               return new Region(_firstRow, (short)_firstColumn, _lastRow, (short)_lastColumn);
-       }
-
-
-       /**
-        *  Check if the specified range is located inside of this cell range.
-        *  
-        * @param range
-        * @return true if this cell range contains the argument range inside if it's area
-        */
-   public boolean contains(CellRange range)
-   {
-               int firstRow = range.getFirstRow();
-               int lastRow = range.getLastRow();
-               int firstCol = range.getFirstColumn();
-               int lastCol = range.getLastColumn();
-               return le(getFirstRow(), firstRow) && ge(getLastRow(), lastRow)
-                               && le(getFirstColumn(), firstCol) && ge(getLastColumn(), lastCol);
-       }
-   
-       public boolean contains(int row, short column)
-       {
-               return le(getFirstRow(), row) && ge(getLastRow(), row)
-                               && le(getFirstColumn(), column) && ge(getLastColumn(), column);
-       }
-       
-   /**
-       * Check if the specified cell range has a shared border with the current range.
-       * 
-       * @return <code>true</code> if the ranges have a complete shared border (i.e.
-       * the two ranges together make a simple rectangular region.
-       */
-       public boolean hasExactSharedBorder(CellRange range)
-       {
-               int oFirstRow = range._firstRow;
-               int oLastRow  = range._lastRow;
-               int oFirstCol = range._firstColumn;
-               int oLastCol  = range._lastColumn;
-               
-               if (_firstRow > 0 && _firstRow-1 == oLastRow || 
-                       oFirstRow > 0 && oFirstRow-1 == _lastRow) {
-                       // ranges have a horizontal border in common
-                       // make sure columns are identical:
-                       return _firstColumn == oFirstCol && _lastColumn == oLastCol;
-               }
-
-               if (_firstColumn>0 && _firstColumn - 1 == oLastCol ||
-                       oFirstCol>0 && _lastColumn == oFirstCol -1) {
-                       // ranges have a vertical border in common
-                       // make sure rows are identical:
-                       return _firstRow == oFirstRow && _lastRow == oLastRow;
-               }
-               return false;
-       }
-       
-       /**
-        * Create an enclosing CellRange for the two cell ranges.
-        * 
-        * @return enclosing CellRange
-        */
-       public CellRange createEnclosingCellRange(CellRange range)
-       {
-               if( range == null)
-               {
-                       return cloneCellRange();
-               }
-               else
-               {
-                       CellRange cellRange = 
-                               new CellRange(
-                                       lt(range.getFirstRow(),getFirstRow())?range.getFirstRow():getFirstRow(),
-                                       gt(range.getLastRow(),getLastRow())?range.getLastRow():getLastRow(),
-                                       lt(range.getFirstColumn(),getFirstColumn())?range.getFirstColumn():getFirstColumn(),
-                                       gt(range.getLastColumn(),getLastColumn())?range.getLastColumn():getLastColumn()
-                               );
-                       return cellRange;
-               }
-       }
-       
-       public CellRange cloneCellRange()
-       {
-               return new CellRange(getFirstRow(),getLastRow(),getFirstColumn(),getLastColumn());
-       }
-
-       /**
-        * @return true if a < b
-        */
-       private static boolean lt(int a, int b)
-       {
-               return a == -1 ? false : (b == -1 ? true : a < b);
-       }
-       
-       /**
-        * @return true if a <= b
-        */
-       private static boolean le(int a, int b)
-       {
-               return a == b || lt(a,b);
-       }
-       
-       /**
-        * @return true if a > b
-        */
-       private static boolean gt(int a, int b)
-       {
-               return lt(b,a);
-       }
-
-       /**
-        * @return true if a >= b
-        */
-       private static boolean ge(int a, int b)
-       {
-               return !lt(a,b);
-       }
-       
-       public String toString()
-       {
-               return "("+getFirstRow()+","+getLastRow()+","+getFirstColumn()+","+getLastColumn()+")";
-       }
-       
-}
diff --git a/src/java/org/apache/poi/hssf/record/cf/CellRangeUtil.java b/src/java/org/apache/poi/hssf/record/cf/CellRangeUtil.java
new file mode 100644 (file)
index 0000000..019644c
--- /dev/null
@@ -0,0 +1,363 @@
+/* ====================================================================
+   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.record.cf;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+import org.apache.poi.ss.util.CellRangeAddress;
+
+/**
+ * 
+ * @author Dmitriy Kumshayev
+ */
+public final class CellRangeUtil
+{
+       
+       private CellRangeUtil() {
+               // no instance of this class
+       }
+       
+       public static final int NO_INTERSECTION = 1;
+       public static final int OVERLAP = 2;
+       /** first range is within the second range */
+       public static final int INSIDE = 3;
+       /** first range encloses or is equal to the second */
+       public static final int ENCLOSES = 4;
+       
+       /**
+        * Intersect this range with the specified range.
+        * 
+        * @param crB - the specified range
+        * @return code which reflects how the specified range is related to this range.<br/>
+        * Possible return codes are:   
+        *              NO_INTERSECTION - the specified range is outside of this range;<br/> 
+        *              OVERLAP - both ranges partially overlap;<br/>
+        *              INSIDE - the specified range is inside of this one<br/>
+        *              ENCLOSES - the specified range encloses (possibly exactly the same as) this range<br/>
+        */
+       public static int intersect(CellRangeAddress crA, CellRangeAddress crB )
+       {
+               
+               int firstRow = crB.getFirstRow();
+               int lastRow  = crB.getLastRow();
+               int firstCol = crB.getFirstColumn();
+               int lastCol  = crB.getLastColumn();
+               
+               if
+               ( 
+                               gt(crA.getFirstRow(),lastRow) || 
+                               lt(crA.getLastRow(),firstRow) ||
+                               gt(crA.getFirstColumn(),lastCol) || 
+                               lt(crA.getLastColumn(),firstCol) 
+               )
+               {
+                       return NO_INTERSECTION;
+               }
+               else if( contains(crA, crB) )
+               {
+                       return INSIDE;
+               }
+               else if( contains(crB, crA))
+               {
+                       return ENCLOSES;
+               }
+               else
+               {
+                       return OVERLAP;
+               }
+                       
+       }
+       
+       /**
+        * Do all possible cell merges between cells of the list so that:<br>
+        *      <li>if a cell range is completely inside of another cell range, it gets removed from the list 
+        *      <li>if two cells have a shared border, merge them into one bigger cell range
+        * @param cellRangeList
+        * @return updated List of cell ranges
+        */
+       public static CellRangeAddress[] mergeCellRanges(CellRangeAddress[] cellRanges) {
+               if(cellRanges.length < 1) {
+                       return cellRanges;
+               }
+               List temp = mergeCellRanges(Arrays.asList(cellRanges));
+               return toArray(temp);
+       }
+       private static List mergeCellRanges(List cellRangeList)
+       {
+
+               while(cellRangeList.size() > 1)
+               {
+                       boolean somethingGotMerged = false;
+                       
+                       for( int i=0; i<cellRangeList.size(); i++)
+                       {
+                               CellRangeAddress range1 = (CellRangeAddress)cellRangeList.get(i);
+                               for( int j=i+1; j<cellRangeList.size(); j++)
+                               {
+                                       CellRangeAddress range2 = (CellRangeAddress)cellRangeList.get(j);
+                                       
+                                       CellRangeAddress[] mergeResult = mergeRanges(range1, range2);
+                                       if(mergeResult == null) {
+                                               continue;
+                                       }
+                                       somethingGotMerged = true;
+                                       // overwrite range1 with first result 
+                                       cellRangeList.set(i, mergeResult[0]);
+                                       // remove range2
+                                       cellRangeList.remove(j--);
+                                       // add any extra results beyond the first
+                                       for(int k=1; k<mergeResult.length; k++) {
+                                               j++;
+                                               cellRangeList.add(j, mergeResult[k]);
+                                       }
+                               }
+                       }
+                       if(!somethingGotMerged) {
+                               break;
+                       }
+               }
+               
+
+               return cellRangeList;
+       }
+       
+       /**
+        * @return the new range(s) to replace the supplied ones.  <code>null</code> if no merge is possible
+        */
+       private static CellRangeAddress[] mergeRanges(CellRangeAddress range1, CellRangeAddress range2) {
+               
+               int x = intersect(range1, range2);
+               switch(x)
+               {
+                       case CellRangeUtil.NO_INTERSECTION: 
+                               if(hasExactSharedBorder(range1, range2)) {
+                                       return new CellRangeAddress[] { createEnclosingCellRange(range1, range2), };
+                               }
+                               // else - No intersection and no shared border: do nothing 
+                               return null;
+                       case CellRangeUtil.OVERLAP:
+                               return resolveRangeOverlap(range1, range2);
+                       case CellRangeUtil.INSIDE:
+                               // Remove range2, since it is completely inside of range1
+                               return new CellRangeAddress[] { range1, };
+                       case CellRangeUtil.ENCLOSES:
+                               // range2 encloses range1, so replace it with the enclosing one
+                               return new CellRangeAddress[] { range2, };
+               }
+               throw new RuntimeException("unexpected intersection result (" + x + ")");
+       }
+       
+       // TODO - write junit test for this
+       static CellRangeAddress[] resolveRangeOverlap(CellRangeAddress rangeA, CellRangeAddress rangeB) {
+               
+               if(rangeA.isFullColumnRange()) {
+                       if(rangeA.isFullRowRange()) {
+                               // Excel seems to leave these unresolved
+                               return null;
+                       }
+                       return sliceUp(rangeA, rangeB);
+               }
+               if(rangeA.isFullRowRange()) {
+                       if(rangeB.isFullColumnRange()) {
+                               // Excel seems to leave these unresolved
+                               return null;
+                       }
+                       return sliceUp(rangeA, rangeB);
+               }
+               if(rangeB.isFullColumnRange()) {
+                       return sliceUp(rangeB, rangeA);
+               }
+               if(rangeB.isFullRowRange()) {
+                       return sliceUp(rangeB, rangeA);
+               }
+               return sliceUp(rangeA, rangeB);
+       }
+
+       /**
+        * @param crB never a full row or full column range
+        * @return an array including <b>this</b> <tt>CellRange</tt> and all parts of <tt>range</tt> 
+        * outside of this range  
+        */
+       private static CellRangeAddress[] sliceUp(CellRangeAddress crA, CellRangeAddress crB) {
+               
+               List temp = new ArrayList();
+               
+               // Chop up range horizontally and vertically
+               temp.add(crB);
+               if(!crA.isFullColumnRange()) {
+                       temp = cutHorizontally(crA.getFirstRow(), temp);
+                       temp = cutHorizontally(crA.getLastRow()+1, temp);
+               }
+               if(!crA.isFullRowRange()) {
+                       temp = cutVertically(crA.getFirstColumn(), temp);
+                       temp = cutVertically(crA.getLastColumn()+1, temp);
+               }
+               CellRangeAddress[] crParts = toArray(temp);
+
+               // form result array
+               temp.clear();
+               temp.add(crA);
+               
+               for (int i = 0; i < crParts.length; i++) {
+                       CellRangeAddress crPart = crParts[i];
+                       // only include parts that are not enclosed by this
+                       if(intersect(crA, crPart) != ENCLOSES) {
+                               temp.add(crPart);
+                       }
+               }
+               return toArray(temp);
+       }
+
+       private static List cutHorizontally(int cutRow, List input) {
+               
+               List result = new ArrayList();
+               CellRangeAddress[] crs = toArray(input);
+               for (int i = 0; i < crs.length; i++) {
+                       CellRangeAddress cr = crs[i];
+                       if(cr.getFirstRow() < cutRow && cutRow < cr.getLastRow()) {
+                               result.add(new CellRangeAddress(cr.getFirstRow(), cutRow, cr.getFirstColumn(), cr.getLastColumn()));
+                               result.add(new CellRangeAddress(cutRow+1, cr.getLastRow(), cr.getFirstColumn(), cr.getLastColumn()));
+                       } else {
+                               result.add(cr);
+                       }
+               }
+               return result;
+       }
+       private static List cutVertically(int cutColumn, List input) {
+               
+               List result = new ArrayList();
+               CellRangeAddress[] crs = toArray(input);
+               for (int i = 0; i < crs.length; i++) {
+                       CellRangeAddress cr = crs[i];
+                       if(cr.getFirstColumn() < cutColumn && cutColumn < cr.getLastColumn()) {
+                               result.add(new CellRangeAddress(cr.getFirstRow(), cr.getLastRow(), cr.getFirstColumn(), cutColumn));
+                               result.add(new CellRangeAddress(cr.getFirstRow(), cr.getLastRow(), cutColumn+1, cr.getLastColumn()));
+                       } else {
+                               result.add(cr);
+                       }
+               }
+               return result;
+       }
+
+
+       private static CellRangeAddress[] toArray(List temp) {
+               CellRangeAddress[] result = new CellRangeAddress[temp.size()];
+               temp.toArray(result);
+               return result;
+       }
+
+
+
+       /**
+        *  Check if the specified range is located inside of this cell range.
+        *  
+        * @param crB
+        * @return true if this cell range contains the argument range inside if it's area
+        */
+   public static boolean contains(CellRangeAddress crA, CellRangeAddress crB)
+   {
+               int firstRow = crB.getFirstRow();
+               int lastRow = crB.getLastRow();
+               int firstCol = crB.getFirstColumn();
+               int lastCol = crB.getLastColumn();
+               return le(crA.getFirstRow(), firstRow) && ge(crA.getLastRow(), lastRow)
+                               && le(crA.getFirstColumn(), firstCol) && ge(crA.getLastColumn(), lastCol);
+       }
+       
+   /**
+       * Check if the specified cell range has a shared border with the current range.
+       * 
+       * @return <code>true</code> if the ranges have a complete shared border (i.e.
+       * the two ranges together make a simple rectangular region.
+       */
+       public static boolean hasExactSharedBorder(CellRangeAddress crA, CellRangeAddress crB) {
+               int oFirstRow = crB.getFirstRow();
+               int oLastRow  = crB.getLastRow();
+               int oFirstCol = crB.getFirstColumn();
+               int oLastCol  = crB.getLastColumn();
+               
+               if (crA.getFirstRow() > 0 && crA.getFirstRow()-1 == oLastRow || 
+                       oFirstRow > 0 && oFirstRow-1 == crA.getLastRow()) {
+                       // ranges have a horizontal border in common
+                       // make sure columns are identical:
+                       return crA.getFirstColumn() == oFirstCol && crA.getLastColumn() == oLastCol;
+               }
+
+               if (crA.getFirstColumn()>0 && crA.getFirstColumn() - 1 == oLastCol ||
+                       oFirstCol>0 && crA.getLastColumn() == oFirstCol -1) {
+                       // ranges have a vertical border in common
+                       // make sure rows are identical:
+                       return crA.getFirstRow() == oFirstRow && crA.getLastRow() == oLastRow;
+               }
+               return false;
+       }
+       
+       /**
+        * Create an enclosing CellRange for the two cell ranges.
+        * 
+        * @return enclosing CellRange
+        */
+       public static CellRangeAddress createEnclosingCellRange(CellRangeAddress crA, CellRangeAddress crB) {
+               if( crB == null) {
+                       return crA.copy();
+               }
+               
+               return
+                       new CellRangeAddress(
+                               lt(crB.getFirstRow(),   crA.getFirstRow())   ?crB.getFirstRow()   :crA.getFirstRow(),
+                               gt(crB.getLastRow(),    crA.getLastRow())    ?crB.getLastRow()    :crA.getLastRow(),
+                               lt(crB.getFirstColumn(),crA.getFirstColumn())?crB.getFirstColumn():crA.getFirstColumn(),
+                               gt(crB.getLastColumn(), crA.getLastColumn()) ?crB.getLastColumn() :crA.getLastColumn()
+                       );
+               
+       }
+       
+       /**
+        * @return true if a < b
+        */
+       private static boolean lt(int a, int b)
+       {
+               return a == -1 ? false : (b == -1 ? true : a < b);
+       }
+       
+       /**
+        * @return true if a <= b
+        */
+       private static boolean le(int a, int b)
+       {
+               return a == b || lt(a,b);
+       }
+       
+       /**
+        * @return true if a > b
+        */
+       private static boolean gt(int a, int b)
+       {
+               return lt(b,a);
+       }
+
+       /**
+        * @return true if a >= b
+        */
+       private static boolean ge(int a, int b)
+       {
+               return !lt(a,b);
+       }
+}
index dc28e705ad903c8fea5bc0f7d0eb64b134d83dfb..f02275860c4be929ac75f44c7792aac571efa238 100644 (file)
@@ -36,7 +36,7 @@ public final class NumberPtg extends ScalarConstantPtg {
     /** Create a NumberPtg from a byte array read from disk */
     public NumberPtg(RecordInputStream in)
     {
-        field_1_value = in.readDouble();
+        this(in.readDouble());
     }
     
     /** Create a NumberPtg from a string representation of  the number
@@ -45,9 +45,12 @@ public final class NumberPtg extends ScalarConstantPtg {
      *  @param value : String representation of a floating point number
      */
     public NumberPtg(String value) {
-        field_1_value = Double.parseDouble(value);
+        this(Double.parseDouble(value));
     }
     
+    public NumberPtg(double value) {
+        field_1_value = value;
+    }
     
     public double getValue()
     {
@@ -67,6 +70,15 @@ public final class NumberPtg extends ScalarConstantPtg {
 
     public String toFormulaString(Workbook book)
     {
-        return "" + getValue();
+        // TODO - java's rendering of double values is not quite same as excel's
+        return String.valueOf(field_1_value);
+    }
+    
+    public String toString() {
+        StringBuffer sb = new StringBuffer(64);
+        sb.append(getClass().getName()).append(" [");
+        sb.append(field_1_value);
+        sb.append("]");
+        return sb.toString();
     }
 }
index 69464f8f43532d3102c43dd6036cabbb56edc31c..50f3450d281647f9b0ea3475fc4d11ab63d66320 100644 (file)
@@ -41,6 +41,7 @@ import org.apache.poi.ss.usermodel.Workbook;
  * @author Jason Height (jheight at chariot dot net dot au)
  */
 public abstract class Ptg implements Cloneable {
+       public static final Ptg[] EMPTY_PTG_ARRAY = { }; 
 
        /* convert infix order ptg list to rpn order ptg list
         * @return List ptgs in RPN order
@@ -250,6 +251,9 @@ public abstract class Ptg implements Cloneable {
                }
        }
        private static Ptg[] toPtgArray(List l) {
+               if (l.isEmpty()) {
+                       return EMPTY_PTG_ARRAY;
+               }
                Ptg[] result = new Ptg[l.size()];
                l.toArray(result);
                return result;
diff --git a/src/java/org/apache/poi/hssf/usermodel/DVConstraint.java b/src/java/org/apache/poi/hssf/usermodel/DVConstraint.java
new file mode 100644 (file)
index 0000000..a1027cc
--- /dev/null
@@ -0,0 +1,479 @@
+/* ====================================================================
+   Copyright 2002-2004   Apache Software Foundation
+
+   Licensed 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 java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+
+import org.apache.poi.hssf.model.FormulaParser;
+import org.apache.poi.hssf.record.formula.NumberPtg;
+import org.apache.poi.hssf.record.formula.Ptg;
+import org.apache.poi.hssf.record.formula.StringPtg;
+
+/**
+ * 
+ * @author Josh Micich
+ */
+public class DVConstraint {
+       /**
+        * ValidationType enum
+        */
+       public static final class ValidationType {
+               private ValidationType() {
+                       // no instances of this class
+               }
+               /** 'Any value' type - value not restricted */
+               public static final int ANY         = 0x00;
+               /** Integer ('Whole number') type */
+               public static final int INTEGER     = 0x01;
+               /** Decimal type */
+               public static final int DECIMAL     = 0x02;
+               /** List type ( combo box type ) */
+               public static final int LIST        = 0x03;
+               /** Date type */
+               public static final int DATE        = 0x04;
+               /** Time type */
+               public static final int TIME        = 0x05;
+               /** String length type */
+               public static final int TEXT_LENGTH = 0x06;
+               /** Formula ( 'Custom' ) type */
+               public static final int FORMULA     = 0x07;
+       }
+       /**
+        * Condition operator enum
+        */
+       public static final class OperatorType {
+               private OperatorType() {
+                       // no instances of this class
+               }
+
+               public static final int BETWEEN = 0x00;
+               public static final int NOT_BETWEEN = 0x01;
+               public static final int EQUAL = 0x02;
+               public static final int NOT_EQUAL = 0x03;
+               public static final int GREATER_THAN = 0x04;
+               public static final int LESS_THAN = 0x05;
+               public static final int GREATER_OR_EQUAL = 0x06;
+               public static final int LESS_OR_EQUAL = 0x07;
+               /** default value to supply when the operator type is not used */
+               public static final int IGNORED = BETWEEN;
+               
+               /* package */ static void validateSecondArg(int comparisonOperator, String paramValue) {
+                       switch (comparisonOperator) {
+                               case BETWEEN:
+                               case NOT_BETWEEN:
+                                       if (paramValue == null) {
+                                               throw new IllegalArgumentException("expr2 must be supplied for 'between' comparisons");
+                                       }
+                               // all other operators don't need second arg
+                       }
+               }
+       }
+       
+       /* package */ static final class FormulaPair {
+
+               private final Ptg[] _formula1;
+               private final Ptg[] _formula2;
+
+               public FormulaPair(Ptg[] formula1, Ptg[] formula2) {
+                       _formula1 = formula1;
+                       _formula2 = formula2;
+               }
+               public Ptg[] getFormula1() {
+                       return _formula1;
+               }
+               public Ptg[] getFormula2() {
+                       return _formula2;
+               }
+               
+       }
+       
+       // convenient access to ValidationType namespace
+       private static final ValidationType VT = null;
+
+       
+       private final int _validationType;
+       private int _operator;
+       private String[] _explicitListValues;
+       
+       private String _formula1;
+       private String _formula2;
+       private Double _value1;
+       private Double _value2;
+
+       
+       private DVConstraint(int validationType, int comparisonOperator, String formulaA,
+                       String formulaB, Double value1, Double value2, String[] excplicitListValues) {
+               _validationType = validationType;
+               _operator = comparisonOperator;
+               _formula1 = formulaA;
+               _formula2 = formulaB;
+               _value1 = value1;
+               _value2 = value2;
+               _explicitListValues = excplicitListValues;
+       }
+       
+       
+       /**
+        * Creates a list constraint
+        */
+       private DVConstraint(String listFormula, String[] excplicitListValues) {
+               this(ValidationType.LIST, OperatorType.IGNORED,
+                       listFormula, null, null, null, excplicitListValues);
+       }
+
+       /**
+        * Creates a number based data validation constraint. The text values entered for expr1 and expr2
+        * can be either standard Excel formulas or formatted number values. If the expression starts 
+        * with '=' it is parsed as a formula, otherwise it is parsed as a formatted number. 
+        * 
+        * @param validationType one of {@link ValidationType#ANY}, {@link ValidationType#DECIMAL},
+        * {@link ValidationType#INTEGER}, {@link ValidationType#TEXT_LENGTH}
+        * @param comparisonOperator any constant from {@link OperatorType} enum
+        * @param expr1 date formula (when first char is '=') or formatted number value
+        * @param expr2 date formula (when first char is '=') or formatted number value
+        */
+       public static DVConstraint createNumericConstraint(int validationType, int comparisonOperator, 
+                       String expr1, String expr2) {
+               switch (validationType) {
+                       case ValidationType.ANY:
+                               if (expr1 != null || expr2 != null) {
+                                       throw new IllegalArgumentException("expr1 and expr2 must be null for validation type 'any'");
+                               }
+                               break;
+                       case ValidationType.DECIMAL:
+                       case ValidationType.INTEGER:
+                       case ValidationType.TEXT_LENGTH:
+                               if (expr1 == null) {
+                                       throw new IllegalArgumentException("expr1 must be supplied");
+                               }
+                               OperatorType.validateSecondArg(comparisonOperator, expr2);
+                               break;
+                       default:
+                               throw new IllegalArgumentException("Validation Type ("
+                                               + validationType + ") not supported with this method");
+               }
+               // formula1 and value1 are mutually exclusive
+               String formula1 = getFormulaFromTextExpression(expr1);
+               Double value1 = formula1 == null ? convertNumber(expr1) : null;
+               // formula2 and value2 are mutually exclusive
+               String formula2 = getFormulaFromTextExpression(expr2);
+               Double value2 = formula2 == null ? convertNumber(expr2) : null;
+               return new DVConstraint(validationType, comparisonOperator, formula1, formula2, value1, value2, null);
+       }
+
+       public static DVConstraint createFormulaListConstraint(String listFormula) {
+               return new DVConstraint(listFormula, null);
+       }
+       public static DVConstraint createExplicitListConstraint(String[] explicitListValues) {
+               return new DVConstraint(null, explicitListValues);
+       }
+       
+       
+       /**
+        * Creates a time based data validation constraint. The text values entered for expr1 and expr2
+        * can be either standard Excel formulas or formatted time values. If the expression starts 
+        * with '=' it is parsed as a formula, otherwise it is parsed as a formatted time.  To parse 
+        * formatted times, two formats are supported:  "HH:MM" or "HH:MM:SS".  This is contrary to 
+        * Excel which uses the default time format from the OS.
+        * 
+        * @param comparisonOperator constant from {@link OperatorType} enum
+        * @param expr1 date formula (when first char is '=') or formatted time value
+        * @param expr2 date formula (when first char is '=') or formatted time value
+        */
+       public static DVConstraint createTimeConstraint(int comparisonOperator, String expr1, String expr2) {
+               if (expr1 == null) {
+                       throw new IllegalArgumentException("expr1 must be supplied");
+               }
+               OperatorType.validateSecondArg(comparisonOperator, expr1);
+               
+               // formula1 and value1 are mutually exclusive
+               String formula1 = getFormulaFromTextExpression(expr1);
+               Double value1 = formula1 == null ? convertTime(expr1) : null;
+               // formula2 and value2 are mutually exclusive
+               String formula2 = getFormulaFromTextExpression(expr2);
+               Double value2 = formula2 == null ? convertTime(expr2) : null;
+               return new DVConstraint(VT.TIME, comparisonOperator, formula1, formula2, value1, value2, null);
+               
+       }
+       /**
+        * Creates a date based data validation constraint. The text values entered for expr1 and expr2
+        * can be either standard Excel formulas or formatted date values. If the expression starts 
+        * with '=' it is parsed as a formula, otherwise it is parsed as a formatted date (Excel uses 
+        * the same convention).  To parse formatted dates, a date format needs to be specified.  This
+        * is contrary to Excel which uses the default short date format from the OS.
+        * 
+        * @param comparisonOperator constant from {@link OperatorType} enum
+        * @param expr1 date formula (when first char is '=') or formatted date value
+        * @param expr2 date formula (when first char is '=') or formatted date value
+        * @param dateFormat ignored if both expr1 and expr2 are formulas.  Default value is "YYYY/MM/DD"
+        * otherwise any other valid argument for <tt>SimpleDateFormat</tt> can be used
+        * @see <a href='http://java.sun.com/j2se/1.5.0/docs/api/java/text/DateFormat.html'>SimpleDateFormat</a>
+        */
+       public static DVConstraint createDateConstraint(int comparisonOperator, String expr1, String expr2, String dateFormat) {
+               if (expr1 == null) {
+                       throw new IllegalArgumentException("expr1 must be supplied");
+               }
+               OperatorType.validateSecondArg(comparisonOperator, expr2);
+               SimpleDateFormat df = dateFormat == null ? null : new SimpleDateFormat(dateFormat);
+               
+               // formula1 and value1 are mutually exclusive
+               String formula1 = getFormulaFromTextExpression(expr1);
+               Double value1 = formula1 == null ? convertDate(expr1, df) : null;
+               // formula2 and value2 are mutually exclusive
+               String formula2 = getFormulaFromTextExpression(expr2);
+               Double value2 = formula2 == null ? convertDate(expr2, df) : null;
+               return new DVConstraint(VT.DATE, comparisonOperator, formula1, formula2, value1, value2, null);
+       }
+       
+       /**
+        * Distinguishes formula expressions from simple value expressions.  This logic is only 
+        * required by a few factory methods in this class that create data validation constraints
+        * from more or less the same parameters that would have been entered in the Excel UI.  The
+        * data validation dialog box uses the convention that formulas begin with '='.  Other methods
+        * in this class follow the POI convention (formulas and values are distinct), so the '=' 
+        * convention is not used there.
+        *  
+        * @param textExpr a formula or value expression
+        * @return all text after '=' if textExpr begins with '='. Otherwise <code>null</code> if textExpr does not begin with '='
+        */
+       private static String getFormulaFromTextExpression(String textExpr) {
+               if (textExpr == null) {
+                       return null;
+               }
+               if (textExpr.length() < 1) {
+                       throw new IllegalArgumentException("Empty string is not a valid formula/value expression");
+               }
+               if (textExpr.charAt(0) == '=') {
+                       return textExpr.substring(1);
+               }
+               return null;
+       }
+
+
+       /**
+        * @return <code>null</code> if numberStr is <code>null</code>
+        */
+       private static Double convertNumber(String numberStr) {
+               if (numberStr == null) {
+                       return null;
+               }
+               try {
+                       return new Double(numberStr);
+               } catch (NumberFormatException e) {
+                       throw new RuntimeException("The supplied text '" + numberStr 
+                                       + "' could not be parsed as a number");
+               }
+       }
+
+       /**
+        * @return <code>null</code> if timeStr is <code>null</code>
+        */
+       private static Double convertTime(String timeStr) {
+               if (timeStr == null) {
+                       return null;
+               }
+               return new Double(HSSFDateUtil.convertTime(timeStr));
+       }
+       /**
+        * @param dateFormat pass <code>null</code> for default YYYYMMDD
+        * @return <code>null</code> if timeStr is <code>null</code>
+        */
+       private static Double convertDate(String dateStr, SimpleDateFormat dateFormat) {
+               if (dateStr == null) {
+                       return null;
+               }
+               Date dateVal; 
+               if (dateFormat == null) {
+                       dateVal = HSSFDateUtil.parseYYYYMMDDDate(dateStr);
+               } else {
+                       try {
+                               dateVal = dateFormat.parse(dateStr);
+                       } catch (ParseException e) {
+                               throw new RuntimeException("Failed to parse date '" + dateStr 
+                                               + "' using specified format '" + dateFormat + "'", e);
+                       }
+               }
+               return new Double(HSSFDateUtil.getExcelDate(dateVal));
+       }
+
+       public static DVConstraint createCustomFormulaConstraint(String formula) {
+               if (formula == null) {
+                       throw new IllegalArgumentException("formula must be supplied");
+               }
+               return new DVConstraint(VT.FORMULA, OperatorType.IGNORED, formula, null, null, null, null);
+       }
+       
+       /**
+        * @return both parsed formulas (for expression 1 and 2). 
+        */
+       /* package */ FormulaPair createFormulas(HSSFWorkbook workbook) {
+               Ptg[] formula1;
+               Ptg[] formula2;
+               if (isListValidationType()) {
+                       formula1 = createListFormula(workbook);
+                       formula2 = Ptg.EMPTY_PTG_ARRAY;
+               } else {
+                       formula1 = convertDoubleFormula(_formula1, _value1, workbook);
+                       formula2 = convertDoubleFormula(_formula2, _value2, workbook);
+               }
+               return new FormulaPair(formula1, formula2);
+       }
+
+       private Ptg[] createListFormula(HSSFWorkbook workbook) {
+
+               if (_explicitListValues == null) {
+                       // formula is parsed with slightly different RVA rules: (root node type must be 'reference')
+                       return FormulaParser.parse(_formula1, workbook, FormulaParser.FORMULA_TYPE_DATAVALIDATION_LIST);
+                       // To do: Excel places restrictions on the available operations within a list formula.
+                       // Some things like union and intersection are not allowed.
+               }
+               // explicit list was provided
+               StringBuffer sb = new StringBuffer(_explicitListValues.length * 16);
+               for (int i = 0; i < _explicitListValues.length; i++) {
+                       if (i > 0) {
+                               sb.append('\0'); // list delimiter is the nul char
+                       }
+                       sb.append(_explicitListValues[i]);
+               
+               }
+               return new Ptg[] { new StringPtg(sb.toString()), };
+       }
+
+       /**
+        * @return The parsed token array representing the formula or value specified. 
+        * Empty array if both formula and value are <code>null</code>
+        */
+       private static Ptg[] convertDoubleFormula(String formula, Double value, HSSFWorkbook workbook) {
+               if (formula == null) {
+                       if (value == null) {
+                               return Ptg.EMPTY_PTG_ARRAY;
+                       }
+                       return new Ptg[] { new NumberPtg(value.doubleValue()), };
+               }
+               if (value != null) {
+                       throw new IllegalStateException("Both formula and value cannot be present");
+               }
+               return FormulaParser.parse(formula, workbook);
+       }
+       
+       
+       /**
+        * @return data validation type of this constraint
+        * @see ValidationType
+        */
+       public int getValidationType() {
+               return _validationType;
+       }
+       /**
+        * Convenience method
+        * @return <code>true</code> if this constraint is a 'list' validation
+        */
+       public boolean isListValidationType() {
+               return _validationType == VT.LIST;
+       }
+       /**
+        * Convenience method
+        * @return <code>true</code> if this constraint is a 'list' validation with explicit values
+        */
+       public boolean isExplicitList() {
+               return _validationType == VT.LIST && _explicitListValues != null;
+       }
+       /**
+        * @return the operator used for this constraint
+        * @see OperatorType
+        */
+       public int getOperator() {
+               return _operator;
+       }
+       /**
+        * Sets the comparison operator for this constraint
+        * @see OperatorType
+        */
+       public void setOperator(int operator) {
+               _operator = operator;
+       }
+       
+       public String[] getExplicitListValues() {
+               return _explicitListValues;
+       }
+       public void setExplicitListValues(String[] explicitListValues) {
+               if (_validationType != VT.LIST) {
+                       throw new RuntimeException("Cannot setExplicitListValues on non-list constraint");
+               }
+               _formula1 = null;
+               _explicitListValues = explicitListValues;
+       }
+
+       /**
+        * @return the formula for expression 1. May be <code>null</code>
+        */
+       public String getFormula1() {
+               return _formula1;
+       }
+       /**
+        * Sets a formula for expression 1.
+        */
+       public void setFormula1(String formula1) {
+               _value1 = null;
+               _explicitListValues = null;
+               _formula1 = formula1;
+       }
+
+       /**
+        * @return the formula for expression 2. May be <code>null</code>
+        */
+       public String getFormula2() {
+               return _formula2;
+       }
+       /**
+        * Sets a formula for expression 2.
+        */
+       public void setFormula2(String formula2) {
+               _value2 = null;
+               _formula2 = formula2;
+       }
+
+       /**
+        * @return the numeric value for expression 1. May be <code>null</code>
+        */
+       public Double getValue1() {
+               return _value1;
+       }
+       /**
+        * Sets a numeric value for expression 1.
+        */
+       public void setValue1(double value1) {
+               _formula1 = null;
+               _value1 = new Double(value1);
+       }
+
+       /**
+        * @return the numeric value for expression 2. May be <code>null</code>
+        */
+       public Double getValue2() {
+               return _value2;
+       }
+       /**
+        * Sets a numeric value for expression 2.
+        */
+       public void setValue2(double value2) {
+               _formula2 = null;
+               _value2 = new Double(value2);
+       }
+}
index 3283f98dde8d9711faf3d1d68f5e3a5e143b37e7..f110d71f9341997ae412fc962cedb551fe40f399 100644 (file)
@@ -34,7 +34,24 @@ import java.util.Iterator;
 import org.apache.poi.hssf.model.FormulaParser;
 import org.apache.poi.hssf.model.Sheet;
 import org.apache.poi.hssf.model.Workbook;
-import org.apache.poi.hssf.record.*;
+import org.apache.poi.hssf.record.BlankRecord;
+import org.apache.poi.hssf.record.BoolErrRecord;
+import org.apache.poi.hssf.record.CellValueRecordInterface;
+import org.apache.poi.hssf.record.CommonObjectDataSubRecord;
+import org.apache.poi.hssf.record.DrawingRecord;
+import org.apache.poi.hssf.record.EOFRecord;
+import org.apache.poi.hssf.record.ExtendedFormatRecord;
+import org.apache.poi.hssf.record.FormulaRecord;
+import org.apache.poi.hssf.record.HyperlinkRecord;
+import org.apache.poi.hssf.record.LabelSSTRecord;
+import org.apache.poi.hssf.record.NoteRecord;
+import org.apache.poi.hssf.record.NumberRecord;
+import org.apache.poi.hssf.record.ObjRecord;
+import org.apache.poi.hssf.record.Record;
+import org.apache.poi.hssf.record.StringRecord;
+import org.apache.poi.hssf.record.SubRecord;
+import org.apache.poi.hssf.record.TextObjectRecord;
+import org.apache.poi.hssf.record.UnicodeString;
 import org.apache.poi.hssf.record.aggregates.FormulaRecordAggregate;
 import org.apache.poi.hssf.record.formula.Ptg;
 import org.apache.poi.ss.usermodel.Cell;
@@ -1055,11 +1072,18 @@ public class HSSFCell implements Cell
     }
 
     /**
-     * Assign a comment to this cell
+     * Assign a comment to this cell. If the supplied
+     *  comment is null, the comment for this cell
+     *  will be removed.
      *
      * @param comment comment associated with this cell
      */
     public void setCellComment(Comment comment){
+       if(comment == null) {
+               removeCellComment();
+               return;
+       }
+
         this.comment = (HSSFComment) comment;
         this.comment.setRow((short)record.getRow());
         this.comment.setColumn(record.getColumn());
@@ -1076,6 +1100,49 @@ public class HSSFCell implements Cell
         }
         return comment;
     }
+     
+    /**
+     * Removes the comment for this cell, if
+     *  there is one.
+     * WARNING - some versions of excel will loose
+     *  all comments after performing this action!
+     */
+    public void removeCellComment() {
+       HSSFComment comment = findCellComment(sheet, record.getRow(), record.getColumn());
+               this.comment = null;
+       
+       if(comment == null) {
+               // Nothing to do
+               return;
+       }
+       
+       // Zap the underlying NoteRecord
+       sheet.getRecords().remove(comment.getNoteRecord());
+       
+       // If we have a TextObjectRecord, is should
+       //  be proceeed by:
+       // MSODRAWING with container
+       // OBJ
+       // MSODRAWING with EscherTextboxRecord
+       if(comment.getTextObjectRecord() != null) {
+               TextObjectRecord txo = comment.getTextObjectRecord();
+               int txoAt = sheet.getRecords().indexOf(txo);
+               
+               if(sheet.getRecords().get(txoAt-3) instanceof DrawingRecord &&
+                       sheet.getRecords().get(txoAt-2) instanceof ObjRecord &&
+                       sheet.getRecords().get(txoAt-1) instanceof DrawingRecord) {
+                       // Zap these, in reverse order
+                       sheet.getRecords().remove(txoAt-1);
+                       sheet.getRecords().remove(txoAt-2);
+                       sheet.getRecords().remove(txoAt-3);
+               } else {
+                       throw new IllegalStateException("Found the wrong records before the TextObjectRecord, can't remove comment");
+               }
+               
+               // Now remove the text record
+               sheet.getRecords().remove(txo);
+       }
+    }
 
     /**
      * Cell comment finder.
index 85eca2d47722ebcd4eb77b2e14a6f80744096e94..0c8a16e4202c3cfd53a529cadc3d8e8ea18491f9 100644 (file)
@@ -156,4 +156,13 @@ public class HSSFComment extends HSSFTextbox implements Comment {
         }
         super.setString(string);
     }
+    
+    /**
+     * Returns the underlying Note record
+     */
+    protected NoteRecord getNoteRecord() { return note; }
+    /**
+     * Returns the underlying Text record
+     */
+    protected TextObjectRecord getTextObjectRecord() { return txo; }
 }
index 6c798abcf4f93e1314fcc83e2a0dce26459a17ad..3029515f4c6bc9e239f348246bb1e9b712ea4012 100644 (file)
 ==================================================================== */
 package org.apache.poi.hssf.usermodel;
 
-import org.apache.poi.hssf.record.CFHeaderRecord;
 import org.apache.poi.hssf.record.CFRuleRecord;
 import org.apache.poi.hssf.record.aggregates.CFRecordsAggregate;
-import org.apache.poi.hssf.record.cf.CellRange;
-import org.apache.poi.hssf.util.Region;
+import org.apache.poi.ss.util.CellRangeAddress;
+import org.apache.poi.ss.util.Region;
 
 /**
  * HSSFConditionalFormatting class encapsulates all settings of Conditional Formatting. 
@@ -96,13 +95,18 @@ public final class HSSFConditionalFormatting
        }
 
        /**
-        * @return array of <tt>Region</tt>s. never <code>null</code> 
+        * @deprecated (Aug-2008) use {@link HSSFConditionalFormatting#getFormattingRanges()}
         */
        public Region[] getFormattingRegions()
        {
-               CFHeaderRecord cfh = cfAggregate.getHeader();
-               CellRange[] cellRanges = cfh.getCellRanges();
-               return CellRange.convertCellRangesToRegions(cellRanges);
+               CellRangeAddress[] cellRanges = getFormattingRanges();
+               return Region.convertCellRangesToRegions(cellRanges);
+       }
+       /**
+        * @return array of <tt>CellRangeAddress</tt>s. never <code>null</code> 
+        */
+       public CellRangeAddress[] getFormattingRanges() {
+               return cfAggregate.getHeader().getCellRanges();
        }
 
        /**
diff --git a/src/java/org/apache/poi/hssf/usermodel/HSSFDataValidation.java b/src/java/org/apache/poi/hssf/usermodel/HSSFDataValidation.java
new file mode 100644 (file)
index 0000000..0591158
--- /dev/null
@@ -0,0 +1,235 @@
+/* ====================================================================
+   Copyright 2002-2004   Apache Software Foundation
+
+   Licensed 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 org.apache.poi.hssf.record.DVRecord;
+import org.apache.poi.hssf.usermodel.DVConstraint.FormulaPair;
+import org.apache.poi.ss.util.CellRangeAddressList;
+
+/**
+ *Utility class for creating data validation cells
+ * 
+ * @author Dragos Buleandra (dragos.buleandra@trade2b.ro)
+ */
+public final class HSSFDataValidation {
+       /**
+        * Error style constants for error box
+        */
+       public static final class ErrorStyle {
+       /** STOP style */
+       public static final int STOP    = 0x00;
+       /** WARNING style */
+       public static final int WARNING = 0x01;
+       /** INFO style */
+       public static final int INFO    = 0x02;
+       }    
+
+       private String _prompt_title;
+       private String _prompt_text;
+       private String _error_title;
+       private String _error_text;
+
+       private int _errorStyle = ErrorStyle.STOP;
+       private boolean _emptyCellAllowed = true;
+       private boolean _suppress_dropdown_arrow = false;
+       private boolean _showPromptBox = true;
+       private boolean _showErrorBox = true;
+       private final CellRangeAddressList _regions;
+       private DVConstraint _constraint;
+
+       /**
+        * Constructor which initializes the cell range on which this object will be
+        * applied
+        * @param constraint 
+        */
+       public HSSFDataValidation(CellRangeAddressList regions, DVConstraint constraint) {
+               _regions = regions;
+               _constraint = constraint;
+       }
+
+
+       public DVConstraint getConstraint() {
+               return _constraint;
+       }
+
+       /**
+        * Sets the error style for error box
+        * @see ErrorStyle
+        */
+       public void setErrorStyle(int error_style) {
+               _errorStyle = error_style;
+       }
+
+       /**
+        * @return the error style of error box
+        * @see ErrorStyle
+        */
+       public int getErrorStyle() {
+               return _errorStyle;
+       }
+
+       /**
+        * Sets if this object allows empty as a valid value
+        * 
+        * @param allowed <code>true</code> if this object should treats empty as valid value , <code>false</code>
+        *            otherwise
+        */
+       public void setEmptyCellAllowed(boolean allowed) {
+               _emptyCellAllowed = allowed;
+       }
+
+       /**
+        * Retrieve the settings for empty cells allowed
+        * 
+        * @return True if this object should treats empty as valid value , false
+        *         otherwise
+        */
+       public boolean getEmptyCellAllowed() {
+               return _emptyCellAllowed;
+       }
+
+       /**
+        * Useful for list validation objects .
+        * 
+        * @param suppress
+        *            True if a list should display the values into a drop down list ,
+        *            false otherwise . In other words , if a list should display
+        *            the arrow sign on its right side
+        */
+       public void setSuppressDropDownArrow(boolean suppress) {
+               _suppress_dropdown_arrow = suppress;
+       }
+
+       /**
+        * Useful only list validation objects . This method always returns false if
+        * the object isn't a list validation object
+        * 
+        * @return <code>true</code> if a list should display the values into a drop down list ,
+        *         <code>false</code> otherwise .
+        */
+       public boolean getSuppressDropDownArrow() {
+               if (_constraint.isListValidationType()) {
+                       return _suppress_dropdown_arrow;
+               }
+               return false;
+       }
+
+       /**
+        * Sets the behaviour when a cell which belongs to this object is selected
+        * 
+        * @param show <code>true</code> if an prompt box should be displayed , <code>false</code> otherwise
+        */
+       public void setShowPromptBox(boolean show) {
+               _showPromptBox = show;
+       }
+
+       /**
+        * @param show <code>true</code> if an prompt box should be displayed , <code>false</code> otherwise
+        */
+       public boolean getShowPromptBox() {
+               return _showPromptBox;
+       }
+
+       /**
+        * Sets the behaviour when an invalid value is entered
+        * 
+        * @param show <code>true</code> if an error box should be displayed , <code>false</code> otherwise
+        */
+       public void setShowErrorBox(boolean show) {
+               _showErrorBox = show;
+       }
+
+       /**
+        * @return <code>true</code> if an error box should be displayed , <code>false</code> otherwise
+        */
+       public boolean getShowErrorBox() {
+               return _showErrorBox;
+       }
+
+
+       /**
+        * Sets the title and text for the prompt box . Prompt box is displayed when
+        * the user selects a cell which belongs to this validation object . In
+        * order for a prompt box to be displayed you should also use method
+        * setShowPromptBox( boolean show )
+        * 
+        * @param title The prompt box's title
+        * @param text The prompt box's text
+        */
+       public void createPromptBox(String title, String text) {
+               _prompt_title = title;
+               _prompt_text = text;
+               this.setShowPromptBox(true);
+       }
+
+       /**
+        * @return Prompt box's title or <code>null</code>
+        */
+       public String getPromptBoxTitle() {
+               return _prompt_title;
+       }
+
+       /**
+        * @return Prompt box's text or <code>null</code>
+        */
+       public String getPromptBoxText() {
+               return _prompt_text;
+       }
+
+       /**
+        * Sets the title and text for the error box . Error box is displayed when
+        * the user enters an invalid value int o a cell which belongs to this
+        * validation object . In order for an error box to be displayed you should
+        * also use method setShowErrorBox( boolean show )
+        * 
+        * @param title The error box's title
+        * @param text The error box's text
+        */
+       public void createErrorBox(String title, String text) {
+               _error_title = title;
+               _error_text = text;
+               this.setShowErrorBox(true);
+       }
+
+       /**
+        * @return Error box's title or <code>null</code>
+        */
+       public String getErrorBoxTitle() {
+               return _error_title;
+       }
+
+       /**
+        * @return Error box's text or <code>null</code>
+        */
+       public String getErrorBoxText() {
+               return _error_text;
+       }
+
+       public DVRecord createDVRecord(HSSFWorkbook workbook) {
+
+               FormulaPair fp = _constraint.createFormulas(workbook);
+               
+               return new DVRecord(_constraint.getValidationType(),
+                               _constraint.getOperator(),
+                               _errorStyle, _emptyCellAllowed, getSuppressDropDownArrow(),
+                               _constraint.isExplicitList(),
+                               _showPromptBox, _prompt_title, _prompt_text,
+                               _showErrorBox, _error_title, _error_text,
+                               fp.getFormula1(), fp.getFormula2(),
+                               _regions);
+       }
+}
index d4bc9613d04134f2596bb83e2616175073f0d138..dcf68b9cf3eb8fcf5c03179e0e2474ccefa69abd 100644 (file)
@@ -28,7 +28,6 @@ import java.text.NumberFormat;
 import java.util.ArrayList;
 import java.util.Iterator;
 import java.util.List;
-import java.util.Stack;
 import java.util.TreeMap;
 
 import org.apache.poi.ddf.EscherRecord;
@@ -49,13 +48,13 @@ import org.apache.poi.hssf.record.SCLRecord;
 import org.apache.poi.hssf.record.VCenterRecord;
 import org.apache.poi.hssf.record.WSBoolRecord;
 import org.apache.poi.hssf.record.WindowTwoRecord;
+import org.apache.poi.hssf.record.aggregates.DataValidityTable;
 import org.apache.poi.hssf.record.formula.Ptg;
 import org.apache.poi.hssf.record.formula.RefPtg;
-import org.apache.poi.hssf.util.HSSFCellRangeAddress;
-import org.apache.poi.hssf.util.HSSFDataValidation;
 import org.apache.poi.hssf.util.PaneInformation;
 import org.apache.poi.ss.usermodel.CellStyle;
 import org.apache.poi.ss.usermodel.Row;
+import org.apache.poi.ss.util.CellRangeAddress;
 import org.apache.poi.hssf.util.Region;
 import org.apache.poi.util.POILogFactory;
 import org.apache.poi.util.POILogger;
@@ -393,92 +392,19 @@ public class HSSFSheet implements org.apache.poi.ss.usermodel.Sheet
 
     /**
      * Creates a data validation object
-     * @param obj_validation The Data validation object settings
+     * @param dataValidation The Data validation object settings
      */
-    public void addValidationData(HSSFDataValidation obj_validation)
-    {
-       if ( obj_validation == null )
-       {
-         return;
-       }
-       DVALRecord dvalRec = (DVALRecord)sheet.findFirstRecordBySid( DVALRecord.sid );
-       int eofLoc = sheet.findFirstRecordLocBySid( EOFRecord.sid );
-       if ( dvalRec == null )
-       {
-          dvalRec = new DVALRecord();
-          sheet.getRecords().add( eofLoc, dvalRec );
-       }
-       int curr_dvRecNo = dvalRec.getDVRecNo();
-       dvalRec.setDVRecNo(curr_dvRecNo+1);
-
-       //create dv record
-       DVRecord dvRecord = new DVRecord();
-
-       //dv record's option flags
-       dvRecord.setDataType( obj_validation.getDataValidationType() );
-       dvRecord.setErrorStyle(obj_validation.getErrorStyle());
-       dvRecord.setEmptyCellAllowed(obj_validation.getEmptyCellAllowed());
-       dvRecord.setSurppresDropdownArrow(obj_validation.getSurppressDropDownArrow());
-       dvRecord.setShowPromptOnCellSelected(obj_validation.getShowPromptBox());
-       dvRecord.setShowErrorOnInvalidValue(obj_validation.getShowErrorBox());
-       dvRecord.setConditionOperator(obj_validation.getOperator());
-
-       //string fields
-       dvRecord.setStringField( DVRecord.STRING_PROMPT_TITLE,obj_validation.getPromptBoxTitle());
-       dvRecord.setStringField( DVRecord.STRING_PROMPT_TEXT, obj_validation.getPromptBoxText());
-       dvRecord.setStringField( DVRecord.STRING_ERROR_TITLE, obj_validation.getErrorBoxTitle());
-       dvRecord.setStringField( DVRecord.STRING_ERROR_TEXT, obj_validation.getErrorBoxText());
-
-       //formula fields ( size and data )
-       String str_formula = obj_validation.getFirstFormula();
-       FormulaParser fp = new FormulaParser(str_formula, workbook);
-       fp.parse();
-       Stack ptg_arr = new Stack();
-       Ptg[] ptg  = fp.getRPNPtg();
-       int size = 0;
-       for (int k = 0; k < ptg.length; k++)
-       {
-           if ( ptg[k] instanceof org.apache.poi.hssf.record.formula.AreaPtg )
-           {
-              //we should set ptgClass to Ptg.CLASS_REF and explicit formula string to false
-              ptg[k].setClass(Ptg.CLASS_REF);
-              obj_validation.setExplicitListFormula(false);
-           }
-           size += ptg[k].getSize();
-           ptg_arr.push(ptg[k]);
-       }
-       dvRecord.setFirstFormulaRPN(ptg_arr);
-       dvRecord.setFirstFormulaSize((short)size);
-
-       dvRecord.setListExplicitFormula(obj_validation.getExplicitListFormula());
-
-       if ( obj_validation.getSecondFormula() != null )
-       {
-         str_formula = obj_validation.getSecondFormula();
-         fp = new FormulaParser(str_formula, workbook);
-         fp.parse();
-         ptg_arr = new Stack();
-         ptg  = fp.getRPNPtg();
-         size = 0;
-         for (int k = 0; k < ptg.length; k++)
-         {
-             size += ptg[k].getSize();
-             ptg_arr.push(ptg[k]);
-         }
-         dvRecord.setSecFormulaRPN(ptg_arr);
-         dvRecord.setSecFormulaSize((short)size);
+    public void addValidationData(HSSFDataValidation dataValidation) {
+       if (dataValidation == null) {
+           throw new IllegalArgumentException("objValidation must not be null");
        }
+       DataValidityTable dvt = sheet.getOrCreateDataValidityTable();
 
-       //dv records cell range field
-       HSSFCellRangeAddress cell_range = new HSSFCellRangeAddress();
-       cell_range.addADDRStructure(obj_validation.getFirstRow(), obj_validation.getFirstColumn(), obj_validation.getLastRow(), obj_validation.getLastColumn());
-       dvRecord.setCellRangeAddress(cell_range);
-
-       //add dv record
-       eofLoc = sheet.findFirstRecordLocBySid( EOFRecord.sid );
-       sheet.getRecords().add( eofLoc, dvRecord );
+       DVRecord dvRecord = dataValidation.createDVRecord(workbook);
+       dvt.addDataValidation(dvRecord);
     }
 
+
     /**
      * Get the visibility state for a given column.
      * @param column - the column to get (0-based)
@@ -610,19 +536,28 @@ public class HSSFSheet implements org.apache.poi.ss.usermodel.Sheet
     }
 
     /**
-     * adds a merged region of cells (hence those cells form one)
-     * @param region (rowfrom/colfrom-rowto/colto) to merge
-     * @return index of this region
+     * @deprecated (Aug-2008) use <tt>CellRangeAddress</tt> instead of <tt>Region</tt>
      */
     public int addMergedRegion(org.apache.poi.ss.util.Region region)
     {
-        //return sheet.addMergedRegion((short) region.getRowFrom(),
         return sheet.addMergedRegion( region.getRowFrom(),
                 region.getColumnFrom(),
                 //(short) region.getRowTo(),
                 region.getRowTo(),
                 region.getColumnTo());
     }
+    /**
+     * adds a merged region of cells (hence those cells form one)
+     * @param region (rowfrom/colfrom-rowto/colto) to merge
+     * @return index of this region
+     */
+    public int addMergedRegion(CellRangeAddress region)
+    {
+        return sheet.addMergedRegion( region.getFirstRow(),
+                region.getFirstColumn(),
+                region.getLastRow(),
+                region.getLastColumn());
+    }
 
     /**
      * Whether a record must be inserted or not at generation to indicate that
@@ -659,7 +594,7 @@ public class HSSFSheet implements org.apache.poi.ss.usermodel.Sheet
 
     /**
      * TODO: Boolean not needed, remove after next release
-     * @deprecated use getVerticallyCenter() instead
+     * @deprecated (Mar-2008) use getVerticallyCenter() instead
      */
     public boolean getVerticallyCenter(boolean value) {
         return getVerticallyCenter();
@@ -724,14 +659,19 @@ public class HSSFSheet implements org.apache.poi.ss.usermodel.Sheet
     }
 
     /**
-     * gets the region at a particular index
-     * @param index of the region to fetch
-     * @return the merged region (simple eh?)
+     * @deprecated (Aug-2008) use {@link HSSFSheet#getMergedRegion(int)}
      */
-
-    public Region getMergedRegionAt(int index)
-    {
-        return new Region(sheet.getMergedRegionAt(index));
+    public Region getMergedRegionAt(int index) {
+        CellRangeAddress cra = getMergedRegion(index);
+        
+               return new Region(cra.getFirstRow(), (short)cra.getFirstColumn(), 
+                               cra.getLastRow(), (short)cra.getLastColumn());
+    }
+    /**
+     * @return the merged region at the specified index
+     */
+    public CellRangeAddress getMergedRegion(int index) {
+        return (CellRangeAddress)sheet.getMergedRegionAt(index);
     }
 
     /**
@@ -1164,36 +1104,43 @@ public class HSSFSheet implements org.apache.poi.ss.usermodel.Sheet
     protected void shiftMerged(int startRow, int endRow, int n, boolean isRow) {
         List shiftedRegions = new ArrayList();
         //move merged regions completely if they fall within the new region boundaries when they are shifted
-        for (int i = 0; i < this.getNumMergedRegions(); i++) {
-             Region merged = this.getMergedRegionAt(i);
+        for (int i = 0; i < getNumMergedRegions(); i++) {
+             CellRangeAddress merged = getMergedRegion(i);
 
-             boolean inStart = (merged.getRowFrom() >= startRow || merged.getRowTo() >= startRow);
-             boolean inEnd =  (merged.getRowTo() <= endRow || merged.getRowFrom() <= endRow);
+             boolean inStart= (merged.getFirstRow() >= startRow || merged.getLastRow() >= startRow);
+             boolean inEnd  = (merged.getFirstRow() <= endRow   || merged.getLastRow() <= endRow);
 
-             //dont check if it's not within the shifted area
-             if (! (inStart && inEnd)) continue;
+             //don't check if it's not within the shifted area
+             if (!inStart || !inEnd) {
+                               continue;
+                        }
 
              //only shift if the region outside the shifted rows is not merged too
-             if (!merged.contains(startRow-1, (short)0) && !merged.contains(endRow+1, (short)0)){
-                 merged.setRowFrom(merged.getRowFrom()+n);
-                 merged.setRowTo(merged.getRowTo()+n);
+             if (!containsCell(merged, startRow-1, 0) && !containsCell(merged, endRow+1, 0)){
+                 merged.setFirstRow(merged.getFirstRow()+n);
+                 merged.setLastRow(merged.getLastRow()+n);
                  //have to remove/add it back
                  shiftedRegions.add(merged);
-                 this.removeMergedRegion(i);
+                 removeMergedRegion(i);
                  i = i -1; // we have to back up now since we removed one
-
              }
-
         }
 
-        //readd so it doesn't get shifted again
+        //read so it doesn't get shifted again
         Iterator iterator = shiftedRegions.iterator();
         while (iterator.hasNext()) {
-            Region region = (Region)iterator.next();
+               CellRangeAddress region = (CellRangeAddress)iterator.next();
 
             this.addMergedRegion(region);
         }
-
+    }
+    private static boolean containsCell(CellRangeAddress cr, int rowIx, int colIx) {
+        if (cr.getFirstRow() <= rowIx && cr.getLastRow() >= rowIx
+                && cr.getFirstColumn() <= colIx && cr.getLastColumn() >= colIx)
+        {
+            return true;
+        }
+        return false;
     }
 
     /**
@@ -1812,17 +1759,20 @@ public class HSSFSheet implements org.apache.poi.ss.usermodel.Sheet
             HSSFRow row = (HSSFRow) it.next();
             HSSFCell cell = row.getCell(column);
 
-            if (cell == null) continue;
+            if (cell == null) {
+                               continue;
+                       }
 
             int colspan = 1;
             for (int i = 0 ; i < getNumMergedRegions(); i++) {
-                if (getMergedRegionAt(i).contains(row.getRowNum(), column)) {
+                CellRangeAddress region = getMergedRegion(i);
+                               if (containsCell(region, row.getRowNum(), column)) {
                        if (!useMergedCells) {
                        // If we're not using merged cells, skip this one and move on to the next. 
                                continue rows;
                        }
-                       cell = row.getCell(getMergedRegionAt(i).getColumnFrom());
-                       colspan = 1+ getMergedRegionAt(i).getColumnTo() - getMergedRegionAt(i).getColumnFrom();
+                       cell = row.getCell(region.getFirstColumn());
+                       colspan = 1 + region.getLastColumn() - region.getFirstColumn();
                 }
             }
 
index 8e8cf40a1bcdaef78735268d2f70d122641045d4..cdfae6cd1c4402c703b0d08fae669fc049be9a01 100644 (file)
@@ -21,6 +21,7 @@ import org.apache.poi.hssf.model.Sheet;
 import org.apache.poi.hssf.record.CFRuleRecord;\r
 import org.apache.poi.hssf.record.aggregates.CFRecordsAggregate;\r
 import org.apache.poi.ss.util.Region;\r
+import org.apache.poi.ss.util.CellRangeAddress;\r
 \r
 /**\r
  * The 'Conditional Formatting' facet of <tt>HSSFSheet</tt>\r
@@ -100,7 +101,12 @@ public final class HSSFSheetConditionalFormatting {
 \r
                return _sheet.addConditionalFormatting(cfraClone);\r
        }\r
-\r
+       /**\r
+        * @deprecated use <tt>CellRangeAddress</tt> instead of <tt>Region</tt>\r
+        */\r
+       public int addConditionalFormatting(Region[] regions, HSSFConditionalFormattingRule[] cfRules) {\r
+               return addConditionalFormatting(Region.convertRegionsToCellRanges(regions), cfRules);\r
+       }\r
        /**\r
         * Allows to add a new Conditional Formatting set to the sheet.\r
         *\r
@@ -109,8 +115,7 @@ public final class HSSFSheetConditionalFormatting {
         *\r
         * @return index of the newly created Conditional Formatting object\r
         */\r
-\r
-       public int addConditionalFormatting(Region[] regions, HSSFConditionalFormattingRule[] cfRules) {\r
+       public int addConditionalFormatting(CellRangeAddress[] regions, HSSFConditionalFormattingRule[] cfRules) {\r
                if (regions == null) {\r
                        throw new IllegalArgumentException("regions must not be null");\r
                }\r
@@ -132,7 +137,7 @@ public final class HSSFSheetConditionalFormatting {
                return _sheet.addConditionalFormatting(cfra);\r
        }\r
 \r
-       public int addConditionalFormatting(Region[] regions,\r
+       public int addConditionalFormatting(CellRangeAddress[] regions,\r
                        HSSFConditionalFormattingRule rule1)\r
        {\r
                return addConditionalFormatting(regions,\r
@@ -142,7 +147,7 @@ public final class HSSFSheetConditionalFormatting {
                                });\r
        }\r
 \r
-       public int addConditionalFormatting(Region[] regions,\r
+       public int addConditionalFormatting(CellRangeAddress[] regions,\r
                        HSSFConditionalFormattingRule rule1,\r
                        HSSFConditionalFormattingRule rule2)\r
        {\r
@@ -153,18 +158,6 @@ public final class HSSFSheetConditionalFormatting {
                                });\r
        }\r
 \r
-       public int addConditionalFormatting(Region[] regions,\r
-                       HSSFConditionalFormattingRule rule1,\r
-                       HSSFConditionalFormattingRule rule2,\r
-                       HSSFConditionalFormattingRule rule3)\r
-       {\r
-               return addConditionalFormatting(regions,\r
-                               new HSSFConditionalFormattingRule[]\r
-                               {\r
-                                               rule1, rule2, rule3\r
-                               });\r
-       }\r
-\r
        /**\r
        * gets Conditional Formatting object at a particular index\r
        * \r
diff --git a/src/java/org/apache/poi/hssf/util/CellRangeAddress.java b/src/java/org/apache/poi/hssf/util/CellRangeAddress.java
new file mode 100644 (file)
index 0000000..c7ff265
--- /dev/null
@@ -0,0 +1,46 @@
+/* ====================================================================\r
+   Copyright 2002-2004   Apache Software Foundation\r
+\r
+   Licensed under the Apache License, Version 2.0 (the "License");\r
+   you may not use this file except in compliance with the License.\r
+   You may obtain a copy of the License at\r
+\r
+       http://www.apache.org/licenses/LICENSE-2.0\r
+\r
+   Unless required by applicable law or agreed to in writing, software\r
+   distributed under the License is distributed on an "AS IS" BASIS,\r
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+   See the License for the specific language governing permissions and\r
+   limitations under the License.\r
+==================================================================== */\r
+\r
+package org.apache.poi.hssf.util;\r
+\r
+import org.apache.poi.hssf.record.RecordInputStream;\r
+import org.apache.poi.hssf.record.SelectionRecord;\r
+import org.apache.poi.util.LittleEndian;\r
+\r
+/**\r
+ * See OOO documentation: excelfileformat.pdf sec 2.5.14 - 'Cell Range Address'<p/>\r
+ * \r
+ * Note - {@link SelectionRecord} uses the BIFF5 version of this structure\r
+ * @author Dragos Buleandra (dragos.buleandra@trade2b.ro)\r
+ */\r
+public class CellRangeAddress extends org.apache.poi.ss.util.CellRangeAddress {\r
+       public CellRangeAddress(int firstRow, int lastRow, int firstCol, int lastCol) {\r
+               super(firstRow, lastRow, firstCol, lastCol);\r
+       }\r
+       public CellRangeAddress() {\r
+               super();\r
+       }\r
+       public CellRangeAddress(RecordInputStream in) {\r
+               if (in.remaining() < ENCODED_SIZE) {\r
+                       // Ran out of data\r
+                       throw new RuntimeException("Ran out of data reading CellRangeAddress");\r
+               } \r
+               _firstRow = in.readUShort();\r
+               _lastRow = in.readUShort();\r
+               _firstCol = in.readUShort();\r
+               _lastCol = in.readUShort();\r
+       }\r
+}\r
diff --git a/src/java/org/apache/poi/hssf/util/CellRangeAddressList.java b/src/java/org/apache/poi/hssf/util/CellRangeAddressList.java
new file mode 100644 (file)
index 0000000..79ea50a
--- /dev/null
@@ -0,0 +1,57 @@
+/* ====================================================================
+   Copyright 2002-2004   Apache Software Foundation
+
+   Licensed 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.util;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.poi.hssf.record.RecordInputStream;
+import org.apache.poi.util.LittleEndian;
+
+/**
+ * Implementation of the cell range address lists,like is described
+ * in OpenOffice.org's Excel Documentation: excelfileformat.pdf sec 2.5.14 -
+ * 'Cell Range Address List'
+ * 
+ * In BIFF8 there is a common way to store absolute cell range address lists in
+ * several records (not formulas). A cell range address list consists of a field
+ * with the number of ranges and the list of the range addresses. Each cell
+ * range address (called an ADDR structure) contains 4 16-bit-values.
+ * </p>
+ * 
+ * @author Dragos Buleandra (dragos.buleandra@trade2b.ro)
+ */
+public class CellRangeAddressList extends org.apache.poi.ss.util.CellRangeAddressList {
+       public CellRangeAddressList(int firstRow, int lastRow, int firstCol, int lastCol) {
+               super(firstRow,lastRow,firstCol,lastCol);
+       }
+       public CellRangeAddressList() {
+               super();
+       }
+
+       /**
+        * @param in the RecordInputstream to read the record from
+        */
+       public CellRangeAddressList(RecordInputStream in) {
+               super();
+               int nItems = in.readUShort();
+
+               for (int k = 0; k < nItems; k++) {
+                       _list.add(new CellRangeAddress(in));
+               }
+       }
+}
diff --git a/src/java/org/apache/poi/hssf/util/HSSFCellRangeAddress.java b/src/java/org/apache/poi/hssf/util/HSSFCellRangeAddress.java
deleted file mode 100644 (file)
index 7380496..0000000
+++ /dev/null
@@ -1,269 +0,0 @@
-/* ====================================================================
-   Copyright 2002-2004   Apache Software Foundation
-
-   Licensed 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.util;
-
-import org.apache.poi.hssf.record.RecordInputStream;
-import org.apache.poi.util.LittleEndian;
-import org.apache.poi.util.POILogFactory;
-import org.apache.poi.util.POILogger;
-
-import java.util.ArrayList;
-
-/**
- * <p>Title: HSSFCellRangeAddress</p>
- * <p>Description:
- *          Implementation of the cell range address lists,like is described in
- *          OpenOffice.org's Excel Documentation .
- *          In BIFF8 there is a common way to store absolute cell range address
- *          lists in several records (not formulas). A cell range address list
- *          consists of a field with the number of ranges and the list of the range
- *          addresses. Each cell range address (called an ADDR structure) contains
- *          4 16-bit-values.</p>
- * <p>Copyright: Copyright (c) 2004</p>
- * <p>Company: </p>
- * @author Dragos Buleandra (dragos.buleandra@trade2b.ro)
- * @version 2.0-pre
- */
-
-public class HSSFCellRangeAddress
-{
-       private static POILogger logger = POILogFactory.getLogger(HSSFCellRangeAddress.class);
-       
-    /**
-     * Number of following ADDR structures
-     */
-    private short             field_addr_number;
-
-    /**
-     * List of ADDR structures. Each structure represents a cell range
-     */
-    private ArrayList         field_regions_list;
-
-    public HSSFCellRangeAddress()
-    {
-
-    }
-
-    /**
-     * Construct a new HSSFCellRangeAddress object and sets its fields appropriately .
-     * Even this isn't an Excel record , I kept the same behavior for reading/writing
-     * the object's data as for a regular record .
-     * 
-     * @param in the RecordInputstream to read the record from
-     */
-    public HSSFCellRangeAddress(RecordInputStream in)
-    {
-        this.fillFields(in);
-    }
-
-    public void fillFields(RecordInputStream in)
-    {
-        this.field_addr_number = in.readShort(); 
-               this.field_regions_list = new ArrayList(this.field_addr_number);
-
-               for (int k = 0; k < this.field_addr_number; k++)
-               {
-            short first_row = in.readShort(); 
-            short first_col = in.readShort();
-            
-            short last_row  = first_row;
-            short last_col  = first_col;
-            if(in.remaining() >= 4) {
-                   last_row  = in.readShort();
-                   last_col  = in.readShort();
-            } else {
-               // Ran out of data
-               // For now, issue a warning, finish, and 
-               //  hope for the best....
-               logger.log(POILogger.WARN, "Ran out of data reading cell references for DVRecord");
-               k = this.field_addr_number;
-            }
-
-                       AddrStructure region = new AddrStructure(first_row, first_col, last_row, last_col);
-                       this.field_regions_list.add(region);
-               }
-    }
-
-    /**
-     * Get the number of following ADDR structures.
-     * The number of this structures is automatically set when reading an Excel file
-     * and/or increased when you manually add a new ADDR structure .
-     * This is the reason there isn't a set method for this field .
-     * @return number of ADDR structures
-     */
-    public short getADDRStructureNumber()
-    {
-        return this.field_addr_number;
-    }
-
-    /**
-     * Add an ADDR structure .
-     * @param first_row - the upper left hand corner's row
-     * @param first_col - the upper left hand corner's col
-     * @param last_row  - the lower right hand corner's row
-     * @param last_col  - the lower right hand corner's col
-     * @return the index of this ADDR structure
-     */
-    public int addADDRStructure(short first_row, short first_col, short last_row, short last_col)
-    {
-        if (this.field_regions_list == null)
-        {
-            //just to be sure :-)
-            this.field_addr_number= 0;
-            this.field_regions_list = new ArrayList(10);
-        }
-        AddrStructure region = new AddrStructure(first_row, last_row, first_col, last_col);
-
-        this.field_regions_list.add(region);
-        this.field_addr_number++;
-        return this.field_addr_number;
-    }
-
-    /**
-     * Remove the ADDR structure stored at the passed in index
-     * @param index The ADDR structure's index
-     */
-    public void removeADDRStructureAt(int index)
-    {
-        this.field_regions_list.remove(index);
-        this.field_addr_number--;
-    }
-
-    /**
-     * return the ADDR structure at the given index.
-     * @return AddrStructure representing
-     */
-    public AddrStructure getADDRStructureAt(int index)
-    {
-        return ( AddrStructure ) this.field_regions_list.get(index);
-    }
-
-    public int serialize(int offset, byte [] data)
-    {
-        int pos  = 2;
-
-        LittleEndian.putShort(data, offset, this.getADDRStructureNumber());
-        for (int k = 0; k < this.getADDRStructureNumber(); k++)
-        {
-            AddrStructure region = this.getADDRStructureAt(k);
-            LittleEndian.putShort(data, offset + pos, region.getFirstRow());
-            pos += 2;
-            LittleEndian.putShort(data, offset + pos, region.getLastRow());
-            pos += 2;
-            LittleEndian.putShort(data, offset + pos, region.getFirstColumn());
-            pos += 2;
-            LittleEndian.putShort(data, offset + pos, region.getLastColumn());
-            pos += 2;
-        }
-        return this.getSize();
-    }
-
-    public int getSize()
-    {
-       return 2 + this.field_addr_number*8;
-    }
-
-    public class AddrStructure
-    {
-        private short _first_row;
-        private short _first_col;
-        private short _last_row;
-        private short _last_col;
-
-        public AddrStructure(short first_row, short last_row, short first_col, short last_col)
-        {
-            this._first_row = first_row;
-            this._last_row   = last_row;
-            this._first_col = first_col;
-            this._last_col   = last_col;
-        }
-
-               /**
-                * get the upper left hand corner column number
-                * @return column number for the upper left hand corner
-                */
-               public short getFirstColumn()
-               {
-                       return this._first_col;
-               }
-
-               /**
-                * get the upper left hand corner row number
-                * @return row number for the upper left hand corner
-                */
-               public short getFirstRow()
-               {
-                       return this._first_row;
-               }
-
-               /**
-                * get the lower right hand corner column number
-                * @return column number for the lower right hand corner
-                */
-               public short getLastColumn()
-               {
-                       return this._last_col;
-               }
-
-               /**
-                * get the lower right hand corner row number
-                * @return row number for the lower right hand corner
-                */
-               public short getLastRow()
-               {
-                       return this._last_row;
-               }
-
-               /**
-                * set the upper left hand corner column number
-                * @param this._first_col  column number for the upper left hand corner
-                */
-               public void setFirstColumn(short first_col)
-               {
-                       this._first_col = first_col;
-               }
-
-               /**
-                * set the upper left hand corner row number
-                * @param rowFrom  row number for the upper left hand corner
-                */
-               public void setFirstRow(short first_row)
-               {
-                       this._first_row = first_row;
-               }
-
-               /**
-                * set the lower right hand corner column number
-                * @param colTo  column number for the lower right hand corner
-                */
-               public void setLastColumn(short last_col)
-               {
-                       this._last_col = last_col;
-               }
-
-               /**
-                * get the lower right hand corner row number
-                * @param rowTo  row number for the lower right hand corner
-                */
-               public void setLastRow(short last_row)
-               {
-                       this._last_row = last_row;
-               }
-       }
-}
-
-
diff --git a/src/java/org/apache/poi/hssf/util/HSSFDataValidation.java b/src/java/org/apache/poi/hssf/util/HSSFDataValidation.java
deleted file mode 100644 (file)
index af578be..0000000
+++ /dev/null
@@ -1,471 +0,0 @@
-/* ====================================================================
-   Copyright 2002-2004   Apache Software Foundation
-
-   Licensed 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.util;
-
-/**
- * <p>Title: HSSFDataValidation</p>
- * <p>Description: Utilty class for creating data validation cells</p>
- * <p>Copyright: Copyright (c) 2004</p>
- * <p>Company: </p>
- * @author Dragos Buleandra (dragos.buleandra@trade2b.ro)
- * @version 2.0-pre
- */
-
-public class HSSFDataValidation
-{
-  /**
-   * Validation data type constants
-   */
-  /**
-   * Any type
-   */
-  public static final int DATA_TYPE_ANY              = 0x00;
-  /**
-   * Integer type
-   */
-  public static final int DATA_TYPE_INTEGER          = 0x01;
-  /**
-   * Decimal type
-   */
-  public static final int DATA_TYPE_DECIMAL          = 0x02;
-  /**
-   * List type ( combo box type )
-   */
-  public static final int DATA_TYPE_LIST             = 0x03;
-  /**
-   * Date type
-   */
-  public static final int DATA_TYPE_DATE             = 0x04;
-  /**
-   * Time type
-   */
-  public static final int DATA_TYPE_TIME             = 0x05;
-  /**
-   * String length type
-   */
-  public static final int DATA_TYPE_TEXT_LENGTH      = 0x06;
-  /**
-   * Formula ( custom ) type
-   */
-  public static final int DATA_TYPE_FORMULA          = 0x07;
-
-  /**
-   * Error style constants for error box
-   */
-  /**
-   * STOP style like
-   */
-  public static final int ERROR_STYLE_STOP           = 0x00;
-  /**
-   * WARNING style like
-   */
-  public static final int ERROR_STYLE_WARNING        = 0x01;
-  /**
-   * INFO style like
-   */
-  public static final int ERROR_STYLE_INFO           = 0x02;
-
-  /**
-   * Condition operator
-   */
-  public static final int OPERATOR_BETWEEN           = 0x00;
-  public static final int OPERATOR_NOT_BETWEEN       = 0x01;
-  public static final int OPERATOR_EQUAL             = 0x02;
-  public static final int OPERATOR_NOT_EQUAL         = 0x03;
-  public static final int OPERATOR_GREATER_THAN      = 0x04;
-  public static final int OPERATOR_LESS_THAN         = 0x05;
-  public static final int OPERATOR_GREATER_OR_EQUAL  = 0x06;
-  public static final int OPERATOR_LESS_OR_EQUAL     = 0x07;
-
-  private short _first_row                  = 0;
-  private short _first_col                  = 0;
-  private short _last_row                   = 0;
-  private short _last_col                   = 0;
-
-  private String _prompt_title              = null;
-  private String _prompt_text               = null;
-  private String _error_title               = null;
-  private String _error_text                = null;
-  private String _string_first_formula      = null;
-  private String _string_sec_formula        = null;
-
-  private int  _data_type                   = HSSFDataValidation.DATA_TYPE_ANY;
-  private int  _error_style                 = HSSFDataValidation.ERROR_STYLE_STOP;
-  private boolean  _list_explicit_formula   = true;
-  private boolean  _empty_cell_allowed      = true;
-  private boolean  _surpress_dropdown_arrow = false;
-  private boolean  _show_prompt_box         = true;
-  private boolean  _show_error_box          = true;
-  private int _operator                     = HSSFDataValidation.OPERATOR_BETWEEN;
-
-
-  /**
-   * Empty constructor
-   */
-  public HSSFDataValidation( )
-  {
-  }
-
-  /**
-   * Constructor wich initializes the cell range on wich this object will be applied
-   * @param first_row First row
-   * @param first_col First column
-   * @param last_row Last row
-   * @param last_col Last column
-   */
-  public HSSFDataValidation( short first_row, short first_col, short last_row, short last_col )
-  {
-      this._first_row = first_row;
-      this._first_col = first_col;
-      this._last_row  = last_row;
-      this._last_col  = last_col;
-  }
-
-  /**
-   * Set the type of this object
-   * @param data_type The type
-   * @see DATA_TYPE_ANY, DATA_TYPE_INTEGER, DATA_TYPE_DECIMNAL, DATA_TYPE_LIST, DATA_TYPE_DATE,
-   *      DATA_TYPE_TIME, DATA_TYPE_TEXT_LENTGH, DATA_TYPE_FORMULA
-   */
-  public void setDataValidationType( int data_type )
-  {
-      this._data_type = data_type;
-  }
-
-  /**
-   * The data type of this object
-   * @return The type
-   * @see DATA_TYPE_ANY, DATA_TYPE_INTEGER, DATA_TYPE_DECIMNAL, DATA_TYPE_LIST, DATA_TYPE_DATE,
-   *      DATA_TYPE_TIME, DATA_TYPE_TEXT_LENTGH, DATA_TYPE_FORMULA
-   */
-  public int getDataValidationType()
-  {
-     return this._data_type;
-  }
-
-  /**
-   * Sets the error style for error box
-   * @param error_style Error style constant
-   * @see ERROR_STYLE_STOP, ERROR_STYLE_WARNING, ERROR_STYLE_INFO
-   */
-  public void setErrorStyle( int error_style )
-  {
-      this._error_style = error_style;
-  }
-
-  /**
-   * returns the error style of errror box
-   * @return the style constant
-   * @see ERROR_STYLE_STOP, ERROR_STYLE_WARNING, ERROR_STYLE_INFO
-   */
-  public int getErrorStyle( )
-  {
-      return this._error_style;
-  }
-
-  /**
-   * If this object has an explicit formula . This is useful only for list data validation object
-   * @param explicit True if use an explicit formula
-   */
-  public void setExplicitListFormula( boolean explicit )
-  {
-     this._list_explicit_formula = explicit;
-  }
-
-  /**
-   * Returns the settings for explicit formula . This is useful only for list data validation objects.
-   * This method always returns false if the object isn't a list validation object
-   * @see setDataValidationType( int data_type )
-   * @return
-   */
-  public boolean getExplicitListFormula( )
-  {
-    if ( this._data_type != HSSFDataValidation.DATA_TYPE_LIST )
-    {
-        return false;
-    }
-    return this._list_explicit_formula ;
-  }
-
-  /**
-   * Sets if this object allows empty as a valid value
-   * @param allowed True if this object should treats empty as valid value , false otherwise
-   */
-  public void setEmptyCellAllowed( boolean allowed )
-  {
-     this._empty_cell_allowed = allowed;
-  }
-
-  /**
-   * Retrieve the settings for empty cells allowed
-   * @return True if this object should treats empty as valid value , false otherwise
-   */
-  public boolean getEmptyCellAllowed( )
-  {
-     return this._empty_cell_allowed ;
-  }
-
-  /**
-   * Useful for list validation objects .
-   * @param surppres True if a list should display the values into a drop down list , false otherwise .
-   *                 In other words , if a list should display the arrow sign on its right side
-   */
-  public void setSurppressDropDownArrow( boolean surppres )
-  {
-     this._surpress_dropdown_arrow = surppres;
-  }
-
-  /**
-   * Useful only list validation objects .
-   * This method always returns false if the object isn't a list validation object
-   * @return True if a list should display the values into a drop down list , false otherwise .
-   * @see setDataValidationType( int data_type )
-   */
-  public boolean getSurppressDropDownArrow( )
-  {
-     if ( this._data_type != HSSFDataValidation.DATA_TYPE_LIST )
-     {
-         return false;
-     }
-     return this._surpress_dropdown_arrow ;
-  }
-
-  /**
-   * Sets the behaviour when a cell which belongs to this object is selected
-   * @param show True if an prompt box should be displayed , false otherwise
-   */
-  public void setShowPromptBox( boolean show )
-  {
-     this._show_prompt_box = show;
-  }
-
-  /**
-   * @param show True if an prompt box should be displayed , false otherwise
-   */
-  public boolean getShowPromptBox( )
-  {
-     if ( (this.getPromptBoxText() == null) && (this.getPromptBoxTitle() == null) )
-     {
-       return false;
-     }
-     return this._show_prompt_box ;
-  }
-
-  /**
-   * Sets the behaviour when an invalid value is entered
-   * @param show True if an error box should be displayed , false otherwise
-   */
-  public void setShowErrorBox( boolean show )
-  {
-     this._show_error_box = show;
-  }
-
-  /**
-   * @return True if an error box should be displayed , false otherwise
-   */
-  public boolean getShowErrorBox( )
-  {
-    if ( (this.getErrorBoxText() == null) && (this.getErrorBoxTitle() == null) )
-    {
-      return false;
-    }
-    return this._show_error_box ;
-  }
-
-  /**
-   * Sets the operator involved in the formula whic governs this object
-   * Example : if you wants that a cell to accept only values between 1 and 5 , which
-   * mathematically means 1 <= value <= 5 , then the operator should be OPERATOR_BETWEEN
-   * @param operator A constant for operator
-   * @see OPERATOR_BETWEEN, OPERATOR_NOT_BETWEEN, OPERATOR_EQUAL, OPERATOR_NOT_EQUAL
-   * OPERATOR_GREATER_THAN, OPERATOR_LESS_THAN, OPERATOR_GREATER_OR_EQUAL,
-   * OPERATOR_LESS_OR_EQUAL
-   */
-  public void setOperator( int operator )
-  {
-      this._operator = operator;
-  }
-
-  /**
-   * Retrieves the operator used for this object's formula
-   * @return
-   * @see OPERATOR_BETWEEN, OPERATOR_NOT_BETWEEN, OPERATOR_EQUAL, OPERATOR_NOT_EQUAL
-   * OPERATOR_GREATER_THAN, OPERATOR_LESS_THAN, OPERATOR_GREATER_OR_EQUAL,
-   * OPERATOR_LESS_OR_EQUAL
-   */
-  public int getOperator()
-  {
-      return this._operator;
-  }
-
-  /**
-   * Sets the title and text for the prompt box . Prompt box is displayed when the user
-   * selects a cell which belongs to this validation object . In order for a prompt box
-   * to be displayed you should also use method setShowPromptBox( boolean show )
-   * @param title The prompt box's title
-   * @param text The prompt box's text
-   * @see setShowPromptBox( boolean show )
-   */
-  public void createPromptBox( String title, String text )
-  {
-      this._prompt_title = title;
-      this._prompt_text  = text;
-      this.setShowPromptBox(true);
-  }
-
-  /**
-   * Returns the prompt box's title
-   * @return Prompt box's title or null
-   */
-  public String getPromptBoxTitle( )
-  {
-    return this._prompt_title;
-  }
-
-  /**
-   * Returns the prompt box's text
-   * @return Prompt box's text or null
-   */
-  public String getPromptBoxText( )
-  {
-    return this._prompt_text;
-  }
-
-  /**
-   * Sets the title and text for the error box . Error box is displayed when the user
-   * enters an invalid value int o a cell which belongs to this validation object .
-   * In order for an error box to be displayed you should also use method
-   * setShowErrorBox( boolean show )
-   * @param title The error box's title
-   * @param text The error box's text
-   * @see setShowErrorBox( boolean show )
-   */
-  public void createErrorBox( String title, String text )
-  {
-      this._error_title = title;
-      this._error_text  = text;
-      this.setShowErrorBox(true);
-  }
-
-  /**
-   * Returns the error box's title
-   * @return Error box's title or null
-   */
-  public String getErrorBoxTitle( )
-  {
-    return this._error_title;
-  }
-
-  /**
-   * Returns the error box's text
-   * @return Error box's text or null
-   */
-  public String getErrorBoxText( )
-  {
-    return this._error_text;
-  }
-
-  /**
-   * Sets the first formula for this object .
-   * A formula is divided into three parts : first formula , operator and second formula .
-   * In other words , a formula contains a left oprand , an operator and a right operand.
-   * This is the general rule . An example is 1<= value <= 5 . In this case ,
-   * the left operand ( or the first formula ) is the number 1 . The operator is
-   * OPERATOR_BETWEEN and the right operand ( or the second formula ) is 5 .
-   * @param formula
-   */
-  public void setFirstFormula( String formula )
-  {
-     this._string_first_formula = formula;
-  }
-
-  /**
-   * Returns the first formula
-   * @return
-   */
-  public String getFirstFormula( )
-  {
-     return this._string_first_formula;
-  }
-
-  /**
-   * Sets the first formula for this object .
-   * A formula is divided into three parts : first formula , operator and second formula .
-   * In other words , a formula contains a left oprand , an operator and a right operand.
-   * This is the general rule . An example is 1<= value <=5 . In this case ,
-   * the left operand ( or the first formula ) is the number 1 . The operator is
-   * OPERATOR_BETWEEN and the right operand ( or the second formula ) is 5 .
-   * But there are cases when a second formula isn't needed :
-   * You want somethink like : all values less than 5 . In this case , there's only a first
-   * formula ( in our case 5 ) and the operator OPERATOR_LESS_THAN
-   * @param formula
-   */
-  public void setSecondFormula( String formula )
-  {
-     this._string_sec_formula = formula;
-  }
-
-  /**
-   * Returns the second formula
-   * @return
-   */
-  public String getSecondFormula( )
-  {
-     return this._string_sec_formula;
-  }
-
-  public void setFirstRow( short first_row )
-  {
-    this._first_row = first_row;
-  }
-
-  public void setFirstColumn( short first_column )
-  {
-    this._first_col = first_column;
-  }
-
-  public void setLastRow( short last_row )
-  {
-    this._last_row = last_row;
-  }
-
-  public void setLastColumn( short last_column )
-  {
-    this._last_col = last_column;
-  }
-
-  public short getFirstRow()
-  {
-    return this._first_row;
-  }
-
-  public short getFirstColumn()
-  {
-    return this._first_col;
-  }
-
-  public short getLastRow()
-  {
-    return this._last_row;
-  }
-
-  public short getLastColumn()
-  {
-    return this._last_col;
-  }
-
-}
\ No newline at end of file
index 3f634af29946cdcb613faf6f24a8863c0cb08f22..25a67db211db9abd9dd050cce3e76111ea6ffa56 100644 (file)
@@ -18,8 +18,6 @@
 
 package org.apache.poi.hssf.util;
 
-import org.apache.poi.hssf.record.MergeCellsRecord.MergedRegion;
-
 /**
  * Represents a from/to row/col square.  This is a object primitive
  * that can be used to represent row,col - row,col just as one would use String
@@ -43,16 +41,6 @@ public class Region extends org.apache.poi.ss.util.Region
                super(rowFrom, colFrom, rowTo, colTo);
     }
 
-    /**
-     * special constructor (I know this is bad but it is so wrong that its right
-     * okay) that makes a region from a mergedcells's region subrecord.
-     */
-
-    public Region(MergedRegion region)
-    {
-        super(region);
-    }
-
     public Region(String ref) {
                super(ref);
        }
index 08f8dda43b5b428e7714b732428ed31b0da21f08..002b4aedc1fd647e45df9126d8acec0d67f27eea 100644 (file)
 ==================================================================== */
 
 
-
-/*
- * DateUtil.java
- *
- * Created on January 19, 2002, 9:30 AM
- */
 package org.apache.poi.ss.usermodel;
 
 import java.util.Calendar;
 import java.util.Date;
 import java.util.GregorianCalendar;
+import java.util.regex.Pattern;
 
 /**
  * Contains methods for dealing with Excel dates.
@@ -38,17 +33,20 @@ import java.util.GregorianCalendar;
  * @author  Alex Jacoby (ajacoby at gmail.com)
  * @author  Pavel Krupets (pkrupets at palmtreebusiness dot com)
  */
-
-public class DateUtil
-{
-    protected DateUtil()
-    {
+public class DateUtil {
+    protected DateUtil() {
+        // no instances of this class
     }
+    private static final int SECONDS_PER_MINUTE = 60;
+    private static final int MINUTES_PER_HOUR = 60;
+    private static final int HOURS_PER_DAY = 24;
+    private static final int SECONDS_PER_DAY = (HOURS_PER_DAY * MINUTES_PER_HOUR * SECONDS_PER_MINUTE);
+
+    private static final int    BAD_DATE         = -1;   // used to specify that date is invalid
+    private static final long   DAY_MILLISECONDS = SECONDS_PER_DAY * 1000L;
+
+    private static final Pattern TIME_SEPARATOR_PATTERN = Pattern.compile(":");
 
-    private static final int    BAD_DATE         =
-        -1;   // used to specify that date is invalid
-    private static final long   DAY_MILLISECONDS = 24 * 60 * 60 * 1000;
-    
     /**
      * Given a Date, converts it into a double representing its internal Excel representation,
      *   which is the number of days since 1/1/1900. Fractional days represent hours, minutes, and seconds.
@@ -57,7 +55,7 @@ public class DateUtil
      * @param  date the Date
      */
     public static double getExcelDate(Date date) {
-       return getExcelDate(date, false);
+        return getExcelDate(date, false);
     }
     /**
      * Given a Date, converts it into a double representing its internal Excel representation,
@@ -74,8 +72,8 @@ public class DateUtil
     }
     /**
      * Given a Date in the form of a Calendar, converts it into a double
-     *  representing its internal Excel representation, which is the 
-     *  number of days since 1/1/1900. Fractional days represent hours, 
+     *  representing its internal Excel representation, which is the
+     *  number of days since 1/1/1900. Fractional days represent hours,
      *  minutes, and seconds.
      *
      * @return Excel representation of Date (-1 if error - test for error by checking for less than 0.1)
@@ -83,41 +81,40 @@ public class DateUtil
      * @param use1904windowing Should 1900 or 1904 date windowing be used?
      */
     public static double getExcelDate(Calendar date, boolean use1904windowing) {
-       // Don't alter the supplied Calendar as we do our work
-       return internalGetExcelDate( (Calendar)date.clone(), use1904windowing );
+        // Don't alter the supplied Calendar as we do our work
+        return internalGetExcelDate( (Calendar)date.clone(), use1904windowing );
     }
     private static double internalGetExcelDate(Calendar date, boolean use1904windowing) {
-        if ((!use1904windowing && date.get(Calendar.YEAR) < 1900) || 
-            (use1904windowing && date.get(Calendar.YEAR) < 1904)) 
+        if ((!use1904windowing && date.get(Calendar.YEAR) < 1900) ||
+            (use1904windowing && date.get(Calendar.YEAR) < 1904))
         {
             return BAD_DATE;
-        } else {
-           // Because of daylight time saving we cannot use
-           //     date.getTime() - calStart.getTimeInMillis()
-           // as the difference in milliseconds between 00:00 and 04:00
-           // can be 3, 4 or 5 hours but Excel expects it to always
-           // be 4 hours.
-           // E.g. 2004-03-28 04:00 CEST - 2004-03-28 00:00 CET is 3 hours
-           // and 2004-10-31 04:00 CET - 2004-10-31 00:00 CEST is 5 hours
-            double fraction = (((date.get(Calendar.HOUR_OF_DAY) * 60
-                                 + date.get(Calendar.MINUTE)
-                                ) * 60 + date.get(Calendar.SECOND)
-                               ) * 1000 + date.get(Calendar.MILLISECOND)
-                              ) / ( double ) DAY_MILLISECONDS;
-            Calendar calStart = dayStart(date);
-            
-            double value = fraction + absoluteDay(calStart, use1904windowing);
-            
-            if (!use1904windowing && value >= 60) {
-                value++;
-            } else if (use1904windowing) {
-                value--;
-            }
-            
-            return value;
         }
+        // Because of daylight time saving we cannot use
+        //     date.getTime() - calStart.getTimeInMillis()
+        // as the difference in milliseconds between 00:00 and 04:00
+        // can be 3, 4 or 5 hours but Excel expects it to always
+        // be 4 hours.
+        // E.g. 2004-03-28 04:00 CEST - 2004-03-28 00:00 CET is 3 hours
+        // and 2004-10-31 04:00 CET - 2004-10-31 00:00 CEST is 5 hours
+        double fraction = (((date.get(Calendar.HOUR_OF_DAY) * 60
+                             + date.get(Calendar.MINUTE)
+                            ) * 60 + date.get(Calendar.SECOND)
+                           ) * 1000 + date.get(Calendar.MILLISECOND)
+                          ) / ( double ) DAY_MILLISECONDS;
+        Calendar calStart = dayStart(date);
+
+        double value = fraction + absoluteDay(calStart, use1904windowing);
+
+        if (!use1904windowing && value >= 60) {
+            value++;
+        } else if (use1904windowing) {
+            value--;
+        }
+
+        return value;
     }
-    
+
     /**
      *  Given an Excel date with using 1900 date windowing, and
      *   converts it to a java.util.Date.
@@ -130,13 +127,13 @@ public class DateUtil
      *  <code>Europe/Copenhagen</code>, on 2004-03-28 the minute after
      *  01:59 CET is 03:00 CEST, if the excel date represents a time between
      *  02:00 and 03:00 then it is converted to past 03:00 summer time
-     *  
+     *
      *  @param date  The Excel date.
      *  @return Java representation of the date, or null if date is not a valid Excel date
      *  @see java.util.TimeZone
      */
     public static Date getJavaDate(double date) {
-       return getJavaDate(date, false);
+        return getJavaDate(date, false);
     }
     /**
      *  Given an Excel date with either 1900 or 1904 date windowing,
@@ -158,95 +155,90 @@ public class DateUtil
      *  @see java.util.TimeZone
      */
     public static Date getJavaDate(double date, boolean use1904windowing) {
-        if (isValidExcelDate(date)) {
-            int startYear = 1900;
-            int dayAdjust = -1; // Excel thinks 2/29/1900 is a valid date, which it isn't
-            int wholeDays = (int)Math.floor(date);
-            if (use1904windowing) {
-                startYear = 1904;
-                dayAdjust = 1; // 1904 date windowing uses 1/2/1904 as the first day
-            }
-            else if (wholeDays < 61) {
-                // Date is prior to 3/1/1900, so adjust because Excel thinks 2/29/1900 exists
-                // If Excel date == 2/29/1900, will become 3/1/1900 in Java representation
-                dayAdjust = 0;
-            }
-            GregorianCalendar calendar = new GregorianCalendar(startYear,0,
-                                                     wholeDays + dayAdjust);
-            int millisecondsInDay = (int)((date - Math.floor(date)) * 
-                                          DAY_MILLISECONDS + 0.5);
-            calendar.set(GregorianCalendar.MILLISECOND, millisecondsInDay);
-            return calendar.getTime();
-        }
-        else {
+        if (!isValidExcelDate(date)) {
             return null;
         }
+        int startYear = 1900;
+        int dayAdjust = -1; // Excel thinks 2/29/1900 is a valid date, which it isn't
+        int wholeDays = (int)Math.floor(date);
+        if (use1904windowing) {
+            startYear = 1904;
+            dayAdjust = 1; // 1904 date windowing uses 1/2/1904 as the first day
+        }
+        else if (wholeDays < 61) {
+            // Date is prior to 3/1/1900, so adjust because Excel thinks 2/29/1900 exists
+            // If Excel date == 2/29/1900, will become 3/1/1900 in Java representation
+            dayAdjust = 0;
+        }
+        GregorianCalendar calendar = new GregorianCalendar(startYear,0,
+                                                 wholeDays + dayAdjust);
+        int millisecondsInDay = (int)((date - Math.floor(date)) *
+                                      DAY_MILLISECONDS + 0.5);
+        calendar.set(GregorianCalendar.MILLISECOND, millisecondsInDay);
+        return calendar.getTime();
     }
-    
+
     /**
      * Given a format ID and its format String, will check to see if the
      *  format represents a date format or not.
      * Firstly, it will check to see if the format ID corresponds to an
-     *  internal excel date format (eg most US date formats) 
+     *  internal excel date format (eg most US date formats)
      * If not, it will check to see if the format string only contains
      *  date formatting characters (ymd-/), which covers most
      *  non US date formats.
-     *  
+     *
      * @param formatIndex The index of the format, eg from ExtendedFormatRecord.getFormatIndex
      * @param formatString The format string, eg from FormatRecord.getFormatString
      * @see #isInternalDateFormat(int)
      */
     public static boolean isADateFormat(int formatIndex, String formatString) {
-       // First up, is this an internal date format?
-       if(isInternalDateFormat(formatIndex)) {
-               return true;
-       }
-       
-       // If we didn't get a real string, it can't be
-       if(formatString == null || formatString.length() == 0) {
-               return false;
-       }
-       
-       String fs = formatString;
-       
-       // Translate \- into just -, before matching
-       fs = fs.replaceAll("\\\\-","-");
-       // And \, into ,
-       fs = fs.replaceAll("\\\\,",",");
-       // And '\ ' into ' '
-       fs = fs.replaceAll("\\\\ "," ");
-       
-       // If it end in ;@, that's some crazy dd/mm vs mm/dd
-       //  switching stuff, which we can ignore
-       fs = fs.replaceAll(";@", "");
-       
-       // If it starts with [$-...], then could be a date, but
-       //  who knows what that starting bit is all about
-       fs = fs.replaceAll("^\\[\\$\\-.*?\\]", "");
-       
-       // If it starts with something like [Black] or [Yellow],
-       //  then it could be a date
-       fs = fs.replaceAll("^\\[[a-zA-Z]+\\]", "");
-       
-       // Otherwise, check it's only made up, in any case, of:
-       //  y m d h s - / , . :
-       // optionally followed by AM/PM
-       // optionally followed by AM/PM
-       if(fs.matches("^[yYmMdDhHsS\\-/,. :]+[ampAMP/]*$")) {
-               return true;
-       }
-       
-       return false;
+        // First up, is this an internal date format?
+        if(isInternalDateFormat(formatIndex)) {
+            return true;
+        }
+
+        // If we didn't get a real string, it can't be
+        if(formatString == null || formatString.length() == 0) {
+            return false;
+        }
+
+        String fs = formatString;
+
+        // Translate \- into just -, before matching
+        fs = fs.replaceAll("\\\\-","-");
+        // And \, into ,
+        fs = fs.replaceAll("\\\\,",",");
+        // And '\ ' into ' '
+        fs = fs.replaceAll("\\\\ "," ");
+
+        // If it end in ;@, that's some crazy dd/mm vs mm/dd
+        //  switching stuff, which we can ignore
+        fs = fs.replaceAll(";@", "");
+
+        // If it starts with [$-...], then could be a date, but
+        //  who knows what that starting bit is all about
+        fs = fs.replaceAll("^\\[\\$\\-.*?\\]", "");
+
+        // If it starts with something like [Black] or [Yellow],
+        //  then it could be a date
+        fs = fs.replaceAll("^\\[[a-zA-Z]+\\]", "");
+
+        // Otherwise, check it's only made up, in any case, of:
+        //  y m d h s - / , . :
+        // optionally followed by AM/PM
+        if(fs.matches("^[yYmMdDhHsS\\-/,. :]+[ampAMP/]*$")) {
+            return true;
+        }
+
+        return false;
     }
 
     /**
      * Given a format ID this will check whether the format represents
      *  an internal excel date format or not.
-     * @see #isADateFormat(int, java.lang.String)  
+     * @see #isADateFormat(int, java.lang.String)
      */
     public static boolean isInternalDateFormat(int format) {
-      boolean retval =false;
-
             switch(format) {
                 // Internal Date Formats as described on page 427 in
                 // Microsoft Excel Dev's Kit...
@@ -262,32 +254,25 @@ public class DateUtil
                 case 0x2d:
                 case 0x2e:
                 case 0x2f:
-                    retval = true;
-                    break;
-                    
-                default:
-                    retval = false;
-                    break;
+                    return true;
             }
-       return retval;
+       return false;
     }
 
     /**
      *  Check if a cell contains a date
-     *  Since dates are stored internally in Excel as double values 
-     *  we infer it is a date if it is formatted as such. 
+     *  Since dates are stored internally in Excel as double values
+     *  we infer it is a date if it is formatted as such.
      *  @see #isADateFormat(int, String)
      *  @see #isInternalDateFormat(int)
      */
     public static boolean isCellDateFormatted(Cell cell) {
         if (cell == null) return false;
         boolean bDate = false;
-        
+
         double d = cell.getNumericCellValue();
         if ( DateUtil.isValidExcelDate(d) ) {
             CellStyle style = cell.getCellStyle();
-            if(style == null) return false;
-            
             int i = style.getDataFormat();
             String f = style.getDataFormatString();
             bDate = isADateFormat(i, f);
@@ -305,7 +290,7 @@ public class DateUtil
     public static boolean isCellInternalDateFormatted(Cell cell) {
         if (cell == null) return false;
         boolean bDate = false;
-        
+
         double d = cell.getNumericCellValue();
         if ( DateUtil.isValidExcelDate(d) ) {
             CellStyle style = cell.getCellStyle();
@@ -335,7 +320,6 @@ public class DateUtil
      * @param  cal the Calendar
      * @exception IllegalArgumentException if date is invalid
      */
-
     protected static int absoluteDay(Calendar cal, boolean use1904windowing)
     {
         return cal.get(Calendar.DAY_OF_YEAR)
@@ -347,7 +331,7 @@ public class DateUtil
      *
      * @return    days  number of days in years prior to yr.
      * @param     yr    a year (1900 < yr < 4000)
-     * @param use1904windowing 
+     * @param use1904windowing
      * @exception IllegalArgumentException if year is outside of range.
      */
 
@@ -356,16 +340,16 @@ public class DateUtil
         if ((!use1904windowing && yr < 1900) || (use1904windowing && yr < 1900)) {
             throw new IllegalArgumentException("'year' must be 1900 or greater");
         }
-        
+
         int yr1  = yr - 1;
         int leapDays =   yr1 / 4   // plus julian leap days in prior years
                        - yr1 / 100 // minus prior century years
-                       + yr1 / 400 // plus years divisible by 400 
+                       + yr1 / 400 // plus years divisible by 400
                        - 460;      // leap days in previous 1900 years
-        
+
         return 365 * (yr - (use1904windowing ? 1904 : 1900)) + leapDays;
     }
-    
+
     // set HH:MM:SS fields of cal to 00:00:00:000
     private static Calendar dayStart(final Calendar cal)
     {
@@ -380,5 +364,95 @@ public class DateUtil
         return cal;
     }
 
-    // ---------------------------------------------------------------------------------------------------------
+
+    private static final class FormatException extends Exception {
+        public FormatException(String msg) {
+            super(msg);
+        }
+    }
+
+    /**
+     * Converts a string of format "HH:MM" or "HH:MM:SS" to its (Excel) numeric equivalent
+     *
+     * @return a double between 0 and 1 representing the fraction of the day
+     */
+    public static double convertTime(String timeStr) {
+        try {
+            return convertTimeInternal(timeStr);
+        } catch (FormatException e) {
+            String msg = "Bad time format '" + timeStr
+                + "' expected 'HH:MM' or 'HH:MM:SS' - " + e.getMessage();
+            throw new IllegalArgumentException(msg);
+        }
+    }
+    private static double convertTimeInternal(String timeStr) throws FormatException {
+        int len = timeStr.length();
+        if (len < 4 || len > 8) {
+            throw new FormatException("Bad length");
+        }
+        String[] parts = TIME_SEPARATOR_PATTERN.split(timeStr);
+
+        String secStr;
+        switch (parts.length) {
+            case 2: secStr = "00"; break;
+            case 3: secStr = parts[2]; break;
+            default:
+                throw new FormatException("Expected 2 or 3 fields but got (" + parts.length + ")");
+        }
+        String hourStr = parts[0];
+        String minStr = parts[1];
+        int hours = parseInt(hourStr, "hour", HOURS_PER_DAY);
+        int minutes = parseInt(minStr, "minute", MINUTES_PER_HOUR);
+        int seconds = parseInt(secStr, "second", SECONDS_PER_MINUTE);
+
+        double totalSeconds = seconds + (minutes + (hours) * 60) * 60;
+        return totalSeconds / (SECONDS_PER_DAY);
+    }
+    /**
+     * Converts a string of format "YYYY/MM/DD" to its (Excel) numeric equivalent
+     *
+     * @return a double representing the (integer) number of days since the start of the Excel epoch
+     */
+    public static Date parseYYYYMMDDDate(String dateStr) {
+        try {
+            return parseYYYYMMDDDateInternal(dateStr);
+        } catch (FormatException e) {
+            String msg = "Bad time format " + dateStr
+                + " expected 'YYYY/MM/DD' - " + e.getMessage();
+            throw new IllegalArgumentException(msg);
+        }
+    }
+    private static Date parseYYYYMMDDDateInternal(String timeStr) throws FormatException {
+        if(timeStr.length() != 10) {
+            throw new FormatException("Bad length");
+        }
+
+        String yearStr = timeStr.substring(0, 4);
+        String monthStr = timeStr.substring(5, 7);
+        String dayStr = timeStr.substring(8, 10);
+        int year = parseInt(yearStr, "year", Short.MIN_VALUE, Short.MAX_VALUE);
+        int month = parseInt(monthStr, "month", 1, 12);
+        int day = parseInt(dayStr, "day", 1, 31);
+
+        Calendar cal = new GregorianCalendar(year, month-1, day, 0, 0, 0);
+        cal.set(Calendar.MILLISECOND, 0);
+        return cal.getTime();
+    }
+    private static int parseInt(String strVal, String fieldName, int rangeMax) throws FormatException {
+        return parseInt(strVal, fieldName, 0, rangeMax-1);
+    }
+
+    private static int parseInt(String strVal, String fieldName, int lowerLimit, int upperLimit) throws FormatException {
+        int result;
+        try {
+            result = Integer.parseInt(strVal);
+        } catch (NumberFormatException e) {
+            throw new FormatException("Bad int format '" + strVal + "' for " + fieldName + " field");
+        }
+        if (result < lowerLimit || result > upperLimit) {
+            throw new FormatException(fieldName + " value (" + result
+                    + ") is outside the allowable range(0.." + upperLimit + ")");
+        }
+        return result;
+    }
 }
diff --git a/src/java/org/apache/poi/ss/util/CellRangeAddress.java b/src/java/org/apache/poi/ss/util/CellRangeAddress.java
new file mode 100644 (file)
index 0000000..e6534b3
--- /dev/null
@@ -0,0 +1,167 @@
+/* ====================================================================
+   Copyright 2002-2004   Apache Software Foundation
+
+   Licensed 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.ss.util;
+
+import org.apache.poi.hssf.record.SelectionRecord;
+import org.apache.poi.util.LittleEndian;
+
+/**
+ * See OOO documentation: excelfileformat.pdf sec 2.5.14 - 'Cell Range Address'<p/>
+ * 
+ * Note - {@link SelectionRecord} uses the BIFF5 version of this structure
+ * @author Dragos Buleandra (dragos.buleandra@trade2b.ro)
+ */
+public class CellRangeAddress {
+       /*
+        * TODO - replace  org.apache.poi.hssf.util.Region
+        */
+       public static final int ENCODED_SIZE = 8;
+
+       /** max 65536 rows in BIFF8 */
+       public static final int LAST_ROW_INDEX = 0x00FFFF; 
+       /** max 256 columns in BIFF8 */
+       public static final int LAST_COLUMN_INDEX = 0x00FF;
+       
+       
+       protected int _firstRow;
+       protected int _firstCol;
+       protected int _lastRow;
+       protected int _lastCol;
+
+       protected CellRangeAddress() {}
+       public CellRangeAddress(int firstRow, int lastRow, int firstCol, int lastCol) {
+               if(!isValid(firstRow, lastRow, firstCol, lastCol)) {
+                       throw new IllegalArgumentException("invalid cell range (" + firstRow + ", " + lastRow 
+                                       + ", " + firstCol + ", " + lastCol + ")");
+               }
+               _firstRow = firstRow;
+               _lastRow = convertM1ToMax(lastRow, LAST_ROW_INDEX);
+               _firstCol = firstCol;
+               _lastCol = convertM1ToMax(lastCol, LAST_COLUMN_INDEX);
+       }
+
+       private static boolean isValid(int firstRow, int lastRow, int firstColumn, int lastColumn)
+       {
+               if(lastRow < 0 || lastRow > LAST_ROW_INDEX) {
+                       return false;
+               }
+               if(firstRow < 0 || firstRow > LAST_ROW_INDEX) {
+                       return false;
+               }
+               
+               if(lastColumn < 0 || lastColumn > LAST_COLUMN_INDEX) {
+                       return false;
+               }
+               if(firstColumn < 0 || firstColumn > LAST_COLUMN_INDEX) {
+                       return false;
+               }
+               return true;
+       }
+       /** 
+        * Range arithmetic is easier when using a large positive number for 'max row or column' 
+        * instead of <tt>-1</tt>. 
+        */
+       private static int convertM1ToMax(int lastIx, int maxIndex) {
+               if(lastIx < 0) {
+                       return maxIndex;
+               }
+               return lastIx;
+       }
+
+       public boolean isFullColumnRange() {
+               return _firstRow == 0 && _lastRow == LAST_ROW_INDEX;
+       }
+       public boolean isFullRowRange() {
+               return _firstCol == 0 && _lastCol == LAST_COLUMN_INDEX;
+       }
+
+       /**
+        * @return column number for the upper left hand corner
+        */
+       public int getFirstColumn() {
+               return _firstCol;
+       }
+
+       /**
+        * @return row number for the upper left hand corner
+        */
+       public int getFirstRow() {
+               return _firstRow;
+       }
+
+       /**
+        * @return column number for the lower right hand corner
+        */
+       public int getLastColumn() {
+               return _lastCol;
+       }
+
+       /**
+        * @return row number for the lower right hand corner
+        */
+       public int getLastRow() {
+               return _lastRow;
+       }
+
+       /**
+        * @param _firstCol column number for the upper left hand corner
+        */
+       public void setFirstColumn(int firstCol) {
+               _firstCol = firstCol;
+       }
+
+       /**
+        * @param rowFrom row number for the upper left hand corner
+        */
+       public void setFirstRow(int firstRow) {
+               _firstRow = firstRow;
+       }
+
+       /**
+        * @param colTo column number for the lower right hand corner
+        */
+       public void setLastColumn(int lastCol) {
+               _lastCol = lastCol;
+       }
+
+       /**
+        * @param rowTo row number for the lower right hand corner
+        */
+       public void setLastRow(int lastRow) {
+               _lastRow = lastRow;
+       }
+
+       public CellRangeAddress copy() {
+               return new CellRangeAddress(_firstRow, _lastRow, _firstCol, _lastCol);
+       }
+
+       public static int getEncodedSize(int numberOfItems) {
+               return numberOfItems * ENCODED_SIZE;
+       }
+       
+       public String toString() {
+               return getClass().getName() + " ["+_firstRow+", "+_lastRow+", "+_firstCol+", "+_lastCol+"]";
+       }
+
+       public int serialize(int offset, byte[] data) {
+               LittleEndian.putUShort(data, offset + 0, _firstRow);
+               LittleEndian.putUShort(data, offset + 2, _lastRow);
+               LittleEndian.putUShort(data, offset + 4, _firstCol);
+               LittleEndian.putUShort(data, offset + 6, _lastCol);
+               return ENCODED_SIZE;
+       }
+}
diff --git a/src/java/org/apache/poi/ss/util/CellRangeAddressList.java b/src/java/org/apache/poi/ss/util/CellRangeAddressList.java
new file mode 100644 (file)
index 0000000..8773c34
--- /dev/null
@@ -0,0 +1,132 @@
+/* ====================================================================
+   Copyright 2002-2004   Apache Software Foundation
+
+   Licensed 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.ss.util;
+
+import java.util.ArrayList;
+import java.util.List;
+import org.apache.poi.util.LittleEndian;
+
+/**
+ * Implementation of the cell range address lists,like is described
+ * in OpenOffice.org's Excel Documentation: excelfileformat.pdf sec 2.5.14 -
+ * 'Cell Range Address List'
+ * 
+ * In BIFF8 there is a common way to store absolute cell range address lists in
+ * several records (not formulas). A cell range address list consists of a field
+ * with the number of ranges and the list of the range addresses. Each cell
+ * range address (called an ADDR structure) contains 4 16-bit-values.
+ * </p>
+ * 
+ * @author Dragos Buleandra (dragos.buleandra@trade2b.ro)
+ */
+public class CellRangeAddressList {
+
+       /**
+        * List of <tt>CellRangeAddress</tt>es. Each structure represents a cell range
+        */
+       protected final List _list;
+
+       public CellRangeAddressList() {
+               _list = new ArrayList();
+       }
+       /**
+        * Convenience constructor for creating a <tt>CellRangeAddressList</tt> with a single 
+        * <tt>CellRangeAddress</tt>.  Other <tt>CellRangeAddress</tt>es may be added later.
+        */
+       public CellRangeAddressList(int firstRow, int lastRow, int firstCol, int lastCol) {
+               this();
+               addCellRangeAddress(firstRow, firstCol, lastRow, lastCol);
+       }
+
+       /**
+        * Get the number of following ADDR structures. The number of this
+        * structures is automatically set when reading an Excel file and/or
+        * increased when you manually add a new ADDR structure . This is the reason
+        * there isn't a set method for this field .
+        * 
+        * @return number of ADDR structures
+        */
+       public int countRanges() {
+               return _list.size();
+       }
+
+       /**
+        * Add a cell range structure.
+        * 
+        * @param firstRow - the upper left hand corner's row
+        * @param firstCol - the upper left hand corner's col
+        * @param lastRow - the lower right hand corner's row
+        * @param lastCol - the lower right hand corner's col
+        * @return the index of this ADDR structure
+        */
+       public void addCellRangeAddress(int firstRow, int firstCol, int lastRow, int lastCol) {
+               CellRangeAddress region = new CellRangeAddress(firstRow, lastRow, firstCol, lastCol);
+               addCellRangeAddress(region);
+       }
+       public void addCellRangeAddress(CellRangeAddress cra) {
+               _list.add(cra);
+       }
+       public CellRangeAddress remove(int rangeIndex) {
+               if (_list.isEmpty()) {
+                       throw new RuntimeException("List is empty");
+               }
+               if (rangeIndex < 0 || rangeIndex >= _list.size()) {
+                       throw new RuntimeException("Range index (" + rangeIndex 
+                                       + ") is outside allowable range (0.." + (_list.size()-1) + ")");
+               }
+               return (CellRangeAddress) _list.remove(rangeIndex);
+       }
+
+       /**
+        * @return <tt>CellRangeAddress</tt> at the given index
+        */
+       public CellRangeAddress getCellRangeAddress(int index) {
+               return (CellRangeAddress) _list.get(index);
+       }
+
+       public int getSize() {
+               return 2 + CellRangeAddress.getEncodedSize(_list.size());
+       }
+
+       public int serialize(int offset, byte[] data) {
+               int pos = 2;
+
+               int nItems = _list.size();
+               LittleEndian.putUShort(data, offset, nItems);
+               for (int k = 0; k < nItems; k++) {
+                       CellRangeAddress region = (CellRangeAddress) _list.get(k);
+                       pos += region.serialize(offset + pos, data);
+               }
+               return getSize();
+       }
+
+       public CellRangeAddressList copy() {
+               CellRangeAddressList result = new CellRangeAddressList();
+               
+               int nItems = _list.size();
+               for (int k = 0; k < nItems; k++) {
+                       CellRangeAddress region = (CellRangeAddress) _list.get(k);
+                       result.addCellRangeAddress(region.copy());
+               }
+               return result;
+       }
+       public CellRangeAddress[] getCellRangeAddresses() {
+               CellRangeAddress[] result = new CellRangeAddress[_list.size()];
+               _list.toArray(result);
+               return result;
+       }
+}
index 71dd31403b120b3da53b96482ceb1a0f955c3771..1ee64c5d024085cdb911db9e88b79a55bc9ed3cf 100644 (file)
    limitations under the License.
 ==================================================================== */
 
-
 package org.apache.poi.ss.util;
 
-import org.apache.poi.hssf.record.MergeCellsRecord.MergedRegion;
 
 /**
  * Represents a from/to row/col square.  This is a object primitive
@@ -26,11 +24,9 @@ import org.apache.poi.hssf.record.MergeCellsRecord.MergedRegion;
  * to represent a string of characters.  Its really only useful for HSSF though.
  *
  * @author  Andrew C. Oliver acoliver at apache dot org
+ * @deprecated (Aug-2008) use {@link CellRangeAddress}
  */
-
-public class Region
-    implements Comparable
-{
+public class Region implements Comparable {
     private int   rowFrom;
     private short colFrom;
     private int   rowTo;
@@ -52,16 +48,6 @@ public class Region
         this.colTo   = colTo;
     }
 
-    /**
-     * special constructor (I know this is bad but it is so wrong that its right
-     * okay) that makes a region from a mergedcells's region subrecord.
-     */
-
-    public Region(MergedRegion region)
-    {
-        this(region.row_from, region.col_from, region.row_to, region.col_to);
-    }
-
     public Region(String ref) {
        CellReference cellReferenceFrom = new CellReference(ref.substring(0, ref.indexOf(":")));
        CellReference cellReferenceTo = new CellReference(ref.substring(ref.indexOf(":") + 1));
@@ -71,7 +57,8 @@ public class Region
        this.colTo = (short) cellReferenceTo.getCol();
        }
 
-       /**
+
+    /**
      * get the upper left hand corner column number
      *
      * @return column number for the upper left hand corner
@@ -159,6 +146,7 @@ public class Region
         this.rowTo = rowTo;
     }
 
+
     /**
      * Answers: "is the row/column inside this range?"
      *
@@ -218,16 +206,51 @@ public class Region
         return compareTo(( Region ) o);
     }
 
-    /**
-     * @return the area contained by this region (number of cells)
-     */
+       /**
+        * Convert a List of CellRange objects to an array of regions 
+        *  
+        * @param List of CellRange objects
+        * @return regions
+        */
+       public static Region[] convertCellRangesToRegions(CellRangeAddress[] cellRanges) {
+               int size = cellRanges.length;
+               if(size < 1) {
+                       return new Region[0];
+               }
+               
+               Region[] result = new Region[size];
+
+               for (int i = 0; i != size; i++) {
+                       result[i] = convertToRegion(cellRanges[i]);
+               }
+               return result;
+       }
+
+
+               
+       private static Region convertToRegion(CellRangeAddress cr) {
+               
+               return new Region(cr.getFirstRow(), (short)cr.getFirstColumn(), cr.getLastRow(), (short)cr.getLastColumn());
+       }
+
+       public static CellRangeAddress[] convertRegionsToCellRanges(Region[] regions) {
+               int size = regions.length;
+               if(size < 1) {
+                       return new CellRangeAddress[0];
+               }
+               
+               CellRangeAddress[] result = new CellRangeAddress[size];
+
+               for (int i = 0; i != size; i++) {
+                       result[i] = convertToCellRangeAddress(regions[i]);
+               }
+               return result;
+       }
+
+       public static CellRangeAddress convertToCellRangeAddress(Region r) {
+               return new CellRangeAddress(r.getRowFrom(), r.getRowTo(), r.getColumnFrom(), r.getColumnTo());
+       }
 
-    public int getArea()
-    {
-        return ((1 + (getRowTo() - getRowFrom()))
-                * (1 + (getColumnTo() - getColumnFrom())));
-    }
-    
     /**
      * @return the string reference for this region
      */
diff --git a/src/scratchpad/examples/src/org/apache/poi/hslf/examples/HeadersFootersDemo.java b/src/scratchpad/examples/src/org/apache/poi/hslf/examples/HeadersFootersDemo.java
new file mode 100644 (file)
index 0000000..3ebcecc
--- /dev/null
@@ -0,0 +1,51 @@
+/* ====================================================================
+   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.hslf.examples;
+
+import org.apache.poi.hslf.usermodel.SlideShow;
+import org.apache.poi.hslf.model.HeadersFooters;
+import org.apache.poi.hslf.model.Slide;
+
+import java.io.FileOutputStream;
+
+/**
+ * Demonstrates how to set headers / footers
+ *
+ * @author Yegor Kozlov
+ */
+public class HeadersFootersDemo {
+    public static void main(String[] args) throws Exception {
+        SlideShow ppt = new SlideShow();
+        
+        HeadersFooters slideHeaders = ppt.getSlideHeadersFooters();
+        slideHeaders.setFootersText("Created by POI-HSLF");
+        slideHeaders.setSlideNumberVisible(true);
+        slideHeaders.setDateTimeText("custom date time");
+
+        HeadersFooters notesHeaders = ppt.getNotesHeadersFooters();
+        notesHeaders.setFootersText("My notes footers");
+        notesHeaders.setHeaderText("My notes header");
+
+        Slide slide = ppt.createSlide();
+
+        FileOutputStream out = new FileOutputStream("headers_footers.ppt");
+        ppt.write(out);
+        out.close();
+
+    }
+
+}
diff --git a/src/scratchpad/src/org/apache/poi/hslf/model/HeadersFooters.java b/src/scratchpad/src/org/apache/poi/hslf/model/HeadersFooters.java
new file mode 100644 (file)
index 0000000..e94bcb9
--- /dev/null
@@ -0,0 +1,226 @@
+\r
+/* ====================================================================\r
+   Licensed to the Apache Software Foundation (ASF) under one or more\r
+   contributor license agreements.  See the NOTICE file distributed with\r
+   this work for additional information regarding copyright ownership.\r
+   The ASF licenses this file to You under the Apache License, Version 2.0\r
+   (the "License"); you may not use this file except in compliance with\r
+   the License.  You may obtain a copy of the License at\r
+\r
+       http://www.apache.org/licenses/LICENSE-2.0\r
+\r
+   Unless required by applicable law or agreed to in writing, software\r
+   distributed under the License is distributed on an "AS IS" BASIS,\r
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+   See the License for the specific language governing permissions and\r
+   limitations under the License.\r
+==================================================================== */\r
+package org.apache.poi.hslf.model;\r
+\r
+import org.apache.poi.hslf.record.*;\r
+import org.apache.poi.hslf.usermodel.SlideShow;\r
+\r
+/**\r
+ * Header / Footer settings.\r
+ *\r
+ * @author Yegor Kozlov\r
+ */\r
+public class HeadersFooters {\r
+\r
+    private HeadersFootersContainer _container;\r
+    private boolean _newRecord;\r
+    private SlideShow _ppt;\r
+\r
+    public HeadersFooters(HeadersFootersContainer rec, SlideShow ppt, boolean newRecord){\r
+        _container = rec;\r
+        _newRecord = newRecord;\r
+        _ppt = ppt;\r
+    }\r
+\r
+    /**\r
+     * Headers's text\r
+     *\r
+     * @return Headers's text\r
+     */\r
+    public String getHeaderText(){\r
+       CString cs = _container.getHeaderAtom();\r
+       return cs == null ? null : cs.getText();\r
+    }\r
+\r
+    /**\r
+     * Sets headers's text\r
+     *\r
+     * @param text headers's text\r
+     */\r
+    public void setHeaderText(String text){\r
+        if(_newRecord) attach();\r
+\r
+        setHeaderVisible(true);\r
+        CString cs = _container.getHeaderAtom();\r
+        if(cs == null) cs = _container.addHeaderAtom();\r
+\r
+        cs.setText(text);\r
+    }\r
+\r
+    /**\r
+     * Footer's text\r
+     *\r
+     * @return Footer's text\r
+     */\r
+    public String getFooterText(){\r
+        CString cs = _container.getFooterAtom();\r
+        return cs == null ? null : cs.getText();\r
+    }\r
+\r
+    /**\r
+     * Sets footers's text\r
+     *\r
+     * @param text footers's text\r
+     */\r
+    public void setFootersText(String text){\r
+        if(_newRecord) attach();\r
+\r
+        setFooterVisible(true);\r
+        CString cs = _container.getFooterAtom();\r
+        if(cs == null) cs = _container.addFooterAtom();\r
+\r
+        cs.setText(text);\r
+    }\r
+\r
+    /**\r
+     * This is the date that the user wants in the footers, instead of today's date.\r
+     *\r
+     * @return custom user date\r
+     */\r
+    public String getDateTimeText(){\r
+        CString cs = _container.getUserDateAtom();\r
+        return cs == null ? null : cs.getText();\r
+    }\r
+\r
+    /**\r
+     * Sets custom user date to be displayed instead of today's date.\r
+     *\r
+     * @param text custom user date\r
+     */\r
+    public void setDateTimeText(String text){\r
+        if(_newRecord) attach();\r
+\r
+        setUserDateVisible(true);\r
+        setDateTimeVisible(true);\r
+        CString cs = _container.getUserDateAtom();\r
+        if(cs == null) cs = _container.addUserDateAtom();\r
+\r
+        cs.setText(text);\r
+    }\r
+\r
+    /**\r
+     * whether the footer text is displayed.\r
+     */\r
+    public boolean isFooterVisible(){\r
+        return _container.getHeadersFootersAtom().getFlag(HeadersFootersAtom.fHasFooter);\r
+    }\r
+\r
+    /**\r
+     * whether the footer text is displayed.\r
+     */\r
+    public void setFooterVisible(boolean flag){\r
+        if(_newRecord) attach();\r
+        _container.getHeadersFootersAtom().setFlag(HeadersFootersAtom.fHasFooter, flag);\r
+    }\r
+\r
+    /**\r
+     * whether the header text is displayed.\r
+     */\r
+    public boolean isHeaderVisible(){\r
+        return _container.getHeadersFootersAtom().getFlag(HeadersFootersAtom.fHasHeader);\r
+    }\r
+\r
+    /**\r
+     * whether the header text is displayed.\r
+     */\r
+    public void setHeaderVisible(boolean flag){\r
+        if(_newRecord) attach();\r
+        _container.getHeadersFootersAtom().setFlag(HeadersFootersAtom.fHasHeader, flag);\r
+    }\r
+\r
+    /**\r
+     * whether the date is displayed in the footer.\r
+     */\r
+    public boolean isDateTimeVisible(){\r
+        return _container.getHeadersFootersAtom().getFlag(HeadersFootersAtom.fHasDate);\r
+    }\r
+\r
+    /**\r
+     * whether the date is displayed in the footer.\r
+     */\r
+    public void setDateTimeVisible(boolean flag){\r
+        if(_newRecord) attach();\r
+        _container.getHeadersFootersAtom().setFlag(HeadersFootersAtom.fHasDate, flag);\r
+    }\r
+\r
+    /**\r
+     * whether the custom user date is used instead of today's date.\r
+     */\r
+    public boolean isUserDateVisible(){\r
+        return _container.getHeadersFootersAtom().getFlag(HeadersFootersAtom.fHasUserDate);\r
+    }\r
+\r
+    /**\r
+     * whether the date is displayed in the footer.\r
+     */\r
+    public void setUserDateVisible(boolean flag){\r
+        if(_newRecord) attach();\r
+        _container.getHeadersFootersAtom().setFlag(HeadersFootersAtom.fHasUserDate, flag);\r
+    }\r
+\r
+    /**\r
+     * whether the slide number is displayed in the footer.\r
+     */\r
+    public boolean isSlideNumberVisible(){\r
+        return _container.getHeadersFootersAtom().getFlag(HeadersFootersAtom.fHasSlideNumber);\r
+    }\r
+\r
+    /**\r
+     * whether the slide number is displayed in the footer.\r
+     */\r
+    public void setSlideNumberVisible(boolean flag){\r
+        if(_newRecord) attach();\r
+        _container.getHeadersFootersAtom().setFlag(HeadersFootersAtom.fHasSlideNumber, flag);\r
+    }\r
+\r
+    /**\r
+     *  An integer that specifies the format ID to be used to style the datetime.\r
+     *\r
+     * @return an integer that specifies the format ID to be used to style the datetime.\r
+     */\r
+    public int getDateTimeFormat(){\r
+        return _container.getHeadersFootersAtom().getFormatId();\r
+    }\r
+\r
+    /**\r
+     *  An integer that specifies the format ID to be used to style the datetime.\r
+     *\r
+     * @param formatId an integer that specifies the format ID to be used to style the datetime.\r
+     */\r
+    public void setDateTimeFormat(int formatId){\r
+        if(_newRecord) attach();\r
+        _container.getHeadersFootersAtom().setFormatId(formatId);\r
+    }\r
+\r
+    /**\r
+     * Attach this HeadersFootersContainer to the parent Document record\r
+     */\r
+    private void attach(){\r
+        Document doc = _ppt.getDocumentRecord();\r
+        Record[] ch = doc.getChildRecords();\r
+        Record lst = null;\r
+        for (int i=0; i < ch.length; i++){\r
+            if(ch[i].getRecordType() == RecordTypes.List.typeID){\r
+                lst = ch[i];\r
+                break;\r
+            }\r
+        }\r
+        doc.addChildAfter(_container, lst);\r
+        _newRecord = false;\r
+    }\r
+}\r
index ee32868eb6353020659d5c889f7013ffcbdf41a2..670a86655a4465de55746ea2384b6997561a907d 100644 (file)
@@ -24,9 +24,7 @@ import java.util.Vector;
 import java.util.Iterator;
 import java.awt.*;
 
-import org.apache.poi.hslf.record.SlideAtom;
-import org.apache.poi.hslf.record.TextHeaderAtom;
-import org.apache.poi.hslf.record.ColorSchemeAtom;
+import org.apache.poi.hslf.record.*;
 import org.apache.poi.hslf.record.SlideListWithText.SlideAtomsSet;
 import org.apache.poi.ddf.EscherDggRecord;
 import org.apache.poi.ddf.EscherContainerRecord;
@@ -381,4 +379,22 @@ public class Slide extends Sheet
         }
     }
 
+    /**
+     * Header / Footer settings for this slide.  
+     *
+     * @return Header / Footer settings for this slide
+     */
+     public HeadersFooters getHeadersFooters(){
+        HeadersFootersContainer hdd = null;
+        Record[] ch = getSheetContainer().getChildRecords();
+        for (int i = 0; i < ch.length; i++) {
+            if(ch[i] instanceof HeadersFootersContainer){
+                hdd = (HeadersFootersContainer)ch[i];
+                break;
+            }
+        }
+        boolean newRecord = false;
+        if(hdd == null) return getSlideShow().getSlideHeadersFooters();
+        else return new HeadersFooters(hdd, getSlideShow(), newRecord);
+    }
 }
index b17efc40bd42a6ec7d8586e93c11b1c75ac38c88..e7e46baa00b2409f0e0ee289770e234f09091dc4 100644 (file)
@@ -56,7 +56,7 @@ public class CString extends RecordAtom {
         * Grabs the count, from the first two bytes of the header.
         * The meaning of the count is specific to the type of the parent record 
         */
-       public int getCount() {
+       public int getOptions() {
                return (int)LittleEndian.getShort(_header);
        }
        
@@ -64,7 +64,7 @@ public class CString extends RecordAtom {
         * Sets the count
         * The meaning of the count is specific to the type of the parent record 
         */
-       public void setCount(int count) {
+       public void setOptions(int count) {
                LittleEndian.putShort(_header, (short)count);
        }
 
index 91ee8ea3a23fa1ae6a4e17a484f907499fd95e6a..31ef11a66c6ef8c7a86ca629278cac62f63c65d2 100644 (file)
@@ -140,9 +140,9 @@ public class Comment2000 extends RecordContainer {
                CString csa = new CString();
                CString csb = new CString();
                CString csc = new CString();
-               csa.setCount(0x00);
-               csb.setCount(0x10);
-               csc.setCount(0x20);
+               csa.setOptions(0x00);
+               csb.setOptions(0x10);
+               csc.setOptions(0x20);
                _children[0] = csa;
                _children[1] = csb;
                _children[2] = csc;
index e0810dbca1d9bdd80f5e7797002c28565a28e44b..fb669b38ead748710de7a5b14eafc2e0d6541d6e 100644 (file)
@@ -25,6 +25,7 @@ import org.apache.poi.poifs.filesystem.*;
 import org.apache.poi.util.LittleEndian;
 import org.apache.poi.util.StringUtil;
 import org.apache.poi.hslf.exceptions.CorruptPowerPointFileException;
+import org.apache.poi.hslf.exceptions.EncryptedPowerPointFileException;
 
 
 /**
@@ -39,14 +40,15 @@ public class CurrentUserAtom
 {
        /** Standard Atom header */
        public static final byte[] atomHeader = new byte[] { 0, 0, -10, 15 };
-       /** The Powerpoint magic numer */
-       public static final byte[] magicNumber = new byte[] { 95, -64, -111, -29 };
+       /** The PowerPoint magic number for a non-encrypted file */
+       public static final byte[] headerToken = new byte[] { 95, -64, -111, -29 };
+       /** The PowerPoint magic number for an encrytpted file */ 
+       public static final byte[] encHeaderToken = new byte[] { -33, -60, -47, -13 };
        /** The Powerpoint 97 version, major and minor numbers */
        public static final byte[] ppt97FileVer = new byte[] { 8, 00, -13, 03, 03, 00 };
 
        /** The version, major and minor numbers */
-       private int docFinalVersionA;
-       private int docFinalVersionB;
+       private int docFinalVersion;
        private byte docMajorNo;
        private byte docMinorNo;
 
@@ -54,7 +56,7 @@ public class CurrentUserAtom
     private long currentEditOffset;
        /** The Username of the last person to edit the file */
        private String lastEditUser;
-       /** The document release version */
+       /** The document release version. Almost always 8 */
        private long releaseVersion;
 
        /** Only correct after reading in or writing out */
@@ -63,8 +65,7 @@ public class CurrentUserAtom
 
        /* ********************* getter/setter follows *********************** */
 
-       public int  getDocFinalVersionA() { return docFinalVersionA; }
-       public int  getDocFinalVersionB() { return docFinalVersionB; }
+       public int  getDocFinalVersion() { return docFinalVersion; }
        public byte getDocMajorNo()       { return docMajorNo; }
        public byte getDocMinorNo()       { return docMinorNo; }
 
@@ -86,7 +87,14 @@ public class CurrentUserAtom
         */
        public CurrentUserAtom() {
                _contents = new byte[0];
-               throw new RuntimeException("Creation support for Current User Atom not complete");
+
+               // Initialise to empty
+               docFinalVersion = 0x03f4;
+               docMajorNo = 3;
+               docMinorNo = 0;
+               releaseVersion = 8;
+               currentEditOffset = 0;
+               lastEditUser = "Apache POI";
        }
 
        /** 
@@ -130,12 +138,20 @@ public class CurrentUserAtom
         * Actually do the creation from a block of bytes
         */
        private void init() {
+               // First up is the size, in 4 bytes, which is fixed
+               // Then is the header - check for encrypted
+               if(_contents[12] == encHeaderToken[0] && 
+                       _contents[13] == encHeaderToken[1] &&
+                       _contents[14] == encHeaderToken[2] &&
+                       _contents[15] == encHeaderToken[3]) {
+                       throw new EncryptedPowerPointFileException("The CurrentUserAtom specifies that the document is encrypted");
+               }
+               
                // Grab the edit offset
                currentEditOffset = LittleEndian.getUInt(_contents,16);
 
                // Grab the versions
-               docFinalVersionA = LittleEndian.getUShort(_contents,20);
-               docFinalVersionB = LittleEndian.getUShort(_contents,22);
+               docFinalVersion = LittleEndian.getUShort(_contents,22);
                docMajorNo = _contents[24];
                docMinorNo = _contents[25];
 
@@ -194,15 +210,22 @@ public class CurrentUserAtom
                // Now we have the size of the details, which is 20
                LittleEndian.putInt(_contents,8,20);
 
-               // Now the ppt magic number (4 bytes)
-               System.arraycopy(magicNumber,0,_contents,12,4);
+               // Now the ppt un-encrypted header token (4 bytes)
+               System.arraycopy(headerToken,0,_contents,12,4);
 
                // Now the current edit offset
                LittleEndian.putInt(_contents,16,(int)currentEditOffset);
 
-               // Now the file versions, 2+2+1+1
-               LittleEndian.putShort(_contents,20,(short)docFinalVersionA);
-               LittleEndian.putShort(_contents,22,(short)docFinalVersionB);
+               // The username gets stored twice, once as US 
+               //  ascii, and again as unicode laster on
+               byte[] asciiUN = new byte[lastEditUser.length()];
+               StringUtil.putCompressedUnicode(lastEditUser,asciiUN,0);
+               
+               // Now we're able to do the length of the last edited user
+               LittleEndian.putShort(_contents,20,(short)asciiUN.length);
+               
+               // Now the file versions, 2+1+1
+               LittleEndian.putShort(_contents,22,(short)docFinalVersion);
                _contents[24] = docMajorNo;
                _contents[25] = docMinorNo;
 
@@ -210,9 +233,7 @@ public class CurrentUserAtom
                _contents[26] = 0;
                _contents[27] = 0;
 
-               // username in bytes in us ascii
-               byte[] asciiUN = new byte[lastEditUser.length()];
-               StringUtil.putCompressedUnicode(lastEditUser,asciiUN,0);
+               // At this point we have the username as us ascii
                System.arraycopy(asciiUN,0,_contents,28,asciiUN.length);
 
                // 4 byte release version
index 501712e9d5f410f7a81e7c5d880fe5ccb60a0bea..6d61f2ef71097bb5f554eae75cd20898fbbb3e0f 100644 (file)
@@ -74,8 +74,8 @@ public class ExEmbed extends RecordContainer {
         CString cs1 = new CString();
         CString cs2 = new CString();
         CString cs3 = new CString();
-//        cs1.setCount(0x00);
-//        cs2.setCount(0x10);
+//        cs1.setOptions(0x00);
+//        cs2.setOptions(0x10);
         _children[0] = new ExEmbedAtom();
         _children[1] = new ExOleObjAtom();
         _children[2] = cs1;
index 8ba58cdb61efaef31b13eee0da202a0cebd5f01c..b0bc1e191fbc6b5d583ab4c0ffd2fe4bbda8ea30 100644 (file)
@@ -136,8 +136,8 @@ public class ExHyperlink extends RecordContainer {
                // Setup our child records
                CString csa = new CString();
                CString csb = new CString();
-               csa.setCount(0x00);
-               csb.setCount(0x10);
+               csa.setOptions(0x00);
+               csb.setOptions(0x10);
                _children[0] = new ExHyperlinkAtom();
                _children[1] = csa;
                _children[2] = csb;
diff --git a/src/scratchpad/src/org/apache/poi/hslf/record/HeadersFootersAtom.java b/src/scratchpad/src/org/apache/poi/hslf/record/HeadersFootersAtom.java
new file mode 100644 (file)
index 0000000..a9457a6
--- /dev/null
@@ -0,0 +1,207 @@
+\r
+/* ====================================================================\r
+   Licensed to the Apache Software Foundation (ASF) under one or more\r
+   contributor license agreements.  See the NOTICE file distributed with\r
+   this work for additional information regarding copyright ownership.\r
+   The ASF licenses this file to You under the Apache License, Version 2.0\r
+   (the "License"); you may not use this file except in compliance with\r
+   the License.  You may obtain a copy of the License at\r
+\r
+       http://www.apache.org/licenses/LICENSE-2.0\r
+\r
+   Unless required by applicable law or agreed to in writing, software\r
+   distributed under the License is distributed on an "AS IS" BASIS,\r
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+   See the License for the specific language governing permissions and\r
+   limitations under the License.\r
+==================================================================== */\r
+        \r
+\r
+package org.apache.poi.hslf.record;\r
+\r
+import org.apache.poi.util.LittleEndian;\r
+import java.io.IOException;\r
+import java.io.OutputStream;\r
+\r
+/**\r
+ * An atom record that specifies options for displaying headers and footers\r
+ * on a presentation slide or notes slide.\r
+ *\r
+ * @author Yegor Kozlov\r
+ */\r
+\r
+public class HeadersFootersAtom extends RecordAtom {\r
+\r
+    /**\r
+     * A bit that specifies whether the date is displayed in the footer.\r
+     * @see {@link #getMask()}, {@link #setMask(int)}},\r
+     */\r
+    public static final int fHasDate = 1;\r
+\r
+    /**\r
+     * A bit that specifies whether the current datetime is used for displaying the datetime.\r
+     * @see {@link #getMask()}, {@link #setMask(int)}},\r
+     */\r
+    public static final int fHasTodayDate = 2;\r
+\r
+    /**\r
+     * A bit that specifies whether the date specified in UserDateAtom record\r
+     * is used for displaying the datetime.\r
+     *\r
+     * @see {@link #getMask()}, {@link #setMask(int)}},\r
+     */\r
+     public static final int fHasUserDate = 4;\r
+\r
+    /**\r
+     * A bit that specifies whether the slide number is displayed in the footer.\r
+     * \r
+     * @see {@link #getMask()}, {@link #setMask(int)}},\r
+     */\r
+    public static final int fHasSlideNumber = 8;\r
+\r
+    /**\r
+     * bit that specifies whether the header text is displayed.\r
+     *\r
+     * @see {@link #getMask()}, {@link #setMask(int)}},\r
+     */\r
+    public static final int fHasHeader = 16;\r
+\r
+    /**\r
+     * bit that specifies whether the footer text is displayed.\r
+     *\r
+     * @see {@link #getMask()}, {@link #setMask(int)}},\r
+     */\r
+    public static final int fHasFooter = 32;\r
+\r
+    /**\r
+     * record header\r
+     */\r
+    private byte[] _header;\r
+\r
+       /**\r
+     * record data\r
+     */\r
+       private byte[] _recdata;\r
+\r
+    /**\r
+     * Build an instance of <code>HeadersFootersAtom</code> from on-disk data\r
+     */\r
+       protected HeadersFootersAtom(byte[] source, int start, int len) {\r
+               // Get the header\r
+               _header = new byte[8];\r
+               System.arraycopy(source,start,_header,0,8);\r
+\r
+               // Grab the record data\r
+               _recdata = new byte[len-8];\r
+               System.arraycopy(source,start+8,_recdata,0,len-8);\r
+       }\r
+\r
+    /**\r
+     * Create a new instance of <code>HeadersFootersAtom</code>\r
+     */\r
+    public HeadersFootersAtom() {\r
+        _recdata = new byte[4];\r
+\r
+        _header = new byte[8];\r
+        LittleEndian.putShort(_header, 2, (short)getRecordType());\r
+        LittleEndian.putInt(_header, 4, _recdata.length);\r
+    }\r
+\r
+    public long getRecordType() {\r
+        return RecordTypes.HeadersFootersAtom.typeID;\r
+    }\r
+\r
+    /**\r
+        * Write the contents of the record back, so it can be written to disk\r
+        */\r
+       public void writeOut(OutputStream out) throws IOException {\r
+               out.write(_header);\r
+               out.write(_recdata);\r
+       }\r
+\r
+    /**\r
+     * A signed integer that specifies the format ID to be used to style the datetime.\r
+     * <p>\r
+     * It MUST be in the range [0, 12]. </br>\r
+     * This value is converted into a string as specified by the index field of the DateTimeMCAtom record.\r
+     * It MUST be ignored unless fHasTodayDate is TRUE.\r
+     * </b>\r
+     *\r
+     * @return  A signed integer that specifies the format ID to be used to style the datetime.\r
+     */\r
+    public int getFormatId(){\r
+        return LittleEndian.getShort(_recdata, 0);\r
+    }\r
+\r
+    /**\r
+     * A signed integer that specifies the format ID to be used to style the datetime.\r
+     *\r
+     * @param formatId  A signed integer that specifies the format ID to be used to style the datetime.\r
+     */\r
+    public void setFormatId(int formatId){\r
+         LittleEndian.putUShort(_recdata, 0, formatId);\r
+    }\r
+\r
+    /**\r
+     *  A bit mask specifying options for displaying headers and footers\r
+     *\r
+     * <li> A - {@link #fHasDate} (1 bit): A bit that specifies whether the date is displayed in the footer.\r
+     * <li> B - {@link #fHasTodayDate} (1 bit): A bit that specifies whether the current datetime is used for\r
+     *      displaying the datetime.\r
+     * <li> C - {@link #fHasUserDate} (1 bit): A bit that specifies whether the date specified in UserDateAtom record\r
+     *      is used for displaying the datetime.\r
+     * <li> D - {@link #fHasSlideNumber} (1 bit): A bit that specifies whether the slide number is displayed in the footer.\r
+     * <li> E - {@link #fHasHeader} (1 bit): A bit that specifies whether the header text specified by HeaderAtom\r
+     *      record is displayed.\r
+     * <li> F - {@link #fHasFooter} (1 bit): A bit that specifies whether the footer text specified by FooterAtom\r
+     *      record is displayed.\r
+     * <li> reserved (10 bits): MUST be zero and MUST be ignored.\r
+     *\r
+     * @return A bit mask specifying options for displaying headers and footers\r
+     */\r
+    public int getMask(){\r
+        return LittleEndian.getShort(_recdata, 2);\r
+    }\r
+\r
+    /**\r
+     *  A bit mask specifying options for displaying headers and footers\r
+     *\r
+     * @param mask A bit mask specifying options for displaying headers and footers\r
+     */\r
+    public void setMask(int mask){\r
+        LittleEndian.putUShort(_recdata, 2, mask);\r
+    }\r
+\r
+    /**\r
+     * @param bit the bit to check\r
+     * @return whether the specified flag is set\r
+     */\r
+    public boolean getFlag(int bit){\r
+        return (getMask() & bit) != 0;\r
+    }\r
+\r
+    /**\r
+     * @param  bit the bit to set\r
+     * @param  value whether the specified bit is set\r
+     */\r
+    public void setFlag(int bit, boolean value){\r
+        int mask = getMask();\r
+        if(value) mask |= bit;\r
+        else mask &= ~bit;\r
+        setMask(mask);\r
+    }\r
+\r
+    public String toString(){\r
+        StringBuffer buf = new StringBuffer();\r
+        buf.append("HeadersFootersAtom\n");\r
+        buf.append("\tFormatId: " + getFormatId() + "\n");\r
+        buf.append("\tMask    : " + getMask() + "\n");\r
+        buf.append("\t  fHasDate        : " + getFlag(fHasDate) + "\n");\r
+        buf.append("\t  fHasTodayDate   : " + getFlag(fHasTodayDate) + "\n");\r
+        buf.append("\t  fHasUserDate    : " + getFlag(fHasUserDate) + "\n");\r
+        buf.append("\t  fHasSlideNumber : " + getFlag(fHasSlideNumber) + "\n");\r
+        buf.append("\t  fHasHeader      : " + getFlag(fHasHeader) + "\n");\r
+        buf.append("\t  fHasFooter      : " + getFlag(fHasFooter) + "\n");\r
+        return buf.toString();\r
+    }\r
+}\r
diff --git a/src/scratchpad/src/org/apache/poi/hslf/record/HeadersFootersContainer.java b/src/scratchpad/src/org/apache/poi/hslf/record/HeadersFootersContainer.java
new file mode 100644 (file)
index 0000000..e121574
--- /dev/null
@@ -0,0 +1,212 @@
+\r
+/* ====================================================================\r
+   Licensed to the Apache Software Foundation (ASF) under one or more\r
+   contributor license agreements.  See the NOTICE file distributed with\r
+   this work for additional information regarding copyright ownership.\r
+   The ASF licenses this file to You under the Apache License, Version 2.0\r
+   (the "License"); you may not use this file except in compliance with\r
+   the License.  You may obtain a copy of the License at\r
+\r
+       http://www.apache.org/licenses/LICENSE-2.0\r
+\r
+   Unless required by applicable law or agreed to in writing, software\r
+   distributed under the License is distributed on an "AS IS" BASIS,\r
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+   See the License for the specific language governing permissions and\r
+   limitations under the License.\r
+==================================================================== */\r
+\r
+package org.apache.poi.hslf.record;\r
+\r
+import org.apache.poi.util.LittleEndian;\r
+import org.apache.poi.util.POILogger;\r
+\r
+import java.io.OutputStream;\r
+import java.io.IOException;\r
+\r
+/**\r
+ * A container record that specifies information about the footers on a presentation slide.\r
+ * <p>\r
+ * It contains:<br>\r
+ * <li> 1. {@link HeadersFootersAtom}\r
+ * <li> 2. {@link CString }, Instance UserDate (0), optional: Stores the user's date.\r
+ *    This is the date that the user wants in the footers, instead of today's date.\r
+ * <li> 3. {@link CString }, Instance Header (1), optional: Stores the Header's contents.\r
+ * <li> 4. {@link CString }, Instance Footer (2), optional: Stores the Footer's contents.\r
+ * </p>\r
+ *\r
+ * @author Yegor Kozlov\r
+ */\r
+public class HeadersFootersContainer extends RecordContainer {\r
+\r
+    /**\r
+     * "instance" field in the record header indicating that this HeadersFootersContaine\r
+     *  is applied for slides\r
+     */\r
+    public static final short SlideHeadersFootersContainer = 0x3F;\r
+    /**\r
+      * "instance" field in the record header indicating that this HeadersFootersContaine\r
+     *   is applied for notes and handouts\r
+      */\r
+    public static final short NotesHeadersFootersContainer = 0x4F;\r
+\r
+    public static final int USERDATEATOM    = 0;\r
+    public static final int HEADERATOM      = 1;\r
+    public static final int FOOTERATOM      = 2;\r
+\r
+    private byte[] _header;\r
+    private HeadersFootersAtom hdAtom;\r
+    private CString csDate, csHeader, csFooter;\r
+\r
+    protected HeadersFootersContainer(byte[] source, int start, int len) {\r
+        // Grab the header\r
+        _header = new byte[8];\r
+        System.arraycopy(source,start,_header,0,8);\r
+\r
+        _children = Record.findChildRecords(source,start+8,len-8);\r
+        for(int i=0; i < _children.length; i++){\r
+            if(_children[i] instanceof HeadersFootersAtom) hdAtom = (HeadersFootersAtom)_children[i];\r
+            else if(_children[i] instanceof CString) {\r
+                CString cs = (CString)_children[i];\r
+                int opts = cs.getOptions() >> 4;\r
+                switch(opts){\r
+                    case USERDATEATOM: csDate = cs; break;\r
+                    case HEADERATOM: csHeader = cs; break;\r
+                    case FOOTERATOM: csFooter = cs; break;\r
+                    default:\r
+                        logger.log(POILogger.WARN, "Unexpected CString.Options in HeadersFootersContainer: " + opts);\r
+                        break;\r
+                }\r
+            } else {\r
+                logger.log(POILogger.WARN, "Unexpected record in HeadersFootersContainer: " + _children[i]);\r
+            }\r
+        }\r
+\r
+    }\r
+\r
+    public HeadersFootersContainer(short options) {\r
+        _header = new byte[8];\r
+        LittleEndian.putShort(_header, 0, options);\r
+        LittleEndian.putShort(_header, 2, (short)getRecordType());\r
+\r
+        hdAtom = new HeadersFootersAtom();\r
+        _children = new Record[]{\r
+            hdAtom\r
+        };\r
+        csDate = csHeader = csFooter = null;\r
+\r
+    }\r
+\r
+    /**\r
+     * Return the type, which is <code>{@link RecordTypes#HeadersFooters}</code>\r
+     */\r
+    public long getRecordType() {\r
+        return RecordTypes.HeadersFooters.typeID;\r
+    }\r
+\r
+    /**\r
+     * Must be either {@link #SlideHeadersFootersContainer} or {@link #NotesHeadersFootersContainer}\r
+     *\r
+     * @return "instance" field in the record header\r
+     */\r
+    public int getOptions(){\r
+        return LittleEndian.getShort(_header, 0);\r
+    }\r
+\r
+    /**\r
+     * Write the contents of the record back, so it can be written to disk\r
+     */\r
+    public void writeOut(OutputStream out) throws IOException {\r
+        writeOut(_header[0],_header[1],getRecordType(),_children,out);\r
+    }\r
+\r
+    /**\r
+     * HeadersFootersAtom stores the basic information of the header and footer structure.\r
+     *\r
+     * @return <code>HeadersFootersAtom</code>\r
+     */\r
+    public HeadersFootersAtom getHeadersFootersAtom(){\r
+        return hdAtom;\r
+    }\r
+\r
+    /**\r
+     * A {@link CString} record that stores the user's date.\r
+     * <p>This is the date that the user wants in the footers, instead of today's date.</p>\r
+     *\r
+     * @return A {@link CString} record that stores the user's date or <code>null</code>\r
+     */\r
+    public CString getUserDateAtom(){\r
+        return csDate;\r
+    }\r
+\r
+    /**\r
+     * A {@link CString} record that stores the Header's contents.\r
+     *\r
+     * @return A {@link CString} record that stores the Header's contents or <code>null</code>\r
+     */\r
+    public CString getHeaderAtom(){\r
+        return csHeader;\r
+    }\r
+\r
+    /**\r
+     * A {@link CString} record that stores the Footers's contents.\r
+     *\r
+     * @return A {@link CString} record that stores the Footers's contents or <code>null</code>\r
+     */\r
+    public CString getFooterAtom(){\r
+        return csFooter;\r
+    }\r
+\r
+    /**\r
+     * Insert a {@link CString} record that stores the user's date.\r
+     *\r
+     * @return  the created {@link CString} record that stores the user's date.\r
+     */\r
+    public CString addUserDateAtom(){\r
+        if(csDate != null) return csDate;\r
+\r
+        csDate = new CString();\r
+        csDate.setOptions(USERDATEATOM << 4);\r
+\r
+        addChildAfter(csDate, hdAtom);\r
+\r
+        return csDate;\r
+    }\r
+\r
+    /**\r
+     * Insert a {@link CString} record that stores the user's date.\r
+     *\r
+     * @return  the created {@link CString} record that stores the user's date.\r
+     */\r
+    public CString addHeaderAtom(){\r
+        if(csHeader != null) return csHeader;\r
+\r
+        csHeader = new CString();\r
+        csHeader.setOptions(HEADERATOM << 4);\r
+\r
+        Record r = hdAtom;\r
+        if(csDate != null) r = hdAtom;\r
+        addChildAfter(csHeader, r);\r
+\r
+        return csHeader;\r
+    }\r
+\r
+    /**\r
+     * Insert a {@link CString} record that stores the user's date.\r
+     *\r
+     * @return  the created {@link CString} record that stores the user's date.\r
+     */\r
+    public CString addFooterAtom(){\r
+        if(csFooter != null) return csFooter;\r
+\r
+        csFooter = new CString();\r
+        csFooter.setOptions(FOOTERATOM << 4);\r
+\r
+        Record r = hdAtom;\r
+        if(csHeader != null) r = csHeader;\r
+        else if(csDate != null) r = csDate;\r
+        addChildAfter(csFooter, r);\r
+\r
+        return csFooter;\r
+    }\r
+}\r
index a6f00da124da26cb05072dce6a74d2d9cb89642b..d7a664725e9e12097ecf52d45d212aec33ed8c38 100644 (file)
@@ -111,8 +111,8 @@ public class RecordTypes {
     public static final Type ExHyperlinkAtom = new Type(4051,ExHyperlinkAtom.class);
     public static final Type ExHyperlink = new Type(4055,ExHyperlink.class);
     public static final Type SlideNumberMCAtom = new Type(4056,null);
-    public static final Type HeadersFooters = new Type(4057,null);
-    public static final Type HeadersFootersAtom = new Type(4058,null);
+    public static final Type HeadersFooters = new Type(4057,HeadersFootersContainer.class);
+    public static final Type HeadersFootersAtom = new Type(4058,HeadersFootersAtom.class);
     public static final Type TxInteractiveInfoAtom = new Type(4063,TxInteractiveInfoAtom.class);
     public static final Type CharFormatAtom = new Type(4066,null);
     public static final Type ParaFormatAtom = new Type(4067,null);
index 56e94431ec1cbbb30aca50d0c23d62134e463333..f38cc7716d05f40a2cd83ee72a7fa0c67f903d24 100644 (file)
@@ -811,4 +811,50 @@ public class SlideShow
     public int getNumberOfFonts() {
         return getDocumentRecord().getEnvironment().getFontCollection().getNumberOfFonts();
     }
+
+    /**
+     * Return  Header / Footer settings for slides
+     *
+     * @return Header / Footer settings for slides
+     */
+    public HeadersFooters getSlideHeadersFooters(){
+        HeadersFootersContainer hdd = null;
+        Record[] ch = _documentRecord.getChildRecords();
+        for (int i = 0; i < ch.length; i++) {
+            if(ch[i] instanceof HeadersFootersContainer &&
+                    ((HeadersFootersContainer)ch[i]).getOptions() == HeadersFootersContainer.SlideHeadersFootersContainer){
+                hdd = (HeadersFootersContainer)ch[i];
+                break;
+            }
+        }
+        boolean newRecord = false;
+        if(hdd == null) {
+            hdd = new HeadersFootersContainer(HeadersFootersContainer.SlideHeadersFootersContainer);
+            newRecord = true;
+        }
+        return new HeadersFooters(hdd, this, newRecord);
+    }
+
+    /**
+     * Return  Header / Footer settings for notes
+     *
+     * @return Header / Footer settings for notes
+     */
+    public HeadersFooters getNotesHeadersFooters(){
+        HeadersFootersContainer hdd = null;
+        Record[] ch = _documentRecord.getChildRecords();
+        for (int i = 0; i < ch.length; i++) {
+            if(ch[i] instanceof HeadersFootersContainer &&
+                    ((HeadersFootersContainer)ch[i]).getOptions() == HeadersFootersContainer.NotesHeadersFootersContainer){
+                hdd = (HeadersFootersContainer)ch[i];
+                break;
+            }
+        }
+        boolean newRecord = false;
+        if(hdd == null) {
+            hdd = new HeadersFootersContainer(HeadersFootersContainer.NotesHeadersFootersContainer);
+            newRecord = true;
+        }
+        return new HeadersFooters(hdd, this, newRecord);
+    }
 }
diff --git a/src/scratchpad/testcases/org/apache/poi/hslf/data/headers_footers.ppt b/src/scratchpad/testcases/org/apache/poi/hslf/data/headers_footers.ppt
new file mode 100644 (file)
index 0000000..891d73f
Binary files /dev/null and b/src/scratchpad/testcases/org/apache/poi/hslf/data/headers_footers.ppt differ
diff --git a/src/scratchpad/testcases/org/apache/poi/hslf/model/TestHeadersFooters.java b/src/scratchpad/testcases/org/apache/poi/hslf/model/TestHeadersFooters.java
new file mode 100644 (file)
index 0000000..8b1cdbe
--- /dev/null
@@ -0,0 +1,118 @@
+\r
+/* ====================================================================\r
+   Licensed to the Apache Software Foundation (ASF) under one or more\r
+   contributor license agreements.  See the NOTICE file distributed with\r
+   this work for additional information regarding copyright ownership.\r
+   The ASF licenses this file to You under the Apache License, Version 2.0\r
+   (the "License"); you may not use this file except in compliance with\r
+   the License.  You may obtain a copy of the License at\r
+\r
+       http://www.apache.org/licenses/LICENSE-2.0\r
+\r
+   Unless required by applicable law or agreed to in writing, software\r
+   distributed under the License is distributed on an "AS IS" BASIS,\r
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+   See the License for the specific language governing permissions and\r
+   limitations under the License.\r
+==================================================================== */\r
+        \r
+\r
+\r
+package org.apache.poi.hslf.model;\r
+\r
+import java.io.*;\r
+import org.apache.poi.hslf.usermodel.SlideShow;\r
+\r
+import junit.framework.TestCase;\r
+\r
+/**\r
+ * Test {@link org.apache.poi.hslf.model.HeadersFooters} object\r
+ */\r
+public class TestHeadersFooters extends TestCase\r
+{\r
+\r
+    public static final String cwd = System.getProperty("HSLF.testdata.path");\r
+\r
+    public void testRead() throws Exception\r
+    {\r
+        File file = new File(cwd, "headers_footers.ppt");\r
+        FileInputStream is = new FileInputStream(file);\r
+        SlideShow ppt = new SlideShow(is);\r
+        is.close();\r
+\r
+        HeadersFooters slideHdd = ppt.getSlideHeadersFooters();\r
+        assertTrue(slideHdd.isFooterVisible());\r
+        assertEquals("Global Slide Footer", slideHdd.getFooterText());\r
+        assertTrue(slideHdd.isSlideNumberVisible());\r
+        assertFalse(slideHdd.isHeaderVisible());\r
+        assertNull(slideHdd.getHeaderText());\r
+        assertFalse(slideHdd.isUserDateVisible());\r
+        assertNull(slideHdd.getDateTimeText());\r
+\r
+\r
+        HeadersFooters notesHdd = ppt.getNotesHeadersFooters();\r
+        assertTrue(notesHdd.isFooterVisible());\r
+        assertEquals("Notes Footer", notesHdd.getFooterText());\r
+        assertTrue(notesHdd.isHeaderVisible());\r
+        assertEquals("Notes Header", notesHdd.getHeaderText());\r
+        assertTrue(notesHdd.isUserDateVisible());\r
+        assertNull(notesHdd.getDateTimeText());\r
+\r
+        Slide[] slide = ppt.getSlides();\r
+        //the first slide uses presentation-scope headers / footers\r
+        HeadersFooters hd1 = slide[0].getHeadersFooters();\r
+        assertEquals(slideHdd.isFooterVisible(), hd1.isFooterVisible());\r
+        assertEquals(slideHdd.getFooterText(), hd1.getFooterText());\r
+        assertEquals(slideHdd.isSlideNumberVisible(), hd1.isSlideNumberVisible());\r
+        assertEquals(slideHdd.isHeaderVisible(), hd1.isHeaderVisible());\r
+        assertEquals(slideHdd.getHeaderText(), hd1.getHeaderText());\r
+        assertEquals(slideHdd.isUserDateVisible(), hd1.isUserDateVisible());\r
+        assertEquals(slideHdd.getDateTimeText(), hd1.getDateTimeText());\r
+\r
+        //the first slide uses per-slide headers / footers\r
+        HeadersFooters hd2 = slide[1].getHeadersFooters();\r
+        assertEquals(true, hd2.isFooterVisible());\r
+        assertEquals("per-slide footer", hd2.getFooterText());\r
+        assertEquals(true, hd2.isUserDateVisible());\r
+        assertEquals("custom date format", hd2.getDateTimeText());\r
+    }\r
+\r
+    public void testCreateSlideFooters() throws Exception\r
+    {\r
+        SlideShow ppt = new SlideShow();\r
+        HeadersFooters hdd = ppt.getSlideHeadersFooters();\r
+        hdd.setFootersText("My slide footer");\r
+        hdd.setSlideNumberVisible(true);\r
+\r
+        ByteArrayOutputStream out = new ByteArrayOutputStream();\r
+        ppt.write(out);\r
+        byte[] b = out.toByteArray();\r
+\r
+        SlideShow ppt2 = new SlideShow(new ByteArrayInputStream(b));\r
+        HeadersFooters hdd2 = ppt2.getSlideHeadersFooters();\r
+        assertTrue(hdd2.isSlideNumberVisible());\r
+        assertTrue(hdd2.isFooterVisible());\r
+        assertEquals("My slide footer", hdd2.getFooterText());\r
+    }\r
+\r
+    public void testCreateNotesFooters() throws Exception\r
+    {\r
+        SlideShow ppt = new SlideShow();\r
+        HeadersFooters hdd = ppt.getNotesHeadersFooters();\r
+        hdd.setFootersText("My notes footer");\r
+        hdd.setHeaderText("My notes header");\r
+        hdd.setSlideNumberVisible(true);\r
+\r
+        ByteArrayOutputStream out = new ByteArrayOutputStream();\r
+        ppt.write(out);\r
+        byte[] b = out.toByteArray();\r
+\r
+        SlideShow ppt2 = new SlideShow(new ByteArrayInputStream(b));\r
+        HeadersFooters hdd2 = ppt2.getNotesHeadersFooters();\r
+        assertTrue(hdd2.isSlideNumberVisible());\r
+        assertTrue(hdd2.isFooterVisible());\r
+        assertEquals("My notes footer", hdd2.getFooterText());\r
+        assertTrue(hdd2.isHeaderVisible());\r
+        assertEquals("My notes header", hdd2.getHeaderText());\r
+    }\r
+}
\ No newline at end of file
index 3f3d1a1447e900060e61e10778f7a452607f1e3d..df8e1a3dae8e092915d49c493f79837d7cfa36b4 100644 (file)
@@ -46,12 +46,12 @@ public class TestCString extends TestCase {
        }
        public void testCount() throws Exception {
                CString ca = new CString(data_a, 0, data_a.length);
-               assertEquals(0, ca.getCount());
+               assertEquals(0, ca.getOptions());
                CString cb = new CString(data_b, 0, data_a.length);
-               assertEquals(0x10, cb.getCount());
+               assertEquals(0x10, cb.getOptions());
                
-               ca.setCount(28);
-               assertEquals(28, ca.getCount());
+               ca.setOptions(28);
+               assertEquals(28, ca.getOptions());
        }
        
        public void testText() throws Exception {
@@ -90,7 +90,7 @@ public class TestCString extends TestCase {
        public void testChange() throws Exception {
                CString ca = new CString(data_a, 0, data_a.length);
                ca.setText("Comments");
-               ca.setCount(0x10);
+               ca.setOptions(0x10);
                
                try {
                        for(int i=0; i<data_a.length; i++) {
diff --git a/src/scratchpad/testcases/org/apache/poi/hslf/record/TestCurrentUserAtom.java b/src/scratchpad/testcases/org/apache/poi/hslf/record/TestCurrentUserAtom.java
new file mode 100644 (file)
index 0000000..e92998e
--- /dev/null
@@ -0,0 +1,115 @@
+
+/* ====================================================================
+   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.hslf.record;
+
+
+import junit.framework.TestCase;
+import java.io.ByteArrayOutputStream;
+import java.io.FileInputStream;
+import java.io.InputStream;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+
+import javax.imageio.stream.FileImageInputStream;
+
+import org.apache.poi.hslf.exceptions.CorruptPowerPointFileException;
+import org.apache.poi.hslf.exceptions.EncryptedPowerPointFileException;
+import org.apache.poi.poifs.filesystem.DocumentEntry;
+import org.apache.poi.poifs.filesystem.POIFSFileSystem;
+
+/**
+ * Tests that CurrentUserAtom works properly.
+ *
+ * @author Nick Burch (nick at torchbox dot com)
+ */
+public class TestCurrentUserAtom extends TestCase {
+       /** Not encrypted */
+       private String normalFile;
+       /** Encrypted */
+       private String encFile;
+
+       protected void setUp() throws Exception {
+               super.setUp();
+               
+               String dirname = System.getProperty("HSLF.testdata.path");
+               normalFile = dirname + "/basic_test_ppt_file.ppt";
+               encFile = dirname + "/Password_Protected-hello.ppt";
+       }
+
+       public void testReadNormal() throws Exception {
+               POIFSFileSystem fs = new POIFSFileSystem(
+                               new FileInputStream(normalFile)
+               );
+               
+               CurrentUserAtom cu = new CurrentUserAtom(fs);
+               
+               // Check the contents
+               assertEquals("Hogwarts", cu.getLastEditUsername());
+               assertEquals(0x2942, cu.getCurrentEditOffset());
+               
+               // Round trip
+               ByteArrayOutputStream baos = new ByteArrayOutputStream();
+               cu.writeOut(baos);
+               
+               CurrentUserAtom cu2 = new CurrentUserAtom(baos.toByteArray());
+               assertEquals("Hogwarts", cu2.getLastEditUsername());
+               assertEquals(0x2942, cu2.getCurrentEditOffset());
+       }
+       
+       public void testReadEnc() throws Exception {
+               POIFSFileSystem fs = new POIFSFileSystem(
+                               new FileInputStream(encFile)
+               );
+               
+               try {
+                       new CurrentUserAtom(fs);
+                       fail();
+               } catch(EncryptedPowerPointFileException e) {
+                       // Good
+               }
+       }
+       
+       public void testWriteNormal() throws Exception {
+               // Get raw contents from a known file
+               POIFSFileSystem fs = new POIFSFileSystem(
+                               new FileInputStream(normalFile)
+               );
+               DocumentEntry docProps = (DocumentEntry)fs.getRoot().getEntry("Current User");
+               byte[] contents = new byte[docProps.getSize()];
+               InputStream in = fs.getRoot().createDocumentInputStream("Current User");
+               in.read(contents);
+               
+               // Now build up a new one
+               CurrentUserAtom cu = new CurrentUserAtom();
+               cu.setLastEditUsername("Hogwarts");
+               cu.setCurrentEditOffset(0x2942);
+               
+               // Check it matches
+               ByteArrayOutputStream baos = new ByteArrayOutputStream();
+               cu.writeOut(baos);
+               byte[] out = baos.toByteArray();
+               
+               assertEquals(contents.length, out.length);
+               for(int i=0; i<contents.length; i++) {
+                       assertEquals("Byte " + i, contents[i], out[i]);
+               }
+       }
+}
diff --git a/src/scratchpad/testcases/org/apache/poi/hslf/record/TestHeadersFootersAtom.java b/src/scratchpad/testcases/org/apache/poi/hslf/record/TestHeadersFootersAtom.java
new file mode 100644 (file)
index 0000000..8c6972c
--- /dev/null
@@ -0,0 +1,95 @@
+\r
+/* ====================================================================\r
+   Licensed to the Apache Software Foundation (ASF) under one or more\r
+   contributor license agreements.  See the NOTICE file distributed with\r
+   this work for additional information regarding copyright ownership.\r
+   The ASF licenses this file to You under the Apache License, Version 2.0\r
+   (the "License"); you may not use this file except in compliance with\r
+   the License.  You may obtain a copy of the License at\r
+\r
+       http://www.apache.org/licenses/LICENSE-2.0\r
+\r
+   Unless required by applicable law or agreed to in writing, software\r
+   distributed under the License is distributed on an "AS IS" BASIS,\r
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+   See the License for the specific language governing permissions and\r
+   limitations under the License.\r
+==================================================================== */\r
+        \r
+\r
+\r
+package org.apache.poi.hslf.record;\r
+\r
+\r
+import junit.framework.TestCase;\r
+import java.io.ByteArrayOutputStream;\r
+import java.util.Arrays;\r
+\r
+/**\r
+ * Tests that {@link HeadersFootersAtom} works properly\r
+ *\r
+ * @author Yegor Kozlov\r
+ */\r
+public class TestHeadersFootersAtom extends TestCase {\r
+       // From a real file\r
+       private byte[] data = new byte[] {\r
+            0x00, 0x00, (byte)0xDA, 0x0F, 0x04, 0x00, 0x00, 00,\r
+            0x00, 0x00, 0x23, 0x00 };\r
+\r
+    public void testRead() throws Exception {\r
+               HeadersFootersAtom record = new HeadersFootersAtom(data, 0, data.length);\r
+               assertEquals(RecordTypes.HeadersFootersAtom.typeID, record.getRecordType());\r
+\r
+        assertEquals(0, record.getFormatId());\r
+        assertEquals(0x23, record.getMask());\r
+\r
+        assertTrue(record.getFlag(HeadersFootersAtom.fHasDate));\r
+        assertTrue(record.getFlag(HeadersFootersAtom.fHasTodayDate));\r
+        assertFalse(record.getFlag(HeadersFootersAtom.fHasUserDate));\r
+        assertFalse(record.getFlag(HeadersFootersAtom.fHasSlideNumber));\r
+        assertFalse(record.getFlag(HeadersFootersAtom.fHasHeader));\r
+        assertTrue(record.getFlag(HeadersFootersAtom.fHasFooter));\r
+    }\r
+\r
+       public void testWrite() throws Exception {\r
+               HeadersFootersAtom record = new HeadersFootersAtom(data, 0, data.length);\r
+               ByteArrayOutputStream baos = new ByteArrayOutputStream();\r
+               record.writeOut(baos);\r
+               byte[] b = baos.toByteArray();\r
+\r
+               assertTrue(Arrays.equals(data, b));\r
+       }\r
+\r
+    public void testNewRecord() throws Exception {\r
+        HeadersFootersAtom record = new HeadersFootersAtom();\r
+        record.setFlag(HeadersFootersAtom.fHasDate, true);\r
+        record.setFlag(HeadersFootersAtom.fHasTodayDate, true);\r
+        record.setFlag(HeadersFootersAtom.fHasFooter, true);\r
+\r
+        ByteArrayOutputStream baos = new ByteArrayOutputStream();\r
+        record.writeOut(baos);\r
+        byte[] b = baos.toByteArray();\r
+\r
+        assertTrue(Arrays.equals(data, b));\r
+    }\r
+\r
+    public void testFlags() throws Exception {\r
+        HeadersFootersAtom record = new HeadersFootersAtom();\r
+\r
+        //in a new record all the bits are 0\r
+        for(int i = 0; i < 6; i++) assertFalse(record.getFlag(1 << i));\r
+\r
+        record.setFlag(HeadersFootersAtom.fHasTodayDate, true);\r
+        assertTrue(record.getFlag(HeadersFootersAtom.fHasTodayDate));\r
+\r
+        record.setFlag(HeadersFootersAtom.fHasTodayDate, true);\r
+        assertTrue(record.getFlag(HeadersFootersAtom.fHasTodayDate));\r
+\r
+        record.setFlag(HeadersFootersAtom.fHasTodayDate, false);\r
+        assertFalse(record.getFlag(HeadersFootersAtom.fHasTodayDate));\r
+\r
+        record.setFlag(HeadersFootersAtom.fHasTodayDate, false);\r
+        assertFalse(record.getFlag(HeadersFootersAtom.fHasTodayDate));\r
+\r
+    }\r
+}
\ No newline at end of file
diff --git a/src/scratchpad/testcases/org/apache/poi/hslf/record/TestHeadersFootersContainer.java b/src/scratchpad/testcases/org/apache/poi/hslf/record/TestHeadersFootersContainer.java
new file mode 100644 (file)
index 0000000..d24ca05
--- /dev/null
@@ -0,0 +1,171 @@
+\r
+/* ====================================================================\r
+   Licensed to the Apache Software Foundation (ASF) under one or more\r
+   contributor license agreements.  See the NOTICE file distributed with\r
+   this work for additional information regarding copyright ownership.\r
+   The ASF licenses this file to You under the Apache License, Version 2.0\r
+   (the "License"); you may not use this file except in compliance with\r
+   the License.  You may obtain a copy of the License at\r
+\r
+       http://www.apache.org/licenses/LICENSE-2.0\r
+\r
+   Unless required by applicable law or agreed to in writing, software\r
+   distributed under the License is distributed on an "AS IS" BASIS,\r
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+   See the License for the specific language governing permissions and\r
+   limitations under the License.\r
+==================================================================== */\r
+        \r
+\r
+\r
+package org.apache.poi.hslf.record;\r
+\r
+\r
+import junit.framework.TestCase;\r
+import java.io.ByteArrayOutputStream;\r
+import java.util.Arrays;\r
+\r
+/**\r
+ * Tests that {@link HeadersFootersContainer} works properly\r
+ *\r
+ * @author Yegor Kozlov\r
+ */\r
+public class TestHeadersFootersContainer extends TestCase {\r
+       // SlideHeadersFootersContainer\r
+       private byte[] slideData = new byte[] {\r
+            0x3F, 0x00, (byte)0xD9, 0x0F, 0x2E, 0x00, 0x00, 0x00,\r
+            0x00, 0x00, (byte)0xDA, 0x0F, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x23, 0x00,\r
+            0x20, 0x00, (byte)0xBA, 0x0F, 0x1A, 0x00, 0x00, 0x00,\r
+            0x4D, 0x00, 0x79, 0x00, 0x20, 0x00, 0x46, 0x00, 0x6F, 0x00, 0x6F, 0x00, 0x74,\r
+            0x00, 0x65, 0x00, 0x72, 0x00, 0x20, 0x00, 0x2D, 0x00, 0x20, 0x00, 0x31, 0x00\r
+\r
+    };\r
+\r
+    // NotesHeadersFootersContainer\r
+    private byte[] notesData = new byte[] {\r
+            0x4F, 0x00, (byte)0xD9, 0x0F, 0x48, 0x00, 0x00, 0x00,\r
+            0x00, 0x00, (byte)0xDA, 0x0F, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3D, 0x00,\r
+            0x10, 0x00, (byte)0xBA, 0x0F, 0x16, 0x00, 0x00, 0x00,\r
+            0x4E, 0x00, 0x6F, 0x00, 0x74, 0x00, 0x65, 0x00, 0x20, 0x00, 0x48, 0x00,\r
+            0x65, 0x00, 0x61, 0x00, 0x64, 0x00, 0x65, 0x00, 0x72, 0x00,\r
+            0x20, 0x00, (byte)0xBA, 0x0F, 0x16, 0x00, 0x00, 0x00,\r
+            0x4E, 0x00, 0x6F, 0x00, 0x74, 0x00, 0x65, 0x00, 0x20, 0x00, 0x46, 0x00,\r
+            0x6F, 0x00, 0x6F, 0x00, 0x74, 0x00, 0x65, 0x00, 0x72, 0x00\r
+    };\r
+\r
+    public void testReadSlideHeadersFootersContainer() throws Exception {\r
+               HeadersFootersContainer record = new HeadersFootersContainer(slideData, 0, slideData.length);\r
+               assertEquals(RecordTypes.HeadersFooters.typeID, record.getRecordType());\r
+        assertEquals(HeadersFootersContainer.SlideHeadersFootersContainer, record.getOptions());\r
+        assertEquals(2, record.getChildRecords().length);\r
+\r
+        HeadersFootersAtom hdd = record.getHeadersFootersAtom();\r
+        assertNotNull(hdd);\r
+\r
+        CString csFooter = record.getFooterAtom();\r
+        assertNotNull(csFooter);\r
+        assertEquals(HeadersFootersContainer.FOOTERATOM, csFooter.getOptions() >> 4);\r
+\r
+        assertEquals("My Footer - 1", csFooter.getText());\r
+\r
+        assertNull(record.getUserDateAtom());\r
+        assertNull(record.getHeaderAtom());\r
+    }\r
+\r
+       public void testWriteSlideHeadersFootersContainer() throws Exception {\r
+               HeadersFootersContainer record = new HeadersFootersContainer(slideData, 0, slideData.length);\r
+               ByteArrayOutputStream baos = new ByteArrayOutputStream();\r
+               record.writeOut(baos);\r
+               byte[] b = baos.toByteArray();\r
+\r
+               assertTrue(Arrays.equals(slideData, b));\r
+       }\r
+\r
+    public void testNewSlideHeadersFootersContainer() throws Exception {\r
+        HeadersFootersContainer record = new HeadersFootersContainer(HeadersFootersContainer.SlideHeadersFootersContainer);\r
+\r
+        assertNotNull(record.getHeadersFootersAtom());\r
+        assertNull(record.getUserDateAtom());\r
+        assertNull(record.getHeaderAtom());\r
+        assertNull(record.getFooterAtom());\r
+\r
+        HeadersFootersAtom hd = record.getHeadersFootersAtom();\r
+        hd.setFlag(HeadersFootersAtom.fHasDate, true);\r
+        hd.setFlag(HeadersFootersAtom.fHasTodayDate, true);\r
+        hd.setFlag(HeadersFootersAtom.fHasFooter, true);\r
+\r
+        CString csFooter = record.addFooterAtom();\r
+        assertNotNull(csFooter);\r
+        assertEquals(HeadersFootersContainer.FOOTERATOM, csFooter.getOptions() >> 4);\r
+        csFooter.setText("My Footer - 1");\r
+\r
+        ByteArrayOutputStream baos = new ByteArrayOutputStream();\r
+        record.writeOut(baos);\r
+        byte[] b = baos.toByteArray();\r
+\r
+        assertTrue(Arrays.equals(slideData, b));\r
+    }\r
+\r
+    public void testReadNotesHeadersFootersContainer() throws Exception {\r
+               HeadersFootersContainer record = new HeadersFootersContainer(notesData, 0, notesData.length);\r
+               assertEquals(RecordTypes.HeadersFooters.typeID, record.getRecordType());\r
+        assertEquals(HeadersFootersContainer.NotesHeadersFootersContainer, record.getOptions());\r
+        assertEquals(3, record.getChildRecords().length);\r
+\r
+        HeadersFootersAtom hdd = record.getHeadersFootersAtom();\r
+        assertNotNull(hdd);\r
+\r
+        CString csHeader = record.getHeaderAtom();\r
+        assertNotNull(csHeader);\r
+        assertEquals(HeadersFootersContainer.HEADERATOM, csHeader.getOptions() >> 4);\r
+        assertEquals("Note Header", csHeader.getText());\r
+\r
+        CString csFooter = record.getFooterAtom();\r
+        assertNotNull(csFooter);\r
+        assertEquals(HeadersFootersContainer.FOOTERATOM, csFooter.getOptions() >> 4);\r
+        assertEquals("Note Footer", csFooter.getText());\r
+    }\r
+\r
+       public void testWriteNotesHeadersFootersContainer() throws Exception {\r
+               HeadersFootersContainer record = new HeadersFootersContainer(notesData, 0, notesData.length);\r
+        ByteArrayOutputStream baos = new ByteArrayOutputStream();\r
+               record.writeOut(baos);\r
+               byte[] b = baos.toByteArray();\r
+\r
+               assertTrue(Arrays.equals(notesData, b));\r
+       }\r
+\r
+    public void testNewNotesHeadersFootersContainer() throws Exception {\r
+        HeadersFootersContainer record = new HeadersFootersContainer(HeadersFootersContainer.NotesHeadersFootersContainer);\r
+\r
+        assertNotNull(record.getHeadersFootersAtom());\r
+        assertNull(record.getUserDateAtom());\r
+        assertNull(record.getHeaderAtom());\r
+        assertNull(record.getFooterAtom());\r
+\r
+        HeadersFootersAtom hd = record.getHeadersFootersAtom();\r
+        hd.setFlag(HeadersFootersAtom.fHasDate, true);\r
+        hd.setFlag(HeadersFootersAtom.fHasTodayDate, false);\r
+        hd.setFlag(HeadersFootersAtom.fHasUserDate, true);\r
+        hd.setFlag(HeadersFootersAtom.fHasSlideNumber, true);\r
+        hd.setFlag(HeadersFootersAtom.fHasHeader, true);\r
+        hd.setFlag(HeadersFootersAtom.fHasFooter, true);\r
+\r
+        CString csHeader = record.addHeaderAtom();\r
+        assertNotNull(csHeader);\r
+        assertEquals(HeadersFootersContainer.HEADERATOM, csHeader.getOptions() >> 4);\r
+        csHeader.setText("Note Header");\r
+\r
+        CString csFooter = record.addFooterAtom();\r
+        assertNotNull(csFooter);\r
+        assertEquals(HeadersFootersContainer.FOOTERATOM, csFooter.getOptions() >> 4);\r
+        csFooter.setText("Note Footer");\r
+\r
+        ByteArrayOutputStream baos = new ByteArrayOutputStream();\r
+        record.writeOut(baos);\r
+        byte[] b = baos.toByteArray();\r
+\r
+        assertTrue(Arrays.equals(notesData, b));\r
+    }\r
+\r
+}
\ No newline at end of file
diff --git a/src/testcases/org/apache/poi/hssf/data/dvEmpty.xls b/src/testcases/org/apache/poi/hssf/data/dvEmpty.xls
new file mode 100644 (file)
index 0000000..72c78cf
Binary files /dev/null and b/src/testcases/org/apache/poi/hssf/data/dvEmpty.xls differ
index ef21cc9b362f3a524966e161b0c6930be905555a..71881accec32469db02f99e6fff5c0f781d5021c 100644 (file)
@@ -297,7 +297,8 @@ public final class TestSheet extends TestCase {
         xfindex = sheet.getXFIndexForColAt((short) 1);
         assertEquals(DEFAULT_IDX, xfindex);
 
-        ColumnInfoRecord nci = ( ColumnInfoRecord ) sheet.createColInfo();
+        // TODO change return type to ColumnInfoRecord 
+        ColumnInfoRecord nci = (ColumnInfoRecord)ColumnInfoRecordsAggregate.createColInfo();
         sheet.columns.insertColumn(nci);
 
         // single column ColumnInfoRecord
index f1c3b7c9cace67319cc2214d56c8f920a44d8906..f86c2941a970cdb3681acb3ccffb9381decc31bf 100644 (file)
 
 package org.apache.poi.hssf.model;
 
-import java.lang.reflect.Field;
-import java.util.ArrayList;
-import java.util.List;
-
 import junit.framework.TestCase;
 
 import org.apache.poi.hssf.record.ColumnInfoRecord;
+import org.apache.poi.hssf.record.aggregates.ColumnInfoRecordsAggregate;
 
 /**
  * @author Tony Poppleton
@@ -32,7 +29,8 @@ public final class TestSheetAdditional extends TestCase {
        
        public void testGetCellWidth() {
                Sheet sheet = Sheet.createSheet();
-               ColumnInfoRecord nci = ( ColumnInfoRecord ) sheet.createColInfo();
+               // TODO change return type to ColumnInfoRecord 
+               ColumnInfoRecord nci = (ColumnInfoRecord)ColumnInfoRecordsAggregate.createColInfo();
 
                // Prepare test model
                nci.setFirstColumn((short)5);
index e883503674bf5bcaf9d743a0a826911145c1f182..ec291ffd49354d235a4e0c8d139cdc879831bb37 100644 (file)
@@ -20,7 +20,7 @@ package org.apache.poi.hssf.record;
 import junit.framework.AssertionFailedError;
 import junit.framework.TestCase;
 
-import org.apache.poi.hssf.record.cf.CellRange;
+import org.apache.poi.ss.util.CellRangeAddress;
 
 /**
  * Tests the serialization and deserialization of the TestCFHeaderRecord
@@ -34,18 +34,18 @@ public final class TestCFHeaderRecord extends TestCase
        public void testCreateCFHeaderRecord () 
        {
                CFHeaderRecord record = new CFHeaderRecord();
-               CellRange[] ranges = {
-                       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),
-                       new CellRange(2,3,2,3),
+               CellRangeAddress[] ranges = {
+                       new CellRangeAddress(0,0xFFFF,5,5),
+                       new CellRangeAddress(0,0xFFFF,6,6),
+                       new CellRangeAddress(0,1,0,1),
+                       new CellRangeAddress(0,1,2,3),
+                       new CellRangeAddress(2,3,0,1),
+                       new CellRangeAddress(2,3,2,3),
                };
                record.setCellRanges(ranges);
                ranges = record.getCellRanges();
                assertEquals(6,ranges.length);
-               CellRange enclosingCellRange = record.getEnclosingCellRange();
+               CellRangeAddress enclosingCellRange = record.getEnclosingCellRange();
                assertEquals(0, enclosingCellRange.getFirstRow());
                assertEquals(65535, enclosingCellRange.getLastRow());
                assertEquals(0, enclosingCellRange.getFirstColumn());
@@ -95,7 +95,7 @@ public final class TestCFHeaderRecord extends TestCase
                assertEquals("#CFRULES", 3, record.getNumberOfConditionalFormats());
                assertTrue(record.getNeedRecalculation());
                confirm(record.getEnclosingCellRange(), 0, 3, 0, 3);
-               CellRange[] ranges = record.getCellRanges();
+               CellRangeAddress[] ranges = record.getCellRanges();
                assertEquals(4, ranges.length);
                confirm(ranges[0], 0, 1, 0, 1);
                confirm(ranges[1], 0, 1, 2, 3);
@@ -154,7 +154,7 @@ public final class TestCFHeaderRecord extends TestCase
                assertEquals("#CFRULES", 19, record.getNumberOfConditionalFormats());
                assertFalse(record.getNeedRecalculation());
                confirm(record.getEnclosingCellRange(), 0, 65535, 0, 255);
-               CellRange[] ranges = record.getCellRanges();
+               CellRangeAddress[] ranges = record.getCellRanges();
                assertEquals(3, ranges.length);
                confirm(ranges[0], 40000, 50000, 2, 2);
                confirm(ranges[1], 0, 65535, 5, 5);
@@ -168,18 +168,11 @@ public final class TestCFHeaderRecord extends TestCase
                        assertEquals("CFHeaderRecord doesn't match", recordData[i], output[i+4]);
                }
        }
-       
 
-       private static void confirm(CellRange cr, int expFirstRow, int expLastRow, int expFirstCol, int expLastColumn) {
+       private static void confirm(CellRangeAddress 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)
-       {
-               System.out.println("Testing org.apache.poi.hssf.record.CFHeaderRecord");
-               junit.textui.TestRunner.run(TestCFHeaderRecord.class);
-       }
 }
index 31ec27b7c4fa6928751963f0d24011a7f37a3e0d..9df7e3b26830e1b2e4972569b3ab46f9f4751b9e 100644 (file)
@@ -1,4 +1,3 @@
-
 /* ====================================================================
    Licensed to the Apache Software Foundation (ASF) under one or more
    contributor license agreements.  See the NOTICE file distributed with
    See the License for the specific language governing permissions and
    limitations under the License.
 ==================================================================== */
-        
+
 package org.apache.poi.hssf.record;
 
 import junit.framework.TestCase;
 
-import org.apache.poi.hssf.record.MergeCellsRecord.MergedRegion;
+import org.apache.poi.ss.util.CellRangeAddress;
 
 /**
  * Make sure the merge cells record behaves
  * @author Danny Mui (dmui at apache dot org)
  *
  */
-public class TestMergeCellsRecord extends TestCase {
+public final class TestMergeCellsRecord extends TestCase {
    
    /**
     * Make sure when a clone is called, we actually clone it.
@@ -40,13 +39,13 @@ public class TestMergeCellsRecord extends TestCase {
       
       assertNotSame("Merged and cloned objects are the same", merge, clone);
       
-      MergedRegion mergeRegion = merge.getAreaAt(0);
-      MergedRegion cloneRegion = clone.getAreaAt(0);
+      CellRangeAddress mergeRegion = merge.getAreaAt(0);
+      CellRangeAddress cloneRegion = clone.getAreaAt(0);
       assertNotSame("Should not point to same objects when cloning", mergeRegion, cloneRegion);
-      assertEquals("New Clone Row From doesnt match", mergeRegion.row_from, cloneRegion.row_from);
-      assertEquals("New Clone Row To doesnt match", mergeRegion.row_to, cloneRegion.row_to);
-      assertEquals("New Clone Col From doesnt match", mergeRegion.col_from, cloneRegion.col_from);
-      assertEquals("New Clone Col To doesnt match", mergeRegion.col_to, cloneRegion.col_to);      
+      assertEquals("New Clone Row From doesnt match", mergeRegion.getFirstRow(), cloneRegion.getFirstRow());
+      assertEquals("New Clone Row To doesnt match", mergeRegion.getLastRow(), cloneRegion.getLastRow());
+      assertEquals("New Clone Col From doesnt match", mergeRegion.getFirstColumn(), cloneRegion.getFirstColumn());
+      assertEquals("New Clone Col To doesnt match", mergeRegion.getLastColumn(), cloneRegion.getLastColumn());      
       
       merge.removeAreaAt(0);
       assertNotNull("Clone's item not removed", clone.getAreaAt(0));
index 2ae8230d5e95dd1f36195d6ac3c878bb80805499..efe352b24463e056152a2a52d44d8f49c286b438 100644 (file)
@@ -28,8 +28,8 @@ import org.apache.poi.hssf.record.CFHeaderRecord;
 import org.apache.poi.hssf.record.CFRuleRecord;
 import org.apache.poi.hssf.record.RecordFactory;
 import org.apache.poi.hssf.record.CFRuleRecord.ComparisonOperator;
-import org.apache.poi.hssf.record.cf.CellRange;
 import org.apache.poi.hssf.usermodel.HSSFWorkbook;
+import org.apache.poi.ss.util.CellRangeAddress;
 
 /**
  * Tests the serialization and deserialization of the CFRecordsAggregate
@@ -49,9 +49,9 @@ public final class TestCFRecordsAggregate extends TestCase
                CFRuleRecord rule2 = CFRuleRecord.create(workbook, ComparisonOperator.BETWEEN, "2", "5");
                CFRuleRecord rule3 = CFRuleRecord.create(workbook, ComparisonOperator.GE, "100", null);
                header.setNumberOfConditionalFormats(3);
-               CellRange[] cellRanges = {
-                               new CellRange(0,1,0,0),
-                               new CellRange(0,1,2,2),
+               CellRangeAddress[] cellRanges = {
+                               new CellRangeAddress(0,1,0,0),
+                               new CellRangeAddress(0,1,2,2),
                };
                header.setCellRanges(cellRanges);
                recs.add(header);
@@ -97,11 +97,4 @@ public final class TestCFRecordsAggregate extends TestCase
                assertEquals(2, cellRanges.length);
                assertEquals(3, header.getNumberOfConditionalFormats());
        }
-
-       public static void main(String[] ignored_args)
-       {
-               System.out.println("Testing org.apache.poi.hssf.record.aggregates.CFRecordsAggregate");
-               junit.textui.TestRunner.run(TestCFRecordsAggregate.class);
-       }
-       
 }
index 2ba196d55a189951c0ffda48ac00702f0de2a8b3..e01bc2ff768621b06173720f752ba216661f8ba0 100644 (file)
@@ -17,6 +17,8 @@ limitations under the License.
 
 package org.apache.poi.hssf.record.cf;
 
+import org.apache.poi.ss.util.CellRangeAddress;
+
 import junit.framework.AssertionFailedError;
 import junit.framework.TestCase;
 
@@ -25,15 +27,15 @@ import junit.framework.TestCase;
  */
 public final class TestCellRange extends TestCase
 {
-       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);
+       private static final CellRangeAddress biggest     = createCR( 0, -1, 0,-1);
+       private static final CellRangeAddress tenthColumn = createCR( 0, -1,10,10);
+       private static final CellRangeAddress tenthRow    = createCR(10, 10, 0,-1);
+       private static final CellRangeAddress box10x10    = createCR( 0, 10, 0,10);
+       private static final CellRangeAddress box9x9      = createCR( 0,  9, 0, 9);
+       private static final CellRangeAddress box10to20c  = createCR( 0, 10,10,20);
+       private static final CellRangeAddress oneCell     = createCR(10, 10,10,10);
 
-       private static final CellRange[] sampleRanges = {
+       private static final CellRangeAddress[] sampleRanges = {
                biggest, tenthColumn, tenthRow, box10x10, box9x9, box10to20c, oneCell,
        };
        
@@ -54,9 +56,9 @@ public final class TestCellRange extends TestCase
         * @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) {
+       private static CellRangeAddress createCR(int firstRow, int lastRow, int firstCol, int lastCol) {
                // max row & max col limit as per BIFF8
-               return new CellRange(
+               return new CellRangeAddress(
                                firstRow, 
                                lastRow == -1 ? 0xFFFF : lastRow, 
                                firstCol,
@@ -65,89 +67,89 @@ public final class TestCellRange extends TestCase
        
        public void testContainsMethod()
        {
-               CellRange [] ranges = sampleRanges;
+               CellRangeAddress [] ranges = sampleRanges;
                for(int i=0; i!=ranges.length;i++)
                {
                        for(int j=0; j!=ranges.length;j++)
                        {
                                boolean expectedResult = containsExpectedResults[i][j];
-                               assertEquals("("+i+","+j+"): ", expectedResult, ranges[i].contains(ranges[j]));
+                               assertEquals("("+i+","+j+"): ", expectedResult, CellRangeUtil.contains(ranges[i], ranges[j]));
                        }
                }
        }
 
-       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 CellRangeAddress col1     = createCR( 0, -1, 1,1);
+       private static final CellRangeAddress col2     = createCR( 0, -1, 2,2);
+       private static final CellRangeAddress row1     = createCR( 1,  1, 0,-1);
+       private static final CellRangeAddress row2     = createCR( 2,  2, 0,-1);
 
-       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);
+       private static final CellRangeAddress box0     = createCR( 0, 2, 0,2);
+       private static final CellRangeAddress box1     = createCR( 0, 1, 0,1);
+       private static final CellRangeAddress box2     = createCR( 0, 1, 2,3);
+       private static final CellRangeAddress box3     = createCR( 2, 3, 0,1);
+       private static final CellRangeAddress box4     = createCR( 2, 3, 2,3);
+       private static final CellRangeAddress box5     = createCR( 1, 3, 1,3);
 
        public void testHasSharedBorderMethod()
        {
-               assertFalse(col1.hasExactSharedBorder(col1));
-               assertFalse(col2.hasExactSharedBorder(col2));
-               assertTrue(col1.hasExactSharedBorder(col2));
-               assertTrue(col2.hasExactSharedBorder(col1));
+               assertFalse(CellRangeUtil.hasExactSharedBorder(col1, col1));
+               assertFalse(CellRangeUtil.hasExactSharedBorder(col2, col2));
+               assertTrue(CellRangeUtil.hasExactSharedBorder(col1, col2));
+               assertTrue(CellRangeUtil.hasExactSharedBorder(col2, col1));
 
-               assertFalse(row1.hasExactSharedBorder(row1));
-               assertFalse(row2.hasExactSharedBorder(row2));
-               assertTrue(row1.hasExactSharedBorder(row2));
-               assertTrue(row2.hasExactSharedBorder(row1));
+               assertFalse(CellRangeUtil.hasExactSharedBorder(row1, row1));
+               assertFalse(CellRangeUtil.hasExactSharedBorder(row2, row2));
+               assertTrue(CellRangeUtil.hasExactSharedBorder(row1, row2));
+               assertTrue(CellRangeUtil.hasExactSharedBorder(row2, row1));
                
-               assertFalse(row1.hasExactSharedBorder(col1));
-               assertFalse(row1.hasExactSharedBorder(col2));
-               assertFalse(col1.hasExactSharedBorder(row1));
-               assertFalse(col2.hasExactSharedBorder(row1));
-               assertFalse(row2.hasExactSharedBorder(col1));
-               assertFalse(row2.hasExactSharedBorder(col2));
-               assertFalse(col1.hasExactSharedBorder(row2));
-               assertFalse(col2.hasExactSharedBorder(row2));
-               assertTrue(col2.hasExactSharedBorder(col1));
+               assertFalse(CellRangeUtil.hasExactSharedBorder(row1, col1));
+               assertFalse(CellRangeUtil.hasExactSharedBorder(row1, col2));
+               assertFalse(CellRangeUtil.hasExactSharedBorder(col1, row1));
+               assertFalse(CellRangeUtil.hasExactSharedBorder(col2, row1));
+               assertFalse(CellRangeUtil.hasExactSharedBorder(row2, col1));
+               assertFalse(CellRangeUtil.hasExactSharedBorder(row2, col2));
+               assertFalse(CellRangeUtil.hasExactSharedBorder(col1, row2));
+               assertFalse(CellRangeUtil.hasExactSharedBorder(col2, row2));
+               assertTrue(CellRangeUtil.hasExactSharedBorder(col2, col1));
                
-               assertFalse(box1.hasExactSharedBorder(box1));
-               assertTrue(box1.hasExactSharedBorder(box2));
-               assertTrue(box1.hasExactSharedBorder(box3));
-               assertFalse(box1.hasExactSharedBorder(box4));
+               assertFalse(CellRangeUtil.hasExactSharedBorder(box1, box1));
+               assertTrue(CellRangeUtil.hasExactSharedBorder(box1, box2));
+               assertTrue(CellRangeUtil.hasExactSharedBorder(box1, box3));
+               assertFalse(CellRangeUtil.hasExactSharedBorder(box1, box4));
                
-               assertTrue(box2.hasExactSharedBorder(box1));
-               assertFalse(box2.hasExactSharedBorder(box2));
-               assertFalse(box2.hasExactSharedBorder(box3));
-               assertTrue(box2.hasExactSharedBorder(box4));
+               assertTrue(CellRangeUtil.hasExactSharedBorder(box2, box1));
+               assertFalse(CellRangeUtil.hasExactSharedBorder(box2, box2));
+               assertFalse(CellRangeUtil.hasExactSharedBorder(box2, box3));
+               assertTrue(CellRangeUtil.hasExactSharedBorder(box2, box4));
                
-               assertTrue(box3.hasExactSharedBorder(box1));
-               assertFalse(box3.hasExactSharedBorder(box2));
-               assertFalse(box3.hasExactSharedBorder(box3));
-               assertTrue(box3.hasExactSharedBorder(box4));
+               assertTrue(CellRangeUtil.hasExactSharedBorder(box3, box1));
+               assertFalse(CellRangeUtil.hasExactSharedBorder(box3, box2));
+               assertFalse(CellRangeUtil.hasExactSharedBorder(box3, box3));
+               assertTrue(CellRangeUtil.hasExactSharedBorder(box3, box4));
                
-               assertFalse(box4.hasExactSharedBorder(box1));
-               assertTrue(box4.hasExactSharedBorder(box2));
-               assertTrue(box4.hasExactSharedBorder(box3));
-               assertFalse(box4.hasExactSharedBorder(box4));
+               assertFalse(CellRangeUtil.hasExactSharedBorder(box4, box1));
+               assertTrue(CellRangeUtil.hasExactSharedBorder(box4, box2));
+               assertTrue(CellRangeUtil.hasExactSharedBorder(box4, box3));
+               assertFalse(CellRangeUtil.hasExactSharedBorder(box4, box4));
        }
 
        public void testIntersectMethod()
        {
-               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));
-               assertEquals(CellRange.NO_INTERSECTION,box3.intersect(box2));
-               assertEquals(CellRange.INSIDE,box0.intersect(box1));
-               assertEquals(CellRange.INSIDE,box0.intersect(box0));
-               assertEquals(CellRange.ENCLOSES,box1.intersect(box0));
-               assertEquals(CellRange.INSIDE,tenthColumn.intersect(oneCell));
-               assertEquals(CellRange.ENCLOSES,oneCell.intersect(tenthColumn));
-               assertEquals(CellRange.OVERLAP,tenthColumn.intersect(tenthRow));
-               assertEquals(CellRange.OVERLAP,tenthRow.intersect(tenthColumn));
-               assertEquals(CellRange.INSIDE,tenthColumn.intersect(tenthColumn));
-               assertEquals(CellRange.INSIDE,tenthRow.intersect(tenthRow));
+               assertEquals(CellRangeUtil.OVERLAP, CellRangeUtil.intersect(box0, box5));
+               assertEquals(CellRangeUtil.OVERLAP, CellRangeUtil.intersect(box5, box0));
+               assertEquals(CellRangeUtil.NO_INTERSECTION, CellRangeUtil.intersect(box1, box4));
+               assertEquals(CellRangeUtil.NO_INTERSECTION, CellRangeUtil.intersect(box4, box1));
+               assertEquals(CellRangeUtil.NO_INTERSECTION, CellRangeUtil.intersect(box2, box3));
+               assertEquals(CellRangeUtil.NO_INTERSECTION, CellRangeUtil.intersect(box3, box2));
+               assertEquals(CellRangeUtil.INSIDE, CellRangeUtil.intersect(box0, box1));
+               assertEquals(CellRangeUtil.INSIDE, CellRangeUtil.intersect(box0, box0));
+               assertEquals(CellRangeUtil.ENCLOSES, CellRangeUtil.intersect(box1, box0));
+               assertEquals(CellRangeUtil.INSIDE, CellRangeUtil.intersect(tenthColumn, oneCell));
+               assertEquals(CellRangeUtil.ENCLOSES, CellRangeUtil.intersect(oneCell, tenthColumn));
+               assertEquals(CellRangeUtil.OVERLAP, CellRangeUtil.intersect(tenthColumn, tenthRow));
+               assertEquals(CellRangeUtil.OVERLAP, CellRangeUtil.intersect(tenthRow, tenthColumn));
+               assertEquals(CellRangeUtil.INSIDE, CellRangeUtil.intersect(tenthColumn, tenthColumn));
+               assertEquals(CellRangeUtil.INSIDE, CellRangeUtil.intersect(tenthRow, tenthRow));
        }
        
        /**
@@ -155,7 +157,7 @@ public final class TestCellRange extends TestCase
         * =$C:$IV,$B$1:$B$8,$B$10:$B$65536,$A:$A
         */
        public void testCreate() {
-               CellRange cr;
+               CellRangeAddress cr;
                
                cr = createCR(0, -1, 2, 255); // $C:$IV
                confirmRange(cr, false, true);
@@ -172,7 +174,7 @@ public final class TestCellRange extends TestCase
                cr = createCR(0, -1, 0, 0); // $A:$A
        }
 
-       private static void confirmRange(CellRange cr, boolean isFullRow, boolean isFullColumn) {
+       private static void confirmRange(CellRangeAddress cr, boolean isFullRow, boolean isFullColumn) {
                assertEquals("isFullRowRange", isFullRow, cr.isFullRowRange());
                assertEquals("isFullColumnRange", isFullColumn, cr.isFullColumnRange());
        }
diff --git a/src/testcases/org/apache/poi/hssf/usermodel/StreamUtility.java b/src/testcases/org/apache/poi/hssf/usermodel/StreamUtility.java
new file mode 100644 (file)
index 0000000..45ae4ac
--- /dev/null
@@ -0,0 +1,131 @@
+/* ====================================================================
+   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 java.io.IOException;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Utility class to help test code verify that generated files do not differ from proof copies in 
+ * any significant detail.  Normally this task would be simple except for the presence of artifacts
+ * in the file that change every time it is generated.  Usually these volatile artifacts are  
+ * time-stamps, user names, or other machine dependent parameters.
+ *  
+ * @author Josh Micich
+ */
+public final class StreamUtility {
+
+       /**
+        * Compares two streams with expected differences in specified regions.  The streams are
+     * expected to be of equal length and comparison is always byte for byte.  That is -
+     * differences can only involve exchanging each individual byte for another single byte.<br>
+     * Both input streams are closed.
+     *  
+        * @param allowableDifferenceRegions array of integer pairs: (offset, length). 
+        * Any differences encountered in these regions of the streams will be ignored
+        * @return <code>null</code> if streams are identical, else the 
+        * byte indexes of differing data.  If streams were different lengths,
+        * the returned indexes will be -1 and the length of the shorter stream
+        */
+       public static int[] diffStreams(InputStream isA, InputStream isB, int[] allowableDifferenceRegions) {
+
+               if((allowableDifferenceRegions.length % 2) != 0) {
+                       throw new RuntimeException("allowableDifferenceRegions length is odd");
+               }
+               boolean success = false;
+               int[] result;
+        try {
+            result = diffInternal(isA, isB, allowableDifferenceRegions);
+            success = true;
+        } catch (IOException e) {
+            throw new RuntimeException(e);
+        } finally {
+            close(isA, success);
+            close(isB, success);
+        }
+        return result;
+       }
+
+       /**
+        * @param success <code>false</code> if the outer method is throwing an exception.
+        */
+    private static void close(InputStream is, boolean success) {
+        try {
+            is.close();
+        } catch (IOException e) {
+            if(success) {
+                // this is a new error. ok to throw
+                throw new RuntimeException(e);
+            }
+            // else don't subvert original exception. just print stack trace for this one
+            e.printStackTrace();
+        }
+    }
+
+    private static int[] diffInternal(InputStream isA, InputStream isB, int[] allowableDifferenceRegions)
+            throws IOException {
+               int offset = 0;
+               List temp = new ArrayList();
+               while (true) {
+                       int b = isA.read();
+                       int b2 = isB.read();
+                       if (b == -1) {
+                               // EOF
+                               if (b2 == -1) {
+                                       return toPrimitiveIntArray(temp);
+                               }
+                               return new int[] { -1, offset, };
+                       }
+                       if (b2 == -1) {
+                               return new int[] { -1, offset, };
+                       }
+                       if (b != b2 && !isIgnoredRegion(allowableDifferenceRegions, offset)) {
+                               temp.add(new Integer(offset));
+                       }
+                       offset++;
+               }
+    }
+
+       private static boolean isIgnoredRegion(int[] allowableDifferenceRegions, int offset) {
+               for (int i = 0; i < allowableDifferenceRegions.length; i+=2) {
+                       int start = allowableDifferenceRegions[i];
+                       int end = start + allowableDifferenceRegions[i+1];
+                       if(start <= offset && offset < end) {
+                               return true;
+                       }
+               }
+               return false;
+       }
+
+       private static int[] toPrimitiveIntArray(List temp) {
+               int nItems = temp.size();
+               if(nItems < 1) {
+                       return null;
+               }
+               Integer[] boxInts = new Integer[nItems];
+               temp.toArray(boxInts);
+               
+               int[] result = new int[nItems];
+               for (int i = 0; i < result.length; i++) {
+                       result[i] = boxInts[i].intValue();
+               }
+               return result;
+       }
+}
index 69937d1034f45d767a49b8325d8929810527c668..541fb893a5783c6164310d26071547a03194675e 100644 (file)
@@ -28,6 +28,7 @@ import junit.framework.AssertionFailedError;
 import junit.framework.TestCase;
 
 import org.apache.poi.ss.util.Region;
+import org.apache.poi.ss.util.CellRangeAddress;
 
 import org.apache.poi.hssf.HSSFTestDataSamples;
 import org.apache.poi.hssf.model.Workbook;
@@ -302,15 +303,14 @@ public final class TestBugs extends TestCase {
 
     /**
      * Merged regions were being removed from the parent in cloned sheets
-     * @throws Exception
      */
     public void test22720() {
        HSSFWorkbook workBook = new HSSFWorkbook();
        workBook.createSheet("TEST");
        HSSFSheet template = workBook.getSheetAt(0);
 
-       template.addMergedRegion(new Region(0, (short)0, 1, (short)2));
-       template.addMergedRegion(new Region(1, (short)0, 2, (short)2));
+       template.addMergedRegion(new CellRangeAddress(0, 1, 0, 2));
+       template.addMergedRegion(new CellRangeAddress(1, 2, 0, 2));
 
        HSSFSheet clone = workBook.cloneSheet(0);
        int originalMerged = template.getNumMergedRegions();
@@ -318,20 +318,20 @@ public final class TestBugs extends TestCase {
 
 //        remove merged regions from clone
        for (int i=template.getNumMergedRegions()-1; i>=0; i--) {
-         clone.removeMergedRegion(i);
+           clone.removeMergedRegion(i);
        }
 
       assertEquals("Original Sheet's Merged Regions were removed", originalMerged, template.getNumMergedRegions());
 //        check if template's merged regions are OK
        if (template.getNumMergedRegions()>0) {
-          // fetch the first merged region...EXCEPTION OCCURS HERE
-          template.getMergedRegionAt(0);
+            // fetch the first merged region...EXCEPTION OCCURS HERE
+            template.getMergedRegion(0);
        }
        //make sure we dont exception
 
     }
 
-    /*Tests read and write of Unicode strings in formula results
+    /**Tests read and write of Unicode strings in formula results
      * bug and testcase submitted by Sompop Kumnoonsate
      * The file contains THAI unicode characters.
      */
index 6a0b15158126b6b3263e2ac50ded004eb27a32d8..4704b3ed84e1545ed23327950b49b3674fea1d43 100644 (file)
@@ -21,6 +21,7 @@ package org.apache.poi.hssf.usermodel;
 
 import junit.framework.TestCase;
 
+import org.apache.poi.ss.util.CellRangeAddress;
 import org.apache.poi.ss.util.Region;
 
 /**
@@ -29,23 +30,15 @@ import org.apache.poi.ss.util.Region;
  *  add that record to the sheet in the testCloneSheetBasic method. 
  * @author  avik
  */
-public class TestCloneSheet extends TestCase {
+public final class TestCloneSheet extends TestCase {
 
-       public TestCloneSheet(String arg0) {
-               super(arg0);
-       }
-       
        public void testCloneSheetBasic(){
-               try{
-                       HSSFWorkbook b = new HSSFWorkbook();
-                       HSSFSheet s = b.createSheet("Test");
-                       s.addMergedRegion(new Region((short)0,(short)0,(short)1,(short)1));
-                       HSSFSheet clonedSheet = b.cloneSheet(0);
-                       
-                       assertEquals("One merged area", 1, clonedSheet.getNumMergedRegions());
-
-               }
-               catch(Exception e){e.printStackTrace();fail(e.getMessage());}
+               HSSFWorkbook b = new HSSFWorkbook();
+               HSSFSheet s = b.createSheet("Test");
+               s.addMergedRegion(new CellRangeAddress(0, 1, 0, 1));
+               HSSFSheet clonedSheet = b.cloneSheet(0);
+               
+               assertEquals("One merged area", 1, clonedSheet.getNumMergedRegions());
        }
 
    /**
@@ -66,5 +59,4 @@ public class TestCloneSheet extends TestCase {
       
       assertTrue("Row 3 still should be broken", clone.isRowBroken(3));
    }
-   
 }
index 2c28edf3ac6e529d7da72ea20143fb9118acd30f..91382af06ddf2823aba0d24dfcca0dd1f5c7da4b 100644 (file)
 
 package org.apache.poi.hssf.usermodel;
 
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.PrintStream;
+
+import junit.framework.AssertionFailedError;
 import junit.framework.TestCase;
 
-import org.apache.poi.hssf.util.*;
+import org.apache.poi.hssf.HSSFTestDataSamples;
+import org.apache.poi.hssf.eventmodel.ERFListener;
+import org.apache.poi.hssf.eventmodel.EventRecordFactory;
+import org.apache.poi.hssf.record.DVRecord;
+import org.apache.poi.hssf.record.RecordFormatException;
+import org.apache.poi.hssf.util.HSSFColor;
+import org.apache.poi.poifs.filesystem.POIFSFileSystem;
+import org.apache.poi.ss.util.CellRangeAddress;
+import org.apache.poi.ss.util.CellRangeAddressList;
 import org.apache.poi.ss.util.Region;
 
-import java.io.*;
-import java.util.*;
-import java.text.SimpleDateFormat;
-
 /**
- * <p>Title: TestDataValidation</p>
- * <p>Description: Class for testing Excel's data validation mechanism
- *    Second test :
- *        -
- * </p>
+ * Class for testing Excel's data validation mechanism
+ *
  * @author Dragos Buleandra ( dragos.buleandra@trade2b.ro )
  */
-public class TestDataValidation extends TestCase
-{
-  public TestDataValidation(String name)
-  {
-    super(name);
-  }
-
-  protected void setUp()
-  {
-    String filename = System.getProperty("HSSF.testdata.path");
-    if (filename == null)
-    {
-       System.setProperty("HSSF.testdata.path", "src/testcases/org/apache/poi/hssf/data");
-    }
-  }
-
-  public void testDataValidation() throws Exception
-  {
-    System.out.println("\nTest no. 2 - Test Excel's Data validation mechanism");
-    String resultFile   = System.getProperty("java.io.tmpdir")+File.separator+"TestDataValidation.xls";
-    HSSFWorkbook wb = new HSSFWorkbook();
-
-    HSSFCellStyle style_1 = this.createStyle( wb, HSSFCellStyle.ALIGN_LEFT );
-    HSSFCellStyle style_2 = this.createStyle( wb, HSSFCellStyle.ALIGN_CENTER );
-    HSSFCellStyle style_3 = this.createStyle( wb, HSSFCellStyle.ALIGN_CENTER, HSSFColor.GREY_25_PERCENT.index, true );
-    HSSFCellStyle style_4 = this.createHeaderStyle(wb);
-    HSSFDataValidation data_validation = null;
-
-    //data validation's number types
-    System.out.print("    Create sheet for Data Validation's number types ... ");
-    HSSFSheet fSheet = wb.createSheet("Number types");
-
-    //"Whole number" validation type
-    this.createDVTypeRow( wb, 0, style_3, "Whole number");
-    this.createHeaderRow( wb, 0, style_4 );
-
-    short start_row = (short)fSheet.getPhysicalNumberOfRows();
-    data_validation = new HSSFDataValidation((short)(start_row),(short)0,(short)(start_row),(short)0);
-    data_validation.setDataValidationType(HSSFDataValidation.DATA_TYPE_INTEGER);
-    data_validation.setOperator(HSSFDataValidation.OPERATOR_BETWEEN);
-    data_validation.setFirstFormula("2");
-    data_validation.setSecondFormula("6");
-    data_validation.createErrorBox("Invalid input !", "Something is wrong ; check condition !");
-    data_validation.createPromptBox("Hi , dear user !", "So , you just selected me ! Thanks !");
-    fSheet.addValidationData(data_validation);
-    this.writeDataValidationSettings( fSheet, style_1, style_2, "Between 2 and 6 ", true, true, true );
-    this.writeOtherSettings( fSheet, style_1, "Error box type = STOP" );
-
-    data_validation.setFirstRow((short)(start_row+1));
-    data_validation.setLastRow((short)(start_row+1));
-    data_validation.setEmptyCellAllowed(false);
-    data_validation.setOperator(HSSFDataValidation.OPERATOR_NOT_BETWEEN);
-    data_validation.setErrorStyle(HSSFDataValidation.ERROR_STYLE_INFO);
-    fSheet.addValidationData(data_validation);
-    this.writeDataValidationSettings( fSheet, style_1, style_2, "Not between 2 and 6 ", false, true, true );
-    this.writeOtherSettings( fSheet, style_1, "Error box type = INFO" );
-
-    data_validation.setFirstRow((short)(start_row+2));
-    data_validation.setLastRow((short)(start_row+2));
-    data_validation.setEmptyCellAllowed(false);
-    data_validation.setShowPromptBox(false);
-    data_validation.setFirstFormula("3");
-    data_validation.setSecondFormula(null);
-    data_validation.setOperator(HSSFDataValidation.OPERATOR_EQUAL);
-    data_validation.setErrorStyle(HSSFDataValidation.ERROR_STYLE_WARNING);
-    fSheet.addValidationData(data_validation);
-    this.writeDataValidationSettings( fSheet, style_1, style_2, "Equal to 3", false, false, true );
-    this.writeOtherSettings( fSheet, style_1, "Error box type = WARNING" );
-
-    data_validation.setFirstRow((short)(start_row+3));
-    data_validation.setLastRow((short)(start_row+3));
-    data_validation.setEmptyCellAllowed(false);
-    data_validation.setShowPromptBox(false);
-    data_validation.setShowErrorBox(false);
-    data_validation.setFirstFormula("3");
-    data_validation.setSecondFormula(null);
-    data_validation.setOperator(HSSFDataValidation.OPERATOR_NOT_EQUAL);
-    fSheet.addValidationData(data_validation);
-    this.writeDataValidationSettings( fSheet, style_1, style_2, "Not equal to 3", false, false, false );
-    this.writeOtherSettings( fSheet, style_1, "-" );
-
-    data_validation.setFirstRow((short)(start_row+4));
-    data_validation.setLastRow((short)(start_row+4));
-    data_validation.setEmptyCellAllowed(true);
-    data_validation.setShowPromptBox(false);
-    data_validation.setShowErrorBox(false);
-    data_validation.setFirstFormula("3");
-    data_validation.setSecondFormula(null);
-    data_validation.setOperator(HSSFDataValidation.OPERATOR_GREATER_THAN);
-    fSheet.addValidationData(data_validation);
-    this.writeDataValidationSettings( fSheet, style_1, style_2, "Greater than 3", true, false, false );
-    this.writeOtherSettings( fSheet, style_1, "-" );
-
-    data_validation.setFirstRow((short)(start_row+5));
-    data_validation.setLastRow((short)(start_row+5));
-    data_validation.setEmptyCellAllowed(true);
-    data_validation.setShowPromptBox(true);
-    data_validation.setShowErrorBox(false);
-    data_validation.setFirstFormula("3");
-    data_validation.setSecondFormula(null);
-    data_validation.setOperator(HSSFDataValidation.OPERATOR_LESS_THAN);
-    fSheet.addValidationData(data_validation);
-    this.writeDataValidationSettings( fSheet, style_1, style_2, "Less than 3", true, true, false );
-    this.writeOtherSettings( fSheet, style_1, "-" );
-
-    data_validation.setFirstRow((short)(start_row+6));
-    data_validation.setLastRow((short)(start_row+6));
-    data_validation.setEmptyCellAllowed(true);
-    data_validation.setShowPromptBox(false);
-    data_validation.setErrorStyle(HSSFDataValidation.ERROR_STYLE_STOP);
-    data_validation.setShowErrorBox(true);
-    data_validation.setFirstFormula("4");
-    data_validation.setSecondFormula(null);
-    data_validation.setOperator(HSSFDataValidation.OPERATOR_GREATER_OR_EQUAL);
-    fSheet.addValidationData(data_validation);
-    this.writeDataValidationSettings( fSheet, style_1, style_2, "Greater than or equal to 4", true, false, true );
-    this.writeOtherSettings( fSheet, style_1, "Error box type = STOP" );
-
-    data_validation.setFirstRow((short)(start_row+7));
-    data_validation.setLastRow((short)(start_row+7));
-    data_validation.setEmptyCellAllowed(false);
-    data_validation.setShowPromptBox(true);
-    data_validation.setShowErrorBox(false);
-    data_validation.setFirstFormula("4");
-    data_validation.setSecondFormula(null);
-    data_validation.setOperator(HSSFDataValidation.OPERATOR_LESS_OR_EQUAL);
-    fSheet.addValidationData(data_validation);
-    this.writeDataValidationSettings( fSheet, style_1, style_2, "Less than or equal to 4", false, true, false );
-    this.writeOtherSettings( fSheet, style_1, "-" );
-
-    //"Decimal" validation type
-    this.createDVTypeRow( wb, 0, style_3, "Decimal");
-    this.createHeaderRow( wb, 0, style_4 );
-
-    start_row += (short)(8+4);
-    data_validation = new HSSFDataValidation((short)(start_row),(short)0,(short)(start_row),(short)0);
-    data_validation.setDataValidationType(HSSFDataValidation.DATA_TYPE_DECIMAL);
-    data_validation.setOperator(HSSFDataValidation.OPERATOR_BETWEEN);
-    data_validation.setFirstFormula("2");
-    data_validation.setSecondFormula("6");
-    data_validation.createErrorBox("Invalid input !", "Something is wrong ; check condition !");
-    data_validation.createPromptBox("Hi , dear user !", "So , you just selected me ! Thanks !");
-    fSheet.addValidationData(data_validation);
-    this.writeDataValidationSettings( fSheet, style_1, style_2, "Between 2 and 6 ", true, true, true );
-    this.writeOtherSettings( fSheet, style_1, "Error box type = STOP" );
-
-    data_validation.setFirstRow((short)(start_row+1));
-    data_validation.setLastRow((short)(start_row+1));
-    data_validation.setEmptyCellAllowed(false);
-    data_validation.setOperator(HSSFDataValidation.OPERATOR_NOT_BETWEEN);
-    data_validation.setErrorStyle(HSSFDataValidation.ERROR_STYLE_INFO);
-    fSheet.addValidationData(data_validation);
-    this.writeDataValidationSettings( fSheet, style_1, style_2, "Not between 2 and 6 ", false, true, true );
-    this.writeOtherSettings( fSheet, style_1, "Error box type = INFO" );
-
-    data_validation.setFirstRow((short)(start_row+2));
-    data_validation.setLastRow((short)(start_row+2));
-    data_validation.setEmptyCellAllowed(false);
-    data_validation.setShowPromptBox(false);
-    data_validation.setFirstFormula("3");
-    data_validation.setSecondFormula(null);
-    data_validation.setOperator(HSSFDataValidation.OPERATOR_EQUAL);
-    data_validation.setErrorStyle(HSSFDataValidation.ERROR_STYLE_WARNING);
-    fSheet.addValidationData(data_validation);
-    this.writeDataValidationSettings( fSheet, style_1, style_2, "Equal to 3", false, false, true );
-    this.writeOtherSettings( fSheet, style_1, "Error box type = WARNING" );
-
-    data_validation.setFirstRow((short)(start_row+3));
-    data_validation.setLastRow((short)(start_row+3));
-    data_validation.setEmptyCellAllowed(false);
-    data_validation.setShowPromptBox(false);
-    data_validation.setShowErrorBox(false);
-    data_validation.setFirstFormula("3");
-    data_validation.setSecondFormula(null);
-    data_validation.setOperator(HSSFDataValidation.OPERATOR_NOT_EQUAL);
-    fSheet.addValidationData(data_validation);
-    this.writeDataValidationSettings( fSheet, style_1, style_2, "Not equal to 3", false, false, false );
-    this.writeOtherSettings( fSheet, style_1, "-" );
-
-    data_validation.setFirstRow((short)(start_row+4));
-    data_validation.setLastRow((short)(start_row+4));
-    data_validation.setEmptyCellAllowed(true);
-    data_validation.setShowPromptBox(false);
-    data_validation.setShowErrorBox(false);
-    data_validation.setFirstFormula("3");
-    data_validation.setSecondFormula(null);
-    data_validation.setOperator(HSSFDataValidation.OPERATOR_GREATER_THAN);
-    fSheet.addValidationData(data_validation);
-    this.writeDataValidationSettings( fSheet, style_1, style_2, "Greater than 3", true, false, false );
-    this.writeOtherSettings( fSheet, style_1, "-" );
-
-    data_validation.setFirstRow((short)(start_row+5));
-    data_validation.setLastRow((short)(start_row+5));
-    data_validation.setEmptyCellAllowed(true);
-    data_validation.setShowPromptBox(true);
-    data_validation.setShowErrorBox(false);
-    data_validation.setFirstFormula("3");
-    data_validation.setSecondFormula(null);
-    data_validation.setOperator(HSSFDataValidation.OPERATOR_LESS_THAN);
-    fSheet.addValidationData(data_validation);
-    this.writeDataValidationSettings( fSheet, style_1, style_2, "Less than 3", true, true, false );
-    this.writeOtherSettings( fSheet, style_1, "-" );
-
-    data_validation.setFirstRow((short)(start_row+6));
-    data_validation.setLastRow((short)(start_row+6));
-    data_validation.setEmptyCellAllowed(true);
-    data_validation.setShowPromptBox(false);
-    data_validation.setErrorStyle(HSSFDataValidation.ERROR_STYLE_STOP);
-    data_validation.setShowErrorBox(true);
-    data_validation.setFirstFormula("4");
-    data_validation.setSecondFormula(null);
-    data_validation.setOperator(HSSFDataValidation.OPERATOR_GREATER_OR_EQUAL);
-    fSheet.addValidationData(data_validation);
-    this.writeDataValidationSettings( fSheet, style_1, style_2, "Greater than or equal to 4", true, false, true );
-    this.writeOtherSettings( fSheet, style_1, "Error box type = STOP" );
-
-    data_validation.setFirstRow((short)(start_row+7));
-    data_validation.setLastRow((short)(start_row+7));
-    data_validation.setEmptyCellAllowed(false);
-    data_validation.setShowPromptBox(true);
-    data_validation.setShowErrorBox(false);
-    data_validation.setFirstFormula("4");
-    data_validation.setSecondFormula(null);
-    data_validation.setOperator(HSSFDataValidation.OPERATOR_LESS_OR_EQUAL);
-    fSheet.addValidationData(data_validation);
-    this.writeDataValidationSettings( fSheet, style_1, style_2, "Less than or equal to 4", false, true, false );
-    this.writeOtherSettings( fSheet, style_1, "-" );
-
-    System.out.println("done !");
-
-    //"List" Data Validation type
-    /** @todo  List*/
-    System.out.print("    Create sheet for 'List' Data Validation type ... ");
-    fSheet = wb.createSheet("Lists");
-
-    this.createDVTypeRow( wb, 1, style_3, "Explicit lists - list items are explicitly provided");
-    this.createDVDeescriptionRow( wb, 1, style_3, "Disadvantage - sum of item's length should be less than 255 characters");
-    this.createHeaderRow( wb, 1, style_4 );
-
-    start_row = (short)fSheet.getPhysicalNumberOfRows();
-    data_validation = new HSSFDataValidation((short)(start_row),(short)0,(short)(start_row),(short)0);
-    data_validation.setDataValidationType(HSSFDataValidation.DATA_TYPE_LIST);
-    data_validation.setFirstFormula("1+2+3");
-    data_validation.setSecondFormula(null);
-    data_validation.setSurppressDropDownArrow(false);
-    data_validation.createErrorBox("Invalid input !", "Something is wrong ; check condition !");
-    data_validation.createPromptBox("Hi , dear user !", "So , you just selected me ! Thanks !");
-    fSheet.addValidationData(data_validation);
-    this.writeDataValidationSettings( fSheet, style_1, style_2, "POIFS,HSSF,HWPF,HPSF", true, true, true );
-    this.writeOtherSettings( fSheet, style_1, "Error box type=STOP ; In-cell dropdown=yes" );
-
-    data_validation = new HSSFDataValidation((short)(start_row+1),(short)0,(short)(start_row+1),(short)0);
-    data_validation.setDataValidationType(HSSFDataValidation.DATA_TYPE_LIST);
-    data_validation.setFirstFormula("4+5+6+7");
-    data_validation.setSecondFormula(null);
-    data_validation.setSurppressDropDownArrow(false);
-    data_validation.setEmptyCellAllowed(false);
-    data_validation.setShowPromptBox(false);
-    data_validation.createErrorBox("Invalid input !", "Something is wrong ; check condition !");
-    fSheet.addValidationData(data_validation);
-    this.writeDataValidationSettings( fSheet, style_1, style_2, "POIFS,HSSF,HWPF,HPSF", false, false, true );
-    this.writeOtherSettings( fSheet, style_1, "Error box type=STOP ; In-cell dropdown=yes" );
-
-    data_validation = new HSSFDataValidation((short)(start_row+2),(short)0,(short)(start_row+2),(short)0);
-    data_validation.setDataValidationType(HSSFDataValidation.DATA_TYPE_LIST);
-    data_validation.setFirstFormula("7+21");
-    data_validation.setSecondFormula(null);
-    data_validation.setSurppressDropDownArrow(true);
-    data_validation.createErrorBox("Invalid input !", "Something is wrong ; check condition !");
-    data_validation.createPromptBox("Hi , dear user !", "So , you just selected me ! Thanks !");
-    fSheet.addValidationData(data_validation);
-    this.writeDataValidationSettings( fSheet, style_1, style_2, "POIFS,HSSF,HWPF,HPSF", true, true, true );
-    this.writeOtherSettings( fSheet, style_1, "Error box type=STOP ; In-cell dropdown=no" );
-
-    data_validation = new HSSFDataValidation((short)(start_row+3),(short)0,(short)(start_row+3),(short)0);
-    data_validation.setDataValidationType(HSSFDataValidation.DATA_TYPE_LIST);
-    data_validation.setFirstFormula("8/2");
-    data_validation.setSecondFormula(null);
-    data_validation.setSurppressDropDownArrow(true);
-    data_validation.createErrorBox("Invalid input !", "Something is wrong ; check condition !");
-    data_validation.setEmptyCellAllowed(false);
-    data_validation.setShowPromptBox(false);
-    fSheet.addValidationData(data_validation);
-    this.writeDataValidationSettings( fSheet, style_1, style_2, "POIFS,HSSF,HWPF,HPSF", false, false, true );
-    this.writeOtherSettings( fSheet, style_1, "Error box type=STOP ; In-cell dropdown=no" );
-
-    this.createDVTypeRow( wb, 1, style_3, "Reference lists - list items are taken from others cells");
-    this.createDVDeescriptionRow( wb, 1, style_3, "Advantage - no restriction regarding the sum of item's length");
-    this.createHeaderRow( wb, 1, style_4 );
-
-    start_row += (short)(4+5);
-    String cellStrValue = "a b c d e f g h i j k l m n o p r s t u v x y z w 0 1 2 3 4 "+
-                          "a b c d e f g h i j k l m n o p r s t u v x y z w 0 1 2 3 4 "+
-                          "a b c d e f g h i j k l m n o p r s t u v x y z w 0 1 2 3 4 "+
-                          "a b c d e f g h i j k l m n o p r s t u v x y z w 0 1 2 3 4 ";
-
-    String strFormula = "$A$100:$A$120";
-    data_validation = new HSSFDataValidation((short)(start_row),(short)0,(short)(start_row),(short)0);
-    data_validation.setDataValidationType(HSSFDataValidation.DATA_TYPE_LIST);
-    data_validation.setFirstFormula(strFormula);
-    data_validation.setSecondFormula(null);
-    data_validation.setSurppressDropDownArrow(false);
-    data_validation.createErrorBox("Invalid input !", "Something is wrong ; check condition !");
-    data_validation.createPromptBox("Hi , dear user !", "So , you just selected me ! Thanks !");
-    fSheet.addValidationData(data_validation);
-    this.writeDataValidationSettings( fSheet, style_1, style_2, strFormula, true, true, true );
-    this.writeOtherSettings( fSheet, style_1, "Error box type=STOP ; In-cell dropdown=yes" );
-
-    data_validation = new HSSFDataValidation((short)(start_row+1),(short)0,(short)(start_row+1),(short)0);
-    data_validation.setDataValidationType(HSSFDataValidation.DATA_TYPE_LIST);
-    data_validation.setFirstFormula(strFormula);
-    data_validation.setSecondFormula(null);
-    data_validation.setSurppressDropDownArrow(false);
-    data_validation.setEmptyCellAllowed(false);
-    data_validation.setShowPromptBox(false);
-    data_validation.createErrorBox("Invalid input !", "Something is wrong ; check condition !");
-    fSheet.addValidationData(data_validation);
-    this.writeDataValidationSettings( fSheet, style_1, style_2, strFormula, false, false, true );
-    this.writeOtherSettings( fSheet, style_1, "Error box type=STOP ; In-cell dropdown=yes" );
-
-    data_validation = new HSSFDataValidation((short)(start_row+2),(short)0,(short)(start_row+2),(short)0);
-    data_validation.setDataValidationType(HSSFDataValidation.DATA_TYPE_LIST);
-    data_validation.setFirstFormula(strFormula);
-    data_validation.setSecondFormula(null);
-    data_validation.setSurppressDropDownArrow(true);
-    data_validation.createErrorBox("Invalid input !", "Something is wrong ; check condition !");
-    data_validation.createPromptBox("Hi , dear user !", "So , you just selected me ! Thanks !");
-    fSheet.addValidationData(data_validation);
-    this.writeDataValidationSettings( fSheet, style_1, style_2, strFormula, true, true, true );
-    this.writeOtherSettings( fSheet, style_1, "Error box type=STOP ; In-cell dropdown=no" );
-
-    data_validation = new HSSFDataValidation((short)(start_row+3),(short)0,(short)(start_row+3),(short)0);
-    data_validation.setDataValidationType(HSSFDataValidation.DATA_TYPE_LIST);
-    data_validation.setFirstFormula(strFormula);
-    data_validation.setSecondFormula(null);
-    data_validation.setSurppressDropDownArrow(true);
-    data_validation.createErrorBox("Invalid input !", "Something is wrong ; check condition !");
-    data_validation.setEmptyCellAllowed(false);
-    data_validation.setShowPromptBox(false);
-    fSheet.addValidationData(data_validation);
-    this.writeDataValidationSettings( fSheet, style_1, style_2, strFormula, false, false, true );
-    this.writeOtherSettings( fSheet, style_1, "Error box type=STOP ; In-cell dropdown=no" );
-
-    for (int i=100; i<=120; i++)
-    {
-       HSSFRow currRow = fSheet.createRow(i);
-       currRow.createCell((short)0).setCellValue(cellStrValue);
-//       currRow.hide( true );
-    }
-
-    System.out.println("done !");
-
-    //Date/Time Validation type
-    System.out.print("    Create sheet for 'Date' and 'Time' Data Validation types ... ");
-    fSheet = wb.createSheet("Date_Time");
-    SimpleDateFormat df = new SimpleDateFormat("m/d/yyyy");
-    HSSFDataFormat dataFormat = wb.createDataFormat();
-    short fmtDate = dataFormat.getFormat("m/d/yyyy");
-    short fmtTime = dataFormat.getFormat("h:mm");
-    HSSFCellStyle cellStyle_data = wb.createCellStyle();
-    cellStyle_data.setDataFormat(fmtDate);
-    HSSFCellStyle cellStyle_time = wb.createCellStyle();
-    cellStyle_time.setDataFormat(fmtTime);
-
-    this.createDVTypeRow( wb, 2, style_3, "Date ( cells are already formated as date - m/d/yyyy)");
-    this.createHeaderRow( wb, 2, style_4 );
-
-    start_row = (short)fSheet.getPhysicalNumberOfRows();
-    data_validation = new HSSFDataValidation((short)(start_row),(short)0,(short)(start_row),(short)0);
-    data_validation.setDataValidationType(HSSFDataValidation.DATA_TYPE_DATE);
-    data_validation.setOperator(HSSFDataValidation.OPERATOR_BETWEEN);
-
-    data_validation.setFirstFormula( String.valueOf((int)HSSFDateUtil.getExcelDate(df.parse("1/2/2004"))) );
-    data_validation.setSecondFormula( String.valueOf((int)HSSFDateUtil.getExcelDate(df.parse("1/6/2004"))) );
-
-    data_validation.createErrorBox("Invalid input !", "Something is wrong ; check condition !");
-    data_validation.createPromptBox("Hi , dear user !", "So , you just selected me ! Thanks !");
-    fSheet.addValidationData(data_validation);
-    this.writeDataValidationSettings( fSheet, style_1, style_2, "Between 1/2/2004 and 1/6/2004 ", true, true, true );
-    this.setCellFormat( fSheet, cellStyle_data );
-    this.writeOtherSettings( fSheet, style_1, "Error box type = STOP" );
-
-    data_validation.setFirstRow((short)(start_row+1));
-    data_validation.setLastRow((short)(start_row+1));
-    data_validation.setEmptyCellAllowed(false);
-    data_validation.setOperator(HSSFDataValidation.OPERATOR_NOT_BETWEEN);
-    data_validation.setErrorStyle(HSSFDataValidation.ERROR_STYLE_INFO);
-    fSheet.addValidationData(data_validation);
-    this.writeDataValidationSettings( fSheet, style_1, style_2, "Not between 1/2/2004 and 1/6/2004 ", false, true, true );
-    this.setCellFormat( fSheet, cellStyle_data );
-    this.writeOtherSettings( fSheet, style_1, "Error box type = INFO" );
-
-    data_validation.setFirstRow((short)(start_row+2));
-    data_validation.setLastRow((short)(start_row+2));
-    data_validation.setEmptyCellAllowed(false);
-    data_validation.setShowPromptBox(false);
-    data_validation.setFirstFormula(String.valueOf((int)HSSFDateUtil.getExcelDate(df.parse("3/2/2004"))));
-    data_validation.setSecondFormula(null);
-    data_validation.setOperator(HSSFDataValidation.OPERATOR_EQUAL);
-    data_validation.setErrorStyle(HSSFDataValidation.ERROR_STYLE_WARNING);
-    fSheet.addValidationData(data_validation);
-    this.writeDataValidationSettings( fSheet, style_1, style_2, "Equal to 3/2/2004", false, false, true );
-    this.setCellFormat( fSheet, cellStyle_data );
-    this.writeOtherSettings( fSheet, style_1, "Error box type = WARNING" );
-
-    data_validation.setFirstRow((short)(start_row+3));
-    data_validation.setLastRow((short)(start_row+3));
-    data_validation.setEmptyCellAllowed(false);
-    data_validation.setShowPromptBox(false);
-    data_validation.setShowErrorBox(false);
-    data_validation.setFirstFormula(String.valueOf((int)HSSFDateUtil.getExcelDate(df.parse("3/2/2004"))));
-    data_validation.setSecondFormula(null);
-    data_validation.setOperator(HSSFDataValidation.OPERATOR_NOT_EQUAL);
-    fSheet.addValidationData(data_validation);
-    this.writeDataValidationSettings( fSheet, style_1, style_2, "Not equal to 3/2/2004", false, false, false );
-    this.setCellFormat( fSheet, cellStyle_data );
-    this.writeOtherSettings( fSheet, style_1, "-" );
-
-    data_validation.setFirstRow((short)(start_row+4));
-    data_validation.setLastRow((short)(start_row+4));
-    data_validation.setEmptyCellAllowed(true);
-    data_validation.setShowPromptBox(false);
-    data_validation.setShowErrorBox(false);
-    data_validation.setFirstFormula(String.valueOf((int)HSSFDateUtil.getExcelDate(df.parse("3/2/2004"))));
-    data_validation.setSecondFormula(null);
-    data_validation.setOperator(HSSFDataValidation.OPERATOR_GREATER_THAN);
-    fSheet.addValidationData(data_validation);
-    this.writeDataValidationSettings( fSheet, style_1, style_2, "Greater than 3/2/2004", true, false, false );
-    this.setCellFormat( fSheet, cellStyle_data );
-    this.writeOtherSettings( fSheet, style_1, "-" );
-
-    data_validation.setFirstRow((short)(start_row+5));
-    data_validation.setLastRow((short)(start_row+5));
-    data_validation.setEmptyCellAllowed(true);
-    data_validation.setShowPromptBox(true);
-    data_validation.setShowErrorBox(false);
-    data_validation.setFirstFormula(String.valueOf((int)HSSFDateUtil.getExcelDate(df.parse("3/2/2004"))));
-    data_validation.setSecondFormula(null);
-    data_validation.setOperator(HSSFDataValidation.OPERATOR_LESS_THAN);
-    fSheet.addValidationData(data_validation);
-    this.writeDataValidationSettings( fSheet, style_1, style_2, "Less than 3/2/2004", true, true, false );
-    this.setCellFormat( fSheet, cellStyle_data );
-    this.writeOtherSettings( fSheet, style_1, "-" );
-
-    data_validation.setFirstRow((short)(start_row+6));
-    data_validation.setLastRow((short)(start_row+6));
-    data_validation.setEmptyCellAllowed(true);
-    data_validation.setShowPromptBox(false);
-    data_validation.setErrorStyle(HSSFDataValidation.ERROR_STYLE_STOP);
-    data_validation.setShowErrorBox(true);
-    data_validation.setFirstFormula(String.valueOf((int)HSSFDateUtil.getExcelDate(df.parse("3/2/2004"))));
-    data_validation.setSecondFormula(null);
-    data_validation.setOperator(HSSFDataValidation.OPERATOR_GREATER_OR_EQUAL);
-    fSheet.addValidationData(data_validation);
-    this.writeDataValidationSettings( fSheet, style_1, style_2, "Greater than or equal to 3/2/2004", true, false, true );
-    this.setCellFormat( fSheet, cellStyle_data );
-    this.writeOtherSettings( fSheet, style_1, "Error box type = STOP" );
-
-    data_validation.setFirstRow((short)(start_row+7));
-    data_validation.setLastRow((short)(start_row+7));
-    data_validation.setEmptyCellAllowed(false);
-    data_validation.setShowPromptBox(true);
-    data_validation.setShowErrorBox(false);
-    data_validation.setFirstFormula(String.valueOf((int)HSSFDateUtil.getExcelDate(df.parse("3/4/2004"))));
-    data_validation.setSecondFormula(null);
-    data_validation.setOperator(HSSFDataValidation.OPERATOR_LESS_OR_EQUAL);
-    fSheet.addValidationData(data_validation);
-    this.writeDataValidationSettings( fSheet, style_1, style_2, "Less than or equal to 3/4/2004", false, true, false );
-    this.setCellFormat( fSheet, cellStyle_data );
-    this.writeOtherSettings( fSheet, style_1, "-" );
-
-    //"Time" validation type
-    this.createDVTypeRow( wb, 2, style_3, "Time ( cells are already formated as time - h:mm)");
-    this.createHeaderRow( wb, 2, style_4 );
-
-    df = new SimpleDateFormat("hh:mm");
-
-    start_row += (short)(8+4);
-    data_validation = new HSSFDataValidation((short)(start_row),(short)0,(short)(start_row),(short)0);
-    data_validation.setDataValidationType(HSSFDataValidation.DATA_TYPE_TIME);
-    data_validation.setOperator(HSSFDataValidation.OPERATOR_BETWEEN);
-    data_validation.setFirstFormula(String.valueOf(HSSFDateUtil.getExcelDate(df.parse("12:00"))));
-    data_validation.setSecondFormula(String.valueOf(HSSFDateUtil.getExcelDate(df.parse("16:00"))));
-    data_validation.createErrorBox("Invalid input !", "Something is wrong ; check condition !");
-    data_validation.createPromptBox("Hi , dear user !", "So , you just selected me ! Thanks !");
-    fSheet.addValidationData(data_validation);
-    this.writeDataValidationSettings( fSheet, style_1, style_2, "Between 12:00 and 16:00 ", true, true, true );
-    this.setCellFormat( fSheet, cellStyle_time );
-    this.writeOtherSettings( fSheet, style_1, "Error box type = STOP" );
-
-    data_validation.setFirstRow((short)(start_row+1));
-    data_validation.setLastRow((short)(start_row+1));
-    data_validation.setEmptyCellAllowed(false);
-    data_validation.setOperator(HSSFDataValidation.OPERATOR_NOT_BETWEEN);
-    data_validation.setErrorStyle(HSSFDataValidation.ERROR_STYLE_INFO);
-    fSheet.addValidationData(data_validation);
-    this.writeDataValidationSettings( fSheet, style_1, style_2, "Not between 12:00 and 16:00 ", false, true, true );
-    this.setCellFormat( fSheet, cellStyle_time );
-    this.writeOtherSettings( fSheet, style_1, "Error box type = INFO" );
-
-    data_validation.setFirstRow((short)(start_row+2));
-    data_validation.setLastRow((short)(start_row+2));
-    data_validation.setEmptyCellAllowed(false);
-    data_validation.setShowPromptBox(false);
-    data_validation.setFirstFormula(String.valueOf((int)HSSFDateUtil.getExcelDate(df.parse("13:35"))));
-    data_validation.setSecondFormula(null);
-    data_validation.setOperator(HSSFDataValidation.OPERATOR_EQUAL);
-    data_validation.setErrorStyle(HSSFDataValidation.ERROR_STYLE_WARNING);
-    fSheet.addValidationData(data_validation);
-    this.writeDataValidationSettings( fSheet, style_1, style_2, "Equal to 13:35", false, false, true );
-    this.setCellFormat( fSheet, cellStyle_time );
-    this.writeOtherSettings( fSheet, style_1, "Error box type = WARNING" );
-
-    data_validation.setFirstRow((short)(start_row+3));
-    data_validation.setLastRow((short)(start_row+3));
-    data_validation.setEmptyCellAllowed(false);
-    data_validation.setShowPromptBox(false);
-    data_validation.setShowErrorBox(false);
-    data_validation.setFirstFormula(String.valueOf(HSSFDateUtil.getExcelDate(df.parse("13:35"))));
-    data_validation.setSecondFormula(null);
-    data_validation.setOperator(HSSFDataValidation.OPERATOR_NOT_EQUAL);
-    fSheet.addValidationData(data_validation);
-    this.writeDataValidationSettings( fSheet, style_1, style_2, "Not equal to 13:35", false, false, false );
-    this.setCellFormat( fSheet, cellStyle_time );
-    this.writeOtherSettings( fSheet, style_1, "-" );
-
-    data_validation.setFirstRow((short)(start_row+4));
-    data_validation.setLastRow((short)(start_row+4));
-    data_validation.setEmptyCellAllowed(true);
-    data_validation.setShowPromptBox(false);
-    data_validation.setShowErrorBox(false);
-    data_validation.setFirstFormula(String.valueOf(HSSFDateUtil.getExcelDate(df.parse("12:00"))));
-    data_validation.setSecondFormula(null);
-    data_validation.setOperator(HSSFDataValidation.OPERATOR_GREATER_THAN);
-    fSheet.addValidationData(data_validation);
-    this.writeDataValidationSettings( fSheet, style_1, style_2, "Greater than 12:00", true, false, false );
-    this.setCellFormat( fSheet, cellStyle_time );
-    this.writeOtherSettings( fSheet, style_1, "-" );
-
-    data_validation.setFirstRow((short)(start_row+5));
-    data_validation.setLastRow((short)(start_row+5));
-    data_validation.setEmptyCellAllowed(true);
-    data_validation.setShowPromptBox(true);
-    data_validation.setShowErrorBox(false);
-    data_validation.setFirstFormula(String.valueOf(HSSFDateUtil.getExcelDate(df.parse("12:00"))));
-    data_validation.setSecondFormula(null);
-    data_validation.setOperator(HSSFDataValidation.OPERATOR_LESS_THAN);
-    fSheet.addValidationData(data_validation);
-    this.writeDataValidationSettings( fSheet, style_1, style_2, "Less than 12:00", true, true, false );
-    this.setCellFormat( fSheet, cellStyle_time );
-    this.writeOtherSettings( fSheet, style_1, "-" );
-
-    data_validation.setFirstRow((short)(start_row+6));
-    data_validation.setLastRow((short)(start_row+6));
-    data_validation.setEmptyCellAllowed(true);
-    data_validation.setShowPromptBox(false);
-    data_validation.setErrorStyle(HSSFDataValidation.ERROR_STYLE_STOP);
-    data_validation.setShowErrorBox(true);
-    data_validation.setFirstFormula(String.valueOf(HSSFDateUtil.getExcelDate(df.parse("14:00"))));
-    data_validation.setSecondFormula(null);
-    data_validation.setOperator(HSSFDataValidation.OPERATOR_GREATER_OR_EQUAL);
-    fSheet.addValidationData(data_validation);
-    this.writeDataValidationSettings( fSheet, style_1, style_2, "Greater than or equal to 14:00", true, false, true );
-    this.setCellFormat( fSheet, cellStyle_time );
-    this.writeOtherSettings( fSheet, style_1, "Error box type = STOP" );
-
-    data_validation.setFirstRow((short)(start_row+7));
-    data_validation.setLastRow((short)(start_row+7));
-    data_validation.setEmptyCellAllowed(false);
-    data_validation.setShowPromptBox(true);
-    data_validation.setShowErrorBox(false);
-    data_validation.setFirstFormula(String.valueOf(HSSFDateUtil.getExcelDate(df.parse("14:00"))));
-    data_validation.setSecondFormula(null);
-    data_validation.setOperator(HSSFDataValidation.OPERATOR_LESS_OR_EQUAL);
-    fSheet.addValidationData(data_validation);
-    this.writeDataValidationSettings( fSheet, style_1, style_2, "Less than or equal to 14:00", false, true, false );
-    this.setCellFormat( fSheet, cellStyle_time );
-    this.writeOtherSettings( fSheet, style_1, "-" );
-
-    System.out.println("done !");
-
-    //"Text length" validation type
-    System.out.print("    Create sheet for 'Text length' Data Validation type... ");
-    fSheet = wb.createSheet("Text length");
-    this.createHeaderRow( wb, 3, style_4 );
-
-    data_validation = new HSSFDataValidation((short)1,(short)0,(short)1,(short)0);
-    data_validation.setDataValidationType(HSSFDataValidation.DATA_TYPE_TEXT_LENGTH);
-    data_validation.setOperator(HSSFDataValidation.OPERATOR_BETWEEN);
-    data_validation.setFirstFormula("2");
-    data_validation.setSecondFormula("6");
-    data_validation.createErrorBox("Invalid input !", "Something is wrong ; check condition !");
-    data_validation.createPromptBox("Hi , dear user !", "So , you just selected me ! Thanks !");
-    fSheet.addValidationData(data_validation);
-    this.writeDataValidationSettings( fSheet, style_1, style_2, "Between 2 and 6 ", true, true, true );
-    this.writeOtherSettings( fSheet, style_1, "Error box type = STOP" );
-
-    data_validation.setFirstRow((short)2);
-    data_validation.setLastRow((short)2);
-    data_validation.setEmptyCellAllowed(false);
-    data_validation.setOperator(HSSFDataValidation.OPERATOR_NOT_BETWEEN);
-    data_validation.setErrorStyle(HSSFDataValidation.ERROR_STYLE_INFO);
-    fSheet.addValidationData(data_validation);
-    this.writeDataValidationSettings( fSheet, style_1, style_2, "Not between 2 and 6 ", false, true, true );
-    this.writeOtherSettings( fSheet, style_1, "Error box type = INFO" );
-
-    data_validation.setFirstRow((short)3);
-    data_validation.setLastRow((short)3);
-    data_validation.setEmptyCellAllowed(false);
-    data_validation.setShowPromptBox(false);
-    data_validation.setFirstFormula("3");
-    data_validation.setSecondFormula(null);
-    data_validation.setOperator(HSSFDataValidation.OPERATOR_EQUAL);
-    data_validation.setErrorStyle(HSSFDataValidation.ERROR_STYLE_WARNING);
-    fSheet.addValidationData(data_validation);
-    this.writeDataValidationSettings( fSheet, style_1, style_2, "Equal to 3", false, false, true );
-    this.writeOtherSettings( fSheet, style_1, "Error box type = WARNING" );
-
-    data_validation.setFirstRow((short)4);
-    data_validation.setLastRow((short)4);
-    data_validation.setEmptyCellAllowed(false);
-    data_validation.setShowPromptBox(false);
-    data_validation.setShowErrorBox(false);
-    data_validation.setFirstFormula("3");
-    data_validation.setSecondFormula(null);
-    data_validation.setOperator(HSSFDataValidation.OPERATOR_NOT_EQUAL);
-    fSheet.addValidationData(data_validation);
-    this.writeDataValidationSettings( fSheet, style_1, style_2, "Not equal to 3", false, false, false );
-    this.writeOtherSettings( fSheet, style_1, "-" );
-
-    data_validation.setFirstRow((short)5);
-    data_validation.setLastRow((short)5);
-    data_validation.setEmptyCellAllowed(true);
-    data_validation.setShowPromptBox(false);
-    data_validation.setShowErrorBox(false);
-    data_validation.setFirstFormula("3");
-    data_validation.setSecondFormula(null);
-    data_validation.setOperator(HSSFDataValidation.OPERATOR_GREATER_THAN);
-    fSheet.addValidationData(data_validation);
-    this.writeDataValidationSettings( fSheet, style_1, style_2, "Greater than 3", true, false, false );
-    this.writeOtherSettings( fSheet, style_1, "-" );
-
-    data_validation.setFirstRow((short)6);
-    data_validation.setLastRow((short)6);
-    data_validation.setEmptyCellAllowed(true);
-    data_validation.setShowPromptBox(true);
-    data_validation.setShowErrorBox(false);
-    data_validation.setFirstFormula("3");
-    data_validation.setSecondFormula(null);
-    data_validation.setOperator(HSSFDataValidation.OPERATOR_LESS_THAN);
-    fSheet.addValidationData(data_validation);
-    this.writeDataValidationSettings( fSheet, style_1, style_2, "Less than 3", true, true, false );
-    this.writeOtherSettings( fSheet, style_1, "-" );
-
-    data_validation.setFirstRow((short)7);
-    data_validation.setLastRow((short)7);
-    data_validation.setEmptyCellAllowed(true);
-    data_validation.setShowPromptBox(false);
-    data_validation.setErrorStyle(HSSFDataValidation.ERROR_STYLE_STOP);
-    data_validation.setShowErrorBox(true);
-    data_validation.setFirstFormula("4");
-    data_validation.setSecondFormula(null);
-    data_validation.setOperator(HSSFDataValidation.OPERATOR_GREATER_OR_EQUAL);
-    fSheet.addValidationData(data_validation);
-    this.writeDataValidationSettings( fSheet, style_1, style_2, "Greater than or equal to 4", true, false, true );
-    this.writeOtherSettings( fSheet, style_1, "Error box type = STOP" );
-
-    data_validation.setFirstRow((short)8);
-    data_validation.setLastRow((short)8);
-    data_validation.setEmptyCellAllowed(false);
-    data_validation.setShowPromptBox(true);
-    data_validation.setShowErrorBox(false);
-    data_validation.setFirstFormula("4");
-    data_validation.setSecondFormula(null);
-    data_validation.setOperator(HSSFDataValidation.OPERATOR_LESS_OR_EQUAL);
-    fSheet.addValidationData(data_validation);
-    this.writeDataValidationSettings( fSheet, style_1, style_2, "Less than or equal to 4", false, true, false );
-    this.writeOtherSettings( fSheet, style_1, "-" );
-    System.out.println("done !");
-
-    //Custom Validation type
-    System.out.print("    Create sheet for 'Custom' Data Validation type ... ");
-    fSheet = wb.createSheet("Custom");
-    this.createHeaderRow( wb, 4, style_4 );
-
-    data_validation = new HSSFDataValidation((short)1,(short)0,(short)1,(short)0);
-    data_validation.setDataValidationType(HSSFDataValidation.DATA_TYPE_FORMULA);
-    data_validation.setFirstFormula("ISNUMBER($A2)");
-    data_validation.setSecondFormula(null);
-    data_validation.setShowPromptBox(true);
-    data_validation.setShowErrorBox(true);
-    data_validation.createErrorBox("Invalid input !", "Something is wrong ; check condition !");
-    data_validation.createPromptBox("Hi , dear user !", "So , you just selected me ! Thanks !");
-    fSheet.addValidationData(data_validation);
-    this.writeDataValidationSettings( fSheet, style_1, style_2, "ISNUMBER(A2)", true, true, true );
-    this.writeOtherSettings( fSheet, style_1, "Error box type = STOP" );
-
-    data_validation = new HSSFDataValidation((short)2,(short)0,(short)2,(short)0);
-    data_validation.setDataValidationType(HSSFDataValidation.DATA_TYPE_FORMULA);
-    data_validation.setFirstFormula("IF(SUM(A2:A3)=5,TRUE,FALSE)");
-    data_validation.setSecondFormula(null);
-    data_validation.setShowPromptBox(false);
-    data_validation.setShowErrorBox(true);
-    data_validation.setErrorStyle(HSSFDataValidation.ERROR_STYLE_WARNING);
-    data_validation.createErrorBox("Invalid input !", "Something is wrong ; check condition !");
-    data_validation.setEmptyCellAllowed(false);
-    fSheet.addValidationData(data_validation);
-    this.writeDataValidationSettings( fSheet, style_1, style_2, "IF(SUM(A2:A3)=5,TRUE,FALSE)", false, false, true );
-    this.writeOtherSettings( fSheet, style_1, "Error box type = WARNING" );
-
-    System.out.println("done !");
-
-    //so , everything it's ok for now ; it remains for you to open the file
-    System.out.println("\n    Everything it's ok since we've got so far -:) !\n"+
-                       "    In order to complete the test , it remains for you to open the file \n"+
-                       "    and see if there are four sheets , as described !");
-    System.out.println("        File was saved in \""+resultFile+"\"");
-
-    FileOutputStream fileOut = new FileOutputStream(resultFile);
-    wb.write(fileOut);
-    fileOut.close();
-  }
-
-  private void createDVTypeRow(  HSSFWorkbook wb, int sheetNo , HSSFCellStyle cellStyle, String strTypeDescription)
-  {
-    HSSFSheet sheet = wb.getSheetAt(sheetNo);
-    HSSFRow row = sheet.createRow(sheet.getPhysicalNumberOfRows());
-    row = sheet.createRow(sheet.getPhysicalNumberOfRows());
-    sheet.addMergedRegion(new Region((short)(sheet.getPhysicalNumberOfRows()-1),(short)0,(short)(sheet.getPhysicalNumberOfRows()-1),(short)5));
-    HSSFCell cell = row.createCell((short)0);
-    cell.setCellValue(strTypeDescription);
-    cell.setCellStyle(cellStyle);
-    row = sheet.createRow(sheet.getPhysicalNumberOfRows());
-  }
-
-  private void createDVDeescriptionRow(  HSSFWorkbook wb, int sheetNo , HSSFCellStyle cellStyle, String strTypeDescription )
-  {
-    HSSFSheet sheet = wb.getSheetAt(sheetNo);
-    HSSFRow row = sheet.getRow(sheet.getPhysicalNumberOfRows()-1);
-    sheet.addMergedRegion(new Region((short)(sheet.getPhysicalNumberOfRows()-1),(short)0,(short)(sheet.getPhysicalNumberOfRows()-1),(short)5));
-    HSSFCell cell = row.createCell((short)0);
-    cell.setCellValue(strTypeDescription);
-    cell.setCellStyle(cellStyle);
-    row = sheet.createRow(sheet.getPhysicalNumberOfRows());
-  }
-
-  private void createHeaderRow( HSSFWorkbook wb, int sheetNo , HSSFCellStyle cellStyle )
-  {
-      HSSFSheet sheet = wb.getSheetAt(sheetNo);
-      HSSFRow row = sheet.createRow(sheet.getPhysicalNumberOfRows());
-      row.setHeight((short)400);
-      for ( int i=0; i<6; i++ )
-      {
-         row.createCell((short)i).setCellStyle( cellStyle );
-         if ( i==2 || i==3 || i==4 )
-         {
-            sheet.setColumnWidth( (short) i, (short) 3500);
-         }
-         else if ( i== 5)
-         {
-            sheet.setColumnWidth( (short) i, (short) 10000);
-         }
-         else
-         {
-            sheet.setColumnWidth( (short) i, (short) 8000);
-         }
-      }
-      HSSFCell cell = row.getCell((short)0);
-      cell.setCellValue("Data validation cells");
-      cell = row.getCell((short)1);
-      cell.setCellValue("Condition");
-      cell = row.getCell((short)2);
-      cell.setCellValue("Allow blank");
-      cell = row.getCell((short)3);
-      cell.setCellValue("Prompt box");
-      cell = row.getCell((short)4);
-      cell.setCellValue("Error box");
-      cell = row.getCell((short)5);
-      cell.setCellValue("Other settings");
-  }
-
-  private HSSFCellStyle createHeaderStyle(HSSFWorkbook wb)
-  {
-    HSSFFont font = wb.createFont();
-    font.setColor( HSSFColor.WHITE.index );
-    font.setBoldweight(HSSFFont.BOLDWEIGHT_BOLD);
-
-    HSSFCellStyle cellStyle = wb.createCellStyle();
-    cellStyle.setFillForegroundColor(HSSFColor.BLUE_GREY.index);
-    cellStyle.setFillPattern(HSSFCellStyle.SOLID_FOREGROUND);
-    cellStyle.setAlignment(HSSFCellStyle.ALIGN_CENTER);
-    cellStyle.setVerticalAlignment(HSSFCellStyle.VERTICAL_CENTER);
-    cellStyle.setBorderLeft(HSSFCellStyle.BORDER_THIN);
-    cellStyle.setLeftBorderColor(HSSFColor.WHITE.index);
-    cellStyle.setBorderTop(HSSFCellStyle.BORDER_THIN);
-    cellStyle.setTopBorderColor(HSSFColor.WHITE.index);
-    cellStyle.setBorderRight(HSSFCellStyle.BORDER_THIN);
-    cellStyle.setRightBorderColor(HSSFColor.WHITE.index);
-    cellStyle.setBorderBottom(HSSFCellStyle.BORDER_THIN);
-    cellStyle.setBottomBorderColor(HSSFColor.WHITE.index);
-    cellStyle.setFont(font);
-    return cellStyle;
-  }
-
-  private HSSFCellStyle createStyle( HSSFWorkbook wb, short h_align, short color, boolean bold )
-  {
-    HSSFFont font = wb.createFont();
-    if ( bold )
-    {
-      font.setBoldweight(HSSFFont.BOLDWEIGHT_BOLD);
-    }
-
-    HSSFCellStyle cellStyle = wb.createCellStyle();
-    cellStyle.setFont(font);
-    cellStyle.setFillForegroundColor(color);
-    cellStyle.setFillPattern(HSSFCellStyle.SOLID_FOREGROUND);
-    cellStyle.setVerticalAlignment(HSSFCellStyle.VERTICAL_CENTER);
-    cellStyle.setAlignment(h_align);
-    cellStyle.setBorderLeft(HSSFCellStyle.BORDER_THIN);
-    cellStyle.setLeftBorderColor(HSSFColor.BLACK.index);
-    cellStyle.setBorderTop(HSSFCellStyle.BORDER_THIN);
-    cellStyle.setTopBorderColor(HSSFColor.BLACK.index);
-    cellStyle.setBorderRight(HSSFCellStyle.BORDER_THIN);
-    cellStyle.setRightBorderColor(HSSFColor.BLACK.index);
-    cellStyle.setBorderBottom(HSSFCellStyle.BORDER_THIN);
-    cellStyle.setBottomBorderColor(HSSFColor.BLACK.index);
-
-    return cellStyle;
-  }
-
-  private HSSFCellStyle createStyle( HSSFWorkbook wb, short h_align )
-  {
-     return this.createStyle(wb, h_align, HSSFColor.WHITE.index, false);
-  }
-
-  private void writeDataValidationSettings( HSSFSheet sheet, HSSFCellStyle style_1, HSSFCellStyle style_2, String strCondition, boolean allowEmpty, boolean inputBox, boolean errorBox  )
-  {
-    HSSFRow row = sheet.createRow( sheet.getPhysicalNumberOfRows() );
-    //condition's string
-    HSSFCell cell = row.createCell((short)1);
-    cell.setCellStyle(style_1);
-    cell.setCellValue(strCondition);
-    //allow empty cells
-    cell = row.createCell((short)2);
-    cell.setCellStyle(style_2);
-    cell.setCellValue( ((allowEmpty) ? "yes" : "no") );
-    //show input box
-    cell = row.createCell((short)3);
-    cell.setCellStyle(style_2);
-    cell.setCellValue( ((inputBox) ? "yes" : "no") );
-    //show error box
-    cell = row.createCell((short)4);
-    cell.setCellStyle(style_2);
-    cell.setCellValue( ((errorBox) ? "yes" : "no") );
-  }
-
-  private void setCellFormat( HSSFSheet sheet, HSSFCellStyle cell_style )
-  {
-    HSSFRow row = sheet.getRow( sheet.getPhysicalNumberOfRows() -1 );
-    HSSFCell cell = row.createCell((short)0);
-    cell.setCellStyle(cell_style);
-  }
-
-  private void writeOtherSettings( HSSFSheet sheet, HSSFCellStyle style, String strStettings )
-  {
-     HSSFRow row = sheet.getRow( sheet.getPhysicalNumberOfRows() -1 );
-     HSSFCell cell = row.createCell((short)5);
-     cell.setCellStyle(style);
-     cell.setCellValue(strStettings);
-  }
-
-  public static void main(String[] args)
-  {
-    junit.textui.TestRunner.run(TestDataValidation.class);
+public final class TestDataValidation extends TestCase {
+
+       /** Convenient access to ERROR_STYLE constants */
+       /*package*/ static final HSSFDataValidation.ErrorStyle ES = null;
+       /** Convenient access to OPERATOR constants */
+       /*package*/ static final DVConstraint.ValidationType VT = null;
+       /** Convenient access to OPERATOR constants */
+       /*package*/ static final DVConstraint.OperatorType OP = null;
+
+       private static void log(String msg) {
+               if (false) { // successful tests should be silent
+                       System.out.println(msg);
+               }      
+       }
+  
+       private static final class ValidationAdder {
+         
+               private final HSSFCellStyle _style_1;
+               private final HSSFCellStyle _style_2;
+               private  final int _validationType;
+               private final HSSFSheet _sheet;
+               private int _currentRowIndex;
+               private final HSSFCellStyle _cellStyle;
+
+               public ValidationAdder(HSSFSheet fSheet, HSSFCellStyle style_1, HSSFCellStyle style_2,
+                               HSSFCellStyle cellStyle, int validationType) {
+                       _sheet = fSheet;
+                       _style_1 = style_1;
+                       _style_2 = style_2;
+                       _cellStyle = cellStyle;
+                       _validationType = validationType;
+                       _currentRowIndex = fSheet.getPhysicalNumberOfRows();
+               }
+               public void addValidation(int operatorType, String firstFormula, String secondFormula,
+                               int errorStyle, String ruleDescr, String promptDescr, 
+                               boolean allowEmpty, boolean inputBox, boolean errorBox) {
+                       String[] explicitListValues = null;
+                       
+                       addValidationInternal(operatorType, firstFormula, secondFormula, errorStyle, ruleDescr,
+                                       promptDescr, allowEmpty, inputBox, errorBox, true,
+                                       explicitListValues);
+               }
+
+               private void addValidationInternal(int operatorType, String firstFormula,
+                               String secondFormula, int errorStyle, String ruleDescr, String promptDescr,
+                               boolean allowEmpty, boolean inputBox, boolean errorBox, boolean suppressDropDown,
+                               String[] explicitListValues) {
+                       int rowNum = _currentRowIndex++;
+
+                       DVConstraint dc = createConstraint(operatorType, firstFormula, secondFormula, explicitListValues);
+
+                       HSSFDataValidation dv = new HSSFDataValidation(new CellRangeAddressList(rowNum, rowNum, 0, 0), dc);
+                       
+                       dv.setEmptyCellAllowed(allowEmpty);
+                       dv.setErrorStyle(errorStyle);
+                       dv.createErrorBox("Invalid Input", "Something is wrong - check condition!");
+                       dv.createPromptBox("Validated Cell", "Allowable values have been restricted");
+
+                       dv.setShowPromptBox(inputBox);
+                       dv.setShowErrorBox(errorBox);
+                       dv.setSuppressDropDownArrow(suppressDropDown);
+                       
+                       
+                       _sheet.addValidationData(dv);
+                       writeDataValidationSettings(_sheet, _style_1, _style_2, ruleDescr, allowEmpty,
+                                       inputBox, errorBox);
+                       if (_cellStyle != null) {
+                               HSSFRow row = _sheet.getRow(_sheet.getPhysicalNumberOfRows() - 1);
+                               HSSFCell cell = row.createCell((short) 0);
+                               cell.setCellStyle(_cellStyle);
+                       }
+                       writeOtherSettings(_sheet, _style_1, promptDescr);
+               }
+               private DVConstraint createConstraint(int operatorType, String firstFormula,
+                               String secondFormula, String[] explicitListValues) {
+                       if (_validationType == VT.LIST) {
+                               if (explicitListValues != null) {
+                                       return DVConstraint.createExplicitListConstraint(explicitListValues);
+                               }
+                               return DVConstraint.createFormulaListConstraint(firstFormula);
+                       }
+                       if (_validationType == VT.TIME) {
+                               return DVConstraint.createTimeConstraint(operatorType, firstFormula, secondFormula);
+                       }
+                       if (_validationType == VT.DATE) {
+                               return DVConstraint.createDateConstraint(operatorType, firstFormula, secondFormula, null);
+                       }
+                       if (_validationType == VT.FORMULA) {
+                               return DVConstraint.createCustomFormulaConstraint(firstFormula);
+                       }
+                       return DVConstraint.createNumericConstraint(_validationType, operatorType, firstFormula, secondFormula);
+               }
+               /**
+                * writes plain text values into cells in a tabular format to form comments readable from within 
+                * the spreadsheet.
+                */
+               private static void writeDataValidationSettings(HSSFSheet sheet, HSSFCellStyle style_1,
+                               HSSFCellStyle style_2, String strCondition, boolean allowEmpty, boolean inputBox,
+                               boolean errorBox) {
+                       HSSFRow row = sheet.createRow(sheet.getPhysicalNumberOfRows());
+                       // condition's string
+                       HSSFCell cell = row.createCell((short) 1);
+                       cell.setCellStyle(style_1);
+                       setCellValue(cell, strCondition);
+                       // allow empty cells
+                       cell = row.createCell((short) 2);
+                       cell.setCellStyle(style_2);
+                       setCellValue(cell, ((allowEmpty) ? "yes" : "no"));
+                       // show input box
+                       cell = row.createCell((short) 3);
+                       cell.setCellStyle(style_2);
+                       setCellValue(cell, ((inputBox) ? "yes" : "no"));
+                       // show error box
+                       cell = row.createCell((short) 4);
+                       cell.setCellStyle(style_2);
+                       setCellValue(cell, ((errorBox) ? "yes" : "no"));
+               }
+               private static void writeOtherSettings(HSSFSheet sheet, HSSFCellStyle style,
+                               String strStettings) {
+                       HSSFRow row = sheet.getRow(sheet.getPhysicalNumberOfRows() - 1);
+                       HSSFCell cell = row.createCell((short) 5);
+                       cell.setCellStyle(style);
+                       setCellValue(cell, strStettings);
+               }
+               public void addListValidation(String[] explicitListValues, String listFormula, String listValsDescr,
+                               boolean allowEmpty, boolean suppressDropDown) {
+                       String promptDescr = (allowEmpty ? "empty ok" : "not empty") 
+                                       + ", " + (suppressDropDown ? "no drop-down" : "drop-down"); 
+                       addValidationInternal(VT.LIST, listFormula, null, ES.STOP, listValsDescr, promptDescr, 
+                                       allowEmpty, false, true, suppressDropDown, explicitListValues);
+               }
+       }
+
+       /**
+        * Manages the cell styles used for formatting the output spreadsheet
+        */
+       private static final class WorkbookFormatter {
+
+               private final HSSFWorkbook _wb;
+               private final HSSFCellStyle _style_1;
+               private final HSSFCellStyle _style_2;
+               private final HSSFCellStyle _style_3;
+               private final HSSFCellStyle _style_4;
+               private HSSFSheet _currentSheet;
+
+               public WorkbookFormatter(HSSFWorkbook wb) {
+                       _wb = wb;
+                       _style_1 = createStyle( wb, HSSFCellStyle.ALIGN_LEFT );
+                       _style_2 = createStyle( wb, HSSFCellStyle.ALIGN_CENTER );
+                       _style_3 = createStyle( wb, HSSFCellStyle.ALIGN_CENTER, HSSFColor.GREY_25_PERCENT.index, true );
+                       _style_4 = createHeaderStyle(wb);
+               }
+               
+               private static HSSFCellStyle createStyle(HSSFWorkbook wb, short h_align, short color,
+                               boolean bold) {
+                       HSSFFont font = wb.createFont();
+                       if (bold) {
+                               font.setBoldweight(HSSFFont.BOLDWEIGHT_BOLD);
+                       }
+
+                       HSSFCellStyle cellStyle = wb.createCellStyle();
+                       cellStyle.setFont(font);
+                       cellStyle.setFillForegroundColor(color);
+                       cellStyle.setFillPattern(HSSFCellStyle.SOLID_FOREGROUND);
+                       cellStyle.setVerticalAlignment(HSSFCellStyle.VERTICAL_CENTER);
+                       cellStyle.setAlignment(h_align);
+                       cellStyle.setBorderLeft(HSSFCellStyle.BORDER_THIN);
+                       cellStyle.setLeftBorderColor(HSSFColor.BLACK.index);
+                       cellStyle.setBorderTop(HSSFCellStyle.BORDER_THIN);
+                       cellStyle.setTopBorderColor(HSSFColor.BLACK.index);
+                       cellStyle.setBorderRight(HSSFCellStyle.BORDER_THIN);
+                       cellStyle.setRightBorderColor(HSSFColor.BLACK.index);
+                       cellStyle.setBorderBottom(HSSFCellStyle.BORDER_THIN);
+                       cellStyle.setBottomBorderColor(HSSFColor.BLACK.index);
+
+                       return cellStyle;
+               }
+
+               private static HSSFCellStyle createStyle(HSSFWorkbook wb, short h_align) {
+                       return createStyle(wb, h_align, HSSFColor.WHITE.index, false);
+               }
+               private static HSSFCellStyle createHeaderStyle(HSSFWorkbook wb) {
+                       HSSFFont font = wb.createFont();
+                       font.setColor( HSSFColor.WHITE.index );
+                       font.setBoldweight(HSSFFont.BOLDWEIGHT_BOLD);
+                       
+                       HSSFCellStyle cellStyle = wb.createCellStyle();
+                       cellStyle.setFillForegroundColor(HSSFColor.BLUE_GREY.index);
+                       cellStyle.setFillPattern(HSSFCellStyle.SOLID_FOREGROUND);
+                       cellStyle.setAlignment(HSSFCellStyle.ALIGN_CENTER);
+                       cellStyle.setVerticalAlignment(HSSFCellStyle.VERTICAL_CENTER);
+                       cellStyle.setBorderLeft(HSSFCellStyle.BORDER_THIN);
+                       cellStyle.setLeftBorderColor(HSSFColor.WHITE.index);
+                       cellStyle.setBorderTop(HSSFCellStyle.BORDER_THIN);
+                       cellStyle.setTopBorderColor(HSSFColor.WHITE.index);
+                       cellStyle.setBorderRight(HSSFCellStyle.BORDER_THIN);
+                       cellStyle.setRightBorderColor(HSSFColor.WHITE.index);
+                       cellStyle.setBorderBottom(HSSFCellStyle.BORDER_THIN);
+                       cellStyle.setBottomBorderColor(HSSFColor.WHITE.index);
+                       cellStyle.setFont(font);
+                       return cellStyle;
+               }
+               
+
+               public HSSFSheet createSheet(String sheetName) {
+                       _currentSheet = _wb.createSheet(sheetName);
+                       return _currentSheet;
+               }
+               public void createDVTypeRow(String strTypeDescription) {
+                       HSSFSheet sheet = _currentSheet;
+                       HSSFRow row = sheet.createRow(sheet.getPhysicalNumberOfRows());
+                       row = sheet.createRow(sheet.getPhysicalNumberOfRows());
+                       sheet.addMergedRegion(new CellRangeAddress(sheet.getPhysicalNumberOfRows()-1, sheet.getPhysicalNumberOfRows()-1, 0, 5));
+                       HSSFCell cell = row.createCell((short) 0);
+                       setCellValue(cell, strTypeDescription);
+                       cell.setCellStyle(_style_3);
+                       row = sheet.createRow(sheet.getPhysicalNumberOfRows());
+               }
+               
+               public void createHeaderRow() {
+                       HSSFSheet sheet = _currentSheet;
+                       HSSFRow row = sheet.createRow(sheet.getPhysicalNumberOfRows());
+                       row.setHeight((short) 400);
+                       for (int i = 0; i < 6; i++) {
+                               row.createCell((short) i).setCellStyle(_style_4);
+                               if (i == 2 || i == 3 || i == 4) {
+                                       sheet.setColumnWidth((short) i, (short) 3500);
+                               } else if (i == 5) {
+                                       sheet.setColumnWidth((short) i, (short) 10000);
+                               } else {
+                                       sheet.setColumnWidth((short) i, (short) 8000);
+                               }
+                       }
+                       HSSFCell cell = row.getCell((short) 0);
+                       setCellValue(cell, "Data validation cells");
+                       cell = row.getCell((short) 1);
+                       setCellValue(cell, "Condition");
+                       cell = row.getCell((short) 2);
+                       setCellValue(cell, "Allow blank");
+                       cell = row.getCell((short) 3);
+                       setCellValue(cell, "Prompt box");
+                       cell = row.getCell((short) 4);
+                       setCellValue(cell, "Error box");
+                       cell = row.getCell((short) 5);
+                       setCellValue(cell, "Other settings");
+               }
+
+               public ValidationAdder createValidationAdder(HSSFCellStyle cellStyle, int dataValidationType) {
+                       return new ValidationAdder(_currentSheet, _style_1, _style_2, cellStyle, dataValidationType);
+               }
+
+               public void createDVDescriptionRow(String strTypeDescription) {
+                       HSSFSheet sheet = _currentSheet;
+                       HSSFRow row = sheet.getRow(sheet.getPhysicalNumberOfRows()-1);
+                       sheet.addMergedRegion(new CellRangeAddress(sheet.getPhysicalNumberOfRows()-1, sheet.getPhysicalNumberOfRows()-1, 0, 5));
+                       HSSFCell cell = row.createCell((short)0);
+                       setCellValue(cell, strTypeDescription);
+                       cell.setCellStyle(_style_3);
+                       row = sheet.createRow(sheet.getPhysicalNumberOfRows());
+               }
+       }
+       
+  
+       private void addCustomValidations(WorkbookFormatter wf) {
+               wf.createSheet("Custom");
+               wf.createHeaderRow();
+
+               ValidationAdder va = wf.createValidationAdder(null, VT.FORMULA);
+               va.addValidation(OP.BETWEEN, "ISNUMBER($A2)", null, ES.STOP, "ISNUMBER(A2)", "Error box type = STOP", true, true, true);
+               va.addValidation(OP.BETWEEN, "IF(SUM(A2:A3)=5,TRUE,FALSE)", null, ES.WARNING, "IF(SUM(A2:A3)=5,TRUE,FALSE)", "Error box type = WARNING", false, false, true);
+       }
+
+       private static void addSimpleNumericValidations(WorkbookFormatter wf) {
+               // data validation's number types
+               wf.createSheet("Numbers");
+
+               // "Whole number" validation type
+               wf.createDVTypeRow("Whole number");
+               wf.createHeaderRow();
+
+               ValidationAdder va = wf.createValidationAdder(null, VT.INTEGER);
+               va.addValidation(OP.BETWEEN, "2", "6", ES.STOP, "Between 2 and 6 ", "Error box type = STOP", true, true, true);
+               va.addValidation(OP.NOT_BETWEEN, "2", "6", ES.INFO, "Not between 2 and 6 ", "Error box type = INFO", false, true, true);
+               va.addValidation(OP.EQUAL, "=3+2", null, ES.WARNING, "Equal to (3+2)", "Error box type = WARNING", false, false, true);
+               va.addValidation(OP.NOT_EQUAL, "3", null, ES.WARNING, "Not equal to 3", "-", false, false, false);
+               va.addValidation(OP.GREATER_THAN, "3", null, ES.WARNING, "Greater than 3", "-", true, false, false);
+               va.addValidation(OP.LESS_THAN, "3", null, ES.WARNING, "Less than 3", "-", true, true, false);
+               va.addValidation(OP.GREATER_OR_EQUAL, "4", null, ES.STOP, "Greater than or equal to 4", "Error box type = STOP", true, false, true);
+               va.addValidation(OP.LESS_OR_EQUAL, "4", null, ES.STOP, "Less than or equal to 4", "-", false, true, false);
+
+               // "Decimal" validation type
+               wf.createDVTypeRow("Decimal");
+               wf.createHeaderRow();
+
+               va = wf.createValidationAdder(null, VT.DECIMAL);
+               va.addValidation(OP.BETWEEN, "2", "6", ES.STOP, "Between 2 and 6 ", "Error box type = STOP", true, true, true);
+               va.addValidation(OP.NOT_BETWEEN, "2", "6", ES.INFO, "Not between 2 and 6 ", "Error box type = INFO", false, true, true);
+               va.addValidation(OP.EQUAL, "3", null, ES.WARNING, "Equal to 3", "Error box type = WARNING", false, false, true);
+               va.addValidation(OP.NOT_EQUAL, "3", null, ES.WARNING, "Not equal to 3", "-", false, false, false);
+               va.addValidation(OP.GREATER_THAN, "=12/6", null, ES.WARNING, "Greater than (12/6)", "-", true, false, false);
+               va.addValidation(OP.LESS_THAN, "3", null, ES.WARNING, "Less than 3", "-", true, true, false);
+               va.addValidation(OP.GREATER_OR_EQUAL, "4", null, ES.STOP, "Greater than or equal to 4", "Error box type = STOP", true, false, true);
+               va.addValidation(OP.LESS_OR_EQUAL, "4", null, ES.STOP, "Less than or equal to 4", "-", false, true, false);
+       }
+       
+       private static void addListValidations(WorkbookFormatter wf, HSSFWorkbook wb) {
+               final String cellStrValue 
+                = "a b c d e f g h i j k l m n o p r s t u v x y z w 0 1 2 3 4 "
+               + "a b c d e f g h i j k l m n o p r s t u v x y z w 0 1 2 3 4 "
+               + "a b c d e f g h i j k l m n o p r s t u v x y z w 0 1 2 3 4 "
+               + "a b c d e f g h i j k l m n o p r s t u v x y z w 0 1 2 3 4 ";
+               final String dataSheetName = "list_data";
+               // "List" Data Validation type
+               HSSFSheet fSheet = wf.createSheet("Lists");
+               HSSFSheet dataSheet = wb.createSheet(dataSheetName);
+
+
+               wf.createDVTypeRow("Explicit lists - list items are explicitly provided");
+               wf.createDVDescriptionRow("Disadvantage - sum of item's length should be less than 255 characters");
+               wf.createHeaderRow();
+
+               ValidationAdder va = wf.createValidationAdder(null, VT.LIST);
+               String listValsDescr = "POIFS,HSSF,HWPF,HPSF";
+               String[] listVals = listValsDescr.split(",");
+               va.addListValidation(listVals, null, listValsDescr, false, false);
+               va.addListValidation(listVals, null, listValsDescr, false, true);
+               va.addListValidation(listVals, null, listValsDescr, true, false);
+               va.addListValidation(listVals, null, listValsDescr, true, true);
+               
+               
+               
+               wf.createDVTypeRow("Reference lists - list items are taken from others cells");
+               wf.createDVDescriptionRow("Advantage - no restriction regarding the sum of item's length");
+               wf.createHeaderRow();
+               va = wf.createValidationAdder(null, VT.LIST);
+               String strFormula = "$A$30:$A$39";
+               va.addListValidation(null, strFormula, strFormula, false, false);
+               
+               strFormula = dataSheetName + "!$A$1:$A$10";
+               va.addListValidation(null, strFormula, strFormula, false, false);
+               HSSFName namedRange = wb.createName();
+               namedRange.setNameName("myName");
+               namedRange.setReference(dataSheetName + "!$A$2:$A$7");
+               strFormula = "myName";
+               va.addListValidation(null, strFormula, strFormula, false, false);
+               strFormula = "offset(myName, 2, 1, 4, 2)"; // Note about last param '2': 
+               // - Excel expects single row or single column when entered in UI, but process this OK otherwise
+               va.addListValidation(null, strFormula, strFormula, false, false);
+               
+               // add list data on same sheet
+               for (int i = 0; i < 10; i++) {
+                       HSSFRow currRow = fSheet.createRow(i + 29);
+                       setCellValue(currRow.createCell((short) 0), cellStrValue);
+               }
+               // add list data on another sheet
+               for (int i = 0; i < 10; i++) {
+                       HSSFRow currRow = dataSheet.createRow(i + 0);
+                       setCellValue(currRow.createCell((short) 0), "Data a" + i);
+                       setCellValue(currRow.createCell((short) 1), "Data b" + i);
+                       setCellValue(currRow.createCell((short) 2), "Data c" + i);
+               }
+       }
+
+       private static void addDateTimeValidations(WorkbookFormatter wf, HSSFWorkbook wb) {
+               wf.createSheet("Dates and Times");
+
+               HSSFDataFormat dataFormat = wb.createDataFormat();
+               short fmtDate = dataFormat.getFormat("m/d/yyyy");
+               short fmtTime = dataFormat.getFormat("h:mm");
+               HSSFCellStyle cellStyle_date = wb.createCellStyle();
+               cellStyle_date.setDataFormat(fmtDate);
+               HSSFCellStyle cellStyle_time = wb.createCellStyle();
+               cellStyle_time.setDataFormat(fmtTime);
+
+               wf.createDVTypeRow("Date ( cells are already formated as date - m/d/yyyy)");
+               wf.createHeaderRow();
+
+               ValidationAdder va = wf.createValidationAdder(cellStyle_date, VT.DATE);
+               va.addValidation(OP.BETWEEN,     "2004/01/02", "2004/01/06", ES.STOP, "Between 1/2/2004 and 1/6/2004 ", "Error box type = STOP", true, true, true);
+               va.addValidation(OP.NOT_BETWEEN, "2004/01/01", "2004/01/06", ES.INFO, "Not between 1/2/2004 and 1/6/2004 ", "Error box type = INFO", false, true, true);
+               va.addValidation(OP.EQUAL,       "2004/03/02", null,       ES.WARNING, "Equal to 3/2/2004", "Error box type = WARNING", false, false, true);
+               va.addValidation(OP.NOT_EQUAL,   "2004/03/02", null,       ES.WARNING, "Not equal to 3/2/2004", "-", false, false, false);
+               va.addValidation(OP.GREATER_THAN,"=DATEVALUE(\"4-Jul-2001\")", null,       ES.WARNING, "Greater than DATEVALUE('4-Jul-2001')", "-", true, false, false);
+               va.addValidation(OP.LESS_THAN,   "2004/03/02", null,       ES.WARNING, "Less than 3/2/2004", "-", true, true, false);
+               va.addValidation(OP.GREATER_OR_EQUAL, "2004/03/02", null,       ES.STOP, "Greater than or equal to 3/2/2004", "Error box type = STOP", true, false, true);
+               va.addValidation(OP.LESS_OR_EQUAL, "2004/03/04", null,       ES.STOP, "Less than or equal to 3/4/2004", "-", false, true, false);
+
+               // "Time" validation type
+               wf.createDVTypeRow("Time ( cells are already formated as time - h:mm)");
+               wf.createHeaderRow();
+
+               va = wf.createValidationAdder(cellStyle_time, VT.TIME);
+               va.addValidation(OP.BETWEEN,     "12:00", "16:00", ES.STOP, "Between 12:00 and 16:00 ", "Error box type = STOP", true, true, true);
+               va.addValidation(OP.NOT_BETWEEN, "12:00", "16:00", ES.INFO, "Not between 12:00 and 16:00 ", "Error box type = INFO", false, true, true);
+               va.addValidation(OP.EQUAL,       "13:35", null,    ES.WARNING, "Equal to 13:35", "Error box type = WARNING", false, false, true);
+               va.addValidation(OP.NOT_EQUAL,   "13:35", null,    ES.WARNING, "Not equal to 13:35", "-", false, false, false);
+               va.addValidation(OP.GREATER_THAN,"12:00", null,    ES.WARNING, "Greater than 12:00", "-", true, false, false);
+               va.addValidation(OP.LESS_THAN,   "=1/2", null,    ES.WARNING, "Less than (1/2) -> 12:00", "-", true, true, false);
+               va.addValidation(OP.GREATER_OR_EQUAL, "14:00", null,    ES.STOP, "Greater than or equal to 14:00", "Error box type = STOP", true, false, true);
+               va.addValidation(OP.LESS_OR_EQUAL,"14:00", null,    ES.STOP, "Less than or equal to 14:00", "-", false, true, false);
+       }
+
+       private static void addTextLengthValidations(WorkbookFormatter wf) {
+               wf.createSheet("Text lengths");
+               wf.createHeaderRow();
+
+               ValidationAdder va = wf.createValidationAdder(null, VT.TEXT_LENGTH);
+               va.addValidation(OP.BETWEEN, "2", "6", ES.STOP, "Between 2 and 6 ", "Error box type = STOP", true, true, true);
+               va.addValidation(OP.NOT_BETWEEN, "2", "6", ES.INFO, "Not between 2 and 6 ", "Error box type = INFO", false, true, true);
+               va.addValidation(OP.EQUAL, "3", null, ES.WARNING, "Equal to 3", "Error box type = WARNING", false, false, true);
+               va.addValidation(OP.NOT_EQUAL, "3", null, ES.WARNING, "Not equal to 3", "-", false, false, false);
+               va.addValidation(OP.GREATER_THAN, "3", null, ES.WARNING, "Greater than 3", "-", true, false, false);
+               va.addValidation(OP.LESS_THAN, "3", null, ES.WARNING, "Less than 3", "-", true, true, false);
+               va.addValidation(OP.GREATER_OR_EQUAL, "4", null, ES.STOP, "Greater than or equal to 4", "Error box type = STOP", true, false, true);
+               va.addValidation(OP.LESS_OR_EQUAL, "4", null, ES.STOP, "Less than or equal to 4", "-", false, true, false);
+       }
+       
+       public void testDataValidation() {
+               log("\nTest no. 2 - Test Excel's Data validation mechanism");
+               HSSFWorkbook wb = new HSSFWorkbook();
+               WorkbookFormatter wf = new WorkbookFormatter(wb);
+
+               log("    Create sheet for Data Validation's number types ... ");
+               addSimpleNumericValidations(wf);
+               log("done !");
+
+               log("    Create sheet for 'List' Data Validation type ... ");
+               addListValidations(wf, wb);
+               log("done !");
+               
+               log("    Create sheet for 'Date' and 'Time' Data Validation types ... ");
+               addDateTimeValidations(wf, wb);
+               log("done !");
+
+               log("    Create sheet for 'Text length' Data Validation type... ");
+               addTextLengthValidations(wf);
+               log("done !");
+
+               // Custom Validation type
+               log("    Create sheet for 'Custom' Data Validation type ... ");
+               addCustomValidations(wf);
+               log("done !");
+
+               ByteArrayOutputStream baos = new ByteArrayOutputStream(22000);
+               try {
+                       wb.write(baos);
+                       baos.close();
+               } catch (IOException e) {
+                       throw new RuntimeException(e);
+               }
+               byte[] generatedContent = baos.toByteArray();
+               boolean isSame;
+               if (false) {
+                       // TODO - add proof spreadsheet and compare
+                       InputStream proofStream = HSSFTestDataSamples.openSampleFileStream("TestDataValidation.xls");
+                       isSame = compareStreams(proofStream, generatedContent);
+               }
+               isSame = true;
+               
+               if (isSame) {
+                       return;
+               }
+               File tempDir = new File(System.getProperty("java.io.tmpdir"));
+               File generatedFile = new File(tempDir, "GeneratedTestDataValidation.xls");
+               try {
+                       FileOutputStream fileOut = new FileOutputStream(generatedFile);
+                       fileOut.write(generatedContent);
+                       fileOut.close();
+               } catch (IOException e) {
+                       throw new RuntimeException(e);
+               }
+       
+               PrintStream ps = System.out;
+       
+               ps.println("This test case has failed because the generated file differs from proof copy '" 
+                               ); // TODO+ proofFile.getAbsolutePath() + "'.");
+               ps.println("The cause is usually a change to this test, or some common spreadsheet generation code.  "
+                               + "The developer has to decide whether the changes were wanted or unwanted.");
+               ps.println("If the changes to the generated version were unwanted, "
+                               + "make the fix elsewhere (do not modify this test or the proof spreadsheet to get the test working).");
+               ps.println("If the changes were wanted, make sure to open the newly generated file in Excel "
+                               + "and verify it manually.  The new proof file should be submitted after it is verified to be correct.");
+               ps.println("");
+               ps.println("One other possible (but less likely) cause of a failed test is a problem in the "
+                               + "comparison logic used here. Perhaps some extra file regions need to be ignored.");
+               ps.println("The generated file has been saved to '" + generatedFile.getAbsolutePath() + "' for manual inspection.");
+       
+               fail("Generated file differs from proof copy.  See sysout comments for details on how to fix.");
+               
+       }
+       
+       private static boolean compareStreams(InputStream isA, byte[] generatedContent) {
+
+               InputStream isB = new ByteArrayInputStream(generatedContent);
+
+               // The allowable regions where the generated file can differ from the 
+               // proof should be small (i.e. much less than 1K)
+               int[] allowableDifferenceRegions = { 
+                               0x0228, 16,  // a region of the file containing the OS username
+                               0x506C, 8,   // See RootProperty (super fields _seconds_2 and _days_2)
+               };
+               int[] diffs = StreamUtility.diffStreams(isA, isB, allowableDifferenceRegions);
+               if (diffs == null) {
+                       return true;
+               }
+               System.err.println("Diff from proof: ");
+               for (int i = 0; i < diffs.length; i++) {
+                       System.err.println("diff at offset: 0x" + Integer.toHexString(diffs[i]));
+               }
+               return false;
+       }
+  
+
+
+
+
+  /* package */ static void setCellValue(HSSFCell cell, String text) {
+         cell.setCellValue(new HSSFRichTextString(text));
+         
   }
+  
+       public void testAddToExistingSheet() {
+
+               // dvEmpty.xls is a simple one sheet workbook.  With a DataValidations header record but no 
+               // DataValidations.  It's important that the example has one SHEETPROTECTION record.
+               // Such a workbook can be created in Excel (2007) by adding datavalidation for one cell
+               // and then deleting the row that contains the cell.
+               HSSFWorkbook wb = HSSFTestDataSamples.openSampleWorkbook("dvEmpty.xls");  
+               int dvRow = 0;
+               HSSFSheet sheet = wb.getSheetAt(0);
+               DVConstraint dc = DVConstraint.createNumericConstraint(VT.INTEGER, OP.EQUAL, "42", null);
+               HSSFDataValidation dv = new HSSFDataValidation(new CellRangeAddressList(dvRow, dvRow, 0, 0), dc);
+               
+               dv.setEmptyCellAllowed(false);
+               dv.setErrorStyle(ES.STOP);
+               dv.setShowPromptBox(true);
+               dv.createErrorBox("Xxx", "Yyy");
+               dv.setSuppressDropDownArrow(true);
+
+               sheet.addValidationData(dv);
+               
+               ByteArrayOutputStream baos = new ByteArrayOutputStream();
+               try {
+                       wb.write(baos);
+               } catch (IOException e) {
+                       throw new RuntimeException(e);
+               }
+               
+               byte[] wbData = baos.toByteArray();
+               
+               if (false) { // TODO (Jul 2008) fix EventRecordFactory to process unknown records, (and DV records for that matter)
+                       EventRecordFactory erf = new EventRecordFactory();
+                       ERFListener erfListener = null; // new MyERFListener();
+                       erf.registerListener(erfListener, null);
+                       try {
+                               POIFSFileSystem fs = new POIFSFileSystem(new ByteArrayInputStream(baos.toByteArray()));
+                               erf.processRecords(fs.createDocumentInputStream("Workbook"));
+                       } catch (RecordFormatException e) {
+                               throw new RuntimeException(e);
+                       } catch (IOException e) {
+                               throw new RuntimeException(e);
+                       }
+               }
+               // else verify record ordering by navigating the raw bytes
+               
+               byte[] dvHeaderRecStart= { (byte)0xB2, 0x01, 0x12, 0x00, };
+               int dvHeaderOffset = findIndex(wbData, dvHeaderRecStart);
+               assertTrue(dvHeaderOffset > 0);
+               int nextRecIndex = dvHeaderOffset + 22;
+               int nextSid 
+                       = ((wbData[nextRecIndex + 0] << 0) & 0x00FF) 
+                       + ((wbData[nextRecIndex + 1] << 8) & 0xFF00)
+                       ;
+               // nextSid should be for a DVRecord.  If anything comes between the DV header record 
+               // and the DV records, Excel will not be able to open the workbook without error.
+               
+               if (nextSid == 0x0867) {
+                       throw new AssertionFailedError("Identified bug 45519");
+               }
+               assertEquals(DVRecord.sid, nextSid);
+       }
+       private int findIndex(byte[] largeData, byte[] searchPattern) {
+               byte firstByte = searchPattern[0];
+               for (int i = 0; i < largeData.length; i++) {
+                       if(largeData[i] != firstByte) {
+                               continue;
+                       }
+                       boolean match = true;
+                       for (int j = 1; j < searchPattern.length; j++) {
+                               if(searchPattern[j] != largeData[i+j]) {
+                                       match = false;
+                                       break;
+                               }
+                       }
+                       if (match) {
+                               return i;
+                       }
+               }
+               return -1;
+       }
 }
index 369aa266588c4a660447f1bc7fea6deb82f5b8c1..978f43577382887cc8d8f77d3a7df90366634590 100644 (file)
@@ -176,4 +176,37 @@ public final class TestHSSFComment extends TestCase {
         }
 
      }
+    
+    public void testDeleteComments() throws Exception {
+        HSSFWorkbook wb = HSSFTestDataSamples.openSampleWorkbook("SimpleWithComments.xls");
+        HSSFSheet sheet = wb.getSheetAt(0);
+        
+        // Zap from rows 1 and 3
+        assertNotNull(sheet.getRow(0).getCell(1).getCellComment());
+        assertNotNull(sheet.getRow(1).getCell(1).getCellComment());
+        assertNotNull(sheet.getRow(2).getCell(1).getCellComment());
+        
+        sheet.getRow(0).getCell(1).removeCellComment();
+        sheet.getRow(2).getCell(1).setCellComment(null);
+        
+        // Check gone so far
+        assertNull(sheet.getRow(0).getCell(1).getCellComment());
+        assertNotNull(sheet.getRow(1).getCell(1).getCellComment());
+        assertNull(sheet.getRow(2).getCell(1).getCellComment());
+        
+        // Save and re-load
+        ByteArrayOutputStream out = new ByteArrayOutputStream();
+        wb.write(out);
+        out.close();
+        wb = new HSSFWorkbook(new ByteArrayInputStream(out.toByteArray()));
+
+        // Check
+        assertNull(sheet.getRow(0).getCell(1).getCellComment());
+        assertNotNull(sheet.getRow(1).getCell(1).getCellComment());
+        assertNull(sheet.getRow(2).getCell(1).getCellComment());
+        
+//        FileOutputStream fout = new FileOutputStream("/tmp/c.xls");
+//        wb.write(fout);
+//        fout.close();
+    }
 }
index 6dbcf815dc8486b3bb4e465bbe86d747269d3ed1..fcb0b8f41e185aa2e3dfc26ed720090ce0dc433b 100644 (file)
@@ -21,7 +21,9 @@ import junit.framework.TestCase;
 
 import org.apache.poi.hssf.record.CFRuleRecord.ComparisonOperator;
 import org.apache.poi.hssf.util.HSSFColor;
+import org.apache.poi.ss.util.CellRangeAddress;
 import org.apache.poi.ss.util.Region;
+
 /**
  * 
  * @author Dmitriy Kumshayev
@@ -57,9 +59,8 @@ public final class TestHSSFConditionalFormatting extends TestCase
                };
 
                short col = 1;
-               Region [] regions =
-               {
-                       new Region(0,col,65535,col)
+               CellRangeAddress [] regions = {
+                       new CellRangeAddress(0, 65535, col, col)
                };
 
                sheetCF.addConditionalFormatting(regions, cfRules);
@@ -72,14 +73,14 @@ public final class TestHSSFConditionalFormatting extends TestCase
                HSSFConditionalFormatting cf = sheetCF.getConditionalFormattingAt(0);
                assertNotNull(cf);
 
-               regions = cf.getFormattingRegions();
+               regions = cf.getFormattingRanges();
                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());
+               CellRangeAddress r = regions[0];
+               assertEquals(1, r.getFirstColumn());
+               assertEquals(1, r.getLastColumn());
+               assertEquals(0, r.getFirstRow());
+               assertEquals(65535, r.getLastRow());
 
                assertEquals(2, cf.getNumberOfRules());
 
index f865d6e494ed9f5c2833de32d55b18f0e84c819e..e24f30da6189447274a89e6c64e8db00e7fc3bd0 100644 (file)
@@ -19,6 +19,7 @@ package org.apache.poi.hssf.usermodel;
 
 import java.text.DecimalFormat;
 import java.text.Format;
+import java.util.Date;
 import java.util.Iterator;
 
 import junit.framework.TestCase;
index 281d1b1cb00aec6c5cf4046bed113c6491ec4633..377a8ffe2474980728c364260f7746e0280767d4 100644 (file)
@@ -39,7 +39,7 @@ import org.apache.poi.hssf.model.Workbook;
  * @author Alex Jacoby (ajacoby at gmail.com)
  * @version %I%, %G%
  */
-public class TestHSSFDateUtil extends TestCase {
+public final class TestHSSFDateUtil extends TestCase {
 
     public static final int CALENDAR_JANUARY = 0;
     public static final int CALENDAR_FEBRUARY = 1;
@@ -47,11 +47,6 @@ public class TestHSSFDateUtil extends TestCase {
     public static final int CALENDAR_APRIL = 3;
     public static final int CALENDAR_JULY = 6;
     public static final int CALENDAR_OCTOBER = 9;
-    
-    public TestHSSFDateUtil(String s)
-    {
-        super(s);
-    }
 
     /**
      * Checks the date conversion functions in the HSSFDateUtil class.
@@ -193,14 +188,13 @@ public class TestHSSFDateUtil extends TestCase {
     }
     
     /**
-     * Tests that we deal with timezones properly
+     * Tests that we deal with time-zones properly
      */
     public void testCalendarConversion() {
         GregorianCalendar date = new GregorianCalendar(2002, 0, 1, 12, 1, 1);
         Date expected = date.getTime();
-        double expectedExcel = HSSFDateUtil.getExcelDate(expected);
 
-        // Iteratating over the hours exposes any rounding issues.
+        // Iterating over the hours exposes any rounding issues.
         for (int hour = -12; hour <= 12; hour++)
         {
             String id = "GMT" + (hour < 0 ? "" : "+") + hour + ":00";
@@ -209,7 +203,7 @@ public class TestHSSFDateUtil extends TestCase {
             double excelDate = HSSFDateUtil.getExcelDate(date, false);
             Date javaDate = HSSFDateUtil.getJavaDate(excelDate);
 
-            // Should match despite timezone
+            // Should match despite time-zone
             assertEquals("Checking timezone " + id, expected.getTime(), javaDate.getTime());
         }
     }
@@ -402,7 +396,11 @@ public class TestHSSFDateUtil extends TestCase {
         assertEquals(34519.0, HSSFDateUtil.getExcelDate(createDate(1998, CALENDAR_JULY, 5), true), 0.00001);
     }
     
-    private Date createDate(int year, int month, int day) {
+    /**
+     * @param month zero based 
+     * @param day one based
+     */
+    private static Date createDate(int year, int month, int day) {
         Calendar c = new GregorianCalendar();
         c.set(year, month, day, 0, 0, 0);
         c.set(Calendar.MILLISECOND, 0);
@@ -420,10 +418,18 @@ public class TestHSSFDateUtil extends TestCase {
         calendar = new GregorianCalendar(1901, 0, 1);
         assertEquals("Checking absolute day (1 Jan 1901)", 366, HSSFDateUtil.absoluteDay(calendar, false));
     }
+
+    public void testConvertTime() {
+       
+        final double delta = 1E-7; // a couple of digits more accuracy than strictly required
+        assertEquals(0.5, HSSFDateUtil.convertTime("12:00"), delta);
+        assertEquals(2.0/3, HSSFDateUtil.convertTime("16:00"), delta);
+        assertEquals(0.0000116, HSSFDateUtil.convertTime("0:00:01"), delta);
+        assertEquals(0.7330440, HSSFDateUtil.convertTime("17:35:35"), delta);
+    }
     
-    public static void main(String [] args) {
-        System.out
-                .println("Testing org.apache.poi.hssf.usermodel.TestHSSFDateUtil");
-        junit.textui.TestRunner.run(TestHSSFDateUtil.class);
+    public void testParseDate() {
+        assertEquals(createDate(2008, Calendar.AUGUST, 3), HSSFDateUtil.parseYYYYMMDDDate("2008/08/03"));
+        assertEquals(createDate(1994, Calendar.MAY, 1), HSSFDateUtil.parseYYYYMMDDDate("1994/05/01"));
     }
 }
index 90971b3c03235b2d44c4efb950b3e4df71529511..6fcd384988e63461b552b217dfeba0ad91d32e65 100644 (file)
@@ -36,6 +36,7 @@ import org.apache.poi.hssf.record.VCenterRecord;
 import org.apache.poi.hssf.record.WSBoolRecord;
 import org.apache.poi.hssf.record.WindowTwoRecord;
 import org.apache.poi.ss.util.Region;
+import org.apache.poi.ss.util.CellRangeAddress;
 
 /**
  * Tests HSSFSheet.  This test case is very incomplete at the moment.
@@ -476,15 +477,15 @@ public final class TestHSSFSheet extends TestCase {
        public void testRemoveMerged() {
                HSSFWorkbook wb = new HSSFWorkbook();
                HSSFSheet sheet = wb.createSheet();
-               Region region = new Region(0, (short)0, 1, (short)1);
+               CellRangeAddress region = new CellRangeAddress(0, 1, 0, 1);
                sheet.addMergedRegion(region);
-               region = new Region(1, (short)0, 2, (short)1);
+               region = new CellRangeAddress(1, 2, 0, 1);
                sheet.addMergedRegion(region);
 
                sheet.removeMergedRegion(0);
 
-               region = sheet.getMergedRegionAt(0);
-               assertEquals("Left over region should be starting at row 1", 1, region.getRowFrom());
+               region = sheet.getMergedRegion(0);
+               assertEquals("Left over region should be starting at row 1", 1, region.getFirstRow());
 
                sheet.removeMergedRegion(0);
 
@@ -496,15 +497,15 @@ public final class TestHSSFSheet extends TestCase {
                sheet.removeMergedRegion(0);
                assertEquals("there should now be zero merged regions!", 0, sheet.getNumMergedRegions());
                //add it again!
-               region.setRowTo(4);
+               region.setLastRow(4);
 
                sheet.addMergedRegion(region);
                assertEquals("there should now be one merged region!", 1, sheet.getNumMergedRegions());
 
                //should exist now!
                assertTrue("there isn't more than one merged region in there", 1 <= sheet.getNumMergedRegions());
-               region = sheet.getMergedRegionAt(0);
-               assertEquals("the merged row to doesnt match the one we put in ", 4, region.getRowTo());
+               region = sheet.getMergedRegion(0);
+               assertEquals("the merged row to doesnt match the one we put in ", 4, region.getLastRow());
        }
 
        public void testShiftMerged() {
@@ -518,13 +519,13 @@ public final class TestHSSFSheet extends TestCase {
                cell = row.createCell((short)1);
                cell.setCellValue(new HSSFRichTextString("second row, second cell"));
 
-               Region region = new Region(1, (short)0, 1, (short)1);
+               CellRangeAddress region = new CellRangeAddress(1, 1, 0, 1);
                sheet.addMergedRegion(region);
 
                sheet.shiftRows(1, 1, 1);
 
-               region = sheet.getMergedRegionAt(0);
-               assertEquals("Merged region not moved over to row 2", 2, region.getRowFrom());
+               region = sheet.getMergedRegion(0);
+               assertEquals("Merged region not moved over to row 2", 2, region.getFirstRow());
        }
 
        /**
@@ -683,7 +684,7 @@ public final class TestHSSFSheet extends TestCase {
                assertTrue("Column autosized with only one row: wrong width", sheet.getColumnWidth((short)0) <= maxWithRow1And2);
 
                //create a region over the 2nd row and auto size the first column
-               sheet.addMergedRegion(new Region(1,(short)0,1,(short)1));
+               sheet.addMergedRegion(new CellRangeAddress(1,1,0,1));
                sheet.autoSizeColumn((short)0);
                HSSFWorkbook wb2 = HSSFTestDataSamples.writeOutAndReadBack(wb);
 
index a5402f64c48b3e9830d0689bba693755af9d2fa9..916f6f4f0faa28b3af71d42b6b4e16d95e3cab32 100644 (file)
@@ -32,6 +32,7 @@ import org.apache.poi.hssf.record.LabelSSTRecord;
 import org.apache.poi.hssf.record.Record;
 import org.apache.poi.hssf.record.aggregates.ValueRecordsAggregate;
 import org.apache.poi.poifs.filesystem.POIFSFileSystem;
+import org.apache.poi.ss.util.CellRangeAddress;
 import org.apache.poi.ss.util.Region;
 import org.apache.poi.util.TempFile;
 
@@ -42,7 +43,7 @@ import org.apache.poi.util.TempFile;
  * @author Greg Merrill
  * @author Siggi Cherem
  */
-public class TestWorkbook extends TestCase {
+public final class TestWorkbook extends TestCase {
     private static final String LAST_NAME_KEY        = "lastName";
     private static final String FIRST_NAME_KEY       = "firstName";
     private static final String SSN_KEY              = "ssn";
@@ -260,10 +261,10 @@ public class TestWorkbook extends TestCase {
         HSSFWorkbook workbook = openSample("Employee.xls");
         HSSFSheet       sheet    = workbook.getSheetAt(0);
 
-        assertEquals(EMPLOYEE_INFORMATION, sheet.getRow(1).getCell(1).getStringCellValue());
-        assertEquals(LAST_NAME_KEY, sheet.getRow(3).getCell(2).getStringCellValue());
-        assertEquals(FIRST_NAME_KEY, sheet.getRow(4).getCell(2).getStringCellValue());
-        assertEquals(SSN_KEY, sheet.getRow(5).getCell(2).getStringCellValue());
+        assertEquals(EMPLOYEE_INFORMATION, sheet.getRow(1).getCell(1).getRichStringCellValue().getString());
+        assertEquals(LAST_NAME_KEY, sheet.getRow(3).getCell(2).getRichStringCellValue().getString());
+        assertEquals(FIRST_NAME_KEY, sheet.getRow(4).getCell(2).getRichStringCellValue().getString());
+        assertEquals(SSN_KEY, sheet.getRow(5).getCell(2).getRichStringCellValue().getString());
     }
 
     /**
@@ -318,13 +319,13 @@ public class TestWorkbook extends TestCase {
 
         sheet    = workbook.getSheetAt(0);
         cell     = sheet.getRow(0).getCell(1);
-        assertEquals(REPLACED, cell.getStringCellValue());
+        assertEquals(REPLACED, cell.getRichStringCellValue().getString());
         cell = sheet.getRow(0).getCell(0);
-        assertEquals(DO_NOT_REPLACE, cell.getStringCellValue());
+        assertEquals(DO_NOT_REPLACE, cell.getRichStringCellValue().getString());
         cell = sheet.getRow(1).getCell(0);
-        assertEquals(REPLACED, cell.getStringCellValue());
+        assertEquals(REPLACED, cell.getRichStringCellValue().getString());
         cell = sheet.getRow(1).getCell(1);
-        assertEquals(DO_NOT_REPLACE, cell.getStringCellValue());
+        assertEquals(DO_NOT_REPLACE, cell.getRichStringCellValue().getString());
     }
 
     /**
@@ -388,10 +389,10 @@ public class TestWorkbook extends TestCase {
 
         workbook = HSSFTestDataSamples.writeOutAndReadBack(workbook);
         sheet    = workbook.getSheetAt(0);
-        assertEquals(EMPLOYEE_INFORMATION, sheet.getRow(1).getCell(1).getStringCellValue());
-        assertEquals(LAST_NAME_VALUE, sheet.getRow(3).getCell(2).getStringCellValue());
-        assertEquals(FIRST_NAME_VALUE, sheet.getRow(4).getCell(2).getStringCellValue());
-        assertEquals(SSN_VALUE, sheet.getRow(5).getCell(2).getStringCellValue());
+        assertEquals(EMPLOYEE_INFORMATION, sheet.getRow(1).getCell(1).getRichStringCellValue().getString());
+        assertEquals(LAST_NAME_VALUE, sheet.getRow(3).getCell(2).getRichStringCellValue().getString());
+        assertEquals(FIRST_NAME_VALUE, sheet.getRow(4).getCell(2).getRichStringCellValue().getString());
+        assertEquals(SSN_VALUE, sheet.getRow(5).getCell(2).getRichStringCellValue().getString());
     }
 
     /**
@@ -421,26 +422,17 @@ public class TestWorkbook extends TestCase {
      *             HSSFSheet last row or first row is incorrect.             <P>
      *
      */
-
-    public void testWriteModifySheetMerged()
-        throws IOException
-    {
-        File             file = TempFile.createTempFile("testWriteSheetMerged",
-                                                    ".xls");
-        FileOutputStream out  = new FileOutputStream(file);
-        FileInputStream  in   = null;
+    public void testWriteModifySheetMerged() {
         HSSFWorkbook     wb   = new HSSFWorkbook();
         HSSFSheet        s    = wb.createSheet();
-        HSSFRow          r    = null;
-        HSSFCell         c    = null;
 
         for (short rownum = ( short ) 0; rownum < 100; rownum++)
         {
-            r = s.createRow(rownum);
+            HSSFRow r = s.createRow(rownum);
 
             for (short cellnum = ( short ) 0; cellnum < 50; cellnum += 2)
             {
-                c = r.createCell(cellnum);
+                HSSFCell c = r.createCell(cellnum);
                 c.setCellValue(rownum * 10000 + cellnum
                                + ((( double ) rownum / 1000)
                                   + (( double ) cellnum / 10000)));
@@ -448,33 +440,27 @@ public class TestWorkbook extends TestCase {
                 c.setCellValue(new HSSFRichTextString("TEST"));
             }
         }
-        s.addMergedRegion(new Region(( short ) 0, ( short ) 0, ( short ) 10,
-                                     ( short ) 10));
-        s.addMergedRegion(new Region(( short ) 30, ( short ) 5, ( short ) 40,
-                                     ( short ) 15));
-        wb.write(out);
-        out.close();
+        s.addMergedRegion(new CellRangeAddress(0, 10, 0, 10));
+        s.addMergedRegion(new CellRangeAddress(30, 40, 5, 15));
         sanityChecker.checkHSSFWorkbook(wb);
-        in = new FileInputStream(file);
-        wb = new HSSFWorkbook(new POIFSFileSystem(in));
+        wb = HSSFTestDataSamples.writeOutAndReadBack(wb);
+        
         s  = wb.getSheetAt(0);
-        Region r1 = s.getMergedRegionAt(0);
-        Region r2 = s.getMergedRegionAt(1);
+        CellRangeAddress r1 = s.getMergedRegion(0);
+        CellRangeAddress r2 = s.getMergedRegion(1);
 
-        in.close();
-
-        // System.out.println(file.length());
-        // assertEquals("FILE LENGTH == 87552",file.length(), 87552);
-        // System.out.println(s.getLastRowNum());
-        assertEquals("REGION1 = 0,0,10,10", 0,
-                     new Region(( short ) 0, ( short ) 0, ( short ) 10,
-                                ( short ) 10).compareTo(r1));
-        assertEquals("REGION2 == 30,5,40,15", 0,
-                     new Region(( short ) 30, ( short ) 5, ( short ) 40,
-                                ( short ) 15).compareTo(r2));
+        confirmRegion(new CellRangeAddress(0, 10, 0, 10), r1);
+        confirmRegion(new CellRangeAddress(30, 40,5, 15), r2);
     }
 
-    /**
+    private static void confirmRegion(CellRangeAddress ra, CellRangeAddress rb) {
+               assertEquals(ra.getFirstRow(), rb.getFirstRow());
+               assertEquals(ra.getLastRow(), rb.getLastRow());
+               assertEquals(ra.getFirstColumn(), rb.getFirstColumn());
+               assertEquals(ra.getLastColumn(), rb.getLastColumn());
+       }
+
+       /**
      * Test the backup field gets set as expected.
      */