View Javadoc

    1   /*
    2    * This file is part of dependency-check-core.
    3    *
    4    * Dependency-check-core is free software: you can redistribute it and/or modify it
    5    * under the terms of the GNU General Public License as published by the Free
    6    * Software Foundation, either version 3 of the License, or (at your option) any
    7    * later version.
    8    *
    9    * Dependency-check-core is distributed in the hope that it will be useful, but
    10   * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
    11   * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
    12   * details.
    13   *
    14   * You should have received a copy of the GNU General Public License along with
    15   * dependency-check-core. If not, see http://www.gnu.org/licenses/.
    16   *
    17   * Copyright (c) 2013 Jeremy Long. All Rights Reserved.
    18   */
    19  package org.owasp.dependencycheck.utils;
    20
    21  import java.util.ArrayList;
    22  import java.util.Iterator;
    23  import java.util.List;
    24  import java.util.regex.Matcher;
    25  import java.util.regex.Pattern;
    26  import org.apache.commons.lang.StringUtils;
    27
    28  /**
    29   * <p>Simple object to track the parts of a version number. The parts are
    30   * contained in a List such that version 1.2.3 will be stored as:
    31   * <code>versionParts[0] = 1;
    32   * versionParts[1] = 2;
    33   * versionParts[2] = 3;
    34   * </code></p>
    35   * <p>Note, the parser contained in this class expects the version numbers to be
    36   * separated by periods. If a different separator is used the parser will likely
    37   * fail.</p>
    38   *
    39   * @author Jeremy Long (jeremy.long@owasp.org)
    40   */
    41  public class DependencyVersion implements Iterable, Comparable<DependencyVersion> {
    42
    43      /**
    44       * Constructor for a empty DependencyVersion.
    45       */
    46      public DependencyVersion() {
    47      }
    48
    49      /**
    50       * Constructor for a DependencyVersion that will parse a version string.
    51       * <b>Note</b>, this should only be used when the version passed in is
    52       * already known to be a well formated version number. Otherwise,
    53       * DependencyVersionUtil.parseVersion() should be used instead.
    54       *
    55       * @param version the well formated version number to parse
    56       */
    57      public DependencyVersion(String version) {
    58          parseVersion(version);
    59      }
    60
    61      /**
    62       * Parses a version string into its sub parts: major, minor, revision,
    63       * build, etc. <b>Note</b>, this should only be used to parse something that
    64       * is already known to be a version number.
    65       *
    66       * @param version the version string to parse
    67       */
    68      public final void parseVersion(String version) {
    69          versionParts = new ArrayList<String>();
    70          if (version != null) {
    71              final Pattern rx = Pattern.compile("(\\d+|[a-z]+\\d+|(release|beta|alpha)$)");
    72              final Matcher matcher = rx.matcher(version.toLowerCase());
    73              while (matcher.find()) {
    74                  versionParts.add(matcher.group());
    75              }
    76              if (versionParts.isEmpty()) {
    77                  versionParts.add(version);
    78              }
    79          }
    80      }
    81      /**
    82       * A list of the version parts.
    83       */
    84      private List<String> versionParts;
    85
    86      /**
    87       * Get the value of versionParts.
    88       *
    89       * @return the value of versionParts
    90       */
    91      public List<String> getVersionParts() {
    92          return versionParts;
    93      }
    94
    95      /**
    96       * Set the value of versionParts.
    97       *
    98       * @param versionParts new value of versionParts
    99       */
    100     public void setVersionParts(List<String> versionParts) {
    101         this.versionParts = versionParts;
    102     }
    103
    104     /**
    105      * Retrieves an iterator for the version parts.
    106      *
    107      * @return an iterator for the version parts
    108      */
    109     public Iterator iterator() {
    110         return versionParts.iterator();
    111     }
    112
    113     /**
    114      * Reconstructs the version string from the split version parts.
    115      *
    116      * @return a string representing the version.
    117      */
    118     @Override
    119     public String toString() {
    120         return StringUtils.join(versionParts.toArray(), ".");
    121     }
    122
    123     /**
    124      * Compares the equality of this object to the one passed in as a parameter.
    125      *
    126      * @param obj the object to compare equality
    127      * @return returns true only if the two objects are equal, otherwise false
    128      */
    129     @Override
    130     public boolean equals(Object obj) {
    131         if (obj == null) {
    132             return false;
    133         }
    134         if (getClass() != obj.getClass()) {
    135             return false;
    136         }
    137         final DependencyVersion other = (DependencyVersion) obj;
    138         final int max = (this.versionParts.size() < other.versionParts.size())
    139                 ? this.versionParts.size() : other.versionParts.size();
    140         //TODO steal better version of code from compareTo
    141         for (int i = 0; i < max; i++) {
    142             final String thisPart = this.versionParts.get(i);
    143             final String otherPart = other.versionParts.get(i);
    144             if (!thisPart.equals(otherPart)) {
    145                 return false;
    146             }
    147         }
    148         if (this.versionParts.size() > max) {
    149             for (int i = max; i < this.versionParts.size(); i++) {
    150                 if (!"0".equals(this.versionParts.get(i))) {
    151                     return false;
    152                 }
    153             }
    154         }
    155
    156         if (other.versionParts.size() > max) {
    157             for (int i = max; i < other.versionParts.size(); i++) {
    158                 if (!"0".equals(other.versionParts.get(i))) {
    159                     return false;
    160                 }
    161             }
    162         }
    163
    164         /*
    165          *  if (this.versionParts != other.versionParts && (this.versionParts == null || !this.versionParts.equals(other.versionParts))) {
    166          *      return false;
    167          *  }
    168          */
    169         return true;
    170     }
    171
    172     /**
    173      * Calculates the hashCode for this object.
    174      *
    175      * @return the hashCode
    176      */
    177     @Override
    178     public int hashCode() {
    179         int hash = 5;
    180         hash = 71 * hash + (this.versionParts != null ? this.versionParts.hashCode() : 0);
    181         return hash;
    182     }
    183
    184     /**
    185      * Determines if the three most major major version parts are identical. For
    186      * instances, if version 1.2.3.4 was compared to 1.2.3 this function would
    187      * return true.
    188      *
    189      * @param version the version number to compare
    190      * @return true if the first three major parts of the version are identical
    191      */
    192     public boolean matchesAtLeastThreeLevels(DependencyVersion version) {
    193         if (version == null) {
    194             return false;
    195         }
    196
    197         boolean ret = true;
    198         int max = (this.versionParts.size() < version.versionParts.size())
    199                 ? this.versionParts.size() : version.versionParts.size();
    200
    201         if (max > 3) {
    202             max = 3;
    203         }
    204
    205         for (int i = 0; i < max; i++) {
    206             if (this.versionParts.get(i) == null || !this.versionParts.get(i).equals(version.versionParts.get(i))) {
    207                 ret = false;
    208                 break;
    209             }
    210         }
    211
    212         return ret;
    213     }
    214
    215     @Override
    216     public int compareTo(DependencyVersion version) {
    217         if (version == null) {
    218             return 1;
    219         }
    220         final List<String> left = this.getVersionParts();
    221         final List<String> right = version.getVersionParts();
    222         final int max = left.size() < right.size() ? left.size() : right.size();
    223
    224         for (int i = 0; i < max; i++) {
    225             final String lStr = left.get(i);
    226             final String rStr = right.get(i);
    227             if (lStr.equals(rStr)) {
    228                 continue;
    229             }
    230             try {
    231                 final int l = Integer.parseInt(lStr);
    232                 final int r = Integer.parseInt(rStr);
    233                 if (l < r) {
    234                     return -1;
    235                 } else if (l > r) {
    236                     return 1;
    237                 }
    238             } catch (NumberFormatException ex) {
    239                 final int comp = left.get(i).compareTo(right.get(i));
    240                 if (comp < 0) {
    241                     return -1;
    242                 } else if (comp > 0) {
    243                     return 1;
    244                 }
    245             }
    246         }
    247         if (left.size() < right.size()) {
    248             return -1;
    249         } else if (left.size() > right.size()) {
    250             return 1;
    251         } else {
    252             return 0;
    253         }
    254     }
    255 }