/* * Copyright (c) 2010 Pentaho Corporation. All rights reserved. * This software was developed by Pentaho Corporation and is provided under the terms * of the GNU Lesser General Public License, Version 2.1. You may not use * this file except in compliance with the license. If you need a copy of the license, * please go to http://www.gnu.org/licenses/lgpl-2.1.txt. The Original Code is Pentaho * Package Manager. The Initial Developer is Pentaho Corporation. * * Software distributed under the GNU Lesser Public License is distributed on an "AS IS" * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. Please refer to * the license for the specific language governing your rights and limitations. */ /* * VersionPackageConstraint * Copyright (c) 2010 Pentaho Corporation. All rights reserved. */ package org.pentaho.packageManagement; /** * Concrete implementation of PackageConstraint that encapsulates * constraints related to version numbers. Handles equality = and * open-ended inequalities (e.g. > x, < x, >= x, <= x). * * @author mhall (mhall{[at]}pentaho{[dot]}com) * @version $Revision: 44030 $ */ public class VersionPackageConstraint extends PackageConstraint { /** The meta data key for the version number */ public static String VERSION_KEY = "Version"; /** Enumeration encapsulating version comparison operations */ public enum VersionComparison { EQUAL("=") { boolean compatibleWith(VersionComparison v) { if (v == VersionComparison.EQUAL) { return true; } return false; } }, GREATERTHAN(">") { boolean compatibleWith(VersionComparison v) { if (v == VersionComparison.GREATERTHAN) { return true; } return false; } }, GREATERTHANOREQUAL(">=") { boolean compatibleWith(VersionComparison v) { if (v == VersionComparison.LESSTHAN || v == VersionComparison.LESSTHANOREQUAL) { return false; } return true; } }, LESSTHAN("<") { boolean compatibleWith(VersionComparison v) { if (v == VersionComparison.LESSTHAN) { return true; } return false; } }, LESSTHANOREQUAL("<=") { boolean compatibleWith(VersionComparison v) { if (v == VersionComparison.GREATERTHAN || v == VersionComparison.GREATERTHANOREQUAL) { return false; } return true; } }; private final String m_stringVal; VersionComparison(String name) { m_stringVal = name; } abstract boolean compatibleWith(VersionComparison v); public String toString() { return m_stringVal; } } /** The comparison operator for this constraint */ protected VersionComparison m_constraint = null; /** * Returns a VersionComparison equivalent to the supplied String operator. * * @param compOpp the comparison operator as a string. * @return a VersionComparison object. */ protected static VersionComparison getVersionComparison(String compOpp) { for (VersionComparison v : VersionComparison.values()) { if (v.toString().equalsIgnoreCase(compOpp)) { return v; } } return null; } /** * Parses a version number and returns major, minor and revision numbers * in an array of integers. * * @param version the version number as a string. * @return an array of integers containing the major, minor and revision * numbers. */ protected static int[] parseVersion(String version) { int major = 0; int minor = 0; int revision = 0; int[] majMinRev = new int[3]; try { String tmpStr = version; tmpStr = tmpStr.replace('-', '.'); if (tmpStr.indexOf(".") > -1) { major = Integer.parseInt(tmpStr.substring(0, tmpStr.indexOf("."))); tmpStr = tmpStr.substring(tmpStr.indexOf(".") + 1); if (tmpStr.indexOf(".") > -1) { minor = Integer.parseInt(tmpStr.substring(0, tmpStr.indexOf("."))); tmpStr = tmpStr.substring(tmpStr.indexOf(".") + 1); if (!tmpStr.equals("")) { revision = Integer.parseInt(tmpStr); } else { revision = 0; } } else { if (!tmpStr.equals("")) { minor = Integer.parseInt(tmpStr); } else { minor = 0; } } } else { if (!tmpStr.equals("")) { major = Integer.parseInt(tmpStr); } else { major = 0; } } } catch (Exception e) { e.printStackTrace(); major = -1; minor = -1; revision = -1; } finally { majMinRev[0] = major; majMinRev[1] = minor; majMinRev[2] = revision; } return majMinRev; } /** * Evaluates the supplied constraint with respect to two supplied * version numbers as strings. * * @param version1 String containing version number 1 * @param constraint the constraint comparison to use * @param version2 String containing version number 2 * @return true if version 1 is compatible with version two with * respect to the constraint. */ protected static boolean checkConstraint(String version1, VersionComparison constraint, String version2) { VersionComparison c = compare(version1, version2); return constraint.compatibleWith(c); } /** * Returns a VersionComparison that represents the comparison between * the supplied version 1 and version 2. * * @param version1 String containing version number 1. * @param version2 String containing version number 2. * @return a VersionComparison object. */ protected static VersionComparison compare(String version1, String version2) { // parse both of the versions int[] majMinRev1 = VersionPackageConstraint.parseVersion(version1); int[] majMinRev2 = VersionPackageConstraint.parseVersion(version2); VersionComparison result; if (majMinRev1[0] < majMinRev2[0]) { result = VersionComparison.LESSTHAN; } else if (majMinRev1[0] == majMinRev2[0]) { if (majMinRev1[1] < majMinRev2[1]) { result = VersionComparison.LESSTHAN; } else if (majMinRev1[1] == majMinRev2[1]) { if (majMinRev1[2] < majMinRev2[2]) { result = VersionComparison.LESSTHAN; } else if (majMinRev1[2] == majMinRev2[2]) { result = VersionComparison.EQUAL; } else { result = VersionComparison.GREATERTHAN; } } else { result = VersionComparison.GREATERTHAN; } } else { result = VersionComparison.GREATERTHAN; } return result; } public VersionPackageConstraint(Package p) { setPackage(p); } public void setVersionConstraint(VersionComparison c) { m_constraint = c; } public VersionComparison getVersionComparison() { return m_constraint; } public void setVersionConstraint(String constraint) { for (VersionComparison v : VersionComparison.values()) { if (v.toString().equalsIgnoreCase(constraint)) { m_constraint = v; break; } } } /** * Check the target package constraint against the constraint embodied * in this package constraint. Returns either the package constraint that * covers both this and the target constraint, or null if this and the target * are incompatible. * * Try and return the *least* restrictive constraint that satisfies * this and the target. * * @param target the package constraint to compare against * @return a package constraint that covers this and the supplied constraint, * or null if they are incompatible. */ public PackageConstraint checkConstraint(PackageConstraint target) throws Exception { if (m_constraint == null) { throw new Exception("[VersionPackageConstraint] No constraint has been set!"); } // delegate to VersionRangePackageConstraint if necessary if (target instanceof VersionRangePackageConstraint) { return target.checkConstraint(this); } String targetVersion = target.getPackage().getPackageMetaDataElement(VERSION_KEY).toString(); String thisVersion = m_thePackage.getPackageMetaDataElement(VERSION_KEY).toString(); VersionComparison comp = compare(thisVersion, targetVersion); if (comp == VersionComparison.EQUAL) { // equal version numbers // return this if the constraints are the same for both this and target if (m_constraint == ((VersionPackageConstraint)target).getVersionComparison()) { return this; } else if (m_constraint == VersionComparison.GREATERTHAN && (((VersionPackageConstraint)target).getVersionComparison() == VersionComparison.GREATERTHAN || ((VersionPackageConstraint)target).getVersionComparison() == VersionComparison.GREATERTHANOREQUAL)) { return this; // satisfies both } else if ((m_constraint == VersionComparison.GREATERTHANOREQUAL || m_constraint == VersionComparison.GREATERTHAN) && ((VersionPackageConstraint)target).getVersionComparison() == VersionComparison.GREATERTHAN) { return target; // satisfies both } return null; // can't satisfy } else { // target constraint >/>= if (((VersionPackageConstraint)target).getVersionComparison() == VersionComparison.GREATERTHAN || ((VersionPackageConstraint)target).getVersionComparison() == VersionComparison.GREATERTHANOREQUAL) { if (m_constraint == VersionComparison.EQUAL || m_constraint == VersionComparison.GREATERTHAN || m_constraint == VersionComparison.GREATERTHANOREQUAL) { // return the higher of the two versions if (comp == VersionComparison.GREATERTHAN) { return this; } else { return target; } } return null; // can't satisfy // target constraint </<= } else if (((VersionPackageConstraint)target).getVersionComparison() == VersionComparison.LESSTHAN || ((VersionPackageConstraint)target).getVersionComparison() == VersionComparison.LESSTHANOREQUAL) { if (m_constraint == VersionComparison.EQUAL || m_constraint == VersionComparison.LESSTHAN || m_constraint == VersionComparison.LESSTHANOREQUAL) { // return the lower of the two versions if (comp == VersionComparison.GREATERTHAN) { return target; // satisfies both } else { return this; } } return null; // can't satisfy } return null; // can't satisfy // Could also be compatible in the case where // our constraint is < and target is > (but would need to implement a // y < x < z type of version constraint } } /** * Check the target package against the constraint embodied * in this PackageConstraint. * * @param target a package to check with respect to the * encapsulated package and the constraint. * * @return true if the constraint is met by the target package * with respect to the encapsulated package + constraint. * @throws Exception if the constraint can't be checked for some * reason. */ public boolean checkConstraint(Package target) throws Exception { if (m_constraint == null) { throw new Exception("[VersionPackageConstraint] No constraint has been set!"); } String targetVersion = target.getPackageMetaDataElement(VERSION_KEY).toString(); String thisVersion = m_thePackage.getPackageMetaDataElement(VERSION_KEY).toString(); return checkConstraint(targetVersion, m_constraint, thisVersion); } public String toString() { String result = m_thePackage.getPackageMetaDataElement("PackageName").toString() + " (" + m_constraint + m_thePackage.getPackageMetaDataElement("Version").toString() + ")"; return result; } }