1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
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 javax.annotation.concurrent.NotThreadSafe;
26 import org.apache.commons.lang3.StringUtils;
27 import org.apache.commons.lang3.builder.HashCodeBuilder;
28 import org.jetbrains.annotations.NotNull;
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44 @NotThreadSafe
45 public class DependencyVersion implements Iterable<String>, Comparable<DependencyVersion> {
46
47
48
49
50 private List<String> versionParts;
51
52
53
54
55 public DependencyVersion() {
56 }
57
58
59
60
61
62
63
64
65
66 public DependencyVersion(String version) {
67 parseVersion(version);
68 }
69
70
71
72
73
74
75
76
77 public final void parseVersion(String version) {
78 versionParts = new ArrayList<>();
79 if (version != null) {
80 final Pattern rx = Pattern
81 .compile("(\\d{1,100}[a-z]{1,3}$|[a-z]{1,3}[_-]?\\d{1,100}|\\d{1,100}|(rc|release|snapshot|beta|alpha)$)",
82 Pattern.CASE_INSENSITIVE);
83 final Matcher matcher = rx.matcher(version.toLowerCase());
84 while (matcher.find()) {
85 versionParts.add(matcher.group());
86 }
87 if (versionParts.isEmpty()) {
88 versionParts.add(version);
89 }
90 }
91 }
92
93
94
95
96
97
98 public List<String> getVersionParts() {
99 return versionParts;
100 }
101
102
103
104
105
106
107 public void setVersionParts(List<String> versionParts) {
108 this.versionParts = versionParts;
109 }
110
111
112
113
114
115
116 @NotNull
117 @Override
118 public Iterator<String> iterator() {
119 return versionParts.iterator();
120 }
121
122
123
124
125
126
127 @Override
128 public String toString() {
129 return StringUtils.join(versionParts, '.');
130 }
131
132
133
134
135
136
137
138 @Override
139 public boolean equals(Object obj) {
140 if (obj == null || !(obj instanceof DependencyVersion)) {
141 return false;
142 }
143 if (this == obj) {
144 return true;
145 }
146 final DependencyVersion other = (DependencyVersion) obj;
147 final int minVersionMatchLength = Math.min(this.versionParts.size(), other.versionParts.size());
148 final int maxVersionMatchLength = Math.max(this.versionParts.size(), other.versionParts.size());
149
150 if (minVersionMatchLength == 1 && maxVersionMatchLength >= 3) {
151 return false;
152 }
153
154
155 for (int i = 0; i < minVersionMatchLength; i++) {
156 final String thisPart = this.versionParts.get(i);
157 final String otherPart = other.versionParts.get(i);
158 if (!thisPart.equals(otherPart)) {
159 return false;
160 }
161 }
162 if (this.versionParts.size() > minVersionMatchLength) {
163 for (int i = minVersionMatchLength; i < this.versionParts.size(); i++) {
164 if (!"0".equals(this.versionParts.get(i))) {
165 return false;
166 }
167 }
168 }
169
170 if (other.versionParts.size() > minVersionMatchLength) {
171 for (int i = minVersionMatchLength; i < other.versionParts.size(); i++) {
172 if (!"0".equals(other.versionParts.get(i))) {
173 return false;
174 }
175 }
176 }
177
178
179
180
181
182
183 return true;
184 }
185
186
187
188
189
190
191 @Override
192 public int hashCode() {
193 return new HashCodeBuilder(5, 71)
194 .append(versionParts)
195 .toHashCode();
196 }
197
198
199
200
201
202
203
204
205
206 public boolean matchesAtLeastThreeLevels(DependencyVersion version) {
207 if (version == null) {
208 return false;
209 }
210 if (Math.abs(this.versionParts.size() - version.versionParts.size()) >= 3) {
211 return false;
212 }
213
214 final int max = Math.min(this.versionParts.size(), version.versionParts.size());
215
216 boolean ret = true;
217 for (int i = 0; i < max; i++) {
218 final String thisVersion = this.versionParts.get(i);
219 final String otherVersion = version.getVersionParts().get(i);
220 if (i >= 3) {
221 if (thisVersion.compareToIgnoreCase(otherVersion) >= 0) {
222 ret = false;
223 break;
224 }
225 } else if (!thisVersion.equals(otherVersion)) {
226 ret = false;
227 break;
228 }
229 }
230
231 return ret;
232 }
233
234 @Override
235 public int compareTo(@NotNull DependencyVersion version) {
236 if (version == null) {
237 return 1;
238 }
239 final List<String> left = this.getVersionParts();
240 final List<String> right = version.getVersionParts();
241 final int max = Math.min(left.size(), right.size());
242
243 for (int i = 0; i < max; i++) {
244 final String lStr = left.get(i);
245 final String rStr = right.get(i);
246 if (lStr.equals(rStr)) {
247 continue;
248 }
249 try {
250 final int l = Integer.parseInt(lStr);
251 final int r = Integer.parseInt(rStr);
252 if (l < r) {
253 return -1;
254 } else if (l > r) {
255 return 1;
256 }
257 } catch (NumberFormatException ex) {
258 final int comp = left.get(i).compareTo(right.get(i));
259 if (comp < 0) {
260 return -1;
261 } else if (comp > 0) {
262 return 1;
263 }
264 }
265 }
266 return Integer.compare(left.size(), right.size());
267 }
268 }