File Coverage

blib/lib/vsDB.pm
Criterion Covered Total %
statement 0 383 0.0
branch 0 142 0.0
condition 0 60 0.0
subroutine 0 37 0.0
pod 32 33 96.9
total 32 655 4.8


line stmt bran cond sub pod time code
1             # ----------------------------------------------------------------------------
2             # vsDB (verysimple database) Module
3             # Copyright (c) 2001 Jason M. Hinkle. All rights reserved. This module is
4             # free software; you may redistribute it and/or modify it under the same
5             # terms as Perl itself.
6             # For more information see: http://www.verysimple.com/scripts/
7             #
8             # LEGAL DISCLAIMER:
9             # This software is provided as-is. Use it at your own risk. The
10             # author takes no responsibility for any damages or losses directly
11             # or indirectly caused by this software.
12             # ----------------------------------------------------------------------------
13             package vsDB;
14             require 5.000;
15             $VERSION = "1.4.3";
16             $ID = "vsDB.pm";
17            
18            
19             #_____________________________________________________________________________
20             sub new {
21 0     0 0   my $class = shift;
22 0           my %keyValues = @_;
23 0           my (%fieldNames,@fileArray,@row,@filterArray);
24            
25             # normalize the input for backwards compatibility & to set default delim.
26 0   0       $keyValues{'file'} = $keyValues{'file'} || $keyValues{'File'} || "";
27 0 0         $keyValues{'delimiter'} = $keyValues{'Delimiter'} unless defined($keyValues{'delimiter'});
28 0 0         $keyValues{'delimiter'} = "\t" unless defined($keyValues{'delimiter'});
29            
30 0           my $this = {
31             fileName => $keyValues{'file'},
32             delimiter => $keyValues{'delimiter'},
33             fieldNames => \%fieldNames,
34             fileArray => \@fileArray,
35             filterArray => \@filterArray,
36             row => \@row,
37             recordCount => 0,
38             filterRecordCount => 0,
39             absolutePosition => 0,
40             pageSize => 10,
41             EOF => 1,
42             isOpen => 0,
43             lastError => '',
44             appendOnly => 1,
45             isDirty => 0,
46             originalCount => 0,
47             CR => '',
48             LF => '',
49             noFieldNames => $keyValues{'NoFieldNames'},
50             };
51 0           bless $this;
52 0           return $this;
53             }
54            
55            
56             # ###########################################################################
57             # PUBLIC PROPERTIES
58            
59            
60             #_____________________________________________________________________________
61             sub Version {
62 0     0 1   return $VERSION;
63             }
64            
65             #_____________________________________________________________________________
66             sub ID {
67 0     0 1   return $ID;
68             }
69            
70             #_____________________________________________________________________________
71             sub LastError {
72 0     0 1   my ($this) = shift;
73 0           return $this->{'lastError'};
74             }
75            
76             #_____________________________________________________________________________
77             sub AbsolutePosition {
78 0     0 1   my ($this) = shift;
79 0           my ($newValue) = shift;
80 0 0         if (defined($newValue)) {
81 0           $this->{'absolutePosition'} = $newValue;
82 0           $this->_RefreshRow;
83             } else {
84 0           return $this->{'absolutePosition'};
85             }
86             }
87            
88             #_____________________________________________________________________________
89             sub ActivePage {
90 0     0 1   my ($this) = shift;
91 0           my ($newValue) = shift;
92 0 0         if (defined($newValue)) {
93 0 0         $newValue = $this->PageCount if ($newValue > $this->PageCount);
94 0           $this->MoveFirst;
95             # don't need to do anything if page 1
96 0 0         return 1 if ($newValue == 1);
97             # set the new page, just move next until we hit the right spot
98 0           my ($records) = ($newValue * $this->PageSize) - $this->PageSize;
99 0   0       for (my $count = 0; $count < $records && !$this->EOF; $count++) {
100 0           $this->MoveNext;
101             }
102             # old code- faster, but doesnt work right with filters
103             #$this->{'absolutePosition'} = ($this->{'pageSize'} * ($newValue-1)) + 1;
104             # make sure we are on the right page if filtered
105             #while ($this->{'filterArray'}[$this->{'absolutePosition'}]) {
106             # $this->{'absolutePosition'}++;
107             #}
108             #$this->_RefreshRow;
109 0           return 1;
110             } else {
111             # BUG - when filtering, this returns the wrong value
112 0 0         return 1 if ($this->{'absolutePosition'} == 1);
113 0           my ($count) = (($this->{'absolutePosition'} - 1) / $this->{'pageSize'}); #/
114 0           my ($activePage) = int($count) + 1;
115 0 0         $activePage = $this->PageCount if ($activePage > $this->PageCount);
116 0           return $activePage;
117             }
118             }
119            
120             #_____________________________________________________________________________
121             sub PageSize {
122 0     0 1   my ($this) = shift;
123 0           my ($newValue) = shift;
124 0 0         if (defined($newValue)) {
125 0 0         $this->{'pageSize'} = int($newValue) if (int($newValue) > 0);
126 0           return 1;
127             } else {
128 0           return $this->{'pageSize'};
129             }
130             }
131            
132             #_____________________________________________________________________________
133             sub PageCount {
134 0     0 1   my ($this) = shift;
135 0           my ($count) = ($this->{'filterRecordCount'} / $this->{'pageSize'}); #/
136 0 0         if (int($count) < $count) {$count = int($count)+1}
  0            
137 0           return $count;
138             }
139            
140             #_____________________________________________________________________________
141             sub File {
142 0     0 1   my ($this) = shift;
143 0           my ($newVal) = shift;
144             # if the file has changed, then we can't just append...
145 0 0         if ($newVal) {$this->{'appendOnly'} = 0}
  0            
146 0           return $this->_GetSetProperty("fileName",$newVal);
147             }
148            
149             #_____________________________________________________________________________
150             sub CR {
151 0     0 1   return shift->_GetSetProperty("CR",shift);
152             }
153            
154             #_____________________________________________________________________________
155             sub NoFieldNames {
156 0     0 1   return shift->_GetSetProperty("noFieldNames",shift);
157             }
158            
159             #_____________________________________________________________________________
160             sub LF {
161 0     0 1   return shift->_GetSetProperty("LF",shift);
162             }
163            
164             #_____________________________________________________________________________
165             sub Delimiter {
166 0     0 1   return shift->_GetSetProperty("delimiter",shift);
167             }
168            
169             #_____________________________________________________________________________
170             sub RecordCount {
171 0     0 1   return shift->{'filterRecordCount'};
172             }
173            
174             #_____________________________________________________________________________
175             sub EOF {
176 0     0     my ($this) = shift;
177 0 0         return 1 if ($this->RecordCount < 1);
178 0           return $this->{'EOF'};
179             }
180            
181             #_____________________________________________________________________________
182             sub FieldValue {
183 0     0 1   my ($this) = shift;
184 0   0       my ($fieldName) = shift || return "ERROR: FieldValue(): Field Name Required";
185 0           my ($newValue) = shift;
186 0           my ($fieldNumber) = $this->{'fieldNames'}{$fieldName};
187 0           my ($lineFeed) = chr(10);
188 0           my ($carriageReturn) = chr(13);
189 0           my ($crReplacement) = $this->{'CR'};
190 0           my ($lfReplacement) = $this->{'LF'};
191 0 0         return "ERROR: FieldValue('" . $fieldName . "') Field Not Found." if (!defined($fieldNumber));
192            
193             # if a new value is defined, update, otherwise return current value
194 0 0         if (defined($newValue)) {
195 0           $this->{'isDirty'} = 1;
196 0 0         if ($this->{'absolutePosition'} <= $this->{'originalCount'}) {
197 0           $this->{'appendOnly'} = 0
198             };
199             # make sure we don't corrupt the file with a delimiter or
200             # line break in the data.
201 0           $newValue =~ s/$this->{'delimiter'}//g;
202 0           $newValue =~ s/$carriageReturn/$crReplacement/g;
203 0           $newValue =~ s/$lineFeed/$lfReplacement/g;
204             # $newValue =~ s/\n//g; # (should already be dealt with)
205 0           $this->{'row'}[$fieldNumber] = $newValue;
206            
207             # update the fileArray to match the current row. originally used
208             # a join on the row array, but that caused unititialize var errors
209             # when there are blank fields. this could probably be improved by
210             # only updating when the cursor is moved or commit is called
211 0           my ($newRow,$newField);
212 0           my ($colNum) = 0;
213 0           foreach ($this->FieldNames) {
214 0           $newField = $this->{'row'}[$colNum];
215 0 0         if (!defined($newField)) {$newField = ""}
  0            
216 0           $newRow .= $newField;
217 0           $newRow .= $this->{'delimiter'};
218 0           $colNum++;
219             }
220             # get rid of the last delimiter
221 0           for (my $x = 1;$x <= length($this->{'delimiter'}); $x++) {
222 0           chop($newRow);
223             }
224            
225 0           $this->{'fileArray'}[$this->{'absolutePosition'}] = $newRow . "\n";
226 0           return 1;
227             } else {
228             # make sure we return a defined value || won't work because it doesn;t
229             # differentiate between 0 and null
230 0 0         if (defined($this->{'row'}[$fieldNumber])) {
231 0           my $returnVal = $this->{'row'}[$fieldNumber];
232 0           $returnVal =~ s/$crReplacement/$carriageReturn/g;
233 0           $returnVal =~ s/$lfReplacement/$lineFeed/g;
234 0           return $returnVal;
235            
236             } else {
237 0           return "";
238             }
239             }
240             }
241            
242             #_____________________________________________________________________________
243             sub FieldNames {
244             # returns all fieldnames as an array
245 0     0 1   my ($this) = shift;
246 0 0         return 0 unless ($this->{'isOpen'});
247 0           my ($fieldRow) = $this->{'fileArray'}[0];
248 0           chop ($fieldRow);
249 0           my (@tempfieldNames) = split($this->{'delimiter'},$fieldRow);
250 0           return @tempfieldNames;
251             }
252            
253             #_____________________________________________________________________________
254             sub Row {
255             # returns current row values as an array
256 0     0 1   my ($this) = shift;
257 0 0         return 0 unless ($this->{'isOpen'});
258 0           my ($tempRow) = $this->{'row'};
259 0           return @$tempRow;
260             }
261            
262             #_____________________________________________________________________________
263             sub xml {
264             # returns current recordset as xml
265 0     0 1   my ($this) = shift;
266 0   0       my ($strRootName) = shift || "vsDB";
267 0   0       my ($strElementName) = shift || "Record";
268 0           my ($strXml);
269            
270 0           $strXml = "\n";
271 0           $strXml .= "\n";
272 0           $strXml .= "<$strRootName>\n";
273 0           $this->MoveFirst;
274 0           my (@fields) = $this->FieldNames;
275 0           my ($field, $fieldValue);
276 0           until ($this->EOF) {
277 0           $strXml .= "<$strElementName>\n";
278 0           foreach $field (@fields) {
279 0           $fieldValue = $this->FieldValue($field);
280 0           $strXml .= "<$field>$fieldValue\n";
281             }
282 0           $strXml .= "\n";
283 0           $this->MoveNext;
284             }
285 0           $strXml .= "\n";
286            
287 0           return $strXml;
288             }
289            
290             #_____________________________________________________________________________
291             sub MoveNext {
292             # moves the curser to the next row in the data file
293 0     0 1   my ($this) = shift;
294 0 0         return 0 unless ($this->{'isOpen'});
295 0 0         return 0 if ($this->{'EOF'});
296 0           $this->{'absolutePosition'}++;
297 0           while ($this->{'filterArray'}[$this->{'absolutePosition'}]) {
298 0           $this->{'absolutePosition'}++;
299             }
300 0           $this->_RefreshRow;
301 0           return 1;
302             }
303            
304             #_____________________________________________________________________________
305             sub MovePrevious {
306             # moves the curser to the previous row in the data file
307 0     0 1   my ($this) = shift;
308 0 0         return 0 unless ($this->{'isOpen'});
309 0 0         return 0 if ($this->{'absolutePosition'} < 2);
310 0           $this->{'absolutePosition'}--;
311 0           while ($this->{'filterArray'}[$this->{'absolutePosition'}]) {
312 0           $this->{'absolutePosition'}--;
313             }
314 0           $this->_RefreshRow;
315 0           return 1;
316             }
317            
318             #_____________________________________________________________________________
319             sub MoveFirst {
320             # moves the curser to the first row in the data file
321 0     0 1   my ($this) = shift;
322 0 0         return 0 unless ($this->{'isOpen'});
323 0           $this->{'absolutePosition'} = 1;
324 0           while ($this->{'filterArray'}[$this->{'absolutePosition'}]) {
325 0           $this->{'absolutePosition'}++;
326             }
327 0           $this->_RefreshRow;
328 0           return 1;
329             }
330            
331             #_____________________________________________________________________________
332             sub MoveLast {
333             # moves the curser to the last row in the data file
334 0     0 1   my ($this) = shift;
335 0 0         return 0 unless ($this->{'isOpen'});
336 0           $this->{'absolutePosition'} = $this->{'recordCount'};
337 0           while ($this->{'filterArray'}[$this->{'absolutePosition'}]) {
338 0           $this->{'absolutePosition'}--;
339             }
340 0           $this->_RefreshRow;
341 0           return 1;
342             }
343            
344             #_____________________________________________________________________________
345             sub Delete {
346             # delete the current row
347 0     0 1   my ($this) = shift;
348 0 0         return 0 unless ($this->{'isOpen'});
349 0 0         return 0 if $this->{'recordCount'} < 1;
350 0 0         return 0 if ($this->{'EOF'});
351 0           $this->{'isDirty'} = 1;
352 0 0         if ($this->{'absolutePosition'} <= $this->{'originalCount'}) {
353 0           $this->{'appendOnly'} = 0
354             };
355 0           my ($tempArray) = $this->{'fileArray'};
356 0           splice(@$tempArray,$this->{'absolutePosition'},1);
357 0           $this->{'recordCount'}--;
358 0           $this->{'filterRecordCount'}--;
359 0           $this->_RefreshRow;
360 0           return 1;
361             }
362            
363             #_____________________________________________________________________________
364             sub AddNew {
365             # add a new row to the end of the recordset
366 0     0 1   my ($this) = shift;
367 0 0         return 0 unless ($this->{'isOpen'});
368 0           $this->{'isDirty'} = 1;
369 0           $this->{'recordCount'}++;
370 0           $this->{'absolutePosition'} = $this->{'recordCount'};
371 0           $this->{'filterRecordCount'}++;
372             # the number of delimiter chars is fieldCount - 1
373 0           my ($delimiterCount) = 0;
374 0           foreach ($this->FieldNames) {
375 0           $delimiterCount++;
376             }
377 0           $delimiterCount--;
378            
379             # add the correct number of colums using the delimiterCount
380 0           $this->{'fileArray'}[$this->{'absolutePosition'}] = ($this->{'delimiter'} x $delimiterCount) . "\n";
381            
382 0           $this->MoveLast;
383            
384 0           return 1;
385             }
386            
387             #_____________________________________________________________________________
388             sub AddNewField {
389             # add a new row to the end of the recordset
390 0     0 1   my ($this) = shift;
391 0   0       my ($newFieldName) = shift || return 0;
392 0           my ($defaultValue) = shift;
393 0 0         $defaultValue = '' unless (defined($defaultValue));
394            
395 0           $this->{'isOpen'} = 1;
396 0           $this->{'appendOnly'} = 0;
397 0           $this->{'isDirty'} = 1;
398            
399             # update the fieldnames array
400 0           my ($nextField) = 0;
401 0           foreach ($this->FieldNames) {
402 0           $nextField++;
403             }
404 0           $this->{'fieldNames'}{$newFieldName} = $nextField;
405            
406 0           chop($this->{'fileArray'}[0]);
407            
408             # update the first row in the file array
409 0 0         if ($nextField > 0) {
410 0           $this->{'fileArray'}[0] .= $this->{'delimiter'};
411             }
412 0           $this->{'fileArray'}[0] .= $newFieldName . "\n";
413            
414 0           return 1;
415             }
416            
417             #_____________________________________________________________________________
418             sub Max {
419             # returns the maximum value for the specified column
420 0     0 1   my ($this) = shift;
421 0   0       my ($fieldName) = shift || return 0;
422 0   0       my ($alpha) = shift || 0;
423 0           my ($curVal);
424 0           my ($curPos) = $this->{'absolutePosition'};
425 0           $this->MoveFirst;
426 0           my ($maxVal) = $this->FieldValue($fieldName);
427 0           while (!$this->EOF) {
428 0           $curVal = $this->FieldValue($fieldName);
429 0 0         if (!$alpha) {
430 0 0         if ($curVal ne "") {
431 0 0         if (int($curVal) > int($maxVal)) {$maxVal = $curVal};
  0            
432             }
433             } else {
434 0 0         if ((lc($curVal) cmp lc($maxVal)) > 0) {$maxVal = $curVal};
  0            
435             }
436 0           $this->MoveNext;
437             }
438 0           $this->AbsolutePosition($curPos);
439 0           return $maxVal;
440             }
441            
442             #_____________________________________________________________________________
443             sub Min {
444             # returns the maximum value for the specified column
445 0     0 1   my ($this) = shift;
446 0   0       my ($fieldName) = shift || return 0;
447 0   0       my ($alpha) = shift || 0;
448 0           my ($curVal);
449 0           my ($curPos) = $this->{'absolutePosition'};
450 0           $this->MoveFirst;
451 0           my ($minVal) = $this->FieldValue($fieldName);
452 0           while (!$this->EOF) {
453 0           $curVal = $this->FieldValue($fieldName);
454 0 0         if ($alpha) {
455 0 0         if ($curVal ne "") {
456 0 0         if (int($curVal) < int($minVal)) {$minVal = $curVal};
  0            
457             }
458             } else {
459 0 0         if ((lc($curVal) cmp lc($minVal)) < 0) {$minVal = $curVal};
  0            
460             }
461 0           $this->MoveNext;
462             }
463 0           $this->AbsolutePosition($curPos);
464 0           return $minVal;
465             }
466            
467             #_____________________________________________________________________________
468             sub Filter {
469             # $obj->Filter($fieldName,$operator,$criteria [,$filterOr]);
470            
471             # TODO: > and < are not working properly... maybe text comparison problem??
472 0     0 1   my ($this) = shift;
473 0   0       my ($fieldName) = shift || return 0;
474 0   0       my ($operator) = shift || "eq";
475 0           my ($criteria) = shift;
476 0   0       my ($filterOr) = shift || 0;
477 0 0         $criteria = "" unless defined($criteria);
478            
479 0           my ($filterSetting);
480 0           my ($absolutePosition) = 0;
481            
482             # manually cycle through because MoveNext will skip past any prev. filtered records
483 0           $this->{'absolutePosition'} = 1;
484 0           $this->_RefreshRow;
485 0           while ($this->{'absolutePosition'} != $absolutePosition) {
486 0           $filterSetting = 0;
487 0           $absolutePosition = $this->{'absolutePosition'};
488            
489             #print "original val=" . ($this->{'filterArray'}[$absolutePosition] || 'x') . " ";
490            
491 0 0 0       if ($filterOr && !$this->{'filterArray'}[$absolutePosition]) {
    0 0        
    0 0        
    0 0        
    0 0        
    0 0        
    0 0        
492             # leave record alone if it already passed and OR is specified
493 0           $filterSetting = 0;
494             } elsif (!$filterOr && $this->{'filterArray'}[$absolutePosition]) {
495             # don't undue any previous filters if AND is specified
496 0           $filterSetting = 1;
497             } elsif ($operator eq "eq" && $this->FieldValue($fieldName) ne $criteria) {
498 0           $filterSetting = 1;
499             } elsif ($operator eq "ne" && !($this->FieldValue($fieldName) ne $criteria)) {
500 0           $filterSetting = 1;
501             } elsif ($operator eq "like" && !(index(lc($this->FieldValue($fieldName)),lc($criteria),0) + 1)) {
502 0           $filterSetting = 1;
503             } elsif ($operator eq ">" && !($this->FieldValue($fieldName) > $criteria) ) {
504 0           $filterSetting = 1;
505             } elsif ($operator eq "<" && !($this->FieldValue($fieldName) < $criteria) ) {
506 0           $filterSetting = 1;
507             }
508            
509             # print $absolutePosition . ": " . $filterSetting;
510            
511             # the filtercount may need to be decremented or incremented, depending on if
512             # the current record was filtered, re-filtered, or unfiltered
513 0 0         if ($this->{'filterArray'}[$absolutePosition]) {
514 0 0         $this->{'filterRecordCount'} = $this->{'filterRecordCount'} + 1 unless ($filterSetting);
515             } else {
516 0           $this->{'filterRecordCount'} = $this->{'filterRecordCount'} - $filterSetting;
517             }
518            
519 0           $this->{'filterArray'}[$absolutePosition] = $filterSetting;
520            
521             #print " filterCount=" . $this->{'filterRecordCount'} . "
";
522            
523 0           $this->{'absolutePosition'}++;
524 0           $this->_RefreshRow;
525             }
526            
527 0           $this->MoveFirst;
528 0           return 1;
529             }
530            
531             #_____________________________________________________________________________
532             sub RemoveFilter {
533 0     0 1   my ($this) = shift;
534 0           my (@newArray);
535 0           $this->{'filterArray'} = \@newArray;
536 0           $this->{'filterRecordCount'} = $this->{'recordCount'};
537 0           return 1;
538             }
539            
540             #_____________________________________________________________________________
541             sub Commit {
542             # update the file, saving all changes made
543 0     0 1   my ($this) = shift;
544 0   0       my ($useFlock) = shift || 0;
545 0           my ($fileName) = $this->{'fileName'};
546            
547             # if no changes were made, don't bother writing to the file
548 0 0         if (!$this->{'isDirty'}) {return 1};
  0            
549            
550 0 0         if ($this->{'appendOnly'}) {
551             # if only new records were added, just append to the file
552 0           my ($nCount);
553 0 0         if (!open (OUTPUTFILE, ">>$fileName")) {
554 0           $this->{'lastError'} = "Commit: Couldn't Open DataFile '$fileName' For Appending";
555 0           return 0
556             };
557 0 0         flock(OUTPUTFILE,2) if ($useFlock);
558 0           my ($tempArray) = $this->{'fileArray'};
559 0           for ($nCount = $this->{'originalCount'} + 1; $nCount <= $this->{'recordCount'}; $nCount++) {
560 0           print OUTPUTFILE @$tempArray[$nCount];
561             }
562             } else {
563             # if records were changed or deleted, we have to replace them all
564 0 0         if (!open (OUTPUTFILE, ">$fileName")) {
565 0           $this->{'lastError'} = "Commit: Couldn't Open DataFile '$fileName' For Writing";
566 0           return 0
567             };
568 0 0         flock(OUTPUTFILE,2) if ($useFlock);
569 0           my ($tempArray) = $this->{'fileArray'};
570 0 0         if ($this->NoFieldNames) {
571             # if no field names was specified, remove the dummy row before saving, then
572             # put it back on after the save is complete
573 0           my ($headerRow) = shift(@$tempArray);
574 0           print OUTPUTFILE join('',@$tempArray);
575 0           unshift(@$tempArray,$headerRow);
576             } else {
577 0           print OUTPUTFILE join('',@$tempArray);
578             }
579             }
580 0           close (OUTPUTFILE);
581 0 0         flock(OUTPUTFILE,8) if ($useFlock);
582 0           return 1;
583             }
584            
585             #_____________________________________________________________________________
586             sub Sort {
587             # sorts the datafile on the given column
588             # obj->Sort($field [, $sortMode]);
589             # if $field is ommited, or an invalid fieldname is used, defaults to
590             # the left-most column.
591             # $sortMode 0 = alpha ascending, 1 = alpha descending, 2 = numeric ascending, 3 = numeric descending
592            
593 0     0 1   my ($this) = shift;
594 0   0       my ($fieldName) = shift || '0';
595 0   0       my ($sortMode) = shift || '0';
596 0           my ($delimiter) = $this->{'delimiter'};
597            
598             # can't append once we've changed the sort order
599 0           $this->{'appendOnly'} = 0;
600            
601             # sorting will mess up filter, so lets remove it
602 0           $this->RemoveFilter;
603            
604             # get the fieldnumber (or default to leftmost column)
605 0   0       my ($fieldNumber) = $this->{'fieldNames'}{$fieldName} || 0;
606             # make a copy of the unsorted array pointer
607 0           my ($unsortedArray) = $this->{'fileArray'};
608             # remove the column names from the array
609 0           my ($fieldNames) = shift(@$unsortedArray);
610            
611             # now we sort the unsorted array
612 0           my (@sortedArray) = sort {
613             # custom sorting comparison routine
614 0           my (@aVals) = split($delimiter,$a);
615 0           my (@bVals) = split($delimiter,$b);
616 0 0         if ($sortMode eq "1") {
    0          
    0          
617             # alpha descending
618 0           return lc($bVals[$fieldNumber]) cmp lc($aVals[$fieldNumber]);
619             } elsif ($sortMode eq "2") {
620             # numeric ascending
621 0           return $bVals[$fieldNumber] > $aVals[$fieldNumber];
622             } elsif ($sortMode eq "3") {
623             # numeric descending
624 0           return $bVals[$fieldNumber] > $aVals[$fieldNumber];
625             } else {
626             # alpha ascending
627 0           return lc($aVals[$fieldNumber]) cmp lc($bVals[$fieldNumber]);
628             }
629 0           undef(@aVals);
630 0           undef(@bVals);
631             } @$unsortedArray;
632             # get rid of the unsorted array
633 0           undef($unsortedArray);
634            
635             # put the column names back in and update the array pointer
636 0           unshift(@sortedArray,$fieldNames);
637 0           $this->{'fileArray'} = \@sortedArray;
638            
639 0           $this->MoveFirst;
640 0           return 1;
641             }
642            
643            
644             #_____________________________________________________________________________
645             sub Open {
646             # open the file, store the contents as an array, get the number of
647             # records and retreive the first row
648 0     0 1   my $this = shift;
649 0           my $fileName = $this->{'fileName'};
650 0           my $delimiter = $this->{'delimiter'};
651 0           my (@tempFileArray);
652            
653             # security check to make sure a command is not being attempted
654 0           $fileName =~ s/;//g;
655 0           $fileName =~ s/|//g;
656            
657 0 0         if (!(-e $fileName)) {
    0          
658 0           $this->{'lastError'} = "Open: Datafile '$fileName' Not Found";
659 0           return 0;
660             } elsif (!(-r $fileName)) {
661 0           $this->{'lastError'} = "Open: Couldn't Open DataFile '$fileName' For Reading";
662 0           return 0;
663             }
664            
665             # try to open the file
666 0 0         if (open(THISFILE, "$fileName")) {
667 0           @tempFileArray = ;
668 0           close(THISFILE);
669             } else {
670 0           return 0;
671             }
672            
673             # if no fieldnames is specified, then insert a dummy row at the front
674             # so the module will use numbers as the fieldnames
675 0 0         if ($this->NoFieldNames) {
676 0           my ($dummyRow) = "";
677 0   0       my (@DummyFieldNames) = split($delimiter, ($tempFileArray[0] || "") );
678 0           my ($dummyDelim) = "";
679 0           my ($count) = 1;
680 0           foreach (@DummyFieldNames) {
681 0           $dummyRow .= $dummyDelim . $count;
682 0           $dummyDelim = $delimiter;
683 0 0         $dummyDelim = "\|" if ($dummyDelim eq "\\|"); # glitch on windows servers with | as delim
684 0           $count++;
685             }
686 0           unshift(@tempFileArray,$dummyRow . "\n")
687             }
688            
689             # get the top row, which should be fieldnames
690 0   0       my $fileRow = $tempFileArray[0] || "\n";
691 0           chop($fileRow);
692            
693             # get the entire contents of the file
694 0           $this->{'fileArray'} = \@tempFileArray;
695            
696             # get the number of rows
697 0           $this->{'recordCount'} = @tempFileArray - 1;
698            
699             # split the top row into fields
700 0           my (@tempfieldNames) = split($delimiter,$fileRow);
701 0           my ($fieldName) = "";
702 0           my ($counter) = 0;
703 0           foreach $fieldName (@tempfieldNames) {
704 0           $this->{'fieldNames'}{$fieldName} = $counter;
705 0           $counter++
706             }
707            
708            
709 0           $this->MoveFirst;
710 0           $this->{'isOpen'} = 1;
711 0           $this->_RefreshRow;
712 0           $this->{'filterRecordCount'} = $this->{'recordCount'};
713 0           $this->{'originalCount'} = $this->{'recordCount'};
714 0           return 1;
715             }
716            
717             #_____________________________________________________________________________
718             sub Close {
719 0     0 1   my ($this) = shift;
720 0           $this->{'isOpen'} = 0;
721 0           return 1;
722             }
723            
724            
725             # ###########################################################################
726             # PRIVATE METHODS
727            
728            
729             #_____________________________________________________________________________
730             sub DESTROY {
731 0     0     my ($this) = shift;
732 0           $this->Close;
733             }
734            
735             #_____________________________________________________________________________
736             sub _RefreshRow {
737             # sync the current row with the fileArray. also do some validation to
738             # make sure we haven't moved the curser out of range
739 0     0     my ($this) = shift;
740 0 0         return 0 unless ($this->{'isOpen'});
741             # make sure absolutePosition is a legit value and set EOF
742 0           $this->{'EOF'} = 0;
743 0 0         $this->{'absolutePosition'} = 1 if ($this->{'absolutePosition'} < 1);
744 0 0         if ($this->{'absolutePosition'} > $this->{'recordCount'}) {
745 0           $this->{'EOF'} = 1;
746 0           $this->{'absolutePosition'} = $this->{'recordCount'};
747 0           return 1;
748             }
749            
750             # now grab the next row
751 0           my ($tempRow) = $this->{'fileArray'}[$this->{'absolutePosition'}];
752 0           chop ($tempRow);
753 0           my (@row) = split($this->{'delimiter'},$tempRow);
754 0           $this->{'row'} = \@row;
755 0           return 1;
756             }
757            
758            
759             #_____________________________________________________________________________
760             sub _GetSetProperty {
761             # private fuction that is used by properties to get/set values
762             # if a parameter is sent in, then the property is set and true is returned.
763             # if no parameter is sent, then the current value is returned
764 0     0     my $this = shift;
765 0           my $fieldName = shift;
766 0           my $newValue = shift;
767 0 0         if (defined($newValue)) {
768 0           $this->{$fieldName} = $newValue;
769             } else {
770 0           return $this->{$fieldName};
771             }
772 0           return 1;
773             }
774            
775             1; # for require
776            
777            
778             __END__