View Javadoc
1   /*
2    * @(#)VersionID.java	1.7 05/11/17
3    * 
4    * Copyright (c) 2006 Sun Microsystems, Inc. All Rights Reserved.
5    *
6    * Redistribution and use in source and binary forms, with or without
7    * modification, are permitted provided that the following conditions are met:
8    *
9    * -Redistribution of source code must retain the above copyright notice, this
10   *  list of conditions and the following disclaimer.
11   *
12   * -Redistribution in binary form must reproduce the above copyright notice,
13   *  this list of conditions and the following disclaimer in the documentation
14   *  and/or other materials provided with the distribution.
15   *
16   * Neither the name of Sun Microsystems, Inc. or the names of contributors may
17   * be used to endorse or promote products derived from this software without
18   * specific prior written permission.
19   *
20   * This software is provided "AS IS," without a warranty of any kind. ALL
21   * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, INCLUDING
22   * ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE
23   * OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN MIDROSYSTEMS, INC. ("SUN")
24   * AND ITS LICENSORS SHALL NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE
25   * AS A RESULT OF USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
26   * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR ANY LOST
27   * REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL,
28   * INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY
29   * OF LIABILITY, ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE,
30   * EVEN IF SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
31   *
32   * You acknowledge that this software is not designed, licensed or intended
33   * for use in the design, construction, operation or maintenance of any
34   * nuclear facility.
35   */
36  
37  package jnlp.sample.util;
38  
39  import java.util.ArrayList;
40  import java.util.Arrays;
41  import java.util.List;
42  
43  /**
44   * VersionID contains a JNLP version ID.
45   * <p/>
46   * The VersionID also contains a prefix indicator that can
47   * be used when stored with a VersionString
48   */
49  public class VersionID
50      implements Comparable
51  {
52      private String[] _tuple;   // Array of Integer or String objects
53  
54      private boolean _usePrefixMatch;   // star (*) prefix
55  
56      private boolean _useGreaterThan;  // plus (+) greather-than
57  
58      private boolean _isCompound;       // and (&) operator
59  
60      private VersionID _rest;            // remaining part after the &
61  
62      /**
63       * Creates a VersionID object
64       */
65      public VersionID( String str )
66      {
67          _usePrefixMatch = false;
68          _useGreaterThan = false;
69          _isCompound = false;
70          if ( str == null || str.length() == 0 )
71          {
72              _tuple = new String[0];
73              return;
74          }
75  
76          // Check for compound
77          int amp = str.indexOf( "&" );
78          if ( amp >= 0 )
79          {
80              _isCompound = true;
81              VersionID firstPart = new VersionID( str.substring( 0, amp ) );
82              _rest = new VersionID( str.substring( amp + 1 ) );
83              _tuple = firstPart._tuple;
84              _usePrefixMatch = firstPart._usePrefixMatch;
85              _useGreaterThan = firstPart._useGreaterThan;
86          }
87          else
88          {
89              // Check for postfix
90              if ( str.endsWith( "+" ) )
91              {
92                  _useGreaterThan = true;
93                  str = str.substring( 0, str.length() - 1 );
94              }
95              else if ( str.endsWith( "*" ) )
96              {
97                  _usePrefixMatch = true;
98                  str = str.substring( 0, str.length() - 1 );
99              }
100 
101             List<String> list = new ArrayList<String>();
102             int start = 0;
103             for ( int i = 0; i < str.length(); i++ )
104             {
105                 // Split at each separator character
106                 if ( ".-_".indexOf( str.charAt( i ) ) != -1 )
107                 {
108                     if ( start < i )
109                     {
110                         String value = str.substring( start, i );
111                         list.add( value );
112                     }
113                     start = i + 1;
114                 }
115             }
116             if ( start < str.length() )
117             {
118                 list.add( str.substring( start, str.length() ) );
119             }
120             _tuple = new String[list.size()];
121             _tuple = list.toArray( _tuple );
122         }
123     }
124 
125     /**
126      * Returns true if no flags are set
127      */
128     public boolean isSimpleVersion()
129     {
130         return !_useGreaterThan && !_usePrefixMatch && !_isCompound;
131     }
132 
133     /**
134      * Match 'this' versionID against vid.
135      * The _usePrefixMatch/_useGreaterThan flag is used to determine if a
136      * prefix match of an exact match should be performed
137      * if _isCompound, must match _rest also.
138      */
139     public boolean match( VersionID vid )
140     {
141         if ( _isCompound )
142         {
143             if ( !_rest.match( vid ) )
144             {
145                 return false;
146             }
147         }
148         return ( _usePrefixMatch )
149             ? this.isPrefixMatch( vid )
150             : ( _useGreaterThan ) ? vid.isGreaterThanOrEqual( this ) : matchTuple( vid );
151     }
152 
153     /**
154      * Compares if two version IDs are equal
155      */
156     public boolean equals( Object o )
157     {
158         if ( matchTuple( o ) )
159         {
160             VersionID ov = (VersionID) o;
161             if ( _rest == null || _rest.equals( ov._rest ) )
162             {
163                 if ( ( _useGreaterThan == ov._useGreaterThan ) && ( _usePrefixMatch == ov._usePrefixMatch ) )
164                 {
165                     return true;
166                 }
167             }
168         }
169         return false;
170     }
171 
172     /**
173      * Compares if two version IDs are equal
174      */
175     private boolean matchTuple( Object o )
176     {
177         // Check for null and type
178         if ( o == null || !( o instanceof VersionID ) )
179         {
180             return false;
181         }
182         VersionID vid = (VersionID) o;
183 
184         // Normalize arrays
185         String[] t1 = normalize( _tuple, vid._tuple.length );
186         String[] t2 = normalize( vid._tuple, _tuple.length );
187 
188         // Check contents
189         for ( int i = 0; i < t1.length; i++ )
190         {
191             Object o1 = getValueAsObject( t1[i] );
192             Object o2 = getValueAsObject( t2[i] );
193             if ( !o1.equals( o2 ) )
194             {
195                 return false;
196             }
197         }
198         return true;
199     }
200 
201     private Object getValueAsObject( String value )
202     {
203         if ( value.length() > 0 && value.charAt( 0 ) != '-' )
204         {
205             try
206             {
207                 return Integer.valueOf( value );
208             }
209             catch ( NumberFormatException nfe )
210             { /* fall through */ }
211         }
212         return value;
213     }
214 
215     public boolean isGreaterThan( VersionID vid )
216     {
217         return isGreaterThanOrEqualHelper( vid, false );
218     }
219 
220     public boolean isGreaterThanOrEqual( VersionID vid )
221     {
222         return isGreaterThanOrEqualHelper( vid, true );
223     }
224 
225     /**
226      * Compares if 'this' is greater than vid
227      */
228     private boolean isGreaterThanOrEqualHelper( VersionID vid, boolean allowEqual )
229     {
230 
231         if ( _isCompound )
232         {
233             if ( !_rest.isGreaterThanOrEqualHelper( vid, allowEqual ) )
234             {
235                 return false;
236             }
237         }
238         // Normalize the two strings
239         String[] t1 = normalize( _tuple, vid._tuple.length );
240         String[] t2 = normalize( vid._tuple, _tuple.length );
241 
242         for ( int i = 0; i < t1.length; i++ )
243         {
244             // Compare current element
245             Object e1 = getValueAsObject( t1[i] );
246             Object e2 = getValueAsObject( t2[i] );
247             if ( e1.equals( e2 ) )
248             {
249                 // So far so good
250             }
251             else
252             {
253                 if ( e1 instanceof Integer && e2 instanceof Integer )
254                 {
255                     return (Integer) e1 > (Integer) e2;
256                 }
257                 else
258                 {
259                     String s1 = t1[i];
260                     String s2 = t2[i];
261                     return s1.compareTo( s2 ) > 0;
262                 }
263 
264             }
265         }
266         // If we get here, they are equal
267         return allowEqual;
268     }
269 
270     /**
271      * Checks if 'this' is a prefix of vid
272      */
273     public boolean isPrefixMatch( VersionID vid )
274     {
275 
276         if ( _isCompound )
277         {
278             if ( !_rest.isPrefixMatch( vid ) )
279             {
280                 return false;
281             }
282         }
283         // Make sure that vid is at least as long as the prefix
284         String[] t2 = normalize( vid._tuple, _tuple.length );
285 
286         for ( int i = 0; i < _tuple.length; i++ )
287         {
288             Object e1 = _tuple[i];
289             Object e2 = t2[i];
290             if ( e1.equals( e2 ) )
291             {
292                 // So far so good
293             }
294             else
295             {
296                 // Not a prefix
297                 return false;
298             }
299         }
300         return true;
301     }
302 
303     /**
304      * Normalize an array to a certain lengh
305      */
306     private String[] normalize( String[] list, int minlength )
307     {
308         if ( list.length < minlength )
309         {
310             // Need to do padding
311             String[] newlist = new String[minlength];
312             System.arraycopy( list, 0, newlist, 0, list.length );
313             Arrays.fill( newlist, list.length, newlist.length, "0" );
314             return newlist;
315         }
316         else
317         {
318             return list;
319         }
320     }
321 
322     public int compareTo( Object o )
323     {
324         if ( o == null || !( o instanceof VersionID ) )
325         {
326             return -1;
327         }
328         VersionID vid = (VersionID) o;
329         return equals( vid ) ? 0 : ( isGreaterThanOrEqual( vid ) ? 1 : -1 );
330     }
331 
332     /**
333      * Show it as a string
334      */
335     public String toString()
336     {
337         StringBuilder sb = new StringBuilder();
338         for ( int i = 0; i < _tuple.length - 1; i++ )
339         {
340             sb.append( _tuple[i] );
341             sb.append( '.' );
342         }
343         if ( _tuple.length > 0 )
344         {
345             sb.append( _tuple[_tuple.length - 1] );
346         }
347         if ( _usePrefixMatch )
348         {
349             sb.append( '+' );
350         }
351         return sb.toString();
352     }
353 }
354