@@ -218,15 +218,15 @@ static final class Tokenizer {
218218 private static final Integer QUALIFIER_ALPHA = -5 ;
219219 private static final Integer QUALIFIER_BETA = -4 ;
220220 private static final Integer QUALIFIER_MILESTONE = -3 ;
221- private static final Map <String , Integer > QUALIFIERS ;
222221 private final String version ;
223222 private int index ;
224- private String token ;
223+ private int tokenStart ;
224+ private int tokenEnd ;
225225 private boolean number ;
226226 private boolean terminatedByNumber ;
227227
228228 Tokenizer (String version ) {
229- this .version = version .length () > 0 ? version : "0" ;
229+ this .version = ! version .isEmpty () ? version : "0" ;
230230 }
231231
232232 public boolean next () {
@@ -269,10 +269,12 @@ public boolean next() {
269269 }
270270
271271 if (end > start ) {
272- this .token = this .version .substring (start , end );
272+ this .tokenStart = start ;
273+ this .tokenEnd = end ;
273274 this .number = state >= 0 ;
274275 } else {
275- this .token = "0" ;
276+ this .tokenStart = 0 ;
277+ this .tokenEnd = 1 ;
276278 this .number = true ;
277279 }
278280
@@ -282,29 +284,33 @@ public boolean next() {
282284
283285 @ Override
284286 public String toString () {
285- return String . valueOf (this .token );
287+ return this . version . substring (this .tokenStart , this . tokenEnd );
286288 }
287289
288290 public Version .Item toItem () {
289291 if (this .number ) {
290292 try {
291- return this .token .length () < 10 ? new Version .Item (4 , Integer .parseInt (this .token )) : new Version .Item (5 , new BigInteger (this .token ));
292- } catch (NumberFormatException var2 ) {
293- throw new IllegalStateException (var2 );
293+ int tokenLength = this .tokenEnd - this .tokenStart ;
294+ return tokenLength < 10 ? new Version .Item (4 , parseInt (version , tokenStart , tokenEnd )) :
295+ new Version .Item (5 , new BigInteger (version .substring (this .tokenStart , this .tokenEnd )));
296+ } catch (NumberFormatException e ) {
297+ throw new IllegalArgumentException ("Illegal version: " + version .substring (this .tokenStart , this .tokenEnd ), e );
294298 }
295299 } else {
300+ int tokenLength = this .tokenEnd - this .tokenStart ;
296301 if (this .index >= this .version .length ()) {
297- if ("min" . equalsIgnoreCase ( this . token )) {
302+ if (tokenLength == 3 && matches ( "min" )) {
298303 return Version .Item .MIN ;
299304 }
300305
301- if ("max" . equalsIgnoreCase ( this . token )) {
306+ if (tokenLength == 3 && matches ( "max" )) {
302307 return Version .Item .MAX ;
303308 }
304309 }
305310
306- if (this .terminatedByNumber && this .token .length () == 1 ) {
307- switch (this .token .charAt (0 )) {
311+ if (this .terminatedByNumber && tokenLength == 1 ) {
312+ char c = this .version .charAt (this .tokenStart );
313+ switch (c ) {
308314 case 'A' :
309315 case 'a' :
310316 return new Version .Item (2 , QUALIFIER_ALPHA );
@@ -317,24 +323,98 @@ public Version.Item toItem() {
317323 }
318324 }
319325
320- Integer qualifier = QUALIFIERS .get (this .token );
321- return qualifier != null ? new Version .Item (2 , qualifier ) : new Version .Item (3 , this .token .toLowerCase (Locale .ENGLISH ));
326+ // Fast path for common qualifiers (avoiding substring allocation and map lookup)
327+ switch (tokenLength ) {
328+ case 0 :
329+ return new Version .Item (2 , 0 );
330+ case 2 :
331+ if (matches ("ga" )) {
332+ return new Version .Item (2 , 0 );
333+ }
334+ if (matches ("rc" )) {
335+ return new Version .Item (2 , -2 );
336+ }
337+ if (matches ("cr" )) {
338+ return new Version .Item (2 , -2 );
339+ }
340+ if (matches ("sp" )) {
341+ return new Version .Item (2 , 1 );
342+ }
343+ break ;
344+ case 4 :
345+ if (matches ("beta" )) {
346+ return new Version .Item (2 , QUALIFIER_BETA );
347+ }
348+ break ;
349+ case 5 :
350+ if (matches ("alpha" )) {
351+ return new Version .Item (2 , QUALIFIER_ALPHA );
352+ }
353+ if (matches ("final" )) {
354+ return new Version .Item (2 , 0 );
355+ }
356+ break ;
357+ case 7 :
358+ if (matches ("release" )) {
359+ return new Version .Item (2 , 0 );
360+ }
361+ break ;
362+ case 8 :
363+ if (matches ("snapshot" )) {
364+ return new Version .Item (2 , -1 );
365+ }
366+ break ;
367+ case 9 :
368+ if (matches ("milestone" )) {
369+ return new Version .Item (2 , QUALIFIER_MILESTONE );
370+ }
371+ break ;
372+ }
373+
374+ // Unknown qualifier - treat as string
375+ String token = this .version .substring (this .tokenStart , this .tokenEnd );
376+ return new Version .Item (3 , token .toLowerCase (Locale .ENGLISH ));
377+ }
378+ }
379+
380+ // Adapted from Java 9's `Integer#parseInt(CharSequence, int, int, int)`
381+ private static int parseInt (String s , int beginIndex , int endIndex )
382+ throws NumberFormatException {
383+
384+ if (beginIndex >= endIndex ) {
385+ throw new NumberFormatException ("Empty string" );
386+ }
387+
388+ int result = 0 ;
389+ int i = beginIndex ;
390+ int limit = -Integer .MAX_VALUE ;
391+ int multmin = limit / 10 ;
392+
393+ char firstChar = s .charAt (i );
394+ if (firstChar < '0' || firstChar > '9' ) {
395+ throw new NumberFormatException ("Invalid character" );
396+ }
397+
398+ // Accumulating negatively avoids surprises near MAX_VALUE
399+ while (i < endIndex ) {
400+ int digit = s .charAt (i ++) - '0' ;
401+ if (digit < 0 || digit > 9 ) {
402+ throw new NumberFormatException ("Invalid character" );
403+ }
404+ if (result < multmin ) {
405+ throw new NumberFormatException ("Value out of range" );
406+ }
407+ result *= 10 ;
408+ if (result < limit + digit ) {
409+ throw new NumberFormatException ("Value out of range" );
410+ }
411+ result -= digit ;
322412 }
413+ return -result ;
323414 }
324415
325- static {
326- QUALIFIERS = new TreeMap <>(String .CASE_INSENSITIVE_ORDER );
327- QUALIFIERS .put ("alpha" , QUALIFIER_ALPHA );
328- QUALIFIERS .put ("beta" , QUALIFIER_BETA );
329- QUALIFIERS .put ("milestone" , QUALIFIER_MILESTONE );
330- QUALIFIERS .put ("cr" , -2 );
331- QUALIFIERS .put ("rc" , -2 );
332- QUALIFIERS .put ("snapshot" , -1 );
333- QUALIFIERS .put ("ga" , 0 );
334- QUALIFIERS .put ("final" , 0 );
335- QUALIFIERS .put ("release" , 0 );
336- QUALIFIERS .put ("" , 0 );
337- QUALIFIERS .put ("sp" , 1 );
416+ private boolean matches (String target ) {
417+ return this .version .regionMatches (true , this .tokenStart , target , 0 , target .length ());
338418 }
339419 }
340420}
0 commit comments