// 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.Collections.Generic;
using Nova.Parsing;
using Nova.Resolving;
namespace Nova.CodeDOM
{
/// <summary>
/// Declares an interface, which includes a name plus a body, along with various optional modifiers.
/// Interfaces define contracts - they have no fields or code.
/// </summary>
/// <remarks>
/// Non-nested interfaces can be only public or internal, and default to internal.
/// Nested interfaces can be any of the 5 access types, and default to private.
/// Other valid modifiers include: new, partial
/// Members of an interface are always public - no modifiers are allowed.
/// Allowed members are: MethodDecls, PropertyDecls, IndexerDecls, DelegateDecls, EventDecls
/// Interfaces can implement other interfaces.
/// The optional base list can contain one or more interfaces.
/// </remarks>
public class InterfaceDecl : BaseListTypeDecl
{
#region /* CONSTRUCTORS */
/// <summary>
/// Create an <see cref="InterfaceDecl"/> with the specified name.
/// </summary>
public InterfaceDecl(string name, Modifiers modifiers)
: base(name, modifiers)
{ }
/// <summary>
/// Create an <see cref="InterfaceDecl"/> with the specified name.
/// </summary>
public InterfaceDecl(string name)
: base(name, Modifiers.None)
{ }
/// <summary>
/// Create an <see cref="InterfaceDecl"/> with the specified name, modifiers, and type parameters.
/// </summary>
public InterfaceDecl(string name, Modifiers modifiers, params TypeParameter[] typeParameters)
: base(name, modifiers, typeParameters)
{ }
/// <summary>
/// Create an <see cref="InterfaceDecl"/> with the specified name, modifiers, and base types.
/// </summary>
public InterfaceDecl(string name, Modifiers modifiers, params Expression[] baseTypes)
: base(name, modifiers, baseTypes)
{ }
#endregion
#region /* PROPERTIES */
/// <summary>
/// Always <c>true</c>.
/// </summary>
public override bool IsInterface
{
get { return true; }
}
/// <summary>
/// The keyword associated with the <see cref="Statement"/>.
/// </summary>
public override string Keyword
{
get { return ParseToken; }
}
#endregion
#region /* METHODS */
/// <summary>
/// Get the method with the specified name and parameter types.
/// </summary>
public override MethodRef GetMethod(string name, params TypeRefBase[] parameterTypes)
{
MethodRef methodRef = base.GetMethod(name, parameterTypes);
if (methodRef == null)
{
// If we didn't find it, search all base interfaces
List<Expression> baseTypes = GetAllBaseTypes();
if (baseTypes != null)
{
foreach (Expression baseTypeExpression in baseTypes)
{
TypeRef baseTypeRef = baseTypeExpression.EvaluateType() as TypeRef;
if (baseTypeRef != null && baseTypeRef.IsInterface)
{
methodRef = baseTypeRef.GetMethod(name, parameterTypes);
if (methodRef != null)
break;
}
}
}
}
return methodRef;
}
/// <summary>
/// Get the property with the specified name.
/// </summary>
public override PropertyRef GetProperty(string name)
{
PropertyRef propertyRef = base.GetProperty(name);
if (propertyRef == null)
{
// If we didn't find it, search all base interfaces
List<Expression> baseTypes = GetAllBaseTypes();
if (baseTypes != null)
{
foreach (Expression baseTypeExpression in baseTypes)
{
TypeRef baseTypeRef = baseTypeExpression.EvaluateType() as TypeRef;
if (baseTypeRef != null && baseTypeRef.IsInterface)
{
propertyRef = baseTypeRef.GetProperty(name);
if (propertyRef != null)
break;
}
}
}
}
return propertyRef;
}
#endregion
#region /* PARSING */
/// <summary>
/// The token used to parse the code object.
/// </summary>
public new const string ParseToken = "interface";
internal static void AddParsePoints()
{
// Interfaces are only valid with a Namespace or 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.
Parser.AddParsePoint(ParseToken, Parse, typeof(IBlock));
}
/// <summary>
/// Parse an <see cref="InterfaceDecl"/>.
/// </summary>
public static InterfaceDecl Parse(Parser parser, CodeObject parent, ParseFlags flags)
{
return new InterfaceDecl(parser, parent);
}
/// <summary>
/// Parse an <see cref="InterfaceDecl"/>.
/// </summary>
protected InterfaceDecl(Parser parser, CodeObject parent)
: base(parser, parent)
{
MoveComments(parser.LastToken); // Get any comments before 'interface'
parser.NextToken(); // Move past 'interface'
ParseNameTypeParameters(parser); // Parse the name and any optional type parameters
ParseModifiersAndAnnotations(parser); // Parse any attributes and/or modifiers
// Move any trailing compiler directives to the Infix1 position (assume we have a base-type list)
MoveAnnotations(AnnotationFlags.IsPostfix, AnnotationFlags.IsInfix1);
ParseBaseTypeList(parser); // Parse the optional base-type list
ParseConstraintClauses(parser); // Parse any constraint clauses
// Move any trailing post annotations on the last base type to the first constraint (if any)
AdjustBaseTypePostComments();
// If we don't have a base-type list, move any trailing compiler directives to the Postfix position
if (_baseTypes == null || _baseTypes.Count == 0)
MoveAnnotations(AnnotationFlags.IsInfix1, AnnotationFlags.IsPostfix);
new Block(out _body, parser, this, true); // Parse the body
// Eat any trailing terminator (they are allowed but not required on non-delegate type declarations)
if (parser.TokenText == ParseTokenTerminator)
parser.NextToken();
}
#endregion
#region /* RESOLVING */
/// <summary>
/// Resolve child code objects in the base class that match the specified name.
/// </summary>
protected override void ResolveRefInBase(string name, Resolver resolver)
{
// Interfaces are implicitly 'object' types, but they have a null base type - instead,
// we must find and search any base interfaces, and then manually search 'object'.
List<Expression> baseTypes = GetAllBaseTypes();
if (baseTypes != null)
{
foreach (Expression baseTypeExpression in baseTypes)
{
TypeRef baseTypeRef = baseTypeExpression.EvaluateType() as TypeRef;
if (baseTypeRef != null && baseTypeRef.IsInterface)
{
baseTypeRef.ResolveRef(name, resolver);
if (resolver.HasCompleteMatch) return; // Abort if we found a match
}
}
}
TypeRef.ResolveRef(TypeRef.ObjectRef, name, resolver);
}
/// <summary>
/// Resolve indexers in any base interfaces.
/// </summary>
protected override void ResolveIndexerRefInBase(Resolver resolver)
{
// Interfaces are implicitly 'object' types, but they have a null base type - instead,
// we must find and search any base interfaces, and then manually search 'object'.
List<Expression> baseTypes = GetAllBaseTypes();
if (baseTypes != null)
{
foreach (Expression baseTypeExpression in baseTypes)
{
TypeRef baseTypeRef = baseTypeExpression.EvaluateType() as TypeRef;
if (baseTypeRef != null && baseTypeRef.IsInterface)
{
baseTypeRef.ResolveIndexerRef(resolver);
if (resolver.HasCompleteMatch) return; // Abort if we found a match
}
}
}
TypeRef.ObjectRef.ResolveIndexerRef(resolver);
}
#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 an interface declaration
return 2;
}
#endregion
}
}