// 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.Generic;
using System.Reflection;
using System.Windows.Media;
using Mono.Cecil;
using ICustomAttributeProvider = Mono.Cecil.ICustomAttributeProvider;
using Nova.CodeDOM;
using Attribute = Nova.CodeDOM.Attribute;
namespace Nova.UI
{
/// <summary>
/// The view model for a <see cref="CodeDOM.Attribute"/>.
/// </summary>
public class AttributeVM : AnnotationVM
{
#region /* STATICS */
internal static void AddViewModelMapping()
{
CreateViewModel.Add(typeof(Attribute),
delegate(CodeObject codeObject, bool isDescription, Dictionary<CodeObject, CodeObjectVM> dictionary) { return new AttributeVM((Attribute)codeObject, dictionary); });
}
#endregion
#region /* FIELDS */
protected ChildListVM<ExpressionVM> _attributeExpressionVMs;
#endregion
#region /* CONSTRUCTORS */
/// <summary>
/// Create a view model instance for the specified <see cref="CodeDOM.Attribute"/>.
/// </summary>
public AttributeVM(Attribute attribute, Dictionary<CodeObject, CodeObjectVM> dictionary)
: base(attribute, dictionary)
{
_attributeExpressionVMs = CreateListVM<Expression, ExpressionVM>(attribute.AttributeExpressions, dictionary);
}
#endregion
#region /* PROPERTIES */
/// <summary>
/// The underlying <see cref="CodeDOM.Attribute"/> model.
/// </summary>
public Attribute Attribute
{
get { return (Attribute)CodeObject; }
}
/// <summary>
/// The list of attribute <see cref="Expression"/>s.
/// </summary>
public ChildListVM<ExpressionVM> AttributeExpressions
{
get { return _attributeExpressionVMs; }
}
/// <summary>
/// True if there are any attribute <see cref="Expression"/>s.
/// </summary>
public bool HasAttributeExpressions
{
get { return (_attributeExpressionVMs != null && _attributeExpressionVMs.Count > 0); }
}
#endregion
#region /* METHODS */
#endregion
#region /* RENDERING */
public static readonly Brush StaticBorderBrush = Brushes.LightPink;
public static readonly Brush StaticBackgroundBrush = Brushes.MistyRose;
public override Brush BorderBrush
{
get { return StaticBorderBrush; }
}
public override Brush BackgroundBrush
{
get { return StaticBackgroundBrush; }
}
public override void Render(CodeRenderer renderer, RenderFlags flags)
{
int newLines = Attribute.NewLines;
bool isPrefix = flags.HasFlag(RenderFlags.IsPrefix);
if (!isPrefix && newLines > 0)
{
if (!flags.HasFlag(RenderFlags.SuppressNewLine))
renderer.NewLines(newLines, ParentVM);
}
else if (flags.HasFlag(RenderFlags.PrefixSpace))
renderer.RenderText(" ", PUNC_BRUSH, ParentVM);
bool increaseIndent = flags.HasFlag(RenderFlags.IncreaseIndent);
if (!flags.HasFlag(RenderFlags.NoBorder))
{
CreateBorder(renderer, flags);
increaseIndent = true; // Always increase the indent level for wrapped lines inside a border
}
RenderFlags passFlags = (flags & RenderFlags.PassMask);
RenderBefore(renderer, passFlags | RenderFlags.IsPrefix);
renderer.RenderText(Attribute.ParseTokenStart, PUNC_BRUSH, this);
// Increase the indent level for any newlines that occur within the declaration if the flag is set
if (increaseIndent)
renderer.IndentOnNewLineBegin(this);
if (Attribute.Target != AttributeTarget.None)
{
renderer.RenderText(AttributeTargetHelpers.AsString(Attribute.Target), KEYWORD_BRUSH, this);
renderer.RenderText(Attribute.ParseTokenTarget + " ", PUNC_BRUSH, this);
}
renderer.RenderList(_attributeExpressionVMs, passFlags | RenderFlags.Attribute, this);
if (increaseIndent)
renderer.IndentOnNewLineEnd(this);
renderer.RenderText(Attribute.ParseTokenEnd, PUNC_BRUSH, this);
RenderEOLComments(renderer, flags);
RenderAfter(renderer, passFlags | (flags & RenderFlags.NoPostAnnotations));
// If we have a border, return to the previous one
if (!flags.HasFlag(RenderFlags.NoBorder))
renderer.ReturnToBorderParent(this);
if (isPrefix)
{
// If this object is rendered as a child prefix object of another, then any whitespace is
// rendered here *after* the object instead of before it.
if (newLines > 0)
renderer.NewLines(newLines, ParentVM);
else
renderer.RenderText(" ", PUNC_BRUSH, ParentVM);
}
}
/// <summary>
/// Helper method to render a collection of Attributes.
/// </summary>
public static void RenderAttributes(CodeRenderer renderer, ChildListVM<AttributeVM> attributes, RenderFlags flags)
{
if (attributes != null && attributes.Count > 0)
{
flags &= ~RenderFlags.Description; // Don't pass description flag through
foreach (AttributeVM attrDecl in attributes)
attrDecl.Render(renderer, flags);
}
}
public static void RenderAttributes(CodeRenderer renderer, ICustomAttributeProvider customAttributeProvider, CodeObjectVM tag, AttributeTarget attributeTarget)
{
int count = 0;
foreach (CustomAttribute attribute in customAttributeProvider.CustomAttributes)
{
// Filter out ExtensionAttributes as they are compiler generated, and displayed in a special manner
TypeReference declaringType = attribute.Constructor.DeclaringType;
if (declaringType != null)
{
if (count > 0)
renderer.RenderText(" ", IDENTIFIER_BRUSH, tag);
renderer.CreateBorder(StaticBorderBrush, StaticBackgroundBrush, tag);
renderer.RenderText(Attribute.ParseTokenStart, PUNC_BRUSH, tag);
if (attributeTarget != AttributeTarget.None)
{
renderer.RenderText(AttributeTargetHelpers.AsString(attributeTarget), KEYWORD_BRUSH, tag);
renderer.RenderText(": ", PUNC_BRUSH, tag);
}
// Render as ConstructorRefVM for nested tooltips
MethodRefVM.RenderMethodDefinitionAsMethodRefVM(renderer, attribute.Constructor.Resolve(), RenderFlags.Attribute, tag);
if (attribute.ConstructorArguments.Count > 0)
{
renderer.RenderText(ParameterDecl.ParseTokenStart, PUNC_BRUSH, tag);
bool isFirst = true;
foreach (CustomAttributeArgument argument in attribute.ConstructorArguments)
{
if (!isFirst)
renderer.RenderText(", ", PUNC_BRUSH, tag);
RenderValue(renderer, argument.Value, tag);
isFirst = false;
}
renderer.RenderText(ParameterDecl.ParseTokenEnd, PUNC_BRUSH, tag);
}
else if (attribute.HasProperties)
{
renderer.RenderText(ParameterDecl.ParseTokenStart, PUNC_BRUSH, tag);
bool isFirst = true;
foreach (Mono.Cecil.CustomAttributeNamedArgument argument in attribute.Properties)
{
if (!isFirst)
renderer.RenderText(", ", PUNC_BRUSH, tag);
renderer.RenderText(argument.Name, IDENTIFIER_BRUSH, tag);
renderer.RenderText(" ", PUNC_BRUSH, tag);
AssignmentVM.RenderOperator(renderer, OPERATOR_BRUSH, tag);
renderer.RenderText(" ", PUNC_BRUSH, tag);
RenderValue(renderer, argument.Argument.Value, tag);
isFirst = false;
}
renderer.RenderText(ParameterDecl.ParseTokenEnd, PUNC_BRUSH, tag);
}
renderer.RenderText(Attribute.ParseTokenEnd, PUNC_BRUSH, tag);
renderer.ReturnToBorderParent(tag);
++count;
}
}
if (count > 0)
renderer.NewLine();
}
public static void RenderAttributes(CodeRenderer renderer, ICustomAttributeProvider customAttributeProvider, CodeObjectVM tag)
{
RenderAttributes(renderer, customAttributeProvider, tag, AttributeTarget.None);
}
public static void RenderAttributes(CodeRenderer renderer, MemberInfo memberInfo, CodeObjectVM tag, AttributeTarget attributeTarget)
{
RenderAttributes(renderer, CustomAttributeData.GetCustomAttributes(memberInfo), tag, attributeTarget);
}
public static void RenderAttributes(CodeRenderer renderer, MemberInfo memberInfo, CodeObjectVM tag)
{
RenderAttributes(renderer, memberInfo, tag, AttributeTarget.None);
}
public static void RenderAttributes(CodeRenderer renderer, ParameterInfo parameterInfo, CodeObjectVM tag, AttributeTarget attributeTarget)
{
RenderAttributes(renderer, CustomAttributeData.GetCustomAttributes(parameterInfo), tag, attributeTarget);
}
public static void RenderAttributes(CodeRenderer renderer, ParameterInfo parameterInfo, CodeObjectVM tag)
{
RenderAttributes(renderer, parameterInfo, tag, AttributeTarget.None);
}
protected static void RenderAttributes(CodeRenderer renderer, IList<CustomAttributeData> attributes, CodeObjectVM tag, AttributeTarget attributeTarget)
{
int count = 0;
// Use the static method so that this works with types from reflection-only assemblies
foreach (CustomAttributeData attribute in attributes)
{
// Filter out ExtensionAttributes as they are compiler generated, and displayed in a special manner
Type declaringType = attribute.Constructor.DeclaringType;
if (declaringType != null)
{
if (count > 0)
renderer.RenderText(" ", IDENTIFIER_BRUSH, tag);
renderer.CreateBorder(StaticBorderBrush, StaticBackgroundBrush, tag);
renderer.RenderText(Attribute.ParseTokenStart, PUNC_BRUSH, tag);
if (attributeTarget != AttributeTarget.None)
{
renderer.RenderText(AttributeTargetHelpers.AsString(attributeTarget), KEYWORD_BRUSH, tag);
renderer.RenderText(": ", PUNC_BRUSH, tag);
}
// Render as ConstructorRefVM for nested tooltips
ConstructorRefVM.RenderConstructorInfoAsConstructorRefVM(renderer, attribute.Constructor, RenderFlags.Attribute, tag);
if (attribute.ConstructorArguments.Count > 0)
{
renderer.RenderText(ParameterDecl.ParseTokenStart, PUNC_BRUSH, tag);
bool isFirst = true;
foreach (CustomAttributeTypedArgument argument in attribute.ConstructorArguments)
{
if (!isFirst)
renderer.RenderText(", ", PUNC_BRUSH, tag);
RenderValue(renderer, argument.Value, tag);
isFirst = false;
}
renderer.RenderText(ParameterDecl.ParseTokenEnd, PUNC_BRUSH, tag);
}
else if (attribute.NamedArguments != null && attribute.NamedArguments.Count > 0)
{
renderer.RenderText(ParameterDecl.ParseTokenStart, PUNC_BRUSH, tag);
bool isFirst = true;
foreach (System.Reflection.CustomAttributeNamedArgument argument in attribute.NamedArguments)
{
if (!isFirst)
renderer.RenderText(", ", PUNC_BRUSH, tag);
renderer.RenderText(argument.MemberInfo.Name, IDENTIFIER_BRUSH, tag);
renderer.RenderText(" ", PUNC_BRUSH, tag);
AssignmentVM.RenderOperator(renderer, OPERATOR_BRUSH, tag);
renderer.RenderText(" ", PUNC_BRUSH, tag);
RenderValue(renderer, argument.TypedValue.Value, tag);
isFirst = false;
}
renderer.RenderText(ParameterDecl.ParseTokenEnd, PUNC_BRUSH, tag);
}
renderer.RenderText(Attribute.ParseTokenEnd, PUNC_BRUSH, tag);
renderer.ReturnToBorderParent(tag);
++count;
}
}
if (count > 0)
renderer.NewLine();
}
protected static void RenderAttributes(CodeRenderer renderer, IList<CustomAttributeData> attributes, CodeObjectVM tag)
{
RenderAttributes(renderer, attributes, tag, AttributeTarget.None);
}
private static void RenderValue(CodeRenderer renderer, object value, CodeObjectVM tag)
{
CodeObject parent = (tag != null ? tag.CodeObject : null);
if (value is TypeDefinition)
{
TypeOf typeOf = new TypeOf(TypeRef.CreateNested((TypeDefinition)value)) { Parent = parent };
CreateVM(typeOf, tag).Render(renderer, RenderFlags.ForceBorder);
}
else if (value is Type)
{
TypeOf typeOf = new TypeOf(TypeRef.CreateNested((Type)value)) { Parent = parent };
CreateVM(typeOf, tag).Render(renderer, RenderFlags.ForceBorder);
}
else
{
if (value is Array)
{
Initializer initializer = new Initializer { Parent = parent };
foreach (object element in (Array)value)
initializer.CreateExpressions().Add(new Literal(element));
CreateVM(initializer, tag).Render(renderer, RenderFlags.ForceBorder);
}
else
{
Literal literal = new Literal(value) { Parent = parent };
CreateVM(literal, tag).Render(renderer, RenderFlags.ForceBorder);
}
}
}
public override void UnRender()
{
ChildListHelpers.UnRender(_attributeExpressionVMs);
base.UnRender();
}
#endregion
}
}