Click here to Skip to main content
15,885,244 members
Articles / Programming Languages / C#

Creating a CodeDOM: Modeling the Semantics of Code (Part 2)

Rate me:
Please Sign up or sign in to vote.
5.00/5 (17 votes)
9 Nov 2012CDDL24 min read 41K   756   33  
Creating a CodeDOM for C#
// The Nova Project by Ken Beckett.
// Copyright (C) 2007-2012 Inevitable Software, all rights reserved.
// Released under the Common Development and Distribution License, CDDL-1.0: http://opensource.org/licenses/cddl1.php

using System;
using System.Collections;
using System.Collections.Generic;

namespace Nova.CodeDOM
{
    /// <summary>
    /// Represents a dictionary of named code objects, allowing multiple entries with the same name.
    /// </summary>
    /// <remarks>
    /// This dictionary is used by <see cref="Block"/>s to maintain a dictionary of child <see cref="INamedCodeObject"/>s to allow for
    /// lookups by name.  It handles multiple entries with the same name (such as overloaded methods, different type parameter counts,
    /// or name collisions in invalid code) by storing a <see cref="NamedCodeObjectGroup"/> object.
    /// </remarks>
    public class NamedCodeObjectDictionary : ICollection
    {
        #region /* FIELDS */

        protected Dictionary<string, INamedCodeObject> _dictionary = new Dictionary<string, INamedCodeObject>();

        #endregion

        #region /* PROPERTIES */

        /// <summary>
        /// The number of items in the dictionary.
        /// </summary>
        public int Count
        {
            get { return _dictionary.Values.Count; }
        }

        /// <summary>
        /// True if access to the <see cref="ICollection"/> is synchronized.
        /// </summary>
        public virtual bool IsSynchronized
        {
            get { return false; }
        }

        /// <summary>
        /// Gets an object that can be used to synchronize access to the <see cref="ICollection"/>.
        /// </summary>
        public virtual object SyncRoot
        {
            get { return this; }
        }

        #endregion

        #region /* METHODS */

        /// <summary>
        /// Add a code object with the specified name to the dictionary.
        /// </summary>
        /// <param name="name">The name of the object.</param>
        /// <param name="namedCodeObject">The named code object.</param>
        public void Add(string name, INamedCodeObject namedCodeObject)
        {
            // Protect against null names - shouldn't occur, but might in rare cases for code with parsing errors
            if (name != null)
            {
                INamedCodeObject existingObj;
                if (_dictionary.TryGetValue(name, out existingObj))
                {
                    // If we had a name collision, add to any existing group, or create a new one
                    if (existingObj is NamedCodeObjectGroup)
                        ((NamedCodeObjectGroup)existingObj).Add(namedCodeObject);
                    else
                    {
                        _dictionary.Remove(name);
                        _dictionary.Add(name, new NamedCodeObjectGroup { existingObj, namedCodeObject });
                    }
                }
                else
                    _dictionary.Add(name, namedCodeObject);
            }
        }

        /// <summary>
        /// Remove the object with the specified name from the dictionary.
        /// </summary>
        /// <param name="name">The name of the object.</param>
        /// <param name="namedCodeObject">The named code object.</param>
        public void Remove(string name, INamedCodeObject namedCodeObject)
        {
            INamedCodeObject existingObj;
            if (_dictionary.TryGetValue(name, out existingObj))
            {
                if (existingObj is NamedCodeObjectGroup)
                {
                    // If there's a group with the given name, remove the object from the group
                    NamedCodeObjectGroup group = (NamedCodeObjectGroup)existingObj;
                    group.Remove(namedCodeObject);
                    if (group.Count == 1)
                    {
                        // If only one object is left in the group, replace the group with the object
                        _dictionary.Remove(name);
                        _dictionary.Add(name, namedCodeObject);
                    }
                }
                else
                    _dictionary.Remove(name);
            }
        }

        /// <summary>
        /// Clear all members from the dictionary.
        /// </summary>
        public void Clear()
        {
            _dictionary.Clear();
        }

        /// <summary>
        /// Find named code object(s) in the dictionary by name.
        /// </summary>
        /// <returns>The matching <see cref="CodeObject"/>, <see cref="NamedCodeObjectGroup"/>, or null if not found.</returns>
        public INamedCodeObject Find(string name)
        {
            if (name == null)
                return null;
            INamedCodeObject found;
            _dictionary.TryGetValue(name, out found);
            return found;
        }

        /// <summary>
        /// Find a named goto target in the dictionary by name.
        /// </summary>
        /// <returns>The matching <see cref="Label"/> or <see cref="SwitchItem"/>, or null if not found
        /// (or <see cref="NamedCodeObjectGroup"/> if there are multiple matches).</returns>
        public INamedCodeObject FindGotoTarget(string name)
        {
            INamedCodeObject found;
            _dictionary.TryGetValue(":" + name, out found);
            return found;
        }

        /// <summary>
        /// Get an enumerator for the objects in the dictionary.
        /// </summary>
        public IEnumerator GetEnumerator()
        {
            foreach (INamedCodeObject namedCodeObject in _dictionary.Values)
                yield return namedCodeObject;
        }

        /// <summary>
        /// Copy the objects in the dictionary to the specified array, starting at the specified offset.
        /// </summary>
        /// <param name="array">The array to copy into.</param>
        /// <param name="index">The starting index in the array.</param>
        public virtual void CopyTo(Array array, int index)
        {
            if (array == null)
                throw new ArgumentNullException("array", "Null array reference");
            if (index < 0)
                throw new ArgumentOutOfRangeException("index", "Index is out of range");
            if (array.Rank > 1)
                throw new ArgumentException("Array is multi-dimensional", "array");

            foreach (INamedCodeObject obj in this)
                array.SetValue(obj, index++);
        }

        #endregion
    }
}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The Common Development and Distribution License (CDDL)


Written By
Software Developer (Senior)
United States United States
I've been writing software since the late 70's, currently focusing mainly on C#.NET. I also like to travel around the world, and I own a Chocolate Factory (sadly, none of my employees are oompa loompas).

Comments and Discussions