[VODesigners] Fixed a crash with incorrect cavowed.inf

//
// Copyright (c) XSharp B.V.  All Rights Reserved.
// Licensed under the Apache License, Version 2.0.
// See License.txt in the project root for license information.
//
USING System.Collections.Generic
USING System.Collections
USING System.Windows.Forms
USING System.Drawing
USING System.IO
USING XSharpModel

INTERNAL ENUM CavoWedInfSection
	MEMBER None
	MEMBER Control
	MEMBER StdProp
	MEMBER AssignMap
	MEMBER Supplemental
	MEMBER options
END ENUM

STATIC CLASS VOWindowEditorTemplate
	STATIC PRIVATE aControls AS ArrayList
	STATIC PRIVATE aEnumTypes AS Dictionary<STRING,STRING>
	STATIC PRIVATE aAssignMap AS Dictionary<STRING,STRING>
	STATIC PRIVATE lLoaded AS LOGIC
	STATIC EXPORT aPages AS HashTable
	//STATIC EXPORT GridX , GridY AS INT
	STATIC CONSTRUCTOR()

		VOWindowEditorTemplate.aControls := ArrayList{}
		VOWindowEditorTemplate.aEnumTypes := Dictionary<STRING , STRING>{}
		VOWindowEditorTemplate.aAssignMap := Dictionary<STRING , STRING>{}
		VOWindowEditorTemplate.aPages := HashTable{}

		VOWindowEditorTemplate.aEnumTypes:Add("YESNO" , "Yes,No")
		VOWindowEditorTemplate.aEnumTypes:Add("__genBOOL" , "True,False")
		VOWindowEditorTemplate.aEnumTypes:Add("__menuTOOLBARSTYLE" , "No Toolbar,Flat Toolbar,Raised Toolbar")
		VOWindowEditorTemplate.aEnumTypes:Add("__menuSHOW" , "Text,Icon,Text and Icon")
		VOWindowEditorTemplate.aEnumTypes:Add("__menuToolBarButtons" , VOMenuEditor.VOMenuToolBarString)

		VOWindowEditorTemplate.aEnumTypes:Add("__fieldspecDataTypes" , "Character,Numeric,Date,Logic,Memo,OLE,Multimedia")

//		VOWindowEditorTemplate.aEnumTypes:Add("DIALOGPOSITON" , "Auto,Center,Center Mouse")

		VOWindowEditorTemplate.aAssignMap:Add("FILLUSING" , "FillUsing")
		VOWindowEditorTemplate.aAssignMap:Add("FONT" , "Font")

	RETURN
	STATIC METHOD Load(cDirectory AS STRING) AS LOGIC
		LOCAL cCavoWed AS STRING
      LOCAL cOrigDir AS STRING
		LOCAL lSuccess AS LOGIC

		IF VOWindowEditorTemplate.lLoaded
			RETURN TRUE
		ENDIF

//		oReader := StreamReader{"C:\CAVOWED.INF"}
//		oReader := StreamReader{"CAVOWED.INF"}
//		oReader := StreamReader{"C:\CAVO28\BIN\CAVOWED.INF"}
//		cCavoWed := "C:\CAVO28\BIN\CAVOWED.INF"
//		cCavoWed := "C:\CAVO28\BIN\CAVOWED.INF"
      cOrigDir := cDirectory
        TRY
        	cCavoWed := cDirectory + "\Properties\CAVOWED.INF"
	        IF !System.IO.File.Exists(cCavoWed)
		        cCavoWed := cDirectory + "\CAVOWED.INF"
				IF !System.IO.File.Exists(cCavoWed)
					cDirectory := Directory.GetParent(cDirectory):FullName
					cCavoWed := cDirectory + "\CAVOWED.INF"
			        IF !System.IO.File.Exists(cCavoWed)
			        	cCavoWed := cDirectory + "\Properties\CAVOWED.INF"
				        IF !System.IO.File.Exists(cCavoWed) .and. Funcs.InstallTemplatesFolder != ""
				        	cCavoWed := Funcs.InstallTemplatesFolder  + "\CAVOWED.INF"
				        ENDIF
			        ENDIF
				ENDIF
			END IF
         CATCH
            NOP
        END TRY
		IF !System.IO.File.Exists(cCavoWed)
			MessageBox.Show("File Cavowed.inf was not found, please locate it on disk." , Resources.EditorName)
		ENDIF
		DO WHILE !System.IO.File.Exists(cCavoWed)
			LOCAL oDlg AS OpenFileDialog
			oDlg := OpenFileDialog{}
			oDlg:Filter := "CavoWED files (*.inf)|*.inf"
			oDlg:Title := "Open cavowed.inf file"
			IF oDlg:ShowDialog() == DialogResult.OK
				cCavoWed := oDlg:FileName:ToLower()
            TRY
               IF cCavoWed:Contains("cavowed") .and. cCavoWed:EndsWith(".inf")
                  File.Copy(cCavoWed , cOrigDir + "\cavowed.inf" , FALSE)
               ENDIF
            CATCH
               NOP
            END TRY
			ELSE
				RETURN FALSE
			ENDIF
		END DO

		IF VOWindowEditorTemplate.LoadInfFile(cCavoWed)
			VOWindowEditorTemplate.lLoaded := TRUE
			lSuccess := TRUE
		ENDIF

	RETURN lSuccess
	INTERNAL STATIC METHOD LoadInfFile(cCavoWed AS STRING) AS LOGIC
		LOCAL oControl AS VOControlTemplate
		LOCAL oProp AS VODesignProperty
		LOCAL oReader AS StreamReader
		LOCAL cLine , cUpper AS STRING
		LOCAL cLeft , cRight AS STRING
		LOCAL cProp , cCaption AS STRING
		LOCAL cParent AS STRING
		LOCAL cPage AS STRING
		LOCAL cType AS STRING
		LOCAL cInitMethod AS STRING
		LOCAL cEnumValues AS STRING
		LOCAL eSection AS CavoWedInfSection
		LOCAL aPages AS List<STRING>
		LOCAL lSuccess AS LOGIC
		LOCAL nAt AS INT

		eSection := CavoWedInfSection.None

		TRY

			oReader := StreamReader{cCavoWed}
			DO WHILE !oReader:Peek() == -1
				cLine := oReader:ReadLine():Trim()
				cUpper := cLine:ToUpper()
				IF cLine:Length == 0
					LOOP
				ENDIF
				cType := ""
                cInitMethod := ""
				IF cUpper[cUpper:Length - 1] == ')' .OR. cUpper:StartsWith("INITMETHOD")
					nAt := cUpper:LastIndexOf('(')
					IF nAt != -1
						cType := cLine:Substring(nAt + 1 , cLine:Length - nAt - 2):ToUpper()
						nAt := cLine:IndexOf('(')
						IF nAt == -1
							nAt := cLine:IndexOf("=")
							IF nAt == -1
								nAt := 0
							ELSE
								nAt ++
							END IF
						END IF
						cInitMethod := cLine:Substring(nAt)
						cUpper := cUpper:Substring(0 , nAt)
						cLine := cLine:Substring(0 , nAt)
					ENDIF
				ENDIF
				DO CASE
				CASE cLine[0] == ';' .or. cLine[0] == '/'
					LOOP
				CASE cLine[0] == '['
					cUpper := cUpper:Substring(1 , cUpper:Length - 2)
					DO CASE
					CASE cUpper:IndexOf("CONTROL") == 0 .or. cUpper:IndexOf("FORM") == 0
						eSection := CavoWedInfSection.Control
						oControl := VOControlTemplate{}
						oControl:lForm := cUpper:IndexOf("FORM") == 0
						oControl:cFullClass := cUpper
						IF VOWindowEditorTemplate.GetFromFullClass(oControl:cFullClass) == NULL
							VOWindowEditorTemplate.aControls:Add(oControl)
						END IF
						cParent := ""
						nAt := cUpper:LastIndexOf(':')
						IF nAt == -1
							oControl:aProperties:Add(VODesignProperty{"_Left","Left",NULL,PropertyType.Numeric})
							oControl:aProperties:Add(VODesignProperty{"_Top","Top",NULL,PropertyType.Numeric})
							oControl:aProperties:Add(VODesignProperty{"_Width","Width",NULL,PropertyType.Numeric})
							oControl:aProperties:Add(VODesignProperty{"_Height","Height",NULL,PropertyType.Numeric})
							oControl:aProperties:Add(VODesignProperty{"_Visible","Visible",NULL,"YESNO"})
							oControl:aProperties:Add(VODesignProperty{"_GenCode","Generate Code",NULL,"YESNO"})
//							oControl:aProperties:Add(VODesignProperty{"Inherit From Class","Inherit From Class",PropertyType.Text})

							oControl:aProperties:Add(VODesignProperty{"_DBInhFrom","Browser Inherit From",NULL,PropertyType.Text})
							oControl:aProperties:Add(VODesignProperty{"_DCInhFrom","Columns Inherit From",NULL,PropertyType.Text})

							oControl:aProperties:Add(VODesignProperty{"_IsDataPage","Page Data Aware",NULL,"YESNO"})
							oControl:aProperties:Add(VODesignProperty{"_PageCaption","Page Caption",NULL,PropertyType.Text})
							oControl:aProperties:Add(VODesignProperty{"_PageName","Page Name",NULL,PropertyType.Text})
						ELSE
							cParent := cUpper:Substring(0 , nAt)
							cUpper := cUpper:Substring(nAt + 1)
/*							nAt := cParent:LastIndexOf(':')
							IF nAt != -1
								cParent := cParent:Substring(nAt + 1)
							ENDIF*/
							oControl:AddFrom(cParent)
						ENDIF
						oControl:cParent := cParent
						oControl:cControl := cUpper
					CASE cUpper == "STDPROPERTIES"
						eSection := CavoWedInfSection.StdProp
					CASE cUpper == "ASSIGNMAP"
						eSection := CavoWedInfSection.AssignMap
					CASE cUpper == "SUPPLEMENTALFILES"
						eSection := CavoWedInfSection.Supplemental
					CASE cUpper == "OPTIONS"
						eSection := CavoWedInfSection.Options
					OTHERWISE
						eSection := CavoWedInfSection.None
					ENDCASE
				CASE eSection == CavoWedInfSection.StdProp
					nAt := cLine:IndexOf('=')
					IF nAt != -1
						cLeft := cLine:Substring(0 , nAt):ToUpper()
						cRight := cLine:Substring(nAt + 1)
						IF cLine:LastIndexOf(')') == -1
							VOWindowEditorTemplate.aEnumTypes:Add(cLeft , cRight)
						ELSE
							nAt := cLine:LastIndexOf(')')
							cRight := cLine:Substring(nAt + 1)
							aPages := Funcs.SplitString(cRight , ',')
/*							aPages := List<STRING>{}
							DO WHILE cRight:Length != 0
								nAt := cRight:IndexOf(',')
								IF nAt == -1
									aPages:Add(cRight)
									cRight := ""
								ELSE
									aPages:Add(cRight:Substring(0 , nAt))
									cRight := cRight:Substring(nAt + 1)
								ENDIF
							END DO*/
							VOWindowEditorTemplate.aPages:Add(cLeft , aPages)
						ENDIF
					ENDIF
				CASE eSection == CavoWedInfSection.Control
					nAt := cLine:IndexOf('=')
					IF nAt != -1
						cLeft := cLine:Substring(0 , nAt):ToUpper()
						cRight := cLine:Substring(nAt + 1)
						DO CASE
						CASE cLeft == "NAME"
							oControl:cName := cRight
							oControl:lUse := TRUE
						CASE cLeft == "TEXT"
							oControl:cStartText := cRight
						CASE cLeft == "CONTTITLE"
							oControl:cTitle := cRight
						CASE cLeft == "CLASSNAME"
							IF oControl:cWinClass == "" // might have been already assigned by OptClassName
								oControl:cWinClass := cRight
							END IF
						CASE cLeft == "OPTCLASSNAME"
							oControl:cWinClass := cRight
						CASE cLeft == "TYPE"
							IF cRight:ToUpper() == "COMMANDCONTROL"
								oControl:lForceCommandControl := TRUE
							ENDIF
						CASE cLeft == "DIALOGTEMPLATE"
							oControl:lCreateResource := cRight == "1"
						CASE cLeft == "INITMETHOD"
/*							nAt := cRight:IndexOf('(')
							IF nAt != -1
								cRight := cRight:Substring(nAt + 1)
								nAt := cRight:LastIndexOf(')')
								IF nAt != -1
									cRight := cRight:Substring(0, nAt - 1)
									oControl:cInitMethod := cRight
								ENDIF
							ENDIF*/
							oControl:cInitMethod := cInitMethod
						CASE cLeft == "STYLE"
							oControl:aStyles := Funcs.SplitString(cRight , '|')
						CASE cLeft == "EXSTYLE"
							oControl:aExStyles := Funcs.SplitString(cRight , '|')
						CASE cLeft == "SIZE"
							TRY
								nAt := cRight:IndexOf(',')
								oControl:oSize := Size{ Convert.ToInt32(cRight:Substring(0 , nAt)) , Convert.ToInt32(cRight:Substring(nAt + 1)) }
                     CATCH
                        NOP
							END TRY
						CASE cLeft == "PROPTABS"
							aPages := oControl:aPages
							DO WHILE cRight:Length != 0
								nAt := cRight:IndexOf(',')
								IF nAt == -1
									cPage := cRight
									cRight := ""
								ELSE
									cPage := cRight:Substring(0 , nAt)
									cRight := cRight:Substring(nAt + 1)
								ENDIF
/*								nAt := cPage:IndexOf(':')
								IF nAt != -1
									cPage := cPage:Substring(nAt + 1)
								ENDIF*/
								aPages:Add(cPage)
							END DO
						CASE cLeft:IndexOf("WINDOWSTYLE") == 0 .or. cLeft:IndexOf("EXWINDOWSTYLE") == 0
							nAt := cRight:IndexOf(',')
							IF nAt != - 1
								cCaption := cRight:Substring(0 , nAt)
								cEnumValues := cRight:Substring(nAt + 1)
								IF cLeft:IndexOf("WINDOWSTYLE") == 0
									oControl:cRawStyles := cEnumValues
									oProp := VODesignProperty{cCaption , cType , cEnumValues , VOStyle.Style}
									oControl:aProperties:Add(oProp)
								ELSE
									oProp := VODesignProperty{cCaption , cType , cEnumValues , VOStyle.ExStyle}
									oControl:aProperties:Add(oProp)
								ENDIF
							ENDIF

						CASE cLeft:IndexOf("ASSIGN") == 0 .or. cLeft:IndexOf("INHERITCLASSNAME") == 0
							nAt := cRight:IndexOf(',')
							IF nAt != - 1
								cCaption := cRight:Substring(0 , nAt)
								cProp := cRight:Substring(nAt + 1)
								DO CASE
								CASE cProp:ToUpper():IndexOf("HYPERLABEL") != -1 .and. FALSE
									oProp := VODesignProperty{cCaption , cCaption , cCaption , PropertyType.Text}
									oProp:lMultiple := TRUE
									oProp:lNoCode := TRUE
									oProp:lNoAuto := TRUE
									oControl:aProperties:Add(oProp)
								CASE cType == "STRING"
									oProp := VODesignProperty{cCaption , cCaption , cProp , PropertyType.Text}
									oControl:aProperties:Add(oProp)
								CASE cType == "NUMERIC"
									oProp := VODesignProperty{cCaption , cCaption , cProp , PropertyType.Numeric}
									oControl:aProperties:Add(oProp)
								CASE cType:IndexOf("CLASS:") == 0
									oProp := VODesignProperty{cCaption , cCaption , cProp , PropertyType.Type}
									nAt := cType:IndexOf("CLASS:")
									oProp:cType := cType:Substring(nAt + 6)
									oControl:aProperties:Add(oProp)
								CASE cType:IndexOf(',') == -1 .AND. cType:Length != 0
									IF cType[0] != '%'
										oProp := VODesignProperty{cCaption , cCaption , cProp , cType}
									ELSE
										cType := cType:Substring(1 , cType:Length - 2)
										oProp := VODesignProperty{cCaption , cCaption , cProp , PropertyType.Text}
										oProp:cSymbolProp := cType
									ENDIF
									oControl:aProperties:Add(oProp)
								CASE cType:IndexOf(',') != -1
									LOCAL nMultiPos AS INT
									nAt := 0
									nMultiPos := 1
									DO WHILE nAt < cType:Length
										IF cType[nAt] == ','
											nMultiPos ++
											nAt ++
										ELSE
											cType := cType:Substring(nAt)
											nAt := cType:IndexOf(',')
											IF nAt != -1
												cType := cType:Substring(0 , nAt)
											ENDIF
											nAt := cType:IndexOf(')')
											IF nAt != -1
												cType := cType:Substring(0 , nAt)
											ENDIF
//											System.Windows.Forms.MessageBox.Show(cType)
											cType := cType:ToUpper()
											DO CASE
											CASE cType:ToUpper() == "NUMERIC"
												oProp := VODesignProperty{cCaption , cCaption , cProp , PropertyType.Numeric}
											CASE cType[0] == '%'
												cType := cType:Substring(1 , cType:Length - 2)
												oProp := VODesignProperty{cCaption , cCaption , cProp , PropertyType.Text}
												oProp:cSymbolProp := cType
											OTHERWISE
												oProp := VODesignProperty{cCaption , cCaption , cProp , PropertyType.Text}
											END CASE
											oProp:lMultiple := TRUE
											oProp:nMultiPos := nMultiPos
											EXIT
										ENDIF
									END DO
									oControl:aProperties:Add(oProp)
								END CASE
							ENDIF

						CASE cLeft:IndexOf("METHOD") == 0
							nAt := cRight:IndexOf(',')
							IF nAt != - 1
								cCaption := cRight:Substring(0 , nAt)
								cProp := cRight:Substring(nAt + 1)
								DO CASE
								CASE cType:IndexOf("CODE:") == 0
									IF cType:Length > 5
										cProp := cType:Substring(5):Trim()
									END IF
									oProp := VODesignProperty{cCaption , cCaption , cProp , PropertyType.Callback}
									oControl:aProperties:Add(oProp)
								CASE cType == "STRING"
									oProp := VODesignProperty{cCaption , cCaption , cProp , PropertyType.Text}
									oProp:lMethod := TRUE
									oControl:aProperties:Add(oProp)
								CASE cCaption == "Group Value" .and. cProp == "Value" // DIRTY HACK
									oProp := VODesignProperty{cCaption , cCaption , cProp , PropertyType.Text}
									oProp:lMethod := TRUE
									oControl:aProperties:Add(oProp)
								CASE cType:IndexOf("CLASS:") == 0
									oProp := VODesignProperty{cCaption , cCaption , cProp , PropertyType.Type}
									oProp:lMethod := TRUE
									oControl:aProperties:Add(oProp)
//								CASE cType == "BOOL"
//								CASE cType == "LOGIC"
								OTHERWISE
									oProp := VODesignProperty{cCaption , cCaption , cProp , cType}
									oProp:lMethod := TRUE
									oControl:aProperties:Add(oProp)
								END CASE
							ENDIF

						END CASE
					ENDIF

				CASE eSection == CavoWedInfSection.AssignMap
					nAt := cLine:IndexOf('=')
					IF nAt != -1
						cLeft := cLine:Substring(0 , nAt):ToUpper()
						cRight := cLine:Substring(nAt + 1)
						IF !VOWindowEditorTemplate.aAssignMap:ContainsKey(cLeft)
							VOWindowEditorTemplate.aAssignMap:Add(cLeft , cRight)
						ENDIF
					ENDIF

				CASE eSection == CavoWedInfSection.Supplemental
					nAt := cLine:IndexOf('=')
					IF nAt != -1
						cLeft := cLine:Substring(0 , nAt):ToUpper()
						cRight := cLine:Substring(nAt + 1):Trim()
						IF cRight:Length != 0
							nAt := cRight:LastIndexOf("\")
							IF nAt != -1 .and. nAt < cRight:Length - 1
								cRight := cRight:Substring(nAt + 1):Trim()
							END IF
							cRight := Funcs.GetFileDir(cCavoWed) + "\" + cRight
							IF System.IO.File.Exists(cRight)
								VOWindowEditorTemplate.LoadInfFile(cRight)
							ELSE
								Funcs.ErrorBox("Could not find file " + cRight , Resources.EditorName)
							ENDIF
						ENDIF
					ENDIF

/*				CASE eSection == CavoWedInfSection.Options
					nAt := cLine:IndexOf('=')
					IF nAt != -1
						cLeft := cLine:Substring(0 , nAt):ToUpper()
						cRight := cLine:Substring(nAt + 1):Trim()
						TRY
							DO CASE
							CASE cLeft == "GRIDX"
								VOWindowEditorTemplate.GridX := Int32.Parse(cRight)
							CASE cLeft == "GRIDY"
								VOWindowEditorTemplate.GridY := Int32.Parse(cRight)
							END CASE
						CATCH
                            NOP
						END TRY
					ENDIF*/

				END CASE

			END DO

			lSuccess := TRUE

		CATCH

			Funcs.ErrorBox("Error loading file " + cCavoWed , Resources.EditorName)

		FINALLY

			IF oReader != NULL
				oReader:Close()
			END IF

		END TRY

	RETURN lSuccess

	STATIC ACCESS Loaded AS LOGIC
	RETURN VOWindowEditorTemplate.lLoaded
	STATIC ACCESS Count AS INT
	RETURN VOWindowEditorTemplate.aControls:Count
	STATIC METHOD Get(n AS INT) AS VOControlTemplate
	RETURN (VOControlTemplate)VOWindowEditorTemplate.aControls[n]
	STATIC METHOD Get(cControl AS STRING) AS VOControlTemplate
		cControl := cControl:ToUpper()
		FOREACH oTemplate AS VOControlTemplate IN VOWindowEditorTemplate.aControls
			IF oTemplate:cControl:ToUpper() == cControl
				RETURN oTemplate
			ENDIF
		NEXT
	RETURN NULL

	STATIC METHOD GetFromFullClass(cFullClass AS STRING) AS VOControlTemplate
		cFullClass := cFullClass:ToUpper()
		FOREACH oTemplate AS VOControlTemplate IN VOWindowEditorTemplate.aControls
			IF oTemplate:cFullClass:ToUpper() == cFullClass
				RETURN oTemplate
			ENDIF
		NEXT
	RETURN NULL

	STATIC METHOD GetParent(cParent AS STRING) AS VOControlTemplate
		cParent := cParent:ToUpper()
		FOREACH oTemplate AS VOControlTemplate IN VOWindowEditorTemplate.aControls
			IF oTemplate:cFullClass:ToUpper() == cParent
				RETURN oTemplate
			ENDIF
		NEXT
	RETURN NULL
	STATIC METHOD GetEnumerator(cKey AS STRING) AS STRING
		IF VOWindowEditorTemplate.aEnumTypes:ContainsKey(cKey)
			RETURN VOWindowEditorTemplate.aEnumTypes[cKey]
		ENDIF
		DO WHILE cKey:Length > 3 // TODO looks like the VO WED works this way, for all types
			cKey := cKey:Substring(0, cKey:Length - 1)
			IF VOWindowEditorTemplate.aEnumTypes:ContainsKey(cKey)
				RETURN VOWindowEditorTemplate.aEnumTypes[cKey]
			ENDIF
		END DO
	RETURN NULL
	STATIC METHOD GetAssignMap(cKey AS STRING) AS STRING
		cKey := cKey:ToUpper()
		IF VOWindowEditorTemplate.aAssignMap:ContainsKey(cKey)
			RETURN VOWindowEditorTemplate.aAssignMap[cKey]
		ENDIF
	RETURN NULL

END CLASS

CLASS VOControlTemplate
	EXPORT cControl AS STRING
	EXPORT cParent AS STRING
	EXPORT cName AS STRING
	EXPORT cFullClass AS STRING
	EXPORT cStartText AS STRING
	EXPORT oSize AS Size
	EXPORT lForm AS LOGIC
	EXPORT lUse AS LOGIC
	EXPORT cTitle AS STRING
	EXPORT cWinClass AS STRING
	EXPORT aPages AS List<STRING>
	EXPORT aProperties AS ArrayList
	EXPORT aStyles AS List<STRING>
	EXPORT aExStyles AS List<STRING>
	EXPORT cInitMethod AS STRING
	EXPORT lCreateResource AS LOGIC
	EXPORT lForceCommandControl AS LOGIC
	EXPORT cRawStyles AS STRING
	CONSTRUCTOR()
		SUPER()
		SELF:cStartText := ""
		SELF:cName := ""
		SELF:cParent := ""
		SELF:cFullClass := ""
		SELF:cControl := ""
		SELF:cTitle := ""
		SELF:cWinClass := ""
		SELF:cInitMethod := ""
		SELF:cRawStyles := ""
		SELF:aProperties := ArrayList{}
		SELF:aPages := List<STRING>{}
      SELF:aExStyles := List<STRING>{}
      SELF:aStyles := List<STRING>{}
	RETURN
	METHOD AddFrom(cControl AS STRING) AS VOID
		LOCAL oTemplate AS VOControlTemplate
		LOCAL n AS INT
//		oTemplate := VOWindowEditorTemplate.Get(cControl)
		oTemplate := VOWindowEditorTemplate.GetParent(cControl)
		IF oTemplate != NULL
			FOR n := 0 UPTO oTemplate:aProperties:Count - 1
				SELF:aProperties:Add(oTemplate:aProperties[n])
				SELF:lCreateResource := oTemplate:lCreateResource
			NEXT
			IF oTemplate:aExStyles != NULL
				SELF:aExStyles := List<STRING>{}
				FOR n := 0 UPTO oTemplate:aExStyles:Count - 1
					SELF:aExStyles:Add(oTemplate:aExStyles[n])
				NEXT
			END IF
		ENDIF
	RETURN

END CLASS

//
// Copyright (c) XSharp B.V.  All Rights Reserved.
// Licensed under the Apache License, Version 2.0.
// See License.txt in the project root for license information.
//
USING System.Collections.Generic
USING System.Collections
USING System.Windows.Forms
USING System.Drawing
USING System.IO
USING XSharpModel

INTERNAL ENUM CavoWedInfSection
	MEMBER None
	MEMBER Control
	MEMBER StdProp
	MEMBER AssignMap
	MEMBER Supplemental
	MEMBER options
END ENUM

STATIC CLASS VOWindowEditorTemplate
	STATIC PRIVATE aControls AS ArrayList
	STATIC PRIVATE aEnumTypes AS Dictionary<STRING,STRING>
	STATIC PRIVATE aAssignMap AS Dictionary<STRING,STRING>
	STATIC PRIVATE lLoaded AS LOGIC
	STATIC EXPORT aPages AS HashTable
	//STATIC EXPORT GridX , GridY AS INT
	STATIC CONSTRUCTOR()

		VOWindowEditorTemplate.aControls := ArrayList{}
		VOWindowEditorTemplate.aEnumTypes := Dictionary<STRING , STRING>{}
		VOWindowEditorTemplate.aAssignMap := Dictionary<STRING , STRING>{}
		VOWindowEditorTemplate.aPages := HashTable{}

		VOWindowEditorTemplate.aEnumTypes:Add("YESNO" , "Yes,No")
		VOWindowEditorTemplate.aEnumTypes:Add("__genBOOL" , "True,False")
		VOWindowEditorTemplate.aEnumTypes:Add("__menuTOOLBARSTYLE" , "No Toolbar,Flat Toolbar,Raised Toolbar")
		VOWindowEditorTemplate.aEnumTypes:Add("__menuSHOW" , "Text,Icon,Text and Icon")
		VOWindowEditorTemplate.aEnumTypes:Add("__menuToolBarButtons" , VOMenuEditor.VOMenuToolBarString)

		VOWindowEditorTemplate.aEnumTypes:Add("__fieldspecDataTypes" , "Character,Numeric,Date,Logic,Memo,OLE,Multimedia")

//		VOWindowEditorTemplate.aEnumTypes:Add("DIALOGPOSITON" , "Auto,Center,Center Mouse")

		VOWindowEditorTemplate.aAssignMap:Add("FILLUSING" , "FillUsing")
		VOWindowEditorTemplate.aAssignMap:Add("FONT" , "Font")

	RETURN
	STATIC METHOD Load(cDirectory AS STRING) AS LOGIC
		LOCAL cCavoWed AS STRING
      LOCAL cOrigDir AS STRING
		LOCAL lSuccess AS LOGIC

		IF VOWindowEditorTemplate.lLoaded
			RETURN TRUE
		ENDIF

//		oReader := StreamReader{"C:\CAVOWED.INF"}
//		oReader := StreamReader{"CAVOWED.INF"}
//		oReader := StreamReader{"C:\CAVO28\BIN\CAVOWED.INF"}
//		cCavoWed := "C:\CAVO28\BIN\CAVOWED.INF"
//		cCavoWed := "C:\CAVO28\BIN\CAVOWED.INF"
      cOrigDir := cDirectory
        TRY
        	cCavoWed := cDirectory + "\Properties\CAVOWED.INF"
	        IF !System.IO.File.Exists(cCavoWed)
		        cCavoWed := cDirectory + "\CAVOWED.INF"
				IF !System.IO.File.Exists(cCavoWed)
					cDirectory := Directory.GetParent(cDirectory):FullName
					cCavoWed := cDirectory + "\CAVOWED.INF"
			        IF !System.IO.File.Exists(cCavoWed)
			        	cCavoWed := cDirectory + "\Properties\CAVOWED.INF"
				        IF !System.IO.File.Exists(cCavoWed) .and. Funcs.InstallTemplatesFolder != ""
				        	cCavoWed := Funcs.InstallTemplatesFolder  + "\CAVOWED.INF"
				        ENDIF
			        ENDIF
				ENDIF
			END IF
         CATCH
            NOP
        END TRY
		IF !System.IO.File.Exists(cCavoWed)
			MessageBox.Show("File Cavowed.inf was not found, please locate it on disk." , Resources.EditorName)
		ENDIF
		DO WHILE !System.IO.File.Exists(cCavoWed)
			LOCAL oDlg AS OpenFileDialog
			oDlg := OpenFileDialog{}
			oDlg:Filter := "CavoWED files (*.inf)|*.inf"
			oDlg:Title := "Open cavowed.inf file"
			IF oDlg:ShowDialog() == DialogResult.OK
				cCavoWed := oDlg:FileName:ToLower()
            TRY
               IF cCavoWed:Contains("cavowed") .and. cCavoWed:EndsWith(".inf")
                  File.Copy(cCavoWed , cOrigDir + "\cavowed.inf" , FALSE)
               ENDIF
            CATCH
               NOP
            END TRY
			ELSE
				RETURN FALSE
			ENDIF
		END DO

		IF VOWindowEditorTemplate.LoadInfFile(cCavoWed)
			VOWindowEditorTemplate.lLoaded := TRUE
			lSuccess := TRUE
		ENDIF

	RETURN lSuccess
	INTERNAL STATIC METHOD LoadInfFile(cCavoWed AS STRING) AS LOGIC
		LOCAL oControl AS VOControlTemplate
		LOCAL oProp AS VODesignProperty
		LOCAL oReader AS StreamReader
		LOCAL cLine , cUpper AS STRING
		LOCAL cLeft , cRight AS STRING
		LOCAL cProp , cCaption AS STRING
		LOCAL cParent AS STRING
		LOCAL cPage AS STRING
		LOCAL cType AS STRING
		LOCAL cInitMethod AS STRING
		LOCAL cEnumValues AS STRING
		LOCAL eSection AS CavoWedInfSection
		LOCAL aPages AS List<STRING>
		LOCAL lSuccess AS LOGIC
		LOCAL nAt AS INT

		eSection := CavoWedInfSection.None

		TRY

			oReader := StreamReader{cCavoWed}
			DO WHILE !oReader:Peek() == -1
				cLine := oReader:ReadLine():Trim()
				cUpper := cLine:ToUpper()
				IF cLine:Length == 0
					LOOP
				ENDIF
				cType := ""
                cInitMethod := ""
				IF cUpper[cUpper:Length - 1] == ')' .OR. cUpper:StartsWith("INITMETHOD")
					nAt := cUpper:LastIndexOf('(')
					IF nAt != -1
						cType := cLine:Substring(nAt + 1 , cLine:Length - nAt - 2):ToUpper()
						nAt := cLine:IndexOf('(')
						IF nAt == -1
							nAt := cLine:IndexOf("=")
							IF nAt == -1
								nAt := 0
							ELSE
								nAt ++
							END IF
						END IF
						cInitMethod := cLine:Substring(nAt)
						cUpper := cUpper:Substring(0 , nAt)
						cLine := cLine:Substring(0 , nAt)
					ENDIF
				ENDIF
				DO CASE
				CASE cLine[0] == ';' .or. cLine[0] == '/'
					LOOP
				CASE cLine[0] == '['
					cUpper := cUpper:Substring(1 , cUpper:Length - 2)
					DO CASE
					CASE cUpper:IndexOf("CONTROL") == 0 .or. cUpper:IndexOf("FORM") == 0
						eSection := CavoWedInfSection.Control
						oControl := VOControlTemplate{}
						oControl:lForm := cUpper:IndexOf("FORM") == 0
						oControl:cFullClass := cUpper
						IF VOWindowEditorTemplate.GetFromFullClass(oControl:cFullClass) == NULL
							VOWindowEditorTemplate.aControls:Add(oControl)
						END IF
						cParent := ""
						nAt := cUpper:LastIndexOf(':')
						IF nAt == -1
							oControl:aProperties:Add(VODesignProperty{"_Left","Left",NULL,PropertyType.Numeric})
							oControl:aProperties:Add(VODesignProperty{"_Top","Top",NULL,PropertyType.Numeric})
							oControl:aProperties:Add(VODesignProperty{"_Width","Width",NULL,PropertyType.Numeric})
							oControl:aProperties:Add(VODesignProperty{"_Height","Height",NULL,PropertyType.Numeric})
							oControl:aProperties:Add(VODesignProperty{"_Visible","Visible",NULL,"YESNO"})
							oControl:aProperties:Add(VODesignProperty{"_GenCode","Generate Code",NULL,"YESNO"})
//							oControl:aProperties:Add(VODesignProperty{"Inherit From Class","Inherit From Class",PropertyType.Text})

							oControl:aProperties:Add(VODesignProperty{"_DBInhFrom","Browser Inherit From",NULL,PropertyType.Text})
							oControl:aProperties:Add(VODesignProperty{"_DCInhFrom","Columns Inherit From",NULL,PropertyType.Text})

							oControl:aProperties:Add(VODesignProperty{"_IsDataPage","Page Data Aware",NULL,"YESNO"})
							oControl:aProperties:Add(VODesignProperty{"_PageCaption","Page Caption",NULL,PropertyType.Text})
							oControl:aProperties:Add(VODesignProperty{"_PageName","Page Name",NULL,PropertyType.Text})
						ELSE
							cParent := cUpper:Substring(0 , nAt)
							cUpper := cUpper:Substring(nAt + 1)
/*							nAt := cParent:LastIndexOf(':')
							IF nAt != -1
								cParent := cParent:Substring(nAt + 1)
							ENDIF*/
							oControl:AddFrom(cParent)
						ENDIF
						oControl:cParent := cParent
						oControl:cControl := cUpper
					CASE cUpper == "STDPROPERTIES"
						eSection := CavoWedInfSection.StdProp
					CASE cUpper == "ASSIGNMAP"
						eSection := CavoWedInfSection.AssignMap
					CASE cUpper == "SUPPLEMENTALFILES"
						eSection := CavoWedInfSection.Supplemental
					CASE cUpper == "OPTIONS"
						eSection := CavoWedInfSection.Options
					OTHERWISE
						eSection := CavoWedInfSection.None
					ENDCASE
				CASE eSection == CavoWedInfSection.StdProp
					nAt := cLine:IndexOf('=')
					IF nAt != -1
						cLeft := cLine:Substring(0 , nAt):ToUpper()
						cRight := cLine:Substring(nAt + 1)
						IF cLine:LastIndexOf(')') == -1
							VOWindowEditorTemplate.aEnumTypes:Add(cLeft , cRight)
						ELSE
							nAt := cLine:LastIndexOf(')')
							cRight := cLine:Substring(nAt + 1)
							aPages := Funcs.SplitString(cRight , ',')
/*							aPages := List<STRING>{}
							DO WHILE cRight:Length != 0
								nAt := cRight:IndexOf(',')
								IF nAt == -1
									aPages:Add(cRight)
									cRight := ""
								ELSE
									aPages:Add(cRight:Substring(0 , nAt))
									cRight := cRight:Substring(nAt + 1)
								ENDIF
							END DO*/
							VOWindowEditorTemplate.aPages:Add(cLeft , aPages)
						ENDIF
					ENDIF
				CASE eSection == CavoWedInfSection.Control
					nAt := cLine:IndexOf('=')
					IF nAt != -1
						cLeft := cLine:Substring(0 , nAt):ToUpper()
						cRight := cLine:Substring(nAt + 1)
						DO CASE
						CASE cLeft == "NAME"
							oControl:cName := cRight
							oControl:lUse := TRUE
						CASE cLeft == "TEXT"
							oControl:cStartText := cRight
						CASE cLeft == "CONTTITLE"
							oControl:cTitle := cRight
						CASE cLeft == "CLASSNAME"
							IF oControl:cWinClass == "" // might have been already assigned by OptClassName
								oControl:cWinClass := cRight
							END IF
						CASE cLeft == "OPTCLASSNAME"
							oControl:cWinClass := cRight
						CASE cLeft == "TYPE"
							IF cRight:ToUpper() == "COMMANDCONTROL"
								oControl:lForceCommandControl := TRUE
							ENDIF
						CASE cLeft == "DIALOGTEMPLATE"
							oControl:lCreateResource := cRight == "1"
						CASE cLeft == "INITMETHOD"
/*							nAt := cRight:IndexOf('(')
							IF nAt != -1
								cRight := cRight:Substring(nAt + 1)
								nAt := cRight:LastIndexOf(')')
								IF nAt != -1
									cRight := cRight:Substring(0, nAt - 1)
									oControl:cInitMethod := cRight
								ENDIF
							ENDIF*/
							oControl:cInitMethod := cInitMethod
						CASE cLeft == "STYLE"
							oControl:aStyles := Funcs.SplitString(cRight , '|')
						CASE cLeft == "EXSTYLE"
							oControl:aExStyles := Funcs.SplitString(cRight , '|')
						CASE cLeft == "SIZE"
							TRY
								nAt := cRight:IndexOf(',')
								oControl:oSize := Size{ Convert.ToInt32(cRight:Substring(0 , nAt)) , Convert.ToInt32(cRight:Substring(nAt + 1)) }
                     CATCH
                        NOP
							END TRY
						CASE cLeft == "PROPTABS"
							aPages := oControl:aPages
							DO WHILE cRight:Length != 0
								nAt := cRight:IndexOf(',')
								IF nAt == -1
									cPage := cRight
									cRight := ""
								ELSE
									cPage := cRight:Substring(0 , nAt)
									cRight := cRight:Substring(nAt + 1)
								ENDIF
/*								nAt := cPage:IndexOf(':')
								IF nAt != -1
									cPage := cPage:Substring(nAt + 1)
								ENDIF*/
								aPages:Add(cPage)
							END DO
						CASE cLeft:IndexOf("WINDOWSTYLE") == 0 .or. cLeft:IndexOf("EXWINDOWSTYLE") == 0
							nAt := cRight:IndexOf(',')
							IF nAt != - 1
								cCaption := cRight:Substring(0 , nAt)
								cEnumValues := cRight:Substring(nAt + 1)
								IF cLeft:IndexOf("WINDOWSTYLE") == 0
									oControl:cRawStyles := cEnumValues
									oProp := VODesignProperty{cCaption , cType , cEnumValues , VOStyle.Style}
									oControl:aProperties:Add(oProp)
								ELSE
									oProp := VODesignProperty{cCaption , cType , cEnumValues , VOStyle.ExStyle}
									oControl:aProperties:Add(oProp)
								ENDIF
							ENDIF

						CASE cLeft:IndexOf("ASSIGN") == 0 .or. cLeft:IndexOf("INHERITCLASSNAME") == 0
							nAt := cRight:IndexOf(',')
							IF nAt != - 1
								cCaption := cRight:Substring(0 , nAt)
								cProp := cRight:Substring(nAt + 1)
								DO CASE
								CASE cProp:ToUpper():IndexOf("HYPERLABEL") != -1 .and. FALSE
									oProp := VODesignProperty{cCaption , cCaption , cCaption , PropertyType.Text}
									oProp:lMultiple := TRUE
									oProp:lNoCode := TRUE
									oProp:lNoAuto := TRUE
									oControl:aProperties:Add(oProp)
								CASE cType == "STRING"
									oProp := VODesignProperty{cCaption , cCaption , cProp , PropertyType.Text}
									oControl:aProperties:Add(oProp)
								CASE cType == "NUMERIC"
									oProp := VODesignProperty{cCaption , cCaption , cProp , PropertyType.Numeric}
									oControl:aProperties:Add(oProp)
								CASE cType:IndexOf("CLASS:") == 0
									oProp := VODesignProperty{cCaption , cCaption , cProp , PropertyType.Type}
									nAt := cType:IndexOf("CLASS:")
									oProp:cType := cType:Substring(nAt + 6)
									oControl:aProperties:Add(oProp)
								CASE cType:IndexOf(',') == -1
									IF cType[0] != '%'
										oProp := VODesignProperty{cCaption , cCaption , cProp , cType}
									ELSE
										cType := cType:Substring(1 , cType:Length - 2)
										oProp := VODesignProperty{cCaption , cCaption , cProp , PropertyType.Text}
										oProp:cSymbolProp := cType
									ENDIF
									oControl:aProperties:Add(oProp)
								CASE cType:IndexOf(',') != -1
									LOCAL nMultiPos AS INT
									nAt := 0
									nMultiPos := 1
									DO WHILE nAt < cType:Length
										IF cType[nAt] == ','
											nMultiPos ++
											nAt ++
										ELSE
											cType := cType:Substring(nAt)
											nAt := cType:IndexOf(',')
											IF nAt != -1
												cType := cType:Substring(0 , nAt)
											ENDIF
											nAt := cType:IndexOf(')')
											IF nAt != -1
												cType := cType:Substring(0 , nAt)
											ENDIF
//											System.Windows.Forms.MessageBox.Show(cType)
											cType := cType:ToUpper()
											DO CASE
											CASE cType:ToUpper() == "NUMERIC"
												oProp := VODesignProperty{cCaption , cCaption , cProp , PropertyType.Numeric}
											CASE cType[0] == '%'
												cType := cType:Substring(1 , cType:Length - 2)
												oProp := VODesignProperty{cCaption , cCaption , cProp , PropertyType.Text}
												oProp:cSymbolProp := cType
											OTHERWISE
												oProp := VODesignProperty{cCaption , cCaption , cProp , PropertyType.Text}
											END CASE
											oProp:lMultiple := TRUE
											oProp:nMultiPos := nMultiPos
											EXIT
										ENDIF
									END DO
									oControl:aProperties:Add(oProp)
								END CASE
							ENDIF

						CASE cLeft:IndexOf("METHOD") == 0
							nAt := cRight:IndexOf(',')
							IF nAt != - 1
								cCaption := cRight:Substring(0 , nAt)
								cProp := cRight:Substring(nAt + 1)
								DO CASE
								CASE cType:IndexOf("CODE:") == 0
									IF cType:Length > 5
										cProp := cType:Substring(5):Trim()
									END IF
									oProp := VODesignProperty{cCaption , cCaption , cProp , PropertyType.Callback}
									oControl:aProperties:Add(oProp)
								CASE cType == "STRING"
									oProp := VODesignProperty{cCaption , cCaption , cProp , PropertyType.Text}
									oProp:lMethod := TRUE
									oControl:aProperties:Add(oProp)
								CASE cCaption == "Group Value" .and. cProp == "Value" // DIRTY HACK
									oProp := VODesignProperty{cCaption , cCaption , cProp , PropertyType.Text}
									oProp:lMethod := TRUE
									oControl:aProperties:Add(oProp)
								CASE cType:IndexOf("CLASS:") == 0
									oProp := VODesignProperty{cCaption , cCaption , cProp , PropertyType.Type}
									oProp:lMethod := TRUE
									oControl:aProperties:Add(oProp)
//								CASE cType == "BOOL"
//								CASE cType == "LOGIC"
								OTHERWISE
									oProp := VODesignProperty{cCaption , cCaption , cProp , cType}
									oProp:lMethod := TRUE
									oControl:aProperties:Add(oProp)
								END CASE
							ENDIF

						END CASE
					ENDIF

				CASE eSection == CavoWedInfSection.AssignMap
					nAt := cLine:IndexOf('=')
					IF nAt != -1
						cLeft := cLine:Substring(0 , nAt):ToUpper()
						cRight := cLine:Substring(nAt + 1)
						IF !VOWindowEditorTemplate.aAssignMap:ContainsKey(cLeft)
							VOWindowEditorTemplate.aAssignMap:Add(cLeft , cRight)
						ENDIF
					ENDIF

				CASE eSection == CavoWedInfSection.Supplemental
					nAt := cLine:IndexOf('=')
					IF nAt != -1
						cLeft := cLine:Substring(0 , nAt):ToUpper()
						cRight := cLine:Substring(nAt + 1):Trim()
						IF cRight:Length != 0
							nAt := cRight:LastIndexOf("\")
							IF nAt != -1 .and. nAt < cRight:Length - 1
								cRight := cRight:Substring(nAt + 1):Trim()
							END IF
							cRight := Funcs.GetFileDir(cCavoWed) + "\" + cRight
							IF System.IO.File.Exists(cRight)
								VOWindowEditorTemplate.LoadInfFile(cRight)
							ELSE
								Funcs.ErrorBox("Could not find file " + cRight , Resources.EditorName)
							ENDIF
						ENDIF
					ENDIF

/*				CASE eSection == CavoWedInfSection.Options
					nAt := cLine:IndexOf('=')
					IF nAt != -1
						cLeft := cLine:Substring(0 , nAt):ToUpper()
						cRight := cLine:Substring(nAt + 1):Trim()
						TRY
							DO CASE
							CASE cLeft == "GRIDX"
								VOWindowEditorTemplate.GridX := Int32.Parse(cRight)
							CASE cLeft == "GRIDY"
								VOWindowEditorTemplate.GridY := Int32.Parse(cRight)
							END CASE
						CATCH
                            NOP
						END TRY
					ENDIF*/

				END CASE

			END DO

			lSuccess := TRUE

		CATCH

			Funcs.ErrorBox("Error loading file " + cCavoWed , Resources.EditorName)

		FINALLY

			IF oReader != NULL
				oReader:Close()
			END IF

		END TRY

	RETURN lSuccess

	STATIC ACCESS Loaded AS LOGIC
	RETURN VOWindowEditorTemplate.lLoaded
	STATIC ACCESS Count AS INT
	RETURN VOWindowEditorTemplate.aControls:Count
	STATIC METHOD Get(n AS INT) AS VOControlTemplate
	RETURN (VOControlTemplate)VOWindowEditorTemplate.aControls[n]
	STATIC METHOD Get(cControl AS STRING) AS VOControlTemplate
		cControl := cControl:ToUpper()
		FOREACH oTemplate AS VOControlTemplate IN VOWindowEditorTemplate.aControls
			IF oTemplate:cControl:ToUpper() == cControl
				RETURN oTemplate
			ENDIF
		NEXT
	RETURN NULL

	STATIC METHOD GetFromFullClass(cFullClass AS STRING) AS VOControlTemplate
		cFullClass := cFullClass:ToUpper()
		FOREACH oTemplate AS VOControlTemplate IN VOWindowEditorTemplate.aControls
			IF oTemplate:cFullClass:ToUpper() == cFullClass
				RETURN oTemplate
			ENDIF
		NEXT
	RETURN NULL

	STATIC METHOD GetParent(cParent AS STRING) AS VOControlTemplate
		cParent := cParent:ToUpper()
		FOREACH oTemplate AS VOControlTemplate IN VOWindowEditorTemplate.aControls
			IF oTemplate:cFullClass:ToUpper() == cParent
				RETURN oTemplate
			ENDIF
		NEXT
	RETURN NULL
	STATIC METHOD GetEnumerator(cKey AS STRING) AS STRING
		IF VOWindowEditorTemplate.aEnumTypes:ContainsKey(cKey)
			RETURN VOWindowEditorTemplate.aEnumTypes[cKey]
		ENDIF
		DO WHILE cKey:Length > 3 // TODO looks like the VO WED works this way, for all types
			cKey := cKey:Substring(0, cKey:Length - 1)
			IF VOWindowEditorTemplate.aEnumTypes:ContainsKey(cKey)
				RETURN VOWindowEditorTemplate.aEnumTypes[cKey]
			ENDIF
		END DO
	RETURN NULL
	STATIC METHOD GetAssignMap(cKey AS STRING) AS STRING
		cKey := cKey:ToUpper()
		IF VOWindowEditorTemplate.aAssignMap:ContainsKey(cKey)
			RETURN VOWindowEditorTemplate.aAssignMap[cKey]
		ENDIF
	RETURN NULL

END CLASS

CLASS VOControlTemplate
	EXPORT cControl AS STRING
	EXPORT cParent AS STRING
	EXPORT cName AS STRING
	EXPORT cFullClass AS STRING
	EXPORT cStartText AS STRING
	EXPORT oSize AS Size
	EXPORT lForm AS LOGIC
	EXPORT lUse AS LOGIC
	EXPORT cTitle AS STRING
	EXPORT cWinClass AS STRING
	EXPORT aPages AS List<STRING>
	EXPORT aProperties AS ArrayList
	EXPORT aStyles AS List<STRING>
	EXPORT aExStyles AS List<STRING>
	EXPORT cInitMethod AS STRING
	EXPORT lCreateResource AS LOGIC
	EXPORT lForceCommandControl AS LOGIC
	EXPORT cRawStyles AS STRING
	CONSTRUCTOR()
		SUPER()
		SELF:cStartText := ""
		SELF:cName := ""
		SELF:cParent := ""
		SELF:cFullClass := ""
		SELF:cControl := ""
		SELF:cTitle := ""
		SELF:cWinClass := ""
		SELF:cInitMethod := ""
		SELF:cRawStyles := ""
		SELF:aProperties := ArrayList{}
		SELF:aPages := List<STRING>{}
      SELF:aExStyles := List<STRING>{}
      SELF:aStyles := List<STRING>{}
	RETURN
	METHOD AddFrom(cControl AS STRING) AS VOID
		LOCAL oTemplate AS VOControlTemplate
		LOCAL n AS INT
//		oTemplate := VOWindowEditorTemplate.Get(cControl)
		oTemplate := VOWindowEditorTemplate.GetParent(cControl)
		IF oTemplate != NULL
			FOR n := 0 UPTO oTemplate:aProperties:Count - 1
				SELF:aProperties:Add(oTemplate:aProperties[n])
				SELF:lCreateResource := oTemplate:lCreateResource
			NEXT
			IF oTemplate:aExStyles != NULL
				SELF:aExStyles := List<STRING>{}
				FOR n := 0 UPTO oTemplate:aExStyles:Count - 1
					SELF:aExStyles:Add(oTemplate:aExStyles[n])
				NEXT
			END IF
		ENDIF
	RETURN

END CLASS


[DBFNTX] Goto should reset  the _TopStack to make sure that the first skip will relocate the current key

//
// Copyright (c) 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.RDD.Support
USING XSharp.RDD.NTX

BEGIN NAMESPACE XSharp.RDD
	/// <summary>DBFNTX RDD. For DBF/DBT/NTX.</summary>
	CLASS DBFNTX INHERIT DBFDBT

		INTERNAL _ntxList AS NtxOrderList

		CONSTRUCTOR()
			SUPER()
			SELF:_ntxList := NtxOrderList{SELF}
			SELF:_oIndex := NtxOrder{SELF}
			RETURN

		PROPERTY SysName AS STRING GET TYPEOF(DbfNtx):ToString()

		#REGION Order Support
		VIRTUAL METHOD OrderCreate(orderInfo AS DBORDERCREATEINFO ) AS LOGIC
			RETURN SELF:_ntxList:Create(orderInfo)

		VIRTUAL METHOD OrderDestroy(orderInfo AS DBORDERINFO ) AS LOGIC
			RETURN SUPER:OrderDestroy(orderInfo)

		VIRTUAL METHOD OrderListAdd( orderInfo AS DbOrderInfo) AS LOGIC
			//
			BEGIN LOCK SELF
				//
				SELF:GoCold()
				LOCAL fullPath AS STRING
				fullPath := orderInfo:BagName
				IF String.IsNullOrEmpty(System.IO.Path.GetDirectoryName(fullPath))
					fullPath := System.IO.Path.Combine(SYstem.IO.Path.GetDirectoryName(SELF:_FileName), fullPath)
				ENDIF
				RETURN SELF:_ntxList:Add(orderInfo, fullPath)
			END LOCK

		VIRTUAL METHOD OrderListDelete(orderInfo AS DbOrderInfo) AS LOGIC
			//
			BEGIN LOCK SELF
				//
				SELF:GoCold()
				RETURN SELF:_ntxList:CloseAll()
			END LOCK

		VIRTUAL METHOD OrderListFocus(orderInfo AS DbOrderInfo) AS LOGIC
			//
			BEGIN LOCK SELF
				//
				SELF:GoCold()
				RETURN SELF:_ntxList:SetFocus(orderInfo)
			END LOCK

		VIRTUAL METHOD OrderListRebuild() AS LOGIC
			//
			BEGIN LOCK SELF
				//
				SELF:GoCold()
				RETURN SELF:_ntxList:Rebuild()
			END LOCK

		OVERRIDE METHOD OrderInfo(nOrdinal AS DWORD , info AS DBORDERINFO ) AS OBJECT
			LOCAL isOk AS LOGIC
			LOCAL result AS DWORD
			LOCAL workOrder AS NtxOrder
			LOCAL orderPos AS LONG
			LOCAL oldvalue AS OBJECT
			//
			isOk := TRUE
			result := 0
			workOrder := NULL
			orderPos := SELF:_ntxList:FindOrder(info:Order)
			IF orderPos <= 0
				workOrder := SELF:_ntxList:CurrentOrder
			ELSE
				workOrder := SELF:_ntxList[orderPos - 1]
			ENDIF
			//
			BEGIN SWITCH nOrdinal
			CASE DBOI_CONDITION
				IF workOrder != NULL
					info:Result := workOrder:Condition
				ENDIF
			CASE DBOI_EXPRESSION
				IF workOrder != NULL
					info:Result := workOrder:Expression
				ENDIF
			CASE DBOI_ORDERCOUNT
				info:Result := SELF:_ntxList:Count
			CASE DBOI_POSITION
				IF (workOrder == NULL)
					info:Result := SELF:RecNo
				ELSE
					isOk := workOrder:_getRecPos( REF result)
					IF (isOk)
						info:Result := result
					ENDIF
				ENDIF
			CASE DBOI_KEYCOUNT
				result := 0
				IF workOrder != NULL
					info:Result := 0
					isOk := workOrder:_CountRecords(REF result)
				ENDIF
				IF (isOk)
					info:Result := result
				ENDIF
			CASE DBOI_NUMBER
				info:Result := orderPos
			CASE DBOI_BAGEXT
				info:Result := ".NTX"
			CASE DBOI_FULLPATH
				IF workOrder != NULL
					info:Result := workOrder:FileName
				ENDIF
			CASE DBOI_BAGNAME
				IF workOrder != NULL
					info:Result := System.IO.Path.GetFileNameWithoutExtension(workOrder:FileName)
				ENDIF
			CASE DBOI_NAME
				IF workOrder != NULL
					info:Result := workOrder:_orderName
				ENDIF
			CASE DBOI_FILEHANDLE
				IF workOrder != NULL
					info:Result := workOrder:_hFile
				ENDIF
			CASE DBOI_ISDESC
				IF workOrder != NULL
					info:Result := workOrder:_Descending
				ENDIF
			CASE DBOI_ISCOND
				IF workOrder != NULL
					info:Result := workOrder:_Conditional
				ENDIF
			CASE DBOI_KEYTYPE
				IF workOrder != NULL
					info:Result := workOrder:_KeyExprType
				ENDIF
			CASE DBOI_KEYSIZE
				IF workOrder != NULL
					info:Result := workOrder:_keySize
				ENDIF
			CASE DBOI_KEYDEC
				IF workOrder != NULL
					info:Result := workOrder:_keyDecimals
				ENDIF
			CASE DBOI_HPLOCKING
				IF workOrder != NULL
					info:Result := workOrder:_HPLocking
				ENDIF
			CASE DBOI_UNIQUE
				IF workOrder != NULL
					info:Result := workOrder:_Unique
				ENDIF
			CASE DBOI_LOCKOFFSET
				IF workOrder != NULL
					info:Result := workOrder:_lockScheme:Offset
				ENDIF
			CASE DBOI_SETCODEBLOCK
				IF workOrder != NULL
					oldvalue := workOrder:_KeyCodeBlock
					IF (info:Result != NULL)
						workOrder:_KeyCodeBlock := (ICodeblock)info:Result
					ENDIF
					info:Result := oldvalue
				ENDIF
			CASE DBOI_KEYVAL
				IF workOrder != NULL
					isOk := TRUE
					TRY
						info:Result := SELF:EvalBlock(workOrder:_KeyCodeBlock)
					CATCH
						isOk := FALSE
					END TRY
					IF (!isOk)
						info:Result := NULL
					ENDIF
				ENDIF
			CASE DBOI_SCOPETOP
				IF workOrder != NULL
					IF (info:Result != NULL)
						workOrder:SetOrderScope(info:Result, XSharp.RDD.Enums.DbOrder_Info.DBOI_SCOPETOP)
					ENDIF
					info:Result := workOrder:_topScope
				ENDIF
			CASE DBOI_SCOPEBOTTOM
				IF workOrder != NULL
					IF (info:Result != NULL)
						workOrder:SetOrderScope(info:Result, XSharp.RDD.Enums.DbOrder_Info.DBOI_SCOPEBOTTOM)
					ENDIF
					info:Result := workOrder:_bottomScope
				ENDIF
			CASE DBOI_SCOPETOPCLEAR
				IF workOrder != NULL
					workOrder:_hasTopScope := FALSE
					workOrder:_topScope := NULL
				ENDIF
			CASE DBOI_SCOPEBOTTOMCLEAR
				IF workOrder != NULL
					workOrder:_hasBottomScope := FALSE
					workOrder:_bottomScope := NULL
				ENDIF
            CASE DBOI_USER + 42
                // Dump Ntx to Txt file
				IF workOrder != NULL
					workOrder:_dump()
				ENDIF

			OTHERWISE
				isOk := (LOGIC)SUPER:OrderInfo(nOrdinal, info)
			END SWITCH
			RETURN isOk

		#endregion

		#region Pack, Zap
		METHOD Pack() AS LOGIC
			LOCAL isOk AS LOGIC
			//
			isOk := SUPER:Pack()
			IF (isOk)
				//
				isOk := SELF:OrderListRebuild()
			ENDIF
			RETURN isOk

		PUBLIC METHOD Zap() AS LOGIC
			LOCAL isOk AS LOGIC
			//
			isOk := SUPER:Zap()
			IF (isOk)
				isOk := SELF:OrderListRebuild()
			ENDIF
			RETURN isOk

			#endregion

		#REGION Open, Close, Create

		PUBLIC OVERRIDE METHOD Close() AS LOGIC
			LOCAL orderInfo AS DbOrderInfo
			//
			orderInfo := DbOrderInfo{}
			orderInfo:AllTags := TRUE
			SELF:OrderListDelete(orderInfo)
			RETURN SUPER:Close()

		PUBLIC OVERRIDE METHOD Create( openInfo AS DBOPENINFO ) AS LOGIC
			LOCAL isOk AS LOGIC
			//
			isOk := SUPER:Create(openInfo)
			IF ( XSharp.RuntimeState.Ansi .AND. isOk)
				VAR sig := SELF:_Header:Version
				// Set bit to Force Ansi Signature
				sig := sig |4
				// Should we also set the CodePage ??
				//SELF:_Header:DbfCodePage := DbfCodePage.CP_DBF_WIN_ANSI
				SELF:_Header:Version := sig
			ENDIF
			RETURN isOk

		#ENDREGION

		#REGION Move

        INTERNAL METHOD ReadRecord() AS LOGIC
            RETURN SELF:_ReadRecord()

		PUBLIC METHOD Seek(seekInfo AS DBSEEKINFO ) AS LOGIC
			LOCAL isOk AS LOGIC
			LOCAL ntxIndex AS NtxOrder
			//
			isOk := FALSE
			BEGIN LOCK SELF
				ntxIndex := SELF:_ntxList:CurrentOrder
				IF (ntxIndex != NULL)
					isOk := ntxIndex:Seek(seekInfo)
				ENDIF
				IF ( !isOk )
					SELF:_DbfError(SubCodes.ERDD_DATATYPE, GenCode.EG_NOORDER )
				ENDIF
			END LOCK
			RETURN isOk

		PUBLIC METHOD GoBottom() AS LOGIC
			BEGIN LOCK SELF
				IF SELF:_ntxList:CurrentOrder != NULL
					RETURN SELF:_ntxList:CurrentOrder:GoBottom()
				ELSE
					RETURN SUPER:GoBottom()
				ENDIF
			END LOCK

		PUBLIC METHOD GoTop() AS LOGIC
			BEGIN LOCK SELF
				IF SELF:_ntxList:CurrentOrder != NULL
					RETURN SELF:_ntxList:CurrentOrder:GoTop()
				ELSE
					RETURN SUPER:GoTop()
				ENDIF
			END LOCK

		METHOD GoTo(nRec AS LONG) AS LOGIC
			BEGIN LOCK SELF
                IF SELF:_ntxList:CurrentOrder != NULL
                    SELF:_ntxList:CurrentOrder:_TopStack := 0
				ENDIF
				RETURN SUPER:GoTo(nRec)
			END LOCK

		PUBLIC METHOD SkipRaw( move AS LONG ) AS LOGIC
			BEGIN LOCK SELF
				IF SELF:_ntxList:CurrentOrder != NULL
					RETURN SELF:_ntxList:CurrentOrder:SkipRaw(move)
				ELSE
					RETURN SUPER:SkipRaw(move)
				ENDIF
			END LOCK

			#ENDREGION

		#REGION GoCold, GoHot, Flush
		PUBLIC OVERRIDE METHOD GoCold() AS LOGIC
			LOCAL isOk AS LOGIC
			//
			isOk := TRUE
			BEGIN LOCK SELF
				IF ( !SELF:IsHot )
					RETURN isOk
				ENDIF
				isOk := SELF:_ntxList:GoCold()
				IF (!isOk)
					RETURN isOk
				ENDIF
				RETURN SUPER:GoCold()
			END LOCK

		PUBLIC OVERRIDE METHOD GoHot() AS LOGIC
			LOCAL isOk AS LOGIC
			//
			isOk := TRUE
			BEGIN LOCK SELF
				isOk := SUPER:GoHot()
				IF (!isOk)
					RETURN isOk
				ENDIF
				RETURN SELF:_ntxList:GoHot()
			END LOCK

		PUBLIC OVERRIDE METHOD Flush() AS LOGIC
			LOCAL isOk AS LOGIC
			//
			isOk := TRUE
			BEGIN LOCK SELF
				isOk := SUPER:Flush()
				RETURN SELF:_ntxList:Flush()
			END LOCK

		#ENDREGION

	END CLASS

END NAMESPACE

//
// Copyright (c) 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.RDD.Support
USING XSharp.RDD.NTX

BEGIN NAMESPACE XSharp.RDD
	/// <summary>DBFNTX RDD. For DBF/DBT/NTX.</summary>
	CLASS DBFNTX INHERIT DBFDBT

		INTERNAL _ntxList AS NtxOrderList

		CONSTRUCTOR()
			SUPER()
			SELF:_ntxList := NtxOrderList{SELF}
			SELF:_oIndex := NtxOrder{SELF}
			RETURN

		PROPERTY SysName AS STRING GET TYPEOF(DbfNtx):ToString()

		#REGION Order Support
		VIRTUAL METHOD OrderCreate(orderInfo AS DBORDERCREATEINFO ) AS LOGIC
			RETURN SELF:_ntxList:Create(orderInfo)

		VIRTUAL METHOD OrderDestroy(orderInfo AS DBORDERINFO ) AS LOGIC
			RETURN SUPER:OrderDestroy(orderInfo)

		VIRTUAL METHOD OrderListAdd( orderInfo AS DbOrderInfo) AS LOGIC
			//
			BEGIN LOCK SELF
				//
				SELF:GoCold()
				LOCAL fullPath AS STRING
				fullPath := orderInfo:BagName
				IF String.IsNullOrEmpty(System.IO.Path.GetDirectoryName(fullPath))
					fullPath := System.IO.Path.Combine(SYstem.IO.Path.GetDirectoryName(SELF:_FileName), fullPath)
				ENDIF
				RETURN SELF:_ntxList:Add(orderInfo, fullPath)
			END LOCK

		VIRTUAL METHOD OrderListDelete(orderInfo AS DbOrderInfo) AS LOGIC
			//
			BEGIN LOCK SELF
				//
				SELF:GoCold()
				RETURN SELF:_ntxList:CloseAll()
			END LOCK

		VIRTUAL METHOD OrderListFocus(orderInfo AS DbOrderInfo) AS LOGIC
			//
			BEGIN LOCK SELF
				//
				SELF:GoCold()
				RETURN SELF:_ntxList:SetFocus(orderInfo)
			END LOCK

		VIRTUAL METHOD OrderListRebuild() AS LOGIC
			//
			BEGIN LOCK SELF
				//
				SELF:GoCold()
				RETURN SELF:_ntxList:Rebuild()
			END LOCK

		OVERRIDE METHOD OrderInfo(nOrdinal AS DWORD , info AS DBORDERINFO ) AS OBJECT
			LOCAL isOk AS LOGIC
			LOCAL result AS DWORD
			LOCAL workOrder AS NtxOrder
			LOCAL orderPos AS LONG
			LOCAL oldvalue AS OBJECT
			//
			isOk := TRUE
			result := 0
			workOrder := NULL
			orderPos := SELF:_ntxList:FindOrder(info:Order)
			IF orderPos <= 0
				workOrder := SELF:_ntxList:CurrentOrder
			ELSE
				workOrder := SELF:_ntxList[orderPos - 1]
			ENDIF
			//
			BEGIN SWITCH nOrdinal
			CASE DBOI_CONDITION
				IF workOrder != NULL
					info:Result := workOrder:Condition
				ENDIF
			CASE DBOI_EXPRESSION
				IF workOrder != NULL
					info:Result := workOrder:Expression
				ENDIF
			CASE DBOI_ORDERCOUNT
				info:Result := SELF:_ntxList:Count
			CASE DBOI_POSITION
				IF (workOrder == NULL)
					info:Result := SELF:RecNo
				ELSE
					isOk := workOrder:_getRecPos( REF result)
					IF (isOk)
						info:Result := result
					ENDIF
				ENDIF
			CASE DBOI_KEYCOUNT
				result := 0
				IF workOrder != NULL
					info:Result := 0
					isOk := workOrder:_CountRecords(REF result)
				ENDIF
				IF (isOk)
					info:Result := result
				ENDIF
			CASE DBOI_NUMBER
				info:Result := orderPos
			CASE DBOI_BAGEXT
				info:Result := ".NTX"
			CASE DBOI_FULLPATH
				IF workOrder != NULL
					info:Result := workOrder:FileName
				ENDIF
			CASE DBOI_BAGNAME
				IF workOrder != NULL
					info:Result := System.IO.Path.GetFileNameWithoutExtension(workOrder:FileName)
				ENDIF
			CASE DBOI_NAME
				IF workOrder != NULL
					info:Result := workOrder:_orderName
				ENDIF
			CASE DBOI_FILEHANDLE
				IF workOrder != NULL
					info:Result := workOrder:_hFile
				ENDIF
			CASE DBOI_ISDESC
				IF workOrder != NULL
					info:Result := workOrder:_Descending
				ENDIF
			CASE DBOI_ISCOND
				IF workOrder != NULL
					info:Result := workOrder:_Conditional
				ENDIF
			CASE DBOI_KEYTYPE
				IF workOrder != NULL
					info:Result := workOrder:_KeyExprType
				ENDIF
			CASE DBOI_KEYSIZE
				IF workOrder != NULL
					info:Result := workOrder:_keySize
				ENDIF
			CASE DBOI_KEYDEC
				IF workOrder != NULL
					info:Result := workOrder:_keyDecimals
				ENDIF
			CASE DBOI_HPLOCKING
				IF workOrder != NULL
					info:Result := workOrder:_HPLocking
				ENDIF
			CASE DBOI_UNIQUE
				IF workOrder != NULL
					info:Result := workOrder:_Unique
				ENDIF
			CASE DBOI_LOCKOFFSET
				IF workOrder != NULL
					info:Result := workOrder:_lockScheme:Offset
				ENDIF
			CASE DBOI_SETCODEBLOCK
				IF workOrder != NULL
					oldvalue := workOrder:_KeyCodeBlock
					IF (info:Result != NULL)
						workOrder:_KeyCodeBlock := (ICodeblock)info:Result
					ENDIF
					info:Result := oldvalue
				ENDIF
			CASE DBOI_KEYVAL
				IF workOrder != NULL
					isOk := TRUE
					TRY
						info:Result := SELF:EvalBlock(workOrder:_KeyCodeBlock)
					CATCH
						isOk := FALSE
					END TRY
					IF (!isOk)
						info:Result := NULL
					ENDIF
				ENDIF
			CASE DBOI_SCOPETOP
				IF workOrder != NULL
					IF (info:Result != NULL)
						workOrder:SetOrderScope(info:Result, XSharp.RDD.Enums.DbOrder_Info.DBOI_SCOPETOP)
					ENDIF
					info:Result := workOrder:_topScope
				ENDIF
			CASE DBOI_SCOPEBOTTOM
				IF workOrder != NULL
					IF (info:Result != NULL)
						workOrder:SetOrderScope(info:Result, XSharp.RDD.Enums.DbOrder_Info.DBOI_SCOPEBOTTOM)
					ENDIF
					info:Result := workOrder:_bottomScope
				ENDIF
			CASE DBOI_SCOPETOPCLEAR
				IF workOrder != NULL
					workOrder:_hasTopScope := FALSE
					workOrder:_topScope := NULL
				ENDIF
			CASE DBOI_SCOPEBOTTOMCLEAR
				IF workOrder != NULL
					workOrder:_hasBottomScope := FALSE
					workOrder:_bottomScope := NULL
				ENDIF
            CASE DBOI_USER + 42
                // Dump Ntx to Txt file
				IF workOrder != NULL
					workOrder:_dump()
				ENDIF

			OTHERWISE
				isOk := (LOGIC)SUPER:OrderInfo(nOrdinal, info)
			END SWITCH
			RETURN isOk

		#endregion

		#region Pack, Zap
		METHOD Pack() AS LOGIC
			LOCAL isOk AS LOGIC
			//
			isOk := SUPER:Pack()
			IF (isOk)
				//
				isOk := SELF:OrderListRebuild()
			ENDIF
			RETURN isOk

		PUBLIC METHOD Zap() AS LOGIC
			LOCAL isOk AS LOGIC
			//
			isOk := SUPER:Zap()
			IF (isOk)
				isOk := SELF:OrderListRebuild()
			ENDIF
			RETURN isOk

			#endregion

		#REGION Open, Close, Create

		PUBLIC OVERRIDE METHOD Close() AS LOGIC
			LOCAL orderInfo AS DbOrderInfo
			//
			orderInfo := DbOrderInfo{}
			orderInfo:AllTags := TRUE
			SELF:OrderListDelete(orderInfo)
			RETURN SUPER:Close()

		PUBLIC OVERRIDE METHOD Create( openInfo AS DBOPENINFO ) AS LOGIC
			LOCAL isOk AS LOGIC
			//
			isOk := SUPER:Create(openInfo)
			IF ( XSharp.RuntimeState.Ansi .AND. isOk)
				VAR sig := SELF:_Header:Version
				// Set bit to Force Ansi Signature
				sig := sig |4
				// Should we also set the CodePage ??
				//SELF:_Header:DbfCodePage := DbfCodePage.CP_DBF_WIN_ANSI
				SELF:_Header:Version := sig
			ENDIF
			RETURN isOk

		#ENDREGION

		#REGION Move

        INTERNAL METHOD ReadRecord() AS LOGIC
            RETURN SELF:_ReadRecord()

		PUBLIC METHOD Seek(seekInfo AS DBSEEKINFO ) AS LOGIC
			LOCAL isOk AS LOGIC
			LOCAL ntxIndex AS NtxOrder
			//
			isOk := FALSE
			BEGIN LOCK SELF
				ntxIndex := SELF:_ntxList:CurrentOrder
				IF (ntxIndex != NULL)
					isOk := ntxIndex:Seek(seekInfo)
				ENDIF
				IF ( !isOk )
					SELF:_DbfError(SubCodes.ERDD_DATATYPE, GenCode.EG_NOORDER )
				ENDIF
			END LOCK
			RETURN isOk

		PUBLIC METHOD GoBottom() AS LOGIC
			BEGIN LOCK SELF
				IF SELF:_ntxList:CurrentOrder != NULL
					RETURN SELF:_ntxList:CurrentOrder:GoBottom()
				ELSE
					RETURN SUPER:GoBottom()
				ENDIF
			END LOCK

		PUBLIC METHOD GoTop() AS LOGIC
			BEGIN LOCK SELF
				IF SELF:_ntxList:CurrentOrder != NULL
					RETURN SELF:_ntxList:CurrentOrder:GoTop()
				ELSE
					RETURN SUPER:GoTop()
				ENDIF
			END LOCK

		PUBLIC METHOD SkipRaw( move AS LONG ) AS LOGIC
			BEGIN LOCK SELF
				IF SELF:_ntxList:CurrentOrder != NULL
					RETURN SELF:_ntxList:CurrentOrder:SkipRaw(move)
				ELSE
					RETURN SUPER:SkipRaw(move)
				ENDIF
			END LOCK

			#ENDREGION

		#REGION GoCold, GoHot, Flush
		PUBLIC OVERRIDE METHOD GoCold() AS LOGIC
			LOCAL isOk AS LOGIC
			//
			isOk := TRUE
			BEGIN LOCK SELF
				IF ( !SELF:IsHot )
					RETURN isOk
				ENDIF
				isOk := SELF:_ntxList:GoCold()
				IF (!isOk)
					RETURN isOk
				ENDIF
				RETURN SUPER:GoCold()
			END LOCK

		PUBLIC OVERRIDE METHOD GoHot() AS LOGIC
			LOCAL isOk AS LOGIC
			//
			isOk := TRUE
			BEGIN LOCK SELF
				isOk := SUPER:GoHot()
				IF (!isOk)
					RETURN isOk
				ENDIF
				RETURN SELF:_ntxList:GoHot()
			END LOCK

		PUBLIC OVERRIDE METHOD Flush() AS LOGIC
			LOCAL isOk AS LOGIC
			//
			isOk := TRUE
			BEGIN LOCK SELF
				isOk := SUPER:Flush()
				RETURN SELF:_ntxList:Flush()
			END LOCK

		#ENDREGION

	END CLASS

END NAMESPACE


Fixed problem with case differences for customattributes at the assembly level

//
// Copyright (c) XSharp B.V.  All Rights Reserved.
// Licensed under the Apache License, Version 2.0.
// See License.txt in the project root for license information.
//
USING System.Collections.Generic
USING System.Collections.Immutable
USING System
USING System.IO
USING System.Diagnostics
USING System.Reflection
BEGIN NAMESPACE XSharpModel
	[DebuggerDisplay("{DisplayName,nq}")];
CLASS AssemblyInfo
		// Fields
		PRIVATE _aExtensions AS List<MethodInfo>
		PRIVATE _assembly AS Assembly
		PRIVATE _aTypes AS IDictionary<STRING, System.Type>
		PRIVATE _fullName AS STRING
		PRIVATE _globalClassName AS STRING
		PRIVATE _HasExtensions AS LOGIC
		PRIVATE _implicitNamespaces AS ImmutableList<STRING>
		PRIVATE _LoadedTypes AS LOGIC
		PRIVATE _Modified AS System.DateTime
		PRIVATE _nameSpaces AS System.Collections.Hashtable
		PRIVATE _nameSpaceTexts AS ImmutableList<STRING>
		PRIVATE _projects AS ImmutableList<XProject>
		PRIVATE _reference AS VSLangProj.Reference
		PRIVATE _zeroNamespace AS AssemblyInfo.NameSpaceContainer

		PRIVATE STATIC WorkFolder AS STRING
		PRIVATE STATIC FailedAssemblies AS Dictionary<STRING, INT>

		PUBLIC STATIC PROPERTY DisableAssemblyReferences AS LOGIC AUTO
		PUBLIC STATIC PROPERTY DisableForeignProjectReferences AS LOGIC AUTO
		PUBLIC STATIC PROPERTY DisableXSharpProjectReferences AS LOGIC AUTO
		STATIC CONSTRUCTOR
			// Clear temp files from previous run
			FailedAssemblies := Dictionary<STRING, INT>{StringComparer.OrdinalIgnoreCase}
			VAR cFolder := Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData)
			cFolder := System.IO.Path.Combine(cFolder, "XSharp\Temp")
			IF ! System.IO.Directory.Exists(cFolder)
				System.IO.Directory.CreateDirectory(cFolder)
			ENDIF
			WorkFolder := cFolder
			LOCAL oDir := DirectoryInfo{cFolder} AS DirectoryInfo
			FOREACH VAR oFile IN oDir:GetFiles()
				TRY
					File.SetAttributes(oFile:FullName, FileAttributes.Normal  )
					File.Delete(oFile:FullName)
				CATCH e AS Exception
					XSolution.WriteException(e)
				END TRY
			NEXT
			DisableXSharpProjectReferences := FALSE
			DisableAssemblyReferences := FALSE
			DisableForeignProjectReferences := FALSE
			RETURN

			// Methods
		CONSTRUCTOR()
			SUPER()
			SELF:_fullName := ""
			SELF:_assembly := NULL
			SELF:_projects := ImmutableList<XProject>.Empty
			SELF:_clearInfo()

		CONSTRUCTOR(reference AS VSLangProj.Reference)
			SELF()
			SELF:_reference := reference

		CONSTRUCTOR(_cFileName AS STRING, _dModified AS System.DateTime)
			SELF()
			SELF:FileName := _cFileName
			SELF:Modified := _dModified
			SELF:UpdateAssembly()

		PRIVATE METHOD _clearInfo() AS VOID
			//
			SELF:_aTypes := Dictionary<STRING, System.Type>{System.StringComparer.OrdinalIgnoreCase}
			SELF:_aExtensions := List<MethodInfo>{}
			SELF:_nameSpaces := System.Collections.Hashtable{System.StringComparer.OrdinalIgnoreCase}
			SELF:_nameSpaceTexts := ImmutableList<STRING>.Empty
			SELF:_implicitNamespaces := ImmutableList<STRING>.Empty
			SELF:_zeroNamespace := AssemblyInfo.NameSpaceContainer{"_"}
			SELF:_LoadedTypes := FALSE
			SELF:_HasExtensions := FALSE

		METHOD AddProject(project AS XProject) AS VOID
			IF ! SELF:_projects:Contains(project)
				SELF:_projects := SELF:_projects:Add(project)
			ENDIF

		PRIVATE METHOD CurrentDomain_AssemblyResolve(sender AS OBJECT, args AS System.ResolveEventArgs) AS Assembly
			VAR folders := List<STRING>{}	// list of folders that we have tried
			VAR folderPath := System.IO.Path.GetDirectoryName(SELF:FileName)
			VAR name := AssemblyName{args:Name}:Name + ".dll"
			VAR assemblyPath := System.IO.Path.Combine(folderPath, name)
			WriteOutputMessage("--> CurrentDomain_AssemblyResolve : "+assemblyPath)
			IF System.IO.File.Exists(assemblyPath)
				VAR assembly := AssemblyInfo.LoadAssemblyFromFile(assemblyPath)
				IF assembly != NULL
					RETURN assembly
				ENDIF
			ENDIF
			folders:Add(folderPath)
			FOREACH path AS STRING IN SystemTypeController.AssemblyFileNames
				folderPath := System.IO.Path.GetDirectoryName(path)
				IF ! folders:Contains(folderPath)
					assemblyPath := System.IO.Path.Combine(folderPath, name)
					IF System.IO.File.Exists(assemblyPath)
						VAR asm := AssemblyInfo.LoadAssemblyFromFile(assemblyPath)
						IF asm != NULL
							RETURN asm
						ENDIF
					ENDIF
					folders:Add(folderPath)
				ENDIF
			NEXT
			WriteOutputMessage("<-- CurrentDomain_AssemblyResolve : "+assemblyPath)
			RETURN NULL

		METHOD GetType(name AS STRING) AS System.Type
			IF SELF:IsModifiedOnDisk
				SELF:LoadAssembly()
			ENDIF
			IF SELF:_assembly != NULL .AND. SELF:_aTypes:Count == 0
				SELF:UpdateAssembly()
			ENDIF
			IF SELF:_aTypes:ContainsKey(name)
				RETURN SELF:Types:Item[name]
			ENDIF
			RETURN NULL

		PRIVATE METHOD GetTypeTypesFromType(oType AS System.Type) AS AssemblyInfo.TypeTypes
			IF oType:IsValueType
				RETURN AssemblyInfo.TypeTypes.Structure
			ENDIF
			IF oType:IsInterface
				RETURN AssemblyInfo.TypeTypes.Interface
			ENDIF
			IF TYPEOF(System.Delegate):IsAssignableFrom(oType)
				RETURN AssemblyInfo.TypeTypes.Delegate
			ENDIF
			RETURN AssemblyInfo.TypeTypes.Class

		PRIVATE STATIC METHOD HasExtensionAttribute(memberInfo AS MemberInfo) AS LOGIC
			TRY
				VAR customAttributes := memberInfo:GetCustomAttributes(FALSE)
				FOREACH VAR custattr IN customAttributes
					IF custattr:ToString() == "System.Runtime.CompilerServices.ExtensionAttribute"
						RETURN TRUE
					ENDIF
				NEXT
			CATCH
				// Failed to retrieve custom attributes
			END TRY
			RETURN FALSE

		INTERNAL METHOD LoadAssembly()  AS VOID
			WriteOutputMessage("--> LoadAssembly : "+SELF:FileName)
			IF String.IsNullOrEmpty(SELF:FileName) .AND. SELF:_reference != NULL
				SELF:FileName := SELF:_reference:Path
			ENDIF
			IF SELF:Exists
				SELF:_assembly := AssemblyInfo.LoadAssemblyFromFile(SELF:FileName)
				IF SELF:_assembly != NULL
					SELF:_fullName  := SELF:_assembly:FullName
					SELF:Modified   := SELF:LastWriteTime
				ENDIF
				SELF:_clearInfo()
			ENDIF
			WriteOutputMessage("<-- LoadAssembly : "+SELF:FileName)

		STATIC METHOD FindPdbName(fileName AS STRING) AS STRING
			WriteOutputMessage("--> FindPDBName : "+fileName)
			TRY
				IF System.IO.File.Exists(fileName)
					VAR pdb			:= System.IO.Path.ChangeExtension(fileName,".pdb")
					IF System.IO.File.Exists(pdb)
						RETURN pdb
					ENDIF
					// ok when it is not in the DLL folder but referenced in the DLL then extract this information
					VAR text        := System.IO.File.ReadAllText(fileName)
					VAR pdbIndex	:= text:IndexOf(".pdb", StringComparison.InvariantCultureIgnoreCase)
					IF pdbIndex > 0
						VAR lastTerminatorIndex := text:Substring(0, pdbIndex).LastIndexOf('\0')
						RETURN text:Substring(lastTerminatorIndex + 1, pdbIndex - lastTerminatorIndex + 3)
					ENDIF
				ENDIF
			FINALLY
				WriteOutputMessage("<-- FindPDBName : "+fileName)
			END TRY
			RETURN string.Empty

		STATIC METHOD LoadAssemblyFromFile(fileName AS STRING) AS Assembly
			LOCAL result  AS Assembly
			LOCAL iAttempts AS INT
			TRY
				WriteOutputMessage("--> LoadAssemblyFromFile : "+fileName)
				IF FailedAssemblies:ContainsKey(fileName)
					iAttempts := FailedAssemblies[fileName]
					IF iAttempts > 3
						WriteOutputMessage("    ... Cannot load assembly, giving up after a few retries:")
						WriteOutputMessage("    ...."  +fileName+" "+ iAttempts:ToString())
						RETURN NULL
					ENDIF
				ENDIF
				IF System.IO.File.Exists(fileName)
					LOCAL cPDB		AS STRING
					LOCAL cPdbCopy	AS STRING
					LOCAL PdbRenamed   := FALSE AS LOGIC
					TRY
						VAR temp		:=  System.IO.Path.Combine(WorkFolder, System.IO.Path.GetFileName(fileName))
						System.IO.File.Copy(fileName, temp,TRUE)
						VAR rawAssembly := System.IO.File.ReadAllBytes(temp)
						cPdb		:= FindPdbName(temp)
						System.IO.File.Delete(temp)
						cPdbCopy := System.IO.Path.ChangeExtension(temp, ".p$$")
						IF !String.IsNullOrEmpty(cPdb) .AND. System.IO.File.Exists(cPdb)
							PdbRenamed := TRUE
							IF System.IO.File.Exists(cPdbCopy)
								System.IO.File.Delete(cPdbCopy)
							ENDIF
							System.IO.File.Move(cPdb, cPdbCopy)
						ENDIF
						result := Assembly.Load(rawAssembly)
						IF FailedAssemblies:ContainsKey(fileName)
							FailedAssemblies:Remove(fileName)
						ENDIF
						RETURN result
					CATCH exception AS System.IO.FileLoadException
						// files with unmanaged code produce the exception:
						// Attempt to load an unverifiable executable with fixups (IAT with more than 2 sections or a TLS section.) (Exception from HRESULT: 0x80131019)
						XSolution.WriteException(exception)
					CATCH exception AS System.Exception
						// Other exception
						XSolution.WriteException(exception)
					FINALLY
						IF PdbRenamed .AND. System.IO.File.Exists(cPdbCopy)
							System.IO.File.Move(cPdbCopy, cPdb)
						ENDIF
					END TRY
					TRY
						// Generate a random name and copy the DLL to prevent locking the original file
						VAR temp := filename
						DO WHILE File.Exists(temp)
							temp := System.IO.Path.Combine(WorkFolder, System.IO.Path.GetRandomFileName())
						ENDDO
						// Need to rename the PDB again. Was undone in the Finally above
						IF PdbRenamed .AND. System.IO.File.Exists(cPdb)
							System.IO.File.Move(cPdb, cPdbCopy)
						ENDIF
						System.IO.File.Copy(filename, temp,TRUE)
						File.SetAttributes(temp, FileAttributes.Normal | FileAttributes.Temporary )
						result := Assembly.LoadFrom(temp)
						IF FailedAssemblies:ContainsKey(fileName)
							FailedAssemblies:Remove(fileName)
						ENDIF
						RETURN result
					CATCH e AS Exception
						XSolution.WriteException(e)
					FINALLY
						IF PdbRenamed .AND. System.IO.File.Exists(cPdbCopy)
							TRY
								System.IO.File.Move(cPdbCopy, cPdb)
							CATCH e AS Exception
								XSolution.WriteException(e)
							END TRY
						ENDIF
					END TRY
					IF FailedAssemblies:ContainsKey(fileName)
						FailedAssemblies[fileName] := FailedAssemblies[fileName] +1
					ELSE
						FailedAssemblies:Add(fileName, 1)
					ENDIF
				ELSE
					// FileName does not exist. No need to change the failedAssemblies list
				ENDIF
			FINALLY
				WriteOutputMessage("<-- LoadAssemblyFromFile : "+fileName)
			END TRY
			RETURN NULL

		METHOD RemoveProject(project AS XProject) AS VOID
			//
			IF SELF:_projects:Contains(project)
				SELF:_projects := SELF:_projects:Remove(project)
			ENDIF

		METHOD Refresh() AS VOID
            IF SELF:Exists
			    VAR currentDT := SELF:LastWriteTime
			    IF currentDT != SELF:Modified
				    WriteOutputMessage("AssemblyInfo.Refresh() Assembly was changed: "+SELF:FileName )
				    SELF:UpdateAssembly()
			    ENDIF
            ENDIF

        PRIVATE METHOD GetPropertySafe(type as System.Type, obj as Object, PropName as STRING) as STRING
            FOREACH var prop in type:GetProperties()
                if String.Compare(prop:Name, PropName, StringComparison.OrdinalIgnoreCase) == 0
                    return prop:GetValue(obj, NULL):ToString()
                endif
            NEXT
            return ""

		INTERNAL METHOD UpdateAssembly() AS VOID
			LOCAL aTypes AS Dictionary<STRING, System.Type>
			LOCAL nspace AS STRING
			LOCAL fullName AS STRING
			LOCAL simpleName AS STRING
			LOCAL message AS STRING
			LOCAL types := NULL AS System.Type[]
			LOCAL index AS LONG
            IF ! SELF:Exists
				WriteOutputMessage("****** AssemblyInfo.UpdateAssembly: assembly "+SELF:FileName +" does not exist")
                RETURN
            ENDIF
			TRY
				WriteOutputMessage("-->AssemblyInfo.UpdateAssembly load types from assembly "+SELF:FileName )
				aTypes		 := Dictionary<STRING, System.Type>{System.StringComparer.OrdinalIgnoreCase}
				_aExtensions := List<MethodInfo>{}
				fullName := ""
				simpleName := ""
				SELF:_nameSpaces:Clear()
				_nameSpaceTexts := ImmutableList<STRING>.Empty
				SELF:_zeroNamespace:Clear()
				SELF:_globalClassName := ""
				SELF:_HasExtensions := FALSE
				SELF:LoadAssembly()
				VAR currentDomain := System.AppDomain.CurrentDomain
				currentDomain:AssemblyResolve += System.ResolveEventHandler{ SELF, @CurrentDomain_AssemblyResolve() }
				TRY
					IF SELF:_assembly != NULL
						VAR customAttributes := SELF:_assembly:GetCustomAttributes(FALSE)
						LOCAL found := 0 AS INT
						FOREACH VAR custattr IN customAttributes
							//
							LOCAL type := custattr:GetType() as System.Type
							SWITCH custattr:ToString():ToLower()
								CASE "vulcan.internal.vulcanclasslibraryattribute"
                                CASE "xsharp.internal.classlibraryattribute"
									SELF:_globalClassName := SELF:GetPropertySafe(type, custAttr, "globalclassname")
									WriteOutputMessage("   ...Globals Classname found: "+_globalClassName )
									VAR defaultNs := SELF:GetPropertySafe(type, custAttr, "defaultnamespace")
									IF ! String.IsNullOrEmpty(defaultNs)
										SELF:_implicitNamespaces := SELF:_implicitNamespaces:Add(defaultNs)
										WriteOutputMessage("   ...implicit Namespace found: "+defaultNs)
									ENDIF
									found += 1
								CASE "system.runtime.compilerservices.extensionattribute"
									SELF:_HasExtensions  := TRUE
									found += 2
								CASE "vulcan.vulcanimplicitnamespaceattribute"
								CASE "xsharp.implicitnamespaceattribute"
									VAR ns := SELF:GetPropertySafe(type, custAttr, "namespace")
									IF ! String.IsNullOrEmpty(ns)
										SELF:_implicitNamespaces := SELF:_implicitNamespaces:Add(ns)
										WriteOutputMessage("   ...implicit Namespace found: "+ns)
									ENDIF
									found += 4
							END SWITCH
							// All attributes found then get out of here
							IF found == 7
								EXIT
							ENDIF
						NEXT

					ENDIF
				CATCH e AS Exception
					XSolution.WriteException(e)
				END TRY
				TRY
					IF SELF:_assembly != NULL
						types := SELF:_assembly:GetTypes()
					ENDIF
				CATCH exception AS ReflectionTypeLoadException
					//
					WriteOutputMessage("Cannot load types from "+ SELF:_assembly:GetName():Name)
					XSolution.WriteException(exception)
					message := NULL
					FOREACH VAR exception2 IN exception:LoaderExceptions
						IF exception2:Message != message
							XSolution.WriteException(exception2)
							message := exception2:Message
						ENDIF
					NEXT
					SELF:_assembly := NULL
				CATCH ex AS System.Exception
					XSolution.WriteException(ex)
				END TRY
				currentDomain:AssemblyResolve -= System.ResolveEventHandler{ SELF, @CurrentDomain_AssemblyResolve() }

				IF types != NULL .AND. types:Length != 0 .AND. (aTypes:Count == 0  .OR. ! _LoadedTypes)
					TRY
						FOREACH VAR type IN types
							fullName := type:FullName
							IF ! fullName:StartsWith("$") .AND. ! fullName:StartsWith("<")
								IF SELF:_HasExtensions .AND. AssemblyInfo.HasExtensionAttribute(type)
									VAR methods := type:GetMethods(BindingFlags.Public | BindingFlags.Static)
									FOREACH info AS MethodInfo IN methods
										IF AssemblyInfo.HasExtensionAttribute(info)
											_aExtensions:Add(info)
										ENDIF
									NEXT
								ENDIF
								//
								// Nested Type ?
								IF fullName:Contains("+")
									fullName := fullName:Replace('+', '.')
								ENDIF
								// Generic ?
								IF fullName:Contains("`")
									fullName := fullName:Substring(0, fullName:IndexOf("`") + 2)
								ENDIF
								// Add to the FullyQualified name
								IF ! aTypes:ContainsKey(fullName)
									aTypes:Add(fullName, type)
								ENDIF
								// Now, with Standard name
								simpleName := type:Name:Replace('+', '.')
								// Not Empty namespace, not a generic, not nested, not starting with underscore
								IF String.IsNullOrEmpty(type:Namespace) .AND. simpleName:IndexOf('`') == -1 .AND. ;
										simpleName:IndexOf('<') == -1 .AND. ! simpleName:StartsWith("_")
									SELF:_zeroNamespace:AddType(simpleName, SELF:GetTypeTypesFromType(type))
								ENDIF
								// Public Type, not Nested and no Underscore
								IF type:IsPublic .AND. simpleName.IndexOf('+') == -1  .AND. simpleName.IndexOf('_') == -1
									// Get the Namespace
									nspace := type:Namespace
									// and the normal name
									simpleName := type:Name
									simpleName := simpleName:Replace('+', '.')
									// Generic ?
									index := simpleName:IndexOf('`')
									IF index != -1
										simpleName := simpleName:Substring(0, index)
									ENDIF
									IF nspace?:Length > 0
										LOCAL container AS AssemblyInfo.NameSpaceContainer
										IF ! SELF:_nameSpaces:ContainsKey(nspace)
											container := AssemblyInfo.NameSpaceContainer{nspace}
											container:AddType(simpleName, SELF:GetTypeTypesFromType(type))
											SELF:_nameSpaces:Add(nspace, container)
											SELF:_nameSpaceTexts := SELF:_nameSpaceTexts:Add(nspace)
										ELSE
											container := (AssemblyInfo.NameSpaceContainer)SELF:_nameSpaces[nspace]
											container:AddType(simpleName, SELF:GetTypeTypesFromType(type))
										ENDIF
										DO WHILE nspace:Contains(".")
											nspace := nspace:Substring(0, nspace:LastIndexOf('.'))
											IF ! SELF:_nameSpaceTexts:Contains(nspace)
												SELF:_nameSpaceTexts := SELF:_nameSpaceTexts:Add(nspace)
											ENDIF
										ENDDO
									ENDIF
								ENDIF
							ENDIF
						NEXT
						SELF:_LoadedTypes := TRUE
						SELF:_aTypes := aTypes.ToImmutableDictionary(System.StringComparer.OrdinalIgnoreCase)
						SELF:_assembly := NULL
					CATCH e AS System.Exception
						XSolution.WriteException(e)
						SELF:_clearInfo()
					END TRY
				ENDIF
			FINALLY
				WriteOutputMessage("<-- AssemblyInfo.UpdateAssembly load types from assembly "+SELF:FileName )
			END TRY
			// Properties
		PROPERTY DisplayName AS STRING
			GET
				IF String.IsNullOrEmpty(SELF:fileName)
					RETURN "(Empty)"
				ENDIF
				RETURN System.IO.Path.GetFileName(SELF:fileName)
			END GET
		END PROPERTY

        INTERNAL STATIC METHOD _SafeExists(cFileName as STRING) AS LOGIC
            local lExists := FALSE as LOGIC
            TRY
                IF !String.IsNullOrEmpty(cFileName)
                    IF System.IO.File.Exists(cFileName)
                        lExists := TRUE
                    ENDIF
                ENDIF
            CATCH
                lExists := FALSE
            END TRY
            RETURN lExists

        PROPERTY Exists             AS LOGIC GET _SafeExists(SELF:FileName)
		PROPERTY FileName           AS STRING AUTO
		PROPERTY FullName           AS STRING GET SELF:_fullName
		PROPERTY GlobalClassName    AS STRING GET SELF:_globalClassName
		PROPERTY HasProjects        AS LOGIC GET SELF:_projects:Count > 0
		PROPERTY ImplicitNamespaces AS IList<STRING> GET SELF:_implicitNamespaces
		PROPERTY IsModifiedOnDisk   AS LOGIC GET SELF:LastWriteTime != SELF:Modified
        PROPERTY LastWriteTime      AS DateTime GET IIF(Self:Exists, System.IO.File.GetLastWriteTime(SELF:FileName), DateTime.MinValue)
		PROPERTY Modified           AS DateTime GET IIF(SELF:Exists, SELF:_Modified, DateTime.MinValue) SET SELF:_Modified := VALUE
		PROPERTY Namespaces         AS IList<STRING> GET SELF:_nameSpaceTexts
		PROPERTY RuntimeVersion     AS STRING
			GET
				SELF:UpdateAssembly()
				IF SELF:_assembly != NULL
					RETURN SELF:_assembly:ImageRuntimeVersion
				ENDIF
				RETURN ""
			END GET
		END PROPERTY
		PROPERTY Types              AS IDictionary<STRING, System.Type> GET SELF:_aTypes

		STATIC METHOD WriteOutputMessage(message AS STRING) AS VOID
			XSolution.WriteOutputMessage("XModel.AssemblyInfo " +message )

		// Nested Types
		INTERNAL CLASS NameSpaceContainer
			// Fields
			INTERNAL _NameSpace := "" AS STRING
			INTERNAL _Types AS SortedList<STRING, AssemblyInfo.TypeTypes>

			CONSTRUCTOR(_cNameSpace AS STRING);SUPER()
				//
				SELF:_NameSpace := _cNameSpace
				SELF:_Types := SortedList<STRING, AssemblyInfo.TypeTypes>{}

			METHOD AddType(typeName AS STRING, type AS AssemblyInfo.TypeTypes) AS VOID
				//
				IF ! SELF:_Types:ContainsKey(typeName)
					//
					SELF:_Types:Add(typeName, type)
				ENDIF

			METHOD Clear() AS VOID
				//
				SELF:_Types:Clear()
			END CLASS

		INTERNAL ENUM TypeTypes AS LONG
			MEMBER @@All:=0xff
			MEMBER @@Class:=1
			MEMBER @@Delegate:=8
			MEMBER @@Interface:=4
			MEMBER @@None:=0
			MEMBER @@Structure:=2
		END ENUM

	END CLASS

END NAMESPACE

//
// Copyright (c) XSharp B.V.  All Rights Reserved.
// Licensed under the Apache License, Version 2.0.
// See License.txt in the project root for license information.
//
USING System.Collections.Generic
USING System.Collections.Immutable
USING System
USING System.IO
USING System.Diagnostics
USING System.Reflection
BEGIN NAMESPACE XSharpModel
	[DebuggerDisplay("{DisplayName,nq}")];
CLASS AssemblyInfo
		// Fields
		PRIVATE _aExtensions AS List<MethodInfo>
		PRIVATE _assembly AS Assembly
		PRIVATE _aTypes AS IDictionary<STRING, System.Type>
		PRIVATE _fullName AS STRING
		PRIVATE _globalClassName AS STRING
		PRIVATE _HasExtensions AS LOGIC
		PRIVATE _implicitNamespaces AS ImmutableList<STRING>
		PRIVATE _LoadedTypes AS LOGIC
		PRIVATE _Modified AS System.DateTime
		PRIVATE _nameSpaces AS System.Collections.Hashtable
		PRIVATE _nameSpaceTexts AS ImmutableList<STRING>
		PRIVATE _projects AS ImmutableList<XProject>
		PRIVATE _reference AS VSLangProj.Reference
		PRIVATE _zeroNamespace AS AssemblyInfo.NameSpaceContainer

		PRIVATE STATIC WorkFolder AS STRING
		PRIVATE STATIC FailedAssemblies AS Dictionary<STRING, INT>

		PUBLIC STATIC PROPERTY DisableAssemblyReferences AS LOGIC AUTO
		PUBLIC STATIC PROPERTY DisableForeignProjectReferences AS LOGIC AUTO
		PUBLIC STATIC PROPERTY DisableXSharpProjectReferences AS LOGIC AUTO
		STATIC CONSTRUCTOR
			// Clear temp files from previous run
			FailedAssemblies := Dictionary<STRING, INT>{StringComparer.OrdinalIgnoreCase}
			VAR cFolder := Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData)
			cFolder := System.IO.Path.Combine(cFolder, "XSharp\Temp")
			IF ! System.IO.Directory.Exists(cFolder)
				System.IO.Directory.CreateDirectory(cFolder)
			ENDIF
			WorkFolder := cFolder
			LOCAL oDir := DirectoryInfo{cFolder} AS DirectoryInfo
			FOREACH VAR oFile IN oDir:GetFiles()
				TRY
					File.SetAttributes(oFile:FullName, FileAttributes.Normal  )
					File.Delete(oFile:FullName)
				CATCH e AS Exception
					XSolution.WriteException(e)
				END TRY
			NEXT
			DisableXSharpProjectReferences := FALSE
			DisableAssemblyReferences := FALSE
			DisableForeignProjectReferences := FALSE
			RETURN

			// Methods
		CONSTRUCTOR()
			SUPER()
			SELF:_fullName := ""
			SELF:_assembly := NULL
			SELF:_projects := ImmutableList<XProject>.Empty
			SELF:_clearInfo()

		CONSTRUCTOR(reference AS VSLangProj.Reference)
			SELF()
			SELF:_reference := reference

		CONSTRUCTOR(_cFileName AS STRING, _dModified AS System.DateTime)
			SELF()
			SELF:FileName := _cFileName
			SELF:Modified := _dModified
			SELF:UpdateAssembly()

		PRIVATE METHOD _clearInfo() AS VOID
			//
			SELF:_aTypes := Dictionary<STRING, System.Type>{System.StringComparer.OrdinalIgnoreCase}
			SELF:_aExtensions := List<MethodInfo>{}
			SELF:_nameSpaces := System.Collections.Hashtable{System.StringComparer.OrdinalIgnoreCase}
			SELF:_nameSpaceTexts := ImmutableList<STRING>.Empty
			SELF:_implicitNamespaces := ImmutableList<STRING>.Empty
			SELF:_zeroNamespace := AssemblyInfo.NameSpaceContainer{"_"}
			SELF:_LoadedTypes := FALSE
			SELF:_HasExtensions := FALSE

		METHOD AddProject(project AS XProject) AS VOID
			IF ! SELF:_projects:Contains(project)
				SELF:_projects := SELF:_projects:Add(project)
			ENDIF

		PRIVATE METHOD CurrentDomain_AssemblyResolve(sender AS OBJECT, args AS System.ResolveEventArgs) AS Assembly
			VAR folders := List<STRING>{}	// list of folders that we have tried
			VAR folderPath := System.IO.Path.GetDirectoryName(SELF:FileName)
			VAR name := AssemblyName{args:Name}:Name + ".dll"
			VAR assemblyPath := System.IO.Path.Combine(folderPath, name)
			WriteOutputMessage("--> CurrentDomain_AssemblyResolve : "+assemblyPath)
			IF System.IO.File.Exists(assemblyPath)
				VAR assembly := AssemblyInfo.LoadAssemblyFromFile(assemblyPath)
				IF assembly != NULL
					RETURN assembly
				ENDIF
			ENDIF
			folders:Add(folderPath)
			FOREACH path AS STRING IN SystemTypeController.AssemblyFileNames
				folderPath := System.IO.Path.GetDirectoryName(path)
				IF ! folders:Contains(folderPath)
					assemblyPath := System.IO.Path.Combine(folderPath, name)
					IF System.IO.File.Exists(assemblyPath)
						VAR asm := AssemblyInfo.LoadAssemblyFromFile(assemblyPath)
						IF asm != NULL
							RETURN asm
						ENDIF
					ENDIF
					folders:Add(folderPath)
				ENDIF
			NEXT
			WriteOutputMessage("<-- CurrentDomain_AssemblyResolve : "+assemblyPath)
			RETURN NULL

		METHOD GetType(name AS STRING) AS System.Type
			IF SELF:IsModifiedOnDisk
				SELF:LoadAssembly()
			ENDIF
			IF SELF:_assembly != NULL .AND. SELF:_aTypes:Count == 0
				SELF:UpdateAssembly()
			ENDIF
			IF SELF:_aTypes:ContainsKey(name)
				RETURN SELF:Types:Item[name]
			ENDIF
			RETURN NULL

		PRIVATE METHOD GetTypeTypesFromType(oType AS System.Type) AS AssemblyInfo.TypeTypes
			IF oType:IsValueType
				RETURN AssemblyInfo.TypeTypes.Structure
			ENDIF
			IF oType:IsInterface
				RETURN AssemblyInfo.TypeTypes.Interface
			ENDIF
			IF TYPEOF(System.Delegate):IsAssignableFrom(oType)
				RETURN AssemblyInfo.TypeTypes.Delegate
			ENDIF
			RETURN AssemblyInfo.TypeTypes.Class

		PRIVATE STATIC METHOD HasExtensionAttribute(memberInfo AS MemberInfo) AS LOGIC
			TRY
				VAR customAttributes := memberInfo:GetCustomAttributes(FALSE)
				FOREACH VAR custattr IN customAttributes
					IF custattr:ToString() == "System.Runtime.CompilerServices.ExtensionAttribute"
						RETURN TRUE
					ENDIF
				NEXT
			CATCH
				// Failed to retrieve custom attributes
			END TRY
			RETURN FALSE

		INTERNAL METHOD LoadAssembly()  AS VOID
			WriteOutputMessage("--> LoadAssembly : "+SELF:FileName)
			IF String.IsNullOrEmpty(SELF:FileName) .AND. SELF:_reference != NULL
				SELF:FileName := SELF:_reference:Path
			ENDIF
			IF SELF:Exists
				SELF:_assembly := AssemblyInfo.LoadAssemblyFromFile(SELF:FileName)
				IF SELF:_assembly != NULL
					SELF:_fullName  := SELF:_assembly:FullName
					SELF:Modified   := SELF:LastWriteTime
				ENDIF
				SELF:_clearInfo()
			ENDIF
			WriteOutputMessage("<-- LoadAssembly : "+SELF:FileName)

		STATIC METHOD FindPdbName(fileName AS STRING) AS STRING
			WriteOutputMessage("--> FindPDBName : "+fileName)
			TRY
				IF System.IO.File.Exists(fileName)
					VAR pdb			:= System.IO.Path.ChangeExtension(fileName,".pdb")
					IF System.IO.File.Exists(pdb)
						RETURN pdb
					ENDIF
					// ok when it is not in the DLL folder but referenced in the DLL then extract this information
					VAR text        := System.IO.File.ReadAllText(fileName)
					VAR pdbIndex	:= text:IndexOf(".pdb", StringComparison.InvariantCultureIgnoreCase)
					IF pdbIndex > 0
						VAR lastTerminatorIndex := text:Substring(0, pdbIndex).LastIndexOf('\0')
						RETURN text:Substring(lastTerminatorIndex + 1, pdbIndex - lastTerminatorIndex + 3)
					ENDIF
				ENDIF
			FINALLY
				WriteOutputMessage("<-- FindPDBName : "+fileName)
			END TRY
			RETURN string.Empty

		STATIC METHOD LoadAssemblyFromFile(fileName AS STRING) AS Assembly
			LOCAL result  AS Assembly
			LOCAL iAttempts AS INT
			TRY
				WriteOutputMessage("--> LoadAssemblyFromFile : "+fileName)
				IF FailedAssemblies:ContainsKey(fileName)
					iAttempts := FailedAssemblies[fileName]
					IF iAttempts > 3
						WriteOutputMessage("    ... Cannot load assembly, giving up after a few retries:")
						WriteOutputMessage("    ...."  +fileName+" "+ iAttempts:ToString())
						RETURN NULL
					ENDIF
				ENDIF
				IF System.IO.File.Exists(fileName)
					LOCAL cPDB		AS STRING
					LOCAL cPdbCopy	AS STRING
					LOCAL PdbRenamed   := FALSE AS LOGIC
					TRY
						VAR temp		:=  System.IO.Path.Combine(WorkFolder, System.IO.Path.GetFileName(fileName))
						System.IO.File.Copy(fileName, temp,TRUE)
						VAR rawAssembly := System.IO.File.ReadAllBytes(temp)
						cPdb		:= FindPdbName(temp)
						System.IO.File.Delete(temp)
						cPdbCopy := System.IO.Path.ChangeExtension(temp, ".p$$")
						IF !String.IsNullOrEmpty(cPdb) .AND. System.IO.File.Exists(cPdb)
							PdbRenamed := TRUE
							IF System.IO.File.Exists(cPdbCopy)
								System.IO.File.Delete(cPdbCopy)
							ENDIF
							System.IO.File.Move(cPdb, cPdbCopy)
						ENDIF
						result := Assembly.Load(rawAssembly)
						IF FailedAssemblies:ContainsKey(fileName)
							FailedAssemblies:Remove(fileName)
						ENDIF
						RETURN result
					CATCH exception AS System.IO.FileLoadException
						// files with unmanaged code produce the exception:
						// Attempt to load an unverifiable executable with fixups (IAT with more than 2 sections or a TLS section.) (Exception from HRESULT: 0x80131019)
						XSolution.WriteException(exception)
					CATCH exception AS System.Exception
						// Other exception
						XSolution.WriteException(exception)
					FINALLY
						IF PdbRenamed .AND. System.IO.File.Exists(cPdbCopy)
							System.IO.File.Move(cPdbCopy, cPdb)
						ENDIF
					END TRY
					TRY
						// Generate a random name and copy the DLL to prevent locking the original file
						VAR temp := filename
						DO WHILE File.Exists(temp)
							temp := System.IO.Path.Combine(WorkFolder, System.IO.Path.GetRandomFileName())
						ENDDO
						// Need to rename the PDB again. Was undone in the Finally above
						IF PdbRenamed .AND. System.IO.File.Exists(cPdb)
							System.IO.File.Move(cPdb, cPdbCopy)
						ENDIF
						System.IO.File.Copy(filename, temp,TRUE)
						File.SetAttributes(temp, FileAttributes.Normal | FileAttributes.Temporary )
						result := Assembly.LoadFrom(temp)
						IF FailedAssemblies:ContainsKey(fileName)
							FailedAssemblies:Remove(fileName)
						ENDIF
						RETURN result
					CATCH e AS Exception
						XSolution.WriteException(e)
					FINALLY
						IF PdbRenamed .AND. System.IO.File.Exists(cPdbCopy)
							TRY
								System.IO.File.Move(cPdbCopy, cPdb)
							CATCH e AS Exception
								XSolution.WriteException(e)
							END TRY
						ENDIF
					END TRY
					IF FailedAssemblies:ContainsKey(fileName)
						FailedAssemblies[fileName] := FailedAssemblies[fileName] +1
					ELSE
						FailedAssemblies:Add(fileName, 1)
					ENDIF
				ELSE
					// FileName does not exist. No need to change the failedAssemblies list
				ENDIF
			FINALLY
				WriteOutputMessage("<-- LoadAssemblyFromFile : "+fileName)
			END TRY
			RETURN NULL

		METHOD RemoveProject(project AS XProject) AS VOID
			//
			IF SELF:_projects:Contains(project)
				SELF:_projects := SELF:_projects:Remove(project)
			ENDIF

		METHOD Refresh() AS VOID
            IF SELF:Exists
			    VAR currentDT := SELF:LastWriteTime
			    IF currentDT != SELF:Modified
				    WriteOutputMessage("AssemblyInfo.Refresh() Assembly was changed: "+SELF:FileName )
				    SELF:UpdateAssembly()
			    ENDIF
            ENDIF

		INTERNAL METHOD UpdateAssembly() AS VOID
			LOCAL aTypes AS Dictionary<STRING, System.Type>
			LOCAL nspace AS STRING
			LOCAL fullName AS STRING
			LOCAL simpleName AS STRING
			LOCAL message AS STRING
			LOCAL types := NULL AS System.Type[]
			LOCAL index AS LONG
            IF ! SELF:Exists
				WriteOutputMessage("****** AssemblyInfo.UpdateAssembly: assembly "+SELF:FileName +" does not exist")
                RETURN
            ENDIF
			TRY
				WriteOutputMessage("-->AssemblyInfo.UpdateAssembly load types from assembly "+SELF:FileName )
				aTypes		 := Dictionary<STRING, System.Type>{System.StringComparer.OrdinalIgnoreCase}
				_aExtensions := List<MethodInfo>{}
				fullName := ""
				simpleName := ""
				SELF:_nameSpaces:Clear()
				_nameSpaceTexts := ImmutableList<STRING>.Empty
				SELF:_zeroNamespace:Clear()
				SELF:_globalClassName := ""
				SELF:_HasExtensions := FALSE
				SELF:LoadAssembly()
				VAR currentDomain := System.AppDomain.CurrentDomain
				currentDomain:AssemblyResolve += System.ResolveEventHandler{ SELF, @CurrentDomain_AssemblyResolve() }
				TRY
					IF SELF:_assembly != NULL
						VAR customAttributes := SELF:_assembly:GetCustomAttributes(FALSE)
						LOCAL found := 0 AS INT
						FOREACH VAR custattr IN customAttributes
							//
							VAR type := custattr:GetType()
							SWITCH custattr:ToString()
								CASE "Vulcan.Internal.VulcanClassLibraryAttribute"
									SELF:_globalClassName := type:GetProperty("globalClassName"):GetValue(custattr, NULL):ToString()
									VAR defaultNs := type:GetProperty("defaultNamespace"):GetValue(custattr, NULL):ToString()
									IF ! String.IsNullOrEmpty(defaultNs)
										SELF:_implicitNamespaces := SELF:_implicitNamespaces:Add(defaultNs)
										WriteOutputMessage("   ...implicit Namespace found: "+defaultNs)
									ENDIF
									found += 1
								CASE "XSharp.Internal.ClassLibraryAttribute"
									SELF:_globalClassName := type:GetProperty("GlobalClassName"):GetValue(custattr, NULL):ToString()
									WriteOutputMessage("   ...Globals Classname found: "+_globalClassName )

									VAR defaultNs := type:GetProperty("DefaultNamespace"):GetValue(custattr, NULL):ToString()
									IF ! String.IsNullOrEmpty(defaultNs)
										SELF:_implicitNamespaces := SELF:_implicitNamespaces:Add(defaultNs)
										WriteOutputMessage("   ...implicit Namespace found: "+defaultNs)
									ENDIF
									found += 1
								CASE "System.Runtime.CompilerServices.ExtensionAttribute"
									SELF:_HasExtensions  := TRUE
									found += 2
								CASE "Vulcan.VulcanImplicitNamespaceAttribute"
								CASE "XSharp.ImplicitNamespaceAttribute"
									VAR ns := type:GetProperty("Namespace"):GetValue(custattr, NULL):ToString()
									IF ! String.IsNullOrEmpty(ns)
										SELF:_implicitNamespaces := SELF:_implicitNamespaces:Add(ns)
										WriteOutputMessage("   ...implicit Namespace found: "+ns)
									ENDIF
									found += 4
							END SWITCH
							// All attributes found then get out of here
							IF found == 7
								EXIT
							ENDIF
						NEXT

					ENDIF
				CATCH e AS Exception
					XSolution.WriteException(e)
				END TRY
				TRY
					IF SELF:_assembly != NULL
						types := SELF:_assembly:GetTypes()
					ENDIF
				CATCH exception AS ReflectionTypeLoadException
					//
					WriteOutputMessage("Cannot load types from "+ SELF:_assembly:GetName():Name)
					XSolution.WriteException(exception)
					message := NULL
					FOREACH VAR exception2 IN exception:LoaderExceptions
						IF exception2:Message != message
							XSolution.WriteException(exception2)
							message := exception2:Message
						ENDIF
					NEXT
					SELF:_assembly := NULL
				CATCH ex AS System.Exception
					XSolution.WriteException(ex)
				END TRY
				currentDomain:AssemblyResolve -= System.ResolveEventHandler{ SELF, @CurrentDomain_AssemblyResolve() }

				IF types != NULL .AND. types:Length != 0 .AND. (aTypes:Count == 0  .OR. ! _LoadedTypes)
					TRY
						FOREACH VAR type IN types
							fullName := type:FullName
							IF ! fullName:StartsWith("$") .AND. ! fullName:StartsWith("<")
								IF SELF:_HasExtensions .AND. AssemblyInfo.HasExtensionAttribute(type)
									VAR methods := type:GetMethods(BindingFlags.Public | BindingFlags.Static)
									FOREACH info AS MethodInfo IN methods
										IF AssemblyInfo.HasExtensionAttribute(info)
											_aExtensions:Add(info)
										ENDIF
									NEXT
								ENDIF
								//
								// Nested Type ?
								IF fullName:Contains("+")
									fullName := fullName:Replace('+', '.')
								ENDIF
								// Generic ?
								IF fullName:Contains("`")
									fullName := fullName:Substring(0, fullName:IndexOf("`") + 2)
								ENDIF
								// Add to the FullyQualified name
								IF ! aTypes:ContainsKey(fullName)
									aTypes:Add(fullName, type)
								ENDIF
								// Now, with Standard name
								simpleName := type:Name:Replace('+', '.')
								// Not Empty namespace, not a generic, not nested, not starting with underscore
								IF String.IsNullOrEmpty(type:Namespace) .AND. simpleName:IndexOf('`') == -1 .AND. ;
										simpleName:IndexOf('<') == -1 .AND. ! simpleName:StartsWith("_")
									SELF:_zeroNamespace:AddType(simpleName, SELF:GetTypeTypesFromType(type))
								ENDIF
								// Public Type, not Nested and no Underscore
								IF type:IsPublic .AND. simpleName.IndexOf('+') == -1  .AND. simpleName.IndexOf('_') == -1
									// Get the Namespace
									nspace := type:Namespace
									// and the normal name
									simpleName := type:Name
									simpleName := simpleName:Replace('+', '.')
									// Generic ?
									index := simpleName:IndexOf('`')
									IF index != -1
										simpleName := simpleName:Substring(0, index)
									ENDIF
									IF nspace?:Length > 0
										LOCAL container AS AssemblyInfo.NameSpaceContainer
										IF ! SELF:_nameSpaces:ContainsKey(nspace)
											container := AssemblyInfo.NameSpaceContainer{nspace}
											container:AddType(simpleName, SELF:GetTypeTypesFromType(type))
											SELF:_nameSpaces:Add(nspace, container)
											SELF:_nameSpaceTexts := SELF:_nameSpaceTexts:Add(nspace)
										ELSE
											container := (AssemblyInfo.NameSpaceContainer)SELF:_nameSpaces[nspace]
											container:AddType(simpleName, SELF:GetTypeTypesFromType(type))
										ENDIF
										DO WHILE nspace:Contains(".")
											nspace := nspace:Substring(0, nspace:LastIndexOf('.'))
											IF ! SELF:_nameSpaceTexts:Contains(nspace)
												SELF:_nameSpaceTexts := SELF:_nameSpaceTexts:Add(nspace)
											ENDIF
										ENDDO
									ENDIF
								ENDIF
							ENDIF
						NEXT
						SELF:_LoadedTypes := TRUE
						SELF:_aTypes := aTypes.ToImmutableDictionary(System.StringComparer.OrdinalIgnoreCase)
						SELF:_assembly := NULL
					CATCH e AS System.Exception
						XSolution.WriteException(e)
						SELF:_clearInfo()
					END TRY
				ENDIF
			FINALLY
				WriteOutputMessage("<-- AssemblyInfo.UpdateAssembly load types from assembly "+SELF:FileName )
			END TRY
			// Properties
		PROPERTY DisplayName AS STRING
			GET
				IF String.IsNullOrEmpty(SELF:fileName)
					RETURN "(Empty)"
				ENDIF
				RETURN System.IO.Path.GetFileName(SELF:fileName)
			END GET
		END PROPERTY

        INTERNAL STATIC METHOD _SafeExists(cFileName as STRING) AS LOGIC
            local lExists := FALSE as LOGIC
            TRY
                IF !String.IsNullOrEmpty(cFileName)
                    IF System.IO.File.Exists(cFileName)
                        lExists := TRUE
                    ENDIF
                ENDIF
            CATCH
                lExists := FALSE
            END TRY
            RETURN lExists

        PROPERTY Exists             AS LOGIC GET _SafeExists(SELF:FileName)
		PROPERTY FileName           AS STRING AUTO
		PROPERTY FullName           AS STRING GET SELF:_fullName
		PROPERTY GlobalClassName    AS STRING GET SELF:_globalClassName
		PROPERTY HasProjects        AS LOGIC GET SELF:_projects:Count > 0
		PROPERTY ImplicitNamespaces AS IList<STRING> GET SELF:_implicitNamespaces
		PROPERTY IsModifiedOnDisk   AS LOGIC GET SELF:LastWriteTime != SELF:Modified
        PROPERTY LastWriteTime      AS DateTime GET IIF(Self:Exists, System.IO.File.GetLastWriteTime(SELF:FileName), DateTime.MinValue)
		PROPERTY Modified           AS DateTime GET IIF(SELF:Exists, SELF:_Modified, DateTime.MinValue) SET SELF:_Modified := VALUE
		PROPERTY Namespaces         AS IList<STRING> GET SELF:_nameSpaceTexts
		PROPERTY RuntimeVersion     AS STRING
			GET
				SELF:UpdateAssembly()
				IF SELF:_assembly != NULL
					RETURN SELF:_assembly:ImageRuntimeVersion
				ENDIF
				RETURN ""
			END GET
		END PROPERTY
		PROPERTY Types              AS IDictionary<STRING, System.Type> GET SELF:_aTypes

		STATIC METHOD WriteOutputMessage(message AS STRING) AS VOID
			XSolution.WriteOutputMessage("XModel.AssemblyInfo " +message )

		// Nested Types
		INTERNAL CLASS NameSpaceContainer
			// Fields
			INTERNAL _NameSpace := "" AS STRING
			INTERNAL _Types AS SortedList<STRING, AssemblyInfo.TypeTypes>

			CONSTRUCTOR(_cNameSpace AS STRING);SUPER()
				//
				SELF:_NameSpace := _cNameSpace
				SELF:_Types := SortedList<STRING, AssemblyInfo.TypeTypes>{}

			METHOD AddType(typeName AS STRING, type AS AssemblyInfo.TypeTypes) AS VOID
				//
				IF ! SELF:_Types:ContainsKey(typeName)
					//
					SELF:_Types:Add(typeName, type)
				ENDIF

			METHOD Clear() AS VOID
				//
				SELF:_Types:Clear()
			END CLASS

		INTERNAL ENUM TypeTypes AS LONG
			MEMBER @@All:=0xff
			MEMBER @@Class:=1
			MEMBER @@Delegate:=8
			MEMBER @@Interface:=4
			MEMBER @@None:=0
			MEMBER @@Structure:=2
		END ENUM

	END CLASS

END NAMESPACE