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 402 2009-08-25 13:51:07Z 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 402 2009-08-25 13:51:07Z 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
078 * and then compares these tokens for 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
084 * version is less than, equal to, or greater 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 )
091 throws ParseException, TokenMgrError
092 {
093 if ( v1 == null )
094 {
095 throw new NullPointerException( "v1" );
096 }
097 if ( v2 == null )
098 {
099 throw new NullPointerException( "v2" );
100 }
101
102 try
103 {
104 final NumberFormat format =
105 NumberFormat.getNumberInstance( Locale.ENGLISH );
106
107 final VersionParser versionParser =
108 new VersionParser( new StringReader( v1 ) );
109
110 final Token[] c = versionParser.parse();
111
112 versionParser.ReInit( new StringReader( v2 ) );
113 final Token[] r = versionParser.parse();
114
115 final int len = Math.max( c.length, r.length );
116
117 int result = 0;
118
119 for ( int i = 0; i < len; i++ )
120 {
121 final Token current;
122 final Token spec;
123
124 if ( i < c.length )
125 {
126 current = c[i];
127 }
128 else
129 {
130 current = new Token();
131 current.kind = r[i].kind;
132
133 if ( r[i].kind == VersionParserConstants.IDENTIFIER )
134 {
135 // If a version has less tokens than another, comparison is stopped
136 // at the first identifier. Remaining tokens are considered suffices
137 // less than the shorter version.
138 result = 1;
139 break;
140 }
141 else if ( r[i].kind == VersionParserConstants.INTEGER )
142 {
143 current.image = "0";
144 }
145 }
146
147 if ( i < r.length )
148 {
149 spec = r[i];
150 }
151 else
152 {
153 spec = new Token();
154 spec.kind = c[i].kind;
155
156 if ( c[i].kind == VersionParserConstants.IDENTIFIER )
157 {
158 // If a version has less tokens than another, comparison is stopped
159 // at the first identifier. Remaining tokens are considered suffices
160 // less than the shorter version.
161 result = -1;
162 break;
163 }
164 else if ( c[i].kind == VersionParserConstants.INTEGER )
165 {
166 spec.image = "0";
167 }
168 }
169
170 if ( current.kind != spec.kind )
171 {
172 final java.text.MessageFormat f =
173 new java.text.MessageFormat( getMessage( "cannotCompare" ) );
174
175 throw new ParseException(
176 f.format( new Object[] { current.image, spec.image, v1, v2 } ) );
177
178 }
179
180 if ( current.kind == VersionParserConstants.IDENTIFIER )
181 {
182 result = current.image.compareTo( spec.image );
183 if ( result != 0 )
184 {
185 break;
186 }
187 }
188 else if ( current.kind == VersionParserConstants.INTEGER )
189 {
190 final Long m = (Long) format.parse( current.image );
191 final Long n = (Long) format.parse( spec.image );
192
193 result = m.compareTo( n );
194
195 if ( result != 0 )
196 {
197 break;
198 }
199 }
200 else
201 {
202 // Unsupported tokens are compared lexicographically by default.
203 result = current.image.compareTo( spec.image );
204 if ( result != 0 )
205 {
206 break;
207 }
208 }
209 }
210
211 return result;
212 }
213 catch ( java.text.ParseException e )
214 {
215 throw new ParseException( e.getMessage() );
216 }
217 }
218
219 private static String getMessage( final String key )
220 {
221 return java.util.ResourceBundle.getBundle(
222 VersionParser.class.getName().replace( '.', '/' ),
223 java.util.Locale.getDefault() ).getString( key );
224
225 }
226
227 final private Token[] Version() throws ParseException {
228 final List tokens = new LinkedList();
229 Token(tokens);
230 label_1:
231 while (true) {
232 switch ((jj_ntk==-1)?jj_ntk():jj_ntk) {
233 case INTEGER:
234 case SEPARATOR:
235 case IDENTIFIER:
236 ;
237 break;
238 default:
239 jj_la1[0] = jj_gen;
240 break label_1;
241 }
242 label_2:
243 while (true) {
244 switch ((jj_ntk==-1)?jj_ntk():jj_ntk) {
245 case SEPARATOR:
246 ;
247 break;
248 default:
249 jj_la1[1] = jj_gen;
250 break label_2;
251 }
252 jj_consume_token(SEPARATOR);
253 }
254 Token(tokens);
255 }
256 jj_consume_token(0);
257 {if (true) return (Token[]) tokens.toArray(new Token[tokens.size()]);}
258 throw new Error("Missing return statement in function");
259 }
260
261 final private void Token(final List tokens) throws ParseException {
262 Token part;
263 switch ((jj_ntk==-1)?jj_ntk():jj_ntk) {
264 case INTEGER:
265 part = jj_consume_token(INTEGER);
266 tokens.add ( part );
267 break;
268 case IDENTIFIER:
269 part = jj_consume_token(IDENTIFIER);
270 tokens.add( part );
271 break;
272 default:
273 jj_la1[2] = jj_gen;
274 jj_consume_token(-1);
275 throw new ParseException();
276 }
277 }
278
279 /** Generated Token Manager. */
280 public VersionParserTokenManager token_source;
281 SimpleCharStream jj_input_stream;
282 /** Current token. */
283 public Token token;
284 /** Next token. */
285 public Token jj_nt;
286 private int jj_ntk;
287 private int jj_gen;
288 final private int[] jj_la1 = new int[3];
289 static private int[] jj_la1_0;
290 static {
291 jj_la1_init_0();
292 }
293 private static void jj_la1_init_0() {
294 jj_la1_0 = new int[] {0xe,0x4,0xa,};
295 }
296
297 /** Constructor with InputStream. */
298 public VersionParser(java.io.InputStream stream) {
299 this(stream, null);
300 }
301 /** Constructor with InputStream and supplied encoding */
302 public VersionParser(java.io.InputStream stream, String encoding) {
303 try { jj_input_stream = new SimpleCharStream(stream, encoding, 1, 1); } catch(java.io.UnsupportedEncodingException e) { throw new RuntimeException(e); }
304 token_source = new VersionParserTokenManager(jj_input_stream);
305 token = new Token();
306 jj_ntk = -1;
307 jj_gen = 0;
308 for (int i = 0; i < 3; i++) jj_la1[i] = -1;
309 }
310
311 /** Reinitialise. */
312 public void ReInit(java.io.InputStream stream) {
313 ReInit(stream, null);
314 }
315 /** Reinitialise. */
316 public void ReInit(java.io.InputStream stream, String encoding) {
317 try { jj_input_stream.ReInit(stream, encoding, 1, 1); } catch(java.io.UnsupportedEncodingException e) { throw new RuntimeException(e); }
318 token_source.ReInit(jj_input_stream);
319 token = new Token();
320 jj_ntk = -1;
321 jj_gen = 0;
322 for (int i = 0; i < 3; i++) jj_la1[i] = -1;
323 }
324
325 /** Constructor. */
326 public VersionParser(java.io.Reader stream) {
327 jj_input_stream = new SimpleCharStream(stream, 1, 1);
328 token_source = new VersionParserTokenManager(jj_input_stream);
329 token = new Token();
330 jj_ntk = -1;
331 jj_gen = 0;
332 for (int i = 0; i < 3; i++) jj_la1[i] = -1;
333 }
334
335 /** Reinitialise. */
336 public void ReInit(java.io.Reader stream) {
337 jj_input_stream.ReInit(stream, 1, 1);
338 token_source.ReInit(jj_input_stream);
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 /** Constructor with generated Token Manager. */
346 public VersionParser(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 /** Reinitialise. */
355 public void ReInit(VersionParserTokenManager tm) {
356 token_source = tm;
357 token = new Token();
358 jj_ntk = -1;
359 jj_gen = 0;
360 for (int i = 0; i < 3; i++) jj_la1[i] = -1;
361 }
362
363 private Token jj_consume_token(int kind) throws ParseException {
364 Token oldToken;
365 if ((oldToken = token).next != null) token = token.next;
366 else token = token.next = token_source.getNextToken();
367 jj_ntk = -1;
368 if (token.kind == kind) {
369 jj_gen++;
370 return token;
371 }
372 token = oldToken;
373 jj_kind = kind;
374 throw generateParseException();
375 }
376
377
378 /** Get the next Token. */
379 final public Token getNextToken() {
380 if (token.next != null) token = token.next;
381 else token = token.next = token_source.getNextToken();
382 jj_ntk = -1;
383 jj_gen++;
384 return token;
385 }
386
387 /** Get the specific Token. */
388 final public Token getToken(int index) {
389 Token t = token;
390 for (int i = 0; i < index; i++) {
391 if (t.next != null) t = t.next;
392 else t = t.next = token_source.getNextToken();
393 }
394 return t;
395 }
396
397 private int jj_ntk() {
398 if ((jj_nt=token.next) == null)
399 return (jj_ntk = (token.next=token_source.getNextToken()).kind);
400 else
401 return (jj_ntk = jj_nt.kind);
402 }
403
404 private java.util.List jj_expentries = new java.util.ArrayList();
405 private int[] jj_expentry;
406 private int jj_kind = -1;
407
408 /** Generate ParseException. */
409 public ParseException generateParseException() {
410 jj_expentries.clear();
411 boolean[] la1tokens = new boolean[4];
412 if (jj_kind >= 0) {
413 la1tokens[jj_kind] = true;
414 jj_kind = -1;
415 }
416 for (int i = 0; i < 3; i++) {
417 if (jj_la1[i] == jj_gen) {
418 for (int j = 0; j < 32; j++) {
419 if ((jj_la1_0[i] & (1<<j)) != 0) {
420 la1tokens[j] = true;
421 }
422 }
423 }
424 }
425 for (int i = 0; i < 4; i++) {
426 if (la1tokens[i]) {
427 jj_expentry = new int[1];
428 jj_expentry[0] = i;
429 jj_expentries.add(jj_expentry);
430 }
431 }
432 int[][] exptokseq = new int[jj_expentries.size()][];
433 for (int i = 0; i < jj_expentries.size(); i++) {
434 exptokseq[i] = (int[])jj_expentries.get(i);
435 }
436 return new ParseException(token, exptokseq, tokenImage);
437 }
438
439 /** Enable tracing. */
440 final public void enable_tracing() {
441 }
442
443 /** Disable tracing. */
444 final public void disable_tracing() {
445 }
446
447 }