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 }