001: /*
002: * $Id: RecordIterator.java,v 1.2 2003/12/03 06:09:00 jonesde Exp $
003: *
004: * Copyright (c) 2001-2003 The Open For Business Project - www.ofbiz.org
005: *
006: * Permission is hereby granted, free of charge, to any person obtaining a
007: * copy of this software and associated documentation files (the "Software"),
008: * to deal in the Software without restriction, including without limitation
009: * the rights to use, copy, modify, merge, publish, distribute, sublicense,
010: * and/or sell copies of the Software, and to permit persons to whom the
011: * Software is furnished to do so, subject to the following conditions:
012: *
013: * The above copyright notice and this permission notice shall be included
014: * in all copies or substantial portions of the Software.
015: *
016: * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
017: * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
018: * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
019: * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
020: * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT
021: * OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR
022: * THE USE OR OTHER DEALINGS IN THE SOFTWARE.
023: *
024: */
025: package org.ofbiz.datafile;
026:
027: import java.io.BufferedReader;
028: import java.io.IOException;
029: import java.io.InputStream;
030: import java.io.InputStreamReader;
031: import java.net.URL;
032: import java.util.Stack;
033:
034: /**
035: * Record Iterator for reading large files
036: *
037: * @author <a href="mailto:jonesde@ofbiz.org">David E. Jones</a>
038: * @version $Revision: 1.2 $
039: * @since 3.0
040: */
041:
042: public class RecordIterator {
043:
044: public static final String module = RecordIterator.class.getName();
045:
046: protected BufferedReader br;
047: protected ModelDataFile modelDataFile;
048: protected InputStream dataFileStream;
049: protected boolean closed = false;
050: protected String locationInfo;
051:
052: protected int nextLineNum = 0;
053: protected String curLine = null;
054: protected Record curRecord = null;
055: protected String nextLine = null;
056: protected Record nextRecord = null;
057:
058: public RecordIterator(URL fileUrl, ModelDataFile modelDataFile)
059: throws DataFileException {
060: this .modelDataFile = modelDataFile;
061:
062: InputStream urlStream = null;
063: try {
064: urlStream = fileUrl.openStream();
065: } catch (IOException e) {
066: throw new DataFileException("Error open URL: "
067: + fileUrl.toString(), e);
068: }
069: this .setupStream(urlStream, fileUrl.toString());
070: }
071:
072: public RecordIterator(InputStream dataFileStream,
073: ModelDataFile modelDataFile, String locationInfo)
074: throws DataFileException {
075: this .modelDataFile = modelDataFile;
076: this .setupStream(dataFileStream, locationInfo);
077: }
078:
079: protected void setupStream(InputStream dataFileStream,
080: String locationInfo) throws DataFileException {
081: this .locationInfo = locationInfo;
082: this .dataFileStream = dataFileStream;
083: this .br = new BufferedReader(new InputStreamReader(
084: dataFileStream));
085:
086: // get the line seeded
087: this .getNextLine();
088: }
089:
090: protected boolean getNextLine() throws DataFileException {
091: this .nextLine = null;
092: this .nextRecord = null;
093:
094: boolean isFixedRecord = ModelDataFile.SEP_FIXED_RECORD
095: .equals(modelDataFile.separatorStyle);
096: // if (Debug.infoOn()) Debug.logInfo("[DataFile.readDataFile] separatorStyle is " + modelDataFile.separatorStyle + ", isFixedRecord: " + isFixedRecord, module);
097:
098: if (isFixedRecord) {
099: if (modelDataFile.recordLength <= 0) {
100: throw new DataFileException(
101: "Cannot read a fixed record length file if no record length is specified");
102: }
103:
104: try {
105: char[] charData = new char[modelDataFile.recordLength + 1];
106:
107: // if (Debug.infoOn()) Debug.logInfo("[DataFile.readDataFile] reading line " + lineNum + " from position " + (lineNum-1)*modelDataFile.recordLength + ", length is " + modelDataFile.recordLength, module);
108: if (br.read(charData, 0, modelDataFile.recordLength) == -1) {
109: nextLine = null;
110: // Debug.logInfo("[DataFile.readDataFile] found end of file, got -1", module);
111: } else {
112: nextLine = new String(charData);
113: // if (Debug.infoOn()) Debug.logInfo("[DataFile.readDataFile] read line " + lineNum + " line is: \"" + line + "\"", module);
114: }
115: } catch (IOException e) {
116: throw new DataFileException("Error reading line #"
117: + nextLineNum + " (index " + (nextLineNum - 1)
118: * modelDataFile.recordLength + " length "
119: + modelDataFile.recordLength
120: + ") from location: " + locationInfo, e);
121: }
122: } else {
123: try {
124: nextLine = br.readLine();
125: } catch (IOException e) {
126: throw new DataFileException("Error reading line #"
127: + nextLineNum + " from location: "
128: + locationInfo, e);
129: }
130: }
131:
132: if (nextLine != null) {
133: nextLineNum++;
134: ModelRecord modelRecord = findModelForLine(nextLine,
135: nextLineNum, modelDataFile);
136: this .nextRecord = Record.createRecord(nextLine,
137: nextLineNum, modelRecord);
138: return true;
139: } else {
140: this .close();
141: return false;
142: }
143: }
144:
145: public int getCurrentLineNumber() {
146: return this .nextLineNum - 1;
147: }
148:
149: public boolean hasNext() {
150: return nextLine != null;
151: }
152:
153: public Record next() throws DataFileException {
154: if (!hasNext()) {
155: return null;
156: }
157:
158: if (ModelDataFile.SEP_FIXED_RECORD
159: .equals(modelDataFile.separatorStyle)
160: || ModelDataFile.SEP_FIXED_LENGTH
161: .equals(modelDataFile.separatorStyle)) {
162: boolean isFixedRecord = ModelDataFile.SEP_FIXED_RECORD
163: .equals(modelDataFile.separatorStyle);
164: // if (Debug.infoOn()) Debug.logInfo("[DataFile.readDataFile] separatorStyle is " + modelDataFile.separatorStyle + ", isFixedRecord: " + isFixedRecord, module);
165:
166: // advance the line (we have already checked to make sure there is a next line
167: this .curLine = this .nextLine;
168: this .curRecord = this .nextRecord;
169:
170: // get a new next line
171: this .getNextLine();
172:
173: // first check to see if the file type has a line size, and if so if this line complies
174: if (!isFixedRecord && modelDataFile.recordLength > 0
175: && curLine.length() != modelDataFile.recordLength) {
176: throw new DataFileException("Line number "
177: + this .getCurrentLineNumber()
178: + " was not the expected length; expected: "
179: + modelDataFile.recordLength + ", got: "
180: + curLine.length());
181: }
182:
183: // if this record has children, put it on the parentStack and get/check the children now
184: if (this .curRecord.getModelRecord().childRecords.size() > 0) {
185: Stack parentStack = new Stack();
186: parentStack.push(curRecord);
187:
188: while (this .nextRecord != null
189: && this .nextRecord.getModelRecord().parentRecord != null) {
190: // if parent equals top parent on stack, add to that parents child list, otherwise pop off parent and try again
191: Record parentRecord = null;
192:
193: while (parentStack.size() > 0) {
194: parentRecord = (Record) parentStack.peek();
195: if (parentRecord.recordName
196: .equals(this .nextRecord
197: .getModelRecord().parentName)) {
198: break;
199: } else {
200: parentStack.pop();
201: parentRecord = null;
202: }
203: }
204:
205: if (parentRecord == null) {
206: throw new DataFileException(
207: "Expected Parent Record not found for line "
208: + this .getCurrentLineNumber()
209: + "; record name of expected parent is "
210: + this .nextRecord
211: .getModelRecord().parentName);
212: }
213:
214: parentRecord.addChildRecord(this .nextRecord);
215:
216: // if the child record we just added is also a parent, push it onto the stack
217: if (this .nextRecord.getModelRecord().childRecords
218: .size() > 0) {
219: parentStack.push(curRecord);
220: }
221:
222: // if it can't find a next line it will nextRecord will be null and the loop will break out
223: this .getNextLine();
224: }
225: }
226: } else if (ModelDataFile.SEP_DELIMITED
227: .equals(modelDataFile.separatorStyle)) {
228: throw new DataFileException(
229: "Delimited files not yet supported");
230: } else {
231: throw new DataFileException("Separator style "
232: + modelDataFile.separatorStyle + " not recognized.");
233: }
234:
235: return curRecord;
236: }
237:
238: public void close() throws DataFileException {
239: if (this .closed) {
240: return;
241: }
242: try {
243: this .br.close(); // this should also close the stream
244: this .closed = true;
245: } catch (IOException e) {
246: throw new DataFileException(
247: "Error closing data file input stream", e);
248: }
249: }
250:
251: /** Searches through the record models to find one with a matching type-code, if no type-code exists that model will always be used if it gets to it
252: * @param line
253: * @param lineNum
254: * @param modelDataFile
255: * @throws DataFileException Exception thown for various errors, generally has a nested exception
256: * @return
257: */
258: protected static ModelRecord findModelForLine(String line,
259: int lineNum, ModelDataFile modelDataFile)
260: throws DataFileException {
261: // if (Debug.infoOn()) Debug.logInfo("[DataFile.findModelForLine] line: " + line, module);
262: ModelRecord modelRecord = null;
263:
264: for (int i = 0; i < modelDataFile.records.size(); i++) {
265: ModelRecord curModelRecord = (ModelRecord) modelDataFile.records
266: .get(i);
267:
268: if (curModelRecord.tcPosition < 0) {
269: modelRecord = curModelRecord;
270: break;
271: }
272:
273: String typeCode = line
274: .substring(curModelRecord.tcPosition,
275: curModelRecord.tcPosition
276: + curModelRecord.tcLength);
277:
278: // try to match with a single typecode
279: if (curModelRecord.typeCode.length() > 0) {
280: // if (Debug.infoOn()) Debug.logInfo("[DataFile.findModelForLine] Doing plain typecode match - code=" + curModelRecord.typeCode + ", filelinecode=" + typeCode, module);
281: if (typeCode != null
282: && typeCode.equals(curModelRecord.typeCode)) {
283: modelRecord = curModelRecord;
284: break;
285: }
286: } // try to match a ranged typecode (tcMin <= typeCode <= tcMax)
287: else if (curModelRecord.tcMin.length() > 0
288: || curModelRecord.tcMax.length() > 0) {
289: if (curModelRecord.tcIsNum) {
290: // if (Debug.infoOn()) Debug.logInfo("[DataFile.findModelForLine] Doing ranged number typecode match - minNum=" + curModelRecord.tcMinNum + ", maxNum=" + curModelRecord.tcMaxNum + ", filelinecode=" + typeCode, module);
291: long typeCodeNum = Long.parseLong(typeCode);
292:
293: if ((curModelRecord.tcMinNum < 0 || typeCodeNum >= curModelRecord.tcMinNum)
294: && (curModelRecord.tcMaxNum < 0 || typeCodeNum <= curModelRecord.tcMaxNum)) {
295: modelRecord = curModelRecord;
296: break;
297: }
298: } else {
299: // if (Debug.infoOn()) Debug.logInfo("[DataFile.findModelForLine] Doing ranged String typecode match - min=" + curModelRecord.tcMin + ", max=" + curModelRecord.tcMax + ", filelinecode=" + typeCode, module);
300: if ((typeCode.compareTo(curModelRecord.tcMin) >= 0)
301: && (typeCode
302: .compareTo(curModelRecord.tcMax) <= 0)) {
303: modelRecord = curModelRecord;
304: break;
305: }
306: }
307: }
308: }
309:
310: if (modelRecord == null) {
311: throw new DataFileException(
312: "Could not find record definition for line "
313: + lineNum
314: + "; first bytes: "
315: + line.substring(0, (line.length() > 5) ? 5
316: : line.length()));
317: }
318: // if (Debug.infoOn()) Debug.logInfo("[DataFile.findModelForLine] Got record model named " + modelRecord.name, module);
319: return modelRecord;
320: }
321: }
|