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

« back to all changes in this revision

Viewing changes to src/addins/MonoDevelop.MacDev/ObjCIntegration/NSObjectTypeInfo.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
 
// NSObjectInfo.cs
3
 
//  
4
 
// Author:
5
 
//       Michael Hutchinson <mhutchinson@novell.com>
6
 
// 
7
 
// Copyright (c) 2011 Novell, Inc.
8
 
// 
9
 
// Permission is hereby granted, free of charge, to any person obtaining a copy
10
 
// of this software and associated documentation files (the "Software"), to deal
11
 
// in the Software without restriction, including without limitation the rights
12
 
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
13
 
// copies of the Software, and to permit persons to whom the Software is
14
 
// furnished to do so, subject to the following conditions:
15
 
// 
16
 
// The above copyright notice and this permission notice shall be included in
17
 
// all copies or substantial portions of the Software.
18
 
// 
19
 
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20
 
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21
 
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
22
 
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23
 
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
24
 
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
25
 
// THE SOFTWARE.
26
 
 
27
 
using System;
28
 
using System.Collections.Generic;
29
 
using System.Linq;
30
 
using System.CodeDom;
31
 
using System.CodeDom.Compiler;
32
 
using System.IO;
33
 
using MonoDevelop.Core;
34
 
 
35
 
namespace MonoDevelop.MacDev.ObjCIntegration
36
 
{
37
 
        public class NSObjectTypeInfo
38
 
        {
39
 
                public NSObjectTypeInfo (string objcName, string cliName, string baseObjCName, string baseCliName, bool isModel, bool isUserType, bool isRegisteredInDesigner)
40
 
                {
41
 
                        IsRegisteredInDesigner = isRegisteredInDesigner;
42
 
                        BaseObjCType = baseObjCName;
43
 
                        BaseCliType = baseCliName;
44
 
                        IsUserType = isUserType;
45
 
                        ObjCName = objcName;
46
 
                        CliName = cliName;
47
 
                        IsModel = isModel;
48
 
                        
49
 
                        UserTypeReferences = new HashSet<string> ();
50
 
                        Outlets = new List<IBOutlet> ();
51
 
                        Actions = new List<IBAction> ();
52
 
                }
53
 
                
54
 
                public string ObjCName { get; private set; }
55
 
                public string CliName { get; internal set; }
56
 
                public bool IsModel { get; internal set; }
57
 
                
58
 
                public string BaseObjCType { get; internal set; }
59
 
                public string BaseCliType { get; internal set; } 
60
 
                public bool BaseIsModel { get; internal set; }
61
 
                
62
 
                public bool IsUserType { get; internal set; }
63
 
                public bool IsRegisteredInDesigner { get; internal set; }
64
 
                
65
 
                public List<IBOutlet> Outlets { get; private set; }
66
 
                public List<IBAction> Actions { get; private set; }
67
 
                
68
 
                public string[] DefinedIn { get; internal set; }
69
 
                
70
 
                public string GetDesignerFile ()
71
 
                {
72
 
                        if (DefinedIn != null)
73
 
                                foreach (var d in DefinedIn)
74
 
                                        if (MonoDevelop.DesignerSupport.CodeBehind.IsDesignerFile (d))
75
 
                                                return d;
76
 
                        return null;
77
 
                }
78
 
                
79
 
                public HashSet<string> UserTypeReferences { get; private set; }
80
 
                
81
 
                static void AddNamespaceForCliType (HashSet<string> namespaces, string ignore, string typeName)
82
 
                {
83
 
                        string ns;
84
 
                        int dot;
85
 
                        
86
 
                        if (typeName == null)
87
 
                                return;
88
 
                        
89
 
                        if ((dot = typeName.LastIndexOf ('.')) == -1)
90
 
                                return;
91
 
                        
92
 
                        ns = typeName.Substring (0, dot);
93
 
                        if (ns != ignore && !namespaces.Contains (ns))
94
 
                                namespaces.Add (ns);
95
 
                }
96
 
                
97
 
                public HashSet<string> GetNamespaces ()
98
 
                {
99
 
                        HashSet<string> namespaces = new HashSet<string> ();
100
 
                        string ignore = null;
101
 
                        int dot;
102
 
                        
103
 
                        if ((dot = CliName.LastIndexOf ('.')) != -1)
104
 
                                ignore = CliName.Substring (0, dot);
105
 
                        
106
 
                        AddNamespaceForCliType (namespaces, ignore, BaseCliType);
107
 
                        
108
 
                        foreach (var outlet in Outlets)
109
 
                                AddNamespaceForCliType (namespaces, ignore, outlet.CliType);
110
 
                        
111
 
                        foreach (var action in Actions) {
112
 
                                foreach (var param in action.Parameters)
113
 
                                        AddNamespaceForCliType (namespaces, ignore, param.CliType);
114
 
                        }
115
 
                        
116
 
                        return namespaces;
117
 
                }
118
 
                
119
 
                static string GetSuggestedRegisterName (string fullName)
120
 
                {
121
 
                        int dot = fullName.LastIndexOf ('.');
122
 
                        if (dot == -1)
123
 
                                return fullName;
124
 
                        
125
 
                        return fullName.Substring (dot + 1);
126
 
                }
127
 
                
128
 
                public void GenerateObjcType (string directory, string[] frameworks)
129
 
                {
130
 
                        if (IsModel) {
131
 
                                // We don't generate header files for protocols.
132
 
                                return;
133
 
                        }
134
 
                        
135
 
                        string hFilePath = Path.Combine (directory, ObjCName + ".h");
136
 
                        string mFilePath = Path.Combine (directory, ObjCName + ".m");
137
 
                        
138
 
                        using (var sw = File.CreateText (hFilePath)) {
139
 
                                sw.WriteLine (modificationWarning);
140
 
                                sw.WriteLine ();
141
 
                                
142
 
                                foreach (var framework in frameworks)
143
 
                                        sw.WriteLine ("#import <{0}/{0}.h>", framework);
144
 
                                
145
 
                                sw.WriteLine ();
146
 
                                
147
 
                                foreach (var reference in UserTypeReferences)
148
 
                                        sw.WriteLine ("#import \"{0}.h\"", reference);
149
 
                                
150
 
                                sw.WriteLine ();
151
 
                                
152
 
                                if (BaseObjCType == null && BaseCliType != null && !BaseIsModel) {
153
 
                                        throw new ObjectiveCGenerationException (string.Format (
154
 
                                                "Could not generate class '{0}' as its base type '{1}' could not be resolved to Objective-C.\n\n" +
155
 
                                                "Hint: Try adding [Register (\"{2}\")] to the class definition for {1}.",
156
 
                                                CliName, BaseCliType, GetSuggestedRegisterName (BaseCliType)), this);
157
 
                                }
158
 
                                
159
 
                                var baseType = BaseIsModel ? "NSObject" : BaseObjCType;
160
 
                                sw.WriteLine ("@interface {0} : {1} {{", ObjCName, baseType);
161
 
                                foreach (var outlet in Outlets) {
162
 
                                        sw.WriteLine ("\t{0} *_{1};", AsId (outlet.ObjCType), outlet.ObjCName);
163
 
                                }
164
 
                                sw.WriteLine ("}");
165
 
                                sw.WriteLine ();
166
 
                                
167
 
                                foreach (var outlet in Outlets) {
168
 
                                        var type = AsId (outlet.ObjCType);
169
 
                                        if (string.IsNullOrEmpty (type)) {
170
 
                                                throw new ObjectiveCGenerationException (string.Format (
171
 
                                                        "Could not generate outlet '{0}' in class '{1}' as its type '{2}' could not be resolved to Objective-C.\n\n" +
172
 
                                                        "Hint: Try adding [Register (\"{3}\")] to the class definition for {2}.",
173
 
                                                        outlet.CliName, this.CliName, outlet.CliType, GetSuggestedRegisterName (outlet.CliType)), this);
174
 
                                        }
175
 
                                        sw.WriteLine ("@property (nonatomic, retain) IBOutlet {0} *{1};", type, outlet.ObjCName);
176
 
                                        sw.WriteLine ();
177
 
                                }
178
 
                                
179
 
                                foreach (var action in Actions) {
180
 
                                        WriteActionSignature (action, sw);
181
 
                                        sw.WriteLine (";");
182
 
                                        sw.WriteLine ();
183
 
                                }
184
 
                                
185
 
                                sw.WriteLine ("@end");
186
 
                        }
187
 
                        
188
 
                        using (var sw = File.CreateText (mFilePath)) {
189
 
                                sw.WriteLine (modificationWarning);
190
 
                                sw.WriteLine ();
191
 
                                
192
 
                                sw.WriteLine ("#import \"{0}.h\"", ObjCName);
193
 
                                sw.WriteLine ();
194
 
                                
195
 
                                sw.WriteLine ("@implementation {0}", ObjCName);
196
 
                                sw.WriteLine ();
197
 
                                
198
 
                                bool hasOutlet = false;
199
 
                                foreach (var outlet in Outlets) {
200
 
                                        sw.WriteLine ("@synthesize {0} = _{0};", outlet.ObjCName);
201
 
                                        hasOutlet = true;
202
 
                                }
203
 
                                if (hasOutlet)
204
 
                                        sw.WriteLine ();
205
 
                                
206
 
                                foreach (var action in Actions) {
207
 
                                        if (action.Parameters.Any (p => p.ObjCType == null))
208
 
                                                continue;
209
 
                                        WriteActionSignature (action, sw);
210
 
                                        sw.WriteLine (" {");
211
 
                                        sw.WriteLine ("}");
212
 
                                        sw.WriteLine ();
213
 
                                }
214
 
                                
215
 
                                sw.WriteLine ("@end");
216
 
                        }
217
 
 
218
 
                        var lastSourceUpdateTime = DefinedIn.Max (f => File.GetLastWriteTime (f));
219
 
                        File.SetLastWriteTime (hFilePath, lastSourceUpdateTime);
220
 
                        File.SetLastWriteTime (mFilePath, lastSourceUpdateTime);
221
 
                }
222
 
                
223
 
                static string AsId (string objcType)
224
 
                {
225
 
                        if (objcType == "NSObject")
226
 
                                return "id";
227
 
                        return objcType;
228
 
                }
229
 
                
230
 
                static string modificationWarning =
231
 
                        "// WARNING\n" +
232
 
                        "// This file has been generated automatically by MonoDevelop to\n" +
233
 
                        "// mirror C# types. Changes in this file made by drag-connecting\n" +
234
 
                        "// from the UI designer will be synchronized back to C#, but\n" +
235
 
                        "// more complex manual changes may not transfer correctly.\n";
236
 
                
237
 
                void WriteActionSignature (IBAction action, System.IO.TextWriter writer)
238
 
                {
239
 
                        writer.Write ("- (IBAction){0}", action.ObjCName);
240
 
                        bool isFirst = true;
241
 
                        
242
 
                        foreach (var param in action.Parameters) {
243
 
                                string paramType = param.ObjCType;
244
 
                                if (paramType == null) {
245
 
                                        throw new ObjectiveCGenerationException (string.Format (
246
 
                                                "Could not generate Obj-C code for action '{0}' in class '{1}' as the type '{2}'" +
247
 
                                                 "of its parameter '{3}' could not be resolved to Obj-C",
248
 
                                                action.CliName, this.CliName, param.CliType, param.Name), this);
249
 
                                        
250
 
                                }
251
 
                                if (isFirst && paramType == "NSObject")
252
 
                                        paramType = "id";
253
 
                                else
254
 
                                        paramType = paramType + " *";
255
 
                                
256
 
                                if (isFirst) {
257
 
                                        isFirst = false;
258
 
                                        writer.Write (":({0}){1}", paramType, param.Name);
259
 
                                } else {
260
 
                                        writer.Write (" {0}:({1}){2}", param.Label, paramType, param.Name);
261
 
                                }
262
 
                        }       
263
 
                }
264
 
                
265
 
                /// <summary>
266
 
                /// Merges CLI info from previous version of the type into the parsed objc-type.
267
 
                /// </summary>
268
 
                public void MergeCliInfo (NSObjectTypeInfo previousType)
269
 
                {
270
 
                        CliName = previousType.CliName;
271
 
                        DefinedIn = previousType.DefinedIn;
272
 
                        IsModel = previousType.IsModel;
273
 
                        BaseIsModel = previousType.BaseIsModel;
274
 
                        IsUserType = previousType.IsUserType;
275
 
                        IsRegisteredInDesigner = previousType.IsRegisteredInDesigner;
276
 
                        
277
 
                        var existingOutlets = new Dictionary<string,IBOutlet> ();
278
 
                        foreach (var o in previousType.Outlets)
279
 
                                existingOutlets[o.ObjCName] = o;
280
 
                        
281
 
                        var existingActions = new Dictionary<string,IBAction> ();
282
 
                        foreach (var a in previousType.Actions)
283
 
                                existingActions[a.ObjCName] = a;
284
 
                        
285
 
                        foreach (var a in Actions) {
286
 
                                IBAction existing;
287
 
                                if (existingActions.TryGetValue (a.ObjCName, out existing)) {
288
 
                                        a.IsDesigner = existing.IsDesigner;
289
 
                                        a.CliName = existing.CliName;
290
 
                                }
291
 
                        }
292
 
                        
293
 
                        foreach (var o in Outlets) {
294
 
                                IBOutlet existing;
295
 
                                if (existingOutlets.TryGetValue (o.ObjCName, out existing)) {
296
 
                                        o.IsDesigner = existing.IsDesigner;
297
 
                                        o.CliName = existing.CliName;
298
 
                                }
299
 
                        }
300
 
                }
301
 
                
302
 
                public void GenerateCodeTypeDeclaration (CodeDomProvider provider, CodeGeneratorOptions generatorOptions,
303
 
                        string wrapperName, out CodeTypeDeclaration ctd, out string ns)
304
 
                {
305
 
                        var registerAtt = new CodeTypeReference (wrapperName + ".Foundation.RegisterAttribute");
306
 
                        
307
 
                        ctd = new System.CodeDom.CodeTypeDeclaration () {
308
 
                                IsPartial = true,
309
 
                        };
310
 
                        
311
 
                        if (Outlets.Any (o => o.IsDesigner) || Actions.Any (a => a.IsDesigner))
312
 
                                AddWarningDisablePragmas (ctd, provider);
313
 
                        
314
 
                        var dotIdx = CliName.LastIndexOf ('.');
315
 
                        if (dotIdx > 0) {
316
 
                                ns = CliName.Substring (0, dotIdx);
317
 
                                ctd.Name = CliName.Substring (dotIdx + 1);
318
 
                        } else {
319
 
                                ctd.Name = CliName;
320
 
                                ns = null;
321
 
                        }
322
 
                        if (IsRegisteredInDesigner)
323
 
                                AddAttribute (ctd.CustomAttributes, registerAtt, ObjCName);
324
 
                        
325
 
                        GenerateActionsOutlets (provider, ctd, wrapperName);
326
 
                }
327
 
                
328
 
                void GenerateActionsOutlets (CodeDomProvider provider, CodeTypeDeclaration type, string wrapperName)
329
 
                {
330
 
                        var outletAtt = new CodeTypeReference (wrapperName + ".Foundation.OutletAttribute");
331
 
                        var actionAtt = new CodeTypeReference (wrapperName + ".Foundation.ActionAttribute");
332
 
                        
333
 
                        foreach (var a in Actions)
334
 
                                if (a.IsDesigner)
335
 
                                        GenerateAction (actionAtt, type, a, provider);
336
 
                        
337
 
                        foreach (var o in Outlets)
338
 
                                if (o.IsDesigner)
339
 
                                        AddOutletProperty (outletAtt, type, o.CliName, new CodeTypeReference (o.CliType));
340
 
                }
341
 
                
342
 
                static void AddOutletProperty (CodeTypeReference outletAtt, CodeTypeDeclaration type, string name,
343
 
                        CodeTypeReference typeRef)
344
 
                {
345
 
                        var fieldName = "__impl_" + name;
346
 
                        var field = new CodeMemberField (typeRef, fieldName);
347
 
                        
348
 
                        var prop = new CodeMemberProperty () {
349
 
                                Name = name,
350
 
                                Type = typeRef
351
 
                        };
352
 
                        AddAttribute (prop.CustomAttributes, outletAtt, name);
353
 
                        
354
 
                        var setValue = new CodePropertySetValueReferenceExpression ();
355
 
                        var thisRef = new CodeThisReferenceExpression ();
356
 
                        var fieldRef = new CodeFieldReferenceExpression (thisRef, fieldName);
357
 
                        
358
 
                        prop.SetStatements.Add (new CodeAssignStatement (fieldRef, setValue));
359
 
                        prop.GetStatements.Add (new CodeMethodReturnStatement (fieldRef));
360
 
                        
361
 
                        prop.Attributes = field.Attributes = (prop.Attributes & ~MemberAttributes.AccessMask) | MemberAttributes.Private;
362
 
                        
363
 
                        type.Members.Add (prop);
364
 
                        type.Members.Add (field);
365
 
                }
366
 
                
367
 
                static void AddAttribute (CodeAttributeDeclarationCollection atts, CodeTypeReference type, string val)
368
 
                {
369
 
                        atts.Add (new CodeAttributeDeclaration (type, new CodeAttributeArgument (new CodePrimitiveExpression (val))));
370
 
                }
371
 
                
372
 
                static void AddWarningDisablePragmas (CodeTypeDeclaration type, CodeDomProvider provider)
373
 
                {
374
 
                        if (provider is Microsoft.CSharp.CSharpCodeProvider) {
375
 
                                type.Members.Add (new CodeSnippetTypeMember ("#pragma warning disable 0169")); // unused member
376
 
                        }
377
 
                }
378
 
                
379
 
                static void GenerateAction (CodeTypeReference actionAtt, CodeTypeDeclaration type, IBAction action, 
380
 
                        CodeDomProvider provider)
381
 
                {
382
 
                        var m = CreateEventMethod (actionAtt, action);
383
 
                        type.Members.Add (m);
384
 
                        
385
 
                        if (provider.FileExtension == "pas") {
386
 
                                m.UserData ["OxygenePartial"] = "YES";
387
 
                                m.UserData ["OxygeneEmpty"] = "YES";
388
 
                        }
389
 
                }
390
 
                
391
 
                static void GenerateReleaseDesignerOutletsMethod (CodeTypeDeclaration type)
392
 
                {
393
 
                        var thisRef = new CodeThisReferenceExpression ();
394
 
                        var nullRef = new CodePrimitiveExpression (null);
395
 
                        
396
 
                        var meth = new CodeMemberMethod () {
397
 
                                Name = "ReleaseDesignerOutlets",
398
 
                        };
399
 
                        
400
 
                        foreach (var outlet in type.Members.OfType<CodeMemberProperty> ()) {
401
 
                                var propRef = new CodePropertyReferenceExpression (thisRef, outlet.Name);
402
 
                                meth.Statements.Add (
403
 
                                        new CodeConditionStatement (
404
 
                                                new CodeBinaryOperatorExpression (propRef, CodeBinaryOperatorType.IdentityInequality, nullRef),
405
 
                                                new CodeExpressionStatement (new CodeMethodInvokeExpression (propRef, "Dispose")),
406
 
                                                new CodeAssignStatement (propRef, nullRef)
407
 
                                        )
408
 
                                );
409
 
                        }
410
 
                        
411
 
                        type.Members.Add (meth);
412
 
                }
413
 
                
414
 
                public static CodeTypeMember CreateEventMethod (CodeTypeReference exportAtt, IBAction action)
415
 
                {
416
 
                        var meth = new CodeMemberMethod () {
417
 
                                Name = action.CliName,
418
 
                                ReturnType = new CodeTypeReference (typeof (void)),
419
 
                        };
420
 
                        foreach (var p in action.Parameters) {
421
 
                                meth.Parameters.Add (new CodeParameterDeclarationExpression () {
422
 
                                        Name = p.Name,
423
 
                                        Type = new CodeTypeReference (p.ObjCType)
424
 
                                });
425
 
                        }
426
 
                        AddAttribute (meth.CustomAttributes, exportAtt, action.GetObjcFullName ());
427
 
                        
428
 
                        return meth;
429
 
                }
430
 
                
431
 
                public override string ToString ()
432
 
                {
433
 
                        return string.Format ("[NSObjectTypeInfo: ObjCName={0}, CliName={1}, IsModel={2}, BaseObjCType={3}, BaseCliType={4}, BaseIsModel={5}, IsUserType={6}, IsRegisteredInDesigner={7}, Outlets={8}, Actions={9}, DefinedIn={10}, UserTypeReferences={11}]", ObjCName, CliName, IsModel, BaseObjCType, BaseCliType, BaseIsModel, IsUserType, IsRegisteredInDesigner, Outlets, Actions, DefinedIn, UserTypeReferences);
434
 
                }
435
 
        }
436
 
        
437
 
        class ObjectiveCGenerationException : Exception
438
 
        {
439
 
                NSObjectTypeInfo typeInfo;
440
 
                
441
 
                public ObjectiveCGenerationException (string message, NSObjectTypeInfo typeInfo) : base (message)
442
 
                {
443
 
                        this.typeInfo = typeInfo;
444
 
                }
445
 
                
446
 
                public NSObjectTypeInfo TypeInfo { get { return typeInfo; } }
447
 
        }
448
 
}