<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>
retval[37] = BooleanFunction.OR;
retval[38] = BooleanFunction.NOT;
retval[39] = NumericFunction.MOD;
+ retval[48] = TextFunction.TEXT;
retval[56] = FinanceFunction.PV;
retval[57] = FinanceFunction.FV;
retval[184] = NumericFunction.FACT;
retval[190] = LogicalFunction.ISNONTEXT;
-
+ retval[197] = NumericFunction.TRUNC;
retval[198] = LogicalFunction.ISLOGICAL;
retval[212] = NumericFunction.ROUNDUP;
/**
* @author Amol S. Deshmukh < amolweb at ya hoo dot com >
* @author Josh Micich
+ * @author Stephen Wolke (smwolke at geistig.com)
*/
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);
+ }
+ };
/* -------------------------------------------------------------------------- */
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;
/**
* @author Amol S. Deshmukh < amolweb at ya hoo dot com >
* @author Josh Micich
+ * @author Stephen Wolke (smwolke at geistig.com)
*/
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 {
}
};
+ /**
+ * 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;
--- /dev/null
+/* ====================================================================
+ 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());
+ }
+
+
+}
--- /dev/null
+/* ====================================================================
+ 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());
+ }
+}