git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@937652 13f79535-47bb-0310-9956-ffa450edef68tags/REL_3_7_BETA1
@@ -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 { | |||
@@ -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; |
@@ -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()); | |||
} | |||
} |