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.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 }