82aa0c6f09
git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1038688 13f79535-47bb-0310-9956-ffa450edef68
212 lines
6.4 KiB
Java
212 lines
6.4 KiB
Java
/* ====================================================================
|
|
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 org.apache.poi.ss.formula.ptg.ArrayPtg;
|
|
import org.apache.poi.ss.formula.ptg.AttrPtg;
|
|
import org.apache.poi.ss.formula.ptg.FuncVarPtg;
|
|
import org.apache.poi.ss.formula.ptg.MemAreaPtg;
|
|
import org.apache.poi.ss.formula.ptg.MemFuncPtg;
|
|
import org.apache.poi.ss.formula.ptg.Ptg;
|
|
import org.apache.poi.ss.formula.function.FunctionMetadataRegistry;
|
|
/**
|
|
* Represents a syntactic element from a formula by encapsulating the corresponding <tt>Ptg</tt>
|
|
* token. Each <tt>ParseNode</tt> may have child <tt>ParseNode</tt>s in the case when the wrapped
|
|
* <tt>Ptg</tt> is non-atomic.
|
|
*
|
|
* @author Josh Micich
|
|
*/
|
|
final class ParseNode {
|
|
|
|
public static final ParseNode[] EMPTY_ARRAY = { };
|
|
private final Ptg _token;
|
|
private final ParseNode[] _children;
|
|
private 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;
|
|
_isIf = isIf(token);
|
|
int tokenCount = 1;
|
|
for (int i = 0; i < children.length; i++) {
|
|
tokenCount += children[i].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 (int i = 0; i < _children.length; i++) {
|
|
result += _children[i].getEncodedSize();
|
|
}
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* Collects the array of <tt>Ptg</tt> 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;
|
|
if (FunctionMetadataRegistry.FUNCTION_NAME_IF.equals(func.getName())) {
|
|
return true;
|
|
}
|
|
}
|
|
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;
|
|
}
|
|
}
|
|
}
|