001: /*
002: * Copyright 2007 The Kuali Foundation.
003: *
004: * Licensed under the Educational Community License, Version 1.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/ecl1.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.module.gl.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.module.gl.bo.OriginEntryFull;
028: import org.kuali.module.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
037: Iterator<OriginEntryFull> {
038: private static Logger LOG = Logger
039: .getLogger(OriginEntryFileIterator.class);
040:
041: protected OriginEntryFull nextEntry;
042: protected BufferedReader reader;
043: protected int lineNumber;
044: protected boolean autoCloseReader;
045:
046: /**
047: * Constructs a OriginEntryFileIterator
048: *
049: * @param reader a reader representing flat-file origin entries
050: * @param autoCloseReader whether to automatically close the reader when the end of origin entries has been reached (i.e. when
051: * hasNext() returns false)
052: */
053: public OriginEntryFileIterator(BufferedReader reader) {
054: this (reader, true);
055: }
056:
057: /**
058: * Constructs a OriginEntryFileIterator
059: *
060: * @param reader a reader representing flat-file origin entries
061: * @param autoCloseReader whether to automatically close the reader when the end of origin entries has been reached (i.e. when
062: * hasNext() returns false)
063: */
064: public OriginEntryFileIterator(BufferedReader reader,
065: boolean autoCloseReader) {
066: if (reader == null) {
067: LOG.error("reader is null in the OriginEntryFileIterator!");
068: throw new IllegalArgumentException("reader is null!");
069: }
070: this .reader = reader;
071: nextEntry = null;
072: lineNumber = 0;
073: this .autoCloseReader = autoCloseReader;
074: }
075:
076: /**
077: * Constructs a OriginEntryFileIterator When constructed with this method, the file handle will be automatically closed when the
078: * end of origin entries has been reached (i.e. when hasNext() returns false)
079: *
080: * @param file the file
081: */
082: public OriginEntryFileIterator(File file) {
083: if (file == null) {
084: LOG.error("reader is null in the OriginEntryFileIterator!");
085: throw new IllegalArgumentException("reader is null!");
086: }
087: try {
088: this .reader = new BufferedReader(new FileReader(file));
089: this .autoCloseReader = true;
090: nextEntry = null;
091: lineNumber = 0;
092: } catch (FileNotFoundException e) {
093: LOG.error("File not found for OriginEntryFileIterator! "
094: + file.getAbsolutePath(), e);
095: throw new RuntimeException(
096: "File not found for OriginEntryFileIterator! "
097: + file.getAbsolutePath());
098: }
099: }
100:
101: /**
102: * @see java.util.Iterator#hasNext()
103: */
104: public boolean hasNext() {
105: if (nextEntry == null) {
106: fetchNextEntry();
107: return nextEntry != null;
108: } else {
109: // we have the next entry loaded
110: return true;
111: }
112: }
113:
114: /**
115: * @see java.util.Iterator#next()
116: */
117: public OriginEntryFull next() {
118: if (nextEntry != null) {
119: // an entry may have been fetched by hasNext()
120: OriginEntryFull temp = nextEntry;
121: nextEntry = null;
122: return temp;
123: } else {
124: // maybe next() is called repeatedly w/o calling hasNext. This is a bad idea, but the
125: // interface allows it
126: fetchNextEntry();
127: if (nextEntry == null) {
128: throw new NoSuchElementException();
129: }
130:
131: // clear out the nextEntry to signal that no record has been loaded
132: OriginEntryFull temp = nextEntry;
133: nextEntry = null;
134: return temp;
135: }
136: }
137:
138: /**
139: * @see java.util.Iterator#remove()
140: */
141: public void remove() {
142: throw new UnsupportedOperationException(
143: "Cannot remove entry from collection");
144: }
145:
146: /**
147: * This method returns the next line in origin entry file
148: */
149: protected void fetchNextEntry() {
150: try {
151: lineNumber++;
152: String line = reader.readLine();
153: if (line == null) {
154: nextEntry = null;
155: if (autoCloseReader) {
156: reader.close();
157: }
158: } else {
159: nextEntry = new OriginEntryFull();
160: try {
161: nextEntry.setFromTextFile(line, lineNumber - 1);
162: } catch (LoadException e) {
163: // wipe out the next entry so that the next call to hasNext or next will force a new row to be fetched
164: nextEntry = null;
165:
166: // if there's an LoadException, then we'll just let it propagate up the call stack
167: throw e;
168: }
169: }
170: } catch (IOException e) {
171: LOG
172: .error(
173: "error in the CorrectionDocumentServiceImpl iterator",
174: e);
175: nextEntry = null;
176: throw new RuntimeException(
177: "error retrieving origin entries");
178: }
179: }
180:
181: /**
182: * @see java.lang.Object#finalize()
183: */
184: @Override
185: protected void finalize() throws Throwable {
186: super.finalize();
187: if (autoCloseReader && reader != null) {
188: reader.close();
189: }
190: }
191: }
|