resolved bugzilla ticket 53642

git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1380882 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Evgeniy Berlog 2012-09-04 21:00:41 +00:00
parent 107c23505b
commit e0db6f31a0
6 changed files with 127 additions and 20 deletions

View File

@ -42,7 +42,7 @@
<section><title>Status</title> <section><title>Status</title>
<p> The code currently provides implementations for all the arithmatic operators. <p> The code currently provides implementations for all the arithmatic operators.
It also provides implementations for approx. 140 built in It also provides implementations for approx. 140 built in
functions in Excel. The framework however makes is easy to add functions in Excel. The framework however makes it easy to add
implementation of new functions. See the <link href="eval-devguide.html"> Formula implementation of new functions. See the <link href="eval-devguide.html"> Formula
evaluation development guide</link> and <link href="../apidocs/org/apache/poi/hssf/record/formula/functions/package-summary.html">javadocs</link> evaluation development guide</link> and <link href="../apidocs/org/apache/poi/hssf/record/formula/functions/package-summary.html">javadocs</link>
for details. </p> for details. </p>
@ -249,7 +249,7 @@ for(int sheetNum = 0; sheetNum &lt; wb.getNumberOfSheets(); sheetNum++) {
existing workbooks with formulas. This can be done in two ways: existing workbooks with formulas. This can be done in two ways:
</p> </p>
<p> <p>
1. Re-evaluate formuals with POI's FormulaEvaluator: 1. Re-evaluate formulas with POI's FormulaEvaluator:
</p> </p>
<source> <source>
Workbook wb = WorkbookFactory.create(new FileInputStream("workbook.xls")); Workbook wb = WorkbookFactory.create(new FileInputStream("workbook.xls"));
@ -308,5 +308,33 @@ for(int sheetNum = 0; sheetNum &lt; wb.getNumberOfSheets(); sheetNum++) {
</li> </li>
</ul> </ul>
</section> </section>
<section><title>Formula Evaluation Debugging</title>
<p>POI is not perfect and you may stumble across formula evaluation problems (Java exceptions
or just different results) in your special use case. To support an easy detailed analysis, a special
logging of the full evaluation is provided.</p>
<p>The output of this logging may be very large (depends on your EXCEL), so this logging has to be explicitly enabled
for each single formula evaluation. Should not be used in production - only for specific development use.</p>
<p>Example use:</p>
<source>
// activate logging to console
System.setProperty("org.apache.poi.util.POILogger", "org.apache.poi.util.SystemOutLogger");
System.setProperty("poi.log.level", POILogger.INFO + "");
// open your file
Workbook wb = new HSSFWorkbook(new FileInputStream("foobar.xls"));
HSSFFormulaEvaluator fe = (HSSFFormulaEvaluator) wb.getCreationHelper().createFormulaEvaluator();
// get your cell
Cell cell = wb.getSheet(0).getRow(0).getCell(0); // just a dummy example
// perform debug output for the next evaluate-call only
fe.setDebugEvaluationOutputForNextEval(true);
evaluator.evaluateFormulaCell(cell);
evaluator.evaluateFormulaCell(cell); // no logging performed for this next evaluate-call
</source>
<p>The special Logger called "POI.FormulaEval" is used (useful if you use the CommonsLogger and a detailed logging configuration).
The used log levels are WARN and INFO (for detailed parameter info and results) - the level are so high to allow this
special logging without beeing disturbed by the bunch of DEBUG log entries from other classes.</p>
</section>
</body> </body>
</document> </document>

View File

@ -389,4 +389,15 @@ public class HSSFFormulaEvaluator implements FormulaEvaluator {
_bookEvaluator.setIgnoreMissingWorkbooks(ignore); _bookEvaluator.setIgnoreMissingWorkbooks(ignore);
} }
/**
* @param value whether perform detailed output
*
* Perform detailed output of formula evaluation for next evaluation only?
* Is for developer use only (also developers using POI for their XLS files).
* Log-Level WARN is for basic info, INFO for detailed information. These quite
* high levels are used because you have to explicitly enable this specific logging.
*/
public void setDebugEvaluationOutputForNextEval(boolean value){
_bookEvaluator.setDebugEvaluationOutputForNextEval(value);
}
} }

View File

@ -171,9 +171,10 @@ public final class HSSFSheet implements org.apache.poi.ss.usermodel.Sheet {
Iterator<CellValueRecordInterface> iter = sheet.getCellValueIterator(); Iterator<CellValueRecordInterface> iter = sheet.getCellValueIterator();
long timestart = System.currentTimeMillis(); long timestart = System.currentTimeMillis();
if (log.check(POILogger.DEBUG)) if (log.check( POILogger.DEBUG )) {
log.log(DEBUG, "Time at start of cell creating in HSSF sheet = ", log.log(DEBUG, "Time at start of cell creating in HSSF sheet = ",
Long.valueOf(timestart)); Long.valueOf(timestart));
}
HSSFRow lastrow = null; HSSFRow lastrow = null;
// Add every cell to its row // Add every cell to its row
@ -199,18 +200,25 @@ public final class HSSFSheet implements org.apache.poi.ss.usermodel.Sheet {
hrow = createRowFromRecord(rowRec); hrow = createRowFromRecord(rowRec);
} }
} }
if (log.check(POILogger.DEBUG)) if (log.check( POILogger.DEBUG )) {
if (cval instanceof Record) {
log.log( DEBUG, "record id = " + Integer.toHexString( ( (Record) cval ).getSid() ) ); log.log( DEBUG, "record id = " + Integer.toHexString( ( (Record) cval ).getSid() ) );
} else {
log.log( DEBUG, "record = " + cval );
}
}
hrow.createCellFromRecord( cval ); hrow.createCellFromRecord( cval );
if (log.check(POILogger.DEBUG)) if (log.check( POILogger.DEBUG )) {
log.log( DEBUG, "record took ", log.log( DEBUG, "record took ",
Long.valueOf( System.currentTimeMillis() - cellstart ) ); Long.valueOf( System.currentTimeMillis() - cellstart ) );
}
} }
if (log.check(POILogger.DEBUG)) if (log.check( POILogger.DEBUG )) {
log.log(DEBUG, "total sheet cell creation took ", log.log(DEBUG, "total sheet cell creation took ",
Long.valueOf(System.currentTimeMillis() - timestart)); Long.valueOf(System.currentTimeMillis() - timestart));
} }
}
/** /**
* Create a new row within the sheet and return the high level representation * Create a new row within the sheet and return the high level representation

View File

@ -70,6 +70,7 @@ import org.apache.poi.util.POILogger;
* For POI internal use only * For POI internal use only
* *
* @author Josh Micich * @author Josh Micich
* @author Thies Wellpott (debug output enhancements)
*/ */
public final class WorkbookEvaluator { public final class WorkbookEvaluator {
@ -384,14 +385,47 @@ public final class WorkbookEvaluator {
} }
throw new RuntimeException("Unexpected cell type (" + cellType + ")"); throw new RuntimeException("Unexpected cell type (" + cellType + ")");
} }
/**
* whether print detailed messages about the next formula evaluation
*/
private boolean dbgEvaluationOutputForNextEval = false;
// special logger for formula evaluation output (because of possibly very large output)
private final POILogger EVAL_LOG = POILogFactory.getLogger("POI.FormulaEval");
// current indent level for evalution; negative value for no output
private int dbgEvaluationOutputIndent = -1;
// visibility raised for testing // visibility raised for testing
/* package */ ValueEval evaluateFormula(OperationEvaluationContext ec, Ptg[] ptgs) { /* package */ ValueEval evaluateFormula(OperationEvaluationContext ec, Ptg[] ptgs) {
String dbgIndentStr = ""; // always init. to non-null just for defensive avoiding NPE
if (dbgEvaluationOutputForNextEval) {
// first evaluation call when ouput is desired, so iit. this evaluator instance
dbgEvaluationOutputIndent = 1;
dbgEvaluationOutputForNextEval = false;
}
if (dbgEvaluationOutputIndent > 0) {
// init. indent string to needed spaces (create as substring vom very long space-only string;
// limit indendation for deep recursions)
dbgIndentStr = " ";
dbgIndentStr = dbgIndentStr.substring(0, Math.min(dbgIndentStr.length(), dbgEvaluationOutputIndent*2));
EVAL_LOG.log(POILogger.WARN, dbgIndentStr
+ "- evaluateFormula('" + ec.getRefEvaluatorForCurrentSheet().getSheetName()
+ "'/" + new CellReference(ec.getRowIndex(), ec.getColumnIndex()).formatAsString()
+ "): " + Arrays.toString(ptgs).replaceAll("\\Qorg.apache.poi.ss.formula.ptg.\\E", ""));
dbgEvaluationOutputIndent++;
}
Stack<ValueEval> stack = new Stack<ValueEval>(); Stack<ValueEval> stack = new Stack<ValueEval>();
for (int i = 0, iSize = ptgs.length; i < iSize; i++) { for (int i = 0, iSize = ptgs.length; i < iSize; i++) {
// since we don't know how to handle these yet :( // since we don't know how to handle these yet :(
Ptg ptg = ptgs[i]; Ptg ptg = ptgs[i];
if (dbgEvaluationOutputIndent > 0) {
EVAL_LOG.log(POILogger.INFO, dbgIndentStr + " * ptg " + i + ": " + ptg);
}
if (ptg instanceof AttrPtg) { if (ptg instanceof AttrPtg) {
AttrPtg attrPtg = (AttrPtg) ptg; AttrPtg attrPtg = (AttrPtg) ptg;
if (attrPtg.isSum()) { if (attrPtg.isSum()) {
@ -497,13 +531,28 @@ public final class WorkbookEvaluator {
} }
// logDebug("push " + opResult); // logDebug("push " + opResult);
stack.push(opResult); stack.push(opResult);
if (dbgEvaluationOutputIndent > 0) {
EVAL_LOG.log(POILogger.INFO, dbgIndentStr + " = " + opResult);
}
} }
ValueEval value = stack.pop(); ValueEval value = stack.pop();
if (!stack.isEmpty()) { if (!stack.isEmpty()) {
throw new IllegalStateException("evaluation stack not empty"); throw new IllegalStateException("evaluation stack not empty");
} }
return dereferenceResult(value, ec.getRowIndex(), ec.getColumnIndex()); ValueEval result = dereferenceResult(value, ec.getRowIndex(), ec.getColumnIndex());
if (dbgEvaluationOutputIndent > 0) {
EVAL_LOG.log(POILogger.INFO, dbgIndentStr + "finshed eval of "
+ new CellReference(ec.getRowIndex(), ec.getColumnIndex()).formatAsString()
+ ": " + result);
dbgEvaluationOutputIndent--;
if (dbgEvaluationOutputIndent == 1) {
// this evaluation is done, reset indent to stop logging
dbgEvaluationOutputIndent = -1;
}
} // if
return result;
} }
/** /**
@ -723,4 +772,8 @@ public final class WorkbookEvaluator {
public static void registerFunction(String name, Function func){ public static void registerFunction(String name, Function func){
FunctionEval.registerFunction(name, func); FunctionEval.registerFunction(name, func);
} }
public void setDebugEvaluationOutputForNextEval(boolean value){
dbgEvaluationOutputForNextEval = value;
}
} }

View File

@ -17,7 +17,8 @@
package org.apache.poi.util; package org.apache.poi.util;
import java.util.*; import java.util.ArrayList;
import java.util.List;
/** /**
* A logger interface that strives to make it as easy as possible for * A logger interface that strives to make it as easy as possible for
@ -31,11 +32,17 @@ import java.util.*;
*/ */
public abstract class POILogger { public abstract class POILogger {
public static int DEBUG = 1; public static final int DEBUG = 1;
public static int INFO = 3; public static final int INFO = 3;
public static int WARN = 5; public static final int WARN = 5;
public static int ERROR = 7; public static final int ERROR = 7;
public static int FATAL = 9; public static final int FATAL = 9;
/** Short strings for numeric log level. Use level as array index. */
protected static final String LEVEL_STRINGS_SHORT[] = {"?", "D", "?", "I", "?", "W", "?", "E", "?", "F", "?"};
/** Long strings for numeric log level. Use level as array index. */
protected static final String LEVEL_STRINGS[] = {"?0?", "DEBUG", "?2?", "INFO", "?4?", "WARN", "?6?", "ERROR", "?8?", "FATAL", "?10+?"};
/** /**
* package scope so it cannot be instantiated outside of the util * package scope so it cannot be instantiated outside of the util

View File

@ -60,7 +60,7 @@ public class SystemOutLogger extends POILogger
public void log(final int level, final Object obj1, public void log(final int level, final Object obj1,
final Throwable exception) { final Throwable exception) {
if (check(level)) { if (check(level)) {
System.out.println("["+_cat+"] "+obj1); System.out.println("[" + _cat + "]" + LEVEL_STRINGS_SHORT[Math.min(LEVEL_STRINGS_SHORT.length-1, level)] + " " + obj1);
if (exception != null) { if (exception != null) {
exception.printStackTrace(System.out); exception.printStackTrace(System.out);
} }