PageRenderTime 31ms CodeModel.GetById 23ms RepoModel.GetById 0ms app.codeStats 0ms

/atlassian-plugins-core/src/main/java/com/atlassian/plugin/util/VersionRange.java

https://bitbucket.org/purewind/atlassian-plugins
Java | 438 lines | 325 code | 67 blank | 46 comment | 76 complexity | e47afbb863598f784d17f7206f79b82d MD5 | raw file
  1. package com.atlassian.plugin.util;
  2. import com.google.common.base.Objects;
  3. import java.util.Comparator;
  4. import java.util.regex.Matcher;
  5. import java.util.regex.Pattern;
  6. import static com.atlassian.plugin.util.VersionStringComparator.VALID_VERSION_PATTERN;
  7. import static com.google.common.base.Preconditions.checkNotNull;
  8. import static com.google.common.base.Preconditions.checkState;
  9. /**
  10. * Represents a version range. Version ranges can be built programmatically or parsed with the
  11. * following definition:
  12. * <table>
  13. * <thead>
  14. * <th>Range</th>
  15. * <th>Meaning</th>
  16. * </thead>
  17. * <tbody>
  18. * <tr>
  19. * <td>1.0</td>
  20. * <td>x >= 1.0</td>
  21. * </tr>
  22. * <tr>
  23. * <td>(,1.0]</td>
  24. * <td>x <= 1.0</td>
  25. * </tr>
  26. * <tr>
  27. * <td>(,1.0)</td>
  28. * <td>x < 1.0</td>
  29. * </tr>
  30. * <tr>
  31. * <td>[1.0]</td>
  32. * <td>x == 1.0</td>
  33. * </tr>
  34. * <tr>
  35. * <td>[1.0,)</td>
  36. * <td>x >= 1.0</td>
  37. * </tr>
  38. * <tr>
  39. * <td>(1.0,)</td>
  40. * <td>x > 1.0</td>
  41. * </tr>
  42. * <tr>
  43. * <td>(1.0,2.0)</td>
  44. * <td>1.0 < x < 2.0</td>
  45. * </tr>
  46. * <tr>
  47. * <td>[1.0,2.0]</td>
  48. * <td>1.0 <= x <= 2.0</td>
  49. * </tr>
  50. * </tbody>
  51. * </table>
  52. *
  53. * @since 3.0
  54. */
  55. public abstract class VersionRange {
  56. private static final Pattern RANGE_PATTERN = Pattern.compile("(\\(|\\[)?(" + VALID_VERSION_PATTERN + ")?(?:,(" + VALID_VERSION_PATTERN + ")?)?(\\)|\\])?");
  57. private VersionRange() {
  58. }
  59. abstract boolean isInRange(String version);
  60. public VersionRange or(VersionRange other) {
  61. return new OrVersionRange(this, other);
  62. }
  63. public static VersionRange empty() {
  64. return new EmptyVersionRange();
  65. }
  66. public static VersionRange all() {
  67. return new AllVersionRange();
  68. }
  69. public static VersionRange parse(String range) {
  70. final Matcher matcher = RANGE_PATTERN.matcher(range);
  71. checkState(matcher.matches(), "Range '" + range + "' doesn't match pattern " + RANGE_PATTERN.pattern());
  72. final String leftParenthesis = matcher.group(1);
  73. final String leftVersion = matcher.group(2);
  74. final String rightVersion = matcher.group(3);
  75. final String rightParenthesis = matcher.group(4);
  76. checkState(leftVersion != null || rightVersion != null, "No version configured for range!");
  77. if (leftParenthesis == null) {
  78. checkState(leftVersion != null);
  79. checkState(rightParenthesis == null);
  80. checkState(rightVersion == null);
  81. return VersionRange.include(leftVersion).unbounded();
  82. } else if (leftParenthesis.equals("[") && rightParenthesis.equals("]") && rightVersion == null) // single version
  83. {
  84. return VersionRange.single(leftVersion);
  85. } else {
  86. final ActualVersionRangeBuilder builder;
  87. if (leftParenthesis.equals("[")) {
  88. checkState(leftVersion != null);
  89. builder = VersionRange.include(leftVersion);
  90. } else if (leftParenthesis.equals("(")) {
  91. if (leftVersion != null) {
  92. builder = VersionRange.exclude(leftVersion);
  93. } else {
  94. builder = VersionRange.unbounded();
  95. }
  96. } else {
  97. throw new IllegalStateException("Incorrect start of range! " + leftParenthesis);
  98. }
  99. if (rightParenthesis.equals("]")) {
  100. checkState(rightVersion != null);
  101. return builder.include(rightVersion);
  102. } else if (rightParenthesis.equals(")")) {
  103. if (rightVersion != null) {
  104. return builder.exclude(rightVersion);
  105. } else {
  106. return builder.unbounded();
  107. }
  108. } else {
  109. throw new IllegalStateException("Incorrect ent of range! " + rightParenthesis);
  110. }
  111. }
  112. }
  113. public static VersionRange single(String version) {
  114. return new SingleVersionRange(version);
  115. }
  116. public static ActualVersionRangeBuilder include(String version) {
  117. return new ActualVersionRangeBuilder(true, version);
  118. }
  119. public static ActualVersionRangeBuilder exclude(String version) {
  120. return new ActualVersionRangeBuilder(false, version);
  121. }
  122. public static ActualVersionRangeBuilder unbounded() {
  123. return new ActualVersionRangeBuilder(true, null);
  124. }
  125. private static class SingleVersionRange extends VersionRange {
  126. private final String version;
  127. private SingleVersionRange(String version) {
  128. this.version = checkNotNull(version);
  129. }
  130. @Override
  131. boolean isInRange(String v) {
  132. return newVersionComparator().compare(this.version, v) == 0;
  133. }
  134. @Override
  135. public int hashCode() {
  136. return Objects.hashCode(version);
  137. }
  138. @Override
  139. public boolean equals(Object obj) {
  140. if (obj == null) {
  141. return false;
  142. }
  143. if (getClass() != obj.getClass()) {
  144. return false;
  145. }
  146. final SingleVersionRange that = (SingleVersionRange) obj;
  147. return Objects.equal(this.version, that.version);
  148. }
  149. @Override
  150. public String toString() {
  151. return "[" + version + "]";
  152. }
  153. }
  154. public static final class ActualVersionRangeBuilder {
  155. private final boolean leftIncluded;
  156. private final String leftVersion;
  157. public ActualVersionRangeBuilder(boolean leftIncluded, String leftVersion) {
  158. this.leftIncluded = leftIncluded;
  159. this.leftVersion = leftVersion;
  160. }
  161. public VersionRange include(String version) {
  162. return newRange(version, true);
  163. }
  164. public VersionRange exclude(String version) {
  165. return newRange(version, false);
  166. }
  167. private VersionRange newRange(String version, boolean rightIncluded) {
  168. if (leftVersion != null) {
  169. return newActualRange(version, rightIncluded);
  170. } else {
  171. return newLeftUnboundedRange(version, rightIncluded);
  172. }
  173. }
  174. private LeftUnboundedVersionRange newLeftUnboundedRange(String version, boolean rightIncluded) {
  175. return new LeftUnboundedVersionRange(rightIncluded, version);
  176. }
  177. private ActualVersionRange newActualRange(String version, boolean rightIncluded) {
  178. return new ActualVersionRange(leftIncluded, leftVersion, rightIncluded, version);
  179. }
  180. public VersionRange unbounded() {
  181. if (leftVersion == null) {
  182. throw new IllegalStateException();
  183. }
  184. return new RightUnboundedVersionRange(leftIncluded, leftVersion);
  185. }
  186. }
  187. private static final class ActualVersionRange extends VersionRange {
  188. private final boolean leftIncluded;
  189. private final String leftVersion;
  190. private final boolean rightIncluded;
  191. private final String rightVersion;
  192. private ActualVersionRange(
  193. boolean leftIncluded, String leftVersion,
  194. boolean rightIncluded, String rightVersion) {
  195. this.leftIncluded = leftIncluded;
  196. this.leftVersion = checkNotNull(leftVersion);
  197. this.rightIncluded = rightIncluded;
  198. this.rightVersion = checkNotNull(rightVersion);
  199. }
  200. @Override
  201. boolean isInRange(String v) {
  202. return isGreaterThan(leftIncluded, leftVersion, v)
  203. && isLowerThan(v, rightVersion, rightIncluded);
  204. }
  205. @Override
  206. public int hashCode() {
  207. return Objects.hashCode(leftIncluded, leftVersion, rightIncluded, rightVersion);
  208. }
  209. @Override
  210. public boolean equals(Object obj) {
  211. if (obj == null) {
  212. return false;
  213. }
  214. if (getClass() != obj.getClass()) {
  215. return false;
  216. }
  217. final ActualVersionRange that = (ActualVersionRange) obj;
  218. return Objects.equal(this.leftIncluded, that.leftIncluded)
  219. && Objects.equal(this.leftVersion, that.leftVersion)
  220. && Objects.equal(this.rightIncluded, that.rightIncluded)
  221. && Objects.equal(this.rightVersion, that.rightVersion);
  222. }
  223. @Override
  224. public String toString() {
  225. return (leftIncluded ? "[" : "(") + leftVersion + "," + rightVersion + (rightIncluded ? "]" : ")");
  226. }
  227. }
  228. private static final class LeftUnboundedVersionRange extends VersionRange {
  229. private final boolean rightIncluded;
  230. private final String rightVersion;
  231. private LeftUnboundedVersionRange(boolean rightIncluded, String rightVersion) {
  232. this.rightIncluded = rightIncluded;
  233. this.rightVersion = checkNotNull(rightVersion);
  234. }
  235. @Override
  236. boolean isInRange(String v) {
  237. return isLowerThan(v, rightVersion, rightIncluded);
  238. }
  239. @Override
  240. public int hashCode() {
  241. return Objects.hashCode(rightIncluded, rightVersion);
  242. }
  243. @Override
  244. public boolean equals(Object obj) {
  245. if (obj == null) {
  246. return false;
  247. }
  248. if (getClass() != obj.getClass()) {
  249. return false;
  250. }
  251. final LeftUnboundedVersionRange that = (LeftUnboundedVersionRange) obj;
  252. return Objects.equal(this.rightIncluded, that.rightIncluded)
  253. && Objects.equal(this.rightVersion, that.rightVersion);
  254. }
  255. @Override
  256. public String toString() {
  257. return "(," + rightVersion + (rightIncluded ? "]" : ")");
  258. }
  259. }
  260. private static final class RightUnboundedVersionRange extends VersionRange {
  261. private final boolean leftIncluded;
  262. private final String leftVersion;
  263. private RightUnboundedVersionRange(boolean leftIncluded, String leftVersion) {
  264. this.leftIncluded = leftIncluded;
  265. this.leftVersion = checkNotNull(leftVersion);
  266. }
  267. @Override
  268. boolean isInRange(String v) {
  269. return isGreaterThan(leftIncluded, leftVersion, v);
  270. }
  271. @Override
  272. public int hashCode() {
  273. return Objects.hashCode(leftIncluded, leftVersion);
  274. }
  275. @Override
  276. public boolean equals(Object obj) {
  277. if (obj == null) {
  278. return false;
  279. }
  280. if (getClass() != obj.getClass()) {
  281. return false;
  282. }
  283. final RightUnboundedVersionRange that = (RightUnboundedVersionRange) obj;
  284. return Objects.equal(this.leftIncluded, that.leftIncluded)
  285. && Objects.equal(this.leftVersion, that.leftVersion);
  286. }
  287. @Override
  288. public String toString() {
  289. return (leftIncluded ? "[" : "(") + leftVersion + ",)";
  290. }
  291. }
  292. private static final class AllVersionRange extends VersionRange {
  293. @Override
  294. boolean isInRange(String version) {
  295. return true;
  296. }
  297. @Override
  298. public int hashCode() {
  299. return 1;
  300. }
  301. @Override
  302. public boolean equals(Object obj) {
  303. return obj != null && getClass() == obj.getClass();
  304. }
  305. @Override
  306. public String toString() {
  307. return "(,)";
  308. }
  309. }
  310. private static final class EmptyVersionRange extends VersionRange {
  311. @Override
  312. boolean isInRange(String version) {
  313. return false;
  314. }
  315. @Override
  316. public int hashCode() {
  317. return 2;
  318. }
  319. @Override
  320. public boolean equals(Object obj) {
  321. return obj != null && getClass() == obj.getClass();
  322. }
  323. @Override
  324. public String toString() {
  325. return "()";
  326. }
  327. }
  328. private static final class OrVersionRange extends VersionRange {
  329. private final VersionRange or1;
  330. private final VersionRange or2;
  331. private OrVersionRange(VersionRange or1, VersionRange or2) {
  332. this.or1 = checkNotNull(or1);
  333. this.or2 = checkNotNull(or2);
  334. }
  335. @Override
  336. boolean isInRange(String v) {
  337. return or1.isInRange(v) || or2.isInRange(v);
  338. }
  339. @Override
  340. public int hashCode() {
  341. return Objects.hashCode(or1, or2);
  342. }
  343. @Override
  344. public boolean equals(Object obj) {
  345. if (obj == null) {
  346. return false;
  347. }
  348. if (getClass() != obj.getClass()) {
  349. return false;
  350. }
  351. final OrVersionRange that = (OrVersionRange) obj;
  352. return Objects.equal(this.or1, that.or1)
  353. && Objects.equal(this.or2, that.or2);
  354. }
  355. @Override
  356. public String toString() {
  357. return or1 + "," + or2;
  358. }
  359. }
  360. private static boolean isLowerThan(String version, String rightVersion, boolean rightIncluded) {
  361. final int rightCompare = newVersionComparator().compare(rightVersion, version);
  362. return rightCompare > 0 || (rightIncluded && rightCompare == 0);
  363. }
  364. private static boolean isGreaterThan(boolean leftIncluded, String leftVersion, String version) {
  365. final int leftCompare = newVersionComparator().compare(version, leftVersion);
  366. return leftCompare > 0 || (leftIncluded && leftCompare == 0);
  367. }
  368. private static Comparator<String> newVersionComparator() {
  369. return new VersionStringComparator();
  370. }
  371. }