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.util.ArrayList; 20 import java.util.Arrays; 21 import java.util.List; 22 23 /** 24 * Abstract class handling common concerns of various {@link LineTokenizer} 25 * implementations such as dealing with names and actual construction of 26 * {@link FieldSet} 27 * 28 * @author Dave Syer 29 * @author Robert Kasanicky 30 * @author Lucas Ward 31 */ 32 public abstract class AbstractLineTokenizer implements LineTokenizer { 33 34 protected String[] names = new String[0]; 35 36 private boolean strict = true; 37 38 private String emptyToken = ""; 39 40 private FieldSetFactory fieldSetFactory = new DefaultFieldSetFactory(); 41 42 /** 43 * Public setter for the strict flag. If true (the default) then number of 44 * tokens in line must match the number of tokens defined 45 * (by {@link Range}, columns, etc.) in {@link LineTokenizer}. 46 * If false then lines with less tokens will be tolerated and padded with 47 * empty columns, and lines with more tokens will 48 * simply be truncated. 49 * 50 * @param strict the strict flag to set 51 */ 52 public void setStrict(boolean strict) { 53 this.strict = strict; 54 } 55 56 /** 57 * Provides access to the strict flag for subclasses if needed. 58 * 59 * @return the strict flag value 60 */ 61 protected boolean isStrict() { 62 return strict; 63 } 64 65 /** 66 * Factory for {@link FieldSet} instances. Can be injected by clients to 67 * customize the default number and date formats. 68 * 69 * @param fieldSetFactory the {@link FieldSetFactory} to set 70 */ 71 public void setFieldSetFactory(FieldSetFactory fieldSetFactory) { 72 this.fieldSetFactory = fieldSetFactory; 73 } 74 75 /** 76 * Setter for column names. Optional, but if set, then all lines must have 77 * as many or fewer tokens. 78 * 79 * @param names 80 */ 81 public void setNames(String[] names) { 82 this.names = names==null ? null : Arrays.asList(names).toArray(new String[names.length]); 83 } 84 85 /** 86 * @return <code>true</code> if column names have been specified 87 * @see #setNames(String[]) 88 */ 89 public boolean hasNames() { 90 if (names != null && names.length > 0) { 91 return true; 92 } 93 return false; 94 } 95 96 /** 97 * Yields the tokens resulting from the splitting of the supplied 98 * <code>line</code>. 99 * 100 * @param line the line to be tokenised (can be <code>null</code>) 101 * 102 * @return the resulting tokens 103 */ 104 public FieldSet tokenize(String line) { 105 106 if (line == null) { 107 line = ""; 108 } 109 110 List<String> tokens = new ArrayList<String>(doTokenize(line)); 111 112 // if names are set and strict flag is false 113 if ( ( names.length != 0 ) && ( ! strict ) ) { 114 adjustTokenCountIfNecessary( tokens ); 115 } 116 117 String[] values = (String[]) tokens.toArray(new String[tokens.size()]); 118 119 if (names.length == 0) { 120 return fieldSetFactory.create(values); 121 } 122 else if (values.length != names.length) { 123 throw new IncorrectTokenCountException(names.length, values.length); 124 } 125 return fieldSetFactory.create(values, names); 126 } 127 128 protected abstract List<String> doTokenize(String line); 129 130 /** 131 * Adds empty tokens or truncates existing token list to match expected 132 * (configured) number of tokens in {@link LineTokenizer}. 133 * 134 * @param tokens - list of tokens 135 */ 136 private void adjustTokenCountIfNecessary( List<String> tokens ) { 137 138 int nameLength = names.length; 139 int tokensSize = tokens.size(); 140 141 // if the number of tokens is not what expected 142 if ( nameLength != tokensSize ) { 143 144 if ( nameLength > tokensSize ) { 145 146 // add empty tokens until the token list size matches 147 // the expected number of tokens 148 for ( int i = 0; i < ( nameLength - tokensSize ); i++ ) { 149 tokens.add( emptyToken ); 150 } 151 152 } else { 153 // truncate token list to match the number of expected tokens 154 for ( int i = tokensSize - 1; i >= nameLength; i-- ) { 155 tokens.remove(i); 156 } 157 } 158 159 } 160 } 161 }