123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130 |
- /* ====================================================================
- Licensed to the Apache Software Foundation (ASF) under one or more
- contributor license agreements. See the NOTICE file distributed with
- this work for additional information regarding copyright ownership.
- The ASF licenses this file to You under the Apache License, Version 2.0
- (the "License"); you may not use this file except in compliance with
- the License. You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
- ==================================================================== */
-
- package org.apache.poi.ss.formula;
-
- import java.util.Stack;
-
- import org.apache.poi.ss.formula.ptg.AttrPtg;
- 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.OperationPtg;
- import org.apache.poi.ss.formula.ptg.ParenthesisPtg;
- import org.apache.poi.ss.formula.ptg.Ptg;
-
- /**
- * Common logic for rendering formulas.<br>
- *
- * For POI internal use only
- *
- * @author Josh Micich
- */
- public class FormulaRenderer {
-
- /**
- * Static method to convert an array of {@link Ptg}s in RPN order
- * to a human readable string format in infix mode.
- * @param book used for defined names and 3D references
- * @param ptgs must not be <code>null</code>
- * @return a human readable String
- */
- public static String toFormulaString(FormulaRenderingWorkbook book, Ptg[] ptgs) {
- if (ptgs == null || ptgs.length == 0) {
- throw new IllegalArgumentException("ptgs must not be null");
- }
- Stack<String> stack = new Stack<>();
-
- for (Ptg ptg : ptgs) {
- // TODO - what about MemNoMemPtg?
- if(ptg instanceof MemAreaPtg || ptg instanceof MemFuncPtg || ptg instanceof MemErrPtg) {
- // marks the start of a list of area expressions which will be naturally combined
- // by their trailing operators (e.g. UnionPtg)
- // TODO - put comment and throw exception in toFormulaString() of these classes
- continue;
- }
- if (ptg instanceof ParenthesisPtg) {
- String contents = stack.pop();
- stack.push ("(" + contents + ")");
- continue;
- }
- if (ptg instanceof AttrPtg) {
- AttrPtg attrPtg = ((AttrPtg) ptg);
- if (attrPtg.isOptimizedIf() || attrPtg.isOptimizedChoose() || attrPtg.isSkip()) {
- continue;
- }
- if (attrPtg.isSpace()) {
- // POI currently doesn't render spaces in formulas
- continue;
- // but if it ever did, care must be taken:
- // tAttrSpace comes *before* the operand it applies to, which may be consistent
- // with how the formula text appears but is against the RPN ordering assumed here
- }
- if (attrPtg.isSemiVolatile()) {
- // similar to tAttrSpace - RPN is violated
- continue;
- }
- if (attrPtg.isSum()) {
- String[] operands = getOperands(stack, attrPtg.getNumberOfOperands());
- stack.push(attrPtg.toFormulaString(operands));
- continue;
- }
- throw new RuntimeException("Unexpected tAttr: " + attrPtg);
- }
-
- if (ptg instanceof WorkbookDependentFormula) {
- WorkbookDependentFormula optg = (WorkbookDependentFormula) ptg;
- stack.push(optg.toFormulaString(book));
- continue;
- }
- if (! (ptg instanceof OperationPtg)) {
- stack.push(ptg.toFormulaString());
- continue;
- }
-
- OperationPtg o = (OperationPtg) ptg;
- String[] operands = getOperands(stack, o.getNumberOfOperands());
- stack.push(o.toFormulaString(operands));
- }
- if(stack.isEmpty()) {
- // inspection of the code above reveals that every stack.pop() is followed by a
- // stack.push(). So this is either an internal error or impossible.
- throw new IllegalStateException("Stack underflow");
- }
- String result = stack.pop();
- if(!stack.isEmpty()) {
- // Might be caused by some tokens like AttrPtg and Mem*Ptg, which really shouldn't
- // put anything on the stack
- throw new IllegalStateException("too much stuff left on the stack");
- }
- return result;
- }
-
- private static String[] getOperands(Stack<String> stack, int nOperands) {
- String[] operands = new String[nOperands];
-
- for (int j = nOperands-1; j >= 0; j--) { // reverse iteration because args were pushed in-order
- if(stack.isEmpty()) {
- String msg = "Too few arguments supplied to operation. Expected (" + nOperands
- + ") operands but got (" + (nOperands - j - 1) + ")";
- throw new IllegalStateException(msg);
- }
- operands[j] = stack.pop();
- }
- return operands;
- }
- }
|