Namespace System
Namespace Xml

Enum XmlNodeType
	None
	Element
	Attribute
	Text
	CDATA
	EntifyReference
	Entity
	ProcessingInstruction
	Comment
	Document
	DocumentType
	DocumentFragment
	Notation
	Whitespace
	SignificantWhitespace
	EndElement
	EndEntity
	XmlDeclaration
End Enum

/*!
@brief	XMLドキュメント内の単一のノードを表す。
*/
Class XmlNode

	nodeType As XmlNodeType
	attributes As XmlAttributeCollection
	childNodes As XmlNodeList
	prefix As String
	localName As String
	namespaceURI As String
	ownerDocument As XmlDocument
	value As String
	previousSibling As XmlNode
	nextSibling As XmlNode

Public

	/*!
	@brief	コンストラクタ
	*/
	Sub XmlNode( nodeType As XmlNodeType, prefix As String, localName As String, namespaceURI As String, doc As XmlDocument )
		This.nodeType = nodeType
		This.prefix = prefix
		This.localName = localName
		This.namespaceURI = namespaceURI
		This.ownerDocument = doc
		This.value = Nothing
		This.previousSibling = Nothing
		This.nextSibling = Nothing

		attributes = New XmlAttributeCollection
		childNodes = New XmlNodeList()
	End Sub

	/*!
	@brief	コンストラクタ
	*/
	Sub XmlNode( nodeType As XmlNodeType, data As String, doc As XmlDocument )
		This.nodeType = nodeType
		This.prefix = Nothing
		This.localName = Nothing
		This.namespaceURI = Nothing
		This.ownerDocument = doc
		This.value = data
		This.previousSibling = Nothing
		This.nextSibling = Nothing

		attributes = New XmlAttributeCollection
		childNodes = Nothing
	End Sub


	'----------------------------------------------------------------
	' パブリック プロパティ
	'----------------------------------------------------------------

	/*!
	@brief	名前またはインデックスによってアクセスできる属性のコレクションを表します。
	*/
	Virtual Function Attributes() As XmlAttributeCollection
		Return attributes
	End Function

	/*!
	@brief	子ノードリストを返します。
	*/
	Virtual Function ChildNodes() As XmlNodeList
		Return childNodes
	End Function

	/*!
	@brief	ノードの最初の子を取得します。
	*/
	Virtual Function FirstChild() As XmlNode
		If childNodes.Count = 0 Then
			' 子ノードが1つもないときはNothingを返す
			Return Nothing
		End If
		Return childNodes[0]
	End Function

	/*!
	@brief	子ノードが1つ以上あるかどうかを取得します。
	*/
	Virtual Function HasChildNodes() As Boolean
		Return Not ( childNodes.Count = 0 )
	End Function

	/*!
	@brief	このノードの子ノードだけを表すマークアップを取得または設定します。
	*/
	Virtual Function InnerXml() As String
		Return InnerXmlSupportedIndent()
	End Function

	/*!
	@brief	ノードの最後の子を取得します。
	*/
	Virtual Function LastChild() As XmlNode
		If childNodes.Count = 0 Then
			' 子ノードが1つもないときはNothingを返す
			Return Nothing
		End If
		Return childNodes[childNodes.Count-1]
	End Function

	/*!
	@brief	ノードのローカル名を取得します。 
	@return	ノードのローカル名。
	*/
	Virtual Function LocalName() As String
		return localName
	End Function

	/*!
	@brief	このノードの名前空間 URI を取得します。
	@return	このノードの名前空間 URI。
	*/
	Virtual Function NamespaceURI() As String
		return NamespaceURI
	End Function

	/*!
	@brief	このノードの直後のノードを取得します。
	@return	このノードの直後のノード。
	*/
	Virtual Function NextSibling() As XmlNode
		Return nextSibling
	End Function

	/*!
	@brief	このノードのノードタイプを取得します。
	@return	このノードのノードタイプ。
	*/
	Virtual Function NodeType() As XmlNodeType
		return nodeType
	End Function

	/*!
	@brief	このノードとそのすべての子ノードを表すマークアップを取得します。
	@return	このノードとそのすべての子ノードを格納しているマークアップ。
	*/
	Virtual Function OuterXml() As String
		Return OwnerXmlSupportedIndent()
	End Function

	/*!
	@brief	このノードが属する XmlDocument を取得します。
	@return	このノードが属する XmlDocument。
	*/
	Virtual Function OwnerDocument() As XmlDocument
		Return ownerDocument
	End Function

	/*!
	@brief	このノードの名前空間プリフィックスを取得または設定します。
	@return	このノードの名前空間プリフィックス。たとえば、プリフィックスは要素 <bk:book> の bk です。プリフィックスがない場合、このプロパティは String.Empty を返します。
	*/
	Virtual Function Prefix() As String
		return prefix
	End Function

	/*!
	@brief	このノードの直前のノードを取得します。
	@return	このノードの直前のノード。
	*/
	Virtual Function PreviousSibling() As XmlNode
		Return previousSibling
	End Function

	/*!
	@brief	このノードの値を取得します。
	@return	このノードの値。
	*/
	Virtual Function Value() As String
		return value
	End Function

	/*!
	@brief	このノードの値を設定します。
	@param	value 値文字列。
	*/
	Virtual Sub Value( value As String )
		This.value = value
	End Sub


	'----------------------------------------------------------------
	' パブリック メソッド
	'----------------------------------------------------------------

	/*!
	@brief	このノードの子ノードリストの末尾に指定したノードを追加する。
	*/
	Virtual Function AppendChild( newChild As XmlNode ) As XmlNode
		Dim lastChild = This.LastChild
		childNodes.Add( newChild )

		If Not ActiveBasic.IsNothing( lastChild ) Then
			' 前後の兄弟要素を指定
			lastChild.nextSibling = newChild
			newChild.previousSibling = lastChild
		End If

		Return newChild
	End Function

	/*!
	@brief	ノードを複製する。
	*/
	Virtual Function Clone() As XmlNode
		' TODO: 実装
	End Function

	/*!
	@brief	派生クラスでオーバーライドされた場合は、ノードの複製を作成する。
	*/
	Virtual Function CloneNode( deep As Boolean ) As XmlNode
		' TODO: 実装
	End Function

	/*!
	@brief	XmlNode のノードに対する Foreachスタイルの反復をサポートします。
	*/
	Virtual Function GetEnumerator() As IEnumerator
		' TODO: 実装
	End Function

	/*!
	@brief	指定したノードを指定した参照ノードの直後に挿入します。
	@param	newChild 挿入するXmlNode。
			refChild 参照ノードであるXmlNode。newNode は、refNode の後に配置されます。
	@return	挿入されるノード。
	*/
	Virtual Function InsertAfter( newChild As XmlNode, refChild As XmlNode ) As XmlNode
		' TODO: 実装
	End Function

	/*!
	@brief	指定したノードを指定した参照ノードの直前に挿入します。
	@param	newChild 挿入するXmlNode。
			refChild 参照ノードであるXmlNode。newNode は、refNode の前に配置されます。
	@return	挿入されるノード。
	*/
	Virtual Function InsertBefore( newChild As XmlNode, refChild As XmlNode ) As XmlNode
		' TODO: 実装
	End Function

	/*!
	@brief	このノードの子ノードのリストの先頭に、指定したノードを追加します。
	@param	newChild 挿入するXmlNode。
	@return	挿入されるノード。
	*/
	Virtual Function PrependChild( newChild As XmlNode ) As XmlNode
		' TODO: 実装
	End Function

	/*!
	@brief	現在のノードのすべての子ノードと属性の両方、またはそのいずれかを削除します。
	*/
	Virtual Sub RemoveAll()
		If Not ActiveBasic.IsNothing( This.attributes ) Then
			This.attributes.Clear()
		End If
		If Not ActiveBasic.IsNothing( This.childNodes ) Then
			This.childNodes.Clear()
		End If
	End Sub

	/*!
	@brief	現在のノードのすべての子ノードを削除します。
	@param	oldChild 削除するXmlNode。
	@return	削除されたノード。
	*/
	Virtual Function RemoveChild( oldChild As XmlNode ) As XmlNode
		childNodes.Remove( oldChild )
	End Function

	/*!
	@brief	子ノード oldChild を newChild ノードに置き換えます。
	@param	newChild 新しいノード。
			oldChild 置換されノード。
	@return	置き換えられたノード。
	*/
	Virtual Function ReplaceChild( newChild As XmlNode, oldChild As XmlNode ) As XmlNode
		' TODO: 実装
	End Function


Protected

	Function GetAttributesStr() As String
		If attributes.Count = 0 Then
			' 属性が1つもない場合
			Return ""
		End If

		Dim result = ""
		Foreach attribute In attributes
			result += " " + attribute.OuterXml
		Next
		Return result
	End Function

	Virtual Function InnerXmlSupportedIndent( isIndent = False As Boolean, indent = 0 As Long ) As String
		Dim result = ""
		If Not ActiveBasic.IsNothing( childNodes ) Then
			' 子ノード
			Foreach childNode In childNodes
				result += childNode.OwnerXmlSupportedIndent( isIndent, indent )
			Next
		End If
		Return result
	End Function

	Virtual Function OwnerXmlSupportedIndent( isIndent = False As Boolean, indent = 0 As Long ) As String
		Dim indentStr = ""
		Dim crlfStr = ""
		If isIndent Then
			' インデントをサポートする場合
			Dim i As Long
			For i=0 To ELM(indent)
				indentStr += Ex"\t"
			Next

			crlfStr = Ex"\r\n"
		End If

		If This.childNodes.Count = 0 Then
			Return indentStr + "<" + localName + This.GetAttributesStr() + " />" + crlfStr
		End If

		Dim result = ""

		If This.FirstChild.NodeType = XmlNodeType.Text Then
			' 子ノードがテキストのときは開始タグ、終了タグの間にインデントや改行を入れない

			' 開始タグ
			result += indentStr + "<" + localName + This.GetAttributesStr() + ">"

			' 子ノードリスト
			result += InnerXmlSupportedIndent( isIndent, indent + 1 )

			' 終了タグ
			result += "</" + localName + ">" + crlfStr
		Else
			' 開始タグ
			result += indentStr + "<" + localName + This.GetAttributesStr() + ">" + crlfStr

			' 子ノードリスト
			result += InnerXmlSupportedIndent( isIndent, indent + 1 )

			' 終了タグ
			result += indentStr + "</" + localName + ">" + crlfStr
		End If

		Return result
	End Function
End Class

TypeDef XmlNodeList = System.Collections.Generic.List<XmlNode>


End Namespace
End Namespace
