1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34 package info.magnolia.module.model;
35
36 import java.util.regex.Matcher;
37 import java.util.regex.Pattern;
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54 public class VersionRange {
55
56 private final static Pattern boundedRangeSyntax = Pattern.compile("^([(\\[\\]])(.*),(.*)([)\\[\\]])$");
57
58 private final static Pattern notBoundedByBrackets = Pattern.compile("^([^(\\[\\]])(.*)([^)\\[\\]])$", Pattern.DOTALL);
59 private final Version from;
60 private final Version to;
61 private final Boundary leftBoundary;
62 private final Boundary rightBoundary;
63
64 public static VersionRange parse(String input) {
65 return new VersionRange(input);
66 }
67
68
69
70
71 @Deprecated
72 public VersionRange(String input) {
73 final String rangeDef = input == null ? "*" : input;
74 final int slashIdx = rangeDef.indexOf('/');
75 final int commaIdx = rangeDef.indexOf(',');
76 final Matcher boundedRangeMatcher = input != null ? boundedRangeSyntax.matcher(input) : null;
77 final Matcher notBoundedByBracketsMatcher = input != null ? notBoundedByBrackets.matcher(input) : null;
78 if (slashIdx < 0 && commaIdx < 0) {
79 this.from = newVersionOrStar(rangeDef, Version.UNDEFINED_FROM);
80 this.to = newVersionOrStar(rangeDef, Version.UNDEFINED_TO);
81 this.leftBoundary = Boundary.leftInclSq;
82 this.rightBoundary = Boundary.rightInclSq;
83 } else if (slashIdx >= 0 && notBoundedByBracketsMatcher != null && notBoundedByBracketsMatcher.matches()) {
84 this.from = newVersionOrStar(rangeDef.substring(0, slashIdx), Version.UNDEFINED_FROM);
85 this.to = newVersionOrStar(rangeDef.substring(slashIdx + 1), Version.UNDEFINED_TO);
86 this.leftBoundary = Boundary.leftInclSq;
87 this.rightBoundary = Boundary.rightInclSq;
88 } else if (boundedRangeMatcher != null && boundedRangeMatcher.matches()) {
89 this.leftBoundary = Boundary.bySymbol(boundedRangeMatcher.group(1).charAt(0), Hand.left);
90 this.from = newVersionOrStarOrEmpty(boundedRangeMatcher.group(2), Version.UNDEFINED_FROM);
91 this.to = newVersionOrStarOrEmpty(boundedRangeMatcher.group(3), Version.UNDEFINED_TO);
92 this.rightBoundary = Boundary.bySymbol(boundedRangeMatcher.group(4).charAt(0), Hand.right);
93 } else {
94 throw new IllegalArgumentException("Can't parse version range: " + input);
95 }
96 validate(input);
97 }
98
99
100
101
102 @Deprecated
103 public VersionRange(Version from, Version to) {
104 this(from, Boundary.leftInclSq, to, Boundary.rightInclSq);
105 }
106
107 private VersionRange(Version from, Boundary leftBoundary, Version to, Boundary rightBoundary) {
108 this.from = from;
109 this.to = to;
110 this.leftBoundary = leftBoundary;
111 this.rightBoundary = rightBoundary;
112 validate("from:" + from + ",to:" + to + ",leftBoundary:" + leftBoundary + ",rightBoundary:" + rightBoundary);
113 }
114
115 private Version newVersionOrStarOrEmpty(String rangeDef, Version ifUndefined) {
116 if ("".equals(rangeDef.trim())) {
117 return ifUndefined;
118 }
119 return newVersionOrStar(rangeDef, ifUndefined);
120 }
121
122 private Version newVersionOrStar(String rangeDef, Version ifUndefined) {
123 if ("*".equals(rangeDef.trim())) {
124 return ifUndefined;
125 }
126 return Version.parseVersion(rangeDef);
127 }
128
129
130
131
132 private void validate(String inputSpec) {
133 if (from.isStrictlyAfter(to)) {
134 throw new IllegalArgumentException("Invalid range: " + from + " is not after " + to + " (specified as \"" + inputSpec + "\")");
135 }
136 }
137
138
139
140
141 @Deprecated
142 public Version getFrom() {
143 return from;
144 }
145
146
147
148
149 @Deprecated
150 public Version getTo() {
151 return to;
152 }
153
154 public boolean contains(Version other) {
155 if (other.isEquivalent(from) && leftBoundary.include) {
156 return true;
157 }
158 if (other.isEquivalent(to) && rightBoundary.include) {
159 return true;
160 }
161 return other.isStrictlyAfter(from) && to.isStrictlyAfter(other);
162 }
163
164 @Override
165 public String toString() {
166 final StringBuilder sb = new StringBuilder();
167 if (from.equals(Version.UNDEFINED_FROM)) {
168 sb.append(Boundary.leftParen.symbol);
169 } else {
170 sb.append(leftBoundary.symbol);
171 sb.append(from);
172 }
173 sb.append(',');
174 if (to.equals(Version.UNDEFINED_TO)) {
175 sb.append(Boundary.rightParen.symbol);
176 } else {
177 sb.append(to);
178 sb.append(rightBoundary.symbol);
179 }
180 return sb.toString();
181 }
182
183 private static enum Hand {left, right}
184
185 private static enum Boundary {
186 leftExclSq(Hand.left, ']', false), leftInclSq(Hand.left, '[', true), leftParen(Hand.left, '(', false),
187 rightExclSq(Hand.right, '[', false), rightInclSq(Hand.right, ']', true), rightParen(Hand.right, ')', false);
188
189 private final Hand hand;
190 private final char symbol;
191 private final boolean include;
192
193 private Boundary(Hand hand, char symbol, boolean include) {
194 this.hand = hand;
195 this.symbol = symbol;
196 this.include = include;
197 }
198
199 static Boundary bySymbol(char c, Hand h) {
200 for (Boundary boundary : values()) {
201 if (boundary.hand == h && boundary.symbol == c) {
202 return boundary;
203 }
204 }
205 throw new IllegalArgumentException("No " + Boundary.class.getSimpleName() + " found by symbol " + c + " for " + h.name() + " boundary. ");
206 }
207 }
208
209 }