// 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 { /// /// Provides code generation facilities. /// 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(); if (pointerReturnType != null) { TypeReference typeRef = ConvertType(pointerReturnType.BaseType, context); typeRef.PointerNestingLevel++; return typeRef; } IList typeArguments = EmptyList.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 typeArguments, ClassFinder context) { if (c.DeclaringType != null) { TypeReference outerClass = CreateTypeReference(c.DeclaringType, typeArguments, context); List args = new List(); 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); } /// /// 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. /// 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 usings = new List(); foreach (string name in u.Usings) { usings.Add(new Using(name)); } if (u.HasAliases) { foreach (KeyValuePair pair in u.Aliases) { usings.Add(new Using(pair.Key, ConvertType(pair.Value, null))); } } return new UsingDeclaration(usings); } public static List ConvertParameters(IList parameters, ClassFinder targetContext) { List l = new List(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 ConvertAttributes(IList 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 resultList = new List(1); if (sec.Attributes.Count > 0) resultList.Add(sec); return resultList; } public static List ConvertTemplates(IList l, ClassFinder targetContext) { List o = new List(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 ConvertInterfaceImplementations(IEnumerable 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()) { node.Modifier &= ~(Modifiers.Public | Modifiers.Private | Modifiers.Protected | Modifiers.Internal); node.Body = null; } foreach (PropertyDeclaration node in members.OfType()) { node.Modifier &= ~(Modifiers.Public | Modifiers.Private | Modifiers.Protected | Modifiers.Internal); node.GetRegion.Block = null; node.SetRegion.Block = null; } foreach (EventDeclaration node in members.OfType()) { 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); } /// /// Generates code for and inserts it into /// after the line . /// protected void InsertCodeAfter(int insertLine, IRefactoringDocument document, string indentation, params AbstractNode[] nodes) { InsertCodeAfter(insertLine, document, indentation, true, nodes); } /// /// Generates code for and inserts it into /// after the line . /// 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()); } } /// /// Generates code for the NRefactory node. /// 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 arguments = new List(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 parameters = new List(); 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 arguments = new List(); 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 nodes = new List(); 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 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(IEnumerable 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); } /// /// Adds the methods implementing the to the list /// . /// public virtual void ImplementInterface(IList 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 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 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 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 oldUsings, IList 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> regions = new List>(); 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(st, en - st)); } regions.Sort(delegate(KeyValuePair a, KeyValuePair 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 } }