// Copyright (C) 2001-2003 Jon A. Maxwell (JAM)
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
package net.sourceforge.jnlp;
import java.util.*;
/**
* A JNLP Version string in the form "1.2-3_abc" followed by an
* optional + (includes all later versions) or * (matches any
* suffixes on versions). More than one version can be included
* in a string by separating them with spaces.
*
* Version strings are divided by "._-" charecters into parts.
* These parts are compared numerically if they can be parsed as
* integers or lexographically as strings otherwise. If the
* number of parts is different between two version strings then
* the smaller one is padded with zero or the empty string. Note
* that the padding in this version means that 1.2+ matches
* 1.4.0-beta1, but may not in future versions.
*
* @author Jon A. Maxwell (JAM) - initial author
* @version $Revision: 1.5 $
*/
public class Version {
// to do: web start does not match versions with a "-" like
// "1.4-beta1" using the + modifier, change to mimic that
// behavior.
// also refactor into Version and VersionID classes so that
// individual version ids can be easily modified to add/remove
// "*" and "+" modifiers.
/** separates parts of a version string */
private static String seperators = ".-_";
/** magic key for whether a version part was created due to normalization */
private static String emptyString = new String(""); // not intern'ed
/** contains all the versions matched */
private String versionString;
/**
* Create a Version object based on a version string (ie,
* "1.2.3+ 4.56*").
*/
public Version(String versions) {
versionString = versions;
}
/**
* Returns true if the version represents a version-id (a
* single version number such as 1.2) and false otherwise.
*/
public boolean isVersionId() {
if (-1 != versionString.indexOf(" "))
return false;
return true;
}
/**
* Returns true if all of this version's version-ids match one
* or more of the specifed version's version-id.
*
* @param version a version string
*/
public boolean matches(String version) {
return matches(new Version(version));
}
/**
* Returns true if all of this version's version-ids match one
* or more of the specifed version's version-id.
*
* @param version a Version object
*/
public boolean matches(Version version) {
List versionStrings = version.getVersionStrings();
for (int i = 0; i < versionStrings.size(); i++) {
if (!this.matchesSingle(versionStrings.get(i)))
return false;
}
return true;
}
/**
* Returns true if any of this version's version-ids match one
* or more of the specifed version's version-id.
*
* @param version a version string
*/
public boolean matchesAny(String version) {
return matches(new Version(version));
}
/**
* Returns true if any of this version's version-ids match one
* or more of the specifed version's version-id.
*
* @param version a Version object
*/
public boolean matchesAny(Version version) {
List versionStrings = version.getVersionStrings();
for (int i = 0; i < versionStrings.size(); i++) {
if (this.matchesSingle(versionStrings.get(i)))
return true;
}
return false;
}
/**
* Returns whether a single version string is supported by this
* Version.
*
* @param version a non-compound version of the form "1.2.3[+*]"
*/
private boolean matchesSingle(String version) {
List versionStrings = this.getVersionStrings();
for (int i = 0; i < versionStrings.size(); i++) {
if (matches(version, versionStrings.get(i)))
return true;
}
return false;
}
/**
* Returns whether a single version string is supported by
* another single version string.
*
* @param subversion a non-compound version without "+" or "*"
* @param version a non-compound version optionally with "+" or "*"
*/
private boolean matches(String subversion, String version) {
List subparts = getParts(subversion);
List parts = getParts(version);
int maxLength = Math.max(subversion.length(), version.length());
if (version.endsWith("*")) // star means rest of parts irrelevant: truncate them
maxLength = parts.size();
List> versions = new ArrayList>();
versions.add(subparts);
versions.add(parts);
normalize(versions, maxLength);
if (equal(subparts, parts))
return true;
if (version.endsWith("+") && greater(subparts, parts))
return true;
return false;
}
/**
* Returns whether the parts of one version are equal to the
* parts of another version.
*
* @param parts1 normalized version parts
* @param parts2 normalized version parts
*/
protected boolean equal(List parts1, List parts2) {
for (int i = 0; i < parts1.size(); i++) {
if (0 != compare(parts1.get(i), parts2.get(i)))
return false;
}
return true;
}
/**
* Returns whether the parts of one version are greater than
* the parts of another version.
*
* @param parts1 normalized version parts
* @param parts2 normalized version parts
*/
protected boolean greater(List parts1, List parts2) {
//if (true) return false;
for (int i = 0; i < parts1.size(); i++) {
// if part1 > part2 then it's a later version, so return true
if (compare(parts1.get(i), parts2.get(i)) > 0)
return true;
// if part1 < part2 then it's a ealier version, so return false
if (compare(parts1.get(i), parts2.get(i)) < 0)
return false;
// if equal go to next part
}
// all parts were equal
return false; // not greater than
}
/**
* Compares two parts of a version string, by value if both can
* be interpreted as integers or lexically otherwise. If a part
* is the result of normalization then it can be the Integer
* zero or an empty string.
*
* Returns a value equivalent to part1.compareTo(part2);
*
* @param part1 a part of a version string
* @param part2 a part of a version string
* @return comparison of the two parts
*/
protected int compare(String part1, String part2) {
Integer number1 = Integer.valueOf(0);
Integer number2 = Integer.valueOf(0);
// compare as integers
// for normalization key, compare exact object, not using .equals
try {
if (!(part1 == emptyString)) // compare to magic normalization key
number1 = Integer.valueOf(part1);
if (!(part2 == emptyString)) // compare to magic normalization key
number2 = Integer.valueOf(part2);
return number1.compareTo(number2);
} catch (NumberFormatException ex) {
// means to compare as strings
}
if (part1 == emptyString) // compare to magic normalization key
part1 = "";
if (part2 == emptyString) // compare to magic normalization key
part2 = "";
return part1.compareTo(part2);
}
/**
* Normalize version strings so that they contain the same
* number of constituent parts.
*
* @param versions list array of parts of a version string
* @param maxLength truncate lists to this maximum length
*/
protected void normalize(List> versions, int maxLength) {
int length = 0;
for (List vers : versions)
length = Math.max(length, vers.size());
if (length > maxLength)
length = maxLength;
for (List vers : versions) {
// remove excess elements
while (vers.size() > length)
vers.remove(vers.size() - 1);
// add in empty pad elements
while (vers.size() < length)
vers.add(emptyString);
}
}
/**
* Return the individual version strings that make up a Version.
*/
protected List getVersionStrings() {
ArrayList strings = new ArrayList();
StringTokenizer st = new StringTokenizer(versionString, " ");
while (st.hasMoreTokens())
strings.add(st.nextToken());
return strings;
}
/**
* Return the constituent parts of a version string.
*
* @param oneVersion a single version id string (not compound)
*/
protected List getParts(String oneVersion) {
ArrayList strings = new ArrayList();
StringTokenizer st = new StringTokenizer(oneVersion, seperators + "+*");
while (st.hasMoreTokens()) {
strings.add(st.nextToken());
}
return strings;
}
public String toString() {
return versionString;
}
}