~ubuntu-branches/ubuntu/trusty/monodevelop/trusty-proposed

« back to all changes in this revision

Viewing changes to src/addins/MonoDevelop.MacDev/ObjCIntegration/NSObjectInfoService.cs

  • Committer: Package Import Robot
  • Author(s): Jo Shields
  • Date: 2013-05-12 09:46:03 UTC
  • mto: This revision was merged to the branch mainline in revision 29.
  • Revision ID: package-import@ubuntu.com-20130512094603-mad323bzcxvmcam0
Tags: upstream-4.0.5+dfsg
ImportĀ upstreamĀ versionĀ 4.0.5+dfsg

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
// 
2
 
// NSObjectInfoService.cs
3
 
//  
4
 
// Author:
5
 
//       Michael Hutchinson <mhutchinson@novell.com>
6
 
// 
7
 
// Copyright (c) 2011 Novell, Inc.
8
 
// Copyright (c) 2011 Xamarin Inc.
9
 
// 
10
 
// Permission is hereby granted, free of charge, to any person obtaining a copy
11
 
// of this software and associated documentation files (the "Software"), to deal
12
 
// in the Software without restriction, including without limitation the rights
13
 
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
14
 
// copies of the Software, and to permit persons to whom the Software is
15
 
// furnished to do so, subject to the following conditions:
16
 
// 
17
 
// The above copyright notice and this permission notice shall be included in
18
 
// all copies or substantial portions of the Software.
19
 
// 
20
 
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
21
 
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22
 
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
23
 
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
24
 
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
25
 
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
26
 
// THE SOFTWARE.
27
 
 
28
 
using System;
29
 
using System.IO;
30
 
using System.Linq;
31
 
using System.Collections.Generic;
32
 
using System.Text.RegularExpressions;
33
 
 
34
 
using MonoDevelop.Projects;
35
 
using ICSharpCode.NRefactory.TypeSystem;
36
 
using ICSharpCode.NRefactory.TypeSystem.Implementation;
37
 
using MonoDevelop.Ide.TypeSystem;
38
 
 
39
 
using MonoDevelop.Ide;
40
 
using MonoDevelop.Core;
41
 
 
42
 
namespace MonoDevelop.MacDev.ObjCIntegration
43
 
{
44
 
        public class NSObjectInfoService
45
 
        {
46
 
                //static readonly Regex frameworkRegex = new Regex ("#import\\s+<([A-Z][A-Za-z]*)/([A-Z][A-Za-z]*)\\.h>", RegexOptions.Compiled);
47
 
                static readonly Regex typeInfoRegex = new Regex ("@(interface|protocol)\\s+(\\w*)\\s*:\\s*(\\w*)", RegexOptions.Compiled);
48
 
                static readonly Regex ibRegex = new Regex ("(-\\s*\\(IBAction\\)|IBOutlet)\\s*([^;]*);", RegexOptions.Compiled);
49
 
                static readonly char[] colonChar = { ':' };
50
 
                static readonly char[] whitespaceChars = { ' ', '\t', '\n', '\r' };
51
 
                static readonly char[] splitActionParamsChars = { ' ', '\t', '\n', '\r', '*', '(', ')' };
52
 
                
53
 
                readonly ITypeReference nsobjectType, registerAttType, connectAttType, exportAttType, modelAttType,
54
 
                        iboutletAttType, ibactionAttType;
55
 
                
56
 
                static Dictionary<TypeSystemService.ProjectContentWrapper,NSObjectProjectInfo> infos = new Dictionary<TypeSystemService.ProjectContentWrapper, NSObjectProjectInfo> ();
57
 
 
58
 
                ITypeDefinition Resolve (TypeSystemService.ProjectContentWrapper dom, ITypeReference reference)
59
 
                {
60
 
                        return reference.Resolve (dom.Compilation).GetDefinition ();
61
 
                }
62
 
                
63
 
 
64
 
                static NSObjectInfoService ()
65
 
                {
66
 
                        TypeSystemService.ProjectUnloaded += HandleDomUnloaded;
67
 
                }
68
 
                
69
 
                public NSObjectInfoService (string wrapperRoot)
70
 
                {
71
 
                        this.WrapperRoot = wrapperRoot;
72
 
                        var typeNamespace = wrapperRoot + ".Foundation";
73
 
                        connectAttType = new GetClassTypeReference (typeNamespace, "ConnectAttribute");
74
 
                        exportAttType = new GetClassTypeReference (typeNamespace, "ExportAttribute");
75
 
                        iboutletAttType = new GetClassTypeReference (typeNamespace, "OutletAttribute");
76
 
                        ibactionAttType = new GetClassTypeReference (typeNamespace, "ActionAttribute");
77
 
                        registerAttType = new GetClassTypeReference (typeNamespace, "RegisterAttribute");
78
 
                        modelAttType = new GetClassTypeReference (typeNamespace, "ModelAttribute");
79
 
                        nsobjectType = new GetClassTypeReference (typeNamespace, "NSObject");
80
 
                }
81
 
                
82
 
                public string WrapperRoot { get; private set; }
83
 
                
84
 
                public NSObjectProjectInfo GetProjectInfo (DotNetProject project, IAssembly lookinAssembly = null)
85
 
                {
86
 
                        var dom = TypeSystemService.GetProjectContentWrapper (project);
87
 
                        project.ReferenceAddedToProject += HandleDomReferencesUpdated;
88
 
                        project.ReferenceRemovedFromProject += HandleDomReferencesUpdated;
89
 
                        return GetProjectInfo (dom, lookinAssembly);
90
 
                }
91
 
                
92
 
                public NSObjectProjectInfo GetProjectInfo (TypeSystemService.ProjectContentWrapper dom, IAssembly lookinAssembly = null)
93
 
                {
94
 
                        NSObjectProjectInfo info;
95
 
                        
96
 
                        lock (infos) {
97
 
                                if (infos.TryGetValue (dom, out info))
98
 
                                        return info;
99
 
                                
100
 
                                var nso = Resolve (dom, nsobjectType);
101
 
                                //only include DOMs that can resolve NSObject
102
 
                                if (nso == null || nso.Kind == TypeKind.Unknown)
103
 
                                        return null;
104
 
                                
105
 
                                info = new NSObjectProjectInfo (dom, this, lookinAssembly);
106
 
                                infos[dom] = info;
107
 
                        }
108
 
                        return info;
109
 
                }
110
 
 
111
 
                static void HandleDomReferencesUpdated (object sender, ProjectReferenceEventArgs e)
112
 
                {
113
 
                        var project = (DotNetProject)sender;
114
 
                        var dom = TypeSystemService.GetProjectContentWrapper (project);
115
 
                        if (dom == null)
116
 
                                return;
117
 
                        NSObjectProjectInfo info;
118
 
                        lock (infos) {
119
 
                                if (!infos.TryGetValue (dom, out info))
120
 
                                        return;
121
 
                        }
122
 
                        info.SetNeedsUpdating ();
123
 
                }
124
 
 
125
 
                static void HandleDomUnloaded (object sender, ProjectUnloadEventArgs e)
126
 
                {
127
 
                        var project = e.Project as DotNetProject;
128
 
                        if (project == null)
129
 
                                return;
130
 
                        var dom = e.Wrapper;
131
 
                        if (dom == null)
132
 
                                return;
133
 
                        lock (infos) {
134
 
                                project.ReferenceAddedToProject -= HandleDomReferencesUpdated;
135
 
                                project.ReferenceRemovedFromProject -= HandleDomReferencesUpdated;
136
 
                                infos.Remove (dom);
137
 
                        }
138
 
                }
139
 
 
140
 
                internal IEnumerable<NSObjectTypeInfo> GetRegisteredObjects (TypeSystemService.ProjectContentWrapper dom, IAssembly assembly)
141
 
                {
142
 
                        var nso = Resolve (dom, nsobjectType);
143
 
                        if (nso == null || nso.Kind == TypeKind.Unknown)
144
 
                                throw new Exception ("Could not get NSObject from type database");
145
 
                        
146
 
                        //FIXME: only emit this for the wrapper NS
147
 
//                      yield return new NSObjectTypeInfo ("NSObject", nso.GetDefinition ().FullName, null, null, false, false, false);
148
 
                        int cnt = 0, infcnt=0, models=0;
149
 
                        nso = assembly.Compilation.Import (nso);
150
 
                        foreach (var contextType in assembly.GetAllTypeDefinitions ()) {
151
 
                                if (contextType.IsDerivedFrom (nso)) {
152
 
                                        var info = ConvertType (dom, contextType);
153
 
                                        if (info != null)
154
 
                                                yield return info;
155
 
                                }
156
 
                        }
157
 
                }
158
 
                
159
 
                NSObjectTypeInfo ConvertType (TypeSystemService.ProjectContentWrapper dom, ITypeDefinition type)
160
 
                {
161
 
                        string objcName = null;
162
 
                        bool isModel = false;
163
 
                        bool registeredInDesigner = true;
164
 
                        
165
 
                        foreach (var att in type.Attributes) {
166
 
                                var attType = att.AttributeType;
167
 
                                if (attType.Equals (Resolve (dom, registerAttType))) {
168
 
                                        if (type.GetProjectContent () != null) {
169
 
                                                registeredInDesigner &=
170
 
                                                        MonoDevelop.DesignerSupport.CodeBehind.IsDesignerFile (att.Region.FileName);
171
 
                                        }
172
 
                                        
173
 
                                        //type registered with an explicit type name are up to the user to provide a valid name
174
 
                                        var posArgs = att.PositionalArguments;
175
 
                                        if (posArgs.Count == 1 || posArgs.Count == 2)
176
 
                                                objcName = posArgs [0].ConstantValue as string;
177
 
                                        //non-nested types in the root namespace have names accessible from obj-c
178
 
                                        else if (string.IsNullOrEmpty (type.Namespace) && type.Name.IndexOf ('.') < 0)
179
 
                                                objcName = type.Name;
180
 
                                }
181
 
                                
182
 
                                if (attType.Equals (Resolve (dom, modelAttType)))
183
 
                                        isModel = true;
184
 
                        }
185
 
                        
186
 
                        if (string.IsNullOrEmpty (objcName))
187
 
                                return null;
188
 
                        
189
 
                        string baseType = type.DirectBaseTypes.First ().ReflectionName;
190
 
                        if (baseType == "System.Object")
191
 
                                baseType = null;
192
 
                        
193
 
                        bool isUserType = !type.ParentAssembly.Equals (Resolve (dom, nsobjectType).ParentAssembly);
194
 
                        
195
 
                        var info = new NSObjectTypeInfo (objcName, type.ReflectionName, null, baseType, isModel, isUserType, registeredInDesigner);
196
 
 
197
 
                        if (info.IsUserType) {
198
 
                                UpdateTypeMembers (dom, info, type);
199
 
                                info.DefinedIn = type.Parts.Select (p => (string) p.Region.FileName).ToArray ();
200
 
                        }
201
 
                        
202
 
                        return info;
203
 
                }
204
 
                
205
 
                void UpdateTypeMembers (TypeSystemService.ProjectContentWrapper dom, NSObjectTypeInfo info, ITypeDefinition type)
206
 
                {
207
 
                        info.Actions.Clear ();
208
 
                        info.Outlets.Clear ();
209
 
                        foreach (var prop in type.Properties) {
210
 
                                foreach (var att in prop.Attributes) {
211
 
                                        var attType = att.AttributeType;
212
 
                                        bool isIBOutlet = attType.Equals (Resolve (dom, iboutletAttType));
213
 
                                        if (!isIBOutlet) {
214
 
                                                if (!attType.Equals (Resolve (dom, connectAttType)))
215
 
                                                        continue;
216
 
                                        }
217
 
                                        string name = null;
218
 
                                        var posArgs = att.PositionalArguments;
219
 
                                        if (posArgs.Count == 1)
220
 
                                                name = posArgs [0].ConstantValue as string;
221
 
                                        if (string.IsNullOrEmpty (name))
222
 
                                                name = prop.Name;
223
 
                                        
224
 
                                        // HACK: Work around bug #1586 in the least obtrusive way possible. Strip out any outlet
225
 
                                        // with the name 'view' on subclasses of MonoTouch.UIKit.UIViewController to avoid 
226
 
                                        // conflicts with the view property mapped there
227
 
                                        if (name == "view") {
228
 
                                                if (type.GetAllBaseTypeDefinitions ().Any (p => p.ReflectionName == "MonoTouch.UIKit.UIViewController"))
229
 
                                                        continue;
230
 
                                        }
231
 
                                        
232
 
                                        var ol = new IBOutlet (name, prop.Name, null, prop.ReturnType.ReflectionName);
233
 
                                        if (MonoDevelop.DesignerSupport.CodeBehind.IsDesignerFile (prop.Region.FileName))
234
 
                                                ol.IsDesigner = true;
235
 
                                        info.Outlets.Add (ol);
236
 
                                        break;
237
 
                                }
238
 
                        }
239
 
                        foreach (var meth in type.Methods) {
240
 
                                foreach (var att in meth.Attributes) {
241
 
                                        var attType = att.AttributeType;
242
 
                                        bool isIBAction = attType.Equals (Resolve (dom, ibactionAttType));
243
 
                                        if (!isIBAction) {
244
 
                                                if (!attType.Equals (Resolve (dom, exportAttType)))
245
 
                                                        continue;
246
 
                                        }
247
 
 
248
 
                                        bool isDesigner = meth.Parts.Any (part => MonoDevelop.DesignerSupport.CodeBehind.IsDesignerFile (part.Region.FileName));
249
 
                                        //only support Export from old designer files, user code must be IBAction
250
 
                                        if (!isDesigner && !isIBAction)
251
 
                                                continue;
252
 
                                        
253
 
                                        string[] name = null;
254
 
                                        var posArgs = att.PositionalArguments;
255
 
                                        if (posArgs.Count == 1 || posArgs.Count == 2) {
256
 
                                                var n = posArgs [0].ConstantValue as string;
257
 
                                                if (!string.IsNullOrEmpty (n))
258
 
                                                        name = n.Split (colonChar);
259
 
                                        }
260
 
                                        var action = new IBAction (name != null ? name [0] : meth.Name, meth.Name);
261
 
                                        int i = 1;
262
 
                                        foreach (var param in meth.Parameters) {
263
 
                                                string label = name != null && i < name.Length ? name [i] : null;
264
 
                                                if (label != null && label.Length == 0)
265
 
                                                        label = null;
266
 
                                                action.Parameters.Add (new IBActionParameter (label, param.Name, null, param.Type.ReflectionName));
267
 
                                        }
268
 
                                        if (meth.Parts.Any (part => MonoDevelop.DesignerSupport.CodeBehind.IsDesignerFile (part.Region.FileName)))
269
 
                                                action.IsDesigner = true;
270
 
                                        info.Actions.Add (action);
271
 
                                        break;
272
 
                                }
273
 
                        }
274
 
                }
275
 
                
276
 
                public static NSObjectTypeInfo ParseHeader (string headerFile)
277
 
                {
278
 
                        string text = File.ReadAllText (headerFile);
279
 
                        string userType = null, userBaseType = null;
280
 
                        MatchCollection matches;
281
 
                        NSObjectTypeInfo type;
282
 
                        
283
 
                        // First, grep for classes
284
 
                        matches = typeInfoRegex.Matches (text);
285
 
                        foreach (Match match in matches) {
286
 
                                if (match.Groups[1].Value != "interface")
287
 
                                        continue;
288
 
                                
289
 
                                if (userType != null) {
290
 
                                        // UNSUPPORTED: more than 1 user-type defined in this header
291
 
                                        return null;
292
 
                                }
293
 
                                
294
 
                                userType = match.Groups[2].Value;
295
 
                                userBaseType = match.Groups[3].Value;
296
 
                        }
297
 
                        
298
 
                        if (userType == null)
299
 
                                return null;
300
 
                        
301
 
                        type = new NSObjectTypeInfo (userType, null, userBaseType, null, false, true, true);
302
 
                        
303
 
                        // Now grep for IBActions and IBOutlets
304
 
                        matches = ibRegex.Matches (text);
305
 
                        foreach (Match match in matches) {
306
 
                                var kind = match.Groups[1].Value;
307
 
                                var def = match.Groups[2].Value;
308
 
                                if (kind == "IBOutlet") {
309
 
                                        var split = def.Split (whitespaceChars, StringSplitOptions.RemoveEmptyEntries);
310
 
                                        string objcType = split[0].TrimEnd ('*');
311
 
                                        string objcName = null;
312
 
 
313
 
                                        for (int i = 1; i < split.Length; i++) {
314
 
                                                objcName = split[i].TrimStart ('*');
315
 
                                                if (string.IsNullOrEmpty (objcName))
316
 
                                                        continue;
317
 
 
318
 
                                                if (i + 1 < split.Length) {
319
 
                                                        // This is a bad sign... what tokens are after the name??
320
 
                                                        objcName = null;
321
 
                                                        break;
322
 
                                                }
323
 
                                        }
324
 
 
325
 
                                        if (string.IsNullOrEmpty (objcType) || string.IsNullOrEmpty (objcName)) {
326
 
                                                MessageService.ShowError (GettextCatalog.GetString ("Error while parsing header file."),
327
 
                                                        string.Format (GettextCatalog.GetString ("The definition '{0}' can't be parsed."), def));
328
 
 
329
 
                                                // We can't recover if objcName is empty...
330
 
                                                if (string.IsNullOrEmpty (objcName))
331
 
                                                        continue;
332
 
 
333
 
                                                // We can try using NSObject...
334
 
                                                objcType = "NSObject";
335
 
                                        }
336
 
 
337
 
                                        if (objcType == "id")
338
 
                                                objcType = "NSObject";
339
 
 
340
 
                                        IBOutlet outlet = new IBOutlet (objcName, objcName, objcType, null);
341
 
                                        outlet.IsDesigner = true;
342
 
                                        
343
 
                                        type.Outlets.Add (outlet);
344
 
                                } else {
345
 
                                        string[] split = def.Split (colonChar);
346
 
                                        string name = split[0].Trim ();
347
 
                                        var action = new IBAction (name, name);
348
 
                                        action.IsDesigner = true;
349
 
                                        
350
 
                                        string label = null;
351
 
                                        for (int i = 1; i < split.Length; i++) {
352
 
                                                var s = split[i].Split (splitActionParamsChars, StringSplitOptions.RemoveEmptyEntries);
353
 
                                                string objcType = s[0];
354
 
                                                if (objcType == "id")
355
 
                                                        objcType = "NSObject";
356
 
                                                var par = new IBActionParameter (label, s[1], objcType, null);
357
 
                                                label = s.Length == 3? s[2] : null;
358
 
                                                action.Parameters.Add (par);
359
 
                                        }
360
 
                                        
361
 
                                        type.Actions.Add (action);
362
 
                                }
363
 
                        }
364
 
                        
365
 
                        return type;
366
 
                }
367
 
        }
368
 
        
369
 
        public class UserTypeChangeEventArgs : EventArgs
370
 
        {
371
 
                public UserTypeChangeEventArgs (IList<UserTypeChange> changes)
372
 
                {
373
 
                        this.Changes = changes;
374
 
                }
375
 
                
376
 
                public IList<UserTypeChange> Changes { get; private set; }
377
 
        }
378
 
        
379
 
        public class UserTypeChange
380
 
        {
381
 
                public UserTypeChange (NSObjectTypeInfo type, UserTypeChangeKind kind)
382
 
                {
383
 
                        this.Type = type;
384
 
                        this.Kind = kind;
385
 
                }
386
 
                
387
 
                public NSObjectTypeInfo Type { get; private set; }
388
 
                public UserTypeChangeKind Kind { get; private set; }
389
 
        }
390
 
        
391
 
        public enum UserTypeChangeKind
392
 
        {
393
 
                Added,
394
 
                Removed,
395
 
                Modified
396
 
        }
397
 
        
398
 
}