001: /*****************************************************************************
002: * Java Plug-in Framework (JPF)
003: * Copyright (C) 2004-2007 Dmitry Olshansky
004: *
005: * This library is free software; you can redistribute it and/or
006: * modify it under the terms of the GNU Lesser General Public
007: * License as published by the Free Software Foundation; either
008: * version 2.1 of the License, or (at your option) any later version.
009: *
010: * This library is distributed in the hope that it will be useful,
011: * but WITHOUT ANY WARRANTY; without even the implied warranty of
012: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
013: * Lesser General Public License for more details.
014: *
015: * You should have received a copy of the GNU Lesser General Public
016: * License along with this library; if not, write to the Free Software
017: * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
018: *****************************************************************************/package org.java.plugin.registry;
019:
020: import java.io.IOException;
021: import java.io.ObjectInputStream;
022: import java.io.ObjectOutputStream;
023: import java.io.Serializable;
024: import java.util.Locale;
025: import java.util.StringTokenizer;
026:
027: /**
028: * This class represents a plug-in version identifier.
029: * <br>
030: * @version $Id$
031: */
032: public final class Version implements Serializable, Comparable<Version> {
033: private static final long serialVersionUID = -3054349171116917643L;
034:
035: /**
036: * Version identifier parts separator.
037: */
038: public static final char SEPARATOR = '.';
039:
040: /**
041: * Parses given string as version identifier. All missing parts will be
042: * initialized to 0 or empty string. Parsing starts from left side of the
043: * string.
044: * @param str version identifier as string
045: * @return version identifier object
046: */
047: public static Version parse(final String str) {
048: Version result = new Version();
049: result.parseString(str);
050: return result;
051: }
052:
053: private transient int major;
054: private transient int minor;
055: private transient int build;
056: private transient String name;
057: private transient String asString;
058:
059: private Version() {
060: // no-op
061: }
062:
063: private void parseString(final String str) {
064: major = 0;
065: minor = 0;
066: build = 0;
067: name = ""; //$NON-NLS-1$
068: StringTokenizer st = new StringTokenizer(str,
069: "" + SEPARATOR, false); //$NON-NLS-1$
070: // major segment
071: if (!st.hasMoreTokens()) {
072: return;
073: }
074: String token = st.nextToken();
075: try {
076: major = Integer.parseInt(token, 10);
077: } catch (NumberFormatException nfe) {
078: name = token;
079: while (st.hasMoreTokens()) {
080: name += st.nextToken();
081: }
082: return;
083: }
084: // minor segment
085: if (!st.hasMoreTokens()) {
086: return;
087: }
088: token = st.nextToken();
089: try {
090: minor = Integer.parseInt(token, 10);
091: } catch (NumberFormatException nfe) {
092: name = token;
093: while (st.hasMoreTokens()) {
094: name += st.nextToken();
095: }
096: return;
097: }
098: // build segment
099: if (!st.hasMoreTokens()) {
100: return;
101: }
102: token = st.nextToken();
103: try {
104: build = Integer.parseInt(token, 10);
105: } catch (NumberFormatException nfe) {
106: name = token;
107: while (st.hasMoreTokens()) {
108: name += st.nextToken();
109: }
110: return;
111: }
112: // name segment
113: if (st.hasMoreTokens()) {
114: name = st.nextToken();
115: while (st.hasMoreTokens()) {
116: name += st.nextToken();
117: }
118: }
119: }
120:
121: /**
122: * Creates version identifier object from given parts. No validation
123: * performed during object instantiation, all values become parts of
124: * version identifier as they are.
125: * @param aMajor major version number
126: * @param aMinor minor version number
127: * @param aBuild build number
128: * @param aName build name, <code>null</code> value becomes empty string
129: */
130: public Version(final int aMajor, final int aMinor,
131: final int aBuild, final String aName) {
132: major = aMajor;
133: minor = aMinor;
134: build = aBuild;
135: name = (aName == null) ? "" : aName; //$NON-NLS-1$
136: }
137:
138: /**
139: * @return build number
140: */
141: public int getBuild() {
142: return build;
143: }
144:
145: /**
146: * @return major version number
147: */
148: public int getMajor() {
149: return major;
150: }
151:
152: /**
153: * @return minor version number
154: */
155: public int getMinor() {
156: return minor;
157: }
158:
159: /**
160: * @return build name
161: */
162: public String getName() {
163: return name;
164: }
165:
166: /**
167: * Compares two version identifiers to see if this one is
168: * greater than or equal to the argument.
169: * <p>
170: * A version identifier is considered to be greater than or equal
171: * if its major component is greater than the argument major
172: * component, or the major components are equal and its minor component
173: * is greater than the argument minor component, or the
174: * major and minor components are equal and its build component is
175: * greater than the argument build component, or all components are equal.
176: * </p>
177: *
178: * @param other the other version identifier
179: * @return <code>true</code> if this version identifier
180: * is compatible with the given version identifier, and
181: * <code>false</code> otherwise
182: */
183: public boolean isGreaterOrEqualTo(final Version other) {
184: if (other == null) {
185: return false;
186: }
187: if (major > other.major) {
188: return true;
189: }
190: if ((major == other.major) && (minor > other.minor)) {
191: return true;
192: }
193: if ((major == other.major) && (minor == other.minor)
194: && (build > other.build)) {
195: return true;
196: }
197: if ((major == other.major) && (minor == other.minor)
198: && (build == other.build)
199: && name.equalsIgnoreCase(other.name)) {
200: return true;
201: }
202: return false;
203: }
204:
205: /**
206: * Compares two version identifiers for compatibility.
207: * <p>
208: * A version identifier is considered to be compatible if its major
209: * component equals to the argument major component, and its minor component
210: * is greater than or equal to the argument minor component.
211: * If the minor components are equal, than the build component of the
212: * version identifier must be greater than or equal to the build component
213: * of the argument identifier.
214: * </p>
215: *
216: * @param other the other version identifier
217: * @return <code>true</code> if this version identifier
218: * is compatible with the given version identifier, and
219: * <code>false</code> otherwise
220: */
221: public boolean isCompatibleWith(final Version other) {
222: if (other == null) {
223: return false;
224: }
225: if (major != other.major) {
226: return false;
227: }
228: if (minor > other.minor) {
229: return true;
230: }
231: if (minor < other.minor) {
232: return false;
233: }
234: if (build >= other.build) {
235: return true;
236: }
237: return false;
238: }
239:
240: /**
241: * Compares two version identifiers for equivalency.
242: * <p>
243: * Two version identifiers are considered to be equivalent if their major
244: * and minor components equal and are at least at the same build level
245: * as the argument.
246: * </p>
247: *
248: * @param other the other version identifier
249: * @return <code>true</code> if this version identifier
250: * is equivalent to the given version identifier, and
251: * <code>false</code> otherwise
252: */
253: public boolean isEquivalentTo(final Version other) {
254: if (other == null) {
255: return false;
256: }
257: if (major != other.major) {
258: return false;
259: }
260: if (minor != other.minor) {
261: return false;
262: }
263: if (build >= other.build) {
264: return true;
265: }
266: return false;
267: }
268:
269: /**
270: * Compares two version identifiers for order using multi-decimal
271: * comparison.
272: *
273: * @param other the other version identifier
274: * @return <code>true</code> if this version identifier
275: * is greater than the given version identifier, and
276: * <code>false</code> otherwise
277: */
278: public boolean isGreaterThan(final Version other) {
279: if (other == null) {
280: return false;
281: }
282: if (major > other.major) {
283: return true;
284: }
285: if (major < other.major) {
286: return false;
287: }
288: if (minor > other.minor) {
289: return true;
290: }
291: if (minor < other.minor) {
292: return false;
293: }
294: if (build > other.build) {
295: return true;
296: }
297: return false;
298:
299: }
300:
301: /**
302: * @see java.lang.Object#hashCode()
303: */
304: @Override
305: public int hashCode() {
306: return toString().hashCode();
307: }
308:
309: /**
310: * @see java.lang.Object#equals(java.lang.Object)
311: */
312: @Override
313: public boolean equals(final Object obj) {
314: if (this == obj) {
315: return true;
316: }
317: if (!(obj instanceof Version)) {
318: return false;
319: }
320: Version other = (Version) obj;
321: if ((major != other.major) || (minor != other.minor)
322: || (build != other.build)
323: || !name.equalsIgnoreCase(other.name)) {
324: return false;
325: }
326: return true;
327: }
328:
329: /**
330: * Returns the string representation of this version identifier.
331: * The result satisfies
332: * <code>version.equals(new Version(version.toString()))</code>.
333: * @return the string representation of this version identifier
334: */
335: @Override
336: public String toString() {
337: if (asString == null) {
338: asString = "" + major + SEPARATOR + minor + SEPARATOR + build //$NON-NLS-1$
339: + (name.length() == 0 ? "" : SEPARATOR + name); //$NON-NLS-1$
340: }
341: return asString;
342: }
343:
344: /**
345: * @param obj version to compare this instance with
346: * @return comparison result
347: * @see java.lang.Comparable#compareTo(java.lang.Object)
348: */
349: public int compareTo(final Version obj) {
350: if (equals(obj)) {
351: return 0;
352: }
353: if (major != obj.major) {
354: return major - obj.major;
355: }
356: if (minor != obj.minor) {
357: return minor - obj.minor;
358: }
359: if (build != obj.build) {
360: return build - obj.build;
361: }
362: return name.toLowerCase(Locale.ENGLISH).compareTo(
363: obj.name.toLowerCase(Locale.ENGLISH));
364: }
365:
366: // Serialization related stuff.
367:
368: private void writeObject(final ObjectOutputStream out)
369: throws IOException {
370: out.writeUTF(toString());
371: }
372:
373: private void readObject(final ObjectInputStream in)
374: throws IOException {
375: parseString(in.readUTF());
376: }
377: }
|