[Docs] Updated tool

[Docs] Updated tool

USING System.Reflection
USING System.Collections.Generic
USING System.Linq
USING System.Text
USING System.XML
GLOBAL gaFiles AS STRING[]
GLOBAL gsPath AS STRING
GLOBAL gsCatPath AS STRING
GLOBAL documents AS Dictionary<STRING, XmlDocument>
FUNCTION Start AS VOID
    gaFiles := <STRING>{"XSharp.Core.DLL", "XSharp.RT.DLL","XSharp.VO.DLL", ;
        "XSharp.Data.DLL","XSharp.VFP.DLL","XSharp.RDD.DLL","XSharp.XPP.DLL",   ;
        "VOSystemClasses.DLL", "VORDDClasses.DLL","VOGUIClasses.DLL", ;
        "VOSQLClasses.DLL","VOInternetClasses.DLL", "VOConsoleClasses.DLL",;
        "XSharp.RT.Debugger.DLL", "XSharp.VOSystemClasses.DLL", ;
        "XSharp.VOConsoleClasses.DLL","XSharp.VOSQLClasses.DLL","XSharp.VORDDClasses.DLL"}
    gsPath  := "c:\XSharp\DevRt\Binaries\Documentation\"
    gsCatPath := "c:\XSharp\DevRt\Docs\Categories\"
    documents := Dictionary<STRING, XmlDocument>{StringComparer.OrdinalIgnoreCase}
    TRY
    //CreateClassList()
    //CreateClassSectionFiles()
    WriteClassTopics()
    //CreateFunctionList()
    //CreateFunctionSectionFiles()
    WriteFunctionTopics()
    CATCH e AS Exception
        ? e:ToString()
        Console.ReadLine()
    END TRY

FUNCTION WriteClassTopics() AS VOID
    VAR aInfo       := CreateClassList()
    VAR aCategories := ReadClassCategories()
    VAR catList     := List<STRING>{}
    FOREACH VAR item IN aInfo
        VAR info := item:Value
        FOREACH VAR cat IN info:Categories
            IF ! String.IsNullOrEmpty(cat:ToLower())
                IF ! catList:Contains(cat:ToLower())
                    catList:Add(cat:ToLower())
                ENDIF
            ENDIF
        NEXT
    NEXT
    VAR missing := FALSE
    catList:Sort()
    FOREACH VAR cat IN catList
        IF ! aCategories:ContainsKey(cat)
            ? "Class Category "+cat+" is missing"
            missing := TRUE
        ENDIF
    NEXT
    IF missing
        RETURN
    ENDIF
    FOREACH VAR cat IN catList
        ? cat
        VAR sFile := gsCatPath+cat+"_Classes.aml"
        VAR contents := System.IO.File.ReadAllText(sFile)
        VAR left   := contents:Substring(0, contents:IndexOf("<content>")+9)
        VAR right  := contents:Substring(contents:IndexOf("</content>"))
        VAR sb     := StringBuilder{}
        sb:Append(left)
        sb:AppendLine("
<tableHeader>")
        sb:AppendLine("<row><entry><para>Namespace</para></entry>")
        sb:AppendLine("<entry><para>Class</para></entry>")
        sb:AppendLine("<entry><para>Description</para></entry>")
        sb:AppendLine("</row></tableHeader>")
        FOREACH VAR item IN aInfo
            VAR info := item:Value
            VAR match := FALSE
            FOREACH VAR keyword IN info:Categories
                IF String.Compare(keyword, cat, TRUE) == 0
                    match := TRUE
                    EXIT
                ENDIF
            NEXT
            IF match
                VAR sig := "T:"+info:NameSpace+"."+ info:Name
                VAR xml  := gsPath+info:assembly:replace(".DLL",".XML")
                VAR desc := ReadDescription(xml, sig, "c:\Program Files (x86)\XSharp\Assemblies\")
                sb:AppendLine("<row><entry><para>"+info:NameSpace+"</para></entry>")
                sb:Append("<entry><para><codeEntityReference qualifyHint=""false"" autoUpgrade=""true"">")
                sb:Append(sig)
                sb:AppendLine("</codeEntityReference></para></entry>")
                sb:AppendLine("<entry><para>"+desc+"</para></entry></row>")
            ENDIF
        NEXT
        sb:AppendLine("
")
        sb:Append(right)
        System.IO.File.WriteAllText(sFile, sb:ToString())
    NEXT

    RETURN

FUNCTION WriteFunctionTopics() AS VOID
    VAR aInfo       := CreateFunctionList()
    VAR aCategories := ReadFunctionCategories()
    VAR catList     := List<STRING>{}
    FOREACH VAR item IN aInfo
        VAR info := item:Value
        FOREACH VAR cat IN info:Categories
            IF ! String.IsNullOrEmpty(cat:ToLower())
                IF ! catList:Contains(cat:ToLower())
                    catList:Add(cat:ToLower())
                ENDIF
            ENDIF
        NEXT
    NEXT
    VAR missing := FALSE
    catList:Sort()
    FOREACH VAR cat IN catList
        IF ! aCategories:ContainsKey(cat)
            ? "Function Category "+cat+" is missing"
            missing := TRUE
        ENDIF
    NEXT
    IF missing
        RETURN
    ENDIF
    FOREACH VAR cat IN catList
        ? cat
        VAR sFile := gsCatPath+cat+"_Functions.aml"
        VAR contents := System.IO.File.ReadAllText(sFile)
        VAR left   := contents:Substring(0, contents:IndexOf("<content>")+9)
        VAR right  := contents:Substring(contents:IndexOf("</content>"))
        VAR sb     := StringBuilder{}
        sb:Append(left)
        sb:AppendLine("
<tableHeader>")
        sb:AppendLine("<row><entry><para>Assembly</para></entry>")
        sb:AppendLine("<entry><para>Function</para></entry>")
        sb:AppendLine("<entry><para>Description</para></entry>")
        sb:AppendLine("</row></tableHeader>")
        // Sort the functions in a temp array
        FOREACH VAR item IN aInfo
            LOCAL info := item:Value   AS FunctionInfo
            VAR match := FALSE
            FOREACH VAR keyword IN info:Categories
                IF String.Compare(keyword, cat, TRUE) == 0
                    match := TRUE
                    EXIT
                ENDIF
            NEXT
            IF match
                VAR assembly := info:assembly:replace(".DLL","")
                VAR xml      := gsPath+info:assembly:replace(".DLL",".XML")
                VAR sig := info:Signature
                VAR funcClass := FunctionsClassName (assembly)
                sig := "M:"+funcClass+"."+info:Name+sig
                VAR desc := ReadDescription(xml, sig,"c:\Program Files (x86)\XSharp\Assemblies\")

                sb:AppendLine("<row><entry><para>"+assembly+"</para></entry>")
                sb:Append("<entry><para><codeEntityReference qualifyHint=""false"" autoUpgrade=""true"">")
                sb:Append(sig)
                sb:AppendLine("</codeEntityReference></para></entry>")
                sb:AppendLine("<entry><para>"+desc+"</para></entry></row>")
            ENDIF
        NEXT
        sb:AppendLine("
")
        sb:Append(right)
        System.IO.File.WriteAllText(sFile, sb:ToString())
    NEXT

    RETURN

FUNCTION FunctionsClassName(asmName AS STRING) AS STRING
    IF !asmName:ToLower():EndsWith(".dll")
        asmName += ".dll"
    ENDIF
    asmName := gsPath+asmName
    VAR asm := Assembly.LoadFrom(asmName)
    FOREACH attribute AS CustomAttributeData IN asm:GetCustomAttributesData()
        IF attribute:AttributeType:Fullname == "XSharp.Internal.ClassLibraryAttribute"
            VAR arg := attribute:ConstructorArguments[0]:Value
            RETURN arg:ToString()
        ENDIF
    NEXT
    RETURN ""

FUNCTION ReadDescription(cXmlFile AS STRING, cSig AS STRING, cAltPath AS STRING) AS STRING
    LOCAL doc AS XmlDocument
    IF System.IO.File.Exists(cXmlFile)
        IF documents:ContainsKey(cXmlFile)
            doc := documents[cXmlFile]
        ELSE
            doc      := XmlDocument{}
            doc:Load(cXmlFile)
            documents:Add(cXmlFile, doc)
        ENDIF
        VAR node := doc:SelectSingleNode("doc/members/member[@name='"+cSig+"']/summary")
        IF (node != NULL)
//            FOREACH  child  AS XmlNode IN  node.ChildNodes
//            IF (child.Name == "summary")
//            RETURN child.InnerText
//            ENDIF
//            NEXT
            RETURN node.InnerText
        ENDIF
    ENDIF
    IF ! string.IsNullOrEmpty(cAltPath)
        cXmlFile := cAltPath+System.IO.Path.GetFileName(cXmlFile)
        RETURN ReadDescription(cXmlFile, cSig,"")
    ENDIF
    RETURN ""

FUNCTION CreateFunctionSectionFiles AS VOID
    VAR sTemplate := gsCatPath+"templateFunctions.aml"
    VAR aLines := ReadFunctionCategories()
    FOREACH VAR pair IN aLines
        VAR sLine := pair:Value
        ? sLine
        VAR sFile := gsCatPath+sLine+"_Functions.aml"
        IF ! System.IO.File.Exists(sFile)
            VAR file := System.IO.File.ReadAllText(sTemplate)
            file := file.Replace("id=""default""", "id=""cat-"+sLine+"_Functions""")
            file := file.Replace("title=""default functions""", "title="""+sLine+" Functions""")
            file := file.Replace("<title>default Functions</title>", "<title>"+sLine +" Functions</title>")
            System.IO.File.WriteAllText(sFile, file)
        ENDIF
    NEXT

FUNCTION CreateClassSectionFiles AS VOID
    VAR sTemplate := gsCatPath+"templateClasses.aml"
    VAR aLines := ReadClassCategories()
    FOREACH VAR pair IN aLines
        VAR sLine := pair:Value
        ? sLine
        VAR sFile := gsCatPath+sLine+"_Classes.aml"
        IF ! System.IO.File.Exists(sFile)
            VAR file := System.IO.File.ReadAllText(sTemplate)
            file := file.Replace("id=""default""", "id=""cat-"+sLine+"_Classes""")
            file := file.Replace("title=""default Classes""", "title="""+sLine+" Classes""")
            file := file.Replace("<title>default Classes</title>", "<title>"+sLine +" Classes</title>")
            System.IO.File.WriteAllText(sFile, file)
        ENDIF
    NEXT

FUNCTION CreateClassList AS SortedDictionary<STRING, MyClassInfo>
    VAR aInfo := ReadClassList()
    VAR aExcluded := ReadExcludedClasses()
    FOREACH VAR sFile IN gaFiles
        TRY
        VAR asm   := Assembly.LoadFrom(gsPath+sFile)
        VAR types := asm:GetTypes()
        FOREACH oType AS System.Type IN types
            IF !oType:FullName:EndsWith("Functions") .and. ;
                 oType:Name:IndexOf("$") == -1 .and.oType:Name:IndexOf("<") == -1 .and. ;
                 ! oType:Name:StartsWith("_")  .and. oType:IsPublic
                VAR classInfo := MyClassInfo{} { assembly := sFile, name := oType:Name, namespace := oType:Namespace}
                IF aExcluded:ContainsKey(classInfo:key)
                    IF aInfo:ContainsKey(classInfo:key)
                        ? "Delete", classInfo:key
                        aInfo:Remove(classInfo:key)
                    ENDIF
                    LOOP
                ENDIF
                classInfo:Type := oType
                IF !aInfo:ContainsKey(classInfo:Key)
                    ? oType:FullName
                    aInfo:Add(classInfo:Key, classInfo)
                ENDIF

            ENDIF
        NEXT
        CATCH e AS Exception
            ? sFile
            ? e:ToString()
        END TRY
    NEXT
    VAR output := System.IO.File.CreateText("RuntimeClasses.CSV")
    FOREACH VAR item IN aInfo
        VAR groups := item:Value:Categories
        VAR sGroups := ""
        FOREACH VAR sGroup IN groups
            sGroups += ","+sGroup
        NEXT
        output:Write(item:Value:assembly+",")
        output:Write(item:Value:namespace+",")
        output:Write(item:Value:Name)
        output:WriteLine(sGroups )
    NEXT
    output:Close()
    RETURN aInfo
FUNCTION CreateFunctionList AS  SortedDictionary<STRING, FunctionInfo>
    VAR aInfo := ReadFunctionList()
    VAR aExcluded := ReadExcludedFunctions()
    FOREACH VAR sFile IN gaFiles
        TRY
        VAR asm   := Assembly.LoadFrom(gsPath+sFile)
        VAR types := asm:GetTypes()
        FOREACH type AS System.Type IN types
            IF type:FullName:EndsWith(".Functions")
                VAR functions := type:GetMethods(BindingFlags.Public+BindingFlags.Static)
                FOREACH m AS MethodInfo IN functions
                    VAR name := m:Name
                    IF (name:StartsWith("$") || name:StartsWith("__")   )
                        LOOP
                    ENDIF
                    VAR info := FunctionInfo{}{ Assembly := sFile, Name := m.Name}
                    IF aExcluded:ContainsKey(info:key)
                        IF aInfo:ContainsKey(info:key)
                            ? "Delete", info:key
                            aInfo:Remove(info:key)
                        ENDIF
                        LOOP
                    ENDIF
                    VAR overloads := type:GetMember(m.Name)
                    IF aInfo:ContainsKey(info:Key)
                        info := aInfo[info:Key]
                    ENDIF
                    info:Overloads := overloads

                    IF m:GetCustomAttributes(typeof(ObsoleteAttribute),FALSE):Count() > 0
                        VAR delete := overloads:Length == 1
                        IF ! delete
                            delete := TRUE
                            FOREACH VAR o IN overloads
                                IF o:GetCustomAttributes(typeof(ObsoleteAttribute),FALSE):Count() == 0
                                    delete := FALSE
                                ENDIF
                            NEXT
                        ENDIF
                        IF delete .and. aInfo:ContainsKey(info:key)
                            aInfo:Remove(info:key)
                        ENDIF
                        LOOP
                    ENDIF
                    TRY
                        IF !aInfo:ContainsKey(info:Key)
                            ? "Add", m.Name
                            aInfo:Add(info:key, info )
                        ENDIF
                    CATCH e AS Exception
                        ? e:ToString()

                    END TRY
                NEXT
            ENDIF
        NEXT
        CATCH e AS ReflectionTypeLoadException
            ? "Failed to load ", sFile
            ? e:ToString()
            ?
            Console.ReadLine()
        END TRY
    NEXT
    VAR output := System.IO.File.CreateText("RuntimeFunctions.CSV")
    FOREACH VAR item IN aInfo
        VAR groups := item:Value:Categories
        VAR sGroups := ""
        FOREACH VAR sGroup IN groups
            sGroups += ","+sGroup
        NEXT
        output:Write(item:Value:assembly+",")
        output:Write(item:Value:Name)
        output:WriteLine(sGroups )
    NEXT
    output:Close()
    RETURN aInfo

FUNCTION ReadFunctionList() AS SortedDictionary<STRING, FunctionInfo>
    VAR aInfo := SortedDictionary<STRING, FunctionInfo> {}
    IF System.IO.File.Exists("RuntimeFunctions.CSV")
        VAR aLines := System.IO.File.ReadAllLines("RuntimeFunctions.CSV")
        FOREACH VAR line IN aLines
            VAR aElements := line:Split(<char>{','})
            IF aElements:Length >= 2
                VAR info := FunctionInfo{} {assembly := aElements[1], Name := aElements[2]}
                VAR groups := STRING[]{aElements:Length -2}
                FOR VAR i := 1 TO aElements:Length-2
                    groups[i] := aElements[i+2]
                NEXT
                info:Categories := groups
                aInfo:Add(info:Key, info)
            ENDIF
        NEXT
    ENDIF
    RETURN aInfo

FUNCTION ReadClassList() AS SortedDictionary<STRING, MyClassInfo>
    VAR aInfo := SortedDictionary<STRING, MyClassInfo> {}
    IF System.IO.File.Exists("RuntimeClasses.CSV")
        VAR aLines := System.IO.File.ReadAllLines("RuntimeClasses.CSV")
        FOREACH VAR line IN aLines
            VAR aElements := line:Split(<char>{','})
            IF aElements:Length >= 3
                VAR info := MyClassInfo{} {assembly := aElements[1], Namespace := aElements[2], Name := aElements[3]}
                VAR groups := STRING[]{aElements:Length -3}
                FOR VAR i := 1 TO aElements:Length-3
                    groups[i] := aElements[i+3]
                NEXT
                info:Categories := groups
                IF !aInfo:ContainsKey(info:Key)
                    aInfo:Add(info:Key, info)
                ELSE
                    NOP
                ENDIF
            ENDIF
        NEXT
    ENDIF
    RETURN aInfo

FUNCTION ReadExcludedFunctions() AS SortedDictionary<STRING, FunctionInfo>
    VAR aInfo := SortedDictionary<STRING, FunctionInfo> {}
    IF System.IO.File.Exists("ExcludedFunctions.CSV")
        VAR aLines := System.IO.File.ReadAllLines("ExcludedFunctions.CSV")
        FOREACH VAR line IN aLines
            VAR aElements := line:Split(<char>{','})
            IF aElements:Length >= 2
                VAR info := FunctionInfo{} {assembly := aElements[1], Name := aElements[2]}
                aInfo:Add(info:Key, info)
            ENDIF
        NEXT
    ENDIF
    RETURN aInfo

FUNCTION ReadExcludedClasses() AS SortedDictionary<STRING, MyClassInfo>
    VAR aInfo := SortedDictionary<STRING, MyClassInfo> {}
    IF System.IO.File.Exists("ExcludedClasses.CSV")
        VAR aLines := System.IO.File.ReadAllLines("ExcludedClasses.CSV")
        FOREACH VAR line IN aLines
            VAR aElements := line:Split(<char>{','})
           IF aElements:Length >= 3
                VAR info := MyClassInfo{} {assembly := aElements[1], Namespace := aElements[2], Name := aElements[3]}
                aInfo:Add(info:Key, info)
            ENDIF
        NEXT
    ENDIF
    RETURN aInfo

FUNCTION ReadFunctionCategories() AS SortedDictionary<STRING, STRING>
    VAR list := SortedDictionary<STRING, STRING>{StringComparer.OrdinalIgnoreCase}
    IF System.IO.File.Exists("FunctionCategories.CSV")
        VAR aLines := System.IO.File.ReadAllLines("FunctionCategories.CSV")
        FOREACH VAR sLine IN aLines
            IF ! String.IsNullOrEmpty(sLine)
                list:Add(sLine:Trim(),sline:Trim())
            ENDIF
        NEXT
    ENDIF
    RETURN list

FUNCTION ReadClassCategories() AS SortedDictionary<STRING, STRING>
    VAR list := SortedDictionary<STRING, STRING>{StringComparer.OrdinalIgnoreCase}
    IF System.IO.File.Exists("ClassesCategories.CSV")
        VAR aLines := System.IO.File.ReadAllLines("ClassesCategories.CSV")
        FOREACH VAR sLine IN aLines
            IF ! String.IsNullOrEmpty(sLine)
                list:Add(sLine:Trim(), sLine:Trim())
            ENDIF
        NEXT
    ENDIF
    RETURN list

CLASS MyClassInfo
    PROPERTY Assembly   AS STRING AUTO
    PROPERTY Name       AS STRING AUTO  := ""
    PROPERTY NameSpace  AS STRING AUTO  := ""
    PROPERTY Description AS STRING AUTO   := ""
    PROPERTY Categories AS STRING[] AUTO  := STRING[]{0}
    PROPERTY Key AS STRING
        GET
            IF String.IsNullOrEmpty(NameSpace)
                RETURN Name:ToLower()
            ELSE
                RETURN NameSpace:ToLower():PadRight(100) +":"+Name:ToLower()
            ENDIF
        END GET
    END PROPERTY
    PROPERTY Type AS System.Type AUTO
END CLASS

CLASS FunctionInfo
    PROPERTY Assembly AS STRING AUTO
    PROPERTY Name AS STRING AUTO
    PROPERTY Description AS STRING AUTO   := ""
    PROPERTY Categories AS STRING[] AUTO  := STRING[]{0}
    PROPERTY Key AS STRING GET Name:ToLower():PadRight(25) +":"+Assembly:ToLower()
    PROPERTY Overloads AS MemberInfo[] AUTO
    PROPERTY Signature AS STRING
        GET
            IF OverLoads == NULL .or. overloads:Length == 0
                RETURN ""
            ENDIF
            LOCAL overload := NULL AS MethodInfo
            FOREACH m AS MemberInfo IN  SELF:Overloads
                IF m:MemberType  == MemberTypes.Method
                    VAR o := (MethodInfo) m
                    IF !o:IsGenericMethod
                        overload := o
                        EXIT
                    ENDIF
                ENDIF
            NEXT
            IF overload == NULL
                overload := (methodInfo) SELF:Overloads[1]
            ENDIF
            VAR PARAMS := overload:GetParameters()
            IF PARAMS:Length == 0
                RETURN ""
            ENDIF
            VAR sb := StringBuilder{}
            VAR genArgs := overload:GetGenericArguments()
            IF overload:IsGenericMethod
                sb:Append("``"+genArgs:Length:ToString())
            ENDIF
            sb:Append("(")
            FOREACH par AS ParameterInfo IN PARAMS
                IF sb:Length > 1
                    sb:Append(",")
                ENDIF
                sb:Append(par:ParameterType:FullName)
            NEXT
            sb:Append(")")
            sb:Replace("&","@")
            RETURN sb:ToString()
        END GET
    END PROPERTY
END CLASS

USING System.Reflection
USING System.Collections.Generic
USING System.Linq
USING System.Text
USING System.XML
GLOBAL gaFiles AS STRING[]
GLOBAL gsPath AS STRING
GLOBAL gsCatPath AS STRING
GLOBAL documents AS Dictionary<STRING, XmlDocument>
FUNCTION Start AS VOID
    gaFiles := <STRING>{"XSharp.Core.DLL", "XSharp.RT.DLL","XSharp.VO.DLL", ;
        "XSharp.Data.DLL","XSharp.VFP.DLL","XSharp.RDD.DLL","XSharp.XPP.DLL",   ;
        "VOSystemClasses.DLL", "VORDDClasses.DLL","VOGUIClasses.DLL", ;
        "VOSQLClasses.DLL","VOInternetClasses.DLL", "VOConsoleClasses.DLL",;
        "XSharp.RT.Debugger.DLL", "XSharp.VOSystemClasses.DLL", ;
        "XSharp.VOConsoleClasses.DLL","XSharp.VOSQLClasses.DLL","XSharp.VORDDClasses.DLL"}
    gsPath  := "c:\XSharp\DevRt\Binaries\Documentation\"
    gsCatPath := "c:\XSharp\DevRt\Docs\Categories\"
    documents := Dictionary<STRING, XmlDocument>{StringComparer.OrdinalIgnoreCase}
    TRY
    //CreateClassList()
    //CreateClassSectionFiles()
    WriteClassTopics()
    //CreateFunctionList()
    //CreateFunctionSectionFiles()
    WriteFunctionTopics()
    CATCH e AS Exception
        ? e:ToString()
        Console.ReadLine()
    END TRY

FUNCTION WriteClassTopics() AS VOID
    VAR aInfo       := CreateClassList()
    VAR aCategories := ReadClassCategories()
    VAR catList     := List<STRING>{}
    FOREACH VAR item IN aInfo
        VAR info := item:Value
        FOREACH VAR cat IN info:Categories
            IF ! String.IsNullOrEmpty(cat:ToLower())
                IF ! catList:Contains(cat:ToLower())
                    catList:Add(cat:ToLower())
                ENDIF
            ENDIF
        NEXT
    NEXT
    VAR missing := FALSE
    catList:Sort()
    FOREACH VAR cat IN catList
        IF ! aCategories:ContainsKey(cat)
            ? "Class Category "+cat+" is missing"
            missing := TRUE
        ENDIF
    NEXT
    IF missing
        RETURN
    ENDIF
    FOREACH VAR cat IN catList
        ? cat
        VAR sFile := gsCatPath+cat+"_Classes.aml"
        VAR contents := System.IO.File.ReadAllText(sFile)
        VAR left   := contents:Substring(0, contents:IndexOf("<content>")+9)
        VAR right  := contents:Substring(contents:IndexOf("</content>"))
        VAR sb     := StringBuilder{}
        sb:Append(left)
        sb:AppendLine("
<tableHeader>")
        sb:AppendLine("<row><entry><para>Namespace</para></entry>")
        sb:AppendLine("<entry><para>Class</para></entry>")
        sb:AppendLine("<entry><para>Description</para></entry>")
        sb:AppendLine("</row></tableHeader>")
        FOREACH VAR item IN aInfo
            VAR info := item:Value
            VAR match := FALSE
            FOREACH VAR keyword IN info:Categories
                IF String.Compare(keyword, cat, TRUE) == 0
                    match := TRUE
                    EXIT
                ENDIF
            NEXT
            IF match
                VAR sig := "T:"+info:NameSpace+"."+ info:Name
                VAR xml  := gsPath+info:assembly:replace(".DLL",".XML")
                VAR desc := ReadDescription(xml, sig, "c:\Program Files (x86)\XSharp\Assemblies\")
                sb:AppendLine("<row><entry><para>"+info:NameSpace+"</para></entry>")
                sb:Append("<entry><para><codeEntityReference qualifyHint=""false"" autoUpgrade=""true"">")
                sb:Append(sig)
                sb:AppendLine("</codeEntityReference></para></entry>")
                sb:AppendLine("<entry><para>"+desc+"</para></entry></row>")
            ENDIF
        NEXT
        sb:AppendLine("
")
        sb:Append(right)
        System.IO.File.WriteAllText(sFile, sb:ToString())
    NEXT

    RETURN

FUNCTION WriteFunctionTopics() AS VOID
    VAR aInfo       := CreateFunctionList()
    VAR aCategories := ReadFunctionCategories()
    VAR catList     := List<STRING>{}
    FOREACH VAR item IN aInfo
        VAR info := item:Value
        FOREACH VAR cat IN info:Categories
            IF ! String.IsNullOrEmpty(cat:ToLower())
                IF ! catList:Contains(cat:ToLower())
                    catList:Add(cat:ToLower())
                ENDIF
            ENDIF
        NEXT
    NEXT
    VAR missing := FALSE
    catList:Sort()
    FOREACH VAR cat IN catList
        IF ! aCategories:ContainsKey(cat)
            ? "Function Category "+cat+" is missing"
            missing := TRUE
        ENDIF
    NEXT
    IF missing
        RETURN
    ENDIF
    FOREACH VAR cat IN catList
        ? cat
        VAR sFile := gsCatPath+cat+"_Functions.aml"
        VAR contents := System.IO.File.ReadAllText(sFile)
        VAR left   := contents:Substring(0, contents:IndexOf("<content>")+9)
        VAR right  := contents:Substring(contents:IndexOf("</content>"))
        VAR sb     := StringBuilder{}
        sb:Append(left)
        sb:AppendLine("
<tableHeader>")
        sb:AppendLine("<row><entry><para>Assembly</para></entry>")
        sb:AppendLine("<entry><para>Function</para></entry>")
        sb:AppendLine("<entry><para>Description</para></entry>")
        sb:AppendLine("</row></tableHeader>")
        // Sort the functions in a temp array
        FOREACH VAR item IN aInfo
            LOCAL info := item:Value   AS FunctionInfo
            VAR match := FALSE
            FOREACH VAR keyword IN info:Categories
                IF String.Compare(keyword, cat, TRUE) == 0
                    match := TRUE
                    EXIT
                ENDIF
            NEXT
            IF match
                VAR assembly := info:assembly:replace(".DLL","")
                VAR xml      := gsPath+info:assembly:replace(".DLL",".XML")
                VAR sig := info:Signature
                sig := "M:"+Assembly+".Functions."+info:Name+sig
                VAR desc := ReadDescription(xml, sig,"c:\Program Files (x86)\XSharp\Assemblies\")

                sb:AppendLine("<row><entry><para>"+assembly+"</para></entry>")
                sb:Append("<entry><para><codeEntityReference qualifyHint=""false"" autoUpgrade=""true"">")
                sb:Append(sig)
                sb:AppendLine("</codeEntityReference></para></entry>")
                sb:AppendLine("<entry><para>"+desc+"</para></entry></row>")
            ENDIF
        NEXT
        sb:AppendLine("
")
        sb:Append(right)
        System.IO.File.WriteAllText(sFile, sb:ToString())
    NEXT

    RETURN

FUNCTION ReadDescription(cXmlFile AS STRING, cSig AS STRING, cAltPath AS STRING) AS STRING
    LOCAL doc AS XmlDocument
    IF System.IO.File.Exists(cXmlFile)
        IF documents:ContainsKey(cXmlFile)
            doc := documents[cXmlFile]
        ELSE
            doc      := XmlDocument{}
            doc:Load(cXmlFile)
            documents:Add(cXmlFile, doc)
        ENDIF
        VAR node := doc:SelectSingleNode("doc/members/member[@name='"+cSig+"']/summary")
        IF (node != NULL)
//            FOREACH  child  AS XmlNode IN  node.ChildNodes
//            IF (child.Name == "summary")
//            RETURN child.InnerText
//            ENDIF
//            NEXT
            RETURN node.InnerText
        ENDIF
    ENDIF
    IF ! string.IsNullOrEmpty(cAltPath)
        cXmlFile := cAltPath+System.IO.Path.GetFileName(cXmlFile)
        RETURN ReadDescription(cXmlFile, cSig,"")
    ENDIF
    RETURN ""

FUNCTION CreateFunctionSectionFiles AS VOID
    VAR sTemplate := gsCatPath+"templateFunctions.aml"
    VAR aLines := ReadFunctionCategories()
    FOREACH VAR pair IN aLines
        VAR sLine := pair:Value
        ? sLine
        VAR sFile := gsCatPath+sLine+"_Functions.aml"
        IF ! System.IO.File.Exists(sFile)
            VAR file := System.IO.File.ReadAllText(sTemplate)
            file := file.Replace("id=""default""", "id=""cat-"+sLine+"_Functions""")
            file := file.Replace("title=""default functions""", "title="""+sLine+" Functions""")
            file := file.Replace("<title>default Functions</title>", "<title>"+sLine +" Functions</title>")
            System.IO.File.WriteAllText(sFile, file)
        ENDIF
    NEXT

FUNCTION CreateClassSectionFiles AS VOID
    VAR sTemplate := gsCatPath+"templateClasses.aml"
    VAR aLines := ReadClassCategories()
    FOREACH VAR pair IN aLines
        VAR sLine := pair:Value
        ? sLine
        VAR sFile := gsCatPath+sLine+"_Classes.aml"
        IF ! System.IO.File.Exists(sFile)
            VAR file := System.IO.File.ReadAllText(sTemplate)
            file := file.Replace("id=""default""", "id=""cat-"+sLine+"_Classes""")
            file := file.Replace("title=""default Classes""", "title="""+sLine+" Classes""")
            file := file.Replace("<title>default Classes</title>", "<title>"+sLine +" Classes</title>")
            System.IO.File.WriteAllText(sFile, file)
        ENDIF
    NEXT

FUNCTION CreateClassList AS SortedDictionary<STRING, MyClassInfo>
    VAR aInfo := ReadClassList()
    VAR aExcluded := ReadExcludedClasses()
    FOREACH VAR sFile IN gaFiles
        TRY
        VAR asm   := Assembly.LoadFrom(gsPath+sFile)
        VAR types := asm:GetTypes()
        FOREACH oType AS System.Type IN types
            IF !oType:FullName:EndsWith("Functions") .and. ;
                 oType:Name:IndexOf("$") == -1 .and.oType:Name:IndexOf("<") == -1 .and. ;
                 ! oType:Name:StartsWith("_")  .and. oType:IsPublic
                VAR classInfo := MyClassInfo{} { assembly := sFile, name := oType:Name, namespace := oType:Namespace}
                IF aExcluded:ContainsKey(classInfo:key)
                    IF aInfo:ContainsKey(classInfo:key)
                        ? "Delete", classInfo:key
                        aInfo:Remove(classInfo:key)
                    ENDIF
                    LOOP
                ENDIF
                classInfo:Type := oType
                IF !aInfo:ContainsKey(classInfo:Key)
                    ? oType:FullName
                    aInfo:Add(classInfo:Key, classInfo)
                ENDIF

            ENDIF
        NEXT
        CATCH e AS Exception
            ? sFile
            ? e:ToString()
        END TRY
    NEXT
    VAR output := System.IO.File.CreateText("RuntimeClasses.CSV")
    FOREACH VAR item IN aInfo
        VAR groups := item:Value:Categories
        VAR sGroups := ""
        FOREACH VAR sGroup IN groups
            sGroups += ","+sGroup
        NEXT
        output:Write(item:Value:assembly+",")
        output:Write(item:Value:namespace+",")
        output:Write(item:Value:Name)
        output:WriteLine(sGroups )
    NEXT
    output:Close()
    RETURN aInfo
FUNCTION CreateFunctionList AS  SortedDictionary<STRING, FunctionInfo>
    VAR aInfo := ReadFunctionList()
    VAR aExcluded := ReadExcludedFunctions()
    FOREACH VAR sFile IN gaFiles
        TRY
        VAR asm   := Assembly.LoadFrom(gsPath+sFile)
        VAR types := asm:GetTypes()
        FOREACH type AS System.Type IN types
            IF type:FullName:EndsWith(".Functions")
                VAR functions := type:GetMethods(BindingFlags.Public+BindingFlags.Static)
                FOREACH m AS MethodInfo IN functions
                    VAR name := m:Name
                    IF (name:StartsWith("$") || name:StartsWith("__")   )
                        LOOP
                    ENDIF
                    VAR info := FunctionInfo{}{ Assembly := sFile, Name := m.Name}
                    IF aExcluded:ContainsKey(info:key)
                        IF aInfo:ContainsKey(info:key)
                            ? "Delete", info:key
                            aInfo:Remove(info:key)
                        ENDIF
                        LOOP
                    ENDIF
                    VAR overloads := type:GetMember(m.Name)
                    IF aInfo:ContainsKey(info:Key)
                        info := aInfo[info:Key]
                    ENDIF
                    info:Overloads := overloads

                    IF m:GetCustomAttributes(typeof(ObsoleteAttribute),FALSE):Count() > 0
                        VAR delete := overloads:Length == 1
                        IF ! delete
                            delete := TRUE
                            FOREACH VAR o IN overloads
                                IF o:GetCustomAttributes(typeof(ObsoleteAttribute),FALSE):Count() == 0
                                    delete := FALSE
                                ENDIF
                            NEXT
                        ENDIF
                        IF delete .and. aInfo:ContainsKey(info:key)
                            aInfo:Remove(info:key)
                        ENDIF
                        LOOP
                    ENDIF
                    TRY
                        IF !aInfo:ContainsKey(info:Key)
                            ? "Add", m.Name
                            aInfo:Add(info:key, info )
                        ENDIF
                    CATCH e AS Exception
                        ? e:ToString()

                    END TRY
                NEXT
            ENDIF
        NEXT
        CATCH e AS ReflectionTypeLoadException
            ? "Failed to load ", sFile
            ? e:ToString()
            ?
            Console.ReadLine()
        END TRY
    NEXT
    VAR output := System.IO.File.CreateText("RuntimeFunctions.CSV")
    FOREACH VAR item IN aInfo
        VAR groups := item:Value:Categories
        VAR sGroups := ""
        FOREACH VAR sGroup IN groups
            sGroups += ","+sGroup
        NEXT
        output:Write(item:Value:assembly+",")
        output:Write(item:Value:Name)
        output:WriteLine(sGroups )
    NEXT
    output:Close()
    RETURN aInfo

FUNCTION ReadFunctionList() AS SortedDictionary<STRING, FunctionInfo>
    VAR aInfo := SortedDictionary<STRING, FunctionInfo> {}
    IF System.IO.File.Exists("RuntimeFunctions.CSV")
        VAR aLines := System.IO.File.ReadAllLines("RuntimeFunctions.CSV")
        FOREACH VAR line IN aLines
            VAR aElements := line:Split(<char>{','})
            IF aElements:Length >= 2
                VAR info := FunctionInfo{} {assembly := aElements[1], Name := aElements[2]}
                VAR groups := STRING[]{aElements:Length -2}
                FOR VAR i := 1 TO aElements:Length-2
                    groups[i] := aElements[i+2]
                NEXT
                info:Categories := groups
                aInfo:Add(info:Key, info)
            ENDIF
        NEXT
    ENDIF
    RETURN aInfo

FUNCTION ReadClassList() AS SortedDictionary<STRING, MyClassInfo>
    VAR aInfo := SortedDictionary<STRING, MyClassInfo> {}
    IF System.IO.File.Exists("RuntimeClasses.CSV")
        VAR aLines := System.IO.File.ReadAllLines("RuntimeClasses.CSV")
        FOREACH VAR line IN aLines
            VAR aElements := line:Split(<char>{','})
            IF aElements:Length >= 3
                VAR info := MyClassInfo{} {assembly := aElements[1], Namespace := aElements[2], Name := aElements[3]}
                VAR groups := STRING[]{aElements:Length -3}
                FOR VAR i := 1 TO aElements:Length-3
                    groups[i] := aElements[i+3]
                NEXT
                info:Categories := groups
                IF !aInfo:ContainsKey(info:Key)
                    aInfo:Add(info:Key, info)
                ELSE
                    NOP
                ENDIF
            ENDIF
        NEXT
    ENDIF
    RETURN aInfo

FUNCTION ReadExcludedFunctions() AS SortedDictionary<STRING, FunctionInfo>
    VAR aInfo := SortedDictionary<STRING, FunctionInfo> {}
    IF System.IO.File.Exists("ExcludedFunctions.CSV")
        VAR aLines := System.IO.File.ReadAllLines("ExcludedFunctions.CSV")
        FOREACH VAR line IN aLines
            VAR aElements := line:Split(<char>{','})
            IF aElements:Length >= 2
                VAR info := FunctionInfo{} {assembly := aElements[1], Name := aElements[2]}
                aInfo:Add(info:Key, info)
            ENDIF
        NEXT
    ENDIF
    RETURN aInfo

FUNCTION ReadExcludedClasses() AS SortedDictionary<STRING, MyClassInfo>
    VAR aInfo := SortedDictionary<STRING, MyClassInfo> {}
    IF System.IO.File.Exists("ExcludedClasses.CSV")
        VAR aLines := System.IO.File.ReadAllLines("ExcludedClasses.CSV")
        FOREACH VAR line IN aLines
            VAR aElements := line:Split(<char>{','})
           IF aElements:Length >= 3
                VAR info := MyClassInfo{} {assembly := aElements[1], Namespace := aElements[2], Name := aElements[3]}
                aInfo:Add(info:Key, info)
            ENDIF
        NEXT
    ENDIF
    RETURN aInfo

FUNCTION ReadFunctionCategories() AS SortedDictionary<STRING, STRING>
    VAR list := SortedDictionary<STRING, STRING>{StringComparer.OrdinalIgnoreCase}
    IF System.IO.File.Exists("FunctionCategories.CSV")
        VAR aLines := System.IO.File.ReadAllLines("FunctionCategories.CSV")
        FOREACH VAR sLine IN aLines
            IF ! String.IsNullOrEmpty(sLine)
                list:Add(sLine:Trim(),sline:Trim())
            ENDIF
        NEXT
    ENDIF
    RETURN list

FUNCTION ReadClassCategories() AS SortedDictionary<STRING, STRING>
    VAR list := SortedDictionary<STRING, STRING>{StringComparer.OrdinalIgnoreCase}
    IF System.IO.File.Exists("ClassesCategories.CSV")
        VAR aLines := System.IO.File.ReadAllLines("ClassesCategories.CSV")
        FOREACH VAR sLine IN aLines
            IF ! String.IsNullOrEmpty(sLine)
                list:Add(sLine:Trim(), sLine:Trim())
            ENDIF
        NEXT
    ENDIF
    RETURN list

CLASS MyClassInfo
    PROPERTY Assembly   AS STRING AUTO
    PROPERTY Name       AS STRING AUTO  := ""
    PROPERTY NameSpace  AS STRING AUTO  := ""
    PROPERTY Description AS STRING AUTO   := ""
    PROPERTY Categories AS STRING[] AUTO  := STRING[]{0}
    PROPERTY Key AS STRING
        GET
            IF String.IsNullOrEmpty(NameSpace)
                RETURN Name:ToLower()
            ELSE
                RETURN NameSpace:ToLower():PadRight(100) +":"+Name:ToLower()
            ENDIF
        END GET
    END PROPERTY
    PROPERTY Type AS System.Type AUTO
END CLASS

CLASS FunctionInfo
    PROPERTY Assembly AS STRING AUTO
    PROPERTY Name AS STRING AUTO
    PROPERTY Description AS STRING AUTO   := ""
    PROPERTY Categories AS STRING[] AUTO  := STRING[]{0}
    PROPERTY Key AS STRING GET Name:ToLower():PadRight(25) +":"+Assembly:ToLower()
    PROPERTY Overloads AS MemberInfo[] AUTO
    PROPERTY Signature AS STRING
        GET
            IF OverLoads == NULL .or. overloads:Length == 0
                RETURN ""
            ENDIF
            LOCAL overload := NULL AS MethodInfo
            FOREACH m AS MemberInfo IN  SELF:Overloads
                IF m:MemberType  == MemberTypes.Method
                    VAR o := (MethodInfo) m
                    IF !o:IsGenericMethod
                        overload := o
                        EXIT
                    ENDIF
                ENDIF
            NEXT
            IF overload == NULL
                overload := (methodInfo) SELF:Overloads[1]
            ENDIF
            VAR PARAMS := overload:GetParameters()
            IF PARAMS:Length == 0
                RETURN ""
            ENDIF
            VAR sb := StringBuilder{}
            VAR genArgs := overload:GetGenericArguments()
            IF overload:IsGenericMethod
                sb:Append("``"+genArgs:Length:ToString())
            ENDIF
            sb:Append("(")
            FOREACH par AS ParameterInfo IN PARAMS
                IF sb:Length > 1
                    sb:Append(",")
                ENDIF
                sb:Append(par:ParameterType:FullName)
            NEXT
            sb:Append(")")
            sb:Replace("&","@")
            RETURN sb:ToString()
        END GET
    END PROPERTY
END CLASS


QtQuickWidgets: module updated (macros, parameters checking, formatting, ...)

Modified:
source/QtQuickWidgets/QQuickWidget.prg

/*

  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

/*

  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 new1
   METHOD new2
   METHOD new3
   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)
*/
HB_FUNC_STATIC( 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)
*/
HB_FUNC_STATIC( QQUICKWIDGET_NEW2 )
{
#if (QT_VERSION >= QT_VERSION_CHECK(5,3,0))
  QQmlEngine * par1 = (QQmlEngine *) _qt5xhb_itemGetPtr(1);
  QQuickWidget * o = new QQuickWidget ( par1, PQWIDGET(2) );
  _qt5xhb_storePointerAndFlag( o, false );
#endif
}

/*
QQuickWidget(const QUrl &source, QWidget *parent = 0)
*/
HB_FUNC_STATIC( 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) )
  {
    HB_FUNC_EXEC( QQUICKWIDGET_NEW1 );
  }
  else if( ISNUMPAR(2) && ISQQMLENGINE(1) && ISQWIDGET(2) )
  {
    HB_FUNC_EXEC( QQUICKWIDGET_NEW2 );
  }
  else if( ISBETWEEN(1,2) && ISQURL(1) && ISOPTQWIDGET(2) )
  {
    HB_FUNC_EXEC( 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 )
  {
    obj->setSource ( *PQURL(1) );
  }
  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 )
  {
    obj->setResizeMode ( (QQuickWidget::ResizeMode) hb_parni(1) );
  }
  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 )
  {
    QQmlComponent * par2 = (QQmlComponent *) _qt5xhb_itemGetPtr(2);
    obj->setContent ( *PQURL(1), par2, PQOBJECT(3) );
  }
  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 )
  {
    QSurfaceFormat * par1 = (QSurfaceFormat *) _qt5xhb_itemGetPtr(1);
    obj->setFormat ( *par1 );
  }
  hb_itemReturn( hb_stackSelfItem() );
#endif
}

#pragma ENDDUMP


Changed error code for DirChange with not existing path

//
// 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 XSharp
USING System.IO
///
/// Return the last DOS error code  (Exit code) and set a new code.
///
/// <param name="nSet">New value for the DOS eror code </param>
/// <returns>
/// </returns>
FUNCTION DosError(nSet AS DWORD) AS DWORD
	LOCAL nOld AS INT
	nOld := System.Environment.ExitCode
	System.Environment.ExitCode := UNCHECKED((INT) nSet)
	RETURN UNCHECKED((DWORD) nOld)

///
/// Return the last DOS error code  (Exit code). use GetDosError() to fetch the error from the Last Win32 call.
///
/// <param name="nSet"></param>
/// <returns>
/// </returns>
FUNCTION DosError() AS DWORD
	LOCAL nOld AS INT
	nOld := System.Environment.ExitCode
	RETURN UNCHECKED((DWORD) nOld)

///
/// Return the DOS error code from any application.
///
/// <returns>
/// </returns>
FUNCTION GetDosError() AS DWORD
	RETURN UNCHECKED((DWORD) System.Runtime.InteropServices.Marshal.GetLastWin32Error())

///
/// Retrieve the contents of a DOS environment variable.
///
/// <param name="c"></param>
/// <returns>
/// </returns>
FUNCTION GetEnv(cVar AS STRING) AS STRING
	RETURN System.Environment.GetEnvironmentVariable(cVar)

	///
/// Update or replace the contents of a DOS environment variable.
///
/// <param name="cVar"></param>
/// <param name="cValue"></param>
/// <returns>
/// </returns>
FUNCTION SetEnv(cVar AS STRING,cValue AS STRING) AS LOGIC
	RETURN SetEnv(cVar, cValue, FALSE)
///
/// Update or replace the contents of a DOS environment variable.
///
/// <param name="cVar"></param>
/// <param name="cValue"></param>
/// <param name="lAppend"></param>
/// <returns>
/// </returns>
FUNCTION SetEnv(cVar AS STRING,cValue AS STRING,lAppend AS LOGIC) AS LOGIC
	LOCAL result AS LOGIC
	TRY
        XSharp.IO.File.clearErrorState()

		IF lAppend
			LOCAL cOldValue AS STRING
			cOldValue := System.Environment.GetEnvironmentVariable(cVar)
			IF ! String.IsNullOrEmpty( cOldValue )
				cOldValue += ";"
			ENDIF
			cValue := cOldValue + cValue
		ENDIF
		System.Environment.SetEnvironmentVariable(cVar, cValue)
		result := System.Environment.GetEnvironmentVariable(cVar) == cValue
	CATCH e as Exception
		XSharp.IO.File.setErrorState(e)
		result := FALSE
	END TRY
	RETURN result

///
/// Identify the current workstation.
///
/// <returns>The workstation ID as a string.</returns>
FUNCTION NetName() AS STRING
	RETURN System.Environment.MachineName

///
/// Return the current Windows directory.
///
/// <param name="cDisk"></param>
/// <returns>
/// </returns>
FUNCTION CurDir (cDisk AS STRING) AS STRING
	RETURN CurDir()

///
/// Return the current Windows directory.
///
/// <returns>
/// </returns>
FUNCTION CurDir() AS STRING
	LOCAL cDir AS STRING
	LOCAL index AS INT
	cDir := System.Environment.CurrentDirectory
	index := cDir:Indexof(Path.VolumeSeparatorChar)
	IF index > 0
		cDir := cDir:Substring(index+1)
	ENDIF
	IF cDir[0] == Path.DirectorySeparatorChar
		cDir := cDir:Substring(1)
	ENDIF
	IF cDir[cDir:Length-1]  == Path.DirectorySeparatorChar
		cDir := cDir:Substring(0, cDir:Length-1)
	ENDIF
	RETURN cDir

///
/// Return the current Windows drive.
///
/// <returns>
/// Return the letter of the current drive without colon
/// </returns>
FUNCTION CurDrive() AS STRING
	LOCAL currentDirectory := System.IO.Directory.GetCurrentDirectory() AS STRING
	LOCAL drive := "" AS STRING
	LOCAL position AS INT

	position := currentDirectory:IndexOf(System.IO.Path.VolumeSeparatorChar)
	IF position > 0
		drive := currentDirectory:Substring(0,position)
	ENDIF
	RETURN drive

///
/// Return the currently selected working directory.
///
/// <returns>
/// </returns>
FUNCTION WorkDir() AS STRING
	LOCAL cPath AS STRING
	LOCAL asm   AS System.Reflection.Assembly
	asm := System.Reflection.Assembly.GetCallingAssembly()
	cPath := asm:ManifestModule:FullyQualifiedName
	cPath := Path.GetDirectoryName(cPath)
	IF cPath[cPath:Length-1] !=  Path.DirectorySeparatorChar
		cPath += Path.DirectorySeparatorChar:ToString()
	ENDIF
	RETURN cPath

///
/// Return the space available on the current disk drive.
///
/// <param name="cDisk"></param>
/// <returns>
/// </returns>
FUNCTION DiskFree() AS INT64
	RETURN DiskFree(CurDrive())

INTERNAL FUNCTION DiskNo2DiskName(nDisk AS INT) AS STRING
	RETURN ('A'+ (nDisk-1)):ToString()

///
/// Return the space available on a specified disk.
///
/// <param name="cDrive">The drivename to get the free space from.</param>
/// <returns>
/// The free space on the specified disk drive.
/// </returns>
FUNCTION DiskFree(cDrive AS STRING) AS INT64
	RETURN DriveInfo{cDrive}:TotalFreeSpace

///
/// Return the space available on a specified disk.
///
/// <param name="nDrive">The drive number (1 = A, 2 = B etc)</param>
/// <returns>
/// The free space on the specified disk drive.
/// </returns>
FUNCTION DiskFree(nDrive AS INT) AS INT64
	LOCAL cDrive AS STRING
	cDrive := DiskNo2DiskName(nDrive)
	RETURN DiskFree(cDrive)

///
/// Return the current Windows drive.
///
/// <returns>
/// Return the letter of the current drive without colon
/// </returns>
FUNCTION DiskName() AS STRING
	RETURN CurDrive()

///
/// Return the capacity of the current disk.
///
/// <returns>
/// The capacity of the current disk.
/// </returns>
FUNCTION DiskSpace() AS INT64
	RETURN DiskSpace(CurDrive())

///
/// Return the capacity of the specified disk.
///
/// <param name="nDisk"></param>
/// <returns>
/// </returns>
FUNCTION DiskSpace(nDisk AS INT) AS INT64
	LOCAL cDisk AS STRING
	cDisk := DiskNo2DiskName(nDisk)
	RETURN DiskSpace(cDisk)

///
/// Return the capacity of the specified disk.
///
/// <param name="cDisk"></param>
/// <returns>
/// </returns>
FUNCTION DiskSpace(cDisk AS STRING) AS INT64
	RETURN DriveInfo{cDisk}:TotalSize

///
/// Detect a concurrency conflict.
///
/// <returns>
/// </returns>
FUNCTION NetErr() AS LOGIC STRICT
    RETURN RuntimeState.NetErr

///
/// Detect a concurrency conflict.
///
/// <param name="lSet"></param>
/// <returns>
/// </returns>
FUNCTION NetErr( lValue AS LOGIC ) AS LOGIC
    LOCAL curvalue := RuntimeState.NetErr AS LOGIC
    RuntimeState.NetErr := lValue
    RETURN curvalue

///
///
/// <param name="n"></param>
/// <returns>
/// </returns>
FUNCTION LockTries() AS DWORD
	RETURN RuntimeState.LockTries

///
///
/// <param name="n"></param>
/// <returns>
/// </returns>
FUNCTION LockTries(nValue AS DWORD) AS DWORD
	LOCAL nResult AS DWORD
	nResult := RuntimeState.LockTries
	RuntimeState.LockTries := nValue
	RETURN nResult

///
/// Change the current Windows directory.
///
/// <param name="pszDir"></param>
/// <returns>
/// </returns>
FUNCTION DirChange(cDir AS STRING) AS INT
	LOCAL result AS INT
	TRY
       XSharp.IO.File.clearErrorState()
		IF Directory.Exists(cDir)
			Directory.SetCurrentDirectory(cDir)
			result := 0
		ELSE
			result := 3 // Path not found
		ENDIF
	CATCH e as Exception
		XSharp.IO.File.setErrorState(e)
        result := (INT) XSharp.IO.File.errorCode
	END TRY
	RETURN result

///
/// Create a directory.
///
/// <param name="pszDir"></param>
/// <returns>
/// </returns>
FUNCTION DirMake(cDir AS STRING) AS INT
	LOCAL result AS INT
	TRY
        XSharp.IO.File.clearErrorState()
		IF !Directory.Exists(cDir)
			Directory.CreateDirectory(cDir)
			result := 0
		ELSE
			result := 183 // ERROR_ALREADY_EXISTS
		ENDIF
	CATCH e as Exception
		XSharp.IO.File.setErrorState(e)
        result := (INT) XSharp.IO.File.errorCode
	END TRY
	RETURN result

///
/// Remove a directory.
///
/// <param name="pszDir"></param>
/// <returns>
/// </returns>
FUNCTION DirRemove(cDir AS STRING) AS INT
	LOCAL result AS INT
	TRY
        XSharp.IO.File.clearErrorState()
		IF Directory.Exists(cDir)
			Directory.Delete(cDir,FALSE)
			result := 0
		ELSE
			result := 2 // Cannot find file
		ENDIF
	CATCH e as Exception
		XSharp.IO.File.setErrorState(e)
        result := (INT) XSharp.IO.File.errorCode
	END TRY
	RETURN result

///
/// Change the current disk drive.
///
/// <param name="pszDisk"></param>
/// <returns>
/// </returns>

FUNCTION DiskChange(c AS STRING) AS LOGIC
	IF String.IsNullOrEmpty(c)
		RETURN FALSE
	ENDIF
	c := c:Substring(0,1)+Path.VolumeSeparatorChar:ToString()+Path.DirectorySeparatorChar:ToString()
	RETURN DirChange(c) == 0

/// Return the operating system name.
/// <returns>
/// </returns>
FUNCTION OS() AS STRING
	RETURN OS(FALSE)

/// Return the operating system name.
/// <param name="lExtended"></param>
/// <returns>
/// </returns>
FUNCTION OS(lExtended AS LOGIC) AS STRING
    LOCAL cOs AS STRING
    VAR o := Environment.OSVersion
	cOs := o:ToString()
	IF !lExtended
		SWITCH o:Platform
		CASE PlatformID.Win32NT
			cOs := "Windows NT"
		CASE PlatformID.Win32S
			cOs := "Win32s on Windows 3.1"
		CASE PlatformID.Win32Windows
			IF o:Version:Major == 4
				IF o:Version:Minor == 0
					cOs := "Windows 95"
				ELSEIF o:Version:Minor == 10
					cOs := "Windows 98"
					IF o:Version:Build == 2222
						cOs := "Windows 98 SE"
					ENDIF
				ELSEIF o:Version:Minor == 90
					cOs := "Windows ME"
				ENDIF
			ENDIF
		CASE PlatformID.WinCE
			cOs := "Windows CE"
		OTHERWISE
			cOs := o:ToString()
		END SWITCH
	ENDIF
    RETURN cOs

FUNCTION TruePath( cFile AS STRING ) AS STRING PASCAL
    LOCAL           nPos        AS DWORD
    LOCAL           cPath       AS STRING

    nPos  := RAt("\", cFile)
    IF nPos = 0
        cPath := cFile
    ELSE
        cPath := cFile:SubString(0, (INT) nPos)
    ENDIF

    RETURN cPath

/// <exclude/>
FUNCTION _ExecName() AS STRING
    RETURN System.Reflection.Assembly.GetExecutingAssembly():Location

FUNCTION ExecName( lFull AS LOGIC ) AS STRING
    LOCAL   nPos        AS DWORD
    LOCAL   cPath       AS STRING

    cPath := _ExecName()

    IF ! lFull
        nPos  := RAt( "\", cPath )

        IF nPos != 0
            cPath := SubStr2( cPath, nPos + 1 )
        ENDIF
    ENDIF

    RETURN cPath

FUNCTION ModuleName( lFull AS LOGIC ) AS STRING
    RETURN ExecName( lFull )

FUNCTION GetMimeType(sFileName AS STRING) AS STRING
    LOCAL sExt AS STRING
	sExt := System.IO.Path.GetExtension(sFileName)
    RETURN Microsoft.Win32.Registry.GetValue("HKEY_CLASSES_ROOT\"+sExt,"Content Type",""):ToString()

///
/// Returns the command line used to invoke the application.
///
/// <returns>
/// </returns>
FUNCTION _GetCmdLine() AS STRING
RETURN System.Environment.CommandLine

//
// 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 XSharp
USING System.IO
///
/// Return the last DOS error code  (Exit code) and set a new code.
///
/// <param name="nSet">New value for the DOS eror code </param>
/// <returns>
/// </returns>
FUNCTION DosError(nSet AS DWORD) AS DWORD
	LOCAL nOld AS INT
	nOld := System.Environment.ExitCode
	System.Environment.ExitCode := UNCHECKED((INT) nSet)
	RETURN UNCHECKED((DWORD) nOld)

///
/// Return the last DOS error code  (Exit code). use GetDosError() to fetch the error from the Last Win32 call.
///
/// <param name="nSet"></param>
/// <returns>
/// </returns>
FUNCTION DosError() AS DWORD
	LOCAL nOld AS INT
	nOld := System.Environment.ExitCode
	RETURN UNCHECKED((DWORD) nOld)

///
/// Return the DOS error code from any application.
///
/// <returns>
/// </returns>
FUNCTION GetDosError() AS DWORD
	RETURN UNCHECKED((DWORD) System.Runtime.InteropServices.Marshal.GetLastWin32Error())

///
/// Retrieve the contents of a DOS environment variable.
///
/// <param name="c"></param>
/// <returns>
/// </returns>
FUNCTION GetEnv(cVar AS STRING) AS STRING
	RETURN System.Environment.GetEnvironmentVariable(cVar)

	///
/// Update or replace the contents of a DOS environment variable.
///
/// <param name="cVar"></param>
/// <param name="cValue"></param>
/// <returns>
/// </returns>
FUNCTION SetEnv(cVar AS STRING,cValue AS STRING) AS LOGIC
	RETURN SetEnv(cVar, cValue, FALSE)
///
/// Update or replace the contents of a DOS environment variable.
///
/// <param name="cVar"></param>
/// <param name="cValue"></param>
/// <param name="lAppend"></param>
/// <returns>
/// </returns>
FUNCTION SetEnv(cVar AS STRING,cValue AS STRING,lAppend AS LOGIC) AS LOGIC
	LOCAL result AS LOGIC
	TRY
        XSharp.IO.File.clearErrorState()

		IF lAppend
			LOCAL cOldValue AS STRING
			cOldValue := System.Environment.GetEnvironmentVariable(cVar)
			IF ! String.IsNullOrEmpty( cOldValue )
				cOldValue += ";"
			ENDIF
			cValue := cOldValue + cValue
		ENDIF
		System.Environment.SetEnvironmentVariable(cVar, cValue)
		result := System.Environment.GetEnvironmentVariable(cVar) == cValue
	CATCH e as Exception
		XSharp.IO.File.setErrorState(e)
		result := FALSE
	END TRY
	RETURN result

///
/// Identify the current workstation.
///
/// <returns>The workstation ID as a string.</returns>
FUNCTION NetName() AS STRING
	RETURN System.Environment.MachineName

///
/// Return the current Windows directory.
///
/// <param name="cDisk"></param>
/// <returns>
/// </returns>
FUNCTION CurDir (cDisk AS STRING) AS STRING
	RETURN CurDir()

///
/// Return the current Windows directory.
///
/// <returns>
/// </returns>
FUNCTION CurDir() AS STRING
	LOCAL cDir AS STRING
	LOCAL index AS INT
	cDir := System.Environment.CurrentDirectory
	index := cDir:Indexof(Path.VolumeSeparatorChar)
	IF index > 0
		cDir := cDir:Substring(index+1)
	ENDIF
	IF cDir[0] == Path.DirectorySeparatorChar
		cDir := cDir:Substring(1)
	ENDIF
	IF cDir[cDir:Length-1]  == Path.DirectorySeparatorChar
		cDir := cDir:Substring(0, cDir:Length-1)
	ENDIF
	RETURN cDir

///
/// Return the current Windows drive.
///
/// <returns>
/// Return the letter of the current drive without colon
/// </returns>
FUNCTION CurDrive() AS STRING
	LOCAL currentDirectory := System.IO.Directory.GetCurrentDirectory() AS STRING
	LOCAL drive := "" AS STRING
	LOCAL position AS INT

	position := currentDirectory:IndexOf(System.IO.Path.VolumeSeparatorChar)
	IF position > 0
		drive := currentDirectory:Substring(0,position)
	ENDIF
	RETURN drive

///
/// Return the currently selected working directory.
///
/// <returns>
/// </returns>
FUNCTION WorkDir() AS STRING
	LOCAL cPath AS STRING
	LOCAL asm   AS System.Reflection.Assembly
	asm := System.Reflection.Assembly.GetCallingAssembly()
	cPath := asm:ManifestModule:FullyQualifiedName
	cPath := Path.GetDirectoryName(cPath)
	IF cPath[cPath:Length-1] !=  Path.DirectorySeparatorChar
		cPath += Path.DirectorySeparatorChar:ToString()
	ENDIF
	RETURN cPath

///
/// Return the space available on the current disk drive.
///
/// <param name="cDisk"></param>
/// <returns>
/// </returns>
FUNCTION DiskFree() AS INT64
	RETURN DiskFree(CurDrive())

INTERNAL FUNCTION DiskNo2DiskName(nDisk AS INT) AS STRING
	RETURN ('A'+ (nDisk-1)):ToString()

///
/// Return the space available on a specified disk.
///
/// <param name="cDrive">The drivename to get the free space from.</param>
/// <returns>
/// The free space on the specified disk drive.
/// </returns>
FUNCTION DiskFree(cDrive AS STRING) AS INT64
	RETURN DriveInfo{cDrive}:TotalFreeSpace

///
/// Return the space available on a specified disk.
///
/// <param name="nDrive">The drive number (1 = A, 2 = B etc)</param>
/// <returns>
/// The free space on the specified disk drive.
/// </returns>
FUNCTION DiskFree(nDrive AS INT) AS INT64
	LOCAL cDrive AS STRING
	cDrive := DiskNo2DiskName(nDrive)
	RETURN DiskFree(cDrive)

///
/// Return the current Windows drive.
///
/// <returns>
/// Return the letter of the current drive without colon
/// </returns>
FUNCTION DiskName() AS STRING
	RETURN CurDrive()

///
/// Return the capacity of the current disk.
///
/// <returns>
/// The capacity of the current disk.
/// </returns>
FUNCTION DiskSpace() AS INT64
	RETURN DiskSpace(CurDrive())

///
/// Return the capacity of the specified disk.
///
/// <param name="nDisk"></param>
/// <returns>
/// </returns>
FUNCTION DiskSpace(nDisk AS INT) AS INT64
	LOCAL cDisk AS STRING
	cDisk := DiskNo2DiskName(nDisk)
	RETURN DiskSpace(cDisk)

///
/// Return the capacity of the specified disk.
///
/// <param name="cDisk"></param>
/// <returns>
/// </returns>
FUNCTION DiskSpace(cDisk AS STRING) AS INT64
	RETURN DriveInfo{cDisk}:TotalSize

///
/// Detect a concurrency conflict.
///
/// <returns>
/// </returns>
FUNCTION NetErr() AS LOGIC STRICT
    RETURN RuntimeState.NetErr

///
/// Detect a concurrency conflict.
///
/// <param name="lSet"></param>
/// <returns>
/// </returns>
FUNCTION NetErr( lValue AS LOGIC ) AS LOGIC
    LOCAL curvalue := RuntimeState.NetErr AS LOGIC
    RuntimeState.NetErr := lValue
    RETURN curvalue

///
///
/// <param name="n"></param>
/// <returns>
/// </returns>
FUNCTION LockTries() AS DWORD
	RETURN RuntimeState.LockTries

///
///
/// <param name="n"></param>
/// <returns>
/// </returns>
FUNCTION LockTries(nValue AS DWORD) AS DWORD
	LOCAL nResult AS DWORD
	nResult := RuntimeState.LockTries
	RuntimeState.LockTries := nValue
	RETURN nResult

///
/// Change the current Windows directory.
///
/// <param name="pszDir"></param>
/// <returns>
/// </returns>
FUNCTION DirChange(cDir AS STRING) AS INT
	LOCAL result AS INT
	TRY
       XSharp.IO.File.clearErrorState()
		IF Directory.Exists(cDir)
			Directory.SetCurrentDirectory(cDir)
			result := 0
		ELSE
			result := -1
		ENDIF
		result := System.Runtime.InteropServices.Marshal.GetLastWin32Error()
	END TRY
	RETURN result

///
/// Create a directory.
///
/// <param name="pszDir"></param>
/// <returns>
/// </returns>
FUNCTION DirMake(cDir AS STRING) AS INT
	LOCAL result AS INT
	TRY
        XSharp.IO.File.clearErrorState()
		IF !Directory.Exists(cDir)
			Directory.CreateDirectory(cDir)
			result := 0
		ELSE
			result := 183 // ERROR_ALREADY_EXISTS
		ENDIF
	CATCH e as Exception
		XSharp.IO.File.setErrorState(e)
        result := (INT) XSharp.IO.File.errorCode
	END TRY
	RETURN result

///
/// Remove a directory.
///
/// <param name="pszDir"></param>
/// <returns>
/// </returns>
FUNCTION DirRemove(cDir AS STRING) AS INT
	LOCAL result AS INT
	TRY
        XSharp.IO.File.clearErrorState()
		IF Directory.Exists(cDir)
			Directory.Delete(cDir,FALSE)
			result := 0
		ELSE
			result := 2 // Cannot find file
		ENDIF
	CATCH e as Exception
		XSharp.IO.File.setErrorState(e)
        result := (INT) XSharp.IO.File.errorCode
	END TRY
	RETURN result

///
/// Change the current disk drive.
///
/// <param name="pszDisk"></param>
/// <returns>
/// </returns>

FUNCTION DiskChange(c AS STRING) AS LOGIC
	IF String.IsNullOrEmpty(c)
		RETURN FALSE
	ENDIF
	c := c:Substring(0,1)+Path.VolumeSeparatorChar:ToString()+Path.DirectorySeparatorChar:ToString()
	RETURN DirChange(c) == 0

/// Return the operating system name.
/// <returns>
/// </returns>
FUNCTION OS() AS STRING
	RETURN OS(FALSE)

/// Return the operating system name.
/// <param name="lExtended"></param>
/// <returns>
/// </returns>
FUNCTION OS(lExtended AS LOGIC) AS STRING
    LOCAL cOs AS STRING
    VAR o := Environment.OSVersion
	cOs := o:ToString()
	IF !lExtended
		SWITCH o:Platform
		CASE PlatformID.Win32NT
			cOs := "Windows NT"
		CASE PlatformID.Win32S
			cOs := "Win32s on Windows 3.1"
		CASE PlatformID.Win32Windows
			IF o:Version:Major == 4
				IF o:Version:Minor == 0
					cOs := "Windows 95"
				ELSEIF o:Version:Minor == 10
					cOs := "Windows 98"
					IF o:Version:Build == 2222
						cOs := "Windows 98 SE"
					ENDIF
				ELSEIF o:Version:Minor == 90
					cOs := "Windows ME"
				ENDIF
			ENDIF
		CASE PlatformID.WinCE
			cOs := "Windows CE"
		OTHERWISE
			cOs := o:ToString()
		END SWITCH
	ENDIF
    RETURN cOs

FUNCTION TruePath( cFile AS STRING ) AS STRING PASCAL
    LOCAL           nPos        AS DWORD
    LOCAL           cPath       AS STRING

    nPos  := RAt("\", cFile)
    IF nPos = 0
        cPath := cFile
    ELSE
        cPath := cFile:SubString(0, (INT) nPos)
    ENDIF

    RETURN cPath

/// <exclude/>
FUNCTION _ExecName() AS STRING
    RETURN System.Reflection.Assembly.GetExecutingAssembly():Location

FUNCTION ExecName( lFull AS LOGIC ) AS STRING
    LOCAL   nPos        AS DWORD
    LOCAL   cPath       AS STRING

    cPath := _ExecName()

    IF ! lFull
        nPos  := RAt( "\", cPath )

        IF nPos != 0
            cPath := SubStr2( cPath, nPos + 1 )
        ENDIF
    ENDIF

    RETURN cPath

FUNCTION ModuleName( lFull AS LOGIC ) AS STRING
    RETURN ExecName( lFull )

FUNCTION GetMimeType(sFileName AS STRING) AS STRING
    LOCAL sExt AS STRING
	sExt := System.IO.Path.GetExtension(sFileName)
    RETURN Microsoft.Win32.Registry.GetValue("HKEY_CLASSES_ROOT\"+sExt,"Content Type",""):ToString()

///
/// Returns the command line used to invoke the application.
///
/// <returns>
/// </returns>
FUNCTION _GetCmdLine() AS STRING
RETURN System.Environment.CommandLine


[RDD] Fixes #631

[RDD] Fixes #631

//
// 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.Text
USING XSharp.RDD.Enums
USING XSharp.RDD.Support
USING XSharp.RDD.CDX
USING System.Diagnostics
USING System.IO
USING STATIC XSharp.Conversions
BEGIN NAMESPACE XSharp.RDD
    /// <summary>DBFFPT RDD. For DBF/FPT. No index support at this level</summary>
    CLASS DBFFPT INHERIT DBF
        PRIVATE _oFptMemo AS FPTMemo
        PROPERTY ReturnRawData as LOGIC GET SET
        CONSTRUCTOR
            SUPER()
            SELF:ReturnRawData := FALSE
            SELF:_Memo := _oFptMemo := FPTMemo{SELF}
            SELF:_oFptMemo:ExportMode := BLOB_EXPORT_APPEND

            /// <inheritdoc />
        OVERRIDE PROPERTY Driver AS STRING GET nameof(DBFFPT)

        OVERRIDE METHOD GetValue(nFldPos AS LONG) AS OBJECT
            IF SELF:_isMemoField( nFldPos )
                // At this level, the return value is the raw Data, in BYTE[]
                VAR rawData := _oFptMemo:GetRawValueWithHeader(nFldPos)
                if rawData != NULL
                    IF SELF:ReturnRawData
                        RETURN rawData
                    ENDIF
                    RETURN SELF:_oFptMemo:DecodeValue(rawData)
                else
                    IF SELF:ReturnRawData
                        RETURN <BYTE>{0}
                    ENDIF
                    var column  := SELF:_GetColumn(nFldPos)
                    return column:BlankValue()
                ENDIF
            ENDIF
            RETURN SUPER:GetValue(nFldPos)

        OVERRIDE METHOD PutValue(nFldPos AS LONG, oValue AS OBJECT) AS LOGIC
            IF SELF:_ReadOnly
                SELF:_dbfError(ERDD.READONLY, XSharp.Gencode.EG_READONLY )
            ENDIF
            SELF:ForceRel()
            IF SELF:_readRecord()
                // GoHot() must be called first because this saves the current index values
                IF ! SELF:_Hot
                    SELF:GoHot()
                ENDIF
                VAR oColumn := SELF:_GetColumn(nFldPos) ASTYPE DbfColumn
                IF oColumn != NULL
                    IF oColumn:IsMemo
                        IF SELF:HasMemo
                            IF oValue == NULL .OR. oValue IS STRING VAR cValue .AND. cValue:Length == 0
                                VAR oldValue := oColumn:GetValue(SELF:_RecordBuffer)
                                IF oldValue != NULL .and. oldValue != DBNull.Value
                                    SELF:_oFptMemo:DeleteBlock((INT) oldValue)
                                ENDIF
                                RETURN oColumn:PutValue(NULL, SELF:_RecordBuffer)
                            ENDIF
                            LOCAL bData AS BYTE[]
                            if oColumn:IsBinary
                                bData := (Byte[]) oValue
                            ELSE
                                bData := SELF:_oFptMemo:EncodeValue(oValue)
                            ENDIF
                            IF SELF:_oFptMemo:PutValue(nFldPos, bData)
                                // Update the Field Info with the new MemoBlock Position
                                RETURN oColumn:PutValue(SELF:_oFptMemo:LastWrittenBlockNumber, SELF:_RecordBuffer)
                            ENDIF
                        ELSE
                            RETURN SUPER:PutValue(nFldPos, oValue)
                        ENDIF
                    ELSE
                        RETURN oColumn:PutValue(oValue, SELF:_RecordBuffer)
                    ENDIF
                 ENDIF
            ENDIF
            RETURN FALSE

            /// <inheritdoc />
        OVERRIDE METHOD Info(nOrdinal AS INT, oNewValue AS OBJECT) AS OBJECT
            LOCAL oResult := NULL AS OBJECT
            SWITCH nOrdinal
            CASE DbInfo.DBI_MEMOHANDLE
                IF ( SELF:_oFptMemo != NULL .AND. SELF:_oFptMemo:IsOpen)
                    oResult := SELF:_oFptMemo:_hFile
                ELSE
                    oResult := IntPtr.Zero
                ENDIF
            CASE DbInfo.DBI_MEMOSTREAM
                IF ( SELF:_oFptMemo != NULL .AND. SELF:_oFptMemo:IsOpen)
                    oResult := SELF:_oFptMemo:_oStream
                ELSE
                    oResult := NULL
                ENDIF
            CASE DbInfo.DBI_MEMOEXT
                IF ( SELF:_oFptMemo != NULL .AND. SELF:_oFptMemo:IsOpen)
                    oResult := System.IO.Path.GetExtension(SELF:_oFptMemo:FileName)
                ELSE
                    oResult := FPTMemo.DefExt
                ENDIF
                IF oNewValue IS STRING VAR strExt
                    FPTMemo.DefExt := strExt
                ENDIF
            CASE DbInfo.DBI_MEMOBLOCKSIZE
                oResult := SELF:_oFptMemo:BlockSize
                IF oNewValue != NULL
                    TRY
                        LOCAL size AS LONG
                        size:= Convert.ToInt32(oNewValue)
                        SELF:_oFptMemo:BlockSize := (WORD) size
                    CATCH ex AS Exception
                        oResult := ""
                        SELF:_dbfError(ex, Subcodes.ERDD_DATATYPE, Gencode.EG_DATATYPE, __FUNCTION__)
                    END TRY
                ENDIF

            CASE DbInfo.DBI_MEMOFIELD
                oResult := ""
                IF oNewValue != NULL
                    TRY
                        LOCAL fldPos AS LONG
                        fldPos := Convert.ToInt32(oNewValue)
                        oResult := SELF:GetValue(fldPos)
                    CATCH ex AS Exception
                        oResult := ""
                        SELF:_dbfError(ex, Subcodes.ERDD_DATATYPE, Gencode.EG_DATATYPE, __FUNCTION__)
                    END TRY
                ENDIF
            CASE DbInfo.DBI_MEMOTYPE
                oResult := DB_MEMO_FPT
            CASE DbInfo.DBI_MEMOVERSION
                oResult := DB_MEMOVER_STD
            CASE DbInfo.BLOB_GET
                IF oNewValue IS XSharp.RDD.IBlobData VAR oBlob
                    oResult := _oFptMemo:BlobInfo(nOrdinal, oBlob)
                ENDIF

            CASE DbInfo.BLOB_NMODE
                IF oNewValue IS LONG VAR iExportMode
                    SELF:_oFptMemo:ExportMode := iExportMode
                ENDIF
            CASE DbFieldInfo.DBS_BLOB_DIRECT_TYPE
                IF oNewValue IS XSharp.RDD.IBlobData VAR oBlob
                    oResult := _oFptMemo:BlobInfo(nOrdinal, oBlob)
                ENDIF
            CASE DbFieldInfo.DBS_BLOB_DIRECT_LEN
                IF oNewValue IS XSharp.RDD.IBlobData VAR oBlob
                    oResult := _oFptMemo:BlobInfo(nOrdinal, oBlob)
                ENDIF
            CASE DbInfo.BLOB_DIRECT_IMPORT
                IF oNewValue IS XSharp.RDD.IBlobData VAR oBlob
                    oResult := _oFptMemo:BlobInfo(nOrdinal, oBlob)
                ENDIF
            CASE DbInfo.BLOB_DIRECT_EXPORT
                IF oNewValue IS XSharp.RDD.IBlobData VAR oBlob
                    oResult := _oFptMemo:BlobInfo(nOrdinal, oBlob)
                ENDIF
            CASE DbInfo.BLOB_DIRECT_GET
                IF oNewValue IS XSharp.RDD.IBlobData VAR oBlob
                    oResult := _oFptMemo:BlobInfo(nOrdinal, oBlob)
                ENDIF
            CASE DbInfo.BLOB_DIRECT_PUT
                IF oNewValue IS XSharp.RDD.IBlobData VAR oBlob
                    oResult := _oFptMemo:BlobInfo(nOrdinal, oBlob)
                ENDIF
            CASE DbInfo.BLOB_ROOT_GET
                IF oNewValue IS XSharp.RDD.IBlobData VAR oBlob
                    oResult := _oFptMemo:BlobInfo(nOrdinal, oBlob)
                ENDIF
            CASE DbInfo.BLOB_ROOT_PUT
                IF oNewValue IS XSharp.RDD.IBlobData VAR oBlob
                    oResult := _oFptMemo:BlobInfo(nOrdinal, oBlob)
                ENDIF
            CASE DbInfo.BLOB_ROOT_LOCK
                IF oNewValue IS XSharp.RDD.IBlobData VAR oBlob
                    oResult := _oFptMemo:BlobInfo(nOrdinal, oBlob)
                ENDIF
            CASE DbInfo.BLOB_ROOT_UNLOCK
                IF oNewValue IS XSharp.RDD.IBlobData VAR oBlob
                    oResult := _oFptMemo:BlobInfo(nOrdinal, oBlob)
                ENDIF
            OTHERWISE
                oResult := SUPER:Info(nOrdinal, oNewValue)
            END SWITCH
            RETURN oResult

        OVERRIDE METHOD PutValueFile(nFldPos AS INT, fileName AS STRING) AS LOGIC
           IF SELF:_ReadOnly
                SELF:_dbfError(ERDD.READONLY, XSharp.Gencode.EG_READONLY )
            ENDIF
            SELF:ForceRel()
            IF SELF:_readRecord()
                // GoHot() must be called first because this saves the current index values
                IF ! SELF:IsHot
                    SELF:GoHot()
                ENDIF
                RETURN SELF:_oFptMemo:PutValueFile(nFldPos, fileName)
            ENDIF
            RETURN FALSE
    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.Text
USING XSharp.RDD.Enums
USING XSharp.RDD.Support
USING XSharp.RDD.CDX
USING System.Diagnostics
USING System.IO
USING STATIC XSharp.Conversions
BEGIN NAMESPACE XSharp.RDD
    /// <summary>DBFFPT RDD. For DBF/FPT. No index support at this level</summary>
    CLASS DBFFPT INHERIT DBF
        PRIVATE _oFptMemo AS FPTMemo
        CONSTRUCTOR
            SUPER()
            SELF:_Memo := _oFptMemo := FPTMemo{SELF}
            SELF:_oFptMemo:ExportMode := BLOB_EXPORT_APPEND

            /// <inheritdoc />
        OVERRIDE PROPERTY Driver AS STRING GET nameof(DBFFPT)

        OVERRIDE METHOD GetValue(nFldPos AS LONG) AS OBJECT
            IF SELF:_isMemoField( nFldPos )
                // At this level, the return value is the raw Data, in BYTE[]
                VAR rawData := _oFptMemo:GetRawValueWithHeader(nFldPos)
                if rawData != NULL
                     RETURN SELF:_oFptMemo:DecodeValue(rawData)
                else
                    var column  := SELF:_GetColumn(nFldPos)
                    return column:BlankValue()
                ENDIF
            ENDIF
            RETURN SUPER:GetValue(nFldPos)

        OVERRIDE METHOD PutValue(nFldPos AS LONG, oValue AS OBJECT) AS LOGIC
            IF SELF:_ReadOnly
                SELF:_dbfError(ERDD.READONLY, XSharp.Gencode.EG_READONLY )
            ENDIF
            SELF:ForceRel()
            IF SELF:_readRecord()
                // GoHot() must be called first because this saves the current index values
                IF ! SELF:_Hot
                    SELF:GoHot()
                ENDIF
                VAR oColumn := SELF:_GetColumn(nFldPos) ASTYPE DbfColumn
                IF oColumn != NULL
                    IF oColumn:IsMemo
                        IF SELF:HasMemo
                            IF oValue == NULL .OR. oValue IS STRING VAR cValue .AND. cValue:Length == 0
                                VAR oldValue := oColumn:GetValue(SELF:_RecordBuffer)
                                IF oldValue != NULL .and. oldValue != DBNull.Value
                                    SELF:_oFptMemo:DeleteBlock((INT) oldValue)
                                ENDIF
                                RETURN oColumn:PutValue(NULL, SELF:_RecordBuffer)
                            ENDIF
                            LOCAL bData AS BYTE[]
                            if oColumn:IsBinary
                                bData := (Byte[]) oValue
                            ELSE
                                bData := SELF:_oFptMemo:EncodeValue(oValue)
                            ENDIF
                            IF SELF:_oFptMemo:PutValue(nFldPos, bData)
                                // Update the Field Info with the new MemoBlock Position
                                RETURN oColumn:PutValue(SELF:_oFptMemo:LastWrittenBlockNumber, SELF:_RecordBuffer)
                            ENDIF
                        ELSE
                            RETURN SUPER:PutValue(nFldPos, oValue)
                        ENDIF
                    ELSE
                        RETURN oColumn:PutValue(oValue, SELF:_RecordBuffer)
                    ENDIF
                 ENDIF
            ENDIF
            RETURN FALSE

            /// <inheritdoc />
        OVERRIDE METHOD Info(nOrdinal AS INT, oNewValue AS OBJECT) AS OBJECT
            LOCAL oResult := NULL AS OBJECT
            SWITCH nOrdinal
            CASE DbInfo.DBI_MEMOHANDLE
                IF ( SELF:_oFptMemo != NULL .AND. SELF:_oFptMemo:IsOpen)
                    oResult := SELF:_oFptMemo:_hFile
                ELSE
                    oResult := IntPtr.Zero
                ENDIF
            CASE DbInfo.DBI_MEMOSTREAM
                IF ( SELF:_oFptMemo != NULL .AND. SELF:_oFptMemo:IsOpen)
                    oResult := SELF:_oFptMemo:_oStream
                ELSE
                    oResult := NULL
                ENDIF
            CASE DbInfo.DBI_MEMOEXT
                IF ( SELF:_oFptMemo != NULL .AND. SELF:_oFptMemo:IsOpen)
                    oResult := System.IO.Path.GetExtension(SELF:_oFptMemo:FileName)
                ELSE
                    oResult := FPTMemo.DefExt
                ENDIF
                IF oNewValue IS STRING VAR strExt
                    FPTMemo.DefExt := strExt
                ENDIF
            CASE DbInfo.DBI_MEMOBLOCKSIZE
                oResult := SELF:_oFptMemo:BlockSize
                IF oNewValue != NULL
                    TRY
                        LOCAL size AS LONG
                        size:= Convert.ToInt32(oNewValue)
                        SELF:_oFptMemo:BlockSize := (WORD) size
                    CATCH ex AS Exception
                        oResult := ""
                        SELF:_dbfError(ex, Subcodes.ERDD_DATATYPE, Gencode.EG_DATATYPE, __FUNCTION__)
                    END TRY
                ENDIF

            CASE DbInfo.DBI_MEMOFIELD
                oResult := ""
                IF oNewValue != NULL
                    TRY
                        LOCAL fldPos AS LONG
                        fldPos := Convert.ToInt32(oNewValue)
                        oResult := SELF:GetValue(fldPos)
                    CATCH ex AS Exception
                        oResult := ""
                        SELF:_dbfError(ex, Subcodes.ERDD_DATATYPE, Gencode.EG_DATATYPE, __FUNCTION__)
                    END TRY
                ENDIF
            CASE DbInfo.DBI_MEMOTYPE
                oResult := DB_MEMO_FPT
            CASE DbInfo.DBI_MEMOVERSION
                oResult := DB_MEMOVER_STD
            CASE DbInfo.BLOB_GET
                IF oNewValue IS XSharp.RDD.IBlobData VAR oBlob
                    oResult := _oFptMemo:BlobInfo(nOrdinal, oBlob)
                ENDIF

            CASE DbInfo.BLOB_NMODE
                IF oNewValue IS LONG VAR iExportMode
                    SELF:_oFptMemo:ExportMode := iExportMode
                ENDIF
            CASE DbFieldInfo.DBS_BLOB_DIRECT_TYPE
                IF oNewValue IS XSharp.RDD.IBlobData VAR oBlob
                    oResult := _oFptMemo:BlobInfo(nOrdinal, oBlob)
                ENDIF
            CASE DbFieldInfo.DBS_BLOB_DIRECT_LEN
                IF oNewValue IS XSharp.RDD.IBlobData VAR oBlob
                    oResult := _oFptMemo:BlobInfo(nOrdinal, oBlob)
                ENDIF
            CASE DbInfo.BLOB_DIRECT_IMPORT
                IF oNewValue IS XSharp.RDD.IBlobData VAR oBlob
                    oResult := _oFptMemo:BlobInfo(nOrdinal, oBlob)
                ENDIF
            CASE DbInfo.BLOB_DIRECT_EXPORT
                IF oNewValue IS XSharp.RDD.IBlobData VAR oBlob
                    oResult := _oFptMemo:BlobInfo(nOrdinal, oBlob)
                ENDIF
            CASE DbInfo.BLOB_DIRECT_GET
                IF oNewValue IS XSharp.RDD.IBlobData VAR oBlob
                    oResult := _oFptMemo:BlobInfo(nOrdinal, oBlob)
                ENDIF
            CASE DbInfo.BLOB_DIRECT_PUT
                IF oNewValue IS XSharp.RDD.IBlobData VAR oBlob
                    oResult := _oFptMemo:BlobInfo(nOrdinal, oBlob)
                ENDIF
            CASE DbInfo.BLOB_ROOT_GET
                IF oNewValue IS XSharp.RDD.IBlobData VAR oBlob
                    oResult := _oFptMemo:BlobInfo(nOrdinal, oBlob)
                ENDIF
            CASE DbInfo.BLOB_ROOT_PUT
                IF oNewValue IS XSharp.RDD.IBlobData VAR oBlob
                    oResult := _oFptMemo:BlobInfo(nOrdinal, oBlob)
                ENDIF
            CASE DbInfo.BLOB_ROOT_LOCK
                IF oNewValue IS XSharp.RDD.IBlobData VAR oBlob
                    oResult := _oFptMemo:BlobInfo(nOrdinal, oBlob)
                ENDIF
            CASE DbInfo.BLOB_ROOT_UNLOCK
                IF oNewValue IS XSharp.RDD.IBlobData VAR oBlob
                    oResult := _oFptMemo:BlobInfo(nOrdinal, oBlob)
                ENDIF
            OTHERWISE
                oResult := SUPER:Info(nOrdinal, oNewValue)
            END SWITCH
            RETURN oResult

        OVERRIDE METHOD PutValueFile(nFldPos AS INT, fileName AS STRING) AS LOGIC
           IF SELF:_ReadOnly
                SELF:_dbfError(ERDD.READONLY, XSharp.Gencode.EG_READONLY )
            ENDIF
            SELF:ForceRel()
            IF SELF:_readRecord()
                // GoHot() must be called first because this saves the current index values
                IF ! SELF:IsHot
                    SELF:GoHot()
                ENDIF
                RETURN SELF:_oFptMemo:PutValueFile(nFldPos, fileName)
            ENDIF
            RETURN FALSE
    END CLASS

 END NAMESPACE