From af3829250516d91c1c0a57c73ea69c7e12154a09 Mon Sep 17 00:00:00 2001 From: Josh Micich Date: Sat, 13 Sep 2008 05:14:26 +0000 Subject: [PATCH] Refactored TextFunctions. Some minor fixes - test cases added. git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@694877 13f79535-47bb-0310-9956-ffa450edef68 --- .../hssf/record/formula/eval/ConcatEval.java | 2 +- .../record/formula/eval/FunctionEval.java | 18 +- .../record/formula/eval/OperandResolver.java | 11 +- .../record/formula/functions/Concatenate.java | 60 ---- .../hssf/record/formula/functions/Exact.java | 84 ------ .../hssf/record/formula/functions/Left.java | 107 ------- .../hssf/record/formula/functions/Len.java | 49 ---- .../hssf/record/formula/functions/Lower.java | 65 ---- .../hssf/record/formula/functions/Mid.java | 87 ------ .../record/formula/functions/Replace.java | 156 ++++------ .../hssf/record/formula/functions/Right.java | 108 ------- .../record/formula/functions/Substitute.java | 187 ++++++------ .../formula/functions/TextFunction.java | 277 ++++++++++++------ .../hssf/record/formula/functions/Trim.java | 53 ---- .../hssf/record/formula/functions/Upper.java | 65 ---- .../poi/hssf/data/FormulaEvalTestData.xls | Bin 156160 -> 157184 bytes .../record/formula/functions/TestLen.java | 2 +- .../record/formula/functions/TestMid.java | 2 +- .../record/formula/functions/TestTrim.java | 2 +- 19 files changed, 341 insertions(+), 994 deletions(-) delete mode 100644 src/java/org/apache/poi/hssf/record/formula/functions/Concatenate.java delete mode 100644 src/java/org/apache/poi/hssf/record/formula/functions/Exact.java delete mode 100644 src/java/org/apache/poi/hssf/record/formula/functions/Left.java delete mode 100644 src/java/org/apache/poi/hssf/record/formula/functions/Len.java delete mode 100644 src/java/org/apache/poi/hssf/record/formula/functions/Lower.java delete mode 100644 src/java/org/apache/poi/hssf/record/formula/functions/Mid.java delete mode 100644 src/java/org/apache/poi/hssf/record/formula/functions/Right.java delete mode 100644 src/java/org/apache/poi/hssf/record/formula/functions/Trim.java delete mode 100644 src/java/org/apache/poi/hssf/record/formula/functions/Upper.java diff --git a/src/java/org/apache/poi/hssf/record/formula/eval/ConcatEval.java b/src/java/org/apache/poi/hssf/record/formula/eval/ConcatEval.java index 8d6729352b..6755cfafef 100644 --- a/src/java/org/apache/poi/hssf/record/formula/eval/ConcatEval.java +++ b/src/java/org/apache/poi/hssf/record/formula/eval/ConcatEval.java @@ -44,7 +44,7 @@ public final class ConcatEval implements OperationEval { if (ve instanceof StringValueEval) { StringValueEval sve = (StringValueEval) ve; sb.append(sve.getStringValue()); - } else if (ve instanceof BlankEval) { + } else if (ve == BlankEval.INSTANCE) { // do nothing } else { // must be an error eval throw new RuntimeException("Unexpected value type (" diff --git a/src/java/org/apache/poi/hssf/record/formula/eval/FunctionEval.java b/src/java/org/apache/poi/hssf/record/formula/eval/FunctionEval.java index 719349a1c8..e68b8ee67d 100644 --- a/src/java/org/apache/poi/hssf/record/formula/eval/FunctionEval.java +++ b/src/java/org/apache/poi/hssf/record/formula/eval/FunctionEval.java @@ -105,8 +105,8 @@ public abstract class FunctionEval implements OperationEval { retval[28] = new Lookup(); // LOOKUP retval[29] = new Index(); // INDEX retval[30] = new Rept(); // REPT - retval[31] = new Mid(); // MID - retval[32] = new Len(); // LEN + retval[31] = TextFunction.MID; + retval[32] = TextFunction.LEN; retval[33] = new Value(); // VALUE retval[34] = new True(); // TRUE retval[35] = new False(); // FALSE @@ -185,13 +185,13 @@ public abstract class FunctionEval implements OperationEval { retval[109] = NumericFunction.LOG; retval[110] = new Exec(); // EXEC retval[111] = new Char(); // CHAR - retval[112] = new Lower(); // LOWER - retval[113] = new Upper(); // UPPER + retval[112] = TextFunction.LOWER; + retval[113] = TextFunction.UPPER; retval[114] = new Proper(); // PROPER - retval[115] = new Left(); // LEFT - retval[116] = new Right(); // RIGHT - retval[117] = new Exact(); // EXACT - retval[118] = new Trim(); // TRIM + retval[115] = TextFunction.LEFT; + retval[116] = TextFunction.RIGHT; + retval[117] = TextFunction.EXACT; + retval[118] = TextFunction.TRIM; retval[119] = new Replace(); // REPLACE retval[120] = new Substitute(); // SUBSTITUTE retval[121] = new Code(); // CODE @@ -394,7 +394,7 @@ public abstract class FunctionEval implements OperationEval { retval[332] = new Tinv(); // TINV retval[334] = new NotImplementedFunction(); // MOVIECOMMAND retval[335] = new NotImplementedFunction(); // GETMOVIE - retval[336] = new Concatenate(); // CONCATENATE + retval[336] = TextFunction.CONCATENATE; retval[337] = NumericFunction.POWER; retval[338] = new NotImplementedFunction(); // PIVOTADDDATA retval[339] = new NotImplementedFunction(); // GETPIVOTTABLE diff --git a/src/java/org/apache/poi/hssf/record/formula/eval/OperandResolver.java b/src/java/org/apache/poi/hssf/record/formula/eval/OperandResolver.java index 87f45236bf..09be70477f 100755 --- a/src/java/org/apache/poi/hssf/record/formula/eval/OperandResolver.java +++ b/src/java/org/apache/poi/hssf/record/formula/eval/OperandResolver.java @@ -272,12 +272,7 @@ public final class OperandResolver { StringValueEval sve = (StringValueEval) ve; return sve.getStringValue(); } - if (ve instanceof NumberEval) { - NumberEval neval = (NumberEval) ve; - return neval.getStringValue(); - } - - if (ve instanceof BlankEval) { + if (ve == BlankEval.INSTANCE) { return ""; } throw new IllegalArgumentException("Unexpected eval class (" + ve.getClass().getName() + ")"); @@ -289,7 +284,7 @@ public final class OperandResolver { */ public static Boolean coerceValueToBoolean(ValueEval ve, boolean stringsAreBlanks) throws EvaluationException { - if (ve == null || ve instanceof BlankEval) { + if (ve == null || ve == BlankEval.INSTANCE) { // TODO - remove 've == null' condition once AreaEval is fixed return null; } @@ -297,7 +292,7 @@ public final class OperandResolver { return Boolean.valueOf(((BoolEval) ve).getBooleanValue()); } - if (ve instanceof BlankEval) { + if (ve == BlankEval.INSTANCE) { return null; } diff --git a/src/java/org/apache/poi/hssf/record/formula/functions/Concatenate.java b/src/java/org/apache/poi/hssf/record/formula/functions/Concatenate.java deleted file mode 100644 index be961c151e..0000000000 --- a/src/java/org/apache/poi/hssf/record/formula/functions/Concatenate.java +++ /dev/null @@ -1,60 +0,0 @@ -/* -* 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. -*/ -/* - * Created on May 15, 2005 - * - */ -package org.apache.poi.hssf.record.formula.functions; - -import org.apache.poi.hssf.record.formula.eval.BlankEval; -import org.apache.poi.hssf.record.formula.eval.ErrorEval; -import org.apache.poi.hssf.record.formula.eval.Eval; -import org.apache.poi.hssf.record.formula.eval.StringEval; -import org.apache.poi.hssf.record.formula.eval.StringValueEval; -import org.apache.poi.hssf.record.formula.eval.ValueEval; - -/** - * @author Amol S. Deshmukh < amolweb at ya hoo dot com > - * - */ -public class Concatenate extends TextFunction { - - - public Eval evaluate(Eval[] operands, int srcCellRow, short srcCellCol) { - ValueEval retval = null; - StringBuffer sb = new StringBuffer(); - - for (int i=0, iSize=operands.length; i MID returns a specific number of - * characters from a text string, starting at the specified position.

- * - * Syntax:
MID(text, start_num, - * num_chars)
- * - * @author Manda Wilson < wilson at c bio dot msk cc dot org > - */ -public class Mid implements Function { - /** - * Returns a specific number of characters from a text string, starting at - * the position you specify, based on the number of characters you specify. - * - * @see org.apache.poi.hssf.record.formula.eval.Eval - */ - public Eval evaluate(Eval[] args, int srcCellRow, short srcCellCol) { - if (args.length != 3) { - return ErrorEval.VALUE_INVALID; - } - - String text; - int startIx; // zero based - int numChars; - - try { - ValueEval evText = OperandResolver.getSingleValue(args[0], srcCellRow, srcCellCol); - text = OperandResolver.coerceValueToString(evText); - int startCharNum = evaluateNumberArg(args[1], srcCellRow, srcCellCol); - numChars = evaluateNumberArg(args[2], srcCellRow, srcCellCol); - startIx = startCharNum - 1; // convert to zero-based - } catch (EvaluationException e) { - return e.getErrorEval(); - } - - int len = text.length(); - if (startIx < 0) { - return ErrorEval.VALUE_INVALID; - } - if (numChars < 0) { - return ErrorEval.VALUE_INVALID; - } - if (numChars < 0 || startIx > len) { - return new StringEval(""); - } - int endIx = startIx + numChars; - if (endIx > len) { - endIx = len; - } - String result = text.substring(startIx, endIx); - return new StringEval(result); - - } - - private static int evaluateNumberArg(Eval arg, int srcCellRow, short srcCellCol) throws EvaluationException { - ValueEval ev = OperandResolver.getSingleValue(arg, srcCellRow, srcCellCol); - // Note - for start_num arg, blank/zero causes error(#VALUE!), - // but for num_chars causes empty string to be returned. - return OperandResolver.coerceValueToInt(ev); - } -} \ No newline at end of file diff --git a/src/java/org/apache/poi/hssf/record/formula/functions/Replace.java b/src/java/org/apache/poi/hssf/record/formula/functions/Replace.java index 95413f0823..1dfc4f8f05 100644 --- a/src/java/org/apache/poi/hssf/record/formula/functions/Replace.java +++ b/src/java/org/apache/poi/hssf/record/formula/functions/Replace.java @@ -1,112 +1,70 @@ -/* -* 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. -*/ -/* - * Created on May 15, 2005 - * - */ +/* ==================================================================== + 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.hssf.record.formula.functions; import org.apache.poi.hssf.record.formula.eval.ErrorEval; import org.apache.poi.hssf.record.formula.eval.Eval; -import org.apache.poi.hssf.record.formula.eval.NumericValueEval; +import org.apache.poi.hssf.record.formula.eval.EvaluationException; import org.apache.poi.hssf.record.formula.eval.StringEval; -import org.apache.poi.hssf.record.formula.eval.StringValueEval; import org.apache.poi.hssf.record.formula.eval.ValueEval; /** - * An implementation of the REPLACE function: - * Replaces part of a text string based on the number of characters - * you specify, with another text string. + * An implementation of the Excel REPLACE() function

: + * Replaces part of a text string based on the number of characters + * you specify, with another text string.
+ * + * Syntax:
+ * REPLACE(oldText, startNum, numChars, newText)

+ * + * oldText The text string containing characters to replace
+ * startNum The position of the first character to replace (1-based)
+ * numChars The number of characters to replace
+ * newText The new text value to replace the removed section
+ * * @author Manda Wilson < wilson at c bio dot msk cc dot org > */ -public class Replace extends TextFunction { +public final class Replace extends TextFunction { + + protected ValueEval evaluateFunc(Eval[] args, int srcCellRow, short srcCellCol) + throws EvaluationException { + if (args.length != 4) { + return ErrorEval.VALUE_INVALID; + } - /** - * Replaces part of a text string based on the number of characters - * you specify, with another text string. - * - * @see org.apache.poi.hssf.record.formula.eval.Eval - */ - public Eval evaluate(Eval[] operands, int srcCellRow, short srcCellCol) { - Eval retval = null; - String oldStr = null; - String newStr = null; - int startNum = 0; - int numChars = 0; - - switch (operands.length) { - default: - retval = ErrorEval.VALUE_INVALID; - case 4: - // first operand is text string containing characters to replace - // second operand is position of first character to replace - // third operand is the number of characters in the old string - // you want to replace with new string - // fourth operand is the new string - ValueEval firstveval = singleOperandEvaluate(operands[0], srcCellRow, srcCellCol); - ValueEval secondveval = singleOperandEvaluate(operands[1], srcCellRow, srcCellCol); - ValueEval thirdveval = singleOperandEvaluate(operands[2], srcCellRow, srcCellCol); - ValueEval fourthveval = singleOperandEvaluate(operands[3], srcCellRow, srcCellCol); - if (firstveval instanceof StringValueEval - && secondveval instanceof NumericValueEval - && thirdveval instanceof NumericValueEval - && fourthveval instanceof StringValueEval) { - - StringValueEval oldStrEval = (StringValueEval) firstveval; - oldStr = oldStrEval.getStringValue(); - - NumericValueEval startNumEval = (NumericValueEval) secondveval; - // NOTE: it is safe to cast to int here - // because in Excel =REPLACE("task", 2.7, 3, "est") - // returns test - // so 2.7 must be truncated to 2 - // and =REPLACE("task", 1, 1.9, "") returns ask - // so 1.9 must be truncated to 1 - startNum = (int) startNumEval.getNumberValue(); - - NumericValueEval numCharsEval = (NumericValueEval) thirdveval; - numChars = (int) numCharsEval.getNumberValue(); - - StringValueEval newStrEval = (StringValueEval) fourthveval; - newStr = newStrEval.getStringValue(); - } else { - retval = ErrorEval.VALUE_INVALID; - } - } - - if (retval == null) { - if (startNum < 1 || numChars < 0) { - retval = ErrorEval.VALUE_INVALID; - } else { - StringBuffer strBuff = new StringBuffer(oldStr); - // remove any characters that should be replaced - if (startNum <= oldStr.length() && numChars != 0) { - strBuff.delete(startNum - 1, startNum - 1 + numChars); - } - // now insert (or append) newStr - if (startNum > strBuff.length()) { - strBuff.append(newStr); - } else { - strBuff.insert(startNum - 1, newStr); - } - retval = new StringEval(strBuff.toString()); - } - } - return retval; - } + String oldStr = evaluateStringArg(args[0], srcCellRow, srcCellCol); + int startNum = evaluateIntArg(args[1], srcCellRow, srcCellCol); + int numChars = evaluateIntArg(args[2], srcCellRow, srcCellCol); + String newStr = evaluateStringArg(args[3], srcCellRow, srcCellCol); + if (startNum < 1 || numChars < 0) { + return ErrorEval.VALUE_INVALID; + } + StringBuffer strBuff = new StringBuffer(oldStr); + // remove any characters that should be replaced + if (startNum <= oldStr.length() && numChars != 0) { + strBuff.delete(startNum - 1, startNum - 1 + numChars); + } + // now insert (or append) newStr + if (startNum > strBuff.length()) { + strBuff.append(newStr); + } else { + strBuff.insert(startNum - 1, newStr); + } + return new StringEval(strBuff.toString()); + } } diff --git a/src/java/org/apache/poi/hssf/record/formula/functions/Right.java b/src/java/org/apache/poi/hssf/record/formula/functions/Right.java deleted file mode 100644 index 771d045734..0000000000 --- a/src/java/org/apache/poi/hssf/record/formula/functions/Right.java +++ /dev/null @@ -1,108 +0,0 @@ -/* -* 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. -*/ -/* - * Created on May 15, 2005 - * - */ -package org.apache.poi.hssf.record.formula.functions; - -import org.apache.poi.hssf.record.formula.eval.BoolEval; -import org.apache.poi.hssf.record.formula.eval.ErrorEval; -import org.apache.poi.hssf.record.formula.eval.Eval; -import org.apache.poi.hssf.record.formula.eval.NumberEval; -import org.apache.poi.hssf.record.formula.eval.RefEval; -import org.apache.poi.hssf.record.formula.eval.StringEval; -import org.apache.poi.hssf.record.formula.eval.ValueEval; - -/** - * @author Amol S. Deshmukh < amolweb at ya hoo dot com > - * - */ -public class Right extends TextFunction { - - public Eval evaluate(Eval[] operands, int srcCellRow, short srcCellCol) { - Eval retval = ErrorEval.VALUE_INVALID; - int index = 1; - switch (operands.length) { - default: - break; - case 2: - Eval indexEval = operands[1]; - index = evaluateAsInteger(indexEval); - if (index < 0) { - break; - } - case 1: - ValueEval veval = singleOperandEvaluate(operands[0], srcCellRow, srcCellCol); - String str = null; - if (veval instanceof StringEval) { - StringEval stringEval = (StringEval) veval; - str = stringEval.getStringValue(); - } - else if (veval instanceof BoolEval) { - BoolEval beval = (BoolEval) veval; - str = beval.getBooleanValue() ? "TRUE" : "FALSE"; - } - else if (veval instanceof NumberEval) { - NumberEval neval = (NumberEval) veval; - str = neval.getStringValue(); - } - if (null != str) { - int strlen = str.length(); - str = str.substring(Math.max(0, strlen-index)); - retval = new StringEval(str); - } - } - return retval; - } - - protected int evaluateAsInteger(Eval eval) { - int numval = -1; - if (eval instanceof NumberEval) { - NumberEval neval = (NumberEval) eval; - double d = neval.getNumberValue(); - numval = (int) d; - } - else if (eval instanceof StringEval) { - StringEval seval = (StringEval) eval; - String s = seval.getStringValue(); - try { - double d = Double.parseDouble(s); - numval = (int) d; - } - catch (Exception e) { - } - } - else if (eval instanceof BoolEval) { - BoolEval beval = (BoolEval) eval; - numval = beval.getBooleanValue() ? 1 : 0; - } - else if (eval instanceof RefEval) { - numval = evaluateAsInteger(xlateRefEval((RefEval) eval)); - } - return numval; - } - - protected Eval xlateRefEval(RefEval reval) { - Eval retval = reval.getInnerValueEval(); - - if (retval instanceof RefEval) { - retval = xlateRefEval((RefEval) retval); - } - return retval; - } -} diff --git a/src/java/org/apache/poi/hssf/record/formula/functions/Substitute.java b/src/java/org/apache/poi/hssf/record/formula/functions/Substitute.java index 9d2e9ce361..b00d7510e3 100644 --- a/src/java/org/apache/poi/hssf/record/formula/functions/Substitute.java +++ b/src/java/org/apache/poi/hssf/record/formula/functions/Substitute.java @@ -1,30 +1,26 @@ -/* -* 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. -*/ -/* - * Created on May 15, 2005 - * - */ +/* ==================================================================== + 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.hssf.record.formula.functions; import org.apache.poi.hssf.record.formula.eval.ErrorEval; import org.apache.poi.hssf.record.formula.eval.Eval; -import org.apache.poi.hssf.record.formula.eval.NumericValueEval; +import org.apache.poi.hssf.record.formula.eval.EvaluationException; import org.apache.poi.hssf.record.formula.eval.StringEval; -import org.apache.poi.hssf.record.formula.eval.StringValueEval; import org.apache.poi.hssf.record.formula.eval.ValueEval; /** @@ -32,86 +28,75 @@ import org.apache.poi.hssf.record.formula.eval.ValueEval; * Substitutes text in a text string with new text, some number of times. * @author Manda Wilson < wilson at c bio dot msk cc dot org > */ -public class Substitute extends TextFunction { - private static final int REPLACE_ALL = -1; - - /** - *Substitutes text in a text string with new text, some number of times. - * - * @see org.apache.poi.hssf.record.formula.eval.Eval - */ - public Eval evaluate(Eval[] operands, int srcCellRow, short srcCellCol) { - Eval retval = null; - String oldStr = null; - String searchStr = null; - String newStr = null; - int numToReplace = REPLACE_ALL; - - switch (operands.length) { - default: - retval = ErrorEval.VALUE_INVALID; - case 4: - ValueEval fourthveval = singleOperandEvaluate(operands[3], srcCellRow, srcCellCol); - if (fourthveval instanceof NumericValueEval) { - NumericValueEval numToReplaceEval = (NumericValueEval) fourthveval; - // NOTE: it is safe to cast to int here - // because in Excel =SUBSTITUTE("teststr","t","T",1.9) - // returns Teststr - // so 1.9 must be truncated to 1 - numToReplace = (int) numToReplaceEval.getNumberValue(); - } else { - retval = ErrorEval.VALUE_INVALID; - } - case 3: - // first operand is text string containing characters to replace - // second operand is text to find - // third operand is replacement text - ValueEval firstveval = singleOperandEvaluate(operands[0], srcCellRow, srcCellCol); - ValueEval secondveval = singleOperandEvaluate(operands[1], srcCellRow, srcCellCol); - ValueEval thirdveval = singleOperandEvaluate(operands[2], srcCellRow, srcCellCol); - if (firstveval instanceof StringValueEval - && secondveval instanceof StringValueEval - && thirdveval instanceof StringValueEval) { - - StringValueEval oldStrEval = (StringValueEval) firstveval; - oldStr = oldStrEval.getStringValue(); - - StringValueEval searchStrEval = (StringValueEval) secondveval; - searchStr = searchStrEval.getStringValue(); - - StringValueEval newStrEval = (StringValueEval) thirdveval; - newStr = newStrEval.getStringValue(); - } else { - retval = ErrorEval.VALUE_INVALID; - } - } - - if (retval == null) { - if (numToReplace != REPLACE_ALL && numToReplace < 1) { - retval = ErrorEval.VALUE_INVALID; - } else if (searchStr.length() == 0) { - retval = new StringEval(oldStr); - } else { - StringBuffer strBuff = new StringBuffer(); - int startIndex = 0; - int nextMatch = -1; - for (int leftToReplace = numToReplace; - (leftToReplace > 0 || numToReplace == REPLACE_ALL) - && (nextMatch = oldStr.indexOf(searchStr, startIndex)) != -1; - leftToReplace--) { - // store everything from end of last match to start of this match - strBuff.append(oldStr.substring(startIndex, nextMatch)); - strBuff.append(newStr); - startIndex = nextMatch + searchStr.length(); +public final class Substitute extends TextFunction { + + protected ValueEval evaluateFunc(Eval[] args, int srcCellRow, short srcCellCol) + throws EvaluationException { + if (args.length < 3 || args.length > 4) { + return ErrorEval.VALUE_INVALID; + } + + String oldStr = evaluateStringArg(args[0], srcCellRow, srcCellCol); + String searchStr = evaluateStringArg(args[1], srcCellRow, srcCellCol); + String newStr = evaluateStringArg(args[2], srcCellRow, srcCellCol); + + String result; + switch (args.length) { + default: + throw new IllegalStateException("Cannot happen"); + case 4: + int instanceNumber = evaluateIntArg(args[3], srcCellRow, srcCellCol); + if (instanceNumber < 1) { + return ErrorEval.VALUE_INVALID; } + result = replaceOneOccurrence(oldStr, searchStr, newStr, instanceNumber); + break; + case 3: + result = replaceAllOccurrences(oldStr, searchStr, newStr); + } + return new StringEval(result); + } + + private static String replaceAllOccurrences(String oldStr, String searchStr, String newStr) { + StringBuffer sb = new StringBuffer(); + int startIndex = 0; + int nextMatch = -1; + while (true) { + nextMatch = oldStr.indexOf(searchStr, startIndex); + if (nextMatch < 0) { // store everything from end of last match to end of string - if (startIndex < oldStr.length()) { - strBuff.append(oldStr.substring(startIndex)); - } - retval = new StringEval(strBuff.toString()); + sb.append(oldStr.substring(startIndex)); + return sb.toString(); + } + // store everything from end of last match to start of this match + sb.append(oldStr.substring(startIndex, nextMatch)); + sb.append(newStr); + startIndex = nextMatch + searchStr.length(); + } + } + + private static String replaceOneOccurrence(String oldStr, String searchStr, String newStr, int instanceNumber) { + if (searchStr.length() < 1) { + return oldStr; + } + int startIndex = 0; + int nextMatch = -1; + int count=0; + while (true) { + nextMatch = oldStr.indexOf(searchStr, startIndex); + if (nextMatch < 0) { + // not enough occurrences found - leave unchanged + return oldStr; + } + count++; + if (count == instanceNumber) { + StringBuffer sb = new StringBuffer(oldStr.length() + newStr.length()); + sb.append(oldStr.substring(0, nextMatch)); + sb.append(newStr); + sb.append(oldStr.substring(nextMatch + searchStr.length())); + return sb.toString(); } - } - return retval; - } - + startIndex = nextMatch + searchStr.length(); + } + } } diff --git a/src/java/org/apache/poi/hssf/record/formula/functions/TextFunction.java b/src/java/org/apache/poi/hssf/record/formula/functions/TextFunction.java index 9da1776127..df7db7ad32 100644 --- a/src/java/org/apache/poi/hssf/record/formula/functions/TextFunction.java +++ b/src/java/org/apache/poi/hssf/record/formula/functions/TextFunction.java @@ -1,31 +1,29 @@ -/* -* 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. -*/ -/* - * Created on May 22, 2005 - * - */ +/* ==================================================================== + 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.hssf.record.formula.functions; -import org.apache.poi.hssf.record.formula.eval.AreaEval; -import org.apache.poi.hssf.record.formula.eval.BlankEval; +import org.apache.poi.hssf.record.formula.eval.BoolEval; import org.apache.poi.hssf.record.formula.eval.ErrorEval; import org.apache.poi.hssf.record.formula.eval.Eval; -import org.apache.poi.hssf.record.formula.eval.RefEval; -import org.apache.poi.hssf.record.formula.eval.StringValueEval; +import org.apache.poi.hssf.record.formula.eval.EvaluationException; +import org.apache.poi.hssf.record.formula.eval.NumberEval; +import org.apache.poi.hssf.record.formula.eval.OperandResolver; +import org.apache.poi.hssf.record.formula.eval.StringEval; import org.apache.poi.hssf.record.formula.eval.ValueEval; /** @@ -33,75 +31,164 @@ import org.apache.poi.hssf.record.formula.eval.ValueEval; * */ public abstract class TextFunction implements Function { - - protected static final String EMPTY_STRING = ""; - - protected ValueEval singleOperandEvaluate(Eval eval, int srcRow, short srcCol) { - ValueEval retval; - if (eval instanceof AreaEval) { - AreaEval ae = (AreaEval) eval; - if (ae.contains(srcRow, srcCol)) { // circular ref! - retval = ErrorEval.CIRCULAR_REF_ERROR; - } - else if (ae.isRow()) { - if (ae.containsColumn(srcCol)) { - ValueEval ve = ae.getValueAt(ae.getFirstRow(), srcCol); - retval = attemptXlateToText(ve); - } - else { - retval = ErrorEval.VALUE_INVALID; - } - } - else if (ae.isColumn()) { - if (ae.containsRow(srcRow)) { - ValueEval ve = ae.getValueAt(srcRow, ae.getFirstColumn()); - retval = attemptXlateToText(ve); - } - else { - retval = ErrorEval.VALUE_INVALID; - } - } - else { - retval = ErrorEval.VALUE_INVALID; - } - } - else { - retval = attemptXlateToText((ValueEval) eval); - } - return retval; - } - - - /** - * converts from Different ValueEval types to StringEval. - * Note: AreaEvals are not handled, if arg is an AreaEval, - * the returned value is ErrorEval.VALUE_INVALID - * @param ve - */ - protected ValueEval attemptXlateToText(ValueEval ve) { - ValueEval retval; - if (ve instanceof StringValueEval) { - retval = ve; - } - else if (ve instanceof RefEval) { - RefEval re = (RefEval) ve; - ValueEval ive = re.getInnerValueEval(); - if (ive instanceof StringValueEval) { - retval = ive; - } - else if (ive instanceof BlankEval) { - retval = ive; - } - else { - retval = ErrorEval.VALUE_INVALID; - } - } - else if (ve instanceof BlankEval) { - retval = ve; - } - else { - retval = ErrorEval.VALUE_INVALID; - } - return retval; - } + + protected static final String EMPTY_STRING = ""; + + protected static final String evaluateStringArg(Eval eval, int srcRow, short srcCol) throws EvaluationException { + ValueEval ve = OperandResolver.getSingleValue(eval, srcRow, srcCol); + return OperandResolver.coerceValueToString(ve); + } + protected static final int evaluateIntArg(Eval arg, int srcCellRow, short srcCellCol) throws EvaluationException { + ValueEval ve = OperandResolver.getSingleValue(arg, srcCellRow, srcCellCol); + return OperandResolver.coerceValueToInt(ve); + } + + public final Eval evaluate(Eval[] args, int srcCellRow, short srcCellCol) { + try { + return evaluateFunc(args, srcCellRow, srcCellCol); + } catch (EvaluationException e) { + return e.getErrorEval(); + } + } + + protected abstract ValueEval evaluateFunc(Eval[] args, int srcCellRow, short srcCellCol) throws EvaluationException; + + /* ---------------------------------------------------------------------- */ + + private static abstract class SingleArgTextFunc extends TextFunction { + + protected SingleArgTextFunc() { + // no fields to initialise + } + protected ValueEval evaluateFunc(Eval[] args, int srcCellRow, short srcCellCol) + throws EvaluationException { + if (args.length != 1) { + return ErrorEval.VALUE_INVALID; + } + String arg = evaluateStringArg(args[0], srcCellRow, srcCellCol); + return evaluate(arg); + } + protected abstract ValueEval evaluate(String arg); + } + + public static final Function LEN = new SingleArgTextFunc() { + protected ValueEval evaluate(String arg) { + return new NumberEval(arg.length()); + } + }; + public static final Function LOWER = new SingleArgTextFunc() { + protected ValueEval evaluate(String arg) { + return new StringEval(arg.toLowerCase()); + } + }; + public static final Function UPPER = new SingleArgTextFunc() { + protected ValueEval evaluate(String arg) { + return new StringEval(arg.toUpperCase()); + } + }; + /** + * An implementation of the TRIM function: + * Removes leading and trailing spaces from value if evaluated operand + * value is string. + * @author Manda Wilson < wilson at c bio dot msk cc dot org > + */ + public static final Function TRIM = new SingleArgTextFunc() { + protected ValueEval evaluate(String arg) { + return new StringEval(arg.trim()); + } + }; + + /** + * An implementation of the MID function
+ * MID returns a specific number of + * characters from a text string, starting at the specified position.

+ * + * Syntax:
MID(text, start_num, + * num_chars)
+ * + * @author Manda Wilson < wilson at c bio dot msk cc dot org > + */ + public static final Function MID = new TextFunction() { + + protected ValueEval evaluateFunc(Eval[] args, int srcCellRow, short srcCellCol) + throws EvaluationException { + if (args.length != 3) { + return ErrorEval.VALUE_INVALID; + } + + String text = evaluateStringArg(args[0], srcCellRow, srcCellCol); + int startCharNum = evaluateIntArg(args[1], srcCellRow, srcCellCol); + int numChars = evaluateIntArg(args[2], srcCellRow, srcCellCol); + int startIx = startCharNum - 1; // convert to zero-based + + // Note - for start_num arg, blank/zero causes error(#VALUE!), + // but for num_chars causes empty string to be returned. + if (startIx < 0) { + return ErrorEval.VALUE_INVALID; + } + if (numChars < 0) { + return ErrorEval.VALUE_INVALID; + } + int len = text.length(); + if (numChars < 0 || startIx > len) { + return new StringEval(""); + } + int endIx = Math.min(startIx + numChars, len); + String result = text.substring(startIx, endIx); + return new StringEval(result); + } + }; + + private static final class LeftRight extends TextFunction { + + private final boolean _isLeft; + protected LeftRight(boolean isLeft) { + _isLeft = isLeft; + } + protected ValueEval evaluateFunc(Eval[] args, int srcCellRow, short srcCellCol) + throws EvaluationException { + if (args.length != 2) { + return ErrorEval.VALUE_INVALID; + } + String arg = evaluateStringArg(args[0], srcCellRow, srcCellCol); + int index = evaluateIntArg(args[1], srcCellRow, srcCellCol); + + String result; + if (_isLeft) { + result = arg.substring(0, Math.min(arg.length(), index)); + } else { + result = arg.substring(Math.max(0, arg.length()-index)); + } + return new StringEval(result); + } + } + + public static final Function LEFT = new LeftRight(true); + public static final Function RIGHT = new LeftRight(false); + + public static final Function CONCATENATE = new TextFunction() { + + protected ValueEval evaluateFunc(Eval[] args, int srcCellRow, short srcCellCol) + throws EvaluationException { + StringBuffer sb = new StringBuffer(); + for (int i=0, iSize=args.length; i>%U2POD1_T?=7@`~9Zvf}M3|QT14`ub87Ut%(u81#x$7Qkd~$8=s?{}>53O0d zW+Pc!aZzoam5|gY*rqY?v$fT0*W9^k#Xx%$SYp+{tVN0fifWy`)ufsL-Vbb_os=#P zxzreMT;P+Xt6aC60ZN;i=9bU10yH%>dFPbPEmsKhHW83}Yf(;4QGS7qJI(QW%e-^F ztb2#+O^gSM*4E4B(@g+>+zRm6PO^G0KzTcLKMJt#6u{2U0jj?SnA`{8ya@356*Bx9 zz@q|Txd|c8j!+VhFfkF~>lB2}bcEHJ2-_wjJUeg!DHNvVV`@`3u5|HhQ)rJbet|z-I`5>Ox5A zLHOnZLRuec9S-rR)^ZiI1ED7`Tc!V_w;=G}4JA_kvV%1D3u_eR*b*)9PRk4wz7F+^ zmojC4UyD>Nn~p2NC7Sxhrd(Nhu1czLFTW$7rZMWFm*13a4(d|>>$fC$PD|CX`FVNd zBK6s^d8V?H2A$gcTX~XzK;xD|MFzR^+v+6gW1bW0Z0Tpk(K<(ZHnzyX{jG0I#11v| zeyTi7T?6C)wk>k^p>9n6p5plf$&V!0IkqAC(Gw`IRsF-;cOO1KL4LyQ|G6+-X{CwN zdh?|({#R1Hr}s9wa|*S*+FPt75OAt@rb4p@#{PR_q70I4+G`fPMRv3H5o`Udea9MH zA}z~GS`2xo-DAZH8O7M28FR6A+KMw|CTaQ@8{U>JzdYU|wTc4#R6-|dlV;*qVr_?R zE5x=0(a@nCtiY)f!gZ}F;|{r z?bC%=DeEcn`qV{)VT`_PF;-dQW5CM$o1$R{tB8RI6@(b==mR*eWMoq8zOU^$*uYRS^HDx4`RmuQN1Zxt5Eel}`r6=umC4#mDIoFb20ChY=iJ_@?# zs-}L&DBRk@YW#)p#A}~aW1g&ye+x9r3cMrL45_eEu|p}9Ckwg2G1#s|gsf0zfkbV= z3Vb)xB&_5U_p#`>5>qVlD$krzhtuUrp0Bcw7NM2`qrFgv+2X+t?Y%nOX4@YHkc~dE zSFd~okJ&J!Pk9^Lwa6HfssN8n#qc9{ z!jL_KRohI5O^u}JP}10mDD0a#G-bFwY1BkNYtZ162H#+;%#cqMRw!*Ijuh$@=CFiP zhd7c`jDcdJjg!t$1#IrO@v7W1$2m zZHN{>Z7nNn`~6fOVp#juPc>s1*Ng?PVsWEj&YLhFE;j0nYZNT$ zCTy@CB@bcIFbJkGsJlPFOyshxEf3->C6X-m2dT<>*vI}Lu9Fq4)$PX;>1o(Qm3Kcr zETM-sef$AjAgj2w<^Yz=E zyt0WlV-yvWq1#26FrEm9a@J{b`~+e1Fh(#K%`k^datvdj6b?=Mza|ZAg#HiMu&^Oc z!?W5Op1H0M^Q?K$rNL*|HD#Dm8doty@Jtj%XFwv)7e&ZWFgI?l=%t0GKzIy7G= zjyJOQcb%9cvp8!`cG5Z6!09MYV74sg?)fJ$L%LbJk2I0rp+9p1eGwCL2;zG4gh}#2 zX9gK8T}KO($_Ol?lxuxZ=WIid~DyTGi2W}?q}@%)s4BBsnvJmJef`d zb-f##Mf2DZ)k3Aq>`1J26igchdu0^twNWtt5H?gTwlk<7Jd2SiE3(O*PtW08xrNY-Y=)Ahv90jNLEw=53I z8?3c4)_ERRDnEclE53jU#R4)1jtRQ2@UlfhVzBfvTDD9+uc>+~kUX%kswpNE4NC#e zD*v>^24l5V7qC5^>wK#zhI(ciiQ7t}R%$bQ@r8st=-kdW5|opag=>GGIIVZ~;$$=0 zwd)u0NeR=)=%+5>!&cVPGxx|p^jt#3NbUS(oFc}WwD>DHBbs*v6vYF<(KC}|97!AreZ+u+ z#7UAsGG43x7M*vckxJh*kU^47GJ#|w2~$j>Cw=EY7ReM6`rd(CNT!kGkW43;K|Eb-^bBU$0ffsi{ zg2<9>y!7i6M5FXE)}J7z${=H@iK0p#W^F^F*daGjt3D}7tP^`u#Z@=f>EA6DUE=Pd zvZ8W#SwTTzS-#hud$VMWi72b^mShwa78SS)3(5<M$9dgyoTlXcG!()l9-_ z#%K&i8VDI%b)G-}4SMjIPj2bctWBw9sz^xOri&9FOX@3-GQ z|9RYV{(G;p%iwG`R4+oy{!~AFBrGmO0Iwd(esg~)=%-Ipg+*IT7`!u?K(A{CUM!jM5S0C#QV<^ZlihR{ngQuJG zRrAJ?iGd$fBLFaPKWLo%SDcZh% zwK63j@ZDhd&6J#PPe)) zu3hi%+EKBQSMJ{e;HUwxH3M{>0vI|2@bDtQrmq3=t^@dY16;oa@a0{A1NQ;6L4cVI z!B>ZH-i&b1itvvBgkJ?A>)+Q zaOyi0m5=a{Yx%Z&dLG&qs<`ZLrE>mlE1~VX$`!?y8S8qmCkC1If@-S@=Z+0cike;Z zSH+$gtJ?O)@so~7MSXGaG2U`IR8cMOHF14-gQ6aP?+7oIx_SHF_RRVrRv8%qSgi99 z`$O1NRagCtQoA!!4XZxDi)hkysya#G-txi!s53bfkj>EAg*;h;yxI)zh!0X+M`}Ne zzz%i!=fOObR9z*XAM~s__p?trZ(`tMs@2fQ$Ox@243P|zkVgOn*Yb1U&)}sIWIU;F zuF^_=i~AC|f7}8^ZR}gjZ5ErN{=09HB8jm6M1`EX*7omwh8Nw9Rm9CnIGfi|kitig zv%R~K{h!8yn7(F13d^x4oFG!0!DRt4P$826DxqFg}m1u?mV|W+Y)p8BE zIGQ&OH7er$`S=xYmFTrZyunNtw3SJCGJyGC5T+cA;?>exN)9F}y*ndCc@8G?EtHP< zI0xgoQ=;x1jN`i{3S5b+xt(gEm9HdDEq^>&JD!W#dckwi8;b3P(YD_lbGxRd{%r>fGyhALYy{<*GY5lm*8CfYiaI$39S#OMij#( z7|nQ>h~9t)4XOzs8ed{9+W8H5*?_}Z<0fniLar66xQq*abTqf4qq)g}a+3q)CI`w* z4wM@W+PpfPg61JGLNRAx2in3wTX6`Jk=H&hIJCiMj4){U_i8pW?=SOnbta|K~VFlG@SJ7>=^<#aC^ZqIgln zmNV3}EwbSX&){a>D$zG*FqOAj2?e*~D}0))?Cy44!7s|$tL?ayCrd881G9M6k5tBr z4$R=~)P>?y2PU}<-|xU&-Y6I4`A$sXPWiyzPI8tl)oyfB-J9jC^(@AVwsTZs_Ge6I zY``E|zC(Y@QT_;~ftd~kxo|VY_AV-F*ChS9uAsux|uFg_^J9zyKX3)-=3 z=rl&u5heTKmpVS8hfELPt!bOV1A-+3E2+RXXQWuwO>J$LU9z(q=kUuiMcuufrC4Q0wW%N>(}fCk?04vGY@|b^=aJz|~E_9h!ivf5JU!xCTjTKYov% z$epy~3f7NHIBcYJp6#dC#akk0KXpN?OxD#;X`gea-A`%9$|@ReQ5C}_N|dNsS}D9m zTa8mLK>aPU;*^VWNuqkmdECZAMb&wWjkhtts!)|i%k~&1$+CHZNu0io?FP9M)atw; zO?+?%cLn%)$lXRZ+qgs#I?KM)Qtsj`5A+rV4{$5b{u_nz&jA3Qx-5`u)GRYJY9}>C{z*G`H zk`NMqk^qrEh<>SbKLENHz;u!sB+>-k7a)RUCJEgez(x{DGMi*BNi@kEk{IoaLCnDY zrTRcm<}ddk_a}^gX_>$_gz)JQYIGggjwgoE$Kvtf2tuK!hzn$;;?^U4SF@U#pO?pH zUJUvsYeWY#o*?^nRS;Y2d2{SwL_m4S`-zJ|%*Y)!D(3YdR>tqh!mSQwQM_1kJA#>m zmrB$f%o_O>n$`A%u+2=(e!b{K7<-tE1=_$G_Aj<9F)cAUDJ>%@J~JczX$kW(W@e|S zhRs_rFFk%ieA@Vz_+=TXVM~&iq%BQen!a>_l-L9<>LuojYAn}l_B>`sgY#a4`{t{t Nr?(|(tqx|_{TC{*AGQDh diff --git a/src/testcases/org/apache/poi/hssf/record/formula/functions/TestLen.java b/src/testcases/org/apache/poi/hssf/record/formula/functions/TestLen.java index a96fb4e2b0..459d2fb555 100755 --- a/src/testcases/org/apache/poi/hssf/record/formula/functions/TestLen.java +++ b/src/testcases/org/apache/poi/hssf/record/formula/functions/TestLen.java @@ -35,7 +35,7 @@ public final class TestLen extends TestCase { private static Eval invokeLen(Eval text) { Eval[] args = new Eval[] { text, }; - return new Len().evaluate(args, -1, (short)-1); + return TextFunction.LEN.evaluate(args, -1, (short)-1); } private void confirmLen(Eval text, int expected) { diff --git a/src/testcases/org/apache/poi/hssf/record/formula/functions/TestMid.java b/src/testcases/org/apache/poi/hssf/record/formula/functions/TestMid.java index 76cd1056ed..81e78e737c 100755 --- a/src/testcases/org/apache/poi/hssf/record/formula/functions/TestMid.java +++ b/src/testcases/org/apache/poi/hssf/record/formula/functions/TestMid.java @@ -38,7 +38,7 @@ public final class TestMid extends TestCase { private static Eval invokeMid(Eval text, Eval startPos, Eval numChars) { Eval[] args = new Eval[] { text, startPos, numChars, }; - return new Mid().evaluate(args, -1, (short)-1); + return TextFunction.MID.evaluate(args, -1, (short)-1); } private void confirmMid(Eval text, Eval startPos, Eval numChars, String expected) { diff --git a/src/testcases/org/apache/poi/hssf/record/formula/functions/TestTrim.java b/src/testcases/org/apache/poi/hssf/record/formula/functions/TestTrim.java index 076ac1fc7e..1c65c9ca6d 100755 --- a/src/testcases/org/apache/poi/hssf/record/formula/functions/TestTrim.java +++ b/src/testcases/org/apache/poi/hssf/record/formula/functions/TestTrim.java @@ -35,7 +35,7 @@ public final class TestTrim extends TestCase { private static Eval invokeTrim(Eval text) { Eval[] args = new Eval[] { text, }; - return new Trim().evaluate(args, -1, (short)-1); + return TextFunction.TRIM.evaluate(args, -1, (short)-1); } private void confirmTrim(Eval text, String expected) { -- 2.39.5