View Javadoc

1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *   http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing,
13   * software distributed under the License is distributed on an
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   * KIND, either express or implied.  See the License for the
16   * specific language governing permissions and limitations
17   * under the License.
18   */
19  package org.apache.myfaces.trinidad.context;
20  
21  import java.util.Arrays;
22  import java.util.regex.Pattern;
23  
24  /**
25   * Immutable Representation of a dot-separated version.
26   * 
27   * This representation
28   * allows individual sections of the version to be wild-carded and allows
29   * for comparisons between Versions with different numbers of version
30   * subsections to be compared.  When comparing Versions, each version
31   * subsection is compared from left to right.  If one Version doesn't have
32   * a version subsection at the current index, the value of versionPadding
33   * is used for this comparison.  Version subsections with the wild-card value "*"
34   * are considered equal.  The value returned by compareTo() is the value of the
35   * first non-equal version subsection or zero if all subsections match.
36   * 
37   * Due to the support for wild-cards, this class has a natural ordering
38   * that is inconsistent with equals.  For example,
39   * <code>Version("5", "*").compareTo(Version("5.0", "*") == 0</code>
40   * <code>Version("5", "*").equals(Version("5.0", "*") == false;</code>
41   * @author Blake Sullivan
42   */
43  public final class Version implements Comparable<Version>
44  {
45    /**
46     * Creates a Version instance from the dot-separated Version String using null as the padding
47     * @param version The dot-separated version to represent
48     * @throws NullPointerException if the version is null
49     * @throws IllegalArgumentException if the version is an empty String
50     * @see #Version(String, String)
51     */
52    public Version(String version)
53    {
54      this(version, null);
55    }
56    
57    /**
58     * Creates a Version instance from the dot-separated Version String and the
59     * versionPadding.
60     * @param version The dot-separated version to represent
61     * @param versionPadding The value to return for sub-version sections
62     * requested beyond the sub-version sections present in the version String
63     * @throws NullPointerException if version or versionPadding are null
64     * @throws IllegalArgumentException if version or versionPadding are the
65     * empty String
66     */
67    public Version(String version, String versionPadding)
68    {
69      _checkNonEmptyString(version, "version");
70      if (versionPadding == null)
71      {
72        versionPadding = "";
73      }
74      
75      // build the array of subversions
76      _versions = _DOT_SPLITTER.split(version, 0);
77      _versionPadding = versionPadding;
78      
79      // since we're immutable, we might as well calculate this up front
80      // while we still have the String version around
81      _hashCode = version.hashCode() * 37 + versionPadding.hashCode();
82    }
83  
84    /**
85     * When comparing Versions, each version
86     * subsection is compared from left to right.  If one Version doesn't have
87     * a version subsection at the current index, the value of versionPadding
88     * is used for this comparison.  Version subsections with the wild-card value "*"
89     * care considered equal.  The value returned by compareTo() is the value of the
90     * first non-equal version subsection or zero if all subsections match.
91     * @param otherVersion The Version object to compare this Version Object with
92     * @return a negative integer, zero, or a positive integer as this object
93     *         is less than, equal to, or greater than the specified object.
94     */
95    public int compareTo(Version otherVersion)
96    {
97      int ourVersionCount = _versions.length;
98      int otherVersionCount = otherVersion._versions.length;
99      
100     int compareCount = (ourVersionCount > otherVersionCount)
101                          ? ourVersionCount
102                          : otherVersionCount;
103     
104     for (int versionIndex = 0; versionIndex < compareCount; versionIndex++)
105     {
106       String ourSubVersion = _getSubVersion(versionIndex);
107       String otherSubVersion = otherVersion._getSubVersion(versionIndex);
108       
109       // treat "*" wildcard as equals
110       if ("*".equals(ourSubVersion) || "*".equals(otherSubVersion))
111       {
112         continue;
113       }
114       else
115       {
116         // compare the sub-result
117         int result = ourSubVersion.compareTo(otherSubVersion);
118         
119         // not equal, so return the result
120         if (result != 0)
121           return result;
122       }
123     }
124     
125     // equivalent
126     return 0;
127   }
128   
129   @Override
130   public String toString()
131   {
132     // rebuild the initial version string from the split array
133     StringBuilder versionBuilder = new StringBuilder();
134     int versionCount = _versions.length;
135     
136     for (int i = 0;;)
137     {
138       versionBuilder.append(_versions[i]);
139       
140       i++;
141       
142       if (i != versionCount)
143         versionBuilder.append('.');
144       else
145         break;
146     }
147         
148     return versionBuilder.toString();
149   }
150   
151   @Override
152   public boolean equals(Object o)
153   {
154     if (o == this)
155       return true;
156     else if (!(o instanceof Version))
157       return false;
158     else
159     {
160       Version otherVersion = (Version)o;
161       
162       // we are equal if all of version content and padding are equal
163       return _versionPadding.equals(otherVersion._versionPadding) &&
164              Arrays.equals(_versions, otherVersion._versions);
165     }
166   }
167   
168   @Override
169   public int hashCode()
170   {
171     // used cached version
172     return _hashCode;
173   }
174   
175   /**
176    * Returns the contents of the sub-version section of the overall version,
177    * padding the result with the version padding if the version section
178    * index is greater than the number of actual version sections in this
179    * version
180    * @param versionIndex index of the "." version section from the left side
181    * of the version string.
182    * @return The content of the version section if available, otehrwise the
183    * versionPadding
184    */
185   private String _getSubVersion(int versionIndex)
186   {
187     if (versionIndex >= _versions.length)
188       return _versionPadding;
189     else
190       return _versions[versionIndex];
191   }
192 
193   
194   private void _checkNonEmptyString(String checkedString, String identifier)
195   {
196     if (checkedString == null)
197       throw new NullPointerException(identifier + " must be non-null");
198     
199     if (checkedString.length() == 0)
200       throw new IllegalArgumentException(identifier + " must be non-empty");
201   }
202   
203   private final String[] _versions;
204   private final String _versionPadding;
205   private final int _hashCode;
206   
207   // cache the compiled splitter
208   private static final Pattern _DOT_SPLITTER = Pattern.compile("\\.");
209 }