You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@poi.apache.org by on...@apache.org on 2016/06/10 07:41:09 UTC

svn commit: r1747657 [3/7] - in /poi/trunk: ./ src/java/org/apache/poi/hssf/model/ src/java/org/apache/poi/hssf/usermodel/ src/java/org/apache/poi/ss/formula/ src/java/org/apache/poi/ss/formula/functions/ src/java/org/apache/poi/ss/formula/ptg/ src/oox...

Modified: poi/trunk/src/java/org/apache/poi/ss/formula/FormulaParsingWorkbook.java
URL: http://svn.apache.org/viewvc/poi/trunk/src/java/org/apache/poi/ss/formula/FormulaParsingWorkbook.java?rev=1747657&r1=1747656&r2=1747657&view=diff
==============================================================================
--- poi/trunk/src/java/org/apache/poi/ss/formula/FormulaParsingWorkbook.java (original)
+++ poi/trunk/src/java/org/apache/poi/ss/formula/FormulaParsingWorkbook.java Fri Jun 10 07:41:09 2016
@@ -20,6 +20,7 @@ package org.apache.poi.ss.formula;
 import org.apache.poi.ss.SpreadsheetVersion;
 import org.apache.poi.ss.formula.ptg.Ptg;
 import org.apache.poi.ss.usermodel.Name;
+import org.apache.poi.ss.usermodel.Table;
 import org.apache.poi.ss.util.AreaReference;
 import org.apache.poi.ss.util.CellReference;
 
@@ -31,46 +32,51 @@ import org.apache.poi.ss.util.CellRefere
  * @author Josh Micich
  */
 public interface FormulaParsingWorkbook {
-	/**
-	 *  named range name matching is case insensitive
-	 */
-	EvaluationName getName(String name, int sheetIndex);
-	
-	/**
-	 * Return the underlying workbook
-	 */
-	Name createName();
-
-	/**
-	 * Return an external name (named range, function, user-defined function) Ptg
-	 */
-	Ptg getNameXPtg(String name, SheetIdentifier sheet);
-	
-	/**
-	 * Produce the appropriate Ptg for a 3d cell reference
-	 */
-	Ptg get3DReferencePtg(CellReference cell, SheetIdentifier sheet);
+    /**
+     *  named range name matching is case insensitive
+     */
+    EvaluationName getName(String name, int sheetIndex);
+    
+    /**
+     * Return the underlying workbook
+     */
+    Name createName();
+
+    /**
+     * XSSF Only - gets a table that exists in the worksheet
+     */
+    Table getTable(String name);
+    
+    /**
+     * Return an external name (named range, function, user-defined function) Ptg
+     */
+    Ptg getNameXPtg(String name, SheetIdentifier sheet);
+    
+    /**
+     * Produce the appropriate Ptg for a 3d cell reference
+     */
+    Ptg get3DReferencePtg(CellReference cell, SheetIdentifier sheet);
 
     /**
      * Produce the appropriate Ptg for a 3d area reference
      */
     Ptg get3DReferencePtg(AreaReference area, SheetIdentifier sheet);
 
-	/**
-	 * gets the externSheet index for a sheet from this workbook
-	 */
-	int getExternalSheetIndex(String sheetName);
-	/**
-	 * gets the externSheet index for a sheet from an external workbook
-	 * @param workbookName e.g. "Budget.xls"
-	 * @param sheetName a name of a sheet in that workbook
-	 */
-	int getExternalSheetIndex(String workbookName, String sheetName);
-
-	/**
-	 * Returns an enum holding spreadhseet properties specific to an Excel version (
-	 * max column and row numbers, max arguments to a function, etc.)
-	 */
-	SpreadsheetVersion getSpreadsheetVersion();
+    /**
+     * gets the externSheet index for a sheet from this workbook
+     */
+    int getExternalSheetIndex(String sheetName);
+    /**
+     * gets the externSheet index for a sheet from an external workbook
+     * @param workbookName e.g. "Budget.xls"
+     * @param sheetName a name of a sheet in that workbook
+     */
+    int getExternalSheetIndex(String workbookName, String sheetName);
+
+    /**
+     * Returns an enum holding spreadhseet properties specific to an Excel version (
+     * max column and row numbers, max arguments to a function, etc.)
+     */
+    SpreadsheetVersion getSpreadsheetVersion();
 
 }

Modified: poi/trunk/src/java/org/apache/poi/ss/formula/OperationEvaluationContext.java
URL: http://svn.apache.org/viewvc/poi/trunk/src/java/org/apache/poi/ss/formula/OperationEvaluationContext.java?rev=1747657&r1=1747656&r2=1747657&view=diff
==============================================================================
--- poi/trunk/src/java/org/apache/poi/ss/formula/OperationEvaluationContext.java (original)
+++ poi/trunk/src/java/org/apache/poi/ss/formula/OperationEvaluationContext.java Fri Jun 10 07:41:09 2016
@@ -46,199 +46,199 @@ import org.apache.poi.ss.util.CellRefere
  * For POI internal use only
  */
 public final class OperationEvaluationContext {
-	public static final FreeRefFunction UDF = UserDefinedFunction.instance;
-	private final EvaluationWorkbook _workbook;
-	private final int _sheetIndex;
-	private final int _rowIndex;
-	private final int _columnIndex;
-	private final EvaluationTracker _tracker;
-	private final WorkbookEvaluator _bookEvaluator;
-
-	public OperationEvaluationContext(WorkbookEvaluator bookEvaluator, EvaluationWorkbook workbook, int sheetIndex, int srcRowNum,
-			int srcColNum, EvaluationTracker tracker) {
-		_bookEvaluator = bookEvaluator;
-		_workbook = workbook;
-		_sheetIndex = sheetIndex;
-		_rowIndex = srcRowNum;
-		_columnIndex = srcColNum;
-		_tracker = tracker;
-	}
-
-	public EvaluationWorkbook getWorkbook() {
-		return _workbook;
-	}
-
-	public int getRowIndex() {
-		return _rowIndex;
-	}
-
-	public int getColumnIndex() {
-		return _columnIndex;
-	}
-
-	SheetRangeEvaluator createExternSheetRefEvaluator(ExternSheetReferenceToken ptg) {
-		return createExternSheetRefEvaluator(ptg.getExternSheetIndex());
-	}
-	SheetRangeEvaluator createExternSheetRefEvaluator(String firstSheetName, String lastSheetName, int externalWorkbookNumber) {
+    public static final FreeRefFunction UDF = UserDefinedFunction.instance;
+    private final EvaluationWorkbook _workbook;
+    private final int _sheetIndex;
+    private final int _rowIndex;
+    private final int _columnIndex;
+    private final EvaluationTracker _tracker;
+    private final WorkbookEvaluator _bookEvaluator;
+
+    public OperationEvaluationContext(WorkbookEvaluator bookEvaluator, EvaluationWorkbook workbook, int sheetIndex, int srcRowNum,
+            int srcColNum, EvaluationTracker tracker) {
+        _bookEvaluator = bookEvaluator;
+        _workbook = workbook;
+        _sheetIndex = sheetIndex;
+        _rowIndex = srcRowNum;
+        _columnIndex = srcColNum;
+        _tracker = tracker;
+    }
+
+    public EvaluationWorkbook getWorkbook() {
+        return _workbook;
+    }
+
+    public int getRowIndex() {
+        return _rowIndex;
+    }
+
+    public int getColumnIndex() {
+        return _columnIndex;
+    }
+
+    SheetRangeEvaluator createExternSheetRefEvaluator(ExternSheetReferenceToken ptg) {
+        return createExternSheetRefEvaluator(ptg.getExternSheetIndex());
+    }
+    SheetRangeEvaluator createExternSheetRefEvaluator(String firstSheetName, String lastSheetName, int externalWorkbookNumber) {
         ExternalSheet externalSheet = _workbook.getExternalSheet(firstSheetName, lastSheetName, externalWorkbookNumber);
         return createExternSheetRefEvaluator(externalSheet);
     }
-	SheetRangeEvaluator createExternSheetRefEvaluator(int externSheetIndex) {
-		ExternalSheet externalSheet = _workbook.getExternalSheet(externSheetIndex);
+    SheetRangeEvaluator createExternSheetRefEvaluator(int externSheetIndex) {
+        ExternalSheet externalSheet = _workbook.getExternalSheet(externSheetIndex);
         return createExternSheetRefEvaluator(externalSheet);
-	}
-	SheetRangeEvaluator createExternSheetRefEvaluator(ExternalSheet externalSheet) {
-		WorkbookEvaluator targetEvaluator;
-		int otherFirstSheetIndex;
-		int otherLastSheetIndex = -1;
-		if (externalSheet == null || externalSheet.getWorkbookName() == null) {
-			// sheet is in same workbook
-			targetEvaluator = _bookEvaluator;
-			if(externalSheet == null) {
-			    otherFirstSheetIndex = 0;
-			} else {
-			    otherFirstSheetIndex = _workbook.getSheetIndex(externalSheet.getSheetName());
-			}
-			
-			if (externalSheet instanceof ExternalSheetRange) {
-			    String lastSheetName = ((ExternalSheetRange)externalSheet).getLastSheetName();
-			    otherLastSheetIndex = _workbook.getSheetIndex(lastSheetName);
-			}
-		} else {
-			// look up sheet by name from external workbook
-			String workbookName = externalSheet.getWorkbookName();
-			try {
-				targetEvaluator = _bookEvaluator.getOtherWorkbookEvaluator(workbookName);
-			} catch (WorkbookNotFoundException e) {
-				throw new RuntimeException(e.getMessage(), e);
-			}
-			
-			otherFirstSheetIndex = targetEvaluator.getSheetIndex(externalSheet.getSheetName());
+    }
+    SheetRangeEvaluator createExternSheetRefEvaluator(ExternalSheet externalSheet) {
+        WorkbookEvaluator targetEvaluator;
+        int otherFirstSheetIndex;
+        int otherLastSheetIndex = -1;
+        if (externalSheet == null || externalSheet.getWorkbookName() == null) {
+            // sheet is in same workbook
+            targetEvaluator = _bookEvaluator;
+            if(externalSheet == null) {
+                otherFirstSheetIndex = 0;
+            } else {
+                otherFirstSheetIndex = _workbook.getSheetIndex(externalSheet.getSheetName());
+            }
+            
+            if (externalSheet instanceof ExternalSheetRange) {
+                String lastSheetName = ((ExternalSheetRange)externalSheet).getLastSheetName();
+                otherLastSheetIndex = _workbook.getSheetIndex(lastSheetName);
+            }
+        } else {
+            // look up sheet by name from external workbook
+            String workbookName = externalSheet.getWorkbookName();
+            try {
+                targetEvaluator = _bookEvaluator.getOtherWorkbookEvaluator(workbookName);
+            } catch (WorkbookNotFoundException e) {
+                throw new RuntimeException(e.getMessage(), e);
+            }
+            
+            otherFirstSheetIndex = targetEvaluator.getSheetIndex(externalSheet.getSheetName());
             if (externalSheet instanceof ExternalSheetRange) {
                 String lastSheetName = ((ExternalSheetRange)externalSheet).getLastSheetName();
                 otherLastSheetIndex = targetEvaluator.getSheetIndex(lastSheetName);
             }
-			
-			if (otherFirstSheetIndex < 0) {
-				throw new RuntimeException("Invalid sheet name '" + externalSheet.getSheetName()
-						+ "' in bool '" + workbookName + "'.");
-			}
-		}
-		
-		if (otherLastSheetIndex == -1) {
-		    // Reference to just one sheet
-		    otherLastSheetIndex = otherFirstSheetIndex;
-		}
-		
-		SheetRefEvaluator[] evals = new SheetRefEvaluator[otherLastSheetIndex-otherFirstSheetIndex+1];
-		for (int i=0; i<evals.length; i++) {
-		    int otherSheetIndex = i+otherFirstSheetIndex;
-		    evals[i] = new SheetRefEvaluator(targetEvaluator, _tracker, otherSheetIndex); 
-		}
-		return new SheetRangeEvaluator(otherFirstSheetIndex, otherLastSheetIndex, evals);
-	}
-
-	/**
-	 * @return <code>null</code> if either workbook or sheet is not found
-	 */
-	private SheetRefEvaluator createExternSheetRefEvaluator(String workbookName, String sheetName) {
-		WorkbookEvaluator targetEvaluator;
-		if (workbookName == null) {
-			targetEvaluator = _bookEvaluator;
-		} else {
-			if (sheetName == null) {
-				throw new IllegalArgumentException("sheetName must not be null if workbookName is provided");
-			}
-			try {
-				targetEvaluator = _bookEvaluator.getOtherWorkbookEvaluator(workbookName);
-			} catch (WorkbookNotFoundException e) {
-				return null;
-			}
-		}
-		int otherSheetIndex = sheetName == null ? _sheetIndex : targetEvaluator.getSheetIndex(sheetName);
-		if (otherSheetIndex < 0) {
-			return null;
-		}
-		return new SheetRefEvaluator(targetEvaluator, _tracker, otherSheetIndex);
-	}
-
-	public SheetRangeEvaluator getRefEvaluatorForCurrentSheet() {
-		SheetRefEvaluator sre = new SheetRefEvaluator(_bookEvaluator, _tracker, _sheetIndex);
-		return new SheetRangeEvaluator(_sheetIndex, sre);
-	}
-
-
-
-	/**
-	 * Resolves a cell or area reference dynamically.
-	 * @param workbookName the name of the workbook containing the reference.  If <code>null</code>
-	 * the current workbook is assumed.  Note - to evaluate formulas which use multiple workbooks,
-	 * a {@link CollaboratingWorkbooksEnvironment} must be set up.
-	 * @param sheetName the name of the sheet containing the reference.  May be <code>null</code>
-	 * (when <tt>workbookName</tt> is also null) in which case the current workbook and sheet is
-	 * assumed.
-	 * @param refStrPart1 the single cell reference or first part of the area reference.  Must not
-	 * be <code>null</code>.
-	 * @param refStrPart2 the second part of the area reference. For single cell references this
-	 * parameter must be <code>null</code>
-	 * @param isA1Style specifies the format for <tt>refStrPart1</tt> and <tt>refStrPart2</tt>.
-	 * Pass <code>true</code> for 'A1' style and <code>false</code> for 'R1C1' style.
-	 * TODO - currently POI only supports 'A1' reference style
-	 * @return a {@link RefEval} or {@link AreaEval}
-	 */
-	public ValueEval getDynamicReference(String workbookName, String sheetName, String refStrPart1,
-			String refStrPart2, boolean isA1Style) {
-		if (!isA1Style) {
-			throw new RuntimeException("R1C1 style not supported yet");
-		}
-		SheetRefEvaluator se = createExternSheetRefEvaluator(workbookName, sheetName);
-		if (se == null) {
-			return ErrorEval.REF_INVALID;
-		}
-		SheetRangeEvaluator sre = new SheetRangeEvaluator(_sheetIndex, se);
-		
-		// ugly typecast - TODO - make spreadsheet version more easily accessible
-		SpreadsheetVersion ssVersion = ((FormulaParsingWorkbook)_workbook).getSpreadsheetVersion();
-
-		NameType part1refType = classifyCellReference(refStrPart1, ssVersion);
-		switch (part1refType) {
-			case BAD_CELL_OR_NAMED_RANGE:
-				return ErrorEval.REF_INVALID;
-			case NAMED_RANGE:
+            
+            if (otherFirstSheetIndex < 0) {
+                throw new RuntimeException("Invalid sheet name '" + externalSheet.getSheetName()
+                        + "' in bool '" + workbookName + "'.");
+            }
+        }
+        
+        if (otherLastSheetIndex == -1) {
+            // Reference to just one sheet
+            otherLastSheetIndex = otherFirstSheetIndex;
+        }
+        
+        SheetRefEvaluator[] evals = new SheetRefEvaluator[otherLastSheetIndex-otherFirstSheetIndex+1];
+        for (int i=0; i<evals.length; i++) {
+            int otherSheetIndex = i+otherFirstSheetIndex;
+            evals[i] = new SheetRefEvaluator(targetEvaluator, _tracker, otherSheetIndex); 
+        }
+        return new SheetRangeEvaluator(otherFirstSheetIndex, otherLastSheetIndex, evals);
+    }
+
+    /**
+     * @return <code>null</code> if either workbook or sheet is not found
+     */
+    private SheetRefEvaluator createExternSheetRefEvaluator(String workbookName, String sheetName) {
+        WorkbookEvaluator targetEvaluator;
+        if (workbookName == null) {
+            targetEvaluator = _bookEvaluator;
+        } else {
+            if (sheetName == null) {
+                throw new IllegalArgumentException("sheetName must not be null if workbookName is provided");
+            }
+            try {
+                targetEvaluator = _bookEvaluator.getOtherWorkbookEvaluator(workbookName);
+            } catch (WorkbookNotFoundException e) {
+                return null;
+            }
+        }
+        int otherSheetIndex = sheetName == null ? _sheetIndex : targetEvaluator.getSheetIndex(sheetName);
+        if (otherSheetIndex < 0) {
+            return null;
+        }
+        return new SheetRefEvaluator(targetEvaluator, _tracker, otherSheetIndex);
+    }
+
+    public SheetRangeEvaluator getRefEvaluatorForCurrentSheet() {
+        SheetRefEvaluator sre = new SheetRefEvaluator(_bookEvaluator, _tracker, _sheetIndex);
+        return new SheetRangeEvaluator(_sheetIndex, sre);
+    }
+
+
+
+    /**
+     * Resolves a cell or area reference dynamically.
+     * @param workbookName the name of the workbook containing the reference.  If <code>null</code>
+     * the current workbook is assumed.  Note - to evaluate formulas which use multiple workbooks,
+     * a {@link CollaboratingWorkbooksEnvironment} must be set up.
+     * @param sheetName the name of the sheet containing the reference.  May be <code>null</code>
+     * (when <tt>workbookName</tt> is also null) in which case the current workbook and sheet is
+     * assumed.
+     * @param refStrPart1 the single cell reference or first part of the area reference.  Must not
+     * be <code>null</code>.
+     * @param refStrPart2 the second part of the area reference. For single cell references this
+     * parameter must be <code>null</code>
+     * @param isA1Style specifies the format for <tt>refStrPart1</tt> and <tt>refStrPart2</tt>.
+     * Pass <code>true</code> for 'A1' style and <code>false</code> for 'R1C1' style.
+     * TODO - currently POI only supports 'A1' reference style
+     * @return a {@link RefEval} or {@link AreaEval}
+     */
+    public ValueEval getDynamicReference(String workbookName, String sheetName, String refStrPart1,
+            String refStrPart2, boolean isA1Style) {
+        if (!isA1Style) {
+            throw new RuntimeException("R1C1 style not supported yet");
+        }
+        SheetRefEvaluator se = createExternSheetRefEvaluator(workbookName, sheetName);
+        if (se == null) {
+            return ErrorEval.REF_INVALID;
+        }
+        SheetRangeEvaluator sre = new SheetRangeEvaluator(_sheetIndex, se);
+        
+        // ugly typecast - TODO - make spreadsheet version more easily accessible
+        SpreadsheetVersion ssVersion = ((FormulaParsingWorkbook)_workbook).getSpreadsheetVersion();
+
+        NameType part1refType = classifyCellReference(refStrPart1, ssVersion);
+        switch (part1refType) {
+            case BAD_CELL_OR_NAMED_RANGE:
+                return ErrorEval.REF_INVALID;
+            case NAMED_RANGE:
                 EvaluationName nm = ((FormulaParsingWorkbook)_workbook).getName(refStrPart1, _sheetIndex);
                 if(!nm.isRange()){
                     throw new RuntimeException("Specified name '" + refStrPart1 + "' is not a range as expected.");
                 }
                 return _bookEvaluator.evaluateNameFormula(nm.getNameDefinition(), this);
-		}
-		if (refStrPart2 == null) {
-			// no ':'
-			switch (part1refType) {
-				case COLUMN:
-				case ROW:
-					return ErrorEval.REF_INVALID;
-				case CELL:
-					CellReference cr = new CellReference(refStrPart1);
-					return new LazyRefEval(cr.getRow(), cr.getCol(), sre);
-			}
-			throw new IllegalStateException("Unexpected reference classification of '" + refStrPart1 + "'.");
-		}
-		NameType part2refType = classifyCellReference(refStrPart1, ssVersion);
-		switch (part2refType) {
-			case BAD_CELL_OR_NAMED_RANGE:
-				return ErrorEval.REF_INVALID;
-			case NAMED_RANGE:
-				throw new RuntimeException("Cannot evaluate '" + refStrPart1
-						+ "'. Indirect evaluation of defined names not supported yet");
-		}
-
-		if (part2refType != part1refType) {
-			// LHS and RHS of ':' must be compatible
-			return ErrorEval.REF_INVALID;
-		}
-		int firstRow, firstCol, lastRow, lastCol;
-		switch (part1refType) {
-			case COLUMN:
+        }
+        if (refStrPart2 == null) {
+            // no ':'
+            switch (part1refType) {
+                case COLUMN:
+                case ROW:
+                    return ErrorEval.REF_INVALID;
+                case CELL:
+                    CellReference cr = new CellReference(refStrPart1);
+                    return new LazyRefEval(cr.getRow(), cr.getCol(), sre);
+            }
+            throw new IllegalStateException("Unexpected reference classification of '" + refStrPart1 + "'.");
+        }
+        NameType part2refType = classifyCellReference(refStrPart1, ssVersion);
+        switch (part2refType) {
+            case BAD_CELL_OR_NAMED_RANGE:
+                return ErrorEval.REF_INVALID;
+            case NAMED_RANGE:
+                throw new RuntimeException("Cannot evaluate '" + refStrPart1
+                        + "'. Indirect evaluation of defined names not supported yet");
+        }
+
+        if (part2refType != part1refType) {
+            // LHS and RHS of ':' must be compatible
+            return ErrorEval.REF_INVALID;
+        }
+        int firstRow, firstCol, lastRow, lastCol;
+        switch (part1refType) {
+            case COLUMN:
                 firstRow =0;
                 if (part2refType.equals(NameType.COLUMN))
                 {
@@ -252,7 +252,7 @@ public final class OperationEvaluationCo
                     lastCol = parseColRef(refStrPart2);
                 }
                 break;
-			case ROW:
+            case ROW:
                 // support of cell range in the form of integer:integer
                 firstCol = 0;
                 if (part2refType.equals(NameType.ROW))
@@ -265,61 +265,61 @@ public final class OperationEvaluationCo
                     firstRow = parseRowRef(refStrPart1);
                     lastRow = parseRowRef(refStrPart2);
                 }
-				break;
-			case CELL:
-				CellReference cr;
-				cr = new CellReference(refStrPart1);
-				firstRow = cr.getRow();
-				firstCol = cr.getCol();
-				cr = new CellReference(refStrPart2);
-				lastRow = cr.getRow();
-				lastCol = cr.getCol();
-				break;
-			default:
-				throw new IllegalStateException("Unexpected reference classification of '" + refStrPart1 + "'.");
-		}
-		return new LazyAreaEval(firstRow, firstCol, lastRow, lastCol, sre);
-	}
-
-	private static int parseRowRef(String refStrPart) {
-		return CellReference.convertColStringToIndex(refStrPart);
-	}
-
-	private static int parseColRef(String refStrPart) {
-		return Integer.parseInt(refStrPart) - 1;
-	}
-
-	private static NameType classifyCellReference(String str, SpreadsheetVersion ssVersion) {
-		int len = str.length();
-		if (len < 1) {
-			return CellReference.NameType.BAD_CELL_OR_NAMED_RANGE;
-		}
-		return CellReference.classifyCellReference(str, ssVersion);
-	}
-
-	public FreeRefFunction findUserDefinedFunction(String functionName) {
-		return _bookEvaluator.findUserDefinedFunction(functionName);
-	}
-
-	public ValueEval getRefEval(int rowIndex, int columnIndex) {
-	    SheetRangeEvaluator sre = getRefEvaluatorForCurrentSheet();
-		return new LazyRefEval(rowIndex, columnIndex, sre);
-	}
-	public ValueEval getRef3DEval(Ref3DPtg rptg) {
-	    SheetRangeEvaluator sre = createExternSheetRefEvaluator(rptg.getExternSheetIndex());
-		return new LazyRefEval(rptg.getRow(), rptg.getColumn(), sre);
-	}
+                break;
+            case CELL:
+                CellReference cr;
+                cr = new CellReference(refStrPart1);
+                firstRow = cr.getRow();
+                firstCol = cr.getCol();
+                cr = new CellReference(refStrPart2);
+                lastRow = cr.getRow();
+                lastCol = cr.getCol();
+                break;
+            default:
+                throw new IllegalStateException("Unexpected reference classification of '" + refStrPart1 + "'.");
+        }
+        return new LazyAreaEval(firstRow, firstCol, lastRow, lastCol, sre);
+    }
+
+    private static int parseRowRef(String refStrPart) {
+        return CellReference.convertColStringToIndex(refStrPart);
+    }
+
+    private static int parseColRef(String refStrPart) {
+        return Integer.parseInt(refStrPart) - 1;
+    }
+
+    private static NameType classifyCellReference(String str, SpreadsheetVersion ssVersion) {
+        int len = str.length();
+        if (len < 1) {
+            return CellReference.NameType.BAD_CELL_OR_NAMED_RANGE;
+        }
+        return CellReference.classifyCellReference(str, ssVersion);
+    }
+
+    public FreeRefFunction findUserDefinedFunction(String functionName) {
+        return _bookEvaluator.findUserDefinedFunction(functionName);
+    }
+
+    public ValueEval getRefEval(int rowIndex, int columnIndex) {
+        SheetRangeEvaluator sre = getRefEvaluatorForCurrentSheet();
+        return new LazyRefEval(rowIndex, columnIndex, sre);
+    }
+    public ValueEval getRef3DEval(Ref3DPtg rptg) {
+        SheetRangeEvaluator sre = createExternSheetRefEvaluator(rptg.getExternSheetIndex());
+        return new LazyRefEval(rptg.getRow(), rptg.getColumn(), sre);
+    }
     public ValueEval getRef3DEval(Ref3DPxg rptg) {
         SheetRangeEvaluator sre = createExternSheetRefEvaluator(
                 rptg.getSheetName(), rptg.getLastSheetName(), rptg.getExternalWorkbookNumber());
         return new LazyRefEval(rptg.getRow(), rptg.getColumn(), sre);
     }
     
-	public ValueEval getAreaEval(int firstRowIndex, int firstColumnIndex,
-			int lastRowIndex, int lastColumnIndex) {
-	    SheetRangeEvaluator sre = getRefEvaluatorForCurrentSheet();
-		return new LazyAreaEval(firstRowIndex, firstColumnIndex, lastRowIndex, lastColumnIndex, sre);
-	}
+    public ValueEval getAreaEval(int firstRowIndex, int firstColumnIndex,
+            int lastRowIndex, int lastColumnIndex) {
+        SheetRangeEvaluator sre = getRefEvaluatorForCurrentSheet();
+        return new LazyAreaEval(firstRowIndex, firstColumnIndex, lastRowIndex, lastColumnIndex, sre);
+    }
     public ValueEval getArea3DEval(Area3DPtg aptg) {
         SheetRangeEvaluator sre = createExternSheetRefEvaluator(aptg.getExternSheetIndex());
         return new LazyAreaEval(aptg.getFirstRow(), aptg.getFirstColumn(),
@@ -348,8 +348,8 @@ public final class OperationEvaluationCo
         );
         return getExternalNameXEval(externName, workbookName);
     }
-	public ValueEval getNameXEval(NameXPxg nameXPxg) {
-	    ExternalSheet externSheet = _workbook.getExternalSheet(nameXPxg.getSheetName(), null, nameXPxg.getExternalWorkbookNumber());
+    public ValueEval getNameXEval(NameXPxg nameXPxg) {
+        ExternalSheet externSheet = _workbook.getExternalSheet(nameXPxg.getSheetName(), null, nameXPxg.getExternalWorkbookNumber());
         if(externSheet == null || externSheet.getWorkbookName() == null) {
             // External reference to our own workbook's name
             return getLocalNameXEval(nameXPxg);
@@ -363,8 +363,8 @@ public final class OperationEvaluationCo
               nameXPxg.getExternalWorkbookNumber()
         );
         return getExternalNameXEval(externName, workbookName);
-	}
-	
+    }
+    
     private ValueEval getLocalNameXEval(NameXPxg nameXPxg) {
         // Look up the sheet, if present
         int sIdx = -1;
@@ -383,7 +383,7 @@ public final class OperationEvaluationCo
             return new FunctionNameEval(name);
         }
     }
-	private ValueEval getLocalNameXEval(NameXPtg nameXPtg) {
+    private ValueEval getLocalNameXEval(NameXPtg nameXPtg) {
         String name = _workbook.resolveNameXText(nameXPtg);
         
         // Try to parse it as a name
@@ -406,8 +406,11 @@ public final class OperationEvaluationCo
             // Must be an external function
             return new FunctionNameEval(name);
         }
-	}
-	
+    }
+    public int getSheetIndex() {
+        return _sheetIndex;
+    }
+    
     private ValueEval getExternalNameXEval(ExternalName externName, String workbookName) {
         try {
             // Fetch the workbook this refers to, and the name as defined with that

Modified: poi/trunk/src/java/org/apache/poi/ss/formula/WorkbookEvaluator.java
URL: http://svn.apache.org/viewvc/poi/trunk/src/java/org/apache/poi/ss/formula/WorkbookEvaluator.java?rev=1747657&r1=1747656&r2=1747657&view=diff
==============================================================================
--- poi/trunk/src/java/org/apache/poi/ss/formula/WorkbookEvaluator.java (original)
+++ poi/trunk/src/java/org/apache/poi/ss/formula/WorkbookEvaluator.java Fri Jun 10 07:41:09 2016
@@ -26,20 +26,60 @@ import java.util.TreeSet;
 
 import org.apache.poi.ss.formula.CollaboratingWorkbooksEnvironment.WorkbookNotFoundException;
 import org.apache.poi.ss.formula.atp.AnalysisToolPak;
-import org.apache.poi.ss.formula.eval.*;
+import org.apache.poi.ss.formula.eval.BlankEval;
+import org.apache.poi.ss.formula.eval.BoolEval;
+import org.apache.poi.ss.formula.eval.ErrorEval;
+import org.apache.poi.ss.formula.eval.EvaluationException;
+import org.apache.poi.ss.formula.eval.ExternalNameEval;
+import org.apache.poi.ss.formula.eval.FunctionEval;
+import org.apache.poi.ss.formula.eval.FunctionNameEval;
+import org.apache.poi.ss.formula.eval.MissingArgEval;
+import org.apache.poi.ss.formula.eval.NotImplementedException;
+import org.apache.poi.ss.formula.eval.NumberEval;
+import org.apache.poi.ss.formula.eval.OperandResolver;
+import org.apache.poi.ss.formula.eval.StringEval;
+import org.apache.poi.ss.formula.eval.ValueEval;
 import org.apache.poi.ss.formula.function.FunctionMetadataRegistry;
 import org.apache.poi.ss.formula.functions.Choose;
 import org.apache.poi.ss.formula.functions.FreeRefFunction;
 import org.apache.poi.ss.formula.functions.Function;
 import org.apache.poi.ss.formula.functions.IfFunc;
-import org.apache.poi.ss.formula.ptg.*;
+import org.apache.poi.ss.formula.ptg.Area3DPtg;
+import org.apache.poi.ss.formula.ptg.Area3DPxg;
+import org.apache.poi.ss.formula.ptg.AreaErrPtg;
+import org.apache.poi.ss.formula.ptg.AreaPtg;
+import org.apache.poi.ss.formula.ptg.AttrPtg;
+import org.apache.poi.ss.formula.ptg.BoolPtg;
+import org.apache.poi.ss.formula.ptg.ControlPtg;
+import org.apache.poi.ss.formula.ptg.DeletedArea3DPtg;
+import org.apache.poi.ss.formula.ptg.DeletedRef3DPtg;
+import org.apache.poi.ss.formula.ptg.ErrPtg;
+import org.apache.poi.ss.formula.ptg.ExpPtg;
+import org.apache.poi.ss.formula.ptg.FuncVarPtg;
+import org.apache.poi.ss.formula.ptg.IntPtg;
+import org.apache.poi.ss.formula.ptg.MemAreaPtg;
+import org.apache.poi.ss.formula.ptg.MemErrPtg;
+import org.apache.poi.ss.formula.ptg.MemFuncPtg;
+import org.apache.poi.ss.formula.ptg.MissingArgPtg;
+import org.apache.poi.ss.formula.ptg.NamePtg;
+import org.apache.poi.ss.formula.ptg.NameXPtg;
+import org.apache.poi.ss.formula.ptg.NameXPxg;
+import org.apache.poi.ss.formula.ptg.NumberPtg;
+import org.apache.poi.ss.formula.ptg.OperationPtg;
+import org.apache.poi.ss.formula.ptg.Ptg;
+import org.apache.poi.ss.formula.ptg.Ref3DPtg;
+import org.apache.poi.ss.formula.ptg.Ref3DPxg;
+import org.apache.poi.ss.formula.ptg.RefErrorPtg;
+import org.apache.poi.ss.formula.ptg.RefPtg;
+import org.apache.poi.ss.formula.ptg.StringPtg;
+import org.apache.poi.ss.formula.ptg.UnionPtg;
+import org.apache.poi.ss.formula.ptg.UnknownPtg;
 import org.apache.poi.ss.formula.udf.AggregatingUDFFinder;
 import org.apache.poi.ss.formula.udf.UDFFinder;
 import org.apache.poi.ss.usermodel.Cell;
 import org.apache.poi.ss.util.CellReference;
 import org.apache.poi.util.POILogFactory;
 import org.apache.poi.util.POILogger;
-
 /**
  * Evaluates formula cells.<p/>
  *
@@ -53,39 +93,39 @@ import org.apache.poi.util.POILogger;
  * @author Thies Wellpott (debug output enhancements)
  */
 public final class WorkbookEvaluator {
-	
-	private static final POILogger LOG = POILogFactory.getLogger(WorkbookEvaluator.class);
+    
+    private static final POILogger LOG = POILogFactory.getLogger(WorkbookEvaluator.class);
 
     private final EvaluationWorkbook _workbook;
-	private EvaluationCache _cache;
-	/** part of cache entry key (useful when evaluating multiple workbooks) */
-	private int _workbookIx;
-
-	private final IEvaluationListener _evaluationListener;
-	private final Map<EvaluationSheet, Integer> _sheetIndexesBySheet;
-	private final Map<String, Integer> _sheetIndexesByName;
-	private CollaboratingWorkbooksEnvironment _collaboratingWorkbookEnvironment;
-	private final IStabilityClassifier _stabilityClassifier;
-	private final AggregatingUDFFinder _udfFinder;
+    private EvaluationCache _cache;
+    /** part of cache entry key (useful when evaluating multiple workbooks) */
+    private int _workbookIx;
+
+    private final IEvaluationListener _evaluationListener;
+    private final Map<EvaluationSheet, Integer> _sheetIndexesBySheet;
+    private final Map<String, Integer> _sheetIndexesByName;
+    private CollaboratingWorkbooksEnvironment _collaboratingWorkbookEnvironment;
+    private final IStabilityClassifier _stabilityClassifier;
+    private final AggregatingUDFFinder _udfFinder;
 
     private boolean _ignoreMissingWorkbooks = false;
 
-	/**
-	 * @param udfFinder pass <code>null</code> for default (AnalysisToolPak only)
-	 */
-	public WorkbookEvaluator(EvaluationWorkbook workbook, IStabilityClassifier stabilityClassifier, UDFFinder udfFinder) {
-		this (workbook, null, stabilityClassifier, udfFinder);
-	}
-	/* package */ WorkbookEvaluator(EvaluationWorkbook workbook, IEvaluationListener evaluationListener,
-			IStabilityClassifier stabilityClassifier, UDFFinder udfFinder) {
-		_workbook = workbook;
-		_evaluationListener = evaluationListener;
-		_cache = new EvaluationCache(evaluationListener);
-		_sheetIndexesBySheet = new IdentityHashMap<EvaluationSheet, Integer>();
-		_sheetIndexesByName = new IdentityHashMap<String, Integer>();
-		_collaboratingWorkbookEnvironment = CollaboratingWorkbooksEnvironment.EMPTY;
-		_workbookIx = 0;
-		_stabilityClassifier = stabilityClassifier;
+    /**
+     * @param udfFinder pass <code>null</code> for default (AnalysisToolPak only)
+     */
+    public WorkbookEvaluator(EvaluationWorkbook workbook, IStabilityClassifier stabilityClassifier, UDFFinder udfFinder) {
+        this (workbook, null, stabilityClassifier, udfFinder);
+    }
+    /* package */ WorkbookEvaluator(EvaluationWorkbook workbook, IEvaluationListener evaluationListener,
+            IStabilityClassifier stabilityClassifier, UDFFinder udfFinder) {
+        _workbook = workbook;
+        _evaluationListener = evaluationListener;
+        _cache = new EvaluationCache(evaluationListener);
+        _sheetIndexesBySheet = new IdentityHashMap<EvaluationSheet, Integer>();
+        _sheetIndexesByName = new IdentityHashMap<String, Integer>();
+        _collaboratingWorkbookEnvironment = CollaboratingWorkbooksEnvironment.EMPTY;
+        _workbookIx = 0;
+        _stabilityClassifier = stabilityClassifier;
 
         AggregatingUDFFinder defaultToolkit = // workbook can be null in unit tests
                 workbook == null ? null : (AggregatingUDFFinder)workbook.getUDFFinder();
@@ -93,492 +133,492 @@ public final class WorkbookEvaluator {
             defaultToolkit.add(udfFinder);
         }
         _udfFinder = defaultToolkit;
-	}
+    }
+
+    /**
+     * also for debug use. Used in toString methods
+     */
+    /* package */ String getSheetName(int sheetIndex) {
+        return _workbook.getSheetName(sheetIndex);
+    }
 
-	/**
-	 * also for debug use. Used in toString methods
-	 */
-	/* package */ String getSheetName(int sheetIndex) {
-		return _workbook.getSheetName(sheetIndex);
-	}
-
-	/* package */ EvaluationSheet getSheet(int sheetIndex) {
-		return _workbook.getSheet(sheetIndex);
-	}
-	
-	/* package */ EvaluationWorkbook getWorkbook() {
-		return _workbook;
-	}
-
-	/* package */ EvaluationName getName(String name, int sheetIndex) {
-	    EvaluationName evalName = _workbook.getName(name, sheetIndex);
-	    return evalName;
-	}
-
-	private static boolean isDebugLogEnabled() {
-		return LOG.check(POILogger.DEBUG);
-	}
-	private static boolean isInfoLogEnabled() {
-		return LOG.check(POILogger.INFO);
-	}
-	private static void logDebug(String s) {
-		if (isDebugLogEnabled()) {
-			LOG.log(POILogger.DEBUG, s);
-		}
-	}
-	private static void logInfo(String s) {
-		if (isInfoLogEnabled()) {
-			LOG.log(POILogger.INFO, s);
-		}
-	}
-	/* package */ void attachToEnvironment(CollaboratingWorkbooksEnvironment collaboratingWorkbooksEnvironment, EvaluationCache cache, int workbookIx) {
-		_collaboratingWorkbookEnvironment = collaboratingWorkbooksEnvironment;
-		_cache = cache;
-		_workbookIx = workbookIx;
-	}
-	/* package */ CollaboratingWorkbooksEnvironment getEnvironment() {
-		return _collaboratingWorkbookEnvironment;
-	}
-
-	/**
-	 * Discards the current workbook environment and attaches to the default 'empty' environment.
-	 * Also resets evaluation cache.
-	 */
-	/* package */ void detachFromEnvironment() {
-		_collaboratingWorkbookEnvironment = CollaboratingWorkbooksEnvironment.EMPTY;
-		_cache = new EvaluationCache(_evaluationListener);
-		_workbookIx = 0;
-	}
-	/**
-	 * @return the evaluator for another workbook which is part of the same {@link CollaboratingWorkbooksEnvironment}
-	 */
-	/* package */ WorkbookEvaluator getOtherWorkbookEvaluator(String workbookName) throws WorkbookNotFoundException {
-		return _collaboratingWorkbookEnvironment.getWorkbookEvaluator(workbookName);
-	}
-
-	/* package */ IEvaluationListener getEvaluationListener() {
-		return _evaluationListener;
-	}
-
-	/**
-	 * Should be called whenever there are changes to input cells in the evaluated workbook.
-	 * Failure to call this method after changing cell values will cause incorrect behaviour
-	 * of the evaluate~ methods of this class
-	 */
-	public void clearAllCachedResultValues() {
-		_cache.clear();
-		_sheetIndexesBySheet.clear();
-	}
-
-	/**
-	 * Should be called to tell the cell value cache that the specified (value or formula) cell
-	 * has changed.
-	 */
-	public void notifyUpdateCell(EvaluationCell cell) {
-		int sheetIndex = getSheetIndex(cell.getSheet());
-		_cache.notifyUpdateCell(_workbookIx, sheetIndex, cell);
-	}
-	/**
-	 * Should be called to tell the cell value cache that the specified cell has just been
-	 * deleted.
-	 */
-	public void notifyDeleteCell(EvaluationCell cell) {
-		int sheetIndex = getSheetIndex(cell.getSheet());
-		_cache.notifyDeleteCell(_workbookIx, sheetIndex, cell);
-	}
-	
-	private int getSheetIndex(EvaluationSheet sheet) {
-		Integer result = _sheetIndexesBySheet.get(sheet);
-		if (result == null) {
-			int sheetIndex = _workbook.getSheetIndex(sheet);
-			if (sheetIndex < 0) {
-				throw new RuntimeException("Specified sheet from a different book");
-			}
-			result = Integer.valueOf(sheetIndex);
-			_sheetIndexesBySheet.put(sheet, result);
-		}
-		return result.intValue();
-	}
-
-	public ValueEval evaluate(EvaluationCell srcCell) {
-		int sheetIndex = getSheetIndex(srcCell.getSheet());
-		return evaluateAny(srcCell, sheetIndex, srcCell.getRowIndex(), srcCell.getColumnIndex(), new EvaluationTracker(_cache));
-	}
-
-	/**
-	 * Case-insensitive.
-	 * @return -1 if sheet with specified name does not exist
-	 */
-	/* package */ int getSheetIndex(String sheetName) {
-		Integer result = _sheetIndexesByName.get(sheetName);
-		if (result == null) {
-			int sheetIndex = _workbook.getSheetIndex(sheetName);
-			if (sheetIndex < 0) {
-				return -1;
-			}
-			result = Integer.valueOf(sheetIndex);
-			_sheetIndexesByName.put(sheetName, result);
-		}
-		return result.intValue();
-	}
-	
-	/* package */ int getSheetIndexByExternIndex(int externSheetIndex) {
-	   return _workbook.convertFromExternSheetIndex(externSheetIndex);
-	}
-
-
-	/**
-	 * @return never <code>null</code>, never {@link BlankEval}
-	 */
-	private ValueEval evaluateAny(EvaluationCell srcCell, int sheetIndex,
-				int rowIndex, int columnIndex, EvaluationTracker tracker) {
-
-		// avoid tracking dependencies to cells that have constant definition
-		boolean shouldCellDependencyBeRecorded = _stabilityClassifier == null ? true
-					: !_stabilityClassifier.isCellFinal(sheetIndex, rowIndex, columnIndex);
-		if (srcCell == null || srcCell.getCellType() != Cell.CELL_TYPE_FORMULA) {
-			ValueEval result = getValueFromNonFormulaCell(srcCell);
-			if (shouldCellDependencyBeRecorded) {
-				tracker.acceptPlainValueDependency(_workbookIx, sheetIndex, rowIndex, columnIndex, result);
-			}
-			return result;
-		}
-
-		FormulaCellCacheEntry cce = _cache.getOrCreateFormulaCellEntry(srcCell);
-		if (shouldCellDependencyBeRecorded || cce.isInputSensitive()) {
-			tracker.acceptFormulaDependency(cce);
-		}
-		IEvaluationListener evalListener = _evaluationListener;
-		ValueEval result;
-		if (cce.getValue() == null) {
-			if (!tracker.startEvaluate(cce)) {
-				return ErrorEval.CIRCULAR_REF_ERROR;
-			}
-			OperationEvaluationContext ec = new OperationEvaluationContext(this, _workbook, sheetIndex, rowIndex, columnIndex, tracker);
-
-			try {
-
-				Ptg[] ptgs = _workbook.getFormulaTokens(srcCell);
-				if (evalListener == null) {
-					result = evaluateFormula(ec, ptgs);
-				} else {
-					evalListener.onStartEvaluate(srcCell, cce);
-					result = evaluateFormula(ec, ptgs);
-					evalListener.onEndEvaluate(cce, result);
-				}
-
-				tracker.updateCacheResult(result);
-			}
-			 catch (NotImplementedException e) {
-				throw addExceptionInfo(e, sheetIndex, rowIndex, columnIndex);
-			 } catch (RuntimeException re) {
-				 if (re.getCause() instanceof WorkbookNotFoundException && _ignoreMissingWorkbooks) {
- 					logInfo(re.getCause().getMessage() + " - Continuing with cached value!");
- 					switch(srcCell.getCachedFormulaResultType()) {
-	 					case Cell.CELL_TYPE_NUMERIC:
-	 						result = new NumberEval(srcCell.getNumericCellValue());
-	 						break;
-	 					case Cell.CELL_TYPE_STRING:
-	 						result =  new StringEval(srcCell.getStringCellValue());
-	 						break;
-	 					case Cell.CELL_TYPE_BLANK:
-	 						result = BlankEval.instance;
-	 						break;
-	 					case Cell.CELL_TYPE_BOOLEAN:
-	 						result =  BoolEval.valueOf(srcCell.getBooleanCellValue());
-	 						break;
-	 					case Cell.CELL_TYPE_ERROR:
-							result =  ErrorEval.valueOf(srcCell.getErrorCellValue());
-							break;
-	 					case Cell.CELL_TYPE_FORMULA:
-						default:
-							throw new RuntimeException("Unexpected cell type '" + srcCell.getCellType()+"' found!");
- 					}
-				 } else {
-					 throw re;
-				 }
-			 } finally {
-				tracker.endEvaluate(cce);
-			}
-		} else {
-			if(evalListener != null) {
-				evalListener.onCacheHit(sheetIndex, rowIndex, columnIndex, cce.getValue());
-			}
-			return cce.getValue();
-		}
-		if (isDebugLogEnabled()) {
-			String sheetName = getSheetName(sheetIndex);
-			CellReference cr = new CellReference(rowIndex, columnIndex);
-			logDebug("Evaluated " + sheetName + "!" + cr.formatAsString() + " to " + result.toString());
-		}
-		// Usually (result === cce.getValue())
-		// But sometimes: (result==ErrorEval.CIRCULAR_REF_ERROR, cce.getValue()==null)
-		// When circular references are detected, the cache entry is only updated for
-		// the top evaluation frame
-		return result;
-	}
-
-	/**
-	 * Adds the current cell reference to the exception for easier debugging.
-	 * Would be nice to get the formula text as well, but that seems to require
-	 * too much digging around and casting to get the FormulaRenderingWorkbook.
-	 */
-	private NotImplementedException addExceptionInfo(NotImplementedException inner, int sheetIndex, int rowIndex, int columnIndex) {
-
-		try {
-			String sheetName = _workbook.getSheetName(sheetIndex);
-			CellReference cr = new CellReference(sheetName, rowIndex, columnIndex, false, false);
-			String msg =  "Error evaluating cell " + cr.formatAsString();
-			return new NotImplementedException(msg, inner);
-		} catch (Exception e) {
-			// avoid bombing out during exception handling
-		    LOG.log(POILogger.ERROR, "Can't add exception info", e);
-			return inner; // preserve original exception
-		}
-	}
-	/**
-	 * Gets the value from a non-formula cell.
-	 * @param cell may be <code>null</code>
-	 * @return {@link BlankEval} if cell is <code>null</code> or blank, never <code>null</code>
-	 */
-	/* package */ static ValueEval getValueFromNonFormulaCell(EvaluationCell cell) {
-		if (cell == null) {
-			return BlankEval.instance;
-		}
-		int cellType = cell.getCellType();
-		switch (cellType) {
-			case Cell.CELL_TYPE_NUMERIC:
-				return new NumberEval(cell.getNumericCellValue());
-			case Cell.CELL_TYPE_STRING:
-				return new StringEval(cell.getStringCellValue());
-			case Cell.CELL_TYPE_BOOLEAN:
-				return BoolEval.valueOf(cell.getBooleanCellValue());
-			case Cell.CELL_TYPE_BLANK:
-				return BlankEval.instance;
-			case Cell.CELL_TYPE_ERROR:
-				return ErrorEval.valueOf(cell.getErrorCellValue());
-		}
-		throw new RuntimeException("Unexpected cell type (" + cellType + ")");
-	}
+    /* package */ EvaluationSheet getSheet(int sheetIndex) {
+        return _workbook.getSheet(sheetIndex);
+    }
+    
+    /* package */ EvaluationWorkbook getWorkbook() {
+        return _workbook;
+    }
+
+    /* package */ EvaluationName getName(String name, int sheetIndex) {
+        EvaluationName evalName = _workbook.getName(name, sheetIndex);
+        return evalName;
+    }
+
+    private static boolean isDebugLogEnabled() {
+        return LOG.check(POILogger.DEBUG);
+    }
+    private static boolean isInfoLogEnabled() {
+        return LOG.check(POILogger.INFO);
+    }
+    private static void logDebug(String s) {
+        if (isDebugLogEnabled()) {
+            LOG.log(POILogger.DEBUG, s);
+        }
+    }
+    private static void logInfo(String s) {
+        if (isInfoLogEnabled()) {
+            LOG.log(POILogger.INFO, s);
+        }
+    }
+    /* package */ void attachToEnvironment(CollaboratingWorkbooksEnvironment collaboratingWorkbooksEnvironment, EvaluationCache cache, int workbookIx) {
+        _collaboratingWorkbookEnvironment = collaboratingWorkbooksEnvironment;
+        _cache = cache;
+        _workbookIx = workbookIx;
+    }
+    /* package */ CollaboratingWorkbooksEnvironment getEnvironment() {
+        return _collaboratingWorkbookEnvironment;
+    }
+
+    /**
+     * Discards the current workbook environment and attaches to the default 'empty' environment.
+     * Also resets evaluation cache.
+     */
+    /* package */ void detachFromEnvironment() {
+        _collaboratingWorkbookEnvironment = CollaboratingWorkbooksEnvironment.EMPTY;
+        _cache = new EvaluationCache(_evaluationListener);
+        _workbookIx = 0;
+    }
+    /**
+     * @return the evaluator for another workbook which is part of the same {@link CollaboratingWorkbooksEnvironment}
+     */
+    /* package */ WorkbookEvaluator getOtherWorkbookEvaluator(String workbookName) throws WorkbookNotFoundException {
+        return _collaboratingWorkbookEnvironment.getWorkbookEvaluator(workbookName);
+    }
+
+    /* package */ IEvaluationListener getEvaluationListener() {
+        return _evaluationListener;
+    }
+
+    /**
+     * Should be called whenever there are changes to input cells in the evaluated workbook.
+     * Failure to call this method after changing cell values will cause incorrect behaviour
+     * of the evaluate~ methods of this class
+     */
+    public void clearAllCachedResultValues() {
+        _cache.clear();
+        _sheetIndexesBySheet.clear();
+    }
+
+    /**
+     * Should be called to tell the cell value cache that the specified (value or formula) cell
+     * has changed.
+     */
+    public void notifyUpdateCell(EvaluationCell cell) {
+        int sheetIndex = getSheetIndex(cell.getSheet());
+        _cache.notifyUpdateCell(_workbookIx, sheetIndex, cell);
+    }
+    /**
+     * Should be called to tell the cell value cache that the specified cell has just been
+     * deleted.
+     */
+    public void notifyDeleteCell(EvaluationCell cell) {
+        int sheetIndex = getSheetIndex(cell.getSheet());
+        _cache.notifyDeleteCell(_workbookIx, sheetIndex, cell);
+    }
+    
+    private int getSheetIndex(EvaluationSheet sheet) {
+        Integer result = _sheetIndexesBySheet.get(sheet);
+        if (result == null) {
+            int sheetIndex = _workbook.getSheetIndex(sheet);
+            if (sheetIndex < 0) {
+                throw new RuntimeException("Specified sheet from a different book");
+            }
+            result = Integer.valueOf(sheetIndex);
+            _sheetIndexesBySheet.put(sheet, result);
+        }
+        return result.intValue();
+    }
+
+    public ValueEval evaluate(EvaluationCell srcCell) {
+        int sheetIndex = getSheetIndex(srcCell.getSheet());
+        return evaluateAny(srcCell, sheetIndex, srcCell.getRowIndex(), srcCell.getColumnIndex(), new EvaluationTracker(_cache));
+    }
+
+    /**
+     * Case-insensitive.
+     * @return -1 if sheet with specified name does not exist
+     */
+    /* package */ int getSheetIndex(String sheetName) {
+        Integer result = _sheetIndexesByName.get(sheetName);
+        if (result == null) {
+            int sheetIndex = _workbook.getSheetIndex(sheetName);
+            if (sheetIndex < 0) {
+                return -1;
+            }
+            result = Integer.valueOf(sheetIndex);
+            _sheetIndexesByName.put(sheetName, result);
+        }
+        return result.intValue();
+    }
+    
+    /* package */ int getSheetIndexByExternIndex(int externSheetIndex) {
+       return _workbook.convertFromExternSheetIndex(externSheetIndex);
+    }
+
+
+    /**
+     * @return never <code>null</code>, never {@link BlankEval}
+     */
+    private ValueEval evaluateAny(EvaluationCell srcCell, int sheetIndex,
+                int rowIndex, int columnIndex, EvaluationTracker tracker) {
+
+        // avoid tracking dependencies to cells that have constant definition
+        boolean shouldCellDependencyBeRecorded = _stabilityClassifier == null ? true
+                    : !_stabilityClassifier.isCellFinal(sheetIndex, rowIndex, columnIndex);
+        if (srcCell == null || srcCell.getCellType() != Cell.CELL_TYPE_FORMULA) {
+            ValueEval result = getValueFromNonFormulaCell(srcCell);
+            if (shouldCellDependencyBeRecorded) {
+                tracker.acceptPlainValueDependency(_workbookIx, sheetIndex, rowIndex, columnIndex, result);
+            }
+            return result;
+        }
+
+        FormulaCellCacheEntry cce = _cache.getOrCreateFormulaCellEntry(srcCell);
+        if (shouldCellDependencyBeRecorded || cce.isInputSensitive()) {
+            tracker.acceptFormulaDependency(cce);
+        }
+        IEvaluationListener evalListener = _evaluationListener;
+        ValueEval result;
+        if (cce.getValue() == null) {
+            if (!tracker.startEvaluate(cce)) {
+                return ErrorEval.CIRCULAR_REF_ERROR;
+            }
+            OperationEvaluationContext ec = new OperationEvaluationContext(this, _workbook, sheetIndex, rowIndex, columnIndex, tracker);
+
+            try {
+
+                Ptg[] ptgs = _workbook.getFormulaTokens(srcCell);
+                if (evalListener == null) {
+                    result = evaluateFormula(ec, ptgs);
+                } else {
+                    evalListener.onStartEvaluate(srcCell, cce);
+                    result = evaluateFormula(ec, ptgs);
+                    evalListener.onEndEvaluate(cce, result);
+                }
+
+                tracker.updateCacheResult(result);
+            }
+             catch (NotImplementedException e) {
+                throw addExceptionInfo(e, sheetIndex, rowIndex, columnIndex);
+             } catch (RuntimeException re) {
+                 if (re.getCause() instanceof WorkbookNotFoundException && _ignoreMissingWorkbooks) {
+                     logInfo(re.getCause().getMessage() + " - Continuing with cached value!");
+                     switch(srcCell.getCachedFormulaResultType()) {
+                         case Cell.CELL_TYPE_NUMERIC:
+                             result = new NumberEval(srcCell.getNumericCellValue());
+                             break;
+                         case Cell.CELL_TYPE_STRING:
+                             result =  new StringEval(srcCell.getStringCellValue());
+                             break;
+                         case Cell.CELL_TYPE_BLANK:
+                             result = BlankEval.instance;
+                             break;
+                         case Cell.CELL_TYPE_BOOLEAN:
+                             result =  BoolEval.valueOf(srcCell.getBooleanCellValue());
+                             break;
+                         case Cell.CELL_TYPE_ERROR:
+                            result =  ErrorEval.valueOf(srcCell.getErrorCellValue());
+                            break;
+                         case Cell.CELL_TYPE_FORMULA:
+                        default:
+                            throw new RuntimeException("Unexpected cell type '" + srcCell.getCellType()+"' found!");
+                     }
+                 } else {
+                     throw re;
+                 }
+             } finally {
+                tracker.endEvaluate(cce);
+            }
+        } else {
+            if(evalListener != null) {
+                evalListener.onCacheHit(sheetIndex, rowIndex, columnIndex, cce.getValue());
+            }
+            return cce.getValue();
+        }
+        if (isDebugLogEnabled()) {
+            String sheetName = getSheetName(sheetIndex);
+            CellReference cr = new CellReference(rowIndex, columnIndex);
+            logDebug("Evaluated " + sheetName + "!" + cr.formatAsString() + " to " + result.toString());
+        }
+        // Usually (result === cce.getValue())
+        // But sometimes: (result==ErrorEval.CIRCULAR_REF_ERROR, cce.getValue()==null)
+        // When circular references are detected, the cache entry is only updated for
+        // the top evaluation frame
+        return result;
+    }
+
+    /**
+     * Adds the current cell reference to the exception for easier debugging.
+     * Would be nice to get the formula text as well, but that seems to require
+     * too much digging around and casting to get the FormulaRenderingWorkbook.
+     */
+    private NotImplementedException addExceptionInfo(NotImplementedException inner, int sheetIndex, int rowIndex, int columnIndex) {
+
+        try {
+            String sheetName = _workbook.getSheetName(sheetIndex);
+            CellReference cr = new CellReference(sheetName, rowIndex, columnIndex, false, false);
+            String msg =  "Error evaluating cell " + cr.formatAsString();
+            return new NotImplementedException(msg, inner);
+        } catch (Exception e) {
+            // avoid bombing out during exception handling
+            LOG.log(POILogger.ERROR, "Can't add exception info", e);
+            return inner; // preserve original exception
+        }
+    }
+    /**
+     * Gets the value from a non-formula cell.
+     * @param cell may be <code>null</code>
+     * @return {@link BlankEval} if cell is <code>null</code> or blank, never <code>null</code>
+     */
+    /* package */ static ValueEval getValueFromNonFormulaCell(EvaluationCell cell) {
+        if (cell == null) {
+            return BlankEval.instance;
+        }
+        int cellType = cell.getCellType();
+        switch (cellType) {
+            case Cell.CELL_TYPE_NUMERIC:
+                return new NumberEval(cell.getNumericCellValue());
+            case Cell.CELL_TYPE_STRING:
+                return new StringEval(cell.getStringCellValue());
+            case Cell.CELL_TYPE_BOOLEAN:
+                return BoolEval.valueOf(cell.getBooleanCellValue());
+            case Cell.CELL_TYPE_BLANK:
+                return BlankEval.instance;
+            case Cell.CELL_TYPE_ERROR:
+                return ErrorEval.valueOf(cell.getErrorCellValue());
+        }
+        throw new RuntimeException("Unexpected cell type (" + cellType + ")");
+    }
 
 
     /**
      * whether print detailed messages about the next formula evaluation
      */
-	private boolean dbgEvaluationOutputForNextEval = false;
+    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
+    /* 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().getSheetNameRange()
+                               + "'/" + 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>();
+        for (int i = 0, iSize = ptgs.length; i < iSize; i++) {
+
+            // since we don't know how to handle these yet :(
+            Ptg ptg = ptgs[i];
+            if (dbgEvaluationOutputIndent > 0) {
+                EVAL_LOG.log(POILogger.INFO, dbgIndentStr + "  * ptg " + i + ": " + ptg);
+            }
+            if (ptg instanceof AttrPtg) {
+                AttrPtg attrPtg = (AttrPtg) ptg;
+                if (attrPtg.isSum()) {
+                    // Excel prefers to encode 'SUM()' as a tAttr token, but this evaluator
+                    // expects the equivalent function token
+                    ptg = FuncVarPtg.SUM;
+                }
+                if (attrPtg.isOptimizedChoose()) {
+                    ValueEval arg0 = stack.pop();
+                    int[] jumpTable = attrPtg.getJumpTable();
+                    int dist;
+                    int nChoices = jumpTable.length;
+                    try {
+                        int switchIndex = Choose.evaluateFirstArg(arg0, ec.getRowIndex(), ec.getColumnIndex());
+                        if (switchIndex<1 || switchIndex > nChoices) {
+                            stack.push(ErrorEval.VALUE_INVALID);
+                            dist = attrPtg.getChooseFuncOffset() + 4; // +4 for tFuncFar(CHOOSE)
+                        } else {
+                            dist = jumpTable[switchIndex-1];
+                        }
+                    } catch (EvaluationException e) {
+                        stack.push(e.getErrorEval());
+                        dist = attrPtg.getChooseFuncOffset() + 4; // +4 for tFuncFar(CHOOSE)
+                    }
+                    // Encoded dist for tAttrChoose includes size of jump table, but
+                    // countTokensToBeSkipped() does not (it counts whole tokens).
+                    dist -= nChoices*2+2; // subtract jump table size
+                    i+= countTokensToBeSkipped(ptgs, i, dist);
+                    continue;
+                }
+                if (attrPtg.isOptimizedIf()) {
+                    ValueEval arg0 = stack.pop();
+                    boolean evaluatedPredicate;
+                    try {
+                        evaluatedPredicate = IfFunc.evaluateFirstArg(arg0, ec.getRowIndex(), ec.getColumnIndex());
+                    } catch (EvaluationException e) {
+                        stack.push(e.getErrorEval());
+                        int dist = attrPtg.getData();
+                        i+= countTokensToBeSkipped(ptgs, i, dist);
+                        attrPtg = (AttrPtg) ptgs[i];
+                        dist = attrPtg.getData()+1;
+                        i+= countTokensToBeSkipped(ptgs, i, dist);
+                        continue;
+                    }
+                    if (evaluatedPredicate) {
+                        // nothing to skip - true param follows
+                    } else {
+                        int dist = attrPtg.getData();
+                        i+= countTokensToBeSkipped(ptgs, i, dist);
+                        Ptg nextPtg = ptgs[i+1];
+                        if (ptgs[i] instanceof AttrPtg && nextPtg instanceof FuncVarPtg && 
+                                // in order to verify that there is no third param, we need to check 
+                                // if we really have the IF next or some other FuncVarPtg as third param, e.g. ROW()/COLUMN()!
+                                ((FuncVarPtg)nextPtg).getFunctionIndex() == FunctionMetadataRegistry.FUNCTION_INDEX_IF) {
+                            // this is an if statement without a false param (as opposed to MissingArgPtg as the false param)
+                            i++;
+                            stack.push(BoolEval.FALSE);
+                        }
+                    }
+                    continue;
+                }
+                if (attrPtg.isSkip()) {
+                    int dist = attrPtg.getData()+1;
+                    i+= countTokensToBeSkipped(ptgs, i, dist);
+                    if (stack.peek() == MissingArgEval.instance) {
+                        stack.pop();
+                        stack.push(BlankEval.instance);
+                    }
+                    continue;
+                }
+            }
+            if (ptg instanceof ControlPtg) {
+                // skip Parentheses, Attr, etc
+                continue;
+            }
+            if (ptg instanceof MemFuncPtg || ptg instanceof MemAreaPtg) {
+                // can ignore, rest of tokens for this expression are in OK RPN order
+                continue;
+            }
+            if (ptg instanceof MemErrPtg) {
+                continue;
+            }
+
+            ValueEval opResult;
+            if (ptg instanceof OperationPtg) {
+                OperationPtg optg = (OperationPtg) ptg;
+
+                if (optg instanceof UnionPtg) { continue; }
+
+
+                int numops = optg.getNumberOfOperands();
+                ValueEval[] ops = new ValueEval[numops];
+
+                // storing the ops in reverse order since they are popping
+                for (int j = numops - 1; j >= 0; j--) {
+                    ValueEval p = stack.pop();
+                    ops[j] = p;
+                }
+//                logDebug("invoke " + operation + " (nAgs=" + numops + ")");
+                opResult = OperationEvaluatorFactory.evaluate(optg, ops, ec);
+            } else {
+                opResult = getEvalForPtg(ptg, ec);
+            }
+            if (opResult == null) {
+                throw new RuntimeException("Evaluation result must not be null");
+            }
+//            logDebug("push " + opResult);
+            stack.push(opResult);
+            if (dbgEvaluationOutputIndent > 0) {
+                EVAL_LOG.log(POILogger.INFO, dbgIndentStr + "    = " + opResult);
+            }
+        }
+
+        ValueEval value = stack.pop();
+        if (!stack.isEmpty()) {
+            throw new IllegalStateException("evaluation stack not empty");
+        }
+        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;
+
+    }
+
+    /**
+     * Calculates the number of tokens that the evaluator should skip upon reaching a tAttrSkip.
+     *
+     * @return the number of tokens (starting from <tt>startIndex+1</tt>) that need to be skipped
+     * to achieve the specified <tt>distInBytes</tt> skip distance.
+     */
+    private static int countTokensToBeSkipped(Ptg[] ptgs, int startIndex, int distInBytes) {
+        int remBytes = distInBytes;
+        int index = startIndex;
+        while (remBytes != 0) {
+            index++;
+            remBytes -= ptgs[index].getSize();
+            if (remBytes < 0) {
+                throw new RuntimeException("Bad skip distance (wrong token size calculation).");
+            }
+            if (index >= ptgs.length) {
+                throw new RuntimeException("Skip distance too far (ran out of formula tokens).");
+            }
+        }
+        return index-startIndex;
+    }
 
-	// 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
-	/* 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().getSheetNameRange()
-			                   + "'/" + 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>();
-		for (int i = 0, iSize = ptgs.length; i < iSize; i++) {
-
-			// since we don't know how to handle these yet :(
-			Ptg ptg = ptgs[i];
-			if (dbgEvaluationOutputIndent > 0) {
-				EVAL_LOG.log(POILogger.INFO, dbgIndentStr + "  * ptg " + i + ": " + ptg);
-			}
-			if (ptg instanceof AttrPtg) {
-				AttrPtg attrPtg = (AttrPtg) ptg;
-				if (attrPtg.isSum()) {
-					// Excel prefers to encode 'SUM()' as a tAttr token, but this evaluator
-					// expects the equivalent function token
-					ptg = FuncVarPtg.SUM;
-				}
-				if (attrPtg.isOptimizedChoose()) {
-					ValueEval arg0 = stack.pop();
-					int[] jumpTable = attrPtg.getJumpTable();
-					int dist;
-					int nChoices = jumpTable.length;
-					try {
-						int switchIndex = Choose.evaluateFirstArg(arg0, ec.getRowIndex(), ec.getColumnIndex());
-						if (switchIndex<1 || switchIndex > nChoices) {
-							stack.push(ErrorEval.VALUE_INVALID);
-							dist = attrPtg.getChooseFuncOffset() + 4; // +4 for tFuncFar(CHOOSE)
-						} else {
-							dist = jumpTable[switchIndex-1];
-						}
-					} catch (EvaluationException e) {
-						stack.push(e.getErrorEval());
-						dist = attrPtg.getChooseFuncOffset() + 4; // +4 for tFuncFar(CHOOSE)
-					}
-					// Encoded dist for tAttrChoose includes size of jump table, but
-					// countTokensToBeSkipped() does not (it counts whole tokens).
-					dist -= nChoices*2+2; // subtract jump table size
-					i+= countTokensToBeSkipped(ptgs, i, dist);
-					continue;
-				}
-				if (attrPtg.isOptimizedIf()) {
-					ValueEval arg0 = stack.pop();
-					boolean evaluatedPredicate;
-					try {
-						evaluatedPredicate = IfFunc.evaluateFirstArg(arg0, ec.getRowIndex(), ec.getColumnIndex());
-					} catch (EvaluationException e) {
-						stack.push(e.getErrorEval());
-						int dist = attrPtg.getData();
-						i+= countTokensToBeSkipped(ptgs, i, dist);
-						attrPtg = (AttrPtg) ptgs[i];
-						dist = attrPtg.getData()+1;
-						i+= countTokensToBeSkipped(ptgs, i, dist);
-						continue;
-					}
-					if (evaluatedPredicate) {
-						// nothing to skip - true param follows
-					} else {
-						int dist = attrPtg.getData();
-						i+= countTokensToBeSkipped(ptgs, i, dist);
-						Ptg nextPtg = ptgs[i+1];
-						if (ptgs[i] instanceof AttrPtg && nextPtg instanceof FuncVarPtg && 
-						        // in order to verify that there is no third param, we need to check 
-						        // if we really have the IF next or some other FuncVarPtg as third param, e.g. ROW()/COLUMN()!
-						        ((FuncVarPtg)nextPtg).getFunctionIndex() == FunctionMetadataRegistry.FUNCTION_INDEX_IF) {
-							// this is an if statement without a false param (as opposed to MissingArgPtg as the false param)
-							i++;
-							stack.push(BoolEval.FALSE);
-						}
-					}
-					continue;
-				}
-				if (attrPtg.isSkip()) {
-					int dist = attrPtg.getData()+1;
-					i+= countTokensToBeSkipped(ptgs, i, dist);
-					if (stack.peek() == MissingArgEval.instance) {
-						stack.pop();
-						stack.push(BlankEval.instance);
-					}
-					continue;
-				}
-			}
-			if (ptg instanceof ControlPtg) {
-				// skip Parentheses, Attr, etc
-				continue;
-			}
-			if (ptg instanceof MemFuncPtg || ptg instanceof MemAreaPtg) {
-				// can ignore, rest of tokens for this expression are in OK RPN order
-				continue;
-			}
-			if (ptg instanceof MemErrPtg) {
-				continue;
-			}
-
-			ValueEval opResult;
-			if (ptg instanceof OperationPtg) {
-				OperationPtg optg = (OperationPtg) ptg;
-
-				if (optg instanceof UnionPtg) { continue; }
-
-
-				int numops = optg.getNumberOfOperands();
-				ValueEval[] ops = new ValueEval[numops];
-
-				// storing the ops in reverse order since they are popping
-				for (int j = numops - 1; j >= 0; j--) {
-					ValueEval p = stack.pop();
-					ops[j] = p;
-				}
-//				logDebug("invoke " + operation + " (nAgs=" + numops + ")");
-				opResult = OperationEvaluatorFactory.evaluate(optg, ops, ec);
-			} else {
-				opResult = getEvalForPtg(ptg, ec);
-			}
-			if (opResult == null) {
-				throw new RuntimeException("Evaluation result must not be null");
-			}
-//			logDebug("push " + opResult);
-			stack.push(opResult);
-			if (dbgEvaluationOutputIndent > 0) {
-				EVAL_LOG.log(POILogger.INFO, dbgIndentStr + "    = " + opResult);
-			}
-		}
-
-		ValueEval value = stack.pop();
-		if (!stack.isEmpty()) {
-			throw new IllegalStateException("evaluation stack not empty");
-		}
-		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;
-
-	}
-
-	/**
-	 * Calculates the number of tokens that the evaluator should skip upon reaching a tAttrSkip.
-	 *
-	 * @return the number of tokens (starting from <tt>startIndex+1</tt>) that need to be skipped
-	 * to achieve the specified <tt>distInBytes</tt> skip distance.
-	 */
-	private static int countTokensToBeSkipped(Ptg[] ptgs, int startIndex, int distInBytes) {
-		int remBytes = distInBytes;
-		int index = startIndex;
-		while (remBytes != 0) {
-			index++;
-			remBytes -= ptgs[index].getSize();
-			if (remBytes < 0) {
-				throw new RuntimeException("Bad skip distance (wrong token size calculation).");
-			}
-			if (index >= ptgs.length) {
-				throw new RuntimeException("Skip distance too far (ran out of formula tokens).");
-			}
-		}
-		return index-startIndex;
-	}
-
-	/**
-	 * Dereferences a single value from any AreaEval or RefEval evaluation
-	 * result. If the supplied evaluationResult is just a plain value, it is
-	 * returned as-is.
-	 *
-	 * @return a {@link NumberEval}, {@link StringEval}, {@link BoolEval}, or
-	 *         {@link ErrorEval}. Never <code>null</code>. {@link BlankEval} is
-	 *         converted to {@link NumberEval#ZERO}
-	 */
-	public static ValueEval dereferenceResult(ValueEval evaluationResult, int srcRowNum, int srcColNum) {
-		ValueEval value;
-		try {
-			value = OperandResolver.getSingleValue(evaluationResult, srcRowNum, srcColNum);
-		} catch (EvaluationException e) {
-			return e.getErrorEval();
-		}
-		if (value == BlankEval.instance) {
-			// Note Excel behaviour here. A blank final final value is converted to zero.
-			return NumberEval.ZERO;
-			// Formulas _never_ evaluate to blank.  If a formula appears to have evaluated to
-			// blank, the actual value is empty string. This can be verified with ISBLANK().
-		}
-		return value;
-	}
+    /**
+     * Dereferences a single value from any AreaEval or RefEval evaluation
+     * result. If the supplied evaluationResult is just a plain value, it is
+     * returned as-is.
+     *
+     * @return a {@link NumberEval}, {@link StringEval}, {@link BoolEval}, or
+     *         {@link ErrorEval}. Never <code>null</code>. {@link BlankEval} is
+     *         converted to {@link NumberEval#ZERO}
+     */
+    public static ValueEval dereferenceResult(ValueEval evaluationResult, int srcRowNum, int srcColNum) {
+        ValueEval value;
+        try {
+            value = OperandResolver.getSingleValue(evaluationResult, srcRowNum, srcColNum);
+        } catch (EvaluationException e) {
+            return e.getErrorEval();
+        }
+        if (value == BlankEval.instance) {
+            // Note Excel behaviour here. A blank final final value is converted to zero.
+            return NumberEval.ZERO;
+            // Formulas _never_ evaluate to blank.  If a formula appears to have evaluated to
+            // blank, the actual value is empty string. This can be verified with ISBLANK().
+        }
+        return value;
+    }
 
 
    /**
@@ -670,7 +710,7 @@ public final class WorkbookEvaluator {
        }
        return eval;
    }
-	
+    
     private ValueEval getEvalForNameRecord(EvaluationName nameRecord, OperationEvaluationContext ec) {
         if (nameRecord.isFunctionName()) {
             return new FunctionNameEval(nameRecord.getNameText());
@@ -685,25 +725,25 @@ public final class WorkbookEvaluator {
     /**
      * YK: Used by OperationEvaluationContext to resolve indirect names.
      */
-	/*package*/ ValueEval evaluateNameFormula(Ptg[] ptgs, OperationEvaluationContext ec) {
+    /*package*/ ValueEval evaluateNameFormula(Ptg[] ptgs, OperationEvaluationContext ec) {
     if (ptgs.length == 1) {
       return getEvalForPtg(ptgs[0], ec);
     }
-	  return evaluateFormula(ec, ptgs);
-	}
+      return evaluateFormula(ec, ptgs);
+    }
+
+    /**
+     * Used by the lazy ref evals whenever they need to get the value of a contained cell.
+     */
+    /* package */ ValueEval evaluateReference(EvaluationSheet sheet, int sheetIndex, int rowIndex,
+            int columnIndex, EvaluationTracker tracker) {
 
-	/**
-	 * Used by the lazy ref evals whenever they need to get the value of a contained cell.
-	 */
-	/* package */ ValueEval evaluateReference(EvaluationSheet sheet, int sheetIndex, int rowIndex,
-			int columnIndex, EvaluationTracker tracker) {
-
-		EvaluationCell cell = sheet.getCell(rowIndex, columnIndex);
-		return evaluateAny(cell, sheetIndex, rowIndex, columnIndex, tracker);
-	}
-	public FreeRefFunction findUserDefinedFunction(String functionName) {
-		return _udfFinder.findFunction(functionName);
-	}
+        EvaluationCell cell = sheet.getCell(rowIndex, columnIndex);
+        return evaluateAny(cell, sheetIndex, rowIndex, columnIndex, tracker);
+    }
+    public FreeRefFunction findUserDefinedFunction(String functionName) {
+        return _udfFinder.findFunction(functionName);
+    }
 
     /**
      * Whether to ignore missing references to external workbooks and



---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@poi.apache.org
For additional commands, e-mail: commits-help@poi.apache.org