View Javadoc

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          * Will calculate the actual positive index matching {link #left}.
116          * <ul>
117          * <li>If the index is negative it will calculate a positive value,</li>
118          * <li>If the size of the list is 0 it will return -1,</li>
119          * <li>If the index is not specified or  less than 0, then 0 is returned</li>
120          * <li>If the index is greater than the {@code size} of the list then -1 is returned</li>
121          * <li>else the positive value of the index is returned.</li>
122          * </ul>
123          *
124          * @param size the size of the list to consider.
125          * @return the actual 'left' index as defined.
126          * @see #actualRight(int)
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          * Will calculate the actual positive index matching {link #right}.
149          * <ul>
150          * <li>If the index is negative it will calculate a positive value,</li>
151          * <li>If the size of the list is 0 it will return -1,</li>
152          * <li>If the index is not specified or greater than {@code size - 1}, then {@code size - 1} is returned</li>
153          * <li>If the index is less than 0 then -1 is returned</li>
154          * <li>else the positive value of the index is returned.</li>
155          * </ul>
156          *
157          * @param size the size of the list to consider.
158          * @return the actual 'right' index as defined.
159          * @see #actualLeft(int)
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 }