﻿'A tiny cfg class by ben Jones used to read and write basic config files.

'Version 1.1 - Updates
'Support for reading and writeing config files.
'Support for Lists.
'Support for \t \n \r
'Support for downloading from the internet.
'Support for reading environment variables.

'Version 1.2 - Updates
'Return config data as a list
'Set config data from a list
'Support for comments ;
'Comments are now not teated as key names eg #=value is treated now as a comment.
'Escape strings are noe optional \t \n \r
'Added GetValues to return all values.
'Added AddComment
'Added RemoveBlankLines

Option Explicit On
Imports System.IO
Imports System.Net

Public Class mycfg

    Private m_Filename As String = vbNullString
    Private cfg_data As List(Of String)
    Private m_KeySeparator As Char = "="c
    Private m_ListSeparator As Char = ","c
    Private m_Comment As Char = ";"c

    Private Structure TCFGData
        Dim LineIdx As Integer
        Dim Data As String
    End Structure

    Public Property Filename As String
        Get
            Return m_Filename
        End Get
        Set(ByVal value As String)
            m_Filename = value
        End Set
    End Property

    Public Sub New()
        'Create new list object.
        cfg_data = New List(Of String)
    End Sub

    Public Property ListSeparator As Char
        Get
            Return m_ListSeparator
        End Get
        Set(ByVal value As Char)
            m_ListSeparator = value
        End Set
    End Property

    Public Property KeySeparator As Char
        Get
            Return m_KeySeparator
        End Get
        Set(ByVal value As Char)
            m_KeySeparator = value
        End Set
    End Property

    Public Property CommmentChar As Char
        Get
            Return m_Comment
        End Get
        Set(ByVal value As Char)
            m_Comment = value
        End Set
    End Property

    Public Sub RemoveBlankLines()
        Dim idx As Integer = 0
        'Removes any blank lines.

        For x As Integer = (cfg_data.Count - 1) To 0 Step -1
            Dim Line As String = cfg_data(x)
            If String.IsNullOrEmpty(Line) Then
                'Remove empty lines.
                cfg_data.RemoveAt(x)
            End If
        Next x
    End Sub

    Public Sub AddComment(ByVal value As String)
        'Add a comment.
        cfg_data.Add(CommmentChar & value)
    End Sub

    Public Function GetValue(ByVal Key As String, Optional ByVal iDefault As String = vbNullString, Optional ByVal EscapeString As Boolean = False) As String
        Dim Temp As TCFGData = Nothing
        'Get CFG Data.
        GetData(Key, Temp, iDefault, True)

        'Check that index was not -1
        If (Temp.LineIdx = -1) Then
            Return iDefault
        End If

        If EscapeString Then
            'For adding new lines and tabs.
            Temp.Data = Replace(Temp.Data, "\t", vbTab)
            Temp.Data = Replace(Temp.Data, "\n", vbLf)
            Temp.Data = Replace(Temp.Data, "\r", vbCr)
        End If

        'Return found data.
        Return Temp.Data
    End Function

    Private Sub GetData(ByVal Key As String, ByRef TheData As TCFGData, Optional ByVal iDefault As String = vbNullString,
                        Optional ByVal ReadData As Boolean = True)
        Dim Idx As Integer = -1

        For Count As Integer = 0 To cfg_data.Count - 1

            Dim SrcLine As String = cfg_data(Count)
            Dim sPos As Integer = SrcLine.IndexOf(KeySeparator)

            If (sPos <> -1) Then
                Dim sKey As String = SrcLine.Substring(0, sPos).Trim().ToUpper()

                'Check if keys match.
                If sKey = Key.ToUpper() Then
                    Idx = Count
                    TheData.LineIdx = Idx
                    If ReadData Then
                        Dim Buffer As String = SrcLine.Substring(sPos + 1).TrimStart()
                        If String.IsNullOrEmpty(Buffer) Then
                            TheData.Data = iDefault
                        Else
                            TheData.Data = Buffer
                        End If
                    End If
                    Exit For
                Else
                    TheData.LineIdx = Idx
                    Idx = -1
                End If
            End If
        Next Count
        'Return index
        TheData.LineIdx = Idx
    End Sub

    Public Sub ReadConfig()
        Dim sr As StreamReader = Nothing

        'Create new list object.
        cfg_data = New List(Of String)

        If Not File.Exists(Filename) Then
            Throw New FileNotFoundException
        Else
            'Create stream reader.
            sr = New StreamReader(Filename)

            While sr.Peek <> -1
                'Add the line to the list object,
                cfg_data.Add(sr.ReadLine())
            End While
            sr.Close()

        End If
    End Sub

    Public Sub Clear()
        'Clear list collection.
        cfg_data = New List(Of String)
    End Sub

    Public Function DeleteList(ByVal Name As String) As Boolean
        'Delete list.
        Return DeleteKey(Name)
    End Function

    Public Function DeleteKey(ByVal Key As String) As Boolean
        Dim Temp As TCFGData = Nothing

        'Get CFG Data.
        GetData(Key, Temp, , False)

        'Check if key was found.
        If (Temp.LineIdx <> -1) Then
            cfg_data.RemoveAt(Temp.LineIdx)
            Return True
        End If

        Return False
    End Function

    Public Sub GetKeys(ByVal Keys As List(Of String))
        Dim Temp As String = vbNullString

        For Each Line As String In cfg_data
            Dim idx As Integer = Line.IndexOf(KeySeparator)
            'Return keys
            If (idx <> -1) And Not Line.StartsWith(CommmentChar) Then
                'Extract key name.
                Temp = Line.Substring(0, idx).Trim()
                'Check for empty string.
                If Not String.IsNullOrEmpty(Temp) Then
                    Keys.Add(Line.Substring(0, idx).Trim())
                End If
            End If
        Next Line
    End Sub

    Public Sub GetValues(ByVal Valus As List(Of String))
        Dim Temp As String = vbNullString

        For Each Line As String In cfg_data
            Dim sLine As String = Line.TrimStart()
            Dim idx As Integer = sLine.IndexOf(KeySeparator)

            'Return values
            If (idx <> -1) And Not Line.StartsWith(CommmentChar) Then
                'Extract value and trim start.
                Temp = sLine.Substring(idx + 1).TrimStart()
                'Check for empty string.
                If Not String.IsNullOrEmpty(Temp) And (idx <> 0) Then
                    Valus.Add(Temp)
                End If
            End If
        Next Line
    End Sub

    Public Sub FromWeb(ByVal Address As String)
        Dim wc As New WebClient()
        'Download the string.
        FromString(wc.DownloadString(Address))
        'Clear up
        wc = Nothing
    End Sub

    Public Sub FromString(ByVal Source As String)

        'Split the string.
        Dim Lines() As String = Split(Source, vbCrLf)
        'Clear List
        Clear()

        'Add each line to the list collection.
        For Each Line As String In Lines
            cfg_data.Add(Line)
        Next Line

    End Sub

    Public Sub FromList(ByVal Items As List(Of String))
        'Clear list
        Clear()
        'Fill list with items
        cfg_data = Items
    End Sub

    Public Function ListExists(ByVal Name As String) As Boolean
        'Check if list is found.
        Return KeyExists(Name)
    End Function

    Public Function KeyExists(ByVal Key As String) As Boolean
        Dim Temp As TCFGData = Nothing
        'Get CFG Data.
        GetData(Key, Temp, , False)
        'Return true or false if key is found.
        Return (Temp.LineIdx <> -1)
    End Function

    Public Sub UpdateConfig()
        Dim sw As StreamWriter = Nothing

        Try
            'Create stream writer object.
            sw = New StreamWriter(Filename)
            'Write data to filename.
            sw.Write(ToString())
            'Close file.
            sw.Close()
        Catch ex As Exception
            Throw New ArgumentException(ex.Message)
        End Try

    End Sub

    Public Function ExpandString(ByVal Key As String, Optional ByVal iDefault As String = vbNullString) As String
        'Read and expend string like %windir%\system32\
        Return Environment.ExpandEnvironmentVariables(CStr(GetValue(Key, iDefault)))
    End Function

    Public Sub ReadList(ByVal Key As String, ByRef Items As List(Of String))
        Dim Temp As TCFGData = Nothing

        'Get CFG Data.
        GetData(Key, Temp, , True)
        '
        If (Temp.LineIdx <> -1) Then
            'Read line
            Dim sLine As String = Temp.Data.Trim()

            If sLine.StartsWith("{") And sLine.EndsWith("}") Then
                'Remove the brackets.
                sLine = sLine.Substring(1, sLine.Length - 2)
                'Split the parts.
                Dim Parts() As String = sLine.Split(CType(ListSeparator, Char))

                'Return the broken parts.
                For Each Part As String In Parts
                    'Only add if we have a string.
                    If Not String.IsNullOrEmpty(Part) Then
                        'Add the parts.
                        Items.Add(Part)
                    End If
                Next Part
            End If
        End If

    End Sub

    Public Sub SetList(ByVal Key As String, ByVal ParamArray Items() As String)
        Dim Temp As TCFGData = Nothing

        'Get CFG Data.
        GetData(Key, Temp, , False)

        Dim sLine As String = "{" & String.Join(ListSeparator, Items) & "}"

        If (Temp.LineIdx = -1) Then
            'Add new list.
            cfg_data.Add(Key & KeySeparator & sLine)
        Else
            'Update the data.
            cfg_data(Temp.LineIdx) = Key & KeySeparator & sLine
        End If

    End Sub

    Public Function RenameList(ByVal OldName As String, ByVal NewName As String) As Boolean
        'Rename list key.
        Return RenameKey(OldName, NewName)
    End Function

    Public Function RenameKey(ByVal OldName As String, ByVal NewName As String) As Boolean
        Dim Temp As TCFGData = Nothing

        'Get CFG Data.
        GetData(OldName, Temp, , True)

        'Check for old key.
        If (Temp.LineIdx = -1) Then
            Return False
        End If

        'Rename the key.
        cfg_data(Temp.LineIdx) = NewName & KeySeparator & Temp.Data

        'Return true.
        Return True

    End Function

    Public Sub SetValue(ByVal Key As String, ByVal Source As String)
        Dim Temp As TCFGData = Nothing

        GetData(Key, Temp, , False)

        If (Temp.LineIdx = -1) Then
            'Add new key and value.
            cfg_data.Add(Key & KeySeparator & Source)
        Else
            'Update old keys data.
            cfg_data(Temp.LineIdx) = Key & KeySeparator & Source
        End If
    End Sub

    Public Overrides Function ToString() As String
        'Return list as a string.
        Return String.Join(vbCrLf, cfg_data.ToArray)
    End Function

    Public Function ToList() As List(Of String)
        'Return the list collection.
        Return cfg_data
    End Function
End Class
