/**
* Licensed to Jasig under one or more contributor license
* agreements. See the NOTICE file distributed with this work
* for additional information regarding copyright ownership.
* Jasig licenses this file to you under the Apache License,
* Version 2.0 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a
* copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.jasig.portal.version;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.jasig.portal.version.om.Version;
import org.jasig.portal.version.om.Version.Field;
/**
* Utilities for working with Version classes
*/
public class VersionUtils {
private static final Pattern VERSION_PATTERN = Pattern.compile("^(\\d+)\\.(\\d+)\\.(\\d+)(?:\\.(\\d+))?(?:[\\.-].*)?$");
/**
* Parse a version string into a Version object, if the string doesn't match the pattern null is returned.
* <p>
* The regular expression used in parsing is: ^(\d+)\.(\d+)\.(\d+)(?:\.(\d+))?(?:[\.-].*)?$
* <p>
* Examples that match correctly:
* <ul>
* <li>4.0.5</li>
* <li>4.0.5.123123</li>
* <li>4.0.5-SNAPSHOT</li>
* <ul>
*
* Examples do NOT match correctly:
* <ul>
* <li>4.0</li>
* <li>4.0.5_123123</li>
* <ul>
*/
public static Version parseVersion(String versionString) {
final Matcher versionMatcher = VERSION_PATTERN.matcher(versionString);
if (!versionMatcher.matches()) {
return null;
}
final int major = Integer.parseInt(versionMatcher.group(1));
final int minor = Integer.parseInt(versionMatcher.group(2));
final int patch = Integer.parseInt(versionMatcher.group(3));
final String local = versionMatcher.group(4);
if (local != null) {
return new SimpleVersion(major, minor, patch, Integer.valueOf(local));
}
return new SimpleVersion(major, minor, patch);
}
/**
* Determine how much of two versions match. Returns null if the versions do not match at all.
*
* @return null for no match or the name of the most specific field that matches.
*/
public static Version.Field getMostSpecificMatchingField(Version v1, Version v2) {
if (v1.getMajor() != v2.getMajor()) {
return null;
}
if (v1.getMinor() != v2.getMinor()) {
return Version.Field.MAJOR;
}
if (v1.getPatch() != v2.getPatch()) {
return Version.Field.MINOR;
}
final Integer l1 = v1.getLocal();
final Integer l2 = v2.getLocal();
if (l1 != l2 && (l1 == null || l2 == null || !l1.equals(l2))) {
return Version.Field.PATCH;
}
return Version.Field.LOCAL;
}
/**
* Determine if an "update" can be done between the from and to versions. The ability to update is defined as
* from == to OR (from.isBefore(to) AND mostSpecificMatchingField in (PATCH, MINOR))
*
* @param from Version updating from
* @param to Version updating to
* @return true if the major and minor versions match and the from.patch value is less than or equal to the to.patch value
*/
public static boolean canUpdate(Version from, Version to) {
final Field mostSpecificMatchingField = getMostSpecificMatchingField(from, to);
switch (mostSpecificMatchingField) {
case LOCAL: {
return true;
}
case PATCH:
case MINOR:{
return from.isBefore(to);
}
default: {
return false;
}
}
}
private static final class SimpleVersion extends AbstractVersion {
private static final long serialVersionUID = 1L;
private final int major;
private final int minor;
private final int patch;
private final Integer local;
public SimpleVersion(int major, int minor, int patch) {
this.major = major;
this.minor = minor;
this.patch = patch;
this.local = null;
}
public SimpleVersion(int major, int minor, int patch, Integer local) {
this.major = major;
this.minor = minor;
this.patch = patch;
this.local = local;
}
@Override
public int getMajor() {
return major;
}
@Override
public int getMinor() {
return minor;
}
@Override
public int getPatch() {
return patch;
}
public Integer getLocal() {
return local;
}
}
}