You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@poi.apache.org by fa...@apache.org on 2021/05/22 20:56:49 UTC

svn commit: r1890120 [16/43] - in /poi/trunk/poi/src: main/java/org/apache/poi/ main/java/org/apache/poi/ddf/ main/java/org/apache/poi/extractor/ main/java/org/apache/poi/hpsf/ main/java/org/apache/poi/hssf/ main/java/org/apache/poi/hssf/dev/ main/java...

Modified: poi/trunk/poi/src/main/java/org/apache/poi/ss/formula/LazyAreaEval.java
URL: http://svn.apache.org/viewvc/poi/trunk/poi/src/main/java/org/apache/poi/ss/formula/LazyAreaEval.java?rev=1890120&r1=1890119&r2=1890120&view=diff
==============================================================================
--- poi/trunk/poi/src/main/java/org/apache/poi/ss/formula/LazyAreaEval.java (original)
+++ poi/trunk/poi/src/main/java/org/apache/poi/ss/formula/LazyAreaEval.java Sat May 22 20:56:44 2021
@@ -28,74 +28,74 @@ import org.apache.poi.ss.util.CellRefere
  * Provides Lazy Evaluation to 3D Ranges
  */
 final class LazyAreaEval extends AreaEvalBase {
-	private final SheetRangeEvaluator _evaluator;
+    private final SheetRangeEvaluator _evaluator;
 
-	LazyAreaEval(AreaI ptg, SheetRangeEvaluator evaluator) {
-		super(ptg, evaluator);
-		_evaluator = evaluator;
-	}
+    LazyAreaEval(AreaI ptg, SheetRangeEvaluator evaluator) {
+        super(ptg, evaluator);
+        _evaluator = evaluator;
+    }
 
-	public LazyAreaEval(int firstRowIndex, int firstColumnIndex, int lastRowIndex,
-			int lastColumnIndex, SheetRangeEvaluator evaluator) {
-		super(evaluator, firstRowIndex, firstColumnIndex, lastRowIndex, lastColumnIndex);
-		_evaluator = evaluator;
-	}
+    public LazyAreaEval(int firstRowIndex, int firstColumnIndex, int lastRowIndex,
+            int lastColumnIndex, SheetRangeEvaluator evaluator) {
+        super(evaluator, firstRowIndex, firstColumnIndex, lastRowIndex, lastColumnIndex);
+        _evaluator = evaluator;
+    }
 
     @Override
-	public ValueEval getRelativeValue(int relativeRowIndex, int relativeColumnIndex) {
+    public ValueEval getRelativeValue(int relativeRowIndex, int relativeColumnIndex) {
         return getRelativeValue(getFirstSheetIndex(), relativeRowIndex, relativeColumnIndex);
     }
     @Override
-	public ValueEval getRelativeValue(int sheetIndex, int relativeRowIndex, int relativeColumnIndex) {
-		int rowIx = (relativeRowIndex + getFirstRow() ) ;
-		int colIx = (relativeColumnIndex + getFirstColumn() ) ;
-
-		return _evaluator.getEvalForCell(sheetIndex, rowIx, colIx);
-	}
-
-	@Override
-	public AreaEval offset(int relFirstRowIx, int relLastRowIx, int relFirstColIx, int relLastColIx) {
-		AreaI area = new OffsetArea(getFirstRow(), getFirstColumn(),
-				relFirstRowIx, relLastRowIx, relFirstColIx, relLastColIx);
-
-		return new LazyAreaEval(area, _evaluator);
-	}
-	@Override
+    public ValueEval getRelativeValue(int sheetIndex, int relativeRowIndex, int relativeColumnIndex) {
+        int rowIx = (relativeRowIndex + getFirstRow() ) ;
+        int colIx = (relativeColumnIndex + getFirstColumn() ) ;
+
+        return _evaluator.getEvalForCell(sheetIndex, rowIx, colIx);
+    }
+
+    @Override
+    public AreaEval offset(int relFirstRowIx, int relLastRowIx, int relFirstColIx, int relLastColIx) {
+        AreaI area = new OffsetArea(getFirstRow(), getFirstColumn(),
+                relFirstRowIx, relLastRowIx, relFirstColIx, relLastColIx);
+
+        return new LazyAreaEval(area, _evaluator);
+    }
+    @Override
     public LazyAreaEval getRow(int rowIndex) {
-		if (rowIndex >= getHeight()) {
-			throw new IllegalArgumentException("Invalid rowIndex " + rowIndex
-					+ ".  Allowable range is (0.." + getHeight() + ").");
-		}
-		int absRowIx = getFirstRow() + rowIndex;
-		return new LazyAreaEval(absRowIx, getFirstColumn(), absRowIx, getLastColumn(), _evaluator);
-	}
-	@Override
-	public LazyAreaEval getColumn(int columnIndex) {
-		if (columnIndex >= getWidth()) {
-			throw new IllegalArgumentException("Invalid columnIndex " + columnIndex
-					+ ".  Allowable range is (0.." + getWidth() + ").");
-		}
-		int absColIx = getFirstColumn() + columnIndex;
-		return new LazyAreaEval(getFirstRow(), absColIx, getLastRow(), absColIx, _evaluator);
-	}
-
-	public String toString() {
-		CellReference crA = new CellReference(getFirstRow(), getFirstColumn());
-		CellReference crB = new CellReference(getLastRow(), getLastColumn());
-		return getClass().getName() + "[" +
-				_evaluator.getSheetNameRange() +
-				'!' +
-				crA.formatAsString() +
-				':' +
-				crB.formatAsString() +
-				"]";
-	}
+        if (rowIndex >= getHeight()) {
+            throw new IllegalArgumentException("Invalid rowIndex " + rowIndex
+                    + ".  Allowable range is (0.." + getHeight() + ").");
+        }
+        int absRowIx = getFirstRow() + rowIndex;
+        return new LazyAreaEval(absRowIx, getFirstColumn(), absRowIx, getLastColumn(), _evaluator);
+    }
+    @Override
+    public LazyAreaEval getColumn(int columnIndex) {
+        if (columnIndex >= getWidth()) {
+            throw new IllegalArgumentException("Invalid columnIndex " + columnIndex
+                    + ".  Allowable range is (0.." + getWidth() + ").");
+        }
+        int absColIx = getFirstColumn() + columnIndex;
+        return new LazyAreaEval(getFirstRow(), absColIx, getLastRow(), absColIx, _evaluator);
+    }
+
+    public String toString() {
+        CellReference crA = new CellReference(getFirstRow(), getFirstColumn());
+        CellReference crB = new CellReference(getLastRow(), getLastColumn());
+        return getClass().getName() + "[" +
+                _evaluator.getSheetNameRange() +
+                '!' +
+                crA.formatAsString() +
+                ':' +
+                crB.formatAsString() +
+                "]";
+    }
 
     /**
      * @return  whether cell at rowIndex and columnIndex is a subtotal
     */
     @Override
-	public boolean isSubTotal(int rowIndex, int columnIndex){
+    public boolean isSubTotal(int rowIndex, int columnIndex){
         // delegate the query to the sheet evaluator which has access to internal ptgs
         SheetRefEvaluator _sre = _evaluator.getSheetEvaluator(_evaluator.getFirstSheetIndex());
         return _sre.isSubTotal(getFirstRow() + rowIndex, getFirstColumn() + columnIndex);
@@ -105,7 +105,7 @@ final class LazyAreaEval extends AreaEva
      * @return whether the row at rowIndex is hidden
      */
     @Override
-	public boolean isRowHidden(int rowIndex) {
+    public boolean isRowHidden(int rowIndex) {
         // delegate the query to the sheet evaluator which has access to internal ptgs
         SheetRefEvaluator _sre = _evaluator.getSheetEvaluator(_evaluator.getFirstSheetIndex());
         return _sre.isRowHidden(getFirstRow() + rowIndex);

Modified: poi/trunk/poi/src/main/java/org/apache/poi/ss/formula/LazyRefEval.java
URL: http://svn.apache.org/viewvc/poi/trunk/poi/src/main/java/org/apache/poi/ss/formula/LazyRefEval.java?rev=1890120&r1=1890119&r2=1890120&view=diff
==============================================================================
--- poi/trunk/poi/src/main/java/org/apache/poi/ss/formula/LazyRefEval.java (original)
+++ poi/trunk/poi/src/main/java/org/apache/poi/ss/formula/LazyRefEval.java Sat May 22 20:56:44 2021
@@ -28,32 +28,32 @@ import org.apache.poi.ss.util.CellRefere
  * Provides Lazy Evaluation to a 3D Reference
  */
 public final class LazyRefEval extends RefEvalBase {
-	private final SheetRangeEvaluator _evaluator;
+    private final SheetRangeEvaluator _evaluator;
 
-	public LazyRefEval(int rowIndex, int columnIndex, SheetRangeEvaluator sre) {
-		super(sre, rowIndex, columnIndex);
-		_evaluator = sre;
-	}
-
-	public ValueEval getInnerValueEval(int sheetIndex) {
-		return _evaluator.getEvalForCell(sheetIndex, getRow(), getColumn());
-	}
-
-	public AreaEval offset(int relFirstRowIx, int relLastRowIx, int relFirstColIx, int relLastColIx) {
-
-		AreaI area = new OffsetArea(getRow(), getColumn(),
-				relFirstRowIx, relLastRowIx, relFirstColIx, relLastColIx);
-
-		return new LazyAreaEval(area, _evaluator);
-	}
-
-	/**
-	 * @return true if the cell is a subtotal
-	 */
-	public boolean isSubTotal() {
-		SheetRefEvaluator sheetEvaluator = _evaluator.getSheetEvaluator(getFirstSheetIndex());
-		return sheetEvaluator.isSubTotal(getRow(), getColumn());
-	}
+    public LazyRefEval(int rowIndex, int columnIndex, SheetRangeEvaluator sre) {
+        super(sre, rowIndex, columnIndex);
+        _evaluator = sre;
+    }
+
+    public ValueEval getInnerValueEval(int sheetIndex) {
+        return _evaluator.getEvalForCell(sheetIndex, getRow(), getColumn());
+    }
+
+    public AreaEval offset(int relFirstRowIx, int relLastRowIx, int relFirstColIx, int relLastColIx) {
+
+        AreaI area = new OffsetArea(getRow(), getColumn(),
+                relFirstRowIx, relLastRowIx, relFirstColIx, relLastColIx);
+
+        return new LazyAreaEval(area, _evaluator);
+    }
+
+    /**
+     * @return true if the cell is a subtotal
+     */
+    public boolean isSubTotal() {
+        SheetRefEvaluator sheetEvaluator = _evaluator.getSheetEvaluator(getFirstSheetIndex());
+        return sheetEvaluator.isSubTotal(getRow(), getColumn());
+    }
     
     /**
      * @return whether the row at rowIndex is hidden
@@ -64,12 +64,12 @@ public final class LazyRefEval extends R
         return _sre.isRowHidden(getRow());
     }
 
-	public String toString() {
-		CellReference cr = new CellReference(getRow(), getColumn());
-		return getClass().getName() + "[" +
-				_evaluator.getSheetNameRange() +
-				'!' +
-				cr.formatAsString() +
-				"]";
-	}
+    public String toString() {
+        CellReference cr = new CellReference(getRow(), getColumn());
+        return getClass().getName() + "[" +
+                _evaluator.getSheetNameRange() +
+                '!' +
+                cr.formatAsString() +
+                "]";
+    }
 }

Modified: poi/trunk/poi/src/main/java/org/apache/poi/ss/formula/OperandClassTransformer.java
URL: http://svn.apache.org/viewvc/poi/trunk/poi/src/main/java/org/apache/poi/ss/formula/OperandClassTransformer.java?rev=1890120&r1=1890119&r2=1890120&view=diff
==============================================================================
--- poi/trunk/poi/src/main/java/org/apache/poi/ss/formula/OperandClassTransformer.java (original)
+++ poi/trunk/poi/src/main/java/org/apache/poi/ss/formula/OperandClassTransformer.java Sat May 22 20:56:44 2021
@@ -55,237 +55,237 @@ import org.apache.poi.ss.formula.ptg.Val
  */
 final class OperandClassTransformer {
 
-	private final FormulaType _formulaType;
+    private final FormulaType _formulaType;
 
-	public OperandClassTransformer(FormulaType formulaType) {
-		_formulaType = formulaType;
-	}
-
-	/**
-	 * Traverses the supplied formula parse tree, calling {@code Ptg.setClass()} for each non-base
-	 * token to set its operand class.
-	 */
-	public void transformFormula(ParseNode rootNode) {
-		byte rootNodeOperandClass;
-		switch (_formulaType) {
-			case CELL:
-				rootNodeOperandClass = Ptg.CLASS_VALUE;
-				break;
-			case ARRAY:
-				rootNodeOperandClass = Ptg.CLASS_ARRAY;
-				break;
-			case NAMEDRANGE:
-			case DATAVALIDATION_LIST:
-				rootNodeOperandClass = Ptg.CLASS_REF;
-				break;
-			default:
-				throw new RuntimeException("Incomplete code - formula type ("
-						+ _formulaType + ") not supported yet");
-
-		}
-		transformNode(rootNode, rootNodeOperandClass, false);
-	}
-
-	/**
-	 * @param callerForceArrayFlag {@code true} if one of the current node's parents is a
-	 * function Ptg which has been changed from default 'V' to 'A' type (due to requirements on
-	 * the function return value).
-	 */
-	private void transformNode(ParseNode node, byte desiredOperandClass,
-			boolean callerForceArrayFlag) {
-		Ptg token = node.getToken();
-		ParseNode[] children = node.getChildren();
-		boolean isSimpleValueFunc = isSimpleValueFunction(token);
-
-		if (isSimpleValueFunc) {
-			boolean localForceArray = desiredOperandClass == Ptg.CLASS_ARRAY;
-			for (ParseNode child : children) {
-				transformNode(child, desiredOperandClass, localForceArray);
-			}
-			setSimpleValueFuncClass((AbstractFunctionPtg) token, desiredOperandClass, callerForceArrayFlag);
-			return;
-		}
-
-		if (isSingleArgSum(token)) {
-			// Need to process the argument of SUM with transformFunctionNode below
-			// so make a dummy FuncVarPtg for that call.
-			token = FuncVarPtg.SUM;
-			// Note - the tAttrSum token (node.getToken()) is a base
-			// token so does not need to have its operand class set
-		}
-		if (token instanceof ValueOperatorPtg || token instanceof ControlPtg
-				|| token instanceof MemFuncPtg
-				|| token instanceof MemAreaPtg
-				|| token instanceof UnionPtg
-				|| token instanceof IntersectionPtg) {
-			// Value Operator Ptgs and Control are base tokens, so token will be unchanged
-			// but any child nodes are processed according to desiredOperandClass and callerForceArrayFlag
-
-			// As per OOO documentation Sec 3.2.4 "Token Class Transformation", "Step 1"
-			// All direct operands of value operators that are initially 'R' type will
-			// be converted to 'V' type.
-			byte localDesiredOperandClass = desiredOperandClass == Ptg.CLASS_REF ? Ptg.CLASS_VALUE : desiredOperandClass;
-			for (ParseNode child : children) {
-				transformNode(child, localDesiredOperandClass, callerForceArrayFlag);
-			}
-			return;
-		}
-		if (token instanceof AbstractFunctionPtg) {
-			transformFunctionNode((AbstractFunctionPtg) token, children, desiredOperandClass, callerForceArrayFlag);
-			return;
-		}
-		if (children.length > 0) {
-			if (token == RangePtg.instance) {
-				// TODO is any token transformation required under the various ref operators?
-				return;
-			}
-			throw new IllegalStateException("Node should not have any children");
-		}
-
-		if (token.isBaseToken()) {
-			// nothing to do
-			return;
-		}
-		token.setClass(transformClass(token.getPtgClass(), desiredOperandClass, callerForceArrayFlag));
-	}
-
-	private static boolean isSingleArgSum(Ptg token) {
-		if (token instanceof AttrPtg) {
-			AttrPtg attrPtg = (AttrPtg) token;
-			return attrPtg.isSum();
-		}
-		return false;
-	}
-
-	private static boolean isSimpleValueFunction(Ptg token) {
-		if (token instanceof AbstractFunctionPtg) {
-			AbstractFunctionPtg aptg = (AbstractFunctionPtg) token;
-			if (aptg.getDefaultOperandClass() != Ptg.CLASS_VALUE) {
-				return false;
-			}
-			int numberOfOperands = aptg.getNumberOfOperands();
-			for (int i=numberOfOperands-1; i>=0; i--) {
-				if (aptg.getParameterClass(i) != Ptg.CLASS_VALUE) {
-					return false;
-				}
-			}
-			return true;
-		}
-		return false;
-	}
-
-	private byte transformClass(byte currentOperandClass, byte desiredOperandClass,
-			boolean callerForceArrayFlag) {
-		switch (desiredOperandClass) {
-			case Ptg.CLASS_VALUE:
-				if (!callerForceArrayFlag) {
-					return Ptg.CLASS_VALUE;
-				}
-				// else fall through
-			case Ptg.CLASS_ARRAY:
-				return Ptg.CLASS_ARRAY;
-			case Ptg.CLASS_REF:
-				if (!callerForceArrayFlag) {
-					return currentOperandClass;
-				}
-				return Ptg.CLASS_REF;
-		}
-		throw new IllegalStateException("Unexpected operand class (" + desiredOperandClass + ")");
-	}
-
-	private void transformFunctionNode(AbstractFunctionPtg afp, ParseNode[] children,
-			byte desiredOperandClass, boolean callerForceArrayFlag) {
-
-		boolean localForceArrayFlag;
-		byte defaultReturnOperandClass = afp.getDefaultOperandClass();
-
-		if (callerForceArrayFlag) {
-			switch (defaultReturnOperandClass) {
-				case Ptg.CLASS_REF:
-					if (desiredOperandClass == Ptg.CLASS_REF) {
-						afp.setClass(Ptg.CLASS_REF);
-					} else {
-						afp.setClass(Ptg.CLASS_ARRAY);
-					}
-					localForceArrayFlag = false;
-					break;
-				case Ptg.CLASS_ARRAY:
-					afp.setClass(Ptg.CLASS_ARRAY);
-					localForceArrayFlag = false;
-					break;
-				case Ptg.CLASS_VALUE:
-					afp.setClass(Ptg.CLASS_ARRAY);
-					localForceArrayFlag = true;
-					break;
-				default:
-					throw new IllegalStateException("Unexpected operand class ("
-							+ defaultReturnOperandClass + ")");
-			}
-		} else {
-			if (defaultReturnOperandClass == desiredOperandClass) {
-				localForceArrayFlag = false;
-				// an alternative would have been to for non-base Ptgs to set their operand class
-				// from their default, but this would require the call in many subclasses because
-				// the default OC is not known until the end of the constructor
-				afp.setClass(defaultReturnOperandClass);
-			} else {
-				switch (desiredOperandClass) {
-					case Ptg.CLASS_VALUE:
-						// always OK to set functions to return 'value'
-						afp.setClass(Ptg.CLASS_VALUE);
-						localForceArrayFlag = false;
-						break;
-					case Ptg.CLASS_ARRAY:
-						switch (defaultReturnOperandClass) {
-							case Ptg.CLASS_REF:
-								afp.setClass(Ptg.CLASS_REF);
-//								afp.setClass(Ptg.CLASS_ARRAY);
-								break;
-							case Ptg.CLASS_VALUE:
-								afp.setClass(Ptg.CLASS_ARRAY);
-								break;
-							default:
-								throw new IllegalStateException("Unexpected operand class ("
-										+ defaultReturnOperandClass + ")");
-						}
-						localForceArrayFlag = (defaultReturnOperandClass == Ptg.CLASS_VALUE);
-						break;
-					case Ptg.CLASS_REF:
-						switch (defaultReturnOperandClass) {
-							case Ptg.CLASS_ARRAY:
-								afp.setClass(Ptg.CLASS_ARRAY);
-								break;
-							case Ptg.CLASS_VALUE:
-								afp.setClass(Ptg.CLASS_VALUE);
-								break;
-							default:
-								throw new IllegalStateException("Unexpected operand class ("
-										+ defaultReturnOperandClass + ")");
-						}
-						localForceArrayFlag = false;
-						break;
-					default:
-						throw new IllegalStateException("Unexpected operand class ("
-								+ desiredOperandClass + ")");
-				}
-
-			}
-		}
-
-		for (int i = 0; i < children.length; i++) {
-			ParseNode child = children[i];
-			byte paramOperandClass = afp.getParameterClass(i);
-			transformNode(child, paramOperandClass, localForceArrayFlag);
-		}
-	}
-
-	private void setSimpleValueFuncClass(AbstractFunctionPtg afp,
-			byte desiredOperandClass, boolean callerForceArrayFlag) {
-
-		if (callerForceArrayFlag  || desiredOperandClass == Ptg.CLASS_ARRAY) {
-			afp.setClass(Ptg.CLASS_ARRAY);
-		} else {
-			afp.setClass(Ptg.CLASS_VALUE);
-		}
-	}
+    public OperandClassTransformer(FormulaType formulaType) {
+        _formulaType = formulaType;
+    }
+
+    /**
+     * Traverses the supplied formula parse tree, calling {@code Ptg.setClass()} for each non-base
+     * token to set its operand class.
+     */
+    public void transformFormula(ParseNode rootNode) {
+        byte rootNodeOperandClass;
+        switch (_formulaType) {
+            case CELL:
+                rootNodeOperandClass = Ptg.CLASS_VALUE;
+                break;
+            case ARRAY:
+                rootNodeOperandClass = Ptg.CLASS_ARRAY;
+                break;
+            case NAMEDRANGE:
+            case DATAVALIDATION_LIST:
+                rootNodeOperandClass = Ptg.CLASS_REF;
+                break;
+            default:
+                throw new RuntimeException("Incomplete code - formula type ("
+                        + _formulaType + ") not supported yet");
+
+        }
+        transformNode(rootNode, rootNodeOperandClass, false);
+    }
+
+    /**
+     * @param callerForceArrayFlag {@code true} if one of the current node's parents is a
+     * function Ptg which has been changed from default 'V' to 'A' type (due to requirements on
+     * the function return value).
+     */
+    private void transformNode(ParseNode node, byte desiredOperandClass,
+            boolean callerForceArrayFlag) {
+        Ptg token = node.getToken();
+        ParseNode[] children = node.getChildren();
+        boolean isSimpleValueFunc = isSimpleValueFunction(token);
+
+        if (isSimpleValueFunc) {
+            boolean localForceArray = desiredOperandClass == Ptg.CLASS_ARRAY;
+            for (ParseNode child : children) {
+                transformNode(child, desiredOperandClass, localForceArray);
+            }
+            setSimpleValueFuncClass((AbstractFunctionPtg) token, desiredOperandClass, callerForceArrayFlag);
+            return;
+        }
+
+        if (isSingleArgSum(token)) {
+            // Need to process the argument of SUM with transformFunctionNode below
+            // so make a dummy FuncVarPtg for that call.
+            token = FuncVarPtg.SUM;
+            // Note - the tAttrSum token (node.getToken()) is a base
+            // token so does not need to have its operand class set
+        }
+        if (token instanceof ValueOperatorPtg || token instanceof ControlPtg
+                || token instanceof MemFuncPtg
+                || token instanceof MemAreaPtg
+                || token instanceof UnionPtg
+                || token instanceof IntersectionPtg) {
+            // Value Operator Ptgs and Control are base tokens, so token will be unchanged
+            // but any child nodes are processed according to desiredOperandClass and callerForceArrayFlag
+
+            // As per OOO documentation Sec 3.2.4 "Token Class Transformation", "Step 1"
+            // All direct operands of value operators that are initially 'R' type will
+            // be converted to 'V' type.
+            byte localDesiredOperandClass = desiredOperandClass == Ptg.CLASS_REF ? Ptg.CLASS_VALUE : desiredOperandClass;
+            for (ParseNode child : children) {
+                transformNode(child, localDesiredOperandClass, callerForceArrayFlag);
+            }
+            return;
+        }
+        if (token instanceof AbstractFunctionPtg) {
+            transformFunctionNode((AbstractFunctionPtg) token, children, desiredOperandClass, callerForceArrayFlag);
+            return;
+        }
+        if (children.length > 0) {
+            if (token == RangePtg.instance) {
+                // TODO is any token transformation required under the various ref operators?
+                return;
+            }
+            throw new IllegalStateException("Node should not have any children");
+        }
+
+        if (token.isBaseToken()) {
+            // nothing to do
+            return;
+        }
+        token.setClass(transformClass(token.getPtgClass(), desiredOperandClass, callerForceArrayFlag));
+    }
+
+    private static boolean isSingleArgSum(Ptg token) {
+        if (token instanceof AttrPtg) {
+            AttrPtg attrPtg = (AttrPtg) token;
+            return attrPtg.isSum();
+        }
+        return false;
+    }
+
+    private static boolean isSimpleValueFunction(Ptg token) {
+        if (token instanceof AbstractFunctionPtg) {
+            AbstractFunctionPtg aptg = (AbstractFunctionPtg) token;
+            if (aptg.getDefaultOperandClass() != Ptg.CLASS_VALUE) {
+                return false;
+            }
+            int numberOfOperands = aptg.getNumberOfOperands();
+            for (int i=numberOfOperands-1; i>=0; i--) {
+                if (aptg.getParameterClass(i) != Ptg.CLASS_VALUE) {
+                    return false;
+                }
+            }
+            return true;
+        }
+        return false;
+    }
+
+    private byte transformClass(byte currentOperandClass, byte desiredOperandClass,
+            boolean callerForceArrayFlag) {
+        switch (desiredOperandClass) {
+            case Ptg.CLASS_VALUE:
+                if (!callerForceArrayFlag) {
+                    return Ptg.CLASS_VALUE;
+                }
+                // else fall through
+            case Ptg.CLASS_ARRAY:
+                return Ptg.CLASS_ARRAY;
+            case Ptg.CLASS_REF:
+                if (!callerForceArrayFlag) {
+                    return currentOperandClass;
+                }
+                return Ptg.CLASS_REF;
+        }
+        throw new IllegalStateException("Unexpected operand class (" + desiredOperandClass + ")");
+    }
+
+    private void transformFunctionNode(AbstractFunctionPtg afp, ParseNode[] children,
+            byte desiredOperandClass, boolean callerForceArrayFlag) {
+
+        boolean localForceArrayFlag;
+        byte defaultReturnOperandClass = afp.getDefaultOperandClass();
+
+        if (callerForceArrayFlag) {
+            switch (defaultReturnOperandClass) {
+                case Ptg.CLASS_REF:
+                    if (desiredOperandClass == Ptg.CLASS_REF) {
+                        afp.setClass(Ptg.CLASS_REF);
+                    } else {
+                        afp.setClass(Ptg.CLASS_ARRAY);
+                    }
+                    localForceArrayFlag = false;
+                    break;
+                case Ptg.CLASS_ARRAY:
+                    afp.setClass(Ptg.CLASS_ARRAY);
+                    localForceArrayFlag = false;
+                    break;
+                case Ptg.CLASS_VALUE:
+                    afp.setClass(Ptg.CLASS_ARRAY);
+                    localForceArrayFlag = true;
+                    break;
+                default:
+                    throw new IllegalStateException("Unexpected operand class ("
+                            + defaultReturnOperandClass + ")");
+            }
+        } else {
+            if (defaultReturnOperandClass == desiredOperandClass) {
+                localForceArrayFlag = false;
+                // an alternative would have been to for non-base Ptgs to set their operand class
+                // from their default, but this would require the call in many subclasses because
+                // the default OC is not known until the end of the constructor
+                afp.setClass(defaultReturnOperandClass);
+            } else {
+                switch (desiredOperandClass) {
+                    case Ptg.CLASS_VALUE:
+                        // always OK to set functions to return 'value'
+                        afp.setClass(Ptg.CLASS_VALUE);
+                        localForceArrayFlag = false;
+                        break;
+                    case Ptg.CLASS_ARRAY:
+                        switch (defaultReturnOperandClass) {
+                            case Ptg.CLASS_REF:
+                                afp.setClass(Ptg.CLASS_REF);
+//                              afp.setClass(Ptg.CLASS_ARRAY);
+                                break;
+                            case Ptg.CLASS_VALUE:
+                                afp.setClass(Ptg.CLASS_ARRAY);
+                                break;
+                            default:
+                                throw new IllegalStateException("Unexpected operand class ("
+                                        + defaultReturnOperandClass + ")");
+                        }
+                        localForceArrayFlag = (defaultReturnOperandClass == Ptg.CLASS_VALUE);
+                        break;
+                    case Ptg.CLASS_REF:
+                        switch (defaultReturnOperandClass) {
+                            case Ptg.CLASS_ARRAY:
+                                afp.setClass(Ptg.CLASS_ARRAY);
+                                break;
+                            case Ptg.CLASS_VALUE:
+                                afp.setClass(Ptg.CLASS_VALUE);
+                                break;
+                            default:
+                                throw new IllegalStateException("Unexpected operand class ("
+                                        + defaultReturnOperandClass + ")");
+                        }
+                        localForceArrayFlag = false;
+                        break;
+                    default:
+                        throw new IllegalStateException("Unexpected operand class ("
+                                + desiredOperandClass + ")");
+                }
+
+            }
+        }
+
+        for (int i = 0; i < children.length; i++) {
+            ParseNode child = children[i];
+            byte paramOperandClass = afp.getParameterClass(i);
+            transformNode(child, paramOperandClass, localForceArrayFlag);
+        }
+    }
+
+    private void setSimpleValueFuncClass(AbstractFunctionPtg afp,
+            byte desiredOperandClass, boolean callerForceArrayFlag) {
+
+        if (callerForceArrayFlag  || desiredOperandClass == Ptg.CLASS_ARRAY) {
+            afp.setClass(Ptg.CLASS_ARRAY);
+        } else {
+            afp.setClass(Ptg.CLASS_VALUE);
+        }
+    }
 }

Modified: poi/trunk/poi/src/main/java/org/apache/poi/ss/formula/OperationEvaluatorFactory.java
URL: http://svn.apache.org/viewvc/poi/trunk/poi/src/main/java/org/apache/poi/ss/formula/OperationEvaluatorFactory.java?rev=1890120&r1=1890119&r2=1890120&view=diff
==============================================================================
--- poi/trunk/poi/src/main/java/org/apache/poi/ss/formula/OperationEvaluatorFactory.java (original)
+++ poi/trunk/poi/src/main/java/org/apache/poi/ss/formula/OperationEvaluatorFactory.java Sat May 22 20:56:44 2021
@@ -62,84 +62,84 @@ import org.apache.poi.ss.util.CellRangeA
  */
 final class OperationEvaluatorFactory {
 
-	private static final Map<Byte, Function> _instancesByPtgClass = initialiseInstancesMap();
+    private static final Map<Byte, Function> _instancesByPtgClass = initialiseInstancesMap();
 
-	private OperationEvaluatorFactory() {
-		// no instances of this class
-	}
-
-	private static Map<Byte, Function> initialiseInstancesMap() {
-		Map<Byte, Function> m = new HashMap<>(32);
-
-		m.put(AddPtg.instance.getSid(), TwoOperandNumericOperation.AddEval); // 0x03
-		m.put(SubtractPtg.instance.getSid(), TwoOperandNumericOperation.SubtractEval); // 0x04
-		m.put(MultiplyPtg.instance.getSid(), TwoOperandNumericOperation.MultiplyEval); // 0x05
-		m.put(DividePtg.instance.getSid(), TwoOperandNumericOperation.DivideEval); // 0x06
-		m.put(PowerPtg.instance.getSid(), TwoOperandNumericOperation.PowerEval); // 0x07
-		m.put(ConcatPtg.instance.getSid(), ConcatEval.instance); // 0x08
-		m.put(LessThanPtg.instance.getSid(), RelationalOperationEval.LessThanEval); // 0x09
-		m.put(LessEqualPtg.instance.getSid(), RelationalOperationEval.LessEqualEval); // 0x0a
-		m.put(EqualPtg.instance.getSid(), RelationalOperationEval.EqualEval); // 0x0b
-		m.put(GreaterEqualPtg.instance.getSid(), RelationalOperationEval.GreaterEqualEval); // 0x0c
-		m.put(GreaterThanPtg.instance.getSid(), RelationalOperationEval.GreaterThanEval); // 0x0D
-		m.put(NotEqualPtg.instance.getSid(), RelationalOperationEval.NotEqualEval); // 0x0e
-		m.put(IntersectionPtg.instance.getSid(), IntersectionEval.instance); // 0x0f
-		m.put(RangePtg.instance.getSid(), RangeEval.instance); // 0x11
-		m.put(UnaryPlusPtg.instance.getSid(), UnaryPlusEval.instance); // 0x12
-		m.put(UnaryMinusPtg.instance.getSid(), UnaryMinusEval.instance); // 0x13
-		m.put(PercentPtg.instance.getSid(), PercentEval.instance); // 0x14
-
-		return m;
-	}
-
-	/**
-	 * returns the OperationEval concrete impl instance corresponding
-	 * to the supplied operationPtg
-	 */
-	public static ValueEval evaluate(OperationPtg ptg, ValueEval[] args,
-			OperationEvaluationContext ec) {
-		if(ptg == null) {
-			throw new IllegalArgumentException("ptg must not be null");
-		}
-		Function result = _instancesByPtgClass.get(ptg.getSid());
-		FreeRefFunction udfFunc = null;
-		if (result == null) {
-			if (ptg instanceof AbstractFunctionPtg) {
-				AbstractFunctionPtg fptg = (AbstractFunctionPtg)ptg;
-				int functionIndex = fptg.getFunctionIndex();
-				switch (functionIndex) {
-					case FunctionMetadataRegistry.FUNCTION_INDEX_INDIRECT:
-						udfFunc = Indirect.instance;
-						break;
-					case FunctionMetadataRegistry.FUNCTION_INDEX_EXTERNAL:
-						udfFunc = UserDefinedFunction.instance;
-						break;
-					default:
-						result = FunctionEval.getBasicFunction(functionIndex);
-						break;
-				}
-			}
-		}
-		if (result != null) {
-			EvaluationSheet evalSheet = ec.getWorkbook().getSheet(ec.getSheetIndex());
-			EvaluationCell evalCell = evalSheet.getCell(ec.getRowIndex(), ec.getColumnIndex());
-
-		    if (evalCell != null && result instanceof ArrayFunction) {
-				ArrayFunction func = (ArrayFunction) result;
-				if(evalCell.isPartOfArrayFormulaGroup()){
-					// array arguments must be evaluated relative to the function defining range
-					CellRangeAddress ca = evalCell.getArrayFormulaRange();
-					return func.evaluateArray(args, ca.getFirstRow(), ca.getFirstColumn());
-				} else if (ec.isArraymode()){
-					return func.evaluateArray(args, ec.getRowIndex(), ec.getColumnIndex());
-				}
-			}
-
-			return  result.evaluate(args, ec.getRowIndex(), ec.getColumnIndex());
-		} else if (udfFunc != null){
-			return  udfFunc.evaluate(args, ec);
-		}
+    private OperationEvaluatorFactory() {
+        // no instances of this class
+    }
+
+    private static Map<Byte, Function> initialiseInstancesMap() {
+        Map<Byte, Function> m = new HashMap<>(32);
+
+        m.put(AddPtg.instance.getSid(), TwoOperandNumericOperation.AddEval); // 0x03
+        m.put(SubtractPtg.instance.getSid(), TwoOperandNumericOperation.SubtractEval); // 0x04
+        m.put(MultiplyPtg.instance.getSid(), TwoOperandNumericOperation.MultiplyEval); // 0x05
+        m.put(DividePtg.instance.getSid(), TwoOperandNumericOperation.DivideEval); // 0x06
+        m.put(PowerPtg.instance.getSid(), TwoOperandNumericOperation.PowerEval); // 0x07
+        m.put(ConcatPtg.instance.getSid(), ConcatEval.instance); // 0x08
+        m.put(LessThanPtg.instance.getSid(), RelationalOperationEval.LessThanEval); // 0x09
+        m.put(LessEqualPtg.instance.getSid(), RelationalOperationEval.LessEqualEval); // 0x0a
+        m.put(EqualPtg.instance.getSid(), RelationalOperationEval.EqualEval); // 0x0b
+        m.put(GreaterEqualPtg.instance.getSid(), RelationalOperationEval.GreaterEqualEval); // 0x0c
+        m.put(GreaterThanPtg.instance.getSid(), RelationalOperationEval.GreaterThanEval); // 0x0D
+        m.put(NotEqualPtg.instance.getSid(), RelationalOperationEval.NotEqualEval); // 0x0e
+        m.put(IntersectionPtg.instance.getSid(), IntersectionEval.instance); // 0x0f
+        m.put(RangePtg.instance.getSid(), RangeEval.instance); // 0x11
+        m.put(UnaryPlusPtg.instance.getSid(), UnaryPlusEval.instance); // 0x12
+        m.put(UnaryMinusPtg.instance.getSid(), UnaryMinusEval.instance); // 0x13
+        m.put(PercentPtg.instance.getSid(), PercentEval.instance); // 0x14
+
+        return m;
+    }
+
+    /**
+     * returns the OperationEval concrete impl instance corresponding
+     * to the supplied operationPtg
+     */
+    public static ValueEval evaluate(OperationPtg ptg, ValueEval[] args,
+            OperationEvaluationContext ec) {
+        if(ptg == null) {
+            throw new IllegalArgumentException("ptg must not be null");
+        }
+        Function result = _instancesByPtgClass.get(ptg.getSid());
+        FreeRefFunction udfFunc = null;
+        if (result == null) {
+            if (ptg instanceof AbstractFunctionPtg) {
+                AbstractFunctionPtg fptg = (AbstractFunctionPtg)ptg;
+                int functionIndex = fptg.getFunctionIndex();
+                switch (functionIndex) {
+                    case FunctionMetadataRegistry.FUNCTION_INDEX_INDIRECT:
+                        udfFunc = Indirect.instance;
+                        break;
+                    case FunctionMetadataRegistry.FUNCTION_INDEX_EXTERNAL:
+                        udfFunc = UserDefinedFunction.instance;
+                        break;
+                    default:
+                        result = FunctionEval.getBasicFunction(functionIndex);
+                        break;
+                }
+            }
+        }
+        if (result != null) {
+            EvaluationSheet evalSheet = ec.getWorkbook().getSheet(ec.getSheetIndex());
+            EvaluationCell evalCell = evalSheet.getCell(ec.getRowIndex(), ec.getColumnIndex());
+
+            if (evalCell != null && result instanceof ArrayFunction) {
+                ArrayFunction func = (ArrayFunction) result;
+                if(evalCell.isPartOfArrayFormulaGroup()){
+                    // array arguments must be evaluated relative to the function defining range
+                    CellRangeAddress ca = evalCell.getArrayFormulaRange();
+                    return func.evaluateArray(args, ca.getFirstRow(), ca.getFirstColumn());
+                } else if (ec.isArraymode()){
+                    return func.evaluateArray(args, ec.getRowIndex(), ec.getColumnIndex());
+                }
+            }
+
+            return  result.evaluate(args, ec.getRowIndex(), ec.getColumnIndex());
+        } else if (udfFunc != null){
+            return  udfFunc.evaluate(args, ec);
+        }
 
-		throw new RuntimeException("Unexpected operation ptg class (" + ptg.getClass().getName() + ")");
-	}
+        throw new RuntimeException("Unexpected operation ptg class (" + ptg.getClass().getName() + ")");
+    }
 }

Modified: poi/trunk/poi/src/main/java/org/apache/poi/ss/formula/ParseNode.java
URL: http://svn.apache.org/viewvc/poi/trunk/poi/src/main/java/org/apache/poi/ss/formula/ParseNode.java?rev=1890120&r1=1890119&r2=1890120&view=diff
==============================================================================
--- poi/trunk/poi/src/main/java/org/apache/poi/ss/formula/ParseNode.java (original)
+++ poi/trunk/poi/src/main/java/org/apache/poi/ss/formula/ParseNode.java Sat May 22 20:56:44 2021
@@ -31,177 +31,177 @@ import org.apache.poi.ss.formula.functio
  */
 final class ParseNode {
 
-	public static final ParseNode[] EMPTY_ARRAY = { };
-	private final Ptg _token;
-	private final ParseNode[] _children;
-	private final boolean _isIf;
-	private final int _tokenCount;
-
-	public ParseNode(Ptg token, ParseNode[] children) {
-		if (token == null) {
-			throw new IllegalArgumentException("token must not be null");
-		}
-		_token = token;
-		_children = children.clone();
-		_isIf = isIf(token);
-		int tokenCount = 1;
-		for (ParseNode child : children) {
-			tokenCount += child.getTokenCount();
-		}
-		if (_isIf) {
-			// there will be 2 or 3 extra tAttr tokens according to whether the false param is present
-			tokenCount += children.length;
-		}
-		_tokenCount = tokenCount;
-	}
-	public ParseNode(Ptg token) {
-		this(token, EMPTY_ARRAY);
-	}
-	public ParseNode(Ptg token, ParseNode child0) {
-		this(token, new ParseNode[] { child0, });
-	}
-	public ParseNode(Ptg token, ParseNode child0, ParseNode child1) {
-		this(token, new ParseNode[] { child0, child1, });
-	}
-	private int getTokenCount() {
-		return _tokenCount;
-	}
-	public int getEncodedSize() {
-		int result = _token instanceof ArrayPtg ? ArrayPtg.PLAIN_TOKEN_SIZE : _token.getSize();
-		for (ParseNode child : _children) {
-			result += child.getEncodedSize();
-		}
-		return result;
-	}
-
-	/**
-	 * Collects the array of {@code Ptg} tokens for the specified tree.
-	 */
-	public static Ptg[] toTokenArray(ParseNode rootNode) {
-		TokenCollector temp = new TokenCollector(rootNode.getTokenCount());
-		rootNode.collectPtgs(temp);
-		return temp.getResult();
-	}
-	private void collectPtgs(TokenCollector temp) {
-		if (isIf(_token)) {
-			collectIfPtgs(temp);
-			return;
-		}
-		boolean isPreFixOperator = _token instanceof MemFuncPtg || _token instanceof MemAreaPtg;
-		if (isPreFixOperator) {
-			temp.add(_token);
-		}
-		for (int i=0; i< getChildren().length; i++) {
-			getChildren()[i].collectPtgs(temp);
-		}
-		if (!isPreFixOperator) {
-			temp.add(_token);
-		}
-	}
-	/**
-	 * The IF() function gets marked up with two or three tAttr tokens.
-	 * Similar logic will be required for CHOOSE() when it is supported
-	 *
-	 * See excelfileformat.pdf sec 3.10.5 "tAttr (19H)
-	 */
-	private void collectIfPtgs(TokenCollector temp) {
-
-		// condition goes first
-		getChildren()[0].collectPtgs(temp);
-
-		// placeholder for tAttrIf
-		int ifAttrIndex = temp.createPlaceholder();
-
-		// true parameter
-		getChildren()[1].collectPtgs(temp);
-
-		// placeholder for first skip attr
-		int skipAfterTrueParamIndex = temp.createPlaceholder();
-		int trueParamSize = temp.sumTokenSizes(ifAttrIndex+1, skipAfterTrueParamIndex);
-
-		AttrPtg attrIf = AttrPtg.createIf(trueParamSize + 4); // distance to start of false parameter/tFuncVar. +4 for tAttrSkip after true
-
-		if (getChildren().length > 2) {
-			// false param present
-
-			// false parameter
-			getChildren()[2].collectPtgs(temp);
-
-			int skipAfterFalseParamIndex = temp.createPlaceholder();
-
-			int falseParamSize =  temp.sumTokenSizes(skipAfterTrueParamIndex+1, skipAfterFalseParamIndex);
-
-			AttrPtg attrSkipAfterTrue = AttrPtg.createSkip(falseParamSize + 4 + 4 - 1); // 1 less than distance to end of if FuncVar(size=4). +4 for attr skip before
-			AttrPtg attrSkipAfterFalse = AttrPtg.createSkip(4 - 1); // 1 less than distance to end of if FuncVar(size=4).
-
-			temp.setPlaceholder(ifAttrIndex, attrIf);
-			temp.setPlaceholder(skipAfterTrueParamIndex, attrSkipAfterTrue);
-			temp.setPlaceholder(skipAfterFalseParamIndex, attrSkipAfterFalse);
-		} else {
-			// false parameter not present
-			AttrPtg attrSkipAfterTrue = AttrPtg.createSkip(4 - 1); // 1 less than distance to end of if FuncVar(size=4).
-
-			temp.setPlaceholder(ifAttrIndex, attrIf);
-			temp.setPlaceholder(skipAfterTrueParamIndex, attrSkipAfterTrue);
-		}
-		temp.add(_token);
-	}
-
-	private static boolean isIf(Ptg token) {
-		if (token instanceof FuncVarPtg) {
-			FuncVarPtg func = (FuncVarPtg) token;
-			return FunctionMetadataRegistry.FUNCTION_NAME_IF.equals(func.getName());
-		}
-		return false;
-	}
-
-	public Ptg getToken() {
-		return _token;
-	}
-
-	public ParseNode[] getChildren() {
-		return _children;
-	}
-
-	private static final class TokenCollector {
-
-		private final Ptg[] _ptgs;
-		private int _offset;
-
-		public TokenCollector(int tokenCount) {
-			_ptgs = new Ptg[tokenCount];
-			_offset = 0;
-		}
-
-		public int sumTokenSizes(int fromIx, int toIx) {
-			int result = 0;
-			for (int i=fromIx; i<toIx; i++) {
-				result += _ptgs[i].getSize();
-			}
-			return result;
-		}
-
-		public int createPlaceholder() {
-			return _offset++;
-		}
-
-		public void add(Ptg token) {
-			if (token == null) {
-				throw new IllegalArgumentException("token must not be null");
-			}
-			_ptgs[_offset] = token;
-			_offset++;
-		}
-
-		public void setPlaceholder(int index, Ptg token) {
-			if (_ptgs[index] != null) {
-				throw new IllegalStateException("Invalid placeholder index (" + index + ")");
-			}
-			_ptgs[index] = token;
-		}
-
-		public Ptg[] getResult() {
-			return _ptgs;
-		}
-	}
+    public static final ParseNode[] EMPTY_ARRAY = { };
+    private final Ptg _token;
+    private final ParseNode[] _children;
+    private final boolean _isIf;
+    private final int _tokenCount;
+
+    public ParseNode(Ptg token, ParseNode[] children) {
+        if (token == null) {
+            throw new IllegalArgumentException("token must not be null");
+        }
+        _token = token;
+        _children = children.clone();
+        _isIf = isIf(token);
+        int tokenCount = 1;
+        for (ParseNode child : children) {
+            tokenCount += child.getTokenCount();
+        }
+        if (_isIf) {
+            // there will be 2 or 3 extra tAttr tokens according to whether the false param is present
+            tokenCount += children.length;
+        }
+        _tokenCount = tokenCount;
+    }
+    public ParseNode(Ptg token) {
+        this(token, EMPTY_ARRAY);
+    }
+    public ParseNode(Ptg token, ParseNode child0) {
+        this(token, new ParseNode[] { child0, });
+    }
+    public ParseNode(Ptg token, ParseNode child0, ParseNode child1) {
+        this(token, new ParseNode[] { child0, child1, });
+    }
+    private int getTokenCount() {
+        return _tokenCount;
+    }
+    public int getEncodedSize() {
+        int result = _token instanceof ArrayPtg ? ArrayPtg.PLAIN_TOKEN_SIZE : _token.getSize();
+        for (ParseNode child : _children) {
+            result += child.getEncodedSize();
+        }
+        return result;
+    }
+
+    /**
+     * Collects the array of {@code Ptg} tokens for the specified tree.
+     */
+    public static Ptg[] toTokenArray(ParseNode rootNode) {
+        TokenCollector temp = new TokenCollector(rootNode.getTokenCount());
+        rootNode.collectPtgs(temp);
+        return temp.getResult();
+    }
+    private void collectPtgs(TokenCollector temp) {
+        if (isIf(_token)) {
+            collectIfPtgs(temp);
+            return;
+        }
+        boolean isPreFixOperator = _token instanceof MemFuncPtg || _token instanceof MemAreaPtg;
+        if (isPreFixOperator) {
+            temp.add(_token);
+        }
+        for (int i=0; i< getChildren().length; i++) {
+            getChildren()[i].collectPtgs(temp);
+        }
+        if (!isPreFixOperator) {
+            temp.add(_token);
+        }
+    }
+    /**
+     * The IF() function gets marked up with two or three tAttr tokens.
+     * Similar logic will be required for CHOOSE() when it is supported
+     *
+     * See excelfileformat.pdf sec 3.10.5 "tAttr (19H)
+     */
+    private void collectIfPtgs(TokenCollector temp) {
+
+        // condition goes first
+        getChildren()[0].collectPtgs(temp);
+
+        // placeholder for tAttrIf
+        int ifAttrIndex = temp.createPlaceholder();
+
+        // true parameter
+        getChildren()[1].collectPtgs(temp);
+
+        // placeholder for first skip attr
+        int skipAfterTrueParamIndex = temp.createPlaceholder();
+        int trueParamSize = temp.sumTokenSizes(ifAttrIndex+1, skipAfterTrueParamIndex);
+
+        AttrPtg attrIf = AttrPtg.createIf(trueParamSize + 4); // distance to start of false parameter/tFuncVar. +4 for tAttrSkip after true
+
+        if (getChildren().length > 2) {
+            // false param present
+
+            // false parameter
+            getChildren()[2].collectPtgs(temp);
+
+            int skipAfterFalseParamIndex = temp.createPlaceholder();
+
+            int falseParamSize =  temp.sumTokenSizes(skipAfterTrueParamIndex+1, skipAfterFalseParamIndex);
+
+            AttrPtg attrSkipAfterTrue = AttrPtg.createSkip(falseParamSize + 4 + 4 - 1); // 1 less than distance to end of if FuncVar(size=4). +4 for attr skip before
+            AttrPtg attrSkipAfterFalse = AttrPtg.createSkip(4 - 1); // 1 less than distance to end of if FuncVar(size=4).
+
+            temp.setPlaceholder(ifAttrIndex, attrIf);
+            temp.setPlaceholder(skipAfterTrueParamIndex, attrSkipAfterTrue);
+            temp.setPlaceholder(skipAfterFalseParamIndex, attrSkipAfterFalse);
+        } else {
+            // false parameter not present
+            AttrPtg attrSkipAfterTrue = AttrPtg.createSkip(4 - 1); // 1 less than distance to end of if FuncVar(size=4).
+
+            temp.setPlaceholder(ifAttrIndex, attrIf);
+            temp.setPlaceholder(skipAfterTrueParamIndex, attrSkipAfterTrue);
+        }
+        temp.add(_token);
+    }
+
+    private static boolean isIf(Ptg token) {
+        if (token instanceof FuncVarPtg) {
+            FuncVarPtg func = (FuncVarPtg) token;
+            return FunctionMetadataRegistry.FUNCTION_NAME_IF.equals(func.getName());
+        }
+        return false;
+    }
+
+    public Ptg getToken() {
+        return _token;
+    }
+
+    public ParseNode[] getChildren() {
+        return _children;
+    }
+
+    private static final class TokenCollector {
+
+        private final Ptg[] _ptgs;
+        private int _offset;
+
+        public TokenCollector(int tokenCount) {
+            _ptgs = new Ptg[tokenCount];
+            _offset = 0;
+        }
+
+        public int sumTokenSizes(int fromIx, int toIx) {
+            int result = 0;
+            for (int i=fromIx; i<toIx; i++) {
+                result += _ptgs[i].getSize();
+            }
+            return result;
+        }
+
+        public int createPlaceholder() {
+            return _offset++;
+        }
+
+        public void add(Ptg token) {
+            if (token == null) {
+                throw new IllegalArgumentException("token must not be null");
+            }
+            _ptgs[_offset] = token;
+            _offset++;
+        }
+
+        public void setPlaceholder(int index, Ptg token) {
+            if (_ptgs[index] != null) {
+                throw new IllegalStateException("Invalid placeholder index (" + index + ")");
+            }
+            _ptgs[index] = token;
+        }
+
+        public Ptg[] getResult() {
+            return _ptgs;
+        }
+    }
 }

Modified: poi/trunk/poi/src/main/java/org/apache/poi/ss/formula/PlainCellCache.java
URL: http://svn.apache.org/viewvc/poi/trunk/poi/src/main/java/org/apache/poi/ss/formula/PlainCellCache.java?rev=1890120&r1=1890119&r2=1890120&view=diff
==============================================================================
--- poi/trunk/poi/src/main/java/org/apache/poi/ss/formula/PlainCellCache.java (original)
+++ poi/trunk/poi/src/main/java/org/apache/poi/ss/formula/PlainCellCache.java Sat May 22 20:56:44 2021
@@ -22,49 +22,49 @@ import java.util.Map;
 
 final class PlainCellCache {
 
-	public static final class Loc {
+    public static final class Loc {
 
-		private final long _bookSheetColumn;
+        private final long _bookSheetColumn;
 
-		private final int _rowIndex;
+        private final int _rowIndex;
 
-		public Loc(int bookIndex, int sheetIndex, int rowIndex, int columnIndex) {
-			_bookSheetColumn = toBookSheetColumn(bookIndex, sheetIndex, columnIndex);
-			_rowIndex = rowIndex;
-		}
+        public Loc(int bookIndex, int sheetIndex, int rowIndex, int columnIndex) {
+            _bookSheetColumn = toBookSheetColumn(bookIndex, sheetIndex, columnIndex);
+            _rowIndex = rowIndex;
+        }
 
-		public static long toBookSheetColumn(int bookIndex, int sheetIndex, int columnIndex) {
-			return ((bookIndex   & 0xFFFFL) << 48)  +
+        public static long toBookSheetColumn(int bookIndex, int sheetIndex, int columnIndex) {
+            return ((bookIndex   & 0xFFFFL) << 48)  +
                    ((sheetIndex  & 0xFFFFL) << 32) +
                    ((columnIndex & 0xFFFFL) << 0);
-		}
+        }
 
-		public Loc(long bookSheetColumn, int rowIndex) {
-			_bookSheetColumn = bookSheetColumn;
-			_rowIndex = rowIndex;
-		}
+        public Loc(long bookSheetColumn, int rowIndex) {
+            _bookSheetColumn = bookSheetColumn;
+            _rowIndex = rowIndex;
+        }
 
-		@Override
+        @Override
         public int hashCode() {
-			return (int)(_bookSheetColumn ^ (_bookSheetColumn >>> 32)) + 17 * _rowIndex;
-		}
+            return (int)(_bookSheetColumn ^ (_bookSheetColumn >>> 32)) + 17 * _rowIndex;
+        }
 
-		@Override
+        @Override
         public boolean equals(Object obj) {
-		    if (!(obj instanceof Loc)) {
-		        return false;
-		    }
-			Loc other = (Loc) obj;
-			return _bookSheetColumn == other._bookSheetColumn && _rowIndex == other._rowIndex;
-		}
-
-		public int getRowIndex() {
-			return _rowIndex;
-		}
+            if (!(obj instanceof Loc)) {
+                return false;
+            }
+            Loc other = (Loc) obj;
+            return _bookSheetColumn == other._bookSheetColumn && _rowIndex == other._rowIndex;
+        }
+
+        public int getRowIndex() {
+            return _rowIndex;
+        }
 
-		public int getColumnIndex() {
+        public int getColumnIndex() {
             return (int)(_bookSheetColumn & 0x000FFFF);
-		}
+        }
 
         public int getSheetIndex() {
             return (int)((_bookSheetColumn >> 32) & 0xFFFF);
@@ -73,27 +73,27 @@ final class PlainCellCache {
         public int getBookIndex() {
             return (int)((_bookSheetColumn >> 48) & 0xFFFF);
         }
-	}
+    }
 
-	private Map<Loc, PlainValueCellCacheEntry> _plainValueEntriesByLoc;
+    private Map<Loc, PlainValueCellCacheEntry> _plainValueEntriesByLoc;
 
-	public PlainCellCache() {
-		_plainValueEntriesByLoc = new HashMap<>();
-	}
+    public PlainCellCache() {
+        _plainValueEntriesByLoc = new HashMap<>();
+    }
 
-	public void put(Loc key, PlainValueCellCacheEntry cce) {
-		_plainValueEntriesByLoc.put(key, cce);
-	}
+    public void put(Loc key, PlainValueCellCacheEntry cce) {
+        _plainValueEntriesByLoc.put(key, cce);
+    }
 
-	public void clear() {
-		_plainValueEntriesByLoc.clear();
-	}
+    public void clear() {
+        _plainValueEntriesByLoc.clear();
+    }
 
-	public PlainValueCellCacheEntry get(Loc key) {
-		return _plainValueEntriesByLoc.get(key);
-	}
+    public PlainValueCellCacheEntry get(Loc key) {
+        return _plainValueEntriesByLoc.get(key);
+    }
 
-	public void remove(Loc key) {
-		_plainValueEntriesByLoc.remove(key);
-	}
+    public void remove(Loc key) {
+        _plainValueEntriesByLoc.remove(key);
+    }
 }

Modified: poi/trunk/poi/src/main/java/org/apache/poi/ss/formula/PlainValueCellCacheEntry.java
URL: http://svn.apache.org/viewvc/poi/trunk/poi/src/main/java/org/apache/poi/ss/formula/PlainValueCellCacheEntry.java?rev=1890120&r1=1890119&r2=1890120&view=diff
==============================================================================
--- poi/trunk/poi/src/main/java/org/apache/poi/ss/formula/PlainValueCellCacheEntry.java (original)
+++ poi/trunk/poi/src/main/java/org/apache/poi/ss/formula/PlainValueCellCacheEntry.java Sat May 22 20:56:44 2021
@@ -23,7 +23,7 @@ import org.apache.poi.ss.formula.eval.Va
  * Used for non-formula cells, primarily to keep track of the referencing (formula) cells.
  */
 final class PlainValueCellCacheEntry extends CellCacheEntry {
-	public PlainValueCellCacheEntry(ValueEval value) {
-		updateValue(value);
-	}
+    public PlainValueCellCacheEntry(ValueEval value) {
+        updateValue(value);
+    }
 }
\ No newline at end of file

Modified: poi/trunk/poi/src/main/java/org/apache/poi/ss/formula/SheetNameFormatter.java
URL: http://svn.apache.org/viewvc/poi/trunk/poi/src/main/java/org/apache/poi/ss/formula/SheetNameFormatter.java?rev=1890120&r1=1890119&r2=1890120&view=diff
==============================================================================
--- poi/trunk/poi/src/main/java/org/apache/poi/ss/formula/SheetNameFormatter.java (original)
+++ poi/trunk/poi/src/main/java/org/apache/poi/ss/formula/SheetNameFormatter.java Sat May 22 20:56:44 2021
@@ -29,27 +29,27 @@ import org.apache.poi.ss.SpreadsheetVers
  */
 public final class SheetNameFormatter {
 
-	private static final char DELIMITER = '\'';
+    private static final char DELIMITER = '\'';
 
-	/**
-	 * Matches a single cell ref with no absolute ('$') markers
-	 */
-	private static final Pattern CELL_REF_PATTERN = Pattern.compile("([A-Za-z]+)([0-9]+)");
-
-	private SheetNameFormatter() {
-		// no instances of this class
-	}
-	/**
-	 * Used to format sheet names as they would appear in cell formula expressions.
-	 * @return the sheet name unchanged if there is no need for delimiting.  Otherwise the sheet
-	 * name is enclosed in single quotes (').  Any single quotes which were already present in the
-	 * sheet name will be converted to double single quotes ('').
-	 */
-	public static String format(String rawSheetName) {
+    /**
+     * Matches a single cell ref with no absolute ('$') markers
+     */
+    private static final Pattern CELL_REF_PATTERN = Pattern.compile("([A-Za-z]+)([0-9]+)");
+
+    private SheetNameFormatter() {
+        // no instances of this class
+    }
+    /**
+     * Used to format sheet names as they would appear in cell formula expressions.
+     * @return the sheet name unchanged if there is no need for delimiting.  Otherwise the sheet
+     * name is enclosed in single quotes (').  Any single quotes which were already present in the
+     * sheet name will be converted to double single quotes ('').
+     */
+    public static String format(String rawSheetName) {
         StringBuilder sb = new StringBuilder((rawSheetName == null ? 0 : rawSheetName.length()) + 2);
-		appendFormat(sb, rawSheetName);
-		return sb.toString();
-	}
+        appendFormat(sb, rawSheetName);
+        return sb.toString();
+    }
 
     /**
      * Convenience method for ({@link #format(String)}) when a StringBuffer is already available.
@@ -57,211 +57,211 @@ public final class SheetNameFormatter {
      * @param out - sheet name will be appended here possibly with delimiting quotes
      * @param rawSheetName - sheet name
      */
-	public static void appendFormat(Appendable out, String rawSheetName) {
-		try {
-			boolean needsQuotes = needsDelimiting(rawSheetName);
-			if(needsQuotes) {
-				out.append(DELIMITER);
-				appendAndEscape(out, rawSheetName);
-				out.append(DELIMITER);
-			} else {
-				appendAndEscape(out, rawSheetName);
-			}
-		} catch (Exception e) {
-			throw new RuntimeException(e);
-		}
-	}
-
-	/**
-	 * Convenience method for ({@link #format(String)}) when a StringBuffer is already available.
-	 *
-	 * @param out - sheet name will be appended here possibly with delimiting quotes
-	 * @param workbookName - workbook name
-	 * @param rawSheetName - sheet name
-	 */
-	public static void appendFormat(Appendable out, String workbookName, String rawSheetName) {
-		try {
-			boolean needsQuotes = needsDelimiting(workbookName) || needsDelimiting(rawSheetName);
-			if(needsQuotes) {
-				out.append(DELIMITER);
-				out.append('[');
-				appendAndEscape(out, workbookName.replace('[', '(').replace(']', ')'));
-				out.append(']');
-				appendAndEscape(out, rawSheetName);
-				out.append(DELIMITER);
-			} else {
-				out.append('[');
-				appendOrREF(out, workbookName);
-				out.append(']');
-				appendOrREF(out, rawSheetName);
-			}
-		} catch (Exception e) {
-			throw new RuntimeException(e);
-		}
-	}
-
-	private static void appendOrREF(Appendable out, String name) throws IOException {
-		if(name == null) {
-			out.append("#REF");
-		} else {
-			out.append(name);
-		}
-	}
-
-	static void appendAndEscape(Appendable sb, String rawSheetName) {
-		try {
-			if (rawSheetName == null) {
-				sb.append("#REF");
-				return;
-			}
-
-			int len = rawSheetName.length();
-			for (int i = 0; i < len; i++) {
-				char ch = rawSheetName.charAt(i);
-				if (ch == DELIMITER) {
-					// single quotes (') are encoded as ('')
-					sb.append(DELIMITER);
-				}
-				sb.append(ch);
-			}
-		} catch (Exception e) {
-			throw new RuntimeException(e);
-		}
-    }
-
-	/**
-	 * Tell if the given raw sheet name needs screening/delimiting.
-	 * @param rawSheetName the sheet name.
-	 * @return true if the given raw sheet name needs screening/delimiting, false otherwise or
-	 * 			if the sheet name is null.
-	 */
-	static boolean needsDelimiting(String rawSheetName) {
-		if(rawSheetName == null) {
-			return false;
-		}
-
-		int len = rawSheetName.length();
-		if(len < 1) {
-		    return false; // some cases we get missing external references, resulting in empty sheet names
-		}
-		if(Character.isDigit(rawSheetName.charAt(0))) {
-			// sheet name with digit in the first position always requires delimiting
-			return true;
-		}
-		for(int i=0; i<len; i++) {
-			char ch = rawSheetName.charAt(i);
-			if(isSpecialChar(ch)) {
-				return true;
-			}
-		}
-		if(Character.isLetter(rawSheetName.charAt(0))
-				&& Character.isDigit(rawSheetName.charAt(len-1))) {
-			// note - values like "A$1:$C$20" don't get this far
-			if(nameLooksLikePlainCellReference(rawSheetName)) {
-				return true;
-			}
-		}
-		if (nameLooksLikeBooleanLiteral(rawSheetName)) {
-			return true;
-		}
-		// Error constant literals all contain '#' and other special characters
-		// so they don't get this far
-		return false;
-	}
-
-	private static boolean nameLooksLikeBooleanLiteral(String rawSheetName) {
-		switch(rawSheetName.charAt(0)) {
-			case 'T': case 't':
-				return "TRUE".equalsIgnoreCase(rawSheetName);
-			case 'F': case 'f':
-				return "FALSE".equalsIgnoreCase(rawSheetName);
-		}
-		return false;
-	}
-
-	/**
-	 * @return {@code true} if the presence of the specified character in a sheet name would
-	 * require the sheet name to be delimited in formulas.  This includes every non-alphanumeric
-	 * character besides underscore '_' and dot '.'.
-	 */
-	/* package */ static boolean isSpecialChar(char ch) {
-		// note - Character.isJavaIdentifierPart() would allow dollars '$'
-		if(Character.isLetterOrDigit(ch)) {
-			return false;
-		}
-		switch(ch) {
-			case '.': // dot is OK
-			case '_': // underscore is OK
-				return false;
-			case '\n':
-			case '\r':
-			case '\t':
-				throw new RuntimeException("Illegal character (0x"
-						+ Integer.toHexString(ch) + ") found in sheet name");
-		}
-		return true;
-	}
-
-
-	/**
-	 * Used to decide whether sheet names like 'AB123' need delimiting due to the fact that they
-	 * look like cell references.
-	 * <p>
-	 * This code is currently being used for translating formulas represented with {@code Ptg}
-	 * tokens into human readable text form.  In formula expressions, a sheet name always has a
-	 * trailing '!' so there is little chance for ambiguity.  It doesn't matter too much what this
-	 * method returns but it is worth noting the likely consumers of these formula text strings:
-	 * <ol>
-	 * <li>POI's own formula parser</li>
-	 * <li>Visual reading by human</li>
-	 * <li>VBA automation entry into Excel cell contents e.g.  ActiveCell.Formula = "=c64!A1"</li>
-	 * <li>Manual entry into Excel cell contents</li>
-	 * <li>Some third party formula parser</li>
-	 * </ol>
-	 *
-	 * At the time of writing, POI's formula parser tolerates cell-like sheet names in formulas
-	 * with or without delimiters.  The same goes for Excel(2007), both manual and automated entry.
-	 * <p>
-	 * For better or worse this implementation attempts to replicate Excel's formula renderer.
-	 * Excel uses range checking on the apparent 'row' and 'column' components.  Note however that
-	 * the maximum sheet size varies across versions.
-	 * @see org.apache.poi.ss.util.CellReference
-	 */
-	/* package */ static boolean cellReferenceIsWithinRange(String lettersPrefix, String numbersSuffix) {
-		return CellReference.cellReferenceIsWithinRange(lettersPrefix, numbersSuffix, SpreadsheetVersion.EXCEL97);
-	}
-
-	/**
-	 * Note - this method assumes the specified rawSheetName has only letters and digits.  It
-	 * cannot be used to match absolute or range references (using the dollar or colon char).
-	 * <p>
-	 * Some notable cases:
-	 *    <table>
-	 *      <caption>Notable cases</caption>
-	 *      <tr><th>Input&nbsp;</th><th>Result&nbsp;</th><th>Comments</th></tr>
-	 *      <tr><td>"A1"&nbsp;&nbsp;</td><td>true</td><td>&nbsp;</td></tr>
-	 *      <tr><td>"a111"&nbsp;&nbsp;</td><td>true</td><td>&nbsp;</td></tr>
-	 *      <tr><td>"AA"&nbsp;&nbsp;</td><td>false</td><td>&nbsp;</td></tr>
-	 *      <tr><td>"aa1"&nbsp;&nbsp;</td><td>true</td><td>&nbsp;</td></tr>
-	 *      <tr><td>"A1A"&nbsp;&nbsp;</td><td>false</td><td>&nbsp;</td></tr>
-	 *      <tr><td>"A1A1"&nbsp;&nbsp;</td><td>false</td><td>&nbsp;</td></tr>
-	 *      <tr><td>"A$1:$C$20"&nbsp;&nbsp;</td><td>false</td><td>Not a plain cell reference</td></tr>
-	 *      <tr><td>"SALES20080101"&nbsp;&nbsp;</td><td>true</td>
-	 *      <td>Still needs delimiting even though well out of range</td></tr>
-	 *    </table>
-	 *
-	 * @return {@code true} if there is any possible ambiguity that the specified rawSheetName
-	 * could be interpreted as a valid cell name.
-	 */
-	/* package */ static boolean nameLooksLikePlainCellReference(String rawSheetName) {
-		Matcher matcher = CELL_REF_PATTERN.matcher(rawSheetName);
-		if(!matcher.matches()) {
-			return false;
-		}
-
-		// rawSheetName == "Sheet1" gets this far.
-		String lettersPrefix = matcher.group(1);
-		String numbersSuffix = matcher.group(2);
-		return cellReferenceIsWithinRange(lettersPrefix, numbersSuffix);
-	}
+    public static void appendFormat(Appendable out, String rawSheetName) {
+        try {
+            boolean needsQuotes = needsDelimiting(rawSheetName);
+            if(needsQuotes) {
+                out.append(DELIMITER);
+                appendAndEscape(out, rawSheetName);
+                out.append(DELIMITER);
+            } else {
+                appendAndEscape(out, rawSheetName);
+            }
+        } catch (Exception e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    /**
+     * Convenience method for ({@link #format(String)}) when a StringBuffer is already available.
+     *
+     * @param out - sheet name will be appended here possibly with delimiting quotes
+     * @param workbookName - workbook name
+     * @param rawSheetName - sheet name
+     */
+    public static void appendFormat(Appendable out, String workbookName, String rawSheetName) {
+        try {
+            boolean needsQuotes = needsDelimiting(workbookName) || needsDelimiting(rawSheetName);
+            if(needsQuotes) {
+                out.append(DELIMITER);
+                out.append('[');
+                appendAndEscape(out, workbookName.replace('[', '(').replace(']', ')'));
+                out.append(']');
+                appendAndEscape(out, rawSheetName);
+                out.append(DELIMITER);
+            } else {
+                out.append('[');
+                appendOrREF(out, workbookName);
+                out.append(']');
+                appendOrREF(out, rawSheetName);
+            }
+        } catch (Exception e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    private static void appendOrREF(Appendable out, String name) throws IOException {
+        if(name == null) {
+            out.append("#REF");
+        } else {
+            out.append(name);
+        }
+    }
+
+    static void appendAndEscape(Appendable sb, String rawSheetName) {
+        try {
+            if (rawSheetName == null) {
+                sb.append("#REF");
+                return;
+            }
+
+            int len = rawSheetName.length();
+            for (int i = 0; i < len; i++) {
+                char ch = rawSheetName.charAt(i);
+                if (ch == DELIMITER) {
+                    // single quotes (') are encoded as ('')
+                    sb.append(DELIMITER);
+                }
+                sb.append(ch);
+            }
+        } catch (Exception e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    /**
+     * Tell if the given raw sheet name needs screening/delimiting.
+     * @param rawSheetName the sheet name.
+     * @return true if the given raw sheet name needs screening/delimiting, false otherwise or
+     *          if the sheet name is null.
+     */
+    static boolean needsDelimiting(String rawSheetName) {
+        if(rawSheetName == null) {
+            return false;
+        }
+
+        int len = rawSheetName.length();
+        if(len < 1) {
+            return false; // some cases we get missing external references, resulting in empty sheet names
+        }
+        if(Character.isDigit(rawSheetName.charAt(0))) {
+            // sheet name with digit in the first position always requires delimiting
+            return true;
+        }
+        for(int i=0; i<len; i++) {
+            char ch = rawSheetName.charAt(i);
+            if(isSpecialChar(ch)) {
+                return true;
+            }
+        }
+        if(Character.isLetter(rawSheetName.charAt(0))
+                && Character.isDigit(rawSheetName.charAt(len-1))) {
+            // note - values like "A$1:$C$20" don't get this far
+            if(nameLooksLikePlainCellReference(rawSheetName)) {
+                return true;
+            }
+        }
+        if (nameLooksLikeBooleanLiteral(rawSheetName)) {
+            return true;
+        }
+        // Error constant literals all contain '#' and other special characters
+        // so they don't get this far
+        return false;
+    }
+
+    private static boolean nameLooksLikeBooleanLiteral(String rawSheetName) {
+        switch(rawSheetName.charAt(0)) {
+            case 'T': case 't':
+                return "TRUE".equalsIgnoreCase(rawSheetName);
+            case 'F': case 'f':
+                return "FALSE".equalsIgnoreCase(rawSheetName);
+        }
+        return false;
+    }
+
+    /**
+     * @return {@code true} if the presence of the specified character in a sheet name would
+     * require the sheet name to be delimited in formulas.  This includes every non-alphanumeric
+     * character besides underscore '_' and dot '.'.
+     */
+    /* package */ static boolean isSpecialChar(char ch) {
+        // note - Character.isJavaIdentifierPart() would allow dollars '$'
+        if(Character.isLetterOrDigit(ch)) {
+            return false;
+        }
+        switch(ch) {
+            case '.': // dot is OK
+            case '_': // underscore is OK
+                return false;
+            case '\n':
+            case '\r':
+            case '\t':
+                throw new RuntimeException("Illegal character (0x"
+                        + Integer.toHexString(ch) + ") found in sheet name");
+        }
+        return true;
+    }
+
+
+    /**
+     * Used to decide whether sheet names like 'AB123' need delimiting due to the fact that they
+     * look like cell references.
+     * <p>
+     * This code is currently being used for translating formulas represented with {@code Ptg}
+     * tokens into human readable text form.  In formula expressions, a sheet name always has a
+     * trailing '!' so there is little chance for ambiguity.  It doesn't matter too much what this
+     * method returns but it is worth noting the likely consumers of these formula text strings:
+     * <ol>
+     * <li>POI's own formula parser</li>
+     * <li>Visual reading by human</li>
+     * <li>VBA automation entry into Excel cell contents e.g.  ActiveCell.Formula = "=c64!A1"</li>
+     * <li>Manual entry into Excel cell contents</li>
+     * <li>Some third party formula parser</li>
+     * </ol>
+     *
+     * At the time of writing, POI's formula parser tolerates cell-like sheet names in formulas
+     * with or without delimiters.  The same goes for Excel(2007), both manual and automated entry.
+     * <p>
+     * For better or worse this implementation attempts to replicate Excel's formula renderer.
+     * Excel uses range checking on the apparent 'row' and 'column' components.  Note however that
+     * the maximum sheet size varies across versions.
+     * @see org.apache.poi.ss.util.CellReference
+     */
+    /* package */ static boolean cellReferenceIsWithinRange(String lettersPrefix, String numbersSuffix) {
+        return CellReference.cellReferenceIsWithinRange(lettersPrefix, numbersSuffix, SpreadsheetVersion.EXCEL97);
+    }
+
+    /**
+     * Note - this method assumes the specified rawSheetName has only letters and digits.  It
+     * cannot be used to match absolute or range references (using the dollar or colon char).
+     * <p>
+     * Some notable cases:
+     *    <table>
+     *      <caption>Notable cases</caption>
+     *      <tr><th>Input&nbsp;</th><th>Result&nbsp;</th><th>Comments</th></tr>
+     *      <tr><td>"A1"&nbsp;&nbsp;</td><td>true</td><td>&nbsp;</td></tr>
+     *      <tr><td>"a111"&nbsp;&nbsp;</td><td>true</td><td>&nbsp;</td></tr>
+     *      <tr><td>"AA"&nbsp;&nbsp;</td><td>false</td><td>&nbsp;</td></tr>
+     *      <tr><td>"aa1"&nbsp;&nbsp;</td><td>true</td><td>&nbsp;</td></tr>
+     *      <tr><td>"A1A"&nbsp;&nbsp;</td><td>false</td><td>&nbsp;</td></tr>
+     *      <tr><td>"A1A1"&nbsp;&nbsp;</td><td>false</td><td>&nbsp;</td></tr>
+     *      <tr><td>"A$1:$C$20"&nbsp;&nbsp;</td><td>false</td><td>Not a plain cell reference</td></tr>
+     *      <tr><td>"SALES20080101"&nbsp;&nbsp;</td><td>true</td>
+     *      <td>Still needs delimiting even though well out of range</td></tr>
+     *    </table>
+     *
+     * @return {@code true} if there is any possible ambiguity that the specified rawSheetName
+     * could be interpreted as a valid cell name.
+     */
+    /* package */ static boolean nameLooksLikePlainCellReference(String rawSheetName) {
+        Matcher matcher = CELL_REF_PATTERN.matcher(rawSheetName);
+        if(!matcher.matches()) {
+            return false;
+        }
+
+        // rawSheetName == "Sheet1" gets this far.
+        String lettersPrefix = matcher.group(1);
+        String numbersSuffix = matcher.group(2);
+        return cellReferenceIsWithinRange(lettersPrefix, numbersSuffix);
+    }
 }

Modified: poi/trunk/poi/src/main/java/org/apache/poi/ss/formula/SheetRangeEvaluator.java
URL: http://svn.apache.org/viewvc/poi/trunk/poi/src/main/java/org/apache/poi/ss/formula/SheetRangeEvaluator.java?rev=1890120&r1=1890119&r2=1890120&view=diff
==============================================================================
--- poi/trunk/poi/src/main/java/org/apache/poi/ss/formula/SheetRangeEvaluator.java (original)
+++ poi/trunk/poi/src/main/java/org/apache/poi/ss/formula/SheetRangeEvaluator.java Sat May 22 20:56:44 2021
@@ -23,54 +23,54 @@ import org.apache.poi.ss.formula.eval.Va
  * Evaluator for returning cells or sheets for a range of sheets
  */
 final class SheetRangeEvaluator implements SheetRange {
-	private final int _firstSheetIndex;
+    private final int _firstSheetIndex;
     private final int _lastSheetIndex;
-	private SheetRefEvaluator[] _sheetEvaluators;
+    private SheetRefEvaluator[] _sheetEvaluators;
 
-	public SheetRangeEvaluator(int firstSheetIndex, int lastSheetIndex, SheetRefEvaluator[] sheetEvaluators) {
-		if (firstSheetIndex < 0) {
-			throw new IllegalArgumentException("Invalid firstSheetIndex: " + firstSheetIndex + ".");
-		}
+    public SheetRangeEvaluator(int firstSheetIndex, int lastSheetIndex, SheetRefEvaluator[] sheetEvaluators) {
+        if (firstSheetIndex < 0) {
+            throw new IllegalArgumentException("Invalid firstSheetIndex: " + firstSheetIndex + ".");
+        }
         if (lastSheetIndex < firstSheetIndex) {
             throw new IllegalArgumentException("Invalid lastSheetIndex: " + lastSheetIndex + " for firstSheetIndex: " + firstSheetIndex + ".");
         }
         _firstSheetIndex = firstSheetIndex;
         _lastSheetIndex = lastSheetIndex;
         _sheetEvaluators = sheetEvaluators.clone();
-	}
+    }
     public SheetRangeEvaluator(int onlySheetIndex, SheetRefEvaluator sheetEvaluator) {
         this(onlySheetIndex, onlySheetIndex, new SheetRefEvaluator[] {sheetEvaluator});
     }
-	
-	public SheetRefEvaluator getSheetEvaluator(int sheetIndex) {
-	    if (sheetIndex < _firstSheetIndex || sheetIndex > _lastSheetIndex) {
+    
+    public SheetRefEvaluator getSheetEvaluator(int sheetIndex) {
+        if (sheetIndex < _firstSheetIndex || sheetIndex > _lastSheetIndex) {
             throw new IllegalArgumentException("Invalid SheetIndex: " + sheetIndex +
                     " - Outside range " + _firstSheetIndex + " : " + _lastSheetIndex);
-	    }
-	    return _sheetEvaluators[sheetIndex-_firstSheetIndex];
-	}
-	
-	public int getFirstSheetIndex() {
-	    return _firstSheetIndex;
-	}
+        }
+        return _sheetEvaluators[sheetIndex-_firstSheetIndex];
+    }
+    
+    public int getFirstSheetIndex() {
+        return _firstSheetIndex;
+    }
     public int getLastSheetIndex() {
         return _lastSheetIndex;
     }
 
-	public String getSheetName(int sheetIndex) {
-	    return getSheetEvaluator(sheetIndex).getSheetName();
-	}
-	public String getSheetNameRange() {
-	    StringBuilder sb = new StringBuilder();
-	    sb.append(getSheetName(_firstSheetIndex));
-	    if (_firstSheetIndex != _lastSheetIndex) {
-	        sb.append(':');
-	        sb.append(getSheetName(_lastSheetIndex));
-	    }
-	    return sb.toString();
-	}
+    public String getSheetName(int sheetIndex) {
+        return getSheetEvaluator(sheetIndex).getSheetName();
+    }
+    public String getSheetNameRange() {
+        StringBuilder sb = new StringBuilder();
+        sb.append(getSheetName(_firstSheetIndex));
+        if (_firstSheetIndex != _lastSheetIndex) {
+            sb.append(':');
+            sb.append(getSheetName(_lastSheetIndex));
+        }
+        return sb.toString();
+    }
 
-	public ValueEval getEvalForCell(int sheetIndex, int rowIndex, int columnIndex) {
+    public ValueEval getEvalForCell(int sheetIndex, int rowIndex, int columnIndex) {
         return getSheetEvaluator(sheetIndex).getEvalForCell(rowIndex, columnIndex);
-	}
+    }
 }

Modified: poi/trunk/poi/src/main/java/org/apache/poi/ss/formula/SheetRefEvaluator.java
URL: http://svn.apache.org/viewvc/poi/trunk/poi/src/main/java/org/apache/poi/ss/formula/SheetRefEvaluator.java?rev=1890120&r1=1890119&r2=1890120&view=diff
==============================================================================
--- poi/trunk/poi/src/main/java/org/apache/poi/ss/formula/SheetRefEvaluator.java (original)
+++ poi/trunk/poi/src/main/java/org/apache/poi/ss/formula/SheetRefEvaluator.java Sat May 22 20:56:44 2021
@@ -26,34 +26,34 @@ import org.apache.poi.ss.usermodel.CellT
  * Evaluator for cells within a specific Sheet
  */
 final class SheetRefEvaluator {
-	private final WorkbookEvaluator _bookEvaluator;
-	private final EvaluationTracker _tracker;
-	private final int _sheetIndex;
-	private EvaluationSheet _sheet;
+    private final WorkbookEvaluator _bookEvaluator;
+    private final EvaluationTracker _tracker;
+    private final int _sheetIndex;
+    private EvaluationSheet _sheet;
 
-	public SheetRefEvaluator(WorkbookEvaluator bookEvaluator, EvaluationTracker tracker, int sheetIndex) {
-		if (sheetIndex < 0) {
-			throw new IllegalArgumentException("Invalid sheetIndex: " + sheetIndex + ".");
-		}
-		_bookEvaluator = bookEvaluator;
-		_tracker = tracker;
-		_sheetIndex = sheetIndex;
-	}
+    public SheetRefEvaluator(WorkbookEvaluator bookEvaluator, EvaluationTracker tracker, int sheetIndex) {
+        if (sheetIndex < 0) {
+            throw new IllegalArgumentException("Invalid sheetIndex: " + sheetIndex + ".");
+        }
+        _bookEvaluator = bookEvaluator;
+        _tracker = tracker;
+        _sheetIndex = sheetIndex;
+    }
 
-	public String getSheetName() {
-		return _bookEvaluator.getSheetName(_sheetIndex);
-	}
+    public String getSheetName() {
+        return _bookEvaluator.getSheetName(_sheetIndex);
+    }
 
-	public ValueEval getEvalForCell(int rowIndex, int columnIndex) {
-		return _bookEvaluator.evaluateReference(getSheet(), _sheetIndex, rowIndex, columnIndex, _tracker);
-	}
+    public ValueEval getEvalForCell(int rowIndex, int columnIndex) {
+        return _bookEvaluator.evaluateReference(getSheet(), _sheetIndex, rowIndex, columnIndex, _tracker);
+    }
 
-	private EvaluationSheet getSheet() {
-		if (_sheet == null) {
-			_sheet = _bookEvaluator.getSheet(_sheetIndex);
-		}
-		return _sheet;
-	}
+    private EvaluationSheet getSheet() {
+        if (_sheet == null) {
+            _sheet = _bookEvaluator.getSheet(_sheetIndex);
+        }
+        return _sheet;
+    }
 
     /**
      * @param rowIndex 

Modified: poi/trunk/poi/src/main/java/org/apache/poi/ss/formula/ThreeDEval.java
URL: http://svn.apache.org/viewvc/poi/trunk/poi/src/main/java/org/apache/poi/ss/formula/ThreeDEval.java?rev=1890120&r1=1890119&r2=1890120&view=diff
==============================================================================
--- poi/trunk/poi/src/main/java/org/apache/poi/ss/formula/ThreeDEval.java (original)
+++ poi/trunk/poi/src/main/java/org/apache/poi/ss/formula/ThreeDEval.java Sat May 22 20:56:44 2021
@@ -26,11 +26,11 @@ import org.apache.poi.ss.formula.eval.Va
  *  which allows for looking up 3D (sheet+row+column) evaluations
  */
 public interface ThreeDEval extends TwoDEval, SheetRange {
-	/**
-	 * @param sheetIndex sheet index (zero based)
-	 * @param rowIndex relative row index (zero based)
-	 * @param columnIndex relative column index (zero based)
-	 * @return element at the specified row and column position
-	 */
-	ValueEval getValue(int sheetIndex, int rowIndex, int columnIndex);
+    /**
+     * @param sheetIndex sheet index (zero based)
+     * @param rowIndex relative row index (zero based)
+     * @param columnIndex relative column index (zero based)
+     * @return element at the specified row and column position
+     */
+    ValueEval getValue(int sheetIndex, int rowIndex, int columnIndex);
 }

Modified: poi/trunk/poi/src/main/java/org/apache/poi/ss/formula/TwoDEval.java
URL: http://svn.apache.org/viewvc/poi/trunk/poi/src/main/java/org/apache/poi/ss/formula/TwoDEval.java?rev=1890120&r1=1890119&r2=1890120&view=diff
==============================================================================
--- poi/trunk/poi/src/main/java/org/apache/poi/ss/formula/TwoDEval.java (original)
+++ poi/trunk/poi/src/main/java/org/apache/poi/ss/formula/TwoDEval.java Sat May 22 20:56:44 2021
@@ -27,40 +27,40 @@ import org.apache.poi.ss.formula.functio
  */
 public interface TwoDEval extends ValueEval {
 
-	/**
-	 * @param rowIndex relative row index (zero based)
-	 * @param columnIndex relative column index (zero based)
-	 * @return element at the specified row and column position
-	 */
-	ValueEval getValue(int rowIndex, int columnIndex);
-
-	int getWidth();
-	int getHeight();
-
-	/**
-	 * @return {@code true} if the area has just a single row, this also includes
-	 * the trivial case when the area has just a single cell.
-	 */
-	default boolean isRow() {
-		return false;
-	}
-
-	/**
-	 * @return {@code true} if the area has just a single column, this also includes
-	 * the trivial case when the area has just a single cell.
-	 */
-	boolean isColumn();
-
-	/**
-	 * @param rowIndex relative row index (zero based)
-	 * @return a single row TwoDEval
-	 */
-	TwoDEval getRow(int rowIndex);
-	/**
-	 * @param columnIndex relative column index (zero based)
-	 * @return a single column TwoDEval
-	 */
-	TwoDEval getColumn(int columnIndex);
+    /**
+     * @param rowIndex relative row index (zero based)
+     * @param columnIndex relative column index (zero based)
+     * @return element at the specified row and column position
+     */
+    ValueEval getValue(int rowIndex, int columnIndex);
+
+    int getWidth();
+    int getHeight();
+
+    /**
+     * @return {@code true} if the area has just a single row, this also includes
+     * the trivial case when the area has just a single cell.
+     */
+    default boolean isRow() {
+        return false;
+    }
+
+    /**
+     * @return {@code true} if the area has just a single column, this also includes
+     * the trivial case when the area has just a single cell.
+     */
+    boolean isColumn();
+
+    /**
+     * @param rowIndex relative row index (zero based)
+     * @return a single row TwoDEval
+     */
+    TwoDEval getRow(int rowIndex);
+    /**
+     * @param columnIndex relative column index (zero based)
+     * @return a single column TwoDEval
+     */
+    TwoDEval getColumn(int columnIndex);
 
 
     /**

Modified: poi/trunk/poi/src/main/java/org/apache/poi/ss/formula/UserDefinedFunction.java
URL: http://svn.apache.org/viewvc/poi/trunk/poi/src/main/java/org/apache/poi/ss/formula/UserDefinedFunction.java?rev=1890120&r1=1890119&r2=1890120&view=diff
==============================================================================
--- poi/trunk/poi/src/main/java/org/apache/poi/ss/formula/UserDefinedFunction.java (original)
+++ poi/trunk/poi/src/main/java/org/apache/poi/ss/formula/UserDefinedFunction.java Sat May 22 20:56:44 2021
@@ -28,34 +28,34 @@ import org.apache.poi.ss.formula.functio
  */
 final class UserDefinedFunction implements FreeRefFunction {
 
-	public static final FreeRefFunction instance = new UserDefinedFunction();
+    public static final FreeRefFunction instance = new UserDefinedFunction();
 
-	private UserDefinedFunction() {
-		// enforce singleton
-	}
+    private UserDefinedFunction() {
+        // enforce singleton
+    }
 
-	@Override
-	public ValueEval evaluate(ValueEval[] args, OperationEvaluationContext ec) {
-		int nIncomingArgs = args.length;
-		if(nIncomingArgs < 1) {
-			throw new RuntimeException("function name argument missing");
-		}
+    @Override
+    public ValueEval evaluate(ValueEval[] args, OperationEvaluationContext ec) {
+        int nIncomingArgs = args.length;
+        if(nIncomingArgs < 1) {
+            throw new RuntimeException("function name argument missing");
+        }
 
-		ValueEval nameArg = args[0];
-		String functionName;
-		if (nameArg instanceof FunctionNameEval) {
-			functionName = ((FunctionNameEval) nameArg).getFunctionName();
-		} else {
-			throw new RuntimeException("First argument should be a NameEval, but got ("
-					+ nameArg.getClass().getName() + ")");
-		}
-		FreeRefFunction targetFunc = ec.findUserDefinedFunction(functionName);
-		if (targetFunc == null) {
-			throw new NotImplementedFunctionException(functionName);
-		}
-		int nOutGoingArgs = nIncomingArgs -1;
-		ValueEval[] outGoingArgs = new ValueEval[nOutGoingArgs];
-		System.arraycopy(args, 1, outGoingArgs, 0, nOutGoingArgs);
-		return targetFunc.evaluate(outGoingArgs, ec);
-	}
+        ValueEval nameArg = args[0];
+        String functionName;
+        if (nameArg instanceof FunctionNameEval) {
+            functionName = ((FunctionNameEval) nameArg).getFunctionName();
+        } else {
+            throw new RuntimeException("First argument should be a NameEval, but got ("
+                    + nameArg.getClass().getName() + ")");
+        }
+        FreeRefFunction targetFunc = ec.findUserDefinedFunction(functionName);
+        if (targetFunc == null) {
+            throw new NotImplementedFunctionException(functionName);
+        }
+        int nOutGoingArgs = nIncomingArgs -1;
+        ValueEval[] outGoingArgs = new ValueEval[nOutGoingArgs];
+        System.arraycopy(args, 1, outGoingArgs, 0, nOutGoingArgs);
+        return targetFunc.evaluate(outGoingArgs, ec);
+    }
 }

Modified: poi/trunk/poi/src/main/java/org/apache/poi/ss/formula/WorkbookDependentFormula.java
URL: http://svn.apache.org/viewvc/poi/trunk/poi/src/main/java/org/apache/poi/ss/formula/WorkbookDependentFormula.java?rev=1890120&r1=1890119&r2=1890120&view=diff
==============================================================================
--- poi/trunk/poi/src/main/java/org/apache/poi/ss/formula/WorkbookDependentFormula.java (original)
+++ poi/trunk/poi/src/main/java/org/apache/poi/ss/formula/WorkbookDependentFormula.java Sat May 22 20:56:44 2021
@@ -24,5 +24,5 @@ import org.apache.poi.util.Internal;
  */
 @Internal
 public interface WorkbookDependentFormula {
-	String toFormulaString(FormulaRenderingWorkbook book);
+    String toFormulaString(FormulaRenderingWorkbook book);
 }

Modified: poi/trunk/poi/src/main/java/org/apache/poi/ss/formula/atp/IfError.java
URL: http://svn.apache.org/viewvc/poi/trunk/poi/src/main/java/org/apache/poi/ss/formula/atp/IfError.java?rev=1890120&r1=1890119&r2=1890120&view=diff
==============================================================================
--- poi/trunk/poi/src/main/java/org/apache/poi/ss/formula/atp/IfError.java (original)
+++ poi/trunk/poi/src/main/java/org/apache/poi/ss/formula/atp/IfError.java Sat May 22 20:56:44 2021
@@ -33,33 +33,33 @@ import org.apache.poi.ss.formula.functio
  */
 final class IfError implements FreeRefFunction {
 
-	public static final FreeRefFunction instance = new IfError();
+    public static final FreeRefFunction instance = new IfError();
 
-	private IfError() {
-		// enforce singleton
-	}
-
-	public ValueEval evaluate(ValueEval[] args, OperationEvaluationContext ec) {
-		if (args.length != 2) {
-			return ErrorEval.VALUE_INVALID;
-		}
-
-		ValueEval val;
-		try {
-			val = evaluateInternal(args[0], args[1], ec.getRowIndex(), ec.getColumnIndex());
-		} catch (EvaluationException e) {
-			return e.getErrorEval();
-		}
-
-		return val;
-	}
-
-	private static ValueEval evaluateInternal(ValueEval arg, ValueEval iferror, int srcCellRow, int srcCellCol) throws EvaluationException {
-		arg = WorkbookEvaluator.dereferenceResult(arg, srcCellRow, srcCellCol);
-		if(arg instanceof ErrorEval) {
-			return iferror;
-		} else {
-			return arg;
-		}
-	}
+    private IfError() {
+        // enforce singleton
+    }
+
+    public ValueEval evaluate(ValueEval[] args, OperationEvaluationContext ec) {
+        if (args.length != 2) {
+            return ErrorEval.VALUE_INVALID;
+        }
+
+        ValueEval val;
+        try {
+            val = evaluateInternal(args[0], args[1], ec.getRowIndex(), ec.getColumnIndex());
+        } catch (EvaluationException e) {
+            return e.getErrorEval();
+        }
+
+        return val;
+    }
+
+    private static ValueEval evaluateInternal(ValueEval arg, ValueEval iferror, int srcCellRow, int srcCellCol) throws EvaluationException {
+        arg = WorkbookEvaluator.dereferenceResult(arg, srcCellRow, srcCellCol);
+        if(arg instanceof ErrorEval) {
+            return iferror;
+        } else {
+            return arg;
+        }
+    }
 }

Modified: poi/trunk/poi/src/main/java/org/apache/poi/ss/formula/atp/MRound.java
URL: http://svn.apache.org/viewvc/poi/trunk/poi/src/main/java/org/apache/poi/ss/formula/atp/MRound.java?rev=1890120&r1=1890119&r2=1890120&view=diff
==============================================================================
--- poi/trunk/poi/src/main/java/org/apache/poi/ss/formula/atp/MRound.java (original)
+++ poi/trunk/poi/src/main/java/org/apache/poi/ss/formula/atp/MRound.java Sat May 22 20:56:44 2021
@@ -32,13 +32,13 @@ import org.apache.poi.ss.formula.functio
  */
 final class MRound implements FreeRefFunction {
 
-	public static final FreeRefFunction instance = new MRound();
+    public static final FreeRefFunction instance = new MRound();
 
-	private MRound() {
-		// enforce singleton
-	}
+    private MRound() {
+        // enforce singleton
+    }
 
-	public ValueEval evaluate(ValueEval[] args, OperationEvaluationContext ec) {
+    public ValueEval evaluate(ValueEval[] args, OperationEvaluationContext ec) {
         double number, multiple, result;
 
         if (args.length != 2) {
@@ -63,5 +63,5 @@ final class MRound implements FreeRefFun
         } catch (EvaluationException e) {
             return e.getErrorEval();
         }
-	}
+    }
 }



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