Files
ayanova7/source/csla10/CSLA/UndoableBase.vb
2018-06-29 19:47:36 +00:00

355 lines
12 KiB
VB.net

Option Strict On
Imports System.Reflection
Imports System.IO
Imports System.Runtime.Serialization
Imports System.Runtime.Serialization.Formatters.Binary
''' <summary>
'''
''' </summary>
Namespace Core
''' <summary>
''' Implements n-level undo capabilities.
''' </summary>
''' <remarks>
''' You should not directly derive from this class. Your
''' business classes should derive from
''' <see cref="CSLA.BusinessBase" />.
''' </remarks>
<Serializable()> _
Public MustInherit Class UndoableBase
Inherits CSLA.Core.BindableBase
' keep a stack of object state values
<NotUndoable()> _
Private mStateStack As New Stack
' variables containing type info for comparisons
Private Shared UndoableType As Type = GetType(UndoableBase)
Private Shared BusinessType As Type = GetType(BusinessBase)
Private Shared CollectionType As Type = GetType(BusinessCollectionBase)
''' <summary>
''' Returns the current edit level of the object.
''' </summary>
Protected ReadOnly Property EditLevel() As Integer
Get
Return mStateStack.Count
End Get
End Property
''' <summary>
''' Copies the state of the object and places the copy
''' onto the state stack.
''' </summary>
Protected Friend Sub CopyState()
Dim currentType As Type = Me.GetType
Dim state As New Hashtable()
Dim fields() As FieldInfo
Dim field As FieldInfo
Dim fieldName As String
Debug.WriteLine("CopyState" & currentType.ToString() & " EditLevel:" & Me.EditLevel.ToString())
Do
' get the list of fields in this type
fields = currentType.GetFields( _
BindingFlags.NonPublic Or _
BindingFlags.Instance Or _
BindingFlags.Public)
For Each field In fields
' make sure we process only our variables
If field.DeclaringType Is currentType Then
' see if this field is marked as not undoable
If Not NotUndoableField(field) Then
' the field is undoable, so it needs to be processed
Dim value As Object = field.GetValue(Me)
If TypeInheritsFrom(field.FieldType, CollectionType) Then
' make sure the variable has a value
If Not value Is Nothing Then
' this is a child collection, cascade the call
CType(value, BusinessCollectionBase).CopyState()
End If
ElseIf TypeInheritsFrom(field.FieldType, BusinessType) Then
' make sure the variable has a value
If Not value Is Nothing Then
' this is a child object, cascade the call
CType(value, BusinessBase).CopyState()
End If
Else
' this is a normal field, simply trap the value
fieldName = field.DeclaringType.Name & "!" & field.Name
state.Add(fieldName, value)
End If
End If
End If
Next
currentType = currentType.BaseType
Loop Until currentType Is UndoableType
' serialize the state and stack it
Dim buffer As New MemoryStream()
Dim formatter As New BinaryFormatter()
formatter.Serialize(buffer, state)
mStateStack.Push(buffer.ToArray)
End Sub
''' <summary>
''' Restores the object's state to the most recently
''' copied values from the state stack.
''' </summary>
''' <remarks>
''' Restores the state of the object to its
''' previous value by taking the data out of
''' the stack and restoring it into the fields
''' of the object.
''' </remarks>
Protected Friend Sub UndoChanges()
' if we are a child object we might be asked to
' undo below the level where we stacked states,
' so just do nothing in that case
If EditLevel > 0 Then
Dim buffer As New MemoryStream(CType(mStateStack.Pop(), Byte()))
buffer.Position = 0
Dim formatter As New BinaryFormatter()
Dim state As Hashtable = CType(formatter.Deserialize(buffer), Hashtable)
Dim currentType As Type = Me.GetType
Dim fields() As FieldInfo
Dim field As FieldInfo
Dim fieldName As String
Do
' get the list of fields in this type
fields = currentType.GetFields( _
BindingFlags.NonPublic Or _
BindingFlags.Instance Or _
BindingFlags.Public)
For Each field In fields
If field.DeclaringType Is currentType Then
' see if the field is undoable or not
If Not NotUndoableField(field) Then
' the field is undoable, so restore its value
If TypeInheritsFrom(field.FieldType, CollectionType) Then
' this is a child collection, cascade the call
CType(field.GetValue(Me), BusinessCollectionBase).UndoChanges()
ElseIf TypeInheritsFrom(field.FieldType, BusinessType) Then
' this is a child object, cascade the call
CType(field.GetValue(Me), BusinessBase).UndoChanges()
Else
' this is a regular field, restore its value
fieldName = field.DeclaringType.Name & "!" & field.Name
field.SetValue(Me, state.Item(fieldName))
End If
End If
End If
Next
currentType = currentType.BaseType
Loop Until currentType Is UndoableType
End If
End Sub
''' <summary>
''' Accepts any changes made to the object since the last
''' state copy was made.
''' </summary>
''' <remarks>
''' The most recent state copy is removed from the state
''' stack and discarded, thus committing any changes made
''' to the object's state.
''' </remarks>
Protected Friend Sub AcceptChanges()
If EditLevel > 0 Then
mStateStack.Pop()
Dim currentType As Type = Me.GetType
Dim fields() As FieldInfo
Dim field As FieldInfo
'Dim fieldName As String
Do
' get the list of fields in this type
fields = currentType.GetFields( _
BindingFlags.NonPublic Or _
BindingFlags.Instance Or _
BindingFlags.Public)
For Each field In fields
If field.DeclaringType Is currentType Then
' see if the field is undoable or not
If Not NotUndoableField(field) Then
' the field is undoable so see if it is a collection
If TypeInheritsFrom(field.FieldType, CollectionType) Then
' it is a collection so cascade the call
CType(field.GetValue(Me), BusinessCollectionBase).AcceptChanges()
ElseIf TypeInheritsFrom(field.FieldType, BusinessType) Then
' it is a child object so cascade the call
' JOHN AYANOVA CUSTOMIZED: Added check if is not nothing
' below because some objects have uninitialized children
'(workorder)
If Not field.GetValue(Me) Is Nothing Then
CType(field.GetValue(Me), BusinessBase).AcceptChanges()
End If
End If
End If
End If
Next
currentType = currentType.BaseType
Loop Until currentType Is UndoableType
End If
End Sub
#Region " Helper Functions "
Private Function NotUndoableField(ByVal Field As FieldInfo) As Boolean
Return Attribute.IsDefined(Field, GetType(NotUndoableAttribute))
'' get a list of all NotUndoableAttributes for this field
'Dim attribs() As Object = _
' Field.GetCustomAttributes(GetType(NotUndoableAttribute), True)
'' return True if any NotUndoableAttributes exist on this field
'Return (UBound(attribs) > -1)
End Function
Private Function TypeInheritsFrom( _
ByVal TypeToCheck As Type, ByVal CheckAgainst As Type) As Boolean
Dim base As Type = TypeToCheck
' scan up through the inheritance hierarchy, checking each
' class to see if it is the one we're looking for
While Not base.BaseType Is Nothing
' if we find the target class return True
If base Is CheckAgainst Then Return True
base = base.BaseType
End While
' the target class is not in the inheritance hierarchy so
' return False
Return False
End Function
#End Region
#Region " DumpState "
''' <summary>
''' Writes the object's field data into the debugger
''' output window in VS.NET.
''' </summary>
Public Sub DumpState()
Dim currentType As Type = Me.GetType
Dim state As New Hashtable()
Dim field As FieldInfo
Dim fieldName As String
Dim fields() As FieldInfo
Debug.IndentSize = 2
Debug.WriteLine("OBJECT " & currentType.Name)
Debug.WriteLine("UndoableBase!EditLevel: " & EditLevel)
Do
' get the list of fields in this type
fields = currentType.GetFields( _
BindingFlags.NonPublic Or _
BindingFlags.Instance Or _
BindingFlags.Public)
For Each field In fields
If field.DeclaringType Is currentType Then
fieldName = field.DeclaringType.Name & "!" & field.Name
' see if this field is marked as not undoable
If Not NotUndoableField(field) Then
' the field is undoable, so it needs to be processed
If TypeInheritsFrom(field.FieldType, CollectionType) Then
' this is a child collection, cascade the call
Debug.Indent()
Debug.WriteLine("COLLECTION " & fieldName)
If Not field.GetValue(Me) Is Nothing Then
CType(field.GetValue(Me), BusinessCollectionBase).DumpState()
Else
Debug.WriteLine("<Nothing>")
End If
Debug.Unindent()
ElseIf TypeInheritsFrom(field.FieldType, BusinessType) Then
' this is a child object, cascade the call
Debug.Indent()
Debug.WriteLine("CHILD OBJECT " & fieldName)
If Not field.GetValue(Me) Is Nothing Then
CType(field.GetValue(Me), BusinessBase).DumpState()
Else
Debug.WriteLine("<Nothing>")
End If
Debug.Unindent()
Else
' this is a normal field, simply trap the value
If Not field.GetValue(Me) Is Nothing Then
Debug.WriteLine(fieldName & ": " & field.GetValue(Me).ToString)
Else
Debug.WriteLine(fieldName & ": <Nothing>")
End If
End If
Else
' field is not undoable
If Not field.GetValue(Me) Is Nothing Then
Debug.WriteLine("<NotUndoable()> " & fieldName & ": " & field.GetValue(Me).ToString)
Else
Debug.WriteLine("<NotUndoable()> " & fieldName & ": <Nothing>")
End If
End If
End If
Next
currentType = currentType.BaseType
Loop Until currentType Is UndoableType
End Sub
#End Region
End Class
End Namespace