// 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;
using System.Linq;
using Nova.Rendering;
using Nova.Resolving;
namespace Nova.CodeDOM
{
/// <summary>
/// Represents a compound field declaration - the declaration of multiple fields of the same type in
/// a single statement.
/// </summary>
/// <remarks>
/// The Modifiers and Type of a MultiFieldDecl will always match those of all of the <see cref="FieldDecl"/>s
/// that it contains, while its Name and Initialization members will always be null. Any changes
/// to the Modifiers or Type of the MultiFieldDecl are propagated to all of its FieldDecl children,
/// and changes to these properties directly on the children are not allowed (exceptions are thrown).
/// </remarks>
public class MultiFieldDecl : FieldDecl, IMultiVariableDecl
{
#region /* FIELDS */
protected ChildList<FieldDecl> _fieldDecls;
#endregion
#region /* CONSTRUCTORS */
/// <summary>
/// Create a multi field declaration from an array of FieldDecls.
/// </summary>
/// <param name="fieldDecls">An array of FieldDecls.</param>
public MultiFieldDecl(params FieldDecl[] fieldDecls)
: base(null, null)
{
_fieldDecls = new ChildList<FieldDecl>(this);
if (fieldDecls.Length > 0)
{
// Acquire the Parent, Type, and Modifiers from the first FieldDecl
FieldDecl fieldDecl1 = fieldDecls[0];
Parent = fieldDecl1.Parent;
fieldDecl1.Parent = null; // Break parent link to avoid clone on Add
SetField(ref _type, (Expression)fieldDecl1.Type.Clone(), true);
_modifiers = fieldDecl1.Modifiers;
// Also move any annotations from the first FieldDecl
if (fieldDecl1.HasAnnotations)
{
Annotations = fieldDecl1.Annotations;
fieldDecl1.Annotations = null;
}
// Add with internal method to avoid changes to Type, etc.
foreach (FieldDecl fieldDecl in fieldDecls)
AddInternal(fieldDecl);
}
}
/// <summary>
/// Create a multi field declaration.
/// </summary>
/// <param name="type">The type of the multi field declaration.</param>
/// <param name="modifiers">The modifiers of the multi field declaration.</param>
/// <param name="names">The list of names to be used.</param>
public MultiFieldDecl(Expression type, Modifiers modifiers, params string[] names)
: base(null, type, modifiers)
{
_fieldDecls = new ChildList<FieldDecl>(this);
foreach (string name in names)
Add(new FieldDecl(name, (Expression)type.Clone(), modifiers));
}
/// <summary>
/// Create a multi field declaration.
/// </summary>
/// <param name="type">The type of the multi field declaration.</param>
/// <param name="names">The list of names to be used.</param>
public MultiFieldDecl(Expression type, params string[] names)
: this(type, Modifiers.None, names)
{ }
#endregion
#region /* PROPERTIES */
/// <summary>
/// The list of <see cref="FieldDecl"/>s.
/// </summary>
public ChildList<FieldDecl> FieldDecls
{
get { return _fieldDecls; }
}
/// <summary>
/// Enumerate the child <see cref="FieldDecl"/>s.
/// </summary>
/// <returns></returns>
public IEnumerator<FieldDecl> GetEnumerator()
{
return ((IEnumerable<FieldDecl>)_fieldDecls).GetEnumerator();
}
/// <summary>
/// Enumerate the child <see cref="FieldDecl"/>s.
/// </summary>
/// <returns></returns>
IEnumerator<VariableDecl> IEnumerable<VariableDecl>.GetEnumerator()
{
return ((IEnumerable<FieldDecl>)_fieldDecls).GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
/// <summary>
/// The number of <see cref="FieldDecl"/>s.
/// </summary>
public int Count
{
get { return _fieldDecls.Count; }
}
/// <summary>
/// Get the <see cref="FieldDecl"/> at the specified index.
/// </summary>
public FieldDecl this[int index]
{
get { return _fieldDecls[index]; }
}
/// <summary>
/// Get the <see cref="VariableDecl"/> at the specified index.
/// </summary>
VariableDecl IMultiVariableDecl.this[int index]
{
get { return _fieldDecls[index]; }
}
/// <summary>
/// Optional <see cref="Modifiers"/>.
/// </summary>
public override Modifiers Modifiers
{
set
{
_modifiers = value;
// Propagate the change to all children
foreach (FieldDecl fieldDecl in _fieldDecls)
fieldDecl.Modifiers = _modifiers;
}
}
/// <summary>
/// The type of the <see cref="MultiFieldDecl"/>.
/// </summary>
public override Expression Type
{
set
{
SetField(ref _type, value, true);
// Propagate the change to all children
foreach (FieldDecl fieldDecl in _fieldDecls)
fieldDecl.SetTypeFromParentMulti((Expression)_type.Clone());
}
}
/// <summary>
/// The line number associated with the <see cref="MultiFieldDecl"/> (actually, the first child <see cref="FieldDecl"/>).
/// </summary>
public override int LineNumber
{
get { return _fieldDecls[0].LineNumber; }
}
/// <summary>
/// The column number associated with the <see cref="MultiFieldDecl"/> (actually, the first child <see cref="FieldDecl"/>).
/// </summary>
public override int ColumnNumber
{
get { return _fieldDecls[0].ColumnNumber; }
}
#endregion
#region /* METHODS */
/// <summary>
/// Add a <see cref="FieldDecl"/>.
/// </summary>
public void Add(FieldDecl fieldDecl)
{
// Set the Type and Modifiers to those of the parent multi
fieldDecl.Type = (Type != null ? (Expression)Type.Clone() : null);
fieldDecl.Modifiers = Modifiers;
AddInternal(fieldDecl);
}
protected void AddInternal(FieldDecl fieldDecl)
{
// Override the default newlines to 0 if it hasn't been explicitly set
if (!fieldDecl.IsNewLinesSet)
fieldDecl.SetNewLines(0);
_fieldDecls.Add(fieldDecl);
}
/// <summary>
/// Add a <see cref="FieldDecl"/> with the specified name.
/// </summary>
public void Add(string name, Expression initialization)
{
AddInternal(new FieldDecl(name, Type != null ? (Expression)Type.Clone() : null, Modifiers, initialization));
}
/// <summary>
/// Add a <see cref="FieldDecl"/> with the specified name.
/// </summary>
public void Add(string name)
{
AddInternal(new FieldDecl(name, Type != null ? (Expression)Type.Clone() : null, Modifiers));
}
/// <summary>
/// Add one more more <see cref="FieldDecl"/>s.
/// </summary>
public void Add(params FieldDecl[] fieldDecls)
{
AddRange(fieldDecls);
}
/// <summary>
/// Add a collection of <see cref="FieldDecl"/>s.
/// </summary>
public void AddRange(IEnumerable<FieldDecl> collection)
{
foreach (FieldDecl fieldDecl in collection)
Add(fieldDecl);
}
/// <summary>
/// This method isn't supported for this type.
/// </summary>
public override SymbolicRef CreateRef(bool isFirstOnLine)
{
throw new Exception("CreateRef() isn't supported for MultiFieldDecls!");
}
/// <summary>
/// Add the <see cref="CodeObject"/> to the specified dictionary.
/// </summary>
public override void AddToDictionary(NamedCodeObjectDictionary dictionary)
{
// For MultiFieldDecls, we need to add each individual FieldDecl
foreach (FieldDecl fieldDecl in _fieldDecls)
dictionary.Add(fieldDecl.Name, fieldDecl);
}
/// <summary>
/// Remove the <see cref="CodeObject"/> from the specified dictionary.
/// </summary>
public override void RemoveFromDictionary(NamedCodeObjectDictionary dictionary)
{
// For MultiFieldDecls, we need to remove each individual FieldDecl
foreach (FieldDecl fieldDecl in _fieldDecls)
dictionary.Remove(fieldDecl.Name, fieldDecl);
}
/// <summary>
/// Deep-clone the code object.
/// </summary>
public override CodeObject Clone()
{
MultiFieldDecl clone = (MultiFieldDecl)base.Clone();
clone._fieldDecls = ChildListHelpers.Clone(_fieldDecls, clone);
return clone;
}
/// <summary>
/// Get the full name of the <see cref="FieldDecl"/>, including the namespace name.
/// </summary>
public override string GetFullName(bool descriptive)
{
return null;
}
#endregion
#region /* RESOLVING */
/// <summary>
/// Resolve all child symbolic references, using the specified <see cref="ResolveCategory"/> and <see cref="ResolveFlags"/>.
/// </summary>
public override CodeObject Resolve(ResolveCategory resolveCategory, ResolveFlags flags)
{
if ((flags & (ResolveFlags.Phase1 | ResolveFlags.Phase3)) == 0)
{
// Resolve the type first, so it can be used to avoid ambiguities while resolving
// any Initializations for the FieldDecls (such as for method groups).
if (_type != null)
Type = (Expression)_type.Resolve(ResolveCategory.Type, flags);
}
if ((flags & (ResolveFlags.Phase1 | ResolveFlags.Phase2)) == 0)
{
ResolveAttributes(flags);
ChildListHelpers.Resolve(_fieldDecls, ResolveCategory.CodeObject, flags);
ResolveDocComments(flags);
}
return this;
}
/// <summary>
/// Resolve child code objects that match the specified name.
/// </summary>
public override void ResolveRef(string name, Resolver resolver)
{
ChildListHelpers.ResolveRef(_fieldDecls, name, resolver);
}
/// <summary>
/// Returns true if the code object is an <see cref="UnresolvedRef"/> or has any <see cref="UnresolvedRef"/> children.
/// </summary>
public override bool HasUnresolvedRef()
{
if (ChildListHelpers.HasUnresolvedRef(_fieldDecls))
return true;
return base.HasUnresolvedRef();
}
#endregion
#region /* FORMATTING */
/// <summary>
/// True if the code object only requires a single line for display by default.
/// </summary>
public override bool IsSingleLineDefault
{
get { return Enumerable.All(_fieldDecls, delegate(FieldDecl fieldDecl) { return fieldDecl.IsSingleLineDefault; }); }
}
/// <summary>
/// Determines if the code object only requires a single line for display.
/// </summary>
public override bool IsSingleLine
{
get { return (base.IsSingleLine && (_fieldDecls.Count == 0 || (!_fieldDecls[0].IsFirstOnLine && _fieldDecls.IsSingleLine))); }
set
{
base.IsSingleLine = value;
if (_fieldDecls.Count > 0)
{
if (value)
_fieldDecls[0].IsFirstOnLine = false;
_fieldDecls.IsSingleLine = value;
}
}
}
#endregion
#region /* RENDERING */
protected override void AsTextPrefix(CodeWriter writer, RenderFlags flags)
{
ModifiersHelpers.AsText(_modifiers, writer);
}
protected override void AsTextStatement(CodeWriter writer, RenderFlags flags)
{
RenderFlags passFlags = (flags & RenderFlags.PassMask);
AsTextType(writer, flags);
UpdateLineCol(writer, flags);
writer.WriteList(_fieldDecls, passFlags | RenderFlags.HasTerminator, this);
}
protected override void AsTextSuffix(CodeWriter writer, RenderFlags flags)
{
// The terminator will be rendered by ChildList above, so don't do it here
}
#endregion
}
}