View Javadoc
1   /*
2    * Copyright 2008 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.openehealth.ipf.platform.camel.core.process.splitter.support;
17  
18  import org.apache.camel.util.ObjectHelper;
19  import org.openehealth.ipf.platform.camel.core.process.splitter.Splitter;
20  
21  import java.io.BufferedReader;
22  import java.io.FileReader;
23  import java.io.IOException;
24  import java.util.Iterator;
25  
26  /**
27   * String-based iterator class that reads a file line by line
28   * This class can be used within a split rule expression to extract each line
29   * of a given file and generate a new exchange via the {@link Splitter}.
30   * E.g. in Groovy, you can use this iterator like this:
31   * 
32   * <code>
33   *       ...
34   *            .split { exchange -> 
35   *                String filename = exchange.getIn().getBody();
36   *                return new TextFileIterator(filename);
37   *            }
38   *       ...
39   * </code>
40   * 
41   * The intention of this class is to read long text files without loading the
42   * whole file into memory. The largest portion of the file that is kept in 
43   * memory is an individual line as read by {@link BufferedReader#readLine()}.
44   * 
45   * @author Jens Riemschneider
46   */
47  public class TextFileIterator implements Iterator<String> {
48      private static final LineSplitterLogic DEFAULT_LINE_SPLITTER_LOGIC = 
49          new NoopLineSplitterLogic();
50      
51      private BufferedReader reader;
52      private String[] nextSplitLine;
53      private int curSplitLineIdx;
54      private final LineSplitterLogic lineSplitterLogic;
55      private boolean closed;
56      
57      /**
58       * Creates an iterator for a given file name
59       * @param filename
60       *          name of the file that should be read
61       * @throws IOException
62       *          If the file does not exist or could not be read
63       */
64      public TextFileIterator(String filename) throws IOException {
65          
66          ObjectHelper.notNull(filename, "filename");
67  
68          lineSplitterLogic = DEFAULT_LINE_SPLITTER_LOGIC;
69          
70          try {
71              reader = new BufferedReader(new FileReader(filename));
72              readNextLine();
73          }
74          catch (IOException e) {
75              close();
76              throw e;
77          }
78      }
79  
80      /**
81       * Creates an iterator for a given file name
82       * @param filename
83       *          name of the file that should be read
84       * @param lineSplitterLogic
85       *          logic that is used to split lines into individual iteration steps
86       * @throws IOException
87       *          If the file does not exist or could not be read
88       */
89      public TextFileIterator(String filename, LineSplitterLogic lineSplitterLogic) 
90          throws IOException {
91          
92          ObjectHelper.notNull(filename, "filename");
93          ObjectHelper.notNull(lineSplitterLogic, "lineSplitterLogic");
94          
95          this.lineSplitterLogic = lineSplitterLogic;
96          reader = new BufferedReader(new FileReader(filename));
97          
98          readNextLine();
99      }
100 
101     /* (non-Javadoc)
102      * @see java.util.Iterator#hasNext()
103      */
104     @Override
105     public boolean hasNext() {
106         return nextSplitLine != null;
107     }
108 
109     /* (non-Javadoc)
110      * @see java.util.Iterator#hasNext()
111      */
112     @Override
113     public String next() {
114         String curLine = nextSplitLine[curSplitLineIdx];
115         advance();
116         return curLine;
117     }
118 
119     /* (non-Javadoc)
120      * Not supported for this iterator
121      * @see java.util.Iterator#remove()
122      */
123     @Override
124     public void remove() {
125         close();
126         throw new UnsupportedOperationException();
127     }
128     
129     /** @return {@code true} if any underlying resources were closed
130      */
131     public boolean isClosed() {
132         return closed;
133     }
134     
135     /**
136      * Closes any open resources and stops an active iteration
137      */
138     public void close() {
139         closed = true;
140         if (reader != null) {
141             try {
142                 reader.close();            
143             } catch (IOException e) {
144                 // Ignored, because we simply try to close the stream to avoid
145                 // resources being locked.
146             }
147         }
148     }
149 
150     
151     private void advance() {
152         try {
153             if (hasNext()) {
154                 if (curSplitLineIdx < nextSplitLine.length - 1) {
155                     ++curSplitLineIdx;
156                 }
157                 else {
158                     readNextLine();
159                 }
160             }
161         } catch (IOException e) {
162             close();
163             throw new IllegalStateException(e);
164         }
165     }
166 
167     private void readNextLine() throws IOException {
168         String nextLine = reader.readLine();
169         if (nextLine != null) {
170             nextSplitLine = lineSplitterLogic.splitLine(nextLine);
171             curSplitLineIdx = 0;
172         }
173         else {
174             nextSplitLine = null;
175             close();
176         }
177     }
178 }
179