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 fortags/REL_3_5_BETA2c681530
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 withc682225
- 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
@@ -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); | |||
} | |||
} | |||
} |
@@ -52,6 +52,11 @@ | |||
<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> |
@@ -49,6 +49,11 @@ | |||
<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> |
@@ -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); | |||
} | |||
@@ -18,14 +18,21 @@ | |||
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 |
@@ -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 */ |
@@ -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"); |
@@ -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; |
@@ -17,13 +17,14 @@ | |||
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; | |||
} | |||
} |
@@ -17,35 +17,33 @@ | |||
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; | |||
} | |||
} |
@@ -16,16 +16,14 @@ | |||
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(); | |||
} | |||
} |
@@ -1,4 +1,3 @@ | |||
/* ==================================================================== | |||
Licensed to the Apache Software Foundation (ASF) under one or more | |||
contributor license agreements. See the NOTICE file distributed with | |||
@@ -15,57 +14,43 @@ | |||
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; |
@@ -1,4 +1,3 @@ | |||
/* ==================================================================== | |||
Licensed to the Apache Software Foundation (ASF) under one or more | |||
contributor license agreements. See the NOTICE file distributed with | |||
@@ -15,70 +14,80 @@ | |||
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; | |||
} | |||
} |
@@ -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); | |||
} | |||
@@ -0,0 +1,178 @@ | |||
/* ==================================================================== | |||
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.aggregates; | |||
import java.util.ArrayList; | |||
import java.util.List; | |||
import org.apache.poi.hssf.model.RecordStream; | |||
import org.apache.poi.hssf.record.CFHeaderRecord; | |||
import org.apache.poi.hssf.record.CFRuleRecord; | |||
import org.apache.poi.hssf.record.DVALRecord; | |||
import org.apache.poi.hssf.record.DVRecord; | |||
import org.apache.poi.hssf.record.EOFRecord; | |||
import org.apache.poi.hssf.record.HyperlinkRecord; | |||
import org.apache.poi.hssf.record.MergeCellsRecord; | |||
import org.apache.poi.hssf.record.PaneRecord; | |||
import org.apache.poi.hssf.record.Record; | |||
import org.apache.poi.hssf.record.SelectionRecord; | |||
import org.apache.poi.hssf.record.WindowTwoRecord; | |||
/** | |||
* Manages the DVALRecord and DVRecords for a single sheet<br/> | |||
* See OOO excelfileformat.pdf section 4.14 | |||
* @author Josh Micich | |||
*/ | |||
public final class DataValidityTable extends RecordAggregate { | |||
private static final short sid = -0x01B2; // not a real record | |||
private final DVALRecord _headerRec; | |||
/** | |||
* The list of data validations for the current sheet. | |||
* Note - this may be empty (contrary to OOO documentation) | |||
*/ | |||
private final List _validationList; | |||
public DataValidityTable(RecordStream rs) { | |||
_headerRec = (DVALRecord) rs.getNext(); | |||
List temp = new ArrayList(); | |||
while (rs.peekNextClass() == DVRecord.class) { | |||
temp.add(rs.getNext()); | |||
} | |||
_validationList = temp; | |||
} | |||
private DataValidityTable() { | |||
_headerRec = new DVALRecord(); | |||
_validationList = new ArrayList(); | |||
} | |||
public short getSid() { | |||
return sid; | |||
} | |||
public int serialize(int offset, byte[] data) { | |||
int result = _headerRec.serialize(offset, data); | |||
for (int i = 0; i < _validationList.size(); i++) { | |||
result += ((Record) _validationList.get(i)).serialize(offset + result, data); | |||
} | |||
return result; | |||
} | |||
public int getRecordSize() { | |||
int result = _headerRec.getRecordSize(); | |||
for (int i = _validationList.size() - 1; i >= 0; i--) { | |||
result += ((Record) _validationList.get(i)).getRecordSize(); | |||
} | |||
return result; | |||
} | |||
/** | |||
* Creates a new <tt>DataValidityTable</tt> and inserts it in the right | |||
* place in the sheetRecords list. | |||
*/ | |||
public static DataValidityTable createForSheet(List sheetRecords) { | |||
int index = findDVTableInsertPos(sheetRecords); | |||
DataValidityTable result = new DataValidityTable(); | |||
sheetRecords.add(index, result); | |||
return result; | |||
} | |||
/** | |||
* Finds the index where the sheet validations header record should be inserted | |||
* @param records the records for this sheet | |||
* | |||
* + WINDOW2 | |||
* o SCL | |||
* o PANE | |||
* oo SELECTION | |||
* o STANDARDWIDTH | |||
* oo MERGEDCELLS | |||
* o LABELRANGES | |||
* o PHONETICPR | |||
* o Conditional Formatting Table | |||
* o Hyperlink Table | |||
* o Data Validity Table | |||
* o SHEETLAYOUT | |||
* o SHEETPROTECTION | |||
* o RANGEPROTECTION | |||
* + EOF | |||
*/ | |||
private static int findDVTableInsertPos(List records) { | |||
int i = records.size() - 1; | |||
if (!(records.get(i) instanceof EOFRecord)) { | |||
throw new IllegalStateException("Last sheet record should be EOFRecord"); | |||
} | |||
while (i > 0) { | |||
i--; | |||
Record rec = (Record) records.get(i); | |||
if (isPriorRecord(rec.getSid())) { | |||
Record nextRec = (Record) records.get(i + 1); | |||
if (!isSubsequentRecord(nextRec.getSid())) { | |||
throw new IllegalStateException("Unexpected (" + nextRec.getClass().getName() | |||
+ ") found after (" + rec.getClass().getName() + ")"); | |||
} | |||
return i; | |||
} | |||
if (!isSubsequentRecord(rec.getSid())) { | |||
throw new IllegalStateException("Unexpected (" + rec.getClass().getName() | |||
+ ") while looking for DV Table insert pos"); | |||
} | |||
} | |||
return 0; | |||
} | |||
// TODO - add UninterpretedRecord as base class for many of these | |||
// unimplemented sids | |||
private static boolean isPriorRecord(short sid) { | |||
switch(sid) { | |||
case WindowTwoRecord.sid: | |||
case 0x00A0: // SCL | |||
case PaneRecord.sid: | |||
case SelectionRecord.sid: | |||
case 0x0099: // STANDARDWIDTH | |||
case MergeCellsRecord.sid: | |||
case 0x015F: // LABELRANGES | |||
case 0x00EF: // PHONETICPR | |||
case CFHeaderRecord.sid: | |||
case CFRuleRecord.sid: | |||
case HyperlinkRecord.sid: | |||
case 0x0800: // QUICKTIP | |||
return true; | |||
} | |||
return false; | |||
} | |||
private static boolean isSubsequentRecord(short sid) { | |||
switch(sid) { | |||
case 0x0862: // SHEETLAYOUT | |||
case 0x0867: // SHEETPROTECTION | |||
case 0x0868: // RANGEPROTECTION | |||
case EOFRecord.sid: | |||
return true; | |||
} | |||
return false; | |||
} | |||
public void addDataValidation(DVRecord dvRecord) { | |||
_validationList.add(dvRecord); | |||
_headerRec.setDVRecNo(_validationList.size()); | |||
} | |||
} |
@@ -0,0 +1,41 @@ | |||
/* ==================================================================== | |||
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.aggregates; | |||
import org.apache.poi.hssf.record.Record; | |||
import org.apache.poi.hssf.record.RecordInputStream; | |||
/** | |||
* <tt>RecordAggregate</tt>s are groups of of BIFF <tt>Record</tt>s that are typically stored | |||
* together and/or updated together. Workbook / Sheet records are typically stored in a sequential | |||
* list, which does not provide much structure to coordinate updates. | |||
* | |||
* @author Josh Micich | |||
*/ | |||
public abstract class RecordAggregate extends Record { | |||
// TODO - convert existing aggregate classes to proper subclasses of this one | |||
protected final void validateSid(short id) { | |||
// TODO - break class hierarchy and make separate from Record | |||
throw new RuntimeException("Should not be called"); | |||
} | |||
protected final void fillFields(RecordInputStream in) { | |||
throw new RuntimeException("Should not be called"); | |||
} | |||
// force subclassses to provide better implementation than default | |||
public abstract int getRecordSize(); | |||
} |
@@ -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()+")"; | |||
} | |||
} |
@@ -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); | |||
} | |||
} |
@@ -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(); | |||
} | |||
} |
@@ -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; |
@@ -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); | |||
} | |||
} |
@@ -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. |
@@ -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; } | |||
} |
@@ -16,11 +16,10 @@ | |||
==================================================================== */ | |||
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(); | |||
} | |||
/** |
@@ -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); | |||
} | |||
} |
@@ -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(); | |||
} | |||
} | |||
@@ -21,6 +21,7 @@ import org.apache.poi.hssf.model.Sheet; | |||
import org.apache.poi.hssf.record.CFRuleRecord; | |||
import org.apache.poi.hssf.record.aggregates.CFRecordsAggregate; | |||
import org.apache.poi.ss.util.Region; | |||
import org.apache.poi.ss.util.CellRangeAddress; | |||
/** | |||
* The 'Conditional Formatting' facet of <tt>HSSFSheet</tt> | |||
@@ -100,7 +101,12 @@ public final class HSSFSheetConditionalFormatting { | |||
return _sheet.addConditionalFormatting(cfraClone); | |||
} | |||
/** | |||
* @deprecated use <tt>CellRangeAddress</tt> instead of <tt>Region</tt> | |||
*/ | |||
public int addConditionalFormatting(Region[] regions, HSSFConditionalFormattingRule[] cfRules) { | |||
return addConditionalFormatting(Region.convertRegionsToCellRanges(regions), cfRules); | |||
} | |||
/** | |||
* Allows to add a new Conditional Formatting set to the sheet. | |||
* | |||
@@ -109,8 +115,7 @@ public final class HSSFSheetConditionalFormatting { | |||
* | |||
* @return index of the newly created Conditional Formatting object | |||
*/ | |||
public int addConditionalFormatting(Region[] regions, HSSFConditionalFormattingRule[] cfRules) { | |||
public int addConditionalFormatting(CellRangeAddress[] regions, HSSFConditionalFormattingRule[] cfRules) { | |||
if (regions == null) { | |||
throw new IllegalArgumentException("regions must not be null"); | |||
} | |||
@@ -132,7 +137,7 @@ public final class HSSFSheetConditionalFormatting { | |||
return _sheet.addConditionalFormatting(cfra); | |||
} | |||
public int addConditionalFormatting(Region[] regions, | |||
public int addConditionalFormatting(CellRangeAddress[] regions, | |||
HSSFConditionalFormattingRule rule1) | |||
{ | |||
return addConditionalFormatting(regions, | |||
@@ -142,7 +147,7 @@ public final class HSSFSheetConditionalFormatting { | |||
}); | |||
} | |||
public int addConditionalFormatting(Region[] regions, | |||
public int addConditionalFormatting(CellRangeAddress[] regions, | |||
HSSFConditionalFormattingRule rule1, | |||
HSSFConditionalFormattingRule rule2) | |||
{ | |||
@@ -153,18 +158,6 @@ public final class HSSFSheetConditionalFormatting { | |||
}); | |||
} | |||
public int addConditionalFormatting(Region[] regions, | |||
HSSFConditionalFormattingRule rule1, | |||
HSSFConditionalFormattingRule rule2, | |||
HSSFConditionalFormattingRule rule3) | |||
{ | |||
return addConditionalFormatting(regions, | |||
new HSSFConditionalFormattingRule[] | |||
{ | |||
rule1, rule2, rule3 | |||
}); | |||
} | |||
/** | |||
* gets Conditional Formatting object at a particular index | |||
* |
@@ -0,0 +1,46 @@ | |||
/* ==================================================================== | |||
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.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 extends org.apache.poi.ss.util.CellRangeAddress { | |||
public CellRangeAddress(int firstRow, int lastRow, int firstCol, int lastCol) { | |||
super(firstRow, lastRow, firstCol, lastCol); | |||
} | |||
public CellRangeAddress() { | |||
super(); | |||
} | |||
public CellRangeAddress(RecordInputStream in) { | |||
if (in.remaining() < ENCODED_SIZE) { | |||
// Ran out of data | |||
throw new RuntimeException("Ran out of data reading CellRangeAddress"); | |||
} | |||
_firstRow = in.readUShort(); | |||
_lastRow = in.readUShort(); | |||
_firstCol = in.readUShort(); | |||
_lastCol = in.readUShort(); | |||
} | |||
} |
@@ -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)); | |||
} | |||
} | |||
} |
@@ -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; | |||
} | |||
} | |||
} | |||
@@ -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; | |||
} | |||
} |
@@ -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); | |||
} |
@@ -16,17 +16,12 @@ | |||
==================================================================== */ | |||
/* | |||
* 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; | |||
} | |||
} |
@@ -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; | |||
} | |||
} |
@@ -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; | |||
} | |||
} |
@@ -15,10 +15,8 @@ | |||
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 | |||
*/ |
@@ -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(); | |||
} | |||
} |
@@ -0,0 +1,226 @@ | |||
/* ==================================================================== | |||
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.model; | |||
import org.apache.poi.hslf.record.*; | |||
import org.apache.poi.hslf.usermodel.SlideShow; | |||
/** | |||
* Header / Footer settings. | |||
* | |||
* @author Yegor Kozlov | |||
*/ | |||
public class HeadersFooters { | |||
private HeadersFootersContainer _container; | |||
private boolean _newRecord; | |||
private SlideShow _ppt; | |||
public HeadersFooters(HeadersFootersContainer rec, SlideShow ppt, boolean newRecord){ | |||
_container = rec; | |||
_newRecord = newRecord; | |||
_ppt = ppt; | |||
} | |||
/** | |||
* Headers's text | |||
* | |||
* @return Headers's text | |||
*/ | |||
public String getHeaderText(){ | |||
CString cs = _container.getHeaderAtom(); | |||
return cs == null ? null : cs.getText(); | |||
} | |||
/** | |||
* Sets headers's text | |||
* | |||
* @param text headers's text | |||
*/ | |||
public void setHeaderText(String text){ | |||
if(_newRecord) attach(); | |||
setHeaderVisible(true); | |||
CString cs = _container.getHeaderAtom(); | |||
if(cs == null) cs = _container.addHeaderAtom(); | |||
cs.setText(text); | |||
} | |||
/** | |||
* Footer's text | |||
* | |||
* @return Footer's text | |||
*/ | |||
public String getFooterText(){ | |||
CString cs = _container.getFooterAtom(); | |||
return cs == null ? null : cs.getText(); | |||
} | |||
/** | |||
* Sets footers's text | |||
* | |||
* @param text footers's text | |||
*/ | |||
public void setFootersText(String text){ | |||
if(_newRecord) attach(); | |||
setFooterVisible(true); | |||
CString cs = _container.getFooterAtom(); | |||
if(cs == null) cs = _container.addFooterAtom(); | |||
cs.setText(text); | |||
} | |||
/** | |||
* This is the date that the user wants in the footers, instead of today's date. | |||
* | |||
* @return custom user date | |||
*/ | |||
public String getDateTimeText(){ | |||
CString cs = _container.getUserDateAtom(); | |||
return cs == null ? null : cs.getText(); | |||
} | |||
/** | |||
* Sets custom user date to be displayed instead of today's date. | |||
* | |||
* @param text custom user date | |||
*/ | |||
public void setDateTimeText(String text){ | |||
if(_newRecord) attach(); | |||
setUserDateVisible(true); | |||
setDateTimeVisible(true); | |||
CString cs = _container.getUserDateAtom(); | |||
if(cs == null) cs = _container.addUserDateAtom(); | |||
cs.setText(text); | |||
} | |||
/** | |||
* whether the footer text is displayed. | |||
*/ | |||
public boolean isFooterVisible(){ | |||
return _container.getHeadersFootersAtom().getFlag(HeadersFootersAtom.fHasFooter); | |||
} | |||
/** | |||
* whether the footer text is displayed. | |||
*/ | |||
public void setFooterVisible(boolean flag){ | |||
if(_newRecord) attach(); | |||
_container.getHeadersFootersAtom().setFlag(HeadersFootersAtom.fHasFooter, flag); | |||
} | |||
/** | |||
* whether the header text is displayed. | |||
*/ | |||
public boolean isHeaderVisible(){ | |||
return _container.getHeadersFootersAtom().getFlag(HeadersFootersAtom.fHasHeader); | |||
} | |||
/** | |||
* whether the header text is displayed. | |||
*/ | |||
public void setHeaderVisible(boolean flag){ | |||
if(_newRecord) attach(); | |||
_container.getHeadersFootersAtom().setFlag(HeadersFootersAtom.fHasHeader, flag); | |||
} | |||
/** | |||
* whether the date is displayed in the footer. | |||
*/ | |||
public boolean isDateTimeVisible(){ | |||
return _container.getHeadersFootersAtom().getFlag(HeadersFootersAtom.fHasDate); | |||
} | |||
/** | |||
* whether the date is displayed in the footer. | |||
*/ | |||
public void setDateTimeVisible(boolean flag){ | |||
if(_newRecord) attach(); | |||
_container.getHeadersFootersAtom().setFlag(HeadersFootersAtom.fHasDate, flag); | |||
} | |||
/** | |||
* whether the custom user date is used instead of today's date. | |||
*/ | |||
public boolean isUserDateVisible(){ | |||
return _container.getHeadersFootersAtom().getFlag(HeadersFootersAtom.fHasUserDate); | |||
} | |||
/** | |||
* whether the date is displayed in the footer. | |||
*/ | |||
public void setUserDateVisible(boolean flag){ | |||
if(_newRecord) attach(); | |||
_container.getHeadersFootersAtom().setFlag(HeadersFootersAtom.fHasUserDate, flag); | |||
} | |||
/** | |||
* whether the slide number is displayed in the footer. | |||
*/ | |||
public boolean isSlideNumberVisible(){ | |||
return _container.getHeadersFootersAtom().getFlag(HeadersFootersAtom.fHasSlideNumber); | |||
} | |||
/** | |||
* whether the slide number is displayed in the footer. | |||
*/ | |||
public void setSlideNumberVisible(boolean flag){ | |||
if(_newRecord) attach(); | |||
_container.getHeadersFootersAtom().setFlag(HeadersFootersAtom.fHasSlideNumber, flag); | |||
} | |||
/** | |||
* An integer that specifies the format ID to be used to style the datetime. | |||
* | |||
* @return an integer that specifies the format ID to be used to style the datetime. | |||
*/ | |||
public int getDateTimeFormat(){ | |||
return _container.getHeadersFootersAtom().getFormatId(); | |||
} | |||
/** | |||
* An integer that specifies the format ID to be used to style the datetime. | |||
* | |||
* @param formatId an integer that specifies the format ID to be used to style the datetime. | |||
*/ | |||
public void setDateTimeFormat(int formatId){ | |||
if(_newRecord) attach(); | |||
_container.getHeadersFootersAtom().setFormatId(formatId); | |||
} | |||
/** | |||
* Attach this HeadersFootersContainer to the parent Document record | |||
*/ | |||
private void attach(){ | |||
Document doc = _ppt.getDocumentRecord(); | |||
Record[] ch = doc.getChildRecords(); | |||
Record lst = null; | |||
for (int i=0; i < ch.length; i++){ | |||
if(ch[i].getRecordType() == RecordTypes.List.typeID){ | |||
lst = ch[i]; | |||
break; | |||
} | |||
} | |||
doc.addChildAfter(_container, lst); | |||
_newRecord = false; | |||
} | |||
} |
@@ -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); | |||
} | |||
} |
@@ -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); | |||
} | |||
@@ -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; |
@@ -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 |
@@ -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; |
@@ -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; |
@@ -0,0 +1,207 @@ | |||
/* ==================================================================== | |||
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 org.apache.poi.util.LittleEndian; | |||
import java.io.IOException; | |||
import java.io.OutputStream; | |||
/** | |||
* An atom record that specifies options for displaying headers and footers | |||
* on a presentation slide or notes slide. | |||
* | |||
* @author Yegor Kozlov | |||
*/ | |||
public class HeadersFootersAtom extends RecordAtom { | |||
/** | |||
* A bit that specifies whether the date is displayed in the footer. | |||
* @see {@link #getMask()}, {@link #setMask(int)}}, | |||
*/ | |||
public static final int fHasDate = 1; | |||
/** | |||
* A bit that specifies whether the current datetime is used for displaying the datetime. | |||
* @see {@link #getMask()}, {@link #setMask(int)}}, | |||
*/ | |||
public static final int fHasTodayDate = 2; | |||
/** | |||
* A bit that specifies whether the date specified in UserDateAtom record | |||
* is used for displaying the datetime. | |||
* | |||
* @see {@link #getMask()}, {@link #setMask(int)}}, | |||
*/ | |||
public static final int fHasUserDate = 4; | |||
/** | |||
* A bit that specifies whether the slide number is displayed in the footer. | |||
* | |||
* @see {@link #getMask()}, {@link #setMask(int)}}, | |||
*/ | |||
public static final int fHasSlideNumber = 8; | |||
/** | |||
* bit that specifies whether the header text is displayed. | |||
* | |||
* @see {@link #getMask()}, {@link #setMask(int)}}, | |||
*/ | |||
public static final int fHasHeader = 16; | |||
/** | |||
* bit that specifies whether the footer text is displayed. | |||
* | |||
* @see {@link #getMask()}, {@link #setMask(int)}}, | |||
*/ | |||
public static final int fHasFooter = 32; | |||
/** | |||
* record header | |||
*/ | |||
private byte[] _header; | |||
/** | |||
* record data | |||
*/ | |||
private byte[] _recdata; | |||
/** | |||
* Build an instance of <code>HeadersFootersAtom</code> from on-disk data | |||
*/ | |||
protected HeadersFootersAtom(byte[] source, int start, int len) { | |||
// Get the header | |||
_header = new byte[8]; | |||
System.arraycopy(source,start,_header,0,8); | |||
// Grab the record data | |||
_recdata = new byte[len-8]; | |||
System.arraycopy(source,start+8,_recdata,0,len-8); | |||
} | |||
/** | |||
* Create a new instance of <code>HeadersFootersAtom</code> | |||
*/ | |||
public HeadersFootersAtom() { | |||
_recdata = new byte[4]; | |||
_header = new byte[8]; | |||
LittleEndian.putShort(_header, 2, (short)getRecordType()); | |||
LittleEndian.putInt(_header, 4, _recdata.length); | |||
} | |||
public long getRecordType() { | |||
return RecordTypes.HeadersFootersAtom.typeID; | |||
} | |||
/** | |||
* Write the contents of the record back, so it can be written to disk | |||
*/ | |||
public void writeOut(OutputStream out) throws IOException { | |||
out.write(_header); | |||
out.write(_recdata); | |||
} | |||
/** | |||
* A signed integer that specifies the format ID to be used to style the datetime. | |||
* <p> | |||
* It MUST be in the range [0, 12]. </br> | |||
* This value is converted into a string as specified by the index field of the DateTimeMCAtom record. | |||
* It MUST be ignored unless fHasTodayDate is TRUE. | |||
* </b> | |||
* | |||
* @return A signed integer that specifies the format ID to be used to style the datetime. | |||
*/ | |||
public int getFormatId(){ | |||
return LittleEndian.getShort(_recdata, 0); | |||
} | |||
/** | |||
* A signed integer that specifies the format ID to be used to style the datetime. | |||
* | |||
* @param formatId A signed integer that specifies the format ID to be used to style the datetime. | |||
*/ | |||
public void setFormatId(int formatId){ | |||
LittleEndian.putUShort(_recdata, 0, formatId); | |||
} | |||
/** | |||
* A bit mask specifying options for displaying headers and footers | |||
* | |||
* <li> A - {@link #fHasDate} (1 bit): A bit that specifies whether the date is displayed in the footer. | |||
* <li> B - {@link #fHasTodayDate} (1 bit): A bit that specifies whether the current datetime is used for | |||
* displaying the datetime. | |||
* <li> C - {@link #fHasUserDate} (1 bit): A bit that specifies whether the date specified in UserDateAtom record | |||
* is used for displaying the datetime. | |||
* <li> D - {@link #fHasSlideNumber} (1 bit): A bit that specifies whether the slide number is displayed in the footer. | |||
* <li> E - {@link #fHasHeader} (1 bit): A bit that specifies whether the header text specified by HeaderAtom | |||
* record is displayed. | |||
* <li> F - {@link #fHasFooter} (1 bit): A bit that specifies whether the footer text specified by FooterAtom | |||
* record is displayed. | |||
* <li> reserved (10 bits): MUST be zero and MUST be ignored. | |||
* | |||
* @return A bit mask specifying options for displaying headers and footers | |||
*/ | |||
public int getMask(){ | |||
return LittleEndian.getShort(_recdata, 2); | |||
} | |||
/** | |||
* A bit mask specifying options for displaying headers and footers | |||
* | |||
* @param mask A bit mask specifying options for displaying headers and footers | |||
*/ | |||
public void setMask(int mask){ | |||
LittleEndian.putUShort(_recdata, 2, mask); | |||
} | |||
/** | |||
* @param bit the bit to check | |||
* @return whether the specified flag is set | |||
*/ | |||
public boolean getFlag(int bit){ | |||
return (getMask() & bit) != 0; | |||
} | |||
/** | |||
* @param bit the bit to set | |||
* @param value whether the specified bit is set | |||
*/ | |||
public void setFlag(int bit, boolean value){ | |||
int mask = getMask(); | |||
if(value) mask |= bit; | |||
else mask &= ~bit; | |||
setMask(mask); | |||
} | |||
public String toString(){ | |||
StringBuffer buf = new StringBuffer(); | |||
buf.append("HeadersFootersAtom\n"); | |||
buf.append("\tFormatId: " + getFormatId() + "\n"); | |||
buf.append("\tMask : " + getMask() + "\n"); | |||
buf.append("\t fHasDate : " + getFlag(fHasDate) + "\n"); | |||
buf.append("\t fHasTodayDate : " + getFlag(fHasTodayDate) + "\n"); | |||
buf.append("\t fHasUserDate : " + getFlag(fHasUserDate) + "\n"); | |||
buf.append("\t fHasSlideNumber : " + getFlag(fHasSlideNumber) + "\n"); | |||
buf.append("\t fHasHeader : " + getFlag(fHasHeader) + "\n"); | |||
buf.append("\t fHasFooter : " + getFlag(fHasFooter) + "\n"); | |||
return buf.toString(); | |||
} | |||
} |
@@ -0,0 +1,212 @@ | |||
/* ==================================================================== | |||
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 org.apache.poi.util.LittleEndian; | |||
import org.apache.poi.util.POILogger; | |||
import java.io.OutputStream; | |||
import java.io.IOException; | |||
/** | |||
* A container record that specifies information about the footers on a presentation slide. | |||
* <p> | |||
* It contains:<br> | |||
* <li> 1. {@link HeadersFootersAtom} | |||
* <li> 2. {@link CString }, Instance UserDate (0), optional: Stores the user's date. | |||
* This is the date that the user wants in the footers, instead of today's date. | |||
* <li> 3. {@link CString }, Instance Header (1), optional: Stores the Header's contents. | |||
* <li> 4. {@link CString }, Instance Footer (2), optional: Stores the Footer's contents. | |||
* </p> | |||
* | |||
* @author Yegor Kozlov | |||
*/ | |||
public class HeadersFootersContainer extends RecordContainer { | |||
/** | |||
* "instance" field in the record header indicating that this HeadersFootersContaine | |||
* is applied for slides | |||
*/ | |||
public static final short SlideHeadersFootersContainer = 0x3F; | |||
/** | |||
* "instance" field in the record header indicating that this HeadersFootersContaine | |||
* is applied for notes and handouts | |||
*/ | |||
public static final short NotesHeadersFootersContainer = 0x4F; | |||
public static final int USERDATEATOM = 0; | |||
public static final int HEADERATOM = 1; | |||
public static final int FOOTERATOM = 2; | |||
private byte[] _header; | |||
private HeadersFootersAtom hdAtom; | |||
private CString csDate, csHeader, csFooter; | |||
protected HeadersFootersContainer(byte[] source, int start, int len) { | |||
// Grab the header | |||
_header = new byte[8]; | |||
System.arraycopy(source,start,_header,0,8); | |||
_children = Record.findChildRecords(source,start+8,len-8); | |||
for(int i=0; i < _children.length; i++){ | |||
if(_children[i] instanceof HeadersFootersAtom) hdAtom = (HeadersFootersAtom)_children[i]; | |||
else if(_children[i] instanceof CString) { | |||
CString cs = (CString)_children[i]; | |||
int opts = cs.getOptions() >> 4; | |||
switch(opts){ | |||
case USERDATEATOM: csDate = cs; break; | |||
case HEADERATOM: csHeader = cs; break; | |||
case FOOTERATOM: csFooter = cs; break; | |||
default: | |||
logger.log(POILogger.WARN, "Unexpected CString.Options in HeadersFootersContainer: " + opts); | |||
break; | |||
} | |||
} else { | |||
logger.log(POILogger.WARN, "Unexpected record in HeadersFootersContainer: " + _children[i]); | |||
} | |||
} | |||
} | |||
public HeadersFootersContainer(short options) { | |||
_header = new byte[8]; | |||
LittleEndian.putShort(_header, 0, options); | |||
LittleEndian.putShort(_header, 2, (short)getRecordType()); | |||
hdAtom = new HeadersFootersAtom(); | |||
_children = new Record[]{ | |||
hdAtom | |||
}; | |||
csDate = csHeader = csFooter = null; | |||
} | |||
/** | |||
* Return the type, which is <code>{@link RecordTypes#HeadersFooters}</code> | |||
*/ | |||
public long getRecordType() { | |||
return RecordTypes.HeadersFooters.typeID; | |||
} | |||
/** | |||
* Must be either {@link #SlideHeadersFootersContainer} or {@link #NotesHeadersFootersContainer} | |||
* | |||
* @return "instance" field in the record header | |||
*/ | |||
public int getOptions(){ | |||
return LittleEndian.getShort(_header, 0); | |||
} | |||
/** | |||
* Write the contents of the record back, so it can be written to disk | |||
*/ | |||
public void writeOut(OutputStream out) throws IOException { | |||
writeOut(_header[0],_header[1],getRecordType(),_children,out); | |||
} | |||
/** | |||
* HeadersFootersAtom stores the basic information of the header and footer structure. | |||
* | |||
* @return <code>HeadersFootersAtom</code> | |||
*/ | |||
public HeadersFootersAtom getHeadersFootersAtom(){ | |||
return hdAtom; | |||
} | |||
/** | |||
* A {@link CString} record that stores the user's date. | |||
* <p>This is the date that the user wants in the footers, instead of today's date.</p> | |||
* | |||
* @return A {@link CString} record that stores the user's date or <code>null</code> | |||
*/ | |||
public CString getUserDateAtom(){ | |||
return csDate; | |||
} | |||
/** | |||
* A {@link CString} record that stores the Header's contents. | |||
* | |||
* @return A {@link CString} record that stores the Header's contents or <code>null</code> | |||
*/ | |||
public CString getHeaderAtom(){ | |||
return csHeader; | |||
} | |||
/** | |||
* A {@link CString} record that stores the Footers's contents. | |||
* | |||
* @return A {@link CString} record that stores the Footers's contents or <code>null</code> | |||
*/ | |||
public CString getFooterAtom(){ | |||
return csFooter; | |||
} | |||
/** | |||
* Insert a {@link CString} record that stores the user's date. | |||
* | |||
* @return the created {@link CString} record that stores the user's date. | |||
*/ | |||
public CString addUserDateAtom(){ | |||
if(csDate != null) return csDate; | |||
csDate = new CString(); | |||
csDate.setOptions(USERDATEATOM << 4); | |||
addChildAfter(csDate, hdAtom); | |||
return csDate; | |||
} | |||
/** | |||
* Insert a {@link CString} record that stores the user's date. | |||
* | |||
* @return the created {@link CString} record that stores the user's date. | |||
*/ | |||
public CString addHeaderAtom(){ | |||
if(csHeader != null) return csHeader; | |||
csHeader = new CString(); | |||
csHeader.setOptions(HEADERATOM << 4); | |||
Record r = hdAtom; | |||
if(csDate != null) r = hdAtom; | |||
addChildAfter(csHeader, r); | |||
return csHeader; | |||
} | |||
/** | |||
* Insert a {@link CString} record that stores the user's date. | |||
* | |||
* @return the created {@link CString} record that stores the user's date. | |||
*/ | |||
public CString addFooterAtom(){ | |||
if(csFooter != null) return csFooter; | |||
csFooter = new CString(); | |||
csFooter.setOptions(FOOTERATOM << 4); | |||
Record r = hdAtom; | |||
if(csHeader != null) r = csHeader; | |||
else if(csDate != null) r = csDate; | |||
addChildAfter(csFooter, r); | |||
return csFooter; | |||
} | |||
} |
@@ -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); |
@@ -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); | |||
} | |||
} |
@@ -0,0 +1,118 @@ | |||
/* ==================================================================== | |||
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.model; | |||
import java.io.*; | |||
import org.apache.poi.hslf.usermodel.SlideShow; | |||
import junit.framework.TestCase; | |||
/** | |||
* Test {@link org.apache.poi.hslf.model.HeadersFooters} object | |||
*/ | |||
public class TestHeadersFooters extends TestCase | |||
{ | |||
public static final String cwd = System.getProperty("HSLF.testdata.path"); | |||
public void testRead() throws Exception | |||
{ | |||
File file = new File(cwd, "headers_footers.ppt"); | |||
FileInputStream is = new FileInputStream(file); | |||
SlideShow ppt = new SlideShow(is); | |||
is.close(); | |||
HeadersFooters slideHdd = ppt.getSlideHeadersFooters(); | |||
assertTrue(slideHdd.isFooterVisible()); | |||
assertEquals("Global Slide Footer", slideHdd.getFooterText()); | |||
assertTrue(slideHdd.isSlideNumberVisible()); | |||
assertFalse(slideHdd.isHeaderVisible()); | |||
assertNull(slideHdd.getHeaderText()); | |||
assertFalse(slideHdd.isUserDateVisible()); | |||
assertNull(slideHdd.getDateTimeText()); | |||
HeadersFooters notesHdd = ppt.getNotesHeadersFooters(); | |||
assertTrue(notesHdd.isFooterVisible()); | |||
assertEquals("Notes Footer", notesHdd.getFooterText()); | |||
assertTrue(notesHdd.isHeaderVisible()); | |||
assertEquals("Notes Header", notesHdd.getHeaderText()); | |||
assertTrue(notesHdd.isUserDateVisible()); | |||
assertNull(notesHdd.getDateTimeText()); | |||
Slide[] slide = ppt.getSlides(); | |||
//the first slide uses presentation-scope headers / footers | |||
HeadersFooters hd1 = slide[0].getHeadersFooters(); | |||
assertEquals(slideHdd.isFooterVisible(), hd1.isFooterVisible()); | |||
assertEquals(slideHdd.getFooterText(), hd1.getFooterText()); | |||
assertEquals(slideHdd.isSlideNumberVisible(), hd1.isSlideNumberVisible()); | |||
assertEquals(slideHdd.isHeaderVisible(), hd1.isHeaderVisible()); | |||
assertEquals(slideHdd.getHeaderText(), hd1.getHeaderText()); | |||
assertEquals(slideHdd.isUserDateVisible(), hd1.isUserDateVisible()); | |||
assertEquals(slideHdd.getDateTimeText(), hd1.getDateTimeText()); | |||
//the first slide uses per-slide headers / footers | |||
HeadersFooters hd2 = slide[1].getHeadersFooters(); | |||
assertEquals(true, hd2.isFooterVisible()); | |||
assertEquals("per-slide footer", hd2.getFooterText()); | |||
assertEquals(true, hd2.isUserDateVisible()); | |||
assertEquals("custom date format", hd2.getDateTimeText()); | |||
} | |||
public void testCreateSlideFooters() throws Exception | |||
{ | |||
SlideShow ppt = new SlideShow(); | |||
HeadersFooters hdd = ppt.getSlideHeadersFooters(); | |||
hdd.setFootersText("My slide footer"); | |||
hdd.setSlideNumberVisible(true); | |||
ByteArrayOutputStream out = new ByteArrayOutputStream(); | |||
ppt.write(out); | |||
byte[] b = out.toByteArray(); | |||
SlideShow ppt2 = new SlideShow(new ByteArrayInputStream(b)); | |||
HeadersFooters hdd2 = ppt2.getSlideHeadersFooters(); | |||
assertTrue(hdd2.isSlideNumberVisible()); | |||
assertTrue(hdd2.isFooterVisible()); | |||
assertEquals("My slide footer", hdd2.getFooterText()); | |||
} | |||
public void testCreateNotesFooters() throws Exception | |||
{ | |||
SlideShow ppt = new SlideShow(); | |||
HeadersFooters hdd = ppt.getNotesHeadersFooters(); | |||
hdd.setFootersText("My notes footer"); | |||
hdd.setHeaderText("My notes header"); | |||
hdd.setSlideNumberVisible(true); | |||
ByteArrayOutputStream out = new ByteArrayOutputStream(); | |||
ppt.write(out); | |||
byte[] b = out.toByteArray(); | |||
SlideShow ppt2 = new SlideShow(new ByteArrayInputStream(b)); | |||
HeadersFooters hdd2 = ppt2.getNotesHeadersFooters(); | |||
assertTrue(hdd2.isSlideNumberVisible()); | |||
assertTrue(hdd2.isFooterVisible()); | |||
assertEquals("My notes footer", hdd2.getFooterText()); | |||
assertTrue(hdd2.isHeaderVisible()); | |||
assertEquals("My notes header", hdd2.getHeaderText()); | |||
} | |||
} |
@@ -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++) { |
@@ -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]); | |||
} | |||
} | |||
} |
@@ -0,0 +1,95 @@ | |||
/* ==================================================================== | |||
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.util.Arrays; | |||
/** | |||
* Tests that {@link HeadersFootersAtom} works properly | |||
* | |||
* @author Yegor Kozlov | |||
*/ | |||
public class TestHeadersFootersAtom extends TestCase { | |||
// From a real file | |||
private byte[] data = new byte[] { | |||
0x00, 0x00, (byte)0xDA, 0x0F, 0x04, 0x00, 0x00, 00, | |||
0x00, 0x00, 0x23, 0x00 }; | |||
public void testRead() throws Exception { | |||
HeadersFootersAtom record = new HeadersFootersAtom(data, 0, data.length); | |||
assertEquals(RecordTypes.HeadersFootersAtom.typeID, record.getRecordType()); | |||
assertEquals(0, record.getFormatId()); | |||
assertEquals(0x23, record.getMask()); | |||
assertTrue(record.getFlag(HeadersFootersAtom.fHasDate)); | |||
assertTrue(record.getFlag(HeadersFootersAtom.fHasTodayDate)); | |||
assertFalse(record.getFlag(HeadersFootersAtom.fHasUserDate)); | |||
assertFalse(record.getFlag(HeadersFootersAtom.fHasSlideNumber)); | |||
assertFalse(record.getFlag(HeadersFootersAtom.fHasHeader)); | |||
assertTrue(record.getFlag(HeadersFootersAtom.fHasFooter)); | |||
} | |||
public void testWrite() throws Exception { | |||
HeadersFootersAtom record = new HeadersFootersAtom(data, 0, data.length); | |||
ByteArrayOutputStream baos = new ByteArrayOutputStream(); | |||
record.writeOut(baos); | |||
byte[] b = baos.toByteArray(); | |||
assertTrue(Arrays.equals(data, b)); | |||
} | |||
public void testNewRecord() throws Exception { | |||
HeadersFootersAtom record = new HeadersFootersAtom(); | |||
record.setFlag(HeadersFootersAtom.fHasDate, true); | |||
record.setFlag(HeadersFootersAtom.fHasTodayDate, true); | |||
record.setFlag(HeadersFootersAtom.fHasFooter, true); | |||
ByteArrayOutputStream baos = new ByteArrayOutputStream(); | |||
record.writeOut(baos); | |||
byte[] b = baos.toByteArray(); | |||
assertTrue(Arrays.equals(data, b)); | |||
} | |||
public void testFlags() throws Exception { | |||
HeadersFootersAtom record = new HeadersFootersAtom(); | |||
//in a new record all the bits are 0 | |||
for(int i = 0; i < 6; i++) assertFalse(record.getFlag(1 << i)); | |||
record.setFlag(HeadersFootersAtom.fHasTodayDate, true); | |||
assertTrue(record.getFlag(HeadersFootersAtom.fHasTodayDate)); | |||
record.setFlag(HeadersFootersAtom.fHasTodayDate, true); | |||
assertTrue(record.getFlag(HeadersFootersAtom.fHasTodayDate)); | |||
record.setFlag(HeadersFootersAtom.fHasTodayDate, false); | |||
assertFalse(record.getFlag(HeadersFootersAtom.fHasTodayDate)); | |||
record.setFlag(HeadersFootersAtom.fHasTodayDate, false); | |||
assertFalse(record.getFlag(HeadersFootersAtom.fHasTodayDate)); | |||
} | |||
} |
@@ -0,0 +1,171 @@ | |||
/* ==================================================================== | |||
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.util.Arrays; | |||
/** | |||
* Tests that {@link HeadersFootersContainer} works properly | |||
* | |||
* @author Yegor Kozlov | |||
*/ | |||
public class TestHeadersFootersContainer extends TestCase { | |||
// SlideHeadersFootersContainer | |||
private byte[] slideData = new byte[] { | |||
0x3F, 0x00, (byte)0xD9, 0x0F, 0x2E, 0x00, 0x00, 0x00, | |||
0x00, 0x00, (byte)0xDA, 0x0F, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x23, 0x00, | |||
0x20, 0x00, (byte)0xBA, 0x0F, 0x1A, 0x00, 0x00, 0x00, | |||
0x4D, 0x00, 0x79, 0x00, 0x20, 0x00, 0x46, 0x00, 0x6F, 0x00, 0x6F, 0x00, 0x74, | |||
0x00, 0x65, 0x00, 0x72, 0x00, 0x20, 0x00, 0x2D, 0x00, 0x20, 0x00, 0x31, 0x00 | |||
}; | |||
// NotesHeadersFootersContainer | |||
private byte[] notesData = new byte[] { | |||
0x4F, 0x00, (byte)0xD9, 0x0F, 0x48, 0x00, 0x00, 0x00, | |||
0x00, 0x00, (byte)0xDA, 0x0F, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3D, 0x00, | |||
0x10, 0x00, (byte)0xBA, 0x0F, 0x16, 0x00, 0x00, 0x00, | |||
0x4E, 0x00, 0x6F, 0x00, 0x74, 0x00, 0x65, 0x00, 0x20, 0x00, 0x48, 0x00, | |||
0x65, 0x00, 0x61, 0x00, 0x64, 0x00, 0x65, 0x00, 0x72, 0x00, | |||
0x20, 0x00, (byte)0xBA, 0x0F, 0x16, 0x00, 0x00, 0x00, | |||
0x4E, 0x00, 0x6F, 0x00, 0x74, 0x00, 0x65, 0x00, 0x20, 0x00, 0x46, 0x00, | |||
0x6F, 0x00, 0x6F, 0x00, 0x74, 0x00, 0x65, 0x00, 0x72, 0x00 | |||
}; | |||
public void testReadSlideHeadersFootersContainer() throws Exception { | |||
HeadersFootersContainer record = new HeadersFootersContainer(slideData, 0, slideData.length); | |||
assertEquals(RecordTypes.HeadersFooters.typeID, record.getRecordType()); | |||
assertEquals(HeadersFootersContainer.SlideHeadersFootersContainer, record.getOptions()); | |||
assertEquals(2, record.getChildRecords().length); | |||
HeadersFootersAtom hdd = record.getHeadersFootersAtom(); | |||
assertNotNull(hdd); | |||
CString csFooter = record.getFooterAtom(); | |||
assertNotNull(csFooter); | |||
assertEquals(HeadersFootersContainer.FOOTERATOM, csFooter.getOptions() >> 4); | |||
assertEquals("My Footer - 1", csFooter.getText()); | |||
assertNull(record.getUserDateAtom()); | |||
assertNull(record.getHeaderAtom()); | |||
} | |||
public void testWriteSlideHeadersFootersContainer() throws Exception { | |||
HeadersFootersContainer record = new HeadersFootersContainer(slideData, 0, slideData.length); | |||
ByteArrayOutputStream baos = new ByteArrayOutputStream(); | |||
record.writeOut(baos); | |||
byte[] b = baos.toByteArray(); | |||
assertTrue(Arrays.equals(slideData, b)); | |||
} | |||
public void testNewSlideHeadersFootersContainer() throws Exception { | |||
HeadersFootersContainer record = new HeadersFootersContainer(HeadersFootersContainer.SlideHeadersFootersContainer); | |||
assertNotNull(record.getHeadersFootersAtom()); | |||
assertNull(record.getUserDateAtom()); | |||
assertNull(record.getHeaderAtom()); | |||
assertNull(record.getFooterAtom()); | |||
HeadersFootersAtom hd = record.getHeadersFootersAtom(); | |||
hd.setFlag(HeadersFootersAtom.fHasDate, true); | |||
hd.setFlag(HeadersFootersAtom.fHasTodayDate, true); | |||
hd.setFlag(HeadersFootersAtom.fHasFooter, true); | |||
CString csFooter = record.addFooterAtom(); | |||
assertNotNull(csFooter); | |||
assertEquals(HeadersFootersContainer.FOOTERATOM, csFooter.getOptions() >> 4); | |||
csFooter.setText("My Footer - 1"); | |||
ByteArrayOutputStream baos = new ByteArrayOutputStream(); | |||
record.writeOut(baos); | |||
byte[] b = baos.toByteArray(); | |||
assertTrue(Arrays.equals(slideData, b)); | |||
} | |||
public void testReadNotesHeadersFootersContainer() throws Exception { | |||
HeadersFootersContainer record = new HeadersFootersContainer(notesData, 0, notesData.length); | |||
assertEquals(RecordTypes.HeadersFooters.typeID, record.getRecordType()); | |||
assertEquals(HeadersFootersContainer.NotesHeadersFootersContainer, record.getOptions()); | |||
assertEquals(3, record.getChildRecords().length); | |||
HeadersFootersAtom hdd = record.getHeadersFootersAtom(); | |||
assertNotNull(hdd); | |||
CString csHeader = record.getHeaderAtom(); | |||
assertNotNull(csHeader); | |||
assertEquals(HeadersFootersContainer.HEADERATOM, csHeader.getOptions() >> 4); | |||
assertEquals("Note Header", csHeader.getText()); | |||
CString csFooter = record.getFooterAtom(); | |||
assertNotNull(csFooter); | |||
assertEquals(HeadersFootersContainer.FOOTERATOM, csFooter.getOptions() >> 4); | |||
assertEquals("Note Footer", csFooter.getText()); | |||
} | |||
public void testWriteNotesHeadersFootersContainer() throws Exception { | |||
HeadersFootersContainer record = new HeadersFootersContainer(notesData, 0, notesData.length); | |||
ByteArrayOutputStream baos = new ByteArrayOutputStream(); | |||
record.writeOut(baos); | |||
byte[] b = baos.toByteArray(); | |||
assertTrue(Arrays.equals(notesData, b)); | |||
} | |||
public void testNewNotesHeadersFootersContainer() throws Exception { | |||
HeadersFootersContainer record = new HeadersFootersContainer(HeadersFootersContainer.NotesHeadersFootersContainer); | |||
assertNotNull(record.getHeadersFootersAtom()); | |||
assertNull(record.getUserDateAtom()); | |||
assertNull(record.getHeaderAtom()); | |||
assertNull(record.getFooterAtom()); | |||
HeadersFootersAtom hd = record.getHeadersFootersAtom(); | |||
hd.setFlag(HeadersFootersAtom.fHasDate, true); | |||
hd.setFlag(HeadersFootersAtom.fHasTodayDate, false); | |||
hd.setFlag(HeadersFootersAtom.fHasUserDate, true); | |||
hd.setFlag(HeadersFootersAtom.fHasSlideNumber, true); | |||
hd.setFlag(HeadersFootersAtom.fHasHeader, true); | |||
hd.setFlag(HeadersFootersAtom.fHasFooter, true); | |||
CString csHeader = record.addHeaderAtom(); | |||
assertNotNull(csHeader); | |||
assertEquals(HeadersFootersContainer.HEADERATOM, csHeader.getOptions() >> 4); | |||
csHeader.setText("Note Header"); | |||
CString csFooter = record.addFooterAtom(); | |||
assertNotNull(csFooter); | |||
assertEquals(HeadersFootersContainer.FOOTERATOM, csFooter.getOptions() >> 4); | |||
csFooter.setText("Note Footer"); | |||
ByteArrayOutputStream baos = new ByteArrayOutputStream(); | |||
record.writeOut(baos); | |||
byte[] b = baos.toByteArray(); | |||
assertTrue(Arrays.equals(notesData, b)); | |||
} | |||
} |
@@ -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 |
@@ -17,13 +17,10 @@ | |||
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); |
@@ -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); | |||
} | |||
} |
@@ -1,4 +1,3 @@ | |||
/* ==================================================================== | |||
Licensed to the Apache Software Foundation (ASF) under one or more | |||
contributor license agreements. See the NOTICE file distributed with | |||
@@ -15,19 +14,19 @@ | |||
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)); |
@@ -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); | |||
} | |||
} |
@@ -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()); | |||
} |
@@ -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; | |||
} | |||
} |
@@ -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. | |||
*/ |
@@ -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)); | |||
} | |||
} |
@@ -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(); | |||
} | |||
} |
@@ -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()); | |||
@@ -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; |
@@ -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")); | |||
} | |||
} |
@@ -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); | |||
@@ -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. | |||
*/ | |||