Use cFilesystem\cFilesystem.pkg Use tsTextFileStructure.pkg Use Defines.pkg Use Flexml.pkg Use cHexHandler.pkg // ToDo: Give example for filling the struct. Class cTextFile is a cFilesystem Procedure Construct_Object Forward Send Construct_Object // Fill this property with the text file structure before opening the file with OpenTextFile. Property tsTextFileStructure plsTextFileStructure // Increments for each successfull NextTextRow read. // When OpenTextFile is called this is set to 0. Property Integer piRowsRead 0 {DesignTime = False} Property Integer piFilenumber -1 // Use own error handler during data formatting since Trap_all will delete all previous ignore_error settings. // If the class used a combination of Ignore_all/Trap_all for the current error object // it would override any ignore_error settings that are set outside the class. {Visibility = Private} Property Handle phOldErrorObjectId 0 Property Integer Error_Processing_State False // Must be public available. End_Procedure Procedure Set psFileName String sFilename tsTextFileStructure lsTextFileStructure Get plsTextFileStructure to lsTextFileStructure Move sFilename to lsTextFileStructure.sFilename Set plsTextFileStructure to lsTextFileStructure End_Procedure Function psFileName Returns String String sFilename tsTextFileStructure lsTextFileStructure Get plsTextFileStructure to lsTextFileStructure Function_Return lsTextFileStructure.sFilename End_Function Function OpenTextFile Returns Boolean Boolean bOk Integer iFilenumber String sFileName Get piFilenumber to iFilenumber If (iFilenumber <> -1) Begin Error DFERR_PROGRAM "Text file already opened." Function_Return False End Get psFileName to sFileName Get BinaryFileNextFilenumber to iFilenumber Get BinaryFileOpen iFilenumber sFileName to bOk If bOk Begin Set piFilenumber to iFilenumber Set piRowsRead to 0 End Function_Return bOk End_Function // If the file is already closed it returns false and no error message. Function CloseTextFile Returns Boolean Boolean bOk Integer iFilenumber Get piFilenumber to iFilenumber If (iFilenumber <> -1) Begin Get BinaryFileClose iFilenumber to bOk Set piFilenumber to -1 End Function_Return bOk End_Function // Returns the size of the text file. Function TextFileSize Returns BigInt Integer iFilenumber BigInt biFileSize Get piFilenumber to iFilenumber If (iFilenumber <> -1) Begin Get BinaryFileSize iFilenumber to biFileSize End Function_Return biFileSize End_Function // Returns the current cached fileposition of the text file. Function TextFilePosition Returns BigInt Integer iFilenumber BigInt biFilePosition Get piFilenumber to iFilenumber If (iFilenumber <> -1) Begin Get BinaryFileCachedPosition iFilenumber to biFilePosition End Function_Return biFilePosition End_Function // Get the next row to sRow array // Returns true if data could be loaded. Function NextTextRow String[] ByRef sRow Boolean ByRef bEndOfFile Returns Boolean tsTextFileStructure lsTextFileStructure Integer iColumns iCurrentColumn iRowsRead Boolean bOk bStop bEndOfRow String sRowData Get plsTextFileStructure to lsTextFileStructure If (lsTextFileStructure.iFormat = C_TEXTFORMAT_FIXEDLENGTH) Begin Move (SizeOfArray(lsTextFileStructure.lsField)) to iColumns End Else Begin Move 0 to iColumns // Columns are build from the first row in the text file. End Move 0 to iCurrentColumn Move "" to sRowData Move (ResizeArray(sRow, 0)) to sRow If (lsTextFileStructure.iFormat = C_TEXTFORMAT_FIXEDLENGTH) Begin Get TextColumnsFixed (&sRow) (&bEndOfRow) (&bEndOfFile) to bOk End Else Begin Repeat Get NextTextColumnCSV iCurrentColumn (&sRow[iCurrentColumn]) (&bEndOfRow) (&bEndOfFile) to bOk If (not(bOk)) Begin Move True to bStop End Else Begin Increment iCurrentColumn End Until (iCurrentColumn = iColumns or bStop = True or bEndOfRow = True or bEndOfFile = True) End If bOk Begin Get piRowsRead to iRowsRead Increment iRowsRead Set piRowsRead to iRowsRead End Function_Return bOk End_Function // Read the next column data from a CSV file. // Returns true if data could be loaded. {Visibility = Private} Function NextTextColumnCSV Integer iColumn String ByRef sData Boolean ByRef bEndOfRow Boolean ByRef bEndOfFile Returns Boolean Integer iFilenumber iBytesRead Boolean bOk bDataReady bCancel tsTextFileStructure lsTextFileStructure Move False to bDataReady Move False to bOk Move False to bEndOfRow Move False to bCancel Get piFilenumber to iFilenumber If (iFilenumber = -1) Begin Error DFERR_PROGRAM "Text file not opened." End Else Begin Get plsTextFileStructure to lsTextFileStructure Get BinaryFileReadCachedCSV iFilenumber lsTextFileStructure.sFieldSeparator lsTextFileStructure.sTextQualification (&sData) (&bEndOfRow) (&bEndOfFile) lsTextFileStructure.sRowSeparator to bDataReady If bDataReady Begin Send OnFormatData iColumn (&sData) (&bCancel) Move (not(bCancel)) to bOk End End Function_Return bOk End_Function // Read the next row from a fixed length file and split data to columns. // Returns true if data could be loaded. {Visibility = Private} Function TextColumnsFixed String[] ByRef sRow Boolean ByRef bEndOfRow Boolean ByRef bEndOfFile Returns Boolean Integer iFilenumber iBytesRead iColumns iColumn Boolean bDataReady bCancel tsTextFileStructure lsTextFileStructure String sRowData Move False to bDataReady Move False to bEndOfRow Move False to bCancel Get piFilenumber to iFilenumber If (iFilenumber = -1) Begin Error DFERR_PROGRAM "Text file not opened." End Else Begin Get plsTextFileStructure to lsTextFileStructure Move "" to sRowData Get BinaryFileReadCachedLN iFilenumber (&sRowData) (&bEndOfFile) lsTextFileStructure.sRowSeparator to bEndOfRow Move (SizeOfArray(lsTextFileStructure.lsField)) to iColumns If (iColumns > 0) Begin Move 0 to iColumn Repeat Move (Mid(sRowData, Integer(lsTextFileStructure.lsField[iColumn].nLength), lsTextFileStructure.lsField[iColumn].iStart)) to sRow[iColumn] Send OnFormatData iColumn (&srow[iColumn]) (&bCancel) Increment iColumn Until (iColumn = iColumns or bCancel = True) If (iColumn = iColumns) Begin Move True to bEndOfRow End Move (not(bCancel)) to bDataReady End End Function_Return bDataReady End_Function // This event is called for each time a column data has been read. // It controls the normal formatting of the data. // Set bCancel to true in order to stop reading from the textfile. {MethodType = Event} Procedure OnFormatData Integer iColumn String ByRef sData Boolean ByRef bCancel tsTextFileStructure lsTextFileStructure Integer iRowsRead Get plsTextFileStructure to lsTextFileStructure If (lsTextFileStructure.iCodePage = C_TEXTCODEPAGE_ANSI) Begin Move (ToOEM(sData)) to sData End Get piRowsRead to iRowsRead If (not(lsTextFileStructure.bFirstRowHasFieldNames = True and iRowsRead = 0)) Begin If (iColumn < SizeOfArray(lsTextFileStructure.lsField)) Begin Set CollectErrors to True Case Begin Case (lsTextFileStructure.lsField[iColumn].iType = C_TEXTFIELDTYPE_NUMERIC) Send textFormatNumeric (&sData) lsTextFileStructure.sDecimalSign Case Break Case (lsTextFileStructure.lsField[iColumn].iType = C_TEXTFIELDTYPE_DATE) Send TextFormatDate (&sData) lsTextFileStructure.sDateFormat lsTextFileStructure.sDateSeparator lsTextFileStructure.bFourDigitYear Case Break Case End Set CollectErrors to False End End End_Procedure {Visibility = Private} Procedure TextFormatNumeric String ByRef sData String sDecimalSign Integer iLocalDecimalSeparator Get_Attribute DF_DECIMAL_SEPARATOR to iLocalDecimalSeparator Move (Number(Replace(sDecimalSign, sData, (Character(iLocalDecimalSeparator))))) to sData End_Procedure {Visibility = Private} Function ExtractPartDate String sDate String sDateFormat String sPartType Returns Integer Integer iPos iValue Move (Pos(sPartType, sDateFormat)) to iPos Move (Mid(sDate, Length(sPartType), iPos)) to iValue Function_Return iValue End_Function {Visibility = Private} Procedure TextFormatDate String ByRef sData String sFormat String sDateSeparator Boolean bFourDigitYear Integer iDataPos iFormatPos iDatePart iEpoch iCount iNewDataPos iNewFormatPos iDataPartLength iFormatPartLength Integer iDateSeparatorLength DateTime dtDate String sDatePart sFormatPart Move 1 to iDataPos Move 1 to iFormatPos Move (Length(sDateSeparator)) to iDateSeparatorLength For iCount from 1 to 3 Move (Pos(sDateSeparator, sData, iDataPos)) to iNewDataPos Move (Pos(sDateSeparator, sFormat, iFormatPos)) to iNewFormatPos If (iNewDataPos = 0) Begin Move (Length(sData) + 1) to iNewDataPos End If (iNewFormatPos = 0) Begin Move (Length(sFormat) + 1) to iNewFormatPos End Move (iNewDataPos - iDataPos) to iDataPartLength Move (iNewFormatPos - iFormatPos) to iFormatPartLength Move (Mid(sData, iDataPartLength, iDataPos)) to sDatePart If (StringConsistsOf(sDatePart,"0123456789")) Begin Move (Mid(sFormat, iFormatPartLength, iFormatPos)) to sFormatPart Move sDatePart to iDatePart Case Begin Case (sFormatPart = "DD") Move (DateSetDay(dtDate, iDatePart)) to dtDate Case Break Case (sFormatPart = "MM") Move (DateSetMonth(dtDate, iDatePart)) to dtDate Case Break Case (sFormatPart = "YY" or sFormatPart = "YYYY") Move (DateSetYear(dtDate, iDatePart)) to dtDate // Two-digit year is okay. Case Break Case End End Else Begin Move "" to sData Procedure_Return End Move (iNewDataPos + iDateSeparatorLength) to iDataPos Move (iNewFormatPos + iDateSeparatorLength) to iFormatPos Loop If (IsDateValid(dtDate)) Begin Move (Date(dtDate)) to sData // Two-digit year is converted by the runtime. End Else Begin Move "" to sData End End_Procedure {Visibility = Private} Procedure Destroy_Object Boolean bOk Get CloseTextFile to bOk Forward Send Destroy_Object End_Procedure {Visibility = Private} Function CompareTsTextFileField tsTextFileField lsA tsTextFileField lsB Returns Integer Move (Uppercase(lsA.sName)) to lsA.sName Move (Uppercase(lsB.sName)) to lsB.sName If (lsA.sName > lsB.sName) Function_Return (GT) If (lsA.sName < lsB.sName) Function_Return (LT) Function_Return (EQ) End_Function // Returns the text column number for the text column name. // The function is not case sensitive. // Returns -1 if the text column is not found. Function TextColumnNumber String sTextColumnName Returns Integer tsTextFileStructure lsTextFileStructure tsTextFileField lsSearchTextFileField Integer iResult Get plsTextFileStructure to lsTextFileStructure Move sTextColumnName to lsSearchTextFileField.sName Move (SearchArray(lsSearchTextFileField, lsTextFileStructure.lsField, Self, (RefFunc(CompareTsTextFileField)))) to iResult Function_Return iResult End_Function // During formatting of data runtime errors are redirected here. {Visibility = Private} Procedure Error_Report Integer iErrNum Integer iErrLine String sErrText If (Error_Processing_State(Self)) Procedure_Return Set Error_Processing_State to True // Just ignore all errors for now. Set Error_Processing_State to False End_Procedure // Set this property to true if runtime error are to be redirected to the object of this class. Procedure Set CollectErrors Boolean bCollectErrors Handle hOldErrorId Get phOldErrorObjectId to hOldErrorId If bCollectErrors Begin If (hOldErrorId <> 0) Begin Procedure_Return End Set phOldErrorObjectId to Error_Object_Id Move (Self) to Error_Object_Id End Else Begin If (hOldErrorId = 0) Begin Procedure_Return End Move hOldErrorId to Error_Object_Id Set phOldErrorObjectId to 0 End End_Procedure // Saves the plsFilestructure in a XML file. // Returns true if the save was successfull. Function SaveTextFileStructure String sFilestructureFile Returns Boolean Integer iSaveError iFields iField Handle hXMLDoc hRoot hFields hField hoNodeToInsert hoInsertedNode tsTextFileStructure lsTextFileStructure String sNS Move C_TextFileStructureNameSpace to sNS Get Create (RefClass(cXMLDOMDocument)) to hXMLDoc Set psDocumentName of hXMLDoc to sFilestructureFile Get CreateDocumentElementNS of hXMLDoc sNS "TextFilestructure" to hRoot Get CreateChildProcessingInstruction of hRoot "xml" 'version="1.0" encoding="UTF-8" standalone="yes"' to hoNodeToInsert If (hoNodeToInsert > 0) Begin Get InsertBeforeNode of hXMLDoc hoNodeToInsert hRoot to hoInsertedNode If ((hoInsertedNode <> hoNodeToInsert) and (hoInsertedNode > 0)) Begin Send Destroy of hoInsertedNode End Send Destroy of hoNodeToInsert End Get plsTextFileStructure to lsTextFileStructure Send SerializeTextFilestructure (&lsTextFileStructure) Send AddElementNS of hRoot sNS "Importfile" lsTextFileStructure.sFilename Send AddElementNS of hRoot sNS "Format" lsTextFileStructure.iFormat Send AddElementNS of hRoot sNS "RowSeparator" lsTextFileStructure.sRowSeparator Send AddElementNS of hRoot sNS "FieldSeparator" lsTextFileStructure.sFieldSeparator Send AddElementNS of hRoot sNS "TextQualifikation" lsTextFileStructure.sTextQualification Send AddElementNS of hRoot sNS "CodepageMode" lsTextFileStructure.iCodePage Send AddElementNS of hRoot sNS "FirstRowHasFieldnames" lsTextFileStructure.bFirstRowHasFieldNames Send AddElementNS of hRoot sNS "DateFormat" lsTextFileStructure.sDateFormat Send AddElementNS of hRoot sNS "DateSeparator" lsTextFileStructure.sDateSeparator Send AddElementNS of hRoot sNS "FourDigitYear" lsTextFileStructure.bFourDigitYear Send AddElementNS of hRoot sNS "DecimalSign" lsTextFileStructure.sDecimalSign Get AddElementNS of hRoot sNS "TextFields" "" to hFields Move (SizeOfArray(lsTextFileStructure.lsField)) to iFields For iField from 0 to (iFields - 1) Get AddElementNS of hFields sNS "Field" "" to hField Send AddElementNS of hField sNS "Name" lsTextFileStructure.lsField[iField].sName Send AddElementNS of hField sNS "Type" lsTextFileStructure.lsField[iField].iType Send AddElementNS of hField sNS "Start" lsTextFileStructure.lsField[iField].iStart Send AddElementNS of hField sNS "Length" lsTextFileStructure.lsField[iField].nLength Send AddElementNS of hField sNS "Skip" lsTextFileStructure.lsField[iField].bSkip Send Destroy of hField Loop Send Destroy of hFields Send Destroy of hRoot Get SaveXMLDocument of hXMLDoc to iSaveError Send Destroy of hXMLDoc Function_Return (iSaveError = 0) End_Function // Loads the XML file into the plsFilestructure property. // Returns true if the load was successfull. Function LoadTextFileStructure String sFilestructureFile Returns Boolean Integer iFields iField Handle hXMLDoc hRoot hFields hField tsTextFileStructure lsTextFileStructure String sNS sRootname Boolean bLoadOk Move C_TextFileStructureNameSpace to sNS Get Create (RefClass(cXMLDOMDocument)) to hXMLDoc Set psDocumentName of hXMLDoc to sFilestructureFile Get LoadXMLDocument of hXMLDoc to bLoadOk If bLoadOk Begin Get DocumentElement of hXMLDoc to hRoot Set psSelectionNamespaces of hRoot to (SFormat("xmlns:atd='%1'", sNS)) Get psBaseName of hRoot to sRootname If (sRootname = "TextFilestructure") Begin Get ChildElementValueNS of hRoot sNS "Importfile" to lsTextFileStructure.sFilename Get ChildElementValueNS of hRoot sNS "Format" to lsTextFileStructure.iFormat Get ChildElementValueNS of hRoot sNS "RowSeparator" to lsTextFileStructure.sRowSeparator Get ChildElementValueNS of hRoot sNS "FieldSeparator" to lsTextFileStructure.sFieldSeparator Get ChildElementValueNS of hRoot sNS "TextQualifikation" to lsTextFileStructure.sTextQualification Get ChildElementValueNS of hRoot sNS "CodepageMode" to lsTextFileStructure.iCodePage Get ChildElementValueNS of hRoot sNS "FirstRowHasFieldnames" to lsTextFileStructure.bFirstRowHasFieldNames Get ChildElementValueNS of hRoot sNS "DateFormat" to lsTextFileStructure.sDateFormat Get ChildElementValueNS of hRoot sNS "DateSeparator" to lsTextFileStructure.sDateSeparator Get ChildElementValueNS of hRoot sNS "FourDigitYear" to lsTextFileStructure.bFourDigitYear Get ChildElementValueNS of hRoot sNS "DecimalSign" to lsTextFileStructure.sDecimalSign Get ChildElementNS of hRoot sNS "TextFields" to hFields If (hFields <> 0) Begin Move 0 to iField Get ChildElementNS of hFields sNS "Field" to hField While (hField <> 0) Get ChildElementValueNS of hField sNS "Name" to lsTextFileStructure.lsField[iField].sName Get ChildElementValueNS of hField sNS "Type" to lsTextFileStructure.lsField[iField].iType Get ChildElementValueNS of hField sNS "Start" to lsTextFileStructure.lsField[iField].iStart Get ChildElementValueNS of hField sNS "Length" to lsTextFileStructure.lsField[iField].nLength Get ChildElementValueNS of hField sNS "Skip" to lsTextFileStructure.lsField[iField].bSkip Get NextElementNS of hField sNS "Field" to hField Increment iField Loop Send Destroy of hFields End Send DeSerializeTextFilestructure (&lsTextFileStructure) End Else Begin Send UserError (SFormat("%1 is not a valid textfilestructure.", sFilestructureFile)) "" Move False to bLoadOk End Send Destroy of hRoot End Else Begin Send UserError (SFormat("%1 is not a valid xml file.", sFilestructureFile)) "" Move False to bLoadOk End Send Destroy of hXMLDoc Set plsTextFileStructure to lsTextFileStructure Function_Return bLoadOk End_Function // Turns strings to Hex values. {Visibility = Private} Procedure SerializeTextFilestructure tsTextFileStructure ByRef lsTextFilestructure Handle hHexHandler Integer iFields iField Get Create (RefClass(cHexHandler)) to hHexHandler Get StrToHex of hHexHandler lsTextFilestructure.sFilename to lsTextFilestructure.sFilename Get StrToHex of hHexHandler lsTextFilestructure.sRowSeparator to lsTextFilestructure.sRowSeparator Get StrToHex of hHexHandler lsTextFilestructure.sFieldSeparator to lsTextFilestructure.sFieldSeparator Get StrToHex of hHexHandler lsTextFilestructure.sTextQualification to lsTextFilestructure.sTextQualification Get StrToHex of hHexHandler lsTextFilestructure.sDateFormat to lsTextFilestructure.sDateFormat Get StrToHex of hHexHandler lsTextFilestructure.sDateSeparator to lsTextFilestructure.sDateSeparator Get StrToHex of hHexHandler lsTextFilestructure.sDecimalSign to lsTextFilestructure.sDecimalSign Move (SizeOfArray(lsTextFilestructure.lsField)) to iFields For iField from 0 to (iFields - 1) Get StrToHex of hHexHandler lsTextFilestructure.lsField[iField].sName to lsTextFilestructure.lsField[iField].sName Loop End_Procedure // Turns Hex values to string. {Visibility = Private} Procedure DeSerializeTextFilestructure tsTextFileStructure ByRef lsTextFilestructure Handle hHexHandler Integer iFields iField Get Create (RefClass(cHexHandler)) to hHexHandler Get HexToStr of hHexHandler lsTextFilestructure.sFilename to lsTextFilestructure.sFilename Get HexToStr of hHexHandler lsTextFilestructure.sRowSeparator to lsTextFilestructure.sRowSeparator Get HexToStr of hHexHandler lsTextFilestructure.sFieldSeparator to lsTextFilestructure.sFieldSeparator Get HexToStr of hHexHandler lsTextFilestructure.sTextQualification to lsTextFilestructure.sTextQualification Get HexToStr of hHexHandler lsTextFilestructure.sDateFormat to lsTextFilestructure.sDateFormat Get HexToStr of hHexHandler lsTextFilestructure.sDateSeparator to lsTextFilestructure.sDateSeparator Get HexToStr of hHexHandler lsTextFilestructure.sDecimalSign to lsTextFilestructure.sDecimalSign Move (SizeOfArray(lsTextFilestructure.lsField)) to iFields For iField from 0 to (iFields - 1) Get HexToStr of hHexHandler lsTextFilestructure.lsField[iField].sName to lsTextFilestructure.lsField[iField].sName Loop End_Procedure End_Class