001 /* Generated By:JavaCC: Do not edit this line. VersionParser.java */ 002 /* 003 * Copyright (c) 2009 The JOMC Project 004 * Copyright (c) 2005 Christian Schulte <cs@jomc.org> 005 * All rights reserved. 006 * 007 * Redistribution and use in source and binary forms, with or without 008 * modification, are permitted provided that the following conditions 009 * are met: 010 * 011 * o Redistributions of source code must retain the above copyright 012 * notice, this list of conditions and the following disclaimer. 013 * 014 * o Redistributions in binary form must reproduce the above copyright 015 * notice, this list of conditions and the following disclaimer in 016 * the documentation and/or other materials provided with the 017 * distribution. 018 * 019 * THIS SOFTWARE IS PROVIDED BY THE JOMC PROJECT AND CONTRIBUTORS "AS IS" 020 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, 021 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 022 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE JOMC PROJECT OR 023 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 024 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 025 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; 026 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 027 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 028 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 029 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 030 * 031 * $Id: VersionParser.jj 733 2009-10-05 17:22:57Z schulte2005 $ 032 * 033 */ 034 package org.jomc.util; 035 036 import java.io.StringReader; 037 import java.util.List; 038 import java.util.LinkedList; 039 import java.util.Locale; 040 import java.text.NumberFormat; 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@jomc.org">Christian Schulte</a> 057 * @version $Id: VersionParser.jj 733 2009-10-05 17:22:57Z schulte2005 $ 058 * @see #compare(String, String) 059 */ 060 public 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 VersionParser versionParser = new VersionParser( new StringReader( v1 ) ); 105 final Token[] c = versionParser.parse(); 106 versionParser.ReInit( new StringReader( v2 ) ); 107 final Token[] r = versionParser.parse(); 108 final int len = Math.max( c.length, r.length ); 109 int result = 0; 110 111 for ( int i = 0; i < len; i++ ) 112 { 113 final Token current; 114 final Token spec; 115 116 if ( i < c.length ) 117 { 118 current = c[i]; 119 } 120 else 121 { 122 current = new Token(); 123 current.kind = r[i].kind; 124 125 if ( r[i].kind == VersionParserConstants.IDENTIFIER ) 126 { 127 // If a version has less tokens than another, comparison is stopped 128 // at the first identifier. Remaining tokens are considered suffices 129 // less than the shorter version. 130 result = 1; 131 break; 132 } 133 else if ( r[i].kind == VersionParserConstants.INTEGER ) 134 { 135 current.image = "0"; 136 } 137 } 138 139 if ( i < r.length ) 140 { 141 spec = r[i]; 142 } 143 else 144 { 145 spec = new Token(); 146 spec.kind = c[i].kind; 147 148 if ( c[i].kind == VersionParserConstants.IDENTIFIER ) 149 { 150 // If a version has less tokens than another, comparison is stopped 151 // at the first identifier. Remaining tokens are considered suffices 152 // less than the shorter version. 153 result = -1; 154 break; 155 } 156 else if ( c[i].kind == VersionParserConstants.INTEGER ) 157 { 158 spec.image = "0"; 159 } 160 } 161 162 if ( current.kind != spec.kind ) 163 { 164 final java.text.MessageFormat f = new java.text.MessageFormat( getMessage( "cannotCompare" ) ); 165 throw new ParseException( f.format( new Object[] 166 { 167 current.image, spec.image, v1, v2 168 } ) ); 169 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 ) 212 { 213 return java.util.ResourceBundle.getBundle( VersionParser.class.getName().replace( '.', '/' ), 214 java.util.Locale.getDefault() ).getString( key ); 215 216 } 217 218 final private Token[] Version() throws ParseException { 219 final List tokens = new LinkedList(); 220 Token(tokens); 221 label_1: 222 while (true) { 223 switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { 224 case INTEGER: 225 case SEPARATOR: 226 case IDENTIFIER: 227 ; 228 break; 229 default: 230 jj_la1[0] = jj_gen; 231 break label_1; 232 } 233 label_2: 234 while (true) { 235 switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { 236 case SEPARATOR: 237 ; 238 break; 239 default: 240 jj_la1[1] = jj_gen; 241 break label_2; 242 } 243 jj_consume_token(SEPARATOR); 244 } 245 Token(tokens); 246 } 247 jj_consume_token(0); 248 {if (true) return (Token[]) tokens.toArray(new Token[tokens.size()]);} 249 throw new Error("Missing return statement in function"); 250 } 251 252 final private void Token(final List tokens) throws ParseException { 253 Token part; 254 switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { 255 case INTEGER: 256 part = jj_consume_token(INTEGER); 257 tokens.add ( part ); 258 break; 259 case IDENTIFIER: 260 part = jj_consume_token(IDENTIFIER); 261 tokens.add( part ); 262 break; 263 default: 264 jj_la1[2] = jj_gen; 265 jj_consume_token(-1); 266 throw new ParseException(); 267 } 268 } 269 270 /** Generated Token Manager. */ 271 public VersionParserTokenManager token_source; 272 SimpleCharStream jj_input_stream; 273 /** Current token. */ 274 public Token token; 275 /** Next token. */ 276 public Token jj_nt; 277 private int jj_ntk; 278 private int jj_gen; 279 final private int[] jj_la1 = new int[3]; 280 static private int[] jj_la1_0; 281 static { 282 jj_la1_init_0(); 283 } 284 private static void jj_la1_init_0() { 285 jj_la1_0 = new int[] {0xe,0x4,0xa,}; 286 } 287 288 /** Constructor with InputStream. */ 289 public VersionParser(java.io.InputStream stream) { 290 this(stream, null); 291 } 292 /** Constructor with InputStream and supplied encoding */ 293 public VersionParser(java.io.InputStream stream, String encoding) { 294 try { jj_input_stream = new SimpleCharStream(stream, encoding, 1, 1); } catch(java.io.UnsupportedEncodingException e) { throw new RuntimeException(e); } 295 token_source = new VersionParserTokenManager(jj_input_stream); 296 token = new Token(); 297 jj_ntk = -1; 298 jj_gen = 0; 299 for (int i = 0; i < 3; i++) jj_la1[i] = -1; 300 } 301 302 /** Reinitialise. */ 303 public void ReInit(java.io.InputStream stream) { 304 ReInit(stream, null); 305 } 306 /** Reinitialise. */ 307 public void ReInit(java.io.InputStream stream, String encoding) { 308 try { jj_input_stream.ReInit(stream, encoding, 1, 1); } catch(java.io.UnsupportedEncodingException e) { throw new RuntimeException(e); } 309 token_source.ReInit(jj_input_stream); 310 token = new Token(); 311 jj_ntk = -1; 312 jj_gen = 0; 313 for (int i = 0; i < 3; i++) jj_la1[i] = -1; 314 } 315 316 /** Constructor. */ 317 public VersionParser(java.io.Reader stream) { 318 jj_input_stream = new SimpleCharStream(stream, 1, 1); 319 token_source = new VersionParserTokenManager(jj_input_stream); 320 token = new Token(); 321 jj_ntk = -1; 322 jj_gen = 0; 323 for (int i = 0; i < 3; i++) jj_la1[i] = -1; 324 } 325 326 /** Reinitialise. */ 327 public void ReInit(java.io.Reader stream) { 328 jj_input_stream.ReInit(stream, 1, 1); 329 token_source.ReInit(jj_input_stream); 330 token = new Token(); 331 jj_ntk = -1; 332 jj_gen = 0; 333 for (int i = 0; i < 3; i++) jj_la1[i] = -1; 334 } 335 336 /** Constructor with generated Token Manager. */ 337 public VersionParser(VersionParserTokenManager tm) { 338 token_source = tm; 339 token = new Token(); 340 jj_ntk = -1; 341 jj_gen = 0; 342 for (int i = 0; i < 3; i++) jj_la1[i] = -1; 343 } 344 345 /** Reinitialise. */ 346 public void ReInit(VersionParserTokenManager tm) { 347 token_source = tm; 348 token = new Token(); 349 jj_ntk = -1; 350 jj_gen = 0; 351 for (int i = 0; i < 3; i++) jj_la1[i] = -1; 352 } 353 354 private Token jj_consume_token(int kind) throws ParseException { 355 Token oldToken; 356 if ((oldToken = token).next != null) token = token.next; 357 else token = token.next = token_source.getNextToken(); 358 jj_ntk = -1; 359 if (token.kind == kind) { 360 jj_gen++; 361 return token; 362 } 363 token = oldToken; 364 jj_kind = kind; 365 throw generateParseException(); 366 } 367 368 369 /** Get the next Token. */ 370 final public Token getNextToken() { 371 if (token.next != null) token = token.next; 372 else token = token.next = token_source.getNextToken(); 373 jj_ntk = -1; 374 jj_gen++; 375 return token; 376 } 377 378 /** Get the specific Token. */ 379 final public Token getToken(int index) { 380 Token t = token; 381 for (int i = 0; i < index; i++) { 382 if (t.next != null) t = t.next; 383 else t = t.next = token_source.getNextToken(); 384 } 385 return t; 386 } 387 388 private int jj_ntk() { 389 if ((jj_nt=token.next) == null) 390 return (jj_ntk = (token.next=token_source.getNextToken()).kind); 391 else 392 return (jj_ntk = jj_nt.kind); 393 } 394 395 private java.util.List jj_expentries = new java.util.ArrayList(); 396 private int[] jj_expentry; 397 private int jj_kind = -1; 398 399 /** Generate ParseException. */ 400 public ParseException generateParseException() { 401 jj_expentries.clear(); 402 boolean[] la1tokens = new boolean[4]; 403 if (jj_kind >= 0) { 404 la1tokens[jj_kind] = true; 405 jj_kind = -1; 406 } 407 for (int i = 0; i < 3; i++) { 408 if (jj_la1[i] == jj_gen) { 409 for (int j = 0; j < 32; j++) { 410 if ((jj_la1_0[i] & (1<<j)) != 0) { 411 la1tokens[j] = true; 412 } 413 } 414 } 415 } 416 for (int i = 0; i < 4; i++) { 417 if (la1tokens[i]) { 418 jj_expentry = new int[1]; 419 jj_expentry[0] = i; 420 jj_expentries.add(jj_expentry); 421 } 422 } 423 int[][] exptokseq = new int[jj_expentries.size()][]; 424 for (int i = 0; i < jj_expentries.size(); i++) { 425 exptokseq[i] = (int[])jj_expentries.get(i); 426 } 427 return new ParseException(token, exptokseq, tokenImage); 428 } 429 430 /** Enable tracing. */ 431 final public void enable_tracing() { 432 } 433 434 /** Disable tracing. */ 435 final public void disable_tracing() { 436 } 437 438 }