Merged revisions 638786-638802,638805-638811,638813-638814,638816-639230,639233-639241,639243-639253,639255-639486,639488-639601,639603-639835,639837-639917,639919-640056,640058-640710,640712-641156,641158-641184,641186-641795,641797-641798,641800-641933,641935-641963,641965-641966,641968-641995,641997-642230,642232-642562,642564-642565,642568-642570,642572-642573,642576-642736,642739-642877,642879,642881-642890,642892-642903,642905-642945,642947-643624,643626-643653,643655-643669,643671,643673-643830,643832-643833,643835-644342,644344-644472,644474-644508,644510-645347,645349-645351,645353-645559,645561-645565,645568-645951,645953-646193,646195-646311,646313-646404,646406-646665,646667-646853,646855-646869,646871-647151,647153-647185,647187-647277,647279-647566,647568-647573,647575,647578-647711,647714-647737,647739-647823,647825-648155,648157-648202,648204-648273,648275,648277-648302,648304-648333,648335-648588,648590-648622,648625-648673,648675-649141,649144,649146-649556,649558-649795,649799,649801-649910,649912-649913,649915-650128,650131-650132,650134-650137,650140-650914,650916-651991,651993-652284,652286-652287,652289,652291,652293-652297,652299-652328,652330-652425,652427-652445,652447-652560,652562-652933,652935,652937-652993,652995-653116,653118-653124,653126-653483,653487-653519,653522-653550,653552-653607,653609-653667,653669-653674,653676-653814,653817-653830,653832-653891,653893-653944,653946-654055,654057-654355,654357-654365,654367-654648,654651-655215,655217-655277,655279-655281,655283-655911,655913-656212,656214,656216-656251,656253-656698,656700-656756,656758-656892,656894-657135,657137-657165,657168-657179,657181-657354,657356-657357,657359-657701,657703-657874,657876-658032,658034-658284,658286,658288-658301,658303-658307,658309-658321,658323-658335,658337-658348,658351,658353-658832,658834-658983,658985,658987-659066,659068-659402,659404-659428,659430-659451,659453-659454,659456-659461,659463-659477,659479-659524,659526-659571,659574,659576-660255,660257-660262,660264-660279,660281-660343,660345-660473,660475-660827,660829-660833,660835-660888,660890-663321,663323-663435,663437-663764,663766-663854,663856-664219,664221-664489,664494-664514,664516-668013,668015-668142,668144-668152,668154,668156-668256,668258,668260-669139,669141-669455,669457-669657,669659-669808,669810-670189,670191-671321,671323-672229,672231-672549,672551-672552,672554-672561,672563-672566,672568,672571-673049,673051-673852,673854-673862,673864-673986,673988-673996,673998-674347,674349-674890,674892-674910,674912-674936,674938-674952,674954-675078,675080-675085,675087-675217,675219-675660,675662-675670,675672-675716,675718-675726,675728-675733,675735-675775,675777-675782,675784,675786-675791,675794-675852,675854-676200,676202,676204,676206-676220,676222-676309,676311-676456,676458-676994,676996-677027,677030-677040,677042-677056,677058-677375,677377-677968,677970-677971,677973,677975-677994,677996-678286,678288-678538,678540-680393,680395-680469,680471-682349 via svnmerge from

https://svn.apache.org/repos/asf/poi/trunk

........
  r680530 | nick | 2008-07-28 23:10:07 +0100 (Mon, 28 Jul 2008) | 1 line
  
  Some work on bug #45466 - Partial support for removing excel comments (won't work for all excel versions yet)
........
  r680853 | nick | 2008-07-29 22:40:47 +0100 (Tue, 29 Jul 2008) | 1 line
  
  Support for creating new HSLF CurrentUserAtoms
........
  r681530 | josh | 2008-07-31 23:44:48 +0100 (Thu, 31 Jul 2008) | 1 line
  
  Fix for bug 45519 - keep data validation records together
........
  r681572 | josh | 2008-08-01 02:04:28 +0100 (Fri, 01 Aug 2008) | 1 line
  
  Small update for c681530 bug 45519
........
  r682225 | josh | 2008-08-03 23:11:26 +0100 (Sun, 03 Aug 2008) | 1 line
  
  Extensive fixes for data validation (bug 44953)
........
  r682227 | josh | 2008-08-03 23:15:46 +0100 (Sun, 03 Aug 2008) | 1 line
  
  should have been submitted with c682225 - Extensive fixes for data validation (bug 44953)
........
  r682229 | josh | 2008-08-03 23:49:58 +0100 (Sun, 03 Aug 2008) | 1 line
  
  fixed BiffViewer to add some missing record types. Formatted switch/case for readability
........
  r682230 | josh | 2008-08-04 00:13:17 +0100 (Mon, 04 Aug 2008) | 1 line
  
  Small tweaks for data validation (bug 44953)
........
  r682282 | josh | 2008-08-04 09:00:11 +0100 (Mon, 04 Aug 2008) | 1 line
  
  Consolidating various duplicates of CellRangeAddress
........
  r682336 | yegor | 2008-08-04 12:40:25 +0100 (Mon, 04 Aug 2008) | 1 line
  
  support for headers / footers in HSLF
........


git-svn-id: https://svn.apache.org/repos/asf/poi/branches/ooxml@682485 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Nick Burch 2008-08-04 20:02:29 +00:00
parent b6af19763e
commit e43dc536c5
71 changed files with 5459 additions and 4504 deletions

View File

@ -21,197 +21,257 @@ import org.apache.poi.hssf.usermodel.HSSFCell;
import org.apache.poi.hssf.usermodel.HSSFRow;
import org.apache.poi.hssf.usermodel.HSSFSheet;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.ss.util.CellRangeAddress;
import org.apache.poi.ss.util.Region;
/**
* Various utility functions that make working with a region of cells easier.
*
*@author Eric Pugh epugh@upstate.com
*@since July 29, 2002
* Various utility functions that make working with a region of cells easier.
*
* @author Eric Pugh epugh@upstate.com
*/
public final class HSSFRegionUtil
{
public final class HSSFRegionUtil {
/** Constructor for the HSSFRegionUtil object */
private HSSFRegionUtil() {
// no instances of this class
}
private HSSFRegionUtil() {
// no instances of this class
}
/**
* For setting the same property on many cells to the same value
*/
private static final class CellPropertySetter {
/**
* 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();
private final HSSFWorkbook _workbook;
private final String _propertyName;
private final Short _propertyValue;
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 ) );
}
}
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);
}
}
/**
* 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();
private static CellRangeAddress toCRA(Region region) {
return Region.convertToCellRangeAddress(region);
}
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 ) );
}
}
/**
* @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();
/**
* 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();
CellPropertySetter cps = new CellPropertySetter(workbook, HSSFCellUtil.BORDER_LEFT, border);
for (int i = rowStart; i <= rowEnd; i++) {
cps.setProperty(HSSFCellUtil.getRow(i, sheet), column);
}
}
for ( int i = rowStart; i <= rowEnd; i++ ) {
HSSFRow row = HSSFCellUtil.getRow( i, sheet );
HSSFCell cell = HSSFCellUtil.getCell( row, 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();
HSSFCellUtil.setCellStyleProperty(
cell, workbook, HSSFCellUtil.BORDER_RIGHT, new Short( border ) );
}
}
CellPropertySetter cps = new CellPropertySetter(workbook, HSSFCellUtil.LEFT_BORDER_COLOR, color);
for (int i = rowStart; i <= rowEnd; i++) {
cps.setProperty(HSSFCellUtil.getRow(i, sheet), column);
}
}
/**
* 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();
/**
* @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();
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 ) );
}
}
CellPropertySetter cps = new CellPropertySetter(workbook, HSSFCellUtil.BORDER_RIGHT, border);
for (int i = rowStart; i <= rowEnd; i++) {
cps.setProperty(HSSFCellUtil.getRow(i, sheet), column);
}
}
/**
* 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++ ) {
/**
* @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();
HSSFCell cell = HSSFCellUtil.getCell( row, i );
HSSFCellUtil.setCellStyleProperty(
cell, workbook, HSSFCellUtil.BORDER_BOTTOM, new Short( border ) );
}
}
CellPropertySetter cps = new CellPropertySetter(workbook, HSSFCellUtil.RIGHT_BORDER_COLOR, color);
for (int i = rowStart; i <= rowEnd; i++) {
cps.setProperty(HSSFCellUtil.getRow(i, sheet), column);
}
}
/**
* 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 ) );
}
}
/**
* @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);
}
}
/**
* 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++ ) {
/**
* @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);
}
}
HSSFCell cell = HSSFCellUtil.getCell( row, i );
HSSFCellUtil.setCellStyleProperty(
cell, workbook, HSSFCellUtil.BORDER_TOP, new Short( border ) );
}
}
/**
* @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);
}
}
/**
* 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 ) );
}
}
/**
* @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);
}
}
}

View File

@ -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>

View File

@ -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>

View File

@ -113,262 +113,138 @@ public final class BiffViewer {
{
switch ( in.getSid() )
{
case ChartRecord.sid:
return new ChartRecord( in );
case ChartFormatRecord.sid:
return new ChartFormatRecord( in );
case SeriesRecord.sid:
return new SeriesRecord( in );
case BeginRecord.sid:
return new BeginRecord( in );
case EndRecord.sid:
return new EndRecord( in );
case BOFRecord.sid:
return new BOFRecord( in );
case InterfaceHdrRecord.sid:
return new InterfaceHdrRecord( in );
case MMSRecord.sid:
return new MMSRecord( in );
case InterfaceEndRecord.sid:
return new InterfaceEndRecord( in );
case WriteAccessRecord.sid:
return new WriteAccessRecord( in );
case CodepageRecord.sid:
return new CodepageRecord( in );
case DSFRecord.sid:
return new DSFRecord( in );
case TabIdRecord.sid:
return new TabIdRecord( in );
case FnGroupCountRecord.sid:
return new FnGroupCountRecord( in );
case WindowProtectRecord.sid:
return new WindowProtectRecord( in );
case ProtectRecord.sid:
return new ProtectRecord( in );
case PasswordRecord.sid:
return new PasswordRecord( in );
case ProtectionRev4Record.sid:
return new ProtectionRev4Record( in );
case PasswordRev4Record.sid:
return new PasswordRev4Record( in );
case WindowOneRecord.sid:
return new WindowOneRecord( in );
case BackupRecord.sid:
return new BackupRecord( in );
case HideObjRecord.sid:
return new HideObjRecord( in );
case DateWindow1904Record.sid:
return new DateWindow1904Record( in );
case PrecisionRecord.sid:
return new PrecisionRecord( in );
case RefreshAllRecord.sid:
return new RefreshAllRecord( in );
case BookBoolRecord.sid:
return new BookBoolRecord( in );
case FontRecord.sid:
return new FontRecord( in );
case FormatRecord.sid:
return new FormatRecord( in );
case ExtendedFormatRecord.sid:
return new ExtendedFormatRecord( in );
case StyleRecord.sid:
return new StyleRecord( in );
case UseSelFSRecord.sid:
return new UseSelFSRecord( in );
case BoundSheetRecord.sid:
return new BoundSheetRecord( in );
case CountryRecord.sid:
return new CountryRecord( in );
case SSTRecord.sid:
return new SSTRecord( in );
case ExtSSTRecord.sid:
return new ExtSSTRecord( in );
case EOFRecord.sid:
return new EOFRecord( in );
case IndexRecord.sid:
return new IndexRecord( in );
case CalcModeRecord.sid:
return new CalcModeRecord( in );
case CalcCountRecord.sid:
return new CalcCountRecord( in );
case RefModeRecord.sid:
return new RefModeRecord( in );
case IterationRecord.sid:
return new IterationRecord( in );
case DeltaRecord.sid:
return new DeltaRecord( in );
case SaveRecalcRecord.sid:
return new SaveRecalcRecord( in );
case PrintHeadersRecord.sid:
return new PrintHeadersRecord( in );
case PrintGridlinesRecord.sid:
return new PrintGridlinesRecord( in );
case GridsetRecord.sid:
return new GridsetRecord( in );
case DrawingGroupRecord.sid:
return new DrawingGroupRecord( in );
case DrawingRecordForBiffViewer.sid:
return new DrawingRecordForBiffViewer( in );
case DrawingSelectionRecord.sid:
return new DrawingSelectionRecord( in );
case GutsRecord.sid:
return new GutsRecord( in );
case DefaultRowHeightRecord.sid:
return new DefaultRowHeightRecord( in );
case WSBoolRecord.sid:
return new WSBoolRecord( in );
case HeaderRecord.sid:
return new HeaderRecord( in );
case FooterRecord.sid:
return new FooterRecord( in );
case HCenterRecord.sid:
return new HCenterRecord( in );
case VCenterRecord.sid:
return new VCenterRecord( in );
case PrintSetupRecord.sid:
return new PrintSetupRecord( in );
case DefaultColWidthRecord.sid:
return new DefaultColWidthRecord( in );
case DimensionsRecord.sid:
return new DimensionsRecord( in );
case RowRecord.sid:
return new RowRecord( in );
case LabelSSTRecord.sid:
return new LabelSSTRecord( in );
case RKRecord.sid:
return new RKRecord( in );
case NumberRecord.sid:
return new NumberRecord( in );
case DBCellRecord.sid:
return new DBCellRecord( in );
case WindowTwoRecord.sid:
return new WindowTwoRecord( in );
case SelectionRecord.sid:
return new SelectionRecord( in );
case ContinueRecord.sid:
return new ContinueRecord( in );
case LabelRecord.sid:
return new LabelRecord( in );
case MulRKRecord.sid:
return new MulRKRecord( in );
case MulBlankRecord.sid:
return new MulBlankRecord( in );
case BlankRecord.sid:
return new BlankRecord( in );
case BoolErrRecord.sid:
return new BoolErrRecord( in );
case ColumnInfoRecord.sid:
return new ColumnInfoRecord( in );
case MergeCellsRecord.sid:
return new MergeCellsRecord( in );
case AreaRecord.sid:
return new AreaRecord( in );
case DataFormatRecord.sid:
return new DataFormatRecord( in );
case BarRecord.sid:
return new BarRecord( in );
case DatRecord.sid:
return new DatRecord( in );
case PlotGrowthRecord.sid:
return new PlotGrowthRecord( in );
case UnitsRecord.sid:
return new UnitsRecord( in );
case FrameRecord.sid:
return new FrameRecord( in );
case ValueRangeRecord.sid:
return new ValueRangeRecord( in );
case SeriesListRecord.sid:
return new SeriesListRecord( in );
case FontBasisRecord.sid:
return new FontBasisRecord( in );
case FontIndexRecord.sid:
return new FontIndexRecord( in );
case LineFormatRecord.sid:
return new LineFormatRecord( in );
case AreaFormatRecord.sid:
return new AreaFormatRecord( in );
case LinkedDataRecord.sid:
return new LinkedDataRecord( in );
case FormulaRecord.sid:
return new FormulaRecord( in );
case SheetPropertiesRecord.sid:
return new SheetPropertiesRecord( in );
case DefaultDataLabelTextPropertiesRecord.sid:
return new DefaultDataLabelTextPropertiesRecord( in );
case TextRecord.sid:
return new TextRecord( in );
case AxisParentRecord.sid:
return new AxisParentRecord( in );
case AxisLineFormatRecord.sid:
return new AxisLineFormatRecord( in );
case SupBookRecord.sid:
return new SupBookRecord( in );
case ExternSheetRecord.sid:
return new ExternSheetRecord( in );
case SCLRecord.sid:
return new SCLRecord( in );
case SeriesToChartGroupRecord.sid:
return new SeriesToChartGroupRecord( in );
case AxisUsedRecord.sid:
return new AxisUsedRecord( in );
case AxisRecord.sid:
return new AxisRecord( in );
case CategorySeriesAxisRecord.sid:
return new CategorySeriesAxisRecord( in );
case AxisOptionsRecord.sid:
return new AxisOptionsRecord( in );
case TickRecord.sid:
return new TickRecord( in );
case SeriesTextRecord.sid:
return new SeriesTextRecord( in );
case ObjectLinkRecord.sid:
return new ObjectLinkRecord( in );
case PlotAreaRecord.sid:
return new PlotAreaRecord( in );
case SeriesIndexRecord.sid:
return new SeriesIndexRecord( in );
case LegendRecord.sid:
return new LegendRecord( in );
case LeftMarginRecord.sid:
return new LeftMarginRecord( in );
case RightMarginRecord.sid:
return new RightMarginRecord( in );
case TopMarginRecord.sid:
return new TopMarginRecord( in );
case BottomMarginRecord.sid:
return new BottomMarginRecord( in );
case PaletteRecord.sid:
return new PaletteRecord( in );
case StringRecord.sid:
return new StringRecord( in );
case NameRecord.sid:
return new NameRecord( in );
case PaneRecord.sid:
return new PaneRecord( in );
case SharedFormulaRecord.sid:
return new SharedFormulaRecord( in);
case ObjRecord.sid:
return new ObjRecord( in);
case TextObjectRecord.sid:
return new TextObjectRecord( in);
case HorizontalPageBreakRecord.sid:
return new HorizontalPageBreakRecord( in);
case VerticalPageBreakRecord.sid:
return new VerticalPageBreakRecord( in);
case WriteProtectRecord.sid:
return new WriteProtectRecord( in);
case FilePassRecord.sid:
return new FilePassRecord(in);
case NoteRecord.sid:
return new NoteRecord( in );
case FileSharingRecord.sid:
return new FileSharingRecord( in );
case HyperlinkRecord.sid:
return new HyperlinkRecord( in );
case TableRecord.sid:
return new TableRecord( in );
case AreaFormatRecord.sid: return new AreaFormatRecord(in);
case AreaRecord.sid: return new AreaRecord(in);
case AxisLineFormatRecord.sid: return new AxisLineFormatRecord(in);
case AxisOptionsRecord.sid: return new AxisOptionsRecord(in);
case AxisParentRecord.sid: return new AxisParentRecord(in);
case AxisRecord.sid: return new AxisRecord(in);
case AxisUsedRecord.sid: return new AxisUsedRecord(in);
case BOFRecord.sid: return new BOFRecord(in);
case BackupRecord.sid: return new BackupRecord(in);
case BarRecord.sid: return new BarRecord(in);
case BeginRecord.sid: return new BeginRecord(in);
case BlankRecord.sid: return new BlankRecord(in);
case BookBoolRecord.sid: return new BookBoolRecord(in);
case BoolErrRecord.sid: return new BoolErrRecord(in);
case BottomMarginRecord.sid: return new BottomMarginRecord(in);
case BoundSheetRecord.sid: return new BoundSheetRecord(in);
case CalcCountRecord.sid: return new CalcCountRecord(in);
case CalcModeRecord.sid: return new CalcModeRecord(in);
case CategorySeriesAxisRecord.sid: return new CategorySeriesAxisRecord(in);
case ChartFormatRecord.sid: return new ChartFormatRecord(in);
case ChartRecord.sid: return new ChartRecord(in);
case CodepageRecord.sid: return new CodepageRecord(in);
case ColumnInfoRecord.sid: return new ColumnInfoRecord(in);
case ContinueRecord.sid: return new ContinueRecord(in);
case CountryRecord.sid: return new CountryRecord(in);
case DBCellRecord.sid: return new DBCellRecord(in);
case DSFRecord.sid: return new DSFRecord(in);
case DatRecord.sid: return new DatRecord(in);
case DataFormatRecord.sid: return new DataFormatRecord(in);
case DateWindow1904Record.sid: return new DateWindow1904Record(in);
case DefaultColWidthRecord.sid:return new DefaultColWidthRecord(in);
case DefaultDataLabelTextPropertiesRecord.sid: return new DefaultDataLabelTextPropertiesRecord(in);
case DefaultRowHeightRecord.sid: return new DefaultRowHeightRecord(in);
case DeltaRecord.sid: return new DeltaRecord(in);
case DimensionsRecord.sid: return new DimensionsRecord(in);
case DrawingGroupRecord.sid: return new DrawingGroupRecord(in);
case DrawingRecordForBiffViewer.sid: return new DrawingRecordForBiffViewer(in);
case DrawingSelectionRecord.sid: return new DrawingSelectionRecord(in);
case DVRecord.sid: return new DVRecord(in);
case DVALRecord.sid: return new DVALRecord(in);
case EOFRecord.sid: return new EOFRecord(in);
case EndRecord.sid: return new EndRecord(in);
case ExtSSTRecord.sid: return new ExtSSTRecord(in);
case ExtendedFormatRecord.sid: return new ExtendedFormatRecord(in);
case ExternSheetRecord.sid: return new ExternSheetRecord(in);
case FilePassRecord.sid: return new FilePassRecord(in);
case FileSharingRecord.sid: return new FileSharingRecord(in);
case FnGroupCountRecord.sid: return new FnGroupCountRecord(in);
case FontBasisRecord.sid: return new FontBasisRecord(in);
case FontIndexRecord.sid: return new FontIndexRecord(in);
case FontRecord.sid: return new FontRecord(in);
case FooterRecord.sid: return new FooterRecord(in);
case FormatRecord.sid: return new FormatRecord(in);
case FormulaRecord.sid: return new FormulaRecord(in);
case FrameRecord.sid: return new FrameRecord(in);
case GridsetRecord.sid: return new GridsetRecord(in);
case GutsRecord.sid: return new GutsRecord(in);
case HCenterRecord.sid: return new HCenterRecord(in);
case HeaderRecord.sid: return new HeaderRecord(in);
case HideObjRecord.sid: return new HideObjRecord(in);
case HorizontalPageBreakRecord.sid: return new HorizontalPageBreakRecord(in);
case HyperlinkRecord.sid: return new HyperlinkRecord(in);
case IndexRecord.sid: return new IndexRecord(in);
case InterfaceEndRecord.sid: return new InterfaceEndRecord(in);
case InterfaceHdrRecord.sid: return new InterfaceHdrRecord(in);
case IterationRecord.sid: return new IterationRecord(in);
case LabelRecord.sid: return new LabelRecord(in);
case LabelSSTRecord.sid: return new LabelSSTRecord(in);
case LeftMarginRecord.sid: return new LeftMarginRecord(in);
case LegendRecord.sid: return new LegendRecord(in);
case LineFormatRecord.sid: return new LineFormatRecord(in);
case LinkedDataRecord.sid: return new LinkedDataRecord(in);
case MMSRecord.sid: return new MMSRecord(in);
case MergeCellsRecord.sid: return new MergeCellsRecord(in);
case MulBlankRecord.sid: return new MulBlankRecord(in);
case MulRKRecord.sid: return new MulRKRecord(in);
case NameRecord.sid: return new NameRecord(in);
case NoteRecord.sid: return new NoteRecord(in);
case NumberRecord.sid: return new NumberRecord(in);
case ObjRecord.sid: return new ObjRecord(in);
case ObjectLinkRecord.sid: return new ObjectLinkRecord(in);
case PaletteRecord.sid: return new PaletteRecord(in);
case PaneRecord.sid: return new PaneRecord(in);
case PasswordRecord.sid: return new PasswordRecord(in);
case PasswordRev4Record.sid: return new PasswordRev4Record(in);
case PlotAreaRecord.sid: return new PlotAreaRecord(in);
case PlotGrowthRecord.sid: return new PlotGrowthRecord(in);
case PrecisionRecord.sid: return new PrecisionRecord(in);
case PrintGridlinesRecord.sid: return new PrintGridlinesRecord(in);
case PrintHeadersRecord.sid: return new PrintHeadersRecord(in);
case PrintSetupRecord.sid: return new PrintSetupRecord(in);
case ProtectRecord.sid: return new ProtectRecord(in);
case ProtectionRev4Record.sid: return new ProtectionRev4Record(in);
case RKRecord.sid: return new RKRecord(in);
case RefModeRecord.sid: return new RefModeRecord(in);
case RefreshAllRecord.sid: return new RefreshAllRecord(in);
case RightMarginRecord.sid: return new RightMarginRecord(in);
case RowRecord.sid: return new RowRecord(in);
case SCLRecord.sid: return new SCLRecord(in);
case SSTRecord.sid: return new SSTRecord(in);
case SaveRecalcRecord.sid: return new SaveRecalcRecord(in);
case SelectionRecord.sid: return new SelectionRecord(in);
case SeriesIndexRecord.sid: return new SeriesIndexRecord(in);
case SeriesListRecord.sid: return new SeriesListRecord(in);
case SeriesRecord.sid: return new SeriesRecord(in);
case SeriesTextRecord.sid: return new SeriesTextRecord(in);
case SeriesToChartGroupRecord.sid: return new SeriesToChartGroupRecord(in);
case SharedFormulaRecord.sid: return new SharedFormulaRecord(in);
case SheetPropertiesRecord.sid:return new SheetPropertiesRecord(in);
case StringRecord.sid: return new StringRecord(in);
case StyleRecord.sid: return new StyleRecord(in);
case SupBookRecord.sid: return new SupBookRecord(in);
case TabIdRecord.sid: return new TabIdRecord(in);
case TableRecord.sid: return new TableRecord(in);
case TextObjectRecord.sid: return new TextObjectRecord(in);
case TextRecord.sid: return new TextRecord(in);
case TickRecord.sid: return new TickRecord(in);
case TopMarginRecord.sid: return new TopMarginRecord(in);
case UnitsRecord.sid: return new UnitsRecord(in);
case UseSelFSRecord.sid: return new UseSelFSRecord(in);
case VCenterRecord.sid: return new VCenterRecord(in);
case ValueRangeRecord.sid: return new ValueRangeRecord(in);
case VerticalPageBreakRecord.sid: return new VerticalPageBreakRecord(in);
case WSBoolRecord.sid: return new WSBoolRecord(in);
case WindowOneRecord.sid: return new WindowOneRecord(in);
case WindowProtectRecord.sid: return new WindowProtectRecord(in);
case WindowTwoRecord.sid: return new WindowTwoRecord(in);
case WriteAccessRecord.sid: return new WriteAccessRecord(in);
case WriteProtectRecord.sid: return new WriteProtectRecord(in);
}
return new UnknownRecord( in );
return new UnknownRecord(in);
}

View File

@ -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

View File

@ -27,6 +27,7 @@ import org.apache.poi.hssf.record.formula.*;
import org.apache.poi.hssf.record.formula.function.FunctionMetadata;
import org.apache.poi.hssf.record.formula.function.FunctionMetadataRegistry;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.hssf.util.AreaReference;
import org.apache.poi.hssf.util.CellReference;
import org.apache.poi.hssf.util.CellReference.NameType;
@ -66,8 +67,11 @@ public final class FormulaParser {
public static final int FORMULA_TYPE_CELL = 0;
public static final int FORMULA_TYPE_SHARED = 1;
public static final int FORMULA_TYPE_ARRAY =2;
public static final int FORMULA_TYPE_CONDFOMRAT = 3;
public static final int FORMULA_TYPE_CONDFORMAT = 3;
public static final int FORMULA_TYPE_NAMEDRANGE = 4;
// this constant is currently very specific. The exact differences from general data
// validation formulas or conditional format formulas is not known yet
public static final int FORMULA_TYPE_DATAVALIDATION_LIST = 5;
private final String formulaString;
private final int formulaLength;
@ -75,12 +79,6 @@ public final class FormulaParser {
private ParseNode _rootNode;
/**
* Used for spotting if we have a cell reference,
* or a named range
*/
private final static Pattern CELL_REFERENCE_PATTERN = Pattern.compile("(?:('?)[^:\\\\/\\?\\*\\[\\]]+\\1!)?\\$?[A-Za-z]+\\$?[\\d]+");
private static char TAB = '\t';
/**
@ -112,9 +110,13 @@ public final class FormulaParser {
}
public static Ptg[] parse(String formula, Workbook book) {
FormulaParser fp = new FormulaParser(formula, book);
return parse(formula, book, FORMULA_TYPE_CELL);
}
public static Ptg[] parse(String formula, Workbook workbook, int formulaType) {
FormulaParser fp = new FormulaParser(formula, workbook);
fp.parse();
return fp.getRPNPtg();
return fp.getRPNPtg(formulaType);
}
/** Read New Character From Input Stream */

View File

@ -66,6 +66,9 @@ final class OperandClassTransformer {
case FormulaParser.FORMULA_TYPE_CELL:
rootNodeOperandClass = Ptg.CLASS_VALUE;
break;
case FormulaParser.FORMULA_TYPE_DATAVALIDATION_LIST:
rootNodeOperandClass = Ptg.CLASS_REF;
break;
default:
throw new RuntimeException("Incomplete code - formula type ("
+ _formulaType + ") not supported yet");

View File

@ -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;

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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();
}
}

View File

@ -1,4 +1,3 @@
/* ====================================================================
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
@ -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;

View File

@ -1,4 +1,3 @@
/* ====================================================================
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
@ -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;
/* package */ static final int ENCODED_SIZE = 6;
private int _firstRow;
private int _lastRow;
private int _firstCol;
private int _lastCol;
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();
}
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;
public void setActiveCellRef(short ref) {
field_4_active_cell_ref_index = ref;
}
/**
* set the number of cell refs (we don't support selection so set to 0
* @param refs - number of references
* @return the pane ID which window pane this is for
*/
public void setNumRefs(short refs)
{
field_5_num_refs = refs;
}
/**
* get which window pane this is for
* @return pane
*/
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;
}
}

View File

@ -25,6 +25,7 @@ import org.apache.poi.hssf.record.CFRuleRecord;
import org.apache.poi.hssf.record.Record;
import org.apache.poi.hssf.record.RecordInputStream;
import org.apache.poi.ss.util.Region;
import org.apache.poi.ss.util.CellRangeAddress;
import org.apache.poi.util.POILogFactory;
import org.apache.poi.util.POILogger;
@ -68,7 +69,7 @@ public final class CFRecordsAggregate extends Record
}
}
public CFRecordsAggregate(Region[] regions, CFRuleRecord[] rules) {
public CFRecordsAggregate(CellRangeAddress[] regions, CFRuleRecord[] rules) {
this(new CFHeaderRecord(regions), rules);
}

View File

@ -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());
}
}

View File

@ -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();
}

View File

@ -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()+")";
}
}

View File

@ -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);
}
}

View File

@ -36,7 +36,7 @@ public final class NumberPtg extends ScalarConstantPtg {
/** Create a NumberPtg from a byte array read from disk */
public NumberPtg(RecordInputStream in)
{
field_1_value = in.readDouble();
this(in.readDouble());
}
/** Create a NumberPtg from a string representation of the number
@ -45,9 +45,12 @@ public final class NumberPtg extends ScalarConstantPtg {
* @param value : String representation of a floating point number
*/
public NumberPtg(String value) {
field_1_value = Double.parseDouble(value);
this(Double.parseDouble(value));
}
public NumberPtg(double value) {
field_1_value = value;
}
public double getValue()
{
@ -67,6 +70,15 @@ public final class NumberPtg extends ScalarConstantPtg {
public String toFormulaString(Workbook book)
{
return "" + getValue();
// TODO - java's rendering of double values is not quite same as excel's
return String.valueOf(field_1_value);
}
public String toString() {
StringBuffer sb = new StringBuffer(64);
sb.append(getClass().getName()).append(" [");
sb.append(field_1_value);
sb.append("]");
return sb.toString();
}
}

View File

@ -41,6 +41,7 @@ import org.apache.poi.ss.usermodel.Workbook;
* @author Jason Height (jheight at chariot dot net dot au)
*/
public abstract class Ptg implements Cloneable {
public static final Ptg[] EMPTY_PTG_ARRAY = { };
/* convert infix order ptg list to rpn order ptg list
* @return List ptgs in RPN order
@ -250,6 +251,9 @@ public abstract class Ptg implements Cloneable {
}
}
private static Ptg[] toPtgArray(List l) {
if (l.isEmpty()) {
return EMPTY_PTG_ARRAY;
}
Ptg[] result = new Ptg[l.size()];
l.toArray(result);
return result;

View File

@ -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);
}
}

View File

@ -34,7 +34,24 @@ import java.util.Iterator;
import org.apache.poi.hssf.model.FormulaParser;
import org.apache.poi.hssf.model.Sheet;
import org.apache.poi.hssf.model.Workbook;
import org.apache.poi.hssf.record.*;
import org.apache.poi.hssf.record.BlankRecord;
import org.apache.poi.hssf.record.BoolErrRecord;
import org.apache.poi.hssf.record.CellValueRecordInterface;
import org.apache.poi.hssf.record.CommonObjectDataSubRecord;
import org.apache.poi.hssf.record.DrawingRecord;
import org.apache.poi.hssf.record.EOFRecord;
import org.apache.poi.hssf.record.ExtendedFormatRecord;
import org.apache.poi.hssf.record.FormulaRecord;
import org.apache.poi.hssf.record.HyperlinkRecord;
import org.apache.poi.hssf.record.LabelSSTRecord;
import org.apache.poi.hssf.record.NoteRecord;
import org.apache.poi.hssf.record.NumberRecord;
import org.apache.poi.hssf.record.ObjRecord;
import org.apache.poi.hssf.record.Record;
import org.apache.poi.hssf.record.StringRecord;
import org.apache.poi.hssf.record.SubRecord;
import org.apache.poi.hssf.record.TextObjectRecord;
import org.apache.poi.hssf.record.UnicodeString;
import org.apache.poi.hssf.record.aggregates.FormulaRecordAggregate;
import org.apache.poi.hssf.record.formula.Ptg;
import org.apache.poi.ss.usermodel.Cell;
@ -1055,11 +1072,18 @@ public class HSSFCell implements Cell
}
/**
* Assign a comment to this cell
* Assign a comment to this cell. If the supplied
* comment is null, the comment for this cell
* will be removed.
*
* @param comment comment associated with this cell
*/
public void setCellComment(Comment comment){
if(comment == null) {
removeCellComment();
return;
}
this.comment = (HSSFComment) comment;
this.comment.setRow((short)record.getRow());
this.comment.setColumn(record.getColumn());
@ -1076,6 +1100,49 @@ public class HSSFCell implements Cell
}
return comment;
}
/**
* Removes the comment for this cell, if
* there is one.
* WARNING - some versions of excel will loose
* all comments after performing this action!
*/
public void removeCellComment() {
HSSFComment comment = findCellComment(sheet, record.getRow(), record.getColumn());
this.comment = null;
if(comment == null) {
// Nothing to do
return;
}
// Zap the underlying NoteRecord
sheet.getRecords().remove(comment.getNoteRecord());
// If we have a TextObjectRecord, is should
// be proceeed by:
// MSODRAWING with container
// OBJ
// MSODRAWING with EscherTextboxRecord
if(comment.getTextObjectRecord() != null) {
TextObjectRecord txo = comment.getTextObjectRecord();
int txoAt = sheet.getRecords().indexOf(txo);
if(sheet.getRecords().get(txoAt-3) instanceof DrawingRecord &&
sheet.getRecords().get(txoAt-2) instanceof ObjRecord &&
sheet.getRecords().get(txoAt-1) instanceof DrawingRecord) {
// Zap these, in reverse order
sheet.getRecords().remove(txoAt-1);
sheet.getRecords().remove(txoAt-2);
sheet.getRecords().remove(txoAt-3);
} else {
throw new IllegalStateException("Found the wrong records before the TextObjectRecord, can't remove comment");
}
// Now remove the text record
sheet.getRecords().remove(txo);
}
}
/**
* Cell comment finder.

View File

@ -156,4 +156,13 @@ public class HSSFComment extends HSSFTextbox implements Comment {
}
super.setString(string);
}
/**
* Returns the underlying Note record
*/
protected NoteRecord getNoteRecord() { return note; }
/**
* Returns the underlying Text record
*/
protected TextObjectRecord getTextObjectRecord() { return txo; }
}

View File

@ -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();
}
/**

View File

@ -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);
}
}

View File

@ -28,7 +28,6 @@ import java.text.NumberFormat;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Stack;
import java.util.TreeMap;
import org.apache.poi.ddf.EscherRecord;
@ -49,13 +48,13 @@ import org.apache.poi.hssf.record.SCLRecord;
import org.apache.poi.hssf.record.VCenterRecord;
import org.apache.poi.hssf.record.WSBoolRecord;
import org.apache.poi.hssf.record.WindowTwoRecord;
import org.apache.poi.hssf.record.aggregates.DataValidityTable;
import org.apache.poi.hssf.record.formula.Ptg;
import org.apache.poi.hssf.record.formula.RefPtg;
import org.apache.poi.hssf.util.HSSFCellRangeAddress;
import org.apache.poi.hssf.util.HSSFDataValidation;
import org.apache.poi.hssf.util.PaneInformation;
import org.apache.poi.ss.usermodel.CellStyle;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.util.CellRangeAddress;
import org.apache.poi.hssf.util.Region;
import org.apache.poi.util.POILogFactory;
import org.apache.poi.util.POILogger;
@ -393,92 +392,19 @@ public class HSSFSheet implements org.apache.poi.ss.usermodel.Sheet
/**
* Creates a data validation object
* @param obj_validation The Data validation object settings
* @param dataValidation The Data validation object settings
*/
public void addValidationData(HSSFDataValidation obj_validation)
{
if ( obj_validation == null )
{
return;
public void addValidationData(HSSFDataValidation dataValidation) {
if (dataValidation == null) {
throw new IllegalArgumentException("objValidation must not be null");
}
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);
DataValidityTable dvt = sheet.getOrCreateDataValidityTable();
//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);
}
//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();
}
}

View File

@ -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
*

View File

@ -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();
}
}

View File

@ -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));
}
}
}

View File

@ -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;
}
}
}

View File

@ -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;
}
}

View File

@ -18,8 +18,6 @@
package org.apache.poi.hssf.util;
import org.apache.poi.hssf.record.MergeCellsRecord.MergedRegion;
/**
* Represents a from/to row/col square. This is a object primitive
* that can be used to represent row,col - row,col just as one would use String
@ -43,16 +41,6 @@ public class Region extends org.apache.poi.ss.util.Region
super(rowFrom, colFrom, rowTo, colTo);
}
/**
* special constructor (I know this is bad but it is so wrong that its right
* okay) that makes a region from a mergedcells's region subrecord.
*/
public Region(MergedRegion region)
{
super(region);
}
public Region(String ref) {
super(ref);
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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
*/

View File

@ -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();
}
}

View File

@ -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;
}
}

View File

@ -24,9 +24,7 @@ import java.util.Vector;
import java.util.Iterator;
import java.awt.*;
import org.apache.poi.hslf.record.SlideAtom;
import org.apache.poi.hslf.record.TextHeaderAtom;
import org.apache.poi.hslf.record.ColorSchemeAtom;
import org.apache.poi.hslf.record.*;
import org.apache.poi.hslf.record.SlideListWithText.SlideAtomsSet;
import org.apache.poi.ddf.EscherDggRecord;
import org.apache.poi.ddf.EscherContainerRecord;
@ -381,4 +379,22 @@ public class Slide extends Sheet
}
}
/**
* Header / Footer settings for this slide.
*
* @return Header / Footer settings for this slide
*/
public HeadersFooters getHeadersFooters(){
HeadersFootersContainer hdd = null;
Record[] ch = getSheetContainer().getChildRecords();
for (int i = 0; i < ch.length; i++) {
if(ch[i] instanceof HeadersFootersContainer){
hdd = (HeadersFootersContainer)ch[i];
break;
}
}
boolean newRecord = false;
if(hdd == null) return getSlideShow().getSlideHeadersFooters();
else return new HeadersFooters(hdd, getSlideShow(), newRecord);
}
}

View File

@ -56,7 +56,7 @@ public class CString extends RecordAtom {
* Grabs the count, from the first two bytes of the header.
* The meaning of the count is specific to the type of the parent record
*/
public int getCount() {
public int getOptions() {
return (int)LittleEndian.getShort(_header);
}
@ -64,7 +64,7 @@ public class CString extends RecordAtom {
* Sets the count
* The meaning of the count is specific to the type of the parent record
*/
public void setCount(int count) {
public void setOptions(int count) {
LittleEndian.putShort(_header, (short)count);
}

View File

@ -140,9 +140,9 @@ public class Comment2000 extends RecordContainer {
CString csa = new CString();
CString csb = new CString();
CString csc = new CString();
csa.setCount(0x00);
csb.setCount(0x10);
csc.setCount(0x20);
csa.setOptions(0x00);
csb.setOptions(0x10);
csc.setOptions(0x20);
_children[0] = csa;
_children[1] = csb;
_children[2] = csc;

View File

@ -25,6 +25,7 @@ import org.apache.poi.poifs.filesystem.*;
import org.apache.poi.util.LittleEndian;
import org.apache.poi.util.StringUtil;
import org.apache.poi.hslf.exceptions.CorruptPowerPointFileException;
import org.apache.poi.hslf.exceptions.EncryptedPowerPointFileException;
/**
@ -39,14 +40,15 @@ public class CurrentUserAtom
{
/** Standard Atom header */
public static final byte[] atomHeader = new byte[] { 0, 0, -10, 15 };
/** The Powerpoint magic numer */
public static final byte[] magicNumber = new byte[] { 95, -64, -111, -29 };
/** The PowerPoint magic number for a non-encrypted file */
public static final byte[] headerToken = new byte[] { 95, -64, -111, -29 };
/** The PowerPoint magic number for an encrytpted file */
public static final byte[] encHeaderToken = new byte[] { -33, -60, -47, -13 };
/** The Powerpoint 97 version, major and minor numbers */
public static final byte[] ppt97FileVer = new byte[] { 8, 00, -13, 03, 03, 00 };
/** The version, major and minor numbers */
private int docFinalVersionA;
private int docFinalVersionB;
private int docFinalVersion;
private byte docMajorNo;
private byte docMinorNo;
@ -54,7 +56,7 @@ public class CurrentUserAtom
private long currentEditOffset;
/** The Username of the last person to edit the file */
private String lastEditUser;
/** The document release version */
/** The document release version. Almost always 8 */
private long releaseVersion;
/** Only correct after reading in or writing out */
@ -63,8 +65,7 @@ public class CurrentUserAtom
/* ********************* getter/setter follows *********************** */
public int getDocFinalVersionA() { return docFinalVersionA; }
public int getDocFinalVersionB() { return docFinalVersionB; }
public int getDocFinalVersion() { return docFinalVersion; }
public byte getDocMajorNo() { return docMajorNo; }
public byte getDocMinorNo() { return docMinorNo; }
@ -86,7 +87,14 @@ public class CurrentUserAtom
*/
public CurrentUserAtom() {
_contents = new byte[0];
throw new RuntimeException("Creation support for Current User Atom not complete");
// Initialise to empty
docFinalVersion = 0x03f4;
docMajorNo = 3;
docMinorNo = 0;
releaseVersion = 8;
currentEditOffset = 0;
lastEditUser = "Apache POI";
}
/**
@ -130,12 +138,20 @@ public class CurrentUserAtom
* Actually do the creation from a block of bytes
*/
private void init() {
// First up is the size, in 4 bytes, which is fixed
// Then is the header - check for encrypted
if(_contents[12] == encHeaderToken[0] &&
_contents[13] == encHeaderToken[1] &&
_contents[14] == encHeaderToken[2] &&
_contents[15] == encHeaderToken[3]) {
throw new EncryptedPowerPointFileException("The CurrentUserAtom specifies that the document is encrypted");
}
// Grab the edit offset
currentEditOffset = LittleEndian.getUInt(_contents,16);
// Grab the versions
docFinalVersionA = LittleEndian.getUShort(_contents,20);
docFinalVersionB = LittleEndian.getUShort(_contents,22);
docFinalVersion = LittleEndian.getUShort(_contents,22);
docMajorNo = _contents[24];
docMinorNo = _contents[25];
@ -194,15 +210,22 @@ public class CurrentUserAtom
// Now we have the size of the details, which is 20
LittleEndian.putInt(_contents,8,20);
// Now the ppt magic number (4 bytes)
System.arraycopy(magicNumber,0,_contents,12,4);
// Now the ppt un-encrypted header token (4 bytes)
System.arraycopy(headerToken,0,_contents,12,4);
// Now the current edit offset
LittleEndian.putInt(_contents,16,(int)currentEditOffset);
// Now the file versions, 2+2+1+1
LittleEndian.putShort(_contents,20,(short)docFinalVersionA);
LittleEndian.putShort(_contents,22,(short)docFinalVersionB);
// The username gets stored twice, once as US
// ascii, and again as unicode laster on
byte[] asciiUN = new byte[lastEditUser.length()];
StringUtil.putCompressedUnicode(lastEditUser,asciiUN,0);
// Now we're able to do the length of the last edited user
LittleEndian.putShort(_contents,20,(short)asciiUN.length);
// Now the file versions, 2+1+1
LittleEndian.putShort(_contents,22,(short)docFinalVersion);
_contents[24] = docMajorNo;
_contents[25] = docMinorNo;
@ -210,9 +233,7 @@ public class CurrentUserAtom
_contents[26] = 0;
_contents[27] = 0;
// username in bytes in us ascii
byte[] asciiUN = new byte[lastEditUser.length()];
StringUtil.putCompressedUnicode(lastEditUser,asciiUN,0);
// At this point we have the username as us ascii
System.arraycopy(asciiUN,0,_contents,28,asciiUN.length);
// 4 byte release version

View File

@ -74,8 +74,8 @@ public class ExEmbed extends RecordContainer {
CString cs1 = new CString();
CString cs2 = new CString();
CString cs3 = new CString();
// cs1.setCount(0x00);
// cs2.setCount(0x10);
// cs1.setOptions(0x00);
// cs2.setOptions(0x10);
_children[0] = new ExEmbedAtom();
_children[1] = new ExOleObjAtom();
_children[2] = cs1;

View File

@ -136,8 +136,8 @@ public class ExHyperlink extends RecordContainer {
// Setup our child records
CString csa = new CString();
CString csb = new CString();
csa.setCount(0x00);
csb.setCount(0x10);
csa.setOptions(0x00);
csb.setOptions(0x10);
_children[0] = new ExHyperlinkAtom();
_children[1] = csa;
_children[2] = csb;

View File

@ -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();
}
}

View File

@ -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;
}
}

View File

@ -111,8 +111,8 @@ public class RecordTypes {
public static final Type ExHyperlinkAtom = new Type(4051,ExHyperlinkAtom.class);
public static final Type ExHyperlink = new Type(4055,ExHyperlink.class);
public static final Type SlideNumberMCAtom = new Type(4056,null);
public static final Type HeadersFooters = new Type(4057,null);
public static final Type HeadersFootersAtom = new Type(4058,null);
public static final Type HeadersFooters = new Type(4057,HeadersFootersContainer.class);
public static final Type HeadersFootersAtom = new Type(4058,HeadersFootersAtom.class);
public static final Type TxInteractiveInfoAtom = new Type(4063,TxInteractiveInfoAtom.class);
public static final Type CharFormatAtom = new Type(4066,null);
public static final Type ParaFormatAtom = new Type(4067,null);

View File

@ -811,4 +811,50 @@ public class SlideShow
public int getNumberOfFonts() {
return getDocumentRecord().getEnvironment().getFontCollection().getNumberOfFonts();
}
/**
* Return Header / Footer settings for slides
*
* @return Header / Footer settings for slides
*/
public HeadersFooters getSlideHeadersFooters(){
HeadersFootersContainer hdd = null;
Record[] ch = _documentRecord.getChildRecords();
for (int i = 0; i < ch.length; i++) {
if(ch[i] instanceof HeadersFootersContainer &&
((HeadersFootersContainer)ch[i]).getOptions() == HeadersFootersContainer.SlideHeadersFootersContainer){
hdd = (HeadersFootersContainer)ch[i];
break;
}
}
boolean newRecord = false;
if(hdd == null) {
hdd = new HeadersFootersContainer(HeadersFootersContainer.SlideHeadersFootersContainer);
newRecord = true;
}
return new HeadersFooters(hdd, this, newRecord);
}
/**
* Return Header / Footer settings for notes
*
* @return Header / Footer settings for notes
*/
public HeadersFooters getNotesHeadersFooters(){
HeadersFootersContainer hdd = null;
Record[] ch = _documentRecord.getChildRecords();
for (int i = 0; i < ch.length; i++) {
if(ch[i] instanceof HeadersFootersContainer &&
((HeadersFootersContainer)ch[i]).getOptions() == HeadersFootersContainer.NotesHeadersFootersContainer){
hdd = (HeadersFootersContainer)ch[i];
break;
}
}
boolean newRecord = false;
if(hdd == null) {
hdd = new HeadersFootersContainer(HeadersFootersContainer.NotesHeadersFootersContainer);
newRecord = true;
}
return new HeadersFooters(hdd, this, newRecord);
}
}

View File

@ -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());
}
}

View File

@ -46,12 +46,12 @@ public class TestCString extends TestCase {
}
public void testCount() throws Exception {
CString ca = new CString(data_a, 0, data_a.length);
assertEquals(0, ca.getCount());
assertEquals(0, ca.getOptions());
CString cb = new CString(data_b, 0, data_a.length);
assertEquals(0x10, cb.getCount());
assertEquals(0x10, cb.getOptions());
ca.setCount(28);
assertEquals(28, ca.getCount());
ca.setOptions(28);
assertEquals(28, ca.getOptions());
}
public void testText() throws Exception {
@ -90,7 +90,7 @@ public class TestCString extends TestCase {
public void testChange() throws Exception {
CString ca = new CString(data_a, 0, data_a.length);
ca.setText("Comments");
ca.setCount(0x10);
ca.setOptions(0x10);
try {
for(int i=0; i<data_a.length; i++) {

View File

@ -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]);
}
}
}

View File

@ -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));
}
}

View File

@ -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));
}
}

Binary file not shown.

View File

@ -297,7 +297,8 @@ public final class TestSheet extends TestCase {
xfindex = sheet.getXFIndexForColAt((short) 1);
assertEquals(DEFAULT_IDX, xfindex);
ColumnInfoRecord nci = ( ColumnInfoRecord ) sheet.createColInfo();
// TODO change return type to ColumnInfoRecord
ColumnInfoRecord nci = (ColumnInfoRecord)ColumnInfoRecordsAggregate.createColInfo();
sheet.columns.insertColumn(nci);
// single column ColumnInfoRecord

View File

@ -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);

View File

@ -20,7 +20,7 @@ package org.apache.poi.hssf.record;
import junit.framework.AssertionFailedError;
import junit.framework.TestCase;
import org.apache.poi.hssf.record.cf.CellRange;
import org.apache.poi.ss.util.CellRangeAddress;
/**
* Tests the serialization and deserialization of the TestCFHeaderRecord
@ -34,18 +34,18 @@ public final class TestCFHeaderRecord extends TestCase
public void testCreateCFHeaderRecord ()
{
CFHeaderRecord record = new CFHeaderRecord();
CellRange[] ranges = {
new CellRange(0,0xFFFF,5,5),
new CellRange(0,0xFFFF,6,6),
new CellRange(0,1,0,1),
new CellRange(0,1,2,3),
new CellRange(2,3,0,1),
new CellRange(2,3,2,3),
CellRangeAddress[] ranges = {
new CellRangeAddress(0,0xFFFF,5,5),
new CellRangeAddress(0,0xFFFF,6,6),
new CellRangeAddress(0,1,0,1),
new CellRangeAddress(0,1,2,3),
new CellRangeAddress(2,3,0,1),
new CellRangeAddress(2,3,2,3),
};
record.setCellRanges(ranges);
ranges = record.getCellRanges();
assertEquals(6,ranges.length);
CellRange enclosingCellRange = record.getEnclosingCellRange();
CellRangeAddress enclosingCellRange = record.getEnclosingCellRange();
assertEquals(0, enclosingCellRange.getFirstRow());
assertEquals(65535, enclosingCellRange.getLastRow());
assertEquals(0, enclosingCellRange.getFirstColumn());
@ -95,7 +95,7 @@ public final class TestCFHeaderRecord extends TestCase
assertEquals("#CFRULES", 3, record.getNumberOfConditionalFormats());
assertTrue(record.getNeedRecalculation());
confirm(record.getEnclosingCellRange(), 0, 3, 0, 3);
CellRange[] ranges = record.getCellRanges();
CellRangeAddress[] ranges = record.getCellRanges();
assertEquals(4, ranges.length);
confirm(ranges[0], 0, 1, 0, 1);
confirm(ranges[1], 0, 1, 2, 3);
@ -154,7 +154,7 @@ public final class TestCFHeaderRecord extends TestCase
assertEquals("#CFRULES", 19, record.getNumberOfConditionalFormats());
assertFalse(record.getNeedRecalculation());
confirm(record.getEnclosingCellRange(), 0, 65535, 0, 255);
CellRange[] ranges = record.getCellRanges();
CellRangeAddress[] ranges = record.getCellRanges();
assertEquals(3, ranges.length);
confirm(ranges[0], 40000, 50000, 2, 2);
confirm(ranges[1], 0, 65535, 5, 5);
@ -168,18 +168,11 @@ public final class TestCFHeaderRecord extends TestCase
assertEquals("CFHeaderRecord doesn't match", recordData[i], output[i+4]);
}
}
private static void confirm(CellRange cr, int expFirstRow, int expLastRow, int expFirstCol, int expLastColumn) {
private static void confirm(CellRangeAddress cr, int expFirstRow, int expLastRow, int expFirstCol, int expLastColumn) {
assertEquals("first row", expFirstRow, cr.getFirstRow());
assertEquals("last row", expLastRow, cr.getLastRow());
assertEquals("first column", expFirstCol, cr.getFirstColumn());
assertEquals("last column", expLastColumn, cr.getLastColumn());
}
public static void main(String[] ignored_args)
{
System.out.println("Testing org.apache.poi.hssf.record.CFHeaderRecord");
junit.textui.TestRunner.run(TestCFHeaderRecord.class);
}
}

View File

@ -1,4 +1,3 @@
/* ====================================================================
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
@ -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));

View File

@ -28,8 +28,8 @@ import org.apache.poi.hssf.record.CFHeaderRecord;
import org.apache.poi.hssf.record.CFRuleRecord;
import org.apache.poi.hssf.record.RecordFactory;
import org.apache.poi.hssf.record.CFRuleRecord.ComparisonOperator;
import org.apache.poi.hssf.record.cf.CellRange;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.ss.util.CellRangeAddress;
/**
* Tests the serialization and deserialization of the CFRecordsAggregate
@ -49,9 +49,9 @@ public final class TestCFRecordsAggregate extends TestCase
CFRuleRecord rule2 = CFRuleRecord.create(workbook, ComparisonOperator.BETWEEN, "2", "5");
CFRuleRecord rule3 = CFRuleRecord.create(workbook, ComparisonOperator.GE, "100", null);
header.setNumberOfConditionalFormats(3);
CellRange[] cellRanges = {
new CellRange(0,1,0,0),
new CellRange(0,1,2,2),
CellRangeAddress[] cellRanges = {
new CellRangeAddress(0,1,0,0),
new CellRangeAddress(0,1,2,2),
};
header.setCellRanges(cellRanges);
recs.add(header);
@ -97,11 +97,4 @@ public final class TestCFRecordsAggregate extends TestCase
assertEquals(2, cellRanges.length);
assertEquals(3, header.getNumberOfConditionalFormats());
}
public static void main(String[] ignored_args)
{
System.out.println("Testing org.apache.poi.hssf.record.aggregates.CFRecordsAggregate");
junit.textui.TestRunner.run(TestCFRecordsAggregate.class);
}
}

View File

@ -17,6 +17,8 @@ limitations under the License.
package org.apache.poi.hssf.record.cf;
import org.apache.poi.ss.util.CellRangeAddress;
import junit.framework.AssertionFailedError;
import junit.framework.TestCase;
@ -25,15 +27,15 @@ import junit.framework.TestCase;
*/
public final class TestCellRange extends TestCase
{
private static final CellRange biggest = createCR( 0, -1, 0,-1);
private static final CellRange tenthColumn = createCR( 0, -1,10,10);
private static final CellRange tenthRow = createCR(10, 10, 0,-1);
private static final CellRange box10x10 = createCR( 0, 10, 0,10);
private static final CellRange box9x9 = createCR( 0, 9, 0, 9);
private static final CellRange box10to20c = createCR( 0, 10,10,20);
private static final CellRange oneCell = createCR(10, 10,10,10);
private static final CellRangeAddress biggest = createCR( 0, -1, 0,-1);
private static final CellRangeAddress tenthColumn = createCR( 0, -1,10,10);
private static final CellRangeAddress tenthRow = createCR(10, 10, 0,-1);
private static final CellRangeAddress box10x10 = createCR( 0, 10, 0,10);
private static final CellRangeAddress box9x9 = createCR( 0, 9, 0, 9);
private static final CellRangeAddress box10to20c = createCR( 0, 10,10,20);
private static final CellRangeAddress oneCell = createCR(10, 10,10,10);
private static final CellRange[] sampleRanges = {
private static final CellRangeAddress[] sampleRanges = {
biggest, tenthColumn, tenthRow, box10x10, box9x9, box10to20c, oneCell,
};
@ -54,9 +56,9 @@ public final class TestCellRange extends TestCase
* @param lastRow pass -1 for max row index
* @param lastCol pass -1 for max col index
*/
private static CellRange createCR(int firstRow, int lastRow, int firstCol, int lastCol) {
private static CellRangeAddress createCR(int firstRow, int lastRow, int firstCol, int lastCol) {
// max row & max col limit as per BIFF8
return new CellRange(
return new CellRangeAddress(
firstRow,
lastRow == -1 ? 0xFFFF : lastRow,
firstCol,
@ -65,89 +67,89 @@ public final class TestCellRange extends TestCase
public void testContainsMethod()
{
CellRange [] ranges = sampleRanges;
CellRangeAddress [] ranges = sampleRanges;
for(int i=0; i!=ranges.length;i++)
{
for(int j=0; j!=ranges.length;j++)
{
boolean expectedResult = containsExpectedResults[i][j];
assertEquals("("+i+","+j+"): ", expectedResult, ranges[i].contains(ranges[j]));
assertEquals("("+i+","+j+"): ", expectedResult, CellRangeUtil.contains(ranges[i], ranges[j]));
}
}
}
private static final CellRange col1 = createCR( 0, -1, 1,1);
private static final CellRange col2 = createCR( 0, -1, 2,2);
private static final CellRange row1 = createCR( 1, 1, 0,-1);
private static final CellRange row2 = createCR( 2, 2, 0,-1);
private static final CellRangeAddress col1 = createCR( 0, -1, 1,1);
private static final CellRangeAddress col2 = createCR( 0, -1, 2,2);
private static final CellRangeAddress row1 = createCR( 1, 1, 0,-1);
private static final CellRangeAddress row2 = createCR( 2, 2, 0,-1);
private static final CellRange box0 = createCR( 0, 2, 0,2);
private static final CellRange box1 = createCR( 0, 1, 0,1);
private static final CellRange box2 = createCR( 0, 1, 2,3);
private static final CellRange box3 = createCR( 2, 3, 0,1);
private static final CellRange box4 = createCR( 2, 3, 2,3);
private static final CellRange box5 = createCR( 1, 3, 1,3);
private static final CellRangeAddress box0 = createCR( 0, 2, 0,2);
private static final CellRangeAddress box1 = createCR( 0, 1, 0,1);
private static final CellRangeAddress box2 = createCR( 0, 1, 2,3);
private static final CellRangeAddress box3 = createCR( 2, 3, 0,1);
private static final CellRangeAddress box4 = createCR( 2, 3, 2,3);
private static final CellRangeAddress box5 = createCR( 1, 3, 1,3);
public void testHasSharedBorderMethod()
{
assertFalse(col1.hasExactSharedBorder(col1));
assertFalse(col2.hasExactSharedBorder(col2));
assertTrue(col1.hasExactSharedBorder(col2));
assertTrue(col2.hasExactSharedBorder(col1));
assertFalse(CellRangeUtil.hasExactSharedBorder(col1, col1));
assertFalse(CellRangeUtil.hasExactSharedBorder(col2, col2));
assertTrue(CellRangeUtil.hasExactSharedBorder(col1, col2));
assertTrue(CellRangeUtil.hasExactSharedBorder(col2, col1));
assertFalse(row1.hasExactSharedBorder(row1));
assertFalse(row2.hasExactSharedBorder(row2));
assertTrue(row1.hasExactSharedBorder(row2));
assertTrue(row2.hasExactSharedBorder(row1));
assertFalse(CellRangeUtil.hasExactSharedBorder(row1, row1));
assertFalse(CellRangeUtil.hasExactSharedBorder(row2, row2));
assertTrue(CellRangeUtil.hasExactSharedBorder(row1, row2));
assertTrue(CellRangeUtil.hasExactSharedBorder(row2, row1));
assertFalse(row1.hasExactSharedBorder(col1));
assertFalse(row1.hasExactSharedBorder(col2));
assertFalse(col1.hasExactSharedBorder(row1));
assertFalse(col2.hasExactSharedBorder(row1));
assertFalse(row2.hasExactSharedBorder(col1));
assertFalse(row2.hasExactSharedBorder(col2));
assertFalse(col1.hasExactSharedBorder(row2));
assertFalse(col2.hasExactSharedBorder(row2));
assertTrue(col2.hasExactSharedBorder(col1));
assertFalse(CellRangeUtil.hasExactSharedBorder(row1, col1));
assertFalse(CellRangeUtil.hasExactSharedBorder(row1, col2));
assertFalse(CellRangeUtil.hasExactSharedBorder(col1, row1));
assertFalse(CellRangeUtil.hasExactSharedBorder(col2, row1));
assertFalse(CellRangeUtil.hasExactSharedBorder(row2, col1));
assertFalse(CellRangeUtil.hasExactSharedBorder(row2, col2));
assertFalse(CellRangeUtil.hasExactSharedBorder(col1, row2));
assertFalse(CellRangeUtil.hasExactSharedBorder(col2, row2));
assertTrue(CellRangeUtil.hasExactSharedBorder(col2, col1));
assertFalse(box1.hasExactSharedBorder(box1));
assertTrue(box1.hasExactSharedBorder(box2));
assertTrue(box1.hasExactSharedBorder(box3));
assertFalse(box1.hasExactSharedBorder(box4));
assertFalse(CellRangeUtil.hasExactSharedBorder(box1, box1));
assertTrue(CellRangeUtil.hasExactSharedBorder(box1, box2));
assertTrue(CellRangeUtil.hasExactSharedBorder(box1, box3));
assertFalse(CellRangeUtil.hasExactSharedBorder(box1, box4));
assertTrue(box2.hasExactSharedBorder(box1));
assertFalse(box2.hasExactSharedBorder(box2));
assertFalse(box2.hasExactSharedBorder(box3));
assertTrue(box2.hasExactSharedBorder(box4));
assertTrue(CellRangeUtil.hasExactSharedBorder(box2, box1));
assertFalse(CellRangeUtil.hasExactSharedBorder(box2, box2));
assertFalse(CellRangeUtil.hasExactSharedBorder(box2, box3));
assertTrue(CellRangeUtil.hasExactSharedBorder(box2, box4));
assertTrue(box3.hasExactSharedBorder(box1));
assertFalse(box3.hasExactSharedBorder(box2));
assertFalse(box3.hasExactSharedBorder(box3));
assertTrue(box3.hasExactSharedBorder(box4));
assertTrue(CellRangeUtil.hasExactSharedBorder(box3, box1));
assertFalse(CellRangeUtil.hasExactSharedBorder(box3, box2));
assertFalse(CellRangeUtil.hasExactSharedBorder(box3, box3));
assertTrue(CellRangeUtil.hasExactSharedBorder(box3, box4));
assertFalse(box4.hasExactSharedBorder(box1));
assertTrue(box4.hasExactSharedBorder(box2));
assertTrue(box4.hasExactSharedBorder(box3));
assertFalse(box4.hasExactSharedBorder(box4));
assertFalse(CellRangeUtil.hasExactSharedBorder(box4, box1));
assertTrue(CellRangeUtil.hasExactSharedBorder(box4, box2));
assertTrue(CellRangeUtil.hasExactSharedBorder(box4, box3));
assertFalse(CellRangeUtil.hasExactSharedBorder(box4, box4));
}
public void testIntersectMethod()
{
assertEquals(CellRange.OVERLAP,box0.intersect(box5));
assertEquals(CellRange.OVERLAP,box5.intersect(box0));
assertEquals(CellRange.NO_INTERSECTION,box1.intersect(box4));
assertEquals(CellRange.NO_INTERSECTION,box4.intersect(box1));
assertEquals(CellRange.NO_INTERSECTION,box2.intersect(box3));
assertEquals(CellRange.NO_INTERSECTION,box3.intersect(box2));
assertEquals(CellRange.INSIDE,box0.intersect(box1));
assertEquals(CellRange.INSIDE,box0.intersect(box0));
assertEquals(CellRange.ENCLOSES,box1.intersect(box0));
assertEquals(CellRange.INSIDE,tenthColumn.intersect(oneCell));
assertEquals(CellRange.ENCLOSES,oneCell.intersect(tenthColumn));
assertEquals(CellRange.OVERLAP,tenthColumn.intersect(tenthRow));
assertEquals(CellRange.OVERLAP,tenthRow.intersect(tenthColumn));
assertEquals(CellRange.INSIDE,tenthColumn.intersect(tenthColumn));
assertEquals(CellRange.INSIDE,tenthRow.intersect(tenthRow));
assertEquals(CellRangeUtil.OVERLAP, CellRangeUtil.intersect(box0, box5));
assertEquals(CellRangeUtil.OVERLAP, CellRangeUtil.intersect(box5, box0));
assertEquals(CellRangeUtil.NO_INTERSECTION, CellRangeUtil.intersect(box1, box4));
assertEquals(CellRangeUtil.NO_INTERSECTION, CellRangeUtil.intersect(box4, box1));
assertEquals(CellRangeUtil.NO_INTERSECTION, CellRangeUtil.intersect(box2, box3));
assertEquals(CellRangeUtil.NO_INTERSECTION, CellRangeUtil.intersect(box3, box2));
assertEquals(CellRangeUtil.INSIDE, CellRangeUtil.intersect(box0, box1));
assertEquals(CellRangeUtil.INSIDE, CellRangeUtil.intersect(box0, box0));
assertEquals(CellRangeUtil.ENCLOSES, CellRangeUtil.intersect(box1, box0));
assertEquals(CellRangeUtil.INSIDE, CellRangeUtil.intersect(tenthColumn, oneCell));
assertEquals(CellRangeUtil.ENCLOSES, CellRangeUtil.intersect(oneCell, tenthColumn));
assertEquals(CellRangeUtil.OVERLAP, CellRangeUtil.intersect(tenthColumn, tenthRow));
assertEquals(CellRangeUtil.OVERLAP, CellRangeUtil.intersect(tenthRow, tenthColumn));
assertEquals(CellRangeUtil.INSIDE, CellRangeUtil.intersect(tenthColumn, tenthColumn));
assertEquals(CellRangeUtil.INSIDE, CellRangeUtil.intersect(tenthRow, tenthRow));
}
/**
@ -155,7 +157,7 @@ public final class TestCellRange extends TestCase
* =$C:$IV,$B$1:$B$8,$B$10:$B$65536,$A:$A
*/
public void testCreate() {
CellRange cr;
CellRangeAddress cr;
cr = createCR(0, -1, 2, 255); // $C:$IV
confirmRange(cr, false, true);
@ -172,7 +174,7 @@ public final class TestCellRange extends TestCase
cr = createCR(0, -1, 0, 0); // $A:$A
}
private static void confirmRange(CellRange cr, boolean isFullRow, boolean isFullColumn) {
private static void confirmRange(CellRangeAddress cr, boolean isFullRow, boolean isFullColumn) {
assertEquals("isFullRowRange", isFullRow, cr.isFullRowRange());
assertEquals("isFullColumnRange", isFullColumn, cr.isFullColumnRange());
}

View File

@ -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;
}
}

View File

@ -28,6 +28,7 @@ import junit.framework.AssertionFailedError;
import junit.framework.TestCase;
import org.apache.poi.ss.util.Region;
import org.apache.poi.ss.util.CellRangeAddress;
import org.apache.poi.hssf.HSSFTestDataSamples;
import org.apache.poi.hssf.model.Workbook;
@ -302,15 +303,14 @@ public final class TestBugs extends TestCase {
/**
* Merged regions were being removed from the parent in cloned sheets
* @throws Exception
*/
public void test22720() {
HSSFWorkbook workBook = new HSSFWorkbook();
workBook.createSheet("TEST");
HSSFSheet template = workBook.getSheetAt(0);
template.addMergedRegion(new Region(0, (short)0, 1, (short)2));
template.addMergedRegion(new Region(1, (short)0, 2, (short)2));
template.addMergedRegion(new CellRangeAddress(0, 1, 0, 2));
template.addMergedRegion(new CellRangeAddress(1, 2, 0, 2));
HSSFSheet clone = workBook.cloneSheet(0);
int originalMerged = template.getNumMergedRegions();
@ -318,20 +318,20 @@ public final class TestBugs extends TestCase {
// remove merged regions from clone
for (int i=template.getNumMergedRegions()-1; i>=0; i--) {
clone.removeMergedRegion(i);
clone.removeMergedRegion(i);
}
assertEquals("Original Sheet's Merged Regions were removed", originalMerged, template.getNumMergedRegions());
// check if template's merged regions are OK
if (template.getNumMergedRegions()>0) {
// fetch the first merged region...EXCEPTION OCCURS HERE
template.getMergedRegionAt(0);
// fetch the first merged region...EXCEPTION OCCURS HERE
template.getMergedRegion(0);
}
//make sure we dont exception
}
/*Tests read and write of Unicode strings in formula results
/**Tests read and write of Unicode strings in formula results
* bug and testcase submitted by Sompop Kumnoonsate
* The file contains THAI unicode characters.
*/

View File

@ -21,6 +21,7 @@ package org.apache.poi.hssf.usermodel;
import junit.framework.TestCase;
import org.apache.poi.ss.util.CellRangeAddress;
import org.apache.poi.ss.util.Region;
/**
@ -29,23 +30,15 @@ import org.apache.poi.ss.util.Region;
* add that record to the sheet in the testCloneSheetBasic method.
* @author avik
*/
public class TestCloneSheet extends TestCase {
public final class TestCloneSheet extends TestCase {
public TestCloneSheet(String arg0) {
super(arg0);
}
public void testCloneSheetBasic(){
try{
HSSFWorkbook b = new HSSFWorkbook();
HSSFSheet s = b.createSheet("Test");
s.addMergedRegion(new Region((short)0,(short)0,(short)1,(short)1));
HSSFSheet clonedSheet = b.cloneSheet(0);
assertEquals("One merged area", 1, clonedSheet.getNumMergedRegions());
}
catch(Exception e){e.printStackTrace();fail(e.getMessage());}
HSSFWorkbook b = new HSSFWorkbook();
HSSFSheet s = b.createSheet("Test");
s.addMergedRegion(new CellRangeAddress(0, 1, 0, 1));
HSSFSheet clonedSheet = b.cloneSheet(0);
assertEquals("One merged area", 1, clonedSheet.getNumMergedRegions());
}
/**
@ -66,5 +59,4 @@ public class TestCloneSheet extends TestCase {
assertTrue("Row 3 still should be broken", clone.isRowBroken(3));
}
}

View File

@ -176,4 +176,37 @@ public final class TestHSSFComment extends TestCase {
}
}
public void testDeleteComments() throws Exception {
HSSFWorkbook wb = HSSFTestDataSamples.openSampleWorkbook("SimpleWithComments.xls");
HSSFSheet sheet = wb.getSheetAt(0);
// Zap from rows 1 and 3
assertNotNull(sheet.getRow(0).getCell(1).getCellComment());
assertNotNull(sheet.getRow(1).getCell(1).getCellComment());
assertNotNull(sheet.getRow(2).getCell(1).getCellComment());
sheet.getRow(0).getCell(1).removeCellComment();
sheet.getRow(2).getCell(1).setCellComment(null);
// Check gone so far
assertNull(sheet.getRow(0).getCell(1).getCellComment());
assertNotNull(sheet.getRow(1).getCell(1).getCellComment());
assertNull(sheet.getRow(2).getCell(1).getCellComment());
// Save and re-load
ByteArrayOutputStream out = new ByteArrayOutputStream();
wb.write(out);
out.close();
wb = new HSSFWorkbook(new ByteArrayInputStream(out.toByteArray()));
// Check
assertNull(sheet.getRow(0).getCell(1).getCellComment());
assertNotNull(sheet.getRow(1).getCell(1).getCellComment());
assertNull(sheet.getRow(2).getCell(1).getCellComment());
// FileOutputStream fout = new FileOutputStream("/tmp/c.xls");
// wb.write(fout);
// fout.close();
}
}

View File

@ -21,7 +21,9 @@ import junit.framework.TestCase;
import org.apache.poi.hssf.record.CFRuleRecord.ComparisonOperator;
import org.apache.poi.hssf.util.HSSFColor;
import org.apache.poi.ss.util.CellRangeAddress;
import org.apache.poi.ss.util.Region;
/**
*
* @author Dmitriy Kumshayev
@ -57,9 +59,8 @@ public final class TestHSSFConditionalFormatting extends TestCase
};
short col = 1;
Region [] regions =
{
new Region(0,col,65535,col)
CellRangeAddress [] regions = {
new CellRangeAddress(0, 65535, col, col)
};
sheetCF.addConditionalFormatting(regions, cfRules);
@ -72,14 +73,14 @@ public final class TestHSSFConditionalFormatting extends TestCase
HSSFConditionalFormatting cf = sheetCF.getConditionalFormattingAt(0);
assertNotNull(cf);
regions = cf.getFormattingRegions();
regions = cf.getFormattingRanges();
assertNotNull(regions);
assertEquals(1, regions.length);
Region r = regions[0];
assertEquals(1, r.getColumnFrom());
assertEquals(1, r.getColumnTo());
assertEquals(0, r.getRowFrom());
assertEquals(65535, r.getRowTo());
CellRangeAddress r = regions[0];
assertEquals(1, r.getFirstColumn());
assertEquals(1, r.getLastColumn());
assertEquals(0, r.getFirstRow());
assertEquals(65535, r.getLastRow());
assertEquals(2, cf.getNumberOfRules());

View File

@ -19,6 +19,7 @@ package org.apache.poi.hssf.usermodel;
import java.text.DecimalFormat;
import java.text.Format;
import java.util.Date;
import java.util.Iterator;
import junit.framework.TestCase;

View File

@ -39,7 +39,7 @@ import org.apache.poi.hssf.model.Workbook;
* @author Alex Jacoby (ajacoby at gmail.com)
* @version %I%, %G%
*/
public class TestHSSFDateUtil extends TestCase {
public final class TestHSSFDateUtil extends TestCase {
public static final int CALENDAR_JANUARY = 0;
public static final int CALENDAR_FEBRUARY = 1;
@ -47,11 +47,6 @@ public class TestHSSFDateUtil extends TestCase {
public static final int CALENDAR_APRIL = 3;
public static final int CALENDAR_JULY = 6;
public static final int CALENDAR_OCTOBER = 9;
public TestHSSFDateUtil(String s)
{
super(s);
}
/**
* Checks the date conversion functions in the HSSFDateUtil class.
@ -193,14 +188,13 @@ public class TestHSSFDateUtil extends TestCase {
}
/**
* Tests that we deal with timezones properly
* Tests that we deal with time-zones properly
*/
public void testCalendarConversion() {
GregorianCalendar date = new GregorianCalendar(2002, 0, 1, 12, 1, 1);
Date expected = date.getTime();
double expectedExcel = HSSFDateUtil.getExcelDate(expected);
// Iteratating over the hours exposes any rounding issues.
// Iterating over the hours exposes any rounding issues.
for (int hour = -12; hour <= 12; hour++)
{
String id = "GMT" + (hour < 0 ? "" : "+") + hour + ":00";
@ -209,7 +203,7 @@ public class TestHSSFDateUtil extends TestCase {
double excelDate = HSSFDateUtil.getExcelDate(date, false);
Date javaDate = HSSFDateUtil.getJavaDate(excelDate);
// Should match despite timezone
// Should match despite time-zone
assertEquals("Checking timezone " + id, expected.getTime(), javaDate.getTime());
}
}
@ -402,7 +396,11 @@ public class TestHSSFDateUtil extends TestCase {
assertEquals(34519.0, HSSFDateUtil.getExcelDate(createDate(1998, CALENDAR_JULY, 5), true), 0.00001);
}
private Date createDate(int year, int month, int day) {
/**
* @param month zero based
* @param day one based
*/
private static Date createDate(int year, int month, int day) {
Calendar c = new GregorianCalendar();
c.set(year, month, day, 0, 0, 0);
c.set(Calendar.MILLISECOND, 0);
@ -420,10 +418,18 @@ public class TestHSSFDateUtil extends TestCase {
calendar = new GregorianCalendar(1901, 0, 1);
assertEquals("Checking absolute day (1 Jan 1901)", 366, HSSFDateUtil.absoluteDay(calendar, false));
}
public void testConvertTime() {
final double delta = 1E-7; // a couple of digits more accuracy than strictly required
assertEquals(0.5, HSSFDateUtil.convertTime("12:00"), delta);
assertEquals(2.0/3, HSSFDateUtil.convertTime("16:00"), delta);
assertEquals(0.0000116, HSSFDateUtil.convertTime("0:00:01"), delta);
assertEquals(0.7330440, HSSFDateUtil.convertTime("17:35:35"), delta);
}
public static void main(String [] args) {
System.out
.println("Testing org.apache.poi.hssf.usermodel.TestHSSFDateUtil");
junit.textui.TestRunner.run(TestHSSFDateUtil.class);
public void testParseDate() {
assertEquals(createDate(2008, Calendar.AUGUST, 3), HSSFDateUtil.parseYYYYMMDDDate("2008/08/03"));
assertEquals(createDate(1994, Calendar.MAY, 1), HSSFDateUtil.parseYYYYMMDDDate("1994/05/01"));
}
}

View File

@ -36,6 +36,7 @@ import org.apache.poi.hssf.record.VCenterRecord;
import org.apache.poi.hssf.record.WSBoolRecord;
import org.apache.poi.hssf.record.WindowTwoRecord;
import org.apache.poi.ss.util.Region;
import org.apache.poi.ss.util.CellRangeAddress;
/**
* Tests HSSFSheet. This test case is very incomplete at the moment.
@ -476,15 +477,15 @@ public final class TestHSSFSheet extends TestCase {
public void testRemoveMerged() {
HSSFWorkbook wb = new HSSFWorkbook();
HSSFSheet sheet = wb.createSheet();
Region region = new Region(0, (short)0, 1, (short)1);
CellRangeAddress region = new CellRangeAddress(0, 1, 0, 1);
sheet.addMergedRegion(region);
region = new Region(1, (short)0, 2, (short)1);
region = new CellRangeAddress(1, 2, 0, 1);
sheet.addMergedRegion(region);
sheet.removeMergedRegion(0);
region = sheet.getMergedRegionAt(0);
assertEquals("Left over region should be starting at row 1", 1, region.getRowFrom());
region = sheet.getMergedRegion(0);
assertEquals("Left over region should be starting at row 1", 1, region.getFirstRow());
sheet.removeMergedRegion(0);
@ -496,15 +497,15 @@ public final class TestHSSFSheet extends TestCase {
sheet.removeMergedRegion(0);
assertEquals("there should now be zero merged regions!", 0, sheet.getNumMergedRegions());
//add it again!
region.setRowTo(4);
region.setLastRow(4);
sheet.addMergedRegion(region);
assertEquals("there should now be one merged region!", 1, sheet.getNumMergedRegions());
//should exist now!
assertTrue("there isn't more than one merged region in there", 1 <= sheet.getNumMergedRegions());
region = sheet.getMergedRegionAt(0);
assertEquals("the merged row to doesnt match the one we put in ", 4, region.getRowTo());
region = sheet.getMergedRegion(0);
assertEquals("the merged row to doesnt match the one we put in ", 4, region.getLastRow());
}
public void testShiftMerged() {
@ -518,13 +519,13 @@ public final class TestHSSFSheet extends TestCase {
cell = row.createCell((short)1);
cell.setCellValue(new HSSFRichTextString("second row, second cell"));
Region region = new Region(1, (short)0, 1, (short)1);
CellRangeAddress region = new CellRangeAddress(1, 1, 0, 1);
sheet.addMergedRegion(region);
sheet.shiftRows(1, 1, 1);
region = sheet.getMergedRegionAt(0);
assertEquals("Merged region not moved over to row 2", 2, region.getRowFrom());
region = sheet.getMergedRegion(0);
assertEquals("Merged region not moved over to row 2", 2, region.getFirstRow());
}
/**
@ -683,7 +684,7 @@ public final class TestHSSFSheet extends TestCase {
assertTrue("Column autosized with only one row: wrong width", sheet.getColumnWidth((short)0) <= maxWithRow1And2);
//create a region over the 2nd row and auto size the first column
sheet.addMergedRegion(new Region(1,(short)0,1,(short)1));
sheet.addMergedRegion(new CellRangeAddress(1,1,0,1));
sheet.autoSizeColumn((short)0);
HSSFWorkbook wb2 = HSSFTestDataSamples.writeOutAndReadBack(wb);

View File

@ -32,6 +32,7 @@ import org.apache.poi.hssf.record.LabelSSTRecord;
import org.apache.poi.hssf.record.Record;
import org.apache.poi.hssf.record.aggregates.ValueRecordsAggregate;
import org.apache.poi.poifs.filesystem.POIFSFileSystem;
import org.apache.poi.ss.util.CellRangeAddress;
import org.apache.poi.ss.util.Region;
import org.apache.poi.util.TempFile;
@ -42,7 +43,7 @@ import org.apache.poi.util.TempFile;
* @author Greg Merrill
* @author Siggi Cherem
*/
public class TestWorkbook extends TestCase {
public final class TestWorkbook extends TestCase {
private static final String LAST_NAME_KEY = "lastName";
private static final String FIRST_NAME_KEY = "firstName";
private static final String SSN_KEY = "ssn";
@ -260,10 +261,10 @@ public class TestWorkbook extends TestCase {
HSSFWorkbook workbook = openSample("Employee.xls");
HSSFSheet sheet = workbook.getSheetAt(0);
assertEquals(EMPLOYEE_INFORMATION, sheet.getRow(1).getCell(1).getStringCellValue());
assertEquals(LAST_NAME_KEY, sheet.getRow(3).getCell(2).getStringCellValue());
assertEquals(FIRST_NAME_KEY, sheet.getRow(4).getCell(2).getStringCellValue());
assertEquals(SSN_KEY, sheet.getRow(5).getCell(2).getStringCellValue());
assertEquals(EMPLOYEE_INFORMATION, sheet.getRow(1).getCell(1).getRichStringCellValue().getString());
assertEquals(LAST_NAME_KEY, sheet.getRow(3).getCell(2).getRichStringCellValue().getString());
assertEquals(FIRST_NAME_KEY, sheet.getRow(4).getCell(2).getRichStringCellValue().getString());
assertEquals(SSN_KEY, sheet.getRow(5).getCell(2).getRichStringCellValue().getString());
}
/**
@ -318,13 +319,13 @@ public class TestWorkbook extends TestCase {
sheet = workbook.getSheetAt(0);
cell = sheet.getRow(0).getCell(1);
assertEquals(REPLACED, cell.getStringCellValue());
assertEquals(REPLACED, cell.getRichStringCellValue().getString());
cell = sheet.getRow(0).getCell(0);
assertEquals(DO_NOT_REPLACE, cell.getStringCellValue());
assertEquals(DO_NOT_REPLACE, cell.getRichStringCellValue().getString());
cell = sheet.getRow(1).getCell(0);
assertEquals(REPLACED, cell.getStringCellValue());
assertEquals(REPLACED, cell.getRichStringCellValue().getString());
cell = sheet.getRow(1).getCell(1);
assertEquals(DO_NOT_REPLACE, cell.getStringCellValue());
assertEquals(DO_NOT_REPLACE, cell.getRichStringCellValue().getString());
}
/**
@ -388,10 +389,10 @@ public class TestWorkbook extends TestCase {
workbook = HSSFTestDataSamples.writeOutAndReadBack(workbook);
sheet = workbook.getSheetAt(0);
assertEquals(EMPLOYEE_INFORMATION, sheet.getRow(1).getCell(1).getStringCellValue());
assertEquals(LAST_NAME_VALUE, sheet.getRow(3).getCell(2).getStringCellValue());
assertEquals(FIRST_NAME_VALUE, sheet.getRow(4).getCell(2).getStringCellValue());
assertEquals(SSN_VALUE, sheet.getRow(5).getCell(2).getStringCellValue());
assertEquals(EMPLOYEE_INFORMATION, sheet.getRow(1).getCell(1).getRichStringCellValue().getString());
assertEquals(LAST_NAME_VALUE, sheet.getRow(3).getCell(2).getRichStringCellValue().getString());
assertEquals(FIRST_NAME_VALUE, sheet.getRow(4).getCell(2).getRichStringCellValue().getString());
assertEquals(SSN_VALUE, sheet.getRow(5).getCell(2).getRichStringCellValue().getString());
}
/**
@ -421,26 +422,17 @@ public class TestWorkbook extends TestCase {
* HSSFSheet last row or first row is incorrect. <P>
*
*/
public void testWriteModifySheetMerged()
throws IOException
{
File file = TempFile.createTempFile("testWriteSheetMerged",
".xls");
FileOutputStream out = new FileOutputStream(file);
FileInputStream in = null;
public void testWriteModifySheetMerged() {
HSSFWorkbook wb = new HSSFWorkbook();
HSSFSheet s = wb.createSheet();
HSSFRow r = null;
HSSFCell c = null;
for (short rownum = ( short ) 0; rownum < 100; rownum++)
{
r = s.createRow(rownum);
HSSFRow r = s.createRow(rownum);
for (short cellnum = ( short ) 0; cellnum < 50; cellnum += 2)
{
c = r.createCell(cellnum);
HSSFCell c = r.createCell(cellnum);
c.setCellValue(rownum * 10000 + cellnum
+ ((( double ) rownum / 1000)
+ (( double ) cellnum / 10000)));
@ -448,33 +440,27 @@ public class TestWorkbook extends TestCase {
c.setCellValue(new HSSFRichTextString("TEST"));
}
}
s.addMergedRegion(new Region(( short ) 0, ( short ) 0, ( short ) 10,
( short ) 10));
s.addMergedRegion(new Region(( short ) 30, ( short ) 5, ( short ) 40,
( short ) 15));
wb.write(out);
out.close();
s.addMergedRegion(new CellRangeAddress(0, 10, 0, 10));
s.addMergedRegion(new CellRangeAddress(30, 40, 5, 15));
sanityChecker.checkHSSFWorkbook(wb);
in = new FileInputStream(file);
wb = new HSSFWorkbook(new POIFSFileSystem(in));
wb = HSSFTestDataSamples.writeOutAndReadBack(wb);
s = wb.getSheetAt(0);
Region r1 = s.getMergedRegionAt(0);
Region r2 = s.getMergedRegionAt(1);
CellRangeAddress r1 = s.getMergedRegion(0);
CellRangeAddress r2 = s.getMergedRegion(1);
in.close();
// System.out.println(file.length());
// assertEquals("FILE LENGTH == 87552",file.length(), 87552);
// System.out.println(s.getLastRowNum());
assertEquals("REGION1 = 0,0,10,10", 0,
new Region(( short ) 0, ( short ) 0, ( short ) 10,
( short ) 10).compareTo(r1));
assertEquals("REGION2 == 30,5,40,15", 0,
new Region(( short ) 30, ( short ) 5, ( short ) 40,
( short ) 15).compareTo(r2));
confirmRegion(new CellRangeAddress(0, 10, 0, 10), r1);
confirmRegion(new CellRangeAddress(30, 40,5, 15), r2);
}
/**
private static void confirmRegion(CellRangeAddress ra, CellRangeAddress rb) {
assertEquals(ra.getFirstRow(), rb.getFirstRow());
assertEquals(ra.getLastRow(), rb.getLastRow());
assertEquals(ra.getFirstColumn(), rb.getFirstColumn());
assertEquals(ra.getLastColumn(), rb.getLastColumn());
}
/**
* Test the backup field gets set as expected.
*/