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
This commit is contained in:
parent
1cbe4a5db2
commit
cec1612121
@ -50,6 +50,8 @@
|
||||
<action dev="POI-DEVELOPERS" type="add">Created a common interface for handling Excel files, irrespective of if they are .xls or .xlsx</action>
|
||||
</release>
|
||||
<release version="3.1.1-alpha1" date="2008-??-??">
|
||||
<action dev="POI-DEVELOPERS" type="add">Allow the cloning of one HSSFCellStyle onto another, including cloning styles from one HSSFWorkbook onto another</action>
|
||||
<action dev="POI-DEVELOPERS" type="fix">45289 - finished support for special comparison operators in COUNTIF</action>
|
||||
<action dev="POI-DEVELOPERS" type="fix">45126 - Avoid generating multiple NamedRanges with the same name, which Excel dislikes</action>
|
||||
<action dev="POI-DEVELOPERS" type="fix">Fix cell.getRichStringCellValue() for formula cells with string results</action>
|
||||
<action dev="POI-DEVELOPERS" type="fix">45365 - Handle more excel number formatting rules in FormatTrackingHSSFListener / XLS2CSVmra</action>
|
||||
|
@ -27,7 +27,21 @@
|
||||
</authors>
|
||||
</header>
|
||||
<body>
|
||||
<section><title>Converting existing HSSF Usermodel code to SS Usermodel (for XSSF and HSSF)</title>
|
||||
<section><title>Things that have to be changed when upgrading to POI 3.5</title>
|
||||
<p>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.</p>
|
||||
<section><title>org.apache.poi.hssf.usermodel.HSSFFormulaEvaluator.CellValue</title>
|
||||
<p>Annoyingly, java will not let you access a static inner class via
|
||||
a child of the parent one. So, all references to
|
||||
<em>org.apache.poi.hssf.usermodel.HSSFFormulaEvaluator.CellValue</em>
|
||||
will need to be changed to
|
||||
<em>org.apache.poi.ss.usermodel.FormulaEvaluator.CellValue</em>
|
||||
</p>
|
||||
</section>
|
||||
</section>
|
||||
<section><title>Converting existing HSSF Usermodel code to SS Usermodel (for XSSF and HSSF)</title>
|
||||
|
||||
<section><title>Why change?</title>
|
||||
<p>If you have existing HSSF usermodel code that works just
|
||||
|
@ -47,6 +47,8 @@
|
||||
<action dev="POI-DEVELOPERS" type="add">Created a common interface for handling Excel files, irrespective of if they are .xls or .xlsx</action>
|
||||
</release>
|
||||
<release version="3.1.1-alpha1" date="2008-??-??">
|
||||
<action dev="POI-DEVELOPERS" type="add">Allow the cloning of one HSSFCellStyle onto another, including cloning styles from one HSSFWorkbook onto another</action>
|
||||
<action dev="POI-DEVELOPERS" type="fix">45289 - finished support for special comparison operators in COUNTIF</action>
|
||||
<action dev="POI-DEVELOPERS" type="fix">45126 - Avoid generating multiple NamedRanges with the same name, which Excel dislikes</action>
|
||||
<action dev="POI-DEVELOPERS" type="fix">Fix cell.getRichStringCellValue() for formula cells with string results</action>
|
||||
<action dev="POI-DEVELOPERS" type="fix">45365 - Handle more excel number formatting rules in FormatTrackingHSSFListener / XLS2CSVmra</action>
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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 <code>null</code> 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<len; i++) {
|
||||
char ch = value.charAt(i);
|
||||
switch(ch) {
|
||||
case '?':
|
||||
hasWildCard = true;
|
||||
// match exactly one character
|
||||
sb.append('.');
|
||||
continue;
|
||||
case '*':
|
||||
hasWildCard = true;
|
||||
// match one or more occurrences of any character
|
||||
sb.append(".*");
|
||||
continue;
|
||||
case '~':
|
||||
if (i+1<len) {
|
||||
ch = value.charAt(i+1);
|
||||
switch (ch) {
|
||||
case '?':
|
||||
case '*':
|
||||
hasWildCard = true;
|
||||
sb.append('[').append(ch).append(']');
|
||||
i++; // Note - incrementing loop variable here
|
||||
continue;
|
||||
}
|
||||
}
|
||||
// else not '~?' or '~*'
|
||||
sb.append('~'); // just plain '~'
|
||||
continue;
|
||||
case '.':
|
||||
case '$':
|
||||
case '^':
|
||||
case '[':
|
||||
case ']':
|
||||
case '(':
|
||||
case ')':
|
||||
// escape literal characters that would have special meaning in regex
|
||||
sb.append("\\").append(ch);
|
||||
continue;
|
||||
}
|
||||
sb.append(ch);
|
||||
}
|
||||
if (hasWildCard) {
|
||||
return Pattern.compile(sb.toString());
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@ -132,8 +338,7 @@ public final class Countif implements Function {
|
||||
// perhaps this should be an exception
|
||||
return ErrorEval.VALUE_INVALID;
|
||||
}
|
||||
|
||||
AreaEval range = (AreaEval) args[0];
|
||||
|
||||
Eval criteriaArg = args[1];
|
||||
if(criteriaArg instanceof RefEval) {
|
||||
// criteria is not a literal value, but a cell reference
|
||||
@ -144,31 +349,47 @@ public final class Countif implements Function {
|
||||
// other non literal tokens such as function calls, have been fully evaluated
|
||||
// for example COUNTIF(B2:D4, COLUMN(E1))
|
||||
}
|
||||
if(criteriaArg instanceof BlankEval) {
|
||||
// If the criteria arg is a reference to a blank cell, countif always returns zero.
|
||||
return NumberEval.ZERO;
|
||||
}
|
||||
I_MatchPredicate mp = createCriteriaPredicate(criteriaArg);
|
||||
return countMatchingCellsInArea(range, mp);
|
||||
return countMatchingCellsInArea(args[0], mp);
|
||||
}
|
||||
/**
|
||||
* @return the number of evaluated cells in the range that match the specified criteria
|
||||
*/
|
||||
private Eval countMatchingCellsInArea(AreaEval range, I_MatchPredicate criteriaPredicate) {
|
||||
ValueEval[] values = range.getValues();
|
||||
private Eval countMatchingCellsInArea(Eval rangeArg, I_MatchPredicate criteriaPredicate) {
|
||||
int result = 0;
|
||||
for (int i = 0; i < values.length; i++) {
|
||||
if(criteriaPredicate.matches(values[i])) {
|
||||
if (rangeArg instanceof RefEval) {
|
||||
RefEval refEval = (RefEval) rangeArg;
|
||||
if(criteriaPredicate.matches(refEval.getInnerValueEval())) {
|
||||
result++;
|
||||
}
|
||||
} else if (rangeArg instanceof AreaEval) {
|
||||
|
||||
AreaEval range = (AreaEval) rangeArg;
|
||||
ValueEval[] values = range.getValues();
|
||||
for (int i = 0; i < values.length; i++) {
|
||||
if(criteriaPredicate.matches(values[i])) {
|
||||
result++;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
throw new IllegalArgumentException("Bad range arg type (" + rangeArg.getClass().getName() + ")");
|
||||
}
|
||||
return new NumberEval(result);
|
||||
}
|
||||
|
||||
private static I_MatchPredicate createCriteriaPredicate(Eval evaluatedCriteriaArg) {
|
||||
|
||||
/* package */ static I_MatchPredicate createCriteriaPredicate(Eval evaluatedCriteriaArg) {
|
||||
|
||||
if(evaluatedCriteriaArg instanceof NumberEval) {
|
||||
return new NumberMatcher(((NumberEval)evaluatedCriteriaArg).getNumberValue());
|
||||
return new NumberMatcher(((NumberEval)evaluatedCriteriaArg).getNumberValue(), CmpOp.OP_NONE);
|
||||
}
|
||||
if(evaluatedCriteriaArg instanceof BoolEval) {
|
||||
return new BooleanMatcher(((BoolEval)evaluatedCriteriaArg).getBooleanValue());
|
||||
return new BooleanMatcher(((BoolEval)evaluatedCriteriaArg).getBooleanValue(), CmpOp.OP_NONE);
|
||||
}
|
||||
|
||||
|
||||
if(evaluatedCriteriaArg instanceof StringEval) {
|
||||
return createGeneralMatchPredicate((StringEval)evaluatedCriteriaArg);
|
||||
}
|
||||
@ -181,50 +402,29 @@ public final class Countif implements Function {
|
||||
*/
|
||||
private static I_MatchPredicate createGeneralMatchPredicate(StringEval stringEval) {
|
||||
String value = stringEval.getStringValue();
|
||||
char firstChar = value.charAt(0);
|
||||
CmpOp operator = CmpOp.getOperator(value);
|
||||
value = value.substring(operator.getLength());
|
||||
|
||||
Boolean booleanVal = parseBoolean(value);
|
||||
if(booleanVal != null) {
|
||||
return new BooleanMatcher(booleanVal.booleanValue());
|
||||
return new BooleanMatcher(booleanVal.booleanValue(), operator);
|
||||
}
|
||||
|
||||
Double doubleVal = parseDouble(value);
|
||||
if(doubleVal != null) {
|
||||
return new NumberMatcher(doubleVal.doubleValue());
|
||||
}
|
||||
switch(firstChar) {
|
||||
case '>':
|
||||
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':
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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());
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -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;
|
||||
|
@ -705,4 +705,21 @@ public interface CellStyle {
|
||||
*/
|
||||
short getFillForegroundColor();
|
||||
|
||||
}
|
||||
/**
|
||||
* 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);
|
||||
}
|
||||
|
@ -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)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -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();
|
||||
|
@ -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) {}
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
|
Binary file not shown.
BIN
src/testcases/org/apache/poi/hssf/data/countifExamples.xls
Normal file
BIN
src/testcases/org/apache/poi/hssf/data/countifExamples.xls
Normal file
Binary file not shown.
@ -38,6 +38,7 @@ public final class AllModelTests {
|
||||
result.addTestSuite(TestRVA.class);
|
||||
result.addTestSuite(TestSheet.class);
|
||||
result.addTestSuite(TestSheetAdditional.class);
|
||||
result.addTestSuite(TestWorkbook.class);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
61
src/testcases/org/apache/poi/hssf/model/TestWorkbook.java
Normal file
61
src/testcases/org/apache/poi/hssf/model/TestWorkbook.java
Normal file
@ -0,0 +1,61 @@
|
||||
/* ====================================================================
|
||||
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.hssf.model;
|
||||
|
||||
import org.apache.poi.hssf.record.FontRecord;
|
||||
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
|
||||
/**
|
||||
* Unit test for the Workbook class.
|
||||
*
|
||||
* @author Glen Stampoultzis (glens at apache.org)
|
||||
*/
|
||||
public final class TestWorkbook extends TestCase {
|
||||
public void testFontStuff() throws Exception {
|
||||
Workbook wb = (new HW()).getWorkbook();
|
||||
|
||||
assertEquals(4, wb.getNumberOfFontRecords());
|
||||
|
||||
FontRecord f1 = wb.getFontRecordAt(0);
|
||||
FontRecord f4 = wb.getFontRecordAt(3);
|
||||
|
||||
assertEquals(0, wb.getFontIndex(f1));
|
||||
assertEquals(3, wb.getFontIndex(f4));
|
||||
|
||||
assertEquals(f1, wb.getFontRecordAt(0));
|
||||
assertEquals(f4, wb.getFontRecordAt(3));
|
||||
|
||||
// There is no 4! new ones go in at 5
|
||||
|
||||
FontRecord n = wb.createNewFont();
|
||||
assertEquals(5, wb.getNumberOfFontRecords());
|
||||
assertEquals(5, wb.getFontIndex(n));
|
||||
assertEquals(n, wb.getFontRecordAt(5));
|
||||
}
|
||||
|
||||
private class HW extends HSSFWorkbook {
|
||||
private HW() {
|
||||
super();
|
||||
}
|
||||
protected Workbook getWorkbook() {
|
||||
return super.getWorkbook();
|
||||
}
|
||||
}
|
||||
}
|
@ -61,7 +61,9 @@ public final class AllRecordTests {
|
||||
result.addTestSuite(TestEmbeddedObjectRefSubRecord.class);
|
||||
result.addTestSuite(TestEndSubRecord.class);
|
||||
result.addTestSuite(TestEscherAggregate.class);
|
||||
result.addTestSuite(TestExtendedFormatRecord.class);
|
||||
result.addTestSuite(TestExternalNameRecord.class);
|
||||
result.addTestSuite(TestFontRecord.class);
|
||||
result.addTestSuite(TestFontBasisRecord.class);
|
||||
result.addTestSuite(TestFontIndexRecord.class);
|
||||
result.addTestSuite(TestFormulaRecord.class);
|
||||
|
@ -0,0 +1,138 @@
|
||||
/* ====================================================================
|
||||
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.hssf.record;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
/**
|
||||
*/
|
||||
public final class TestExtendedFormatRecord extends TestCase {
|
||||
byte[] header = new byte[] {
|
||||
0xE0-256, 00, 0x14, 00 // sid=e0, 20 bytes long
|
||||
};
|
||||
byte[] data = new byte[] {
|
||||
00, 00, // Font 0
|
||||
00, 00, // Format 0
|
||||
0xF5-256, 0xFF-256, // Cell opts ...
|
||||
0x20, 00, // Alignment 20
|
||||
00, 00, // Ident 0
|
||||
00, 00, // Border 0
|
||||
00, 00, // Palette 0
|
||||
00, 00, 00, 00, // ADTL Palette 0
|
||||
0xC0-256, 0x20 // Fill Palette 20c0
|
||||
};
|
||||
|
||||
public TestExtendedFormatRecord(String name)
|
||||
{
|
||||
super(name);
|
||||
}
|
||||
|
||||
public void testLoad()
|
||||
throws Exception
|
||||
{
|
||||
ExtendedFormatRecord record = new ExtendedFormatRecord(new TestcaseRecordInputStream((short)0xe0, (short)data.length, data));
|
||||
assertEquals(0, record.getFontIndex());
|
||||
assertEquals(0, record.getFormatIndex());
|
||||
assertEquals(0xF5-256, record.getCellOptions());
|
||||
assertEquals(0x20, record.getAlignmentOptions());
|
||||
assertEquals(0, record.getIndentionOptions());
|
||||
assertEquals(0, record.getBorderOptions());
|
||||
assertEquals(0, record.getPaletteOptions());
|
||||
assertEquals(0, record.getAdtlPaletteOptions());
|
||||
assertEquals(0x20c0, record.getFillPaletteOptions());
|
||||
|
||||
assertEquals( 20 + 4, record.getRecordSize() );
|
||||
record.validateSid((short)0xe0);
|
||||
}
|
||||
|
||||
public void testStore()
|
||||
{
|
||||
// .fontindex = 0
|
||||
// .formatindex = 0
|
||||
// .celloptions = fffffff5
|
||||
// .islocked = true
|
||||
// .ishidden = false
|
||||
// .recordtype= 1
|
||||
// .parentidx = fff
|
||||
// .alignmentoptions= 20
|
||||
// .alignment = 0
|
||||
// .wraptext = false
|
||||
// .valignment= 2
|
||||
// .justlast = 0
|
||||
// .rotation = 0
|
||||
// .indentionoptions= 0
|
||||
// .indent = 0
|
||||
// .shrinktoft= false
|
||||
// .mergecells= false
|
||||
// .readngordr= 0
|
||||
// .formatflag= false
|
||||
// .fontflag = false
|
||||
// .prntalgnmt= false
|
||||
// .borderflag= false
|
||||
// .paternflag= false
|
||||
// .celloption= false
|
||||
// .borderoptns = 0
|
||||
// .lftln = 0
|
||||
// .rgtln = 0
|
||||
// .topln = 0
|
||||
// .btmln = 0
|
||||
// .paleteoptns = 0
|
||||
// .leftborder= 0
|
||||
// .rghtborder= 0
|
||||
// .diag = 0
|
||||
// .paleteoptn2 = 0
|
||||
// .topborder = 0
|
||||
// .botmborder= 0
|
||||
// .adtldiag = 0
|
||||
// .diaglnstyl= 0
|
||||
// .fillpattrn= 0
|
||||
// .fillpaloptn = 20c0
|
||||
// .foreground= 40
|
||||
// .background= 41
|
||||
|
||||
ExtendedFormatRecord record = new ExtendedFormatRecord();
|
||||
record.setFontIndex((short)0);
|
||||
record.setFormatIndex((short)0);
|
||||
|
||||
record.setLocked(true);
|
||||
record.setHidden(false);
|
||||
record.setXFType((short)1);
|
||||
record.setParentIndex((short)0xfff);
|
||||
|
||||
record.setVerticalAlignment((short)2);
|
||||
|
||||
record.setFillForeground((short)0x40);
|
||||
record.setFillBackground((short)0x41);
|
||||
|
||||
byte [] recordBytes = record.serialize();
|
||||
assertEquals(recordBytes.length - 4, data.length);
|
||||
for (int i = 0; i < data.length; i++)
|
||||
assertEquals("At offset " + i, data[i], recordBytes[i+4]);
|
||||
}
|
||||
|
||||
public void testCloneOnto() throws Exception {
|
||||
ExtendedFormatRecord base = new ExtendedFormatRecord(new TestcaseRecordInputStream((short)0xe0, (short)data.length, data));
|
||||
|
||||
ExtendedFormatRecord other = new ExtendedFormatRecord();
|
||||
other.cloneStyleFrom(base);
|
||||
|
||||
byte [] recordBytes = other.serialize();
|
||||
assertEquals(recordBytes.length - 4, data.length);
|
||||
for (int i = 0; i < data.length; i++)
|
||||
assertEquals("At offset " + i, data[i], recordBytes[i+4]);
|
||||
}
|
||||
}
|
124
src/testcases/org/apache/poi/hssf/record/TestFontRecord.java
Normal file
124
src/testcases/org/apache/poi/hssf/record/TestFontRecord.java
Normal file
@ -0,0 +1,124 @@
|
||||
|
||||
/* ====================================================================
|
||||
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.hssf.record;
|
||||
|
||||
|
||||
import junit.framework.TestCase;
|
||||
|
||||
/**
|
||||
* Tests the serialization and deserialization of the FontRecord
|
||||
* class works correctly. Test data taken directly from a real
|
||||
* Excel file.
|
||||
*/
|
||||
public class TestFontRecord
|
||||
extends TestCase
|
||||
{
|
||||
byte[] header = new byte[] {
|
||||
0x31, 00, 0x1a, 00, // sid=31, 26 bytes long
|
||||
};
|
||||
byte[] data = new byte[] {
|
||||
0xC8-256, 00, // font height = xc8
|
||||
00, 00, // attrs = 0
|
||||
0xFF-256, 0x7F, // colour palette = x7fff
|
||||
0x90-256, 0x01, // bold weight = x190
|
||||
00, 00, // supersubscript
|
||||
00, 00, // underline, family
|
||||
00, 00, // charset, padding
|
||||
05, 01, // name length, unicode flag
|
||||
0x41, 0x00, 0x72, 0x00, 0x69, // Arial, as unicode
|
||||
0x00, 0x61, 0x00, 0x6C, 0x00
|
||||
};
|
||||
|
||||
public TestFontRecord(String name)
|
||||
{
|
||||
super(name);
|
||||
}
|
||||
|
||||
public void testLoad()
|
||||
throws Exception
|
||||
{
|
||||
|
||||
FontRecord record = new FontRecord(new TestcaseRecordInputStream((short)0x31, (short)data.length, data));
|
||||
assertEquals( 0xc8, record.getFontHeight());
|
||||
assertEquals( 0x00, record.getAttributes());
|
||||
assertFalse( record.isItalic());
|
||||
assertFalse( record.isStruckout());
|
||||
assertFalse( record.isMacoutlined());
|
||||
assertFalse( record.isMacshadowed());
|
||||
assertEquals( 0x7fff, record.getColorPaletteIndex());
|
||||
assertEquals( 0x190, record.getBoldWeight());
|
||||
assertEquals( 0x00, record.getSuperSubScript());
|
||||
assertEquals( 0x00, record.getUnderline());
|
||||
assertEquals( 0x00, record.getFamily());
|
||||
assertEquals( 0x00, record.getCharset());
|
||||
assertEquals( 0x05, record.getFontNameLength());
|
||||
assertEquals( "Arial", record.getFontName());
|
||||
|
||||
|
||||
assertEquals( 26 + 4, record.getRecordSize() );
|
||||
record.validateSid((short)0x31);
|
||||
}
|
||||
|
||||
public void testStore()
|
||||
{
|
||||
// .fontheight = c8
|
||||
// .attributes = 0
|
||||
// .italic = false
|
||||
// .strikout = false
|
||||
// .macoutlined= false
|
||||
// .macshadowed= false
|
||||
// .colorpalette = 7fff
|
||||
// .boldweight = 190
|
||||
// .supersubscript = 0
|
||||
// .underline = 0
|
||||
// .family = 0
|
||||
// .charset = 0
|
||||
// .namelength = 5
|
||||
// .fontname = Arial
|
||||
|
||||
FontRecord record = new FontRecord();
|
||||
record.setFontHeight((short)0xc8);
|
||||
record.setAttributes((short)0);
|
||||
record.setColorPaletteIndex((short)0x7fff);
|
||||
record.setBoldWeight((short)0x190);
|
||||
record.setSuperSubScript((short)0);
|
||||
record.setUnderline((byte)0);
|
||||
record.setFamily((byte)0);
|
||||
record.setCharset((byte)0);
|
||||
record.setFontNameLength((byte)5);
|
||||
record.setFontName("Arial");
|
||||
|
||||
byte [] recordBytes = record.serialize();
|
||||
assertEquals(recordBytes.length - 4, data.length);
|
||||
for (int i = 0; i < data.length; i++)
|
||||
assertEquals("At offset " + i, data[i], recordBytes[i+4]);
|
||||
}
|
||||
|
||||
public void testCloneOnto() throws Exception {
|
||||
FontRecord base = new FontRecord(new TestcaseRecordInputStream((short)0x31, (short)data.length, data));
|
||||
|
||||
FontRecord other = new FontRecord();
|
||||
other.cloneStyleFrom(base);
|
||||
|
||||
byte [] recordBytes = other.serialize();
|
||||
assertEquals(recordBytes.length - 4, data.length);
|
||||
for (int i = 0; i < data.length; i++)
|
||||
assertEquals("At offset " + i, data[i], recordBytes[i+4]);
|
||||
}
|
||||
}
|
@ -18,8 +18,10 @@
|
||||
|
||||
package org.apache.poi.hssf.record.formula.functions;
|
||||
|
||||
import junit.framework.AssertionFailedError;
|
||||
import junit.framework.TestCase;
|
||||
|
||||
import org.apache.poi.hssf.HSSFTestDataSamples;
|
||||
import org.apache.poi.hssf.record.formula.AreaPtg;
|
||||
import org.apache.poi.hssf.record.formula.RefPtg;
|
||||
import org.apache.poi.hssf.record.formula.eval.Area2DEval;
|
||||
@ -31,6 +33,13 @@ import org.apache.poi.hssf.record.formula.eval.NumberEval;
|
||||
import org.apache.poi.hssf.record.formula.eval.Ref2DEval;
|
||||
import org.apache.poi.hssf.record.formula.eval.StringEval;
|
||||
import org.apache.poi.hssf.record.formula.eval.ValueEval;
|
||||
import org.apache.poi.hssf.record.formula.functions.Countif.I_MatchPredicate;
|
||||
import org.apache.poi.hssf.usermodel.HSSFCell;
|
||||
import org.apache.poi.hssf.usermodel.HSSFFormulaEvaluator;
|
||||
import org.apache.poi.hssf.usermodel.HSSFRow;
|
||||
import org.apache.poi.hssf.usermodel.HSSFSheet;
|
||||
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
|
||||
import org.apache.poi.ss.usermodel.FormulaEvaluator.CellValue;
|
||||
|
||||
/**
|
||||
* Test cases for COUNT(), COUNTA() COUNTIF(), COUNTBLANK()
|
||||
@ -146,4 +155,154 @@ public final class TestCountFuncs extends TestCase {
|
||||
double result = NumericFunctionInvoker.invoke(new Countif(), args);
|
||||
assertEquals(expected, result, 0);
|
||||
}
|
||||
|
||||
public void testCountIfEmptyStringCriteria() {
|
||||
I_MatchPredicate mp;
|
||||
|
||||
// pred '=' matches blank cell but not empty string
|
||||
mp = Countif.createCriteriaPredicate(new StringEval("="));
|
||||
confirmPredicate(false, mp, "");
|
||||
confirmPredicate(true, mp, null);
|
||||
|
||||
// pred '' matches both blank cell but not empty string
|
||||
mp = Countif.createCriteriaPredicate(new StringEval(""));
|
||||
confirmPredicate(true, mp, "");
|
||||
confirmPredicate(true, mp, null);
|
||||
|
||||
// pred '<>' 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<maxRow; rowIx++) {
|
||||
HSSFRow row = sheet.getRow(rowIx);
|
||||
if(row == null) {
|
||||
continue;
|
||||
}
|
||||
HSSFCell cell = row.getCell(COL_IX_ACTUAL);
|
||||
fe.setCurrentRow(row);
|
||||
CellValue cv = fe.evaluate(cell);
|
||||
double actualValue = cv.getNumberValue();
|
||||
double expectedValue = row.getCell(COL_IX_EXPECTED).getNumericCellValue();
|
||||
if (actualValue != expectedValue) {
|
||||
System.err.println("Problem with test case on row " + (rowIx+1) + " "
|
||||
+ "Expected = (" + expectedValue + ") Actual=(" + actualValue + ") ");
|
||||
failureCount++;
|
||||
}
|
||||
}
|
||||
|
||||
if (failureCount > 0) {
|
||||
throw new AssertionFailedError(failureCount + " countif evaluations failed. See stderr for more details");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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);
|
||||
|
Loading…
Reference in New Issue
Block a user