Bugzilla 55036 - Dec2HEx formula support

git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1488729 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Yegor Kozlov 2013-06-02 15:13:47 +00:00
parent c225df5b71
commit d1a6ade582
7 changed files with 253 additions and 6 deletions

View File

@ -27,6 +27,7 @@ import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.CellStyle; import org.apache.poi.ss.usermodel.CellStyle;
import org.apache.poi.ss.usermodel.Row; import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.SpreadsheetVersion; import org.apache.poi.ss.SpreadsheetVersion;
import org.apache.poi.util.Configurator;
/** /**
* High level representation of a row of a spreadsheet. * High level representation of a row of a spreadsheet.
@ -39,7 +40,7 @@ import org.apache.poi.ss.SpreadsheetVersion;
public final class HSSFRow implements Row { public final class HSSFRow implements Row {
// used for collections // used for collections
public final static int INITIAL_CAPACITY = 5; public final static int INITIAL_CAPACITY = Configurator.getIntValue("HSSFRow.ColInitialCapacity", 5);
private int rowNum; private int rowNum;
private HSSFCell[] cells; private HSSFCell[] cells;

View File

@ -50,6 +50,7 @@ import org.apache.poi.ss.util.CellRangeAddress;
import org.apache.poi.ss.util.CellReference; import org.apache.poi.ss.util.CellReference;
import org.apache.poi.ss.util.SSCellRange; import org.apache.poi.ss.util.SSCellRange;
import org.apache.poi.ss.util.SheetUtil; import org.apache.poi.ss.util.SheetUtil;
import org.apache.poi.util.Configurator;
import org.apache.poi.util.POILogFactory; import org.apache.poi.util.POILogFactory;
import org.apache.poi.util.POILogger; import org.apache.poi.util.POILogger;
@ -74,7 +75,7 @@ public final class HSSFSheet implements org.apache.poi.ss.usermodel.Sheet {
* rows. It is currently set to 20. If you generate larger sheets you may benefit * rows. It is currently set to 20. If you generate larger sheets you may benefit
* by setting this to a higher number and recompiling a custom edition of HSSFSheet. * by setting this to a higher number and recompiling a custom edition of HSSFSheet.
*/ */
public final static int INITIAL_CAPACITY = 20; public final static int INITIAL_CAPACITY = Configurator.getIntValue("HSSFSheet.RowInitialCapacity", 20);
/** /**
* reference to the low level {@link InternalSheet} object * reference to the low level {@link InternalSheet} object

View File

@ -29,6 +29,7 @@ import java.util.Iterator;
import java.util.List; import java.util.List;
import java.util.regex.Pattern; import java.util.regex.Pattern;
import org.apache.commons.codec.digest.DigestUtils;
import org.apache.poi.POIDocument; import org.apache.poi.POIDocument;
import org.apache.poi.ddf.EscherBSERecord; import org.apache.poi.ddf.EscherBSERecord;
import org.apache.poi.ddf.EscherBitmapBlip; import org.apache.poi.ddf.EscherBitmapBlip;
@ -54,9 +55,9 @@ import org.apache.poi.ss.formula.udf.UDFFinder;
import org.apache.poi.ss.usermodel.Row.MissingCellPolicy; import org.apache.poi.ss.usermodel.Row.MissingCellPolicy;
import org.apache.poi.ss.util.CellRangeAddress; import org.apache.poi.ss.util.CellRangeAddress;
import org.apache.poi.ss.util.WorkbookUtil; import org.apache.poi.ss.util.WorkbookUtil;
import org.apache.poi.util.Configurator;
import org.apache.poi.util.POILogFactory; import org.apache.poi.util.POILogFactory;
import org.apache.poi.util.POILogger; import org.apache.poi.util.POILogger;
import org.apache.commons.codec.digest.DigestUtils;
import org.apache.poi.ss.formula.udf.IndexedUDFFinder; import org.apache.poi.ss.formula.udf.IndexedUDFFinder;
@ -93,7 +94,7 @@ public final class HSSFWorkbook extends POIDocument implements org.apache.poi.ss
* since you're never allowed to have more or less than three sheets! * since you're never allowed to have more or less than three sheets!
*/ */
public final static int INITIAL_CAPACITY = 3; public final static int INITIAL_CAPACITY = Configurator.getIntValue("HSSFWorkbook.SheetInitialCapacity",3);
/** /**
* this is the reference to the low level Workbook object * this is the reference to the low level Workbook object

View File

@ -0,0 +1,122 @@
/* ====================================================================
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.ss.formula.functions;
import org.apache.poi.ss.formula.eval.*;
/**
* Implementation for Excel DELTA() function.<p/>
* <p/>
* <b>Syntax</b>:<br/> <b>DEC2HEX </b>(<b>number</b>,<b>places</b> )<br/>
* <p/>
* Converts a decimal number to hexadecimal.
*
* The decimal integer you want to convert. If number is negative, places is ignored
* and this function returns a 10-character (40-bit) hexadecimal number in which the
* most significant bit is the sign bit. The remaining 39 bits are magnitude bits.
* Negative numbers are represented using two's-complement notation.
*
* <ul>
* <li>If number < -549,755,813,888 or if number > 549,755,813,887, this function returns the #NUM! error value.</li>
* <li>If number is nonnumeric, this function returns the #VALUE! error value.</li>
* </ul>
*
* <h2>places</h2>
*
* The number of characters to use. The places argument is useful for padding the
* return value with leading 0s (zeros).
*
* <ul>
* <li>If this argument is omitted, this function uses the minimum number of characters necessary.</li>
* <li>If this function requires more than places characters, it returns the #NUM! error value.</li>
* <li>If this argument is nonnumeric, this function returns the #VALUE! error value.</li>
* <li>If this argument is negative, this function returns the #NUM! error value.</li>
* <li>If this argument contains a decimal value, this function ignores the numbers to the right side of the decimal point.</li>
* </ul>
*
* @author cedric dot walter @ gmail dot com
*/
public final class Dec2Hex extends Var1or2ArgFunction {
private final static long MIN_VALUE = new Long("-549755813888").longValue();
private final static long MAX_VALUE = new Long("549755813887").longValue();
private final static int DEFAULT_PLACES_VALUE = 10;;
@Override
public ValueEval evaluate(int srcRowIndex, int srcColumnIndex, ValueEval number, ValueEval places) {
ValueEval veText1;
try {
veText1 = OperandResolver.getSingleValue(number, srcRowIndex, srcColumnIndex);
} catch (EvaluationException e) {
return e.getErrorEval();
}
String strText1 = OperandResolver.coerceValueToString(veText1);
Double number1 = OperandResolver.parseDouble(strText1);
//If this number argument is non numeric, this function returns the #VALUE! error value.
if (number1 == null) {
return ErrorEval.VALUE_INVALID;
}
//If number < -549,755,813,888 or if number > 549,755,813,887, this function returns the #NUM! error value.
if (number1.longValue() < MIN_VALUE || number1.longValue() > MAX_VALUE) {
return ErrorEval.NUM_ERROR;
}
int placesNumber = 0;
if (number1 < 0) {
placesNumber = DEFAULT_PLACES_VALUE;
} else {
ValueEval placesValueEval;
try {
placesValueEval = OperandResolver.getSingleValue(places, srcRowIndex, srcColumnIndex);
} catch (EvaluationException e) {
return e.getErrorEval();
}
String placesStr = OperandResolver.coerceValueToString(placesValueEval);
Double placesNumberDouble = OperandResolver.parseDouble(placesStr);
//non numeric value
if (placesNumberDouble == null) {
return ErrorEval.VALUE_INVALID;
}
//If this argument contains a decimal value, this function ignores the numbers to the right side of the decimal point.
placesNumber = placesNumberDouble.intValue();
if (placesNumber < 0) {
return ErrorEval.NUM_ERROR;
}
}
String hex = "";
if (placesNumber != 0) {
hex = String.format("%0"+placesNumber+"X", number1.intValue() & 0x0FFFFF);
}
else {
hex = Integer.toHexString(number1.intValue());
}
return new StringEval(hex.toUpperCase());
}
@Override
public ValueEval evaluate(int srcRowIndex, int srcColumnIndex, ValueEval arg0) {
return this.evaluate(srcRowIndex, srcColumnIndex, arg0, new StringEval(String.valueOf(DEFAULT_PLACES_VALUE)));
}
}

View File

@ -0,0 +1,40 @@
package org.apache.poi.util;
/* ====================================================================
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.
==================================================================== */
/**
*
* @author Cedric Walter (cedric.walter at innoveo.com)
*/
public class Configurator {
private static POILogger logger = POILogFactory.getLogger(Configurator.class);
public static int getIntValue(String systemProperty, int defaultValue) {
int result = defaultValue;
String property = System.getProperty(systemProperty);
try {
result = Integer.valueOf(property);
} catch (Exception e) {
logger.log(POILogger.ERROR, "System property -D"+systemProperty +" do not contains a valid integer " + property);
}
return result;
}
}

View File

@ -226,7 +226,7 @@ public abstract class BaseTestFunctionsFromSpreadsheet extends TestCase {
HSSFCell expectedValueCell = r.getCell(SS.COLUMN_INDEX_EXPECTED_RESULT); HSSFCell expectedValueCell = r.getCell(SS.COLUMN_INDEX_EXPECTED_RESULT);
String rowComment = getRowCommentColumnValue(r); String rowComment = getRowCommentColumnValue(r);
String msgPrefix = formatTestCaseDetails(sheetName, r.getRowNum(), c, currentGroupComment, rowComment); String msgPrefix = formatTestCaseDetails(this.getFilename(),sheetName, r.getRowNum(), c, currentGroupComment, rowComment);
try { try {
confirmExpectedResult(msgPrefix, expectedValueCell, actualValue); confirmExpectedResult(msgPrefix, expectedValueCell, actualValue);
_evaluationSuccessCount ++; _evaluationSuccessCount ++;
@ -250,10 +250,13 @@ public abstract class BaseTestFunctionsFromSpreadsheet extends TestCase {
} }
private static String formatTestCaseDetails(String sheetName, int rowIndex, HSSFCell c, String currentGroupComment, private static String formatTestCaseDetails(String filename, String sheetName, int rowIndex, HSSFCell c, String currentGroupComment,
String rowComment) { String rowComment) {
StringBuffer sb = new StringBuffer(); StringBuffer sb = new StringBuffer();
sb.append("In ").append(filename).append(" ");
CellReference cr = new CellReference(sheetName, rowIndex, c.getColumnIndex(), false, false); CellReference cr = new CellReference(sheetName, rowIndex, c.getColumnIndex(), false, false);
sb.append(cr.formatAsString()); sb.append(cr.formatAsString());
sb.append(" {=").append(c.getCellFormula()).append("}"); sb.append(" {=").append(c.getCellFormula()).append("}");

View File

@ -0,0 +1,79 @@
/* ====================================================================
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.ss.formula.functions;
import junit.framework.TestCase;
import org.apache.poi.ss.formula.eval.ErrorEval;
import org.apache.poi.ss.formula.eval.StringEval;
import org.apache.poi.ss.formula.eval.ValueEval;
/**
* Tests for {@link Dec2Hex}
*
* @author cedric dot walter @ gmail dot com
*/
public final class TestDec2Hex extends TestCase {
private static ValueEval invokeValue(String number1, String number2) {
ValueEval[] args = new ValueEval[] { new StringEval(number1), new StringEval(number2), };
return new Dec2Hex().evaluate(args, -1, -1);
}
private static ValueEval invokeValue(String number1) {
ValueEval[] args = new ValueEval[] { new StringEval(number1), };
return new Dec2Hex().evaluate(args, -1, -1);
}
private static void confirmValue(String msg, String number1, String number2, String expected) {
ValueEval result = invokeValue(number1, number2);
assertEquals(StringEval.class, result.getClass());
assertEquals(msg, expected, ((StringEval) result).getStringValue());
}
private static void confirmValue(String msg, String number1, String expected) {
ValueEval result = invokeValue(number1);
assertEquals(StringEval.class, result.getClass());
assertEquals(msg, expected, ((StringEval) result).getStringValue());
}
private static void confirmValueError(String msg, String number1, String number2, ErrorEval numError) {
ValueEval result = invokeValue(number1, number2);
assertEquals(ErrorEval.class, result.getClass());
assertEquals(msg, numError, result);
}
public void testBasic() {
confirmValue("Converts decimal 100 to hexadecimal with 0 characters (64)", "100","0", "64");
confirmValue("Converts decimal 100 to hexadecimal with 4 characters (0064)", "100","4", "0064");
confirmValue("Converts decimal 100 to hexadecimal with 5 characters (0064)", "100","5", "00064");
confirmValue("Converts decimal 100 to hexadecimal with 10 (default) characters", "100","10", "0000000064");
confirmValue("If argument places contains a decimal value, dec2hex ignores the numbers to the right side of the decimal point.", "100","10.0", "0000000064");
confirmValue("Converts decimal -54 to hexadecimal, 0 is ignored", "-54", "0", "00000FFFCA");
confirmValue("Converts decimal -54 to hexadecimal, 2 is ignored","-54", "2", "00000FFFCA");
confirmValue("places is optionnal","-54", "00000FFFCA");
}
public void testErrors() {
confirmValueError("Out of range min number","-549755813889","0", ErrorEval.NUM_ERROR);
confirmValueError("Out of range max number","549755813888","0", ErrorEval.NUM_ERROR);
confirmValueError("negative places not allowed","549755813888","-10", ErrorEval.NUM_ERROR);
confirmValueError("non number places not allowed","ABCDEF","0", ErrorEval.VALUE_INVALID);
}
}