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 }