// 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.Reflection;
namespace Nova.CodeDOM
{
/// <summary>
/// Represents a group of named code objects (<see cref="INamedCodeObject"/>s and/or <see cref="MemberInfo"/>s)
/// with the same name, such as overloaded methods or other name collisions.
/// </summary>
/// <remarks>
/// Used by <see cref="NamedCodeObjectDictionary"/> to hold groups of child <see cref="INamedCodeObject"/>s with the same name,
/// and also by various Get() and Find() type methods to return collections of <see cref="INamedCodeObject"/>s and/or
/// <see cref="MemberInfo"/>s with the same name.
/// </remarks>
public class NamedCodeObjectGroup : INamedCodeObject, ICollection
{
#region /* FIELDS */
/// <summary>
/// The list of code objects with the same name.
/// </summary>
protected ArrayList _list = new ArrayList();
#endregion
#region /* CONSTRUCTORS */
/// <summary>
/// Create an empty <see cref="NamedCodeObjectGroup"/>.
/// </summary>
public NamedCodeObjectGroup()
{ }
/// <summary>
/// Create a <see cref="NamedCodeObjectGroup"/>, adding the specified object to it.
/// </summary>
public NamedCodeObjectGroup(object obj)
{
Add(obj);
}
#endregion
#region /* PROPERTIES */
/// <summary>
/// The name of the <see cref="NamedCodeObjectGroup"/>.
/// </summary>
public string Name
{
get
{
object obj = _list[0];
string name;
if (obj is INamedCodeObject)
name = ((INamedCodeObject)obj).Name;
else if (obj is MemberInfo)
name = ((MemberInfo)obj).Name;
else
name = null;
return name;
}
}
/// <summary>
/// The descriptive category of the code object.
/// </summary>
public string Category
{
get { return "ambiguity"; }
}
/// <summary>
/// The number of items in the group.
/// </summary>
public int Count
{
get { return _list.Count; }
}
/// <summary>
/// Get the first item in the group.
/// </summary>
public object First
{
get { return _list[0]; }
}
/// <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 the specified <see cref="INamedCodeObject"/> to the group.
/// </summary>
public void Add(INamedCodeObject namedCodeObject)
{
_list.Add(namedCodeObject);
}
/// <summary>
/// Add the specified <see cref="MemberInfo"/> to the group.
/// </summary>
public void Add(MemberInfo memberInfo)
{
_list.Add(memberInfo);
}
/// <summary>
/// Add an object to the group.
/// </summary>
/// <param name="obj">The object being added.</param>
public void Add(object obj)
{
if (obj is IEnumerable)
AddRange((IEnumerable)obj);
else if (obj != null)
_list.Add(obj);
}
protected void AddRange(IEnumerable collection)
{
if (collection != null)
{
// Call the Add method for each member, allowing for nested
// arrays and/or collections.
foreach (object obj in collection)
Add(obj);
}
}
/// <summary>
/// This method is not supported for this type.
/// </summary>
public SymbolicRef CreateRef(bool isFirstOnLine)
{
throw new Exception("Can't create a reference to a NamedCodeObjectGroup!");
}
/// <summary>
/// This method is not supported for this type.
/// </summary>
public SymbolicRef CreateRef()
{
return CreateRef(false);
}
/// <summary>
/// Remove all items from the group.
/// </summary>
public void Clear()
{
_list.Clear();
}
/// <summary>
/// Copy the objects in the group 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 (object obj in this)
array.SetValue(obj, index++);
}
/// <summary>
/// Check if the group contains the specified code object.
/// </summary>
/// <param name="codeObject">The code object being searched for.</param>
/// <returns>True if the group contains the object, otherwise false.</returns>
public bool Contains(CodeObject codeObject)
{
// NOTE: A bug in .NET causes an exception if Equals is called (which Contains uses) to
// compare a MethodInfo for a generic method declaration to any non-MethodInfo object.
// We currently only call this method for CodeObjects, so we look for them specifically
// to avoid the bug.
//return _list.Contains(obj);
if (_list.Count > 0)
{
foreach (object obj in _list)
{
if (obj is CodeObject && obj.Equals(codeObject))
return true;
}
}
return false;
}
/// <summary>
/// Get an enumerator for the objects in the group.
/// </summary>
public IEnumerator GetEnumerator()
{
return _list.GetEnumerator();
}
/// <summary>
/// Remove the specified object from the group.
/// </summary>
public void Remove(object obj)
{
_list.Remove(obj);
}
/// <summary>
/// Remove the object at the specified index from the group.
/// </summary>
public void RemoveAt(int index)
{
_list.RemoveAt(index);
}
/// <summary>
/// Get the object in the group at the specified index.
/// </summary>
public object this[int index]
{
get { return _list[index]; }
}
/// <summary>
/// Add the <see cref="CodeObject"/> to the specified dictionary.
/// </summary>
public virtual void AddToDictionary(NamedCodeObjectDictionary dictionary)
{
dictionary.Add(Name, this);
}
/// <summary>
/// Remove the <see cref="CodeObject"/> from the specified dictionary.
/// </summary>
public virtual void RemoveFromDictionary(NamedCodeObjectDictionary dictionary)
{
dictionary.Remove(Name, this);
}
/// <summary>
/// This method always returns null for this type.
/// </summary>
public T FindParent<T>() where T : CodeObject
{
return null;
}
/// <summary>
/// Get the full name of the <see cref="INamedCodeObject"/>, including any namespace name.
/// </summary>
public string GetFullName(bool descriptive)
{
return Name;
}
/// <summary>
/// Get the full name of the <see cref="INamedCodeObject"/>, including any namespace name.
/// </summary>
public string GetFullName()
{
return Name;
}
#endregion
#region /* STATIC METHODS */
/// <summary>
/// Add a source object or group to a target object or group, converting the target into a group if necessary.
/// </summary>
/// <param name="target">The target object or group.</param>
/// <param name="source">The source object or group.</param>
public static void Add(ref object target, object source)
{
if (target == null)
{
if (source is ICollection)
{
if (((ICollection)source).Count > 0)
target = new NamedCodeObjectGroup(source);
}
else
target = source;
}
else if (source != null && !(source is ICollection && ((ICollection)source).Count == 0))
{
if (!(target is NamedCodeObjectGroup))
target = new NamedCodeObjectGroup(target);
((NamedCodeObjectGroup)target).Add(source);
}
}
/// <summary>
/// Add a source object or group to a target object or group, converting the target into a group if necessary.
/// </summary>
/// <param name="target">The target object or group.</param>
/// <param name="source">The source object or group.</param>
public static void Add(ref INamedCodeObject target, INamedCodeObject source)
{
if (target == null)
{
if (source is NamedCodeObjectGroup)
target = new NamedCodeObjectGroup(source);
else
target = source;
}
else if (source != null)
{
if (!(target is NamedCodeObjectGroup))
target = new NamedCodeObjectGroup(target);
((NamedCodeObjectGroup)target).Add(source);
}
}
#endregion
}
}