001/* 002 * Copyright 2007 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 */ 016package org.kuali.ole.gl.batch.service.impl; 017 018import java.io.BufferedReader; 019import java.io.File; 020import java.io.FileNotFoundException; 021import java.io.FileReader; 022import java.io.IOException; 023import java.util.Iterator; 024import java.util.NoSuchElementException; 025 026import org.apache.log4j.Logger; 027import org.kuali.ole.gl.businessobject.OriginEntryFull; 028import org.kuali.ole.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 */ 036public 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}