/******************************************************************************* * Copyright (c) 2007, 2009 SpringSource * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * SpringSource - initial API and implementation *******************************************************************************/ package org.eclipse.virgo.ide.bundlerepository.domain; /** * Represents a version range as specified in an import package header * */ public class VersionRange { public static final OsgiVersion INFINITY = new OsgiVersion(Integer.MAX_VALUE, Integer.MAX_VALUE, Integer.MAX_VALUE, ""); private transient OsgiVersion lowerBound; private transient OsgiVersion upperBound; private boolean isInclusiveLowerBound = true; // if true then the a version == lowerBound will satisfy this range private boolean isInclusiveUpperBound = false; // if true then a version == upperBound will satisfy this range // these next shenanigans are because JPA can't persist an embeddable type nested in an embeddable type // therefore we have to either fudge things in the database (by storing version ranges in their own table), or // fudge things in the domain model (to allow version range and import package information to be stored in // the same table). We've chosen the latter option here as we never want to retrieve PackageImports without their // version ranges. private int lowerBoundMajor; private int lowerBoundMinor; private int lowerBoundService; private String lowerBoundQualifier; private int upperBoundMajor; private int upperBoundMinor; private int upperBoundService; private String upperBoundQualifier; protected VersionRange() { } /** * Construct a version range from the String format found in the version attribute of an import package header * @param osgiRangeSpecification range specification */ public VersionRange(String osgiRangeSpecification) { if ((osgiRangeSpecification == null) || (osgiRangeSpecification.equals(""))) { osgiRangeSpecification = "0.0.0"; } // strip any surrounding quotes if (osgiRangeSpecification.startsWith("\"")) { osgiRangeSpecification = osgiRangeSpecification.substring(1, osgiRangeSpecification.length() - 1); } osgiRangeSpecification = extractLowerBoundCriteria(osgiRangeSpecification); osgiRangeSpecification = extractUpperBoundCriteria(osgiRangeSpecification); boolean isRange = (osgiRangeSpecification.indexOf(",") != -1); if (isRange) { int splitIndex = osgiRangeSpecification.indexOf(","); setLowerBound(new OsgiVersion(osgiRangeSpecification.substring(0, splitIndex))); try { setUpperBound(new OsgiVersion(osgiRangeSpecification.substring(splitIndex + 1))); } catch (IllegalArgumentException e) { // default to infinity setUpperBound(INFINITY); } } else { setLowerBound(new OsgiVersion(osgiRangeSpecification)); setUpperBound(INFINITY); } } /** * Create a version range object * @param lowerBound lower bound of range * @param lowerInclusive whether the lower bound is inclusive (true) or exclusive (false) * @param upperBound upper bound of range * @param upperInclusive whether the upper bound is inclusive (true) or exclusive (false) */ public VersionRange(OsgiVersion lowerBound, boolean lowerInclusive, OsgiVersion upperBound, boolean upperInclusive) { setLowerBound(lowerBound); setUpperBound(upperBound); this.isInclusiveLowerBound = lowerInclusive; this.isInclusiveUpperBound = upperInclusive; } /* shenanigans with version fields are because of JPA limitation */ public OsgiVersion getLowerBound() { if (this.lowerBound == null) { this.lowerBound = new OsgiVersion(lowerBoundMajor, lowerBoundMinor, lowerBoundService, lowerBoundQualifier); } return this.lowerBound; } /* shenanigans with version fields are because of JPA limitation */ public void setLowerBound(OsgiVersion bound) { this.lowerBound = bound; this.lowerBoundMajor = bound.getMajor(); this.lowerBoundMinor = bound.getMinor(); this.lowerBoundService = bound.getService(); this.lowerBoundQualifier = bound.getQualifier(); } /* shenanigans with version fields are because of JPA limitation */ public OsgiVersion getUpperBound() { if (this.upperBound == null) { this.upperBound = new OsgiVersion(upperBoundMajor, upperBoundMinor, upperBoundService, upperBoundQualifier); } return this.upperBound; } public boolean isUpperBoundInfinity() { return INFINITY.equals(this.upperBound); } /* shenanigans with version fields are because of JPA limitation */ public void setUpperBound(OsgiVersion bound) { this.upperBound = bound; this.upperBoundMajor = bound.getMajor(); this.upperBoundMinor = bound.getMinor(); this.upperBoundService = bound.getService(); this.upperBoundQualifier = bound.getQualifier(); } public boolean isInclusiveLowerBound() { return isInclusiveLowerBound; } public boolean isInclusiveUpperBound() { return isInclusiveUpperBound; } /** * Return true if the given version falls within this range */ public boolean contains(OsgiVersion version) { // check lower bound int lowerBoundComparison = version.compareTo(getLowerBound()); if (lowerBoundComparison < 0) { return false; } else if ((lowerBoundComparison == 0) && !this.isInclusiveLowerBound) { return false; } // we've passed the lower bound test, check the upper bound int upperBoundComparison = version.compareTo(getUpperBound()); if (upperBoundComparison > 0) { return false; } else if ((upperBoundComparison == 0) && !this.isInclusiveUpperBound) { return false; } return true; } @Override public String toString() { StringBuilder sb = new StringBuilder(); sb.append(isInclusiveLowerBound ? "[" : "("); sb.append(getLowerBound().toString()); sb.append(", "); if (getUpperBound().equals(INFINITY)) { sb.append("infinity)"); } else { sb.append(getUpperBound().toString()); sb.append(isInclusiveUpperBound ? "]" : ")"); } return sb.toString(); } /* generated */ @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + (isInclusiveLowerBound ? 1231 : 1237); result = prime * result + (isInclusiveUpperBound ? 1231 : 1237); result = prime * result + lowerBoundMajor; result = prime * result + lowerBoundMinor; result = prime * result + ((lowerBoundQualifier == null) ? 0 : lowerBoundQualifier.hashCode()); result = prime * result + lowerBoundService; result = prime * result + upperBoundMajor; result = prime * result + upperBoundMinor; result = prime * result + ((upperBoundQualifier == null) ? 0 : upperBoundQualifier.hashCode()); result = prime * result + upperBoundService; return result; } /* generated */ @Override public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; final VersionRange other = (VersionRange) obj; if (isInclusiveLowerBound != other.isInclusiveLowerBound) return false; if (isInclusiveUpperBound != other.isInclusiveUpperBound) return false; if (lowerBoundMajor != other.lowerBoundMajor) return false; if (lowerBoundMinor != other.lowerBoundMinor) return false; if (lowerBoundQualifier == null) { if (other.lowerBoundQualifier != null) return false; } else if (!lowerBoundQualifier.equals(other.lowerBoundQualifier)) return false; if (lowerBoundService != other.lowerBoundService) return false; if (upperBoundMajor != other.upperBoundMajor) return false; if (upperBoundMinor != other.upperBoundMinor) return false; if (upperBoundQualifier == null) { if (other.upperBoundQualifier != null) return false; } else if (!upperBoundQualifier.equals(other.upperBoundQualifier)) return false; if (upperBoundService != other.upperBoundService) return false; return true; } /** * Parse any inclusive "]" or exclusive ")" upper bound postfix and return the input string with them stripped */ private String extractUpperBoundCriteria(String osgiRangeSpecification) { // parse any ending inclusive / exclusive brackets if (osgiRangeSpecification.endsWith(")")) { this.isInclusiveUpperBound = false; osgiRangeSpecification = osgiRangeSpecification.substring(0, osgiRangeSpecification.length() - 1); } else if (osgiRangeSpecification.endsWith("]")) { this.isInclusiveUpperBound = true; osgiRangeSpecification = osgiRangeSpecification.substring(0, osgiRangeSpecification.length() - 1); } else { this.isInclusiveUpperBound = true; } return osgiRangeSpecification; } /** * Parse any inclusive "[" or exclusive "(" lower bound prefix and return the input string with them stripped. */ private String extractLowerBoundCriteria(String osgiRangeSpecification) { // parse any opening inclusive / exclusive brackets if (osgiRangeSpecification.startsWith("(")) { this.isInclusiveLowerBound = false; osgiRangeSpecification = osgiRangeSpecification.substring(1); } else if (osgiRangeSpecification.startsWith("[")) { osgiRangeSpecification = osgiRangeSpecification.substring(1); this.isInclusiveLowerBound = true; } else { this.isInclusiveLowerBound = true; } return osgiRangeSpecification; } }