Bug 55742: Apply patch for Oct2Dec and refactor Hex2Dec to also use BaseNumberUtils.convertToDecimal
git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1538765 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
parent
7014abae83
commit
6cb3b01753
@ -136,7 +136,7 @@ public final class AnalysisToolPak implements UDFFinder {
|
||||
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);
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
@ -20,8 +20,6 @@ package org.apache.poi.ss.formula.functions;
|
||||
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 Fixed1ArgFunction implements FreeRefFunction {
|
||||
|
||||
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) {
|
||||
|
64
src/java/org/apache/poi/ss/formula/functions/Oct2Dec.java
Normal file
64
src/java/org/apache/poi/ss/formula/functions/Oct2Dec.java
Normal file
@ -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]);
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
@ -0,0 +1,63 @@
|
||||
/* ====================================================================
|
||||
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 org.apache.poi.ss.formula.functions.Oct2Dec}
|
||||
*
|
||||
* @author cedric dot walter @ gmail dot com
|
||||
*/
|
||||
public final class TestOct2Dec extends TestCase {
|
||||
|
||||
private static ValueEval invokeValue(String number1) {
|
||||
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(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 '' 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("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);
|
||||
}
|
||||
}
|
Binary file not shown.
Loading…
Reference in New Issue
Block a user