/** * Licensed to Apereo under one or more contributor license agreements. See the NOTICE file * distributed with this work for additional information regarding copyright ownership. Apereo * 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 the * following location: * * <p>http://www.apache.org/licenses/LICENSE-2.0 * * <p>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.apereo.portal.version; import java.util.regex.Matcher; import java.util.regex.Pattern; import org.apereo.portal.version.om.Version; import org.apereo.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>4.0.5.123123 * <li>4.0.5-SNAPSHOT * <ul> * Examples do NOT match correctly: * <ul> * <li>4.0 * <li>4.0.5_123123 * <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; } } }