001: /*
002: * $Id: Record.java,v 1.1 2003/08/19 00:27:10 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.Serializable;
028: import java.text.NumberFormat;
029: import java.text.ParseException;
030: import java.text.SimpleDateFormat;
031: import java.util.ArrayList;
032: import java.util.Date;
033: import java.util.HashMap;
034: import java.util.List;
035: import java.util.Map;
036:
037: /**
038: * Record
039: *
040: * @author <a href="mailto:jonesde@ofbiz.org">David E. Jones</a>
041: * @version $Revision: 1.1 $
042: * @since 2.0
043: */
044: public class Record implements Serializable {
045:
046: /** Contains a map with field data by name */
047: protected Map fields;
048:
049: /** Contains the name of the record definition */
050: protected String recordName;
051:
052: /** Contains the definition for the record */
053: protected transient ModelRecord modelRecord;
054:
055: protected Record parentRecord = null;
056: protected List childRecords = new ArrayList();
057:
058: /** Creates new Record */
059: protected Record(ModelRecord modelRecord) {
060: if (modelRecord == null)
061: throw new IllegalArgumentException(
062: "Cannont create a Record with a null modelRecord parameter");
063: this .recordName = modelRecord.name;
064: this .modelRecord = modelRecord;
065: this .fields = new HashMap();
066: }
067:
068: /** Creates new Record from existing Map */
069: protected Record(ModelRecord modelRecord, Map fields) {
070: if (modelRecord == null)
071: throw new IllegalArgumentException(
072: "Cannont create a Record with a null modelRecord parameter");
073: this .recordName = modelRecord.name;
074: this .modelRecord = modelRecord;
075: this .fields = (fields == null ? new HashMap() : new HashMap(
076: fields));
077: }
078:
079: public String getRecordName() {
080: return recordName;
081: }
082:
083: public ModelRecord getModelRecord() {
084: if (modelRecord == null) {
085: throw new IllegalStateException(
086: "[Record.getModelRecord] could not find modelRecord for recordName "
087: + recordName);
088: }
089: return modelRecord;
090: }
091:
092: public Object get(String name) {
093: if (getModelRecord().getModelField(name) == null) {
094: throw new IllegalArgumentException("[Record.get] \"" + name
095: + "\" is not a field of " + recordName);
096: // Debug.logWarning("[GenericRecord.get] \"" + name + "\" is not a field of " + recordName + ", but getting anyway...", module);
097: }
098: return fields.get(name);
099: }
100:
101: public String getString(String name) {
102: Object object = get(name);
103:
104: if (object == null)
105: return null;
106: if (object instanceof java.lang.String)
107: return (String) object;
108: else
109: return object.toString();
110: }
111:
112: public java.sql.Timestamp getTimestamp(String name) {
113: return (java.sql.Timestamp) get(name);
114: }
115:
116: public java.sql.Time getTime(String name) {
117: return (java.sql.Time) get(name);
118: }
119:
120: public java.sql.Date getDate(String name) {
121: return (java.sql.Date) get(name);
122: }
123:
124: public Integer getInteger(String name) {
125: return (Integer) get(name);
126: }
127:
128: public Long getLong(String name) {
129: return (Long) get(name);
130: }
131:
132: public Float getFloat(String name) {
133: return (Float) get(name);
134: }
135:
136: public Double getDouble(String name) {
137: return (Double) get(name);
138: }
139:
140: /** Sets the named field to the passed value, even if the value is null
141: * @param name The field name to set
142: * @param value The value to set
143: */
144: public void set(String name, Object value) {
145: set(name, value, true);
146: }
147:
148: /** Sets the named field to the passed value. If value is null, it is only
149: * set if the setIfNull parameter is true.
150: * @param name The field name to set
151: * @param value The value to set
152: * @param setIfNull Specifies whether or not to set the value if it is null
153: */
154: public synchronized void set(String name, Object value,
155: boolean setIfNull) {
156: if (getModelRecord().getModelField(name) == null) {
157: throw new IllegalArgumentException("[Record.set] \"" + name
158: + "\" is not a field of " + recordName);
159: // Debug.logWarning("[GenericRecord.set] \"" + name + "\" is not a field of " + recordName + ", but setting anyway...", module);
160: }
161: if (value != null || setIfNull) {
162: if (value instanceof Boolean) {
163: value = ((Boolean) value).booleanValue() ? "Y" : "N";
164: }
165: fields.put(name, value);
166: }
167: }
168:
169: /** Sets the named field to the passed value, converting the value from a String to the corrent type using <code>Type.valueOf()</code>
170: * @param name The field name to set
171: * @param value The String value to convert and set
172: */
173: public void setString(String name, String value)
174: throws ParseException {
175: if (name == null || value == null || value.equals(""))
176: return;
177: ModelField field = getModelRecord().getModelField(name);
178:
179: if (field == null)
180: set(name, value); // this will get an error in the set() method...
181:
182: // if the string is all spaces ignore
183: boolean nonSpace = false;
184:
185: for (int i = 0; i < value.length(); i++) {
186: if (value.charAt(i) != ' ') {
187: nonSpace = true;
188: break;
189: }
190: }
191: if (!nonSpace)
192: return;
193:
194: // if (Debug.verboseOn()) Debug.logVerbose("Value: " + value, module);
195:
196: String fieldType = field.type;
197:
198: // first the custom types that need to be parsed
199: if (fieldType.equals("CustomTimestamp")) {
200: // this custom type will take a string a parse according to date formatting
201: // string then put the result in a java.sql.Timestamp
202: // a common timestamp format for flat files is with no separators: yyyyMMddHHmmss
203: SimpleDateFormat sdf = new SimpleDateFormat(field.format);
204: java.util.Date tempDate = sdf.parse(value);
205: java.sql.Timestamp timestamp = new java.sql.Timestamp(
206: tempDate.getTime());
207:
208: set(name, timestamp);
209: } else if (fieldType.equals("CustomDate")) {
210: // a common date only format for flat files is with no separators: yyyyMMdd or MMddyyyy
211: SimpleDateFormat sdf = new SimpleDateFormat(field.format);
212: java.util.Date tempDate = sdf.parse(value);
213: java.sql.Date date = new java.sql.Date(tempDate.getTime());
214:
215: set(name, date);
216: } else if (fieldType.equals("CustomTime")) {
217: // a common time only format for flat files is with no separators: HHmmss
218: SimpleDateFormat sdf = new SimpleDateFormat(field.format);
219: java.util.Date tempDate = sdf.parse(value);
220: java.sql.Time time = new java.sql.Time(tempDate.getTime());
221:
222: set(name, time);
223: } else if (fieldType.equals("FixedPointDouble")) {
224: // this custom type will parse a fixed point number according to the number
225: // of decimal places in the formatting string then place it in a Double
226: NumberFormat nf = NumberFormat.getNumberInstance();
227: Number tempNum = nf.parse(value);
228: double number = tempNum.doubleValue();
229: double decimalPlaces = Double.parseDouble(field.format);
230: double divisor = Math.pow(10.0, decimalPlaces);
231:
232: number = number / divisor;
233: set(name, new Double(number));
234: } // standard types
235: else if (fieldType.equals("java.lang.String")
236: || fieldType.equals("String"))
237: set(name, value);
238: else if (fieldType.equals("java.sql.Timestamp")
239: || fieldType.equals("Timestamp"))
240: set(name, java.sql.Timestamp.valueOf(value));
241: else if (fieldType.equals("java.sql.Time")
242: || fieldType.equals("Time"))
243: set(name, java.sql.Time.valueOf(value));
244: else if (fieldType.equals("java.sql.Date")
245: || fieldType.equals("Date"))
246: set(name, java.sql.Date.valueOf(value));
247: else if (fieldType.equals("java.lang.Integer")
248: || fieldType.equals("Integer"))
249: set(name, Integer.valueOf(value));
250: else if (fieldType.equals("java.lang.Long")
251: || fieldType.equals("Long"))
252: set(name, Long.valueOf(value));
253: else if (fieldType.equals("java.lang.Float")
254: || fieldType.equals("Float"))
255: set(name, Float.valueOf(value));
256: else if (fieldType.equals("java.lang.Double")
257: || fieldType.equals("Double"))
258: set(name, Double.valueOf(value));
259: else {
260: throw new IllegalArgumentException("Field type "
261: + fieldType + " not currently supported. Sorry.");
262: }
263: }
264:
265: public String getFixedString(String name) {
266: if (name == null)
267: return null;
268: if (getModelRecord() == null)
269: throw new IllegalArgumentException(
270: "Could not find modelrecord for field named \""
271: + name + "\"");
272: ModelField field = getModelRecord().getModelField(name);
273:
274: if (field == null)
275: throw new IllegalArgumentException(
276: "Could not find model for field named \"" + name
277: + "\"");
278:
279: Object value = get(name);
280:
281: if (value == null) {
282: return null;
283: }
284:
285: String fieldType = field.type;
286: String str = null;
287:
288: // first the custom types that need to be parsed
289: if (fieldType.equals("CustomTimestamp")) {
290: // a common timestamp format for flat files is with no separators: yyyyMMddHHmmss
291: SimpleDateFormat sdf = new SimpleDateFormat(field.format);
292: java.sql.Timestamp timestamp = (java.sql.Timestamp) value;
293:
294: str = sdf.format(new Date(timestamp.getTime()));
295: } else if (fieldType.equals("CustomDate")) {
296: // a common date only format for flat files is with no separators: yyyyMMdd or MMddyyyy
297: SimpleDateFormat sdf = new SimpleDateFormat(field.format);
298: java.sql.Date date = (java.sql.Date) value;
299:
300: str = sdf.format(new Date(date.getTime()));
301: } else if (fieldType.equals("CustomTime")) {
302: // a common time only format for flat files is with no separators: HHmmss
303: SimpleDateFormat sdf = new SimpleDateFormat(field.format);
304: java.sql.Time time = (java.sql.Time) value;
305:
306: str = sdf.format(new Date(time.getTime()));
307: } else if (fieldType.equals("FixedPointDouble")) {
308: // this custom type will parse a fixed point number according to the number
309: // of decimal places in the formatting string then place it in a Double
310: NumberFormat nf = NumberFormat.getNumberInstance();
311: double decimalPlaces = Double.parseDouble(field.format);
312: double multiplier = Math.pow(10.0, decimalPlaces);
313: double dnum = multiplier * ((Double) value).doubleValue();
314: long number = Math.round(dnum);
315:
316: str = padFrontZeros(Long.toString(number), field.length);
317: // if (Debug.infoOn()) Debug.logInfo("[Record.getFixedString] FixedPointDouble: multiplier=" + multiplier + ", value=" + value + ", dnum=" + dnum + ", number=" + number + ", str=" + str, module);
318: } // standard types
319: else if (fieldType.equals("java.lang.String")
320: || fieldType.equals("String"))
321: str = value.toString();
322: else if (fieldType.equals("java.sql.Timestamp")
323: || fieldType.equals("Timestamp"))
324: str = value.toString();
325: else if (fieldType.equals("java.sql.Time")
326: || fieldType.equals("Time"))
327: str = value.toString();
328: else if (fieldType.equals("java.sql.Date")
329: || fieldType.equals("Date"))
330: str = value.toString();
331: // for all numbers, pad front with zeros if field length is specified
332: else if (fieldType.equals("java.lang.Integer")
333: || fieldType.equals("Integer"))
334: str = padFrontZeros(value.toString(), field.length);
335: else if (fieldType.equals("java.lang.Long")
336: || fieldType.equals("Long"))
337: str = padFrontZeros(value.toString(), field.length);
338: else if (fieldType.equals("java.lang.Float")
339: || fieldType.equals("Float"))
340: str = padFrontZeros(value.toString(), field.length);
341: else if (fieldType.equals("java.lang.Double")
342: || fieldType.equals("Double"))
343: str = padFrontZeros(value.toString(), field.length);
344: else {
345: throw new IllegalArgumentException("Field type "
346: + fieldType + " not currently supported. Sorry.");
347: }
348:
349: if (str != null && field.length > 0
350: && str.length() < field.length) {
351: // pad the end with spaces
352: StringBuffer strBuf = new StringBuffer(str);
353:
354: while (strBuf.length() < field.length)
355: strBuf.append(' ');
356: str = strBuf.toString();
357: }
358: return str;
359: }
360:
361: public String writeLineString(ModelDataFile modelDataFile)
362: throws DataFileException {
363: ModelRecord modelRecord = getModelRecord();
364: boolean isFixedRecord = ModelDataFile.SEP_FIXED_RECORD
365: .equals(modelDataFile.separatorStyle);
366: boolean isFixedLength = ModelDataFile.SEP_FIXED_LENGTH
367: .equals(modelDataFile.separatorStyle);
368: boolean isDelimited = ModelDataFile.SEP_DELIMITED
369: .equals(modelDataFile.separatorStyle);
370:
371: StringBuffer lineBuf = new StringBuffer();
372:
373: for (int f = 0; f < modelRecord.fields.size(); f++) {
374: ModelField modelField = (ModelField) modelRecord.fields
375: .get(f);
376: String data = this .getFixedString(modelField.name);
377:
378: // if field is null (not set) then assume we want to pad the field
379: char PAD_CHAR = ' ';
380:
381: if (data == null) {
382: StringBuffer sb = new StringBuffer("");
383:
384: for (int i = 0; i < modelField.length; i++)
385: sb.append(PAD_CHAR);
386: data = new String(sb);
387: }
388:
389: // Pad the record
390: if (isFixedRecord) {
391: while (modelField.position > lineBuf.length())
392: lineBuf.append(" ");
393: }
394: // if (Debug.infoOn()) Debug.logInfo("Field: " + modelField.name + " Position: " + modelField.position + " BufLen: " + lineBuf.length(), module);
395:
396: // if (Debug.infoOn()) Debug.logInfo("Got data \"" + data + "\" for field " + modelField.name + " in record " + modelRecord.name, module);
397: if (modelField.length > 0
398: && data.length() != modelField.length)
399: throw new DataFileException("Got field length "
400: + data.length()
401: + " but expected field length is "
402: + modelField.length + " for field \""
403: + modelField.name + "\" of record \""
404: + modelRecord.name + "\" data is: \"" + data
405: + "\"");
406:
407: lineBuf.append(data);
408: if (isDelimited)
409: lineBuf.append(modelDataFile.delimiter);
410: }
411: if ((isFixedRecord || isFixedLength)
412: && modelDataFile.recordLength > 0
413: && lineBuf.length() != modelDataFile.recordLength)
414: throw new DataFileException("Got record length "
415: + lineBuf.length()
416: + " but expected record length is "
417: + modelDataFile.recordLength + " for record \""
418: + modelRecord.name + "\" data line is: \""
419: + lineBuf + "\"");
420:
421: // for convenience, insert the type-code in where it is looked for, if exists
422: if (modelRecord.tcPosition > 0
423: && modelRecord.typeCode.length() > 0) {
424: lineBuf.replace(modelRecord.tcPosition,
425: modelRecord.tcPosition + modelRecord.tcLength,
426: modelRecord.typeCode);
427: }
428:
429: if (isFixedLength || isDelimited)
430: lineBuf.append('\n');
431:
432: return lineBuf.toString();
433: }
434:
435: String padFrontZeros(String str, int totalLength) {
436: if (totalLength > 0 && str.length() < totalLength) {
437: // pad the front with zeros
438: StringBuffer zeros = new StringBuffer();
439: int numZeros = totalLength - str.length();
440:
441: for (int i = 0; i < numZeros; i++)
442: zeros.append('0');
443: zeros.append(str);
444: return zeros.toString();
445: } else
446: return str;
447: }
448:
449: public Record getParentRecord() {
450: return parentRecord;
451: }
452:
453: public List getChildRecords() {
454: return childRecords;
455: }
456:
457: public void addChildRecord(Record record) {
458: childRecords.add(record);
459: }
460:
461: /** Creates new Record
462: * @param modelRecord
463: * @throws DataFileException Exception thown for various errors, generally has a nested exception
464: * @return
465: */
466: public static Record createRecord(ModelRecord modelRecord)
467: throws DataFileException {
468: Record record = new Record(modelRecord);
469:
470: return record;
471: }
472:
473: /** Creates new Record from existing fields Map
474: * @param modelRecord
475: * @param fields
476: * @throws DataFileException Exception thown for various errors, generally has a nested exception
477: * @return
478: */
479: public static Record createRecord(ModelRecord modelRecord,
480: Map fields) throws DataFileException {
481: Record record = new Record(modelRecord, fields);
482:
483: return record;
484: }
485:
486: /**
487: * @param line
488: * @param lineNum
489: * @param modelRecord
490: * @throws DataFileException Exception thown for various errors, generally has a nested exception
491: * @return
492: */
493: public static Record createRecord(String line, int lineNum,
494: ModelRecord modelRecord) throws DataFileException {
495: Record record = new Record(modelRecord);
496:
497: for (int i = 0; i < modelRecord.fields.size(); i++) {
498: ModelField modelField = (ModelField) modelRecord.fields
499: .get(i);
500: String strVal = null;
501:
502: try {
503: strVal = line.substring(modelField.position,
504: modelField.position + modelField.length);
505: } catch (IndexOutOfBoundsException ioobe) {
506: throw new DataFileException("Field " + modelField.name
507: + " from " + modelField.position + " for "
508: + modelField.length
509: + " chars could not be read from a line ("
510: + lineNum + ") with only " + line.length()
511: + " chars.", ioobe);
512: }
513: try {
514: record.setString(modelField.name, strVal);
515: } catch (java.text.ParseException e) {
516: throw new DataFileException("Could not parse field "
517: + modelField.name + ", format string \""
518: + modelField.format + "\" with value " + strVal
519: + " on line " + lineNum, e);
520: } catch (java.lang.NumberFormatException e) {
521: throw new DataFileException(
522: "Number not valid for field " + modelField.name
523: + ", format string \""
524: + modelField.format + "\" with value "
525: + strVal + " on line " + lineNum, e);
526: }
527: }
528: return record;
529: }
530: }
|