View Javadoc

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 &lt;1, Integer.MAX_VALUE-1&gt;
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      @Override
65  	public void setAsText(String text) throws IllegalArgumentException {
66  		
67  		//split text into ranges
68  		String[] strRanges = text.split(",");
69  		Range[] ranges = new Range[strRanges.length];
70  		
71  		//parse ranges and create array of Range objects 
72  		for (int i = 0; i < strRanges.length; i++) {			
73  			String[] range = strRanges[i].split("-");
74  		
75  			int min;
76  			int max;
77  			
78  			if ((range.length == 1) && (StringUtils.hasText(range[0]))) {
79  				min = Integer.parseInt(range[0].trim());
80  				// correct max value will be assigned later
81  				ranges[i] = new Range(min);
82  			} else if ((range.length == 2) && (StringUtils.hasText(range[0]))
83  					&& (StringUtils.hasText(range[1]))) {
84  				min = Integer.parseInt(range[0].trim());
85  				max = Integer.parseInt(range[1].trim());
86  				ranges[i] = new Range(min,max);
87  			} else {
88  				throw new IllegalArgumentException("Range[" + i + "]: range (" + strRanges[i] + ") is invalid");
89  			}			
90  			
91  		}
92  	
93  		setMaxValues(ranges);
94  		setValue(ranges);
95  	}
96  	
97      @Override
98  	public String getAsText() {
99  		Range[] ranges = (Range[])getValue();
100 		
101 		StringBuffer sb = new StringBuffer();
102 
103 		for (int i = 0; i < ranges.length; i++) {
104 			if(i>0) {
105 				sb.append(", ");
106 			}
107 			sb.append(ranges[i]);
108 		}
109 		return sb.toString();
110 	}
111 	
112 	private void setMaxValues(final Range[] ranges) {
113 		
114 		// Array of integers to track range values by index
115 		Integer[] c = new Integer[ranges.length];
116 		for (int i=0; i<c.length; i++) {
117 			c[i] = i;
118 		}
119 		
120 		//sort array of Ranges
121 		Arrays.sort(c, new Comparator<Integer>() {
122                 @Override
123 				public int compare(Integer r1, Integer r2) {
124 					return ranges[r1].getMin()-ranges[r2].getMin();
125 				}								
126 			}
127 		);
128 		
129 		//set max values for all unbound ranges (except last range)
130 		for (int i = 0; i < c.length - 1; i++) {
131 			if (!ranges[c[i]].hasMaxValue()) {
132 				//set max value to (min value - 1) of the next range
133 				ranges[c[i]] = new Range(ranges[c[i]].getMin(),ranges[c[i+1]].getMin() - 1);
134 			}
135 		}
136 		
137 		if (forceDisjointRanges) {
138 			verifyRanges(ranges);
139 		}
140 	}
141 	
142 	
143 	private void verifyRanges(Range[] ranges) {
144 		//verify that ranges are disjoint		
145 		for(int i = 1; i < ranges.length;i++) {
146 			Assert.isTrue(ranges[i-1].getMax() < ranges[i].getMin(),
147 					"Ranges must be disjoint. Range[" + (i-1) + "]: (" + ranges[i-1] + 
148 					") Range[" + i +"]: (" + ranges[i] + ")");
149 		}
150 	}
151 }