From d86802695995268de2d3ea59de7c27f3997142a9 Mon Sep 17 00:00:00 2001 From: Dominik Stadler Date: Tue, 23 May 2017 21:13:02 +0000 Subject: [PATCH] Bug 61063: Add a RefListEval to handle UnionPtg and implement it for Rank(), may be missing for other functions where ArrayEval is currently handled git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1795963 13f79535-47bb-0310-9956-ffa450edef68 --- .../poi/ss/formula/WorkbookEvaluator.java | 31 +++---- .../poi/ss/formula/eval/RefListEval.java | 46 ++++++++++ .../ss/formula/functions/LogicalFunction.java | 13 +-- .../apache/poi/ss/formula/functions/Rank.java | 86 +++++++++++------- .../poi/xssf/usermodel/TestXSSFBugs.java | 19 ++++ .../poi/ss/formula/TestWorkbookEvaluator.java | 20 ++-- .../poi/ss/formula/functions/TestTFunc.java | 13 +-- test-data/spreadsheet/61063.xlsx | Bin 0 -> 9151 bytes 8 files changed, 140 insertions(+), 88 deletions(-) create mode 100644 src/java/org/apache/poi/ss/formula/eval/RefListEval.java create mode 100644 test-data/spreadsheet/61063.xlsx diff --git a/src/java/org/apache/poi/ss/formula/WorkbookEvaluator.java b/src/java/org/apache/poi/ss/formula/WorkbookEvaluator.java index cda98828e..89b73d025 100644 --- a/src/java/org/apache/poi/ss/formula/WorkbookEvaluator.java +++ b/src/java/org/apache/poi/ss/formula/WorkbookEvaluator.java @@ -28,19 +28,7 @@ import java.util.TreeSet; import org.apache.poi.ss.SpreadsheetVersion; import org.apache.poi.ss.formula.CollaboratingWorkbooksEnvironment.WorkbookNotFoundException; import org.apache.poi.ss.formula.atp.AnalysisToolPak; -import org.apache.poi.ss.formula.eval.BlankEval; -import org.apache.poi.ss.formula.eval.BoolEval; -import org.apache.poi.ss.formula.eval.ErrorEval; -import org.apache.poi.ss.formula.eval.EvaluationException; -import org.apache.poi.ss.formula.eval.ExternalNameEval; -import org.apache.poi.ss.formula.eval.FunctionEval; -import org.apache.poi.ss.formula.eval.FunctionNameEval; -import org.apache.poi.ss.formula.eval.MissingArgEval; -import org.apache.poi.ss.formula.eval.NotImplementedException; -import org.apache.poi.ss.formula.eval.NumberEval; -import org.apache.poi.ss.formula.eval.OperandResolver; -import org.apache.poi.ss.formula.eval.StringEval; -import org.apache.poi.ss.formula.eval.ValueEval; +import org.apache.poi.ss.formula.eval.*; import org.apache.poi.ss.formula.function.FunctionMetadataRegistry; import org.apache.poi.ss.formula.functions.Choose; import org.apache.poi.ss.formula.functions.FreeRefFunction; @@ -84,12 +72,12 @@ public final class WorkbookEvaluator { private final IStabilityClassifier _stabilityClassifier; private final AggregatingUDFFinder _udfFinder; - private boolean _ignoreMissingWorkbooks = false; + private boolean _ignoreMissingWorkbooks; /** * whether print detailed messages about the next formula evaluation */ - private boolean dbgEvaluationOutputForNextEval = false; + private boolean dbgEvaluationOutputForNextEval; // special logger for formula evaluation output (because of possibly very large output) private final POILogger EVAL_LOG = POILogFactory.getLogger("POI.FormulaEval"); @@ -415,11 +403,10 @@ public final class WorkbookEvaluator { Stack stack = new Stack(); for (int i = 0, iSize = ptgs.length; i < iSize; i++) { - // since we don't know how to handle these yet :( Ptg ptg = ptgs[i]; if (dbgEvaluationOutputIndent > 0) { - EVAL_LOG.log(POILogger.INFO, dbgIndentStr + " * ptg " + i + ": " + ptg); + EVAL_LOG.log(POILogger.INFO, dbgIndentStr + " * ptg " + i + ": " + ptg + ", stack: " + stack); } if (ptg instanceof AttrPtg) { AttrPtg attrPtg = (AttrPtg) ptg; @@ -504,13 +491,17 @@ public final class WorkbookEvaluator { continue; } + if (ptg instanceof UnionPtg) { + ValueEval v2 = stack.pop(); + ValueEval v1 = stack.pop(); + stack.push(new RefListEval(v1, v2)); + continue; + } + ValueEval opResult; if (ptg instanceof OperationPtg) { OperationPtg optg = (OperationPtg) ptg; - if (optg instanceof UnionPtg) { continue; } - - int numops = optg.getNumberOfOperands(); ValueEval[] ops = new ValueEval[numops]; diff --git a/src/java/org/apache/poi/ss/formula/eval/RefListEval.java b/src/java/org/apache/poi/ss/formula/eval/RefListEval.java new file mode 100644 index 000000000..ee884da78 --- /dev/null +++ b/src/java/org/apache/poi/ss/formula/eval/RefListEval.java @@ -0,0 +1,46 @@ +/* ==================================================================== + 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.eval; + +import java.util.ArrayList; +import java.util.List; + +/** + * Handling of a list of values, e.g. the 2nd argument in RANK(A1,(B1,B2,B3),1) + */ +public class RefListEval implements ValueEval { + private final List list = new ArrayList(); + + public RefListEval(ValueEval v1, ValueEval v2) { + add(v1); + add(v2); + } + + private void add(ValueEval v) { + // flatten multiple nested RefListEval + if(v instanceof RefListEval) { + list.addAll(((RefListEval)v).list); + } else { + list.add(v); + } + } + + public List getList() { + return list; + } +} diff --git a/src/java/org/apache/poi/ss/formula/functions/LogicalFunction.java b/src/java/org/apache/poi/ss/formula/functions/LogicalFunction.java index a0825774b..1bf9cf5ca 100644 --- a/src/java/org/apache/poi/ss/formula/functions/LogicalFunction.java +++ b/src/java/org/apache/poi/ss/formula/functions/LogicalFunction.java @@ -17,16 +17,7 @@ package org.apache.poi.ss.formula.functions; -import org.apache.poi.ss.formula.eval.AreaEval; -import org.apache.poi.ss.formula.eval.BlankEval; -import org.apache.poi.ss.formula.eval.BoolEval; -import org.apache.poi.ss.formula.eval.ErrorEval; -import org.apache.poi.ss.formula.eval.EvaluationException; -import org.apache.poi.ss.formula.eval.NumberEval; -import org.apache.poi.ss.formula.eval.OperandResolver; -import org.apache.poi.ss.formula.eval.RefEval; -import org.apache.poi.ss.formula.eval.StringEval; -import org.apache.poi.ss.formula.eval.ValueEval; +import org.apache.poi.ss.formula.eval.*; /** * Implementation of the various ISxxx Logical Functions, which @@ -131,7 +122,7 @@ public abstract class LogicalFunction extends Fixed1ArgFunction { public static final Function ISREF = new Fixed1ArgFunction() { public ValueEval evaluate(int srcRowIndex, int srcColumnIndex, ValueEval arg0) { - if (arg0 instanceof RefEval || arg0 instanceof AreaEval) { + if (arg0 instanceof RefEval || arg0 instanceof AreaEval || arg0 instanceof RefListEval) { return BoolEval.TRUE; } return BoolEval.FALSE; diff --git a/src/java/org/apache/poi/ss/formula/functions/Rank.java b/src/java/org/apache/poi/ss/formula/functions/Rank.java index c6ff86068..c5e07506b 100644 --- a/src/java/org/apache/poi/ss/formula/functions/Rank.java +++ b/src/java/org/apache/poi/ss/formula/functions/Rank.java @@ -19,13 +19,7 @@ package org.apache.poi.ss.formula.functions; -import org.apache.poi.ss.formula.eval.AreaEval; -import org.apache.poi.ss.formula.eval.ErrorEval; -import org.apache.poi.ss.formula.eval.EvaluationException; -import org.apache.poi.ss.formula.eval.NumberEval; -import org.apache.poi.ss.formula.eval.OperandResolver; -import org.apache.poi.ss.formula.eval.RefEval; -import org.apache.poi.ss.formula.eval.ValueEval; +import org.apache.poi.ss.formula.eval.*; /** @@ -45,51 +39,56 @@ import org.apache.poi.ss.formula.eval.ValueEval; public class Rank extends Var2or3ArgFunction { public ValueEval evaluate(int srcRowIndex, int srcColumnIndex, ValueEval arg0, ValueEval arg1) { - - AreaEval aeRange; - double result; try { ValueEval ve = OperandResolver.getSingleValue(arg0, srcRowIndex, srcColumnIndex); - result = OperandResolver.coerceValueToDouble(ve); + double result = OperandResolver.coerceValueToDouble(ve); if (Double.isNaN(result) || Double.isInfinite(result)) { throw new EvaluationException(ErrorEval.NUM_ERROR); } - aeRange = convertRangeArg(arg1); + + if(arg1 instanceof RefListEval) { + return eval(result, ((RefListEval)arg1), true); + } + + final AreaEval aeRange = convertRangeArg(arg1); + + return eval(result, aeRange, true); } catch (EvaluationException e) { return e.getErrorEval(); } - return eval(srcRowIndex, srcColumnIndex, result, aeRange, true); } public ValueEval evaluate(int srcRowIndex, int srcColumnIndex, ValueEval arg0, ValueEval arg1, ValueEval arg2) { - - AreaEval aeRange; - double result; - boolean order=false; try { ValueEval ve = OperandResolver.getSingleValue(arg0, srcRowIndex, srcColumnIndex); - result = OperandResolver.coerceValueToDouble(ve); + final double result = OperandResolver.coerceValueToDouble(ve); if (Double.isNaN(result) || Double.isInfinite(result)) { throw new EvaluationException(ErrorEval.NUM_ERROR); } - aeRange = convertRangeArg(arg1); - - ve = OperandResolver.getSingleValue(arg2, srcRowIndex, srcColumnIndex); - int order_value = OperandResolver.coerceValueToInt(ve); - if(order_value==0){ - order=true; - }else if(order_value==1){ - order=false; - }else throw new EvaluationException(ErrorEval.NUM_ERROR); - + + ve = OperandResolver.getSingleValue(arg2, srcRowIndex, srcColumnIndex); + int order_value = OperandResolver.coerceValueToInt(ve); + final boolean order; + if(order_value==0) { + order = true; + } else if(order_value==1) { + order = false; + } else { + throw new EvaluationException(ErrorEval.NUM_ERROR); + } + + if(arg1 instanceof RefListEval) { + return eval(result, ((RefListEval)arg1), order); + } + + final AreaEval aeRange = convertRangeArg(arg1); + return eval(result, aeRange, order); } catch (EvaluationException e) { return e.getErrorEval(); } - return eval(srcRowIndex, srcColumnIndex, result, aeRange, order); } - private static ValueEval eval(int srcRowIndex, int srcColumnIndex, double arg0, AreaEval aeRange, boolean descending_order) { - + private static ValueEval eval(double arg0, AreaEval aeRange, boolean descending_order) { int rank = 1; int height=aeRange.getHeight(); int width= aeRange.getWidth(); @@ -105,9 +104,30 @@ public class Rank extends Var2or3ArgFunction { } return new NumberEval(rank); } - - private static Double getValue(AreaEval aeRange, int relRowIndex, int relColIndex) { + private static ValueEval eval(double arg0, RefListEval aeRange, boolean descending_order) { + int rank = 1; + for(ValueEval ve : aeRange.getList()) { + if (ve instanceof RefEval) { + ve = ((RefEval) ve).getInnerValueEval(((RefEval) ve).getFirstSheetIndex()); + } + + final Double value; + if (ve instanceof NumberEval) { + value = ((NumberEval)ve).getNumberValue(); + } else { + continue; + } + + if(descending_order && value>arg0 || !descending_order && value`GmMv(^Ta_Ev85G01~PNf|{fkC>vyHlh=NhJj7E|L63-{*T( z9^bXTpWj;Vv)4Lj)~s{xYtEj1UHiWFy^oqA0wN&*8Gs4^0B8Yyp08uw;Q#;{WB`Bw zfC{fK1-5fGw{teq@US;`GURZ#wV}yGglA3%z{BqUr~McI1B3Ak3SC@;y?aOt87o?| zHU!e3PaL^VHru}exE~k}j5Dl&oV>hLtHj>slXnHq^Ho1syYQ7y2H$@&C8QhB_bxf* zq>4(hU9O0QcVy;b`$5Z7G-Q+b(Mj=P0e>6e##XXn0c@@eyPyZGQua70S!@|ZiMlAE z7U)~nc`EiGCc(@GCs|T;-=ZdeF!dtXk4=Uu%(stb=sa^jgd)hV)8Rsv zBH-F@@!;D9{R^I_*nln>1lQ)-lJy5`KzB88hdR(1d*AQ^GmKDAMWK<065J{AHD2t~ zazP#Ty)j_}?DCl6+=ng5Hq{xol6oS#m%==+05f1YZ9Iof$$Zp2)R<#w%S;)Cm&Gv$LH5 zPw^8;4}IE{cxHCmnqK*2X7!T6=|bS8vTSA&_1Z5EzO2HLcvvaS=mo(eGfWdtX*X%x zMYDw**CxvmvFW0OZzwDMF6Y?s%G9%SX zHNd%2oG)Is)s_^>ELCYzQ_S1Sz?ZSCO;hV+s0ln1Ip~ z?;5^d#nU?L*$b|b`wcO))Kl)%-lw#LVt=Go_7S<$>UekD~T&``CL zi_k~#K#b4lwOtUq!n9l@nFqHhjqvM}XnyCNaaZi?mO6SRCBB1#ugk~VDODV=`ea_J zmaCAxnQ+H{4ByBnx#^=KHp%JsHU9oc&Eh-Dw;N~_hD3FO5%bes!-)tI@H>urUJJvd zPUcuxZUhs7y~^e`T_UxvBL>z17}^MDG8GKrJd^eX8%ZX1H(e%$3a|D-?@HlzMi__) zzcMd;sC22;(ExU=Za?3z^;yQ+6lGB$bi3A-!=cN&SDPC~jWAae^G2iICs9WIUHI5a z(_MI$`Fm|LUc|10tVws}iDGHPqk`vsjL_L1*yRxA;;2QQ^10%nuZMlmLN{6#Ra|yB zMRzpvqLA5O!#ds$_mjhI<4uvqIi2)IQILofV_j*I#L<~+=vgl9uH>6TNK>Cx)bDU+pV`0WMhy1&F{V^2ImgctR zoIlRo-!;Cis|qH6MA$}uKq=z|He+%nlXWSat{}GrW=^wjIy6)5sfaXHC6h9pVAaSn zFi1-n#MV2Ai|1&428t@DqlUjPYlkL%I#F>|h6upJozTv_`8ITMGvA!l;=3%-J&K11 zIjrCLP&O$nZJHKiMN!@IuTdN#E6zVMAfs5f5qk^EklfqO)BCsv9@ST4*d2**YiNo|+ z|7>St>$jY(airyq!liqGkB6)0!PTWZE5}&=1w5rwTP|KFDXDPA3n-c;Y>$$ou4P){ z#@PI~&5~T}CX4wEyaTTF+7L!`A&;}5oG)x2dmPjZ&JLlfH)osl#}^Z2MxxivWSkLaMy)qz zYiXiI$dc&n44wx)aYkt(9tU5T+I%kR;al1F^HHhCpUXKzDYB%f0_=LZN>@M(9JQt- zrklm>w4sWjW6RTt64L2849C5zsykra!fYsFoUa^UQ& zo*-f1JateTG8hc6mx_!jba!*uuCEnTOt-U;*qCl5$Cn*M2sH~qz$XYfrbxpFW_%J+ zplsSzH?r?%LjRPNA7qsyAL_h12;623~LMLndpSL+oP&Lm9ExVuK4uZG(sujHIBp=)st%M}Zvt_6z-C8J zfza8D(!z!PeM7^CD9c}4IysK! zq3-u07DHb9&u%z_9Jn9oMn=y)DQcKKj*W?{6%4525tc!7H{7(tuL9UXz33LcBFh`{2+{{#U{9zKEDQXh8AjQUwz#1D? z+@yWHMi`%XV5e`kq{Lge_`3u?PjQR#yk6h!2TC$#qJMtZ9WFvA8;s?GWiaK{6efx4 zqOLs7V{D(dJmRCuYe~4yn3o7vxjXRn2^7K4ZOtr>G~RKm+{%*YVvTNfr^8gcW^3rs zW{;gElZnVyi+%vzSZf+x&uX?U7nS(qGus{(tLE9kUTYV~S$jU4l!M@yyhJ`!eYLrDX}{XGMGuFp*RCS7d`3OcIt)CofwXXJbD_j)t2`i^|gx>#JNJhS66 zQ%E)rjM0Rb!V%%}0Q7?&B^K6t)o zu}Fxj>d|*cU)JsR+%a#%*vv*R43{7f0pGJH%_+Ijwwj?7Kc)g4GxrSDN|G+`zN_!S z(fuN*>RvZWH;9|i5%L8n&ZRRmN_cqk<@!=DTv|wD*2G_uil!fWGSl{fYFVHLz>tUw zq}9)~E70I!cYnjPO=F+iyUy=GLY~lw+DOLXk;id(so-H7M1eMNMvOdJ!(T2@b%bjf zEN!1$fAfBpgVJjL5^2rl<8GrKNWTV@-V0S_e?Iv+ zn)N%9yj~eqe$Mxj+*U1xY^>|H#CV&M^D5bTWyntRY}*qtE$b~Dcy&IXD_B7DuM6X5 zFFLl?m=)eEVt;BX$^VjZsKlCoGCbz@QOw^~@ACv{(Z(b8qAp~Ji6Ci;;b80BwB`7v zN*=lUctr&*8p??p|7*JFMB!x-lc|L_Mk9IOKmutTy)Jhjy^vn_ldrub_s>gx`WpED zxihAx%fgNI)gKMV|6#m%{~53UKjZb`(S1A|n6`khW#E6%*AJH%A8!bTauJ5^G3|)a z7}Ws717dR}#P8s17Ue3de|51PgexW)v|+NkzQQgCU8&{77cH9|HLs;i;Vy^@jL!rJ zNYU93WKg=i#bDw56;iuh7g`|iNBcGUvGVuVA0UsN_`c3K<}l~+$k7nZLVQu z*gaM;Iwiun9!PgD3L}8>*vySD9!>ZBF%f=pZe-~W5y1tkufgnYg&J>b&@Oa0LYa8n7XknbQ0)9MAcK5)P zP_4408t2&!nzW|=;IKWz6)5v_Idaz+K3fl#MV`#$U?tz=v%T~dow^MjKix}a_pKqm z)vKAD7|NaTB;pCm$^M(1D(;=MO2Qc?xZqUwSR_uQ)QsFc31yV{y(`XBQdVyiYf`!* zCF)m3Vn%FN{f|~vD~%z_oJ`l|N&~)(9TBUtQhoAn#etkk+L`>(!H(q!oc>f-hX#=) zo1Ss2f=8oAPmk=mMW?#^6KV=Q%&L09#G@o5J!A0-Gs~H6z`QnzbK*Zu(xZ{(7~ZhS zIRJJL{%ewUvNUlt2dO(dTG>5y`Yw3P0SWX@E?n`kmPN|WZQD!?JgeC^Orr>xL(_;5 z`L~#vb%FyU%OhKLprZ_qg37R?m3CY(Kf}C8b85^4RwT1yyFrrhrJS*0beOL6kh`w1 z^as~Ety(!G^TH_iw@s&kLX~@3dQ6!3FRDRa4!h4V7?GPUVTt4KDJ36s;d)ry#)7O$rEbY8K3{g(( z)e_k80o_6|nUE7aa+b3WdulCU|D(Ak`n~mTiq8yTUk4E-er?u$7jB1d#ZK^ZR-ddIJ-%@%fvD&c`;$ zT2Xc=@>6<|@)EQ({N+90e(-m6Wb7UmoQgS&_ThN>48-`!>7}uyI%j83uq1NT+6lo( zTh7Tgj@ej4^BTed<4#j{Ss%UM0@}QbYCqxVLj*)hG7bvuuWwZV=|pRfgGUzvj#@sI zJqbo(N7ovcslRgJ?#Q}|y3_n}jDPc?nAH?5zVIyPb!jfnHKwa8ClMK@%zKj5#Rb)^ zvVk3SBBTiW7E}wPX^;g-Rqqtz$qOf?)irZ(N5kZ>Rr?Y8axL%#+?r5>j z1;z9~`1;z3qn{W>7iqz`IA1dW!Yl*FtmTy9isbo;t&0@BI4gXjP)jYHh)pkkO8db? z@CW2n<&uv3a02@b=4ca0vk&vodSC5G0LeYV4nNllK!p7G9v0ub$bQ&vMX_Ll7VWsn zyz8Gq^ z$)LF}l8a-Oj+oATT0A$3LM2_AO#R=b@OEOVX+s_F5`K+P+ho*HuOk-dBdD1JJu1<^ z$N?kmhn@Dr?3K879Gl01bn(9T4HHe_Efjd&A# z)W`NQ+%Q-$K;xW6X;T5_(Ic($-w^&Z>5XNEB&Wj&s13V{`zrxW?Ct;IAB=c^wUoFH zyE!h*&^?4x?9kn)lx&hvajnPII-^I9 zNWGUx^p2_O`_gPBix{kI(t*?jp>E{o6ZLT!r6nB~#;N4T48$JS5}yM`wRH?@I73aH z4P1`3CRh?vIYkosUIo;!AOx`51_@~&rsU7nx=aENHcqsyP31CjwxHYswa3V}&H%y$I@Sz?~t&KTQl-r_tN* zuJ@Ts#H_F;c;rJ~XL--|Q(2lcNy19$W75uzKr5TF6GzL%#mWjn5-Xkh6*l$W1#xsCH*lNHa>q(N_>xVDVX z-}OV|;BiKX=op=K1S)u^Z-baI9KcjL7E?l#d5|0k^e%2q%6mHs92Pe?y`2m);XORl z8~<3}f?};3yo-Nl1~_Px`CSF?*PAVc>Wq)0aN=g+n=`sf8PW8 zHE;@6GycyzLbq{l7yN%B@u2;!{C_+6c8U6Da0~h$3)Z&*ZZA210{G(n``7it=L_dmH8Ua_Z-^%)^ZFA5eZQu5P2;_R~L6=wbdFhVnOeeH-Dn$Nh;g z_7}px9q;YX->u5eKmgzo836D%<8nLv_eAq+crV2-;eVx_+tIfL{WCs=`bW=yled~8 T5{#$cT|5>*;XBo7zJL24A!>Y4 literal 0 HcmV?d00001