Exercise 7: Solution

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Plato.Core;

namespace Plato.Language.Parser
{
    /// <summary>
    /// Parses word-to-entity mappings.
    /// </summary>
    public class WordMappingParser
    {
        Brain brain;
        IEntity is_a;

        public WordMappingParser(Brain brain)
        {
            this.brain = brain;

            is_a = brain.Get("is_a");
        }

        /// <summary>
        /// Parse one or more lines of input that define word-to-entity
        /// mappings.
        /// </summary>
        /// <param name="str">The text input.</param>
        public void Parse(string str)
        {
            string[] lines = str.Split(new char[] { '\n', '\r' },
                                       StringSplitOptions.RemoveEmptyEntries);

            // Process each line of input
            int lineNum = 0;
            foreach (string line in lines)
            {
                ++lineNum;
                line.Trim();

                if (String.IsNullOrEmpty(line))
                {
                    continue;
                }

                try
                {
                    string wordId;
                    string mapping;
                    List<string> wordTypes = new List<string>();
                    string entityId;

                    if (line.Contains("->"))
                    {
                        string[] parts = line.Split(new string[] { "->" },
                                                    StringSplitOptions.None);
                        if (parts.Length != 2)
                        {
                            throw new TooManyArrowsException(
                                "Expecting a single '->'");
                        }
                        else
                        {
                            wordId = parts[0].Trim();
                            mapping = parts[1].Trim();

                            int colonPos = mapping.IndexOf(':');

                            if (colonPos >= 0)
                            {
                                entityId = mapping.Substring(0, colonPos).Trim();
                                mapping = mapping.Substring(
                                    colonPos + 1,
                                    mapping.Length - colonPos - 1).Trim();

                                // The word is additionally specified as being
                                // a noun, verb, etc.
                                wordTypes = mapping.Split(
                                    new char[] { ',', ' ' },
                                    StringSplitOptions.RemoveEmptyEntries).ToList<string>();
                            }
                            else
                            {
                                entityId = mapping;
                            }

                            IEntity word = brain.Get(wordId);
                            if (word == null)
                            {
                                word = brain.CreateEntity(wordId);
                            }

                            IEntity entity = brain.Get(entityId);
                            if (entity == null)
                            {
                                entity = brain.CreateEntity(entityId);
                            }

                            brain.CreateRelationship(word, is_a, entity);

                            foreach (string wordTypeId in wordTypes)
                            {
                                IEntity wordType = brain.Get(wordTypeId);
                                if (wordType == null)
                                {
                                    wordType = brain.CreateEntity(wordTypeId);
                                }

                                brain.CreateRelationship(entity, is_a, wordType);
                            }
                        }
                    }
                    else
                    {
                        throw new MissingArrowException("Missing '->'");
                    }
                }
                catch (AlterableException e)
                {
                    e.AlterMessage(
                        String.Format("Line {0}: {1}",
                                      lineNum, e.Message));
                    throw e;
                }
                catch (Exception e)
                {
                    throw new Exception(
                        String.Format("Line {0}: {1}",
                                      lineNum, e.Message));
                }
            }
        }
    }
}

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Plato.Core
{
    /// <summary>
    /// A tree of entities.
    /// </summary>
    public class EntityTree
    {
        EntityTreeNode root;
        IDictionary<string, EntityTreeNode> entities;

        /// <summary>
        /// Create an entity tree.
        /// </summary>
        /// <param name="root">The root node.</param>
        /// <param name="entities">The dictionary of entities.</param>
        public EntityTree(
            IEntity root,
            IDictionary<string,EntityTreeNode> entities)
        {
            this.root = new EntityTreeNode(root, null);
            this.entities = entities;
        }

        /// <summary>
        /// The root node of the Tree.
        /// </summary>
        public EntityTreeNode Root
        {
            get { return root; }
        }

        /// <summary>
        /// Get a flat list of the entities.
        /// </summary>
        /// <returns></returns>
        public ICollection<EntityTreeNode> GetList()
        {
            return entities.Values;
        }
    }
}

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Plato.Core
{
    /// <summary>
    /// Represents a node in a tree of entities.
    /// </summary>
    public class EntityTreeNode
    {
        IEntity entity;
        IList<EntityTreeNode> children;
        EntityTreeNode parent;

        public EntityTreeNode(IEntity entity, EntityTreeNode parent)
        {
            this.entity = entity;
            this.parent = parent;
            this.children = new List<EntityTreeNode>();
        }

        /// <summary>
        /// The node's entity.
        /// </summary>
        public IEntity Entity
        {
            get { return entity; }
        }

        /// <summary>
        /// The node's parent.
        /// </summary>
        public EntityTreeNode Parent
        {
            get { return parent; }
        }

        /// <summary>
        /// The node's children.
        /// </summary>
        public IList<EntityTreeNode> Children
        {
            get { return children; }
        }
    }
}

/// <summary>
/// Returns a tree that represents the entity's is_a relationships.
/// </summary>
/// <returns>A tree.</returns>
public EntityTree GetIsATree()
{
    IDictionary<string, EntityTreeNode> entities =
        new Dictionary<string, EntityTreeNode>();

    EntityTree tree = new EntityTree(this, entities);

    PopulateIsATree(tree.Root, entities);

    return tree;
}

/// <summary>
/// Helper function for GetIsATree.
/// </summary>
/// <param name="treeNode">The tree node to populate.</param>
/// <param name="entities">Dictionary of already-processed entities.</param>
private void PopulateIsATree(EntityTreeNode treeNode,
                             IDictionary<string, EntityTreeNode> entities)
{
    entities.Add(new KeyValuePair<string, EntityTreeNode>(
        this.Id, treeNode));

    // Depth-first, recursive iteration
    foreach (List<IRelationship> relationshipList in relatedEntities.Values)
    {
        foreach (IRelationship relationship in relationshipList)
        {
            if (relationship.Relation.Id == "is_a")
            {
                if (!entities.ContainsKey(relationship.Entity2.Id))
                {
                    EntityTreeNode child = new EntityTreeNode(
                        relationship.Entity2, treeNode);

                    ((Entity)relationship.Entity2).PopulateIsATree(child, entities);

                    treeNode.Children.Add(child);
                }
            }
        }
    }
}