/*----------------------------------------------------------------------------*/ /* */ /* Copyright (c) 2011-2014 Rexx Language Association. All rights reserved. */ /* */ /* This program and the accompanying materials are made available under */ /* the terms of the Common Public License v1.0 which accompanies this */ /* distribution. A copy is also available at the following address: */ /* https://www.oorexx.org/license.html */ /* */ /* Redistribution and use in source and binary forms, with or */ /* without modification, are permitted provided that the following */ /* conditions are met: */ /* */ /* Redistributions of source code must retain the above copyright */ /* notice, this list of conditions and the following disclaimer. */ /* Redistributions in binary form must reproduce the above copyright */ /* notice, this list of conditions and the following disclaimer in */ /* the documentation and/or other materials provided with the distribution. */ /* */ /* Neither the name of Rexx Language Association nor the names */ /* of its contributors may be used to endorse or promote products */ /* derived from this software without specific prior written permission. */ /* */ /* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS */ /* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT */ /* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS */ /* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT */ /* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, */ /* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED */ /* TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, */ /* OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY */ /* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING */ /* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS */ /* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /* */ /*----------------------------------------------------------------------------*/ /* ooDialog User Guide Exercise08: GenericFile v02-00 24May13 Contains: class "GenericFile". Pre-requisites: None. Desription: A component that opens and reads a text file that has a specific format. Outstanding problems: None reported. Changes: v01-00 21Jly12: First version. 25Aug12: Moved file open from init method to a separate method. 16Dec12: Trivial correction of a couple of comments. 11Jan13: Commented-out 'say' instructions. v02-00 24May13: Added inherit from the 'Component' mixin. Constraints: Format of each record must be: recordId | field2 | field3 | .... | fieldn Note that the RecordId or Key must be in the first column. } ------------------------------------------------------------------------------*/ ::REQUIRES "Support\Component.rex" /*////////////////////////////////////////////////////////////////////////////// ============================================================================== GenericFile v01-01 24Aug12 ----------- = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = */ ::CLASS GenericFile PUBLIC INHERIT Component ::ATTRIBUTE fileHeaders GET PUBLIC -- a 1D array of column labels/headers ::ATTRIBUTE fileHeaders SET PRIVATE ::ATTRIBUTE fileRecords GET PUBLIC -- a 2D array of records ::ATTRIBUTE fileRecords SET PRIVATE ::ATTRIBUTE fileArray GET PUBLIC -- a 1D array of raw data from the file. ::ATTRIBUTE fileArray SET PRIVATE ::ATTRIBUTE fileAsDirectory GET PUBLIC -- a directory containing: -- * headers: a 1D array -- * records: a 2D array -- * count: the number of records ::ATTRIBUTE fileAsDirectory SET PRIVATE /*---------------------------------------------------------------------------- Class Methods - none. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ /*---------------------------------------------------------------------------- Instance Methods - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - init - Given a filename and the number of columns in the file, loads the fileinto attribute 'fileArray', a 1-dimensional array where each item is a single "raw" file line or record. Returns the number of records in the file (or, if unsuccesful at reading the file, returns 0 (.false) if file doesn't exist, or -1 if no records in the file. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ ::METHOD init PUBLIC expose columns recordCount fileArray filename use arg filename, columns dirFile = self~readFile(fileName, columns) if dirFile = .false | dirFile = -1 then return .false self~fileAsDirectory = dirFile -- store whole file in the attribute self~fileHeaders = dirFile[headers] -- store fileHeaders in the attribute self~fileRecords = dirFile[records] -- store fileRecords in the attribute recordCount = dirFile[count] -- store no. records in the attribute return recordCount /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - readFile - Given a filename and the number of columns, reads a file and returns it in "fileAsDirectory" format. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ ::METHOD readFile PRIVATE expose recordCount use strict arg fileName, columns recordCount = 0 --say "GenericFile-readFile-01: fileName =" fileName "columns =" columns file = .stream~new(filename) --say "GenericFile-readFile-02: file~query(exists):" file~query("exists") if file~query("exists") = "" then do say "GenericFile-readFile-03: file" filename " does not exist." return .false end recordCount = file~lines if recordCount < 2 then do recordCount = 0 return -1 -- Problem - no records, only headers - or nothing!. end recordCount = recordCount - 1 -- Exclude columns headers line from the count. arrRawfile = file~makearray -- read file into an array; each line is an array item file~close --say "GenericFile-readFile-04:" filearray~string self~fileArray = arrRawFile -- (deprecated) store "raw" file array in attribute -- Now turn the raw file data into a directory: -- (1) get a 1D array of column headers or "field labels": fileHeaders = arrRawFile[1] arrFileHeaders = self~parseLine(fileHeaders, columns) self~fileHeaders = arrFileHeaders -- (2) get a 2D array of field values from data records: arrFileRecords = .Array~new do i=1 to recordCount -- get a 2D array of file records arrRecord = self~parseLine(arrRawFile[i+1], columns) do j = 1 to columns arrFileRecords[i,j] = arrRecord[j] end end self~fileRecords = arrFileRecords -- (3) Build a directory containing headers, records, and lines (or record count). dirFile = .directory~new dirFile[headers] = arrFileHeaders dirFile[count] = recordCount dirFile[records] = arrFileRecords return dirFile -- Returns a directory containing the file and file info. /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - parseLine - parse text from file line and place into an array. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ ::METHOD parseLine PRIVATE -- returns a 1D array of fields use strict arg line, columns arr = .Array~new do i=1 to columns parse var line field "|" line field = field~strip arr[i] = field end return arr /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - list - Lists a file in raw form on the console. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ ::METHOD list PUBLIC expose recordCount filename if recordCount = 0 then do return .false end say; say "GenericFile-list: records in File '"||filename||"'" say self~fileHeaders do i=1 to recordCount say self~fileRecords[i] end return .true /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - getRecord - Given a "key", returns a directory containing the record data associated with that key. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ ::METHOD getRecord PUBLIC -- Returns a directory containing the data for the record with the requested -- key (dataKey). expose recordCount columns use strict arg dataKey if dataKey = "" then return .false --say "GenericFile-getRecord-01: dataKey=" dataKey if recordCount = 0 then do --say "GenericFile-getRecord: recordCount is zero - no records!" return .false -- File empty or does not exist end found = .false do recordNo = 1 to recordCount --say "GenericFile-getRecord-02: self~fileRecords dims:" self~fileRecords~dimension recordId = self~fileRecords[recordNo,1] --say "GenericFile-getRecord-03: recordId, dataKey = <"||recordId||">" "<"||dataKey||">" if recordId = dataKey then do found = .true; leave; end end if \found then return .false -- Records exist, but specified record not found. dirData = .Directory~new do j=1 to columns header = self~FileHeaders[j] dirData[header] = self~fileRecords[recordNo,j] end -- say "GenericFile-getRecord-04: Results:" -- do i over dirData -- say "'"||i||"' = '"||dirData[i]||"'" -- end --say "GenericFile-getRecord-05: record =" return dirData /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - getFile - returns file as a directory, with indices 'headers', 'records', 'count'. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ ::METHOD getFile PUBLIC --say "GenericFile-getFile-01: self~fileAsDirectory =" self~fileAsDirectory --do i over self~fileAsDirectory --say "GenericFile-getFile-02 index & type =" i i~objectName --end --arrData = self~fileAsDirectory[records] --say "GenericFile-getFile-03 - arrData =" arrData --say "GenericFile-getFile-04 - arrData[1,1] =" arrData[1,1] return self~fileAsDirectory /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/ ::METHOD update -- INCOMPLETE!!! -- Given a record as a Directory, replaces the existing record with the -- new data and writes the whole file to disk. -- Returns .false (a) if a record with the provided key does not exist, or -- (b) if values for all fields are not provided. expose recordCount columns fileArray fileName use arg recordKey, dirRecord -- record key + new record -- Find the record: do i=2 to recordCount record = fileArray[i] parse var record recordId . -- assume recordId is first column if recordId = recordKey then do found = .true; leave; end end if \found then return .false -- Records exist, but specified record not found. -- found record is i'th. Now replace that array item with the new data: currentRecord = fileArray[i] ---- This method is only opartially completed. !!!!!!!!!!!! newrecord = "" do j=1 to columns --fileArray[i]field = end /*============================================================================*/