Extensive fixes for data validation (bug 44953)

git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@682225 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Josh Micich 2008-08-03 22:11:26 +00:00
parent 29af743583
commit 8f5b968caf
18 changed files with 2100 additions and 2457 deletions

View File

@ -37,6 +37,7 @@
<!-- Don't forget to update status.xml too! --> <!-- Don't forget to update status.xml too! -->
<release version="3.1.1-alpha1" date="2008-??-??"> <release version="3.1.1-alpha1" date="2008-??-??">
<action dev="POI-DEVELOPERS" type="fix">44953 - Extensive fixes for data validation</action>
<action dev="POI-DEVELOPERS" type="fix">45519 - Fixed to keep datavalidation records together</action> <action dev="POI-DEVELOPERS" type="fix">45519 - Fixed to keep datavalidation records together</action>
<action dev="POI-DEVELOPERS" type="add">Support for creating new HSLF CurrentUserAtoms</action> <action dev="POI-DEVELOPERS" type="add">Support for creating new HSLF CurrentUserAtoms</action>
<action dev="POI-DEVELOPERS" type="add">45466 - Partial support for removing excel comments (won't work for all excel versions yet)</action> <action dev="POI-DEVELOPERS" type="add">45466 - Partial support for removing excel comments (won't work for all excel versions yet)</action>

View File

@ -34,6 +34,7 @@
<!-- Don't forget to update changes.xml too! --> <!-- Don't forget to update changes.xml too! -->
<changes> <changes>
<release version="3.1.1-alpha1" date="2008-??-??"> <release version="3.1.1-alpha1" date="2008-??-??">
<action dev="POI-DEVELOPERS" type="fix">44953 - Extensive fixes for data validation</action>
<action dev="POI-DEVELOPERS" type="fix">45519 - Fixed to keep datavalidation records together</action> <action dev="POI-DEVELOPERS" type="fix">45519 - Fixed to keep datavalidation records together</action>
<action dev="POI-DEVELOPERS" type="add">Support for creating new HSLF CurrentUserAtoms</action> <action dev="POI-DEVELOPERS" type="add">Support for creating new HSLF CurrentUserAtoms</action>
<action dev="POI-DEVELOPERS" type="add">45466 - Partial support for removing excel comments (won't work for all excel versions yet)</action> <action dev="POI-DEVELOPERS" type="add">45466 - Partial support for removing excel comments (won't work for all excel versions yet)</action>

View File

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

View File

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

View File

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

View File

@ -19,9 +19,7 @@ package org.apache.poi.hssf.record.aggregates;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Stack;
import org.apache.poi.hssf.model.FormulaParser;
import org.apache.poi.hssf.model.RecordStream; import org.apache.poi.hssf.model.RecordStream;
import org.apache.poi.hssf.record.CFHeaderRecord; import org.apache.poi.hssf.record.CFHeaderRecord;
import org.apache.poi.hssf.record.CFRuleRecord; import org.apache.poi.hssf.record.CFRuleRecord;
@ -34,10 +32,6 @@ import org.apache.poi.hssf.record.PaneRecord;
import org.apache.poi.hssf.record.Record; import org.apache.poi.hssf.record.Record;
import org.apache.poi.hssf.record.SelectionRecord; import org.apache.poi.hssf.record.SelectionRecord;
import org.apache.poi.hssf.record.WindowTwoRecord; import org.apache.poi.hssf.record.WindowTwoRecord;
import org.apache.poi.hssf.record.formula.Ptg;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.hssf.util.HSSFCellRangeAddress;
import org.apache.poi.hssf.util.HSSFDataValidation;
/** /**
* Manages the DVALRecord and DVRecords for a single sheet<br/> * Manages the DVALRecord and DVRecords for a single sheet<br/>
@ -177,63 +171,7 @@ public final class DataValidityTable extends RecordAggregate {
return false; return false;
} }
public void addDataValidation(HSSFDataValidation dataValidation, HSSFWorkbook workbook) { public void addDataValidation(DVRecord dvRecord) {
DVRecord dvRecord = new DVRecord();
// dv record's option flags
dvRecord.setDataType(dataValidation.getDataValidationType());
dvRecord.setErrorStyle(dataValidation.getErrorStyle());
dvRecord.setEmptyCellAllowed(dataValidation.getEmptyCellAllowed());
dvRecord.setSuppressDropdownArrow(dataValidation.getSuppressDropDownArrow());
dvRecord.setShowPromptOnCellSelected(dataValidation.getShowPromptBox());
dvRecord.setShowErrorOnInvalidValue(dataValidation.getShowErrorBox());
dvRecord.setConditionOperator(dataValidation.getOperator());
// string fields
dvRecord.setStringField(DVRecord.STRING_PROMPT_TITLE, dataValidation.getPromptBoxTitle());
dvRecord.setStringField(DVRecord.STRING_PROMPT_TEXT, dataValidation.getPromptBoxText());
dvRecord.setStringField(DVRecord.STRING_ERROR_TITLE, dataValidation.getErrorBoxTitle());
dvRecord.setStringField(DVRecord.STRING_ERROR_TEXT, dataValidation.getErrorBoxText());
// formula fields ( size and data )
Stack ptg_arr = new Stack();
Ptg[] ptg = FormulaParser.parse(dataValidation.getFirstFormula(), workbook);
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(dataValidation.getExplicitListFormula());
if (dataValidation.getSecondFormula() != null) {
ptg_arr = new Stack();
ptg = FormulaParser.parse(dataValidation.getSecondFormula(), workbook);
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(dataValidation.getFirstRow(), dataValidation.getFirstColumn(),
dataValidation.getLastRow(), dataValidation.getLastColumn());
dvRecord.setCellRangeAddress(cell_range);
_validationList.add(dvRecord); _validationList.add(dvRecord);
_headerRec.setDVRecNo(_validationList.size()); _headerRec.setDVRecNo(_validationList.size());
} }

View File

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

View File

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

View File

@ -0,0 +1,479 @@
/* ====================================================================
Copyright 2002-2004 Apache Software Foundation
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
==================================================================== */
package org.apache.poi.hssf.usermodel;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import org.apache.poi.hssf.model.FormulaParser;
import org.apache.poi.hssf.record.formula.NumberPtg;
import org.apache.poi.hssf.record.formula.Ptg;
import org.apache.poi.hssf.record.formula.StringPtg;
/**
*
* @author Josh Micich
*/
public class DVConstraint {
/**
* ValidationType enum
*/
public static final class ValidationType {
private ValidationType() {
// no instances of this class
}
/** 'Any value' type - value not restricted */
public static final int ANY = 0x00;
/** Integer ('Whole number') type */
public static final int INTEGER = 0x01;
/** Decimal type */
public static final int DECIMAL = 0x02;
/** List type ( combo box type ) */
public static final int LIST = 0x03;
/** Date type */
public static final int DATE = 0x04;
/** Time type */
public static final int TIME = 0x05;
/** String length type */
public static final int TEXT_LENGTH = 0x06;
/** Formula ( 'Custom' ) type */
public static final int FORMULA = 0x07;
}
/**
* Condition operator enum
*/
public static final class OperatorType {
private OperatorType() {
// no instances of this class
}
public static final int BETWEEN = 0x00;
public static final int NOT_BETWEEN = 0x01;
public static final int EQUAL = 0x02;
public static final int NOT_EQUAL = 0x03;
public static final int GREATER_THAN = 0x04;
public static final int LESS_THAN = 0x05;
public static final int GREATER_OR_EQUAL = 0x06;
public static final int LESS_OR_EQUAL = 0x07;
/** default value to supply when the operator type is not used */
public static final int IGNORED = BETWEEN;
/* package */ static void validateSecondArg(int comparisonOperator, String paramValue) {
switch (comparisonOperator) {
case BETWEEN:
case NOT_BETWEEN:
if (paramValue == null) {
throw new IllegalArgumentException("expr2 must be supplied for 'between' comparisons");
}
// all other operators don't need second arg
}
}
}
/* package */ static final class FormulaPair {
private final Ptg[] _formula1;
private final Ptg[] _formula2;
public FormulaPair(Ptg[] formula1, Ptg[] formula2) {
_formula1 = formula1;
_formula2 = formula2;
}
public Ptg[] getFormula1() {
return _formula1;
}
public Ptg[] getFormula2() {
return _formula2;
}
}
// convenient access to ValidationType namespace
private static final ValidationType VT = null;
private final int _validationType;
private int _operator;
private String[] _explicitListValues;
private String _formula1;
private String _formula2;
private Double _value1;
private Double _value2;
private DVConstraint(int validationType, int comparisonOperator, String formulaA,
String formulaB, Double value1, Double value2, String[] excplicitListValues) {
_validationType = validationType;
_operator = comparisonOperator;
_formula1 = formulaA;
_formula2 = formulaB;
_value1 = value1;
_value2 = value2;
_explicitListValues = excplicitListValues;
}
/**
* Creates a list constraint
*/
private DVConstraint(String listFormula, String[] excplicitListValues) {
this(ValidationType.LIST, OperatorType.IGNORED,
listFormula, null, null, null, excplicitListValues);
}
/**
* Creates a number based data validation constraint. The text values entered for expr1 and expr2
* can be either standard Excel formulas or formatted number values. If the expression starts
* with '=' it is parsed as a formula, otherwise it is parsed as a formatted number.
*
* @param validationType one of {@link ValidationType#ANY}, {@link ValidationType#DECIMAL},
* {@link ValidationType#INTEGER}, {@link ValidationType#TEXT_LENGTH}
* @param comparisonOperator any constant from {@link OperatorType} enum
* @param expr1 date formula (when first char is '=') or formatted number value
* @param expr2 date formula (when first char is '=') or formatted number value
*/
public static DVConstraint createNumericConstraint(int validationType, int comparisonOperator,
String expr1, String expr2) {
switch (validationType) {
case ValidationType.ANY:
if (expr1 != null || expr2 != null) {
throw new IllegalArgumentException("expr1 and expr2 must be null for validation type 'any'");
}
break;
case ValidationType.DECIMAL:
case ValidationType.INTEGER:
case ValidationType.TEXT_LENGTH:
if (expr1 == null) {
throw new IllegalArgumentException("expr1 must be supplied");
}
OperatorType.validateSecondArg(comparisonOperator, expr2);
break;
default:
throw new IllegalArgumentException("Validation Type ("
+ validationType + ") not supported with this method");
}
// formula1 and value1 are mutually exclusive
String formula1 = getFormulaFromTextExpression(expr1);
Double value1 = formula1 == null ? convertNumber(expr1) : null;
// formula2 and value2 are mutually exclusive
String formula2 = getFormulaFromTextExpression(expr2);
Double value2 = formula2 == null ? convertNumber(expr2) : null;
return new DVConstraint(validationType, comparisonOperator, formula1, formula2, value1, value2, null);
}
public static DVConstraint createFormulaListConstraint(String listFormula) {
return new DVConstraint(listFormula, null);
}
public static DVConstraint createExplicitListConstraint(String[] explicitListValues) {
return new DVConstraint(null, explicitListValues);
}
/**
* Creates a time based data validation constraint. The text values entered for expr1 and expr2
* can be either standard Excel formulas or formatted time values. If the expression starts
* with '=' it is parsed as a formula, otherwise it is parsed as a formatted time. To parse
* formatted times, two formats are supported: "HH:MM" or "HH:MM:SS". This is contrary to
* Excel which uses the default time format from the OS.
*
* @param comparisonOperator constant from {@link OperatorType} enum
* @param expr1 date formula (when first char is '=') or formatted time value
* @param expr2 date formula (when first char is '=') or formatted time value
*/
public static DVConstraint createTimeConstraint(int comparisonOperator, String expr1, String expr2) {
if (expr1 == null) {
throw new IllegalArgumentException("expr1 must be supplied");
}
OperatorType.validateSecondArg(comparisonOperator, expr1);
// formula1 and value1 are mutually exclusive
String formula1 = getFormulaFromTextExpression(expr1);
Double value1 = formula1 == null ? convertTime(expr1) : null;
// formula2 and value2 are mutually exclusive
String formula2 = getFormulaFromTextExpression(expr2);
Double value2 = formula2 == null ? convertTime(expr2) : null;
return new DVConstraint(VT.TIME, comparisonOperator, formula1, formula2, value1, value2, null);
}
/**
* Creates a date based data validation constraint. The text values entered for expr1 and expr2
* can be either standard Excel formulas or formatted date values. If the expression starts
* with '=' it is parsed as a formula, otherwise it is parsed as a formatted date (Excel uses
* the same convention). To parse formatted dates, a date format needs to be specified. This
* is contrary to Excel which uses the default short date format from the OS.
*
* @param comparisonOperator constant from {@link OperatorType} enum
* @param expr1 date formula (when first char is '=') or formatted date value
* @param expr2 date formula (when first char is '=') or formatted date value
* @param dateFormat ignored if both expr1 and expr2 are formulas. Default value is "YYYY/MM/DD"
* otherwise any other valid argument for <tt>SimpleDateFormat</tt> can be used
* @see <a href='http://java.sun.com/j2se/1.5.0/docs/api/java/text/DateFormat.html'>SimpleDateFormat</a>
*/
public static DVConstraint createDateConstraint(int comparisonOperator, String expr1, String expr2, String dateFormat) {
if (expr1 == null) {
throw new IllegalArgumentException("expr1 must be supplied");
}
OperatorType.validateSecondArg(comparisonOperator, expr2);
SimpleDateFormat df = dateFormat == null ? null : new SimpleDateFormat(dateFormat);
// formula1 and value1 are mutually exclusive
String formula1 = getFormulaFromTextExpression(expr1);
Double value1 = formula1 == null ? convertDate(expr1, df) : null;
// formula2 and value2 are mutually exclusive
String formula2 = getFormulaFromTextExpression(expr2);
Double value2 = formula2 == null ? convertDate(expr2, df) : null;
return new DVConstraint(VT.DATE, comparisonOperator, formula1, formula2, value1, value2, null);
}
/**
* Distinguishes formula expressions from simple value expressions. This logic is only
* required by a few factory methods in this class that create data validation constraints
* from more or less the same parameters that would have been entered in the Excel UI. The
* data validation dialog box uses the convention that formulas begin with '='. Other methods
* in this class follow the POI convention (formulas and values are distinct), so the '='
* convention is not used there.
*
* @param textExpr a formula or value expression
* @return all text after '=' if textExpr begins with '='. Otherwise <code>null</code> if textExpr does not begin with '='
*/
private static String getFormulaFromTextExpression(String textExpr) {
if (textExpr == null) {
return null;
}
if (textExpr.length() < 1) {
throw new IllegalArgumentException("Empty string is not a valid formula/value expression");
}
if (textExpr.charAt(0) == '=') {
return textExpr.substring(1);
}
return null;
}
/**
* @return <code>null</code> if numberStr is <code>null</code>
*/
private static Double convertNumber(String numberStr) {
if (numberStr == null) {
return null;
}
try {
return new Double(numberStr);
} catch (NumberFormatException e) {
throw new RuntimeException("The supplied text '" + numberStr
+ "' could not be parsed as a number");
}
}
/**
* @return <code>null</code> if timeStr is <code>null</code>
*/
private static Double convertTime(String timeStr) {
if (timeStr == null) {
return null;
}
return new Double(HSSFDateUtil.convertTime(timeStr));
}
/**
* @param dateFormat pass <code>null</code> for default YYYYMMDD
* @return <code>null</code> if timeStr is <code>null</code>
*/
private static Double convertDate(String dateStr, SimpleDateFormat dateFormat) {
if (dateStr == null) {
return null;
}
Date dateVal;
if (dateFormat == null) {
dateVal = HSSFDateUtil.parseYYYYMMDDDate(dateStr);
} else {
try {
dateVal = dateFormat.parse(dateStr);
} catch (ParseException e) {
throw new RuntimeException("Failed to parse date '" + dateStr
+ "' using specified format '" + dateFormat + "'", e);
}
}
return new Double(HSSFDateUtil.getExcelDate(dateVal));
}
public static DVConstraint createFormulaConstraint(String formula) {
if (formula == null) {
throw new IllegalArgumentException("formula must be supplied");
}
return new DVConstraint(VT.FORMULA, OperatorType.IGNORED, formula, null, null, null, null);
}
/**
* @return both parsed formulas (for expression 1 and 2).
*/
/* package */ FormulaPair createFormulas(HSSFWorkbook workbook) {
Ptg[] formula1;
Ptg[] formula2;
if (isListValidationType()) {
formula1 = createListFormula(workbook);
formula2 = Ptg.EMPTY_PTG_ARRAY;
} else {
formula1 = convertDoubleFormula(_formula1, _value1, workbook);
formula2 = convertDoubleFormula(_formula2, _value2, workbook);
}
return new FormulaPair(formula1, formula2);
}
private Ptg[] createListFormula(HSSFWorkbook workbook) {
if (_explicitListValues == null) {
// formula is parsed with slightly different RVA rules: (root node type must be 'reference')
return FormulaParser.parse(_formula1, workbook, FormulaParser.FORMULA_TYPE_DATAVALIDATION_LIST);
// To do: Excel places restrictions on the available operations within a list formula.
// Some things like union and intersection are not allowed.
}
// explicit list was provided
StringBuffer sb = new StringBuffer(_explicitListValues.length * 16);
for (int i = 0; i < _explicitListValues.length; i++) {
if (i > 0) {
sb.append('\0'); // list delimiter is the nul char
}
sb.append(_explicitListValues[i]);
}
return new Ptg[] { new StringPtg(sb.toString()), };
}
/**
* @return The parsed token array representing the formula or value specified.
* Empty array if both formula and value are <code>null</code>
*/
private static Ptg[] convertDoubleFormula(String formula, Double value, HSSFWorkbook workbook) {
if (formula == null) {
if (value == null) {
return Ptg.EMPTY_PTG_ARRAY;
}
return new Ptg[] { new NumberPtg(value.doubleValue()), };
}
if (value != null) {
throw new IllegalStateException("Both formula and value cannot be present");
}
return FormulaParser.parse(formula, workbook);
}
/**
* @return data validation type of this constraint
* @see ValidationType
*/
public int getValidationType() {
return _validationType;
}
/**
* Convenience method
* @return <code>true</code> if this constraint is a 'list' validation
*/
public boolean isListValidationType() {
return _validationType == VT.LIST;
}
/**
* Convenience method
* @return <code>true</code> if this constraint is a 'list' validation with explicit values
*/
public boolean isExplicitList() {
return _validationType == VT.LIST && _explicitListValues != null;
}
/**
* @return the operator used for this constraint
* @see OperatorType
*/
public int getOperator() {
return _operator;
}
/**
* Sets the comparison operator for this constraint
* @see OperatorType
*/
public void setOperator(int operator) {
_operator = operator;
}
public String[] getExplicitListValues() {
return _explicitListValues;
}
public void setExplicitListValues(String[] explicitListValues) {
if (_validationType != VT.LIST) {
throw new RuntimeException("Cannot setExplicitListValues on non-list constraint");
}
_formula1 = null;
_explicitListValues = explicitListValues;
}
/**
* @return the formula for expression 1. May be <code>null</code>
*/
public String getFormula1() {
return _formula1;
}
/**
* Sets a formula for expression 1.
*/
public void setFormula1(String formula1) {
_value1 = null;
_explicitListValues = null;
_formula1 = formula1;
}
/**
* @return the formula for expression 2. May be <code>null</code>
*/
public String getFormula2() {
return _formula2;
}
/**
* Sets a formula for expression 2.
*/
public void setFormula2(String formula2) {
_value2 = null;
_formula2 = formula2;
}
/**
* @return the numeric value for expression 1. May be <code>null</code>
*/
public Double getValue1() {
return _value1;
}
/**
* Sets a numeric value for expression 1.
*/
public void setValue1(double value1) {
_formula1 = null;
_value1 = new Double(value1);
}
/**
* @return the numeric value for expression 2. May be <code>null</code>
*/
public Double getValue2() {
return _value2;
}
/**
* Sets a numeric value for expression 2.
*/
public void setValue2(double value2) {
_formula2 = null;
_value2 = new Double(value2);
}
}

View File

@ -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.hssf.util.CellRangeAddressList;
/**
*Utility class for creating data validation cells
*
* @author Dragos Buleandra (dragos.buleandra@trade2b.ro)
*/
public final class HSSFDataValidation {
/**
* Error style constants for error box
*/
public static final class ErrorStyle {
/** STOP style */
public static final int STOP = 0x00;
/** WARNING style */
public static final int WARNING = 0x01;
/** INFO style */
public static final int INFO = 0x02;
}
private String _prompt_title;
private String _prompt_text;
private String _error_title;
private String _error_text;
private int _errorStyle = ErrorStyle.STOP;
private boolean _emptyCellAllowed = true;
private boolean _suppress_dropdown_arrow = false;
private boolean _showPromptBox = true;
private boolean _showErrorBox = true;
private final CellRangeAddressList _regions;
private DVConstraint _constraint;
/**
* Constructor which initializes the cell range on which this object will be
* applied
* @param constraint
*/
public HSSFDataValidation(CellRangeAddressList regions, DVConstraint constraint) {
_regions = regions;
_constraint = constraint;
}
public DVConstraint getConstraint() {
return _constraint;
}
/**
* Sets the error style for error box
* @see ErrorStyle
*/
public void setErrorStyle(int error_style) {
_errorStyle = error_style;
}
/**
* @return the error style of error box
* @see ErrorStyle
*/
public int getErrorStyle() {
return _errorStyle;
}
/**
* Sets if this object allows empty as a valid value
*
* @param allowed <code>true</code> if this object should treats empty as valid value , <code>false</code>
* otherwise
*/
public void setEmptyCellAllowed(boolean allowed) {
_emptyCellAllowed = allowed;
}
/**
* Retrieve the settings for empty cells allowed
*
* @return True if this object should treats empty as valid value , false
* otherwise
*/
public boolean getEmptyCellAllowed() {
return _emptyCellAllowed;
}
/**
* Useful for list validation objects .
*
* @param suppress
* True if a list should display the values into a drop down list ,
* false otherwise . In other words , if a list should display
* the arrow sign on its right side
*/
public void setSuppressDropDownArrow(boolean suppress) {
_suppress_dropdown_arrow = suppress;
}
/**
* Useful only list validation objects . This method always returns false if
* the object isn't a list validation object
*
* @return <code>true</code> if a list should display the values into a drop down list ,
* <code>false</code> otherwise .
*/
public boolean getSuppressDropDownArrow() {
if (_constraint.isListValidationType()) {
return _suppress_dropdown_arrow;
}
return false;
}
/**
* Sets the behaviour when a cell which belongs to this object is selected
*
* @param show <code>true</code> if an prompt box should be displayed , <code>false</code> otherwise
*/
public void setShowPromptBox(boolean show) {
_showPromptBox = show;
}
/**
* @param show <code>true</code> if an prompt box should be displayed , <code>false</code> otherwise
*/
public boolean getShowPromptBox() {
return _showPromptBox;
}
/**
* Sets the behaviour when an invalid value is entered
*
* @param show <code>true</code> if an error box should be displayed , <code>false</code> otherwise
*/
public void setShowErrorBox(boolean show) {
_showErrorBox = show;
}
/**
* @return <code>true</code> if an error box should be displayed , <code>false</code> otherwise
*/
public boolean getShowErrorBox() {
return _showErrorBox;
}
/**
* Sets the title and text for the prompt box . Prompt box is displayed when
* the user selects a cell which belongs to this validation object . In
* order for a prompt box to be displayed you should also use method
* setShowPromptBox( boolean show )
*
* @param title The prompt box's title
* @param text The prompt box's text
*/
public void createPromptBox(String title, String text) {
_prompt_title = title;
_prompt_text = text;
this.setShowPromptBox(true);
}
/**
* @return Prompt box's title or <code>null</code>
*/
public String getPromptBoxTitle() {
return _prompt_title;
}
/**
* @return Prompt box's text or <code>null</code>
*/
public String getPromptBoxText() {
return _prompt_text;
}
/**
* Sets the title and text for the error box . Error box is displayed when
* the user enters an invalid value int o a cell which belongs to this
* validation object . In order for an error box to be displayed you should
* also use method setShowErrorBox( boolean show )
*
* @param title The error box's title
* @param text The error box's text
*/
public void createErrorBox(String title, String text) {
_error_title = title;
_error_text = text;
this.setShowErrorBox(true);
}
/**
* @return Error box's title or <code>null</code>
*/
public String getErrorBoxTitle() {
return _error_title;
}
/**
* @return Error box's text or <code>null</code>
*/
public String getErrorBoxText() {
return _error_text;
}
public DVRecord createDVRecord(HSSFWorkbook workbook) {
FormulaPair fp = _constraint.createFormulas(workbook);
return new DVRecord(_constraint.getValidationType(),
_constraint.getOperator(),
_errorStyle, _emptyCellAllowed, getSuppressDropDownArrow(),
_constraint.isExplicitList(),
_showPromptBox, _prompt_title, _prompt_text,
_showErrorBox, _error_title, _error_text,
fp.getFormula1(), fp.getFormula2(),
_regions);
}
}

View File

@ -16,17 +16,12 @@
==================================================================== */ ==================================================================== */
/*
* DateUtil.java
*
* Created on January 19, 2002, 9:30 AM
*/
package org.apache.poi.hssf.usermodel; package org.apache.poi.hssf.usermodel;
import java.util.Calendar; import java.util.Calendar;
import java.util.Date; import java.util.Date;
import java.util.GregorianCalendar; import java.util.GregorianCalendar;
import java.util.regex.Pattern;
/** /**
* Contains methods for dealing with Excel dates. * Contains methods for dealing with Excel dates.
@ -38,17 +33,20 @@ import java.util.GregorianCalendar;
* @author Alex Jacoby (ajacoby at gmail.com) * @author Alex Jacoby (ajacoby at gmail.com)
* @author Pavel Krupets (pkrupets at palmtreebusiness dot com) * @author Pavel Krupets (pkrupets at palmtreebusiness dot com)
*/ */
public final class HSSFDateUtil {
public class HSSFDateUtil private HSSFDateUtil() {
{ // no instances of this class
private HSSFDateUtil()
{
} }
private static final int SECONDS_PER_MINUTE = 60;
private static final int MINUTES_PER_HOUR = 60;
private static final int HOURS_PER_DAY = 24;
private static final int SECONDS_PER_DAY = (HOURS_PER_DAY * MINUTES_PER_HOUR * SECONDS_PER_MINUTE);
private static final int BAD_DATE = -1; // used to specify that date is invalid
private static final long DAY_MILLISECONDS = SECONDS_PER_DAY * 1000L;
private static final Pattern TIME_SEPARATOR_PATTERN = Pattern.compile(":");
private static final int BAD_DATE =
-1; // used to specify that date is invalid
private static final long DAY_MILLISECONDS = 24 * 60 * 60 * 1000;
/** /**
* Given a Date, converts it into a double representing its internal Excel representation, * Given a Date, converts it into a double representing its internal Excel representation,
* which is the number of days since 1/1/1900. Fractional days represent hours, minutes, and seconds. * which is the number of days since 1/1/1900. Fractional days represent hours, minutes, and seconds.
@ -57,7 +55,7 @@ public class HSSFDateUtil
* @param date the Date * @param date the Date
*/ */
public static double getExcelDate(Date date) { public static double getExcelDate(Date date) {
return getExcelDate(date, false); return getExcelDate(date, false);
} }
/** /**
* Given a Date, converts it into a double representing its internal Excel representation, * Given a Date, converts it into a double representing its internal Excel representation,
@ -74,8 +72,8 @@ public class HSSFDateUtil
} }
/** /**
* Given a Date in the form of a Calendar, converts it into a double * Given a Date in the form of a Calendar, converts it into a double
* representing its internal Excel representation, which is the * representing its internal Excel representation, which is the
* number of days since 1/1/1900. Fractional days represent hours, * number of days since 1/1/1900. Fractional days represent hours,
* minutes, and seconds. * minutes, and seconds.
* *
* @return Excel representation of Date (-1 if error - test for error by checking for less than 0.1) * @return Excel representation of Date (-1 if error - test for error by checking for less than 0.1)
@ -83,41 +81,40 @@ public class HSSFDateUtil
* @param use1904windowing Should 1900 or 1904 date windowing be used? * @param use1904windowing Should 1900 or 1904 date windowing be used?
*/ */
public static double getExcelDate(Calendar date, boolean use1904windowing) { public static double getExcelDate(Calendar date, boolean use1904windowing) {
// Don't alter the supplied Calendar as we do our work // Don't alter the supplied Calendar as we do our work
return internalGetExcelDate( (Calendar)date.clone(), use1904windowing ); return internalGetExcelDate( (Calendar)date.clone(), use1904windowing );
} }
private static double internalGetExcelDate(Calendar date, boolean use1904windowing) { private static double internalGetExcelDate(Calendar date, boolean use1904windowing) {
if ((!use1904windowing && date.get(Calendar.YEAR) < 1900) || if ((!use1904windowing && date.get(Calendar.YEAR) < 1900) ||
(use1904windowing && date.get(Calendar.YEAR) < 1904)) (use1904windowing && date.get(Calendar.YEAR) < 1904))
{ {
return BAD_DATE; return BAD_DATE;
} else {
// Because of daylight time saving we cannot use
// date.getTime() - calStart.getTimeInMillis()
// as the difference in milliseconds between 00:00 and 04:00
// can be 3, 4 or 5 hours but Excel expects it to always
// be 4 hours.
// E.g. 2004-03-28 04:00 CEST - 2004-03-28 00:00 CET is 3 hours
// and 2004-10-31 04:00 CET - 2004-10-31 00:00 CEST is 5 hours
double fraction = (((date.get(Calendar.HOUR_OF_DAY) * 60
+ date.get(Calendar.MINUTE)
) * 60 + date.get(Calendar.SECOND)
) * 1000 + date.get(Calendar.MILLISECOND)
) / ( double ) DAY_MILLISECONDS;
Calendar calStart = dayStart(date);
double value = fraction + absoluteDay(calStart, use1904windowing);
if (!use1904windowing && value >= 60) {
value++;
} else if (use1904windowing) {
value--;
}
return value;
} }
// Because of daylight time saving we cannot use
// date.getTime() - calStart.getTimeInMillis()
// as the difference in milliseconds between 00:00 and 04:00
// can be 3, 4 or 5 hours but Excel expects it to always
// be 4 hours.
// E.g. 2004-03-28 04:00 CEST - 2004-03-28 00:00 CET is 3 hours
// and 2004-10-31 04:00 CET - 2004-10-31 00:00 CEST is 5 hours
double fraction = (((date.get(Calendar.HOUR_OF_DAY) * 60
+ date.get(Calendar.MINUTE)
) * 60 + date.get(Calendar.SECOND)
) * 1000 + date.get(Calendar.MILLISECOND)
) / ( double ) DAY_MILLISECONDS;
Calendar calStart = dayStart(date);
double value = fraction + absoluteDay(calStart, use1904windowing);
if (!use1904windowing && value >= 60) {
value++;
} else if (use1904windowing) {
value--;
}
return value;
} }
/** /**
* Given an Excel date with using 1900 date windowing, and * Given an Excel date with using 1900 date windowing, and
* converts it to a java.util.Date. * converts it to a java.util.Date.
@ -130,13 +127,13 @@ public class HSSFDateUtil
* <code>Europe/Copenhagen</code>, on 2004-03-28 the minute after * <code>Europe/Copenhagen</code>, on 2004-03-28 the minute after
* 01:59 CET is 03:00 CEST, if the excel date represents a time between * 01:59 CET is 03:00 CEST, if the excel date represents a time between
* 02:00 and 03:00 then it is converted to past 03:00 summer time * 02:00 and 03:00 then it is converted to past 03:00 summer time
* *
* @param date The Excel date. * @param date The Excel date.
* @return Java representation of the date, or null if date is not a valid Excel date * @return Java representation of the date, or null if date is not a valid Excel date
* @see java.util.TimeZone * @see java.util.TimeZone
*/ */
public static Date getJavaDate(double date) { public static Date getJavaDate(double date) {
return getJavaDate(date, false); return getJavaDate(date, false);
} }
/** /**
* Given an Excel date with either 1900 or 1904 date windowing, * Given an Excel date with either 1900 or 1904 date windowing,
@ -158,94 +155,90 @@ public class HSSFDateUtil
* @see java.util.TimeZone * @see java.util.TimeZone
*/ */
public static Date getJavaDate(double date, boolean use1904windowing) { public static Date getJavaDate(double date, boolean use1904windowing) {
if (isValidExcelDate(date)) { if (!isValidExcelDate(date)) {
int startYear = 1900;
int dayAdjust = -1; // Excel thinks 2/29/1900 is a valid date, which it isn't
int wholeDays = (int)Math.floor(date);
if (use1904windowing) {
startYear = 1904;
dayAdjust = 1; // 1904 date windowing uses 1/2/1904 as the first day
}
else if (wholeDays < 61) {
// Date is prior to 3/1/1900, so adjust because Excel thinks 2/29/1900 exists
// If Excel date == 2/29/1900, will become 3/1/1900 in Java representation
dayAdjust = 0;
}
GregorianCalendar calendar = new GregorianCalendar(startYear,0,
wholeDays + dayAdjust);
int millisecondsInDay = (int)((date - Math.floor(date)) *
DAY_MILLISECONDS + 0.5);
calendar.set(GregorianCalendar.MILLISECOND, millisecondsInDay);
return calendar.getTime();
}
else {
return null; return null;
} }
int startYear = 1900;
int dayAdjust = -1; // Excel thinks 2/29/1900 is a valid date, which it isn't
int wholeDays = (int)Math.floor(date);
if (use1904windowing) {
startYear = 1904;
dayAdjust = 1; // 1904 date windowing uses 1/2/1904 as the first day
}
else if (wholeDays < 61) {
// Date is prior to 3/1/1900, so adjust because Excel thinks 2/29/1900 exists
// If Excel date == 2/29/1900, will become 3/1/1900 in Java representation
dayAdjust = 0;
}
GregorianCalendar calendar = new GregorianCalendar(startYear,0,
wholeDays + dayAdjust);
int millisecondsInDay = (int)((date - Math.floor(date)) *
DAY_MILLISECONDS + 0.5);
calendar.set(GregorianCalendar.MILLISECOND, millisecondsInDay);
return calendar.getTime();
} }
/** /**
* Given a format ID and its format String, will check to see if the * Given a format ID and its format String, will check to see if the
* format represents a date format or not. * format represents a date format or not.
* Firstly, it will check to see if the format ID corresponds to an * Firstly, it will check to see if the format ID corresponds to an
* internal excel date format (eg most US date formats) * internal excel date format (eg most US date formats)
* If not, it will check to see if the format string only contains * If not, it will check to see if the format string only contains
* date formatting characters (ymd-/), which covers most * date formatting characters (ymd-/), which covers most
* non US date formats. * non US date formats.
* *
* @param formatIndex The index of the format, eg from ExtendedFormatRecord.getFormatIndex * @param formatIndex The index of the format, eg from ExtendedFormatRecord.getFormatIndex
* @param formatString The format string, eg from FormatRecord.getFormatString * @param formatString The format string, eg from FormatRecord.getFormatString
* @see #isInternalDateFormat(int) * @see #isInternalDateFormat(int)
*/ */
public static boolean isADateFormat(int formatIndex, String formatString) { public static boolean isADateFormat(int formatIndex, String formatString) {
// First up, is this an internal date format? // First up, is this an internal date format?
if(isInternalDateFormat(formatIndex)) { if(isInternalDateFormat(formatIndex)) {
return true; return true;
} }
// If we didn't get a real string, it can't be // If we didn't get a real string, it can't be
if(formatString == null || formatString.length() == 0) { if(formatString == null || formatString.length() == 0) {
return false; return false;
} }
String fs = formatString; String fs = formatString;
// Translate \- into just -, before matching // Translate \- into just -, before matching
fs = fs.replaceAll("\\\\-","-"); fs = fs.replaceAll("\\\\-","-");
// And \, into , // And \, into ,
fs = fs.replaceAll("\\\\,",","); fs = fs.replaceAll("\\\\,",",");
// And '\ ' into ' ' // And '\ ' into ' '
fs = fs.replaceAll("\\\\ "," "); fs = fs.replaceAll("\\\\ "," ");
// If it end in ;@, that's some crazy dd/mm vs mm/dd // If it end in ;@, that's some crazy dd/mm vs mm/dd
// switching stuff, which we can ignore // switching stuff, which we can ignore
fs = fs.replaceAll(";@", ""); fs = fs.replaceAll(";@", "");
// If it starts with [$-...], then could be a date, but // If it starts with [$-...], then could be a date, but
// who knows what that starting bit is all about // who knows what that starting bit is all about
fs = fs.replaceAll("^\\[\\$\\-.*?\\]", ""); fs = fs.replaceAll("^\\[\\$\\-.*?\\]", "");
// If it starts with something like [Black] or [Yellow], // If it starts with something like [Black] or [Yellow],
// then it could be a date // then it could be a date
fs = fs.replaceAll("^\\[[a-zA-Z]+\\]", ""); fs = fs.replaceAll("^\\[[a-zA-Z]+\\]", "");
// Otherwise, check it's only made up, in any case, of: // Otherwise, check it's only made up, in any case, of:
// y m d h s - / , . : // y m d h s - / , . :
// optionally followed by AM/PM // optionally followed by AM/PM
if(fs.matches("^[yYmMdDhHsS\\-/,. :]+[ampAMP/]*$")) { if(fs.matches("^[yYmMdDhHsS\\-/,. :]+[ampAMP/]*$")) {
return true; return true;
} }
return false; return false;
} }
/** /**
* Given a format ID this will check whether the format represents * Given a format ID this will check whether the format represents
* an internal excel date format or not. * an internal excel date format or not.
* @see #isADateFormat(int, java.lang.String) * @see #isADateFormat(int, java.lang.String)
*/ */
public static boolean isInternalDateFormat(int format) { public static boolean isInternalDateFormat(int format) {
boolean retval =false;
switch(format) { switch(format) {
// Internal Date Formats as described on page 427 in // Internal Date Formats as described on page 427 in
// Microsoft Excel Dev's Kit... // Microsoft Excel Dev's Kit...
@ -261,27 +254,22 @@ public class HSSFDateUtil
case 0x2d: case 0x2d:
case 0x2e: case 0x2e:
case 0x2f: case 0x2f:
retval = true; return true;
break;
default:
retval = false;
break;
} }
return retval; return false;
} }
/** /**
* Check if a cell contains a date * Check if a cell contains a date
* Since dates are stored internally in Excel as double values * Since dates are stored internally in Excel as double values
* we infer it is a date if it is formatted as such. * we infer it is a date if it is formatted as such.
* @see #isADateFormat(int, String) * @see #isADateFormat(int, String)
* @see #isInternalDateFormat(int) * @see #isInternalDateFormat(int)
*/ */
public static boolean isCellDateFormatted(HSSFCell cell) { public static boolean isCellDateFormatted(HSSFCell cell) {
if (cell == null) return false; if (cell == null) return false;
boolean bDate = false; boolean bDate = false;
double d = cell.getNumericCellValue(); double d = cell.getNumericCellValue();
if ( HSSFDateUtil.isValidExcelDate(d) ) { if ( HSSFDateUtil.isValidExcelDate(d) ) {
HSSFCellStyle style = cell.getCellStyle(); HSSFCellStyle style = cell.getCellStyle();
@ -302,7 +290,7 @@ public class HSSFDateUtil
public static boolean isCellInternalDateFormatted(HSSFCell cell) { public static boolean isCellInternalDateFormatted(HSSFCell cell) {
if (cell == null) return false; if (cell == null) return false;
boolean bDate = false; boolean bDate = false;
double d = cell.getNumericCellValue(); double d = cell.getNumericCellValue();
if ( HSSFDateUtil.isValidExcelDate(d) ) { if ( HSSFDateUtil.isValidExcelDate(d) ) {
HSSFCellStyle style = cell.getCellStyle(); HSSFCellStyle style = cell.getCellStyle();
@ -344,7 +332,7 @@ public class HSSFDateUtil
* *
* @return days number of days in years prior to yr. * @return days number of days in years prior to yr.
* @param yr a year (1900 < yr < 4000) * @param yr a year (1900 < yr < 4000)
* @param use1904windowing * @param use1904windowing
* @exception IllegalArgumentException if year is outside of range. * @exception IllegalArgumentException if year is outside of range.
*/ */
@ -353,16 +341,16 @@ public class HSSFDateUtil
if ((!use1904windowing && yr < 1900) || (use1904windowing && yr < 1900)) { if ((!use1904windowing && yr < 1900) || (use1904windowing && yr < 1900)) {
throw new IllegalArgumentException("'year' must be 1900 or greater"); throw new IllegalArgumentException("'year' must be 1900 or greater");
} }
int yr1 = yr - 1; int yr1 = yr - 1;
int leapDays = yr1 / 4 // plus julian leap days in prior years int leapDays = yr1 / 4 // plus julian leap days in prior years
- yr1 / 100 // minus prior century years - yr1 / 100 // minus prior century years
+ yr1 / 400 // plus years divisible by 400 + yr1 / 400 // plus years divisible by 400
- 460; // leap days in previous 1900 years - 460; // leap days in previous 1900 years
return 365 * (yr - (use1904windowing ? 1904 : 1900)) + leapDays; return 365 * (yr - (use1904windowing ? 1904 : 1900)) + leapDays;
} }
// set HH:MM:SS fields of cal to 00:00:00:000 // set HH:MM:SS fields of cal to 00:00:00:000
private static Calendar dayStart(final Calendar cal) private static Calendar dayStart(final Calendar cal)
{ {
@ -377,5 +365,95 @@ public class HSSFDateUtil
return cal; return cal;
} }
// ---------------------------------------------------------------------------------------------------------
private static final class FormatException extends Exception {
public FormatException(String msg) {
super(msg);
}
}
/**
* Converts a string of format "HH:MM" or "HH:MM:SS" to its (Excel) numeric equivalent
*
* @return a double between 0 and 1 representing the fraction of the day
*/
public static double convertTime(String timeStr) {
try {
return convertTimeInternal(timeStr);
} catch (FormatException e) {
String msg = "Bad time format '" + timeStr
+ "' expected 'HH:MM' or 'HH:MM:SS' - " + e.getMessage();
throw new IllegalArgumentException(msg);
}
}
private static double convertTimeInternal(String timeStr) throws FormatException {
int len = timeStr.length();
if (len < 4 || len > 8) {
throw new FormatException("Bad length");
}
String[] parts = TIME_SEPARATOR_PATTERN.split(timeStr);
String secStr;
switch (parts.length) {
case 2: secStr = "00"; break;
case 3: secStr = parts[2]; break;
default:
throw new FormatException("Expected 2 or 3 fields but got (" + parts.length + ")");
}
String hourStr = parts[0];
String minStr = parts[1];
int hours = parseInt(hourStr, "hour", HOURS_PER_DAY);
int minutes = parseInt(minStr, "minute", MINUTES_PER_HOUR);
int seconds = parseInt(secStr, "second", SECONDS_PER_MINUTE);
double totalSeconds = seconds + (minutes + (hours) * 60) * 60;
return totalSeconds / (SECONDS_PER_DAY);
}
/**
* Converts a string of format "YYYY/MM/DD" to its (Excel) numeric equivalent
*
* @return a double representing the (integer) number of days since the start of the Excel epoch
*/
public static Date parseYYYYMMDDDate(String dateStr) {
try {
return parseYYYYMMDDDateInternal(dateStr);
} catch (FormatException e) {
String msg = "Bad time format " + dateStr
+ " expected 'YYYY/MM/DD' - " + e.getMessage();
throw new IllegalArgumentException(msg);
}
}
private static Date parseYYYYMMDDDateInternal(String timeStr) throws FormatException {
if(timeStr.length() != 10) {
throw new FormatException("Bad length");
}
String yearStr = timeStr.substring(0, 4);
String monthStr = timeStr.substring(5, 7);
String dayStr = timeStr.substring(8, 10);
int year = parseInt(yearStr, "year", Short.MIN_VALUE, Short.MAX_VALUE);
int month = parseInt(monthStr, "month", 1, 12);
int day = parseInt(dayStr, "day", 1, 31);
Calendar cal = new GregorianCalendar(year, month-1, day, 0, 0, 0);
cal.set(Calendar.MILLISECOND, 0);
return cal.getTime();
}
private static int parseInt(String strVal, String fieldName, int rangeMax) throws FormatException {
return parseInt(strVal, fieldName, 0, rangeMax-1);
}
private static int parseInt(String strVal, String fieldName, int lowerLimit, int upperLimit) throws FormatException {
int result;
try {
result = Integer.parseInt(strVal);
} catch (NumberFormatException e) {
throw new FormatException("Bad int format '" + strVal + "' for " + fieldName + " field");
}
if (result < lowerLimit || result > upperLimit) {
throw new FormatException(fieldName + " value (" + result
+ ") is outside the allowable range(0.." + upperLimit + ")");
}
return result;
}
} }

View File

@ -38,7 +38,6 @@ import org.apache.poi.hssf.record.*;
import org.apache.poi.hssf.record.aggregates.DataValidityTable; import org.apache.poi.hssf.record.aggregates.DataValidityTable;
import org.apache.poi.hssf.record.formula.Ptg; import org.apache.poi.hssf.record.formula.Ptg;
import org.apache.poi.hssf.record.formula.RefPtg; import org.apache.poi.hssf.record.formula.RefPtg;
import org.apache.poi.hssf.util.HSSFDataValidation;
import org.apache.poi.hssf.util.PaneInformation; import org.apache.poi.hssf.util.PaneInformation;
import org.apache.poi.hssf.util.Region; import org.apache.poi.hssf.util.Region;
import org.apache.poi.util.POILogFactory; import org.apache.poi.util.POILogFactory;
@ -382,7 +381,8 @@ public final class HSSFSheet {
} }
DataValidityTable dvt = sheet.getOrCreateDataValidityTable(); DataValidityTable dvt = sheet.getOrCreateDataValidityTable();
dvt.addDataValidation(dataValidation, workbook); DVRecord dvRecord = dataValidation.createDVRecord(workbook);
dvt.addDataValidation(dvRecord);
} }

View File

@ -0,0 +1,130 @@
/* ====================================================================
Copyright 2002-2004 Apache Software Foundation
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
==================================================================== */
package org.apache.poi.hssf.util;
import org.apache.poi.hssf.record.RecordInputStream;
import org.apache.poi.util.LittleEndian;
/**
* See OOO documentation: excelfileformat.pdf sec 2.5.14 - 'Cell Range Address'
*
* @author Dragos Buleandra (dragos.buleandra@trade2b.ro)
*/
public final class CellRangeAddress {
private static final int ENCODED_SIZE = 8;
private int _firstRow;
private int _firstCol;
private int _lastRow;
private int _lastCol;
/*
* TODO - replace other incarnations of 'Cell Range Address' throughout POI:
* org.apache.poi.hssf.util.CellRange
* org.apache.poi.hssf.record.cf.CellRange
* org.apache.poi.hssf.util.HSSFCellRangeAddress.AddrStructure
* org.apache.poi.hssf.record.MergeCellsRecord.MergedRegion
* org.apache.poi.hssf.record.SelectionRecord.Reference
*
*/
public CellRangeAddress(int firstRow, int lastRow, int firstCol, int lastCol) {
_firstRow = firstRow;
_lastRow = lastRow;
_firstCol = firstCol;
_lastCol = lastCol;
}
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();
}
/**
* @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;
}
/* package */ int serialize(byte[] data, int offset) {
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;
}
public static int getEncodedSize(int numberOfItems) {
return numberOfItems * ENCODED_SIZE;
}
}

View File

@ -0,0 +1,117 @@
/* ====================================================================
Copyright 2002-2004 Apache Software Foundation
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
==================================================================== */
package org.apache.poi.hssf.util;
import java.util.ArrayList;
import java.util.List;
import org.apache.poi.hssf.record.RecordInputStream;
import org.apache.poi.util.LittleEndian;
/**
* Implementation of the cell range address lists,like is described
* in OpenOffice.org's Excel Documentation: excelfileformat.pdf sec 2.5.14 -
* 'Cell Range Address List'
*
* In BIFF8 there is a common way to store absolute cell range address lists in
* several records (not formulas). A cell range address list consists of a field
* with the number of ranges and the list of the range addresses. Each cell
* range address (called an ADDR structure) contains 4 16-bit-values.
* </p>
*
* @author Dragos Buleandra (dragos.buleandra@trade2b.ro)
*/
public final class CellRangeAddressList {
/**
* List of <tt>CellRangeAddress</tt>es. Each structure represents a cell range
*/
private final List _list;
public CellRangeAddressList() {
_list = new ArrayList();
}
/**
* Convenience constructor for creating a <tt>CellRangeAddressList</tt> with a single
* <tt>CellRangeAddress</tt>. Other <tt>CellRangeAddress</tt>es may be added later.
*/
public CellRangeAddressList(int firstRow, int lastRow, int firstCol, int lastCol) {
this();
addCellRangeAddress(firstRow, firstCol, lastRow, lastCol);
}
/**
* @param in the RecordInputstream to read the record from
*/
public CellRangeAddressList(RecordInputStream in) {
int nItems = in.readUShort();
_list = new ArrayList(nItems);
for (int k = 0; k < nItems; k++) {
_list.add(new CellRangeAddress(in));
}
}
/**
* Get the number of following ADDR structures. The number of this
* structures is automatically set when reading an Excel file and/or
* increased when you manually add a new ADDR structure . This is the reason
* there isn't a set method for this field .
*
* @return number of ADDR structures
*/
public int getADDRStructureNumber() {
return _list.size();
}
/**
* Add an ADDR structure .
*
* @param firstRow - the upper left hand corner's row
* @param firstCol - the upper left hand corner's col
* @param lastRow - the lower right hand corner's row
* @param lastCol - the lower right hand corner's col
* @return the index of this ADDR structure
*/
public void addCellRangeAddress(int firstRow, int firstCol, int lastRow, int lastCol) {
CellRangeAddress region = new CellRangeAddress(firstRow, lastRow, firstCol, lastCol);
_list.add(region);
}
/**
* @return <tt>CellRangeAddress</tt> at the given index
*/
public CellRangeAddress getCellRangeAddress(int index) {
return (CellRangeAddress) _list.get(index);
}
public int serialize(int offset, byte[] data) {
int pos = 2;
int nItems = _list.size();
LittleEndian.putUShort(data, offset, nItems);
for (int k = 0; k < nItems; k++) {
CellRangeAddress region = (CellRangeAddress) _list.get(k);
pos += region.serialize(data, offset + pos);
}
return getSize();
}
public int getSize() {
return 2 + CellRangeAddress.getEncodedSize(_list.size());
}
}

View File

@ -1,269 +0,0 @@
/* ====================================================================
Copyright 2002-2004 Apache Software Foundation
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
==================================================================== */
package org.apache.poi.hssf.util;
import org.apache.poi.hssf.record.RecordInputStream;
import org.apache.poi.util.LittleEndian;
import org.apache.poi.util.POILogFactory;
import org.apache.poi.util.POILogger;
import java.util.ArrayList;
/**
* <p>Title: HSSFCellRangeAddress</p>
* <p>Description:
* Implementation of the cell range address lists,like is described in
* OpenOffice.org's Excel Documentation .
* In BIFF8 there is a common way to store absolute cell range address
* lists in several records (not formulas). A cell range address list
* consists of a field with the number of ranges and the list of the range
* addresses. Each cell range address (called an ADDR structure) contains
* 4 16-bit-values.</p>
* <p>Copyright: Copyright (c) 2004</p>
* <p>Company: </p>
* @author Dragos Buleandra (dragos.buleandra@trade2b.ro)
* @version 2.0-pre
*/
public class HSSFCellRangeAddress
{
private static POILogger logger = POILogFactory.getLogger(HSSFCellRangeAddress.class);
/**
* Number of following ADDR structures
*/
private short field_addr_number;
/**
* List of ADDR structures. Each structure represents a cell range
*/
private ArrayList field_regions_list;
public HSSFCellRangeAddress()
{
}
/**
* Construct a new HSSFCellRangeAddress object and sets its fields appropriately .
* Even this isn't an Excel record , I kept the same behavior for reading/writing
* the object's data as for a regular record .
*
* @param in the RecordInputstream to read the record from
*/
public HSSFCellRangeAddress(RecordInputStream in)
{
this.fillFields(in);
}
public void fillFields(RecordInputStream in)
{
this.field_addr_number = in.readShort();
this.field_regions_list = new ArrayList(this.field_addr_number);
for (int k = 0; k < this.field_addr_number; k++)
{
short first_row = in.readShort();
short first_col = in.readShort();
short last_row = first_row;
short last_col = first_col;
if(in.remaining() >= 4) {
last_row = in.readShort();
last_col = in.readShort();
} else {
// Ran out of data
// For now, issue a warning, finish, and
// hope for the best....
logger.log(POILogger.WARN, "Ran out of data reading cell references for DVRecord");
k = this.field_addr_number;
}
AddrStructure region = new AddrStructure(first_row, first_col, last_row, last_col);
this.field_regions_list.add(region);
}
}
/**
* Get the number of following ADDR structures.
* The number of this structures is automatically set when reading an Excel file
* and/or increased when you manually add a new ADDR structure .
* This is the reason there isn't a set method for this field .
* @return number of ADDR structures
*/
public short getADDRStructureNumber()
{
return this.field_addr_number;
}
/**
* Add an ADDR structure .
* @param first_row - the upper left hand corner's row
* @param first_col - the upper left hand corner's col
* @param last_row - the lower right hand corner's row
* @param last_col - the lower right hand corner's col
* @return the index of this ADDR structure
*/
public int addADDRStructure(short first_row, short first_col, short last_row, short last_col)
{
if (this.field_regions_list == null)
{
//just to be sure :-)
this.field_addr_number= 0;
this.field_regions_list = new ArrayList(10);
}
AddrStructure region = new AddrStructure(first_row, last_row, first_col, last_col);
this.field_regions_list.add(region);
this.field_addr_number++;
return this.field_addr_number;
}
/**
* Remove the ADDR structure stored at the passed in index
* @param index The ADDR structure's index
*/
public void removeADDRStructureAt(int index)
{
this.field_regions_list.remove(index);
this.field_addr_number--;
}
/**
* return the ADDR structure at the given index.
* @return AddrStructure representing
*/
public AddrStructure getADDRStructureAt(int index)
{
return ( AddrStructure ) this.field_regions_list.get(index);
}
public int serialize(int offset, byte [] data)
{
int pos = 2;
LittleEndian.putShort(data, offset, this.getADDRStructureNumber());
for (int k = 0; k < this.getADDRStructureNumber(); k++)
{
AddrStructure region = this.getADDRStructureAt(k);
LittleEndian.putShort(data, offset + pos, region.getFirstRow());
pos += 2;
LittleEndian.putShort(data, offset + pos, region.getLastRow());
pos += 2;
LittleEndian.putShort(data, offset + pos, region.getFirstColumn());
pos += 2;
LittleEndian.putShort(data, offset + pos, region.getLastColumn());
pos += 2;
}
return this.getSize();
}
public int getSize()
{
return 2 + this.field_addr_number*8;
}
public class AddrStructure
{
private short _first_row;
private short _first_col;
private short _last_row;
private short _last_col;
public AddrStructure(short first_row, short last_row, short first_col, short last_col)
{
this._first_row = first_row;
this._last_row = last_row;
this._first_col = first_col;
this._last_col = last_col;
}
/**
* get the upper left hand corner column number
* @return column number for the upper left hand corner
*/
public short getFirstColumn()
{
return this._first_col;
}
/**
* get the upper left hand corner row number
* @return row number for the upper left hand corner
*/
public short getFirstRow()
{
return this._first_row;
}
/**
* get the lower right hand corner column number
* @return column number for the lower right hand corner
*/
public short getLastColumn()
{
return this._last_col;
}
/**
* get the lower right hand corner row number
* @return row number for the lower right hand corner
*/
public short getLastRow()
{
return this._last_row;
}
/**
* set the upper left hand corner column number
* @param this._first_col column number for the upper left hand corner
*/
public void setFirstColumn(short first_col)
{
this._first_col = first_col;
}
/**
* set the upper left hand corner row number
* @param rowFrom row number for the upper left hand corner
*/
public void setFirstRow(short first_row)
{
this._first_row = first_row;
}
/**
* set the lower right hand corner column number
* @param colTo column number for the lower right hand corner
*/
public void setLastColumn(short last_col)
{
this._last_col = last_col;
}
/**
* get the lower right hand corner row number
* @param rowTo row number for the lower right hand corner
*/
public void setLastRow(short last_row)
{
this._last_row = last_row;
}
}
}

View File

@ -1,483 +0,0 @@
/* ====================================================================
Copyright 2002-2004 Apache Software Foundation
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
==================================================================== */
package org.apache.poi.hssf.util;
/**
* <p>Title: HSSFDataValidation</p>
* <p>Description: Utilty class for creating data validation cells</p>
* <p>Copyright: Copyright (c) 2004</p>
* <p>Company: </p>
* @author Dragos Buleandra (dragos.buleandra@trade2b.ro)
* @version 2.0-pre
*/
public class HSSFDataValidation
{
/**
* Validation data type constants
*/
/**
* Any type
*/
public static final int DATA_TYPE_ANY = 0x00;
/**
* Integer type
*/
public static final int DATA_TYPE_INTEGER = 0x01;
/**
* Decimal type
*/
public static final int DATA_TYPE_DECIMAL = 0x02;
/**
* List type ( combo box type )
*/
public static final int DATA_TYPE_LIST = 0x03;
/**
* Date type
*/
public static final int DATA_TYPE_DATE = 0x04;
/**
* Time type
*/
public static final int DATA_TYPE_TIME = 0x05;
/**
* String length type
*/
public static final int DATA_TYPE_TEXT_LENGTH = 0x06;
/**
* Formula ( custom ) type
*/
public static final int DATA_TYPE_FORMULA = 0x07;
/**
* Error style constants for error box
*/
/**
* STOP style like
*/
public static final int ERROR_STYLE_STOP = 0x00;
/**
* WARNING style like
*/
public static final int ERROR_STYLE_WARNING = 0x01;
/**
* INFO style like
*/
public static final int ERROR_STYLE_INFO = 0x02;
/**
* Condition operator
*/
public static final int OPERATOR_BETWEEN = 0x00;
public static final int OPERATOR_NOT_BETWEEN = 0x01;
public static final int OPERATOR_EQUAL = 0x02;
public static final int OPERATOR_NOT_EQUAL = 0x03;
public static final int OPERATOR_GREATER_THAN = 0x04;
public static final int OPERATOR_LESS_THAN = 0x05;
public static final int OPERATOR_GREATER_OR_EQUAL = 0x06;
public static final int OPERATOR_LESS_OR_EQUAL = 0x07;
private short _first_row = 0;
private short _first_col = 0;
private short _last_row = 0;
private short _last_col = 0;
private String _prompt_title = null;
private String _prompt_text = null;
private String _error_title = null;
private String _error_text = null;
private String _string_first_formula = null;
private String _string_sec_formula = null;
private int _data_type = HSSFDataValidation.DATA_TYPE_ANY;
private int _error_style = HSSFDataValidation.ERROR_STYLE_STOP;
private boolean _list_explicit_formula = true;
private boolean _empty_cell_allowed = true;
private boolean _surpress_dropdown_arrow = false;
private boolean _show_prompt_box = true;
private boolean _show_error_box = true;
private int _operator = HSSFDataValidation.OPERATOR_BETWEEN;
/**
* Empty constructor
*/
public HSSFDataValidation( )
{
}
/**
* Constructor wich initializes the cell range on wich this object will be applied
* @param first_row First row
* @param first_col First column
* @param last_row Last row
* @param last_col Last column
*/
public HSSFDataValidation( short first_row, short first_col, short last_row, short last_col )
{
this._first_row = first_row;
this._first_col = first_col;
this._last_row = last_row;
this._last_col = last_col;
}
/**
* Set the type of this object
* @param data_type The type
* @see DATA_TYPE_ANY, DATA_TYPE_INTEGER, DATA_TYPE_DECIMNAL, DATA_TYPE_LIST, DATA_TYPE_DATE,
* DATA_TYPE_TIME, DATA_TYPE_TEXT_LENTGH, DATA_TYPE_FORMULA
*/
public void setDataValidationType( int data_type )
{
this._data_type = data_type;
}
/**
* The data type of this object
* @return The type
* @see DATA_TYPE_ANY, DATA_TYPE_INTEGER, DATA_TYPE_DECIMNAL, DATA_TYPE_LIST, DATA_TYPE_DATE,
* DATA_TYPE_TIME, DATA_TYPE_TEXT_LENTGH, DATA_TYPE_FORMULA
*/
public int getDataValidationType()
{
return this._data_type;
}
/**
* Sets the error style for error box
* @param error_style Error style constant
* @see ERROR_STYLE_STOP, ERROR_STYLE_WARNING, ERROR_STYLE_INFO
*/
public void setErrorStyle( int error_style )
{
this._error_style = error_style;
}
/**
* returns the error style of errror box
* @return the style constant
* @see ERROR_STYLE_STOP, ERROR_STYLE_WARNING, ERROR_STYLE_INFO
*/
public int getErrorStyle( )
{
return this._error_style;
}
/**
* If this object has an explicit formula . This is useful only for list data validation object
* @param explicit True if use an explicit formula
*/
public void setExplicitListFormula( boolean explicit )
{
this._list_explicit_formula = explicit;
}
/**
* Returns the settings for explicit formula . This is useful only for list data validation objects.
* This method always returns false if the object isn't a list validation object
* @see setDataValidationType( int data_type )
* @return
*/
public boolean getExplicitListFormula( )
{
if ( this._data_type != HSSFDataValidation.DATA_TYPE_LIST )
{
return false;
}
return this._list_explicit_formula ;
}
/**
* Sets if this object allows empty as a valid value
* @param allowed True if this object should treats empty as valid value , false otherwise
*/
public void setEmptyCellAllowed( boolean allowed )
{
this._empty_cell_allowed = allowed;
}
/**
* Retrieve the settings for empty cells allowed
* @return True if this object should treats empty as valid value , false otherwise
*/
public boolean getEmptyCellAllowed( )
{
return this._empty_cell_allowed ;
}
/**
* @deprecated - (Jul-2008) use setSuppressDropDownArrow
*/
public void setSurppressDropDownArrow( boolean suppress ) {
setSuppressDropDownArrow(suppress);
}
/**
* @deprecated - (Jul-2008) use getSuppressDropDownArrow
*/
public boolean getSurppressDropDownArrow( ) {
return getSuppressDropDownArrow();
}
/**
* Useful for list validation objects .
* @param surppres True if a list should display the values into a drop down list , false otherwise .
* In other words , if a list should display the arrow sign on its right side
*/
public void setSuppressDropDownArrow( boolean surppres )
{
this._surpress_dropdown_arrow = surppres;
}
/**
* Useful only list validation objects .
* This method always returns false if the object isn't a list validation object
* @return True if a list should display the values into a drop down list , false otherwise .
* @see setDataValidationType( int data_type )
*/
public boolean getSuppressDropDownArrow( )
{
if ( this._data_type != HSSFDataValidation.DATA_TYPE_LIST )
{
return false;
}
return this._surpress_dropdown_arrow ;
}
/**
* Sets the behaviour when a cell which belongs to this object is selected
* @param show True if an prompt box should be displayed , false otherwise
*/
public void setShowPromptBox( boolean show )
{
this._show_prompt_box = show;
}
/**
* @param show True if an prompt box should be displayed , false otherwise
*/
public boolean getShowPromptBox( )
{
if ( (this.getPromptBoxText() == null) && (this.getPromptBoxTitle() == null) )
{
return false;
}
return this._show_prompt_box ;
}
/**
* Sets the behaviour when an invalid value is entered
* @param show True if an error box should be displayed , false otherwise
*/
public void setShowErrorBox( boolean show )
{
this._show_error_box = show;
}
/**
* @return True if an error box should be displayed , false otherwise
*/
public boolean getShowErrorBox( )
{
if ( (this.getErrorBoxText() == null) && (this.getErrorBoxTitle() == null) )
{
return false;
}
return this._show_error_box ;
}
/**
* Sets the operator involved in the formula whic governs this object
* Example : if you wants that a cell to accept only values between 1 and 5 , which
* mathematically means 1 <= value <= 5 , then the operator should be OPERATOR_BETWEEN
* @param operator A constant for operator
* @see OPERATOR_BETWEEN, OPERATOR_NOT_BETWEEN, OPERATOR_EQUAL, OPERATOR_NOT_EQUAL
* OPERATOR_GREATER_THAN, OPERATOR_LESS_THAN, OPERATOR_GREATER_OR_EQUAL,
* OPERATOR_LESS_OR_EQUAL
*/
public void setOperator( int operator )
{
this._operator = operator;
}
/**
* Retrieves the operator used for this object's formula
* @return
* @see OPERATOR_BETWEEN, OPERATOR_NOT_BETWEEN, OPERATOR_EQUAL, OPERATOR_NOT_EQUAL
* OPERATOR_GREATER_THAN, OPERATOR_LESS_THAN, OPERATOR_GREATER_OR_EQUAL,
* OPERATOR_LESS_OR_EQUAL
*/
public int getOperator()
{
return this._operator;
}
/**
* Sets the title and text for the prompt box . Prompt box is displayed when the user
* selects a cell which belongs to this validation object . In order for a prompt box
* to be displayed you should also use method setShowPromptBox( boolean show )
* @param title The prompt box's title
* @param text The prompt box's text
* @see setShowPromptBox( boolean show )
*/
public void createPromptBox( String title, String text )
{
this._prompt_title = title;
this._prompt_text = text;
this.setShowPromptBox(true);
}
/**
* Returns the prompt box's title
* @return Prompt box's title or null
*/
public String getPromptBoxTitle( )
{
return this._prompt_title;
}
/**
* Returns the prompt box's text
* @return Prompt box's text or null
*/
public String getPromptBoxText( )
{
return this._prompt_text;
}
/**
* Sets the title and text for the error box . Error box is displayed when the user
* enters an invalid value int o a cell which belongs to this validation object .
* In order for an error box to be displayed you should also use method
* setShowErrorBox( boolean show )
* @param title The error box's title
* @param text The error box's text
* @see setShowErrorBox( boolean show )
*/
public void createErrorBox( String title, String text )
{
this._error_title = title;
this._error_text = text;
this.setShowErrorBox(true);
}
/**
* Returns the error box's title
* @return Error box's title or null
*/
public String getErrorBoxTitle( )
{
return this._error_title;
}
/**
* Returns the error box's text
* @return Error box's text or null
*/
public String getErrorBoxText( )
{
return this._error_text;
}
/**
* Sets the first formula for this object .
* A formula is divided into three parts : first formula , operator and second formula .
* In other words , a formula contains a left oprand , an operator and a right operand.
* This is the general rule . An example is 1<= value <= 5 . In this case ,
* the left operand ( or the first formula ) is the number 1 . The operator is
* OPERATOR_BETWEEN and the right operand ( or the second formula ) is 5 .
* @param formula
*/
public void setFirstFormula( String formula )
{
this._string_first_formula = formula;
}
/**
* Returns the first formula
* @return
*/
public String getFirstFormula( )
{
return this._string_first_formula;
}
/**
* Sets the first formula for this object .
* A formula is divided into three parts : first formula , operator and second formula .
* In other words , a formula contains a left oprand , an operator and a right operand.
* This is the general rule . An example is 1<= value <=5 . In this case ,
* the left operand ( or the first formula ) is the number 1 . The operator is
* OPERATOR_BETWEEN and the right operand ( or the second formula ) is 5 .
* But there are cases when a second formula isn't needed :
* You want somethink like : all values less than 5 . In this case , there's only a first
* formula ( in our case 5 ) and the operator OPERATOR_LESS_THAN
* @param formula
*/
public void setSecondFormula( String formula )
{
this._string_sec_formula = formula;
}
/**
* Returns the second formula
* @return
*/
public String getSecondFormula( )
{
return this._string_sec_formula;
}
public void setFirstRow( short first_row )
{
this._first_row = first_row;
}
public void setFirstColumn( short first_column )
{
this._first_col = first_column;
}
public void setLastRow( short last_row )
{
this._last_row = last_row;
}
public void setLastColumn( short last_column )
{
this._last_col = last_column;
}
public short getFirstRow()
{
return this._first_row;
}
public short getFirstColumn()
{
return this._first_col;
}
public short getLastRow()
{
return this._last_row;
}
public short getLastColumn()
{
return this._last_col;
}
}

View File

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