diff --git a/src/contrib/src/org/apache/poi/hssf/usermodel/contrib/HSSFCellUtil.java b/src/contrib/src/org/apache/poi/hssf/usermodel/contrib/HSSFCellUtil.java index 67f4b38cb..8f40ab391 100644 --- a/src/contrib/src/org/apache/poi/hssf/usermodel/contrib/HSSFCellUtil.java +++ b/src/contrib/src/org/apache/poi/hssf/usermodel/contrib/HSSFCellUtil.java @@ -113,7 +113,7 @@ public final class HSSFCellUtil if ( cell == null ) { - cell = row.createCell( (short)column ); + cell = row.createCell(column ); } return cell; } diff --git a/src/documentation/content/xdocs/changes.xml b/src/documentation/content/xdocs/changes.xml index 62fee5b1c..bfc44a828 100644 --- a/src/documentation/content/xdocs/changes.xml +++ b/src/documentation/content/xdocs/changes.xml @@ -64,6 +64,9 @@ Created a common interface for handling Excel files, irrespective of if they are .xls or .xlsx + 45682 - Fix for cloning of CFRecordsAggregate + Initial support for evaluating external add-in functions like YEARFRAC + 45672 - Fix for MissingRecordAwareHSSFListener to prevent multiple LastCellOfRowDummyRecords when shared formulas are present 45645 - Fix for HSSFSheet.autoSizeColumn() for widths exceeding Short.MAX_VALUE 45623 - Support for additional HSSF header and footer fields, including bold and full file path 45623 - Support stripping HSSF header and footer fields (eg page number) out of header and footer text if required diff --git a/src/documentation/content/xdocs/status.xml b/src/documentation/content/xdocs/status.xml index 96272b165..5ed54a515 100644 --- a/src/documentation/content/xdocs/status.xml +++ b/src/documentation/content/xdocs/status.xml @@ -61,6 +61,9 @@ Created a common interface for handling Excel files, irrespective of if they are .xls or .xlsx + 45682 - Fix for cloning of CFRecordsAggregate + Initial support for evaluating external add-in functions like YEARFRAC + 45672 - Fix for MissingRecordAwareHSSFListener to prevent multiple LastCellOfRowDummyRecords when shared formulas are present 45645 - Fix for HSSFSheet.autoSizeColumn() for widths exceeding Short.MAX_VALUE 45623 - Support for additional HSSF header and footer fields, including bold and full file path 45623 - Support stripping HSSF header and footer fields (eg page number) out of header and footer text if required diff --git a/src/java/org/apache/poi/hssf/dev/HSSF.java b/src/java/org/apache/poi/hssf/dev/HSSF.java index 5297f0781..6583b94c6 100644 --- a/src/java/org/apache/poi/hssf/dev/HSSF.java +++ b/src/java/org/apache/poi/hssf/dev/HSSF.java @@ -132,7 +132,7 @@ public class HSSF { c.setCellStyle(cs); } - c = r.createCell(( short ) (cellnum + 1), + c = r.createCell(cellnum + 1, HSSFCell.CELL_TYPE_STRING); c.setCellValue(new HSSFRichTextString("TEST")); s.setColumnWidth(( short ) (cellnum + 1), @@ -347,7 +347,7 @@ public class HSSF sheet.removeRow(row); } HSSFRow row = sheet.getRow(39); - HSSFCell cell = row.getCell(( short ) 3); + HSSFCell cell = row.getCell(3); cell.setCellType(HSSFCell.CELL_TYPE_STRING); cell.setCellValue("MODIFIED CELL!!!!!"); diff --git a/src/java/org/apache/poi/hssf/eventusermodel/MissingRecordAwareHSSFListener.java b/src/java/org/apache/poi/hssf/eventusermodel/MissingRecordAwareHSSFListener.java index 0bdcb1d3d..e41b0b92a 100644 --- a/src/java/org/apache/poi/hssf/eventusermodel/MissingRecordAwareHSSFListener.java +++ b/src/java/org/apache/poi/hssf/eventusermodel/MissingRecordAwareHSSFListener.java @@ -17,22 +17,15 @@ package org.apache.poi.hssf.eventusermodel; -import org.apache.poi.hssf.eventusermodel.HSSFListener; import org.apache.poi.hssf.eventusermodel.dummyrecord.LastCellOfRowDummyRecord; import org.apache.poi.hssf.eventusermodel.dummyrecord.MissingCellDummyRecord; import org.apache.poi.hssf.eventusermodel.dummyrecord.MissingRowDummyRecord; import org.apache.poi.hssf.record.BOFRecord; -import org.apache.poi.hssf.record.BlankRecord; -import org.apache.poi.hssf.record.BoolErrRecord; -import org.apache.poi.hssf.record.BoundSheetRecord; -import org.apache.poi.hssf.record.FormulaRecord; -import org.apache.poi.hssf.record.LabelRecord; -import org.apache.poi.hssf.record.LabelSSTRecord; +import org.apache.poi.hssf.record.CellValueRecordInterface; import org.apache.poi.hssf.record.NoteRecord; -import org.apache.poi.hssf.record.NumberRecord; -import org.apache.poi.hssf.record.RKRecord; import org.apache.poi.hssf.record.Record; import org.apache.poi.hssf.record.RowRecord; +import org.apache.poi.hssf.record.SharedFormulaRecord; /** *

A HSSFListener which tracks rows and columns, and will @@ -44,16 +37,16 @@ import org.apache.poi.hssf.record.RowRecord; * file, or was skipped from being written as it was * blank. */ -public class MissingRecordAwareHSSFListener implements HSSFListener { +public final class MissingRecordAwareHSSFListener implements HSSFListener { private HSSFListener childListener; // Need to have different counters for cell rows and // row rows, as you sometimes get a RowRecord in the // middle of some cells, and that'd break everything - private int lastRowRow = -1; + private int lastRowRow; - private int lastCellRow = -1; - private int lastCellColumn = -1; + private int lastCellRow; + private int lastCellColumn; /** * Constructs a new MissingRecordAwareHSSFListener, which @@ -62,128 +55,80 @@ public class MissingRecordAwareHSSFListener implements HSSFListener { * @param listener The HSSFListener to pass records on to */ public MissingRecordAwareHSSFListener(HSSFListener listener) { + resetCounts(); childListener = listener; } public void processRecord(Record record) { - int thisRow = -1; - int thisColumn = -1; - - switch (record.getSid()) - { - // the BOFRecord can represent either the beginning of a sheet or the workbook - case BOFRecord.sid: - BOFRecord bof = (BOFRecord) record; - if (bof.getType() == bof.TYPE_WORKBOOK) - { - // Reset the row and column counts - new workbook - lastRowRow = -1; - lastCellRow = -1; - lastCellColumn = -1; - //System.out.println("Encountered workbook"); - } else if (bof.getType() == bof.TYPE_WORKSHEET) - { - // Reset the row and column counts - new sheet - lastRowRow = -1; - lastCellRow = -1; - lastCellColumn = -1; - //System.out.println("Encountered sheet reference"); - } - break; - case BoundSheetRecord.sid: - BoundSheetRecord bsr = (BoundSheetRecord) record; - //System.out.println("New sheet named: " + bsr.getSheetname()); - break; - case RowRecord.sid: - RowRecord rowrec = (RowRecord) record; - //System.out.println("Row " + rowrec.getRowNumber() + " found, first column at " - // + rowrec.getFirstCol() + " last column at " + rowrec.getLastCol()); - - // If there's a jump in rows, fire off missing row records - if(lastRowRow+1 < rowrec.getRowNumber()) { - for(int i=(lastRowRow+1); i -1) { for(int i=lastCellRow; inull. */ @@ -166,10 +126,16 @@ public final class CFRecordsAggregate extends Record { return (CFRuleRecord) rules.get(idx); } public void setRule(int idx, CFRuleRecord r) { + if (r == null) { + throw new IllegalArgumentException("r must not be null"); + } checkRuleIndex(idx); rules.set(idx, r); } public void addRule(CFRuleRecord r) { + if (r == null) { + throw new IllegalArgumentException("r must not be null"); + } if(rules.size() >= MAX_CONDTIONAL_FORMAT_RULES) { throw new IllegalStateException("Cannot have more than " + MAX_CONDTIONAL_FORMAT_RULES + " conditional format rules"); @@ -181,26 +147,6 @@ public final class CFRecordsAggregate extends Record { return rules.size(); } - /** - * @return sum of sizes of all aggregated records - */ - public int getRecordSize() - { - int size = 0; - if( header != null) - { - size += header.getRecordSize(); - } - if( rules != null) - { - for(Iterator irecs = rules.iterator(); irecs.hasNext(); ) - { - size += (( Record ) irecs.next()).getRecordSize(); - } - } - return size; - } - /** * String representation of CFRecordsAggregate */ @@ -216,12 +162,17 @@ public final class CFRecordsAggregate extends Record { for(int i=0; i @@ -53,7 +52,8 @@ public final class ConditionalFormattingTable extends RecordAggregate { public void visitContainedRecords(RecordVisitor rv) { for (int i = 0; i < _cfHeaders.size(); i++) { - rv.visitRecord((Record) _cfHeaders.get(i)); + CFRecordsAggregate subAgg = (CFRecordsAggregate) _cfHeaders.get(i); + subAgg.visitContainedRecords(rv); } } diff --git a/src/java/org/apache/poi/hssf/record/formula/NameXPtg.java b/src/java/org/apache/poi/hssf/record/formula/NameXPtg.java index b6076e1ea..393a8cd69 100644 --- a/src/java/org/apache/poi/hssf/record/formula/NameXPtg.java +++ b/src/java/org/apache/poi/hssf/record/formula/NameXPtg.java @@ -30,11 +30,11 @@ public final class NameXPtg extends OperandPtg { private final static int SIZE = 7; /** index to REF entry in externsheet record */ - private int _sheetRefIndex; + private final int _sheetRefIndex; /** index to defined name or externname table(1 based) */ - private int _nameNumber; + private final int _nameNumber; /** reserved must be 0 */ - private int _reserved; + private final int _reserved; private NameXPtg(int sheetRefIndex, int nameNumber, int reserved) { _sheetRefIndex = sheetRefIndex; @@ -73,4 +73,11 @@ public final class NameXPtg extends OperandPtg { public byte getDefaultOperandClass() { return Ptg.CLASS_VALUE; } + + public int getSheetRefIndex() { + return _sheetRefIndex; + } + public int getNameIndex() { + return _nameNumber - 1; + } } diff --git a/src/java/org/apache/poi/hssf/record/formula/atp/AnalysisToolPak.java b/src/java/org/apache/poi/hssf/record/formula/atp/AnalysisToolPak.java new file mode 100644 index 000000000..e5ec7fa83 --- /dev/null +++ b/src/java/org/apache/poi/hssf/record/formula/atp/AnalysisToolPak.java @@ -0,0 +1,154 @@ +/* ==================================================================== + 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.formula.atp; + +import java.util.HashMap; +import java.util.Map; + +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.ValueEval; +import org.apache.poi.hssf.record.formula.functions.FreeRefFunction; +import org.apache.poi.ss.usermodel.Sheet; +import org.apache.poi.ss.usermodel.Workbook; + +public final class AnalysisToolPak { + + private static final FreeRefFunction NotImplemented = new FreeRefFunction() { + + public ValueEval evaluate(Eval[] args, int srcCellRow, short srcCellCol, + Workbook workbook, Sheet sheet) { + return ErrorEval.FUNCTION_NOT_IMPLEMENTED; + } + }; + + private static Map _functionsByName = createFunctionsMap(); + + private AnalysisToolPak() { + // no instances of this class + } + + public static FreeRefFunction findFunction(String name) { + return (FreeRefFunction)_functionsByName.get(name); + } + + private static Map createFunctionsMap() { + Map m = new HashMap(100); + + r(m, "ACCRINT", null); + r(m, "ACCRINTM", null); + r(m, "AMORDEGRC", null); + r(m, "AMORLINC", null); + r(m, "BESSELI", null); + r(m, "BESSELJ", null); + r(m, "BESSELK", null); + r(m, "BESSELY", null); + r(m, "BIN2DEC", null); + r(m, "BIN2HEX", null); + r(m, "BIN2OCT", null); + r(m, "CO MPLEX", null); + r(m, "CONVERT", null); + r(m, "COUPDAYBS", null); + r(m, "COUPDAYS", null); + r(m, "COUPDAYSNC", null); + r(m, "COUPNCD", null); + r(m, "COUPNUM", null); + r(m, "COUPPCD", null); + r(m, "CUMIPMT", null); + r(m, "CUMPRINC", null); + r(m, "DEC2BIN", null); + r(m, "DEC2HEX", null); + r(m, "DEC2OCT", null); + r(m, "DELTA", null); + r(m, "DISC", null); + r(m, "DOLLARDE", null); + r(m, "DOLLARFR", null); + r(m, "DURATION", null); + r(m, "EDATE", null); + r(m, "EFFECT", null); + r(m, "EOMONTH", null); + r(m, "ERF", null); + r(m, "ERFC", null); + r(m, "FACTDOUBLE", null); + r(m, "FVSCHEDULE", null); + r(m, "GCD", null); + r(m, "GESTEP", null); + r(m, "HEX2BIN", null); + r(m, "HEX2DEC", null); + r(m, "HEX2OCT", null); + r(m, "IMABS", null); + r(m, "IMAGINARY", null); + r(m, "IMARGUMENT", null); + r(m, "IMCONJUGATE", null); + r(m, "IMCOS", null); + r(m, "IMDIV", null); + r(m, "IMEXP", null); + r(m, "IMLN", null); + r(m, "IMLOG10", null); + r(m, "IMLOG2", null); + r(m, "IMPOWER", null); + r(m, "IMPRODUCT", null); + r(m, "IMREAL", null); + r(m, "IMSIN", null); + r(m, "IMSQRT", null); + r(m, "IMSUB", null); + r(m, "IMSUM", null); + r(m, "INTRATE", null); + r(m, "ISEVEN", null); + r(m, "ISODD", null); + r(m, "LCM", null); + r(m, "MDURATION", null); + r(m, "MROUND", null); + r(m, "MULTINOMIAL", null); + r(m, "NETWORKDAYS", null); + r(m, "NOMINAL", null); + r(m, "OCT2BIN", null); + r(m, "OCT2DEC", null); + r(m, "OCT2HEX", null); + r(m, "ODDFPRICE", null); + r(m, "ODDFYIELD", null); + r(m, "ODDLPRICE", null); + r(m, "ODDLYIELD", null); + r(m, "PRICE", null); + r(m, "PRICEDISC", null); + r(m, "PRICEMAT", null); + r(m, "QUOTIENT", null); + r(m, "RAND BETWEEN", null); + r(m, "RECEIVED", null); + r(m, "SERIESSUM", null); + r(m, "SQRTPI", null); + r(m, "TBILLEQ", null); + r(m, "TBILLPRICE", null); + r(m, "TBILLYIELD", null); + r(m, "WEEKNUM", null); + r(m, "WORKDAY", null); + r(m, "XIRR", null); + r(m, "XNPV", null); + r(m, "YEARFRAC", YearFrac.instance); + r(m, "YIELD", null); + r(m, "YIELDDISC", null); + r(m, "YIELDMAT", null); + + return m; + } + + private static void r(Map m, String functionName, FreeRefFunction pFunc) { + FreeRefFunction func = pFunc == null ? NotImplemented : pFunc; + m.put(functionName, func); + } +} diff --git a/src/java/org/apache/poi/hssf/record/formula/atp/YearFrac.java b/src/java/org/apache/poi/hssf/record/formula/atp/YearFrac.java new file mode 100644 index 000000000..517189d40 --- /dev/null +++ b/src/java/org/apache/poi/hssf/record/formula/atp/YearFrac.java @@ -0,0 +1,160 @@ +/* ==================================================================== + 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.formula.atp; + +import java.util.Calendar; +import java.util.GregorianCalendar; +import java.util.regex.Pattern; + +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.NumberEval; +import org.apache.poi.hssf.record.formula.eval.OperandResolver; +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.FreeRefFunction; +import org.apache.poi.ss.usermodel.DateUtil; +import org.apache.poi.ss.usermodel.Sheet; +import org.apache.poi.ss.usermodel.Workbook; +/** + * Implementation of Excel 'Analysis ToolPak' function YEARFRAC()
+ * + * Returns the fraction of the year spanned by two dates.

+ * + * Syntax
+ * YEARFRAC(startDate, endDate, basis)

+ * + * The basis optionally specifies the behaviour of YEARFRAC as follows: + * + * + * + * + * + * + * + * + *
ValueDays per MonthDays per Year
0 (default)30360
1actualactual
2actual360
3actual365
430360
+ * + */ +final class YearFrac implements FreeRefFunction { + + public static final FreeRefFunction instance = new YearFrac(); + + private YearFrac() { + // enforce singleton + } + + public ValueEval evaluate(Eval[] args, int srcCellRow, short srcCellCol, Workbook workbook, + Sheet sheet) { + + double result; + try { + int basis = 0; // default + switch(args.length) { + case 3: + basis = evaluateIntArg(args[2], srcCellRow, srcCellCol); + case 2: + break; + default: + return ErrorEval.VALUE_INVALID; + } + double startDateVal = evaluateDateArg(args[0], srcCellRow, srcCellCol); + double endDateVal = evaluateDateArg(args[1], srcCellRow, srcCellCol); + result = YearFracCalculator.calculate(startDateVal, endDateVal, basis); + } catch (EvaluationException e) { + return e.getErrorEval(); + } + + return new NumberEval(result); + } + + private static double evaluateDateArg(Eval arg, int srcCellRow, short srcCellCol) throws EvaluationException { + ValueEval ve = OperandResolver.getSingleValue(arg, srcCellRow, srcCellCol); + + if (ve instanceof StringEval) { + String strVal = ((StringEval) ve).getStringValue(); + Double dVal = OperandResolver.parseDouble(strVal); + if (dVal != null) { + return dVal.doubleValue(); + } + Calendar date = parseDate(strVal); + return DateUtil.getExcelDate(date, false); + } + return OperandResolver.coerceValueToDouble(ve); + } + + private static Calendar parseDate(String strVal) throws EvaluationException { + String[] parts = Pattern.compile("/").split(strVal); + if (parts.length != 3) { + throw new EvaluationException(ErrorEval.VALUE_INVALID); + } + String part2 = parts[2]; + int spacePos = part2.indexOf(' '); + if (spacePos > 0) { + // drop time portion if present + part2 = part2.substring(0, spacePos); + } + int f0; + int f1; + int f2; + try { + f0 = Integer.parseInt(parts[0]); + f1 = Integer.parseInt(parts[1]); + f2 = Integer.parseInt(part2); + } catch (NumberFormatException e) { + throw new EvaluationException(ErrorEval.VALUE_INVALID); + } + if (f0<0 || f1<0 || f2<0 || f0>12 || f1>12 || f2>12) { + // easy to see this cannot be a valid date + throw new EvaluationException(ErrorEval.VALUE_INVALID); + } + + if (f0 >= 1900 && f0 < 9999) { + // when 4 digit value appears first, the format is YYYY/MM/DD, regardless of OS settings + return makeDate(f0, f1, f2); + } + // otherwise the format seems to depend on OS settings (default date format) + if (false) { + // MM/DD/YYYY is probably a good guess, if the in the US + return makeDate(f2, f0, f1); + } + // TODO - find a way to choose the correct date format + throw new RuntimeException("Unable to determine date format for text '" + strVal + "'"); + } + + /** + * @param month 1-based + */ + private static Calendar makeDate(int year, int month, int day) throws EvaluationException { + if (month < 1 || month > 12) { + throw new EvaluationException(ErrorEval.VALUE_INVALID); + } + Calendar cal = new GregorianCalendar(year, month-1, 1, 0, 0, 0); + cal.set(Calendar.MILLISECOND, 0); + if (day <1 || day>cal.getActualMaximum(Calendar.DAY_OF_MONTH)) { + throw new EvaluationException(ErrorEval.VALUE_INVALID); + } + return cal; + } + + private static int evaluateIntArg(Eval arg, int srcCellRow, short srcCellCol) throws EvaluationException { + ValueEval ve = OperandResolver.getSingleValue(arg, srcCellRow, srcCellCol); + return OperandResolver.coerceValueToInt(ve); + } +} diff --git a/src/java/org/apache/poi/hssf/record/formula/atp/YearFracCalculator.java b/src/java/org/apache/poi/hssf/record/formula/atp/YearFracCalculator.java new file mode 100644 index 000000000..40a5eb8db --- /dev/null +++ b/src/java/org/apache/poi/hssf/record/formula/atp/YearFracCalculator.java @@ -0,0 +1,344 @@ +/* ==================================================================== + 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.formula.atp; + +import java.util.Calendar; +import java.util.GregorianCalendar; +import java.util.TimeZone; + +import org.apache.poi.hssf.record.formula.eval.ErrorEval; +import org.apache.poi.hssf.record.formula.eval.EvaluationException; +import org.apache.poi.hssf.usermodel.HSSFDateUtil; + + +/** + * Internal calculation methods for Excel 'Analysis ToolPak' function YEARFRAC()
+ * + * Algorithm inspired by www.dwheeler.com/yearfrac + * + * @author Josh Micich + */ +final class YearFracCalculator { + /** use UTC time-zone to avoid daylight savings issues */ + private static final TimeZone UTC_TIME_ZONE = TimeZone.getTimeZone("UTC"); + private static final int MS_PER_HOUR = 60 * 60 * 1000; + private static final int MS_PER_DAY = 24 * MS_PER_HOUR; + private static final int DAYS_PER_NORMAL_YEAR = 365; + private static final int DAYS_PER_LEAP_YEAR = DAYS_PER_NORMAL_YEAR + 1; + + /** the length of normal long months i.e. 31 */ + private static final int LONG_MONTH_LEN = 31; + /** the length of normal short months i.e. 30 */ + private static final int SHORT_MONTH_LEN = 30; + private static final int SHORT_FEB_LEN = 28; + private static final int LONG_FEB_LEN = SHORT_FEB_LEN + 1; + + private YearFracCalculator() { + // no instances of this class + } + + + public static double calculate(double pStartDateVal, double pEndDateVal, int basis) throws EvaluationException { + + if (basis < 0 || basis >= 5) { + // if basis is invalid the result is #NUM! + throw new EvaluationException(ErrorEval.NUM_ERROR); + } + + // common logic for all bases + + // truncate day values + int startDateVal = (int) Math.floor(pStartDateVal); + int endDateVal = (int) Math.floor(pEndDateVal); + if (startDateVal == endDateVal) { + // when dates are equal, result is zero + return 0; + } + // swap start and end if out of order + if (startDateVal > endDateVal) { + int temp = startDateVal; + startDateVal = endDateVal; + endDateVal = temp; + } + + switch (basis) { + case 0: return basis0(startDateVal, endDateVal); + case 1: return basis1(startDateVal, endDateVal); + case 2: return basis2(startDateVal, endDateVal); + case 3: return basis3(startDateVal, endDateVal); + case 4: return basis4(startDateVal, endDateVal); + } + throw new IllegalStateException("cannot happen"); + } + + + /** + * @param startDateVal assumed to be less than or equal to endDateVal + * @param endDateVal assumed to be greater than or equal to startDateVal + */ + public static double basis0(int startDateVal, int endDateVal) { + SimpleDate startDate = createDate(startDateVal); + SimpleDate endDate = createDate(endDateVal); + int date1day = startDate.day; + int date2day = endDate.day; + + // basis zero has funny adjustments to the day-of-month fields when at end-of-month + if (date1day == LONG_MONTH_LEN && date2day == LONG_MONTH_LEN) { + date1day = SHORT_MONTH_LEN; + date2day = SHORT_MONTH_LEN; + } else if (date1day == LONG_MONTH_LEN) { + date1day = SHORT_MONTH_LEN; + } else if (date1day == SHORT_MONTH_LEN && date2day == LONG_MONTH_LEN) { + date2day = SHORT_MONTH_LEN; + // Note: If date2day==31, it STAYS 31 if date1day < 30. + // Special fixes for February: + } else if (startDate.month == 2 && isLastDayOfMonth(startDate)) { + // Note - these assignments deliberately set Feb 30 date. + date1day = SHORT_MONTH_LEN; + if (endDate.month == 2 && isLastDayOfMonth(endDate)) { + // only adjusted when first date is last day in Feb + date2day = SHORT_MONTH_LEN; + } + } + return calculateAdjusted(startDate, endDate, date1day, date2day); + } + /** + * @param startDateVal assumed to be less than or equal to endDateVal + * @param endDateVal assumed to be greater than or equal to startDateVal + */ + public static double basis1(int startDateVal, int endDateVal) { + SimpleDate startDate = createDate(startDateVal); + SimpleDate endDate = createDate(endDateVal); + double yearLength; + if (isGreaterThanOneYear(startDate, endDate)) { + yearLength = averageYearLength(startDate.year, endDate.year); + } else if (shouldCountFeb29(startDate, endDate)) { + yearLength = DAYS_PER_LEAP_YEAR; + } else { + yearLength = DAYS_PER_NORMAL_YEAR; + } + return dateDiff(startDate.tsMilliseconds, endDate.tsMilliseconds) / yearLength; + } + + /** + * @param startDateVal assumed to be less than or equal to endDateVal + * @param endDateVal assumed to be greater than or equal to startDateVal + */ + public static double basis2(int startDateVal, int endDateVal) { + return (endDateVal - startDateVal) / 360.0; + } + /** + * @param startDateVal assumed to be less than or equal to endDateVal + * @param endDateVal assumed to be greater than or equal to startDateVal + */ + public static double basis3(double startDateVal, double endDateVal) { + return (endDateVal - startDateVal) / 365.0; + } + /** + * @param startDateVal assumed to be less than or equal to endDateVal + * @param endDateVal assumed to be greater than or equal to startDateVal + */ + public static double basis4(int startDateVal, int endDateVal) { + SimpleDate startDate = createDate(startDateVal); + SimpleDate endDate = createDate(endDateVal); + int date1day = startDate.day; + int date2day = endDate.day; + + + // basis four has funny adjustments to the day-of-month fields when at end-of-month + if (date1day == LONG_MONTH_LEN) { + date1day = SHORT_MONTH_LEN; + } + if (date2day == LONG_MONTH_LEN) { + date2day = SHORT_MONTH_LEN; + } + // Note - no adjustments for end of Feb + return calculateAdjusted(startDate, endDate, date1day, date2day); + } + + + private static double calculateAdjusted(SimpleDate startDate, SimpleDate endDate, int date1day, + int date2day) { + double dayCount + = (endDate.year - startDate.year) * 360 + + (endDate.month - startDate.month) * SHORT_MONTH_LEN + + (date2day - date1day) * 1; + return dayCount / 360; + } + + private static boolean isLastDayOfMonth(SimpleDate date) { + if (date.day < SHORT_FEB_LEN) { + return false; + } + return date.day == getLastDayOfMonth(date); + } + + private static int getLastDayOfMonth(SimpleDate date) { + switch (date.month) { + case 1: + case 3: + case 5: + case 7: + case 8: + case 10: + case 12: + return LONG_MONTH_LEN; + case 4: + case 6: + case 9: + case 11: + return SHORT_MONTH_LEN; + } + if (isLeapYear(date.year)) { + return LONG_FEB_LEN; + } + return SHORT_FEB_LEN; + } + + /** + * Assumes dates are no more than 1 year apart. + * @return true if dates both within a leap year, or span a period including Feb 29 + */ + private static boolean shouldCountFeb29(SimpleDate start, SimpleDate end) { + boolean startIsLeapYear = isLeapYear(start.year); + if (startIsLeapYear && start.year == end.year) { + // note - dates may not actually span Feb-29, but it gets counted anyway in this case + return true; + } + + boolean endIsLeapYear = isLeapYear(end.year); + if (!startIsLeapYear && !endIsLeapYear) { + return false; + } + if (startIsLeapYear) { + switch (start.month) { + case SimpleDate.JANUARY: + case SimpleDate.FEBRUARY: + return true; + } + return false; + } + if (endIsLeapYear) { + switch (end.month) { + case SimpleDate.JANUARY: + return false; + case SimpleDate.FEBRUARY: + break; + default: + return true; + } + return end.day == LONG_FEB_LEN; + } + return false; + } + + /** + * @return the whole number of days between the two time-stamps. Both time-stamps are + * assumed to represent 12:00 midnight on the respective day. + */ + private static int dateDiff(long startDateMS, long endDateMS) { + long msDiff = endDateMS - startDateMS; + + // some extra checks to make sure we don't hide some other bug with the rounding + int remainderHours = (int) ((msDiff % MS_PER_DAY) / MS_PER_HOUR); + switch (remainderHours) { + case 0: // normal case + break; + case 1: // transition from normal time to daylight savings adjusted + case 23: // transition from daylight savings adjusted to normal time + // Unexpected since we are using UTC_TIME_ZONE + default: + throw new RuntimeException("Unexpected date diff between " + startDateMS + " and " + endDateMS); + + } + return (int) (0.5 + ((double)msDiff / MS_PER_DAY)); + } + + private static double averageYearLength(int startYear, int endYear) { + int dayCount = 0; + for (int i=startYear; i<=endYear; i++) { + dayCount += DAYS_PER_NORMAL_YEAR; + if (isLeapYear(i)) { + dayCount++; + } + } + double numberOfYears = endYear-startYear+1; + return dayCount / numberOfYears; + } + + private static boolean isLeapYear(int i) { + // leap years are always divisible by 4 + if (i % 4 != 0) { + return false; + } + // each 4th century is a leap year + if (i % 400 == 0) { + return true; + } + // all other centuries are *not* leap years + if (i % 100 == 0) { + return false; + } + return true; + } + + private static boolean isGreaterThanOneYear(SimpleDate start, SimpleDate end) { + if (start.year == end.year) { + return false; + } + if (start.year + 1 != end.year) { + return true; + } + + if (start.month > end.month) { + return false; + } + if (start.month < end.month) { + return true; + } + + return start.day < end.day; + } + + private static SimpleDate createDate(int dayCount) { + GregorianCalendar calendar = new GregorianCalendar(UTC_TIME_ZONE); + HSSFDateUtil.setCalendar(calendar, dayCount, 0, false); + return new SimpleDate(calendar); + } + + private static final class SimpleDate { + + public static final int JANUARY = 1; + public static final int FEBRUARY = 2; + + public final int year; + /** 1-based month */ + public final int month; + /** day of month */ + public final int day; + /** milliseconds since 1970 */ + public long tsMilliseconds; + + public SimpleDate(Calendar cal) { + year = cal.get(Calendar.YEAR); + month = cal.get(Calendar.MONTH) + 1; + day = cal.get(Calendar.DAY_OF_MONTH); + tsMilliseconds = cal.getTimeInMillis(); + } + } +} diff --git a/src/java/org/apache/poi/hssf/record/formula/eval/ExternalFunction.java b/src/java/org/apache/poi/hssf/record/formula/eval/ExternalFunction.java index f40c83202..ffaf780ab 100755 --- a/src/java/org/apache/poi/hssf/record/formula/eval/ExternalFunction.java +++ b/src/java/org/apache/poi/hssf/record/formula/eval/ExternalFunction.java @@ -17,14 +17,16 @@ package org.apache.poi.hssf.record.formula.eval; +import org.apache.poi.hssf.record.formula.atp.AnalysisToolPak; import org.apache.poi.hssf.record.formula.functions.FreeRefFunction; import org.apache.poi.ss.usermodel.Sheet; import org.apache.poi.ss.usermodel.Workbook; /** * - * Common entry point for all external functions (where + * Common entry point for all user-defined (non-built-in) functions (where * AbstractFunctionPtg.field_2_fnc_index == 255) * + * TODO rename to UserDefinedFunction * @author Josh Micich */ final class ExternalFunction implements FreeRefFunction { @@ -36,27 +38,43 @@ final class ExternalFunction implements FreeRefFunction { throw new RuntimeException("function name argument missing"); } - if (!(args[0] instanceof NameEval)) { - throw new RuntimeException("First argument should be a NameEval, but got (" - + args[0].getClass().getName() + ")"); - } - NameEval functionNameEval = (NameEval) args[0]; - - int nOutGoingArgs = nIncomingArgs -1; - Eval[] outGoingArgs = new Eval[nOutGoingArgs]; - System.arraycopy(args, 1, outGoingArgs, 0, nOutGoingArgs); - + Eval nameArg = args[0]; FreeRefFunction targetFunc; try { - targetFunc = findTargetFunction(workbook, functionNameEval); + if (nameArg instanceof NameEval) { + targetFunc = findInternalUserDefinedFunction(workbook, (NameEval) nameArg); + } else if (nameArg instanceof NameXEval) { + targetFunc = findExternalUserDefinedFunction(workbook, (NameXEval) nameArg); + } else { + throw new RuntimeException("First argument should be a NameEval, but got (" + + nameArg.getClass().getName() + ")"); + } } catch (EvaluationException e) { return e.getErrorEval(); } - + int nOutGoingArgs = nIncomingArgs -1; + Eval[] outGoingArgs = new Eval[nOutGoingArgs]; + System.arraycopy(args, 1, outGoingArgs, 0, nOutGoingArgs); return targetFunc.evaluate(outGoingArgs, srcCellRow, srcCellCol, workbook, sheet); } - private FreeRefFunction findTargetFunction(Workbook workbook, NameEval functionNameEval) throws EvaluationException { + private FreeRefFunction findExternalUserDefinedFunction(Workbook workbook, + NameXEval n) throws EvaluationException { + String functionName = workbook.resolveNameXText(n.getSheetRefIndex(), n.getNameNumber()); + + if(false) { + System.out.println("received call to external user defined function (" + functionName + ")"); + } + // currently only looking for functions from the 'Analysis TookPak' + // not sure how much this logic would need to change to support other or multiple add-ins. + FreeRefFunction result = AnalysisToolPak.findFunction(functionName); + if (result != null) { + return result; + } + throw new EvaluationException(ErrorEval.FUNCTION_NOT_IMPLEMENTED); + } + + private FreeRefFunction findInternalUserDefinedFunction(Workbook workbook, NameEval functionNameEval) throws EvaluationException { int numberOfNames = workbook.getNumberOfNames(); @@ -68,7 +86,7 @@ final class ExternalFunction implements FreeRefFunction { String functionName = workbook.getNameName(nameIndex); if(false) { - System.out.println("received call to external function index (" + functionName + ")"); + System.out.println("received call to internal user defined function (" + functionName + ")"); } // TODO - detect if the NameRecord corresponds to a named range, function, or something undefined // throw the right errors in these cases @@ -77,5 +95,5 @@ final class ExternalFunction implements FreeRefFunction { throw new EvaluationException(ErrorEval.FUNCTION_NOT_IMPLEMENTED); } - } + diff --git a/src/java/org/apache/poi/hssf/record/formula/eval/NameXEval.java b/src/java/org/apache/poi/hssf/record/formula/eval/NameXEval.java new file mode 100644 index 000000000..12b6be380 --- /dev/null +++ b/src/java/org/apache/poi/hssf/record/formula/eval/NameXEval.java @@ -0,0 +1,49 @@ +/* ==================================================================== + 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.formula.eval; + +/** + * @author Josh Micich + */ +public final class NameXEval implements Eval { + + /** index to REF entry in externsheet record */ + private final int _sheetRefIndex; + /** index to defined name or externname table(1 based) */ + private final int _nameNumber; + + public NameXEval(int sheetRefIndex, int nameNumber) { + _sheetRefIndex = sheetRefIndex; + _nameNumber = nameNumber; + } + + public int getSheetRefIndex() { + return _sheetRefIndex; + } + public int getNameNumber() { + return _nameNumber; + } + + public String toString() { + StringBuffer sb = new StringBuffer(64); + sb.append(getClass().getName()).append(" ["); + sb.append(_sheetRefIndex).append(", ").append(_nameNumber); + sb.append("]"); + return sb.toString(); + } +} diff --git a/src/java/org/apache/poi/hssf/usermodel/HSSFConditionalFormattingRule.java b/src/java/org/apache/poi/hssf/usermodel/HSSFConditionalFormattingRule.java index 10d86015a..fc34b9da9 100644 --- a/src/java/org/apache/poi/hssf/usermodel/HSSFConditionalFormattingRule.java +++ b/src/java/org/apache/poi/hssf/usermodel/HSSFConditionalFormattingRule.java @@ -41,6 +41,12 @@ public final class HSSFConditionalFormattingRule private final HSSFWorkbook workbook; HSSFConditionalFormattingRule(HSSFWorkbook pWorkbook, CFRuleRecord pRuleRecord) { + if (pWorkbook == null) { + throw new IllegalArgumentException("pWorkbook must not be null"); + } + if (pRuleRecord == null) { + throw new IllegalArgumentException("pRuleRecord must not be null"); + } workbook = pWorkbook; cfRuleRecord = pRuleRecord; } diff --git a/src/java/org/apache/poi/hssf/usermodel/HSSFRow.java b/src/java/org/apache/poi/hssf/usermodel/HSSFRow.java index dff82a732..4e351e6ad 100644 --- a/src/java/org/apache/poi/hssf/usermodel/HSSFRow.java +++ b/src/java/org/apache/poi/hssf/usermodel/HSSFRow.java @@ -349,7 +349,7 @@ public final class HSSFRow implements Comparable, Row { } if(policy == CREATE_NULL_AS_BLANK) { if(cell == null) { - return createCell((short)cellnum, HSSFCell.CELL_TYPE_BLANK); + return createCell(cellnum, HSSFCell.CELL_TYPE_BLANK); } return cell; } diff --git a/src/java/org/apache/poi/hssf/usermodel/HSSFSheet.java b/src/java/org/apache/poi/hssf/usermodel/HSSFSheet.java index 3e51fe831..280284439 100644 --- a/src/java/org/apache/poi/hssf/usermodel/HSSFSheet.java +++ b/src/java/org/apache/poi/hssf/usermodel/HSSFSheet.java @@ -1836,7 +1836,7 @@ public class HSSFSheet implements org.apache.poi.ss.usermodel.Sheet // new HSSFComment instances, which is bad HSSFRow r = getRow(row); if(r != null) { - HSSFCell c = r.getCell((short)column); + HSSFCell c = r.getCell(column); if(c != null) { return c.getCellComment(); } else { diff --git a/src/java/org/apache/poi/ss/usermodel/DateUtil.java b/src/java/org/apache/poi/ss/usermodel/DateUtil.java index 002b4aedc..d45748909 100644 --- a/src/java/org/apache/poi/ss/usermodel/DateUtil.java +++ b/src/java/org/apache/poi/ss/usermodel/DateUtil.java @@ -158,9 +158,16 @@ public class DateUtil { if (!isValidExcelDate(date)) { return null; } + int wholeDays = (int)Math.floor(date); + int millisecondsInDay = (int)((date - wholeDays) * DAY_MILLISECONDS + 0.5); + Calendar calendar = new GregorianCalendar(); // using default time-zone + setCalendar(calendar, wholeDays, millisecondsInDay, use1904windowing); + return calendar.getTime(); + } + public static void setCalendar(Calendar calendar, int wholeDays, + int millisecondsInDay, boolean use1904windowing) { int startYear = 1900; int dayAdjust = -1; // Excel thinks 2/29/1900 is a valid date, which it isn't - int wholeDays = (int)Math.floor(date); if (use1904windowing) { startYear = 1904; dayAdjust = 1; // 1904 date windowing uses 1/2/1904 as the first day @@ -170,12 +177,8 @@ public class DateUtil { // If Excel date == 2/29/1900, will become 3/1/1900 in Java representation dayAdjust = 0; } - GregorianCalendar calendar = new GregorianCalendar(startYear,0, - wholeDays + dayAdjust); - int millisecondsInDay = (int)((date - Math.floor(date)) * - DAY_MILLISECONDS + 0.5); + calendar.set(startYear,0, wholeDays + dayAdjust, 0, 0, 0); calendar.set(GregorianCalendar.MILLISECOND, millisecondsInDay); - return calendar.getTime(); } /** diff --git a/src/java/org/apache/poi/ss/usermodel/FormulaEvaluator.java b/src/java/org/apache/poi/ss/usermodel/FormulaEvaluator.java index 22321ff9e..9cd7f918e 100644 --- a/src/java/org/apache/poi/ss/usermodel/FormulaEvaluator.java +++ b/src/java/org/apache/poi/ss/usermodel/FormulaEvaluator.java @@ -50,6 +50,7 @@ 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.FunctionEval; import org.apache.poi.hssf.record.formula.eval.NameEval; +import org.apache.poi.hssf.record.formula.eval.NameXEval; import org.apache.poi.hssf.record.formula.eval.NumberEval; import org.apache.poi.hssf.record.formula.eval.OperationEval; import org.apache.poi.hssf.record.formula.eval.Ref2DEval; @@ -348,7 +349,8 @@ public class FormulaEvaluator { continue; } if (ptg instanceof NameXPtg) { - // TODO - external functions + NameXPtg nameXPtg = (NameXPtg) ptg; + stack.push(new NameXEval(nameXPtg.getSheetRefIndex(), nameXPtg.getNameIndex())); continue; } if (ptg instanceof UnknownPtg) { continue; } diff --git a/src/scratchpad/src/org/apache/poi/hpbf/HPBFDocument.java b/src/scratchpad/src/org/apache/poi/hpbf/HPBFDocument.java new file mode 100644 index 000000000..49de0d087 --- /dev/null +++ b/src/scratchpad/src/org/apache/poi/hpbf/HPBFDocument.java @@ -0,0 +1,86 @@ +/* ==================================================================== + 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.hpbf; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; + +import org.apache.poi.POIDocument; +import org.apache.poi.hpbf.model.EscherDelayStm; +import org.apache.poi.hpbf.model.EscherStm; +import org.apache.poi.hpbf.model.MainContents; +import org.apache.poi.hpbf.model.QuillContents; +import org.apache.poi.poifs.filesystem.DirectoryNode; +import org.apache.poi.poifs.filesystem.POIFSFileSystem; + +/** + * This class provides the basic functionality + * for HPBF, our implementation of the publisher + * file format. + */ +public final class HPBFDocument extends POIDocument { + private MainContents mainContents; + private QuillContents quillContents; + private EscherStm escherStm; + private EscherDelayStm escherDelayStm; + + /** + * Opens a new publisher document + */ + public HPBFDocument(POIFSFileSystem fs) throws IOException { + this(fs.getRoot(), fs); + } + public HPBFDocument(InputStream inp) throws IOException { + this(new POIFSFileSystem(inp)); + } + + /** + * Opens an embeded publisher document, + * at the given directory. + */ + public HPBFDocument(DirectoryNode dir, POIFSFileSystem fs) throws IOException { + super(dir, fs); + + // Go looking for our interesting child + // streams + mainContents = new MainContents(dir); + quillContents = new QuillContents(dir); + + // Now the Escher bits + escherStm = new EscherStm(dir); + escherDelayStm = new EscherDelayStm(dir); + } + + public MainContents getMainContents() { + return mainContents; + } + public QuillContents getQuillContents() { + return quillContents; + } + public EscherStm getEscherStm() { + return escherStm; + } + public EscherDelayStm getEscherDelayStm() { + return escherDelayStm; + } + + public void write(OutputStream out) throws IOException { + throw new IllegalStateException("Writing is not yet implemented, see http://poi.apache.org/hpbf/"); + } +} diff --git a/src/scratchpad/src/org/apache/poi/hpbf/extractor/PublisherTextExtractor.java b/src/scratchpad/src/org/apache/poi/hpbf/extractor/PublisherTextExtractor.java new file mode 100644 index 000000000..2257283a0 --- /dev/null +++ b/src/scratchpad/src/org/apache/poi/hpbf/extractor/PublisherTextExtractor.java @@ -0,0 +1,78 @@ +/* ==================================================================== + 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.hpbf.extractor; + +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; + +import org.apache.poi.POIOLE2TextExtractor; +import org.apache.poi.hpbf.HPBFDocument; +import org.apache.poi.hpbf.model.qcbits.QCBit; +import org.apache.poi.hpbf.model.qcbits.QCTextBit; +import org.apache.poi.poifs.filesystem.POIFSFileSystem; + +/** + * Extract text from HPBF Publisher files + */ +public class PublisherTextExtractor extends POIOLE2TextExtractor { + private HPBFDocument doc; + + public PublisherTextExtractor(HPBFDocument doc) { + super(doc); + this.doc = doc; + } + public PublisherTextExtractor(POIFSFileSystem fs) throws IOException { + this(new HPBFDocument(fs)); + } + public PublisherTextExtractor(InputStream is) throws IOException { + this(new POIFSFileSystem(is)); + } + + public String getText() { + StringBuffer text = new StringBuffer(); + + // Get the text from the Quill Contents + QCBit[] bits = doc.getQuillContents().getBits(); + for(int i=0; i"); + } + + for(int i=0; i 0) { + EscherRecord er = erf.createRecord(data, 0); + er.fillFields(data, 0, erf); + left -= er.getRecordSize(); + + ec.add(er); + } + + records = (EscherRecord[]) + ec.toArray(new EscherRecord[ec.size()]); + } + + public EscherRecord[] getEscherRecords() { + return records; + } + + /** + * Serialises our Escher children back + * into bytes. + */ + protected void generateData() { + int size = 0; + for(int i=0; i QuillSub -> CONTENTS + */ +public final class QuillContents extends HPBFPart { + private static final String[] PATH = { "Quill", "QuillSub", "CONTENTS", }; + private QCBit[] bits; + + public QuillContents(DirectoryNode baseDir) throws IOException { + super(baseDir, PATH); + + // Now parse the first 512 bytes, and produce + // all our bits + + // Check first 8 bytes + String f8 = new String(data, 0, 8); + if(! f8.equals("CHNKINK ")) { + throw new IllegalArgumentException("Expecting 'CHNKINK ' but was '"+f8+"'"); + } + // Ignore the next 24, for now at least + + // Now, parse all our QC Bits + bits = new QCBit[20]; + for(int i=0; i<20; i++) { + int offset = 0x20 + i*24; + if(data[offset] == 0x18 && data[offset+1] == 0x00) { + // Has some data + String thingType = new String(data, offset+2, 4); + int optA = LittleEndian.getUShort(data, offset+6); + int optB = LittleEndian.getUShort(data, offset+8); + int optC = LittleEndian.getUShort(data, offset+10); + String bitType = new String(data, offset+12, 4); + int from = (int)LittleEndian.getUInt(data, offset+16); + int len = (int)LittleEndian.getUInt(data, offset+20); + + byte[] bitData = new byte[len]; + System.arraycopy(data, from, bitData, 0, len); + + // Create + if(bitType.equals("TEXT")) { + bits[i] = new QCTextBit(thingType, bitType, bitData); + } else { + bits[i] = new UnknownQCBit(thingType, bitType, bitData); + } + bits[i].setOptA(optA); + bits[i].setOptB(optB); + bits[i].setOptC(optC); + } else { + // Doesn't have data + } + } + } + + public QCBit[] getBits() { + return bits; + } + + protected void generateData() { + // TODO + throw new IllegalStateException("Not done yet!"); + } +} diff --git a/src/scratchpad/src/org/apache/poi/hpbf/model/qcbits/QCBit.java b/src/scratchpad/src/org/apache/poi/hpbf/model/qcbits/QCBit.java new file mode 100644 index 000000000..61c7955f6 --- /dev/null +++ b/src/scratchpad/src/org/apache/poi/hpbf/model/qcbits/QCBit.java @@ -0,0 +1,69 @@ +/* ==================================================================== + 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.hpbf.model.qcbits; + +/** + * Parent of all Quill CONTENTS bits + */ +public abstract class QCBit { + protected String thingType; + protected String bitType; + protected byte[] data; + + protected int optA; + protected int optB; + protected int optC; + + public QCBit(String thingType, String bitType, byte[] data) { + this.thingType = thingType; + this.bitType = bitType; + this.data = data; + } + + /** + * Returns the type of the thing, eg TEXT, FONT + * or TOKN + */ + public String getThingType() { return thingType; } + /** + * Returns the type of the bit data, eg TEXT + * or PLC + */ + public String getBitType() { return bitType; } + public byte[] getData() { return data; } + + public int getOptA() { + return optA; + } + public void setOptA(int optA) { + this.optA = optA; + } + + public int getOptB() { + return optB; + } + public void setOptB(int optB) { + this.optB = optB; + } + + public int getOptC() { + return optC; + } + public void setOptC(int optC) { + this.optC = optC; + } +} diff --git a/src/scratchpad/src/org/apache/poi/hpbf/model/qcbits/QCTextBit.java b/src/scratchpad/src/org/apache/poi/hpbf/model/qcbits/QCTextBit.java new file mode 100644 index 000000000..e3c8dcb58 --- /dev/null +++ b/src/scratchpad/src/org/apache/poi/hpbf/model/qcbits/QCTextBit.java @@ -0,0 +1,43 @@ +/* ==================================================================== + 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.hpbf.model.qcbits; + +import org.apache.poi.util.StringUtil; + +/** + * A Text based bit of Quill Contents + */ +public class QCTextBit extends QCBit { + public QCTextBit(String thingType, String bitType, byte[] data) { + super(thingType, bitType, data); + } + + /** + * Returns the text. Note that line endings + * are \r and not \n + */ + public String getText() { + return StringUtil.getFromUnicodeLE( + data, 0, data.length/2 + ); + } + + public void setText(String text) { + data = new byte[text.length()*2]; + StringUtil.putUnicodeLE(text, data, 0); + } +} diff --git a/src/scratchpad/src/org/apache/poi/hpbf/model/qcbits/UnknownQCBit.java b/src/scratchpad/src/org/apache/poi/hpbf/model/qcbits/UnknownQCBit.java new file mode 100644 index 000000000..2548b4fca --- /dev/null +++ b/src/scratchpad/src/org/apache/poi/hpbf/model/qcbits/UnknownQCBit.java @@ -0,0 +1,27 @@ +/* ==================================================================== + 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.hpbf.model.qcbits; + +/** + * Any Quill Contents bits we don't know + * how to handle explicitly + */ +public class UnknownQCBit extends QCBit { + public UnknownQCBit(String thingType, String bitType, byte[] data) { + super(thingType, bitType, data); + } +} diff --git a/src/scratchpad/testcases/org/apache/poi/hpbf/TestHPBFDocument.java b/src/scratchpad/testcases/org/apache/poi/hpbf/TestHPBFDocument.java new file mode 100644 index 000000000..c0186dd8d --- /dev/null +++ b/src/scratchpad/testcases/org/apache/poi/hpbf/TestHPBFDocument.java @@ -0,0 +1,60 @@ +/* ==================================================================== + 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.hpbf; + +import java.io.File; +import java.io.FileInputStream; + +import junit.framework.TestCase; + +public class TestHPBFDocument extends TestCase { + private String dir; + + protected void setUp() throws Exception { + dir = System.getProperty("HPBF.testdata.path"); + } + + public void testOpen() throws Exception { + File f = new File(dir, "Sample.pub"); + HPBFDocument doc = new HPBFDocument( + new FileInputStream(f) + ); + + assertNotNull(doc); + } + + public void testBits() throws Exception { + File f = new File(dir, "Sample.pub"); + HPBFDocument doc = new HPBFDocument( + new FileInputStream(f) + ); + + assertNotNull(doc.getMainContents()); + assertNotNull(doc.getQuillContents()); + assertNotNull(doc.getEscherStm()); + assertNotNull(doc.getEscherDelayStm()); + + assertTrue(doc.getMainContents().getData().length > 0); + assertTrue(doc.getQuillContents().getData().length > 0); + assertTrue(doc.getEscherStm().getData().length > 0); + assertTrue(doc.getEscherDelayStm().getData().length == 0); + } + + // TODO +// public void testWrite() throws Exception { +// } +} diff --git a/src/scratchpad/testcases/org/apache/poi/hpbf/data/Simple.pub b/src/scratchpad/testcases/org/apache/poi/hpbf/data/Simple.pub new file mode 100755 index 000000000..2397b9d01 Binary files /dev/null and b/src/scratchpad/testcases/org/apache/poi/hpbf/data/Simple.pub differ diff --git a/src/scratchpad/testcases/org/apache/poi/hpbf/extractor/TextPublisherTextExtractor.java b/src/scratchpad/testcases/org/apache/poi/hpbf/extractor/TextPublisherTextExtractor.java new file mode 100644 index 000000000..96396e107 --- /dev/null +++ b/src/scratchpad/testcases/org/apache/poi/hpbf/extractor/TextPublisherTextExtractor.java @@ -0,0 +1,105 @@ +/* ==================================================================== + 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.hpbf.extractor; + +import java.io.File; +import java.io.FileInputStream; + +import org.apache.poi.hpbf.HPBFDocument; + +import junit.framework.TestCase; + +public class TextPublisherTextExtractor extends TestCase { + private String dir; + + protected void setUp() throws Exception { + dir = System.getProperty("HPBF.testdata.path"); + } + + public void testBasics() throws Exception { + File f = new File(dir, "Sample.pub"); + HPBFDocument doc = new HPBFDocument( + new FileInputStream(f) + ); + + PublisherTextExtractor ext = + new PublisherTextExtractor(doc); + ext.getText(); + + f = new File(dir, "Simple.pub"); + ext = new PublisherTextExtractor( + new FileInputStream(f) + ); + ext.getText(); + } + + public void testContents() throws Exception { + File f = new File(dir, "Sample.pub"); + HPBFDocument doc = new HPBFDocument( + new FileInputStream(f) + ); + + PublisherTextExtractor ext = + new PublisherTextExtractor(doc); + String text = ext.getText(); + + assertEquals( +"This is some text on the first page\n" + +"It’s in times new roman, font size 10, all normal\n" + +"" + +"This is in bold and italic\n" + +"It’s Arial, 20 point font\n" + +"It’s in the second textbox on the first page\n" + +"" + +"This is the second page\n\n" + +"" + +"It is also times new roman, 10 point\n" + +"" + +"Table on page 2\nTop right\n" + +"P2 table left\nP2 table right\n" + +"Bottom Left\nBottom Right\n" + +"" + +"This text is on page two\n" + +"#This is a link to Apache POI\n" + +"More normal text\n" + +"Link to a file\n" + +"" + +"More text, more hyperlinks\n" + +"email link\n" + +"Final hyperlink\n" + +"Within doc to page 1\n" + , text + ); + + // Now a simpler one + f = new File(dir, "Simple.pub"); + ext = new PublisherTextExtractor( + new FileInputStream(f) + ); + text = ext.getText(); + assertEquals( +"0123456789\n" + +"0123456789abcdef\n" + +"0123456789abcdef0123456789abcdef\n" + +"0123456789\n" + +"0123456789abcdef\n" + +"0123456789abcdef0123456789abcdef\n" + +"0123456789abcdef0123456789abcdef0123456789abcdef\n" + , text + ); + } +} diff --git a/src/scratchpad/testcases/org/apache/poi/hpbf/model/TestEscherParts.java b/src/scratchpad/testcases/org/apache/poi/hpbf/model/TestEscherParts.java new file mode 100644 index 000000000..dbaf46c64 --- /dev/null +++ b/src/scratchpad/testcases/org/apache/poi/hpbf/model/TestEscherParts.java @@ -0,0 +1,50 @@ +/* ==================================================================== + 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.hpbf.model; + +import java.io.File; +import java.io.FileInputStream; + +import org.apache.poi.hpbf.HPBFDocument; + +import junit.framework.TestCase; + +public class TestEscherParts extends TestCase { + private String dir; + + protected void setUp() throws Exception { + dir = System.getProperty("HPBF.testdata.path"); + } + + public void testBasics() throws Exception { + File f = new File(dir, "Sample.pub"); + HPBFDocument doc = new HPBFDocument( + new FileInputStream(f) + ); + + EscherStm es = doc.getEscherStm(); + EscherDelayStm eds = doc.getEscherDelayStm(); + + assertNotNull(es); + assertNotNull(eds); + + assertEquals(13, es.getEscherRecords().length); + assertEquals(0, eds.getEscherRecords().length); + + // TODO - check the contents + } +} diff --git a/src/scratchpad/testcases/org/apache/poi/hpbf/model/TestQuillContents.java b/src/scratchpad/testcases/org/apache/poi/hpbf/model/TestQuillContents.java new file mode 100644 index 000000000..ce6ddf83e --- /dev/null +++ b/src/scratchpad/testcases/org/apache/poi/hpbf/model/TestQuillContents.java @@ -0,0 +1,80 @@ +/* ==================================================================== + 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.hpbf.model; + +import java.io.File; +import java.io.FileInputStream; + +import org.apache.poi.hpbf.HPBFDocument; +import org.apache.poi.hpbf.model.qcbits.QCTextBit; + +import junit.framework.TestCase; + +public class TestQuillContents extends TestCase { + private String dir; + + protected void setUp() throws Exception { + dir = System.getProperty("HPBF.testdata.path"); + } + + public void testBasics() throws Exception { + File f = new File(dir, "Sample.pub"); + HPBFDocument doc = new HPBFDocument( + new FileInputStream(f) + ); + + QuillContents qc = doc.getQuillContents(); + assertEquals(20, qc.getBits().length); + for(int i=0; i<19; i++) { + assertNotNull(qc.getBits()[i]); + } + // Last one is blank + assertNull(qc.getBits()[19]); + + // Should be text, then three STSHs + assertEquals("TEXT", qc.getBits()[0].getThingType()); + assertEquals("TEXT", qc.getBits()[0].getBitType()); + assertEquals(0, qc.getBits()[0].getOptA()); + + assertEquals("STSH", qc.getBits()[1].getThingType()); + assertEquals("STSH", qc.getBits()[1].getBitType()); + assertEquals(0, qc.getBits()[1].getOptA()); + + assertEquals("STSH", qc.getBits()[2].getThingType()); + assertEquals("STSH", qc.getBits()[2].getBitType()); + assertEquals(1, qc.getBits()[2].getOptA()); + + assertEquals("STSH", qc.getBits()[3].getThingType()); + assertEquals("STSH", qc.getBits()[3].getBitType()); + assertEquals(2, qc.getBits()[3].getOptA()); + } + + public void testText() throws Exception { + File f = new File(dir, "Sample.pub"); + HPBFDocument doc = new HPBFDocument( + new FileInputStream(f) + ); + + QuillContents qc = doc.getQuillContents(); + assertEquals(20, qc.getBits().length); + + QCTextBit text = (QCTextBit)qc.getBits()[0]; + String t = text.getText(); + assertTrue(t.startsWith("This is some text on the first page")); + assertTrue(t.endsWith("Within doc to page 1\r")); + } +} diff --git a/src/testcases/org/apache/poi/hssf/data/ex45672.xls b/src/testcases/org/apache/poi/hssf/data/ex45672.xls new file mode 100644 index 000000000..fbf48e8a2 Binary files /dev/null and b/src/testcases/org/apache/poi/hssf/data/ex45672.xls differ diff --git a/src/testcases/org/apache/poi/hssf/data/yearfracExamples.xls b/src/testcases/org/apache/poi/hssf/data/yearfracExamples.xls new file mode 100644 index 000000000..2b2be7d7d Binary files /dev/null and b/src/testcases/org/apache/poi/hssf/data/yearfracExamples.xls differ diff --git a/src/testcases/org/apache/poi/hssf/eventmodel/TestModelFactory.java b/src/testcases/org/apache/poi/hssf/eventmodel/TestModelFactory.java index 2d3759994..14e3c2c56 100644 --- a/src/testcases/org/apache/poi/hssf/eventmodel/TestModelFactory.java +++ b/src/testcases/org/apache/poi/hssf/eventmodel/TestModelFactory.java @@ -139,7 +139,7 @@ public class TestModelFactory extends TestCase ByteArrayOutputStream stream = new ByteArrayOutputStream(); HSSFSheet sheet = book.createSheet("Test"); HSSFRow row = sheet.createRow(0); - HSSFCell cell = row.createCell((short)0); + HSSFCell cell = row.createCell(0); cell.setCellValue(10.5); book.write(stream); return stream; diff --git a/src/testcases/org/apache/poi/hssf/eventusermodel/TestMissingRecordAwareHSSFListener.java b/src/testcases/org/apache/poi/hssf/eventusermodel/TestMissingRecordAwareHSSFListener.java index 37e594940..aa4bbcc6f 100644 --- a/src/testcases/org/apache/poi/hssf/eventusermodel/TestMissingRecordAwareHSSFListener.java +++ b/src/testcases/org/apache/poi/hssf/eventusermodel/TestMissingRecordAwareHSSFListener.java @@ -21,6 +21,7 @@ import java.io.InputStream; import java.util.ArrayList; import java.util.List; +import junit.framework.AssertionFailedError; import junit.framework.TestCase; import org.apache.poi.hssf.HSSFTestDataSamples; @@ -31,6 +32,7 @@ import org.apache.poi.hssf.record.BOFRecord; import org.apache.poi.hssf.record.LabelSSTRecord; import org.apache.poi.hssf.record.Record; import org.apache.poi.hssf.record.RowRecord; +import org.apache.poi.hssf.record.SharedFormulaRecord; import org.apache.poi.poifs.filesystem.POIFSFileSystem; /** * Tests for MissingRecordAwareHSSFListener @@ -39,44 +41,29 @@ public final class TestMissingRecordAwareHSSFListener extends TestCase { private Record[] r; + private void readRecords(String sampleFileName) { + HSSFRequest req = new HSSFRequest(); + MockHSSFListener mockListen = new MockHSSFListener(); + MissingRecordAwareHSSFListener listener = new MissingRecordAwareHSSFListener(mockListen); + req.addListenerForAllRecords(listener); + + HSSFEventFactory factory = new HSSFEventFactory(); + try { + InputStream is = HSSFTestDataSamples.openSampleFileStream(sampleFileName); + POIFSFileSystem fs = new POIFSFileSystem(is); + factory.processWorkbookEvents(req, fs); + } catch (IOException e) { + throw new RuntimeException(e); + } + + r = mockListen.getRecords(); + assertTrue(r.length > 100); + } public void openNormal() { - HSSFRequest req = new HSSFRequest(); - MockHSSFListener mockListen = new MockHSSFListener(); - MissingRecordAwareHSSFListener listener = new MissingRecordAwareHSSFListener(mockListen); - req.addListenerForAllRecords(listener); - - HSSFEventFactory factory = new HSSFEventFactory(); - try { - InputStream is = HSSFTestDataSamples.openSampleFileStream("MissingBits.xls"); - POIFSFileSystem fs = new POIFSFileSystem(is); - factory.processWorkbookEvents(req, fs); - } catch (IOException e) { - throw new RuntimeException(e); - } - - r = mockListen.getRecords(); - assertTrue(r.length > 100); - } - public void openAlt() { - HSSFRequest req = new HSSFRequest(); - MockHSSFListener mockListen = new MockHSSFListener(); - MissingRecordAwareHSSFListener listener = new MissingRecordAwareHSSFListener(mockListen); - req.addListenerForAllRecords(listener); - - HSSFEventFactory factory = new HSSFEventFactory(); - try { - InputStream is = HSSFTestDataSamples.openSampleFileStream("MRExtraLines.xls"); - POIFSFileSystem fs = new POIFSFileSystem(is); - factory.processWorkbookEvents(req, fs); - } catch (IOException e) { - throw new RuntimeException(e); - } - - r = mockListen.getRecords(); - assertTrue(r.length > 100); - } + readRecords("MissingBits.xls"); + } - public void testMissingRowRecords() throws Exception { + public void testMissingRowRecords() { openNormal(); // We have rows 0, 1, 2, 20 and 21 @@ -126,7 +113,7 @@ public final class TestMissingRecordAwareHSSFListener extends TestCase { assertEquals(19, mr.getRowNumber()); } - public void testEndOfRowRecords() throws Exception { + public void testEndOfRowRecords() { openNormal(); // Find the cell at 0,0 @@ -248,7 +235,7 @@ public final class TestMissingRecordAwareHSSFListener extends TestCase { } - public void testMissingCellRecords() throws Exception { + public void testMissingCellRecords() { openNormal(); // Find the cell at 0,0 @@ -350,29 +337,21 @@ public final class TestMissingRecordAwareHSSFListener extends TestCase { // Make sure we don't put in any extra new lines // that aren't already there - public void testNoExtraNewLines() throws Exception { + public void testNoExtraNewLines() { // Load a different file - openAlt(); - - // This file has has something in lines 1-33 - List lcor = new ArrayList(); + readRecords("MRExtraLines.xls"); + + int rowCount=0; for(int i=0; i 0.000000001) { + double hours = diff * 365 * 24; + System.out.println(startDate + " " + endDate + " off by " + hours + " hours"); + assertEquals(expectedValue, actualValue, 0.000000001); + } + + } + + private static double md(int year, int month, int day) { + Calendar c = new GregorianCalendar(); + + c.set(year, month-1, day, 0, 0, 0); + c.set(Calendar.MILLISECOND, 0); + return HSSFDateUtil.getExcelDate(c.getTime()); + } +} diff --git a/src/testcases/org/apache/poi/hssf/record/formula/atp/TestYearFracCalculatorFromSpreadsheet.java b/src/testcases/org/apache/poi/hssf/record/formula/atp/TestYearFracCalculatorFromSpreadsheet.java new file mode 100644 index 000000000..2cad8e362 --- /dev/null +++ b/src/testcases/org/apache/poi/hssf/record/formula/atp/TestYearFracCalculatorFromSpreadsheet.java @@ -0,0 +1,178 @@ +/* ==================================================================== + 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.formula.atp; + +import java.io.PrintStream; +import java.util.Calendar; +import java.util.GregorianCalendar; +import java.util.Iterator; + +import junit.framework.Assert; +import junit.framework.AssertionFailedError; +import junit.framework.ComparisonFailure; +import junit.framework.TestCase; + +import org.apache.poi.hssf.HSSFTestDataSamples; +import org.apache.poi.hssf.record.formula.eval.EvaluationException; +import org.apache.poi.hssf.usermodel.HSSFCell; +import org.apache.poi.hssf.usermodel.HSSFDateUtil; +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; + +/** + * Tests YearFracCalculator using test-cases listed in a sample spreadsheet + * + * @author Josh Micich + */ +public final class TestYearFracCalculatorFromSpreadsheet extends TestCase { + + private static final class SS { + + public static final int BASIS_COLUMN = 1; // "B" + public static final int START_YEAR_COLUMN = 2; // "C" + public static final int END_YEAR_COLUMN = 5; // "F" + public static final int YEARFRAC_FORMULA_COLUMN = 11; // "L" + public static final int EXPECTED_RESULT_COLUMN = 13; // "N" + } + + public void testAll() { + + HSSFWorkbook wb = HSSFTestDataSamples.openSampleWorkbook("yearfracExamples.xls"); + HSSFSheet sheet = wb.getSheetAt(0); + HSSFFormulaEvaluator formulaEvaluator = new HSSFFormulaEvaluator(sheet, wb); + int nSuccess = 0; + int nFailures = 0; + int nUnexpectedErrors = 0; + Iterator rowIterator = sheet.rowIterator(); + while(rowIterator.hasNext()) { + HSSFRow row = (HSSFRow) rowIterator.next(); + + HSSFCell cell = row.getCell(SS.YEARFRAC_FORMULA_COLUMN); + if (cell == null || cell.getCellType() != HSSFCell.CELL_TYPE_FORMULA) { + continue; + } + try { + processRow(row, cell, formulaEvaluator); + nSuccess++; + } catch (RuntimeException e) { + nUnexpectedErrors ++; + printShortStackTrace(System.err, e); + } catch (AssertionFailedError e) { + nFailures ++; + printShortStackTrace(System.err, e); + } + } + if (nUnexpectedErrors + nFailures > 0) { + String msg = nFailures + " failures(s) and " + nUnexpectedErrors + + " unexpected errors(s) occurred. See stderr for details"; + throw new AssertionFailedError(msg); + } + if (nSuccess < 1) { + throw new RuntimeException("No test sample cases found"); + } + } + + private static void processRow(HSSFRow row, HSSFCell cell, HSSFFormulaEvaluator formulaEvaluator) { + + double startDate = makeDate(row, SS.START_YEAR_COLUMN); + double endDate = makeDate(row, SS.END_YEAR_COLUMN); + + int basis = getIntCell(row, SS.BASIS_COLUMN); + + double expectedValue = getDoubleCell(row, SS.EXPECTED_RESULT_COLUMN); + + double actualValue; + try { + actualValue = YearFracCalculator.calculate(startDate, endDate, basis); + } catch (EvaluationException e) { + throw new RuntimeException(e); + } + if (expectedValue != actualValue) { + throw new ComparisonFailure("Direct calculate failed - row " + (row.getRowNum()+1), + String.valueOf(expectedValue), String.valueOf(actualValue)); + } + actualValue = formulaEvaluator.evaluate(cell).getNumberValue(); + if (expectedValue != actualValue) { + throw new ComparisonFailure("Formula evaluate failed - row " + (row.getRowNum()+1), + String.valueOf(expectedValue), String.valueOf(actualValue)); + } + } + + private static double makeDate(HSSFRow row, int yearColumn) { + int year = getIntCell(row, yearColumn + 0); + int month = getIntCell(row, yearColumn + 1); + int day = getIntCell(row, yearColumn + 2); + Calendar c = new GregorianCalendar(year, month-1, day, 0, 0, 0); + c.set(Calendar.MILLISECOND, 0); + return HSSFDateUtil.getExcelDate(c.getTime()); + } + + private static int getIntCell(HSSFRow row, int colIx) { + double dVal = getDoubleCell(row, colIx); + if (Math.floor(dVal) != dVal) { + throw new RuntimeException("Non integer value (" + dVal + + ") cell found at column " + (char)('A' + colIx)); + } + return (int)dVal; + } + + private static double getDoubleCell(HSSFRow row, int colIx) { + HSSFCell cell = row.getCell(colIx); + if (cell == null) { + throw new RuntimeException("No cell found at column " + colIx); + } + double dVal = cell.getNumericCellValue(); + return dVal; + } + + /** + * Useful to keep output concise when expecting many failures to be reported by this test case + * TODO - refactor duplicates in other Test~FromSpreadsheet classes + */ + private static void printShortStackTrace(PrintStream ps, Throwable e) { + StackTraceElement[] stes = e.getStackTrace(); + + int startIx = 0; + // skip any top frames inside junit.framework.Assert + while(startIx= endIx) { + // something went wrong. just print the whole stack trace + e.printStackTrace(ps); + } + endIx -= 4; // skip 4 frames of reflection invocation + ps.println(e.toString()); + for(int i=startIx; i 0; x=(short)(x*2) ) { - r = s.createRow((short) x); + r = s.createRow(x); for (short y = 1; y < 256 && y > 0; y= (short) (y +2)) { - c = r.createCell((short) y); + c = r.createCell(y); c.setCellFormula("" + x+"."+y + operator + y +"."+x); } } if (s.getLastRowNum() < Short.MAX_VALUE) { - r = s.createRow((short)0); - c = r.createCell((short)0); + r = s.createRow(0); + c = r.createCell(0); c.setCellFormula("" + Float.MAX_VALUE + operator + Float.MAX_VALUE); } wb.write(out); @@ -216,11 +216,11 @@ public final class TestFormulas extends TestCase { // dont know how to check correct result .. for the moment, we just verify that the file can be read. for (short x = 1; x < Short.MAX_VALUE && x > 0; x=(short)(x*2)) { - r = s.getRow((short) x); + r = s.getRow(x); for (short y = 1; y < 256 && y > 0; y=(short)(y+2)) { - c = r.getCell((short) y); + c = r.getCell(y); assertTrue("got a formula",c.getCellFormula()!=null); assertTrue("loop Formula is as expected "+x+"."+y+operator+y+"."+x+"!="+c.getCellFormula(),( @@ -337,8 +337,8 @@ public final class TestFormulas extends TestCase { HSSFCell c = null; //get our minimum values - r = s.getRow((short)0); - c = r.getCell((short)1); + r = s.getRow(0); + c = r.getCell(1); //get our minimum values assertTrue("minval Formula is as expected A2"+operator+"A3 != "+c.getCellFormula(), ( ("A2"+operator+"A3").equals(c.getCellFormula()) @@ -346,7 +346,7 @@ public final class TestFormulas extends TestCase { for (short x = 1; x < Short.MAX_VALUE && x > 0; x=(short)(x*2)) { - r = s.getRow((short) x); + r = s.getRow(x); for (short y = 1; y < 256 && y > 0; y++) { @@ -372,7 +372,7 @@ public final class TestFormulas extends TestCase { refy2=(short)(y-3); } - c = r.getCell((short) y); + c = r.getCell(y); CellReference cr= new CellReference(refx1, refy1, false, false); ref=cr.formatAsString(); cr=new CellReference(refx2,refy2, false, false); @@ -389,8 +389,8 @@ public final class TestFormulas extends TestCase { } //test our maximum values - r = s.getRow((short)0); - c = r.getCell((short)0); + r = s.getRow(0); + c = r.getCell(0); assertTrue("maxval Formula is as expected",( ("B1"+operator+"IV255").equals(c.getCellFormula()) @@ -416,8 +416,8 @@ public final class TestFormulas extends TestCase { HSSFCell c = null; //get our minimum values - r = s.createRow((short)0); - c = r.createCell((short)1); + r = s.createRow(0); + c = r.createCell(1); c.setCellFormula(formula); wb.write(out); @@ -429,8 +429,8 @@ public final class TestFormulas extends TestCase { s = wb.getSheetAt(0); //get our minimum values - r = s.getRow((short)0); - c = r.getCell((short)1); + r = s.getRow(0); + c = r.getCell(1); assertTrue("minval Formula is as expected", formula.equals(c.getCellFormula()) ); @@ -496,18 +496,18 @@ public final class TestFormulas extends TestCase { HSSFCell c = null; //get our minimum values - r = s.getRow((short)0); - c = r.getCell((short)1); + r = s.getRow(0); + c = r.getCell(1); assertTrue("minval Formula is as expected 1"+operator+"1 != "+c.getCellFormula(), ( ("1"+operator+"1").equals(c.getCellFormula()) )); for (short x = 1; x < Short.MAX_VALUE && x > 0; x=(short)(x*2)) { - r = s.getRow((short) x); + r = s.getRow(x); for (short y = 1; y < 256 && y > 0; y++) { - c = r.getCell((short) y); + c = r.getCell(y); assertTrue("loop Formula is as expected "+x+operator+y+"!="+c.getCellFormula(),( (""+x+operator+y).equals(c.getCellFormula()) @@ -519,8 +519,8 @@ public final class TestFormulas extends TestCase { } //test our maximum values - r = s.getRow((short)0); - c = r.getCell((short)0); + r = s.getRow(0); + c = r.getCell(0); assertTrue("maxval Formula is as expected",( @@ -549,9 +549,9 @@ public final class TestFormulas extends TestCase { HSSFCell c = null; - r = s.createRow((short) 0); + r = s.createRow(0); - c = r.createCell((short) 0); + c = r.createCell(0); c.setCellFormula(function+"(A2:A3)"); @@ -563,7 +563,7 @@ public final class TestFormulas extends TestCase { wb = new HSSFWorkbook(in); s = wb.getSheetAt(0); r = s.getRow(0); - c = r.getCell((short)0); + c = r.getCell(0); assertTrue("function ="+function+"(A2:A3)", ( (function+"(A2:A3)").equals((function+"(A2:A3)")) ) @@ -586,9 +586,9 @@ public final class TestFormulas extends TestCase { HSSFCell c = null; - r = s.createRow((short) 0); + r = s.createRow(0); - c = r.createCell((short) 0); + c = r.createCell(0); c.setCellFormula(function+"(A2,A3)"); @@ -600,7 +600,7 @@ public final class TestFormulas extends TestCase { wb = new HSSFWorkbook(in); s = wb.getSheetAt(0); r = s.getRow(0); - c = r.getCell((short)0); + c = r.getCell(0); assertTrue("function ="+function+"(A2,A3)", ( (function+"(A2,A3)").equals(c.getCellFormula()) ) @@ -624,11 +624,11 @@ public final class TestFormulas extends TestCase { HSSFCell c = null; - r = s.createRow((short) 0); + r = s.createRow(0); - c = r.createCell((short) 0); + c = r.createCell(0); c.setCellFormula(function+"(A2:A4,B2:B4)"); - c=r.createCell((short) 1); + c=r.createCell(1); c.setCellFormula(function+"($A$2:$A4,B$2:B4)"); wb.write(out); @@ -639,13 +639,13 @@ public final class TestFormulas extends TestCase { wb = new HSSFWorkbook(in); s = wb.getSheetAt(0); r = s.getRow(0); - c = r.getCell((short)0); + c = r.getCell(0); assertTrue("function ="+function+"(A2:A4,B2:B4)", ( (function+"(A2:A4,B2:B4)").equals(c.getCellFormula()) ) ); - c=r.getCell((short) 1); + c=r.getCell(1); assertTrue("function ="+function+"($A$2:$A4,B$2:B4)", ( (function+"($A$2:$A4,B$2:B4)").equals(c.getCellFormula()) ) ); @@ -663,17 +663,17 @@ public final class TestFormulas extends TestCase { HSSFCell c = null; - r = s.createRow((short) 0); + r = s.createRow(0); - c = r.createCell((short) 0); + c = r.createCell(0); c.setCellFormula("A3+A2"); - c=r.createCell( (short) 1); + c=r.createCell(1); c.setCellFormula("$A3+$A2"); - c=r.createCell( (short) 2); + c=r.createCell(2); c.setCellFormula("A$3+A$2"); - c=r.createCell( (short) 3); + c=r.createCell(3); c.setCellFormula("$A$3+$A$2"); - c=r.createCell( (short) 4); + c=r.createCell(4); c.setCellFormula("SUM($A$3,$A$2)"); wb.write(out); @@ -684,15 +684,15 @@ public final class TestFormulas extends TestCase { wb = new HSSFWorkbook(in); s = wb.getSheetAt(0); r = s.getRow(0); - c = r.getCell((short)0); + c = r.getCell(0); assertTrue("A3+A2", ("A3+A2").equals(c.getCellFormula())); - c = r.getCell((short)1); + c = r.getCell(1); assertTrue("$A3+$A2", ("$A3+$A2").equals(c.getCellFormula())); - c = r.getCell((short)2); + c = r.getCell(2); assertTrue("A$3+A$2", ("A$3+A$2").equals(c.getCellFormula())); - c = r.getCell((short)3); + c = r.getCell(3); assertTrue("$A$3+$A$2", ("$A$3+$A$2").equals(c.getCellFormula())); - c = r.getCell((short)4); + c = r.getCell(4); assertTrue("SUM($A$3,$A$2)", ("SUM($A$3,$A$2)").equals(c.getCellFormula())); in.close(); } @@ -706,15 +706,15 @@ public final class TestFormulas extends TestCase { HSSFSheet s = wb.createSheet("A"); HSSFRow r = null; HSSFCell c = null; - r = s.createRow((short)0); - c = r.createCell((short)0);c.setCellValue(1); - c = r.createCell((short)1);c.setCellValue(2); + r = s.createRow(0); + c = r.createCell(0);c.setCellValue(1); + c = r.createCell(1);c.setCellValue(2); s = wb.createSheet("B"); - r = s.createRow((short)0); - c=r.createCell((short)0); c.setCellFormula("AVERAGE(A!A1:B1)"); - c=r.createCell((short)1); c.setCellFormula("A!A1+A!B1"); - c=r.createCell((short)2); c.setCellFormula("A!$A$1+A!$B1"); + r = s.createRow(0); + c=r.createCell(0); c.setCellFormula("AVERAGE(A!A1:B1)"); + c=r.createCell(1); c.setCellFormula("A!A1+A!B1"); + c=r.createCell(2); c.setCellFormula("A!$A$1+A!$B1"); wb.write(out); out.close(); @@ -724,9 +724,9 @@ public final class TestFormulas extends TestCase { wb = new HSSFWorkbook(in); s = wb.getSheet("B"); r = s.getRow(0); - c = r.getCell((short)0); + c = r.getCell(0); assertTrue("expected: AVERAGE(A!A1:B1) got: "+c.getCellFormula(), ("AVERAGE(A!A1:B1)").equals(c.getCellFormula())); - c = r.getCell((short)1); + c = r.getCell(1); assertTrue("expected: A!A1+A!B1 got: "+c.getCellFormula(), ("A!A1+A!B1").equals(c.getCellFormula())); in.close(); } @@ -740,29 +740,29 @@ public final class TestFormulas extends TestCase { HSSFCell c = null; - r = s.createRow((short) 0); + r = s.createRow(0); - c = r.createCell((short) 0); + c = r.createCell(0); c.setCellFormula("A3+A2"); - c=r.createCell( (short) 1); + c=r.createCell(1); c.setCellFormula("AVERAGE(A3,A2)"); - c=r.createCell( (short) 2); + c=r.createCell(2); c.setCellFormula("ROW(A3)"); - c=r.createCell( (short) 3); + c=r.createCell(3); c.setCellFormula("AVERAGE(A2:A3)"); - c=r.createCell( (short) 4); + c=r.createCell(4); c.setCellFormula("POWER(A2,A3)"); - c=r.createCell( (short) 5); + c=r.createCell(5); c.setCellFormula("SIN(A2)"); - c=r.createCell( (short) 6); + c=r.createCell(6); c.setCellFormula("SUM(A2:A3)"); - c=r.createCell( (short) 7); + c=r.createCell(7); c.setCellFormula("SUM(A2,A3)"); - r = s.createRow((short) 1);c=r.createCell( (short) 0); c.setCellValue(2.0); - r = s.createRow((short) 2);c=r.createCell( (short) 0); c.setCellValue(3.0); + r = s.createRow(1);c=r.createCell(0); c.setCellValue(2.0); + r = s.createRow(2);c=r.createCell(0); c.setCellValue(3.0); wb.write(out); out.close(); @@ -778,10 +778,10 @@ public final class TestFormulas extends TestCase { HSSFSheet s = wb.createSheet("A"); HSSFRow r = null; HSSFCell c = null; - r = s.createRow((short)0); - c=r.createCell((short)1); c.setCellFormula("UPPER(\"abc\")"); - c=r.createCell((short)2); c.setCellFormula("LOWER(\"ABC\")"); - c=r.createCell((short)3); c.setCellFormula("CONCATENATE(\" my \",\" name \")"); + r = s.createRow(0); + c=r.createCell(1); c.setCellFormula("UPPER(\"abc\")"); + c=r.createCell(2); c.setCellFormula("LOWER(\"ABC\")"); + c=r.createCell(3); c.setCellFormula("CONCATENATE(\" my \",\" name \")"); wb.write(out); out.close(); @@ -789,7 +789,7 @@ public final class TestFormulas extends TestCase { wb = openSample("StringFormulas.xls"); s = wb.getSheetAt(0); r = s.getRow(0); - c = r.getCell((short)0); + c = r.getCell(0); assertTrue("expected: UPPER(\"xyz\") got "+c.getCellFormula(), ("UPPER(\"xyz\")").equals(c.getCellFormula())); //c = r.getCell((short)1); //assertTrue("expected: A!A1+A!B1 got: "+c.getCellFormula(), ("A!A1+A!B1").equals(c.getCellFormula())); @@ -807,8 +807,8 @@ public final class TestFormulas extends TestCase { HSSFSheet s = wb.createSheet("A"); HSSFRow r = null; HSSFCell c = null; - r = s.createRow((short)0); - c=r.createCell((short)1); c.setCellFormula("IF(A1 0) { + throw new AssertionFailedError("Indentified bug 45682"); + } + throw e; + } + assertEquals(2, wb.getNumberOfSheets()); + } } diff --git a/src/testcases/org/apache/poi/hssf/usermodel/TestHSSFDateUtil.java b/src/testcases/org/apache/poi/hssf/usermodel/TestHSSFDateUtil.java index 377a8ffe2..9e451ee05 100644 --- a/src/testcases/org/apache/poi/hssf/usermodel/TestHSSFDateUtil.java +++ b/src/testcases/org/apache/poi/hssf/usermodel/TestHSSFDateUtil.java @@ -16,7 +16,6 @@ limitations under the License. ==================================================================== */ - package org.apache.poi.hssf.usermodel; import java.util.Calendar; @@ -52,9 +51,7 @@ public final class TestHSSFDateUtil extends TestCase { * Checks the date conversion functions in the HSSFDateUtil class. */ - public void testDateConversion() - throws Exception - { + public void testDateConversion() { // Iteratating over the hours exposes any rounding issues. for (int hour = 0; hour < 23; hour++) @@ -131,7 +128,7 @@ public final class TestHSSFDateUtil extends TestCase { for (int hour = 0; hour < 24; hour++, excelDate += oneHour) { // Skip 02:00 CET as that is the Daylight change time - // and Java converts it automatically to 03:00 CEST + // and Java converts it automatically to 03:00 CEST if (hour == 2) { continue; } @@ -186,7 +183,7 @@ public final class TestHSSFDateUtil extends TestCase { HSSFDateUtil.getExcelDate(javaDate, false), oneMinute); } } - + /** * Tests that we deal with time-zones properly */ @@ -207,8 +204,7 @@ public final class TestHSSFDateUtil extends TestCase { assertEquals("Checking timezone " + id, expected.getTime(), javaDate.getTime()); } } - - + /** * Tests that we correctly detect date formats as such */ @@ -220,7 +216,7 @@ public final class TestHSSFDateUtil extends TestCase { assertTrue( HSSFDateUtil.isInternalDateFormat(builtins[i]) ); assertTrue( HSSFDateUtil.isADateFormat(builtins[i],formatStr) ); } - + // Now try a few built-in non date formats builtins = new short[] { 0x01, 0x02, 0x17, 0x1f, 0x30 }; for(int i=0; i blank // 5 -> num - row.createCell((short)0).setCellValue(new HSSFRichTextString("test")); - row.createCell((short)1).setCellValue(3.2); - row.createCell((short)4, HSSFCell.CELL_TYPE_BLANK); - row.createCell((short)5).setCellValue(4); + row.createCell(0).setCellValue(new HSSFRichTextString("test")); + row.createCell(1).setCellValue(3.2); + row.createCell(4, HSSFCell.CELL_TYPE_BLANK); + row.createCell(5).setCellValue(4); // First up, no policy given, uses default assertEquals(HSSFCell.CELL_TYPE_STRING, row.getCell(0).getCellType()); @@ -281,12 +281,12 @@ public final class TestHSSFRow extends TestCase { public void testRowHeight() { HSSFWorkbook workbook = new HSSFWorkbook(); HSSFSheet sheet = workbook.createSheet(); - HSSFRow row1 = sheet.createRow( (short) 0); + HSSFRow row1 = sheet.createRow(0); assertEquals(0xFF, row1.getHeight()); assertEquals(sheet.getDefaultRowHeight(), row1.getHeight()); - HSSFRow row2 = sheet.createRow( (short) 1); + HSSFRow row2 = sheet.createRow(1); row2.setHeight((short)400); assertEquals(400, row2.getHeight()); diff --git a/src/testcases/org/apache/poi/hssf/usermodel/TestHSSFSheet.java b/src/testcases/org/apache/poi/hssf/usermodel/TestHSSFSheet.java index a24e5e797..4c8c8c5d6 100644 --- a/src/testcases/org/apache/poi/hssf/usermodel/TestHSSFSheet.java +++ b/src/testcases/org/apache/poi/hssf/usermodel/TestHSSFSheet.java @@ -147,10 +147,10 @@ public final class TestHSSFSheet extends TestCase { public void testReadBooleans() { HSSFWorkbook workbook = new HSSFWorkbook(); HSSFSheet sheet = workbook.createSheet("Test boolean"); - HSSFRow row = sheet.createRow((short) 2); - HSSFCell cell = row.createCell((short) 9); + HSSFRow row = sheet.createRow(2); + HSSFCell cell = row.createCell(9); cell.setCellValue(true); - cell = row.createCell((short) 11); + cell = row.createCell(11); cell.setCellValue(true); workbook = HSSFTestDataSamples.writeOutAndReadBack(workbook); @@ -164,7 +164,7 @@ public final class TestHSSFSheet extends TestCase { public void testRemoveRow() { HSSFWorkbook workbook = new HSSFWorkbook(); HSSFSheet sheet = workbook.createSheet("Test boolean"); - HSSFRow row = sheet.createRow((short) 2); + HSSFRow row = sheet.createRow(2); sheet.removeRow(row); } @@ -186,8 +186,8 @@ public final class TestHSSFSheet extends TestCase { HSSFWorkbook workbook = new HSSFWorkbook(); HSSFSheet sheet = workbook.createSheet("Test Clone"); HSSFRow row = sheet.createRow(0); - HSSFCell cell = row.createCell((short) 0); - HSSFCell cell2 = row.createCell((short) 1); + HSSFCell cell = row.createCell(0); + HSSFCell cell2 = row.createCell(1); cell.setCellValue(new HSSFRichTextString("clone_test")); cell2.setCellFormula("sin(1)"); @@ -216,8 +216,8 @@ public final class TestHSSFSheet extends TestCase { public void testCloneSheetMultipleTimes() { HSSFWorkbook workbook = new HSSFWorkbook(); HSSFSheet sheet = workbook.createSheet("Test Clone"); - HSSFRow row = sheet.createRow((short) 0); - HSSFCell cell = row.createCell((short) 0); + HSSFRow row = sheet.createRow(0); + HSSFCell cell = row.createCell(0); cell.setCellValue(new HSSFRichTextString("clone_test")); //Clone the sheet multiple times workbook.cloneSheet(0); @@ -510,11 +510,11 @@ public final class TestHSSFSheet extends TestCase { HSSFWorkbook wb = new HSSFWorkbook(); HSSFSheet sheet = wb.createSheet(); HSSFRow row = sheet.createRow(0); - HSSFCell cell = row.createCell((short)0); + HSSFCell cell = row.createCell(0); cell.setCellValue(new HSSFRichTextString("first row, first cell")); row = sheet.createRow(1); - cell = row.createCell((short)1); + cell = row.createCell(1); cell.setCellValue(new HSSFRichTextString("second row, second cell")); CellRangeAddress region = new CellRangeAddress(1, 1, 0, 1); @@ -617,13 +617,13 @@ public final class TestHSSFSheet extends TestCase { HSSFWorkbook wb = new HSSFWorkbook(); HSSFSheet s = wb.createSheet("Sheet1"); HSSFRow r = s.createRow(0); - r.createCell((short) 0).setCellValue(1); - r.createCell((short) 1).setCellFormula("A1*2"); + r.createCell(0).setCellValue(1); + r.createCell(1).setCellFormula("A1*2"); HSSFSheet s1 = wb.cloneSheet(0); r = s1.getRow(0); - assertEquals("double", r.getCell((short) 0).getNumericCellValue(), 1, 0); // sanity check - assertNotNull(r.getCell((short) 1)); - assertEquals("formula", r.getCell((short) 1).getCellFormula(), "A1*2"); + assertEquals("double", r.getCell(0).getNumericCellValue(), 1, 0); // sanity check + assertNotNull(r.getCell(1)); + assertEquals("formula", r.getCell(1).getCellFormula(), "A1*2"); } /** test that new default column styles get applied */ @@ -633,7 +633,7 @@ public final class TestHSSFSheet extends TestCase { HSSFSheet s = wb.createSheet(); s.setDefaultColumnStyle((short) 0, style); HSSFRow r = s.createRow(0); - HSSFCell c = r.createCell((short) 0); + HSSFCell c = r.createCell(0); assertEquals("style should match", style.getIndex(), c.getCellStyle().getIndex()); } @@ -710,8 +710,8 @@ public final class TestHSSFSheet extends TestCase { HSSFSheet sheet = workbook.getSheetAt(0); HSSFSheet sheet2 = workbook.getSheetAt(0); HSSFRow row = sheet.getRow(0); - row.createCell((short) 0).setCellValue(5); - row.createCell((short) 1).setCellValue(8); + row.createCell(0).setCellValue(5); + row.createCell(1).setCellValue(8); assertFalse(sheet.getForceFormulaRecalculation()); assertFalse(sheet2.getForceFormulaRecalculation()); diff --git a/src/testcases/org/apache/poi/hssf/usermodel/TestHSSFWorkbook.java b/src/testcases/org/apache/poi/hssf/usermodel/TestHSSFWorkbook.java index 35a2d955f..658a3f488 100644 --- a/src/testcases/org/apache/poi/hssf/usermodel/TestHSSFWorkbook.java +++ b/src/testcases/org/apache/poi/hssf/usermodel/TestHSSFWorkbook.java @@ -290,10 +290,10 @@ public final class TestHSSFWorkbook extends TestCase { assertEquals(true, sheet3.isActive()); if (false) { // helpful if viewing this workbook in excel: - sheet1.createRow(0).createCell((short)0).setCellValue(new HSSFRichTextString("Sheet1")); - sheet2.createRow(0).createCell((short)0).setCellValue(new HSSFRichTextString("Sheet2")); - sheet3.createRow(0).createCell((short)0).setCellValue(new HSSFRichTextString("Sheet3")); - sheet4.createRow(0).createCell((short)0).setCellValue(new HSSFRichTextString("Sheet4")); + sheet1.createRow(0).createCell(0).setCellValue(new HSSFRichTextString("Sheet1")); + sheet2.createRow(0).createCell(0).setCellValue(new HSSFRichTextString("Sheet2")); + sheet3.createRow(0).createCell(0).setCellValue(new HSSFRichTextString("Sheet3")); + sheet4.createRow(0).createCell(0).setCellValue(new HSSFRichTextString("Sheet4")); try { File fOut = TempFile.createTempFile("sheetMultiSelect", ".xls"); diff --git a/src/testcases/org/apache/poi/hssf/usermodel/TestReadWriteChart.java b/src/testcases/org/apache/poi/hssf/usermodel/TestReadWriteChart.java index 342f6e26f..186ee18a6 100644 --- a/src/testcases/org/apache/poi/hssf/usermodel/TestReadWriteChart.java +++ b/src/testcases/org/apache/poi/hssf/usermodel/TestReadWriteChart.java @@ -39,14 +39,14 @@ public final class TestReadWriteChart extends TestCase { HSSFWorkbook workbook = HSSFTestDataSamples.openSampleWorkbook("SimpleChart.xls"); HSSFSheet sheet = workbook.getSheetAt(0); HSSFRow firstRow = sheet.getRow(0); - HSSFCell firstCell = firstRow.getCell(( short ) 0); + HSSFCell firstCell = firstRow.getCell(0); //System.out.println("first assertion for date"); assertEquals(new GregorianCalendar(2000, 0, 1, 10, 51, 2).getTime(), HSSFDateUtil .getJavaDate(firstCell.getNumericCellValue(), false)); - HSSFRow row = sheet.createRow(( short ) 15); - HSSFCell cell = row.createCell(( short ) 1); + HSSFRow row = sheet.createRow(15); + HSSFCell cell = row.createCell(1); cell.setCellValue(22); Sheet newSheet = workbook.getSheetAt(0).getSheet(); diff --git a/src/testcases/org/apache/poi/hssf/usermodel/TestSheetHiding.java b/src/testcases/org/apache/poi/hssf/usermodel/TestSheetHiding.java index 62a26e90b..ddd910159 100644 --- a/src/testcases/org/apache/poi/hssf/usermodel/TestSheetHiding.java +++ b/src/testcases/org/apache/poi/hssf/usermodel/TestSheetHiding.java @@ -61,10 +61,10 @@ public final class TestSheetHiding extends TestCase { assertEquals(1, wbU.getSheetAt(1).getRow(0).getLastCellNum()); // Text should be sheet based - assertEquals("Sheet1A1", wbH.getSheetAt(0).getRow(0).getCell((short)0).getRichStringCellValue().getString()); - assertEquals("Sheet2A1", wbH.getSheetAt(1).getRow(0).getCell((short)0).getRichStringCellValue().getString()); - assertEquals("Sheet1A1", wbU.getSheetAt(0).getRow(0).getCell((short)0).getRichStringCellValue().getString()); - assertEquals("Sheet2A1", wbU.getSheetAt(1).getRow(0).getCell((short)0).getRichStringCellValue().getString()); + assertEquals("Sheet1A1", wbH.getSheetAt(0).getRow(0).getCell(0).getRichStringCellValue().getString()); + assertEquals("Sheet2A1", wbH.getSheetAt(1).getRow(0).getCell(0).getRichStringCellValue().getString()); + assertEquals("Sheet1A1", wbU.getSheetAt(0).getRow(0).getCell(0).getRichStringCellValue().getString()); + assertEquals("Sheet2A1", wbU.getSheetAt(1).getRow(0).getCell(0).getRichStringCellValue().getString()); } /** diff --git a/src/testcases/org/apache/poi/hssf/usermodel/TestSheetShiftRows.java b/src/testcases/org/apache/poi/hssf/usermodel/TestSheetShiftRows.java index 03d67cc21..101bd325d 100755 --- a/src/testcases/org/apache/poi/hssf/usermodel/TestSheetShiftRows.java +++ b/src/testcases/org/apache/poi/hssf/usermodel/TestSheetShiftRows.java @@ -99,8 +99,8 @@ public final class TestSheetShiftRows extends TestCase { public void testShiftRow(){ HSSFWorkbook b = new HSSFWorkbook(); HSSFSheet s = b.createSheet(); - s.createRow(0).createCell((short)0).setCellValue("TEST1"); - s.createRow(3).createCell((short)0).setCellValue("TEST2"); + s.createRow(0).createCell(0).setCellValue("TEST1"); + s.createRow(3).createCell(0).setCellValue("TEST2"); s.shiftRows(0,4,1); } @@ -112,8 +112,8 @@ public final class TestSheetShiftRows extends TestCase { public void testShiftRow0(){ HSSFWorkbook b = new HSSFWorkbook(); HSSFSheet s = b.createSheet(); - s.createRow(0).createCell((short)0).setCellValue("TEST1"); - s.createRow(3).createCell((short)0).setCellValue("TEST2"); + s.createRow(0).createCell(0).setCellValue("TEST1"); + s.createRow(3).createCell(0).setCellValue("TEST2"); s.shiftRows(0,4,1); } @@ -125,7 +125,7 @@ public final class TestSheetShiftRows extends TestCase { HSSFWorkbook b = new HSSFWorkbook(); HSSFSheet s = b.createSheet(); HSSFRow row = s.createRow(4); - row.createCell((short)0).setCellValue("test"); + row.createCell(0).setCellValue("test"); s.setRowBreak(4); s.shiftRows(4, 4, 2); @@ -203,34 +203,34 @@ public final class TestSheetShiftRows extends TestCase { HSSFSheet sheet = wb.getSheet("Sheet1"); assertEquals(19, sheet.getLastRowNum()); - assertEquals("cell B1 (ref)", sheet.getRow(0).getCell((short)3).getRichStringCellValue().toString()); - assertEquals("CONCATENATE(B1,\" (ref)\")", sheet.getRow(0).getCell((short)3).getCellFormula()); - assertEquals("cell B2 (ref)", sheet.getRow(1).getCell((short)3).getRichStringCellValue().toString()); - assertEquals("CONCATENATE(B2,\" (ref)\")", sheet.getRow(1).getCell((short)3).getCellFormula()); - assertEquals("cell B3 (ref)", sheet.getRow(2).getCell((short)3).getRichStringCellValue().toString()); - assertEquals("CONCATENATE(B3,\" (ref)\")", sheet.getRow(2).getCell((short)3).getCellFormula()); - assertEquals("cell B2 (ref)", sheet.getRow(6).getCell((short)1).getRichStringCellValue().toString()); - assertEquals("CONCATENATE(B2,\" (ref)\")", sheet.getRow(6).getCell((short)1).getCellFormula()); + assertEquals("cell B1 (ref)", sheet.getRow(0).getCell(3).getRichStringCellValue().toString()); + assertEquals("CONCATENATE(B1,\" (ref)\")", sheet.getRow(0).getCell(3).getCellFormula()); + assertEquals("cell B2 (ref)", sheet.getRow(1).getCell(3).getRichStringCellValue().toString()); + assertEquals("CONCATENATE(B2,\" (ref)\")", sheet.getRow(1).getCell(3).getCellFormula()); + assertEquals("cell B3 (ref)", sheet.getRow(2).getCell(3).getRichStringCellValue().toString()); + assertEquals("CONCATENATE(B3,\" (ref)\")", sheet.getRow(2).getCell(3).getCellFormula()); + assertEquals("cell B2 (ref)", sheet.getRow(6).getCell(1).getRichStringCellValue().toString()); + assertEquals("CONCATENATE(B2,\" (ref)\")", sheet.getRow(6).getCell(1).getCellFormula()); sheet.shiftRows(1, 1, 10); // Row 1 => Row 11 // So strings on row 11 unchanged, but reference in formula is - assertEquals("cell B1 (ref)", sheet.getRow(0).getCell((short)3).getRichStringCellValue().toString()); - assertEquals("CONCATENATE(B1,\" (ref)\")", sheet.getRow(0).getCell((short)3).getCellFormula()); + assertEquals("cell B1 (ref)", sheet.getRow(0).getCell(3).getRichStringCellValue().toString()); + assertEquals("CONCATENATE(B1,\" (ref)\")", sheet.getRow(0).getCell(3).getCellFormula()); assertEquals(0, sheet.getRow(1).getPhysicalNumberOfCells()); // still save b2 - assertEquals("cell B2 (ref)", sheet.getRow(11).getCell((short)3).getRichStringCellValue().toString()); + assertEquals("cell B2 (ref)", sheet.getRow(11).getCell(3).getRichStringCellValue().toString()); // but points to b12 - assertEquals("CONCATENATE(B12,\" (ref)\")", sheet.getRow(11).getCell((short)3).getCellFormula()); + assertEquals("CONCATENATE(B12,\" (ref)\")", sheet.getRow(11).getCell(3).getCellFormula()); - assertEquals("cell B3 (ref)", sheet.getRow(2).getCell((short)3).getRichStringCellValue().toString()); - assertEquals("CONCATENATE(B3,\" (ref)\")", sheet.getRow(2).getCell((short)3).getCellFormula()); + assertEquals("cell B3 (ref)", sheet.getRow(2).getCell(3).getRichStringCellValue().toString()); + assertEquals("CONCATENATE(B3,\" (ref)\")", sheet.getRow(2).getCell(3).getCellFormula()); // one on a non-shifted row also updated - assertEquals("cell B2 (ref)", sheet.getRow(6).getCell((short)1).getRichStringCellValue().toString()); - assertEquals("CONCATENATE(B12,\" (ref)\")", sheet.getRow(6).getCell((short)1).getCellFormula()); + assertEquals("cell B2 (ref)", sheet.getRow(6).getCell(1).getRichStringCellValue().toString()); + assertEquals("CONCATENATE(B12,\" (ref)\")", sheet.getRow(6).getCell(1).getCellFormula()); } } diff --git a/src/testcases/org/apache/poi/hssf/usermodel/TestUnicodeWorkbook.java b/src/testcases/org/apache/poi/hssf/usermodel/TestUnicodeWorkbook.java index d67c3f469..2a9215285 100644 --- a/src/testcases/org/apache/poi/hssf/usermodel/TestUnicodeWorkbook.java +++ b/src/testcases/org/apache/poi/hssf/usermodel/TestUnicodeWorkbook.java @@ -57,14 +57,14 @@ public class TestUnicodeWorkbook extends TestCase { f.setRight("\u20ac"); HSSFRow r = s.createRow(0); - HSSFCell c = r.createCell((short)1); + HSSFCell c = r.createCell(1); c.setCellValue(12.34); c.getCellStyle().setDataFormat(fmt); - HSSFCell c2 = r.createCell((short)2); + HSSFCell c2 = r.createCell(2); c.setCellValue(new HSSFRichTextString("\u20ac")); - HSSFCell c3 = r.createCell((short)3); + HSSFCell c3 = r.createCell(3); String formulaString = "TEXT(12.34,\"\u20ac###,##\")"; c3.setCellFormula(formulaString); @@ -95,16 +95,16 @@ public class TestUnicodeWorkbook extends TestCase { //Test the dataformat r = s.getRow(0); - c = r.getCell((short)1); + c = r.getCell(1); df = wb.createDataFormat(); assertEquals(formatStr, df.getFormat(c.getCellStyle().getDataFormat())); //Test the cell string value - c2 = r.getCell((short)2); + c2 = r.getCell(2); assertEquals(c.getRichStringCellValue().getString(), "\u20ac"); //Test the cell formula - c3 = r.getCell((short)3); + c3 = r.getCell(3); assertEquals(c3.getCellFormula(), formulaString); } @@ -122,7 +122,7 @@ public class TestUnicodeWorkbook extends TestCase { HSSFSheet s = wb.createSheet("test"); HSSFRow r = s.createRow(0); - HSSFCell c = r.createCell((short)1); + HSSFCell c = r.createCell(1); c.setCellValue(new HSSFRichTextString("\u00e4")); //Confirm that the sring will be compressed @@ -140,7 +140,7 @@ public class TestUnicodeWorkbook extends TestCase { s = wb.getSheet("test"); assertNotNull(s); - c = r.getCell((short)1); + c = r.getCell(1); assertEquals(c.getRichStringCellValue().getString(), "\u00e4"); } diff --git a/src/testcases/org/apache/poi/hssf/usermodel/TestWorkbook.java b/src/testcases/org/apache/poi/hssf/usermodel/TestWorkbook.java index c0231cdfe..c6fa2ee0d 100644 --- a/src/testcases/org/apache/poi/hssf/usermodel/TestWorkbook.java +++ b/src/testcases/org/apache/poi/hssf/usermodel/TestWorkbook.java @@ -96,7 +96,7 @@ public final class TestWorkbook extends TestCase { c.setCellValue(rownum * 10000 + cellnum + ((( double ) rownum / 1000) + (( double ) cellnum / 10000))); - c = r.createCell(( short ) (cellnum + 1)); + c = r.createCell(cellnum + 1); c.setCellValue(new HSSFRichTextString("TEST")); } } @@ -142,7 +142,7 @@ public final class TestWorkbook extends TestCase { c.setCellValue(rownum * 10000 + cellnum + ((( double ) rownum / 1000) + (( double ) cellnum / 10000))); - c = r.createCell(( short ) (cellnum + 1)); + c = r.createCell(cellnum + 1); c.setCellValue(new HSSFRichTextString("TEST")); } } @@ -226,8 +226,8 @@ public final class TestWorkbook extends TestCase { short df = format.getFormat("0.0"); cs.setDataFormat(df); - r = s.createRow((short)0); - c = r.createCell((short)0); + r = s.createRow(0); + c = r.createCell(0); c.setCellStyle(cs); c.setCellValue(1.25); @@ -239,7 +239,7 @@ public final class TestWorkbook extends TestCase { HSSFWorkbook workbook = new HSSFWorkbook(fs); HSSFSheet sheet = workbook.getSheetAt(0); HSSFCell cell = - sheet.getRow(( short ) 0).getCell(( short ) 0); + sheet.getRow(0).getCell(0); format = workbook.createDataFormat(); assertEquals(1.25,cell.getNumericCellValue(), 1e-10); @@ -350,7 +350,7 @@ public final class TestWorkbook extends TestCase { for (int k = 0; k < 4; k++) { - HSSFCell cell = sheet.getRow(( short ) k).getCell(( short ) 0); + HSSFCell cell = sheet.getRow(k).getCell(0); cell.setCellValue(new HSSFRichTextString(REPLACED)); } @@ -360,7 +360,7 @@ public final class TestWorkbook extends TestCase { sheet = workbook.getSheetAt(0); for (int k = 0; k < 4; k++) { - HSSFCell cell = sheet.getRow(( short ) k).getCell(( short ) 0); + HSSFCell cell = sheet.getRow(k).getCell(0); assertEquals(REPLACED, cell.getRichStringCellValue().getString()); } @@ -439,7 +439,7 @@ public final class TestWorkbook extends TestCase { c.setCellValue(rownum * 10000 + cellnum + ((( double ) rownum / 1000) + (( double ) cellnum / 10000))); - c = r.createCell(( short ) (cellnum + 1)); + c = r.createCell(cellnum + 1); c.setCellValue(new HSSFRichTextString("TEST")); } } @@ -530,7 +530,7 @@ public final class TestWorkbook extends TestCase { for ( i = 0, j = 32771; j > 0; i++, j-- ) { row = sheet.createRow(i); - cell = row.createCell((short) 0); + cell = row.createCell(0); cell.setCellValue(i); } sanityChecker.checkHSSFWorkbook(workbook); @@ -556,7 +556,7 @@ public final class TestWorkbook extends TestCase { HSSFRow row = sheet.createRow(0); - HSSFCell cell = row.createCell((short)1); + HSSFCell cell = row.createCell(1); cell.setCellValue(new HSSFRichTextString("hi"));