Public Class %CLASSNAME%
    '------------------------------------------------------------------ 
    '   cfCollection. Carl Franklin's Generic collection class. 
    '   Use this in your VB.NET code the way you would use a generic 
    '   collection in VB6. 
    '
    '   You may use and redistribute this code as long as you do not
    '   charge money for it in any way, even as part of another 
    '   product.
    '
    '   Want to learn VB.Net hands-on with me? 
    '   Come to one of my VB.Net Master Classes and let me show you
    '   the way to .NET Nirvana
    '
    '   Details at http://www.franklins.net/vbnetmc.asp
    '
    '   Carl Franklin
    '   carl@franklins.net
    '------------------------------------------------------------------
    
    '-- The ArrayList is a collection that you can access by index, but
    '   does not keep a list of keys. This is used to store items.
    Private mvarItems As Collections.ArrayList
    
    '-- This one is used to store Keys
    Private mvarKeys As Collections.ArrayList
    
    '-- This collection is 1 based by default. Set this to True to make
    '   it zero based
    Private mvarZeroBased As Boolean
    
    '-- BaseError is the lowest error number this object can generate
    '   as a user-defined error
    'Private Const BaseError As Integer = ObjectError   '-- ObjectError is flaky in Beta 1
    Private Const BaseError As Integer = 513
    
    '-- These error values arbitrarily start at 1000
    Private Const ERR_ITEM_SET As Integer = BaseError + 1000
    Private Const ERR_ITEM_GET As Integer = BaseError + 1001
    Private Const ERR_ADD As Integer = BaseError + 1002
    Private Const ERR_REMOVE As Integer = BaseError + 1004
    Private Const ERR_ADDRANGE As Integer = BaseError + 1005
    
    Public Overridable Sub Add(ByVal Item As %SINGLENAME%, Optional ByVal Key As String = "")
        '-- This is your generic Add method. Nothing too fancy here.
        '   Overridable means that this method can be handled in 
        '   a derived class.
        Try
            mvarItems.Add(Item)     '-- Add the item to mvarItems
            mvarKeys.Add(LCase(Key)) '-- Add the key to mvarKeys - even if it's "". It's critical to keep the same number of elements in each array.
            '-- LCase is for case-insensitivity. Take it out in this class if you want a case sensitive collection.
        Catch e As Exception
            Err.Raise(ERR_ADD, "cfCollection.Add", e.Message)
        End Try
    End Sub
    
    Public Overridable Sub AddRange(ByVal c As System.Collections.ICollection)
        '-- Appends a range of items passed as a collection
        Try
            mvarItems.AddRange(c)
        Catch e As Exception
            Err.Raise(ERR_ADDRANGE, "cfCollection.AddRange", e.Message)
        End Try
    End Sub
    
    
    Public Overridable ReadOnly Property Index(ByVal Key As String) As Integer
        '-- Returns the index given a key.
        '   If ZeroBased is True, and the key is not found, it returns
        '   a negative number.
        '   If ZeroBased is False, and the key is not found, it returns
        '   zero.
        Get
            '-- BinarySearch returns the index in mvarKeys of the Key.
            '   This index will match up to mvarItems.
            Dim ThisIndex As Integer = mvarKeys.BinarySearch(LCase(Key))
            
            If mvarZeroBased = False Then   '-- Adjust for zero-based
                ThisIndex = ThisIndex + 1
            End If
            
            '-- Return the index.
            Return ThisIndex
        End Get
    End Property
    
    Public Overridable ReadOnly Property IndexOf(ByVal Item As %SINGLENAME%) As Integer
        '-- Returns the index of a given Item
        Get
            Return mvarItems.IndexOf(Item)
        End Get
    End Property
    
    Public Overridable ReadOnly Property Key(ByVal Index As Integer) As String
        '-- Returns the Key given an index
        Get
            Dim ThisIndex As Integer = CInt(Index)
            '-- Adjust for zero based
            If mvarZeroBased = False Then
                ThisIndex = ThisIndex - 1
            End If
            '-- The Keys collection holds the keys
            Return CStr(mvarKeys.Item(ThisIndex))
        End Get
    End Property
    
    Public Overridable ReadOnly Property Count() As Integer
        '-- Returns the number of items in the collection
        Get
            '-- Pass it on up.
            Return mvarItems.Count
        End Get
    End Property
    
    Public Overridable ReadOnly Property Exists(ByVal Key As String) As Boolean
        '-- Check for the existence of an item by it's key
        Get
            '-- BinarySearch returns the index where the key
            '   is, or a negative number if it did not find it. 
            If mvarKeys.BinarySearch(LCase(Key)) >= 0 Then
                Return True
            Else
                Return False
            End If
        End Get
    End Property
    
    Public Overridable Sub Clear()
        '-- Clears the collection
        mvarItems.Clear()
        mvarKeys.Clear()
    End Sub
    
    Public Overridable Property ZeroBased() As Boolean
        '-- Toggles whether to treat indexes 
        '   as zero-based or one-based.
        Get
            Return mvarZeroBased
        End Get
        Set
            mvarZeroBased = Value
        End Set
    End Property
    
    Public Overridable Sub Remove(ByVal Index As Object)
        '-- Removes an item from the collection.
        '   If an Integer is passed, it is assumed
        '   to be an index. If a string is passed,
        '   it's assumed to be a key.
        Try
            If TypeOf Index Is Integer Then '-- Index
                
                '-- Convert to Integer
                Dim ThisIndex As Integer = CInt(Index)
                
                '-- Adjust for zero-based
                If mvarZeroBased = False Then
                    ThisIndex = ThisIndex - 1
                End If
                
                '-- Is this index valid?
                If ThisIndex < mvarItems.Count Then
                    '-- Remove the item and its key
                    mvarItems.RemoveAt(ThisIndex)
                    mvarKeys.RemoveAt(ThisIndex)
                End If
                
            ElseIf TypeOf Index Is String Then  '-- Key
                
                '-- Search for the key
                Dim ThisIndex As Integer = mvarKeys.BinarySearch(LCase(CStr(Index)))
                
                If ThisIndex >= 0 Then  '-- Did we find it?
                    '-- Yes! Remove the item and its key
                    mvarItems.RemoveAt(ThisIndex)
                    mvarKeys.RemoveAt(ThisIndex)
                End If
            End If
        Catch e As Exception
            Err.Raise(ERR_REMOVE, "cfCollection.Remove", e.Message)
        End Try
    End Sub
    
    Default Public Overridable Property Item(ByVal Index As Object) As %SINGLENAME%
        '-- This handler gets and sets the value in the collection.
        '   Default means that it's the default property for this 
        '   class. 
        
        Get '-- Get Property Handler
            Try
                If TypeOf Index Is Integer Then '-- Index
                    '-- Convert to Integer
                    Dim ThisIndex As Integer = CInt(Index)
                    
                    '-- Adjust for zero-based
                    If mvarZeroBased = False Then
                        ThisIndex = ThisIndex - 1
                    End If
                    
                    '-- Is this a valid index?
                    If ThisIndex < mvarItems.Count Then
                        '-- Yes! Return the item.
                        Return CType(mvarItems.Item(ThisIndex), %SINGLENAME%)
                    End If
                    
                ElseIf TypeOf Index Is String Then  '-- Key
                    
                    '-- Find the index given the key
                    Dim ThisIndex As Integer = mvarKeys.BinarySearch(LCase(CStr(Index)))
                    
                    '-- If valid, return the item.
                    If ThisIndex >= 0 Then
                        Return CType(mvarItems.Item(ThisIndex), %SINGLENAME%)
                    End If
                End If
            Catch e As Exception
                Err.Raise(ERR_ITEM_GET, "cfCollection.Item.Get", e.Message)
            End Try
        End Get
        
        Set '-- Set Property Handler
            Try
                If TypeOf Index Is Integer Then '-- Index
                    '-- Convert to integer
                    Dim ThisIndex As Integer = CInt(Index)
                    
                    '-- Adjust for Zero-based
                    If mvarZeroBased = False Then
                        ThisIndex = ThisIndex - 1
                    End If
                    
                    '-- Is this a valid index?
                    If ThisIndex > mvarItems.Count Then
                        '-- Yes. Set the value
                        mvarItems.Item(ThisIndex) = Value
                    End If
                    
                ElseIf TypeOf Index Is String Then  '-- Key
                    
                    '-- Get the index from the key
                    Dim ThisIndex As Integer = mvarKeys.BinarySearch(LCase(CStr(Index)))
                    
                    '-- Did we find it?
                    If ThisIndex >= 0 Then
                        '-- Yes! Set the value
                        mvarItems.Item(ThisIndex) = Value
                    End If
                End If
            Catch e As Exception
                Err.Raise(ERR_ITEM_SET, "cfCollection", e.Message)
            End Try
        End Set
    End Property
    
    '-- These two functions are required for For/Each
    Public Overloads ReadOnly Property GetEnumerator() As System.Collections.IEnumerator
        Get
            Return mvarItems.GetEnumerator
        End Get
    End Property
    Public Overloads ReadOnly Property GetEnumerator(ByVal Index As Integer, ByVal Count As Integer) As System.Collections.IEnumerator
        Get
            Return mvarItems.GetEnumerator(Index, Count)
        End Get
    End Property
    
    Public Sub New()
        mvarItems = New Collections.ArrayList()
        mvarKeys = New Collections.ArrayList()
    End Sub
    
    Protected Overrides Sub Finalize()
        mvarItems = Nothing
        mvarKeys = Nothing
    End Sub
    
End Class
