/main/src/addins/MonoDevelop.GtkCore/MonoDevelop.GtkCore.GuiBuilder/CodeBinder.cs
C# | 352 lines | 220 code | 53 blank | 79 comment | 57 complexity | 0185f08312fe5da1d7bf3e50dd9aba69 MD5 | raw file
Possible License(s): LGPL-2.0, GPL-2.0, CC-BY-SA-3.0, MIT, LGPL-2.1, Apache-2.0, BSD-3-Clause
- //
- // CodeBinder.cs
- //
- // Author:
- // Lluis Sanchez Gual
- //
- // Copyright (C) 2006 Novell, Inc (http://www.novell.com)
- //
- // Permission is hereby granted, free of charge, to any person obtaining
- // a copy of this software and associated documentation files (the
- // "Software"), to deal in the Software without restriction, including
- // without limitation the rights to use, copy, modify, merge, publish,
- // distribute, sublicense, and/or sell copies of the Software, and to
- // permit persons to whom the Software is furnished to do so, subject to
- // the following conditions:
- //
- // The above copyright notice and this permission notice shall be
- // included in all copies or substantial portions of the Software.
- //
- // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
- // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
- // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
- // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
- // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
- // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
- // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
- //
- using System.CodeDom;
- using System.Collections.Generic;
- using System.Linq;
- using MonoDevelop.Core;
- using MonoDevelop.Core.ProgressMonitoring;
- using MonoDevelop.Projects;
- using MonoDevelop.Projects.Text;
- using MonoDevelop.Ide.Gui;
- using MonoDevelop.GtkCore.Dialogs;
- using MonoDevelop.Ide;
- using MonoDevelop.Ide.TypeSystem;
- using MonoDevelop.Ide.FindInFiles;
- using Microsoft.CodeAnalysis;
- using Microsoft.CodeAnalysis.CSharp;
- using Microsoft.CodeAnalysis.CSharp.Syntax;
- using ICSharpCode.NRefactory6.CSharp;
- using MonoDevelop.CSharp.Refactoring;
- using MonoDevelop.Refactoring;
- using System.Xml.XPath;
- using System.IO;
- using System.Threading.Tasks;
- namespace MonoDevelop.GtkCore.GuiBuilder
- {
- /// This class provides several methods for managing the relation
- /// between an object (e.g. a window) and the source code that will implement the
- /// code for that object.
- ///
- /// Once created, a CodeBinder object will keep track of the class bound to the
- /// object. If the class is renamed, it will properly update the object name.
- public class CodeBinder
- {
- ITextFileProvider textFileProvider;
- Stetic.Component targetObject;
- MonoDevelop.Projects.Project project;
- GuiBuilderProject gproject;
- string className;
- string classFile;
-
- public CodeBinder (MonoDevelop.Projects.Project project, ITextFileProvider textFileProvider, Stetic.Component targetObject)
- {
- this.project = project;
- this.textFileProvider = textFileProvider;
- gproject = GtkDesignInfo.FromProject (project).GuiBuilderProject;
- TargetObject = targetObject;
- }
-
- public Stetic.Component TargetObject {
- get { return targetObject; }
- set {
- this.targetObject = value;
- if (targetObject != null) {
- var cls = gproject.FindClass (GetClassName (targetObject));
- if (cls != null) {
- className = cls.GetFullName ();
- classFile = cls.Locations.First ().SourceTree.FilePath;
- }
- }
- }
- }
-
- /// Synchronizes the bindings between the object and the source code
- public void UpdateBindings (string fileName)
- {
- if (targetObject == null)
- return;
- classFile = fileName;
-
- var cls = GetClass ();
- if (cls != null) {
- UpdateBindings (targetObject, cls);
- targetObject.GeneratePublic = cls.DeclaredAccessibility == Accessibility.Public;
- }
- }
-
- void UpdateBindings (Stetic.Component obj, ITypeSymbol cls)
- {
- if (targetObject == null || cls == null)
- return;
- // Remove signals for which there isn't a handler in the class
-
- Stetic.SignalCollection objectSignals = obj.GetSignals ();
- if (objectSignals != null) {
- Stetic.Signal[] signals = new Stetic.Signal [objectSignals.Count];
- objectSignals.CopyTo (signals, 0);
- foreach (Stetic.Signal signal in signals) {
- if (FindSignalHandler (cls, signal) == null) {
- obj.RemoveSignal (signal);
- }
- }
- }
- // Update children
-
- foreach (Stetic.Component ob in obj.GetChildren ())
- UpdateBindings (ob, cls);
- }
-
- static IMethodSymbol FindSignalHandler (ITypeSymbol cls, Stetic.Signal signal)
- {
- return cls.GetMembers (signal.Handler).OfType<IMethodSymbol> ().FirstOrDefault ();
- }
- public async Task UpdateField (Stetic.Component obj, string oldName)
- {
- if (targetObject == null)
- return;
-
- if (obj == targetObject)
- return; // The root widget name can only be changed internally.
-
- var cls = GetClass (false);
-
- string newName = GetObjectName (obj);
- if (newName.Length == 0)
- return;
-
- if (cls != null) {
- var f = ClassUtils.FindWidgetField (cls, oldName);
- if (f != null) {
- await MonoDevelop.Refactoring.Rename.RenameRefactoring.Rename (f, newName);
- }
- }
- }
-
- /// Adds a signal handler to the class
- public void BindSignal (Stetic.Signal signal)
- {
- if (targetObject == null)
- return;
- var cls = GetClass ();
- if (cls == null)
- return;
- if (FindSignalHandler (cls, signal) != null)
- return;
- var met = SyntaxFactory.MethodDeclaration (SyntaxFactory.ParseTypeName (signal.SignalDescriptor.HandlerReturnTypeName),
- signal.Handler)
- .WithBody (SyntaxFactory.Block ())
- .AddModifiers (SyntaxFactory.Token (SyntaxKind.ProtectedKeyword));
- var parameters = new List<ParameterSyntax> ();
- foreach (Stetic.ParameterDescriptor pinfo in signal.SignalDescriptor.HandlerParameters)
- parameters.Add (SyntaxFactory.Parameter (new SyntaxList<AttributeListSyntax> (), new SyntaxTokenList (), SyntaxFactory.ParseTypeName (pinfo.TypeName), SyntaxFactory.Identifier (pinfo.Name), null));
- met = met.AddParameterListParameters (parameters.ToArray ());
- CodeGenerationService.AddNewMember (project, cls, GetSourceLocation (cls), met);
- }
- static Location GetSourceLocation (INamedTypeSymbol cls)
- {
- foreach (var loc in cls.Locations) {
- if (loc.IsInSource) {
- if (!Path.GetDirectoryName (loc.SourceTree.FilePath).EndsWith ("gtk-gui", FilePath.PathComparison))
- return loc;
- }
- }
-
- return cls.Locations.First ();
- }
- public async Task UpdateSignal (Stetic.Signal oldSignal, Stetic.Signal newSignal)
- {
- if (targetObject == null)
- return;
- if (oldSignal.Handler == newSignal.Handler)
- return;
- var cls = GetClass ();
- if (cls == null)
- return;
- var met = FindSignalHandler (cls, oldSignal);
- if (met == null)
- return;
- await MonoDevelop.Refactoring.Rename.RenameRefactoring.Rename (met, newSignal.Handler);
- }
- /// Adds a field to the class
- public async Task BindToField (Stetic.Component obj)
- {
- if (targetObject == null)
- return;
- string name = GetMemberName (obj);
- var cls = GetClass ();
-
- if (FindField (cls, name) != null)
- return;
- var location = GetSourceLocation(cls);
- var doc = await IdeApp.Workbench.OpenDocument (location.SourceTree.FilePath, project, true);
-
- var editor = doc.Editor;
- if (editor != null) {
- await CodeGenerationService.AddNewMember (project, cls, cls.Locations.First (), GetFieldCode (cls, obj, name));
- }
- }
-
- FieldDeclarationSyntax GetFieldCode (ITypeSymbol cls, Stetic.Component obj, string name)
- {
- return SyntaxFactory.FieldDeclaration (
- SyntaxFactory.VariableDeclaration (
- SyntaxFactory.ParseTypeName (obj.Type.ClassName),
- new SeparatedSyntaxList<VariableDeclaratorSyntax> {
- SyntaxFactory.VariableDeclarator (name)
- }
- )
- ).AddModifiers (SyntaxFactory.Token (SyntaxKind.ProtectedKeyword));
- }
-
-
- static IFieldSymbol FindField (ITypeSymbol cls, string name)
- {
- return cls
- .GetMembers (name)
- .OfType<IFieldSymbol> ()
- .FirstOrDefault ();
- }
-
- public INamedTypeSymbol GetClass ()
- {
- return GetClass (true);
- }
-
- public INamedTypeSymbol GetClass (bool getUserClass)
- {
- if (targetObject == null)
- return null;
- var cls = gproject.FindClass (className, getUserClass);
- if (cls != null)
- return cls;
-
- // The class name may have changed. Try to guess the new name.
- // TODO (roslyn port) - is that really required ?
- // var matches = new List<INamedTypeSymbol> ();
- // ParsedDocument unit = null;
- // var ctx = gproject.GetParserContext ();
- // var doc = IdeApp.TypeSystemService.ParseFile (project, classFile);
- // if (doc != null) {
- // unit = doc;
- // foreach (var fcls in unit.TopLevelTypeDefinitions) {
- // if (IsValidClass (fcls, targetObject))
- // matches.Add (fcls);
- // }
- // }
- //
- // // If found the class, just return it
- // if (matches.Count == 1) {
- // cls = matches [0];
- // className = cls.GetFullName ();
- // targetObject.Name = className;
- // gproject.SaveWindow (true, targetObject.Name);
- // return cls;
- // }
- //
- // // If not found, warn the user.
- //
- // if (unit != null && unit.TopLevelTypeDefinitions.Count > 0) {
- // using (var dialog = new SelectRenamedClassDialog (unit.TopLevelTypeDefinitions.Select (c => c.Resolve (project)))) {
- // if (dialog.Run ()) {
- // className = dialog.SelectedClass;
- // if (className == null)
- // return null;
- // else {
- // targetObject.Name = className;
- // gproject.SaveWindow (true, targetObject.Name);
- // return gproject.FindClass (className);
- // }
- // }
- // }
- // } else {
- // MessageService.ShowError (GettextCatalog.GetString ("The class bound to the component '{0}' could not be found. This may be due to syntax errors in the source code file.", GetObjectName(targetObject)));
- // }
-
- return null;
- }
-
- static bool IsValidClass (ITypeSymbol cls, Stetic.Component obj)
- {
- if (cls.BaseType.SpecialType == SpecialType.System_Object)
- return false;
- string typeName = obj.Type.ClassName;
-
- if (cls.BaseType.GetFullName () == typeName)
- return true;
- return IsValidClass (cls.BaseType, obj);
- }
-
- internal static string GetClassName (Stetic.Component obj)
- {
- return GetObjectName (obj);
- }
-
- internal static string GetMemberName (Stetic.Component obj)
- {
- return obj.Name;
- }
-
- internal static string GetObjectName (Stetic.Component obj)
- {
- return obj.Name;
- }
-
- internal static string GetClassName (Stetic.ProjectItemInfo obj)
- {
- return GetObjectName (obj);
- }
-
- internal static string GetObjectName (Stetic.ProjectItemInfo obj)
- {
- return obj.Name;
- }
- }
- }