''' <summary>
''' Represents a collection of ingredients where the (type of) an ingredient is the key
''' and the amount of that ingredient the value.
''' </summary>
Public Class Ingredients
Implements IDictionary(Of IIngredient, Integer)
Private Const ErrorMessage As String = "Adding or removing the specified amount of ingredients will result in a negative. Ingredients not added."
' The innerDictionary has a ChangeableDictionaryValue(Of Integer) as value
' because the value of a KeyValuePair cannot be changed once it is assigned.
Private _innerDictionary As Dictionary(Of IIngredient, ChangeableDictionaryValue(Of Integer))
''' <summary>
''' Instantiates a new cult with no members.
''' </summary>
Public Sub New()
MyBase.New()
_innerDictionary = New Dictionary(Of IIngredient, ChangeableDictionaryValue(Of Integer))
End Sub
''' <summary>
''' Returns a KeyValuePair of the _innerDictionary given a key.
''' The KeyValuePair is selected by type of the key rather than its spot in memory.
''' </summary>
''' <param name="key">An ingredient to look for.</param>
''' <returns>A KeyValuePair where the type of the key is the same as the type of the key parameter.</returns>
Private Function GetPairByKeyType(ByVal key As IIngredient) As KeyValuePair(Of IIngredient, ChangeableDictionaryValue(Of Integer))
Dim pair As KeyValuePair(Of IIngredient, ChangeableDictionaryValue(Of Integer)) = _innerDictionary.Where(Function(p) p.Key.GetType = key.GetType).FirstOrDefault
If pair.Key Is Nothing Then
Throw New KeyNotFoundException
Else
Return pair
End If
End Function
''' <summary>
''' Adds the specified amount of the ingredient.
''' </summary>
''' <param name="key">The ingredient to add.</param>
''' <param name="value">The amount to add of the specified ingredient.</param>
Public Overridable Sub Add(ByVal key As IIngredient, ByVal value As Integer) Implements System.Collections.Generic.IDictionary(Of IIngredient, Integer).Add
Add(New KeyValuePair(Of IIngredient, Integer)(key, value))
End Sub
''' <summary>
''' Adds the KeyValuePair to the _innerDictionary.
''' </summary>
''' <param name="item">The KeyValuePair to add.</param>
Private Sub Add(ByVal item As System.Collections.Generic.KeyValuePair(Of IIngredient, Integer)) Implements System.Collections.Generic.ICollection(Of System.Collections.Generic.KeyValuePair(Of IIngredient, Integer)).Add
' If the key already exists then do not add a new KeyValuePair.
' Add the specified amount to the already existing KeyValuePair instead.
If Me.ContainsKey(item.Key) Then
If Me(item.Key) + item.Value < 0 Then
Throw New InvalidOperationException(ErrorMessage)
Else
Me(item.Key) += item.Value
End If
Else
If item.Value < 0 Then
Throw New InvalidOperationException(ErrorMessage)
Else
_innerDictionary.Add(item.Key, New ChangeableDictionaryValue(Of Integer)(item.Value))
End If
End If
End Sub
''' <summary>
''' Removes an ingredient.
''' </summary>
''' <param name="key">The ingredient to remove.</param>
''' <returns>True if the key was found and successfully removed.</returns>
Public Overridable Function Remove(ByVal key As IIngredient) As Boolean Implements System.Collections.Generic.IDictionary(Of IIngredient, Integer).Remove
If Me.ContainsKey(key) Then
Return _innerDictionary.Remove(GetPairByKeyType(key).Key)
Else
Return False
End If
End Function
''' <summary>
''' Removes the specified value from the ingredient of the key.
''' Removes by checking the type of the key rather than pointer.
''' </summary>
''' <param name="item">The KeyValuePair to remove.</param>
''' <returns>True if the key was found and successfully removed.</returns>
Private Function Remove(ByVal item As System.Collections.Generic.KeyValuePair(Of IIngredient, Integer)) As Boolean Implements System.Collections.Generic.ICollection(Of System.Collections.Generic.KeyValuePair(Of IIngredient, Integer)).Remove
Me.Add(item.Key, -item.Value)
Return True
End Function
''' <summary>
''' Clears the dictionary of ingredients.
''' </summary>
Public Sub Clear() Implements System.Collections.Generic.ICollection(Of System.Collections.Generic.KeyValuePair(Of IIngredient, Integer)).Clear
_innerDictionary.Clear()
End Sub
''' <summary>
''' Checks if the specified amount of the key ingredient is present in the dictionary.
''' </summary>
''' <param name="ingredient">The key ingredient to check for presence.</param>
''' <param name="amount">The amount of the ingredient to check for presence.</param>
''' <returns>True if the specified amount of the given key ingredient is present in the dictionary.</returns>
Public Function Contains(ByVal ingredient As IIngredient, ByVal amount As Integer) As Boolean
Return Me.Contains(New KeyValuePair(Of IIngredient, Integer)(ingredient, amount))
End Function
''' <summary>
''' Checks if the specified KeyValuePair is present in the dictionary.
''' Does not check pointer, but if the amount of the specified key type is present.
''' </summary>
''' <param name="item">The KeyValuePair containing the key ingredient and amount to check.</param>
''' <returns>True if the specified amount of the given key ingredient is present in the dictionary.</returns>
Private Function Contains(ByVal item As System.Collections.Generic.KeyValuePair(Of IIngredient, Integer)) As Boolean Implements System.Collections.Generic.ICollection(Of System.Collections.Generic.KeyValuePair(Of IIngredient, Integer)).Contains
If Me.ContainsKey(item.Key) Then
Return GetPairByKeyType(item.Key).Value.Value >= item.Value
Else
Return False
End If
End Function
''' <summary>
''' Checks if an ingredient with the same type as the specified key is already present in the dictionary.
''' </summary>
''' <param name="key">The key ingredient to check for presence.</param>
''' <returns>True if a type of the specified ingredient is present in the dictionary.</returns>
Public Function ContainsKey(ByVal key As IIngredient) As Boolean Implements System.Collections.Generic.IDictionary(Of IIngredient, Integer).ContainsKey
Dim pair As KeyValuePair(Of IIngredient, ChangeableDictionaryValue(Of Integer)) = _innerDictionary.Where(Function(p) p.Key.GetType = key.GetType).FirstOrDefault
If pair.Key Is Nothing Then
Return False
Else
Return True
End If
End Function
''' <summary>
''' Gets the amount of an ingredient if the ingredient exists.
''' </summary>
''' <param name="key">The ingredient to get the amount of.</param>
''' <param name="value">When this method returns, contains the value associated with the specified key, if the key is found; otherwise, the default value for the type of the value parameter.</param>
''' <returns>True if the key exists and the value was found.</returns>
Public Function TryGetValue(ByVal key As IIngredient, ByRef value As Integer) As Boolean Implements System.Collections.Generic.IDictionary(Of IIngredient, Integer).TryGetValue
If Me.ContainsKey(key) Then
value = GetPairByKeyType(key).Value.Value
Return True
Else
value = Nothing
Return False
End If
End Function
''' <summary>
''' Gets or sets the amount associated with the specified ingredient.
''' </summary>
''' <param name="key">An ingredient to get or set the amount of.</param>
''' <value>The amount of the ingredient to set.</value>
''' <returns>The amount of the specified ingredient.</returns>
Default Public Overridable Property Item(ByVal key As IIngredient) As Integer Implements System.Collections.Generic.IDictionary(Of IIngredient, Integer).Item
Get
Return GetPairByKeyType(key).Value.Value
End Get
Set(ByVal value As Integer)
If value < 0 Then
Throw New InvalidOperationException(ErrorMessage)
Else
GetPairByKeyType(key).Value.Value = value
End If
End Set
End Property
''' <summary>
''' Gets a collection containing all ingredients in the dictionary.
''' </summary>
''' <returns>A collection of ingredients.</returns>
Public ReadOnly Property Keys As System.Collections.Generic.ICollection(Of IIngredient) Implements System.Collections.Generic.IDictionary(Of IIngredient, Integer).Keys
Get
Return _innerDictionary.Keys
End Get
End Property
''' <summary>
''' Gets a collection containing all amounts of ingredients in the dictionary.
''' </summary>
''' <returns>A collection of integers.</returns>
Public ReadOnly Property Values As System.Collections.Generic.ICollection(Of Integer) Implements System.Collections.Generic.IDictionary(Of IIngredient, Integer).Values
Get
Return InnerDictToIntValue.Values
End Get
End Property
''' <summary>
''' Gets the amount of unique ingredients in the dictionary.
''' </summary>
''' <returns>The amount of unique ingredients in the dictionary.</returns>
Public ReadOnly Property Count As Integer Implements System.Collections.Generic.ICollection(Of System.Collections.Generic.KeyValuePair(Of IIngredient, Integer)).Count
Get
Return _innerDictionary.Count
End Get
End Property
''' <summary>
''' Returns a value that specifies wether the dictionary is read only.
''' </summary>
''' <returns>True if the dictionary is read only.</returns>
Public ReadOnly Property IsReadOnly As Boolean Implements System.Collections.Generic.ICollection(Of System.Collections.Generic.KeyValuePair(Of IIngredient, Integer)).IsReadOnly
Get
Return False
End Get
End Property
''' <summary>
''' Converts the _innerDictionary to a new dictionary which values cannot be changed.
''' </summary>
''' <returns>A new dictionary containing all keys and values of the _innerDictionary.</returns>
Private Function InnerDictToIntValue() As Dictionary(Of IIngredient, Integer)
Dim dict As New Dictionary(Of IIngredient, Integer)
' Put all KeyValuePairs of the _innerDictionary in the new dictionary using a one-line LINQ query.
_innerDictionary.ToList.ForEach(Sub(pair) dict.Add(pair.Key, pair.Value.Value))
Return dict
End Function
''' <summary>
''' Copies the elements of the Dictionary to an Array, starting at a particular Array index.
''' </summary>
''' <param name="array">The one-dimensional Array that is the destination of the elements copied from the Dictionary. The Array must have zero-based indexing.</param>
''' <param name="arrayIndex">The zero-based index in array at which copying begins.</param>
Private Sub CopyTo(ByVal array() As System.Collections.Generic.KeyValuePair(Of IIngredient, Integer), ByVal arrayIndex As Integer) Implements System.Collections.Generic.ICollection(Of System.Collections.Generic.KeyValuePair(Of IIngredient, Integer)).CopyTo
' We do not want to implement this Method ourselves. The _innerDictionary already has it implemented.
' This is a private Method of the _innerDictionary though, so we first have to cast it to an IDictionary.
' Remember that the _innerDictionary has a ChangeableValue while we Implement an IDictionary with an Integer as value.
' First convert the _innerDictionary to a Dictionary with an Integer as value.
DirectCast(InnerDictToIntValue(), IDictionary(Of IIngredient, Integer)).CopyTo(array, arrayIndex)
End Sub
''' <summary>
''' Returns an Enumerator that iterates through the ingredients and their amounts.
''' </summary>
''' <returns>An Enumerator for ingredients.</returns>
Public Function GetEnumerator() As System.Collections.Generic.IEnumerator(Of System.Collections.Generic.KeyValuePair(Of IIngredient, Integer)) Implements System.Collections.Generic.IEnumerable(Of System.Collections.Generic.KeyValuePair(Of IIngredient, Integer)).GetEnumerator
' First convert the _innerDictionary to a Dictionary having an Integer as value.
Return InnerDictToIntValue.GetEnumerator
End Function
''' <summary>
''' Returns an Enumerator that iterates through the ingredients and their amounts.
''' </summary>
''' <returns>An Enumerator for ingredients.</returns>
Private Function GetEnumerator1() As System.Collections.IEnumerator Implements System.Collections.IEnumerable.GetEnumerator
Return Me.GetEnumerator
End Function
End Class