Namespace System
Namespace Xml
Namespace Serialization


Class XmlSerializer
	m_typeInfo As System.TypeInfo


	'----------------------------------------------------------------
	' シリアライズ ロジック
	'----------------------------------------------------------------

	Static Sub SerializeForBasicType( typeInfo As TypeInfo, memoryOffset As LONG_PTR, o As Object, doc As XmlDocument, parentNode As XmlNode )
		Dim valueStr = Nothing As String
		If typeInfo.FullName = "Byte" Then
			valueStr = Str$( GetByte( ObjPtr( o ) + memoryOffset ) As Byte )
		ElseIf typeInfo.FullName = "SByte" Then
			valueStr = Str$( GetByte( ObjPtr( o ) + memoryOffset ) As SByte )
		ElseIf typeInfo.FullName = "Word" Then
			valueStr = Str$( GetWord( ObjPtr( o ) + memoryOffset ) As Word )
		ElseIf typeInfo.FullName = "Integer" Then
			valueStr = Str$( GetWord( ObjPtr( o ) + memoryOffset ) As Integer )
		ElseIf typeInfo.FullName = "DWord" Then
			valueStr = Str$( GetDWord( ObjPtr( o ) + memoryOffset ) As DWord )
		ElseIf typeInfo.FullName = "Long" Then
			valueStr = Str$( GetDWord( ObjPtr( o ) + memoryOffset ) As Long )
		ElseIf typeInfo.FullName = "QWord" Then
			valueStr = Str$( GetQWord( ObjPtr( o ) + memoryOffset ) As QWord )
		ElseIf typeInfo.FullName = "Int64" Then
			valueStr = Str$( GetQWord( ObjPtr( o ) + memoryOffset ) As Int64 )
		ElseIf typeInfo.FullName = "Boolean" Then
			If GetByte( ObjPtr( o ) + memoryOffset ) As Boolean Then
				valueStr = "True"
			Else
				valueStr = "False"
			End If
		ElseIf typeInfo.FullName = "Single" Then
			valueStr = Str$( GetSingle( ObjPtr( o ) + memoryOffset ) As Single )
		ElseIf typeInfo.FullName = "Double" Then
			valueStr = Str$( GetDouble( ObjPtr( o ) + memoryOffset ) As Double )
		ElseIf typeInfo.FullName = "VoidPtr" Then
			valueStr = Str$( Get_LONG_PTR( ObjPtr( o ) + memoryOffset ) )
		Else
			Throw
		End If

		Dim childNode = doc.CreateElement( typeInfo.FullName )
		parentNode.AppendChild( childNode )
		childNode.AppendChild( doc.CreateTextNode( valueStr ) )
	End Sub

	Static Sub SerializeForClass( typeInfo As TypeInfo, o As Object, doc As XmlDocument, parentNode = Nothing As XmlNode )
		If typeInfo.IsClass() and typeInfo.FullName = "System.Object" Then
			' Object型はシリアライズしない
			Return
		End If

		If ActiveBasic.IsNothing( parentNode ) Then
			parentNode = doc
		End If

		Dim memberInfos = typeInfo.GetMembers()

		' 子ノードを生成
		Dim childNode = doc.CreateElement( typeInfo.Name )
		Dim namespaceAttr = doc.CreateAttribute( "namespace" )
		namespaceAttr.Value = typeInfo.Namespace
		childNode.Attributes.Add( namespaceAttr )

		' 親ノードへ追加
		parentNode.AppendChild( childNode )

		If Not ActiveBasic.IsNothing( typeInfo.BaseType ) Then
			' 基底クラスが存在するとき

			' 基底クラスをシリアライズ
			SerializeForClass( typeInfo.BaseType, o, doc, childNode )
		End If

		' メンバをシリアライズ
		Foreach memberInfo In memberInfos
			If memberInfo.MemberType.IsPointer() Then
				Throw
			ElseIf memberInfo.MemberType.IsClass() Then
				SerializeForClass( memberInfo.MemberType, o, doc, childNode )
			ElseIf memberInfo.MemberType.IsValueType() Then
				SerializeForBasicType( memberInfo.MemberType, memberInfo._System_Offset, o, doc, childNode )
			End If
		Next
	End Sub


	'----------------------------------------------------------------
	' デシリアライズ ロジック
	'----------------------------------------------------------------

	Static Sub DeserializeForBasicType( typeInfo As TypeInfo, memoryOffset As LONG_PTR, object As Object, xmlNode As XmlNode )
		If Not xmlNode.ChildNodes.Count = 1 Then
			Throw
		End If

		Dim valueStr = xmlNode.ChildNodes[0].Value
		Dim memberPtr = ObjPtr( object ) + memoryOffset

		If typeInfo.FullName = "Byte" Then
			SetByte( memberPtr, Val( valueStr ) )
		ElseIf typeInfo.FullName = "SByte" Then
			SetByte( memberPtr, Val( valueStr ) )
		ElseIf typeInfo.FullName = "Word" Then
			SetWord( memberPtr, Val( valueStr ) )
		ElseIf typeInfo.FullName = "Integer" Then
			SetWord( memberPtr, Val( valueStr ) )
		ElseIf typeInfo.FullName = "DWord" Then
			SetDWord( memberPtr, Val( valueStr ) )
		ElseIf typeInfo.FullName = "Long" Then
			SetDWord( memberPtr, Val( valueStr ) )
		ElseIf typeInfo.FullName = "QWord" Then
			SetQWord( memberPtr, Val( valueStr ) )
		ElseIf typeInfo.FullName = "Int64" Then
			SetQWord( memberPtr, Val( valueStr ) )
		ElseIf typeInfo.FullName = "Boolean" Then
			If valueStr = "True" Then
				SetByte( memberPtr, 1 )
			ElseIf valueStr = "False" Then
				SetByte( memberPtr, 0 )
			Else
				Throw
			End If
		ElseIf typeInfo.FullName = "Single" Then
			SetSingle( memberPtr, Val( valueStr ) )
		ElseIf typeInfo.FullName = "Double" Then
			SetDouble( memberPtr, Val( valueStr ) )
		ElseIf typeInfo.FullName = "VoidPtr" Then
			Set_LONG_PTR( memberPtr, Val( valueStr ) As LONG_PTR )
		Else
			Throw
		End If
	End Sub

	Function DeserializeForClass( typeInfo As TypeInfo, xmlNode As XmlNode ) As Object
		If typeInfo.IsClass() and typeInfo.FullName = "System.Object" Then
			' Object型にたどり着いた場合

			' 派生クラスのフルネームを元に特定されたクラスを生成し、デフォルトコンストラクタを呼ぶ
			Return ActiveBasic.Core._System_TypeForClass._System_New( m_typeInfo As ActiveBasic.Core._System_TypeForClass )
		End If

		If typeInfo.Name <> xmlNode.LocalName Then
			Throw
		End If

		Dim object = Nothing As Object
		Dim memberInfos = typeInfo.GetMembers()

		If Not ActiveBasic.IsNothing( typeInfo.BaseType ) Then
			' 基底クラスが存在するとき

			' 基底クラスをシリアライズ
			object = DeserializeForClass( typeInfo.BaseType, xmlNode )
		End If

		' メンバをシリアライズ
		Dim childNode = xmlNode.ChildNodes[0] As XmlNode
		Foreach memberInfo In memberInfos
			If ActiveBasic.IsNothing( childNode ) Then
				Throw
			End If

			If memberInfo.MemberType.IsPointer() Then
				Throw
			ElseIf memberInfo.MemberType.IsClass() Then
				Dim memberObject = DeserializeForClass( memberInfo.MemberType, childNode )
				Set_LONG_PTR( ObjPtr( object ) + memberInfo._System_Offset, ObjPtr( memberObject ) As LONG_PTR )
			ElseIf memberInfo.MemberType.IsValueType() Then
				DeserializeForBasicType( memberInfo.MemberType, memberInfo._System_Offset, object, childNode )
			End If

			childNode = childNode.NextSibling
		Next

		If Not ActiveBasic.IsNothing( childNode ) Then
			Throw
		End If

		Return object
	End Function

Public
	Sub XmlSerializer( typeInfo As System.TypeInfo )
		This.m_typeInfo = typeInfo
	End Sub
	Sub ~XmlSerializer()
	End Sub


	/*!
	@brief	XML文書をオブジェクトに逆シリアライズします。
	@author	Daisuke Yamamoto
	*/
	Function Deserialize( stream As System.IO.Stream ) As Object
		Dim doc = New XmlDocument
		doc.Load( stream )
		Return DeserializeForClass( m_typeInfo, doc.ChildNodes[1] )
	End Function

	/*!
	@brief	オブジェクトをXML文書にシリアライズします。
	@author	Daisuke Yamamoto
	*/
	Sub Serialize( stream As System.IO.Stream, o As Object )
		Dim doc = New XmlDocument
		doc.AppendChild( doc.CreateXmlDeclaration( "1.0", "shift-jis", Nothing ) )

		SerializeForClass( m_typeInfo, o, doc )

		' ストリームに保存
		doc.Save( stream )
	End Sub
End Class


End Namespace
End Namespace
End Namespace
