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.module.ld.util;
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.exception.LoadException;
028    import org.kuali.kfs.module.ld.businessobject.LaborOriginEntry;
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 LaborOriginEntryFileIterator implements Iterator<LaborOriginEntry> {
037        private static Logger LOG = Logger.getLogger(LaborOriginEntryFileIterator.class);
038    
039        protected LaborOriginEntry nextEntry;
040        protected BufferedReader reader;
041        protected int lineNumber;
042        protected boolean autoCloseReader;
043    
044        /**
045         * Constructs a LaborOriginEntryFileIterator
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 LaborOriginEntryFileIterator(BufferedReader reader) {
052            this(reader, true);
053        }
054    
055        /**
056         * Constructs a LaborOriginEntryFileIterator
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 LaborOriginEntryFileIterator(BufferedReader reader, boolean autoCloseReader) {
063    
064            if (reader == null) {
065                LOG.error("reader is null in the LaborOriginEntryFileIterator!");
066                throw new IllegalArgumentException("reader is null!");
067            }
068            this.reader = reader;
069            nextEntry = null;
070            lineNumber = 0;
071            this.autoCloseReader = autoCloseReader;
072        }
073    
074        /**
075         * Constructs a LaborOriginEntryFileIterator When constructed with this method, the file handle will be automatically closed
076         * when the end of origin entries has been reached (i.e. when hasNext() returns false)
077         * 
078         * @param file the file
079         */
080        public LaborOriginEntryFileIterator(File file) {
081            if (file == null) {
082                LOG.error("reader is null in the LaborOriginEntryFileIterator!");
083                throw new IllegalArgumentException("reader is null!");
084            }
085            try {
086                this.reader = new BufferedReader(new FileReader(file));
087                this.autoCloseReader = true;
088                nextEntry = null;
089                lineNumber = 0;
090            }
091            catch (FileNotFoundException e) {
092                LOG.error("File not found for LaborOriginEntryFileIterator! " + file.getAbsolutePath(), e);
093                throw new RuntimeException("File not found for LaborOriginEntryFileIterator! " + file.getAbsolutePath());
094            }
095        }
096    
097        /**
098         * @see java.util.Iterator#hasNext()
099         */
100        public boolean hasNext() {
101            if (nextEntry == null) {
102                fetchNextEntry();
103                return nextEntry != null;
104            }
105            else {
106                // we have the next entry loaded
107                return true;
108            }
109        }
110    
111        /**
112         * @see java.util.Iterator#next()
113         */
114        public LaborOriginEntry next() {
115            if (nextEntry != null) {
116                // an entry may have been fetched by hasNext()
117                LaborOriginEntry temp = nextEntry;
118                nextEntry = null;
119                return temp;
120            }
121            else {
122                // maybe next() is called repeatedly w/o calling hasNext. This is a bad idea, but the
123                // interface allows it
124                fetchNextEntry();
125                if (nextEntry == null) {
126                    throw new NoSuchElementException();
127                }
128    
129                // clear out the nextEntry to signal that no record has been loaded
130                LaborOriginEntry temp = nextEntry;
131                nextEntry = null;
132                return temp;
133            }
134        }
135    
136        /**
137         * @see java.util.Iterator#remove()
138         */
139        public void remove() {
140            throw new UnsupportedOperationException("Cannot remove entry from collection");
141        }
142    
143        protected void fetchNextEntry() {
144            try {
145                lineNumber++;
146                String line = reader.readLine();
147                if (line == null) {
148                    nextEntry = null;
149                    if (autoCloseReader) {
150                        reader.close();
151                    }
152                }
153                else {
154                    nextEntry = new LaborOriginEntry();
155                    try {
156                        nextEntry.setFromTextFileForBatch(line, lineNumber - 1);
157                    }
158                    catch (LoadException e) {
159                        // wipe out the next entry so that the next call to hasNext or next will force a new row to be fetched
160                        nextEntry = null;
161    
162                        // if there's an LoadException, then we'll just let it propagate up the call stack
163                        throw e;
164                    }
165                }
166            }
167            catch (IOException e) {
168                LOG.error("error in the CorrectionDocumentServiceImpl iterator", e);
169                nextEntry = null;
170                throw new RuntimeException("error retrieving origin entries");
171            }
172        }
173    
174        /**
175         * @see java.lang.Object#finalize()
176         */
177        @Override
178        protected void finalize() throws Throwable {
179            super.finalize();
180            if (autoCloseReader && reader != null) {
181                reader.close();
182            }
183        }
184    }