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 }