1 package net.mograsim.plugin.asm;
3 import static java.lang.String.format;
5 import java.math.BigInteger;
6 import java.util.regex.Matcher;
7 import java.util.regex.Pattern;
9 public final class AsmNumberUtil
12 private AsmNumberUtil()
17 private static final String sgnPat = "([-+]?)";
18 private static final String binPat = "((?:[01]+_)*[01]+)";
19 private static final String octPat = "((?:[0-7]+_)*[0-7]+)";
20 private static final String decPat = "((?:[0-9]+_)*[0-9]+)";
21 private static final String hexPat = "((?:[0-9a-f]+_)*[0-9a-f]+)";
23 private static final String preferredBinPat = "%1$s0b%2$s";
24 private static final String preferredOctPat = "%s%sq";
25 private static final String preferredDecPat = "%s%s";
26 private static final String preferredHexPat = "%1$s0x%2$s";
28 static final Pattern numberBin = Pattern.compile(format(preferredBinPat + "|%1$s%2$sb", sgnPat, binPat), Pattern.CASE_INSENSITIVE);
29 static final Pattern numberOct = Pattern.compile(format(preferredOctPat, sgnPat, octPat), Pattern.CASE_INSENSITIVE);
30 static final Pattern numberDec = Pattern.compile(format(preferredDecPat, sgnPat, decPat));
31 static final Pattern numberHex = Pattern.compile(format(preferredHexPat + "|%1$s%2$sh", sgnPat, hexPat), Pattern.CASE_INSENSITIVE);
32 static final Pattern numberFloat = Pattern.compile(format("%1$s%2$s(?:\\.%2$s)?(?:e%1$s%2$s)?", sgnPat, decPat),
33 Pattern.CASE_INSENSITIVE);
35 public static boolean isPrefix(CharSequence cs, Pattern p)
37 Matcher m = p.matcher(cs);
38 return m.matches() || m.hitEnd();
41 public static boolean isBinary(CharSequence cs)
43 return numberBin.matcher(cs).matches();
46 public static boolean isOctal(CharSequence cs)
48 return numberOct.matcher(cs).matches();
51 public static boolean isDecimal(CharSequence cs)
53 return numberDec.matcher(cs).matches();
56 public static boolean isHexadecimal(CharSequence cs)
58 return numberHex.matcher(cs).matches();
61 public static boolean isFloatingPoint(CharSequence cs)
63 return numberFloat.matcher(cs).matches();
66 public static boolean isNumber(CharSequence cs)
68 return getType(cs) != NumberType.NONE;
71 public static boolean quickCheckForNumber(CharSequence cs)
73 if (cs.length() == 0 || !isStart(cs.charAt(0)))
75 return cs.length() == 1 || isPart(cs.charAt(1));
78 public static boolean isStart(int c)
80 return isDigit(c) || c == '+' || c == '-';
83 public static boolean isDigit(int c)
85 return ('0' <= c && c <= '9') || ('A' <= c && c <= 'F') || ('a' <= c && c <= 'f');
88 public static boolean isPart(int c)
90 return isDigit(c) || isMarker(Character.toLowerCase(c));
93 public static boolean isMarker(int lowerCase)
112 public static NumberType getType(CharSequence cs)
114 if (!quickCheckForNumber(cs))
115 return NumberType.NONE;
117 return NumberType.DECIMAL;
118 if (isHexadecimal(cs))
119 return NumberType.HEXADECIMAL;
121 return NumberType.BINARY;
123 return NumberType.OCTAL;
124 if (isFloatingPoint(cs))
125 return NumberType.FLOATINGPOINT;
126 return NumberType.NONE;
130 * Checks if the {@link CharSequence} is a prefix of a valid number, and returns the NumberType it is a prefix of. If a String is a
131 * prefix of multiple different {@link NumberType}s, the one with the smallest radix is returned. If no valid {@link NumberType} is
132 * found, {@link NumberType#NONE} is returned.
134 * @param cs The potential prefix
135 * @return The type the {@link CharSequence} is a prefix of
137 public static NumberType prefixOfType(CharSequence cs)
139 if (isPrefix(cs, numberBin))
140 return NumberType.BINARY;
141 if (isPrefix(cs, numberOct))
142 return NumberType.OCTAL;
143 if (isPrefix(cs, numberDec))
144 return NumberType.DECIMAL;
145 if (isPrefix(cs, numberHex))
146 return NumberType.HEXADECIMAL;
147 if (isPrefix(cs, numberFloat))
148 return NumberType.FLOATINGPOINT;
149 return NumberType.NONE;
152 private static CharSequence extractSignAndDigits(NumberType type, CharSequence cs)
154 return type.numberPattern.matcher(cs).replaceAll("$1$2").replaceAll("_", "");
158 * Computes the {@link BigInteger} value of a {@link CharSequence}, by determining the {@link NumberType} and parsing the number with
159 * the determined radix.
161 * @throws NumberFormatException if the {@link CharSequence} does not match any of the expected number patterns
163 public static BigInteger valueOf(CharSequence cs)
165 NumberType type = getType(cs);
166 if (NumberType.NONE.equals(type))
167 throw new NumberFormatException();
168 return new BigInteger(extractSignAndDigits(type, cs).toString(), type.radix);
172 * Formats a {@link BigInteger} in accordance with the pattern associated with the supplied {@link NumberType}
174 public static String toString(BigInteger number, NumberType type)
180 pattern = preferredBinPat;
183 pattern = preferredOctPat;
187 pattern = preferredDecPat;
190 pattern = preferredHexPat;
192 if (number.signum() < 0)
193 return String.format(pattern, "-", number.abs().toString(type.radix));
194 return String.format(pattern, "", number.toString(type.radix));
197 public enum NumberType
199 NONE(-1, null), BINARY(2, AsmNumberUtil.numberBin), OCTAL(8, AsmNumberUtil.numberOct), DECIMAL(10, AsmNumberUtil.numberDec),
200 HEXADECIMAL(16, AsmNumberUtil.numberHex), FLOATINGPOINT(10, AsmNumberUtil.numberFloat);
202 public final int radix;
203 final Pattern numberPattern;
205 NumberType(int radix, Pattern numberPattern)
208 this.numberPattern = numberPattern;