1 | /* |
2 | * Copyright 2006-2007 the original author or authors. |
3 | * |
4 | * Licensed under the Apache License, Version 2.0 (the "License"); |
5 | * you may not use this file except in compliance with the License. |
6 | * You may obtain a copy of the License at |
7 | * |
8 | * http://www.apache.org/licenses/LICENSE-2.0 |
9 | * |
10 | * Unless required by applicable law or agreed to in writing, software |
11 | * distributed under the License is distributed on an "AS IS" BASIS, |
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
13 | * See the License for the specific language governing permissions and |
14 | * limitations under the License. |
15 | */ |
16 | |
17 | package org.springframework.batch.item.file.transform; |
18 | |
19 | import java.beans.PropertyEditorSupport; |
20 | import java.util.Arrays; |
21 | import java.util.Comparator; |
22 | |
23 | import org.springframework.util.Assert; |
24 | import org.springframework.util.StringUtils; |
25 | |
26 | /** |
27 | * Property editor implementation which parses string and creates array of |
28 | * ranges. Ranges can be provided in any order. </br> Input string should be |
29 | * provided in following format: 'range1, range2, range3,...' where range is |
30 | * specified as: |
31 | * <ul> |
32 | * <li>'X-Y', where X is minimum value and Y is maximum value (condition X<=Y |
33 | * is verified)</li> |
34 | * <li>or 'Z', where Z is minimum and maximum is calculated as (minimum of |
35 | * adjacent range - 1). Maximum of the last range is never calculated. Range |
36 | * stays unbound at maximum side if maximum value is not provided.</li> |
37 | * </ul> |
38 | * Minimum and maximum values can be from interval <1, Integer.MAX_VALUE-1> |
39 | * <p> |
40 | * Examples:</br> |
41 | * '1, 15, 25, 38, 55-60' is equal to '1-14, 15-24, 25-37, 38-54, 55-60' </br> |
42 | * '36, 14, 1-10, 15, 49-57' is equal to '36-48, 14-14, 1-10, 15-35, 49-57' |
43 | * <p> |
44 | * Property editor also allows to validate whether ranges are disjoint. Validation |
45 | * can be turned on/off by using {@link #setForceDisjointRanges(boolean)}. By default |
46 | * validation is turned off. |
47 | * |
48 | * @author peter.zozom |
49 | */ |
50 | public class RangeArrayPropertyEditor extends PropertyEditorSupport { |
51 | |
52 | private boolean forceDisjointRanges = false; |
53 | |
54 | /** |
55 | * Set force disjoint ranges. If set to TRUE, ranges are validated to be disjoint. |
56 | * For example: defining ranges '1-10, 5-15' will cause IllegalArgumentException in |
57 | * case of forceDisjointRanges=TRUE. |
58 | * @param forceDisjointRanges |
59 | */ |
60 | public void setForceDisjointRanges(boolean forceDisjointRanges) { |
61 | this.forceDisjointRanges = forceDisjointRanges; |
62 | } |
63 | |
64 | public void setAsText(String text) throws IllegalArgumentException { |
65 | |
66 | //split text into ranges |
67 | String[] strRanges = text.split(","); |
68 | Range[] ranges = new Range[strRanges.length]; |
69 | |
70 | //parse ranges and create array of Range objects |
71 | for (int i = 0; i < strRanges.length; i++) { |
72 | String[] range = strRanges[i].split("-"); |
73 | |
74 | int min; |
75 | int max; |
76 | |
77 | if ((range.length == 1) && (StringUtils.hasText(range[0]))) { |
78 | min = Integer.parseInt(range[0].trim()); |
79 | // correct max value will be assigned later |
80 | ranges[i] = new Range(min); |
81 | } else if ((range.length == 2) && (StringUtils.hasText(range[0])) |
82 | && (StringUtils.hasText(range[1]))) { |
83 | min = Integer.parseInt(range[0].trim()); |
84 | max = Integer.parseInt(range[1].trim()); |
85 | ranges[i] = new Range(min,max); |
86 | } else { |
87 | throw new IllegalArgumentException("Range[" + i + "]: range (" + strRanges[i] + ") is invalid"); |
88 | } |
89 | |
90 | } |
91 | |
92 | setMaxValues(ranges); |
93 | setValue(ranges); |
94 | } |
95 | |
96 | public String getAsText() { |
97 | Range[] ranges = (Range[])getValue(); |
98 | |
99 | StringBuffer sb = new StringBuffer(); |
100 | |
101 | for (int i = 0; i < ranges.length; i++) { |
102 | if(i>0) { |
103 | sb.append(", "); |
104 | } |
105 | sb.append(ranges[i]); |
106 | } |
107 | return sb.toString(); |
108 | } |
109 | |
110 | private void setMaxValues(final Range[] ranges) { |
111 | |
112 | // Array of integers to track range values by index |
113 | Integer[] c = new Integer[ranges.length]; |
114 | for (int i=0; i<c.length; i++) { |
115 | c[i] = i; |
116 | } |
117 | |
118 | //sort array of Ranges |
119 | Arrays.sort(c, new Comparator<Integer>() { |
120 | public int compare(Integer r1, Integer r2) { |
121 | return ranges[r1].getMin()-ranges[r2].getMin(); |
122 | } |
123 | } |
124 | ); |
125 | |
126 | //set max values for all unbound ranges (except last range) |
127 | for (int i = 0; i < c.length - 1; i++) { |
128 | if (!ranges[c[i]].hasMaxValue()) { |
129 | //set max value to (min value - 1) of the next range |
130 | ranges[c[i]] = new Range(ranges[c[i]].getMin(),ranges[c[i+1]].getMin() - 1); |
131 | } |
132 | } |
133 | |
134 | if (forceDisjointRanges) { |
135 | verifyRanges(ranges); |
136 | } |
137 | } |
138 | |
139 | |
140 | private void verifyRanges(Range[] ranges) { |
141 | //verify that ranges are disjoint |
142 | for(int i = 1; i < ranges.length;i++) { |
143 | Assert.isTrue(ranges[i-1].getMax() < ranges[i].getMin(), |
144 | "Ranges must be disjoint. Range[" + (i-1) + "]: (" + ranges[i-1] + |
145 | ") Range[" + i +"]: (" + ranges[i] + ")"); |
146 | } |
147 | } |
148 | } |