From 02564b5824388807d6cef35b05d6caaa46e714c5 Mon Sep 17 00:00:00 2001 From: Nick Burch Date: Mon, 3 Dec 2007 13:00:02 +0000 Subject: [PATCH] Start on importing validation patches from bug #27511 (DVRecord still needs more work before it can go in) git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@600513 13f79535-47bb-0310-9956-ffa450edef68 --- .../apache/poi/hssf/record/DVALRecord.java | 157 ++++++ .../poi/hssf/util/HSSFCellRangeAddress.java | 254 ++++++++++ .../poi/hssf/util/HSSFDataValidation.java | 471 ++++++++++++++++++ 3 files changed, 882 insertions(+) create mode 100644 src/java/org/apache/poi/hssf/record/DVALRecord.java create mode 100644 src/java/org/apache/poi/hssf/util/HSSFCellRangeAddress.java create mode 100644 src/java/org/apache/poi/hssf/util/HSSFDataValidation.java diff --git a/src/java/org/apache/poi/hssf/record/DVALRecord.java b/src/java/org/apache/poi/hssf/record/DVALRecord.java new file mode 100644 index 000000000..858f525ca --- /dev/null +++ b/src/java/org/apache/poi/hssf/record/DVALRecord.java @@ -0,0 +1,157 @@ + +/* ==================================================================== + 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.record; + +import org.apache.poi.util.LittleEndian; + +/** + * Title: DVAL Record

+ * Description: used in data validation ; + * This record is the list header of all data validation records in the current sheet. + * @author Dragos Buleandra (dragos.buleandra@trade2b.ro) + * @version 2.0-pre + */ + +public class DVALRecord extends Record +{ + public final static short sid = 0x01B2; + + //unknown field ; it's size should be 10 + private short field_unknown = 0x0000; + + //Object ID of the drop down arrow object for list boxes ; + //in our case this will be always FFFF , until + //MSODrawingGroup and MSODrawing records are implemented + private int field_cbo_id = 0xFFFFFFFF; + + //Number of following DV records + //Default value is 1 + private int field_3_dv_no = 0x00000000; + + public DVALRecord() + { + } + + /** + * Constructs a DVAL record and sets its fields appropriately. + * + * @param in the RecordInputstream to read the record from + */ + + public DVALRecord(RecordInputStream in) + { + super(in); + } + + protected void validateSid(short id) + { + if (id != sid) + { + throw new RecordFormatException("NOT A valid DVAL RECORD"); + } + } + + protected void fillFields(RecordInputStream in) + { + for ( int i=0; i<5; i++) + { + this.field_unknown = in.readShort(); + } + this.field_cbo_id = in.readInt(); + this.field_3_dv_no = in.readInt(); + } + + /** + * set the object ID of the drop down arrow object for list boxes + * @param cboID - Object ID + */ + public void setObjectID(int cboID) + { + this.field_cbo_id = cboID; + } + + /** + * Set the number of following DV records + * @param dvNo - the DV records number + */ + public void setDVRecNo(int dvNo) + { + this.field_3_dv_no = dvNo; + } + + /** + * get Object ID of the drop down arrow object for list boxes + */ + public int getObjectID( ) + { + return this.field_cbo_id; + } + + /** + * Get number of following DV records + */ + public int getDVRecNo( ) + { + return this.field_3_dv_no; + } + + + public String toString() + { + StringBuffer buffer = new StringBuffer(); + + buffer.append("[DVAL]\n"); + buffer.append(" .comboObjectID = ").append(Integer.toHexString(this.getObjectID())).append("\n"); + buffer.append(" .DVRecordsNumber = ").append(Integer.toHexString(this.getDVRecNo())).append("\n"); + buffer.append("[/DVAL]\n"); + return buffer.toString(); + } + + public int serialize(int offset, byte [] data) + { + LittleEndian.putShort(data, 0 + offset, this.sid); + LittleEndian.putShort(data, 2 + offset, ( short)(this.getRecordSize()-4)); + for ( int i=0; i<5; i++) + { + LittleEndian.putShort(data, 4 + i*2 + offset, (short)this.field_unknown); + } + LittleEndian.putInt(data, 14 + offset, this.getObjectID()); + LittleEndian.putInt(data, 18 + offset, this.getDVRecNo()); + return getRecordSize(); + } + + //with 4 bytes header + public int getRecordSize() + { + return 22; + } + + public short getSid() + { + return this.sid; + } + + public Object clone() + { + DVALRecord rec = new DVALRecord(); + rec.field_unknown = this.field_unknown; + rec.field_cbo_id = this.field_cbo_id; + rec.field_3_dv_no = this.field_3_dv_no; + return rec; + } +} \ No newline at end of file diff --git a/src/java/org/apache/poi/hssf/util/HSSFCellRangeAddress.java b/src/java/org/apache/poi/hssf/util/HSSFCellRangeAddress.java new file mode 100644 index 000000000..745359d92 --- /dev/null +++ b/src/java/org/apache/poi/hssf/util/HSSFCellRangeAddress.java @@ -0,0 +1,254 @@ +/* ==================================================================== + 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.util.LittleEndian; +import java.util.ArrayList; + +/** + *

Title: HSSFCellRangeAddress

+ *

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

+ *

Copyright: Copyright (c) 2004

+ *

Company:

+ * @author Dragos Buleandra (dragos.buleandra@trade2b.ro) + * @version 2.0-pre + */ + +public class HSSFCellRangeAddress +{ + /** + * 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 data Excel's file stream data + * @param offset the offset in Excel's file data + */ + public HSSFCellRangeAddress( byte [] data, int offset ) + { + this.fillFields(data, offset); + } + + public void fillFields(byte [] data, int offset) + { + this.field_addr_number = LittleEndian.getShort(data, 0 + offset); + this.field_regions_list = new ArrayList(this.field_addr_number); + int pos = 2; + + for (int k = 0; k < this.field_addr_number; k++) + { + short first_row = LittleEndian.getShort(data, pos + offset); + short first_col = LittleEndian.getShort(data, pos + 2 + offset); + short last_row = LittleEndian.getShort(data, pos + 4 + offset); + short last_col = LittleEndian.getShort(data, pos + 6 + offset); + + AddrStructure region = new AddrStructure(first_row, first_col, last_row, last_col); + pos += 8; + 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 new file mode 100644 index 000000000..af578bee4 --- /dev/null +++ b/src/java/org/apache/poi/hssf/util/HSSFDataValidation.java @@ -0,0 +1,471 @@ +/* ==================================================================== + Copyright 2002-2004 Apache Software Foundation + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +==================================================================== */ + +package org.apache.poi.hssf.util; + +/** + *

Title: HSSFDataValidation

+ *

Description: Utilty class for creating data validation cells

+ *

Copyright: Copyright (c) 2004

+ *

Company:

+ * @author Dragos Buleandra (dragos.buleandra@trade2b.ro) + * @version 2.0-pre + */ + +public class HSSFDataValidation +{ + /** + * Validation data type constants + */ + /** + * Any type + */ + public static final int DATA_TYPE_ANY = 0x00; + /** + * Integer type + */ + public static final int DATA_TYPE_INTEGER = 0x01; + /** + * Decimal type + */ + public static final int DATA_TYPE_DECIMAL = 0x02; + /** + * List type ( combo box type ) + */ + public static final int DATA_TYPE_LIST = 0x03; + /** + * Date type + */ + public static final int DATA_TYPE_DATE = 0x04; + /** + * Time type + */ + public static final int DATA_TYPE_TIME = 0x05; + /** + * String length type + */ + public static final int DATA_TYPE_TEXT_LENGTH = 0x06; + /** + * Formula ( custom ) type + */ + public static final int DATA_TYPE_FORMULA = 0x07; + + /** + * Error style constants for error box + */ + /** + * STOP style like + */ + public static final int ERROR_STYLE_STOP = 0x00; + /** + * WARNING style like + */ + public static final int ERROR_STYLE_WARNING = 0x01; + /** + * INFO style like + */ + public static final int ERROR_STYLE_INFO = 0x02; + + /** + * Condition operator + */ + public static final int OPERATOR_BETWEEN = 0x00; + public static final int OPERATOR_NOT_BETWEEN = 0x01; + public static final int OPERATOR_EQUAL = 0x02; + public static final int OPERATOR_NOT_EQUAL = 0x03; + public static final int OPERATOR_GREATER_THAN = 0x04; + public static final int OPERATOR_LESS_THAN = 0x05; + public static final int OPERATOR_GREATER_OR_EQUAL = 0x06; + public static final int OPERATOR_LESS_OR_EQUAL = 0x07; + + private short _first_row = 0; + private short _first_col = 0; + private short _last_row = 0; + private short _last_col = 0; + + private String _prompt_title = null; + private String _prompt_text = null; + private String _error_title = null; + private String _error_text = null; + private String _string_first_formula = null; + private String _string_sec_formula = null; + + private int _data_type = HSSFDataValidation.DATA_TYPE_ANY; + private int _error_style = HSSFDataValidation.ERROR_STYLE_STOP; + private boolean _list_explicit_formula = true; + private boolean _empty_cell_allowed = true; + private boolean _surpress_dropdown_arrow = false; + private boolean _show_prompt_box = true; + private boolean _show_error_box = true; + private int _operator = HSSFDataValidation.OPERATOR_BETWEEN; + + + /** + * Empty constructor + */ + public HSSFDataValidation( ) + { + } + + /** + * Constructor wich initializes the cell range on wich this object will be applied + * @param first_row First row + * @param first_col First column + * @param last_row Last row + * @param last_col Last column + */ + public HSSFDataValidation( short first_row, short first_col, short last_row, short last_col ) + { + this._first_row = first_row; + this._first_col = first_col; + this._last_row = last_row; + this._last_col = last_col; + } + + /** + * Set the type of this object + * @param data_type The type + * @see DATA_TYPE_ANY, DATA_TYPE_INTEGER, DATA_TYPE_DECIMNAL, DATA_TYPE_LIST, DATA_TYPE_DATE, + * DATA_TYPE_TIME, DATA_TYPE_TEXT_LENTGH, DATA_TYPE_FORMULA + */ + public void setDataValidationType( int data_type ) + { + this._data_type = data_type; + } + + /** + * The data type of this object + * @return The type + * @see DATA_TYPE_ANY, DATA_TYPE_INTEGER, DATA_TYPE_DECIMNAL, DATA_TYPE_LIST, DATA_TYPE_DATE, + * DATA_TYPE_TIME, DATA_TYPE_TEXT_LENTGH, DATA_TYPE_FORMULA + */ + public int getDataValidationType() + { + return this._data_type; + } + + /** + * Sets the error style for error box + * @param error_style Error style constant + * @see ERROR_STYLE_STOP, ERROR_STYLE_WARNING, ERROR_STYLE_INFO + */ + public void setErrorStyle( int error_style ) + { + this._error_style = error_style; + } + + /** + * returns the error style of errror box + * @return the style constant + * @see ERROR_STYLE_STOP, ERROR_STYLE_WARNING, ERROR_STYLE_INFO + */ + public int getErrorStyle( ) + { + return this._error_style; + } + + /** + * If this object has an explicit formula . This is useful only for list data validation object + * @param explicit True if use an explicit formula + */ + public void setExplicitListFormula( boolean explicit ) + { + this._list_explicit_formula = explicit; + } + + /** + * Returns the settings for explicit formula . This is useful only for list data validation objects. + * This method always returns false if the object isn't a list validation object + * @see setDataValidationType( int data_type ) + * @return + */ + public boolean getExplicitListFormula( ) + { + if ( this._data_type != HSSFDataValidation.DATA_TYPE_LIST ) + { + return false; + } + return this._list_explicit_formula ; + } + + /** + * Sets if this object allows empty as a valid value + * @param allowed True if this object should treats empty as valid value , false otherwise + */ + public void setEmptyCellAllowed( boolean allowed ) + { + this._empty_cell_allowed = allowed; + } + + /** + * Retrieve the settings for empty cells allowed + * @return True if this object should treats empty as valid value , false otherwise + */ + public boolean getEmptyCellAllowed( ) + { + return this._empty_cell_allowed ; + } + + /** + * 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 setSurppressDropDownArrow( 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 getSurppressDropDownArrow( ) + { + 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