1 | package org.springframework.batch.item.file.transform; |
2 | |
3 | import java.beans.PropertyEditorSupport; |
4 | import java.util.Arrays; |
5 | import java.util.Comparator; |
6 | |
7 | import org.springframework.util.Assert; |
8 | import org.springframework.util.StringUtils; |
9 | |
10 | /** |
11 | * Property editor implementation which parses string and creates array of |
12 | * ranges. Ranges can be provided in any order. </br> Input string should be |
13 | * provided in following format: 'range1, range2, range3,...' where range is |
14 | * specified as: |
15 | * <ul> |
16 | * <li>'X-Y', where X is minimum value and Y is maximum value (condition X<=Y |
17 | * is verified)</li> |
18 | * <li>or 'Z', where Z is minimum and maximum is calculated as (minimum of |
19 | * adjacent range - 1). Maximum of the last range is never calculated. Range |
20 | * stays unbound at maximum side if maximum value is not provided.</li> |
21 | * </ul> |
22 | * Minimum and maximum values can be from interval <1, Integer.MAX_VALUE-1> |
23 | * <p> |
24 | * Examples:</br> |
25 | * '1, 15, 25, 38, 55-60' is equal to '1-14, 15-24, 25-37, 38-54, 55-60' </br> |
26 | * '36, 14, 1-10, 15, 49-57' is equal to '36-48, 14-14, 1-10, 15-35, 49-57' |
27 | * <p> |
28 | * Property editor also allows to validate whether ranges are disjoint. Validation |
29 | * can be turned on/off by using {@link #setForceDisjointRanges(boolean)}. By default |
30 | * validation is turned off. |
31 | * |
32 | * @author peter.zozom |
33 | */ |
34 | public class RangeArrayPropertyEditor extends PropertyEditorSupport { |
35 | |
36 | private boolean forceDisjointRanges = false; |
37 | |
38 | /** |
39 | * Set force disjoint ranges. If set to TRUE, ranges are validated to be disjoint. |
40 | * For example: defining ranges '1-10, 5-15' will cause IllegalArgumentException in |
41 | * case of forceDisjointRanges=TRUE. |
42 | * @param forceDisjointRanges |
43 | */ |
44 | public void setForceDisjointRanges(boolean forceDisjointRanges) { |
45 | this.forceDisjointRanges = forceDisjointRanges; |
46 | } |
47 | |
48 | public void setAsText(String text) throws IllegalArgumentException { |
49 | |
50 | //split text into ranges |
51 | String[] strRanges = text.split(","); |
52 | Range[] ranges = new Range[strRanges.length]; |
53 | |
54 | //parse ranges and create array of Range objects |
55 | for (int i = 0; i < strRanges.length; i++) { |
56 | String[] range = strRanges[i].split("-"); |
57 | |
58 | int min; |
59 | int max; |
60 | |
61 | if ((range.length == 1) && (StringUtils.hasText(range[0]))) { |
62 | min = Integer.parseInt(range[0].trim()); |
63 | // correct max value will be assigned later |
64 | ranges[i] = new Range(min); |
65 | } else if ((range.length == 2) && (StringUtils.hasText(range[0])) |
66 | && (StringUtils.hasText(range[1]))) { |
67 | min = Integer.parseInt(range[0].trim()); |
68 | max = Integer.parseInt(range[1].trim()); |
69 | ranges[i] = new Range(min,max); |
70 | } else { |
71 | throw new IllegalArgumentException("Range[" + i + "]: range (" + strRanges[i] + ") is invalid"); |
72 | } |
73 | |
74 | } |
75 | |
76 | setMaxValues(ranges); |
77 | setValue(ranges); |
78 | } |
79 | |
80 | public String getAsText() { |
81 | Range[] ranges = (Range[])getValue(); |
82 | |
83 | StringBuffer sb = new StringBuffer(); |
84 | |
85 | for (int i = 0; i < ranges.length; i++) { |
86 | if(i>0) { |
87 | sb.append(", "); |
88 | } |
89 | sb.append(ranges[i]); |
90 | } |
91 | return sb.toString(); |
92 | } |
93 | |
94 | private void setMaxValues(Range[] ranges) { |
95 | |
96 | //clone array, original array should stay same |
97 | Range[] c = (Range[])ranges.clone(); |
98 | |
99 | //sort array of Ranges |
100 | Arrays.sort(c, new Comparator() { |
101 | public int compare(Object o1, Object o2) { |
102 | Range c1 = (Range)o1; |
103 | Range c2 = (Range)o2; |
104 | return c1.getMin()-c2.getMin(); |
105 | } |
106 | } |
107 | ); |
108 | |
109 | //set max values for all unbound ranges (except last range) |
110 | for (int i = 0; i < c.length - 1; i++) { |
111 | if (!c[i].hasMaxValue()) { |
112 | //set max value to (min value - 1) of the next range |
113 | c[i].setMax(c[i+1].getMin() - 1); |
114 | } |
115 | } |
116 | |
117 | if (forceDisjointRanges) { |
118 | verifyRanges(c); |
119 | } |
120 | } |
121 | |
122 | |
123 | private void verifyRanges(Range[] ranges) { |
124 | //verify that ranges are disjoint |
125 | for(int i = 1; i < ranges.length;i++) { |
126 | Assert.isTrue(ranges[i-1].getMax() < ranges[i].getMin(), |
127 | "Ranges must be disjoint. Range[" + (i-1) + "]: (" + ranges[i-1] + |
128 | ") Range[" + i +"]: (" + ranges[i] + ")"); |
129 | } |
130 | } |
131 | } |