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   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          * Will calculate the actual positive index matching {link #left}.
143          * <ul>
144          * <li>If the index is negative it will calculate a positive value,</li>
145          * <li>If the size of the list is 0 it will return -1,</li>
146          * <li>If the index is not specified or  less than 0, then 0 is returned</li>
147          * <li>If the index is greater than the {@code size} of the list then -1 is returned</li>
148          * <li>else the positive value of the index is returned.</li>
149          * </ul>
150          * @param size the size of the list to consider.
151          * @return the actual 'left' index as defined.
152          * @see #actualRight(int)
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          * Will calculate the actual positive index matching {link #right}.
183          * <ul>
184          * <li>If the index is negative it will calculate a positive value,</li>
185          * <li>If the size of the list is 0 it will return -1,</li>
186          * <li>If the index is not specified or greater than {@code size - 1}, then {@code size - 1} is returned</li>
187          * <li>If the index is less than 0 then -1 is returned</li>
188          * <li>else the positive value of the index is returned.</li>
189          * </ul>
190          * @param size the size of the list to consider.
191          * @return the actual 'right' index as defined.
192          * @see #actualLeft(int)
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 }