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 			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 		context.put('[', name, "];");
144 
145 		auto data = context.data;
146 
147 		if (classMethod)
148 			cls.staticProperties ~= data;
149 
150 		else
151 			cls.properties ~= data;
152 
153 		cls.propertyList.add(name);
154 	}
155 
156 	void translateSetter (Type type, String context, string name, ClassData cls, bool classMethod, string parameterName = "")
157 	{
158 		auto selector = toObjcSetterName(name) ~ ':';
159 
160 		context ~= "@property ";
161 
162 		if (classMethod)
163 			context ~= "static ";
164 
165 		context ~= "void ";
166 		context ~= translateIdentifier(name);
167 		context ~= " (";
168 		context ~= translateType(type);
169 
170 		if (parameterName.any)
171 		{
172 			context ~= " ";
173 			context ~= parameterName;
174 		}
175 
176 		context ~= ") [";
177 		context ~= selector;
178 		context ~= "];";
179 
180 		auto data = context.data;
181 
182 		if (classMethod)
183 			cls.staticProperties ~= data;
184 
185 		else
186 			cls.properties ~= data;
187 
188 		cls.propertyList.add(selector);
189 	}
190 
191 	string toDSetterName (string name)
192 	{
193 		assert(isSetter(name));
194 		name = name[3 .. $];
195 		auto firstLetter = name[0 .. 1];
196 		auto r = firstLetter.toLower ~ name[1 .. $];
197 		return r.assumeUnique;
198 	}
199 
200 	string toObjcSetterName (string name)
201 	{
202 		auto r = "set" ~ name[0 .. 1].toUpper ~ name[1 .. $];
203 		return r.assumeUnique;
204 	}
205 
206 	bool isGetter (FunctionCursor cursor, string name)
207 	{
208 		return cursor.resultType.kind != CXTypeKind.CXType_Void && cursor.parameters.isEmpty;
209 	}
210 
211 	bool isSetter (string name)
212 	{
213 		if (name.length > 3 && name.startsWith("set"))
214 		{
215 			auto firstLetter = name[3 .. $].first;
216 			return firstLetter.isUpper;
217 		}
218 
219 		return false;
220 	}
221 
222 	bool isSetter (FunctionCursor cursor, string name)
223 	{
224 		return isSetter(name) &&
225 			cursor.resultType.kind == CXTypeKind.CXType_Void &&
226 			cursor.parameters.length == 1;
227 	}
228 }