/* * The Kuali Financial System, a comprehensive financial management system for higher education. * * Copyright 2005-2014 The Kuali Foundation * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package org.kuali.kfs.module.ld.util; import java.io.BufferedReader; import java.io.File; import java.io.FileNotFoundException; import java.io.FileReader; import java.io.IOException; import java.util.Iterator; import java.util.NoSuchElementException; import org.apache.log4j.Logger; import org.kuali.kfs.gl.exception.LoadException; import org.kuali.kfs.module.ld.businessobject.LaborOriginEntry; /** * This class lazy loads the origin entries in a flat file. This implementation uses a limited amount of memory because it does not * pre-load all of the origin entries at once. (Assuming that the Java garbage collector is working well). However, if the code that * uses this iterator stores the contents of this iterator in a big list somewhere, then a lot of memory may be consumed, depending * on the size of the file. */ public class LaborOriginEntryFileIterator implements Iterator<LaborOriginEntry> { private static Logger LOG = Logger.getLogger(LaborOriginEntryFileIterator.class); protected LaborOriginEntry nextEntry; protected BufferedReader reader; protected int lineNumber; protected boolean autoCloseReader; /** * Constructs a LaborOriginEntryFileIterator * * @param reader a reader representing flat-file origin entries * @param autoCloseReader whether to automatically close the reader when the end of origin entries has been reached (i.e. when * hasNext() returns false) */ public LaborOriginEntryFileIterator(BufferedReader reader) { this(reader, true); } /** * Constructs a LaborOriginEntryFileIterator * * @param reader a reader representing flat-file origin entries * @param autoCloseReader whether to automatically close the reader when the end of origin entries has been reached (i.e. when * hasNext() returns false) */ public LaborOriginEntryFileIterator(BufferedReader reader, boolean autoCloseReader) { if (reader == null) { LOG.error("reader is null in the LaborOriginEntryFileIterator!"); throw new IllegalArgumentException("reader is null!"); } this.reader = reader; nextEntry = null; lineNumber = 0; this.autoCloseReader = autoCloseReader; } /** * Constructs a LaborOriginEntryFileIterator When constructed with this method, the file handle will be automatically closed * when the end of origin entries has been reached (i.e. when hasNext() returns false) * * @param file the file */ public LaborOriginEntryFileIterator(File file) { if (file == null) { LOG.error("reader is null in the LaborOriginEntryFileIterator!"); throw new IllegalArgumentException("reader is null!"); } try { this.reader = new BufferedReader(new FileReader(file)); this.autoCloseReader = true; nextEntry = null; lineNumber = 0; } catch (FileNotFoundException e) { LOG.error("File not found for LaborOriginEntryFileIterator! " + file.getAbsolutePath(), e); throw new RuntimeException("File not found for LaborOriginEntryFileIterator! " + file.getAbsolutePath()); } } /** * @see java.util.Iterator#hasNext() */ public boolean hasNext() { if (nextEntry == null) { fetchNextEntry(); return nextEntry != null; } else { // we have the next entry loaded return true; } } /** * @see java.util.Iterator#next() */ public LaborOriginEntry next() { if (nextEntry != null) { // an entry may have been fetched by hasNext() LaborOriginEntry temp = nextEntry; nextEntry = null; return temp; } else { // maybe next() is called repeatedly w/o calling hasNext. This is a bad idea, but the // interface allows it fetchNextEntry(); if (nextEntry == null) { throw new NoSuchElementException(); } // clear out the nextEntry to signal that no record has been loaded LaborOriginEntry temp = nextEntry; nextEntry = null; return temp; } } /** * @see java.util.Iterator#remove() */ public void remove() { throw new UnsupportedOperationException("Cannot remove entry from collection"); } protected void fetchNextEntry() { try { lineNumber++; String line = reader.readLine(); if (line == null) { nextEntry = null; if (autoCloseReader) { reader.close(); } } else { nextEntry = new LaborOriginEntry(); try { nextEntry.setFromTextFileForBatch(line, lineNumber - 1); } catch (LoadException e) { // wipe out the next entry so that the next call to hasNext or next will force a new row to be fetched nextEntry = null; // if there's an LoadException, then we'll just let it propagate up the call stack throw e; } } } catch (IOException e) { LOG.error("error in the CorrectionDocumentServiceImpl iterator", e); nextEntry = null; throw new RuntimeException("error retrieving origin entries"); } } /** * @see java.lang.Object#finalize() */ @Override protected void finalize() throws Throwable { super.finalize(); if (autoCloseReader && reader != null) { reader.close(); } } }