001: /*
002: * ====================================================================
003: *
004: * The Apache Software License, Version 1.1
005: *
006: * Copyright (c) 1999-2003 The Apache Software Foundation.
007: * All rights reserved.
008: *
009: * Redistribution and use in source and binary forms, with or without
010: * modification, are permitted provided that the following conditions
011: * are met:
012: *
013: * 1. Redistributions of source code must retain the above copyright
014: * notice, this list of conditions and the following disclaimer.
015: *
016: * 2. Redistributions in binary form must reproduce the above copyright
017: * notice, this list of conditions and the following disclaimer in
018: * the documentation and/or other materials provided with the
019: * distribution.
020: *
021: * 3. The end-user documentation included with the redistribution, if
022: * any, must include the following acknowledgement:
023: * "This product includes software developed by the
024: * Apache Software Foundation (http://www.apache.org/)."
025: * Alternately, this acknowledgement may appear in the software itself,
026: * if and wherever such third-party acknowledgements normally appear.
027: *
028: * 4. The names "The Jakarta Project", "Commons", and "Apache Software
029: * Foundation" must not be used to endorse or promote products derived
030: * from this software without prior written permission. For written
031: * permission, please contact apache@apache.org.
032: *
033: * 5. Products derived from this software may not be called "Apache"
034: * nor may "Apache" appear in their names without prior written
035: * permission of the Apache Software Foundation.
036: *
037: * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
038: * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
039: * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
040: * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
041: * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
042: * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
043: * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
044: * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
045: * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
046: * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
047: * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
048: * SUCH DAMAGE.
049: * ====================================================================
050: *
051: * This software consists of voluntary contributions made by many
052: * individuals on behalf of the Apache Software Foundation. For more
053: * information on the Apache Software Foundation, please see
054: * <http://www.apache.org/>.
055: *
056: */
057:
058: package org.apache.commons.jrcs.rcs;
059:
060: import java.util.Arrays;
061: import java.util.StringTokenizer;
062:
063: import org.apache.commons.jrcs.util.ToString;
064:
065: /**
066: * Contains and manages a version number of the form "x(\.y)*".
067: * This class is NOT thread safe.
068: *
069: * @see Archive
070: *
071: * @author <a href="mailto:juanco@suigeneris.org">Juanco Anez</a>
072: * @version $Id: Version.java 2967 2005-10-26 10:52:33Z ian@caret.cam.ac.uk $
073: */
074: public class Version extends ToString implements Cloneable, Comparable {
075: private int[] numbers = new int[0];
076:
077: /**
078: * Creates a new Version with a single digit version number
079: * @param major the version number
080: */
081: public Version(int major) {
082: numbers = new int[] { major };
083: }
084:
085: /**
086: * Creates a new Version with a major.minor version number.
087: * @param major the major version number
088: * @param major the minor version number
089: */
090: public Version(int major, int minor) {
091: numbers = new int[] { major, minor };
092: }
093:
094: /**
095: * Converts an array of Integer to a Version.
096: * @param num an array of Integers
097: */
098: public Version(Integer[] num) {
099: numbers = new int[num.length];
100: for (int i = 0; i < num.length; i++) {
101: numbers[i] = num[i].intValue();
102: }
103: }
104:
105: /**
106: * Converts an array of int to a Version.
107: * @param num an array of int
108: */
109: public Version(int[] num) {
110: numbers = (int[]) num.clone();
111: }
112:
113: /**
114: * Converts string to a version.
115: * @param v a string accepted by the following regular expression.
116: * <code>
117: * [0-9]+(.[0-9]+)*
118: * </code>
119: * @throws InvalidVersionNumberException if the string cannot be parsed
120: */
121: public Version(String v) throws InvalidVersionNumberException {
122: if (v.endsWith(".")) {
123: v = v + "0";
124: }
125: StringTokenizer t = new StringTokenizer(v, ".");
126:
127: int count = t.countTokens();
128: if (even(count) && v.endsWith(".0")) {
129: count--;
130: } // allow a .0 ending only in branch revisions
131:
132: numbers = new int[count];
133: for (int i = 0; i < count; i++) {
134: try {
135: numbers[i] = Integer.parseInt(t.nextToken());
136: } catch (NumberFormatException e) {
137: throw new InvalidVersionNumberException(v);
138: }
139: }
140: }
141:
142: /**
143: * Create a new Version by copying another.
144: * @param v the version to copy
145: */
146: public Version(Version v) {
147: this .numbers = (int[]) v.numbers.clone();
148: if (!Arrays.equals(this .numbers, v.numbers)) {
149: throw new IllegalStateException(numbers.toString());
150: }
151: }
152:
153: /**
154: * Create an empty version number.
155: */
156: public Version() {
157: }
158:
159: public Object clone() {
160: return new Version(this );
161: }
162:
163: /**
164: * Return the current version number as an array of int.
165: * @return the current version number as an array of int.
166: */
167: public int[] getNumbers() {
168: return (int[]) this .numbers.clone();
169: }
170:
171: /**
172: * Compares two versions.
173: * The comparison is done the usual way, i.e., 2.0 is greter than 1.99.1,
174: * and 0.1.2 is greater than 0.1
175: * @param ver the version to compare to.
176: * @return 0 if this == ver, 1 if this greater than ver, -1 otherwise.
177: */
178: public int compareVersions(Version ver) {
179: int[] nthis = this .numbers;
180: int[] nthat = ver.numbers;
181:
182: int i;
183: for (i = 0; i < nthis .length; i++) {
184: if (i >= nthat.length || nthis [i] > nthat[i]) {
185: return 1;
186: } else if (nthis [i] < nthat[i]) {
187: return -1;
188: }
189: }
190: // all matched up to i-1
191: if (nthat.length > i) {
192: return -1;
193: } else {
194: return 0;
195: }
196: }
197:
198: /**
199: * Compares two versions in lexigographical order.
200: * Unlike compareVersions, this comparison is not done in
201: * the way usual for versions numbers. The order relationship
202: * stablished here is the one CVS used to store nodes into archive
203: * files.
204: * @param other The version to compare to
205: * @see #compareVersions
206: */
207: public int compareTo(Object other) {
208: if (other == this ) {
209: return 0;
210: } else if (!(other instanceof Version)) {
211: throw new IllegalArgumentException(other.toString());
212: } else {
213: Version otherVer = (Version) other;
214: if (this .size() != otherVer.size()) {
215: return this .size() - otherVer.size();
216: } else {
217: return -compareVersions(otherVer);
218: }
219: }
220: }
221:
222: /**
223: * Determine if this version is greater than the given one.
224: * @param ver the version to compare to.
225: * @return true if compareVersions(ver) > 0
226: * @see #compareVersions
227: */
228: public boolean isGreaterThan(Version ver) {
229: return compareVersions(ver) > 0;
230: }
231:
232: /**
233: * Determine if this version is greater than or equal to the given one.
234: * @param ver the version to compare to.
235: * @return true if compareVersions(ver) >= 0
236: * @see #compareVersions
237: */
238: public boolean isGreaterOrEqualThan(Version ver) {
239: return compareVersions(ver) >= 0;
240: }
241:
242: /**
243: * Determine if this version is less than the given one.
244: * @param ver the version to compare to.
245: * @return true if compareVersions(ver) < 0
246: * @see #compareVersions
247: */
248: public boolean isLessThan(Version ver) {
249: return compareVersions(ver) < 0;
250: }
251:
252: /**
253: * Determine if this version is less than or equal to the given one.
254: * @param ver the version to compare to.
255: * @return true if compareVersions(ver) <= 0
256: * @see #compareVersions
257: */
258: public boolean isLessOrEqualThan(Version ver) {
259: return compareVersions(ver) <= 0;
260: }
261:
262: /**
263: * Determine if two versions are equal.
264: * @param o the version to compare to
265: * @return true if both versions represent the same version number
266: */
267: public boolean equals(Object o) {
268: if (this == o) {
269: return true;
270: } else if (!(o instanceof Version)) {
271: return false;
272: } else if (hashCode() != o.hashCode()) {
273: return false;
274: } else {
275: return compareTo((Version) o) == 0;
276: }
277: }
278:
279: public int hashCode() {
280: return toString().hashCode();
281: }
282:
283: /**
284: * Return the version number at the given position.
285: * @param pos the position.
286: * @return the number.
287: */
288: public int at(int pos) {
289: return numbers[pos];
290: }
291:
292: /**
293: * Return the last number in the version number.
294: * @return the number.
295: */
296: public int last() {
297: return at(size() - 1);
298: }
299:
300: /**
301: * Return the last number in the version number.
302: * @return the number.
303: */
304: public Version getBase(int positions) {
305: positions = (positions > numbers.length ? numbers.length
306: : positions);
307: int[] result = new int[positions];
308: System.arraycopy(this .numbers, 0, result, 0, positions);
309: return new Version(result);
310: }
311:
312: public Version getBranchPoint() {
313: return getBase(size() - 1);
314: }
315:
316: public Version next() {
317: Version result = new Version(this );
318: result.numbers[this .numbers.length - 1] = this .last() + 1;
319: return result;
320: }
321:
322: protected void __addBranch(Integer branch) {
323: __addBranch(branch.intValue());
324: }
325:
326: protected void __addBranch(int branch) {
327: int[] newnum = new int[numbers.length + 1];
328: System.arraycopy(this .numbers, 0, newnum, 0, numbers.length);
329: newnum[numbers.length] = branch;
330: this .numbers = newnum;
331: }
332:
333: public Version newBranch(int branch) {
334: int[] newnum = new int[numbers.length + 1];
335: System.arraycopy(this .numbers, 0, newnum, 0, numbers.length);
336: newnum[numbers.length] = branch;
337:
338: Version result = new Version();
339: result.numbers = newnum;
340: return result;
341: }
342:
343: public int size() {
344: return numbers.length;
345: }
346:
347: public boolean isTrunk() {
348: return (size() >= 1) && (size() <= 2);
349: }
350:
351: public boolean isBranch() {
352: return size() > 2;
353: }
354:
355: public boolean isRevision() {
356: return even();
357: }
358:
359: public boolean isGhost() {
360: for (int i = 0; i < size(); i++) {
361: if (numbers[i] <= 0) {
362: return true;
363: }
364: }
365: return false;
366: }
367:
368: public boolean even(int n) {
369: return n % 2 == 0;
370: }
371:
372: public boolean even() {
373: return even(size());
374: }
375:
376: public boolean odd(int n) {
377: return !even(n);
378: }
379:
380: public boolean odd() {
381: return !even();
382: }
383:
384: public void toString(StringBuffer s) {
385: if (size() > 0) {
386: s.append(Integer.toString(numbers[0]));
387: for (int i = 1; i < numbers.length; i++) {
388: s.append(".");
389: s.append(Integer.toString(numbers[i]));
390: }
391: }
392: }
393: }
|