/** * This file Copyright (c) 2003-2012 Magnolia International * Ltd. (http://www.magnolia-cms.com). All rights reserved. * * * This file is dual-licensed under both the Magnolia * Network Agreement and the GNU General Public License. * You may elect to use one or the other of these licenses. * * This file is distributed in the hope that it will be * useful, but AS-IS and WITHOUT ANY WARRANTY; without even the * implied warranty of MERCHANTABILITY or FITNESS FOR A * PARTICULAR PURPOSE, TITLE, or NONINFRINGEMENT. * Redistribution, except as permitted by whichever of the GPL * or MNA you select, is prohibited. * * 1. For the GPL license (GPL), you can redistribute and/or * modify this file under the terms of the GNU General * Public License, Version 3, as published by the Free Software * Foundation. You should have received a copy of the GNU * General Public License, Version 3 along with this program; * if not, write to the Free Software Foundation, Inc., 51 * Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * 2. For the Magnolia Network Agreement (MNA), this file * and the accompanying materials are made available under the * terms of the MNA which accompanies this distribution, and * is available at http://www.magnolia-cms.com/mna.html * * Any modifications to this file must keep this entire header * intact. * */ package info.magnolia.module.model; import java.util.regex.Pattern; /** * Represents a module version. Format is x.y.z-classifier. y,z and classifier are * optional. The classifier string is ignored in version comparisons. * * @author gjoseph * @version $Revision: $ ($Author: $) */ public class Version { private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(Version.class); public static final Version UNDEFINED_FROM = new UndefinedEarlierVersion(); public static final Version UNDEFINED_TO = new UndefinedLaterVersion(); public static final Version UNDEFINED_DEVELOPMENT_VERSION = new UndefinedDevelopmentVersion(); private static final Pattern classifierValidation = Pattern.compile("[A-Za-z0-9-_]+"); private final short major; private final short minor; private final short patch; private final String classifier; /** * Convenience constructor that could be used to register Deltas or update tasks. */ protected Version(int major, int minor, int patch) { this.major = (short) major; this.minor = (short) minor; this.patch = (short) patch; this.classifier = null; } private Version(String versionStr) { final String numbers; final int classifierIdx = versionStr.indexOf('-'); if (classifierIdx > 0) { classifier = versionStr.substring(classifierIdx + 1); if (!classifierValidation.matcher(classifier).matches()) { throw new IllegalArgumentException("Invalid classifier: \"" + classifier + "\" in version \"" + versionStr + "\""); } numbers = versionStr.substring(0, classifierIdx); } else { classifier = null; numbers = versionStr; } final String[] strings = numbers.split("\\.", -1); if (strings.length > 0) { major = getShortFor("major revision", versionStr, strings[0]); } else { major = getShortFor("major revision", versionStr, versionStr); } if (strings.length > 1) { minor = getShortFor("minor revision", versionStr, strings[1]); } else { minor = 0; } if (strings.length > 2) { patch = getShortFor("patch revision", versionStr, strings[2]); } else { patch = 0; } } /** * Factory method that will parse a version string and return the correct Version implementation. * @param versionStr version as string, for example <code>1.2.3-test</code>. The String * <code>${project.version}</code> is interpreted as an undefined version during development ant it will always * match version ranges * @return a Version implementation, never null */ public static Version parseVersion(String versionStr) { versionStr = versionStr.trim(); log.debug("parsing version [{}]", versionStr); if (UndefinedDevelopmentVersion.isDevelopmentVersion(versionStr)) { // development mode. return UNDEFINED_DEVELOPMENT_VERSION; } return new Version(versionStr); } public static Version parseVersion(int major, int minor, int patch) { return new Version(major, minor, patch); } /** * Compares major, minor and patch revisions of this Version against the given Version. * Classifier is ignored. */ public boolean isEquivalent(final Version other) { if(other == UNDEFINED_DEVELOPMENT_VERSION){ return true; } return this.getMajor() == other.getMajor() && this.getMinor() == other.getMinor() && this.getPatch() == other.getPatch(); } public boolean isStrictlyAfter(final Version other) { if(isEquivalent(other)){ return false; } if (this.getMajor() != other.getMajor()) { return this.getMajor() > other.getMajor(); } if (this.getMinor() != other.getMinor()) { return this.getMinor() > other.getMinor(); } if (this.getPatch() != other.getPatch()) { return this.getPatch() > other.getPatch(); } return false; } public boolean isBeforeOrEquivalent(final Version other) { return !isStrictlyAfter(other); } public short getMajor() { return major; } public short getMinor() { return minor; } public short getPatch() { return patch; } public String getClassifier() { return classifier; } @Override public String toString() { return major + "." + minor + "." + patch + (classifier != null ? "-" + classifier : ""); } private short getShortFor(String message, String versionStr, String input) { try { return Short.parseShort(input); } catch (NumberFormatException e) { throw new RuntimeException("Invalid " + message + ": \"" + input + "\" in version \"" + versionStr + "\""); } } private static final class UndefinedLaterVersion extends Version { public UndefinedLaterVersion() { super(Short.MAX_VALUE, Short.MAX_VALUE, Short.MAX_VALUE); } @Override public String toString() { return "*"; } } private static final class UndefinedEarlierVersion extends Version { public UndefinedEarlierVersion() { super(Short.MIN_VALUE, Short.MIN_VALUE, Short.MIN_VALUE); } @Override public String toString() { return "*"; } } /** * A undefined developer version being always equivalent to other versions. Any version in the module xml starting * with <code>${</code> like <code>${project.version}</code> or <code>${buildNumber}</code> */ static final class UndefinedDevelopmentVersion extends Version { @Deprecated static final String KEY = "${project.version}"; public UndefinedDevelopmentVersion() { super(0, 0, 0); } @Override public boolean isEquivalent(Version other) { return true; } @Override public String toString() { return KEY; } public static boolean isDevelopmentVersion(String version) { return version != null && version.startsWith("${"); } } // generated methods: @Override public boolean equals(Object o) { if (this == o) { return true; } if (o == null || getClass() != o.getClass()) { return false; } Version version = (Version) o; if (major != version.major) { return false; } if (minor != version.minor) { return false; } if (patch != version.patch) { return false; } if (classifier != null ? !classifier.equals(version.classifier) : version.classifier != null) { return false; } return true; } @Override public int hashCode() { int result; result = major; result = 31 * result + minor; result = 31 * result + patch; result = 31 * result + (classifier != null ? classifier.hashCode() : 0); return result; } }