Author: cedricwalter
Date: Mon Nov  4 21:09:40 2013
New Revision: 1538765

URL: http://svn.apache.org/r1538765
Log:
Bug 55742: Apply patch for Oct2Dec and refactor Hex2Dec to also use 
BaseNumberUtils.convertToDecimal

Added:
    poi/trunk/src/java/org/apache/poi/ss/formula/functions/BaseNumberUtils.java
    poi/trunk/src/java/org/apache/poi/ss/formula/functions/Oct2Dec.java
    poi/trunk/src/testcases/org/apache/poi/ss/formula/functions/TestHex2Dec.java
    poi/trunk/src/testcases/org/apache/poi/ss/formula/functions/TestOct2Dec.java
      - copied, changed from r1531506, 
poi/trunk/src/testcases/org/apache/poi/ss/formula/functions/TestDec2Hex.java
Modified:
    poi/trunk/src/java/org/apache/poi/ss/formula/atp/AnalysisToolPak.java
    poi/trunk/src/java/org/apache/poi/ss/formula/functions/Hex2Dec.java
    poi/trunk/test-data/spreadsheet/FormulaEvalTestData.xls

Modified: poi/trunk/src/java/org/apache/poi/ss/formula/atp/AnalysisToolPak.java
URL: 
http://svn.apache.org/viewvc/poi/trunk/src/java/org/apache/poi/ss/formula/atp/AnalysisToolPak.java?rev=1538765&r1=1538764&r2=1538765&view=diff
==============================================================================
--- poi/trunk/src/java/org/apache/poi/ss/formula/atp/AnalysisToolPak.java 
(original)
+++ poi/trunk/src/java/org/apache/poi/ss/formula/atp/AnalysisToolPak.java Mon 
Nov  4 21:09:40 2013
@@ -136,7 +136,7 @@ public final class AnalysisToolPak imple
         r(m, "MULTINOMIAL", null);
         r(m, "NETWORKDAYS", NetworkdaysFunction.instance);
         r(m, "NOMINAL", null);
-        r(m, "OCT2BIN", null);
+        r(m, "OCT2BIN", Oct2Dec.instance);
         r(m, "OCT2DEC", null);
         r(m, "OCT2HEX", null);
         r(m, "ODDFPRICE", null);

Added: 
poi/trunk/src/java/org/apache/poi/ss/formula/functions/BaseNumberUtils.java
URL: 
http://svn.apache.org/viewvc/poi/trunk/src/java/org/apache/poi/ss/formula/functions/BaseNumberUtils.java?rev=1538765&view=auto
==============================================================================
--- poi/trunk/src/java/org/apache/poi/ss/formula/functions/BaseNumberUtils.java 
(added)
+++ poi/trunk/src/java/org/apache/poi/ss/formula/functions/BaseNumberUtils.java 
Mon Nov  4 21:09:40 2013
@@ -0,0 +1,78 @@
+/* ====================================================================
+   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;
+
+/**
+ * <p>Some utils for converting from and to any base<p/>
+ *
+ * @author cedric dot walter @ gmail dot com
+ */
+public class BaseNumberUtils {
+
+
+    public static double convertToDecimal(String value, int base, int 
maxNumberOfPlaces) throws IllegalArgumentException {
+        if (value.isEmpty()) {
+            return 0.0;
+        }
+
+        long stringLength = value.length();
+        if (stringLength > maxNumberOfPlaces) {
+            throw new IllegalArgumentException();
+        }
+
+        double decimalValue = 0.0;
+
+        long signedDigit = 0;
+        boolean hasSignedDigit = true;
+        char[] characters = value.toCharArray();
+        for (char character : characters) {
+            long digit;
+
+            if ('0' <= character && character <= '9') {
+                digit = character - '0';
+            } else if ('A' <= character && character <= 'Z') {
+                digit = 10 + (character - 'A');
+            } else if ('a' <= character && character <= 'z') {
+                digit = 10 + (character - 'a');
+            } else {
+                digit = base;
+            }
+
+            if (digit < base) {
+                if (hasSignedDigit) {
+                    hasSignedDigit = false;
+                    signedDigit = digit;
+                }
+                decimalValue = decimalValue * base + digit;
+            } else {
+                throw new IllegalArgumentException("character not allowed");
+            }
+        }
+
+        boolean isNegative = (!hasSignedDigit && stringLength == 
maxNumberOfPlaces  && (signedDigit >= base / 2));
+        if (isNegative) {
+            decimalValue = getTwoComplement(base, maxNumberOfPlaces, 
decimalValue);
+            decimalValue = decimalValue * -1.0;
+        }
+
+        return decimalValue;
+    }
+
+    private static double getTwoComplement(double base, double 
maxNumberOfPlaces, double decimalValue) {
+        return (Math.pow(base, maxNumberOfPlaces) - decimalValue);
+    }
+}

Modified: poi/trunk/src/java/org/apache/poi/ss/formula/functions/Hex2Dec.java
URL: 
http://svn.apache.org/viewvc/poi/trunk/src/java/org/apache/poi/ss/formula/functions/Hex2Dec.java?rev=1538765&r1=1538764&r2=1538765&view=diff
==============================================================================
--- poi/trunk/src/java/org/apache/poi/ss/formula/functions/Hex2Dec.java 
(original)
+++ poi/trunk/src/java/org/apache/poi/ss/formula/functions/Hex2Dec.java Mon Nov 
 4 21:09:40 2013
@@ -20,8 +20,6 @@ package org.apache.poi.ss.formula.functi
 import org.apache.poi.ss.formula.OperationEvaluationContext;
 import org.apache.poi.ss.formula.eval.*;
 
-import java.math.BigInteger;
-
 /**
  * Implementation for Excel HEX2DEC() function.<p/>
  * <p/>
@@ -41,53 +39,16 @@ public class Hex2Dec extends Fixed1ArgFu
 
     public static final FreeRefFunction instance = new Hex2Dec();
 
+    static final int HEXADECIMAL_BASE = 16;
+    static final int MAX_NUMBER_OF_PLACES = 10;
+
     public ValueEval evaluate(int srcRowIndex, int srcColumnIndex, ValueEval 
numberVE) {
-        String number = OperandResolver.coerceValueToString(numberVE);
-        if (number.length() > 10) {
+        String hex = OperandResolver.coerceValueToString(numberVE);
+        try {
+            return new NumberEval(BaseNumberUtils.convertToDecimal(hex, 
HEXADECIMAL_BASE, MAX_NUMBER_OF_PLACES));
+        }  catch (IllegalArgumentException e) {
             return ErrorEval.NUM_ERROR;
         }
-
-        String unsigned;
-        boolean isPositive = false;
-        boolean isNegative = false;
-        if (number.length() < 10) {
-            unsigned = number;
-            isPositive = true;
-        } else {
-            //remove sign bit
-            unsigned = number.substring(1);
-            isNegative =
-                    number.startsWith("8") || number.startsWith("9") ||
-                            number.startsWith("A") || number.startsWith("B") ||
-                            number.startsWith("C") || number.startsWith("D") ||
-                            number.startsWith("E") || number.startsWith("F");
-        }
-
-        long decimal;
-        if (isPositive) {
-            try {
-                decimal = Integer.parseInt(unsigned, 16);
-            } catch (NumberFormatException ee) {
-                // number is not a valid hexadecimal number
-                return ErrorEval.NUM_ERROR;
-            }
-        } else {
-            if (isNegative) {
-                BigInteger temp = new BigInteger(unsigned, 16);
-                BigInteger subtract = 
BigInteger.ONE.shiftLeft(unsigned.length() * 4);
-                temp = temp.subtract(subtract);
-                decimal = temp.longValue();
-            } else {
-                try {
-                    decimal = Integer.parseInt(unsigned, 16);
-                } catch (NumberFormatException ee) {
-                    // number is not a valid hexadecimal number
-                    return ErrorEval.NUM_ERROR;
-                }
-            }
-        }
-
-        return new NumberEval(decimal);
     }
 
     public ValueEval evaluate(ValueEval[] args, OperationEvaluationContext ec) 
{

Added: poi/trunk/src/java/org/apache/poi/ss/formula/functions/Oct2Dec.java
URL: 
http://svn.apache.org/viewvc/poi/trunk/src/java/org/apache/poi/ss/formula/functions/Oct2Dec.java?rev=1538765&view=auto
==============================================================================
--- poi/trunk/src/java/org/apache/poi/ss/formula/functions/Oct2Dec.java (added)
+++ poi/trunk/src/java/org/apache/poi/ss/formula/functions/Oct2Dec.java Mon Nov 
 4 21:09:40 2013
@@ -0,0 +1,64 @@
+/* ====================================================================
+   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.OperationEvaluationContext;
+import org.apache.poi.ss.formula.eval.ErrorEval;
+import org.apache.poi.ss.formula.eval.NumberEval;
+import org.apache.poi.ss.formula.eval.OperandResolver;
+import org.apache.poi.ss.formula.eval.ValueEval;
+
+/**
+ * <p>Implementation for Excel Oct2Dec() function.<p/>
+ * <p>
+ * Converts an octal number to decimal.
+ * </p>
+ * <p>
+ * <b>Syntax</b>:<br/> <b>Oct2Dec  </b>(<b>number</b> )
+ * </p>
+ * <p/>
+ * Number     is the octal number you want to convert. Number may not contain 
more than 10 octal characters (30 bits).
+ * The most significant bit of number is the sign bit. The remaining 29 bits 
are magnitude bits.
+ * Negative numbers are represented using two's-complement notation..
+ * <p/>
+ * If number is not a valid octal number, OCT2DEC returns the #NUM! error 
value.
+ *
+ * @author cedric dot walter @ gmail dot com
+ */
+public class Oct2Dec extends Fixed1ArgFunction implements FreeRefFunction {
+
+    public static final FreeRefFunction instance = new Oct2Dec();
+
+    static final int MAX_NUMBER_OF_PLACES = 10;
+    static final int OCTAL_BASE = 8;
+
+    public ValueEval evaluate(int srcRowIndex, int srcColumnIndex, ValueEval 
numberVE) {
+        String octal = OperandResolver.coerceValueToString(numberVE);
+        try {
+           return new NumberEval(BaseNumberUtils.convertToDecimal(octal, 
OCTAL_BASE, MAX_NUMBER_OF_PLACES));
+        }  catch (IllegalArgumentException e) {
+            return ErrorEval.NUM_ERROR;
+        }
+    }
+
+    public ValueEval evaluate(ValueEval[] args, OperationEvaluationContext ec) 
{
+        if (args.length != 1) {
+            return ErrorEval.VALUE_INVALID;
+        }
+        return evaluate(ec.getRowIndex(), ec.getColumnIndex(), args[0]);
+    }
+}

Added: 
poi/trunk/src/testcases/org/apache/poi/ss/formula/functions/TestHex2Dec.java
URL: 
http://svn.apache.org/viewvc/poi/trunk/src/testcases/org/apache/poi/ss/formula/functions/TestHex2Dec.java?rev=1538765&view=auto
==============================================================================
--- 
poi/trunk/src/testcases/org/apache/poi/ss/formula/functions/TestHex2Dec.java 
(added)
+++ 
poi/trunk/src/testcases/org/apache/poi/ss/formula/functions/TestHex2Dec.java 
Mon Nov  4 21:09:40 2013
@@ -0,0 +1,60 @@
+/* ====================================================================
+   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.NumberEval;
+import org.apache.poi.ss.formula.eval.StringEval;
+import org.apache.poi.ss.formula.eval.ValueEval;
+
+/**
+ * Tests for {@link Hex2Dec}
+ *
+ * @author cedric dot walter @ gmail dot com
+ */
+public final class TestHex2Dec extends TestCase {
+
+    private static ValueEval invokeValue(String number1) {
+               ValueEval[] args = new ValueEval[] { new StringEval(number1) };
+               return new Hex2Dec().evaluate(args, -1, -1);
+       }
+
+    private static void confirmValue(String msg, String number1, String 
expected) {
+               ValueEval result = invokeValue(number1);
+               assertEquals(NumberEval.class, result.getClass());
+               assertEquals(msg, expected, ((NumberEval) 
result).getStringValue());
+       }
+
+    private static void confirmValueError(String msg, String number1, 
ErrorEval numError) {
+        ValueEval result = invokeValue(number1);
+        assertEquals(ErrorEval.class, result.getClass());
+        assertEquals(msg, numError, result);
+    }
+
+       public void testBasic() {
+               confirmValue("Converts octal 'A5' to decimal (165)", "A5", 
"165");
+               confirmValue("Converts octal FFFFFFFF5B to decimal (-165)", 
"FFFFFFFF5B", "-165");
+               confirmValue("Converts octal 3DA408B9 to decimal (-165)", 
"3DA408B9", "1034160313");
+       }
+
+    public void testErrors() {
+        confirmValueError("not a valid octal number","GGGGGGG", 
ErrorEval.NUM_ERROR);
+        confirmValueError("not a valid octal number","3.14159", 
ErrorEval.NUM_ERROR);
+    }
+}

Copied: 
poi/trunk/src/testcases/org/apache/poi/ss/formula/functions/TestOct2Dec.java 
(from r1531506, 
poi/trunk/src/testcases/org/apache/poi/ss/formula/functions/TestDec2Hex.java)
URL: 
http://svn.apache.org/viewvc/poi/trunk/src/testcases/org/apache/poi/ss/formula/functions/TestOct2Dec.java?p2=poi/trunk/src/testcases/org/apache/poi/ss/formula/functions/TestOct2Dec.java&p1=poi/trunk/src/testcases/org/apache/poi/ss/formula/functions/TestDec2Hex.java&r1=1531506&r2=1538765&rev=1538765&view=diff
==============================================================================
--- 
poi/trunk/src/testcases/org/apache/poi/ss/formula/functions/TestDec2Hex.java 
(original)
+++ 
poi/trunk/src/testcases/org/apache/poi/ss/formula/functions/TestOct2Dec.java 
Mon Nov  4 21:09:40 2013
@@ -19,60 +19,45 @@ package org.apache.poi.ss.formula.functi
 
 import junit.framework.TestCase;
 import org.apache.poi.ss.formula.eval.ErrorEval;
+import org.apache.poi.ss.formula.eval.NumberEval;
 import org.apache.poi.ss.formula.eval.StringEval;
 import org.apache.poi.ss.formula.eval.ValueEval;
 
 /**
- * Tests for {@link Dec2Hex}
+ * Tests for {@link org.apache.poi.ss.formula.functions.Oct2Dec}
  *
  * @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);
-       }
+public final class TestOct2Dec extends TestCase {
 
     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());
+               ValueEval[] args = new ValueEval[] { new StringEval(number1) };
+               return new Oct2Dec().evaluate(args, -1, -1);
        }
 
     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());
+               assertEquals(NumberEval.class, result.getClass());
+               assertEquals(msg, expected, ((NumberEval) 
result).getStringValue());
        }
 
-    private static void confirmValueError(String msg, String number1, String 
number2, ErrorEval numError) {
-        ValueEval result = invokeValue(number1, number2);
+    private static void confirmValueError(String msg, String number1, 
ErrorEval numError) {
+        ValueEval result = invokeValue(number1);
         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, 2 is 
ignored","-54", "2",  "FFFFFFFFCA");
-               confirmValue("places is optionnal","-54", "FFFFFFFFCA");
+               confirmValue("Converts octal '' to decimal (0)", "", "0");
+               confirmValue("Converts octal 54 to decimal (44)", "54", "44");
+               confirmValue("Converts octal 7777777533 to decimal (-165)", 
"7777777533", "-165");
+               confirmValue("Converts octal 7000000000 to decimal 
(-134217728)", "7000000000", "-134217728");
+               confirmValue("Converts octal 7776667533 to decimal (-299173)", 
"7776667533", "-299173");
        }
 
     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);
+        confirmValueError("not a valid octal number","ABCDEFGH", 
ErrorEval.NUM_ERROR);
+        confirmValueError("not a valid octal number","99999999", 
ErrorEval.NUM_ERROR);
+        confirmValueError("not a valid octal number","3.14159", 
ErrorEval.NUM_ERROR);
     }
 }

Modified: poi/trunk/test-data/spreadsheet/FormulaEvalTestData.xls
URL: 
http://svn.apache.org/viewvc/poi/trunk/test-data/spreadsheet/FormulaEvalTestData.xls?rev=1538765&r1=1538764&r2=1538765&view=diff
==============================================================================
Binary files - no diff available.



---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to