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