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 }