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 }