From c1748f0143e3665256076a6c2065def095cb5767 Mon Sep 17 00:00:00 2001 From: Yegor Kozlov Date: Sun, 16 May 2010 15:49:21 +0000 Subject: [PATCH] support for data validation for OOXML, see Bugzilla 49244 git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@944869 13f79535-47bb-0310-9956-ffa450edef68 --- src/documentation/content/xdocs/status.xml | 1 + .../poi/hssf/usermodel/DVConstraint.java | 276 ++++------ .../hssf/usermodel/HSSFDataValidation.java | 138 ++--- .../usermodel/HSSFDataValidationHelper.java | 118 +++++ .../apache/poi/hssf/usermodel/HSSFSheet.java | 15 +- .../poi/ss/usermodel/DataValidation.java | 152 ++++++ .../usermodel/DataValidationConstraint.java | 118 +++++ .../ss/usermodel/DataValidationHelper.java | 46 ++ .../org/apache/poi/ss/usermodel/Sheet.java | 8 + .../xssf/usermodel/XSSFDataValidation.java | 244 +++++++++ .../XSSFDataValidationConstraint.java | 194 +++++++ .../usermodel/XSSFDataValidationHelper.java | 155 ++++++ .../apache/poi/xssf/usermodel/XSSFSheet.java | 87 ++- .../usermodel/TestXSSFDataValidation.java | 242 +++++++++ .../hssf/usermodel/TestDataValidation.java | 453 +--------------- .../poi/hssf/usermodel/TestHSSFSheet.java | 11 +- .../ss/usermodel/BaseTestDataValidation.java | 501 ++++++++++++++++++ .../spreadsheet/DataValidations-49244.xlsx | Bin 0 -> 10705 bytes 18 files changed, 2062 insertions(+), 697 deletions(-) create mode 100644 src/java/org/apache/poi/hssf/usermodel/HSSFDataValidationHelper.java create mode 100644 src/java/org/apache/poi/ss/usermodel/DataValidation.java create mode 100644 src/java/org/apache/poi/ss/usermodel/DataValidationConstraint.java create mode 100644 src/java/org/apache/poi/ss/usermodel/DataValidationHelper.java create mode 100644 src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFDataValidation.java create mode 100644 src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFDataValidationConstraint.java create mode 100644 src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFDataValidationHelper.java create mode 100644 src/ooxml/testcases/org/apache/poi/xssf/usermodel/TestXSSFDataValidation.java create mode 100644 src/testcases/org/apache/poi/ss/usermodel/BaseTestDataValidation.java create mode 100644 test-data/spreadsheet/DataValidations-49244.xlsx diff --git a/src/documentation/content/xdocs/status.xml b/src/documentation/content/xdocs/status.xml index 4a86cee54..b80709fb2 100644 --- a/src/documentation/content/xdocs/status.xml +++ b/src/documentation/content/xdocs/status.xml @@ -34,6 +34,7 @@ + 49244 -Support for data validation for OOXML format 49066 - Worksheet/cell formatting, with view and HTML converter 49020 - Workaround Excel outputting invalid XML in button definitions by not closing BR tags 49050 - Improve performance of AbstractEscherHolderRecord when there are lots of Continue Records diff --git a/src/java/org/apache/poi/hssf/usermodel/DVConstraint.java b/src/java/org/apache/poi/hssf/usermodel/DVConstraint.java index b28a21786..5e56acffa 100644 --- a/src/java/org/apache/poi/hssf/usermodel/DVConstraint.java +++ b/src/java/org/apache/poi/hssf/usermodel/DVConstraint.java @@ -26,68 +26,14 @@ import org.apache.poi.hssf.record.formula.NumberPtg; import org.apache.poi.hssf.record.formula.Ptg; import org.apache.poi.hssf.record.formula.StringPtg; import org.apache.poi.ss.formula.FormulaType; +import org.apache.poi.ss.usermodel.DataValidationConstraint; /** * * @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 { +public class DVConstraint implements DataValidationConstraint { + /* package */ public static final class FormulaPair { private final Ptg[] _formula1; private final Ptg[] _formula2; @@ -211,8 +157,8 @@ public class DVConstraint { 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 @@ -321,6 +267,113 @@ public class DVConstraint { return new DVConstraint(VT.FORMULA, OperatorType.IGNORED, formula, null, null, null, null); } + /* (non-Javadoc) + * @see org.apache.poi.hssf.usermodel.DataValidationConstraint#getValidationType() + */ + public int getValidationType() { + return _validationType; + } + /** + * Convenience method + * @return true if this constraint is a 'list' validation + */ + public boolean isListValidationType() { + return _validationType == VT.LIST; + } + /** + * Convenience method + * @return true if this constraint is a 'list' validation with explicit values + */ + public boolean isExplicitList() { + return _validationType == VT.LIST && _explicitListValues != null; + } + /* (non-Javadoc) + * @see org.apache.poi.hssf.usermodel.DataValidationConstraint#getOperator() + */ + public int getOperator() { + return _operator; + } + /* (non-Javadoc) + * @see org.apache.poi.hssf.usermodel.DataValidationConstraint#setOperator(int) + */ + public void setOperator(int operator) { + _operator = operator; + } + + /* (non-Javadoc) + * @see org.apache.poi.hssf.usermodel.DataValidationConstraint#getExplicitListValues() + */ + public String[] getExplicitListValues() { + return _explicitListValues; + } + /* (non-Javadoc) + * @see org.apache.poi.hssf.usermodel.DataValidationConstraint#setExplicitListValues(java.lang.String[]) + */ + public void setExplicitListValues(String[] explicitListValues) { + if (_validationType != VT.LIST) { + throw new RuntimeException("Cannot setExplicitListValues on non-list constraint"); + } + _formula1 = null; + _explicitListValues = explicitListValues; + } + + /* (non-Javadoc) + * @see org.apache.poi.hssf.usermodel.DataValidationConstraint#getFormula1() + */ + public String getFormula1() { + return _formula1; + } + /* (non-Javadoc) + * @see org.apache.poi.hssf.usermodel.DataValidationConstraint#setFormula1(java.lang.String) + */ + public void setFormula1(String formula1) { + _value1 = null; + _explicitListValues = null; + _formula1 = formula1; + } + + /* (non-Javadoc) + * @see org.apache.poi.hssf.usermodel.DataValidationConstraint#getFormula2() + */ + public String getFormula2() { + return _formula2; + } + /* (non-Javadoc) + * @see org.apache.poi.hssf.usermodel.DataValidationConstraint#setFormula2(java.lang.String) + */ + public void setFormula2(String formula2) { + _value2 = null; + _formula2 = formula2; + } + + /** + * @return the numeric value for expression 1. May be null + */ + public Double getValue1() { + return _value1; + } + /** + * Sets a numeric value for expression 1. + */ + public void setValue1(double value1) { + _formula1 = null; + _value1 = new Double(value1); + } + + /** + * @return the numeric value for expression 2. May be null + */ + public Double getValue2() { + return _value2; + } + /** + * Sets a numeric value for expression 2. + */ + public void setValue2(double value2) { + _formula2 = null; + _value2 = new Double(value2); + } + /** * @return both parsed formulas (for expression 1 and 2). */ @@ -374,110 +427,5 @@ public class DVConstraint { } HSSFWorkbook wb = sheet.getWorkbook(); return HSSFFormulaParser.parse(formula, wb, FormulaType.CELL, wb.getSheetIndex(sheet)); - } - - - /** - * @return data validation type of this constraint - * @see ValidationType - */ - public int getValidationType() { - return _validationType; - } - /** - * Convenience method - * @return true if this constraint is a 'list' validation - */ - public boolean isListValidationType() { - return _validationType == VT.LIST; - } - /** - * Convenience method - * @return true if this constraint is a 'list' validation with explicit values - */ - public boolean isExplicitList() { - return _validationType == VT.LIST && _explicitListValues != null; - } - /** - * @return the operator used for this constraint - * @see OperatorType - */ - public int getOperator() { - return _operator; - } - /** - * Sets the comparison operator for this constraint - * @see OperatorType - */ - public void setOperator(int operator) { - _operator = operator; - } - - public String[] getExplicitListValues() { - return _explicitListValues; - } - public void setExplicitListValues(String[] explicitListValues) { - if (_validationType != VT.LIST) { - throw new RuntimeException("Cannot setExplicitListValues on non-list constraint"); - } - _formula1 = null; - _explicitListValues = explicitListValues; - } - - /** - * @return the formula for expression 1. May be null - */ - public String getFormula1() { - return _formula1; - } - /** - * Sets a formula for expression 1. - */ - public void setFormula1(String formula1) { - _value1 = null; - _explicitListValues = null; - _formula1 = formula1; - } - - /** - * @return the formula for expression 2. May be null - */ - public String getFormula2() { - return _formula2; - } - /** - * Sets a formula for expression 2. - */ - public void setFormula2(String formula2) { - _value2 = null; - _formula2 = formula2; - } - - /** - * @return the numeric value for expression 1. May be null - */ - public Double getValue1() { - return _value1; - } - /** - * Sets a numeric value for expression 1. - */ - public void setValue1(double value1) { - _formula1 = null; - _value1 = new Double(value1); - } - - /** - * @return the numeric value for expression 2. May be null - */ - public Double getValue2() { - return _value2; - } - /** - * Sets a numeric value for expression 2. - */ - public void setValue2(double value2) { - _formula2 = null; - _value2 = new Double(value2); - } + } } diff --git a/src/java/org/apache/poi/hssf/usermodel/HSSFDataValidation.java b/src/java/org/apache/poi/hssf/usermodel/HSSFDataValidation.java index 0704fd5cb..9ea6038d0 100644 --- a/src/java/org/apache/poi/hssf/usermodel/HSSFDataValidation.java +++ b/src/java/org/apache/poi/hssf/usermodel/HSSFDataValidation.java @@ -19,6 +19,9 @@ 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.ss.usermodel.DataValidation; +import org.apache.poi.ss.usermodel.DataValidationConstraint; +import org.apache.poi.ss.usermodel.DataValidationConstraint.ValidationType; import org.apache.poi.ss.util.CellRangeAddressList; /** @@ -26,19 +29,7 @@ import org.apache.poi.ss.util.CellRangeAddressList; * * @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; - } - +public final class HSSFDataValidation implements DataValidation { private String _prompt_title; private String _prompt_text; private String _error_title; @@ -49,7 +40,7 @@ public final class HSSFDataValidation { private boolean _suppress_dropdown_arrow = false; private boolean _showPromptBox = true; private boolean _showErrorBox = true; - private final CellRangeAddressList _regions; + private CellRangeAddressList _regions; private DVConstraint _constraint; /** @@ -57,119 +48,106 @@ public final class HSSFDataValidation { * applied * @param constraint */ - public HSSFDataValidation(CellRangeAddressList regions, DVConstraint constraint) { + public HSSFDataValidation(CellRangeAddressList regions, DataValidationConstraint constraint) { _regions = regions; - _constraint = constraint; + + //FIXME: This cast can be avoided. + _constraint = (DVConstraint)constraint; } + /* (non-Javadoc) + * @see org.apache.poi.hssf.usermodel.DataValidation#getConstraint() + */ + public DataValidationConstraint getValidationConstraint() { + return _constraint; + } + public DVConstraint getConstraint() { return _constraint; } + + public CellRangeAddressList getRegions() { + return _regions; + } - /** - * Sets the error style for error box - * @see ErrorStyle + + /* (non-Javadoc) + * @see org.apache.poi.hssf.usermodel.DataValidation#setErrorStyle(int) */ public void setErrorStyle(int error_style) { _errorStyle = error_style; } - /** - * @return the error style of error box - * @see ErrorStyle + /* (non-Javadoc) + * @see org.apache.poi.hssf.usermodel.DataValidation#getErrorStyle() */ public int getErrorStyle() { return _errorStyle; } - /** - * Sets if this object allows empty as a valid value - * - * @param allowed true if this object should treats empty as valid value , false - * otherwise + /* (non-Javadoc) + * @see org.apache.poi.hssf.usermodel.DataValidation#setEmptyCellAllowed(boolean) */ 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 + /* (non-Javadoc) + * @see org.apache.poi.hssf.usermodel.DataValidation#getEmptyCellAllowed() */ 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 + /* (non-Javadoc) + * @see org.apache.poi.hssf.usermodel.DataValidation#setSuppressDropDownArrow(boolean) */ public void setSuppressDropDownArrow(boolean suppress) { _suppress_dropdown_arrow = suppress; } - /** - * Useful only list validation objects . This method always returns false if - * the object isn't a list validation object - * - * @return true if a list should display the values into a drop down list , - * false otherwise . + /* (non-Javadoc) + * @see org.apache.poi.hssf.usermodel.DataValidation#getSuppressDropDownArrow() */ public boolean getSuppressDropDownArrow() { - if (_constraint.isListValidationType()) { + if (_constraint.getValidationType()==ValidationType.LIST) { return _suppress_dropdown_arrow; } return false; } - /** - * Sets the behaviour when a cell which belongs to this object is selected - * - * @param show true if an prompt box should be displayed , false otherwise + /* (non-Javadoc) + * @see org.apache.poi.hssf.usermodel.DataValidation#setShowPromptBox(boolean) */ public void setShowPromptBox(boolean show) { _showPromptBox = show; } - /** - * @return true if an prompt box should be displayed , false otherwise + /* (non-Javadoc) + * @see org.apache.poi.hssf.usermodel.DataValidation#getShowPromptBox() */ public boolean getShowPromptBox() { return _showPromptBox; } - /** - * Sets the behaviour when an invalid value is entered - * - * @param show true if an error box should be displayed , false otherwise + /* (non-Javadoc) + * @see org.apache.poi.hssf.usermodel.DataValidation#setShowErrorBox(boolean) */ public void setShowErrorBox(boolean show) { _showErrorBox = show; } - /** - * @return true if an error box should be displayed , false otherwise + /* (non-Javadoc) + * @see org.apache.poi.hssf.usermodel.DataValidation#getShowErrorBox() */ 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 + /* (non-Javadoc) + * @see org.apache.poi.hssf.usermodel.DataValidation#createPromptBox(java.lang.String, java.lang.String) */ public void createPromptBox(String title, String text) { _prompt_title = title; @@ -177,28 +155,22 @@ public final class HSSFDataValidation { this.setShowPromptBox(true); } - /** - * @return Prompt box's title or null + /* (non-Javadoc) + * @see org.apache.poi.hssf.usermodel.DataValidation#getPromptBoxTitle() */ public String getPromptBoxTitle() { return _prompt_title; } - /** - * @return Prompt box's text or null + /* (non-Javadoc) + * @see org.apache.poi.hssf.usermodel.DataValidation#getPromptBoxText() */ 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 + /* (non-Javadoc) + * @see org.apache.poi.hssf.usermodel.DataValidation#createErrorBox(java.lang.String, java.lang.String) */ public void createErrorBox(String title, String text) { _error_title = title; @@ -206,15 +178,15 @@ public final class HSSFDataValidation { this.setShowErrorBox(true); } - /** - * @return Error box's title or null + /* (non-Javadoc) + * @see org.apache.poi.hssf.usermodel.DataValidation#getErrorBoxTitle() */ public String getErrorBoxTitle() { return _error_title; } - /** - * @return Error box's text or null + /* (non-Javadoc) + * @see org.apache.poi.hssf.usermodel.DataValidation#getErrorBoxText() */ public String getErrorBoxText() { return _error_text; @@ -227,7 +199,7 @@ public final class HSSFDataValidation { return new DVRecord(_constraint.getValidationType(), _constraint.getOperator(), _errorStyle, _emptyCellAllowed, getSuppressDropDownArrow(), - _constraint.isExplicitList(), + _constraint.getValidationType()==ValidationType.LIST && _constraint.getExplicitListValues()!=null, _showPromptBox, _prompt_title, _prompt_text, _showErrorBox, _error_title, _error_text, fp.getFormula1(), fp.getFormula2(), diff --git a/src/java/org/apache/poi/hssf/usermodel/HSSFDataValidationHelper.java b/src/java/org/apache/poi/hssf/usermodel/HSSFDataValidationHelper.java new file mode 100644 index 000000000..5418f5d3c --- /dev/null +++ b/src/java/org/apache/poi/hssf/usermodel/HSSFDataValidationHelper.java @@ -0,0 +1,118 @@ +/** + * + */ +package org.apache.poi.hssf.usermodel; + +import org.apache.poi.ss.usermodel.DataValidation; +import org.apache.poi.ss.usermodel.DataValidationConstraint; +import org.apache.poi.ss.usermodel.DataValidationHelper; +import org.apache.poi.ss.usermodel.DataValidationConstraint.ValidationType; +import org.apache.poi.ss.util.CellRangeAddressList; + +/** + * @author Radhakrishnan J + * + */ +public class HSSFDataValidationHelper implements DataValidationHelper { + @SuppressWarnings("unused") + private HSSFSheet sheet; + + public HSSFDataValidationHelper(HSSFSheet sheet) { + super(); + this.sheet = sheet; + } + + /* + * (non-Javadoc) + * + * @see + * org.apache.poi.ss.usermodel.DataValidationHelper#createDateConstraint + * (int, java.lang.String, java.lang.String, java.lang.String) + */ + public DataValidationConstraint createDateConstraint(int operatorType, String formula1, String formula2, String dateFormat) { + return DVConstraint.createDateConstraint(operatorType, formula1, formula2, dateFormat); + } + + /* + * (non-Javadoc) + * + * @see + * org.apache.poi.ss.usermodel.DataValidationHelper#createExplicitListConstraint + * (java.lang.String[]) + */ + public DataValidationConstraint createExplicitListConstraint(String[] listOfValues) { + return DVConstraint.createExplicitListConstraint(listOfValues); + } + + /* + * (non-Javadoc) + * + * @see + * org.apache.poi.ss.usermodel.DataValidationHelper#createFormulaListConstraint + * (java.lang.String) + */ + public DataValidationConstraint createFormulaListConstraint(String listFormula) { + return DVConstraint.createFormulaListConstraint(listFormula); + } + + + + public DataValidationConstraint createNumericConstraint(int validationType,int operatorType, String formula1, String formula2) { + return DVConstraint.createNumericConstraint(validationType, operatorType, formula1, formula2); + } + + public DataValidationConstraint createIntegerConstraint(int operatorType, String formula1, String formula2) { + return DVConstraint.createNumericConstraint(ValidationType.INTEGER, operatorType, formula1, formula2); + } + + /* + * (non-Javadoc) + * + * @see + * org.apache.poi.ss.usermodel.DataValidationHelper#createNumericConstraint + * (int, java.lang.String, java.lang.String) + */ + public DataValidationConstraint createDecimalConstraint(int operatorType, String formula1, String formula2) { + return DVConstraint.createNumericConstraint(ValidationType.DECIMAL, operatorType, formula1, formula2); + } + + /* + * (non-Javadoc) + * + * @see + * org.apache.poi.ss.usermodel.DataValidationHelper#createTextLengthConstraint + * (int, java.lang.String, java.lang.String) + */ + public DataValidationConstraint createTextLengthConstraint(int operatorType, String formula1, String formula2) { + return DVConstraint.createNumericConstraint(ValidationType.TEXT_LENGTH, operatorType, formula1, formula2); + } + + /* + * (non-Javadoc) + * + * @see + * org.apache.poi.ss.usermodel.DataValidationHelper#createTimeConstraint + * (int, java.lang.String, java.lang.String, java.lang.String) + */ + public DataValidationConstraint createTimeConstraint(int operatorType, String formula1, String formula2) { + return DVConstraint.createTimeConstraint(operatorType, formula1, formula2); + } + + + + public DataValidationConstraint createCustomConstraint(String formula) { + return DVConstraint.createCustomFormulaConstraint(formula); + } + + /* + * (non-Javadoc) + * + * @see + * org.apache.poi.ss.usermodel.DataValidationHelper#createValidation(org + * .apache.poi.ss.usermodel.DataValidationConstraint, + * org.apache.poi.ss.util.CellRangeAddressList) + */ + public DataValidation createValidation(DataValidationConstraint constraint, CellRangeAddressList cellRangeAddressList) { + return new HSSFDataValidation(cellRangeAddressList, constraint); + } +} diff --git a/src/java/org/apache/poi/hssf/usermodel/HSSFSheet.java b/src/java/org/apache/poi/hssf/usermodel/HSSFSheet.java index 75e48cfa0..8ceb1df43 100644 --- a/src/java/org/apache/poi/hssf/usermodel/HSSFSheet.java +++ b/src/java/org/apache/poi/hssf/usermodel/HSSFSheet.java @@ -51,15 +51,17 @@ import org.apache.poi.hssf.record.formula.FormulaShifter; import org.apache.poi.hssf.record.formula.Ptg; import org.apache.poi.hssf.util.PaneInformation; import org.apache.poi.hssf.util.Region; +import org.apache.poi.ss.SpreadsheetVersion; import org.apache.poi.ss.formula.FormulaType; import org.apache.poi.ss.usermodel.Cell; import org.apache.poi.ss.usermodel.CellRange; import org.apache.poi.ss.usermodel.CellStyle; +import org.apache.poi.ss.usermodel.DataValidation; +import org.apache.poi.ss.usermodel.DataValidationHelper; import org.apache.poi.ss.usermodel.Row; import org.apache.poi.ss.util.CellRangeAddress; import org.apache.poi.ss.util.CellReference; import org.apache.poi.ss.util.SSCellRange; -import org.apache.poi.ss.SpreadsheetVersion; import org.apache.poi.util.POILogFactory; import org.apache.poi.util.POILogger; @@ -373,13 +375,14 @@ public final class HSSFSheet implements org.apache.poi.ss.usermodel.Sheet { * Creates a data validation object * @param dataValidation The Data validation object settings */ - public void addValidationData(HSSFDataValidation dataValidation) { + public void addValidationData(DataValidation dataValidation) { if (dataValidation == null) { throw new IllegalArgumentException("objValidation must not be null"); } + HSSFDataValidation hssfDataValidation = (HSSFDataValidation)dataValidation; DataValidityTable dvt = _sheet.getOrCreateDataValidityTable(); - DVRecord dvRecord = dataValidation.createDVRecord(this); + DVRecord dvRecord = hssfDataValidation.createDVRecord(this); dvt.addDataValidation(dvRecord); } @@ -1997,4 +2000,10 @@ public final class HSSFSheet implements org.apache.poi.ss.usermodel.Sheet { } return result; } + + public DataValidationHelper getDataValidationHelper() { + return new HSSFDataValidationHelper(this); + } + + } diff --git a/src/java/org/apache/poi/ss/usermodel/DataValidation.java b/src/java/org/apache/poi/ss/usermodel/DataValidation.java new file mode 100644 index 000000000..c80ed6a74 --- /dev/null +++ b/src/java/org/apache/poi/ss/usermodel/DataValidation.java @@ -0,0 +1,152 @@ +/* ==================================================================== + Licensed to the Apache Software Foundation (ASF) under one or more + contributor license agreements. See the NOTICE file distributed with + this work for additional information regarding copyright ownership. + The ASF licenses this file to You 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.ss.usermodel; + +import org.apache.poi.ss.util.CellRangeAddressList; + + +public interface DataValidation { + /** + * 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; + } + + public abstract DataValidationConstraint getValidationConstraint(); + + /** + * Sets the error style for error box + * @see ErrorStyle + */ + public abstract void setErrorStyle(int error_style); + + /**o + * @return the error style of error box + * @see ErrorStyle + */ + public abstract int getErrorStyle(); + + /** + * 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 abstract void setEmptyCellAllowed(boolean allowed); + + /** + * Retrieve the settings for empty cells allowed + * + * @return True if this object should treats empty as valid value , false + * otherwise + */ + public abstract boolean getEmptyCellAllowed(); + + /** + * 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 abstract void setSuppressDropDownArrow(boolean suppress); + + /** + * Useful only list validation objects . This method always returns false if + * the object isn't a list validation object + * + * @return true if a list should display the values into a drop down list , + * false otherwise . + */ + public abstract boolean getSuppressDropDownArrow(); + + /** + * 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 abstract void setShowPromptBox(boolean show); + + /** + * @return true if an prompt box should be displayed , false otherwise + */ + public abstract boolean getShowPromptBox(); + + /** + * Sets the behaviour when an invalid value is entered + * + * @param show true if an error box should be displayed , false otherwise + */ + public abstract void setShowErrorBox(boolean show); + + /** + * @return true if an error box should be displayed , false otherwise + */ + public abstract boolean getShowErrorBox(); + + /** + * 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 abstract void createPromptBox(String title, String text); + + /** + * @return Prompt box's title or null + */ + public abstract String getPromptBoxTitle(); + + /** + * @return Prompt box's text or null + */ + public abstract String getPromptBoxText(); + + /** + * 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 abstract void createErrorBox(String title, String text); + + /** + * @return Error box's title or null + */ + public abstract String getErrorBoxTitle(); + + /** + * @return Error box's text or null + */ + public abstract String getErrorBoxText(); + + public abstract CellRangeAddressList getRegions(); + +} diff --git a/src/java/org/apache/poi/ss/usermodel/DataValidationConstraint.java b/src/java/org/apache/poi/ss/usermodel/DataValidationConstraint.java new file mode 100644 index 000000000..e3d2c376d --- /dev/null +++ b/src/java/org/apache/poi/ss/usermodel/DataValidationConstraint.java @@ -0,0 +1,118 @@ +/* ==================================================================== + Licensed to the Apache Software Foundation (ASF) under one or more + contributor license agreements. See the NOTICE file distributed with + this work for additional information regarding copyright ownership. + The ASF licenses this file to You 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.ss.usermodel; + + +public interface DataValidationConstraint { + + /** + * @return data validation type of this constraint + * @see ValidationType + */ + public abstract int getValidationType(); + + /** + * @return the operator used for this constraint + * @see OperatorType + */ + public abstract int getOperator(); + + /** + * Sets the comparison operator for this constraint + * @see OperatorType + */ + public abstract void setOperator(int operator); + + public abstract String[] getExplicitListValues(); + + public abstract void setExplicitListValues(String[] explicitListValues); + + /** + * @return the formula for expression 1. May be null + */ + public abstract String getFormula1(); + + /** + * Sets a formula for expression 1. + */ + public abstract void setFormula1(String formula1); + + /** + * @return the formula for expression 2. May be null + */ + public abstract String getFormula2(); + + /** + * Sets a formula for expression 2. + */ + public abstract void setFormula2(String formula2); + + /** + * 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 */ public 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 + } + } + } +} diff --git a/src/java/org/apache/poi/ss/usermodel/DataValidationHelper.java b/src/java/org/apache/poi/ss/usermodel/DataValidationHelper.java new file mode 100644 index 000000000..2e749467e --- /dev/null +++ b/src/java/org/apache/poi/ss/usermodel/DataValidationHelper.java @@ -0,0 +1,46 @@ +/* ==================================================================== + Licensed to the Apache Software Foundation (ASF) under one or more + contributor license agreements. See the NOTICE file distributed with + this work for additional information regarding copyright ownership. + The ASF licenses this file to You 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.ss.usermodel; + +import org.apache.poi.ss.util.CellRangeAddressList; + +/** + * @author Radhakrishnan J + * + */ +public interface DataValidationHelper { + + DataValidationConstraint createFormulaListConstraint(String listFormula); + + DataValidationConstraint createExplicitListConstraint(String[] listOfValues); + + DataValidationConstraint createNumericConstraint(int validationType,int operatorType, String formula1, String formula2); + + DataValidationConstraint createTextLengthConstraint(int operatorType, String formula1, String formula2); + + DataValidationConstraint createDecimalConstraint(int operatorType, String formula1, String formula2); + + DataValidationConstraint createIntegerConstraint(int operatorType, String formula1, String formula2); + + DataValidationConstraint createDateConstraint(int operatorType, String formula1, String formula2,String dateFormat); + + DataValidationConstraint createTimeConstraint(int operatorType, String formula1, String formula2); + + DataValidationConstraint createCustomConstraint(String formula); + + DataValidation createValidation(DataValidationConstraint constraint,CellRangeAddressList cellRangeAddressList); +} diff --git a/src/java/org/apache/poi/ss/usermodel/Sheet.java b/src/java/org/apache/poi/ss/usermodel/Sheet.java index 084155943..9bef5269d 100644 --- a/src/java/org/apache/poi/ss/usermodel/Sheet.java +++ b/src/java/org/apache/poi/ss/usermodel/Sheet.java @@ -798,4 +798,12 @@ public interface Sheet extends Iterable { * @return the {@link CellRange} of cells affected by this change */ CellRange removeArrayFormula(Cell cell); + + public DataValidationHelper getDataValidationHelper(); + + /** + * Creates a data validation object + * @param dataValidation The Data validation object settings + */ + public void addValidationData(DataValidation dataValidation); } diff --git a/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFDataValidation.java b/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFDataValidation.java new file mode 100644 index 000000000..f1adbd3a5 --- /dev/null +++ b/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFDataValidation.java @@ -0,0 +1,244 @@ +/** + * + */ +package org.apache.poi.xssf.usermodel; + +import java.util.HashMap; +import java.util.Map; + +import org.apache.poi.ss.usermodel.DataValidation; +import org.apache.poi.ss.usermodel.DataValidationConstraint; +import org.apache.poi.ss.usermodel.DataValidationConstraint.ValidationType; +import org.apache.poi.ss.util.CellRangeAddress; +import org.apache.poi.ss.util.CellRangeAddressList; +import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTDataValidation; +import org.openxmlformats.schemas.spreadsheetml.x2006.main.STDataValidationErrorStyle; +import org.openxmlformats.schemas.spreadsheetml.x2006.main.STDataValidationOperator; +import org.openxmlformats.schemas.spreadsheetml.x2006.main.STDataValidationType; +import org.openxmlformats.schemas.spreadsheetml.x2006.main.STDataValidationOperator.Enum; + +/** + * @author Radhakrishnan J + * + */ +public class XSSFDataValidation implements DataValidation { + private CTDataValidation ctDdataValidation; + private XSSFDataValidationConstraint validationConstraint; + private CellRangeAddressList regions; + + static Map operatorTypeMappings = new HashMap(); + static Map operatorTypeReverseMappings = new HashMap(); + static Map validationTypeMappings = new HashMap(); + static Map validationTypeReverseMappings = new HashMap(); + static Map errorStyleMappings = new HashMap(); + static { + errorStyleMappings.put(DataValidation.ErrorStyle.INFO, STDataValidationErrorStyle.INFORMATION); + errorStyleMappings.put(DataValidation.ErrorStyle.STOP, STDataValidationErrorStyle.STOP); + errorStyleMappings.put(DataValidation.ErrorStyle.WARNING, STDataValidationErrorStyle.WARNING); + } + + + static { + operatorTypeMappings.put(DataValidationConstraint.OperatorType.BETWEEN,STDataValidationOperator.BETWEEN); + operatorTypeMappings.put(DataValidationConstraint.OperatorType.NOT_BETWEEN,STDataValidationOperator.NOT_BETWEEN); + operatorTypeMappings.put(DataValidationConstraint.OperatorType.EQUAL,STDataValidationOperator.EQUAL); + operatorTypeMappings.put(DataValidationConstraint.OperatorType.NOT_EQUAL,STDataValidationOperator.NOT_EQUAL); + operatorTypeMappings.put(DataValidationConstraint.OperatorType.GREATER_THAN,STDataValidationOperator.GREATER_THAN); + operatorTypeMappings.put(DataValidationConstraint.OperatorType.GREATER_OR_EQUAL,STDataValidationOperator.GREATER_THAN_OR_EQUAL); + operatorTypeMappings.put(DataValidationConstraint.OperatorType.LESS_THAN,STDataValidationOperator.LESS_THAN); + operatorTypeMappings.put(DataValidationConstraint.OperatorType.LESS_OR_EQUAL,STDataValidationOperator.LESS_THAN_OR_EQUAL); + + for( Map.Entry entry : operatorTypeMappings.entrySet() ) { + operatorTypeReverseMappings.put(entry.getValue(),entry.getKey()); + } + } + + static { + validationTypeMappings.put(DataValidationConstraint.ValidationType.FORMULA,STDataValidationType.CUSTOM); + validationTypeMappings.put(DataValidationConstraint.ValidationType.DATE,STDataValidationType.DATE); + validationTypeMappings.put(DataValidationConstraint.ValidationType.DECIMAL,STDataValidationType.DECIMAL); + validationTypeMappings.put(DataValidationConstraint.ValidationType.LIST,STDataValidationType.LIST); + validationTypeMappings.put(DataValidationConstraint.ValidationType.ANY,STDataValidationType.NONE); + validationTypeMappings.put(DataValidationConstraint.ValidationType.TEXT_LENGTH,STDataValidationType.TEXT_LENGTH); + validationTypeMappings.put(DataValidationConstraint.ValidationType.TIME,STDataValidationType.TIME); + validationTypeMappings.put(DataValidationConstraint.ValidationType.INTEGER,STDataValidationType.WHOLE); + + for( Map.Entry entry : validationTypeMappings.entrySet() ) { + validationTypeReverseMappings.put(entry.getValue(),entry.getKey()); + } + } + + + XSSFDataValidation(CellRangeAddressList regions,CTDataValidation ctDataValidation) { + super(); + this.validationConstraint = getConstraint(ctDataValidation); + this.ctDdataValidation = ctDataValidation; + this.regions = regions; + this.ctDdataValidation.setErrorStyle(STDataValidationErrorStyle.STOP); + this.ctDdataValidation.setAllowBlank(true); + } + + public XSSFDataValidation(XSSFDataValidationConstraint constraint,CellRangeAddressList regions,CTDataValidation ctDataValidation) { + super(); + this.validationConstraint = constraint; + this.ctDdataValidation = ctDataValidation; + this.regions = regions; + this.ctDdataValidation.setErrorStyle(STDataValidationErrorStyle.STOP); + this.ctDdataValidation.setAllowBlank(true); + } + + CTDataValidation getCtDdataValidation() { + return ctDdataValidation; + } + + + + /* (non-Javadoc) + * @see org.apache.poi.ss.usermodel.DataValidation#createErrorBox(java.lang.String, java.lang.String) + */ + public void createErrorBox(String title, String text) { + ctDdataValidation.setErrorTitle(title); + ctDdataValidation.setError(text); + } + + /* (non-Javadoc) + * @see org.apache.poi.ss.usermodel.DataValidation#createPromptBox(java.lang.String, java.lang.String) + */ + public void createPromptBox(String title, String text) { + ctDdataValidation.setPromptTitle(title); + ctDdataValidation.setPrompt(text); + } + + /* (non-Javadoc) + * @see org.apache.poi.ss.usermodel.DataValidation#getEmptyCellAllowed() + */ + public boolean getEmptyCellAllowed() { + return ctDdataValidation.getAllowBlank(); + } + + /* (non-Javadoc) + * @see org.apache.poi.ss.usermodel.DataValidation#getErrorBoxText() + */ + public String getErrorBoxText() { + return ctDdataValidation.getError(); + } + + /* (non-Javadoc) + * @see org.apache.poi.ss.usermodel.DataValidation#getErrorBoxTitle() + */ + public String getErrorBoxTitle() { + return ctDdataValidation.getErrorTitle(); + } + + /* (non-Javadoc) + * @see org.apache.poi.ss.usermodel.DataValidation#getErrorStyle() + */ + public int getErrorStyle() { + return ctDdataValidation.getErrorStyle().intValue(); + } + + /* (non-Javadoc) + * @see org.apache.poi.ss.usermodel.DataValidation#getPromptBoxText() + */ + public String getPromptBoxText() { + return ctDdataValidation.getPrompt(); + } + + /* (non-Javadoc) + * @see org.apache.poi.ss.usermodel.DataValidation#getPromptBoxTitle() + */ + public String getPromptBoxTitle() { + return ctDdataValidation.getPromptTitle(); + } + + /* (non-Javadoc) + * @see org.apache.poi.ss.usermodel.DataValidation#getShowErrorBox() + */ + public boolean getShowErrorBox() { + return ctDdataValidation.getShowErrorMessage(); + } + + /* (non-Javadoc) + * @see org.apache.poi.ss.usermodel.DataValidation#getShowPromptBox() + */ + public boolean getShowPromptBox() { + return ctDdataValidation.getShowInputMessage(); + } + + /* (non-Javadoc) + * @see org.apache.poi.ss.usermodel.DataValidation#getSuppressDropDownArrow() + */ + public boolean getSuppressDropDownArrow() { + return !ctDdataValidation.getShowDropDown(); + } + + /* (non-Javadoc) + * @see org.apache.poi.ss.usermodel.DataValidation#getValidationConstraint() + */ + public DataValidationConstraint getValidationConstraint() { + return validationConstraint; + } + + /* (non-Javadoc) + * @see org.apache.poi.ss.usermodel.DataValidation#setEmptyCellAllowed(boolean) + */ + public void setEmptyCellAllowed(boolean allowed) { + ctDdataValidation.setAllowBlank(allowed); + } + + /* (non-Javadoc) + * @see org.apache.poi.ss.usermodel.DataValidation#setErrorStyle(int) + */ + public void setErrorStyle(int errorStyle) { + ctDdataValidation.setErrorStyle(errorStyleMappings.get(errorStyle)); + } + + /* (non-Javadoc) + * @see org.apache.poi.ss.usermodel.DataValidation#setShowErrorBox(boolean) + */ + public void setShowErrorBox(boolean show) { + ctDdataValidation.setShowErrorMessage(show); + } + + /* (non-Javadoc) + * @see org.apache.poi.ss.usermodel.DataValidation#setShowPromptBox(boolean) + */ + public void setShowPromptBox(boolean show) { + ctDdataValidation.setShowInputMessage(show); + } + + /* (non-Javadoc) + * @see org.apache.poi.ss.usermodel.DataValidation#setSuppressDropDownArrow(boolean) + */ + public void setSuppressDropDownArrow(boolean suppress) { + if (validationConstraint.getValidationType()==ValidationType.LIST) { + ctDdataValidation.setShowDropDown(!suppress); + } + } + + public CellRangeAddressList getRegions() { + return regions; + } + + public String prettyPrint() { + StringBuilder builder = new StringBuilder(); + for(CellRangeAddress address : regions.getCellRangeAddresses()) { + builder.append(address.formatAsString()); + } + builder.append(" => "); + builder.append(this.validationConstraint.prettyPrint()); + return builder.toString(); + } + + private XSSFDataValidationConstraint getConstraint(CTDataValidation ctDataValidation) { + XSSFDataValidationConstraint constraint = null; + String formula1 = ctDataValidation.getFormula1(); + String formula2 = ctDataValidation.getFormula2(); + Enum operator = ctDataValidation.getOperator(); + org.openxmlformats.schemas.spreadsheetml.x2006.main.STDataValidationType.Enum type = ctDataValidation.getType(); + Integer validationType = XSSFDataValidation.validationTypeReverseMappings.get(type); + Integer operatorType = XSSFDataValidation.operatorTypeReverseMappings.get(operator); + constraint = new XSSFDataValidationConstraint(validationType,operatorType, formula1,formula2); + return constraint; + } +} diff --git a/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFDataValidationConstraint.java b/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFDataValidationConstraint.java new file mode 100644 index 000000000..f09466d97 --- /dev/null +++ b/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFDataValidationConstraint.java @@ -0,0 +1,194 @@ +/** + * + */ +package org.apache.poi.xssf.usermodel; + +import java.util.Arrays; + +import org.apache.poi.ss.usermodel.DataValidationConstraint; +import org.openxmlformats.schemas.spreadsheetml.x2006.main.STDataValidationType; +import org.openxmlformats.schemas.spreadsheetml.x2006.main.STDataValidationOperator.Enum; + +/** + * @author Radhakrishnan J + * + */ +public class XSSFDataValidationConstraint implements DataValidationConstraint { + private String formula1; + private String formula2; + private int validationType = -1; + private int operator = -1; + private String[] explicitListOfValues; + + public XSSFDataValidationConstraint(String[] explicitListOfValues) { + if( explicitListOfValues==null || explicitListOfValues.length==0) { + throw new IllegalArgumentException("List validation with explicit values must specify at least one value"); + } + this.validationType = ValidationType.LIST; + setExplicitListValues(explicitListOfValues); + + validate(); + } + + public XSSFDataValidationConstraint(int validationType,String formula1) { + super(); + setFormula1(formula1); + this.validationType = validationType; + validate(); + } + + + + public XSSFDataValidationConstraint(int validationType, int operator,String formula1) { + super(); + setFormula1(formula1); + this.validationType = validationType; + this.operator = operator; + validate(); + } + + public XSSFDataValidationConstraint(int validationType, int operator,String formula1, String formula2) { + super(); + setFormula1(formula1); + setFormula2(formula2); + this.validationType = validationType; + this.operator = operator; + + validate(); + + //FIXME: Need to confirm if this is not a formula. + if( ValidationType.LIST==validationType) { + explicitListOfValues = formula1.split(","); + } + } + + /* (non-Javadoc) + * @see org.apache.poi.ss.usermodel.DataValidationConstraint#getExplicitListValues() + */ + public String[] getExplicitListValues() { + return explicitListOfValues; + } + + /* (non-Javadoc) + * @see org.apache.poi.ss.usermodel.DataValidationConstraint#getFormula1() + */ + public String getFormula1() { + return formula1; + } + + /* (non-Javadoc) + * @see org.apache.poi.ss.usermodel.DataValidationConstraint#getFormula2() + */ + public String getFormula2() { + return formula2; + } + + /* (non-Javadoc) + * @see org.apache.poi.ss.usermodel.DataValidationConstraint#getOperator() + */ + public int getOperator() { + return operator; + } + + /* (non-Javadoc) + * @see org.apache.poi.ss.usermodel.DataValidationConstraint#getValidationType() + */ + public int getValidationType() { + return validationType; + } + + /* (non-Javadoc) + * @see org.apache.poi.ss.usermodel.DataValidationConstraint#setExplicitListValues(java.lang.String[]) + */ + public void setExplicitListValues(String[] explicitListValues) { + this.explicitListOfValues = explicitListValues; + if( explicitListOfValues!=null && explicitListOfValues.length > 0 ) { + StringBuilder builder = new StringBuilder("\""); + for (int i = 0; i < explicitListValues.length; i++) { + String string = explicitListValues[i]; + if( builder.length() > 1) { + builder.append(","); + } + builder.append(string); + } + builder.append("\""); + setFormula1(builder.toString()); + } + } + + /* (non-Javadoc) + * @see org.apache.poi.ss.usermodel.DataValidationConstraint#setFormula1(java.lang.String) + */ + public void setFormula1(String formula1) { + this.formula1 = removeLeadingEquals(formula1); + } + + protected String removeLeadingEquals(String formula1) { + return isFormulaEmpty(formula1) ? formula1 : formula1.charAt(0)=='=' ? formula1.substring(1) : formula1; + } + + /* (non-Javadoc) + * @see org.apache.poi.ss.usermodel.DataValidationConstraint#setFormula2(java.lang.String) + */ + public void setFormula2(String formula2) { + this.formula2 = removeLeadingEquals(formula2); + } + + /* (non-Javadoc) + * @see org.apache.poi.ss.usermodel.DataValidationConstraint#setOperator(int) + */ + public void setOperator(int operator) { + this.operator = operator; + } + + public void validate() { + if (validationType==ValidationType.ANY) { + return; + } + + if (validationType==ValidationType.LIST ) { + if (isFormulaEmpty(formula1)) { + throw new IllegalArgumentException("A valid formula or a list of values must be specified for list validation."); + } + } else { + if( isFormulaEmpty(formula1) ) { + throw new IllegalArgumentException("Formula is not specified. Formula is required for all validation types except explicit list validation."); + } + + if( validationType!= ValidationType.FORMULA ) { + if (operator==-1) { + throw new IllegalArgumentException("This validation type requires an operator to be specified."); + } else if (( operator==OperatorType.BETWEEN || operator==OperatorType.NOT_BETWEEN) && isFormulaEmpty(formula2)) { + throw new IllegalArgumentException("Between and not between comparisons require two formulae to be specified."); + } + } + } + } + + protected boolean isFormulaEmpty(String formula1) { + return formula1 == null || formula1.trim().length()==0; + } + + public String prettyPrint() { + StringBuilder builder = new StringBuilder(); + STDataValidationType.Enum vt = XSSFDataValidation.validationTypeMappings.get(validationType); + Enum ot = XSSFDataValidation.operatorTypeMappings.get(operator); + builder.append(vt); + builder.append(' '); + if (validationType!=ValidationType.ANY) { + if (validationType != ValidationType.LIST && validationType != ValidationType.ANY && validationType != ValidationType.FORMULA) { + builder.append(",").append(ot).append(", "); + } + final String QUOTE = ""; + if (validationType == ValidationType.LIST && explicitListOfValues != null) { + builder.append(QUOTE).append(Arrays.asList(explicitListOfValues)).append(QUOTE).append(' '); + } else { + builder.append(QUOTE).append(formula1).append(QUOTE).append(' '); + } + if (formula2 != null) { + builder.append(QUOTE).append(formula2).append(QUOTE).append(' '); + } + } + return builder.toString(); + } +} diff --git a/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFDataValidationHelper.java b/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFDataValidationHelper.java new file mode 100644 index 000000000..00b2a3a4f --- /dev/null +++ b/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFDataValidationHelper.java @@ -0,0 +1,155 @@ +/** + * + */ +package org.apache.poi.xssf.usermodel; + +import java.util.ArrayList; +import java.util.List; + +import org.apache.poi.ss.usermodel.DataValidation; +import org.apache.poi.ss.usermodel.DataValidationConstraint; +import org.apache.poi.ss.usermodel.DataValidationHelper; +import org.apache.poi.ss.usermodel.DataValidationConstraint.ValidationType; +import org.apache.poi.ss.util.CellRangeAddress; +import org.apache.poi.ss.util.CellRangeAddressList; +import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTDataValidation; +import org.openxmlformats.schemas.spreadsheetml.x2006.main.STDataValidationType; + +/** + * @author Radhakrishnan J + * + */ +public class XSSFDataValidationHelper implements DataValidationHelper { + private XSSFSheet xssfSheet; + + + public XSSFDataValidationHelper(XSSFSheet xssfSheet) { + super(); + this.xssfSheet = xssfSheet; + } + + /* (non-Javadoc) + * @see org.apache.poi.ss.usermodel.DataValidationHelper#createDateConstraint(int, java.lang.String, java.lang.String, java.lang.String) + */ + public DataValidationConstraint createDateConstraint(int operatorType, String formula1, String formula2, String dateFormat) { + return new XSSFDataValidationConstraint(ValidationType.DATE, operatorType,formula1, formula2); + } + + /* (non-Javadoc) + * @see org.apache.poi.ss.usermodel.DataValidationHelper#createDecimalConstraint(int, java.lang.String, java.lang.String) + */ + public DataValidationConstraint createDecimalConstraint(int operatorType, String formula1, String formula2) { + return new XSSFDataValidationConstraint(ValidationType.DECIMAL, operatorType,formula1, formula2); + } + + /* (non-Javadoc) + * @see org.apache.poi.ss.usermodel.DataValidationHelper#createExplicitListConstraint(java.lang.String[]) + */ + public DataValidationConstraint createExplicitListConstraint(String[] listOfValues) { + return new XSSFDataValidationConstraint(listOfValues); + } + + /* (non-Javadoc) + * @see org.apache.poi.ss.usermodel.DataValidationHelper#createFormulaListConstraint(java.lang.String) + */ + public DataValidationConstraint createFormulaListConstraint(String listFormula) { + return new XSSFDataValidationConstraint(ValidationType.LIST, listFormula); + } + + + + public DataValidationConstraint createNumericConstraint(int validationType, int operatorType, String formula1, String formula2) { + if( validationType==ValidationType.INTEGER) { + return createIntegerConstraint(operatorType, formula1, formula2); + } else if ( validationType==ValidationType.DECIMAL) { + return createDecimalConstraint(operatorType, formula1, formula2); + } else if ( validationType==ValidationType.TEXT_LENGTH) { + return createTextLengthConstraint(operatorType, formula1, formula2); + } + return null; + } + + /* (non-Javadoc) + * @see org.apache.poi.ss.usermodel.DataValidationHelper#createIntegerConstraint(int, java.lang.String, java.lang.String) + */ + public DataValidationConstraint createIntegerConstraint(int operatorType, String formula1, String formula2) { + return new XSSFDataValidationConstraint(ValidationType.INTEGER, operatorType,formula1,formula2); + } + + /* (non-Javadoc) + * @see org.apache.poi.ss.usermodel.DataValidationHelper#createTextLengthConstraint(int, java.lang.String, java.lang.String) + */ + public DataValidationConstraint createTextLengthConstraint(int operatorType, String formula1, String formula2) { + return new XSSFDataValidationConstraint(ValidationType.TEXT_LENGTH, operatorType,formula1,formula2); + } + + /* (non-Javadoc) + * @see org.apache.poi.ss.usermodel.DataValidationHelper#createTimeConstraint(int, java.lang.String, java.lang.String, java.lang.String) + */ + public DataValidationConstraint createTimeConstraint(int operatorType, String formula1, String formula2) { + return new XSSFDataValidationConstraint(ValidationType.TIME, operatorType,formula1,formula2); + } + + public DataValidationConstraint createCustomConstraint(String formula) { + return new XSSFDataValidationConstraint(ValidationType.FORMULA, formula); + } + + /* (non-Javadoc) + * @see org.apache.poi.ss.usermodel.DataValidationHelper#createValidation(org.apache.poi.ss.usermodel.DataValidationConstraint, org.apache.poi.ss.util.CellRangeAddressList) + */ + public DataValidation createValidation(DataValidationConstraint constraint, CellRangeAddressList cellRangeAddressList) { + XSSFDataValidationConstraint dataValidationConstraint = (XSSFDataValidationConstraint)constraint; + CTDataValidation newDataValidation = CTDataValidation.Factory.newInstance(); + + int validationType = constraint.getValidationType(); + switch(validationType) { + case DataValidationConstraint.ValidationType.LIST: + newDataValidation.setType(STDataValidationType.LIST); + newDataValidation.setFormula1(constraint.getFormula1()); + break; + case DataValidationConstraint.ValidationType.ANY: + newDataValidation.setType(STDataValidationType.NONE); + break; + case DataValidationConstraint.ValidationType.TEXT_LENGTH: + newDataValidation.setType(STDataValidationType.TEXT_LENGTH); + break; + case DataValidationConstraint.ValidationType.DATE: + newDataValidation.setType(STDataValidationType.DATE); + break; + case DataValidationConstraint.ValidationType.INTEGER: + newDataValidation.setType(STDataValidationType.WHOLE); + break; + case DataValidationConstraint.ValidationType.DECIMAL: + newDataValidation.setType(STDataValidationType.DECIMAL); + break; + case DataValidationConstraint.ValidationType.TIME: + newDataValidation.setType(STDataValidationType.TIME); + break; + case DataValidationConstraint.ValidationType.FORMULA: + newDataValidation.setType(STDataValidationType.CUSTOM); + break; + default: + newDataValidation.setType(STDataValidationType.NONE); + } + + if (validationType!=ValidationType.ANY && validationType!=ValidationType.LIST) { + newDataValidation.setOperator(XSSFDataValidation.operatorTypeMappings.get(constraint.getOperator())); + if (constraint.getFormula1() != null) { + newDataValidation.setFormula1(constraint.getFormula1()); + } + if (constraint.getFormula2() != null) { + newDataValidation.setFormula2(constraint.getFormula2()); + } + } + + CellRangeAddress[] cellRangeAddresses = cellRangeAddressList.getCellRangeAddresses(); + List sqref = new ArrayList(); + for (int i = 0; i < cellRangeAddresses.length; i++) { + CellRangeAddress cellRangeAddress = cellRangeAddresses[i]; + sqref.add(cellRangeAddress.formatAsString()); + } + newDataValidation.setSqref(sqref); + + return new XSSFDataValidation(dataValidationConstraint,cellRangeAddressList,newDataValidation); + } +} diff --git a/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFSheet.java b/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFSheet.java index 08e5c95ea..14fcd81f3 100644 --- a/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFSheet.java +++ b/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFSheet.java @@ -42,11 +42,14 @@ import org.apache.poi.ss.SpreadsheetVersion; import org.apache.poi.ss.usermodel.Cell; import org.apache.poi.ss.usermodel.CellRange; import org.apache.poi.ss.usermodel.CellStyle; +import org.apache.poi.ss.usermodel.DataValidation; +import org.apache.poi.ss.usermodel.DataValidationHelper; import org.apache.poi.ss.usermodel.Footer; import org.apache.poi.ss.usermodel.Header; import org.apache.poi.ss.usermodel.Row; import org.apache.poi.ss.usermodel.Sheet; import org.apache.poi.ss.util.CellRangeAddress; +import org.apache.poi.ss.util.CellRangeAddressList; import org.apache.poi.ss.util.CellReference; import org.apache.poi.ss.util.SSCellRange; import org.apache.poi.util.Internal; @@ -58,7 +61,41 @@ import org.apache.poi.xssf.usermodel.helpers.XSSFRowShifter; import org.apache.xmlbeans.XmlException; import org.apache.xmlbeans.XmlOptions; import org.openxmlformats.schemas.officeDocument.x2006.relationships.STRelationshipId; -import org.openxmlformats.schemas.spreadsheetml.x2006.main.*; +import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTBreak; +import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTCell; +import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTCellFormula; +import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTCol; +import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTCols; +import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTComment; +import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTCommentList; +import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTDataValidation; +import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTDataValidations; +import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTDrawing; +import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTHeaderFooter; +import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTHyperlink; +import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTLegacyDrawing; +import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTMergeCell; +import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTMergeCells; +import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTOutlinePr; +import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTPageBreak; +import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTPageMargins; +import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTPageSetUpPr; +import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTPane; +import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTPrintOptions; +import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTRow; +import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTSelection; +import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTSheet; +import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTSheetData; +import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTSheetFormatPr; +import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTSheetPr; +import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTSheetProtection; +import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTSheetView; +import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTSheetViews; +import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTWorksheet; +import org.openxmlformats.schemas.spreadsheetml.x2006.main.STCellFormulaType; +import org.openxmlformats.schemas.spreadsheetml.x2006.main.STPane; +import org.openxmlformats.schemas.spreadsheetml.x2006.main.STPaneState; +import org.openxmlformats.schemas.spreadsheetml.x2006.main.WorksheetDocument; /** * High level representation of a SpreadsheetML worksheet. @@ -82,6 +119,7 @@ public class XSSFSheet extends POIXMLDocumentPart implements Sheet { private CommentsTable sheetComments; private Map sharedFormulas; private List arrayFormulas; + private XSSFDataValidationHelper dataValidationHelper; /** * Creates new XSSFSheet - called by XSSFWorkbook to create a sheet from scratch. @@ -90,6 +128,7 @@ public class XSSFSheet extends POIXMLDocumentPart implements Sheet { */ protected XSSFSheet() { super(); + dataValidationHelper = new XSSFDataValidationHelper(this); onDocumentCreate(); } @@ -102,6 +141,7 @@ public class XSSFSheet extends POIXMLDocumentPart implements Sheet { */ protected XSSFSheet(PackagePart part, PackageRelationship rel) { super(part, rel); + dataValidationHelper = new XSSFDataValidationHelper(this); } /** @@ -2794,4 +2834,49 @@ public class XSSFSheet extends POIXMLDocumentPart implements Sheet { String ref = ((XSSFCell)cell).getCTCell().getR(); throw new IllegalArgumentException("Cell " + ref + " is not part of an array formula."); } + + + public DataValidationHelper getDataValidationHelper() { + return dataValidationHelper; + } + + public List getDataValidations() { + List xssfValidations = new ArrayList(); + CTDataValidations dataValidations = this.worksheet.getDataValidations(); + if( dataValidations!=null && dataValidations.getCount() > 0 ) { + CTDataValidation[] dataValidationList = dataValidations.getDataValidationArray(); + for (CTDataValidation ctDataValidation : dataValidationList) { + CellRangeAddressList addressList = new CellRangeAddressList(); + + @SuppressWarnings("unchecked") + List sqref = ctDataValidation.getSqref(); + for (String stRef : sqref) { + String[] regions = stRef.split(" "); + for (int i = 0; i < regions.length; i++) { + String[] parts = regions[i].split(":"); + CellReference begin = new CellReference(parts[0]); + CellReference end = parts.length > 1 ? new CellReference(parts[1]) : begin; + CellRangeAddress cellRangeAddress = new CellRangeAddress(begin.getRow(), end.getRow(), begin.getCol(), end.getCol()); + addressList.addCellRangeAddress(cellRangeAddress); + } + } + XSSFDataValidation xssfDataValidation = new XSSFDataValidation(addressList, ctDataValidation); + xssfValidations.add(xssfDataValidation); + } + } + return xssfValidations; + } + + public void addValidationData(DataValidation dataValidation) { + XSSFDataValidation xssfDataValidation = (XSSFDataValidation)dataValidation; + CTDataValidations dataValidations = worksheet.getDataValidations(); + if( dataValidations==null ) { + dataValidations = worksheet.addNewDataValidations(); + } + int currentCount = dataValidations.getDataValidationArray().length; + CTDataValidation newval = dataValidations.addNewDataValidation(); + newval.set(xssfDataValidation.getCtDdataValidation()); + dataValidations.setCount(currentCount + 1); + + } } diff --git a/src/ooxml/testcases/org/apache/poi/xssf/usermodel/TestXSSFDataValidation.java b/src/ooxml/testcases/org/apache/poi/xssf/usermodel/TestXSSFDataValidation.java new file mode 100644 index 000000000..a89c43bd8 --- /dev/null +++ b/src/ooxml/testcases/org/apache/poi/xssf/usermodel/TestXSSFDataValidation.java @@ -0,0 +1,242 @@ +/* ==================================================================== + Licensed to the Apache Software Foundation (ASF) under one or more + contributor license agreements. See the NOTICE file distributed with + this work for additional information regarding copyright ownership. + The ASF licenses this file to You 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.xssf.usermodel; + +import java.math.BigDecimal; +import java.util.List; + +import org.apache.poi.ss.usermodel.*; +import org.apache.poi.ss.usermodel.DataValidationConstraint.OperatorType; +import org.apache.poi.ss.usermodel.DataValidationConstraint.ValidationType; +import org.apache.poi.ss.util.CellRangeAddress; +import org.apache.poi.ss.util.CellRangeAddressList; +import org.apache.poi.ss.util.CellReference; +import org.apache.poi.xssf.XSSFITestDataProvider; +import org.apache.poi.xssf.XSSFTestDataSamples; + +public class TestXSSFDataValidation extends BaseTestDataValidation { + + public TestXSSFDataValidation(){ + super(XSSFITestDataProvider.instance); + } + + public void testAddValidations() throws Exception { + XSSFWorkbook workbook = XSSFTestDataSamples.openSampleWorkbook("DataValidations-49244.xlsx"); + Sheet sheet = workbook.getSheetAt(0); + List dataValidations = ((XSSFSheet)sheet).getDataValidations(); + +/** + * For each validation type, there are two cells with the same validation. This tests + * application of a single validation definition to multiple cells. + * + * For list ( 3 validations for explicit and 3 for formula ) + * - one validation that allows blank. + * - one that does not allow blank. + * - one that does not show the drop down arrow. + * = 2 + * + * For number validations ( integer/decimal and text length ) with 8 different types of operators. + * = 50 + * + * = 52 ( Total ) + */ + assertEquals(52,dataValidations.size()); + + DataValidationHelper dataValidationHelper = sheet.getDataValidationHelper(); + int[] validationTypes = new int[]{ValidationType.INTEGER,ValidationType.DECIMAL,ValidationType.TEXT_LENGTH}; + + int[] singleOperandOperatorTypes = new int[]{ + OperatorType.LESS_THAN,OperatorType.LESS_OR_EQUAL, + OperatorType.GREATER_THAN,OperatorType.GREATER_OR_EQUAL, + OperatorType.EQUAL,OperatorType.NOT_EQUAL + } ; + int[] doubleOperandOperatorTypes = new int[]{ + OperatorType.BETWEEN,OperatorType.NOT_BETWEEN + }; + + BigDecimal value = new BigDecimal("10"),value2 = new BigDecimal("20"); + BigDecimal dvalue = new BigDecimal("10.001"),dvalue2 = new BigDecimal("19.999"); + final int lastRow = sheet.getLastRowNum(); + int offset = lastRow + 3; + + int lastKnownNumValidations = dataValidations.size(); + + Row row = sheet.createRow(offset++); + Cell cell = row.createCell(0); + DataValidationConstraint explicitListValidation = dataValidationHelper.createExplicitListConstraint(new String[]{"MA","MI","CA"}); + CellRangeAddressList cellRangeAddressList = new CellRangeAddressList(); + cellRangeAddressList.addCellRangeAddress(cell.getRowIndex(), cell.getColumnIndex(), cell.getRowIndex(), cell.getColumnIndex()); + DataValidation dataValidation = dataValidationHelper.createValidation(explicitListValidation, cellRangeAddressList); + setOtherValidationParameters(dataValidation); + sheet.addValidationData(dataValidation); + lastKnownNumValidations++; + + row = sheet.createRow(offset++); + cell = row.createCell(0); + + cellRangeAddressList = new CellRangeAddressList(); + cellRangeAddressList.addCellRangeAddress(cell.getRowIndex(), cell.getColumnIndex(), cell.getRowIndex(), cell.getColumnIndex()); + + Cell firstCell = row.createCell(1);firstCell.setCellValue("UT"); + Cell secondCell = row.createCell(2);secondCell.setCellValue("MN"); + Cell thirdCell = row.createCell(3);thirdCell.setCellValue("IL"); + + int rowNum = row.getRowNum() + 1; + String listFormula = new StringBuilder("$B$").append(rowNum).append(":").append("$D$").append(rowNum).toString(); + DataValidationConstraint formulaListValidation = dataValidationHelper.createFormulaListConstraint(listFormula); + + dataValidation = dataValidationHelper.createValidation(formulaListValidation, cellRangeAddressList); + setOtherValidationParameters(dataValidation); + sheet.addValidationData(dataValidation); + lastKnownNumValidations++; + + offset++; + offset++; + + for (int i = 0; i < validationTypes.length; i++) { + int validationType = validationTypes[i]; + offset = offset + 2; + final Row row0 = sheet.createRow(offset++); + Cell cell_10 = row0.createCell(0); + cell_10.setCellValue(validationType==ValidationType.DECIMAL ? "Decimal " : validationType==ValidationType.INTEGER ? "Integer" : "Text Length"); + offset++; + for (int j = 0; j < singleOperandOperatorTypes.length; j++) { + int operatorType = singleOperandOperatorTypes[j]; + final Row row1 = sheet.createRow(offset++); + + //For Integer (> and >=) we add 1 extra cell for validations whose formulae reference other cells. + final Row row2 = i==0 && j < 2 ? sheet.createRow(offset++) : null; + + cell_10 = row1.createCell(0); + cell_10.setCellValue(XSSFDataValidation.operatorTypeMappings.get(operatorType).toString()); + Cell cell_11 = row1.createCell(1); + Cell cell_21 = row1.createCell(2); + Cell cell_22 = i==0 && j < 2 ? row2.createCell(2) : null; + + Cell cell_13 = row1.createCell(3); + + + cell_13.setCellType(Cell.CELL_TYPE_NUMERIC); + cell_13.setCellValue(validationType==ValidationType.DECIMAL ? dvalue.doubleValue() : value.intValue()); + + + //First create value based validation; + DataValidationConstraint constraint = dataValidationHelper.createNumericConstraint(validationType,operatorType, value.toString(), null); + cellRangeAddressList = new CellRangeAddressList(); + cellRangeAddressList.addCellRangeAddress(new CellRangeAddress(cell_11.getRowIndex(),cell_11.getRowIndex(),cell_11.getColumnIndex(),cell_11.getColumnIndex())); + DataValidation validation = dataValidationHelper.createValidation(constraint, cellRangeAddressList); + setOtherValidationParameters(validation); + sheet.addValidationData(validation); + assertEquals(++lastKnownNumValidations,((XSSFSheet)sheet).getDataValidations().size()); + + //Now create real formula based validation. + String formula1 = new CellReference(cell_13.getRowIndex(),cell_13.getColumnIndex()).formatAsString(); + constraint = dataValidationHelper.createNumericConstraint(validationType,operatorType, formula1, null); + if (i==0 && j==0) { + cellRangeAddressList = new CellRangeAddressList(); + cellRangeAddressList.addCellRangeAddress(new CellRangeAddress(cell_21.getRowIndex(), cell_21.getRowIndex(), cell_21.getColumnIndex(), cell_21.getColumnIndex())); + validation = dataValidationHelper.createValidation(constraint, cellRangeAddressList); + setOtherValidationParameters(validation); + sheet.addValidationData(validation); + assertEquals(++lastKnownNumValidations, ((XSSFSheet) sheet).getDataValidations().size()); + + cellRangeAddressList = new CellRangeAddressList(); + cellRangeAddressList.addCellRangeAddress(new CellRangeAddress(cell_22.getRowIndex(), cell_22.getRowIndex(), cell_22.getColumnIndex(), cell_22.getColumnIndex())); + validation = dataValidationHelper.createValidation(constraint, cellRangeAddressList); + setOtherValidationParameters( validation); + sheet.addValidationData(validation); + assertEquals(++lastKnownNumValidations, ((XSSFSheet) sheet).getDataValidations().size()); + } else if(i==0 && j==1 ){ + cellRangeAddressList = new CellRangeAddressList(); + cellRangeAddressList.addCellRangeAddress(new CellRangeAddress(cell_21.getRowIndex(), cell_21.getRowIndex(), cell_21.getColumnIndex(), cell_21.getColumnIndex())); + cellRangeAddressList.addCellRangeAddress(new CellRangeAddress(cell_22.getRowIndex(), cell_22.getRowIndex(), cell_22.getColumnIndex(), cell_22.getColumnIndex())); + validation = dataValidationHelper.createValidation(constraint, cellRangeAddressList); + setOtherValidationParameters( validation); + sheet.addValidationData(validation); + assertEquals(++lastKnownNumValidations, ((XSSFSheet) sheet).getDataValidations().size()); + } else { + cellRangeAddressList = new CellRangeAddressList(); + cellRangeAddressList.addCellRangeAddress(new CellRangeAddress(cell_21.getRowIndex(), cell_21.getRowIndex(), cell_21.getColumnIndex(), cell_21.getColumnIndex())); + validation = dataValidationHelper.createValidation(constraint, cellRangeAddressList); + setOtherValidationParameters(validation); + sheet.addValidationData(validation); + assertEquals(++lastKnownNumValidations, ((XSSFSheet) sheet).getDataValidations().size()); + } + } + + for (int j = 0; j < doubleOperandOperatorTypes.length; j++) { + int operatorType = doubleOperandOperatorTypes[j]; + final Row row1 = sheet.createRow(offset++); + + cell_10 = row1.createCell(0); + cell_10.setCellValue(XSSFDataValidation.operatorTypeMappings.get(operatorType).toString()); + + Cell cell_11 = row1.createCell(1); + Cell cell_21 = row1.createCell(2); + + Cell cell_13 = row1.createCell(3); + Cell cell_14 = row1.createCell(4); + + + String value1String = validationType==ValidationType.DECIMAL ? dvalue.toString() : value.toString(); + cell_13.setCellType(Cell.CELL_TYPE_NUMERIC); + cell_13.setCellValue(validationType==ValidationType.DECIMAL ? dvalue.doubleValue() : value.intValue()); + + String value2String = validationType==ValidationType.DECIMAL ? dvalue2.toString() : value2.toString(); + cell_14.setCellType(Cell.CELL_TYPE_NUMERIC); + cell_14.setCellValue(validationType==ValidationType.DECIMAL ? dvalue2.doubleValue() : value2.intValue()); + + + //First create value based validation; + DataValidationConstraint constraint = dataValidationHelper.createNumericConstraint(validationType,operatorType, value1String, value2String); + cellRangeAddressList = new CellRangeAddressList(); + cellRangeAddressList.addCellRangeAddress(new CellRangeAddress(cell_11.getRowIndex(),cell_11.getRowIndex(),cell_11.getColumnIndex(),cell_11.getColumnIndex())); + DataValidation validation = dataValidationHelper.createValidation(constraint, cellRangeAddressList); + setOtherValidationParameters(validation); + sheet.addValidationData(validation); + assertEquals(++lastKnownNumValidations,((XSSFSheet)sheet).getDataValidations().size()); + + + //Now create real formula based validation. + String formula1 = new CellReference(cell_13.getRowIndex(),cell_13.getColumnIndex()).formatAsString(); + String formula2 = new CellReference(cell_14.getRowIndex(),cell_14.getColumnIndex()).formatAsString(); + constraint = dataValidationHelper.createNumericConstraint(validationType,operatorType, formula1, formula2); + cellRangeAddressList = new CellRangeAddressList(); + cellRangeAddressList.addCellRangeAddress(new CellRangeAddress(cell_21.getRowIndex(),cell_21.getRowIndex(),cell_21.getColumnIndex(),cell_21.getColumnIndex())); + validation = dataValidationHelper.createValidation(constraint, cellRangeAddressList); + + setOtherValidationParameters(validation); + sheet.addValidationData(validation); + assertEquals(++lastKnownNumValidations,((XSSFSheet)sheet).getDataValidations().size()); + } + } + + workbook = XSSFTestDataSamples.writeOutAndReadBack(workbook); + Sheet sheetAt = workbook.getSheetAt(0); + assertEquals(lastKnownNumValidations,((XSSFSheet)sheetAt).getDataValidations().size()); + } + + protected void setOtherValidationParameters(DataValidation validation) { + boolean yesNo = true; + validation.setEmptyCellAllowed(yesNo); + validation.setShowErrorBox(yesNo); + validation.setShowPromptBox(yesNo); + validation.createErrorBox("Error Message Title", "Error Message"); + validation.createPromptBox("Prompt", "Enter some value"); + validation.setSuppressDropDownArrow(yesNo); + } +} diff --git a/src/testcases/org/apache/poi/hssf/usermodel/TestDataValidation.java b/src/testcases/org/apache/poi/hssf/usermodel/TestDataValidation.java index 9c850e40f..a24013f84 100644 --- a/src/testcases/org/apache/poi/hssf/usermodel/TestDataValidation.java +++ b/src/testcases/org/apache/poi/hssf/usermodel/TestDataValidation.java @@ -28,6 +28,7 @@ import java.io.PrintStream; import junit.framework.AssertionFailedError; import junit.framework.TestCase; +import org.apache.poi.hssf.HSSFITestDataProvider; import org.apache.poi.hssf.HSSFTestDataSamples; import org.apache.poi.hssf.eventmodel.ERFListener; import org.apache.poi.hssf.eventmodel.EventRecordFactory; @@ -35,6 +36,7 @@ import org.apache.poi.hssf.record.DVRecord; import org.apache.poi.hssf.record.RecordFormatException; import org.apache.poi.hssf.util.HSSFColor; import org.apache.poi.poifs.filesystem.POIFSFileSystem; +import org.apache.poi.ss.usermodel.*; import org.apache.poi.ss.util.CellRangeAddress; import org.apache.poi.ss.util.CellRangeAddressList; @@ -43,448 +45,14 @@ import org.apache.poi.ss.util.CellRangeAddressList; * * @author Dragos Buleandra ( dragos.buleandra@trade2b.ro ) */ -public final class TestDataValidation extends TestCase { +public final class TestDataValidation extends BaseTestDataValidation { - /** Convenient access to ERROR_STYLE constants */ - /*package*/ static final HSSFDataValidation.ErrorStyle ES = null; - /** Convenient access to OPERATOR constants */ - /*package*/ static final DVConstraint.ValidationType VT = null; - /** Convenient access to OPERATOR constants */ - /*package*/ 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 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(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.createCustomFormulaConstraint(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(1); - cell.setCellStyle(style_1); - setCellValue(cell, strCondition); - // allow empty cells - cell = row.createCell(2); - cell.setCellStyle(style_2); - setCellValue(cell, ((allowEmpty) ? "yes" : "no")); - // show input box - cell = row.createCell(3); - cell.setCellStyle(style_2); - setCellValue(cell, ((inputBox) ? "yes" : "no")); - // show error box - cell = row.createCell(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(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); - } - } - - /** - * 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); - } - - 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 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; - } - - - 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 CellRangeAddress(sheet.getPhysicalNumberOfRows()-1, sheet.getPhysicalNumberOfRows()-1, 0, 5)); - HSSFCell cell = row.createCell(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(i).setCellStyle(_style_4); - if (i == 2 || i == 3 || i == 4) { - sheet.setColumnWidth(i, 3500); - } else if (i == 5) { - sheet.setColumnWidth(i, 10000); - } else { - sheet.setColumnWidth(i, 8000); - } - } - HSSFCell cell = row.getCell(0); - setCellValue(cell, "Data validation cells"); - cell = row.getCell(1); - setCellValue(cell, "Condition"); - cell = row.getCell(2); - setCellValue(cell, "Allow blank"); - cell = row.getCell(3); - setCellValue(cell, "Prompt box"); - cell = row.getCell(4); - setCellValue(cell, "Error box"); - cell = row.getCell(5); - setCellValue(cell, "Other settings"); - } - - public ValidationAdder createValidationAdder(HSSFCellStyle cellStyle, int dataValidationType) { - return new ValidationAdder(_currentSheet, _style_1, _style_2, cellStyle, dataValidationType); - } - - public void createDVDescriptionRow(String strTypeDescription) { - HSSFSheet sheet = _currentSheet; - HSSFRow row = sheet.getRow(sheet.getPhysicalNumberOfRows()-1); - sheet.addMergedRegion(new CellRangeAddress(sheet.getPhysicalNumberOfRows()-1, sheet.getPhysicalNumberOfRows()-1, 0, 5)); - HSSFCell cell = row.createCell(0); - setCellValue(cell, strTypeDescription); - cell.setCellStyle(_style_3); - row = sheet.createRow(sheet.getPhysicalNumberOfRows()); - } - } - - - private void addCustomValidations(WorkbookFormatter wf) { - wf.createSheet("Custom"); - wf.createHeaderRow(); - - 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); + public TestDataValidation(){ + super(HSSFITestDataProvider.instance); + } - 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.setRefersToFormula(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(0), cellStrValue); - } - // add list data on another sheet - for (int i = 0; i < 10; i++) { - HSSFRow currRow = dataSheet.createRow(i + 0); - setCellValue(currRow.createCell(0), "Data a" + i); - setCellValue(currRow.createCell(1), "Data b" + i); - setCellValue(currRow.createCell(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 !"); + public void assertDataValidation(Workbook wb) { ByteArrayOutputStream baos = new ByteArrayOutputStream(22000); try { @@ -572,9 +140,10 @@ public final class TestDataValidation extends TestCase { // and then deleting the row that contains the cell. HSSFWorkbook wb = HSSFTestDataSamples.openSampleWorkbook("dvEmpty.xls"); int dvRow = 0; - HSSFSheet sheet = wb.getSheetAt(0); - DVConstraint dc = DVConstraint.createNumericConstraint(VT.INTEGER, OP.EQUAL, "42", null); - HSSFDataValidation dv = new HSSFDataValidation(new CellRangeAddressList(dvRow, dvRow, 0, 0), dc); + Sheet sheet = wb.getSheetAt(0); + DataValidationHelper dataValidationHelper = sheet.getDataValidationHelper(); + DataValidationConstraint dc = dataValidationHelper.createIntegerConstraint(OP.EQUAL, "42", null); + DataValidation dv = dataValidationHelper.createValidation(dc,new CellRangeAddressList(dvRow, dvRow, 0, 0)); dv.setEmptyCellAllowed(false); dv.setErrorStyle(ES.STOP); diff --git a/src/testcases/org/apache/poi/hssf/usermodel/TestHSSFSheet.java b/src/testcases/org/apache/poi/hssf/usermodel/TestHSSFSheet.java index 09fdc73b8..8420c15a8 100644 --- a/src/testcases/org/apache/poi/hssf/usermodel/TestHSSFSheet.java +++ b/src/testcases/org/apache/poi/hssf/usermodel/TestHSSFSheet.java @@ -42,6 +42,9 @@ import org.apache.poi.hssf.record.WindowTwoRecord; import org.apache.poi.hssf.record.aggregates.WorksheetProtectionBlock; import org.apache.poi.hssf.usermodel.RecordInspector.RecordCollector; import org.apache.poi.ss.usermodel.BaseTestSheet; +import org.apache.poi.ss.usermodel.DataValidation; +import org.apache.poi.ss.usermodel.DataValidationConstraint; +import org.apache.poi.ss.usermodel.DataValidationHelper; import org.apache.poi.ss.util.CellRangeAddress; import org.apache.poi.ss.util.CellRangeAddressList; import org.apache.poi.util.TempFile; @@ -382,10 +385,10 @@ public final class TestHSSFSheet extends BaseTestSheet { HSSFSheet sheet = workbook.createSheet("Sheet1"); sheet.protectSheet("secret"); - DVConstraint dvc = DVConstraint.createNumericConstraint(DVConstraint.ValidationType.INTEGER, - DVConstraint.OperatorType.BETWEEN, "10", "100"); + DataValidationHelper dataValidationHelper = sheet.getDataValidationHelper(); + DataValidationConstraint dvc = dataValidationHelper.createIntegerConstraint(DataValidationConstraint.OperatorType.BETWEEN, "10", "100"); CellRangeAddressList numericCellAddressList = new CellRangeAddressList(0, 0, 1, 1); - HSSFDataValidation dv = new HSSFDataValidation(numericCellAddressList, dvc); + DataValidation dv = dataValidationHelper.createValidation(dvc,numericCellAddressList); try { sheet.addValidationData(dv); } catch (IllegalStateException e) { @@ -535,7 +538,7 @@ public final class TestHSSFSheet extends BaseTestSheet { int minWithRow1And2 = 6400; int maxWithRow1And2 = 7800; int minWithRow1Only = 2750; - int maxWithRow1Only = 3300; + int maxWithRow1Only = 3400; // autoSize the first column and check its size before the merged region (1,0,1,1) is set: // it has to be based on the 2nd row width diff --git a/src/testcases/org/apache/poi/ss/usermodel/BaseTestDataValidation.java b/src/testcases/org/apache/poi/ss/usermodel/BaseTestDataValidation.java new file mode 100644 index 000000000..40893c9dc --- /dev/null +++ b/src/testcases/org/apache/poi/ss/usermodel/BaseTestDataValidation.java @@ -0,0 +1,501 @@ +/* ==================================================================== + Licensed to the Apache Software Foundation (ASF) under one or more + contributor license agreements. See the NOTICE file distributed with + this work for additional information regarding copyright ownership. + The ASF licenses this file to You 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.ss.usermodel; + +import junit.framework.TestCase; + +import org.apache.poi.ss.ITestDataProvider; +import org.apache.poi.ss.util.CellRangeAddress; +import org.apache.poi.ss.util.CellRangeAddressList; +import org.apache.poi.util.POILogFactory; +import org.apache.poi.util.POILogger; + +/** + * Class for testing Excel's data validation mechanism + * + * @author Dragos Buleandra ( dragos.buleandra@trade2b.ro ) + */ +public abstract class BaseTestDataValidation extends TestCase { + private final ITestDataProvider _testDataProvider; + + private static final POILogger log = POILogFactory.getLogger(BaseTestDataValidation.class); + + protected BaseTestDataValidation(ITestDataProvider testDataProvider) { + _testDataProvider = testDataProvider; + } + + /** Convenient access to ERROR_STYLE constants */ + protected static final DataValidation.ErrorStyle ES = null; + /** Convenient access to OPERATOR constants */ + protected static final DataValidationConstraint.ValidationType VT = null; + /** Convenient access to OPERATOR constants */ + protected static final DataValidationConstraint.OperatorType OP = null; + + private static final class ValidationAdder { + + private final CellStyle _style_1; + private final CellStyle _style_2; + private final int _validationType; + private final Sheet _sheet; + private int _currentRowIndex; + private final CellStyle _cellStyle; + + public ValidationAdder(Sheet fSheet, CellStyle style_1, CellStyle style_2, + CellStyle 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 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++; + + DataValidationHelper dataValidationHelper = _sheet.getDataValidationHelper(); + DataValidationConstraint dc = createConstraint(dataValidationHelper,operatorType, firstFormula, secondFormula, explicitListValues); + + DataValidation dv = dataValidationHelper.createValidation(dc,new CellRangeAddressList(rowNum, rowNum, 0, 0)); + + 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) { + Row row = _sheet.getRow(_sheet.getPhysicalNumberOfRows() - 1); + Cell cell = row.createCell(0); + cell.setCellStyle(_cellStyle); + } + writeOtherSettings(_sheet, _style_1, promptDescr); + } + private DataValidationConstraint createConstraint(DataValidationHelper dataValidationHelper,int operatorType, String firstFormula, + String secondFormula, String[] explicitListValues) { + if (_validationType == VT.LIST) { + if (explicitListValues != null) { + return dataValidationHelper.createExplicitListConstraint(explicitListValues); + } + return dataValidationHelper.createFormulaListConstraint(firstFormula); + } + if (_validationType == VT.TIME) { + return dataValidationHelper.createTimeConstraint(operatorType, firstFormula, secondFormula); + } + if (_validationType == VT.DATE) { + return dataValidationHelper.createDateConstraint(operatorType, firstFormula, secondFormula, null); + } + if (_validationType == VT.FORMULA) { + return dataValidationHelper.createCustomConstraint(firstFormula); + } + + if( _validationType == VT.INTEGER) { + return dataValidationHelper.createIntegerConstraint(operatorType, firstFormula, secondFormula); + } + if( _validationType == VT.DECIMAL) { + return dataValidationHelper.createDecimalConstraint(operatorType, firstFormula, secondFormula); + } + if( _validationType == VT.TEXT_LENGTH) { + return dataValidationHelper.createTextLengthConstraint(operatorType, firstFormula, secondFormula); + } + return null; + } + /** + * writes plain text values into cells in a tabular format to form comments readable from within + * the spreadsheet. + */ + private static void writeDataValidationSettings(Sheet sheet, CellStyle style_1, + CellStyle style_2, String strCondition, boolean allowEmpty, boolean inputBox, + boolean errorBox) { + Row row = sheet.createRow(sheet.getPhysicalNumberOfRows()); + // condition's string + Cell cell = row.createCell(1); + cell.setCellStyle(style_1); + setCellValue(cell, strCondition); + // allow empty cells + cell = row.createCell(2); + cell.setCellStyle(style_2); + setCellValue(cell, ((allowEmpty) ? "yes" : "no")); + // show input box + cell = row.createCell(3); + cell.setCellStyle(style_2); + setCellValue(cell, ((inputBox) ? "yes" : "no")); + // show error box + cell = row.createCell(4); + cell.setCellStyle(style_2); + setCellValue(cell, ((errorBox) ? "yes" : "no")); + } + private static void writeOtherSettings(Sheet sheet, CellStyle style, + String strStettings) { + Row row = sheet.getRow(sheet.getPhysicalNumberOfRows() - 1); + Cell cell = row.createCell(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 static void log(String msg) { + log.log(POILogger.INFO, msg); + } + + /** + * Manages the cell styles used for formatting the output spreadsheet + */ + private static final class WorkbookFormatter { + + private final Workbook _wb; + private final CellStyle _style_1; + private final CellStyle _style_2; + private final CellStyle _style_3; + private final CellStyle _style_4; + private Sheet _currentSheet; + + public WorkbookFormatter(Workbook wb) { + _wb = wb; + _style_1 = createStyle( wb, CellStyle.ALIGN_LEFT ); + _style_2 = createStyle( wb, CellStyle.ALIGN_CENTER ); + _style_3 = createStyle( wb, CellStyle.ALIGN_CENTER, IndexedColors.GREY_25_PERCENT.getIndex(), true ); + _style_4 = createHeaderStyle(wb); + } + + private static CellStyle createStyle(Workbook wb, short h_align, short color, + boolean bold) { + Font font = wb.createFont(); + if (bold) { + font.setBoldweight(Font.BOLDWEIGHT_BOLD); + } + + CellStyle cellStyle = wb.createCellStyle(); + cellStyle.setFont(font); + cellStyle.setFillForegroundColor(color); + cellStyle.setFillPattern(CellStyle.SOLID_FOREGROUND); + cellStyle.setVerticalAlignment(CellStyle.VERTICAL_CENTER); + cellStyle.setAlignment(h_align); + cellStyle.setBorderLeft(CellStyle.BORDER_THIN); + cellStyle.setLeftBorderColor(IndexedColors.BLACK.getIndex()); + cellStyle.setBorderTop(CellStyle.BORDER_THIN); + cellStyle.setTopBorderColor(IndexedColors.BLACK.getIndex()); + cellStyle.setBorderRight(CellStyle.BORDER_THIN); + cellStyle.setRightBorderColor(IndexedColors.BLACK.getIndex()); + cellStyle.setBorderBottom(CellStyle.BORDER_THIN); + cellStyle.setBottomBorderColor(IndexedColors.BLACK.getIndex()); + + return cellStyle; + } + + private static CellStyle createStyle(Workbook wb, short h_align) { + return createStyle(wb, h_align, IndexedColors.WHITE.getIndex(), false); + } + private static CellStyle createHeaderStyle(Workbook wb) { + Font font = wb.createFont(); + font.setColor( IndexedColors.WHITE.getIndex() ); + font.setBoldweight(Font.BOLDWEIGHT_BOLD); + + CellStyle cellStyle = wb.createCellStyle(); + cellStyle.setFillForegroundColor(IndexedColors.BLUE_GREY.getIndex()); + cellStyle.setFillPattern(CellStyle.SOLID_FOREGROUND); + cellStyle.setAlignment(CellStyle.ALIGN_CENTER); + cellStyle.setVerticalAlignment(CellStyle.VERTICAL_CENTER); + cellStyle.setBorderLeft(CellStyle.BORDER_THIN); + cellStyle.setLeftBorderColor(IndexedColors.WHITE.getIndex()); + cellStyle.setBorderTop(CellStyle.BORDER_THIN); + cellStyle.setTopBorderColor(IndexedColors.WHITE.getIndex()); + cellStyle.setBorderRight(CellStyle.BORDER_THIN); + cellStyle.setRightBorderColor(IndexedColors.WHITE.getIndex()); + cellStyle.setBorderBottom(CellStyle.BORDER_THIN); + cellStyle.setBottomBorderColor(IndexedColors.WHITE.getIndex()); + cellStyle.setFont(font); + return cellStyle; + } + + + public Sheet createSheet(String sheetName) { + _currentSheet = _wb.createSheet(sheetName); + return _currentSheet; + } + public void createDVTypeRow(String strTypeDescription) { + Sheet sheet = _currentSheet; + Row row = sheet.createRow(sheet.getPhysicalNumberOfRows()); + sheet.addMergedRegion(new CellRangeAddress(sheet.getPhysicalNumberOfRows()-1, sheet.getPhysicalNumberOfRows()-1, 0, 5)); + Cell cell = row.createCell(0); + setCellValue(cell, strTypeDescription); + cell.setCellStyle(_style_3); + row = sheet.createRow(sheet.getPhysicalNumberOfRows()); + } + + public void createHeaderRow() { + Sheet sheet = _currentSheet; + Row row = sheet.createRow(sheet.getPhysicalNumberOfRows()); + row.setHeight((short) 400); + for (int i = 0; i < 6; i++) { + row.createCell(i).setCellStyle(_style_4); + if (i == 2 || i == 3 || i == 4) { + sheet.setColumnWidth(i, 3500); + } else if (i == 5) { + sheet.setColumnWidth(i, 10000); + } else { + sheet.setColumnWidth(i, 8000); + } + } + Cell cell = row.getCell(0); + setCellValue(cell, "Data validation cells"); + cell = row.getCell(1); + setCellValue(cell, "Condition"); + cell = row.getCell(2); + setCellValue(cell, "Allow blank"); + cell = row.getCell(3); + setCellValue(cell, "Prompt box"); + cell = row.getCell(4); + setCellValue(cell, "Error box"); + cell = row.getCell(5); + setCellValue(cell, "Other settings"); + } + + public ValidationAdder createValidationAdder(CellStyle cellStyle, int dataValidationType) { + return new ValidationAdder(_currentSheet, _style_1, _style_2, cellStyle, dataValidationType); + } + + public void createDVDescriptionRow(String strTypeDescription) { + Sheet sheet = _currentSheet; + Row row = sheet.getRow(sheet.getPhysicalNumberOfRows()-1); + sheet.addMergedRegion(new CellRangeAddress(sheet.getPhysicalNumberOfRows()-1, sheet.getPhysicalNumberOfRows()-1, 0, 5)); + Cell cell = row.createCell(0); + setCellValue(cell, strTypeDescription); + cell.setCellStyle(_style_3); + row = sheet.createRow(sheet.getPhysicalNumberOfRows()); + } + } + + + private void addCustomValidations(WorkbookFormatter wf) { + wf.createSheet("Custom"); + wf.createHeaderRow(); + + 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, Workbook 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 + Sheet fSheet = wf.createSheet("Lists"); + Sheet 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); + Name namedRange = wb.createName(); + namedRange.setNameName("myName"); + namedRange.setRefersToFormula(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++) { + Row currRow = fSheet.createRow(i + 29); + setCellValue(currRow.createCell(0), cellStrValue); + } + // add list data on another sheet + for (int i = 0; i < 10; i++) { + Row currRow = dataSheet.createRow(i + 0); + setCellValue(currRow.createCell(0), "Data a" + i); + setCellValue(currRow.createCell(1), "Data b" + i); + setCellValue(currRow.createCell(2), "Data c" + i); + } + } + + private static void addDateTimeValidations(WorkbookFormatter wf, Workbook wb) { + wf.createSheet("Dates and Times"); + + DataFormat dataFormat = wb.createDataFormat(); + short fmtDate = dataFormat.getFormat("m/d/yyyy"); + short fmtTime = dataFormat.getFormat("h:mm"); + CellStyle cellStyle_date = wb.createCellStyle(); + cellStyle_date.setDataFormat(fmtDate); + CellStyle 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"); + Workbook wb = _testDataProvider.createWorkbook(); + 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 !"); + + wb = _testDataProvider.writeOutAndReadBack(wb); + } + + + + /* package */ static void setCellValue(Cell cell, String text) { + cell.setCellValue(text); + + } + +} \ No newline at end of file diff --git a/test-data/spreadsheet/DataValidations-49244.xlsx b/test-data/spreadsheet/DataValidations-49244.xlsx new file mode 100644 index 0000000000000000000000000000000000000000..ee3f6ef9685ea9d99a1faea0a9282b2413e294e0 GIT binary patch literal 10705 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