1 package com.atlassian.plugins.rest.common.expand.parameter;
2
3 import com.google.common.collect.Sets;
4 import org.apache.commons.lang.StringUtils;
5
6 import static java.util.Arrays.asList;
7 import java.util.SortedSet;
8 import java.util.regex.Pattern;
9
10 final class IndexParser
11 {
12 private static final String INDEX = "-?\\d+";
13 private static final String RANGE = "(?:" + INDEX + ")?:(?:" + INDEX + ")?";
14
15 private static final Pattern INDEX_PATTERN = Pattern.compile(INDEX);
16 private static final Pattern RANGE_PATTERN = Pattern.compile(RANGE);
17
18 public final static Indexes ALL = new RangeIndexes(null, null);
19 public final static Indexes EMPTY = new EmptyIndexes();
20
21 private IndexParser()
22 {
23 }
24
25 static Indexes parse(String indexes)
26 {
27 if (StringUtils.isBlank(indexes))
28 {
29 return ALL;
30 }
31 else if (INDEX_PATTERN.matcher(indexes).matches())
32 {
33 return new SimpleIndexes(Integer.parseInt(indexes));
34 }
35 else if (RANGE_PATTERN.matcher(indexes).matches())
36 {
37 final String leftAsString = StringUtils.substringBefore(indexes, ":");
38 final String rightAsString = StringUtils.substringAfter(indexes, ":");
39 return new RangeIndexes(
40 StringUtils.isNotBlank(leftAsString) ? Integer.parseInt(leftAsString) : null,
41 StringUtils.isNotBlank(rightAsString) ? Integer.parseInt(rightAsString) : null);
42 }
43 else
44 {
45 return EMPTY;
46 }
47 }
48
49 static class SimpleIndexes implements Indexes
50 {
51 private final int index;
52
53 SimpleIndexes(int index)
54 {
55 this.index = index;
56 }
57
58 public boolean isRange()
59 {
60 return false;
61 }
62
63 public int getMinIndex(int size)
64 {
65 return getIndex(size);
66 }
67
68 public int getMaxIndex(int size)
69 {
70 return getIndex(size);
71 }
72
73 private int getIndex(int size)
74 {
75 return isInBound(index, size) ? toPositiveIndex(index, size) : -1;
76 }
77
78 public boolean contains(int i, int size)
79 {
80 return isInBound(index, size) && toPositiveIndex(index, size) == i;
81 }
82
83 public SortedSet<Integer> getIndexes(int size)
84 {
85 return isInBound(index, size) ? Sets.newTreeSet(asList(toPositiveIndex(index, size))) : Sets.<Integer>newTreeSet();
86 }
87 }
88
89 static class RangeIndexes implements Indexes
90 {
91 private final Integer left;
92 private final Integer right;
93
94 RangeIndexes(Integer left, Integer right)
95 {
96 this.left = left;
97 this.right = right;
98 }
99
100 public boolean isRange()
101 {
102 return true;
103 }
104
105 public int getMinIndex(int size)
106 {
107 return actualLeft(size);
108 }
109
110 public int getMaxIndex(int size)
111 {
112 return actualRight(size);
113 }
114
115 public boolean contains(int index, int size)
116 {
117 if (!isInBound(index, size))
118 {
119 return false;
120 }
121
122 final int p = toPositiveIndex(index, size);
123 return p >= actualLeft(size) && p <= actualRight(size);
124 }
125
126 public SortedSet<Integer> getIndexes(int size)
127 {
128 final SortedSet<Integer> allIndexes = Sets.newTreeSet();
129 final int actualLeft = actualLeft(size);
130 final int actualRight = actualRight(size);
131 if (actualLeft != -1 && actualRight != -1)
132 {
133 for (int i = actualLeft; i <= actualRight; i++)
134 {
135 allIndexes.add(i);
136 }
137 }
138 return allIndexes;
139 }
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154 private int actualLeft(int size)
155 {
156 if (size == 0)
157 {
158 return -1;
159 }
160
161 if (left == null)
162 {
163 return 0;
164 }
165
166 final int positiveLeft = toPositiveIndex(left, size);
167 if (positiveLeft < 0)
168 {
169 return 0;
170 }
171 else if (positiveLeft >= size)
172 {
173 return -1;
174 }
175 else
176 {
177 return positiveLeft;
178 }
179 }
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194 private int actualRight(int size)
195 {
196 if (size == 0)
197 {
198 return -1;
199 }
200
201 if (right == null)
202 {
203 return size - 1;
204 }
205
206 final int positiveRight = toPositiveIndex(right, size);
207 if (positiveRight < 0)
208 {
209 return -1;
210 }
211 else if (positiveRight >= size - 1)
212 {
213 return size - 1;
214 }
215 else
216 {
217 return positiveRight;
218 }
219 }
220 }
221
222 private static class EmptyIndexes implements Indexes
223 {
224 public boolean isRange()
225 {
226 return false;
227 }
228
229 public int getMinIndex(int size)
230 {
231 return -1;
232 }
233
234 public int getMaxIndex(int size)
235 {
236 return -1;
237 }
238
239 public boolean contains(int index, int size)
240 {
241 return false;
242 }
243
244 public SortedSet<Integer> getIndexes(int size)
245 {
246 return Sets.newTreeSet();
247 }
248
249 }
250
251 private static int toPositiveIndex(int i, int size)
252 {
253 return i < 0 ? i + size : i;
254 }
255
256 private static boolean isInBound(int i, int size)
257 {
258 final int p = toPositiveIndex(i, size);
259 return p >= 0 && p < size;
260 }
261 }