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 ( ( &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 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    }