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