// 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 Nova.Parsing;
using Nova.Rendering;
using Nova.Resolving;
namespace Nova.CodeDOM
{
/// <summary>
/// Represents a special method used to initialize an instance of a <see cref="ClassDecl"/> or <see cref="StructDecl"/>.
/// If a constructor is static, it can't have any parameters, and can't have any other modifiers
/// except extern (in which case it has no body).
/// </summary>
public class ConstructorDecl : MethodDeclBase
{
#region /* FIELDS */
/// <summary>
/// Optional ThisInitializer or BaseInitializer call to another constructor.
/// </summary>
protected ConstructorInitializer _initializer;
#endregion
#region /* CONSTRUCTORS */
/// <summary>
/// Create a <see cref="ConstructorDecl"/>.
/// </summary>
public ConstructorDecl(params ParameterDecl[] parameters)
: base(null, null, parameters)
{ }
/// <summary>
/// Create a <see cref="ConstructorDecl"/>.
/// </summary>
public ConstructorDecl(Modifiers modifiers, params ParameterDecl[] parameters)
: base(null, null, modifiers, parameters)
{ }
/// <summary>
/// Create a <see cref="ConstructorDecl"/>.
/// </summary>
public ConstructorDecl(Modifiers modifiers, CodeObject body, params ParameterDecl[] parameters)
: base(null, null, modifiers, body, parameters)
{ }
/// <summary>
/// Create a <see cref="ConstructorDecl"/>.
/// </summary>
public ConstructorDecl(CodeObject body, params ParameterDecl[] parameters)
: base(null, null, body, parameters)
{ }
#endregion
#region /* PROPERTIES */
/// <summary>
/// The name of the <see cref="ConstructorDecl"/>.
/// </summary>
public override string Name
{
get
{
// Always use the name of the current parent TypeDecl if possible.
if (_parent is TypeDecl)
return ((TypeDecl)_parent).Name;
// The _name field should normally be null, but might be a string in rare
// cases, such as a compiler-generated ConstructorDecl for a Type parent.
return (string)_name;
}
}
/// <summary>
/// The descriptive category of the code object.
/// </summary>
public override string Category
{
get { return "constructor"; }
}
/// <summary>
/// An optional <see cref="ConstructorInitializer"/>.
/// </summary>
public ConstructorInitializer Initializer
{
get { return _initializer; }
set { SetField(ref _initializer, value, true); }
}
#endregion
#region /* METHODS */
/// <summary>
/// Create a reference to the <see cref="ConstructorDecl"/>.
/// </summary>
/// <param name="isFirstOnLine">True if the reference should be displayed on a new line.</param>
/// <returns>A <see cref="ConstructorRef"/>.</returns>
public override SymbolicRef CreateRef(bool isFirstOnLine)
{
return new ConstructorRef(this, isFirstOnLine);
}
/// <summary>
/// Get a reference to the declaring type of the constructor.
/// </summary>
public TypeRef GetDeclaringType()
{
TypeRef declaringType;
if (_parent is TypeDecl)
declaringType = (TypeRef)_parent.CreateRef();
// Handle the special case where the Parent is null for the generated ConstructorDecl of an external delegate type
else if (_parent == null && IsGenerated && ParameterCount == 1)
declaringType = Parameters[0].EvaluateType() as TypeRef;
else
declaringType = null;
return declaringType;
}
/// <summary>
/// Deep-clone the code object.
/// </summary>
public override CodeObject Clone()
{
ConstructorDecl clone = (ConstructorDecl)base.Clone();
clone.CloneField(ref clone._initializer, _initializer);
return clone;
}
#endregion
#region /* PARSING */
internal static void AddParsePoints()
{
// Use a parse-priority of 0 (MethodDecl uses 50, LambdaExpression uses 100, Call uses 200, Cast uses 300, Expression parens uses 400)
Parser.AddParsePoint(ParameterDecl.ParseTokenStart, Parse, typeof(TypeDecl));
}
/// <summary>
/// Parse a <see cref="ConstructorDecl"/>.
/// </summary>
public static ConstructorDecl Parse(Parser parser, CodeObject parent, ParseFlags flags)
{
// Validate that we have an unused identifier token that matches our parent TypeDecl name
// (otherwise, abort and give MethodDecl a chance to parse it)
if (parser.HasUnusedIdentifier && parent is TypeDecl && parser.LastUnusedTokenText == ((TypeDecl)parent).Name)
return new ConstructorDecl(parser, parent, flags);
return null;
}
protected ConstructorDecl(Parser parser, CodeObject parent, ParseFlags flags)
: base(parser, parent)
{
ParseMethodNameAndType(parser, parent, true, true);
_name = null; // Clear name (we use the parent's name instead)
ParseModifiersAndAnnotations(parser); // Parse any attributes and/or modifiers
// Move any trailing compiler directives to the Infix2 position (assume we have an initializer)
MoveAnnotations(AnnotationFlags.IsPostfix, AnnotationFlags.IsInfix2);
ParseParameters(parser); // Parse the parameters
// Check for compiler directives, storing them as infix annotations on the parent
Block.ParseCompilerDirectives(parser, this, AnnotationFlags.IsInfix2);
SetField(ref _initializer, ConstructorInitializer.Parse(parser, this), false);
// If we don't have an initializer, move any trailing compiler directives to the Postfix position
if (_initializer == null)
MoveAnnotations(AnnotationFlags.IsInfix2, AnnotationFlags.IsPostfix);
ParseTerminatorOrBody(parser, flags);
}
#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)
ChildListHelpers.Resolve(_parameters, ResolveCategory.CodeObject, flags);
if ((flags & (ResolveFlags.Phase1 | ResolveFlags.Phase2)) == 0)
{
ResolveAttributes(flags);
if (_initializer != null)
_initializer.Resolve(ResolveCategory.CodeObject, flags);
if (_body != null)
_body.Resolve(ResolveCategory.CodeObject, flags);
ResolveDocComments(flags);
}
return this;
}
/// <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 (_initializer != null && _initializer.HasUnresolvedRef())
return true;
return base.HasUnresolvedRef();
}
#endregion
#region /* ANALYSIS */
public override void Accept(IVisitor visitor)
{
// Don't visit generated constructors
if (!IsGenerated)
{
AcceptAnnotations(visitor);
visitor.Visit(this);
if (_returnType != null)
_returnType.Accept(visitor);
ChildListHelpers.Accept(_parameters, visitor);
AcceptEOLComments(visitor);
if (_initializer != null)
_initializer.Accept(visitor);
AcceptAnnotations(visitor, AnnotationFlags.IsPostfix);
if (_body != null)
_body.Accept(visitor);
}
}
#endregion
#region /* FORMATTING */
/// <summary>
/// Reformat the <see cref="Block"/> body.
/// </summary>
public override void ReformatBlock()
{
base.ReformatBlock();
// Format the constructor initializer IsFirstOnLine setting to match the body
if (_initializer != null && !_initializer.IsNewLinesSet)
_initializer.IsFirstOnLine = _body.IsFirstOnLine;
}
protected override void DefaultFormatField(CodeObject field)
{
base.DefaultFormatField(field);
// Default the constructor initializer IsFirstOnLine setting to match the body
if (!field.IsNewLinesSet)
field.SetNewLines((_body != null && _body.IsFirstOnLine) ? 1 : 0);
}
/// <summary>
/// Determines if the code object only requires a single line for display.
/// </summary>
public override bool IsSingleLine
{
get { return (base.IsSingleLine && (_initializer == null || (!_initializer.IsFirstOnLine && _initializer.IsSingleLine))); }
set
{
base.IsSingleLine = value;
if (_initializer != null)
{
if (value)
_initializer.IsFirstOnLine = false;
_initializer.IsSingleLine = value;
}
}
}
#endregion
#region /* RENDERING */
internal override void AsTextName(CodeWriter writer, RenderFlags flags)
{
writer.Write(Name);
}
protected override void AsTextSuffix(CodeWriter writer, RenderFlags flags)
{
if (_initializer == null)
base.AsTextSuffix(writer, flags);
}
protected override void AsTextAfter(CodeWriter writer, RenderFlags flags)
{
AsTextAnnotations(writer, AnnotationFlags.IsInfix2, flags);
if (_initializer != null && !flags.HasFlag(RenderFlags.Description))
{
writer.BeginIndentOnNewLine(this);
_initializer.AsText(writer, flags | RenderFlags.IncreaseIndent
| (_initializer.IsFirstOnLine ? 0 : RenderFlags.PrefixSpace) | RenderFlags.HasTerminator);
writer.EndIndentation(this);
}
base.AsTextAfter(writer, flags);
}
#endregion
}
}