From: Yegor Kozlov Date: Sun, 2 Jun 2013 15:13:47 +0000 (+0000) Subject: Bugzilla 55036 - Dec2HEx formula support X-Git-Tag: 3.10-beta1~38 X-Git-Url: https://source.dussan.org/?a=commitdiff_plain;h=d1a6ade5826cf519acdd1ca2c9254074fb1e68ac;p=poi.git Bugzilla 55036 - Dec2HEx formula support git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1488729 13f79535-47bb-0310-9956-ffa450edef68 --- diff --git a/src/java/org/apache/poi/hssf/usermodel/HSSFRow.java b/src/java/org/apache/poi/hssf/usermodel/HSSFRow.java index 402397e542..8dfe8e7224 100644 --- a/src/java/org/apache/poi/hssf/usermodel/HSSFRow.java +++ b/src/java/org/apache/poi/hssf/usermodel/HSSFRow.java @@ -27,6 +27,7 @@ import org.apache.poi.ss.usermodel.Cell; import org.apache.poi.ss.usermodel.CellStyle; import org.apache.poi.ss.usermodel.Row; import org.apache.poi.ss.SpreadsheetVersion; +import org.apache.poi.util.Configurator; /** * 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 { // 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 HSSFCell[] cells; diff --git a/src/java/org/apache/poi/hssf/usermodel/HSSFSheet.java b/src/java/org/apache/poi/hssf/usermodel/HSSFSheet.java index 5e53c022ad..e0e3503edb 100644 --- a/src/java/org/apache/poi/hssf/usermodel/HSSFSheet.java +++ b/src/java/org/apache/poi/hssf/usermodel/HSSFSheet.java @@ -50,6 +50,7 @@ import org.apache.poi.ss.util.CellRangeAddress; import org.apache.poi.ss.util.CellReference; import org.apache.poi.ss.util.SSCellRange; import org.apache.poi.ss.util.SheetUtil; +import org.apache.poi.util.Configurator; import org.apache.poi.util.POILogFactory; 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 * 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 diff --git a/src/java/org/apache/poi/hssf/usermodel/HSSFWorkbook.java b/src/java/org/apache/poi/hssf/usermodel/HSSFWorkbook.java index 8f2a33f6b5..9d1873b3c3 100644 --- a/src/java/org/apache/poi/hssf/usermodel/HSSFWorkbook.java +++ b/src/java/org/apache/poi/hssf/usermodel/HSSFWorkbook.java @@ -29,6 +29,7 @@ import java.util.Iterator; import java.util.List; import java.util.regex.Pattern; +import org.apache.commons.codec.digest.DigestUtils; import org.apache.poi.POIDocument; import org.apache.poi.ddf.EscherBSERecord; 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.util.CellRangeAddress; import org.apache.poi.ss.util.WorkbookUtil; +import org.apache.poi.util.Configurator; import org.apache.poi.util.POILogFactory; import org.apache.poi.util.POILogger; -import org.apache.commons.codec.digest.DigestUtils; 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! */ - 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 diff --git a/src/java/org/apache/poi/ss/formula/functions/Dec2Hex.java b/src/java/org/apache/poi/ss/formula/functions/Dec2Hex.java new file mode 100644 index 0000000000..85d5df5e71 --- /dev/null +++ b/src/java/org/apache/poi/ss/formula/functions/Dec2Hex.java @@ -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.

+ *

+ * Syntax:
DEC2HEX (number,places )
+ *

+ * 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. + * + *

+ * + *

places

+ * + * The number of characters to use. The places argument is useful for padding the + * return value with leading 0s (zeros). + * + * + * + * @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))); + } +} \ No newline at end of file diff --git a/src/java/org/apache/poi/util/Configurator.java b/src/java/org/apache/poi/util/Configurator.java new file mode 100644 index 0000000000..7269030dd7 --- /dev/null +++ b/src/java/org/apache/poi/util/Configurator.java @@ -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; + } + + +} diff --git a/src/testcases/org/apache/poi/ss/formula/functions/BaseTestFunctionsFromSpreadsheet.java b/src/testcases/org/apache/poi/ss/formula/functions/BaseTestFunctionsFromSpreadsheet.java index 920593c83f..6993e15f0f 100644 --- a/src/testcases/org/apache/poi/ss/formula/functions/BaseTestFunctionsFromSpreadsheet.java +++ b/src/testcases/org/apache/poi/ss/formula/functions/BaseTestFunctionsFromSpreadsheet.java @@ -226,7 +226,7 @@ public abstract class BaseTestFunctionsFromSpreadsheet extends TestCase { HSSFCell expectedValueCell = r.getCell(SS.COLUMN_INDEX_EXPECTED_RESULT); 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 { confirmExpectedResult(msgPrefix, expectedValueCell, actualValue); _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) { StringBuffer sb = new StringBuffer(); + + sb.append("In ").append(filename).append(" "); + CellReference cr = new CellReference(sheetName, rowIndex, c.getColumnIndex(), false, false); sb.append(cr.formatAsString()); sb.append(" {=").append(c.getCellFormula()).append("}"); diff --git a/src/testcases/org/apache/poi/ss/formula/functions/TestDec2Hex.java b/src/testcases/org/apache/poi/ss/formula/functions/TestDec2Hex.java new file mode 100644 index 0000000000..8ff090cbe0 --- /dev/null +++ b/src/testcases/org/apache/poi/ss/formula/functions/TestDec2Hex.java @@ -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); + } +}