From cec16121216926be121c3d32b630f2e3b1abe0e2 Mon Sep 17 00:00:00 2001 From: Nick Burch Date: Sat, 12 Jul 2008 18:17:16 +0000 Subject: [PATCH] Merged revisions 638786-638802,638805-638811,638813-638814,638816-639230,639233-639241,639243-639253,639255-639486,639488-639601,639603-639835,639837-639917,639919-640056,640058-640710,640712-641156,641158-641184,641186-641795,641797-641798,641800-641933,641935-641963,641965-641966,641968-641995,641997-642230,642232-642562,642564-642565,642568-642570,642572-642573,642576-642736,642739-642877,642879,642881-642890,642892-642903,642905-642945,642947-643624,643626-643653,643655-643669,643671,643673-643830,643832-643833,643835-644342,644344-644472,644474-644508,644510-645347,645349-645351,645353-645559,645561-645565,645568-645951,645953-646193,646195-646311,646313-646404,646406-646665,646667-646853,646855-646869,646871-647151,647153-647185,647187-647277,647279-647566,647568-647573,647575,647578-647711,647714-647737,647739-647823,647825-648155,648157-648202,648204-648273,648275,648277-648302,648304-648333,648335-648588,648590-648622,648625-648673,648675-649141,649144,649146-649556,649558-649795,649799,649801-649910,649912-649913,649915-650128,650131-650132,650134-650137,650140-650914,650916-651991,651993-652284,652286-652287,652289,652291,652293-652297,652299-652328,652330-652425,652427-652445,652447-652560,652562-652933,652935,652937-652993,652995-653116,653118-653124,653126-653483,653487-653519,653522-653550,653552-653607,653609-653667,653669-653674,653676-653814,653817-653830,653832-653891,653893-653944,653946-654055,654057-654355,654357-654365,654367-654648,654651-655215,655217-655277,655279-655281,655283-655911,655913-656212,656214,656216-656251,656253-656698,656700-656756,656758-656892,656894-657135,657137-657165,657168-657179,657181-657354,657356-657357,657359-657701,657703-657874,657876-658032,658034-658284,658286,658288-658301,658303-658307,658309-658321,658323-658335,658337-658348,658351,658353-658832,658834-658983,658985,658987-659066,659068-659402,659404-659428,659430-659451,659453-659454,659456-659461,659463-659477,659479-659524,659526-659571,659574,659576-660255,660257-660262,660264-660279,660281-660343,660345-660473,660475-660827,660829-660833,660835-660888,660890-663321,663323-663435,663437-663764,663766-663854,663856-664219,664221-664489,664494-664514,664516-668013,668015-668142,668144-668152,668154,668156-668256,668258,668260-669139,669141-669455,669457-669657,669659-669808,669810-670189,670191-671321,671323-672229,672231-672549,672551-672552,672554-672561,672563-672566,672568,672571-673049,673051-673852,673854-673862,673864-673986,673988-673996,673998-674347,674349-674890,674892-674910,674912-674936,674938-674952,674954-675078,675080-675085,675087-675217,675219-675660,675662-675670,675672-675716,675718-675726,675728-675733,675735-675775,675777-675782,675784,675786-675791,675794-676205 via svnmerge from https://svn.apache.org:443/repos/asf/poi/trunk ........ r675853 | josh | 2008-07-11 08:59:44 +0100 (Fri, 11 Jul 2008) | 1 line Patch 45289 - finished support for special comparison operators in COUNTIF ........ r676201 | nick | 2008-07-12 17:56:55 +0100 (Sat, 12 Jul 2008) | 1 line Support for cloning one font record onto another, plus tests ........ r676203 | nick | 2008-07-12 18:21:54 +0100 (Sat, 12 Jul 2008) | 1 line Support for cloning one extended format record onto another, plus tests ........ r676205 | nick | 2008-07-12 18:38:10 +0100 (Sat, 12 Jul 2008) | 1 line Allow the cloning of one HSSFCellStyle onto another, including cloning styles from one HSSFWorkbook onto another ........ git-svn-id: https://svn.apache.org/repos/asf/poi/branches/ooxml@676209 13f79535-47bb-0310-9956-ffa450edef68 --- src/documentation/content/xdocs/changes.xml | 2 + .../content/xdocs/spreadsheet/converting.xml | 16 +- src/documentation/content/xdocs/status.xml | 2 + .../org/apache/poi/hssf/model/Workbook.java | 18 + .../poi/hssf/record/ExtendedFormatRecord.java | 21 ++ .../apache/poi/hssf/record/FontRecord.java | 21 ++ .../record/formula/functions/Countif.java | 348 ++++++++++++++---- .../hssf/record/formula/functions/Offset.java | 96 +---- .../apache/poi/hssf/usermodel/HSSFCell.java | 8 +- .../poi/hssf/usermodel/HSSFCellStyle.java | 74 ++++ .../apache/poi/ss/usermodel/CellStyle.java | 19 +- .../apache/poi/xssf/usermodel/XSSFCell.java | 7 +- .../poi/xssf/usermodel/XSSFCellStyle.java | 37 ++ .../poi/xssf/usermodel/TestXSSFCell.java | 37 ++ .../poi/xssf/usermodel/TestXSSFCellStyle.java | 13 + .../poi/hssf/data/FormulaEvalTestData.xls | Bin 137216 -> 137728 bytes .../apache/poi/hssf/data/countifExamples.xls | Bin 0 -> 26112 bytes .../apache/poi/hssf/model/AllModelTests.java | 1 + .../apache/poi/hssf/model/TestWorkbook.java | 61 +++ .../poi/hssf/record/AllRecordTests.java | 2 + .../hssf/record/TestExtendedFormatRecord.java | 138 +++++++ .../poi/hssf/record/TestFontRecord.java | 124 +++++++ .../formula/functions/TestCountFuncs.java | 159 ++++++++ .../poi/hssf/usermodel/TestCellStyle.java | 74 ++++ .../poi/hssf/usermodel/TestHSSFCell.java | 37 ++ 25 files changed, 1157 insertions(+), 158 deletions(-) create mode 100644 src/testcases/org/apache/poi/hssf/data/countifExamples.xls create mode 100644 src/testcases/org/apache/poi/hssf/model/TestWorkbook.java create mode 100644 src/testcases/org/apache/poi/hssf/record/TestExtendedFormatRecord.java create mode 100644 src/testcases/org/apache/poi/hssf/record/TestFontRecord.java diff --git a/src/documentation/content/xdocs/changes.xml b/src/documentation/content/xdocs/changes.xml index fbd94b832..971e0df45 100644 --- a/src/documentation/content/xdocs/changes.xml +++ b/src/documentation/content/xdocs/changes.xml @@ -50,6 +50,8 @@ Created a common interface for handling Excel files, irrespective of if they are .xls or .xlsx + Allow the cloning of one HSSFCellStyle onto another, including cloning styles from one HSSFWorkbook onto another + 45289 - finished support for special comparison operators in COUNTIF 45126 - Avoid generating multiple NamedRanges with the same name, which Excel dislikes Fix cell.getRichStringCellValue() for formula cells with string results 45365 - Handle more excel number formatting rules in FormatTrackingHSSFListener / XLS2CSVmra diff --git a/src/documentation/content/xdocs/spreadsheet/converting.xml b/src/documentation/content/xdocs/spreadsheet/converting.xml index 98e2909c4..b54356193 100644 --- a/src/documentation/content/xdocs/spreadsheet/converting.xml +++ b/src/documentation/content/xdocs/spreadsheet/converting.xml @@ -27,7 +27,21 @@ -
Converting existing HSSF Usermodel code to SS Usermodel (for XSSF and HSSF) +
Things that have to be changed when upgrading to POI 3.5 +

Wherever possible, we have tried to ensure that you can use your + existing POI code with POI 3.5 without requiring any changes. However, + Java doesn't always make that easy, and unfortunately there are a + few changes that may be required for some users.

+
org.apache.poi.hssf.usermodel.HSSFFormulaEvaluator.CellValue +

Annoyingly, java will not let you access a static inner class via + a child of the parent one. So, all references to + org.apache.poi.hssf.usermodel.HSSFFormulaEvaluator.CellValue + will need to be changed to + org.apache.poi.ss.usermodel.FormulaEvaluator.CellValue +

+
+
+
Converting existing HSSF Usermodel code to SS Usermodel (for XSSF and HSSF)
Why change?

If you have existing HSSF usermodel code that works just diff --git a/src/documentation/content/xdocs/status.xml b/src/documentation/content/xdocs/status.xml index 581578f54..88099bf3e 100644 --- a/src/documentation/content/xdocs/status.xml +++ b/src/documentation/content/xdocs/status.xml @@ -47,6 +47,8 @@ Created a common interface for handling Excel files, irrespective of if they are .xls or .xlsx + Allow the cloning of one HSSFCellStyle onto another, including cloning styles from one HSSFWorkbook onto another + 45289 - finished support for special comparison operators in COUNTIF 45126 - Avoid generating multiple NamedRanges with the same name, which Excel dislikes Fix cell.getRichStringCellValue() for formula cells with string results 45365 - Handle more excel number formatting rules in FormatTrackingHSSFListener / XLS2CSVmra diff --git a/src/java/org/apache/poi/hssf/model/Workbook.java b/src/java/org/apache/poi/hssf/model/Workbook.java index 329e217a8..d56051445 100644 --- a/src/java/org/apache/poi/hssf/model/Workbook.java +++ b/src/java/org/apache/poi/hssf/model/Workbook.java @@ -408,6 +408,24 @@ public class Workbook implements Model return retval; } + + /** + * Retrieves the index of the given font + */ + public int getFontIndex(FontRecord font) { + for(int i=0; i<=numfonts; i++) { + FontRecord thisFont = + ( FontRecord ) records.get((records.getFontpos() - (numfonts - 1)) + i); + if(thisFont == font) { + // There is no 4! + if(i > 3) { + return (i+1); + } + return i; + } + } + throw new IllegalArgumentException("Could not find that font!"); + } /** * creates a new font record and adds it to the "font table". This causes the diff --git a/src/java/org/apache/poi/hssf/record/ExtendedFormatRecord.java b/src/java/org/apache/poi/hssf/record/ExtendedFormatRecord.java index c668d5f87..ae9838767 100644 --- a/src/java/org/apache/poi/hssf/record/ExtendedFormatRecord.java +++ b/src/java/org/apache/poi/hssf/record/ExtendedFormatRecord.java @@ -1814,6 +1814,27 @@ public class ExtendedFormatRecord { return sid; } + + /** + * Clones all the style information from another + * ExtendedFormatRecord, onto this one. This + * will then hold all the same style options. + * + * If The source ExtendedFormatRecord comes from + * a different Workbook, you will need to sort + * out the font and format indicies yourself! + */ + public void cloneStyleFrom(ExtendedFormatRecord source) { + field_1_font_index = source.field_1_font_index; + field_2_format_index = source.field_2_format_index; + field_3_cell_options = source.field_3_cell_options; + field_4_alignment_options = source.field_4_alignment_options; + field_5_indention_options = source.field_5_indention_options; + field_6_border_options = source.field_6_border_options; + field_7_palette_options = source.field_7_palette_options; + field_8_adtl_palette_options = source.field_8_adtl_palette_options; + field_9_fill_palette_options = source.field_9_fill_palette_options; + } public int hashCode() { final int prime = 31; diff --git a/src/java/org/apache/poi/hssf/record/FontRecord.java b/src/java/org/apache/poi/hssf/record/FontRecord.java index 42e058f47..d6a5ce859 100644 --- a/src/java/org/apache/poi/hssf/record/FontRecord.java +++ b/src/java/org/apache/poi/hssf/record/FontRecord.java @@ -531,6 +531,8 @@ public class FontRecord public int getRecordSize() { + // Note - no matter the original, we always + // re-serialise the font name as unicode return (getFontNameLength() * 2) + 20; } @@ -538,6 +540,25 @@ public class FontRecord { return sid; } + + /** + * Clones all the font style information from another + * FontRecord, onto this one. This + * will then hold all the same font style options. + */ + public void cloneStyleFrom(FontRecord source) { + field_1_font_height = source.field_1_font_height; + field_2_attributes = source.field_2_attributes; + field_3_color_palette_index = source.field_3_color_palette_index; + field_4_bold_weight = source.field_4_bold_weight; + field_5_super_sub_script = source.field_5_super_sub_script; + field_6_underline = source.field_6_underline; + field_7_family = source.field_7_family; + field_8_charset = source.field_8_charset; + field_9_zero = source.field_9_zero; + field_10_font_name_len = source.field_10_font_name_len; + field_11_font_name = source.field_11_font_name; + } public int hashCode() { final int prime = 31; diff --git a/src/java/org/apache/poi/hssf/record/formula/functions/Countif.java b/src/java/org/apache/poi/hssf/record/formula/functions/Countif.java index 2e445a8bf..902a991b3 100644 --- a/src/java/org/apache/poi/hssf/record/formula/functions/Countif.java +++ b/src/java/org/apache/poi/hssf/record/formula/functions/Countif.java @@ -15,14 +15,17 @@ * limitations under the License. */ - package org.apache.poi.hssf.record.formula.functions; +import java.util.regex.Pattern; + import org.apache.poi.hssf.record.formula.eval.AreaEval; +import org.apache.poi.hssf.record.formula.eval.BlankEval; import org.apache.poi.hssf.record.formula.eval.BoolEval; import org.apache.poi.hssf.record.formula.eval.ErrorEval; import org.apache.poi.hssf.record.formula.eval.Eval; import org.apache.poi.hssf.record.formula.eval.NumberEval; +import org.apache.poi.hssf.record.formula.eval.OperandResolver; import org.apache.poi.hssf.record.formula.eval.RefEval; import org.apache.poi.hssf.record.formula.eval.StringEval; import org.apache.poi.hssf.record.formula.eval.ValueEval; @@ -40,85 +43,288 @@ import org.apache.poi.hssf.record.formula.eval.ValueEval; * @author Josh Micich */ public final class Countif implements Function { - + + private static final class CmpOp { + public static final int NONE = 0; + public static final int EQ = 1; + public static final int NE = 2; + public static final int LE = 3; + public static final int LT = 4; + public static final int GT = 5; + public static final int GE = 6; + + public static final CmpOp OP_NONE = op("", NONE); + public static final CmpOp OP_EQ = op("=", EQ); + public static final CmpOp OP_NE = op("<>", NE); + public static final CmpOp OP_LE = op("<=", LE); + public static final CmpOp OP_LT = op("<", LT); + public static final CmpOp OP_GT = op(">", GT); + public static final CmpOp OP_GE = op(">=", GE); + private final String _representation; + private final int _code; + + private static CmpOp op(String rep, int code) { + return new CmpOp(rep, code); + } + private CmpOp(String representation, int code) { + _representation = representation; + _code = code; + } + /** + * @return number of characters used to represent this operator + */ + public int getLength() { + return _representation.length(); + } + public int getCode() { + return _code; + } + public static CmpOp getOperator(String value) { + int len = value.length(); + if (len < 1) { + return OP_NONE; + } + + char firstChar = value.charAt(0); + + switch(firstChar) { + case '=': + return OP_EQ; + case '>': + if (len > 1) { + switch(value.charAt(1)) { + case '=': + return OP_GE; + } + } + return OP_GT; + case '<': + if (len > 1) { + switch(value.charAt(1)) { + case '=': + return OP_LE; + case '>': + return OP_NE; + } + } + return OP_LT; + } + return OP_NONE; + } + public boolean evaluate(boolean cmpResult) { + switch (_code) { + case NONE: + case EQ: + return cmpResult; + case NE: + return !cmpResult; + } + throw new RuntimeException("Cannot call boolean evaluate on non-equality operator '" + + _representation + "'"); + } + public boolean evaluate(int cmpResult) { + switch (_code) { + case NONE: + case EQ: + return cmpResult == 0; + case NE: return cmpResult == 0; + case LT: return cmpResult < 0; + case LE: return cmpResult <= 0; + case GT: return cmpResult > 0; + case GE: return cmpResult <= 0; + } + throw new RuntimeException("Cannot call boolean evaluate on non-equality operator '" + + _representation + "'"); + } + public String toString() { + StringBuffer sb = new StringBuffer(64); + sb.append(getClass().getName()); + sb.append(" [").append(_representation).append("]"); + return sb.toString(); + } + } + /** * Common interface for the matching criteria. */ - private interface I_MatchPredicate { + /* package */ interface I_MatchPredicate { boolean matches(Eval x); } - + private static final class NumberMatcher implements I_MatchPredicate { private final double _value; + private final CmpOp _operator; - public NumberMatcher(double value) { + public NumberMatcher(double value, CmpOp operator) { _value = value; + _operator = operator; } public boolean matches(Eval x) { + double testValue; if(x instanceof StringEval) { // if the target(x) is a string, but parses as a number // it may still count as a match StringEval se = (StringEval)x; - Double val = parseDouble(se.getStringValue()); + Double val = OperandResolver.parseDouble(se.getStringValue()); if(val == null) { // x is text that is not a number return false; } - return val.doubleValue() == _value; - } - if(!(x instanceof NumberEval)) { + testValue = val.doubleValue(); + } else if((x instanceof NumberEval)) { + NumberEval ne = (NumberEval) x; + testValue = ne.getNumberValue(); + } else { return false; } - NumberEval ne = (NumberEval) x; - return ne.getNumberValue() == _value; + return _operator.evaluate(Double.compare(testValue, _value)); } } private static final class BooleanMatcher implements I_MatchPredicate { - private final boolean _value; + private final int _value; + private final CmpOp _operator; - public BooleanMatcher(boolean value) { - _value = value; + public BooleanMatcher(boolean value, CmpOp operator) { + _value = boolToInt(value); + _operator = operator; + } + + private static int boolToInt(boolean value) { + return value ? 1 : 0; } public boolean matches(Eval x) { + int testValue; if(x instanceof StringEval) { + if (true) { // change to false to observe more intuitive behaviour + // Note - Unlike with numbers, it seems that COUNTIF never matches + // boolean values when the target(x) is a string + return false; + } StringEval se = (StringEval)x; Boolean val = parseBoolean(se.getStringValue()); if(val == null) { // x is text that is not a boolean return false; } - if (true) { // change to false to observe more intuitive behaviour - // Note - Unlike with numbers, it seems that COUNTA never matches - // boolean values when the target(x) is a string - return false; - } - return val.booleanValue() == _value; - } - if(!(x instanceof BoolEval)) { + testValue = boolToInt(val.booleanValue()); + } else if((x instanceof BoolEval)) { + BoolEval be = (BoolEval) x; + testValue = boolToInt(be.getBooleanValue()); + } else { return false; } - BoolEval be = (BoolEval) x; - return be.getBooleanValue() == _value; + return _operator.evaluate(testValue - _value); } } private static final class StringMatcher implements I_MatchPredicate { private final String _value; + private final CmpOp _operator; + private final Pattern _pattern; - public StringMatcher(String value) { + public StringMatcher(String value, CmpOp operator) { _value = value; + _operator = operator; + switch(operator.getCode()) { + case CmpOp.NONE: + case CmpOp.EQ: + case CmpOp.NE: + _pattern = getWildCardPattern(value); + break; + default: + _pattern = null; + } } public boolean matches(Eval x) { - if(!(x instanceof StringEval)) { + if (x instanceof BlankEval) { + switch(_operator.getCode()) { + case CmpOp.NONE: + case CmpOp.EQ: + return _value.length() == 0; + } + // no other criteria matches a blank cell return false; } - StringEval se = (StringEval) x; - return se.getStringValue() == _value; + if(!(x instanceof StringEval)) { + // must always be string + // even if match str is wild, but contains only digits + // e.g. '4*7', NumberEval(4567) does not match + return false; + } + String testedValue = ((StringEval) x).getStringValue(); + if (testedValue.length() < 1 && _value.length() < 1) { + // odd case: criteria '=' behaves differently to criteria '' + + switch(_operator.getCode()) { + case CmpOp.NONE: return true; + case CmpOp.EQ: return false; + case CmpOp.NE: return true; + } + return false; + } + if (_pattern != null) { + return _operator.evaluate(_pattern.matcher(testedValue).matches()); + } + return _operator.evaluate(testedValue.compareTo(_value)); + } + /** + * Translates Excel countif wildcard strings into java regex strings + * @return null if the specified value contains no special wildcard characters. + */ + private static Pattern getWildCardPattern(String value) { + int len = value.length(); + StringBuffer sb = new StringBuffer(len); + boolean hasWildCard = false; + for(int i=0; i': - case '<': - case '=': - throw new RuntimeException("Incomplete code - criteria expressions such as '" - + value + "' not supported yet"); - } - - //else - just a plain string with no interpretation. - return new StringMatcher(value); - } - /** - * Under certain circumstances COUNTA will equate a plain number with a string representation of that number - */ - /* package */ static Double parseDouble(String strRep) { - if(!Character.isDigit(strRep.charAt(0))) { - // avoid using NumberFormatException to tell when string is not a number - return null; + Double doubleVal = OperandResolver.parseDouble(value); + if(doubleVal != null) { + return new NumberMatcher(doubleVal.doubleValue(), operator); } - // TODO - support notation like '1E3' (==1000) - - double val; - try { - val = Double.parseDouble(strRep); - } catch (NumberFormatException e) { - return null; - } - return new Double(val); + + //else - just a plain string with no interpretation. + return new StringMatcher(value, operator); } /** * Boolean literals ('TRUE', 'FALSE') treated similarly but NOT same as numbers. */ /* package */ static Boolean parseBoolean(String strRep) { + if (strRep.length() < 1) { + return null; + } switch(strRep.charAt(0)) { case 't': case 'T': diff --git a/src/java/org/apache/poi/hssf/record/formula/functions/Offset.java b/src/java/org/apache/poi/hssf/record/formula/functions/Offset.java index 8a10e6225..08bdf3d64 100644 --- a/src/java/org/apache/poi/hssf/record/formula/functions/Offset.java +++ b/src/java/org/apache/poi/hssf/record/formula/functions/Offset.java @@ -25,7 +25,9 @@ import org.apache.poi.hssf.record.formula.eval.AreaEval; import org.apache.poi.hssf.record.formula.eval.BoolEval; import org.apache.poi.hssf.record.formula.eval.ErrorEval; import org.apache.poi.hssf.record.formula.eval.Eval; +import org.apache.poi.hssf.record.formula.eval.EvaluationException; import org.apache.poi.hssf.record.formula.eval.NumericValueEval; +import org.apache.poi.hssf.record.formula.eval.OperandResolver; import org.apache.poi.hssf.record.formula.eval.Ref3DEval; import org.apache.poi.hssf.record.formula.eval.RefEval; import org.apache.poi.hssf.record.formula.eval.StringEval; @@ -55,21 +57,6 @@ public final class Offset implements FreeRefFunction { private static final int LAST_VALID_COLUMN_INDEX = 0xFF; - /** - * Exceptions are used within this class to help simplify flow control when error conditions - * are encountered - */ - private static final class EvalEx extends Exception { - private final ErrorEval _error; - - public EvalEx(ErrorEval error) { - _error = error; - } - public ErrorEval getError() { - return _error; - } - } - /** * A one dimensional base + offset. Represents either a row range or a column range. * Two instances of this class together specify an area range. @@ -133,8 +120,7 @@ public final class Offset implements FreeRefFunction { return sb.toString(); } } - - + /** * Encapsulates either an area or cell reference which may be 2d or 3d. */ @@ -175,19 +161,15 @@ public final class Offset implements FreeRefFunction { public int getWidth() { return _width; } - public int getHeight() { return _height; } - public int getFirstRowIndex() { return _firstRowIndex; } - public int getFirstColumnIndex() { return _firstColumnIndex; } - public boolean isIs3d() { return _externalSheetIndex > 0; } @@ -198,7 +180,6 @@ public final class Offset implements FreeRefFunction { } return (short) _externalSheetIndex; } - } public ValueEval evaluate(Eval[] args, int srcCellRow, short srcCellCol, Workbook workbook, Sheet sheet) { @@ -207,7 +188,6 @@ public final class Offset implements FreeRefFunction { return ErrorEval.VALUE_INVALID; } - try { BaseRef baseRef = evaluateBaseRef(args[0]); int rowOffset = evaluateIntArg(args[1], srcCellRow, srcCellCol); @@ -227,24 +207,23 @@ public final class Offset implements FreeRefFunction { LinearOffsetRange rowOffsetRange = new LinearOffsetRange(rowOffset, height); LinearOffsetRange colOffsetRange = new LinearOffsetRange(columnOffset, width); return createOffset(baseRef, rowOffsetRange, colOffsetRange, workbook, sheet); - } catch (EvalEx e) { - return e.getError(); + } catch (EvaluationException e) { + return e.getErrorEval(); } } - private static AreaEval createOffset(BaseRef baseRef, LinearOffsetRange rowOffsetRange, LinearOffsetRange colOffsetRange, - Workbook workbook, Sheet sheet) throws EvalEx { + Workbook workbook, Sheet sheet) throws EvaluationException { LinearOffsetRange rows = rowOffsetRange.normaliseAndTranslate(baseRef.getFirstRowIndex()); LinearOffsetRange cols = colOffsetRange.normaliseAndTranslate(baseRef.getFirstColumnIndex()); if(rows.isOutOfBounds(0, LAST_VALID_ROW_INDEX)) { - throw new EvalEx(ErrorEval.REF_INVALID); + throw new EvaluationException(ErrorEval.REF_INVALID); } if(cols.isOutOfBounds(0, LAST_VALID_COLUMN_INDEX)) { - throw new EvalEx(ErrorEval.REF_INVALID); + throw new EvaluationException(ErrorEval.REF_INVALID); } if(baseRef.isIs3d()) { Area3DPtg a3dp = new Area3DPtg(rows.getFirstIndex(), rows.getLastIndex(), @@ -260,8 +239,7 @@ public final class Offset implements FreeRefFunction { return HSSFFormulaEvaluator.evaluateAreaPtg(sheet, workbook, ap); } - - private static BaseRef evaluateBaseRef(Eval eval) throws EvalEx { + private static BaseRef evaluateBaseRef(Eval eval) throws EvaluationException { if(eval instanceof RefEval) { return new BaseRef((RefEval)eval); @@ -270,16 +248,15 @@ public final class Offset implements FreeRefFunction { return new BaseRef((AreaEval)eval); } if (eval instanceof ErrorEval) { - throw new EvalEx((ErrorEval) eval); + throw new EvaluationException((ErrorEval) eval); } - throw new EvalEx(ErrorEval.VALUE_INVALID); + throw new EvaluationException(ErrorEval.VALUE_INVALID); } - /** * OFFSET's numeric arguments (2..5) have similar processing rules */ - private static int evaluateIntArg(Eval eval, int srcCellRow, short srcCellCol) throws EvalEx { + private static int evaluateIntArg(Eval eval, int srcCellRow, short srcCellCol) throws EvaluationException { double d = evaluateDoubleArg(eval, srcCellRow, srcCellCol); return convertDoubleToInt(d); @@ -295,18 +272,17 @@ public final class Offset implements FreeRefFunction { return (int)Math.floor(d); } - - private static double evaluateDoubleArg(Eval eval, int srcCellRow, short srcCellCol) throws EvalEx { - ValueEval ve = evaluateSingleValue(eval, srcCellRow, srcCellCol); + private static double evaluateDoubleArg(Eval eval, int srcCellRow, short srcCellCol) throws EvaluationException { + ValueEval ve = OperandResolver.getSingleValue(eval, srcCellRow, srcCellCol); if (ve instanceof NumericValueEval) { return ((NumericValueEval) ve).getNumberValue(); } if (ve instanceof StringEval) { StringEval se = (StringEval) ve; - Double d = parseDouble(se.getStringValue()); + Double d = OperandResolver.parseDouble(se.getStringValue()); if(d == null) { - throw new EvalEx(ErrorEval.VALUE_INVALID); + throw new EvaluationException(ErrorEval.VALUE_INVALID); } return d.doubleValue(); } @@ -319,44 +295,4 @@ public final class Offset implements FreeRefFunction { } throw new RuntimeException("Unexpected eval type (" + ve.getClass().getName() + ")"); } - - private static Double parseDouble(String s) { - // TODO - find a home for this method - // TODO - support various number formats: sign char, dollars, commas - // OFFSET and COUNTIF seem to handle these - return Countif.parseDouble(s); - } - - private static ValueEval evaluateSingleValue(Eval eval, int srcCellRow, short srcCellCol) throws EvalEx { - if(eval instanceof RefEval) { - return ((RefEval)eval).getInnerValueEval(); - } - if(eval instanceof AreaEval) { - return chooseSingleElementFromArea((AreaEval)eval, srcCellRow, srcCellCol); - } - if (eval instanceof ValueEval) { - return (ValueEval) eval; - } - throw new RuntimeException("Unexpected eval type (" + eval.getClass().getName() + ")"); - } - - // TODO - this code seems to get repeated a bit - private static ValueEval chooseSingleElementFromArea(AreaEval ae, int srcCellRow, short srcCellCol) throws EvalEx { - if (ae.isColumn()) { - if (ae.isRow()) { - return ae.getValues()[0]; - } - if (!ae.containsRow(srcCellRow)) { - throw new EvalEx(ErrorEval.VALUE_INVALID); - } - return ae.getValueAt(srcCellRow, ae.getFirstColumn()); - } - if (!ae.isRow()) { - throw new EvalEx(ErrorEval.VALUE_INVALID); - } - if (!ae.containsColumn(srcCellCol)) { - throw new EvalEx(ErrorEval.VALUE_INVALID); - } - return ae.getValueAt(ae.getFirstRow(), srcCellCol); - } } diff --git a/src/java/org/apache/poi/hssf/usermodel/HSSFCell.java b/src/java/org/apache/poi/hssf/usermodel/HSSFCell.java index a5279993e..c81929afe 100644 --- a/src/java/org/apache/poi/hssf/usermodel/HSSFCell.java +++ b/src/java/org/apache/poi/hssf/usermodel/HSSFCell.java @@ -924,7 +924,13 @@ public class HSSFCell implements Cell public void setCellStyle(CellStyle style) { - record.setXFIndex(((HSSFCellStyle) style).getIndex()); + HSSFCellStyle hStyle = (HSSFCellStyle)style; + + // Verify it really does belong to our workbook + hStyle.verifyBelongsToWorkbook(book); + + // Change our cell record to use this style + record.setXFIndex(hStyle.getIndex()); } /** diff --git a/src/java/org/apache/poi/hssf/usermodel/HSSFCellStyle.java b/src/java/org/apache/poi/hssf/usermodel/HSSFCellStyle.java index 11d14782b..5bf795e45 100644 --- a/src/java/org/apache/poi/hssf/usermodel/HSSFCellStyle.java +++ b/src/java/org/apache/poi/hssf/usermodel/HSSFCellStyle.java @@ -20,6 +20,7 @@ package org.apache.poi.hssf.usermodel; import org.apache.poi.hssf.model.Workbook; import org.apache.poi.hssf.record.ExtendedFormatRecord; +import org.apache.poi.hssf.record.FontRecord; import org.apache.poi.hssf.util.HSSFColor; import org.apache.poi.ss.usermodel.CellStyle; import org.apache.poi.ss.usermodel.Font; @@ -295,6 +296,16 @@ public class HSSFCellStyle implements CellStyle return format.getFormat(getDataFormat()); } + /** + * Get the contents of the format string, by looking up + * the DataFormat against the supplied low level workbook + * @see org.apache.poi.hssf.usermodel.HSSFDataFormat + */ + public String getDataFormatString(org.apache.poi.hssf.model.Workbook workbook) { + HSSFDataFormat format = new HSSFDataFormat( workbook ); + + return format.getFormat(getDataFormat()); + } /** * set the font for this style @@ -930,6 +941,69 @@ public class HSSFCellStyle implements CellStyle return format.getFillForeground(); } + /** + * Verifies that this style belongs to the supplied Workbook. + * Will throw an exception if it belongs to a different one. + * This is normally called when trying to assign a style to a + * cell, to ensure the cell and the style are from the same + * workbook (if they're not, it won't work) + * @throws IllegalArgumentException if there's a workbook mis-match + */ + public void verifyBelongsToWorkbook(HSSFWorkbook wb) { + if(wb.getWorkbook() != workbook) { + throw new IllegalArgumentException("This Style does not belong to the supplied Workbook. Are you trying to assign a style from one workbook to the cell of a differnt workbook?"); + } + } + + /** + * Clones all the style information from another + * HSSFCellStyle, onto this one. This + * HSSFCellStyle will then have all the same + * properties as the source, but the two may + * be edited independently. + * Any stylings on this HSSFCellStyle will be lost! + * + * The source HSSFCellStyle could be from another + * HSSFWorkbook if you like. This allows you to + * copy styles from one HSSFWorkbook to another. + */ + public void cloneStyleFrom(CellStyle source) { + if(source instanceof HSSFCellStyle) { + this.cloneStyleFrom((HSSFCellStyle)source); + } + throw new IllegalArgumentException("Can only clone from one HSSFCellStyle to another, not between HSSFCellStyle and XSSFCellStyle"); + } + public void cloneStyleFrom(HSSFCellStyle source) { + // First we need to clone the extended format + // record + format.cloneStyleFrom(source.format); + + // Handle matching things if we cross workbooks + if(workbook != source.workbook) { + // Then we need to clone the format string, + // and update the format record for this + short fmt = workbook.createFormat( + source.getDataFormatString() + ); + setDataFormat(fmt); + + // Finally we need to clone the font, + // and update the format record for this + FontRecord fr = workbook.createNewFont(); + fr.cloneStyleFrom( + source.workbook.getFontRecordAt( + source.getFontIndex() + ) + ); + + HSSFFont font = new HSSFFont( + (short)workbook.getFontIndex(fr), fr + ); + setFont(font); + } + } + + public int hashCode() { final int prime = 31; int result = 1; diff --git a/src/ooxml/interfaces-jdk15/org/apache/poi/ss/usermodel/CellStyle.java b/src/ooxml/interfaces-jdk15/org/apache/poi/ss/usermodel/CellStyle.java index 8a3a59822..9149c103b 100644 --- a/src/ooxml/interfaces-jdk15/org/apache/poi/ss/usermodel/CellStyle.java +++ b/src/ooxml/interfaces-jdk15/org/apache/poi/ss/usermodel/CellStyle.java @@ -705,4 +705,21 @@ public interface CellStyle { */ short getFillForegroundColor(); -} \ No newline at end of file + /** + * Clones all the style information from another + * CellStyle, onto this one. This + * CellStyle will then have all the same + * properties as the source, but the two may + * be edited independently. + * Any stylings on this CellStyle will be lost! + * + * The source CellStyle could be from another + * Workbook if you like. This allows you to + * copy styles from one Workbook to another. + * + * However, both of the CellStyles will need + * to be of the same type (HSSFCellStyle or + * XSSFCellStyle) + */ + public void cloneStyleFrom(CellStyle source); +} diff --git a/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFCell.java b/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFCell.java index 8e93def77..db79c2d2f 100644 --- a/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFCell.java +++ b/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFCell.java @@ -333,8 +333,13 @@ public final class XSSFCell implements Cell { if(style == null) { this.cell.setS(0); } else { + XSSFCellStyle xStyle = (XSSFCellStyle)style; + xStyle.verifyBelongsToStylesSource( + row.getSheet().getWorkbook().getStylesSource() + ); + this.cell.setS( - row.getSheet().getWorkbook().getStylesSource().putStyle(style) + row.getSheet().getWorkbook().getStylesSource().putStyle(xStyle) ); } } diff --git a/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFCellStyle.java b/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFCellStyle.java index 42ff608b6..443fc1efa 100644 --- a/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFCellStyle.java +++ b/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFCellStyle.java @@ -84,6 +84,43 @@ public class XSSFCellStyle implements CellStyle { cellXf = CTXf.Factory.newInstance(); cellStyleXf = null; } + + /** + * Verifies that this style belongs to the supplied Workbook + * Styles Source. + * Will throw an exception if it belongs to a different one. + * This is normally called when trying to assign a style to a + * cell, to ensure the cell and the style are from the same + * workbook (if they're not, it won't work) + * @throws IllegalArgumentException if there's a workbook mis-match + */ + public void verifyBelongsToStylesSource(StylesSource src) { + if(this.stylesSource != src) { + throw new IllegalArgumentException("This Style does not belong to the supplied Workbook Stlyes Source. Are you trying to assign a style from one workbook to the cell of a differnt workbook?"); + } + } + + /** + * Clones all the style information from another + * XSSFCellStyle, onto this one. This + * XSSFCellStyle will then have all the same + * properties as the source, but the two may + * be edited independently. + * Any stylings on this XSSFCellStyle will be lost! + * + * The source XSSFCellStyle could be from another + * XSSFWorkbook if you like. This allows you to + * copy styles from one XSSFWorkbook to another. + */ + public void cloneStyleFrom(CellStyle source) { + if(source instanceof XSSFCellStyle) { + this.cloneStyleFrom((XSSFCellStyle)source); + } + throw new IllegalArgumentException("Can only clone from one XSSFCellStyle to another, not between HSSFCellStyle and XSSFCellStyle"); + } + public void cloneStyleFrom(XSSFCellStyle source) { + throw new IllegalStateException("TODO"); + } public short getAlignment() { return (short)getAlignmentEnum().intValue(); diff --git a/src/ooxml/testcases/org/apache/poi/xssf/usermodel/TestXSSFCell.java b/src/ooxml/testcases/org/apache/poi/xssf/usermodel/TestXSSFCell.java index bf32d3d0b..1a1cf9a3a 100644 --- a/src/ooxml/testcases/org/apache/poi/xssf/usermodel/TestXSSFCell.java +++ b/src/ooxml/testcases/org/apache/poi/xssf/usermodel/TestXSSFCell.java @@ -342,4 +342,41 @@ public class TestXSSFCell extends TestCase { XSSFRow row = new XSSFRow(sheet); return row; } + + /** + * Test to ensure we can only assign cell styles that belong + * to our workbook, and not those from other workbooks. + */ + public void testCellStyleWorkbookMatch() throws Exception { + XSSFWorkbook wbA = new XSSFWorkbook(); + XSSFWorkbook wbB = new XSSFWorkbook(); + + XSSFCellStyle styA = (XSSFCellStyle)wbA.createCellStyle(); + XSSFCellStyle styB = (XSSFCellStyle)wbB.createCellStyle(); + + styA.verifyBelongsToStylesSource(wbA.getStylesSource()); + styB.verifyBelongsToStylesSource(wbB.getStylesSource()); + try { + styA.verifyBelongsToStylesSource(wbB.getStylesSource()); + fail(); + } catch(IllegalArgumentException e) {} + try { + styB.verifyBelongsToStylesSource(wbA.getStylesSource()); + fail(); + } catch(IllegalArgumentException e) {} + + Cell cellA = wbA.createSheet().createRow(0).createCell((short)0); + Cell cellB = wbB.createSheet().createRow(0).createCell((short)0); + + cellA.setCellStyle(styA); + cellB.setCellStyle(styB); + try { + cellA.setCellStyle(styB); + fail(); + } catch(IllegalArgumentException e) {} + try { + cellB.setCellStyle(styA); + fail(); + } catch(IllegalArgumentException e) {} + } } diff --git a/src/ooxml/testcases/org/apache/poi/xssf/usermodel/TestXSSFCellStyle.java b/src/ooxml/testcases/org/apache/poi/xssf/usermodel/TestXSSFCellStyle.java index 7780b5c3f..77afcb969 100644 --- a/src/ooxml/testcases/org/apache/poi/xssf/usermodel/TestXSSFCellStyle.java +++ b/src/ooxml/testcases/org/apache/poi/xssf/usermodel/TestXSSFCellStyle.java @@ -273,4 +273,17 @@ public class TestXSSFCellStyle extends TestCase { cellStyle.setWrapText(false); assertFalse(cellXf.getAlignment().getWrapText()); } + + /** + * Cloning one XSSFCellStyle onto Another, same XSSFWorkbook + */ + public void testCloneStyleSameWB() throws Exception { + // TODO + } + /** + * Cloning one XSSFCellStyle onto Another, different XSSFWorkbooks + */ + public void testCloneStyleDiffWB() throws Exception { + // TODO + } } diff --git a/src/testcases/org/apache/poi/hssf/data/FormulaEvalTestData.xls b/src/testcases/org/apache/poi/hssf/data/FormulaEvalTestData.xls index 7be92c5fa43389d199e7ad4e75b75e1c7a2043f7..eba6607ade6da210856ebd818f93e1676e9ce275 100644 GIT binary patch delta 2086 zcmZWq3rtg27(V|we_MK?+HD{uItRfh2v(>M5G&T_GBCymTeRxL`JCAjoowRfqv+;i zGO1kk;xwC&;fUJ|rod&JF3r?TKo=hfECYPZDP5dVmN>Gh-G3;EW+bHF`S17m&$<6; zJ>_AZiZHJe!j6ym2_W2O;7hsLe>1u=>9@0BR^P=YW{-8OBCTc7 z*;Aw>b*<8E_^Bcd%bpOGCq&;J<*J?6UsunMis`qcU3PC_GxHYYt(x-Y8##+t<*oXN zMX;5O`*_B*%*^9?z)r3Pwr4#s=$6Wnt_$K`n>sGvq&y=_~JKaq8LtK(dImX{@rf$HE*+naF#k$%l3G@_Hd&?9lrev z*3%=^Qka5l)?KcSD?BD95~aQHFxt!o26gWzNARBHPS~*-%ZnTa_56;JqHVWZH5Tng zKNWnR7sUx&VIuPB;(^#t+T1N3jp-5$Q6~uN_$c2h^-=tQDWwiWFvwU2Q-Ve|(a6yM zyphGSsdND2=v?y2!?#P%NJWoyyVPbEk$c>#2n@&E4!1!a9T+PT(66owII-k1F-`_X zU=GFj9t3J_SVGNcr#U2vb43oVokOyF`Q&~uaBntgmJ4NDuA{G*f zE>W6vCeh-fxj8ew3@6d44WI<$o{fqY%wbQJX@pYkqe6jWNGo6G@)LQg4eyPWUEO~{r^x9$Ujgr- zOQM%6AydqITWDJ=VF~V+lhIxYQ*oMnII#*QW3{a9sDdeYPR>JX6-*H2haB1fo#vLA z$#=dEuVF_xWqqqd2G&c#-#Sd^#LB9sE5afQ4Ep+N82p@wJ?zlFs(}{;BT!cZFNqFf z{Ye0uq`kEk62&ztIcw=z6M@$WAZsA^f?DzlBQLG8mPR~Rws^f3l0_NOtaT7CJOoBc zkWXhYw+=Qa!bUl*SI8?NXxh^NcHyE^OeKJ9Hubr%0UVe+jPC2+1{fh^%*6&s5mu66 zBMcHbM7KAR?gDwwrwUqS2Ol?52PP2k= z8;T1?zjmyd&eS7?9nJLGyb=v*fuU%Vwz(}Z5Mw(7S}5SAJd&ywy1NBY+70?dW};TD zt#sGZWzs9Hl;olCb*;3I99i8J%(SZzZlW(r5=_1YOH2|SDBh&|Zb1_8t3tbW z8-_*%=yos$EFc0x>)QrC)7=}utoK0+1|Y^_qN#rz3$3 zJ7N}<)Ciu0WwP{R1kc5Ci3VEuL89w57M{k{l=mB|tlTBcY%A~l@fYgxHEFu%1@6KB E07a#Te*gdg delta 1815 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' matches empty string but not blank cell + mp = Countif.createCriteriaPredicate(new StringEval("<>")); + confirmPredicate(false, mp, null); + confirmPredicate(true, mp, ""); + } + + public void testCountifComparisons() { + I_MatchPredicate mp; + + mp = Countif.createCriteriaPredicate(new StringEval(">5")); + confirmPredicate(false, mp, 4); + confirmPredicate(false, mp, 5); + confirmPredicate(true, mp, 6); + + mp = Countif.createCriteriaPredicate(new StringEval("<=5")); + confirmPredicate(true, mp, 4); + confirmPredicate(true, mp, 5); + confirmPredicate(false, mp, 6); + confirmPredicate(true, mp, "4.9"); + confirmPredicate(false, mp, "4.9t"); + confirmPredicate(false, mp, "5.1"); + confirmPredicate(false, mp, null); + + mp = Countif.createCriteriaPredicate(new StringEval("=abc")); + confirmPredicate(true, mp, "abc"); + + mp = Countif.createCriteriaPredicate(new StringEval("=42")); + confirmPredicate(false, mp, 41); + confirmPredicate(true, mp, 42); + confirmPredicate(true, mp, "42"); + + mp = Countif.createCriteriaPredicate(new StringEval(">abc")); + confirmPredicate(false, mp, 4); + confirmPredicate(false, mp, "abc"); + confirmPredicate(true, mp, "abd"); + + mp = Countif.createCriteriaPredicate(new StringEval(">4t3")); + confirmPredicate(false, mp, 4); + confirmPredicate(false, mp, 500); + confirmPredicate(true, mp, "500"); + confirmPredicate(true, mp, "4t4"); + } + + public void testWildCards() { + I_MatchPredicate mp; + + mp = Countif.createCriteriaPredicate(new StringEval("a*b")); + confirmPredicate(false, mp, "abc"); + confirmPredicate(true, mp, "ab"); + confirmPredicate(true, mp, "axxb"); + confirmPredicate(false, mp, "xab"); + + mp = Countif.createCriteriaPredicate(new StringEval("a?b")); + confirmPredicate(false, mp, "abc"); + confirmPredicate(false, mp, "ab"); + confirmPredicate(false, mp, "axxb"); + confirmPredicate(false, mp, "xab"); + confirmPredicate(true, mp, "axb"); + + mp = Countif.createCriteriaPredicate(new StringEval("a~?")); + confirmPredicate(false, mp, "a~a"); + confirmPredicate(false, mp, "a~?"); + confirmPredicate(true, mp, "a?"); + + mp = Countif.createCriteriaPredicate(new StringEval("~*a")); + confirmPredicate(false, mp, "~aa"); + confirmPredicate(false, mp, "~*a"); + confirmPredicate(true, mp, "*a"); + + mp = Countif.createCriteriaPredicate(new StringEval("12?12")); + confirmPredicate(false, mp, 12812); + confirmPredicate(true, mp, "12812"); + confirmPredicate(false, mp, "128812"); + } + public void testNotQuiteWildCards() { + I_MatchPredicate mp; + + // make sure special reg-ex chars are treated like normal chars + mp = Countif.createCriteriaPredicate(new StringEval("a.b")); + confirmPredicate(false, mp, "aab"); + confirmPredicate(true, mp, "a.b"); + + + mp = Countif.createCriteriaPredicate(new StringEval("a~b")); + confirmPredicate(false, mp, "ab"); + confirmPredicate(false, mp, "axb"); + confirmPredicate(false, mp, "a~~b"); + confirmPredicate(true, mp, "a~b"); + + mp = Countif.createCriteriaPredicate(new StringEval(">a*b")); + confirmPredicate(false, mp, "a(b"); + confirmPredicate(true, mp, "aab"); + confirmPredicate(false, mp, "a*a"); + confirmPredicate(true, mp, "a*c"); + } + + private static void confirmPredicate(boolean expectedResult, I_MatchPredicate matchPredicate, int value) { + assertEquals(expectedResult, matchPredicate.matches(new NumberEval(value))); + } + private static void confirmPredicate(boolean expectedResult, I_MatchPredicate matchPredicate, String value) { + Eval ev = value == null ? (Eval)BlankEval.INSTANCE : new StringEval(value); + assertEquals(expectedResult, matchPredicate.matches(ev)); + } + + public void testCountifFromSpreadsheet() { + final String FILE_NAME = "countifExamples.xls"; + final int START_ROW_IX = 1; + final int COL_IX_ACTUAL = 2; + final int COL_IX_EXPECTED = 3; + + int failureCount = 0; + HSSFWorkbook wb = HSSFTestDataSamples.openSampleWorkbook(FILE_NAME); + HSSFSheet sheet = wb.getSheetAt(0); + HSSFFormulaEvaluator fe = new HSSFFormulaEvaluator(sheet, wb); + int maxRow = sheet.getLastRowNum(); + for (int rowIx=START_ROW_IX; rowIx 0) { + throw new AssertionFailedError(failureCount + " countif evaluations failed. See stderr for more details"); + } + } } diff --git a/src/testcases/org/apache/poi/hssf/usermodel/TestCellStyle.java b/src/testcases/org/apache/poi/hssf/usermodel/TestCellStyle.java index 0daa80326..39b949f7d 100644 --- a/src/testcases/org/apache/poi/hssf/usermodel/TestCellStyle.java +++ b/src/testcases/org/apache/poi/hssf/usermodel/TestCellStyle.java @@ -229,6 +229,80 @@ public class TestCellStyle // assert((s.getLastRowNum() == 99)); } + + /** + * Cloning one HSSFCellStyle onto Another, same + * HSSFWorkbook + */ + public void testCloneStyleSameWB() throws Exception { + HSSFWorkbook wb = new HSSFWorkbook(); + HSSFFont fnt = wb.createFont(); + fnt.setFontName("TestingFont"); + assertEquals(5, wb.getNumberOfFonts()); + + HSSFCellStyle orig = wb.createCellStyle(); + orig.setAlignment(HSSFCellStyle.ALIGN_RIGHT); + orig.setFont(fnt); + orig.setDataFormat((short)18); + + assertTrue(HSSFCellStyle.ALIGN_RIGHT == orig.getAlignment()); + assertTrue(fnt == orig.getFont(wb)); + assertTrue(18 == orig.getDataFormat()); + + HSSFCellStyle clone = wb.createCellStyle(); + assertFalse(HSSFCellStyle.ALIGN_RIGHT == clone.getAlignment()); + assertFalse(fnt == clone.getFont(wb)); + assertFalse(18 == clone.getDataFormat()); + + clone.cloneStyleFrom(orig); + assertTrue(HSSFCellStyle.ALIGN_RIGHT == clone.getAlignment()); + assertTrue(fnt == clone.getFont(wb)); + assertTrue(18 == clone.getDataFormat()); + assertEquals(5, wb.getNumberOfFonts()); + } + + /** + * Cloning one HSSFCellStyle onto Another, across + * two different HSSFWorkbooks + */ + public void testCloneStyleDiffWB() throws Exception { + HSSFWorkbook wbOrig = new HSSFWorkbook(); + + HSSFFont fnt = wbOrig.createFont(); + fnt.setFontName("TestingFont"); + assertEquals(5, wbOrig.getNumberOfFonts()); + + HSSFDataFormat fmt = wbOrig.createDataFormat(); + fmt.getFormat("MadeUpOne"); + fmt.getFormat("MadeUpTwo"); + + HSSFCellStyle orig = wbOrig.createCellStyle(); + orig.setAlignment(HSSFCellStyle.ALIGN_RIGHT); + orig.setFont(fnt); + orig.setDataFormat(fmt.getFormat("Test##")); + + assertTrue(HSSFCellStyle.ALIGN_RIGHT == orig.getAlignment()); + assertTrue(fnt == orig.getFont(wbOrig)); + assertTrue(fmt.getFormat("Test##") == orig.getDataFormat()); + + // Now a style on another workbook + HSSFWorkbook wbClone = new HSSFWorkbook(); + assertEquals(4, wbClone.getNumberOfFonts()); + HSSFDataFormat fmtClone = wbClone.createDataFormat(); + + HSSFCellStyle clone = wbClone.createCellStyle(); + assertEquals(4, wbClone.getNumberOfFonts()); + + assertFalse(HSSFCellStyle.ALIGN_RIGHT == clone.getAlignment()); + assertFalse("TestingFont" == clone.getFont(wbClone).getFontName()); + + clone.cloneStyleFrom(orig); + assertTrue(HSSFCellStyle.ALIGN_RIGHT == clone.getAlignment()); + assertTrue("TestingFont" == clone.getFont(wbClone).getFontName()); + assertTrue(fmtClone.getFormat("Test##") == clone.getDataFormat()); + assertFalse(fmtClone.getFormat("Test##") == fmt.getFormat("Test##")); + assertEquals(5, wbClone.getNumberOfFonts()); + } public static void main(String [] ignored_args) { diff --git a/src/testcases/org/apache/poi/hssf/usermodel/TestHSSFCell.java b/src/testcases/org/apache/poi/hssf/usermodel/TestHSSFCell.java index 7f4375847..c3d73cf4a 100644 --- a/src/testcases/org/apache/poi/hssf/usermodel/TestHSSFCell.java +++ b/src/testcases/org/apache/poi/hssf/usermodel/TestHSSFCell.java @@ -381,6 +381,43 @@ public final class TestHSSFCell extends TestCase { throw new AssertionFailedError("Identified bug 44606"); } } + + /** + * Test to ensure we can only assign cell styles that belong + * to our workbook, and not those from other workbooks. + */ + public void testCellStyleWorkbookMatch() throws Exception { + HSSFWorkbook wbA = new HSSFWorkbook(); + HSSFWorkbook wbB = new HSSFWorkbook(); + + HSSFCellStyle styA = wbA.createCellStyle(); + HSSFCellStyle styB = wbB.createCellStyle(); + + styA.verifyBelongsToWorkbook(wbA); + styB.verifyBelongsToWorkbook(wbB); + try { + styA.verifyBelongsToWorkbook(wbB); + fail(); + } catch(IllegalArgumentException e) {} + try { + styB.verifyBelongsToWorkbook(wbA); + fail(); + } catch(IllegalArgumentException e) {} + + HSSFCell cellA = wbA.createSheet().createRow(0).createCell((short)0); + HSSFCell cellB = wbB.createSheet().createRow(0).createCell((short)0); + + cellA.setCellStyle(styA); + cellB.setCellStyle(styB); + try { + cellA.setCellStyle(styB); + fail(); + } catch(IllegalArgumentException e) {} + try { + cellB.setCellStyle(styA); + fail(); + } catch(IllegalArgumentException e) {} + } public static void main(String [] args) { junit.textui.TestRunner.run(TestHSSFCell.class);