diff --git a/src/java/org/apache/poi/ss/formula/eval/ErrorEval.java b/src/java/org/apache/poi/ss/formula/eval/ErrorEval.java index 24e5d0a53..82a4e9361 100644 --- a/src/java/org/apache/poi/ss/formula/eval/ErrorEval.java +++ b/src/java/org/apache/poi/ss/formula/eval/ErrorEval.java @@ -17,13 +17,17 @@ package org.apache.poi.ss.formula.eval; -import org.apache.poi.ss.usermodel.ErrorConstants; +import java.util.HashMap; +import java.util.Map; + import org.apache.poi.ss.usermodel.FormulaError; /** * Evaluations for formula errors */ public final class ErrorEval implements ValueEval { + private static final Map evals = new HashMap(); + /** #NULL! - Intersection of two cell ranges is empty */ public static final ErrorEval NULL_INTERSECTION = new ErrorEval(FormulaError.NULL); /** #DIV/0! - Division by zero */ @@ -40,29 +44,23 @@ public final class ErrorEval implements ValueEval { public static final ErrorEval NA = new ErrorEval(FormulaError.NA); // POI internal error codes - private static final int CIRCULAR_REF_ERROR_CODE = 0xFFFFFFC4; - private static final int FUNCTION_NOT_IMPLEMENTED_CODE = 0xFFFFFFE2; + public static final ErrorEval FUNCTION_NOT_IMPLEMENTED = new ErrorEval(FormulaError.FUNCTION_NOT_IMPLEMENTED); // Note - Excel does not seem to represent this condition with an error code - public static final ErrorEval CIRCULAR_REF_ERROR = new ErrorEval(CIRCULAR_REF_ERROR_CODE); + public static final ErrorEval CIRCULAR_REF_ERROR = new ErrorEval(FormulaError.CIRCULAR_REF); /** * Translates an Excel internal error code into the corresponding POI ErrorEval instance * @param errorCode */ public static ErrorEval valueOf(int errorCode) { - switch(errorCode) { - case ErrorConstants.ERROR_NULL: return NULL_INTERSECTION; - case ErrorConstants.ERROR_DIV_0: return DIV_ZERO; - case ErrorConstants.ERROR_VALUE: return VALUE_INVALID; - case ErrorConstants.ERROR_REF: return REF_INVALID; - case ErrorConstants.ERROR_NAME: return NAME_INVALID; - case ErrorConstants.ERROR_NUM: return NUM_ERROR; - case ErrorConstants.ERROR_NA: return NA; - // non-std errors (conditions modelled as errors by POI) - case CIRCULAR_REF_ERROR_CODE: return CIRCULAR_REF_ERROR; + FormulaError error = FormulaError.forInt(errorCode); + ErrorEval eval = evals.get(error); + if (eval != null) { + return eval; + } else { + throw new RuntimeException("Unhandled error type " + eval + " for code " + errorCode); } - throw new RuntimeException("Unexpected error code (" + errorCode + ")"); } /** @@ -72,36 +70,28 @@ public final class ErrorEval implements ValueEval { */ public static String getText(int errorCode) { if(FormulaError.isValidCode(errorCode)) { - return FormulaError.forInt((byte)errorCode).getString(); - } - // It is desirable to make these (arbitrary) strings look clearly different from any other - // value expression that might appear in a formula. In addition these error strings should - // look unlike the standard Excel errors. Hence tilde ('~') was used. - switch(errorCode) { - case CIRCULAR_REF_ERROR_CODE: return "~CIRCULAR~REF~"; - case FUNCTION_NOT_IMPLEMENTED_CODE: return "~FUNCTION~NOT~IMPLEMENTED~"; + return FormulaError.forInt(errorCode).getString(); } + // Give a special string, based on ~, to make clear this isn't a standard Excel error return "~non~std~err(" + errorCode + ")~"; } - private int _errorCode; - /** - * @param errorCode an 8-bit value - */ - private ErrorEval(int errorCode) { - _errorCode = errorCode; - } + private FormulaError _error; private ErrorEval(FormulaError error) { - _errorCode = error.getCode(); + _error = error; + evals.put(error, this); } public int getErrorCode() { - return _errorCode; + return _error.getLongCode(); + } + public String getErrorString() { + return _error.getString(); } public String toString() { StringBuffer sb = new StringBuffer(64); sb.append(getClass().getName()).append(" ["); - sb.append(getText(_errorCode)); + sb.append(_error.getString()); sb.append("]"); return sb.toString(); } diff --git a/src/java/org/apache/poi/ss/usermodel/FormulaError.java b/src/java/org/apache/poi/ss/usermodel/FormulaError.java index 739362436..e540d69a0 100644 --- a/src/java/org/apache/poi/ss/usermodel/FormulaError.java +++ b/src/java/org/apache/poi/ss/usermodel/FormulaError.java @@ -93,7 +93,23 @@ public enum FormulaError { *

* This error value can be produced by calling the function NA */ - NA(0x2A, "#N/A"); + NA(0x2A, "#N/A"), + + // These are POI-specific error codes + // It is desirable to make these (arbitrary) strings look clearly different from any other + // value expression that might appear in a formula. In addition these error strings should + // look unlike the standard Excel errors. Hence tilde ('~') was used. + + /** + * POI specific code to indicate that there is a circular reference + * in the formula + */ + CIRCULAR_REF(0xFFFFFFC4, "~CIRCULAR~REF~"), + /** + * POI specific code to indicate that the funcition required is + * not implemented in POI + */ + FUNCTION_NOT_IMPLEMENTED(0xFFFFFFE2, "~FUNCTION~NOT~IMPLEMENTED~"); private final byte type; private final int longType; @@ -151,6 +167,7 @@ public enum FormulaError { } public static FormulaError forInt(int type){ FormulaError err = imap.get(type); + if(err == null) err = bmap.get((byte)type); if(err == null) throw new IllegalArgumentException("Unknown error type: " + type); return err; } diff --git a/src/ooxml/testcases/org/apache/poi/xssf/usermodel/TestXSSFBugs.java b/src/ooxml/testcases/org/apache/poi/xssf/usermodel/TestXSSFBugs.java index 06af21cc8..c2dbb09c8 100644 --- a/src/ooxml/testcases/org/apache/poi/xssf/usermodel/TestXSSFBugs.java +++ b/src/ooxml/testcases/org/apache/poi/xssf/usermodel/TestXSSFBugs.java @@ -2151,7 +2151,7 @@ public final class TestXSSFBugs extends BaseTestBugzillaIssues { CellValue value = evaluator.evaluate(cell); assertEquals(Cell.CELL_TYPE_ERROR, value.getCellType()); assertEquals(-60, value.getErrorValue()); - // TODO Fix this -// assertEquals("", FormulaError.forInt(value.getErrorValue()).toString()); + assertEquals("~CIRCULAR~REF~", FormulaError.forInt(value.getErrorValue()).getString()); + assertEquals("CIRCULAR_REF", FormulaError.forInt(value.getErrorValue()).toString()); } }