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
32 public static boolean isValidVersionString(final String version)
33 {
34 return (version != null) && version.matches(VALID_VERSION_PATTERN);
35 }
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60 public int compare(final String version1, final String version2)
61 {
62
63 String thisVersion = "0";
64 if (StringUtils.isNotEmpty(version1))
65 {
66 thisVersion = version1.replaceAll(" ", "");
67 }
68 String compareVersion = "0";
69 if (StringUtils.isNotEmpty(version2))
70 {
71 compareVersion = version2.replaceAll(" ", "");
72 }
73
74 if (!thisVersion.matches(VALID_VERSION_PATTERN) || !compareVersion.matches(VALID_VERSION_PATTERN))
75 {
76 throw new IllegalArgumentException("Version number '" + thisVersion + "' cannot be compared to '" + compareVersion + "'");
77 }
78
79
80 final String[] v1 = thisVersion.split(DELIMITER_PATTERN);
81 final String[] v2 = compareVersion.split(DELIMITER_PATTERN);
82
83 final Comparator<String> componentComparator = new VersionStringComponentComparator();
84
85
86 for (int i = 0; i < (v1.length > v2.length ? v1.length : v2.length); i++)
87 {
88 final String component1 = i >= v1.length ? "0" : v1[i];
89 final String component2 = i >= v2.length ? "0" : v2[i];
90
91 if (componentComparator.compare(component1, component2) != 0)
92 {
93 return componentComparator.compare(component1, component2);
94 }
95 }
96
97 return 0;
98 }
99
100 private class VersionStringComponentComparator implements Comparator<String>
101 {
102 public static final int FIRST_GREATER = 1;
103 public static final int SECOND_GREATER = -1;
104
105 public int compare(final String component1, final String component2)
106 {
107 if (component1.equalsIgnoreCase(component2))
108 {
109 return 0;
110 }
111
112 if (isInteger(component1) && isInteger(component2))
113 {
114
115 if (Integer.parseInt(component1) > Integer.parseInt(component2))
116 {
117 return FIRST_GREATER;
118 }
119 if (Integer.parseInt(component2) > Integer.parseInt(component1))
120 {
121 return SECOND_GREATER;
122 }
123 return 0;
124 }
125
126
127 final Integer comp1IntPart = getStartingInteger(component1);
128 final Integer comp2IntPart = getStartingInteger(component2);
129 if (comp1IntPart != null && comp2IntPart != null)
130 {
131 if (comp1IntPart > comp2IntPart)
132 {
133 return FIRST_GREATER;
134 }
135 else if (comp2IntPart > comp1IntPart)
136 {
137 return SECOND_GREATER;
138 }
139 }
140
141
142
143
144
145 if (isInteger(component1))
146 {
147 return FIRST_GREATER;
148 }
149 if (isInteger(component2))
150 {
151 return SECOND_GREATER;
152 }
153
154
155 return component1.compareToIgnoreCase(component2);
156 }
157
158 private boolean isInteger(final String string)
159 {
160 return string.matches("\\d+");
161 }
162
163 private Integer getStartingInteger(final String string)
164 {
165 Matcher matcher = START_WITH_INT_PATTERN.matcher(string);
166 if (matcher.find())
167 {
168
169 return new Integer(matcher.group(1));
170 }
171 return null;
172 }
173
174 }
175 }