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.gl.batch.service.impl;
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.businessobject.OriginEntryFull;
028 import org.kuali.kfs.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 */
036 public 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 }