]> source.dussan.org Git - poi.git/commitdiff
Extensive fixes for data validation (bug 44953)
authorJosh Micich <josh@apache.org>
Sun, 3 Aug 2008 22:11:26 +0000 (22:11 +0000)
committerJosh Micich <josh@apache.org>
Sun, 3 Aug 2008 22:11:26 +0000 (22:11 +0000)
git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@682225 13f79535-47bb-0310-9956-ffa450edef68

18 files changed:
src/documentation/content/xdocs/changes.xml
src/documentation/content/xdocs/status.xml
src/java/org/apache/poi/hssf/model/FormulaParser.java
src/java/org/apache/poi/hssf/model/OperandClassTransformer.java
src/java/org/apache/poi/hssf/record/DVRecord.java
src/java/org/apache/poi/hssf/record/aggregates/DataValidityTable.java
src/java/org/apache/poi/hssf/record/formula/NumberPtg.java
src/java/org/apache/poi/hssf/record/formula/Ptg.java
src/java/org/apache/poi/hssf/usermodel/DVConstraint.java [new file with mode: 0644]
src/java/org/apache/poi/hssf/usermodel/HSSFDataValidation.java [new file with mode: 0644]
src/java/org/apache/poi/hssf/usermodel/HSSFDateUtil.java
src/java/org/apache/poi/hssf/usermodel/HSSFSheet.java
src/java/org/apache/poi/hssf/util/CellRangeAddress.java [new file with mode: 0644]
src/java/org/apache/poi/hssf/util/CellRangeAddressList.java [new file with mode: 0644]
src/java/org/apache/poi/hssf/util/HSSFCellRangeAddress.java [deleted file]
src/java/org/apache/poi/hssf/util/HSSFDataValidation.java [deleted file]
src/testcases/org/apache/poi/hssf/usermodel/TestDataValidation.java
src/testcases/org/apache/poi/hssf/usermodel/TestHSSFDateUtil.java

index 3660da72e47f15b10c5912b33dbedff585a63bbe..77f9608e60bfd822dde478456a79913d7221b584 100644 (file)
@@ -37,6 +37,7 @@
 
                <!-- Don't forget to update status.xml too! -->
         <release version="3.1.1-alpha1" date="2008-??-??">
+           <action dev="POI-DEVELOPERS" type="fix">44953 - Extensive fixes for data validation</action>
            <action dev="POI-DEVELOPERS" type="fix">45519 - Fixed to keep datavalidation records together</action>
            <action dev="POI-DEVELOPERS" type="add">Support for creating new HSLF CurrentUserAtoms</action>
            <action dev="POI-DEVELOPERS" type="add">45466 - Partial support for removing excel comments (won't work for all excel versions yet)</action>
index 4927e7150d2738a5b6cc4d95ab301ecf782e2e6a..ef4274eeacc7be133a289ac3033a0264e3054f5b 100644 (file)
@@ -34,6 +34,7 @@
        <!-- Don't forget to update changes.xml too! -->
     <changes>
         <release version="3.1.1-alpha1" date="2008-??-??">
+           <action dev="POI-DEVELOPERS" type="fix">44953 - Extensive fixes for data validation</action>
            <action dev="POI-DEVELOPERS" type="fix">45519 - Fixed to keep datavalidation records together</action>
            <action dev="POI-DEVELOPERS" type="add">Support for creating new HSLF CurrentUserAtoms</action>
            <action dev="POI-DEVELOPERS" type="add">45466 - Partial support for removing excel comments (won't work for all excel versions yet)</action>
index 75b3aeb42eed04458ad7a0a2b7f8ae23d2c0eaec..7782d8ed45299fe526cd08a249ecd3d0b4c32d2d 100644 (file)
@@ -66,8 +66,11 @@ public final class FormulaParser {
     public static final int FORMULA_TYPE_CELL = 0;
     public static final int FORMULA_TYPE_SHARED = 1;
     public static final int FORMULA_TYPE_ARRAY =2;
-    public static final int FORMULA_TYPE_CONDFOMRAT = 3;
+    public static final int FORMULA_TYPE_CONDFORMAT = 3;
     public static final int FORMULA_TYPE_NAMEDRANGE = 4;
+    // this constant is currently very specific.  The exact differences from general data 
+    // validation formulas or conditional format formulas is not known yet
+    public static final int FORMULA_TYPE_DATAVALIDATION_LIST = 5;    
 
     private final String formulaString;
     private final int formulaLength;
@@ -75,12 +78,6 @@ public final class FormulaParser {
 
     private ParseNode _rootNode;
 
-    /**
-     * Used for spotting if we have a cell reference,
-     *  or a named range
-     */
-    private final static Pattern CELL_REFERENCE_PATTERN = Pattern.compile("(?:('?)[^:\\\\/\\?\\*\\[\\]]+\\1!)?\\$?[A-Za-z]+\\$?[\\d]+");
-
     private static char TAB = '\t';
 
     /**
@@ -112,9 +109,13 @@ public final class FormulaParser {
     }
 
     public static Ptg[] parse(String formula, HSSFWorkbook book) {
-        FormulaParser fp = new FormulaParser(formula, book);
+        return parse(formula, book, FORMULA_TYPE_CELL);
+    }
+
+    public static Ptg[] parse(String formula, HSSFWorkbook workbook, int formulaType) {
+        FormulaParser fp = new FormulaParser(formula, workbook);
         fp.parse();
-        return fp.getRPNPtg();
+        return fp.getRPNPtg(formulaType);
     }
 
     /** Read New Character From Input Stream */
index 07d2bd2fd623c343034a29462b5634a09fd870e6..9b5804f0c4a4ab2e8b845cd681424758640e6635 100644 (file)
@@ -66,6 +66,9 @@ final class OperandClassTransformer {
                        case FormulaParser.FORMULA_TYPE_CELL:
                                rootNodeOperandClass = Ptg.CLASS_VALUE;
                                break;
+                       case FormulaParser.FORMULA_TYPE_DATAVALIDATION_LIST:
+                               rootNodeOperandClass = Ptg.CLASS_REF;
+                               break;
                        default:
                                throw new RuntimeException("Incomplete code - formula type (" 
                                                + _formulaType + ") not supported yet");
index cbc732234fd401cfd663d04bf8f4235271ffd043..a90730ee55c262f04fb2682d778a4a655cf57f4a 100644 (file)
 
 package org.apache.poi.hssf.record;
 
-import java.io.IOException;
-import java.util.Enumeration;
-import java.util.Hashtable;
-import java.util.Stack;
-
+import org.apache.poi.hssf.record.UnicodeString.UnicodeRecordStats;
 import org.apache.poi.hssf.record.formula.Ptg;
-import org.apache.poi.hssf.util.HSSFCellRangeAddress;
-import org.apache.poi.hssf.util.HSSFCellRangeAddress.AddrStructure;
+import org.apache.poi.hssf.usermodel.DVConstraint;
+import org.apache.poi.hssf.usermodel.HSSFDataValidation;
+import org.apache.poi.hssf.util.CellRangeAddress;
+import org.apache.poi.hssf.util.CellRangeAddressList;
 import org.apache.poi.util.BitField;
 import org.apache.poi.util.LittleEndian;
-import org.apache.poi.util.StringUtil;
 
 /**
  * Title:        DATAVALIDATION Record (0x01BE)<p/>
@@ -35,606 +32,312 @@ import org.apache.poi.util.StringUtil;
  *               are stored in a sequential list of DV records. This list is followed by
  *               DVAL record(s)
  * @author Dragos Buleandra (dragos.buleandra@trade2b.ro)
- * @version 2.0-pre
+ * @author Josh Micich
  */
-public final class DVRecord extends Record
-{
-    public final static short sid = 0x01BE;
-
-    /**
-     * Option flags
-     */
-    private int                field_option_flags;
-
-    /**
-     * Title of the prompt box
-     */
-    private String             field_title_prompt;
-
-    /**
-     * Title of the error box
-     */
-    private String             field_title_error;
-
-    /**
-     * Text of the prompt box
-     */
-    private String             field_text_prompt;
-
-    /**
-     * Text of the error box
-     */
-    private String             field_text_error;
-
-    /**
-     * Size of the formula data for first condition
-     */
-    private short             field_size_first_formula;
-
-    /**
-     * Not used
-     */
-    private short             field_not_used_1 = 0x3FE0;
-
-    /**
-     * Formula data for first condition (RPN token array without size field)
-     */
-    private Stack             field_rpn_token_1 ;
-
-    /**
-     * Size of the formula data for second condition
-     */
-    private short             field_size_sec_formula;
-
-    /**
-     * Not used
-     */
-    private short             field_not_used_2 = 0x0000;
-
-    /**
-     * Formula data for second condition (RPN token array without size field)
-     */
-    private Stack             field_rpn_token_2 ;
-
-    /**
-     * Cell range address list with all affected ranges
-     */
-    private HSSFCellRangeAddress         field_regions;
-
-    public static final Integer STRING_PROMPT_TITLE = new Integer(0);
-    public static final Integer STRING_ERROR_TITLE  = new Integer(1);
-    public static final Integer STRING_PROMPT_TEXT  = new Integer(2);
-    public static final Integer STRING_ERROR_TEXT   = new Integer(3);
-    private Hashtable _hash_strings ;
-
-    /**
-     * Option flags field
-     * @see org.apache.poi.hssf.util.HSSFDataValidation utility class
-     */
-    private BitField          opt_data_type                    = new BitField(0x0000000F);
-    private BitField          opt_error_style                  = new BitField(0x00000070);
-    private BitField          opt_string_list_formula          = new BitField(0x00000080);
-    private BitField          opt_empty_cell_allowed           = new BitField(0x00000100);
-    private BitField          opt_suppress_dropdown_arrow      = new BitField(0x00000200);
-    private BitField          opt_show_prompt_on_cell_selected = new BitField(0x00040000);
-    private BitField          opt_show_error_on_invalid_value  = new BitField(0x00080000);
-    private BitField          opt_condition_operator           = new BitField(0x00F00000);
-
-    public DVRecord()
-    {
-    }
-
-    /**
-     * Constructs a DV record and sets its fields appropriately.
-     *
-     * @param in the RecordInputstream to read the record from
-     */
-
-    public DVRecord(RecordInputStream in)
-    {
-        super(in);
-    }
-
-    protected void validateSid(short id)
-    {
-        if (id != sid)
-        {
-            throw new RecordFormatException("NOT a valid DV RECORD");
-        }
-    }
-
-    protected void fillFields(RecordInputStream in)
-    {
-       field_rpn_token_1 = new Stack();
-       field_rpn_token_2 = new Stack();
-        
-       this.field_option_flags = in.readInt();
-       this._hash_strings = new Hashtable(4);
-       
-       StringHandler strHandler_prompt_title = new StringHandler( in );
-       this.field_title_prompt = strHandler_prompt_title.getStringData();
-       this._hash_strings.put(DVRecord.STRING_PROMPT_TITLE, strHandler_prompt_title);
-
-       StringHandler strHandler_error_title = new StringHandler( in );
-       this.field_title_error = strHandler_error_title.getStringData();
-       this._hash_strings.put(DVRecord.STRING_ERROR_TITLE, strHandler_error_title);
-
-       StringHandler strHandler_prompt_text = new StringHandler( in );
-       this.field_text_prompt = strHandler_prompt_text.getStringData();
-       this._hash_strings.put(DVRecord.STRING_PROMPT_TEXT, strHandler_prompt_text);
-
-       StringHandler strHandler_error_text = new StringHandler( in );
-       this.field_text_error = strHandler_error_text.getStringData();
-       this._hash_strings.put(DVRecord.STRING_ERROR_TEXT, strHandler_error_text);
-
-       this.field_size_first_formula = in.readShort(); 
-       this.field_not_used_1 = in.readShort();
-
-       //read first formula data condition
-       int token_pos = 0;
-       while (token_pos < this.field_size_first_formula)
-       {
-           Ptg ptg = Ptg.createPtg(in);
-           token_pos += ptg.getSize();
-           field_rpn_token_1.push(ptg);
-       }
-
-       this.field_size_sec_formula = in.readShort(); 
-       this.field_not_used_2 = in.readShort();
-
-       //read sec formula data condition
-       if (false) { // TODO - prior to bug 44710 this 'skip' was being executed. write a junit to confirm this fix
-           try {
-               in.skip(this.field_size_sec_formula);
-           } catch(IOException e) {
-               e.printStackTrace();
-               throw new IllegalStateException(e.getMessage());
-           }
-       }
-       token_pos = 0;
-       while (token_pos < this.field_size_sec_formula)
-       {
-           Ptg ptg = Ptg.createPtg(in);
-           token_pos += ptg.getSize();
-           field_rpn_token_2.push(ptg);
-       }
-
-       //read cell range address list with all affected ranges
-       this.field_regions = new HSSFCellRangeAddress(in);
-    }
-
-
-    // --> start option flags
-    /**
-     * set the condition data type
-     * @param type - condition data type
-     * @see org.apache.poi.hssf.util.HSSFDataValidation utility class
-     */
-    public void setDataType(int type)
-    {
-        this.field_option_flags =  this.opt_data_type.setValue(this.field_option_flags, type);
-    }
-
-    /**
-     * get the condition data type
-     * @return the condition data type
-     * @see org.apache.poi.hssf.util.HSSFDataValidation utility class
-     */
-    public int getDataType()
-    {
-       return this.opt_data_type.getValue(this.field_option_flags);
-    }
-
-    /**
-     * set the condition error style
-     * @param type - condition error style
-     * @see org.apache.poi.hssf.util.HSSFDataValidation utility class
-     */
-    public void setErrorStyle(int style)
-    {
-        this.field_option_flags =  this.opt_error_style.setValue(this.field_option_flags, style);
-    }
-
-    /**
-     * get the condition error style
-     * @return the condition error style
-     * @see org.apache.poi.hssf.util.HSSFDataValidation utility class
-     */
-    public int getErrorStyle()
-    {
-       return this.opt_error_style.getValue(this.field_option_flags);
-    }
-
-    /**
-     * set if in list validations the string list is explicitly given in the formula
-     * @param type - true if in list validations the string list is explicitly given in the formula; false otherwise
-     * @see org.apache.poi.hssf.util.HSSFDataValidation utility class
-     */
-    public void setListExplicitFormula(boolean explicit)
-    {
-        this.field_option_flags = this.opt_string_list_formula.setBoolean(this.field_option_flags, explicit);
-    }
-
-    /**
-     * return true if in list validations the string list is explicitly given in the formula, false otherwise
-     * @return true if in list validations the string list is explicitly given in the formula, false otherwise
-     * @see org.apache.poi.hssf.util.HSSFDataValidation utility class
-     */
-    public boolean getListExplicitFormula()
-    {
-       return (this.opt_string_list_formula.isSet(this.field_option_flags));
-    }
-
-    /**
-     * set if empty values are allowed in cells
-     * @param type - true if empty values are allowed in cells, false otherwise
-     * @see org.apache.poi.hssf.util.HSSFDataValidation utility class
-     */
-    public void setEmptyCellAllowed(boolean allowed)
-    {
-        this.field_option_flags =  this.opt_empty_cell_allowed.setBoolean(this.field_option_flags, allowed);
-    }
-
-    /**
-     * return true if empty values are allowed in cells, false otherwise
-     * @return if empty values are allowed in cells, false otherwise
-     * @see org.apache.poi.hssf.util.HSSFDataValidation utility class
-     */
-    public boolean getEmptyCellAllowed()
-    {
-       return (this.opt_empty_cell_allowed.isSet(this.field_option_flags));
-    }
-    /**
-     * @deprecated - (Jul-2008) use setSuppressDropDownArrow
-      */
-    public void setSurppresDropdownArrow(boolean suppress) {
-        setSuppressDropdownArrow(suppress);
-    }
-    /**
-     * @deprecated - (Jul-2008) use getSuppressDropDownArrow
-      */
-    public boolean getSurppresDropdownArrow() {
-        return getSuppressDropdownArrow();
-    }
-
-    /**
-     * set if drop down arrow should be suppressed when list validation is used
-     * @param type - true if drop down arrow should be suppressed when list validation is used, false otherwise
-     * @see org.apache.poi.hssf.util.HSSFDataValidation utility class
-     */
-    public void setSuppressDropdownArrow(boolean suppress)
-    {
-        this.field_option_flags =  this.opt_suppress_dropdown_arrow.setBoolean(this.field_option_flags, suppress);
-    }
-
-    /**
-     * return true if drop down arrow should be suppressed when list validation is used, false otherwise
-     * @return if drop down arrow should be suppressed when list validation is used, false otherwise
-     * @see org.apache.poi.hssf.util.HSSFDataValidation utility class
-     */
-    public boolean getSuppressDropdownArrow()
-    {
-       return (this.opt_suppress_dropdown_arrow.isSet(this.field_option_flags));
-    }
-
-    /**
-     * set if a prompt window should appear when cell is selected
-     * @param type - true if a prompt window should appear when cell is selected, false otherwise
-     * @see org.apache.poi.hssf.util.HSSFDataValidation utility class
-     */
-    public void setShowPromptOnCellSelected(boolean show)
-    {
-        this.field_option_flags =  this.opt_show_prompt_on_cell_selected.setBoolean(this.field_option_flags, show);
-    }
-
-    /**
-     * return true if a prompt window should appear when cell is selected, false otherwise
-     * @return if a prompt window should appear when cell is selected, false otherwise
-     * @see org.apache.poi.hssf.util.HSSFDataValidation utility class
-     */
-    public boolean getShowPromptOnCellSelected()
-    {
-       return (this.opt_show_prompt_on_cell_selected.isSet(this.field_option_flags));
-    }
-
-    /**
-     * set if an error window should appear when an invalid value is entered in the cell
-     * @param type - true if an error window should appear when an invalid value is entered in the cell, false otherwise
-     * @see org.apache.poi.hssf.util.HSSFDataValidation utility class
-     */
-    public void setShowErrorOnInvalidValue(boolean show)
-    {
-        this.field_option_flags =  this.opt_show_error_on_invalid_value.setBoolean(this.field_option_flags, show);
-    }
-
-    /**
-     * return true if an error window should appear when an invalid value is entered in the cell, false otherwise
-     * @return if an error window should appear when an invalid value is entered in the cell, false otherwise
-     * @see org.apache.poi.hssf.util.HSSFDataValidation utility class
-     */
-    public boolean getShowErrorOnInvalidValue()
-    {
-       return (this.opt_show_error_on_invalid_value.isSet(this.field_option_flags));
-    }
-
-    /**
-     * set the condition operator
-     * @param type - condition operator
-     * @see org.apache.poi.hssf.util.HSSFDataValidation utility class
-     */
-    public void setConditionOperator(int operator)
-    {
-        this.field_option_flags =  this.opt_condition_operator.setValue(this.field_option_flags, operator);
-    }
-
-    /**
-     * get the condition operator
-     * @return the condition operator
-     * @see org.apache.poi.hssf.util.HSSFDataValidation utility class
-     */
-    public int getConditionOperator()
-    {
-       return this.opt_condition_operator.getValue(this.field_option_flags);
-    }
-    // <-- end option flags
-
-    public void setFirstFormulaRPN( Stack rpn )
-    {
-        this.field_rpn_token_1 = rpn;
-    }
-
-    public void setFirstFormulaSize( short size )
-    {
-        this.field_size_first_formula = size;
-    }
-
-    public void setSecFormulaRPN( Stack rpn )
-    {
-        this.field_rpn_token_2 = rpn;
-    }
-
-    public void setSecFormulaSize( short size )
-    {
-        this.field_size_sec_formula = size;
-    }
-
-    public void setStringField( Integer type, String str_data )
-    {
-       if ( this._hash_strings == null )
-       {
-          this._hash_strings = new Hashtable();
-       }
-       StringHandler strHandler = new StringHandler();
-       if ( str_data == null )
-       {
-          str_data = "";
-       }
-       else
-       {
-          strHandler.setStringLength(str_data.length());
-       }
-       strHandler.setStringData(str_data);
-
-       strHandler.setUnicodeFlag((byte)0x00);
-       this._hash_strings.put( type, strHandler);
-    }
-
-    public String getStringField( Integer type )
-    {
-        return ((StringHandler)this._hash_strings.get(type)).getStringData();
-    }
-
-    public void setCellRangeAddress( HSSFCellRangeAddress range )
-    {
-        this.field_regions = range;
-    }
-
-    public HSSFCellRangeAddress getCellRangeAddress( )
-    {
-        return this.field_regions;
-    }
-
-    /**
-     * gets the option flags field.
-     * @return options - the option flags field
-     */
-    public int getOptionFlags()
-    {
-       return this.field_option_flags;
-    }
-
-    public String toString()
-    {
-      /** @todo DVRecord string representation */
-        StringBuffer sb = new StringBuffer();
-        sb.append("[DV]\n");
-        sb.append(" options=").append(Integer.toHexString(field_option_flags));
-        sb.append(" title-prompt=").append(field_title_prompt);
-        sb.append(" title-error=").append(field_title_error);
-        sb.append(" text-prompt=").append(field_text_prompt);
-        sb.append(" text-error=").append(field_text_error);
-        sb.append("\n");
-        appendFormula(sb, "Formula 1:",  field_rpn_token_1);
-        appendFormula(sb, "Formula 2:",  field_rpn_token_2);
-        int nRegions = field_regions.getADDRStructureNumber();
-        for(int i=0; i<nRegions; i++) {
-            AddrStructure addr = field_regions.getADDRStructureAt(i);
-            sb.append('(').append(addr.getFirstRow()).append(',').append(addr.getLastRow());
-            sb.append(',').append(addr.getFirstColumn()).append(',').append(addr.getLastColumn()).append(')');
-        }
-        sb.append("\n");
-        sb.append("[/DV]");
-
-        return sb.toString();
-    }
-
-    private void appendFormula(StringBuffer sb, String label, Stack stack) {
-        sb.append(label);
-        if (stack.isEmpty()) {
-            sb.append("<empty>\n");
-            return;
-        }
-        sb.append("\n");
-        Ptg[] ptgs = new Ptg[stack.size()];
-        stack.toArray(ptgs);
-        for (int i = 0; i < ptgs.length; i++) {
-            sb.append('\t').append(ptgs[i].toString()).append('\n');
-        }
-    }
-
-    public int serialize(int offset, byte [] data)
-    {
-        int size = this.getRecordSize();
-        LittleEndian.putShort(data, 0 + offset, sid);
-        LittleEndian.putShort(data, 2 + offset, ( short ) (size-4));
-
-        int pos = 4;
-        LittleEndian.putInt(data, pos + offset, this.getOptionFlags());
-        pos += 4;
-        pos += ((StringHandler)this._hash_strings.get( DVRecord.STRING_PROMPT_TITLE )).serialize(pos+offset, data);
-        pos += ((StringHandler)this._hash_strings.get( DVRecord.STRING_ERROR_TITLE )).serialize(pos+offset, data);
-        pos += ((StringHandler)this._hash_strings.get( DVRecord.STRING_PROMPT_TEXT )).serialize(pos+offset, data);
-        pos += ((StringHandler)this._hash_strings.get( DVRecord.STRING_ERROR_TEXT )).serialize(pos+offset, data);
-        LittleEndian.putShort(data, offset+pos, this.field_size_first_formula);
-        pos += 2;
-        LittleEndian.putShort(data, offset+pos, this.field_not_used_1);
-        pos += 2;
-
-        for (int k = 0; k < this.field_rpn_token_1.size(); k++)
-        {
-            Ptg ptg = ( Ptg ) this.field_rpn_token_1.get(k);
-            ptg.writeBytes(data, pos+offset);
-            pos += ptg.getSize();
-        }
-
-        LittleEndian.putShort(data, offset+pos, this.field_size_sec_formula);
-        pos += 2;
-        LittleEndian.putShort(data, offset+pos, this.field_not_used_2);
-        pos += 2;
-        if ( this.field_size_sec_formula > 0 )
-        {
-          for (int k = 0; k < this.field_rpn_token_2.size(); k++)
-          {
-              Ptg ptg = ( Ptg ) this.field_rpn_token_2.get(k);
-              ptg.writeBytes(data, pos+offset);
-              pos += ptg.getSize();
-          }
-        }
-        this.field_regions.serialize(pos+offset, data);
-        return size;
-    }
-
-    public int getRecordSize()
-    {
-        int size = 4+4+2+2+2+2;//header+options_field+first_formula_size+first_unused+sec_formula_size+sec+unused;
-        if ( this._hash_strings != null )
-        {
-            Enumeration enum_keys = this._hash_strings.keys();
-            while ( enum_keys.hasMoreElements() )
-            {
-                size += ((StringHandler)this._hash_strings.get( (Integer)enum_keys.nextElement() )).getSize();
-            }
-        }
-        size += this.field_size_first_formula+ this.field_size_sec_formula;
-        size += this.field_regions.getSize();
-        return size;
-    }
-
-    public short getSid()
-    {
-        return this.sid;
-    }
-    
-    /**
-     * Clones the object. Uses serialisation, as the
-     *  contents are somewhat complex
-     */
-    public Object clone() {
-        return cloneViaReserialise();
-    }
-
-    /**@todo DVRecord = Serializare */
-
-    private static final class StringHandler
-    {
-        private int     _string_length       = 0x0001;
-        private byte    _string_unicode_flag = 0x00;
-        private String  _string_data         = "0x00";
-        private int     _start_offset;
-        private int     _end_offset;
-
-        StringHandler()
-        {
-
-        }
-
-        StringHandler(RecordInputStream in)
-        {
-            this.fillFields(in);
-        }
-
-        protected void fillFields(RecordInputStream in) 
-        {
-            this._string_length       = in.readUShort(); 
-            this._string_unicode_flag = in.readByte(); 
-            if (this._string_unicode_flag == 1)
-            {
-                this._string_data = in.readUnicodeLEString(this._string_length);
-            }
-            else
-            {
-                this._string_data = in.readCompressedUnicode(this._string_length);
-            }
-        }
-
-        private void setStringData( String string_data )
-        {
-          this._string_data = string_data;
-        }
-
-        private String getStringData()
-        {
-            return this._string_data;
-        }
-
-        private int getEndOffset()
-        {
-            return this._end_offset;
-        }
-
-        public int serialize( int offset, byte[] data )
-        {
-            LittleEndian.putUShort(data, offset, this._string_length );
-            data[2 + offset] = this._string_unicode_flag;
-            if (this._string_unicode_flag == 1)
-            {
-                StringUtil.putUnicodeLE(this._string_data, data, 3 + offset);
-            }
-            else
-            {
-                StringUtil.putCompressedUnicode(this._string_data, data, 3 + offset);
-            }
-            return getSize();
-        }
-
-        private void setUnicodeFlag( byte flag )
-        {
-            this._string_unicode_flag = flag;
-        }
-
-        private void setStringLength( int len )
-        {
-           this._string_length = len;
-        }
-
-        private int getStringByteLength()
-        {
-            return (this._string_unicode_flag == 1) ? this._string_length * 2 : this._string_length;
-        }
-
-        public int getSize()
-        {
-            return 2 + 1 + getStringByteLength();
-        }
-    }
+public final class DVRecord extends Record {
+       public final static short sid = 0x01BE;
+       
+       /** the unicode string used for error/prompt title/text when not present */
+       private static final UnicodeString NULL_TEXT_STRING = new UnicodeString("\0");
+
+       /** Option flags */
+       private int _option_flags;
+       /** Title of the prompt box */
+       private UnicodeString _promptTitle;
+       /** Title of the error box */
+       private UnicodeString _errorTitle;
+       /** Text of the prompt box */
+       private UnicodeString _promptText;
+       /** Text of the error box */
+       private UnicodeString _errorText;
+       /** Not used - Excel seems to always write 0x3FE0 */
+       private short _not_used_1 = 0x3FE0;
+       /** Formula data for first condition (RPN token array without size field) */
+       private Ptg[] _formula1;
+       /** Not used - Excel seems to always write 0x0000 */
+       private short _not_used_2 = 0x0000;
+       /** Formula data for second condition (RPN token array without size field) */
+       private Ptg[] _formula2;
+       /** Cell range address list with all affected ranges */
+       private CellRangeAddressList _regions;
+
+       /**
+        * Option flags field
+        * 
+        * @see org.apache.poi.hssf.util.HSSFDataValidation utility class
+        */
+       private static final BitField opt_data_type                    = new BitField(0x0000000F);
+       private static final BitField opt_error_style                  = new BitField(0x00000070);
+       private static final BitField opt_string_list_formula          = new BitField(0x00000080);
+       private static final BitField opt_empty_cell_allowed           = new BitField(0x00000100);
+       private static final BitField opt_suppress_dropdown_arrow      = new BitField(0x00000200);
+       private static final BitField opt_show_prompt_on_cell_selected = new BitField(0x00040000);
+       private static final BitField opt_show_error_on_invalid_value  = new BitField(0x00080000);
+       private static final BitField opt_condition_operator           = new BitField(0x00700000);
+
+       /**
+        * Constructs a DV record and sets its fields appropriately.
+        *
+        * @param in the RecordInputstream to read the record from
+        */
+       public DVRecord(RecordInputStream in) {
+               super(in);
+       }
+
+       public DVRecord(int validationType, int operator, int errorStyle, boolean emptyCellAllowed,
+                       boolean suppressDropDownArrow, boolean isExplicitList,
+                       boolean showPromptBox, String promptTitle, String promptText, 
+                       boolean showErrorBox, String errorTitle, String errorText,
+                       Ptg[] formula1, Ptg[] formula2,
+                       CellRangeAddressList regions) {
+               
+               int flags = 0;
+               flags = opt_data_type.setValue(flags, validationType);
+               flags = opt_condition_operator.setValue(flags, operator);
+               flags = opt_error_style.setValue(flags, errorStyle);
+               flags = opt_empty_cell_allowed.setBoolean(flags, emptyCellAllowed);
+               flags = opt_suppress_dropdown_arrow.setBoolean(flags, suppressDropDownArrow);
+               flags = opt_string_list_formula.setBoolean(flags, isExplicitList);
+               flags = opt_show_prompt_on_cell_selected.setBoolean(flags, showPromptBox);
+               flags = opt_show_error_on_invalid_value.setBoolean(flags, showErrorBox);
+               _option_flags = flags;
+               _promptTitle = resolveTitleText(promptTitle);
+               _promptText = resolveTitleText(promptText);
+               _errorTitle = resolveTitleText(errorTitle);
+               _errorText = resolveTitleText(errorText);
+               _formula1 = formula1;
+               _formula2 = formula2;
+               _regions = regions;
+       }
+
+       protected void validateSid(short id) {
+               if (id != sid) {
+                       throw new RecordFormatException("NOT a valid DV RECORD");
+               }
+       }
+
+       protected void fillFields(RecordInputStream in) {
+               
+          _option_flags = in.readInt();
+          
+          _promptTitle = readUnicodeString(in);
+          _errorTitle = readUnicodeString(in);
+          _promptText = readUnicodeString(in);
+          _errorText = readUnicodeString(in);
+
+          int field_size_first_formula = in.readUShort(); 
+          _not_used_1 = in.readShort();
+
+          //read first formula data condition
+          _formula1 = Ptg.readTokens(field_size_first_formula, in);
+
+          int field_size_sec_formula = in.readUShort(); 
+          _not_used_2 = in.readShort();
+
+          //read sec formula data condition
+          _formula2 = Ptg.readTokens(field_size_sec_formula, in);
+
+          //read cell range address list with all affected ranges
+          _regions = new CellRangeAddressList(in);
+       }
+
+       // --> start option flags
+       /**
+        * @return the condition data type
+        * @see DVConstraint.ValidationType
+        */
+       public int getDataType() {
+          return opt_data_type.getValue(_option_flags);
+       }
+
+       /**
+        * @return the condition error style
+        * @see HSSFDataValidation.ErrorStyle
+        */
+       public int getErrorStyle() {
+          return opt_error_style.getValue(_option_flags);
+       }
+
+       /**
+        * @return <code>true</code> if in list validations the string list is explicitly given in the
+        *  formula, <code>false</code> otherwise
+         */
+       public boolean getListExplicitFormula() {
+          return (opt_string_list_formula.isSet(_option_flags));
+       }
+
+       /**
+        * @return <code>true</code> if empty values are allowed in cells, <code>false</code> otherwise
+        */
+       public boolean getEmptyCellAllowed() {
+          return (opt_empty_cell_allowed.isSet(_option_flags));
+       }
+
+
+       /**
+         * @return <code>true</code> if drop down arrow should be suppressed when list validation is
+         * used, <code>false</code> otherwise
+        */
+       public boolean getSuppressDropdownArrow() {
+          return (opt_suppress_dropdown_arrow.isSet(_option_flags));
+       }
+
+       /**
+        * @return <code>true</code> if a prompt window should appear when cell is selected, <code>false</code> otherwise
+        */
+       public boolean getShowPromptOnCellSelected() {
+          return (opt_show_prompt_on_cell_selected.isSet(_option_flags));
+       }
+
+       /**
+        * @return <code>true</code> if an error window should appear when an invalid value is entered
+        *  in the cell, <code>false</code> otherwise
+        */
+       public boolean getShowErrorOnInvalidValue() {
+          return (opt_show_error_on_invalid_value.isSet(_option_flags));
+       }
+
+       /**
+        * get the condition operator
+        * @return the condition operator
+        * @see org.apache.poi.hssf.util.HSSFDataValidation utility class
+        */
+       public int getConditionOperator() {
+          return opt_condition_operator.getValue(_option_flags);
+       }
+       // <-- end option flags
+
+
+
+
+       public CellRangeAddressList getCellRangeAddress() {
+               return this._regions;
+       }
+
+
+       public String toString() {
+               StringBuffer sb = new StringBuffer();
+               sb.append("[DV]\n");
+               sb.append(" options=").append(Integer.toHexString(_option_flags));
+               sb.append(" title-prompt=").append(formatTextTitle(_promptTitle));
+               sb.append(" title-error=").append(formatTextTitle(_errorTitle));
+               sb.append(" text-prompt=").append(formatTextTitle(_promptText));
+               sb.append(" text-error=").append(formatTextTitle(_errorText));
+               sb.append("\n");
+               appendFormula(sb, "Formula 1:",  _formula1);
+               appendFormula(sb, "Formula 2:",  _formula2);
+               sb.append("Regions: ");
+               int nRegions = _regions.getADDRStructureNumber();
+               for(int i=0; i<nRegions; i++) {
+                       if (i>0) {
+                               sb.append(", ");
+                       }
+                       CellRangeAddress addr = _regions.getCellRangeAddress(i);
+                       sb.append('(').append(addr.getFirstRow()).append(',').append(addr.getLastRow());
+                       sb.append(',').append(addr.getFirstColumn()).append(',').append(addr.getLastColumn()).append(')');
+               }
+               sb.append("\n");
+               sb.append("[/DV]");
+
+               return sb.toString();
+       }
+
+       private static String formatTextTitle(UnicodeString us) {
+               String str = us.getString();
+               if (str.length() == 1 && str.charAt(0) == '\0') {
+                       return "'\\0'";
+               }
+               return str;
+       }
+
+       private void appendFormula(StringBuffer sb, String label, Ptg[] ptgs) {
+               sb.append(label);
+               if (ptgs.length < 1) {
+                       sb.append("<empty>\n");
+                       return;
+               }
+               sb.append("\n");
+               for (int i = 0; i < ptgs.length; i++) {
+                       sb.append('\t').append(ptgs[i].toString()).append('\n');
+               }
+       }
+
+       public int serialize(int offset, byte [] data) {
+               int size = this.getRecordSize();
+               LittleEndian.putShort(data, 0 + offset, sid);
+               LittleEndian.putShort(data, 2 + offset, ( short ) (size-4));
+
+               int pos = 4;
+               LittleEndian.putInt(data, pos + offset, _option_flags);
+               pos += 4;
+               
+               pos += serializeUnicodeString(_promptTitle, pos+offset, data);
+               pos += serializeUnicodeString(_errorTitle, pos+offset, data);
+               pos += serializeUnicodeString(_promptText, pos+offset, data);
+               pos += serializeUnicodeString(_errorText, pos+offset, data);
+               LittleEndian.putUShort(data, offset+pos, Ptg.getEncodedSize(_formula1));
+               pos += 2;
+               LittleEndian.putUShort(data, offset+pos, _not_used_1);
+               pos += 2;
+
+               pos += Ptg.serializePtgs(_formula1, data, pos+offset);
+
+               LittleEndian.putUShort(data, offset+pos, Ptg.getEncodedSize(_formula2));
+               pos += 2;
+               LittleEndian.putShort(data, offset+pos, _not_used_2);
+               pos += 2;
+               pos += Ptg.serializePtgs(_formula2, data, pos+offset);
+               _regions.serialize(pos+offset, data);
+               return size;
+       }
+
+       /**
+        * When entered via the UI, Excel translates empty string into "\0"
+        * While it is possible to encode the title/text as empty string (Excel doesn't exactly crash),
+        * the resulting tool-tip text / message box looks wrong.  It is best to do the same as the 
+        * Excel UI and encode 'not present' as "\0". 
+        */
+       private static UnicodeString resolveTitleText(String str) {
+               if (str == null || str.length() < 1) {
+                       return NULL_TEXT_STRING;
+               }
+               return new UnicodeString(str);
+       }
+       
+       private static UnicodeString readUnicodeString(RecordInputStream in) {
+               return new UnicodeString(in);
+       }
+
+       private static int serializeUnicodeString(UnicodeString us, int offset, byte[] data) {
+               UnicodeRecordStats urs = new UnicodeRecordStats();
+               us.serialize(urs, offset, data);
+               return urs.recordSize;
+       }
+       private static int getUnicodeStringSize(UnicodeString str) {
+               return 3 + str.getString().length();
+       }
+
+       public int getRecordSize()  {
+               int size = 4+4+2+2+2+2;//header+options_field+first_formula_size+first_unused+sec_formula_size+sec+unused;
+               size += getUnicodeStringSize(_promptTitle);
+               size += getUnicodeStringSize(_errorTitle);
+               size += getUnicodeStringSize(_promptText);
+               size += getUnicodeStringSize(_errorText);
+               size += Ptg.getEncodedSize(_formula1);
+               size += Ptg.getEncodedSize(_formula2);
+               size += _regions.getSize();
+               return size;
+       }
+
+       public short getSid() {
+               return sid;
+       }
+       
+       /**
+        * Clones the object. Uses serialisation, as the
+        *  contents are somewhat complex
+        */
+       public Object clone() {
+               return cloneViaReserialise();
+       }
 }
index b3d2069ca996e89d94103f36d6b9a29973bee903..133386d7dfd5b59e7c929dcdcc718ebcdab585d1 100644 (file)
@@ -19,9 +19,7 @@ package org.apache.poi.hssf.record.aggregates;
 \r
 import java.util.ArrayList;\r
 import java.util.List;\r
-import java.util.Stack;\r
 \r
-import org.apache.poi.hssf.model.FormulaParser;\r
 import org.apache.poi.hssf.model.RecordStream;\r
 import org.apache.poi.hssf.record.CFHeaderRecord;\r
 import org.apache.poi.hssf.record.CFRuleRecord;\r
@@ -34,10 +32,6 @@ import org.apache.poi.hssf.record.PaneRecord;
 import org.apache.poi.hssf.record.Record;\r
 import org.apache.poi.hssf.record.SelectionRecord;\r
 import org.apache.poi.hssf.record.WindowTwoRecord;\r
-import org.apache.poi.hssf.record.formula.Ptg;\r
-import org.apache.poi.hssf.usermodel.HSSFWorkbook;\r
-import org.apache.poi.hssf.util.HSSFCellRangeAddress;\r
-import org.apache.poi.hssf.util.HSSFDataValidation;\r
 \r
 /**\r
  * Manages the DVALRecord and DVRecords for a single sheet<br/>\r
@@ -177,63 +171,7 @@ public final class DataValidityTable extends RecordAggregate {
                return false;\r
        }\r
 \r
-       public void addDataValidation(HSSFDataValidation dataValidation, HSSFWorkbook workbook) {\r
-\r
-               DVRecord dvRecord = new DVRecord();\r
-\r
-               // dv record's option flags\r
-               dvRecord.setDataType(dataValidation.getDataValidationType());\r
-               dvRecord.setErrorStyle(dataValidation.getErrorStyle());\r
-               dvRecord.setEmptyCellAllowed(dataValidation.getEmptyCellAllowed());\r
-               dvRecord.setSuppressDropdownArrow(dataValidation.getSuppressDropDownArrow());\r
-               dvRecord.setShowPromptOnCellSelected(dataValidation.getShowPromptBox());\r
-               dvRecord.setShowErrorOnInvalidValue(dataValidation.getShowErrorBox());\r
-               dvRecord.setConditionOperator(dataValidation.getOperator());\r
-\r
-               // string fields\r
-               dvRecord.setStringField(DVRecord.STRING_PROMPT_TITLE, dataValidation.getPromptBoxTitle());\r
-               dvRecord.setStringField(DVRecord.STRING_PROMPT_TEXT, dataValidation.getPromptBoxText());\r
-               dvRecord.setStringField(DVRecord.STRING_ERROR_TITLE, dataValidation.getErrorBoxTitle());\r
-               dvRecord.setStringField(DVRecord.STRING_ERROR_TEXT, dataValidation.getErrorBoxText());\r
-\r
-               // formula fields ( size and data )\r
-               Stack ptg_arr = new Stack();\r
-               Ptg[] ptg = FormulaParser.parse(dataValidation.getFirstFormula(), workbook);\r
-               int size = 0;\r
-               for (int k = 0; k < ptg.length; k++) {\r
-                       if (ptg[k] instanceof org.apache.poi.hssf.record.formula.AreaPtg) {\r
-                               // we should set ptgClass to Ptg.CLASS_REF and explicit formula\r
-                               // string to false\r
-                               // ptg[k].setClass(Ptg.CLASS_REF);\r
-                               // obj_validation.setExplicitListFormula(false);\r
-                       }\r
-                       size += ptg[k].getSize();\r
-                       ptg_arr.push(ptg[k]);\r
-               }\r
-               dvRecord.setFirstFormulaRPN(ptg_arr);\r
-               dvRecord.setFirstFormulaSize((short) size);\r
-\r
-               dvRecord.setListExplicitFormula(dataValidation.getExplicitListFormula());\r
-\r
-               if (dataValidation.getSecondFormula() != null) {\r
-\r
-                       ptg_arr = new Stack();\r
-                       ptg = FormulaParser.parse(dataValidation.getSecondFormula(), workbook);\r
-                       size = 0;\r
-                       for (int k = 0; k < ptg.length; k++) {\r
-                               size += ptg[k].getSize();\r
-                               ptg_arr.push(ptg[k]);\r
-                       }\r
-                       dvRecord.setSecFormulaRPN(ptg_arr);\r
-                       dvRecord.setSecFormulaSize((short) size);\r
-               }\r
-\r
-               // dv records cell range field\r
-               HSSFCellRangeAddress cell_range = new HSSFCellRangeAddress();\r
-               cell_range.addADDRStructure(dataValidation.getFirstRow(), dataValidation.getFirstColumn(),\r
-                               dataValidation.getLastRow(), dataValidation.getLastColumn());\r
-               dvRecord.setCellRangeAddress(cell_range);\r
-\r
+       public void addDataValidation(DVRecord dvRecord) {\r
                _validationList.add(dvRecord);\r
                _headerRec.setDVRecNo(_validationList.size());\r
        }\r
index 02b00750110889e602549c1ef73028eebd1f90ff..84d4a1704d84b2375d2f0fa25c1885eb945db75d 100644 (file)
@@ -36,7 +36,7 @@ public final class NumberPtg extends ScalarConstantPtg {
     /** Create a NumberPtg from a byte array read from disk */
     public NumberPtg(RecordInputStream in)
     {
-        field_1_value = in.readDouble();
+        this(in.readDouble());
     }
     
     /** Create a NumberPtg from a string representation of  the number
@@ -45,9 +45,12 @@ public final class NumberPtg extends ScalarConstantPtg {
      *  @param value : String representation of a floating point number
      */
     public NumberPtg(String value) {
-        field_1_value = Double.parseDouble(value);
+        this(Double.parseDouble(value));
     }
     
+    public NumberPtg(double value) {
+        field_1_value = value;
+    }
     
     public double getValue()
     {
@@ -67,6 +70,15 @@ public final class NumberPtg extends ScalarConstantPtg {
 
     public String toFormulaString(HSSFWorkbook book)
     {
-        return "" + getValue();
+        // TODO - java's rendering of double values is not quite same as excel's
+        return String.valueOf(field_1_value);
+    }
+    
+    public String toString() {
+        StringBuffer sb = new StringBuffer(64);
+        sb.append(getClass().getName()).append(" [");
+        sb.append(field_1_value);
+        sb.append("]");
+        return sb.toString();
     }
 }
index 7a882e4ae01c12549a16e8c15f4ba5ae064dc3aa..d1ea411cb9b89c0e296d4d3d4ed7fc64ea641399 100644 (file)
@@ -41,6 +41,7 @@ import org.apache.poi.hssf.usermodel.HSSFWorkbook;
  * @author Jason Height (jheight at chariot dot net dot au)
  */
 public abstract class Ptg implements Cloneable {
+       public static final Ptg[] EMPTY_PTG_ARRAY = { }; 
 
        /* convert infix order ptg list to rpn order ptg list
         * @return List ptgs in RPN order
@@ -250,6 +251,9 @@ public abstract class Ptg implements Cloneable {
                }
        }
        private static Ptg[] toPtgArray(List l) {
+               if (l.isEmpty()) {
+                       return EMPTY_PTG_ARRAY;
+               }
                Ptg[] result = new Ptg[l.size()];
                l.toArray(result);
                return result;
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 (file)
index 0000000..d4f1473
--- /dev/null
@@ -0,0 +1,479 @@
+/* ====================================================================
+   Copyright 2002-2004   Apache Software Foundation
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+==================================================================== */
+
+package org.apache.poi.hssf.usermodel;
+
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+
+import org.apache.poi.hssf.model.FormulaParser;
+import org.apache.poi.hssf.record.formula.NumberPtg;
+import org.apache.poi.hssf.record.formula.Ptg;
+import org.apache.poi.hssf.record.formula.StringPtg;
+
+/**
+ * 
+ * @author Josh Micich
+ */
+public class DVConstraint {
+       /**
+        * ValidationType enum
+        */
+       public static final class ValidationType {
+               private ValidationType() {
+                       // no instances of this class
+               }
+       /** 'Any value' type - value not restricted */
+       public static final int ANY         = 0x00;
+       /** Integer ('Whole number') type */
+       public static final int INTEGER     = 0x01;
+       /** Decimal type */
+       public static final int DECIMAL     = 0x02;
+       /** List type ( combo box type ) */
+       public static final int LIST        = 0x03;
+       /** Date type */
+       public static final int DATE        = 0x04;
+       /** Time type */
+       public static final int TIME        = 0x05;
+       /** String length type */
+       public static final int TEXT_LENGTH = 0x06;
+       /** Formula ( 'Custom' ) type */
+       public static final int FORMULA     = 0x07;
+       }
+       /**
+        * Condition operator enum
+        */
+       public static final class OperatorType {
+               private OperatorType() {
+                       // no instances of this class
+               }
+
+               public static final int BETWEEN = 0x00;
+               public static final int NOT_BETWEEN = 0x01;
+               public static final int EQUAL = 0x02;
+               public static final int NOT_EQUAL = 0x03;
+               public static final int GREATER_THAN = 0x04;
+               public static final int LESS_THAN = 0x05;
+               public static final int GREATER_OR_EQUAL = 0x06;
+               public static final int LESS_OR_EQUAL = 0x07;
+               /** default value to supply when the operator type is not used */
+               public static final int IGNORED = BETWEEN;
+               
+               /* package */ static void validateSecondArg(int comparisonOperator, String paramValue) {
+                       switch (comparisonOperator) {
+                               case BETWEEN:
+                               case NOT_BETWEEN:
+                                       if (paramValue == null) {
+                                               throw new IllegalArgumentException("expr2 must be supplied for 'between' comparisons");
+                                       }
+                               // all other operators don't need second arg
+                       }
+               }
+       }
+       
+       /* package */ static final class FormulaPair {
+
+               private final Ptg[] _formula1;
+               private final Ptg[] _formula2;
+
+               public FormulaPair(Ptg[] formula1, Ptg[] formula2) {
+                       _formula1 = formula1;
+                       _formula2 = formula2;
+               }
+               public Ptg[] getFormula1() {
+                       return _formula1;
+               }
+               public Ptg[] getFormula2() {
+                       return _formula2;
+               }
+               
+       }
+       
+       // convenient access to ValidationType namespace
+       private static final ValidationType VT = null;
+
+       
+       private final int _validationType;
+       private int _operator;
+       private String[] _explicitListValues;
+       
+       private String _formula1;
+       private String _formula2;
+       private Double _value1;
+       private Double _value2;
+
+       
+       private DVConstraint(int validationType, int comparisonOperator, String formulaA,
+                       String formulaB, Double value1, Double value2, String[] excplicitListValues) {
+               _validationType = validationType;
+               _operator = comparisonOperator;
+               _formula1 = formulaA;
+               _formula2 = formulaB;
+               _value1 = value1;
+               _value2 = value2;
+               _explicitListValues = excplicitListValues;
+       }
+       
+       
+       /**
+        * Creates a list constraint
+        */
+       private DVConstraint(String listFormula, String[] excplicitListValues) {
+               this(ValidationType.LIST, OperatorType.IGNORED,
+                       listFormula, null, null, null, excplicitListValues);
+       }
+
+       /**
+        * Creates a number based data validation constraint. The text values entered for expr1 and expr2
+        * can be either standard Excel formulas or formatted number values. If the expression starts 
+        * with '=' it is parsed as a formula, otherwise it is parsed as a formatted number. 
+        * 
+        * @param validationType one of {@link ValidationType#ANY}, {@link ValidationType#DECIMAL},
+        * {@link ValidationType#INTEGER}, {@link ValidationType#TEXT_LENGTH}
+        * @param comparisonOperator any constant from {@link OperatorType} enum
+        * @param expr1 date formula (when first char is '=') or formatted number value
+        * @param expr2 date formula (when first char is '=') or formatted number value
+        */
+       public static DVConstraint createNumericConstraint(int validationType, int comparisonOperator, 
+                       String expr1, String expr2) {
+               switch (validationType) {
+                       case ValidationType.ANY:
+                               if (expr1 != null || expr2 != null) {
+                                       throw new IllegalArgumentException("expr1 and expr2 must be null for validation type 'any'");
+                               }
+                               break;
+                       case ValidationType.DECIMAL:
+                       case ValidationType.INTEGER:
+                       case ValidationType.TEXT_LENGTH:
+                               if (expr1 == null) {
+                                       throw new IllegalArgumentException("expr1 must be supplied");
+                               }
+                               OperatorType.validateSecondArg(comparisonOperator, expr2);
+                               break;
+                       default:
+                               throw new IllegalArgumentException("Validation Type ("
+                                               + validationType + ") not supported with this method");
+               }
+               // formula1 and value1 are mutually exclusive
+               String formula1 = getFormulaFromTextExpression(expr1);
+               Double value1 = formula1 == null ? convertNumber(expr1) : null;
+               // formula2 and value2 are mutually exclusive
+               String formula2 = getFormulaFromTextExpression(expr2);
+               Double value2 = formula2 == null ? convertNumber(expr2) : null;
+               return new DVConstraint(validationType, comparisonOperator, formula1, formula2, value1, value2, null);
+       }
+
+       public static DVConstraint createFormulaListConstraint(String listFormula) {
+               return new DVConstraint(listFormula, null);
+       }
+       public static DVConstraint createExplicitListConstraint(String[] explicitListValues) {
+               return new DVConstraint(null, explicitListValues);
+       }
+       
+       
+       /**
+        * Creates a time based data validation constraint. The text values entered for expr1 and expr2
+        * can be either standard Excel formulas or formatted time values. If the expression starts 
+        * with '=' it is parsed as a formula, otherwise it is parsed as a formatted time.  To parse 
+        * formatted times, two formats are supported:  "HH:MM" or "HH:MM:SS".  This is contrary to 
+        * Excel which uses the default time format from the OS.
+        * 
+        * @param comparisonOperator constant from {@link OperatorType} enum
+        * @param expr1 date formula (when first char is '=') or formatted time value
+        * @param expr2 date formula (when first char is '=') or formatted time value
+        */
+       public static DVConstraint createTimeConstraint(int comparisonOperator, String expr1, String expr2) {
+               if (expr1 == null) {
+                       throw new IllegalArgumentException("expr1 must be supplied");
+               }
+               OperatorType.validateSecondArg(comparisonOperator, expr1);
+               
+               // formula1 and value1 are mutually exclusive
+               String formula1 = getFormulaFromTextExpression(expr1);
+               Double value1 = formula1 == null ? convertTime(expr1) : null;
+               // formula2 and value2 are mutually exclusive
+               String formula2 = getFormulaFromTextExpression(expr2);
+               Double value2 = formula2 == null ? convertTime(expr2) : null;
+               return new DVConstraint(VT.TIME, comparisonOperator, formula1, formula2, value1, value2, null);
+               
+       }
+       /**
+        * Creates a date based data validation constraint. The text values entered for expr1 and expr2
+        * can be either standard Excel formulas or formatted date values. If the expression starts 
+        * with '=' it is parsed as a formula, otherwise it is parsed as a formatted date (Excel uses 
+        * the same convention).  To parse formatted dates, a date format needs to be specified.  This
+        * is contrary to Excel which uses the default short date format from the OS.
+        * 
+        * @param comparisonOperator constant from {@link OperatorType} enum
+        * @param expr1 date formula (when first char is '=') or formatted date value
+        * @param expr2 date formula (when first char is '=') or formatted date value
+        * @param dateFormat ignored if both expr1 and expr2 are formulas.  Default value is "YYYY/MM/DD"
+        * otherwise any other valid argument for <tt>SimpleDateFormat</tt> can be used
+        * @see <a href='http://java.sun.com/j2se/1.5.0/docs/api/java/text/DateFormat.html'>SimpleDateFormat</a>
+        */
+       public static DVConstraint createDateConstraint(int comparisonOperator, String expr1, String expr2, String dateFormat) {
+               if (expr1 == null) {
+                       throw new IllegalArgumentException("expr1 must be supplied");
+               }
+               OperatorType.validateSecondArg(comparisonOperator, expr2);
+               SimpleDateFormat df = dateFormat == null ? null : new SimpleDateFormat(dateFormat);
+               
+               // formula1 and value1 are mutually exclusive
+               String formula1 = getFormulaFromTextExpression(expr1);
+               Double value1 = formula1 == null ? convertDate(expr1, df) : null;
+               // formula2 and value2 are mutually exclusive
+               String formula2 = getFormulaFromTextExpression(expr2);
+               Double value2 = formula2 == null ? convertDate(expr2, df) : null;
+               return new DVConstraint(VT.DATE, comparisonOperator, formula1, formula2, value1, value2, null);
+       }
+       
+       /**
+        * Distinguishes formula expressions from simple value expressions.  This logic is only 
+        * required by a few factory methods in this class that create data validation constraints
+        * from more or less the same parameters that would have been entered in the Excel UI.  The
+        * data validation dialog box uses the convention that formulas begin with '='.  Other methods
+        * in this class follow the POI convention (formulas and values are distinct), so the '=' 
+        * convention is not used there.
+        *  
+        * @param textExpr a formula or value expression
+        * @return all text after '=' if textExpr begins with '='. Otherwise <code>null</code> if textExpr does not begin with '='
+        */
+       private static String getFormulaFromTextExpression(String textExpr) {
+               if (textExpr == null) {
+                       return null;
+               }
+               if (textExpr.length() < 1) {
+                       throw new IllegalArgumentException("Empty string is not a valid formula/value expression");
+               }
+               if (textExpr.charAt(0) == '=') {
+                       return textExpr.substring(1);
+               }
+               return null;
+       }
+
+
+       /**
+        * @return <code>null</code> if numberStr is <code>null</code>
+        */
+       private static Double convertNumber(String numberStr) {
+               if (numberStr == null) {
+                       return null;
+               }
+               try {
+                       return new Double(numberStr);
+               } catch (NumberFormatException e) {
+                       throw new RuntimeException("The supplied text '" + numberStr 
+                                       + "' could not be parsed as a number");
+               }
+       }
+
+       /**
+        * @return <code>null</code> if timeStr is <code>null</code>
+        */
+       private static Double convertTime(String timeStr) {
+               if (timeStr == null) {
+                       return null;
+               }
+               return new Double(HSSFDateUtil.convertTime(timeStr));
+       }
+       /**
+        * @param dateFormat pass <code>null</code> for default YYYYMMDD
+        * @return <code>null</code> if timeStr is <code>null</code>
+        */
+       private static Double convertDate(String dateStr, SimpleDateFormat dateFormat) {
+               if (dateStr == null) {
+                       return null;
+               }
+               Date dateVal; 
+               if (dateFormat == null) {
+                       dateVal = HSSFDateUtil.parseYYYYMMDDDate(dateStr);
+               } else {
+                       try {
+                               dateVal = dateFormat.parse(dateStr);
+                       } catch (ParseException e) {
+                               throw new RuntimeException("Failed to parse date '" + dateStr 
+                                               + "' using specified format '" + dateFormat + "'", e);
+                       }
+               }
+               return new Double(HSSFDateUtil.getExcelDate(dateVal));
+       }
+
+       public static DVConstraint createFormulaConstraint(String formula) {
+               if (formula == null) {
+                       throw new IllegalArgumentException("formula must be supplied");
+               }
+               return new DVConstraint(VT.FORMULA, OperatorType.IGNORED, formula, null, null, null, null);
+       }
+       
+       /**
+        * @return both parsed formulas (for expression 1 and 2). 
+        */
+       /* package */ FormulaPair createFormulas(HSSFWorkbook workbook) {
+               Ptg[] formula1;
+               Ptg[] formula2;
+               if (isListValidationType()) {
+                       formula1 = createListFormula(workbook);
+                       formula2 = Ptg.EMPTY_PTG_ARRAY;
+               } else {
+               formula1 = convertDoubleFormula(_formula1, _value1, workbook);
+               formula2 = convertDoubleFormula(_formula2, _value2, workbook);
+               }
+               return new FormulaPair(formula1, formula2);
+       }
+
+       private Ptg[] createListFormula(HSSFWorkbook workbook) {
+
+               if (_explicitListValues == null) {
+                       // formula is parsed with slightly different RVA rules: (root node type must be 'reference')
+                       return FormulaParser.parse(_formula1, workbook, FormulaParser.FORMULA_TYPE_DATAVALIDATION_LIST);
+                       // To do: Excel places restrictions on the available operations within a list formula.
+                       // Some things like union and intersection are not allowed.
+               }
+               // explicit list was provided
+               StringBuffer sb = new StringBuffer(_explicitListValues.length * 16);
+               for (int i = 0; i < _explicitListValues.length; i++) {
+                       if (i > 0) {
+                               sb.append('\0'); // list delimiter is the nul char
+                       }
+                       sb.append(_explicitListValues[i]);
+               
+               }
+               return new Ptg[] { new StringPtg(sb.toString()), };
+       }
+
+       /**
+        * @return The parsed token array representing the formula or value specified. 
+        * Empty array if both formula and value are <code>null</code>
+        */
+       private static Ptg[] convertDoubleFormula(String formula, Double value, HSSFWorkbook workbook) {
+               if (formula == null) {
+                       if (value == null) {
+                               return Ptg.EMPTY_PTG_ARRAY;
+                       }
+                       return new Ptg[] { new NumberPtg(value.doubleValue()), };
+               }
+               if (value != null) {
+                       throw new IllegalStateException("Both formula and value cannot be present");
+               }
+               return FormulaParser.parse(formula, workbook);
+       }
+       
+       
+       /**
+        * @return data validation type of this constraint
+        * @see ValidationType
+        */
+       public int getValidationType() {
+               return _validationType;
+       }
+       /**
+        * Convenience method
+        * @return <code>true</code> if this constraint is a 'list' validation
+        */
+       public boolean isListValidationType() {
+               return _validationType == VT.LIST;
+       }
+       /**
+        * Convenience method
+        * @return <code>true</code> if this constraint is a 'list' validation with explicit values
+        */
+       public boolean isExplicitList() {
+               return _validationType == VT.LIST && _explicitListValues != null;
+       }
+       /**
+        * @return the operator used for this constraint
+        * @see OperatorType
+        */
+       public int getOperator() {
+               return _operator;
+       }
+       /**
+        * Sets the comparison operator for this constraint
+        * @see OperatorType
+        */
+       public void setOperator(int operator) {
+               _operator = operator;
+       }
+       
+       public String[] getExplicitListValues() {
+               return _explicitListValues;
+       }
+       public void setExplicitListValues(String[] explicitListValues) {
+               if (_validationType != VT.LIST) {
+                       throw new RuntimeException("Cannot setExplicitListValues on non-list constraint");
+               }
+               _formula1 = null;
+               _explicitListValues = explicitListValues;
+       }
+
+       /**
+        * @return the formula for expression 1. May be <code>null</code>
+        */
+       public String getFormula1() {
+               return _formula1;
+       }
+       /**
+        * Sets a formula for expression 1.
+        */
+       public void setFormula1(String formula1) {
+               _value1 = null;
+               _explicitListValues = null;
+               _formula1 = formula1;
+       }
+
+       /**
+        * @return the formula for expression 2. May be <code>null</code>
+        */
+       public String getFormula2() {
+               return _formula2;
+       }
+       /**
+        * Sets a formula for expression 2.
+        */
+       public void setFormula2(String formula2) {
+               _value2 = null;
+               _formula2 = formula2;
+       }
+
+       /**
+        * @return the numeric value for expression 1. May be <code>null</code>
+        */
+       public Double getValue1() {
+               return _value1;
+       }
+       /**
+        * Sets a numeric value for expression 1.
+        */
+       public void setValue1(double value1) {
+               _formula1 = null;
+               _value1 = new Double(value1);
+       }
+
+       /**
+        * @return the numeric value for expression 2. May be <code>null</code>
+        */
+       public Double getValue2() {
+               return _value2;
+       }
+       /**
+        * Sets a numeric value for expression 2.
+        */
+       public void setValue2(double value2) {
+               _formula2 = null;
+               _value2 = new Double(value2);
+       }
+}
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 (file)
index 0000000..3be3843
--- /dev/null
@@ -0,0 +1,235 @@
+/* ====================================================================
+   Copyright 2002-2004   Apache Software Foundation
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+==================================================================== */
+
+package org.apache.poi.hssf.usermodel;
+
+import org.apache.poi.hssf.record.DVRecord;
+import org.apache.poi.hssf.usermodel.DVConstraint.FormulaPair;
+import org.apache.poi.hssf.util.CellRangeAddressList;
+
+/**
+ *Utility class for creating data validation cells
+ * 
+ * @author Dragos Buleandra (dragos.buleandra@trade2b.ro)
+ */
+public final class HSSFDataValidation {
+       /**
+        * Error style constants for error box
+        */
+       public static final class ErrorStyle {
+       /** STOP style */
+       public static final int STOP    = 0x00;
+       /** WARNING style */
+       public static final int WARNING = 0x01;
+       /** INFO style */
+       public static final int INFO    = 0x02;
+       }    
+
+       private String _prompt_title;
+       private String _prompt_text;
+       private String _error_title;
+       private String _error_text;
+
+       private int _errorStyle = ErrorStyle.STOP;
+       private boolean _emptyCellAllowed = true;
+       private boolean _suppress_dropdown_arrow = false;
+       private boolean _showPromptBox = true;
+       private boolean _showErrorBox = true;
+       private final CellRangeAddressList _regions;
+       private DVConstraint _constraint;
+
+       /**
+        * Constructor which initializes the cell range on which this object will be
+        * applied
+        * @param constraint 
+        */
+       public HSSFDataValidation(CellRangeAddressList regions, DVConstraint constraint) {
+               _regions = regions;
+               _constraint = constraint;
+       }
+
+
+       public DVConstraint getConstraint() {
+               return _constraint;
+       }
+
+       /**
+        * Sets the error style for error box
+        * @see ErrorStyle
+        */
+       public void setErrorStyle(int error_style) {
+               _errorStyle = error_style;
+       }
+
+       /**
+        * @return the error style of error box
+        * @see ErrorStyle
+        */
+       public int getErrorStyle() {
+               return _errorStyle;
+       }
+
+       /**
+        * Sets if this object allows empty as a valid value
+        * 
+        * @param allowed <code>true</code> if this object should treats empty as valid value , <code>false</code>
+        *            otherwise
+        */
+       public void setEmptyCellAllowed(boolean allowed) {
+               _emptyCellAllowed = allowed;
+       }
+
+       /**
+        * Retrieve the settings for empty cells allowed
+        * 
+        * @return True if this object should treats empty as valid value , false
+        *         otherwise
+        */
+       public boolean getEmptyCellAllowed() {
+               return _emptyCellAllowed;
+       }
+
+       /**
+        * Useful for list validation objects .
+        * 
+        * @param suppress
+        *            True if a list should display the values into a drop down list ,
+        *            false otherwise . In other words , if a list should display
+        *            the arrow sign on its right side
+        */
+       public void setSuppressDropDownArrow(boolean suppress) {
+               _suppress_dropdown_arrow = suppress;
+       }
+
+       /**
+        * Useful only list validation objects . This method always returns false if
+        * the object isn't a list validation object
+        * 
+        * @return <code>true</code> if a list should display the values into a drop down list ,
+        *         <code>false</code> otherwise .
+        */
+       public boolean getSuppressDropDownArrow() {
+               if (_constraint.isListValidationType()) {
+                       return _suppress_dropdown_arrow;
+               }
+               return false;
+       }
+
+       /**
+        * Sets the behaviour when a cell which belongs to this object is selected
+        * 
+        * @param show <code>true</code> if an prompt box should be displayed , <code>false</code> otherwise
+        */
+       public void setShowPromptBox(boolean show) {
+               _showPromptBox = show;
+       }
+
+       /**
+        * @param show <code>true</code> if an prompt box should be displayed , <code>false</code> otherwise
+        */
+       public boolean getShowPromptBox() {
+               return _showPromptBox;
+       }
+
+       /**
+        * Sets the behaviour when an invalid value is entered
+        * 
+        * @param show <code>true</code> if an error box should be displayed , <code>false</code> otherwise
+        */
+       public void setShowErrorBox(boolean show) {
+               _showErrorBox = show;
+       }
+
+       /**
+        * @return <code>true</code> if an error box should be displayed , <code>false</code> otherwise
+        */
+       public boolean getShowErrorBox() {
+               return _showErrorBox;
+       }
+
+
+       /**
+        * Sets the title and text for the prompt box . Prompt box is displayed when
+        * the user selects a cell which belongs to this validation object . In
+        * order for a prompt box to be displayed you should also use method
+        * setShowPromptBox( boolean show )
+        * 
+        * @param title The prompt box's title
+        * @param text The prompt box's text
+        */
+       public void createPromptBox(String title, String text) {
+               _prompt_title = title;
+               _prompt_text = text;
+               this.setShowPromptBox(true);
+       }
+
+       /**
+        * @return Prompt box's title or <code>null</code>
+        */
+       public String getPromptBoxTitle() {
+               return _prompt_title;
+       }
+
+       /**
+        * @return Prompt box's text or <code>null</code>
+        */
+       public String getPromptBoxText() {
+               return _prompt_text;
+       }
+
+       /**
+        * Sets the title and text for the error box . Error box is displayed when
+        * the user enters an invalid value int o a cell which belongs to this
+        * validation object . In order for an error box to be displayed you should
+        * also use method setShowErrorBox( boolean show )
+        * 
+        * @param title The error box's title
+        * @param text The error box's text
+        */
+       public void createErrorBox(String title, String text) {
+               _error_title = title;
+               _error_text = text;
+               this.setShowErrorBox(true);
+       }
+
+       /**
+        * @return Error box's title or <code>null</code>
+        */
+       public String getErrorBoxTitle() {
+               return _error_title;
+       }
+
+       /**
+        * @return Error box's text or <code>null</code>
+        */
+       public String getErrorBoxText() {
+               return _error_text;
+       }
+
+       public DVRecord createDVRecord(HSSFWorkbook workbook) {
+
+               FormulaPair fp = _constraint.createFormulas(workbook);
+               
+               return new DVRecord(_constraint.getValidationType(),
+                               _constraint.getOperator(),
+                               _errorStyle, _emptyCellAllowed, getSuppressDropDownArrow(),
+                               _constraint.isExplicitList(),
+                               _showPromptBox, _prompt_title, _prompt_text,
+                               _showErrorBox, _error_title, _error_text,
+                               fp.getFormula1(), fp.getFormula2(),
+                               _regions);
+       }
+}
\ No newline at end of file
index ab353ca02c09b59200b1219a543fe3c5fccb1bc8..457f0b9ed19be9b09e390e96fba4292dff23a5e1 100644 (file)
 ==================================================================== */
 
 
-
-/*
- * DateUtil.java
- *
- * Created on January 19, 2002, 9:30 AM
- */
 package org.apache.poi.hssf.usermodel;
 
 import java.util.Calendar;
 import java.util.Date;
 import java.util.GregorianCalendar;
+import java.util.regex.Pattern;
 
 /**
  * Contains methods for dealing with Excel dates.
@@ -38,17 +33,20 @@ import java.util.GregorianCalendar;
  * @author  Alex Jacoby (ajacoby at gmail.com)
  * @author  Pavel Krupets (pkrupets at palmtreebusiness dot com)
  */
-
-public class HSSFDateUtil
-{
-    private HSSFDateUtil()
-    {
+public final class HSSFDateUtil {
+    private HSSFDateUtil() {
+        // no instances of this class
     }
+    private static final int SECONDS_PER_MINUTE = 60;
+    private static final int MINUTES_PER_HOUR = 60;
+    private static final int HOURS_PER_DAY = 24;
+    private static final int SECONDS_PER_DAY = (HOURS_PER_DAY * MINUTES_PER_HOUR * SECONDS_PER_MINUTE);
+
+    private static final int    BAD_DATE         = -1;   // used to specify that date is invalid
+    private static final long   DAY_MILLISECONDS = SECONDS_PER_DAY * 1000L;
+
+    private static final Pattern TIME_SEPARATOR_PATTERN = Pattern.compile(":");
 
-    private static final int    BAD_DATE         =
-        -1;   // used to specify that date is invalid
-    private static final long   DAY_MILLISECONDS = 24 * 60 * 60 * 1000;
-    
     /**
      * Given a Date, converts it into a double representing its internal Excel representation,
      *   which is the number of days since 1/1/1900. Fractional days represent hours, minutes, and seconds.
@@ -57,7 +55,7 @@ public class HSSFDateUtil
      * @param  date the Date
      */
     public static double getExcelDate(Date date) {
-       return getExcelDate(date, false);
+        return getExcelDate(date, false);
     }
     /**
      * Given a Date, converts it into a double representing its internal Excel representation,
@@ -74,8 +72,8 @@ public class HSSFDateUtil
     }
     /**
      * Given a Date in the form of a Calendar, converts it into a double
-     *  representing its internal Excel representation, which is the 
-     *  number of days since 1/1/1900. Fractional days represent hours, 
+     *  representing its internal Excel representation, which is the
+     *  number of days since 1/1/1900. Fractional days represent hours,
      *  minutes, and seconds.
      *
      * @return Excel representation of Date (-1 if error - test for error by checking for less than 0.1)
@@ -83,41 +81,40 @@ public class HSSFDateUtil
      * @param use1904windowing Should 1900 or 1904 date windowing be used?
      */
     public static double getExcelDate(Calendar date, boolean use1904windowing) {
-       // Don't alter the supplied Calendar as we do our work
-       return internalGetExcelDate( (Calendar)date.clone(), use1904windowing );
+        // Don't alter the supplied Calendar as we do our work
+        return internalGetExcelDate( (Calendar)date.clone(), use1904windowing );
     }
     private static double internalGetExcelDate(Calendar date, boolean use1904windowing) {
-        if ((!use1904windowing && date.get(Calendar.YEAR) < 1900) || 
-            (use1904windowing && date.get(Calendar.YEAR) < 1904)) 
+        if ((!use1904windowing && date.get(Calendar.YEAR) < 1900) ||
+            (use1904windowing && date.get(Calendar.YEAR) < 1904))
         {
             return BAD_DATE;
-        } else {
-           // Because of daylight time saving we cannot use
-           //     date.getTime() - calStart.getTimeInMillis()
-           // as the difference in milliseconds between 00:00 and 04:00
-           // can be 3, 4 or 5 hours but Excel expects it to always
-           // be 4 hours.
-           // E.g. 2004-03-28 04:00 CEST - 2004-03-28 00:00 CET is 3 hours
-           // and 2004-10-31 04:00 CET - 2004-10-31 00:00 CEST is 5 hours
-            double fraction = (((date.get(Calendar.HOUR_OF_DAY) * 60
-                                 + date.get(Calendar.MINUTE)
-                                ) * 60 + date.get(Calendar.SECOND)
-                               ) * 1000 + date.get(Calendar.MILLISECOND)
-                              ) / ( double ) DAY_MILLISECONDS;
-            Calendar calStart = dayStart(date);
-            
-            double value = fraction + absoluteDay(calStart, use1904windowing);
-            
-            if (!use1904windowing && value >= 60) {
-                value++;
-            } else if (use1904windowing) {
-                value--;
-            }
-            
-            return value;
         }
+        // Because of daylight time saving we cannot use
+        //     date.getTime() - calStart.getTimeInMillis()
+        // as the difference in milliseconds between 00:00 and 04:00
+        // can be 3, 4 or 5 hours but Excel expects it to always
+        // be 4 hours.
+        // E.g. 2004-03-28 04:00 CEST - 2004-03-28 00:00 CET is 3 hours
+        // and 2004-10-31 04:00 CET - 2004-10-31 00:00 CEST is 5 hours
+        double fraction = (((date.get(Calendar.HOUR_OF_DAY) * 60
+                             + date.get(Calendar.MINUTE)
+                            ) * 60 + date.get(Calendar.SECOND)
+                           ) * 1000 + date.get(Calendar.MILLISECOND)
+                          ) / ( double ) DAY_MILLISECONDS;
+        Calendar calStart = dayStart(date);
+
+        double value = fraction + absoluteDay(calStart, use1904windowing);
+
+        if (!use1904windowing && value >= 60) {
+            value++;
+        } else if (use1904windowing) {
+            value--;
+        }
+
+        return value;
     }
-    
+
     /**
      *  Given an Excel date with using 1900 date windowing, and
      *   converts it to a java.util.Date.
@@ -130,13 +127,13 @@ public class HSSFDateUtil
      *  <code>Europe/Copenhagen</code>, on 2004-03-28 the minute after
      *  01:59 CET is 03:00 CEST, if the excel date represents a time between
      *  02:00 and 03:00 then it is converted to past 03:00 summer time
-     *  
+     *
      *  @param date  The Excel date.
      *  @return Java representation of the date, or null if date is not a valid Excel date
      *  @see java.util.TimeZone
      */
     public static Date getJavaDate(double date) {
-       return getJavaDate(date, false);
+        return getJavaDate(date, false);
     }
     /**
      *  Given an Excel date with either 1900 or 1904 date windowing,
@@ -158,94 +155,90 @@ public class HSSFDateUtil
      *  @see java.util.TimeZone
      */
     public static Date getJavaDate(double date, boolean use1904windowing) {
-        if (isValidExcelDate(date)) {
-            int startYear = 1900;
-            int dayAdjust = -1; // Excel thinks 2/29/1900 is a valid date, which it isn't
-            int wholeDays = (int)Math.floor(date);
-            if (use1904windowing) {
-                startYear = 1904;
-                dayAdjust = 1; // 1904 date windowing uses 1/2/1904 as the first day
-            }
-            else if (wholeDays < 61) {
-                // Date is prior to 3/1/1900, so adjust because Excel thinks 2/29/1900 exists
-                // If Excel date == 2/29/1900, will become 3/1/1900 in Java representation
-                dayAdjust = 0;
-            }
-            GregorianCalendar calendar = new GregorianCalendar(startYear,0,
-                                                     wholeDays + dayAdjust);
-            int millisecondsInDay = (int)((date - Math.floor(date)) * 
-                                          DAY_MILLISECONDS + 0.5);
-            calendar.set(GregorianCalendar.MILLISECOND, millisecondsInDay);
-            return calendar.getTime();
-        }
-        else {
+        if (!isValidExcelDate(date)) {
             return null;
         }
+        int startYear = 1900;
+        int dayAdjust = -1; // Excel thinks 2/29/1900 is a valid date, which it isn't
+        int wholeDays = (int)Math.floor(date);
+        if (use1904windowing) {
+            startYear = 1904;
+            dayAdjust = 1; // 1904 date windowing uses 1/2/1904 as the first day
+        }
+        else if (wholeDays < 61) {
+            // Date is prior to 3/1/1900, so adjust because Excel thinks 2/29/1900 exists
+            // If Excel date == 2/29/1900, will become 3/1/1900 in Java representation
+            dayAdjust = 0;
+        }
+        GregorianCalendar calendar = new GregorianCalendar(startYear,0,
+                                                 wholeDays + dayAdjust);
+        int millisecondsInDay = (int)((date - Math.floor(date)) *
+                                      DAY_MILLISECONDS + 0.5);
+        calendar.set(GregorianCalendar.MILLISECOND, millisecondsInDay);
+        return calendar.getTime();
     }
-    
+
     /**
      * Given a format ID and its format String, will check to see if the
      *  format represents a date format or not.
      * Firstly, it will check to see if the format ID corresponds to an
-     *  internal excel date format (eg most US date formats) 
+     *  internal excel date format (eg most US date formats)
      * If not, it will check to see if the format string only contains
      *  date formatting characters (ymd-/), which covers most
      *  non US date formats.
-     *  
+     *
      * @param formatIndex The index of the format, eg from ExtendedFormatRecord.getFormatIndex
      * @param formatString The format string, eg from FormatRecord.getFormatString
      * @see #isInternalDateFormat(int)
      */
     public static boolean isADateFormat(int formatIndex, String formatString) {
-       // First up, is this an internal date format?
-       if(isInternalDateFormat(formatIndex)) {
-               return true;
-       }
-       
-       // If we didn't get a real string, it can't be
-       if(formatString == null || formatString.length() == 0) {
-               return false;
-       }
-       
-       String fs = formatString;
-       
-       // Translate \- into just -, before matching
-       fs = fs.replaceAll("\\\\-","-");
-       // And \, into ,
-       fs = fs.replaceAll("\\\\,",",");
-       // And '\ ' into ' '
-       fs = fs.replaceAll("\\\\ "," ");
-       
-       // If it end in ;@, that's some crazy dd/mm vs mm/dd
-       //  switching stuff, which we can ignore
-       fs = fs.replaceAll(";@", "");
-       
-       // If it starts with [$-...], then could be a date, but
-       //  who knows what that starting bit is all about
-       fs = fs.replaceAll("^\\[\\$\\-.*?\\]", "");
-       
-       // If it starts with something like [Black] or [Yellow],
-       //  then it could be a date
-       fs = fs.replaceAll("^\\[[a-zA-Z]+\\]", "");
-       
-       // Otherwise, check it's only made up, in any case, of:
-       //  y m d h s - / , . :
-       // optionally followed by AM/PM
-       if(fs.matches("^[yYmMdDhHsS\\-/,. :]+[ampAMP/]*$")) {
-               return true;
-       }
-       
-       return false;
+        // First up, is this an internal date format?
+        if(isInternalDateFormat(formatIndex)) {
+            return true;
+        }
+
+        // If we didn't get a real string, it can't be
+        if(formatString == null || formatString.length() == 0) {
+            return false;
+        }
+
+        String fs = formatString;
+
+        // Translate \- into just -, before matching
+        fs = fs.replaceAll("\\\\-","-");
+        // And \, into ,
+        fs = fs.replaceAll("\\\\,",",");
+        // And '\ ' into ' '
+        fs = fs.replaceAll("\\\\ "," ");
+
+        // If it end in ;@, that's some crazy dd/mm vs mm/dd
+        //  switching stuff, which we can ignore
+        fs = fs.replaceAll(";@", "");
+
+        // If it starts with [$-...], then could be a date, but
+        //  who knows what that starting bit is all about
+        fs = fs.replaceAll("^\\[\\$\\-.*?\\]", "");
+
+        // If it starts with something like [Black] or [Yellow],
+        //  then it could be a date
+        fs = fs.replaceAll("^\\[[a-zA-Z]+\\]", "");
+
+        // Otherwise, check it's only made up, in any case, of:
+        //  y m d h s - / , . :
+        // optionally followed by AM/PM
+        if(fs.matches("^[yYmMdDhHsS\\-/,. :]+[ampAMP/]*$")) {
+            return true;
+        }
+
+        return false;
     }
 
     /**
      * Given a format ID this will check whether the format represents
      *  an internal excel date format or not.
-     * @see #isADateFormat(int, java.lang.String)  
+     * @see #isADateFormat(int, java.lang.String)
      */
     public static boolean isInternalDateFormat(int format) {
-      boolean retval =false;
-
             switch(format) {
                 // Internal Date Formats as described on page 427 in
                 // Microsoft Excel Dev's Kit...
@@ -261,27 +254,22 @@ public class HSSFDateUtil
                 case 0x2d:
                 case 0x2e:
                 case 0x2f:
-                    retval = true;
-                    break;
-                    
-                default:
-                    retval = false;
-                    break;
+                    return true;
             }
-       return retval;
+       return false;
     }
 
     /**
      *  Check if a cell contains a date
-     *  Since dates are stored internally in Excel as double values 
-     *  we infer it is a date if it is formatted as such. 
+     *  Since dates are stored internally in Excel as double values
+     *  we infer it is a date if it is formatted as such.
      *  @see #isADateFormat(int, String)
      *  @see #isInternalDateFormat(int)
      */
     public static boolean isCellDateFormatted(HSSFCell cell) {
         if (cell == null) return false;
         boolean bDate = false;
-        
+
         double d = cell.getNumericCellValue();
         if ( HSSFDateUtil.isValidExcelDate(d) ) {
             HSSFCellStyle style = cell.getCellStyle();
@@ -302,7 +290,7 @@ public class HSSFDateUtil
     public static boolean isCellInternalDateFormatted(HSSFCell cell) {
         if (cell == null) return false;
         boolean bDate = false;
-        
+
         double d = cell.getNumericCellValue();
         if ( HSSFDateUtil.isValidExcelDate(d) ) {
             HSSFCellStyle style = cell.getCellStyle();
@@ -344,7 +332,7 @@ public class HSSFDateUtil
      *
      * @return    days  number of days in years prior to yr.
      * @param     yr    a year (1900 < yr < 4000)
-     * @param use1904windowing 
+     * @param use1904windowing
      * @exception IllegalArgumentException if year is outside of range.
      */
 
@@ -353,16 +341,16 @@ public class HSSFDateUtil
         if ((!use1904windowing && yr < 1900) || (use1904windowing && yr < 1900)) {
             throw new IllegalArgumentException("'year' must be 1900 or greater");
         }
-        
+
         int yr1  = yr - 1;
         int leapDays =   yr1 / 4   // plus julian leap days in prior years
                        - yr1 / 100 // minus prior century years
-                       + yr1 / 400 // plus years divisible by 400 
+                       + yr1 / 400 // plus years divisible by 400
                        - 460;      // leap days in previous 1900 years
-        
+
         return 365 * (yr - (use1904windowing ? 1904 : 1900)) + leapDays;
     }
-    
+
     // set HH:MM:SS fields of cal to 00:00:00:000
     private static Calendar dayStart(final Calendar cal)
     {
@@ -377,5 +365,95 @@ public class HSSFDateUtil
         return cal;
     }
 
-    // ---------------------------------------------------------------------------------------------------------
+
+    private static final class FormatException extends Exception {
+        public FormatException(String msg) {
+            super(msg);
+        }
+    }
+
+    /**
+     * Converts a string of format "HH:MM" or "HH:MM:SS" to its (Excel) numeric equivalent
+     *
+     * @return a double between 0 and 1 representing the fraction of the day
+     */
+    public static double convertTime(String timeStr) {
+        try {
+            return convertTimeInternal(timeStr);
+        } catch (FormatException e) {
+            String msg = "Bad time format '" + timeStr
+                + "' expected 'HH:MM' or 'HH:MM:SS' - " + e.getMessage();
+            throw new IllegalArgumentException(msg);
+        }
+    }
+    private static double convertTimeInternal(String timeStr) throws FormatException {
+        int len = timeStr.length();
+        if (len < 4 || len > 8) {
+            throw new FormatException("Bad length");
+        }
+        String[] parts = TIME_SEPARATOR_PATTERN.split(timeStr);
+
+        String secStr;
+        switch (parts.length) {
+            case 2: secStr = "00"; break;
+            case 3: secStr = parts[2]; break;
+            default:
+                throw new FormatException("Expected 2 or 3 fields but got (" + parts.length + ")");
+        }
+        String hourStr = parts[0];
+        String minStr = parts[1];
+        int hours = parseInt(hourStr, "hour", HOURS_PER_DAY);
+        int minutes = parseInt(minStr, "minute", MINUTES_PER_HOUR);
+        int seconds = parseInt(secStr, "second", SECONDS_PER_MINUTE);
+
+        double totalSeconds = seconds + (minutes + (hours) * 60) * 60;
+        return totalSeconds / (SECONDS_PER_DAY);
+    }
+    /**
+     * Converts a string of format "YYYY/MM/DD" to its (Excel) numeric equivalent
+     *
+     * @return a double representing the (integer) number of days since the start of the Excel epoch
+     */
+    public static Date parseYYYYMMDDDate(String dateStr) {
+        try {
+            return parseYYYYMMDDDateInternal(dateStr);
+        } catch (FormatException e) {
+            String msg = "Bad time format " + dateStr
+                + " expected 'YYYY/MM/DD' - " + e.getMessage();
+            throw new IllegalArgumentException(msg);
+        }
+    }
+    private static Date parseYYYYMMDDDateInternal(String timeStr) throws FormatException {
+        if(timeStr.length() != 10) {
+            throw new FormatException("Bad length");
+        }
+
+        String yearStr = timeStr.substring(0, 4);
+        String monthStr = timeStr.substring(5, 7);
+        String dayStr = timeStr.substring(8, 10);
+        int year = parseInt(yearStr, "year", Short.MIN_VALUE, Short.MAX_VALUE);
+        int month = parseInt(monthStr, "month", 1, 12);
+        int day = parseInt(dayStr, "day", 1, 31);
+
+        Calendar cal = new GregorianCalendar(year, month-1, day, 0, 0, 0);
+        cal.set(Calendar.MILLISECOND, 0);
+        return cal.getTime();
+    }
+    private static int parseInt(String strVal, String fieldName, int rangeMax) throws FormatException {
+        return parseInt(strVal, fieldName, 0, rangeMax-1);
+    }
+
+    private static int parseInt(String strVal, String fieldName, int lowerLimit, int upperLimit) throws FormatException {
+        int result;
+        try {
+            result = Integer.parseInt(strVal);
+        } catch (NumberFormatException e) {
+            throw new FormatException("Bad int format '" + strVal + "' for " + fieldName + " field");
+        }
+        if (result < lowerLimit || result > upperLimit) {
+            throw new FormatException(fieldName + " value (" + result
+                    + ") is outside the allowable range(0.." + upperLimit + ")");
+        }
+        return result;
+    }
 }
index 9b6c206190aaa628f15edb033feb5d5c79aaad1d..a99e48b64e9cf17c05e36c607ae81879f9433e53 100644 (file)
@@ -38,7 +38,6 @@ import org.apache.poi.hssf.record.*;
 import org.apache.poi.hssf.record.aggregates.DataValidityTable;
 import org.apache.poi.hssf.record.formula.Ptg;
 import org.apache.poi.hssf.record.formula.RefPtg;
-import org.apache.poi.hssf.util.HSSFDataValidation;
 import org.apache.poi.hssf.util.PaneInformation;
 import org.apache.poi.hssf.util.Region;
 import org.apache.poi.util.POILogFactory;
@@ -382,7 +381,8 @@ public final class HSSFSheet {
        }
        DataValidityTable dvt = sheet.getOrCreateDataValidityTable();
 
-       dvt.addDataValidation(dataValidation, workbook);
+       DVRecord dvRecord = dataValidation.createDVRecord(workbook);
+       dvt.addDataValidation(dvRecord);
     }
 
 
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 (file)
index 0000000..2f5d24a
--- /dev/null
@@ -0,0 +1,130 @@
+/* ====================================================================\r
+   Copyright 2002-2004   Apache Software Foundation\r
+\r
+   Licensed under the Apache License, Version 2.0 (the "License");\r
+   you may not use this file except in compliance with the License.\r
+   You may obtain a copy of the License at\r
+\r
+       http://www.apache.org/licenses/LICENSE-2.0\r
+\r
+   Unless required by applicable law or agreed to in writing, software\r
+   distributed under the License is distributed on an "AS IS" BASIS,\r
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+   See the License for the specific language governing permissions and\r
+   limitations under the License.\r
+==================================================================== */\r
+\r
+package org.apache.poi.hssf.util;\r
+\r
+import org.apache.poi.hssf.record.RecordInputStream;\r
+import org.apache.poi.util.LittleEndian;\r
+\r
+/**\r
+ * See OOO documentation: excelfileformat.pdf sec 2.5.14 - 'Cell Range Address'\r
+ * \r
+ * @author Dragos Buleandra (dragos.buleandra@trade2b.ro)\r
+ */\r
+public final class CellRangeAddress {\r
+       private static final int ENCODED_SIZE = 8;\r
+\r
+       private int _firstRow;\r
+       private int _firstCol;\r
+       private int _lastRow;\r
+       private int _lastCol;\r
+\r
+       /*\r
+        * TODO - replace other incarnations of 'Cell Range Address' throughout POI:\r
+        * org.apache.poi.hssf.util.CellRange\r
+        * org.apache.poi.hssf.record.cf.CellRange\r
+        * org.apache.poi.hssf.util.HSSFCellRangeAddress.AddrStructure\r
+        * org.apache.poi.hssf.record.MergeCellsRecord.MergedRegion\r
+        * org.apache.poi.hssf.record.SelectionRecord.Reference\r
+        * \r
+        */\r
+       \r
+       public CellRangeAddress(int firstRow, int lastRow, int firstCol, int lastCol) {\r
+               _firstRow = firstRow;\r
+               _lastRow = lastRow;\r
+               _firstCol = firstCol;\r
+               _lastCol = lastCol;\r
+       }\r
+\r
+       public CellRangeAddress(RecordInputStream in) {\r
+               if (in.remaining() < ENCODED_SIZE) {\r
+                       // Ran out of data\r
+                       throw new RuntimeException("Ran out of data reading CellRangeAddress");\r
+               } \r
+               _firstRow = in.readUShort();\r
+               _lastRow = in.readUShort();\r
+               _firstCol = in.readUShort();\r
+               _lastCol = in.readUShort();\r
+       }\r
+\r
+       /**\r
+        * @return column number for the upper left hand corner\r
+        */\r
+       public int getFirstColumn() {\r
+               return _firstCol;\r
+       }\r
+\r
+       /**\r
+        * @return row number for the upper left hand corner\r
+        */\r
+       public int getFirstRow() {\r
+               return _firstRow;\r
+       }\r
+\r
+       /**\r
+        * @return column number for the lower right hand corner\r
+        */\r
+       public int getLastColumn() {\r
+               return _lastCol;\r
+       }\r
+\r
+       /**\r
+        * @return row number for the lower right hand corner\r
+        */\r
+       public int getLastRow() {\r
+               return _lastRow;\r
+       }\r
+\r
+       /**\r
+        * @param _firstCol column number for the upper left hand corner\r
+        */\r
+       public void setFirstColumn(int firstCol) {\r
+               _firstCol = firstCol;\r
+       }\r
+\r
+       /**\r
+        * @param rowFrom row number for the upper left hand corner\r
+        */\r
+       public void setFirstRow(int firstRow) {\r
+               _firstRow = firstRow;\r
+       }\r
+\r
+       /**\r
+        * @param colTo column number for the lower right hand corner\r
+        */\r
+       public void setLastColumn(int lastCol) {\r
+               _lastCol = lastCol;\r
+       }\r
+\r
+       /**\r
+        * @param rowTo row number for the lower right hand corner\r
+        */\r
+       public void setLastRow(int lastRow) {\r
+               _lastRow = lastRow;\r
+       }\r
+\r
+       /* package */ int serialize(byte[] data, int offset) {\r
+               LittleEndian.putUShort(data, offset + 0, _firstRow);\r
+               LittleEndian.putUShort(data, offset + 2, _lastRow);\r
+               LittleEndian.putUShort(data, offset + 4, _firstCol);\r
+               LittleEndian.putUShort(data, offset + 6, _lastCol);\r
+               return ENCODED_SIZE;\r
+       }\r
+\r
+       public static int getEncodedSize(int numberOfItems) {\r
+               return numberOfItems * ENCODED_SIZE;\r
+       }\r
+}
\ 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 (file)
index 0000000..d522197
--- /dev/null
@@ -0,0 +1,117 @@
+/* ====================================================================
+   Copyright 2002-2004   Apache Software Foundation
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+==================================================================== */
+
+package org.apache.poi.hssf.util;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.poi.hssf.record.RecordInputStream;
+import org.apache.poi.util.LittleEndian;
+
+/**
+ * Implementation of the cell range address lists,like is described
+ * in OpenOffice.org's Excel Documentation: excelfileformat.pdf sec 2.5.14 -
+ * 'Cell Range Address List'
+ * 
+ * In BIFF8 there is a common way to store absolute cell range address lists in
+ * several records (not formulas). A cell range address list consists of a field
+ * with the number of ranges and the list of the range addresses. Each cell
+ * range address (called an ADDR structure) contains 4 16-bit-values.
+ * </p>
+ * 
+ * @author Dragos Buleandra (dragos.buleandra@trade2b.ro)
+ */
+public final class CellRangeAddressList {
+
+       /**
+        * List of <tt>CellRangeAddress</tt>es. Each structure represents a cell range
+        */
+       private final List _list;
+
+       public CellRangeAddressList() {
+               _list = new ArrayList();
+       }
+       /**
+        * Convenience constructor for creating a <tt>CellRangeAddressList</tt> with a single 
+        * <tt>CellRangeAddress</tt>.  Other <tt>CellRangeAddress</tt>es may be added later.
+        */
+       public CellRangeAddressList(int firstRow, int lastRow, int firstCol, int lastCol) {
+               this();
+               addCellRangeAddress(firstRow, firstCol, lastRow, lastCol);
+       }
+
+       /**
+        * @param in the RecordInputstream to read the record from
+        */
+       public CellRangeAddressList(RecordInputStream in) {
+               int nItems = in.readUShort();
+               _list = new ArrayList(nItems);
+
+               for (int k = 0; k < nItems; k++) {
+                       _list.add(new CellRangeAddress(in));
+               }
+       }
+
+       /**
+        * Get the number of following ADDR structures. The number of this
+        * structures is automatically set when reading an Excel file and/or
+        * increased when you manually add a new ADDR structure . This is the reason
+        * there isn't a set method for this field .
+        * 
+        * @return number of ADDR structures
+        */
+       public int getADDRStructureNumber() {
+               return _list.size();
+       }
+
+       /**
+        * Add an ADDR structure .
+        * 
+        * @param firstRow - the upper left hand corner's row
+        * @param firstCol - the upper left hand corner's col
+        * @param lastRow - the lower right hand corner's row
+        * @param lastCol - the lower right hand corner's col
+        * @return the index of this ADDR structure
+        */
+       public void addCellRangeAddress(int firstRow, int firstCol, int lastRow, int lastCol) {
+               CellRangeAddress region = new CellRangeAddress(firstRow, lastRow, firstCol, lastCol);
+               _list.add(region);
+       }
+
+       /**
+        * @return <tt>CellRangeAddress</tt> at the given index
+        */
+       public CellRangeAddress getCellRangeAddress(int index) {
+               return (CellRangeAddress) _list.get(index);
+       }
+
+       public int serialize(int offset, byte[] data) {
+               int pos = 2;
+
+               int nItems = _list.size();
+               LittleEndian.putUShort(data, offset, nItems);
+               for (int k = 0; k < nItems; k++) {
+                       CellRangeAddress region = (CellRangeAddress) _list.get(k);
+                       pos += region.serialize(data, offset + pos);
+               }
+               return getSize();
+       }
+
+       public int getSize() {
+               return 2 + CellRangeAddress.getEncodedSize(_list.size());
+       }
+}
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 (file)
index 7380496..0000000
+++ /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;
-
-/**
- * <p>Title: HSSFCellRangeAddress</p>
- * <p>Description:
- *          Implementation of the cell range address lists,like is described in
- *          OpenOffice.org's Excel Documentation .
- *          In BIFF8 there is a common way to store absolute cell range address
- *          lists in several records (not formulas). A cell range address list
- *          consists of a field with the number of ranges and the list of the range
- *          addresses. Each cell range address (called an ADDR structure) contains
- *          4 16-bit-values.</p>
- * <p>Copyright: Copyright (c) 2004</p>
- * <p>Company: </p>
- * @author Dragos Buleandra (dragos.buleandra@trade2b.ro)
- * @version 2.0-pre
- */
-
-public class HSSFCellRangeAddress
-{
-       private static POILogger logger = POILogFactory.getLogger(HSSFCellRangeAddress.class);
-       
-    /**
-     * Number of following ADDR structures
-     */
-    private short             field_addr_number;
-
-    /**
-     * List of ADDR structures. Each structure represents a cell range
-     */
-    private ArrayList         field_regions_list;
-
-    public HSSFCellRangeAddress()
-    {
-
-    }
-
-    /**
-     * Construct a new HSSFCellRangeAddress object and sets its fields appropriately .
-     * Even this isn't an Excel record , I kept the same behavior for reading/writing
-     * the object's data as for a regular record .
-     * 
-     * @param in the RecordInputstream to read the record from
-     */
-    public HSSFCellRangeAddress(RecordInputStream in)
-    {
-        this.fillFields(in);
-    }
-
-    public void fillFields(RecordInputStream in)
-    {
-        this.field_addr_number = in.readShort(); 
-               this.field_regions_list = new ArrayList(this.field_addr_number);
-
-               for (int k = 0; k < this.field_addr_number; k++)
-               {
-            short first_row = in.readShort(); 
-            short first_col = in.readShort();
-            
-            short last_row  = first_row;
-            short last_col  = first_col;
-            if(in.remaining() >= 4) {
-                   last_row  = in.readShort();
-                   last_col  = in.readShort();
-            } else {
-               // Ran out of data
-               // For now, issue a warning, finish, and 
-               //  hope for the best....
-               logger.log(POILogger.WARN, "Ran out of data reading cell references for DVRecord");
-               k = this.field_addr_number;
-            }
-
-                       AddrStructure region = new AddrStructure(first_row, first_col, last_row, last_col);
-                       this.field_regions_list.add(region);
-               }
-    }
-
-    /**
-     * Get the number of following ADDR structures.
-     * The number of this structures is automatically set when reading an Excel file
-     * and/or increased when you manually add a new ADDR structure .
-     * This is the reason there isn't a set method for this field .
-     * @return number of ADDR structures
-     */
-    public short getADDRStructureNumber()
-    {
-        return this.field_addr_number;
-    }
-
-    /**
-     * Add an ADDR structure .
-     * @param first_row - the upper left hand corner's row
-     * @param first_col - the upper left hand corner's col
-     * @param last_row  - the lower right hand corner's row
-     * @param last_col  - the lower right hand corner's col
-     * @return the index of this ADDR structure
-     */
-    public int addADDRStructure(short first_row, short first_col, short last_row, short last_col)
-    {
-        if (this.field_regions_list == null)
-        {
-            //just to be sure :-)
-            this.field_addr_number= 0;
-            this.field_regions_list = new ArrayList(10);
-        }
-        AddrStructure region = new AddrStructure(first_row, last_row, first_col, last_col);
-
-        this.field_regions_list.add(region);
-        this.field_addr_number++;
-        return this.field_addr_number;
-    }
-
-    /**
-     * Remove the ADDR structure stored at the passed in index
-     * @param index The ADDR structure's index
-     */
-    public void removeADDRStructureAt(int index)
-    {
-        this.field_regions_list.remove(index);
-        this.field_addr_number--;
-    }
-
-    /**
-     * return the ADDR structure at the given index.
-     * @return AddrStructure representing
-     */
-    public AddrStructure getADDRStructureAt(int index)
-    {
-        return ( AddrStructure ) this.field_regions_list.get(index);
-    }
-
-    public int serialize(int offset, byte [] data)
-    {
-        int pos  = 2;
-
-        LittleEndian.putShort(data, offset, this.getADDRStructureNumber());
-        for (int k = 0; k < this.getADDRStructureNumber(); k++)
-        {
-            AddrStructure region = this.getADDRStructureAt(k);
-            LittleEndian.putShort(data, offset + pos, region.getFirstRow());
-            pos += 2;
-            LittleEndian.putShort(data, offset + pos, region.getLastRow());
-            pos += 2;
-            LittleEndian.putShort(data, offset + pos, region.getFirstColumn());
-            pos += 2;
-            LittleEndian.putShort(data, offset + pos, region.getLastColumn());
-            pos += 2;
-        }
-        return this.getSize();
-    }
-
-    public int getSize()
-    {
-       return 2 + this.field_addr_number*8;
-    }
-
-    public class AddrStructure
-    {
-        private short _first_row;
-        private short _first_col;
-        private short _last_row;
-        private short _last_col;
-
-        public AddrStructure(short first_row, short last_row, short first_col, short last_col)
-        {
-            this._first_row = first_row;
-            this._last_row   = last_row;
-            this._first_col = first_col;
-            this._last_col   = last_col;
-        }
-
-               /**
-                * get the upper left hand corner column number
-                * @return column number for the upper left hand corner
-                */
-               public short getFirstColumn()
-               {
-                       return this._first_col;
-               }
-
-               /**
-                * get the upper left hand corner row number
-                * @return row number for the upper left hand corner
-                */
-               public short getFirstRow()
-               {
-                       return this._first_row;
-               }
-
-               /**
-                * get the lower right hand corner column number
-                * @return column number for the lower right hand corner
-                */
-               public short getLastColumn()
-               {
-                       return this._last_col;
-               }
-
-               /**
-                * get the lower right hand corner row number
-                * @return row number for the lower right hand corner
-                */
-               public short getLastRow()
-               {
-                       return this._last_row;
-               }
-
-               /**
-                * set the upper left hand corner column number
-                * @param this._first_col  column number for the upper left hand corner
-                */
-               public void setFirstColumn(short first_col)
-               {
-                       this._first_col = first_col;
-               }
-
-               /**
-                * set the upper left hand corner row number
-                * @param rowFrom  row number for the upper left hand corner
-                */
-               public void setFirstRow(short first_row)
-               {
-                       this._first_row = first_row;
-               }
-
-               /**
-                * set the lower right hand corner column number
-                * @param colTo  column number for the lower right hand corner
-                */
-               public void setLastColumn(short last_col)
-               {
-                       this._last_col = last_col;
-               }
-
-               /**
-                * get the lower right hand corner row number
-                * @param rowTo  row number for the lower right hand corner
-                */
-               public void setLastRow(short last_row)
-               {
-                       this._last_row = last_row;
-               }
-       }
-}
-
-
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 (file)
index 4d8f48b..0000000
+++ /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;
-
-/**
- * <p>Title: HSSFDataValidation</p>
- * <p>Description: Utilty class for creating data validation cells</p>
- * <p>Copyright: Copyright (c) 2004</p>
- * <p>Company: </p>
- * @author Dragos Buleandra (dragos.buleandra@trade2b.ro)
- * @version 2.0-pre
- */
-
-public class HSSFDataValidation
-{
-  /**
-   * Validation data type constants
-   */
-  /**
-   * Any type
-   */
-  public static final int DATA_TYPE_ANY              = 0x00;
-  /**
-   * Integer type
-   */
-  public static final int DATA_TYPE_INTEGER          = 0x01;
-  /**
-   * Decimal type
-   */
-  public static final int DATA_TYPE_DECIMAL          = 0x02;
-  /**
-   * List type ( combo box type )
-   */
-  public static final int DATA_TYPE_LIST             = 0x03;
-  /**
-   * Date type
-   */
-  public static final int DATA_TYPE_DATE             = 0x04;
-  /**
-   * Time type
-   */
-  public static final int DATA_TYPE_TIME             = 0x05;
-  /**
-   * String length type
-   */
-  public static final int DATA_TYPE_TEXT_LENGTH      = 0x06;
-  /**
-   * Formula ( custom ) type
-   */
-  public static final int DATA_TYPE_FORMULA          = 0x07;
-
-  /**
-   * Error style constants for error box
-   */
-  /**
-   * STOP style like
-   */
-  public static final int ERROR_STYLE_STOP           = 0x00;
-  /**
-   * WARNING style like
-   */
-  public static final int ERROR_STYLE_WARNING        = 0x01;
-  /**
-   * INFO style like
-   */
-  public static final int ERROR_STYLE_INFO           = 0x02;
-
-  /**
-   * Condition operator
-   */
-  public static final int OPERATOR_BETWEEN           = 0x00;
-  public static final int OPERATOR_NOT_BETWEEN       = 0x01;
-  public static final int OPERATOR_EQUAL             = 0x02;
-  public static final int OPERATOR_NOT_EQUAL         = 0x03;
-  public static final int OPERATOR_GREATER_THAN      = 0x04;
-  public static final int OPERATOR_LESS_THAN         = 0x05;
-  public static final int OPERATOR_GREATER_OR_EQUAL  = 0x06;
-  public static final int OPERATOR_LESS_OR_EQUAL     = 0x07;
-
-  private short _first_row                  = 0;
-  private short _first_col                  = 0;
-  private short _last_row                   = 0;
-  private short _last_col                   = 0;
-
-  private String _prompt_title              = null;
-  private String _prompt_text               = null;
-  private String _error_title               = null;
-  private String _error_text                = null;
-  private String _string_first_formula      = null;
-  private String _string_sec_formula        = null;
-
-  private int  _data_type                   = HSSFDataValidation.DATA_TYPE_ANY;
-  private int  _error_style                 = HSSFDataValidation.ERROR_STYLE_STOP;
-  private boolean  _list_explicit_formula   = true;
-  private boolean  _empty_cell_allowed      = true;
-  private boolean  _surpress_dropdown_arrow = false;
-  private boolean  _show_prompt_box         = true;
-  private boolean  _show_error_box          = true;
-  private int _operator                     = HSSFDataValidation.OPERATOR_BETWEEN;
-
-
-  /**
-   * Empty constructor
-   */
-  public HSSFDataValidation( )
-  {
-  }
-
-  /**
-   * Constructor wich initializes the cell range on wich this object will be applied
-   * @param first_row First row
-   * @param first_col First column
-   * @param last_row Last row
-   * @param last_col Last column
-   */
-  public HSSFDataValidation( short first_row, short first_col, short last_row, short last_col )
-  {
-      this._first_row = first_row;
-      this._first_col = first_col;
-      this._last_row  = last_row;
-      this._last_col  = last_col;
-  }
-
-  /**
-   * Set the type of this object
-   * @param data_type The type
-   * @see DATA_TYPE_ANY, DATA_TYPE_INTEGER, DATA_TYPE_DECIMNAL, DATA_TYPE_LIST, DATA_TYPE_DATE,
-   *      DATA_TYPE_TIME, DATA_TYPE_TEXT_LENTGH, DATA_TYPE_FORMULA
-   */
-  public void setDataValidationType( int data_type )
-  {
-      this._data_type = data_type;
-  }
-
-  /**
-   * The data type of this object
-   * @return The type
-   * @see DATA_TYPE_ANY, DATA_TYPE_INTEGER, DATA_TYPE_DECIMNAL, DATA_TYPE_LIST, DATA_TYPE_DATE,
-   *      DATA_TYPE_TIME, DATA_TYPE_TEXT_LENTGH, DATA_TYPE_FORMULA
-   */
-  public int getDataValidationType()
-  {
-     return this._data_type;
-  }
-
-  /**
-   * Sets the error style for error box
-   * @param error_style Error style constant
-   * @see ERROR_STYLE_STOP, ERROR_STYLE_WARNING, ERROR_STYLE_INFO
-   */
-  public void setErrorStyle( int error_style )
-  {
-      this._error_style = error_style;
-  }
-
-  /**
-   * returns the error style of errror box
-   * @return the style constant
-   * @see ERROR_STYLE_STOP, ERROR_STYLE_WARNING, ERROR_STYLE_INFO
-   */
-  public int getErrorStyle( )
-  {
-      return this._error_style;
-  }
-
-  /**
-   * If this object has an explicit formula . This is useful only for list data validation object
-   * @param explicit True if use an explicit formula
-   */
-  public void setExplicitListFormula( boolean explicit )
-  {
-     this._list_explicit_formula = explicit;
-  }
-
-  /**
-   * Returns the settings for explicit formula . This is useful only for list data validation objects.
-   * This method always returns false if the object isn't a list validation object
-   * @see setDataValidationType( int data_type )
-   * @return
-   */
-  public boolean getExplicitListFormula( )
-  {
-    if ( this._data_type != HSSFDataValidation.DATA_TYPE_LIST )
-    {
-        return false;
-    }
-    return this._list_explicit_formula ;
-  }
-
-  /**
-   * Sets if this object allows empty as a valid value
-   * @param allowed True if this object should treats empty as valid value , false otherwise
-   */
-  public void setEmptyCellAllowed( boolean allowed )
-  {
-     this._empty_cell_allowed = allowed;
-  }
-
-  /**
-   * Retrieve the settings for empty cells allowed
-   * @return True if this object should treats empty as valid value , false otherwise
-   */
-  public boolean getEmptyCellAllowed( )
-  {
-     return this._empty_cell_allowed ;
-  }
-  /**
-   * @deprecated - (Jul-2008) use setSuppressDropDownArrow
-    */
-  public void setSurppressDropDownArrow( boolean suppress ) {
-    setSuppressDropDownArrow(suppress);
-  }
-  /**
-   * @deprecated - (Jul-2008) use getSuppressDropDownArrow
-    */
-  public boolean getSurppressDropDownArrow( ) {
-    return getSuppressDropDownArrow();
-  }
-
-  /**
-   * Useful for list validation objects .
-   * @param surppres True if a list should display the values into a drop down list , false otherwise .
-   *                 In other words , if a list should display the arrow sign on its right side
-   */
-  public void setSuppressDropDownArrow( boolean surppres )
-  {
-     this._surpress_dropdown_arrow = surppres;
-  }
-
-  /**
-   * Useful only list validation objects .
-   * This method always returns false if the object isn't a list validation object
-   * @return True if a list should display the values into a drop down list , false otherwise .
-   * @see setDataValidationType( int data_type )
-   */
-  public boolean getSuppressDropDownArrow( )
-  {
-     if ( this._data_type != HSSFDataValidation.DATA_TYPE_LIST )
-     {
-         return false;
-     }
-     return this._surpress_dropdown_arrow ;
-  }
-
-  /**
-   * Sets the behaviour when a cell which belongs to this object is selected
-   * @param show True if an prompt box should be displayed , false otherwise
-   */
-  public void setShowPromptBox( boolean show )
-  {
-     this._show_prompt_box = show;
-  }
-
-  /**
-   * @param show True if an prompt box should be displayed , false otherwise
-   */
-  public boolean getShowPromptBox( )
-  {
-     if ( (this.getPromptBoxText() == null) && (this.getPromptBoxTitle() == null) )
-     {
-       return false;
-     }
-     return this._show_prompt_box ;
-  }
-
-  /**
-   * Sets the behaviour when an invalid value is entered
-   * @param show True if an error box should be displayed , false otherwise
-   */
-  public void setShowErrorBox( boolean show )
-  {
-     this._show_error_box = show;
-  }
-
-  /**
-   * @return True if an error box should be displayed , false otherwise
-   */
-  public boolean getShowErrorBox( )
-  {
-    if ( (this.getErrorBoxText() == null) && (this.getErrorBoxTitle() == null) )
-    {
-      return false;
-    }
-    return this._show_error_box ;
-  }
-
-  /**
-   * Sets the operator involved in the formula whic governs this object
-   * Example : if you wants that a cell to accept only values between 1 and 5 , which
-   * mathematically means 1 <= value <= 5 , then the operator should be OPERATOR_BETWEEN
-   * @param operator A constant for operator
-   * @see OPERATOR_BETWEEN, OPERATOR_NOT_BETWEEN, OPERATOR_EQUAL, OPERATOR_NOT_EQUAL
-   * OPERATOR_GREATER_THAN, OPERATOR_LESS_THAN, OPERATOR_GREATER_OR_EQUAL,
-   * OPERATOR_LESS_OR_EQUAL
-   */
-  public void setOperator( int operator )
-  {
-      this._operator = operator;
-  }
-
-  /**
-   * Retrieves the operator used for this object's formula
-   * @return
-   * @see OPERATOR_BETWEEN, OPERATOR_NOT_BETWEEN, OPERATOR_EQUAL, OPERATOR_NOT_EQUAL
-   * OPERATOR_GREATER_THAN, OPERATOR_LESS_THAN, OPERATOR_GREATER_OR_EQUAL,
-   * OPERATOR_LESS_OR_EQUAL
-   */
-  public int getOperator()
-  {
-      return this._operator;
-  }
-
-  /**
-   * Sets the title and text for the prompt box . Prompt box is displayed when the user
-   * selects a cell which belongs to this validation object . In order for a prompt box
-   * to be displayed you should also use method setShowPromptBox( boolean show )
-   * @param title The prompt box's title
-   * @param text The prompt box's text
-   * @see setShowPromptBox( boolean show )
-   */
-  public void createPromptBox( String title, String text )
-  {
-      this._prompt_title = title;
-      this._prompt_text  = text;
-      this.setShowPromptBox(true);
-  }
-
-  /**
-   * Returns the prompt box's title
-   * @return Prompt box's title or null
-   */
-  public String getPromptBoxTitle( )
-  {
-    return this._prompt_title;
-  }
-
-  /**
-   * Returns the prompt box's text
-   * @return Prompt box's text or null
-   */
-  public String getPromptBoxText( )
-  {
-    return this._prompt_text;
-  }
-
-  /**
-   * Sets the title and text for the error box . Error box is displayed when the user
-   * enters an invalid value int o a cell which belongs to this validation object .
-   * In order for an error box to be displayed you should also use method
-   * setShowErrorBox( boolean show )
-   * @param title The error box's title
-   * @param text The error box's text
-   * @see setShowErrorBox( boolean show )
-   */
-  public void createErrorBox( String title, String text )
-  {
-      this._error_title = title;
-      this._error_text  = text;
-      this.setShowErrorBox(true);
-  }
-
-  /**
-   * Returns the error box's title
-   * @return Error box's title or null
-   */
-  public String getErrorBoxTitle( )
-  {
-    return this._error_title;
-  }
-
-  /**
-   * Returns the error box's text
-   * @return Error box's text or null
-   */
-  public String getErrorBoxText( )
-  {
-    return this._error_text;
-  }
-
-  /**
-   * Sets the first formula for this object .
-   * A formula is divided into three parts : first formula , operator and second formula .
-   * In other words , a formula contains a left oprand , an operator and a right operand.
-   * This is the general rule . An example is 1<= value <= 5 . In this case ,
-   * the left operand ( or the first formula ) is the number 1 . The operator is
-   * OPERATOR_BETWEEN and the right operand ( or the second formula ) is 5 .
-   * @param formula
-   */
-  public void setFirstFormula( String formula )
-  {
-     this._string_first_formula = formula;
-  }
-
-  /**
-   * Returns the first formula
-   * @return
-   */
-  public String getFirstFormula( )
-  {
-     return this._string_first_formula;
-  }
-
-  /**
-   * Sets the first formula for this object .
-   * A formula is divided into three parts : first formula , operator and second formula .
-   * In other words , a formula contains a left oprand , an operator and a right operand.
-   * This is the general rule . An example is 1<= value <=5 . In this case ,
-   * the left operand ( or the first formula ) is the number 1 . The operator is
-   * OPERATOR_BETWEEN and the right operand ( or the second formula ) is 5 .
-   * But there are cases when a second formula isn't needed :
-   * You want somethink like : all values less than 5 . In this case , there's only a first
-   * formula ( in our case 5 ) and the operator OPERATOR_LESS_THAN
-   * @param formula
-   */
-  public void setSecondFormula( String formula )
-  {
-     this._string_sec_formula = formula;
-  }
-
-  /**
-   * Returns the second formula
-   * @return
-   */
-  public String getSecondFormula( )
-  {
-     return this._string_sec_formula;
-  }
-
-  public void setFirstRow( short first_row )
-  {
-    this._first_row = first_row;
-  }
-
-  public void setFirstColumn( short first_column )
-  {
-    this._first_col = first_column;
-  }
-
-  public void setLastRow( short last_row )
-  {
-    this._last_row = last_row;
-  }
-
-  public void setLastColumn( short last_column )
-  {
-    this._last_col = last_column;
-  }
-
-  public short getFirstRow()
-  {
-    return this._first_row;
-  }
-
-  public short getFirstColumn()
-  {
-    return this._first_col;
-  }
-
-  public short getLastRow()
-  {
-    return this._last_row;
-  }
-
-  public short getLastColumn()
-  {
-    return this._last_col;
-  }
-
-}
\ No newline at end of file
index 8c43fa74eb2c68f4133086ab5db81d2019546053..b588c6e6ccdb4a9f8d5196de6a509b34938a8d8c 100644 (file)
@@ -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;
 
 /**
- * <p>Title: TestDataValidation</p>
- * <p>Description: Class for testing Excel's data validation mechanism
- *    Second test :
- *        -
- * </p>
+ * 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];
index 894c77d685d24e40d7a6f9f1c621edb2fd6bd1dc..4fce0af1d3a2e87d6b28fa5958b4d65a916045b8 100644 (file)
@@ -39,7 +39,7 @@ import org.apache.poi.hssf.model.Workbook;
  * @author Alex Jacoby (ajacoby at gmail.com)
  * @version %I%, %G%
  */
-public class TestHSSFDateUtil extends TestCase {
+public final class TestHSSFDateUtil extends TestCase {
 
     public static final int CALENDAR_JANUARY = 0;
     public static final int CALENDAR_FEBRUARY = 1;
@@ -47,11 +47,6 @@ public class TestHSSFDateUtil extends TestCase {
     public static final int CALENDAR_APRIL = 3;
     public static final int CALENDAR_JULY = 6;
     public static final int CALENDAR_OCTOBER = 9;
-    
-    public TestHSSFDateUtil(String s)
-    {
-        super(s);
-    }
 
     /**
      * Checks the date conversion functions in the HSSFDateUtil class.
@@ -193,14 +188,13 @@ public class TestHSSFDateUtil extends TestCase {
     }
     
     /**
-     * Tests that we deal with timezones properly
+     * Tests that we deal with time-zones properly
      */
     public void testCalendarConversion() {
         GregorianCalendar date = new GregorianCalendar(2002, 0, 1, 12, 1, 1);
         Date expected = date.getTime();
-        double expectedExcel = HSSFDateUtil.getExcelDate(expected);
 
-        // Iteratating over the hours exposes any rounding issues.
+        // Iterating over the hours exposes any rounding issues.
         for (int hour = -12; hour <= 12; hour++)
         {
             String id = "GMT" + (hour < 0 ? "" : "+") + hour + ":00";
@@ -209,7 +203,7 @@ public class TestHSSFDateUtil extends TestCase {
             double excelDate = HSSFDateUtil.getExcelDate(date, false);
             Date javaDate = HSSFDateUtil.getJavaDate(excelDate);
 
-            // Should match despite timezone
+            // Should match despite time-zone
             assertEquals("Checking timezone " + id, expected.getTime(), javaDate.getTime());
         }
     }
@@ -402,7 +396,11 @@ public class TestHSSFDateUtil extends TestCase {
         assertEquals(34519.0, HSSFDateUtil.getExcelDate(createDate(1998, CALENDAR_JULY, 5), true), 0.00001);
     }
     
-    private Date createDate(int year, int month, int day) {
+    /**
+     * @param month zero based 
+     * @param day one based
+     */
+    private static Date createDate(int year, int month, int day) {
         Calendar c = new GregorianCalendar();
         c.set(year, month, day, 0, 0, 0);
         c.set(Calendar.MILLISECOND, 0);
@@ -420,10 +418,18 @@ public class TestHSSFDateUtil extends TestCase {
         calendar = new GregorianCalendar(1901, 0, 1);
         assertEquals("Checking absolute day (1 Jan 1901)", 366, HSSFDateUtil.absoluteDay(calendar, false));
     }
+
+    public void testConvertTime() {
+       
+        final double delta = 1E-7; // a couple of digits more accuracy than strictly required
+        assertEquals(0.5, HSSFDateUtil.convertTime("12:00"), delta);
+        assertEquals(2.0/3, HSSFDateUtil.convertTime("16:00"), delta);
+        assertEquals(0.0000116, HSSFDateUtil.convertTime("0:00:01"), delta);
+        assertEquals(0.7330440, HSSFDateUtil.convertTime("17:35:35"), delta);
+    }
     
-    public static void main(String [] args) {
-        System.out
-                .println("Testing org.apache.poi.hssf.usermodel.TestHSSFDateUtil");
-        junit.textui.TestRunner.run(TestHSSFDateUtil.class);
+    public void testParseDate() {
+        assertEquals(createDate(2008, Calendar.AUGUST, 3), HSSFDateUtil.parseYYYYMMDDDate("2008/08/03"));
+        assertEquals(createDate(1994, Calendar.MAY, 1), HSSFDateUtil.parseYYYYMMDDDate("1994/05/01"));
     }
 }