/** * Copyright (C) 2011 Jacob Scott <jascottytechie@gmail.com> * Description: provides checking for parsing numbers from strings & validating input * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package me.jascotty2.libv01.io; import java.math.BigInteger; import java.util.regex.Pattern; /** * @author jacob */ public class CheckInput { // double string checking from Double documentation final static String Digits = "(\\p{Digit}+)"; final static String HexDigits = "(\\p{XDigit}+)"; // an exponent is 'e' or 'E' followed by an optionally // signed decimal integer. final static String Exp = "[eE][+-]?" + Digits; final static String fpRegex = ("[\\x00-\\x20]*" + // Optional leading "whitespace" "[+-]?(" + // Optional sign character "NaN|" + // "NaN" string "Infinity|" + // "Infinity" string // A decimal floating-point string representing a finite positive // number without a leading sign has at most five basic pieces: // Digits . Digits ExponentPart FloatTypeSuffix // // Since this method allows integer-only strings as input // in addition to strings of floating-point literals, the // two sub-patterns below are simplifications of the grammar // productions from the Java Language Specification, 2nd // edition, section 3.10.2. // Digits ._opt Digits_opt ExponentPart_opt FloatTypeSuffix_opt "(((" + Digits + "(\\.)?(" + Digits + "?)(" + Exp + ")?)|" + // . Digits ExponentPart_opt FloatTypeSuffix_opt "(\\.(" + Digits + ")(" + Exp + ")?)|" + // Hexadecimal strings "((" + // 0[xX] HexDigits ._opt BinaryExponent FloatTypeSuffix_opt "(0[xX]" + HexDigits + "(\\.)?)|" + // 0[xX] HexDigits_opt . HexDigits BinaryExponent FloatTypeSuffix_opt "(0[xX]" + HexDigits + "?(\\.)" + HexDigits + ")" + ")[pP][+-]?" + Digits + "))" + "[fFdD]?))" + "[\\x00-\\x20]*");// Optional trailing "whitespace" // int final static String IntPattern = "[+-]?" + Digits; // or: "^-?\\d+$" public static boolean IsInt(String input) { //return Pattern.matches(IntPattern, input); // pattern may be faster for small strings, but NumberFormatException is more accurate :( try { Integer.parseInt(input); return true; } catch (NumberFormatException e) { return false; } } public static boolean IsLong(String input) { //return Pattern.matches(IntPattern, input); // pattern faster for small strings, but NumberFormatException is more accurate :( try { Long.parseLong(input); return true; } catch (NumberFormatException e) { return false; } } public static boolean IsByte(String input) { try { Byte.parseByte(input); return true; } catch (NumberFormatException e) { return false; } } public static boolean IsDouble(String input) { return Pattern.matches(fpRegex, input); } public static long GetLong(String input, long onError) { if (input == null) { return onError; } try { return Pattern.matches(IntPattern, input) ? Long.parseLong(input) : onError; } catch (NumberFormatException e) { // just in case the number is too large... can never be too careful.. return onError; } } public static int GetInt(String input, int onError) { if (input == null) { return onError; } try { return Pattern.matches(IntPattern, input) ? Integer.parseInt(input) : onError; } catch (NumberFormatException e) { // just in case the number is too large... can never be too careful.. return onError; } } public static double GetDouble(String input, double onError) { if (input == null) { return onError; } try { return Pattern.matches(fpRegex, input) ? Double.parseDouble(input) : onError; } catch (NumberFormatException e) { return onError; } } public static byte GetByte(String input, byte onError) { if (input == null) { return onError; } // not fully sure how to catch a byte with an expression, so checking int instead try { return Pattern.matches(IntPattern, input) ? Byte.parseByte(input) : onError; } catch (NumberFormatException e) { return onError; } } public static BigInteger GetBigInt(String str, long defaultNum) { if (str == null) { return new BigInteger(String.valueOf(defaultNum)); } try { return new BigInteger(str); } catch (Exception e) { return new BigInteger(String.valueOf(defaultNum)); } } public static BigInteger GetBigInt_TimeSpanInSec(String str) throws Exception { return GetBigInt_TimeSpanInSec(str, 's'); } public static BigInteger GetBigInt_TimeSpanInSec(String str, char defaultUnit) throws Exception { BigInteger ret = new BigInteger("0"); int charPos = 0; for (; charPos < str.length(); ++charPos) { if (!Character.isDigit(str.charAt(charPos))) { break; } } //boolean good = false; if (charPos > 0) { // double-check value if (CheckInput.IsInt(str.substring(0, charPos))) { ret = new BigInteger(str.substring(0, charPos)); char unit = str.length() == charPos ? defaultUnit : str.charAt(charPos); if (unit == 's') { // do nothing: is already seconds return ret; } else if (unit == 'm') { // it's annoying that this class doesn't accept a long.. ret = ret.multiply(new BigInteger("60")); } else if (unit == 'h') { ret = ret.multiply(new BigInteger("3600")); } else if (unit == 'd') { ret = ret.multiply(new BigInteger("86400")); } else if (unit == 'w') { ret = ret.multiply(new BigInteger("604800")); } else if (unit == 'M') { // using 1m = 30 days ret = ret.multiply(new BigInteger("18144000")); } else { throw new Exception("Unknown TimeSpan unit: " + str.charAt(charPos)); } return ret; } else { throw new Exception("Invalid Numerical Value: " + str); } } // will throw it's own exception return new BigInteger(str); } /** * attempts to extract a number from within a string <br/> * ex. "$5.03", "5.03 Dollars" <br/> * if there are multiple, will only return the first * @param input * @param onError * @return */ public static double ExtractDouble(String input, double onError) { int numStart = -1, numEnd = input.length() - 1; boolean dec = false; for (int i = 0; i < input.length(); ++i) { if ((Character.isDigit(input.charAt(i)) || (input.charAt(i) == '.' && !dec && (dec = true)))) { if (numStart < 0) { numStart = i; } } else if (numStart >= 0) { numEnd = i - 1; break; } } if (numStart > 0 && input.charAt(numStart - 1) == '.') { --numStart; } if (numStart > 0 && input.charAt(numStart - 1) == '-') { --numStart; } if (numStart >= 0) { return GetDouble(input.substring(numStart, numEnd + 1), onError); } return onError; } } // end class CheckInput