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 ( ( &lt;SEPARATOR&gt; )* Token )* &lt;EOF&gt;
046     * Token      ::= &lt;INTEGER&gt;
047     *              | &lt;IDENTIFIER&gt;
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    }