001    /*
002     * Copyright 2011 The Kuali Foundation.
003     * 
004     * Licensed under the Educational Community 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     * http://www.opensource.org/licenses/ecl2.php
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    package org.kuali.kfs.gl.batch.service.impl;
017    
018    import java.io.BufferedReader;
019    import java.io.File;
020    import java.io.FileNotFoundException;
021    import java.io.FileReader;
022    import java.io.IOException;
023    import java.util.Iterator;
024    import java.util.NoSuchElementException;
025    
026    import org.apache.log4j.Logger;
027    import org.kuali.kfs.gl.businessobject.OriginEntryFull;
028    import org.kuali.kfs.gl.exception.LoadException;
029    
030    /**
031     * This class lazy loads the origin entries in a flat file. This implementation uses a limited amount of memory because it does not
032     * pre-load all of the origin entries at once. (Assuming that the Java garbage collector is working well). However, if the code that
033     * uses this iterator stores the contents of this iterator in a big list somewhere, then a lot of memory may be consumed, depending
034     * on the size of the file.
035     */
036    public class OriginEntryFileIterator implements Iterator<OriginEntryFull> {
037        private static Logger LOG = Logger.getLogger(OriginEntryFileIterator.class);
038    
039        protected OriginEntryFull nextEntry;
040        protected BufferedReader reader;
041        protected int lineNumber;
042        protected boolean autoCloseReader;
043    
044        /**
045         * Constructs a OriginEntryFileIterator
046         * 
047         * @param reader a reader representing flat-file origin entries
048         * @param autoCloseReader whether to automatically close the reader when the end of origin entries has been reached (i.e. when
049         *        hasNext() returns false)
050         */
051        public OriginEntryFileIterator(BufferedReader reader) {
052            this(reader, true);
053        }
054    
055        /**
056         * Constructs a OriginEntryFileIterator
057         * 
058         * @param reader a reader representing flat-file origin entries
059         * @param autoCloseReader whether to automatically close the reader when the end of origin entries has been reached (i.e. when
060         *        hasNext() returns false)
061         */
062        public OriginEntryFileIterator(BufferedReader reader, boolean autoCloseReader) {
063            if (reader == null) {
064                LOG.error("reader is null in the OriginEntryFileIterator!");
065                throw new IllegalArgumentException("reader is null!");
066            }
067            this.reader = reader;
068            nextEntry = null;
069            lineNumber = 0;
070            this.autoCloseReader = autoCloseReader;
071        }
072    
073        /**
074         * Constructs a OriginEntryFileIterator When constructed with this method, the file handle will be automatically closed when the
075         * end of origin entries has been reached (i.e. when hasNext() returns false)
076         * 
077         * @param file the file
078         */
079        public OriginEntryFileIterator(File file) {
080            if (file == null) {
081                LOG.error("reader is null in the OriginEntryFileIterator!");
082                throw new IllegalArgumentException("reader is null!");
083            }
084            try {
085                this.reader = new BufferedReader(new FileReader(file));
086                this.autoCloseReader = true;
087                nextEntry = null;
088                lineNumber = 0;
089            }
090            catch (FileNotFoundException e) {
091                LOG.error("File not found for OriginEntryFileIterator! " + file.getAbsolutePath(), e);
092                throw new RuntimeException("File not found for OriginEntryFileIterator! " + file.getAbsolutePath());
093            }
094        }
095    
096        /**
097         * @see java.util.Iterator#hasNext()
098         */
099        public boolean hasNext() {
100            if (nextEntry == null) {
101                fetchNextEntry();
102                return nextEntry != null;
103            }
104            else {
105                // we have the next entry loaded
106                return true;
107            }
108        }
109    
110        /**
111         * @see java.util.Iterator#next()
112         */
113        public OriginEntryFull next() {
114            if (nextEntry != null) {
115                // an entry may have been fetched by hasNext()
116                OriginEntryFull temp = nextEntry;
117                nextEntry = null;
118                return temp;
119            }
120            else {
121                // maybe next() is called repeatedly w/o calling hasNext. This is a bad idea, but the
122                // interface allows it
123                fetchNextEntry();
124                if (nextEntry == null) {
125                    throw new NoSuchElementException();
126                }
127    
128                // clear out the nextEntry to signal that no record has been loaded
129                OriginEntryFull temp = nextEntry;
130                nextEntry = null;
131                return temp;
132            }
133        }
134    
135        /**
136         * @see java.util.Iterator#remove()
137         */
138        public void remove() {
139            throw new UnsupportedOperationException("Cannot remove entry from collection");
140        }
141    
142        /**
143         * This method returns the next line in origin entry file
144         */
145        protected void fetchNextEntry() {
146            try {
147                lineNumber++;
148                String line = reader.readLine();
149                if (line == null) {
150                    nextEntry = null;
151                    if (autoCloseReader) {
152                        reader.close();
153                    }
154                }
155                else {
156                    nextEntry = new OriginEntryFull();
157                    try {
158                        nextEntry.setFromTextFileForBatch(line, lineNumber - 1);
159                    }
160                    catch (LoadException e) {
161                        // wipe out the next entry so that the next call to hasNext or next will force a new row to be fetched
162                        nextEntry = null;
163    
164                        // if there's an LoadException, then we'll just let it propagate up the call stack
165                        throw e;
166                    }
167                }
168            }
169            catch (IOException e) {
170                LOG.error("error in the CorrectionDocumentServiceImpl iterator", e);
171                nextEntry = null;
172                throw new RuntimeException("error retrieving origin entries");
173            }
174        }
175    
176        /**
177         * @see java.lang.Object#finalize()
178         */
179        @Override
180        protected void finalize() throws Throwable {
181            super.finalize();
182            if (autoCloseReader && reader != null) {
183                reader.close();
184            }
185        }
186    }