Added implementation for TEXT() and TRUNC(), see Bugzilla 49025 and 49026
git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@937652 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
parent
2187e24118
commit
927e153c94
@ -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>
|
||||
|
@ -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;
|
||||
|
@ -26,6 +26,7 @@ import org.apache.poi.hssf.record.formula.eval.ValueEval;
|
||||
/**
|
||||
* @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 {
|
||||
|
||||
@ -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);
|
||||
}
|
||||
};
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
|
||||
|
@ -17,6 +17,12 @@
|
||||
|
||||
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 < amolweb at ya hoo dot com >
|
||||
* @author Josh Micich
|
||||
* @author Stephen Wolke (smwolke at geistig.com)
|
||||
*/
|
||||
public abstract class TextFunction implements Function {
|
||||
|
||||
@ -42,6 +49,11 @@ public abstract class TextFunction implements Function {
|
||||
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 {
|
||||
return evaluateFunc(args, srcCellRow, srcCellCol);
|
||||
@ -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;
|
||||
|
@ -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());
|
||||
}
|
||||
|
||||
|
||||
}
|
@ -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());
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user