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 5203a8ce6c
commit 9b67bb83a4
18 changed files with 2100 additions and 2457 deletions

View File

@ -37,6 +37,7 @@
<!-- Don't forget to update status.xml too! -->
<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="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>

View File

@ -34,6 +34,7 @@
<!-- Don't forget to update changes.xml too! -->
<changes>
<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="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>

View File

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

View File

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

View File

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

View File

@ -19,9 +19,7 @@ package org.apache.poi.hssf.record.aggregates;
import java.util.ArrayList;
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.record.CFHeaderRecord;
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.SelectionRecord;
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/>
@ -177,63 +171,7 @@ public final class DataValidityTable extends RecordAggregate {
return false;
}
public void addDataValidation(HSSFDataValidation dataValidation, HSSFWorkbook workbook) {
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);
public void addDataValidation(DVRecord dvRecord) {
_validationList.add(dvRecord);
_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 */
public NumberPtg(RecordInputStream in)
{
field_1_value = in.readDouble();
this(in.readDouble());
}
/** Create a NumberPtg from a string representation of the number
@ -45,9 +45,12 @@ public final class NumberPtg extends ScalarConstantPtg {
* @param value : String representation of a floating point number
*/
public NumberPtg(String value) {
field_1_value = Double.parseDouble(value);
this(Double.parseDouble(value));
}
public NumberPtg(double value) {
field_1_value = value;
}
public double getValue()
{
@ -67,6 +70,15 @@ public final class NumberPtg extends ScalarConstantPtg {
public String toFormulaString(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)
*/
public abstract class Ptg implements Cloneable {
public static final Ptg[] EMPTY_PTG_ARRAY = { };
/* convert infix order ptg list to rpn order ptg list
* @return List ptgs in RPN order
@ -250,6 +251,9 @@ public abstract class Ptg implements Cloneable {
}
}
private static Ptg[] toPtgArray(List l) {
if (l.isEmpty()) {
return EMPTY_PTG_ARRAY;
}
Ptg[] result = new Ptg[l.size()];
l.toArray(result);
return result;

View File

@ -0,0 +1,479 @@
/* ====================================================================
Copyright 2002-2004 Apache Software Foundation
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
==================================================================== */
package org.apache.poi.hssf.usermodel;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import org.apache.poi.hssf.model.FormulaParser;
import org.apache.poi.hssf.record.formula.NumberPtg;
import org.apache.poi.hssf.record.formula.Ptg;
import org.apache.poi.hssf.record.formula.StringPtg;
/**
*
* @author Josh Micich
*/
public class DVConstraint {
/**
* ValidationType enum
*/
public static final class ValidationType {
private ValidationType() {
// no instances of this class
}
/** 'Any value' type - value not restricted */
public static final int ANY = 0x00;
/** Integer ('Whole number') type */
public static final int INTEGER = 0x01;
/** Decimal type */
public static final int DECIMAL = 0x02;
/** List type ( combo box type ) */
public static final int LIST = 0x03;
/** Date type */
public static final int DATE = 0x04;
/** Time type */
public static final int TIME = 0x05;
/** String length type */
public static final int TEXT_LENGTH = 0x06;
/** Formula ( 'Custom' ) type */
public static final int FORMULA = 0x07;
}
/**
* Condition operator enum
*/
public static final class OperatorType {
private OperatorType() {
// no instances of this class
}
public static final int BETWEEN = 0x00;
public static final int NOT_BETWEEN = 0x01;
public static final int EQUAL = 0x02;
public static final int NOT_EQUAL = 0x03;
public static final int GREATER_THAN = 0x04;
public static final int LESS_THAN = 0x05;
public static final int GREATER_OR_EQUAL = 0x06;
public static final int LESS_OR_EQUAL = 0x07;
/** default value to supply when the operator type is not used */
public static final int IGNORED = BETWEEN;
/* package */ static void validateSecondArg(int comparisonOperator, String paramValue) {
switch (comparisonOperator) {
case BETWEEN:
case NOT_BETWEEN:
if (paramValue == null) {
throw new IllegalArgumentException("expr2 must be supplied for 'between' comparisons");
}
// all other operators don't need second arg
}
}
}
/* package */ static final class FormulaPair {
private final Ptg[] _formula1;
private final Ptg[] _formula2;
public FormulaPair(Ptg[] formula1, Ptg[] formula2) {
_formula1 = formula1;
_formula2 = formula2;
}
public Ptg[] getFormula1() {
return _formula1;
}
public Ptg[] getFormula2() {
return _formula2;
}
}
// convenient access to ValidationType namespace
private static final ValidationType VT = null;
private final int _validationType;
private int _operator;
private String[] _explicitListValues;
private String _formula1;
private String _formula2;
private Double _value1;
private Double _value2;
private DVConstraint(int validationType, int comparisonOperator, String formulaA,
String formulaB, Double value1, Double value2, String[] excplicitListValues) {
_validationType = validationType;
_operator = comparisonOperator;
_formula1 = formulaA;
_formula2 = formulaB;
_value1 = value1;
_value2 = value2;
_explicitListValues = excplicitListValues;
}
/**
* Creates a list constraint
*/
private DVConstraint(String listFormula, String[] excplicitListValues) {
this(ValidationType.LIST, OperatorType.IGNORED,
listFormula, null, null, null, excplicitListValues);
}
/**
* Creates a number based data validation constraint. The text values entered for expr1 and expr2
* can be either standard Excel formulas or formatted number values. If the expression starts
* with '=' it is parsed as a formula, otherwise it is parsed as a formatted number.
*
* @param validationType one of {@link ValidationType#ANY}, {@link ValidationType#DECIMAL},
* {@link ValidationType#INTEGER}, {@link ValidationType#TEXT_LENGTH}
* @param comparisonOperator any constant from {@link OperatorType} enum
* @param expr1 date formula (when first char is '=') or formatted number value
* @param expr2 date formula (when first char is '=') or formatted number value
*/
public static DVConstraint createNumericConstraint(int validationType, int comparisonOperator,
String expr1, String expr2) {
switch (validationType) {
case ValidationType.ANY:
if (expr1 != null || expr2 != null) {
throw new IllegalArgumentException("expr1 and expr2 must be null for validation type 'any'");
}
break;
case ValidationType.DECIMAL:
case ValidationType.INTEGER:
case ValidationType.TEXT_LENGTH:
if (expr1 == null) {
throw new IllegalArgumentException("expr1 must be supplied");
}
OperatorType.validateSecondArg(comparisonOperator, expr2);
break;
default:
throw new IllegalArgumentException("Validation Type ("
+ validationType + ") not supported with this method");
}
// formula1 and value1 are mutually exclusive
String formula1 = getFormulaFromTextExpression(expr1);
Double value1 = formula1 == null ? convertNumber(expr1) : null;
// formula2 and value2 are mutually exclusive
String formula2 = getFormulaFromTextExpression(expr2);
Double value2 = formula2 == null ? convertNumber(expr2) : null;
return new DVConstraint(validationType, comparisonOperator, formula1, formula2, value1, value2, null);
}
public static DVConstraint createFormulaListConstraint(String listFormula) {
return new DVConstraint(listFormula, null);
}
public static DVConstraint createExplicitListConstraint(String[] explicitListValues) {
return new DVConstraint(null, explicitListValues);
}
/**
* Creates a time based data validation constraint. The text values entered for expr1 and expr2
* can be either standard Excel formulas or formatted time values. If the expression starts
* with '=' it is parsed as a formula, otherwise it is parsed as a formatted time. To parse
* formatted times, two formats are supported: "HH:MM" or "HH:MM:SS". This is contrary to
* Excel which uses the default time format from the OS.
*
* @param comparisonOperator constant from {@link OperatorType} enum
* @param expr1 date formula (when first char is '=') or formatted time value
* @param expr2 date formula (when first char is '=') or formatted time value
*/
public static DVConstraint createTimeConstraint(int comparisonOperator, String expr1, String expr2) {
if (expr1 == null) {
throw new IllegalArgumentException("expr1 must be supplied");
}
OperatorType.validateSecondArg(comparisonOperator, expr1);
// formula1 and value1 are mutually exclusive
String formula1 = getFormulaFromTextExpression(expr1);
Double value1 = formula1 == null ? convertTime(expr1) : null;
// formula2 and value2 are mutually exclusive
String formula2 = getFormulaFromTextExpression(expr2);
Double value2 = formula2 == null ? convertTime(expr2) : null;
return new DVConstraint(VT.TIME, comparisonOperator, formula1, formula2, value1, value2, null);
}
/**
* Creates a date based data validation constraint. The text values entered for expr1 and expr2
* can be either standard Excel formulas or formatted date values. If the expression starts
* with '=' it is parsed as a formula, otherwise it is parsed as a formatted date (Excel uses
* the same convention). To parse formatted dates, a date format needs to be specified. This
* is contrary to Excel which uses the default short date format from the OS.
*
* @param comparisonOperator constant from {@link OperatorType} enum
* @param expr1 date formula (when first char is '=') or formatted date value
* @param expr2 date formula (when first char is '=') or formatted date value
* @param dateFormat ignored if both expr1 and expr2 are formulas. Default value is "YYYY/MM/DD"
* otherwise any other valid argument for <tt>SimpleDateFormat</tt> can be used
* @see <a href='http://java.sun.com/j2se/1.5.0/docs/api/java/text/DateFormat.html'>SimpleDateFormat</a>
*/
public static DVConstraint createDateConstraint(int comparisonOperator, String expr1, String expr2, String dateFormat) {
if (expr1 == null) {
throw new IllegalArgumentException("expr1 must be supplied");
}
OperatorType.validateSecondArg(comparisonOperator, expr2);
SimpleDateFormat df = dateFormat == null ? null : new SimpleDateFormat(dateFormat);
// formula1 and value1 are mutually exclusive
String formula1 = getFormulaFromTextExpression(expr1);
Double value1 = formula1 == null ? convertDate(expr1, df) : null;
// formula2 and value2 are mutually exclusive
String formula2 = getFormulaFromTextExpression(expr2);
Double value2 = formula2 == null ? convertDate(expr2, df) : null;
return new DVConstraint(VT.DATE, comparisonOperator, formula1, formula2, value1, value2, null);
}
/**
* Distinguishes formula expressions from simple value expressions. This logic is only
* required by a few factory methods in this class that create data validation constraints
* from more or less the same parameters that would have been entered in the Excel UI. The
* data validation dialog box uses the convention that formulas begin with '='. Other methods
* in this class follow the POI convention (formulas and values are distinct), so the '='
* convention is not used there.
*
* @param textExpr a formula or value expression
* @return all text after '=' if textExpr begins with '='. Otherwise <code>null</code> if textExpr does not begin with '='
*/
private static String getFormulaFromTextExpression(String textExpr) {
if (textExpr == null) {
return null;
}
if (textExpr.length() < 1) {
throw new IllegalArgumentException("Empty string is not a valid formula/value expression");
}
if (textExpr.charAt(0) == '=') {
return textExpr.substring(1);
}
return null;
}
/**
* @return <code>null</code> if numberStr is <code>null</code>
*/
private static Double convertNumber(String numberStr) {
if (numberStr == null) {
return null;
}
try {
return new Double(numberStr);
} catch (NumberFormatException e) {
throw new RuntimeException("The supplied text '" + numberStr
+ "' could not be parsed as a number");
}
}
/**
* @return <code>null</code> if timeStr is <code>null</code>
*/
private static Double convertTime(String timeStr) {
if (timeStr == null) {
return null;
}
return new Double(HSSFDateUtil.convertTime(timeStr));
}
/**
* @param dateFormat pass <code>null</code> for default YYYYMMDD
* @return <code>null</code> if timeStr is <code>null</code>
*/
private static Double convertDate(String dateStr, SimpleDateFormat dateFormat) {
if (dateStr == null) {
return null;
}
Date dateVal;
if (dateFormat == null) {
dateVal = HSSFDateUtil.parseYYYYMMDDDate(dateStr);
} else {
try {
dateVal = dateFormat.parse(dateStr);
} catch (ParseException e) {
throw new RuntimeException("Failed to parse date '" + dateStr
+ "' using specified format '" + dateFormat + "'", e);
}
}
return new Double(HSSFDateUtil.getExcelDate(dateVal));
}
public static DVConstraint 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;
import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.regex.Pattern;
/**
* Contains methods for dealing with Excel dates.
@ -38,17 +33,20 @@ import java.util.GregorianCalendar;
* @author Alex Jacoby (ajacoby at gmail.com)
* @author Pavel Krupets (pkrupets at palmtreebusiness dot com)
*/
public class HSSFDateUtil
{
private HSSFDateUtil()
{
public final class HSSFDateUtil {
private HSSFDateUtil() {
// no instances of this class
}
private static final int SECONDS_PER_MINUTE = 60;
private static final int MINUTES_PER_HOUR = 60;
private static final int HOURS_PER_DAY = 24;
private static final int SECONDS_PER_DAY = (HOURS_PER_DAY * MINUTES_PER_HOUR * SECONDS_PER_MINUTE);
private static final int BAD_DATE = -1; // used to specify that date is invalid
private static final long DAY_MILLISECONDS = SECONDS_PER_DAY * 1000L;
private static final Pattern TIME_SEPARATOR_PATTERN = Pattern.compile(":");
private static final int BAD_DATE =
-1; // used to specify that date is invalid
private static final long DAY_MILLISECONDS = 24 * 60 * 60 * 1000;
/**
* Given a Date, converts it into a double representing its internal Excel representation,
* which is the number of days since 1/1/1900. Fractional days represent hours, minutes, and seconds.
@ -57,7 +55,7 @@ public class HSSFDateUtil
* @param date the Date
*/
public static double getExcelDate(Date date) {
return getExcelDate(date, false);
return getExcelDate(date, false);
}
/**
* Given a Date, converts it into a double representing its internal Excel representation,
@ -74,8 +72,8 @@ public class HSSFDateUtil
}
/**
* Given a Date in the form of a Calendar, converts it into a double
* representing its internal Excel representation, which is the
* number of days since 1/1/1900. Fractional days represent hours,
* representing its internal Excel representation, which is the
* number of days since 1/1/1900. Fractional days represent hours,
* minutes, and seconds.
*
* @return Excel representation of Date (-1 if error - test for error by checking for less than 0.1)
@ -83,41 +81,40 @@ public class HSSFDateUtil
* @param use1904windowing Should 1900 or 1904 date windowing be used?
*/
public static double getExcelDate(Calendar date, boolean use1904windowing) {
// Don't alter the supplied Calendar as we do our work
return internalGetExcelDate( (Calendar)date.clone(), use1904windowing );
// Don't alter the supplied Calendar as we do our work
return internalGetExcelDate( (Calendar)date.clone(), use1904windowing );
}
private static double internalGetExcelDate(Calendar date, boolean use1904windowing) {
if ((!use1904windowing && date.get(Calendar.YEAR) < 1900) ||
(use1904windowing && date.get(Calendar.YEAR) < 1904))
if ((!use1904windowing && date.get(Calendar.YEAR) < 1900) ||
(use1904windowing && date.get(Calendar.YEAR) < 1904))
{
return BAD_DATE;
} else {
// Because of daylight time saving we cannot use
// date.getTime() - calStart.getTimeInMillis()
// as the difference in milliseconds between 00:00 and 04:00
// can be 3, 4 or 5 hours but Excel expects it to always
// be 4 hours.
// E.g. 2004-03-28 04:00 CEST - 2004-03-28 00:00 CET is 3 hours
// and 2004-10-31 04:00 CET - 2004-10-31 00:00 CEST is 5 hours
double fraction = (((date.get(Calendar.HOUR_OF_DAY) * 60
+ date.get(Calendar.MINUTE)
) * 60 + date.get(Calendar.SECOND)
) * 1000 + date.get(Calendar.MILLISECOND)
) / ( double ) DAY_MILLISECONDS;
Calendar calStart = dayStart(date);
double value = fraction + absoluteDay(calStart, use1904windowing);
if (!use1904windowing && value >= 60) {
value++;
} else if (use1904windowing) {
value--;
}
return value;
}
// Because of daylight time saving we cannot use
// date.getTime() - calStart.getTimeInMillis()
// as the difference in milliseconds between 00:00 and 04:00
// can be 3, 4 or 5 hours but Excel expects it to always
// be 4 hours.
// E.g. 2004-03-28 04:00 CEST - 2004-03-28 00:00 CET is 3 hours
// and 2004-10-31 04:00 CET - 2004-10-31 00:00 CEST is 5 hours
double fraction = (((date.get(Calendar.HOUR_OF_DAY) * 60
+ date.get(Calendar.MINUTE)
) * 60 + date.get(Calendar.SECOND)
) * 1000 + date.get(Calendar.MILLISECOND)
) / ( double ) DAY_MILLISECONDS;
Calendar calStart = dayStart(date);
double value = fraction + absoluteDay(calStart, use1904windowing);
if (!use1904windowing && value >= 60) {
value++;
} else if (use1904windowing) {
value--;
}
return value;
}
/**
* Given an Excel date with using 1900 date windowing, and
* converts it to a java.util.Date.
@ -130,13 +127,13 @@ public class HSSFDateUtil
* <code>Europe/Copenhagen</code>, on 2004-03-28 the minute after
* 01:59 CET is 03:00 CEST, if the excel date represents a time between
* 02:00 and 03:00 then it is converted to past 03:00 summer time
*
*
* @param date The Excel date.
* @return Java representation of the date, or null if date is not a valid Excel date
* @see java.util.TimeZone
*/
public static Date getJavaDate(double date) {
return getJavaDate(date, false);
return getJavaDate(date, false);
}
/**
* Given an Excel date with either 1900 or 1904 date windowing,
@ -158,94 +155,90 @@ public class HSSFDateUtil
* @see java.util.TimeZone
*/
public static Date getJavaDate(double date, boolean use1904windowing) {
if (isValidExcelDate(date)) {
int startYear = 1900;
int dayAdjust = -1; // Excel thinks 2/29/1900 is a valid date, which it isn't
int wholeDays = (int)Math.floor(date);
if (use1904windowing) {
startYear = 1904;
dayAdjust = 1; // 1904 date windowing uses 1/2/1904 as the first day
}
else if (wholeDays < 61) {
// Date is prior to 3/1/1900, so adjust because Excel thinks 2/29/1900 exists
// If Excel date == 2/29/1900, will become 3/1/1900 in Java representation
dayAdjust = 0;
}
GregorianCalendar calendar = new GregorianCalendar(startYear,0,
wholeDays + dayAdjust);
int millisecondsInDay = (int)((date - Math.floor(date)) *
DAY_MILLISECONDS + 0.5);
calendar.set(GregorianCalendar.MILLISECOND, millisecondsInDay);
return calendar.getTime();
}
else {
if (!isValidExcelDate(date)) {
return null;
}
int startYear = 1900;
int dayAdjust = -1; // Excel thinks 2/29/1900 is a valid date, which it isn't
int wholeDays = (int)Math.floor(date);
if (use1904windowing) {
startYear = 1904;
dayAdjust = 1; // 1904 date windowing uses 1/2/1904 as the first day
}
else if (wholeDays < 61) {
// Date is prior to 3/1/1900, so adjust because Excel thinks 2/29/1900 exists
// If Excel date == 2/29/1900, will become 3/1/1900 in Java representation
dayAdjust = 0;
}
GregorianCalendar calendar = new GregorianCalendar(startYear,0,
wholeDays + dayAdjust);
int millisecondsInDay = (int)((date - Math.floor(date)) *
DAY_MILLISECONDS + 0.5);
calendar.set(GregorianCalendar.MILLISECOND, millisecondsInDay);
return calendar.getTime();
}
/**
* Given a format ID and its format String, will check to see if the
* format represents a date format or not.
* Firstly, it will check to see if the format ID corresponds to an
* internal excel date format (eg most US date formats)
* internal excel date format (eg most US date formats)
* If not, it will check to see if the format string only contains
* date formatting characters (ymd-/), which covers most
* non US date formats.
*
*
* @param formatIndex The index of the format, eg from ExtendedFormatRecord.getFormatIndex
* @param formatString The format string, eg from FormatRecord.getFormatString
* @see #isInternalDateFormat(int)
*/
public static boolean isADateFormat(int formatIndex, String formatString) {
// First up, is this an internal date format?
if(isInternalDateFormat(formatIndex)) {
return true;
}
// If we didn't get a real string, it can't be
if(formatString == null || formatString.length() == 0) {
return false;
}
String fs = formatString;
// Translate \- into just -, before matching
fs = fs.replaceAll("\\\\-","-");
// And \, into ,
fs = fs.replaceAll("\\\\,",",");
// And '\ ' into ' '
fs = fs.replaceAll("\\\\ "," ");
// If it end in ;@, that's some crazy dd/mm vs mm/dd
// switching stuff, which we can ignore
fs = fs.replaceAll(";@", "");
// If it starts with [$-...], then could be a date, but
// who knows what that starting bit is all about
fs = fs.replaceAll("^\\[\\$\\-.*?\\]", "");
// If it starts with something like [Black] or [Yellow],
// then it could be a date
fs = fs.replaceAll("^\\[[a-zA-Z]+\\]", "");
// Otherwise, check it's only made up, in any case, of:
// y m d h s - / , . :
// optionally followed by AM/PM
if(fs.matches("^[yYmMdDhHsS\\-/,. :]+[ampAMP/]*$")) {
return true;
}
return false;
// First up, is this an internal date format?
if(isInternalDateFormat(formatIndex)) {
return true;
}
// If we didn't get a real string, it can't be
if(formatString == null || formatString.length() == 0) {
return false;
}
String fs = formatString;
// Translate \- into just -, before matching
fs = fs.replaceAll("\\\\-","-");
// And \, into ,
fs = fs.replaceAll("\\\\,",",");
// And '\ ' into ' '
fs = fs.replaceAll("\\\\ "," ");
// If it end in ;@, that's some crazy dd/mm vs mm/dd
// switching stuff, which we can ignore
fs = fs.replaceAll(";@", "");
// If it starts with [$-...], then could be a date, but
// who knows what that starting bit is all about
fs = fs.replaceAll("^\\[\\$\\-.*?\\]", "");
// If it starts with something like [Black] or [Yellow],
// then it could be a date
fs = fs.replaceAll("^\\[[a-zA-Z]+\\]", "");
// Otherwise, check it's only made up, in any case, of:
// y m d h s - / , . :
// optionally followed by AM/PM
if(fs.matches("^[yYmMdDhHsS\\-/,. :]+[ampAMP/]*$")) {
return true;
}
return false;
}
/**
* Given a format ID this will check whether the format represents
* an internal excel date format or not.
* @see #isADateFormat(int, java.lang.String)
* @see #isADateFormat(int, java.lang.String)
*/
public static boolean isInternalDateFormat(int format) {
boolean retval =false;
switch(format) {
// Internal Date Formats as described on page 427 in
// Microsoft Excel Dev's Kit...
@ -261,27 +254,22 @@ public class HSSFDateUtil
case 0x2d:
case 0x2e:
case 0x2f:
retval = true;
break;
default:
retval = false;
break;
return true;
}
return retval;
return false;
}
/**
* Check if a cell contains a date
* Since dates are stored internally in Excel as double values
* we infer it is a date if it is formatted as such.
* Since dates are stored internally in Excel as double values
* we infer it is a date if it is formatted as such.
* @see #isADateFormat(int, String)
* @see #isInternalDateFormat(int)
*/
public static boolean isCellDateFormatted(HSSFCell cell) {
if (cell == null) return false;
boolean bDate = false;
double d = cell.getNumericCellValue();
if ( HSSFDateUtil.isValidExcelDate(d) ) {
HSSFCellStyle style = cell.getCellStyle();
@ -302,7 +290,7 @@ public class HSSFDateUtil
public static boolean isCellInternalDateFormatted(HSSFCell cell) {
if (cell == null) return false;
boolean bDate = false;
double d = cell.getNumericCellValue();
if ( HSSFDateUtil.isValidExcelDate(d) ) {
HSSFCellStyle style = cell.getCellStyle();
@ -344,7 +332,7 @@ public class HSSFDateUtil
*
* @return days number of days in years prior to yr.
* @param yr a year (1900 < yr < 4000)
* @param use1904windowing
* @param use1904windowing
* @exception IllegalArgumentException if year is outside of range.
*/
@ -353,16 +341,16 @@ public class HSSFDateUtil
if ((!use1904windowing && yr < 1900) || (use1904windowing && yr < 1900)) {
throw new IllegalArgumentException("'year' must be 1900 or greater");
}
int yr1 = yr - 1;
int leapDays = yr1 / 4 // plus julian leap days in prior years
- yr1 / 100 // minus prior century years
+ yr1 / 400 // plus years divisible by 400
+ yr1 / 400 // plus years divisible by 400
- 460; // leap days in previous 1900 years
return 365 * (yr - (use1904windowing ? 1904 : 1900)) + leapDays;
}
// set HH:MM:SS fields of cal to 00:00:00:000
private static Calendar dayStart(final Calendar cal)
{
@ -377,5 +365,95 @@ public class HSSFDateUtil
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.formula.Ptg;
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.Region;
import org.apache.poi.util.POILogFactory;
@ -382,7 +381,8 @@ public final class HSSFSheet {
}
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)
* @version %I%, %G%
*/
public class TestHSSFDateUtil extends TestCase {
public final class TestHSSFDateUtil extends TestCase {
public static final int CALENDAR_JANUARY = 0;
public static final int CALENDAR_FEBRUARY = 1;
@ -47,11 +47,6 @@ public class TestHSSFDateUtil extends TestCase {
public static final int CALENDAR_APRIL = 3;
public static final int CALENDAR_JULY = 6;
public static final int CALENDAR_OCTOBER = 9;
public TestHSSFDateUtil(String s)
{
super(s);
}
/**
* Checks the date conversion functions in the HSSFDateUtil class.
@ -193,14 +188,13 @@ public class TestHSSFDateUtil extends TestCase {
}
/**
* Tests that we deal with timezones properly
* Tests that we deal with time-zones properly
*/
public void testCalendarConversion() {
GregorianCalendar date = new GregorianCalendar(2002, 0, 1, 12, 1, 1);
Date expected = date.getTime();
double expectedExcel = HSSFDateUtil.getExcelDate(expected);
// Iteratating over the hours exposes any rounding issues.
// Iterating over the hours exposes any rounding issues.
for (int hour = -12; hour <= 12; hour++)
{
String id = "GMT" + (hour < 0 ? "" : "+") + hour + ":00";
@ -209,7 +203,7 @@ public class TestHSSFDateUtil extends TestCase {
double excelDate = HSSFDateUtil.getExcelDate(date, false);
Date javaDate = HSSFDateUtil.getJavaDate(excelDate);
// Should match despite timezone
// Should match despite time-zone
assertEquals("Checking timezone " + id, expected.getTime(), javaDate.getTime());
}
}
@ -402,7 +396,11 @@ public class TestHSSFDateUtil extends TestCase {
assertEquals(34519.0, HSSFDateUtil.getExcelDate(createDate(1998, CALENDAR_JULY, 5), true), 0.00001);
}
private Date createDate(int year, int month, int day) {
/**
* @param month zero based
* @param day one based
*/
private static Date createDate(int year, int month, int day) {
Calendar c = new GregorianCalendar();
c.set(year, month, day, 0, 0, 0);
c.set(Calendar.MILLISECOND, 0);
@ -420,10 +418,18 @@ public class TestHSSFDateUtil extends TestCase {
calendar = new GregorianCalendar(1901, 0, 1);
assertEquals("Checking absolute day (1 Jan 1901)", 366, HSSFDateUtil.absoluteDay(calendar, false));
}
public void testConvertTime() {
final double delta = 1E-7; // a couple of digits more accuracy than strictly required
assertEquals(0.5, HSSFDateUtil.convertTime("12:00"), delta);
assertEquals(2.0/3, HSSFDateUtil.convertTime("16:00"), delta);
assertEquals(0.0000116, HSSFDateUtil.convertTime("0:00:01"), delta);
assertEquals(0.7330440, HSSFDateUtil.convertTime("17:35:35"), delta);
}
public static void main(String [] args) {
System.out
.println("Testing org.apache.poi.hssf.usermodel.TestHSSFDateUtil");
junit.textui.TestRunner.run(TestHSSFDateUtil.class);
public void testParseDate() {
assertEquals(createDate(2008, Calendar.AUGUST, 3), HSSFDateUtil.parseYYYYMMDDDate("2008/08/03"));
assertEquals(createDate(1994, Calendar.MAY, 1), HSSFDateUtil.parseYYYYMMDDDate("1994/05/01"));
}
}