]> source.dussan.org Git - poi.git/commitdiff
Added implementation for TEXT() and TRUNC(), see Bugzilla 49025 and 49026
authorYegor Kozlov <yegor@apache.org>
Sat, 24 Apr 2010 17:17:20 +0000 (17:17 +0000)
committerYegor Kozlov <yegor@apache.org>
Sat, 24 Apr 2010 17:17:20 +0000 (17:17 +0000)
git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@937652 13f79535-47bb-0310-9956-ffa450edef68

src/documentation/content/xdocs/status.xml
src/java/org/apache/poi/hssf/record/formula/eval/FunctionEval.java
src/java/org/apache/poi/hssf/record/formula/functions/NumericFunction.java
src/java/org/apache/poi/hssf/record/formula/functions/TextFunction.java
src/testcases/org/apache/poi/hssf/record/formula/functions/TestText.java [new file with mode: 0644]
src/testcases/org/apache/poi/hssf/record/formula/functions/TestTrunc.java [new file with mode: 0644]

index f3805fb6b8635821b0168fa73863cb4a51a8a5e5..bc9fb9da08b4bdf753d34c61d89d2129d97bf675 100644 (file)
@@ -34,6 +34,8 @@
 
     <changes>
         <release version="3.7-SNAPSHOT" date="2010-??-??">
+           <action dev="POI-DEVELOPERS" type="add">49026 - Added implementation for TEXT()  </action>
+           <action dev="POI-DEVELOPERS" type="add">49025 - Added implementation for TRUNC()  </action>
            <action dev="POI-DEVELOPERS" type="fix">49147 - Properly close internal InputStream in ExtractorFactory#createExtractor(File)</action>
            <action dev="POI-DEVELOPERS" type="fix">49138 - Fixed locale-sensitive formatters in PackagePropertiesPart</action>
            <action dev="POI-DEVELOPERS" type="fix">49153 - Ensure that CTVectorVariant is included in poi-ooxml-schemas.jar</action>
index 7b17996dc3900353d3fa87cc6ca16094b3e14c3d..a50ac599732ed1e52d95b669cb53d8f15094cef4 100644 (file)
@@ -94,6 +94,7 @@ public final class FunctionEval {
                retval[37] = BooleanFunction.OR;
                retval[38] = BooleanFunction.NOT;
                retval[39] = NumericFunction.MOD;
+               retval[48] = TextFunction.TEXT;
 
                retval[56] = FinanceFunction.PV;
                retval[57] = FinanceFunction.FV;
@@ -152,7 +153,7 @@ public final class FunctionEval {
                retval[184] = NumericFunction.FACT;
 
                retval[190] = LogicalFunction.ISNONTEXT;
-
+               retval[197] = NumericFunction.TRUNC;
                retval[198] = LogicalFunction.ISLOGICAL;
 
                retval[212] = NumericFunction.ROUNDUP;
index 28aed5cc2855862a4dfa6a2efe59f6e5f3c68ad6..bf69e6181fb395eba00b0d14487753c295d9c115 100644 (file)
@@ -26,6 +26,7 @@ import org.apache.poi.hssf.record.formula.eval.ValueEval;
 /**
  * @author Amol S. Deshmukh &lt; amolweb at ya hoo dot com &gt;
  * @author Josh Micich
+ * @author Stephen Wolke (smwolke at geistig.com)
  */
 public abstract class NumericFunction implements Function {
 
@@ -321,6 +322,27 @@ public abstract class NumericFunction implements Function {
                        return MathX.roundUp(d0, (int)d1);
                }
        };
+       static final NumberEval TRUNC_ARG2_DEFAULT = new NumberEval(0);
+       public static final Function TRUNC = new Var1or2ArgFunction() {
+
+               public ValueEval evaluate(int srcRowIndex, int srcColumnIndex, ValueEval arg0) {
+                       return evaluate(srcRowIndex, srcColumnIndex, arg0, TRUNC_ARG2_DEFAULT);
+               }
+
+               public ValueEval evaluate(int srcRowIndex, int srcColumnIndex, ValueEval arg0, ValueEval arg1) {
+                       double result;
+                       try {
+                               double d0 = singleOperandEvaluate(arg0, srcRowIndex, srcColumnIndex);
+                               double d1 = singleOperandEvaluate(arg1, srcRowIndex, srcColumnIndex);
+                               double multi = Math.pow(10d,d1);
+                               result = Math.floor(d0 * multi) / multi;
+                               checkValue(result);
+                       }catch (EvaluationException e) {
+                               return e.getErrorEval();
+                       }
+                       return new NumberEval(result);
+               }
+       };
 
        /* -------------------------------------------------------------------------- */
 
index 6b108f2f65b6469b755687e0ddc9d03cbc6a586b..3e43ac28f67348d262977df48358221ee67d92dc 100644 (file)
 
 package org.apache.poi.hssf.record.formula.functions;
 
+import java.text.DateFormat;
+import java.text.DecimalFormat;
+import java.text.NumberFormat;
+import java.text.SimpleDateFormat;
+import java.util.Calendar;
+import java.util.GregorianCalendar;
 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.EvaluationException;
@@ -28,6 +34,7 @@ import org.apache.poi.hssf.record.formula.eval.ValueEval;
 /**
  * @author Amol S. Deshmukh &lt; amolweb at ya hoo dot com &gt;
  * @author Josh Micich
+ * @author Stephen Wolke (smwolke at geistig.com)
  */
 public abstract class TextFunction implements Function {
 
@@ -41,6 +48,11 @@ public abstract class TextFunction implements Function {
                ValueEval ve = OperandResolver.getSingleValue(arg, srcCellRow, srcCellCol);
                return OperandResolver.coerceValueToInt(ve);
        }
+       
+       protected static final double evaluateDoubleArg(ValueEval arg, int srcCellRow, int srcCellCol) throws EvaluationException {
+               ValueEval ve = OperandResolver.getSingleValue(arg, srcCellRow, srcCellCol);
+               return OperandResolver.coerceValueToDouble(ve);
+       }
 
        public final ValueEval evaluate(ValueEval[] args, int srcCellRow, int srcCellCol) {
                try {
@@ -206,6 +218,86 @@ public abstract class TextFunction implements Function {
                }
        };
 
+       /**
+        * An implementation of the TEXT function<br/>
+        * TEXT returns a number value formatted with the given
+        * number formatting string. This function is not a complete implementation of
+        * the Excel function.  This function implements decimal formatting
+        * with the Java class DecimalFormat.  For date formatting this function uses
+        * the SimpleDateFormat class.<p/>
+        *
+        * <b>Syntax<b>:<br/> <b>TEXT</b>(<b>value</b>, <b>format_text</b>)<br/>
+        *
+        */
+       public static final Function TEXT = new Fixed2ArgFunction() {
+
+               public ValueEval evaluate(int srcRowIndex, int srcColumnIndex, ValueEval arg0, ValueEval arg1) {
+                       double s0;
+                       String s1;
+                       try {
+                               s0 = evaluateDoubleArg(arg0, srcRowIndex, srcColumnIndex);
+                               s1 = evaluateStringArg(arg1, srcRowIndex, srcColumnIndex);
+                       } catch (EvaluationException e) {
+                               return e.getErrorEval();
+                       }
+                       if (s1.matches("[\\d,\\#,\\.,\\$,\\,]+")) {
+                       NumberFormat formatter = new DecimalFormat(s1);
+                       return new StringEval(formatter.format(s0));
+                       } else if (s1.indexOf("/") == s1.lastIndexOf("/") && s1.indexOf("/") >=0 && !s1.contains("-")) {
+                               double wholePart = Math.floor(s0);
+                               double decPart = s0 - wholePart;
+                               if (wholePart * decPart == 0) {
+                                       return new StringEval("0");
+                               }
+                               String[] parts = s1.split(" ");
+                               String[] fractParts;
+                               if (parts.length == 2) {
+                                       fractParts = parts[1].split("/");
+                               } else {
+                                       fractParts = s1.split("/");
+                               }
+
+                               if (fractParts.length == 2) {
+                                       double minVal = 1.0;
+                                       double currDenom = Math.pow(10 ,  fractParts[1].length()) - 1d;
+                                       double currNeum = 0;
+                                       for (int i = (int)(Math.pow(10,  fractParts[1].length())- 1d); i > 0; i--) {
+                                               for(int i2 = (int)(Math.pow(10,  fractParts[1].length())- 1d); i2 > 0; i2--){
+                                                       if (minVal >=  Math.abs((double)i2/(double)i - decPart)) {
+                                                               currDenom = i;
+                                                               currNeum = i2;
+                                                               minVal = Math.abs((double)i2/(double)i  - decPart);
+                                                       }
+                                               }
+                                       }
+                                       NumberFormat neumFormatter = new DecimalFormat(fractParts[0]);
+                                       NumberFormat denomFormatter = new DecimalFormat(fractParts[1]);
+                                       if (parts.length == 2) {
+                                               NumberFormat wholeFormatter = new DecimalFormat(parts[0]);
+                                               String result = wholeFormatter.format(wholePart) + " " + neumFormatter.format(currNeum) + "/" + denomFormatter.format(currDenom);
+                                               return new StringEval(result);
+                                       } else {
+                                               String result = neumFormatter.format(currNeum + (currDenom * wholePart)) + "/" + denomFormatter.format(currDenom);
+                                               return new StringEval(result);
+                                       }
+                               } else {
+                                       return ErrorEval.VALUE_INVALID;
+                               }
+                       } else {
+                               try {
+                                       DateFormat dateFormatter = new SimpleDateFormat(s1);
+                                       Calendar cal = new GregorianCalendar(1899, 11, 30, 0, 0, 0);
+                                       cal.add(Calendar.DATE, (int)Math.floor(s0));
+                                       double dayFraction = s0 - Math.floor(s0);
+                                       cal.add(Calendar.MILLISECOND, (int) Math.round(dayFraction * 24 * 60 * 60 * 1000));
+                                       return new StringEval(dateFormatter.format(cal.getTime()));
+                               } catch (Exception e) {
+                                       return ErrorEval.VALUE_INVALID;
+                               }
+                       }
+               }
+       };
+       
        private static final class SearchFind extends Var2or3ArgFunction {
 
                private final boolean _isCaseSensitive;
diff --git a/src/testcases/org/apache/poi/hssf/record/formula/functions/TestText.java b/src/testcases/org/apache/poi/hssf/record/formula/functions/TestText.java
new file mode 100644 (file)
index 0000000..c3e429b
--- /dev/null
@@ -0,0 +1,105 @@
+/* ====================================================================
+   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 junit.framework.TestCase;
+import org.apache.poi.hssf.record.formula.eval.ErrorEval;
+import org.apache.poi.hssf.record.formula.eval.NumberEval;
+import org.apache.poi.hssf.record.formula.eval.ValueEval;
+import org.apache.poi.hssf.record.formula.eval.StringEval;
+
+/**
+ * Test case for TEXT()
+ *
+ * @author Stephen Wolke (smwolke at geistig.com)
+ */
+public final class TestText extends TestCase {
+       private static final TextFunction T = null;
+       
+       public void testTextWithStringFirstArg() {
+
+               ValueEval strArg = new StringEval("abc");
+               ValueEval formatArg = new StringEval("abc");
+               ValueEval[] args = { strArg, formatArg };
+               ValueEval result = T.TEXT.evaluate(args, -1, (short)-1);
+               assertEquals(ErrorEval.VALUE_INVALID, result);
+       }
+       
+       public void testTextWithDeciamlFormatSecondArg() {
+
+               ValueEval numArg = new NumberEval(321321.321);
+               ValueEval formatArg = new StringEval("#,###.00000");
+               ValueEval[] args = { numArg, formatArg };
+               ValueEval result = T.TEXT.evaluate(args, -1, (short)-1);
+               ValueEval testResult = new StringEval("321,321.32100");
+               assertEquals(testResult.toString(), result.toString());
+               numArg = new NumberEval(321.321);
+               formatArg = new StringEval("00000.00000");
+               args[0] = numArg; 
+               args[1] = formatArg; 
+               result = T.TEXT.evaluate(args, -1, (short)-1);
+               testResult = new StringEval("00321.32100");
+               assertEquals(testResult.toString(), result.toString());
+               
+               formatArg = new StringEval("$#.#");
+               args[1] = formatArg; 
+               result = T.TEXT.evaluate(args, -1, (short)-1);
+               testResult = new StringEval("$321.3");
+               assertEquals(testResult.toString(), result.toString());
+       }
+       
+       public void testTextWithFractionFormatSecondArg() {
+
+               ValueEval numArg = new NumberEval(321.321);
+               ValueEval formatArg = new StringEval("# #/#");
+               ValueEval[] args = { numArg, formatArg };
+               ValueEval result = T.TEXT.evaluate(args, -1, (short)-1);
+               ValueEval testResult = new StringEval("321 1/3");
+               assertEquals(testResult.toString(), result.toString());
+               
+               formatArg = new StringEval("# #/##");
+               args[1] = formatArg; 
+               result = T.TEXT.evaluate(args, -1, (short)-1);
+               testResult = new StringEval("321 26/81");
+               assertEquals(testResult.toString(), result.toString());
+               
+               formatArg = new StringEval("#/##");
+               args[1] = formatArg; 
+               result = T.TEXT.evaluate(args, -1, (short)-1);
+               testResult = new StringEval("26027/81");
+               assertEquals(testResult.toString(), result.toString());
+       }
+       
+       public void testTextWithDateFormatSecondArg() {
+
+               ValueEval numArg = new NumberEval(321.321);
+               ValueEval formatArg = new StringEval("dd:MM:yyyy hh:mm:ss");
+               ValueEval[] args = { numArg, formatArg };
+               ValueEval result = T.TEXT.evaluate(args, -1, (short)-1);
+               ValueEval testResult = new StringEval("16:11:1900 07:42:14");
+               assertEquals(testResult.toString(), result.toString());
+               
+               formatArg = new StringEval("MMMM dd, yyyy");
+               args[1] = formatArg; 
+               result = T.TEXT.evaluate(args, -1, (short)-1);
+               testResult = new StringEval("November 16, 1900");
+               assertEquals(testResult.toString(), result.toString());
+       }
+       
+       
+}
diff --git a/src/testcases/org/apache/poi/hssf/record/formula/functions/TestTrunc.java b/src/testcases/org/apache/poi/hssf/record/formula/functions/TestTrunc.java
new file mode 100644 (file)
index 0000000..ef42d1c
--- /dev/null
@@ -0,0 +1,59 @@
+/* ====================================================================
+   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.ValueEval;
+import org.apache.poi.hssf.record.formula.eval.NumberEval;
+import org.apache.poi.hssf.record.formula.eval.StringEval;
+
+/**
+ * Test case for TRUNC()
+ *
+ * @author Stephen Wolke (smwolke at geistig.com)
+ */
+public final class TestTrunc extends AbstractNumericTestCase {
+       private static final NumericFunction F = null;
+       public void testTruncWithStringArg() {
+
+               ValueEval strArg = new StringEval("abc");
+               ValueEval[] args = { strArg, new NumberEval(2) };
+               ValueEval result = F.TRUNC.evaluate(args, -1, (short)-1);
+               assertEquals(ErrorEval.VALUE_INVALID, result);
+       }
+
+       public void testTruncWithWholeNumber() {
+               ValueEval[] args = { new NumberEval(200), new NumberEval(2) };
+               @SuppressWarnings("static-access")
+               ValueEval result = F.TRUNC.evaluate(args, -1, (short)-1);
+               assertEquals("TRUNC", (new NumberEval(200d)).getNumberValue(), ((NumberEval)result).getNumberValue());
+       }
+       
+       public void testTruncWithDecimalNumber() {
+               ValueEval[] args = { new NumberEval(2.612777), new NumberEval(3) };
+               @SuppressWarnings("static-access")
+               ValueEval result = F.TRUNC.evaluate(args, -1, (short)-1);
+               assertEquals("TRUNC", (new NumberEval(2.612d)).getNumberValue(), ((NumberEval)result).getNumberValue());
+       }
+       
+       public void testTruncWithDecimalNumberOneArg() {
+               ValueEval[] args = { new NumberEval(2.612777) };
+               ValueEval result = F.TRUNC.evaluate(args, -1, (short)-1);
+               assertEquals("TRUNC", (new NumberEval(2d)).getNumberValue(), ((NumberEval)result).getNumberValue());
+       }
+}