From: Josh Micich Date: Sun, 3 Aug 2008 22:11:26 +0000 (+0000) Subject: Extensive fixes for data validation (bug 44953) X-Git-Tag: REL_3_2_FINAL~222 X-Git-Url: https://source.dussan.org/?a=commitdiff_plain;h=9b67bb83a45e8bf7bd2b1c03ec933a628ddb7253;p=poi.git Extensive fixes for data validation (bug 44953) git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@682225 13f79535-47bb-0310-9956-ffa450edef68 --- diff --git a/src/documentation/content/xdocs/changes.xml b/src/documentation/content/xdocs/changes.xml index 3660da72e4..77f9608e60 100644 --- a/src/documentation/content/xdocs/changes.xml +++ b/src/documentation/content/xdocs/changes.xml @@ -37,6 +37,7 @@ + 44953 - Extensive fixes for data validation 45519 - Fixed to keep datavalidation records together Support for creating new HSLF CurrentUserAtoms 45466 - Partial support for removing excel comments (won't work for all excel versions yet) diff --git a/src/documentation/content/xdocs/status.xml b/src/documentation/content/xdocs/status.xml index 4927e7150d..ef4274eeac 100644 --- a/src/documentation/content/xdocs/status.xml +++ b/src/documentation/content/xdocs/status.xml @@ -34,6 +34,7 @@ + 44953 - Extensive fixes for data validation 45519 - Fixed to keep datavalidation records together Support for creating new HSLF CurrentUserAtoms 45466 - Partial support for removing excel comments (won't work for all excel versions yet) diff --git a/src/java/org/apache/poi/hssf/model/FormulaParser.java b/src/java/org/apache/poi/hssf/model/FormulaParser.java index 75b3aeb42e..7782d8ed45 100644 --- a/src/java/org/apache/poi/hssf/model/FormulaParser.java +++ b/src/java/org/apache/poi/hssf/model/FormulaParser.java @@ -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 */ diff --git a/src/java/org/apache/poi/hssf/model/OperandClassTransformer.java b/src/java/org/apache/poi/hssf/model/OperandClassTransformer.java index 07d2bd2fd6..9b5804f0c4 100644 --- a/src/java/org/apache/poi/hssf/model/OperandClassTransformer.java +++ b/src/java/org/apache/poi/hssf/model/OperandClassTransformer.java @@ -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"); diff --git a/src/java/org/apache/poi/hssf/record/DVRecord.java b/src/java/org/apache/poi/hssf/record/DVRecord.java index cbc732234f..a90730ee55 100644 --- a/src/java/org/apache/poi/hssf/record/DVRecord.java +++ b/src/java/org/apache/poi/hssf/record/DVRecord.java @@ -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)

@@ -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\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 true if in list validations the string list is explicitly given in the + * formula, false otherwise + */ + public boolean getListExplicitFormula() { + return (opt_string_list_formula.isSet(_option_flags)); + } + + /** + * @return true if empty values are allowed in cells, false otherwise + */ + public boolean getEmptyCellAllowed() { + return (opt_empty_cell_allowed.isSet(_option_flags)); + } + + + /** + * @return true if drop down arrow should be suppressed when list validation is + * used, false otherwise + */ + public boolean getSuppressDropdownArrow() { + return (opt_suppress_dropdown_arrow.isSet(_option_flags)); + } + + /** + * @return true if a prompt window should appear when cell is selected, false otherwise + */ + public boolean getShowPromptOnCellSelected() { + return (opt_show_prompt_on_cell_selected.isSet(_option_flags)); + } + + /** + * @return true if an error window should appear when an invalid value is entered + * in the cell, false otherwise + */ + public boolean getShowErrorOnInvalidValue() { + return (opt_show_error_on_invalid_value.isSet(_option_flags)); + } + + /** + * get the condition operator + * @return the condition operator + * @see org.apache.poi.hssf.util.HSSFDataValidation utility class + */ + public int getConditionOperator() { + return opt_condition_operator.getValue(_option_flags); + } + // <-- end option flags + + + + + public CellRangeAddressList getCellRangeAddress() { + return this._regions; + } + + + public String toString() { + StringBuffer sb = new StringBuffer(); + sb.append("[DV]\n"); + sb.append(" options=").append(Integer.toHexString(_option_flags)); + sb.append(" title-prompt=").append(formatTextTitle(_promptTitle)); + sb.append(" title-error=").append(formatTextTitle(_errorTitle)); + sb.append(" text-prompt=").append(formatTextTitle(_promptText)); + sb.append(" text-error=").append(formatTextTitle(_errorText)); + sb.append("\n"); + appendFormula(sb, "Formula 1:", _formula1); + appendFormula(sb, "Formula 2:", _formula2); + sb.append("Regions: "); + int nRegions = _regions.getADDRStructureNumber(); + for(int i=0; i0) { + 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("\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(); + } } diff --git a/src/java/org/apache/poi/hssf/record/aggregates/DataValidityTable.java b/src/java/org/apache/poi/hssf/record/aggregates/DataValidityTable.java index b3d2069ca9..133386d7df 100644 --- a/src/java/org/apache/poi/hssf/record/aggregates/DataValidityTable.java +++ b/src/java/org/apache/poi/hssf/record/aggregates/DataValidityTable.java @@ -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
@@ -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()); } diff --git a/src/java/org/apache/poi/hssf/record/formula/NumberPtg.java b/src/java/org/apache/poi/hssf/record/formula/NumberPtg.java index 02b0075011..84d4a1704d 100644 --- a/src/java/org/apache/poi/hssf/record/formula/NumberPtg.java +++ b/src/java/org/apache/poi/hssf/record/formula/NumberPtg.java @@ -36,7 +36,7 @@ public final class NumberPtg extends ScalarConstantPtg { /** Create a NumberPtg from a byte array read from disk */ public NumberPtg(RecordInputStream in) { - field_1_value = in.readDouble(); + this(in.readDouble()); } /** Create a NumberPtg from a string representation of the number @@ -45,9 +45,12 @@ public final class NumberPtg extends ScalarConstantPtg { * @param value : String representation of a floating point number */ public NumberPtg(String value) { - field_1_value = Double.parseDouble(value); + this(Double.parseDouble(value)); } + public NumberPtg(double value) { + field_1_value = value; + } public double getValue() { @@ -67,6 +70,15 @@ public final class NumberPtg extends ScalarConstantPtg { public String toFormulaString(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(); } } diff --git a/src/java/org/apache/poi/hssf/record/formula/Ptg.java b/src/java/org/apache/poi/hssf/record/formula/Ptg.java index 7a882e4ae0..d1ea411cb9 100644 --- a/src/java/org/apache/poi/hssf/record/formula/Ptg.java +++ b/src/java/org/apache/poi/hssf/record/formula/Ptg.java @@ -41,6 +41,7 @@ import org.apache.poi.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; diff --git a/src/java/org/apache/poi/hssf/usermodel/DVConstraint.java b/src/java/org/apache/poi/hssf/usermodel/DVConstraint.java new file mode 100644 index 0000000000..d4f147316b --- /dev/null +++ b/src/java/org/apache/poi/hssf/usermodel/DVConstraint.java @@ -0,0 +1,479 @@ +/* ==================================================================== + Copyright 2002-2004 Apache Software Foundation + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +==================================================================== */ + +package org.apache.poi.hssf.usermodel; + +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.util.Date; + +import org.apache.poi.hssf.model.FormulaParser; +import org.apache.poi.hssf.record.formula.NumberPtg; +import org.apache.poi.hssf.record.formula.Ptg; +import org.apache.poi.hssf.record.formula.StringPtg; + +/** + * + * @author Josh Micich + */ +public class DVConstraint { + /** + * ValidationType enum + */ + public static final class ValidationType { + private ValidationType() { + // no instances of this class + } + /** 'Any value' type - value not restricted */ + public static final int ANY = 0x00; + /** Integer ('Whole number') type */ + public static final int INTEGER = 0x01; + /** Decimal type */ + public static final int DECIMAL = 0x02; + /** List type ( combo box type ) */ + public static final int LIST = 0x03; + /** Date type */ + public static final int DATE = 0x04; + /** Time type */ + public static final int TIME = 0x05; + /** String length type */ + public static final int TEXT_LENGTH = 0x06; + /** Formula ( 'Custom' ) type */ + public static final int FORMULA = 0x07; + } + /** + * Condition operator enum + */ + public static final class OperatorType { + private OperatorType() { + // no instances of this class + } + + public static final int BETWEEN = 0x00; + public static final int NOT_BETWEEN = 0x01; + public static final int EQUAL = 0x02; + public static final int NOT_EQUAL = 0x03; + public static final int GREATER_THAN = 0x04; + public static final int LESS_THAN = 0x05; + public static final int GREATER_OR_EQUAL = 0x06; + public static final int LESS_OR_EQUAL = 0x07; + /** default value to supply when the operator type is not used */ + public static final int IGNORED = BETWEEN; + + /* package */ static void validateSecondArg(int comparisonOperator, String paramValue) { + switch (comparisonOperator) { + case BETWEEN: + case NOT_BETWEEN: + if (paramValue == null) { + throw new IllegalArgumentException("expr2 must be supplied for 'between' comparisons"); + } + // all other operators don't need second arg + } + } + } + + /* package */ static final class FormulaPair { + + private final Ptg[] _formula1; + private final Ptg[] _formula2; + + public FormulaPair(Ptg[] formula1, Ptg[] formula2) { + _formula1 = formula1; + _formula2 = formula2; + } + public Ptg[] getFormula1() { + return _formula1; + } + public Ptg[] getFormula2() { + return _formula2; + } + + } + + // convenient access to ValidationType namespace + private static final ValidationType VT = null; + + + private final int _validationType; + private int _operator; + private String[] _explicitListValues; + + private String _formula1; + private String _formula2; + private Double _value1; + private Double _value2; + + + private DVConstraint(int validationType, int comparisonOperator, String formulaA, + String formulaB, Double value1, Double value2, String[] excplicitListValues) { + _validationType = validationType; + _operator = comparisonOperator; + _formula1 = formulaA; + _formula2 = formulaB; + _value1 = value1; + _value2 = value2; + _explicitListValues = excplicitListValues; + } + + + /** + * Creates a list constraint + */ + private DVConstraint(String listFormula, String[] excplicitListValues) { + this(ValidationType.LIST, OperatorType.IGNORED, + listFormula, null, null, null, excplicitListValues); + } + + /** + * Creates a number based data validation constraint. The text values entered for expr1 and expr2 + * can be either standard Excel formulas or formatted number values. If the expression starts + * with '=' it is parsed as a formula, otherwise it is parsed as a formatted number. + * + * @param validationType one of {@link ValidationType#ANY}, {@link ValidationType#DECIMAL}, + * {@link ValidationType#INTEGER}, {@link ValidationType#TEXT_LENGTH} + * @param comparisonOperator any constant from {@link OperatorType} enum + * @param expr1 date formula (when first char is '=') or formatted number value + * @param expr2 date formula (when first char is '=') or formatted number value + */ + public static DVConstraint createNumericConstraint(int validationType, int comparisonOperator, + String expr1, String expr2) { + switch (validationType) { + case ValidationType.ANY: + if (expr1 != null || expr2 != null) { + throw new IllegalArgumentException("expr1 and expr2 must be null for validation type 'any'"); + } + break; + case ValidationType.DECIMAL: + case ValidationType.INTEGER: + case ValidationType.TEXT_LENGTH: + if (expr1 == null) { + throw new IllegalArgumentException("expr1 must be supplied"); + } + OperatorType.validateSecondArg(comparisonOperator, expr2); + break; + default: + throw new IllegalArgumentException("Validation Type (" + + validationType + ") not supported with this method"); + } + // formula1 and value1 are mutually exclusive + String formula1 = getFormulaFromTextExpression(expr1); + Double value1 = formula1 == null ? convertNumber(expr1) : null; + // formula2 and value2 are mutually exclusive + String formula2 = getFormulaFromTextExpression(expr2); + Double value2 = formula2 == null ? convertNumber(expr2) : null; + return new DVConstraint(validationType, comparisonOperator, formula1, formula2, value1, value2, null); + } + + public static DVConstraint createFormulaListConstraint(String listFormula) { + return new DVConstraint(listFormula, null); + } + public static DVConstraint createExplicitListConstraint(String[] explicitListValues) { + return new DVConstraint(null, explicitListValues); + } + + + /** + * Creates a time based data validation constraint. The text values entered for expr1 and expr2 + * can be either standard Excel formulas or formatted time values. If the expression starts + * with '=' it is parsed as a formula, otherwise it is parsed as a formatted time. To parse + * formatted times, two formats are supported: "HH:MM" or "HH:MM:SS". This is contrary to + * Excel which uses the default time format from the OS. + * + * @param comparisonOperator constant from {@link OperatorType} enum + * @param expr1 date formula (when first char is '=') or formatted time value + * @param expr2 date formula (when first char is '=') or formatted time value + */ + public static DVConstraint createTimeConstraint(int comparisonOperator, String expr1, String expr2) { + if (expr1 == null) { + throw new IllegalArgumentException("expr1 must be supplied"); + } + OperatorType.validateSecondArg(comparisonOperator, expr1); + + // formula1 and value1 are mutually exclusive + String formula1 = getFormulaFromTextExpression(expr1); + Double value1 = formula1 == null ? convertTime(expr1) : null; + // formula2 and value2 are mutually exclusive + String formula2 = getFormulaFromTextExpression(expr2); + Double value2 = formula2 == null ? convertTime(expr2) : null; + return new DVConstraint(VT.TIME, comparisonOperator, formula1, formula2, value1, value2, null); + + } + /** + * Creates a date based data validation constraint. The text values entered for expr1 and expr2 + * can be either standard Excel formulas or formatted date values. If the expression starts + * with '=' it is parsed as a formula, otherwise it is parsed as a formatted date (Excel uses + * the same convention). To parse formatted dates, a date format needs to be specified. This + * is contrary to Excel which uses the default short date format from the OS. + * + * @param comparisonOperator constant from {@link OperatorType} enum + * @param expr1 date formula (when first char is '=') or formatted date value + * @param expr2 date formula (when first char is '=') or formatted date value + * @param dateFormat ignored if both expr1 and expr2 are formulas. Default value is "YYYY/MM/DD" + * otherwise any other valid argument for SimpleDateFormat can be used + * @see SimpleDateFormat + */ + public static DVConstraint createDateConstraint(int comparisonOperator, String expr1, String expr2, String dateFormat) { + if (expr1 == null) { + throw new IllegalArgumentException("expr1 must be supplied"); + } + OperatorType.validateSecondArg(comparisonOperator, expr2); + SimpleDateFormat df = dateFormat == null ? null : new SimpleDateFormat(dateFormat); + + // formula1 and value1 are mutually exclusive + String formula1 = getFormulaFromTextExpression(expr1); + Double value1 = formula1 == null ? convertDate(expr1, df) : null; + // formula2 and value2 are mutually exclusive + String formula2 = getFormulaFromTextExpression(expr2); + Double value2 = formula2 == null ? convertDate(expr2, df) : null; + return new DVConstraint(VT.DATE, comparisonOperator, formula1, formula2, value1, value2, null); + } + + /** + * Distinguishes formula expressions from simple value expressions. This logic is only + * required by a few factory methods in this class that create data validation constraints + * from more or less the same parameters that would have been entered in the Excel UI. The + * data validation dialog box uses the convention that formulas begin with '='. Other methods + * in this class follow the POI convention (formulas and values are distinct), so the '=' + * convention is not used there. + * + * @param textExpr a formula or value expression + * @return all text after '=' if textExpr begins with '='. Otherwise null if textExpr does not begin with '=' + */ + private static String getFormulaFromTextExpression(String textExpr) { + if (textExpr == null) { + return null; + } + if (textExpr.length() < 1) { + throw new IllegalArgumentException("Empty string is not a valid formula/value expression"); + } + if (textExpr.charAt(0) == '=') { + return textExpr.substring(1); + } + return null; + } + + + /** + * @return null if numberStr is null + */ + private static Double convertNumber(String numberStr) { + if (numberStr == null) { + return null; + } + try { + return new Double(numberStr); + } catch (NumberFormatException e) { + throw new RuntimeException("The supplied text '" + numberStr + + "' could not be parsed as a number"); + } + } + + /** + * @return null if timeStr is null + */ + private static Double convertTime(String timeStr) { + if (timeStr == null) { + return null; + } + return new Double(HSSFDateUtil.convertTime(timeStr)); + } + /** + * @param dateFormat pass null for default YYYYMMDD + * @return null if timeStr is null + */ + private static Double convertDate(String dateStr, SimpleDateFormat dateFormat) { + if (dateStr == null) { + return null; + } + Date dateVal; + if (dateFormat == null) { + dateVal = HSSFDateUtil.parseYYYYMMDDDate(dateStr); + } else { + try { + dateVal = dateFormat.parse(dateStr); + } catch (ParseException e) { + throw new RuntimeException("Failed to parse date '" + dateStr + + "' using specified format '" + dateFormat + "'", e); + } + } + return new Double(HSSFDateUtil.getExcelDate(dateVal)); + } + + public static DVConstraint 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 null + */ + private static Ptg[] convertDoubleFormula(String formula, Double value, HSSFWorkbook workbook) { + if (formula == null) { + if (value == null) { + return Ptg.EMPTY_PTG_ARRAY; + } + return new Ptg[] { new NumberPtg(value.doubleValue()), }; + } + if (value != null) { + throw new IllegalStateException("Both formula and value cannot be present"); + } + return FormulaParser.parse(formula, workbook); + } + + + /** + * @return data validation type of this constraint + * @see ValidationType + */ + public int getValidationType() { + return _validationType; + } + /** + * Convenience method + * @return true if this constraint is a 'list' validation + */ + public boolean isListValidationType() { + return _validationType == VT.LIST; + } + /** + * Convenience method + * @return true if this constraint is a 'list' validation with explicit values + */ + public boolean isExplicitList() { + return _validationType == VT.LIST && _explicitListValues != null; + } + /** + * @return the operator used for this constraint + * @see OperatorType + */ + public int getOperator() { + return _operator; + } + /** + * Sets the comparison operator for this constraint + * @see OperatorType + */ + public void setOperator(int operator) { + _operator = operator; + } + + public String[] getExplicitListValues() { + return _explicitListValues; + } + public void setExplicitListValues(String[] explicitListValues) { + if (_validationType != VT.LIST) { + throw new RuntimeException("Cannot setExplicitListValues on non-list constraint"); + } + _formula1 = null; + _explicitListValues = explicitListValues; + } + + /** + * @return the formula for expression 1. May be null + */ + public String getFormula1() { + return _formula1; + } + /** + * Sets a formula for expression 1. + */ + public void setFormula1(String formula1) { + _value1 = null; + _explicitListValues = null; + _formula1 = formula1; + } + + /** + * @return the formula for expression 2. May be null + */ + public String getFormula2() { + return _formula2; + } + /** + * Sets a formula for expression 2. + */ + public void setFormula2(String formula2) { + _value2 = null; + _formula2 = formula2; + } + + /** + * @return the numeric value for expression 1. May be null + */ + public Double getValue1() { + return _value1; + } + /** + * Sets a numeric value for expression 1. + */ + public void setValue1(double value1) { + _formula1 = null; + _value1 = new Double(value1); + } + + /** + * @return the numeric value for expression 2. May be null + */ + public Double getValue2() { + return _value2; + } + /** + * Sets a numeric value for expression 2. + */ + public void setValue2(double value2) { + _formula2 = null; + _value2 = new Double(value2); + } +} diff --git a/src/java/org/apache/poi/hssf/usermodel/HSSFDataValidation.java b/src/java/org/apache/poi/hssf/usermodel/HSSFDataValidation.java new file mode 100644 index 0000000000..3be3843692 --- /dev/null +++ b/src/java/org/apache/poi/hssf/usermodel/HSSFDataValidation.java @@ -0,0 +1,235 @@ +/* ==================================================================== + Copyright 2002-2004 Apache Software Foundation + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +==================================================================== */ + +package org.apache.poi.hssf.usermodel; + +import org.apache.poi.hssf.record.DVRecord; +import org.apache.poi.hssf.usermodel.DVConstraint.FormulaPair; +import org.apache.poi.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 true if this object should treats empty as valid value , false + * otherwise + */ + public void setEmptyCellAllowed(boolean allowed) { + _emptyCellAllowed = allowed; + } + + /** + * Retrieve the settings for empty cells allowed + * + * @return True if this object should treats empty as valid value , false + * otherwise + */ + public boolean getEmptyCellAllowed() { + return _emptyCellAllowed; + } + + /** + * Useful for list validation objects . + * + * @param suppress + * True if a list should display the values into a drop down list , + * false otherwise . In other words , if a list should display + * the arrow sign on its right side + */ + public void setSuppressDropDownArrow(boolean suppress) { + _suppress_dropdown_arrow = suppress; + } + + /** + * Useful only list validation objects . This method always returns false if + * the object isn't a list validation object + * + * @return true if a list should display the values into a drop down list , + * false otherwise . + */ + public boolean getSuppressDropDownArrow() { + if (_constraint.isListValidationType()) { + return _suppress_dropdown_arrow; + } + return false; + } + + /** + * Sets the behaviour when a cell which belongs to this object is selected + * + * @param show true if an prompt box should be displayed , false otherwise + */ + public void setShowPromptBox(boolean show) { + _showPromptBox = show; + } + + /** + * @param show true if an prompt box should be displayed , false otherwise + */ + public boolean getShowPromptBox() { + return _showPromptBox; + } + + /** + * Sets the behaviour when an invalid value is entered + * + * @param show true if an error box should be displayed , false otherwise + */ + public void setShowErrorBox(boolean show) { + _showErrorBox = show; + } + + /** + * @return true if an error box should be displayed , false otherwise + */ + public boolean getShowErrorBox() { + return _showErrorBox; + } + + + /** + * Sets the title and text for the prompt box . Prompt box is displayed when + * the user selects a cell which belongs to this validation object . In + * order for a prompt box to be displayed you should also use method + * setShowPromptBox( boolean show ) + * + * @param title The prompt box's title + * @param text The prompt box's text + */ + public void createPromptBox(String title, String text) { + _prompt_title = title; + _prompt_text = text; + this.setShowPromptBox(true); + } + + /** + * @return Prompt box's title or null + */ + public String getPromptBoxTitle() { + return _prompt_title; + } + + /** + * @return Prompt box's text or null + */ + public String getPromptBoxText() { + return _prompt_text; + } + + /** + * Sets the title and text for the error box . Error box is displayed when + * the user enters an invalid value int o a cell which belongs to this + * validation object . In order for an error box to be displayed you should + * also use method setShowErrorBox( boolean show ) + * + * @param title The error box's title + * @param text The error box's text + */ + public void createErrorBox(String title, String text) { + _error_title = title; + _error_text = text; + this.setShowErrorBox(true); + } + + /** + * @return Error box's title or null + */ + public String getErrorBoxTitle() { + return _error_title; + } + + /** + * @return Error box's text or null + */ + public String getErrorBoxText() { + return _error_text; + } + + public DVRecord createDVRecord(HSSFWorkbook workbook) { + + FormulaPair fp = _constraint.createFormulas(workbook); + + return new DVRecord(_constraint.getValidationType(), + _constraint.getOperator(), + _errorStyle, _emptyCellAllowed, getSuppressDropDownArrow(), + _constraint.isExplicitList(), + _showPromptBox, _prompt_title, _prompt_text, + _showErrorBox, _error_title, _error_text, + fp.getFormula1(), fp.getFormula2(), + _regions); + } +} \ No newline at end of file diff --git a/src/java/org/apache/poi/hssf/usermodel/HSSFDateUtil.java b/src/java/org/apache/poi/hssf/usermodel/HSSFDateUtil.java index ab353ca02c..457f0b9ed1 100644 --- a/src/java/org/apache/poi/hssf/usermodel/HSSFDateUtil.java +++ b/src/java/org/apache/poi/hssf/usermodel/HSSFDateUtil.java @@ -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 * Europe/Copenhagen, on 2004-03-28 the minute after * 01:59 CET is 03:00 CEST, if the excel date represents a time between * 02:00 and 03:00 then it is converted to past 03:00 summer time - * + * * @param date The Excel date. * @return Java representation of the date, or null if date is not a valid Excel date * @see java.util.TimeZone */ public static Date getJavaDate(double date) { - return getJavaDate(date, false); + return getJavaDate(date, false); } /** * Given an Excel date with either 1900 or 1904 date windowing, @@ -158,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; + } } diff --git a/src/java/org/apache/poi/hssf/usermodel/HSSFSheet.java b/src/java/org/apache/poi/hssf/usermodel/HSSFSheet.java index 9b6c206190..a99e48b64e 100644 --- a/src/java/org/apache/poi/hssf/usermodel/HSSFSheet.java +++ b/src/java/org/apache/poi/hssf/usermodel/HSSFSheet.java @@ -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); } diff --git a/src/java/org/apache/poi/hssf/util/CellRangeAddress.java b/src/java/org/apache/poi/hssf/util/CellRangeAddress.java new file mode 100644 index 0000000000..2f5d24aa02 --- /dev/null +++ b/src/java/org/apache/poi/hssf/util/CellRangeAddress.java @@ -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; + } +} \ No newline at end of file diff --git a/src/java/org/apache/poi/hssf/util/CellRangeAddressList.java b/src/java/org/apache/poi/hssf/util/CellRangeAddressList.java new file mode 100644 index 0000000000..d52219769d --- /dev/null +++ b/src/java/org/apache/poi/hssf/util/CellRangeAddressList.java @@ -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. + *

+ * + * @author Dragos Buleandra (dragos.buleandra@trade2b.ro) + */ +public final class CellRangeAddressList { + + /** + * List of CellRangeAddresses. Each structure represents a cell range + */ + private final List _list; + + public CellRangeAddressList() { + _list = new ArrayList(); + } + /** + * Convenience constructor for creating a CellRangeAddressList with a single + * CellRangeAddress. Other CellRangeAddresses 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 CellRangeAddress 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()); + } +} diff --git a/src/java/org/apache/poi/hssf/util/HSSFCellRangeAddress.java b/src/java/org/apache/poi/hssf/util/HSSFCellRangeAddress.java deleted file mode 100644 index 73804966fb..0000000000 --- a/src/java/org/apache/poi/hssf/util/HSSFCellRangeAddress.java +++ /dev/null @@ -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; - -/** - *

Title: HSSFCellRangeAddress

- *

Description: - * Implementation of the cell range address lists,like is described in - * OpenOffice.org's Excel Documentation . - * In BIFF8 there is a common way to store absolute cell range address - * lists in several records (not formulas). A cell range address list - * consists of a field with the number of ranges and the list of the range - * addresses. Each cell range address (called an ADDR structure) contains - * 4 16-bit-values.

- *

Copyright: Copyright (c) 2004

- *

Company:

- * @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; - } - } -} - - diff --git a/src/java/org/apache/poi/hssf/util/HSSFDataValidation.java b/src/java/org/apache/poi/hssf/util/HSSFDataValidation.java deleted file mode 100644 index 4d8f48b929..0000000000 --- a/src/java/org/apache/poi/hssf/util/HSSFDataValidation.java +++ /dev/null @@ -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; - -/** - *

Title: HSSFDataValidation

- *

Description: Utilty class for creating data validation cells

- *

Copyright: Copyright (c) 2004

- *

Company:

- * @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; - } - -} \ No newline at end of file diff --git a/src/testcases/org/apache/poi/hssf/usermodel/TestDataValidation.java b/src/testcases/org/apache/poi/hssf/usermodel/TestDataValidation.java index 8c43fa74eb..b588c6e6cc 100644 --- a/src/testcases/org/apache/poi/hssf/usermodel/TestDataValidation.java +++ b/src/testcases/org/apache/poi/hssf/usermodel/TestDataValidation.java @@ -21,7 +21,8 @@ import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; -import java.text.SimpleDateFormat; +import java.io.InputStream; +import java.io.PrintStream; import junit.framework.AssertionFailedError; import junit.framework.TestCase; @@ -31,877 +32,537 @@ import org.apache.poi.hssf.eventmodel.ERFListener; import org.apache.poi.hssf.eventmodel.EventRecordFactory; import org.apache.poi.hssf.record.DVRecord; import org.apache.poi.hssf.record.RecordFormatException; +import org.apache.poi.hssf.util.CellRangeAddressList; import org.apache.poi.hssf.util.HSSFColor; -import org.apache.poi.hssf.util.HSSFDataValidation; import org.apache.poi.hssf.util.Region; import org.apache.poi.poifs.filesystem.POIFSFileSystem; /** - *

Title: TestDataValidation

- *

Description: Class for testing Excel's data validation mechanism - * Second test : - * - - *

+ * Class for testing Excel's data validation mechanism + * * @author Dragos Buleandra ( dragos.buleandra@trade2b.ro ) */ -public class TestDataValidation extends TestCase -{ - - public void testDataValidation() throws Exception - { - System.out.println("\nTest no. 2 - Test Excel's Data validation mechanism"); - String resultFile = System.getProperty("java.io.tmpdir")+File.separator+"TestDataValidation.xls"; - HSSFWorkbook wb = new HSSFWorkbook(); - - HSSFCellStyle style_1 = this.createStyle( wb, HSSFCellStyle.ALIGN_LEFT ); - HSSFCellStyle style_2 = this.createStyle( wb, HSSFCellStyle.ALIGN_CENTER ); - HSSFCellStyle style_3 = this.createStyle( wb, HSSFCellStyle.ALIGN_CENTER, HSSFColor.GREY_25_PERCENT.index, true ); - HSSFCellStyle style_4 = this.createHeaderStyle(wb); - HSSFDataValidation data_validation = null; - - //data validation's number types - System.out.print(" Create sheet for Data Validation's number types ... "); - HSSFSheet fSheet = wb.createSheet("Number types"); - - //"Whole number" validation type - this.createDVTypeRow( wb, 0, style_3, "Whole number"); - this.createHeaderRow( wb, 0, style_4 ); - - short start_row = (short)fSheet.getPhysicalNumberOfRows(); - data_validation = new HSSFDataValidation((short)(start_row),(short)0,(short)(start_row),(short)0); - data_validation.setDataValidationType(HSSFDataValidation.DATA_TYPE_INTEGER); - data_validation.setOperator(HSSFDataValidation.OPERATOR_BETWEEN); - data_validation.setFirstFormula("2"); - data_validation.setSecondFormula("6"); - data_validation.createErrorBox("Invalid input !", "Something is wrong ; check condition !"); - data_validation.createPromptBox("Hi , dear user !", "So , you just selected me ! Thanks !"); - fSheet.addValidationData(data_validation); - this.writeDataValidationSettings( fSheet, style_1, style_2, "Between 2 and 6 ", true, true, true ); - this.writeOtherSettings( fSheet, style_1, "Error box type = STOP" ); - - data_validation.setFirstRow((short)(start_row+1)); - data_validation.setLastRow((short)(start_row+1)); - data_validation.setEmptyCellAllowed(false); - data_validation.setOperator(HSSFDataValidation.OPERATOR_NOT_BETWEEN); - data_validation.setErrorStyle(HSSFDataValidation.ERROR_STYLE_INFO); - fSheet.addValidationData(data_validation); - this.writeDataValidationSettings( fSheet, style_1, style_2, "Not between 2 and 6 ", false, true, true ); - this.writeOtherSettings( fSheet, style_1, "Error box type = INFO" ); - - data_validation.setFirstRow((short)(start_row+2)); - data_validation.setLastRow((short)(start_row+2)); - data_validation.setEmptyCellAllowed(false); - data_validation.setShowPromptBox(false); - data_validation.setFirstFormula("3"); - data_validation.setSecondFormula(null); - data_validation.setOperator(HSSFDataValidation.OPERATOR_EQUAL); - data_validation.setErrorStyle(HSSFDataValidation.ERROR_STYLE_WARNING); - fSheet.addValidationData(data_validation); - this.writeDataValidationSettings( fSheet, style_1, style_2, "Equal to 3", false, false, true ); - this.writeOtherSettings( fSheet, style_1, "Error box type = WARNING" ); - - data_validation.setFirstRow((short)(start_row+3)); - data_validation.setLastRow((short)(start_row+3)); - data_validation.setEmptyCellAllowed(false); - data_validation.setShowPromptBox(false); - data_validation.setShowErrorBox(false); - data_validation.setFirstFormula("3"); - data_validation.setSecondFormula(null); - data_validation.setOperator(HSSFDataValidation.OPERATOR_NOT_EQUAL); - fSheet.addValidationData(data_validation); - this.writeDataValidationSettings( fSheet, style_1, style_2, "Not equal to 3", false, false, false ); - this.writeOtherSettings( fSheet, style_1, "-" ); - - data_validation.setFirstRow((short)(start_row+4)); - data_validation.setLastRow((short)(start_row+4)); - data_validation.setEmptyCellAllowed(true); - data_validation.setShowPromptBox(false); - data_validation.setShowErrorBox(false); - data_validation.setFirstFormula("3"); - data_validation.setSecondFormula(null); - data_validation.setOperator(HSSFDataValidation.OPERATOR_GREATER_THAN); - fSheet.addValidationData(data_validation); - this.writeDataValidationSettings( fSheet, style_1, style_2, "Greater than 3", true, false, false ); - this.writeOtherSettings( fSheet, style_1, "-" ); - - data_validation.setFirstRow((short)(start_row+5)); - data_validation.setLastRow((short)(start_row+5)); - data_validation.setEmptyCellAllowed(true); - data_validation.setShowPromptBox(true); - data_validation.setShowErrorBox(false); - data_validation.setFirstFormula("3"); - data_validation.setSecondFormula(null); - data_validation.setOperator(HSSFDataValidation.OPERATOR_LESS_THAN); - fSheet.addValidationData(data_validation); - this.writeDataValidationSettings( fSheet, style_1, style_2, "Less than 3", true, true, false ); - this.writeOtherSettings( fSheet, style_1, "-" ); - - data_validation.setFirstRow((short)(start_row+6)); - data_validation.setLastRow((short)(start_row+6)); - data_validation.setEmptyCellAllowed(true); - data_validation.setShowPromptBox(false); - data_validation.setErrorStyle(HSSFDataValidation.ERROR_STYLE_STOP); - data_validation.setShowErrorBox(true); - data_validation.setFirstFormula("4"); - data_validation.setSecondFormula(null); - data_validation.setOperator(HSSFDataValidation.OPERATOR_GREATER_OR_EQUAL); - fSheet.addValidationData(data_validation); - this.writeDataValidationSettings( fSheet, style_1, style_2, "Greater than or equal to 4", true, false, true ); - this.writeOtherSettings( fSheet, style_1, "Error box type = STOP" ); - - data_validation.setFirstRow((short)(start_row+7)); - data_validation.setLastRow((short)(start_row+7)); - data_validation.setEmptyCellAllowed(false); - data_validation.setShowPromptBox(true); - data_validation.setShowErrorBox(false); - data_validation.setFirstFormula("4"); - data_validation.setSecondFormula(null); - data_validation.setOperator(HSSFDataValidation.OPERATOR_LESS_OR_EQUAL); - fSheet.addValidationData(data_validation); - this.writeDataValidationSettings( fSheet, style_1, style_2, "Less than or equal to 4", false, true, false ); - this.writeOtherSettings( fSheet, style_1, "-" ); - - //"Decimal" validation type - this.createDVTypeRow( wb, 0, style_3, "Decimal"); - this.createHeaderRow( wb, 0, style_4 ); - - start_row += (short)(8+4); - data_validation = new HSSFDataValidation((short)(start_row),(short)0,(short)(start_row),(short)0); - data_validation.setDataValidationType(HSSFDataValidation.DATA_TYPE_DECIMAL); - data_validation.setOperator(HSSFDataValidation.OPERATOR_BETWEEN); - data_validation.setFirstFormula("2"); - data_validation.setSecondFormula("6"); - data_validation.createErrorBox("Invalid input !", "Something is wrong ; check condition !"); - data_validation.createPromptBox("Hi , dear user !", "So , you just selected me ! Thanks !"); - fSheet.addValidationData(data_validation); - this.writeDataValidationSettings( fSheet, style_1, style_2, "Between 2 and 6 ", true, true, true ); - this.writeOtherSettings( fSheet, style_1, "Error box type = STOP" ); - - data_validation.setFirstRow((short)(start_row+1)); - data_validation.setLastRow((short)(start_row+1)); - data_validation.setEmptyCellAllowed(false); - data_validation.setOperator(HSSFDataValidation.OPERATOR_NOT_BETWEEN); - data_validation.setErrorStyle(HSSFDataValidation.ERROR_STYLE_INFO); - fSheet.addValidationData(data_validation); - this.writeDataValidationSettings( fSheet, style_1, style_2, "Not between 2 and 6 ", false, true, true ); - this.writeOtherSettings( fSheet, style_1, "Error box type = INFO" ); - - data_validation.setFirstRow((short)(start_row+2)); - data_validation.setLastRow((short)(start_row+2)); - data_validation.setEmptyCellAllowed(false); - data_validation.setShowPromptBox(false); - data_validation.setFirstFormula("3"); - data_validation.setSecondFormula(null); - data_validation.setOperator(HSSFDataValidation.OPERATOR_EQUAL); - data_validation.setErrorStyle(HSSFDataValidation.ERROR_STYLE_WARNING); - fSheet.addValidationData(data_validation); - this.writeDataValidationSettings( fSheet, style_1, style_2, "Equal to 3", false, false, true ); - this.writeOtherSettings( fSheet, style_1, "Error box type = WARNING" ); - - data_validation.setFirstRow((short)(start_row+3)); - data_validation.setLastRow((short)(start_row+3)); - data_validation.setEmptyCellAllowed(false); - data_validation.setShowPromptBox(false); - data_validation.setShowErrorBox(false); - data_validation.setFirstFormula("3"); - data_validation.setSecondFormula(null); - data_validation.setOperator(HSSFDataValidation.OPERATOR_NOT_EQUAL); - fSheet.addValidationData(data_validation); - this.writeDataValidationSettings( fSheet, style_1, style_2, "Not equal to 3", false, false, false ); - this.writeOtherSettings( fSheet, style_1, "-" ); - - data_validation.setFirstRow((short)(start_row+4)); - data_validation.setLastRow((short)(start_row+4)); - data_validation.setEmptyCellAllowed(true); - data_validation.setShowPromptBox(false); - data_validation.setShowErrorBox(false); - data_validation.setFirstFormula("3"); - data_validation.setSecondFormula(null); - data_validation.setOperator(HSSFDataValidation.OPERATOR_GREATER_THAN); - fSheet.addValidationData(data_validation); - this.writeDataValidationSettings( fSheet, style_1, style_2, "Greater than 3", true, false, false ); - this.writeOtherSettings( fSheet, style_1, "-" ); - - data_validation.setFirstRow((short)(start_row+5)); - data_validation.setLastRow((short)(start_row+5)); - data_validation.setEmptyCellAllowed(true); - data_validation.setShowPromptBox(true); - data_validation.setShowErrorBox(false); - data_validation.setFirstFormula("3"); - data_validation.setSecondFormula(null); - data_validation.setOperator(HSSFDataValidation.OPERATOR_LESS_THAN); - fSheet.addValidationData(data_validation); - this.writeDataValidationSettings( fSheet, style_1, style_2, "Less than 3", true, true, false ); - this.writeOtherSettings( fSheet, style_1, "-" ); - - data_validation.setFirstRow((short)(start_row+6)); - data_validation.setLastRow((short)(start_row+6)); - data_validation.setEmptyCellAllowed(true); - data_validation.setShowPromptBox(false); - data_validation.setErrorStyle(HSSFDataValidation.ERROR_STYLE_STOP); - data_validation.setShowErrorBox(true); - data_validation.setFirstFormula("4"); - data_validation.setSecondFormula(null); - data_validation.setOperator(HSSFDataValidation.OPERATOR_GREATER_OR_EQUAL); - fSheet.addValidationData(data_validation); - this.writeDataValidationSettings( fSheet, style_1, style_2, "Greater than or equal to 4", true, false, true ); - this.writeOtherSettings( fSheet, style_1, "Error box type = STOP" ); - - data_validation.setFirstRow((short)(start_row+7)); - data_validation.setLastRow((short)(start_row+7)); - data_validation.setEmptyCellAllowed(false); - data_validation.setShowPromptBox(true); - data_validation.setShowErrorBox(false); - data_validation.setFirstFormula("4"); - data_validation.setSecondFormula(null); - data_validation.setOperator(HSSFDataValidation.OPERATOR_LESS_OR_EQUAL); - fSheet.addValidationData(data_validation); - this.writeDataValidationSettings( fSheet, style_1, style_2, "Less than or equal to 4", false, true, false ); - this.writeOtherSettings( fSheet, style_1, "-" ); - - System.out.println("done !"); - - //"List" Data Validation type - /** @todo List*/ - System.out.print(" Create sheet for 'List' Data Validation type ... "); - fSheet = wb.createSheet("Lists"); - - this.createDVTypeRow( wb, 1, style_3, "Explicit lists - list items are explicitly provided"); - this.createDVDeescriptionRow( wb, 1, style_3, "Disadvantage - sum of item's length should be less than 255 characters"); - this.createHeaderRow( wb, 1, style_4 ); - - start_row = (short)fSheet.getPhysicalNumberOfRows(); - data_validation = new HSSFDataValidation((short)(start_row),(short)0,(short)(start_row),(short)0); - data_validation.setDataValidationType(HSSFDataValidation.DATA_TYPE_LIST); - data_validation.setFirstFormula("1+2+3"); - data_validation.setSecondFormula(null); - data_validation.setSurppressDropDownArrow(false); - data_validation.createErrorBox("Invalid input !", "Something is wrong ; check condition !"); - data_validation.createPromptBox("Hi , dear user !", "So , you just selected me ! Thanks !"); - fSheet.addValidationData(data_validation); - this.writeDataValidationSettings( fSheet, style_1, style_2, "POIFS,HSSF,HWPF,HPSF", true, true, true ); - this.writeOtherSettings( fSheet, style_1, "Error box type=STOP ; In-cell dropdown=yes" ); - - data_validation = new HSSFDataValidation((short)(start_row+1),(short)0,(short)(start_row+1),(short)0); - data_validation.setDataValidationType(HSSFDataValidation.DATA_TYPE_LIST); - data_validation.setFirstFormula("4+5+6+7"); - data_validation.setSecondFormula(null); - data_validation.setSurppressDropDownArrow(false); - data_validation.setEmptyCellAllowed(false); - data_validation.setShowPromptBox(false); - data_validation.createErrorBox("Invalid input !", "Something is wrong ; check condition !"); - fSheet.addValidationData(data_validation); - this.writeDataValidationSettings( fSheet, style_1, style_2, "POIFS,HSSF,HWPF,HPSF", false, false, true ); - this.writeOtherSettings( fSheet, style_1, "Error box type=STOP ; In-cell dropdown=yes" ); - - data_validation = new HSSFDataValidation((short)(start_row+2),(short)0,(short)(start_row+2),(short)0); - data_validation.setDataValidationType(HSSFDataValidation.DATA_TYPE_LIST); - data_validation.setFirstFormula("7+21"); - data_validation.setSecondFormula(null); - data_validation.setSurppressDropDownArrow(true); - data_validation.createErrorBox("Invalid input !", "Something is wrong ; check condition !"); - data_validation.createPromptBox("Hi , dear user !", "So , you just selected me ! Thanks !"); - fSheet.addValidationData(data_validation); - this.writeDataValidationSettings( fSheet, style_1, style_2, "POIFS,HSSF,HWPF,HPSF", true, true, true ); - this.writeOtherSettings( fSheet, style_1, "Error box type=STOP ; In-cell dropdown=no" ); - - data_validation = new HSSFDataValidation((short)(start_row+3),(short)0,(short)(start_row+3),(short)0); - data_validation.setDataValidationType(HSSFDataValidation.DATA_TYPE_LIST); - data_validation.setFirstFormula("8/2"); - data_validation.setSecondFormula(null); - data_validation.setSurppressDropDownArrow(true); - data_validation.createErrorBox("Invalid input !", "Something is wrong ; check condition !"); - data_validation.setEmptyCellAllowed(false); - data_validation.setShowPromptBox(false); - fSheet.addValidationData(data_validation); - this.writeDataValidationSettings( fSheet, style_1, style_2, "POIFS,HSSF,HWPF,HPSF", false, false, true ); - this.writeOtherSettings( fSheet, style_1, "Error box type=STOP ; In-cell dropdown=no" ); - - this.createDVTypeRow( wb, 1, style_3, "Reference lists - list items are taken from others cells"); - this.createDVDeescriptionRow( wb, 1, style_3, "Advantage - no restriction regarding the sum of item's length"); - this.createHeaderRow( wb, 1, style_4 ); - - start_row += (short)(4+5); - String cellStrValue = "a b c d e f g h i j k l m n o p r s t u v x y z w 0 1 2 3 4 "+ - "a b c d e f g h i j k l m n o p r s t u v x y z w 0 1 2 3 4 "+ - "a b c d e f g h i j k l m n o p r s t u v x y z w 0 1 2 3 4 "+ - "a b c d e f g h i j k l m n o p r s t u v x y z w 0 1 2 3 4 "; - - String strFormula = "$A$100:$A$120"; - data_validation = new HSSFDataValidation((short)(start_row),(short)0,(short)(start_row),(short)0); - data_validation.setDataValidationType(HSSFDataValidation.DATA_TYPE_LIST); - data_validation.setFirstFormula(strFormula); - data_validation.setSecondFormula(null); - data_validation.setSurppressDropDownArrow(false); - data_validation.createErrorBox("Invalid input !", "Something is wrong ; check condition !"); - data_validation.createPromptBox("Hi , dear user !", "So , you just selected me ! Thanks !"); - fSheet.addValidationData(data_validation); - this.writeDataValidationSettings( fSheet, style_1, style_2, strFormula, true, true, true ); - this.writeOtherSettings( fSheet, style_1, "Error box type=STOP ; In-cell dropdown=yes" ); - - data_validation = new HSSFDataValidation((short)(start_row+1),(short)0,(short)(start_row+1),(short)0); - data_validation.setDataValidationType(HSSFDataValidation.DATA_TYPE_LIST); - data_validation.setFirstFormula(strFormula); - data_validation.setSecondFormula(null); - data_validation.setSurppressDropDownArrow(false); - data_validation.setEmptyCellAllowed(false); - data_validation.setShowPromptBox(false); - data_validation.createErrorBox("Invalid input !", "Something is wrong ; check condition !"); - fSheet.addValidationData(data_validation); - this.writeDataValidationSettings( fSheet, style_1, style_2, strFormula, false, false, true ); - this.writeOtherSettings( fSheet, style_1, "Error box type=STOP ; In-cell dropdown=yes" ); - - data_validation = new HSSFDataValidation((short)(start_row+2),(short)0,(short)(start_row+2),(short)0); - data_validation.setDataValidationType(HSSFDataValidation.DATA_TYPE_LIST); - data_validation.setFirstFormula(strFormula); - data_validation.setSecondFormula(null); - data_validation.setSurppressDropDownArrow(true); - data_validation.createErrorBox("Invalid input !", "Something is wrong ; check condition !"); - data_validation.createPromptBox("Hi , dear user !", "So , you just selected me ! Thanks !"); - fSheet.addValidationData(data_validation); - this.writeDataValidationSettings( fSheet, style_1, style_2, strFormula, true, true, true ); - this.writeOtherSettings( fSheet, style_1, "Error box type=STOP ; In-cell dropdown=no" ); - - data_validation = new HSSFDataValidation((short)(start_row+3),(short)0,(short)(start_row+3),(short)0); - data_validation.setDataValidationType(HSSFDataValidation.DATA_TYPE_LIST); - data_validation.setFirstFormula(strFormula); - data_validation.setSecondFormula(null); - data_validation.setSurppressDropDownArrow(true); - data_validation.createErrorBox("Invalid input !", "Something is wrong ; check condition !"); - data_validation.setEmptyCellAllowed(false); - data_validation.setShowPromptBox(false); - fSheet.addValidationData(data_validation); - this.writeDataValidationSettings( fSheet, style_1, style_2, strFormula, false, false, true ); - this.writeOtherSettings( fSheet, style_1, "Error box type=STOP ; In-cell dropdown=no" ); - - for (int i=100; i<=120; i++) - { - HSSFRow currRow = fSheet.createRow(i); - currRow.createCell((short)0).setCellValue(cellStrValue); -// currRow.hide( true ); - } - - System.out.println("done !"); - - //Date/Time Validation type - System.out.print(" Create sheet for 'Date' and 'Time' Data Validation types ... "); - fSheet = wb.createSheet("Date_Time"); - SimpleDateFormat df = new SimpleDateFormat("m/d/yyyy"); - HSSFDataFormat dataFormat = wb.createDataFormat(); - short fmtDate = dataFormat.getFormat("m/d/yyyy"); - short fmtTime = dataFormat.getFormat("h:mm"); - HSSFCellStyle cellStyle_data = wb.createCellStyle(); - cellStyle_data.setDataFormat(fmtDate); - HSSFCellStyle cellStyle_time = wb.createCellStyle(); - cellStyle_time.setDataFormat(fmtTime); - - this.createDVTypeRow( wb, 2, style_3, "Date ( cells are already formated as date - m/d/yyyy)"); - this.createHeaderRow( wb, 2, style_4 ); - - start_row = (short)fSheet.getPhysicalNumberOfRows(); - data_validation = new HSSFDataValidation((short)(start_row),(short)0,(short)(start_row),(short)0); - data_validation.setDataValidationType(HSSFDataValidation.DATA_TYPE_DATE); - data_validation.setOperator(HSSFDataValidation.OPERATOR_BETWEEN); - - data_validation.setFirstFormula( String.valueOf((int)HSSFDateUtil.getExcelDate(df.parse("1/2/2004"))) ); - data_validation.setSecondFormula( String.valueOf((int)HSSFDateUtil.getExcelDate(df.parse("1/6/2004"))) ); - - data_validation.createErrorBox("Invalid input !", "Something is wrong ; check condition !"); - data_validation.createPromptBox("Hi , dear user !", "So , you just selected me ! Thanks !"); - fSheet.addValidationData(data_validation); - this.writeDataValidationSettings( fSheet, style_1, style_2, "Between 1/2/2004 and 1/6/2004 ", true, true, true ); - this.setCellFormat( fSheet, cellStyle_data ); - this.writeOtherSettings( fSheet, style_1, "Error box type = STOP" ); - - data_validation.setFirstRow((short)(start_row+1)); - data_validation.setLastRow((short)(start_row+1)); - data_validation.setEmptyCellAllowed(false); - data_validation.setOperator(HSSFDataValidation.OPERATOR_NOT_BETWEEN); - data_validation.setErrorStyle(HSSFDataValidation.ERROR_STYLE_INFO); - fSheet.addValidationData(data_validation); - this.writeDataValidationSettings( fSheet, style_1, style_2, "Not between 1/2/2004 and 1/6/2004 ", false, true, true ); - this.setCellFormat( fSheet, cellStyle_data ); - this.writeOtherSettings( fSheet, style_1, "Error box type = INFO" ); - - data_validation.setFirstRow((short)(start_row+2)); - data_validation.setLastRow((short)(start_row+2)); - data_validation.setEmptyCellAllowed(false); - data_validation.setShowPromptBox(false); - data_validation.setFirstFormula(String.valueOf((int)HSSFDateUtil.getExcelDate(df.parse("3/2/2004")))); - data_validation.setSecondFormula(null); - data_validation.setOperator(HSSFDataValidation.OPERATOR_EQUAL); - data_validation.setErrorStyle(HSSFDataValidation.ERROR_STYLE_WARNING); - fSheet.addValidationData(data_validation); - this.writeDataValidationSettings( fSheet, style_1, style_2, "Equal to 3/2/2004", false, false, true ); - this.setCellFormat( fSheet, cellStyle_data ); - this.writeOtherSettings( fSheet, style_1, "Error box type = WARNING" ); - - data_validation.setFirstRow((short)(start_row+3)); - data_validation.setLastRow((short)(start_row+3)); - data_validation.setEmptyCellAllowed(false); - data_validation.setShowPromptBox(false); - data_validation.setShowErrorBox(false); - data_validation.setFirstFormula(String.valueOf((int)HSSFDateUtil.getExcelDate(df.parse("3/2/2004")))); - data_validation.setSecondFormula(null); - data_validation.setOperator(HSSFDataValidation.OPERATOR_NOT_EQUAL); - fSheet.addValidationData(data_validation); - this.writeDataValidationSettings( fSheet, style_1, style_2, "Not equal to 3/2/2004", false, false, false ); - this.setCellFormat( fSheet, cellStyle_data ); - this.writeOtherSettings( fSheet, style_1, "-" ); - - data_validation.setFirstRow((short)(start_row+4)); - data_validation.setLastRow((short)(start_row+4)); - data_validation.setEmptyCellAllowed(true); - data_validation.setShowPromptBox(false); - data_validation.setShowErrorBox(false); - data_validation.setFirstFormula(String.valueOf((int)HSSFDateUtil.getExcelDate(df.parse("3/2/2004")))); - data_validation.setSecondFormula(null); - data_validation.setOperator(HSSFDataValidation.OPERATOR_GREATER_THAN); - fSheet.addValidationData(data_validation); - this.writeDataValidationSettings( fSheet, style_1, style_2, "Greater than 3/2/2004", true, false, false ); - this.setCellFormat( fSheet, cellStyle_data ); - this.writeOtherSettings( fSheet, style_1, "-" ); - - data_validation.setFirstRow((short)(start_row+5)); - data_validation.setLastRow((short)(start_row+5)); - data_validation.setEmptyCellAllowed(true); - data_validation.setShowPromptBox(true); - data_validation.setShowErrorBox(false); - data_validation.setFirstFormula(String.valueOf((int)HSSFDateUtil.getExcelDate(df.parse("3/2/2004")))); - data_validation.setSecondFormula(null); - data_validation.setOperator(HSSFDataValidation.OPERATOR_LESS_THAN); - fSheet.addValidationData(data_validation); - this.writeDataValidationSettings( fSheet, style_1, style_2, "Less than 3/2/2004", true, true, false ); - this.setCellFormat( fSheet, cellStyle_data ); - this.writeOtherSettings( fSheet, style_1, "-" ); - - data_validation.setFirstRow((short)(start_row+6)); - data_validation.setLastRow((short)(start_row+6)); - data_validation.setEmptyCellAllowed(true); - data_validation.setShowPromptBox(false); - data_validation.setErrorStyle(HSSFDataValidation.ERROR_STYLE_STOP); - data_validation.setShowErrorBox(true); - data_validation.setFirstFormula(String.valueOf((int)HSSFDateUtil.getExcelDate(df.parse("3/2/2004")))); - data_validation.setSecondFormula(null); - data_validation.setOperator(HSSFDataValidation.OPERATOR_GREATER_OR_EQUAL); - fSheet.addValidationData(data_validation); - this.writeDataValidationSettings( fSheet, style_1, style_2, "Greater than or equal to 3/2/2004", true, false, true ); - this.setCellFormat( fSheet, cellStyle_data ); - this.writeOtherSettings( fSheet, style_1, "Error box type = STOP" ); - - data_validation.setFirstRow((short)(start_row+7)); - data_validation.setLastRow((short)(start_row+7)); - data_validation.setEmptyCellAllowed(false); - data_validation.setShowPromptBox(true); - data_validation.setShowErrorBox(false); - data_validation.setFirstFormula(String.valueOf((int)HSSFDateUtil.getExcelDate(df.parse("3/4/2004")))); - data_validation.setSecondFormula(null); - data_validation.setOperator(HSSFDataValidation.OPERATOR_LESS_OR_EQUAL); - fSheet.addValidationData(data_validation); - this.writeDataValidationSettings( fSheet, style_1, style_2, "Less than or equal to 3/4/2004", false, true, false ); - this.setCellFormat( fSheet, cellStyle_data ); - this.writeOtherSettings( fSheet, style_1, "-" ); - - //"Time" validation type - this.createDVTypeRow( wb, 2, style_3, "Time ( cells are already formated as time - h:mm)"); - this.createHeaderRow( wb, 2, style_4 ); - - df = new SimpleDateFormat("hh:mm"); - - start_row += (short)(8+4); - data_validation = new HSSFDataValidation((short)(start_row),(short)0,(short)(start_row),(short)0); - data_validation.setDataValidationType(HSSFDataValidation.DATA_TYPE_TIME); - data_validation.setOperator(HSSFDataValidation.OPERATOR_BETWEEN); - data_validation.setFirstFormula(String.valueOf(HSSFDateUtil.getExcelDate(df.parse("12:00")))); - data_validation.setSecondFormula(String.valueOf(HSSFDateUtil.getExcelDate(df.parse("16:00")))); - data_validation.createErrorBox("Invalid input !", "Something is wrong ; check condition !"); - data_validation.createPromptBox("Hi , dear user !", "So , you just selected me ! Thanks !"); - fSheet.addValidationData(data_validation); - this.writeDataValidationSettings( fSheet, style_1, style_2, "Between 12:00 and 16:00 ", true, true, true ); - this.setCellFormat( fSheet, cellStyle_time ); - this.writeOtherSettings( fSheet, style_1, "Error box type = STOP" ); - - data_validation.setFirstRow((short)(start_row+1)); - data_validation.setLastRow((short)(start_row+1)); - data_validation.setEmptyCellAllowed(false); - data_validation.setOperator(HSSFDataValidation.OPERATOR_NOT_BETWEEN); - data_validation.setErrorStyle(HSSFDataValidation.ERROR_STYLE_INFO); - fSheet.addValidationData(data_validation); - this.writeDataValidationSettings( fSheet, style_1, style_2, "Not between 12:00 and 16:00 ", false, true, true ); - this.setCellFormat( fSheet, cellStyle_time ); - this.writeOtherSettings( fSheet, style_1, "Error box type = INFO" ); - - data_validation.setFirstRow((short)(start_row+2)); - data_validation.setLastRow((short)(start_row+2)); - data_validation.setEmptyCellAllowed(false); - data_validation.setShowPromptBox(false); - data_validation.setFirstFormula(String.valueOf((int)HSSFDateUtil.getExcelDate(df.parse("13:35")))); - data_validation.setSecondFormula(null); - data_validation.setOperator(HSSFDataValidation.OPERATOR_EQUAL); - data_validation.setErrorStyle(HSSFDataValidation.ERROR_STYLE_WARNING); - fSheet.addValidationData(data_validation); - this.writeDataValidationSettings( fSheet, style_1, style_2, "Equal to 13:35", false, false, true ); - this.setCellFormat( fSheet, cellStyle_time ); - this.writeOtherSettings( fSheet, style_1, "Error box type = WARNING" ); - - data_validation.setFirstRow((short)(start_row+3)); - data_validation.setLastRow((short)(start_row+3)); - data_validation.setEmptyCellAllowed(false); - data_validation.setShowPromptBox(false); - data_validation.setShowErrorBox(false); - data_validation.setFirstFormula(String.valueOf(HSSFDateUtil.getExcelDate(df.parse("13:35")))); - data_validation.setSecondFormula(null); - data_validation.setOperator(HSSFDataValidation.OPERATOR_NOT_EQUAL); - fSheet.addValidationData(data_validation); - this.writeDataValidationSettings( fSheet, style_1, style_2, "Not equal to 13:35", false, false, false ); - this.setCellFormat( fSheet, cellStyle_time ); - this.writeOtherSettings( fSheet, style_1, "-" ); - - data_validation.setFirstRow((short)(start_row+4)); - data_validation.setLastRow((short)(start_row+4)); - data_validation.setEmptyCellAllowed(true); - data_validation.setShowPromptBox(false); - data_validation.setShowErrorBox(false); - data_validation.setFirstFormula(String.valueOf(HSSFDateUtil.getExcelDate(df.parse("12:00")))); - data_validation.setSecondFormula(null); - data_validation.setOperator(HSSFDataValidation.OPERATOR_GREATER_THAN); - fSheet.addValidationData(data_validation); - this.writeDataValidationSettings( fSheet, style_1, style_2, "Greater than 12:00", true, false, false ); - this.setCellFormat( fSheet, cellStyle_time ); - this.writeOtherSettings( fSheet, style_1, "-" ); - - data_validation.setFirstRow((short)(start_row+5)); - data_validation.setLastRow((short)(start_row+5)); - data_validation.setEmptyCellAllowed(true); - data_validation.setShowPromptBox(true); - data_validation.setShowErrorBox(false); - data_validation.setFirstFormula(String.valueOf(HSSFDateUtil.getExcelDate(df.parse("12:00")))); - data_validation.setSecondFormula(null); - data_validation.setOperator(HSSFDataValidation.OPERATOR_LESS_THAN); - fSheet.addValidationData(data_validation); - this.writeDataValidationSettings( fSheet, style_1, style_2, "Less than 12:00", true, true, false ); - this.setCellFormat( fSheet, cellStyle_time ); - this.writeOtherSettings( fSheet, style_1, "-" ); - - data_validation.setFirstRow((short)(start_row+6)); - data_validation.setLastRow((short)(start_row+6)); - data_validation.setEmptyCellAllowed(true); - data_validation.setShowPromptBox(false); - data_validation.setErrorStyle(HSSFDataValidation.ERROR_STYLE_STOP); - data_validation.setShowErrorBox(true); - data_validation.setFirstFormula(String.valueOf(HSSFDateUtil.getExcelDate(df.parse("14:00")))); - data_validation.setSecondFormula(null); - data_validation.setOperator(HSSFDataValidation.OPERATOR_GREATER_OR_EQUAL); - fSheet.addValidationData(data_validation); - this.writeDataValidationSettings( fSheet, style_1, style_2, "Greater than or equal to 14:00", true, false, true ); - this.setCellFormat( fSheet, cellStyle_time ); - this.writeOtherSettings( fSheet, style_1, "Error box type = STOP" ); - - data_validation.setFirstRow((short)(start_row+7)); - data_validation.setLastRow((short)(start_row+7)); - data_validation.setEmptyCellAllowed(false); - data_validation.setShowPromptBox(true); - data_validation.setShowErrorBox(false); - data_validation.setFirstFormula(String.valueOf(HSSFDateUtil.getExcelDate(df.parse("14:00")))); - data_validation.setSecondFormula(null); - data_validation.setOperator(HSSFDataValidation.OPERATOR_LESS_OR_EQUAL); - fSheet.addValidationData(data_validation); - this.writeDataValidationSettings( fSheet, style_1, style_2, "Less than or equal to 14:00", false, true, false ); - this.setCellFormat( fSheet, cellStyle_time ); - this.writeOtherSettings( fSheet, style_1, "-" ); - - System.out.println("done !"); - - //"Text length" validation type - System.out.print(" Create sheet for 'Text length' Data Validation type... "); - fSheet = wb.createSheet("Text length"); - this.createHeaderRow( wb, 3, style_4 ); - - data_validation = new HSSFDataValidation((short)1,(short)0,(short)1,(short)0); - data_validation.setDataValidationType(HSSFDataValidation.DATA_TYPE_TEXT_LENGTH); - data_validation.setOperator(HSSFDataValidation.OPERATOR_BETWEEN); - data_validation.setFirstFormula("2"); - data_validation.setSecondFormula("6"); - data_validation.createErrorBox("Invalid input !", "Something is wrong ; check condition !"); - data_validation.createPromptBox("Hi , dear user !", "So , you just selected me ! Thanks !"); - fSheet.addValidationData(data_validation); - this.writeDataValidationSettings( fSheet, style_1, style_2, "Between 2 and 6 ", true, true, true ); - this.writeOtherSettings( fSheet, style_1, "Error box type = STOP" ); - - data_validation.setFirstRow((short)2); - data_validation.setLastRow((short)2); - data_validation.setEmptyCellAllowed(false); - data_validation.setOperator(HSSFDataValidation.OPERATOR_NOT_BETWEEN); - data_validation.setErrorStyle(HSSFDataValidation.ERROR_STYLE_INFO); - fSheet.addValidationData(data_validation); - this.writeDataValidationSettings( fSheet, style_1, style_2, "Not between 2 and 6 ", false, true, true ); - this.writeOtherSettings( fSheet, style_1, "Error box type = INFO" ); - - data_validation.setFirstRow((short)3); - data_validation.setLastRow((short)3); - data_validation.setEmptyCellAllowed(false); - data_validation.setShowPromptBox(false); - data_validation.setFirstFormula("3"); - data_validation.setSecondFormula(null); - data_validation.setOperator(HSSFDataValidation.OPERATOR_EQUAL); - data_validation.setErrorStyle(HSSFDataValidation.ERROR_STYLE_WARNING); - fSheet.addValidationData(data_validation); - this.writeDataValidationSettings( fSheet, style_1, style_2, "Equal to 3", false, false, true ); - this.writeOtherSettings( fSheet, style_1, "Error box type = WARNING" ); - - data_validation.setFirstRow((short)4); - data_validation.setLastRow((short)4); - data_validation.setEmptyCellAllowed(false); - data_validation.setShowPromptBox(false); - data_validation.setShowErrorBox(false); - data_validation.setFirstFormula("3"); - data_validation.setSecondFormula(null); - data_validation.setOperator(HSSFDataValidation.OPERATOR_NOT_EQUAL); - fSheet.addValidationData(data_validation); - this.writeDataValidationSettings( fSheet, style_1, style_2, "Not equal to 3", false, false, false ); - this.writeOtherSettings( fSheet, style_1, "-" ); - - data_validation.setFirstRow((short)5); - data_validation.setLastRow((short)5); - data_validation.setEmptyCellAllowed(true); - data_validation.setShowPromptBox(false); - data_validation.setShowErrorBox(false); - data_validation.setFirstFormula("3"); - data_validation.setSecondFormula(null); - data_validation.setOperator(HSSFDataValidation.OPERATOR_GREATER_THAN); - fSheet.addValidationData(data_validation); - this.writeDataValidationSettings( fSheet, style_1, style_2, "Greater than 3", true, false, false ); - this.writeOtherSettings( fSheet, style_1, "-" ); - - data_validation.setFirstRow((short)6); - data_validation.setLastRow((short)6); - data_validation.setEmptyCellAllowed(true); - data_validation.setShowPromptBox(true); - data_validation.setShowErrorBox(false); - data_validation.setFirstFormula("3"); - data_validation.setSecondFormula(null); - data_validation.setOperator(HSSFDataValidation.OPERATOR_LESS_THAN); - fSheet.addValidationData(data_validation); - this.writeDataValidationSettings( fSheet, style_1, style_2, "Less than 3", true, true, false ); - this.writeOtherSettings( fSheet, style_1, "-" ); - - data_validation.setFirstRow((short)7); - data_validation.setLastRow((short)7); - data_validation.setEmptyCellAllowed(true); - data_validation.setShowPromptBox(false); - data_validation.setErrorStyle(HSSFDataValidation.ERROR_STYLE_STOP); - data_validation.setShowErrorBox(true); - data_validation.setFirstFormula("4"); - data_validation.setSecondFormula(null); - data_validation.setOperator(HSSFDataValidation.OPERATOR_GREATER_OR_EQUAL); - fSheet.addValidationData(data_validation); - this.writeDataValidationSettings( fSheet, style_1, style_2, "Greater than or equal to 4", true, false, true ); - this.writeOtherSettings( fSheet, style_1, "Error box type = STOP" ); - - data_validation.setFirstRow((short)8); - data_validation.setLastRow((short)8); - data_validation.setEmptyCellAllowed(false); - data_validation.setShowPromptBox(true); - data_validation.setShowErrorBox(false); - data_validation.setFirstFormula("4"); - data_validation.setSecondFormula(null); - data_validation.setOperator(HSSFDataValidation.OPERATOR_LESS_OR_EQUAL); - fSheet.addValidationData(data_validation); - this.writeDataValidationSettings( fSheet, style_1, style_2, "Less than or equal to 4", false, true, false ); - this.writeOtherSettings( fSheet, style_1, "-" ); - System.out.println("done !"); - - //Custom Validation type - System.out.print(" Create sheet for 'Custom' Data Validation type ... "); - fSheet = wb.createSheet("Custom"); - this.createHeaderRow( wb, 4, style_4 ); - - data_validation = new HSSFDataValidation((short)1,(short)0,(short)1,(short)0); - data_validation.setDataValidationType(HSSFDataValidation.DATA_TYPE_FORMULA); - data_validation.setFirstFormula("ISNUMBER($A2)"); - data_validation.setSecondFormula(null); - data_validation.setShowPromptBox(true); - data_validation.setShowErrorBox(true); - data_validation.createErrorBox("Invalid input !", "Something is wrong ; check condition !"); - data_validation.createPromptBox("Hi , dear user !", "So , you just selected me ! Thanks !"); - fSheet.addValidationData(data_validation); - this.writeDataValidationSettings( fSheet, style_1, style_2, "ISNUMBER(A2)", true, true, true ); - this.writeOtherSettings( fSheet, style_1, "Error box type = STOP" ); - - data_validation = new HSSFDataValidation((short)2,(short)0,(short)2,(short)0); - data_validation.setDataValidationType(HSSFDataValidation.DATA_TYPE_FORMULA); - data_validation.setFirstFormula("IF(SUM(A2:A3)=5,TRUE,FALSE)"); - data_validation.setSecondFormula(null); - data_validation.setShowPromptBox(false); - data_validation.setShowErrorBox(true); - data_validation.setErrorStyle(HSSFDataValidation.ERROR_STYLE_WARNING); - data_validation.createErrorBox("Invalid input !", "Something is wrong ; check condition !"); - data_validation.setEmptyCellAllowed(false); - fSheet.addValidationData(data_validation); - this.writeDataValidationSettings( fSheet, style_1, style_2, "IF(SUM(A2:A3)=5,TRUE,FALSE)", false, false, true ); - this.writeOtherSettings( fSheet, style_1, "Error box type = WARNING" ); - - System.out.println("done !"); - - //so , everything it's ok for now ; it remains for you to open the file - System.out.println("\n Everything it's ok since we've got so far -:) !\n"+ - " In order to complete the test , it remains for you to open the file \n"+ - " and see if there are four sheets , as described !"); - System.out.println(" File was saved in \""+resultFile+"\""); - - FileOutputStream fileOut = new FileOutputStream(resultFile); - wb.write(fileOut); - fileOut.close(); - } +public final class TestDataValidation extends TestCase { + + /** Convenient access to ERROR_STYLE constants */ + private static final HSSFDataValidation.ErrorStyle ES = null; + /** Convenient access to OPERATOR constants */ + private static final DVConstraint.ValidationType VT = null; + /** Convenient access to OPERATOR constants */ + private static final DVConstraint.OperatorType OP = null; + + private static void log(String msg) { + if (false) { // successful tests should be silent + System.out.println(msg); + } + } + + private static final class ValidationAdder { + + private final HSSFCellStyle _style_1; + private final HSSFCellStyle _style_2; + private final int _validationType; + private final HSSFSheet _sheet; + private int _currentRowIndex; + private final HSSFCellStyle _cellStyle; + + public ValidationAdder(HSSFSheet fSheet, HSSFCellStyle style_1, HSSFCellStyle style_2, + HSSFCellStyle cellStyle, int validationType) { + _sheet = fSheet; + _style_1 = style_1; + _style_2 = style_2; + _cellStyle = cellStyle; + _validationType = validationType; + _currentRowIndex = fSheet.getPhysicalNumberOfRows(); + } + public void addValidation(int operatorType, String firstFormula, String secondFormula, + int errorStyle, String ruleDescr, String promptDescr, + boolean allowEmpty, boolean inputBox, boolean errorBox) { + String[] explicitListValues = null; + + addValidationInternal(operatorType, firstFormula, secondFormula, errorStyle, ruleDescr, + promptDescr, allowEmpty, inputBox, errorBox, true, + explicitListValues); + } - private void createDVTypeRow( HSSFWorkbook wb, int sheetNo , HSSFCellStyle cellStyle, String strTypeDescription) - { - HSSFSheet sheet = wb.getSheetAt(sheetNo); - HSSFRow row = sheet.createRow(sheet.getPhysicalNumberOfRows()); - row = sheet.createRow(sheet.getPhysicalNumberOfRows()); - sheet.addMergedRegion(new Region((short)(sheet.getPhysicalNumberOfRows()-1),(short)0,(short)(sheet.getPhysicalNumberOfRows()-1),(short)5)); - HSSFCell cell = row.createCell((short)0); - cell.setCellValue(strTypeDescription); - cell.setCellStyle(cellStyle); - row = sheet.createRow(sheet.getPhysicalNumberOfRows()); - } + private void addValidationInternal(int operatorType, String firstFormula, + String secondFormula, int errorStyle, String ruleDescr, String promptDescr, + boolean allowEmpty, boolean inputBox, boolean errorBox, boolean suppressDropDown, + String[] explicitListValues) { + int rowNum = _currentRowIndex++; + + DVConstraint dc = createConstraint(operatorType, firstFormula, secondFormula, explicitListValues); + + HSSFDataValidation dv = new HSSFDataValidation(new CellRangeAddressList(rowNum, rowNum, 0, 0), dc); + + dv.setEmptyCellAllowed(allowEmpty); + dv.setErrorStyle(errorStyle); + dv.createErrorBox("Invalid Input", "Something is wrong - check condition!"); + dv.createPromptBox("Validated Cell", "Allowable values have been restricted"); + + dv.setShowPromptBox(inputBox); + dv.setShowErrorBox(errorBox); + dv.setSuppressDropDownArrow(suppressDropDown); + + + _sheet.addValidationData(dv); + writeDataValidationSettings(_sheet, _style_1, _style_2, ruleDescr, allowEmpty, + inputBox, errorBox); + if (_cellStyle != null) { + HSSFRow row = _sheet.getRow(_sheet.getPhysicalNumberOfRows() - 1); + HSSFCell cell = row.createCell((short) 0); + cell.setCellStyle(_cellStyle); + } + writeOtherSettings(_sheet, _style_1, promptDescr); + } + private DVConstraint createConstraint(int operatorType, String firstFormula, + String secondFormula, String[] explicitListValues) { + if (_validationType == VT.LIST) { + if (explicitListValues != null) { + return DVConstraint.createExplicitListConstraint(explicitListValues); + } + return DVConstraint.createFormulaListConstraint(firstFormula); + } + if (_validationType == VT.TIME) { + return DVConstraint.createTimeConstraint(operatorType, firstFormula, secondFormula); + } + if (_validationType == VT.DATE) { + return DVConstraint.createDateConstraint(operatorType, firstFormula, secondFormula, null); + } + if (_validationType == VT.FORMULA) { + return DVConstraint.createFormulaConstraint(firstFormula); + } + return DVConstraint.createNumericConstraint(_validationType, operatorType, firstFormula, secondFormula); + } + /** + * writes plain text values into cells in a tabular format to form comments readable from within + * the spreadsheet. + */ + private static void writeDataValidationSettings(HSSFSheet sheet, HSSFCellStyle style_1, + HSSFCellStyle style_2, String strCondition, boolean allowEmpty, boolean inputBox, + boolean errorBox) { + HSSFRow row = sheet.createRow(sheet.getPhysicalNumberOfRows()); + // condition's string + HSSFCell cell = row.createCell((short) 1); + cell.setCellStyle(style_1); + setCellValue(cell, strCondition); + // allow empty cells + cell = row.createCell((short) 2); + cell.setCellStyle(style_2); + setCellValue(cell, ((allowEmpty) ? "yes" : "no")); + // show input box + cell = row.createCell((short) 3); + cell.setCellStyle(style_2); + setCellValue(cell, ((inputBox) ? "yes" : "no")); + // show error box + cell = row.createCell((short) 4); + cell.setCellStyle(style_2); + setCellValue(cell, ((errorBox) ? "yes" : "no")); + } + private static void writeOtherSettings(HSSFSheet sheet, HSSFCellStyle style, + String strStettings) { + HSSFRow row = sheet.getRow(sheet.getPhysicalNumberOfRows() - 1); + HSSFCell cell = row.createCell((short) 5); + cell.setCellStyle(style); + setCellValue(cell, strStettings); + } + public void addListValidation(String[] explicitListValues, String listFormula, String listValsDescr, + boolean allowEmpty, boolean suppressDropDown) { + String promptDescr = (allowEmpty ? "empty ok" : "not empty") + + ", " + (suppressDropDown ? "no drop-down" : "drop-down"); + addValidationInternal(VT.LIST, listFormula, null, ES.STOP, listValsDescr, promptDescr, + allowEmpty, false, true, suppressDropDown, explicitListValues); + } + } - private void createDVDeescriptionRow( HSSFWorkbook wb, int sheetNo , HSSFCellStyle cellStyle, String strTypeDescription ) - { - HSSFSheet sheet = wb.getSheetAt(sheetNo); - HSSFRow row = sheet.getRow(sheet.getPhysicalNumberOfRows()-1); - sheet.addMergedRegion(new Region((short)(sheet.getPhysicalNumberOfRows()-1),(short)0,(short)(sheet.getPhysicalNumberOfRows()-1),(short)5)); - HSSFCell cell = row.createCell((short)0); - cell.setCellValue(strTypeDescription); - cell.setCellStyle(cellStyle); - row = sheet.createRow(sheet.getPhysicalNumberOfRows()); - } + /** + * Manages the cell styles used for formatting the output spreadsheet + */ + private static final class WorkbookFormatter { + + private final HSSFWorkbook _wb; + private final HSSFCellStyle _style_1; + private final HSSFCellStyle _style_2; + private final HSSFCellStyle _style_3; + private final HSSFCellStyle _style_4; + private HSSFSheet _currentSheet; + + public WorkbookFormatter(HSSFWorkbook wb) { + _wb = wb; + _style_1 = createStyle( wb, HSSFCellStyle.ALIGN_LEFT ); + _style_2 = createStyle( wb, HSSFCellStyle.ALIGN_CENTER ); + _style_3 = createStyle( wb, HSSFCellStyle.ALIGN_CENTER, HSSFColor.GREY_25_PERCENT.index, true ); + _style_4 = createHeaderStyle(wb); + } + + private static HSSFCellStyle createStyle(HSSFWorkbook wb, short h_align, short color, + boolean bold) { + HSSFFont font = wb.createFont(); + if (bold) { + font.setBoldweight(HSSFFont.BOLDWEIGHT_BOLD); + } - private void createHeaderRow( HSSFWorkbook wb, int sheetNo , HSSFCellStyle cellStyle ) - { - HSSFSheet sheet = wb.getSheetAt(sheetNo); - HSSFRow row = sheet.createRow(sheet.getPhysicalNumberOfRows()); - row.setHeight((short)400); - for ( int i=0; i<6; i++ ) - { - row.createCell((short)i).setCellStyle( cellStyle ); - if ( i==2 || i==3 || i==4 ) - { - sheet.setColumnWidth( (short) i, (short) 3500); - } - else if ( i== 5) - { - sheet.setColumnWidth( (short) i, (short) 10000); - } - else - { - sheet.setColumnWidth( (short) i, (short) 8000); - } - } - HSSFCell cell = row.getCell((short)0); - cell.setCellValue("Data validation cells"); - cell = row.getCell((short)1); - cell.setCellValue("Condition"); - cell = row.getCell((short)2); - cell.setCellValue("Allow blank"); - cell = row.getCell((short)3); - cell.setCellValue("Prompt box"); - cell = row.getCell((short)4); - cell.setCellValue("Error box"); - cell = row.getCell((short)5); - cell.setCellValue("Other settings"); - } + HSSFCellStyle cellStyle = wb.createCellStyle(); + cellStyle.setFont(font); + cellStyle.setFillForegroundColor(color); + cellStyle.setFillPattern(HSSFCellStyle.SOLID_FOREGROUND); + cellStyle.setVerticalAlignment(HSSFCellStyle.VERTICAL_CENTER); + cellStyle.setAlignment(h_align); + cellStyle.setBorderLeft(HSSFCellStyle.BORDER_THIN); + cellStyle.setLeftBorderColor(HSSFColor.BLACK.index); + cellStyle.setBorderTop(HSSFCellStyle.BORDER_THIN); + cellStyle.setTopBorderColor(HSSFColor.BLACK.index); + cellStyle.setBorderRight(HSSFCellStyle.BORDER_THIN); + cellStyle.setRightBorderColor(HSSFColor.BLACK.index); + cellStyle.setBorderBottom(HSSFCellStyle.BORDER_THIN); + cellStyle.setBottomBorderColor(HSSFColor.BLACK.index); + + return cellStyle; + } - private HSSFCellStyle createHeaderStyle(HSSFWorkbook wb) - { - HSSFFont font = wb.createFont(); - font.setColor( HSSFColor.WHITE.index ); - font.setBoldweight(HSSFFont.BOLDWEIGHT_BOLD); - - HSSFCellStyle cellStyle = wb.createCellStyle(); - cellStyle.setFillForegroundColor(HSSFColor.BLUE_GREY.index); - cellStyle.setFillPattern(HSSFCellStyle.SOLID_FOREGROUND); - cellStyle.setAlignment(HSSFCellStyle.ALIGN_CENTER); - cellStyle.setVerticalAlignment(HSSFCellStyle.VERTICAL_CENTER); - cellStyle.setBorderLeft(HSSFCellStyle.BORDER_THIN); - cellStyle.setLeftBorderColor(HSSFColor.WHITE.index); - cellStyle.setBorderTop(HSSFCellStyle.BORDER_THIN); - cellStyle.setTopBorderColor(HSSFColor.WHITE.index); - cellStyle.setBorderRight(HSSFCellStyle.BORDER_THIN); - cellStyle.setRightBorderColor(HSSFColor.WHITE.index); - cellStyle.setBorderBottom(HSSFCellStyle.BORDER_THIN); - cellStyle.setBottomBorderColor(HSSFColor.WHITE.index); - cellStyle.setFont(font); - return cellStyle; - } + private static HSSFCellStyle createStyle(HSSFWorkbook wb, short h_align) { + return createStyle(wb, h_align, HSSFColor.WHITE.index, false); + } + private static HSSFCellStyle createHeaderStyle(HSSFWorkbook wb) { + HSSFFont font = wb.createFont(); + font.setColor( HSSFColor.WHITE.index ); + font.setBoldweight(HSSFFont.BOLDWEIGHT_BOLD); + + HSSFCellStyle cellStyle = wb.createCellStyle(); + cellStyle.setFillForegroundColor(HSSFColor.BLUE_GREY.index); + cellStyle.setFillPattern(HSSFCellStyle.SOLID_FOREGROUND); + cellStyle.setAlignment(HSSFCellStyle.ALIGN_CENTER); + cellStyle.setVerticalAlignment(HSSFCellStyle.VERTICAL_CENTER); + cellStyle.setBorderLeft(HSSFCellStyle.BORDER_THIN); + cellStyle.setLeftBorderColor(HSSFColor.WHITE.index); + cellStyle.setBorderTop(HSSFCellStyle.BORDER_THIN); + cellStyle.setTopBorderColor(HSSFColor.WHITE.index); + cellStyle.setBorderRight(HSSFCellStyle.BORDER_THIN); + cellStyle.setRightBorderColor(HSSFColor.WHITE.index); + cellStyle.setBorderBottom(HSSFCellStyle.BORDER_THIN); + cellStyle.setBottomBorderColor(HSSFColor.WHITE.index); + cellStyle.setFont(font); + return cellStyle; + } + - private HSSFCellStyle createStyle( HSSFWorkbook wb, short h_align, short color, boolean bold ) - { - HSSFFont font = wb.createFont(); - if ( bold ) - { - font.setBoldweight(HSSFFont.BOLDWEIGHT_BOLD); - } - - HSSFCellStyle cellStyle = wb.createCellStyle(); - cellStyle.setFont(font); - cellStyle.setFillForegroundColor(color); - cellStyle.setFillPattern(HSSFCellStyle.SOLID_FOREGROUND); - cellStyle.setVerticalAlignment(HSSFCellStyle.VERTICAL_CENTER); - cellStyle.setAlignment(h_align); - cellStyle.setBorderLeft(HSSFCellStyle.BORDER_THIN); - cellStyle.setLeftBorderColor(HSSFColor.BLACK.index); - cellStyle.setBorderTop(HSSFCellStyle.BORDER_THIN); - cellStyle.setTopBorderColor(HSSFColor.BLACK.index); - cellStyle.setBorderRight(HSSFCellStyle.BORDER_THIN); - cellStyle.setRightBorderColor(HSSFColor.BLACK.index); - cellStyle.setBorderBottom(HSSFCellStyle.BORDER_THIN); - cellStyle.setBottomBorderColor(HSSFColor.BLACK.index); - - return cellStyle; - } + public HSSFSheet createSheet(String sheetName) { + _currentSheet = _wb.createSheet(sheetName); + return _currentSheet; + } + public void createDVTypeRow(String strTypeDescription) { + HSSFSheet sheet = _currentSheet; + HSSFRow row = sheet.createRow(sheet.getPhysicalNumberOfRows()); + row = sheet.createRow(sheet.getPhysicalNumberOfRows()); + sheet.addMergedRegion(new Region((short) (sheet.getPhysicalNumberOfRows() - 1), + (short) 0, (short) (sheet.getPhysicalNumberOfRows() - 1), (short) 5)); + HSSFCell cell = row.createCell((short) 0); + setCellValue(cell, strTypeDescription); + cell.setCellStyle(_style_3); + row = sheet.createRow(sheet.getPhysicalNumberOfRows()); + } + + public void createHeaderRow() { + HSSFSheet sheet = _currentSheet; + HSSFRow row = sheet.createRow(sheet.getPhysicalNumberOfRows()); + row.setHeight((short) 400); + for (int i = 0; i < 6; i++) { + row.createCell((short) i).setCellStyle(_style_4); + if (i == 2 || i == 3 || i == 4) { + sheet.setColumnWidth((short) i, (short) 3500); + } else if (i == 5) { + sheet.setColumnWidth((short) i, (short) 10000); + } else { + sheet.setColumnWidth((short) i, (short) 8000); + } + } + HSSFCell cell = row.getCell((short) 0); + setCellValue(cell, "Data validation cells"); + cell = row.getCell((short) 1); + setCellValue(cell, "Condition"); + cell = row.getCell((short) 2); + setCellValue(cell, "Allow blank"); + cell = row.getCell((short) 3); + setCellValue(cell, "Prompt box"); + cell = row.getCell((short) 4); + setCellValue(cell, "Error box"); + cell = row.getCell((short) 5); + setCellValue(cell, "Other settings"); + } - private HSSFCellStyle createStyle( HSSFWorkbook wb, short h_align ) - { - return this.createStyle(wb, h_align, HSSFColor.WHITE.index, false); - } + public ValidationAdder createValidationAdder(HSSFCellStyle cellStyle, int dataValidationType) { + return new ValidationAdder(_currentSheet, _style_1, _style_2, cellStyle, dataValidationType); + } - private void writeDataValidationSettings( HSSFSheet sheet, HSSFCellStyle style_1, HSSFCellStyle style_2, String strCondition, boolean allowEmpty, boolean inputBox, boolean errorBox ) - { - HSSFRow row = sheet.createRow( sheet.getPhysicalNumberOfRows() ); - //condition's string - HSSFCell cell = row.createCell((short)1); - cell.setCellStyle(style_1); - cell.setCellValue(strCondition); - //allow empty cells - cell = row.createCell((short)2); - cell.setCellStyle(style_2); - cell.setCellValue( ((allowEmpty) ? "yes" : "no") ); - //show input box - cell = row.createCell((short)3); - cell.setCellStyle(style_2); - cell.setCellValue( ((inputBox) ? "yes" : "no") ); - //show error box - cell = row.createCell((short)4); - cell.setCellStyle(style_2); - cell.setCellValue( ((errorBox) ? "yes" : "no") ); - } + public void createDVDescriptionRow(String strTypeDescription) { + HSSFSheet sheet = _currentSheet; + HSSFRow row = sheet.getRow(sheet.getPhysicalNumberOfRows()-1); + sheet.addMergedRegion(new Region((short)(sheet.getPhysicalNumberOfRows()-1),(short)0,(short)(sheet.getPhysicalNumberOfRows()-1),(short)5)); + HSSFCell cell = row.createCell((short)0); + setCellValue(cell, strTypeDescription); + cell.setCellStyle(_style_3); + row = sheet.createRow(sheet.getPhysicalNumberOfRows()); + } + } + + + private void addCustomValidations(WorkbookFormatter wf) { + wf.createSheet("Custom"); + wf.createHeaderRow(); - private void setCellFormat( HSSFSheet sheet, HSSFCellStyle cell_style ) - { - HSSFRow row = sheet.getRow( sheet.getPhysicalNumberOfRows() -1 ); - HSSFCell cell = row.createCell((short)0); - cell.setCellStyle(cell_style); - } + ValidationAdder va = wf.createValidationAdder(null, VT.FORMULA); + va.addValidation(OP.BETWEEN, "ISNUMBER($A2)", null, ES.STOP, "ISNUMBER(A2)", "Error box type = STOP", true, true, true); + va.addValidation(OP.BETWEEN, "IF(SUM(A2:A3)=5,TRUE,FALSE)", null, ES.WARNING, "IF(SUM(A2:A3)=5,TRUE,FALSE)", "Error box type = WARNING", false, false, true); + } + + private static void addSimpleNumericValidations(WorkbookFormatter wf) { + // data validation's number types + wf.createSheet("Numbers"); + + // "Whole number" validation type + wf.createDVTypeRow("Whole number"); + wf.createHeaderRow(); + + ValidationAdder va = wf.createValidationAdder(null, VT.INTEGER); + va.addValidation(OP.BETWEEN, "2", "6", ES.STOP, "Between 2 and 6 ", "Error box type = STOP", true, true, true); + va.addValidation(OP.NOT_BETWEEN, "2", "6", ES.INFO, "Not between 2 and 6 ", "Error box type = INFO", false, true, true); + va.addValidation(OP.EQUAL, "=3+2", null, ES.WARNING, "Equal to (3+2)", "Error box type = WARNING", false, false, true); + va.addValidation(OP.NOT_EQUAL, "3", null, ES.WARNING, "Not equal to 3", "-", false, false, false); + va.addValidation(OP.GREATER_THAN, "3", null, ES.WARNING, "Greater than 3", "-", true, false, false); + va.addValidation(OP.LESS_THAN, "3", null, ES.WARNING, "Less than 3", "-", true, true, false); + va.addValidation(OP.GREATER_OR_EQUAL, "4", null, ES.STOP, "Greater than or equal to 4", "Error box type = STOP", true, false, true); + va.addValidation(OP.LESS_OR_EQUAL, "4", null, ES.STOP, "Less than or equal to 4", "-", false, true, false); + + // "Decimal" validation type + wf.createDVTypeRow("Decimal"); + wf.createHeaderRow(); + + va = wf.createValidationAdder(null, VT.DECIMAL); + va.addValidation(OP.BETWEEN, "2", "6", ES.STOP, "Between 2 and 6 ", "Error box type = STOP", true, true, true); + va.addValidation(OP.NOT_BETWEEN, "2", "6", ES.INFO, "Not between 2 and 6 ", "Error box type = INFO", false, true, true); + va.addValidation(OP.EQUAL, "3", null, ES.WARNING, "Equal to 3", "Error box type = WARNING", false, false, true); + va.addValidation(OP.NOT_EQUAL, "3", null, ES.WARNING, "Not equal to 3", "-", false, false, false); + va.addValidation(OP.GREATER_THAN, "=12/6", null, ES.WARNING, "Greater than (12/6)", "-", true, false, false); + va.addValidation(OP.LESS_THAN, "3", null, ES.WARNING, "Less than 3", "-", true, true, false); + va.addValidation(OP.GREATER_OR_EQUAL, "4", null, ES.STOP, "Greater than or equal to 4", "Error box type = STOP", true, false, true); + va.addValidation(OP.LESS_OR_EQUAL, "4", null, ES.STOP, "Less than or equal to 4", "-", false, true, false); + } + + private static void addListValidations(WorkbookFormatter wf, HSSFWorkbook wb) { + final String cellStrValue + = "a b c d e f g h i j k l m n o p r s t u v x y z w 0 1 2 3 4 " + + "a b c d e f g h i j k l m n o p r s t u v x y z w 0 1 2 3 4 " + + "a b c d e f g h i j k l m n o p r s t u v x y z w 0 1 2 3 4 " + + "a b c d e f g h i j k l m n o p r s t u v x y z w 0 1 2 3 4 "; + final String dataSheetName = "list_data"; + // "List" Data Validation type + HSSFSheet fSheet = wf.createSheet("Lists"); + HSSFSheet dataSheet = wb.createSheet(dataSheetName); + + + wf.createDVTypeRow("Explicit lists - list items are explicitly provided"); + wf.createDVDescriptionRow("Disadvantage - sum of item's length should be less than 255 characters"); + wf.createHeaderRow(); + + ValidationAdder va = wf.createValidationAdder(null, VT.LIST); + String listValsDescr = "POIFS,HSSF,HWPF,HPSF"; + String[] listVals = listValsDescr.split(","); + va.addListValidation(listVals, null, listValsDescr, false, false); + va.addListValidation(listVals, null, listValsDescr, false, true); + va.addListValidation(listVals, null, listValsDescr, true, false); + va.addListValidation(listVals, null, listValsDescr, true, true); + + + + wf.createDVTypeRow("Reference lists - list items are taken from others cells"); + wf.createDVDescriptionRow("Advantage - no restriction regarding the sum of item's length"); + wf.createHeaderRow(); + va = wf.createValidationAdder(null, VT.LIST); + String strFormula = "$A$30:$A$39"; + va.addListValidation(null, strFormula, strFormula, false, false); + + strFormula = dataSheetName + "!$A$1:$A$10"; + va.addListValidation(null, strFormula, strFormula, false, false); + HSSFName namedRange = wb.createName(); + namedRange.setNameName("myName"); + namedRange.setReference(dataSheetName + "!$A$2:$A$7"); + strFormula = "myName"; + va.addListValidation(null, strFormula, strFormula, false, false); + strFormula = "offset(myName, 2, 1, 4, 2)"; // Note about last param '2': + // - Excel expects single row or single column when entered in UI, but process this OK otherwise + va.addListValidation(null, strFormula, strFormula, false, false); + + // add list data on same sheet + for (int i = 0; i < 10; i++) { + HSSFRow currRow = fSheet.createRow(i + 29); + setCellValue(currRow.createCell((short) 0), cellStrValue); + } + // add list data on another sheet + for (int i = 0; i < 10; i++) { + HSSFRow currRow = dataSheet.createRow(i + 0); + setCellValue(currRow.createCell((short) 0), "Data a" + i); + setCellValue(currRow.createCell((short) 1), "Data b" + i); + setCellValue(currRow.createCell((short) 2), "Data c" + i); + } + } + + private static void addDateTimeValidations(WorkbookFormatter wf, HSSFWorkbook wb) { + wf.createSheet("Dates and Times"); + + HSSFDataFormat dataFormat = wb.createDataFormat(); + short fmtDate = dataFormat.getFormat("m/d/yyyy"); + short fmtTime = dataFormat.getFormat("h:mm"); + HSSFCellStyle cellStyle_date = wb.createCellStyle(); + cellStyle_date.setDataFormat(fmtDate); + HSSFCellStyle cellStyle_time = wb.createCellStyle(); + cellStyle_time.setDataFormat(fmtTime); + + wf.createDVTypeRow("Date ( cells are already formated as date - m/d/yyyy)"); + wf.createHeaderRow(); + + ValidationAdder va = wf.createValidationAdder(cellStyle_date, VT.DATE); + va.addValidation(OP.BETWEEN, "2004/01/02", "2004/01/06", ES.STOP, "Between 1/2/2004 and 1/6/2004 ", "Error box type = STOP", true, true, true); + va.addValidation(OP.NOT_BETWEEN, "2004/01/01", "2004/01/06", ES.INFO, "Not between 1/2/2004 and 1/6/2004 ", "Error box type = INFO", false, true, true); + va.addValidation(OP.EQUAL, "2004/03/02", null, ES.WARNING, "Equal to 3/2/2004", "Error box type = WARNING", false, false, true); + va.addValidation(OP.NOT_EQUAL, "2004/03/02", null, ES.WARNING, "Not equal to 3/2/2004", "-", false, false, false); + va.addValidation(OP.GREATER_THAN,"=DATEVALUE(\"4-Jul-2001\")", null, ES.WARNING, "Greater than DATEVALUE('4-Jul-2001')", "-", true, false, false); + va.addValidation(OP.LESS_THAN, "2004/03/02", null, ES.WARNING, "Less than 3/2/2004", "-", true, true, false); + va.addValidation(OP.GREATER_OR_EQUAL, "2004/03/02", null, ES.STOP, "Greater than or equal to 3/2/2004", "Error box type = STOP", true, false, true); + va.addValidation(OP.LESS_OR_EQUAL, "2004/03/04", null, ES.STOP, "Less than or equal to 3/4/2004", "-", false, true, false); + + // "Time" validation type + wf.createDVTypeRow("Time ( cells are already formated as time - h:mm)"); + wf.createHeaderRow(); + + va = wf.createValidationAdder(cellStyle_time, VT.TIME); + va.addValidation(OP.BETWEEN, "12:00", "16:00", ES.STOP, "Between 12:00 and 16:00 ", "Error box type = STOP", true, true, true); + va.addValidation(OP.NOT_BETWEEN, "12:00", "16:00", ES.INFO, "Not between 12:00 and 16:00 ", "Error box type = INFO", false, true, true); + va.addValidation(OP.EQUAL, "13:35", null, ES.WARNING, "Equal to 13:35", "Error box type = WARNING", false, false, true); + va.addValidation(OP.NOT_EQUAL, "13:35", null, ES.WARNING, "Not equal to 13:35", "-", false, false, false); + va.addValidation(OP.GREATER_THAN,"12:00", null, ES.WARNING, "Greater than 12:00", "-", true, false, false); + va.addValidation(OP.LESS_THAN, "=1/2", null, ES.WARNING, "Less than (1/2) -> 12:00", "-", true, true, false); + va.addValidation(OP.GREATER_OR_EQUAL, "14:00", null, ES.STOP, "Greater than or equal to 14:00", "Error box type = STOP", true, false, true); + va.addValidation(OP.LESS_OR_EQUAL,"14:00", null, ES.STOP, "Less than or equal to 14:00", "-", false, true, false); + } + + private static void addTextLengthValidations(WorkbookFormatter wf) { + wf.createSheet("Text lengths"); + wf.createHeaderRow(); + + ValidationAdder va = wf.createValidationAdder(null, VT.TEXT_LENGTH); + va.addValidation(OP.BETWEEN, "2", "6", ES.STOP, "Between 2 and 6 ", "Error box type = STOP", true, true, true); + va.addValidation(OP.NOT_BETWEEN, "2", "6", ES.INFO, "Not between 2 and 6 ", "Error box type = INFO", false, true, true); + va.addValidation(OP.EQUAL, "3", null, ES.WARNING, "Equal to 3", "Error box type = WARNING", false, false, true); + va.addValidation(OP.NOT_EQUAL, "3", null, ES.WARNING, "Not equal to 3", "-", false, false, false); + va.addValidation(OP.GREATER_THAN, "3", null, ES.WARNING, "Greater than 3", "-", true, false, false); + va.addValidation(OP.LESS_THAN, "3", null, ES.WARNING, "Less than 3", "-", true, true, false); + va.addValidation(OP.GREATER_OR_EQUAL, "4", null, ES.STOP, "Greater than or equal to 4", "Error box type = STOP", true, false, true); + va.addValidation(OP.LESS_OR_EQUAL, "4", null, ES.STOP, "Less than or equal to 4", "-", false, true, false); + } + + public void testDataValidation() { + log("\nTest no. 2 - Test Excel's Data validation mechanism"); + HSSFWorkbook wb = new HSSFWorkbook(); + WorkbookFormatter wf = new WorkbookFormatter(wb); + + log(" Create sheet for Data Validation's number types ... "); + addSimpleNumericValidations(wf); + log("done !"); + + log(" Create sheet for 'List' Data Validation type ... "); + addListValidations(wf, wb); + log("done !"); + + log(" Create sheet for 'Date' and 'Time' Data Validation types ... "); + addDateTimeValidations(wf, wb); + log("done !"); + + log(" Create sheet for 'Text length' Data Validation type... "); + addTextLengthValidations(wf); + log("done !"); + + // Custom Validation type + log(" Create sheet for 'Custom' Data Validation type ... "); + addCustomValidations(wf); + log("done !"); + + ByteArrayOutputStream baos = new ByteArrayOutputStream(22000); + try { + wb.write(baos); + baos.close(); + } catch (IOException e) { + throw new RuntimeException(e); + } + byte[] generatedContent = baos.toByteArray(); + boolean isSame; + if (false) { + // TODO - add proof spreadsheet and compare + InputStream proofStream = HSSFTestDataSamples.openSampleFileStream("TestDataValidation.xls"); + isSame = compareStreams(proofStream, generatedContent); + } + isSame = true; + + if (isSame) { + return; + } + File tempDir = new File(System.getProperty("java.io.tmpdir")); + File generatedFile = new File(tempDir, "GeneratedTestDataValidation.xls"); + try { + FileOutputStream fileOut = new FileOutputStream(generatedFile); + fileOut.write(generatedContent); + fileOut.close(); + } catch (IOException e) { + throw new RuntimeException(e); + } + + PrintStream ps = System.out; + + ps.println("This test case has failed because the generated file differs from proof copy '" + ); // TODO+ proofFile.getAbsolutePath() + "'."); + ps.println("The cause is usually a change to this test, or some common spreadsheet generation code. " + + "The developer has to decide whether the changes were wanted or unwanted."); + ps.println("If the changes to the generated version were unwanted, " + + "make the fix elsewhere (do not modify this test or the proof spreadsheet to get the test working)."); + ps.println("If the changes were wanted, make sure to open the newly generated file in Excel " + + "and verify it manually. The new proof file should be submitted after it is verified to be correct."); + ps.println(""); + ps.println("One other possible (but less likely) cause of a failed test is a problem in the " + + "comparison logic used here. Perhaps some extra file regions need to be ignored."); + ps.println("The generated file has been saved to '" + generatedFile.getAbsolutePath() + "' for manual inspection."); + + fail("Generated file differs from proof copy. See sysout comments for details on how to fix."); + + } + + private static boolean compareStreams(InputStream isA, byte[] generatedContent) { + + InputStream isB = new ByteArrayInputStream(generatedContent); + + // The allowable regions where the generated file can differ from the + // proof should be small (i.e. much less than 1K) + int[] allowableDifferenceRegions = { + 0x0228, 16, // a region of the file containing the OS username + 0x506C, 8, // See RootProperty (super fields _seconds_2 and _days_2) + }; + int[] diffs = StreamUtility.diffStreams(isA, isB, allowableDifferenceRegions); + if (diffs == null) { + return true; + } + System.err.println("Diff from proof: "); + for (int i = 0; i < diffs.length; i++) { + System.err.println("diff at offset: 0x" + Integer.toHexString(diffs[i])); + } + return false; + } + - private void writeOtherSettings( HSSFSheet sheet, HSSFCellStyle style, String strStettings ) - { - HSSFRow row = sheet.getRow( sheet.getPhysicalNumberOfRows() -1 ); - HSSFCell cell = row.createCell((short)5); - cell.setCellStyle(style); - cell.setCellValue(strStettings); - } + + + /* package */ static void setCellValue(HSSFCell cell, String text) { + cell.setCellValue(new HSSFRichTextString(text)); + + } public void testAddToExistingSheet() { @@ -913,19 +574,32 @@ public class TestDataValidation extends TestCase int dvRow = 0; HSSFSheet sheet = wb.getSheetAt(0); sheet.createRow(dvRow).createCell((short)0); - HSSFDataValidation dv = new HSSFDataValidation((short)dvRow, (short)0, (short)dvRow, (short)0); + DVConstraint dc = DVConstraint.createNumericConstraint(VT.INTEGER, OP.EQUAL, "402", null); + HSSFDataValidation dv = new HSSFDataValidation(new CellRangeAddressList(dvRow, 0, dvRow, 0), dc); + - dv.setDataValidationType(HSSFDataValidation.DATA_TYPE_INTEGER); dv.setEmptyCellAllowed(false); - dv.setOperator(HSSFDataValidation.OPERATOR_EQUAL); - dv.setFirstFormula("42"); - dv.setErrorStyle(HSSFDataValidation.ERROR_STYLE_STOP); + dv.setErrorStyle(ES.STOP); dv.setShowPromptBox(true); dv.createErrorBox("Error", "The value is wrong"); - dv.setSurppressDropDownArrow(true); + dv.setSuppressDropDownArrow(true); + + // sheet.addValidationData(dv); + + + dc = DVConstraint.createNumericConstraint(VT.INTEGER, OP.EQUAL, "42", null); + dv = new HSSFDataValidation(new CellRangeAddressList(0, 0, 0, 0), dc); + + + dv.setEmptyCellAllowed(false); + dv.setErrorStyle(ES.STOP); + dv.setShowPromptBox(true); + dv.createErrorBox("Xxx", "Yyy"); + dv.setSuppressDropDownArrow(true); sheet.addValidationData(dv); - wb.toString(); + + ByteArrayOutputStream baos = new ByteArrayOutputStream(); try { @@ -963,9 +637,22 @@ public class TestDataValidation extends TestCase // and the DV records, Excel will not be able to open the workbook without error. if (nextSid == 0x0867) { - throw new AssertionFailedError("Identified bug 45519"); + throw new AssertionFailedError("Identified bug XXXX"); } assertEquals(DVRecord.sid, nextSid); + + + + File tempDir = new File("c:/josh/temp"); + File generatedFile = new File(tempDir, "dvEx2.xls"); + try { + FileOutputStream fileOut = new FileOutputStream(generatedFile); + wb.write(fileOut); + fileOut.close(); + } catch (IOException e) { + throw new RuntimeException(e); + } + } private int findIndex(byte[] largeData, byte[] searchPattern) { byte firstByte = searchPattern[0]; diff --git a/src/testcases/org/apache/poi/hssf/usermodel/TestHSSFDateUtil.java b/src/testcases/org/apache/poi/hssf/usermodel/TestHSSFDateUtil.java index 894c77d685..4fce0af1d3 100644 --- a/src/testcases/org/apache/poi/hssf/usermodel/TestHSSFDateUtil.java +++ b/src/testcases/org/apache/poi/hssf/usermodel/TestHSSFDateUtil.java @@ -39,7 +39,7 @@ import org.apache.poi.hssf.model.Workbook; * @author Alex Jacoby (ajacoby at gmail.com) * @version %I%, %G% */ -public class TestHSSFDateUtil extends TestCase { +public final class TestHSSFDateUtil extends TestCase { public static final int CALENDAR_JANUARY = 0; public static final int CALENDAR_FEBRUARY = 1; @@ -47,11 +47,6 @@ public class TestHSSFDateUtil extends TestCase { public static final int CALENDAR_APRIL = 3; public static final int CALENDAR_JULY = 6; public static final int CALENDAR_OCTOBER = 9; - - public TestHSSFDateUtil(String s) - { - super(s); - } /** * Checks the date conversion functions in the HSSFDateUtil class. @@ -193,14 +188,13 @@ public class TestHSSFDateUtil extends TestCase { } /** - * Tests that we deal with timezones properly + * Tests that we deal with time-zones properly */ public void testCalendarConversion() { GregorianCalendar date = new GregorianCalendar(2002, 0, 1, 12, 1, 1); Date expected = date.getTime(); - double expectedExcel = HSSFDateUtil.getExcelDate(expected); - // Iteratating over the hours exposes any rounding issues. + // Iterating over the hours exposes any rounding issues. for (int hour = -12; hour <= 12; hour++) { String id = "GMT" + (hour < 0 ? "" : "+") + hour + ":00"; @@ -209,7 +203,7 @@ public class TestHSSFDateUtil extends TestCase { double excelDate = HSSFDateUtil.getExcelDate(date, false); Date javaDate = HSSFDateUtil.getJavaDate(excelDate); - // Should match despite timezone + // Should match despite time-zone assertEquals("Checking timezone " + id, expected.getTime(), javaDate.getTime()); } } @@ -402,7 +396,11 @@ public class TestHSSFDateUtil extends TestCase { assertEquals(34519.0, HSSFDateUtil.getExcelDate(createDate(1998, CALENDAR_JULY, 5), true), 0.00001); } - private Date createDate(int year, int month, int day) { + /** + * @param month zero based + * @param day one based + */ + private static Date createDate(int year, int month, int day) { Calendar c = new GregorianCalendar(); c.set(year, month, day, 0, 0, 0); c.set(Calendar.MILLISECOND, 0); @@ -420,10 +418,18 @@ public class TestHSSFDateUtil extends TestCase { calendar = new GregorianCalendar(1901, 0, 1); assertEquals("Checking absolute day (1 Jan 1901)", 366, HSSFDateUtil.absoluteDay(calendar, false)); } + + public void testConvertTime() { + + final double delta = 1E-7; // a couple of digits more accuracy than strictly required + assertEquals(0.5, HSSFDateUtil.convertTime("12:00"), delta); + assertEquals(2.0/3, HSSFDateUtil.convertTime("16:00"), delta); + assertEquals(0.0000116, HSSFDateUtil.convertTime("0:00:01"), delta); + assertEquals(0.7330440, HSSFDateUtil.convertTime("17:35:35"), delta); + } - public static void main(String [] args) { - System.out - .println("Testing org.apache.poi.hssf.usermodel.TestHSSFDateUtil"); - junit.textui.TestRunner.run(TestHSSFDateUtil.class); + public void testParseDate() { + assertEquals(createDate(2008, Calendar.AUGUST, 3), HSSFDateUtil.parseYYYYMMDDDate("2008/08/03")); + assertEquals(createDate(1994, Calendar.MAY, 1), HSSFDateUtil.parseYYYYMMDDDate("1994/05/01")); } }