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.HSSFRow;
import org.apache.poi.hssf.usermodel.HSSFSheet; import org.apache.poi.hssf.usermodel.HSSFSheet;
import org.apache.poi.hssf.usermodel.HSSFWorkbook; import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.ss.util.CellRangeAddress;
import org.apache.poi.ss.util.Region; import org.apache.poi.ss.util.Region;
/** /**
* Various utility functions that make working with a region of cells easier. * Various utility functions that make working with a region of cells easier.
* *
*@author Eric Pugh epugh@upstate.com * @author Eric Pugh epugh@upstate.com
*@since July 29, 2002
*/ */
public final class HSSFRegionUtil public final class HSSFRegionUtil {
{
/** Constructor for the HSSFRegionUtil object */ private HSSFRegionUtil() {
private HSSFRegionUtil() { // no instances of this class
// no instances of this class }
} /**
* For setting the same property on many cells to the same value
*/
private static final class CellPropertySetter {
/** private final HSSFWorkbook _workbook;
* Sets the left border for a region of cells by manipulating the cell style private final String _propertyName;
* of the individual cells on the left private final Short _propertyValue;
*
*@param border The new border
*@param region The region that should have the border
*@param workbook The workbook that the region is on.
*@param sheet The sheet that the region is on.
*/
public static void setBorderLeft( short border, Region region, HSSFSheet sheet, HSSFWorkbook workbook )
{
int rowStart = region.getRowFrom();
int rowEnd = region.getRowTo();
int column = region.getColumnFrom();
for ( int i = rowStart; i <= rowEnd; i++ ) { public CellPropertySetter(HSSFWorkbook workbook, String propertyName, int value) {
HSSFRow row = HSSFCellUtil.getRow( i, sheet ); _workbook = workbook;
HSSFCell cell = HSSFCellUtil.getCell( row, column ); _propertyName = propertyName;
HSSFCellUtil.setCellStyleProperty( _propertyValue = new Short((short)value);
cell, workbook, HSSFCellUtil.BORDER_LEFT, new Short( border ) ); }
} public void setProperty(HSSFRow row, int column) {
} HSSFCell cell = HSSFCellUtil.getCell(row, column);
HSSFCellUtil.setCellStyleProperty(cell, _workbook, _propertyName, _propertyValue);
}
}
/** private static CellRangeAddress toCRA(Region region) {
* Sets the leftBorderColor attribute of the HSSFRegionUtil object return Region.convertToCellRangeAddress(region);
* }
*@param color The color of the border
*@param region The region that should have the border
*@param workbook The workbook that the region is on.
*@param sheet The sheet that the region is on.
*/
public static void setLeftBorderColor( short color, Region region, HSSFSheet sheet, HSSFWorkbook workbook )
{
int rowStart = region.getRowFrom();
int rowEnd = region.getRowTo();
int column = region.getColumnFrom();
for ( int i = rowStart; i <= rowEnd; i++ ) { /**
HSSFRow row = HSSFCellUtil.getRow( i, sheet ); * @deprecated (Aug 2008) use {@link CellRangeAddress} instead of {@link Region}
HSSFCell cell = HSSFCellUtil.getCell( row, column ); */
HSSFCellUtil.setCellStyleProperty( public static void setBorderLeft(short border, Region region, HSSFSheet sheet,
cell, workbook, HSSFCellUtil.LEFT_BORDER_COLOR, new Short( color ) ); HSSFWorkbook workbook) {
} setBorderLeft(border, toCRA(region), sheet, workbook);
} }
/**
* Sets the left border for a region of cells by manipulating the cell style
* of the individual cells on the left
*
* @param border The new border
* @param region The region that should have the border
* @param workbook The workbook that the region is on.
* @param sheet The sheet that the region is on.
*/
public static void setBorderLeft(int border, CellRangeAddress region, HSSFSheet sheet,
HSSFWorkbook workbook) {
int rowStart = region.getFirstRow();
int rowEnd = region.getLastRow();
int column = region.getFirstColumn();
/** CellPropertySetter cps = new CellPropertySetter(workbook, HSSFCellUtil.BORDER_LEFT, border);
* Sets the borderRight attribute of the HSSFRegionUtil object for (int i = rowStart; i <= rowEnd; i++) {
* cps.setProperty(HSSFCellUtil.getRow(i, sheet), column);
*@param border The new border }
*@param region The region that should have the border }
*@param workbook The workbook that the region is on.
*@param sheet The sheet that the region is on.
*/
public static void setBorderRight( short border, Region region, HSSFSheet sheet, HSSFWorkbook workbook )
{
int rowStart = region.getRowFrom();
int rowEnd = region.getRowTo();
int column = region.getColumnTo();
for ( int i = rowStart; i <= rowEnd; i++ ) { /**
HSSFRow row = HSSFCellUtil.getRow( i, sheet ); * @deprecated (Aug 2008) use {@link CellRangeAddress} instead of {@link Region}
HSSFCell cell = HSSFCellUtil.getCell( row, column ); */
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( CellPropertySetter cps = new CellPropertySetter(workbook, HSSFCellUtil.LEFT_BORDER_COLOR, color);
cell, workbook, HSSFCellUtil.BORDER_RIGHT, new Short( border ) ); for (int i = rowStart; i <= rowEnd; i++) {
} cps.setProperty(HSSFCellUtil.getRow(i, sheet), column);
} }
}
/** /**
* Sets the rightBorderColor attribute of the HSSFRegionUtil object * @deprecated (Aug 2008) use {@link CellRangeAddress} instead of {@link Region}
* */
*@param color The color of the border public static void setBorderRight(short border, Region region, HSSFSheet sheet,
*@param region The region that should have the border HSSFWorkbook workbook) {
*@param workbook The workbook that the region is on. setBorderRight(border, toCRA(region), sheet, workbook);
*@param sheet The sheet that the region is on. }
*/ /**
public static void setRightBorderColor( short color, Region region, HSSFSheet sheet, HSSFWorkbook workbook ) * Sets the borderRight attribute of the HSSFRegionUtil object
{ *
int rowStart = region.getRowFrom(); * @param border The new border
int rowEnd = region.getRowTo(); * @param region The region that should have the border
int column = region.getColumnTo(); * @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++ ) { CellPropertySetter cps = new CellPropertySetter(workbook, HSSFCellUtil.BORDER_RIGHT, border);
HSSFRow row = HSSFCellUtil.getRow( i, sheet ); for (int i = rowStart; i <= rowEnd; i++) {
HSSFCell cell = HSSFCellUtil.getCell( row, column ); cps.setProperty(HSSFCellUtil.getRow(i, sheet), column);
HSSFCellUtil.setCellStyleProperty( }
cell, workbook, HSSFCellUtil.RIGHT_BORDER_COLOR, new Short( color ) ); }
}
}
/** /**
* Sets the borderBottom attribute of the HSSFRegionUtil object * @deprecated (Aug 2008) use {@link CellRangeAddress} instead of {@link Region}
* */
*@param border The new border public static void setRightBorderColor(short color, Region region, HSSFSheet sheet,
*@param region The region that should have the border HSSFWorkbook workbook) {
*@param workbook The workbook that the region is on. setRightBorderColor(color, toCRA(region), sheet, workbook);
*@param sheet The sheet that the region is on. }
*/ /**
public static void setBorderBottom( short border, Region region, HSSFSheet sheet, HSSFWorkbook workbook ) * Sets the rightBorderColor attribute of the HSSFRegionUtil object
{ *
int colStart = region.getColumnFrom(); * @param color The color of the border
int colEnd = region.getColumnTo(); * @param region The region that should have the border
int rowIndex = region.getRowTo(); * @param workbook The workbook that the region is on.
HSSFRow row = HSSFCellUtil.getRow( rowIndex, sheet ); * @param sheet The sheet that the region is on.
for ( int i = colStart; i <= colEnd; i++ ) { */
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 ); CellPropertySetter cps = new CellPropertySetter(workbook, HSSFCellUtil.RIGHT_BORDER_COLOR, color);
HSSFCellUtil.setCellStyleProperty( for (int i = rowStart; i <= rowEnd; i++) {
cell, workbook, HSSFCellUtil.BORDER_BOTTOM, new Short( border ) ); cps.setProperty(HSSFCellUtil.getRow(i, sheet), column);
} }
} }
/** /**
* Sets the bottomBorderColor attribute of the HSSFRegionUtil object * @deprecated (Aug 2008) use {@link CellRangeAddress} instead of {@link Region}
* */
*@param color The color of the border public static void setBorderBottom(short border, Region region, HSSFSheet sheet,
*@param region The region that should have the border HSSFWorkbook workbook) {
*@param workbook The workbook that the region is on. setBorderBottom(border, toCRA(region), sheet, workbook);
*@param sheet The sheet that the region is on. }
*/ /**
public static void setBottomBorderColor( short color, Region region, HSSFSheet sheet, HSSFWorkbook workbook ) * Sets the borderBottom attribute of the HSSFRegionUtil object
{ *
int colStart = region.getColumnFrom(); * @param border The new border
int colEnd = region.getColumnTo(); * @param region The region that should have the border
int rowIndex = region.getRowTo(); * @param workbook The workbook that the region is on.
HSSFRow row = HSSFCellUtil.getRow( rowIndex, sheet ); * @param sheet The sheet that the region is on.
for ( int i = colStart; i <= colEnd; i++ ) { */
HSSFCell cell = HSSFCellUtil.getCell( row, i ); public static void setBorderBottom(int border, CellRangeAddress region, HSSFSheet sheet,
HSSFCellUtil.setCellStyleProperty( HSSFWorkbook workbook) {
cell, workbook, HSSFCellUtil.BOTTOM_BORDER_COLOR, new Short( color ) ); 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 * @deprecated (Aug 2008) use {@link CellRangeAddress} instead of {@link Region}
* */
*@param border The new border public static void setBottomBorderColor(short color, Region region, HSSFSheet sheet,
*@param region The region that should have the border HSSFWorkbook workbook) {
*@param workbook The workbook that the region is on. setBottomBorderColor(color, toCRA(region), sheet, workbook);
*@param sheet The sheet that the region is on. }
*/ /**
public static void setBorderTop( short border, Region region, HSSFSheet sheet, HSSFWorkbook workbook ) * Sets the bottomBorderColor attribute of the HSSFRegionUtil object
{ *
int colStart = region.getColumnFrom(); * @param color The color of the border
int colEnd = region.getColumnTo(); * @param region The region that should have the border
int rowIndex = region.getRowFrom(); * @param workbook The workbook that the region is on.
HSSFRow row = HSSFCellUtil.getRow( rowIndex, sheet ); * @param sheet The sheet that the region is on.
for ( int i = colStart; i <= colEnd; i++ ) { */
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( * @deprecated (Aug 2008) use {@link CellRangeAddress} instead of {@link Region}
cell, workbook, HSSFCellUtil.BORDER_TOP, new Short( border ) ); */
} 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 * @deprecated (Aug 2008) use {@link CellRangeAddress} instead of {@link Region}
* */
*@param color The color of the border public static void setTopBorderColor(short color, Region region, HSSFSheet sheet,
*@param region The region that should have the border HSSFWorkbook workbook) {
*@param workbook The workbook that the region is on. setTopBorderColor(color, toCRA(region), sheet, workbook);
*@param sheet The sheet that the region is on. }
*/ /**
public static void setTopBorderColor( short color, Region region, HSSFSheet sheet, HSSFWorkbook workbook ) * Sets the topBorderColor attribute of the HSSFRegionUtil object
{ *
int colStart = region.getColumnFrom(); * @param color The color of the border
int colEnd = region.getColumnTo(); * @param region The region that should have the border
int rowIndex = region.getRowFrom(); * @param workbook The workbook that the region is on.
HSSFRow row = HSSFCellUtil.getRow( rowIndex, sheet ); * @param sheet The sheet that the region is on.
for ( int i = colStart; i <= colEnd; i++ ) { */
HSSFCell cell = HSSFCellUtil.getCell( row, i ); public static void setTopBorderColor(int color, CellRangeAddress region, HSSFSheet sheet,
HSSFCellUtil.setCellStyleProperty( HSSFWorkbook workbook) {
cell, workbook, HSSFCellUtil.TOP_BORDER_COLOR, new Short( color ) ); 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> <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>
<release version="3.1.1-alpha1" date="2008-??-??"> <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="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="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> <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> <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>
<release version="3.1.1-alpha1" date="2008-??-??"> <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="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="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> <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() ) switch ( in.getSid() )
{ {
case ChartRecord.sid: case AreaFormatRecord.sid: return new AreaFormatRecord(in);
return new ChartRecord( in ); case AreaRecord.sid: return new AreaRecord(in);
case ChartFormatRecord.sid: case AxisLineFormatRecord.sid: return new AxisLineFormatRecord(in);
return new ChartFormatRecord( in ); case AxisOptionsRecord.sid: return new AxisOptionsRecord(in);
case SeriesRecord.sid: case AxisParentRecord.sid: return new AxisParentRecord(in);
return new SeriesRecord( in ); case AxisRecord.sid: return new AxisRecord(in);
case BeginRecord.sid: case AxisUsedRecord.sid: return new AxisUsedRecord(in);
return new BeginRecord( in ); case BOFRecord.sid: return new BOFRecord(in);
case EndRecord.sid: case BackupRecord.sid: return new BackupRecord(in);
return new EndRecord( in ); case BarRecord.sid: return new BarRecord(in);
case BOFRecord.sid: case BeginRecord.sid: return new BeginRecord(in);
return new BOFRecord( in ); case BlankRecord.sid: return new BlankRecord(in);
case InterfaceHdrRecord.sid: case BookBoolRecord.sid: return new BookBoolRecord(in);
return new InterfaceHdrRecord( in ); case BoolErrRecord.sid: return new BoolErrRecord(in);
case MMSRecord.sid: case BottomMarginRecord.sid: return new BottomMarginRecord(in);
return new MMSRecord( in ); case BoundSheetRecord.sid: return new BoundSheetRecord(in);
case InterfaceEndRecord.sid: case CalcCountRecord.sid: return new CalcCountRecord(in);
return new InterfaceEndRecord( in ); case CalcModeRecord.sid: return new CalcModeRecord(in);
case WriteAccessRecord.sid: case CategorySeriesAxisRecord.sid: return new CategorySeriesAxisRecord(in);
return new WriteAccessRecord( in ); case ChartFormatRecord.sid: return new ChartFormatRecord(in);
case CodepageRecord.sid: case ChartRecord.sid: return new ChartRecord(in);
return new CodepageRecord( in ); case CodepageRecord.sid: return new CodepageRecord(in);
case DSFRecord.sid: case ColumnInfoRecord.sid: return new ColumnInfoRecord(in);
return new DSFRecord( in ); case ContinueRecord.sid: return new ContinueRecord(in);
case TabIdRecord.sid: case CountryRecord.sid: return new CountryRecord(in);
return new TabIdRecord( in ); case DBCellRecord.sid: return new DBCellRecord(in);
case FnGroupCountRecord.sid: case DSFRecord.sid: return new DSFRecord(in);
return new FnGroupCountRecord( in ); case DatRecord.sid: return new DatRecord(in);
case WindowProtectRecord.sid: case DataFormatRecord.sid: return new DataFormatRecord(in);
return new WindowProtectRecord( in ); case DateWindow1904Record.sid: return new DateWindow1904Record(in);
case ProtectRecord.sid: case DefaultColWidthRecord.sid:return new DefaultColWidthRecord(in);
return new ProtectRecord( in ); case DefaultDataLabelTextPropertiesRecord.sid: return new DefaultDataLabelTextPropertiesRecord(in);
case PasswordRecord.sid: case DefaultRowHeightRecord.sid: return new DefaultRowHeightRecord(in);
return new PasswordRecord( in ); case DeltaRecord.sid: return new DeltaRecord(in);
case ProtectionRev4Record.sid: case DimensionsRecord.sid: return new DimensionsRecord(in);
return new ProtectionRev4Record( in ); case DrawingGroupRecord.sid: return new DrawingGroupRecord(in);
case PasswordRev4Record.sid: case DrawingRecordForBiffViewer.sid: return new DrawingRecordForBiffViewer(in);
return new PasswordRev4Record( in ); case DrawingSelectionRecord.sid: return new DrawingSelectionRecord(in);
case WindowOneRecord.sid: case DVRecord.sid: return new DVRecord(in);
return new WindowOneRecord( in ); case DVALRecord.sid: return new DVALRecord(in);
case BackupRecord.sid: case EOFRecord.sid: return new EOFRecord(in);
return new BackupRecord( in ); case EndRecord.sid: return new EndRecord(in);
case HideObjRecord.sid: case ExtSSTRecord.sid: return new ExtSSTRecord(in);
return new HideObjRecord( in ); case ExtendedFormatRecord.sid: return new ExtendedFormatRecord(in);
case DateWindow1904Record.sid: case ExternSheetRecord.sid: return new ExternSheetRecord(in);
return new DateWindow1904Record( in ); case FilePassRecord.sid: return new FilePassRecord(in);
case PrecisionRecord.sid: case FileSharingRecord.sid: return new FileSharingRecord(in);
return new PrecisionRecord( in ); case FnGroupCountRecord.sid: return new FnGroupCountRecord(in);
case RefreshAllRecord.sid: case FontBasisRecord.sid: return new FontBasisRecord(in);
return new RefreshAllRecord( in ); case FontIndexRecord.sid: return new FontIndexRecord(in);
case BookBoolRecord.sid: case FontRecord.sid: return new FontRecord(in);
return new BookBoolRecord( in ); case FooterRecord.sid: return new FooterRecord(in);
case FontRecord.sid: case FormatRecord.sid: return new FormatRecord(in);
return new FontRecord( in ); case FormulaRecord.sid: return new FormulaRecord(in);
case FormatRecord.sid: case FrameRecord.sid: return new FrameRecord(in);
return new FormatRecord( in ); case GridsetRecord.sid: return new GridsetRecord(in);
case ExtendedFormatRecord.sid: case GutsRecord.sid: return new GutsRecord(in);
return new ExtendedFormatRecord( in ); case HCenterRecord.sid: return new HCenterRecord(in);
case StyleRecord.sid: case HeaderRecord.sid: return new HeaderRecord(in);
return new StyleRecord( in ); case HideObjRecord.sid: return new HideObjRecord(in);
case UseSelFSRecord.sid: case HorizontalPageBreakRecord.sid: return new HorizontalPageBreakRecord(in);
return new UseSelFSRecord( in ); case HyperlinkRecord.sid: return new HyperlinkRecord(in);
case BoundSheetRecord.sid: case IndexRecord.sid: return new IndexRecord(in);
return new BoundSheetRecord( in ); case InterfaceEndRecord.sid: return new InterfaceEndRecord(in);
case CountryRecord.sid: case InterfaceHdrRecord.sid: return new InterfaceHdrRecord(in);
return new CountryRecord( in ); case IterationRecord.sid: return new IterationRecord(in);
case SSTRecord.sid: case LabelRecord.sid: return new LabelRecord(in);
return new SSTRecord( in ); case LabelSSTRecord.sid: return new LabelSSTRecord(in);
case ExtSSTRecord.sid: case LeftMarginRecord.sid: return new LeftMarginRecord(in);
return new ExtSSTRecord( in ); case LegendRecord.sid: return new LegendRecord(in);
case EOFRecord.sid: case LineFormatRecord.sid: return new LineFormatRecord(in);
return new EOFRecord( in ); case LinkedDataRecord.sid: return new LinkedDataRecord(in);
case IndexRecord.sid: case MMSRecord.sid: return new MMSRecord(in);
return new IndexRecord( in ); case MergeCellsRecord.sid: return new MergeCellsRecord(in);
case CalcModeRecord.sid: case MulBlankRecord.sid: return new MulBlankRecord(in);
return new CalcModeRecord( in ); case MulRKRecord.sid: return new MulRKRecord(in);
case CalcCountRecord.sid: case NameRecord.sid: return new NameRecord(in);
return new CalcCountRecord( in ); case NoteRecord.sid: return new NoteRecord(in);
case RefModeRecord.sid: case NumberRecord.sid: return new NumberRecord(in);
return new RefModeRecord( in ); case ObjRecord.sid: return new ObjRecord(in);
case IterationRecord.sid: case ObjectLinkRecord.sid: return new ObjectLinkRecord(in);
return new IterationRecord( in ); case PaletteRecord.sid: return new PaletteRecord(in);
case DeltaRecord.sid: case PaneRecord.sid: return new PaneRecord(in);
return new DeltaRecord( in ); case PasswordRecord.sid: return new PasswordRecord(in);
case SaveRecalcRecord.sid: case PasswordRev4Record.sid: return new PasswordRev4Record(in);
return new SaveRecalcRecord( in ); case PlotAreaRecord.sid: return new PlotAreaRecord(in);
case PrintHeadersRecord.sid: case PlotGrowthRecord.sid: return new PlotGrowthRecord(in);
return new PrintHeadersRecord( in ); case PrecisionRecord.sid: return new PrecisionRecord(in);
case PrintGridlinesRecord.sid: case PrintGridlinesRecord.sid: return new PrintGridlinesRecord(in);
return new PrintGridlinesRecord( in ); case PrintHeadersRecord.sid: return new PrintHeadersRecord(in);
case GridsetRecord.sid: case PrintSetupRecord.sid: return new PrintSetupRecord(in);
return new GridsetRecord( in ); case ProtectRecord.sid: return new ProtectRecord(in);
case DrawingGroupRecord.sid: case ProtectionRev4Record.sid: return new ProtectionRev4Record(in);
return new DrawingGroupRecord( in ); case RKRecord.sid: return new RKRecord(in);
case DrawingRecordForBiffViewer.sid: case RefModeRecord.sid: return new RefModeRecord(in);
return new DrawingRecordForBiffViewer( in ); case RefreshAllRecord.sid: return new RefreshAllRecord(in);
case DrawingSelectionRecord.sid: case RightMarginRecord.sid: return new RightMarginRecord(in);
return new DrawingSelectionRecord( in ); case RowRecord.sid: return new RowRecord(in);
case GutsRecord.sid: case SCLRecord.sid: return new SCLRecord(in);
return new GutsRecord( in ); case SSTRecord.sid: return new SSTRecord(in);
case DefaultRowHeightRecord.sid: case SaveRecalcRecord.sid: return new SaveRecalcRecord(in);
return new DefaultRowHeightRecord( in ); case SelectionRecord.sid: return new SelectionRecord(in);
case WSBoolRecord.sid: case SeriesIndexRecord.sid: return new SeriesIndexRecord(in);
return new WSBoolRecord( in ); case SeriesListRecord.sid: return new SeriesListRecord(in);
case HeaderRecord.sid: case SeriesRecord.sid: return new SeriesRecord(in);
return new HeaderRecord( in ); case SeriesTextRecord.sid: return new SeriesTextRecord(in);
case FooterRecord.sid: case SeriesToChartGroupRecord.sid: return new SeriesToChartGroupRecord(in);
return new FooterRecord( in ); case SharedFormulaRecord.sid: return new SharedFormulaRecord(in);
case HCenterRecord.sid: case SheetPropertiesRecord.sid:return new SheetPropertiesRecord(in);
return new HCenterRecord( in ); case StringRecord.sid: return new StringRecord(in);
case VCenterRecord.sid: case StyleRecord.sid: return new StyleRecord(in);
return new VCenterRecord( in ); case SupBookRecord.sid: return new SupBookRecord(in);
case PrintSetupRecord.sid: case TabIdRecord.sid: return new TabIdRecord(in);
return new PrintSetupRecord( in ); case TableRecord.sid: return new TableRecord(in);
case DefaultColWidthRecord.sid: case TextObjectRecord.sid: return new TextObjectRecord(in);
return new DefaultColWidthRecord( in ); case TextRecord.sid: return new TextRecord(in);
case DimensionsRecord.sid: case TickRecord.sid: return new TickRecord(in);
return new DimensionsRecord( in ); case TopMarginRecord.sid: return new TopMarginRecord(in);
case RowRecord.sid: case UnitsRecord.sid: return new UnitsRecord(in);
return new RowRecord( in ); case UseSelFSRecord.sid: return new UseSelFSRecord(in);
case LabelSSTRecord.sid: case VCenterRecord.sid: return new VCenterRecord(in);
return new LabelSSTRecord( in ); case ValueRangeRecord.sid: return new ValueRangeRecord(in);
case RKRecord.sid: case VerticalPageBreakRecord.sid: return new VerticalPageBreakRecord(in);
return new RKRecord( in ); case WSBoolRecord.sid: return new WSBoolRecord(in);
case NumberRecord.sid: case WindowOneRecord.sid: return new WindowOneRecord(in);
return new NumberRecord( in ); case WindowProtectRecord.sid: return new WindowProtectRecord(in);
case DBCellRecord.sid: case WindowTwoRecord.sid: return new WindowTwoRecord(in);
return new DBCellRecord( in ); case WriteAccessRecord.sid: return new WriteAccessRecord(in);
case WindowTwoRecord.sid: case WriteProtectRecord.sid: return new WriteProtectRecord(in);
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 );
} }
return new UnknownRecord( in ); return new UnknownRecord(in);
} }

View File

@ -18,14 +18,21 @@
package org.apache.poi.hssf.dev; package org.apache.poi.hssf.dev;
import java.io.IOException;
import java.io.FileInputStream; import java.io.FileInputStream;
import java.io.FileOutputStream; 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.poifs.filesystem.POIFSFileSystem;
import org.apache.poi.ss.util.Region; import org.apache.poi.ss.util.Region;
import org.apache.poi.hssf.usermodel.*;
import org.apache.poi.hssf.util.*;
/** /**
* File for HSSF testing/examples * File for HSSF testing/examples
@ -127,7 +134,7 @@ public class HSSF
} }
c = r.createCell(( short ) (cellnum + 1), c = r.createCell(( short ) (cellnum + 1),
HSSFCell.CELL_TYPE_STRING); HSSFCell.CELL_TYPE_STRING);
c.setCellValue("TEST"); c.setCellValue(new HSSFRichTextString("TEST"));
s.setColumnWidth(( short ) (cellnum + 1), s.setColumnWidth(( short ) (cellnum + 1),
( short ) ((50 * 8) / (( double ) 1 / 20))); ( short ) ((50 * 8) / (( double ) 1 / 20)));
if ((rownum % 2) == 0) if ((rownum % 2) == 0)
@ -149,10 +156,8 @@ public class HSSF
// c.setCellValue(0); // c.setCellValue(0);
c.setCellStyle(cs3); c.setCellStyle(cs3);
} }
s.addMergedRegion(new Region(( short ) 0, ( short ) 0, ( short ) 3, s.addMergedRegion(new CellRangeAddress(0, 3, 0, 3));
( short ) 3)); s.addMergedRegion(new CellRangeAddress(100, 110, 100, 110));
s.addMergedRegion(new Region(( short ) 100, ( short ) 100,
( short ) 110, ( short ) 110));
// end draw thick black border // end draw thick black border
// create a sheet, set its title then delete it // 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.FunctionMetadata;
import org.apache.poi.hssf.record.formula.function.FunctionMetadataRegistry; import org.apache.poi.hssf.record.formula.function.FunctionMetadataRegistry;
import org.apache.poi.ss.usermodel.Workbook; 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.AreaReference;
import org.apache.poi.hssf.util.CellReference; import org.apache.poi.hssf.util.CellReference;
import org.apache.poi.hssf.util.CellReference.NameType; 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_CELL = 0;
public static final int FORMULA_TYPE_SHARED = 1; public static final int FORMULA_TYPE_SHARED = 1;
public static final int FORMULA_TYPE_ARRAY =2; 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; 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 String formulaString;
private final int formulaLength; private final int formulaLength;
@ -75,12 +79,6 @@ public final class FormulaParser {
private ParseNode _rootNode; 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'; private static char TAB = '\t';
/** /**
@ -112,9 +110,13 @@ public final class FormulaParser {
} }
public static Ptg[] parse(String formula, Workbook book) { 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(); fp.parse();
return fp.getRPNPtg(); return fp.getRPNPtg(formulaType);
} }
/** Read New Character From Input Stream */ /** Read New Character From Input Stream */

View File

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

View File

@ -25,7 +25,7 @@ import org.apache.poi.hssf.record.Record;
* *
* @author Josh Micich * @author Josh Micich
*/ */
final class RecordStream { public final class RecordStream {
private final List _list; private final List _list;
private int _nextIndex; private int _nextIndex;

View File

@ -17,13 +17,14 @@
package org.apache.poi.hssf.model; 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.ColumnInfoRecordsAggregate;
import org.apache.poi.hssf.record.aggregates.DataValidityTable;
import org.apache.poi.hssf.record.aggregates.FormulaRecordAggregate; import org.apache.poi.hssf.record.aggregates.FormulaRecordAggregate;
import org.apache.poi.hssf.record.aggregates.RowRecordsAggregate; import org.apache.poi.hssf.record.aggregates.RowRecordsAggregate;
import org.apache.poi.hssf.record.aggregates.ValueRecordsAggregate; import org.apache.poi.hssf.record.aggregates.ValueRecordsAggregate;
import org.apache.poi.hssf.record.aggregates.CFRecordsAggregate; 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.hssf.util.PaneInformation;
import org.apache.poi.util.POILogFactory; import org.apache.poi.util.POILogFactory;
@ -31,7 +32,7 @@ import org.apache.poi.util.POILogger;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Iterator; 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) * 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 ProtectRecord protect = null;
protected PageBreakRecord rowBreaks = null; protected PageBreakRecord rowBreaks = null;
protected PageBreakRecord colBreaks = null; protected PageBreakRecord colBreaks = null;
private DataValidityTable _dataValidityTable= null;
protected ObjectProtectRecord objprotect = null; protected ObjectProtectRecord objprotect = null;
protected ScenarioProtectRecord scenprotect = null; protected ScenarioProtectRecord scenprotect = null;
protected PasswordRecord password = null; protected PasswordRecord password = null;
@ -299,7 +301,12 @@ public final class Sheet implements Model {
// and POI always re-calculates its contents // and POI always re-calculates its contents
rec = null; 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 ) else if ( rec.getSid() == ProtectRecord.sid )
{ {
retval.protect = (ProtectRecord) rec; retval.protect = (ProtectRecord) rec;
@ -425,56 +432,56 @@ public final class Sheet implements Model {
Sheet retval = new Sheet(); Sheet retval = new Sheet();
ArrayList records = new ArrayList(30); ArrayList records = new ArrayList(30);
records.add(retval.createBOF()); records.add(createBOF());
// records.add(retval.createIndex()); // records.add(retval.createIndex());
records.add(retval.createCalcMode()); records.add(createCalcMode());
records.add(retval.createCalcCount() ); records.add(createCalcCount() );
records.add( retval.createRefMode() ); records.add(createRefMode() );
records.add( retval.createIteration() ); records.add(createIteration() );
records.add( retval.createDelta() ); records.add(createDelta() );
records.add( retval.createSaveRecalc() ); records.add(createSaveRecalc() );
records.add( retval.createPrintHeaders() ); records.add(createPrintHeaders() );
retval.printGridlines = (PrintGridlinesRecord) retval.createPrintGridlines(); retval.printGridlines = createPrintGridlines();
records.add( retval.printGridlines ); records.add( retval.printGridlines );
retval.gridset = (GridsetRecord) retval.createGridset(); retval.gridset = createGridset();
records.add( retval.gridset ); records.add( retval.gridset );
records.add( retval.createGuts() ); records.add( retval.createGuts() );
retval.defaultrowheight = retval.defaultrowheight = createDefaultRowHeight();
(DefaultRowHeightRecord) retval.createDefaultRowHeight();
records.add( retval.defaultrowheight ); records.add( retval.defaultrowheight );
records.add( retval.createWSBool() ); records.add( retval.createWSBool() );
// 'Page Settings Block'
retval.rowBreaks = new PageBreakRecord(PageBreakRecord.HORIZONTAL_SID); retval.rowBreaks = new PageBreakRecord(PageBreakRecord.HORIZONTAL_SID);
records.add(retval.rowBreaks); records.add(retval.rowBreaks);
retval.colBreaks = new PageBreakRecord(PageBreakRecord.VERTICAL_SID); retval.colBreaks = new PageBreakRecord(PageBreakRecord.VERTICAL_SID);
records.add(retval.colBreaks); records.add(retval.colBreaks);
retval.header = (HeaderRecord) retval.createHeader(); retval.header = createHeader();
records.add( retval.header ); records.add( retval.header );
retval.footer = (FooterRecord) retval.createFooter(); retval.footer = createFooter();
records.add( retval.footer ); records.add( retval.footer );
records.add( retval.createHCenter() ); records.add(createHCenter() );
records.add( retval.createVCenter() ); records.add(createVCenter() );
retval.printSetup = (PrintSetupRecord) retval.createPrintSetup(); retval.printSetup = createPrintSetup();
records.add( retval.printSetup ); 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); records.add( retval.defaultcolwidth);
ColumnInfoRecordsAggregate columns = new ColumnInfoRecordsAggregate(); ColumnInfoRecordsAggregate columns = new ColumnInfoRecordsAggregate();
records.add( columns ); records.add( columns );
retval.columns = columns; retval.columns = columns;
retval.dims = ( DimensionsRecord ) retval.createDimensions(); retval.dims = createDimensions();
records.add(retval.dims); records.add(retval.dims);
retval.dimsloc = records.size()-1; retval.dimsloc = records.size()-1;
records.add(retval.windowTwo = retval.createWindowTwo()); records.add(retval.windowTwo = retval.createWindowTwo());
retval.setLoc(records.size() - 1); retval.setLoc(records.size() - 1);
retval.selection = retval.selection = createSelection();
(SelectionRecord) retval.createSelection();
records.add(retval.selection); records.add(retval.selection);
retval.protect = (ProtectRecord) retval.createProtect(); records.add(new EOFRecord());
records.add(retval.protect);
records.add(retval.createEOF());
retval.records = records; 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 // Validate input
if (rowTo < rowFrom) { if (rowTo < rowFrom) {
throw new IllegalArgumentException("The 'to' row (" + rowTo throw new IllegalArgumentException("The 'to' row (" + rowTo
@ -522,7 +529,7 @@ public final class Sheet implements Model {
if (merged == null || merged.getNumAreas() == 1027) if (merged == null || merged.getNumAreas() == 1027)
{ {
merged = ( MergeCellsRecord ) createMergedCells(); merged = createMergedCells();
mergedRecords.add(merged); mergedRecords.add(merged);
records.add(records.size() - 1, 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 //safety checks
if (index >= numMergedRegions || mergedRecords.size() == 0) 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) * 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
*/ */
private static RowRecord createRow(int row) {
public RowRecord createRow(int row)
{
return RowRecordsAggregate.createRow( 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 * Adds a value record to the sheet's contained binary records
* (i.e. LabelSSTRecord or NumberRecord). * (i.e. LabelSSTRecord or NumberRecord).
@ -1247,13 +1141,8 @@ public final class Sheet implements Model {
/** /**
* creates the BOF record * creates the BOF record
* @see org.apache.poi.hssf.record.BOFRecord
* @see org.apache.poi.hssf.record.Record
* @return record containing a BOFRecord
*/ */
private static BOFRecord createBOF() {
protected Record createBOF()
{
BOFRecord retval = new BOFRecord(); BOFRecord retval = new BOFRecord();
retval.setVersion(( short ) 0x600); retval.setVersion(( short ) 0x600);
@ -1266,31 +1155,10 @@ public final class Sheet implements Model {
return retval; 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) * 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
*/ */
private static CalcModeRecord createCalcMode() {
protected Record createCalcMode()
{
CalcModeRecord retval = new CalcModeRecord(); CalcModeRecord retval = new CalcModeRecord();
retval.setCalcMode(( short ) 1); 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) * creates the CalcCount record and sets it to 100 (default number of iterations)
* @see org.apache.poi.hssf.record.CalcCountRecord
* @see org.apache.poi.hssf.record.Record
* @return record containing a CalcCountRecord
*/ */
private static CalcCountRecord createCalcCount() {
protected Record createCalcCount()
{
CalcCountRecord retval = new CalcCountRecord(); CalcCountRecord retval = new CalcCountRecord();
retval.setIterations(( short ) 0x64); // default 64 iterations retval.setIterations(( short ) 100); // default 100 iterations
return retval; return retval;
} }
/** /**
* creates the RefMode record and sets it to A1 Mode (default reference mode) * 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
*/ */
private static RefModeRecord createRefMode() {
protected Record createRefMode()
{
RefModeRecord retval = new RefModeRecord(); RefModeRecord retval = new RefModeRecord();
retval.setMode(RefModeRecord.USE_A1_MODE); 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) * 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
*/ */
private static IterationRecord createIteration() {
protected Record createIteration()
{
IterationRecord retval = new IterationRecord(); IterationRecord retval = new IterationRecord();
retval.setIteration(false); 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) * 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
*/ */
private static DeltaRecord createDelta() {
protected Record createDelta()
{
DeltaRecord retval = new DeltaRecord(); DeltaRecord retval = new DeltaRecord();
retval.setMaxChange(0.0010); 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) * 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
*/ */
private static SaveRecalcRecord createSaveRecalc() {
protected Record createSaveRecalc()
{
SaveRecalcRecord retval = new SaveRecalcRecord(); SaveRecalcRecord retval = new SaveRecalcRecord();
retval.setRecalc(true); 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) * 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
*/ */
private static PrintHeadersRecord createPrintHeaders() {
protected Record createPrintHeaders()
{
PrintHeadersRecord retval = new PrintHeadersRecord(); PrintHeadersRecord retval = new PrintHeadersRecord();
retval.setPrintHeaders(false); 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 * 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 * 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
*/ */
private static PrintGridlinesRecord createPrintGridlines() {
protected Record createPrintGridlines()
{
PrintGridlinesRecord retval = new PrintGridlinesRecord(); PrintGridlinesRecord retval = new PrintGridlinesRecord();
retval.setPrintGridlines(false); 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) * 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
*/ */
private static GridsetRecord createGridset() {
protected Record createGridset()
{
GridsetRecord retval = new GridsetRecord(); GridsetRecord retval = new GridsetRecord();
retval.setGridset(true); 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 * 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 private static GutsRecord createGuts() {
* @return record containing a GutsRecordRecord
*/
protected Record createGuts()
{
GutsRecord retval = new GutsRecord(); GutsRecord retval = new GutsRecord();
retval.setLeftRowGutter(( short ) 0); 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 * 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
*/ */
private static DefaultRowHeightRecord createDefaultRowHeight() {
protected Record createDefaultRowHeight()
{
DefaultRowHeightRecord retval = new DefaultRowHeightRecord(); DefaultRowHeightRecord retval = new DefaultRowHeightRecord();
retval.setOptionFlags(( short ) 0); retval.setOptionFlags(( short ) 0);
@ -1455,13 +1272,8 @@ public final class Sheet implements Model {
/** /**
* creates the WSBoolRecord and sets its values to defaults * 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
*/ */
private static WSBoolRecord createWSBool() {
protected Record createWSBool()
{
WSBoolRecord retval = new WSBoolRecord(); WSBoolRecord retval = new WSBoolRecord();
retval.setWSBool1(( byte ) 0x4); 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 * 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
*/ */
private static HeaderRecord createHeader() {
protected Record createHeader()
{
HeaderRecord retval = new HeaderRecord(); HeaderRecord retval = new HeaderRecord();
retval.setHeaderLength(( byte ) 0); 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 * 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
*/ */
private static FooterRecord createFooter() {
protected Record createFooter()
{
FooterRecord retval = new FooterRecord(); FooterRecord retval = new FooterRecord();
retval.setFooterLength(( byte ) 0); 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) * 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
*/ */
private static HCenterRecord createHCenter() {
protected Record createHCenter()
{
HCenterRecord retval = new HCenterRecord(); HCenterRecord retval = new HCenterRecord();
retval.setHCenter(false); 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) * 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 private static VCenterRecord createVCenter() {
* @return record containing a VCenterRecord
*/
protected Record createVCenter()
{
VCenterRecord retval = new VCenterRecord(); VCenterRecord retval = new VCenterRecord();
retval.setVCenter(false); retval.setVCenter(false);
@ -1537,9 +1329,7 @@ public final class Sheet implements Model {
* @see org.apache.poi.hssf.record.Record * @see org.apache.poi.hssf.record.Record
* @return record containing a PrintSetupRecord * @return record containing a PrintSetupRecord
*/ */
private static PrintSetupRecord createPrintSetup() {
protected Record createPrintSetup()
{
PrintSetupRecord retval = new PrintSetupRecord(); PrintSetupRecord retval = new PrintSetupRecord();
retval.setPaperSize(( short ) 1); retval.setPaperSize(( short ) 1);
@ -1558,30 +1348,13 @@ public final class Sheet implements Model {
/** /**
* creates the DefaultColWidth Record and sets it to 8 * creates the DefaultColWidth Record and sets it to 8
* @see org.apache.poi.hssf.record.DefaultColWidthRecord */
* @see org.apache.poi.hssf.record.Record private static DefaultColWidthRecord createDefaultColWidth() {
* @return record containing a DefaultColWidthRecord
*/
protected Record createDefaultColWidth()
{
DefaultColWidthRecord retval = new DefaultColWidthRecord(); DefaultColWidthRecord retval = new DefaultColWidthRecord();
retval.setColWidth(( short ) 8); retval.setColWidth(( short ) 8);
return retval; 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) * get the default column width for the sheet (if the columns do not define their own width)
* @return default column width * @return default column width
@ -1600,7 +1373,7 @@ public final class Sheet implements Model {
public boolean isGridsPrinted() public boolean isGridsPrinted()
{ {
if (gridset == null) { if (gridset == null) {
gridset = (GridsetRecord)createGridset(); gridset = createGridset();
//Insert the newlycreated Gridset record at the end of the record (just before the EOF) //Insert the newlycreated Gridset record at the end of the record (just before the EOF)
int loc = findFirstRecordLocBySid(EOFRecord.sid); int loc = findFirstRecordLocBySid(EOFRecord.sid);
records.add(loc, gridset); records.add(loc, gridset);
@ -1816,22 +1589,18 @@ public final class Sheet implements Model {
GutsRecord guts = (GutsRecord) findFirstRecordBySid( GutsRecord.sid ); GutsRecord guts = (GutsRecord) findFirstRecordBySid( GutsRecord.sid );
guts.setColLevelMax( (short) ( maxLevel+1 ) ); guts.setColLevelMax( (short) ( maxLevel+1 ) );
if (maxLevel == 0) if (maxLevel == 0) {
guts.setTopColGutter( (short)0 ); guts.setTopColGutter( (short)0 );
else } else {
guts.setTopColGutter( (short) ( 29 + (12 * (maxLevel-1)) ) ); guts.setTopColGutter( (short) ( 29 + (12 * (maxLevel-1)) ) );
}
} }
/** /**
* creates the Dimensions Record and sets it to bogus values (you should set this yourself * 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) * 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
*/ */
private static DimensionsRecord createDimensions() {
protected Record createDimensions()
{
DimensionsRecord retval = new DimensionsRecord(); DimensionsRecord retval = new DimensionsRecord();
retval.setFirstCol(( short ) 0); retval.setFirstCol(( short ) 0);
@ -1849,13 +1618,8 @@ public final class Sheet implements Model {
* headercolor = 0x40 <P> * headercolor = 0x40 <P>
* pagebreakzoom = 0x0 <P> * pagebreakzoom = 0x0 <P>
* normalzoom = 0x0 <p> * normalzoom = 0x0 <p>
* @see org.apache.poi.hssf.record.WindowTwoRecord
* @see org.apache.poi.hssf.record.Record
* @return record containing a WindowTwoRecord
*/ */
private static WindowTwoRecord createWindowTwo() {
protected WindowTwoRecord createWindowTwo()
{
WindowTwoRecord retval = new WindowTwoRecord(); WindowTwoRecord retval = new WindowTwoRecord();
retval.setOptions(( short ) 0x6b6); retval.setOptions(( short ) 0x6b6);
@ -1869,21 +1633,9 @@ public final class Sheet implements Model {
/** /**
* Creates the Selection record and sets it to nothing selected * Creates the Selection record and sets it to nothing selected
* */
* @see org.apache.poi.hssf.record.SelectionRecord private static SelectionRecord createSelection() {
* @see org.apache.poi.hssf.record.Record return new SelectionRecord(0, 0);
* @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;
} }
public short getTopRow() public short getTopRow()
@ -1903,19 +1655,15 @@ public final class Sheet implements Model {
* Sets the left column to show in desktop window pane. * Sets the left column to show in desktop window pane.
* @param leftCol 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){ public void setLeftCol(short leftCol){
if (windowTwo!=null) if (windowTwo!=null) {
{
windowTwo.setLeftCol(leftCol); windowTwo.setLeftCol(leftCol);
}
} }
}
public short getLeftCol() public short getLeftCol() {
{ return (windowTwo==null) ? (short) 0 : windowTwo.getLeftCol();
return (windowTwo==null) ? (short) 0 : windowTwo.getLeftCol(); }
}
/** /**
* Returns the active row * 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 * @see org.apache.poi.hssf.record.SelectionRecord
* @return row the active column index * @return column of the active cell
*/ */
public short getActiveCellCol() public short getActiveCellCol() {
{ if (selection == null) {
if (selection == null) return 0;
{
return (short) 0;
} }
return selection.getActiveCellCol(); return (short)selection.getActiveCellCol();
} }
/** /**
@ -1977,23 +1721,8 @@ public final class Sheet implements Model {
} }
} }
protected Record createMergedCells() private static MergeCellsRecord createMergedCells() {
{ return new MergeCellsRecord();
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();
} }
/** /**
@ -2383,28 +2112,20 @@ public final class Sheet implements Model {
/** /**
* creates a Protect record with protect set to false. * 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() private static ProtectRecord createProtect() {
{ if (log.check( POILogger.DEBUG )) {
if (log.check( POILogger.DEBUG ))
log.log(POILogger.DEBUG, "create protect record with protection disabled"); log.log(POILogger.DEBUG, "create protect record with protection disabled");
ProtectRecord retval = new ProtectRecord(); }
ProtectRecord retval = new ProtectRecord();
retval.setProtect(false); retval.setProtect(false); // TODO - supply param to constructor
return retval; return retval;
} }
/** /**
* creates an ObjectProtect record with protect set to false. * 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 )) if (log.check( POILogger.DEBUG ))
log.log(POILogger.DEBUG, "create protect record with protection disabled"); log.log(POILogger.DEBUG, "create protect record with protection disabled");
ObjectProtectRecord retval = new ObjectProtectRecord(); ObjectProtectRecord retval = new ObjectProtectRecord();
@ -2415,12 +2136,8 @@ public final class Sheet implements Model {
/** /**
* creates a ScenarioProtect record with protect set to false. * 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 )) if (log.check( POILogger.DEBUG ))
log.log(POILogger.DEBUG, "create protect record with protection disabled"); log.log(POILogger.DEBUG, "create protect record with protection disabled");
ScenarioProtectRecord retval = new ScenarioProtectRecord(); ScenarioProtectRecord retval = new ScenarioProtectRecord();
@ -2435,9 +2152,9 @@ public final class Sheet implements Model {
public ProtectRecord getProtect() public ProtectRecord getProtect()
{ {
if (protect == null) { if (protect == null) {
protect = (ProtectRecord)createProtect(); protect = createProtect();
//Insert the newlycreated protect record at the end of the record (just before the EOF) // Insert the newly created protect record just before DefaultColWidthRecord
int loc = findFirstRecordLocBySid(EOFRecord.sid); int loc = findFirstRecordLocBySid(DefaultColWidthRecord.sid);
records.add(loc, protect); records.add(loc, protect);
} }
return protect; return protect;
@ -2459,14 +2176,11 @@ public final class Sheet implements Model {
/** /**
* creates a Password record with password set to 00. * 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() private static PasswordRecord createPassword() {
{ if (log.check( POILogger.DEBUG )) {
if (log.check( POILogger.DEBUG )) log.log(POILogger.DEBUG, "create password record with 00 password");
log.log(POILogger.DEBUG, "create password record with 00 password"); }
PasswordRecord retval = new PasswordRecord(); PasswordRecord retval = new PasswordRecord();
retval.setPassword((short)00); retval.setPassword((short)00);
@ -2892,4 +2606,10 @@ public final class Sheet implements Model {
rows.expandRow( row ); 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; package org.apache.poi.hssf.record;
import org.apache.poi.hssf.record.cf.CellRange; import org.apache.poi.hssf.record.cf.CellRangeUtil;
import org.apache.poi.hssf.util.Region; import org.apache.poi.ss.util.CellRangeAddress;
import org.apache.poi.ss.util.CellRangeAddressList;
import org.apache.poi.util.LittleEndian; import org.apache.poi.util.LittleEndian;
/** /**
* Conditional Formatting Header record (CFHEADER) * Conditional Formatting Header record CFHEADER (0x1B0)
* *
* @author Dmitriy Kumshayev * @author Dmitriy Kumshayev
*/ */
public final class CFHeaderRecord extends Record public final class CFHeaderRecord extends Record {
{
public static final short sid = 0x1B0; public static final short sid = 0x1B0;
private static final CellRange[] EMPTY_CELL_RANGE_ARRAY = { };
private int field_1_numcf; private int field_1_numcf;
private int field_2_need_recalculation; private int field_2_need_recalculation;
private CellRange field_3_enclosing_cell_range; private CellRangeAddress field_3_enclosing_cell_range;
private CellRange[] field_4_cell_ranges; private CellRangeAddressList field_4_cell_ranges;
/** Creates new CFHeaderRecord */ /** Creates new CFHeaderRecord */
public 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); CellRangeAddress[] unmergedRanges = regions;
CellRange[] mergeCellRanges = CellRange.mergeCellRanges(unmergedRanges); CellRangeAddress[] mergeCellRanges = CellRangeUtil.mergeCellRanges(unmergedRanges);
setCellRanges(mergeCellRanges); setCellRanges(mergeCellRanges);
} }
@ -58,14 +56,8 @@ public final class CFHeaderRecord extends Record
{ {
field_1_numcf = in.readShort(); field_1_numcf = in.readShort();
field_2_need_recalculation = in.readShort(); field_2_need_recalculation = in.readShort();
field_3_enclosing_cell_range = new CellRange(in.readUShort(), in.readUShort(), in.readUShort(), in.readUShort()); field_3_enclosing_cell_range = new org.apache.poi.hssf.util.CellRangeAddress(in);
int numCellRanges = in.readShort(); field_4_cell_ranges = new org.apache.poi.hssf.util.CellRangeAddressList(in);
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;
} }
public int getNumberOfConditionalFormats() public int getNumberOfConditionalFormats()
@ -87,14 +79,14 @@ public final class CFHeaderRecord extends Record
field_2_need_recalculation=b?1:0; field_2_need_recalculation=b?1:0;
} }
public CellRange getEnclosingCellRange() public CellRangeAddress getEnclosingCellRange()
{ {
return field_3_enclosing_cell_range; 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. * modify the enclosing cell range accordingly.
* @param List cellRanges - list of CellRange objects * @param List cellRanges - list of CellRange objects
*/ */
public void setCellRanges(CellRange[] cellRanges) public void setCellRanges(CellRangeAddress[] cellRanges)
{ {
if(cellRanges == null) if(cellRanges == null)
{ {
throw new IllegalArgumentException("cellRanges must not be null"); throw new IllegalArgumentException("cellRanges must not be null");
} }
field_4_cell_ranges = (CellRange[]) cellRanges.clone(); CellRangeAddressList cral = new CellRangeAddressList();
CellRange enclosingRange = null; CellRangeAddress enclosingRange = null;
for (int i = 0; i < cellRanges.length; i++) 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() public CellRangeAddress[] getCellRanges() {
{ return field_4_cell_ranges.getCellRangeAddresses();
return (CellRange[]) field_4_cell_ranges.clone();
} }
public String toString() public String toString()
@ -131,50 +125,39 @@ public final class CFHeaderRecord extends Record
buffer.append(" .numCF = ").append(getNumberOfConditionalFormats()).append("\n"); buffer.append(" .numCF = ").append(getNumberOfConditionalFormats()).append("\n");
buffer.append(" .needRecalc = ").append(getNeedRecalculation()).append("\n"); buffer.append(" .needRecalc = ").append(getNeedRecalculation()).append("\n");
buffer.append(" .enclosingCellRange= ").append(getEnclosingCellRange()).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=["); buffer.append(i==0?"":",").append(field_4_cell_ranges.getCellRangeAddress(i).toString());
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("]\n");
buffer.append("[/CFHEADER]\n"); buffer.append("[/CFHEADER]\n");
return buffer.toString(); 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 * @return byte array containing instance data
*/ */
public int serialize(int offset, byte[] data) {
public int serialize(int offset, byte[] data) int dataSize = getDataSize();
{
int recordsize = getRecordSize();
LittleEndian.putShort(data, 0 + offset, sid); LittleEndian.putUShort(data, 0 + offset, sid);
LittleEndian.putShort(data, 2 + offset, (short) (recordsize-4)); LittleEndian.putUShort(data, 2 + offset, dataSize);
LittleEndian.putShort(data, 4 + offset, (short) field_1_numcf); LittleEndian.putUShort(data, 4 + offset, field_1_numcf);
LittleEndian.putShort(data, 6 + offset, (short) field_2_need_recalculation); LittleEndian.putUShort(data, 6 + offset, field_2_need_recalculation);
LittleEndian.putShort(data, 8 + offset, (short) field_3_enclosing_cell_range.getFirstRow()); field_3_enclosing_cell_range.serialize(8 + offset, data);
LittleEndian.putShort(data, 10 + offset, (short) field_3_enclosing_cell_range.getLastRow()); field_4_cell_ranges.serialize(16 + offset, data);
LittleEndian.putShort(data, 12 + offset, (short) field_3_enclosing_cell_range.getFirstColumn()); return 4 + dataSize;
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();
} }
public int getRecordSize() public int getRecordSize() {
{ return 4 + getDataSize();
return 18+8*field_4_cell_ranges.length;
} }
/** /**
@ -204,11 +187,7 @@ public final class CFHeaderRecord extends Record
result.field_1_numcf = field_1_numcf; result.field_1_numcf = field_1_numcf;
result.field_2_need_recalculation = field_2_need_recalculation; result.field_2_need_recalculation = field_2_need_recalculation;
result.field_3_enclosing_cell_range = field_3_enclosing_cell_range; result.field_3_enclosing_cell_range = field_3_enclosing_cell_range;
CellRange[] crs = new CellRange[field_4_cell_ranges.length]; result.field_4_cell_ranges = field_4_cell_ranges.copy();
for (int i = 0; i < crs.length; i++) {
crs[i] = field_4_cell_ranges[i].cloneCellRange();
}
result.field_4_cell_ranges = crs;
return result; return result;
} }
} }

View File

@ -16,16 +16,14 @@
package org.apache.poi.hssf.record; package org.apache.poi.hssf.record;
import java.io.IOException; import org.apache.poi.hssf.record.UnicodeString.UnicodeRecordStats;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.Stack;
import org.apache.poi.hssf.record.formula.Ptg; 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.BitField;
import org.apache.poi.util.LittleEndian; import org.apache.poi.util.LittleEndian;
import org.apache.poi.util.StringUtil;
/** /**
* Title: DATAVALIDATION Record (0x01BE)<p/> * 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 * are stored in a sequential list of DV records. This list is followed by
* DVAL record(s) * DVAL record(s)
* @author Dragos Buleandra (dragos.buleandra@trade2b.ro) * @author Dragos Buleandra (dragos.buleandra@trade2b.ro)
* @version 2.0-pre * @author Josh Micich
*/ */
public final class DVRecord extends Record public final class DVRecord extends Record {
{ public final static short sid = 0x01BE;
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
*/ /** Option flags */
private int field_option_flags; private int _option_flags;
/** Title of the prompt box */
/** private UnicodeString _promptTitle;
* Title of the prompt box /** Title of the error box */
*/ private UnicodeString _errorTitle;
private String field_title_prompt; /** Text of the prompt box */
private UnicodeString _promptText;
/** /** Text of the error box */
* Title of the error box private UnicodeString _errorText;
*/ /** Not used - Excel seems to always write 0x3FE0 */
private String field_title_error; private short _not_used_1 = 0x3FE0;
/** Formula data for first condition (RPN token array without size field) */
/** private Ptg[] _formula1;
* Text of the prompt box /** Not used - Excel seems to always write 0x0000 */
*/ private short _not_used_2 = 0x0000;
private String field_text_prompt; /** Formula data for second condition (RPN token array without size field) */
private Ptg[] _formula2;
/** /** Cell range address list with all affected ranges */
* Text of the error box private CellRangeAddressList _regions;
*/
private String field_text_error; /**
* Option flags field
/** *
* Size of the formula data for first condition * @see org.apache.poi.hssf.util.HSSFDataValidation utility class
*/ */
private short field_size_first_formula; 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);
* Not used private static final BitField opt_empty_cell_allowed = new BitField(0x00000100);
*/ private static final BitField opt_suppress_dropdown_arrow = new BitField(0x00000200);
private short field_not_used_1 = 0x3FE0; 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);
* Formula data for first condition (RPN token array without size field)
*/ /**
private Stack field_rpn_token_1 ; * Constructs a DV record and sets its fields appropriately.
*
/** * @param in the RecordInputstream to read the record from
* Size of the formula data for second condition */
*/ public DVRecord(RecordInputStream in) {
private short field_size_sec_formula; super(in);
}
/**
* Not used public DVRecord(int validationType, int operator, int errorStyle, boolean emptyCellAllowed,
*/ boolean suppressDropDownArrow, boolean isExplicitList,
private short field_not_used_2 = 0x0000; boolean showPromptBox, String promptTitle, String promptText,
boolean showErrorBox, String errorTitle, String errorText,
/** Ptg[] formula1, Ptg[] formula2,
* Formula data for second condition (RPN token array without size field) CellRangeAddressList regions) {
*/
private Stack field_rpn_token_2 ; int flags = 0;
flags = opt_data_type.setValue(flags, validationType);
/** flags = opt_condition_operator.setValue(flags, operator);
* Cell range address list with all affected ranges flags = opt_error_style.setValue(flags, errorStyle);
*/ flags = opt_empty_cell_allowed.setBoolean(flags, emptyCellAllowed);
private HSSFCellRangeAddress field_regions; flags = opt_suppress_dropdown_arrow.setBoolean(flags, suppressDropDownArrow);
flags = opt_string_list_formula.setBoolean(flags, isExplicitList);
public static final Integer STRING_PROMPT_TITLE = new Integer(0); flags = opt_show_prompt_on_cell_selected.setBoolean(flags, showPromptBox);
public static final Integer STRING_ERROR_TITLE = new Integer(1); flags = opt_show_error_on_invalid_value.setBoolean(flags, showErrorBox);
public static final Integer STRING_PROMPT_TEXT = new Integer(2); _option_flags = flags;
public static final Integer STRING_ERROR_TEXT = new Integer(3); _promptTitle = resolveTitleText(promptTitle);
private Hashtable _hash_strings ; _promptText = resolveTitleText(promptText);
_errorTitle = resolveTitleText(errorTitle);
/** _errorText = resolveTitleText(errorText);
* Option flags field _formula1 = formula1;
* @see org.apache.poi.hssf.util.HSSFDataValidation utility class _formula2 = formula2;
*/ _regions = regions;
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); protected void validateSid(short id) {
private BitField opt_empty_cell_allowed = new BitField(0x00000100); if (id != sid) {
private BitField opt_surppres_dropdown_arrow = new BitField(0x00000200); throw new RecordFormatException("NOT a valid DV RECORD");
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);
protected void fillFields(RecordInputStream in) {
public DVRecord()
{ _option_flags = in.readInt();
}
_promptTitle = readUnicodeString(in);
/** _errorTitle = readUnicodeString(in);
* Constructs a DV record and sets its fields appropriately. _promptText = readUnicodeString(in);
* _errorText = readUnicodeString(in);
* @param in the RecordInputstream to read the record from
*/ int field_size_first_formula = in.readUShort();
_not_used_1 = in.readShort();
public DVRecord(RecordInputStream in)
{ //read first formula data condition
super(in); _formula1 = Ptg.readTokens(field_size_first_formula, in);
}
int field_size_sec_formula = in.readUShort();
protected void validateSid(short id) _not_used_2 = in.readShort();
{
if (id != sid) //read sec formula data condition
{ _formula2 = Ptg.readTokens(field_size_sec_formula, in);
throw new RecordFormatException("NOT a valid DV RECORD");
} //read cell range address list with all affected ranges
} _regions = new org.apache.poi.hssf.util.CellRangeAddressList(in);
}
protected void fillFields(RecordInputStream in)
{ // --> start option flags
field_rpn_token_1 = new Stack(); /**
field_rpn_token_2 = new Stack(); * @return the condition data type
* @see DVConstraint.ValidationType
this.field_option_flags = in.readInt(); */
this._hash_strings = new Hashtable(4); public int getDataType() {
return opt_data_type.getValue(_option_flags);
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); /**
* @return the condition error style
StringHandler strHandler_error_title = new StringHandler( in ); * @see HSSFDataValidation.ErrorStyle
this.field_title_error = strHandler_error_title.getStringData(); */
this._hash_strings.put(DVRecord.STRING_ERROR_TITLE, strHandler_error_title); public int getErrorStyle() {
return opt_error_style.getValue(_option_flags);
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); /**
* @return <code>true</code> if in list validations the string list is explicitly given in the
StringHandler strHandler_error_text = new StringHandler( in ); * formula, <code>false</code> otherwise
this.field_text_error = strHandler_error_text.getStringData(); */
this._hash_strings.put(DVRecord.STRING_ERROR_TEXT, strHandler_error_text); public boolean getListExplicitFormula() {
return (opt_string_list_formula.isSet(_option_flags));
this.field_size_first_formula = in.readShort(); }
this.field_not_used_1 = in.readShort();
/**
//read first formula data condition * @return <code>true</code> if empty values are allowed in cells, <code>false</code> otherwise
int token_pos = 0; */
while (token_pos < this.field_size_first_formula) public boolean getEmptyCellAllowed() {
{ return (opt_empty_cell_allowed.isSet(_option_flags));
Ptg ptg = Ptg.createPtg(in); }
token_pos += ptg.getSize();
field_rpn_token_1.push(ptg);
} /**
* @return <code>true</code> if drop down arrow should be suppressed when list validation is
this.field_size_sec_formula = in.readShort(); * used, <code>false</code> otherwise
this.field_not_used_2 = in.readShort(); */
public boolean getSuppressDropdownArrow() {
//read sec formula data condition return (opt_suppress_dropdown_arrow.isSet(_option_flags));
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) { * @return <code>true</code> if a prompt window should appear when cell is selected, <code>false</code> otherwise
e.printStackTrace(); */
throw new IllegalStateException(e.getMessage()); public boolean getShowPromptOnCellSelected() {
} return (opt_show_prompt_on_cell_selected.isSet(_option_flags));
} }
token_pos = 0;
while (token_pos < this.field_size_sec_formula) /**
{ * @return <code>true</code> if an error window should appear when an invalid value is entered
Ptg ptg = Ptg.createPtg(in); * in the cell, <code>false</code> otherwise
token_pos += ptg.getSize(); */
field_rpn_token_2.push(ptg); public boolean getShowErrorOnInvalidValue() {
} return (opt_show_error_on_invalid_value.isSet(_option_flags));
}
//read cell range address list with all affected ranges
this.field_regions = new HSSFCellRangeAddress(in); /**
} * get the condition operator
* @return the condition operator
* @see org.apache.poi.hssf.util.HSSFDataValidation utility class
// --> start option flags */
/** public int getConditionOperator() {
* set the condition data type return opt_condition_operator.getValue(_option_flags);
* @param type - condition data type }
* @see org.apache.poi.hssf.util.HSSFDataValidation utility class // <-- end option flags
*/
public void setDataType(int type)
{
this.field_option_flags = this.opt_data_type.setValue(this.field_option_flags, type);
} public CellRangeAddressList getCellRangeAddress() {
return this._regions;
/** }
* get the condition data type
* @return the condition data type
* @see org.apache.poi.hssf.util.HSSFDataValidation utility class public String toString() {
*/ StringBuffer sb = new StringBuffer();
public int getDataType() sb.append("[DV]\n");
{ sb.append(" options=").append(Integer.toHexString(_option_flags));
return this.opt_data_type.getValue(this.field_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));
* set the condition error style sb.append("\n");
* @param type - condition error style appendFormula(sb, "Formula 1:", _formula1);
* @see org.apache.poi.hssf.util.HSSFDataValidation utility class appendFormula(sb, "Formula 2:", _formula2);
*/ sb.append("Regions: ");
public void setErrorStyle(int style) int nRegions = _regions.countRanges();
{ for(int i=0; i<nRegions; i++) {
this.field_option_flags = this.opt_error_style.setValue(this.field_option_flags, style); if (i>0) {
} sb.append(", ");
}
/** CellRangeAddress addr = _regions.getCellRangeAddress(i);
* get the condition error style sb.append('(').append(addr.getFirstRow()).append(',').append(addr.getLastRow());
* @return the condition error style sb.append(',').append(addr.getFirstColumn()).append(',').append(addr.getLastColumn()).append(')');
* @see org.apache.poi.hssf.util.HSSFDataValidation utility class }
*/ sb.append("\n");
public int getErrorStyle() sb.append("[/DV]");
{
return this.opt_error_style.getValue(this.field_option_flags); return sb.toString();
} }
/** private static String formatTextTitle(UnicodeString us) {
* set if in list validations the string list is explicitly given in the formula String str = us.getString();
* @param type - true if in list validations the string list is explicitly given in the formula; false otherwise if (str.length() == 1 && str.charAt(0) == '\0') {
* @see org.apache.poi.hssf.util.HSSFDataValidation utility class return "'\\0'";
*/ }
public void setListExplicitFormula(boolean explicit) return str;
{ }
this.field_option_flags = this.opt_string_list_formula.setBoolean(this.field_option_flags, explicit);
} private void appendFormula(StringBuffer sb, String label, Ptg[] ptgs) {
sb.append(label);
/** if (ptgs.length < 1) {
* return true if in list validations the string list is explicitly given in the formula, false otherwise sb.append("<empty>\n");
* @return true if in list validations the string list is explicitly given in the formula, false otherwise return;
* @see org.apache.poi.hssf.util.HSSFDataValidation utility class }
*/ sb.append("\n");
public boolean getListExplicitFormula() for (int i = 0; i < ptgs.length; i++) {
{ sb.append('\t').append(ptgs[i].toString()).append('\n');
return (this.opt_string_list_formula.isSet(this.field_option_flags)); }
} }
/** public int serialize(int offset, byte [] data) {
* set if empty values are allowed in cells int size = this.getRecordSize();
* @param type - true if empty values are allowed in cells, false otherwise LittleEndian.putShort(data, 0 + offset, sid);
* @see org.apache.poi.hssf.util.HSSFDataValidation utility class LittleEndian.putShort(data, 2 + offset, ( short ) (size-4));
*/
public void setEmptyCellAllowed(boolean allowed) int pos = 4;
{ LittleEndian.putInt(data, pos + offset, _option_flags);
this.field_option_flags = this.opt_empty_cell_allowed.setBoolean(this.field_option_flags, allowed); pos += 4;
}
pos += serializeUnicodeString(_promptTitle, pos+offset, data);
/** pos += serializeUnicodeString(_errorTitle, pos+offset, data);
* return true if empty values are allowed in cells, false otherwise pos += serializeUnicodeString(_promptText, pos+offset, data);
* @return if empty values are allowed in cells, false otherwise pos += serializeUnicodeString(_errorText, pos+offset, data);
* @see org.apache.poi.hssf.util.HSSFDataValidation utility class LittleEndian.putUShort(data, offset+pos, Ptg.getEncodedSize(_formula1));
*/ pos += 2;
public boolean getEmptyCellAllowed() LittleEndian.putUShort(data, offset+pos, _not_used_1);
{ pos += 2;
return (this.opt_empty_cell_allowed.isSet(this.field_option_flags));
} pos += Ptg.serializePtgs(_formula1, data, pos+offset);
/** LittleEndian.putUShort(data, offset+pos, Ptg.getEncodedSize(_formula2));
* set if drop down arrow should be surppressed when list validation is used pos += 2;
* @param type - true if drop down arrow should be surppressed when list validation is used, false otherwise LittleEndian.putShort(data, offset+pos, _not_used_2);
* @see org.apache.poi.hssf.util.HSSFDataValidation utility class pos += 2;
*/ pos += Ptg.serializePtgs(_formula2, data, pos+offset);
public void setSurppresDropdownArrow(boolean surppress) _regions.serialize(pos+offset, data);
{ return size;
this.field_option_flags = this.opt_surppres_dropdown_arrow.setBoolean(this.field_option_flags, surppress); }
}
/**
/** * When entered via the UI, Excel translates empty string into "\0"
* return true if drop down arrow should be surppressed when list validation is used, false otherwise * While it is possible to encode the title/text as empty string (Excel doesn't exactly crash),
* @return if drop down arrow should be surppressed when list validation is used, false otherwise * the resulting tool-tip text / message box looks wrong. It is best to do the same as the
* @see org.apache.poi.hssf.util.HSSFDataValidation utility class * Excel UI and encode 'not present' as "\0".
*/ */
public boolean getSurppresDropdownArrow() private static UnicodeString resolveTitleText(String str) {
{ if (str == null || str.length() < 1) {
return (this.opt_surppres_dropdown_arrow.isSet(this.field_option_flags)); return NULL_TEXT_STRING;
} }
return new UnicodeString(str);
/** }
* 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 private static UnicodeString readUnicodeString(RecordInputStream in) {
* @see org.apache.poi.hssf.util.HSSFDataValidation utility class return new UnicodeString(in);
*/ }
public void setShowPromptOnCellSelected(boolean show)
{ private static int serializeUnicodeString(UnicodeString us, int offset, byte[] data) {
this.field_option_flags = this.opt_show_prompt_on_cell_selected.setBoolean(this.field_option_flags, show); UnicodeRecordStats urs = new UnicodeRecordStats();
} us.serialize(urs, offset, data);
return urs.recordSize;
/** }
* return true if a prompt window should appear when cell is selected, false otherwise private static int getUnicodeStringSize(UnicodeString str) {
* @return if a prompt window should appear when cell is selected, false otherwise return 3 + str.getString().length();
* @see org.apache.poi.hssf.util.HSSFDataValidation utility class }
*/
public boolean getShowPromptOnCellSelected() public int getRecordSize() {
{ int size = 4+4+2+2+2+2;//header+options_field+first_formula_size+first_unused+sec_formula_size+sec+unused;
return (this.opt_show_prompt_on_cell_selected.isSet(this.field_option_flags)); size += getUnicodeStringSize(_promptTitle);
} size += getUnicodeStringSize(_errorTitle);
size += getUnicodeStringSize(_promptText);
/** size += getUnicodeStringSize(_errorText);
* set if an error window should appear when an invalid value is entered in the cell size += Ptg.getEncodedSize(_formula1);
* @param type - true if an error window should appear when an invalid value is entered in the cell, false otherwise size += Ptg.getEncodedSize(_formula2);
* @see org.apache.poi.hssf.util.HSSFDataValidation utility class size += _regions.getSize();
*/ return size;
public void setShowErrorOnInvalidValue(boolean show) }
{
this.field_option_flags = this.opt_show_error_on_invalid_value.setBoolean(this.field_option_flags, show); public short getSid() {
} return sid;
}
/**
* 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 * Clones the object. Uses serialisation, as the
* @see org.apache.poi.hssf.util.HSSFDataValidation utility class * contents are somewhat complex
*/ */
public boolean getShowErrorOnInvalidValue() public Object clone() {
{ return cloneViaReserialise();
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();
}
}
} }

View File

@ -1,4 +1,3 @@
/* ==================================================================== /* ====================================================================
Licensed to the Apache Software Foundation (ASF) under one or more Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with contributor license agreements. See the NOTICE file distributed with
@ -15,57 +14,43 @@
See the License for the specific language governing permissions and See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
==================================================================== */ ==================================================================== */
package org.apache.poi.hssf.record; package org.apache.poi.hssf.record;
import java.util.ArrayList; import org.apache.poi.ss.util.CellRangeAddress;
import java.util.Iterator; import org.apache.poi.ss.util.CellRangeAddressList;
import org.apache.poi.util.LittleEndian; import org.apache.poi.util.LittleEndian;
/** /**
* Title: Merged Cells Record * Title: Merged Cells Record (0x00E5)
* <br> * <br/>
* Description: Optional record defining a square area of cells to "merged" into * Description: Optional record defining a square area of cells to "merged" into
* one cell. <br> * one cell. <br>
* REFERENCE: NONE (UNDOCUMENTED PRESENTLY) <br> * REFERENCE: NONE (UNDOCUMENTED PRESENTLY) <br>
* @author Andrew C. Oliver (acoliver at apache dot org) * @author Andrew C. Oliver (acoliver at apache dot org)
* @version 2.0-pre * @version 2.0-pre
*/ */
public class MergeCellsRecord public final class MergeCellsRecord extends Record {
extends Record public final static short sid = 0x00E5;
{ private CellRangeAddressList _regions;
public final static short sid = 0xe5;
private ArrayList field_2_regions;
public MergeCellsRecord() /**
{ * Creates an empty <tt>MergedCellsRecord</tt>
*/
public MergeCellsRecord() {
_regions = new CellRangeAddressList();
} }
/** /**
* Constructs a MergedCellsRecord and sets its fields appropriately * Constructs a MergedCellsRecord and sets its fields appropriately
* @param in the RecordInputstream to read the record from * @param in the RecordInputstream to read the record from
*/ */
public MergeCellsRecord(RecordInputStream in) {
public MergeCellsRecord(RecordInputStream in)
{
super(in); super(in);
} }
protected void fillFields(RecordInputStream in) protected void fillFields(RecordInputStream in) {
{ _regions = new org.apache.poi.hssf.util.CellRangeAddressList(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);
}
} }
/** /**
@ -73,27 +58,8 @@ public class MergeCellsRecord
* ahead and delete the record. * ahead and delete the record.
* @return number of areas * @return number of areas
*/ */
public short getNumAreas() {
public short getNumAreas() return (short)_regions.countRanges();
{
//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)
{
} }
/** /**
@ -101,178 +67,84 @@ public class MergeCellsRecord
* be correct provided you do not add ahead of or remove ahead of it (in which case * 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) * you should increment or decrement appropriately....in other words its an arrayList)
* *
* @param rowfrom - the upper left hand corner's row * @param firstRow - the upper left hand corner's row
* @param colfrom - the upper left hand corner's col * @param firstCol - the upper left hand corner's col
* @param rowto - the lower right hand corner's row * @param lastRow - the lower right hand corner's row
* @param colto - the lower right hand corner's col * @param lastCol - the lower right hand corner's col
* @return new index of said area (don't depend on it if you add/remove) * @return new index of said area (don't depend on it if you add/remove)
*/ */
public void addArea(int firstRow, int firstCol, int lastRow, int lastCol) {
//public int addArea(short rowfrom, short colfrom, short rowto, short colto) _regions.addCellRangeAddress(firstRow, firstCol, lastRow, lastCol);
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;
} }
/** /**
* essentially unmerge the cells in the "area" stored at the passed in index * essentially unmerge the cells in the "area" stored at the passed in index
* @param area index * @param areaIndex
*/ */
public void removeAreaAt(int areaIndex) {
public void removeAreaAt(int area) _regions.remove(areaIndex);
{
field_2_regions.remove(area);
} }
/** /**
* return the MergedRegion at the given index. * @return MergedRegion at the given index representing the area that is Merged (r1,c1 - r2,c2)
*
* @return MergedRegion representing the area that is Merged (r1,c1 - r2,c2)
*/ */
public CellRangeAddress getAreaAt(int index) {
public MergedRegion getAreaAt(int index) return _regions.getCellRangeAddress(index);
{
return ( MergedRegion ) field_2_regions.get(index);
} }
public int getRecordSize() public int getRecordSize() {
{ return 4 + _regions.getSize();
int retValue;
retValue = 6 + (8 * field_2_regions.size());
return retValue;
} }
public short getSid() public short getSid() {
{
return sid; return sid;
} }
public int serialize(int offset, byte [] data) public int serialize(int offset, byte [] data) {
{ int dataSize = _regions.getSize();
int recordsize = getRecordSize();
int pos = 6;
LittleEndian.putShort(data, offset + 0, sid); LittleEndian.putShort(data, offset + 0, sid);
LittleEndian.putShort(data, offset + 2, ( short ) (recordsize - 4)); LittleEndian.putUShort(data, offset + 2, dataSize);
LittleEndian.putShort(data, offset + 4, getNumAreas()); _regions.serialize(offset + 4, data);
for (int k = 0; k < getNumAreas(); k++) return 4 + dataSize;
{
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;
} }
public String toString() public String toString() {
{
StringBuffer retval = new StringBuffer(); StringBuffer retval = new StringBuffer();
retval.append("[MERGEDCELLS]").append("\n"); retval.append("[MERGEDCELLS]").append("\n");
retval.append(" .sid =").append(sid).append("\n"); retval.append(" .sid =").append(sid).append("\n");
retval.append(" .numregions =").append(getNumAreas()) retval.append(" .numregions =").append(getNumAreas())
.append("\n"); .append("\n");
for (int k = 0; k < getNumAreas(); k++) for (int k = 0; k < _regions.countRanges(); k++) {
{ CellRangeAddress region = _regions.getCellRangeAddress(k);
MergedRegion region = ( MergedRegion ) field_2_regions.get(k);
retval.append(" .rowfrom =").append(region.row_from) retval.append(" .rowfrom =").append(region.getFirstRow())
.append("\n"); .append("\n");
retval.append(" .colfrom =").append(region.col_from) retval.append(" .colfrom =").append(region.getFirstColumn())
.append("\n"); .append("\n");
retval.append(" .rowto =").append(region.row_to) retval.append(" .rowto =").append(region.getLastRow())
.append("\n"); .append("\n");
retval.append(" .colto =").append(region.col_to) retval.append(" .colto =").append(region.getLastColumn())
.append("\n"); .append("\n");
} }
retval.append("[MERGEDCELLS]").append("\n"); retval.append("[MERGEDCELLS]").append("\n");
return retval.toString(); return retval.toString();
} }
protected void validateSid(short id) protected void validateSid(short id) {
{ if (id != sid) {
if (id != sid)
{
throw new RecordFormatException("NOT A MERGEDCELLS RECORD!! " throw new RecordFormatException("NOT A MERGEDCELLS RECORD!! "
+ id); + 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() { public Object clone() {
MergeCellsRecord rec = new MergeCellsRecord(); MergeCellsRecord rec = new MergeCellsRecord();
rec.field_2_regions = new ArrayList(); for (int k = 0; k < _regions.countRanges(); k++) {
Iterator iterator = field_2_regions.iterator(); CellRangeAddress oldRegion = _regions.getCellRangeAddress(k);
while (iterator.hasNext()) { rec.addArea(oldRegion.getFirstRow(), oldRegion.getFirstColumn(),
MergedRegion oldRegion = (MergedRegion)iterator.next(); oldRegion.getLastRow(), oldRegion.getLastColumn());
rec.addArea(oldRegion.row_from, oldRegion.col_from, oldRegion.row_to, oldRegion.col_to);
} }
return rec; return rec;

View File

@ -1,4 +1,3 @@
/* ==================================================================== /* ====================================================================
Licensed to the Apache Software Foundation (ASF) under one or more Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with contributor license agreements. See the NOTICE file distributed with
@ -15,70 +14,80 @@
See the License for the specific language governing permissions and See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
==================================================================== */ ==================================================================== */
package org.apache.poi.hssf.record; package org.apache.poi.hssf.record;
import java.util.*;
import org.apache.poi.util.LittleEndian; import org.apache.poi.util.LittleEndian;
/** /**
* Title: Selection Record<P> * Title: Selection Record (0x001D)<P>
* Description: shows the user's selection on the sheet * Description: shows the user's selection on the sheet
* for write set num refs to 0<P> * 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> * 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 Andrew C. Oliver (acoliver at apache dot org)
* @author Jason Height (jheight at chariot dot net dot au) * @author Jason Height (jheight at chariot dot net dot au)
* @author Glen Stampoultzis (glens at apache.org) * @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 * Note - column values are 8-bit so cannot use <tt>CellRangeAddressList</tt>
{ */
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
public class Reference { public class Reference {
private short field_1_first_row; /* package */ static final int ENCODED_SIZE = 6;
private short field_2_last_row; private int _firstRow;
private byte field_3_first_column; private int _lastRow;
private byte field_4_last_column; private int _firstCol;
private int _lastCol;
Reference(RecordInputStream in) { /* package */ Reference(int firstRow, int lastRow, int firstColumn, int lastColumn) {
field_1_first_row = in.readShort(); _firstRow = firstRow;
field_2_last_row = in.readShort(); _lastRow = lastRow;
field_3_first_column = in.readByte(); _firstCol = firstColumn;
field_4_last_column = in.readByte(); _lastCol = lastColumn;
} }
/* package */ Reference(RecordInputStream in) {
public short getFirstRow() { this(in.readUShort(), in.readUShort(), in.readUByte(), in.readUByte());
return field_1_first_row; }
} public void serialize(int offset, byte[] data) {
LittleEndian.putUShort(data, offset + 0, _firstRow);
public short getLastRow() { LittleEndian.putUShort(data, offset + 2, _lastRow);
return field_2_last_row; LittleEndian.putByte(data, offset + 4, _firstCol);
} LittleEndian.putByte(data, offset + 6, _lastCol);
}
public byte getFirstColumn() {
return field_3_first_column; public int getFirstRow() {
} return _firstRow;
}
public byte getLastColumn() { public int getLastRow() {
return field_4_last_column; 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 * @param in the RecordInputstream to read the record from
*/ */
public SelectionRecord(RecordInputStream in) public SelectionRecord(RecordInputStream in) {
{
super(in); super(in);
} }
protected void validateSid(short id) protected void validateSid(short id) {
{ if (id != sid) {
if (id != sid)
{
throw new RecordFormatException("NOT A valid Selection RECORD"); throw new RecordFormatException("NOT A valid Selection RECORD");
} }
} }
protected void fillFields(RecordInputStream in) protected void fillFields(RecordInputStream in) {
{
field_1_pane = in.readByte(); field_1_pane = in.readByte();
//field_2_row_active_cell = LittleEndian.getShort(data, 1 + offset);
field_2_row_active_cell = in.readUShort(); field_2_row_active_cell = in.readUShort();
field_3_col_active_cell = in.readShort(); field_3_col_active_cell = in.readShort();
field_4_ref_active_cell = in.readShort(); field_4_active_cell_ref_index = in.readShort();
field_5_num_refs = in.readShort(); int field_5_num_refs = in.readUShort();
field_6_refs = new ArrayList(field_5_num_refs); field_6_refs = new Reference[field_5_num_refs];
for (int i=0; i<field_5_num_refs; i++) { for (int i = 0; i < field_6_refs.length; i++) {
field_6_refs.add(new Reference(in)); field_6_refs[i] = new Reference(in);
} }
} }
/** /**
* set which window pane this is for * set which window pane this is for
* @param pane
*/ */
public void setPane(byte pane) {
public void setPane(byte pane)
{
field_1_pane = pane; field_1_pane = pane;
} }
@ -128,10 +129,7 @@ public class SelectionRecord
* set the active cell's row * set the active cell's row
* @param row number of active cell * @param row number of active cell
*/ */
public void setActiveCellRow(int row) {
//public void setActiveCellRow(short row)
public void setActiveCellRow(int row)
{
field_2_row_active_cell = row; field_2_row_active_cell = row;
} }
@ -139,9 +137,7 @@ public class SelectionRecord
* set the active cell's col * set the active cell's col
* @param col number of active cell * @param col number of active cell
*/ */
public void setActiveCellCol(short col) {
public void setActiveCellCol(short col)
{
field_3_col_active_cell = col; field_3_col_active_cell = col;
} }
@ -149,29 +145,14 @@ public class SelectionRecord
* set the active cell's reference number * set the active cell's reference number
* @param ref number of active cell * @param ref number of active cell
*/ */
public void setActiveCellRef(short ref) {
public void setActiveCellRef(short ref) field_4_active_cell_ref_index = ref;
{
field_4_ref_active_cell = ref;
} }
/** /**
* set the number of cell refs (we don't support selection so set to 0 * @return the pane ID which window pane this is for
* @param refs - number of references
*/ */
public byte getPane() {
public void setNumRefs(short refs)
{
field_5_num_refs = refs;
}
/**
* get which window pane this is for
* @return pane
*/
public byte getPane()
{
return field_1_pane; return field_1_pane;
} }
@ -179,10 +160,7 @@ public class SelectionRecord
* get the active cell's row * get the active cell's row
* @return row number of active cell * @return row number of active cell
*/ */
public int getActiveCellRow() {
//public short getActiveCellRow()
public int getActiveCellRow()
{
return field_2_row_active_cell; return field_2_row_active_cell;
} }
@ -190,9 +168,7 @@ public class SelectionRecord
* get the active cell's col * get the active cell's col
* @return col number of active cell * @return col number of active cell
*/ */
public int getActiveCellCol() {
public short getActiveCellCol()
{
return field_3_col_active_cell; return field_3_col_active_cell;
} }
@ -200,20 +176,8 @@ public class SelectionRecord
* get the active cell's reference number * get the active cell's reference number
* @return ref number of active cell * @return ref number of active cell
*/ */
public int getActiveCellRef() {
public short getActiveCellRef() return (short)field_4_active_cell_ref_index;
{
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 String toString() public String toString()
@ -230,47 +194,44 @@ public class SelectionRecord
buffer.append(" .activecellref = ") buffer.append(" .activecellref = ")
.append(Integer.toHexString(getActiveCellRef())).append("\n"); .append(Integer.toHexString(getActiveCellRef())).append("\n");
buffer.append(" .numrefs = ") buffer.append(" .numrefs = ")
.append(Integer.toHexString(getNumRefs())).append("\n"); .append(Integer.toHexString(field_6_refs.length)).append("\n");
buffer.append("[/SELECTION]\n"); buffer.append("[/SELECTION]\n");
return buffer.toString(); return buffer.toString();
} }
private int getDataSize() {
//hacked to provide one cell reference to 0,0 - 0,0 return 9 // 1 byte + 4 shorts
public int serialize(int offset, byte [] data) + field_6_refs.length * Reference.ENCODED_SIZE;
{ }
LittleEndian.putShort(data, 0 + offset, sid); public int serialize(int offset, byte [] data) {
LittleEndian.putShort(data, 2 + offset, ( short ) 15); int dataSize = getDataSize();
data[ 4 + offset ] = getPane(); LittleEndian.putUShort(data, 0 + offset, sid);
//LittleEndian.putShort(data, 5 + offset, getActiveCellRow()); LittleEndian.putUShort(data, 2 + offset, dataSize);
LittleEndian.putShort(data, 5 + offset, ( short ) getActiveCellRow()); LittleEndian.putByte(data, 4 + offset, getPane());
LittleEndian.putShort(data, 7 + offset, getActiveCellCol()); LittleEndian.putUShort(data, 5 + offset, getActiveCellRow());
LittleEndian.putShort(data, 9 + offset, getActiveCellRef()); LittleEndian.putUShort(data, 7 + offset, getActiveCellCol());
LittleEndian.putShort(data, 11 + offset, ( short ) 1); LittleEndian.putUShort(data, 9 + offset, getActiveCellRef());
LittleEndian.putShort(data, 13 + offset, ( short ) getActiveCellRow()); int nRefs = field_6_refs.length;
LittleEndian.putShort(data, 15 + offset, ( short ) getActiveCellRow()); LittleEndian.putUShort(data, 11 + offset, nRefs);
data[ 17 + offset ] = (byte)getActiveCellCol(); for (int i = 0; i < field_6_refs.length; i++) {
data[ 18 + offset ] = (byte)getActiveCellCol(); Reference r = field_6_refs[i];
return getRecordSize(); r.serialize(offset + 13 + i * Reference.ENCODED_SIZE, data);
}
return 4 + dataSize;
} }
public int getRecordSize() public int getRecordSize() {
{ return 4 + getDataSize();
return 19;
} }
public short getSid() public short getSid() {
{
return sid; return sid;
} }
public Object clone() { public Object clone() {
SelectionRecord rec = new SelectionRecord(); SelectionRecord rec = new SelectionRecord(field_2_row_active_cell, field_3_col_active_cell);
rec.field_1_pane = field_1_pane; rec.field_1_pane = field_1_pane;
rec.field_2_row_active_cell = field_2_row_active_cell; rec.field_4_active_cell_ref_index = field_4_active_cell_ref_index;
rec.field_3_col_active_cell = field_3_col_active_cell; rec.field_6_refs = field_6_refs;
rec.field_4_ref_active_cell = field_4_ref_active_cell; return rec;
rec.field_5_num_refs = field_5_num_refs;
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.Record;
import org.apache.poi.hssf.record.RecordInputStream; import org.apache.poi.hssf.record.RecordInputStream;
import org.apache.poi.ss.util.Region; 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.POILogFactory;
import org.apache.poi.util.POILogger; 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); 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 */ /** Create a NumberPtg from a byte array read from disk */
public NumberPtg(RecordInputStream in) public NumberPtg(RecordInputStream in)
{ {
field_1_value = in.readDouble(); this(in.readDouble());
} }
/** Create a NumberPtg from a string representation of the number /** 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 * @param value : String representation of a floating point number
*/ */
public NumberPtg(String value) { 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() public double getValue()
{ {
@ -67,6 +70,15 @@ public final class NumberPtg extends ScalarConstantPtg {
public String toFormulaString(Workbook book) 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) * @author Jason Height (jheight at chariot dot net dot au)
*/ */
public abstract class Ptg implements Cloneable { public abstract class Ptg implements Cloneable {
public static final Ptg[] EMPTY_PTG_ARRAY = { };
/* convert infix order ptg list to rpn order ptg list /* convert infix order ptg list to rpn order ptg list
* @return List ptgs in RPN order * @return List ptgs in RPN order
@ -250,6 +251,9 @@ public abstract class Ptg implements Cloneable {
} }
} }
private static Ptg[] toPtgArray(List l) { private static Ptg[] toPtgArray(List l) {
if (l.isEmpty()) {
return EMPTY_PTG_ARRAY;
}
Ptg[] result = new Ptg[l.size()]; Ptg[] result = new Ptg[l.size()];
l.toArray(result); l.toArray(result);
return 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.FormulaParser;
import org.apache.poi.hssf.model.Sheet; import org.apache.poi.hssf.model.Sheet;
import org.apache.poi.hssf.model.Workbook; 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.aggregates.FormulaRecordAggregate;
import org.apache.poi.hssf.record.formula.Ptg; import org.apache.poi.hssf.record.formula.Ptg;
import org.apache.poi.ss.usermodel.Cell; 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 * @param comment comment associated with this cell
*/ */
public void setCellComment(Comment comment){ public void setCellComment(Comment comment){
if(comment == null) {
removeCellComment();
return;
}
this.comment = (HSSFComment) comment; this.comment = (HSSFComment) comment;
this.comment.setRow((short)record.getRow()); this.comment.setRow((short)record.getRow());
this.comment.setColumn(record.getColumn()); this.comment.setColumn(record.getColumn());
@ -1076,6 +1100,49 @@ public class HSSFCell implements Cell
} }
return comment; 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. * Cell comment finder.

View File

@ -156,4 +156,13 @@ public class HSSFComment extends HSSFTextbox implements Comment {
} }
super.setString(string); 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; 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.CFRuleRecord;
import org.apache.poi.hssf.record.aggregates.CFRecordsAggregate; import org.apache.poi.hssf.record.aggregates.CFRecordsAggregate;
import org.apache.poi.hssf.record.cf.CellRange; import org.apache.poi.ss.util.CellRangeAddress;
import org.apache.poi.hssf.util.Region; import org.apache.poi.ss.util.Region;
/** /**
* HSSFConditionalFormatting class encapsulates all settings of Conditional Formatting. * 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() public Region[] getFormattingRegions()
{ {
CFHeaderRecord cfh = cfAggregate.getHeader(); CellRangeAddress[] cellRanges = getFormattingRanges();
CellRange[] cellRanges = cfh.getCellRanges(); return Region.convertCellRangesToRegions(cellRanges);
return CellRange.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.ArrayList;
import java.util.Iterator; import java.util.Iterator;
import java.util.List; import java.util.List;
import java.util.Stack;
import java.util.TreeMap; import java.util.TreeMap;
import org.apache.poi.ddf.EscherRecord; 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.VCenterRecord;
import org.apache.poi.hssf.record.WSBoolRecord; import org.apache.poi.hssf.record.WSBoolRecord;
import org.apache.poi.hssf.record.WindowTwoRecord; 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.Ptg;
import org.apache.poi.hssf.record.formula.RefPtg; 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.hssf.util.PaneInformation;
import org.apache.poi.ss.usermodel.CellStyle; import org.apache.poi.ss.usermodel.CellStyle;
import org.apache.poi.ss.usermodel.Row; 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.hssf.util.Region;
import org.apache.poi.util.POILogFactory; import org.apache.poi.util.POILogFactory;
import org.apache.poi.util.POILogger; 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 * 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) public void addValidationData(HSSFDataValidation dataValidation) {
{ if (dataValidation == null) {
if ( obj_validation == null ) throw new IllegalArgumentException("objValidation must not be null");
{
return;
} }
DVALRecord dvalRec = (DVALRecord)sheet.findFirstRecordBySid( DVALRecord.sid ); DataValidityTable dvt = sheet.getOrCreateDataValidityTable();
int eofLoc = sheet.findFirstRecordLocBySid( EOFRecord.sid );
if ( dvalRec == null )
{
dvalRec = new DVALRecord();
sheet.getRecords().add( eofLoc, dvalRec );
}
int curr_dvRecNo = dvalRec.getDVRecNo();
dvalRec.setDVRecNo(curr_dvRecNo+1);
//create dv record DVRecord dvRecord = dataValidation.createDVRecord(workbook);
DVRecord dvRecord = new DVRecord(); dvt.addDataValidation(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 );
} }
/** /**
* Get the visibility state for a given column. * Get the visibility state for a given column.
* @param column - the column to get (0-based) * @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) * @deprecated (Aug-2008) use <tt>CellRangeAddress</tt> instead of <tt>Region</tt>
* @param region (rowfrom/colfrom-rowto/colto) to merge
* @return index of this region
*/ */
public int addMergedRegion(org.apache.poi.ss.util.Region region) public int addMergedRegion(org.apache.poi.ss.util.Region region)
{ {
//return sheet.addMergedRegion((short) region.getRowFrom(),
return sheet.addMergedRegion( region.getRowFrom(), return sheet.addMergedRegion( region.getRowFrom(),
region.getColumnFrom(), region.getColumnFrom(),
//(short) region.getRowTo(), //(short) region.getRowTo(),
region.getRowTo(), region.getRowTo(),
region.getColumnTo()); 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 * 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 * TODO: Boolean not needed, remove after next release
* @deprecated use getVerticallyCenter() instead * @deprecated (Mar-2008) use getVerticallyCenter() instead
*/ */
public boolean getVerticallyCenter(boolean value) { public boolean getVerticallyCenter(boolean value) {
return getVerticallyCenter(); return getVerticallyCenter();
@ -724,14 +659,19 @@ public class HSSFSheet implements org.apache.poi.ss.usermodel.Sheet
} }
/** /**
* gets the region at a particular index * @deprecated (Aug-2008) use {@link HSSFSheet#getMergedRegion(int)}
* @param index of the region to fetch
* @return the merged region (simple eh?)
*/ */
public Region getMergedRegionAt(int index) {
public Region getMergedRegionAt(int index) CellRangeAddress cra = getMergedRegion(index);
{
return new Region(sheet.getMergedRegionAt(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) { protected void shiftMerged(int startRow, int endRow, int n, boolean isRow) {
List shiftedRegions = new ArrayList(); List shiftedRegions = new ArrayList();
//move merged regions completely if they fall within the new region boundaries when they are shifted //move merged regions completely if they fall within the new region boundaries when they are shifted
for (int i = 0; i < this.getNumMergedRegions(); i++) { for (int i = 0; i < getNumMergedRegions(); i++) {
Region merged = this.getMergedRegionAt(i); CellRangeAddress merged = getMergedRegion(i);
boolean inStart = (merged.getRowFrom() >= startRow || merged.getRowTo() >= startRow); boolean inStart= (merged.getFirstRow() >= startRow || merged.getLastRow() >= startRow);
boolean inEnd = (merged.getRowTo() <= endRow || merged.getRowFrom() <= endRow); boolean inEnd = (merged.getFirstRow() <= endRow || merged.getLastRow() <= endRow);
//dont check if it's not within the shifted area //don't check if it's not within the shifted area
if (! (inStart && inEnd)) continue; if (!inStart || !inEnd) {
continue;
}
//only shift if the region outside the shifted rows is not merged too //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)){ if (!containsCell(merged, startRow-1, 0) && !containsCell(merged, endRow+1, 0)){
merged.setRowFrom(merged.getRowFrom()+n); merged.setFirstRow(merged.getFirstRow()+n);
merged.setRowTo(merged.getRowTo()+n); merged.setLastRow(merged.getLastRow()+n);
//have to remove/add it back //have to remove/add it back
shiftedRegions.add(merged); shiftedRegions.add(merged);
this.removeMergedRegion(i); removeMergedRegion(i);
i = i -1; // we have to back up now since we removed one 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(); Iterator iterator = shiftedRegions.iterator();
while (iterator.hasNext()) { while (iterator.hasNext()) {
Region region = (Region)iterator.next(); CellRangeAddress region = (CellRangeAddress)iterator.next();
this.addMergedRegion(region); 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(); HSSFRow row = (HSSFRow) it.next();
HSSFCell cell = row.getCell(column); HSSFCell cell = row.getCell(column);
if (cell == null) continue; if (cell == null) {
continue;
}
int colspan = 1; int colspan = 1;
for (int i = 0 ; i < getNumMergedRegions(); i++) { 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 (!useMergedCells) {
// If we're not using merged cells, skip this one and move on to the next. // If we're not using merged cells, skip this one and move on to the next.
continue rows; continue rows;
} }
cell = row.getCell(getMergedRegionAt(i).getColumnFrom()); cell = row.getCell(region.getFirstColumn());
colspan = 1+ getMergedRegionAt(i).getColumnTo() - getMergedRegionAt(i).getColumnFrom(); 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.CFRuleRecord;
import org.apache.poi.hssf.record.aggregates.CFRecordsAggregate; import org.apache.poi.hssf.record.aggregates.CFRecordsAggregate;
import org.apache.poi.ss.util.Region; import org.apache.poi.ss.util.Region;
import org.apache.poi.ss.util.CellRangeAddress;
/** /**
* The 'Conditional Formatting' facet of <tt>HSSFSheet</tt> * The 'Conditional Formatting' facet of <tt>HSSFSheet</tt>
@ -100,7 +101,12 @@ public final class HSSFSheetConditionalFormatting {
return _sheet.addConditionalFormatting(cfraClone); 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. * 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 * @return index of the newly created Conditional Formatting object
*/ */
public int addConditionalFormatting(CellRangeAddress[] regions, HSSFConditionalFormattingRule[] cfRules) {
public int addConditionalFormatting(Region[] regions, HSSFConditionalFormattingRule[] cfRules) {
if (regions == null) { if (regions == null) {
throw new IllegalArgumentException("regions must not be null"); throw new IllegalArgumentException("regions must not be null");
} }
@ -132,7 +137,7 @@ public final class HSSFSheetConditionalFormatting {
return _sheet.addConditionalFormatting(cfra); return _sheet.addConditionalFormatting(cfra);
} }
public int addConditionalFormatting(Region[] regions, public int addConditionalFormatting(CellRangeAddress[] regions,
HSSFConditionalFormattingRule rule1) HSSFConditionalFormattingRule rule1)
{ {
return addConditionalFormatting(regions, return addConditionalFormatting(regions,
@ -142,7 +147,7 @@ public final class HSSFSheetConditionalFormatting {
}); });
} }
public int addConditionalFormatting(Region[] regions, public int addConditionalFormatting(CellRangeAddress[] regions,
HSSFConditionalFormattingRule rule1, HSSFConditionalFormattingRule rule1,
HSSFConditionalFormattingRule rule2) 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 * 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; 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 * 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 * 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); 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) { public Region(String ref) {
super(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; package org.apache.poi.ss.usermodel;
import java.util.Calendar; import java.util.Calendar;
import java.util.Date; import java.util.Date;
import java.util.GregorianCalendar; import java.util.GregorianCalendar;
import java.util.regex.Pattern;
/** /**
* Contains methods for dealing with Excel dates. * Contains methods for dealing with Excel dates.
@ -38,17 +33,20 @@ import java.util.GregorianCalendar;
* @author Alex Jacoby (ajacoby at gmail.com) * @author Alex Jacoby (ajacoby at gmail.com)
* @author Pavel Krupets (pkrupets at palmtreebusiness dot com) * @author Pavel Krupets (pkrupets at palmtreebusiness dot com)
*/ */
public class DateUtil {
public class DateUtil protected DateUtil() {
{ // no instances of this class
protected DateUtil()
{
} }
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, * 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. * 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 * @param date the Date
*/ */
public static double getExcelDate(Date 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, * 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 * Given a Date in the form of a Calendar, converts it into a double
* representing its internal Excel representation, which is the * representing its internal Excel representation, which is the
* number of days since 1/1/1900. Fractional days represent hours, * number of days since 1/1/1900. Fractional days represent hours,
* minutes, and seconds. * minutes, and seconds.
* *
* @return Excel representation of Date (-1 if error - test for error by checking for less than 0.1) * @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? * @param use1904windowing Should 1900 or 1904 date windowing be used?
*/ */
public static double getExcelDate(Calendar date, boolean use1904windowing) { public static double getExcelDate(Calendar date, boolean use1904windowing) {
// Don't alter the supplied Calendar as we do our work // Don't alter the supplied Calendar as we do our work
return internalGetExcelDate( (Calendar)date.clone(), use1904windowing ); return internalGetExcelDate( (Calendar)date.clone(), use1904windowing );
} }
private static double internalGetExcelDate(Calendar date, boolean use1904windowing) { private static double internalGetExcelDate(Calendar date, boolean use1904windowing) {
if ((!use1904windowing && date.get(Calendar.YEAR) < 1900) || if ((!use1904windowing && date.get(Calendar.YEAR) < 1900) ||
(use1904windowing && date.get(Calendar.YEAR) < 1904)) (use1904windowing && date.get(Calendar.YEAR) < 1904))
{ {
return BAD_DATE; 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 * Given an Excel date with using 1900 date windowing, and
* converts it to a java.util.Date. * 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 * <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 * 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 * 02:00 and 03:00 then it is converted to past 03:00 summer time
* *
* @param date The Excel date. * @param date The Excel date.
* @return Java representation of the date, or null if date is not a valid Excel date * @return Java representation of the date, or null if date is not a valid Excel date
* @see java.util.TimeZone * @see java.util.TimeZone
*/ */
public static Date getJavaDate(double date) { 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, * Given an Excel date with either 1900 or 1904 date windowing,
@ -158,95 +155,90 @@ public class DateUtil
* @see java.util.TimeZone * @see java.util.TimeZone
*/ */
public static Date getJavaDate(double date, boolean use1904windowing) { public static Date getJavaDate(double date, boolean use1904windowing) {
if (isValidExcelDate(date)) { 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 {
return null; 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 * Given a format ID and its format String, will check to see if the
* format represents a date format or not. * format represents a date format or not.
* Firstly, it will check to see if the format ID corresponds to an * 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 * If not, it will check to see if the format string only contains
* date formatting characters (ymd-/), which covers most * date formatting characters (ymd-/), which covers most
* non US date formats. * non US date formats.
* *
* @param formatIndex The index of the format, eg from ExtendedFormatRecord.getFormatIndex * @param formatIndex The index of the format, eg from ExtendedFormatRecord.getFormatIndex
* @param formatString The format string, eg from FormatRecord.getFormatString * @param formatString The format string, eg from FormatRecord.getFormatString
* @see #isInternalDateFormat(int) * @see #isInternalDateFormat(int)
*/ */
public static boolean isADateFormat(int formatIndex, String formatString) { public static boolean isADateFormat(int formatIndex, String formatString) {
// First up, is this an internal date format? // First up, is this an internal date format?
if(isInternalDateFormat(formatIndex)) { if(isInternalDateFormat(formatIndex)) {
return true; return true;
} }
// If we didn't get a real string, it can't be // If we didn't get a real string, it can't be
if(formatString == null || formatString.length() == 0) { if(formatString == null || formatString.length() == 0) {
return false; return false;
} }
String fs = formatString; String fs = formatString;
// Translate \- into just -, before matching // Translate \- into just -, before matching
fs = fs.replaceAll("\\\\-","-"); fs = fs.replaceAll("\\\\-","-");
// And \, into , // And \, into ,
fs = fs.replaceAll("\\\\,",","); fs = fs.replaceAll("\\\\,",",");
// And '\ ' into ' ' // And '\ ' into ' '
fs = fs.replaceAll("\\\\ "," "); fs = fs.replaceAll("\\\\ "," ");
// If it end in ;@, that's some crazy dd/mm vs mm/dd // If it end in ;@, that's some crazy dd/mm vs mm/dd
// switching stuff, which we can ignore // switching stuff, which we can ignore
fs = fs.replaceAll(";@", ""); fs = fs.replaceAll(";@", "");
// If it starts with [$-...], then could be a date, but // If it starts with [$-...], then could be a date, but
// who knows what that starting bit is all about // who knows what that starting bit is all about
fs = fs.replaceAll("^\\[\\$\\-.*?\\]", ""); fs = fs.replaceAll("^\\[\\$\\-.*?\\]", "");
// If it starts with something like [Black] or [Yellow], // If it starts with something like [Black] or [Yellow],
// then it could be a date // then it could be a date
fs = fs.replaceAll("^\\[[a-zA-Z]+\\]", ""); fs = fs.replaceAll("^\\[[a-zA-Z]+\\]", "");
// Otherwise, check it's only made up, in any case, of: // Otherwise, check it's only made up, in any case, of:
// y m d h s - / , . : // y m d h s - / , . :
// optionally followed by AM/PM // optionally followed by AM/PM
// optionally followed by AM/PM if(fs.matches("^[yYmMdDhHsS\\-/,. :]+[ampAMP/]*$")) {
if(fs.matches("^[yYmMdDhHsS\\-/,. :]+[ampAMP/]*$")) { return true;
return true; }
}
return false;
return false;
} }
/** /**
* Given a format ID this will check whether the format represents * Given a format ID this will check whether the format represents
* an internal excel date format or not. * 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) { public static boolean isInternalDateFormat(int format) {
boolean retval =false;
switch(format) { switch(format) {
// Internal Date Formats as described on page 427 in // Internal Date Formats as described on page 427 in
// Microsoft Excel Dev's Kit... // Microsoft Excel Dev's Kit...
@ -262,32 +254,25 @@ public class DateUtil
case 0x2d: case 0x2d:
case 0x2e: case 0x2e:
case 0x2f: case 0x2f:
retval = true; return true;
break;
default:
retval = false;
break;
} }
return retval; return false;
} }
/** /**
* Check if a cell contains a date * Check if a cell contains a date
* Since dates are stored internally in Excel as double values * Since dates are stored internally in Excel as double values
* we infer it is a date if it is formatted as such. * we infer it is a date if it is formatted as such.
* @see #isADateFormat(int, String) * @see #isADateFormat(int, String)
* @see #isInternalDateFormat(int) * @see #isInternalDateFormat(int)
*/ */
public static boolean isCellDateFormatted(Cell cell) { public static boolean isCellDateFormatted(Cell cell) {
if (cell == null) return false; if (cell == null) return false;
boolean bDate = false; boolean bDate = false;
double d = cell.getNumericCellValue(); double d = cell.getNumericCellValue();
if ( DateUtil.isValidExcelDate(d) ) { if ( DateUtil.isValidExcelDate(d) ) {
CellStyle style = cell.getCellStyle(); CellStyle style = cell.getCellStyle();
if(style == null) return false;
int i = style.getDataFormat(); int i = style.getDataFormat();
String f = style.getDataFormatString(); String f = style.getDataFormatString();
bDate = isADateFormat(i, f); bDate = isADateFormat(i, f);
@ -305,7 +290,7 @@ public class DateUtil
public static boolean isCellInternalDateFormatted(Cell cell) { public static boolean isCellInternalDateFormatted(Cell cell) {
if (cell == null) return false; if (cell == null) return false;
boolean bDate = false; boolean bDate = false;
double d = cell.getNumericCellValue(); double d = cell.getNumericCellValue();
if ( DateUtil.isValidExcelDate(d) ) { if ( DateUtil.isValidExcelDate(d) ) {
CellStyle style = cell.getCellStyle(); CellStyle style = cell.getCellStyle();
@ -335,7 +320,6 @@ public class DateUtil
* @param cal the Calendar * @param cal the Calendar
* @exception IllegalArgumentException if date is invalid * @exception IllegalArgumentException if date is invalid
*/ */
protected static int absoluteDay(Calendar cal, boolean use1904windowing) protected static int absoluteDay(Calendar cal, boolean use1904windowing)
{ {
return cal.get(Calendar.DAY_OF_YEAR) return cal.get(Calendar.DAY_OF_YEAR)
@ -347,7 +331,7 @@ public class DateUtil
* *
* @return days number of days in years prior to yr. * @return days number of days in years prior to yr.
* @param yr a year (1900 < yr < 4000) * @param yr a year (1900 < yr < 4000)
* @param use1904windowing * @param use1904windowing
* @exception IllegalArgumentException if year is outside of range. * @exception IllegalArgumentException if year is outside of range.
*/ */
@ -356,16 +340,16 @@ public class DateUtil
if ((!use1904windowing && yr < 1900) || (use1904windowing && yr < 1900)) { if ((!use1904windowing && yr < 1900) || (use1904windowing && yr < 1900)) {
throw new IllegalArgumentException("'year' must be 1900 or greater"); throw new IllegalArgumentException("'year' must be 1900 or greater");
} }
int yr1 = yr - 1; int yr1 = yr - 1;
int leapDays = yr1 / 4 // plus julian leap days in prior years int leapDays = yr1 / 4 // plus julian leap days in prior years
- yr1 / 100 // minus prior century 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 - 460; // leap days in previous 1900 years
return 365 * (yr - (use1904windowing ? 1904 : 1900)) + leapDays; return 365 * (yr - (use1904windowing ? 1904 : 1900)) + leapDays;
} }
// set HH:MM:SS fields of cal to 00:00:00:000 // set HH:MM:SS fields of cal to 00:00:00:000
private static Calendar dayStart(final Calendar cal) private static Calendar dayStart(final Calendar cal)
{ {
@ -380,5 +364,95 @@ public class DateUtil
return cal; 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. limitations under the License.
==================================================================== */ ==================================================================== */
package org.apache.poi.ss.util; 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 * 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. * to represent a string of characters. Its really only useful for HSSF though.
* *
* @author Andrew C. Oliver acoliver at apache dot org * @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 int rowFrom;
private short colFrom; private short colFrom;
private int rowTo; private int rowTo;
@ -52,16 +48,6 @@ public class Region
this.colTo = colTo; 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) { public Region(String ref) {
CellReference cellReferenceFrom = new CellReference(ref.substring(0, ref.indexOf(":"))); CellReference cellReferenceFrom = new CellReference(ref.substring(0, ref.indexOf(":")));
CellReference cellReferenceTo = new CellReference(ref.substring(ref.indexOf(":") + 1)); CellReference cellReferenceTo = new CellReference(ref.substring(ref.indexOf(":") + 1));
@ -71,7 +57,8 @@ public class Region
this.colTo = (short) cellReferenceTo.getCol(); this.colTo = (short) cellReferenceTo.getCol();
} }
/**
/**
* get the upper left hand corner column number * get the upper left hand corner column number
* *
* @return column number for the upper left hand corner * @return column number for the upper left hand corner
@ -159,6 +146,7 @@ public class Region
this.rowTo = rowTo; this.rowTo = rowTo;
} }
/** /**
* Answers: "is the row/column inside this range?" * Answers: "is the row/column inside this range?"
* *
@ -218,16 +206,51 @@ public class Region
return compareTo(( Region ) o); 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 * @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.util.Iterator;
import java.awt.*; import java.awt.*;
import org.apache.poi.hslf.record.SlideAtom; import org.apache.poi.hslf.record.*;
import org.apache.poi.hslf.record.TextHeaderAtom;
import org.apache.poi.hslf.record.ColorSchemeAtom;
import org.apache.poi.hslf.record.SlideListWithText.SlideAtomsSet; import org.apache.poi.hslf.record.SlideListWithText.SlideAtomsSet;
import org.apache.poi.ddf.EscherDggRecord; import org.apache.poi.ddf.EscherDggRecord;
import org.apache.poi.ddf.EscherContainerRecord; 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. * 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 * 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); return (int)LittleEndian.getShort(_header);
} }
@ -64,7 +64,7 @@ public class CString extends RecordAtom {
* Sets the count * Sets the count
* The meaning of the count is specific to the type of the parent record * 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); LittleEndian.putShort(_header, (short)count);
} }

View File

@ -140,9 +140,9 @@ public class Comment2000 extends RecordContainer {
CString csa = new CString(); CString csa = new CString();
CString csb = new CString(); CString csb = new CString();
CString csc = new CString(); CString csc = new CString();
csa.setCount(0x00); csa.setOptions(0x00);
csb.setCount(0x10); csb.setOptions(0x10);
csc.setCount(0x20); csc.setOptions(0x20);
_children[0] = csa; _children[0] = csa;
_children[1] = csb; _children[1] = csb;
_children[2] = csc; _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.LittleEndian;
import org.apache.poi.util.StringUtil; import org.apache.poi.util.StringUtil;
import org.apache.poi.hslf.exceptions.CorruptPowerPointFileException; import org.apache.poi.hslf.exceptions.CorruptPowerPointFileException;
import org.apache.poi.hslf.exceptions.EncryptedPowerPointFileException;
/** /**
@ -39,14 +40,15 @@ public class CurrentUserAtom
{ {
/** Standard Atom header */ /** Standard Atom header */
public static final byte[] atomHeader = new byte[] { 0, 0, -10, 15 }; public static final byte[] atomHeader = new byte[] { 0, 0, -10, 15 };
/** The Powerpoint magic numer */ /** The PowerPoint magic number for a non-encrypted file */
public static final byte[] magicNumber = new byte[] { 95, -64, -111, -29 }; 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 */ /** The Powerpoint 97 version, major and minor numbers */
public static final byte[] ppt97FileVer = new byte[] { 8, 00, -13, 03, 03, 00 }; public static final byte[] ppt97FileVer = new byte[] { 8, 00, -13, 03, 03, 00 };
/** The version, major and minor numbers */ /** The version, major and minor numbers */
private int docFinalVersionA; private int docFinalVersion;
private int docFinalVersionB;
private byte docMajorNo; private byte docMajorNo;
private byte docMinorNo; private byte docMinorNo;
@ -54,7 +56,7 @@ public class CurrentUserAtom
private long currentEditOffset; private long currentEditOffset;
/** The Username of the last person to edit the file */ /** The Username of the last person to edit the file */
private String lastEditUser; private String lastEditUser;
/** The document release version */ /** The document release version. Almost always 8 */
private long releaseVersion; private long releaseVersion;
/** Only correct after reading in or writing out */ /** Only correct after reading in or writing out */
@ -63,8 +65,7 @@ public class CurrentUserAtom
/* ********************* getter/setter follows *********************** */ /* ********************* getter/setter follows *********************** */
public int getDocFinalVersionA() { return docFinalVersionA; } public int getDocFinalVersion() { return docFinalVersion; }
public int getDocFinalVersionB() { return docFinalVersionB; }
public byte getDocMajorNo() { return docMajorNo; } public byte getDocMajorNo() { return docMajorNo; }
public byte getDocMinorNo() { return docMinorNo; } public byte getDocMinorNo() { return docMinorNo; }
@ -86,7 +87,14 @@ public class CurrentUserAtom
*/ */
public CurrentUserAtom() { public CurrentUserAtom() {
_contents = new byte[0]; _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 * Actually do the creation from a block of bytes
*/ */
private void init() { 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 // Grab the edit offset
currentEditOffset = LittleEndian.getUInt(_contents,16); currentEditOffset = LittleEndian.getUInt(_contents,16);
// Grab the versions // Grab the versions
docFinalVersionA = LittleEndian.getUShort(_contents,20); docFinalVersion = LittleEndian.getUShort(_contents,22);
docFinalVersionB = LittleEndian.getUShort(_contents,22);
docMajorNo = _contents[24]; docMajorNo = _contents[24];
docMinorNo = _contents[25]; docMinorNo = _contents[25];
@ -194,15 +210,22 @@ public class CurrentUserAtom
// Now we have the size of the details, which is 20 // Now we have the size of the details, which is 20
LittleEndian.putInt(_contents,8,20); LittleEndian.putInt(_contents,8,20);
// Now the ppt magic number (4 bytes) // Now the ppt un-encrypted header token (4 bytes)
System.arraycopy(magicNumber,0,_contents,12,4); System.arraycopy(headerToken,0,_contents,12,4);
// Now the current edit offset // Now the current edit offset
LittleEndian.putInt(_contents,16,(int)currentEditOffset); LittleEndian.putInt(_contents,16,(int)currentEditOffset);
// Now the file versions, 2+2+1+1 // The username gets stored twice, once as US
LittleEndian.putShort(_contents,20,(short)docFinalVersionA); // ascii, and again as unicode laster on
LittleEndian.putShort(_contents,22,(short)docFinalVersionB); 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[24] = docMajorNo;
_contents[25] = docMinorNo; _contents[25] = docMinorNo;
@ -210,9 +233,7 @@ public class CurrentUserAtom
_contents[26] = 0; _contents[26] = 0;
_contents[27] = 0; _contents[27] = 0;
// username in bytes in us ascii // At this point we have the username as us ascii
byte[] asciiUN = new byte[lastEditUser.length()];
StringUtil.putCompressedUnicode(lastEditUser,asciiUN,0);
System.arraycopy(asciiUN,0,_contents,28,asciiUN.length); System.arraycopy(asciiUN,0,_contents,28,asciiUN.length);
// 4 byte release version // 4 byte release version

View File

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

View File

@ -136,8 +136,8 @@ public class ExHyperlink extends RecordContainer {
// Setup our child records // Setup our child records
CString csa = new CString(); CString csa = new CString();
CString csb = new CString(); CString csb = new CString();
csa.setCount(0x00); csa.setOptions(0x00);
csb.setCount(0x10); csb.setOptions(0x10);
_children[0] = new ExHyperlinkAtom(); _children[0] = new ExHyperlinkAtom();
_children[1] = csa; _children[1] = csa;
_children[2] = csb; _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 ExHyperlinkAtom = new Type(4051,ExHyperlinkAtom.class);
public static final Type ExHyperlink = new Type(4055,ExHyperlink.class); public static final Type ExHyperlink = new Type(4055,ExHyperlink.class);
public static final Type SlideNumberMCAtom = new Type(4056,null); public static final Type SlideNumberMCAtom = new Type(4056,null);
public static final Type HeadersFooters = new Type(4057,null); public static final Type HeadersFooters = new Type(4057,HeadersFootersContainer.class);
public static final Type HeadersFootersAtom = new Type(4058,null); public static final Type HeadersFootersAtom = new Type(4058,HeadersFootersAtom.class);
public static final Type TxInteractiveInfoAtom = new Type(4063,TxInteractiveInfoAtom.class); public static final Type TxInteractiveInfoAtom = new Type(4063,TxInteractiveInfoAtom.class);
public static final Type CharFormatAtom = new Type(4066,null); public static final Type CharFormatAtom = new Type(4066,null);
public static final Type ParaFormatAtom = new Type(4067,null); public static final Type ParaFormatAtom = new Type(4067,null);

View File

@ -811,4 +811,50 @@ public class SlideShow
public int getNumberOfFonts() { public int getNumberOfFonts() {
return getDocumentRecord().getEnvironment().getFontCollection().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 { public void testCount() throws Exception {
CString ca = new CString(data_a, 0, data_a.length); 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); CString cb = new CString(data_b, 0, data_a.length);
assertEquals(0x10, cb.getCount()); assertEquals(0x10, cb.getOptions());
ca.setCount(28); ca.setOptions(28);
assertEquals(28, ca.getCount()); assertEquals(28, ca.getOptions());
} }
public void testText() throws Exception { public void testText() throws Exception {
@ -90,7 +90,7 @@ public class TestCString extends TestCase {
public void testChange() throws Exception { public void testChange() throws Exception {
CString ca = new CString(data_a, 0, data_a.length); CString ca = new CString(data_a, 0, data_a.length);
ca.setText("Comments"); ca.setText("Comments");
ca.setCount(0x10); ca.setOptions(0x10);
try { try {
for(int i=0; i<data_a.length; i++) { 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); xfindex = sheet.getXFIndexForColAt((short) 1);
assertEquals(DEFAULT_IDX, xfindex); assertEquals(DEFAULT_IDX, xfindex);
ColumnInfoRecord nci = ( ColumnInfoRecord ) sheet.createColInfo(); // TODO change return type to ColumnInfoRecord
ColumnInfoRecord nci = (ColumnInfoRecord)ColumnInfoRecordsAggregate.createColInfo();
sheet.columns.insertColumn(nci); sheet.columns.insertColumn(nci);
// single column ColumnInfoRecord // single column ColumnInfoRecord

View File

@ -17,13 +17,10 @@
package org.apache.poi.hssf.model; package org.apache.poi.hssf.model;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.List;
import junit.framework.TestCase; import junit.framework.TestCase;
import org.apache.poi.hssf.record.ColumnInfoRecord; import org.apache.poi.hssf.record.ColumnInfoRecord;
import org.apache.poi.hssf.record.aggregates.ColumnInfoRecordsAggregate;
/** /**
* @author Tony Poppleton * @author Tony Poppleton
@ -32,7 +29,8 @@ public final class TestSheetAdditional extends TestCase {
public void testGetCellWidth() { public void testGetCellWidth() {
Sheet sheet = Sheet.createSheet(); Sheet sheet = Sheet.createSheet();
ColumnInfoRecord nci = ( ColumnInfoRecord ) sheet.createColInfo(); // TODO change return type to ColumnInfoRecord
ColumnInfoRecord nci = (ColumnInfoRecord)ColumnInfoRecordsAggregate.createColInfo();
// Prepare test model // Prepare test model
nci.setFirstColumn((short)5); nci.setFirstColumn((short)5);

View File

@ -20,7 +20,7 @@ package org.apache.poi.hssf.record;
import junit.framework.AssertionFailedError; import junit.framework.AssertionFailedError;
import junit.framework.TestCase; 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 * Tests the serialization and deserialization of the TestCFHeaderRecord
@ -34,18 +34,18 @@ public final class TestCFHeaderRecord extends TestCase
public void testCreateCFHeaderRecord () public void testCreateCFHeaderRecord ()
{ {
CFHeaderRecord record = new CFHeaderRecord(); CFHeaderRecord record = new CFHeaderRecord();
CellRange[] ranges = { CellRangeAddress[] ranges = {
new CellRange(0,0xFFFF,5,5), new CellRangeAddress(0,0xFFFF,5,5),
new CellRange(0,0xFFFF,6,6), new CellRangeAddress(0,0xFFFF,6,6),
new CellRange(0,1,0,1), new CellRangeAddress(0,1,0,1),
new CellRange(0,1,2,3), new CellRangeAddress(0,1,2,3),
new CellRange(2,3,0,1), new CellRangeAddress(2,3,0,1),
new CellRange(2,3,2,3), new CellRangeAddress(2,3,2,3),
}; };
record.setCellRanges(ranges); record.setCellRanges(ranges);
ranges = record.getCellRanges(); ranges = record.getCellRanges();
assertEquals(6,ranges.length); assertEquals(6,ranges.length);
CellRange enclosingCellRange = record.getEnclosingCellRange(); CellRangeAddress enclosingCellRange = record.getEnclosingCellRange();
assertEquals(0, enclosingCellRange.getFirstRow()); assertEquals(0, enclosingCellRange.getFirstRow());
assertEquals(65535, enclosingCellRange.getLastRow()); assertEquals(65535, enclosingCellRange.getLastRow());
assertEquals(0, enclosingCellRange.getFirstColumn()); assertEquals(0, enclosingCellRange.getFirstColumn());
@ -95,7 +95,7 @@ public final class TestCFHeaderRecord extends TestCase
assertEquals("#CFRULES", 3, record.getNumberOfConditionalFormats()); assertEquals("#CFRULES", 3, record.getNumberOfConditionalFormats());
assertTrue(record.getNeedRecalculation()); assertTrue(record.getNeedRecalculation());
confirm(record.getEnclosingCellRange(), 0, 3, 0, 3); confirm(record.getEnclosingCellRange(), 0, 3, 0, 3);
CellRange[] ranges = record.getCellRanges(); CellRangeAddress[] ranges = record.getCellRanges();
assertEquals(4, ranges.length); assertEquals(4, ranges.length);
confirm(ranges[0], 0, 1, 0, 1); confirm(ranges[0], 0, 1, 0, 1);
confirm(ranges[1], 0, 1, 2, 3); confirm(ranges[1], 0, 1, 2, 3);
@ -154,7 +154,7 @@ public final class TestCFHeaderRecord extends TestCase
assertEquals("#CFRULES", 19, record.getNumberOfConditionalFormats()); assertEquals("#CFRULES", 19, record.getNumberOfConditionalFormats());
assertFalse(record.getNeedRecalculation()); assertFalse(record.getNeedRecalculation());
confirm(record.getEnclosingCellRange(), 0, 65535, 0, 255); confirm(record.getEnclosingCellRange(), 0, 65535, 0, 255);
CellRange[] ranges = record.getCellRanges(); CellRangeAddress[] ranges = record.getCellRanges();
assertEquals(3, ranges.length); assertEquals(3, ranges.length);
confirm(ranges[0], 40000, 50000, 2, 2); confirm(ranges[0], 40000, 50000, 2, 2);
confirm(ranges[1], 0, 65535, 5, 5); 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]); 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("first row", expFirstRow, cr.getFirstRow());
assertEquals("last row", expLastRow, cr.getLastRow()); assertEquals("last row", expLastRow, cr.getLastRow());
assertEquals("first column", expFirstCol, cr.getFirstColumn()); assertEquals("first column", expFirstCol, cr.getFirstColumn());
assertEquals("last column", expLastColumn, cr.getLastColumn()); 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 Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with contributor license agreements. See the NOTICE file distributed with
@ -15,19 +14,19 @@
See the License for the specific language governing permissions and See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
==================================================================== */ ==================================================================== */
package org.apache.poi.hssf.record; package org.apache.poi.hssf.record;
import junit.framework.TestCase; 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 * Make sure the merge cells record behaves
* @author Danny Mui (dmui at apache dot org) * @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. * 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); assertNotSame("Merged and cloned objects are the same", merge, clone);
MergedRegion mergeRegion = merge.getAreaAt(0); CellRangeAddress mergeRegion = merge.getAreaAt(0);
MergedRegion cloneRegion = clone.getAreaAt(0); CellRangeAddress cloneRegion = clone.getAreaAt(0);
assertNotSame("Should not point to same objects when cloning", mergeRegion, cloneRegion); 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 From doesnt match", mergeRegion.getFirstRow(), cloneRegion.getFirstRow());
assertEquals("New Clone Row To doesnt match", mergeRegion.row_to, cloneRegion.row_to); assertEquals("New Clone Row To doesnt match", mergeRegion.getLastRow(), cloneRegion.getLastRow());
assertEquals("New Clone Col From doesnt match", mergeRegion.col_from, cloneRegion.col_from); assertEquals("New Clone Col From doesnt match", mergeRegion.getFirstColumn(), cloneRegion.getFirstColumn());
assertEquals("New Clone Col To doesnt match", mergeRegion.col_to, cloneRegion.col_to); assertEquals("New Clone Col To doesnt match", mergeRegion.getLastColumn(), cloneRegion.getLastColumn());
merge.removeAreaAt(0); merge.removeAreaAt(0);
assertNotNull("Clone's item not removed", clone.getAreaAt(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.CFRuleRecord;
import org.apache.poi.hssf.record.RecordFactory; import org.apache.poi.hssf.record.RecordFactory;
import org.apache.poi.hssf.record.CFRuleRecord.ComparisonOperator; 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.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.ss.util.CellRangeAddress;
/** /**
* Tests the serialization and deserialization of the CFRecordsAggregate * 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 rule2 = CFRuleRecord.create(workbook, ComparisonOperator.BETWEEN, "2", "5");
CFRuleRecord rule3 = CFRuleRecord.create(workbook, ComparisonOperator.GE, "100", null); CFRuleRecord rule3 = CFRuleRecord.create(workbook, ComparisonOperator.GE, "100", null);
header.setNumberOfConditionalFormats(3); header.setNumberOfConditionalFormats(3);
CellRange[] cellRanges = { CellRangeAddress[] cellRanges = {
new CellRange(0,1,0,0), new CellRangeAddress(0,1,0,0),
new CellRange(0,1,2,2), new CellRangeAddress(0,1,2,2),
}; };
header.setCellRanges(cellRanges); header.setCellRanges(cellRanges);
recs.add(header); recs.add(header);
@ -97,11 +97,4 @@ public final class TestCFRecordsAggregate extends TestCase
assertEquals(2, cellRanges.length); assertEquals(2, cellRanges.length);
assertEquals(3, header.getNumberOfConditionalFormats()); 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; package org.apache.poi.hssf.record.cf;
import org.apache.poi.ss.util.CellRangeAddress;
import junit.framework.AssertionFailedError; import junit.framework.AssertionFailedError;
import junit.framework.TestCase; import junit.framework.TestCase;
@ -25,15 +27,15 @@ import junit.framework.TestCase;
*/ */
public final class TestCellRange extends TestCase public final class TestCellRange extends TestCase
{ {
private static final CellRange biggest = createCR( 0, -1, 0,-1); private static final CellRangeAddress biggest = createCR( 0, -1, 0,-1);
private static final CellRange tenthColumn = createCR( 0, -1,10,10); private static final CellRangeAddress tenthColumn = createCR( 0, -1,10,10);
private static final CellRange tenthRow = createCR(10, 10, 0,-1); private static final CellRangeAddress tenthRow = createCR(10, 10, 0,-1);
private static final CellRange box10x10 = createCR( 0, 10, 0,10); private static final CellRangeAddress box10x10 = createCR( 0, 10, 0,10);
private static final CellRange box9x9 = createCR( 0, 9, 0, 9); private static final CellRangeAddress box9x9 = createCR( 0, 9, 0, 9);
private static final CellRange box10to20c = createCR( 0, 10,10,20); private static final CellRangeAddress box10to20c = createCR( 0, 10,10,20);
private static final CellRange oneCell = createCR(10, 10,10,10); 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, 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 lastRow pass -1 for max row index
* @param lastCol pass -1 for max col 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 // max row & max col limit as per BIFF8
return new CellRange( return new CellRangeAddress(
firstRow, firstRow,
lastRow == -1 ? 0xFFFF : lastRow, lastRow == -1 ? 0xFFFF : lastRow,
firstCol, firstCol,
@ -65,89 +67,89 @@ public final class TestCellRange extends TestCase
public void testContainsMethod() public void testContainsMethod()
{ {
CellRange [] ranges = sampleRanges; CellRangeAddress [] ranges = sampleRanges;
for(int i=0; i!=ranges.length;i++) for(int i=0; i!=ranges.length;i++)
{ {
for(int j=0; j!=ranges.length;j++) for(int j=0; j!=ranges.length;j++)
{ {
boolean expectedResult = containsExpectedResults[i][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 CellRangeAddress col1 = createCR( 0, -1, 1,1);
private static final CellRange col2 = createCR( 0, -1, 2,2); private static final CellRangeAddress col2 = createCR( 0, -1, 2,2);
private static final CellRange row1 = createCR( 1, 1, 0,-1); private static final CellRangeAddress row1 = createCR( 1, 1, 0,-1);
private static final CellRange row2 = createCR( 2, 2, 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 CellRangeAddress box0 = createCR( 0, 2, 0,2);
private static final CellRange box1 = createCR( 0, 1, 0,1); private static final CellRangeAddress box1 = createCR( 0, 1, 0,1);
private static final CellRange box2 = createCR( 0, 1, 2,3); private static final CellRangeAddress box2 = createCR( 0, 1, 2,3);
private static final CellRange box3 = createCR( 2, 3, 0,1); private static final CellRangeAddress box3 = createCR( 2, 3, 0,1);
private static final CellRange box4 = createCR( 2, 3, 2,3); private static final CellRangeAddress box4 = createCR( 2, 3, 2,3);
private static final CellRange box5 = createCR( 1, 3, 1,3); private static final CellRangeAddress box5 = createCR( 1, 3, 1,3);
public void testHasSharedBorderMethod() public void testHasSharedBorderMethod()
{ {
assertFalse(col1.hasExactSharedBorder(col1)); assertFalse(CellRangeUtil.hasExactSharedBorder(col1, col1));
assertFalse(col2.hasExactSharedBorder(col2)); assertFalse(CellRangeUtil.hasExactSharedBorder(col2, col2));
assertTrue(col1.hasExactSharedBorder(col2)); assertTrue(CellRangeUtil.hasExactSharedBorder(col1, col2));
assertTrue(col2.hasExactSharedBorder(col1)); assertTrue(CellRangeUtil.hasExactSharedBorder(col2, col1));
assertFalse(row1.hasExactSharedBorder(row1)); assertFalse(CellRangeUtil.hasExactSharedBorder(row1, row1));
assertFalse(row2.hasExactSharedBorder(row2)); assertFalse(CellRangeUtil.hasExactSharedBorder(row2, row2));
assertTrue(row1.hasExactSharedBorder(row2)); assertTrue(CellRangeUtil.hasExactSharedBorder(row1, row2));
assertTrue(row2.hasExactSharedBorder(row1)); assertTrue(CellRangeUtil.hasExactSharedBorder(row2, row1));
assertFalse(row1.hasExactSharedBorder(col1)); assertFalse(CellRangeUtil.hasExactSharedBorder(row1, col1));
assertFalse(row1.hasExactSharedBorder(col2)); assertFalse(CellRangeUtil.hasExactSharedBorder(row1, col2));
assertFalse(col1.hasExactSharedBorder(row1)); assertFalse(CellRangeUtil.hasExactSharedBorder(col1, row1));
assertFalse(col2.hasExactSharedBorder(row1)); assertFalse(CellRangeUtil.hasExactSharedBorder(col2, row1));
assertFalse(row2.hasExactSharedBorder(col1)); assertFalse(CellRangeUtil.hasExactSharedBorder(row2, col1));
assertFalse(row2.hasExactSharedBorder(col2)); assertFalse(CellRangeUtil.hasExactSharedBorder(row2, col2));
assertFalse(col1.hasExactSharedBorder(row2)); assertFalse(CellRangeUtil.hasExactSharedBorder(col1, row2));
assertFalse(col2.hasExactSharedBorder(row2)); assertFalse(CellRangeUtil.hasExactSharedBorder(col2, row2));
assertTrue(col2.hasExactSharedBorder(col1)); assertTrue(CellRangeUtil.hasExactSharedBorder(col2, col1));
assertFalse(box1.hasExactSharedBorder(box1)); assertFalse(CellRangeUtil.hasExactSharedBorder(box1, box1));
assertTrue(box1.hasExactSharedBorder(box2)); assertTrue(CellRangeUtil.hasExactSharedBorder(box1, box2));
assertTrue(box1.hasExactSharedBorder(box3)); assertTrue(CellRangeUtil.hasExactSharedBorder(box1, box3));
assertFalse(box1.hasExactSharedBorder(box4)); assertFalse(CellRangeUtil.hasExactSharedBorder(box1, box4));
assertTrue(box2.hasExactSharedBorder(box1)); assertTrue(CellRangeUtil.hasExactSharedBorder(box2, box1));
assertFalse(box2.hasExactSharedBorder(box2)); assertFalse(CellRangeUtil.hasExactSharedBorder(box2, box2));
assertFalse(box2.hasExactSharedBorder(box3)); assertFalse(CellRangeUtil.hasExactSharedBorder(box2, box3));
assertTrue(box2.hasExactSharedBorder(box4)); assertTrue(CellRangeUtil.hasExactSharedBorder(box2, box4));
assertTrue(box3.hasExactSharedBorder(box1)); assertTrue(CellRangeUtil.hasExactSharedBorder(box3, box1));
assertFalse(box3.hasExactSharedBorder(box2)); assertFalse(CellRangeUtil.hasExactSharedBorder(box3, box2));
assertFalse(box3.hasExactSharedBorder(box3)); assertFalse(CellRangeUtil.hasExactSharedBorder(box3, box3));
assertTrue(box3.hasExactSharedBorder(box4)); assertTrue(CellRangeUtil.hasExactSharedBorder(box3, box4));
assertFalse(box4.hasExactSharedBorder(box1)); assertFalse(CellRangeUtil.hasExactSharedBorder(box4, box1));
assertTrue(box4.hasExactSharedBorder(box2)); assertTrue(CellRangeUtil.hasExactSharedBorder(box4, box2));
assertTrue(box4.hasExactSharedBorder(box3)); assertTrue(CellRangeUtil.hasExactSharedBorder(box4, box3));
assertFalse(box4.hasExactSharedBorder(box4)); assertFalse(CellRangeUtil.hasExactSharedBorder(box4, box4));
} }
public void testIntersectMethod() public void testIntersectMethod()
{ {
assertEquals(CellRange.OVERLAP,box0.intersect(box5)); assertEquals(CellRangeUtil.OVERLAP, CellRangeUtil.intersect(box0, box5));
assertEquals(CellRange.OVERLAP,box5.intersect(box0)); assertEquals(CellRangeUtil.OVERLAP, CellRangeUtil.intersect(box5, box0));
assertEquals(CellRange.NO_INTERSECTION,box1.intersect(box4)); assertEquals(CellRangeUtil.NO_INTERSECTION, CellRangeUtil.intersect(box1, box4));
assertEquals(CellRange.NO_INTERSECTION,box4.intersect(box1)); assertEquals(CellRangeUtil.NO_INTERSECTION, CellRangeUtil.intersect(box4, box1));
assertEquals(CellRange.NO_INTERSECTION,box2.intersect(box3)); assertEquals(CellRangeUtil.NO_INTERSECTION, CellRangeUtil.intersect(box2, box3));
assertEquals(CellRange.NO_INTERSECTION,box3.intersect(box2)); assertEquals(CellRangeUtil.NO_INTERSECTION, CellRangeUtil.intersect(box3, box2));
assertEquals(CellRange.INSIDE,box0.intersect(box1)); assertEquals(CellRangeUtil.INSIDE, CellRangeUtil.intersect(box0, box1));
assertEquals(CellRange.INSIDE,box0.intersect(box0)); assertEquals(CellRangeUtil.INSIDE, CellRangeUtil.intersect(box0, box0));
assertEquals(CellRange.ENCLOSES,box1.intersect(box0)); assertEquals(CellRangeUtil.ENCLOSES, CellRangeUtil.intersect(box1, box0));
assertEquals(CellRange.INSIDE,tenthColumn.intersect(oneCell)); assertEquals(CellRangeUtil.INSIDE, CellRangeUtil.intersect(tenthColumn, oneCell));
assertEquals(CellRange.ENCLOSES,oneCell.intersect(tenthColumn)); assertEquals(CellRangeUtil.ENCLOSES, CellRangeUtil.intersect(oneCell, tenthColumn));
assertEquals(CellRange.OVERLAP,tenthColumn.intersect(tenthRow)); assertEquals(CellRangeUtil.OVERLAP, CellRangeUtil.intersect(tenthColumn, tenthRow));
assertEquals(CellRange.OVERLAP,tenthRow.intersect(tenthColumn)); assertEquals(CellRangeUtil.OVERLAP, CellRangeUtil.intersect(tenthRow, tenthColumn));
assertEquals(CellRange.INSIDE,tenthColumn.intersect(tenthColumn)); assertEquals(CellRangeUtil.INSIDE, CellRangeUtil.intersect(tenthColumn, tenthColumn));
assertEquals(CellRange.INSIDE,tenthRow.intersect(tenthRow)); 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 * =$C:$IV,$B$1:$B$8,$B$10:$B$65536,$A:$A
*/ */
public void testCreate() { public void testCreate() {
CellRange cr; CellRangeAddress cr;
cr = createCR(0, -1, 2, 255); // $C:$IV cr = createCR(0, -1, 2, 255); // $C:$IV
confirmRange(cr, false, true); confirmRange(cr, false, true);
@ -172,7 +174,7 @@ public final class TestCellRange extends TestCase
cr = createCR(0, -1, 0, 0); // $A:$A 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("isFullRowRange", isFullRow, cr.isFullRowRange());
assertEquals("isFullColumnRange", isFullColumn, cr.isFullColumnRange()); 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 junit.framework.TestCase;
import org.apache.poi.ss.util.Region; 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.HSSFTestDataSamples;
import org.apache.poi.hssf.model.Workbook; 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 * Merged regions were being removed from the parent in cloned sheets
* @throws Exception
*/ */
public void test22720() { public void test22720() {
HSSFWorkbook workBook = new HSSFWorkbook(); HSSFWorkbook workBook = new HSSFWorkbook();
workBook.createSheet("TEST"); workBook.createSheet("TEST");
HSSFSheet template = workBook.getSheetAt(0); HSSFSheet template = workBook.getSheetAt(0);
template.addMergedRegion(new Region(0, (short)0, 1, (short)2)); template.addMergedRegion(new CellRangeAddress(0, 1, 0, 2));
template.addMergedRegion(new Region(1, (short)0, 2, (short)2)); template.addMergedRegion(new CellRangeAddress(1, 2, 0, 2));
HSSFSheet clone = workBook.cloneSheet(0); HSSFSheet clone = workBook.cloneSheet(0);
int originalMerged = template.getNumMergedRegions(); int originalMerged = template.getNumMergedRegions();
@ -318,20 +318,20 @@ public final class TestBugs extends TestCase {
// remove merged regions from clone // remove merged regions from clone
for (int i=template.getNumMergedRegions()-1; i>=0; i--) { 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()); assertEquals("Original Sheet's Merged Regions were removed", originalMerged, template.getNumMergedRegions());
// check if template's merged regions are OK // check if template's merged regions are OK
if (template.getNumMergedRegions()>0) { if (template.getNumMergedRegions()>0) {
// fetch the first merged region...EXCEPTION OCCURS HERE // fetch the first merged region...EXCEPTION OCCURS HERE
template.getMergedRegionAt(0); template.getMergedRegion(0);
} }
//make sure we dont exception //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 * bug and testcase submitted by Sompop Kumnoonsate
* The file contains THAI unicode characters. * The file contains THAI unicode characters.
*/ */

View File

@ -21,6 +21,7 @@ package org.apache.poi.hssf.usermodel;
import junit.framework.TestCase; import junit.framework.TestCase;
import org.apache.poi.ss.util.CellRangeAddress;
import org.apache.poi.ss.util.Region; 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. * add that record to the sheet in the testCloneSheetBasic method.
* @author avik * @author avik
*/ */
public class TestCloneSheet extends TestCase { public final class TestCloneSheet extends TestCase {
public TestCloneSheet(String arg0) {
super(arg0);
}
public void testCloneSheetBasic(){ public void testCloneSheetBasic(){
try{ HSSFWorkbook b = new HSSFWorkbook();
HSSFWorkbook b = new HSSFWorkbook(); HSSFSheet s = b.createSheet("Test");
HSSFSheet s = b.createSheet("Test"); s.addMergedRegion(new CellRangeAddress(0, 1, 0, 1));
s.addMergedRegion(new Region((short)0,(short)0,(short)1,(short)1)); HSSFSheet clonedSheet = b.cloneSheet(0);
HSSFSheet clonedSheet = b.cloneSheet(0);
assertEquals("One merged area", 1, clonedSheet.getNumMergedRegions());
assertEquals("One merged area", 1, clonedSheet.getNumMergedRegions());
}
catch(Exception e){e.printStackTrace();fail(e.getMessage());}
} }
/** /**
@ -66,5 +59,4 @@ public class TestCloneSheet extends TestCase {
assertTrue("Row 3 still should be broken", clone.isRowBroken(3)); 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.record.CFRuleRecord.ComparisonOperator;
import org.apache.poi.hssf.util.HSSFColor; import org.apache.poi.hssf.util.HSSFColor;
import org.apache.poi.ss.util.CellRangeAddress;
import org.apache.poi.ss.util.Region; import org.apache.poi.ss.util.Region;
/** /**
* *
* @author Dmitriy Kumshayev * @author Dmitriy Kumshayev
@ -57,9 +59,8 @@ public final class TestHSSFConditionalFormatting extends TestCase
}; };
short col = 1; short col = 1;
Region [] regions = CellRangeAddress [] regions = {
{ new CellRangeAddress(0, 65535, col, col)
new Region(0,col,65535,col)
}; };
sheetCF.addConditionalFormatting(regions, cfRules); sheetCF.addConditionalFormatting(regions, cfRules);
@ -72,14 +73,14 @@ public final class TestHSSFConditionalFormatting extends TestCase
HSSFConditionalFormatting cf = sheetCF.getConditionalFormattingAt(0); HSSFConditionalFormatting cf = sheetCF.getConditionalFormattingAt(0);
assertNotNull(cf); assertNotNull(cf);
regions = cf.getFormattingRegions(); regions = cf.getFormattingRanges();
assertNotNull(regions); assertNotNull(regions);
assertEquals(1, regions.length); assertEquals(1, regions.length);
Region r = regions[0]; CellRangeAddress r = regions[0];
assertEquals(1, r.getColumnFrom()); assertEquals(1, r.getFirstColumn());
assertEquals(1, r.getColumnTo()); assertEquals(1, r.getLastColumn());
assertEquals(0, r.getRowFrom()); assertEquals(0, r.getFirstRow());
assertEquals(65535, r.getRowTo()); assertEquals(65535, r.getLastRow());
assertEquals(2, cf.getNumberOfRules()); assertEquals(2, cf.getNumberOfRules());

View File

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

View File

@ -39,7 +39,7 @@ import org.apache.poi.hssf.model.Workbook;
* @author Alex Jacoby (ajacoby at gmail.com) * @author Alex Jacoby (ajacoby at gmail.com)
* @version %I%, %G% * @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_JANUARY = 0;
public static final int CALENDAR_FEBRUARY = 1; 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_APRIL = 3;
public static final int CALENDAR_JULY = 6; public static final int CALENDAR_JULY = 6;
public static final int CALENDAR_OCTOBER = 9; public static final int CALENDAR_OCTOBER = 9;
public TestHSSFDateUtil(String s)
{
super(s);
}
/** /**
* Checks the date conversion functions in the HSSFDateUtil class. * 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() { public void testCalendarConversion() {
GregorianCalendar date = new GregorianCalendar(2002, 0, 1, 12, 1, 1); GregorianCalendar date = new GregorianCalendar(2002, 0, 1, 12, 1, 1);
Date expected = date.getTime(); 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++) for (int hour = -12; hour <= 12; hour++)
{ {
String id = "GMT" + (hour < 0 ? "" : "+") + hour + ":00"; String id = "GMT" + (hour < 0 ? "" : "+") + hour + ":00";
@ -209,7 +203,7 @@ public class TestHSSFDateUtil extends TestCase {
double excelDate = HSSFDateUtil.getExcelDate(date, false); double excelDate = HSSFDateUtil.getExcelDate(date, false);
Date javaDate = HSSFDateUtil.getJavaDate(excelDate); Date javaDate = HSSFDateUtil.getJavaDate(excelDate);
// Should match despite timezone // Should match despite time-zone
assertEquals("Checking timezone " + id, expected.getTime(), javaDate.getTime()); 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); 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(); Calendar c = new GregorianCalendar();
c.set(year, month, day, 0, 0, 0); c.set(year, month, day, 0, 0, 0);
c.set(Calendar.MILLISECOND, 0); c.set(Calendar.MILLISECOND, 0);
@ -420,10 +418,18 @@ public class TestHSSFDateUtil extends TestCase {
calendar = new GregorianCalendar(1901, 0, 1); calendar = new GregorianCalendar(1901, 0, 1);
assertEquals("Checking absolute day (1 Jan 1901)", 366, HSSFDateUtil.absoluteDay(calendar, false)); 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) { public void testParseDate() {
System.out assertEquals(createDate(2008, Calendar.AUGUST, 3), HSSFDateUtil.parseYYYYMMDDDate("2008/08/03"));
.println("Testing org.apache.poi.hssf.usermodel.TestHSSFDateUtil"); assertEquals(createDate(1994, Calendar.MAY, 1), HSSFDateUtil.parseYYYYMMDDDate("1994/05/01"));
junit.textui.TestRunner.run(TestHSSFDateUtil.class);
} }
} }

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.WSBoolRecord;
import org.apache.poi.hssf.record.WindowTwoRecord; import org.apache.poi.hssf.record.WindowTwoRecord;
import org.apache.poi.ss.util.Region; 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. * Tests HSSFSheet. This test case is very incomplete at the moment.
@ -476,15 +477,15 @@ public final class TestHSSFSheet extends TestCase {
public void testRemoveMerged() { public void testRemoveMerged() {
HSSFWorkbook wb = new HSSFWorkbook(); HSSFWorkbook wb = new HSSFWorkbook();
HSSFSheet sheet = wb.createSheet(); 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); sheet.addMergedRegion(region);
region = new Region(1, (short)0, 2, (short)1); region = new CellRangeAddress(1, 2, 0, 1);
sheet.addMergedRegion(region); sheet.addMergedRegion(region);
sheet.removeMergedRegion(0); sheet.removeMergedRegion(0);
region = sheet.getMergedRegionAt(0); region = sheet.getMergedRegion(0);
assertEquals("Left over region should be starting at row 1", 1, region.getRowFrom()); assertEquals("Left over region should be starting at row 1", 1, region.getFirstRow());
sheet.removeMergedRegion(0); sheet.removeMergedRegion(0);
@ -496,15 +497,15 @@ public final class TestHSSFSheet extends TestCase {
sheet.removeMergedRegion(0); sheet.removeMergedRegion(0);
assertEquals("there should now be zero merged regions!", 0, sheet.getNumMergedRegions()); assertEquals("there should now be zero merged regions!", 0, sheet.getNumMergedRegions());
//add it again! //add it again!
region.setRowTo(4); region.setLastRow(4);
sheet.addMergedRegion(region); sheet.addMergedRegion(region);
assertEquals("there should now be one merged region!", 1, sheet.getNumMergedRegions()); assertEquals("there should now be one merged region!", 1, sheet.getNumMergedRegions());
//should exist now! //should exist now!
assertTrue("there isn't more than one merged region in there", 1 <= sheet.getNumMergedRegions()); assertTrue("there isn't more than one merged region in there", 1 <= sheet.getNumMergedRegions());
region = sheet.getMergedRegionAt(0); region = sheet.getMergedRegion(0);
assertEquals("the merged row to doesnt match the one we put in ", 4, region.getRowTo()); assertEquals("the merged row to doesnt match the one we put in ", 4, region.getLastRow());
} }
public void testShiftMerged() { public void testShiftMerged() {
@ -518,13 +519,13 @@ public final class TestHSSFSheet extends TestCase {
cell = row.createCell((short)1); cell = row.createCell((short)1);
cell.setCellValue(new HSSFRichTextString("second row, second cell")); 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.addMergedRegion(region);
sheet.shiftRows(1, 1, 1); sheet.shiftRows(1, 1, 1);
region = sheet.getMergedRegionAt(0); region = sheet.getMergedRegion(0);
assertEquals("Merged region not moved over to row 2", 2, region.getRowFrom()); 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); 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 //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); sheet.autoSizeColumn((short)0);
HSSFWorkbook wb2 = HSSFTestDataSamples.writeOutAndReadBack(wb); 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.Record;
import org.apache.poi.hssf.record.aggregates.ValueRecordsAggregate; import org.apache.poi.hssf.record.aggregates.ValueRecordsAggregate;
import org.apache.poi.poifs.filesystem.POIFSFileSystem; 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.ss.util.Region;
import org.apache.poi.util.TempFile; import org.apache.poi.util.TempFile;
@ -42,7 +43,7 @@ import org.apache.poi.util.TempFile;
* @author Greg Merrill * @author Greg Merrill
* @author Siggi Cherem * @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 LAST_NAME_KEY = "lastName";
private static final String FIRST_NAME_KEY = "firstName"; private static final String FIRST_NAME_KEY = "firstName";
private static final String SSN_KEY = "ssn"; private static final String SSN_KEY = "ssn";
@ -260,10 +261,10 @@ public class TestWorkbook extends TestCase {
HSSFWorkbook workbook = openSample("Employee.xls"); HSSFWorkbook workbook = openSample("Employee.xls");
HSSFSheet sheet = workbook.getSheetAt(0); HSSFSheet sheet = workbook.getSheetAt(0);
assertEquals(EMPLOYEE_INFORMATION, sheet.getRow(1).getCell(1).getStringCellValue()); assertEquals(EMPLOYEE_INFORMATION, sheet.getRow(1).getCell(1).getRichStringCellValue().getString());
assertEquals(LAST_NAME_KEY, sheet.getRow(3).getCell(2).getStringCellValue()); assertEquals(LAST_NAME_KEY, sheet.getRow(3).getCell(2).getRichStringCellValue().getString());
assertEquals(FIRST_NAME_KEY, sheet.getRow(4).getCell(2).getStringCellValue()); assertEquals(FIRST_NAME_KEY, sheet.getRow(4).getCell(2).getRichStringCellValue().getString());
assertEquals(SSN_KEY, sheet.getRow(5).getCell(2).getStringCellValue()); assertEquals(SSN_KEY, sheet.getRow(5).getCell(2).getRichStringCellValue().getString());
} }
/** /**
@ -318,13 +319,13 @@ public class TestWorkbook extends TestCase {
sheet = workbook.getSheetAt(0); sheet = workbook.getSheetAt(0);
cell = sheet.getRow(0).getCell(1); cell = sheet.getRow(0).getCell(1);
assertEquals(REPLACED, cell.getStringCellValue()); assertEquals(REPLACED, cell.getRichStringCellValue().getString());
cell = sheet.getRow(0).getCell(0); 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); cell = sheet.getRow(1).getCell(0);
assertEquals(REPLACED, cell.getStringCellValue()); assertEquals(REPLACED, cell.getRichStringCellValue().getString());
cell = sheet.getRow(1).getCell(1); 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); workbook = HSSFTestDataSamples.writeOutAndReadBack(workbook);
sheet = workbook.getSheetAt(0); sheet = workbook.getSheetAt(0);
assertEquals(EMPLOYEE_INFORMATION, sheet.getRow(1).getCell(1).getStringCellValue()); assertEquals(EMPLOYEE_INFORMATION, sheet.getRow(1).getCell(1).getRichStringCellValue().getString());
assertEquals(LAST_NAME_VALUE, sheet.getRow(3).getCell(2).getStringCellValue()); assertEquals(LAST_NAME_VALUE, sheet.getRow(3).getCell(2).getRichStringCellValue().getString());
assertEquals(FIRST_NAME_VALUE, sheet.getRow(4).getCell(2).getStringCellValue()); assertEquals(FIRST_NAME_VALUE, sheet.getRow(4).getCell(2).getRichStringCellValue().getString());
assertEquals(SSN_VALUE, sheet.getRow(5).getCell(2).getStringCellValue()); 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> * HSSFSheet last row or first row is incorrect. <P>
* *
*/ */
public void testWriteModifySheetMerged() {
public void testWriteModifySheetMerged()
throws IOException
{
File file = TempFile.createTempFile("testWriteSheetMerged",
".xls");
FileOutputStream out = new FileOutputStream(file);
FileInputStream in = null;
HSSFWorkbook wb = new HSSFWorkbook(); HSSFWorkbook wb = new HSSFWorkbook();
HSSFSheet s = wb.createSheet(); HSSFSheet s = wb.createSheet();
HSSFRow r = null;
HSSFCell c = null;
for (short rownum = ( short ) 0; rownum < 100; rownum++) 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) for (short cellnum = ( short ) 0; cellnum < 50; cellnum += 2)
{ {
c = r.createCell(cellnum); HSSFCell c = r.createCell(cellnum);
c.setCellValue(rownum * 10000 + cellnum c.setCellValue(rownum * 10000 + cellnum
+ ((( double ) rownum / 1000) + ((( double ) rownum / 1000)
+ (( double ) cellnum / 10000))); + (( double ) cellnum / 10000)));
@ -448,33 +440,27 @@ public class TestWorkbook extends TestCase {
c.setCellValue(new HSSFRichTextString("TEST")); c.setCellValue(new HSSFRichTextString("TEST"));
} }
} }
s.addMergedRegion(new Region(( short ) 0, ( short ) 0, ( short ) 10, s.addMergedRegion(new CellRangeAddress(0, 10, 0, 10));
( short ) 10)); s.addMergedRegion(new CellRangeAddress(30, 40, 5, 15));
s.addMergedRegion(new Region(( short ) 30, ( short ) 5, ( short ) 40,
( short ) 15));
wb.write(out);
out.close();
sanityChecker.checkHSSFWorkbook(wb); sanityChecker.checkHSSFWorkbook(wb);
in = new FileInputStream(file); wb = HSSFTestDataSamples.writeOutAndReadBack(wb);
wb = new HSSFWorkbook(new POIFSFileSystem(in));
s = wb.getSheetAt(0); s = wb.getSheetAt(0);
Region r1 = s.getMergedRegionAt(0); CellRangeAddress r1 = s.getMergedRegion(0);
Region r2 = s.getMergedRegionAt(1); CellRangeAddress r2 = s.getMergedRegion(1);
in.close(); confirmRegion(new CellRangeAddress(0, 10, 0, 10), r1);
confirmRegion(new CellRangeAddress(30, 40,5, 15), r2);
// 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));
} }
/** 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. * Test the backup field gets set as expected.
*/ */