mirror of https://github.com/apache/poi.git
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
16f576c335
commit
f97310c1f6
|
@ -136,7 +136,7 @@ public final class AnalysisToolPak implements UDFFinder {
|
||||||
r(m, "MULTINOMIAL", null);
|
r(m, "MULTINOMIAL", null);
|
||||||
r(m, "NETWORKDAYS", NetworkdaysFunction.instance);
|
r(m, "NETWORKDAYS", NetworkdaysFunction.instance);
|
||||||
r(m, "NOMINAL", null);
|
r(m, "NOMINAL", null);
|
||||||
r(m, "OCT2BIN", null);
|
r(m, "OCT2BIN", Oct2Dec.instance);
|
||||||
r(m, "OCT2DEC", null);
|
r(m, "OCT2DEC", null);
|
||||||
r(m, "OCT2HEX", null);
|
r(m, "OCT2HEX", null);
|
||||||
r(m, "ODDFPRICE", 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.OperationEvaluationContext;
|
||||||
import org.apache.poi.ss.formula.eval.*;
|
import org.apache.poi.ss.formula.eval.*;
|
||||||
|
|
||||||
import java.math.BigInteger;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Implementation for Excel HEX2DEC() function.<p/>
|
* Implementation for Excel HEX2DEC() function.<p/>
|
||||||
* <p/>
|
* <p/>
|
||||||
|
@ -41,53 +39,16 @@ public class Hex2Dec extends Fixed1ArgFunction implements FreeRefFunction {
|
||||||
|
|
||||||
public static final FreeRefFunction instance = new Hex2Dec();
|
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) {
|
public ValueEval evaluate(int srcRowIndex, int srcColumnIndex, ValueEval numberVE) {
|
||||||
String number = OperandResolver.coerceValueToString(numberVE);
|
String hex = OperandResolver.coerceValueToString(numberVE);
|
||||||
if (number.length() > 10) {
|
try {
|
||||||
|
return new NumberEval(BaseNumberUtils.convertToDecimal(hex, HEXADECIMAL_BASE, MAX_NUMBER_OF_PLACES));
|
||||||
|
} catch (IllegalArgumentException e) {
|
||||||
return ErrorEval.NUM_ERROR;
|
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) {
|
public ValueEval evaluate(ValueEval[] args, OperationEvaluationContext ec) {
|
||||||
|
|
|
@ -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