/// <summary> /// Resolves the type by mapping the known Objective-C type information to .NET types. /// </summary> /// <returns> /// The number of unresolved types still remaining. /// </returns> /// <param name='type'> /// The NSObjectTypeInfo that contains the known Objective-C type information. /// Typically this will be the result of NSObjectInfoService.ParseHeader(). /// </param> /// <param name='provider'> /// A CodeDom provider which is used to make sure type names don't conflict with language keywords. /// </param> /// <param name='defaultNamespace'> /// The default namespace used when forcing type resolution. /// </param> public void ResolveObjcToCli (IProgressMonitor monitor, NSObjectTypeInfo type, CodeDomProvider provider, string defaultNamespace) { NSObjectTypeInfo resolved; // Resolve our base type if (type.BaseCliType == null) { if (TryResolveObjcToCli (type.BaseObjCType, out resolved)) { type.BaseCliType = resolved.CliName; } else { var reference = new GetClassTypeReference (defaultNamespace, provider.CreateValidIdentifier (type.BaseObjCType)); type.BaseCliType = reference.Resolve (dom.Compilation).ReflectionName; var message = string.Format ("Failed to resolve Objective-C type '{0}' to a type in the current solution.", type.BaseObjCType); message += string.Format (" Adding a [Register (\"{0}\")] attribute to the class which corresponds to this will allow it to be synced to Objective-C.", type.BaseObjCType); monitor.ReportError (null, new UserException ("Error while syncing", message)); } } // Resolve [Outlet] types foreach (var outlet in type.Outlets) { if (outlet.CliType != null) continue; if (TryResolveObjcToCli (outlet.ObjCType, out resolved)) { outlet.CliType = resolved.CliName; } else { outlet.CliType = defaultNamespace + "." + provider.CreateValidIdentifier (outlet.ObjCType); var message = string.Format ("Failed to resolve Objective-C outlet '{0}' of type '{1}' to a type in the current solution.", outlet.ObjCName, outlet.ObjCType); message += string.Format (" Adding a [Register (\"{0}\")] attribute to the class which corresponds to this will allow it to be synced to Objective-C.", outlet.ObjCType); monitor.ReportError (null, new UserException ("Error while syncing", message)); } } // Resolve [Action] param types foreach (var action in type.Actions) { foreach (var param in action.Parameters) { if (param.CliType != null) continue; if (TryResolveObjcToCli (param.ObjCType, out resolved)) { param.CliType = resolved.CliName; } else { param.CliType = defaultNamespace + "." + provider.CreateValidIdentifier (param.ObjCType); var message = string.Format ("Failed to resolve paramater '{0}' of type '{2}' on Objective-C action '{1}' to a type in the current solution.", param.Name, action.ObjCName, param.ObjCType); message += string.Format (" Adding a [Register (\"{0}\")] attribute to the class which corresponds to this will allow it to be synced to Objective-C.", param.ObjCType); monitor.ReportError (null, new UserException ("Error while syncing", message)); } } } }
protected override string GenerateCode(ITypeDefinition currentClass) { bool implementInterface = this.implementInterface.IsChecked == true; bool hasOnPropertyChanged = HasOnPropertyChanged(currentClass); bool useEventArgs = false; AstNode insertionAnchorElement = refactoringContext.GetNode(); if ((insertionAnchorElement == null) || !(insertionAnchorElement.Parent is TypeDeclaration)) { return null; } NewLineNode newLineNode = insertionAnchorElement as NewLineNode; while (insertionAnchorElement.PrevSibling is NewLineNode) insertionAnchorElement = insertionAnchorElement.PrevSibling ?? insertionAnchorElement; using (Script script = refactoringContext.StartScript()) { TypeDeclaration currentClassDeclaration = insertionAnchorElement.Parent as TypeDeclaration; if (implementInterface && !currentClass.IsStatic) { if (!hasOnPropertyChanged) { var nodes = new List<AstNode>(); if (!currentClass.GetAllBaseTypeDefinitions().Any(bt => bt.FullName == "System.ComponentModel.INotifyPropertyChanged")) { AstNode nodeBeforeClassBlock = currentClassDeclaration.LBraceToken; if (nodeBeforeClassBlock.PrevSibling is NewLineNode) { // There's a new line before the brace, insert before it! nodeBeforeClassBlock = nodeBeforeClassBlock.PrevSibling; } int insertion = editor.Document.GetOffset(nodeBeforeClassBlock.StartLocation); AstType interfaceTypeNode = refactoringContext.CreateShortType("System.ComponentModel", "INotifyPropertyChanged", 0); var directBaseTypes = currentClass.DirectBaseTypes.Where(t => t.FullName != "System.Object"); if (currentClassDeclaration.BaseTypes.Count > 0) { script.InsertText(insertion, ", " + interfaceTypeNode + " "); } else { script.InsertText(insertion, " : " + interfaceTypeNode + " "); } } var rt = new GetClassTypeReference("System.ComponentModel", "INotifyPropertyChanged", 0); var rtResolved = rt.Resolve(refactoringContext.Compilation); var ev = rtResolved.GetEvents().First(e => e.Name == "PropertyChanged"); EventDeclaration propertyChangedEvent = new EventDeclaration(); propertyChangedEvent.Variables.Add(new VariableInitializer(ev.Name)); propertyChangedEvent.Modifiers = Modifiers.Public; propertyChangedEvent.ReturnType = refactoringContext.CreateShortType(ev.ReturnType); nodes.Add(propertyChangedEvent); MethodDeclaration onEvent = CreateOnEventMethod(ev, currentClass); nodes.Add(onEvent); foreach (var node in nodes) { script.InsertAfter(insertionAnchorElement, node); AppendNewLine(script, insertionAnchorElement, newLineNode); } useEventArgs = false; } else { useEventArgs = currentClass.GetMethods().First(m => m.Name == "OnPropertyChanged").Parameters[0].Type.FullName != "System.String"; } } foreach (FieldWrapper field in fields.Where(f => f.IsIncluded)) { var prop = CreateProperty(field.Field, true, field.AddSetter); if (!field.Field.IsStatic && !currentClass.IsStatic && field.AddSetter && implementInterface) { var invocation = new ExpressionStatement(CreateInvocation(field.PropertyName, useEventArgs)); var assignment = prop.Setter.Body.Children.ElementAt(0) as Statement; prop.Setter.Body = new BlockStatement(); BlockStatement elseBlock = new BlockStatement(); elseBlock.Add(assignment.Clone()); elseBlock.Add(invocation); prop.Setter.Body.Add( new IfElseStatement( new BinaryOperatorExpression(new IdentifierExpression(field.MemberName), BinaryOperatorType.InEquality, new IdentifierExpression("value")), elseBlock ) ); } script.InsertAfter(insertionAnchorElement, prop); AppendNewLine(script, insertionAnchorElement, newLineNode); } } return null; }
public static void Main(string[] args) { if (args.Length != 4) { Console.WriteLine("use: RenameClass.exe <SolutionPath> <ClassNamespace> <CurrentClassName> <NewClassName>"); return; } var solutionFile = args[0]; // "C:\\Users\\v-ezeqs\\Documents\\Visual Studio 2010\\Projects\\Application36\\Application36.sln" var classNamespace = args[1]; // "Application36.WebHost" var className = args[2]; // "SiteMaster" var classNewName = args[3]; // "SiteMaster2" if (!File.Exists(solutionFile)) { Console.WriteLine("Solution not found at {0}", solutionFile); return; } Console.WriteLine("Loading solution..."); // Loading Solution in Memory Solution solution = new Solution(solutionFile); Console.WriteLine("Finding references..."); // Define which Type I'm looking for var typeReference = new GetClassTypeReference(classNamespace, className) as ITypeReference; // Try to find the Type definition in solution's projects foreach (var proj in solution.Projects) { var type = typeReference.Resolve(proj.Compilation); if (type.Kind != TypeKind.Unknown) { SetSearchedMembers(new List<object>() { type }); } } if (searchedMembers == null) { Console.WriteLine("Not References found. Refactoring Done."); return; } // Find all related members related with the Type (like Members, Methods, etc) ICSharpCode.NRefactory.CSharp.Resolver.FindReferences refFinder = new ICSharpCode.NRefactory.CSharp.Resolver.FindReferences(); var scopes = searchedMembers.Select (e => refFinder.GetSearchScopes (e as IEntity)); // Finding references to the Type on the one of the different Solution files refs = new List<dynamic>(); foreach (var file in solution.AllFiles.Distinct (new CSharpFileEqualityComparer())) { foreach (var scope in scopes) { refFinder.FindReferencesInFile( scope, file.UnresolvedTypeSystemForFile, file.SyntaxTree, file.Project.Compilation, (astNode, result) => { var newRef = GetReference(result, astNode, file); if (newRef == null || refs.Any(r => r.File.FileName == newRef.File.FileName && r.Region == newRef.Region)) return; refs.Add(newRef); }, CancellationToken.None ); } } Console.WriteLine("Refactoring {0} places in {1} files...", refs.Count(), refs.Select(x => x.File.FileName).Distinct().Count()); // Perform replace for each of the References found foreach (var r in refs) { // DocumentScript expects the the AST to stay unmodified (so that it fits // to the document state at the time of the DocumentScript constructor call), // so we call Freeze() to prevent accidental modifications (e.g. forgetting a Clone() call). r.File.SyntaxTree.Freeze(); // Create a document containing the file content: var fileText = File.ReadAllText(r.File.FileName); var document = new StringBuilderDocument(fileText); using (var script = new DocumentScript(document, FormattingOptionsFactory.CreateAllman(), new TextEditorOptions())) { // Alternative 1: clone a portion of the AST and modify it //var copy = (InvocationExpression)expr.Clone(); //copy.Arguments.Add(stringComparisonAst.Member("Ordinal")); //script.Replace(expr, copy); // Alternative 2: perform direct text insertion / replace int offset = script.GetCurrentOffset(r.Region.Begin); var length = r.Region.End.Column - r.Region.Begin.Column; script.Replace(offset, length, classNewName); } File.WriteAllText(r.File.FileName, document.Text); } Console.WriteLine("Refactoring Done."); }