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  package org.springframework.batch.item.file;
17  
18  import java.io.BufferedReader;
19  import java.io.IOException;
20  import java.io.InputStreamReader;
21  import java.io.Reader;
22  import java.io.UnsupportedEncodingException;
23  
24  import org.springframework.core.io.Resource;
25  
26  /**
27   * A {@link BufferedReaderFactory} useful for reading simple binary (or text)
28   * files with no line endings, such as those produced by mainframe copy books.
29   * The reader splits a stream up across fixed line endings (rather than the
30   * usual convention based on plain text). The line endings are discarded, just
31   * as with the default plain text implementation.
32   * 
33   * @author Dave Syer
34   * 
35   * @since 2.1
36   */
37  public class SimpleBinaryBufferedReaderFactory implements BufferedReaderFactory {
38  
39  	/**
40  	 * The default line ending value.
41  	 */
42  	private static final String DEFAULT_LINE_ENDING = "\n";
43  
44  	private String lineEnding = DEFAULT_LINE_ENDING;
45  
46  	/**
47  	 * @param lineEnding
48  	 */
49  	public void setLineEnding(String lineEnding) {
50  		this.lineEnding = lineEnding;
51  	}
52  
53      @Override
54  	public BufferedReader create(Resource resource, String encoding) throws UnsupportedEncodingException, IOException {
55  		return new BinaryBufferedReader(new InputStreamReader(resource.getInputStream(), encoding), lineEnding);
56  	}
57  
58  	/**
59  	 * BufferedReader extension that splits lines based on a line ending, rather
60  	 * than the usual plain text conventions.
61  	 * 
62  	 * @author Dave Syer
63  	 * 
64  	 */
65  	private final class BinaryBufferedReader extends BufferedReader {
66  
67  		private final String ending;
68  
69  		/**
70  		 * @param in
71  		 */
72  		private BinaryBufferedReader(Reader in, String ending) {
73  			super(in);
74  			this.ending = ending;
75  		}
76  
77  		@Override
78  		public String readLine() throws IOException {
79  
80  			StringBuilder buffer = null;
81  
82  			synchronized (lock) {
83  
84  				int next = read();
85  				if (next == -1) {
86  					return null;
87  				}
88  
89  				buffer = new StringBuilder();
90  				StringBuilder candidateEnding = new StringBuilder();
91  
92  				while (!isEndOfLine(buffer, candidateEnding, next)) {
93  					next = read();
94  				}
95  				buffer.append(candidateEnding);
96  
97  			}
98  
99  			if (buffer != null && buffer.length() > 0) {
100 				return buffer.toString();
101 			}
102 			return null;
103 
104 		}
105 
106 		/**
107 		 * Check for end of line and accumulate a buffer for next time.
108 		 * 
109 		 * @param buffer the current line excluding the candidate ending
110 		 * @param candidate a buffer containing accumulated state
111 		 * @param next the next character (or -1 for end of file)
112 		 * @return true if the values together signify the end of a file
113 		 */
114 		private boolean isEndOfLine(StringBuilder buffer, StringBuilder candidate, int next) {
115 
116 			if (next == -1) {
117 				return true;
118 			}
119 
120 			char c = (char) next;
121 			if (ending.charAt(0) == c || candidate.length() > 0) {
122 				candidate.append(c);
123 			}
124 
125 			if (candidate.length() == 0) {
126 				buffer.append(c);
127 				return false;
128 			}
129 
130 			boolean end = ending.equals(candidate.toString());
131 			if (end) {
132 				candidate.delete(0, candidate.length());
133 			}
134 			else if (candidate.length() >= ending.length()) {
135 				buffer.append(candidate);
136 				candidate.delete(0, candidate.length());
137 			}
138 
139 			return end;
140 
141 		}
142 	}
143 
144 }