1
2
3
4
5
6
7
8
9
10
11
12
13 package org.abstracthorizon.danube.http.util;
14
15 import java.util.ArrayList;
16 import java.util.Collections;
17 import java.util.Comparator;
18 import java.util.List;
19
20
21
22
23
24
25 public class Ranges {
26
27
28 protected Range singleRange;
29
30
31 protected List<Range> ranges;
32
33
34 protected long size = -1;
35
36
37
38
39 public Ranges() {
40 }
41
42
43
44
45
46 public boolean isMultiRange() {
47 return ranges != null;
48 }
49
50
51
52
53
54 public void setSize(long size) {
55 this.size = size;
56 if (isMultiRange()) {
57 for (Range range : ranges) {
58 updateSize(range);
59 }
60 makeCanonic();
61 } else if (singleRange != null) {
62 updateSize(singleRange);
63 }
64 }
65
66
67
68
69
70 public long getSize() {
71 return size;
72 }
73
74
75
76
77
78 public List<Range> getRanges() {
79 return ranges;
80 }
81
82
83
84
85
86 public Range getSingleRange() {
87 return singleRange;
88 }
89
90
91
92
93
94 protected void updateSize(Range range) {
95 if (size >= 0) {
96 if (range.from == -1) {
97 range.from = size - range.to;
98 range.to = size - 1;
99 }
100 if (range.to == -1) {
101 range.to = size - 1;
102 }
103 }
104 }
105
106
107
108
109
110
111
112 public void addRange(long from, long to) {
113 if ((from != -1) || (to != -1)) {
114 Range range = new Range(from , to);
115 updateSize(range);
116 if (singleRange == null) {
117 if (ranges == null) {
118 singleRange = range;
119 } else {
120 ranges.add(range);
121 makeCanonic();
122 }
123 } else {
124 if (!combine(singleRange, range)) {
125 ranges = new ArrayList<Range>();
126 ranges.add(singleRange);
127 ranges.add(range);
128 singleRange = null;
129 }
130 }
131 }
132 }
133
134
135
136
137
138
139
140
141
142
143 protected boolean combine(Range range1, Range range2) {
144 if (range1.from == -1) {
145 if (range2.from == -1) {
146 if (range1.to < range2.to) {
147 range1.to = range2.to;
148 }
149 return true;
150 } else {
151 return false;
152 }
153 } else if (range1.to == -1) {
154 if (range2.to == -1) {
155 if (range1.from > range2.from) {
156 range1.from = range2.from;
157 }
158 return true;
159 } else {
160 return false;
161 }
162 } else {
163 if (range1.from <= range2.from) {
164 if (range1.to <= range2.from) {
165 return false;
166 } else {
167 if (range1.to < range2.to) {
168 range1.to = range2.to;
169 }
170 return true;
171 }
172 } else {
173 if (range1.from >= range2.to) {
174 return false;
175 } else {
176 if (range1.to < range2.to) {
177 range1.from = range2.from;
178 range1.to = range2.to;
179 } else {
180 range1.from = range2.from;
181 }
182 return true;
183 }
184 }
185 }
186 }
187
188
189
190
191 protected void makeCanonic() {
192 if (ranges != null) {
193 Collections.sort(ranges, new Comparator<Range>() {
194 public int compare(Range r1, Range r2) {
195 if (r1.from < r2.from) {
196 return -1;
197 } else if (r1.from > r2.from) {
198 return 1;
199 } else if (r1.to < r2.to) {
200 return -1;
201 } else if (r1.to > r2.to) {
202 return 1;
203 }
204 return 0;
205 }
206 });
207 int i = 0;
208 while (i + 1 < ranges.size()) {
209 Range r1 = ranges.get(i);
210 Range r2 = ranges.get(i + 1);
211 if (combine(r1, r2)) {
212 ranges.remove(i + 1);
213 } else {
214 i = i + 1;
215 }
216 }
217 }
218 }
219
220
221
222
223
224
225 public static Ranges parseRange(String ranges) {
226 if (ranges.startsWith("bytes=")) {
227 Ranges result = new Ranges();
228 int j = 6;
229 int i = ranges.indexOf(',');
230 while (i > 0) {
231 if (i > j) {
232 if (!parseOneRange(ranges, j, i, result)) {
233 return null;
234 }
235 j = i + 1;
236 if (j <= ranges.length()) {
237 i = ranges.indexOf(',', j + 1);
238 } else {
239 return result;
240 }
241 } else {
242 return null;
243 }
244 }
245 if (!parseOneRange(ranges, j, ranges.length(), result)) {
246 return null;
247 }
248 return result;
249 } else {
250 return null;
251 }
252 }
253
254
255
256
257
258
259 public static Ranges parseContentRange(String ranges) {
260 if (ranges.startsWith("bytes ")) {
261 Ranges result = new Ranges();
262 long s = -1;
263 int j = 6;
264 int i = ranges.indexOf('/');
265 if (i >= 0) {
266 if (i + 1 >= ranges.length()) {
267 return null;
268 }
269 if ((i + 2 == ranges.length() && ranges.charAt(i + 1) == '*')) {
270 s = -1;
271 } else {
272 s = parseLong(ranges, i+1, ranges.length());
273 if (s == -1) {
274 return null;
275 }
276 result.setSize(s);
277 }
278 } else {
279 i = ranges.length();
280 }
281 if (!parseOneRange(ranges, j, i, result)) {
282 return null;
283 }
284 return result;
285 } else {
286 return null;
287 }
288 }
289
290
291
292
293
294
295
296
297
298 protected static boolean parseOneRange(String input, int from, int to, Ranges result) {
299 long f = -1;
300 long t = -1;
301 int i = input.indexOf('-', from);
302 if ((i < from) || (i > to)) {
303 return false;
304 }
305 if (i > from) {
306 f = parseLong(input, from, i);
307 if (f < 0) {
308 return false;
309 }
310 }
311 if (i + 1 < to) {
312 t = parseLong(input, i + 1, to);
313 if (t < 0) {
314 return false;
315 }
316 }
317 result.addRange(f, t);
318 return true;
319 }
320
321
322
323
324
325
326
327 protected static long parseLong(String input, int from, int to) {
328 long res = 0;
329 int c = input.charAt(from);
330 if (Character.isDigit(c)) {
331 res = c - '0';
332 from = from + 1;
333 while (from < to) {
334 c = input.charAt(from);
335 if (Character.isDigit(c)) {
336 res = res * 10 + c - '0';
337 from = from + 1;
338 } else {
339 return -1;
340 }
341 }
342 return res;
343 } else {
344 return -1;
345 }
346 }
347
348
349
350
351 public String toString() {
352 StringBuffer res = new StringBuffer("Ranges[");
353 boolean first = true;
354 for (Range range : ranges) {
355 if (first) {
356 first = false;
357 } else {
358 res.append(',');
359 }
360 res.append(range.toString());
361 }
362 res.append(']');
363 return res.toString();
364 }
365
366
367
368
369
370
371 public String format() {
372 if (isMultiRange()) {
373 return null;
374 }
375 StringBuffer res = new StringBuffer("bytes ");
376 if (singleRange.to == -1) {
377 singleRange.to = size -1;
378 }
379 if (singleRange.from == -1) {
380 singleRange.from = 0;
381 }
382 res.append(singleRange.from).append('-').append(singleRange.to).append('/').append(size);
383 return res.toString();
384 }
385
386
387
388
389
390
391 public static class Range {
392
393
394 private long from = -1;
395
396
397 private long to = -1;
398
399
400
401
402
403
404 public Range(long from, long to) {
405 this.from = from;
406 this.to = to;
407 }
408
409
410
411
412
413 public long getFrom() {
414 return from;
415 }
416
417
418
419
420
421 public long getTo() {
422 return to;
423 }
424
425
426
427
428
429 public long getSize() {
430 return to - from;
431 }
432
433
434
435
436
437 public boolean isSuffix() {
438 return from == -1;
439 }
440
441
442
443
444
445 public boolean isPrefix() {
446 return to == -1;
447 }
448
449
450
451
452
453 public String toString() {
454 if (from == -1) {
455 return "Range[-" + to + "]";
456 } else if (to == -1) {
457 return "Range[" + from + "-]";
458 } else {
459 return "Range[" + from + "-" + to + "]";
460 }
461 }
462 }
463 }