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 }