001/* 002 * Copyright 2006-2014 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 java.util.ArrayList; 020import java.util.Arrays; 021import java.util.List; 022 023import org.springframework.util.StringUtils; 024 025/** 026 * Abstract class handling common concerns of various {@link LineTokenizer} 027 * implementations such as dealing with names and actual construction of 028 * {@link FieldSet} 029 * 030 * @author Dave Syer 031 * @author Robert Kasanicky 032 * @author Lucas Ward 033 * @author Michael Minella 034 */ 035public abstract class AbstractLineTokenizer implements LineTokenizer { 036 037 protected String[] names = new String[0]; 038 039 private boolean strict = true; 040 041 private String emptyToken = ""; 042 043 private FieldSetFactory fieldSetFactory = new DefaultFieldSetFactory(); 044 045 /** 046 * Public setter for the strict flag. If true (the default) then number of 047 * tokens in line must match the number of tokens defined 048 * (by {@link Range}, columns, etc.) in {@link LineTokenizer}. 049 * If false then lines with less tokens will be tolerated and padded with 050 * empty columns, and lines with more tokens will 051 * simply be truncated. 052 * 053 * @param strict the strict flag to set 054 */ 055 public void setStrict(boolean strict) { 056 this.strict = strict; 057 } 058 059 /** 060 * Provides access to the strict flag for subclasses if needed. 061 * 062 * @return the strict flag value 063 */ 064 protected boolean isStrict() { 065 return strict; 066 } 067 068 /** 069 * Factory for {@link FieldSet} instances. Can be injected by clients to 070 * customize the default number and date formats. 071 * 072 * @param fieldSetFactory the {@link FieldSetFactory} to set 073 */ 074 public void setFieldSetFactory(FieldSetFactory fieldSetFactory) { 075 this.fieldSetFactory = fieldSetFactory; 076 } 077 078 /** 079 * Setter for column names. Optional, but if set, then all lines must have 080 * as many or fewer tokens. 081 * 082 * @param names names of each column 083 */ 084 public void setNames(String... names) { 085 if(names == null) { 086 this.names = null; 087 } 088 else { 089 boolean valid = false; 090 for (String name : names) { 091 if(StringUtils.hasText(name)) { 092 valid = true; 093 break; 094 } 095 } 096 097 if(valid) { 098 this.names = Arrays.asList(names).toArray(new String[names.length]); 099 } 100 } 101 } 102 103 /** 104 * @return <code>true</code> if column names have been specified 105 * @see #setNames(String[]) 106 */ 107 public boolean hasNames() { 108 if (names != null && names.length > 0) { 109 return true; 110 } 111 return false; 112 } 113 114 /** 115 * Yields the tokens resulting from the splitting of the supplied 116 * <code>line</code>. 117 * 118 * @param line the line to be tokenized (can be <code>null</code>) 119 * 120 * @return the resulting tokens 121 */ 122 @Override 123 public FieldSet tokenize(String line) { 124 125 if (line == null) { 126 line = ""; 127 } 128 129 List<String> tokens = new ArrayList<String>(doTokenize(line)); 130 131 // if names are set and strict flag is false 132 if ( ( names.length != 0 ) && ( ! strict ) ) { 133 adjustTokenCountIfNecessary( tokens ); 134 } 135 136 String[] values = tokens.toArray(new String[tokens.size()]); 137 138 if (names.length == 0) { 139 return fieldSetFactory.create(values); 140 } 141 else if (values.length != names.length) { 142 throw new IncorrectTokenCountException(names.length, values.length, line); 143 } 144 return fieldSetFactory.create(values, names); 145 } 146 147 protected abstract List<String> doTokenize(String line); 148 149 /** 150 * Adds empty tokens or truncates existing token list to match expected 151 * (configured) number of tokens in {@link LineTokenizer}. 152 * 153 * @param tokens - list of tokens 154 */ 155 private void adjustTokenCountIfNecessary( List<String> tokens ) { 156 157 int nameLength = names.length; 158 int tokensSize = tokens.size(); 159 160 // if the number of tokens is not what expected 161 if ( nameLength != tokensSize ) { 162 163 if ( nameLength > tokensSize ) { 164 165 // add empty tokens until the token list size matches 166 // the expected number of tokens 167 for ( int i = 0; i < ( nameLength - tokensSize ); i++ ) { 168 tokens.add( emptyToken ); 169 } 170 171 } else { 172 // truncate token list to match the number of expected tokens 173 for ( int i = tokensSize - 1; i >= nameLength; i-- ) { 174 tokens.remove(i); 175 } 176 } 177 178 } 179 } 180}