/* * Copyright 2010 Pablo Arrighi, Alex Concha, Miguel Lezama for version 1. * Copyright 2013 Pablo Arrighi, Miguel Lezama, Kevin Mazet for version 2. * * This file is part of GOOL. * * GOOL is free software: you can redistribute it and/or modify it under the terms of the GNU * General Public License as published by the Free Software Foundation, version 3. * * GOOL is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License version 3 for more details. * * You should have received a copy of the GNU General Public License along with GOOL, * in the file COPYING.txt. If not, see <http://www.gnu.org/licenses/>. */ package gool.generator.objc; import gool.ast.core.ArrayAccess; import gool.ast.core.ArrayNew; import gool.ast.core.Assign; import gool.ast.core.BinaryOperation; import gool.ast.core.Catch; import gool.ast.core.ClassNew; import gool.ast.core.Constructor; import gool.ast.core.CustomDependency; import gool.ast.core.Dependency; import gool.ast.core.EnhancedForLoop; import gool.ast.core.EqualsCall; import gool.ast.core.Expression; import gool.ast.core.Field; import gool.ast.core.FieldAccess; import gool.ast.core.Finally; import gool.ast.core.Language; import gool.ast.core.MainMeth; import gool.ast.core.MemberSelect; import gool.ast.core.Meth; import gool.ast.core.MethCall; import gool.ast.core.Modifier; import gool.ast.core.Operator; import gool.ast.core.ParentCall; import gool.ast.core.RecognizedDependency; import gool.ast.core.Return; import gool.ast.core.This; import gool.ast.core.ThisCall; import gool.ast.core.Throw; import gool.ast.core.ToStringCall; import gool.ast.core.Try; import gool.ast.core.TypeDependency; import gool.ast.core.VarAccess; import gool.ast.core.VarDeclaration; import gool.ast.list.ListAddCall; import gool.ast.list.ListContainsCall; import gool.ast.list.ListGetCall; import gool.ast.list.ListGetIteratorCall; import gool.ast.list.ListIsEmptyCall; import gool.ast.list.ListRemoveAtCall; import gool.ast.list.ListRemoveCall; import gool.ast.list.ListSizeCall; import gool.ast.map.MapContainsKeyCall; import gool.ast.map.MapEntryGetKeyCall; import gool.ast.map.MapEntryGetValueCall; import gool.ast.map.MapGetCall; import gool.ast.map.MapGetIteratorCall; import gool.ast.map.MapIsEmptyCall; import gool.ast.map.MapPutCall; import gool.ast.map.MapRemoveCall; import gool.ast.map.MapSizeCall; import gool.ast.system.SystemCommandDependency; import gool.ast.system.SystemOutDependency; import gool.ast.system.SystemOutPrintCall; import gool.ast.type.IType; import gool.ast.type.PrimitiveType; import gool.ast.type.TypeArray; import gool.ast.type.TypeBool; import gool.ast.type.TypeChar; import gool.ast.type.TypeClass; import gool.ast.type.TypeDecimal; import gool.ast.type.TypeEntry; import gool.ast.type.TypeException; import gool.ast.type.TypeInt; import gool.ast.type.TypeList; import gool.ast.type.TypeMap; import gool.ast.type.TypeMethod; import gool.ast.type.TypeNone; import gool.ast.type.TypeNull; import gool.ast.type.TypeObject; import gool.ast.type.TypeString; import gool.generator.GeneratorHelper; import gool.generator.common.CommonCodeGenerator; import gool.generator.common.GeneratorMatcher; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import org.apache.commons.lang.StringUtils; public class ObjcGenerator extends CommonCodeGenerator { private String removePointer(IType type) { return removePointer(type.toString()); } private String removePointer(String type) { return type.replaceAll("[\\s*]+$", ""); } private static Map<String, Dependency> customDependencies = new HashMap<String, Dependency>(); @Override public void addCustomDependency(String key, Dependency value) { customDependencies.put(key, value); } @Override public String getCode(EnhancedForLoop enhancedForLoop) { return String .format("for(%s %s in %s){\n\t%s}", enhancedForLoop.getVarDec().getType(), enhancedForLoop.getVarDec().getName(), (enhancedForLoop.getExpression().getType() instanceof TypeMap) ? String .format("%s.entrySet()", enhancedForLoop.getExpression()) : enhancedForLoop.getExpression(), enhancedForLoop.getStatements()); } // List Methods @Override public String getCode(ListAddCall lac) { String nsObject = GeneratorHelperObjc.staticString(lac.getParameters() .get(0)); if (lac.getParameters().size() == 1) { if (lac.getParameters().get(0).getType() instanceof PrimitiveType && !(lac.getParameters().get(0).getType() instanceof TypeString)) { String nsNumber = "[[NSNumber alloc]initWith" + GeneratorHelperObjc.type(lac.getParameters().get(0) .getType()) + ":" + lac.getParameters().get(0) + "]"; return String.format("[%s addObject:%s]", lac.getExpression(), nsNumber); } return String.format("[%s addObject:%s%s]", lac.getExpression(), nsObject, lac.getParameters().get(0)); } else { String s = String.format("[%s arrayWithObjects:%s%s]", lac.getExpression(), nsObject, StringUtils.join(lac.getParameters(), ", ")); String str[] = new String[lac.getParameters().size()]; for (int i = 2; i <= lac.getParameters().size(); i++) { nsObject = GeneratorHelperObjc.staticString(lac.getParameters() .get(0)); str[i] = (String) String.format("[%s addObject:%s%s]\n", lac.getExpression(), nsObject, lac.getParameters().get(i)); } return String.format("%s\n%s", s, str); } } @Override public String getCode(ListContainsCall lcc) { String param0 = GeneratorHelperObjc.initWithObject(lcc.getParameters() .get(0)); return String.format("[%s containsObject:%s]", lcc.getExpression(), param0); } @Override public String getCode(ListGetCall lgc) { return String.format("[%s objectAtIndex:%s]", lgc.getExpression(), lgc .getParameters().get(0)); } @Override public String getCode(ListGetIteratorCall lgic) { return String.format("[%s objectEnumerator]", lgic.getExpression()); } @Override public String getCode(ListIsEmptyCall liec) { String s = "( " + String.format("[%s count]", liec.getExpression()) + "== 0)"; return s; } @Override public String getCode(ListRemoveAtCall lrc) { return String.format("[%s removeObjectAtIndex:%s]", lrc.getExpression(), lrc.getParameters().get(0)); } @Override public String getCode(ListRemoveCall lrc) { String param0 = GeneratorHelperObjc.initWithObject(lrc.getParameters() .get(0)); return String.format("[%s removeObject:%s]", lrc.getExpression(), param0); } @Override public String getCode(ListSizeCall lsc) { return String.format("[%s count]", lsc.getExpression()); } // ///////////// // Map Methods @Override public String getCode(MapContainsKeyCall mapContainsKeyCall) { /* * return String.format("[[%s objectForKey:@%s]isEqualToString:@" + "(" * + "null" + ")" + "]", mapContainsKeyCall.getExpression(), * mapContainsKeyCall.getParameters()); */// TODO return " /* MapContainsKeyCall non implemente */ "; } @Override public String getCode(MapEntryGetKeyCall mapEntryGetKeyCall) { return String .format("[%s allKeys]", mapEntryGetKeyCall.getExpression()); } @Override public String getCode(MapEntryGetValueCall mapEntryGetValueCall) { return String.format("[%s allValues]", mapEntryGetValueCall.getExpression()); } @Override public String getCode(MapGetCall mapGetCall) { String param0 = GeneratorHelperObjc.initWithObject(mapGetCall .getParameters().get(0)); return String.format("[%s objectForKey:%s]", mapGetCall.getExpression(), param0); } @Override public String getCode(MapGetIteratorCall mapGetIteratorCall) { return null; } @Override public String getCode(MapIsEmptyCall mapIsEmptyCall) { String s = "( " + String.format("[%s count]", mapIsEmptyCall.getExpression()) + "== 0)"; return s; } @Override public String getCode(MapPutCall mapPutCall) { String param0 = GeneratorHelperObjc.initWithObject(mapPutCall .getParameters().get(0)); String param1 = GeneratorHelperObjc.initWithObject(mapPutCall .getParameters().get(1)); return String.format("[%s setObject:%s forKey:%s]", mapPutCall.getExpression(), param0, param1); } @Override public String getCode(MapRemoveCall mapRemoveCall) { String param = GeneratorHelperObjc.initWithObject(mapRemoveCall .getParameters().get(0)); return String.format("[%s removeObjectForKey:%s]", mapRemoveCall.getExpression(), param); } @Override public String getCode(MapSizeCall mapSizeCall) { return String.format("[[(NSArray * )%s allKeys] count]", mapSizeCall.getExpression()); // TODO } // ///////////// // Meth Methods @Override public String getCode(MainMeth mainMeth) { return "int main(int argc, const char * argv[])"; } @Override public String getCode(ClassNew classNew) { String init = new String("init"); boolean b = false; for (Expression e : classNew.getParameters()) { String nsString = GeneratorHelperObjc.staticStringMini(e); if (!b) { init += "With"; init += String.format("%s:%s%s ", removePointer(e.getType()), nsString, e.toString()); b = true; } else { init += "With"; init += String.format("and%s:%s%s ", removePointer(e.getType()), nsString, e.toString()); } } return String.format("[[%s alloc]%s]", removePointer(classNew.getType()), init); } // retourne l'expression appellant la méthode private Expression getTarget(Object n) { if (n instanceof MemberSelect) { return ((MemberSelect) n).getTarget(); } else if (n instanceof MethCall) { return ((MethCall) n).getTarget(); } else if (n instanceof FieldAccess) { return ((FieldAccess) n).getTarget(); } else if (n instanceof EqualsCall) { return ((EqualsCall) n).getTarget(); } else return null; } // génère la liste des paramètres pour l'appel d'une méthode private String getMethCallName(List<Expression> param, boolean typeFirstParam) { String arg = ""; String p; String nsString; boolean b = false; for (Expression e : param) { nsString = GeneratorHelperObjc.staticStringMini(e); if (e.getType() == TypeChar.INSTANCE) p = "'" + e.toString() + "'"; else p = e.toString(); if (!b) { if (typeFirstParam) arg += String.format("%s:%s%s ", removePointer(e.getType()), nsString, p); else arg += String.format("%s%s ", nsString, p); b = true; } else arg += String.format("and%s:%s%s ", removePointer(e.getType()), nsString, p); } return arg; } // Génère la liste des paramètres pour la définition d'une méthode private String getMethDefName(List<VarDeclaration> param) { String arg = ""; boolean b = false; for (VarDeclaration e : param) { if (!b) { arg += String.format("%s:(%s)%s ", removePointer(e.getType()), e.getType(), e.getName()); b = true; } else arg += String.format("and%s:(%s)%s ", removePointer(e.getType()), e.getType(), e.getName()); } return arg; } private String methUnknow(String generalName, String library) { return String .format("/* La méthode %s de la bibliothèque %s n'est pas implémenté pour le langage */", generalName.replaceAll("\\s", ""), library); } @Override public String getCode(MethCall methodCall) { String arg = new String(); String specificName; arg = getMethCallName(methodCall.getParameters(), true); if (methodCall.getTarget() instanceof VarAccess) return String.format("[%s%s]", methodCall.getTarget(), arg); if (methodCall.getTarget() instanceof ParentCall) return getCode((ParentCall) methodCall.getTarget()); return String.format("[%s%s]", methodCall.getTarget(), arg); } @Override public String getCode(Meth meth) { String ret = new String(); String arg = new String(); String mod = (meth.getModifiers().contains(Modifier.STATIC) ? "+" : "-"); if (!(meth.getType() instanceof TypeNone)) ret = String.format("(%s)", meth.getType().toString()); else ret = "(void)"; arg = getMethDefName(meth.getParams()); return String.format("%s %s %s%s", mod, ret, meth.getName(), arg); } @Override public String getCode(Constructor cons) { String param = new String(); param = getMethDefName(cons.getParams()); return String.format("- (%s *)initWith%s", cons.getClassDef().getName(), param); } @Override public String getCode(SystemOutPrintCall systemOutPrintCall) { String nsString = GeneratorHelperObjc.staticString(systemOutPrintCall .getParameters().get(0)); String out = null; String format = null; if (systemOutPrintCall.getParameters().get(0).getType() instanceof TypeClass) out = "[" + systemOutPrintCall.getParameters().get(0) + " toString]"; format = (systemOutPrintCall.getParameters().get(0) instanceof ArrayAccess) && ((ArrayAccess) systemOutPrintCall.getParameters().get(0)) .getExpression().getType() instanceof TypeArray ? GeneratorHelperObjc .format(((TypeArray) ((ArrayAccess) systemOutPrintCall .getParameters().get(0)).getExpression().getType()) .getElementType()) : GeneratorHelperObjc .format(systemOutPrintCall.getParameters().get(0)); // I added the two following lines to fix a bug dealing with // non-string parameters in NSLog calls. if (!format.equals("%@")) nsString = ""; return String.format( "NSLog(@\"%s\",%s%s)", format, nsString, out == null ? GeneratorHelper.joinParams(systemOutPrintCall .getParameters()) : out); } @Override public String getCode(ToStringCall tsc) { return String.format("[%s toString]", tsc.getTarget()); } @Override public String getCode(EqualsCall equalsCall) { String arg = getMethCallName(equalsCall.getParameters(), false); return String.format("[%s isEqual: %s]", equalsCall.getTarget(), arg); } @Override public String getCode(ParentCall parentCall) { String out = "self = [super init"; out += getMethCallName(parentCall.getParameters(), true); out += "]"; return out; } @Override public String getCode(ThisCall thisCall) { return "self()"; } @Override public String getCode(Return returnExpr) { String nsString = GeneratorHelperObjc.staticString(returnExpr .getExpression()); return String.format("return (%s%s)", nsString, returnExpr.getExpression()); } // ///////////// // Type Methods @Override public String getCode(TypeBool typeBool) { return "BOOL"; } @Override public String getCode(TypeDecimal typeReal) { return "double"; } @Override public String getCode(TypeEntry typeEntry) { return String.format("[dictionary setObject:@%s forKey:@%s]", typeEntry.getElementType(), typeEntry.getKeyType()); } public String getCode(TypeObject typeObject) { // type java.object // return NSObject return "id"; } @Override public String getCode(TypeInt typeInt) { return "int"; } @Override public String getCode(TypeArray typeArray) { return String.format("%s", typeArray.getElementType()); } @Override public String getCode(TypeList typeList) { return String.format("NSMutableArray *"); } @Override public String getCode(TypeMap typeMap) { return "NSMutableDictionary *"; } @Override public String getCode(TypeString typeString) { return "NSMutableString *"; } @Override public String getCode(TypeChar typeChar) { return "char"; } @Override public String getCode(This pthis) { return "self"; } @Override public String getCode(TypeNull typeNull) { return "nil"; } @Override public String getCode(TypeClass typeClass) { String pointer = typeClass.isEnum() ? "" : "*"; return super.getCode(typeClass) + pointer; } @Override public String getCode(TypeDependency typeDependency) { if (typeDependency.getType() instanceof TypeList) { return "Foundation/Foundation.h"; } if (typeDependency.getType() instanceof TypeMap) { return "Foundation/Foundation.h"; } if (typeDependency.getType() instanceof TypeEntry) { return "Foundation/Foundation.h"; } if (typeDependency.getType() instanceof TypeString) { return "Foundation/Foundation.h"; } if (typeDependency.getType() instanceof TypeBool) { return "noprint"; } if (typeDependency.getType() instanceof TypeInt) { return "noprint"; } if (typeDependency.getType() instanceof TypeException) { return "Foundation/Foundation.h"; } return removePointer(super.getCode(typeDependency)).concat(".h"); } // ///////////// // Dependency methods @Override public String getCode(SystemOutDependency systemOutDependency) { return "Foundation/Foundation.h"; } @Override public String getCode(CustomDependency customDependency) { // TODO Auto-generated method stub return null; } @Override public String getCode(SystemCommandDependency systemCommandDependency) { // TODO Auto-generated method stub return null; } // ///////////// @Override public String getCode(Assign assign) { return assign.getLValue() + " = " + GeneratorHelperObjc.staticStringMini(assign.getValue()) + assign.getValue(); } @Override public String getCode(VarDeclaration varDec) { // TODO Si une variable se nomme id .... fail String initialValue = ""; String type = varDec.getType().toString(); if (varDec.getInitialValue() != null) { if (varDec.getInitialValue().getType() instanceof TypeNull) initialValue = " = nil"; // TODO pas normal, nil est mit dans le // type de la valeur initital et pas // dans la valeur initiale else if (varDec.getInitialValue().getType() instanceof TypeString && !(varDec.getInitialValue() instanceof MethCall)) initialValue = " = @" + varDec.getInitialValue(); else if (varDec.getInitialValue().getType() instanceof TypeChar && !(varDec.getInitialValue() instanceof MethCall)) initialValue = " = " + varDec.getInitialValue(); else initialValue = " = " + varDec.getInitialValue(); } if (varDec.getType() instanceof TypeArray) return String.format("%s %s[%s]", type, varDec.getName(), ((ArrayNew) varDec.getInitialValue()).getDimesExpressions() .get(0)); return String.format("%s %s%s", type, varDec.getName(), initialValue); } @Override public String getCode(BinaryOperation binaryOp) { String left = binaryOp.getLeft().toString(); String right = binaryOp.getRight().toString(); if (binaryOp.getOperator() == Operator.PLUS && binaryOp.getType().equals(TypeString.INSTANCE)) { String nsStringLeft = GeneratorHelperObjc.staticString(binaryOp .getLeft()); String nsStringRight = GeneratorHelperObjc.staticString(binaryOp .getRight()); if (binaryOp.getLeft().getType() instanceof TypeClass) left = "[" + left + " toString]"; if (binaryOp.getRight().getType() instanceof TypeClass) right = "[" + right + " toString]"; String fleft = (binaryOp.getLeft() instanceof ArrayAccess) && ((ArrayAccess) binaryOp.getLeft()).getExpression() .getType() instanceof TypeArray ? GeneratorHelperObjc .format(((TypeArray) ((ArrayAccess) binaryOp.getLeft()) .getExpression().getType()).getElementType()) : GeneratorHelperObjc.format(binaryOp.getLeft()); String fright = (binaryOp.getRight() instanceof ArrayAccess) && ((ArrayAccess) binaryOp.getRight()).getExpression() .getType() instanceof TypeArray ? GeneratorHelperObjc .format(((TypeArray) ((ArrayAccess) binaryOp.getRight()) .getExpression().getType()).getElementType()) : GeneratorHelperObjc.format(binaryOp.getRight()); return String.format( "[NSString stringWithFormat:@\"%s%s\",%s%s,%s%s]", fleft, fright, nsStringLeft, left, nsStringRight, right); } return super.getCode(binaryOp); } @Override public String getCode(MemberSelect memberSelect) { String target = memberSelect.getTarget().toString().equals("this") ? "self" : memberSelect.getTarget().toString(); String sep; if (memberSelect.getType() instanceof TypeMethod) sep = " "; else sep = "->"; return String.format("%s%s%s", target, sep, memberSelect.getIdentifier()); } @Override public String getCode(Modifier modifier) { switch (modifier) { case ABSTRACT: return ""; case FINAL: return "const"; case PRIVATE: return "@private"; case PROTECTED: return "@protected"; case PUBLIC: return "@public"; case STATIC: return "+"; default: return super.getCode(modifier); } } @Override public String getCode(Field field) { String out = String.format("%s %s", field.getType(), field.getName()); if (field.getDefaultValue() != null) { out = String.format("%s = %s", out, field.getDefaultValue()); } if (field.getType().toString().equals("noprint")) return ""; return out; } @Override public String getCode(TypeException typeException) { return "NSException *"; } @Override public String getCode(Catch catchBlock) { return "@catch"; } @Override public String getCode(Try tryBlock) { return "@try "; } @Override public String getCode(Throw throwexpression) { return "@throw"; } public String getCode(RecognizedDependency recognizedDependency) { List<String> imports = GeneratorMatcher.matchImports(recognizedDependency.getName()); if(imports.isEmpty()) return "/* import "+recognizedDependency.getName()+" not generated by GOOL, passed on. */"; String result = ""; for (String Import : imports) { if (Import.startsWith("+")) Import = Import.substring(1); result += "#import <" + Import.replace(".", "/") + ".h>\n"; } return result; } }