001: /*
002: * Extension of tinySQLTable which manipulates text files.
003: *
004: * Copyright 1996, Brian C. Jepson
005: * (bjepson@ids.net)
006: *
007: * $Author: davis $
008: * $Date: 2004/12/18 21:26:34 $
009: * $Revision: 1.1 $
010: *
011: * This library is free software; you can redistribute it and/or
012: * modify it under the terms of the GNU Lesser General Public
013: * License as published by the Free Software Foundation; either
014: * version 2.1 of the License, or (at your option) any later version.
015: *
016: * This library is distributed in the hope that it will be useful,
017: * but WITHOUT ANY WARRANTY; without even the implied warranty of
018: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
019: * Lesser General Public License for more details.
020: *
021: * You should have received a copy of the GNU Lesser General Public
022: * License along with this library; if not, write to the Free Software
023: * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
024: *
025: */
026:
027: package com.sqlmagic.tinysql;
028:
029: import java.util.*;
030: import java.lang.*;
031: import java.io.*;
032: import java.sql.Types;
033:
034: /**
035: * @author Thomas Morgner <mgs@sherito.org> Changed column types to java.sql.types.
036: */
037: public class textFileTable extends tinySQLTable {
038:
039: // The data directory for tables
040: //
041: public String dataDir;
042:
043: // the object I'll use to manipulate the table
044: //
045: RandomAccessFile ftbl;
046:
047: // some constants that I don't actually use that much...
048: //
049: int COLUMN_SIZE = 0;
050: int COLUMN_TYPE = 1;
051: int COLUMN_POS = 2;
052:
053: long record_number = 0; // current record
054: long record_length; // length of a record
055:
056: /**
057: *
058: * Constructs a textFileTable. This is only called by getTable()
059: * in textFile.java.
060: *
061: * @param dDir data directory
062: * @param table_name the name of the table
063: *
064: */
065: textFileTable(String dDir, String table_name)
066: throws tinySQLException {
067:
068: dataDir = dDir; // set the data directory
069: table = table_name; // set the table name
070:
071: // attempt to open the file in read/write mode
072: //
073: try {
074: ftbl = new RandomAccessFile(dataDir + "/" + table_name,
075: "rw");
076: } catch (Exception e) {
077: throw new tinySQLException("Could not open the file "
078: + table + ".");
079: }
080:
081: // read in the table definition
082: //
083: readColumnInfo();
084:
085: }
086:
087: public int GetRowCount() {
088: // Not implemented get for text files.
089: return 0;
090: }
091:
092: /**
093: @return Length in bytes of one row
094: or 0 if not known
095: */
096: public int getRecordLength() {
097: return 0;
098: }
099:
100: /**
101: *
102: * close method. Try not to call this until you are sure
103: * the object is about to go out of scope.
104: *
105: */
106: public void close() throws tinySQLException {
107:
108: try {
109: ftbl.close();
110: } catch (IOException e) {
111: throw new tinySQLException(e.getMessage());
112: }
113: }
114:
115: /**
116: *
117: * Check if file is open for writing.
118: *
119: */
120: public boolean isOpen() throws tinySQLException {
121:
122: return true;
123: }
124:
125: /**
126: *
127: * Returns the size of a column
128: *
129: * @param column name of the column
130: * @see tinySQLTable#ColSize
131: *
132: */
133: public int ColSize(String column) {
134:
135: // retrieve the column info array from the column_info Hashtable
136: //
137: String info[] = (String[]) column_info.get(column);
138:
139: // return its size
140: //
141: return Integer.parseInt(info[COLUMN_SIZE]);
142:
143: }
144:
145: public int ColDec(String column) {
146: // returns the decimal places for a column - not implemented
147: // for text files.
148: return 0;
149: }
150:
151: /**
152: *
153: * Returns the datatype of a column.
154: *
155: * @param column name of the column.
156: * @see tinySQLTable#ColType
157: *
158: * @author Thomas Morgner <mgs@sherito.org>
159: * Q&D Hack, Just assume everybody uses java.sql.Types-IntegerConstants
160: * as Type Declaration. Perhaps there could be an translation function,
161: * which converts Strings to Integer-Types.
162: */
163: public int ColType(String column) {
164:
165: // retrieve the column info array from the column_info Hashtable
166: //
167: String info[] = (String[]) column_info.get(column);
168:
169: // return its datatype
170: //
171: return Integer.parseInt(info[COLUMN_TYPE]);
172:
173: }
174:
175: /**
176: *
177: * Updates the current row in the table.
178: *
179: * @param c Ordered Vector of column names
180: * @param v Ordered Vector (must match order of c) of values
181: * @see tinySQLTable#UpdateCurrentRow
182: *
183: */
184: public void UpdateCurrentRow(Vector c, Vector v)
185: throws tinySQLException {
186:
187: // the Vectors v and c are expected to have the
188: // same number of elements. It is also expected
189: // that the elements correspond to each other,
190: // such that value 1 of Vector v corresponds to
191: // column 1 of Vector c, and so forth.
192: //
193: for (int i = 0; i < v.size(); i++) {
194:
195: // get the column name and the value, and
196: // invoke UpdateCol() to update it.
197: //
198: String column = (String) c.elementAt(i);
199: String value = (String) v.elementAt(i);
200: UpdateCol(column, value);
201: }
202:
203: }
204:
205: /**
206: *
207: * Position the record pointer at the top of the table.
208: *
209: * @see tinySQLTable#GoTop
210: *
211: */
212: public void GoTop() throws tinySQLException {
213:
214: try {
215: ftbl.seek(0);
216: record_number = 0;
217: } catch (IOException e) {
218: throw new tinySQLException(e.getMessage());
219: }
220:
221: }
222:
223: /**
224: *
225: * Advance the record pointer to the next record.
226: *
227: * @see tinySQLTable#NextRecord
228: *
229: */
230: public boolean NextRecord() throws tinySQLException {
231:
232: // if the record number is greater than zero,
233: // advance the pointer. Otherwise, we're on the first
234: // record, and it hasn't been visited before.
235: //
236: if (record_number > 0) {
237:
238: // try to make it to the next record. An IOException
239: // indicates that we have hit the end of file.
240: //
241: try {
242: ftbl.seek(ftbl.getFilePointer() + record_length + 1);
243: } catch (IOException e) {
244: return false;
245: }
246:
247: }
248:
249: // increment the record pointer
250: //
251: record_number++;
252:
253: // check for end of file, just in case...
254: //
255: try {
256: if (ftbl.getFilePointer() == ftbl.length()) {
257: return false;
258: }
259: } catch (Exception e) {
260: throw new tinySQLException(e.getMessage());
261: }
262:
263: return true;
264:
265: }
266:
267: /**
268: *
269: * Insert a row. If c or v == null, insert a blank row
270: *
271: * @param c Ordered Vector of column names
272: * @param v Ordered Vector (must match order of c) of values
273: * @see tinySQLTable#InsertRow()
274: *
275: */
276: public void InsertRow(Vector c, Vector v) throws tinySQLException {
277:
278: try {
279:
280: // go to the end of the file
281: //
282: ftbl.seek(ftbl.length());
283:
284: // write out the deleted indicator
285: //
286: ftbl.write('N');
287:
288: // write out a blank record
289: //
290: for (int i = 1; i < record_length; i++) {
291: ftbl.write(' ');
292: }
293: ftbl.write('\n');
294:
295: // reposition at start of current record
296: //
297: ftbl.seek(ftbl.getFilePointer() - (record_length + 1));
298:
299: } catch (Exception e) {
300: throw new tinySQLException(e.getMessage());
301: }
302:
303: if (c != null && v != null)
304: UpdateCurrentRow(c, v);
305: }
306:
307: /**
308: *
309: * Retrieve a column's string value from the current row.
310: *
311: * @param column the column name
312: * @see tinySQLTable#GetCol
313: *
314: */
315: public String GetCol(String column) throws tinySQLException {
316:
317: try {
318:
319: // get the column info
320: //
321: String info[] = (String[]) column_info.get(column);
322:
323: // retrieve datatype, size, and position within row
324: //
325: String datatype = info[COLUMN_TYPE];
326: int size = Integer.parseInt(info[COLUMN_SIZE]);
327: int pos = Integer.parseInt(info[COLUMN_POS]);
328:
329: // save the file pointer
330: //
331: long OldPosition = ftbl.getFilePointer();
332:
333: // read the whole line from this row.
334: //
335: String line = ftbl.readLine();
336:
337: // retrieve the column from the line we just read,
338: // at offset pos, for length size
339: //
340: String result = line.substring(pos, pos + size);
341:
342: // restore the file pointer
343: //
344: ftbl.seek(OldPosition);
345:
346: // trim the result if it was numeric
347: //
348: if (datatype.equals("NUMERIC")) {
349: return result.trim();
350: } else {
351: return result;
352: }
353:
354: } catch (Exception e) {
355: throw new tinySQLException(e.getMessage());
356: }
357: }
358:
359: /**
360: *
361: * Update a single column.
362: *
363: * @param column the column name
364: * @param value the String value with which update the column
365: * @see tinySQLTable#UpdateCol
366: *
367: */
368: public void UpdateCol(String column, String value)
369: throws tinySQLException {
370:
371: try {
372:
373: // read the column info
374: //
375: String info[] = (String[]) column_info.get(column);
376:
377: // retrieve datatype, size, and position within row
378: //
379: String datatype = info[COLUMN_TYPE];
380: long size = Long.parseLong(info[COLUMN_SIZE]);
381: long pos = Long.parseLong(info[COLUMN_POS]);
382:
383: // position the file pointer at the column
384: // offset.
385: //
386: ftbl.seek(ftbl.getFilePointer() + pos);
387: String writeval;
388:
389: if (value.length() > (int) size) {
390:
391: // truncate the value, if it exceeds the width
392: // of the column
393: //
394: writeval = value.substring(0, (int) size);
395:
396: } else {
397:
398: // add some padding to the end of the string
399: //
400: StringBuffer pad = new StringBuffer();
401: for (int p = 0; p < ((int) size) - value.length(); p++) {
402: pad.append(" ");
403: }
404: writeval = value + pad.toString();
405: }
406:
407: // write out the column
408: //
409: ftbl.writeBytes(writeval);
410:
411: // rewind the file pointer
412: //
413: ftbl.seek(ftbl.getFilePointer()
414: - (pos + (long) writeval.length()));
415:
416: } catch (Exception e) {
417: e.printStackTrace();
418: throw new tinySQLException(e.getMessage());
419: }
420: }
421:
422: /**
423: *
424: * Delete the current row.
425: *
426: * @see tinySQLTable#DeleteRow
427: *
428: */
429: public void DeleteRow() throws tinySQLException {
430:
431: // this is real easy; just flip the value of the _DELETED column
432: //
433: UpdateCol("_DELETED", "Y");
434:
435: }
436:
437: /**
438: *
439: * Is the current row deleted?
440: *
441: * @see tinySQLTable#isDeleted()
442: *
443: */
444: public boolean isDeleted() throws tinySQLException {
445:
446: // this is real easy; just check the value of the _DELETED column
447: //
448: return (GetCol("_DELETED")).equals("Y");
449: }
450:
451: // end methods implemented from tinySQLTable.java
452: // the rest of this stuff is internal methods
453: // for textFileTable
454: //
455:
456: /*
457: *
458: * Reads in a table definition and populates the column_info
459: * Hashtable
460: *
461: */
462: void readColumnInfo() throws tinySQLException {
463:
464: try {
465:
466: column_info = new Hashtable();
467:
468: // Open an FileInputStream to the .def (table
469: // definition) file
470: //
471: FileInputStream fdef = new FileInputStream(dataDir + "/"
472: + table + ".def");
473:
474: // use a StreamTokenizer to break up the stream.
475: //
476: Reader r = new BufferedReader(new InputStreamReader(fdef));
477: StreamTokenizer def = new StreamTokenizer(r);
478:
479: // set the | as a delimiter, and set everything between
480: // 0 and z as word characters. Let it know that eol is
481: // *not* significant, and that it should parse numbers.
482: //
483: def.whitespaceChars('|', '|');
484: def.wordChars('0', 'z');
485: def.eolIsSignificant(false);
486: def.parseNumbers();
487:
488: // read each token from the tokenizer
489: //
490: while (def.nextToken() != def.TT_EOF) {
491:
492: // first token is the datatype
493: //
494: // Q&D: Default is char value, numeric is special
495: String datatype = String.valueOf(Types.CHAR);
496: if (def.sval.equals("NUMERIC")) {
497: datatype = String.valueOf(Types.NUMERIC);
498: }
499:
500: // get the next token; it's the column name
501: //
502: def.nextToken();
503: String column = def.sval;
504:
505: // get the third token; it's the size of the column
506: //
507: def.nextToken();
508: long size = (new Double(def.nval)).longValue();
509:
510: // create an info array
511: //
512: String[] info = new String[3];
513:
514: // store the datatype, the size, and the position
515: // within the record (the record length *before*
516: // we increment it with the size of this column
517: //
518: info[COLUMN_TYPE] = datatype;
519: info[COLUMN_SIZE] = Long.toString(size);
520: info[COLUMN_POS] = Long.toString(record_length);
521:
522: // this is the start position of the next column
523: //
524: record_length += size;
525:
526: // store this info in the column_info hash,
527: // keyed by column name.
528: //
529: column_info.put(column, info);
530:
531: }
532:
533: fdef.close(); // close the file
534:
535: } catch (Exception e) {
536:
537: throw new tinySQLException(e.getMessage());
538:
539: }
540:
541: }
542:
543: }
|