The final restructured version for automatic build using maven tycho
[Mograsim.git] / plugins / net.mograsim.plugin.core / src / net / mograsim / plugin / asm / AsmNumberUtil.java
1 package net.mograsim.plugin.asm;
2
3 import static java.lang.String.format;
4
5 import java.math.BigInteger;
6 import java.util.regex.Matcher;
7 import java.util.regex.Pattern;
8
9 public final class AsmNumberUtil
10 {
11
12         private AsmNumberUtil()
13         {
14
15         }
16
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]+)";
22
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";
27
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);
34
35         public static boolean isPrefix(CharSequence cs, Pattern p)
36         {
37                 Matcher m = p.matcher(cs);
38                 return m.matches() || m.hitEnd();
39         }
40
41         public static boolean isBinary(CharSequence cs)
42         {
43                 return numberBin.matcher(cs).matches();
44         }
45
46         public static boolean isOctal(CharSequence cs)
47         {
48                 return numberOct.matcher(cs).matches();
49         }
50
51         public static boolean isDecimal(CharSequence cs)
52         {
53                 return numberDec.matcher(cs).matches();
54         }
55
56         public static boolean isHexadecimal(CharSequence cs)
57         {
58                 return numberHex.matcher(cs).matches();
59         }
60
61         public static boolean isFloatingPoint(CharSequence cs)
62         {
63                 return numberFloat.matcher(cs).matches();
64         }
65
66         public static boolean isNumber(CharSequence cs)
67         {
68                 return getType(cs) != NumberType.NONE;
69         }
70
71         public static boolean quickCheckForNumber(CharSequence cs)
72         {
73                 if (cs.length() == 0 || !isStart(cs.charAt(0)))
74                         return false;
75                 return cs.length() == 1 || isPart(cs.charAt(1));
76         }
77
78         public static boolean isStart(int c)
79         {
80                 return isDigit(c) || c == '+' || c == '-';
81         }
82
83         public static boolean isDigit(int c)
84         {
85                 return ('0' <= c && c <= '9') || ('A' <= c && c <= 'F') || ('a' <= c && c <= 'f');
86         }
87
88         public static boolean isPart(int c)
89         {
90                 return isDigit(c) || isMarker(Character.toLowerCase(c));
91         }
92
93         public static boolean isMarker(int lowerCase)
94         {
95                 switch (lowerCase)
96                 {
97                 case '.':
98                 case '_':
99                 case '+':
100                 case '-':
101                 case 'e':
102                 case 'x':
103                 case 'b':
104                 case 'q':
105                 case 'h':
106                         return true;
107                 default:
108                         return false;
109                 }
110         }
111
112         public static NumberType getType(CharSequence cs)
113         {
114                 if (!quickCheckForNumber(cs))
115                         return NumberType.NONE;
116                 if (isDecimal(cs))
117                         return NumberType.DECIMAL;
118                 if (isHexadecimal(cs))
119                         return NumberType.HEXADECIMAL;
120                 if (isBinary(cs))
121                         return NumberType.BINARY;
122                 if (isOctal(cs))
123                         return NumberType.OCTAL;
124                 if (isFloatingPoint(cs))
125                         return NumberType.FLOATINGPOINT;
126                 return NumberType.NONE;
127         }
128
129         /**
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.
133          * 
134          * @param cs The potential prefix
135          * @return The type the {@link CharSequence} is a prefix of
136          */
137         public static NumberType prefixOfType(CharSequence cs)
138         {
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;
150         }
151
152         private static CharSequence extractSignAndDigits(NumberType type, CharSequence cs)
153         {
154                 return type.numberPattern.matcher(cs).replaceAll("$1$2").replaceAll("_", "");
155         }
156
157         /**
158          * Computes the {@link BigInteger} value of a {@link CharSequence}, by determining the {@link NumberType} and parsing the number with
159          * the determined radix.
160          * 
161          * @throws NumberFormatException if the {@link CharSequence} does not match any of the expected number patterns
162          */
163         public static BigInteger valueOf(CharSequence cs)
164         {
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);
169         }
170
171         /**
172          * Formats a {@link BigInteger} in accordance with the pattern associated with the supplied {@link NumberType}
173          */
174         public static String toString(BigInteger number, NumberType type)
175         {
176                 String pattern;
177                 switch (type)
178                 {
179                 case BINARY:
180                         pattern = preferredBinPat;
181                         break;
182                 case OCTAL:
183                         pattern = preferredOctPat;
184                         break;
185                 default:
186                 case DECIMAL:
187                         pattern = preferredDecPat;
188                         break;
189                 case HEXADECIMAL:
190                         pattern = preferredHexPat;
191                 }
192                 if (number.signum() < 0)
193                         return String.format(pattern, "-", number.abs().toString(type.radix));
194                 return String.format(pattern, "", number.toString(type.radix));
195         }
196
197         public enum NumberType
198         {
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);
201
202                 public final int radix;
203                 final Pattern numberPattern;
204
205                 NumberType(int radix, Pattern numberPattern)
206                 {
207                         this.radix = radix;
208                         this.numberPattern = numberPattern;
209                 }
210         }
211 }