バージョン

異なるデータ ソースの使用

この詳細なガイドでは、データをファイルから Network Node コントロールに読み込む方法を示します。例として、Graph Modelling Language (GML) ファイルが使用されます。トピックの最後で、完全なコード例を提供します。トピックは以下のとおりです。

概要

Network Node コントロールは、IEnumerable の形式でデータをサポートしますが、そのデータはファイルから簡単に読み取ることができます。この詳細なガイドでは、データを GML から Network Node コントロールに読み込む方法を示します。

プレビュー

以下は最終結果のプレビューです。

xamNetworkNode UsingDifferentDataSourcesGml 01.png

要件

この記事は、「Network Node コントロールの追加」の記事を既に読んでいることを前提とし、開始点として詳細説明でこのコードを使用します。概要: 以下は実装の概念的概要です。

  1. ノード レイアウトの構成

  2. GML データの読み込み

  3. DownloadStringCompleted イベントの処理

  4. (オプション) 結果を確認します

手順

  1. GML データを読み込みます。

提供される GML パーサーはデータ モデルを以下のように使用します。

XAML の場合:

<Grid x:Name="LayoutRoot" Background="White">
        <ig:XamNetworkNode x:Name="xnn">
            <ig:XamNetworkNode.GlobalNodeLayouts>
                <ig:NetworkNodeNodeLayout
                    TargetTypeName = "GmlNode"
                    DisplayMemberPath = "Id"
                    ToolTipMemberPath = "Label"
                    ConnectionsMemberPath = "Connections"
                    ConnectionTargetMemberPath = "Target"
                    />
            </ig:XamNetworkNode.GlobalNodeLayouts>
        </ig:XamNetworkNode>
    </Grid>

アプリケーション コンストラクターで、InitializeComponent を呼び出した後に、WebClient インスタンスを使用して、GML データを文字列として読み込みます。便宜上提供された GML ファイル Graph2.gml を使用します。

C# の場合:

        public MainPage()
        {
            InitializeComponent();
            var webClient = new WebClient();
            webClient.DownloadStringCompleted += GmlFileLoaded;
            webClient.DownloadStringAsync(new Uri("Graph2.gml", UriKind.RelativeOrAbsolute));
        }

Visual Basic の場合:

        Public Sub New()
            InitializeComponent()
            Dim webClient = New WebClient()
            AddHandler webClient.DownloadStringCompleted, AddressOf GmlFileLoaded
            webClient.DownloadStringAsync(New Uri("Graph2.gml", UriKind.RelativeOrAbsolute))
        End Sub
  1. DownloadStringCompleted イベントを処理します。

ハンドラーはファイル データを解析するように構成され、Network Node コントロールの ItemsSource プロパティを設定します。

C# の場合:

        private void GmlFileLoaded(object sender, DownloadStringCompletedEventArgs e)
        {
            var stream = new StringReader(e.Result);
            var nodes = new GmlParser().Parse(stream);
            xnn.ItemsSource = nodes;
        }

Visual Basic の場合:

Private Sub GmlFileLoaded(sender As Object, e As DownloadStringCompletedEventArgs)
            Dim stream = New StringReader(e.Result)
            Dim nodes = New GmlParser().Parse(stream)
            xnn.ItemsSource = nodes
End Sub

DownloadStringCompletedEventArgs Result プロパティは文字列として書式設定された GML データを持ち、GmlParser はインフラジスティックスが提供するクラスです。GmlParser.Parse は TextReader オブジェクトを取得し、文字列を解析して、IEnumerable を返します。GmlNode と その他のユーティリティ クラスは GmlParser で定義されます。

  1. (オプション) 結果を確認します。

実装を検証するために、アプリケーションを実行します。この時点で、Graph2.gml からのデータは、上記の図 1 に示すように表示されます。

全コード例

以下は完全なコードです。

XAML の場合:

<UserControl x:Class="xamNetworkNode_LoadingGmlData.MainPage"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    xmlns:ig="http://schemas.infragistics.com/xaml"
    mc:Ignorable="d"
    d:DesignHeight="300" d:DesignWidth="400">
    <Grid x:Name="LayoutRoot" Background="White">
        <ig:XamNetworkNode x:Name="xnn">
            <ig:XamNetworkNode.GlobalNodeLayouts>
                <ig:NetworkNodeNodeLayout
                    TargetTypeName = "GmlNode"
                    DisplayMemberPath = "Id"
                    ToolTipMemberPath = "Label"
                    ConnectionsMemberPath = "Connections"
                    ConnectionTargetMemberPath = "Target"
                    />
            </ig:XamNetworkNode.GlobalNodeLayouts>
        </ig:XamNetworkNode>
    </Grid>
</UserControl>

C# の場合:

using System;
using System.IO;
using System.Net;
using System.Windows.Controls;
using xamNetworkNode_LoadingGmlData.Resources;
namespace xamNetworkNode_LoadingGmlData
{
    public partial class MainPage : UserControl
    {
        public MainPage()
        {
            InitializeComponent();
            var webClient = new WebClient();
            webClient.DownloadStringCompleted += GmlFileLoaded;
            webClient.DownloadStringAsync(new Uri("Graph2.gml", UriKind.RelativeOrAbsolute));
        }
        private void GmlFileLoaded(object sender, DownloadStringCompletedEventArgs e)
        {
            var stream = new StringReader(e.Result);
            var nodes = new GmlParser().Parse(stream);
            xnn.ItemsSource = nodes;
        }
    }
}

Visual Basic の場合:

Imports System
Imports System.IO
Imports System.Net
Imports System.Windows.Controls
Imports xamNetworkNode_LoadingGmlData.Resources
Namespace xamNetworkNode_LoadingGmlData
    Public Partial Class MainPage
        Inherits UserControl
        Public Sub New()
            InitializeComponent()
            Dim webClient = New WebClient()
            AddHandler webClient.DownloadStringCompleted, AddressOf GmlFileLoaded
            webClient.DownloadStringAsync(New Uri("Graph2.gml", UriKind.RelativeOrAbsolute))
        End Sub
        Private Sub GmlFileLoaded(sender As Object, e As DownloadStringCompletedEventArgs)
            Dim stream = New StringReader(e.Result)
            Dim nodes = New GmlParser().Parse(stream)
            xnn.ItemsSource = nodes
        End Sub
    End Class
End Namespace

GmlParser.cs

C# の場合:

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
/*
 * このパーサーは、劣等一般公衆利用許諾書の下で、Universitat Passau によってリリースされた C コードに基づいています
 * http://www.fim.uni-passau.de/en/fim/faculty/chairs/theoretische-informatik/projects.html
 *
 * したがってこのクラスは、同じライセンスの下で配布されます
 */
namespace xamNetworkNode_LoadingGmlData.Resources
{
    public class GmlNode
    {
        public int Id { get; set; }
        public string Label { get; set; }
        public IList<GmlConnection> Connections { get; set; }
        public GmlNode()
        {
            Connections = new List<GmlConnection>();
        }
    }
    public class GmlConnection
    {
        public GmlNode Target { get; set; }
        public string Label { get; set; }
    }
    public class GmlParser
    {
        private List<GmlNode> _nodes;
        public IEnumerable<GmlNode> Parse(TextReader reader)
        {
            var scanner = new GmlScanner();
            string propertyKey = null;
            GmlConnection connection = null;
            var lastElement = 0;
            _nodes = new List<GmlNode>();
            foreach (var token in scanner.GetTokens(reader))
            {
                switch (token.Type)
                {
                    case GmlTokenType.Key:
                        switch (token.Data)
                        {
                            case "node":
                                _nodes.Add(new GmlNode());
                                lastElement = 1;
                                break;
                            case "edge":
                                connection = new GmlConnection();
                                lastElement = 2;
                                break;
                            case "id":
                            case "label":
                            case "source":
                            case "target":
                                if (lastElement > 0)
                                {
                                    propertyKey = token.Data;
                                }
                                else
                                {
                                    propertyKey = null;
                                }
                                break;
                            default:
                                lastElement = 0;
                                break;
                        }
                        break;
                    case GmlTokenType.Int:
                        if (propertyKey != null)
                        {
                            switch (propertyKey)
                            {
                                case "id":
                                    if (lastElement == 1)
                                    {
                                        _nodes[_nodes.Count - 1].Id = Convert.ToInt32(token.Data);
                                    }
                                    break;
                                case "source":
                                    var sourceId = Convert.ToInt32(token.Data);
                                    FindNodeById(sourceId).Connections.Add(connection);
                                    break;
                                case "target":
                                    var targetId = Convert.ToInt32(token.Data);
                                    connection.Target = FindNodeById(targetId);
                                    break;
                            }
                        }
                        break;
                    case GmlTokenType.String:
                        if (propertyKey == "label")
                        {
                            if (lastElement == 1)
                            {
                                _nodes[_nodes.Count - 1].Label = token.Data;
                            }
                            else if (lastElement == 2)
                            {
                                connection.Label = token.Data;
                            }
                        }
                        break;
                }
            }
            return _nodes;
        }
        private GmlNode FindNodeById(int id)
        {
            return _nodes.First(node => node.Id == id);
        }
    }
    public class GmlScanner
    {
        private static readonly string[] IsoTable = { " ", "¡", "¢", "£", "¤", "¥", "¦", "§", "¨", "©", "ª", "«", "¬", "­", "®", "¯", "°", "±", "²", "³", "´", "µ", "¶", "·", "¸", "¹", "º", "»", "¼", "½", "¾", "¿", "À", "Á", "Â", "Ã", "Ä", "Å", "Æ", "Ç", "È", "É", "Ê", "Ë", "Ì", "Í", "Î", "Ï", "Ð", "Ñ", "Ò", "Ó", "Ô", "Õ", "Ö", "×", "Ø", "Ù", "Ú", "Û", "Ü", "Ý", "Þ", "ß", "à", "á", "â", "ã", "ä", "å", "æ", "ç", "è", "é", "ê", "ë", "ì", "í", "î", "ï", "ð", "ñ", "ò", "ó", "ô", "õ", "ö", "÷", "ø", "ù", "ú", "û", "ü", "ý", "þ", "ÿ" };
        private static int DecodeIsoChar(string str, int len)
        {
            int i;
            int ret = '&';
            if (string.Compare(str, 0, """, 0, len) == 0)
            {
                return 34;
            }
            if (string.Compare(str, 0, "&", 0, len) == 0)
            {
                return 38;
            }
            if (string.Compare(str, 0, "<", 0, len) == 0)
            {
                return 60;
            }
            if (string.Compare(str, 0, ">", 0, len) == 0)
            {
                return 62;
            }
            for (i = 0; i < 96; i++)
            {
                if (string.Compare(str, 0, IsoTable[i], 0, len) == 0)
                {
                    ret = i + 160;
                    break;
                }
            }
            return ret;
        }
        public IEnumerable<GmlToken> GetTokens(TextReader reader)
        {
            var line = 1;
            var column = 1;
            var sb = new StringBuilder();
            var i = reader.Peek();
            while (i > -1)
            {
                var c = (char)i;
                if (char.IsWhiteSpace(c))
                {
                    if (c == '\n')
                    {
                        line++;
                        column = 1;
                    }
                    else
                    {
                        column++;
                    }
                    reader.Read();
                }
                else
                {
                    int previousLine;
                    int previousColumn;
                    if (char.IsDigit(c) || c == '.' || c == '+' || c == '-')
                    {
                        var isDouble = false;
                        previousLine = line;
                        previousColumn = column;
                        do
                        {
                            if (c == '.')
                            {
                                if (isDouble)
                                {
                                    throw new GmlParseException(line, column);
                                }
                                isDouble = true;
                            }
                            sb.Append(c);
                            column++;
                            reader.Read();
                            c = (char)reader.Peek();
                        } while (char.IsDigit(c) || c == '.');
                        if (!char.IsWhiteSpace(c))
                        {
                            throw new GmlParseException(line, column);
                        }
                        if (c == '\r' || c == '\n')
                        {
                            line++;
                            column = 1;
                        }
                        if (isDouble)
                        {
                            yield return new GmlToken(GmlTokenType.Double, previousLine, previousColumn, sb.ToString());
                        }
                        else
                        {
                            yield return new GmlToken(GmlTokenType.Int, previousLine, previousColumn, sb.ToString());
                        }
                    }
                    else if (char.IsLetter(c) || c == '_')
                    {
                        previousLine = line;
                        previousColumn = column;
                        do
                        {
                            sb.Append(c);
                            column++;
                            reader.Read();
                            c = (char)reader.Peek();
                        } while (char.IsLetterOrDigit(c) || c == '_');
                        if (c == '\r' || c == '\n')
                        {
                            line++;
                            column = 1;
                        }
                        if (!char.IsWhiteSpace(c))
                        {
                            throw new GmlParseException(line, column);
                        }
                        yield return new GmlToken(GmlTokenType.Key, previousLine, previousColumn, sb.ToString());
                    }
                    else
                    {
                        previousLine = line;
                        previousColumn = column;
                        switch (c)
                        {
                            case '#':
                                do
                                {
                                    reader.Read();
                                    c = (char)reader.Peek();
                                } while (c != '\r' && c != '\n' && c != -1);
                                line++;
                                column = 1;
                                break;
                            case '[':
                                line++;
                                reader.Read();
                                yield return new GmlToken(GmlTokenType.LBracket, previousLine, previousColumn);
                                break;
                            case ']':
                                line++;
                                reader.Read();
                                yield return new GmlToken(GmlTokenType.RBracket, previousLine, previousColumn);
                                break;
                            case '"':
                                column++;
                                reader.Read();
                                c = (char)reader.Peek();
                                while (c != '"')
                                {
                                    if (c == '&')
                                    {
                                        var iso = new StringBuilder(8);
                                        while (c != ';')
                                        {
                                            if (c == '"' || c == -1)
                                            {
                                                iso.Clear();
                                                break;
                                            }
                                            if (iso.Length < 8)
                                            {
                                                iso.Append(c);
                                            }
                                            reader.Read();
                                            c = (char)reader.Peek();
                                        }
                                        if (iso.Length == 8)
                                        {
                                            c = '&';
                                        }
                                        else
                                        {
                                            iso.Append(";");
                                            c = (char)DecodeIsoChar(iso.ToString(), iso.Length);
                                        }
                                    }
                                    sb.Append(c);
                                    column++;
                                    reader.Read();
                                    c = (char)reader.Peek();
                                    if (c == -1)
                                    {
                                        throw new GmlParseException(line, column);
                                    }
                                    if (c == '\r' || c == '\n')
                                    {
                                        line++;
                                        column = 1;
                                    }
                                }
                                reader.Read();
                                yield return new GmlToken(GmlTokenType.String, previousLine, previousColumn, sb.ToString());
                                break;
                            default:
                                throw new GmlParseException(line, column);
                        }
                    }
                }
                i = reader.Peek();
                sb.Clear();
            }
        }
    }
    public class GmlParseException : Exception
    {
        private readonly int _line;
        private readonly int _column;
        /// <summary>
        <see cref="T:System.Exception"/> クラスの新しいインスタンスを初期化します
        /// </summary>
        public GmlParseException(int line, int column)
        {
            _line = line;
            _column = column;
        }
        /// <summary>
        /// 指定したエラー メッセージで <see cref="T:System.Exception"/> クラスの新しいインスタンスを初期化します
        /// </summary>
        /// <param name="message">エラーを説明するメッセージ</param>
        /// <param name="line"></param>
        /// <param name="column"></param>
        public GmlParseException(string message, int line, int column)
            : base(message)
        {
            _line = line;
            _column = column;
        }
        /// <summary>
        /// 指定したエラー メッセージおよびこの例外の原因である内部例外への参照を含む <see cref="T:System.Exception"/> クラスの新しいインスタンスを初期化します
        /// </summary>
        /// <param name="message">例外の理由を説明するエラー メッセージ</param><param name="innerException">現在の例外の原因である例外。内部例外が指定されていない場合は null 参照 (Visual Basic で Nothing)</param>
        /// <param name="line"></param>
        /// <param name="column"></param>
        public GmlParseException(string message, Exception innerException, int line, int column)
            : base(message, innerException)
        {
            _line = line;
            _column = column;
        }
        public int Line
        {
            get { return _line; }
        }
        public int Column
        {
            get { return _column; }
        }
    }
    public enum GmlTokenType
    {
        Key,
        Int,
        Double,
        String,
        LBracket,
        RBracket
    }
    public class GmlToken
    {
        public GmlTokenType Type { get; private set; }
        public string Data { get; private set; }
        public int Line { get; private set; }
        public int Column { get; private set; }
        public GmlToken(GmlTokenType type, int line, int column, string data)
        {
            Type = type;
            Data = data;
            Line = line;
            Column = column;
        }
        public GmlToken(GmlTokenType type, int line, int column)
            : this(type, line, column, null)
        {
        }
    }
}

GmlParser.vb

Visual Basic の場合:

Imports System
Imports System.Collections.Generic
Imports System.IO
Imports System.Linq
Imports System.Text
'
' * このパーサーは、劣等一般公衆利用許諾書の下で、Universitat Passau によってリリースされた C コードに基づいています
' * http://www.fim.uni-passau.de/en/fim/faculty/chairs/theoretische-informatik/projects.html
' *
' * したがってこのクラスは、同じライセンスの下で配布されます
'
Namespace xamNetworkNode_LoadingGmlData.Resources
    Public Class GmlNode
        Public Property Id() As Integer
            Get
                Return m_Id
            End Get
            Set
                m_Id = Value
            End Set
        End Property
        Private m_Id As Integer
        Public Property Label() As String
            Get
                Return m_Label
            End Get
            Set
                m_Label = Value
            End Set
        End Property
        Private m_Label As String
        Public Property Connections() As IList(Of GmlConnection)
            Get
                Return m_Connections
            End Get
            Set
                m_Connections = Value
            End Set
        End Property
        Private m_Connections As IList(Of GmlConnection)
        Public Sub New()
            Connections = New List(Of GmlConnection)()
        End Sub
    End Class
    Public Class GmlConnection
        Public Property Target() As GmlNode
            Get
                Return m_Target
            End Get
            Set
                m_Target = Value
            End Set
        End Property
        Private m_Target As GmlNode
        Public Property Label() As String
            Get
                Return m_Label
            End Get
            Set
                m_Label = Value
            End Set
        End Property
        Private m_Label As String
    End Class
    Public Class GmlParser
        Private _nodes As List(Of GmlNode)
        Public Function Parse(reader As TextReader) As IEnumerable(Of GmlNode)
            Dim scanner = New GmlScanner()
            Dim propertyKey As String = Nothing
            Dim connection As GmlConnection = Nothing
            Dim lastElement = 0
            _nodes = New List(Of GmlNode)()
            For Each token As var In scanner.GetTokens(reader)
                Select Case token.Type
                    Case GmlTokenType.Key
                        Select Case token.Data
                            Case "node"
                                _nodes.Add(New GmlNode())
                                lastElement = 1
                                Exit Select
                            Case "edge"
                                connection = New GmlConnection()
                                lastElement = 2
                                Exit Select
                            Case "id", "label", "source", "target"
                                If lastElement > 0 Then
                                    propertyKey = token.Data
                                Else
                                    propertyKey = Nothing
                                End If
                                Exit Select
                            Case Else
                                lastElement = 0
                                Exit Select
                        End Select
                        Exit Select
                    Case GmlTokenType.Int
                        If propertyKey IsNot Nothing Then
                            Select Case propertyKey
                                Case "id"
                                    If lastElement = 1 Then
                                        _nodes(_nodes.Count - 1).Id = Convert.ToInt32(token.Data)
                                    End If
                                    Exit Select
                                Case "source"
                                    Dim sourceId = Convert.ToInt32(token.Data)
                                    FindNodeById(sourceId).Connections.Add(connection)
                                    Exit Select
                                Case "target"
                                    Dim targetId = Convert.ToInt32(token.Data)
                                    connection.Target = FindNodeById(targetId)
                                    Exit Select
                            End Select
                        End If
                        Exit Select
                    Case GmlTokenType.[String]
                        If propertyKey = "label" Then
                            If lastElement = 1 Then
                                _nodes(_nodes.Count - 1).Label = token.Data
                            ElseIf lastElement = 2 Then
                                connection.Label = token.Data
                            End If
                        End If
                        Exit Select
                End Select
            Next
            Return _nodes
        End Function
        Private Function FindNodeById(id As Integer) As GmlNode
            Return _nodes.First(Function(node) node.Id = id)
        End Function
    End Class
    Public Class GmlScanner
        Private Shared ReadOnly IsoTable As String() = {" ", "¡", "¢", "£", "¤", "¥", _
            "¦", "§", "¨", "©", "ª", "«", _
            "¬", "­", "®", "¯", "°", "±", _
            "²", "³", "´", "µ", "¶", "·", _
            "¸", "¹", "º", "»", "¼", "½", _
            "¾", "¿", "À", "Á", "Â", "Ã", _
            "Ä", "Å", "Æ", "Ç", "È", "É", _
            "Ê", "Ë", "Ì", "Í", "Î", "Ï", _
            "Ð", "Ñ", "Ò", "Ó", "Ô", "Õ", _
            "Ö", "×", "Ø", "Ù", "Ú", "Û", _
            "Ü", "Ý", "Þ", "ß", "à", "á", _
            "â", "ã", "ä", "å", "æ", "ç", _
            "è", "é", "ê", "ë", "ì", "í", _
            "î", "ï", "ð", "ñ", "ò", "ó", _
            "ô", "õ", "ö", "÷", "ø", "ù", _
            "ú", "û", "ü", "ý", "þ", "ÿ"}
        Private Shared Function DecodeIsoChar(str As String, len As Integer) As Integer
            Dim i As Integer
            Dim ret As Integer = "&"C
            If String.Compare(str, 0, """, 0, len) = 0 Then
                Return 34
            End If
            If String.Compare(str, 0, "&", 0, len) = 0 Then
                Return 38
            End If
            If String.Compare(str, 0, "<", 0, len) = 0 Then
                Return 60
            End If
            If String.Compare(str, 0, ">", 0, len) = 0 Then
                Return 62
            End If
            For i = 0 To 95
                If String.Compare(str, 0, IsoTable(i), 0, len) = 0 Then
                    ret = i + 160
                    Exit For
                End If
            Next
            Return ret
        End Function
        Public Function GetTokens(reader As TextReader) As IEnumerable(Of GmlToken)
            Dim line = 1
            Dim column = 1
            Dim sb = New StringBuilder()
            Dim i = reader.Peek()
            While i > -1
                Dim c = ChrW(i)
                If Char.IsWhiteSpace(c) Then
                    If c = ControlChars.Lf Then
                        line += 1
                        column = 1
                    Else
                        column += 1
                    End If
                    reader.Read()
                Else
                    Dim previousLine As Integer
                    Dim previousColumn As Integer
                    If Char.IsDigit(c) OrElse c = "."C OrElse c = "+"C OrElse c = "-"C Then
                        Dim isDouble = False
                        previousLine = line
                        previousColumn = column
                        Do
                            If c = "."C Then
                                If isDouble Then
                                    Throw New GmlParseException(line, column)
                                End If
                                isDouble = True
                            End If
                            sb.Append(c)
                            column += 1
                            reader.Read()
                            c = ChrW(reader.Peek())
                        Loop While Char.IsDigit(c) OrElse c = "."C
                        If Not Char.IsWhiteSpace(c) Then
                            Throw New GmlParseException(line, column)
                        End If
                        If c = ControlChars.Cr OrElse c = ControlChars.Lf Then
                            line += 1
                            column = 1
                        End If
                        If isDouble Then
                            yield Return New GmlToken(GmlTokenType.[Double], previousLine, previousColumn, sb.ToString())
                        Else
                            yield Return New GmlToken(GmlTokenType.Int, previousLine, previousColumn, sb.ToString())
                        End If
                    ElseIf Char.IsLetter(c) OrElse c = "_"C Then
                        previousLine = line
                        previousColumn = column
                        Do
                            sb.Append(c)
                            column += 1
                            reader.Read()
                            c = ChrW(reader.Peek())
                        Loop While Char.IsLetterOrDigit(c) OrElse c = "_"C
                        If c = ControlChars.Cr OrElse c = ControlChars.Lf Then
                            line += 1
                            column = 1
                        End If
                        If Not Char.IsWhiteSpace(c) Then
                            Throw New GmlParseException(line, column)
                        End If
                        yield Return New GmlToken(GmlTokenType.Key, previousLine, previousColumn, sb.ToString())
                    Else
                        previousLine = line
                        previousColumn = column
                        Select Case c
                            Case "#"C
                                Do
                                    reader.Read()
                                    c = ChrW(reader.Peek())
                                Loop While c <> ControlChars.Cr AndAlso c <> ControlChars.Lf AndAlso c <> -1
                                line += 1
                                column = 1
                                Exit Select
                            Case "["C
                                line += 1
                                reader.Read()
                                yield Return New GmlToken(GmlTokenType.LBracket, previousLine, previousColumn)
                                Exit Select
                            Case "]"C
                                line += 1
                                reader.Read()
                                yield Return New GmlToken(GmlTokenType.RBracket, previousLine, previousColumn)
                                Exit Select
                            Case """"C
                                column += 1
                                reader.Read()
                                c = ChrW(reader.Peek())
                                While c <> """"C
                                    If c = "&"C Then
                                        Dim iso = New StringBuilder(8)
                                        While c <> ";"C
                                            If c = """"C OrElse c = -1 Then
                                                iso.Clear()
                                                Exit While
                                            End If
                                            If iso.Length < 8 Then
                                                iso.Append(c)
                                            End If
                                            reader.Read()
                                            c = ChrW(reader.Peek())
                                        End While
                                        If iso.Length = 8 Then
                                            c = "&"C
                                        Else
                                            iso.Append(";")
                                            c = ChrW(DecodeIsoChar(iso.ToString(), iso.Length))
                                        End If
                                    End If
                                    sb.Append(c)
                                    column += 1
                                    reader.Read()
                                    c = ChrW(reader.Peek())
                                    If c = -1 Then
                                        Throw New GmlParseException(line, column)
                                    End If
                                    If c = ControlChars.Cr OrElse c = ControlChars.Lf Then
                                        line += 1
                                        column = 1
                                    End If
                                End While
                                reader.Read()
                                yield Return New GmlToken(GmlTokenType.[String], previousLine, previousColumn, sb.ToString())
                                Exit Select
                            Case Else
                                Throw New GmlParseException(line, column)
                        End Select
                    End If
                End If
                i = reader.Peek()
                sb.Clear()
            End While
        End Function
    End Class
    Public Class GmlParseException
        Inherits Exception
        Private ReadOnly _line As Integer
        Private ReadOnly _column As Integer
        ''' <summary>
        ''' <see cref="T:System.Exception"/> クラスの新しいインスタンスを初期化します
        ''' </summary>
        Public Sub New(line As Integer, column As Integer)
            _line = line
            _column = column
        End Sub
        ''' <summary>
        ''' 指定したエラー メッセージで <see cref="T:System.Exception"/> クラスの新しいインスタンスを初期化します
        ''' </summary>
        ''' <param name="message">エラーを説明するメッセージ</param>
        ''' <param name="line"></param>
        ''' <param name="column"></param>
        Public Sub New(message As String, line As Integer, column As Integer)
            MyBase.New(message)
            _line = line
            _column = column
        End Sub
        ''' <summary>
        ''' 指定したエラー メッセージおよびこの例外の原因である内部例外への参照を含む <see cref="T:System.Exception"/> クラスの新しいインスタンスを初期化します
        ''' </summary>
        ''' <param name="message">例外の理由を説明するエラー メッセージ</param><param name="innerException">現在の例外の原因である例外。内部例外が指定されていない場合は null 参照 (Visual Basic で Nothing)</param>
        ''' <param name="line"></param>
        ''' <param name="column"></param>
        Public Sub New(message As String, innerException As Exception, line As Integer, column As Integer)
            MyBase.New(message, innerException)
            _line = line
            _column = column
        End Sub
        Public ReadOnly Property Line() As Integer
            Get
                Return _line
            End Get
        End Property
        Public ReadOnly Property Column() As Integer
            Get
                Return _column
            End Get
        End Property
    End Class
    Public Enum GmlTokenType
        Key
        Int
        [Double]
        [String]
        LBracket
        RBracket
    End Enum
    Public Class GmlToken
        Public Property Type() As GmlTokenType
            Get
                Return m_Type
            End Get
            Private Set
                m_Type = Value
            End Set
        End Property
        Private m_Type As GmlTokenType
        Public Property Data() As String
            Get
                Return m_Data
            End Get
            Private Set
                m_Data = Value
            End Set
        End Property
        Private m_Data As String
        Public Property Line() As Integer
            Get
                Return m_Line
            End Get
            Private Set
                m_Line = Value
            End Set
        End Property
        Private m_Line As Integer
        Public Property Column() As Integer
            Get
                Return m_Column
            End Get
            Private Set
                m_Column = Value
            End Set
        End Property
        Private m_Column As Integer
        Public Sub New(type__1 As GmlTokenType, line__2 As Integer, column__3 As Integer, data__4 As String)
            Type = type__1
            Data = data__4
            Line = line__2
            Column = column__3
        End Sub
        Public Sub New(type As GmlTokenType, line As Integer, column As Integer)
            Me.New(type, line, column, Nothing)
        End Sub
    End Class
End Namespace