mirror of https://github.com/icsharpcode/ILSpy.git
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
868 lines
34 KiB
868 lines
34 KiB
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) |
|
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) |
|
|
|
using System; |
|
using System.Collections.Generic; |
|
using System.Linq; |
|
using System.Text; |
|
|
|
using ICSharpCode.NRefactory.Ast; |
|
using ICSharpCode.NRefactory.AstBuilder; |
|
using NR = ICSharpCode.NRefactory.Ast; |
|
|
|
namespace ICSharpCode.SharpDevelop.Dom.Refactoring |
|
{ |
|
/// <summary> |
|
/// Provides code generation facilities. |
|
/// </summary> |
|
public abstract class CodeGenerator |
|
{ |
|
protected CodeGenerator() |
|
{ |
|
HostCallback.InitializeCodeGeneratorOptions(this); |
|
} |
|
|
|
#region Dummy Code Generator |
|
public static readonly CodeGenerator DummyCodeGenerator = new DummyCodeGeneratorClass(); |
|
|
|
private class DummyCodeGeneratorClass : CodeGenerator |
|
{ |
|
public override string GenerateCode(AbstractNode node, string indentation) |
|
{ |
|
return " - there is no code generator for this language - "; |
|
} |
|
} |
|
#endregion |
|
|
|
#region DOM -> NRefactory conversion (static) |
|
public static TypeReference ConvertType(IReturnType returnType, ClassFinder context) |
|
{ |
|
if (returnType == null) return TypeReference.Null; |
|
if (returnType is NullReturnType) return TypeReference.Null; |
|
|
|
ArrayReturnType arrayReturnType = returnType.CastToArrayReturnType(); |
|
if (arrayReturnType != null) { |
|
TypeReference typeRef = ConvertType(arrayReturnType.ArrayElementType, context); |
|
int[] rank = typeRef.RankSpecifier ?? new int[0]; |
|
Array.Resize(ref rank, rank.Length + 1); |
|
rank[rank.Length - 1] = arrayReturnType.ArrayDimensions - 1; |
|
typeRef.RankSpecifier = rank; |
|
return typeRef; |
|
} |
|
PointerReturnType pointerReturnType = returnType.CastToDecoratingReturnType<PointerReturnType>(); |
|
if (pointerReturnType != null) { |
|
TypeReference typeRef = ConvertType(pointerReturnType.BaseType, context); |
|
typeRef.PointerNestingLevel++; |
|
return typeRef; |
|
} |
|
|
|
IList<IReturnType> typeArguments = EmptyList<IReturnType>.Instance; |
|
if (returnType.IsConstructedReturnType) { |
|
typeArguments = returnType.CastToConstructedReturnType().TypeArguments; |
|
} |
|
IClass c = returnType.GetUnderlyingClass(); |
|
if (c != null) { |
|
return CreateTypeReference(c, typeArguments, context); |
|
} else { |
|
TypeReference typeRef; |
|
if (IsPrimitiveType(returnType)) |
|
typeRef = new TypeReference(returnType.FullyQualifiedName, true); |
|
else if (context != null && CanUseShortTypeName(returnType, context)) |
|
typeRef = new TypeReference(returnType.Name); |
|
else { |
|
string fullName = returnType.FullyQualifiedName; |
|
if (string.IsNullOrEmpty(fullName)) |
|
fullName = returnType.Name; |
|
typeRef = new TypeReference(fullName); |
|
} |
|
foreach (IReturnType typeArgument in typeArguments) { |
|
typeRef.GenericTypes.Add(ConvertType(typeArgument, context)); |
|
} |
|
return typeRef; |
|
} |
|
} |
|
|
|
static TypeReference CreateTypeReference(IClass c, IList<IReturnType> typeArguments, ClassFinder context) |
|
{ |
|
if (c.DeclaringType != null) { |
|
TypeReference outerClass = CreateTypeReference(c.DeclaringType, typeArguments, context); |
|
List<TypeReference> args = new List<TypeReference>(); |
|
for (int i = c.DeclaringType.TypeParameters.Count; i < Math.Min(c.TypeParameters.Count, typeArguments.Count); i++) { |
|
args.Add(ConvertType(typeArguments[i], context)); |
|
} |
|
return new InnerClassTypeReference(outerClass, c.Name, args); |
|
} else { |
|
TypeReference typeRef; |
|
if (IsPrimitiveType(c.DefaultReturnType)) |
|
typeRef = new TypeReference(c.FullyQualifiedName, true); |
|
else if (context != null && CanUseShortTypeName(c.DefaultReturnType, context)) |
|
typeRef = new TypeReference(c.Name); |
|
else |
|
typeRef = new TypeReference(c.FullyQualifiedName); |
|
for (int i = 0; i < Math.Min(c.TypeParameters.Count, typeArguments.Count); i++) { |
|
typeRef.GenericTypes.Add(ConvertType(typeArguments[i], context)); |
|
} |
|
return typeRef; |
|
} |
|
} |
|
|
|
static bool IsPrimitiveType(IReturnType returnType) |
|
{ |
|
return TypeReference.PrimitiveTypesCSharpReverse.ContainsKey(returnType.FullyQualifiedName); |
|
} |
|
|
|
/// <summary> |
|
/// Returns true if the short name of a type is valid in the given context. |
|
/// Returns false for primitive types because they should be passed around using their |
|
/// fully qualified names to allow the ambience or output visitor to use the intrinsic |
|
/// type name. |
|
/// </summary> |
|
public static bool CanUseShortTypeName(IReturnType returnType, ClassFinder context) |
|
{ |
|
if (returnType == null || context == null) |
|
return false; |
|
IReturnType typeInTargetContext = context.SearchType(returnType.Name, returnType.TypeArgumentCount); |
|
return typeInTargetContext != null |
|
&& typeInTargetContext.FullyQualifiedName == returnType.FullyQualifiedName |
|
&& typeInTargetContext.TypeArgumentCount == returnType.TypeArgumentCount; |
|
} |
|
|
|
public static Modifiers ConvertModifier(ModifierEnum modifiers, ClassFinder targetContext) |
|
{ |
|
if (targetContext != null && targetContext.ProjectContent != null && targetContext.CallingClass != null) { |
|
if (targetContext.ProjectContent.Language.IsClassWithImplicitlyStaticMembers(targetContext.CallingClass)) { |
|
return ((Modifiers)modifiers) & ~Modifiers.Static; |
|
} |
|
} |
|
if (modifiers.HasFlag(ModifierEnum.Static)) |
|
modifiers &= ~(ModifierEnum.Abstract | ModifierEnum.Sealed); |
|
return (Modifiers)modifiers; |
|
} |
|
|
|
public static NR.ParameterModifiers ConvertModifier(Dom.ParameterModifiers m) |
|
{ |
|
return (NR.ParameterModifiers)m; |
|
} |
|
|
|
public static UsingDeclaration ConvertUsing(IUsing u) |
|
{ |
|
List<Using> usings = new List<Using>(); |
|
foreach (string name in u.Usings) { |
|
usings.Add(new Using(name)); |
|
} |
|
if (u.HasAliases) { |
|
foreach (KeyValuePair<string, IReturnType> pair in u.Aliases) { |
|
usings.Add(new Using(pair.Key, ConvertType(pair.Value, null))); |
|
} |
|
} |
|
return new UsingDeclaration(usings); |
|
} |
|
|
|
public static List<ParameterDeclarationExpression> ConvertParameters(IList<IParameter> parameters, ClassFinder targetContext) |
|
{ |
|
List<ParameterDeclarationExpression> l = new List<ParameterDeclarationExpression>(parameters.Count); |
|
foreach (IParameter p in parameters) { |
|
ParameterDeclarationExpression pd = new ParameterDeclarationExpression(ConvertType(p.ReturnType, targetContext), |
|
p.Name, |
|
ConvertModifier(p.Modifiers)); |
|
pd.Attributes = ConvertAttributes(p.Attributes, targetContext); |
|
l.Add(pd); |
|
} |
|
return l; |
|
} |
|
|
|
public static List<AttributeSection> ConvertAttributes(IList<IAttribute> attributes, ClassFinder targetContext) |
|
{ |
|
AttributeSection sec = new AttributeSection(); |
|
foreach (IAttribute att in attributes) { |
|
sec.Attributes.Add(new ICSharpCode.NRefactory.Ast.Attribute( |
|
ConvertType(att.AttributeType, targetContext).Type, |
|
att.PositionalArguments.Select(o => (Expression)new PrimitiveExpression(o)).ToList(), |
|
att.NamedArguments.Select(p => new NamedArgumentExpression(p.Key, new PrimitiveExpression(p.Value))).ToList() |
|
)); |
|
} |
|
List<AttributeSection> resultList = new List<AttributeSection>(1); |
|
if (sec.Attributes.Count > 0) |
|
resultList.Add(sec); |
|
return resultList; |
|
} |
|
|
|
public static List<TemplateDefinition> ConvertTemplates(IList<ITypeParameter> l, ClassFinder targetContext) |
|
{ |
|
List<TemplateDefinition> o = new List<TemplateDefinition>(l.Count); |
|
foreach (ITypeParameter p in l) { |
|
TemplateDefinition td = new TemplateDefinition(p.Name, ConvertAttributes(p.Attributes, targetContext)); |
|
foreach (IReturnType rt in p.Constraints) { |
|
td.Bases.Add(ConvertType(rt, targetContext)); |
|
} |
|
o.Add(td); |
|
} |
|
return o; |
|
} |
|
|
|
public static BlockStatement CreateNotImplementedBlock() |
|
{ |
|
BlockStatement b = new BlockStatement(); |
|
b.Throw(new TypeReference("NotImplementedException").New()); |
|
return b; |
|
} |
|
|
|
public static AttributedNode ConvertMember(IMethod m, ClassFinder targetContext) |
|
{ |
|
if (m.IsConstructor) { |
|
return new ConstructorDeclaration(m.Name, |
|
ConvertModifier(m.Modifiers, targetContext), |
|
ConvertParameters(m.Parameters, targetContext), |
|
ConvertAttributes(m.Attributes, targetContext)) { |
|
Body = CreateNotImplementedBlock() |
|
}; |
|
} else if (m.Name == "#dtor") { // TODO : maybe add IsDestructor property? |
|
return new DestructorDeclaration(m.Name, |
|
ConvertModifier(m.Modifiers, targetContext), |
|
ConvertAttributes(m.Attributes, targetContext)) { |
|
Body = CreateNotImplementedBlock() |
|
}; |
|
} else { |
|
return new MethodDeclaration { |
|
Name = m.Name, |
|
Modifier = ConvertModifier(m.Modifiers, targetContext), |
|
TypeReference = ConvertType(m.ReturnType, targetContext), |
|
Parameters = ConvertParameters(m.Parameters, targetContext), |
|
Attributes = ConvertAttributes(m.Attributes, targetContext), |
|
Templates = ConvertTemplates(m.TypeParameters, targetContext), |
|
Body = m.Modifiers.HasFlag(ModifierEnum.Extern) ? null : CreateNotImplementedBlock(), |
|
IsExtensionMethod = m.IsExtensionMethod, |
|
InterfaceImplementations = ConvertInterfaceImplementations(m.InterfaceImplementations, targetContext) |
|
}; |
|
} |
|
} |
|
|
|
public static List<InterfaceImplementation> ConvertInterfaceImplementations(IEnumerable<ExplicitInterfaceImplementation> items, ClassFinder targetContext) |
|
{ |
|
return items |
|
.Select(i => new InterfaceImplementation(ConvertType(i.InterfaceReference, targetContext), i.MemberName)) |
|
.ToList(); |
|
} |
|
|
|
public static AttributedNode ConvertMember(IMember m, ClassFinder targetContext) |
|
{ |
|
if (m == null) |
|
throw new ArgumentNullException("m"); |
|
if (m is IProperty) |
|
return ConvertMember((IProperty)m, targetContext); |
|
else if (m is IMethod) |
|
return ConvertMember((IMethod)m, targetContext); |
|
else if (m is IEvent) |
|
return ConvertMember((IEvent)m, targetContext); |
|
else if (m is IField) |
|
return ConvertMember((IField)m, targetContext); |
|
else |
|
throw new ArgumentException("Unknown member: " + m.GetType().FullName); |
|
} |
|
|
|
public static PropertyDeclaration ConvertMember(IProperty p, ClassFinder targetContext) |
|
{ |
|
PropertyDeclaration md = new PropertyDeclaration(ConvertModifier(p.Modifiers, targetContext), |
|
ConvertAttributes(p.Attributes, targetContext), |
|
p.Name, |
|
ConvertParameters(p.Parameters, targetContext)); |
|
md.TypeReference = ConvertType(p.ReturnType, targetContext); |
|
md.InterfaceImplementations = ConvertInterfaceImplementations(p.InterfaceImplementations, targetContext); |
|
if (p.CanGet) { |
|
md.GetRegion = new PropertyGetRegion(p.Modifiers.HasFlag(ModifierEnum.Extern) ? null : CreateNotImplementedBlock(), null); |
|
md.GetRegion.Modifier = ConvertModifier(p.GetterModifiers, null); |
|
} |
|
if (p.CanSet) { |
|
md.SetRegion = new PropertySetRegion(p.Modifiers.HasFlag(ModifierEnum.Extern) ? null : CreateNotImplementedBlock(), null); |
|
md.SetRegion.Modifier = ConvertModifier(p.SetterModifiers, null); |
|
} |
|
return md; |
|
} |
|
|
|
public static FieldDeclaration ConvertMember(IField f, ClassFinder targetContext) |
|
{ |
|
TypeReference type = ConvertType(f.ReturnType, targetContext); |
|
|
|
FieldDeclaration fd = new FieldDeclaration(ConvertAttributes(f.Attributes, targetContext), |
|
type, ConvertModifier(f.Modifiers, targetContext)); |
|
|
|
VariableDeclaration vd = new VariableDeclaration(f.Name, null, type); |
|
fd.Fields.Add(vd); |
|
|
|
|
|
if (f.IsConst && f.DeclaringType.ClassType != ClassType.Enum) |
|
vd.Initializer = ExpressionBuilder.CreateDefaultValueForType(type); |
|
else if (f.Modifiers.HasFlag(ModifierEnum.Fixed)) { |
|
if (f.ReturnType.IsArrayReturnType) |
|
fd.TypeReference = ConvertType(f.ReturnType.CastToArrayReturnType().ArrayElementType, targetContext); |
|
vd.FixedArrayInitialization = new PrimitiveExpression(1); |
|
} |
|
|
|
return fd; |
|
} |
|
|
|
public static EventDeclaration ConvertMember(IEvent e, ClassFinder targetContext) |
|
{ |
|
return new EventDeclaration { |
|
TypeReference = ConvertType(e.ReturnType, targetContext), |
|
Name = e.Name, |
|
Modifier = ConvertModifier(e.Modifiers, targetContext), |
|
Attributes = ConvertAttributes(e.Attributes, targetContext), |
|
InterfaceImplementations = ConvertInterfaceImplementations(e.InterfaceImplementations, targetContext) |
|
|
|
}; |
|
} |
|
|
|
public static AttributedNode ConvertClass(IClass c, ClassFinder targetContext) |
|
{ |
|
if (c.ClassType == Dom.ClassType.Delegate) { |
|
IMethod invoke = c.Methods.First(m => m.Name == "Invoke"); |
|
|
|
var d = new DelegateDeclaration(ConvertModifier(c.Modifiers, targetContext), ConvertAttributes(c.Attributes, targetContext)) { |
|
Name = c.Name, |
|
Parameters = ConvertParameters(invoke.Parameters, targetContext), |
|
ReturnType = ConvertType(invoke.ReturnType, targetContext), |
|
Templates = ConvertTemplates(c.TypeParameters, targetContext) |
|
}; |
|
|
|
return d; |
|
} else { |
|
var t = new TypeDeclaration(ConvertModifier(c.Modifiers, targetContext), ConvertAttributes(c.Attributes, targetContext)) { |
|
Type = (NRefactory.Ast.ClassType)c.ClassType, |
|
BaseTypes = c.BaseTypes.Select(type => ConvertType(type, targetContext)).ToList(), |
|
Templates = ConvertTemplates(c.TypeParameters, targetContext), |
|
Name = c.Name |
|
}; |
|
|
|
AttributedNode[] members = c.AllMembers.Select(m => ConvertMember(m, targetContext)).ToArray(); |
|
|
|
if (c.ClassType == ClassType.Interface) { |
|
foreach (MethodDeclaration node in members.OfType<MethodDeclaration>()) { |
|
node.Modifier &= ~(Modifiers.Public | Modifiers.Private | Modifiers.Protected | Modifiers.Internal); |
|
node.Body = null; |
|
} |
|
foreach (PropertyDeclaration node in members.OfType<PropertyDeclaration>()) { |
|
node.Modifier &= ~(Modifiers.Public | Modifiers.Private | Modifiers.Protected | Modifiers.Internal); |
|
node.GetRegion.Block = null; |
|
node.SetRegion.Block = null; |
|
} |
|
foreach (EventDeclaration node in members.OfType<EventDeclaration>()) { |
|
node.Modifier &= ~(Modifiers.Public | Modifiers.Private | Modifiers.Protected | Modifiers.Internal); |
|
} |
|
} |
|
|
|
t.Children.AddRange(members); |
|
t.Children.AddRange(c.InnerClasses.Select(c2 => ConvertClass(c2, targetContext))); |
|
|
|
return t; |
|
} |
|
} |
|
#endregion |
|
|
|
readonly CodeGeneratorOptions options = new CodeGeneratorOptions(); |
|
|
|
public CodeGeneratorOptions Options { |
|
get { return options; } |
|
} |
|
|
|
#region Code generation / insertion |
|
public virtual void InsertCodeAfter(IClass @class, IRefactoringDocument document, params AbstractNode[] nodes) |
|
{ |
|
InsertCodeAfter(@class.BodyRegion.EndLine, document, |
|
GetIndentation(document, @class.BodyRegion.BeginLine), nodes); |
|
} |
|
|
|
public virtual void InsertCodeAfter(IMember member, IRefactoringDocument document, params AbstractNode[] nodes) |
|
{ |
|
if (member is IMethodOrProperty) { |
|
InsertCodeAfter(((IMethodOrProperty)member).BodyRegion.EndLine, document, |
|
GetIndentation(document, member.Region.BeginLine), nodes); |
|
} else { |
|
int line = member.Region.EndLine; |
|
// VB uses the position after the EOL as end location for fields, so insert after |
|
// the previous line if the end position is pointing to the start of a line. |
|
if (member.Region.EndColumn == 1) |
|
line--; |
|
InsertCodeAfter(line, document, |
|
GetIndentation(document, member.Region.BeginLine), nodes); |
|
} |
|
} |
|
|
|
public virtual void InsertCodeAtEnd(DomRegion region, IRefactoringDocument document, params AbstractNode[] nodes) |
|
{ |
|
InsertCodeAfter(region.EndLine - 1, document, |
|
GetIndentation(document, region.BeginLine) + options.IndentString, nodes); |
|
} |
|
|
|
public virtual void InsertCodeInClass(IClass c, IRefactoringDocument document, int targetLine, params AbstractNode[] nodes) |
|
{ |
|
InsertCodeAfter(targetLine, document, |
|
GetIndentation(document, c.Region.BeginLine) + options.IndentString, false, nodes); |
|
} |
|
|
|
protected string GetIndentation(IRefactoringDocument document, int line) |
|
{ |
|
string lineText = document.GetLine(line).Text; |
|
return lineText.Substring(0, lineText.Length - lineText.TrimStart().Length); |
|
} |
|
|
|
/// <summary> |
|
/// Generates code for <paramref name="nodes"/> and inserts it into <paramref name="document"/> |
|
/// after the line <paramref name="insertLine"/>. |
|
/// </summary> |
|
protected void InsertCodeAfter(int insertLine, IRefactoringDocument document, string indentation, params AbstractNode[] nodes) |
|
{ |
|
InsertCodeAfter(insertLine, document, indentation, true, nodes); |
|
} |
|
|
|
/// <summary> |
|
/// Generates code for <paramref name="nodes"/> and inserts it into <paramref name="document"/> |
|
/// after the line <paramref name="insertLine"/>. |
|
/// </summary> |
|
protected void InsertCodeAfter(int insertLine, IRefactoringDocument document, string indentation, bool startWithEmptyLine, params AbstractNode[] nodes) |
|
{ |
|
StringBuilder b = new StringBuilder(); |
|
for (int i = 0; i < nodes.Length; i++) { |
|
if (options.EmptyLinesBetweenMembers) { |
|
if (startWithEmptyLine || i > 0) { |
|
b.AppendLine(indentation); |
|
} |
|
} |
|
b.Append(GenerateCode(nodes[i], indentation)); |
|
} |
|
if (insertLine < document.TotalNumberOfLines) { |
|
IRefactoringDocumentLine lineSegment = document.GetLine(insertLine + 1); |
|
document.Insert(lineSegment.Offset, b.ToString()); |
|
} else { |
|
b.Insert(0, Environment.NewLine); |
|
document.Insert(document.TextLength, b.ToString()); |
|
} |
|
} |
|
|
|
/// <summary> |
|
/// Generates code for the NRefactory node. |
|
/// </summary> |
|
public abstract string GenerateCode(AbstractNode node, string indentation); |
|
#endregion |
|
|
|
#region Generate property |
|
public virtual string GetPropertyName(string fieldName) |
|
{ |
|
if (string.IsNullOrEmpty(fieldName)) |
|
return fieldName; |
|
if (fieldName.StartsWith("_") && fieldName.Length > 1) |
|
return Char.ToUpper(fieldName[1]) + fieldName.Substring(2); |
|
else if (fieldName.StartsWith("m_") && fieldName.Length > 2) |
|
return Char.ToUpper(fieldName[2]) + fieldName.Substring(3); |
|
else |
|
return Char.ToUpper(fieldName[0]) + fieldName.Substring(1); |
|
} |
|
|
|
public virtual string GetParameterName(string fieldName) |
|
{ |
|
if (string.IsNullOrEmpty(fieldName)) |
|
return fieldName; |
|
if (fieldName.StartsWith("_") && fieldName.Length > 1) |
|
return Char.ToLower(fieldName[1]) + fieldName.Substring(2); |
|
else if (fieldName.StartsWith("m_") && fieldName.Length > 2) |
|
return Char.ToLower(fieldName[2]) + fieldName.Substring(3); |
|
else |
|
return Char.ToLower(fieldName[0]) + fieldName.Substring(1); |
|
} |
|
|
|
public virtual string GetFieldName(string propertyName) |
|
{ |
|
if (string.IsNullOrEmpty(propertyName)) |
|
return propertyName; |
|
string newName = Char.ToLower(propertyName[0]) + propertyName.Substring(1); |
|
if (newName == propertyName) |
|
return "_" + newName; |
|
else |
|
return newName; |
|
} |
|
|
|
public virtual PropertyDeclaration CreateProperty(IField field, bool createGetter, bool createSetter) |
|
{ |
|
ClassFinder targetContext = new ClassFinder(field); |
|
string name = GetPropertyName(field.Name); |
|
PropertyDeclaration property = new PropertyDeclaration(ConvertModifier(field.Modifiers, targetContext), |
|
null, |
|
name, |
|
null); |
|
property.TypeReference = ConvertType(field.ReturnType, new ClassFinder(field)); |
|
if (createGetter) { |
|
BlockStatement block = new BlockStatement(); |
|
block.Return(new IdentifierExpression(field.Name)); |
|
property.GetRegion = new PropertyGetRegion(block, null); |
|
} |
|
if (createSetter) { |
|
BlockStatement block = new BlockStatement(); |
|
block.Assign(new IdentifierExpression(field.Name), new IdentifierExpression("value")); |
|
property.SetRegion = new PropertySetRegion(block, null); |
|
} |
|
|
|
property.Modifier = Modifiers.Public | (property.Modifier & Modifiers.Static); |
|
return property; |
|
} |
|
#endregion |
|
|
|
#region Generate Changed Event |
|
public virtual void CreateChangedEvent(IProperty property, IRefactoringDocument document) |
|
{ |
|
ClassFinder targetContext = new ClassFinder(property); |
|
string name = property.Name + "Changed"; |
|
EventDeclaration ed = new EventDeclaration { |
|
TypeReference = new TypeReference("EventHandler"), |
|
Name = name, |
|
Modifier = ConvertModifier(property.Modifiers & (ModifierEnum.VisibilityMask | ModifierEnum.Static), targetContext), |
|
}; |
|
InsertCodeAfter(property, document, ed); |
|
|
|
List<Expression> arguments = new List<Expression>(2); |
|
if (property.IsStatic) |
|
arguments.Add(new PrimitiveExpression(null, "null")); |
|
else |
|
arguments.Add(new ThisReferenceExpression()); |
|
arguments.Add(new IdentifierExpression("EventArgs").Member("Empty")); |
|
InsertCodeAtEnd(property.SetterRegion, document, |
|
new RaiseEventStatement(name, arguments)); |
|
} |
|
#endregion |
|
|
|
#region Generate OnEventMethod |
|
public virtual MethodDeclaration CreateOnEventMethod(IEvent e) |
|
{ |
|
ClassFinder context = new ClassFinder(e); |
|
List<ParameterDeclarationExpression> parameters = new List<ParameterDeclarationExpression>(); |
|
bool sender = false; |
|
if (e.ReturnType != null) { |
|
IMethod invoke = e.ReturnType.GetMethods().Find(delegate(IMethod m) { return m.Name=="Invoke"; }); |
|
if (invoke != null) { |
|
foreach (IParameter param in invoke.Parameters) { |
|
parameters.Add(new ParameterDeclarationExpression(ConvertType(param.ReturnType, context), param.Name)); |
|
} |
|
if (parameters.Count > 0 && string.Equals(parameters[0].ParameterName, "sender", StringComparison.InvariantCultureIgnoreCase)) { |
|
sender = true; |
|
parameters.RemoveAt(0); |
|
} |
|
} |
|
} |
|
|
|
ModifierEnum modifier; |
|
if (e.IsStatic) |
|
modifier = ModifierEnum.Private | ModifierEnum.Static; |
|
else if (e.DeclaringType.IsSealed) |
|
modifier = ModifierEnum.Protected; |
|
else |
|
modifier = ModifierEnum.Protected | ModifierEnum.Virtual; |
|
MethodDeclaration method = new MethodDeclaration { |
|
Name = "On" + e.Name, |
|
Modifier = ConvertModifier(modifier, context), |
|
TypeReference = new TypeReference("System.Void", true), |
|
Parameters = parameters |
|
}; |
|
|
|
List<Expression> arguments = new List<Expression>(); |
|
if (sender) { |
|
if (e.IsStatic) |
|
arguments.Add(new PrimitiveExpression(null, "null")); |
|
else |
|
arguments.Add(new ThisReferenceExpression()); |
|
} |
|
foreach (ParameterDeclarationExpression param in parameters) { |
|
arguments.Add(new IdentifierExpression(param.ParameterName)); |
|
} |
|
method.Body = new BlockStatement(); |
|
method.Body.AddChild(new RaiseEventStatement(e.Name, arguments)); |
|
|
|
return method; |
|
} |
|
#endregion |
|
|
|
#region Interface implementation |
|
protected string GetInterfaceName(IReturnType interf, IMember member, ClassFinder context) |
|
{ |
|
if (CanUseShortTypeName(member.DeclaringType.DefaultReturnType, context)) |
|
return member.DeclaringType.Name; |
|
else |
|
return member.DeclaringType.FullyQualifiedName; |
|
} |
|
|
|
public virtual void ImplementInterface(IReturnType interf, IRefactoringDocument document, bool explicitImpl, IClass targetClass) |
|
{ |
|
List<AbstractNode> nodes = new List<AbstractNode>(); |
|
ImplementInterface(nodes, interf, explicitImpl, targetClass); |
|
InsertCodeAtEnd(targetClass.Region, document, nodes.ToArray()); |
|
} |
|
|
|
static bool InterfaceMemberAlreadyImplementedParametersAreIdentical(IMember a, IMember b) |
|
{ |
|
if (a is IMethodOrProperty && b is IMethodOrProperty) { |
|
return DiffUtility.Compare(((IMethodOrProperty)a).Parameters, |
|
((IMethodOrProperty)b).Parameters) == 0; |
|
} else { |
|
return true; |
|
} |
|
} |
|
|
|
static T CloneAndAddExplicitImpl<T>(T member, IClass targetClass) |
|
where T : class, IMember |
|
{ |
|
T copy = (T)member.Clone(); |
|
copy.DeclaringTypeReference = targetClass.DefaultReturnType; |
|
copy.InterfaceImplementations.Add(new ExplicitInterfaceImplementation(member.DeclaringTypeReference, member.Name)); |
|
return copy; |
|
} |
|
|
|
// FIXME this whole method could be probably replaced by DOM.ExtensionMethodsPublic.HasMember |
|
public static bool InterfaceMemberAlreadyImplemented<T>(IEnumerable<T> existingMembers, T interfaceMember, |
|
out bool requireAlternativeImplementation) |
|
where T : class, IMember |
|
{ |
|
IReturnType interf = interfaceMember.DeclaringTypeReference; |
|
requireAlternativeImplementation = false; |
|
foreach (T existing in existingMembers) { |
|
StringComparer nameComparer = existing.DeclaringType.ProjectContent.Language.NameComparer; |
|
|
|
// if existing has same name as interfaceMember, and for methods the parameter list must also be identical: |
|
if (nameComparer.Equals(existing.Name, interfaceMember.Name)) { |
|
if (InterfaceMemberAlreadyImplementedParametersAreIdentical(existing, interfaceMember)) { |
|
// implicit implementation found |
|
if (object.Equals(existing.ReturnType, interfaceMember.ReturnType)) { |
|
return true; |
|
} else { |
|
requireAlternativeImplementation = true; |
|
} |
|
} |
|
} else { |
|
foreach (ExplicitInterfaceImplementation eii in existing.InterfaceImplementations) { |
|
if (object.Equals(eii.InterfaceReference, interf) && nameComparer.Equals(eii.MemberName, interfaceMember.Name)) { |
|
if (InterfaceMemberAlreadyImplementedParametersAreIdentical(existing, interfaceMember)) { |
|
// explicit implementation found |
|
if (object.Equals(existing.ReturnType, interfaceMember.ReturnType)) { |
|
return true; |
|
} else { |
|
requireAlternativeImplementation = true; |
|
} |
|
} |
|
} |
|
} |
|
} |
|
} |
|
return false; |
|
} |
|
|
|
static InterfaceImplementation CreateInterfaceImplementation(IMember interfaceMember, ClassFinder context) |
|
{ |
|
return new InterfaceImplementation(ConvertType(interfaceMember.DeclaringTypeReference, context), interfaceMember.Name); |
|
} |
|
|
|
/// <summary> |
|
/// Adds the methods implementing the <paramref name="interf"/> to the list |
|
/// <paramref name="nodes"/>. |
|
/// </summary> |
|
public virtual void ImplementInterface(IList<AbstractNode> nodes, IReturnType interf, bool explicitImpl, IClass targetClass) |
|
{ |
|
ClassFinder context = new ClassFinder(targetClass, targetClass.Region.BeginLine + 1, 0); |
|
Modifiers implicitImplModifier = ConvertModifier(ModifierEnum.Public, context); |
|
Modifiers explicitImplModifier = ConvertModifier(context.Language.ExplicitInterfaceImplementationIsPrivateScope ? ModifierEnum.None : ModifierEnum.Public, context); |
|
List<IEvent> targetClassEvents = targetClass.DefaultReturnType.GetEvents(); |
|
bool requireAlternativeImplementation; |
|
foreach (IEvent e in interf.GetEvents()) { |
|
if (!InterfaceMemberAlreadyImplemented(targetClassEvents, e, out requireAlternativeImplementation)) { |
|
EventDeclaration ed = ConvertMember(e, context); |
|
ed.Attributes.Clear(); |
|
if (explicitImpl || requireAlternativeImplementation) { |
|
ed.InterfaceImplementations.Add(CreateInterfaceImplementation(e, context)); |
|
|
|
if (context.Language.RequiresAddRemoveRegionInExplicitInterfaceImplementation) { |
|
ed.AddRegion = new EventAddRegion(null); |
|
ed.AddRegion.Block = CreateNotImplementedBlock(); |
|
ed.RemoveRegion = new EventRemoveRegion(null); |
|
ed.RemoveRegion.Block = CreateNotImplementedBlock(); |
|
} |
|
|
|
targetClassEvents.Add(CloneAndAddExplicitImpl(e, targetClass)); |
|
ed.Modifier = explicitImplModifier; |
|
} else { |
|
targetClassEvents.Add(e); |
|
ed.Modifier = implicitImplModifier; |
|
} |
|
nodes.Add(ed); |
|
} |
|
} |
|
List<IProperty> targetClassProperties = targetClass.DefaultReturnType.GetProperties(); |
|
foreach (IProperty p in interf.GetProperties()) { |
|
if (!InterfaceMemberAlreadyImplemented(targetClassProperties, p, out requireAlternativeImplementation)) { |
|
AttributedNode pd = ConvertMember(p, context); |
|
pd.Attributes.Clear(); |
|
if (explicitImpl || requireAlternativeImplementation) { |
|
InterfaceImplementation impl = CreateInterfaceImplementation(p, context); |
|
((PropertyDeclaration)pd).InterfaceImplementations.Add(impl); |
|
targetClassProperties.Add(CloneAndAddExplicitImpl(p, targetClass)); |
|
pd.Modifier = explicitImplModifier; |
|
} else { |
|
targetClassProperties.Add(p); |
|
pd.Modifier = implicitImplModifier; |
|
} |
|
nodes.Add(pd); |
|
} |
|
} |
|
List<IMethod> targetClassMethods = targetClass.DefaultReturnType.GetMethods(); |
|
foreach (IMethod m in interf.GetMethods()) { |
|
if (!InterfaceMemberAlreadyImplemented(targetClassMethods, m, out requireAlternativeImplementation)) { |
|
MethodDeclaration md = ConvertMember(m, context) as MethodDeclaration; |
|
md.Attributes.Clear(); |
|
if (md != null) { |
|
if (explicitImpl || requireAlternativeImplementation) { |
|
md.InterfaceImplementations.Add(CreateInterfaceImplementation(m, context)); |
|
targetClassMethods.Add(CloneAndAddExplicitImpl(m, targetClass)); |
|
md.Modifier = explicitImplModifier; |
|
} else { |
|
targetClassMethods.Add(m); |
|
md.Modifier = implicitImplModifier; |
|
} |
|
nodes.Add(md); |
|
} |
|
} |
|
} |
|
} |
|
#endregion |
|
|
|
#region Abstract class implementation |
|
public static void ImplementAbstractClass(IRefactoringDocument doc, IClass target, IReturnType abstractClass) |
|
{ |
|
CodeGenerator generator = target.ProjectContent.Language.CodeGenerator; |
|
var pos = doc.OffsetToPosition(doc.PositionToOffset(target.BodyRegion.EndLine, target.BodyRegion.EndColumn) - 1); |
|
ClassFinder context = new ClassFinder(target, pos.Line, pos.Column); |
|
|
|
foreach (IMember member in MemberLookupHelper.GetAccessibleMembers(abstractClass, target, LanguageProperties.CSharp, true) |
|
.Where(m => m.IsAbstract && !target.HasMember(m))) { |
|
generator.InsertCodeAtEnd(target.BodyRegion, doc, generator.GetOverridingMethod(member, context)); |
|
} |
|
} |
|
#endregion |
|
|
|
#region Override member |
|
public virtual AttributedNode GetOverridingMethod(IMember baseMember, ClassFinder targetContext) |
|
{ |
|
AbstractMember newMember = (AbstractMember)baseMember.Clone(); |
|
newMember.Modifiers &= ~(ModifierEnum.Virtual | ModifierEnum.Abstract); |
|
newMember.Modifiers |= ModifierEnum.Override; |
|
// set modifiers be before calling convert so that a body is generated |
|
AttributedNode node = ConvertMember(newMember, targetContext); |
|
node.Attributes.Clear(); // don't copy over attributes |
|
|
|
if (!baseMember.IsAbstract) { |
|
// replace the method/property body with a call to the base method/property |
|
MethodDeclaration method = node as MethodDeclaration; |
|
if (method != null) { |
|
method.Body.Children.Clear(); |
|
if (method.TypeReference.Type == "System.Void") { |
|
method.Body.AddChild(new ExpressionStatement(CreateForwardingMethodCall(method))); |
|
} else { |
|
method.Body.AddChild(new ReturnStatement(CreateForwardingMethodCall(method))); |
|
} |
|
} |
|
PropertyDeclaration property = node as PropertyDeclaration; |
|
if (property != null) { |
|
Expression field = new BaseReferenceExpression().Member(property.Name); |
|
if (!property.GetRegion.Block.IsNull) { |
|
property.GetRegion.Block.Children.Clear(); |
|
property.GetRegion.Block.Return(field); |
|
} |
|
if (!property.SetRegion.Block.IsNull) { |
|
property.SetRegion.Block.Children.Clear(); |
|
property.SetRegion.Block.Assign(field, new IdentifierExpression("value")); |
|
} |
|
} |
|
} |
|
return node; |
|
} |
|
|
|
static InvocationExpression CreateForwardingMethodCall(MethodDeclaration method) |
|
{ |
|
Expression methodName = new MemberReferenceExpression(new BaseReferenceExpression(), |
|
method.Name); |
|
InvocationExpression ie = new InvocationExpression(methodName, null); |
|
foreach (ParameterDeclarationExpression param in method.Parameters) { |
|
Expression expr = new IdentifierExpression(param.ParameterName); |
|
if (param.ParamModifier == NR.ParameterModifiers.Ref) { |
|
expr = new DirectionExpression(FieldDirection.Ref, expr); |
|
} else if (param.ParamModifier == NR.ParameterModifiers.Out) { |
|
expr = new DirectionExpression(FieldDirection.Out, expr); |
|
} |
|
ie.Arguments.Add(expr); |
|
} |
|
return ie; |
|
} |
|
#endregion |
|
|
|
#region Using statements |
|
public virtual void ReplaceUsings(IRefactoringDocument document, IList<IUsing> oldUsings, IList<IUsing> newUsings) |
|
{ |
|
if (oldUsings.Count == newUsings.Count) { |
|
bool identical = true; |
|
for (int i = 0; i < oldUsings.Count; i++) { |
|
if (oldUsings[i] != newUsings[i]) { |
|
identical = false; |
|
break; |
|
} |
|
} |
|
if (identical) return; |
|
} |
|
|
|
int firstLine = int.MaxValue; |
|
List<KeyValuePair<int, int>> regions = new List<KeyValuePair<int, int>>(); |
|
foreach (IUsing u in oldUsings) { |
|
if (u.Region.BeginLine < firstLine) |
|
firstLine = u.Region.BeginLine; |
|
int st = document.PositionToOffset(u.Region.BeginLine, u.Region.BeginColumn); |
|
int en = document.PositionToOffset(u.Region.EndLine, u.Region.EndColumn); |
|
regions.Add(new KeyValuePair<int, int>(st, en - st)); |
|
} |
|
|
|
regions.Sort(delegate(KeyValuePair<int, int> a, KeyValuePair<int, int> b) { |
|
return a.Key.CompareTo(b.Key); |
|
}); |
|
int insertionOffset = regions.Count == 0 ? 0 : regions[0].Key; |
|
string indentation; |
|
if (firstLine != int.MaxValue) { |
|
indentation = GetIndentation(document, firstLine); |
|
insertionOffset -= indentation.Length; |
|
} else { |
|
indentation = ""; |
|
} |
|
|
|
document.StartUndoableAction(); |
|
for (int i = regions.Count - 1; i >= 0; i--) { |
|
document.Remove(regions[i].Key, regions[i].Value); |
|
} |
|
int lastNewLine = insertionOffset; |
|
for (int i = insertionOffset; i < document.TextLength; i++) { |
|
char c = document.GetCharAt(i); |
|
if (!char.IsWhiteSpace(c)) |
|
break; |
|
if (c == '\n') { |
|
if (i > 0 && document.GetCharAt(i - 1) == '\r') |
|
lastNewLine = i - 1; |
|
else |
|
lastNewLine = i; |
|
} |
|
} |
|
if (lastNewLine != insertionOffset) { |
|
document.Remove(insertionOffset, lastNewLine - insertionOffset); |
|
} |
|
StringBuilder txt = new StringBuilder(); |
|
foreach (IUsing us in newUsings) { |
|
if (us == null) |
|
txt.AppendLine(indentation); |
|
else |
|
txt.Append(GenerateCode(ConvertUsing(us), indentation)); |
|
} |
|
document.Insert(insertionOffset, txt.ToString()); |
|
document.EndUndoableAction(); |
|
} |
|
#endregion |
|
} |
|
}
|
|
|