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