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
8 import java.util.SortedSet;
9 import java.util.regex.Pattern;
10
11 final class IndexParser {
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 static Indexes parse(String indexes) {
25 if (StringUtils.isBlank(indexes)) {
26 return ALL;
27 } else if (INDEX_PATTERN.matcher(indexes).matches()) {
28 return new SimpleIndexes(Integer.parseInt(indexes));
29 } else if (RANGE_PATTERN.matcher(indexes).matches()) {
30 final String leftAsString = StringUtils.substringBefore(indexes, ":");
31 final String rightAsString = StringUtils.substringAfter(indexes, ":");
32 return new RangeIndexes(
33 StringUtils.isNotBlank(leftAsString) ? Integer.parseInt(leftAsString) : null,
34 StringUtils.isNotBlank(rightAsString) ? Integer.parseInt(rightAsString) : null);
35 } else {
36 return EMPTY;
37 }
38 }
39
40 static class SimpleIndexes implements Indexes {
41 private final int index;
42
43 SimpleIndexes(int index) {
44 this.index = index;
45 }
46
47 public boolean isRange() {
48 return false;
49 }
50
51 public int getMinIndex(int size) {
52 return getIndex(size);
53 }
54
55 public int getMaxIndex(int size) {
56 return getIndex(size);
57 }
58
59 private int getIndex(int size) {
60 return isInBound(index, size) ? toPositiveIndex(index, size) : -1;
61 }
62
63 public boolean contains(int i, int size) {
64 return isInBound(index, size) && toPositiveIndex(index, size) == i;
65 }
66
67 public SortedSet<Integer> getIndexes(int size) {
68 return isInBound(index, size) ? Sets.newTreeSet(asList(toPositiveIndex(index, size))) : Sets.<Integer>newTreeSet();
69 }
70 }
71
72 static class RangeIndexes implements Indexes {
73 private final Integer left;
74 private final Integer right;
75
76 RangeIndexes(Integer left, Integer right) {
77 this.left = left;
78 this.right = right;
79 }
80
81 public boolean isRange() {
82 return true;
83 }
84
85 public int getMinIndex(int size) {
86 return actualLeft(size);
87 }
88
89 public int getMaxIndex(int size) {
90 return actualRight(size);
91 }
92
93 public boolean contains(int index, int size) {
94 if (!isInBound(index, size)) {
95 return false;
96 }
97
98 final int p = toPositiveIndex(index, size);
99 return p >= actualLeft(size) && p <= actualRight(size);
100 }
101
102 public SortedSet<Integer> getIndexes(int size) {
103 final SortedSet<Integer> allIndexes = Sets.newTreeSet();
104 final int actualLeft = actualLeft(size);
105 final int actualRight = actualRight(size);
106 if (actualLeft != -1 && actualRight != -1) {
107 for (int i = actualLeft; i <= actualRight; i++) {
108 allIndexes.add(i);
109 }
110 }
111 return allIndexes;
112 }
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128 private int actualLeft(int size) {
129 if (size == 0) {
130 return -1;
131 }
132
133 if (left == null) {
134 return 0;
135 }
136
137 final int positiveLeft = toPositiveIndex(left, size);
138 if (positiveLeft < 0) {
139 return 0;
140 } else if (positiveLeft >= size) {
141 return -1;
142 } else {
143 return positiveLeft;
144 }
145 }
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161 private int actualRight(int size) {
162 if (size == 0) {
163 return -1;
164 }
165
166 if (right == null) {
167 return size - 1;
168 }
169
170 final int positiveRight = toPositiveIndex(right, size);
171 if (positiveRight < 0) {
172 return -1;
173 } else if (positiveRight >= size - 1) {
174 return size - 1;
175 } else {
176 return positiveRight;
177 }
178 }
179 }
180
181 private static class EmptyIndexes implements Indexes {
182 public boolean isRange() {
183 return false;
184 }
185
186 public int getMinIndex(int size) {
187 return -1;
188 }
189
190 public int getMaxIndex(int size) {
191 return -1;
192 }
193
194 public boolean contains(int index, int size) {
195 return false;
196 }
197
198 public SortedSet<Integer> getIndexes(int size) {
199 return Sets.newTreeSet();
200 }
201
202 }
203
204 private static int toPositiveIndex(int i, int size) {
205 return i < 0 ? i + size : i;
206 }
207
208 private static boolean isInBound(int i, int size) {
209 final int p = toPositiveIndex(i, size);
210 return p >= 0 && p < size;
211 }
212 }