33
33
using System.CodeDom;
34
34
using System.Reflection;
35
using System.Collections;
35
36
using System.Collections.Generic;
37
38
using MonoDevelop.Projects;
38
using MonoDevelop.Projects.Parser;
39
using MonoDevelop.Projects.Dom;
40
using MonoDevelop.Projects.Dom.Parser;
39
41
using MonoDevelop.Projects.Text;
40
42
using MonoDevelop.Projects.CodeGeneration;
41
43
using MonoDevelop.Ide.Gui;
47
public class BindingService
49
public static class BindingService
49
51
//TODO: currently case-sensitive, so some languages may not like this
50
52
const bool ignoreCase = false;
52
private BindingService ()
56
public static IMember GetCompatibleMemberInClass (IClass cls, CodeTypeMember member)
58
IParserContext ctx = IdeApp.ProjectOperations.ParserDatabase.GetProjectParserContext ((MonoDevelop.Projects.Project) cls.SourceProject);
54
public static IMember GetCompatibleMemberInClass (IType cls, CodeTypeMember member)
56
ProjectDom ctx = ProjectDomService.GetProjectDom ((MonoDevelop.Projects.Project) cls.SourceProject);
59
57
return GetCompatibleMemberInClass (ctx, cls, member);
62
public static IMember GetCompatibleMemberInClass (IParserContext ctx, IClass cls, CodeTypeMember member)
60
public static IMember GetCompatibleMemberInClass (ProjectDom ctx, IType cls, CodeTypeMember member)
64
62
//check for identical property names
65
63
foreach (IProperty prop in cls.Properties) {
66
64
if (string.Compare (prop.Name, member.Name, ignoreCase) == 0) {
67
EnsureClassExists (ctx, prop.ReturnType.FullyQualifiedName, GetValidRegion (prop));
65
EnsureClassExists (ctx, prop.ReturnType.FullName, GetValidRegion (prop));
68
66
CodeMemberProperty memProp = member as CodeMemberProperty;
69
if (memProp == null || !IsTypeCompatible (ctx, prop.ReturnType.FullyQualifiedName, memProp.Type.BaseType))
70
throw new MemberExistsException (cls.FullyQualifiedName, MemberType.Property, member, GetValidRegion (prop));
67
if (memProp == null || !IsTypeCompatible (ctx, prop.ReturnType.FullName, memProp.Type.BaseType))
68
throw new MemberExistsException (cls.FullName, MemberType.Property, member, GetValidRegion (prop), cls.CompilationUnit.FileName);
75
73
//check for identical method names
76
74
foreach (IMethod meth in cls.Methods) {
77
75
if (string.Compare (meth.Name, member.Name, ignoreCase) == 0) {
78
EnsureClassExists (ctx, meth.ReturnType.FullyQualifiedName, GetValidRegion (meth));
76
EnsureClassExists (ctx, meth.ReturnType.FullName, GetValidRegion (meth));
79
77
CodeMemberMethod memMeth = member as CodeMemberMethod;
80
if (memMeth == null || !IsTypeCompatible (ctx, meth.ReturnType.FullyQualifiedName, memMeth.ReturnType.BaseType))
81
throw new MemberExistsException (cls.FullyQualifiedName, MemberType.Method, member, GetValidRegion (meth));
78
if (memMeth == null || !IsTypeCompatible (ctx, meth.ReturnType.FullName, memMeth.ReturnType.BaseType))
79
throw new MemberExistsException (cls.FullName, MemberType.Method, member, GetValidRegion (meth), cls.CompilationUnit.FileName);
86
84
//check for identical event names
87
85
foreach (IEvent ev in cls.Events) {
88
86
if (string.Compare (ev.Name, member.Name, ignoreCase) == 0) {
89
EnsureClassExists (ctx, ev.ReturnType.FullyQualifiedName, GetValidRegion (ev));
87
EnsureClassExists (ctx, ev.ReturnType.FullName, GetValidRegion (ev));
90
88
CodeMemberEvent memEv = member as CodeMemberEvent;
91
if (memEv == null || !IsTypeCompatible (ctx, ev.ReturnType.FullyQualifiedName, memEv.Type.BaseType))
92
throw new MemberExistsException (cls.FullyQualifiedName, MemberType.Event, member, GetValidRegion (ev));
89
if (memEv == null || !IsTypeCompatible (ctx, ev.ReturnType.FullName, memEv.Type.BaseType))
90
throw new MemberExistsException (cls.FullName, MemberType.Event, member, GetValidRegion (ev), cls.CompilationUnit.FileName);
97
95
//check for identical field names
98
96
foreach (IField field in cls.Fields) {
99
97
if (string.Compare (field.Name, member.Name, ignoreCase) == 0) {
100
EnsureClassExists (ctx, field.ReturnType.FullyQualifiedName, GetValidRegion (field));
98
EnsureClassExists (ctx, field.ReturnType.FullName, GetValidRegion (field));
101
99
CodeMemberField memField = member as CodeMemberField;
102
if (memField == null || !IsTypeCompatible (ctx, field.ReturnType.FullyQualifiedName, memField.Type.BaseType))
103
throw new MemberExistsException (cls.FullyQualifiedName, MemberType.Field, member, GetValidRegion (field));
100
if (memField == null || !IsTypeCompatible (ctx, field.ReturnType.FullName, memField.Type.BaseType))
101
throw new MemberExistsException (cls.FullName, MemberType.Field, member, GetValidRegion (field), cls.CompilationUnit.FileName);
108
106
//walk down into base classes, if any
109
107
foreach (IReturnType baseType in cls.BaseTypes) {
110
IClass c = ctx.GetClass (baseType.FullyQualifiedName);
108
IType c = ctx.GetType (baseType);
112
throw new TypeNotFoundException (baseType.FullyQualifiedName, cls.Region);
110
throw new TypeNotFoundException (baseType.FullName, cls.BodyRegion, cls.CompilationUnit.FileName);
113
111
IMember mem = GetCompatibleMemberInClass (ctx, c, member);
122
static IRegion GetValidRegion (IMember member)
120
static DomRegion GetValidRegion (IMember member)
124
if (member.Region.FileName == null)
125
member.Region.FileName = member.DeclaringType.Region.FileName;
126
return member.Region;
122
if (member.BodyRegion == null || member.DeclaringType.CompilationUnit.FileName == null)
123
return member.DeclaringType.BodyRegion;
124
return member.BodyRegion;
129
static IClass EnsureClassExists (IParserContext ctx, string className, IRegion location)
127
static IType EnsureClassExists (ProjectDom ctx, string className, DomRegion location)
131
IClass cls = ctx.GetClass (className);
129
IType cls = ctx.GetType (className);
133
throw new TypeNotFoundException (className, location);
131
throw new TypeNotFoundException (className, location, null);
137
static bool IsTypeCompatible (IParserContext ctx, string existingType, string checkType)
135
static bool IsTypeCompatible (ProjectDom ctx, string existingType, string checkType)
139
137
if (existingType == checkType)
141
IClass cls = EnsureClassExists (ctx, checkType, null);
139
IType cls = EnsureClassExists (ctx, checkType, DomRegion.Empty);
142
140
foreach (IReturnType baseType in cls.BaseTypes) {
143
if (IsTypeCompatible (ctx, existingType, baseType.FullyQualifiedName))
141
if (IsTypeCompatible (ctx, existingType, baseType.FullName))
149
public static IMember AddMemberToClass (CombineEntry entry, IClass cls, CodeTypeMember member, bool throwIfExists)
151
return AddMemberToClass (entry, cls, cls.Parts[0], member, throwIfExists);
154
public static IMember AddMemberToClass (CombineEntry entry, IClass cls, IClass specificPartToAffect, CodeTypeMember member, bool throwIfExists)
147
public static IMember AddMemberToClass (SolutionItem entry, IType cls, IType specificPartToAffect, CodeTypeMember member, bool throwIfExists)
156
149
bool isChildClass = false;
157
foreach (IClass c in cls.Parts)
150
foreach (IType c in cls.Parts)
158
151
if (c == specificPartToAffect)
159
152
isChildClass = true;
160
153
if (!isChildClass)
166
159
return GetCodeGenerator (entry).AddMember (specificPartToAffect, member);
168
161
if (throwIfExists)
169
throw new MemberExistsException (cls.Name, member, MemberType.Method, existingMember.Region);
162
throw new MemberExistsException (cls.Name, member, MemberType.Method, existingMember.BodyRegion, cls.CompilationUnit.FileName);
171
164
return existingMember;
174
public static CodeRefactorer GetCodeGenerator (CombineEntry entry)
167
public static CodeRefactorer GetCodeGenerator (SolutionItem entry)
176
CodeRefactorer cr = new CodeRefactorer (entry.RootCombine, IdeApp.ProjectOperations.ParserDatabase);
169
CodeRefactorer cr = new CodeRefactorer (entry.ParentSolution);
177
170
cr.TextFileProvider = OpenDocumentFileProvider.Instance;
181
174
//TODO: check accessibility
182
public static string[] GetCompatibleMethodsInClass (IClass cls, CodeMemberMethod testMethod)
175
public static string[] GetCompatibleMethodsInClass (IType cls, CodeMemberMethod testMethod)
184
177
List<string> list = new List<string> ();
187
180
if (method.Parameters.Count != testMethod.Parameters.Count)
190
if (method.ReturnType.FullyQualifiedName != testMethod.ReturnType.BaseType)
183
if (method.ReturnType.FullName != testMethod.ReturnType.BaseType)
193
186
//compare each parameter
194
187
bool mismatch = false;
195
188
for (int i = 0; i < testMethod.Parameters.Count; i++)
196
if (method.Parameters[i].ReturnType.FullyQualifiedName != testMethod.Parameters[i].Type.BaseType)
189
if (method.Parameters[i].ReturnType.FullName != testMethod.Parameters[i].Type.BaseType)
216
public static bool IdentifierExistsInClass (IClass cls, string identifier)
220
foreach (IMethod method in cls.Methods)
221
if (method.Name == identifier)
224
foreach (IProperty property in cls.Properties)
225
if (property.Name == identifier)
228
foreach (IEvent ev in cls.Events)
229
if (ev.Name == identifier)
232
foreach (IField field in cls.Fields)
233
if (field.Name == identifier)
240
public static string GenerateIdentifierUniqueInClass (IClass cls, string trialIdentifier)
209
public static bool IdentifierExistsInClass (ProjectDom parserContext, IType cls, string identifier)
211
foreach (IMember member in cls.Members)
212
if (member.Name == identifier)
215
return VisibleIdentifierExistsInBaseClasses (parserContext.GetInheritanceTree (cls), identifier);
218
static bool VisibleIdentifierExistsInBaseClasses (IEnumerable<IType> classes, string identifier)
220
foreach (IType cls in classes) {
221
foreach (IEnumerable en in new IEnumerable[] {cls.Methods, cls.Properties, cls.Events, cls.Fields})
222
foreach (IMember item in en)
223
if (!item.IsPrivate && item.Name == identifier)
225
foreach (IType innerClass in cls.InnerTypes)
226
if (!innerClass.IsPrivate && innerClass.Name == identifier)
232
public static string GenerateIdentifierUniqueInClass (ProjectDom parserContext, IType cls, string trialIdentifier)
242
234
string trialValue = trialIdentifier;
244
236
for (int suffix = 1; suffix <= int.MaxValue; suffix++)
246
if (!IdentifierExistsInClass (cls, trialValue))
238
if (!IdentifierExistsInClass (parserContext, cls, trialValue))
247
239
return trialValue;
249
241
trialValue = trialIdentifier + suffix.ToString ();
256
248
//opens the code view with the desired method, creating it if it doesn't already exist
257
public static void CreateAndShowMember (CombineEntry project, IClass cls, CodeTypeMember member)
249
public static void CreateAndShowMember (SolutionItem project, IType cls, IType specificPartToAffect, CodeTypeMember member)
259
251
//only adds the method if it doesn't already exist
260
IMember mem = AddMemberToClass (project, cls, member, false);
252
IMember mem = AddMemberToClass (project, cls, specificPartToAffect, member, false);
262
254
//some tests in case code refactorer returns bad values
263
int beginline = cls.Region.BeginLine;
264
if (mem.Region != null && mem.Region.BeginLine >= beginline && mem.Region.BeginLine <= cls.Region.EndLine)
265
beginline = mem.Region.BeginLine;
255
int beginline = specificPartToAffect.Location.Line;
256
if (!mem.BodyRegion.IsEmpty && mem.BodyRegion.Start.Line >= beginline && mem.Location.Line <= specificPartToAffect.BodyRegion.End.Line)
257
beginline = mem.BodyRegion.Start.Line;
267
259
//jump to the member or class
268
IdeApp.Workbench.OpenDocument (cls.Region.FileName, beginline, 1, true);
260
IdeApp.Workbench.OpenDocument (specificPartToAffect.CompilationUnit.FileName, beginline, 1, true);
271
263
public static System.CodeDom.CodeTypeMember ReflectionToCodeDomMember (MemberInfo memberInfo)
308
300
foreach (ParameterInfo pi in pinfos) {
309
301
CodeParameterDeclarationExpression newPar = new CodeParameterDeclarationExpression (pi.ParameterType.FullName, pi.Name);
310
302
if (pi.IsIn) newPar.Direction = FieldDirection.In;
311
if (pi.IsOut) newPar.Direction = FieldDirection.Out;
303
else if (pi.IsOut) newPar.Direction = FieldDirection.Out;
304
newMethod.Parameters.Add (newPar);
310
public static System.CodeDom.CodeMemberMethod MDDomToCodeDomMethod (IEvent ev, ProjectDom context)
312
if (ev.ReturnType == null)
314
IType cls = context.GetType (ev.ReturnType);
317
foreach (IMethod m in cls.Methods)
318
if (m.Name == "Invoke")
319
return MDDomToCodeDomMethod (m);
323
public static System.CodeDom.CodeMemberMethod MDDomToCodeDomMethod (IMethod mi)
325
CodeMemberMethod newMethod = new CodeMemberMethod ();
326
newMethod.Name = mi.Name;
327
newMethod.ReturnType = new System.CodeDom.CodeTypeReference (mi.ReturnType.FullName);
329
newMethod.Attributes = System.CodeDom.MemberAttributes.Private;
330
switch (mi.Modifiers) {
331
case Modifiers.Internal:
332
newMethod.Attributes |= System.CodeDom.MemberAttributes.Assembly;
334
case Modifiers.ProtectedAndInternal:
335
newMethod.Attributes |= System.CodeDom.MemberAttributes.FamilyAndAssembly;
337
case Modifiers.Protected:
338
newMethod.Attributes |= System.CodeDom.MemberAttributes.Family;
340
case Modifiers.ProtectedOrInternal:
341
newMethod.Attributes |= System.CodeDom.MemberAttributes.FamilyAndAssembly;
343
case Modifiers.Public:
344
newMethod.Attributes |= System.CodeDom.MemberAttributes.Public;
346
case Modifiers.Static:
347
newMethod.Attributes |= System.CodeDom.MemberAttributes.Static;
351
foreach (IParameter p in mi.Parameters) {
352
CodeParameterDeclarationExpression newPar = new CodeParameterDeclarationExpression (p.ReturnType.FullName, p.Name);
353
if (p.IsRef) newPar.Direction = FieldDirection.Ref;
354
else if (p.IsOut) newPar.Direction = FieldDirection.Out;
355
else newPar.Direction = FieldDirection.In;
312
357
newMethod.Parameters.Add (newPar);