From 9aba9d81bea33eee75a7390bc4371fe49774e1e9 Mon Sep 17 00:00:00 2001 From: Ryan Kurtz Date: Tue, 1 Oct 2024 13:43:42 -0400 Subject: [PATCH] GP-4955: The ElfLoader imagebase option can now contain a leading 0x --- ...ReferencesToFieldByNameOrOffsetAction.java | 17 +- .../java/ghidra/app/script/AskDialog.java | 6 +- .../util/opinion/ElfLoaderOptionsFactory.java | 8 +- .../util/bin/format/pdb/DefaultPdbMember.java | 8 +- .../dialogs/NumberRangeInputDialog.java | 15 +- .../textfield/GValidatedTextField.java | 54 +++- .../java/ghidra/util/NumericUtilities.java | 234 ++++++++++-------- .../ghidra/util/NumericUtilitiesTest.java | 137 +++++++++- 8 files changed, 339 insertions(+), 140 deletions(-) diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/actions/FindReferencesToFieldByNameOrOffsetAction.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/actions/FindReferencesToFieldByNameOrOffsetAction.java index d1d6d3aea1..b4654104e0 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/actions/FindReferencesToFieldByNameOrOffsetAction.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/actions/FindReferencesToFieldByNameOrOffsetAction.java @@ -4,9 +4,9 @@ * Licensed 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. @@ -89,11 +89,12 @@ public class FindReferencesToFieldByNameOrOffsetAction extends AbstractFindRefer return null; // cancelled } - Long longChoice = parseInt(userChoice); - if (longChoice != null) { - return new FieldMatcher(dt, longChoice.intValue()); + try { + return new FieldMatcher(dt, NumericUtilities.parseInt(userChoice)); + } + catch (NumberFormatException e) { + return new FieldMatcher(dt, userChoice); } - return new FieldMatcher(dt, userChoice); } private String[] getCompositeFieldNames(Composite composite) { @@ -112,8 +113,4 @@ public class FindReferencesToFieldByNameOrOffsetAction extends AbstractFindRefer return names.toArray(String[]::new); } - - private Long parseInt(String s) { - return NumericUtilities.parseNumber(s, null); - } } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/script/AskDialog.java b/Ghidra/Features/Base/src/main/java/ghidra/app/script/AskDialog.java index ee24ff5d02..72df6cd92c 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/script/AskDialog.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/script/AskDialog.java @@ -21,6 +21,8 @@ import java.util.List; import javax.swing.*; +import org.apache.commons.lang3.StringUtils; + import docking.DialogComponentProvider; import docking.DockingWindowManager; import docking.widgets.combobox.GComboBox; @@ -246,12 +248,12 @@ public class AskDialog extends DialogComponentProvider { protected Integer getValueAsInt() { String text = getValueAsString(); - return text != null ? NumericUtilities.parseInt(text) : null; + return !StringUtils.isBlank(text) ? NumericUtilities.parseInt(text) : null; } protected Long getValueAsLong() { String text = getValueAsString(); - return text != null ? NumericUtilities.parseLong(text) : null; + return !StringUtils.isBlank(text) ? NumericUtilities.parseLong(text) : null; } protected Double getValueAsDouble() { diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/opinion/ElfLoaderOptionsFactory.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/opinion/ElfLoaderOptionsFactory.java index 7a17808814..177c922514 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/opinion/ElfLoaderOptionsFactory.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/opinion/ElfLoaderOptionsFactory.java @@ -4,9 +4,9 @@ * Licensed 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. @@ -220,9 +220,9 @@ public class ElfLoaderOptionsFactory { if (!String.class.isAssignableFrom(option.getValueClass())) { return "Invalid type for option: " + name + " - " + option.getValueClass(); } - String value = (String) option.getValue(); + String value = ((String) option.getValue()); try { - space.getAddress(Long.parseUnsignedLong(value, 16), true);// verify valid address + space.getAddress(NumericUtilities.parseHexLong(value), true);// verify valid address } catch (NumberFormatException e) { return "Invalid " + name + " - expecting hexidecimal address offset"; diff --git a/Ghidra/Features/PDB/src/main/java/ghidra/app/util/bin/format/pdb/DefaultPdbMember.java b/Ghidra/Features/PDB/src/main/java/ghidra/app/util/bin/format/pdb/DefaultPdbMember.java index 35ac356d77..243b9f8b0a 100644 --- a/Ghidra/Features/PDB/src/main/java/ghidra/app/util/bin/format/pdb/DefaultPdbMember.java +++ b/Ghidra/Features/PDB/src/main/java/ghidra/app/util/bin/format/pdb/DefaultPdbMember.java @@ -18,6 +18,7 @@ package ghidra.app.util.bin.format.pdb; import java.util.List; import org.apache.commons.lang3.StringUtils; +import org.apache.commons.lang3.math.NumberUtils; import ghidra.app.util.SymbolPathParser; import ghidra.program.model.data.DataType; @@ -134,14 +135,15 @@ public class DefaultPdbMember extends PdbMember { try { int colonIndex = bitSizeOffsetStr.indexOf(':'); if (colonIndex > 0) { - bitFieldOffset = (int) NumericUtilities.parseNumber( - bitSizeOffsetStr.substring(colonIndex + 1)); + bitFieldOffset = + NumericUtilities.parseInt(bitSizeOffsetStr.substring(colonIndex + 1)); bitSizeOffsetStr = bitSizeOffsetStr.substring(0, colonIndex); } else { dataTypeParser.setMissingBitOffsetError(); } - bitFieldSize = (int) NumericUtilities.parseNumber(bitSizeOffsetStr); + NumberUtils.toInt(bitSizeOffsetStr, 0); + bitFieldSize = NumericUtilities.parseInt(bitSizeOffsetStr); } catch (NumberFormatException e) { Msg.error(this, "Invalid PDB bitfield specification: " + name); diff --git a/Ghidra/Framework/Docking/src/main/java/docking/widgets/dialogs/NumberRangeInputDialog.java b/Ghidra/Framework/Docking/src/main/java/docking/widgets/dialogs/NumberRangeInputDialog.java index da34f0cad7..9dd7ce22f0 100644 --- a/Ghidra/Framework/Docking/src/main/java/docking/widgets/dialogs/NumberRangeInputDialog.java +++ b/Ghidra/Framework/Docking/src/main/java/docking/widgets/dialogs/NumberRangeInputDialog.java @@ -4,9 +4,9 @@ * Licensed 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. @@ -151,8 +151,7 @@ public class NumberRangeInputDialog extends DialogComponentProvider { String trimmed = rangeText.trim(); if (!trimmed.contains(RANGE_DELIMITER)) { try { - long parsedLong = NumericUtilities.parseLong(trimmed); - int intValue = (int) parsedLong; + int intValue = NumericUtilities.parseInt(trimmed); rangeList.addRange(intValue, intValue); } catch (NumberFormatException e) { @@ -165,12 +164,8 @@ public class NumberRangeInputDialog extends DialogComponentProvider { // this must be a range String[] startAndEnd = trimmed.split(RANGE_DELIMITER); try { - long parsedLong = NumericUtilities.parseLong(startAndEnd[0]); - int startInt = (int) parsedLong; - - parsedLong = NumericUtilities.parseLong(startAndEnd[1]); - int endInt = (int) parsedLong; - + int startInt = NumericUtilities.parseInt(startAndEnd[0]); + int endInt = NumericUtilities.parseInt(startAndEnd[1]); rangeList.addRange(startInt, endInt); } catch (NumberFormatException e) { diff --git a/Ghidra/Framework/Docking/src/main/java/docking/widgets/textfield/GValidatedTextField.java b/Ghidra/Framework/Docking/src/main/java/docking/widgets/textfield/GValidatedTextField.java index 1b6b763925..dce900bb1c 100644 --- a/Ghidra/Framework/Docking/src/main/java/docking/widgets/textfield/GValidatedTextField.java +++ b/Ghidra/Framework/Docking/src/main/java/docking/widgets/textfield/GValidatedTextField.java @@ -4,9 +4,9 @@ * Licensed 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. @@ -172,8 +172,9 @@ public class GValidatedTextField extends JTextField { @Override public void validate(String oldText, String newText) throws ValidationFailedException { try { - long oldLong = NumericUtilities.parseLong(oldText); - long newLong = NumericUtilities.parseLong(newText); + + long oldLong = sanitizeInput(oldText); + long newLong = sanitizeInput(newText); validateLong(oldLong, newLong); } catch (NumberFormatException e) { @@ -181,7 +182,52 @@ public class GValidatedTextField extends JTextField { } } + @SuppressWarnings("unused") public void validateLong(long oldLong, long newLong) throws ValidationFailedException { + // do nothing + } + + /** + * Similar to {@link NumericUtilities#parseLong(String)}, but we return 0 if the string + * is null, empty, or looks like a partially entered value (such as "-", "0x", etc) + * + * @param s the string to parse + * @return the parsed {@code long} value, or 0 if the string is null, empty, or looks + * like a partially entered value (such as "-", "0x", etc) + * @throws NumberFormatException if the string does not represent a valid {@code long} + * value + */ + private long sanitizeInput(String s) throws NumberFormatException { + // Trim the string and return 0 if it's empty. This could be a partial input. + s = (s == null ? "" : s.trim()); + if (s.isEmpty()) { + return 0; + } + + // Chop off the optional sign character of the string, we'll add it back later + long sign = 1; + if (s.startsWith("-")) { + sign = -1; + s = s.substring(1); + } + else if (s.startsWith("+")) { + s = s.substring(1); + } + + // Chop off the optional hex prefix...we'll decide to do hex later + boolean hexPrefix = s.startsWith("0x") || s.startsWith("0X"); + if (hexPrefix) { + s = s.substring(2); + } + + // Since we might have chopped off prefixes, check again and return 0 if it's empty. + // This could be a partial input. + if (s.isEmpty()) { + return 0; + } + + return sign * + (hexPrefix ? NumericUtilities.parseHexLong(s) : NumericUtilities.parseLong(s)); } } } diff --git a/Ghidra/Framework/Generic/src/main/java/ghidra/util/NumericUtilities.java b/Ghidra/Framework/Generic/src/main/java/ghidra/util/NumericUtilities.java index a7e3ec1a23..7d50e781d5 100644 --- a/Ghidra/Framework/Generic/src/main/java/ghidra/util/NumericUtilities.java +++ b/Ghidra/Framework/Generic/src/main/java/ghidra/util/NumericUtilities.java @@ -18,6 +18,7 @@ package ghidra.util; import java.math.BigInteger; import java.util.*; import java.util.concurrent.atomic.AtomicLong; +import java.util.function.Function; import java.util.stream.Collectors; import java.util.stream.Stream; @@ -29,6 +30,7 @@ import util.CollectionUtils; public final class NumericUtilities { public static final BigInteger MAX_UNSIGNED_LONG = new BigInteger("ffffffffffffffff", 16); public static final BigInteger MAX_SIGNED_LONG = new BigInteger("7fffffffffffffff", 16); + public static final BigInteger MAX_UNSIGNED_INT = new BigInteger("ffffffff", 16); public static final long MAX_UNSIGNED_INT32_AS_LONG = 0xffffffffL; private final static String HEX_PREFIX_X = "0X"; @@ -64,7 +66,9 @@ public final class NumericUtilities { * * @param numStr the number string * @return the long value or 0 + * @deprecated use {@link #parseLong(String)} instead */ + @Deprecated(since = "11.3", forRemoval = true) public static long parseNumber(String numStr) { return parseNumber(numStr, Long.valueOf(0)); } @@ -75,7 +79,9 @@ public final class NumericUtilities { * @param s the string to parse * @param defaultValue the default value to use if the string cannot be parsed * @return the long value + * @deprecated use {@link #parseLong(String, long)} instead */ + @Deprecated(since = "11.3", forRemoval = true) public static Long parseNumber(String s, Long defaultValue) { s = (s == null ? "" : s.trim()); if (s.length() == 0) { @@ -104,90 +110,149 @@ public final class NumericUtilities { * the top bit set to be implicitly parsed as negative values. * * @param s the string to parse - * @return the {@code int} value, or 0 if the string to parse is null or blank + * @return the parsed {@code int} value * @throws NumberFormatException if the string does not represent a valid {@code int} value */ - public static int parseInt(String s) { - String origStr = s; - int sign = 1; - - s = (s == null ? "" : s.trim()); - if (s.length() == 0) { - return 0; - } - if (s.startsWith("-")) { - sign = -1; - s = s.substring(1); - } - int radix = 10; - - if (s.startsWith(HEX_PREFIX_x) || s.startsWith(HEX_PREFIX_X)) { - if (s.length() > 10) { - throw new NumberFormatException(s + " has too many digits."); - } - s = s.substring(2); - radix = 16; - } - if (s.length() == 0) { - return 0; - } - try { - BigInteger bi = new BigInteger(s, radix); - return bi.intValue() * sign; - } - catch (NumberFormatException e) { - // This is a little hacky, but the message should be complete and report about the - // original string - NumberFormatException e2 = - new NumberFormatException("Cannot parse int from " + origStr); - e2.setStackTrace(e.getStackTrace()); - throw e2; - } - catch (ArithmeticException e) { - throw new NumberFormatException(origStr + " is too big."); - } + public static int parseInt(String s) throws NumberFormatException { + return parseHelper(s, false, BigInteger::intValue, MAX_UNSIGNED_INT); } - + /** - * Parses the given decimal/hex string as a {@code long} value. This method allows values with + * Parses the given decimal/hex string as an {@code int} value. This method allows values with * the top bit set to be implicitly parsed as negative values. * * @param s the string to parse - * @return the {@code long} value, or 0 if the string to parse is null or blank + * @param defaultValue the default value to return if the string does not represent a valid + * {@code int} value + * @return the parsed {@code int} value or the {@code defaultValue} if the string does not + * represent a valid {@code int} value + */ + public static int parseInt(String s, int defaultValue) { + try { + return parseInt(s); + } + catch (NumberFormatException e) { + return defaultValue; + } + } + + /** + * Parses the given decimal/hex string as an {@code long} value. This method allows values with + * the top bit set to be implicitly parsed as negative values. + * + * @param s the string to parse + * @return the parsed {@code long} value * @throws NumberFormatException if the string does not represent a valid {@code long} value */ - public static long parseLong(String s) { - String origStr = s; - long sign = 1; + public static long parseLong(String s) throws NumberFormatException { + return parseHelper(s, false, BigInteger::longValue, MAX_UNSIGNED_LONG); + } - s = (s == null ? "" : s.trim()); - if (s.length() == 0) { - return 0; + /** + * Parses the given decimal/hex string as an {@code long} value. This method allows values with + * the top bit set to be implicitly parsed as negative values. + * + * @param s the string to parse + * @param defaultValue the default value to return if the string does not represent a valid + * {@code long} value + * @return the parsed {@code long} value or the {@code defaultValue} if the string does not + * represent a valid {@code long} value + */ + public static long parseLong(String s, long defaultValue) { + try { + return parseLong(s); } - if (s.startsWith("-")) { - sign = -1; + catch (NumberFormatException e) { + return defaultValue; + } + } + + /** + * Parses the given hex string as a {@code long} value. + *

+ * Note: The string is treated as hex regardless of whether or not it contains the {@code 0x} + * prefix. + * + * @param s the string to parse + * @return the parsed {@code long} value + * @throws NumberFormatException if the string does not represent a valid value + */ + public static long parseHexLong(String s) throws NumberFormatException { + return parseHelper(s, true, BigInteger::longValue, MAX_UNSIGNED_LONG); + } + + /** + * Parses the given hex string as a {@link BigInteger} value. + *

+ * Note: The string is treated as hex regardless of whether or not it contains the {@code 0x} + * prefix. + * + * @param s the string to parse + * @return the parsed {@link BigInteger} value + * @throws NumberFormatException if the string does not represent a valid value + * @deprecated use {@link #parseHexLong(String)} instead + */ + @Deprecated(since = "11.3", forRemoval = true) + public static BigInteger parseHexBigInteger(String s) throws NumberFormatException { + return parseHelper(s, true, Function.identity(), MAX_UNSIGNED_LONG); + } + + /** + * Parses the given decimal/hex string as a custom type. This method allows values with the top + * bit set to be implicitly parsed as negative values. + * + * @param s the string to parse + * @param forceHex true if the string to parse should be treated as hex, even if it doesn't + * start with {@code 0x}; otherwise, false; + * @param func a {@link Function} used to convert the parsed {@link BigInteger} to a custom type + * @param max the maximum value that can be used to represent the type of value being parsed if + * it were treated as unsigned + * @param The type of value being parsed + * @return the parsed value + * @throws NumberFormatException if the string does not represent a valid value + */ + private static T parseHelper(String s, boolean forceHex, Function func, + BigInteger max) throws NumberFormatException { + String origStr = s; + + // Trim the string, and throw exception if it's null/empty + s = (s == null ? "" : s.trim()); + if (s.isEmpty()) { + throw new NumberFormatException("String to parse is empty"); + } + + // Chop off the optional sign character of the string, we'll add it back later + String sign = ""; + if (s.startsWith("-") || s.startsWith("+")) { + sign = s.substring(0, 1); s = s.substring(1); } - int radix = 10; - if (s.startsWith(HEX_PREFIX_x) || s.startsWith(HEX_PREFIX_X)) { - if (s.length() > 18) { - throw new NumberFormatException(s + " has too many digits."); - } + // Process the radix + boolean hexPrefix = s.startsWith(HEX_PREFIX_x) || s.startsWith(HEX_PREFIX_X); + int radix = forceHex || hexPrefix ? 16 : 10; + if (hexPrefix) { s = s.substring(2); - radix = 16; } - if (s.length() == 0) { - return 0; + + // Make sure next character is not + or - (protects against things like "0x-ffff") + if (!s.isEmpty() && (s.charAt(0) == '-' || s.charAt(0) == '+')) { + throw new NumberFormatException("Cannot parse " + origStr); } + + // Try to convert the string to the desired type try { - BigInteger bi = new BigInteger(s, radix); - return bi.longValue() * sign; + + // Check size + if (new BigInteger(s, radix).compareTo(max) > 0) { + throw new NumberFormatException(s + " exceeds maximum data type size."); + } + + return func.apply(new BigInteger(sign + s, radix)); } catch (NumberFormatException e) { // A little hacky, but the message should be complete and report the original string - NumberFormatException e2 = - new NumberFormatException("Cannot parse long from " + origStr); + NumberFormatException e2 = new NumberFormatException("Cannot parse " + origStr); e2.setStackTrace(e.getStackTrace()); throw e2; } @@ -196,47 +261,6 @@ public final class NumericUtilities { } } - /** - * Parses the given string as a hex long value, detecting whether or not it begins with a hex - * prefix, and if not, parses as a long int value. - * @param s the string to parse - * @return the long value - * @throws NumberFormatException if the string is blank - */ - public static long parseHexLong(String s) { - return parseHexBigInteger(s).longValue(); - } - - /** - * Parses the given hex string as a BigIntge value, detecting whether or not it begins with a - * hex prefix, and if not, parses as a long int value. - * @param s the string to parse - * @return the long value - * @throws NumberFormatException if the string is blank - */ - public static BigInteger parseHexBigInteger(String s) { - - s = (s == null ? "" : s.trim()); - if (s.length() == 0) { - throw new NumberFormatException(s + " no digits."); - } - - boolean negative = false; - if (s.startsWith("-")) { - negative = true; - s = s.substring(1); - } - - if (s.startsWith(HEX_PREFIX_x) || s.startsWith(HEX_PREFIX_X)) { - s = s.substring(2); - } - - if (negative) { - s = "-" + s; - } - return new BigInteger(s, 16); - } - private static BigInteger decodeMagnitude(int p, String s) { // Special case, so it doesn't get chewed by octal parser if ("0".equals(s)) { @@ -314,7 +338,7 @@ public final class NumericUtilities { * @return the string */ public final static String toSignedHexString(long value) { - StringBuffer buf = new StringBuffer(); + StringBuilder buf = new StringBuilder(); if (value < 0) { buf.append("-"); } diff --git a/Ghidra/Framework/Generic/src/test/java/ghidra/util/NumericUtilitiesTest.java b/Ghidra/Framework/Generic/src/test/java/ghidra/util/NumericUtilitiesTest.java index b8c4731ed5..de46ec49cc 100644 --- a/Ghidra/Framework/Generic/src/test/java/ghidra/util/NumericUtilitiesTest.java +++ b/Ghidra/Framework/Generic/src/test/java/ghidra/util/NumericUtilitiesTest.java @@ -15,8 +15,7 @@ */ package ghidra.util; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.fail; +import static org.junit.Assert.*; import java.math.BigInteger; import java.util.*; @@ -362,60 +361,194 @@ public class NumericUtilitiesTest { fail(); } catch (NumberFormatException e) { + // do nothing } try { NumericUtilities.decodeBigInteger("+"); fail(); } catch (NumberFormatException e) { + // do nothing } try { NumericUtilities.decodeBigInteger("-"); fail(); } catch (NumberFormatException e) { + // do nothing } try { NumericUtilities.decodeBigInteger("0x"); fail(); } catch (NumberFormatException e) { + // do nothing } try { NumericUtilities.decodeBigInteger("0b"); fail(); } catch (NumberFormatException e) { + // do nothing } try { NumericUtilities.decodeBigInteger("a01"); fail(); } catch (NumberFormatException e) { + // do nothing } try { NumericUtilities.decodeBigInteger("081"); fail(); } catch (NumberFormatException e) { + // do nothing } try { NumericUtilities.decodeBigInteger("0x9g"); fail(); } catch (NumberFormatException e) { + // do nothing } try { NumericUtilities.decodeBigInteger(" 10"); fail(); } catch (NumberFormatException e) { + // do nothing } try { NumericUtilities.decodeBigInteger("10 "); fail(); } catch (NumberFormatException e) { + // do nothing + } + } + + @Test + public void testParse() { + + // parseInt() + assertEquals(-1, NumericUtilities.parseInt("0xffffffff")); + assertEquals(-1, NumericUtilities.parseInt("+0xffffffff")); + assertEquals(1, NumericUtilities.parseInt("-0xffffffff")); + assertEquals(0, NumericUtilities.parseInt("0x100000000", 0)); + assertEquals(-1, NumericUtilities.parseInt("4294967295")); // 0xffffffff + try { + assertEquals(0, NumericUtilities.parseInt("0x100000000")); + fail(); + } + catch (NumberFormatException e) { + // do nothing + } + try { + assertEquals(0, NumericUtilities.parseInt("4294967296")); // 0x100000000 + fail(); + } + catch (NumberFormatException e) { + // do nothing + } + try { + assertEquals(0, NumericUtilities.parseInt("0x-5")); + fail(); + } + catch (NumberFormatException e) { + // do nothing + } + try { + assertEquals(0, NumericUtilities.parseInt(null)); + fail(); + } + catch (NumberFormatException e) { + // do nothing + } + try { + assertEquals(0, NumericUtilities.parseInt(" ")); + fail(); + } + catch (NumberFormatException e) { + // do nothing + } + + // parseLong() + assertEquals(-1, NumericUtilities.parseLong("0xffffffffffffffff")); + assertEquals(-1, NumericUtilities.parseLong("+0xffffffffffffffff")); + assertEquals(1, NumericUtilities.parseLong("-0xffffffffffffffff")); + assertEquals(0, NumericUtilities.parseLong("0x10000000000000000", 0)); + assertEquals(-1, NumericUtilities.parseLong("18446744073709551615")); // 0xffffffffffffffff + try { + assertEquals(0, NumericUtilities.parseLong("0x10000000000000000")); + fail(); + } + catch (NumberFormatException e) { + // do nothing + } + try { + assertEquals(0, NumericUtilities.parseLong("18446744073709551616")); // 0x10000000000000000 + fail(); + } + catch (NumberFormatException e) { + // do nothing + } + try { + assertEquals(0, NumericUtilities.parseInt("0x-5")); + fail(); + } + catch (NumberFormatException e) { + // do nothing + } + try { + assertEquals(0, NumericUtilities.parseLong(null)); + fail(); + } + catch (NumberFormatException e) { + // do nothing + } + try { + assertEquals(0, NumericUtilities.parseLong(" ")); + fail(); + } + catch (NumberFormatException e) { + // do nothing + } + + // parseHexLong() + assertEquals(-1, NumericUtilities.parseHexLong("ffffffffffffffff")); + assertEquals(-1, NumericUtilities.parseHexLong("0xffffffffffffffff")); + assertEquals(-1, NumericUtilities.parseHexLong("+ffffffffffffffff")); + assertEquals(-1, NumericUtilities.parseHexLong("+0xffffffffffffffff")); + assertEquals(1, NumericUtilities.parseHexLong("-ffffffffffffffff")); + assertEquals(1, NumericUtilities.parseHexLong("-0xffffffffffffffff")); + try { + assertEquals(0, NumericUtilities.parseHexLong("0x10000000000000000")); + fail(); + } + catch (NumberFormatException e) { + // do nothing + } + try { + assertEquals(0, NumericUtilities.parseHexLong("0x-5")); + fail(); + } + catch (NumberFormatException e) { + // do nothing + } + try { + assertEquals(0, NumericUtilities.parseHexLong(null)); + fail(); + } + catch (NumberFormatException e) { + // do nothing + } + try { + assertEquals(0, NumericUtilities.parseHexLong(" ")); + fail(); + } + catch (NumberFormatException e) { + // do nothing } } }