001/* 002 * Copyright 2006-2007 the original author or authors. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * https://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016 017package org.springframework.batch.item.file.transform; 018 019import org.springframework.util.Assert; 020import org.springframework.util.StringUtils; 021 022import java.beans.PropertyEditorSupport; 023import java.util.Arrays; 024import java.util.Comparator; 025 026/** 027 * Property editor implementation which parses string and creates array of 028 * ranges. Ranges can be provided in any order. <br> Input string should be 029 * provided in following format: 'range1, range2, range3,...' where range is 030 * specified as: 031 * <ul> 032 * <li>'X-Y', where X is minimum value and Y is maximum value (condition X<=Y 033 * is verified)</li> 034 * <li>or 'Z', where Z is minimum and maximum is calculated as (minimum of 035 * adjacent range - 1). Maximum of the last range is never calculated. Range 036 * stays unbound at maximum side if maximum value is not provided.</li> 037 * </ul> 038 * Minimum and maximum values can be from interval <1, Integer.MAX_VALUE-1> 039 * <p> 040 * Examples:<br> 041 * '1, 15, 25, 38, 55-60' is equal to '1-14, 15-24, 25-37, 38-54, 55-60' <br> 042 * '36, 14, 1-10, 15, 49-57' is equal to '36-48, 14-14, 1-10, 15-35, 49-57' 043 * <p> 044 * Property editor also allows to validate whether ranges are disjoint. Validation 045 * can be turned on/off by using {@link #setForceDisjointRanges(boolean)}. By default 046 * validation is turned off. 047 * 048 * @author peter.zozom 049 */ 050public class RangeArrayPropertyEditor extends PropertyEditorSupport { 051 052 private boolean forceDisjointRanges = false; 053 054 /** 055 * Set force disjoint ranges. If set to TRUE, ranges are validated to be disjoint. 056 * For example: defining ranges '1-10, 5-15' will cause IllegalArgumentException in 057 * case of forceDisjointRanges=TRUE. 058 * @param forceDisjointRanges true to force disjoint ranges. 059 */ 060 public void setForceDisjointRanges(boolean forceDisjointRanges) { 061 this.forceDisjointRanges = forceDisjointRanges; 062 } 063 064 @Override 065 public void setAsText(String text) throws IllegalArgumentException { 066 067 //split text into ranges 068 String[] strRanges = text.split(","); 069 Range[] ranges = new Range[strRanges.length]; 070 071 //parse ranges and create array of Range objects 072 for (int i = 0; i < strRanges.length; i++) { 073 String[] range = strRanges[i].split("-"); 074 075 int min; 076 int max; 077 078 if ((range.length == 1) && (StringUtils.hasText(range[0]))) { 079 min = Integer.parseInt(range[0].trim()); 080 // correct max value will be assigned later 081 ranges[i] = new Range(min); 082 } else if ((range.length == 2) && (StringUtils.hasText(range[0])) 083 && (StringUtils.hasText(range[1]))) { 084 min = Integer.parseInt(range[0].trim()); 085 max = Integer.parseInt(range[1].trim()); 086 ranges[i] = new Range(min,max); 087 } else { 088 throw new IllegalArgumentException("Range[" + i + "]: range (" + strRanges[i] + ") is invalid"); 089 } 090 091 } 092 093 setMaxValues(ranges); 094 setValue(ranges); 095 } 096 097 @Override 098 public String getAsText() { 099 Range[] ranges = (Range[])getValue(); 100 101 StringBuilder sb = new StringBuilder(); 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}