[VFP] Changed error handling for SqlStatement

//
// 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
USING System.Collections.Generic
USING System.Text
USING System.Data.Common
USING System.Data
USING System.Diagnostics
USING System.Threading
USING XSharp.VFP
USING System.Reflection
USING XSharp.RDD
USING XSharp.RDD.Enums

INTERNAL CLASS XSharp.VFP.SQLStatement
    PROTECT _oConnection     AS SQLConnection
    PROTECT _oNetCommand     AS DbCommand
    PROTECT _oLastDataReader AS DbDataReader
    PROTECT _nextCursorNo    AS LONG
    PROTECT _aSyncState      AS AsyncState
    PROTECT _oThread         AS Thread
    PROTECT _aQueryResult    AS ARRAY
    PROTECT _aResult         AS IList<IRdd>

    PROPERTY Connected          AS LOGIC GET Connection:State == System.Data.ConnectionState.Open
    PROPERTY Connection         AS SQLConnection GET _oConnection
    PROPERTY Asynchronous       AS LOGIC  AUTO GET SET
    PROPERTY BatchMode          AS LOGIC  AUTO GET SET
    PROPERTY ConnectBusy        AS LOGIC  GET Connection:ConnectBusy
    PROPERTY ConnectionString   AS STRING GET Connection:ConnectionString
    PROPERTY ConnectionTimeOut  AS LONG   GET Connection:ConnectionTimeOut  SET Connection:ConnectionTimeOut := Value
    PROPERTY CursorName         AS STRING AUTO GET SET
    PROPERTY DataSource         AS STRING GET Connection:DataSource         SET Connection:DataSource := Value
    PROPERTY DisconnectRollback AS LOGIC AUTO GET SET
    PROPERTY DispWarnings       AS LOGIC AUTO GET SET
    PROPERTY Handle             AS LONG   AUTO GET SET
    PROPERTY IdleTimeout        AS LONG  AUTO GET SET
    PROPERTY ODBChdbc           AS DbConnection GET SELF:Connection:NetConnection
    PROPERTY ODBChstmt          AS DbCommand    GET _oNetCommand
    PROPERTY Password           AS STRING GET Connection:Password
    PROPERTY Shared             AS LOGIC  GET Connection:Shared             SET Connection:Shared := Value
    PROPERTY UserId             AS STRING GET Connection:UserId
    PRIVATE _PacketSize         AS LONG
    PROPERTY PacketSize         AS LONG
        GET
            IF SQLReflection.GetPropertyValue(SELF:Connection:NetConnection, "PacketSize", OUT VAR result)
                RETURN (LONG) result
            ENDIF
            RETURN _PacketSize

        END GET
        SET
            _PacketSize := value
            SQLReflection.SetPropertyValue(SELF:Connection:NetConnection, "PacketSize", value)
            RETURN

    END SET
    END PROPERTY
    PROPERTY Prepared           AS LOGIC AUTO GET PRIVATE SET
    PROPERTY QueryTimeOut       AS LONG  GET _oNetCommand:CommandTimeout SET _oNetCommand:CommandTimeout := Value
    PROPERTY TransactionMode    AS LONG  AUTO GET SET
    PROPERTY UsesTransaction    AS LOGIC GET SELF:TransactionMode == DB_TRANSMANUAL
    PROPERTY WaitTime           AS LONG  AUTO GET SET

    METHOD SetDefaults() AS VOID
        SELF:Shared             := FALSE
        SELF:Asynchronous       := FALSE
        SELF:BatchMode          := TRUE
        SELF:DisconnectRollback := FALSE
        SELF:DispWarnings       := FALSE
        SELF:IdleTimeout        := 0
        SELF:PacketSize         := 4096
        SELF:Prepared           := FALSE
        SELF:QueryTimeOut       := 0
        SELF:TransactionMode    := DB_TRANSAUTO
        SELF:WaitTime           := 100
        SELF:_oNetCommand       := NULL
        SELF:_oConnection       := NULL
        SELF:_aSyncState        := AsyncState.Idle

    PRIVATE METHOD _AllocateCommand() AS VOID
        SELF:_oNetCommand := SELF:Connection:Factory:CreateCommand()
        SELF:_oNetCommand:Connection := SELF:Connection:NetConnection
        _oConnection:AddStatement(SELF)
        RETURN

    PRIVATE METHOD _CloseReader() AS VOID
        IF _oLastDataReader != NULL
            _oLastDataReader:Close()
            _oLastDataReader := NULL
        ENDIF

    PRIVATE METHOD CreateFile(oSchema AS DataTable, cCursorName AS STRING) AS IRdd
        LOCAL nFields AS LONG
        LOCAL aStruct AS ARRAY
        LOCAL aFieldNames AS List<STRING>
        nFields := oSchema:Rows:Count
        aFieldNames := List<STRING>{}
        aStruct := ArrayNew( (DWORD) nFields)
        nFields := 1
        FOREACH schemaRow AS DataRow IN oSchema:Rows
            VAR fieldInfo     := FromSchema(schemaRow, aFieldNames)
            fieldInfo:Ordinal := nFields
            aStruct[nFields]  := {fieldInfo:Name, fieldInfo:FieldTypeStr, fieldInfo:Length, fieldInfo:Decimals, fieldInfo:ColumnName, fieldInfo}
            nFields++
        NEXT
        nFields := aStruct:Count
        VAR cTemp := System.IO.Path.GetTempFileName()
        DbCreate(cTemp, aStruct, "DBFVFP")
        VAR nArea := _SelectString(cCursorName)
        IF nArea != 0
            DbCloseArea()
        ENDIF
        VoDbUseArea(TRUE, "DBFVFPSQL",cTemp,cCursorName,FALSE,FALSE)
        LOCAL oRDD AS IRdd
        oRDD := (IRdd) DbInfo(DbInfo.DBI_RDD_OBJECT)

        FOR VAR nI := 1 TO ALen(aStruct)
            LOCAL fieldInfo AS DbColumnInfo
            fieldInfo := aStruct[nI, 6]
            oRDD:FieldInfo(nI, DBS_COLUMNINFO, fieldInfo)
        NEXT
        RETURN oRDD

    PRIVATE METHOD _CopyFromReaderToRDD(oDataReader AS DbDataReader, oRDD AS IRdd) AS LOGIC
        LOCAL oMIGet := NULL AS MethodInfo
        VAR nFields := oRDD:FieldCount
        VAR nCounter := 0
        // DBFVFPSQL has a method SetData to set all the values of the current row.
        oMIGet := oRDD:GetType():GetMethod("GetData", BindingFlags.Instance+BindingFlags.IgnoreCase+BindingFlags.Public)
        IF oMIGet != NULL
            VAR GetData := (SqlGetData) oMIGet:CreateDelegate(typeof(SqlGetData), oRDD)
            DO WHILE oDataReader:Read()
                oRDD:Append(TRUE)
                // Get the data array from the workarea
                VAR data := GetData()
                // and fetch its values. This automatically updates the array that is owned by the RDD
                oDataReader:GetValues(data)
                IF SELF:Asynchronous .AND. ++nCounter % 100 == 0
                    IF SELF:_aSyncState == AsyncState.Cancelling
                        EXIT
                    ENDIF
                ENDIF

            ENDDO
        ELSE
            VAR data := OBJECT[]{nFields+1}  // 1 extra for the NullFlags
            DO WHILE oDataReader:Read()
                oRDD:Append(TRUE)
                // use our local data array
                oDataReader:GetValues(data)
                // and write the values to the RDD
                FOR VAR nFld := 1 UPTO nFields
                    oRDD:PutValue(nFld, data[nFld])
                NEXT
                IF SELF:Asynchronous .AND. ++nCounter % 100 == 0
                    IF SELF:_aSyncState == AsyncState.Cancelling
                        EXIT
                    ENDIF
                ENDIF
            ENDDO
        ENDIF
        RETURN TRUE

    PRIVATE METHOD _CreateWorkarea(oSchema AS DataTable, oDataReader AS DbDataReader, cCursorName AS STRING) AS LOGIC
        LOCAL oRDD AS IRdd
        oRDD := SELF:CreateFile(oSchema, cCursorName)
        SELF:_CopyFromReaderToRDD(oDataReader, oRDD)
        RETURN TRUE

    PRIVATE METHOD _ReturnsRows(cCommand AS STRING) AS LOGIC
        LOCAL cStmts := cCommand:Split(";":ToCharArray()) AS STRING[]
        LOCAL aParts := cStmts[cStmts:Length]:Split(" ()":ToCharArray()) AS STRING[]
        IF aParts:Length > 0
            LOCAL cWord := aParts[1]:ToLower() AS STRING
            SWITCH cWord
            CASE "select"
            CASE "execute"
                RETURN TRUE
            // dml
            CASE "insert"
            CASE "delete"
            CASE "update"
            // ddl
            CASE "create"
            CASE "drop"
            CASE "alter"
                RETURN FALSE
            OTHERWISE
                RETURN TRUE  //?
            END SWITCH
        ENDIF
        RETURN FALSE

    CONSTRUCTOR(cDataSource AS STRING, cUser AS STRING, cPassword AS STRING, lShared AS LOGIC)
        SELF:SetDefaults()
        _oConnection    := SQLConnection{cDataSource, cUser, cPassword, lShared}
        SELF:_AllocateCommand()
        RETURN

    CONSTRUCTOR(cConnectionString AS STRING, lShared AS LOGIC)
        _oConnection := SQLConnection{cConnectionString, lShared}
        SELF:_AllocateCommand()
        RETURN

    CONSTRUCTOR(oConnection AS SQLConnection)
        _oConnection := oConnection
        SELF:_AllocateCommand()
        RETURN

    METHOD BeginTransaction AS LOGIC
        IF SELF:UsesTransaction
            IF SELF:Connection:Transaction == NULL
                SELF:Connection:BeginTransaction()
            ENDIF
            SELF:_oNetCommand:Transaction := SELF:Connection:Transaction
        ENDIF
        RETURN TRUE

    METHOD Cancel AS LOGIC
        IF SELF:_aSyncState == AsyncState.Executing
            BEGIN LOCK SELF
                SELF:_aSyncState := AsyncState.Cancelling
            END LOCK
            DO WHILE SELF:_aSyncState == AsyncState.Cancelling
                System.Threading.Thread.Sleep(100)
            ENDDO
            SELF:ThreadComplete()
            BEGIN LOCK SELF
                SELF:_aSyncState := AsyncState.Idle
            END LOCK
            RETURN TRUE
        ENDIF
        RETURN FALSE

    METHOD Commit AS LOGIC
        SELF:_CloseReader()
        RETURN SELF:Connection:CommitTransaction()

    METHOD Rollback AS LOGIC
        SELF:_CloseReader()
        RETURN SELF:Connection:RollbackTransaction()

    METHOD DisConnect AS LOGIC
        SELF:_CloseReader()
        IF SELF:Connected
            IF SELF:DisconnectRollback
                SELF:Rollback()
            ELSE
                SELF:Commit()
            ENDIF
            _oNetCommand:Dispose()
            IF ! SELF:Connection:Shared
                SELF:Connection:Close()
            ENDIF
        ENDIF
        _oConnection:RemoveStatement(SELF)
        _oNetCommand := NULL
        _oConnection := NULL
        RETURN TRUE

    METHOD Prepare(cCommand AS STRING, cCursorName AS STRING) AS LONG
        SELF:_CloseReader()
        SELF:_oNetCommand:CommandText := cCommand
        SELF:_oNetCommand:Prepare()
        SELF:Prepared := TRUE
        SELF:CursorName := cCursorName
        RETURN 1

    PRIVATE METHOD _SaveResult(aInfo AS ARRAY) AS LONG
        CopyToInfo(_aQueryResult, aInfo)
        IF ALen(_aQueryResult) > 0
            RETURN (LONG) ALen(_aQueryResult)
        ENDIF
        RETURN 0

    METHOD _AsyncExecuteResult(aInfo AS ARRAY) AS LONG
        IF SELF:Asynchronous
            SWITCH SELF:_aSyncState
            CASE AsyncState.Executing
                RETURN 0
            CASE AsyncState.Ready
                SELF:ThreadComplete()
                RETURN SELF:_SaveResult(aInfo)
            CASE AsyncState.Idle
            CASE AsyncState.Cancelled
                SELF:CopyToCursorAsync()
                RETURN SELF:_SaveResult(aInfo)
            CASE AsyncState.Exception
                RETURN -1
            CASE AsyncState.Cancelling
                RETURN 0
            END SWITCH
        ENDIF
        RETURN 0
    METHOD Execute(cCommand AS STRING, cCursorName AS STRING, aInfo AS ARRAY) AS LONG
        SELF:BeginTransaction()
        SELF:_oNetCommand:CommandText := cCommand
        SELF:CursorName := cCursorName
        IF SELF:Asynchronous
            RETURN SELF:_AsyncExecuteResult(aInfo)
        ELSE
            SELF:CopyToCursor()
        ENDIF
        RETURN SELF:_SaveResult(aInfo)

    METHOD Execute(aInfo AS ARRAY) AS LONG
        SELF:BeginTransaction()
        IF SELF:Asynchronous
            RETURN SELF:_AsyncExecuteResult(aInfo)
        ELSE
            SELF:CopyToCursor()
        ENDIF
        RETURN SELF:_SaveResult(aInfo)

    PRIVATE METHOD CopyToInfo(aResult AS ARRAY, aInfo AS ARRAY) AS VOID
        IF aResult != NULL_ARRAY
            ASize(aInfo, ALen(aResult))
            IF ALen(aInfo) > 0
                ACopy(aResult, aInfo)
            ENDIF
        ELSE
            ASize(aInfo, 0)
        ENDIF
        RETURN

    METHOD BackgroundWorker(o AS OBJECT) AS VOID STRICT
        LOCAL aResults := List<IRdd>{}
        TRY
            VAR oDataReader := SELF:_oNetCommand:ExecuteReader()
            LOCAL cursorNo := 0 AS LONG
            BEGIN LOCK SELF
                SELF:_oLastDataReader := oDataReader
            END LOCK
            DO WHILE TRUE
                VAR oSchema := oDataReader:GetSchemaTable()
                VAR cursorName := SELF:CursorName
                IF cursorNo != 0
                    cursorName += cursorNo:ToString()
                ENDIF
                LOCAL oRDD AS IRdd
                oRDD := SELF:CreateFile(oSchema, cursorName)
                SELF:_CopyFromReaderToRDD(oDataReader, oRDD)
                aResults:Add(oRDD)
                IF SELF:_aSyncState == AsyncState.Cancelling
                    EXIT
                ENDIF
                IF ! oDataReader:NextResult()
                    EXIT
                ENDIF
            ENDDO
            BEGIN LOCK SELF
                SELF:_aResult    := aResults
                IF SELF:_aSyncState == AsyncState.Executing
                    SELF:_aSyncState := AsyncState.Ready
                ENDIF
            END LOCK
        CATCH e AS Exception
            RuntimeState.LastRddError := Error{e}
            BEGIN LOCK SELF
                SELF:_CloseReader()
                SELF:_aSyncState := AsyncState.Exception
                IF SELF:_oThread:ThreadState == System.Threading.ThreadState.Running
                    SELF:_oThread:Abort()
                ENDIF
                SELF:_oThread := NULL
            END LOCK
        END TRY
         RETURN

    METHOD ThreadComplete() AS VOID STRICT
        SELF:_aQueryResult := {}
        IF SELF:_aResult == NULL
            SELF:_aSyncState := AsyncState.Idle
            RETURN
        ENDIF
        VAR aRDDs := SELF:_aResult
        SELF:_aResult := NULL
        FOREACH oRdd AS IRdd IN aRDDs
            VAR name := oRdd:Alias
            VAR count := oRdd:RecCount
            AAdd(SELF:_aQueryResult, {name, count})
            VAR nArea := RuntimeState.Workareas.FindAlias(name)
            IF nArea > 0
                RuntimeState.Workareas.CloseArea(nArea)
            ELSE
                nArea := RuntimeState.Workareas.FindEmptyArea(TRUE)
            ENDIF
            RuntimeState.Workareas.SetArea(nArea, oRdd)
        NEXT
        IF SELF:_aSyncState == AsyncState.Ready
            SELF:_aSyncState := AsyncState.Idle
        ENDIF

    PRIVATE METHOD CopyToCursorAsync() AS VOID
        IF SELF:_ReturnsRows(SELF:_oNetCommand:CommandText)
            _aQueryResult := {}
            IF SELF:_aSyncState == AsyncState.Idle
                BEGIN LOCK SELF
                    SELF:_CloseReader()
                    _oThread := Thread{BackgroundWorker}
                    SELF:_aSyncState := AsyncState.Executing
                    _oThread:Start(SELF)
                END LOCK
            ENDIF
        ELSE
            VAR result := SELF:_oNetCommand:ExecuteNonQuery()
            _aQueryResult := {{"", result}}
        ENDIF

    PRIVATE METHOD CopyToCursor() AS VOID
        TRY
            IF SELF:_ReturnsRows(SELF:_oNetCommand:CommandText)
                VAR oDataReader := SELF:_oNetCommand:ExecuteReader()
                CopyToCursor(oDataReader, 0)
            ELSE
                VAR result := SELF:_oNetCommand:ExecuteNonQuery()
                _aQueryResult := {{"", result}}
            ENDIF
        CATCH e AS Exception
            RuntimeState.LastRddError := Error{e}
            _aQueryResult := {{"", -1}}
        END TRY

    PRIVATE METHOD CopyToCursor(oDataReader AS DbDataReader, cursorNo AS LONG) AS VOID
        LOCAL result   := {} AS ARRAY
        _oLastDataReader := NULL_OBJECT
        DO WHILE TRUE
            VAR cursorName := SELF:CursorName
            IF cursorNo != 0
                cursorName += cursorNo:ToString()
            ENDIF
    		oDataReader := SELF:Connection:Factory:AfterOpen(oDataReader)
            VAR oSchema := oDataReader:GetSchemaTable()
            SELF:_CreateWorkarea(oSchema, oDataReader, cursorName)
            AAdd(result, {cursorName, RecCount()})
            cursorNo += 1
            IF ! SELF:BatchMode
                _oLastDataReader := oDataReader
                _nextCursorNo    := cursorNo
                EXIT
            ENDIF
            IF ! oDataReader:NextResult()
                EXIT
            ENDIF
        ENDDO
        _aQueryResult := result
        RETURN

    METHOD MoreResults(cursorName AS STRING, aInfo AS ARRAY) AS LONG
        IF !String.IsNullOrEmpty(cursorName)
            SELF:CursorName := cursorName
            SELF:_nextCursorNo := 0
        ENDIF
        IF _oLastDataReader != NULL .AND. _oLastDataReader:NextResult()
            CopyToCursor(_oLastDataReader, _nextCursorNo)
            RETURN SELF:_SaveResult(aInfo)
        ENDIF
        ASize(aInfo,1)
        aInfo[1] := {{0, -1}}
        RETURN 0

    PRIVATE METHOD GetNumRestrictions(cCollectionName AS STRING) AS LONG
        LOCAL nRestrictions := 0 AS LONG
        LOCAL oTable := SELF:Connection:NetConnection:GetSchema("MetadataCollections",NULL) AS DataTable
        FOREACH oRow AS DataRow IN oTable:Rows
			IF String.Compare( (STRING)oRow:Item["CollectionName"], cCollectionName, StringComparison.OrdinalIgnoreCase) == 0
				nRestrictions := (INT) oRow:Item["NumberOfRestrictions"]
				EXIT
			ENDIF
        NEXT
        RETURN nRestrictions

    #region MetaData
   METHOD GetTables(cType AS STRING, cCursorName AS STRING) AS LOGIC
        SELF:CursorName := cCursorName
        VAR types := cType:Split(",":ToCharArray(),StringSplitOptions.RemoveEmptyEntries)
        VAR list  := List<STRING>{}
        FOREACH VAR type IN types
            VAR sType := type
            IF sType:StartsWith("'")
                sType := sType:Replace("'","")
            ELSEIF sType:StartsWith(e"\"")
                sType := sType:Replace(e"\"","")
            ENDIF
            list:Add(sType)
        NEXT

        LOCAL filter AS STRING[]
        LOCAL oTables AS List<DataTable>
        LOCAL oTable AS DataTable
        LOCAL nRestrictions AS LONG
        oTables := List<DataTable>{}

        nRestrictions := GetNumRestrictions("Tables")
        filter := STRING[]{nRestrictions}
        oTable := SELF:Connection:NetConnection:GetSchema("Tables", filter)
        oTables:Add(oTable)

        nRestrictions := GetNumRestrictions("Views")
        filter := STRING[]{nRestrictions}
        oTable := SELF:Connection:NetConnection:GetSchema("Views", filter)
        oTables:Add(oTable)

        LOCAL aStruct AS ARRAY
        aStruct := ArrayNew(5)
        aStruct[1] := {"TABLE_CAT","C:0",128,0}
        aStruct[2] := {"TABLE_SCHEM","C:0",128,0}
        aStruct[3] := {"TABLE_NAME","C:0",128,0}
        aStruct[4] := {"TABLE_TYPE","C:0",32,0}
        aStruct[5] := {"REMARKS","C:0",254,0}
        VAR cTemp := System.IO.Path.GetTempFileName()
        DbCreate(cTemp, aStruct, "DBFVFP")
        VAR nArea := _SelectString(cCursorName)
        IF nArea != 0
            DbCloseArea()
        ENDIF
        VoDbUseArea(TRUE, "DBFVFPSQL",cTemp,cCursorName,FALSE,FALSE)
        LOCAL oRDD AS IRdd
        oRDD := (IRdd) DbInfo(DbInfo.DBI_RDD_OBJECT)
        FOREACH VAR oT IN oTables
            FOREACH oRow AS DataRow IN oT:Rows
                VAR oValues := SELF:Connection:Factory:GetMetaDataTableValues(oRow)
                VAR match := TRUE
                IF list:Count > 0
                    match := list:Contains((STRING) oValues[4])
                ENDIF
                IF match
                    oRDD:Append(TRUE)
                    FOR VAR nFld := 1 TO 5
                        oRDD:PutValue(nFld, oValues[nFld])
                    NEXT
                ENDIF
            NEXT
       NEXT
       RETURN TRUE

   METHOD GetColumnsFox(cTableName AS STRING, cCursorName AS STRING) AS LOGIC
        SELF:_oNetCommand:CommandText := "Select * from "+cTableName+" where 1 = 0"
        VAR oDataReader := SELF:_oNetCommand:ExecuteReader()
        VAR oSchema := oDataReader:GetSchemaTable()
        VAR aFieldNames := List<STRING>{}
        VAR aStruct := ArrayNew(4)
        aStruct[1] := {"FIELD_NAME", "C", 14,0}
        aStruct[2] := {"FIELD_TYPE", "C", 1,0}
        aStruct[3] := {"FIELD_LEN", "N", 3,0}
        aStruct[4] := {"FIELD_DEC", "N", 3,0}
        VAR cTemp := System.IO.Path.GetTempFileName()
        DbCreate(cTemp, aStruct, "DBFVFP")
        VoDbUseArea(TRUE, "DBFVFPSQL",cTemp,cCursorName,FALSE,FALSE)
        LOCAL oRDD AS IRdd
        oRDD := (IRdd) DbInfo(DbInfo.DBI_RDD_OBJECT)
        FOREACH schemaRow AS DataRow IN oSchema:Rows
            VAR fieldInfo    := FromSchema(schemaRow, aFieldNames)
            oRDD:Append(TRUE)
            oRDD:PutValue(1, fieldInfo:Name)
            oRDD:PutValue(2, Left(fieldInfo:FieldTypeStr,1))
            oRDD:PutValue(3, fieldInfo:Length)
            oRDD:PutValue(4, fieldInfo:Decimals)
            oRDD:GoCold()
        NEXT
        RETURN TRUE

   METHOD GetColumnsNative(cTableName AS STRING, cCursorName AS STRING) AS LOGIC
        LOCAL filter AS STRING[]
        LOCAL oTable AS DataTable
        LOCAL nRestrictions AS LONG
        nRestrictions := GetNumRestrictions("Columns")
        filter := STRING[]{nRestrictions}
        filter[3] := cTableName
        oTable := SELF:Connection:NetConnection:GetSchema("Columns", filter)
        LOCAL aStruct AS ARRAY
        aStruct := ArrayNew(19)
        aStruct[1] := {"TABLE_CAT","C:0",128,0}
        aStruct[2] := {"TABLE_SCHE","C:0",128,0}
        aStruct[3] := {"TABLE_NAME","C",128,0}
        aStruct[4] := {"COLUMN_NAM","C",128,0}
        aStruct[5] := {"DATA_TYPE","I",4,0}
        aStruct[6] := {"TYPE_NAME","C",128,0}
        aStruct[7] := {"COLUMN_SIZ","I:0",4,0}
        aStruct[8] := {"BUFFER_LEN","I:0",4,0}
        aStruct[9] := {"DECIMAL_DI","I:0",4,0}
        aStruct[10] := {"NUM_PREC_R","I:0",4,0}
        aStruct[11] := {"NULLABLE","I",4,0}
        aStruct[12] := {"REMARKS","C:0",254,0}
        aStruct[13] := {"COLUMN_DEF","M:0",4,0}
        aStruct[14] := {"SQL_DATA_T","I",4,0}
        aStruct[15] := {"SQL_DATETI","I:0",4,0}
        aStruct[16] := {"CHAR_OCTET","I:0",4,0}
        aStruct[17] := {"ORDINAL_PO","I",4,0}
        aStruct[18] := {"IS_NULLABL","C:0",254,0}
        aStruct[19] := {"SS_DATA_TY","I:0",4,0}
        VAR cTemp := System.IO.Path.GetTempFileName()
        DbCreate(cTemp, aStruct, "DBFVFP")
        VAR nArea := _SelectString(cCursorName)
        IF nArea != 0
            DbCloseArea()
        ENDIF
        VoDbUseArea(TRUE, "DBFVFPSQL",cTemp,cCursorName,FALSE,FALSE)
        LOCAL oRDD AS IRdd
        oRDD := (IRdd) DbInfo(DbInfo.DBI_RDD_OBJECT)
        FOREACH oRow AS DataRow IN oTable:Rows
            oRDD:Append(TRUE)
            VAR oValues := SELF:Connection:Factory:GetMetaDataColumnValues(oRow)
            FOR VAR i := 1 TO oValues:Length
                oRDD:PutValue(i, oValues[i])
            NEXT
         NEXT
       RETURN TRUE

   METHOD GetColumns(cTableName AS STRING, cType AS STRING, cCursorName AS STRING) AS LOGIC
        SELF:CursorName := cCursorName
        IF String.IsNullOrEmpty(cTableName) .OR. String.IsNullOrEmpty(cType)
            RETURN FALSE
        ENDIF
        SWITCH cType
        CASE "FOXPRO"
            RETURN GetColumnsFox(cTableName, cCursorName)
        CASE "NATIVE"
            RETURN GetColumnsNative(cTableName, cCursorName)
        END SWITCH
        RETURN FALSE
    #endregion

    STATIC METHOD FromSchema(schemaRow AS DataRow, aFieldNames AS IList<STRING>) AS XSharp.RDD.DbColumnInfo
		LOCAL nLen, nDec AS LONG
		LOCAL cType AS STRING
		LOCAL oType	AS System.Type
		LOCAL TC AS TypeCode
        LOCAL result AS DbColumnInfo
        LOCAL columnName AS STRING
        columnName   := schemaRow["ColumnName"]:ToString( )
        oType   := (Type) schemaRow["DataType"]
		TC      := Type.GetTypeCode(oType)
		nDec    := 0
		SWITCH TC
		CASE TypeCode.String
			cType   := "C"
			nLen    := (Int32)schemaRow["ColumnSize"]
			// Automatically Convert Long Strings to Memos
			IF nLen  > 255 .OR. nLen < 0
				nLen    := 4
				cType   := "M"
            ENDIF
            result := DbColumnInfo{columnName,cType,nLen ,0 }

		CASE TypeCode.Boolean
			result := DbColumnInfo{columnName,"L",1 ,0 }

        CASE TypeCode.Decimal
		    result := DbColumnInfo{columnName,"Y",16 ,4 }
            result:NumericScale     := 16
            result:NumericPrecision := 4

		CASE TypeCode.Double
        CASE TypeCode.Single
			nDec := 1
			nLen := 10
            VAR nScale := Convert.ToInt32(schemaRow:Item["NumericScale"])
            VAR nPrec  := Convert.ToInt32(schemaRow:Item["NumericPrecision"])
            IF nScale == 255
                nScale := 2
            ENDIF
			nDec := nScale
			nLen := nPrec
            IF nLen == 0
				// I have seen a case where nDec == 31 and nLen == 0
				// Fix this to something usefull
				nDec := 2
				nLen := 10
			ELSEIF nDec == 127
				//IF nLen == 38 .AND. oProviderType = ProviderType.Oracle // Standardvalue for calculated fields in oracle-queries, cutting decimals leads to wrong results in that case
				//	nDec := 10
				//ELSE
					nDec := 0 // Overflow abfangen
				//ENDIF
            ENDIF
            result := DbColumnInfo{columnName,"N",nLen ,nDec }
            result:NumericScale     := nScale
            result:NumericPrecision := nPrec

		CASE TypeCode.Int32		// -2147483647 - 2147483648 (2^31)
            result := DbColumnInfo{columnName,"I",4 ,0}
            IF (LOGIC)schemaRow["IsAutoIncrement"]
                result:Flags |= DBFFieldFlags.AutoIncrement
            ENDIF

		CASE TypeCode.Int64		// - 9223372036854775807 - 9223372036854775808 (2^63)
            result := DbColumnInfo{columnName,"N",21 ,0}

		CASE TypeCode.Int16	// -32767 - 32768 (2^15)
            result := DbColumnInfo{columnName,"N",6 ,0}

		CASE TypeCode.Byte
            result := DbColumnInfo{columnName,"N",4 ,0}

		CASE TypeCode.SByte	// 0 - 255 	(2^8)
            result := DbColumnInfo{columnName,"N",3 ,0}

		CASE TypeCode.UInt16	// 0 - 65535 (2^16)
            result := DbColumnInfo{columnName,"N",5 ,0}

		CASE TypeCode.UInt32		// 0 - 4294836225 (2^32)
            result := DbColumnInfo{columnName,"N",10 ,0}

		CASE TypeCode.UInt64	// 0 - 18445618199572250625 (2^64)
			nLen := 20
            result := DbColumnInfo{columnName,"N",nLen ,0}

		CASE TypeCode.DateTime
			cType   := "T"
			nLen 	:= 8
            result  := DbColumnInfo{columnName,cType,nLen ,0}

        CASE TypeCode.Object
            IF oType == typeof(BYTE[])
			    cType   := "P"
			    nLen 	:= 4
                result  := DbColumnInfo{columnName,cType,nLen ,0}

            ELSE
			    LOCAL lIsDate := FALSE AS LOGIC
			    LOCAL oMems AS MethodInfo[]
			    LOCAL lFound := FALSE AS LOGIC
			    // check to see if the datatype has a dbType
			    oMems := oType:GetMethods(BindingFlags.Public|BindingFlags.Static)
			    FOREACH oMem AS MethodInfo IN oMems
				    IF oMem:ReturnType == TypeOf(System.DateTime)  .AND. String.Compare(oMem:Name, "op_Explicit", StringComparison.OrdinalIgnoreCase) == 0
					    lIsDate := TRUE
					    lFound  := TRUE
					    EXIT
				    ENDIF
			    NEXT
			    IF ! lFound
				    LOCAL cTypeName AS STRING
				    cTypeName := oType:Name:ToUpperInvariant()
				    lIsDate     := cTypeName:Contains("DATE")
			    ENDIF
			    IF lIsDate
				    cType   := "D"
				    nLen 	:= 8
			    ELSE
				    cType 	:= "C"
				    nLen 	:= 10
			    ENDIF
                result := DbColumnInfo{columnName,cType,nLen ,0}
            ENDIF
		OTHERWISE
			cType := "C"
			nLen 	:= (Int32)schemaRow["ColumnSize"]
			IF nLen <= 0
				cType := "M"
                nLen  := 4
			ENDIF
            result := DbColumnInfo{columnName,cType,nLen ,0}

        END SWITCH
        IF (LOGIC)schemaRow["AllowDBNull"]
            result:Flags |= DBFFieldFlags.Nullable
        ENDIF
        VAR cFldName        := SQLSupport.CleanupColumnName(columnName)
        result:ColumnName   := columnName
        result:Name         := MakeFieldNameUnique(cFldName, aFieldNames)
        result:Alias        := result:Name
        result:DotNetType   := oType
		RETURN result

   STATIC METHOD MakeFieldNameUnique(cName AS STRING, aFldNames AS IList<STRING> ) AS STRING
        LOCAL dwPos, dwFld AS LONG
        LOCAL cNewname		 AS STRING
        IF String.IsNullOrEmpty(cName)
            dwFld := 0
            dwPos := 1
            DO WHILE dwPos >= 0
                ++dwFld
                cName := "FLD"+dwFld:ToString():PadLeft(3,c'0')
                dwPos := aFldNames:IndexOf(cName:ToUpper())
            ENDDO
        ELSE
            // remove column prefixes
            dwPos := cName:IndexOf(".")+1
            IF dwPos > 0
                cName := cName:Substring(dwPos)
            ENDIF
            // remove embedded spaces
            cName 	:= cName:Replace(" ", "_"):ToUpper()
            cNewname := Left(cName,10)
            dwFld 	:= 1
            DO WHILE aFldNames:IndexOf(cNewname) >= 0
                ++dwFld
                VAR tmp := dwFld:ToString()
                cNewname := cName:Substring(0, 10 - tmp:Length)+tmp
            ENDDO
            cName 	:= cNewname
        ENDIF
        aFldNames:Add(cName)
    RETURN cName

END CLASS

INTERNAL DELEGATE SqlGetData() AS OBJECT[]

INTERNAL ENUM XSharp.VFP.AsyncState
    MEMBER Idle         := 0
    MEMBER Executing    := 1
    MEMBER Ready        := 2
    MEMBER Cancelling   := 3
    MEMBER Cancelled    := 4
    MEMBER Exception    := 5
END ENUM

//
// 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
USING System.Collections.Generic
USING System.Text
USING System.Data.Common
USING System.Data
USING System.Diagnostics
USING System.Threading
USING XSharp.VFP
USING System.Reflection
USING XSharp.RDD
USING XSharp.RDD.Enums

INTERNAL CLASS XSharp.VFP.SQLStatement
    PROTECT _oConnection     AS SQLConnection
    PROTECT _oNetCommand     AS DbCommand
    PROTECT _oLastDataReader AS DbDataReader
    PROTECT _nextCursorNo    AS LONG
    PROTECT _aSyncState      AS AsyncState
    PROTECT _oThread         AS Thread
    PROTECT _aQueryResult    AS ARRAY
    PROTECT _aResult         AS IList<IRdd>

    PROPERTY Connected          AS LOGIC GET Connection:State == System.Data.ConnectionState.Open
    PROPERTY Connection         AS SQLConnection GET _oConnection
    PROPERTY Asynchronous       AS LOGIC  AUTO GET SET
    PROPERTY BatchMode          AS LOGIC  AUTO GET SET
    PROPERTY ConnectBusy        AS LOGIC  GET Connection:ConnectBusy
    PROPERTY ConnectionString   AS STRING GET Connection:ConnectionString
    PROPERTY ConnectionTimeOut  AS LONG   GET Connection:ConnectionTimeOut  SET Connection:ConnectionTimeOut := Value
    PROPERTY CursorName         AS STRING AUTO GET SET
    PROPERTY DataSource         AS STRING GET Connection:DataSource         SET Connection:DataSource := Value
    PROPERTY DisconnectRollback AS LOGIC AUTO GET SET
    PROPERTY DispWarnings       AS LOGIC AUTO GET SET
    PROPERTY Handle             AS LONG   AUTO GET SET
    PROPERTY IdleTimeout        AS LONG  AUTO GET SET
    PROPERTY ODBChdbc           AS DbConnection GET SELF:Connection:NetConnection
    PROPERTY ODBChstmt          AS DbCommand    GET _oNetCommand
    PROPERTY Password           AS STRING GET Connection:Password
    PROPERTY Shared             AS LOGIC  GET Connection:Shared             SET Connection:Shared := Value
    PROPERTY UserId             AS STRING GET Connection:UserId
    PRIVATE _PacketSize         AS LONG
    PROPERTY PacketSize         AS LONG
        GET
            IF SQLReflection.GetPropertyValue(SELF:Connection:NetConnection, "PacketSize", OUT VAR result)
                RETURN (LONG) result
            ENDIF
            RETURN _PacketSize

        END GET
        SET
            _PacketSize := value
            SQLReflection.SetPropertyValue(SELF:Connection:NetConnection, "PacketSize", value)
            RETURN

    END SET
    END PROPERTY
    PROPERTY Prepared           AS LOGIC AUTO GET PRIVATE SET
    PROPERTY QueryTimeOut       AS LONG  GET _oNetCommand:CommandTimeout SET _oNetCommand:CommandTimeout := Value
    PROPERTY TransactionMode    AS LONG  AUTO GET SET
    PROPERTY UsesTransaction    AS LOGIC GET SELF:TransactionMode == DB_TRANSMANUAL
    PROPERTY WaitTime           AS LONG  AUTO GET SET

    METHOD SetDefaults() AS VOID
        SELF:Shared             := FALSE
        SELF:Asynchronous       := FALSE
        SELF:BatchMode          := TRUE
        SELF:DisconnectRollback := FALSE
        SELF:DispWarnings       := FALSE
        SELF:IdleTimeout        := 0
        SELF:PacketSize         := 4096
        SELF:Prepared           := FALSE
        SELF:QueryTimeOut       := 0
        SELF:TransactionMode    := DB_TRANSAUTO
        SELF:WaitTime           := 100
        SELF:_oNetCommand       := NULL
        SELF:_oConnection       := NULL
        SELF:_aSyncState        := AsyncState.Idle

    PRIVATE METHOD _AllocateCommand() AS VOID
        SELF:_oNetCommand := SELF:Connection:Factory:CreateCommand()
        SELF:_oNetCommand:Connection := SELF:Connection:NetConnection
        _oConnection:AddStatement(SELF)
        RETURN

    PRIVATE METHOD _CloseReader() AS VOID
        IF _oLastDataReader != NULL
            _oLastDataReader:Close()
            _oLastDataReader := NULL
        ENDIF

    PRIVATE METHOD CreateFile(oSchema AS DataTable, cCursorName AS STRING) AS IRdd
        LOCAL nFields AS LONG
        LOCAL aStruct AS ARRAY
        LOCAL aFieldNames AS List<STRING>
        nFields := oSchema:Rows:Count
        aFieldNames := List<STRING>{}
        aStruct := ArrayNew( (DWORD) nFields)
        nFields := 1
        FOREACH schemaRow AS DataRow IN oSchema:Rows
            VAR fieldInfo     := FromSchema(schemaRow, aFieldNames)
            fieldInfo:Ordinal := nFields
            aStruct[nFields]  := {fieldInfo:Name, fieldInfo:FieldTypeStr, fieldInfo:Length, fieldInfo:Decimals, fieldInfo:ColumnName, fieldInfo}
            nFields++
        NEXT
        nFields := aStruct:Count
        VAR cTemp := System.IO.Path.GetTempFileName()
        DbCreate(cTemp, aStruct, "DBFVFP")
        VAR nArea := _SelectString(cCursorName)
        IF nArea != 0
            DbCloseArea()
        ENDIF
        VoDbUseArea(TRUE, "DBFVFPSQL",cTemp,cCursorName,FALSE,FALSE)
        LOCAL oRDD AS IRdd
        oRDD := (IRdd) DbInfo(DbInfo.DBI_RDD_OBJECT)

        FOR VAR nI := 1 TO ALen(aStruct)
            LOCAL fieldInfo AS DbColumnInfo
            fieldInfo := aStruct[nI, 6]
            oRDD:FieldInfo(nI, DBS_COLUMNINFO, fieldInfo)
        NEXT
        RETURN oRDD

    PRIVATE METHOD _CopyFromReaderToRDD(oDataReader AS DbDataReader, oRDD AS IRdd) AS LOGIC
        LOCAL oMIGet := NULL AS MethodInfo
        VAR nFields := oRDD:FieldCount
        VAR nCounter := 0
        // DBFVFPSQL has a method SetData to set all the values of the current row.
        oMIGet := oRDD:GetType():GetMethod("GetData", BindingFlags.Instance+BindingFlags.IgnoreCase+BindingFlags.Public)
        IF oMIGet != NULL
            VAR GetData := (SqlGetData) oMIGet:CreateDelegate(typeof(SqlGetData), oRDD)
            DO WHILE oDataReader:Read()
                oRDD:Append(TRUE)
                // Get the data array from the workarea
                VAR data := GetData()
                // and fetch its values. This automatically updates the array that is owned by the RDD
                oDataReader:GetValues(data)
                IF SELF:Asynchronous .AND. ++nCounter % 100 == 0
                    IF SELF:_aSyncState == AsyncState.Cancelling
                        EXIT
                    ENDIF
                ENDIF

            ENDDO
        ELSE
            VAR data := OBJECT[]{nFields+1}  // 1 extra for the NullFlags
            DO WHILE oDataReader:Read()
                oRDD:Append(TRUE)
                // use our local data array
                oDataReader:GetValues(data)
                // and write the values to the RDD
                FOR VAR nFld := 1 UPTO nFields
                    oRDD:PutValue(nFld, data[nFld])
                NEXT
                IF SELF:Asynchronous .AND. ++nCounter % 100 == 0
                    IF SELF:_aSyncState == AsyncState.Cancelling
                        EXIT
                    ENDIF
                ENDIF
            ENDDO
        ENDIF
        RETURN TRUE

    PRIVATE METHOD _CreateWorkarea(oSchema AS DataTable, oDataReader AS DbDataReader, cCursorName AS STRING) AS LOGIC
        LOCAL oRDD AS IRdd
        oRDD := SELF:CreateFile(oSchema, cCursorName)
        SELF:_CopyFromReaderToRDD(oDataReader, oRDD)
        RETURN TRUE

    PRIVATE METHOD _ReturnsRows(cCommand AS STRING) AS LOGIC
        LOCAL cStmts := cCommand:Split(";":ToCharArray()) AS STRING[]
        LOCAL aParts := cStmts[cStmts:Length]:Split(" ()":ToCharArray()) AS STRING[]
        IF aParts:Length > 0
            LOCAL cWord := aParts[1]:ToLower() AS STRING
            SWITCH cWord
            CASE "select"
            CASE "execute"
                RETURN TRUE
            // dml
            CASE "insert"
            CASE "delete"
            CASE "update"
            // ddl
            CASE "create"
            CASE "drop"
            CASE "alter"
                RETURN FALSE
            OTHERWISE
                RETURN TRUE  //?
            END SWITCH
        ENDIF
        RETURN FALSE

    CONSTRUCTOR(cDataSource AS STRING, cUser AS STRING, cPassword AS STRING, lShared AS LOGIC)
        SELF:SetDefaults()
        _oConnection    := SQLConnection{cDataSource, cUser, cPassword, lShared}
        SELF:_AllocateCommand()
        RETURN

    CONSTRUCTOR(cConnectionString AS STRING, lShared AS LOGIC)
        _oConnection := SQLConnection{cConnectionString, lShared}
        SELF:_AllocateCommand()
        RETURN

    CONSTRUCTOR(oConnection AS SQLConnection)
        _oConnection := oConnection
        SELF:_AllocateCommand()
        RETURN

    METHOD BeginTransaction AS LOGIC
        IF SELF:UsesTransaction
            IF SELF:Connection:Transaction == NULL
                SELF:Connection:BeginTransaction()
            ENDIF
            SELF:_oNetCommand:Transaction := SELF:Connection:Transaction
        ENDIF
        RETURN TRUE

    METHOD Cancel AS LOGIC
        IF SELF:_aSyncState == AsyncState.Executing
            BEGIN LOCK SELF
                SELF:_aSyncState := AsyncState.Cancelling
            END LOCK
            DO WHILE SELF:_aSyncState == AsyncState.Cancelling
                System.Threading.Thread.Sleep(100)
            ENDDO
            SELF:ThreadComplete()
            BEGIN LOCK SELF
                SELF:_aSyncState := AsyncState.Idle
            END LOCK
            RETURN TRUE
        ENDIF
        RETURN FALSE

    METHOD Commit AS LOGIC
        SELF:_CloseReader()
        RETURN SELF:Connection:CommitTransaction()

    METHOD Rollback AS LOGIC
        SELF:_CloseReader()
        RETURN SELF:Connection:RollbackTransaction()

    METHOD DisConnect AS LOGIC
        SELF:_CloseReader()
        IF SELF:Connected
            IF SELF:DisconnectRollback
                SELF:Rollback()
            ELSE
                SELF:Commit()
            ENDIF
            _oNetCommand:Dispose()
            IF ! SELF:Connection:Shared
                SELF:Connection:Close()
            ENDIF
        ENDIF
        _oConnection:RemoveStatement(SELF)
        _oNetCommand := NULL
        _oConnection := NULL
        RETURN TRUE

    METHOD Prepare(cCommand AS STRING, cCursorName AS STRING) AS LONG
        SELF:_CloseReader()
        SELF:_oNetCommand:CommandText := cCommand
        SELF:_oNetCommand:Prepare()
        SELF:Prepared := TRUE
        SELF:CursorName := cCursorName
        RETURN 1

    PRIVATE METHOD _SaveResult(aInfo AS ARRAY) AS LONG
        CopyToInfo(_aQueryResult, aInfo)
        IF ALen(_aQueryResult) > 0
            RETURN (LONG) ALen(_aQueryResult)
        ENDIF
        RETURN 0

    METHOD _AsyncExecuteResult(aInfo AS ARRAY) AS LONG
        IF SELF:Asynchronous
            SWITCH SELF:_aSyncState
            CASE AsyncState.Executing
                RETURN 0
            CASE AsyncState.Ready
                SELF:ThreadComplete()
                RETURN SELF:_SaveResult(aInfo)
            CASE AsyncState.Idle
            CASE AsyncState.Cancelled
                SELF:CopyToCursorAsync()
                RETURN SELF:_SaveResult(aInfo)
            CASE AsyncState.Exception
                RETURN -1
            CASE AsyncState.Cancelling
                RETURN 0
            END SWITCH
        ENDIF
        RETURN 0
    METHOD Execute(cCommand AS STRING, cCursorName AS STRING, aInfo AS ARRAY) AS LONG
        SELF:BeginTransaction()
        SELF:_oNetCommand:CommandText := cCommand
        SELF:CursorName := cCursorName
        IF SELF:Asynchronous
            RETURN SELF:_AsyncExecuteResult(aInfo)
        ELSE
            SELF:CopyToCursor()
        ENDIF
        RETURN SELF:_SaveResult(aInfo)

    METHOD Execute(aInfo AS ARRAY) AS LONG
        SELF:BeginTransaction()
        IF SELF:Asynchronous
            RETURN SELF:_AsyncExecuteResult(aInfo)
        ELSE
            SELF:CopyToCursor()
        ENDIF
        RETURN SELF:_SaveResult(aInfo)

    PRIVATE METHOD CopyToInfo(aResult AS ARRAY, aInfo AS ARRAY) AS VOID
        IF aResult != NULL_ARRAY
            ASize(aInfo, ALen(aResult))
            IF ALen(aInfo) > 0
                ACopy(aResult, aInfo)
            ENDIF
        ELSE
            ASize(aInfo, 0)
        ENDIF
        RETURN

    METHOD BackgroundWorker(o AS OBJECT) AS VOID STRICT
        LOCAL aResults := List<IRdd>{}
        TRY
            VAR oDataReader := SELF:_oNetCommand:ExecuteReader()
            LOCAL cursorNo := 0 AS LONG
            BEGIN LOCK SELF
                SELF:_oLastDataReader := oDataReader
            END LOCK
            DO WHILE TRUE
                VAR oSchema := oDataReader:GetSchemaTable()
                VAR cursorName := SELF:CursorName
                IF cursorNo != 0
                    cursorName += cursorNo:ToString()
                ENDIF
                LOCAL oRDD AS IRdd
                oRDD := SELF:CreateFile(oSchema, cursorName)
                SELF:_CopyFromReaderToRDD(oDataReader, oRDD)
                aResults:Add(oRDD)
                IF SELF:_aSyncState == AsyncState.Cancelling
                    EXIT
                ENDIF
                IF ! oDataReader:NextResult()
                    EXIT
                ENDIF
            ENDDO
            BEGIN LOCK SELF
                SELF:_aResult    := aResults
                IF SELF:_aSyncState == AsyncState.Executing
                    SELF:_aSyncState := AsyncState.Ready
                ENDIF
            END LOCK
        CATCH e AS Exception
            BEGIN LOCK SELF
                SELF:_CloseReader()
                SELF:_aSyncState := AsyncState.Exception
                IF SELF:_oThread:ThreadState == ThreadState.Running
                    SELF:_oThread:Abort()
                ENDIF
                SELF:_oThread := NULL
            END LOCK
        END TRY
         RETURN

    METHOD ThreadComplete() AS VOID STRICT
        SELF:_aQueryResult := {}
        IF SELF:_aResult == NULL
            SELF:_aSyncState := AsyncState.Idle
            RETURN
        ENDIF
        VAR aRDDs := SELF:_aResult
        SELF:_aResult := NULL
        FOREACH oRdd AS IRdd IN aRDDs
            VAR name := oRdd:Alias
            VAR count := oRdd:RecCount
            AAdd(SELF:_aQueryResult, {name, count})
            VAR nArea := RuntimeState.Workareas.FindAlias(name)
            IF nArea > 0
                RuntimeState.Workareas.CloseArea(nArea)
            ELSE
                nArea := RuntimeState.Workareas.FindEmptyArea(TRUE)
            ENDIF
            RuntimeState.Workareas.SetArea(nArea, oRdd)
        NEXT
        IF SELF:_aSyncState == AsyncState.Ready
            SELF:_aSyncState := AsyncState.Idle
        ENDIF

    PRIVATE METHOD CopyToCursorAsync() AS VOID
        IF SELF:_ReturnsRows(SELF:_oNetCommand:CommandText)
            _aQueryResult := {}
            IF SELF:_aSyncState == AsyncState.Idle
                BEGIN LOCK SELF
                    SELF:_CloseReader()
                    _oThread := Thread{BackgroundWorker}
                    SELF:_aSyncState := AsyncState.Executing
                    _oThread:Start(SELF)
                END LOCK
            ENDIF
        ELSE
            VAR result := SELF:_oNetCommand:ExecuteNonQuery()
            _aQueryResult := {{"", result}}
        ENDIF

    PRIVATE METHOD CopyToCursor() AS VOID
        IF SELF:_ReturnsRows(SELF:_oNetCommand:CommandText)
            VAR oDataReader := SELF:_oNetCommand:ExecuteReader()
            CopyToCursor(oDataReader, 0)
        ELSE
            VAR result := SELF:_oNetCommand:ExecuteNonQuery()
            _aQueryResult := {{"", result}}
        ENDIF

    PRIVATE METHOD CopyToCursor(oDataReader AS DbDataReader, cursorNo AS LONG) AS VOID
        LOCAL result   := {} AS ARRAY
        _oLastDataReader := NULL_OBJECT
        DO WHILE TRUE
            VAR cursorName := SELF:CursorName
            IF cursorNo != 0
                cursorName += cursorNo:ToString()
            ENDIF
    		oDataReader := SELF:Connection:Factory:AfterOpen(oDataReader)
            VAR oSchema := oDataReader:GetSchemaTable()
            SELF:_CreateWorkarea(oSchema, oDataReader, cursorName)
            AAdd(result, {cursorName, RecCount()})
            cursorNo += 1
            IF ! SELF:BatchMode
                _oLastDataReader := oDataReader
                _nextCursorNo    := cursorNo
                EXIT
            ENDIF
            IF ! oDataReader:NextResult()
                EXIT
            ENDIF
        ENDDO
        _aQueryResult := result
        RETURN

    METHOD MoreResults(cursorName AS STRING, aInfo AS ARRAY) AS LONG
        IF !String.IsNullOrEmpty(cursorName)
            SELF:CursorName := cursorName
            SELF:_nextCursorNo := 0
        ENDIF
        IF _oLastDataReader != NULL .AND. _oLastDataReader:NextResult()
            CopyToCursor(_oLastDataReader, _nextCursorNo)
            RETURN SELF:_SaveResult(aInfo)
        ENDIF
        ASize(aInfo,1)
        aInfo[1] := {{0, -1}}
        RETURN 0

    PRIVATE METHOD GetNumRestrictions(cCollectionName AS STRING) AS LONG
        LOCAL nRestrictions := 0 AS LONG
        LOCAL oTable := SELF:Connection:NetConnection:GetSchema("MetadataCollections",NULL) AS DataTable
        FOREACH oRow AS DataRow IN oTable:Rows
			IF String.Compare( (STRING)oRow:Item["CollectionName"], cCollectionName, StringComparison.OrdinalIgnoreCase) == 0
				nRestrictions := (INT) oRow:Item["NumberOfRestrictions"]
				EXIT
			ENDIF
        NEXT
        RETURN nRestrictions

    #region MetaData
   METHOD GetTables(cType AS STRING, cCursorName AS STRING) AS LOGIC
        SELF:CursorName := cCursorName
        VAR types := cType:Split(",":ToCharArray(),StringSplitOptions.RemoveEmptyEntries)
        VAR list  := List<STRING>{}
        FOREACH VAR type IN types
            VAR sType := type
            IF sType:StartsWith("'")
                sType := sType:Replace("'","")
            ELSEIF sType:StartsWith(e"\"")
                sType := sType:Replace(e"\"","")
            ENDIF
            list:Add(sType)
        NEXT

        LOCAL filter AS STRING[]
        LOCAL oTables AS List<DataTable>
        LOCAL oTable AS DataTable
        LOCAL nRestrictions AS LONG
        oTables := List<DataTable>{}

        nRestrictions := GetNumRestrictions("Tables")
        filter := STRING[]{nRestrictions}
        oTable := SELF:Connection:NetConnection:GetSchema("Tables", filter)
        oTables:Add(oTable)

        nRestrictions := GetNumRestrictions("Views")
        filter := STRING[]{nRestrictions}
        oTable := SELF:Connection:NetConnection:GetSchema("Views", filter)
        oTables:Add(oTable)

        LOCAL aStruct AS ARRAY
        aStruct := ArrayNew(5)
        aStruct[1] := {"TABLE_CAT","C:0",128,0}
        aStruct[2] := {"TABLE_SCHEM","C:0",128,0}
        aStruct[3] := {"TABLE_NAME","C:0",128,0}
        aStruct[4] := {"TABLE_TYPE","C:0",32,0}
        aStruct[5] := {"REMARKS","C:0",254,0}
        VAR cTemp := System.IO.Path.GetTempFileName()
        DbCreate(cTemp, aStruct, "DBFVFP")
        VAR nArea := _SelectString(cCursorName)
        IF nArea != 0
            DbCloseArea()
        ENDIF
        VoDbUseArea(TRUE, "DBFVFPSQL",cTemp,cCursorName,FALSE,FALSE)
        LOCAL oRDD AS IRdd
        oRDD := (IRdd) DbInfo(DbInfo.DBI_RDD_OBJECT)
        FOREACH VAR oT IN oTables
            FOREACH oRow AS DataRow IN oT:Rows
                VAR oValues := SELF:Connection:Factory:GetMetaDataTableValues(oRow)
                VAR match := TRUE
                IF list:Count > 0
                    match := list:Contains((STRING) oValues[4])
                ENDIF
                IF match
                    oRDD:Append(TRUE)
                    FOR VAR nFld := 1 TO 5
                        oRDD:PutValue(nFld, oValues[nFld])
                    NEXT
                ENDIF
            NEXT
       NEXT
       RETURN TRUE

   METHOD GetColumnsFox(cTableName AS STRING, cCursorName AS STRING) AS LOGIC
        SELF:_oNetCommand:CommandText := "Select * from "+cTableName+" where 1 = 0"
        VAR oDataReader := SELF:_oNetCommand:ExecuteReader()
        VAR oSchema := oDataReader:GetSchemaTable()
        VAR aFieldNames := List<STRING>{}
        VAR aStruct := ArrayNew(4)
        aStruct[1] := {"FIELD_NAME", "C", 14,0}
        aStruct[2] := {"FIELD_TYPE", "C", 1,0}
        aStruct[3] := {"FIELD_LEN", "N", 3,0}
        aStruct[4] := {"FIELD_DEC", "N", 3,0}
        VAR cTemp := System.IO.Path.GetTempFileName()
        DbCreate(cTemp, aStruct, "DBFVFP")
        VoDbUseArea(TRUE, "DBFVFPSQL",cTemp,cCursorName,FALSE,FALSE)
        LOCAL oRDD AS IRdd
        oRDD := (IRdd) DbInfo(DbInfo.DBI_RDD_OBJECT)
        FOREACH schemaRow AS DataRow IN oSchema:Rows
            VAR fieldInfo    := FromSchema(schemaRow, aFieldNames)
            oRDD:Append(TRUE)
            oRDD:PutValue(1, fieldInfo:Name)
            oRDD:PutValue(2, Left(fieldInfo:FieldTypeStr,1))
            oRDD:PutValue(3, fieldInfo:Length)
            oRDD:PutValue(4, fieldInfo:Decimals)
            oRDD:GoCold()
        NEXT
        RETURN TRUE

   METHOD GetColumnsNative(cTableName AS STRING, cCursorName AS STRING) AS LOGIC
        LOCAL filter AS STRING[]
        LOCAL oTable AS DataTable
        LOCAL nRestrictions AS LONG
        nRestrictions := GetNumRestrictions("Columns")
        filter := STRING[]{nRestrictions}
        filter[3] := cTableName
        oTable := SELF:Connection:NetConnection:GetSchema("Columns", filter)
        LOCAL aStruct AS ARRAY
        aStruct := ArrayNew(19)
        aStruct[1] := {"TABLE_CAT","C:0",128,0}
        aStruct[2] := {"TABLE_SCHE","C:0",128,0}
        aStruct[3] := {"TABLE_NAME","C",128,0}
        aStruct[4] := {"COLUMN_NAM","C",128,0}
        aStruct[5] := {"DATA_TYPE","I",4,0}
        aStruct[6] := {"TYPE_NAME","C",128,0}
        aStruct[7] := {"COLUMN_SIZ","I:0",4,0}
        aStruct[8] := {"BUFFER_LEN","I:0",4,0}
        aStruct[9] := {"DECIMAL_DI","I:0",4,0}
        aStruct[10] := {"NUM_PREC_R","I:0",4,0}
        aStruct[11] := {"NULLABLE","I",4,0}
        aStruct[12] := {"REMARKS","C:0",254,0}
        aStruct[13] := {"COLUMN_DEF","M:0",4,0}
        aStruct[14] := {"SQL_DATA_T","I",4,0}
        aStruct[15] := {"SQL_DATETI","I:0",4,0}
        aStruct[16] := {"CHAR_OCTET","I:0",4,0}
        aStruct[17] := {"ORDINAL_PO","I",4,0}
        aStruct[18] := {"IS_NULLABL","C:0",254,0}
        aStruct[19] := {"SS_DATA_TY","I:0",4,0}
        VAR cTemp := System.IO.Path.GetTempFileName()
        DbCreate(cTemp, aStruct, "DBFVFP")
        VAR nArea := _SelectString(cCursorName)
        IF nArea != 0
            DbCloseArea()
        ENDIF
        VoDbUseArea(TRUE, "DBFVFPSQL",cTemp,cCursorName,FALSE,FALSE)
        LOCAL oRDD AS IRdd
        oRDD := (IRdd) DbInfo(DbInfo.DBI_RDD_OBJECT)
        FOREACH oRow AS DataRow IN oTable:Rows
            oRDD:Append(TRUE)
            VAR oValues := SELF:Connection:Factory:GetMetaDataColumnValues(oRow)
            FOR VAR i := 1 TO oValues:Length
                oRDD:PutValue(i, oValues[i])
            NEXT
         NEXT
       RETURN TRUE

   METHOD GetColumns(cTableName AS STRING, cType AS STRING, cCursorName AS STRING) AS LOGIC
        SELF:CursorName := cCursorName
        IF String.IsNullOrEmpty(cTableName) .OR. String.IsNullOrEmpty(cType)
            RETURN FALSE
        ENDIF
        SWITCH cType
        CASE "FOXPRO"
            RETURN GetColumnsFox(cTableName, cCursorName)
        CASE "NATIVE"
            RETURN GetColumnsNative(cTableName, cCursorName)
        END SWITCH
        RETURN FALSE
    #endregion

    STATIC METHOD FromSchema(schemaRow AS DataRow, aFieldNames AS IList<STRING>) AS XSharp.RDD.DbColumnInfo
		LOCAL nLen, nDec AS LONG
		LOCAL cType AS STRING
		LOCAL oType	AS System.Type
		LOCAL TC AS TypeCode
        LOCAL result AS DbColumnInfo
        LOCAL columnName AS STRING
        columnName   := schemaRow["ColumnName"]:ToString( )
        oType   := (Type) schemaRow["DataType"]
		TC      := Type.GetTypeCode(oType)
		nDec    := 0
		SWITCH TC
		CASE TypeCode.String
			cType   := "C"
			nLen    := (Int32)schemaRow["ColumnSize"]
			// Automatically Convert Long Strings to Memos
			IF nLen  > 255 .OR. nLen < 0
				nLen    := 4
				cType   := "M"
            ENDIF
            result := DbColumnInfo{columnName,cType,nLen ,0 }

		CASE TypeCode.Boolean
			result := DbColumnInfo{columnName,"L",1 ,0 }

        CASE TypeCode.Decimal
		    result := DbColumnInfo{columnName,"Y",16 ,4 }
            result:NumericScale     := 16
            result:NumericPrecision := 4

		CASE TypeCode.Double
        CASE TypeCode.Single
			nDec := 1
			nLen := 10
            VAR nScale := Convert.ToInt32(schemaRow:Item["NumericScale"])
            VAR nPrec  := Convert.ToInt32(schemaRow:Item["NumericPrecision"])
            IF nScale == 255
                nScale := 2
            ENDIF
			nDec := nScale
			nLen := nPrec
            IF nLen == 0
				// I have seen a case where nDec == 31 and nLen == 0
				// Fix this to something usefull
				nDec := 2
				nLen := 10
			ELSEIF nDec == 127
				//IF nLen == 38 .AND. oProviderType = ProviderType.Oracle // Standardvalue for calculated fields in oracle-queries, cutting decimals leads to wrong results in that case
				//	nDec := 10
				//ELSE
					nDec := 0 // Overflow abfangen
				//ENDIF
            ENDIF
            result := DbColumnInfo{columnName,"N",nLen ,nDec }
            result:NumericScale     := nScale
            result:NumericPrecision := nPrec

		CASE TypeCode.Int32		// -2147483647 - 2147483648 (2^31)
            result := DbColumnInfo{columnName,"I",4 ,0}
            IF (LOGIC)schemaRow["IsAutoIncrement"]
                result:Flags |= DBFFieldFlags.AutoIncrement
            ENDIF

		CASE TypeCode.Int64		// - 9223372036854775807 - 9223372036854775808 (2^63)
            result := DbColumnInfo{columnName,"N",21 ,0}

		CASE TypeCode.Int16	// -32767 - 32768 (2^15)
            result := DbColumnInfo{columnName,"N",6 ,0}

		CASE TypeCode.Byte
            result := DbColumnInfo{columnName,"N",4 ,0}

		CASE TypeCode.SByte	// 0 - 255 	(2^8)
            result := DbColumnInfo{columnName,"N",3 ,0}

		CASE TypeCode.UInt16	// 0 - 65535 (2^16)
            result := DbColumnInfo{columnName,"N",5 ,0}

		CASE TypeCode.UInt32		// 0 - 4294836225 (2^32)
            result := DbColumnInfo{columnName,"N",10 ,0}

		CASE TypeCode.UInt64	// 0 - 18445618199572250625 (2^64)
			nLen := 20
            result := DbColumnInfo{columnName,"N",nLen ,0}

		CASE TypeCode.DateTime
			cType   := "T"
			nLen 	:= 8
            result  := DbColumnInfo{columnName,cType,nLen ,0}

        CASE TypeCode.Object
            IF oType == typeof(BYTE[])
			    cType   := "P"
			    nLen 	:= 4
                result  := DbColumnInfo{columnName,cType,nLen ,0}

            ELSE
			    LOCAL lIsDate := FALSE AS LOGIC
			    LOCAL oMems AS MethodInfo[]
			    LOCAL lFound := FALSE AS LOGIC
			    // check to see if the datatype has a dbType
			    oMems := oType:GetMethods(BindingFlags.Public|BindingFlags.Static)
			    FOREACH oMem AS MethodInfo IN oMems
				    IF oMem:ReturnType == TypeOf(System.DateTime)  .AND. String.Compare(oMem:Name, "op_Explicit", StringComparison.OrdinalIgnoreCase) == 0
					    lIsDate := TRUE
					    lFound  := TRUE
					    EXIT
				    ENDIF
			    NEXT
			    IF ! lFound
				    LOCAL cTypeName AS STRING
				    cTypeName := oType:Name:ToUpperInvariant()
				    lIsDate     := cTypeName:Contains("DATE")
			    ENDIF
			    IF lIsDate
				    cType   := "D"
				    nLen 	:= 8
			    ELSE
				    cType 	:= "C"
				    nLen 	:= 10
			    ENDIF
                result := DbColumnInfo{columnName,cType,nLen ,0}
            ENDIF
		OTHERWISE
			cType := "C"
			nLen 	:= (Int32)schemaRow["ColumnSize"]
			IF nLen <= 0
				cType := "M"
                nLen  := 4
			ENDIF
            result := DbColumnInfo{columnName,cType,nLen ,0}

        END SWITCH
        IF (LOGIC)schemaRow["AllowDBNull"]
            result:Flags |= DBFFieldFlags.Nullable
        ENDIF
        VAR cFldName        := SQLSupport.CleanupColumnName(columnName)
        result:ColumnName   := columnName
        result:Name         := MakeFieldNameUnique(cFldName, aFieldNames)
        result:Alias        := result:Name
        result:DotNetType   := oType
		RETURN result

   STATIC METHOD MakeFieldNameUnique(cName AS STRING, aFldNames AS IList<STRING> ) AS STRING
        LOCAL dwPos, dwFld AS LONG
        LOCAL cNewname		 AS STRING
        IF String.IsNullOrEmpty(cName)
            dwFld := 0
            dwPos := 1
            DO WHILE dwPos >= 0
                ++dwFld
                cName := "FLD"+dwFld:ToString():PadLeft(3,c'0')
                dwPos := aFldNames:IndexOf(cName:ToUpper())
            ENDDO
        ELSE
            // remove column prefixes
            dwPos := cName:IndexOf(".")+1
            IF dwPos > 0
                cName := cName:Substring(dwPos)
            ENDIF
            // remove embedded spaces
            cName 	:= cName:Replace(" ", "_"):ToUpper()
            cNewname := Left(cName,10)
            dwFld 	:= 1
            DO WHILE aFldNames:IndexOf(cNewname) >= 0
                ++dwFld
                VAR tmp := dwFld:ToString()
                cNewname := cName:Substring(0, 10 - tmp:Length)+tmp
            ENDDO
            cName 	:= cNewname
        ENDIF
        aFldNames:Add(cName)
    RETURN cName

END CLASS

INTERNAL DELEGATE SqlGetData() AS OBJECT[]

INTERNAL ENUM XSharp.VFP.AsyncState
    MEMBER Idle         := 0
    MEMBER Executing    := 1
    MEMBER Ready        := 2
    MEMBER Cancelling   := 3
    MEMBER Cancelled    := 4
    MEMBER Exception    := 5
END ENUM


QtQuickWidgets: data for code generation updated

Modified:
codegen/QtQuickWidgets/QQuickWidget.prg

$header

#include "hbclass.ch"

#ifndef QT5XHB_NO_REQUESTS
REQUEST QURL
REQUEST QQMLENGINE
REQUEST QQMLCONTEXT
REQUEST QQUICKITEM
REQUEST QQMLERROR
REQUEST QSIZE
REQUEST QSURFACEFORMAT
#endif

CLASS QQuickWidget INHERIT QWidget

   DATA self_destruction INIT .F.

   METHOD new
   METHOD delete
   METHOD source
   METHOD setSource
   METHOD engine
   METHOD rootContext
   METHOD rootObject
   METHOD resizeMode
   METHOD setResizeMode
   METHOD status
   METHOD errors
   METHOD sizeHint
   METHOD initialSize
   METHOD setContent
   METHOD format
   METHOD setFormat

   METHOD onStatusChanged
   METHOD onSceneGraphError

   DESTRUCTOR destroyObject

END CLASS

$destructor

#pragma BEGINDUMP

$includes=5,3,0

#include <QQmlError>

/*
explicit QQuickWidget(QWidget *parent = 0)
*/
void QQuickWidget_new1 ()
{
#if (QT_VERSION >= QT_VERSION_CHECK(5,3,0))
  QQuickWidget * o = new QQuickWidget ( OPQWIDGET(1,0) );
  _qt5xhb_storePointerAndFlag( o, false );
#endif
}

/*
QQuickWidget(QQmlEngine* engine, QWidget *parent)
*/
void QQuickWidget_new2 ()
{
#if (QT_VERSION >= QT_VERSION_CHECK(5,3,0))
  QQuickWidget * o = new QQuickWidget ( PQQMLENGINE(1), PQWIDGET(2) );
  _qt5xhb_storePointerAndFlag( o, false );
#endif
}

/*
QQuickWidget(const QUrl &source, QWidget *parent = 0)
*/
void QQuickWidget_new3 ()
{
#if (QT_VERSION >= QT_VERSION_CHECK(5,3,0))
  QQuickWidget * o = new QQuickWidget ( *PQURL(1), OPQWIDGET(2,0) );
  _qt5xhb_storePointerAndFlag( o, false );
#endif
}

//[1]explicit QQuickWidget(QWidget *parent = 0)
//[2]QQuickWidget(QQmlEngine* engine, QWidget *parent)
//[3]QQuickWidget(const QUrl &source, QWidget *parent = 0)

HB_FUNC_STATIC( QQUICKWIDGET_NEW )
{
#if (QT_VERSION >= QT_VERSION_CHECK(5,3,0))
  if( ISBETWEEN(0,1) && ISOPTQWIDGET(1) )
  {
    QQuickWidget_new1();
  }
  else if( ISNUMPAR(2) && ISQQMLENGINE(1) && ISQWIDGET(2) )
  {
    QQuickWidget_new2();
  }
  else if( ISBETWEEN(1,2) && ISQURL(1) && ISOPTQWIDGET(2) )
  {
    QQuickWidget_new3();
  }
  else
  {
    hb_errRT_BASE( EG_ARG, 3012, NULL, HB_ERR_FUNCNAME, HB_ERR_ARGS_BASEPARAMS );
  }
#endif
}

$deleteMethod=5,3,0

/*
QUrl source() const
*/
HB_FUNC_STATIC( QQUICKWIDGET_SOURCE )
{
#if (QT_VERSION >= QT_VERSION_CHECK(5,3,0))
  QQuickWidget * obj = (QQuickWidget *) _qt5xhb_itemGetPtrStackSelfItem();

  if( obj )
  {
    QUrl * ptr = new QUrl( obj->source () );
    _qt5xhb_createReturnClass ( ptr, "QURL", true );
  }
#endif
}

/*
void setSource(const QUrl&)
*/
HB_FUNC_STATIC( QQUICKWIDGET_SETSOURCE )
{
#if (QT_VERSION >= QT_VERSION_CHECK(5,3,0))
  QQuickWidget * obj = (QQuickWidget *) _qt5xhb_itemGetPtrStackSelfItem();

  if( obj )
  {
    if( ISQURL(1) )
    {
      obj->setSource ( *PQURL(1) );
    }
    else
    {
      hb_errRT_BASE( EG_ARG, 3012, NULL, HB_ERR_FUNCNAME, HB_ERR_ARGS_BASEPARAMS );
    }
  }

  hb_itemReturn( hb_stackSelfItem() );
#endif
}

/*
QQmlEngine* engine() const
*/
HB_FUNC_STATIC( QQUICKWIDGET_ENGINE )
{
#if (QT_VERSION >= QT_VERSION_CHECK(5,3,0))
  QQuickWidget * obj = (QQuickWidget *) _qt5xhb_itemGetPtrStackSelfItem();

  if( obj )
  {
    QQmlEngine * ptr = obj->engine ();
    _qt5xhb_createReturnClass ( ptr, "QQMLENGINE" );
  }
#endif
}

/*
QQmlContext* rootContext() const
*/
HB_FUNC_STATIC( QQUICKWIDGET_ROOTCONTEXT )
{
#if (QT_VERSION >= QT_VERSION_CHECK(5,3,0))
  QQuickWidget * obj = (QQuickWidget *) _qt5xhb_itemGetPtrStackSelfItem();

  if( obj )
  {
    QQmlContext * ptr = obj->rootContext ();
    _qt5xhb_createReturnClass ( ptr, "QQMLCONTEXT" );
  }
#endif
}

/*
QQuickItem *rootObject() const
*/
HB_FUNC_STATIC( QQUICKWIDGET_ROOTOBJECT )
{
#if (QT_VERSION >= QT_VERSION_CHECK(5,3,0))
  QQuickWidget * obj = (QQuickWidget *) _qt5xhb_itemGetPtrStackSelfItem();

  if( obj )
  {
    QQuickItem * ptr = obj->rootObject ();
    _qt5xhb_createReturnClass ( ptr, "QQUICKITEM" );
  }
#endif
}

/*
ResizeMode resizeMode() const
*/
HB_FUNC_STATIC( QQUICKWIDGET_RESIZEMODE )
{
#if (QT_VERSION >= QT_VERSION_CHECK(5,3,0))
  QQuickWidget * obj = (QQuickWidget *) _qt5xhb_itemGetPtrStackSelfItem();

  if( obj )
  {
    hb_retni( obj->resizeMode () );
  }
#endif
}

/*
void setResizeMode(ResizeMode)
*/
HB_FUNC_STATIC( QQUICKWIDGET_SETRESIZEMODE )
{
#if (QT_VERSION >= QT_VERSION_CHECK(5,3,0))
  QQuickWidget * obj = (QQuickWidget *) _qt5xhb_itemGetPtrStackSelfItem();

  if( obj )
  {
    if( ISNUM(1) )
    {
      obj->setResizeMode ( (QQuickWidget::ResizeMode) hb_parni(1) );
    }
    else
    {
      hb_errRT_BASE( EG_ARG, 3012, NULL, HB_ERR_FUNCNAME, HB_ERR_ARGS_BASEPARAMS );
    }
  }

  hb_itemReturn( hb_stackSelfItem() );
#endif
}

/*
Status status() const
*/
HB_FUNC_STATIC( QQUICKWIDGET_STATUS )
{
#if (QT_VERSION >= QT_VERSION_CHECK(5,3,0))
  QQuickWidget * obj = (QQuickWidget *) _qt5xhb_itemGetPtrStackSelfItem();

  if( obj )
  {
    hb_retni( obj->status () );
  }
#endif
}

/*
QList<QQmlError> errors() const
*/
HB_FUNC_STATIC( QQUICKWIDGET_ERRORS )
{
#if (QT_VERSION >= QT_VERSION_CHECK(5,3,0))
  QQuickWidget * obj = (QQuickWidget *) _qt5xhb_itemGetPtrStackSelfItem();

  if( obj )
  {
    QList<QQmlError> list = obj->errors ();
    PHB_DYNS pDynSym;
    #ifdef __XHARBOUR__
    pDynSym = hb_dynsymFind( "QQMLERROR" );
    #else
    pDynSym = hb_dynsymFindName( "QQMLERROR" );
    #endif
    PHB_ITEM pArray;
    pArray = hb_itemArrayNew(0);
    int i;
    for(i=0;i<list.count();i++)
    {
      if( pDynSym )
      {
        #ifdef __XHARBOUR__
        hb_vmPushSymbol( pDynSym->pSymbol );
        #else
        hb_vmPushDynSym( pDynSym );
        #endif
        hb_vmPushNil();
        hb_vmDo( 0 );
        PHB_ITEM pObject = hb_itemNew( NULL );
        hb_itemCopy( pObject, hb_stackReturnItem() );
        PHB_ITEM pItem = hb_itemNew( NULL );
        hb_itemPutPtr( pItem, (QQmlError *) new QQmlError ( list[i] ) );
        hb_objSendMsg( pObject, "_POINTER", 1, pItem );
        hb_itemRelease( pItem );
        hb_arrayAddForward( pArray, pObject );
        hb_itemRelease( pObject );
      }
    }
    hb_itemReturnRelease(pArray);
  }
#endif
}

/*
QSize sizeHint() const
*/
HB_FUNC_STATIC( QQUICKWIDGET_SIZEHINT )
{
#if (QT_VERSION >= QT_VERSION_CHECK(5,3,0))
  QQuickWidget * obj = (QQuickWidget *) _qt5xhb_itemGetPtrStackSelfItem();

  if( obj )
  {
    QSize * ptr = new QSize( obj->sizeHint () );
    _qt5xhb_createReturnClass ( ptr, "QSIZE", true );
  }
#endif
}

/*
QSize initialSize() const
*/
HB_FUNC_STATIC( QQUICKWIDGET_INITIALSIZE )
{
#if (QT_VERSION >= QT_VERSION_CHECK(5,3,0))
  QQuickWidget * obj = (QQuickWidget *) _qt5xhb_itemGetPtrStackSelfItem();

  if( obj )
  {
    QSize * ptr = new QSize( obj->initialSize () );
    _qt5xhb_createReturnClass ( ptr, "QSIZE", true );
  }
#endif
}

/*
void setContent(const QUrl& url, QQmlComponent *component, QObject *item)
*/
HB_FUNC_STATIC( QQUICKWIDGET_SETCONTENT )
{
#if (QT_VERSION >= QT_VERSION_CHECK(5,3,0))
  QQuickWidget * obj = (QQuickWidget *) _qt5xhb_itemGetPtrStackSelfItem();

  if( obj )
  {
    if( ISQURL(1) && ISQQMLCOMPONENT(2) && ISQOBJECT(3) )
    {
      obj->setContent ( *PQURL(1), PQQMLCOMPONENT(2), PQOBJECT(3) );
    }
    else
    {
      hb_errRT_BASE( EG_ARG, 3012, NULL, HB_ERR_FUNCNAME, HB_ERR_ARGS_BASEPARAMS );
    }
  }

  hb_itemReturn( hb_stackSelfItem() );
#endif
}

/*
QSurfaceFormat format() const
*/
HB_FUNC_STATIC( QQUICKWIDGET_FORMAT )
{
#if (QT_VERSION >= QT_VERSION_CHECK(5,3,0))
  QQuickWidget * obj = (QQuickWidget *) _qt5xhb_itemGetPtrStackSelfItem();

  if( obj )
  {
    QSurfaceFormat * ptr = new QSurfaceFormat( obj->format () );
    _qt5xhb_createReturnClass ( ptr, "QSURFACEFORMAT" );
  }
#endif
}

/*
void setFormat(const QSurfaceFormat &format)
*/
HB_FUNC_STATIC( QQUICKWIDGET_SETFORMAT )
{
#if (QT_VERSION >= QT_VERSION_CHECK(5,3,0))
  QQuickWidget * obj = (QQuickWidget *) _qt5xhb_itemGetPtrStackSelfItem();

  if( obj )
  {
    if( ISQSURFACEFORMAT(1) )
    {
      obj->setFormat ( *PQSURFACEFORMAT(1) );
    }
    else
    {
      hb_errRT_BASE( EG_ARG, 3012, NULL, HB_ERR_FUNCNAME, HB_ERR_ARGS_BASEPARAMS );
    }
  }

  hb_itemReturn( hb_stackSelfItem() );
#endif
}

#pragma ENDDUMP

/*

  Qt5xHb - bibliotecas de ligao entre Harbour/xHarbour e Qt Framework 5

  Copyright (C) 2012-2017 Marcos Antonio Gambeta <marcosgambeta@uol.com.br>

*/

#include "hbclass.ch"

#ifndef QT5XHB_NO_REQUESTS
REQUEST QURL
REQUEST QQMLENGINE
REQUEST QQMLCONTEXT
REQUEST QQUICKITEM
REQUEST QQMLERROR
REQUEST QSIZE
REQUEST QSURFACEFORMAT
#endif

CLASS QQuickWidget INHERIT QWidget

   DATA self_destruction INIT .F.

   METHOD new
   METHOD delete
   METHOD source
   METHOD setSource
   METHOD engine
   METHOD rootContext
   METHOD rootObject
   METHOD resizeMode
   METHOD setResizeMode
   METHOD status
   METHOD errors
   METHOD sizeHint
   METHOD initialSize
   METHOD setContent
   METHOD format
   METHOD setFormat

   METHOD onStatusChanged
   METHOD onSceneGraphError

   DESTRUCTOR destroyObject

END CLASS

PROCEDURE destroyObject () CLASS QQuickWidget
   IF ::self_destruction
      ::delete()
   ENDIF
RETURN

#pragma BEGINDUMP

#include <Qt>

#ifndef __XHARBOUR__
#if (QT_VERSION >= QT_VERSION_CHECK(5,3,0))
#include <QQuickWidget>
#endif
#endif

#include "qt5xhb_common.h"
#include "qt5xhb_macros.h"
#include "qt5xhb_utils.h"

#ifdef __XHARBOUR__
#if (QT_VERSION >= QT_VERSION_CHECK(5,3,0))
#include <QQuickWidget>
#endif
#endif

#include <QQmlError>

/*
explicit QQuickWidget(QWidget *parent = 0)
*/
void QQuickWidget_new1 ()
{
#if (QT_VERSION >= QT_VERSION_CHECK(5,3,0))
  QQuickWidget * o = new QQuickWidget ( OPQWIDGET(1,0) );
  _qt5xhb_storePointerAndFlag( o, false );
#endif
}

/*
QQuickWidget(QQmlEngine* engine, QWidget *parent)
*/
void QQuickWidget_new2 ()
{
#if (QT_VERSION >= QT_VERSION_CHECK(5,3,0))
  QQuickWidget * o = new QQuickWidget ( PQQMLENGINE(1), PQWIDGET(2) );
  _qt5xhb_storePointerAndFlag( o, false );
#endif
}

/*
QQuickWidget(const QUrl &source, QWidget *parent = 0)
*/
void QQuickWidget_new3 ()
{
#if (QT_VERSION >= QT_VERSION_CHECK(5,3,0))
  QQuickWidget * o = new QQuickWidget ( *PQURL(1), OPQWIDGET(2,0) );
  _qt5xhb_storePointerAndFlag( o, false );
#endif
}

//[1]explicit QQuickWidget(QWidget *parent = 0)
//[2]QQuickWidget(QQmlEngine* engine, QWidget *parent)
//[3]QQuickWidget(const QUrl &source, QWidget *parent = 0)

HB_FUNC_STATIC( QQUICKWIDGET_NEW )
{
#if (QT_VERSION >= QT_VERSION_CHECK(5,3,0))
  if( ISBETWEEN(0,1) && ISOPTQWIDGET(1) )
  {
    QQuickWidget_new1();
  }
  else if( ISNUMPAR(2) && ISQQMLENGINE(1) && ISQWIDGET(2) )
  {
    QQuickWidget_new2();
  }
  else if( ISBETWEEN(1,2) && ISQURL(1) && ISOPTQWIDGET(2) )
  {
    QQuickWidget_new3();
  }
  else
  {
    hb_errRT_BASE( EG_ARG, 3012, NULL, HB_ERR_FUNCNAME, HB_ERR_ARGS_BASEPARAMS );
  }
#endif
}

HB_FUNC_STATIC( QQUICKWIDGET_DELETE )
{
#if (QT_VERSION >= QT_VERSION_CHECK(5,3,0))
  QQuickWidget * obj = (QQuickWidget *) hb_itemGetPtr( hb_objSendMsg( hb_stackSelfItem(), "POINTER", 0 ) );

  if( obj )
  {
    delete obj;
    obj = NULL;
    PHB_ITEM self = hb_stackSelfItem();
    PHB_ITEM ptr = hb_itemPutPtr( NULL, NULL );
    hb_objSendMsg( self, "_pointer", 1, ptr );
    hb_itemRelease( ptr );
  }

  hb_itemReturn( hb_stackSelfItem() );
#endif
}

/*
QUrl source() const
*/
HB_FUNC_STATIC( QQUICKWIDGET_SOURCE )
{
#if (QT_VERSION >= QT_VERSION_CHECK(5,3,0))
  QQuickWidget * obj = (QQuickWidget *) _qt5xhb_itemGetPtrStackSelfItem();

  if( obj )
  {
    QUrl * ptr = new QUrl( obj->source () );
    _qt5xhb_createReturnClass ( ptr, "QURL", true );
  }
#endif
}

/*
void setSource(const QUrl&)
*/
HB_FUNC_STATIC( QQUICKWIDGET_SETSOURCE )
{
#if (QT_VERSION >= QT_VERSION_CHECK(5,3,0))
  QQuickWidget * obj = (QQuickWidget *) _qt5xhb_itemGetPtrStackSelfItem();

  if( obj )
  {
    if( ISQURL(1) )
    {
      obj->setSource ( *PQURL(1) );
    }
    else
    {
      hb_errRT_BASE( EG_ARG, 3012, NULL, HB_ERR_FUNCNAME, HB_ERR_ARGS_BASEPARAMS );
    }
  }

  hb_itemReturn( hb_stackSelfItem() );
#endif
}

/*
QQmlEngine* engine() const
*/
HB_FUNC_STATIC( QQUICKWIDGET_ENGINE )
{
#if (QT_VERSION >= QT_VERSION_CHECK(5,3,0))
  QQuickWidget * obj = (QQuickWidget *) _qt5xhb_itemGetPtrStackSelfItem();

  if( obj )
  {
    QQmlEngine * ptr = obj->engine ();
    _qt5xhb_createReturnClass ( ptr, "QQMLENGINE" );
  }
#endif
}

/*
QQmlContext* rootContext() const
*/
HB_FUNC_STATIC( QQUICKWIDGET_ROOTCONTEXT )
{
#if (QT_VERSION >= QT_VERSION_CHECK(5,3,0))
  QQuickWidget * obj = (QQuickWidget *) _qt5xhb_itemGetPtrStackSelfItem();

  if( obj )
  {
    QQmlContext * ptr = obj->rootContext ();
    _qt5xhb_createReturnClass ( ptr, "QQMLCONTEXT" );
  }
#endif
}

/*
QQuickItem *rootObject() const
*/
HB_FUNC_STATIC( QQUICKWIDGET_ROOTOBJECT )
{
#if (QT_VERSION >= QT_VERSION_CHECK(5,3,0))
  QQuickWidget * obj = (QQuickWidget *) _qt5xhb_itemGetPtrStackSelfItem();

  if( obj )
  {
    QQuickItem * ptr = obj->rootObject ();
    _qt5xhb_createReturnClass ( ptr, "QQUICKITEM" );
  }
#endif
}

/*
ResizeMode resizeMode() const
*/
HB_FUNC_STATIC( QQUICKWIDGET_RESIZEMODE )
{
#if (QT_VERSION >= QT_VERSION_CHECK(5,3,0))
  QQuickWidget * obj = (QQuickWidget *) _qt5xhb_itemGetPtrStackSelfItem();

  if( obj )
  {
    hb_retni( obj->resizeMode () );
  }
#endif
}

/*
void setResizeMode(ResizeMode)
*/
HB_FUNC_STATIC( QQUICKWIDGET_SETRESIZEMODE )
{
#if (QT_VERSION >= QT_VERSION_CHECK(5,3,0))
  QQuickWidget * obj = (QQuickWidget *) _qt5xhb_itemGetPtrStackSelfItem();

  if( obj )
  {
    if( ISNUM(1) )
    {
      obj->setResizeMode ( (QQuickWidget::ResizeMode) hb_parni(1) );
    }
    else
    {
      hb_errRT_BASE( EG_ARG, 3012, NULL, HB_ERR_FUNCNAME, HB_ERR_ARGS_BASEPARAMS );
    }
  }

  hb_itemReturn( hb_stackSelfItem() );
#endif
}

/*
Status status() const
*/
HB_FUNC_STATIC( QQUICKWIDGET_STATUS )
{
#if (QT_VERSION >= QT_VERSION_CHECK(5,3,0))
  QQuickWidget * obj = (QQuickWidget *) _qt5xhb_itemGetPtrStackSelfItem();

  if( obj )
  {
    hb_retni( obj->status () );
  }
#endif
}

/*
QList<QQmlError> errors() const
*/
HB_FUNC_STATIC( QQUICKWIDGET_ERRORS )
{
#if (QT_VERSION >= QT_VERSION_CHECK(5,3,0))
  QQuickWidget * obj = (QQuickWidget *) _qt5xhb_itemGetPtrStackSelfItem();

  if( obj )
  {
    QList<QQmlError> list = obj->errors ();
    PHB_DYNS pDynSym;
    #ifdef __XHARBOUR__
    pDynSym = hb_dynsymFind( "QQMLERROR" );
    #else
    pDynSym = hb_dynsymFindName( "QQMLERROR" );
    #endif
    PHB_ITEM pArray;
    pArray = hb_itemArrayNew(0);
    int i;
    for(i=0;i<list.count();i++)
    {
      if( pDynSym )
      {
        #ifdef __XHARBOUR__
        hb_vmPushSymbol( pDynSym->pSymbol );
        #else
        hb_vmPushDynSym( pDynSym );
        #endif
        hb_vmPushNil();
        hb_vmDo( 0 );
        PHB_ITEM pObject = hb_itemNew( NULL );
        hb_itemCopy( pObject, hb_stackReturnItem() );
        PHB_ITEM pItem = hb_itemNew( NULL );
        hb_itemPutPtr( pItem, (QQmlError *) new QQmlError ( list[i] ) );
        hb_objSendMsg( pObject, "_POINTER", 1, pItem );
        hb_itemRelease( pItem );
        hb_arrayAddForward( pArray, pObject );
        hb_itemRelease( pObject );
      }
    }
    hb_itemReturnRelease(pArray);
  }
#endif
}

/*
QSize sizeHint() const
*/
HB_FUNC_STATIC( QQUICKWIDGET_SIZEHINT )
{
#if (QT_VERSION >= QT_VERSION_CHECK(5,3,0))
  QQuickWidget * obj = (QQuickWidget *) _qt5xhb_itemGetPtrStackSelfItem();

  if( obj )
  {
    QSize * ptr = new QSize( obj->sizeHint () );
    _qt5xhb_createReturnClass ( ptr, "QSIZE", true );
  }
#endif
}

/*
QSize initialSize() const
*/
HB_FUNC_STATIC( QQUICKWIDGET_INITIALSIZE )
{
#if (QT_VERSION >= QT_VERSION_CHECK(5,3,0))
  QQuickWidget * obj = (QQuickWidget *) _qt5xhb_itemGetPtrStackSelfItem();

  if( obj )
  {
    QSize * ptr = new QSize( obj->initialSize () );
    _qt5xhb_createReturnClass ( ptr, "QSIZE", true );
  }
#endif
}

/*
void setContent(const QUrl& url, QQmlComponent *component, QObject *item)
*/
HB_FUNC_STATIC( QQUICKWIDGET_SETCONTENT )
{
#if (QT_VERSION >= QT_VERSION_CHECK(5,3,0))
  QQuickWidget * obj = (QQuickWidget *) _qt5xhb_itemGetPtrStackSelfItem();

  if( obj )
  {
    if( ISQURL(1) && ISQQMLCOMPONENT(2) && ISQOBJECT(3) )
    {
      obj->setContent ( *PQURL(1), PQQMLCOMPONENT(2), PQOBJECT(3) );
    }
    else
    {
      hb_errRT_BASE( EG_ARG, 3012, NULL, HB_ERR_FUNCNAME, HB_ERR_ARGS_BASEPARAMS );
    }
  }

  hb_itemReturn( hb_stackSelfItem() );
#endif
}

/*
QSurfaceFormat format() const
*/
HB_FUNC_STATIC( QQUICKWIDGET_FORMAT )
{
#if (QT_VERSION >= QT_VERSION_CHECK(5,3,0))
  QQuickWidget * obj = (QQuickWidget *) _qt5xhb_itemGetPtrStackSelfItem();

  if( obj )
  {
    QSurfaceFormat * ptr = new QSurfaceFormat( obj->format () );
    _qt5xhb_createReturnClass ( ptr, "QSURFACEFORMAT" );
  }
#endif
}

/*
void setFormat(const QSurfaceFormat &format)
*/
HB_FUNC_STATIC( QQUICKWIDGET_SETFORMAT )
{
#if (QT_VERSION >= QT_VERSION_CHECK(5,3,0))
  QQuickWidget * obj = (QQuickWidget *) _qt5xhb_itemGetPtrStackSelfItem();

  if( obj )
  {
    if( ISQSURFACEFORMAT(1) )
    {
      obj->setFormat ( *PQSURFACEFORMAT(1) );
    }
    else
    {
      hb_errRT_BASE( EG_ARG, 3012, NULL, HB_ERR_FUNCNAME, HB_ERR_ARGS_BASEPARAMS );
    }
  }

  hb_itemReturn( hb_stackSelfItem() );
#endif
}

#pragma ENDDUMP


[Runtime] Fix for ArrayNew() (https://github.com/X-Sharp/XSharpPublic/issues/134)

//
// 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.Collections
USING System.Collections.Generic
USING System.Linq
USING System.Diagnostics
USING System.Reflection
USING System.Text
USING XSharp
BEGIN NAMESPACE XSharp
    /// Internal type that implements the VO Compatible ARRAY type.

    /// This type has methods and properties that normally are never directly called from user code.
    ///
    /// <seealso cref='T:XSharp.IIndexer' />
    /// <include file="RTComments.xml" path="Comments/ZeroBasedIndex/*" />
    [DebuggerDisplay("{DebuggerString(),nq}", Type := "ARRAY")] ;
    [DebuggerTypeProxy(TYPEOF(ArrayDebugView))];
    PUBLIC SEALED CLASS __Array INHERIT __ArrayBase<USUAL> IMPLEMENTS IIndexer

        /// <inheritdoc />
        CONSTRUCTOR()
            SUPER()

            /// <inheritdoc />
        CONSTRUCTOR(capacity AS DWORD)
            SUPER(capacity)

            /// Create an array and fill it with elements from an existing .Net array of USUALS
        CONSTRUCTOR( elements AS USUAL[] )
            SELF()
            IF elements == NULL
                THROW ArgumentNullException{NAMEOF(elements)}
            ENDIF
            _internalList:Capacity := elements:Length
            _internalList:AddRange(elements)
            RETURN

            /// <inheritdoc />
        CONSTRUCTOR( elements AS OBJECT[] )
            SELF()
            IF elements == NULL
                RETURN // empty array
            ENDIF
            FOREACH element AS OBJECT IN elements
                IF element == NULL
                    _internalList:add(DEFAULT(USUAL))
                ELSEIF element IS OBJECT[]
                    LOCAL objects AS OBJECT[]
                    objects := (OBJECT[]) element
                    _internalList:Add( __Array{ objects})
                ELSE
                    _internalList:Add( USUAL{ element})
                ENDIF
            NEXT
            RETURN

        INTERNAL STATIC METHOD ArrayCreate(dimensions PARAMS INT[] ) AS ARRAY
            LOCAL count := dimensions:Length AS INT
            IF count <= 0
                THROW ArgumentException{"No dimensions provided."}
            ENDIF
            LOCAL initializer := OBJECT[]{dimensions[1]} AS OBJECT[]
            LOCAL arrayNew AS ARRAY
            arrayNew := ARRAY{initializer}

            IF count > 1
                LOCAL i AS INT
                FOR i:=0+__ARRAYBASE__  UPTO dimensions[1]-1+__ARRAYBASE__
                    LOCAL newParams := INT[]{count-1} AS INT[]
                    Array.Copy(dimensions,1,newParams,0,count-1)
                    arrayNew:_internalList[(INT) i-__ARRAYBASE__ ] := __ArrayNew(newParams)
                NEXT
            ENDIF
            RETURN arrayNew

        /// <exclude/>
        STATIC METHOD __ArrayNew( dimensions PARAMS INT[] ) AS __Array
            LOCAL newArray AS ARRAY
            IF dimensions:Length != 0
                newArray := __ArrayNewHelper(dimensions,1)
            ELSE
                newArray := ARRAY{}
            ENDIF
            RETURN newArray

        INTERNAL STATIC METHOD __ArrayNewHelper(dimensions AS INT[], currentDim AS INT) AS ARRAY
            LOCAL size AS DWORD
            LOCAL newArray AS ARRAY
            size := (DWORD) dimensions[currentDim]
            // ARRAY{} constructor param is named "capacity", but it adds empty elements to the array, so it is "size" really. We might want to redesign that
            newArray := ARRAY{size}
            IF currentDim != dimensions:Length
                LOCAL nextDim := currentDim+1 AS INT
                LOCAL index   := 1 AS INT
                DO WHILE index <= size
                    newArray[index - 1 + __ARRAYBASE__] := USUAL{__ArrayNewHelper(dimensions,nextDim)}
                    index+=1
                ENDDO
                RETURN newArray
            ENDIF
            RETURN newArray

        INTERNAL NEW METHOD Clone() AS ARRAY
            LOCAL aResult AS ARRAY
            LOCAL nCount AS DWORD
            nCount := (DWORD) _internalList:Count
            aResult := ARRAY{nCount}
            IF nCount == 0
                // warning, nCount-1 below will become MAXDWORD for nCount == 0
                RETURN aResult
            END IF
            FOR VAR I := 0 TO nCount-1
                VAR u := _internalList[i]
                IF u:isArray
                    VAR aElement := (ARRAY) u
                    IF aElement != NULL_ARRAY
                        aResult:_internalList[i] := aElement:Clone()
                    ELSE
                        aResult:_internalList[i] := aElement
                    ENDIF
                ELSE
                    aResult:_internalList[i] := u
                ENDIF
            NEXT
            RETURN aResult

        INTERNAL METHOD CloneShallow() AS ARRAY
            RETURN (ARRAY) SUPER:Clone()

        /// <include file="RTComments.xml" path="Comments/ZeroBasedIndexProperty/*" />
        /// <param name="index"><include file="RTComments.xml" path="Comments/ZeroBasedIndexParam/*" /></param>
        /// <returns>The value of the property of the element stored at the indicated location in the array.</returns>
        PUBLIC PROPERTY SELF[index PARAMS INT[]] AS USUAL
            GET
                RETURN __GetElement(index)
            END GET
            SET
                SELF:__SetElement(VALUE,index)
            END SET
        END PROPERTY

        NEW INTERNAL METHOD Swap(position AS INT, element AS USUAL) AS USUAL
            RETURN SUPER:Swap(position, element)

        /// <include file="RTComments.xml" path="Comments/ZeroBasedIndexProperty/*" />
        /// <param name="index"><include file="RTComments.xml" path="Comments/ZeroBasedIndexParam/*" /></param>
        /// <returns>The element stored at the specified location in the array.</returns>
        PUBLIC METHOD __GetElement(index PARAMS INT[]) AS USUAL
            LOCAL length := index:Length AS INT
            LOCAL currentArray AS ARRAY
            LOCAL i AS INT
            currentArray := SELF
            FOR i:= 1  UPTO length  -1 // walk all but the last level
                LOCAL u := currentArray:_internalList[ index[i] ] AS USUAL
                IF u:IsNil
                    RETURN u
                ENDIF
                IF (OBJECT) u IS IIndexedProperties .AND. i == length-1
                    LOCAL o := (IIndexedProperties) (OBJECT) u AS IIndexedProperties
                    RETURN o[index[length]]
                ENDIF
                IF !u:IsArray
                    THROW InvalidOperationException{"out of range error."}
                ENDIF
                currentArray := (ARRAY) u
            NEXT
            RETURN currentArray:_internalList[ index[length] ]

        INTERNAL METHOD DebuggerString() AS STRING
            LOCAL sb AS StringBuilder
            LOCAL cnt, tot AS LONG
            sb := StringBuilder{}
            sb:Append(SELF:ToString())
            sb:Append("{")
            tot := _internallist:Count
            cnt := 0
            FOREACH VAR element IN SELF:_internallist
                IF cnt > 0
                    sb:Append(",")
                ENDIF
                sb:Append(element:ToString())
                cnt++
                IF cnt > 5
                    IF cnt < tot
                        sb:Append(",..")
                    ENDIF
                    EXIT
                ENDIF
            NEXT
            sb:Append("}")
            RETURN sb:ToString()

        /// <include file="RTComments.xml" path="Comments/ZeroBasedIndexProperty/*" />
        /// <param name="index"><include file="RTComments.xml" path="Comments/ZeroBasedIndexParam/*" /></param>
        /// <param name='u'>New element to store in the array at the position specified</param>
        /// <returns>The new element</returns>
        PUBLIC METHOD __SetElement(u AS USUAL, index PARAMS INT[] ) AS USUAL
            // indices are 0 based
            IF SELF:CheckLock()
                LOCAL length := index:Length AS INT
                LOCAL currentArray := SELF AS ARRAY
                FOR VAR i := 1 UPTO length-1
                    LOCAL uArray := currentArray:_internalList[index[i]] AS USUAL
                    IF (OBJECT) u IS IIndexedProperties .AND. i == length-1
                        LOCAL o := (IIndexedProperties) (OBJECT) u AS IIndexedProperties
                        o[index[length]] := u
                        RETURN u
                    ENDIF
                    IF !(uArray:IsArray)
                        THROW InvalidOperationException{"Out of range error."}
                    ENDIF
                    currentArray := (ARRAY) uArray
                NEXT
                currentArray:_internalList[index[length]] := u
            ENDIF
            RETURN u

       /// Implicit conversion to OBJECT[]. SubArrays become nested OBJECT[] arrays.
        STATIC OPERATOR IMPLICIT ( a AS __Array) AS OBJECT[]
            LOCAL aResult := List<OBJECT>{} AS List<OBJECT>
            FOREACH VAR uElement IN a
                IF uElement:ISArray
                    LOCAL aSubArray AS ARRAY
                    aSubArray := uElement
                    aResult:Add( (OBJECT[]) aSubArray)
                ELSE
                    aResult:Add(  (OBJECT) uElement)
                ENDIF
            NEXT
            RETURN aResult:ToArray()

        /// <exclude/>
        STATIC OPERATOR IMPLICIT ( a AS OBJECT[]) AS __Array
            RETURN __Array{a}

        INTERNAL STATIC METHOD Copy(aSource AS ARRAY,aTarget AS ARRAY,;
            start AS DWORD, sourceLen AS DWORD, offSet AS DWORD, targetLen AS DWORD ) AS VOID
            LOCAL x AS DWORD
            // Adjust
            start-=1
            offSet-=1
            sourceLen-=1
            targetLen-=1
            IF start < sourceLen
                FOR x := start UPTO sourceLen
                    aTarget:_InternalList[(INT) offSet] := aSource:_InternalList[(INT) x]
                    offSet++
                    IF offSet > targetLen
                        EXIT
                    ENDIF
                NEXT
            ELSE
                FOR x := start DOWNTO sourceLen
                    aTarget:_InternalList[(INT) offSet] := aSource:_InternalList[(INT) x]
                    offSet++
                    IF offSet > targetLen
                        EXIT
                    ENDIF
                NEXT
            ENDIF
            RETURN

        NEW INTERNAL METHOD Sort(startIndex AS INT, count AS INT, comparer AS IComparer<__USUAL>) AS VOID
           IF startIndex <= 0
                startIndex := 1
            ENDIF
            IF count < 0
                count := _internalList:Count - startIndex + __ARRAYBASE__
            ENDIF
            _internalList:Sort(startIndex-__ARRAYBASE__ ,count,comparer)
            RETURN

        INTERNAL CLASS ArrayDebugView
            PRIVATE _value AS ARRAY
            PUBLIC CONSTRUCTOR (a AS ARRAY)
                _value := a
                //[DebuggerBrowsable(DebuggerBrowsableState.RootHidden)] ;
           PUBLIC PROPERTY Elements AS List<USUAL> GET _value:_internalList

        END CLASS

    END	CLASS

END NAMESPACE

//
// 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.Collections
USING System.Collections.Generic
USING System.Linq
USING System.Diagnostics
USING System.Reflection
USING System.Text
USING XSharp
BEGIN NAMESPACE XSharp
    /// Internal type that implements the VO Compatible ARRAY type.

    /// This type has methods and properties that normally are never directly called from user code.
    ///
    /// <seealso cref='T:XSharp.IIndexer' />
    /// <include file="RTComments.xml" path="Comments/ZeroBasedIndex/*" />
    [DebuggerDisplay("{DebuggerString(),nq}", Type := "ARRAY")] ;
    [DebuggerTypeProxy(TYPEOF(ArrayDebugView))];
    PUBLIC SEALED CLASS __Array INHERIT __ArrayBase<USUAL> IMPLEMENTS IIndexer

        /// <inheritdoc />
        CONSTRUCTOR()
            SUPER()

            /// <inheritdoc />
        CONSTRUCTOR(capacity AS DWORD)
            SUPER(capacity)

            /// Create an array and fill it with elements from an existing .Net array of USUALS
        CONSTRUCTOR( elements AS USUAL[] )
            SELF()
            IF elements == NULL
                THROW ArgumentNullException{NAMEOF(elements)}
            ENDIF
            _internalList:Capacity := elements:Length
            _internalList:AddRange(elements)
            RETURN

            /// <inheritdoc />
        CONSTRUCTOR( elements AS OBJECT[] )
            SELF()
            IF elements == NULL
                RETURN // empty array
            ENDIF
            FOREACH element AS OBJECT IN elements
                IF element == NULL
                    _internalList:add(DEFAULT(USUAL))
                ELSEIF element IS OBJECT[]
                    LOCAL objects AS OBJECT[]
                    objects := (OBJECT[]) element
                    _internalList:Add( __Array{ objects})
                ELSE
                    _internalList:Add( USUAL{ element})
                ENDIF
            NEXT
            RETURN

        INTERNAL STATIC METHOD ArrayCreate(dimensions PARAMS INT[] ) AS ARRAY
            LOCAL count := dimensions:Length AS INT
            IF count <= 0
                THROW ArgumentException{"No dimensions provided."}
            ENDIF
            LOCAL initializer := OBJECT[]{dimensions[1]} AS OBJECT[]
            LOCAL arrayNew AS ARRAY
            arrayNew := ARRAY{initializer}

            IF count > 1
                LOCAL i AS INT
                FOR i:=0+__ARRAYBASE__  UPTO dimensions[1]-1+__ARRAYBASE__
                    LOCAL newParams := INT[]{count-1} AS INT[]
                    Array.Copy(dimensions,1,newParams,0,count-1)
                    arrayNew:_internalList[(INT) i-__ARRAYBASE__ ] := __ArrayNew(newParams)
                NEXT
            ENDIF
            RETURN arrayNew

        /// <exclude/>
        STATIC METHOD __ArrayNew( dimensions PARAMS INT[] ) AS __Array
            LOCAL newArray AS ARRAY
            IF dimensions:Length != 0
                newArray := __ArrayNewHelper(dimensions,1)
            ELSE
                newArray := ARRAY{}
            ENDIF
            RETURN newArray

        INTERNAL STATIC METHOD __ArrayNewHelper(dimensions AS INT[], currentDim AS INT) AS ARRAY
            LOCAL capacity  AS DWORD // one based ?
            LOCAL newArray AS ARRAY
            capacity := (DWORD) dimensions[currentDim]
            newArray := ARRAY{capacity}
            IF currentDim != dimensions:Length
                LOCAL nextDim := currentDim+1 AS INT
                LOCAL index   := 1 AS INT
                DO WHILE index <= capacity
                    newArray:Add(USUAL{__ArrayNewHelper(dimensions,nextDim)})
                    index+=1
                ENDDO
                RETURN newArray
            ENDIF
            RETURN newArray

        INTERNAL NEW METHOD Clone() AS ARRAY
            LOCAL aResult AS ARRAY
            LOCAL nCount AS DWORD
            nCount := (DWORD) _internalList:Count
            aResult := ARRAY{nCount}
            IF nCount == 0
                // warning, nCount-1 below will become MAXDWORD for nCount == 0
                RETURN aResult
            END IF
            FOR VAR I := 0 TO nCount-1
                VAR u := _internalList[i]
                IF u:isArray
                    VAR aElement := (ARRAY) u
                    IF aElement != NULL_ARRAY
                        aResult:_internalList[i] := aElement:Clone()
                    ELSE
                        aResult:_internalList[i] := aElement
                    ENDIF
                ELSE
                    aResult:_internalList[i] := u
                ENDIF
            NEXT
            RETURN aResult

        INTERNAL METHOD CloneShallow() AS ARRAY
            RETURN (ARRAY) SUPER:Clone()

        /// <include file="RTComments.xml" path="Comments/ZeroBasedIndexProperty/*" />
        /// <param name="index"><include file="RTComments.xml" path="Comments/ZeroBasedIndexParam/*" /></param>
        /// <returns>The value of the property of the element stored at the indicated location in the array.</returns>
        PUBLIC PROPERTY SELF[index PARAMS INT[]] AS USUAL
            GET
                RETURN __GetElement(index)
            END GET
            SET
                SELF:__SetElement(VALUE,index)
            END SET
        END PROPERTY

        NEW INTERNAL METHOD Swap(position AS INT, element AS USUAL) AS USUAL
            RETURN SUPER:Swap(position, element)

        /// <include file="RTComments.xml" path="Comments/ZeroBasedIndexProperty/*" />
        /// <param name="index"><include file="RTComments.xml" path="Comments/ZeroBasedIndexParam/*" /></param>
        /// <returns>The element stored at the specified location in the array.</returns>
        PUBLIC METHOD __GetElement(index PARAMS INT[]) AS USUAL
            LOCAL length := index:Length AS INT
            LOCAL currentArray AS ARRAY
            LOCAL i AS INT
            currentArray := SELF
            FOR i:= 1  UPTO length  -1 // walk all but the last level
                LOCAL u := currentArray:_internalList[ index[i] ] AS USUAL
                IF u:IsNil
                    RETURN u
                ENDIF
                IF (OBJECT) u IS IIndexedProperties .AND. i == length-1
                    LOCAL o := (IIndexedProperties) (OBJECT) u AS IIndexedProperties
                    RETURN o[index[length]]
                ENDIF
                IF !u:IsArray
                    THROW InvalidOperationException{"out of range error."}
                ENDIF
                currentArray := (ARRAY) u
            NEXT
            RETURN currentArray:_internalList[ index[length] ]

        INTERNAL METHOD DebuggerString() AS STRING
            LOCAL sb AS StringBuilder
            LOCAL cnt, tot AS LONG
            sb := StringBuilder{}
            sb:Append(SELF:ToString())
            sb:Append("{")
            tot := _internallist:Count
            cnt := 0
            FOREACH VAR element IN SELF:_internallist
                IF cnt > 0
                    sb:Append(",")
                ENDIF
                sb:Append(element:ToString())
                cnt++
                IF cnt > 5
                    IF cnt < tot
                        sb:Append(",..")
                    ENDIF
                    EXIT
                ENDIF
            NEXT
            sb:Append("}")
            RETURN sb:ToString()

        /// <include file="RTComments.xml" path="Comments/ZeroBasedIndexProperty/*" />
        /// <param name="index"><include file="RTComments.xml" path="Comments/ZeroBasedIndexParam/*" /></param>
        /// <param name='u'>New element to store in the array at the position specified</param>
        /// <returns>The new element</returns>
        PUBLIC METHOD __SetElement(u AS USUAL, index PARAMS INT[] ) AS USUAL
            // indices are 0 based
            IF SELF:CheckLock()
                LOCAL length := index:Length AS INT
                LOCAL currentArray := SELF AS ARRAY
                FOR VAR i := 1 UPTO length-1
                    LOCAL uArray := currentArray:_internalList[index[i]] AS USUAL
                    IF (OBJECT) u IS IIndexedProperties .AND. i == length-1
                        LOCAL o := (IIndexedProperties) (OBJECT) u AS IIndexedProperties
                        o[index[length]] := u
                        RETURN u
                    ENDIF
                    IF !(uArray:IsArray)
                        THROW InvalidOperationException{"Out of range error."}
                    ENDIF
                    currentArray := (ARRAY) uArray
                NEXT
                currentArray:_internalList[index[length]] := u
            ENDIF
            RETURN u

       /// Implicit conversion to OBJECT[]. SubArrays become nested OBJECT[] arrays.
        STATIC OPERATOR IMPLICIT ( a AS __Array) AS OBJECT[]
            LOCAL aResult := List<OBJECT>{} AS List<OBJECT>
            FOREACH VAR uElement IN a
                IF uElement:ISArray
                    LOCAL aSubArray AS ARRAY
                    aSubArray := uElement
                    aResult:Add( (OBJECT[]) aSubArray)
                ELSE
                    aResult:Add(  (OBJECT) uElement)
                ENDIF
            NEXT
            RETURN aResult:ToArray()

        /// <exclude/>
        STATIC OPERATOR IMPLICIT ( a AS OBJECT[]) AS __Array
            RETURN __Array{a}

        INTERNAL STATIC METHOD Copy(aSource AS ARRAY,aTarget AS ARRAY,;
            start AS DWORD, sourceLen AS DWORD, offSet AS DWORD, targetLen AS DWORD ) AS VOID
            LOCAL x AS DWORD
            // Adjust
            start-=1
            offSet-=1
            sourceLen-=1
            targetLen-=1
            IF start < sourceLen
                FOR x := start UPTO sourceLen
                    aTarget:_InternalList[(INT) offSet] := aSource:_InternalList[(INT) x]
                    offSet++
                    IF offSet > targetLen
                        EXIT
                    ENDIF
                NEXT
            ELSE
                FOR x := start DOWNTO sourceLen
                    aTarget:_InternalList[(INT) offSet] := aSource:_InternalList[(INT) x]
                    offSet++
                    IF offSet > targetLen
                        EXIT
                    ENDIF
                NEXT
            ENDIF
            RETURN

        NEW INTERNAL METHOD Sort(startIndex AS INT, count AS INT, comparer AS IComparer<__USUAL>) AS VOID
           IF startIndex <= 0
                startIndex := 1
            ENDIF
            IF count < 0
                count := _internalList:Count - startIndex + __ARRAYBASE__
            ENDIF
            _internalList:Sort(startIndex-__ARRAYBASE__ ,count,comparer)
            RETURN

        INTERNAL CLASS ArrayDebugView
            PRIVATE _value AS ARRAY
            PUBLIC CONSTRUCTOR (a AS ARRAY)
                _value := a
                //[DebuggerBrowsable(DebuggerBrowsableState.RootHidden)] ;
           PUBLIC PROPERTY Elements AS List<USUAL> GET _value:_internalList

        END CLASS

    END	CLASS

END NAMESPACE