From cfdf265975a3961143e059717352630b7d42ab7d Mon Sep 17 00:00:00 2001
From: Nick Burch
* pagebreakzoom = 0x0
* normalzoom = 0x0
- * @see org.apache.poi.hssf.record.WindowTwoRecord
- * @see org.apache.poi.hssf.record.Record
- * @return record containing a WindowTwoRecord
*/
-
- protected WindowTwoRecord createWindowTwo()
- {
+ private static WindowTwoRecord createWindowTwo() {
WindowTwoRecord retval = new WindowTwoRecord();
retval.setOptions(( short ) 0x6b6);
@@ -1869,21 +1633,9 @@ public final class Sheet implements Model {
/**
* Creates the Selection record and sets it to nothing selected
- *
- * @see org.apache.poi.hssf.record.SelectionRecord
- * @see org.apache.poi.hssf.record.Record
- * @return record containing a SelectionRecord
- */
-
- protected Record createSelection()
- {
- SelectionRecord retval = new SelectionRecord();
-
- retval.setPane(( byte ) 0x3);
- retval.setActiveCellCol(( short ) 0x0);
- retval.setActiveCellRow(( short ) 0x0);
- retval.setNumRefs(( short ) 0x0);
- return retval;
+ */
+ private static SelectionRecord createSelection() {
+ return new SelectionRecord(0, 0);
}
public short getTopRow()
@@ -1903,19 +1655,15 @@ public final class Sheet implements Model {
* Sets the left column to show in desktop window pane.
* @param leftCol the left column to show in desktop window pane
*/
- public void setLeftCol(short leftCol){
- if (windowTwo!=null)
- {
+ public void setLeftCol(short leftCol){
+ if (windowTwo!=null) {
windowTwo.setLeftCol(leftCol);
- }
}
+ }
- public short getLeftCol()
- {
- return (windowTwo==null) ? (short) 0 : windowTwo.getLeftCol();
- }
-
-
+ public short getLeftCol() {
+ return (windowTwo==null) ? (short) 0 : windowTwo.getLeftCol();
+ }
/**
* Returns the active row
@@ -1948,18 +1696,14 @@ public final class Sheet implements Model {
}
/**
- * Returns the active column
- *
* @see org.apache.poi.hssf.record.SelectionRecord
- * @return row the active column index
+ * @return column of the active cell
*/
- public short getActiveCellCol()
- {
- if (selection == null)
- {
- return (short) 0;
+ public short getActiveCellCol() {
+ if (selection == null) {
+ return 0;
}
- return selection.getActiveCellCol();
+ return (short)selection.getActiveCellCol();
}
/**
@@ -1977,23 +1721,8 @@ public final class Sheet implements Model {
}
}
- protected Record createMergedCells()
- {
- MergeCellsRecord retval = new MergeCellsRecord();
- retval.setNumAreas(( short ) 0);
- return retval;
- }
-
- /**
- * creates the EOF record
- * @see org.apache.poi.hssf.record.EOFRecord
- * @see org.apache.poi.hssf.record.Record
- * @return record containing a EOFRecord
- */
-
- protected Record createEOF()
- {
- return new EOFRecord();
+ private static MergeCellsRecord createMergedCells() {
+ return new MergeCellsRecord();
}
/**
@@ -2383,28 +2112,20 @@ public final class Sheet implements Model {
/**
* creates a Protect record with protect set to false.
- * @see org.apache.poi.hssf.record.ProtectRecord
- * @see org.apache.poi.hssf.record.Record
- * @return a ProtectRecord
*/
- protected Record createProtect()
- {
- if (log.check( POILogger.DEBUG ))
+ private static ProtectRecord createProtect() {
+ if (log.check( POILogger.DEBUG )) {
log.log(POILogger.DEBUG, "create protect record with protection disabled");
- ProtectRecord retval = new ProtectRecord();
-
- retval.setProtect(false);
+ }
+ ProtectRecord retval = new ProtectRecord();
+ retval.setProtect(false); // TODO - supply param to constructor
return retval;
}
/**
* creates an ObjectProtect record with protect set to false.
- * @see org.apache.poi.hssf.record.ObjectProtectRecord
- * @see org.apache.poi.hssf.record.Record
- * @return an ObjectProtectRecord
*/
- protected ObjectProtectRecord createObjectProtect()
- {
+ private static ObjectProtectRecord createObjectProtect() {
if (log.check( POILogger.DEBUG ))
log.log(POILogger.DEBUG, "create protect record with protection disabled");
ObjectProtectRecord retval = new ObjectProtectRecord();
@@ -2415,12 +2136,8 @@ public final class Sheet implements Model {
/**
* creates a ScenarioProtect record with protect set to false.
- * @see org.apache.poi.hssf.record.ScenarioProtectRecord
- * @see org.apache.poi.hssf.record.Record
- * @return a ScenarioProtectRecord
*/
- protected ScenarioProtectRecord createScenarioProtect()
- {
+ private static ScenarioProtectRecord createScenarioProtect() {
if (log.check( POILogger.DEBUG ))
log.log(POILogger.DEBUG, "create protect record with protection disabled");
ScenarioProtectRecord retval = new ScenarioProtectRecord();
@@ -2435,9 +2152,9 @@ public final class Sheet implements Model {
public ProtectRecord getProtect()
{
if (protect == null) {
- protect = (ProtectRecord)createProtect();
- //Insert the newlycreated protect record at the end of the record (just before the EOF)
- int loc = findFirstRecordLocBySid(EOFRecord.sid);
+ protect = createProtect();
+ // Insert the newly created protect record just before DefaultColWidthRecord
+ int loc = findFirstRecordLocBySid(DefaultColWidthRecord.sid);
records.add(loc, protect);
}
return protect;
@@ -2459,14 +2176,11 @@ public final class Sheet implements Model {
/**
* creates a Password record with password set to 00.
- * @see org.apache.poi.hssf.record.PasswordRecord
- * @see org.apache.poi.hssf.record.Record
- * @return a PasswordRecord
*/
- protected PasswordRecord createPassword()
- {
- if (log.check( POILogger.DEBUG ))
- log.log(POILogger.DEBUG, "create password record with 00 password");
+ private static PasswordRecord createPassword() {
+ if (log.check( POILogger.DEBUG )) {
+ log.log(POILogger.DEBUG, "create password record with 00 password");
+ }
PasswordRecord retval = new PasswordRecord();
retval.setPassword((short)00);
@@ -2892,4 +2606,10 @@ public final class Sheet implements Model {
rows.expandRow( row );
}
}
+ public DataValidityTable getOrCreateDataValidityTable() {
+ if (_dataValidityTable == null) {
+ _dataValidityTable = DataValidityTable.createForSheet(records);
+ }
+ return _dataValidityTable;
+ }
}
diff --git a/src/java/org/apache/poi/hssf/record/CFHeaderRecord.java b/src/java/org/apache/poi/hssf/record/CFHeaderRecord.java
index 6447546ec..862adc9c5 100644
--- a/src/java/org/apache/poi/hssf/record/CFHeaderRecord.java
+++ b/src/java/org/apache/poi/hssf/record/CFHeaderRecord.java
@@ -17,35 +17,33 @@
package org.apache.poi.hssf.record;
-import org.apache.poi.hssf.record.cf.CellRange;
-import org.apache.poi.hssf.util.Region;
+import org.apache.poi.hssf.record.cf.CellRangeUtil;
+import org.apache.poi.ss.util.CellRangeAddress;
+import org.apache.poi.ss.util.CellRangeAddressList;
import org.apache.poi.util.LittleEndian;
/**
- * Conditional Formatting Header record (CFHEADER)
+ * Conditional Formatting Header record CFHEADER (0x1B0)
*
* @author Dmitriy Kumshayev
*/
-public final class CFHeaderRecord extends Record
-{
+public final class CFHeaderRecord extends Record {
public static final short sid = 0x1B0;
- private static final CellRange[] EMPTY_CELL_RANGE_ARRAY = { };
-
private int field_1_numcf;
private int field_2_need_recalculation;
- private CellRange field_3_enclosing_cell_range;
- private CellRange[] field_4_cell_ranges;
+ private CellRangeAddress field_3_enclosing_cell_range;
+ private CellRangeAddressList field_4_cell_ranges;
/** Creates new CFHeaderRecord */
public CFHeaderRecord()
{
- field_4_cell_ranges = EMPTY_CELL_RANGE_ARRAY;
+ field_4_cell_ranges = new CellRangeAddressList();
}
- public CFHeaderRecord(org.apache.poi.ss.util.Region[] regions)
+ public CFHeaderRecord(CellRangeAddress[] regions)
{
- CellRange[] unmergedRanges = CellRange.convertRegionsToCellRanges(regions);
- CellRange[] mergeCellRanges = CellRange.mergeCellRanges(unmergedRanges);
+ CellRangeAddress[] unmergedRanges = regions;
+ CellRangeAddress[] mergeCellRanges = CellRangeUtil.mergeCellRanges(unmergedRanges);
setCellRanges(mergeCellRanges);
}
@@ -58,14 +56,8 @@ public final class CFHeaderRecord extends Record
{
field_1_numcf = in.readShort();
field_2_need_recalculation = in.readShort();
- field_3_enclosing_cell_range = new CellRange(in.readUShort(), in.readUShort(), in.readUShort(), in.readUShort());
- int numCellRanges = in.readShort();
- CellRange[] crs = new CellRange[numCellRanges];
- for( int i=0; i
+ * Title: Selection Record (0x001D)
* Description: shows the user's selection on the sheet
* for write set num refs to 0
*
- * TODO : Fully implement reference subrecords.
* REFERENCE: PG 291 Microsoft Excel 97 Developer's Kit (ISBN: 1-57231-498-2)
* @author Andrew C. Oliver (acoliver at apache dot org)
* @author Jason Height (jheight at chariot dot net dot au)
* @author Glen Stampoultzis (glens at apache.org)
*/
+public final class SelectionRecord extends Record {
+ public final static short sid = 0x001D;
+ private byte field_1_pane;
+ private int field_2_row_active_cell;
+ private int field_3_col_active_cell;
+ private int field_4_active_cell_ref_index;
+ private Reference[] field_6_refs;
-public class SelectionRecord
- extends Record
-{
- public final static short sid = 0x1d;
- private byte field_1_pane;
- //private short field_2_row_active_cell;
- private int field_2_row_active_cell;
- private short field_3_col_active_cell;
- private short field_4_ref_active_cell;
- private short field_5_num_refs;
- private ArrayList field_6_refs; // not used yet
-
+ /**
+ * Note - column values are 8-bit so cannot use CellRangeAddressList
+ */
public class Reference {
- private short field_1_first_row;
- private short field_2_last_row;
- private byte field_3_first_column;
- private byte field_4_last_column;
+ /* package */ static final int ENCODED_SIZE = 6;
+ private int _firstRow;
+ private int _lastRow;
+ private int _firstCol;
+ private int _lastCol;
- Reference(RecordInputStream in) {
- field_1_first_row = in.readShort();
- field_2_last_row = in.readShort();
- field_3_first_column = in.readByte();
- field_4_last_column = in.readByte();
- }
-
- public short getFirstRow() {
- return field_1_first_row;
- }
-
- public short getLastRow() {
- return field_2_last_row;
- }
-
- public byte getFirstColumn() {
- return field_3_first_column;
- }
-
- public byte getLastColumn() {
- return field_4_last_column;
- }
+ /* package */ Reference(int firstRow, int lastRow, int firstColumn, int lastColumn) {
+ _firstRow = firstRow;
+ _lastRow = lastRow;
+ _firstCol = firstColumn;
+ _lastCol = lastColumn;
+ }
+ /* package */ Reference(RecordInputStream in) {
+ this(in.readUShort(), in.readUShort(), in.readUByte(), in.readUByte());
+ }
+ public void serialize(int offset, byte[] data) {
+ LittleEndian.putUShort(data, offset + 0, _firstRow);
+ LittleEndian.putUShort(data, offset + 2, _lastRow);
+ LittleEndian.putByte(data, offset + 4, _firstCol);
+ LittleEndian.putByte(data, offset + 6, _lastCol);
+ }
+
+ public int getFirstRow() {
+ return _firstRow;
+ }
+ public int getLastRow() {
+ return _lastRow;
+ }
+ public int getFirstColumn() {
+ return _firstCol;
+ }
+ public int getLastColumn() {
+ return _lastCol;
+ }
}
- public SelectionRecord()
- {
+ /**
+ * Creates a default selection record (cell A1, in pane ID 3)
+ */
+ public SelectionRecord(int activeCellRow, int activeCellCol) {
+ field_1_pane = 3; // pane id 3 is always present. see OOO sec 5.75 'PANE'
+ field_2_row_active_cell = activeCellRow;
+ field_3_col_active_cell = activeCellCol;
+ field_4_active_cell_ref_index = 0;
+ field_6_refs = new Reference[] {
+ new Reference(activeCellRow, activeCellRow, activeCellCol, activeCellCol),
+ };
}
/**
@@ -86,41 +95,33 @@ public class SelectionRecord
* @param in the RecordInputstream to read the record from
*/
- public SelectionRecord(RecordInputStream in)
- {
+ public SelectionRecord(RecordInputStream in) {
super(in);
}
- protected void validateSid(short id)
- {
- if (id != sid)
- {
+ protected void validateSid(short id) {
+ if (id != sid) {
throw new RecordFormatException("NOT A valid Selection RECORD");
}
}
- protected void fillFields(RecordInputStream in)
- {
+ protected void fillFields(RecordInputStream in) {
field_1_pane = in.readByte();
- //field_2_row_active_cell = LittleEndian.getShort(data, 1 + offset);
field_2_row_active_cell = in.readUShort();
field_3_col_active_cell = in.readShort();
- field_4_ref_active_cell = in.readShort();
- field_5_num_refs = in.readShort();
+ field_4_active_cell_ref_index = in.readShort();
+ int field_5_num_refs = in.readUShort();
- field_6_refs = new ArrayList(field_5_num_refs);
- for (int i=0; i Title: HSSFCellRangeAddress 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. Copyright: Copyright (c) 2004 Company: Title: HSSFDataValidation Description: Utilty class for creating data validation cells Copyright: Copyright (c) 2004 Company: true
if in list validations the string list is explicitly given in the
+ * formula, false
otherwise
+ */
+ public boolean getListExplicitFormula() {
+ return (opt_string_list_formula.isSet(_option_flags));
+ }
+
+ /**
+ * @return true
if empty values are allowed in cells, false
otherwise
+ */
+ public boolean getEmptyCellAllowed() {
+ return (opt_empty_cell_allowed.isSet(_option_flags));
+ }
+
+
+ /**
+ * @return true
if drop down arrow should be suppressed when list validation is
+ * used, false
otherwise
+ */
+ public boolean getSuppressDropdownArrow() {
+ return (opt_suppress_dropdown_arrow.isSet(_option_flags));
+ }
+
+ /**
+ * @return true
if a prompt window should appear when cell is selected, false
otherwise
+ */
+ public boolean getShowPromptOnCellSelected() {
+ return (opt_show_prompt_on_cell_selected.isSet(_option_flags));
+ }
+
+ /**
+ * @return true
if an error window should appear when an invalid value is entered
+ * in the cell, false
otherwise
+ */
+ public boolean getShowErrorOnInvalidValue() {
+ return (opt_show_error_on_invalid_value.isSet(_option_flags));
+ }
+
+ /**
+ * get the condition operator
+ * @return the condition operator
+ * @see org.apache.poi.hssf.util.HSSFDataValidation utility class
+ */
+ public int getConditionOperator() {
+ return opt_condition_operator.getValue(_option_flags);
+ }
+ // <-- end option flags
+
+
+
+
+ public CellRangeAddressList getCellRangeAddress() {
+ return this._regions;
+ }
+
+
+ public String toString() {
+ StringBuffer sb = new StringBuffer();
+ sb.append("[DV]\n");
+ sb.append(" options=").append(Integer.toHexString(_option_flags));
+ sb.append(" title-prompt=").append(formatTextTitle(_promptTitle));
+ sb.append(" title-error=").append(formatTextTitle(_errorTitle));
+ sb.append(" text-prompt=").append(formatTextTitle(_promptText));
+ sb.append(" text-error=").append(formatTextTitle(_errorText));
+ sb.append("\n");
+ appendFormula(sb, "Formula 1:", _formula1);
+ appendFormula(sb, "Formula 2:", _formula2);
+ sb.append("Regions: ");
+ int nRegions = _regions.countRanges();
+ for(int i=0; i
+ * Title: Merged Cells Record (0x00E5)
+ *
* Description: Optional record defining a square area of cells to "merged" into
* one cell.
* REFERENCE: NONE (UNDOCUMENTED PRESENTLY)
* @author Andrew C. Oliver (acoliver at apache dot org)
* @version 2.0-pre
*/
-public class MergeCellsRecord
- extends Record
-{
- public final static short sid = 0xe5;
- private ArrayList field_2_regions;
+public final class MergeCellsRecord extends Record {
+ public final static short sid = 0x00E5;
+ private CellRangeAddressList _regions;
- public MergeCellsRecord()
- {
+ /**
+ * Creates an empty MergedCellsRecord
+ */
+ public MergeCellsRecord() {
+ _regions = new CellRangeAddressList();
}
/**
* Constructs a MergedCellsRecord and sets its fields appropriately
* @param in the RecordInputstream to read the record from
*/
-
- public MergeCellsRecord(RecordInputStream in)
- {
+ public MergeCellsRecord(RecordInputStream in) {
super(in);
}
- protected void fillFields(RecordInputStream in)
- {
- short numAreas = in.readShort();
- field_2_regions = new ArrayList(numAreas + 10);
-
- for (int k = 0; k < numAreas; k++)
- {
- MergedRegion region =
- new MergedRegion(in.readShort(), in.readShort(),
- in.readShort(), in.readShort());
-
- field_2_regions.add(region);
- }
+ protected void fillFields(RecordInputStream in) {
+ _regions = new org.apache.poi.hssf.util.CellRangeAddressList(in);
}
/**
@@ -73,27 +58,8 @@ public class MergeCellsRecord
* ahead and delete the record.
* @return number of areas
*/
-
- public short getNumAreas()
- {
- //if the array size is larger than a short (65536), the record can't hold that many merges anyway
- if (field_2_regions == null) return 0;
- return (short)field_2_regions.size();
- }
-
- /**
- * set the number of merged areas. You do not need to call this if you use addArea,
- * it will be incremented automatically or decremented when an area is removed. If
- * you are setting this to 0 then you are a terrible person. Just remove the record.
- * (just kidding about you being a terrible person..hehe)
- * @deprecated We now link the size to the actual array of merged regions
- * @see #getNumAreas()
- * @param numareas number of areas
- */
-
- public void setNumAreas(short numareas)
- {
-
+ public short getNumAreas() {
+ return (short)_regions.countRanges();
}
/**
@@ -101,178 +67,84 @@ public class MergeCellsRecord
* be correct provided you do not add ahead of or remove ahead of it (in which case
* you should increment or decrement appropriately....in other words its an arrayList)
*
- * @param rowfrom - the upper left hand corner's row
- * @param colfrom - the upper left hand corner's col
- * @param rowto - the lower right hand corner's row
- * @param colto - the lower right hand corner's col
+ * @param firstRow - the upper left hand corner's row
+ * @param firstCol - the upper left hand corner's col
+ * @param lastRow - the lower right hand corner's row
+ * @param lastCol - the lower right hand corner's col
* @return new index of said area (don't depend on it if you add/remove)
*/
-
- //public int addArea(short rowfrom, short colfrom, short rowto, short colto)
- public int addArea(int rowfrom, short colfrom, int rowto, short colto)
- {
- if (field_2_regions == null)
- {
- field_2_regions = new ArrayList(10);
- }
- MergedRegion region = new MergedRegion(rowfrom, rowto, colfrom,
- colto);
-
- field_2_regions.add(region);
- return field_2_regions.size() - 1;
+ public void addArea(int firstRow, int firstCol, int lastRow, int lastCol) {
+ _regions.addCellRangeAddress(firstRow, firstCol, lastRow, lastCol);
}
/**
* essentially unmerge the cells in the "area" stored at the passed in index
- * @param area index
+ * @param areaIndex
*/
-
- public void removeAreaAt(int area)
- {
- field_2_regions.remove(area);
+ public void removeAreaAt(int areaIndex) {
+ _regions.remove(areaIndex);
}
/**
- * return the MergedRegion at the given index.
- *
- * @return MergedRegion representing the area that is Merged (r1,c1 - r2,c2)
+ * @return MergedRegion at the given index representing the area that is Merged (r1,c1 - r2,c2)
*/
-
- public MergedRegion getAreaAt(int index)
- {
- return ( MergedRegion ) field_2_regions.get(index);
+ public CellRangeAddress getAreaAt(int index) {
+ return _regions.getCellRangeAddress(index);
}
- public int getRecordSize()
- {
- int retValue;
-
- retValue = 6 + (8 * field_2_regions.size());
- return retValue;
+ public int getRecordSize() {
+ return 4 + _regions.getSize();
}
- public short getSid()
- {
+ public short getSid() {
return sid;
}
- public int serialize(int offset, byte [] data)
- {
- int recordsize = getRecordSize();
- int pos = 6;
+ public int serialize(int offset, byte [] data) {
+ int dataSize = _regions.getSize();
LittleEndian.putShort(data, offset + 0, sid);
- LittleEndian.putShort(data, offset + 2, ( short ) (recordsize - 4));
- LittleEndian.putShort(data, offset + 4, getNumAreas());
- for (int k = 0; k < getNumAreas(); k++)
- {
- MergedRegion region = getAreaAt(k);
-
- //LittleEndian.putShort(data, offset + pos, region.row_from);
- LittleEndian.putShort(data, offset + pos, ( short ) region.row_from);
- pos += 2;
- //LittleEndian.putShort(data, offset + pos, region.row_to);
- LittleEndian.putShort(data, offset + pos, ( short ) region.row_to);
- pos += 2;
- LittleEndian.putShort(data, offset + pos, region.col_from);
- pos += 2;
- LittleEndian.putShort(data, offset + pos, region.col_to);
- pos += 2;
- }
- return recordsize;
+ LittleEndian.putUShort(data, offset + 2, dataSize);
+ _regions.serialize(offset + 4, data);
+ return 4 + dataSize;
}
- public String toString()
- {
+ public String toString() {
StringBuffer retval = new StringBuffer();
retval.append("[MERGEDCELLS]").append("\n");
retval.append(" .sid =").append(sid).append("\n");
retval.append(" .numregions =").append(getNumAreas())
.append("\n");
- for (int k = 0; k < getNumAreas(); k++)
- {
- MergedRegion region = ( MergedRegion ) field_2_regions.get(k);
+ for (int k = 0; k < _regions.countRanges(); k++) {
+ CellRangeAddress region = _regions.getCellRangeAddress(k);
- retval.append(" .rowfrom =").append(region.row_from)
+ retval.append(" .rowfrom =").append(region.getFirstRow())
.append("\n");
- retval.append(" .colfrom =").append(region.col_from)
+ retval.append(" .colfrom =").append(region.getFirstColumn())
.append("\n");
- retval.append(" .rowto =").append(region.row_to)
+ retval.append(" .rowto =").append(region.getLastRow())
.append("\n");
- retval.append(" .colto =").append(region.col_to)
+ retval.append(" .colto =").append(region.getLastColumn())
.append("\n");
}
retval.append("[MERGEDCELLS]").append("\n");
return retval.toString();
}
- protected void validateSid(short id)
- {
- if (id != sid)
- {
+ protected void validateSid(short id) {
+ if (id != sid) {
throw new RecordFormatException("NOT A MERGEDCELLS RECORD!! "
+ id);
}
}
- /**
- * this is a low level representation of a MergedRegion of cells. It is an
- * inner class because we do not want it used without reference to this class.
- *
- */
-
- public class MergedRegion
- {
-
- /**
- * create a merged region all in one stroke.
- */
-
- //public MergedRegion(short row_from, short row_to, short col_from,
- public MergedRegion(int row_from, int row_to, short col_from,
- short col_to)
- {
- this.row_from = row_from;
- this.row_to = row_to;
- this.col_from = col_from;
- this.col_to = col_to;
- }
-
- /**
- * upper lefthand corner row
- */
-
- //public short row_from;
- public int row_from;
-
- /**
- * lower right hand corner row
- */
-
- //public short row_to;
- public int row_to;
-
- /**
- * upper right hand corner col
- */
-
- public short col_from;
-
- /**
- * lower right hand corner col
- */
-
- public short col_to;
- }
-
public Object clone() {
MergeCellsRecord rec = new MergeCellsRecord();
- rec.field_2_regions = new ArrayList();
- Iterator iterator = field_2_regions.iterator();
- while (iterator.hasNext()) {
- MergedRegion oldRegion = (MergedRegion)iterator.next();
- rec.addArea(oldRegion.row_from, oldRegion.col_from, oldRegion.row_to, oldRegion.col_to);
+ for (int k = 0; k < _regions.countRanges(); k++) {
+ CellRangeAddress oldRegion = _regions.getCellRangeAddress(k);
+ rec.addArea(oldRegion.getFirstRow(), oldRegion.getFirstColumn(),
+ oldRegion.getLastRow(), oldRegion.getLastColumn());
}
return rec;
diff --git a/src/java/org/apache/poi/hssf/record/SelectionRecord.java b/src/java/org/apache/poi/hssf/record/SelectionRecord.java
index 8ad4d0cef..2f36ea644 100644
--- a/src/java/org/apache/poi/hssf/record/SelectionRecord.java
+++ b/src/java/org/apache/poi/hssf/record/SelectionRecord.java
@@ -1,4 +1,3 @@
-
/* ====================================================================
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
@@ -15,70 +14,80 @@
See the License for the specific language governing permissions and
limitations under the License.
==================================================================== */
-
package org.apache.poi.hssf.record;
-import java.util.*;
-
import org.apache.poi.util.LittleEndian;
/**
- * Title: Selection Record
- * Possible return codes are:
- * NO_INTERSECTION - the specified range is outside of this range;
- * OVERLAP - both ranges partially overlap;
- * INSIDE - the specified range is inside of this one
- * ENCLOSES - the specified range encloses (possibly exactly the same as) this range
- */
- 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:
- *
+ * Possible return codes are:
+ * NO_INTERSECTION - the specified range is outside of this range;
+ * OVERLAP - both ranges partially overlap;
+ * INSIDE - the specified range is inside of this one
+ * ENCLOSES - the specified range encloses (possibly exactly the same as) this range
+ */
+ 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:
+ * true
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);
+ }
+}
diff --git a/src/java/org/apache/poi/hssf/record/formula/NumberPtg.java b/src/java/org/apache/poi/hssf/record/formula/NumberPtg.java
index dc28e705a..f02275860 100644
--- a/src/java/org/apache/poi/hssf/record/formula/NumberPtg.java
+++ b/src/java/org/apache/poi/hssf/record/formula/NumberPtg.java
@@ -36,7 +36,7 @@ public final class NumberPtg extends ScalarConstantPtg {
/** Create a NumberPtg from a byte array read from disk */
public NumberPtg(RecordInputStream in)
{
- field_1_value = in.readDouble();
+ this(in.readDouble());
}
/** Create a NumberPtg from a string representation of the number
@@ -45,9 +45,12 @@ public final class NumberPtg extends ScalarConstantPtg {
* @param value : String representation of a floating point number
*/
public NumberPtg(String value) {
- field_1_value = Double.parseDouble(value);
+ this(Double.parseDouble(value));
}
+ public NumberPtg(double value) {
+ field_1_value = value;
+ }
public double getValue()
{
@@ -67,6 +70,15 @@ public final class NumberPtg extends ScalarConstantPtg {
public String toFormulaString(Workbook book)
{
- return "" + getValue();
+ // TODO - java's rendering of double values is not quite same as excel's
+ return String.valueOf(field_1_value);
+ }
+
+ public String toString() {
+ StringBuffer sb = new StringBuffer(64);
+ sb.append(getClass().getName()).append(" [");
+ sb.append(field_1_value);
+ sb.append("]");
+ return sb.toString();
}
}
diff --git a/src/java/org/apache/poi/hssf/record/formula/Ptg.java b/src/java/org/apache/poi/hssf/record/formula/Ptg.java
index 69464f8f4..50f3450d2 100644
--- a/src/java/org/apache/poi/hssf/record/formula/Ptg.java
+++ b/src/java/org/apache/poi/hssf/record/formula/Ptg.java
@@ -41,6 +41,7 @@ import org.apache.poi.ss.usermodel.Workbook;
* @author Jason Height (jheight at chariot dot net dot au)
*/
public abstract class Ptg implements Cloneable {
+ public static final Ptg[] EMPTY_PTG_ARRAY = { };
/* convert infix order ptg list to rpn order ptg list
* @return List ptgs in RPN order
@@ -250,6 +251,9 @@ public abstract class Ptg implements Cloneable {
}
}
private static Ptg[] toPtgArray(List l) {
+ if (l.isEmpty()) {
+ return EMPTY_PTG_ARRAY;
+ }
Ptg[] result = new Ptg[l.size()];
l.toArray(result);
return result;
diff --git a/src/java/org/apache/poi/hssf/usermodel/DVConstraint.java b/src/java/org/apache/poi/hssf/usermodel/DVConstraint.java
new file mode 100644
index 000000000..a1027ccfa
--- /dev/null
+++ b/src/java/org/apache/poi/hssf/usermodel/DVConstraint.java
@@ -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 SimpleDateFormat can be used
+ * @see SimpleDateFormat
+ */
+ 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 null
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 null
if numberStr is null
+ */
+ 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 null
if timeStr is null
+ */
+ private static Double convertTime(String timeStr) {
+ if (timeStr == null) {
+ return null;
+ }
+ return new Double(HSSFDateUtil.convertTime(timeStr));
+ }
+ /**
+ * @param dateFormat pass null
for default YYYYMMDD
+ * @return null
if timeStr is null
+ */
+ 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 null
+ */
+ 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 true
if this constraint is a 'list' validation
+ */
+ public boolean isListValidationType() {
+ return _validationType == VT.LIST;
+ }
+ /**
+ * Convenience method
+ * @return true
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 null
+ */
+ 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 null
+ */
+ 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 null
+ */
+ 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 null
+ */
+ public Double getValue2() {
+ return _value2;
+ }
+ /**
+ * Sets a numeric value for expression 2.
+ */
+ public void setValue2(double value2) {
+ _formula2 = null;
+ _value2 = new Double(value2);
+ }
+}
diff --git a/src/java/org/apache/poi/hssf/usermodel/HSSFCell.java b/src/java/org/apache/poi/hssf/usermodel/HSSFCell.java
index 3283f98dd..f110d71f9 100644
--- a/src/java/org/apache/poi/hssf/usermodel/HSSFCell.java
+++ b/src/java/org/apache/poi/hssf/usermodel/HSSFCell.java
@@ -34,7 +34,24 @@ import java.util.Iterator;
import org.apache.poi.hssf.model.FormulaParser;
import org.apache.poi.hssf.model.Sheet;
import org.apache.poi.hssf.model.Workbook;
-import org.apache.poi.hssf.record.*;
+import org.apache.poi.hssf.record.BlankRecord;
+import org.apache.poi.hssf.record.BoolErrRecord;
+import org.apache.poi.hssf.record.CellValueRecordInterface;
+import org.apache.poi.hssf.record.CommonObjectDataSubRecord;
+import org.apache.poi.hssf.record.DrawingRecord;
+import org.apache.poi.hssf.record.EOFRecord;
+import org.apache.poi.hssf.record.ExtendedFormatRecord;
+import org.apache.poi.hssf.record.FormulaRecord;
+import org.apache.poi.hssf.record.HyperlinkRecord;
+import org.apache.poi.hssf.record.LabelSSTRecord;
+import org.apache.poi.hssf.record.NoteRecord;
+import org.apache.poi.hssf.record.NumberRecord;
+import org.apache.poi.hssf.record.ObjRecord;
+import org.apache.poi.hssf.record.Record;
+import org.apache.poi.hssf.record.StringRecord;
+import org.apache.poi.hssf.record.SubRecord;
+import org.apache.poi.hssf.record.TextObjectRecord;
+import org.apache.poi.hssf.record.UnicodeString;
import org.apache.poi.hssf.record.aggregates.FormulaRecordAggregate;
import org.apache.poi.hssf.record.formula.Ptg;
import org.apache.poi.ss.usermodel.Cell;
@@ -1055,11 +1072,18 @@ public class HSSFCell implements Cell
}
/**
- * Assign a comment to this cell
+ * Assign a comment to this cell. If the supplied
+ * comment is null, the comment for this cell
+ * will be removed.
*
* @param comment comment associated with this cell
*/
public void setCellComment(Comment comment){
+ if(comment == null) {
+ removeCellComment();
+ return;
+ }
+
this.comment = (HSSFComment) comment;
this.comment.setRow((short)record.getRow());
this.comment.setColumn(record.getColumn());
@@ -1076,6 +1100,49 @@ public class HSSFCell implements Cell
}
return comment;
}
+
+ /**
+ * Removes the comment for this cell, if
+ * there is one.
+ * WARNING - some versions of excel will loose
+ * all comments after performing this action!
+ */
+ public void removeCellComment() {
+ HSSFComment comment = findCellComment(sheet, record.getRow(), record.getColumn());
+ this.comment = null;
+
+ if(comment == null) {
+ // Nothing to do
+ return;
+ }
+
+ // Zap the underlying NoteRecord
+ sheet.getRecords().remove(comment.getNoteRecord());
+
+ // If we have a TextObjectRecord, is should
+ // be proceeed by:
+ // MSODRAWING with container
+ // OBJ
+ // MSODRAWING with EscherTextboxRecord
+ if(comment.getTextObjectRecord() != null) {
+ TextObjectRecord txo = comment.getTextObjectRecord();
+ int txoAt = sheet.getRecords().indexOf(txo);
+
+ if(sheet.getRecords().get(txoAt-3) instanceof DrawingRecord &&
+ sheet.getRecords().get(txoAt-2) instanceof ObjRecord &&
+ sheet.getRecords().get(txoAt-1) instanceof DrawingRecord) {
+ // Zap these, in reverse order
+ sheet.getRecords().remove(txoAt-1);
+ sheet.getRecords().remove(txoAt-2);
+ sheet.getRecords().remove(txoAt-3);
+ } else {
+ throw new IllegalStateException("Found the wrong records before the TextObjectRecord, can't remove comment");
+ }
+
+ // Now remove the text record
+ sheet.getRecords().remove(txo);
+ }
+ }
/**
* Cell comment finder.
diff --git a/src/java/org/apache/poi/hssf/usermodel/HSSFComment.java b/src/java/org/apache/poi/hssf/usermodel/HSSFComment.java
index 85eca2d47..0c8a16e42 100644
--- a/src/java/org/apache/poi/hssf/usermodel/HSSFComment.java
+++ b/src/java/org/apache/poi/hssf/usermodel/HSSFComment.java
@@ -156,4 +156,13 @@ public class HSSFComment extends HSSFTextbox implements Comment {
}
super.setString(string);
}
+
+ /**
+ * Returns the underlying Note record
+ */
+ protected NoteRecord getNoteRecord() { return note; }
+ /**
+ * Returns the underlying Text record
+ */
+ protected TextObjectRecord getTextObjectRecord() { return txo; }
}
diff --git a/src/java/org/apache/poi/hssf/usermodel/HSSFConditionalFormatting.java b/src/java/org/apache/poi/hssf/usermodel/HSSFConditionalFormatting.java
index 6c798abcf..3029515f4 100644
--- a/src/java/org/apache/poi/hssf/usermodel/HSSFConditionalFormatting.java
+++ b/src/java/org/apache/poi/hssf/usermodel/HSSFConditionalFormatting.java
@@ -16,11 +16,10 @@
==================================================================== */
package org.apache.poi.hssf.usermodel;
-import org.apache.poi.hssf.record.CFHeaderRecord;
import org.apache.poi.hssf.record.CFRuleRecord;
import org.apache.poi.hssf.record.aggregates.CFRecordsAggregate;
-import org.apache.poi.hssf.record.cf.CellRange;
-import org.apache.poi.hssf.util.Region;
+import org.apache.poi.ss.util.CellRangeAddress;
+import org.apache.poi.ss.util.Region;
/**
* HSSFConditionalFormatting class encapsulates all settings of Conditional Formatting.
@@ -96,13 +95,18 @@ public final class HSSFConditionalFormatting
}
/**
- * @return array of Regions. never null
+ * @deprecated (Aug-2008) use {@link HSSFConditionalFormatting#getFormattingRanges()}
*/
public Region[] getFormattingRegions()
{
- CFHeaderRecord cfh = cfAggregate.getHeader();
- CellRange[] cellRanges = cfh.getCellRanges();
- return CellRange.convertCellRangesToRegions(cellRanges);
+ CellRangeAddress[] cellRanges = getFormattingRanges();
+ return Region.convertCellRangesToRegions(cellRanges);
+ }
+ /**
+ * @return array of CellRangeAddresss. never null
+ */
+ public CellRangeAddress[] getFormattingRanges() {
+ return cfAggregate.getHeader().getCellRanges();
}
/**
diff --git a/src/java/org/apache/poi/hssf/usermodel/HSSFDataValidation.java b/src/java/org/apache/poi/hssf/usermodel/HSSFDataValidation.java
new file mode 100644
index 000000000..0591158b6
--- /dev/null
+++ b/src/java/org/apache/poi/hssf/usermodel/HSSFDataValidation.java
@@ -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 true
if this object should treats empty as valid value , false
+ * 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 true
if a list should display the values into a drop down list ,
+ * false
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 true
if an prompt box should be displayed , false
otherwise
+ */
+ public void setShowPromptBox(boolean show) {
+ _showPromptBox = show;
+ }
+
+ /**
+ * @param show true
if an prompt box should be displayed , false
otherwise
+ */
+ public boolean getShowPromptBox() {
+ return _showPromptBox;
+ }
+
+ /**
+ * 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) {
+ _showErrorBox = show;
+ }
+
+ /**
+ * @return true
if an error box should be displayed , false
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 null
+ */
+ public String getPromptBoxTitle() {
+ return _prompt_title;
+ }
+
+ /**
+ * @return Prompt box's text or null
+ */
+ 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 null
+ */
+ public String getErrorBoxTitle() {
+ return _error_title;
+ }
+
+ /**
+ * @return Error box's text or null
+ */
+ 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);
+ }
+}
diff --git a/src/java/org/apache/poi/hssf/usermodel/HSSFSheet.java b/src/java/org/apache/poi/hssf/usermodel/HSSFSheet.java
index d4bc9613d..dcf68b9cf 100644
--- a/src/java/org/apache/poi/hssf/usermodel/HSSFSheet.java
+++ b/src/java/org/apache/poi/hssf/usermodel/HSSFSheet.java
@@ -28,7 +28,6 @@ import java.text.NumberFormat;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
-import java.util.Stack;
import java.util.TreeMap;
import org.apache.poi.ddf.EscherRecord;
@@ -49,13 +48,13 @@ import org.apache.poi.hssf.record.SCLRecord;
import org.apache.poi.hssf.record.VCenterRecord;
import org.apache.poi.hssf.record.WSBoolRecord;
import org.apache.poi.hssf.record.WindowTwoRecord;
+import org.apache.poi.hssf.record.aggregates.DataValidityTable;
import org.apache.poi.hssf.record.formula.Ptg;
import org.apache.poi.hssf.record.formula.RefPtg;
-import org.apache.poi.hssf.util.HSSFCellRangeAddress;
-import org.apache.poi.hssf.util.HSSFDataValidation;
import org.apache.poi.hssf.util.PaneInformation;
import org.apache.poi.ss.usermodel.CellStyle;
import org.apache.poi.ss.usermodel.Row;
+import org.apache.poi.ss.util.CellRangeAddress;
import org.apache.poi.hssf.util.Region;
import org.apache.poi.util.POILogFactory;
import org.apache.poi.util.POILogger;
@@ -393,92 +392,19 @@ public class HSSFSheet implements org.apache.poi.ss.usermodel.Sheet
/**
* Creates a data validation object
- * @param obj_validation The Data validation object settings
+ * @param dataValidation The Data validation object settings
*/
- public void addValidationData(HSSFDataValidation obj_validation)
- {
- if ( obj_validation == null )
- {
- return;
+ public void addValidationData(HSSFDataValidation dataValidation) {
+ if (dataValidation == null) {
+ throw new IllegalArgumentException("objValidation must not be null");
}
- DVALRecord dvalRec = (DVALRecord)sheet.findFirstRecordBySid( DVALRecord.sid );
- int eofLoc = sheet.findFirstRecordLocBySid( EOFRecord.sid );
- if ( dvalRec == null )
- {
- dvalRec = new DVALRecord();
- sheet.getRecords().add( eofLoc, dvalRec );
- }
- int curr_dvRecNo = dvalRec.getDVRecNo();
- dvalRec.setDVRecNo(curr_dvRecNo+1);
+ DataValidityTable dvt = sheet.getOrCreateDataValidityTable();
- //create dv record
- DVRecord dvRecord = new DVRecord();
-
- //dv record's option flags
- dvRecord.setDataType( obj_validation.getDataValidationType() );
- dvRecord.setErrorStyle(obj_validation.getErrorStyle());
- dvRecord.setEmptyCellAllowed(obj_validation.getEmptyCellAllowed());
- dvRecord.setSurppresDropdownArrow(obj_validation.getSurppressDropDownArrow());
- dvRecord.setShowPromptOnCellSelected(obj_validation.getShowPromptBox());
- dvRecord.setShowErrorOnInvalidValue(obj_validation.getShowErrorBox());
- dvRecord.setConditionOperator(obj_validation.getOperator());
-
- //string fields
- dvRecord.setStringField( DVRecord.STRING_PROMPT_TITLE,obj_validation.getPromptBoxTitle());
- dvRecord.setStringField( DVRecord.STRING_PROMPT_TEXT, obj_validation.getPromptBoxText());
- dvRecord.setStringField( DVRecord.STRING_ERROR_TITLE, obj_validation.getErrorBoxTitle());
- dvRecord.setStringField( DVRecord.STRING_ERROR_TEXT, obj_validation.getErrorBoxText());
-
- //formula fields ( size and data )
- String str_formula = obj_validation.getFirstFormula();
- FormulaParser fp = new FormulaParser(str_formula, workbook);
- fp.parse();
- Stack ptg_arr = new Stack();
- Ptg[] ptg = fp.getRPNPtg();
- int size = 0;
- for (int k = 0; k < ptg.length; k++)
- {
- if ( ptg[k] instanceof org.apache.poi.hssf.record.formula.AreaPtg )
- {
- //we should set ptgClass to Ptg.CLASS_REF and explicit formula string to false
- ptg[k].setClass(Ptg.CLASS_REF);
- obj_validation.setExplicitListFormula(false);
- }
- size += ptg[k].getSize();
- ptg_arr.push(ptg[k]);
- }
- dvRecord.setFirstFormulaRPN(ptg_arr);
- dvRecord.setFirstFormulaSize((short)size);
-
- dvRecord.setListExplicitFormula(obj_validation.getExplicitListFormula());
-
- if ( obj_validation.getSecondFormula() != null )
- {
- str_formula = obj_validation.getSecondFormula();
- fp = new FormulaParser(str_formula, workbook);
- fp.parse();
- ptg_arr = new Stack();
- ptg = fp.getRPNPtg();
- size = 0;
- for (int k = 0; k < ptg.length; k++)
- {
- size += ptg[k].getSize();
- ptg_arr.push(ptg[k]);
- }
- dvRecord.setSecFormulaRPN(ptg_arr);
- dvRecord.setSecFormulaSize((short)size);
- }
-
- //dv records cell range field
- HSSFCellRangeAddress cell_range = new HSSFCellRangeAddress();
- cell_range.addADDRStructure(obj_validation.getFirstRow(), obj_validation.getFirstColumn(), obj_validation.getLastRow(), obj_validation.getLastColumn());
- dvRecord.setCellRangeAddress(cell_range);
-
- //add dv record
- eofLoc = sheet.findFirstRecordLocBySid( EOFRecord.sid );
- sheet.getRecords().add( eofLoc, dvRecord );
+ DVRecord dvRecord = dataValidation.createDVRecord(workbook);
+ dvt.addDataValidation(dvRecord);
}
+
/**
* Get the visibility state for a given column.
* @param column - the column to get (0-based)
@@ -610,19 +536,28 @@ public class HSSFSheet implements org.apache.poi.ss.usermodel.Sheet
}
/**
- * adds a merged region of cells (hence those cells form one)
- * @param region (rowfrom/colfrom-rowto/colto) to merge
- * @return index of this region
+ * @deprecated (Aug-2008) use CellRangeAddress instead of Region
*/
public int addMergedRegion(org.apache.poi.ss.util.Region region)
{
- //return sheet.addMergedRegion((short) region.getRowFrom(),
return sheet.addMergedRegion( region.getRowFrom(),
region.getColumnFrom(),
//(short) region.getRowTo(),
region.getRowTo(),
region.getColumnTo());
}
+ /**
+ * adds a merged region of cells (hence those cells form one)
+ * @param region (rowfrom/colfrom-rowto/colto) to merge
+ * @return index of this region
+ */
+ public int addMergedRegion(CellRangeAddress region)
+ {
+ return sheet.addMergedRegion( region.getFirstRow(),
+ region.getFirstColumn(),
+ region.getLastRow(),
+ region.getLastColumn());
+ }
/**
* Whether a record must be inserted or not at generation to indicate that
@@ -659,7 +594,7 @@ public class HSSFSheet implements org.apache.poi.ss.usermodel.Sheet
/**
* TODO: Boolean not needed, remove after next release
- * @deprecated use getVerticallyCenter() instead
+ * @deprecated (Mar-2008) use getVerticallyCenter() instead
*/
public boolean getVerticallyCenter(boolean value) {
return getVerticallyCenter();
@@ -724,14 +659,19 @@ public class HSSFSheet implements org.apache.poi.ss.usermodel.Sheet
}
/**
- * gets the region at a particular index
- * @param index of the region to fetch
- * @return the merged region (simple eh?)
+ * @deprecated (Aug-2008) use {@link HSSFSheet#getMergedRegion(int)}
*/
-
- public Region getMergedRegionAt(int index)
- {
- return new Region(sheet.getMergedRegionAt(index));
+ public Region getMergedRegionAt(int index) {
+ CellRangeAddress cra = getMergedRegion(index);
+
+ return new Region(cra.getFirstRow(), (short)cra.getFirstColumn(),
+ cra.getLastRow(), (short)cra.getLastColumn());
+ }
+ /**
+ * @return the merged region at the specified index
+ */
+ public CellRangeAddress getMergedRegion(int index) {
+ return (CellRangeAddress)sheet.getMergedRegionAt(index);
}
/**
@@ -1164,36 +1104,43 @@ public class HSSFSheet implements org.apache.poi.ss.usermodel.Sheet
protected void shiftMerged(int startRow, int endRow, int n, boolean isRow) {
List shiftedRegions = new ArrayList();
//move merged regions completely if they fall within the new region boundaries when they are shifted
- for (int i = 0; i < this.getNumMergedRegions(); i++) {
- Region merged = this.getMergedRegionAt(i);
+ for (int i = 0; i < getNumMergedRegions(); i++) {
+ CellRangeAddress merged = getMergedRegion(i);
- boolean inStart = (merged.getRowFrom() >= startRow || merged.getRowTo() >= startRow);
- boolean inEnd = (merged.getRowTo() <= endRow || merged.getRowFrom() <= endRow);
+ boolean inStart= (merged.getFirstRow() >= startRow || merged.getLastRow() >= startRow);
+ boolean inEnd = (merged.getFirstRow() <= endRow || merged.getLastRow() <= endRow);
- //dont check if it's not within the shifted area
- if (! (inStart && inEnd)) continue;
+ //don't check if it's not within the shifted area
+ if (!inStart || !inEnd) {
+ continue;
+ }
//only shift if the region outside the shifted rows is not merged too
- if (!merged.contains(startRow-1, (short)0) && !merged.contains(endRow+1, (short)0)){
- merged.setRowFrom(merged.getRowFrom()+n);
- merged.setRowTo(merged.getRowTo()+n);
+ if (!containsCell(merged, startRow-1, 0) && !containsCell(merged, endRow+1, 0)){
+ merged.setFirstRow(merged.getFirstRow()+n);
+ merged.setLastRow(merged.getLastRow()+n);
//have to remove/add it back
shiftedRegions.add(merged);
- this.removeMergedRegion(i);
+ removeMergedRegion(i);
i = i -1; // we have to back up now since we removed one
-
}
-
}
- //readd so it doesn't get shifted again
+ //read so it doesn't get shifted again
Iterator iterator = shiftedRegions.iterator();
while (iterator.hasNext()) {
- Region region = (Region)iterator.next();
+ CellRangeAddress region = (CellRangeAddress)iterator.next();
this.addMergedRegion(region);
}
-
+ }
+ private static boolean containsCell(CellRangeAddress cr, int rowIx, int colIx) {
+ if (cr.getFirstRow() <= rowIx && cr.getLastRow() >= rowIx
+ && cr.getFirstColumn() <= colIx && cr.getLastColumn() >= colIx)
+ {
+ return true;
+ }
+ return false;
}
/**
@@ -1812,17 +1759,20 @@ public class HSSFSheet implements org.apache.poi.ss.usermodel.Sheet
HSSFRow row = (HSSFRow) it.next();
HSSFCell cell = row.getCell(column);
- if (cell == null) continue;
+ if (cell == null) {
+ continue;
+ }
int colspan = 1;
for (int i = 0 ; i < getNumMergedRegions(); i++) {
- if (getMergedRegionAt(i).contains(row.getRowNum(), column)) {
+ CellRangeAddress region = getMergedRegion(i);
+ if (containsCell(region, row.getRowNum(), column)) {
if (!useMergedCells) {
// If we're not using merged cells, skip this one and move on to the next.
continue rows;
}
- cell = row.getCell(getMergedRegionAt(i).getColumnFrom());
- colspan = 1+ getMergedRegionAt(i).getColumnTo() - getMergedRegionAt(i).getColumnFrom();
+ cell = row.getCell(region.getFirstColumn());
+ colspan = 1 + region.getLastColumn() - region.getFirstColumn();
}
}
diff --git a/src/java/org/apache/poi/hssf/usermodel/HSSFSheetConditionalFormatting.java b/src/java/org/apache/poi/hssf/usermodel/HSSFSheetConditionalFormatting.java
index 8e8cf40a1..cdfae6cd1 100644
--- a/src/java/org/apache/poi/hssf/usermodel/HSSFSheetConditionalFormatting.java
+++ b/src/java/org/apache/poi/hssf/usermodel/HSSFSheetConditionalFormatting.java
@@ -21,6 +21,7 @@ import org.apache.poi.hssf.model.Sheet;
import org.apache.poi.hssf.record.CFRuleRecord;
import org.apache.poi.hssf.record.aggregates.CFRecordsAggregate;
import org.apache.poi.ss.util.Region;
+import org.apache.poi.ss.util.CellRangeAddress;
/**
* The 'Conditional Formatting' facet of HSSFSheet
@@ -100,7 +101,12 @@ public final class HSSFSheetConditionalFormatting {
return _sheet.addConditionalFormatting(cfraClone);
}
-
+ /**
+ * @deprecated use CellRangeAddress instead of Region
+ */
+ public int addConditionalFormatting(Region[] regions, HSSFConditionalFormattingRule[] cfRules) {
+ return addConditionalFormatting(Region.convertRegionsToCellRanges(regions), cfRules);
+ }
/**
* Allows to add a new Conditional Formatting set to the sheet.
*
@@ -109,8 +115,7 @@ public final class HSSFSheetConditionalFormatting {
*
* @return index of the newly created Conditional Formatting object
*/
-
- public int addConditionalFormatting(Region[] regions, HSSFConditionalFormattingRule[] cfRules) {
+ public int addConditionalFormatting(CellRangeAddress[] regions, HSSFConditionalFormattingRule[] cfRules) {
if (regions == null) {
throw new IllegalArgumentException("regions must not be null");
}
@@ -132,7 +137,7 @@ public final class HSSFSheetConditionalFormatting {
return _sheet.addConditionalFormatting(cfra);
}
- public int addConditionalFormatting(Region[] regions,
+ public int addConditionalFormatting(CellRangeAddress[] regions,
HSSFConditionalFormattingRule rule1)
{
return addConditionalFormatting(regions,
@@ -142,7 +147,7 @@ public final class HSSFSheetConditionalFormatting {
});
}
- public int addConditionalFormatting(Region[] regions,
+ public int addConditionalFormatting(CellRangeAddress[] regions,
HSSFConditionalFormattingRule rule1,
HSSFConditionalFormattingRule rule2)
{
@@ -153,18 +158,6 @@ public final class HSSFSheetConditionalFormatting {
});
}
- public int addConditionalFormatting(Region[] regions,
- HSSFConditionalFormattingRule rule1,
- HSSFConditionalFormattingRule rule2,
- HSSFConditionalFormattingRule rule3)
- {
- return addConditionalFormatting(regions,
- new HSSFConditionalFormattingRule[]
- {
- rule1, rule2, rule3
- });
- }
-
/**
* gets Conditional Formatting object at a particular index
*
diff --git a/src/java/org/apache/poi/hssf/util/CellRangeAddress.java b/src/java/org/apache/poi/hssf/util/CellRangeAddress.java
new file mode 100644
index 000000000..c7ff2653c
--- /dev/null
+++ b/src/java/org/apache/poi/hssf/util/CellRangeAddress.java
@@ -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'
+ *
+ * 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();
+ }
+}
diff --git a/src/java/org/apache/poi/hssf/util/CellRangeAddressList.java b/src/java/org/apache/poi/hssf/util/CellRangeAddressList.java
new file mode 100644
index 000000000..79ea50abb
--- /dev/null
+++ b/src/java/org/apache/poi/hssf/util/CellRangeAddressList.java
@@ -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.
+ * Europe/Copenhagen
, on 2004-03-28 the minute after
* 01:59 CET is 03:00 CEST, if the excel date represents a time between
* 02:00 and 03:00 then it is converted to past 03:00 summer time
- *
+ *
* @param date The Excel date.
* @return Java representation of the date, or null if date is not a valid Excel date
* @see java.util.TimeZone
*/
public static Date getJavaDate(double date) {
- return getJavaDate(date, false);
+ return getJavaDate(date, false);
}
/**
* Given an Excel date with either 1900 or 1904 date windowing,
@@ -158,95 +155,90 @@ public class DateUtil
* @see java.util.TimeZone
*/
public static Date getJavaDate(double date, boolean use1904windowing) {
- if (isValidExcelDate(date)) {
- int startYear = 1900;
- int dayAdjust = -1; // Excel thinks 2/29/1900 is a valid date, which it isn't
- int wholeDays = (int)Math.floor(date);
- if (use1904windowing) {
- startYear = 1904;
- dayAdjust = 1; // 1904 date windowing uses 1/2/1904 as the first day
- }
- else if (wholeDays < 61) {
- // Date is prior to 3/1/1900, so adjust because Excel thinks 2/29/1900 exists
- // If Excel date == 2/29/1900, will become 3/1/1900 in Java representation
- dayAdjust = 0;
- }
- GregorianCalendar calendar = new GregorianCalendar(startYear,0,
- wholeDays + dayAdjust);
- int millisecondsInDay = (int)((date - Math.floor(date)) *
- DAY_MILLISECONDS + 0.5);
- calendar.set(GregorianCalendar.MILLISECOND, millisecondsInDay);
- return calendar.getTime();
- }
- else {
+ if (!isValidExcelDate(date)) {
return null;
}
+ int startYear = 1900;
+ int dayAdjust = -1; // Excel thinks 2/29/1900 is a valid date, which it isn't
+ int wholeDays = (int)Math.floor(date);
+ if (use1904windowing) {
+ startYear = 1904;
+ dayAdjust = 1; // 1904 date windowing uses 1/2/1904 as the first day
+ }
+ else if (wholeDays < 61) {
+ // Date is prior to 3/1/1900, so adjust because Excel thinks 2/29/1900 exists
+ // If Excel date == 2/29/1900, will become 3/1/1900 in Java representation
+ dayAdjust = 0;
+ }
+ GregorianCalendar calendar = new GregorianCalendar(startYear,0,
+ wholeDays + dayAdjust);
+ int millisecondsInDay = (int)((date - Math.floor(date)) *
+ DAY_MILLISECONDS + 0.5);
+ calendar.set(GregorianCalendar.MILLISECOND, millisecondsInDay);
+ return calendar.getTime();
}
-
+
/**
* Given a format ID and its format String, will check to see if the
* format represents a date format or not.
* Firstly, it will check to see if the format ID corresponds to an
- * internal excel date format (eg most US date formats)
+ * internal excel date format (eg most US date formats)
* If not, it will check to see if the format string only contains
* date formatting characters (ymd-/), which covers most
* non US date formats.
- *
+ *
* @param formatIndex The index of the format, eg from ExtendedFormatRecord.getFormatIndex
* @param formatString The format string, eg from FormatRecord.getFormatString
* @see #isInternalDateFormat(int)
*/
public static boolean isADateFormat(int formatIndex, String formatString) {
- // First up, is this an internal date format?
- if(isInternalDateFormat(formatIndex)) {
- return true;
- }
-
- // If we didn't get a real string, it can't be
- if(formatString == null || formatString.length() == 0) {
- return false;
- }
-
- String fs = formatString;
-
- // Translate \- into just -, before matching
- fs = fs.replaceAll("\\\\-","-");
- // And \, into ,
- fs = fs.replaceAll("\\\\,",",");
- // And '\ ' into ' '
- fs = fs.replaceAll("\\\\ "," ");
-
- // If it end in ;@, that's some crazy dd/mm vs mm/dd
- // switching stuff, which we can ignore
- fs = fs.replaceAll(";@", "");
-
- // If it starts with [$-...], then could be a date, but
- // who knows what that starting bit is all about
- fs = fs.replaceAll("^\\[\\$\\-.*?\\]", "");
-
- // If it starts with something like [Black] or [Yellow],
- // then it could be a date
- fs = fs.replaceAll("^\\[[a-zA-Z]+\\]", "");
-
- // Otherwise, check it's only made up, in any case, of:
- // y m d h s - / , . :
- // optionally followed by AM/PM
- // optionally followed by AM/PM
- if(fs.matches("^[yYmMdDhHsS\\-/,. :]+[ampAMP/]*$")) {
- return true;
- }
-
- return false;
+ // First up, is this an internal date format?
+ if(isInternalDateFormat(formatIndex)) {
+ return true;
+ }
+
+ // If we didn't get a real string, it can't be
+ if(formatString == null || formatString.length() == 0) {
+ return false;
+ }
+
+ String fs = formatString;
+
+ // Translate \- into just -, before matching
+ fs = fs.replaceAll("\\\\-","-");
+ // And \, into ,
+ fs = fs.replaceAll("\\\\,",",");
+ // And '\ ' into ' '
+ fs = fs.replaceAll("\\\\ "," ");
+
+ // If it end in ;@, that's some crazy dd/mm vs mm/dd
+ // switching stuff, which we can ignore
+ fs = fs.replaceAll(";@", "");
+
+ // If it starts with [$-...], then could be a date, but
+ // who knows what that starting bit is all about
+ fs = fs.replaceAll("^\\[\\$\\-.*?\\]", "");
+
+ // If it starts with something like [Black] or [Yellow],
+ // then it could be a date
+ fs = fs.replaceAll("^\\[[a-zA-Z]+\\]", "");
+
+ // Otherwise, check it's only made up, in any case, of:
+ // y m d h s - / , . :
+ // optionally followed by AM/PM
+ if(fs.matches("^[yYmMdDhHsS\\-/,. :]+[ampAMP/]*$")) {
+ return true;
+ }
+
+ return false;
}
/**
* Given a format ID this will check whether the format represents
* an internal excel date format or not.
- * @see #isADateFormat(int, java.lang.String)
+ * @see #isADateFormat(int, java.lang.String)
*/
public static boolean isInternalDateFormat(int format) {
- boolean retval =false;
-
switch(format) {
// Internal Date Formats as described on page 427 in
// Microsoft Excel Dev's Kit...
@@ -262,32 +254,25 @@ public class DateUtil
case 0x2d:
case 0x2e:
case 0x2f:
- retval = true;
- break;
-
- default:
- retval = false;
- break;
+ return true;
}
- return retval;
+ return false;
}
/**
* Check if a cell contains a date
- * Since dates are stored internally in Excel as double values
- * we infer it is a date if it is formatted as such.
+ * Since dates are stored internally in Excel as double values
+ * we infer it is a date if it is formatted as such.
* @see #isADateFormat(int, String)
* @see #isInternalDateFormat(int)
*/
public static boolean isCellDateFormatted(Cell cell) {
if (cell == null) return false;
boolean bDate = false;
-
+
double d = cell.getNumericCellValue();
if ( DateUtil.isValidExcelDate(d) ) {
CellStyle style = cell.getCellStyle();
- if(style == null) return false;
-
int i = style.getDataFormat();
String f = style.getDataFormatString();
bDate = isADateFormat(i, f);
@@ -305,7 +290,7 @@ public class DateUtil
public static boolean isCellInternalDateFormatted(Cell cell) {
if (cell == null) return false;
boolean bDate = false;
-
+
double d = cell.getNumericCellValue();
if ( DateUtil.isValidExcelDate(d) ) {
CellStyle style = cell.getCellStyle();
@@ -335,7 +320,6 @@ public class DateUtil
* @param cal the Calendar
* @exception IllegalArgumentException if date is invalid
*/
-
protected static int absoluteDay(Calendar cal, boolean use1904windowing)
{
return cal.get(Calendar.DAY_OF_YEAR)
@@ -347,7 +331,7 @@ public class DateUtil
*
* @return days number of days in years prior to yr.
* @param yr a year (1900 < yr < 4000)
- * @param use1904windowing
+ * @param use1904windowing
* @exception IllegalArgumentException if year is outside of range.
*/
@@ -356,16 +340,16 @@ public class DateUtil
if ((!use1904windowing && yr < 1900) || (use1904windowing && yr < 1900)) {
throw new IllegalArgumentException("'year' must be 1900 or greater");
}
-
+
int yr1 = yr - 1;
int leapDays = yr1 / 4 // plus julian leap days in prior years
- yr1 / 100 // minus prior century years
- + yr1 / 400 // plus years divisible by 400
+ + yr1 / 400 // plus years divisible by 400
- 460; // leap days in previous 1900 years
-
+
return 365 * (yr - (use1904windowing ? 1904 : 1900)) + leapDays;
}
-
+
// set HH:MM:SS fields of cal to 00:00:00:000
private static Calendar dayStart(final Calendar cal)
{
@@ -380,5 +364,95 @@ public class DateUtil
return cal;
}
- // ---------------------------------------------------------------------------------------------------------
+
+ private static final class FormatException extends Exception {
+ public FormatException(String msg) {
+ super(msg);
+ }
+ }
+
+ /**
+ * Converts a string of format "HH:MM" or "HH:MM:SS" to its (Excel) numeric equivalent
+ *
+ * @return a double between 0 and 1 representing the fraction of the day
+ */
+ public static double convertTime(String timeStr) {
+ try {
+ return convertTimeInternal(timeStr);
+ } catch (FormatException e) {
+ String msg = "Bad time format '" + timeStr
+ + "' expected 'HH:MM' or 'HH:MM:SS' - " + e.getMessage();
+ throw new IllegalArgumentException(msg);
+ }
+ }
+ private static double convertTimeInternal(String timeStr) throws FormatException {
+ int len = timeStr.length();
+ if (len < 4 || len > 8) {
+ throw new FormatException("Bad length");
+ }
+ String[] parts = TIME_SEPARATOR_PATTERN.split(timeStr);
+
+ String secStr;
+ switch (parts.length) {
+ case 2: secStr = "00"; break;
+ case 3: secStr = parts[2]; break;
+ default:
+ throw new FormatException("Expected 2 or 3 fields but got (" + parts.length + ")");
+ }
+ String hourStr = parts[0];
+ String minStr = parts[1];
+ int hours = parseInt(hourStr, "hour", HOURS_PER_DAY);
+ int minutes = parseInt(minStr, "minute", MINUTES_PER_HOUR);
+ int seconds = parseInt(secStr, "second", SECONDS_PER_MINUTE);
+
+ double totalSeconds = seconds + (minutes + (hours) * 60) * 60;
+ return totalSeconds / (SECONDS_PER_DAY);
+ }
+ /**
+ * Converts a string of format "YYYY/MM/DD" to its (Excel) numeric equivalent
+ *
+ * @return a double representing the (integer) number of days since the start of the Excel epoch
+ */
+ public static Date parseYYYYMMDDDate(String dateStr) {
+ try {
+ return parseYYYYMMDDDateInternal(dateStr);
+ } catch (FormatException e) {
+ String msg = "Bad time format " + dateStr
+ + " expected 'YYYY/MM/DD' - " + e.getMessage();
+ throw new IllegalArgumentException(msg);
+ }
+ }
+ private static Date parseYYYYMMDDDateInternal(String timeStr) throws FormatException {
+ if(timeStr.length() != 10) {
+ throw new FormatException("Bad length");
+ }
+
+ String yearStr = timeStr.substring(0, 4);
+ String monthStr = timeStr.substring(5, 7);
+ String dayStr = timeStr.substring(8, 10);
+ int year = parseInt(yearStr, "year", Short.MIN_VALUE, Short.MAX_VALUE);
+ int month = parseInt(monthStr, "month", 1, 12);
+ int day = parseInt(dayStr, "day", 1, 31);
+
+ Calendar cal = new GregorianCalendar(year, month-1, day, 0, 0, 0);
+ cal.set(Calendar.MILLISECOND, 0);
+ return cal.getTime();
+ }
+ private static int parseInt(String strVal, String fieldName, int rangeMax) throws FormatException {
+ return parseInt(strVal, fieldName, 0, rangeMax-1);
+ }
+
+ private static int parseInt(String strVal, String fieldName, int lowerLimit, int upperLimit) throws FormatException {
+ int result;
+ try {
+ result = Integer.parseInt(strVal);
+ } catch (NumberFormatException e) {
+ throw new FormatException("Bad int format '" + strVal + "' for " + fieldName + " field");
+ }
+ if (result < lowerLimit || result > upperLimit) {
+ throw new FormatException(fieldName + " value (" + result
+ + ") is outside the allowable range(0.." + upperLimit + ")");
+ }
+ return result;
+ }
}
diff --git a/src/java/org/apache/poi/ss/util/CellRangeAddress.java b/src/java/org/apache/poi/ss/util/CellRangeAddress.java
new file mode 100644
index 000000000..e6534b34e
--- /dev/null
+++ b/src/java/org/apache/poi/ss/util/CellRangeAddress.java
@@ -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'
+ *
+ * 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 -1.
+ */
+ private static int convertM1ToMax(int lastIx, int maxIndex) {
+ if(lastIx < 0) {
+ return maxIndex;
+ }
+ return lastIx;
+ }
+
+ public boolean isFullColumnRange() {
+ return _firstRow == 0 && _lastRow == LAST_ROW_INDEX;
+ }
+ public boolean isFullRowRange() {
+ return _firstCol == 0 && _lastCol == LAST_COLUMN_INDEX;
+ }
+
+ /**
+ * @return column number for the upper left hand corner
+ */
+ public int getFirstColumn() {
+ return _firstCol;
+ }
+
+ /**
+ * @return row number for the upper left hand corner
+ */
+ public int getFirstRow() {
+ return _firstRow;
+ }
+
+ /**
+ * @return column number for the lower right hand corner
+ */
+ public int getLastColumn() {
+ return _lastCol;
+ }
+
+ /**
+ * @return row number for the lower right hand corner
+ */
+ public int getLastRow() {
+ return _lastRow;
+ }
+
+ /**
+ * @param _firstCol column number for the upper left hand corner
+ */
+ public void setFirstColumn(int firstCol) {
+ _firstCol = firstCol;
+ }
+
+ /**
+ * @param rowFrom row number for the upper left hand corner
+ */
+ public void setFirstRow(int firstRow) {
+ _firstRow = firstRow;
+ }
+
+ /**
+ * @param colTo column number for the lower right hand corner
+ */
+ public void setLastColumn(int lastCol) {
+ _lastCol = lastCol;
+ }
+
+ /**
+ * @param rowTo row number for the lower right hand corner
+ */
+ public void setLastRow(int lastRow) {
+ _lastRow = lastRow;
+ }
+
+ public CellRangeAddress copy() {
+ return new CellRangeAddress(_firstRow, _lastRow, _firstCol, _lastCol);
+ }
+
+ public static int getEncodedSize(int numberOfItems) {
+ return numberOfItems * ENCODED_SIZE;
+ }
+
+ public String toString() {
+ return getClass().getName() + " ["+_firstRow+", "+_lastRow+", "+_firstCol+", "+_lastCol+"]";
+ }
+
+ public int serialize(int offset, byte[] data) {
+ LittleEndian.putUShort(data, offset + 0, _firstRow);
+ LittleEndian.putUShort(data, offset + 2, _lastRow);
+ LittleEndian.putUShort(data, offset + 4, _firstCol);
+ LittleEndian.putUShort(data, offset + 6, _lastCol);
+ return ENCODED_SIZE;
+ }
+}
diff --git a/src/java/org/apache/poi/ss/util/CellRangeAddressList.java b/src/java/org/apache/poi/ss/util/CellRangeAddressList.java
new file mode 100644
index 000000000..8773c34ee
--- /dev/null
+++ b/src/java/org/apache/poi/ss/util/CellRangeAddressList.java
@@ -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.
+ *
HeadersFootersAtom
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 HeadersFootersAtom
+ */
+ 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.
+ * + * It MUST be in the range [0, 12]. + * 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. + * + * + * @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 + * + *
+ * It contains:
+ *
{@link RecordTypes#HeadersFooters}
+ */
+ 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 HeadersFootersAtom
+ */
+ public HeadersFootersAtom getHeadersFootersAtom(){
+ return hdAtom;
+ }
+
+ /**
+ * A {@link CString} record that stores the user's date.
+ * This is the date that the user wants in the footers, instead of today's date.
+ * + * @return A {@link CString} record that stores the user's date ornull
+ */
+ 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 null
+ */
+ 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 null
+ */
+ 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;
+ }
+}
diff --git a/src/scratchpad/src/org/apache/poi/hslf/record/RecordTypes.java b/src/scratchpad/src/org/apache/poi/hslf/record/RecordTypes.java
index a6f00da12..d7a664725 100644
--- a/src/scratchpad/src/org/apache/poi/hslf/record/RecordTypes.java
+++ b/src/scratchpad/src/org/apache/poi/hslf/record/RecordTypes.java
@@ -111,8 +111,8 @@ public class RecordTypes {
public static final Type ExHyperlinkAtom = new Type(4051,ExHyperlinkAtom.class);
public static final Type ExHyperlink = new Type(4055,ExHyperlink.class);
public static final Type SlideNumberMCAtom = new Type(4056,null);
- public static final Type HeadersFooters = new Type(4057,null);
- public static final Type HeadersFootersAtom = new Type(4058,null);
+ public static final Type HeadersFooters = new Type(4057,HeadersFootersContainer.class);
+ public static final Type HeadersFootersAtom = new Type(4058,HeadersFootersAtom.class);
public static final Type TxInteractiveInfoAtom = new Type(4063,TxInteractiveInfoAtom.class);
public static final Type CharFormatAtom = new Type(4066,null);
public static final Type ParaFormatAtom = new Type(4067,null);
diff --git a/src/scratchpad/src/org/apache/poi/hslf/usermodel/SlideShow.java b/src/scratchpad/src/org/apache/poi/hslf/usermodel/SlideShow.java
index 56e94431e..f38cc7716 100644
--- a/src/scratchpad/src/org/apache/poi/hslf/usermodel/SlideShow.java
+++ b/src/scratchpad/src/org/apache/poi/hslf/usermodel/SlideShow.java
@@ -811,4 +811,50 @@ public class SlideShow
public int getNumberOfFonts() {
return getDocumentRecord().getEnvironment().getFontCollection().getNumberOfFonts();
}
+
+ /**
+ * Return Header / Footer settings for slides
+ *
+ * @return Header / Footer settings for slides
+ */
+ public HeadersFooters getSlideHeadersFooters(){
+ HeadersFootersContainer hdd = null;
+ Record[] ch = _documentRecord.getChildRecords();
+ for (int i = 0; i < ch.length; i++) {
+ if(ch[i] instanceof HeadersFootersContainer &&
+ ((HeadersFootersContainer)ch[i]).getOptions() == HeadersFootersContainer.SlideHeadersFootersContainer){
+ hdd = (HeadersFootersContainer)ch[i];
+ break;
+ }
+ }
+ boolean newRecord = false;
+ if(hdd == null) {
+ hdd = new HeadersFootersContainer(HeadersFootersContainer.SlideHeadersFootersContainer);
+ newRecord = true;
+ }
+ return new HeadersFooters(hdd, this, newRecord);
+ }
+
+ /**
+ * Return Header / Footer settings for notes
+ *
+ * @return Header / Footer settings for notes
+ */
+ public HeadersFooters getNotesHeadersFooters(){
+ HeadersFootersContainer hdd = null;
+ Record[] ch = _documentRecord.getChildRecords();
+ for (int i = 0; i < ch.length; i++) {
+ if(ch[i] instanceof HeadersFootersContainer &&
+ ((HeadersFootersContainer)ch[i]).getOptions() == HeadersFootersContainer.NotesHeadersFootersContainer){
+ hdd = (HeadersFootersContainer)ch[i];
+ break;
+ }
+ }
+ boolean newRecord = false;
+ if(hdd == null) {
+ hdd = new HeadersFootersContainer(HeadersFootersContainer.NotesHeadersFootersContainer);
+ newRecord = true;
+ }
+ return new HeadersFooters(hdd, this, newRecord);
+ }
}
diff --git a/src/scratchpad/testcases/org/apache/poi/hslf/data/headers_footers.ppt b/src/scratchpad/testcases/org/apache/poi/hslf/data/headers_footers.ppt
new file mode 100644
index 0000000000000000000000000000000000000000..891d73f95ebfed39f551c87851ad2d7ff1aac94c
GIT binary patch
literal 12800
zcmeHN3vg7`8UF9R8TG@9>Mvi@(m~taFz#cr~CzsTjPt?8p1b9pv1(b6Jp56ZpV^ z0~y0ix(lt$Mgz7;!(3?lbycWFl$Ap9#Ez3|%N}^(fjprikw~6UcA6y=(dBx7h-ewc9M>ZZo&IXb(4*8tpVoD0{fE zgtF7}LlCxlZJw8>sr(8H9Tikp)%NSxD8s=bR0w;oWmxZOSG9LvYdFZ4WN}s!b~hNq zsn7mU6@K-?;;yx&&X{E^>+Fmr(zE;Y)zrr$shPC!q|61_U2mZ}GH4d^GW@RCN%B`k z8D=#6hF= uc_8YM9L&e@~kXHgf`a+F<)rgKxRB z8Kbgs@wBNvZ0w#k*#6GbPr0)htFm$Nv>85Z?4CB*{_0!Ly0f`ZW#i&$Gkw_DJ#Dak z@Z{_6Y{sc yXYz&o+i>I~tu(5kuYg_-zW`tI4Geu?N;%Rey*w{U- zwe91Rce=G^IDVeSx-~m)E>p(MiJ4N*v65)*!JoT>PR)VBqg2o-3aH(S@YBb-)$X9v za-i@Z6|_zPwfhXwv1hJz2d&G2!dq0(1_jjaAvmbj^#ga%={ZpNh6*}M0k!)B(UlLs z;0`(?2MSM6L1!zVcIy-U`|#y%pgPs_v}}2_t509xw7fPxQ$)+LaO=vBz{NcK!lnSv zj`*H;4$MeWf%6C0!(S2Y>D<*5O?M{~efmKsV5lZjPV&eQAL;4=bnXaQshCMHC{K(n z2E3~v7?j{#CBNiVjRrN?pLEAlnd7NHG!myO@N2}1ZL!Yn#)?=^PoI83k!wt5806Z9 z$zUfA+ZnizLAo9^J0}(I7>G%sy^~r}@!Y#4E0uh*q>@jTRPw1m6sDQIMVUU`aEek~ z1STtR=1rct125vTO}!;3Td$NY!3pGCS%a>_NN_z@5p`zuhEZp_QYW0zQD+y #-kYcyTNr(G}GyQ3$Xh^CXNea8CO-gKXSg%c_?H&cOw zlzVYtj%}Y1_U8kR`=Imn5N9v^ftM+k_1V~_dzGPWSfF2#Ou|tYI&rmenJ`@er6m *;FT`mGX8mhXCgm3`6o1l!Q1TMh=D42l?%9xgC5{=Y7i3O zbL`-Vg-Spu8s!4c@laZ<)UKBP&@h_Ir+HQ-cI<*H>~fkcRE_rVY0@3=MK*e_qw!vb zWi;OK_#eA}1tv&{OYPl>?XfPi|6i#HGZt~FT^M>hjNavl07Y4E%*G-s`tSxv2^2S+ zE{6QHCblb`iXyxyfmxe?z`O>)+|m?$iUW&W(TI489S*C#1K0*J9Ud;#OX!YmPFXbq z%Qf)ylsJ^jP^R`Z+~yXmWjL1zh&9P{tWWQ7VuXgFzc5A`Lh+$?UiAo3uuo!zG~VOL z1zP!Jg*5qOg*5rpA1bE>gkAKtyVAnqdlaUl0lik}Am8Tpkk1j)VRI(rB#}>+B=X6U zL_TpSyQH@mx|`+{p|}`^Scf*JvI~D!8N2F2cGdOW=^kinwzF2zNSJ3mydw?6vA*)X zU1Nwv0wS4a4^Z|ViY^ootZ-@luy88J{sQaWiX*bM))Tu}B`t=HXhSrW;E*N5Ys4vN zs2q`c130A6udJx$7FKx>7aG1p-$-}ZHc9S8>nibC50DPg+N&Y*K5@4iZRcRf$H5ms zi`}$acu=kc_n@s9MbikC^Jt4WI(!RSbr0_~!!xel7K^24AJxawQ5#R`wsQ0CejK;H z-}a8{9fBKc_#DSUc6^7&;5?;O=aiLY_;naGXmo4^{JPk+|-0?AX7Hd;uJmJ9r4@8!7H9 zapnJ~==8~h@BeOf$GXU7L2nfFnd15_LFq*qE7hS}5gFP+yg3zSUZat}ElS@kuG3LE zF48*@KK5$e*Jn}QjWpQq@8g59K!jQG2@`9fO{CvPtZ$=Z0CSjuoEWI#OpADU6X_aA zfr1z%Ik67pRS)qw3%)l@;z;^b=u!Sthv-$fC(DA9PT1yqf`1F7rF=2dZLq^_4jDo? z`a6 J{pHgD8 1>8HUPYF_6K~w#0sM_Lh6keYAF4#Y1=LG;_vdukr7l_aL)x z@cX6691K<>bMIe|T!p*^nMa4c$k^edn~_H$^We+A_7*Dfi4v}BAglKr%T{-Hrjk38 z@w9Q-UW{0RSMU7OP5eKyrFh>v;`WvNUota3f%5s1U;9I+Up_t1Jiem;2-@HD(Q$77 z^T^yMxc%#p`9R!8WIhDdh0KTic*@Fc;8{Pn`zB-_8E-~reH=n&{oISJ>H{P*<_;HF z{LGk~)bB}P!g8=Z + * 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 null
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 successfalse
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; + } +} diff --git a/src/testcases/org/apache/poi/hssf/usermodel/TestBugs.java b/src/testcases/org/apache/poi/hssf/usermodel/TestBugs.java index 69937d103..541fb893a 100644 --- a/src/testcases/org/apache/poi/hssf/usermodel/TestBugs.java +++ b/src/testcases/org/apache/poi/hssf/usermodel/TestBugs.java @@ -28,6 +28,7 @@ import junit.framework.AssertionFailedError; import junit.framework.TestCase; import org.apache.poi.ss.util.Region; +import org.apache.poi.ss.util.CellRangeAddress; import org.apache.poi.hssf.HSSFTestDataSamples; import org.apache.poi.hssf.model.Workbook; @@ -302,15 +303,14 @@ public final class TestBugs extends TestCase { /** * Merged regions were being removed from the parent in cloned sheets - * @throws Exception */ public void test22720() { HSSFWorkbook workBook = new HSSFWorkbook(); workBook.createSheet("TEST"); HSSFSheet template = workBook.getSheetAt(0); - template.addMergedRegion(new Region(0, (short)0, 1, (short)2)); - template.addMergedRegion(new Region(1, (short)0, 2, (short)2)); + template.addMergedRegion(new CellRangeAddress(0, 1, 0, 2)); + template.addMergedRegion(new CellRangeAddress(1, 2, 0, 2)); HSSFSheet clone = workBook.cloneSheet(0); int originalMerged = template.getNumMergedRegions(); @@ -318,20 +318,20 @@ public final class TestBugs extends TestCase { // remove merged regions from clone for (int i=template.getNumMergedRegions()-1; i>=0; i--) { - clone.removeMergedRegion(i); + clone.removeMergedRegion(i); } assertEquals("Original Sheet's Merged Regions were removed", originalMerged, template.getNumMergedRegions()); // check if template's merged regions are OK if (template.getNumMergedRegions()>0) { - // fetch the first merged region...EXCEPTION OCCURS HERE - template.getMergedRegionAt(0); + // fetch the first merged region...EXCEPTION OCCURS HERE + template.getMergedRegion(0); } //make sure we dont exception } - /*Tests read and write of Unicode strings in formula results + /**Tests read and write of Unicode strings in formula results * bug and testcase submitted by Sompop Kumnoonsate * The file contains THAI unicode characters. */ diff --git a/src/testcases/org/apache/poi/hssf/usermodel/TestCloneSheet.java b/src/testcases/org/apache/poi/hssf/usermodel/TestCloneSheet.java index 6a0b15158..4704b3ed8 100644 --- a/src/testcases/org/apache/poi/hssf/usermodel/TestCloneSheet.java +++ b/src/testcases/org/apache/poi/hssf/usermodel/TestCloneSheet.java @@ -21,6 +21,7 @@ package org.apache.poi.hssf.usermodel; import junit.framework.TestCase; +import org.apache.poi.ss.util.CellRangeAddress; import org.apache.poi.ss.util.Region; /** @@ -29,23 +30,15 @@ import org.apache.poi.ss.util.Region; * add that record to the sheet in the testCloneSheetBasic method. * @author avik */ -public class TestCloneSheet extends TestCase { +public final class TestCloneSheet extends TestCase { - public TestCloneSheet(String arg0) { - super(arg0); - } - public void testCloneSheetBasic(){ - try{ - HSSFWorkbook b = new HSSFWorkbook(); - HSSFSheet s = b.createSheet("Test"); - s.addMergedRegion(new Region((short)0,(short)0,(short)1,(short)1)); - HSSFSheet clonedSheet = b.cloneSheet(0); - - assertEquals("One merged area", 1, clonedSheet.getNumMergedRegions()); - - } - catch(Exception e){e.printStackTrace();fail(e.getMessage());} + HSSFWorkbook b = new HSSFWorkbook(); + HSSFSheet s = b.createSheet("Test"); + s.addMergedRegion(new CellRangeAddress(0, 1, 0, 1)); + HSSFSheet clonedSheet = b.cloneSheet(0); + + assertEquals("One merged area", 1, clonedSheet.getNumMergedRegions()); } /** @@ -66,5 +59,4 @@ public class TestCloneSheet extends TestCase { assertTrue("Row 3 still should be broken", clone.isRowBroken(3)); } - } diff --git a/src/testcases/org/apache/poi/hssf/usermodel/TestDataValidation.java b/src/testcases/org/apache/poi/hssf/usermodel/TestDataValidation.java index 2c28edf3a..91382af06 100644 --- a/src/testcases/org/apache/poi/hssf/usermodel/TestDataValidation.java +++ b/src/testcases/org/apache/poi/hssf/usermodel/TestDataValidation.java @@ -16,896 +16,631 @@ package org.apache.poi.hssf.usermodel; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.PrintStream; + +import junit.framework.AssertionFailedError; import junit.framework.TestCase; -import org.apache.poi.hssf.util.*; +import org.apache.poi.hssf.HSSFTestDataSamples; +import org.apache.poi.hssf.eventmodel.ERFListener; +import org.apache.poi.hssf.eventmodel.EventRecordFactory; +import org.apache.poi.hssf.record.DVRecord; +import org.apache.poi.hssf.record.RecordFormatException; +import org.apache.poi.hssf.util.HSSFColor; +import org.apache.poi.poifs.filesystem.POIFSFileSystem; +import org.apache.poi.ss.util.CellRangeAddress; +import org.apache.poi.ss.util.CellRangeAddressList; import org.apache.poi.ss.util.Region; -import java.io.*; -import java.util.*; -import java.text.SimpleDateFormat; - /** - *Title: TestDataValidation
- *Description: Class for testing Excel's data validation mechanism - * Second test : - * - - *
+ * Class for testing Excel's data validation mechanism + * * @author Dragos Buleandra ( dragos.buleandra@trade2b.ro ) */ -public class TestDataValidation extends TestCase -{ - public TestDataValidation(String name) - { - super(name); - } - - protected void setUp() - { - String filename = System.getProperty("HSSF.testdata.path"); - if (filename == null) - { - System.setProperty("HSSF.testdata.path", "src/testcases/org/apache/poi/hssf/data"); - } - } - - public void testDataValidation() throws Exception - { - System.out.println("\nTest no. 2 - Test Excel's Data validation mechanism"); - String resultFile = System.getProperty("java.io.tmpdir")+File.separator+"TestDataValidation.xls"; - HSSFWorkbook wb = new HSSFWorkbook(); - - HSSFCellStyle style_1 = this.createStyle( wb, HSSFCellStyle.ALIGN_LEFT ); - HSSFCellStyle style_2 = this.createStyle( wb, HSSFCellStyle.ALIGN_CENTER ); - HSSFCellStyle style_3 = this.createStyle( wb, HSSFCellStyle.ALIGN_CENTER, HSSFColor.GREY_25_PERCENT.index, true ); - HSSFCellStyle style_4 = this.createHeaderStyle(wb); - HSSFDataValidation data_validation = null; - - //data validation's number types - System.out.print(" Create sheet for Data Validation's number types ... "); - HSSFSheet fSheet = wb.createSheet("Number types"); - - //"Whole number" validation type - this.createDVTypeRow( wb, 0, style_3, "Whole number"); - this.createHeaderRow( wb, 0, style_4 ); - - short start_row = (short)fSheet.getPhysicalNumberOfRows(); - data_validation = new HSSFDataValidation((short)(start_row),(short)0,(short)(start_row),(short)0); - data_validation.setDataValidationType(HSSFDataValidation.DATA_TYPE_INTEGER); - data_validation.setOperator(HSSFDataValidation.OPERATOR_BETWEEN); - data_validation.setFirstFormula("2"); - data_validation.setSecondFormula("6"); - data_validation.createErrorBox("Invalid input !", "Something is wrong ; check condition !"); - data_validation.createPromptBox("Hi , dear user !", "So , you just selected me ! Thanks !"); - fSheet.addValidationData(data_validation); - this.writeDataValidationSettings( fSheet, style_1, style_2, "Between 2 and 6 ", true, true, true ); - this.writeOtherSettings( fSheet, style_1, "Error box type = STOP" ); - - data_validation.setFirstRow((short)(start_row+1)); - data_validation.setLastRow((short)(start_row+1)); - data_validation.setEmptyCellAllowed(false); - data_validation.setOperator(HSSFDataValidation.OPERATOR_NOT_BETWEEN); - data_validation.setErrorStyle(HSSFDataValidation.ERROR_STYLE_INFO); - fSheet.addValidationData(data_validation); - this.writeDataValidationSettings( fSheet, style_1, style_2, "Not between 2 and 6 ", false, true, true ); - this.writeOtherSettings( fSheet, style_1, "Error box type = INFO" ); - - data_validation.setFirstRow((short)(start_row+2)); - data_validation.setLastRow((short)(start_row+2)); - data_validation.setEmptyCellAllowed(false); - data_validation.setShowPromptBox(false); - data_validation.setFirstFormula("3"); - data_validation.setSecondFormula(null); - data_validation.setOperator(HSSFDataValidation.OPERATOR_EQUAL); - data_validation.setErrorStyle(HSSFDataValidation.ERROR_STYLE_WARNING); - fSheet.addValidationData(data_validation); - this.writeDataValidationSettings( fSheet, style_1, style_2, "Equal to 3", false, false, true ); - this.writeOtherSettings( fSheet, style_1, "Error box type = WARNING" ); - - data_validation.setFirstRow((short)(start_row+3)); - data_validation.setLastRow((short)(start_row+3)); - data_validation.setEmptyCellAllowed(false); - data_validation.setShowPromptBox(false); - data_validation.setShowErrorBox(false); - data_validation.setFirstFormula("3"); - data_validation.setSecondFormula(null); - data_validation.setOperator(HSSFDataValidation.OPERATOR_NOT_EQUAL); - fSheet.addValidationData(data_validation); - this.writeDataValidationSettings( fSheet, style_1, style_2, "Not equal to 3", false, false, false ); - this.writeOtherSettings( fSheet, style_1, "-" ); - - data_validation.setFirstRow((short)(start_row+4)); - data_validation.setLastRow((short)(start_row+4)); - data_validation.setEmptyCellAllowed(true); - data_validation.setShowPromptBox(false); - data_validation.setShowErrorBox(false); - data_validation.setFirstFormula("3"); - data_validation.setSecondFormula(null); - data_validation.setOperator(HSSFDataValidation.OPERATOR_GREATER_THAN); - fSheet.addValidationData(data_validation); - this.writeDataValidationSettings( fSheet, style_1, style_2, "Greater than 3", true, false, false ); - this.writeOtherSettings( fSheet, style_1, "-" ); - - data_validation.setFirstRow((short)(start_row+5)); - data_validation.setLastRow((short)(start_row+5)); - data_validation.setEmptyCellAllowed(true); - data_validation.setShowPromptBox(true); - data_validation.setShowErrorBox(false); - data_validation.setFirstFormula("3"); - data_validation.setSecondFormula(null); - data_validation.setOperator(HSSFDataValidation.OPERATOR_LESS_THAN); - fSheet.addValidationData(data_validation); - this.writeDataValidationSettings( fSheet, style_1, style_2, "Less than 3", true, true, false ); - this.writeOtherSettings( fSheet, style_1, "-" ); - - data_validation.setFirstRow((short)(start_row+6)); - data_validation.setLastRow((short)(start_row+6)); - data_validation.setEmptyCellAllowed(true); - data_validation.setShowPromptBox(false); - data_validation.setErrorStyle(HSSFDataValidation.ERROR_STYLE_STOP); - data_validation.setShowErrorBox(true); - data_validation.setFirstFormula("4"); - data_validation.setSecondFormula(null); - data_validation.setOperator(HSSFDataValidation.OPERATOR_GREATER_OR_EQUAL); - fSheet.addValidationData(data_validation); - this.writeDataValidationSettings( fSheet, style_1, style_2, "Greater than or equal to 4", true, false, true ); - this.writeOtherSettings( fSheet, style_1, "Error box type = STOP" ); - - data_validation.setFirstRow((short)(start_row+7)); - data_validation.setLastRow((short)(start_row+7)); - data_validation.setEmptyCellAllowed(false); - data_validation.setShowPromptBox(true); - data_validation.setShowErrorBox(false); - data_validation.setFirstFormula("4"); - data_validation.setSecondFormula(null); - data_validation.setOperator(HSSFDataValidation.OPERATOR_LESS_OR_EQUAL); - fSheet.addValidationData(data_validation); - this.writeDataValidationSettings( fSheet, style_1, style_2, "Less than or equal to 4", false, true, false ); - this.writeOtherSettings( fSheet, style_1, "-" ); - - //"Decimal" validation type - this.createDVTypeRow( wb, 0, style_3, "Decimal"); - this.createHeaderRow( wb, 0, style_4 ); - - start_row += (short)(8+4); - data_validation = new HSSFDataValidation((short)(start_row),(short)0,(short)(start_row),(short)0); - data_validation.setDataValidationType(HSSFDataValidation.DATA_TYPE_DECIMAL); - data_validation.setOperator(HSSFDataValidation.OPERATOR_BETWEEN); - data_validation.setFirstFormula("2"); - data_validation.setSecondFormula("6"); - data_validation.createErrorBox("Invalid input !", "Something is wrong ; check condition !"); - data_validation.createPromptBox("Hi , dear user !", "So , you just selected me ! Thanks !"); - fSheet.addValidationData(data_validation); - this.writeDataValidationSettings( fSheet, style_1, style_2, "Between 2 and 6 ", true, true, true ); - this.writeOtherSettings( fSheet, style_1, "Error box type = STOP" ); - - data_validation.setFirstRow((short)(start_row+1)); - data_validation.setLastRow((short)(start_row+1)); - data_validation.setEmptyCellAllowed(false); - data_validation.setOperator(HSSFDataValidation.OPERATOR_NOT_BETWEEN); - data_validation.setErrorStyle(HSSFDataValidation.ERROR_STYLE_INFO); - fSheet.addValidationData(data_validation); - this.writeDataValidationSettings( fSheet, style_1, style_2, "Not between 2 and 6 ", false, true, true ); - this.writeOtherSettings( fSheet, style_1, "Error box type = INFO" ); - - data_validation.setFirstRow((short)(start_row+2)); - data_validation.setLastRow((short)(start_row+2)); - data_validation.setEmptyCellAllowed(false); - data_validation.setShowPromptBox(false); - data_validation.setFirstFormula("3"); - data_validation.setSecondFormula(null); - data_validation.setOperator(HSSFDataValidation.OPERATOR_EQUAL); - data_validation.setErrorStyle(HSSFDataValidation.ERROR_STYLE_WARNING); - fSheet.addValidationData(data_validation); - this.writeDataValidationSettings( fSheet, style_1, style_2, "Equal to 3", false, false, true ); - this.writeOtherSettings( fSheet, style_1, "Error box type = WARNING" ); - - data_validation.setFirstRow((short)(start_row+3)); - data_validation.setLastRow((short)(start_row+3)); - data_validation.setEmptyCellAllowed(false); - data_validation.setShowPromptBox(false); - data_validation.setShowErrorBox(false); - data_validation.setFirstFormula("3"); - data_validation.setSecondFormula(null); - data_validation.setOperator(HSSFDataValidation.OPERATOR_NOT_EQUAL); - fSheet.addValidationData(data_validation); - this.writeDataValidationSettings( fSheet, style_1, style_2, "Not equal to 3", false, false, false ); - this.writeOtherSettings( fSheet, style_1, "-" ); - - data_validation.setFirstRow((short)(start_row+4)); - data_validation.setLastRow((short)(start_row+4)); - data_validation.setEmptyCellAllowed(true); - data_validation.setShowPromptBox(false); - data_validation.setShowErrorBox(false); - data_validation.setFirstFormula("3"); - data_validation.setSecondFormula(null); - data_validation.setOperator(HSSFDataValidation.OPERATOR_GREATER_THAN); - fSheet.addValidationData(data_validation); - this.writeDataValidationSettings( fSheet, style_1, style_2, "Greater than 3", true, false, false ); - this.writeOtherSettings( fSheet, style_1, "-" ); - - data_validation.setFirstRow((short)(start_row+5)); - data_validation.setLastRow((short)(start_row+5)); - data_validation.setEmptyCellAllowed(true); - data_validation.setShowPromptBox(true); - data_validation.setShowErrorBox(false); - data_validation.setFirstFormula("3"); - data_validation.setSecondFormula(null); - data_validation.setOperator(HSSFDataValidation.OPERATOR_LESS_THAN); - fSheet.addValidationData(data_validation); - this.writeDataValidationSettings( fSheet, style_1, style_2, "Less than 3", true, true, false ); - this.writeOtherSettings( fSheet, style_1, "-" ); - - data_validation.setFirstRow((short)(start_row+6)); - data_validation.setLastRow((short)(start_row+6)); - data_validation.setEmptyCellAllowed(true); - data_validation.setShowPromptBox(false); - data_validation.setErrorStyle(HSSFDataValidation.ERROR_STYLE_STOP); - data_validation.setShowErrorBox(true); - data_validation.setFirstFormula("4"); - data_validation.setSecondFormula(null); - data_validation.setOperator(HSSFDataValidation.OPERATOR_GREATER_OR_EQUAL); - fSheet.addValidationData(data_validation); - this.writeDataValidationSettings( fSheet, style_1, style_2, "Greater than or equal to 4", true, false, true ); - this.writeOtherSettings( fSheet, style_1, "Error box type = STOP" ); - - data_validation.setFirstRow((short)(start_row+7)); - data_validation.setLastRow((short)(start_row+7)); - data_validation.setEmptyCellAllowed(false); - data_validation.setShowPromptBox(true); - data_validation.setShowErrorBox(false); - data_validation.setFirstFormula("4"); - data_validation.setSecondFormula(null); - data_validation.setOperator(HSSFDataValidation.OPERATOR_LESS_OR_EQUAL); - fSheet.addValidationData(data_validation); - this.writeDataValidationSettings( fSheet, style_1, style_2, "Less than or equal to 4", false, true, false ); - this.writeOtherSettings( fSheet, style_1, "-" ); - - System.out.println("done !"); - - //"List" Data Validation type - /** @todo List*/ - System.out.print(" Create sheet for 'List' Data Validation type ... "); - fSheet = wb.createSheet("Lists"); - - this.createDVTypeRow( wb, 1, style_3, "Explicit lists - list items are explicitly provided"); - this.createDVDeescriptionRow( wb, 1, style_3, "Disadvantage - sum of item's length should be less than 255 characters"); - this.createHeaderRow( wb, 1, style_4 ); - - start_row = (short)fSheet.getPhysicalNumberOfRows(); - data_validation = new HSSFDataValidation((short)(start_row),(short)0,(short)(start_row),(short)0); - data_validation.setDataValidationType(HSSFDataValidation.DATA_TYPE_LIST); - data_validation.setFirstFormula("1+2+3"); - data_validation.setSecondFormula(null); - data_validation.setSurppressDropDownArrow(false); - data_validation.createErrorBox("Invalid input !", "Something is wrong ; check condition !"); - data_validation.createPromptBox("Hi , dear user !", "So , you just selected me ! Thanks !"); - fSheet.addValidationData(data_validation); - this.writeDataValidationSettings( fSheet, style_1, style_2, "POIFS,HSSF,HWPF,HPSF", true, true, true ); - this.writeOtherSettings( fSheet, style_1, "Error box type=STOP ; In-cell dropdown=yes" ); - - data_validation = new HSSFDataValidation((short)(start_row+1),(short)0,(short)(start_row+1),(short)0); - data_validation.setDataValidationType(HSSFDataValidation.DATA_TYPE_LIST); - data_validation.setFirstFormula("4+5+6+7"); - data_validation.setSecondFormula(null); - data_validation.setSurppressDropDownArrow(false); - data_validation.setEmptyCellAllowed(false); - data_validation.setShowPromptBox(false); - data_validation.createErrorBox("Invalid input !", "Something is wrong ; check condition !"); - fSheet.addValidationData(data_validation); - this.writeDataValidationSettings( fSheet, style_1, style_2, "POIFS,HSSF,HWPF,HPSF", false, false, true ); - this.writeOtherSettings( fSheet, style_1, "Error box type=STOP ; In-cell dropdown=yes" ); - - data_validation = new HSSFDataValidation((short)(start_row+2),(short)0,(short)(start_row+2),(short)0); - data_validation.setDataValidationType(HSSFDataValidation.DATA_TYPE_LIST); - data_validation.setFirstFormula("7+21"); - data_validation.setSecondFormula(null); - data_validation.setSurppressDropDownArrow(true); - data_validation.createErrorBox("Invalid input !", "Something is wrong ; check condition !"); - data_validation.createPromptBox("Hi , dear user !", "So , you just selected me ! Thanks !"); - fSheet.addValidationData(data_validation); - this.writeDataValidationSettings( fSheet, style_1, style_2, "POIFS,HSSF,HWPF,HPSF", true, true, true ); - this.writeOtherSettings( fSheet, style_1, "Error box type=STOP ; In-cell dropdown=no" ); - - data_validation = new HSSFDataValidation((short)(start_row+3),(short)0,(short)(start_row+3),(short)0); - data_validation.setDataValidationType(HSSFDataValidation.DATA_TYPE_LIST); - data_validation.setFirstFormula("8/2"); - data_validation.setSecondFormula(null); - data_validation.setSurppressDropDownArrow(true); - data_validation.createErrorBox("Invalid input !", "Something is wrong ; check condition !"); - data_validation.setEmptyCellAllowed(false); - data_validation.setShowPromptBox(false); - fSheet.addValidationData(data_validation); - this.writeDataValidationSettings( fSheet, style_1, style_2, "POIFS,HSSF,HWPF,HPSF", false, false, true ); - this.writeOtherSettings( fSheet, style_1, "Error box type=STOP ; In-cell dropdown=no" ); - - this.createDVTypeRow( wb, 1, style_3, "Reference lists - list items are taken from others cells"); - this.createDVDeescriptionRow( wb, 1, style_3, "Advantage - no restriction regarding the sum of item's length"); - this.createHeaderRow( wb, 1, style_4 ); - - start_row += (short)(4+5); - String cellStrValue = "a b c d e f g h i j k l m n o p r s t u v x y z w 0 1 2 3 4 "+ - "a b c d e f g h i j k l m n o p r s t u v x y z w 0 1 2 3 4 "+ - "a b c d e f g h i j k l m n o p r s t u v x y z w 0 1 2 3 4 "+ - "a b c d e f g h i j k l m n o p r s t u v x y z w 0 1 2 3 4 "; - - String strFormula = "$A$100:$A$120"; - data_validation = new HSSFDataValidation((short)(start_row),(short)0,(short)(start_row),(short)0); - data_validation.setDataValidationType(HSSFDataValidation.DATA_TYPE_LIST); - data_validation.setFirstFormula(strFormula); - data_validation.setSecondFormula(null); - data_validation.setSurppressDropDownArrow(false); - data_validation.createErrorBox("Invalid input !", "Something is wrong ; check condition !"); - data_validation.createPromptBox("Hi , dear user !", "So , you just selected me ! Thanks !"); - fSheet.addValidationData(data_validation); - this.writeDataValidationSettings( fSheet, style_1, style_2, strFormula, true, true, true ); - this.writeOtherSettings( fSheet, style_1, "Error box type=STOP ; In-cell dropdown=yes" ); - - data_validation = new HSSFDataValidation((short)(start_row+1),(short)0,(short)(start_row+1),(short)0); - data_validation.setDataValidationType(HSSFDataValidation.DATA_TYPE_LIST); - data_validation.setFirstFormula(strFormula); - data_validation.setSecondFormula(null); - data_validation.setSurppressDropDownArrow(false); - data_validation.setEmptyCellAllowed(false); - data_validation.setShowPromptBox(false); - data_validation.createErrorBox("Invalid input !", "Something is wrong ; check condition !"); - fSheet.addValidationData(data_validation); - this.writeDataValidationSettings( fSheet, style_1, style_2, strFormula, false, false, true ); - this.writeOtherSettings( fSheet, style_1, "Error box type=STOP ; In-cell dropdown=yes" ); - - data_validation = new HSSFDataValidation((short)(start_row+2),(short)0,(short)(start_row+2),(short)0); - data_validation.setDataValidationType(HSSFDataValidation.DATA_TYPE_LIST); - data_validation.setFirstFormula(strFormula); - data_validation.setSecondFormula(null); - data_validation.setSurppressDropDownArrow(true); - data_validation.createErrorBox("Invalid input !", "Something is wrong ; check condition !"); - data_validation.createPromptBox("Hi , dear user !", "So , you just selected me ! Thanks !"); - fSheet.addValidationData(data_validation); - this.writeDataValidationSettings( fSheet, style_1, style_2, strFormula, true, true, true ); - this.writeOtherSettings( fSheet, style_1, "Error box type=STOP ; In-cell dropdown=no" ); - - data_validation = new HSSFDataValidation((short)(start_row+3),(short)0,(short)(start_row+3),(short)0); - data_validation.setDataValidationType(HSSFDataValidation.DATA_TYPE_LIST); - data_validation.setFirstFormula(strFormula); - data_validation.setSecondFormula(null); - data_validation.setSurppressDropDownArrow(true); - data_validation.createErrorBox("Invalid input !", "Something is wrong ; check condition !"); - data_validation.setEmptyCellAllowed(false); - data_validation.setShowPromptBox(false); - fSheet.addValidationData(data_validation); - this.writeDataValidationSettings( fSheet, style_1, style_2, strFormula, false, false, true ); - this.writeOtherSettings( fSheet, style_1, "Error box type=STOP ; In-cell dropdown=no" ); - - for (int i=100; i<=120; i++) - { - HSSFRow currRow = fSheet.createRow(i); - currRow.createCell((short)0).setCellValue(cellStrValue); -// currRow.hide( true ); - } - - System.out.println("done !"); - - //Date/Time Validation type - System.out.print(" Create sheet for 'Date' and 'Time' Data Validation types ... "); - fSheet = wb.createSheet("Date_Time"); - SimpleDateFormat df = new SimpleDateFormat("m/d/yyyy"); - HSSFDataFormat dataFormat = wb.createDataFormat(); - short fmtDate = dataFormat.getFormat("m/d/yyyy"); - short fmtTime = dataFormat.getFormat("h:mm"); - HSSFCellStyle cellStyle_data = wb.createCellStyle(); - cellStyle_data.setDataFormat(fmtDate); - HSSFCellStyle cellStyle_time = wb.createCellStyle(); - cellStyle_time.setDataFormat(fmtTime); - - this.createDVTypeRow( wb, 2, style_3, "Date ( cells are already formated as date - m/d/yyyy)"); - this.createHeaderRow( wb, 2, style_4 ); - - start_row = (short)fSheet.getPhysicalNumberOfRows(); - data_validation = new HSSFDataValidation((short)(start_row),(short)0,(short)(start_row),(short)0); - data_validation.setDataValidationType(HSSFDataValidation.DATA_TYPE_DATE); - data_validation.setOperator(HSSFDataValidation.OPERATOR_BETWEEN); - - data_validation.setFirstFormula( String.valueOf((int)HSSFDateUtil.getExcelDate(df.parse("1/2/2004"))) ); - data_validation.setSecondFormula( String.valueOf((int)HSSFDateUtil.getExcelDate(df.parse("1/6/2004"))) ); - - data_validation.createErrorBox("Invalid input !", "Something is wrong ; check condition !"); - data_validation.createPromptBox("Hi , dear user !", "So , you just selected me ! Thanks !"); - fSheet.addValidationData(data_validation); - this.writeDataValidationSettings( fSheet, style_1, style_2, "Between 1/2/2004 and 1/6/2004 ", true, true, true ); - this.setCellFormat( fSheet, cellStyle_data ); - this.writeOtherSettings( fSheet, style_1, "Error box type = STOP" ); - - data_validation.setFirstRow((short)(start_row+1)); - data_validation.setLastRow((short)(start_row+1)); - data_validation.setEmptyCellAllowed(false); - data_validation.setOperator(HSSFDataValidation.OPERATOR_NOT_BETWEEN); - data_validation.setErrorStyle(HSSFDataValidation.ERROR_STYLE_INFO); - fSheet.addValidationData(data_validation); - this.writeDataValidationSettings( fSheet, style_1, style_2, "Not between 1/2/2004 and 1/6/2004 ", false, true, true ); - this.setCellFormat( fSheet, cellStyle_data ); - this.writeOtherSettings( fSheet, style_1, "Error box type = INFO" ); - - data_validation.setFirstRow((short)(start_row+2)); - data_validation.setLastRow((short)(start_row+2)); - data_validation.setEmptyCellAllowed(false); - data_validation.setShowPromptBox(false); - data_validation.setFirstFormula(String.valueOf((int)HSSFDateUtil.getExcelDate(df.parse("3/2/2004")))); - data_validation.setSecondFormula(null); - data_validation.setOperator(HSSFDataValidation.OPERATOR_EQUAL); - data_validation.setErrorStyle(HSSFDataValidation.ERROR_STYLE_WARNING); - fSheet.addValidationData(data_validation); - this.writeDataValidationSettings( fSheet, style_1, style_2, "Equal to 3/2/2004", false, false, true ); - this.setCellFormat( fSheet, cellStyle_data ); - this.writeOtherSettings( fSheet, style_1, "Error box type = WARNING" ); - - data_validation.setFirstRow((short)(start_row+3)); - data_validation.setLastRow((short)(start_row+3)); - data_validation.setEmptyCellAllowed(false); - data_validation.setShowPromptBox(false); - data_validation.setShowErrorBox(false); - data_validation.setFirstFormula(String.valueOf((int)HSSFDateUtil.getExcelDate(df.parse("3/2/2004")))); - data_validation.setSecondFormula(null); - data_validation.setOperator(HSSFDataValidation.OPERATOR_NOT_EQUAL); - fSheet.addValidationData(data_validation); - this.writeDataValidationSettings( fSheet, style_1, style_2, "Not equal to 3/2/2004", false, false, false ); - this.setCellFormat( fSheet, cellStyle_data ); - this.writeOtherSettings( fSheet, style_1, "-" ); - - data_validation.setFirstRow((short)(start_row+4)); - data_validation.setLastRow((short)(start_row+4)); - data_validation.setEmptyCellAllowed(true); - data_validation.setShowPromptBox(false); - data_validation.setShowErrorBox(false); - data_validation.setFirstFormula(String.valueOf((int)HSSFDateUtil.getExcelDate(df.parse("3/2/2004")))); - data_validation.setSecondFormula(null); - data_validation.setOperator(HSSFDataValidation.OPERATOR_GREATER_THAN); - fSheet.addValidationData(data_validation); - this.writeDataValidationSettings( fSheet, style_1, style_2, "Greater than 3/2/2004", true, false, false ); - this.setCellFormat( fSheet, cellStyle_data ); - this.writeOtherSettings( fSheet, style_1, "-" ); - - data_validation.setFirstRow((short)(start_row+5)); - data_validation.setLastRow((short)(start_row+5)); - data_validation.setEmptyCellAllowed(true); - data_validation.setShowPromptBox(true); - data_validation.setShowErrorBox(false); - data_validation.setFirstFormula(String.valueOf((int)HSSFDateUtil.getExcelDate(df.parse("3/2/2004")))); - data_validation.setSecondFormula(null); - data_validation.setOperator(HSSFDataValidation.OPERATOR_LESS_THAN); - fSheet.addValidationData(data_validation); - this.writeDataValidationSettings( fSheet, style_1, style_2, "Less than 3/2/2004", true, true, false ); - this.setCellFormat( fSheet, cellStyle_data ); - this.writeOtherSettings( fSheet, style_1, "-" ); - - data_validation.setFirstRow((short)(start_row+6)); - data_validation.setLastRow((short)(start_row+6)); - data_validation.setEmptyCellAllowed(true); - data_validation.setShowPromptBox(false); - data_validation.setErrorStyle(HSSFDataValidation.ERROR_STYLE_STOP); - data_validation.setShowErrorBox(true); - data_validation.setFirstFormula(String.valueOf((int)HSSFDateUtil.getExcelDate(df.parse("3/2/2004")))); - data_validation.setSecondFormula(null); - data_validation.setOperator(HSSFDataValidation.OPERATOR_GREATER_OR_EQUAL); - fSheet.addValidationData(data_validation); - this.writeDataValidationSettings( fSheet, style_1, style_2, "Greater than or equal to 3/2/2004", true, false, true ); - this.setCellFormat( fSheet, cellStyle_data ); - this.writeOtherSettings( fSheet, style_1, "Error box type = STOP" ); - - data_validation.setFirstRow((short)(start_row+7)); - data_validation.setLastRow((short)(start_row+7)); - data_validation.setEmptyCellAllowed(false); - data_validation.setShowPromptBox(true); - data_validation.setShowErrorBox(false); - data_validation.setFirstFormula(String.valueOf((int)HSSFDateUtil.getExcelDate(df.parse("3/4/2004")))); - data_validation.setSecondFormula(null); - data_validation.setOperator(HSSFDataValidation.OPERATOR_LESS_OR_EQUAL); - fSheet.addValidationData(data_validation); - this.writeDataValidationSettings( fSheet, style_1, style_2, "Less than or equal to 3/4/2004", false, true, false ); - this.setCellFormat( fSheet, cellStyle_data ); - this.writeOtherSettings( fSheet, style_1, "-" ); - - //"Time" validation type - this.createDVTypeRow( wb, 2, style_3, "Time ( cells are already formated as time - h:mm)"); - this.createHeaderRow( wb, 2, style_4 ); - - df = new SimpleDateFormat("hh:mm"); - - start_row += (short)(8+4); - data_validation = new HSSFDataValidation((short)(start_row),(short)0,(short)(start_row),(short)0); - data_validation.setDataValidationType(HSSFDataValidation.DATA_TYPE_TIME); - data_validation.setOperator(HSSFDataValidation.OPERATOR_BETWEEN); - data_validation.setFirstFormula(String.valueOf(HSSFDateUtil.getExcelDate(df.parse("12:00")))); - data_validation.setSecondFormula(String.valueOf(HSSFDateUtil.getExcelDate(df.parse("16:00")))); - data_validation.createErrorBox("Invalid input !", "Something is wrong ; check condition !"); - data_validation.createPromptBox("Hi , dear user !", "So , you just selected me ! Thanks !"); - fSheet.addValidationData(data_validation); - this.writeDataValidationSettings( fSheet, style_1, style_2, "Between 12:00 and 16:00 ", true, true, true ); - this.setCellFormat( fSheet, cellStyle_time ); - this.writeOtherSettings( fSheet, style_1, "Error box type = STOP" ); - - data_validation.setFirstRow((short)(start_row+1)); - data_validation.setLastRow((short)(start_row+1)); - data_validation.setEmptyCellAllowed(false); - data_validation.setOperator(HSSFDataValidation.OPERATOR_NOT_BETWEEN); - data_validation.setErrorStyle(HSSFDataValidation.ERROR_STYLE_INFO); - fSheet.addValidationData(data_validation); - this.writeDataValidationSettings( fSheet, style_1, style_2, "Not between 12:00 and 16:00 ", false, true, true ); - this.setCellFormat( fSheet, cellStyle_time ); - this.writeOtherSettings( fSheet, style_1, "Error box type = INFO" ); - - data_validation.setFirstRow((short)(start_row+2)); - data_validation.setLastRow((short)(start_row+2)); - data_validation.setEmptyCellAllowed(false); - data_validation.setShowPromptBox(false); - data_validation.setFirstFormula(String.valueOf((int)HSSFDateUtil.getExcelDate(df.parse("13:35")))); - data_validation.setSecondFormula(null); - data_validation.setOperator(HSSFDataValidation.OPERATOR_EQUAL); - data_validation.setErrorStyle(HSSFDataValidation.ERROR_STYLE_WARNING); - fSheet.addValidationData(data_validation); - this.writeDataValidationSettings( fSheet, style_1, style_2, "Equal to 13:35", false, false, true ); - this.setCellFormat( fSheet, cellStyle_time ); - this.writeOtherSettings( fSheet, style_1, "Error box type = WARNING" ); - - data_validation.setFirstRow((short)(start_row+3)); - data_validation.setLastRow((short)(start_row+3)); - data_validation.setEmptyCellAllowed(false); - data_validation.setShowPromptBox(false); - data_validation.setShowErrorBox(false); - data_validation.setFirstFormula(String.valueOf(HSSFDateUtil.getExcelDate(df.parse("13:35")))); - data_validation.setSecondFormula(null); - data_validation.setOperator(HSSFDataValidation.OPERATOR_NOT_EQUAL); - fSheet.addValidationData(data_validation); - this.writeDataValidationSettings( fSheet, style_1, style_2, "Not equal to 13:35", false, false, false ); - this.setCellFormat( fSheet, cellStyle_time ); - this.writeOtherSettings( fSheet, style_1, "-" ); - - data_validation.setFirstRow((short)(start_row+4)); - data_validation.setLastRow((short)(start_row+4)); - data_validation.setEmptyCellAllowed(true); - data_validation.setShowPromptBox(false); - data_validation.setShowErrorBox(false); - data_validation.setFirstFormula(String.valueOf(HSSFDateUtil.getExcelDate(df.parse("12:00")))); - data_validation.setSecondFormula(null); - data_validation.setOperator(HSSFDataValidation.OPERATOR_GREATER_THAN); - fSheet.addValidationData(data_validation); - this.writeDataValidationSettings( fSheet, style_1, style_2, "Greater than 12:00", true, false, false ); - this.setCellFormat( fSheet, cellStyle_time ); - this.writeOtherSettings( fSheet, style_1, "-" ); - - data_validation.setFirstRow((short)(start_row+5)); - data_validation.setLastRow((short)(start_row+5)); - data_validation.setEmptyCellAllowed(true); - data_validation.setShowPromptBox(true); - data_validation.setShowErrorBox(false); - data_validation.setFirstFormula(String.valueOf(HSSFDateUtil.getExcelDate(df.parse("12:00")))); - data_validation.setSecondFormula(null); - data_validation.setOperator(HSSFDataValidation.OPERATOR_LESS_THAN); - fSheet.addValidationData(data_validation); - this.writeDataValidationSettings( fSheet, style_1, style_2, "Less than 12:00", true, true, false ); - this.setCellFormat( fSheet, cellStyle_time ); - this.writeOtherSettings( fSheet, style_1, "-" ); - - data_validation.setFirstRow((short)(start_row+6)); - data_validation.setLastRow((short)(start_row+6)); - data_validation.setEmptyCellAllowed(true); - data_validation.setShowPromptBox(false); - data_validation.setErrorStyle(HSSFDataValidation.ERROR_STYLE_STOP); - data_validation.setShowErrorBox(true); - data_validation.setFirstFormula(String.valueOf(HSSFDateUtil.getExcelDate(df.parse("14:00")))); - data_validation.setSecondFormula(null); - data_validation.setOperator(HSSFDataValidation.OPERATOR_GREATER_OR_EQUAL); - fSheet.addValidationData(data_validation); - this.writeDataValidationSettings( fSheet, style_1, style_2, "Greater than or equal to 14:00", true, false, true ); - this.setCellFormat( fSheet, cellStyle_time ); - this.writeOtherSettings( fSheet, style_1, "Error box type = STOP" ); - - data_validation.setFirstRow((short)(start_row+7)); - data_validation.setLastRow((short)(start_row+7)); - data_validation.setEmptyCellAllowed(false); - data_validation.setShowPromptBox(true); - data_validation.setShowErrorBox(false); - data_validation.setFirstFormula(String.valueOf(HSSFDateUtil.getExcelDate(df.parse("14:00")))); - data_validation.setSecondFormula(null); - data_validation.setOperator(HSSFDataValidation.OPERATOR_LESS_OR_EQUAL); - fSheet.addValidationData(data_validation); - this.writeDataValidationSettings( fSheet, style_1, style_2, "Less than or equal to 14:00", false, true, false ); - this.setCellFormat( fSheet, cellStyle_time ); - this.writeOtherSettings( fSheet, style_1, "-" ); - - System.out.println("done !"); - - //"Text length" validation type - System.out.print(" Create sheet for 'Text length' Data Validation type... "); - fSheet = wb.createSheet("Text length"); - this.createHeaderRow( wb, 3, style_4 ); - - data_validation = new HSSFDataValidation((short)1,(short)0,(short)1,(short)0); - data_validation.setDataValidationType(HSSFDataValidation.DATA_TYPE_TEXT_LENGTH); - data_validation.setOperator(HSSFDataValidation.OPERATOR_BETWEEN); - data_validation.setFirstFormula("2"); - data_validation.setSecondFormula("6"); - data_validation.createErrorBox("Invalid input !", "Something is wrong ; check condition !"); - data_validation.createPromptBox("Hi , dear user !", "So , you just selected me ! Thanks !"); - fSheet.addValidationData(data_validation); - this.writeDataValidationSettings( fSheet, style_1, style_2, "Between 2 and 6 ", true, true, true ); - this.writeOtherSettings( fSheet, style_1, "Error box type = STOP" ); - - data_validation.setFirstRow((short)2); - data_validation.setLastRow((short)2); - data_validation.setEmptyCellAllowed(false); - data_validation.setOperator(HSSFDataValidation.OPERATOR_NOT_BETWEEN); - data_validation.setErrorStyle(HSSFDataValidation.ERROR_STYLE_INFO); - fSheet.addValidationData(data_validation); - this.writeDataValidationSettings( fSheet, style_1, style_2, "Not between 2 and 6 ", false, true, true ); - this.writeOtherSettings( fSheet, style_1, "Error box type = INFO" ); - - data_validation.setFirstRow((short)3); - data_validation.setLastRow((short)3); - data_validation.setEmptyCellAllowed(false); - data_validation.setShowPromptBox(false); - data_validation.setFirstFormula("3"); - data_validation.setSecondFormula(null); - data_validation.setOperator(HSSFDataValidation.OPERATOR_EQUAL); - data_validation.setErrorStyle(HSSFDataValidation.ERROR_STYLE_WARNING); - fSheet.addValidationData(data_validation); - this.writeDataValidationSettings( fSheet, style_1, style_2, "Equal to 3", false, false, true ); - this.writeOtherSettings( fSheet, style_1, "Error box type = WARNING" ); - - data_validation.setFirstRow((short)4); - data_validation.setLastRow((short)4); - data_validation.setEmptyCellAllowed(false); - data_validation.setShowPromptBox(false); - data_validation.setShowErrorBox(false); - data_validation.setFirstFormula("3"); - data_validation.setSecondFormula(null); - data_validation.setOperator(HSSFDataValidation.OPERATOR_NOT_EQUAL); - fSheet.addValidationData(data_validation); - this.writeDataValidationSettings( fSheet, style_1, style_2, "Not equal to 3", false, false, false ); - this.writeOtherSettings( fSheet, style_1, "-" ); - - data_validation.setFirstRow((short)5); - data_validation.setLastRow((short)5); - data_validation.setEmptyCellAllowed(true); - data_validation.setShowPromptBox(false); - data_validation.setShowErrorBox(false); - data_validation.setFirstFormula("3"); - data_validation.setSecondFormula(null); - data_validation.setOperator(HSSFDataValidation.OPERATOR_GREATER_THAN); - fSheet.addValidationData(data_validation); - this.writeDataValidationSettings( fSheet, style_1, style_2, "Greater than 3", true, false, false ); - this.writeOtherSettings( fSheet, style_1, "-" ); - - data_validation.setFirstRow((short)6); - data_validation.setLastRow((short)6); - data_validation.setEmptyCellAllowed(true); - data_validation.setShowPromptBox(true); - data_validation.setShowErrorBox(false); - data_validation.setFirstFormula("3"); - data_validation.setSecondFormula(null); - data_validation.setOperator(HSSFDataValidation.OPERATOR_LESS_THAN); - fSheet.addValidationData(data_validation); - this.writeDataValidationSettings( fSheet, style_1, style_2, "Less than 3", true, true, false ); - this.writeOtherSettings( fSheet, style_1, "-" ); - - data_validation.setFirstRow((short)7); - data_validation.setLastRow((short)7); - data_validation.setEmptyCellAllowed(true); - data_validation.setShowPromptBox(false); - data_validation.setErrorStyle(HSSFDataValidation.ERROR_STYLE_STOP); - data_validation.setShowErrorBox(true); - data_validation.setFirstFormula("4"); - data_validation.setSecondFormula(null); - data_validation.setOperator(HSSFDataValidation.OPERATOR_GREATER_OR_EQUAL); - fSheet.addValidationData(data_validation); - this.writeDataValidationSettings( fSheet, style_1, style_2, "Greater than or equal to 4", true, false, true ); - this.writeOtherSettings( fSheet, style_1, "Error box type = STOP" ); - - data_validation.setFirstRow((short)8); - data_validation.setLastRow((short)8); - data_validation.setEmptyCellAllowed(false); - data_validation.setShowPromptBox(true); - data_validation.setShowErrorBox(false); - data_validation.setFirstFormula("4"); - data_validation.setSecondFormula(null); - data_validation.setOperator(HSSFDataValidation.OPERATOR_LESS_OR_EQUAL); - fSheet.addValidationData(data_validation); - this.writeDataValidationSettings( fSheet, style_1, style_2, "Less than or equal to 4", false, true, false ); - this.writeOtherSettings( fSheet, style_1, "-" ); - System.out.println("done !"); - - //Custom Validation type - System.out.print(" Create sheet for 'Custom' Data Validation type ... "); - fSheet = wb.createSheet("Custom"); - this.createHeaderRow( wb, 4, style_4 ); - - data_validation = new HSSFDataValidation((short)1,(short)0,(short)1,(short)0); - data_validation.setDataValidationType(HSSFDataValidation.DATA_TYPE_FORMULA); - data_validation.setFirstFormula("ISNUMBER($A2)"); - data_validation.setSecondFormula(null); - data_validation.setShowPromptBox(true); - data_validation.setShowErrorBox(true); - data_validation.createErrorBox("Invalid input !", "Something is wrong ; check condition !"); - data_validation.createPromptBox("Hi , dear user !", "So , you just selected me ! Thanks !"); - fSheet.addValidationData(data_validation); - this.writeDataValidationSettings( fSheet, style_1, style_2, "ISNUMBER(A2)", true, true, true ); - this.writeOtherSettings( fSheet, style_1, "Error box type = STOP" ); - - data_validation = new HSSFDataValidation((short)2,(short)0,(short)2,(short)0); - data_validation.setDataValidationType(HSSFDataValidation.DATA_TYPE_FORMULA); - data_validation.setFirstFormula("IF(SUM(A2:A3)=5,TRUE,FALSE)"); - data_validation.setSecondFormula(null); - data_validation.setShowPromptBox(false); - data_validation.setShowErrorBox(true); - data_validation.setErrorStyle(HSSFDataValidation.ERROR_STYLE_WARNING); - data_validation.createErrorBox("Invalid input !", "Something is wrong ; check condition !"); - data_validation.setEmptyCellAllowed(false); - fSheet.addValidationData(data_validation); - this.writeDataValidationSettings( fSheet, style_1, style_2, "IF(SUM(A2:A3)=5,TRUE,FALSE)", false, false, true ); - this.writeOtherSettings( fSheet, style_1, "Error box type = WARNING" ); - - System.out.println("done !"); - - //so , everything it's ok for now ; it remains for you to open the file - System.out.println("\n Everything it's ok since we've got so far -:) !\n"+ - " In order to complete the test , it remains for you to open the file \n"+ - " and see if there are four sheets , as described !"); - System.out.println(" File was saved in \""+resultFile+"\""); - - FileOutputStream fileOut = new FileOutputStream(resultFile); - wb.write(fileOut); - fileOut.close(); - } - - private void createDVTypeRow( HSSFWorkbook wb, int sheetNo , HSSFCellStyle cellStyle, String strTypeDescription) - { - HSSFSheet sheet = wb.getSheetAt(sheetNo); - HSSFRow row = sheet.createRow(sheet.getPhysicalNumberOfRows()); - row = sheet.createRow(sheet.getPhysicalNumberOfRows()); - sheet.addMergedRegion(new Region((short)(sheet.getPhysicalNumberOfRows()-1),(short)0,(short)(sheet.getPhysicalNumberOfRows()-1),(short)5)); - HSSFCell cell = row.createCell((short)0); - cell.setCellValue(strTypeDescription); - cell.setCellStyle(cellStyle); - row = sheet.createRow(sheet.getPhysicalNumberOfRows()); - } - - private void createDVDeescriptionRow( HSSFWorkbook wb, int sheetNo , HSSFCellStyle cellStyle, String strTypeDescription ) - { - HSSFSheet sheet = wb.getSheetAt(sheetNo); - HSSFRow row = sheet.getRow(sheet.getPhysicalNumberOfRows()-1); - sheet.addMergedRegion(new Region((short)(sheet.getPhysicalNumberOfRows()-1),(short)0,(short)(sheet.getPhysicalNumberOfRows()-1),(short)5)); - HSSFCell cell = row.createCell((short)0); - cell.setCellValue(strTypeDescription); - cell.setCellStyle(cellStyle); - row = sheet.createRow(sheet.getPhysicalNumberOfRows()); - } - - private void createHeaderRow( HSSFWorkbook wb, int sheetNo , HSSFCellStyle cellStyle ) - { - HSSFSheet sheet = wb.getSheetAt(sheetNo); - HSSFRow row = sheet.createRow(sheet.getPhysicalNumberOfRows()); - row.setHeight((short)400); - for ( int i=0; i<6; i++ ) - { - row.createCell((short)i).setCellStyle( cellStyle ); - if ( i==2 || i==3 || i==4 ) - { - sheet.setColumnWidth( (short) i, (short) 3500); - } - else if ( i== 5) - { - sheet.setColumnWidth( (short) i, (short) 10000); - } - else - { - sheet.setColumnWidth( (short) i, (short) 8000); - } - } - HSSFCell cell = row.getCell((short)0); - cell.setCellValue("Data validation cells"); - cell = row.getCell((short)1); - cell.setCellValue("Condition"); - cell = row.getCell((short)2); - cell.setCellValue("Allow blank"); - cell = row.getCell((short)3); - cell.setCellValue("Prompt box"); - cell = row.getCell((short)4); - cell.setCellValue("Error box"); - cell = row.getCell((short)5); - cell.setCellValue("Other settings"); - } - - private HSSFCellStyle createHeaderStyle(HSSFWorkbook wb) - { - HSSFFont font = wb.createFont(); - font.setColor( HSSFColor.WHITE.index ); - font.setBoldweight(HSSFFont.BOLDWEIGHT_BOLD); - - HSSFCellStyle cellStyle = wb.createCellStyle(); - cellStyle.setFillForegroundColor(HSSFColor.BLUE_GREY.index); - cellStyle.setFillPattern(HSSFCellStyle.SOLID_FOREGROUND); - cellStyle.setAlignment(HSSFCellStyle.ALIGN_CENTER); - cellStyle.setVerticalAlignment(HSSFCellStyle.VERTICAL_CENTER); - cellStyle.setBorderLeft(HSSFCellStyle.BORDER_THIN); - cellStyle.setLeftBorderColor(HSSFColor.WHITE.index); - cellStyle.setBorderTop(HSSFCellStyle.BORDER_THIN); - cellStyle.setTopBorderColor(HSSFColor.WHITE.index); - cellStyle.setBorderRight(HSSFCellStyle.BORDER_THIN); - cellStyle.setRightBorderColor(HSSFColor.WHITE.index); - cellStyle.setBorderBottom(HSSFCellStyle.BORDER_THIN); - cellStyle.setBottomBorderColor(HSSFColor.WHITE.index); - cellStyle.setFont(font); - return cellStyle; - } - - private HSSFCellStyle createStyle( HSSFWorkbook wb, short h_align, short color, boolean bold ) - { - HSSFFont font = wb.createFont(); - if ( bold ) - { - font.setBoldweight(HSSFFont.BOLDWEIGHT_BOLD); - } - - HSSFCellStyle cellStyle = wb.createCellStyle(); - cellStyle.setFont(font); - cellStyle.setFillForegroundColor(color); - cellStyle.setFillPattern(HSSFCellStyle.SOLID_FOREGROUND); - cellStyle.setVerticalAlignment(HSSFCellStyle.VERTICAL_CENTER); - cellStyle.setAlignment(h_align); - cellStyle.setBorderLeft(HSSFCellStyle.BORDER_THIN); - cellStyle.setLeftBorderColor(HSSFColor.BLACK.index); - cellStyle.setBorderTop(HSSFCellStyle.BORDER_THIN); - cellStyle.setTopBorderColor(HSSFColor.BLACK.index); - cellStyle.setBorderRight(HSSFCellStyle.BORDER_THIN); - cellStyle.setRightBorderColor(HSSFColor.BLACK.index); - cellStyle.setBorderBottom(HSSFCellStyle.BORDER_THIN); - cellStyle.setBottomBorderColor(HSSFColor.BLACK.index); - - return cellStyle; - } - - private HSSFCellStyle createStyle( HSSFWorkbook wb, short h_align ) - { - return this.createStyle(wb, h_align, HSSFColor.WHITE.index, false); - } - - private void writeDataValidationSettings( HSSFSheet sheet, HSSFCellStyle style_1, HSSFCellStyle style_2, String strCondition, boolean allowEmpty, boolean inputBox, boolean errorBox ) - { - HSSFRow row = sheet.createRow( sheet.getPhysicalNumberOfRows() ); - //condition's string - HSSFCell cell = row.createCell((short)1); - cell.setCellStyle(style_1); - cell.setCellValue(strCondition); - //allow empty cells - cell = row.createCell((short)2); - cell.setCellStyle(style_2); - cell.setCellValue( ((allowEmpty) ? "yes" : "no") ); - //show input box - cell = row.createCell((short)3); - cell.setCellStyle(style_2); - cell.setCellValue( ((inputBox) ? "yes" : "no") ); - //show error box - cell = row.createCell((short)4); - cell.setCellStyle(style_2); - cell.setCellValue( ((errorBox) ? "yes" : "no") ); - } - - private void setCellFormat( HSSFSheet sheet, HSSFCellStyle cell_style ) - { - HSSFRow row = sheet.getRow( sheet.getPhysicalNumberOfRows() -1 ); - HSSFCell cell = row.createCell((short)0); - cell.setCellStyle(cell_style); - } - - private void writeOtherSettings( HSSFSheet sheet, HSSFCellStyle style, String strStettings ) - { - HSSFRow row = sheet.getRow( sheet.getPhysicalNumberOfRows() -1 ); - HSSFCell cell = row.createCell((short)5); - cell.setCellStyle(style); - cell.setCellValue(strStettings); - } - - public static void main(String[] args) - { - junit.textui.TestRunner.run(TestDataValidation.class); +public final class TestDataValidation extends TestCase { + + /** Convenient access to ERROR_STYLE constants */ + /*package*/ static final HSSFDataValidation.ErrorStyle ES = null; + /** Convenient access to OPERATOR constants */ + /*package*/ static final DVConstraint.ValidationType VT = null; + /** Convenient access to OPERATOR constants */ + /*package*/ static final DVConstraint.OperatorType OP = null; + + private static void log(String msg) { + if (false) { // successful tests should be silent + System.out.println(msg); + } + } + + private static final class ValidationAdder { + + private final HSSFCellStyle _style_1; + private final HSSFCellStyle _style_2; + private final int _validationType; + private final HSSFSheet _sheet; + private int _currentRowIndex; + private final HSSFCellStyle _cellStyle; + + public ValidationAdder(HSSFSheet fSheet, HSSFCellStyle style_1, HSSFCellStyle style_2, + HSSFCellStyle cellStyle, int validationType) { + _sheet = fSheet; + _style_1 = style_1; + _style_2 = style_2; + _cellStyle = cellStyle; + _validationType = validationType; + _currentRowIndex = fSheet.getPhysicalNumberOfRows(); + } + public void addValidation(int operatorType, String firstFormula, String secondFormula, + int errorStyle, String ruleDescr, String promptDescr, + boolean allowEmpty, boolean inputBox, boolean errorBox) { + String[] explicitListValues = null; + + addValidationInternal(operatorType, firstFormula, secondFormula, errorStyle, ruleDescr, + promptDescr, allowEmpty, inputBox, errorBox, true, + explicitListValues); + } + + private void addValidationInternal(int operatorType, String firstFormula, + String secondFormula, int errorStyle, String ruleDescr, String promptDescr, + boolean allowEmpty, boolean inputBox, boolean errorBox, boolean suppressDropDown, + String[] explicitListValues) { + int rowNum = _currentRowIndex++; + + DVConstraint dc = createConstraint(operatorType, firstFormula, secondFormula, explicitListValues); + + HSSFDataValidation dv = new HSSFDataValidation(new CellRangeAddressList(rowNum, rowNum, 0, 0), dc); + + dv.setEmptyCellAllowed(allowEmpty); + dv.setErrorStyle(errorStyle); + dv.createErrorBox("Invalid Input", "Something is wrong - check condition!"); + dv.createPromptBox("Validated Cell", "Allowable values have been restricted"); + + dv.setShowPromptBox(inputBox); + dv.setShowErrorBox(errorBox); + dv.setSuppressDropDownArrow(suppressDropDown); + + + _sheet.addValidationData(dv); + writeDataValidationSettings(_sheet, _style_1, _style_2, ruleDescr, allowEmpty, + inputBox, errorBox); + if (_cellStyle != null) { + HSSFRow row = _sheet.getRow(_sheet.getPhysicalNumberOfRows() - 1); + HSSFCell cell = row.createCell((short) 0); + cell.setCellStyle(_cellStyle); + } + writeOtherSettings(_sheet, _style_1, promptDescr); + } + private DVConstraint createConstraint(int operatorType, String firstFormula, + String secondFormula, String[] explicitListValues) { + if (_validationType == VT.LIST) { + if (explicitListValues != null) { + return DVConstraint.createExplicitListConstraint(explicitListValues); + } + return DVConstraint.createFormulaListConstraint(firstFormula); + } + if (_validationType == VT.TIME) { + return DVConstraint.createTimeConstraint(operatorType, firstFormula, secondFormula); + } + if (_validationType == VT.DATE) { + return DVConstraint.createDateConstraint(operatorType, firstFormula, secondFormula, null); + } + if (_validationType == VT.FORMULA) { + return DVConstraint.createCustomFormulaConstraint(firstFormula); + } + return DVConstraint.createNumericConstraint(_validationType, operatorType, firstFormula, secondFormula); + } + /** + * writes plain text values into cells in a tabular format to form comments readable from within + * the spreadsheet. + */ + private static void writeDataValidationSettings(HSSFSheet sheet, HSSFCellStyle style_1, + HSSFCellStyle style_2, String strCondition, boolean allowEmpty, boolean inputBox, + boolean errorBox) { + HSSFRow row = sheet.createRow(sheet.getPhysicalNumberOfRows()); + // condition's string + HSSFCell cell = row.createCell((short) 1); + cell.setCellStyle(style_1); + setCellValue(cell, strCondition); + // allow empty cells + cell = row.createCell((short) 2); + cell.setCellStyle(style_2); + setCellValue(cell, ((allowEmpty) ? "yes" : "no")); + // show input box + cell = row.createCell((short) 3); + cell.setCellStyle(style_2); + setCellValue(cell, ((inputBox) ? "yes" : "no")); + // show error box + cell = row.createCell((short) 4); + cell.setCellStyle(style_2); + setCellValue(cell, ((errorBox) ? "yes" : "no")); + } + private static void writeOtherSettings(HSSFSheet sheet, HSSFCellStyle style, + String strStettings) { + HSSFRow row = sheet.getRow(sheet.getPhysicalNumberOfRows() - 1); + HSSFCell cell = row.createCell((short) 5); + cell.setCellStyle(style); + setCellValue(cell, strStettings); + } + public void addListValidation(String[] explicitListValues, String listFormula, String listValsDescr, + boolean allowEmpty, boolean suppressDropDown) { + String promptDescr = (allowEmpty ? "empty ok" : "not empty") + + ", " + (suppressDropDown ? "no drop-down" : "drop-down"); + addValidationInternal(VT.LIST, listFormula, null, ES.STOP, listValsDescr, promptDescr, + allowEmpty, false, true, suppressDropDown, explicitListValues); + } + } + + /** + * Manages the cell styles used for formatting the output spreadsheet + */ + private static final class WorkbookFormatter { + + private final HSSFWorkbook _wb; + private final HSSFCellStyle _style_1; + private final HSSFCellStyle _style_2; + private final HSSFCellStyle _style_3; + private final HSSFCellStyle _style_4; + private HSSFSheet _currentSheet; + + public WorkbookFormatter(HSSFWorkbook wb) { + _wb = wb; + _style_1 = createStyle( wb, HSSFCellStyle.ALIGN_LEFT ); + _style_2 = createStyle( wb, HSSFCellStyle.ALIGN_CENTER ); + _style_3 = createStyle( wb, HSSFCellStyle.ALIGN_CENTER, HSSFColor.GREY_25_PERCENT.index, true ); + _style_4 = createHeaderStyle(wb); + } + + private static HSSFCellStyle createStyle(HSSFWorkbook wb, short h_align, short color, + boolean bold) { + HSSFFont font = wb.createFont(); + if (bold) { + font.setBoldweight(HSSFFont.BOLDWEIGHT_BOLD); + } + + HSSFCellStyle cellStyle = wb.createCellStyle(); + cellStyle.setFont(font); + cellStyle.setFillForegroundColor(color); + cellStyle.setFillPattern(HSSFCellStyle.SOLID_FOREGROUND); + cellStyle.setVerticalAlignment(HSSFCellStyle.VERTICAL_CENTER); + cellStyle.setAlignment(h_align); + cellStyle.setBorderLeft(HSSFCellStyle.BORDER_THIN); + cellStyle.setLeftBorderColor(HSSFColor.BLACK.index); + cellStyle.setBorderTop(HSSFCellStyle.BORDER_THIN); + cellStyle.setTopBorderColor(HSSFColor.BLACK.index); + cellStyle.setBorderRight(HSSFCellStyle.BORDER_THIN); + cellStyle.setRightBorderColor(HSSFColor.BLACK.index); + cellStyle.setBorderBottom(HSSFCellStyle.BORDER_THIN); + cellStyle.setBottomBorderColor(HSSFColor.BLACK.index); + + return cellStyle; + } + + private static HSSFCellStyle createStyle(HSSFWorkbook wb, short h_align) { + return createStyle(wb, h_align, HSSFColor.WHITE.index, false); + } + private static HSSFCellStyle createHeaderStyle(HSSFWorkbook wb) { + HSSFFont font = wb.createFont(); + font.setColor( HSSFColor.WHITE.index ); + font.setBoldweight(HSSFFont.BOLDWEIGHT_BOLD); + + HSSFCellStyle cellStyle = wb.createCellStyle(); + cellStyle.setFillForegroundColor(HSSFColor.BLUE_GREY.index); + cellStyle.setFillPattern(HSSFCellStyle.SOLID_FOREGROUND); + cellStyle.setAlignment(HSSFCellStyle.ALIGN_CENTER); + cellStyle.setVerticalAlignment(HSSFCellStyle.VERTICAL_CENTER); + cellStyle.setBorderLeft(HSSFCellStyle.BORDER_THIN); + cellStyle.setLeftBorderColor(HSSFColor.WHITE.index); + cellStyle.setBorderTop(HSSFCellStyle.BORDER_THIN); + cellStyle.setTopBorderColor(HSSFColor.WHITE.index); + cellStyle.setBorderRight(HSSFCellStyle.BORDER_THIN); + cellStyle.setRightBorderColor(HSSFColor.WHITE.index); + cellStyle.setBorderBottom(HSSFCellStyle.BORDER_THIN); + cellStyle.setBottomBorderColor(HSSFColor.WHITE.index); + cellStyle.setFont(font); + return cellStyle; + } + + + public HSSFSheet createSheet(String sheetName) { + _currentSheet = _wb.createSheet(sheetName); + return _currentSheet; + } + public void createDVTypeRow(String strTypeDescription) { + HSSFSheet sheet = _currentSheet; + HSSFRow row = sheet.createRow(sheet.getPhysicalNumberOfRows()); + row = sheet.createRow(sheet.getPhysicalNumberOfRows()); + sheet.addMergedRegion(new CellRangeAddress(sheet.getPhysicalNumberOfRows()-1, sheet.getPhysicalNumberOfRows()-1, 0, 5)); + HSSFCell cell = row.createCell((short) 0); + setCellValue(cell, strTypeDescription); + cell.setCellStyle(_style_3); + row = sheet.createRow(sheet.getPhysicalNumberOfRows()); + } + + public void createHeaderRow() { + HSSFSheet sheet = _currentSheet; + HSSFRow row = sheet.createRow(sheet.getPhysicalNumberOfRows()); + row.setHeight((short) 400); + for (int i = 0; i < 6; i++) { + row.createCell((short) i).setCellStyle(_style_4); + if (i == 2 || i == 3 || i == 4) { + sheet.setColumnWidth((short) i, (short) 3500); + } else if (i == 5) { + sheet.setColumnWidth((short) i, (short) 10000); + } else { + sheet.setColumnWidth((short) i, (short) 8000); + } + } + HSSFCell cell = row.getCell((short) 0); + setCellValue(cell, "Data validation cells"); + cell = row.getCell((short) 1); + setCellValue(cell, "Condition"); + cell = row.getCell((short) 2); + setCellValue(cell, "Allow blank"); + cell = row.getCell((short) 3); + setCellValue(cell, "Prompt box"); + cell = row.getCell((short) 4); + setCellValue(cell, "Error box"); + cell = row.getCell((short) 5); + setCellValue(cell, "Other settings"); + } + + public ValidationAdder createValidationAdder(HSSFCellStyle cellStyle, int dataValidationType) { + return new ValidationAdder(_currentSheet, _style_1, _style_2, cellStyle, dataValidationType); + } + + public void createDVDescriptionRow(String strTypeDescription) { + HSSFSheet sheet = _currentSheet; + HSSFRow row = sheet.getRow(sheet.getPhysicalNumberOfRows()-1); + sheet.addMergedRegion(new CellRangeAddress(sheet.getPhysicalNumberOfRows()-1, sheet.getPhysicalNumberOfRows()-1, 0, 5)); + HSSFCell cell = row.createCell((short)0); + setCellValue(cell, strTypeDescription); + cell.setCellStyle(_style_3); + row = sheet.createRow(sheet.getPhysicalNumberOfRows()); + } + } + + + private void addCustomValidations(WorkbookFormatter wf) { + wf.createSheet("Custom"); + wf.createHeaderRow(); + + ValidationAdder va = wf.createValidationAdder(null, VT.FORMULA); + va.addValidation(OP.BETWEEN, "ISNUMBER($A2)", null, ES.STOP, "ISNUMBER(A2)", "Error box type = STOP", true, true, true); + va.addValidation(OP.BETWEEN, "IF(SUM(A2:A3)=5,TRUE,FALSE)", null, ES.WARNING, "IF(SUM(A2:A3)=5,TRUE,FALSE)", "Error box type = WARNING", false, false, true); + } + + private static void addSimpleNumericValidations(WorkbookFormatter wf) { + // data validation's number types + wf.createSheet("Numbers"); + + // "Whole number" validation type + wf.createDVTypeRow("Whole number"); + wf.createHeaderRow(); + + ValidationAdder va = wf.createValidationAdder(null, VT.INTEGER); + va.addValidation(OP.BETWEEN, "2", "6", ES.STOP, "Between 2 and 6 ", "Error box type = STOP", true, true, true); + va.addValidation(OP.NOT_BETWEEN, "2", "6", ES.INFO, "Not between 2 and 6 ", "Error box type = INFO", false, true, true); + va.addValidation(OP.EQUAL, "=3+2", null, ES.WARNING, "Equal to (3+2)", "Error box type = WARNING", false, false, true); + va.addValidation(OP.NOT_EQUAL, "3", null, ES.WARNING, "Not equal to 3", "-", false, false, false); + va.addValidation(OP.GREATER_THAN, "3", null, ES.WARNING, "Greater than 3", "-", true, false, false); + va.addValidation(OP.LESS_THAN, "3", null, ES.WARNING, "Less than 3", "-", true, true, false); + va.addValidation(OP.GREATER_OR_EQUAL, "4", null, ES.STOP, "Greater than or equal to 4", "Error box type = STOP", true, false, true); + va.addValidation(OP.LESS_OR_EQUAL, "4", null, ES.STOP, "Less than or equal to 4", "-", false, true, false); + + // "Decimal" validation type + wf.createDVTypeRow("Decimal"); + wf.createHeaderRow(); + + va = wf.createValidationAdder(null, VT.DECIMAL); + va.addValidation(OP.BETWEEN, "2", "6", ES.STOP, "Between 2 and 6 ", "Error box type = STOP", true, true, true); + va.addValidation(OP.NOT_BETWEEN, "2", "6", ES.INFO, "Not between 2 and 6 ", "Error box type = INFO", false, true, true); + va.addValidation(OP.EQUAL, "3", null, ES.WARNING, "Equal to 3", "Error box type = WARNING", false, false, true); + va.addValidation(OP.NOT_EQUAL, "3", null, ES.WARNING, "Not equal to 3", "-", false, false, false); + va.addValidation(OP.GREATER_THAN, "=12/6", null, ES.WARNING, "Greater than (12/6)", "-", true, false, false); + va.addValidation(OP.LESS_THAN, "3", null, ES.WARNING, "Less than 3", "-", true, true, false); + va.addValidation(OP.GREATER_OR_EQUAL, "4", null, ES.STOP, "Greater than or equal to 4", "Error box type = STOP", true, false, true); + va.addValidation(OP.LESS_OR_EQUAL, "4", null, ES.STOP, "Less than or equal to 4", "-", false, true, false); + } + + private static void addListValidations(WorkbookFormatter wf, HSSFWorkbook wb) { + final String cellStrValue + = "a b c d e f g h i j k l m n o p r s t u v x y z w 0 1 2 3 4 " + + "a b c d e f g h i j k l m n o p r s t u v x y z w 0 1 2 3 4 " + + "a b c d e f g h i j k l m n o p r s t u v x y z w 0 1 2 3 4 " + + "a b c d e f g h i j k l m n o p r s t u v x y z w 0 1 2 3 4 "; + final String dataSheetName = "list_data"; + // "List" Data Validation type + HSSFSheet fSheet = wf.createSheet("Lists"); + HSSFSheet dataSheet = wb.createSheet(dataSheetName); + + + wf.createDVTypeRow("Explicit lists - list items are explicitly provided"); + wf.createDVDescriptionRow("Disadvantage - sum of item's length should be less than 255 characters"); + wf.createHeaderRow(); + + ValidationAdder va = wf.createValidationAdder(null, VT.LIST); + String listValsDescr = "POIFS,HSSF,HWPF,HPSF"; + String[] listVals = listValsDescr.split(","); + va.addListValidation(listVals, null, listValsDescr, false, false); + va.addListValidation(listVals, null, listValsDescr, false, true); + va.addListValidation(listVals, null, listValsDescr, true, false); + va.addListValidation(listVals, null, listValsDescr, true, true); + + + + wf.createDVTypeRow("Reference lists - list items are taken from others cells"); + wf.createDVDescriptionRow("Advantage - no restriction regarding the sum of item's length"); + wf.createHeaderRow(); + va = wf.createValidationAdder(null, VT.LIST); + String strFormula = "$A$30:$A$39"; + va.addListValidation(null, strFormula, strFormula, false, false); + + strFormula = dataSheetName + "!$A$1:$A$10"; + va.addListValidation(null, strFormula, strFormula, false, false); + HSSFName namedRange = wb.createName(); + namedRange.setNameName("myName"); + namedRange.setReference(dataSheetName + "!$A$2:$A$7"); + strFormula = "myName"; + va.addListValidation(null, strFormula, strFormula, false, false); + strFormula = "offset(myName, 2, 1, 4, 2)"; // Note about last param '2': + // - Excel expects single row or single column when entered in UI, but process this OK otherwise + va.addListValidation(null, strFormula, strFormula, false, false); + + // add list data on same sheet + for (int i = 0; i < 10; i++) { + HSSFRow currRow = fSheet.createRow(i + 29); + setCellValue(currRow.createCell((short) 0), cellStrValue); + } + // add list data on another sheet + for (int i = 0; i < 10; i++) { + HSSFRow currRow = dataSheet.createRow(i + 0); + setCellValue(currRow.createCell((short) 0), "Data a" + i); + setCellValue(currRow.createCell((short) 1), "Data b" + i); + setCellValue(currRow.createCell((short) 2), "Data c" + i); + } + } + + private static void addDateTimeValidations(WorkbookFormatter wf, HSSFWorkbook wb) { + wf.createSheet("Dates and Times"); + + HSSFDataFormat dataFormat = wb.createDataFormat(); + short fmtDate = dataFormat.getFormat("m/d/yyyy"); + short fmtTime = dataFormat.getFormat("h:mm"); + HSSFCellStyle cellStyle_date = wb.createCellStyle(); + cellStyle_date.setDataFormat(fmtDate); + HSSFCellStyle cellStyle_time = wb.createCellStyle(); + cellStyle_time.setDataFormat(fmtTime); + + wf.createDVTypeRow("Date ( cells are already formated as date - m/d/yyyy)"); + wf.createHeaderRow(); + + ValidationAdder va = wf.createValidationAdder(cellStyle_date, VT.DATE); + va.addValidation(OP.BETWEEN, "2004/01/02", "2004/01/06", ES.STOP, "Between 1/2/2004 and 1/6/2004 ", "Error box type = STOP", true, true, true); + va.addValidation(OP.NOT_BETWEEN, "2004/01/01", "2004/01/06", ES.INFO, "Not between 1/2/2004 and 1/6/2004 ", "Error box type = INFO", false, true, true); + va.addValidation(OP.EQUAL, "2004/03/02", null, ES.WARNING, "Equal to 3/2/2004", "Error box type = WARNING", false, false, true); + va.addValidation(OP.NOT_EQUAL, "2004/03/02", null, ES.WARNING, "Not equal to 3/2/2004", "-", false, false, false); + va.addValidation(OP.GREATER_THAN,"=DATEVALUE(\"4-Jul-2001\")", null, ES.WARNING, "Greater than DATEVALUE('4-Jul-2001')", "-", true, false, false); + va.addValidation(OP.LESS_THAN, "2004/03/02", null, ES.WARNING, "Less than 3/2/2004", "-", true, true, false); + va.addValidation(OP.GREATER_OR_EQUAL, "2004/03/02", null, ES.STOP, "Greater than or equal to 3/2/2004", "Error box type = STOP", true, false, true); + va.addValidation(OP.LESS_OR_EQUAL, "2004/03/04", null, ES.STOP, "Less than or equal to 3/4/2004", "-", false, true, false); + + // "Time" validation type + wf.createDVTypeRow("Time ( cells are already formated as time - h:mm)"); + wf.createHeaderRow(); + + va = wf.createValidationAdder(cellStyle_time, VT.TIME); + va.addValidation(OP.BETWEEN, "12:00", "16:00", ES.STOP, "Between 12:00 and 16:00 ", "Error box type = STOP", true, true, true); + va.addValidation(OP.NOT_BETWEEN, "12:00", "16:00", ES.INFO, "Not between 12:00 and 16:00 ", "Error box type = INFO", false, true, true); + va.addValidation(OP.EQUAL, "13:35", null, ES.WARNING, "Equal to 13:35", "Error box type = WARNING", false, false, true); + va.addValidation(OP.NOT_EQUAL, "13:35", null, ES.WARNING, "Not equal to 13:35", "-", false, false, false); + va.addValidation(OP.GREATER_THAN,"12:00", null, ES.WARNING, "Greater than 12:00", "-", true, false, false); + va.addValidation(OP.LESS_THAN, "=1/2", null, ES.WARNING, "Less than (1/2) -> 12:00", "-", true, true, false); + va.addValidation(OP.GREATER_OR_EQUAL, "14:00", null, ES.STOP, "Greater than or equal to 14:00", "Error box type = STOP", true, false, true); + va.addValidation(OP.LESS_OR_EQUAL,"14:00", null, ES.STOP, "Less than or equal to 14:00", "-", false, true, false); + } + + private static void addTextLengthValidations(WorkbookFormatter wf) { + wf.createSheet("Text lengths"); + wf.createHeaderRow(); + + ValidationAdder va = wf.createValidationAdder(null, VT.TEXT_LENGTH); + va.addValidation(OP.BETWEEN, "2", "6", ES.STOP, "Between 2 and 6 ", "Error box type = STOP", true, true, true); + va.addValidation(OP.NOT_BETWEEN, "2", "6", ES.INFO, "Not between 2 and 6 ", "Error box type = INFO", false, true, true); + va.addValidation(OP.EQUAL, "3", null, ES.WARNING, "Equal to 3", "Error box type = WARNING", false, false, true); + va.addValidation(OP.NOT_EQUAL, "3", null, ES.WARNING, "Not equal to 3", "-", false, false, false); + va.addValidation(OP.GREATER_THAN, "3", null, ES.WARNING, "Greater than 3", "-", true, false, false); + va.addValidation(OP.LESS_THAN, "3", null, ES.WARNING, "Less than 3", "-", true, true, false); + va.addValidation(OP.GREATER_OR_EQUAL, "4", null, ES.STOP, "Greater than or equal to 4", "Error box type = STOP", true, false, true); + va.addValidation(OP.LESS_OR_EQUAL, "4", null, ES.STOP, "Less than or equal to 4", "-", false, true, false); + } + + public void testDataValidation() { + log("\nTest no. 2 - Test Excel's Data validation mechanism"); + HSSFWorkbook wb = new HSSFWorkbook(); + WorkbookFormatter wf = new WorkbookFormatter(wb); + + log(" Create sheet for Data Validation's number types ... "); + addSimpleNumericValidations(wf); + log("done !"); + + log(" Create sheet for 'List' Data Validation type ... "); + addListValidations(wf, wb); + log("done !"); + + log(" Create sheet for 'Date' and 'Time' Data Validation types ... "); + addDateTimeValidations(wf, wb); + log("done !"); + + log(" Create sheet for 'Text length' Data Validation type... "); + addTextLengthValidations(wf); + log("done !"); + + // Custom Validation type + log(" Create sheet for 'Custom' Data Validation type ... "); + addCustomValidations(wf); + log("done !"); + + ByteArrayOutputStream baos = new ByteArrayOutputStream(22000); + try { + wb.write(baos); + baos.close(); + } catch (IOException e) { + throw new RuntimeException(e); + } + byte[] generatedContent = baos.toByteArray(); + boolean isSame; + if (false) { + // TODO - add proof spreadsheet and compare + InputStream proofStream = HSSFTestDataSamples.openSampleFileStream("TestDataValidation.xls"); + isSame = compareStreams(proofStream, generatedContent); + } + isSame = true; + + if (isSame) { + return; + } + File tempDir = new File(System.getProperty("java.io.tmpdir")); + File generatedFile = new File(tempDir, "GeneratedTestDataValidation.xls"); + try { + FileOutputStream fileOut = new FileOutputStream(generatedFile); + fileOut.write(generatedContent); + fileOut.close(); + } catch (IOException e) { + throw new RuntimeException(e); + } + + PrintStream ps = System.out; + + ps.println("This test case has failed because the generated file differs from proof copy '" + ); // TODO+ proofFile.getAbsolutePath() + "'."); + ps.println("The cause is usually a change to this test, or some common spreadsheet generation code. " + + "The developer has to decide whether the changes were wanted or unwanted."); + ps.println("If the changes to the generated version were unwanted, " + + "make the fix elsewhere (do not modify this test or the proof spreadsheet to get the test working)."); + ps.println("If the changes were wanted, make sure to open the newly generated file in Excel " + + "and verify it manually. The new proof file should be submitted after it is verified to be correct."); + ps.println(""); + ps.println("One other possible (but less likely) cause of a failed test is a problem in the " + + "comparison logic used here. Perhaps some extra file regions need to be ignored."); + ps.println("The generated file has been saved to '" + generatedFile.getAbsolutePath() + "' for manual inspection."); + + fail("Generated file differs from proof copy. See sysout comments for details on how to fix."); + + } + + private static boolean compareStreams(InputStream isA, byte[] generatedContent) { + + InputStream isB = new ByteArrayInputStream(generatedContent); + + // The allowable regions where the generated file can differ from the + // proof should be small (i.e. much less than 1K) + int[] allowableDifferenceRegions = { + 0x0228, 16, // a region of the file containing the OS username + 0x506C, 8, // See RootProperty (super fields _seconds_2 and _days_2) + }; + int[] diffs = StreamUtility.diffStreams(isA, isB, allowableDifferenceRegions); + if (diffs == null) { + return true; + } + System.err.println("Diff from proof: "); + for (int i = 0; i < diffs.length; i++) { + System.err.println("diff at offset: 0x" + Integer.toHexString(diffs[i])); + } + return false; + } + + + + + + /* package */ static void setCellValue(HSSFCell cell, String text) { + cell.setCellValue(new HSSFRichTextString(text)); + } + + public void testAddToExistingSheet() { + + // dvEmpty.xls is a simple one sheet workbook. With a DataValidations header record but no + // DataValidations. It's important that the example has one SHEETPROTECTION record. + // Such a workbook can be created in Excel (2007) by adding datavalidation for one cell + // and then deleting the row that contains the cell. + HSSFWorkbook wb = HSSFTestDataSamples.openSampleWorkbook("dvEmpty.xls"); + int dvRow = 0; + HSSFSheet sheet = wb.getSheetAt(0); + DVConstraint dc = DVConstraint.createNumericConstraint(VT.INTEGER, OP.EQUAL, "42", null); + HSSFDataValidation dv = new HSSFDataValidation(new CellRangeAddressList(dvRow, dvRow, 0, 0), dc); + + dv.setEmptyCellAllowed(false); + dv.setErrorStyle(ES.STOP); + dv.setShowPromptBox(true); + dv.createErrorBox("Xxx", "Yyy"); + dv.setSuppressDropDownArrow(true); + + sheet.addValidationData(dv); + + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + try { + wb.write(baos); + } catch (IOException e) { + throw new RuntimeException(e); + } + + byte[] wbData = baos.toByteArray(); + + if (false) { // TODO (Jul 2008) fix EventRecordFactory to process unknown records, (and DV records for that matter) + EventRecordFactory erf = new EventRecordFactory(); + ERFListener erfListener = null; // new MyERFListener(); + erf.registerListener(erfListener, null); + try { + POIFSFileSystem fs = new POIFSFileSystem(new ByteArrayInputStream(baos.toByteArray())); + erf.processRecords(fs.createDocumentInputStream("Workbook")); + } catch (RecordFormatException e) { + throw new RuntimeException(e); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + // else verify record ordering by navigating the raw bytes + + byte[] dvHeaderRecStart= { (byte)0xB2, 0x01, 0x12, 0x00, }; + int dvHeaderOffset = findIndex(wbData, dvHeaderRecStart); + assertTrue(dvHeaderOffset > 0); + int nextRecIndex = dvHeaderOffset + 22; + int nextSid + = ((wbData[nextRecIndex + 0] << 0) & 0x00FF) + + ((wbData[nextRecIndex + 1] << 8) & 0xFF00) + ; + // nextSid should be for a DVRecord. If anything comes between the DV header record + // and the DV records, Excel will not be able to open the workbook without error. + + if (nextSid == 0x0867) { + throw new AssertionFailedError("Identified bug 45519"); + } + assertEquals(DVRecord.sid, nextSid); + } + private int findIndex(byte[] largeData, byte[] searchPattern) { + byte firstByte = searchPattern[0]; + for (int i = 0; i < largeData.length; i++) { + if(largeData[i] != firstByte) { + continue; + } + boolean match = true; + for (int j = 1; j < searchPattern.length; j++) { + if(searchPattern[j] != largeData[i+j]) { + match = false; + break; + } + } + if (match) { + return i; + } + } + return -1; + } } diff --git a/src/testcases/org/apache/poi/hssf/usermodel/TestHSSFComment.java b/src/testcases/org/apache/poi/hssf/usermodel/TestHSSFComment.java index 369aa2665..978f43577 100644 --- a/src/testcases/org/apache/poi/hssf/usermodel/TestHSSFComment.java +++ b/src/testcases/org/apache/poi/hssf/usermodel/TestHSSFComment.java @@ -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(); + } } diff --git a/src/testcases/org/apache/poi/hssf/usermodel/TestHSSFConditionalFormatting.java b/src/testcases/org/apache/poi/hssf/usermodel/TestHSSFConditionalFormatting.java index 6dbcf815d..fcb0b8f41 100644 --- a/src/testcases/org/apache/poi/hssf/usermodel/TestHSSFConditionalFormatting.java +++ b/src/testcases/org/apache/poi/hssf/usermodel/TestHSSFConditionalFormatting.java @@ -21,7 +21,9 @@ import junit.framework.TestCase; import org.apache.poi.hssf.record.CFRuleRecord.ComparisonOperator; import org.apache.poi.hssf.util.HSSFColor; +import org.apache.poi.ss.util.CellRangeAddress; import org.apache.poi.ss.util.Region; + /** * * @author Dmitriy Kumshayev @@ -57,9 +59,8 @@ public final class TestHSSFConditionalFormatting extends TestCase }; short col = 1; - Region [] regions = - { - new Region(0,col,65535,col) + CellRangeAddress [] regions = { + new CellRangeAddress(0, 65535, col, col) }; sheetCF.addConditionalFormatting(regions, cfRules); @@ -72,14 +73,14 @@ public final class TestHSSFConditionalFormatting extends TestCase HSSFConditionalFormatting cf = sheetCF.getConditionalFormattingAt(0); assertNotNull(cf); - regions = cf.getFormattingRegions(); + regions = cf.getFormattingRanges(); assertNotNull(regions); assertEquals(1, regions.length); - Region r = regions[0]; - assertEquals(1, r.getColumnFrom()); - assertEquals(1, r.getColumnTo()); - assertEquals(0, r.getRowFrom()); - assertEquals(65535, r.getRowTo()); + CellRangeAddress r = regions[0]; + assertEquals(1, r.getFirstColumn()); + assertEquals(1, r.getLastColumn()); + assertEquals(0, r.getFirstRow()); + assertEquals(65535, r.getLastRow()); assertEquals(2, cf.getNumberOfRules()); diff --git a/src/testcases/org/apache/poi/hssf/usermodel/TestHSSFDataFormatter.java b/src/testcases/org/apache/poi/hssf/usermodel/TestHSSFDataFormatter.java index f865d6e49..e24f30da6 100644 --- a/src/testcases/org/apache/poi/hssf/usermodel/TestHSSFDataFormatter.java +++ b/src/testcases/org/apache/poi/hssf/usermodel/TestHSSFDataFormatter.java @@ -19,6 +19,7 @@ package org.apache.poi.hssf.usermodel; import java.text.DecimalFormat; import java.text.Format; +import java.util.Date; import java.util.Iterator; import junit.framework.TestCase; diff --git a/src/testcases/org/apache/poi/hssf/usermodel/TestHSSFDateUtil.java b/src/testcases/org/apache/poi/hssf/usermodel/TestHSSFDateUtil.java index 281d1b1cb..377a8ffe2 100644 --- a/src/testcases/org/apache/poi/hssf/usermodel/TestHSSFDateUtil.java +++ b/src/testcases/org/apache/poi/hssf/usermodel/TestHSSFDateUtil.java @@ -39,7 +39,7 @@ import org.apache.poi.hssf.model.Workbook; * @author Alex Jacoby (ajacoby at gmail.com) * @version %I%, %G% */ -public class TestHSSFDateUtil extends TestCase { +public final class TestHSSFDateUtil extends TestCase { public static final int CALENDAR_JANUARY = 0; public static final int CALENDAR_FEBRUARY = 1; @@ -47,11 +47,6 @@ public class TestHSSFDateUtil extends TestCase { public static final int CALENDAR_APRIL = 3; public static final int CALENDAR_JULY = 6; public static final int CALENDAR_OCTOBER = 9; - - public TestHSSFDateUtil(String s) - { - super(s); - } /** * Checks the date conversion functions in the HSSFDateUtil class. @@ -193,14 +188,13 @@ public class TestHSSFDateUtil extends TestCase { } /** - * Tests that we deal with timezones properly + * Tests that we deal with time-zones properly */ public void testCalendarConversion() { GregorianCalendar date = new GregorianCalendar(2002, 0, 1, 12, 1, 1); Date expected = date.getTime(); - double expectedExcel = HSSFDateUtil.getExcelDate(expected); - // Iteratating over the hours exposes any rounding issues. + // Iterating over the hours exposes any rounding issues. for (int hour = -12; hour <= 12; hour++) { String id = "GMT" + (hour < 0 ? "" : "+") + hour + ":00"; @@ -209,7 +203,7 @@ public class TestHSSFDateUtil extends TestCase { double excelDate = HSSFDateUtil.getExcelDate(date, false); Date javaDate = HSSFDateUtil.getJavaDate(excelDate); - // Should match despite timezone + // Should match despite time-zone assertEquals("Checking timezone " + id, expected.getTime(), javaDate.getTime()); } } @@ -402,7 +396,11 @@ public class TestHSSFDateUtil extends TestCase { assertEquals(34519.0, HSSFDateUtil.getExcelDate(createDate(1998, CALENDAR_JULY, 5), true), 0.00001); } - private Date createDate(int year, int month, int day) { + /** + * @param month zero based + * @param day one based + */ + private static Date createDate(int year, int month, int day) { Calendar c = new GregorianCalendar(); c.set(year, month, day, 0, 0, 0); c.set(Calendar.MILLISECOND, 0); @@ -420,10 +418,18 @@ public class TestHSSFDateUtil extends TestCase { calendar = new GregorianCalendar(1901, 0, 1); assertEquals("Checking absolute day (1 Jan 1901)", 366, HSSFDateUtil.absoluteDay(calendar, false)); } + + public void testConvertTime() { + + final double delta = 1E-7; // a couple of digits more accuracy than strictly required + assertEquals(0.5, HSSFDateUtil.convertTime("12:00"), delta); + assertEquals(2.0/3, HSSFDateUtil.convertTime("16:00"), delta); + assertEquals(0.0000116, HSSFDateUtil.convertTime("0:00:01"), delta); + assertEquals(0.7330440, HSSFDateUtil.convertTime("17:35:35"), delta); + } - public static void main(String [] args) { - System.out - .println("Testing org.apache.poi.hssf.usermodel.TestHSSFDateUtil"); - junit.textui.TestRunner.run(TestHSSFDateUtil.class); + public void testParseDate() { + assertEquals(createDate(2008, Calendar.AUGUST, 3), HSSFDateUtil.parseYYYYMMDDDate("2008/08/03")); + assertEquals(createDate(1994, Calendar.MAY, 1), HSSFDateUtil.parseYYYYMMDDDate("1994/05/01")); } } diff --git a/src/testcases/org/apache/poi/hssf/usermodel/TestHSSFSheet.java b/src/testcases/org/apache/poi/hssf/usermodel/TestHSSFSheet.java index 90971b3c0..6fcd38498 100644 --- a/src/testcases/org/apache/poi/hssf/usermodel/TestHSSFSheet.java +++ b/src/testcases/org/apache/poi/hssf/usermodel/TestHSSFSheet.java @@ -36,6 +36,7 @@ import org.apache.poi.hssf.record.VCenterRecord; import org.apache.poi.hssf.record.WSBoolRecord; import org.apache.poi.hssf.record.WindowTwoRecord; import org.apache.poi.ss.util.Region; +import org.apache.poi.ss.util.CellRangeAddress; /** * Tests HSSFSheet. This test case is very incomplete at the moment. @@ -476,15 +477,15 @@ public final class TestHSSFSheet extends TestCase { public void testRemoveMerged() { HSSFWorkbook wb = new HSSFWorkbook(); HSSFSheet sheet = wb.createSheet(); - Region region = new Region(0, (short)0, 1, (short)1); + CellRangeAddress region = new CellRangeAddress(0, 1, 0, 1); sheet.addMergedRegion(region); - region = new Region(1, (short)0, 2, (short)1); + region = new CellRangeAddress(1, 2, 0, 1); sheet.addMergedRegion(region); sheet.removeMergedRegion(0); - region = sheet.getMergedRegionAt(0); - assertEquals("Left over region should be starting at row 1", 1, region.getRowFrom()); + region = sheet.getMergedRegion(0); + assertEquals("Left over region should be starting at row 1", 1, region.getFirstRow()); sheet.removeMergedRegion(0); @@ -496,15 +497,15 @@ public final class TestHSSFSheet extends TestCase { sheet.removeMergedRegion(0); assertEquals("there should now be zero merged regions!", 0, sheet.getNumMergedRegions()); //add it again! - region.setRowTo(4); + region.setLastRow(4); sheet.addMergedRegion(region); assertEquals("there should now be one merged region!", 1, sheet.getNumMergedRegions()); //should exist now! assertTrue("there isn't more than one merged region in there", 1 <= sheet.getNumMergedRegions()); - region = sheet.getMergedRegionAt(0); - assertEquals("the merged row to doesnt match the one we put in ", 4, region.getRowTo()); + region = sheet.getMergedRegion(0); + assertEquals("the merged row to doesnt match the one we put in ", 4, region.getLastRow()); } public void testShiftMerged() { @@ -518,13 +519,13 @@ public final class TestHSSFSheet extends TestCase { cell = row.createCell((short)1); cell.setCellValue(new HSSFRichTextString("second row, second cell")); - Region region = new Region(1, (short)0, 1, (short)1); + CellRangeAddress region = new CellRangeAddress(1, 1, 0, 1); sheet.addMergedRegion(region); sheet.shiftRows(1, 1, 1); - region = sheet.getMergedRegionAt(0); - assertEquals("Merged region not moved over to row 2", 2, region.getRowFrom()); + region = sheet.getMergedRegion(0); + assertEquals("Merged region not moved over to row 2", 2, region.getFirstRow()); } /** @@ -683,7 +684,7 @@ public final class TestHSSFSheet extends TestCase { assertTrue("Column autosized with only one row: wrong width", sheet.getColumnWidth((short)0) <= maxWithRow1And2); //create a region over the 2nd row and auto size the first column - sheet.addMergedRegion(new Region(1,(short)0,1,(short)1)); + sheet.addMergedRegion(new CellRangeAddress(1,1,0,1)); sheet.autoSizeColumn((short)0); HSSFWorkbook wb2 = HSSFTestDataSamples.writeOutAndReadBack(wb); diff --git a/src/testcases/org/apache/poi/hssf/usermodel/TestWorkbook.java b/src/testcases/org/apache/poi/hssf/usermodel/TestWorkbook.java index a5402f64c..916f6f4f0 100644 --- a/src/testcases/org/apache/poi/hssf/usermodel/TestWorkbook.java +++ b/src/testcases/org/apache/poi/hssf/usermodel/TestWorkbook.java @@ -32,6 +32,7 @@ import org.apache.poi.hssf.record.LabelSSTRecord; import org.apache.poi.hssf.record.Record; import org.apache.poi.hssf.record.aggregates.ValueRecordsAggregate; import org.apache.poi.poifs.filesystem.POIFSFileSystem; +import org.apache.poi.ss.util.CellRangeAddress; import org.apache.poi.ss.util.Region; import org.apache.poi.util.TempFile; @@ -42,7 +43,7 @@ import org.apache.poi.util.TempFile; * @author Greg Merrill * @author Siggi Cherem */ -public class TestWorkbook extends TestCase { +public final class TestWorkbook extends TestCase { private static final String LAST_NAME_KEY = "lastName"; private static final String FIRST_NAME_KEY = "firstName"; private static final String SSN_KEY = "ssn"; @@ -260,10 +261,10 @@ public class TestWorkbook extends TestCase { HSSFWorkbook workbook = openSample("Employee.xls"); HSSFSheet sheet = workbook.getSheetAt(0); - assertEquals(EMPLOYEE_INFORMATION, sheet.getRow(1).getCell(1).getStringCellValue()); - assertEquals(LAST_NAME_KEY, sheet.getRow(3).getCell(2).getStringCellValue()); - assertEquals(FIRST_NAME_KEY, sheet.getRow(4).getCell(2).getStringCellValue()); - assertEquals(SSN_KEY, sheet.getRow(5).getCell(2).getStringCellValue()); + assertEquals(EMPLOYEE_INFORMATION, sheet.getRow(1).getCell(1).getRichStringCellValue().getString()); + assertEquals(LAST_NAME_KEY, sheet.getRow(3).getCell(2).getRichStringCellValue().getString()); + assertEquals(FIRST_NAME_KEY, sheet.getRow(4).getCell(2).getRichStringCellValue().getString()); + assertEquals(SSN_KEY, sheet.getRow(5).getCell(2).getRichStringCellValue().getString()); } /** @@ -318,13 +319,13 @@ public class TestWorkbook extends TestCase { sheet = workbook.getSheetAt(0); cell = sheet.getRow(0).getCell(1); - assertEquals(REPLACED, cell.getStringCellValue()); + assertEquals(REPLACED, cell.getRichStringCellValue().getString()); cell = sheet.getRow(0).getCell(0); - assertEquals(DO_NOT_REPLACE, cell.getStringCellValue()); + assertEquals(DO_NOT_REPLACE, cell.getRichStringCellValue().getString()); cell = sheet.getRow(1).getCell(0); - assertEquals(REPLACED, cell.getStringCellValue()); + assertEquals(REPLACED, cell.getRichStringCellValue().getString()); cell = sheet.getRow(1).getCell(1); - assertEquals(DO_NOT_REPLACE, cell.getStringCellValue()); + assertEquals(DO_NOT_REPLACE, cell.getRichStringCellValue().getString()); } /** @@ -388,10 +389,10 @@ public class TestWorkbook extends TestCase { workbook = HSSFTestDataSamples.writeOutAndReadBack(workbook); sheet = workbook.getSheetAt(0); - assertEquals(EMPLOYEE_INFORMATION, sheet.getRow(1).getCell(1).getStringCellValue()); - assertEquals(LAST_NAME_VALUE, sheet.getRow(3).getCell(2).getStringCellValue()); - assertEquals(FIRST_NAME_VALUE, sheet.getRow(4).getCell(2).getStringCellValue()); - assertEquals(SSN_VALUE, sheet.getRow(5).getCell(2).getStringCellValue()); + assertEquals(EMPLOYEE_INFORMATION, sheet.getRow(1).getCell(1).getRichStringCellValue().getString()); + assertEquals(LAST_NAME_VALUE, sheet.getRow(3).getCell(2).getRichStringCellValue().getString()); + assertEquals(FIRST_NAME_VALUE, sheet.getRow(4).getCell(2).getRichStringCellValue().getString()); + assertEquals(SSN_VALUE, sheet.getRow(5).getCell(2).getRichStringCellValue().getString()); } /** @@ -421,26 +422,17 @@ public class TestWorkbook extends TestCase { * HSSFSheet last row or first row is incorrect.* */ - - public void testWriteModifySheetMerged() - throws IOException - { - File file = TempFile.createTempFile("testWriteSheetMerged", - ".xls"); - FileOutputStream out = new FileOutputStream(file); - FileInputStream in = null; + public void testWriteModifySheetMerged() { HSSFWorkbook wb = new HSSFWorkbook(); HSSFSheet s = wb.createSheet(); - HSSFRow r = null; - HSSFCell c = null; for (short rownum = ( short ) 0; rownum < 100; rownum++) { - r = s.createRow(rownum); + HSSFRow r = s.createRow(rownum); for (short cellnum = ( short ) 0; cellnum < 50; cellnum += 2) { - c = r.createCell(cellnum); + HSSFCell c = r.createCell(cellnum); c.setCellValue(rownum * 10000 + cellnum + ((( double ) rownum / 1000) + (( double ) cellnum / 10000))); @@ -448,33 +440,27 @@ public class TestWorkbook extends TestCase { c.setCellValue(new HSSFRichTextString("TEST")); } } - s.addMergedRegion(new Region(( short ) 0, ( short ) 0, ( short ) 10, - ( short ) 10)); - s.addMergedRegion(new Region(( short ) 30, ( short ) 5, ( short ) 40, - ( short ) 15)); - wb.write(out); - out.close(); + s.addMergedRegion(new CellRangeAddress(0, 10, 0, 10)); + s.addMergedRegion(new CellRangeAddress(30, 40, 5, 15)); sanityChecker.checkHSSFWorkbook(wb); - in = new FileInputStream(file); - wb = new HSSFWorkbook(new POIFSFileSystem(in)); + wb = HSSFTestDataSamples.writeOutAndReadBack(wb); + s = wb.getSheetAt(0); - Region r1 = s.getMergedRegionAt(0); - Region r2 = s.getMergedRegionAt(1); + CellRangeAddress r1 = s.getMergedRegion(0); + CellRangeAddress r2 = s.getMergedRegion(1); - in.close(); - - // System.out.println(file.length()); - // assertEquals("FILE LENGTH == 87552",file.length(), 87552); - // System.out.println(s.getLastRowNum()); - assertEquals("REGION1 = 0,0,10,10", 0, - new Region(( short ) 0, ( short ) 0, ( short ) 10, - ( short ) 10).compareTo(r1)); - assertEquals("REGION2 == 30,5,40,15", 0, - new Region(( short ) 30, ( short ) 5, ( short ) 40, - ( short ) 15).compareTo(r2)); + confirmRegion(new CellRangeAddress(0, 10, 0, 10), r1); + confirmRegion(new CellRangeAddress(30, 40,5, 15), r2); } - /** + private static void confirmRegion(CellRangeAddress ra, CellRangeAddress rb) { + assertEquals(ra.getFirstRow(), rb.getFirstRow()); + assertEquals(ra.getLastRow(), rb.getLastRow()); + assertEquals(ra.getFirstColumn(), rb.getFirstColumn()); + assertEquals(ra.getLastColumn(), rb.getLastColumn()); + } + + /** * Test the backup field gets set as expected. */