1 package com.atlassian.plugin.util;
2
3 import org.apache.commons.lang.StringUtils;
4
5 import java.math.BigInteger;
6 import java.util.Comparator;
7 import java.util.regex.Matcher;
8 import java.util.regex.Pattern;
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26 public class VersionStringComparator implements Comparator<String>
27 {
28 public static final String DELIMITER_PATTERN = "[\\.-]";
29 public static final String COMPONENT_PATTERN = "[\\d\\w]+";
30 public static final String VALID_VERSION_PATTERN = COMPONENT_PATTERN + "(?:" + DELIMITER_PATTERN + COMPONENT_PATTERN + ")*";
31 private static final Pattern START_WITH_INT_PATTERN = Pattern.compile("(^\\d+)");
32 public static final Pattern SNAPSHOT_PATTERN = Pattern.compile(".*-SNAPSHOT$");
33
34 public static boolean isValidVersionString(final String version)
35 {
36 return (version != null) && version.matches(VALID_VERSION_PATTERN);
37 }
38
39 public static boolean isSnapshotVersion(final String version)
40 {
41 return (version != null) && SNAPSHOT_PATTERN.matcher(version).matches();
42 }
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67 public int compare(final String version1, final String version2)
68 {
69
70 String thisVersion = "0";
71 if (StringUtils.isNotEmpty(version1))
72 {
73 thisVersion = version1.replaceAll(" ", "");
74 }
75 String compareVersion = "0";
76 if (StringUtils.isNotEmpty(version2))
77 {
78 compareVersion = version2.replaceAll(" ", "");
79 }
80
81 if (!thisVersion.matches(VALID_VERSION_PATTERN) || !compareVersion.matches(VALID_VERSION_PATTERN))
82 {
83 throw new IllegalArgumentException("Version number '" + thisVersion + "' cannot be compared to '" + compareVersion + "'");
84 }
85
86
87 final String[] v1 = thisVersion.split(DELIMITER_PATTERN);
88 final String[] v2 = compareVersion.split(DELIMITER_PATTERN);
89
90 final Comparator<String> componentComparator = new VersionStringComponentComparator();
91
92
93 for (int i = 0; i < (v1.length > v2.length ? v1.length : v2.length); i++)
94 {
95 final String component1 = i >= v1.length ? "0" : v1[i];
96 final String component2 = i >= v2.length ? "0" : v2[i];
97
98 if (componentComparator.compare(component1, component2) != 0)
99 {
100 return componentComparator.compare(component1, component2);
101 }
102 }
103
104 return 0;
105 }
106
107 private class VersionStringComponentComparator implements Comparator<String>
108 {
109 public static final int FIRST_GREATER = 1;
110 public static final int SECOND_GREATER = -1;
111
112 public int compare(final String component1, final String component2)
113 {
114 if (component1.equalsIgnoreCase(component2))
115 {
116 return 0;
117 }
118
119 if (isInteger(component1) && isInteger(component2))
120 {
121 return new BigInteger(component1).compareTo( new BigInteger(component2));
122 }
123
124
125 final BigInteger comp1BigIntPart = getStartingInteger(component1);
126 final BigInteger comp2BigIntPart = getStartingInteger(component2);
127 if (comp1BigIntPart != null && comp2BigIntPart != null)
128 {
129 if(comp1BigIntPart.compareTo(comp2BigIntPart) > 0){
130 return FIRST_GREATER;
131 }
132
133 if(comp2BigIntPart.compareTo(comp1BigIntPart) > 0){
134 return SECOND_GREATER;
135 }
136 }
137
138
139
140
141
142 if (isInteger(component1))
143 {
144 return FIRST_GREATER;
145 }
146 if (isInteger(component2))
147 {
148 return SECOND_GREATER;
149 }
150
151
152 return component1.compareToIgnoreCase(component2);
153 }
154
155 private boolean isInteger(final String string)
156 {
157 return string.matches("\\d+");
158 }
159
160 private BigInteger getStartingInteger(final String string)
161 {
162 Matcher matcher = START_WITH_INT_PATTERN.matcher(string);
163 if (matcher.find())
164 {
165
166 return new BigInteger(matcher.group(1));
167 }
168 return null;
169 }
170
171 }
172 }