Coverage Report - org.owasp.dependencycheck.utils.DependencyVersion
 
Classes in this File Line Coverage Branch Coverage Complexity
DependencyVersion
92%
76/82
80%
50/62
5.091
 
 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  13
 public class DependencyVersion implements Iterable, Comparable<DependencyVersion> {
 42  
 
 43  
     /**
 44  
      * Constructor for a empty DependencyVersion.
 45  
      */
 46  20
     public DependencyVersion() {
 47  20
     }
 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  12869
     public DependencyVersion(String version) {
 58  12869
         parseVersion(version);
 59  12869
     }
 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  12871
         versionParts = new ArrayList<String>();
 70  12871
         if (version != null) {
 71  12871
             final Pattern rx = Pattern.compile("(\\d+|[a-z]+\\d+|(release|beta|alpha)$)");
 72  12871
             final Matcher matcher = rx.matcher(version.toLowerCase());
 73  53001
             while (matcher.find()) {
 74  40130
                 versionParts.add(matcher.group());
 75  
             }
 76  12871
             if (versionParts.isEmpty()) {
 77  77
                 versionParts.add(version);
 78  
             }
 79  
         }
 80  12871
     }
 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  18242
         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  19
         this.versionParts = versionParts;
 102  19
     }
 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  1
         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  3718
         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  12505
         if (obj == null) {
 132  0
             return false;
 133  
         }
 134  12505
         if (getClass() != obj.getClass()) {
 135  0
             return false;
 136  
         }
 137  12505
         final DependencyVersion other = (DependencyVersion) obj;
 138  12505
         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  18989
         for (int i = 0; i < max; i++) {
 142  18298
             final String thisPart = this.versionParts.get(i);
 143  18298
             final String otherPart = other.versionParts.get(i);
 144  18298
             if (!thisPart.equals(otherPart)) {
 145  11814
                 return false;
 146  
             }
 147  
         }
 148  691
         if (this.versionParts.size() > max) {
 149  86
             for (int i = max; i < this.versionParts.size(); i++) {
 150  86
                 if (!"0".equals(this.versionParts.get(i))) {
 151  86
                     return false;
 152  
                 }
 153  
             }
 154  
         }
 155  
 
 156  605
         if (other.versionParts.size() > max) {
 157  463
             for (int i = max; i < other.versionParts.size(); i++) {
 158  447
                 if (!"0".equals(other.versionParts.get(i))) {
 159  415
                     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  190
         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  1
         int hash = 5;
 180  1
         hash = 71 * hash + (this.versionParts != null ? this.versionParts.hashCode() : 0);
 181  1
         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  8359
         if (version == null) {
 194  0
             return false;
 195  
         }
 196  
 
 197  8359
         boolean ret = true;
 198  8359
         int max = (this.versionParts.size() < version.versionParts.size())
 199  
                 ? this.versionParts.size() : version.versionParts.size();
 200  
 
 201  8359
         if (max > 3) {
 202  2
             max = 3;
 203  
         }
 204  
 
 205  12674
         for (int i = 0; i < max; i++) {
 206  12260
             if (this.versionParts.get(i) == null || !this.versionParts.get(i).equals(version.versionParts.get(i))) {
 207  7945
                 ret = false;
 208  7945
                 break;
 209  
             }
 210  
         }
 211  
 
 212  8359
         return ret;
 213  
     }
 214  
 
 215  
     @Override
 216  
     public int compareTo(DependencyVersion version) {
 217  54
         if (version == null) {
 218  0
             return 1;
 219  
         }
 220  54
         final List<String> left = this.getVersionParts();
 221  54
         final List<String> right = version.getVersionParts();
 222  54
         final int max = left.size() < right.size() ? left.size() : right.size();
 223  
 
 224  88
         for (int i = 0; i < max; i++) {
 225  81
             final String lStr = left.get(i);
 226  81
             final String rStr = right.get(i);
 227  81
             if (lStr.equals(rStr)) {
 228  34
                 continue;
 229  
             }
 230  
             try {
 231  47
                 final int l = Integer.parseInt(lStr);
 232  46
                 final int r = Integer.parseInt(rStr);
 233  41
                 if (l < r) {
 234  32
                     return -1;
 235  9
                 } else if (l > r) {
 236  9
                     return 1;
 237  
                 }
 238  6
             } catch (NumberFormatException ex) {
 239  6
                 final int comp = left.get(i).compareTo(right.get(i));
 240  6
                 if (comp < 0) {
 241  0
                     return -1;
 242  6
                 } else if (comp > 0) {
 243  6
                     return 1;
 244  
                 }
 245  0
             }
 246  
         }
 247  7
         if (left.size() < right.size()) {
 248  3
             return -1;
 249  4
         } else if (left.size() > right.size()) {
 250  3
             return 1;
 251  
         } else {
 252  1
             return 0;
 253  
         }
 254  
     }
 255  
 }