<commit_before>//
// Copyright (c) XSharp B.V.  All Rights Reserved.  
// Licensed under the Apache License, Version 2.0.  
// See License.txt in the project root for license information.
//

USING System.IO
USING XSharp.RDD.Enums
USING System.Collections.Generic
USING System.Linq
USING System.Runtime.CompilerServices
USING XSharp.RDD.Enums
USING XSharp.RDD.Support
BEGIN NAMESPACE XSharp.RDD
    /// <summary>Class for DBF Column reading / writing </summary>
    CLASS DbfColumn INHERIT RddFieldInfo
        INTERNAL NullBit        := -1 AS LONG
        INTERNAL LengthBit      := -1 AS LONG
        INTERNAL OffSetInHeader := -1 as LONG
        INTERNAL RDD        AS XSharp.RDD.DBF

        /// <summary>Create a DbfColumn class, used when creating files.</summary>
        STATIC METHOD Create(oInfo AS RddFieldInfo, oRDD AS XSharp.RDD.DBF) AS DbfColumn
            // Commented out types are Harbour specific
            SWITCH oInfo:FieldType
            CASE DbFieldType.Character   // C
            CASE DbFieldType.VarChar     // 'V'
            CASE DbFieldType.VarBinary   // 'Q'
                RETURN DbfCharacterColumn{oInfo,oRDD}
            CASE DbFieldType.Date        // D
                RETURN DbfDateColumn{oInfo,oRDD}
            CASE DbFieldType.Number      // N
            CASE DbFieldType.Float          // 'F'
                RETURN DbfNumericColumn{oInfo,oRDD}
            CASE DbFieldType.Memo        // M
            CASE DbFieldType.Picture        // 'P'
            CASE DbFieldType.General        // 'G'
            CASE DbFieldType.Blob           // 'W'
                RETURN DbfMemoColumn{oInfo,oRDD}
            CASE DbFieldType.Logic       // L
                RETURN DbfLogicColumn{oInfo,oRDD}
            CASE DbFieldType.Integer    // 'I' 
            //CASE DbFieldType.Integer2   // '2' 
            //CASE DbFieldType.Integer4   // '4'
                IF oInfo:IsAutoIncrement
                    RETURN DbfAutoIncrementColumn{oInfo,oRDD}
                ENDIF
                RETURN DbfIntegerColumn{oInfo,oRDD}
            //CASE DbFieldType.AutoIncrement
            //   RETURN DbfAutoIncrementColumn{oInfo}
            CASE DbFieldType.Double         // 'B'
            //CASE DbFieldType.Double8        // '8'
                RETURN DbfDoubleColumn{oInfo,oRDD}
            CASE DbFieldType.Currency		// 'Y'
            //CASE DbFieldType.CurrencyDouble	// 'Z'
                RETURN DbfCurrencyColumn{oInfo,oRDD}
            //CASE DbFieldType.TimeStamp      // '@'
            //CASE DbFieldType.ModTime        // '='
            //CASE DbFieldType.RowVer         // '^'
            //    RETURN DbfTimeStampColumn{oInfo}
            CASE DbFieldType.DateTime      // 'T'
                RETURN DbfDateTimeColumn{oInfo,oRDD}
            CASE DbFieldType.NullFlags
                RETURN DbfNullColumn{oInfo,oRDD}
            CASE DbFieldType.VOObject       // 'O'
            CASE DbFieldType.Unknown        // 'O'
            OTHERWISE
                RETURN DbfColumn{oInfo,oRDD}
            END SWITCH

        /// <summary>Create a DbfColumn class, based on the definition in the DBF. Used when opening a file. </summary>
        STATIC METHOD Create(oField REF DbfField, oRDD AS XSharp.RDD.DBF, nHeaderOffSet AS LONG) AS DbfColumn
            LOCAL oInfo AS RddFieldInfo
            LOCAL oColumn as DbfColumn
            oInfo        := RddFieldInfo{oField:Name, oField:Type, oField:Len, oField:Dec, oField:Offset, oField:Flags}
            oColumn      := DbfColumn.Create(oInfo, oRDD)
            oColumn:OffSetInHeader := nHeaderOffSet
            IF oColumn is DbfAutoIncrementColumn VAR dbfac
                dbfac:IncrStep  := oField:IncStep
                dbfac:Counter   := oField:Counter
            ENDIF
            return oColumn



        PROTECTED CONSTRUCTOR(oInfo AS RddFieldInfo,oRDD AS XSharp.RDD.DBF)
            SUPER(oInfo)
            SELF:RDD        := oRDD
            RETURN

       /// <summary>Initialize the buffer used for appends. Gets called once when a DBF is opened.</summary>
       VIRTUAL METHOD InitValue(buffer AS BYTE[]) AS VOID
            // this gets called to update the initialize the buffer for 'append blank'. Gets called once when a DBF is opened.
            RETURN

       /// <summary>Update the buffer after appending. For example for AutoIncrement or TimeStamp columns.</summary>
       /// <param name="buffer">Record buffer for the current record</param>
       VIRTUAL METHOD NewRecord(buffer AS BYTE[]) AS VOID
            // this gets called to update the appended record. Only used at this moment to set the AutoIncrement value
            // but can also be used to set a timestamp or rowversion later
            RETURN

        PROTECTED METHOD _GetString(buffer AS BYTE[]) AS STRING
            // The default implementation returns the part of the buffer as a string
            RETURN SELF:RDD:_Encoding:GetString(buffer, SELF:Offset, SELF:Length)

        /// <summary>Get the value from the buffer</summary>
       /// <param name="buffer">Record buffer for the current record</param>
        VIRTUAL METHOD GetValue(buffer AS BYTE[]) AS OBJECT
            IF SELF:IsNull()
                RETURN NULL
            ENDIF
            RETURN SELF:_GetString(buffer)

        /// <summary>Handle NULL values.</summary>
       /// <param name="buffer">Record buffer for the current record</param>
        VIRTUAL METHOD HandleNullValue(buffer AS BYTE[]) AS LOGIC 
            SELF:InitValue(buffer)                      
            IF SELF:IsNullable
                RETURN SELF:SetNullValue()
            ENDIF
            RETURN TRUE

       /// <summary>Write the value to the buffer</summary>
       /// <param name="buffer">Record buffer for the current record</param>
       /// <param name="oValue">Value to write.</param>
        VIRTUAL METHOD PutValue(oValue AS OBJECT, buffer AS BYTE[]) AS LOGIC
            RETURN FALSE

        [MethodImpl(MethodImplOptions.AggressiveInlining)];
         INTERNAL METHOD IsNull() AS LOGIC
            IF SELF:IsNullable .AND. SELF:RDD:_NullColumn != NULL
                RETURN SELF:RDD:_NullColumn:GetBit(SELF:NullBit)
            ENDIF
            RETURN FALSE

        [MethodImpl(MethodImplOptions.AggressiveInlining)];
        PROTECTED METHOD SetNullValue() AS LOGIC 
            IF SELF:IsNullable .AND. SELF:RDD:_NullColumn != NULL
                SELF:RDD:_NullColumn:SetBit(SELF:NullBit, TRUE)
                RETURN TRUE
            ENDIF
            RETURN FALSE

         [MethodImpl(MethodImplOptions.AggressiveInlining)];
         PROTECTED METHOD ClearNullValue() AS VOID
            IF SELF:IsNullable .AND. SELF:RDD:_NullColumn != NULL
                SELF:RDD:_NullColumn:SetBit(SELF:NullBit, FALSE)
            ENDIF
            RETURN 

        /// <summary>Get the default "empty" value, as you would get at EOF</summary>
        [MethodImpl(MethodImplOptions.AggressiveInlining)];
        VIRTUAL METHOD EmptyValue() AS OBJECT
            RETURN String.Empty

        [MethodImpl(MethodImplOptions.AggressiveInlining)];
        PROTECTED METHOD _GetNumericValue(buffer AS BYTE[]) AS INT64
            LOCAL lValid := TRUE AS LOGIC
            LOCAL nValue AS INT64
            LOCAL nPos, nLen AS LONG
            nValue := 0
            nLen := SELF:Length
            nPos := 0
            IF SELF:Offset+nLen >= buffer:Length
                NOP
            ENDIF
            DO WHILE nPos < nLen .AND. buffer[SELF:Offset+nPos] == 32 
                nPos++
            ENDDO
            FOR VAR i := nPos TO nLen -1
                LOCAL b AS BYTE
                b := buffer[SELF:Offset+i]
                SWITCH b
                CASE 48  // 0
                CASE 49  // 1
                CASE 50  // 2
                CASE 51  // 3
                CASE 52  // 4
                CASE 53  // 5
                CASE 54  // 6
                CASE 55  // 7
                CASE 56  // 8
                CASE 57  // 9
                    nValue := nValue * 10 + (b - 48)
                OTHERWISE
                    lValid := FALSE        
                END SWITCH
                IF ! lValid
                    EXIT
                ENDIF
            NEXT
            IF lValid
                RETURN nValue
            ENDIF
            RETURN 0

         [MethodImpl(MethodImplOptions.AggressiveInlining)];
        PROTECTED METHOD GetNumber<T>(oValue AS OBJECT, oResult OUT T) AS LOGIC WHERE T IS NEW()
            LOCAL tc AS TypeCode
            IF oValue != NULL
                tc := Type.GetTypeCode(oValue:GetType())
                SWITCH tc
                CASE TypeCode.SByte
                CASE TypeCode.Int16
                CASE TypeCode.Int32
                CASE TypeCode.Int64
                CASE TypeCode.Byte
                CASE TypeCode.UInt16
                CASE TypeCode.UInt32
                CASE TypeCode.UInt64
                CASE TypeCode.Decimal
                CASE TypeCode.Double
                CASE TypeCode.Single
                    oResult := (T) Convert.ChangeType(oValue, typeof(T) )
                    RETURN TRUE
                CASE TypeCode.Object
                    oResult := (T) Convert.ChangeType((((IFloat) oValue):Value), typeof(T) )
                    RETURN TRUE
                END SWITCH
            ENDIF
            oResult :=  T{}
            RETURN FALSE

        PROTECTED METHOD _PutString(buffer AS BYTE[], strValue AS STRING) AS VOID
            SELF:RDD:_Encoding:GetBytes(strValue, 0, SELF:Length, buffer, SELF:Offset)

    END CLASS

    /// <summary>Class for reading / writing String Columns</summary>
    CLASS DbfCharacterColumn INHERIT DbfColumn

         CONSTRUCTOR(oInfo AS RddFieldInfo,oRDD AS XSharp.RDD.DBF)
            SUPER(oInfo, oRDD)
            IF SELF:Decimals != 0
                SELF:Length         += SELF:Decimals * 256
			    SELF:Decimals       := 0
            ENDIF
            RETURN
        /// <inheritdoc/>
       OVERRIDE METHOD InitValue(buffer AS BYTE[]) AS VOID
            IF SELF:IsVarLength 
                buffer[SELF:Offset + SELF:Length-1] := 0
            ELSEIF SELF:IsUnicode
                VAR str := STRING{' ',SELF:Length /2}
                System.Buffer.BlockCopy(str:ToCharArray(),0, buffer, SELF:Offset, SELF:Length)
            ENDIF
            RETURN
       /// <inheritdoc/>
       OVERRIDE METHOD EmptyValue() AS OBJECT
           IF SELF:IsUnicode
                RETURN STRING{ ' ', SELF:Length /2 }
           ENDIF
           RETURN STRING{ ' ', SELF:Length }
        /// <inheritdoc/>
       OVERRIDE METHOD PutValue(oValue AS OBJECT, buffer AS BYTE[]) AS LOGIC
            LOCAL str AS STRING
            IF oValue == NULL
                RETURN SELF:HandleNullValue(buffer)
            ELSEIF SELF:IsNullable
                SELF:ClearNullValue()
            ENDIF
            IF oValue IS CHAR
                str := ((CHAR) oValue):ToString()
            ELSEIF oValue IS STRING
                str := (STRING) oValue
            ELSE
                SELF:RDD:_dbfError(Subcodes.ERDD_DATATYPE, EG_DATATYPE)
                RETURN FALSE
            ENDIF
            IF str:Length < SELF:Length .AND. ! SELF:IsVarLength
                IF SELF:IsBinary
                    str := str:PadRight(SELF:Length,'\0')
                ELSEIF SELF:IsUnicode
                    str := str:PadRight(SELF:Length/2,' ')
                ELSE
                    str := str:PadRight(SELF:Length,' ')
                ENDIF
            ENDIF
            IF SELF:IsBinary
                LOCAL nLen := Math.Min(SELF:Length, str:Length) AS LONG
                System.Text.Encoding.Default:GetBytes(str, 0, nLen, buffer, SELF:Offset)
            ELSEIF SELF:IsUnicode
                System.Buffer.BlockCopy(str:ToCharArray(),0, buffer, SELF:Offset, SELF:Length)
            ELSE
                SELF:_PutString(buffer, str)
            ENDIF
            IF SELF:IsVarLength
                IF SELF:RDD:_NullColumn != NULL
                    IF str:Length >= SELF:Length
                        // we used all the bytes, so set the varlength bit to 0
                        SELF:RDD:_NullColumn:SetBit(SELF:LengthBit, FALSE)
                    ELSE
                        SELF:RDD:_NullColumn:SetBit(SELF:LengthBit, TRUE)
                        buffer[SELF:Offset + SELF:Length-1] := (BYTE) str:Length
                    ENDIF
                ELSE
                    buffer[SELF:Offset + SELF:Length-1] := (BYTE) Math.Max(str:Length, SELF:Length-1)
                ENDIF
            ENDIF
            RETURN TRUE

        /// <inheritdoc/>
        OVERRIDE METHOD Validate() AS LOGIC
            IF SELF:Length == 0  .OR. SELF:Decimals > 0  .OR. SELF:Length > System.UInt16.MaxValue 
                RETURN FALSE
            ENDIF
            RETURN TRUE
        /// <inheritdoc/>
        OVERRIDE METHOD GetValue(buffer AS BYTE[]) AS OBJECT
            LOCAL result AS STRING
            IF SELF:IsNull()
                RETURN NULL
            ENDIF
            IF SELF:IsBinary
                result := System.Text.Encoding.Default:GetString(buffer, SELF:Offset, SELF:Length)
            ELSEIF SELF:IsUnicode
                VAR chars := CHAR[]{SELF:Length /2}
                System.Buffer.BlockCopy(buffer, SELF:Offset, chars, 0, SELF:Length)
                result := STRING{chars}
            ELSE
                result := SUPER:_GetString(buffer)
            ENDIF
            IF IsVarLength
                LOCAL len AS LONG
                len := SELF:Length
                IF SELF:RDD:_NullColumn != NULL
                    LOCAL lSet AS LOGIC
                    lSet := SELF:RDD:_NullColumn:GetBit(SELF:LengthBit)
                    IF lSet
                        len := buffer[SELF:Offset + SELF:Length-1]
                    ENDIF
                ENDIF
                IF !String.IsNullOrEmpty(result)
                    result := result:Substring(0, len)
                ENDIF
            ELSE
                IF String.IsNullOrEmpty(result)
                    result := STRING{ ' ', SELF:Length }
                ENDIF
            ENDIF
            RETURN result


    END CLASS
    /// <summary>Class for reading / writing Date Columns</summary>
    CLASS DbfDateColumn INHERIT DbfColumn

        CONSTRUCTOR(oInfo AS RddFieldInfo,oRDD AS XSharp.RDD.DBF)
            SUPER(oInfo,oRDD)
            RETURN
        /// <inheritdoc/>
        OVERRIDE METHOD GetValue(buffer AS BYTE[]) AS OBJECT
            LOCAL result := NULL AS IDate
            IF SELF:IsNull()
                RETURN NULL
            ENDIF
            LOCAL nValue := SELF:_GetNumericValue(buffer) AS INT64
            LOCAL lOk := FALSE AS LOGIC
            IF nValue != 0
                LOCAL nDay   := (LONG) nValue % 100 AS INT
                nValue /= 100
                LOCAL nMonth  := (LONG) nValue % 100 AS INT
                nValue /= 100
                LOCAL nYear   := (LONG) nValue AS INT
                IF nYear > 0 .AND. nDay > 0 .AND. nMonth > 0
                    result := DbDate{nYear,nMonth,nDay}
                    lOk := TRUE
                ENDIF
            ENDIF
            IF ! lOk
                result := DbDate{0,0,0}
            ENDIF
            RETURN result
        /// <inheritdoc/>
        OVERRIDE METHOD PutValue(oValue AS OBJECT, buffer AS BYTE[]) AS LOGIC
            IF oValue == NULL
                RETURN SELF:HandleNullValue(buffer)
            ELSEIF SELF:IsNullable
                SELF:ClearNullValue()
            ENDIF
            IF oValue IS System.DateTime VAR dt
                IF dt == DateTime.MinValue
                    oValue := DbDate{0,0,0}
                ELSE
                    oValue := DbDate{dt:Year, dt:Month, dt:Day}
                ENDIF
            ENDIF
            IF oValue IS IDate VAR dValue
                LOCAL str AS STRING
                IF dValue:IsEmpty
                    str := Space(8)
                ELSE
                    VAR dt := DateTime{dValue:Year, dValue:Month, dValue:Day}
                    str := dt:ToString( "yyyyMMdd" )
                ENDIF
                SELF:_PutString(buffer, str)
                RETURN TRUE
            ENDIF
            SELF:RDD:_dbfError(Subcodes.ERDD_DATATYPE, EG_DATATYPE)
            RETURN FALSE

        /// <inheritdoc/>

        OVERRIDE METHOD EmptyValue() AS OBJECT
            RETURN DbDate{0,0,0}
        /// <inheritdoc/>
        OVERRIDE METHOD Validate() AS LOGIC
            RETURN SUPER:Validate()


    END CLASS

    /// <summary>Class for reading / writing Logic Columns</summary>
    CLASS DbfLogicColumn INHERIT DbfColumn

        CONSTRUCTOR(oInfo AS RddFieldInfo,oRDD AS XSharp.RDD.DBF)
            SUPER(oInfo,oRDD)
            RETURN

        /// <inheritdoc/>
        OVERRIDE METHOD GetValue(buffer AS BYTE[]) AS OBJECT
            LOCAL result AS LOGIC
            IF SELF:IsNull()
                RETURN NULL
            ENDIF
            SWITCH buffer[SELF:Offset]
            CASE 84 // T
            CASE 116 // t
            CASE 89 // Y
            CASE 121 // y
                result := TRUE
            OTHERWISE
                result := FALSE
            END SWITCH
            RETURN result
            
        /// <inheritdoc/>
       OVERRIDE METHOD PutValue(oValue AS OBJECT, buffer AS BYTE[]) AS LOGIC
            IF oValue == NULL
                RETURN SELF:HandleNullValue(buffer)
            ELSEIF SELF:IsNullable
                SELF:ClearNullValue()
            ENDIF
            IF oValue IS LOGIC VAR logicValue
                buffer[SELF:Offset] := IIF( logicValue, (BYTE)'T', (BYTE)'F' )
                RETURN TRUE
            ENDIF
            SELF:RDD:_dbfError(Subcodes.ERDD_DATATYPE, EG_DATATYPE)
            RETURN FALSE
        /// <inheritdoc/>
       OVERRIDE METHOD EmptyValue() AS OBJECT
            RETURN FALSE
        /// <inheritdoc/>
       OVERRIDE METHOD Validate() AS LOGIC
            RETURN SUPER:Validate()

    END CLASS

    /// <summary>Class for reading / writing Numeric Columns</summary>
    CLASS DbfNumericColumn INHERIT DbfColumn

    CONSTRUCTOR(oInfo AS RddFieldInfo,oRDD AS XSharp.RDD.DBF)
            SUPER(oInfo,oRDD)
            RETURN
        /// <inheritdoc/>
    OVERRIDE METHOD GetValue(buffer AS BYTE[]) AS OBJECT
            LOCAL result AS IFloat
            LOCAL r8 AS REAL8
            IF SELF:IsNull()
                RETURN NULL
            ENDIF
            LOCAL nPos, nLen AS LONG
            nLen := SELF:Length
            nPos := 0
            DO WHILE  nPos < nLen .AND. buffer[SELF:Offset+nPos] == 32 
                nPos++
            ENDDO
            LOCAL lDec := FALSE AS LOGIC
            LOCAL lValid := TRUE AS LOGIC
            LOCAL nBefore, nCount, nDec AS INT64
            LOCAL lNegative := FALSE AS LOGIC
            LOCAL lFirst    := TRUE AS LOGIC
            LOCAL lLast     := FALSE AS LOGIC
            nBefore :=  nCount := nDec:= 0 
            FOR VAR i := nPos TO nLen -1
                LOCAL b AS BYTE
                b := buffer[SELF:Offset+i]
                IF lLast .AND. b != 32
                    lValid := FALSE
                    EXIT
                ENDIF
                SWITCH b
                CASE 43  // +
                    IF ! lFirst             // in the middle is invalid
                        lValid := FALSE
                    ELSE
                        lNegative := FALSE
                    ENDIF
                CASE 45  // -
                    IF ! lFirst             // in the middle is invalid
                        lValid := FALSE
                    ELSE
                        lNegative := TRUE
                    ENDIF
                CASE 48  // 0
                CASE 49  // 1
                CASE 50  // 2
                CASE 51  // 3
                CASE 52  // 4
                CASE 53  // 5
                CASE 54  // 6
                CASE 55  // 7
                CASE 56  // 8
                CASE 57  // 9 
                    nCount := nCount * 10 + (b - 48)
                    nDec   += 1
                CASE 46  // .
                    IF lDec // second Dot ?
                        lValid := FALSE
                    ELSE
                        nBefore := nCount
                        nCount  := 0
                        nDec    := 0
                        lDec    := TRUE
                    ENDIF
                CASE 32
                    lLast := TRUE       
                CASE 42  // *  Numeric overflow
                OTHERWISE
                    lValid := FALSE        
                END SWITCH
                IF ! lValid 
                    EXIT
                ENDIF
                lFirst := FALSE
            NEXT
            IF lValid
                IF lDec
                    r8 := nBefore + (nCount * 1.0 / 10 ^ nDec)
                ELSE
                    r8 := nCount
                ENDIF
                IF lNegative
                    r8 := -r8
                ENDIF
            ELSE
                 r8 := 0
            ENDIF
            result := DbFloat{r8, SELF:Length, SELF:Decimals}
            RETURN result
        /// <inheritdoc/>
      OVERRIDE METHOD PutValue(oValue AS OBJECT, buffer AS BYTE[]) AS LOGIC
            LOCAL r8Value AS REAL8
            IF oValue == NULL
                RETURN SELF:HandleNullValue(buffer)
            ELSEIF SELF:IsNullable
                SELF:ClearNullValue()
            ENDIF
            IF ! SELF:GetNumber(oValue, OUT r8Value)
                SELF:RDD:_dbfError(Subcodes.ERDD_DATATYPE, EG_DATATYPE)
                RETURN FALSE
            ENDIF
            VAR numformat := SELF:RDD:_numformat
            numformat:NumberDecimalDigits := SELF:Decimals
            LOCAL str AS STRING
            str := r8Value:ToString("F", numformat)
            LOCAL lDataWidthError := FALSE AS LOGIC
            IF str:Length > Length 
                str := STRING{'*', Length}
                lDataWidthError := TRUE
            ELSE
                str := str:PadLeft(Length,' ')
            ENDIF
            SELF:_PutString(buffer, str)
            IF lDataWidthError
                SELF:RDD:_dbfError(ERDD_DATAWIDTH, EG_DATAWIDTH)
            ENDIF
            RETURN TRUE
        /// <inheritdoc/>
        OVERRIDE METHOD EmptyValue() AS OBJECT
            RETURN DbFloat{0.0, SELF:Length, SELF:Decimals}
        /// <inheritdoc/>
       OVERRIDE METHOD Validate() AS LOGIC
           IF SELF:Length >= 1  .AND.  SELF:Length <= 255 
                IF SELF:Decimals > 0 
                    // We must check that we have enough space for DOT and decimal
                    IF SELF:Length <= 2  .OR.  SELF:Decimals >= SELF:Length -1 
                        RETURN FALSE
                    ENDIF
                ENDIF
            ELSE
                RETURN FALSE
            ENDIF
            RETURN TRUE


    END CLASS

    /// <summary>Class for reading / writing Memo Columns. This class returns and writes the block numbers.</summary>
    CLASS DbfMemoColumn INHERIT DbfColumn

        CONSTRUCTOR(oInfo AS RddFieldInfo,oRDD AS XSharp.RDD.DBF)
            SUPER(oInfo,oRDD)
            RETURN

        /// <inheritdoc/>
        /// <remarks>At this level this returns the memo block number.</remarks>
        OVERRIDE METHOD GetValue(buffer AS BYTE[]) AS OBJECT
            // Read the Memo Block Number
            LOCAL result AS LONG
            IF SELF:IsNull()
                RETURN NULL
            ENDIF
            IF SELF:Length == 10
                result  := (LONG) SELF:_GetNumericValue(buffer)
            ELSE
                result := BuffToLong(buffer, SELF:Offset)
            ENDIF
            RETURN result
        /// <inheritdoc/>
        OVERRIDE METHOD InitValue(buffer AS BYTE[]) AS VOID
            IF SELF:Length == 4
                VAR data := BitConverter.GetBytes((LONG) 0)
                Array.Copy(data, 0, buffer, SELF:Offset, SELF:Length)
            ENDIF
            RETURN
        /// <inheritdoc/>
        /// <remarks>At this level this writes the memo block number.</remarks>
        OVERRIDE METHOD PutValue(oValue AS OBJECT, buffer AS BYTE[]) AS LOGIC
            // oValue is the memo block number
            LOCAL intValue AS LONG
            IF oValue == NULL
                RETURN SELF:HandleNullValue(buffer)
            ELSEIF SELF:IsNullable
                SELF:ClearNullValue()
            ENDIF
            IF ! SELF:GetNumber(oValue, OUT intValue)
                SELF:RDD:_dbfError(Subcodes.ERDD_DATATYPE, EG_DATATYPE)
                RETURN FALSE
            ENDIF
            IF SELF:Length == 4
                VAR data := BitConverter.GetBytes(intValue)
                Array.Copy(data, 0, buffer, SELF:Offset, SELF:Length)
            ELSE
                LOCAL strValue AS STRING
                IF intValue == 0
                    strValue := STRING{' ',10}
                ELSE
                    strValue := intValue:ToString():PadLeft(10,' ')
                ENDIF
                SELF:_PutString(buffer, strValue)
            ENDIF
            RETURN TRUE
            
        /// <inheritdoc/>
        OVERRIDE METHOD EmptyValue() AS OBJECT
            RETURN NULL
            
        /// <inheritdoc/>
       OVERRIDE METHOD Validate() AS LOGIC
            RETURN (SELF:Length == 10 .OR. SELF:Length == 4)  .AND.  SELF:Decimals == 0 


    END CLASS

    /// <summary>Class for reading / writing Integer Columns. </summary>
    CLASS DbfIntegerColumn INHERIT DbfColumn

        CONSTRUCTOR(oInfo AS RddFieldInfo,oRDD AS XSharp.RDD.DBF)
            SUPER(oInfo,oRDD)
            RETURN
        /// <inheritdoc/>
        OVERRIDE METHOD GetValue(buffer AS BYTE[]) AS OBJECT
            LOCAL result AS LONG
            IF SELF:IsNull()
                RETURN NULL
            ENDIF
            IF SELF:Length == 4
                result := BuffToLong(buffer, SELF:Offset)
            ELSEIF Length == 2
                result := BuffToShort(buffer, SELF:Offset)
            ELSE
                result := 0
            ENDIF
            RETURN result
            
        /// <inheritdoc/>
        OVERRIDE METHOD InitValue(buffer AS BYTE[]) AS VOID
            IF SELF:Length == 4
                VAR data := BitConverter.GetBytes((LONG) 0)
                Array.Copy(data, 0, buffer, SELF:Offset, SELF:Length)

            ELSEIF SELF:Length == 2
                VAR data := BitConverter.GetBytes((SHORT) 0)
                Array.Copy(data, 0, buffer, SELF:Offset, SELF:Length)

            ENDIF
            RETURN
        /// <inheritdoc/>
        OVERRIDE METHOD PutValue(oValue AS OBJECT, buffer AS BYTE[]) AS LOGIC
            LOCAL intValue AS LONG
            IF oValue == NULL
                RETURN SELF:HandleNullValue(buffer)
            ELSEIF SELF:IsNullable
                SELF:ClearNullValue()
            ENDIF
            IF ! SELF:GetNumber(oValue, OUT intValue)
                SELF:RDD:_dbfError(Subcodes.ERDD_DATATYPE, EG_DATATYPE)
                RETURN FALSE
            ENDIF
            IF SELF:Length == 4
                VAR data := BitConverter.GetBytes(intValue)
                Array.Copy(data, 0, buffer, SELF:Offset, SELF:Length)
            ELSEIF SELF:Length == 2
                VAR data := BitConverter.GetBytes((SHORT) intValue)
                Array.Copy(data, 0, buffer, SELF:Offset, SELF:Length)
            ENDIF
            RETURN TRUE

        /// <inheritdoc/>
        OVERRIDE METHOD EmptyValue() AS OBJECT
            RETURN 0
        /// <inheritdoc/>
       OVERRIDE METHOD Validate() AS LOGIC
            RETURN (SELF:Length == 4 .OR. SELF:Length == 2)  .AND.  SELF:Decimals == 0 

    END CLASS

    /// <summary>Class for reading / writing AutoIncrement Columns. </summary>
    CLASS DbfAutoIncrementColumn INHERIT DbfIntegerColumn
        INTERNAL IncrStep as LONG
        INTERNAL Counter  AS LONG

        CONSTRUCTOR(oInfo AS RddFieldInfo,oRDD AS XSharp.RDD.DBF)
            SUPER(oInfo,oRDD)
            RETURN
        /// <summary>Read the next autoincrement number from the dbf header </summary>
        VIRTUAL METHOD Read() AS LOGIC
            LOCAL oField  as DbfField
            oField := DbfField{}
            oField:initialize()
            oField:Encoding := SELF:RDD:_Encoding
            SELF:RDD:_readField(SELF:OffSetInHeader, oField)
            SELF:Counter := oField:Counter
            SELF:IncrStep := oField:IncStep
            RETURN TRUE

        /// <summary>Write the next autoincrement number to the dbf header </summary>
        VIRTUAL METHOD Write() AS LOGIC
            LOCAL oField  AS DbfField
            oField := DbfField{}
            oField:initialize()
            oField:Encoding := SELF:RDD:_Encoding
            SELF:RDD:_readField(SELF:OffSetInHeader, oField)
            oField:Counter := SELF:Counter 
            oField:IncStep := (BYTE) SELF:IncrStep 
            SELF:RDD:_writeField(SELF:OffSetInHeader, oField)
            RETURN TRUE

        /// <inheritdoc/>
        VIRTUAL METHOD NewRecord(buffer AS BYTE[]) AS VOID
            // when shared then read the header again to get the current values of the counter and incrstep
            // increment the counter and write to the header
            // then write the current value to the buffer
            LOCAL nCurrent as LONG
            nCurrent := SELF:Counter
            IF SELF:RDD:HeaderLock(DbLockMode.Lock)
            
                SELF:Read()
                SELF:Counter += SELF:IncrStep
                SELF:Write()
                SELF:RDD:HeaderLock( DbLockMode.UnLock )
            ENDIF
            SELF:PutValue(nCurrent, buffer)
            RETURN


    END CLASS

    /// <summary>Class for reading / writing Double Columns. </summary>
    CLASS DbfDoubleColumn INHERIT DbfColumn

        CONSTRUCTOR(oInfo AS RddFieldInfo,oRDD AS XSharp.RDD.DBF)
            SUPER(oInfo,oRDD)
            RETURN
        /// <inheritdoc/>
        OVERRIDE METHOD GetValue(buffer AS BYTE[]) AS OBJECT
            LOCAL result AS REAL8
            IF SELF:IsNull()
                RETURN NULL
            ENDIF
            result := BitConverter.ToDouble(buffer, SELF:Offset)
            RETURN DbFloat{result,-1,-1}
        /// <inheritdoc/>
        OVERRIDE METHOD InitValue(buffer AS BYTE[]) AS VOID
            VAR data := BitConverter.GetBytes((REAL8) 0)
            Array.Copy(data, 0, buffer, SELF:Offset, SELF:Length)
            RETURN
        /// <inheritdoc/>
        OVERRIDE METHOD PutValue(oValue AS OBJECT, buffer AS BYTE[]) AS LOGIC
            LOCAL r8Value AS REAL8<commit_msg>[RDD] Disallow writing to AutoNumber fields.<commit_after>//
// Copyright (c) XSharp B.V.  All Rights Reserved.  
// Licensed under the Apache License, Version 2.0.  
// See License.txt in the project root for license information.
//

USING System.IO
USING XSharp.RDD.Enums
USING System.Collections.Generic
USING System.Linq
USING System.Runtime.CompilerServices
USING XSharp.RDD.Enums
USING XSharp.RDD.Support
BEGIN NAMESPACE XSharp.RDD
    /// <summary>Class for DBF Column reading / writing </summary>
    CLASS DbfColumn INHERIT RddFieldInfo
        INTERNAL NullBit        := -1 AS LONG
        INTERNAL LengthBit      := -1 AS LONG
        INTERNAL OffSetInHeader := -1 as LONG
        INTERNAL RDD        AS XSharp.RDD.DBF

        /// <summary>Create a DbfColumn class, used when creating files.</summary>
        STATIC METHOD Create(oInfo AS RddFieldInfo, oRDD AS XSharp.RDD.DBF) AS DbfColumn
            // Commented out types are Harbour specific
            SWITCH oInfo:FieldType
            CASE DbFieldType.Character   // C
            CASE DbFieldType.VarChar     // 'V'
            CASE DbFieldType.VarBinary   // 'Q'
                RETURN DbfCharacterColumn{oInfo,oRDD}
            CASE DbFieldType.Date        // D
                RETURN DbfDateColumn{oInfo,oRDD}
            CASE DbFieldType.Number      // N
            CASE DbFieldType.Float          // 'F'
                RETURN DbfNumericColumn{oInfo,oRDD}
            CASE DbFieldType.Memo        // M
            CASE DbFieldType.Picture        // 'P'
            CASE DbFieldType.General        // 'G'
            CASE DbFieldType.Blob           // 'W'
                RETURN DbfMemoColumn{oInfo,oRDD}
            CASE DbFieldType.Logic       // L
                RETURN DbfLogicColumn{oInfo,oRDD}
            CASE DbFieldType.Integer    // 'I' 
            //CASE DbFieldType.Integer2   // '2' 
            //CASE DbFieldType.Integer4   // '4'
                IF oInfo:IsAutoIncrement
                    RETURN DbfAutoIncrementColumn{oInfo,oRDD}
                ENDIF
                RETURN DbfIntegerColumn{oInfo,oRDD}
            //CASE DbFieldType.AutoIncrement
            //   RETURN DbfAutoIncrementColumn{oInfo}
            CASE DbFieldType.Double         // 'B'
            //CASE DbFieldType.Double8        // '8'
                RETURN DbfDoubleColumn{oInfo,oRDD}
            CASE DbFieldType.Currency		// 'Y'
            //CASE DbFieldType.CurrencyDouble	// 'Z'
                RETURN DbfCurrencyColumn{oInfo,oRDD}
            //CASE DbFieldType.TimeStamp      // '@'
            //CASE DbFieldType.ModTime        // '='
            //CASE DbFieldType.RowVer         // '^'
            //    RETURN DbfTimeStampColumn{oInfo}
            CASE DbFieldType.DateTime      // 'T'
                RETURN DbfDateTimeColumn{oInfo,oRDD}
            CASE DbFieldType.NullFlags
                RETURN DbfNullColumn{oInfo,oRDD}
            CASE DbFieldType.VOObject       // 'O'
            CASE DbFieldType.Unknown        // 'O'
            OTHERWISE
                RETURN DbfColumn{oInfo,oRDD}
            END SWITCH

        /// <summary>Create a DbfColumn class, based on the definition in the DBF. Used when opening a file. </summary>
        STATIC METHOD Create(oField REF DbfField, oRDD AS XSharp.RDD.DBF, nHeaderOffSet AS LONG) AS DbfColumn
            LOCAL oInfo AS RddFieldInfo
            LOCAL oColumn as DbfColumn
            oInfo        := RddFieldInfo{oField:Name, oField:Type, oField:Len, oField:Dec, oField:Offset, oField:Flags}
            oColumn      := DbfColumn.Create(oInfo, oRDD)
            oColumn:OffSetInHeader := nHeaderOffSet
            IF oColumn is DbfAutoIncrementColumn VAR dbfac
                dbfac:IncrStep  := oField:IncStep
                dbfac:Counter   := oField:Counter
            ENDIF
            return oColumn



        PROTECTED CONSTRUCTOR(oInfo AS RddFieldInfo,oRDD AS XSharp.RDD.DBF)
            SUPER(oInfo)
            SELF:RDD        := oRDD
            RETURN

       /// <summary>Initialize the buffer used for appends. Gets called once when a DBF is opened.</summary>
       VIRTUAL METHOD InitValue(buffer AS BYTE[]) AS VOID
            // this gets called to update the initialize the buffer for 'append blank'. Gets called once when a DBF is opened.
            RETURN

       /// <summary>Update the buffer after appending. For example for AutoIncrement or TimeStamp columns.</summary>
       /// <param name="buffer">Record buffer for the current record</param>
       VIRTUAL METHOD NewRecord(buffer AS BYTE[]) AS VOID
            // this gets called to update the appended record. Only used at this moment to set the AutoIncrement value
            // but can also be used to set a timestamp or rowversion later
            RETURN

        PROTECTED METHOD _GetString(buffer AS BYTE[]) AS STRING
            // The default implementation returns the part of the buffer as a string
            RETURN SELF:RDD:_Encoding:GetString(buffer, SELF:Offset, SELF:Length)

        /// <summary>Get the value from the buffer</summary>
       /// <param name="buffer">Record buffer for the current record</param>
        VIRTUAL METHOD GetValue(buffer AS BYTE[]) AS OBJECT
            IF SELF:IsNull()
                RETURN NULL
            ENDIF
            RETURN SELF:_GetString(buffer)

        /// <summary>Handle NULL values.</summary>
       /// <param name="buffer">Record buffer for the current record</param>
        VIRTUAL METHOD HandleNullValue(buffer AS BYTE[]) AS LOGIC 
            SELF:InitValue(buffer)                      
            IF SELF:IsNullable
                RETURN SELF:SetNullValue()
            ENDIF
            RETURN TRUE

       /// <summary>Write the value to the buffer</summary>
       /// <param name="buffer">Record buffer for the current record</param>
       /// <param name="oValue">Value to write.</param>
        VIRTUAL METHOD PutValue(oValue AS OBJECT, buffer AS BYTE[]) AS LOGIC
            RETURN FALSE

        [MethodImpl(MethodImplOptions.AggressiveInlining)];
         INTERNAL METHOD IsNull() AS LOGIC
            IF SELF:IsNullable .AND. SELF:RDD:_NullColumn != NULL
                RETURN SELF:RDD:_NullColumn:GetBit(SELF:NullBit)
            ENDIF
            RETURN FALSE

        [MethodImpl(MethodImplOptions.AggressiveInlining)];
        PROTECTED METHOD SetNullValue() AS LOGIC 
            IF SELF:IsNullable .AND. SELF:RDD:_NullColumn != NULL
                SELF:RDD:_NullColumn:SetBit(SELF:NullBit, TRUE)
                RETURN TRUE
            ENDIF
            RETURN FALSE

         [MethodImpl(MethodImplOptions.AggressiveInlining)];
         PROTECTED METHOD ClearNullValue() AS VOID
            IF SELF:IsNullable .AND. SELF:RDD:_NullColumn != NULL
                SELF:RDD:_NullColumn:SetBit(SELF:NullBit, FALSE)
            ENDIF
            RETURN 

        /// <summary>Get the default "empty" value, as you would get at EOF</summary>
        [MethodImpl(MethodImplOptions.AggressiveInlining)];
        VIRTUAL METHOD EmptyValue() AS OBJECT
            RETURN String.Empty

        [MethodImpl(MethodImplOptions.AggressiveInlining)];
        PROTECTED METHOD _GetNumericValue(buffer AS BYTE[]) AS INT64
            LOCAL lValid := TRUE AS LOGIC
            LOCAL nValue AS INT64
            LOCAL nPos, nLen AS LONG
            nValue := 0
            nLen := SELF:Length
            nPos := 0
            IF SELF:Offset+nLen >= buffer:Length
                NOP
            ENDIF
            DO WHILE nPos < nLen .AND. buffer[SELF:Offset+nPos] == 32 
                nPos++
            ENDDO
            FOR VAR i := nPos TO nLen -1
                LOCAL b AS BYTE
                b := buffer[SELF:Offset+i]
                SWITCH b
                CASE 48  // 0
                CASE 49  // 1
                CASE 50  // 2
                CASE 51  // 3
                CASE 52  // 4
                CASE 53  // 5
                CASE 54  // 6
                CASE 55  // 7
                CASE 56  // 8
                CASE 57  // 9
                    nValue := nValue * 10 + (b - 48)
                OTHERWISE
                    lValid := FALSE        
                END SWITCH
                IF ! lValid
                    EXIT
                ENDIF
            NEXT
            IF lValid
                RETURN nValue
            ENDIF
            RETURN 0

         [MethodImpl(MethodImplOptions.AggressiveInlining)];
        PROTECTED METHOD GetNumber<T>(oValue AS OBJECT, oResult OUT T) AS LOGIC WHERE T IS NEW()
            LOCAL tc AS TypeCode
            IF oValue != NULL
                tc := Type.GetTypeCode(oValue:GetType())
                SWITCH tc
                CASE TypeCode.SByte
                CASE TypeCode.Int16
                CASE TypeCode.Int32
                CASE TypeCode.Int64
                CASE TypeCode.Byte
                CASE TypeCode.UInt16
                CASE TypeCode.UInt32
                CASE TypeCode.UInt64
                CASE TypeCode.Decimal
                CASE TypeCode.Double
                CASE TypeCode.Single
                    oResult := (T) Convert.ChangeType(oValue, typeof(T) )
                    RETURN TRUE
                CASE TypeCode.Object
                    oResult := (T) Convert.ChangeType((((IFloat) oValue):Value), typeof(T) )
                    RETURN TRUE
                END SWITCH
            ENDIF
            oResult :=  T{}
            RETURN FALSE

        PROTECTED METHOD _PutString(buffer AS BYTE[], strValue AS STRING) AS VOID
            SELF:RDD:_Encoding:GetBytes(strValue, 0, SELF:Length, buffer, SELF:Offset)

    END CLASS

    /// <summary>Class for reading / writing String Columns</summary>
    CLASS DbfCharacterColumn INHERIT DbfColumn

         CONSTRUCTOR(oInfo AS RddFieldInfo,oRDD AS XSharp.RDD.DBF)
            SUPER(oInfo, oRDD)
            IF SELF:Decimals != 0
                SELF:Length         += SELF:Decimals * 256
			    SELF:Decimals       := 0
            ENDIF
            RETURN
        /// <inheritdoc/>
       OVERRIDE METHOD InitValue(buffer AS BYTE[]) AS VOID
            IF SELF:IsVarLength 
                buffer[SELF:Offset + SELF:Length-1] := 0
            ELSEIF SELF:IsUnicode
                VAR str := STRING{' ',SELF:Length /2}
                System.Buffer.BlockCopy(str:ToCharArray(),0, buffer, SELF:Offset, SELF:Length)
            ENDIF
            RETURN
       /// <inheritdoc/>
       OVERRIDE METHOD EmptyValue() AS OBJECT
           IF SELF:IsUnicode
                RETURN STRING{ ' ', SELF:Length /2 }
           ENDIF
           RETURN STRING{ ' ', SELF:Length }
        /// <inheritdoc/>
       OVERRIDE METHOD PutValue(oValue AS OBJECT, buffer AS BYTE[]) AS LOGIC
            LOCAL str AS STRING
            IF oValue == NULL
                RETURN SELF:HandleNullValue(buffer)
            ELSEIF SELF:IsNullable
                SELF:ClearNullValue()
            ENDIF
            IF oValue IS CHAR
                str := ((CHAR) oValue):ToString()
            ELSEIF oValue IS STRING
                str := (STRING) oValue
            ELSE
                SELF:RDD:_dbfError(Subcodes.ERDD_DATATYPE, EG_DATATYPE)
                RETURN FALSE
            ENDIF
            IF str:Length < SELF:Length .AND. ! SELF:IsVarLength
                IF SELF:IsBinary
                    str := str:PadRight(SELF:Length,'\0')
                ELSEIF SELF:IsUnicode
                    str := str:PadRight(SELF:Length/2,' ')
                ELSE
                    str := str:PadRight(SELF:Length,' ')
                ENDIF
            ENDIF
            IF SELF:IsBinary
                LOCAL nLen := Math.Min(SELF:Length, str:Length) AS LONG
                System.Text.Encoding.Default:GetBytes(str, 0, nLen, buffer, SELF:Offset)
            ELSEIF SELF:IsUnicode
                System.Buffer.BlockCopy(str:ToCharArray(),0, buffer, SELF:Offset, SELF:Length)
            ELSE
                SELF:_PutString(buffer, str)
            ENDIF
            IF SELF:IsVarLength
                IF SELF:RDD:_NullColumn != NULL
                    IF str:Length >= SELF:Length
                        // we used all the bytes, so set the varlength bit to 0
                        SELF:RDD:_NullColumn:SetBit(SELF:LengthBit, FALSE)
                    ELSE
                        SELF:RDD:_NullColumn:SetBit(SELF:LengthBit, TRUE)
                        buffer[SELF:Offset + SELF:Length-1] := (BYTE) str:Length
                    ENDIF
                ELSE
                    buffer[SELF:Offset + SELF:Length-1] := (BYTE) Math.Max(str:Length, SELF:Length-1)
                ENDIF
            ENDIF
            RETURN TRUE

        /// <inheritdoc/>
        OVERRIDE METHOD Validate() AS LOGIC
            IF SELF:Length == 0  .OR. SELF:Decimals > 0  .OR. SELF:Length > System.UInt16.MaxValue 
                RETURN FALSE
            ENDIF
            RETURN TRUE
        /// <inheritdoc/>
        OVERRIDE METHOD GetValue(buffer AS BYTE[]) AS OBJECT
            LOCAL result AS STRING
            IF SELF:IsNull()
                RETURN NULL
            ENDIF
            IF SELF:IsBinary
                result := System.Text.Encoding.Default:GetString(buffer, SELF:Offset, SELF:Length)
            ELSEIF SELF:IsUnicode
                VAR chars := CHAR[]{SELF:Length /2}
                System.Buffer.BlockCopy(buffer, SELF:Offset, chars, 0, SELF:Length)
                result := STRING{chars}
            ELSE
                result := SUPER:_GetString(buffer)
            ENDIF
            IF IsVarLength
                LOCAL len AS LONG
                len := SELF:Length
                IF SELF:RDD:_NullColumn != NULL
                    LOCAL lSet AS LOGIC
                    lSet := SELF:RDD:_NullColumn:GetBit(SELF:LengthBit)
                    IF lSet
                        len := buffer[SELF:Offset + SELF:Length-1]
                    ENDIF
                ENDIF
                IF !String.IsNullOrEmpty(result)
                    result := result:Substring(0, len)
                ENDIF
            ELSE
                IF String.IsNullOrEmpty(result)
                    result := STRING{ ' ', SELF:Length }
                ENDIF
            ENDIF
            RETURN result


    END CLASS
    /// <summary>Class for reading / writing Date Columns</summary>
    CLASS DbfDateColumn INHERIT DbfColumn

        CONSTRUCTOR(oInfo AS RddFieldInfo,oRDD AS XSharp.RDD.DBF)
            SUPER(oInfo,oRDD)
            RETURN
        /// <inheritdoc/>
        OVERRIDE METHOD GetValue(buffer AS BYTE[]) AS OBJECT
            LOCAL result := NULL AS IDate
            IF SELF:IsNull()
                RETURN NULL
            ENDIF
            LOCAL nValue := SELF:_GetNumericValue(buffer) AS INT64
            LOCAL lOk := FALSE AS LOGIC
            IF nValue != 0
                LOCAL nDay   := (LONG) nValue % 100 AS INT
                nValue /= 100
                LOCAL nMonth  := (LONG) nValue % 100 AS INT
                nValue /= 100
                LOCAL nYear   := (LONG) nValue AS INT
                IF nYear > 0 .AND. nDay > 0 .AND. nMonth > 0
                    result := DbDate{nYear,nMonth,nDay}
                    lOk := TRUE
                ENDIF
            ENDIF
            IF ! lOk
                result := DbDate{0,0,0}
            ENDIF
            RETURN result
        /// <inheritdoc/>
        OVERRIDE METHOD PutValue(oValue AS OBJECT, buffer AS BYTE[]) AS LOGIC
            IF oValue == NULL
                RETURN SELF:HandleNullValue(buffer)
            ELSEIF SELF:IsNullable
                SELF:ClearNullValue()
            ENDIF
            IF oValue IS System.DateTime VAR dt
                IF dt == DateTime.MinValue
                    oValue := DbDate{0,0,0}
                ELSE
                    oValue := DbDate{dt:Year, dt:Month, dt:Day}
                ENDIF
            ENDIF
            IF oValue IS IDate VAR dValue
                LOCAL str AS STRING
                IF dValue:IsEmpty
                    str := Space(8)
                ELSE
                    VAR dt := DateTime{dValue:Year, dValue:Month, dValue:Day}
                    str := dt:ToString( "yyyyMMdd" )
                ENDIF
                SELF:_PutString(buffer, str)
                RETURN TRUE
            ENDIF
            SELF:RDD:_dbfError(Subcodes.ERDD_DATATYPE, EG_DATATYPE)
            RETURN FALSE

        /// <inheritdoc/>

        OVERRIDE METHOD EmptyValue() AS OBJECT
            RETURN DbDate{0,0,0}
        /// <inheritdoc/>
        OVERRIDE METHOD Validate() AS LOGIC
            RETURN SUPER:Validate()


    END CLASS

    /// <summary>Class for reading / writing Logic Columns</summary>
    CLASS DbfLogicColumn INHERIT DbfColumn

        CONSTRUCTOR(oInfo AS RddFieldInfo,oRDD AS XSharp.RDD.DBF)
            SUPER(oInfo,oRDD)
            RETURN

        /// <inheritdoc/>
        OVERRIDE METHOD GetValue(buffer AS BYTE[]) AS OBJECT
            LOCAL result AS LOGIC
            IF SELF:IsNull()
                RETURN NULL
            ENDIF
            SWITCH buffer[SELF:Offset]
            CASE 84 // T
            CASE 116 // t
            CASE 89 // Y
            CASE 121 // y
                result := TRUE
            OTHERWISE
                result := FALSE
            END SWITCH
            RETURN result
            
        /// <inheritdoc/>
       OVERRIDE METHOD PutValue(oValue AS OBJECT, buffer AS BYTE[]) AS LOGIC
            IF oValue == NULL
                RETURN SELF:HandleNullValue(buffer)
            ELSEIF SELF:IsNullable
                SELF:ClearNullValue()
            ENDIF
            IF oValue IS LOGIC VAR logicValue
                buffer[SELF:Offset] := IIF( logicValue, (BYTE)'T', (BYTE)'F' )
                RETURN TRUE
            ENDIF
            SELF:RDD:_dbfError(Subcodes.ERDD_DATATYPE, EG_DATATYPE)
            RETURN FALSE
        /// <inheritdoc/>
       OVERRIDE METHOD EmptyValue() AS OBJECT
            RETURN FALSE
        /// <inheritdoc/>
       OVERRIDE METHOD Validate() AS LOGIC
            RETURN SUPER:Validate()

    END CLASS

    /// <summary>Class for reading / writing Numeric Columns</summary>
    CLASS DbfNumericColumn INHERIT DbfColumn

    CONSTRUCTOR(oInfo AS RddFieldInfo,oRDD AS XSharp.RDD.DBF)
            SUPER(oInfo,oRDD)
            RETURN
        /// <inheritdoc/>
    OVERRIDE METHOD GetValue(buffer AS BYTE[]) AS OBJECT
            LOCAL result AS IFloat
            LOCAL r8 AS REAL8
            IF SELF:IsNull()
                RETURN NULL
            ENDIF
            LOCAL nPos, nLen AS LONG
            nLen := SELF:Length
            nPos := 0
            DO WHILE  nPos < nLen .AND. buffer[SELF:Offset+nPos] == 32 
                nPos++
            ENDDO
            LOCAL lDec := FALSE AS LOGIC
            LOCAL lValid := TRUE AS LOGIC
            LOCAL nBefore, nCount, nDec AS INT64
            LOCAL lNegative := FALSE AS LOGIC
            LOCAL lFirst    := TRUE AS LOGIC
            LOCAL lLast     := FALSE AS LOGIC
            nBefore :=  nCount := nDec:= 0 
            FOR VAR i := nPos TO nLen -1
                LOCAL b AS BYTE
                b := buffer[SELF:Offset+i]
                IF lLast .AND. b != 32
                    lValid := FALSE
                    EXIT
                ENDIF
                SWITCH b
                CASE 43  // +
                    IF ! lFirst             // in the middle is invalid
                        lValid := FALSE
                    ELSE
                        lNegative := FALSE
                    ENDIF
                CASE 45  // -
                    IF ! lFirst             // in the middle is invalid
                        lValid := FALSE
                    ELSE
                        lNegative := TRUE
                    ENDIF
                CASE 48  // 0
                CASE 49  // 1
                CASE 50  // 2
                CASE 51  // 3
                CASE 52  // 4
                CASE 53  // 5
                CASE 54  // 6
                CASE 55  // 7
                CASE 56  // 8
                CASE 57  // 9 
                    nCount := nCount * 10 + (b - 48)
                    nDec   += 1
                CASE 46  // .
                    IF lDec // second Dot ?
                        lValid := FALSE
                    ELSE
                        nBefore := nCount
                        nCount  := 0
                        nDec    := 0
                        lDec    := TRUE
                    ENDIF
                CASE 32
                    lLast := TRUE       
                CASE 42  // *  Numeric overflow
                OTHERWISE
                    lValid := FALSE        
                END SWITCH
                IF ! lValid 
                    EXIT
                ENDIF
                lFirst := FALSE
            NEXT
            IF lValid
                IF lDec
                    r8 := nBefore + (nCount * 1.0 / 10 ^ nDec)
                ELSE
                    r8 := nCount
                ENDIF
                IF lNegative
                    r8 := -r8
                ENDIF
            ELSE
                 r8 := 0
            ENDIF
            result := DbFloat{r8, SELF:Length, SELF:Decimals}
            RETURN result
        /// <inheritdoc/>
      OVERRIDE METHOD PutValue(oValue AS OBJECT, buffer AS BYTE[]) AS LOGIC
            LOCAL r8Value AS REAL8
            IF oValue == NULL
                RETURN SELF:HandleNullValue(buffer)
            ELSEIF SELF:IsNullable
                SELF:ClearNullValue()
            ENDIF
            IF ! SELF:GetNumber(oValue, OUT r8Value)
                SELF:RDD:_dbfError(Subcodes.ERDD_DATATYPE, EG_DATATYPE)
                RETURN FALSE
            ENDIF
            VAR numformat := SELF:RDD:_numformat
            numformat:NumberDecimalDigits := SELF:Decimals
            LOCAL str AS STRING
            str := r8Value:ToString("F", numformat)
            LOCAL lDataWidthError := FALSE AS LOGIC
            IF str:Length > Length 
                str := STRING{'*', Length}
                lDataWidthError := TRUE
            ELSE
                str := str:PadLeft(Length,' ')
            ENDIF
            SELF:_PutString(buffer, str)
            IF lDataWidthError
                SELF:RDD:_dbfError(ERDD_DATAWIDTH, EG_DATAWIDTH)
            ENDIF
            RETURN TRUE
        /// <inheritdoc/>
        OVERRIDE METHOD EmptyValue() AS OBJECT
            RETURN DbFloat{0.0, SELF:Length, SELF:Decimals}
        /// <inheritdoc/>
       OVERRIDE METHOD Validate() AS LOGIC
           IF SELF:Length >= 1  .AND.  SELF:Length <= 255 
                IF SELF:Decimals > 0 
                    // We must check that we have enough space for DOT and decimal
                    IF SELF:Length <= 2  .OR.  SELF:Decimals >= SELF:Length -1 
                        RETURN FALSE
                    ENDIF
                ENDIF
            ELSE
                RETURN FALSE
            ENDIF
            RETURN TRUE


    END CLASS

    /// <summary>Class for reading / writing Memo Columns. This class returns and writes the block numbers.</summary>
    CLASS DbfMemoColumn INHERIT DbfColumn

        CONSTRUCTOR(oInfo AS RddFieldInfo,oRDD AS XSharp.RDD.DBF)
            SUPER(oInfo,oRDD)
            RETURN

        /// <inheritdoc/>
        /// <remarks>At this level this returns the memo block number.</remarks>
        OVERRIDE METHOD GetValue(buffer AS BYTE[]) AS OBJECT
            // Read the Memo Block Number
            LOCAL result AS LONG
            IF SELF:IsNull()
                RETURN NULL
            ENDIF
            IF SELF:Length == 10
                result  := (LONG) SELF:_GetNumericValue(buffer)
            ELSE
                result := BuffToLong(buffer, SELF:Offset)
            ENDIF
            RETURN result
        /// <inheritdoc/>
        OVERRIDE METHOD InitValue(buffer AS BYTE[]) AS VOID
            IF SELF:Length == 4
                VAR data := BitConverter.GetBytes((LONG) 0)
                Array.Copy(data, 0, buffer, SELF:Offset, SELF:Length)
            ENDIF
            RETURN
        /// <inheritdoc/>
        /// <remarks>At this level this writes the memo block number.</remarks>
        OVERRIDE METHOD PutValue(oValue AS OBJECT, buffer AS BYTE[]) AS LOGIC
            // oValue is the memo block number
            LOCAL intValue AS LONG
            IF oValue == NULL
                RETURN SELF:HandleNullValue(buffer)
            ELSEIF SELF:IsNullable
                SELF:ClearNullValue()
            ENDIF
            IF ! SELF:GetNumber(oValue, OUT intValue)
                SELF:RDD:_dbfError(Subcodes.ERDD_DATATYPE, EG_DATATYPE)
                RETURN FALSE
            ENDIF
            IF SELF:Length == 4
                VAR data := BitConverter.GetBytes(intValue)
                Array.Copy(data, 0, buffer, SELF:Offset, SELF:Length)
            ELSE
                LOCAL strValue AS STRING
                IF intValue == 0
                    strValue := STRING{' ',10}
                ELSE
                    strValue := intValue:ToString():PadLeft(10,' ')
                ENDIF
                SELF:_PutString(buffer, strValue)
            ENDIF
            RETURN TRUE
            
        /// <inheritdoc/>
        OVERRIDE METHOD EmptyValue() AS OBJECT
            RETURN NULL
            
        /// <inheritdoc/>
       OVERRIDE METHOD Validate() AS LOGIC
            RETURN (SELF:Length == 10 .OR. SELF:Length == 4)  .AND.  SELF:Decimals == 0 


    END CLASS

    /// <summary>Class for reading / writing Integer Columns. </summary>
    CLASS DbfIntegerColumn INHERIT DbfColumn

        CONSTRUCTOR(oInfo AS RddFieldInfo,oRDD AS XSharp.RDD.DBF)
            SUPER(oInfo,oRDD)
            RETURN
        /// <inheritdoc/>
        OVERRIDE METHOD GetValue(buffer AS BYTE[]) AS OBJECT
            LOCAL result AS LONG
            IF SELF:IsNull()
                RETURN NULL
            ENDIF
            IF SELF:Length == 4
                result := BuffToLong(buffer, SELF:Offset)
            ELSEIF Length == 2
                result := BuffToShort(buffer, SELF:Offset)
            ELSE
                result := 0
            ENDIF
            RETURN result
            
        /// <inheritdoc/>
        OVERRIDE METHOD InitValue(buffer AS BYTE[]) AS VOID
            IF SELF:Length == 4
                VAR data := BitConverter.GetBytes((LONG) 0)
                Array.Copy(data, 0, buffer, SELF:Offset, SELF:Length)

            ELSEIF SELF:Length == 2
                VAR data := BitConverter.GetBytes((SHORT) 0)
                Array.Copy(data, 0, buffer, SELF:Offset, SELF:Length)

            ENDIF
            RETURN
        /// <inheritdoc/>
        OVERRIDE METHOD PutValue(oValue AS OBJECT, buffer AS BYTE[]) AS LOGIC
            LOCAL intValue AS LONG
            IF oValue == NULL
                RETURN SELF:HandleNullValue(buffer)
            ELSEIF SELF:IsNullable
                SELF:ClearNullValue()
            ENDIF
            IF ! SELF:GetNumber(oValue, OUT intValue)
                SELF:RDD:_dbfError(Subcodes.ERDD_DATATYPE, EG_DATATYPE)
                RETURN FALSE
            ENDIF
            IF SELF:Length == 4
                VAR data := BitConverter.GetBytes(intValue)
                Array.Copy(data, 0, buffer, SELF:Offset, SELF:Length)
            ELSEIF SELF:Length == 2
                VAR data := BitConverter.GetBytes((SHORT) intValue)
                Array.Copy(data, 0, buffer, SELF:Offset, SELF:Length)
            ENDIF
            RETURN TRUE

        /// <inheritdoc/>
        OVERRIDE METHOD EmptyValue() AS OBJECT
            RETURN 0
        /// <inheritdoc/>
       OVERRIDE METHOD Validate() AS LOGIC
            RETURN (SELF:Length == 4 .OR. SELF:Length == 2)  .AND.  SELF:Decimals == 0 

    END CLASS

    /// <summary>Class for reading / writing AutoIncrement Columns. </summary>
    CLASS DbfAutoIncrementColumn INHERIT DbfIntegerColumn
        INTERNAL IncrStep as LONG
        INTERNAL Counter  AS LONG

        CONSTRUCTOR(oInfo AS RddFieldInfo,oRDD AS XSharp.RDD.DBF)
            SUPER(oInfo,oRDD)
            RETURN
        /// <summary>Read the next autoincrement number from the dbf header </summary>
        VIRTUAL METHOD Read() AS LOGIC
            LOCAL oField  as DbfField
            oField := DbfField{}
            oField:initialize()
            oField:Encoding := SELF:RDD:_Encoding
            SELF:RDD:_readField(SELF:OffSetInHeader, oField)
            SELF:Counter := oField:Counter
            SELF:IncrStep := oField:IncStep
            RETURN TRUE

        /// <summary>Write the next autoincrement number to the dbf header </summary>
        VIRTUAL METHOD Write() AS LOGIC
            LOCAL oField  AS DbfField
            oField := DbfField{}
            oField:initialize()
            oField:Encoding := SELF:RDD:_Encoding
            SELF:RDD:_readField(SELF:OffSetInHeader, oField)
            oField:Counter := SELF:Counter 
            oField:IncStep := (BYTE) SELF:IncrStep 
            SELF:RDD:_writeField(SELF:OffSetInHeader, oField)
            RETURN TRUE  
	    
        /// <inheritdoc/>
        VIRTUAL METHOD PutValue(oValue AS OBJECT, buffer AS BYTE[]) AS LOGIC
            // FoxPro throws an error when writing to an autonumber field. We do that too.    
            SELF:RDD:_dbfError(Subcodes.ERDD_WRITE ,EG_READONLY,"PutValue", i"Field '{SELF:Name}' is ReadOnly")
            RETURN FALSE
	    
        /// <inheritdoc/>
        VIRTUAL METHOD NewRecord(buffer AS BYTE[]) AS VOID
            // when shared then read the header again to get the current values of the counter and incrstep
            // increment the counter and write to the header
            // then write the current value to the buffer
            LOCAL nCurrent as LONG
            nCurrent := SELF:Counter
            DO WHILE ! SELF:RDD:HeaderLock(DbLockMode.Lock)
            ENDDO
            SELF:Read()
            SELF:Counter += SELF:IncrStep
            SELF:Write()
            SELF:RDD:HeaderLock( DbLockMode.UnLock )
	    
	    // Call SUPER:PutValue because we have overwritten PutValue to not allow to write, 
	    // but this of course is the exception, since we HAVE to write the new autonumber field.
            SUPER:PutValue(nCurrent, buffer)
            RETURN


    END CLASS

    /// <summary>Class for reading / writing Double Columns. </summary>
    CLASS DbfDoubleColumn INHERIT DbfColumn

        CONSTRUCTOR(oInfo AS RddFieldInfo,oRDD AS XSharp.RDD.DBF)
            SUPER(oInfo,oRDD)
            RETURN
        /// <inheritdoc/>
        OVERRIDE METHOD GetValue(buffer AS BYTE[]) AS OBJECT
            LOCAL result AS REAL8
            IF SELF:IsNull()
                RETURN NULL
            ENDIF
            result := BitConverter.ToDouble(buffer, SELF:Offset)
            RETURN DbFloat{result,-1,-1}
        /// <inheritdoc/>
        OVERRIDE METHOD InitValue(buffer AS BYTE[]) AS VOID
            VAR data := BitConverter.GetBytes((REAL8) 0)
            Array.Copy(data, 0, buffer, SELF:Offset, SELF:Length)
            RETURN
        /// <inheritdoc/>
        OVERRIDE METHOD PutValue(oValue AS OBJECT, buffer AS BYTE[]) AS LOGIC
            LOCAL r8Value AS REAL8
{'fraction_non_alphanumeric': 0.07479743035155115, 'fraction_numerical': 0.008231914000710204, 'mean_word_length': 1.33942529169656, 'pattern_counts': {'":': 0, '<': 182, '<?xml version=': 0, '>': 194, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '7231387', 'n_tokens_mistral': 18775, 'n_tokens_neox': 16476, 'n_words': 4676}