1 /**
2  * Copyright: Copyright (c) 2011 Jacob Carlborg. All rights reserved.
3  * Authors: Jacob Carlborg
4  * Version: Initial created: Jan 29, 2012
5  * License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost Software License 1.0)
6  */
7 module dstep.translator.objc.ObjcInterface;
8 
9 import mambo.core._;
10 
11 import clang.c.Index;
12 import clang.Cursor;
13 import clang.Type;
14 import clang.Util;
15 import clang.Visitor;
16 
17 import dstep.translator.Translator;
18 import dstep.translator.Declaration;
19 import dstep.translator.Output;
20 import dstep.translator.Type;
21 
22 class ObjcInterface (Data) : Declaration
23 {
24     this (Cursor cursor, Cursor parent, Translator translator)
25     {
26         super(cursor, parent, translator);
27     }
28 
29     override string translate ()
30     {
31         auto cursor = cursor.objc;
32 
33         return writeClass(spelling, cursor.superClass.spelling, collectInterfaces(cursor.objc), {
34             foreach (cursor, parent ; cursor.declarations)
35             {
36                 with (CXCursorKind)
37                     switch (cursor.kind)
38                     {
39                         case CXCursor_ObjCInstanceMethodDecl: translateMethod(cursor.func); break;
40                         case CXCursor_ObjCClassMethodDecl: translateMethod(cursor.func, true); break;
41                         case CXCursor_ObjCPropertyDecl: translateProperty(cursor); break;
42                         case CXCursor_ObjCIvarDecl: translateInstanceVariable(cursor); break;
43                         default: break;
44                     }
45             }
46         });
47     }
48 
49     protected string[] collectInterfaces (ObjcCursor cursor)
50     {
51         string[] interfaces;
52 
53         foreach (cursor , parent ; cursor.protocols)
54             interfaces ~= translateIdentifier(cursor.spelling);
55 
56         return interfaces;
57     }
58 
59 private:
60 
61     string writeClass (string name, string superClassName, string[] interfaces, void delegate () dg)
62     {
63         output.currentClass = new Data;
64         output.currentClass.name = translateIdentifier(name);
65         output.currentClass.interfaces = interfaces;
66 
67         if (superClassName.isPresent)
68             output.currentClass.superclass ~= translateIdentifier(superClassName);
69 
70         dg();
71 
72         return output.currentClass.data;
73     }
74 
75     void translateMethod (FunctionCursor func, bool classMethod = false, string name = null)
76     {
77         auto method = output.newContext();
78         auto cls = output.currentClass;
79 
80         if (cls.propertyList.contains(func.spelling))
81             return;
82 
83         name = cls.getMethodName(func, name, false);
84 
85         if (isGetter(func, name))
86             translateGetter(func.resultType, method, name, cls, classMethod);
87 
88         else if (isSetter(func, name))
89         {
90             auto param = func.parameters.first;
91             name = toDSetterName(name);
92             translateSetter(param.type, method, name, cls, classMethod, param.spelling);
93         }
94 
95         else
96         {
97             name = translateIdentifier(name);
98             translateFunction(func, name, method, classMethod);
99 
100             method ~= ' ';
101             writeSelector(method, func.spelling);
102             method ~= ';';
103 
104             if (classMethod)
105                 cls.staticMethods ~= method.data;
106 
107             else
108                 cls.instanceMethods ~= method.data;
109         }
110     }
111 
112     void translateProperty (Cursor cursor)
113     {
114         auto context = output.newContext();
115         auto cls = output.currentClass;
116         auto name = cls.getMethodName(cursor.func, "", false);
117 
118         translateGetter(cursor.type, context, name, cls, false);
119         context = output.newContext();
120         translateSetter(cursor.type, context, name, cls, false);
121     }
122 
123     void translateInstanceVariable (Cursor cursor)
124     {
125         auto var = output.newContext();
126         translator.variable(cursor, var);
127         output.currentClass.instanceVariables ~= var.data;
128     }
129 
130     void translateGetter (Type type, String context, string name, ClassData cls, bool classMethod)
131     {
132         auto dName = name == "class" ? name : translateIdentifier(name);
133 
134         context ~= "@property ";
135 
136         if (classMethod)
137             context ~= "static ";
138 
139         context ~= translateType(type);
140         context ~= " ";
141         context ~= dName;
142         context ~= " () ";
143         writeSelector(context, name);
144         context ~= ';';
145 
146         auto data = context.data;
147 
148         if (classMethod)
149             cls.staticProperties ~= data;
150 
151         else
152             cls.properties ~= data;
153 
154         cls.propertyList.add(name);
155     }
156 
157     void translateSetter (Type type, String context, string name, ClassData cls, bool classMethod, string parameterName = "")
158     {
159         auto selector = toObjcSetterName(name) ~ ':';
160 
161         context ~= "@property ";
162 
163         if (classMethod)
164             context ~= "static ";
165 
166         context ~= "void ";
167         context ~= translateIdentifier(name);
168         context ~= " (";
169         context ~= translateType(type);
170 
171         if (parameterName.any)
172         {
173             context ~= " ";
174             context ~= parameterName;
175         }
176 
177         context ~= ") ";
178         writeSelector(context, selector);
179         context ~= ';';
180 
181         auto data = context.data;
182 
183         if (classMethod)
184             cls.staticProperties ~= data;
185 
186         else
187             cls.properties ~= data;
188 
189         cls.propertyList.add(selector);
190     }
191 
192     string toDSetterName (string name)
193     {
194         assert(isSetter(name));
195         name = name[3 .. $];
196         auto firstLetter = name[0 .. 1];
197         auto r = firstLetter.toLower ~ name[1 .. $];
198         return r.assumeUnique;
199     }
200 
201     string toObjcSetterName (string name)
202     {
203         auto r = "set" ~ name[0 .. 1].toUpper ~ name[1 .. $];
204         return r.assumeUnique;
205     }
206 
207     bool isGetter (FunctionCursor cursor, string name)
208     {
209         return cursor.resultType.kind != CXTypeKind.CXType_Void && cursor.parameters.isEmpty;
210     }
211 
212     bool isSetter (string name)
213     {
214         if (name.length > 3 && name.startsWith("set"))
215         {
216             auto firstLetter = name[3 .. $].first;
217             return firstLetter.isUpper;
218         }
219 
220         return false;
221     }
222 
223     bool isSetter (FunctionCursor cursor, string name)
224     {
225         return isSetter(name) &&
226             cursor.resultType.kind == CXTypeKind.CXType_Void &&
227             cursor.parameters.length == 1;
228     }
229 
230     void writeSelector (String context, string selector)
231     {
232         context.put(`@selector("`, selector, `")`);
233     }
234 }