001/* Generated By:JavaCC: Do not edit this line. VersionParser.java */ 002/* 003 * Copyright (C) 2005 Christian Schulte <cs@schulte.it> 004 * All rights reserved. 005 * 006 * Redistribution and use in source and binary forms, with or without 007 * modification, are permitted provided that the following conditions 008 * are met: 009 * 010 * o Redistributions of source code must retain the above copyright 011 * notice, this list of conditions and the following disclaimer. 012 * 013 * o Redistributions in binary form must reproduce the above copyright 014 * notice, this list of conditions and the following disclaimer in 015 * the documentation and/or other materials provided with the 016 * distribution. 017 * 018 * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, 019 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY 020 * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL 021 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY DIRECT, INDIRECT, 022 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 023 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 024 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 025 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 026 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 027 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 028 * 029 * $JOMC: VersionParser.jj 5091 2016-04-04 15:40:17Z schulte $ 030 * 031 */ 032package org.jomc.util; 033 034import java.io.StringReader; 035import java.text.MessageFormat; 036import java.text.NumberFormat; 037import java.util.List; 038import java.util.LinkedList; 039import java.util.Locale; 040import java.util.ResourceBundle; 041 042/** 043 * Parses and compares version identifiers. 044 * <p><blockquote><pre> 045 * Version ::= Token ( ( <SEPARATOR> )* Token )* <EOF> 046 * Token ::= <INTEGER> 047 * | <IDENTIFIER> 048 * </pre></blockquote></p> 049 * <p> 050 * A separator character is defined as<blockquote><pre> 051 * [".","_","-","@","/","\\"," ","\t","\n","\r","\f","\b","\"","\'"]</pre></blockquote> 052 * An integer is a sequence of digits. An identifier is everything else, not 053 * a separator character or an integer. 054 * </p> 055 * 056 * @author <a href="mailto:cs@schulte.it">Christian Schulte</a> 057 * @version $JOMC: VersionParser.jj 5091 2016-04-04 15:40:17Z schulte $ 058 * @see #compare(String, String) 059 */ 060public final class VersionParser implements VersionParserConstants { 061 062 /** 063 * Parses the input to produce an array of tokens. 064 * 065 * @return The parsed tokens. 066 * 067 * @throws ParseException if the parse fails. 068 * @throws TokenMgrError for any invalid tokens. 069 */ 070 public Token[] parse() throws ParseException, TokenMgrError 071 { 072 return this.Version(); 073 } 074 075 /** 076 * Compares two versions for order. 077 * <p>This method parses the given strings to produce a sequence of tokens and then compares these tokens for 078 * order.</p> 079 * 080 * @param v1 The version to compare with. 081 * @param v2 The version to compare to. 082 * 083 * @return A negative integer, zero, or a positive integer as the first version is less than, equal to, or greater 084 * than the second. 085 * 086 * @throws NullPointerException if {@code v1} or {@code v2} is {@code null}. 087 * @throws ParseException if parsing fails. 088 * @throws TokenMgrError for any invalid tokens. 089 */ 090 public static int compare( final String v1, final String v2 ) throws ParseException, TokenMgrError 091 { 092 if ( v1 == null ) 093 { 094 throw new NullPointerException( "v1" ); 095 } 096 if ( v2 == null ) 097 { 098 throw new NullPointerException( "v2" ); 099 } 100 101 try 102 { 103 final NumberFormat format = NumberFormat.getNumberInstance( Locale.ENGLISH ); 104 final StringReader v1Reader = new StringReader( v1 ); 105 final VersionParser versionParser = new VersionParser( v1Reader ); 106 final Token[] c = versionParser.parse(); 107 final StringReader v2Reader = new StringReader( v2 ); 108 versionParser.ReInit( v2Reader ); 109 final Token[] r = versionParser.parse(); 110 final int len = Math.max( c.length, r.length ); 111 int result = 0; 112 113 v1Reader.close(); 114 v2Reader.close(); 115 116 for ( int i = 0; i < len; i++ ) 117 { 118 final Token current; 119 final Token spec; 120 121 if ( i < c.length ) 122 { 123 current = c[i]; 124 } 125 else 126 { 127 current = new Token(); 128 current.kind = r[i].kind; 129 130 if ( r[i].kind == VersionParserConstants.IDENTIFIER ) 131 { 132 // If a version has less tokens than another, comparison is stopped 133 // at the first identifier. Remaining tokens are considered suffices 134 // less than the shorter version. 135 result = 1; 136 break; 137 } 138 else if ( r[i].kind == VersionParserConstants.INTEGER ) 139 { 140 current.image = "0"; 141 } 142 } 143 144 if ( i < r.length ) 145 { 146 spec = r[i]; 147 } 148 else 149 { 150 spec = new Token(); 151 spec.kind = c[i].kind; 152 153 if ( c[i].kind == VersionParserConstants.IDENTIFIER ) 154 { 155 // If a version has less tokens than another, comparison is stopped 156 // at the first identifier. Remaining tokens are considered suffices 157 // less than the shorter version. 158 result = -1; 159 break; 160 } 161 else if ( c[i].kind == VersionParserConstants.INTEGER ) 162 { 163 spec.image = "0"; 164 } 165 } 166 167 if ( current.kind != spec.kind ) 168 { 169 throw new ParseException( getMessage( "cannotCompare", current.image, spec.image, v1, v2 ) ); 170 } 171 172 if ( current.kind == VersionParserConstants.IDENTIFIER ) 173 { 174 result = current.image.compareTo( spec.image ); 175 if ( result != 0 ) 176 { 177 break; 178 } 179 } 180 else if ( current.kind == VersionParserConstants.INTEGER ) 181 { 182 final Long m = (Long) format.parse( current.image ); 183 final Long n = (Long) format.parse( spec.image ); 184 185 result = m.compareTo( n ); 186 187 if ( result != 0 ) 188 { 189 break; 190 } 191 } 192 else 193 { 194 // Unsupported tokens are compared lexicographically by default. 195 result = current.image.compareTo( spec.image ); 196 if ( result != 0 ) 197 { 198 break; 199 } 200 } 201 } 202 203 return result; 204 } 205 catch ( java.text.ParseException e ) 206 { 207 throw new ParseException( e.getMessage() ); 208 } 209 } 210 211 private static String getMessage( final String key, final Object... arguments ) 212 { 213 return MessageFormat.format( ResourceBundle.getBundle( VersionParser.class.getName().replace( '.', '/' ), 214 Locale.getDefault() ).getString( key ), 215 arguments ); 216 217 } 218 219 final private Token[] Version() throws ParseException { 220 final List tokens = new LinkedList(); 221 Token(tokens); 222 label_1: 223 while (true) { 224 switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { 225 case INTEGER: 226 case SEPARATOR: 227 case IDENTIFIER: 228 ; 229 break; 230 default: 231 jj_la1[0] = jj_gen; 232 break label_1; 233 } 234 label_2: 235 while (true) { 236 switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { 237 case SEPARATOR: 238 ; 239 break; 240 default: 241 jj_la1[1] = jj_gen; 242 break label_2; 243 } 244 jj_consume_token(SEPARATOR); 245 } 246 Token(tokens); 247 } 248 jj_consume_token(0); 249 {if (true) return (Token[]) tokens.toArray(new Token[tokens.size()]);} 250 throw new Error("Missing return statement in function"); 251 } 252 253 final private void Token(final List tokens) throws ParseException { 254 Token part; 255 switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { 256 case INTEGER: 257 part = jj_consume_token(INTEGER); 258 tokens.add ( part ); 259 break; 260 case IDENTIFIER: 261 part = jj_consume_token(IDENTIFIER); 262 tokens.add( part ); 263 break; 264 default: 265 jj_la1[2] = jj_gen; 266 jj_consume_token(-1); 267 throw new ParseException(); 268 } 269 } 270 271 /** Generated Token Manager. */ 272 public VersionParserTokenManager token_source; 273 SimpleCharStream jj_input_stream; 274 /** Current token. */ 275 public Token token; 276 /** Next token. */ 277 public Token jj_nt; 278 private int jj_ntk; 279 private int jj_gen; 280 final private int[] jj_la1 = new int[3]; 281 static private int[] jj_la1_0; 282 static { 283 jj_la1_init_0(); 284 } 285 private static void jj_la1_init_0() { 286 jj_la1_0 = new int[] {0xe,0x4,0xa,}; 287 } 288 289 /** Constructor with InputStream. */ 290 public VersionParser(java.io.InputStream stream) { 291 this(stream, null); 292 } 293 /** Constructor with InputStream and supplied encoding */ 294 public VersionParser(java.io.InputStream stream, String encoding) { 295 try { jj_input_stream = new SimpleCharStream(stream, encoding, 1, 1); } catch(java.io.UnsupportedEncodingException e) { throw new RuntimeException(e); } 296 token_source = new VersionParserTokenManager(jj_input_stream); 297 token = new Token(); 298 jj_ntk = -1; 299 jj_gen = 0; 300 for (int i = 0; i < 3; i++) jj_la1[i] = -1; 301 } 302 303 /** Reinitialise. */ 304 public void ReInit(java.io.InputStream stream) { 305 ReInit(stream, null); 306 } 307 /** Reinitialise. */ 308 public void ReInit(java.io.InputStream stream, String encoding) { 309 try { jj_input_stream.ReInit(stream, encoding, 1, 1); } catch(java.io.UnsupportedEncodingException e) { throw new RuntimeException(e); } 310 token_source.ReInit(jj_input_stream); 311 token = new Token(); 312 jj_ntk = -1; 313 jj_gen = 0; 314 for (int i = 0; i < 3; i++) jj_la1[i] = -1; 315 } 316 317 /** Constructor. */ 318 public VersionParser(java.io.Reader stream) { 319 jj_input_stream = new SimpleCharStream(stream, 1, 1); 320 token_source = new VersionParserTokenManager(jj_input_stream); 321 token = new Token(); 322 jj_ntk = -1; 323 jj_gen = 0; 324 for (int i = 0; i < 3; i++) jj_la1[i] = -1; 325 } 326 327 /** Reinitialise. */ 328 public void ReInit(java.io.Reader stream) { 329 jj_input_stream.ReInit(stream, 1, 1); 330 token_source.ReInit(jj_input_stream); 331 token = new Token(); 332 jj_ntk = -1; 333 jj_gen = 0; 334 for (int i = 0; i < 3; i++) jj_la1[i] = -1; 335 } 336 337 /** Constructor with generated Token Manager. */ 338 public VersionParser(VersionParserTokenManager tm) { 339 token_source = tm; 340 token = new Token(); 341 jj_ntk = -1; 342 jj_gen = 0; 343 for (int i = 0; i < 3; i++) jj_la1[i] = -1; 344 } 345 346 /** Reinitialise. */ 347 public void ReInit(VersionParserTokenManager tm) { 348 token_source = tm; 349 token = new Token(); 350 jj_ntk = -1; 351 jj_gen = 0; 352 for (int i = 0; i < 3; i++) jj_la1[i] = -1; 353 } 354 355 private Token jj_consume_token(int kind) throws ParseException { 356 Token oldToken; 357 if ((oldToken = token).next != null) token = token.next; 358 else token = token.next = token_source.getNextToken(); 359 jj_ntk = -1; 360 if (token.kind == kind) { 361 jj_gen++; 362 return token; 363 } 364 token = oldToken; 365 jj_kind = kind; 366 throw generateParseException(); 367 } 368 369 370/** Get the next Token. */ 371 final public Token getNextToken() { 372 if (token.next != null) token = token.next; 373 else token = token.next = token_source.getNextToken(); 374 jj_ntk = -1; 375 jj_gen++; 376 return token; 377 } 378 379/** Get the specific Token. */ 380 final public Token getToken(int index) { 381 Token t = token; 382 for (int i = 0; i < index; i++) { 383 if (t.next != null) t = t.next; 384 else t = t.next = token_source.getNextToken(); 385 } 386 return t; 387 } 388 389 private int jj_ntk() { 390 if ((jj_nt=token.next) == null) 391 return (jj_ntk = (token.next=token_source.getNextToken()).kind); 392 else 393 return (jj_ntk = jj_nt.kind); 394 } 395 396 private java.util.List jj_expentries = new java.util.ArrayList(); 397 private int[] jj_expentry; 398 private int jj_kind = -1; 399 400 /** Generate ParseException. */ 401 public ParseException generateParseException() { 402 jj_expentries.clear(); 403 boolean[] la1tokens = new boolean[4]; 404 if (jj_kind >= 0) { 405 la1tokens[jj_kind] = true; 406 jj_kind = -1; 407 } 408 for (int i = 0; i < 3; i++) { 409 if (jj_la1[i] == jj_gen) { 410 for (int j = 0; j < 32; j++) { 411 if ((jj_la1_0[i] & (1<<j)) != 0) { 412 la1tokens[j] = true; 413 } 414 } 415 } 416 } 417 for (int i = 0; i < 4; i++) { 418 if (la1tokens[i]) { 419 jj_expentry = new int[1]; 420 jj_expentry[0] = i; 421 jj_expentries.add(jj_expentry); 422 } 423 } 424 int[][] exptokseq = new int[jj_expentries.size()][]; 425 for (int i = 0; i < jj_expentries.size(); i++) { 426 exptokseq[i] = (int[])jj_expentries.get(i); 427 } 428 return new ParseException(token, exptokseq, tokenImage); 429 } 430 431 /** Enable tracing. */ 432 final public void enable_tracing() { 433 } 434 435 /** Disable tracing. */ 436 final public void disable_tracing() { 437 } 438 439}