// 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.Linq;
using Nova.Parsing;
using Nova.Rendering;
namespace Nova.CodeDOM
{
/// <summary>
/// The common base class of <see cref="PropertyDecl"/>, <see cref="IndexerDecl"/>, and <see cref="EventDecl"/>.
/// </summary>
public abstract class PropertyDeclBase : BlockStatement, IVariableDecl, IModifiers
{
#region /* FIELDS */
protected Modifiers _modifiers;
/// <summary>
/// The return type is an <see cref="Expression"/> that must evaluate to a <see cref="TypeRef"/> in valid code.
/// </summary>
protected Expression _type;
/// <summary>
/// The name can be a string or an Expression (in which case it should be a Dot operator
/// with a TypeRef to an Interface on the left and an interface member ref on the right), or if this
/// is an IndexerDecl it can be a ThisRef or a Dot operator with a TypeRef to an Interface on the
/// left and ThisRef on the right.
/// </summary>
protected object _name;
#endregion
#region /* CONSTRUCTORS */
protected PropertyDeclBase(string name, Expression type, Modifiers modifiers, CodeObject body)
: base(body, true)
{
_name = name;
Type = type;
_modifiers = modifiers;
}
protected PropertyDeclBase(string name, Expression type, Modifiers modifiers)
: this(name, type, modifiers, new Block())
{ }
protected PropertyDeclBase(string name, Expression type)
: this(name, type, Modifiers.None)
{ }
protected PropertyDeclBase(Expression name, Expression type, Modifiers modifiers)
: this(null, type, modifiers)
{
_name = name;
}
#endregion
#region /* PROPERTIES */
/// <summary>
/// The name of the property.
/// </summary>
public virtual string Name
{
get
{
if (_name is string)
return (string)_name;
// If it's an explicit interface implementation, use the full name
if (_name is Expression)
return ((Expression)_name).AsString();
return null;
}
set { _name = value; }
}
/// <summary>
/// The descriptive category of the code object.
/// </summary>
public abstract string Category { get; }
/// <summary>
/// The type of the property.
/// </summary>
public Expression Type
{
get { return _type; }
set { SetField(ref _type, value, true); }
}
/// <summary>
/// True if the property is readable.
/// </summary>
public abstract bool IsReadable { get; }
/// <summary>
/// True if the property is writable.
/// </summary>
public abstract bool IsWritable { get; }
/// <summary>
/// Optional <see cref="Modifiers"/> for the property.
/// </summary>
public Modifiers Modifiers
{
get { return _modifiers; }
set { _modifiers = value; }
}
/// <summary>
/// True if the property is abstract.
/// </summary>
public bool IsAbstract
{
get { return _modifiers.HasFlag(Modifiers.Abstract); }
set { _modifiers = (value ? _modifiers | Modifiers.Abstract : _modifiers & ~Modifiers.Abstract); }
}
/// <summary>
/// True if the property is static.
/// </summary>
public bool IsStatic
{
get { return _modifiers.HasFlag(Modifiers.Static); }
set { _modifiers = (value ? _modifiers | Modifiers.Static : _modifiers & ~Modifiers.Static); }
}
/// <summary>
/// True if the property has public access.
/// </summary>
public bool IsPublic
{
get { return _modifiers.HasFlag(Modifiers.Public); }
// Force other flags off if setting to Public
set { _modifiers = (value ? _modifiers & ~(Modifiers.Private | Modifiers.Protected | Modifiers.Internal) | Modifiers.Public : _modifiers & ~Modifiers.Public); }
}
/// <summary>
/// True if the property has private access.
/// </summary>
public bool IsPrivate
{
get { return _modifiers.HasFlag(Modifiers.Private); }
// Force other flags off if setting to Private
set { _modifiers = (value ? _modifiers & ~(Modifiers.Protected | Modifiers.Internal | Modifiers.Public) | Modifiers.Private : _modifiers & ~Modifiers.Private); }
}
/// <summary>
/// True if the property has protected access.
/// </summary>
public bool IsProtected
{
get { return _modifiers.HasFlag(Modifiers.Protected); }
// Force certain other flags off if setting to Protected
set { _modifiers = (value ? _modifiers & ~(Modifiers.Private | Modifiers.Public) | Modifiers.Protected : _modifiers & ~Modifiers.Protected); }
}
/// <summary>
/// True if the property has internal access.
/// </summary>
public bool IsInternal
{
get { return _modifiers.HasFlag(Modifiers.Internal); }
// Force certain other flags off if setting to Protected
set { _modifiers = (value ? _modifiers & ~(Modifiers.Private | Modifiers.Public) | Modifiers.Internal : _modifiers & ~Modifiers.Internal); }
}
/// <summary>
/// True if the method is an override.
/// </summary>
public bool IsOverride
{
get { return _modifiers.HasFlag(Modifiers.Override); }
set { _modifiers = (value ? _modifiers | Modifiers.Override : _modifiers & ~Modifiers.Override); }
}
/// <summary>
/// True if the property is virtual.
/// </summary>
public bool IsVirtual
{
get { return _modifiers.HasFlag(Modifiers.Virtual); }
set { _modifiers = (value ? _modifiers | Modifiers.Virtual : _modifiers & ~Modifiers.Virtual); }
}
/// <summary>
/// True if this is an explicit interface implementation.
/// </summary>
public bool IsExplicitInterfaceImplementation
{
get { return _name is Dot; }
}
/// <summary>
/// Get the explicit interface expression (if any).
/// </summary>
public Expression ExplicitInterfaceExpression
{
get { return _name as Expression; }
}
/// <summary>
/// Get the declaring <see cref="TypeDecl"/>.
/// </summary>
public TypeDecl DeclaringType
{
get { return (_parent as TypeDecl); }
}
#endregion
#region /* METHODS */
/// <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>
/// Get the access rights of the property.
/// </summary>
public abstract void GetAccessRights(bool isTargetOfAssignment, out bool isPrivate, out bool isProtected, out bool isInternal);
/// <summary>
/// Deep-clone the code object.
/// </summary>
public override CodeObject Clone()
{
PropertyDeclBase clone = (PropertyDeclBase)base.Clone();
clone.CloneField(ref clone._type, _type);
clone.CloneField(ref clone._name, _name);
return clone;
}
/// <summary>
/// Get the full name of the <see cref="INamedCodeObject"/>, including any namespace name.
/// </summary>
/// <param name="descriptive">True to display type parameters and method parameters, otherwise false.</param>
public virtual string GetFullName(bool descriptive)
{
if (_parent is TypeDecl)
return ((TypeDecl)_parent).GetFullName(descriptive) + "." + _name;
return _name.ToString();
}
/// <summary>
/// Get the full name of the <see cref="INamedCodeObject"/>, including any namespace name.
/// </summary>
public string GetFullName()
{
return GetFullName(false);
}
#endregion
#region /* PARSING */
internal static void AddParsePoints()
{
// Property declarations are only valid with a TypeDecl parent, but we'll allow any IBlock so that we can
// properly parse them if they accidentally end up at the wrong level (only to flag them as errors).
// This also allows for them to be embedded in a DocCode object.
// Use a parse-priority of 200 (GenericMethodDecl uses 0, UnresolvedRef uses 100, BlockDecl uses 300, Initializer uses 400)
Parser.AddParsePoint(Block.ParseTokenStart, 200, Parse, typeof(IBlock));
}
/// <summary>
/// Parse a <see cref="PropertyDecl"/> or <see cref="EventDecl"/>.
/// </summary>
public static PropertyDeclBase Parse(Parser parser, CodeObject parent, ParseFlags flags)
{
// If our parent is a TypeDecl, verify that we have an unused Expression (it can be either an
// identifier or a Dot operator for explicit interface implementations). Otherwise, require a
// possible type in addition to the Expression.
// If it doesn't seem to match the proper pattern, abort so that other types can try parsing it.
if ((parent is TypeDecl && parser.HasUnusedExpression) || parser.HasUnusedTypeRefAndExpression)
{
// If we have an unused 'event' modifier, it's an event, otherwise treat it as a property
string eventModifier = ModifiersHelpers.AsString(Modifiers.Event).Trim();
bool isEvent = (Enumerable.Any(parser.Unused, delegate(ParsedObject parsedObject) { return parsedObject is Token && ((Token)parsedObject).Text == eventModifier; }));
return (isEvent ? (PropertyDeclBase)new EventDecl(parser, parent) : new PropertyDecl(parser, parent));
}
return null;
}
protected PropertyDeclBase(Parser parser, CodeObject parent, bool parse)
: base(parser, parent)
{
IsFirstOnLine = true; // Force all property, indexer, and event declarations to start on a new line
if (parse)
{
// Parse the name
if (parser.HasUnusedIdentifier)
{
Token token = parser.RemoveLastUnusedToken();
_name = token.NonVerbatimText;
SetLineCol(token);
}
else
{
// Support Dot expressions so we can handle explicit interface members
Expression expression = parser.RemoveLastUnusedExpression();
SetField(ref _name, expression, false);
Expression leftExpression = (expression is BinaryOperator ? ((BinaryOperator)expression).Left : expression);
if (leftExpression != null)
SetLineCol(leftExpression);
}
ParseTypeModifiersAnnotations(parser); // Parse type and any modifiers and/or attributes
new Block(out _body, parser, this, true); // Parse the body
}
}
protected void ParseTypeModifiersAnnotations(Parser parser)
{
ParseUnusedType(parser, ref _type); // Parse the type from the Unused list
_modifiers = ModifiersHelpers.Parse(parser, this); // Parse any modifiers in reverse from the Unused list
ParseUnusedAnnotations(parser, this, false); // Parse attributes and/or doc comments from the Unused list
}
/// <summary>
/// Determine if the specified comment should be associated with the current code object during parsing.
/// </summary>
public override bool AssociateCommentWhenParsing(CommentBase comment)
{
return true;
}
#endregion
#region /* FORMATTING */
/// <summary>
/// Determine a default of 1 or 2 newlines when adding items to a <see cref="Block"/>.
/// </summary>
public override int DefaultNewLines(CodeObject previous)
{
// Always default to a blank line before a property declaration, unless it's formatted on a
// single line and is preceeded by another single-line property declaration of the same type or a comment.
if (IsSingleLine && ((previous.GetType() == GetType() && previous.IsSingleLine) || previous is Comment))
return 1;
return 2;
}
/// <summary>
/// The number of newlines preceeding the object (0 to N).
/// </summary>
public override int NewLines
{
get { return base.NewLines; }
set
{
// If we're changing to or from zero, also change any prefix attributes
bool isFirstOnLine = (value != 0);
if (_annotations != null && ((!isFirstOnLine && IsFirstOnLine) || (isFirstOnLine && !IsFirstOnLine)))
{
foreach (Annotation annotation in _annotations)
{
if (annotation is Attribute)
annotation.IsFirstOnLine = isFirstOnLine;
}
}
base.NewLines = value;
}
}
/// <summary>
/// Reformat the <see cref="Block"/> body.
/// </summary>
public override void ReformatBlock()
{
base.ReformatBlock();
// If the child accessors have no bodies (they are interface properties/indexers/events),
// then format the entire statement as a single line.
if (_body != null)
{
if (Enumerable.All(_body, delegate(CodeObject codeObject) { return codeObject is AccessorDecl && ((AccessorDecl)codeObject).Body == null; }))
IsSingleLine = true;
}
}
/// <summary>
/// Determines if the code object only requires a single line for display.
/// </summary>
public override bool IsSingleLine
{
get
{
return (base.IsSingleLine && (_type == null || (!_type.IsFirstOnLine && _type.IsSingleLine))
&& (!(_name is Expression) || (!((Expression)_name).IsFirstOnLine && ((Expression)_name).IsSingleLine)));
}
set
{
base.IsSingleLine = value;
if (value)
{
if (_type != null)
{
_type.IsFirstOnLine = false;
_type.IsSingleLine = true;
}
if (_name is Expression)
{
((Expression)_name).IsFirstOnLine = false;
((Expression)_name).IsSingleLine = true;
}
}
}
}
#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);
if (_type != null)
_type.AsText(writer, passFlags | RenderFlags.IsPrefix);
UpdateLineCol(writer, flags);
if (flags.HasFlag(RenderFlags.Description) && _parent is TypeDecl)
{
((TypeDecl)_parent).AsTextName(writer, flags);
Dot.AsTextDot(writer);
}
if (_name is string)
writer.WriteIdentifier((string)_name, flags);
else if (_name is Expression)
((Expression)_name).AsText(writer, passFlags & ~(RenderFlags.Description | RenderFlags.ShowParentTypes));
}
#endregion
}
}